IB/srp: Maintain a single connection per I_T nexus
authorBart Van Assche <bvanassche@acm.org>
Fri, 28 Jun 2013 12:51:26 +0000 (14:51 +0200)
committerRoland Dreier <roland@purestorage.com>
Mon, 1 Jul 2013 17:40:07 +0000 (10:40 -0700)
An SRP target is required to maintain a single connection between
initiator and target.  This means that if the 'add_target' attribute
is used to create a second connection to a target, the first
connection will be logged out and that the SCSI error handler will
kick in.  The SCSI error handler will cause the SRP initiator to
reconnect, which will cause I/O over the second connection to fail.
Avoid such ping-pong behavior by disabling relogins.

If reconnecting manually is necessary, that is possible by deleting
and recreating an rport via sysfs.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Sebastian Riemer <sebastian.riemer@profitbricks.com>
Acked-by: David Dillow <dillowda@ornl.gov>
Signed-off-by: Roland Dreier <roland@purestorage.com>
drivers/infiniband/ulp/srp/ib_srp.c

index 1f33101..830ef00 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)
@@ -2009,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
  *
@@ -2265,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");