Commit 8c3dca34 authored by Ursula Braun's avatar Ursula Braun Committed by David S. Miller
Browse files

net/smc: build and send V2 CLC proposal



The new format of an SMCD V2 CLC proposal is introduced, and
building and checking of SMCD V2 CLC proposals is adapted
accordingly.

Signed-off-by: default avatarUrsula Braun <ubraun@linux.ibm.com>
Signed-off-by: default avatarKarsten Graul <kgraul@linux.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d70bf4f7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1301,7 +1301,7 @@ static void smc_find_ism_device_serv(struct smc_sock *new_smc,
	if (!smcd_indicated(pclc->hdr.typev1))
		goto not_found;
	ini->is_smcd = true; /* prepare ISM check */
	ini->ism_peer_gid[0] = pclc_smcd->gid;
	ini->ism_peer_gid[0] = ntohll(pclc_smcd->ism.gid);
	if (smc_find_ism_device(new_smc, ini))
		goto not_found;
	if (!smc_listen_ism_init(new_smc, ini))
+6 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@

#define SMC_V1		1		/* SMC version V1 */
#define SMC_V2		2		/* SMC version V2 */
#define SMC_RELEASE	0

#define SMCPROTO_SMC		0	/* SMC protocol, IPv4 */
#define SMCPROTO_SMC6		1	/* SMC protocol, IPv6 */
@@ -28,6 +29,8 @@
					 * devices
					 */

#define SMC_MAX_EID_LEN		32

extern struct proto smc_proto;
extern struct proto smc_proto6;

@@ -251,6 +254,9 @@ extern struct workqueue_struct *smc_close_wq; /* wq for close work */

extern u8	local_systemid[SMC_SYSTEMID_LEN]; /* unique system identifier */

#define ntohll(x) be64_to_cpu(x)
#define htonll(x) cpu_to_be64(x)

/* convert an u32 value into network byte order, store it into a 3 byte field */
static inline void hton24(u8 *net, u32 host)
{
+135 −36
Original line number Diff line number Diff line
@@ -34,12 +34,52 @@ static const char SMC_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xd9'};
/* eye catcher "SMCD" EBCDIC for CLC messages */
static const char SMCD_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xc4'};

/* check arriving CLC proposal */
static bool smc_clc_msg_prop_valid(struct smc_clc_msg_proposal *pclc)
{
	struct smc_clc_msg_proposal_prefix *pclc_prfx;
	struct smc_clc_smcd_v2_extension *smcd_v2_ext;
	struct smc_clc_msg_hdr *hdr = &pclc->hdr;
	struct smc_clc_v2_extension *v2_ext;

	v2_ext = smc_get_clc_v2_ext(pclc);
	pclc_prfx = smc_clc_proposal_get_prefix(pclc);
	if (hdr->version == SMC_V1) {
		if (hdr->typev1 == SMC_TYPE_N)
			return false;
		if (ntohs(hdr->length) !=
			sizeof(*pclc) + ntohs(pclc->iparea_offset) +
			sizeof(*pclc_prfx) +
			pclc_prfx->ipv6_prefixes_cnt *
				sizeof(struct smc_clc_ipv6_prefix) +
			sizeof(struct smc_clc_msg_trail))
			return false;
	} else {
		if (ntohs(hdr->length) !=
			sizeof(*pclc) +
			sizeof(struct smc_clc_msg_smcd) +
			(hdr->typev1 != SMC_TYPE_N ?
				sizeof(*pclc_prfx) +
				pclc_prfx->ipv6_prefixes_cnt *
				sizeof(struct smc_clc_ipv6_prefix) : 0) +
			(hdr->typev2 != SMC_TYPE_N ?
				sizeof(*v2_ext) +
				v2_ext->hdr.eid_cnt * SMC_MAX_EID_LEN : 0) +
			(smcd_indicated(hdr->typev2) ?
				sizeof(*smcd_v2_ext) + v2_ext->hdr.ism_gid_cnt *
					sizeof(struct smc_clc_smcd_gid_chid) :
				0) +
			sizeof(struct smc_clc_msg_trail))
			return false;
	}
	return true;
}

/* check if received message has a correct header length and contains valid
 * heading and trailing eyecatchers
 */
static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm, bool check_trl)
{
	struct smc_clc_msg_proposal_prefix *pclc_prfx;
	struct smc_clc_msg_accept_confirm *clc;
	struct smc_clc_msg_proposal *pclc;
	struct smc_clc_msg_decline *dclc;
@@ -51,13 +91,7 @@ static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm, bool check_trl)
	switch (clcm->type) {
	case SMC_CLC_PROPOSAL:
		pclc = (struct smc_clc_msg_proposal *)clcm;
		pclc_prfx = smc_clc_proposal_get_prefix(pclc);
		if (ntohs(pclc->hdr.length) <
			sizeof(*pclc) + ntohs(pclc->iparea_offset) +
			sizeof(*pclc_prfx) +
			pclc_prfx->ipv6_prefixes_cnt *
				sizeof(struct smc_clc_ipv6_prefix) +
			sizeof(*trl))
		if (!smc_clc_msg_prop_valid(pclc))
			return false;
		trl = (struct smc_clc_msg_trail *)
			((u8 *)pclc + ntohs(pclc->hdr.length) - sizeof(*trl));
@@ -327,9 +361,6 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
		goto out;
	}

	if (clcm->type == SMC_CLC_PROPOSAL && clcm->typev1 == SMC_TYPE_N)
		reason_code = SMC_CLC_DECL_VERSMISMAT; /* just V2 offered */

	/* receive the complete CLC message */
	memset(&msg, 0, sizeof(struct msghdr));
	if (datlen > buflen) {
@@ -412,15 +443,18 @@ 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, struct smc_init_info *ini)
{
	struct smc_clc_smcd_v2_extension *smcd_v2_ext;
	struct smc_clc_msg_proposal_prefix *pclc_prfx;
	struct smc_clc_msg_proposal *pclc_base;
	struct smc_clc_smcd_gid_chid *gidchids;
	struct smc_clc_msg_proposal_area *pclc;
	struct smc_clc_ipv6_prefix *ipv6_prfx;
	struct smc_clc_v2_extension *v2_ext;
	struct smc_clc_msg_smcd *pclc_smcd;
	struct smc_clc_msg_trail *trl;
	int len, i, plen, rc;
	int reason_code = 0;
	struct kvec vec[5];
	struct kvec vec[8];
	struct msghdr msg;

	pclc = kzalloc(sizeof(*pclc), GFP_KERNEL);
@@ -431,24 +465,37 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini)
	pclc_smcd = &pclc->pclc_smcd;
	pclc_prfx = &pclc->pclc_prfx;
	ipv6_prfx = pclc->pclc_prfx_ipv6;
	v2_ext = &pclc->pclc_v2_ext;
	smcd_v2_ext = &pclc->pclc_smcd_v2_ext;
	gidchids = pclc->pclc_gidchids;
	trl = &pclc->pclc_trl;

	pclc_base->hdr.version = SMC_V2;
	pclc_base->hdr.typev1 = ini->smc_type_v1;
	pclc_base->hdr.typev2 = ini->smc_type_v2;
	plen = sizeof(*pclc_base) + sizeof(*pclc_smcd) + sizeof(*trl);

	/* retrieve ip prefixes for CLC proposal msg */
	if (ini->smc_type_v1 != SMC_TYPE_N) {
		rc = smc_clc_prfx_set(smc->clcsock, pclc_prfx, ipv6_prfx);
		if (rc) {
			if (ini->smc_type_v2 == SMC_TYPE_N) {
				kfree(pclc);
		return SMC_CLC_DECL_CNFERR; /* configuration error */
				return SMC_CLC_DECL_CNFERR;
			}
			pclc_base->hdr.typev1 = SMC_TYPE_N;
		} else {
			pclc_base->iparea_offset = htons(sizeof(*pclc_smcd));
			plen += sizeof(*pclc_prfx) +
					pclc_prfx->ipv6_prefixes_cnt *
					sizeof(ipv6_prfx[0]);
		}
	}

	/* send SMC Proposal CLC message */
	plen = sizeof(*pclc_base) + sizeof(*pclc_prfx) +
	       (pclc_prfx->ipv6_prefixes_cnt * sizeof(ipv6_prfx[0])) +
	       sizeof(*trl);
	/* build SMC Proposal CLC message */
	memcpy(pclc_base->hdr.eyecatcher, SMC_EYECATCHER,
	       sizeof(SMC_EYECATCHER));
	pclc_base->hdr.type = SMC_CLC_PROPOSAL;
	pclc_base->hdr.version = SMC_V1;		/* SMC version */
	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,
@@ -456,25 +503,65 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini)
		memcpy(pclc_base->lcl.gid, ini->ib_gid, SMC_GID_SIZE);
		memcpy(pclc_base->lcl.mac, &ini->ib_dev->mac[ini->ib_port - 1],
		       ETH_ALEN);
		pclc_base->iparea_offset = htons(0);
	}
	if (smcd_indicated(ini->smc_type_v1)) {
		/* add SMC-D specifics */
		plen += sizeof(*pclc_smcd);
		pclc_base->iparea_offset = htons(sizeof(*pclc_smcd));
		pclc_smcd->gid = ini->ism_dev[0]->local_gid;
		if (ini->ism_dev[0]) {
			pclc_smcd->ism.gid = htonll(ini->ism_dev[0]->local_gid);
			pclc_smcd->ism.chid =
				htons(smc_ism_get_chid(ini->ism_dev[0]));
		}
	pclc_base->hdr.length = htons(plen);
	}
	if (ini->smc_type_v2 == SMC_TYPE_N) {
		pclc_smcd->v2_ext_offset = 0;
	} else {
		u16 v2_ext_offset;
		u8 *eid = NULL;

		v2_ext_offset = sizeof(*pclc_smcd) -
			offsetofend(struct smc_clc_msg_smcd, v2_ext_offset);
		if (ini->smc_type_v1 != SMC_TYPE_N)
			v2_ext_offset += sizeof(*pclc_prfx) +
						pclc_prfx->ipv6_prefixes_cnt *
						sizeof(ipv6_prfx[0]);
		pclc_smcd->v2_ext_offset = htons(v2_ext_offset);
		v2_ext->hdr.eid_cnt = 0;
		v2_ext->hdr.ism_gid_cnt = ini->ism_offered_cnt;
		v2_ext->hdr.flag.release = SMC_RELEASE;
		v2_ext->hdr.flag.seid = 1;
		v2_ext->hdr.smcd_v2_ext_offset = htons(sizeof(*v2_ext) -
				offsetofend(struct smc_clnt_opts_area_hdr,
					    smcd_v2_ext_offset) +
				v2_ext->hdr.eid_cnt * SMC_MAX_EID_LEN);
		if (ini->ism_dev[0])
			smc_ism_get_system_eid(ini->ism_dev[0], &eid);
		else
			smc_ism_get_system_eid(ini->ism_dev[1], &eid);
		if (eid)
			memcpy(smcd_v2_ext->system_eid, eid, SMC_MAX_EID_LEN);
		plen += sizeof(*v2_ext) + sizeof(*smcd_v2_ext);
		if (ini->ism_offered_cnt) {
			for (i = 1; i <= ini->ism_offered_cnt; i++) {
				gidchids[i - 1].gid =
					htonll(ini->ism_dev[i]->local_gid);
				gidchids[i - 1].chid =
					htons(smc_ism_get_chid(ini->ism_dev[i]));
			}
			plen += ini->ism_offered_cnt *
				sizeof(struct smc_clc_smcd_gid_chid);
		}
	}
	pclc_base->hdr.length = htons(plen);
	memcpy(trl->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER));

	/* send SMC Proposal CLC message */
	memset(&msg, 0, sizeof(msg));
	i = 0;
	vec[i].iov_base = pclc_base;
	vec[i++].iov_len = sizeof(*pclc_base);
	if (smcd_indicated(ini->smc_type_v1)) {
	vec[i].iov_base = pclc_smcd;
	vec[i++].iov_len = sizeof(*pclc_smcd);
	}
	if (ini->smc_type_v1 != SMC_TYPE_N) {
		vec[i].iov_base = pclc_prfx;
		vec[i++].iov_len = sizeof(*pclc_prfx);
		if (pclc_prfx->ipv6_prefixes_cnt > 0) {
@@ -482,6 +569,18 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini)
			vec[i++].iov_len = pclc_prfx->ipv6_prefixes_cnt *
					   sizeof(ipv6_prfx[0]);
		}
	}
	if (ini->smc_type_v2 != SMC_TYPE_N) {
		vec[i].iov_base = v2_ext;
		vec[i++].iov_len = sizeof(*v2_ext);
		vec[i].iov_base = smcd_v2_ext;
		vec[i++].iov_len = sizeof(*smcd_v2_ext);
		if (ini->ism_offered_cnt) {
			vec[i].iov_base = gidchids;
			vec[i++].iov_len = ini->ism_offered_cnt *
					sizeof(struct smc_clc_smcd_gid_chid);
		}
	}
	vec[i].iov_base = trl;
	vec[i++].iov_len = sizeof(*trl);
	/* due to the few bytes needed for clc-handshake this cannot block */
