Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[linux-2.6.git] / drivers / scsi / fcoe / fcoe.c
index 54c870b..9276121 100644 (file)
@@ -74,6 +74,7 @@ static int fcoe_rcv(struct sk_buff *, 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 *);
@@ -146,6 +147,7 @@ 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 struct libfc_function_template fcoe_libfc_fcn_templ = {
        .frame_send = fcoe_xmit,
@@ -153,6 +155,7 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {
        .ddp_done = fcoe_ddp_done,
        .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 = {
@@ -629,6 +632,8 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
        port->fcoe_pending_queue_active = 0;
        setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport);
 
+       fcoe_link_speed_update(lport);
+
        if (!lport->vport) {
                /*
                 * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN:
@@ -653,15 +658,13 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
 /**
  * fcoe_shost_config() - Set up the SCSI host associated with a local port
  * @lport: The local port
- * @shost: The SCSI host to associate with the local port
  * @dev:   The device associated with the SCSI host
  *
  * Must be called after fcoe_lport_config() and fcoe_netdev_config()
  *
  * Returns: 0 for success
  */
-static int fcoe_shost_config(struct fc_lport *lport, struct Scsi_Host *shost,
-                            struct device *dev)
+static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
 {
        int rc = 0;
 
@@ -669,6 +672,8 @@ static int fcoe_shost_config(struct fc_lport *lport, struct Scsi_Host *shost,
        lport->host->max_lun = FCOE_MAX_LUN;
        lport->host->max_id = FCOE_MAX_FCP_TARGET;
        lport->host->max_channel = 0;
+       lport->host->max_cmd_len = FCOE_MAX_CMD_LEN;
+
        if (lport->vport)
                lport->host->transportt = fcoe_vport_transport_template;
        else
@@ -796,6 +801,12 @@ 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)
 {
@@ -818,7 +829,6 @@ 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);
        rtnl_unlock();
@@ -841,6 +851,7 @@ static void fcoe_if_destroy(struct fc_lport *lport)
 
        /* Release the Scsi_Host */
        scsi_host_put(lport->host);
+       module_put(THIS_MODULE);
 }
 
 /**
@@ -897,7 +908,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
        struct net_device *netdev = fcoe->netdev;
        struct fc_lport *lport = NULL;
        struct fcoe_port *port;
-       struct Scsi_Host *shost;
        int rc;
        /*
         * parent is only a vport if npiv is 1,
@@ -919,7 +929,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
                rc = -ENOMEM;
                goto out;
        }
-       shost = lport->host;
        port = lport_priv(lport);
        port->lport = lport;
        port->fcoe = fcoe;
@@ -934,7 +943,8 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
        }
 
        if (npiv) {
-               FCOE_NETDEV_DBG(netdev, "Setting vport names, 0x%llX 0x%llX\n",
+               FCOE_NETDEV_DBG(netdev, "Setting vport names, "
+                               "%16.16llx %16.16llx\n",
                                vport->node_name, vport->port_name);
                fc_set_wwnn(lport, vport->node_name);
                fc_set_wwpn(lport, vport->port_name);
@@ -949,7 +959,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
        }
 
        /* configure lport scsi host properties */
-       rc = fcoe_shost_config(lport, shost, parent);
+       rc = fcoe_shost_config(lport, parent);
        if (rc) {
                FCOE_NETDEV_DBG(netdev, "Could not configure shost for the "
                                "interface\n");
@@ -1073,7 +1083,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu)
        struct sk_buff *skb;
 #ifdef CONFIG_SMP
        struct fcoe_percpu_s *p0;
-       unsigned targ_cpu = smp_processor_id();
+       unsigned targ_cpu = get_cpu();
 #endif /* CONFIG_SMP */
 
        FCOE_DBG("Destroying receive thread for CPU %d\n", cpu);
@@ -1129,6 +1139,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu)
                        kfree_skb(skb);
                spin_unlock_bh(&p->fcoe_rx_list.lock);
        }
+       put_cpu();
 #else
        /*
         * This a non-SMP scenario where the singular Rx thread is
@@ -1297,8 +1308,8 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
 
        return 0;
 err:
-       fc_lport_get_stats(lport)->ErrorFrames++;
-
+       per_cpu_ptr(lport->dev_stats, get_cpu())->ErrorFrames++;
+       put_cpu();
 err2:
        kfree_skb(skb);
        return -1;
@@ -1444,7 +1455,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
                return 0;
        }
 
-       if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
+       if (unlikely(fh->fh_type == FC_TYPE_ELS) &&
            fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb))
                return 0;
 
@@ -1527,9 +1538,10 @@ 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 = fc_lport_get_stats(lport);
+       stats = per_cpu_ptr(lport->dev_stats, get_cpu());
        stats->TxFrames++;
        stats->TxWords += wlen;
+       put_cpu();
 
        /* send down to lld */
        fr_dev(fp) = lport;
@@ -1563,7 +1575,6 @@ static void fcoe_recv_frame(struct sk_buff *skb)
        struct fc_frame_header *fh;
        struct fcoe_crc_eof crc_eof;
        struct fc_frame *fp;
-       u8 *mac = NULL;
        struct fcoe_port *port;
        struct fcoe_hdr *hp;
 
@@ -1583,13 +1594,9 @@ static void fcoe_recv_frame(struct sk_buff *skb)
                        skb_end_pointer(skb), skb->csum,
                        skb->dev ? skb->dev->name : "<NULL>");
 
