IB/srp: Maintain a single connection per I_T nexus
[deliverable/linux.git] / drivers / infiniband / ulp / srp / ib_srp.c
index 7ccf3284dda3ae623d23f459fc2652c0ce734f5f..830ef0037087fd47c2e370c59cc02d80c9fa1bbb 100644 (file)
@@ -542,11 +542,11 @@ static void srp_remove_work(struct work_struct *work)
 
        WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED);
 
+       srp_remove_target(target);
+
        spin_lock(&target->srp_host->target_lock);
        list_del(&target->list);
        spin_unlock(&target->srp_host->target_lock);
-
-       srp_remove_target(target);
 }
 
 static void srp_rport_delete(struct srp_rport *rport)
@@ -1744,18 +1744,25 @@ static int srp_abort(struct scsi_cmnd *scmnd)
 {
        struct srp_target_port *target = host_to_target(scmnd->device->host);
        struct srp_request *req = (struct srp_request *) scmnd->host_scribble;
+       int ret;
 
        shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n");
 
        if (!req || !srp_claim_req(target, req, scmnd))
                return FAILED;
-       srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
-                         SRP_TSK_ABORT_TASK);
+       if (srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
+                             SRP_TSK_ABORT_TASK) == 0 ||
+           target->transport_offline)
+               ret = SUCCESS;
+       else if (target->transport_offline)
+               ret = FAST_IO_FAIL;
+       else
+               ret = FAILED;
        srp_free_req(target, req, scmnd, 0);
        scmnd->result = DID_ABORT << 16;
        scmnd->scsi_done(scmnd);
 
-       return SUCCESS;
+       return ret;
 }
 
 static int srp_reset_device(struct scsi_cmnd *scmnd)
@@ -1946,6 +1953,7 @@ static struct scsi_host_template srp_template = {
        .eh_abort_handler               = srp_abort,
        .eh_device_reset_handler        = srp_reset_device,
        .eh_host_reset_handler          = srp_reset_host,
+       .skip_settle_delay              = true,
        .sg_tablesize                   = SRP_DEF_SG_TABLESIZE,
        .can_queue                      = SRP_CMD_SQ_SIZE,
        .this_id                        = -1,
@@ -2001,6 +2009,36 @@ static struct class srp_class = {
        .dev_release = srp_release_dev
 };
 
+/**
+ * srp_conn_unique() - check whether the connection to a target is unique
+ */
+static bool srp_conn_unique(struct srp_host *host,
+                           struct srp_target_port *target)
+{
+       struct srp_target_port *t;
+       bool ret = false;
+
+       if (target->state == SRP_TARGET_REMOVED)
+               goto out;
+
+       ret = true;
+
+       spin_lock(&host->target_lock);
+       list_for_each_entry(t, &host->target_list, list) {
+               if (t != target &&
+                   target->id_ext == t->id_ext &&
+                   target->ioc_guid == t->ioc_guid &&
+                   target->initiator_ext == t->initiator_ext) {
+                       ret = false;
+                       break;
+               }
+       }
+       spin_unlock(&host->target_lock);
+
+out:
+       return ret;
+}
+
 /*
  * Target ports are added by writing
  *
@@ -2257,6 +2295,16 @@ static ssize_t srp_create_target(struct device *dev,
        if (ret)
                goto err;
 
+       if (!srp_conn_unique(target->srp_host, target)) {
+               shost_printk(KERN_INFO, target->scsi_host,
+                            PFX "Already connected to target port with id_ext=%016llx;ioc_guid=%016llx;initiator_ext=%016llx\n",
+                            be64_to_cpu(target->id_ext),
+                            be64_to_cpu(target->ioc_guid),
+                            be64_to_cpu(target->initiator_ext));
+               ret = -EEXIST;
+               goto err;
+       }
+
        if (!host->srp_dev->fmr_pool && !target->allow_ext_sg &&
                                target->cmd_sg_cnt < target->sg_tablesize) {
                pr_warn("No FMR pool and no external indirect descriptors, limiting sg_tablesize to cmd_sg_cnt\n");
@@ -2507,6 +2555,8 @@ static void srp_remove_one(struct ib_device *device)
        struct srp_target_port *target;
 
        srp_dev = ib_get_client_data(device, &srp_client);
+       if (!srp_dev)
+               return;
 
        list_for_each_entry_safe(host, tmp_host, &srp_dev->dev_list, list) {
                device_unregister(&host->dev);
This page took 0.025127 seconds and 5 git commands to generate.