linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / message / fusion / mptfc.c
index 8564877..c3a3499 100644 (file)
@@ -154,7 +154,7 @@ MODULE_DEVICE_TABLE(pci, mptfc_pci_table);
 
 static struct scsi_transport_template *mptfc_transport_template = NULL;
 
-static struct fc_function_template mptfc_transport_functions = {
+struct fc_function_template mptfc_transport_functions = {
        .dd_fcrport_size = 8,
        .show_host_node_name = 1,
        .show_host_port_name = 1,
@@ -341,10 +341,31 @@ mptfc_generate_rport_ids(FCDevicePage0_t *pg0, struct fc_rport_identifiers *rid)
        rid->port_name = ((u64)pg0->WWPN.High) << 32 | (u64)pg0->WWPN.Low;
        rid->port_id =   pg0->PortIdentifier;
        rid->roles = FC_RPORT_ROLE_UNKNOWN;
+       rid->roles |= FC_RPORT_ROLE_FCP_TARGET;
+       if (pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR)
+               rid->roles |= FC_RPORT_ROLE_FCP_INITIATOR;
 
        return 0;
 }
 
+static void
+mptfc_remap_sdev(struct scsi_device *sdev, void *arg)
+{
+       VirtDevice              *vdev;
+       VirtTarget              *vtarget;
+       struct scsi_target      *starget;
+
+       starget = scsi_target(sdev);
+       if (starget->hostdata == arg) {
+               vtarget = arg;
+               vdev = sdev->hostdata;
+               if (vdev) {
+                       vdev->bus_id = vtarget->bus_id;
+                       vdev->target_id = vtarget->target_id;
+               }
+       }
+}
+
 static void
 mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0)
 {
@@ -352,18 +373,15 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0)
        struct fc_rport         *rport;
        struct mptfc_rport_info *ri;
        int                     new_ri = 1;
-       u64                     pn, nn;
+       u64                     pn;
+       unsigned long           flags;
        VirtTarget              *vtarget;
-       u32                     roles = FC_RPORT_ROLE_UNKNOWN;
 
        if (mptfc_generate_rport_ids(pg0, &rport_ids) < 0)
                return;
 
-       roles |= FC_RPORT_ROLE_FCP_TARGET;
-       if (pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR)
-               roles |= FC_RPORT_ROLE_FCP_INITIATOR;
-
        /* scan list looking for a match */
+       spin_lock_irqsave(&ioc->fc_rport_lock, flags);
        list_for_each_entry(ri, &ioc->fc_rports, list) {
                pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low;
                if (pn == rport_ids.port_name) {        /* match */
@@ -373,9 +391,11 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0)
                }
        }
        if (new_ri) {   /* allocate one */
+               spin_unlock_irqrestore(&ioc->fc_rport_lock, flags);
                ri = kzalloc(sizeof(struct mptfc_rport_info), GFP_KERNEL);
                if (!ri)
                        return;
+               spin_lock_irqsave(&ioc->fc_rport_lock, flags);
                list_add_tail(&ri->list, &ioc->fc_rports);
        }
 
@@ -385,11 +405,14 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0)
        /* MPT_RPORT_INFO_FLAGS_REGISTERED - rport not previously deleted */
        if (!(ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED)) {
                ri->flags |= MPT_RPORT_INFO_FLAGS_REGISTERED;
+               spin_unlock_irqrestore(&ioc->fc_rport_lock, flags);
                rport = fc_remote_port_add(ioc->sh, channel, &rport_ids);
+               spin_lock_irqsave(&ioc->fc_rport_lock, flags);
                if (rport) {
                        ri->rport = rport;
                        if (new_ri) /* may have been reset by user */
                                rport->dev_loss_tmo = mptfc_dev_loss_tmo;
+                       *((struct mptfc_rport_info **)rport->dd_data) = ri;
                        /*
                         * if already mapped, remap here.  If not mapped,
                         * target_alloc will allocate vtarget and map,
@@ -400,22 +423,19 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0)
                                if (vtarget) {
                                        vtarget->target_id = pg0->CurrentTargetID;
                                        vtarget->bus_id = pg0->CurrentBus;
+                                       starget_for_each_device(ri->starget,
+                                               vtarget,mptfc_remap_sdev);
                                }
+                               ri->remap_needed = 0;
                        }
-                       *((struct mptfc_rport_info **)rport->dd_data) = ri;
-                       /* scan will be scheduled once rport becomes a target */
-                       fc_remote_port_rolechg(rport,roles);
-
-                       pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low;
-                       nn = (u64)ri->pg0.WWNN.High << 32 | (u64)ri->pg0.WWNN.Low;
                        dfcprintk ((MYIOC_s_INFO_FMT
                                "mptfc_reg_dev.%d: %x, %llx / %llx, tid %d, "
                                "rport tid %d, tmo %d\n",
                                        ioc->name,
-                                       ioc->sh->host_no,
+                                       oc->sh->host_no,
                                        pg0->PortIdentifier,
-                                       (unsigned long long)nn,
-                                       (unsigned long long)pn,
+                                       pg0->WWNN,
+                                       pg0->WWPN,
                                        pg0->CurrentTargetID,
                                        ri->rport->scsi_target_id,
                                        ri->rport->dev_loss_tmo));
@@ -425,6 +445,8 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0)
                        ri = NULL;
                }
        }
