net/smc: CLC accept / confirm V2
[linux-2.6-microblaze.git] / net / smc / af_smc.c
index e7649bb..2a9d142 100644 (file)
@@ -55,6 +55,9 @@ static DEFINE_MUTEX(smc_client_lgr_pending);  /* serialize link group
                                                 * creation on client
                                                 */
 
+struct workqueue_struct        *smc_hs_wq;     /* wq for handshake work */
+struct workqueue_struct        *smc_close_wq;  /* wq for close work */
+
 static void smc_tcp_listen_work(struct work_struct *);
 static void smc_connect_work(struct work_struct *);
 
@@ -436,10 +439,10 @@ static int smcr_clnt_conf_first_link(struct smc_sock *smc)
 static void smcr_conn_save_peer_info(struct smc_sock *smc,
                                     struct smc_clc_msg_accept_confirm *clc)
 {
-       int bufsize = smc_uncompress_bufsize(clc->rmbe_size);
+       int bufsize = smc_uncompress_bufsize(clc->r0.rmbe_size);
 
-       smc->conn.peer_rmbe_idx = clc->rmbe_idx;
-       smc->conn.local_tx_ctrl.token = ntohl(clc->rmbe_alert_token);
+       smc->conn.peer_rmbe_idx = clc->r0.rmbe_idx;
+       smc->conn.local_tx_ctrl.token = ntohl(clc->r0.rmbe_alert_token);
        smc->conn.peer_rmbe_size = bufsize;
        atomic_set(&smc->conn.peer_rmbe_space, smc->conn.peer_rmbe_size);
        smc->conn.tx_off = bufsize * (smc->conn.peer_rmbe_idx - 1);
@@ -448,10 +451,10 @@ static void smcr_conn_save_peer_info(struct smc_sock *smc,
 static void smcd_conn_save_peer_info(struct smc_sock *smc,
                                     struct smc_clc_msg_accept_confirm *clc)
 {
-       int bufsize = smc_uncompress_bufsize(clc->dmbe_size);
+       int bufsize = smc_uncompress_bufsize(clc->d0.dmbe_size);
 
-       smc->conn.peer_rmbe_idx = clc->dmbe_idx;
-       smc->conn.peer_token = clc->token;
+       smc->conn.peer_rmbe_idx = clc->d0.dmbe_idx;
+       smc->conn.peer_token = clc->d0.token;
        /* msg header takes up space in the buffer */
        smc->conn.peer_rmbe_size = bufsize - sizeof(struct smcd_cdc_msg);
        atomic_set(&smc->conn.peer_rmbe_space, smc->conn.peer_rmbe_size);
@@ -470,11 +473,11 @@ static void smc_conn_save_peer_info(struct smc_sock *smc,
 static void smc_link_save_peer_info(struct smc_link *link,
                                    struct smc_clc_msg_accept_confirm *clc)
 {
-       link->peer_qpn = ntoh24(clc->qpn);
-       memcpy(link->peer_gid, clc->lcl.gid, SMC_GID_SIZE);
-       memcpy(link->peer_mac, clc->lcl.mac, sizeof(link->peer_mac));
-       link->peer_psn = ntoh24(clc->psn);
-       link->peer_mtu = clc->qp_mtu;
+       link->peer_qpn = ntoh24(clc->r0.qpn);
+       memcpy(link->peer_gid, clc->r0.lcl.gid, SMC_GID_SIZE);
+       memcpy(link->peer_mac, clc->r0.lcl.mac, sizeof(link->peer_mac));
+       link->peer_psn = ntoh24(clc->r0.psn);
+       link->peer_mtu = clc->r0.qp_mtu;
 }
 
 static void smc_switch_to_fallback(struct smc_sock *smc)
@@ -523,11 +526,11 @@ static int smc_connect_decline_fallback(struct smc_sock *smc, int reason_code)
 
 /* abort connecting */
 static int smc_connect_abort(struct smc_sock *smc, int reason_code,
-                            int local_contact)
+                            int local_first)
 {
        bool is_smcd = smc->conn.lgr->is_smcd;
 
-       if (local_contact == SMC_FIRST_CONTACT)
+       if (local_first)
                smc_lgr_cleanup_early(&smc->conn);
        else
                smc_conn_free(&smc->conn);
@@ -561,47 +564,120 @@ static int smc_find_ism_device(struct smc_sock *smc, struct smc_init_info *ini)
 {
        /* Find ISM device with same PNETID as connecting interface  */
        smc_pnet_find_ism_resource(smc->clcsock->sk, ini);
-       if (!ini->ism_dev)
+       if (!ini->ism_dev[0])
                return SMC_CLC_DECL_NOSMCDDEV;
+       else
+               ini->ism_chid[0] = smc_ism_get_chid(ini->ism_dev[0]);
        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)
 {
-       if (ini->vlan_id && smc_ism_get_vlan(ini->ism_dev, ini->vlan_id))
+       if (ini->vlan_id && smc_ism_get_vlan(ini->ism_dev[0], ini->vlan_id))
                return SMC_CLC_DECL_ISMVLANERR;
        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, ini->vlan_id))
+       if (ini->vlan_id && smc_ism_put_vlan(ini->ism_dev[0], ini->vlan_id))
                return SMC_CLC_DECL_CNFERR;
        return 0;
 }
 
+#define SMC_CLC_MAX_ACCEPT_LEN \
+       (sizeof(struct smc_clc_msg_accept_confirm_v2) + \
+        sizeof(struct smc_clc_msg_trail))
+
 /* CLC handshake during connect */
-static int smc_connect_clc(struct smc_sock *smc, int smc_type,
-                          struct smc_clc_msg_accept_confirm *aclc,
+static int smc_connect_clc(struct smc_sock *smc,
+                          struct smc_clc_msg_accept_confirm_v2 *aclc2,
                           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 */
-       return smc_clc_wait_msg(smc, aclc, sizeof(*aclc), SMC_CLC_ACCEPT,
-                               CLC_WAIT_TIME);
+       return smc_clc_wait_msg(smc, aclc2, SMC_CLC_MAX_ACCEPT_LEN,
+                               SMC_CLC_ACCEPT, CLC_WAIT_TIME);
 }
 
 /* setup for RDMA connection of client */
@@ -613,9 +689,9 @@ static int smc_connect_rdma(struct smc_sock *smc,
        struct smc_link *link;
 
        ini->is_smcd = false;
-       ini->ib_lcl = &aclc->lcl;
-       ini->ib_clcqpn = ntoh24(aclc->qpn);
-       ini->srv_first_contact = aclc->hdr.flag;
+       ini->ib_lcl = &aclc->r0.lcl;
+       ini->ib_clcqpn = ntoh24(aclc->r0.qpn);
+       ini->first_contact_peer = aclc->hdr.typev2 & SMC_FIRST_CONTACT_MASK;
 
        mutex_lock(&smc_client_lgr_pending);
        reason_code = smc_conn_create(smc, ini);
@@ -626,7 +702,7 @@ static int smc_connect_rdma(struct smc_sock *smc,
 
        smc_conn_save_peer_info(smc, aclc);
 
-       if (ini->cln_first_contact == SMC_FIRST_CONTACT) {
+       if (ini->first_contact_local) {
                link = smc->conn.lnk;
        } else {
                /* set link that was assigned by server */
@@ -634,60 +710,63 @@ static int smc_connect_rdma(struct smc_sock *smc,
                for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
                        struct smc_link *l = &smc->conn.lgr->lnk[i];
 
-                       if (l->peer_qpn == ntoh24(aclc->qpn) &&
-                           !memcmp(l->peer_gid, &aclc->lcl.gid, SMC_GID_SIZE) &&
-                           !memcmp(l->peer_mac, &aclc->lcl.mac, sizeof(l->peer_mac))) {
+                       if (l->peer_qpn == ntoh24(aclc->r0.qpn) &&
+                           !memcmp(l->peer_gid, &aclc->r0.lcl.gid,
+                                   SMC_GID_SIZE) &&
+                           !memcmp(l->peer_mac, &aclc->r0.lcl.mac,
+                                   sizeof(l->peer_mac))) {
                                link = l;
                                break;
                        }
                }
                if (!link)
                        return smc_connect_abort(smc, SMC_CLC_DECL_NOSRVLINK,
-                                                ini->cln_first_contact);
+                                                ini->first_contact_local);
                smc->conn.lnk = link;
        }
 
        /* create send buffer and rmb */
        if (smc_buf_create(smc, false))
                return smc_connect_abort(smc, SMC_CLC_DECL_MEM,
-                                        ini->cln_first_contact);
+                                        ini->first_contact_local);
 
-       if (ini->cln_first_contact == SMC_FIRST_CONTACT)
+       if (ini->first_contact_local)
                smc_link_save_peer_info(link, aclc);
 
        if (smc_rmb_rtoken_handling(&smc->conn, link, aclc))
                return smc_connect_abort(smc, SMC_CLC_DECL_ERR_RTOK,
-                                        ini->cln_first_contact);
+                                        ini->first_contact_local);
 
        smc_close_init(smc);
        smc_rx_init(smc);
 
-       if (ini->cln_first_contact == SMC_FIRST_CONTACT) {
+       if (ini->first_contact_local) {
                if (smc_ib_ready_link(link))
                        return smc_connect_abort(smc, SMC_CLC_DECL_ERR_RDYLNK,
-                                                ini->cln_first_contact);
+                                                ini->first_contact_local);
        } else {
                if (smcr_lgr_reg_rmbs(link, smc->conn.rmb_desc))
                        return smc_connect_abort(smc, SMC_CLC_DECL_ERR_REGRMB,
-                                                ini->cln_first_contact);
+                                                ini->first_contact_local);
        }
        smc_rmb_sync_sg_for_device(&smc->conn);
 
-       reason_code = smc_clc_send_confirm(smc);
+       reason_code = smc_clc_send_confirm(smc, ini->first_contact_local,
+                                          SMC_V1);
        if (reason_code)
                return smc_connect_abort(smc, reason_code,
-                                        ini->cln_first_contact);
+                                        ini->first_contact_local);
 
        smc_tx_init(smc);
 
-       if (ini->cln_first_contact == SMC_FIRST_CONTACT) {
+       if (ini->first_contact_local) {
                /* QP confirmation over RoCE fabric */
                smc_llc_flow_initiate(link->lgr, SMC_LLC_FLOW_ADD_LINK);
                reason_code = smcr_clnt_conf_first_link(smc);
                smc_llc_flow_stop(link->lgr, &link->lgr->llc_flow_lcl);
                if (reason_code)
                        return smc_connect_abort(smc, reason_code,
-                                                ini->cln_first_contact);
+                                                ini->first_contact_local);
        }
        mutex_unlock(&smc_client_lgr_pending);
 
@@ -699,6 +778,25 @@ static int smc_connect_rdma(struct smc_sock *smc,
        return 0;
 }
 
+/* The server has chosen one of the proposed ISM devices for the communication.
+ * Determine from the CHID of the received CLC ACCEPT the ISM device chosen.
+ */
+static int
+smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm_v2 *aclc,
+                              struct smc_init_info *ini)
+{
+       int i;
+
+       for (i = 0; i < ini->ism_offered_cnt + 1; i++) {
+               if (ini->ism_chid[i] == ntohs(aclc->chid)) {
+                       ini->ism_selected = i;
+                       return 0;
+               }
+       }
+
+       return -EPROTO;
+}
+
 /* setup for ISM connection of client */
 static int smc_connect_ism(struct smc_sock *smc,
                           struct smc_clc_msg_accept_confirm *aclc,
@@ -707,8 +805,17 @@ static int smc_connect_ism(struct smc_sock *smc,
        int rc = 0;
 
        ini->is_smcd = true;
-       ini->ism_gid = aclc->gid;
-       ini->srv_first_contact = aclc->hdr.flag;
+       ini->first_contact_peer = aclc->hdr.typev2 & SMC_FIRST_CONTACT_MASK;
+
+       if (aclc->hdr.version == SMC_V2) {
+               struct smc_clc_msg_accept_confirm_v2 *aclc_v2 =
+                       (struct smc_clc_msg_accept_confirm_v2 *)aclc;
+
+               rc = smc_v2_determine_accepted_chid(aclc_v2, ini);
+               if (rc)
+                       return rc;
+       }
+       ini->ism_peer_gid[ini->ism_selected] = aclc->d0.gid;
 
        /* there is only one lgr role for SMC-D; use server lock */
        mutex_lock(&smc_server_lgr_pending);
@@ -724,16 +831,17 @@ static int smc_connect_ism(struct smc_sock *smc,
                return smc_connect_abort(smc, (rc == -ENOSPC) ?
                                              SMC_CLC_DECL_MAX_DMB :
                                              SMC_CLC_DECL_MEM,
-                                        ini->cln_first_contact);
+                                        ini->first_contact_local);
 
        smc_conn_save_peer_info(smc, aclc);
        smc_close_init(smc);
        smc_rx_init(smc);
        smc_tx_init(smc);
 
-       rc = smc_clc_send_confirm(smc);
+       rc = smc_clc_send_confirm(smc, ini->first_contact_local,
+                                 aclc->hdr.version);
        if (rc)
-               return smc_connect_abort(smc, rc, ini->cln_first_contact);
+               return smc_connect_abort(smc, rc, ini->first_contact_local);
        mutex_unlock(&smc_server_lgr_pending);
 
        smc_copy_sock_settings_to_clc(smc);
@@ -744,13 +852,31 @@ 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) &&
+              !smcd_indicated(ini->smc_type_v2)) ||
+             (aclc->hdr.version == SMC_V1 &&
+              !smcd_indicated(ini->smc_type_v1)) ||
+             (aclc->hdr.version == SMC_V2 &&
+              !smcd_indicated(ini->smc_type_v2)))))
+               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 = {0};
-       int smc_type;
+       struct smc_clc_msg_accept_confirm_v2 *aclc2;
+       struct smc_clc_msg_accept_confirm *aclc;
+       struct smc_init_info *ini = NULL;
+       u8 *buf = NULL;
        int rc = 0;
 
        if (smc->use_fallback)