-       /*
-        * Save source MAC address before discarding header.
-        */
        port = lport_priv(lport);
        if (skb_is_nonlinear(skb))
                skb_linearize(skb);     /* not ideal */
-       mac = eth_hdr(skb)->h_source;
 
        /*
         * Frame length checks and setting up the header pointers
@@ -1598,7 +1605,7 @@ static void fcoe_recv_frame(struct sk_buff *skb)
        hp = (struct fcoe_hdr *) skb_network_header(skb);
        fh = (struct fc_frame_header *) skb_transport_header(skb);
 
-       stats = fc_lport_get_stats(lport);
+       stats = per_cpu_ptr(lport->dev_stats, get_cpu());
        if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
                if (stats->ErrorFrames < 5)
                        printk(KERN_WARNING "fcoe: FCoE version "
@@ -1607,9 +1614,7 @@ static void fcoe_recv_frame(struct sk_buff *skb)
                               "initiator supports version "
                               "%x\n", FC_FCOE_DECAPS_VER(hp),
                               FC_FCOE_VER);
-               stats->ErrorFrames++;
-               kfree_skb(skb);
-               return;
+               goto drop;
        }
 
        skb_pull(skb, sizeof(struct fcoe_hdr));
@@ -1624,16 +1629,12 @@ static void fcoe_recv_frame(struct sk_buff *skb)
        fr_sof(fp) = hp->fcoe_sof;
 
        /* Copy out the CRC and EOF trailer for access */
-       if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
-               kfree_skb(skb);
-               return;
-       }
+       if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof)))
+               goto drop;
        fr_eof(fp) = crc_eof.fcoe_eof;
        fr_crc(fp) = crc_eof.fcoe_crc32;
-       if (pskb_trim(skb, fr_len)) {
-               kfree_skb(skb);
-               return;
-       }
+       if (pskb_trim(skb, fr_len))
+               goto drop;
 
        /*
         * We only check CRC if no offload is available and if it is
@@ -1647,25 +1648,27 @@ static void fcoe_recv_frame(struct sk_buff *skb)
                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) {
-               fc_exch_recv(lport, fp);
-               return;
-       }
-       if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+       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++;
-                       stats->ErrorFrames++;
-                       fc_frame_free(fp);
-                       return;
+                       goto drop;
                }
                fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
        }
+       put_cpu();
        fc_exch_recv(lport, fp);
+       return;
+
+drop:
+       stats->ErrorFrames++;
+       put_cpu();
+       kfree_skb(skb);
 }
 
 /**
@@ -1835,11 +1838,15 @@ static int fcoe_device_notification(struct notifier_block *notifier,
                FCOE_NETDEV_DBG(netdev, "Unknown event %ld "
                                "from netdev netlink\n", event);
        }
+
+       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 = fc_lport_get_stats(lport);
+               stats = per_cpu_ptr(lport->dev_stats, get_cpu());
                stats->LinkFailureCount++;
+               put_cpu();
                fcoe_clean_pending_queue(lport);
        }
 out:
@@ -1901,13 +1908,19 @@ static int fcoe_disable(const char *buffer, struct kernel_param *kp)
                goto out_nodev;
        }
 
-       rtnl_lock();
+       if (!rtnl_trylock()) {
+               dev_put(netdev);
+               mutex_unlock(&fcoe_config_mutex);
+               return restart_syscall();
+       }
+
        fcoe = fcoe_hostlist_lookup_port(netdev);
        rtnl_unlock();
 
-       if (fcoe)
+       if (fcoe) {
                fc_fabric_logoff(fcoe->ctlr.lp);
-       else
+               fcoe_ctlr_link_down(&fcoe->ctlr);
+       } else
                rc = -ENODEV;
 
        dev_put(netdev);
@@ -1950,13 +1963,20 @@ static int fcoe_enable(const char *buffer, struct kernel_param *kp)
                goto out_nodev;
        }
 
-       rtnl_lock();
+       if (!rtnl_trylock()) {
+               dev_put(netdev);
+               mutex_unlock(&fcoe_config_mutex);
+               return restart_syscall();
+       }
+
        fcoe = fcoe_hostlist_lookup_port(netdev);
        rtnl_unlock();
 
-       if (fcoe)
+       if (fcoe) {
+               if (!fcoe_link_ok(fcoe->ctlr.lp))
+                       fcoe_ctlr_link_up(&fcoe->ctlr);
                rc = fc_fabric_login(fcoe->ctlr.lp);
-       else
+       } else
                rc = -ENODEV;
 
        dev_put(netdev);
@@ -1999,7 +2019,12 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
                goto out_nodev;
        }
 
-       rtnl_lock();
+       if (!rtnl_trylock()) {
+               dev_put(netdev);
+               mutex_unlock(&fcoe_config_mutex);
+               return restart_syscall();
+       }
+
        fcoe = fcoe_hostlist_lookup_port(netdev);
        if (!fcoe) {
                rtnl_unlock();
@@ -2008,9 +2033,8 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
        }
        list_del(&fcoe->list);
        fcoe_interface_cleanup(fcoe);
-       rtnl_unlock();
+       /* RTNL mutex is dropped by fcoe_if_destroy */
        fcoe_if_destroy(fcoe->ctlr.lp);