+       spin_unlock_irqrestore(&ioc->fc_rport_lock,flags);
+
 }
 
 /*
@@ -474,6 +496,7 @@ mptfc_target_alloc(struct scsi_target *starget)
                        vtarget->target_id = ri->pg0.CurrentTargetID;
                        vtarget->bus_id = ri->pg0.CurrentBus;
                        ri->starget = starget;
+                       ri->remap_needed = 0;
                        rc = 0;
                }
        }
@@ -491,7 +514,7 @@ mptfc_target_alloc(struct scsi_target *starget)
  *     Return non-zero if allocation fails.
  *     Init memory once per LUN.
  */
-static int
+int
 mptfc_slave_alloc(struct scsi_device *sdev)
 {
        MPT_SCSI_HOST           *hd;
@@ -499,10 +522,10 @@ mptfc_slave_alloc(struct scsi_device *sdev)
        VirtDevice              *vdev;
        struct scsi_target      *starget;
        struct fc_rport         *rport;
+       unsigned long           flags;
 
 
-       starget = scsi_target(sdev);
-       rport = starget_to_rport(starget);
+       rport = starget_to_rport(scsi_target(sdev));
 
        if (!rport || fc_remote_port_chkready(rport))
                return -ENXIO;
@@ -516,8 +539,10 @@ mptfc_slave_alloc(struct scsi_device *sdev)
                return -ENOMEM;
        }
 
+       spin_lock_irqsave(&hd->ioc->fc_rport_lock,flags);
 
        sdev->hostdata = vdev;
+       starget = scsi_target(sdev);
        vtarget = starget->hostdata;
 
        if (vtarget->num_luns == 0) {
@@ -528,30 +553,23 @@ mptfc_slave_alloc(struct scsi_device *sdev)
        }
 
        vdev->vtarget = vtarget;
+       vdev->ioc_id = hd->ioc->id;
        vdev->lun = sdev->lun;
+       vdev->target_id = vtarget->target_id;
+       vdev->bus_id = vtarget->bus_id;
 
-       vtarget->num_luns++;
+       spin_unlock_irqrestore(&hd->ioc->fc_rport_lock,flags);
 
+       vtarget->num_luns++;
 
-#ifdef DMPT_DEBUG_FC
-       {
-       u64 nn, pn;
-       struct mptfc_rport_info *ri;
-       ri = *((struct mptfc_rport_info **)rport->dd_data);
-       pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low;
-       nn = (u64)ri->pg0.WWNN.High << 32 | (u64)ri->pg0.WWNN.Low;
        dfcprintk ((MYIOC_s_INFO_FMT
                "mptfc_slv_alloc.%d: num_luns %d, sdev.id %d, "
                "CurrentTargetID %d, %x %llx %llx\n",
-               hd->ioc->name,
+               ioc->name,
                sdev->host->host_no,
                vtarget->num_luns,
                sdev->id, ri->pg0.CurrentTargetID,
-               ri->pg0.PortIdentifier,
-               (unsigned long long)pn,
-               (unsigned long long)nn));
-       }
-#endif
+               ri->pg0.PortIdentifier, ri->pg0.WWPN, ri->pg0.WWNN));
 
        return 0;
 }
@@ -569,31 +587,11 @@ mptfc_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
                done(SCpnt);
                return 0;
        }
-
-       /* dd_data is null until finished adding target */
        ri = *((struct mptfc_rport_info **)rport->dd_data);
-       if (unlikely(!ri)) {
-               dfcprintk ((MYIOC_s_INFO_FMT
-                       "mptfc_qcmd.%d: %d:%d, dd_data is null.\n",
-                       ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->name,
-                       ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->sh->host_no,
-                       SCpnt->device->id,SCpnt->device->lun));
-               SCpnt->result = DID_IMM_RETRY << 16;
-               done(SCpnt);
-               return 0;
-       }
+       if (unlikely(ri->remap_needed))
+               return SCSI_MLQUEUE_HOST_BUSY;
 
-       err = mptscsih_qcmd(SCpnt,done);
-#ifdef DMPT_DEBUG_FC
-       if (unlikely(err)) {
-               dfcprintk ((MYIOC_s_INFO_FMT
-                       "mptfc_qcmd.%d: %d:%d, mptscsih_qcmd returns non-zero.\n",
-                       ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->name,
-                       ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->sh->host_no,
-                       SCpnt->device->id,SCpnt->device->lun));
-       }
-#endif
-       return err;
+       return mptscsih_qcmd(SCpnt,done);
 }
 
 static void