@@ -764,54 +890,66 @@ static int __smc_connect(struct smc_sock *smc)
        if (using_ipsec(smc))
                return smc_connect_decline_fallback(smc, SMC_CLC_DECL_IPSEC);
 
-       /* get vlan id from IP device */
-       if (smc_vlan_by_tcpsk(smc->clcsock, &ini))
-               return smc_connect_decline_fallback(smc,
-                                                   SMC_CLC_DECL_GETVLANERR);
+       ini = kzalloc(sizeof(*ini), GFP_KERNEL);
+       if (!ini)
+               return smc_connect_decline_fallback(smc, SMC_CLC_DECL_MEM);
 
-       /* 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->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)) {
+               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)
-               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);
-               return smc_connect_decline_fallback(smc, rc);
+       buf = kzalloc(SMC_CLC_MAX_ACCEPT_LEN, GFP_KERNEL);
+       if (!buf) {
+               rc = SMC_CLC_DECL_MEM;
+               goto fallback;
        }
+       aclc2 = (struct smc_clc_msg_accept_confirm_v2 *)buf;
+       aclc = (struct smc_clc_msg_accept_confirm *)aclc2;
+
+       /* perform CLC handshake */
+       rc = smc_connect_clc(smc, aclc2, 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.path == SMC_TYPE_R)
-               rc = smc_connect_rdma(smc, &aclc, &ini);
-       else if (ism_supported && aclc.hdr.path == 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);
-               return smc_connect_decline_fallback(smc, rc);
-       }
+       if (aclc->hdr.typev1 == SMC_TYPE_R)
+               rc = smc_connect_rdma(smc, aclc, ini);
+       else if (aclc->hdr.typev1 == SMC_TYPE_D)
+               rc = smc_connect_ism(smc, aclc, ini);
+       if (rc)
+               goto vlan_cleanup;
 
