Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / scsi / lpfc / lpfc_vport.c
index 85797db..86d05be 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2006 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -125,16 +125,26 @@ lpfc_vport_sparm(struct lpfc_hba *phba, struct lpfc_vport *vport)
        pmb->vport = vport;
        rc = lpfc_sli_issue_mbox_wait(phba, pmb, phba->fc_ratov * 2);
        if (rc != MBX_SUCCESS) {
-               lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_VPORT,
-                               "%d (%d):1818 VPort failed init, mbxCmd x%x "
-                               "READ_SPARM mbxStatus x%x, rc = x%x\n",
-                               phba->brd_no, vport->vpi,
-                               mb->mbxCommand, mb->mbxStatus, rc);
-               lpfc_mbuf_free(phba, mp->virt, mp->phys);
-               kfree(mp);
-               if (rc != MBX_TIMEOUT)
-                       mempool_free(pmb, phba->mbox_mem_pool);
-               return -EIO;
+               if (signal_pending(current)) {
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT | LOG_VPORT,
+                                        "1830 Signal aborted mbxCmd x%x\n",
+                                        mb->mbxCommand);
+                       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+                       kfree(mp);
+                       if (rc != MBX_TIMEOUT)
+                               mempool_free(pmb, phba->mbox_mem_pool);
+                       return -EINTR;
+               } else {
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT | LOG_VPORT,
+                                        "1818 VPort failed init, mbxCmd x%x "
+                                        "READ_SPARM mbxStatus x%x, rc = x%x\n",
+                                        mb->mbxCommand, mb->mbxStatus, rc);
+                       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+                       kfree(mp);
+                       if (rc != MBX_TIMEOUT)
+                               mempool_free(pmb, phba->mbox_mem_pool);
+                       return -EIO;
+               }
        }
 
        memcpy(&vport->fc_sparam, mp->virt, sizeof (struct serv_parm));
@@ -162,9 +172,9 @@ lpfc_valid_wwn_format(struct lpfc_hba *phba, struct lpfc_name *wwn,
                return 1;
 
        lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
-                       "%d:1822 Invalid %s: %02x:%02x:%02x:%02x:"
+                       "1822 Invalid %s: %02x:%02x:%02x:%02x:"
                        "%02x:%02x:%02x:%02x\n",
-                       phba->brd_no, name_type,
+                       name_type,
                        wwn->u.wwn[0], wwn->u.wwn[1],
                        wwn->u.wwn[2], wwn->u.wwn[3],
                        wwn->u.wwn[4], wwn->u.wwn[5],
@@ -176,16 +186,21 @@ static int
 lpfc_unique_wwpn(struct lpfc_hba *phba, struct lpfc_vport *new_vport)
 {
        struct lpfc_vport *vport;
+       unsigned long flags;
 
+       spin_lock_irqsave(&phba->hbalock, flags);
        list_for_each_entry(vport, &phba->port_list, listentry) {
                if (vport == new_vport)
                        continue;
                /* If they match, return not unique */
                if (memcmp(&vport->fc_sparam.portName,
-                       &new_vport->fc_sparam.portName,
-                       sizeof(struct lpfc_name)) == 0)
+                          &new_vport->fc_sparam.portName,
+                          sizeof(struct lpfc_name)) == 0) {
+                       spin_unlock_irqrestore(&phba->hbalock, flags);
                        return 0;
+               }
        }
+       spin_unlock_irqrestore(&phba->hbalock, flags);
        return 1;
 }
 
@@ -193,20 +208,21 @@ int
 lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
 {
        struct lpfc_nodelist *ndlp;
-       struct lpfc_vport *pport =
-               (struct lpfc_vport *) fc_vport->shost->hostdata;
+       struct Scsi_Host *shost = fc_vport->shost;
+       struct lpfc_vport *pport = (struct lpfc_vport *) shost->hostdata;
        struct lpfc_hba   *phba = pport->phba;
        struct lpfc_vport *vport = NULL;
        int instance;
        int vpi;
        int rc = VPORT_ERROR;
+       int status;
 
        if ((phba->sli_rev < 3) ||
                !(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)) {
                lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
-                               "%d:1808 Create VPORT failed: "
+                               "1808 Create VPORT failed: "
                                "NPIV is not enabled: SLImode:%d\n",
-                               phba->brd_no, phba->sli_rev);
+                               phba->sli_rev);
                rc = VPORT_INVAL;
                goto error_out;
        }
