IB/srp: Add RDMA/CM support
authorBart Van Assche <bart.vanassche@wdc.com>
Mon, 22 Jan 2018 22:27:12 +0000 (14:27 -0800)
committerDoug Ledford <dledford@redhat.com>
Tue, 23 Jan 2018 16:35:04 +0000 (11:35 -0500)
Since the SRP_LOGIN_REQ defined in the SRP standard is larger than
what fits in the RDMA/CM login request private data, introduce a new
login request format for the RDMA/CM.

Note: since srp_daemon and ibsrpdm rely on the subnet manager and
since there is no equivalent of the IB subnet manager in non-IB
networks, login has to be performed manually for non-IB networks.

Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
drivers/infiniband/ulp/srp/ib_srp.c
drivers/infiniband/ulp/srp/ib_srp.h
include/scsi/srp.h

index 9472f59..6109f2e 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/random.h>
 #include <linux/jiffies.h>
 #include <linux/lockdep.h>
+#include <linux/inet.h>
 #include <rdma/ib_cache.h>
 
 #include <linux/atomic.h>
@@ -144,7 +145,9 @@ static void srp_remove_one(struct ib_device *device, void *client_data);
 static void srp_recv_done(struct ib_cq *cq, struct ib_wc *wc);
 static void srp_handle_qp_err(struct ib_cq *cq, struct ib_wc *wc,
                const char *opname);
-static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event);
+static int srp_ib_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event);
+static int srp_rdma_cm_handler(struct rdma_cm_id *cm_id,
+                              struct rdma_cm_event *event);
 
 static struct scsi_transport_template *ib_srp_transport_template;
 static struct workqueue_struct *srp_remove_wq;
@@ -265,8 +268,8 @@ static void srp_qp_event(struct ib_event *event, void *context)
                 ib_event_msg(event->event), event->event);
 }
 
-static int srp_init_qp(struct srp_target_port *target,
-                      struct ib_qp *qp)
+static int srp_init_ib_qp(struct srp_target_port *target,
+                         struct ib_qp *qp)
 {
        struct ib_qp_attr *attr;
        int ret;
@@ -277,7 +280,7 @@ static int srp_init_qp(struct srp_target_port *target,
 
        ret = ib_find_cached_pkey(target->srp_host->srp_dev->dev,
                                  target->srp_host->port,
-                                 be16_to_cpu(target->pkey),
+                                 be16_to_cpu(target->ib_cm.pkey),
                                  &attr->pkey_index);
        if (ret)
                goto out;
@@ -298,32 +301,110 @@ out:
        return ret;
 }
 
-static int srp_new_cm_id(struct srp_rdma_ch *ch)
+static int srp_new_ib_cm_id(struct srp_rdma_ch *ch)
 {
        struct srp_target_port *target = ch->target;
        struct ib_cm_id *new_cm_id;
 
        new_cm_id = ib_create_cm_id(target->srp_host->srp_dev->dev,
-                                   srp_cm_handler, ch);
+                                   srp_ib_cm_handler, ch);
        if (IS_ERR(new_cm_id))
                return PTR_ERR(new_cm_id);
 
-       if (ch->cm_id)
-               ib_destroy_cm_id(ch->cm_id);
-       ch->cm_id = new_cm_id;
+       if (ch->ib_cm.cm_id)
+               ib_destroy_cm_id(ch->ib_cm.cm_id);
+       ch->ib_cm.cm_id = new_cm_id;
        if (rdma_cap_opa_ah(target->srp_host->srp_dev->dev,
                            target->srp_host->port))
-               ch->path.rec_type = SA_PATH_REC_TYPE_OPA;
+               ch->ib_cm.path.rec_type = SA_PATH_REC_TYPE_OPA;
        else
-               ch->path.rec_type = SA_PATH_REC_TYPE_IB;
-       ch->path.sgid = target->sgid;
-       ch->path.dgid = target->orig_dgid;
-       ch->path.pkey = target->pkey;
-       ch->path.service_id = target->service_id;
+               ch->ib_cm.path.rec_type = SA_PATH_REC_TYPE_IB;
+       ch->ib_cm.path.sgid = target->sgid;
+       ch->ib_cm.path.dgid = target->ib_cm.orig_dgid;
+       ch->ib_cm.path.pkey = target->ib_cm.pkey;
+       ch->ib_cm.path.service_id = target->ib_cm.service_id;
 
        return 0;
 }
 
+static const char *inet_ntop(const void *sa, char *dst, unsigned int size)
+{
+       switch (((struct sockaddr *)sa)->sa_family) {
+       case AF_INET:
+               snprintf(dst, size, "%pI4",
+                        &((struct sockaddr_in *)sa)->sin_addr);
+               break;
+       case AF_INET6:
+               snprintf(dst, size, "%pI6",
+                        &((struct sockaddr_in6 *)sa)->sin6_addr);
+               break;
+       default:
+               snprintf(dst, size, "???");
+               break;
+       }
+       return dst;
+}
+
+static int srp_new_rdma_cm_id(struct srp_rdma_ch *ch)
+{
+       struct srp_target_port *target = ch->target;
+       struct rdma_cm_id *new_cm_id;
+       char src_addr[64], dst_addr[64];
+       int ret;
+
+       new_cm_id = rdma_create_id(target->net, srp_rdma_cm_handler, ch,
+                                  RDMA_PS_TCP, IB_QPT_RC);
+       if (IS_ERR(new_cm_id)) {
+               ret = PTR_ERR(new_cm_id);
+               new_cm_id = NULL;
+               goto out;
+       }
+
+       init_completion(&ch->done);
+       ret = rdma_resolve_addr(new_cm_id, target->rdma_cm.src_specified ?
+                               (struct sockaddr *)&target->rdma_cm.src : NULL,
+                               (struct sockaddr *)&target->rdma_cm.dst,
+                               SRP_PATH_REC_TIMEOUT_MS);
+       if (ret) {
+               pr_err("No route available from %s to %s (%d)\n",
+                      target->rdma_cm.src_specified ?
+                      inet_ntop(&target->rdma_cm.src, src_addr,
+                                sizeof(src_addr)) : "(any)",
+                      inet_ntop(&target->rdma_cm.dst, dst_addr,
+                                sizeof(dst_addr)),
+                      ret);
+               goto out;
+       }
+       ret = wait_for_completion_interruptible(&ch->done);
+       if (ret < 0)
+               goto out;
+
+       ret = ch->status;
+       if (ret) {
+               pr_err("Resolving address %s failed (%d)\n",
+                      inet_ntop(&target->rdma_cm.dst, dst_addr,
+                                sizeof(dst_addr)),
+                      ret);
+               goto out;
+       }
+
+       swap(ch->rdma_cm.cm_id, new_cm_id);
+
+out:
+       if (new_cm_id)
+               rdma_destroy_id(new_cm_id);
+
+       return ret;
+}
+
+static int srp_new_cm_id(struct srp_rdma_ch *ch)
+{
+       struct srp_target_port *target = ch->target;
+
+       return target->using_rdma_cm ? srp_new_rdma_cm_id(ch) :
+               srp_new_ib_cm_id(ch);
+}
+
 static struct ib_fmr_pool *srp_alloc_fmr_pool(struct srp_target_port *target)
 {
        struct srp_device *dev = target->srp_host->srp_dev;
@@ -521,16 +602,25 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch)
        init_attr->send_cq             = send_cq;
        init_attr->recv_cq             = recv_cq;
 