-       smc_connect_ism_vlan_cleanup(smc, ism_supported, &ini);
+       smc_connect_ism_vlan_cleanup(smc, ini);
+       kfree(buf);
+       kfree(ini);
        return 0;
+
+vlan_cleanup:
+       smc_connect_ism_vlan_cleanup(smc, ini);
+       kfree(buf);
+fallback:
+       kfree(ini);
+       return smc_connect_decline_fallback(smc, rc);
 }
 
 static void smc_connect_work(struct work_struct *work)
@@ -903,7 +1041,7 @@ static int smc_connect(struct socket *sock, struct sockaddr *addr,
        if (smc->use_fallback)
                goto out;
        if (flags & O_NONBLOCK) {
-               if (schedule_work(&smc->connect_work))
+               if (queue_work(smc_hs_wq, &smc->connect_work))
                        smc->connect_nonblock = 1;
                rc = -EINPROGRESS;
        } else {
@@ -940,10 +1078,10 @@ static int smc_clcsock_accept(struct smc_sock *lsmc, struct smc_sock **new_smc)
 
        mutex_lock(&lsmc->clcsock_release_lock);
        if (lsmc->clcsock)
-               rc = kernel_accept(lsmc->clcsock, &new_clcsock, 0);
+               rc = kernel_accept(lsmc->clcsock, &new_clcsock, SOCK_NONBLOCK);
        mutex_unlock(&lsmc->clcsock_release_lock);
        lock_sock(lsk);
-       if  (rc < 0)
+       if  (rc < 0 && rc != -EAGAIN)
                lsk->sk_err = -rc;
        if (rc < 0 || lsk->sk_state == SMC_CLOSED) {
                new_sk->sk_prot->unhash(new_sk);
@@ -956,6 +1094,10 @@ static int smc_clcsock_accept(struct smc_sock *lsmc, struct smc_sock **new_smc)
                goto out;
        }
 
+       /* new clcsock has inherited the smc listen-specific sk_data_ready
+        * function; switch it back to the original sk_data_ready function
+        */
+       new_clcsock->sk->sk_data_ready = lsmc->clcsk_data_ready;
        (*new_smc)->clcsock = new_clcsock;
 out:
        return rc;
@@ -1123,10 +1265,10 @@ static void smc_listen_out_err(struct smc_sock *new_smc)
 
 /* listen worker: decline and fall back if possible */
 static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
-                              int local_contact)
+                              struct smc_init_info *ini)
 {
        /* RDMA setup failed, switch back to TCP */
-       if (local_contact == SMC_FIRST_CONTACT)
+       if (ini->first_contact_local)
                smc_lgr_cleanup_early(&new_smc->conn);
        else
                smc_conn_free(&new_smc->conn);
@@ -1145,6 +1287,47 @@ static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
        smc_listen_out_connected(new_smc);
 }
 
