diff options
Diffstat (limited to 'drivers/scsi/fcoe/fcoe.c')
| -rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 1720 | 
1 files changed, 973 insertions, 747 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index d23a538a9df..00ee0ed642a 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -18,7 +18,6 @@   */  #include <linux/module.h> -#include <linux/version.h>  #include <linux/spinlock.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h> @@ -31,6 +30,9 @@  #include <linux/fs.h>  #include <linux/sysfs.h>  #include <linux/ctype.h> +#include <linux/workqueue.h> +#include <net/dcbnl.h> +#include <net/dcbevent.h>  #include <scsi/scsi_tcq.h>  #include <scsi/scsicam.h>  #include <scsi/scsi_transport.h> @@ -39,6 +41,7 @@  #include <scsi/fc/fc_encaps.h>  #include <scsi/fc/fc_fip.h> +#include <scsi/fc/fc_fcoe.h>  #include <scsi/libfc.h>  #include <scsi/fc_frame.h> @@ -51,20 +54,26 @@ MODULE_DESCRIPTION("FCoE");  MODULE_LICENSE("GPL v2");  /* Performance tuning parameters for fcoe */ -static unsigned int fcoe_ddp_min; +static unsigned int fcoe_ddp_min = 4096;  module_param_named(ddp_min, fcoe_ddp_min, uint, S_IRUGO | S_IWUSR);  MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for "	\  		 "Direct Data Placement (DDP)."); -DEFINE_MUTEX(fcoe_config_mutex); +unsigned int fcoe_debug_logging; +module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); + +static DEFINE_MUTEX(fcoe_config_mutex); + +static struct workqueue_struct *fcoe_wq;  /* fcoe_percpu_clean completion.  Waiter protected by fcoe_create_mutex */  static DECLARE_COMPLETION(fcoe_flush_completion);  /* fcoe host list */  /* must only by accessed under the RTNL mutex */ -LIST_HEAD(fcoe_hostlist); -DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu); +static LIST_HEAD(fcoe_hostlist); +static DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);  /* Function Prototypes */  static int fcoe_reset(struct Scsi_Host *); @@ -72,15 +81,13 @@ static int fcoe_xmit(struct fc_lport *, struct fc_frame *);  static int fcoe_rcv(struct sk_buff *, struct net_device *,  		    struct packet_type *, struct net_device *);  static int fcoe_percpu_receive_thread(void *); -static void fcoe_clean_pending_queue(struct fc_lport *);  static void fcoe_percpu_clean(struct fc_lport *); -static int fcoe_link_speed_update(struct fc_lport *);  static int fcoe_link_ok(struct fc_lport *);  static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);  static int fcoe_hostlist_add(const struct fc_lport *); +static void fcoe_hostlist_del(const struct fc_lport *); -static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *);  static int fcoe_device_notification(struct notifier_block *, ulong, void *);  static void fcoe_dev_setup(void);  static void fcoe_dev_cleanup(void); @@ -98,13 +105,22 @@ static void fcoe_destroy_work(struct work_struct *);  static int fcoe_ddp_setup(struct fc_lport *, u16, struct scatterlist *,  			  unsigned int);  static int fcoe_ddp_done(struct fc_lport *, u16); - +static int fcoe_ddp_target(struct fc_lport *, u16, struct scatterlist *, +			   unsigned int);  static int fcoe_cpu_callback(struct notifier_block *, unsigned long, void *); +static int fcoe_dcb_app_notification(struct notifier_block *notifier, +				     ulong event, void *ptr); + +static bool fcoe_match(struct net_device *netdev); +static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode); +static int fcoe_destroy(struct net_device *netdev); +static int fcoe_enable(struct net_device *netdev); +static int fcoe_disable(struct net_device *netdev); + +/* fcoe_syfs control interface handlers */ +static int fcoe_ctlr_alloc(struct net_device *netdev); +static int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev); -static int fcoe_create(const char *, struct kernel_param *); -static int fcoe_destroy(const char *, struct kernel_param *); -static int fcoe_enable(const char *, struct kernel_param *); -static int fcoe_disable(const char *, struct kernel_param *);  static struct fc_seq *fcoe_elsct_send(struct fc_lport *,  				      u32 did, struct fc_frame *, @@ -115,26 +131,6 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *,  				      void *, u32 timeout);  static void fcoe_recv_frame(struct sk_buff *skb); -static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *); - -module_param_call(create, fcoe_create, NULL, (void *)FIP_MODE_FABRIC, S_IWUSR); -__MODULE_PARM_TYPE(create, "string"); -MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface"); -module_param_call(create_vn2vn, fcoe_create, NULL, -		  (void *)FIP_MODE_VN2VN, S_IWUSR); -__MODULE_PARM_TYPE(create_vn2vn, "string"); -MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance " -		 "on an Ethernet interface"); -module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR); -__MODULE_PARM_TYPE(destroy, "string"); -MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface"); -module_param_call(enable, fcoe_enable, NULL, NULL, S_IWUSR); -__MODULE_PARM_TYPE(enable, "string"); -MODULE_PARM_DESC(enable, " Enables fcoe on a ethernet interface."); -module_param_call(disable, fcoe_disable, NULL, NULL, S_IWUSR); -__MODULE_PARM_TYPE(disable, "string"); -MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface."); -  /* notification function for packets from net device */  static struct notifier_block fcoe_notifier = {  	.notifier_call = fcoe_device_notification, @@ -145,31 +141,60 @@ static struct notifier_block fcoe_cpu_notifier = {  	.notifier_call = fcoe_cpu_callback,  }; -static struct scsi_transport_template *fcoe_transport_template; -static struct scsi_transport_template *fcoe_vport_transport_template; +/* notification function for DCB events */ +static struct notifier_block dcb_notifier = { +	.notifier_call = fcoe_dcb_app_notification, +}; + +static struct scsi_transport_template *fcoe_nport_scsi_transport; +static struct scsi_transport_template *fcoe_vport_scsi_transport;  static int fcoe_vport_destroy(struct fc_vport *);  static int fcoe_vport_create(struct fc_vport *, bool disabled);  static int fcoe_vport_disable(struct fc_vport *, bool disable);  static void fcoe_set_vport_symbolic_name(struct fc_vport *);  static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *); +static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *); + +static struct fcoe_sysfs_function_template fcoe_sysfs_templ = { +	.set_fcoe_ctlr_mode = fcoe_ctlr_set_fip_mode, +	.set_fcoe_ctlr_enabled = fcoe_ctlr_enabled, +	.get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb, +	.get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb, +	.get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb, +	.get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb, +	.get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb, +	.get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb, + +	.get_fcoe_fcf_selected = fcoe_fcf_get_selected, +	.get_fcoe_fcf_vlan_id = fcoe_fcf_get_vlan_id, +};  static struct libfc_function_template fcoe_libfc_fcn_templ = {  	.frame_send = fcoe_xmit,  	.ddp_setup = fcoe_ddp_setup,  	.ddp_done = fcoe_ddp_done, +	.ddp_target = fcoe_ddp_target,  	.elsct_send = fcoe_elsct_send,  	.get_lesb = fcoe_get_lesb,  	.lport_set_port_id = fcoe_set_port_id,  }; -struct fc_function_template fcoe_transport_function = { +static struct fc_function_template fcoe_nport_fc_functions = {  	.show_host_node_name = 1,  	.show_host_port_name = 1,  	.show_host_supported_classes = 1,  	.show_host_supported_fc4s = 1,  	.show_host_active_fc4s = 1,  	.show_host_maxframe_size = 1, +	.show_host_serial_number = 1, +	.show_host_manufacturer = 1, +	.show_host_model = 1, +	.show_host_model_description = 1, +	.show_host_hardware_version = 1, +	.show_host_driver_version = 1, +	.show_host_firmware_version = 1, +	.show_host_optionrom_version = 1,  	.show_host_port_id = 1,  	.show_host_supported_speeds = 1, @@ -203,13 +228,21 @@ struct fc_function_template fcoe_transport_function = {  	.bsg_request = fc_lport_bsg_request,  }; -struct fc_function_template fcoe_vport_transport_function = { +static struct fc_function_template fcoe_vport_fc_functions = {  	.show_host_node_name = 1,  	.show_host_port_name = 1,  	.show_host_supported_classes = 1,  	.show_host_supported_fc4s = 1,  	.show_host_active_fc4s = 1,  	.show_host_maxframe_size = 1, +	.show_host_serial_number = 1, +	.show_host_manufacturer = 1, +	.show_host_model = 1, +	.show_host_model_description = 1, +	.show_host_hardware_version = 1, +	.show_host_driver_version = 1, +	.show_host_firmware_version = 1, +	.show_host_optionrom_version = 1,  	.show_host_port_id = 1,  	.show_host_supported_speeds = 1, @@ -268,7 +301,7 @@ static struct scsi_host_template fcoe_shost_template = {  static int fcoe_interface_setup(struct fcoe_interface *fcoe,  				struct net_device *netdev)  { -	struct fcoe_ctlr *fip = &fcoe->ctlr; +	struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);  	struct netdev_hw_addr *ha;  	struct net_device *real_dev;  	u8 flogi_maddr[ETH_ALEN]; @@ -285,9 +318,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,  	}  	/* Do not support for bonding device */ -	if ((netdev->priv_flags & IFF_MASTER_ALB) || -	    (netdev->priv_flags & IFF_SLAVE_INACTIVE) || -	    (netdev->priv_flags & IFF_MASTER_8023AD)) { +	if (netdev->priv_flags & IFF_BONDING && netdev->flags & IFF_MASTER) {  		FCOE_NETDEV_DBG(netdev, "Bonded interfaces not supported\n");  		return -EOPNOTSUPP;  	} @@ -296,6 +327,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,  	 * use the first one for SPMA */  	real_dev = (netdev->priv_flags & IFF_802_1Q_VLAN) ?  		vlan_dev_real_dev(netdev) : netdev; +	fcoe->realdev = real_dev;  	rcu_read_lock();  	for_each_dev_addr(real_dev, ha) {  		if ((ha->type == NETDEV_HW_ADDR_T_SAN) && @@ -353,47 +385,69 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,  static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,  						    enum fip_state fip_mode)  { +	struct fcoe_ctlr_device *ctlr_dev; +	struct fcoe_ctlr *ctlr;  	struct fcoe_interface *fcoe; +	int size;  	int err; -	fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL); -	if (!fcoe) { -		FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n"); -		return NULL; +	if (!try_module_get(THIS_MODULE)) { +		FCOE_NETDEV_DBG(netdev, +				"Could not get a reference to the module\n"); +		fcoe = ERR_PTR(-EBUSY); +		goto out; +	} + +	size = sizeof(struct fcoe_ctlr) + sizeof(struct fcoe_interface); +	ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &fcoe_sysfs_templ, +					size); +	if (!ctlr_dev) { +		FCOE_DBG("Failed to add fcoe_ctlr_device\n"); +		fcoe = ERR_PTR(-ENOMEM); +		goto out_putmod;  	} +	ctlr = fcoe_ctlr_device_priv(ctlr_dev); +	ctlr->cdev = ctlr_dev; +	fcoe = fcoe_ctlr_priv(ctlr); +  	dev_hold(netdev); -	kref_init(&fcoe->kref);  	/*  	 * Initialize FIP.  	 */ -	fcoe_ctlr_init(&fcoe->ctlr, fip_mode); -	fcoe->ctlr.send = fcoe_fip_send; -	fcoe->ctlr.update_mac = fcoe_update_src_mac; -	fcoe->ctlr.get_src_addr = fcoe_get_src_mac; +	fcoe_ctlr_init(ctlr, fip_mode); +	ctlr->send = fcoe_fip_send; +	ctlr->update_mac = fcoe_update_src_mac; +	ctlr->get_src_addr = fcoe_get_src_mac;  	err = fcoe_interface_setup(fcoe, netdev);  	if (err) { -		fcoe_ctlr_destroy(&fcoe->ctlr); -		kfree(fcoe); +		fcoe_ctlr_destroy(ctlr); +		fcoe_ctlr_device_delete(ctlr_dev);  		dev_put(netdev); -		return NULL; +		fcoe = ERR_PTR(err); +		goto out_putmod;  	} +	goto out; + +out_putmod: +	module_put(THIS_MODULE); +out:  	return fcoe;  }  /** - * fcoe_interface_cleanup() - Clean up a FCoE interface + * fcoe_interface_remove() - remove FCoE interface from netdev   * @fcoe: The FCoE interface to be cleaned up   *   * Caller must be holding the RTNL mutex   */ -void fcoe_interface_cleanup(struct fcoe_interface *fcoe) +static void fcoe_interface_remove(struct fcoe_interface *fcoe)  {  	struct net_device *netdev = fcoe->netdev; -	struct fcoe_ctlr *fip = &fcoe->ctlr; +	struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);  	u8 flogi_maddr[ETH_ALEN];  	const struct net_device_ops *ops; @@ -425,41 +479,30 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)  			FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE"  					" specific feature for LLD.\n");  	} +	fcoe->removed = 1;  } +  /** - * fcoe_interface_release() - fcoe_port kref release function - * @kref: Embedded reference count in an fcoe_interface struct + * fcoe_interface_cleanup() - Clean up a FCoE interface + * @fcoe: The FCoE interface to be cleaned up   */ -static void fcoe_interface_release(struct kref *kref) +static void fcoe_interface_cleanup(struct fcoe_interface *fcoe)  { -	struct fcoe_interface *fcoe; -	struct net_device *netdev; +	struct net_device *netdev = fcoe->netdev; +	struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe); + +	rtnl_lock(); +	if (!fcoe->removed) +		fcoe_interface_remove(fcoe); +	rtnl_unlock(); -	fcoe = container_of(kref, struct fcoe_interface, kref); -	netdev = fcoe->netdev; +	/* Release the self-reference taken during fcoe_interface_create() */  	/* tear-down the FCoE controller */ -	fcoe_ctlr_destroy(&fcoe->ctlr); -	kfree(fcoe); +	fcoe_ctlr_destroy(fip); +	scsi_host_put(fip->lp->host);  	dev_put(netdev); -} - -/** - * fcoe_interface_get() - Get a reference to a FCoE interface - * @fcoe: The FCoE interface to be held - */ -static inline void fcoe_interface_get(struct fcoe_interface *fcoe) -{ -	kref_get(&fcoe->kref); -} - -/** - * fcoe_interface_put() - Put a reference to a FCoE interface - * @fcoe: The FCoE interface to be released - */ -static inline void fcoe_interface_put(struct fcoe_interface *fcoe) -{ -	kref_put(&fcoe->kref, fcoe_interface_release); +	module_put(THIS_MODULE);  }  /** @@ -477,13 +520,28 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,  			 struct net_device *orig_dev)  {  	struct fcoe_interface *fcoe; +	struct fcoe_ctlr *ctlr;  	fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type); -	fcoe_ctlr_recv(&fcoe->ctlr, skb); +	ctlr = fcoe_to_ctlr(fcoe); +	fcoe_ctlr_recv(ctlr, skb);  	return 0;  }  /** + * fcoe_port_send() - Send an Ethernet-encapsulated FIP/FCoE frame + * @port: The FCoE port + * @skb: The FIP/FCoE packet to be sent + */ +static void fcoe_port_send(struct fcoe_port *port, struct sk_buff *skb) +{ +	if (port->fcoe_pending_queue.qlen) +		fcoe_check_wait_queue(port->lport, skb); +	else if (fcoe_start_io(skb)) +		fcoe_check_wait_queue(port->lport, skb); +} + +/**   * fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame   * @fip: The FCoE controller   * @skb: The FIP packet to be sent @@ -491,7 +549,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,  static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)  {  	skb->dev = fcoe_from_ctlr(fip)->netdev; -	dev_queue_xmit(skb); +	fcoe_port_send(lport_priv(fip->lp), skb);  }  /** @@ -505,15 +563,13 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)  static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr)  {  	struct fcoe_port *port = lport_priv(lport); -	struct fcoe_interface *fcoe = port->fcoe; +	struct fcoe_interface *fcoe = port->priv; -	rtnl_lock();  	if (!is_zero_ether_addr(port->data_src_addr))  		dev_uc_del(fcoe->netdev, port->data_src_addr);  	if (!is_zero_ether_addr(addr))  		dev_uc_add(fcoe->netdev, addr);  	memcpy(port->data_src_addr, addr, ETH_ALEN); -	rtnl_unlock();  }  /** @@ -561,34 +617,6 @@ static int fcoe_lport_config(struct fc_lport *lport)  }  /** - * fcoe_queue_timer() - The fcoe queue timer - * @lport: The local port - * - * Calls fcoe_check_wait_queue on timeout - */ -static void fcoe_queue_timer(ulong lport) -{ -	fcoe_check_wait_queue((struct fc_lport *)lport, NULL); -} - -/** - * fcoe_get_wwn() - Get the world wide name from LLD if it supports it - * @netdev: the associated net device - * @wwn: the output WWN - * @type: the type of WWN (WWPN or WWNN) - * - * Returns: 0 for success - */ -static int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type) -{ -	const struct net_device_ops *ops = netdev->netdev_ops; - -	if (ops->ndo_fcoe_get_wwn) -		return ops->ndo_fcoe_get_wwn(netdev, wwn, type); -	return -EINVAL; -} - -/**   * fcoe_netdev_features_change - Updates the lport's offload flags based   * on the LLD netdev's FCoE feature flags   */ @@ -646,11 +674,13 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)  	u32 mfs;  	u64 wwnn, wwpn;  	struct fcoe_interface *fcoe; +	struct fcoe_ctlr *ctlr;  	struct fcoe_port *port;  	/* Setup lport private data to point to fcoe softc */  	port = lport_priv(lport); -	fcoe = port->fcoe; +	fcoe = port->priv; +	ctlr = fcoe_to_ctlr(fcoe);  	/*  	 * Determine max frame size based on underlying device and optional @@ -677,10 +707,10 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)  	if (!lport->vport) {  		if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN)) -			wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0); +			wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr, 1, 0);  		fc_set_wwnn(lport, wwnn);  		if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN)) -			wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, +			wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,  						 2, 0);  		fc_set_wwpn(lport, wwpn);  	} @@ -708,9 +738,9 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)  	lport->host->max_cmd_len = FCOE_MAX_CMD_LEN;  	if (lport->vport) -		lport->host->transportt = fcoe_vport_transport_template; +		lport->host->transportt = fcoe_vport_scsi_transport;  	else -		lport->host->transportt = fcoe_transport_template; +		lport->host->transportt = fcoe_nport_scsi_transport;  	/* add the new host to the SCSI-ml */  	rc = scsi_add_host(lport->host, dev); @@ -730,6 +760,89 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)  	return 0;  } + +/** + * fcoe_fdmi_info() - Get FDMI related info from net devive for SW FCoE + * @lport:  The local port that is associated with the net device + * @netdev: The associated net device + * + * Must be called after fcoe_shost_config() as it will use local port mutex + * + */ +static void fcoe_fdmi_info(struct fc_lport *lport, struct net_device *netdev) +{ +	struct fcoe_interface *fcoe; +	struct fcoe_port *port; +	struct net_device *realdev; +	int rc; + +	port = lport_priv(lport); +	fcoe = port->priv; +	realdev = fcoe->realdev; + +	if (!realdev) +		return; + +	/* No FDMI state m/c for NPIV ports */ +	if (lport->vport) +		return; + +	if (realdev->netdev_ops->ndo_fcoe_get_hbainfo) { +		struct netdev_fcoe_hbainfo *fdmi; +		fdmi = kzalloc(sizeof(*fdmi), GFP_KERNEL); +		if (!fdmi) +			return; + +		rc = realdev->netdev_ops->ndo_fcoe_get_hbainfo(realdev, +							       fdmi); +		if (rc) { +			printk(KERN_INFO "fcoe: Failed to retrieve FDMI " +					"information from netdev.\n"); +			return; +		} + +		snprintf(fc_host_serial_number(lport->host), +			 FC_SERIAL_NUMBER_SIZE, +			 "%s", +			 fdmi->serial_number); +		snprintf(fc_host_manufacturer(lport->host), +			 FC_SERIAL_NUMBER_SIZE, +			 "%s", +			 fdmi->manufacturer); +		snprintf(fc_host_model(lport->host), +			 FC_SYMBOLIC_NAME_SIZE, +			 "%s", +			 fdmi->model); +		snprintf(fc_host_model_description(lport->host), +			 FC_SYMBOLIC_NAME_SIZE, +			 "%s", +			 fdmi->model_description); +		snprintf(fc_host_hardware_version(lport->host), +			 FC_VERSION_STRING_SIZE, +			 "%s", +			 fdmi->hardware_version); +		snprintf(fc_host_driver_version(lport->host), +			 FC_VERSION_STRING_SIZE, +			 "%s", +			 fdmi->driver_version); +		snprintf(fc_host_optionrom_version(lport->host), +			 FC_VERSION_STRING_SIZE, +			 "%s", +			 fdmi->optionrom_version); +		snprintf(fc_host_firmware_version(lport->host), +			 FC_VERSION_STRING_SIZE, +			 "%s", +			 fdmi->firmware_version); + +		/* Enable FDMI lport states */ +		lport->fdmi_enabled = 1; +		kfree(fdmi); +	} else { +		lport->fdmi_enabled = 0; +		printk(KERN_INFO "fcoe: No FDMI support.\n"); +	} +} +  /**   * fcoe_oem_match() - The match routine for the offloaded exchange manager   * @fp: The I/O frame @@ -743,12 +856,28 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)   * The offload EM that this routine is associated with will handle any   * packets that are for SCSI read requests.   * + * This has been enhanced to work when FCoE stack is operating in target + * mode. + *   * Returns: True for read types I/O, otherwise returns false.   */ -bool fcoe_oem_match(struct fc_frame *fp) +static bool fcoe_oem_match(struct fc_frame *fp)  { -	return fc_fcp_is_read(fr_fsp(fp)) && -		(fr_fsp(fp)->data_len > fcoe_ddp_min); +	struct fc_frame_header *fh = fc_frame_header_get(fp); +	struct fcp_cmnd *fcp; + +	if (fc_fcp_is_read(fr_fsp(fp)) && +	    (fr_fsp(fp)->data_len > fcoe_ddp_min)) +		return true; +	else if ((fr_fsp(fp) == NULL) && +		 (fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD) && +		 (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN)) { +		fcp = fc_frame_payload_get(fp, sizeof(*fcp)); +		if ((fcp->fc_flags & FCP_CFL_WRDATA) && +		    (ntohl(fcp->fc_dl) > fcoe_ddp_min)) +			return true; +	} +	return false;  }  /** @@ -760,7 +889,7 @@ bool fcoe_oem_match(struct fc_frame *fp)  static inline int fcoe_em_config(struct fc_lport *lport)  {  	struct fcoe_port *port = lport_priv(lport); -	struct fcoe_interface *fcoe = port->fcoe; +	struct fcoe_interface *fcoe = port->priv;  	struct fcoe_interface *oldfcoe = NULL;  	struct net_device *old_real_dev, *cur_real_dev;  	u16 min_xid = FCOE_MIN_XID; @@ -835,16 +964,11 @@ skip_oem:   * fcoe_if_destroy() - Tear down a SW FCoE instance   * @lport: The local port to be destroyed   * - * Locking: must be called with the RTNL mutex held and RTNL mutex - * needed to be dropped by this function since not dropping RTNL - * would cause circular locking warning on synchronous fip worker - * cancelling thru fcoe_interface_put invoked by this function. - *   */  static void fcoe_if_destroy(struct fc_lport *lport)  {  	struct fcoe_port *port = lport_priv(lport); -	struct fcoe_interface *fcoe = port->fcoe; +	struct fcoe_interface *fcoe = port->priv;  	struct net_device *netdev = fcoe->netdev;  	FCOE_NETDEV_DBG(netdev, "Destroying interface\n"); @@ -854,7 +978,6 @@ static void fcoe_if_destroy(struct fc_lport *lport)  	/* Cleanup the fc_lport */  	fc_lport_destroy(lport); -	fc_fcp_destroy(lport);  	/* Stop the transmit retry timer */  	del_timer_sync(&port->timer); @@ -862,13 +985,15 @@ static void fcoe_if_destroy(struct fc_lport *lport)  	/* Free existing transmit skbs */  	fcoe_clean_pending_queue(lport); +	rtnl_lock();  	if (!is_zero_ether_addr(port->data_src_addr))  		dev_uc_del(netdev, port->data_src_addr); +	if (lport->vport) +		synchronize_net(); +	else +		fcoe_interface_remove(fcoe);  	rtnl_unlock(); -	/* receives may not be stopped until after this */ -	fcoe_interface_put(fcoe); -  	/* Free queued packets for the per-CPU receive threads */  	fcoe_percpu_clean(lport); @@ -876,15 +1001,21 @@ static void fcoe_if_destroy(struct fc_lport *lport)  	fc_remove_host(lport->host);  	scsi_remove_host(lport->host); +	/* Destroy lport scsi_priv */ +	fc_fcp_destroy(lport); +  	/* There are no more rports or I/O, free the EM */  	fc_exch_mgr_free(lport);  	/* Free memory used by statistical counters */  	fc_lport_free_stats(lport); -	/* Release the Scsi_Host */ -	scsi_host_put(lport->host); -	module_put(THIS_MODULE); +	/* +	 * Release the Scsi_Host for vport but hold on to +	 * master lport until it fcoe interface fully cleaned-up. +	 */ +	if (lport->vport) +		scsi_host_put(lport->host);  }  /** @@ -910,6 +1041,28 @@ static int fcoe_ddp_setup(struct fc_lport *lport, u16 xid,  }  /** + * fcoe_ddp_target() - Call a LLD's ddp_target through the net device + * @lport: The local port to setup DDP for + * @xid:   The exchange ID for this DDP transfer + * @sgl:   The scatterlist describing this transfer + * @sgc:   The number of sg items + * + * Returns: 0 if the DDP context was not configured + */ +static int fcoe_ddp_target(struct fc_lport *lport, u16 xid, +			   struct scatterlist *sgl, unsigned int sgc) +{ +	struct net_device *netdev = fcoe_netdev(lport); + +	if (netdev->netdev_ops->ndo_fcoe_ddp_target) +		return netdev->netdev_ops->ndo_fcoe_ddp_target(netdev, xid, +							       sgl, sgc); + +	return 0; +} + + +/**   * fcoe_ddp_done() - Call a LLD's ddp_done through the net device   * @lport: The local port to complete DDP on   * @xid:   The exchange ID for this DDP transfer @@ -938,9 +1091,11 @@ static int fcoe_ddp_done(struct fc_lport *lport, u16 xid)  static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,  				       struct device *parent, int npiv)  { +	struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);  	struct net_device *netdev = fcoe->netdev; -	struct fc_lport *lport = NULL; +	struct fc_lport *lport, *n_port;  	struct fcoe_port *port; +	struct Scsi_Host *shost;  	int rc;  	/*  	 * parent is only a vport if npiv is 1, @@ -950,13 +1105,11 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,  	FCOE_NETDEV_DBG(netdev, "Create Interface\n"); -	if (!npiv) { -		lport = libfc_host_alloc(&fcoe_shost_template, -					 sizeof(struct fcoe_port)); -	} else	{ -		lport = libfc_vport_create(vport, -					   sizeof(struct fcoe_port)); -	} +	if (!npiv) +		lport = libfc_host_alloc(&fcoe_shost_template, sizeof(*port)); +	else +		lport = libfc_vport_create(vport, sizeof(*port)); +  	if (!lport) {  		FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");  		rc = -ENOMEM; @@ -964,9 +1117,18 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,  	}  	port = lport_priv(lport);  	port->lport = lport; -	port->fcoe = fcoe; +	port->priv = fcoe; +	port->get_netdev = fcoe_netdev; +	port->max_queue_depth = FCOE_MAX_QUEUE_DEPTH; +	port->min_queue_depth = FCOE_MIN_QUEUE_DEPTH;  	INIT_WORK(&port->destroy_work, fcoe_destroy_work); +	/* +	 * Need to add the lport to the hostlist +	 * so we catch NETDEV_CHANGE events. +	 */ +	fcoe_hostlist_add(lport); +  	/* configure a fc_lport including the exchange manager */  	rc = fcoe_lport_config(lport);  	if (rc) { @@ -1000,39 +1162,45 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,  	}  	/* Initialize the library */ -	rc = fcoe_libfc_config(lport, &fcoe->ctlr, &fcoe_libfc_fcn_templ, 1); +	rc = fcoe_libfc_config(lport, ctlr, &fcoe_libfc_fcn_templ, 1);  	if (rc) {  		FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "  				"interface\n");  		goto out_lp_destroy;  	} -	if (!npiv) { -		/* -		 * fcoe_em_alloc() and fcoe_hostlist_add() both -		 * need to be atomic with respect to other changes to the -		 * hostlist since fcoe_em_alloc() looks for an existing EM -		 * instance on host list updated by fcoe_hostlist_add(). -		 * -		 * This is currently handled through the fcoe_config_mutex -		 * begin held. -		 */ +	/* Initialized FDMI information */ +	fcoe_fdmi_info(lport, netdev); +	/* +	 * fcoe_em_alloc() and fcoe_hostlist_add() both +	 * need to be atomic with respect to other changes to the +	 * hostlist since fcoe_em_alloc() looks for an existing EM +	 * instance on host list updated by fcoe_hostlist_add(). +	 * +	 * This is currently handled through the fcoe_config_mutex +	 * begin held. +	 */ +	if (!npiv)  		/* lport exch manager allocation */  		rc = fcoe_em_config(lport); -		if (rc) { -			FCOE_NETDEV_DBG(netdev, "Could not configure the EM " -					"for the interface\n"); -			goto out_lp_destroy; -		} +	else { +		shost = vport_to_shost(vport); +		n_port = shost_priv(shost); +		rc = fc_exch_mgr_list_clone(n_port, lport); +	} + +	if (rc) { +		FCOE_NETDEV_DBG(netdev, "Could not configure the EM\n"); +		goto out_lp_destroy;  	} -	fcoe_interface_get(fcoe);  	return lport;  out_lp_destroy:  	fc_exch_mgr_free(lport);  out_host_put: +	fcoe_hostlist_del(lport);  	scsi_host_put(lport->host);  out:  	return ERR_PTR(rc); @@ -1048,11 +1216,12 @@ out:  static int __init fcoe_if_init(void)  {  	/* attach to scsi transport */ -	fcoe_transport_template = fc_attach_transport(&fcoe_transport_function); -	fcoe_vport_transport_template = -		fc_attach_transport(&fcoe_vport_transport_function); +	fcoe_nport_scsi_transport = +		fc_attach_transport(&fcoe_nport_fc_functions); +	fcoe_vport_scsi_transport = +		fc_attach_transport(&fcoe_vport_fc_functions); -	if (!fcoe_transport_template) { +	if (!fcoe_nport_scsi_transport) {  		printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n");  		return -ENODEV;  	} @@ -1067,12 +1236,12 @@ static int __init fcoe_if_init(void)   *   * Returns: 0 on success   */ -int __exit fcoe_if_exit(void) +static int __exit fcoe_if_exit(void)  { -	fc_release_transport(fcoe_transport_template); -	fc_release_transport(fcoe_vport_transport_template); -	fcoe_transport_template = NULL; -	fcoe_vport_transport_template = NULL; +	fc_release_transport(fcoe_nport_scsi_transport); +	fc_release_transport(fcoe_vport_scsi_transport); +	fcoe_nport_scsi_transport = NULL; +	fcoe_vport_scsi_transport = NULL;  	return 0;  } @@ -1087,8 +1256,9 @@ static void fcoe_percpu_thread_create(unsigned int cpu)  	p = &per_cpu(fcoe_percpu, cpu); -	thread = kthread_create(fcoe_percpu_receive_thread, -				(void *)p, "fcoethread/%d", cpu); +	thread = kthread_create_on_node(fcoe_percpu_receive_thread, +					(void *)p, cpu_to_node(cpu), +					"fcoethread/%d", cpu);  	if (likely(!IS_ERR(thread))) {  		kthread_bind(thread, cpu); @@ -1224,6 +1394,26 @@ static int fcoe_cpu_callback(struct notifier_block *nfb,  }  /** + * fcoe_select_cpu() - Selects CPU to handle post-processing of incoming + *			command. + * + * This routine selects next CPU based on cpumask to distribute + * incoming requests in round robin. + * + * Returns: int CPU number + */ +static inline unsigned int fcoe_select_cpu(void) +{ +	static unsigned int selected_cpu; + +	selected_cpu = cpumask_next(selected_cpu, cpu_online_mask); +	if (selected_cpu >= nr_cpu_ids) +		selected_cpu = cpumask_first(cpu_online_mask); + +	return selected_cpu; +} + +/**   * fcoe_rcv() - Receive packets from a net device   * @skb:    The received packet   * @netdev: The net device that the packet was received on @@ -1235,11 +1425,12 @@ static int fcoe_cpu_callback(struct notifier_block *nfb,   *   * Returns: 0 for success   */ -int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, +static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,  	     struct packet_type *ptype, struct net_device *olddev)  {  	struct fc_lport *lport;  	struct fcoe_rcv_info *fr; +	struct fcoe_ctlr *ctlr;  	struct fcoe_interface *fcoe;  	struct fc_frame_header *fh;  	struct fcoe_percpu_s *fps; @@ -1247,24 +1438,31 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,  	unsigned int cpu;  	fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type); -	lport = fcoe->ctlr.lp; +	ctlr = fcoe_to_ctlr(fcoe); +	lport = ctlr->lp;  	if (unlikely(!lport)) { -		FCOE_NETDEV_DBG(netdev, "Cannot find hba structure"); +		FCOE_NETDEV_DBG(netdev, "Cannot find hba structure\n");  		goto err2;  	}  	if (!lport->link_up)  		goto err2; -	FCOE_NETDEV_DBG(netdev, "skb_info: len:%d data_len:%d head:%p " -			"data:%p tail:%p end:%p sum:%d dev:%s", +	FCOE_NETDEV_DBG(netdev, +			"skb_info: len:%d data_len:%d head:%p data:%p tail:%p end:%p sum:%d dev:%s\n",  			skb->len, skb->data_len, skb->head, skb->data,  			skb_tail_pointer(skb), skb_end_pointer(skb),  			skb->csum, skb->dev ? skb->dev->name : "<NULL>"); + +	skb = skb_share_check(skb, GFP_ATOMIC); + +	if (skb == NULL) +		return NET_RX_DROP; +  	eh = eth_hdr(skb); -	if (is_fip_mode(&fcoe->ctlr) && -	    compare_ether_addr(eh->h_source, fcoe->ctlr.dest_addr)) { +	if (is_fip_mode(ctlr) && +	    !ether_addr_equal(eh->h_source, ctlr->dest_addr)) {  		FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n",  				eh->h_source);  		goto err; @@ -1289,21 +1487,28 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,  	fr = fcoe_dev_from_skb(skb);  	fr->fr_dev = lport; -	fr->ptype = ptype;  	/*  	 * In case the incoming frame's exchange is originated from  	 * the initiator, then received frame's exchange id is ANDed  	 * with fc_cpu_mask bits to get the same cpu on which exchange -	 * was originated, otherwise just use the current cpu. +	 * was originated, otherwise select cpu using rx exchange id +	 * or fcoe_select_cpu().  	 */  	if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX)  		cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask; -	else -		cpu = smp_processor_id(); +	else { +		if (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN) +			cpu = fcoe_select_cpu(); +		else +			cpu = ntohs(fh->fh_rx_id) & fc_cpu_mask; +	} + +	if (cpu >= nr_cpu_ids) +		goto err;  	fps = &per_cpu(fcoe_percpu, cpu); -	spin_lock_bh(&fps->fcoe_rx_list.lock); +	spin_lock(&fps->fcoe_rx_list.lock);  	if (unlikely(!fps->thread)) {  		/*  		 * The targeted CPU is not ready, let's target @@ -1314,12 +1519,12 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,  				"ready for incoming skb- using first online "  				"CPU.\n"); -		spin_unlock_bh(&fps->fcoe_rx_list.lock); +		spin_unlock(&fps->fcoe_rx_list.lock);  		cpu = cpumask_first(cpu_online_mask);  		fps = &per_cpu(fcoe_percpu, cpu); -		spin_lock_bh(&fps->fcoe_rx_list.lock); +		spin_lock(&fps->fcoe_rx_list.lock);  		if (!fps->thread) { -			spin_unlock_bh(&fps->fcoe_rx_list.lock); +			spin_unlock(&fps->fcoe_rx_list.lock);  			goto err;  		}  	} @@ -1330,137 +1535,44 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,  	 * so we're free to queue skbs into it's queue.  	 */ -	/* If this is a SCSI-FCP frame, and this is already executing on the -	 * correct CPU, and the queue for this CPU is empty, then go ahead -	 * and process the frame directly in the softirq context. -	 * This lets us process completions without context switching from the -	 * NET_RX softirq, to our receive processing thread, and then back to -	 * BLOCK softirq context. +	/* +	 * Note: We used to have a set of conditions under which we would +	 * call fcoe_recv_frame directly, rather than queuing to the rx list +	 * as it could save a few cycles, but doing so is prohibited, as +	 * fcoe_recv_frame has several paths that may sleep, which is forbidden +	 * in softirq context.  	 */ -	if (fh->fh_type == FC_TYPE_FCP && -	    cpu == smp_processor_id() && -	    skb_queue_empty(&fps->fcoe_rx_list)) { -		spin_unlock_bh(&fps->fcoe_rx_list.lock); -		fcoe_recv_frame(skb); -	} else { -		__skb_queue_tail(&fps->fcoe_rx_list, skb); -		if (fps->fcoe_rx_list.qlen == 1) -			wake_up_process(fps->thread); -		spin_unlock_bh(&fps->fcoe_rx_list.lock); -	} +	__skb_queue_tail(&fps->fcoe_rx_list, skb); +	if (fps->thread->state == TASK_INTERRUPTIBLE) +		wake_up_process(fps->thread); +	spin_unlock(&fps->fcoe_rx_list.lock); -	return 0; +	return NET_RX_SUCCESS;  err: -	per_cpu_ptr(lport->dev_stats, get_cpu())->ErrorFrames++; +	per_cpu_ptr(lport->stats, get_cpu())->ErrorFrames++;  	put_cpu();  err2:  	kfree_skb(skb); -	return -1; -} - -/** - * fcoe_start_io() - Start FCoE I/O - * @skb: The packet to be transmitted - * - * This routine is called from the net device to start transmitting - * FCoE packets. - * - * Returns: 0 for success - */ -static inline int fcoe_start_io(struct sk_buff *skb) -{ -	struct sk_buff *nskb; -	int rc; - -	nskb = skb_clone(skb, GFP_ATOMIC); -	rc = dev_queue_xmit(nskb); -	if (rc != 0) -		return rc; -	kfree_skb(skb); -	return 0; +	return NET_RX_DROP;  }  /** - * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC + * fcoe_alloc_paged_crc_eof() - Allocate a page to be used for the trailer CRC   * @skb:  The packet to be transmitted   * @tlen: The total length of the trailer   * - * This routine allocates a page for frame trailers. The page is re-used if - * there is enough room left on it for the current trailer. If there isn't - * enough buffer left a new page is allocated for the trailer. Reference to - * the page from this function as well as the skbs using the page fragments - * ensure that the page is freed at the appropriate time. - *   * Returns: 0 for success   */ -static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen) +static int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen)  {  	struct fcoe_percpu_s *fps; -	struct page *page; +	int rc;  	fps = &get_cpu_var(fcoe_percpu); -	page = fps->crc_eof_page; -	if (!page) { -		page = alloc_page(GFP_ATOMIC); -		if (!page) { -			put_cpu_var(fcoe_percpu); -			return -ENOMEM; -		} -		fps->crc_eof_page = page; -		fps->crc_eof_offset = 0; -	} - -	get_page(page); -	skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, -			   fps->crc_eof_offset, tlen); -	skb->len += tlen; -	skb->data_len += tlen; -	skb->truesize += tlen; -	fps->crc_eof_offset += sizeof(struct fcoe_crc_eof); - -	if (fps->crc_eof_offset >= PAGE_SIZE) { -		fps->crc_eof_page = NULL; -		fps->crc_eof_offset = 0; -		put_page(page); -	} +	rc = fcoe_get_paged_crc_eof(skb, tlen, fps);  	put_cpu_var(fcoe_percpu); -	return 0; -} -/** - * fcoe_fc_crc() - Calculates the CRC for a given frame - * @fp: The frame to be checksumed - * - * This uses crc32() routine to calculate the CRC for a frame - * - * Return: The 32 bit CRC value - */ -u32 fcoe_fc_crc(struct fc_frame *fp) -{ -	struct sk_buff *skb = fp_skb(fp); -	struct skb_frag_struct *frag; -	unsigned char *data; -	unsigned long off, len, clen; -	u32 crc; -	unsigned i; - -	crc = crc32(~0, skb->data, skb_headlen(skb)); - -	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -		frag = &skb_shinfo(skb)->frags[i]; -		off = frag->page_offset; -		len = frag->size; -		while (len > 0) { -			clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK)); -			data = kmap_atomic(frag->page + (off >> PAGE_SHIFT), -					   KM_SKB_DATA_SOFTIRQ); -			crc = crc32(crc, data + (off & ~PAGE_MASK), clen); -			kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ); -			off += clen; -			len -= clen; -		} -	} -	return crc; +	return rc;  }  /** @@ -1470,20 +1582,21 @@ u32 fcoe_fc_crc(struct fc_frame *fp)   *   * Return: 0 for success   */ -int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) +static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)  {  	int wlen;  	u32 crc;  	struct ethhdr *eh;  	struct fcoe_crc_eof *cp;  	struct sk_buff *skb; -	struct fcoe_dev_stats *stats; +	struct fc_stats *stats;  	struct fc_frame_header *fh;  	unsigned int hlen;		/* header length implies the version */  	unsigned int tlen;		/* trailer length */  	unsigned int elen;		/* eth header, may include vlan */  	struct fcoe_port *port = lport_priv(lport); -	struct fcoe_interface *fcoe = port->fcoe; +	struct fcoe_interface *fcoe = port->priv; +	struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);  	u8 sof, eof;  	struct fcoe_hdr *hp; @@ -1499,7 +1612,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)  	}  	if (unlikely(fh->fh_type == FC_TYPE_ELS) && -	    fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb)) +	    fcoe_ctlr_els_send(ctlr, lport, skb))  		return 0;  	sof = fr_sof(fp); @@ -1512,7 +1625,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)  	/* crc offload */  	if (likely(lport->crc_offload)) { -		skb->ip_summed = CHECKSUM_PARTIAL; +		skb->ip_summed = CHECKSUM_UNNECESSARY;  		skb->csum_start = skb_headroom(skb);  		skb->csum_offset = skb->len;  		crc = 0; @@ -1524,12 +1637,12 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)  	/* copy port crc and eof to the skb buff */  	if (skb_is_nonlinear(skb)) {  		skb_frag_t *frag; -		if (fcoe_get_paged_crc_eof(skb, tlen)) { +		if (fcoe_alloc_paged_crc_eof(skb, tlen)) {  			kfree_skb(skb);  			return -ENOMEM;  		}  		frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1]; -		cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ) +		cp = kmap_atomic(skb_frag_page(frag))  			+ frag->page_offset;  	} else {  		cp = (struct fcoe_crc_eof *)skb_put(skb, tlen); @@ -1540,7 +1653,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)  	cp->fcoe_crc32 = cpu_to_le32(~crc);  	if (skb_is_nonlinear(skb)) { -		kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ); +		kunmap_atomic(cp);  		cp = NULL;  	} @@ -1550,17 +1663,28 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)  	skb_reset_network_header(skb);  	skb->mac_len = elen;  	skb->protocol = htons(ETH_P_FCOE); -	skb->dev = fcoe->netdev; +	skb->priority = fcoe->priority; + +	if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN && +	    fcoe->realdev->features & NETIF_F_HW_VLAN_CTAG_TX) { +		/* must set skb->dev before calling vlan_put_tag */ +		skb->dev = fcoe->realdev; +		skb = __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), +					     vlan_dev_vlan_id(fcoe->netdev)); +		if (!skb) +			return -ENOMEM; +	} else +		skb->dev = fcoe->netdev;  	/* fill up mac and fcoe headers */  	eh = eth_hdr(skb);  	eh->h_proto = htons(ETH_P_FCOE); -	memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN); -	if (fcoe->ctlr.map_dest) +	memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN); +	if (ctlr->map_dest)  		memcpy(eh->h_dest + 3, fh->fh_d_id, 3); -	if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN)) -		memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN); +	if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN)) +		memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN);  	else  		memcpy(eh->h_source, port->data_src_addr, ETH_ALEN); @@ -1579,18 +1703,14 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)  		skb_shinfo(skb)->gso_size = 0;  	}  	/* update tx stats: regardless if LLD fails */ -	stats = per_cpu_ptr(lport->dev_stats, get_cpu()); +	stats = per_cpu_ptr(lport->stats, get_cpu());  	stats->TxFrames++;  	stats->TxWords += wlen;  	put_cpu();  	/* send down to lld */  	fr_dev(fp) = lport; -	if (port->fcoe_pending_queue.qlen) -		fcoe_check_wait_queue(lport, skb); -	else if (fcoe_start_io(skb)) -		fcoe_check_wait_queue(lport, skb); - +	fcoe_port_send(port, skb);  	return 0;  } @@ -1604,6 +1724,59 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)  }  /** + * fcoe_filter_frames() - filter out bad fcoe frames, i.e. bad CRC + * @lport: The local port the frame was received on + * @fp:	   The received frame + * + * Return: 0 on passing filtering checks + */ +static inline int fcoe_filter_frames(struct fc_lport *lport, +				     struct fc_frame *fp) +{ +	struct fcoe_ctlr *ctlr; +	struct fcoe_interface *fcoe; +	struct fc_frame_header *fh; +	struct sk_buff *skb = (struct sk_buff *)fp; +	struct fc_stats *stats; + +	/* +	 * We only check CRC if no offload is available and if it is +	 * it's solicited data, in which case, the FCP layer would +	 * check it during the copy. +	 */ +	if (lport->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY) +		fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; +	else +		fr_flags(fp) |= FCPHF_CRC_UNCHECKED; + +	fh = (struct fc_frame_header *) skb_transport_header(skb); +	fh = fc_frame_header_get(fp); +	if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP) +		return 0; + +	fcoe = ((struct fcoe_port *)lport_priv(lport))->priv; +	ctlr = fcoe_to_ctlr(fcoe); +	if (is_fip_mode(ctlr) && fc_frame_payload_op(fp) == ELS_LOGO && +	    ntoh24(fh->fh_s_id) == FC_FID_FLOGI) { +		FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n"); +		return -EINVAL; +	} + +	if (!(fr_flags(fp) & FCPHF_CRC_UNCHECKED) || +	    le32_to_cpu(fr_crc(fp)) == ~crc32(~0, skb->data, skb->len)) { +		fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; +		return 0; +	} + +	stats = per_cpu_ptr(lport->stats, get_cpu()); +	stats->InvalidCRCCount++; +	if (stats->InvalidCRCCount < 5) +		printk(KERN_WARNING "fcoe: dropping frame with CRC error\n"); +	put_cpu(); +	return -EINVAL; +} + +/**   * fcoe_recv_frame() - process a single received frame   * @skb: frame to process   */ @@ -1612,8 +1785,7 @@ static void fcoe_recv_frame(struct sk_buff *skb)  	u32 fr_len;  	struct fc_lport *lport;  	struct fcoe_rcv_info *fr; -	struct fcoe_dev_stats *stats; -	struct fc_frame_header *fh; +	struct fc_stats *stats;  	struct fcoe_crc_eof crc_eof;  	struct fc_frame *fp;  	struct fcoe_port *port; @@ -1623,30 +1795,28 @@ static void fcoe_recv_frame(struct sk_buff *skb)  	lport = fr->fr_dev;  	if (unlikely(!lport)) {  		if (skb->destructor != fcoe_percpu_flush_done) -			FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb"); +			FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb\n");  		kfree_skb(skb);  		return;  	} -	FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d " -			"head:%p data:%p tail:%p end:%p sum:%d dev:%s", +	FCOE_NETDEV_DBG(skb->dev, +			"skb_info: len:%d data_len:%d head:%p data:%p tail:%p end:%p sum:%d dev:%s\n",  			skb->len, skb->data_len,  			skb->head, skb->data, skb_tail_pointer(skb),  			skb_end_pointer(skb), skb->csum,  			skb->dev ? skb->dev->name : "<NULL>");  	port = lport_priv(lport); -	if (skb_is_nonlinear(skb)) -		skb_linearize(skb);	/* not ideal */ +	skb_linearize(skb); /* check for skb_is_nonlinear is within skb_linearize */  	/*  	 * Frame length checks and setting up the header pointers  	 * was done in fcoe_rcv already.  	 */  	hp = (struct fcoe_hdr *) skb_network_header(skb); -	fh = (struct fc_frame_header *) skb_transport_header(skb); -	stats = per_cpu_ptr(lport->dev_stats, get_cpu()); +	stats = per_cpu_ptr(lport->stats, get_cpu());  	if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {  		if (stats->ErrorFrames < 5)  			printk(KERN_WARNING "fcoe: FCoE version " @@ -1677,35 +1847,11 @@ static void fcoe_recv_frame(struct sk_buff *skb)  	if (pskb_trim(skb, fr_len))  		goto drop; -	/* -	 * We only check CRC if no offload is available and if it is -	 * it's solicited data, in which case, the FCP layer would -	 * check it during the copy. -	 */ -	if (lport->crc_offload && -	    skb->ip_summed == CHECKSUM_UNNECESSARY) -		fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; -	else -		fr_flags(fp) |= FCPHF_CRC_UNCHECKED; - -	fh = fc_frame_header_get(fp); -	if ((fh->fh_r_ctl != FC_RCTL_DD_SOL_DATA || -	    fh->fh_type != FC_TYPE_FCP) && -	    (fr_flags(fp) & FCPHF_CRC_UNCHECKED)) { -		if (le32_to_cpu(fr_crc(fp)) != -		    ~crc32(~0, skb->data, fr_len)) { -			if (stats->InvalidCRCCount < 5) -				printk(KERN_WARNING "fcoe: dropping " -				       "frame with CRC error\n"); -			stats->InvalidCRCCount++; -			goto drop; -		} -		fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; +	if (!fcoe_filter_frames(lport, fp)) { +		put_cpu(); +		fc_exch_recv(lport, fp); +		return;  	} -	put_cpu(); -	fc_exch_recv(lport, fp); -	return; -  drop:  	stats->ErrorFrames++;  	put_cpu(); @@ -1718,87 +1864,37 @@ drop:   *   * Return: 0 for success   */ -int fcoe_percpu_receive_thread(void *arg) +static int fcoe_percpu_receive_thread(void *arg)  {  	struct fcoe_percpu_s *p = arg;  	struct sk_buff *skb; +	struct sk_buff_head tmp; + +	skb_queue_head_init(&tmp); -	set_user_nice(current, -20); +	set_user_nice(current, MIN_NICE); +retry:  	while (!kthread_should_stop()) {  		spin_lock_bh(&p->fcoe_rx_list.lock); -		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) { +		skb_queue_splice_init(&p->fcoe_rx_list, &tmp); + +		if (!skb_queue_len(&tmp)) {  			set_current_state(TASK_INTERRUPTIBLE);  			spin_unlock_bh(&p->fcoe_rx_list.lock);  			schedule();  			set_current_state(TASK_RUNNING); -			if (kthread_should_stop()) -				return 0; -			spin_lock_bh(&p->fcoe_rx_list.lock); +			goto retry;  		} -		spin_unlock_bh(&p->fcoe_rx_list.lock); -		fcoe_recv_frame(skb); -	} -	return 0; -} - -/** - * fcoe_check_wait_queue() - Attempt to clear the transmit backlog - * @lport: The local port whose backlog is to be cleared - * - * This empties the wait_queue, dequeues the head of the wait_queue queue - * and calls fcoe_start_io() for each packet. If all skb have been - * transmitted it returns the qlen. If an error occurs it restores - * wait_queue (to try again later) and returns -1. - * - * The wait_queue is used when the skb transmit fails. The failed skb - * will go in the wait_queue which will be emptied by the timer function or - * by the next skb transmit. - */ -static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb) -{ -	struct fcoe_port *port = lport_priv(lport); -	int rc; - -	spin_lock_bh(&port->fcoe_pending_queue.lock); - -	if (skb) -		__skb_queue_tail(&port->fcoe_pending_queue, skb); -	if (port->fcoe_pending_queue_active) -		goto out; -	port->fcoe_pending_queue_active = 1; - -	while (port->fcoe_pending_queue.qlen) { -		/* keep qlen > 0 until fcoe_start_io succeeds */ -		port->fcoe_pending_queue.qlen++; -		skb = __skb_dequeue(&port->fcoe_pending_queue); +		spin_unlock_bh(&p->fcoe_rx_list.lock); -		spin_unlock_bh(&port->fcoe_pending_queue.lock); -		rc = fcoe_start_io(skb); -		spin_lock_bh(&port->fcoe_pending_queue.lock); +		while ((skb = __skb_dequeue(&tmp)) != NULL) +			fcoe_recv_frame(skb); -		if (rc) { -			__skb_queue_head(&port->fcoe_pending_queue, skb); -			/* undo temporary increment above */ -			port->fcoe_pending_queue.qlen--; -			break; -		} -		/* undo temporary increment above */ -		port->fcoe_pending_queue.qlen--;  	} - -	if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH) -		lport->qfull = 0; -	if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer)) -		mod_timer(&port->timer, jiffies + 2); -	port->fcoe_pending_queue_active = 0; -out: -	if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) -		lport->qfull = 1; -	spin_unlock_bh(&port->fcoe_pending_queue.lock); -	return; +	return 0;  }  /** @@ -1806,6 +1902,7 @@ out:   */  static void fcoe_dev_setup(void)  { +	register_dcbevent_notifier(&dcb_notifier);  	register_netdevice_notifier(&fcoe_notifier);  } @@ -1814,9 +1911,69 @@ static void fcoe_dev_setup(void)   */  static void fcoe_dev_cleanup(void)  { +	unregister_dcbevent_notifier(&dcb_notifier);  	unregister_netdevice_notifier(&fcoe_notifier);  } +static struct fcoe_interface * +fcoe_hostlist_lookup_realdev_port(struct net_device *netdev) +{ +	struct fcoe_interface *fcoe; +	struct net_device *real_dev; + +	list_for_each_entry(fcoe, &fcoe_hostlist, list) { +		if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN) +			real_dev = vlan_dev_real_dev(fcoe->netdev); +		else +			real_dev = fcoe->netdev; + +		if (netdev == real_dev) +			return fcoe; +	} +	return NULL; +} + +static int fcoe_dcb_app_notification(struct notifier_block *notifier, +				     ulong event, void *ptr) +{ +	struct dcb_app_type *entry = ptr; +	struct fcoe_ctlr *ctlr; +	struct fcoe_interface *fcoe; +	struct net_device *netdev; +	int prio; + +	if (entry->app.selector != DCB_APP_IDTYPE_ETHTYPE) +		return NOTIFY_OK; + +	netdev = dev_get_by_index(&init_net, entry->ifindex); +	if (!netdev) +		return NOTIFY_OK; + +	fcoe = fcoe_hostlist_lookup_realdev_port(netdev); +	dev_put(netdev); +	if (!fcoe) +		return NOTIFY_OK; + +	ctlr = fcoe_to_ctlr(fcoe); + +	if (entry->dcbx & DCB_CAP_DCBX_VER_CEE) +		prio = ffs(entry->app.priority) - 1; +	else +		prio = entry->app.priority; + +	if (prio < 0) +		return NOTIFY_OK; + +	if (entry->app.protocol == ETH_P_FIP || +	    entry->app.protocol == ETH_P_FCOE) +		ctlr->priority = prio; + +	if (entry->app.protocol == ETH_P_FCOE) +		fcoe->priority = prio; + +	return NOTIFY_OK; +} +  /**   * fcoe_device_notification() - Handler for net device events   * @notifier: The context of the notification @@ -1830,18 +1987,21 @@ static void fcoe_dev_cleanup(void)  static int fcoe_device_notification(struct notifier_block *notifier,  				    ulong event, void *ptr)  { +	struct fcoe_ctlr_device *cdev;  	struct fc_lport *lport = NULL; -	struct net_device *netdev = ptr; +	struct net_device *netdev = netdev_notifier_info_to_dev(ptr); +	struct fcoe_ctlr *ctlr;  	struct fcoe_interface *fcoe;  	struct fcoe_port *port; -	struct fcoe_dev_stats *stats; +	struct fc_stats *stats;  	u32 link_possible = 1;  	u32 mfs;  	int rc = NOTIFY_OK;  	list_for_each_entry(fcoe, &fcoe_hostlist, list) {  		if (fcoe->netdev == netdev) { -			lport = fcoe->ctlr.lp; +			ctlr = fcoe_to_ctlr(fcoe); +			lport = ctlr->lp;  			break;  		}  	} @@ -1870,9 +2030,8 @@ static int fcoe_device_notification(struct notifier_block *notifier,  		break;  	case NETDEV_UNREGISTER:  		list_del(&fcoe->list); -		port = lport_priv(fcoe->ctlr.lp); -		fcoe_interface_cleanup(fcoe); -		schedule_work(&port->destroy_work); +		port = lport_priv(ctlr->lp); +		queue_work(fcoe_wq, &port->destroy_work);  		goto out;  		break;  	case NETDEV_FEAT_CHANGE: @@ -1885,202 +2044,158 @@ static int fcoe_device_notification(struct notifier_block *notifier,  	fcoe_link_speed_update(lport); -	if (link_possible && !fcoe_link_ok(lport)) -		fcoe_ctlr_link_up(&fcoe->ctlr); -	else if (fcoe_ctlr_link_down(&fcoe->ctlr)) { -		stats = per_cpu_ptr(lport->dev_stats, get_cpu()); -		stats->LinkFailureCount++; -		put_cpu(); -		fcoe_clean_pending_queue(lport); +	cdev = fcoe_ctlr_to_ctlr_dev(ctlr); + +	if (link_possible && !fcoe_link_ok(lport)) { +		switch (cdev->enabled) { +		case FCOE_CTLR_DISABLED: +			pr_info("Link up while interface is disabled.\n"); +			break; +		case FCOE_CTLR_ENABLED: +		case FCOE_CTLR_UNUSED: +			fcoe_ctlr_link_up(ctlr); +		}; +	} else if (fcoe_ctlr_link_down(ctlr)) { +		switch (cdev->enabled) { +		case FCOE_CTLR_DISABLED: +			pr_info("Link down while interface is disabled.\n"); +			break; +		case FCOE_CTLR_ENABLED: +		case FCOE_CTLR_UNUSED: +			stats = per_cpu_ptr(lport->stats, get_cpu()); +			stats->LinkFailureCount++; +			put_cpu(); +			fcoe_clean_pending_queue(lport); +		};  	}  out:  	return rc;  }  /** - * fcoe_if_to_netdev() - Parse a name buffer to get a net device - * @buffer: The name of the net device - * - * Returns: NULL or a ptr to net_device - */ -static struct net_device *fcoe_if_to_netdev(const char *buffer) -{ -	char *cp; -	char ifname[IFNAMSIZ + 2]; - -	if (buffer) { -		strlcpy(ifname, buffer, IFNAMSIZ); -		cp = ifname + strlen(ifname); -		while (--cp >= ifname && *cp == '\n') -			*cp = '\0'; -		return dev_get_by_name(&init_net, ifname); -	} -	return NULL; -} - -/**   * fcoe_disable() - Disables a FCoE interface - * @buffer: The name of the Ethernet interface to be disabled - * @kp:	    The associated kernel parameter + * @netdev  : The net_device object the Ethernet interface to create on   * - * Called from sysfs. + * Called from fcoe transport.   *   * Returns: 0 for success + * + * Deprecated: use fcoe_ctlr_enabled()   */ -static int fcoe_disable(const char *buffer, struct kernel_param *kp) +static int fcoe_disable(struct net_device *netdev)  { +	struct fcoe_ctlr *ctlr;  	struct fcoe_interface *fcoe; -	struct net_device *netdev;  	int rc = 0;  	mutex_lock(&fcoe_config_mutex); -#ifdef CONFIG_FCOE_MODULE -	/* -	 * Make sure the module has been initialized, and is not about to be -	 * removed.  Module paramter sysfs files are writable before the -	 * module_init function is called and after module_exit. -	 */ -	if (THIS_MODULE->state != MODULE_STATE_LIVE) { -		rc = -ENODEV; -		goto out_nodev; -	} -#endif - -	netdev = fcoe_if_to_netdev(buffer); -	if (!netdev) { -		rc = -ENODEV; -		goto out_nodev; -	} - -	if (!rtnl_trylock()) { -		dev_put(netdev); -		mutex_unlock(&fcoe_config_mutex); -		return restart_syscall(); -	} +	rtnl_lock();  	fcoe = fcoe_hostlist_lookup_port(netdev);  	rtnl_unlock();  	if (fcoe) { -		fcoe_ctlr_link_down(&fcoe->ctlr); -		fcoe_clean_pending_queue(fcoe->ctlr.lp); +		ctlr = fcoe_to_ctlr(fcoe); +		fcoe_ctlr_link_down(ctlr); +		fcoe_clean_pending_queue(ctlr->lp);  	} else  		rc = -ENODEV; -	dev_put(netdev); -out_nodev:  	mutex_unlock(&fcoe_config_mutex);  	return rc;  }  /**   * fcoe_enable() - Enables a FCoE interface - * @buffer: The name of the Ethernet interface to be enabled - * @kp:     The associated kernel parameter + * @netdev  : The net_device object the Ethernet interface to create on   * - * Called from sysfs. + * Called from fcoe transport.   *   * Returns: 0 for success   */ -static int fcoe_enable(const char *buffer, struct kernel_param *kp) +static int fcoe_enable(struct net_device *netdev)  { +	struct fcoe_ctlr *ctlr;  	struct fcoe_interface *fcoe; -	struct net_device *netdev;  	int rc = 0;  	mutex_lock(&fcoe_config_mutex); -#ifdef CONFIG_FCOE_MODULE -	/* -	 * Make sure the module has been initialized, and is not about to be -	 * removed.  Module paramter sysfs files are writable before the -	 * module_init function is called and after module_exit. -	 */ -	if (THIS_MODULE->state != MODULE_STATE_LIVE) { -		rc = -ENODEV; -		goto out_nodev; -	} -#endif +	rtnl_lock(); +	fcoe = fcoe_hostlist_lookup_port(netdev); +	rtnl_unlock(); -	netdev = fcoe_if_to_netdev(buffer); -	if (!netdev) { +	if (!fcoe) {  		rc = -ENODEV; -		goto out_nodev; -	} - -	if (!rtnl_trylock()) { -		dev_put(netdev); -		mutex_unlock(&fcoe_config_mutex); -		return restart_syscall(); +		goto out;  	} -	fcoe = fcoe_hostlist_lookup_port(netdev); -	rtnl_unlock(); +	ctlr = fcoe_to_ctlr(fcoe); -	if (!fcoe) -		rc = -ENODEV; -	else if (!fcoe_link_ok(fcoe->ctlr.lp)) -		fcoe_ctlr_link_up(&fcoe->ctlr); +	if (!fcoe_link_ok(ctlr->lp)) +		fcoe_ctlr_link_up(ctlr); -	dev_put(netdev); -out_nodev: +out:  	mutex_unlock(&fcoe_config_mutex);  	return rc;  }  /** + * fcoe_ctlr_enabled() - Enable or disable an FCoE Controller + * @cdev: The FCoE Controller that is being enabled or disabled + * + * fcoe_sysfs will ensure that the state of 'enabled' has + * changed, so no checking is necessary here. This routine simply + * calls fcoe_enable or fcoe_disable, both of which are deprecated. + * When those routines are removed the functionality can be merged + * here. + */ +static int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev) +{ +	struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(cdev); +	struct fc_lport *lport = ctlr->lp; +	struct net_device *netdev = fcoe_netdev(lport); + +	switch (cdev->enabled) { +	case FCOE_CTLR_ENABLED: +		return fcoe_enable(netdev); +	case FCOE_CTLR_DISABLED: +		return fcoe_disable(netdev); +	case FCOE_CTLR_UNUSED: +	default: +		return -ENOTSUPP; +	}; +} + +/**   * fcoe_destroy() - Destroy a FCoE interface - * @buffer: The name of the Ethernet interface to be destroyed - * @kp:	    The associated kernel parameter + * @netdev  : The net_device object the Ethernet interface to create on   * - * Called from sysfs. + * Called from fcoe transport   *   * Returns: 0 for success   */ -static int fcoe_destroy(const char *buffer, struct kernel_param *kp) +static int fcoe_destroy(struct net_device *netdev)  { +	struct fcoe_ctlr *ctlr;  	struct fcoe_interface *fcoe; -	struct net_device *netdev; +	struct fc_lport *lport; +	struct fcoe_port *port;  	int rc = 0;  	mutex_lock(&fcoe_config_mutex); -#ifdef CONFIG_FCOE_MODULE -	/* -	 * Make sure the module has been initialized, and is not about to be -	 * removed.  Module paramter sysfs files are writable before the -	 * module_init function is called and after module_exit. -	 */ -	if (THIS_MODULE->state != MODULE_STATE_LIVE) { -		rc = -ENODEV; -		goto out_nodev; -	} -#endif - -	netdev = fcoe_if_to_netdev(buffer); -	if (!netdev) { -		rc = -ENODEV; -		goto out_nodev; -	} - -	if (!rtnl_trylock()) { -		dev_put(netdev); -		mutex_unlock(&fcoe_config_mutex); -		return restart_syscall(); -	} - +	rtnl_lock();  	fcoe = fcoe_hostlist_lookup_port(netdev);  	if (!fcoe) { -		rtnl_unlock();  		rc = -ENODEV; -		goto out_putdev; +		goto out_nodev;  	} -	fcoe_interface_cleanup(fcoe); +	ctlr = fcoe_to_ctlr(fcoe); +	lport = ctlr->lp; +	port = lport_priv(lport);  	list_del(&fcoe->list); -	/* RTNL mutex is dropped by fcoe_if_destroy */ -	fcoe_if_destroy(fcoe->ctlr.lp); - -out_putdev: -	dev_put(netdev); +	queue_work(fcoe_wq, &port->destroy_work);  out_nodev: +	rtnl_unlock();  	mutex_unlock(&fcoe_config_mutex);  	return rc;  } @@ -2091,148 +2206,230 @@ out_nodev:   */  static void fcoe_destroy_work(struct work_struct *work)  { +	struct fcoe_ctlr_device *cdev; +	struct fcoe_ctlr *ctlr;  	struct fcoe_port *port; +	struct fcoe_interface *fcoe; +	struct Scsi_Host *shost; +	struct fc_host_attrs *fc_host; +	unsigned long flags; +	struct fc_vport *vport; +	struct fc_vport *next_vport;  	port = container_of(work, struct fcoe_port, destroy_work); +	shost = port->lport->host; +	fc_host = shost_to_fc_host(shost); + +	/* Loop through all the vports and mark them for deletion */ +	spin_lock_irqsave(shost->host_lock, flags); +	list_for_each_entry_safe(vport, next_vport, &fc_host->vports, peers) { +		if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) { +			continue; +		} else { +			vport->flags |= FC_VPORT_DELETING; +			queue_work(fc_host_work_q(shost), +				   &vport->vport_delete_work); +		} +	} +	spin_unlock_irqrestore(shost->host_lock, flags); + +	flush_workqueue(fc_host_work_q(shost)); +  	mutex_lock(&fcoe_config_mutex); -	rtnl_lock(); -	/* RTNL mutex is dropped by fcoe_if_destroy */ + +	fcoe = port->priv; +	ctlr = fcoe_to_ctlr(fcoe); +	cdev = fcoe_ctlr_to_ctlr_dev(ctlr); +  	fcoe_if_destroy(port->lport); +	fcoe_interface_cleanup(fcoe); +  	mutex_unlock(&fcoe_config_mutex); + +	fcoe_ctlr_device_delete(cdev);  }  /** - * fcoe_create() - Create a fcoe interface - * @buffer: The name of the Ethernet interface to create on - * @kp:	    The associated kernel param + * fcoe_match() - Check if the FCoE is supported on the given netdevice + * @netdev  : The net_device object the Ethernet interface to create on   * - * Called from sysfs. + * Called from fcoe transport.   * - * Returns: 0 for success + * Returns: always returns true as this is the default FCoE transport, + * i.e., support all netdevs.   */ -static int fcoe_create(const char *buffer, struct kernel_param *kp) +static bool fcoe_match(struct net_device *netdev)  { -	enum fip_state fip_mode = (enum fip_state)(long)kp->arg; -	int rc; -	struct fcoe_interface *fcoe; -	struct fc_lport *lport; -	struct net_device *netdev; +	return true; +} -	mutex_lock(&fcoe_config_mutex); +/** + * fcoe_dcb_create() - Initialize DCB attributes and hooks + * @netdev: The net_device object of the L2 link that should be queried + * @port: The fcoe_port to bind FCoE APP priority with + * @ + */ +static void fcoe_dcb_create(struct fcoe_interface *fcoe) +{ +#ifdef CONFIG_DCB +	int dcbx; +	u8 fup, up; +	struct net_device *netdev = fcoe->realdev; +	struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); +	struct dcb_app app = { +				.priority = 0, +				.protocol = ETH_P_FCOE +			     }; -	if (!rtnl_trylock()) { -		mutex_unlock(&fcoe_config_mutex); -		return restart_syscall(); -	} +	/* setup DCB priority attributes. */ +	if (netdev && netdev->dcbnl_ops && netdev->dcbnl_ops->getdcbx) { +		dcbx = netdev->dcbnl_ops->getdcbx(netdev); -#ifdef CONFIG_FCOE_MODULE -	/* -	 * Make sure the module has been initialized, and is not about to be -	 * removed.  Module paramter sysfs files are writable before the -	 * module_init function is called and after module_exit. -	 */ -	if (THIS_MODULE->state != MODULE_STATE_LIVE) { -		rc = -ENODEV; -		goto out_nomod; +		if (dcbx & DCB_CAP_DCBX_VER_IEEE) { +			app.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE; +			up = dcb_ieee_getapp_mask(netdev, &app); +			app.protocol = ETH_P_FIP; +			fup = dcb_ieee_getapp_mask(netdev, &app); +		} else { +			app.selector = DCB_APP_IDTYPE_ETHTYPE; +			up = dcb_getapp(netdev, &app); +			app.protocol = ETH_P_FIP; +			fup = dcb_getapp(netdev, &app); +		} + +		fcoe->priority = ffs(up) ? ffs(up) - 1 : 0; +		ctlr->priority = ffs(fup) ? ffs(fup) - 1 : fcoe->priority;  	}  #endif +} -	if (!try_module_get(THIS_MODULE)) { -		rc = -EINVAL; -		goto out_nomod; -	} +enum fcoe_create_link_state { +	FCOE_CREATE_LINK_DOWN, +	FCOE_CREATE_LINK_UP, +}; -	netdev = fcoe_if_to_netdev(buffer); -	if (!netdev) { -		rc = -ENODEV; -		goto out_nodev; -	} +/** + * _fcoe_create() - (internal) Create a fcoe interface + * @netdev  :   The net_device object the Ethernet interface to create on + * @fip_mode:   The FIP mode for this creation + * @link_state: The ctlr link state on creation + * + * Called from either the libfcoe 'create' module parameter + * via fcoe_create or from fcoe_syfs's ctlr_create file. + * + * libfcoe's 'create' module parameter is deprecated so some + * consolidation of code can be done when that interface is + * removed. + */ +static int _fcoe_create(struct net_device *netdev, enum fip_state fip_mode, +			enum fcoe_create_link_state link_state) +{ +	int rc = 0; +	struct fcoe_ctlr_device *ctlr_dev; +	struct fcoe_ctlr *ctlr; +	struct fcoe_interface *fcoe; +	struct fc_lport *lport; + +	mutex_lock(&fcoe_config_mutex); +	rtnl_lock();  	/* look for existing lport */  	if (fcoe_hostlist_lookup(netdev)) {  		rc = -EEXIST; -		goto out_putdev; +		goto out_nodev;  	}  	fcoe = fcoe_interface_create(netdev, fip_mode); -	if (!fcoe) { -		rc = -ENOMEM; -		goto out_putdev; +	if (IS_ERR(fcoe)) { +		rc = PTR_ERR(fcoe); +		goto out_nodev;  	} -	lport = fcoe_if_create(fcoe, &netdev->dev, 0); +	ctlr = fcoe_to_ctlr(fcoe); +	ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr); +	lport = fcoe_if_create(fcoe, &ctlr_dev->dev, 0);  	if (IS_ERR(lport)) {  		printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",  		       netdev->name);  		rc = -EIO; +		rtnl_unlock();  		fcoe_interface_cleanup(fcoe); -		goto out_free; +		mutex_unlock(&fcoe_config_mutex); +		fcoe_ctlr_device_delete(ctlr_dev); +		goto out;  	}  	/* Make this the "master" N_Port */ -	fcoe->ctlr.lp = lport; +	ctlr->lp = lport; -	/* add to lports list */ -	fcoe_hostlist_add(lport); +	/* setup DCB priority attributes. */ +	fcoe_dcb_create(fcoe);  	/* start FIP Discovery and FLOGI */  	lport->boot_time = jiffies;  	fc_fabric_login(lport); -	if (!fcoe_link_ok(lport)) -		fcoe_ctlr_link_up(&fcoe->ctlr);  	/* -	 * Release from init in fcoe_interface_create(), on success lport -	 * should be holding a reference taken in fcoe_if_create(). +	 * If the fcoe_ctlr_device is to be set to DISABLED +	 * it must be done after the lport is added to the +	 * hostlist, but before the rtnl_lock is released. +	 * This is because the rtnl_lock protects the +	 * hostlist that fcoe_device_notification uses. If +	 * the FCoE Controller is intended to be created +	 * DISABLED then 'enabled' needs to be considered +	 * handling link events. 'enabled' must be set +	 * before the lport can be found in the hostlist +	 * when a link up event is received.  	 */ -	fcoe_interface_put(fcoe); -	dev_put(netdev); -	rtnl_unlock(); -	mutex_unlock(&fcoe_config_mutex); +	if (link_state == FCOE_CREATE_LINK_UP) +		ctlr_dev->enabled = FCOE_CTLR_ENABLED; +	else +		ctlr_dev->enabled = FCOE_CTLR_DISABLED; + +	if (link_state == FCOE_CREATE_LINK_UP && +	    !fcoe_link_ok(lport)) { +		rtnl_unlock(); +		fcoe_ctlr_link_up(ctlr); +		mutex_unlock(&fcoe_config_mutex); +		return rc; +	} -	return 0; -out_free: -	fcoe_interface_put(fcoe); -out_putdev: -	dev_put(netdev);  out_nodev: -	module_put(THIS_MODULE); -out_nomod:  	rtnl_unlock();  	mutex_unlock(&fcoe_config_mutex); +out:  	return rc;  }  /** - * fcoe_link_speed_update() - Update the supported and actual link speeds - * @lport: The local port to update speeds for + * fcoe_create() - Create a fcoe interface + * @netdev  : The net_device object the Ethernet interface to create on + * @fip_mode: The FIP mode for this creation + * + * Called from fcoe transport   * - * Returns: 0 if the ethtool query was successful - *          -1 if the ethtool query failed + * Returns: 0 for success   */ -int fcoe_link_speed_update(struct fc_lport *lport) +static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)  { -	struct fcoe_port *port = lport_priv(lport); -	struct net_device *netdev = port->fcoe->netdev; -	struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - -	if (!dev_ethtool_get_settings(netdev, &ecmd)) { -		lport->link_supported_speeds &= -			~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); -		if (ecmd.supported & (SUPPORTED_1000baseT_Half | -				      SUPPORTED_1000baseT_Full)) -			lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; -		if (ecmd.supported & SUPPORTED_10000baseT_Full) -			lport->link_supported_speeds |= -				FC_PORTSPEED_10GBIT; -		if (ecmd.speed == SPEED_1000) -			lport->link_speed = FC_PORTSPEED_1GBIT; -		if (ecmd.speed == SPEED_10000) -			lport->link_speed = FC_PORTSPEED_10GBIT; +	return _fcoe_create(netdev, fip_mode, FCOE_CREATE_LINK_UP); +} -		return 0; -	} -	return -1; +/** + * fcoe_ctlr_alloc() - Allocate a fcoe interface from fcoe_sysfs + * @netdev: The net_device to be used by the allocated FCoE Controller + * + * This routine is called from fcoe_sysfs. It will start the fcoe_ctlr + * in a link_down state. The allows the user an opportunity to configure + * the FCoE Controller from sysfs before enabling the FCoE Controller. + * + * Creating in with this routine starts the FCoE Controller in Fabric + * mode. The user can change to VN2VN or another mode before enabling. + */ +static int fcoe_ctlr_alloc(struct net_device *netdev) +{ +	return _fcoe_create(netdev, FIP_MODE_FABRIC, +			    FCOE_CREATE_LINK_DOWN);  }  /** @@ -2242,10 +2439,9 @@ int fcoe_link_speed_update(struct fc_lport *lport)   * Returns: 0 if link is UP and OK, -1 if not   *   */ -int fcoe_link_ok(struct fc_lport *lport) +static int fcoe_link_ok(struct fc_lport *lport)  { -	struct fcoe_port *port = lport_priv(lport); -	struct net_device *netdev = port->fcoe->netdev; +	struct net_device *netdev = fcoe_netdev(lport);  	if (netif_oper_up(netdev))  		return 0; @@ -2263,42 +2459,25 @@ int fcoe_link_ok(struct fc_lport *lport)   * there no packets that will be handled by the lport, but also that any   * threads already handling packet have returned.   */ -void fcoe_percpu_clean(struct fc_lport *lport) +static void fcoe_percpu_clean(struct fc_lport *lport)  {  	struct fcoe_percpu_s *pp; -	struct fcoe_rcv_info *fr; -	struct sk_buff_head *list; -	struct sk_buff *skb, *next; -	struct sk_buff *head; +	struct sk_buff *skb;  	unsigned int cpu;  	for_each_possible_cpu(cpu) {  		pp = &per_cpu(fcoe_percpu, cpu); -		spin_lock_bh(&pp->fcoe_rx_list.lock); -		list = &pp->fcoe_rx_list; -		head = list->next; -		for (skb = head; skb != (struct sk_buff *)list; -		     skb = next) { -			next = skb->next; -			fr = fcoe_dev_from_skb(skb); -			if (fr->fr_dev == lport) { -				__skb_unlink(skb, list); -				kfree_skb(skb); -			} -		} -		if (!pp->thread || !cpu_online(cpu)) { -			spin_unlock_bh(&pp->fcoe_rx_list.lock); +		if (!pp->thread || !cpu_online(cpu))  			continue; -		}  		skb = dev_alloc_skb(0); -		if (!skb) { -			spin_unlock_bh(&pp->fcoe_rx_list.lock); +		if (!skb)  			continue; -		} +  		skb->destructor = fcoe_percpu_flush_done; +		spin_lock_bh(&pp->fcoe_rx_list.lock);  		__skb_queue_tail(&pp->fcoe_rx_list, skb);  		if (pp->fcoe_rx_list.qlen == 1)  			wake_up_process(pp->thread); @@ -2309,33 +2488,25 @@ void fcoe_percpu_clean(struct fc_lport *lport)  }  /** - * fcoe_clean_pending_queue() - Dequeue a skb and free it - * @lport: The local port to dequeue a skb on - */ -void fcoe_clean_pending_queue(struct fc_lport *lport) -{ -	struct fcoe_port  *port = lport_priv(lport); -	struct sk_buff *skb; - -	spin_lock_bh(&port->fcoe_pending_queue.lock); -	while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) { -		spin_unlock_bh(&port->fcoe_pending_queue.lock); -		kfree_skb(skb); -		spin_lock_bh(&port->fcoe_pending_queue.lock); -	} -	spin_unlock_bh(&port->fcoe_pending_queue.lock); -} - -/**   * fcoe_reset() - Reset a local port   * @shost: The SCSI host associated with the local port to be reset   *   * Returns: Always 0 (return value required by FC transport template)   */ -int fcoe_reset(struct Scsi_Host *shost) +static int fcoe_reset(struct Scsi_Host *shost)  {  	struct fc_lport *lport = shost_priv(shost); -	fc_lport_reset(lport); +	struct fcoe_port *port = lport_priv(lport); +	struct fcoe_interface *fcoe = port->priv; +	struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); +	struct fcoe_ctlr_device *cdev = fcoe_ctlr_to_ctlr_dev(ctlr); + +	fcoe_ctlr_link_down(ctlr); +	fcoe_clean_pending_queue(ctlr->lp); + +	if (cdev->enabled != FCOE_CTLR_DISABLED && +	    !fcoe_link_ok(ctlr->lp)) +		fcoe_ctlr_link_up(ctlr);  	return 0;  } @@ -2370,10 +2541,12 @@ fcoe_hostlist_lookup_port(const struct net_device *netdev)   */  static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)  { +	struct fcoe_ctlr *ctlr;  	struct fcoe_interface *fcoe;  	fcoe = fcoe_hostlist_lookup_port(netdev); -	return (fcoe) ? fcoe->ctlr.lp : NULL; +	ctlr = fcoe_to_ctlr(fcoe); +	return (fcoe) ? ctlr->lp : NULL;  }  /** @@ -2393,13 +2566,44 @@ static int fcoe_hostlist_add(const struct fc_lport *lport)  	fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport));  	if (!fcoe) {  		port = lport_priv(lport); -		fcoe = port->fcoe; +		fcoe = port->priv;  		list_add_tail(&fcoe->list, &fcoe_hostlist);  	}  	return 0;  }  /** + * fcoe_hostlist_del() - Remove the FCoE interface identified by a local + *			 port to the hostlist + * @lport: The local port that identifies the FCoE interface to be added + * + * Locking: must be called with the RTNL mutex held + * + */ +static void fcoe_hostlist_del(const struct fc_lport *lport) +{ +	struct fcoe_interface *fcoe; +	struct fcoe_port *port; + +	port = lport_priv(lport); +	fcoe = port->priv; +	list_del(&fcoe->list); +	return; +} + +static struct fcoe_transport fcoe_sw_transport = { +	.name = {FCOE_TRANSPORT_DEFAULT}, +	.attached = false, +	.list = LIST_HEAD_INIT(fcoe_sw_transport.list), +	.match = fcoe_match, +	.alloc = fcoe_ctlr_alloc, +	.create = fcoe_create, +	.destroy = fcoe_destroy, +	.enable = fcoe_enable, +	.disable = fcoe_disable, +}; + +/**   * fcoe_init() - Initialize fcoe.ko   *   * Returns: 0 on success, or a negative value on failure @@ -2410,6 +2614,18 @@ static int __init fcoe_init(void)  	unsigned int cpu;  	int rc = 0; +	fcoe_wq = alloc_workqueue("fcoe", 0, 0); +	if (!fcoe_wq) +		return -ENOMEM; + +	/* register as a fcoe transport */ +	rc = fcoe_transport_attach(&fcoe_sw_transport); +	if (rc) { +		printk(KERN_ERR "failed to register an fcoe transport, check " +			"if libfcoe is loaded\n"); +		return rc; +	} +  	mutex_lock(&fcoe_config_mutex);  	for_each_possible_cpu(cpu) { @@ -2417,14 +2633,18 @@ static int __init fcoe_init(void)  		skb_queue_head_init(&p->fcoe_rx_list);  	} +	cpu_notifier_register_begin(); +  	for_each_online_cpu(cpu)  		fcoe_percpu_thread_create(cpu);  	/* Initialize per CPU interrupt thread */ -	rc = register_hotcpu_notifier(&fcoe_cpu_notifier); +	rc = __register_hotcpu_notifier(&fcoe_cpu_notifier);  	if (rc)  		goto out_free; +	cpu_notifier_register_done(); +  	/* Setup link change notification */  	fcoe_dev_setup(); @@ -2439,7 +2659,11 @@ out_free:  	for_each_online_cpu(cpu) {  		fcoe_percpu_thread_destroy(cpu);  	} + +	cpu_notifier_register_done(); +  	mutex_unlock(&fcoe_config_mutex); +	destroy_workqueue(fcoe_wq);  	return rc;  }  module_init(fcoe_init); @@ -2452,6 +2676,7 @@ module_init(fcoe_init);  static void __exit fcoe_exit(void)  {  	struct fcoe_interface *fcoe, *tmp; +	struct fcoe_ctlr *ctlr;  	struct fcoe_port *port;  	unsigned int cpu; @@ -2462,30 +2687,39 @@ static void __exit fcoe_exit(void)  	/* releases the associated fcoe hosts */  	rtnl_lock();  	list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) { -		list_del(&fcoe->list); -		port = lport_priv(fcoe->ctlr.lp); -		fcoe_interface_cleanup(fcoe); -		schedule_work(&port->destroy_work); +		ctlr = fcoe_to_ctlr(fcoe); +		port = lport_priv(ctlr->lp); +		fcoe_hostlist_del(port->lport); +		queue_work(fcoe_wq, &port->destroy_work);  	}  	rtnl_unlock(); -	unregister_hotcpu_notifier(&fcoe_cpu_notifier); +	cpu_notifier_register_begin();  	for_each_online_cpu(cpu)  		fcoe_percpu_thread_destroy(cpu); +	__unregister_hotcpu_notifier(&fcoe_cpu_notifier); + +	cpu_notifier_register_done(); +  	mutex_unlock(&fcoe_config_mutex); -	/* flush any asyncronous interface destroys, -	 * this should happen after the netdev notifier is unregistered */ -	flush_scheduled_work(); -	/* That will flush out all the N_Ports on the hostlist, but now we -	 * may have NPIV VN_Ports scheduled for destruction */ -	flush_scheduled_work(); +	/* +	 * destroy_work's may be chained but destroy_workqueue() +	 * can take care of them. Just kill the fcoe_wq. +	 */ +	destroy_workqueue(fcoe_wq); -	/* detach from scsi transport -	 * must happen after all destroys are done, therefor after the flush */ +	/* +	 * Detaching from the scsi transport must happen after all +	 * destroys are done on the fcoe_wq. destroy_workqueue will +	 * enusre the fcoe_wq is flushed. +	 */  	fcoe_if_exit(); + +	/* detach from fcoe transport */ +	fcoe_transport_detach(&fcoe_sw_transport);  }  module_exit(fcoe_exit); @@ -2509,14 +2743,11 @@ static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)  		goto done;  	mac = fr_cb(fp)->granted_mac; -	if (is_zero_ether_addr(mac)) { -		/* pre-FIP */ -		if (fcoe_ctlr_recv_flogi(fip, lport, fp)) { -			fc_frame_free(fp); -			return; -		} -	} -	fcoe_update_src_mac(lport, mac); +	/* pre-FIP */ +	if (is_zero_ether_addr(mac)) +		fcoe_ctlr_recv_flogi(fip, lport, fp); +	if (!is_zero_ether_addr(mac)) +		fcoe_update_src_mac(lport, mac);  done:  	fc_lport_flogi_resp(seq, fp, lport);  } @@ -2557,8 +2788,8 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,  				      void *arg, u32 timeout)  {  	struct fcoe_port *port = lport_priv(lport); -	struct fcoe_interface *fcoe = port->fcoe; -	struct fcoe_ctlr *fip = &fcoe->ctlr; +	struct fcoe_interface *fcoe = port->priv; +	struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);  	struct fc_frame_header *fh = fc_frame_header_get(fp);  	switch (op) { @@ -2590,12 +2821,25 @@ static int fcoe_vport_create(struct fc_vport *vport, bool disabled)  	struct Scsi_Host *shost = vport_to_shost(vport);  	struct fc_lport *n_port = shost_priv(shost);  	struct fcoe_port *port = lport_priv(n_port); -	struct fcoe_interface *fcoe = port->fcoe; +	struct fcoe_interface *fcoe = port->priv;  	struct net_device *netdev = fcoe->netdev;  	struct fc_lport *vn_port; +	int rc; +	char buf[32]; + +	rc = fcoe_validate_vport_create(vport); +	if (rc) { +		fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf)); +		printk(KERN_ERR "fcoe: Failed to create vport, " +			"WWPN (0x%s) already exists\n", +			buf); +		return rc; +	}  	mutex_lock(&fcoe_config_mutex); +	rtnl_lock();  	vn_port = fcoe_if_create(fcoe, &vport->dev, 1); +	rtnl_unlock();  	mutex_unlock(&fcoe_config_mutex);  	if (IS_ERR(vn_port)) { @@ -2625,12 +2869,15 @@ static int fcoe_vport_destroy(struct fc_vport *vport)  	struct Scsi_Host *shost = vport_to_shost(vport);  	struct fc_lport *n_port = shost_priv(shost);  	struct fc_lport *vn_port = vport->dd_data; -	struct fcoe_port *port = lport_priv(vn_port);  	mutex_lock(&n_port->lp_mutex);  	list_del(&vn_port->list);  	mutex_unlock(&n_port->lp_mutex); -	schedule_work(&port->destroy_work); + +	mutex_lock(&fcoe_config_mutex); +	fcoe_if_destroy(vn_port); +	mutex_unlock(&fcoe_config_mutex); +  	return 0;  } @@ -2686,36 +2933,14 @@ static void fcoe_set_vport_symbolic_name(struct fc_vport *vport)  			     NULL, NULL, 3 * lport->r_a_tov);  } -/** - * fcoe_get_lesb() - Fill the FCoE Link Error Status Block - * @lport: the local port - * @fc_lesb: the link error status block - */ -static void fcoe_get_lesb(struct fc_lport *lport, -			 struct fc_els_lesb *fc_lesb) +static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev)  { -	unsigned int cpu; -	u32 lfc, vlfc, mdac; -	struct fcoe_dev_stats *devst; -	struct fcoe_fc_els_lesb *lesb; -	struct rtnl_link_stats64 temp; -	struct net_device *netdev = fcoe_netdev(lport); +	struct fcoe_ctlr_device *ctlr_dev = +		fcoe_fcf_dev_to_ctlr_dev(fcf_dev); +	struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev); +	struct fcoe_interface *fcoe = fcoe_ctlr_priv(ctlr); -	lfc = 0; -	vlfc = 0; -	mdac = 0; -	lesb = (struct fcoe_fc_els_lesb *)fc_lesb; -	memset(lesb, 0, sizeof(*lesb)); -	for_each_possible_cpu(cpu) { -		devst = per_cpu_ptr(lport->dev_stats, cpu); -		lfc += devst->LinkFailureCount; -		vlfc += devst->VLinkFailureCount; -		mdac += devst->MissDiscAdvCount; -	} -	lesb->lesb_link_fail = htonl(lfc); -	lesb->lesb_vlink_fail = htonl(vlfc); -	lesb->lesb_miss_fka = htonl(mdac); -	lesb->lesb_fcs_error = htonl(dev_get_stats(netdev, &temp)->rx_crc_errors); +	fcf_dev->vlan_id = vlan_dev_vlan_id(fcoe->netdev);  }  /** @@ -2734,8 +2959,9 @@ static void fcoe_set_port_id(struct fc_lport *lport,  			     u32 port_id, struct fc_frame *fp)  {  	struct fcoe_port *port = lport_priv(lport); -	struct fcoe_interface *fcoe = port->fcoe; +	struct fcoe_interface *fcoe = port->priv; +	struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);  	if (fp && fc_frame_payload_op(fp) == ELS_FLOGI) -		fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp); +		fcoe_ctlr_recv_flogi(ctlr, lport, fp);  }  | 
