diff options
Diffstat (limited to 'drivers/scsi/fcoe/fcoe.c')
| -rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 1716 |
1 files changed, 970 insertions, 746 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 9f9600b6700..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"); @@ -861,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); @@ -884,9 +1010,12 @@ static void fcoe_if_destroy(struct fc_lport *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); } /** @@ -912,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 @@ -940,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, @@ -952,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; @@ -966,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) { @@ -1002,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); @@ -1050,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; } @@ -1069,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; } @@ -1089,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); @@ -1226,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 @@ -1237,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; @@ -1249,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; @@ -1291,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 @@ -1316,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; } } @@ -1332,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; } /** @@ -1472,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; @@ -1501,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); @@ -1514,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; @@ -1526,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); @@ -1542,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; } @@ -1552,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); @@ -1581,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; } @@ -1606,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 */ @@ -1614,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; @@ -1625,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 " @@ -1679,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(); @@ -1720,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; } /** @@ -1808,6 +1902,7 @@ out: */ static void fcoe_dev_setup(void) { + register_dcbevent_notifier(&dcb_notifier); register_netdevice_notifier(&fcoe_notifier); } @@ -1816,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 @@ -1832,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; } } @@ -1872,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: @@ -1887,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; } @@ -2093,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); } /** @@ -2244,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; @@ -2265,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); @@ -2311,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; } @@ -2372,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; } /** @@ -2395,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 @@ -2412,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) { @@ -2419,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(); @@ -2441,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); @@ -2454,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; @@ -2464,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); @@ -2511,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); } @@ -2559,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) { @@ -2592,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)) { @@ -2627,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; } @@ -2688,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); } /** @@ -2736,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); } |