+/* listen worker: version checking */
+static int smc_listen_v2_check(struct smc_sock *new_smc,
+                              struct smc_clc_msg_proposal *pclc,
+                              struct smc_init_info *ini)
+{
+       struct smc_clc_smcd_v2_extension *pclc_smcd_v2_ext;
+       struct smc_clc_v2_extension *pclc_v2_ext;
+
+       ini->smc_type_v1 = pclc->hdr.typev1;
+       ini->smc_type_v2 = pclc->hdr.typev2;
+       ini->smcd_version = ini->smc_type_v1 != SMC_TYPE_N ? SMC_V1 : 0;
+       if (pclc->hdr.version > SMC_V1)
+               ini->smcd_version |=
+                               ini->smc_type_v2 != SMC_TYPE_N ? SMC_V2 : 0;
+       if (!smc_ism_v2_capable) {
+               ini->smcd_version &= ~SMC_V2;
+               goto out;
+       }
+       pclc_v2_ext = smc_get_clc_v2_ext(pclc);
+       if (!pclc_v2_ext) {
+               ini->smcd_version &= ~SMC_V2;
+               goto out;
+       }
+       pclc_smcd_v2_ext = smc_get_clc_smcd_v2_ext(pclc_v2_ext);
+       if (!pclc_smcd_v2_ext)
+               ini->smcd_version &= ~SMC_V2;
+
+out:
+       if (!ini->smcd_version) {
+               if (pclc->hdr.typev1 == SMC_TYPE_B ||
+                   pclc->hdr.typev2 == SMC_TYPE_B)
+                       return SMC_CLC_DECL_NOSMCDEV;
+               if (pclc->hdr.typev1 == SMC_TYPE_D ||
+                   pclc->hdr.typev2 == SMC_TYPE_D)
+                       return SMC_CLC_DECL_NOSMCDDEV;
+               return SMC_CLC_DECL_NOSMCRDEV;
+       }
+
+       return 0;
+}
+
 /* listen worker: check prefixes */
 static int smc_listen_prfx_check(struct smc_sock *new_smc,
                                 struct smc_clc_msg_proposal *pclc)