@@ -214,9 +230,9 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
        vpi = lpfc_alloc_vpi(phba);
        if (vpi == 0) {
                lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
-                               "%d:1809 Create VPORT failed: "
+                               "1809 Create VPORT failed: "
                                "Max VPORTs (%d) exceeded\n",
-                               phba->brd_no, phba->max_vpi);
+                               phba->max_vpi);
                rc = VPORT_NORESOURCES;
                goto error_out;
        }
@@ -225,18 +241,17 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
        /* Assign an unused board number */
        if ((instance = lpfc_get_instance()) < 0) {
                lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
-                               "%d:1810 Create VPORT failed: Cannot get "
-                               "instance number\n", phba->brd_no);
+                               "1810 Create VPORT failed: Cannot get "
+                               "instance number\n");
                lpfc_free_vpi(phba, vpi);
                rc = VPORT_NORESOURCES;
                goto error_out;
        }
 
-       vport = lpfc_create_port(phba, instance, fc_vport);
+       vport = lpfc_create_port(phba, instance, &fc_vport->dev);
        if (!vport) {
                lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
-                               "%d:1811 Create VPORT failed: vpi x%x\n",
-                               phba->brd_no, vpi);
+                               "1811 Create VPORT failed: vpi x%x\n", vpi);
                lpfc_free_vpi(phba, vpi);
                rc = VPORT_NORESOURCES;
                goto error_out;
@@ -245,14 +260,19 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
        vport->vpi = vpi;
        lpfc_debugfs_initialize(vport);
 
-       if (lpfc_vport_sparm(phba, vport)) {
-               lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
-                               "%d:1813 Create VPORT failed: vpi:%d "
-                               "Cannot get sparam\n",
-                               phba->brd_no, vpi);
+       if ((status = lpfc_vport_sparm(phba, vport))) {
+               if (status == -EINTR) {
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+                                        "1831 Create VPORT Interrupted.\n");
+                       rc = VPORT_ERROR;
+               } else {
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+                                        "1813 Create VPORT failed. "
+                                        "Cannot get sparam\n");
+                       rc = VPORT_NORESOURCES;
+               }
                lpfc_free_vpi(phba, vpi);
                destroy_port(vport);
-               rc = VPORT_NORESOURCES;
                goto error_out;
        }
 
@@ -269,10 +289,9 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
 
        if (!lpfc_valid_wwn_format(phba, &vport->fc_sparam.nodeName, "WWNN") ||
            !lpfc_valid_wwn_format(phba, &vport->fc_sparam.portName, "WWPN")) {
-               lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
-                               "%d:1821 Create VPORT failed: vpi:%d "
-                               "Invalid WWN format\n",
-                               phba->brd_no, vpi);
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+                                "1821 Create VPORT failed. "
+                                "Invalid WWN format\n");
                lpfc_free_vpi(phba, vpi);
                destroy_port(vport);
                rc = VPORT_INVAL;
@@ -280,10 +299,9 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
        }
 
        if (!lpfc_unique_wwpn(phba, vport)) {
-               lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
-                               "%d:1823 Create VPORT failed: vpi:%d "
-                               "Duplicate WWN on HBA\n",
-                               phba->brd_no, vpi);
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+                                "1823 Create VPORT failed. "
+                                "Duplicate WWN on HBA\n");
                lpfc_free_vpi(phba, vpi);
                destroy_port(vport);
                rc = VPORT_INVAL;
@@ -309,16 +327,15 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
         * up and ready to FDISC.
         */
        ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