+68 −5
Original line number Diff line number Diff line
@@ -81,8 +81,6 @@ struct smc_clc_msg_local { /* header2 of clc messages */
	u8 mac[6];		/* mac of ib_device port */
};

#define SMC_CLC_MAX_V6_PREFIX	8

/* Struct would be 4 byte aligned, but it is used in an array that is sent
 * to peers and must conform to RFC7609, hence we need to use packed here.
 */
@@ -91,6 +89,44 @@ struct smc_clc_ipv6_prefix {
	u8 prefix_len;
} __packed;			/* format defined in RFC7609 */

#if defined(__BIG_ENDIAN_BITFIELD)
struct smc_clc_v2_flag {
	u8 release : 4,
	   rsvd    : 3,
	   seid    : 1;
};
#elif defined(__LITTLE_ENDIAN_BITFIELD)
struct smc_clc_v2_flag {
	u8 seid   : 1,
	rsvd      : 3,
	release   : 4;
};
#endif

struct smc_clnt_opts_area_hdr {
	u8 eid_cnt;		/* number of user defined EIDs */
	u8 ism_gid_cnt;		/* number of ISMv2 GIDs */
	u8 reserved1;
	struct smc_clc_v2_flag flag;
	u8 reserved2[2];
	__be16 smcd_v2_ext_offset; /* SMC-Dv2 Extension Offset */
};

