]> Pileus Git - ~andy/linux/blobdiff - drivers/scsi/fcoe/fcoe.c
Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi...
[~andy/linux] / drivers / scsi / fcoe / fcoe.c
index e9599600aa230b8b6315c405730352f99e6a7d9a..335e85192807a4b75e7aa229cc2b2b6f396e0b9b 100644 (file)
@@ -168,6 +168,14 @@ static struct fc_function_template fcoe_nport_fc_functions = {
        .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,
@@ -208,6 +216,14 @@ static struct fc_function_template fcoe_vport_fc_functions = {
        .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,
@@ -364,11 +380,10 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
        if (!fcoe) {
                FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n");
                fcoe = ERR_PTR(-ENOMEM);
-               goto out_nomod;
+               goto out_putmod;
        }
 
        dev_hold(netdev);
-       kref_init(&fcoe->kref);
 
        /*
         * Initialize FIP.
@@ -384,53 +399,17 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
                kfree(fcoe);
                dev_put(netdev);
                fcoe = ERR_PTR(err);
-               goto out_nomod;
+               goto out_putmod;
        }
 
        goto out;
 
-out_nomod:
+out_putmod:
        module_put(THIS_MODULE);
 out:
        return fcoe;
 }
 
-/**
- * fcoe_interface_release() - fcoe_port kref release function
- * @kref: Embedded reference count in an fcoe_interface struct
- */
-static void fcoe_interface_release(struct kref *kref)
-{
-       struct fcoe_interface *fcoe;
-       struct net_device *netdev;
-
-       fcoe = container_of(kref, struct fcoe_interface, kref);
-       netdev = fcoe->netdev;
-       /* tear-down the FCoE controller */
-       fcoe_ctlr_destroy(&fcoe->ctlr);
-       kfree(fcoe);
-       dev_put(netdev);
-       module_put(THIS_MODULE);
-}
-
-/**
- * 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);
-}
-
 /**
  * fcoe_interface_cleanup() - Clean up a FCoE interface
  * @fcoe: The FCoE interface to be cleaned up
@@ -478,7 +457,11 @@ static void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
        rtnl_unlock();
 
        /* Release the self-reference taken during fcoe_interface_create() */
-       fcoe_interface_put(fcoe);
+       /* tear-down the FCoE controller */
+       fcoe_ctlr_destroy(fip);
+       kfree(fcoe);
+       dev_put(netdev);
+       module_put(THIS_MODULE);
 }
 
 /**
@@ -734,6 +717,85 @@ 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;
+       struct netdev_fcoe_hbainfo fdmi;
+
+       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) {
+               memset(&fdmi, 0, sizeof(fdmi));
+               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;
+       } 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
@@ -881,9 +943,6 @@ static void fcoe_if_destroy(struct fc_lport *lport)
                dev_uc_del(netdev, port->data_src_addr);
        rtnl_unlock();
 
-       /* Release reference held in fcoe_if_create() */
-       fcoe_interface_put(fcoe);
-
        /* Free queued packets for the per-CPU receive threads */
        fcoe_percpu_clean(lport);
 
@@ -1047,6 +1106,9 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
                goto out_lp_destroy;
        }
 
+       /* 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
@@ -1070,7 +1132,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
                goto out_lp_destroy;
        }
 
-       fcoe_interface_get(fcoe);
        return lport;
 
 out_lp_destroy:
@@ -1375,7 +1436,7 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
                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
@@ -1386,12 +1447,12 @@ static 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;
                }
        }
@@ -1402,24 +1463,17 @@ static 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;
 err:
@@ -1498,7 +1552,7 @@ static 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;
@@ -1515,7 +1569,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
                        return -ENOMEM;
                }
                frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
-               cp = kmap_atomic(skb_frag_page(frag), KM_SKB_DATA_SOFTIRQ)
+               cp = kmap_atomic(skb_frag_page(frag))
                        + frag->page_offset;
        } else {
                cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
@@ -1526,7 +1580,7 @@ static 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;
        }
 
@@ -1736,23 +1790,29 @@ 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);
 
        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);
+               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)) {
                        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);
-               }
-               spin_unlock_bh(&p->fcoe_rx_list.lock);
-               fcoe_recv_frame(skb);
+               } else
+                       spin_unlock_bh(&p->fcoe_rx_list.lock);
        }
        return 0;
 }
@@ -2009,20 +2069,13 @@ static void fcoe_destroy_work(struct work_struct *work)
 {
        struct fcoe_port *port;
        struct fcoe_interface *fcoe;
-       int npiv = 0;
 
        port = container_of(work, struct fcoe_port, destroy_work);
        mutex_lock(&fcoe_config_mutex);
 
-       /* set if this is an NPIV port */
-       npiv = port->lport->vport ? 1 : 0;
-
        fcoe = port->priv;
        fcoe_if_destroy(port->lport);
-
-       /* Do not tear down the fcoe interface for NPIV port */
-       if (!npiv)
-               fcoe_interface_cleanup(fcoe);
+       fcoe_interface_cleanup(fcoe);
 
        mutex_unlock(&fcoe_config_mutex);
 }
@@ -2133,8 +2186,12 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
        /* start FIP Discovery and FLOGI */
        lport->boot_time = jiffies;
        fc_fabric_login(lport);
-       if (!fcoe_link_ok(lport))
+       if (!fcoe_link_ok(lport)) {
+               rtnl_unlock();
                fcoe_ctlr_link_up(&fcoe->ctlr);
+               mutex_unlock(&fcoe_config_mutex);
+               return rc;
+       }
 
 out_nodev:
        rtnl_unlock();
@@ -2207,31 +2264,14 @@ static int fcoe_link_ok(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) {
@@ -2240,6 +2280,7 @@ static void fcoe_percpu_clean(struct fc_lport *lport)
                }
                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);
@@ -2593,12 +2634,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);
-       queue_work(fcoe_wq, &port->destroy_work);
+
+       mutex_lock(&fcoe_config_mutex);
+       fcoe_if_destroy(vn_port);
+       mutex_unlock(&fcoe_config_mutex);
+
        return 0;
 }