forked from mirrors/linux
		
	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: Ursula Braun <ubraun@linux.ibm.com> Signed-off-by: Karsten Graul <kgraul@linux.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									d70bf4f7a9
								
							
						
					
					
						commit
						8c3dca341a
					
				
					 4 changed files with 210 additions and 42 deletions
				
			
		|  | @ -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)) | ||||
|  |  | |||
|  | @ -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) | ||||
| { | ||||
|  |  | |||
|  | @ -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 */ | ||||
| 	rc = smc_clc_prfx_set(smc->clcsock, pclc_prfx, ipv6_prfx); | ||||
| 	if (rc) { | ||||
| 		kfree(pclc); | ||||
| 		return SMC_CLC_DECL_CNFERR; /* configuration error */ | ||||
| 	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; | ||||
| 			} | ||||
| 			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,31 +503,83 @@ 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])); | ||||
| 		} | ||||
| 	} | ||||
| 	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); | ||||
| 	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) { | ||||
| 			vec[i].iov_base = ipv6_prfx; | ||||
| 			vec[i++].iov_len = pclc_prfx->ipv6_prefixes_cnt * | ||||
| 					   sizeof(ipv6_prfx[0]); | ||||
| 		} | ||||
| 	} | ||||
| 	vec[i].iov_base = pclc_prfx; | ||||
| 	vec[i++].iov_len = sizeof(*pclc_prfx); | ||||
| 	if (pclc_prfx->ipv6_prefixes_cnt > 0) { | ||||
| 		vec[i].iov_base = ipv6_prfx; | ||||
| 		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); | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Ursula Braun
						Ursula Braun