diff options
Diffstat (limited to 'drivers/scsi/fcoe/fcoe.c')
| -rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 408 |
1 files changed, 263 insertions, 145 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index fe30b1b65e1..00ee0ed642a 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -82,11 +82,11 @@ 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_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 int fcoe_device_notification(struct notifier_block *, ulong, void *); static void fcoe_dev_setup(void); @@ -117,6 +117,11 @@ 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 struct fc_seq *fcoe_elsct_send(struct fc_lport *, u32 did, struct fc_frame *, unsigned int op, @@ -126,8 +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 *); - /* notification function for packets from net device */ static struct notifier_block fcoe_notifier = { .notifier_call = fcoe_device_notification, @@ -151,11 +154,11 @@ 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_ctlr_get_lesb(struct fcoe_ctlr_device *); static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *); static struct fcoe_sysfs_function_template fcoe_sysfs_templ = { - .get_fcoe_ctlr_mode = fcoe_ctlr_get_fip_mode, + .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, @@ -405,6 +408,7 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev, } ctlr = fcoe_ctlr_device_priv(ctlr_dev); + ctlr->cdev = ctlr_dev; fcoe = fcoe_ctlr_priv(ctlr); dev_hold(netdev); @@ -487,7 +491,6 @@ static void fcoe_interface_cleanup(struct fcoe_interface *fcoe) { struct net_device *netdev = fcoe->netdev; struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe); - struct fcoe_ctlr_device *ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip); rtnl_lock(); if (!fcoe->removed) @@ -498,7 +501,6 @@ static void fcoe_interface_cleanup(struct fcoe_interface *fcoe) /* tear-down the FCoE controller */ fcoe_ctlr_destroy(fip); scsi_host_put(fip->lp->host); - fcoe_ctlr_device_delete(ctlr_dev); dev_put(netdev); module_put(THIS_MODULE); } @@ -773,7 +775,6 @@ static void fcoe_fdmi_info(struct fc_lport *lport, struct net_device *netdev) struct fcoe_port *port; struct net_device *realdev; int rc; - struct netdev_fcoe_hbainfo fdmi; port = lport_priv(lport); fcoe = port->priv; @@ -787,9 +788,13 @@ static void fcoe_fdmi_info(struct fc_lport *lport, struct net_device *netdev) return; if (realdev->netdev_ops->ndo_fcoe_get_hbainfo) { - memset(&fdmi, 0, sizeof(fdmi)); + struct netdev_fcoe_hbainfo *fdmi; + fdmi = kzalloc(sizeof(*fdmi), GFP_KERNEL); + if (!fdmi) + return; + rc = realdev->netdev_ops->ndo_fcoe_get_hbainfo(realdev, - &fdmi); + fdmi); if (rc) { printk(KERN_INFO "fcoe: Failed to retrieve FDMI " "information from netdev.\n"); @@ -799,38 +804,39 @@ static void fcoe_fdmi_info(struct fc_lport *lport, struct net_device *netdev) snprintf(fc_host_serial_number(lport->host), FC_SERIAL_NUMBER_SIZE, "%s", - fdmi.serial_number); + fdmi->serial_number); snprintf(fc_host_manufacturer(lport->host), FC_SERIAL_NUMBER_SIZE, "%s", - fdmi.manufacturer); + fdmi->manufacturer); snprintf(fc_host_model(lport->host), FC_SYMBOLIC_NAME_SIZE, "%s", - fdmi.model); + fdmi->model); snprintf(fc_host_model_description(lport->host), FC_SYMBOLIC_NAME_SIZE, "%s", - fdmi.model_description); + fdmi->model_description); snprintf(fc_host_hardware_version(lport->host), FC_VERSION_STRING_SIZE, "%s", - fdmi.hardware_version); + fdmi->hardware_version); snprintf(fc_host_driver_version(lport->host), FC_VERSION_STRING_SIZE, "%s", - fdmi.driver_version); + fdmi->driver_version); snprintf(fc_host_optionrom_version(lport->host), FC_VERSION_STRING_SIZE, "%s", - fdmi.optionrom_version); + fdmi->optionrom_version); snprintf(fc_host_firmware_version(lport->host), FC_VERSION_STRING_SIZE, "%s", - fdmi.firmware_version); + 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"); @@ -1112,10 +1118,17 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, port = lport_priv(lport); port->lport = lport; 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) { @@ -1187,6 +1200,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, 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); @@ -1427,22 +1441,28 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, 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(ctlr) && - compare_ether_addr(eh->h_source, ctlr->dest_addr)) { + !ether_addr_equal(eh->h_source, ctlr->dest_addr)) { FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n", eh->h_source); goto err; @@ -1527,13 +1547,13 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, 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; + return NET_RX_DROP; } /** @@ -1569,7 +1589,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) 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 */ @@ -1643,13 +1663,16 @@ static 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->priority = port->priority; + skb->priority = fcoe->priority; if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN && - fcoe->realdev->features & NETIF_F_HW_VLAN_TX) { - skb->vlan_tci = VLAN_TAG_PRESENT | - vlan_dev_vlan_id(fcoe->netdev); + 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; @@ -1680,7 +1703,7 @@ static 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(); @@ -1714,7 +1737,7 @@ static inline int fcoe_filter_frames(struct fc_lport *lport, struct fcoe_interface *fcoe; struct fc_frame_header *fh; struct sk_buff *skb = (struct sk_buff *)fp; - struct fcoe_dev_stats *stats; + struct fc_stats *stats; /* * We only check CRC if no offload is available and if it is @@ -1745,7 +1768,7 @@ static inline int fcoe_filter_frames(struct fc_lport *lport, return 0; } - stats = per_cpu_ptr(lport->dev_stats, get_cpu()); + stats = per_cpu_ptr(lport->stats, get_cpu()); stats->InvalidCRCCount++; if (stats->InvalidCRCCount < 5) printk(KERN_WARNING "fcoe: dropping frame with CRC error\n"); @@ -1762,7 +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_stats *stats; struct fcoe_crc_eof crc_eof; struct fc_frame *fp; struct fcoe_port *port; @@ -1772,13 +1795,13 @@ 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, @@ -1793,7 +1816,7 @@ static void fcoe_recv_frame(struct sk_buff *skb) */ hp = (struct fcoe_hdr *) skb_network_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 " @@ -1849,25 +1872,27 @@ static int fcoe_percpu_receive_thread(void *arg) 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); skb_queue_splice_init(&p->fcoe_rx_list, &tmp); - spin_unlock_bh(&p->fcoe_rx_list.lock); - while ((skb = __skb_dequeue(&tmp)) != NULL) - fcoe_recv_frame(skb); - - spin_lock_bh(&p->fcoe_rx_list.lock); - if (!skb_queue_len(&p->fcoe_rx_list)) { + if (!skb_queue_len(&tmp)) { set_current_state(TASK_INTERRUPTIBLE); spin_unlock_bh(&p->fcoe_rx_list.lock); schedule(); set_current_state(TASK_RUNNING); - } else - spin_unlock_bh(&p->fcoe_rx_list.lock); + goto retry; + } + + spin_unlock_bh(&p->fcoe_rx_list.lock); + + while ((skb = __skb_dequeue(&tmp)) != NULL) + fcoe_recv_frame(skb); + } return 0; } @@ -1915,7 +1940,6 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier, struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; struct net_device *netdev; - struct fcoe_port *port; int prio; if (entry->app.selector != DCB_APP_IDTYPE_ETHTYPE) @@ -1944,10 +1968,8 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier, entry->app.protocol == ETH_P_FCOE) ctlr->priority = prio; - if (entry->app.protocol == ETH_P_FCOE) { - port = lport_priv(ctlr->lp); - port->priority = prio; - } + if (entry->app.protocol == ETH_P_FCOE) + fcoe->priority = prio; return NOTIFY_OK; } @@ -1965,12 +1987,13 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier, 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; @@ -2021,13 +2044,29 @@ 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(ctlr); - else if (fcoe_ctlr_link_down(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; @@ -2040,6 +2079,8 @@ out: * Called from fcoe transport. * * Returns: 0 for success + * + * Deprecated: use fcoe_ctlr_enabled() */ static int fcoe_disable(struct net_device *netdev) { @@ -2099,6 +2140,33 @@ out: } /** + * 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 * @netdev : The net_device object the Ethernet interface to create on * @@ -2138,17 +2206,47 @@ 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); 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); } /** @@ -2178,7 +2276,6 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe) u8 fup, up; struct net_device *netdev = fcoe->realdev; struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); - struct fcoe_port *port = lport_priv(ctlr->lp); struct dcb_app app = { .priority = 0, .protocol = ETH_P_FCOE @@ -2200,22 +2297,32 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe) fup = dcb_getapp(netdev, &app); } - port->priority = ffs(up) ? ffs(up) - 1 : 0; - ctlr->priority = ffs(fup) ? ffs(fup) - 1 : port->priority; + fcoe->priority = ffs(up) ? ffs(up) - 1 : 0; + ctlr->priority = ffs(fup) ? ffs(fup) - 1 : fcoe->priority; } #endif } +enum fcoe_create_link_state { + FCOE_CREATE_LINK_DOWN, + FCOE_CREATE_LINK_UP, +}; + /** - * 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 + * _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 fcoe transport + * Called from either the libfcoe 'create' module parameter + * via fcoe_create or from fcoe_syfs's ctlr_create file. * - * Returns: 0 for success + * 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) +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; @@ -2247,7 +2354,9 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) rc = -EIO; rtnl_unlock(); fcoe_interface_cleanup(fcoe); - goto out_nortnl; + mutex_unlock(&fcoe_config_mutex); + fcoe_ctlr_device_delete(ctlr_dev); + goto out; } /* Make this the "master" N_Port */ @@ -2256,13 +2365,29 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) /* setup DCB priority attributes. */ fcoe_dcb_create(fcoe); - /* add to lports list */ - fcoe_hostlist_add(lport); - /* start FIP Discovery and FLOGI */ lport->boot_time = jiffies; fc_fabric_login(lport); - if (!fcoe_link_ok(lport)) { + + /* + * 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. + */ + 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); @@ -2271,43 +2396,40 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) out_nodev: rtnl_unlock(); -out_nortnl: 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 */ -static int fcoe_link_speed_update(struct fc_lport *lport) +static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) { - struct net_device *netdev = fcoe_netdev(lport); - struct ethtool_cmd ecmd; - - if (!__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; - switch (ethtool_cmd_speed(&ecmd)) { - case SPEED_1000: - lport->link_speed = FC_PORTSPEED_1GBIT; - break; - case SPEED_10000: - lport->link_speed = FC_PORTSPEED_10GBIT; - break; - } - return 0; - } - return -1; + return _fcoe_create(netdev, fip_mode, FCOE_CREATE_LINK_UP); +} + +/** + * 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); } /** @@ -2377,10 +2499,13 @@ static int fcoe_reset(struct Scsi_Host *shost) 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 (!fcoe_link_ok(ctlr->lp)) + + if (cdev->enabled != FCOE_CTLR_DISABLED && + !fcoe_link_ok(ctlr->lp)) fcoe_ctlr_link_up(ctlr); return 0; } @@ -2447,12 +2572,31 @@ static int fcoe_hostlist_add(const struct fc_lport *lport) 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, @@ -2489,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(); @@ -2511,6 +2659,9 @@ 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; @@ -2536,18 +2687,22 @@ 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); 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); /* @@ -2778,43 +2933,6 @@ 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) -{ - struct net_device *netdev = fcoe_netdev(lport); - - __fcoe_get_lesb(lport, fc_lesb, netdev); -} - -static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) -{ - struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); - struct net_device *netdev = fcoe_netdev(fip->lp); - struct fcoe_fc_els_lesb *fcoe_lesb; - struct fc_els_lesb fc_lesb; - - __fcoe_get_lesb(fip->lp, &fc_lesb, netdev); - fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb); - - ctlr_dev->lesb.lesb_link_fail = - ntohl(fcoe_lesb->lesb_link_fail); - ctlr_dev->lesb.lesb_vlink_fail = - ntohl(fcoe_lesb->lesb_vlink_fail); - ctlr_dev->lesb.lesb_miss_fka = - ntohl(fcoe_lesb->lesb_miss_fka); - ctlr_dev->lesb.lesb_symb_err = - ntohl(fcoe_lesb->lesb_symb_err); - ctlr_dev->lesb.lesb_err_block = - ntohl(fcoe_lesb->lesb_err_block); - ctlr_dev->lesb.lesb_fcs_error = - ntohl(fcoe_lesb->lesb_fcs_error); -} - static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev) { struct fcoe_ctlr_device *ctlr_dev = |
