|
@@ -23,6 +23,7 @@
|
|
|
#include <linux/workqueue.h>
|
|
|
#include <linux/in.h>
|
|
|
#include <linux/sched/signal.h>
|
|
|
+#include <linux/if_vlan.h>
|
|
|
|
|
|
#include <net/sock.h>
|
|
|
#include <net/tcp.h>
|
|
@@ -35,6 +36,7 @@
|
|
|
#include "smc_cdc.h"
|
|
|
#include "smc_core.h"
|
|
|
#include "smc_ib.h"
|
|
|
+#include "smc_ism.h"
|
|
|
#include "smc_pnet.h"
|
|
|
#include "smc_tx.h"
|
|
|
#include "smc_rx.h"
|
|
@@ -372,8 +374,8 @@ static int smc_clnt_conf_first_link(struct smc_sock *smc)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void smc_conn_save_peer_info(struct smc_sock *smc,
|
|
|
- struct smc_clc_msg_accept_confirm *clc)
|
|
|
+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);
|
|
|
|
|
@@ -384,6 +386,28 @@ static void smc_conn_save_peer_info(struct smc_sock *smc,
|
|
|
smc->conn.tx_off = bufsize * (smc->conn.peer_rmbe_idx - 1);
|
|
|
}
|
|
|
|
|
|
+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);
|
|
|
+
|
|
|
+ smc->conn.peer_rmbe_idx = clc->dmbe_idx;
|
|
|
+ smc->conn.peer_token = clc->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);
|
|
|
+ smc->conn.tx_off = bufsize * smc->conn.peer_rmbe_idx;
|
|
|
+}
|
|
|
+
|
|
|
+static void smc_conn_save_peer_info(struct smc_sock *smc,
|
|
|
+ struct smc_clc_msg_accept_confirm *clc)
|
|
|
+{
|
|
|
+ if (smc->conn.lgr->is_smcd)
|
|
|
+ smcd_conn_save_peer_info(smc, clc);
|
|
|
+ else
|
|
|
+ smcr_conn_save_peer_info(smc, clc);
|
|
|
+}
|
|
|
+
|
|
|
static void smc_link_save_peer_info(struct smc_link *link,
|
|
|
struct smc_clc_msg_accept_confirm *clc)
|
|
|
{
|
|
@@ -450,15 +474,51 @@ static int smc_check_rdma(struct smc_sock *smc, struct smc_ib_device **ibdev,
|
|
|
return reason_code;
|
|
|
}
|
|
|
|
|
|
+/* check if there is an ISM device available for this connection. */
|
|
|
+/* called for connect and listen */
|
|
|
+static int smc_check_ism(struct smc_sock *smc, struct smcd_dev **ismdev)
|
|
|
+{
|
|
|
+ /* Find ISM device with same PNETID as connecting interface */
|
|
|
+ smc_pnet_find_ism_resource(smc->clcsock->sk, ismdev);
|
|
|
+ if (!(*ismdev))
|
|
|
+ return SMC_CLC_DECL_CNFERR; /* configuration error */
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 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 smcd_dev *ismdev,
|
|
|
+ unsigned short vlan_id)
|
|
|
+{
|
|
|
+ if (vlan_id && smc_ism_get_vlan(ismdev, vlan_id))
|
|
|
+ return SMC_CLC_DECL_CNFERR;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 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,
|
|
|
+ struct smcd_dev *ismdev,
|
|
|
+ unsigned short vlan_id)
|
|
|
+{
|
|
|
+ if (!is_smcd)
|
|
|
+ return 0;
|
|
|
+ if (vlan_id && smc_ism_put_vlan(ismdev, vlan_id))
|
|
|
+ return SMC_CLC_DECL_CNFERR;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/* CLC handshake during connect */
|
|
|
static int smc_connect_clc(struct smc_sock *smc, int smc_type,
|
|
|
struct smc_clc_msg_accept_confirm *aclc,
|
|
|
- struct smc_ib_device *ibdev, u8 ibport)
|
|
|
+ struct smc_ib_device *ibdev, u8 ibport,
|
|
|
+ struct smcd_dev *ismdev)
|
|
|
{
|
|
|
int rc = 0;
|
|
|
|
|
|
/* do inband token exchange */
|
|
|
- rc = smc_clc_send_proposal(smc, smc_type, ibdev, ibport, NULL);
|
|
|
+ rc = smc_clc_send_proposal(smc, smc_type, ibdev, ibport, ismdev);
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
/* receive SMC Accept CLC message */
|
|
@@ -538,11 +598,50 @@ static int smc_connect_rdma(struct smc_sock *smc,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* setup for ISM connection of client */
|
|
|
+static int smc_connect_ism(struct smc_sock *smc,
|
|
|
+ struct smc_clc_msg_accept_confirm *aclc,
|
|
|
+ struct smcd_dev *ismdev)
|
|
|
+{
|
|
|
+ int local_contact = SMC_FIRST_CONTACT;
|
|
|
+ int rc = 0;
|
|
|
+
|
|
|
+ mutex_lock(&smc_create_lgr_pending);
|
|
|
+ local_contact = smc_conn_create(smc, true, aclc->hdr.flag, NULL, 0,
|
|
|
+ NULL, ismdev, aclc->gid);
|
|
|
+ if (local_contact < 0)
|
|
|
+ return smc_connect_abort(smc, SMC_CLC_DECL_MEM, 0);
|
|
|
+
|
|
|
+ /* Create send and receive buffers */
|
|
|
+ if (smc_buf_create(smc, true))
|
|
|
+ return smc_connect_abort(smc, SMC_CLC_DECL_MEM, local_contact);
|
|
|
+
|
|
|
+ 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);
|
|
|
+ if (rc)
|
|
|
+ return smc_connect_abort(smc, rc, local_contact);
|
|
|
+ mutex_unlock(&smc_create_lgr_pending);
|
|
|
+
|
|
|
+ smc_copy_sock_settings_to_clc(smc);
|
|
|
+ if (smc->sk.sk_state == SMC_INIT)
|
|
|
+ smc->sk.sk_state = SMC_ACTIVE;
|
|
|
+
|
|
|
+ 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_ib_device *ibdev;
|
|
|
+ struct smcd_dev *ismdev;
|
|
|
+ unsigned short vlan;
|
|
|
+ int smc_type;
|
|
|
int rc = 0;
|
|
|
u8 ibport;
|
|
|
|
|
@@ -559,20 +658,52 @@ static int __smc_connect(struct smc_sock *smc)
|
|
|
if (using_ipsec(smc))
|
|
|
return smc_connect_decline_fallback(smc, SMC_CLC_DECL_IPSEC);
|
|
|
|
|
|
- /* check if a RDMA device is available; if not, fall back */
|
|
|
- if (smc_check_rdma(smc, &ibdev, &ibport))
|
|
|
+ /* check for VLAN ID */
|
|
|
+ if (smc_vlan_by_tcpsk(smc->clcsock, &vlan))
|
|
|
+ return smc_connect_decline_fallback(smc, SMC_CLC_DECL_CNFERR);
|
|
|
+
|
|
|
+ /* check if there is an ism device available */
|
|
|
+ if (!smc_check_ism(smc, &ismdev) &&
|
|
|
+ !smc_connect_ism_vlan_setup(smc, ismdev, vlan)) {
|
|
|
+ /* ISM is supported for this connection */
|
|
|
+ ism_supported = true;
|
|
|
+ smc_type = SMC_TYPE_D;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* check if there is a rdma device available */
|
|
|
+ if (!smc_check_rdma(smc, &ibdev, &ibport)) {
|
|
|
+ /* 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 */
|
|
|
+ }
|
|
|
+
|
|
|
+ /* if neither ISM nor RDMA are supported, fallback */
|
|
|
+ if (!rdma_supported && !ism_supported)
|
|
|
return smc_connect_decline_fallback(smc, SMC_CLC_DECL_CNFERR);
|
|
|
|
|
|
/* perform CLC handshake */
|
|
|
- rc = smc_connect_clc(smc, SMC_TYPE_R, &aclc, ibdev, ibport);
|
|
|
- if (rc)
|
|
|
+ rc = smc_connect_clc(smc, smc_type, &aclc, ibdev, ibport, ismdev);
|
|
|
+ if (rc) {
|
|
|
+ smc_connect_ism_vlan_cleanup(smc, ism_supported, ismdev, vlan);
|
|
|
return smc_connect_decline_fallback(smc, rc);
|
|
|
+ }
|
|
|
|
|
|
- /* connect using rdma */
|
|
|
- rc = smc_connect_rdma(smc, &aclc, ibdev, ibport);
|
|
|
- if (rc)
|
|
|
+ /* depending on previous steps, connect using rdma or ism */
|
|
|
+ if (rdma_supported && aclc.hdr.path == SMC_TYPE_R)
|
|
|
+ rc = smc_connect_rdma(smc, &aclc, ibdev, ibport);
|
|
|
+ else if (ism_supported && aclc.hdr.path == SMC_TYPE_D)
|
|
|
+ rc = smc_connect_ism(smc, &aclc, ismdev);
|
|
|
+ else
|
|
|
+ rc = SMC_CLC_DECL_CNFERR;
|
|
|
+ if (rc) {
|
|
|
+ smc_connect_ism_vlan_cleanup(smc, ism_supported, ismdev, vlan);
|
|
|
return smc_connect_decline_fallback(smc, rc);
|
|
|
+ }
|
|
|
|
|
|
+ smc_connect_ism_vlan_cleanup(smc, ism_supported, ismdev, vlan);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -909,6 +1040,44 @@ static int smc_listen_rdma_init(struct smc_sock *new_smc,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* 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 smcd_dev *ismdev,
|
|
|
+ int *local_contact)
|
|
|
+{
|
|
|
+ struct smc_clc_msg_smcd *pclc_smcd;
|
|
|
+
|
|
|
+ pclc_smcd = smc_get_clc_msg_smcd(pclc);
|
|
|
+ *local_contact = smc_conn_create(new_smc, true, 0, NULL, 0, NULL,
|
|
|
+ ismdev, pclc_smcd->gid);
|
|
|
+ if (*local_contact < 0) {
|
|
|
+ if (*local_contact == -ENOMEM)
|
|
|
+ return SMC_CLC_DECL_MEM;/* insufficient memory*/
|
|
|
+ return SMC_CLC_DECL_INTERR; /* other error */
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 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 (*local_contact == SMC_FIRST_CONTACT)
|
|
|
+ smc_lgr_forget(new_smc->conn.lgr);
|
|
|
+ smc_conn_free(&new_smc->conn);
|
|
|
+ return SMC_CLC_DECL_CNFERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Create send and receive buffers */
|
|
|
+ if (smc_buf_create(new_smc, true)) {
|
|
|
+ if (*local_contact == SMC_FIRST_CONTACT)
|
|
|
+ smc_lgr_forget(new_smc->conn.lgr);
|
|
|
+ smc_conn_free(&new_smc->conn);
|
|
|
+ return SMC_CLC_DECL_MEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/* listen worker: register buffers */
|
|
|
static int smc_listen_rdma_reg(struct smc_sock *new_smc, int local_contact)
|
|
|
{
|
|
@@ -967,6 +1136,8 @@ static void smc_listen_work(struct work_struct *work)
|
|
|
struct smc_clc_msg_accept_confirm cclc;
|
|
|
struct smc_clc_msg_proposal *pclc;
|
|
|
struct smc_ib_device *ibdev;
|
|
|
+ bool ism_supported = false;
|
|
|
+ struct smcd_dev *ismdev;
|
|
|
u8 buf[SMC_CLC_MAX_LEN];
|
|
|
int local_contact = 0;
|
|
|
int reason_code = 0;
|
|
@@ -1007,13 +1178,21 @@ static void smc_listen_work(struct work_struct *work)
|
|
|
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) &&
|
|
|
+ !smc_check_ism(new_smc, &ismdev) &&
|
|
|
+ !smc_listen_ism_init(new_smc, pclc, ismdev, &local_contact)) {
|
|
|
+ ism_supported = true;
|
|
|
+ }
|
|
|
+
|
|
|
/* check if RDMA is available */
|
|
|
- if ((pclc->hdr.path != SMC_TYPE_R && pclc->hdr.path != SMC_TYPE_B) ||
|
|
|
- smc_check_rdma(new_smc, &ibdev, &ibport) ||
|
|
|
- smc_listen_rdma_check(new_smc, pclc) ||
|
|
|
- smc_listen_rdma_init(new_smc, pclc, ibdev, ibport,
|
|
|
- &local_contact) ||
|
|
|
- smc_listen_rdma_reg(new_smc, local_contact)) {
|
|
|
+ if (!ism_supported &&
|
|
|
+ ((pclc->hdr.path != SMC_TYPE_R && pclc->hdr.path != SMC_TYPE_B) ||
|
|
|
+ smc_check_rdma(new_smc, &ibdev, &ibport) ||
|
|
|
+ smc_listen_rdma_check(new_smc, pclc) ||
|
|
|
+ smc_listen_rdma_init(new_smc, pclc, ibdev, ibport,
|
|
|
+ &local_contact) ||
|
|
|
+ smc_listen_rdma_reg(new_smc, local_contact))) {
|
|
|
/* SMC not supported, decline */
|
|
|
mutex_unlock(&smc_create_lgr_pending);
|
|
|
smc_listen_decline(new_smc, SMC_CLC_DECL_CNFERR, local_contact);
|
|
@@ -1038,7 +1217,8 @@ static void smc_listen_work(struct work_struct *work)
|
|
|
}
|
|
|
|
|
|
/* finish worker */
|
|
|
- smc_listen_rdma_finish(new_smc, &cclc, local_contact);
|
|
|
+ if (!ism_supported)
|
|
|
+ smc_listen_rdma_finish(new_smc, &cclc, local_contact);
|
|
|
smc_conn_save_peer_info(new_smc, &cclc);
|
|
|
mutex_unlock(&smc_create_lgr_pending);
|
|
|
smc_listen_out_connected(new_smc);
|