-       if (ndlp && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
+       if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
+           ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
                if (phba->link_flag & LS_NPIV_FAB_SUPPORTED) {
                        lpfc_set_disctmo(vport);
                        lpfc_initial_fdisc(vport);
                } else {
                        lpfc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
-                       lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
-                                       "%d (%d):0262 No NPIV Fabric "
-                                       "support\n",
-                                       phba->brd_no, vport->vpi);
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+                                        "0262 No NPIV Fabric support\n");
                }
        } else {
                lpfc_vport_set_state(vport, FC_VPORT_FAILED);
@@ -326,12 +343,14 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
        rc = VPORT_OK;
 
 out:
+       lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+                       "1825 Vport Created.\n");
        lpfc_host_attrib_init(lpfc_shost_from_vport(vport));
 error_out:
        return rc;
 }
 
-int
+static int
 disable_vport(struct fc_vport *fc_vport)
 {
        struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
@@ -340,7 +359,8 @@ disable_vport(struct fc_vport *fc_vport)
        long timeout;
 
        ndlp = lpfc_findnode_did(vport, Fabric_DID);
-       if (ndlp && phba->link_state >= LPFC_LINK_UP) {
+       if (ndlp && NLP_CHK_NODE_ACT(ndlp)
+           && phba->link_state >= LPFC_LINK_UP) {
                vport->unreg_vpi_cmpl = VPORT_INVAL;
                timeout = msecs_to_jiffies(phba->fc_ratov * 2000);
                if (!lpfc_issue_els_npiv_logo(vport, ndlp))
@@ -354,6 +374,8 @@ disable_vport(struct fc_vport *fc_vport)
         * calling lpfc_cleanup_rpis(vport, 1)
         */
        list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
+               if (!NLP_CHK_NODE_ACT(ndlp))
+                       continue;
                if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
                        continue;
                lpfc_disc_state_machine(vport, ndlp, NULL,
@@ -371,10 +393,12 @@ disable_vport(struct fc_vport *fc_vport)
        lpfc_mbx_unreg_vpi(vport);
 
        lpfc_vport_set_state(vport, FC_VPORT_DISABLED);
+       lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+                        "1826 Vport Disabled.\n");
        return VPORT_OK;
 }
 
-int
+static int
 enable_vport(struct fc_vport *fc_vport)
 {
        struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
@@ -394,21 +418,21 @@ enable_vport(struct fc_vport *fc_vport)
         * up and ready to FDISC.
         */
        ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
-       if (ndlp && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
+       if (ndlp && NLP_CHK_NODE_ACT(ndlp)
+           && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
                if (phba->link_flag & LS_NPIV_FAB_SUPPORTED) {
                        lpfc_set_disctmo(vport);
                        lpfc_initial_fdisc(vport);
                } else {
                        lpfc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
-                       lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
-                                       "%d (%d):0264 No NPIV Fabric "
-                                       "support\n",
-                                       phba->brd_no, vport->vpi);
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+                                        "0264 No NPIV Fabric support\n");
                }
        } else {
                lpfc_vport_set_state(vport, FC_VPORT_FAILED);
        }
-
+       lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+                        "1827 Vport Enabled.\n");
        return VPORT_OK;
 }
 
@@ -426,13 +450,33 @@ int
 lpfc_vport_delete(struct fc_vport *fc_vport)
 {
        struct lpfc_nodelist *ndlp = NULL;
-       struct lpfc_nodelist *next_ndlp;
        struct Scsi_Host *shost = (struct Scsi_Host *) fc_vport->shost;
        struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
        struct lpfc_hba   *phba = vport->phba;
        long timeout;
-       int rc = VPORT_ERROR;
 
+       if (vport->port_type == LPFC_PHYSICAL_PORT) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+                                "1812 vport_delete failed: Cannot delete "
+                                "physical host\n");
+               return VPORT_ERROR;
+       }
+       /*
+        * If we are not unloading the driver then prevent the vport_delete
+        * from happening until after this vport's discovery is finished.
+        */
+       if (!(phba->pport->load_flag & FC_UNLOADING)) {
+               int check_count = 0;
+               while (check_count < ((phba->fc_ratov * 3) + 3) &&
+                      vport->port_state > LPFC_VPORT_FAILED &&
+                      vport->port_state < LPFC_VPORT_READY) {
+                       check_count++;
+                       msleep(1000);
+               }
+               if (vport->port_state > LPFC_VPORT_FAILED &&
+                   vport->port_state < LPFC_VPORT_READY)
+                       return -EAGAIN;
+       }
        /*
         * This is a bit of a mess.  We want to ensure the shost doesn't get
         * torn down until we're done with the embedded lpfc_vport structure.
@@ -450,25 +494,62 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
         */
        if (!scsi_host_get(shost) || !scsi_host_get(shost))
                return VPORT_INVAL;