-       module_put(THIS_MODULE);
 
 out_putdev:
        dev_put(netdev);
@@ -2029,6 +2053,8 @@ static void fcoe_destroy_work(struct work_struct *work)
 
        port = container_of(work, struct fcoe_port, destroy_work);
        mutex_lock(&fcoe_config_mutex);
+       rtnl_lock();
+       /* RTNL mutex is dropped by fcoe_if_destroy */
        fcoe_if_destroy(port->lport);
        mutex_unlock(&fcoe_config_mutex);
 }
@@ -2050,6 +2076,12 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
        struct net_device *netdev;
 
        mutex_lock(&fcoe_config_mutex);
+
+       if (!rtnl_trylock()) {
+               mutex_unlock(&fcoe_config_mutex);
+               return restart_syscall();
+       }
+
 #ifdef CONFIG_FCOE_MODULE
        /*
         * Make sure the module has been initialized, and is not about to be
@@ -2058,7 +2090,7 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
         */
        if (THIS_MODULE->state != MODULE_STATE_LIVE) {
                rc = -ENODEV;
-               goto out_nodev;
+               goto out_nomod;
        }
 #endif
 
@@ -2067,7 +2099,6 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
                goto out_nomod;
        }
 
-       rtnl_lock();
        netdev = fcoe_if_to_netdev(buffer);
        if (!netdev) {
                rc = -ENODEV;
@@ -2122,35 +2153,27 @@ out_free:
 out_putdev:
        dev_put(netdev);
 out_nodev:
-       rtnl_unlock();
        module_put(THIS_MODULE);
 out_nomod:
+       rtnl_unlock();
        mutex_unlock(&fcoe_config_mutex);
        return rc;
 }
 
 /**
- * fcoe_link_ok() - Check if the link is OK for a local port
- * @lport: The local port to check link on
- *
- * Any permanently-disqualifying conditions have been previously checked.
- * This also updates the speed setting, which may change with link for 100/1000.
- *
- * This function should probably be checking for PAUSE support at some point
- * in the future. Currently Per-priority-pause is not determinable using
- * ethtool, so we shouldn't be restrictive until that problem is resolved.
- *
- * Returns: 0 if link is OK for use by FCoE.
+ * fcoe_link_speed_update() - Update the supported and actual link speeds
+ * @lport: The local port to update speeds for
  *
+ * Returns: 0 if the ethtool query was successful
+ *          -1 if the ethtool query failed
  */
-int fcoe_link_ok(struct fc_lport *lport)
+int fcoe_link_speed_update(struct fc_lport *lport)
 {
        struct fcoe_port *port = lport_priv(lport);
        struct net_device *netdev = port->fcoe->netdev;
        struct ethtool_cmd ecmd = { ETHTOOL_GSET };
 
-       if ((netdev->flags & IFF_UP) && netif_carrier_ok(netdev) &&
-           (!dev_ethtool_get_settings(netdev, &ecmd))) {
+       if (!dev_ethtool_get_settings(netdev, &ecmd)) {
                lport->link_supported_speeds &=
                        ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
                if (ecmd.supported & (SUPPORTED_1000baseT_Half |
@@ -2170,6 +2193,23 @@ int fcoe_link_ok(struct fc_lport *lport)
 }
 
 /**
+ * fcoe_link_ok() - Check if the link is OK for a local port
+ * @lport: The local port to check link on
+ *
+ * Returns: 0 if link is UP and OK, -1 if not
+ *
+ */
+int fcoe_link_ok(struct fc_lport *lport)
+{
+       struct fcoe_port *port = lport_priv(lport);
+       struct net_device *netdev = port->fcoe->netdev;
+
+       if (netif_oper_up(netdev))
+               return 0;
+       return -1;
+}
+
+/**
  * fcoe_percpu_clean() - Clear all pending skbs for an local port
  * @lport: The local port whose skbs are to be cleared
  *
@@ -2631,3 +2671,25 @@ static void fcoe_get_lesb(struct fc_lport *lport,
        lesb->lesb_miss_fka = htonl(mdac);
        lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors);
 }
+
+/**
+ * fcoe_set_port_id() - Callback from libfc when Port_ID is set.
+ * @lport: the local port
+ * @port_id: the port ID
+ * @fp: the received frame, if any, that caused the port_id to be set.
+ *
+ * This routine handles the case where we received a FLOGI and are
+ * entering point-to-point mode.  We need to call fcoe_ctlr_recv_flogi()
+ * so it can set the non-mapped mode and gateway address.
+ *
+ * The FLOGI LS_ACC is handled by fcoe_flogi_resp().
+ */
+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;
+
+       if (fp && fc_frame_payload_op(fp) == ELS_FLOGI)
+               fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp);
+}