@@ -1152,6 +1335,8 @@ static int smc_listen_prfx_check(struct smc_sock *new_smc,
        struct smc_clc_msg_proposal_prefix *pclc_prfx;
        struct socket *newclcsock = new_smc->clcsock;
 
+       if (pclc->hdr.typev1 == SMC_TYPE_N)
+               return 0;
        pclc_prfx = smc_clc_proposal_get_prefix(pclc);
        if (smc_clc_prfx_match(newclcsock, pclc_prfx))
                return SMC_CLC_DECL_DIFFPREFIX;
@@ -1179,33 +1364,18 @@ static int smc_listen_rdma_init(struct smc_sock *new_smc,
 
 /* listen worker: initialize connection and buffers for SMC-D */
 static int smc_listen_ism_init(struct smc_sock *new_smc,
-                              struct smc_clc_msg_proposal *pclc,
                               struct smc_init_info *ini)
 {
-       struct smc_clc_msg_smcd *pclc_smcd;
        int rc;
 
-       pclc_smcd = smc_get_clc_msg_smcd(pclc);
-       ini->ism_gid = pclc_smcd->gid;
        rc = smc_conn_create(new_smc, ini);
        if (rc)
                return rc;
 
-       /* Check if peer can be reached via ISM device */
-       if (smc_ism_cantalk(new_smc->conn.lgr->peer_gid,
-                           new_smc->conn.lgr->vlan_id,
-                           new_smc->conn.lgr->smcd)) {
-               if (ini->cln_first_contact == SMC_FIRST_CONTACT)
-                       smc_lgr_cleanup_early(&new_smc->conn);
-               else
-                       smc_conn_free(&new_smc->conn);
-               return SMC_CLC_DECL_SMCDNOTALK;
-       }
-
        /* Create send and receive buffers */
        rc = smc_buf_create(new_smc, true);
        if (rc) {
-               if (ini->cln_first_contact == SMC_FIRST_CONTACT)
+               if (ini->first_contact_local)
                        smc_lgr_cleanup_early(&new_smc->conn);
                else
                        smc_conn_free(&new_smc->conn);
@@ -1216,12 +1386,131 @@ static int smc_listen_ism_init(struct smc_sock *new_smc,
        return 0;
 }
 
+static bool smc_is_already_selected(struct smcd_dev *smcd,
+                                   struct smc_init_info *ini,
+                                   int matches)
+{
+       int i;
+
+       for (i = 0; i < matches; i++)
+               if (smcd == ini->ism_dev[i])
+                       return true;
+
+       return false;
+}
+
+/* check for ISM devices matching proposed ISM devices */
+static void smc_check_ism_v2_match(struct smc_init_info *ini,
+                                  u16 proposed_chid, u64 proposed_gid,
+                                  unsigned int *matches)
+{
+       struct smcd_dev *smcd;
+
+       list_for_each_entry(smcd, &smcd_dev_list.list, list) {
+               if (smcd->going_away)
+                       continue;
+               if (smc_is_already_selected(smcd, ini, *matches))
+                       continue;
+               if (smc_ism_get_chid(smcd) == proposed_chid &&
+                   !smc_ism_cantalk(proposed_gid, ISM_RESERVED_VLANID, smcd)) {
+                       ini->ism_peer_gid[*matches] = proposed_gid;
+                       ini->ism_dev[*matches] = smcd;
+                       (*matches)++;
+                       break;
+               }
+       }
+}
+
+static void smc_find_ism_v2_device_serv(struct smc_sock *new_smc,
+                                       struct smc_clc_msg_proposal *pclc,
+                                       struct smc_init_info *ini)
+{
+       struct smc_clc_smcd_v2_extension *smcd_v2_ext;
+       struct smc_clc_v2_extension *smc_v2_ext;
+       struct smc_clc_msg_smcd *pclc_smcd;
+       unsigned int matches = 0;
+       u8 *eid = NULL;
+       int i;
+
+       if (!(ini->smcd_version & SMC_V2) || !smcd_indicated(ini->smc_type_v2))
+               return;
+
+       pclc_smcd = smc_get_clc_msg_smcd(pclc);
+       smc_v2_ext = smc_get_clc_v2_ext(pclc);
+       smcd_v2_ext = smc_get_clc_smcd_v2_ext(smc_v2_ext);
+       if (!smcd_v2_ext ||
+           !smc_v2_ext->hdr.flag.seid) /* no system EID support for SMCD */
+               goto not_found;
+
+       mutex_lock(&smcd_dev_list.mutex);
+       if (pclc_smcd->ism.chid)
+               /* check for ISM device matching proposed native ISM device */
+               smc_check_ism_v2_match(ini, ntohs(pclc_smcd->ism.chid),
+                                      ntohll(pclc_smcd->ism.gid), &matches);
+       for (i = 1; i <= smc_v2_ext->hdr.ism_gid_cnt; i++) {
+               /* check for ISM devices matching proposed non-native ISM
+                * devices
+                */
+               smc_check_ism_v2_match(ini,
+                                      ntohs(smcd_v2_ext->gidchid[i - 1].chid),
+                                      ntohll(smcd_v2_ext->gidchid[i - 1].gid),
+                                      &matches);
+       }
+       mutex_unlock(&smcd_dev_list.mutex);
+
+       if (ini->ism_dev[0]) {
+               smc_ism_get_system_eid(ini->ism_dev[0], &eid);
+               if (memcmp(eid, smcd_v2_ext->system_eid, SMC_MAX_EID_LEN))
+                       goto not_found;
+       } else {
+               goto not_found;
+       }
+
+       /* separate - outside the smcd_dev_list.lock */
+       for (i = 0; i < matches; i++) {
+               ini->smcd_version = SMC_V2;
+               ini->is_smcd = true;
+               ini->ism_selected = i;
+               if (smc_listen_ism_init(new_smc, ini))
+                       /* try next active ISM device */
+                       continue;
+               return; /* matching and usable V2 ISM device found */
+       }
+
+not_found:
+       ini->smcd_version &= ~SMC_V2;
+       ini->ism_dev[0] = NULL;
+       ini->is_smcd = false;
+}
+
+static void smc_find_ism_v1_device_serv(struct smc_sock *new_smc,
+                                       struct smc_clc_msg_proposal *pclc,
+                                       struct smc_init_info *ini)
+{
+       struct smc_clc_msg_smcd *pclc_smcd = smc_get_clc_msg_smcd(pclc);
+
+       /* check if ISM V1 is available */
+       if (!(ini->smcd_version & SMC_V1) || !smcd_indicated(ini->smc_type_v1))
+               goto not_found;
+       ini->is_smcd = true; /* prepare ISM check */
+       ini->ism_peer_gid[0] = ntohll(pclc_smcd->ism.gid);
+       if (smc_find_ism_device(new_smc, ini))
+               goto not_found;
+       ini->ism_selected = 0;
+       if (!smc_listen_ism_init(new_smc, ini))
+               return;         /* V1 ISM device found */
+
+not_found:
+       ini->ism_dev[0] = NULL;
+       ini->is_smcd = false;
+}
+
 /* listen worker: register buffers */
-static int smc_listen_rdma_reg(struct smc_sock *new_smc, int local_contact)
+static int smc_listen_rdma_reg(struct smc_sock *new_smc, bool local_first)
 {
        struct smc_connection *conn = &new_smc->conn;
 
-       if (local_contact != SMC_FIRST_CONTACT) {
+       if (!local_first) {
                if (smcr_lgr_reg_rmbs(conn->lnk, conn->rmb_desc))
                        return SMC_CLC_DECL_ERR_REGRMB;
        }
@@ -1230,52 +1519,103 @@ static int smc_listen_rdma_reg(struct smc_sock *new_smc, int local_contact)
        return 0;
 }
 
+static int smc_find_rdma_v1_device_serv(struct smc_sock *new_smc,
+                                       struct smc_clc_msg_proposal *pclc,
+                                       struct smc_init_info *ini)
+{
+       int rc;
+
+       if (!smcr_indicated(ini->smc_type_v1))
+               return SMC_CLC_DECL_NOSMCDEV;
+
+       /* prepare RDMA check */
+       ini->ib_lcl = &pclc->lcl;
+       rc = smc_find_rdma_device(new_smc, ini);
+       if (rc) {
+               /* no RDMA device found */
+               if (ini->smc_type_v1 == SMC_TYPE_B)
+                       /* neither ISM nor RDMA device found */
+                       rc = SMC_CLC_DECL_NOSMCDEV;
+               return rc;
+       }
+       rc = smc_listen_rdma_init(new_smc, ini);
+       if (rc)
+               return rc;
+       return smc_listen_rdma_reg(new_smc, ini->first_contact_local);
+}
+
+/* determine the local device matching to proposal */
+static int smc_listen_find_device(struct smc_sock *new_smc,
+                                 struct smc_clc_msg_proposal *pclc,
+                                 struct smc_init_info *ini)
+{
+       int rc;
+
+       /* check for ISM device matching V2 proposed device */
+       smc_find_ism_v2_device_serv(new_smc, pclc, ini);
+       if (ini->ism_dev[0])
+               return 0;
+
+       if (!(ini->smcd_version & SMC_V1))
+               return SMC_CLC_DECL_NOSMCDEV;
+
+       /* check for matching IP prefix and subnet length */
+       rc = smc_listen_prfx_check(new_smc, pclc);
+       if (rc)
+               return rc;
+
+       /* get vlan id from IP device */
+       if (smc_vlan_by_tcpsk(new_smc->clcsock, ini))
+               return SMC_CLC_DECL_GETVLANERR;
+
+       /* check for ISM device matching V1 proposed device */
+       smc_find_ism_v1_device_serv(new_smc, pclc, ini);
+       if (ini->ism_dev[0])
+               return 0;
+
+       if (pclc->hdr.typev1 == SMC_TYPE_D)
+               return SMC_CLC_DECL_NOSMCDDEV; /* skip RDMA and decline */
+
+       /* check if RDMA is available */
+       return smc_find_rdma_v1_device_serv(new_smc, pclc, ini);
+}
+
 /* listen worker: finish RDMA setup */
 static int smc_listen_rdma_finish(struct smc_sock *new_smc,
                                  struct smc_clc_msg_accept_confirm *cclc,
-                                 int local_contact)
+                                 bool local_first)
 {
        struct smc_link *link = new_smc->conn.lnk;
        int reason_code = 0;
 
-       if (local_contact == SMC_FIRST_CONTACT)
+       if (local_first)
                smc_link_save_peer_info(link, cclc);
 
-       if (smc_rmb_rtoken_handling(&new_smc->conn, link, cclc)) {
-               reason_code = SMC_CLC_DECL_ERR_RTOK;
-               goto decline;
-       }
+       if (smc_rmb_rtoken_handling(&new_smc->conn, link, cclc))
+               return SMC_CLC_DECL_ERR_RTOK;
 
-       if (local_contact == SMC_FIRST_CONTACT) {
-               if (smc_ib_ready_link(link)) {
-                       reason_code = SMC_CLC_DECL_ERR_RDYLNK;
-                       goto decline;
-               }
+       if (local_first) {
+               if (smc_ib_ready_link(link))
+                       return SMC_CLC_DECL_ERR_RDYLNK;
                /* QP confirmation over RoCE fabric */
                smc_llc_flow_initiate(link->lgr, SMC_LLC_FLOW_ADD_LINK);
                reason_code = smcr_serv_conf_first_link(new_smc);
                smc_llc_flow_stop(link->lgr, &link->lgr->llc_flow_lcl);
-               if (reason_code)
-                       goto decline;
        }
-       return 0;
-
-decline:
-       smc_listen_decline(new_smc, reason_code, local_contact);
        return reason_code;
 }
 
-/* setup for RDMA connection of server */
+/* setup for connection of server */
 static void smc_listen_work(struct work_struct *work)
 {
        struct smc_sock *new_smc = container_of(work, struct smc_sock,
                                                smc_listen_work);
        struct socket *newclcsock = new_smc->clcsock;
-       struct smc_clc_msg_accept_confirm cclc;
+       struct smc_clc_msg_accept_confirm_v2 *cclc2;
+       struct smc_clc_msg_accept_confirm *cclc;
+       struct smc_clc_msg_proposal_area *buf;
        struct smc_clc_msg_proposal *pclc;
-       struct smc_init_info ini = {0};
-       bool ism_supported = false;
-       u8 buf[SMC_CLC_MAX_LEN];
+       struct smc_init_info *ini = NULL;
        int rc = 0;
 
        if (new_smc->listen_smc->sk.sk_state != SMC_LISTEN)
@@ -1297,8 +1637,13 @@ static void smc_listen_work(struct work_struct *work)
        /* do inband token exchange -
         * wait for and receive SMC Proposal CLC message
         */
-       pclc = (struct smc_clc_msg_proposal *)&buf;
-       rc = smc_clc_wait_msg(new_smc, pclc, SMC_CLC_MAX_LEN,
+       buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+       if (!buf) {
+               rc = SMC_CLC_DECL_MEM;
+               goto out_decl;
+       }
+       pclc = (struct smc_clc_msg_proposal *)buf;
+       rc = smc_clc_wait_msg(new_smc, pclc, sizeof(*buf),
                              SMC_CLC_PROPOSAL, CLC_WAIT_TIME);
        if (rc)
                goto out_decl;
@@ -1309,90 +1654,69 @@ static void smc_listen_work(struct work_struct *work)
                goto out_decl;
        }
 
-       /* check for matching IP prefix and subnet length */
-       rc = smc_listen_prfx_check(new_smc, pclc);
-       if (rc)
+       ini = kzalloc(sizeof(*ini), GFP_KERNEL);
+       if (!ini) {
+               rc = SMC_CLC_DECL_MEM;
                goto out_decl;
+       }
 
-       /* get vlan id from IP device */
-       if (smc_vlan_by_tcpsk(new_smc->clcsock, &ini)) {
-               rc = SMC_CLC_DECL_GETVLANERR;
+       /* initial version checking */
+       rc = smc_listen_v2_check(new_smc, pclc, ini);
+       if (rc)
                goto out_decl;
-       }
 
        mutex_lock(&smc_server_lgr_pending);
        smc_close_init(new_smc);
        smc_rx_init(new_smc);
        smc_tx_init(new_smc);
 
-       /* check if ISM is available */
-       if (pclc->hdr.path == SMC_TYPE_D || pclc->hdr.path == SMC_TYPE_B) {
-               ini.is_smcd = true; /* prepare ISM check */
-               rc = smc_find_ism_device(new_smc, &ini);
-               if (!rc)
-                       rc = smc_listen_ism_init(new_smc, pclc, &ini);
-               if (!rc)
-                       ism_supported = true;
-               else if (pclc->hdr.path == SMC_TYPE_D)
-                       goto out_unlock; /* skip RDMA and decline */
-       }
-
-       /* check if RDMA is available */
-       if (!ism_supported) { /* SMC_TYPE_R or SMC_TYPE_B */
-               /* prepare RDMA check */
-               ini.is_smcd = false;
-               ini.ism_dev = NULL;
-               ini.ib_lcl = &pclc->lcl;
-               rc = smc_find_rdma_device(new_smc, &ini);
-               if (rc) {
-                       /* no RDMA device found */
-                       if (pclc->hdr.path == SMC_TYPE_B)
-                               /* neither ISM nor RDMA device found */
-                               rc = SMC_CLC_DECL_NOSMCDEV;
-                       goto out_unlock;
-               }
-               rc = smc_listen_rdma_init(new_smc, &ini);
-               if (rc)
-                       goto out_unlock;
-               rc = smc_listen_rdma_reg(new_smc, ini.cln_first_contact);
-               if (rc)
-                       goto out_unlock;
-       }
+       /* determine ISM or RoCE device used for connection */
+       rc = smc_listen_find_device(new_smc, pclc, ini);
+       if (rc)
+               goto out_unlock;
 
        /* send SMC Accept CLC message */
-       rc = smc_clc_send_accept(new_smc, ini.cln_first_contact);
+       rc = smc_clc_send_accept(new_smc, ini->first_contact_local,
+                                ini->smcd_version == SMC_V2 ? SMC_V2 : SMC_V1);
        if (rc)
                goto out_unlock;
 
        /* SMC-D does not need this lock any more */
-       if (ism_supported)
+       if (ini->is_smcd)
                mutex_unlock(&smc_server_lgr_pending);
 
        /* receive SMC Confirm CLC message */
-       rc = smc_clc_wait_msg(new_smc, &cclc, sizeof(cclc),
+       cclc2 = (struct smc_clc_msg_accept_confirm_v2 *)buf;
+       cclc = (struct smc_clc_msg_accept_confirm *)cclc2;
+       memset(buf, 0, sizeof(struct smc_clc_msg_proposal_area));
+       rc = smc_clc_wait_msg(new_smc, cclc2,
+                             sizeof(struct smc_clc_msg_proposal_area),
                              SMC_CLC_CONFIRM, CLC_WAIT_TIME);
        if (rc) {
-               if (!ism_supported)
+               if (!ini->is_smcd)
                        goto out_unlock;
                goto out_decl;
        }
 
        /* finish worker */
-       if (!ism_supported) {
-               rc = smc_listen_rdma_finish(new_smc, &cclc,
-                                           ini.cln_first_contact);
-               mutex_unlock(&smc_server_lgr_pending);
+       if (!ini->is_smcd) {
+               rc = smc_listen_rdma_finish(new_smc, cclc,
+                                           ini->first_contact_local);
                if (rc)
-                       return;
+                       goto out_unlock;
+               mutex_unlock(&smc_server_lgr_pending);
        }
-       smc_conn_save_peer_info(new_smc, &cclc);
+       smc_conn_save_peer_info(new_smc, cclc);
        smc_listen_out_connected(new_smc);
-       return;
+       goto out_free;
 
 out_unlock:
        mutex_unlock(&smc_server_lgr_pending);
 out_decl:
-       smc_listen_decline(new_smc, rc, ini.cln_first_contact);
+       smc_listen_decline(new_smc, rc, ini);
+out_free:
+       kfree(ini);
+       kfree(buf);
 }
 
 static void smc_tcp_listen_work(struct work_struct *work)
@@ -1406,7 +1730,7 @@ static void smc_tcp_listen_work(struct work_struct *work)
        lock_sock(lsk);
        while (lsk->sk_state == SMC_LISTEN) {
                rc = smc_clcsock_accept(lsmc, &new_smc);
-               if (rc)
+               if (rc) /* clcsock accept queue empty or error */
                        goto out;
                if (!new_smc)
                        continue;
@@ -1420,13 +1744,29 @@ static void smc_tcp_listen_work(struct work_struct *work)
                new_smc->sk.sk_sndbuf = lsmc->sk.sk_sndbuf;
                new_smc->sk.sk_rcvbuf = lsmc->sk.sk_rcvbuf;
                sock_hold(&new_smc->sk); /* sock_put in passive closing */
-               if (!schedule_work(&new_smc->smc_listen_work))
+               if (!queue_work(smc_hs_wq, &new_smc->smc_listen_work))
                        sock_put(&new_smc->sk);
        }
 
 out:
        release_sock(lsk);
-       sock_put(&lsmc->sk); /* sock_hold in smc_listen */
+       sock_put(&lsmc->sk); /* sock_hold in smc_clcsock_data_ready() */
+}
+
+static void smc_clcsock_data_ready(struct sock *listen_clcsock)
+{
+       struct smc_sock *lsmc;
+
+       lsmc = (struct smc_sock *)
+              ((uintptr_t)listen_clcsock->sk_user_data & ~SK_USER_DATA_NOCOPY);
+       if (!lsmc)
+               return;
+       lsmc->clcsk_data_ready(listen_clcsock);
+       if (lsmc->sk.sk_state == SMC_LISTEN) {
+               sock_hold(&lsmc->sk); /* sock_put in smc_tcp_listen_work() */
+               if (!queue_work(smc_hs_wq, &lsmc->tcp_listen_work))
+                       sock_put(&lsmc->sk);
+       }
 }
 
 static int smc_listen(struct socket *sock, int backlog)
@@ -1455,15 +1795,19 @@ static int smc_listen(struct socket *sock, int backlog)
        if (!smc->use_fallback)
                tcp_sk(smc->clcsock->sk)->syn_smc = 1;
 
+       /* save original sk_data_ready function and establish
+        * smc-specific sk_data_ready function
+        */
+       smc->clcsk_data_ready = smc->clcsock->sk->sk_data_ready;
+       smc->clcsock->sk->sk_data_ready = smc_clcsock_data_ready;
+       smc->clcsock->sk->sk_user_data =
+               (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
        rc = kernel_listen(smc->clcsock, backlog);
        if (rc)
                goto out;
        sk->sk_max_ack_backlog = backlog;
        sk->sk_ack_backlog = 0;
        sk->sk_state = SMC_LISTEN;
-       sock_hold(sk); /* sock_hold in tcp_listen_worker */
-       if (!schedule_work(&smc->tcp_listen_work))
-               sock_put(sk);
 
 out:
        release_sock(sk);
@@ -1788,8 +2132,8 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
                    sk->sk_state != SMC_LISTEN &&
                    sk->sk_state != SMC_CLOSED) {
                        if (val)
-                               mod_delayed_work(system_wq, &smc->conn.tx_work,
-                                                0);
+                               mod_delayed_work(smc->conn.lgr->tx_wq,
+                                                &smc->conn.tx_work, 0);
                }
                break;
        case TCP_CORK:
@@ -1797,8 +2141,8 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
                    sk->sk_state != SMC_LISTEN &&
                    sk->sk_state != SMC_CLOSED) {
                        if (!val)
-                               mod_delayed_work(system_wq, &smc->conn.tx_work,
-                                                0);
+                               mod_delayed_work(smc->conn.lgr->tx_wq,
+                                                &smc->conn.tx_work, 0);
                }
                break;
        case TCP_DEFER_ACCEPT:
@@ -2077,14 +2421,25 @@ static int __init smc_init(void)
        if (rc)
                return rc;
 
+       smc_ism_init();
+
        rc = smc_pnet_init();
        if (rc)
                goto out_pernet_subsys;
 
+       rc = -ENOMEM;
+       smc_hs_wq = alloc_workqueue("smc_hs_wq", 0, 0);
+       if (!smc_hs_wq)
+               goto out_pnet;
+
+       smc_close_wq = alloc_workqueue("smc_close_wq", 0, 0);
+       if (!smc_close_wq)
+               goto out_alloc_hs_wq;
+
        rc = smc_core_init();
        if (rc) {
                pr_err("%s: smc_core_init fails with %d\n", __func__, rc);
-               goto out_pnet;
+               goto out_alloc_wqs;
        }
 
        rc = smc_llc_init();
@@ -2136,6 +2491,10 @@ out_proto:
        proto_unregister(&smc_proto);
 out_core:
        smc_core_exit();
+out_alloc_wqs:
+       destroy_workqueue(smc_close_wq);
+out_alloc_hs_wq:
+       destroy_workqueue(smc_hs_wq);
 out_pnet:
        smc_pnet_exit();
 out_pernet_subsys:
@@ -2150,6 +2509,8 @@ static void __exit smc_exit(void)
        sock_unregister(PF_SMC);
        smc_core_exit();
        smc_ib_unregister_client();
+       destroy_workqueue(smc_close_wq);
+       destroy_workqueue(smc_hs_wq);
        proto_unregister(&smc_proto6);
        proto_unregister(&smc_proto);
        smc_pnet_exit();