net/smc: determine proposed ISM devices
authorUrsula Braun <ubraun@linux.ibm.com>
Sat, 26 Sep 2020 10:44:27 +0000 (12:44 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 28 Sep 2020 22:19:03 +0000 (15:19 -0700)
SMCD Version 2 allows to propose up to 8 additional ISM devices
offered to the peer as candidates for SMCD communication.
This patch covers determination of the ISM devices to be proposed.
ISM devices without PNETID are preferred, since ISM devices with
PNETID are a V1 leftover and will disappear over the time.

Signed-off-by: Ursula Braun <ubraun@linux.ibm.com>
Signed-off-by: Karsten Graul <kgraul@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/smc/af_smc.c
net/smc/smc.h
net/smc/smc_clc.c
net/smc/smc_clc.h
net/smc/smc_core.h
net/smc/smc_ism.c
net/smc/smc_pnet.c
net/smc/smc_pnet.h

index f02ed74a28e607cefdaf1c2d3fa817cc2a3f8703..1d01a01c7fd58d2b4c312e34854bcdefdac6e882 100644 (file)
@@ -571,6 +571,41 @@ static int smc_find_ism_device(struct smc_sock *smc, struct smc_init_info *ini)
        return 0;
 }
 
+/* determine possible V2 ISM devices (either without PNETID or with PNETID plus
+ * PNETID matching net_device)
+ */
+static int smc_find_ism_v2_device_clnt(struct smc_sock *smc,
+                                      struct smc_init_info *ini)
+{
+       int rc = SMC_CLC_DECL_NOSMCDDEV;
+       struct smcd_dev *smcd;
+       int i = 1;
+
+       if (smcd_indicated(ini->smc_type_v1))
+               rc = 0;         /* already initialized for V1 */
+       mutex_lock(&smcd_dev_list.mutex);
+       list_for_each_entry(smcd, &smcd_dev_list.list, list) {
+               if (smcd->going_away || smcd == ini->ism_dev[0])
+                       continue;
+               if (!smc_pnet_is_pnetid_set(smcd->pnetid) ||
+                   smc_pnet_is_ndev_pnetid(sock_net(&smc->sk), smcd->pnetid)) {
+                       ini->ism_dev[i] = smcd;
+                       ini->ism_chid[i] = smc_ism_get_chid(ini->ism_dev[i]);
+                       ini->is_smcd = true;
+                       rc = 0;
+                       i++;
+                       if (i > SMC_MAX_ISM_DEVS)
+                               break;
+               }
+       }
+       mutex_unlock(&smcd_dev_list.mutex);
+       ini->ism_offered_cnt = i - 1;
+       if (!ini->ism_dev[0] && !ini->ism_dev[1])
+               ini->smcd_version = 0;
+
+       return rc;
+}
+
 /* Check for VLAN ID and register it on ISM device just for CLC handshake */
 static int smc_connect_ism_vlan_setup(struct smc_sock *smc,
                                      struct smc_init_info *ini)
@@ -580,13 +615,45 @@ static int smc_connect_ism_vlan_setup(struct smc_sock *smc,
        return 0;
 }
 
+static int smc_find_proposal_devices(struct smc_sock *smc,
+                                    struct smc_init_info *ini)
+{
+       int rc = 0;
+
+       /* check if there is an ism device available */
+       if (ini->smcd_version & SMC_V1) {
+               if (smc_find_ism_device(smc, ini) ||
+                   smc_connect_ism_vlan_setup(smc, ini)) {
+                       if (ini->smc_type_v1 == SMC_TYPE_B)
+                               ini->smc_type_v1 = SMC_TYPE_R;
+                       else
+                               ini->smc_type_v1 = SMC_TYPE_N;
+               } /* else ISM V1 is supported for this connection */
+               if (smc_find_rdma_device(smc, ini)) {
+                       if (ini->smc_type_v1 == SMC_TYPE_B)
+                               ini->smc_type_v1 = SMC_TYPE_D;
+                       else
+                               ini->smc_type_v1 = SMC_TYPE_N;
+               } /* else RDMA is supported for this connection */
+       }
+       if (smc_ism_v2_capable && smc_find_ism_v2_device_clnt(smc, ini))
+               ini->smc_type_v2 = SMC_TYPE_N;
+
+       /* if neither ISM nor RDMA are supported, fallback */
+       if (!smcr_indicated(ini->smc_type_v1) &&
+           ini->smc_type_v1 == SMC_TYPE_N && ini->smc_type_v2 == SMC_TYPE_N)
+               rc = SMC_CLC_DECL_NOSMCDEV;
+
+       return rc;
+}
+
 /* cleanup temporary VLAN ID registration used for CLC handshake. If ISM is
  * used, the VLAN ID will be registered again during the connection setup.
  */