-       qp = ib_create_qp(dev->pd, init_attr);
-       if (IS_ERR(qp)) {
-               ret = PTR_ERR(qp);
+       if (target->using_rdma_cm) {
+               ret = rdma_create_qp(ch->rdma_cm.cm_id, dev->pd, init_attr);
+               qp = ch->rdma_cm.cm_id->qp;
+       } else {
+               qp = ib_create_qp(dev->pd, init_attr);
+               if (!IS_ERR(qp)) {
+                       ret = srp_init_ib_qp(target, qp);
+                       if (ret)
+                               ib_destroy_qp(qp);
+               } else {
+                       ret = PTR_ERR(qp);
+               }
+       }
+       if (ret) {
+               pr_err("QP creation failed for dev %s: %d\n",
+                      dev_name(&dev->dev->dev), ret);
                goto err_send_cq;
        }
 
-       ret = srp_init_qp(target, qp);
-       if (ret)
-               goto err_qp;
-
        if (dev->use_fast_reg) {
                fr_pool = srp_alloc_fr_pool(target);
                if (IS_ERR(fr_pool)) {
@@ -574,7 +664,10 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch)
        return 0;
 
 err_qp:
-       ib_destroy_qp(qp);
+       if (target->using_rdma_cm)
+               rdma_destroy_qp(ch->rdma_cm.cm_id);
+       else
+               ib_destroy_qp(qp);
 
 err_send_cq:
        ib_free_cq(send_cq);
@@ -600,9 +693,16 @@ static void srp_free_ch_ib(struct srp_target_port *target,
        if (!ch->target)
                return;
 
-       if (ch->cm_id) {
-               ib_destroy_cm_id(ch->cm_id);
-               ch->cm_id = NULL;
+       if (target->using_rdma_cm) {
+               if (ch->rdma_cm.cm_id) {
+                       rdma_destroy_id(ch->rdma_cm.cm_id);
+                       ch->rdma_cm.cm_id = NULL;
+               }
+       } else {
+               if (ch->ib_cm.cm_id) {
+                       ib_destroy_cm_id(ch->ib_cm.cm_id);
+                       ch->ib_cm.cm_id = NULL;
+               }
        }
 
        /* If srp_new_cm_id() succeeded but srp_create_ch_ib() not, return. */
@@ -658,16 +758,16 @@ static void srp_path_rec_completion(int status,
                shost_printk(KERN_ERR, target->scsi_host,
                             PFX "Got failed path rec status %d\n", status);
        else
-               ch->path = *pathrec;
+               ch->ib_cm.path = *pathrec;
        complete(&ch->done);
 }
 
-static int srp_lookup_path(struct srp_rdma_ch *ch)
+static int srp_ib_lookup_path(struct srp_rdma_ch *ch)
 {
        struct srp_target_port *target = ch->target;
        int ret = -ENODEV;
 
-       ch->path.numb_path = 1;
+       ch->ib_cm.path.numb_path = 1;
 
        init_completion(&ch->done);
 
@@ -678,10 +778,10 @@ static int srp_lookup_path(struct srp_rdma_ch *ch)
        if (!scsi_host_get(target->scsi_host))
                goto out;
 
-       ch->path_query_id = ib_sa_path_rec_get(&srp_sa_client,
+       ch->ib_cm.path_query_id = ib_sa_path_rec_get(&srp_sa_client,
                                               target->srp_host->srp_dev->dev,
                                               target->srp_host->port,
-                                              &ch->path,
+                                              &ch->ib_cm.path,
                                               IB_SA_PATH_REC_SERVICE_ID |
                                               IB_SA_PATH_REC_DGID       |
                                               IB_SA_PATH_REC_SGID       |
@@ -690,8 +790,8 @@ static int srp_lookup_path(struct srp_rdma_ch *ch)
                                               SRP_PATH_REC_TIMEOUT_MS,
                                               GFP_KERNEL,
                                               srp_path_rec_completion,
-                                              ch, &ch->path_query);
-       ret = ch->path_query_id;
+                                              ch, &ch->ib_cm.path_query);
+       ret = ch->ib_cm.path_query_id;
        if (ret < 0)
                goto put;
 
@@ -703,9 +803,9 @@ static int srp_lookup_path(struct srp_rdma_ch *ch)
        if (ret < 0)
                shost_printk(KERN_WARNING, target->scsi_host,
                             PFX "Path record query failed: sgid %pI6, dgid %pI6, pkey %#04x, service_id %#16llx\n",
-                            ch->path.sgid.raw, ch->path.dgid.raw,
-                            be16_to_cpu(target->pkey),
-                            be64_to_cpu(target->service_id));
+                            ch->ib_cm.path.sgid.raw, ch->ib_cm.path.dgid.raw,
+                            be16_to_cpu(target->ib_cm.pkey),
+                            be64_to_cpu(target->ib_cm.service_id));
 
 put:
        scsi_host_put(target->scsi_host);
@@ -714,6 +814,34 @@ out:
        return ret;
 }
 
+static int srp_rdma_lookup_path(struct srp_rdma_ch *ch)
+{
+       struct srp_target_port *target = ch->target;
+       int ret;
+
+       init_completion(&ch->done);
+
+       ret = rdma_resolve_route(ch->rdma_cm.cm_id, SRP_PATH_REC_TIMEOUT_MS);
+       if (ret)
+               return ret;
+
+       wait_for_completion_interruptible(&ch->done);
+
+       if (ch->status != 0)
+               shost_printk(KERN_WARNING, target->scsi_host,
+                            PFX "Path resolution failed\n");
+
+       return ch->status;
+}
+
+static int srp_lookup_path(struct srp_rdma_ch *ch)
+{
+       struct srp_target_port *target = ch->target;
+
+       return target->using_rdma_cm ? srp_rdma_lookup_path(ch) :
+               srp_ib_lookup_path(ch);
+}
+
 static u8 srp_get_subnet_timeout(struct srp_host *host)
 {
        struct ib_port_attr attr;
@@ -735,8 +863,10 @@ static int srp_send_req(struct srp_rdma_ch *ch, bool multich)
 {
        struct srp_target_port *target = ch->target;
        struct {
-               struct ib_cm_req_param param;
-               struct srp_login_req   priv;
+               struct rdma_conn_param    rdma_param;
+               struct srp_login_req_rdma rdma_req;
+               struct ib_cm_req_param    ib_param;
+               struct srp_login_req      ib_req;
        } *req = NULL;
        char *ipi, *tpi;
        int status;
@@ -745,44 +875,62 @@ static int srp_send_req(struct srp_rdma_ch *ch, bool multich)
        if (!req)
                return -ENOMEM;
 
-       req->param.flow_control               = 1;
-       req->param.retry_count                = target->tl_retry_count;
+       req->ib_param.flow_control = 1;
+       req->ib_param.retry_count = target->tl_retry_count;
 
        /*
         * Pick some arbitrary defaults here; we could make these
         * module parameters if anyone cared about setting them.
         */
-       req->param.responder_resources        = 4;
-       req->param.rnr_retry_count            = 7;
-       req->param.max_cm_retries             = 15;
-
-       req->priv.opcode        = SRP_LOGIN_REQ;
-       req->priv.tag           = 0;
-       req->priv.req_it_iu_len = cpu_to_be32(target->max_iu_len);
-       req->priv.req_buf_fmt   = cpu_to_be16(SRP_BUF_FORMAT_DIRECT |
+       req->ib_param.responder_resources = 4;
+       req->ib_param.rnr_retry_count = 7;
+       req->ib_param.max_cm_retries = 15;
+
+       req->ib_req.opcode = SRP_LOGIN_REQ;
+       req->ib_req.tag = 0;
+       req->ib_req.req_it_iu_len = cpu_to_be32(target->max_iu_len);
+       req->ib_req.req_buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT |
                                              SRP_BUF_FORMAT_INDIRECT);
-       req->priv.req_flags     = (multich ? SRP_MULTICHAN_MULTI :
-                                  SRP_MULTICHAN_SINGLE);
-
-       {
+       req->ib_req.req_flags = (multich ? SRP_MULTICHAN_MULTI :
+                                SRP_MULTICHAN_SINGLE);
+
+       if (target->using_rdma_cm) {
+               req->rdma_param.flow_control = req->ib_param.flow_control;
+               req->rdma_param.responder_resources =
+                       req->ib_param.responder_resources;
+               req->rdma_param.initiator_depth = req->ib_param.initiator_depth;
+               req->rdma_param.retry_count = req->ib_param.retry_count;
+               req->rdma_param.rnr_retry_count = req->ib_param.rnr_retry_count;
+               req->rdma_param.private_data = &req->rdma_req;
+               req->rdma_param.private_data_len = sizeof(req->rdma_req);
+
+               req->rdma_req.opcode = req->ib_req.opcode;
+               req->rdma_req.tag = req->ib_req.tag;
+               req->rdma_req.req_it_iu_len = req->ib_req.req_it_iu_len;
+               req->rdma_req.req_buf_fmt = req->ib_req.req_buf_fmt;
+               req->rdma_req.req_flags = req->ib_req.req_flags;
+
+               ipi = req->rdma_req.initiator_port_id;
+               tpi = req->rdma_req.target_port_id;
+       } else {
                u8 subnet_timeout;
 
                subnet_timeout = srp_get_subnet_timeout(target->srp_host);
 
-               req->param.primary_path = &ch->path;
-               req->param.alternate_path = NULL;
-               req->param.service_id = target->service_id;
-               get_random_bytes(&req->param.starting_psn, 4);
-               req->param.starting_psn &= 0xffffff;
-               req->param.qp_num = ch->qp->qp_num;
-               req->param.qp_type = ch->qp->qp_type;
-               req->param.local_cm_response_timeout = subnet_timeout + 2;
-               req->param.remote_cm_response_timeout = subnet_timeout + 2;
-               req->param.private_data = &req->priv;
-               req->param.private_data_len = sizeof(req->priv);
-
-               ipi = req->priv.initiator_port_id;
-               tpi = req->priv.target_port_id;
+               req->ib_param.primary_path = &ch->ib_cm.path;
+               req->ib_param.alternate_path = NULL;
+               req->ib_param.service_id = target->ib_cm.service_id;
+               get_random_bytes(&req->ib_param.starting_psn, 4);
+               req->ib_param.starting_psn &= 0xffffff;
+               req->ib_param.qp_num = ch->qp->qp_num;
+               req->ib_param.qp_type = ch->qp->qp_type;
+               req->ib_param.local_cm_response_timeout = subnet_timeout + 2;
+               req->ib_param.remote_cm_response_timeout = subnet_timeout + 2;
+               req->ib_param.private_data = &req->ib_req;
+               req->ib_param.private_data_len = sizeof(req->ib_req);
+
+               ipi = req->ib_req.initiator_port_id;
+               tpi = req->ib_req.target_port_id;
        }
 
        /*
@@ -820,7 +968,10 @@ static int srp_send_req(struct srp_rdma_ch *ch, bool multich)
                memcpy(ipi + 8, &target->srp_host->srp_dev->dev->node_guid, 8);
        }
 
-       status = ib_send_cm_req(ch->cm_id, &req->param);
+       if (target->using_rdma_cm)
+               status = rdma_connect(ch->rdma_cm.cm_id, &req->rdma_param);
+       else
+               status = ib_send_cm_req(ch->ib_cm.cm_id, &req->ib_param);
 
        kfree(req);
 
@@ -847,14 +998,23 @@ static bool srp_queue_remove_work(struct srp_target_port *target)
 static void srp_disconnect_target(struct srp_target_port *target)
 {
        struct srp_rdma_ch *ch;
-       int i;
+       int i, ret;
 
        /* XXX should send SRP_I_LOGOUT request */
 
        for (i = 0; i < target->ch_count; i++) {
                ch = &target->ch[i];
                ch->connected = false;
-               if (ch->cm_id && ib_send_cm_dreq(ch->cm_id, NULL, 0)) {
+               ret = 0;
+               if (target->using_rdma_cm) {
+                       if (ch->rdma_cm.cm_id)
+                               rdma_disconnect(ch->rdma_cm.cm_id);
+               } else {
+                       if (ch->ib_cm.cm_id)
+                               ret = ib_send_cm_dreq(ch->ib_cm.cm_id,
+                                                     NULL, 0);
+               }
+               if (ret < 0) {
                        shost_printk(KERN_DEBUG, target->scsi_host,
                                     PFX "Sending CM DREQ failed\n");
                }
@@ -968,6 +1128,7 @@ static void srp_remove_target(struct srp_target_port *target)
        scsi_remove_host(target->scsi_host);
        srp_stop_rport_timers(target->rport);
        srp_disconnect_target(target);
+       kobj_ns_drop(KOBJ_NS_TYPE_NET, target->net);
        for (i = 0; i < target->ch_count; i++) {
                ch = &target->ch[i];
                srp_free_ch_ib(target, ch);
@@ -2355,7 +2516,7 @@ static void srp_cm_rep_handler(struct ib_cm_id *cm_id,
        struct srp_target_port *target = ch->target;
        struct ib_qp_attr *qp_attr = NULL;
        int attr_mask = 0;
-       int ret;
+       int ret = 0;
        int i;
 
        if (lrsp->opcode == SRP_LOGIN_RSP) {
@@ -2385,40 +2546,42 @@ static void srp_cm_rep_handler(struct ib_cm_id *cm_id,
                        goto error;
        }
 
-       ret = -ENOMEM;
-       qp_attr = kmalloc(sizeof *qp_attr, GFP_KERNEL);
-       if (!qp_attr)
-               goto error;
-
-       qp_attr->qp_state = IB_QPS_RTR;
-       ret = ib_cm_init_qp_attr(cm_id, qp_attr, &attr_mask);
-       if (ret)
-               goto error_free;
-
-       ret = ib_modify_qp(ch->qp, qp_attr, attr_mask);
-       if (ret)
-               goto error_free;
-
        for (i = 0; i < target->queue_size; i++) {
                struct srp_iu *iu = ch->rx_ring[i];
 
                ret = srp_post_recv(ch, iu);
                if (ret)
-                       goto error_free;
+                       goto error;
        }
 
-       qp_attr->qp_state = IB_QPS_RTS;
-       ret = ib_cm_init_qp_attr(cm_id, qp_attr, &attr_mask);
-       if (ret)
-               goto error_free;
+       if (!target->using_rdma_cm) {
+               ret = -ENOMEM;
+               qp_attr = kmalloc(sizeof(*qp_attr), GFP_KERNEL);
+               if (!qp_attr)
+                       goto error;
+
+               qp_attr->qp_state = IB_QPS_RTR;
+               ret = ib_cm_init_qp_attr(cm_id, qp_attr, &attr_mask);
+               if (ret)
+                       goto error_free;
 
-       target->rq_tmo_jiffies = srp_compute_rq_tmo(qp_attr, attr_mask);
+               ret = ib_modify_qp(ch->qp, qp_attr, attr_mask);
+               if (ret)
+                       goto error_free;
 
-       ret = ib_modify_qp(ch->qp, qp_attr, attr_mask);
-       if (ret)
-               goto error_free;
+               qp_attr->qp_state = IB_QPS_RTS;
+               ret = ib_cm_init_qp_attr(cm_id, qp_attr, &attr_mask);
+               if (ret)
+                       goto error_free;
+
+               target->rq_tmo_jiffies = srp_compute_rq_tmo(qp_attr, attr_mask);
+
+               ret = ib_modify_qp(ch->qp, qp_attr, attr_mask);
+               if (ret)
+                       goto error_free;
 
-       ret = ib_send_cm_rtu(cm_id, NULL, 0);
+               ret = ib_send_cm_rtu(cm_id, NULL, 0);
+       }
 
 error_free:
        kfree(qp_attr);
@@ -2427,41 +2590,43 @@ error:
        ch->status = ret;
 }
 
-static void srp_cm_rej_handler(struct ib_cm_id *cm_id,
-                              struct ib_cm_event *event,
-                              struct srp_rdma_ch *ch)
+static void srp_ib_cm_rej_handler(struct ib_cm_id *cm_id,
+                                 struct ib_cm_event *event,
+                                 struct srp_rdma_ch *ch)
 {
        struct srp_target_port *target = ch->target;
        struct Scsi_Host *shost = target->scsi_host;
        struct ib_class_port_info *cpi;
        int opcode;
+       u16 dlid;
 
        switch (event->param.rej_rcvd.reason) {
        case IB_CM_REJ_PORT_CM_REDIRECT:
                cpi = event->param.rej_rcvd.ari;
-               sa_path_set_dlid(&ch->path, ntohs(cpi->redirect_lid));
-               ch->path.pkey = cpi->redirect_pkey;
+               dlid = be16_to_cpu(cpi->redirect_lid);
+               sa_path_set_dlid(&ch->ib_cm.path, dlid);
+               ch->ib_cm.path.pkey = cpi->redirect_pkey;
                cm_id->remote_cm_qpn = be32_to_cpu(cpi->redirect_qp) & 0x00ffffff;
-               memcpy(ch->path.dgid.raw, cpi->redirect_gid, 16);
+               memcpy(ch->ib_cm.path.dgid.raw, cpi->redirect_gid, 16);
 
-               ch->status = sa_path_get_dlid(&ch->path) ?
-                       SRP_DLID_REDIRECT : SRP_PORT_REDIRECT;
+               ch->status = dlid ? SRP_DLID_REDIRECT : SRP_PORT_REDIRECT;
                break;
 
        case IB_CM_REJ_PORT_REDIRECT:
                if (srp_target_is_topspin(target)) {
+                       union ib_gid *dgid = &ch->ib_cm.path.dgid;
+
                        /*
                         * Topspin/Cisco SRP gateways incorrectly send
                         * reject reason code 25 when they mean 24
                         * (port redirect).
                         */
-                       memcpy(ch->path.dgid.raw,
-                              event->param.rej_rcvd.ari, 16);
+                       memcpy(dgid->raw, event->param.rej_rcvd.ari, 16);
 
                        shost_printk(KERN_DEBUG, shost,
                                     PFX "Topspin/Cisco redirect to target port GID %016llx%016llx\n",
-                                    be64_to_cpu(ch->path.dgid.global.subnet_prefix),
-                                    be64_to_cpu(ch->path.dgid.global.interface_id));
+                                    be64_to_cpu(dgid->global.subnet_prefix),
+                                    be64_to_cpu(dgid->global.interface_id));
 
                        ch->status = SRP_PORT_REDIRECT;
                } else {
@@ -2490,7 +2655,8 @@ static void srp_cm_rej_handler(struct ib_cm_id *cm_id,
                                shost_printk(KERN_WARNING, shost, PFX
                                             "SRP LOGIN from %pI6 to %pI6 REJECTED, reason 0x%08x\n",
                                             target->sgid.raw,
-                                            target->orig_dgid.raw, reason);
+                                            target->ib_cm.orig_dgid.raw,
+                                            reason);
                } else
                        shost_printk(KERN_WARNING, shost,
                                     "  REJ reason: IB_CM_REJ_CONSUMER_DEFINED,"
@@ -2510,7 +2676,7 @@ static void srp_cm_rej_handler(struct ib_cm_id *cm_id,
        }
 }
 
-static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
+static int srp_ib_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
 {
        struct srp_rdma_ch *ch = cm_id->context;
        struct srp_target_port *target = ch->target;
@@ -2533,7 +2699,7 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
                shost_printk(KERN_DEBUG, target->scsi_host, PFX "REJ received\n");
                comp = 1;
 
-               srp_cm_rej_handler(cm_id, event, ch);
+               srp_ib_cm_rej_handler(cm_id, event, ch);
                break;
 
        case IB_CM_DREQ_RECEIVED:
@@ -2571,6 +2737,135 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
        return 0;
 }
 
+static void srp_rdma_cm_rej_handler(struct srp_rdma_ch *ch,
+                                   struct rdma_cm_event *event)
+{
+       struct srp_target_port *target = ch->target;
+       struct Scsi_Host *shost = target->scsi_host;
+       int opcode;
+
+       switch (event->status) {
+       case IB_CM_REJ_DUPLICATE_LOCAL_COMM_ID:
+               shost_printk(KERN_WARNING, shost,
+                           "  REJ reason: IB_CM_REJ_DUPLICATE_LOCAL_COMM_ID\n");
+               ch->status = -ECONNRESET;
+               break;
+
+       case IB_CM_REJ_CONSUMER_DEFINED:
+               opcode = *(u8 *) event->param.conn.private_data;
+               if (opcode == SRP_LOGIN_REJ) {
+                       struct srp_login_rej *rej =
+                               (struct srp_login_rej *)
+                               event->param.conn.private_data;
+                       u32 reason = be32_to_cpu(rej->reason);
+
+                       if (reason == SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE)
+                               shost_printk(KERN_WARNING, shost,
+                                            PFX "SRP_LOGIN_REJ: requested max_it_iu_len too large\n");
+                       else
+                               shost_printk(KERN_WARNING, shost,
+                                           PFX "SRP LOGIN REJECTED, reason 0x%08x\n", reason);
+               } else {
+                       shost_printk(KERN_WARNING, shost,
+                                    "  REJ reason: IB_CM_REJ_CONSUMER_DEFINED, opcode 0x%02x\n",
+                                    opcode);
+               }
+               ch->status = -ECONNRESET;
+               break;
+
+       case IB_CM_REJ_STALE_CONN:
+               shost_printk(KERN_WARNING, shost,
+                            "  REJ reason: stale connection\n");
+               ch->status = SRP_STALE_CONN;
+               break;
+
+       default:
+               shost_printk(KERN_WARNING, shost, "  REJ reason 0x%x\n",
+                            event->status);
+               ch->status = -ECONNRESET;
+               break;
+       }
+}
+
+static int srp_rdma_cm_handler(struct rdma_cm_id *cm_id,
+                              struct rdma_cm_event *event)
+{
+       struct srp_rdma_ch *ch = cm_id->context;
+       struct srp_target_port *target = ch->target;
+       int comp = 0;
+
+       switch (event->event) {
+       case RDMA_CM_EVENT_ADDR_RESOLVED:
+               ch->status = 0;
+               comp = 1;
+               break;
+
+       case RDMA_CM_EVENT_ADDR_ERROR:
+               ch->status = -ENXIO;
+               comp = 1;
+               break;
+
+       case RDMA_CM_EVENT_ROUTE_RESOLVED:
+               ch->status = 0;
+               comp = 1;
+               break;
+
+       case RDMA_CM_EVENT_ROUTE_ERROR:
+       case RDMA_CM_EVENT_UNREACHABLE:
+               ch->status = -EHOSTUNREACH;
+               comp = 1;
+               break;
+
+       case RDMA_CM_EVENT_CONNECT_ERROR:
+               shost_printk(KERN_DEBUG, target->scsi_host,
+                            PFX "Sending CM REQ failed\n");
+               comp = 1;
+               ch->status = -ECONNRESET;
+               break;
+
+       case RDMA_CM_EVENT_ESTABLISHED:
+               comp = 1;
+               srp_cm_rep_handler(NULL, event->param.conn.private_data, ch);
+               break;
+
+       case RDMA_CM_EVENT_REJECTED:
+               shost_printk(KERN_DEBUG, target->scsi_host, PFX "REJ received\n");
+               comp = 1;
+
+               srp_rdma_cm_rej_handler(ch, event);
+               break;
+
+       case RDMA_CM_EVENT_DISCONNECTED:
+               if (ch->connected) {
+                       shost_printk(KERN_WARNING, target->scsi_host,
+                                    PFX "received DREQ\n");
+                       rdma_disconnect(ch->rdma_cm.cm_id);
+                       comp = 1;
+                       ch->status = 0;
+                       queue_work(system_long_wq, &target->tl_err_work);
+               }
+               break;
+
+       case RDMA_CM_EVENT_TIMEWAIT_EXIT:
+               shost_printk(KERN_ERR, target->scsi_host,
+                            PFX "connection closed\n");
+
+               comp = 1;
+               ch->status = 0;
+               break;
+
+       default:
+               shost_printk(KERN_WARNING, target->scsi_host,
+                            PFX "Unhandled CM event %d\n", event->event);
+               break;
+       }
+
+       if (comp)
+               complete(&ch->done);
+
+       return 0;
+}
+
 /**
  * srp_change_queue_depth - setting device queue depth
  * @sdev: scsi device struct
@@ -2772,7 +3067,10 @@ static ssize_t show_service_id(struct device *dev,
 {
        struct srp_target_port *target = host_to_target(class_to_shost(dev));
 
-       return sprintf(buf, "0x%016llx\n", be64_to_cpu(target->service_id));
+       if (target->using_rdma_cm)
+               return -ENOENT;
+       return sprintf(buf, "0x%016llx\n",
+                      be64_to_cpu(target->ib_cm.service_id));
 }
 
 static ssize_t show_pkey(struct device *dev, struct device_attribute *attr,
@@ -2780,7 +3078,9 @@ static ssize_t show_pkey(struct device *dev, struct device_attribute *attr,
 {
        struct srp_target_port *target = host_to_target(class_to_shost(dev));
 
-       return sprintf(buf, "0x%04x\n", be16_to_cpu(target->pkey));
+       if (target->using_rdma_cm)
+               return -ENOENT;
+       return sprintf(buf, "0x%04x\n", be16_to_cpu(target->ib_cm.pkey));
 }
 
 static ssize_t show_sgid(struct device *dev, struct device_attribute *attr,
@@ -2797,7 +3097,9 @@ static ssize_t show_dgid(struct device *dev, struct device_attribute *attr,
        struct srp_target_port *target = host_to_target(class_to_shost(dev));
        struct srp_rdma_ch *ch = &target->ch[0];
 
-       return sprintf(buf, "%pI6\n", ch->path.dgid.raw);
+       if (target->using_rdma_cm)
+               return -ENOENT;
+       return sprintf(buf, "%pI6\n", ch->ib_cm.path.dgid.raw);
 }
 
 static ssize_t show_orig_dgid(struct device *dev,
@@ -2805,7 +3107,9 @@ static ssize_t show_orig_dgid(struct device *dev,
 {
        struct srp_target_port *target = host_to_target(class_to_shost(dev));
 
-       return sprintf(buf, "%pI6\n", target->orig_dgid.raw);
+       if (target->using_rdma_cm)
+               return -ENOENT;
+       return sprintf(buf, "%pI6\n", target->ib_cm.orig_dgid.raw);
 }
 
 static ssize_t show_req_lim(struct device *dev,
@@ -3050,6 +3354,9 @@ static bool srp_conn_unique(struct srp_host *host,
                if (t != target &&
                    target->id_ext == t->id_ext &&
                    target->ioc_guid == t->ioc_guid &&
+                   (!target->using_rdma_cm ||
+                    memcmp(&target->rdma_cm.dst, &t->rdma_cm.dst,
+                           sizeof(target->rdma_cm.dst)) == 0) &&
                    target->initiator_ext == t->initiator_ext) {
                        ret = false;
                        break;
@@ -3066,6 +3373,9 @@ out:
  *
  *     id_ext=<SRP ID ext>,ioc_guid=<SRP IOC GUID>,dgid=<dest GID>,
  *     pkey=<P_Key>,service_id=<service ID>
+ * or
+ *     id_ext=<SRP ID ext>,ioc_guid=<SRP IOC GUID>,
+ *     [src=<IPv4 address>,]dest=<IPv4 address>:<port number>
  *
  * to the add_target sysfs attribute.
  */
@@ -3086,11 +3396,19 @@ enum {
        SRP_OPT_COMP_VECTOR     = 1 << 12,
        SRP_OPT_TL_RETRY_COUNT  = 1 << 13,
        SRP_OPT_QUEUE_SIZE      = 1 << 14,
-       SRP_OPT_ALL             = (SRP_OPT_ID_EXT       |
-                                  SRP_OPT_IOC_GUID     |
-                                  SRP_OPT_DGID         |
-                                  SRP_OPT_PKEY         |
-                                  SRP_OPT_SERVICE_ID),
+       SRP_OPT_IP_SRC          = 1 << 15,
+       SRP_OPT_IP_DEST         = 1 << 16,
+};
+
+static unsigned int srp_opt_mandatory[] = {
+       SRP_OPT_ID_EXT          |
+       SRP_OPT_IOC_GUID        |
+       SRP_OPT_DGID            |
+       SRP_OPT_PKEY            |
+       SRP_OPT_SERVICE_ID,
+       SRP_OPT_ID_EXT          |
+       SRP_OPT_IOC_GUID        |
+       SRP_OPT_IP_DEST,
 };
 
 static const match_table_t srp_opt_tokens = {
@@ -3109,10 +3427,28 @@ static const match_table_t srp_opt_tokens = {
        { SRP_OPT_COMP_VECTOR,          "comp_vector=%u"        },
        { SRP_OPT_TL_RETRY_COUNT,       "tl_retry_count=%u"     },
        { SRP_OPT_QUEUE_SIZE,           "queue_size=%d"         },
+       { SRP_OPT_IP_SRC,               "src=%s"                },
+       { SRP_OPT_IP_DEST,              "dest=%s"               },
        { SRP_OPT_ERR,                  NULL                    }
 };
 
-static int srp_parse_options(const char *buf, struct srp_target_port *target)
+static int srp_parse_in(struct net *net, struct sockaddr_storage *sa,
+                       const char *addr_port_str)
+{
+       char *addr = kstrdup(addr_port_str, GFP_KERNEL);
+       char *port_str = addr;
+       int ret;
+
+       if (!addr)
+               return -ENOMEM;
+       strsep(&port_str, ":");
+       ret = inet_pton_with_scope(net, AF_UNSPEC, addr, port_str, sa);
+       kfree(addr);
+       return ret;
+}
+
+static int srp_parse_options(struct net *net, const char *buf,
+                            struct srp_target_port *target)
 {
        char *options, *sep_opt;
        char *p;
@@ -3180,7 +3516,7 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target)
                                goto out;
                        }
 
-                       ret = hex2bin(target->orig_dgid.raw, p, 16);
+                       ret = hex2bin(target->ib_cm.orig_dgid.raw, p, 16);
                        kfree(p);
                        if (ret < 0)
                                goto out;
@@ -3191,7 +3527,7 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target)
                                pr_warn("bad P_Key parameter '%s'\n", p);
                                goto out;
                        }
-                       target->pkey = cpu_to_be16(token);
+                       target->ib_cm.pkey = cpu_to_be16(token);
                        break;
 
                case SRP_OPT_SERVICE_ID:
@@ -3206,7 +3542,39 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target)
                                kfree(p);
                                goto out;
                        }
-                       target->service_id = cpu_to_be64(ull);
+                       target->ib_cm.service_id = cpu_to_be64(ull);
+                       kfree(p);
+                       break;
+
+               case SRP_OPT_IP_SRC:
+                       p = match_strdup(args);
+                       if (!p) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+                       ret = srp_parse_in(net, &target->rdma_cm.src.ss, p);
+                       if (ret < 0) {
+                               pr_warn("bad source parameter '%s'\n", p);
+                               kfree(p);
+                               goto out;
+                       }
+                       target->rdma_cm.src_specified = true;
+                       kfree(p);
+                       break;
+
+               case SRP_OPT_IP_DEST:
+                       p = match_strdup(args);
+                       if (!p) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+                       ret = srp_parse_in(net, &target->rdma_cm.dst.ss, p);
+                       if (ret < 0) {
+                               pr_warn("bad dest parameter '%s'\n", p);
+                               kfree(p);
+                               goto out;
+                       }
+                       target->using_rdma_cm = true;
                        kfree(p);
                        break;
 
@@ -3321,14 +3689,14 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target)
                }
        }
 
-       if ((opt_mask & SRP_OPT_ALL) == SRP_OPT_ALL)
-               ret = 0;
-       else
-               for (i = 0; i < ARRAY_SIZE(srp_opt_tokens); ++i)
-                       if ((srp_opt_tokens[i].token & SRP_OPT_ALL) &&
-                           !(srp_opt_tokens[i].token & opt_mask))
-                               pr_warn("target creation request is missing parameter '%s'\n",
-                                       srp_opt_tokens[i].pattern);
+       for (i = 0; i < ARRAY_SIZE(srp_opt_mandatory); i++) {
+               if ((opt_mask & srp_opt_mandatory[i]) == srp_opt_mandatory[i]) {
+                       ret = 0;
+                       break;
+               }
+       }
+       if (ret)
+               pr_warn("target creation request is missing one or more parameters\n");
 
        if (target->scsi_host->cmd_per_lun > target->scsi_host->can_queue
            && (opt_mask & SRP_OPT_MAX_CMD_PER_LUN))
@@ -3369,6 +3737,7 @@ static ssize_t srp_create_target(struct device *dev,
 
        target = host_to_target(target_host);
 
+       target->net             = kobj_ns_grab_current(KOBJ_NS_TYPE_NET);
        target->io_class        = SRP_REV16A_IB_IO_CLASS;
        target->scsi_host       = target_host;
        target->srp_host        = host;
@@ -3390,18 +3759,29 @@ static ssize_t srp_create_target(struct device *dev,
        if (ret < 0)
                goto put;
 
-       ret = srp_parse_options(buf, target);
+       ret = srp_parse_options(target->net, buf, target);
        if (ret)
                goto out;
 
        target->req_ring_size = target->queue_size - SRP_TSK_MGMT_SQ_SIZE;
 
        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));
+               if (target->using_rdma_cm) {
+                       char dst_addr[64];
+
+                       shost_printk(KERN_INFO, target->scsi_host,
+                                    PFX "Already connected to target port with id_ext=%016llx;ioc_guid=%016llx;dest=%s\n",
+                                    be64_to_cpu(target->id_ext),
+                                    be64_to_cpu(target->ioc_guid),
+                                    inet_ntop(&target->rdma_cm.dst, dst_addr,
+                                              sizeof(dst_addr)));
+               } else {
+                       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 out;
        }
@@ -3502,11 +3882,18 @@ static ssize_t srp_create_target(struct device *dev,
 
                        ret = srp_connect_ch(ch, multich);
                        if (ret) {
+                               char dst[64];
+
+                               if (target->using_rdma_cm)
+                                       inet_ntop(&target->rdma_cm.dst, dst,
+                                                 sizeof(dst));
+                               else
+                                       snprintf(dst, sizeof(dst), "%pI6",
+                                                target->ib_cm.orig_dgid.raw);
                                shost_printk(KERN_ERR, target->scsi_host,
-                                            PFX "Connection %d/%d to %pI6 failed\n",
+                                            PFX "Connection %d/%d to %s failed\n",
                                             ch_start + cpu_idx,
-                                            target->ch_count,
-                                            ch->target->orig_dgid.raw);
+                                            target->ch_count, dst);
                                if (node_idx == 0 && cpu_idx == 0) {
                                        goto free_ch;
                                } else {
@@ -3531,13 +3918,25 @@ connected:
                goto err_disconnect;
 
        if (target->state != SRP_TARGET_REMOVED) {
-               shost_printk(KERN_DEBUG, target->scsi_host, PFX
-                            "new target: id_ext %016llx ioc_guid %016llx pkey %04x service_id %016llx sgid %pI6 dgid %pI6\n",
-                            be64_to_cpu(target->id_ext),
-                            be64_to_cpu(target->ioc_guid),
-                            be16_to_cpu(target->pkey),
-                            be64_to_cpu(target->service_id),
-                            target->sgid.raw, target->orig_dgid.raw);
+               if (target->using_rdma_cm) {
+                       char dst[64];
+
+                       inet_ntop(&target->rdma_cm.dst, dst, sizeof(dst));
+                       shost_printk(KERN_DEBUG, target->scsi_host, PFX
+                                    "new target: id_ext %016llx ioc_guid %016llx sgid %pI6 dest %s\n",
+                                    be64_to_cpu(target->id_ext),
+                                    be64_to_cpu(target->ioc_guid),
+                                    target->sgid.raw, dst);
+               } else {
+                       shost_printk(KERN_DEBUG, target->scsi_host, PFX
+                                    "new target: id_ext %016llx ioc_guid %016llx pkey %04x service_id %016llx sgid %pI6 dgid %pI6\n",
+                                    be64_to_cpu(target->id_ext),
+                                    be64_to_cpu(target->ioc_guid),
+                                    be16_to_cpu(target->ib_cm.pkey),
+                                    be64_to_cpu(target->ib_cm.service_id),
+                                    target->sgid.raw,
+                                    target->ib_cm.orig_dgid.raw);
+               }
        }
 
        ret = count;
@@ -3547,8 +3946,16 @@ out:
 
 put:
        scsi_host_put(target->scsi_host);
-       if (ret < 0)
+       if (ret < 0) {
+               /*
+                * If a call to srp_remove_target() has not been scheduled,
+                * drop the network namespace reference now that was obtained
+                * earlier in this function.
+                */
+               if (target->state != SRP_TARGET_REMOVED)
+                       kobj_ns_drop(KOBJ_NS_TYPE_NET, target->net);
                scsi_host_put(target->scsi_host);
+       }
 
        return ret;
 
index a814f5e..911fc15 100644 (file)
@@ -45,6 +45,7 @@
 #include <rdma/ib_sa.h>
 #include <rdma/ib_cm.h>
 #include <rdma/ib_fmr_pool.h>
+#include <rdma/rdma_cm.h>
 
 enum {
        SRP_PATH_REC_TIMEOUT_MS = 1000,
@@ -153,11 +154,18 @@ struct srp_rdma_ch {
        struct completion       done;
        int                     status;
 
-       struct sa_path_rec      path;
-       struct ib_sa_query     *path_query;
-       int                     path_query_id;
+       union {
+               struct ib_cm {
+                       struct sa_path_rec      path;
+                       struct ib_sa_query      *path_query;
+                       int                     path_query_id;
+                       struct ib_cm_id         *cm_id;
+               } ib_cm;
+               struct rdma_cm {
+                       struct rdma_cm_id       *cm_id;
+               } rdma_cm;
+       };
 
-       struct ib_cm_id        *cm_id;
        struct srp_iu         **tx_ring;
        struct srp_iu         **rx_ring;
        struct srp_request     *req_ring;
@@ -182,6 +190,7 @@ struct srp_target_port {
        /* read only in the hot path */
        u32                     global_rkey;
        struct srp_rdma_ch      *ch;
+       struct net              *net;
        u32                     ch_count;
        u32                     lkey;
        enum srp_target_state   state;
@@ -194,7 +203,6 @@ struct srp_target_port {
        union ib_gid            sgid;
        __be64                  id_ext;
        __be64                  ioc_guid;
-       __be64                  service_id;
        __be64                  initiator_ext;
        u16                     io_class;
        struct srp_host        *srp_host;
@@ -210,8 +218,28 @@ struct srp_target_port {
        int                     comp_vector;
        int                     tl_retry_count;
 
-       union ib_gid            orig_dgid;
-       __be16                  pkey;
+       bool                    using_rdma_cm;
+
+       union {
+               struct {
+                       __be64                  service_id;
+                       union ib_gid            orig_dgid;
+                       __be16                  pkey;
+               } ib_cm;
+               struct {
+                       union {
+                               struct sockaddr_in      ip4;
+                               struct sockaddr_in6     ip6;
+                               struct sockaddr_storage ss;
+                       } src;
+                       union {
+                               struct sockaddr_in      ip4;
+                               struct sockaddr_in6     ip6;
+                               struct sockaddr_storage ss;
+                       } dst;
+                       bool src_specified;
+               } rdma_cm;
+       };
 
        u32                     rq_tmo_jiffies;
 
index 5be834d..c16a3c9 100644 (file)
@@ -129,6 +129,23 @@ struct srp_login_req {
        u8      target_port_id[16];
 };
 
+/**
+ * struct srp_login_req_rdma - RDMA/CM login parameters.
+ *
+ * RDMA/CM over InfiniBand can only carry 92 - 36 = 56 bytes of private
+ * data. The %srp_login_req_rdma structure contains the same information as
+ * %srp_login_req but with the reserved data removed.
+ */
+struct srp_login_req_rdma {
+       u64     tag;
+       __be16  req_buf_fmt;
+       u8      req_flags;
+       u8      opcode;
+       __be32  req_it_iu_len;
+       u8      initiator_port_id[16];
+       u8      target_port_id[16];
+};
+
 /*
  * The SRP spec defines the size of the LOGIN_RSP structure to be 52
  * bytes, so it needs to be packed to avoid having it padded to 56