-
-       if (vport->port_type == LPFC_PHYSICAL_PORT) {
-               lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
-                               "%d:1812 vport_delete failed: Cannot delete "
-                               "physical host\n", phba->brd_no);
-               goto out;
-       }
-
+       spin_lock_irq(&phba->hbalock);
        vport->load_flag |= FC_UNLOADING;
-
+       spin_unlock_irq(&phba->hbalock);
        kfree(vport->vname);
        lpfc_debugfs_terminate(vport);
        fc_remove_host(lpfc_shost_from_vport(vport));
        scsi_remove_host(lpfc_shost_from_vport(vport));
 
        ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
-       if (ndlp && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE &&
-               phba->link_state >= LPFC_LINK_UP) {
 
+       /* In case of driver unload, we shall not perform fabric logo as the
+        * worker thread already stopped at this stage and, in this case, we
+        * can safely skip the fabric logo.
+        */
+       if (phba->pport->load_flag & FC_UNLOADING) {
+               if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
+                   ndlp->nlp_state == NLP_STE_UNMAPPED_NODE &&
+                   phba->link_state >= LPFC_LINK_UP) {
+                       /* First look for the Fabric ndlp */
+                       ndlp = lpfc_findnode_did(vport, Fabric_DID);
+                       if (!ndlp)
+                               goto skip_logo;
+                       else if (!NLP_CHK_NODE_ACT(ndlp)) {
+                               ndlp = lpfc_enable_node(vport, ndlp,
+                                                       NLP_STE_UNUSED_NODE);
+                               if (!ndlp)
+                                       goto skip_logo;
+                       }
+                       /* Remove ndlp from vport npld list */
+                       lpfc_dequeue_node(vport, ndlp);
+
+                       /* Indicate free memory when release */
+                       spin_lock_irq(&phba->ndlp_lock);
+                       NLP_SET_FREE_REQ(ndlp);
+                       spin_unlock_irq(&phba->ndlp_lock);
+                       /* Kick off release ndlp when it can be safely done */
+                       lpfc_nlp_put(ndlp);
+               }
+               goto skip_logo;
+       }
+
+       /* Otherwise, we will perform fabric logo as needed */
+       if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
+           ndlp->nlp_state == NLP_STE_UNMAPPED_NODE &&
+           phba->link_state >= LPFC_LINK_UP) {
+               if (vport->cfg_enable_da_id) {
+                       timeout = msecs_to_jiffies(phba->fc_ratov * 2000);
+                       if (!lpfc_ns_cmd(vport, SLI_CTNS_DA_ID, 0, 0))
+                               while (vport->ct_flags && timeout)
+                                       timeout = schedule_timeout(timeout);
+                       else
+                               lpfc_printf_log(vport->phba, KERN_WARNING,
+                                               LOG_VPORT,
+                                               "1829 CT command failed to "
+                                               "delete objects on fabric. \n");
+               }
                /* First look for the Fabric ndlp */
                ndlp = lpfc_findnode_did(vport, Fabric_DID);
                if (!ndlp) {
@@ -477,8 +558,27 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
                        if (!ndlp)
                                goto skip_logo;
                        lpfc_nlp_init(vport, ndlp, Fabric_DID);
+                       /* Indicate free memory when release */
+                       NLP_SET_FREE_REQ(ndlp);
                } else {
+                       if (!NLP_CHK_NODE_ACT(ndlp))
+                               ndlp = lpfc_enable_node(vport, ndlp,
+                                               NLP_STE_UNUSED_NODE);
+                               if (!ndlp)
+                                       goto skip_logo;
+
+                       /* Remove ndlp from vport npld list */
                        lpfc_dequeue_node(vport, ndlp);
+                       spin_lock_irq(&phba->ndlp_lock);
+                       if (!NLP_CHK_FREE_REQ(ndlp))
+                               /* Indicate free memory when release */
+                               NLP_SET_FREE_REQ(ndlp);
+                       else {
+                               /* Skip this if ndlp is already in free mode */
+                               spin_unlock_irq(&phba->ndlp_lock);
+                               goto skip_logo;
+                       }
+                       spin_unlock_irq(&phba->ndlp_lock);
                }
                vport->unreg_vpi_cmpl = VPORT_INVAL;
                timeout = msecs_to_jiffies(phba->fc_ratov * 2000);
@@ -488,36 +588,63 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
        }
 
 skip_logo:
+       lpfc_cleanup(vport);
        lpfc_sli_host_down(vport);
 
-       list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
-               lpfc_disc_state_machine(vport, ndlp, NULL,
-                                            NLP_EVT_DEVICE_RECOVERY);
-               lpfc_disc_state_machine(vport, ndlp, NULL,
-                                            NLP_EVT_DEVICE_RM);
-       }
-
        lpfc_stop_vport_timers(vport);
-       lpfc_unreg_all_rpis(vport);
-       lpfc_unreg_default_rpis(vport);
-       /*
-        * Completion of unreg_vpi (lpfc_mbx_cmpl_unreg_vpi) does the
-        * scsi_host_put() to release the vport.
-        */
-       lpfc_mbx_unreg_vpi(vport);
+
+       if (!(phba->pport->load_flag & FC_UNLOADING)) {
+               lpfc_unreg_all_rpis(vport);
+               lpfc_unreg_default_rpis(vport);
+               /*
+                * Completion of unreg_vpi (lpfc_mbx_cmpl_unreg_vpi)
+                * does the scsi_host_put() to release the vport.
+                */
+               lpfc_mbx_unreg_vpi(vport);
+       }
 
        lpfc_free_vpi(phba, vport->vpi);
        vport->work_port_events = 0;
        spin_lock_irq(&phba->hbalock);
        list_del_init(&vport->listentry);
        spin_unlock_irq(&phba->hbalock);
-
-       rc = VPORT_OK;
-out:
+       lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+                        "1828 Vport Deleted.\n");
        scsi_host_put(shost);
-       return rc;
+       return VPORT_OK;
 }
 
+struct lpfc_vport **
+lpfc_create_vport_work_array(struct lpfc_hba *phba)
+{
+       struct lpfc_vport *port_iterator;
+       struct lpfc_vport **vports;
+       int index = 0;
+       vports = kzalloc((phba->max_vpi + 1) * sizeof(struct lpfc_vport *),
+                        GFP_KERNEL);
+       if (vports == NULL)
+               return NULL;
+       spin_lock_irq(&phba->hbalock);
+       list_for_each_entry(port_iterator, &phba->port_list, listentry) {
+               if (!scsi_host_get(lpfc_shost_from_vport(port_iterator))) {
+                       lpfc_printf_vlog(port_iterator, KERN_WARNING, LOG_VPORT,
+                                        "1801 Create vport work array FAILED: "
+                                        "cannot do scsi_host_get\n");
+                       continue;
+               }
+               vports[index++] = port_iterator;
+       }
+       spin_unlock_irq(&phba->hbalock);
+       return vports;
+}
 
-EXPORT_SYMBOL(lpfc_vport_create);
-EXPORT_SYMBOL(lpfc_vport_delete);
+void
+lpfc_destroy_vport_work_array(struct lpfc_hba *phba, struct lpfc_vport **vports)
+{
+       int i;
+       if (vports == NULL)
+               return;
+       for (i=0; vports[i] != NULL && i <= phba->max_vpi; i++)
+               scsi_host_put(lpfc_shost_from_vport(vports[i]));
+       kfree(vports);
+}