@@ -634,17 +632,18 @@ mptfc_rescan_devices(void *arg)
        MPT_ADAPTER             *ioc = (MPT_ADAPTER *)arg;
        int                     ii;
        int                     work_to_do;
-       u64                     pn;
        unsigned long           flags;
        struct mptfc_rport_info *ri;
 
        do {
                /* start by tagging all ports as missing */
+               spin_lock_irqsave(&ioc->fc_rport_lock,flags);
                list_for_each_entry(ri, &ioc->fc_rports, list) {
                        if (ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED) {
                                ri->flags |= MPT_RPORT_INFO_FLAGS_MISSING;
                        }
                }
+               spin_unlock_irqrestore(&ioc->fc_rport_lock,flags);
 
                /*
                 * now rescan devices known to adapter,
@@ -657,24 +656,33 @@ mptfc_rescan_devices(void *arg)
                }
 
                /* delete devices still missing */
+               spin_lock_irqsave(&ioc->fc_rport_lock, flags);
                list_for_each_entry(ri, &ioc->fc_rports, list) {
                        /* if newly missing, delete it */
-                       if (ri->flags & MPT_RPORT_INFO_FLAGS_MISSING) {
+                       if ((ri->flags & (MPT_RPORT_INFO_FLAGS_REGISTERED |
+                                         MPT_RPORT_INFO_FLAGS_MISSING))
+                         == (MPT_RPORT_INFO_FLAGS_REGISTERED |
+                             MPT_RPORT_INFO_FLAGS_MISSING)) {
 
                                ri->flags &= ~(MPT_RPORT_INFO_FLAGS_REGISTERED|
                                               MPT_RPORT_INFO_FLAGS_MISSING);
-                               fc_remote_port_delete(ri->rport);       /* won't sleep */
+                               ri->remap_needed = 1;
+                               fc_remote_port_delete(ri->rport);
+                               /*
+                                * remote port not really deleted 'cause
+                                * binding is by WWPN and driver only
+                                * registers FCP_TARGETs but cannot trust
+                                * data structures.
+                                */
                                ri->rport = NULL;
-
-                               pn = (u64)ri->pg0.WWPN.High << 32 |
-                                    (u64)ri->pg0.WWPN.Low;
                                dfcprintk ((MYIOC_s_INFO_FMT
                                        "mptfc_rescan.%d: %llx deleted\n",
                                        ioc->name,
                                        ioc->sh->host_no,
-                                       (unsigned long long)pn));
+                                       ri->pg0.WWPN));
                        }
                }
+               spin_unlock_irqrestore(&ioc->fc_rport_lock,flags);
 
                /*
                 * allow multiple passes as target state
@@ -879,23 +887,10 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto out_mptfc_probe;
        }
 
-       /* initialize workqueue */
-
-       snprintf(ioc->fc_rescan_work_q_name, KOBJ_NAME_LEN, "mptfc_wq_%d",
-               sh->host_no);
-       ioc->fc_rescan_work_q =
-               create_singlethread_workqueue(ioc->fc_rescan_work_q_name);
-       if (!ioc->fc_rescan_work_q)
-               goto out_mptfc_probe;
-
-       /*
-        * scan for rports -
-        *      by doing it via the workqueue, some locking is eliminated
-        */
-
-       ioc->fc_rescan_work_count = 1;
-       queue_work(ioc->fc_rescan_work_q, &ioc->fc_rescan_work);
-       flush_workqueue(ioc->fc_rescan_work_q);
+       for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) {
+               mptfc_init_host_attr(ioc,ii);
+               mptfc_GetFcDevPage0(ioc,ii,mptfc_register_dev);
+       }
 
        return 0;
 
@@ -946,7 +941,7 @@ mptfc_init(void)
        mptfcInternalCtx = mpt_register(mptscsih_scandv_complete, MPTFC_DRIVER);
 
        if (mpt_event_register(mptfcDoneCtx, mptscsih_event_process) == 0) {
-               devtverboseprintk((KERN_INFO MYNAM
+               devtprintk((KERN_INFO MYNAM
                  ": Registered for IOC event notifications\n"));
        }
 
@@ -971,18 +966,8 @@ mptfc_init(void)
 static void __devexit
 mptfc_remove(struct pci_dev *pdev)
 {
-       MPT_ADAPTER             *ioc = pci_get_drvdata(pdev);
-       struct mptfc_rport_info *p, *n;
-       struct workqueue_struct *work_q;
-       unsigned long           flags;
-
-       /* destroy workqueue */
-       if ((work_q=ioc->fc_rescan_work_q)) {
-               spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags);
-               ioc->fc_rescan_work_q = NULL;
-               spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags);
-               destroy_workqueue(work_q);
-       }
+       MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+       struct mptfc_rport_info *p, *n;
 
        fc_remove_host(ioc->sh);