forked from mirrors/linux
		
	qeth: bridgeport support - basic control
Introduce functions to assign roles and check state of bridgeport-capable HiperSocket devices, and sysfs attributes providing access to these functions from userspace. Introduce udev events emitted when the state of a bridgeport device changes. Signed-off-by: Eugene Crosser <eugene.crosser@ru.ibm.com> Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com> Reviewed-by: Ursula Braun <ursula.braun@de.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									3977458c9c
								
							
						
					
					
						commit
						b4d72c08b3
					
				
					 9 changed files with 685 additions and 2 deletions
				
			
		
							
								
								
									
										21
									
								
								Documentation/s390/qeth.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								Documentation/s390/qeth.txt
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| IBM s390 QDIO Ethernet Driver | ||||
| 
 | ||||
| HiperSockets Bridge Port Support | ||||
| 
 | ||||
| Uevents | ||||
| 
 | ||||
| To generate the events the device must be assigned a role of either | ||||
| a primary or a secondary Bridge Port. For more information, see | ||||
| "z/VM Connectivity, SC24-6174". | ||||
| 
 | ||||
| When run on HiperSockets Bridge Capable Port hardware, and the state | ||||
| of some configured Bridge Port device on the channel changes, a udev | ||||
| event with ACTION=CHANGE is emitted on behalf of the corresponding | ||||
| ccwgroup device. The event has the following attributes: | ||||
| 
 | ||||
| BRIDGEPORT=statechange -  indicates that the Bridge Port device changed | ||||
|   its state. | ||||
| 
 | ||||
| ROLE={primary|secondary|none} - the role assigned to the port. | ||||
| 
 | ||||
| STATE={active|standby|inactive} - the newly assumed state of the port. | ||||
|  | @ -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 | ||||
|  |  | |||
|  | @ -156,6 +156,24 @@ 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, | ||||
| }; | ||||
| 
 | ||||
| struct qeth_sbp_info { | ||||
| 	__u32 supported_funcs; | ||||
| 	enum qeth_sbp_roles role; | ||||
| }; | ||||
| 
 | ||||
| static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, | ||||
| 		enum qeth_ipa_funcs func) | ||||
| { | ||||
|  | @ -672,6 +690,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; | ||||
|  | @ -857,6 +876,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); | ||||
|  | @ -925,6 +945,11 @@ int qeth_query_card_info(struct qeth_card *card, | |||
| 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); | ||||
| void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd); | ||||
| void qeth_bridgeport_query_support(struct qeth_card *card); | ||||
| 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_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 *); | ||||
|  |  | |||
|  | @ -68,7 +68,7 @@ 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; | ||||
| 
 | ||||
| static void qeth_close_dev_handler(struct work_struct *work) | ||||
| { | ||||
|  | @ -615,6 +615,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: | ||||
| 				if (cmd->data.sbp.hdr.command_code == | ||||
| 					IPA_SBP_BRIDGE_PORT_STATE_CHANGE) { | ||||
| 					qeth_bridge_state_change(card, cmd); | ||||
| 					return NULL; | ||||
| 				} else | ||||
| 					return cmd; | ||||
| 			case IPA_CMD_MODCCID: | ||||
| 				return cmd; | ||||
| 			case IPA_CMD_REGISTER_LOCAL_ADDR: | ||||
|  | @ -4956,12 +4963,17 @@ int qeth_core_hardsetup_card(struct qeth_card *card) | |||
| 
 | ||||
| 	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)) | ||||
| 		qeth_query_setadapterparms(card); | ||||
| 	if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) | ||||
| 		qeth_query_setdiagass(card); | ||||
| 	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"); | ||||
| 	return 0; | ||||
| out: | ||||
| 	dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " | ||||
|  |  | |||
|  | @ -249,6 +249,7 @@ 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"}, | ||||
|  |  | |||
|  | @ -104,6 +104,7 @@ 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, | ||||
|  | @ -500,6 +501,88 @@ 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 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; | ||||
| 
 | ||||
| /* Header for each IPA command */ | ||||
| struct qeth_ipacmd_hdr { | ||||
| 	__u8   command; | ||||
|  | @ -529,6 +612,7 @@ struct qeth_ipa_cmd { | |||
| 		struct qeth_ipacmd_setadpparms		setadapterparms; | ||||
| 		struct qeth_set_routing			setrtg; | ||||
| 		struct qeth_ipacmd_diagass		diagass; | ||||
| 		struct qeth_ipacmd_setbridgeport	sbp; | ||||
| 	} data; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										15
									
								
								drivers/s390/net/qeth_l2.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								drivers/s390/net/qeth_l2.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -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__ */ | ||||
|  | @ -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 *); | ||||
|  | @ -880,6 +881,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 +893,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); | ||||
| 
 | ||||
|  | @ -1003,6 +1006,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); | ||||
|  | @ -1347,6 +1352,365 @@ void qeth_osn_deregister(struct net_device *dev) | |||
| } | ||||
| EXPORT_SYMBOL(qeth_osn_deregister); | ||||
| 
 | ||||
| /* SETBRIDGEPORT support, async notifications */ | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| EXPORT_SYMBOL(qeth_bridge_state_change); | ||||
| 
 | ||||
| /* 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. | ||||
|  */ | ||||
| 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; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(qeth_bridgeport_query_support); | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| module_init(qeth_l2_init); | ||||
| module_exit(qeth_l2_exit); | ||||
| MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); | ||||
|  |  | |||
							
								
								
									
										161
									
								
								drivers/s390/net/qeth_l2_sys.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								drivers/s390/net/qeth_l2_sys.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,161 @@ | |||
| /*
 | ||||
|  *    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 struct attribute *qeth_l2_bridgeport_attrs[] = { | ||||
| 	&dev_attr_bridge_role.attr, | ||||
| 	&dev_attr_bridge_state.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) | ||||
| { | ||||
| 	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); | ||||
| 	} | ||||
| } | ||||
		Loading…
	
		Reference in a new issue
	
	 Eugene Crosser
						Eugene Crosser