diff options
Diffstat (limited to 'drivers/s390/net')
| -rw-r--r-- | drivers/s390/net/Makefile | 2 | ||||
| -rw-r--r-- | drivers/s390/net/claw.c | 2 | ||||
| -rw-r--r-- | drivers/s390/net/claw.h | 8 | ||||
| -rw-r--r-- | drivers/s390/net/ctcm_dbug.c | 2 | ||||
| -rw-r--r-- | drivers/s390/net/ctcm_main.c | 2 | ||||
| -rw-r--r-- | drivers/s390/net/ctcm_sysfs.c | 14 | ||||
| -rw-r--r-- | drivers/s390/net/lcs.c | 16 | ||||
| -rw-r--r-- | drivers/s390/net/lcs.h | 8 | ||||
| -rw-r--r-- | drivers/s390/net/netiucv.c | 16 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_core.h | 50 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_core_main.c | 330 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_core_mpc.c | 2 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_core_mpc.h | 150 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_core_sys.c | 22 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_l2.h | 15 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 650 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_l2_sys.c | 223 | ||||
| -rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 32 | 
18 files changed, 1376 insertions, 168 deletions
diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile index 4dfe8c1092d..d28f05d0c75 100644 --- a/drivers/s390/net/Makefile +++ b/drivers/s390/net/Makefile @@ -11,7 +11,7 @@ obj-$(CONFIG_LCS) += lcs.o  obj-$(CONFIG_CLAW) += claw.o  qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o  obj-$(CONFIG_QETH) += qeth.o -qeth_l2-y += qeth_l2_main.o +qeth_l2-y += qeth_l2_main.o qeth_l2_sys.o  obj-$(CONFIG_QETH_L2) += qeth_l2.o  qeth_l3-y += qeth_l3_main.o qeth_l3_sys.o  obj-$(CONFIG_QETH_L3) += qeth_l3.o diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index fd7b3bd8078..d837c3c5330 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -3348,7 +3348,7 @@ static int __init claw_init(void)  	}  	CLAW_DBF_TEXT(2, setup, "init_mod");  	claw_root_dev = root_device_register("claw"); -	ret = PTR_RET(claw_root_dev); +	ret = PTR_ERR_OR_ZERO(claw_root_dev);  	if (ret)  		goto register_err;  	ret = ccw_driver_register(&claw_ccw_driver); diff --git a/drivers/s390/net/claw.h b/drivers/s390/net/claw.h index 1bc5904df19..3339b9b607b 100644 --- a/drivers/s390/net/claw.h +++ b/drivers/s390/net/claw.h @@ -114,15 +114,9 @@ do { \  	debug_event(claw_dbf_##name,level,(void*)(addr),len); \  } while (0) -/* Allow to sort out low debug levels early to avoid wasted sprints */ -static inline int claw_dbf_passes(debug_info_t *dbf_grp, int level) -{ -	return (level <= dbf_grp->level); -} -  #define CLAW_DBF_TEXT_(level,name,text...) \  	do { \ -		if (claw_dbf_passes(claw_dbf_##name, level)) { \ +		if (debug_level_enabled(claw_dbf_##name, level)) { \  			sprintf(debug_buffer, text); \  			debug_text_event(claw_dbf_##name, level, \  						debug_buffer); \ diff --git a/drivers/s390/net/ctcm_dbug.c b/drivers/s390/net/ctcm_dbug.c index 6514e1cb3f1..8363f1c966e 100644 --- a/drivers/s390/net/ctcm_dbug.c +++ b/drivers/s390/net/ctcm_dbug.c @@ -66,7 +66,7 @@ void ctcm_dbf_longtext(enum ctcm_dbf_names dbf_nix, int level, char *fmt, ...)  	char dbf_txt_buf[64];  	va_list args; -	if (level > (ctcm_dbf[dbf_nix].id)->level) +	if (!debug_level_enabled(ctcm_dbf[dbf_nix].id, level))  		return;  	va_start(args, fmt);  	vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args); diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 70b3a023100..03b6ad03557 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1837,7 +1837,7 @@ static int __init ctcm_init(void)  	if (ret)  		goto out_err;  	ctcm_root_dev = root_device_register("ctcm"); -	ret = PTR_RET(ctcm_root_dev); +	ret = PTR_ERR_OR_ZERO(ctcm_root_dev);  	if (ret)  		goto register_err;  	ret = ccw_driver_register(&ctcm_ccw_driver); diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c index 985b5dcbdac..6bcfbbb20f0 100644 --- a/drivers/s390/net/ctcm_sysfs.c +++ b/drivers/s390/net/ctcm_sysfs.c @@ -34,8 +34,9 @@ static ssize_t ctcm_buffer_write(struct device *dev,  		struct device_attribute *attr, const char *buf, size_t count)  {  	struct net_device *ndev; -	int bs1; +	unsigned int bs1;  	struct ctcm_priv *priv = dev_get_drvdata(dev); +	int rc;  	ndev = priv->channel[CTCM_READ]->netdev;  	if (!(priv && priv->channel[CTCM_READ] && ndev)) { @@ -43,7 +44,9 @@ static ssize_t ctcm_buffer_write(struct device *dev,  		return -ENODEV;  	} -	sscanf(buf, "%u", &bs1); +	rc = sscanf(buf, "%u", &bs1); +	if (rc != 1) +		goto einval;  	if (bs1 > CTCM_BUFSIZE_LIMIT)  					goto einval;  	if (bs1 < (576 + LL_HEADER_LENGTH + 2)) @@ -143,13 +146,14 @@ static ssize_t ctcm_proto_show(struct device *dev,  static ssize_t ctcm_proto_store(struct device *dev,  		struct device_attribute *attr, const char *buf, size_t count)  { -	int value; +	int value, rc;  	struct ctcm_priv *priv = dev_get_drvdata(dev);  	if (!priv)  		return -ENODEV; -	sscanf(buf, "%u", &value); -	if (!((value == CTCM_PROTO_S390)  || +	rc = sscanf(buf, "%d", &value); +	if ((rc != 1) || +	    !((value == CTCM_PROTO_S390)  ||  	      (value == CTCM_PROTO_LINUX) ||  	      (value == CTCM_PROTO_MPC) ||  	      (value == CTCM_PROTO_OS390))) diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index f404f55b319..0a7d87c372b 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -899,6 +899,7 @@ lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer,  	add_timer(&timer);  	wait_event(reply->wait_q, reply->received);  	del_timer_sync(&timer); +	destroy_timer_on_stack(&timer);  	LCS_DBF_TEXT_(4, trace, "rc:%d",reply->rc);  	rc = reply->rc;  	lcs_put_reply(reply); @@ -1942,14 +1943,16 @@ static ssize_t  lcs_portno_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)  {          struct lcs_card *card; -        int value; +	int value, rc;  	card = dev_get_drvdata(dev);          if (!card)                  return 0; -        sscanf(buf, "%u", &value); +	rc = sscanf(buf, "%d", &value); +	if (rc != 1) +		return -EINVAL;          /* TODO: sanity checks */          card->portno = value; @@ -1996,14 +1999,17 @@ static ssize_t  lcs_timeout_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)  {          struct lcs_card *card; -        int value; +	unsigned int value; +	int rc;  	card = dev_get_drvdata(dev);          if (!card)                  return 0; -        sscanf(buf, "%u", &value); +	rc = sscanf(buf, "%u", &value); +	if (rc != 1) +		return -EINVAL;          /* TODO: sanity checks */          card->lancmd_timeout = value; @@ -2441,7 +2447,7 @@ __init lcs_init_module(void)  	if (rc)  		goto out_err;  	lcs_root_dev = root_device_register("lcs"); -	rc = PTR_RET(lcs_root_dev); +	rc = PTR_ERR_OR_ZERO(lcs_root_dev);  	if (rc)  		goto register_err;  	rc = ccw_driver_register(&lcs_ccw_driver); diff --git a/drivers/s390/net/lcs.h b/drivers/s390/net/lcs.h index 8c03392ac83..150fcb4cebc 100644 --- a/drivers/s390/net/lcs.h +++ b/drivers/s390/net/lcs.h @@ -16,15 +16,9 @@ do { \  	debug_event(lcs_dbf_##name,level,(void*)(addr),len); \  } while (0) -/* Allow to sort out low debug levels early to avoid wasted sprints */ -static inline int lcs_dbf_passes(debug_info_t *dbf_grp, int level) -{ -	return (level <= dbf_grp->level); -} -  #define LCS_DBF_TEXT_(level,name,text...) \  	do { \ -		if (lcs_dbf_passes(lcs_dbf_##name, level)) { \ +		if (debug_level_enabled(lcs_dbf_##name, level)) { \  			sprintf(debug_buffer, text); \  			debug_text_event(lcs_dbf_##name, level, debug_buffer); \  		} \ diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 279ad504ec3..ce16d1bdb20 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -105,15 +105,9 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");  DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf); -/* Allow to sort out low debug levels early to avoid wasted sprints */ -static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level) -{ -	return (level <= dbf_grp->level); -} -  #define IUCV_DBF_TEXT_(name, level, text...) \  	do { \ -		if (iucv_dbf_passes(iucv_dbf_##name, level)) { \ +		if (debug_level_enabled(iucv_dbf_##name, level)) { \  			char* __buf = get_cpu_var(iucv_dbf_txt_buf); \  			sprintf(__buf, text); \  			debug_text_event(iucv_dbf_##name, level, __buf); \ @@ -745,8 +739,12 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg)  	IUCV_DBF_TEXT(trace, 4, __func__); -	if (conn && conn->netdev) -		privptr = netdev_priv(conn->netdev); +	if (!conn || !conn->netdev) { +		IUCV_DBF_TEXT(data, 2, +			      "Send confirmation for unlinked connection\n"); +		return; +	} +	privptr = netdev_priv(conn->netdev);  	conn->prof.tx_pending--;  	if (single_flag) {  		if ((skb = skb_dequeue(&conn->commit_queue))) { diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 41ef94320ee..a2088af51cc 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -156,6 +156,27 @@ struct qeth_ipa_info {  	__u32 enabled_funcs;  }; +/* SETBRIDGEPORT stuff */ +enum qeth_sbp_roles { +	QETH_SBP_ROLE_NONE	= 0, +	QETH_SBP_ROLE_PRIMARY	= 1, +	QETH_SBP_ROLE_SECONDARY	= 2, +}; + +enum qeth_sbp_states { +	QETH_SBP_STATE_INACTIVE	= 0, +	QETH_SBP_STATE_STANDBY	= 1, +	QETH_SBP_STATE_ACTIVE	= 2, +}; + +#define QETH_SBP_HOST_NOTIFICATION 1 + +struct qeth_sbp_info { +	__u32 supported_funcs; +	enum qeth_sbp_roles role; +	__u32 hostnotification:1; +}; +  static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa,  		enum qeth_ipa_funcs func)  { @@ -247,10 +268,8 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,  #define QETH_NO_PRIO_QUEUEING 0  #define QETH_PRIO_Q_ING_PREC  1  #define QETH_PRIO_Q_ING_TOS   2 -#define IP_TOS_LOWDELAY 0x10 -#define IP_TOS_HIGHTHROUGHPUT 0x08 -#define IP_TOS_HIGHRELIABILITY 0x04 -#define IP_TOS_NOTIMPORTANT 0x02 +#define QETH_PRIO_Q_ING_SKB   3 +#define QETH_PRIO_Q_ING_VLAN  4  /* Packing */  #define QETH_LOW_WATERMARK_PACK  2 @@ -672,6 +691,7 @@ struct qeth_card_options {  	struct qeth_ipa_info adp; /*Adapter parameters*/  	struct qeth_routing_info route6;  	struct qeth_ipa_info ipa6; +	struct qeth_sbp_info sbp; /* SETBRIDGEPORT options */  	int fake_broadcast;  	int add_hhlen;  	int layer2; @@ -716,6 +736,8 @@ struct qeth_discipline {  	int (*freeze)(struct ccwgroup_device *);  	int (*thaw) (struct ccwgroup_device *);  	int (*restore)(struct ccwgroup_device *); +	int (*control_event_handler)(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd);  };  struct qeth_vlan_vid { @@ -738,6 +760,12 @@ struct qeth_rx {  	int qdio_err;  }; +struct carrier_info { +	__u8  card_type; +	__u16 port_mode; +	__u32 port_speed; +}; +  #define QETH_NAPI_WEIGHT NAPI_POLL_WEIGHT  struct qeth_card { @@ -824,8 +852,11 @@ static inline int qeth_get_micros(void)  static inline int qeth_get_ip_version(struct sk_buff *skb)  { -	struct ethhdr *ehdr = (struct ethhdr *)skb->data; -	switch (ehdr->h_proto) { +	__be16 *p = &((struct ethhdr *)skb->data)->h_proto; + +	if (*p == ETH_P_8021Q) +		p += 2; +	switch (*p) {  	case ETH_P_IPV6:  		return 6;  	case ETH_P_IP: @@ -851,6 +882,7 @@ extern struct qeth_discipline qeth_l2_discipline;  extern struct qeth_discipline qeth_l3_discipline;  extern const struct attribute_group *qeth_generic_attr_groups[];  extern const struct attribute_group *qeth_osn_attr_groups[]; +extern struct workqueue_struct *qeth_wq;  const char *qeth_get_cardname_short(struct qeth_card *);  int qeth_realloc_buffer_pool(struct qeth_card *, int); @@ -914,9 +946,15 @@ struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *);  int qeth_mdio_read(struct net_device *, int, int);  int qeth_snmp_command(struct qeth_card *, char __user *);  int qeth_query_oat_command(struct qeth_card *, char __user *); +int qeth_query_card_info(struct qeth_card *card, +	struct carrier_info *carrier_info);  int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,  	int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long),  	void *reply_param); +int qeth_bridgeport_query_ports(struct qeth_card *card, +	enum qeth_sbp_roles *role, enum qeth_sbp_states *state); +int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); +int qeth_bridgeport_an_set(struct qeth_card *card, int enable);  int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int);  int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int);  int qeth_get_elements_for_frags(struct sk_buff *); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 0a328d0d11b..f54bec54d67 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -20,8 +20,10 @@  #include <linux/kthread.h>  #include <linux/slab.h>  #include <net/iucv/af_iucv.h> +#include <net/dsfield.h>  #include <asm/ebcdic.h> +#include <asm/chpid.h>  #include <asm/io.h>  #include <asm/sysinfo.h>  #include <asm/compat.h> @@ -33,8 +35,8 @@ struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = {  	/*                   N  P  A    M  L  V                      H  */  	[QETH_DBF_SETUP] = {"qeth_setup",  				8, 1,   8, 5, &debug_hex_ascii_view, NULL}, -	[QETH_DBF_MSG]   = {"qeth_msg", -				8, 1, 128, 3, &debug_sprintf_view,   NULL}, +	[QETH_DBF_MSG]	 = {"qeth_msg", 8, 1, 11 * sizeof(long), 3, +			    &debug_sprintf_view, NULL},  	[QETH_DBF_CTRL]  = {"qeth_control",  		8, 1, QETH_DBF_CTRL_LEN, 5, &debug_hex_ascii_view, NULL},  }; @@ -68,7 +70,8 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,  		enum qeth_qdio_buffer_states newbufstate);  static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); -static struct workqueue_struct *qeth_wq; +struct workqueue_struct *qeth_wq; +EXPORT_SYMBOL_GPL(qeth_wq);  static void qeth_close_dev_handler(struct work_struct *work)  { @@ -615,6 +618,13 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,  					card->info.hwtrap = 2;  				qeth_schedule_recovery(card);  				return NULL; +			case IPA_CMD_SETBRIDGEPORT: +			case IPA_CMD_ADDRESS_CHANGE_NOTIF: +				if (card->discipline->control_event_handler +								(card, cmd)) +					return cmd; +				else +					return NULL;  			case IPA_CMD_MODCCID:  				return cmd;  			case IPA_CMD_REGISTER_LOCAL_ADDR: @@ -1004,7 +1014,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,  	card = CARD_FROM_CDEV(cdev); -	if (!IS_ERR(irb)) +	if (!card || !IS_ERR(irb))  		return 0;  	switch (PTR_ERR(irb)) { @@ -1020,7 +1030,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,  		QETH_CARD_TEXT(card, 2, "ckirberr");  		QETH_CARD_TEXT_(card, 2, "  rc%d", -ETIMEDOUT);  		if (intparm == QETH_RCD_PARM) { -			if (card && (card->data.ccwdev == cdev)) { +			if (card->data.ccwdev == cdev) {  				card->data.state = CH_STATE_DOWN;  				wake_up(&card->wait_q);  			} @@ -1336,16 +1346,7 @@ static void qeth_set_multiple_write_queues(struct qeth_card *card)  static void qeth_update_from_chp_desc(struct qeth_card *card)  {  	struct ccw_device *ccwdev; -	struct channelPath_dsc { -		u8 flags; -		u8 lsn; -		u8 desc; -		u8 chpid; -		u8 swla; -		u8 zeroes; -		u8 chla; -		u8 chpp; -	} *chp_dsc; +	struct channel_path_desc *chp_dsc;  	QETH_DBF_TEXT(SETUP, 2, "chp_desc"); @@ -1652,7 +1653,6 @@ int qeth_qdio_clear_card(struct qeth_card *card, int use_halt)  				QDIO_FLAG_CLEANUP_USING_CLEAR);  		if (rc)  			QETH_CARD_TEXT_(card, 3, "1err%d", rc); -		qdio_free(CARD_DDEV(card));  		atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED);  		break;  	case QETH_QDIO_CLEANING: @@ -2597,6 +2597,7 @@ static int qeth_mpc_initialize(struct qeth_card *card)  	return 0;  out_qdio:  	qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); +	qdio_free(CARD_DDEV(card));  	return rc;  } @@ -3662,42 +3663,56 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev,  }  EXPORT_SYMBOL_GPL(qeth_qdio_output_handler); +/** + * Note: Function assumes that we have 4 outbound queues. + */  int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,  			int ipv, int cast_type)  { -	if (!ipv && (card->info.type == QETH_CARD_TYPE_OSD || -		     card->info.type == QETH_CARD_TYPE_OSX)) -		return card->qdio.default_out_queue; -	switch (card->qdio.no_out_queues) { -	case 4: -		if (cast_type && card->info.is_multicast_different) -			return card->info.is_multicast_different & -				(card->qdio.no_out_queues - 1); -		if (card->qdio.do_prio_queueing && (ipv == 4)) { -			const u8 tos = ip_hdr(skb)->tos; - -			if (card->qdio.do_prio_queueing == -				QETH_PRIO_Q_ING_TOS) { -				if (tos & IP_TOS_NOTIMPORTANT) -					return 3; -				if (tos & IP_TOS_HIGHRELIABILITY) -					return 2; -				if (tos & IP_TOS_HIGHTHROUGHPUT) -					return 1; -				if (tos & IP_TOS_LOWDELAY) -					return 0; -			} -			if (card->qdio.do_prio_queueing == -				QETH_PRIO_Q_ING_PREC) -				return 3 - (tos >> 6); -		} else if (card->qdio.do_prio_queueing && (ipv == 6)) { -			/* TODO: IPv6!!! */ +	__be16 *tci; +	u8 tos; + +	if (cast_type && card->info.is_multicast_different) +		return card->info.is_multicast_different & +			(card->qdio.no_out_queues - 1); + +	switch (card->qdio.do_prio_queueing) { +	case QETH_PRIO_Q_ING_TOS: +	case QETH_PRIO_Q_ING_PREC: +		switch (ipv) { +		case 4: +			tos = ipv4_get_dsfield(ip_hdr(skb)); +			break; +		case 6: +			tos = ipv6_get_dsfield(ipv6_hdr(skb)); +			break; +		default: +			return card->qdio.default_out_queue;  		} -		return card->qdio.default_out_queue; -	case 1: /* fallthrough for single-out-queue 1920-device */ +		if (card->qdio.do_prio_queueing == QETH_PRIO_Q_ING_PREC) +			return ~tos >> 6 & 3; +		if (tos & IPTOS_MINCOST) +			return 3; +		if (tos & IPTOS_RELIABILITY) +			return 2; +		if (tos & IPTOS_THROUGHPUT) +			return 1; +		if (tos & IPTOS_LOWDELAY) +			return 0; +		break; +	case QETH_PRIO_Q_ING_SKB: +		if (skb->priority > 5) +			return 0; +		return ~skb->priority >> 1 & 3; +	case QETH_PRIO_Q_ING_VLAN: +		tci = &((struct ethhdr *)skb->data)->h_proto; +		if (*tci == ETH_P_8021Q) +			return ~*(tci + 1) >> (VLAN_PRIO_SHIFT + 1) & 3; +		break;  	default: -		return card->qdio.default_out_queue; +		break;  	} +	return card->qdio.default_out_queue;  }  EXPORT_SYMBOL_GPL(qeth_get_priority_queue); @@ -4451,7 +4466,7 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata)  	struct qeth_cmd_buffer *iob;  	struct qeth_ipa_cmd *cmd;  	struct qeth_snmp_ureq *ureq; -	int req_len; +	unsigned int req_len;  	struct qeth_arp_query_info qinfo = {0, };  	int rc = 0; @@ -4467,6 +4482,10 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata)  	/* skip 4 bytes (data_len struct member) to get req_len */  	if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int)))  		return -EFAULT; +	if (req_len > (QETH_BUFSIZE - IPA_PDU_HEADER_SIZE - +		       sizeof(struct qeth_ipacmd_hdr) - +		       sizeof(struct qeth_ipacmd_setadpparms_hdr))) +		return -EINVAL;  	ureq = memdup_user(udata, req_len + sizeof(struct qeth_snmp_ureq_hdr));  	if (IS_ERR(ureq)) {  		QETH_CARD_TEXT(card, 2, "snmpnome"); @@ -4598,6 +4617,42 @@ out:  }  EXPORT_SYMBOL_GPL(qeth_query_oat_command); +static int qeth_query_card_info_cb(struct qeth_card *card, +				   struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd; +	struct qeth_query_card_info *card_info; +	struct carrier_info *carrier_info; + +	QETH_CARD_TEXT(card, 2, "qcrdincb"); +	carrier_info = (struct carrier_info *)reply->param; +	cmd = (struct qeth_ipa_cmd *)data; +	card_info = &cmd->data.setadapterparms.data.card_info; +	if (cmd->data.setadapterparms.hdr.return_code == 0) { +		carrier_info->card_type = card_info->card_type; +		carrier_info->port_mode = card_info->port_mode; +		carrier_info->port_speed = card_info->port_speed; +	} + +	qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); +	return 0; +} + +int qeth_query_card_info(struct qeth_card *card, +				struct carrier_info *carrier_info) +{ +	struct qeth_cmd_buffer *iob; + +	QETH_CARD_TEXT(card, 2, "qcrdinfo"); +	if (!qeth_adp_supported(card, IPA_SETADP_QUERY_CARD_INFO)) +		return -EOPNOTSUPP; +	iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_CARD_INFO, +		sizeof(struct qeth_ipacmd_setadpparms_hdr)); +	return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb, +					(void *)carrier_info); +} +EXPORT_SYMBOL_GPL(qeth_query_card_info); +  static inline int qeth_get_qdio_q_format(struct qeth_card *card)  {  	switch (card->info.type) { @@ -4858,9 +4913,11 @@ retry:  	if (retries < 3)  		QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n",  			dev_name(&card->gdev->dev)); +	rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD);  	ccw_device_set_offline(CARD_DDEV(card));  	ccw_device_set_offline(CARD_WDEV(card));  	ccw_device_set_offline(CARD_RDEV(card)); +	qdio_free(CARD_DDEV(card));  	rc = ccw_device_set_online(CARD_RDEV(card));  	if (rc)  		goto retriable; @@ -4870,7 +4927,6 @@ retry:  	rc = ccw_device_set_online(CARD_DDEV(card));  	if (rc)  		goto retriable; -	rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD);  retriable:  	if (rc == -ERESTARTSYS) {  		QETH_DBF_TEXT(SETUP, 2, "break1"); @@ -4916,6 +4972,7 @@ retriable:  	card->options.ipa4.supported_funcs = 0;  	card->options.adp.supported_funcs = 0; +	card->options.sbp.supported_funcs = 0;  	card->info.diagass_support = 0;  	qeth_query_ipassists(card, QETH_PROT_IPV4);  	if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) @@ -5096,7 +5153,7 @@ void qeth_dbf_longtext(debug_info_t *id, int level, char *fmt, ...)  	char dbf_txt_buf[32];  	va_list args; -	if (level > id->level) +	if (!debug_level_enabled(id, level))  		return;  	va_start(args, fmt);  	vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args); @@ -5602,11 +5659,66 @@ void qeth_core_get_drvinfo(struct net_device *dev,  }  EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); +/* Helper function to fill 'advertizing' and 'supported' which are the same. */ +/* Autoneg and full-duplex are supported and advertized uncondionally.	     */ +/* Always advertize and support all speeds up to specified, and only one     */ +/* specified port type.							     */ +static void qeth_set_ecmd_adv_sup(struct ethtool_cmd *ecmd, +				int maxspeed, int porttype) +{ +	int port_sup, port_adv, spd_sup, spd_adv; + +	switch (porttype) { +	case PORT_TP: +		port_sup = SUPPORTED_TP; +		port_adv = ADVERTISED_TP; +		break; +	case PORT_FIBRE: +		port_sup = SUPPORTED_FIBRE; +		port_adv = ADVERTISED_FIBRE; +		break; +	default: +		port_sup = SUPPORTED_TP; +		port_adv = ADVERTISED_TP; +		WARN_ON_ONCE(1); +	} + +	/* "Fallthrough" case'es ordered from high to low result in setting  */ +	/* flags cumulatively, starting from the specified speed and down to */ +	/* the lowest possible.						     */ +	spd_sup = 0; +	spd_adv = 0; +	switch (maxspeed) { +	case SPEED_10000: +		spd_sup |= SUPPORTED_10000baseT_Full; +		spd_adv |= ADVERTISED_10000baseT_Full; +	case SPEED_1000: +		spd_sup |= SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; +		spd_adv |= ADVERTISED_1000baseT_Half | +						ADVERTISED_1000baseT_Full; +	case SPEED_100: +		spd_sup |= SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; +		spd_adv |= ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; +	case SPEED_10: +		spd_sup |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; +		spd_adv |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; +	break; +	default: +		spd_sup = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; +		spd_adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; +		WARN_ON_ONCE(1); +	} +	ecmd->advertising = ADVERTISED_Autoneg | port_adv | spd_adv; +	ecmd->supported = SUPPORTED_Autoneg | port_sup | spd_sup; +} +  int qeth_core_ethtool_get_settings(struct net_device *netdev,  					struct ethtool_cmd *ecmd)  {  	struct qeth_card *card = netdev->ml_priv;  	enum qeth_link_types link_type; +	struct carrier_info carrier_info; +	u32 speed;  	if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))  		link_type = QETH_LINK_TYPE_10GBIT_ETH; @@ -5614,79 +5726,93 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,  		link_type = card->info.link_type;  	ecmd->transceiver = XCVR_INTERNAL; -	ecmd->supported = SUPPORTED_Autoneg; -	ecmd->advertising = ADVERTISED_Autoneg;  	ecmd->duplex = DUPLEX_FULL;  	ecmd->autoneg = AUTONEG_ENABLE;  	switch (link_type) {  	case QETH_LINK_TYPE_FAST_ETH:  	case QETH_LINK_TYPE_LANE_ETH100: -		ecmd->supported |= SUPPORTED_10baseT_Half | -					SUPPORTED_10baseT_Full | -					SUPPORTED_100baseT_Half | -					SUPPORTED_100baseT_Full | -					SUPPORTED_TP; -		ecmd->advertising |= ADVERTISED_10baseT_Half | -					ADVERTISED_10baseT_Full | -					ADVERTISED_100baseT_Half | -					ADVERTISED_100baseT_Full | -					ADVERTISED_TP; -		ecmd->speed = SPEED_100; +		qeth_set_ecmd_adv_sup(ecmd, SPEED_100, PORT_TP); +		speed = SPEED_100;  		ecmd->port = PORT_TP;  		break;  	case QETH_LINK_TYPE_GBIT_ETH:  	case QETH_LINK_TYPE_LANE_ETH1000: -		ecmd->supported |= SUPPORTED_10baseT_Half | -					SUPPORTED_10baseT_Full | -					SUPPORTED_100baseT_Half | -					SUPPORTED_100baseT_Full | -					SUPPORTED_1000baseT_Half | -					SUPPORTED_1000baseT_Full | -					SUPPORTED_FIBRE; -		ecmd->advertising |= ADVERTISED_10baseT_Half | -					ADVERTISED_10baseT_Full | -					ADVERTISED_100baseT_Half | -					ADVERTISED_100baseT_Full | -					ADVERTISED_1000baseT_Half | -					ADVERTISED_1000baseT_Full | -					ADVERTISED_FIBRE; -		ecmd->speed = SPEED_1000; +		qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE); +		speed = SPEED_1000;  		ecmd->port = PORT_FIBRE;  		break;  	case QETH_LINK_TYPE_10GBIT_ETH: -		ecmd->supported |= SUPPORTED_10baseT_Half | -					SUPPORTED_10baseT_Full | -					SUPPORTED_100baseT_Half | -					SUPPORTED_100baseT_Full | -					SUPPORTED_1000baseT_Half | -					SUPPORTED_1000baseT_Full | -					SUPPORTED_10000baseT_Full | -					SUPPORTED_FIBRE; -		ecmd->advertising |= ADVERTISED_10baseT_Half | -					ADVERTISED_10baseT_Full | -					ADVERTISED_100baseT_Half | -					ADVERTISED_100baseT_Full | -					ADVERTISED_1000baseT_Half | -					ADVERTISED_1000baseT_Full | -					ADVERTISED_10000baseT_Full | -					ADVERTISED_FIBRE; -		ecmd->speed = SPEED_10000; +		qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE); +		speed = SPEED_10000;  		ecmd->port = PORT_FIBRE;  		break;  	default: -		ecmd->supported |= SUPPORTED_10baseT_Half | -					SUPPORTED_10baseT_Full | -					SUPPORTED_TP; -		ecmd->advertising |= ADVERTISED_10baseT_Half | -					ADVERTISED_10baseT_Full | -					ADVERTISED_TP; -		ecmd->speed = SPEED_10; +		qeth_set_ecmd_adv_sup(ecmd, SPEED_10, PORT_TP); +		speed = SPEED_10; +		ecmd->port = PORT_TP; +	} +	ethtool_cmd_speed_set(ecmd, speed); + +	/* Check if we can obtain more accurate information.	 */ +	/* If QUERY_CARD_INFO command is not supported or fails, */ +	/* just return the heuristics that was filled above.	 */ +	if (qeth_query_card_info(card, &carrier_info) != 0) +		return 0; + +	netdev_dbg(netdev, +	"card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n", +			carrier_info.card_type, +			carrier_info.port_mode, +			carrier_info.port_speed); + +	/* Update attributes for which we've obtained more authoritative */ +	/* information, leave the rest the way they where filled above.  */ +	switch (carrier_info.card_type) { +	case CARD_INFO_TYPE_1G_COPPER_A: +	case CARD_INFO_TYPE_1G_COPPER_B: +		qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_TP);  		ecmd->port = PORT_TP; +		break; +	case CARD_INFO_TYPE_1G_FIBRE_A: +	case CARD_INFO_TYPE_1G_FIBRE_B: +		qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE); +		ecmd->port = PORT_FIBRE; +		break; +	case CARD_INFO_TYPE_10G_FIBRE_A: +	case CARD_INFO_TYPE_10G_FIBRE_B: +		qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE); +		ecmd->port = PORT_FIBRE; +		break; +	} + +	switch (carrier_info.port_mode) { +	case CARD_INFO_PORTM_FULLDUPLEX: +		ecmd->duplex = DUPLEX_FULL; +		break; +	case CARD_INFO_PORTM_HALFDUPLEX: +		ecmd->duplex = DUPLEX_HALF; +		break; +	} + +	switch (carrier_info.port_speed) { +	case CARD_INFO_PORTS_10M: +		speed = SPEED_10; +		break; +	case CARD_INFO_PORTS_100M: +		speed = SPEED_100; +		break; +	case CARD_INFO_PORTS_1G: +		speed = SPEED_1000; +		break; +	case CARD_INFO_PORTS_10G: +		speed = SPEED_10000; +		break;  	} +	ethtool_cmd_speed_set(ecmd, speed);  	return 0;  } @@ -5708,7 +5834,7 @@ static int __init qeth_core_init(void)  	if (rc)  		goto out_err;  	qeth_core_root_dev = root_device_register("qeth"); -	rc = PTR_RET(qeth_core_root_dev); +	rc = PTR_ERR_OR_ZERO(qeth_core_root_dev);  	if (rc)  		goto register_err;  	qeth_core_header_cache = kmem_cache_create("qeth_hdr", diff --git a/drivers/s390/net/qeth_core_mpc.c b/drivers/s390/net/qeth_core_mpc.c index 06c55780005..7b55768a959 100644 --- a/drivers/s390/net/qeth_core_mpc.c +++ b/drivers/s390/net/qeth_core_mpc.c @@ -249,10 +249,12 @@ static struct ipa_cmd_names qeth_ipa_cmd_names[] = {  	{IPA_CMD_DELIP,		"delip"},  	{IPA_CMD_SETADAPTERPARMS, "setadapterparms"},  	{IPA_CMD_SET_DIAG_ASS,	"set_diag_ass"}, +	{IPA_CMD_SETBRIDGEPORT,	"set_bridge_port"},  	{IPA_CMD_CREATE_ADDR,	"create_addr"},  	{IPA_CMD_DESTROY_ADDR,	"destroy_addr"},  	{IPA_CMD_REGISTER_LOCAL_ADDR,	"register_local_addr"},  	{IPA_CMD_UNREGISTER_LOCAL_ADDR,	"unregister_local_addr"}, +	{IPA_CMD_ADDRESS_CHANGE_NOTIF, "address_change_notification"},  	{IPA_CMD_UNKNOWN,	"unknown"},  }; diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 07085d55f9a..cf6a90ed42a 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -104,10 +104,12 @@ enum qeth_ipa_cmds {  	IPA_CMD_DELIP			= 0xb7,  	IPA_CMD_SETADAPTERPARMS		= 0xb8,  	IPA_CMD_SET_DIAG_ASS		= 0xb9, +	IPA_CMD_SETBRIDGEPORT		= 0xbe,  	IPA_CMD_CREATE_ADDR		= 0xc3,  	IPA_CMD_DESTROY_ADDR		= 0xc4,  	IPA_CMD_REGISTER_LOCAL_ADDR	= 0xd1,  	IPA_CMD_UNREGISTER_LOCAL_ADDR	= 0xd2, +	IPA_CMD_ADDRESS_CHANGE_NOTIF	= 0xd3,  	IPA_CMD_UNKNOWN			= 0x00  }; @@ -274,7 +276,24 @@ enum qeth_ipa_set_access_mode_rc {  	SET_ACCESS_CTRL_RC_REFLREL_FAILED	= 0x0024,  	SET_ACCESS_CTRL_RC_REFLREL_DEACT_FAILED	= 0x0028,  }; - +enum qeth_card_info_card_type { +	CARD_INFO_TYPE_1G_COPPER_A	= 0x61, +	CARD_INFO_TYPE_1G_FIBRE_A	= 0x71, +	CARD_INFO_TYPE_10G_FIBRE_A	= 0x91, +	CARD_INFO_TYPE_1G_COPPER_B	= 0xb1, +	CARD_INFO_TYPE_1G_FIBRE_B	= 0xa1, +	CARD_INFO_TYPE_10G_FIBRE_B	= 0xc1, +}; +enum qeth_card_info_port_mode { +	CARD_INFO_PORTM_HALFDUPLEX	= 0x0002, +	CARD_INFO_PORTM_FULLDUPLEX	= 0x0003, +}; +enum qeth_card_info_port_speed { +	CARD_INFO_PORTS_10M		= 0x00000005, +	CARD_INFO_PORTS_100M		= 0x00000006, +	CARD_INFO_PORTS_1G		= 0x00000007, +	CARD_INFO_PORTS_10G		= 0x00000008, +};  /* (SET)DELIP(M) IPA stuff ***************************************************/  struct qeth_ipacmd_setdelip4 { @@ -404,6 +423,14 @@ struct qeth_qoat_priv {  	char *buffer;  }; +struct qeth_query_card_info { +	__u8	card_type; +	__u8	reserved1; +	__u16	port_mode; +	__u32	port_speed; +	__u32	reserved2; +}; +  struct qeth_ipacmd_setadpparms_hdr {  	__u32 supp_hw_cmds;  	__u32 reserved1; @@ -424,6 +451,7 @@ struct qeth_ipacmd_setadpparms {  		struct qeth_snmp_cmd snmp;  		struct qeth_set_access_ctrl set_access_ctrl;  		struct qeth_query_oat query_oat; +		struct qeth_query_card_info card_info;  		__u32 mode;  	} data;  } __attribute__ ((packed)); @@ -474,6 +502,124 @@ struct qeth_ipacmd_diagass {  	__u8   cdata[64];  } __attribute__ ((packed)); +/* SETBRIDGEPORT IPA Command:	 *********************************************/ +enum qeth_ipa_sbp_cmd { +	IPA_SBP_QUERY_COMMANDS_SUPPORTED	= 0x00000000L, +	IPA_SBP_RESET_BRIDGE_PORT_ROLE		= 0x00000001L, +	IPA_SBP_SET_PRIMARY_BRIDGE_PORT		= 0x00000002L, +	IPA_SBP_SET_SECONDARY_BRIDGE_PORT	= 0x00000004L, +	IPA_SBP_QUERY_BRIDGE_PORTS		= 0x00000008L, +	IPA_SBP_BRIDGE_PORT_STATE_CHANGE	= 0x00000010L, +}; + +struct net_if_token { +	__u16 devnum; +	__u8 cssid; +	__u8 iid; +	__u8 ssid; +	__u8 chpid; +	__u16 chid; +} __packed; + +struct mac_addr_lnid { +	__u8 mac[6]; +	__u16 lnid; +} __packed; + +struct qeth_ipacmd_sbp_hdr { +	__u32 supported_sbp_cmds; +	__u32 enabled_sbp_cmds; +	__u16 cmdlength; +	__u16 reserved1; +	__u32 command_code; +	__u16 return_code; +	__u8  used_total; +	__u8  seq_no; +	__u32 reserved2; +} __packed; + +struct qeth_sbp_query_cmds_supp { +	__u32 supported_cmds; +	__u32 reserved; +} __packed; + +struct qeth_sbp_reset_role { +} __packed; + +struct qeth_sbp_set_primary { +	struct net_if_token token; +} __packed; + +struct qeth_sbp_set_secondary { +} __packed; + +struct qeth_sbp_port_entry { +		__u8 role; +		__u8 state; +		__u8 reserved1; +		__u8 reserved2; +		struct net_if_token token; +} __packed; + +struct qeth_sbp_query_ports { +	__u8 primary_bp_supported; +	__u8 secondary_bp_supported; +	__u8 num_entries; +	__u8 entry_length; +	struct qeth_sbp_port_entry entry[]; +} __packed; + +struct qeth_sbp_state_change { +	__u8 primary_bp_supported; +	__u8 secondary_bp_supported; +	__u8 num_entries; +	__u8 entry_length; +	struct qeth_sbp_port_entry entry[]; +} __packed; + +struct qeth_ipacmd_setbridgeport { +	struct qeth_ipacmd_sbp_hdr hdr; +	union { +		struct qeth_sbp_query_cmds_supp query_cmds_supp; +		struct qeth_sbp_reset_role reset_role; +		struct qeth_sbp_set_primary set_primary; +		struct qeth_sbp_set_secondary set_secondary; +		struct qeth_sbp_query_ports query_ports; +		struct qeth_sbp_state_change state_change; +	} data; +} __packed; + +/* ADDRESS_CHANGE_NOTIFICATION adapter-initiated "command" *******************/ +/* Bitmask for entry->change_code. Both bits may be raised.		     */ +enum qeth_ipa_addr_change_code { +	IPA_ADDR_CHANGE_CODE_VLANID		= 0x01, +	IPA_ADDR_CHANGE_CODE_MACADDR		= 0x02, +	IPA_ADDR_CHANGE_CODE_REMOVAL		= 0x80,	/* else addition */ +}; +enum qeth_ipa_addr_change_retcode { +	IPA_ADDR_CHANGE_RETCODE_OK		= 0x0000, +	IPA_ADDR_CHANGE_RETCODE_LOSTEVENTS	= 0x0010, +}; +enum qeth_ipa_addr_change_lostmask { +	IPA_ADDR_CHANGE_MASK_OVERFLOW		= 0x01, +	IPA_ADDR_CHANGE_MASK_STATECHANGE	= 0x02, +}; + +struct qeth_ipacmd_addr_change_entry { +	struct net_if_token token; +	struct mac_addr_lnid addr_lnid; +	__u8 change_code; +	__u8 reserved1; +	__u16 reserved2; +} __packed; + +struct qeth_ipacmd_addr_change { +	__u8 lost_event_mask; +	__u8 reserved; +	__u16 num_entries; +	struct qeth_ipacmd_addr_change_entry entry[]; +} __packed; +  /* Header for each IPA command */  struct qeth_ipacmd_hdr {  	__u8   command; @@ -503,6 +649,8 @@ struct qeth_ipa_cmd {  		struct qeth_ipacmd_setadpparms		setadapterparms;  		struct qeth_set_routing			setrtg;  		struct qeth_ipacmd_diagass		diagass; +		struct qeth_ipacmd_setbridgeport	sbp; +		struct qeth_ipacmd_addr_change		addrchange;  	} data;  } __attribute__ ((packed)); diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index 425c0ecf1f3..8a25a2be989 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -217,6 +217,10 @@ static ssize_t qeth_dev_prioqing_show(struct device *dev,  		return sprintf(buf, "%s\n", "by precedence");  	case QETH_PRIO_Q_ING_TOS:  		return sprintf(buf, "%s\n", "by type of service"); +	case QETH_PRIO_Q_ING_SKB: +		return sprintf(buf, "%s\n", "by skb-priority"); +	case QETH_PRIO_Q_ING_VLAN: +		return sprintf(buf, "%s\n", "by VLAN headers");  	default:  		return sprintf(buf, "always queue %i\n",  			       card->qdio.default_out_queue); @@ -250,11 +254,23 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev,  	}  	tmp = strsep((char **) &buf, "\n"); -	if (!strcmp(tmp, "prio_queueing_prec")) +	if (!strcmp(tmp, "prio_queueing_prec")) {  		card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_PREC; -	else if (!strcmp(tmp, "prio_queueing_tos")) +		card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; +	} else if (!strcmp(tmp, "prio_queueing_skb")) { +		card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_SKB; +		card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; +	} else if (!strcmp(tmp, "prio_queueing_tos")) {  		card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_TOS; -	else if (!strcmp(tmp, "no_prio_queueing:0")) { +		card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; +	} else if (!strcmp(tmp, "prio_queueing_vlan")) { +		if (!card->options.layer2) { +			rc = -ENOTSUPP; +			goto out; +		} +		card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_VLAN; +		card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; +	} else if (!strcmp(tmp, "no_prio_queueing:0")) {  		card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING;  		card->qdio.default_out_queue = 0;  	} else if (!strcmp(tmp, "no_prio_queueing:1")) { diff --git a/drivers/s390/net/qeth_l2.h b/drivers/s390/net/qeth_l2.h new file mode 100644 index 00000000000..0767556404b --- /dev/null +++ b/drivers/s390/net/qeth_l2.h @@ -0,0 +1,15 @@ +/* + *    Copyright IBM Corp. 2013 + *    Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com> + */ + +#ifndef __QETH_L2_H__ +#define __QETH_L2_H__ + +#include "qeth_core.h" + +int qeth_l2_create_device_attributes(struct device *); +void qeth_l2_remove_device_attributes(struct device *); +void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); + +#endif /* __QETH_L2_H__ */ diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index ec8ccdae7ab..5ef5b4f4575 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -21,6 +21,7 @@  #include <linux/list.h>  #include "qeth_core.h" +#include "qeth_l2.h"  static int qeth_l2_set_offline(struct ccwgroup_device *);  static int qeth_l2_stop(struct net_device *); @@ -32,6 +33,11 @@ static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *,  					    unsigned long));  static void qeth_l2_set_multicast_list(struct net_device *);  static int qeth_l2_recover(void *); +static void qeth_bridgeport_query_support(struct qeth_card *card); +static void qeth_bridge_state_change(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd); +static void qeth_bridge_host_event(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd);  static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)  { @@ -235,7 +241,7 @@ static inline int qeth_l2_get_cast_type(struct qeth_card *card,  }  static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, -			struct sk_buff *skb, int ipv, int cast_type) +			struct sk_buff *skb, int cast_type)  {  	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb); @@ -719,15 +725,20 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  	int elements = 0;  	struct qeth_card *card = dev->ml_priv;  	struct sk_buff *new_skb = skb; -	int ipv = qeth_get_ip_version(skb);  	int cast_type = qeth_l2_get_cast_type(card, skb); -	struct qeth_qdio_out_q *queue = card->qdio.out_qs -		[qeth_get_priority_queue(card, skb, ipv, cast_type)]; +	struct qeth_qdio_out_q *queue;  	int tx_bytes = skb->len;  	int data_offset = -1;  	int elements_needed = 0;  	int hd_len = 0; +	if (card->qdio.do_prio_queueing || (cast_type && +					card->info.is_multicast_different)) +		queue = card->qdio.out_qs[qeth_get_priority_queue(card, skb, +					qeth_get_ip_version(skb), cast_type)]; +	else +		queue = card->qdio.out_qs[card->qdio.default_out_queue]; +  	if ((card->state != CARD_STATE_UP) || !card->lan_online) {  		card->stats.tx_carrier_errors++;  		goto tx_drop; @@ -756,7 +767,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  				goto tx_drop;  			elements_needed++;  			skb_reset_mac_header(new_skb); -			qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type); +			qeth_l2_fill_header(card, hdr, new_skb, cast_type);  			hdr->hdr.l2.pkt_length = new_skb->len;  			memcpy(((char *)hdr) + sizeof(struct qeth_hdr),  				skb_mac_header(new_skb), ETH_HLEN); @@ -769,7 +780,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  			hdr = (struct qeth_hdr *)skb_push(new_skb,  						sizeof(struct qeth_hdr));  			skb_set_mac_header(new_skb, sizeof(struct qeth_hdr)); -			qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type); +			qeth_l2_fill_header(card, hdr, new_skb, cast_type);  		}  	} @@ -880,6 +891,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)  {  	struct qeth_card *card = dev_get_drvdata(&gdev->dev); +	qeth_l2_create_device_attributes(&gdev->dev);  	INIT_LIST_HEAD(&card->vid_list);  	INIT_LIST_HEAD(&card->mc_list);  	card->options.layer2 = 1; @@ -891,6 +903,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)  {  	struct qeth_card *card = dev_get_drvdata(&cgdev->dev); +	qeth_l2_remove_device_attributes(&cgdev->dev);  	qeth_set_allowed_threads(card, 0, 1);  	wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); @@ -956,10 +969,9 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)  	card->dev->watchdog_timeo = QETH_TX_TIMEOUT;  	card->dev->mtu = card->info.initial_mtu;  	card->dev->netdev_ops = &qeth_l2_netdev_ops; -	if (card->info.type != QETH_CARD_TYPE_OSN) -		SET_ETHTOOL_OPS(card->dev, &qeth_l2_ethtool_ops); -	else -		SET_ETHTOOL_OPS(card->dev, &qeth_l2_osn_ops); +	card->dev->ethtool_ops = +		(card->info.type != QETH_CARD_TYPE_OSN) ? +		&qeth_l2_ethtool_ops : &qeth_l2_osn_ops;  	card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;  	card->info.broadcast_capable = 1;  	qeth_l2_request_initial_mac(card); @@ -986,6 +998,10 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)  		rc = -ENODEV;  		goto out_remove;  	} +	qeth_bridgeport_query_support(card); +	if (card->options.sbp.supported_funcs) +		dev_info(&card->gdev->dev, +		"The device represents a HiperSockets Bridge Capable Port\n");  	qeth_trace_features(card);  	if (!card->dev && qeth_l2_setup_netdev(card)) { @@ -1003,6 +1019,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)  	} else  		card->info.hwtrap = 0; +	qeth_l2_setup_bridgeport_attrs(card); +  	card->state = CARD_STATE_HARDSETUP;  	memset(&card->rx, 0, sizeof(struct qeth_rx));  	qeth_print_status_message(card); @@ -1077,6 +1095,7 @@ out_remove:  	ccw_device_set_offline(CARD_DDEV(card));  	ccw_device_set_offline(CARD_WDEV(card));  	ccw_device_set_offline(CARD_RDEV(card)); +	qdio_free(CARD_DDEV(card));  	if (recover_flag == CARD_STATE_RECOVER)  		card->state = CARD_STATE_RECOVER;  	else @@ -1118,6 +1137,7 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev,  		rc = (rc2) ? rc2 : rc3;  	if (rc)  		QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); +	qdio_free(CARD_DDEV(card));  	if (recover_flag == CARD_STATE_UP)  		card->state = CARD_STATE_RECOVER;  	/* let user_space know that device is offline */ @@ -1180,6 +1200,7 @@ static void qeth_l2_shutdown(struct ccwgroup_device *gdev)  		qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);  	qeth_qdio_clear_card(card, 0);  	qeth_clear_qdio_buffers(card); +	qdio_free(CARD_DDEV(card));  }  static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev) @@ -1228,6 +1249,26 @@ out:  	return rc;  } +/* Returns zero if the command is successfully "consumed" */ +static int qeth_l2_control_event(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd) +{ +	switch (cmd->hdr.command) { +	case IPA_CMD_SETBRIDGEPORT: +		if (cmd->data.sbp.hdr.command_code == +				IPA_SBP_BRIDGE_PORT_STATE_CHANGE) { +			qeth_bridge_state_change(card, cmd); +			return 0; +		} else +			return 1; +	case IPA_CMD_ADDRESS_CHANGE_NOTIF: +		qeth_bridge_host_event(card, cmd); +		return 0; +	default: +		return 1; +	} +} +  struct qeth_discipline qeth_l2_discipline = {  	.start_poll = qeth_qdio_start_poll,  	.input_handler = (qdio_handler_t *) qeth_qdio_input_handler, @@ -1241,6 +1282,7 @@ struct qeth_discipline qeth_l2_discipline = {  	.freeze = qeth_l2_pm_suspend,  	.thaw = qeth_l2_pm_resume,  	.restore = qeth_l2_pm_resume, +	.control_event_handler = qeth_l2_control_event,  };  EXPORT_SYMBOL_GPL(qeth_l2_discipline); @@ -1347,6 +1389,594 @@ void qeth_osn_deregister(struct net_device *dev)  }  EXPORT_SYMBOL(qeth_osn_deregister); +/* SETBRIDGEPORT support, async notifications */ + +enum qeth_an_event_type {anev_reg_unreg, anev_abort, anev_reset}; + +/** + * qeth_bridge_emit_host_event() - bridgeport address change notification + * @card:  qeth_card structure pointer, for udev events. + * @evtype:  "normal" register/unregister, or abort, or reset. For abort + *	      and reset token and addr_lnid are unused and may be NULL. + * @code:  event bitmask: high order bit 0x80 value 1 means removal of an + *			  object, 0 - addition of an object. + *			  0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC. + * @token: "network token" structure identifying physical address of the port. + * @addr_lnid: pointer to structure with MAC address and VLAN ID. + * + * This function is called when registrations and deregistrations are + * reported by the hardware, and also when notifications are enabled - + * for all currently registered addresses. + */ +static void qeth_bridge_emit_host_event(struct qeth_card *card, +	enum qeth_an_event_type evtype, +	u8 code, struct net_if_token *token, struct mac_addr_lnid *addr_lnid) +{ +	char str[7][32]; +	char *env[8]; +	int i = 0; + +	switch (evtype) { +	case anev_reg_unreg: +		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=%s", +				(code & IPA_ADDR_CHANGE_CODE_REMOVAL) +				? "deregister" : "register"); +		env[i] = str[i]; i++; +		if (code & IPA_ADDR_CHANGE_CODE_VLANID) { +			snprintf(str[i], sizeof(str[i]), "VLAN=%d", +				addr_lnid->lnid); +			env[i] = str[i]; i++; +		} +		if (code & IPA_ADDR_CHANGE_CODE_MACADDR) { +			snprintf(str[i], sizeof(str[i]), "MAC=%pM6", +				&addr_lnid->mac); +			env[i] = str[i]; i++; +		} +		snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x", +			token->cssid, token->ssid, token->devnum); +		env[i] = str[i]; i++; +		snprintf(str[i], sizeof(str[i]), "NTOK_IID=%02x", token->iid); +		env[i] = str[i]; i++; +		snprintf(str[i], sizeof(str[i]), "NTOK_CHPID=%02x", +				token->chpid); +		env[i] = str[i]; i++; +		snprintf(str[i], sizeof(str[i]), "NTOK_CHID=%04x", token->chid); +		env[i] = str[i]; i++; +		break; +	case anev_abort: +		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=abort"); +		env[i] = str[i]; i++; +		break; +	case anev_reset: +		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=reset"); +		env[i] = str[i]; i++; +		break; +	} +	env[i] = NULL; +	kobject_uevent_env(&card->gdev->dev.kobj, KOBJ_CHANGE, env); +} + +struct qeth_bridge_state_data { +	struct work_struct worker; +	struct qeth_card *card; +	struct qeth_sbp_state_change qports; +}; + +static void qeth_bridge_state_change_worker(struct work_struct *work) +{ +	struct qeth_bridge_state_data *data = +		container_of(work, struct qeth_bridge_state_data, worker); +	/* We are only interested in the first entry - local port */ +	struct qeth_sbp_port_entry *entry = &data->qports.entry[0]; +	char env_locrem[32]; +	char env_role[32]; +	char env_state[32]; +	char *env[] = { +		env_locrem, +		env_role, +		env_state, +		NULL +	}; + +	/* Role should not change by itself, but if it did, */ +	/* information from the hardware is authoritative.  */ +	mutex_lock(&data->card->conf_mutex); +	data->card->options.sbp.role = entry->role; +	mutex_unlock(&data->card->conf_mutex); + +	snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange"); +	snprintf(env_role, sizeof(env_role), "ROLE=%s", +		(entry->role == QETH_SBP_ROLE_NONE) ? "none" : +		(entry->role == QETH_SBP_ROLE_PRIMARY) ? "primary" : +		(entry->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" : +		"<INVALID>"); +	snprintf(env_state, sizeof(env_state), "STATE=%s", +		(entry->state == QETH_SBP_STATE_INACTIVE) ? "inactive" : +		(entry->state == QETH_SBP_STATE_STANDBY) ? "standby" : +		(entry->state == QETH_SBP_STATE_ACTIVE) ? "active" : +		"<INVALID>"); +	kobject_uevent_env(&data->card->gdev->dev.kobj, +				KOBJ_CHANGE, env); +	kfree(data); +} + +static void qeth_bridge_state_change(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd) +{ +	struct qeth_sbp_state_change *qports = +		 &cmd->data.sbp.data.state_change; +	struct qeth_bridge_state_data *data; +	int extrasize; + +	QETH_CARD_TEXT(card, 2, "brstchng"); +	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { +		QETH_CARD_TEXT_(card, 2, "BPsz%.8d", qports->entry_length); +		return; +	} +	extrasize = sizeof(struct qeth_sbp_port_entry) * qports->num_entries; +	data = kzalloc(sizeof(struct qeth_bridge_state_data) + extrasize, +		GFP_ATOMIC); +	if (!data) { +		QETH_CARD_TEXT(card, 2, "BPSalloc"); +		return; +	} +	INIT_WORK(&data->worker, qeth_bridge_state_change_worker); +	data->card = card; +	memcpy(&data->qports, qports, +			sizeof(struct qeth_sbp_state_change) + extrasize); +	queue_work(qeth_wq, &data->worker); +} + +struct qeth_bridge_host_data { +	struct work_struct worker; +	struct qeth_card *card; +	struct qeth_ipacmd_addr_change hostevs; +}; + +static void qeth_bridge_host_event_worker(struct work_struct *work) +{ +	struct qeth_bridge_host_data *data = +		container_of(work, struct qeth_bridge_host_data, worker); +	int i; + +	if (data->hostevs.lost_event_mask) { +		dev_info(&data->card->gdev->dev, +"Address notification from the HiperSockets Bridge Port stopped %s (%s)\n", +			data->card->dev->name, +			(data->hostevs.lost_event_mask == 0x01) +			? "Overflow" +			: (data->hostevs.lost_event_mask == 0x02) +			? "Bridge port state change" +			: "Unknown reason"); +		mutex_lock(&data->card->conf_mutex); +		data->card->options.sbp.hostnotification = 0; +		mutex_unlock(&data->card->conf_mutex); +		qeth_bridge_emit_host_event(data->card, anev_abort, +			0, NULL, NULL); +	} else +		for (i = 0; i < data->hostevs.num_entries; i++) { +			struct qeth_ipacmd_addr_change_entry *entry = +					&data->hostevs.entry[i]; +			qeth_bridge_emit_host_event(data->card, +					anev_reg_unreg, +					entry->change_code, +					&entry->token, &entry->addr_lnid); +		} +	kfree(data); +} + +static void qeth_bridge_host_event(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd) +{ +	struct qeth_ipacmd_addr_change *hostevs = +		 &cmd->data.addrchange; +	struct qeth_bridge_host_data *data; +	int extrasize; + +	QETH_CARD_TEXT(card, 2, "brhostev"); +	if (cmd->hdr.return_code != 0x0000) { +		if (cmd->hdr.return_code == 0x0010) { +			if (hostevs->lost_event_mask == 0x00) +				hostevs->lost_event_mask = 0xff; +		} else { +			QETH_CARD_TEXT_(card, 2, "BPHe%04x", +				cmd->hdr.return_code); +			return; +		} +	} +	extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) * +						hostevs->num_entries; +	data = kzalloc(sizeof(struct qeth_bridge_host_data) + extrasize, +		GFP_ATOMIC); +	if (!data) { +		QETH_CARD_TEXT(card, 2, "BPHalloc"); +		return; +	} +	INIT_WORK(&data->worker, qeth_bridge_host_event_worker); +	data->card = card; +	memcpy(&data->hostevs, hostevs, +			sizeof(struct qeth_ipacmd_addr_change) + extrasize); +	queue_work(qeth_wq, &data->worker); +} + +/* SETBRIDGEPORT support; sending commands */ + +struct _qeth_sbp_cbctl { +	u16 ipa_rc; +	u16 cmd_rc; +	union { +		u32 supported; +		struct { +			enum qeth_sbp_roles *role; +			enum qeth_sbp_states *state; +		} qports; +	} data; +}; + +/** + * qeth_bridgeport_makerc() - derive "traditional" error from hardware codes. + * @card:		      qeth_card structure pointer, for debug messages. + * @cbctl:		      state structure with hardware return codes. + * @setcmd:		      IPA command code + * + * Returns negative errno-compatible error indication or 0 on success. + */ +static int qeth_bridgeport_makerc(struct qeth_card *card, +	struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd) +{ +	int rc; + +	switch (cbctl->ipa_rc) { +	case IPA_RC_SUCCESS: +		switch (cbctl->cmd_rc) { +		case 0x0000: +			rc = 0; +			break; +		case 0x0004: +			rc = -ENOSYS; +			break; +		case 0x000C: /* Not configured as bridge Port */ +			rc = -ENODEV; /* maybe not the best code here? */ +			dev_err(&card->gdev->dev, +	"The HiperSockets device is not configured as a Bridge Port\n"); +			break; +		case 0x0014: /* Another device is Primary */ +			switch (setcmd) { +			case IPA_SBP_SET_PRIMARY_BRIDGE_PORT: +				rc = -EEXIST; +				dev_err(&card->gdev->dev, +	"The HiperSockets LAN already has a primary Bridge Port\n"); +				break; +			case IPA_SBP_SET_SECONDARY_BRIDGE_PORT: +				rc = -EBUSY; +				dev_err(&card->gdev->dev, +	"The HiperSockets device is already a primary Bridge Port\n"); +				break; +			default: +				rc = -EIO; +			} +			break; +		case 0x0018: /* This device is currently Secondary */ +			rc = -EBUSY; +			dev_err(&card->gdev->dev, +	"The HiperSockets device is already a secondary Bridge Port\n"); +			break; +		case 0x001C: /* Limit for Secondary devices reached */ +			rc = -EEXIST; +			dev_err(&card->gdev->dev, +	"The HiperSockets LAN cannot have more secondary Bridge Ports\n"); +			break; +		case 0x0024: /* This device is currently Primary */ +			rc = -EBUSY; +			dev_err(&card->gdev->dev, +	"The HiperSockets device is already a primary Bridge Port\n"); +			break; +		case 0x0020: /* Not authorized by zManager */ +			rc = -EACCES; +			dev_err(&card->gdev->dev, +	"The HiperSockets device is not authorized to be a Bridge Port\n"); +			break; +		default: +			rc = -EIO; +		} +		break; +	case IPA_RC_NOTSUPP: +		rc = -ENOSYS; +		break; +	case IPA_RC_UNSUPPORTED_COMMAND: +		rc = -ENOSYS; +		break; +	default: +		rc = -EIO; +	} +	if (rc) { +		QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc); +		QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc); +	} +	return rc; +} + +static int qeth_bridgeport_query_support_cb(struct qeth_card *card, +	struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; +	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; +	QETH_CARD_TEXT(card, 2, "brqsupcb"); +	cbctl->ipa_rc = cmd->hdr.return_code; +	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; +	if ((cbctl->ipa_rc == 0) && (cbctl->cmd_rc == 0)) { +		cbctl->data.supported = +			cmd->data.sbp.data.query_cmds_supp.supported_cmds; +	} else { +		cbctl->data.supported = 0; +	} +	return 0; +} + +/** + * qeth_bridgeport_query_support() - store bitmask of supported subfunctions. + * @card:			     qeth_card structure pointer. + * + * Sets bitmask of supported setbridgeport subfunctions in the qeth_card + * strucutre: card->options.sbp.supported_funcs. + */ +static void qeth_bridgeport_query_support(struct qeth_card *card) +{ +	struct qeth_cmd_buffer *iob; +	struct qeth_ipa_cmd *cmd; +	struct _qeth_sbp_cbctl cbctl; + +	QETH_CARD_TEXT(card, 2, "brqsuppo"); +	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); +	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); +	cmd->data.sbp.hdr.cmdlength = +		sizeof(struct qeth_ipacmd_sbp_hdr) + +		sizeof(struct qeth_sbp_query_cmds_supp); +	cmd->data.sbp.hdr.command_code = +		IPA_SBP_QUERY_COMMANDS_SUPPORTED; +	cmd->data.sbp.hdr.used_total = 1; +	cmd->data.sbp.hdr.seq_no = 1; +	if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb, +							(void *)&cbctl) || +	    qeth_bridgeport_makerc(card, &cbctl, +					IPA_SBP_QUERY_COMMANDS_SUPPORTED)) { +		/* non-zero makerc signifies failure, and produce messages */ +		card->options.sbp.role = QETH_SBP_ROLE_NONE; +		return; +	} +	card->options.sbp.supported_funcs = cbctl.data.supported; +} + +static int qeth_bridgeport_query_ports_cb(struct qeth_card *card, +	struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; +	struct qeth_sbp_query_ports *qports = &cmd->data.sbp.data.query_ports; +	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; + +	QETH_CARD_TEXT(card, 2, "brqprtcb"); +	cbctl->ipa_rc = cmd->hdr.return_code; +	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; +	if ((cbctl->ipa_rc != 0) || (cbctl->cmd_rc != 0)) +		return 0; +	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { +		cbctl->cmd_rc = 0xffff; +		QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length); +		return 0; +	} +	/* first entry contains the state of the local port */ +	if (qports->num_entries > 0) { +		if (cbctl->data.qports.role) +			*cbctl->data.qports.role = qports->entry[0].role; +		if (cbctl->data.qports.state) +			*cbctl->data.qports.state = qports->entry[0].state; +	} +	return 0; +} + +/** + * qeth_bridgeport_query_ports() - query local bridgeport status. + * @card:			   qeth_card structure pointer. + * @role:   Role of the port: 0-none, 1-primary, 2-secondary. + * @state:  State of the port: 0-inactive, 1-standby, 2-active. + * + * Returns negative errno-compatible error indication or 0 on success. + * + * 'role' and 'state' are not updated in case of hardware operation failure. + */ +int qeth_bridgeport_query_ports(struct qeth_card *card, +	enum qeth_sbp_roles *role, enum qeth_sbp_states *state) +{ +	int rc = 0; +	struct qeth_cmd_buffer *iob; +	struct qeth_ipa_cmd *cmd; +	struct _qeth_sbp_cbctl cbctl = { +		.data = { +			.qports = { +				.role = role, +				.state = state, +			}, +		}, +	}; + +	QETH_CARD_TEXT(card, 2, "brqports"); +	if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS)) +		return -EOPNOTSUPP; +	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); +	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); +	cmd->data.sbp.hdr.cmdlength = +		sizeof(struct qeth_ipacmd_sbp_hdr); +	cmd->data.sbp.hdr.command_code = +		IPA_SBP_QUERY_BRIDGE_PORTS; +	cmd->data.sbp.hdr.used_total = 1; +	cmd->data.sbp.hdr.seq_no = 1; +	rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb, +				(void *)&cbctl); +	if (rc) +		return rc; +	rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS); +	if (rc) +		return rc; +	return 0; +} +EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports); + +static int qeth_bridgeport_set_cb(struct qeth_card *card, +	struct qeth_reply *reply, unsigned long data) +{ +	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; +	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; +	QETH_CARD_TEXT(card, 2, "brsetrcb"); +	cbctl->ipa_rc = cmd->hdr.return_code; +	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; +	return 0; +} + +/** + * qeth_bridgeport_setrole() - Assign primary role to the port. + * @card:		       qeth_card structure pointer. + * @role:		       Role to assign. + * + * Returns negative errno-compatible error indication or 0 on success. + */ +int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role) +{ +	int rc = 0; +	int cmdlength; +	struct qeth_cmd_buffer *iob; +	struct qeth_ipa_cmd *cmd; +	struct _qeth_sbp_cbctl cbctl; +	enum qeth_ipa_sbp_cmd setcmd; + +	QETH_CARD_TEXT(card, 2, "brsetrol"); +	switch (role) { +	case QETH_SBP_ROLE_NONE: +		setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE; +		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) + +			sizeof(struct qeth_sbp_reset_role); +		break; +	case QETH_SBP_ROLE_PRIMARY: +		setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT; +		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) + +			sizeof(struct qeth_sbp_set_primary); +		break; +	case QETH_SBP_ROLE_SECONDARY: +		setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT; +		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) + +			sizeof(struct qeth_sbp_set_secondary); +		break; +	default: +		return -EINVAL; +	} +	if (!(card->options.sbp.supported_funcs & setcmd)) +		return -EOPNOTSUPP; +	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); +	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); +	cmd->data.sbp.hdr.cmdlength = cmdlength; +	cmd->data.sbp.hdr.command_code = setcmd; +	cmd->data.sbp.hdr.used_total = 1; +	cmd->data.sbp.hdr.seq_no = 1; +	rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, +				(void *)&cbctl); +	if (rc) +		return rc; +	rc = qeth_bridgeport_makerc(card, &cbctl, setcmd); +	return rc; +} + +/** + * qeth_anset_makerc() - derive "traditional" error from hardware codes. + * @card:		      qeth_card structure pointer, for debug messages. + * + * Returns negative errno-compatible error indication or 0 on success. + */ +static int qeth_anset_makerc(struct qeth_card *card, int pnso_rc, u16 response) +{ +	int rc; + +	if (pnso_rc == 0) +		switch (response) { +		case 0x0001: +			rc = 0; +			break; +		case 0x0004: +		case 0x0100: +		case 0x0106: +			rc = -ENOSYS; +			dev_err(&card->gdev->dev, +				"Setting address notification failed\n"); +			break; +		case 0x0107: +			rc = -EAGAIN; +			break; +		default: +			rc = -EIO; +		} +	else +		rc = -EIO; + +	if (rc) { +		QETH_CARD_TEXT_(card, 2, "SBPp%04x", pnso_rc); +		QETH_CARD_TEXT_(card, 2, "SBPr%04x", response); +	} +	return rc; +} + +static void qeth_bridgeport_an_set_cb(void *priv, +		enum qdio_brinfo_entry_type type, void *entry) +{ +	struct qeth_card *card = (struct qeth_card *)priv; +	struct qdio_brinfo_entry_l2 *l2entry; +	u8 code; + +	if (type != l2_addr_lnid) { +		WARN_ON_ONCE(1); +		return; +	} + +	l2entry = (struct qdio_brinfo_entry_l2 *)entry; +	code = IPA_ADDR_CHANGE_CODE_MACADDR; +	if (l2entry->addr_lnid.lnid) +		code |= IPA_ADDR_CHANGE_CODE_VLANID; +	qeth_bridge_emit_host_event(card, anev_reg_unreg, code, +		(struct net_if_token *)&l2entry->nit, +		(struct mac_addr_lnid *)&l2entry->addr_lnid); +} + +/** + * qeth_bridgeport_an_set() - Enable or disable bridgeport address notification + * @card:		      qeth_card structure pointer. + * @enable:		      0 - disable, non-zero - enable notifications + * + * Returns negative errno-compatible error indication or 0 on success. + * + * On enable, emits a series of address notifications udev events for all + * currently registered hosts. + */ +int qeth_bridgeport_an_set(struct qeth_card *card, int enable) +{ +	int rc; +	u16 response; +	struct ccw_device *ddev; +	struct subchannel_id schid; + +	if (!card) +		return -EINVAL; +	if (!card->options.sbp.supported_funcs) +		return -EOPNOTSUPP; +	ddev = CARD_DDEV(card); +	ccw_device_get_schid(ddev, &schid); + +	if (enable) { +		qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL); +		rc = qdio_pnso_brinfo(schid, 1, &response, +			qeth_bridgeport_an_set_cb, card); +	} else +		rc = qdio_pnso_brinfo(schid, 0, &response, NULL, NULL); +	return qeth_anset_makerc(card, rc, response); +} +EXPORT_SYMBOL_GPL(qeth_bridgeport_an_set); +  module_init(qeth_l2_init);  module_exit(qeth_l2_exit);  MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c new file mode 100644 index 00000000000..ae1bc04b865 --- /dev/null +++ b/drivers/s390/net/qeth_l2_sys.c @@ -0,0 +1,223 @@ +/* + *    Copyright IBM Corp. 2013 + *    Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com> + */ + +#include <linux/slab.h> +#include <asm/ebcdic.h> +#include "qeth_l2.h" + +#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \ +struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store) + +static int qeth_card_hw_is_reachable(struct qeth_card *card) +{ +	return (card->state == CARD_STATE_SOFTSETUP) || +		(card->state == CARD_STATE_UP); +} + +static ssize_t qeth_bridge_port_role_state_show(struct device *dev, +				struct device_attribute *attr, char *buf, +				int show_state) +{ +	struct qeth_card *card = dev_get_drvdata(dev); +	enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE; +	int rc = 0; +	char *word; + +	if (!card) +		return -EINVAL; + +	mutex_lock(&card->conf_mutex); + +	if (qeth_card_hw_is_reachable(card) && +					card->options.sbp.supported_funcs) +		rc = qeth_bridgeport_query_ports(card, +			&card->options.sbp.role, &state); +	if (!rc) { +		if (show_state) +			switch (state) { +			case QETH_SBP_STATE_INACTIVE: +				word = "inactive"; break; +			case QETH_SBP_STATE_STANDBY: +				word = "standby"; break; +			case QETH_SBP_STATE_ACTIVE: +				word = "active"; break; +			default: +				rc = -EIO; +			} +		else +			switch (card->options.sbp.role) { +			case QETH_SBP_ROLE_NONE: +				word = "none"; break; +			case QETH_SBP_ROLE_PRIMARY: +				word = "primary"; break; +			case QETH_SBP_ROLE_SECONDARY: +				word = "secondary"; break; +			default: +				rc = -EIO; +			} +		if (rc) +			QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x", +				card->options.sbp.role, state); +		else +			rc = sprintf(buf, "%s\n", word); +	} + +	mutex_unlock(&card->conf_mutex); + +	return rc; +} + +static ssize_t qeth_bridge_port_role_show(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	return qeth_bridge_port_role_state_show(dev, attr, buf, 0); +} + +static ssize_t qeth_bridge_port_role_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	struct qeth_card *card = dev_get_drvdata(dev); +	int rc = 0; +	enum qeth_sbp_roles role; + +	if (!card) +		return -EINVAL; +	if (sysfs_streq(buf, "primary")) +		role = QETH_SBP_ROLE_PRIMARY; +	else if (sysfs_streq(buf, "secondary")) +		role = QETH_SBP_ROLE_SECONDARY; +	else if (sysfs_streq(buf, "none")) +		role = QETH_SBP_ROLE_NONE; +	else +		return -EINVAL; + +	mutex_lock(&card->conf_mutex); + +	if (qeth_card_hw_is_reachable(card)) { +		rc = qeth_bridgeport_setrole(card, role); +		if (!rc) +			card->options.sbp.role = role; +	} else +		card->options.sbp.role = role; + +	mutex_unlock(&card->conf_mutex); + +	return rc ? rc : count; +} + +static DEVICE_ATTR(bridge_role, 0644, qeth_bridge_port_role_show, +		   qeth_bridge_port_role_store); + +static ssize_t qeth_bridge_port_state_show(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	return qeth_bridge_port_role_state_show(dev, attr, buf, 1); +} + +static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show, +		   NULL); + +static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	struct qeth_card *card = dev_get_drvdata(dev); +	int enabled; + +	if (!card) +		return -EINVAL; + +	mutex_lock(&card->conf_mutex); + +	enabled = card->options.sbp.hostnotification; + +	mutex_unlock(&card->conf_mutex); + +	return sprintf(buf, "%d\n", enabled); +} + +static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	struct qeth_card *card = dev_get_drvdata(dev); +	int rc = 0; +	int enable; + +	if (!card) +		return -EINVAL; + +	if (sysfs_streq(buf, "0")) +		enable = 0; +	else if (sysfs_streq(buf, "1")) +		enable = 1; +	else +		return -EINVAL; + +	mutex_lock(&card->conf_mutex); + +	if (qeth_card_hw_is_reachable(card)) { +		rc = qeth_bridgeport_an_set(card, enable); +		if (!rc) +			card->options.sbp.hostnotification = enable; +	} else +		card->options.sbp.hostnotification = enable; + +	mutex_unlock(&card->conf_mutex); + +	return rc ? rc : count; +} + +static DEVICE_ATTR(bridge_hostnotify, 0644, +			qeth_bridgeport_hostnotification_show, +			qeth_bridgeport_hostnotification_store); + +static struct attribute *qeth_l2_bridgeport_attrs[] = { +	&dev_attr_bridge_role.attr, +	&dev_attr_bridge_state.attr, +	&dev_attr_bridge_hostnotify.attr, +	NULL, +}; + +static struct attribute_group qeth_l2_bridgeport_attr_group = { +	.attrs = qeth_l2_bridgeport_attrs, +}; + +int qeth_l2_create_device_attributes(struct device *dev) +{ +	return sysfs_create_group(&dev->kobj, &qeth_l2_bridgeport_attr_group); +} + +void qeth_l2_remove_device_attributes(struct device *dev) +{ +	sysfs_remove_group(&dev->kobj, &qeth_l2_bridgeport_attr_group); +} + +/** + * qeth_l2_setup_bridgeport_attrs() - set/restore attrs when turning online. + * @card:			      qeth_card structure pointer + * + * Note: this function is called with conf_mutex held by the caller + */ +void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) +{ +	int rc; + +	if (!card) +		return; +	if (!card->options.sbp.supported_funcs) +		return; +	if (card->options.sbp.role != QETH_SBP_ROLE_NONE) { +		/* Conditional to avoid spurious error messages */ +		qeth_bridgeport_setrole(card, card->options.sbp.role); +		/* Let the callback function refresh the stored role value. */ +		qeth_bridgeport_query_ports(card, +			&card->options.sbp.role, NULL); +	} +	if (card->options.sbp.hostnotification) { +		rc = qeth_bridgeport_an_set(card, 1); +		if (rc) +			card->options.sbp.hostnotification = 0; +	} else +		qeth_bridgeport_an_set(card, 0); +} diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index c1b0b2761f8..14e0b5810e8 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -63,7 +63,7 @@ void qeth_l3_ipaddr4_to_string(const __u8 *addr, char *buf)  int qeth_l3_string_to_ipaddr4(const char *buf, __u8 *addr)  {  	int count = 0, rc = 0; -	int in[4]; +	unsigned int in[4];  	char c;  	rc = sscanf(buf, "%u.%u.%u.%u%c", @@ -1659,7 +1659,7 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card)  	for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {  		struct net_device *netdev; -		netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), +		netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),  					      vid);  		if (netdev == NULL ||  		    !(netdev->flags & IFF_UP)) @@ -1721,7 +1721,7 @@ static void qeth_l3_add_vlan_mc6(struct qeth_card *card)  	for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {  		struct net_device *netdev; -		netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), +		netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),  					      vid);  		if (netdev == NULL ||  		    !(netdev->flags & IFF_UP)) @@ -1766,7 +1766,7 @@ static void qeth_l3_free_vlan_addresses4(struct qeth_card *card,  	QETH_CARD_TEXT(card, 4, "frvaddr4"); -	netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), vid); +	netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid);  	if (!netdev)  		return;  	in_dev = in_dev_get(netdev); @@ -1796,7 +1796,7 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card,  	QETH_CARD_TEXT(card, 4, "frvaddr6"); -	netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), vid); +	netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid);  	if (!netdev)  		return;  	in6_dev = in6_dev_get(netdev); @@ -2089,7 +2089,7 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev,  		struct net_device *netdev;  		rcu_read_lock(); -		netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), +		netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),  					      vid);  		rcu_read_unlock();  		if (netdev == dev) { @@ -2926,8 +2926,11 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)  	struct sk_buff *new_skb = NULL;  	int ipv = qeth_get_ip_version(skb);  	int cast_type = qeth_l3_get_cast_type(card, skb); -	struct qeth_qdio_out_q *queue = card->qdio.out_qs -		[qeth_get_priority_queue(card, skb, ipv, cast_type)]; +	struct qeth_qdio_out_q *queue = +		card->qdio.out_qs[card->qdio.do_prio_queueing +			|| (cast_type && card->info.is_multicast_different) ? +			qeth_get_priority_queue(card, skb, ipv, cast_type) : +			card->qdio.default_out_queue];  	int tx_bytes = skb->len;  	bool large_send;  	int data_offset = -1; @@ -3298,7 +3301,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)  	card->dev->ml_priv = card;  	card->dev->watchdog_timeo = QETH_TX_TIMEOUT;  	card->dev->mtu = card->info.initial_mtu; -	SET_ETHTOOL_OPS(card->dev, &qeth_l3_ethtool_ops); +	card->dev->ethtool_ops = &qeth_l3_ethtool_ops;  	card->dev->features |=	NETIF_F_HW_VLAN_CTAG_TX |  				NETIF_F_HW_VLAN_CTAG_RX |  				NETIF_F_HW_VLAN_CTAG_FILTER; @@ -3447,6 +3450,7 @@ out_remove:  	ccw_device_set_offline(CARD_DDEV(card));  	ccw_device_set_offline(CARD_WDEV(card));  	ccw_device_set_offline(CARD_RDEV(card)); +	qdio_free(CARD_DDEV(card));  	if (recover_flag == CARD_STATE_RECOVER)  		card->state = CARD_STATE_RECOVER;  	else @@ -3493,6 +3497,7 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev,  		rc = (rc2) ? rc2 : rc3;  	if (rc)  		QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); +	qdio_free(CARD_DDEV(card));  	if (recover_flag == CARD_STATE_UP)  		card->state = CARD_STATE_RECOVER;  	/* let user_space know that device is offline */ @@ -3545,6 +3550,7 @@ static void qeth_l3_shutdown(struct ccwgroup_device *gdev)  		qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);  	qeth_qdio_clear_card(card, 0);  	qeth_clear_qdio_buffers(card); +	qdio_free(CARD_DDEV(card));  }  static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev) @@ -3593,6 +3599,13 @@ out:  	return rc;  } +/* Returns zero if the command is successfully "consumed" */ +static int qeth_l3_control_event(struct qeth_card *card, +					struct qeth_ipa_cmd *cmd) +{ +	return 1; +} +  struct qeth_discipline qeth_l3_discipline = {  	.start_poll = qeth_qdio_start_poll,  	.input_handler = (qdio_handler_t *) qeth_qdio_input_handler, @@ -3606,6 +3619,7 @@ struct qeth_discipline qeth_l3_discipline = {  	.freeze = qeth_l3_pm_suspend,  	.thaw = qeth_l3_pm_resume,  	.restore = qeth_l3_pm_resume, +	.control_event_handler = qeth_l3_control_event,  };  EXPORT_SYMBOL_GPL(qeth_l3_discipline);  | 