-static int smc_connect_ism_vlan_cleanup(struct smc_sock *smc, bool is_smcd,
+static int smc_connect_ism_vlan_cleanup(struct smc_sock *smc,
                                        struct smc_init_info *ini)
 {
-       if (!is_smcd)
+       if (!smcd_indicated(ini->smc_type_v1))
                return 0;
        if (ini->vlan_id && smc_ism_put_vlan(ini->ism_dev[0], ini->vlan_id))
                return SMC_CLC_DECL_CNFERR;
@@ -594,14 +661,14 @@ static int smc_connect_ism_vlan_cleanup(struct smc_sock *smc, bool is_smcd,
 }
 
 /* CLC handshake during connect */
-static int smc_connect_clc(struct smc_sock *smc, int smc_type,
+static int smc_connect_clc(struct smc_sock *smc,
                           struct smc_clc_msg_accept_confirm *aclc,
                           struct smc_init_info *ini)
 {
        int rc = 0;
 
        /* do inband token exchange */
-       rc = smc_clc_send_proposal(smc, smc_type, ini);
+       rc = smc_clc_send_proposal(smc, ini);
        if (rc)
                return rc;
        /* receive SMC Accept CLC message */
@@ -751,13 +818,24 @@ static int smc_connect_ism(struct smc_sock *smc,
        return 0;
 }
 
+/* check if received accept type and version matches a proposed one */
+static int smc_connect_check_aclc(struct smc_init_info *ini,
+                                 struct smc_clc_msg_accept_confirm *aclc)
+{
+       if ((aclc->hdr.typev1 == SMC_TYPE_R &&
+            !smcr_indicated(ini->smc_type_v1)) ||
+           (aclc->hdr.typev1 == SMC_TYPE_D &&
+            !smcd_indicated(ini->smc_type_v1)))
+               return SMC_CLC_DECL_MODEUNSUPP;
+
+       return 0;
+}
+
 /* perform steps before actually connecting */
 static int __smc_connect(struct smc_sock *smc)
 {
-       bool ism_supported = false, rdma_supported = false;
        struct smc_clc_msg_accept_confirm aclc;
        struct smc_init_info *ini = NULL;
-       int smc_type;
        int rc = 0;
 
        if (smc->use_fallback)
@@ -775,61 +853,52 @@ static int __smc_connect(struct smc_sock *smc)
        if (!ini)
                return smc_connect_decline_fallback(smc, SMC_CLC_DECL_MEM);
 
+       ini->smcd_version = SMC_V1;
+       ini->smcd_version |= smc_ism_v2_capable ? SMC_V2 : 0;
+       ini->smc_type_v1 = SMC_TYPE_B;
+       ini->smc_type_v2 = smc_ism_v2_capable ? SMC_TYPE_D : SMC_TYPE_N;
+
        /* get vlan id from IP device */
        if (smc_vlan_by_tcpsk(smc->clcsock, ini)) {
-               kfree(ini);
-               return smc_connect_decline_fallback(smc,
-                                                   SMC_CLC_DECL_GETVLANERR);
-       }
-
-       /* check if there is an ism device available */
-       if (!smc_find_ism_device(smc, ini) &&
-           !smc_connect_ism_vlan_setup(smc, ini)) {
-               /* ISM is supported for this connection */
-               ism_supported = true;
-               smc_type = SMC_TYPE_D;
-       }
-
-       /* check if there is a rdma device available */
-       if (!smc_find_rdma_device(smc, ini)) {
-               /* RDMA is supported for this connection */
-               rdma_supported = true;
-               if (ism_supported)
-                       smc_type = SMC_TYPE_B; /* both */
-               else
-                       smc_type = SMC_TYPE_R; /* only RDMA */
+               ini->smcd_version &= ~SMC_V1;
+               ini->smc_type_v1 = SMC_TYPE_N;
+               if (!ini->smcd_version) {
+                       rc = SMC_CLC_DECL_GETVLANERR;
+                       goto fallback;
+               }
        }
 
-       /* if neither ISM nor RDMA are supported, fallback */
-       if (!rdma_supported && !ism_supported) {
-               kfree(ini);
-               return smc_connect_decline_fallback(smc, SMC_CLC_DECL_NOSMCDEV);
-       }
+       rc = smc_find_proposal_devices(smc, ini);
+       if (rc)
+               goto fallback;
 
        /* perform CLC handshake */
-       rc = smc_connect_clc(smc, smc_type, &aclc, ini);
-       if (rc) {
-               smc_connect_ism_vlan_cleanup(smc, ism_supported, ini);
-               kfree(ini);
-               return smc_connect_decline_fallback(smc, rc);
-       }
+       rc = smc_connect_clc(smc, &aclc, ini);
+       if (rc)
+               goto vlan_cleanup;
+
+       /* check if smc modes and versions of CLC proposal and accept match */
+       rc = smc_connect_check_aclc(ini, &aclc);
+       if (rc)
+               goto vlan_cleanup;
 
        /* depending on previous steps, connect using rdma or ism */
-       if (rdma_supported && aclc.hdr.typev1 == SMC_TYPE_R)
+       if (aclc.hdr.typev1 == SMC_TYPE_R)
                rc = smc_connect_rdma(smc, &aclc, ini);
-       else if (ism_supported && aclc.hdr.typev1 == SMC_TYPE_D)
+       else if (aclc.hdr.typev1 == SMC_TYPE_D)
                rc = smc_connect_ism(smc, &aclc, ini);
-       else
-               rc = SMC_CLC_DECL_MODEUNSUPP;
-       if (rc) {
-               smc_connect_ism_vlan_cleanup(smc, ism_supported, ini);
-               kfree(ini);
-               return smc_connect_decline_fallback(smc, rc);
-       }
+       if (rc)
+               goto vlan_cleanup;
 
-       smc_connect_ism_vlan_cleanup(smc, ism_supported, ini);
+       smc_connect_ism_vlan_cleanup(smc, ini);
        kfree(ini);
        return 0;
+
+vlan_cleanup:
+       smc_connect_ism_vlan_cleanup(smc, ini);
+fallback:
+       kfree(ini);
+       return smc_connect_decline_fallback(smc, rc);
 }
 
 static void smc_connect_work(struct work_struct *work)
index 6c89cb80860ba6f83721d7b2b7344e532b703e49..0b9c904e2282c06af00a632e85d85e75ea37ae88 100644 (file)
@@ -19,6 +19,7 @@
 #include "smc_ib.h"
 
 #define SMC_V1         1               /* SMC version V1 */
+#define SMC_V2         2               /* SMC version V2 */
 
 #define SMCPROTO_SMC           0       /* SMC protocol, IPv4 */
 #define SMCPROTO_SMC6          1       /* SMC protocol, IPv6 */
index 684172ddfb3d651389b64b20444e52c7bd9ded92..37e3e7a262da4651dc9d5a3c003470fbc51728c1 100644 (file)
@@ -410,8 +410,7 @@ int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info)
 }
 
 /* send CLC PROPOSAL message across internal TCP socket */
-int smc_clc_send_proposal(struct smc_sock *smc, int smc_type,
-                         struct smc_init_info *ini)
+int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini)
 {
        struct smc_clc_msg_proposal_prefix *pclc_prfx;
        struct smc_clc_msg_proposal *pclc_base;
@@ -449,8 +448,8 @@ int smc_clc_send_proposal(struct smc_sock *smc, int smc_type,
               sizeof(SMC_EYECATCHER));
        pclc_base->hdr.type = SMC_CLC_PROPOSAL;
        pclc_base->hdr.version = SMC_V1;                /* SMC version */
-       pclc_base->hdr.typev1 = smc_type;
-       if (smcr_indicated(smc_type)) {
+       pclc_base->hdr.typev1 = ini->smc_type_v1;
+       if (smcr_indicated(ini->smc_type_v1)) {
                /* add SMC-R specifics */
                memcpy(pclc_base->lcl.id_for_peer, local_systemid,
                       sizeof(local_systemid));
@@ -459,7 +458,7 @@ int smc_clc_send_proposal(struct smc_sock *smc, int smc_type,
                       ETH_ALEN);
                pclc_base->iparea_offset = htons(0);
        }
-       if (smcd_indicated(smc_type)) {
+       if (smcd_indicated(ini->smc_type_v1)) {
                /* add SMC-D specifics */
                plen += sizeof(*pclc_smcd);
                pclc_base->iparea_offset = htons(sizeof(*pclc_smcd));
@@ -472,7 +471,7 @@ int smc_clc_send_proposal(struct smc_sock *smc, int smc_type,
        i = 0;
        vec[i].iov_base = pclc_base;
        vec[i++].iov_len = sizeof(*pclc_base);
-       if (smcd_indicated(smc_type)) {
+       if (smcd_indicated(ini->smc_type_v1)) {
                vec[i].iov_base = pclc_smcd;
                vec[i++].iov_len = sizeof(*pclc_smcd);
        }
index c4644d14beae812facc3f1f94829d2a90ac25b1f..a3aa90bf4ad75bce549bef8d83dcb9c3f01d3159 100644 (file)
@@ -205,8 +205,7 @@ int smc_clc_prfx_match(struct socket *clcsock,
 int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
                     u8 expected_type, unsigned long timeout);
 int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info);
-int smc_clc_send_proposal(struct smc_sock *smc, int smc_type,
-                         struct smc_init_info *ini);
+int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini);
 int smc_clc_send_confirm(struct smc_sock *smc);
 int smc_clc_send_accept(struct smc_sock *smc, bool srv_first_contact);
 
index d33bcbc3238f3d389702e65881b2ccb82f55edd4..39a5e2156694a0fe84d72d5e90bf9e9bcb003b4c 100644 (file)
@@ -291,6 +291,8 @@ struct smc_clc_msg_local;
 
 struct smc_init_info {
        u8                      is_smcd;
+       u8                      smc_type_v1;
+       u8                      smc_type_v2;
        u8                      first_contact_peer;
        u8                      first_contact_local;
        unsigned short          vlan_id;
@@ -304,6 +306,8 @@ struct smc_init_info {
        u64                     ism_peer_gid[SMC_MAX_ISM_DEVS + 1];
        struct smcd_dev         *ism_dev[SMC_MAX_ISM_DEVS + 1];
        u16                     ism_chid[SMC_MAX_ISM_DEVS + 1];
+       u8                      ism_offered_cnt; /* # of ISM devices offered */
+       u8                      smcd_version;
 };
 
 /* Find the connection associated with the given alert token in the link group.
index c5a5b70251b6e2c8ad4501e5f5566c1e7384dc1f..e9a6487a42cbd14c4cf73b31bb0ab17bfd1d825b 100644 (file)
@@ -338,7 +338,11 @@ int smcd_register_dev(struct smcd_dev *smcd)
                if ((*system_eid) + 24 != '0' || (*system_eid) + 28 != '0')
                        smc_ism_v2_capable = true;
        }
-       list_add_tail(&smcd->list, &smcd_dev_list.list);
+       /* sort list: devices without pnetid before devices with pnetid */
+       if (smcd->pnetid[0])
+               list_add_tail(&smcd->list, &smcd_dev_list.list);
+       else
+               list_add(&smcd->list, &smcd_dev_list.list);
        mutex_unlock(&smcd_dev_list.mutex);
 
        pr_warn_ratelimited("smc: adding smcd device %s with pnetid %.16s%s\n",
index ee1456f6ae1b0cbbb62bebb6c072bbd5cfc6bb10..f3c18b991d35c42442b332fbe280e976a8fd6517 100644 (file)
@@ -73,7 +73,7 @@ struct smc_pnetentry {
 };
 
 /* Check if the pnetid is set */
-static bool smc_pnet_is_pnetid_set(u8 *pnetid)
+bool smc_pnet_is_pnetid_set(u8 *pnetid)
 {
        if (pnetid[0] == 0 || pnetid[0] == _S)
                return false;
index 677a47ac158eae563e5e9ab80b20ed319b8ea36d..14039272f7e4263a3d86f29d0c4f048731af387f 100644 (file)
@@ -66,4 +66,5 @@ void smc_pnet_find_alt_roce(struct smc_link_group *lgr,
                            struct smc_init_info *ini,
                            struct smc_ib_device *known_dev);
 bool smc_pnet_is_ndev_pnetid(struct net *net, u8 *pnetid);
+bool smc_pnet_is_pnetid_set(u8 *pnetid);
 #endif