struct smc_clc_smcd_gid_chid {
	__be64 gid;		/* ISM GID */
	__be16 chid;		/* ISMv2 CHID */
} __packed;		/* format defined in
			 * IBM Shared Memory Communications Version 2
			 * (https://www.ibm.com/support/pages/node/6326337)
			 */

struct smc_clc_v2_extension {
	struct smc_clnt_opts_area_hdr hdr;
	u8 roce[16];		/* RoCEv2 GID */
	u8 reserved[16];
	u8 user_eids[0][SMC_MAX_EID_LEN];
};

struct smc_clc_msg_proposal_prefix {	/* prefix part of clc proposal message*/
	__be32 outgoing_subnet;	/* subnet mask */
	u8 prefix_len;		/* number of significant bits in mask */
@@ -99,8 +135,15 @@ struct smc_clc_msg_proposal_prefix { /* prefix part of clc proposal message*/
} __aligned(4);

struct smc_clc_msg_smcd {	/* SMC-D GID information */
	u64 gid;		/* ISM GID of requestor */
	u8 res[32];
	struct smc_clc_smcd_gid_chid ism; /* ISM native GID+CHID of requestor */
	__be16 v2_ext_offset;	/* SMC Version 2 Extension Offset */
	u8 reserved[28];
};

struct smc_clc_smcd_v2_extension {
	u8 system_eid[SMC_MAX_EID_LEN];
	u8 reserved[16];
	struct smc_clc_smcd_gid_chid gidchid[0];
};

struct smc_clc_msg_proposal {	/* clc proposal message sent by Linux */
@@ -109,11 +152,16 @@ struct smc_clc_msg_proposal { /* clc proposal message sent by Linux */
	__be16 iparea_offset;	/* offset to IP address information area */
} __aligned(4);

#define SMC_CLC_MAX_V6_PREFIX		8

struct smc_clc_msg_proposal_area {
	struct smc_clc_msg_proposal		pclc_base;
	struct smc_clc_msg_smcd			pclc_smcd;
	struct smc_clc_msg_proposal_prefix	pclc_prfx;
	struct smc_clc_ipv6_prefix	pclc_prfx_ipv6[SMC_CLC_MAX_V6_PREFIX];
	struct smc_clc_v2_extension		pclc_v2_ext;
	struct smc_clc_smcd_v2_extension	pclc_smcd_v2_ext;
	struct smc_clc_smcd_gid_chid		pclc_gidchids[SMC_MAX_ISM_DEVS];
	struct smc_clc_msg_trail		pclc_trl;
};

@@ -190,13 +238,28 @@ static inline bool smcd_indicated(int smc_type)
static inline struct smc_clc_msg_smcd *
smc_get_clc_msg_smcd(struct smc_clc_msg_proposal *prop)
{
	if (smcd_indicated(prop->hdr.type) &&
	if (smcd_indicated(prop->hdr.typev1) &&
	    ntohs(prop->iparea_offset) != sizeof(struct smc_clc_msg_smcd))
		return NULL;

	return (struct smc_clc_msg_smcd *)(prop + 1);
}

static inline struct smc_clc_v2_extension *
smc_get_clc_v2_ext(struct smc_clc_msg_proposal *prop)
{
	struct smc_clc_msg_smcd *prop_smcd = smc_get_clc_msg_smcd(prop);

	if (!prop_smcd || !ntohs(prop_smcd->v2_ext_offset))
		return NULL;

	return (struct smc_clc_v2_extension *)
	       ((u8 *)prop_smcd +
	       offsetof(struct smc_clc_msg_smcd, v2_ext_offset) +
	       sizeof(prop_smcd->v2_ext_offset) +
	       ntohs(prop_smcd->v2_ext_offset));
}

struct smcd_dev;
struct smc_init_info;