Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph...
[firefly-linux-kernel-4.4.55.git] / drivers / scsi / fcoe / fcoe.c
index 666b7ac4475f6921237d3cbb6f60e15b14fed936..b5d92fc93c7023f6d0f395c5ce44c4589afeabea 100644 (file)
@@ -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,
@@ -1112,10 +1115,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 +1197,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);
@@ -1964,6 +1975,7 @@ 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 fcoe_ctlr *ctlr;
@@ -2020,13 +2032,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->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;
@@ -2039,6 +2067,8 @@ out:
  * Called from fcoe transport.
  *
  * Returns: 0 for success
+ *
+ * Deprecated: use fcoe_ctlr_enabled()
  */
 static int fcoe_disable(struct net_device *netdev)
 {
@@ -2097,6 +2127,33 @@ out:
        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
  * @netdev  : The net_device object the Ethernet interface to create on
@@ -2139,8 +2196,31 @@ static void fcoe_destroy_work(struct work_struct *work)
 {
        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;
@@ -2204,16 +2284,26 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe)
 #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;
@@ -2254,13 +2344,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);
@@ -2275,37 +2381,34 @@ out_nortnl:
 }
 
 /**
- * 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 for success
+ */
+static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
+{
+       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
  *
- * Returns: 0 if the ethtool query was successful
- *          -1 if the ethtool query failed
+ * 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_link_speed_update(struct fc_lport *lport)
+static int fcoe_ctlr_alloc(struct net_device *netdev)
 {
-       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_FABRIC,
+                           FCOE_CREATE_LINK_DOWN);
 }
 
 /**
@@ -2375,10 +2478,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;
 }
@@ -2445,12 +2551,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,
@@ -2534,9 +2659,9 @@ 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();
@@ -2776,43 +2901,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 =