xref: /openbmc/linux/net/smc/smc_core.c (revision 55e43d6abd078ed6d219902ce8cb4d68e3c993ba)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
20cfdd8f9SUrsula Braun /*
30cfdd8f9SUrsula Braun  *  Shared Memory Communications over RDMA (SMC-R) and RoCE
40cfdd8f9SUrsula Braun  *
50cfdd8f9SUrsula Braun  *  Basic Transport Functions exploiting Infiniband API
60cfdd8f9SUrsula Braun  *
70cfdd8f9SUrsula Braun  *  Copyright IBM Corp. 2016
80cfdd8f9SUrsula Braun  *
90cfdd8f9SUrsula Braun  *  Author(s):  Ursula Braun <ubraun@linux.vnet.ibm.com>
100cfdd8f9SUrsula Braun  */
110cfdd8f9SUrsula Braun 
120cfdd8f9SUrsula Braun #include <linux/socket.h>
130cfdd8f9SUrsula Braun #include <linux/if_vlan.h>
140cfdd8f9SUrsula Braun #include <linux/random.h>
150cfdd8f9SUrsula Braun #include <linux/workqueue.h>
166dabd405SUrsula Braun #include <linux/wait.h>
17a33a803cSUrsula Braun #include <linux/reboot.h>
1892f3cb0eSUrsula Braun #include <linux/mutex.h>
19099b990bSGuvenc Gulce #include <linux/list.h>
20099b990bSGuvenc Gulce #include <linux/smc.h>
210cfdd8f9SUrsula Braun #include <net/tcp.h>
220cfdd8f9SUrsula Braun #include <net/sock.h>
230cfdd8f9SUrsula Braun #include <rdma/ib_verbs.h>
24ddb457c6SParav Pandit #include <rdma/ib_cache.h>
250cfdd8f9SUrsula Braun 
260cfdd8f9SUrsula Braun #include "smc.h"
270cfdd8f9SUrsula Braun #include "smc_clc.h"
280cfdd8f9SUrsula Braun #include "smc_core.h"
290cfdd8f9SUrsula Braun #include "smc_ib.h"
30f38ba179SUrsula Braun #include "smc_wr.h"
319bf9abeaSUrsula Braun #include "smc_llc.h"
325f08318fSUrsula Braun #include "smc_cdc.h"
33b38d7324SUrsula Braun #include "smc_close.h"
34c6ba7c9bSHans Wippel #include "smc_ism.h"
35099b990bSGuvenc Gulce #include "smc_netlink.h"
36e0e4b8faSGuvenc Gulce #include "smc_stats.h"
37a3a0e81bSTony Lu #include "smc_tracepoint.h"
380cfdd8f9SUrsula Braun 
399bf9abeaSUrsula Braun #define SMC_LGR_NUM_INCR		256
405bc11ddbSUrsula Braun #define SMC_LGR_FREE_DELAY_SERV		(600 * HZ)
417f58a1adSUrsula Braun #define SMC_LGR_FREE_DELAY_CLNT		(SMC_LGR_FREE_DELAY_SERV + 10 * HZ)
420cfdd8f9SUrsula Braun 
43a3db10efSGuvenc Gulce struct smc_lgr_list smc_lgr_list = {	/* established link groups */
449fda3510SHans Wippel 	.lock = __SPIN_LOCK_UNLOCKED(smc_lgr_list.lock),
459fda3510SHans Wippel 	.list = LIST_HEAD_INIT(smc_lgr_list.list),
469fda3510SHans Wippel 	.num = 0,
479fda3510SHans Wippel };
489bf9abeaSUrsula Braun 
4929115cefSUrsula Braun static atomic_t lgr_cnt = ATOMIC_INIT(0); /* number of existing link groups */
506dabd405SUrsula Braun static DECLARE_WAIT_QUEUE_HEAD(lgrs_deleted);
516dabd405SUrsula Braun 
526511aad3SHans Wippel static void smc_buf_free(struct smc_link_group *lgr, bool is_rmb,
536511aad3SHans Wippel 			 struct smc_buf_desc *buf_desc);
545f78fe96SKarsten Graul static void __smc_lgr_terminate(struct smc_link_group *lgr, bool soft);
55a6920d1dSKarsten Graul 
56541afa10SKarsten Graul static void smc_link_down_work(struct work_struct *work);
571f90a05dSKarsten Graul 
58a0a62ee1SUrsula Braun /* return head of link group list and its lock for a given link group */
smc_lgr_list_head(struct smc_link_group * lgr,spinlock_t ** lgr_lock)59a0a62ee1SUrsula Braun static inline struct list_head *smc_lgr_list_head(struct smc_link_group *lgr,
60a0a62ee1SUrsula Braun 						  spinlock_t **lgr_lock)
61a0a62ee1SUrsula Braun {
62a0a62ee1SUrsula Braun 	if (lgr->is_smcd) {
63a0a62ee1SUrsula Braun 		*lgr_lock = &lgr->smcd->lgr_lock;
64a0a62ee1SUrsula Braun 		return &lgr->smcd->lgr_list;
65a0a62ee1SUrsula Braun 	}
66a0a62ee1SUrsula Braun 
67a0a62ee1SUrsula Braun 	*lgr_lock = &smc_lgr_list.lock;
68a0a62ee1SUrsula Braun 	return &smc_lgr_list.list;
69a0a62ee1SUrsula Braun }
70a0a62ee1SUrsula Braun 
smc_ibdev_cnt_inc(struct smc_link * lnk)71ddc99286SGuvenc Gulce static void smc_ibdev_cnt_inc(struct smc_link *lnk)
72ddc99286SGuvenc Gulce {
73ddc99286SGuvenc Gulce 	atomic_inc(&lnk->smcibdev->lnk_cnt_by_port[lnk->ibport - 1]);
74ddc99286SGuvenc Gulce }
75ddc99286SGuvenc Gulce 
smc_ibdev_cnt_dec(struct smc_link * lnk)76ddc99286SGuvenc Gulce static void smc_ibdev_cnt_dec(struct smc_link *lnk)
77ddc99286SGuvenc Gulce {
78ddc99286SGuvenc Gulce 	atomic_dec(&lnk->smcibdev->lnk_cnt_by_port[lnk->ibport - 1]);
79ddc99286SGuvenc Gulce }
80ddc99286SGuvenc Gulce 
smc_lgr_schedule_free_work(struct smc_link_group * lgr)8197cdbc42SKarsten Graul static void smc_lgr_schedule_free_work(struct smc_link_group *lgr)
8297cdbc42SKarsten Graul {
8397cdbc42SKarsten Graul 	/* client link group creation always follows the server link group
8497cdbc42SKarsten Graul 	 * creation. For client use a somewhat higher removal delay time,
8597cdbc42SKarsten Graul 	 * otherwise there is a risk of out-of-sync link groups.
8697cdbc42SKarsten Graul 	 */
87f9aab6f2SUrsula Braun 	if (!lgr->freeing) {
8897cdbc42SKarsten Graul 		mod_delayed_work(system_wq, &lgr->free_work,
89c6ba7c9bSHans Wippel 				 (!lgr->is_smcd && lgr->role == SMC_CLNT) ?
908e316b9eSUrsula Braun 						SMC_LGR_FREE_DELAY_CLNT :
918e316b9eSUrsula Braun 						SMC_LGR_FREE_DELAY_SERV);
928e316b9eSUrsula Braun 	}
9397cdbc42SKarsten Graul }
9497cdbc42SKarsten Graul 
950cfdd8f9SUrsula Braun /* Register connection's alert token in our lookup structure.
960cfdd8f9SUrsula Braun  * To use rbtrees we have to implement our own insert core.
970cfdd8f9SUrsula Braun  * Requires @conns_lock
980cfdd8f9SUrsula Braun  * @smc		connection to register
990cfdd8f9SUrsula Braun  * Returns 0 on success, != otherwise.
1000cfdd8f9SUrsula Braun  */
smc_lgr_add_alert_token(struct smc_connection * conn)1010cfdd8f9SUrsula Braun static void smc_lgr_add_alert_token(struct smc_connection *conn)
1020cfdd8f9SUrsula Braun {
1030cfdd8f9SUrsula Braun 	struct rb_node **link, *parent = NULL;
1040cfdd8f9SUrsula Braun 	u32 token = conn->alert_token_local;
1050cfdd8f9SUrsula Braun 
1060cfdd8f9SUrsula Braun 	link = &conn->lgr->conns_all.rb_node;
1070cfdd8f9SUrsula Braun 	while (*link) {
1080cfdd8f9SUrsula Braun 		struct smc_connection *cur = rb_entry(*link,
1090cfdd8f9SUrsula Braun 					struct smc_connection, alert_node);
1100cfdd8f9SUrsula Braun 
1110cfdd8f9SUrsula Braun 		parent = *link;
1120cfdd8f9SUrsula Braun 		if (cur->alert_token_local > token)
1130cfdd8f9SUrsula Braun 			link = &parent->rb_left;
1140cfdd8f9SUrsula Braun 		else
1150cfdd8f9SUrsula Braun 			link = &parent->rb_right;
1160cfdd8f9SUrsula Braun 	}
1170cfdd8f9SUrsula Braun 	/* Put the new node there */
1180cfdd8f9SUrsula Braun 	rb_link_node(&conn->alert_node, parent, link);
1190cfdd8f9SUrsula Braun 	rb_insert_color(&conn->alert_node, &conn->lgr->conns_all);
1200cfdd8f9SUrsula Braun }
1210cfdd8f9SUrsula Braun 
12256bc3b20SKarsten Graul /* assign an SMC-R link to the connection */
smcr_lgr_conn_assign_link(struct smc_connection * conn,bool first)12356bc3b20SKarsten Graul static int smcr_lgr_conn_assign_link(struct smc_connection *conn, bool first)
12456bc3b20SKarsten Graul {
12556bc3b20SKarsten Graul 	enum smc_link_state expected = first ? SMC_LNK_ACTIVATING :
12656bc3b20SKarsten Graul 				       SMC_LNK_ACTIVE;
12756bc3b20SKarsten Graul 	int i, j;
12856bc3b20SKarsten Graul 
12956bc3b20SKarsten Graul 	/* do link balancing */
13035112271SWen Gu 	conn->lnk = NULL;	/* reset conn->lnk first */
13156bc3b20SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
13256bc3b20SKarsten Graul 		struct smc_link *lnk = &conn->lgr->lnk[i];
13356bc3b20SKarsten Graul 
134ad6c111bSKarsten Graul 		if (lnk->state != expected || lnk->link_is_asym)
13556bc3b20SKarsten Graul 			continue;
13656bc3b20SKarsten Graul 		if (conn->lgr->role == SMC_CLNT) {
13756bc3b20SKarsten Graul 			conn->lnk = lnk; /* temporary, SMC server assigns link*/
13856bc3b20SKarsten Graul 			break;
13956bc3b20SKarsten Graul 		}
14056bc3b20SKarsten Graul 		if (conn->lgr->conns_num % 2) {
14156bc3b20SKarsten Graul 			for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) {
14256bc3b20SKarsten Graul 				struct smc_link *lnk2;
14356bc3b20SKarsten Graul 
14456bc3b20SKarsten Graul 				lnk2 = &conn->lgr->lnk[j];
145ad6c111bSKarsten Graul 				if (lnk2->state == expected &&
146ad6c111bSKarsten Graul 				    !lnk2->link_is_asym) {
14756bc3b20SKarsten Graul 					conn->lnk = lnk2;
14856bc3b20SKarsten Graul 					break;
14956bc3b20SKarsten Graul 				}
15056bc3b20SKarsten Graul 			}
15156bc3b20SKarsten Graul 		}
15256bc3b20SKarsten Graul 		if (!conn->lnk)
15356bc3b20SKarsten Graul 			conn->lnk = lnk;
15456bc3b20SKarsten Graul 		break;
15556bc3b20SKarsten Graul 	}
15656bc3b20SKarsten Graul 	if (!conn->lnk)
15756bc3b20SKarsten Graul 		return SMC_CLC_DECL_NOACTLINK;
15807d51580SGuvenc Gulce 	atomic_inc(&conn->lnk->conn_cnt);
15956bc3b20SKarsten Graul 	return 0;
16056bc3b20SKarsten Graul }
16156bc3b20SKarsten Graul 
1620cfdd8f9SUrsula Braun /* Register connection in link group by assigning an alert token
1630cfdd8f9SUrsula Braun  * registered in a search tree.
1640cfdd8f9SUrsula Braun  * Requires @conns_lock
1650cfdd8f9SUrsula Braun  * Note that '0' is a reserved value and not assigned.
1660cfdd8f9SUrsula Braun  */
smc_lgr_register_conn(struct smc_connection * conn,bool first)16756bc3b20SKarsten Graul static int smc_lgr_register_conn(struct smc_connection *conn, bool first)
1680cfdd8f9SUrsula Braun {
1690cfdd8f9SUrsula Braun 	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
1700cfdd8f9SUrsula Braun 	static atomic_t nexttoken = ATOMIC_INIT(0);
17156bc3b20SKarsten Graul 	int rc;
1720cfdd8f9SUrsula Braun 
17356bc3b20SKarsten Graul 	if (!conn->lgr->is_smcd) {
17456bc3b20SKarsten Graul 		rc = smcr_lgr_conn_assign_link(conn, first);
17536595d8aSWen Gu 		if (rc) {
17636595d8aSWen Gu 			conn->lgr = NULL;
17756bc3b20SKarsten Graul 			return rc;
17856bc3b20SKarsten Graul 		}
17936595d8aSWen Gu 	}
1800cfdd8f9SUrsula Braun 	/* find a new alert_token_local value not yet used by some connection
1810cfdd8f9SUrsula Braun 	 * in this link group
1820cfdd8f9SUrsula Braun 	 */
1830cfdd8f9SUrsula Braun 	sock_hold(&smc->sk); /* sock_put in smc_lgr_unregister_conn() */
1840cfdd8f9SUrsula Braun 	while (!conn->alert_token_local) {
1850cfdd8f9SUrsula Braun 		conn->alert_token_local = atomic_inc_return(&nexttoken);
1860cfdd8f9SUrsula Braun 		if (smc_lgr_find_conn(conn->alert_token_local, conn->lgr))
1870cfdd8f9SUrsula Braun 			conn->alert_token_local = 0;
1880cfdd8f9SUrsula Braun 	}
1890cfdd8f9SUrsula Braun 	smc_lgr_add_alert_token(conn);
1900cfdd8f9SUrsula Braun 	conn->lgr->conns_num++;
191b9247544SKarsten Graul 	return 0;
1920cfdd8f9SUrsula Braun }
1930cfdd8f9SUrsula Braun 
1940cfdd8f9SUrsula Braun /* Unregister connection and reset the alert token of the given connection<
1950cfdd8f9SUrsula Braun  */
__smc_lgr_unregister_conn(struct smc_connection * conn)1960cfdd8f9SUrsula Braun static void __smc_lgr_unregister_conn(struct smc_connection *conn)
1970cfdd8f9SUrsula Braun {
1980cfdd8f9SUrsula Braun 	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
1990cfdd8f9SUrsula Braun 	struct smc_link_group *lgr = conn->lgr;
2000cfdd8f9SUrsula Braun 
2010cfdd8f9SUrsula Braun 	rb_erase(&conn->alert_node, &lgr->conns_all);
20207d51580SGuvenc Gulce 	if (conn->lnk)
20307d51580SGuvenc Gulce 		atomic_dec(&conn->lnk->conn_cnt);
2040cfdd8f9SUrsula Braun 	lgr->conns_num--;
2050cfdd8f9SUrsula Braun 	conn->alert_token_local = 0;
2060cfdd8f9SUrsula Braun 	sock_put(&smc->sk); /* sock_hold in smc_lgr_register_conn() */
2070cfdd8f9SUrsula Braun }
2080cfdd8f9SUrsula Braun 
209fb692ec4SKarsten Graul /* Unregister connection from lgr
2100cfdd8f9SUrsula Braun  */
smc_lgr_unregister_conn(struct smc_connection * conn)2110cfdd8f9SUrsula Braun static void smc_lgr_unregister_conn(struct smc_connection *conn)
2120cfdd8f9SUrsula Braun {
2130cfdd8f9SUrsula Braun 	struct smc_link_group *lgr = conn->lgr;
2140cfdd8f9SUrsula Braun 
215ea89c6c0SWen Gu 	if (!smc_conn_lgr_valid(conn))
21677f838acSKarsten Graul 		return;
2170cfdd8f9SUrsula Braun 	write_lock_bh(&lgr->conns_lock);
2180cfdd8f9SUrsula Braun 	if (conn->alert_token_local) {
2190cfdd8f9SUrsula Braun 		__smc_lgr_unregister_conn(conn);
2200cfdd8f9SUrsula Braun 	}
2210cfdd8f9SUrsula Braun 	write_unlock_bh(&lgr->conns_lock);
2220cfdd8f9SUrsula Braun }
2230cfdd8f9SUrsula Braun 
smc_nl_get_sys_info(struct sk_buff * skb,struct netlink_callback * cb)224099b990bSGuvenc Gulce int smc_nl_get_sys_info(struct sk_buff *skb, struct netlink_callback *cb)
225099b990bSGuvenc Gulce {
226099b990bSGuvenc Gulce 	struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb);
227099b990bSGuvenc Gulce 	char hostname[SMC_MAX_HOSTNAME_LEN + 1];
228099b990bSGuvenc Gulce 	char smc_seid[SMC_MAX_EID_LEN + 1];
229099b990bSGuvenc Gulce 	struct nlattr *attrs;
230099b990bSGuvenc Gulce 	u8 *seid = NULL;
231099b990bSGuvenc Gulce 	u8 *host = NULL;
232099b990bSGuvenc Gulce 	void *nlh;
233099b990bSGuvenc Gulce 
234099b990bSGuvenc Gulce 	nlh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
235099b990bSGuvenc Gulce 			  &smc_gen_nl_family, NLM_F_MULTI,
236099b990bSGuvenc Gulce 			  SMC_NETLINK_GET_SYS_INFO);
237099b990bSGuvenc Gulce 	if (!nlh)
238099b990bSGuvenc Gulce 		goto errmsg;
239099b990bSGuvenc Gulce 	if (cb_ctx->pos[0])
240099b990bSGuvenc Gulce 		goto errout;
241099b990bSGuvenc Gulce 	attrs = nla_nest_start(skb, SMC_GEN_SYS_INFO);
242099b990bSGuvenc Gulce 	if (!attrs)
243099b990bSGuvenc Gulce 		goto errout;
244099b990bSGuvenc Gulce 	if (nla_put_u8(skb, SMC_NLA_SYS_VER, SMC_V2))
245099b990bSGuvenc Gulce 		goto errattr;
246099b990bSGuvenc Gulce 	if (nla_put_u8(skb, SMC_NLA_SYS_REL, SMC_RELEASE))
247099b990bSGuvenc Gulce 		goto errattr;
248099b990bSGuvenc Gulce 	if (nla_put_u8(skb, SMC_NLA_SYS_IS_ISM_V2, smc_ism_is_v2_capable()))
249099b990bSGuvenc Gulce 		goto errattr;
250b0539f5eSKarsten Graul 	if (nla_put_u8(skb, SMC_NLA_SYS_IS_SMCR_V2, true))
251b0539f5eSKarsten Graul 		goto errattr;
252099b990bSGuvenc Gulce 	smc_clc_get_hostname(&host);
253099b990bSGuvenc Gulce 	if (host) {
25425fe2c9cSJakub Kicinski 		memcpy(hostname, host, SMC_MAX_HOSTNAME_LEN);
25525fe2c9cSJakub Kicinski 		hostname[SMC_MAX_HOSTNAME_LEN] = 0;
256099b990bSGuvenc Gulce 		if (nla_put_string(skb, SMC_NLA_SYS_LOCAL_HOST, hostname))
257099b990bSGuvenc Gulce 			goto errattr;
258099b990bSGuvenc Gulce 	}
25911a26c59SKarsten Graul 	if (smc_ism_is_v2_capable()) {
26011a26c59SKarsten Graul 		smc_ism_get_system_eid(&seid);
2618a446536SGuvenc Gulce 		memcpy(smc_seid, seid, SMC_MAX_EID_LEN);
2628a446536SGuvenc Gulce 		smc_seid[SMC_MAX_EID_LEN] = 0;
263099b990bSGuvenc Gulce 		if (nla_put_string(skb, SMC_NLA_SYS_SEID, smc_seid))
264099b990bSGuvenc Gulce 			goto errattr;
265099b990bSGuvenc Gulce 	}
266099b990bSGuvenc Gulce 	nla_nest_end(skb, attrs);
267099b990bSGuvenc Gulce 	genlmsg_end(skb, nlh);
268099b990bSGuvenc Gulce 	cb_ctx->pos[0] = 1;
269099b990bSGuvenc Gulce 	return skb->len;
270099b990bSGuvenc Gulce 
271099b990bSGuvenc Gulce errattr:
272099b990bSGuvenc Gulce 	nla_nest_cancel(skb, attrs);
273099b990bSGuvenc Gulce errout:
274099b990bSGuvenc Gulce 	genlmsg_cancel(skb, nlh);
275099b990bSGuvenc Gulce errmsg:
276099b990bSGuvenc Gulce 	return skb->len;
277099b990bSGuvenc Gulce }
278099b990bSGuvenc Gulce 
279b0539f5eSKarsten Graul /* Fill SMC_NLA_LGR_D_V2_COMMON/SMC_NLA_LGR_R_V2_COMMON nested attributes */
smc_nl_fill_lgr_v2_common(struct smc_link_group * lgr,struct sk_buff * skb,struct netlink_callback * cb,struct nlattr * v2_attrs)280b0539f5eSKarsten Graul static int smc_nl_fill_lgr_v2_common(struct smc_link_group *lgr,
281b0539f5eSKarsten Graul 				     struct sk_buff *skb,
282b0539f5eSKarsten Graul 				     struct netlink_callback *cb,
283b0539f5eSKarsten Graul 				     struct nlattr *v2_attrs)
284b0539f5eSKarsten Graul {
285b0539f5eSKarsten Graul 	char smc_host[SMC_MAX_HOSTNAME_LEN + 1];
286b0539f5eSKarsten Graul 	char smc_eid[SMC_MAX_EID_LEN + 1];
287b0539f5eSKarsten Graul 
288b0539f5eSKarsten Graul 	if (nla_put_u8(skb, SMC_NLA_LGR_V2_VER, lgr->smc_version))
289b0539f5eSKarsten Graul 		goto errv2attr;
290b0539f5eSKarsten Graul 	if (nla_put_u8(skb, SMC_NLA_LGR_V2_REL, lgr->peer_smc_release))
291b0539f5eSKarsten Graul 		goto errv2attr;
292b0539f5eSKarsten Graul 	if (nla_put_u8(skb, SMC_NLA_LGR_V2_OS, lgr->peer_os))
293b0539f5eSKarsten Graul 		goto errv2attr;
294b0539f5eSKarsten Graul 	memcpy(smc_host, lgr->peer_hostname, SMC_MAX_HOSTNAME_LEN);
295b0539f5eSKarsten Graul 	smc_host[SMC_MAX_HOSTNAME_LEN] = 0;
296b0539f5eSKarsten Graul 	if (nla_put_string(skb, SMC_NLA_LGR_V2_PEER_HOST, smc_host))
297b0539f5eSKarsten Graul 		goto errv2attr;
298b0539f5eSKarsten Graul 	memcpy(smc_eid, lgr->negotiated_eid, SMC_MAX_EID_LEN);
299b0539f5eSKarsten Graul 	smc_eid[SMC_MAX_EID_LEN] = 0;
300b0539f5eSKarsten Graul 	if (nla_put_string(skb, SMC_NLA_LGR_V2_NEG_EID, smc_eid))
301b0539f5eSKarsten Graul 		goto errv2attr;
302b0539f5eSKarsten Graul 
303b0539f5eSKarsten Graul 	nla_nest_end(skb, v2_attrs);
304b0539f5eSKarsten Graul 	return 0;
305b0539f5eSKarsten Graul 
306b0539f5eSKarsten Graul errv2attr:
307b0539f5eSKarsten Graul 	nla_nest_cancel(skb, v2_attrs);
308b0539f5eSKarsten Graul 	return -EMSGSIZE;
309b0539f5eSKarsten Graul }
310b0539f5eSKarsten Graul 
smc_nl_fill_smcr_lgr_v2(struct smc_link_group * lgr,struct sk_buff * skb,struct netlink_callback * cb)311b0539f5eSKarsten Graul static int smc_nl_fill_smcr_lgr_v2(struct smc_link_group *lgr,
312b0539f5eSKarsten Graul 				   struct sk_buff *skb,
313b0539f5eSKarsten Graul 				   struct netlink_callback *cb)
314b0539f5eSKarsten Graul {
315b0539f5eSKarsten Graul 	struct nlattr *v2_attrs;
316b0539f5eSKarsten Graul 
317b0539f5eSKarsten Graul 	v2_attrs = nla_nest_start(skb, SMC_NLA_LGR_R_V2);
318b0539f5eSKarsten Graul 	if (!v2_attrs)
319b0539f5eSKarsten Graul 		goto errattr;
320b0539f5eSKarsten Graul 	if (nla_put_u8(skb, SMC_NLA_LGR_R_V2_DIRECT, !lgr->uses_gateway))
321b0539f5eSKarsten Graul 		goto errv2attr;
322bbed596cSGuangguan Wang 	if (nla_put_u8(skb, SMC_NLA_LGR_R_V2_MAX_CONNS, lgr->max_conns))
323bbed596cSGuangguan Wang 		goto errv2attr;
324bbed596cSGuangguan Wang 	if (nla_put_u8(skb, SMC_NLA_LGR_R_V2_MAX_LINKS, lgr->max_links))
325bbed596cSGuangguan Wang 		goto errv2attr;
326b0539f5eSKarsten Graul 
327b0539f5eSKarsten Graul 	nla_nest_end(skb, v2_attrs);
328b0539f5eSKarsten Graul 	return 0;
329b0539f5eSKarsten Graul 
330b0539f5eSKarsten Graul errv2attr:
331b0539f5eSKarsten Graul 	nla_nest_cancel(skb, v2_attrs);
332b0539f5eSKarsten Graul errattr:
333b0539f5eSKarsten Graul 	return -EMSGSIZE;
334b0539f5eSKarsten Graul }
335b0539f5eSKarsten Graul 
smc_nl_fill_lgr(struct smc_link_group * lgr,struct sk_buff * skb,struct netlink_callback * cb)336e9b8c845SGuvenc Gulce static int smc_nl_fill_lgr(struct smc_link_group *lgr,
337e9b8c845SGuvenc Gulce 			   struct sk_buff *skb,
338e9b8c845SGuvenc Gulce 			   struct netlink_callback *cb)
339e9b8c845SGuvenc Gulce {
340e9b8c845SGuvenc Gulce 	char smc_target[SMC_MAX_PNETID_LEN + 1];
341b0539f5eSKarsten Graul 	struct nlattr *attrs, *v2_attrs;
342e9b8c845SGuvenc Gulce 
343e9b8c845SGuvenc Gulce 	attrs = nla_nest_start(skb, SMC_GEN_LGR_SMCR);
344e9b8c845SGuvenc Gulce 	if (!attrs)
345e9b8c845SGuvenc Gulce 		goto errout;
346e9b8c845SGuvenc Gulce 
347e9b8c845SGuvenc Gulce 	if (nla_put_u32(skb, SMC_NLA_LGR_R_ID, *((u32 *)&lgr->id)))
348e9b8c845SGuvenc Gulce 		goto errattr;
349e9b8c845SGuvenc Gulce 	if (nla_put_u32(skb, SMC_NLA_LGR_R_CONNS_NUM, lgr->conns_num))
350e9b8c845SGuvenc Gulce 		goto errattr;
351e9b8c845SGuvenc Gulce 	if (nla_put_u8(skb, SMC_NLA_LGR_R_ROLE, lgr->role))
352e9b8c845SGuvenc Gulce 		goto errattr;
353e9b8c845SGuvenc Gulce 	if (nla_put_u8(skb, SMC_NLA_LGR_R_TYPE, lgr->type))
354e9b8c845SGuvenc Gulce 		goto errattr;
355ddefb2d2SWen Gu 	if (nla_put_u8(skb, SMC_NLA_LGR_R_BUF_TYPE, lgr->buf_type))
356ddefb2d2SWen Gu 		goto errattr;
357e9b8c845SGuvenc Gulce 	if (nla_put_u8(skb, SMC_NLA_LGR_R_VLAN_ID, lgr->vlan_id))
358e9b8c845SGuvenc Gulce 		goto errattr;
35979d39fc5STony Lu 	if (nla_put_u64_64bit(skb, SMC_NLA_LGR_R_NET_COOKIE,
36079d39fc5STony Lu 			      lgr->net->net_cookie, SMC_NLA_LGR_R_PAD))
36179d39fc5STony Lu 		goto errattr;
3628a446536SGuvenc Gulce 	memcpy(smc_target, lgr->pnet_id, SMC_MAX_PNETID_LEN);
3638a446536SGuvenc Gulce 	smc_target[SMC_MAX_PNETID_LEN] = 0;
364e9b8c845SGuvenc Gulce 	if (nla_put_string(skb, SMC_NLA_LGR_R_PNETID, smc_target))
365e9b8c845SGuvenc Gulce 		goto errattr;
366b0539f5eSKarsten Graul 	if (lgr->smc_version > SMC_V1) {
367b0539f5eSKarsten Graul 		v2_attrs = nla_nest_start(skb, SMC_NLA_LGR_R_V2_COMMON);
368b0539f5eSKarsten Graul 		if (!v2_attrs)
369b0539f5eSKarsten Graul 			goto errattr;
370b0539f5eSKarsten Graul 		if (smc_nl_fill_lgr_v2_common(lgr, skb, cb, v2_attrs))
371b0539f5eSKarsten Graul 			goto errattr;
372b0539f5eSKarsten Graul 		if (smc_nl_fill_smcr_lgr_v2(lgr, skb, cb))
373b0539f5eSKarsten Graul 			goto errattr;
374b0539f5eSKarsten Graul 	}
375e9b8c845SGuvenc Gulce 
376e9b8c845SGuvenc Gulce 	nla_nest_end(skb, attrs);
377e9b8c845SGuvenc Gulce 	return 0;
378e9b8c845SGuvenc Gulce errattr:
379e9b8c845SGuvenc Gulce 	nla_nest_cancel(skb, attrs);
380e9b8c845SGuvenc Gulce errout:
381e9b8c845SGuvenc Gulce 	return -EMSGSIZE;
382e9b8c845SGuvenc Gulce }
383e9b8c845SGuvenc Gulce 
smc_nl_fill_lgr_link(struct smc_link_group * lgr,struct smc_link * link,struct sk_buff * skb,struct netlink_callback * cb)3845a7e09d5SGuvenc Gulce static int smc_nl_fill_lgr_link(struct smc_link_group *lgr,
3855a7e09d5SGuvenc Gulce 				struct smc_link *link,
386e9b8c845SGuvenc Gulce 				struct sk_buff *skb,
387e9b8c845SGuvenc Gulce 				struct netlink_callback *cb)
388e9b8c845SGuvenc Gulce {
3898a446536SGuvenc Gulce 	char smc_ibname[IB_DEVICE_NAME_MAX];
3905a7e09d5SGuvenc Gulce 	u8 smc_gid_target[41];
3915a7e09d5SGuvenc Gulce 	struct nlattr *attrs;
3925a7e09d5SGuvenc Gulce 	u32 link_uid = 0;
393e9b8c845SGuvenc Gulce 	void *nlh;
394e9b8c845SGuvenc Gulce 
395e9b8c845SGuvenc Gulce 	nlh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
396e9b8c845SGuvenc Gulce 			  &smc_gen_nl_family, NLM_F_MULTI,
3975a7e09d5SGuvenc Gulce 			  SMC_NETLINK_GET_LINK_SMCR);
3985a7e09d5SGuvenc Gulce 	if (!nlh)
3995a7e09d5SGuvenc Gulce 		goto errmsg;
4005a7e09d5SGuvenc Gulce 
4015a7e09d5SGuvenc Gulce 	attrs = nla_nest_start(skb, SMC_GEN_LINK_SMCR);
4025a7e09d5SGuvenc Gulce 	if (!attrs)
4035a7e09d5SGuvenc Gulce 		goto errout;
4045a7e09d5SGuvenc Gulce 
4055a7e09d5SGuvenc Gulce 	if (nla_put_u8(skb, SMC_NLA_LINK_ID, link->link_id))
4065a7e09d5SGuvenc Gulce 		goto errattr;
4075a7e09d5SGuvenc Gulce 	if (nla_put_u32(skb, SMC_NLA_LINK_STATE, link->state))
4085a7e09d5SGuvenc Gulce 		goto errattr;
4095a7e09d5SGuvenc Gulce 	if (nla_put_u32(skb, SMC_NLA_LINK_CONN_CNT,
4105a7e09d5SGuvenc Gulce 			atomic_read(&link->conn_cnt)))
4115a7e09d5SGuvenc Gulce 		goto errattr;
4125a7e09d5SGuvenc Gulce 	if (nla_put_u8(skb, SMC_NLA_LINK_IB_PORT, link->ibport))
4135a7e09d5SGuvenc Gulce 		goto errattr;
4145a7e09d5SGuvenc Gulce 	if (nla_put_u32(skb, SMC_NLA_LINK_NET_DEV, link->ndev_ifidx))
4155a7e09d5SGuvenc Gulce 		goto errattr;
4165a7e09d5SGuvenc Gulce 	snprintf(smc_ibname, sizeof(smc_ibname), "%s", link->ibname);
4175a7e09d5SGuvenc Gulce 	if (nla_put_string(skb, SMC_NLA_LINK_IB_DEV, smc_ibname))
4185a7e09d5SGuvenc Gulce 		goto errattr;
4195a7e09d5SGuvenc Gulce 	memcpy(&link_uid, link->link_uid, sizeof(link_uid));
4205a7e09d5SGuvenc Gulce 	if (nla_put_u32(skb, SMC_NLA_LINK_UID, link_uid))
4215a7e09d5SGuvenc Gulce 		goto errattr;
4225a7e09d5SGuvenc Gulce 	memcpy(&link_uid, link->peer_link_uid, sizeof(link_uid));
4235a7e09d5SGuvenc Gulce 	if (nla_put_u32(skb, SMC_NLA_LINK_PEER_UID, link_uid))
4245a7e09d5SGuvenc Gulce 		goto errattr;
4255a7e09d5SGuvenc Gulce 	memset(smc_gid_target, 0, sizeof(smc_gid_target));
4265a7e09d5SGuvenc Gulce 	smc_gid_be16_convert(smc_gid_target, link->gid);
4275a7e09d5SGuvenc Gulce 	if (nla_put_string(skb, SMC_NLA_LINK_GID, smc_gid_target))
4285a7e09d5SGuvenc Gulce 		goto errattr;
4295a7e09d5SGuvenc Gulce 	memset(smc_gid_target, 0, sizeof(smc_gid_target));
4305a7e09d5SGuvenc Gulce 	smc_gid_be16_convert(smc_gid_target, link->peer_gid);
4315a7e09d5SGuvenc Gulce 	if (nla_put_string(skb, SMC_NLA_LINK_PEER_GID, smc_gid_target))
4325a7e09d5SGuvenc Gulce 		goto errattr;
4335a7e09d5SGuvenc Gulce 
4345a7e09d5SGuvenc Gulce 	nla_nest_end(skb, attrs);
4355a7e09d5SGuvenc Gulce 	genlmsg_end(skb, nlh);
4365a7e09d5SGuvenc Gulce 	return 0;
4375a7e09d5SGuvenc Gulce errattr:
4385a7e09d5SGuvenc Gulce 	nla_nest_cancel(skb, attrs);
4395a7e09d5SGuvenc Gulce errout:
4405a7e09d5SGuvenc Gulce 	genlmsg_cancel(skb, nlh);
4415a7e09d5SGuvenc Gulce errmsg:
4425a7e09d5SGuvenc Gulce 	return -EMSGSIZE;
4435a7e09d5SGuvenc Gulce }
4445a7e09d5SGuvenc Gulce 
smc_nl_handle_lgr(struct smc_link_group * lgr,struct sk_buff * skb,struct netlink_callback * cb,bool list_links)4455a7e09d5SGuvenc Gulce static int smc_nl_handle_lgr(struct smc_link_group *lgr,
4465a7e09d5SGuvenc Gulce 			     struct sk_buff *skb,
4475a7e09d5SGuvenc Gulce 			     struct netlink_callback *cb,
4485a7e09d5SGuvenc Gulce 			     bool list_links)
4495a7e09d5SGuvenc Gulce {
4505a7e09d5SGuvenc Gulce 	void *nlh;
4515a7e09d5SGuvenc Gulce 	int i;
4525a7e09d5SGuvenc Gulce 
4535a7e09d5SGuvenc Gulce 	nlh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
4545a7e09d5SGuvenc Gulce 			  &smc_gen_nl_family, NLM_F_MULTI,
455e9b8c845SGuvenc Gulce 			  SMC_NETLINK_GET_LGR_SMCR);
456e9b8c845SGuvenc Gulce 	if (!nlh)
457e9b8c845SGuvenc Gulce 		goto errmsg;
458e9b8c845SGuvenc Gulce 	if (smc_nl_fill_lgr(lgr, skb, cb))
459e9b8c845SGuvenc Gulce 		goto errout;
460e9b8c845SGuvenc Gulce 
461e9b8c845SGuvenc Gulce 	genlmsg_end(skb, nlh);
4625a7e09d5SGuvenc Gulce 	if (!list_links)
4635a7e09d5SGuvenc Gulce 		goto out;
4645a7e09d5SGuvenc Gulce 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
4655a7e09d5SGuvenc Gulce 		if (!smc_link_usable(&lgr->lnk[i]))
4665a7e09d5SGuvenc Gulce 			continue;
4675a7e09d5SGuvenc Gulce 		if (smc_nl_fill_lgr_link(lgr, &lgr->lnk[i], skb, cb))
4685a7e09d5SGuvenc Gulce 			goto errout;
4695a7e09d5SGuvenc Gulce 	}
4705a7e09d5SGuvenc Gulce out:
471e9b8c845SGuvenc Gulce 	return 0;
472e9b8c845SGuvenc Gulce 
473e9b8c845SGuvenc Gulce errout:
474e9b8c845SGuvenc Gulce 	genlmsg_cancel(skb, nlh);
475e9b8c845SGuvenc Gulce errmsg:
476e9b8c845SGuvenc Gulce 	return -EMSGSIZE;
477e9b8c845SGuvenc Gulce }
478e9b8c845SGuvenc Gulce 
smc_nl_fill_lgr_list(struct smc_lgr_list * smc_lgr,struct sk_buff * skb,struct netlink_callback * cb,bool list_links)479e9b8c845SGuvenc Gulce static void smc_nl_fill_lgr_list(struct smc_lgr_list *smc_lgr,
480e9b8c845SGuvenc Gulce 				 struct sk_buff *skb,
4815a7e09d5SGuvenc Gulce 				 struct netlink_callback *cb,
4825a7e09d5SGuvenc Gulce 				 bool list_links)
483e9b8c845SGuvenc Gulce {
484e9b8c845SGuvenc Gulce 	struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb);
485e9b8c845SGuvenc Gulce 	struct smc_link_group *lgr;
486e9b8c845SGuvenc Gulce 	int snum = cb_ctx->pos[0];
487e9b8c845SGuvenc Gulce 	int num = 0;
488e9b8c845SGuvenc Gulce 
489e9b8c845SGuvenc Gulce 	spin_lock_bh(&smc_lgr->lock);
490e9b8c845SGuvenc Gulce 	list_for_each_entry(lgr, &smc_lgr->list, list) {
491e9b8c845SGuvenc Gulce 		if (num < snum)
492e9b8c845SGuvenc Gulce 			goto next;
4935a7e09d5SGuvenc Gulce 		if (smc_nl_handle_lgr(lgr, skb, cb, list_links))
494e9b8c845SGuvenc Gulce 			goto errout;
495e9b8c845SGuvenc Gulce next:
496e9b8c845SGuvenc Gulce 		num++;
497e9b8c845SGuvenc Gulce 	}
498e9b8c845SGuvenc Gulce errout:
499e9b8c845SGuvenc Gulce 	spin_unlock_bh(&smc_lgr->lock);
500e9b8c845SGuvenc Gulce 	cb_ctx->pos[0] = num;
501e9b8c845SGuvenc Gulce }
502e9b8c845SGuvenc Gulce 
smc_nl_fill_smcd_lgr(struct smc_link_group * lgr,struct sk_buff * skb,struct netlink_callback * cb)5038f9dde4bSGuvenc Gulce static int smc_nl_fill_smcd_lgr(struct smc_link_group *lgr,
5048f9dde4bSGuvenc Gulce 				struct sk_buff *skb,
5058f9dde4bSGuvenc Gulce 				struct netlink_callback *cb)
5068f9dde4bSGuvenc Gulce {
5078f9dde4bSGuvenc Gulce 	char smc_pnet[SMC_MAX_PNETID_LEN + 1];
5089de4df7bSStefan Raspl 	struct smcd_dev *smcd = lgr->smcd;
5097e5ef8ebSWen Gu 	struct smcd_gid smcd_gid;
5108f9dde4bSGuvenc Gulce 	struct nlattr *attrs;
5118f9dde4bSGuvenc Gulce 	void *nlh;
5128f9dde4bSGuvenc Gulce 
5138f9dde4bSGuvenc Gulce 	nlh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
5148f9dde4bSGuvenc Gulce 			  &smc_gen_nl_family, NLM_F_MULTI,
5158f9dde4bSGuvenc Gulce 			  SMC_NETLINK_GET_LGR_SMCD);
5168f9dde4bSGuvenc Gulce 	if (!nlh)
5178f9dde4bSGuvenc Gulce 		goto errmsg;
5188f9dde4bSGuvenc Gulce 
5198f9dde4bSGuvenc Gulce 	attrs = nla_nest_start(skb, SMC_GEN_LGR_SMCD);
5208f9dde4bSGuvenc Gulce 	if (!attrs)
5218f9dde4bSGuvenc Gulce 		goto errout;
5228f9dde4bSGuvenc Gulce 
5238f9dde4bSGuvenc Gulce 	if (nla_put_u32(skb, SMC_NLA_LGR_D_ID, *((u32 *)&lgr->id)))
5248f9dde4bSGuvenc Gulce 		goto errattr;
5257e5ef8ebSWen Gu 	smcd->ops->get_local_gid(smcd, &smcd_gid);
5269de4df7bSStefan Raspl 	if (nla_put_u64_64bit(skb, SMC_NLA_LGR_D_GID,
5277e5ef8ebSWen Gu 			      smcd_gid.gid, SMC_NLA_LGR_D_PAD))
5288f9dde4bSGuvenc Gulce 		goto errattr;
5297e5ef8ebSWen Gu 	if (nla_put_u64_64bit(skb, SMC_NLA_LGR_D_PEER_GID, lgr->peer_gid.gid,
5308f9dde4bSGuvenc Gulce 			      SMC_NLA_LGR_D_PAD))
5318f9dde4bSGuvenc Gulce 		goto errattr;
5328f9dde4bSGuvenc Gulce 	if (nla_put_u8(skb, SMC_NLA_LGR_D_VLAN_ID, lgr->vlan_id))
5338f9dde4bSGuvenc Gulce 		goto errattr;
5348f9dde4bSGuvenc Gulce 	if (nla_put_u32(skb, SMC_NLA_LGR_D_CONNS_NUM, lgr->conns_num))
5358f9dde4bSGuvenc Gulce 		goto errattr;
5368f9dde4bSGuvenc Gulce 	if (nla_put_u32(skb, SMC_NLA_LGR_D_CHID, smc_ism_get_chid(lgr->smcd)))
5378f9dde4bSGuvenc Gulce 		goto errattr;
5388a446536SGuvenc Gulce 	memcpy(smc_pnet, lgr->smcd->pnetid, SMC_MAX_PNETID_LEN);
5398a446536SGuvenc Gulce 	smc_pnet[SMC_MAX_PNETID_LEN] = 0;
5408f9dde4bSGuvenc Gulce 	if (nla_put_string(skb, SMC_NLA_LGR_D_PNETID, smc_pnet))
5418f9dde4bSGuvenc Gulce 		goto errattr;
542b0539f5eSKarsten Graul 	if (lgr->smc_version > SMC_V1) {
543b0539f5eSKarsten Graul 		struct nlattr *v2_attrs;
5448f9dde4bSGuvenc Gulce 
545b0539f5eSKarsten Graul 		v2_attrs = nla_nest_start(skb, SMC_NLA_LGR_D_V2_COMMON);
5468f9dde4bSGuvenc Gulce 		if (!v2_attrs)
5478f9dde4bSGuvenc Gulce 			goto errattr;
548b0539f5eSKarsten Graul 		if (smc_nl_fill_lgr_v2_common(lgr, skb, cb, v2_attrs))
549b0539f5eSKarsten Graul 			goto errattr;
550b0539f5eSKarsten Graul 	}
5518f9dde4bSGuvenc Gulce 	nla_nest_end(skb, attrs);
5528f9dde4bSGuvenc Gulce 	genlmsg_end(skb, nlh);
5538f9dde4bSGuvenc Gulce 	return 0;
5548f9dde4bSGuvenc Gulce 
5558f9dde4bSGuvenc Gulce errattr:
5568f9dde4bSGuvenc Gulce 	nla_nest_cancel(skb, attrs);
5578f9dde4bSGuvenc Gulce errout:
5588f9dde4bSGuvenc Gulce 	genlmsg_cancel(skb, nlh);
5598f9dde4bSGuvenc Gulce errmsg:
5608f9dde4bSGuvenc Gulce 	return -EMSGSIZE;
5618f9dde4bSGuvenc Gulce }
5628f9dde4bSGuvenc Gulce 
smc_nl_handle_smcd_lgr(struct smcd_dev * dev,struct sk_buff * skb,struct netlink_callback * cb)5638f9dde4bSGuvenc Gulce static int smc_nl_handle_smcd_lgr(struct smcd_dev *dev,
5648f9dde4bSGuvenc Gulce 				  struct sk_buff *skb,
5658f9dde4bSGuvenc Gulce 				  struct netlink_callback *cb)
5668f9dde4bSGuvenc Gulce {
5678f9dde4bSGuvenc Gulce 	struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb);
5688f9dde4bSGuvenc Gulce 	struct smc_link_group *lgr;
5698f9dde4bSGuvenc Gulce 	int snum = cb_ctx->pos[1];
5708f9dde4bSGuvenc Gulce 	int rc = 0, num = 0;
5718f9dde4bSGuvenc Gulce 
5728f9dde4bSGuvenc Gulce 	spin_lock_bh(&dev->lgr_lock);
5738f9dde4bSGuvenc Gulce 	list_for_each_entry(lgr, &dev->lgr_list, list) {
5748f9dde4bSGuvenc Gulce 		if (!lgr->is_smcd)
5758f9dde4bSGuvenc Gulce 			continue;
5768f9dde4bSGuvenc Gulce 		if (num < snum)
5778f9dde4bSGuvenc Gulce 			goto next;
5788f9dde4bSGuvenc Gulce 		rc = smc_nl_fill_smcd_lgr(lgr, skb, cb);
5798f9dde4bSGuvenc Gulce 		if (rc)
5808f9dde4bSGuvenc Gulce 			goto errout;
5818f9dde4bSGuvenc Gulce next:
5828f9dde4bSGuvenc Gulce 		num++;
5838f9dde4bSGuvenc Gulce 	}
5848f9dde4bSGuvenc Gulce errout:
5858f9dde4bSGuvenc Gulce 	spin_unlock_bh(&dev->lgr_lock);
5868f9dde4bSGuvenc Gulce 	cb_ctx->pos[1] = num;
5878f9dde4bSGuvenc Gulce 	return rc;
5888f9dde4bSGuvenc Gulce }
5898f9dde4bSGuvenc Gulce 
smc_nl_fill_smcd_dev(struct smcd_dev_list * dev_list,struct sk_buff * skb,struct netlink_callback * cb)5908f9dde4bSGuvenc Gulce static int smc_nl_fill_smcd_dev(struct smcd_dev_list *dev_list,
5918f9dde4bSGuvenc Gulce 				struct sk_buff *skb,
5928f9dde4bSGuvenc Gulce 				struct netlink_callback *cb)
5938f9dde4bSGuvenc Gulce {
5948f9dde4bSGuvenc Gulce 	struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb);
5958f9dde4bSGuvenc Gulce 	struct smcd_dev *smcd_dev;
5968f9dde4bSGuvenc Gulce 	int snum = cb_ctx->pos[0];
5978f9dde4bSGuvenc Gulce 	int rc = 0, num = 0;
5988f9dde4bSGuvenc Gulce 
5998f9dde4bSGuvenc Gulce 	mutex_lock(&dev_list->mutex);
6008f9dde4bSGuvenc Gulce 	list_for_each_entry(smcd_dev, &dev_list->list, list) {
6018f9dde4bSGuvenc Gulce 		if (list_empty(&smcd_dev->lgr_list))
6028f9dde4bSGuvenc Gulce 			continue;
6038f9dde4bSGuvenc Gulce 		if (num < snum)
6048f9dde4bSGuvenc Gulce 			goto next;
6058f9dde4bSGuvenc Gulce 		rc = smc_nl_handle_smcd_lgr(smcd_dev, skb, cb);
6068f9dde4bSGuvenc Gulce 		if (rc)
6078f9dde4bSGuvenc Gulce 			goto errout;
6088f9dde4bSGuvenc Gulce next:
6098f9dde4bSGuvenc Gulce 		num++;
6108f9dde4bSGuvenc Gulce 	}
6118f9dde4bSGuvenc Gulce errout:
6128f9dde4bSGuvenc Gulce 	mutex_unlock(&dev_list->mutex);
6138f9dde4bSGuvenc Gulce 	cb_ctx->pos[0] = num;
6148f9dde4bSGuvenc Gulce 	return rc;
6158f9dde4bSGuvenc Gulce }
6168f9dde4bSGuvenc Gulce 
smcr_nl_get_lgr(struct sk_buff * skb,struct netlink_callback * cb)617e9b8c845SGuvenc Gulce int smcr_nl_get_lgr(struct sk_buff *skb, struct netlink_callback *cb)
618e9b8c845SGuvenc Gulce {
6195a7e09d5SGuvenc Gulce 	bool list_links = false;
6205a7e09d5SGuvenc Gulce 
6215a7e09d5SGuvenc Gulce 	smc_nl_fill_lgr_list(&smc_lgr_list, skb, cb, list_links);
6225a7e09d5SGuvenc Gulce 	return skb->len;
6235a7e09d5SGuvenc Gulce }
6245a7e09d5SGuvenc Gulce 
smcr_nl_get_link(struct sk_buff * skb,struct netlink_callback * cb)6255a7e09d5SGuvenc Gulce int smcr_nl_get_link(struct sk_buff *skb, struct netlink_callback *cb)
6265a7e09d5SGuvenc Gulce {
6275a7e09d5SGuvenc Gulce 	bool list_links = true;
6285a7e09d5SGuvenc Gulce 
6295a7e09d5SGuvenc Gulce 	smc_nl_fill_lgr_list(&smc_lgr_list, skb, cb, list_links);
630e9b8c845SGuvenc Gulce 	return skb->len;
631e9b8c845SGuvenc Gulce }
632e9b8c845SGuvenc Gulce 
smcd_nl_get_lgr(struct sk_buff * skb,struct netlink_callback * cb)6338f9dde4bSGuvenc Gulce int smcd_nl_get_lgr(struct sk_buff *skb, struct netlink_callback *cb)
6348f9dde4bSGuvenc Gulce {
6358f9dde4bSGuvenc Gulce 	smc_nl_fill_smcd_dev(&smcd_dev_list, skb, cb);
6368f9dde4bSGuvenc Gulce 	return skb->len;
6378f9dde4bSGuvenc Gulce }
6388f9dde4bSGuvenc Gulce 
smc_lgr_cleanup_early(struct smc_link_group * lgr)63936595d8aSWen Gu void smc_lgr_cleanup_early(struct smc_link_group *lgr)
64051e3dfa8SUrsula Braun {
6419ec6bf19SKarsten Graul 	spinlock_t *lgr_lock;
64251e3dfa8SUrsula Braun 
64351e3dfa8SUrsula Braun 	if (!lgr)
64451e3dfa8SUrsula Braun 		return;
64551e3dfa8SUrsula Braun 
646789b6cc2SDust Li 	smc_lgr_list_head(lgr, &lgr_lock);
6479ec6bf19SKarsten Graul 	spin_lock_bh(lgr_lock);
6489ec6bf19SKarsten Graul 	/* do not use this link group for new connections */
649789b6cc2SDust Li 	if (!list_empty(&lgr->list))
650789b6cc2SDust Li 		list_del_init(&lgr->list);
6519ec6bf19SKarsten Graul 	spin_unlock_bh(lgr_lock);
652f9aab6f2SUrsula Braun 	__smc_lgr_terminate(lgr, true);
65351e3dfa8SUrsula Braun }
65451e3dfa8SUrsula Braun 
smcr_lgr_link_deactivate_all(struct smc_link_group * lgr)655a52bcc91SKarsten Graul static void smcr_lgr_link_deactivate_all(struct smc_link_group *lgr)
656a52bcc91SKarsten Graul {
657a52bcc91SKarsten Graul 	int i;
658a52bcc91SKarsten Graul 
659a52bcc91SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
660a52bcc91SKarsten Graul 		struct smc_link *lnk = &lgr->lnk[i];
661a52bcc91SKarsten Graul 
66290cee52fSDust Li 		if (smc_link_sendable(lnk))
663a52bcc91SKarsten Graul 			lnk->state = SMC_LNK_INACTIVE;
664a52bcc91SKarsten Graul 	}
6656778a6beSKarsten Graul 	wake_up_all(&lgr->llc_msg_waiter);
6666778a6beSKarsten Graul 	wake_up_all(&lgr->llc_flow_waiter);
667a52bcc91SKarsten Graul }
668a52bcc91SKarsten Graul 
6693f3f0e36SUrsula Braun static void smc_lgr_free(struct smc_link_group *lgr);
6703f3f0e36SUrsula Braun 
smc_lgr_free_work(struct work_struct * work)6710cfdd8f9SUrsula Braun static void smc_lgr_free_work(struct work_struct *work)
6720cfdd8f9SUrsula Braun {
6730cfdd8f9SUrsula Braun 	struct smc_link_group *lgr = container_of(to_delayed_work(work),
6740cfdd8f9SUrsula Braun 						  struct smc_link_group,
6750cfdd8f9SUrsula Braun 						  free_work);
676a0a62ee1SUrsula Braun 	spinlock_t *lgr_lock;
6770cfdd8f9SUrsula Braun 	bool conns;
6780cfdd8f9SUrsula Braun 
679a0a62ee1SUrsula Braun 	smc_lgr_list_head(lgr, &lgr_lock);
680a0a62ee1SUrsula Braun 	spin_lock_bh(lgr_lock);
6818e316b9eSUrsula Braun 	if (lgr->freeing) {
6828e316b9eSUrsula Braun 		spin_unlock_bh(lgr_lock);
6838e316b9eSUrsula Braun 		return;
6848e316b9eSUrsula Braun 	}
6850cfdd8f9SUrsula Braun 	read_lock_bh(&lgr->conns_lock);
6860cfdd8f9SUrsula Braun 	conns = RB_EMPTY_ROOT(&lgr->conns_all);
6870cfdd8f9SUrsula Braun 	read_unlock_bh(&lgr->conns_lock);
6880cfdd8f9SUrsula Braun 	if (!conns) { /* number of lgr connections is no longer zero */
689a0a62ee1SUrsula Braun 		spin_unlock_bh(lgr_lock);
6900cfdd8f9SUrsula Braun 		return;
6910cfdd8f9SUrsula Braun 	}
6920cfdd8f9SUrsula Braun 	list_del_init(&lgr->list); /* remove from smc_lgr_list */
6938e316b9eSUrsula Braun 	lgr->freeing = 1; /* this instance does the freeing, no new schedule */
6948e316b9eSUrsula Braun 	spin_unlock_bh(lgr_lock);
6958e316b9eSUrsula Braun 	cancel_delayed_work(&lgr->free_work);
6960d18a0cbSKarsten Graul 
697f3811fd7SKarsten Graul 	if (!lgr->is_smcd && !lgr->terminating)
698f3811fd7SKarsten Graul 		smc_llc_send_link_delete_all(lgr, true,
699f3811fd7SKarsten Graul 					     SMC_LLC_DEL_PROG_INIT_TERM);
70042bfba9eSUrsula Braun 	if (lgr->is_smcd && !lgr->terminating)
7010512f69eSHans Wippel 		smc_ism_signal_shutdown(lgr);
702a52bcc91SKarsten Graul 	if (!lgr->is_smcd)
703a52bcc91SKarsten Graul 		smcr_lgr_link_deactivate_all(lgr);
7040cfdd8f9SUrsula Braun 	smc_lgr_free(lgr);
7050cfdd8f9SUrsula Braun }
7060cfdd8f9SUrsula Braun 
smc_lgr_terminate_work(struct work_struct * work)707f528ba24SUrsula Braun static void smc_lgr_terminate_work(struct work_struct *work)
708f528ba24SUrsula Braun {
709f528ba24SUrsula Braun 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
710f528ba24SUrsula Braun 						  terminate_work);
711f528ba24SUrsula Braun 
7125f78fe96SKarsten Graul 	__smc_lgr_terminate(lgr, true);
713f528ba24SUrsula Braun }
714f528ba24SUrsula Braun 
715026c381fSKarsten Graul /* return next unique link id for the lgr */
smcr_next_link_id(struct smc_link_group * lgr)716026c381fSKarsten Graul static u8 smcr_next_link_id(struct smc_link_group *lgr)
717026c381fSKarsten Graul {
718026c381fSKarsten Graul 	u8 link_id;
719026c381fSKarsten Graul 	int i;
720026c381fSKarsten Graul 
721026c381fSKarsten Graul 	while (1) {
722cf4f5530SWen Gu again:
723026c381fSKarsten Graul 		link_id = ++lgr->next_link_id;
724026c381fSKarsten Graul 		if (!link_id)	/* skip zero as link_id */
725026c381fSKarsten Graul 			link_id = ++lgr->next_link_id;
726026c381fSKarsten Graul 		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
727d854fcbfSKarsten Graul 			if (smc_link_usable(&lgr->lnk[i]) &&
728026c381fSKarsten Graul 			    lgr->lnk[i].link_id == link_id)
729cf4f5530SWen Gu 				goto again;
730026c381fSKarsten Graul 		}
731026c381fSKarsten Graul 		break;
732026c381fSKarsten Graul 	}
733026c381fSKarsten Graul 	return link_id;
734026c381fSKarsten Graul }
735026c381fSKarsten Graul 
smcr_copy_dev_info_to_link(struct smc_link * link)7366443b2f6SGuvenc Gulce static void smcr_copy_dev_info_to_link(struct smc_link *link)
7376443b2f6SGuvenc Gulce {
7386443b2f6SGuvenc Gulce 	struct smc_ib_device *smcibdev = link->smcibdev;
7396443b2f6SGuvenc Gulce 
7406443b2f6SGuvenc Gulce 	snprintf(link->ibname, sizeof(link->ibname), "%s",
7416443b2f6SGuvenc Gulce 		 smcibdev->ibdev->name);
7426443b2f6SGuvenc Gulce 	link->ndev_ifidx = smcibdev->ndev_ifidx[link->ibport - 1];
7436443b2f6SGuvenc Gulce }
7446443b2f6SGuvenc Gulce 
smcr_link_init(struct smc_link_group * lgr,struct smc_link * lnk,u8 link_idx,struct smc_init_info * ini)745336ba09fSKarsten Graul int smcr_link_init(struct smc_link_group *lgr, struct smc_link *lnk,
746026c381fSKarsten Graul 		   u8 link_idx, struct smc_init_info *ini)
747f3c1deddSKarsten Graul {
748e49300a6SKarsten Graul 	struct smc_ib_device *smcibdev;
749f3c1deddSKarsten Graul 	u8 rndvec[3];
750f3c1deddSKarsten Graul 	int rc;
751f3c1deddSKarsten Graul 
752e49300a6SKarsten Graul 	if (lgr->smc_version == SMC_V2) {
753e49300a6SKarsten Graul 		lnk->smcibdev = ini->smcrv2.ib_dev_v2;
754e49300a6SKarsten Graul 		lnk->ibport = ini->smcrv2.ib_port_v2;
755e49300a6SKarsten Graul 	} else {
756e49300a6SKarsten Graul 		lnk->smcibdev = ini->ib_dev;
757e49300a6SKarsten Graul 		lnk->ibport = ini->ib_port;
758e49300a6SKarsten Graul 	}
759e49300a6SKarsten Graul 	get_device(&lnk->smcibdev->ibdev->dev);
760e49300a6SKarsten Graul 	atomic_inc(&lnk->smcibdev->lnk_cnt);
76120c9398dSWen Gu 	refcount_set(&lnk->refcnt, 1); /* link refcnt is set to 1 */
76220c9398dSWen Gu 	lnk->clearing = 0;
763e49300a6SKarsten Graul 	lnk->path_mtu = lnk->smcibdev->pattr[lnk->ibport - 1].active_mtu;
764026c381fSKarsten Graul 	lnk->link_id = smcr_next_link_id(lgr);
765387707fdSKarsten Graul 	lnk->lgr = lgr;
76661f434b0SWen Gu 	smc_lgr_hold(lgr); /* lgr_put in smcr_link_clear() */
767026c381fSKarsten Graul 	lnk->link_idx = link_idx;
768e9b1a4f8SYacan Liu 	lnk->wr_rx_id_compl = 0;
769ddc99286SGuvenc Gulce 	smc_ibdev_cnt_inc(lnk);
7706443b2f6SGuvenc Gulce 	smcr_copy_dev_info_to_link(lnk);
77107d51580SGuvenc Gulce 	atomic_set(&lnk->conn_cnt, 0);
77245fa8da0SKarsten Graul 	smc_llc_link_set_uid(lnk);
773541afa10SKarsten Graul 	INIT_WORK(&lnk->link_down_wrk, smc_link_down_work);
774e49300a6SKarsten Graul 	if (!lnk->smcibdev->initialized) {
775e49300a6SKarsten Graul 		rc = (int)smc_ib_setup_per_ibdev(lnk->smcibdev);
776f3c1deddSKarsten Graul 		if (rc)
777f3c1deddSKarsten Graul 			goto out;
778f3c1deddSKarsten Graul 	}
779f3c1deddSKarsten Graul 	get_random_bytes(rndvec, sizeof(rndvec));
780f3c1deddSKarsten Graul 	lnk->psn_initial = rndvec[0] + (rndvec[1] << 8) +
781f3c1deddSKarsten Graul 		(rndvec[2] << 16);
782f3c1deddSKarsten Graul 	rc = smc_ib_determine_gid(lnk->smcibdev, lnk->ibport,
78324fb6811SKarsten Graul 				  ini->vlan_id, lnk->gid, &lnk->sgid_index,
78424fb6811SKarsten Graul 				  lgr->smc_version == SMC_V2 ?
78524fb6811SKarsten Graul 						  &ini->smcrv2 : NULL);
786f3c1deddSKarsten Graul 	if (rc)
787f3c1deddSKarsten Graul 		goto out;
788f3c1deddSKarsten Graul 	rc = smc_llc_link_init(lnk);
789f3c1deddSKarsten Graul 	if (rc)
790f3c1deddSKarsten Graul 		goto out;
791f3c1deddSKarsten Graul 	rc = smc_wr_alloc_link_mem(lnk);
792f3c1deddSKarsten Graul 	if (rc)
793f3c1deddSKarsten Graul 		goto clear_llc_lnk;
794f3c1deddSKarsten Graul 	rc = smc_ib_create_protection_domain(lnk);
795f3c1deddSKarsten Graul 	if (rc)
796f3c1deddSKarsten Graul 		goto free_link_mem;
797f3c1deddSKarsten Graul 	rc = smc_ib_create_queue_pair(lnk);
798f3c1deddSKarsten Graul 	if (rc)
799f3c1deddSKarsten Graul 		goto dealloc_pd;
800f3c1deddSKarsten Graul 	rc = smc_wr_create_link(lnk);
801f3c1deddSKarsten Graul 	if (rc)
802f3c1deddSKarsten Graul 		goto destroy_qp;
803741a49a4SKarsten Graul 	lnk->state = SMC_LNK_ACTIVATING;
804f3c1deddSKarsten Graul 	return 0;
805f3c1deddSKarsten Graul 
806f3c1deddSKarsten Graul destroy_qp:
807f3c1deddSKarsten Graul 	smc_ib_destroy_queue_pair(lnk);
808f3c1deddSKarsten Graul dealloc_pd:
809f3c1deddSKarsten Graul 	smc_ib_dealloc_protection_domain(lnk);
810f3c1deddSKarsten Graul free_link_mem:
811f3c1deddSKarsten Graul 	smc_wr_free_link_mem(lnk);
812f3c1deddSKarsten Graul clear_llc_lnk:
8130a99be43SKarsten Graul 	smc_llc_link_clear(lnk, false);
814f3c1deddSKarsten Graul out:
815ddc99286SGuvenc Gulce 	smc_ibdev_cnt_dec(lnk);
816e49300a6SKarsten Graul 	put_device(&lnk->smcibdev->ibdev->dev);
817e49300a6SKarsten Graul 	smcibdev = lnk->smcibdev;
818f3c1deddSKarsten Graul 	memset(lnk, 0, sizeof(struct smc_link));
819d854fcbfSKarsten Graul 	lnk->state = SMC_LNK_UNUSED;
820e49300a6SKarsten Graul 	if (!atomic_dec_return(&smcibdev->lnk_cnt))
821e49300a6SKarsten Graul 		wake_up(&smcibdev->lnks_deleted);
82261f434b0SWen Gu 	smc_lgr_put(lgr); /* lgr_hold above */
823f3c1deddSKarsten Graul 	return rc;
824f3c1deddSKarsten Graul }
825f3c1deddSKarsten Graul 
8260cfdd8f9SUrsula Braun /* create a new SMC link group */
smc_lgr_create(struct smc_sock * smc,struct smc_init_info * ini)827bc36d2fcSKarsten Graul static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
8280cfdd8f9SUrsula Braun {
8290cfdd8f9SUrsula Braun 	struct smc_link_group *lgr;
830a2351c5dSUrsula Braun 	struct list_head *lgr_list;
8318c81ba20SStefan Raspl 	struct smcd_dev *smcd;
8320cfdd8f9SUrsula Braun 	struct smc_link *lnk;
833a0a62ee1SUrsula Braun 	spinlock_t *lgr_lock;
834026c381fSKarsten Graul 	u8 link_idx;
8350cfdd8f9SUrsula Braun 	int rc = 0;
836cd6851f3SUrsula Braun 	int i;
8370cfdd8f9SUrsula Braun 
838bc36d2fcSKarsten Graul 	if (ini->is_smcd && ini->vlan_id) {
8395c21c4ccSUrsula Braun 		if (smc_ism_get_vlan(ini->ism_dev[ini->ism_selected],
8405c21c4ccSUrsula Braun 				     ini->vlan_id)) {
8417a62725aSKarsten Graul 			rc = SMC_CLC_DECL_ISMVLANERR;
842c6ba7c9bSHans Wippel 			goto out;
843c6ba7c9bSHans Wippel 		}
8447a62725aSKarsten Graul 	}
845c6ba7c9bSHans Wippel 
8460cfdd8f9SUrsula Braun 	lgr = kzalloc(sizeof(*lgr), GFP_KERNEL);
8470cfdd8f9SUrsula Braun 	if (!lgr) {
8487a62725aSKarsten Graul 		rc = SMC_CLC_DECL_MEM;
84929ee2701SUrsula Braun 		goto ism_put_vlan;
8500cfdd8f9SUrsula Braun 	}
85122ef473dSKarsten Graul 	lgr->tx_wq = alloc_workqueue("smc_tx_wq-%*phN", 0, 0,
85222ef473dSKarsten Graul 				     SMC_LGR_ID_SIZE, &lgr->id);
85322ef473dSKarsten Graul 	if (!lgr->tx_wq) {
85422ef473dSKarsten Graul 		rc = -ENOMEM;
85522ef473dSKarsten Graul 		goto free_lgr;
85622ef473dSKarsten Graul 	}
857bc36d2fcSKarsten Graul 	lgr->is_smcd = ini->is_smcd;
858517c300eSKarsten Graul 	lgr->sync_err = 0;
8598e316b9eSUrsula Braun 	lgr->terminating = 0;
8608e316b9eSUrsula Braun 	lgr->freeing = 0;
861bc36d2fcSKarsten Graul 	lgr->vlan_id = ini->vlan_id;
86261f434b0SWen Gu 	refcount_set(&lgr->refcnt, 1); /* set lgr refcnt to 1 */
863aff7bfedSD. Wythe 	init_rwsem(&lgr->sndbufs_lock);
864aff7bfedSD. Wythe 	init_rwsem(&lgr->rmbs_lock);
865c6ba7c9bSHans Wippel 	rwlock_init(&lgr->conns_lock);
866cd6851f3SUrsula Braun 	for (i = 0; i < SMC_RMBE_SIZES; i++) {
867cd6851f3SUrsula Braun 		INIT_LIST_HEAD(&lgr->sndbufs[i]);
868cd6851f3SUrsula Braun 		INIT_LIST_HEAD(&lgr->rmbs[i]);
869cd6851f3SUrsula Braun 	}
870026c381fSKarsten Graul 	lgr->next_link_id = 0;
8719fda3510SHans Wippel 	smc_lgr_list.num += SMC_LGR_NUM_INCR;
8729fda3510SHans Wippel 	memcpy(&lgr->id, (u8 *)&smc_lgr_list.num, SMC_LGR_ID_SIZE);
8730cfdd8f9SUrsula Braun 	INIT_DELAYED_WORK(&lgr->free_work, smc_lgr_free_work);
874f528ba24SUrsula Braun 	INIT_WORK(&lgr->terminate_work, smc_lgr_terminate_work);
8750cfdd8f9SUrsula Braun 	lgr->conns_all = RB_ROOT;
876bc36d2fcSKarsten Graul 	if (ini->is_smcd) {
877c6ba7c9bSHans Wippel 		/* SMC-D specific settings */
8788c81ba20SStefan Raspl 		smcd = ini->ism_dev[ini->ism_selected];
8798c81ba20SStefan Raspl 		get_device(smcd->ops->get_dev(smcd));
8807e5ef8ebSWen Gu 		lgr->peer_gid.gid =
8817e5ef8ebSWen Gu 			ini->ism_peer_gid[ini->ism_selected].gid;
8827e5ef8ebSWen Gu 		lgr->peer_gid.gid_ext =
8837e5ef8ebSWen Gu 			ini->ism_peer_gid[ini->ism_selected].gid_ext;
8845c21c4ccSUrsula Braun 		lgr->smcd = ini->ism_dev[ini->ism_selected];
8855c21c4ccSUrsula Braun 		lgr_list = &ini->ism_dev[ini->ism_selected]->lgr_list;
886a0a62ee1SUrsula Braun 		lgr_lock = &lgr->smcd->lgr_lock;
887b81a5eb7SUrsula Braun 		lgr->smc_version = ini->smcd_version;
88850c6b20eSUrsula Braun 		lgr->peer_shutdown = 0;
8895c21c4ccSUrsula Braun 		atomic_inc(&ini->ism_dev[ini->ism_selected]->lgr_cnt);
890c6ba7c9bSHans Wippel 	} else {
891c6ba7c9bSHans Wippel 		/* SMC-R specific settings */
892e49300a6SKarsten Graul 		struct smc_ib_device *ibdev;
893e49300a6SKarsten Graul 		int ibport;
894e49300a6SKarsten Graul 
895c6ba7c9bSHans Wippel 		lgr->role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
896e49300a6SKarsten Graul 		lgr->smc_version = ini->smcr_version;
897e49300a6SKarsten Graul 		memcpy(lgr->peer_systemid, ini->peer_systemid,
898bc36d2fcSKarsten Graul 		       SMC_SYSTEMID_LEN);
899e49300a6SKarsten Graul 		if (lgr->smc_version == SMC_V2) {
900e49300a6SKarsten Graul 			ibdev = ini->smcrv2.ib_dev_v2;
901e49300a6SKarsten Graul 			ibport = ini->smcrv2.ib_port_v2;
902e49300a6SKarsten Graul 			lgr->saddr = ini->smcrv2.saddr;
903e49300a6SKarsten Graul 			lgr->uses_gateway = ini->smcrv2.uses_gateway;
904e49300a6SKarsten Graul 			memcpy(lgr->nexthop_mac, ini->smcrv2.nexthop_mac,
905e49300a6SKarsten Graul 			       ETH_ALEN);
9067f0620b9SGuangguan Wang 			lgr->max_conns = ini->max_conns;
90769b888e3SGuangguan Wang 			lgr->max_links = ini->max_links;
908e49300a6SKarsten Graul 		} else {
909e49300a6SKarsten Graul 			ibdev = ini->ib_dev;
910e49300a6SKarsten Graul 			ibport = ini->ib_port;
9117f0620b9SGuangguan Wang 			lgr->max_conns = SMC_CONN_PER_LGR_MAX;
91269b888e3SGuangguan Wang 			lgr->max_links = SMC_LINKS_ADD_LNK_MAX;
913e49300a6SKarsten Graul 		}
914e49300a6SKarsten Graul 		memcpy(lgr->pnet_id, ibdev->pnetid[ibport - 1],
91535dcf7ecSKarsten Graul 		       SMC_MAX_PNETID_LEN);
916bdee15e8SDan Carpenter 		rc = smc_wr_alloc_lgr_mem(lgr);
917bdee15e8SDan Carpenter 		if (rc)
9188799e310SKarsten Graul 			goto free_wq;
91900a049cfSKarsten Graul 		smc_llc_lgr_init(lgr, smc);
92000a049cfSKarsten Graul 
921026c381fSKarsten Graul 		link_idx = SMC_SINGLE_LINK;
922026c381fSKarsten Graul 		lnk = &lgr->lnk[link_idx];
923026c381fSKarsten Graul 		rc = smcr_link_init(lgr, lnk, link_idx, ini);
9248799e310SKarsten Graul 		if (rc) {
9258799e310SKarsten Graul 			smc_wr_free_lgr_mem(lgr);
92622ef473dSKarsten Graul 			goto free_wq;
9278799e310SKarsten Graul 		}
9280237a3a6STony Lu 		lgr->net = smc_ib_net(lnk->smcibdev);
929a2351c5dSUrsula Braun 		lgr_list = &smc_lgr_list.list;
930a0a62ee1SUrsula Braun 		lgr_lock = &smc_lgr_list.lock;
931b984f370SWen Gu 		lgr->buf_type = lgr->net->smc.sysctl_smcr_buf_type;
9326dabd405SUrsula Braun 		atomic_inc(&lgr_cnt);
933c6ba7c9bSHans Wippel 	}
9340cfdd8f9SUrsula Braun 	smc->conn.lgr = lgr;
935a0a62ee1SUrsula Braun 	spin_lock_bh(lgr_lock);
936a9e44502SKarsten Graul 	list_add_tail(&lgr->list, lgr_list);
937a0a62ee1SUrsula Braun 	spin_unlock_bh(lgr_lock);
938f38ba179SUrsula Braun 	return 0;
939f38ba179SUrsula Braun 
94022ef473dSKarsten Graul free_wq:
94122ef473dSKarsten Graul 	destroy_workqueue(lgr->tx_wq);
942f38ba179SUrsula Braun free_lgr:
943f38ba179SUrsula Braun 	kfree(lgr);
94429ee2701SUrsula Braun ism_put_vlan:
94529ee2701SUrsula Braun 	if (ini->is_smcd && ini->vlan_id)
9465c21c4ccSUrsula Braun 		smc_ism_put_vlan(ini->ism_dev[ini->ism_selected], ini->vlan_id);
9470cfdd8f9SUrsula Braun out:
9487a62725aSKarsten Graul 	if (rc < 0) {
9497a62725aSKarsten Graul 		if (rc == -ENOMEM)
9507a62725aSKarsten Graul 			rc = SMC_CLC_DECL_MEM;
9517a62725aSKarsten Graul 		else
9527a62725aSKarsten Graul 			rc = SMC_CLC_DECL_INTERR;
9537a62725aSKarsten Graul 	}
9540cfdd8f9SUrsula Braun 	return rc;
9550cfdd8f9SUrsula Braun }
9560cfdd8f9SUrsula Braun 
smc_write_space(struct smc_connection * conn)957c6f02ebeSKarsten Graul static int smc_write_space(struct smc_connection *conn)
958c6f02ebeSKarsten Graul {
959c6f02ebeSKarsten Graul 	int buffer_len = conn->peer_rmbe_size;
960c6f02ebeSKarsten Graul 	union smc_host_cursor prod;
961c6f02ebeSKarsten Graul 	union smc_host_cursor cons;
962c6f02ebeSKarsten Graul 	int space;
963c6f02ebeSKarsten Graul 
964c6f02ebeSKarsten Graul 	smc_curs_copy(&prod, &conn->local_tx_ctrl.prod, conn);
965c6f02ebeSKarsten Graul 	smc_curs_copy(&cons, &conn->local_rx_ctrl.cons, conn);
966c6f02ebeSKarsten Graul 	/* determine rx_buf space */
967c6f02ebeSKarsten Graul 	space = buffer_len - smc_curs_diff(buffer_len, &cons, &prod);
968c6f02ebeSKarsten Graul 	return space;
969c6f02ebeSKarsten Graul }
970c6f02ebeSKarsten Graul 
smc_switch_cursor(struct smc_sock * smc,struct smc_cdc_tx_pend * pend,struct smc_wr_buf * wr_buf)971b8ded9deSKarsten Graul static int smc_switch_cursor(struct smc_sock *smc, struct smc_cdc_tx_pend *pend,
972b8ded9deSKarsten Graul 			     struct smc_wr_buf *wr_buf)
973c6f02ebeSKarsten Graul {
974c6f02ebeSKarsten Graul 	struct smc_connection *conn = &smc->conn;
975c6f02ebeSKarsten Graul 	union smc_host_cursor cons, fin;
976c6f02ebeSKarsten Graul 	int rc = 0;
977c6f02ebeSKarsten Graul 	int diff;
978c6f02ebeSKarsten Graul 
979c6f02ebeSKarsten Graul 	smc_curs_copy(&conn->tx_curs_sent, &conn->tx_curs_fin, conn);
980c6f02ebeSKarsten Graul 	smc_curs_copy(&fin, &conn->local_tx_ctrl_fin, conn);
981c6f02ebeSKarsten Graul 	/* set prod cursor to old state, enforce tx_rdma_writes() */
982c6f02ebeSKarsten Graul 	smc_curs_copy(&conn->local_tx_ctrl.prod, &fin, conn);
983c6f02ebeSKarsten Graul 	smc_curs_copy(&cons, &conn->local_rx_ctrl.cons, conn);
984c6f02ebeSKarsten Graul 
985c6f02ebeSKarsten Graul 	if (smc_curs_comp(conn->peer_rmbe_size, &cons, &fin) < 0) {
986c6f02ebeSKarsten Graul 		/* cons cursor advanced more than fin, and prod was set
987c6f02ebeSKarsten Graul 		 * fin above, so now prod is smaller than cons. Fix that.
988c6f02ebeSKarsten Graul 		 */
989c6f02ebeSKarsten Graul 		diff = smc_curs_diff(conn->peer_rmbe_size, &fin, &cons);
990c6f02ebeSKarsten Graul 		smc_curs_add(conn->sndbuf_desc->len,
991c6f02ebeSKarsten Graul 			     &conn->tx_curs_sent, diff);
992c6f02ebeSKarsten Graul 		smc_curs_add(conn->sndbuf_desc->len,
993c6f02ebeSKarsten Graul 			     &conn->tx_curs_fin, diff);
994c6f02ebeSKarsten Graul 
995c6f02ebeSKarsten Graul 		smp_mb__before_atomic();
996c6f02ebeSKarsten Graul 		atomic_add(diff, &conn->sndbuf_space);
997c6f02ebeSKarsten Graul 		smp_mb__after_atomic();
998c6f02ebeSKarsten Graul 
999c6f02ebeSKarsten Graul 		smc_curs_add(conn->peer_rmbe_size,
1000c6f02ebeSKarsten Graul 			     &conn->local_tx_ctrl.prod, diff);
1001c6f02ebeSKarsten Graul 		smc_curs_add(conn->peer_rmbe_size,
1002c6f02ebeSKarsten Graul 			     &conn->local_tx_ctrl_fin, diff);
1003c6f02ebeSKarsten Graul 	}
1004c6f02ebeSKarsten Graul 	/* recalculate, value is used by tx_rdma_writes() */
1005c6f02ebeSKarsten Graul 	atomic_set(&smc->conn.peer_rmbe_space, smc_write_space(conn));
1006c6f02ebeSKarsten Graul 
1007c6f02ebeSKarsten Graul 	if (smc->sk.sk_state != SMC_INIT &&
1008c6f02ebeSKarsten Graul 	    smc->sk.sk_state != SMC_CLOSED) {
1009b8ded9deSKarsten Graul 		rc = smcr_cdc_msg_send_validation(conn, pend, wr_buf);
1010c6f02ebeSKarsten Graul 		if (!rc) {
101122ef473dSKarsten Graul 			queue_delayed_work(conn->lgr->tx_wq, &conn->tx_work, 0);
1012c6f02ebeSKarsten Graul 			smc->sk.sk_data_ready(&smc->sk);
1013c6f02ebeSKarsten Graul 		}
1014b8ded9deSKarsten Graul 	} else {
1015b8ded9deSKarsten Graul 		smc_wr_tx_put_slot(conn->lnk,
1016b8ded9deSKarsten Graul 				   (struct smc_wr_tx_pend_priv *)pend);
1017c6f02ebeSKarsten Graul 	}
1018c6f02ebeSKarsten Graul 	return rc;
1019c6f02ebeSKarsten Graul }
1020c6f02ebeSKarsten Graul 
smc_switch_link_and_count(struct smc_connection * conn,struct smc_link * to_lnk)102164513d26SGuvenc Gulce void smc_switch_link_and_count(struct smc_connection *conn,
102207d51580SGuvenc Gulce 			       struct smc_link *to_lnk)
102307d51580SGuvenc Gulce {
102407d51580SGuvenc Gulce 	atomic_dec(&conn->lnk->conn_cnt);
102520c9398dSWen Gu 	/* link_hold in smc_conn_create() */
102620c9398dSWen Gu 	smcr_link_put(conn->lnk);
102707d51580SGuvenc Gulce 	conn->lnk = to_lnk;
102807d51580SGuvenc Gulce 	atomic_inc(&conn->lnk->conn_cnt);
102920c9398dSWen Gu 	/* link_put in smc_conn_free() */
103020c9398dSWen Gu 	smcr_link_hold(conn->lnk);
103107d51580SGuvenc Gulce }
103207d51580SGuvenc Gulce 
smc_switch_conns(struct smc_link_group * lgr,struct smc_link * from_lnk,bool is_dev_err)1033c6f02ebeSKarsten Graul struct smc_link *smc_switch_conns(struct smc_link_group *lgr,
1034c6f02ebeSKarsten Graul 				  struct smc_link *from_lnk, bool is_dev_err)
1035c6f02ebeSKarsten Graul {
1036c6f02ebeSKarsten Graul 	struct smc_link *to_lnk = NULL;
1037b8ded9deSKarsten Graul 	struct smc_cdc_tx_pend *pend;
1038c6f02ebeSKarsten Graul 	struct smc_connection *conn;
1039b8ded9deSKarsten Graul 	struct smc_wr_buf *wr_buf;
1040c6f02ebeSKarsten Graul 	struct smc_sock *smc;
1041c6f02ebeSKarsten Graul 	struct rb_node *node;
1042c6f02ebeSKarsten Graul 	int i, rc = 0;
1043c6f02ebeSKarsten Graul 
1044c6f02ebeSKarsten Graul 	/* link is inactive, wake up tx waiters */
1045c6f02ebeSKarsten Graul 	smc_wr_wakeup_tx_wait(from_lnk);
1046c6f02ebeSKarsten Graul 
1047c6f02ebeSKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
1048741a49a4SKarsten Graul 		if (!smc_link_active(&lgr->lnk[i]) || i == from_lnk->link_idx)
1049c6f02ebeSKarsten Graul 			continue;
1050c6f02ebeSKarsten Graul 		if (is_dev_err && from_lnk->smcibdev == lgr->lnk[i].smcibdev &&
1051c6f02ebeSKarsten Graul 		    from_lnk->ibport == lgr->lnk[i].ibport) {
1052c6f02ebeSKarsten Graul 			continue;
1053c6f02ebeSKarsten Graul 		}
1054c6f02ebeSKarsten Graul 		to_lnk = &lgr->lnk[i];
1055c6f02ebeSKarsten Graul 		break;
1056c6f02ebeSKarsten Graul 	}
105795f7f3e7SKarsten Graul 	if (!to_lnk || !smc_wr_tx_link_hold(to_lnk)) {
1058c6f02ebeSKarsten Graul 		smc_lgr_terminate_sched(lgr);
1059c6f02ebeSKarsten Graul 		return NULL;
1060c6f02ebeSKarsten Graul 	}
1061c6f02ebeSKarsten Graul again:
1062c6f02ebeSKarsten Graul 	read_lock_bh(&lgr->conns_lock);
1063c6f02ebeSKarsten Graul 	for (node = rb_first(&lgr->conns_all); node; node = rb_next(node)) {
1064c6f02ebeSKarsten Graul 		conn = rb_entry(node, struct smc_connection, alert_node);
1065c6f02ebeSKarsten Graul 		if (conn->lnk != from_lnk)
1066c6f02ebeSKarsten Graul 			continue;
1067c6f02ebeSKarsten Graul 		smc = container_of(conn, struct smc_sock, conn);
1068c6f02ebeSKarsten Graul 		/* conn->lnk not yet set in SMC_INIT state */
1069c6f02ebeSKarsten Graul 		if (smc->sk.sk_state == SMC_INIT)
1070c6f02ebeSKarsten Graul 			continue;
1071c6f02ebeSKarsten Graul 		if (smc->sk.sk_state == SMC_CLOSED ||
1072c6f02ebeSKarsten Graul 		    smc->sk.sk_state == SMC_PEERCLOSEWAIT1 ||
1073c6f02ebeSKarsten Graul 		    smc->sk.sk_state == SMC_PEERCLOSEWAIT2 ||
1074c6f02ebeSKarsten Graul 		    smc->sk.sk_state == SMC_APPFINCLOSEWAIT ||
1075c6f02ebeSKarsten Graul 		    smc->sk.sk_state == SMC_APPCLOSEWAIT1 ||
1076c6f02ebeSKarsten Graul 		    smc->sk.sk_state == SMC_APPCLOSEWAIT2 ||
1077c6f02ebeSKarsten Graul 		    smc->sk.sk_state == SMC_PEERFINCLOSEWAIT ||
1078c6f02ebeSKarsten Graul 		    smc->sk.sk_state == SMC_PEERABORTWAIT ||
1079c6f02ebeSKarsten Graul 		    smc->sk.sk_state == SMC_PROCESSABORT) {
1080c6f02ebeSKarsten Graul 			spin_lock_bh(&conn->send_lock);
108107d51580SGuvenc Gulce 			smc_switch_link_and_count(conn, to_lnk);
1082c6f02ebeSKarsten Graul 			spin_unlock_bh(&conn->send_lock);
1083c6f02ebeSKarsten Graul 			continue;
1084c6f02ebeSKarsten Graul 		}
1085c6f02ebeSKarsten Graul 		sock_hold(&smc->sk);
1086c6f02ebeSKarsten Graul 		read_unlock_bh(&lgr->conns_lock);
1087b8ded9deSKarsten Graul 		/* pre-fetch buffer outside of send_lock, might sleep */
1088b8ded9deSKarsten Graul 		rc = smc_cdc_get_free_slot(conn, to_lnk, &wr_buf, NULL, &pend);
108995f7f3e7SKarsten Graul 		if (rc)
109095f7f3e7SKarsten Graul 			goto err_out;
1091c6f02ebeSKarsten Graul 		/* avoid race with smcr_tx_sndbuf_nonempty() */
1092c6f02ebeSKarsten Graul 		spin_lock_bh(&conn->send_lock);
109307d51580SGuvenc Gulce 		smc_switch_link_and_count(conn, to_lnk);
1094b8ded9deSKarsten Graul 		rc = smc_switch_cursor(smc, pend, wr_buf);
1095c6f02ebeSKarsten Graul 		spin_unlock_bh(&conn->send_lock);
1096c6f02ebeSKarsten Graul 		sock_put(&smc->sk);
109795f7f3e7SKarsten Graul 		if (rc)
109895f7f3e7SKarsten Graul 			goto err_out;
1099c6f02ebeSKarsten Graul 		goto again;
1100c6f02ebeSKarsten Graul 	}
1101c6f02ebeSKarsten Graul 	read_unlock_bh(&lgr->conns_lock);
110295f7f3e7SKarsten Graul 	smc_wr_tx_link_put(to_lnk);
1103c6f02ebeSKarsten Graul 	return to_lnk;
110495f7f3e7SKarsten Graul 
110595f7f3e7SKarsten Graul err_out:
110695f7f3e7SKarsten Graul 	smcr_link_down_cond_sched(to_lnk);
110795f7f3e7SKarsten Graul 	smc_wr_tx_link_put(to_lnk);
110895f7f3e7SKarsten Graul 	return NULL;
1109c6f02ebeSKarsten Graul }
1110c6f02ebeSKarsten Graul 
smcr_buf_unuse(struct smc_buf_desc * buf_desc,bool is_rmb,struct smc_link_group * lgr)1111b8d19945SWen Gu static void smcr_buf_unuse(struct smc_buf_desc *buf_desc, bool is_rmb,
11126d74c3a8SKarsten Graul 			   struct smc_link_group *lgr)
1113b9247544SKarsten Graul {
1114aff7bfedSD. Wythe 	struct rw_semaphore *lock;	/* lock buffer list */
1115d5500667SKarsten Graul 	int rc;
1116d5500667SKarsten Graul 
1117b8d19945SWen Gu 	if (is_rmb && buf_desc->is_conf_rkey && !list_empty(&lgr->list)) {
1118b9247544SKarsten Graul 		/* unregister rmb with peer */
1119d5500667SKarsten Graul 		rc = smc_llc_flow_initiate(lgr, SMC_LLC_FLOW_RKEY);
1120d5500667SKarsten Graul 		if (!rc) {
1121d5500667SKarsten Graul 			/* protect against smc_llc_cli_rkey_exchange() */
1122f6421014SD. Wythe 			down_read(&lgr->llc_conf_mutex);
1123b8d19945SWen Gu 			smc_llc_do_delete_rkey(lgr, buf_desc);
1124b8d19945SWen Gu 			buf_desc->is_conf_rkey = false;
1125f6421014SD. Wythe 			up_read(&lgr->llc_conf_mutex);
1126d5500667SKarsten Graul 			smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
1127b9247544SKarsten Graul 		}
1128d5500667SKarsten Graul 	}
1129d5500667SKarsten Graul 
1130b8d19945SWen Gu 	if (buf_desc->is_reg_err) {
1131b9247544SKarsten Graul 		/* buf registration failed, reuse not possible */
1132b8d19945SWen Gu 		lock = is_rmb ? &lgr->rmbs_lock :
1133b8d19945SWen Gu 				&lgr->sndbufs_lock;
1134aff7bfedSD. Wythe 		down_write(lock);
1135b8d19945SWen Gu 		list_del(&buf_desc->list);
1136aff7bfedSD. Wythe 		up_write(lock);
1137b9247544SKarsten Graul 
1138b8d19945SWen Gu 		smc_buf_free(lgr, is_rmb, buf_desc);
1139b9247544SKarsten Graul 	} else {
1140475f9ff6SD. Wythe 		/* memzero_explicit provides potential memory barrier semantics */
1141475f9ff6SD. Wythe 		memzero_explicit(buf_desc->cpu_addr, buf_desc->len);
1142475f9ff6SD. Wythe 		WRITE_ONCE(buf_desc->used, 0);
1143b9247544SKarsten Graul 	}
1144b9247544SKarsten Graul }
1145b9247544SKarsten Graul 
smcd_buf_detach(struct smc_connection * conn)114621f6f41eSWen Gu static void smcd_buf_detach(struct smc_connection *conn)
114721f6f41eSWen Gu {
114821f6f41eSWen Gu 	struct smcd_dev *smcd = conn->lgr->smcd;
114921f6f41eSWen Gu 	u64 peer_token = conn->peer_token;
115021f6f41eSWen Gu 
115121f6f41eSWen Gu 	if (!conn->sndbuf_desc)
115221f6f41eSWen Gu 		return;
115321f6f41eSWen Gu 
115421f6f41eSWen Gu 	smc_ism_detach_dmb(smcd, peer_token);
115521f6f41eSWen Gu 
115621f6f41eSWen Gu 	kfree(conn->sndbuf_desc);
115721f6f41eSWen Gu 	conn->sndbuf_desc = NULL;
115821f6f41eSWen Gu }
115921f6f41eSWen Gu 
smc_buf_unuse(struct smc_connection * conn,struct smc_link_group * lgr)1160fb692ec4SKarsten Graul static void smc_buf_unuse(struct smc_connection *conn,
1161fb692ec4SKarsten Graul 			  struct smc_link_group *lgr)
1162cd6851f3SUrsula Braun {
11631c552696STony Lu 	if (conn->sndbuf_desc) {
1164b8d19945SWen Gu 		if (!lgr->is_smcd && conn->sndbuf_desc->is_vm) {
1165b8d19945SWen Gu 			smcr_buf_unuse(conn->sndbuf_desc, false, lgr);
1166b8d19945SWen Gu 		} else {
1167475f9ff6SD. Wythe 			memzero_explicit(conn->sndbuf_desc->cpu_addr, conn->sndbuf_desc->len);
1168475f9ff6SD. Wythe 			WRITE_ONCE(conn->sndbuf_desc->used, 0);
11691c552696STony Lu 		}
1170b8d19945SWen Gu 	}
1171b8d19945SWen Gu 	if (conn->rmb_desc) {
1172b8d19945SWen Gu 		if (!lgr->is_smcd) {
1173b8d19945SWen Gu 			smcr_buf_unuse(conn->rmb_desc, true, lgr);
1174b8d19945SWen Gu 		} else {
1175475f9ff6SD. Wythe 			memzero_explicit(conn->rmb_desc->cpu_addr,
1176475f9ff6SD. Wythe 					 conn->rmb_desc->len + sizeof(struct smcd_cdc_msg));
1177475f9ff6SD. Wythe 			WRITE_ONCE(conn->rmb_desc->used, 0);
1178b8d19945SWen Gu 		}
1179cd6851f3SUrsula Braun 	}
11801c552696STony Lu }
1181cd6851f3SUrsula Braun 
11820cfdd8f9SUrsula Braun /* remove a finished connection from its link group */
smc_conn_free(struct smc_connection * conn)11830cfdd8f9SUrsula Braun void smc_conn_free(struct smc_connection *conn)
11840cfdd8f9SUrsula Braun {
1185fb692ec4SKarsten Graul 	struct smc_link_group *lgr = conn->lgr;
1186fb692ec4SKarsten Graul 
118761f434b0SWen Gu 	if (!lgr || conn->freed)
118861f434b0SWen Gu 		/* Connection has never been registered in a
118961f434b0SWen Gu 		 * link group, or has already been freed.
119061f434b0SWen Gu 		 */
11910cfdd8f9SUrsula Braun 		return;
119261f434b0SWen Gu 
119361f434b0SWen Gu 	conn->freed = 1;
1194ea89c6c0SWen Gu 	if (!smc_conn_lgr_valid(conn))
119561f434b0SWen Gu 		/* Connection has already unregistered from
119661f434b0SWen Gu 		 * link group.
119761f434b0SWen Gu 		 */
119861f434b0SWen Gu 		goto lgr_put;
119961f434b0SWen Gu 
1200fb692ec4SKarsten Graul 	if (lgr->is_smcd) {
120142bfba9eSUrsula Braun 		if (!list_empty(&lgr->list))
1202c6ba7c9bSHans Wippel 			smc_ism_unset_conn(conn);
120321f6f41eSWen Gu 		if (smc_ism_support_dmb_nocopy(lgr->smcd))
120421f6f41eSWen Gu 			smcd_buf_detach(conn);
1205be244f28SHans Wippel 		tasklet_kill(&conn->rx_tsklet);
1206be244f28SHans Wippel 	} else {
1207349d4312SDust Li 		smc_cdc_wait_pend_tx_wr(conn);
1208b286a065SKarsten Graul 		if (current_work() != &conn->abort_work)
1209b286a065SKarsten Graul 			cancel_work_sync(&conn->abort_work);
1210be244f28SHans Wippel 	}
12112a0674ffSUrsula Braun 	if (!list_empty(&lgr->list)) {
1212fb692ec4SKarsten Graul 		smc_buf_unuse(conn, lgr); /* allow buffer reuse */
12130537f0a2SD. Wythe 		smc_lgr_unregister_conn(conn);
12142a0674ffSUrsula Braun 	}
1215fb692ec4SKarsten Graul 
1216fb692ec4SKarsten Graul 	if (!lgr->conns_num)
1217fb692ec4SKarsten Graul 		smc_lgr_schedule_free_work(lgr);
121861f434b0SWen Gu lgr_put:
121920c9398dSWen Gu 	if (!lgr->is_smcd)
122020c9398dSWen Gu 		smcr_link_put(conn->lnk); /* link_hold in smc_conn_create() */
122161f434b0SWen Gu 	smc_lgr_put(lgr); /* lgr_hold in smc_conn_create() */
12220cfdd8f9SUrsula Braun }
12230cfdd8f9SUrsula Braun 
12244a3641c1SKarsten Graul /* unregister a link from a buf_desc */
smcr_buf_unmap_link(struct smc_buf_desc * buf_desc,bool is_rmb,struct smc_link * lnk)12254a3641c1SKarsten Graul static void smcr_buf_unmap_link(struct smc_buf_desc *buf_desc, bool is_rmb,
12264a3641c1SKarsten Graul 				struct smc_link *lnk)
12274a3641c1SKarsten Graul {
1228b8d19945SWen Gu 	if (is_rmb || buf_desc->is_vm)
12294a3641c1SKarsten Graul 		buf_desc->is_reg_mr[lnk->link_idx] = false;
12304a3641c1SKarsten Graul 	if (!buf_desc->is_map_ib[lnk->link_idx])
12314a3641c1SKarsten Graul 		return;
1232b8d19945SWen Gu 
1233b8d19945SWen Gu 	if ((is_rmb || buf_desc->is_vm) &&
1234b8d19945SWen Gu 	    buf_desc->mr[lnk->link_idx]) {
1235b8d19945SWen Gu 		smc_ib_put_memory_region(buf_desc->mr[lnk->link_idx]);
1236b8d19945SWen Gu 		buf_desc->mr[lnk->link_idx] = NULL;
12374a3641c1SKarsten Graul 	}
1238b8d19945SWen Gu 	if (is_rmb)
12394a3641c1SKarsten Graul 		smc_ib_buf_unmap_sg(lnk, buf_desc, DMA_FROM_DEVICE);
1240b8d19945SWen Gu 	else
12414a3641c1SKarsten Graul 		smc_ib_buf_unmap_sg(lnk, buf_desc, DMA_TO_DEVICE);
1242b8d19945SWen Gu 
12434a3641c1SKarsten Graul 	sg_free_table(&buf_desc->sgt[lnk->link_idx]);
12444a3641c1SKarsten Graul 	buf_desc->is_map_ib[lnk->link_idx] = false;
12454a3641c1SKarsten Graul }
12464a3641c1SKarsten Graul 
12474a3641c1SKarsten Graul /* unmap all buffers of lgr for a deleted link */
smcr_buf_unmap_lgr(struct smc_link * lnk)12484a3641c1SKarsten Graul static void smcr_buf_unmap_lgr(struct smc_link *lnk)
12494a3641c1SKarsten Graul {
12504a3641c1SKarsten Graul 	struct smc_link_group *lgr = lnk->lgr;
12514a3641c1SKarsten Graul 	struct smc_buf_desc *buf_desc, *bf;
12524a3641c1SKarsten Graul 	int i;
12534a3641c1SKarsten Graul 
12544a3641c1SKarsten Graul 	for (i = 0; i < SMC_RMBE_SIZES; i++) {
1255aff7bfedSD. Wythe 		down_write(&lgr->rmbs_lock);
12564a3641c1SKarsten Graul 		list_for_each_entry_safe(buf_desc, bf, &lgr->rmbs[i], list)
12574a3641c1SKarsten Graul 			smcr_buf_unmap_link(buf_desc, true, lnk);
1258aff7bfedSD. Wythe 		up_write(&lgr->rmbs_lock);
1259aff7bfedSD. Wythe 
1260aff7bfedSD. Wythe 		down_write(&lgr->sndbufs_lock);
12614a3641c1SKarsten Graul 		list_for_each_entry_safe(buf_desc, bf, &lgr->sndbufs[i],
12624a3641c1SKarsten Graul 					 list)
12634a3641c1SKarsten Graul 			smcr_buf_unmap_link(buf_desc, false, lnk);
1264aff7bfedSD. Wythe 		up_write(&lgr->sndbufs_lock);
12654a3641c1SKarsten Graul 	}
12664a3641c1SKarsten Graul }
12674a3641c1SKarsten Graul 
smcr_rtoken_clear_link(struct smc_link * lnk)12684a3641c1SKarsten Graul static void smcr_rtoken_clear_link(struct smc_link *lnk)
12694a3641c1SKarsten Graul {
12704a3641c1SKarsten Graul 	struct smc_link_group *lgr = lnk->lgr;
12714a3641c1SKarsten Graul 	int i;
12724a3641c1SKarsten Graul 
12734a3641c1SKarsten Graul 	for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
12744a3641c1SKarsten Graul 		lgr->rtokens[i][lnk->link_idx].rkey = 0;
12754a3641c1SKarsten Graul 		lgr->rtokens[i][lnk->link_idx].dma_addr = 0;
12764a3641c1SKarsten Graul 	}
12774a3641c1SKarsten Graul }
12784a3641c1SKarsten Graul 
__smcr_link_clear(struct smc_link * lnk)127920c9398dSWen Gu static void __smcr_link_clear(struct smc_link *lnk)
12800cfdd8f9SUrsula Braun {
128161f434b0SWen Gu 	struct smc_link_group *lgr = lnk->lgr;
1282d854fcbfSKarsten Graul 	struct smc_ib_device *smcibdev;
1283d854fcbfSKarsten Graul 
1284f38ba179SUrsula Braun 	smc_wr_free_link_mem(lnk);
1285ddc99286SGuvenc Gulce 	smc_ibdev_cnt_dec(lnk);
1286f3c1deddSKarsten Graul 	put_device(&lnk->smcibdev->ibdev->dev);
1287d854fcbfSKarsten Graul 	smcibdev = lnk->smcibdev;
1288d854fcbfSKarsten Graul 	memset(lnk, 0, sizeof(struct smc_link));
1289d854fcbfSKarsten Graul 	lnk->state = SMC_LNK_UNUSED;
1290d854fcbfSKarsten Graul 	if (!atomic_dec_return(&smcibdev->lnk_cnt))
1291d854fcbfSKarsten Graul 		wake_up(&smcibdev->lnks_deleted);
129261f434b0SWen Gu 	smc_lgr_put(lgr); /* lgr_hold in smcr_link_init() */
12930cfdd8f9SUrsula Braun }
12940cfdd8f9SUrsula Braun 
129520c9398dSWen Gu /* must be called under lgr->llc_conf_mutex lock */
smcr_link_clear(struct smc_link * lnk,bool log)129620c9398dSWen Gu void smcr_link_clear(struct smc_link *lnk, bool log)
129720c9398dSWen Gu {
129820c9398dSWen Gu 	if (!lnk->lgr || lnk->clearing ||
129920c9398dSWen Gu 	    lnk->state == SMC_LNK_UNUSED)
130020c9398dSWen Gu 		return;
130120c9398dSWen Gu 	lnk->clearing = 1;
130220c9398dSWen Gu 	lnk->peer_qpn = 0;
130320c9398dSWen Gu 	smc_llc_link_clear(lnk, log);
130420c9398dSWen Gu 	smcr_buf_unmap_lgr(lnk);
130520c9398dSWen Gu 	smcr_rtoken_clear_link(lnk);
130620c9398dSWen Gu 	smc_ib_modify_qp_error(lnk);
130720c9398dSWen Gu 	smc_wr_free_link(lnk);
130820c9398dSWen Gu 	smc_ib_destroy_queue_pair(lnk);
130920c9398dSWen Gu 	smc_ib_dealloc_protection_domain(lnk);
131020c9398dSWen Gu 	smcr_link_put(lnk); /* theoretically last link_put */
131120c9398dSWen Gu }
131220c9398dSWen Gu 
smcr_link_hold(struct smc_link * lnk)131320c9398dSWen Gu void smcr_link_hold(struct smc_link *lnk)
131420c9398dSWen Gu {
131520c9398dSWen Gu 	refcount_inc(&lnk->refcnt);
131620c9398dSWen Gu }
131720c9398dSWen Gu 
smcr_link_put(struct smc_link * lnk)131820c9398dSWen Gu void smcr_link_put(struct smc_link *lnk)
131920c9398dSWen Gu {
132020c9398dSWen Gu 	if (refcount_dec_and_test(&lnk->refcnt))
132120c9398dSWen Gu 		__smcr_link_clear(lnk);
132220c9398dSWen Gu }
132320c9398dSWen Gu 
smcr_buf_free(struct smc_link_group * lgr,bool is_rmb,struct smc_buf_desc * buf_desc)1324c6ba7c9bSHans Wippel static void smcr_buf_free(struct smc_link_group *lgr, bool is_rmb,
13256511aad3SHans Wippel 			  struct smc_buf_desc *buf_desc)
1326cd6851f3SUrsula Braun {
1327b9247544SKarsten Graul 	int i;
13286511aad3SHans Wippel 
13294a3641c1SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
13304a3641c1SKarsten Graul 		smcr_buf_unmap_link(buf_desc, is_rmb, &lgr->lnk[i]);
1331387707fdSKarsten Graul 
1332b8d19945SWen Gu 	if (!buf_desc->is_vm && buf_desc->pages)
13332ef4f27aSStefan Raspl 		__free_pages(buf_desc->pages, buf_desc->order);
1334b8d19945SWen Gu 	else if (buf_desc->is_vm && buf_desc->cpu_addr)
1335b8d19945SWen Gu 		vfree(buf_desc->cpu_addr);
13363e034725SUrsula Braun 	kfree(buf_desc);
13373e034725SUrsula Braun }
13383e034725SUrsula Braun 
smcd_buf_free(struct smc_link_group * lgr,bool is_dmb,struct smc_buf_desc * buf_desc)1339c6ba7c9bSHans Wippel static void smcd_buf_free(struct smc_link_group *lgr, bool is_dmb,
1340c6ba7c9bSHans Wippel 			  struct smc_buf_desc *buf_desc)
1341c6ba7c9bSHans Wippel {
1342be244f28SHans Wippel 	if (is_dmb) {
1343be244f28SHans Wippel 		/* restore original buf len */
1344be244f28SHans Wippel 		buf_desc->len += sizeof(struct smcd_cdc_msg);
1345c6ba7c9bSHans Wippel 		smc_ism_unregister_dmb(lgr->smcd, buf_desc);
1346be244f28SHans Wippel 	} else {
1347c6ba7c9bSHans Wippel 		kfree(buf_desc->cpu_addr);
1348be244f28SHans Wippel 	}
1349c6ba7c9bSHans Wippel 	kfree(buf_desc);
1350c6ba7c9bSHans Wippel }
1351c6ba7c9bSHans Wippel 
smc_buf_free(struct smc_link_group * lgr,bool is_rmb,struct smc_buf_desc * buf_desc)1352c6ba7c9bSHans Wippel static void smc_buf_free(struct smc_link_group *lgr, bool is_rmb,
1353c6ba7c9bSHans Wippel 			 struct smc_buf_desc *buf_desc)
1354c6ba7c9bSHans Wippel {
1355c6ba7c9bSHans Wippel 	if (lgr->is_smcd)
1356c6ba7c9bSHans Wippel 		smcd_buf_free(lgr, is_rmb, buf_desc);
1357c6ba7c9bSHans Wippel 	else
1358c6ba7c9bSHans Wippel 		smcr_buf_free(lgr, is_rmb, buf_desc);
1359c6ba7c9bSHans Wippel }
1360c6ba7c9bSHans Wippel 
__smc_lgr_free_bufs(struct smc_link_group * lgr,bool is_rmb)13613e034725SUrsula Braun static void __smc_lgr_free_bufs(struct smc_link_group *lgr, bool is_rmb)
13623e034725SUrsula Braun {
13633e034725SUrsula Braun 	struct smc_buf_desc *buf_desc, *bf_desc;
13643e034725SUrsula Braun 	struct list_head *buf_list;
13653e034725SUrsula Braun 	int i;
13663e034725SUrsula Braun 
13673e034725SUrsula Braun 	for (i = 0; i < SMC_RMBE_SIZES; i++) {
13683e034725SUrsula Braun 		if (is_rmb)
13693e034725SUrsula Braun 			buf_list = &lgr->rmbs[i];
13703e034725SUrsula Braun 		else
13713e034725SUrsula Braun 			buf_list = &lgr->sndbufs[i];
13723e034725SUrsula Braun 		list_for_each_entry_safe(buf_desc, bf_desc, buf_list,
13733e034725SUrsula Braun 					 list) {
13743e034725SUrsula Braun 			list_del(&buf_desc->list);
13756511aad3SHans Wippel 			smc_buf_free(lgr, is_rmb, buf_desc);
1376cd6851f3SUrsula Braun 		}
1377cd6851f3SUrsula Braun 	}
1378cd6851f3SUrsula Braun }
1379cd6851f3SUrsula Braun 
smc_lgr_free_bufs(struct smc_link_group * lgr)13803e034725SUrsula Braun static void smc_lgr_free_bufs(struct smc_link_group *lgr)
13813e034725SUrsula Braun {
13823e034725SUrsula Braun 	/* free send buffers */
13833e034725SUrsula Braun 	__smc_lgr_free_bufs(lgr, false);
13843e034725SUrsula Braun 	/* free rmbs */
13853e034725SUrsula Braun 	__smc_lgr_free_bufs(lgr, true);
13863e034725SUrsula Braun }
13873e034725SUrsula Braun 
138861f434b0SWen Gu /* won't be freed until no one accesses to lgr anymore */
__smc_lgr_free(struct smc_link_group * lgr)138961f434b0SWen Gu static void __smc_lgr_free(struct smc_link_group *lgr)
139061f434b0SWen Gu {
139161f434b0SWen Gu 	smc_lgr_free_bufs(lgr);
139261f434b0SWen Gu 	if (lgr->is_smcd) {
139361f434b0SWen Gu 		if (!atomic_dec_return(&lgr->smcd->lgr_cnt))
139461f434b0SWen Gu 			wake_up(&lgr->smcd->lgrs_deleted);
139561f434b0SWen Gu 	} else {
139661f434b0SWen Gu 		smc_wr_free_lgr_mem(lgr);
139761f434b0SWen Gu 		if (!atomic_dec_return(&lgr_cnt))
139861f434b0SWen Gu 			wake_up(&lgrs_deleted);
139961f434b0SWen Gu 	}
140061f434b0SWen Gu 	kfree(lgr);
140161f434b0SWen Gu }
140261f434b0SWen Gu 
14030cfdd8f9SUrsula Braun /* remove a link group */
smc_lgr_free(struct smc_link_group * lgr)14043f3f0e36SUrsula Braun static void smc_lgr_free(struct smc_link_group *lgr)
14050cfdd8f9SUrsula Braun {
1406b9247544SKarsten Graul 	int i;
1407b9247544SKarsten Graul 
1408a52bcc91SKarsten Graul 	if (!lgr->is_smcd) {
1409b5dd4d69SD. Wythe 		down_write(&lgr->llc_conf_mutex);
1410a52bcc91SKarsten Graul 		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
1411a52bcc91SKarsten Graul 			if (lgr->lnk[i].state != SMC_LNK_UNUSED)
14120a99be43SKarsten Graul 				smcr_link_clear(&lgr->lnk[i], false);
1413a52bcc91SKarsten Graul 		}
1414b5dd4d69SD. Wythe 		up_write(&lgr->llc_conf_mutex);
1415a52bcc91SKarsten Graul 		smc_llc_lgr_clear(lgr);
1416a52bcc91SKarsten Graul 	}
1417a52bcc91SKarsten Graul 
141822ef473dSKarsten Graul 	destroy_workqueue(lgr->tx_wq);
1419b3cb53c0SUrsula Braun 	if (lgr->is_smcd) {
1420c6ba7c9bSHans Wippel 		smc_ism_put_vlan(lgr->smcd, lgr->vlan_id);
14218c81ba20SStefan Raspl 		put_device(lgr->smcd->ops->get_dev(lgr->smcd));
1422b3cb53c0SUrsula Braun 	}
142361f434b0SWen Gu 	smc_lgr_put(lgr); /* theoretically last lgr_put */
142461f434b0SWen Gu }
142561f434b0SWen Gu 
smc_lgr_hold(struct smc_link_group * lgr)142661f434b0SWen Gu void smc_lgr_hold(struct smc_link_group *lgr)
142761f434b0SWen Gu {
142861f434b0SWen Gu 	refcount_inc(&lgr->refcnt);
142961f434b0SWen Gu }
143061f434b0SWen Gu 
smc_lgr_put(struct smc_link_group * lgr)143161f434b0SWen Gu void smc_lgr_put(struct smc_link_group *lgr)
143261f434b0SWen Gu {
143361f434b0SWen Gu 	if (refcount_dec_and_test(&lgr->refcnt))
143461f434b0SWen Gu 		__smc_lgr_free(lgr);
14350cfdd8f9SUrsula Braun }
14360cfdd8f9SUrsula Braun 
smc_sk_wake_ups(struct smc_sock * smc)14372a0674ffSUrsula Braun static void smc_sk_wake_ups(struct smc_sock *smc)
14382a0674ffSUrsula Braun {
14392a0674ffSUrsula Braun 	smc->sk.sk_write_space(&smc->sk);
14402a0674ffSUrsula Braun 	smc->sk.sk_data_ready(&smc->sk);
14412a0674ffSUrsula Braun 	smc->sk.sk_state_change(&smc->sk);
14422a0674ffSUrsula Braun }
14432a0674ffSUrsula Braun 
14442a0674ffSUrsula Braun /* kill a connection */
smc_conn_kill(struct smc_connection * conn,bool soft)14455421ec28SUrsula Braun static void smc_conn_kill(struct smc_connection *conn, bool soft)
14462a0674ffSUrsula Braun {
14472a0674ffSUrsula Braun 	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
14482a0674ffSUrsula Braun 
144950c6b20eSUrsula Braun 	if (conn->lgr->is_smcd && conn->lgr->peer_shutdown)
145050c6b20eSUrsula Braun 		conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
145150c6b20eSUrsula Braun 	else
14522a0674ffSUrsula Braun 		smc_close_abort(conn);
14532a0674ffSUrsula Braun 	conn->killed = 1;
14542a0674ffSUrsula Braun 	smc->sk.sk_err = ECONNABORTED;
145550c6b20eSUrsula Braun 	smc_sk_wake_ups(smc);
145642bfba9eSUrsula Braun 	if (conn->lgr->is_smcd) {
145742bfba9eSUrsula Braun 		smc_ism_unset_conn(conn);
145821f6f41eSWen Gu 		if (smc_ism_support_dmb_nocopy(conn->lgr->smcd))
145921f6f41eSWen Gu 			smcd_buf_detach(conn);
14605421ec28SUrsula Braun 		if (soft)
146150c6b20eSUrsula Braun 			tasklet_kill(&conn->rx_tsklet);
14625421ec28SUrsula Braun 		else
14635421ec28SUrsula Braun 			tasklet_unlock_wait(&conn->rx_tsklet);
14646a37ad3dSUrsula Braun 	} else {
1465349d4312SDust Li 		smc_cdc_wait_pend_tx_wr(conn);
146642bfba9eSUrsula Braun 	}
146750c6b20eSUrsula Braun 	smc_lgr_unregister_conn(conn);
146881cf4f47SUrsula Braun 	smc_close_active_abort(smc);
14692a0674ffSUrsula Braun }
14702a0674ffSUrsula Braun 
smc_lgr_cleanup(struct smc_link_group * lgr)147142bfba9eSUrsula Braun static void smc_lgr_cleanup(struct smc_link_group *lgr)
147242bfba9eSUrsula Braun {
147342bfba9eSUrsula Braun 	if (lgr->is_smcd) {
147442bfba9eSUrsula Braun 		smc_ism_signal_shutdown(lgr);
147542bfba9eSUrsula Braun 	} else {
14763e0c40afSKarsten Graul 		u32 rsn = lgr->llc_termination_rsn;
14773e0c40afSKarsten Graul 
14783e0c40afSKarsten Graul 		if (!rsn)
14793e0c40afSKarsten Graul 			rsn = SMC_LLC_DEL_PROG_INIT_TERM;
14803e0c40afSKarsten Graul 		smc_llc_send_link_delete_all(lgr, false, rsn);
1481a52bcc91SKarsten Graul 		smcr_lgr_link_deactivate_all(lgr);
148242bfba9eSUrsula Braun 	}
1483b9247544SKarsten Graul }
148442bfba9eSUrsula Braun 
1485ba952060SKarsten Graul /* terminate link group
1486ba952060SKarsten Graul  * @soft: true if link group shutdown can take its time
1487ba952060SKarsten Graul  *	  false if immediate link group shutdown is required
1488ba952060SKarsten Graul  */
__smc_lgr_terminate(struct smc_link_group * lgr,bool soft)14895421ec28SUrsula Braun static void __smc_lgr_terminate(struct smc_link_group *lgr, bool soft)
14900cfdd8f9SUrsula Braun {
14910cfdd8f9SUrsula Braun 	struct smc_connection *conn;
1492b38d7324SUrsula Braun 	struct smc_sock *smc;
14930cfdd8f9SUrsula Braun 	struct rb_node *node;
14940cfdd8f9SUrsula Braun 
1495517c300eSKarsten Graul 	if (lgr->terminating)
1496517c300eSKarsten Graul 		return;	/* lgr already terminating */
1497a52bcc91SKarsten Graul 	/* cancel free_work sync, will terminate when lgr->freeing is set */
149813085e1bSWenjia Zhang 	cancel_delayed_work(&lgr->free_work);
1499517c300eSKarsten Graul 	lgr->terminating = 1;
15000cfdd8f9SUrsula Braun 
150169318b52SUrsula Braun 	/* kill remaining link group connections */
150269318b52SUrsula Braun 	read_lock_bh(&lgr->conns_lock);
15030cfdd8f9SUrsula Braun 	node = rb_first(&lgr->conns_all);
15040cfdd8f9SUrsula Braun 	while (node) {
150569318b52SUrsula Braun 		read_unlock_bh(&lgr->conns_lock);
15060cfdd8f9SUrsula Braun 		conn = rb_entry(node, struct smc_connection, alert_node);
1507b38d7324SUrsula Braun 		smc = container_of(conn, struct smc_sock, conn);
150881cf4f47SUrsula Braun 		sock_hold(&smc->sk); /* sock_put below */
150969318b52SUrsula Braun 		lock_sock(&smc->sk);
15105421ec28SUrsula Braun 		smc_conn_kill(conn, soft);
151169318b52SUrsula Braun 		release_sock(&smc->sk);
151281cf4f47SUrsula Braun 		sock_put(&smc->sk); /* sock_hold above */
151369318b52SUrsula Braun 		read_lock_bh(&lgr->conns_lock);
15140cfdd8f9SUrsula Braun 		node = rb_first(&lgr->conns_all);
15150cfdd8f9SUrsula Braun 	}
151669318b52SUrsula Braun 	read_unlock_bh(&lgr->conns_lock);
151742bfba9eSUrsula Braun 	smc_lgr_cleanup(lgr);
15185421ec28SUrsula Braun 	smc_lgr_free(lgr);
15190cfdd8f9SUrsula Braun }
15200cfdd8f9SUrsula Braun 
15215f78fe96SKarsten Graul /* unlink link group and schedule termination */
smc_lgr_terminate_sched(struct smc_link_group * lgr)15225f78fe96SKarsten Graul void smc_lgr_terminate_sched(struct smc_link_group *lgr)
1523b9f227c3SHans Wippel {
1524a0a62ee1SUrsula Braun 	spinlock_t *lgr_lock;
1525a0a62ee1SUrsula Braun 
1526a0a62ee1SUrsula Braun 	smc_lgr_list_head(lgr, &lgr_lock);
1527a0a62ee1SUrsula Braun 	spin_lock_bh(lgr_lock);
15283739707cSKarsten Graul 	if (list_empty(&lgr->list) || lgr->terminating || lgr->freeing) {
1529a0a62ee1SUrsula Braun 		spin_unlock_bh(lgr_lock);
15308caa6544SUrsula Braun 		return;	/* lgr already terminating */
15318caa6544SUrsula Braun 	}
15328caa6544SUrsula Braun 	list_del_init(&lgr->list);
1533a52bcc91SKarsten Graul 	lgr->freeing = 1;
15348caa6544SUrsula Braun 	spin_unlock_bh(lgr_lock);
15355f78fe96SKarsten Graul 	schedule_work(&lgr->terminate_work);
1536b9f227c3SHans Wippel }
1537b9f227c3SHans Wippel 
15385421ec28SUrsula Braun /* Called when peer lgr shutdown (regularly or abnormally) is received */
smc_smcd_terminate(struct smcd_dev * dev,struct smcd_gid * peer_gid,unsigned short vlan)15397e5ef8ebSWen Gu void smc_smcd_terminate(struct smcd_dev *dev, struct smcd_gid *peer_gid,
15407e5ef8ebSWen Gu 			unsigned short vlan)
1541c6ba7c9bSHans Wippel {
1542c6ba7c9bSHans Wippel 	struct smc_link_group *lgr, *l;
1543c6ba7c9bSHans Wippel 	LIST_HEAD(lgr_free_list);
1544c6ba7c9bSHans Wippel 
1545c6ba7c9bSHans Wippel 	/* run common cleanup function and build free list */
1546a0a62ee1SUrsula Braun 	spin_lock_bh(&dev->lgr_lock);
1547a2351c5dSUrsula Braun 	list_for_each_entry_safe(lgr, l, &dev->lgr_list, list) {
15487e5ef8ebSWen Gu 		if ((!peer_gid->gid ||
15497e5ef8ebSWen Gu 		     (lgr->peer_gid.gid == peer_gid->gid &&
15507e5ef8ebSWen Gu 		      !smc_ism_is_virtual(dev) ? 1 :
15517e5ef8ebSWen Gu 		      lgr->peer_gid.gid_ext == peer_gid->gid_ext)) &&
15520512f69eSHans Wippel 		    (vlan == VLAN_VID_MASK || lgr->vlan_id == vlan)) {
15537e5ef8ebSWen Gu 			if (peer_gid->gid) /* peer triggered termination */
155450c6b20eSUrsula Braun 				lgr->peer_shutdown = 1;
1555c6ba7c9bSHans Wippel 			list_move(&lgr->list, &lgr_free_list);
1556a52bcc91SKarsten Graul 			lgr->freeing = 1;
1557c6ba7c9bSHans Wippel 		}
1558c6ba7c9bSHans Wippel 	}
1559a0a62ee1SUrsula Braun 	spin_unlock_bh(&dev->lgr_lock);
1560c6ba7c9bSHans Wippel 
1561c6ba7c9bSHans Wippel 	/* cancel the regular free workers and actually free lgrs */
1562c6ba7c9bSHans Wippel 	list_for_each_entry_safe(lgr, l, &lgr_free_list, list) {
1563c6ba7c9bSHans Wippel 		list_del_init(&lgr->list);
156450c6b20eSUrsula Braun 		schedule_work(&lgr->terminate_work);
1565c6ba7c9bSHans Wippel 	}
1566c6ba7c9bSHans Wippel }
1567c6ba7c9bSHans Wippel 
15685421ec28SUrsula Braun /* Called when an SMCD device is removed or the smc module is unloaded */
smc_smcd_terminate_all(struct smcd_dev * smcd)15695421ec28SUrsula Braun void smc_smcd_terminate_all(struct smcd_dev *smcd)
15705421ec28SUrsula Braun {
15715421ec28SUrsula Braun 	struct smc_link_group *lgr, *lg;
15725421ec28SUrsula Braun 	LIST_HEAD(lgr_free_list);
15735421ec28SUrsula Braun 
15745421ec28SUrsula Braun 	spin_lock_bh(&smcd->lgr_lock);
15755421ec28SUrsula Braun 	list_splice_init(&smcd->lgr_list, &lgr_free_list);
15765421ec28SUrsula Braun 	list_for_each_entry(lgr, &lgr_free_list, list)
15775421ec28SUrsula Braun 		lgr->freeing = 1;
15785421ec28SUrsula Braun 	spin_unlock_bh(&smcd->lgr_lock);
15795421ec28SUrsula Braun 
15805421ec28SUrsula Braun 	list_for_each_entry_safe(lgr, lg, &lgr_free_list, list) {
15815421ec28SUrsula Braun 		list_del_init(&lgr->list);
15825421ec28SUrsula Braun 		__smc_lgr_terminate(lgr, false);
15835421ec28SUrsula Braun 	}
15845edd6b9cSUrsula Braun 
15855edd6b9cSUrsula Braun 	if (atomic_read(&smcd->lgr_cnt))
15865edd6b9cSUrsula Braun 		wait_event(smcd->lgrs_deleted, !atomic_read(&smcd->lgr_cnt));
15875421ec28SUrsula Braun }
15885421ec28SUrsula Braun 
15890b29ec64SUrsula Braun /* Called when an SMCR device is removed or the smc module is unloaded.
15900b29ec64SUrsula Braun  * If smcibdev is given, all SMCR link groups using this device are terminated.
15910b29ec64SUrsula Braun  * If smcibdev is NULL, all SMCR link groups are terminated.
15920b29ec64SUrsula Braun  */
smc_smcr_terminate_all(struct smc_ib_device * smcibdev)15930b29ec64SUrsula Braun void smc_smcr_terminate_all(struct smc_ib_device *smcibdev)
15940b29ec64SUrsula Braun {
15950b29ec64SUrsula Braun 	struct smc_link_group *lgr, *lg;
15960b29ec64SUrsula Braun 	LIST_HEAD(lgr_free_list);
1597b9247544SKarsten Graul 	int i;
15980b29ec64SUrsula Braun 
15990b29ec64SUrsula Braun 	spin_lock_bh(&smc_lgr_list.lock);
16000b29ec64SUrsula Braun 	if (!smcibdev) {
16010b29ec64SUrsula Braun 		list_splice_init(&smc_lgr_list.list, &lgr_free_list);
16020b29ec64SUrsula Braun 		list_for_each_entry(lgr, &lgr_free_list, list)
16030b29ec64SUrsula Braun 			lgr->freeing = 1;
16040b29ec64SUrsula Braun 	} else {
16050b29ec64SUrsula Braun 		list_for_each_entry_safe(lgr, lg, &smc_lgr_list.list, list) {
1606b9247544SKarsten Graul 			for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
160787523930SKarsten Graul 				if (lgr->lnk[i].smcibdev == smcibdev)
160856d99e81SWen Gu 					smcr_link_down_cond_sched(&lgr->lnk[i]);
16090b29ec64SUrsula Braun 			}
16100b29ec64SUrsula Braun 		}
16110b29ec64SUrsula Braun 	}
16120b29ec64SUrsula Braun 	spin_unlock_bh(&smc_lgr_list.lock);
16130b29ec64SUrsula Braun 
16140b29ec64SUrsula Braun 	list_for_each_entry_safe(lgr, lg, &lgr_free_list, list) {
16150b29ec64SUrsula Braun 		list_del_init(&lgr->list);
16163e0c40afSKarsten Graul 		smc_llc_set_termination_rsn(lgr, SMC_LLC_DEL_OP_INIT_TERM);
16170b29ec64SUrsula Braun 		__smc_lgr_terminate(lgr, false);
16180b29ec64SUrsula Braun 	}
16196dabd405SUrsula Braun 
16206dabd405SUrsula Braun 	if (smcibdev) {
16216dabd405SUrsula Braun 		if (atomic_read(&smcibdev->lnk_cnt))
16226dabd405SUrsula Braun 			wait_event(smcibdev->lnks_deleted,
16236dabd405SUrsula Braun 				   !atomic_read(&smcibdev->lnk_cnt));
16246dabd405SUrsula Braun 	} else {
16256dabd405SUrsula Braun 		if (atomic_read(&lgr_cnt))
16266dabd405SUrsula Braun 			wait_event(lgrs_deleted, !atomic_read(&lgr_cnt));
16276dabd405SUrsula Braun 	}
16280b29ec64SUrsula Braun }
16290b29ec64SUrsula Braun 
1630ad6c111bSKarsten Graul /* set new lgr type and clear all asymmetric link tagging */
smcr_lgr_set_type(struct smc_link_group * lgr,enum smc_lgr_type new_type)1631ad6c111bSKarsten Graul void smcr_lgr_set_type(struct smc_link_group *lgr, enum smc_lgr_type new_type)
1632ad6c111bSKarsten Graul {
16330a99be43SKarsten Graul 	char *lgr_type = "";
1634ad6c111bSKarsten Graul 	int i;
1635ad6c111bSKarsten Graul 
1636ad6c111bSKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
1637ad6c111bSKarsten Graul 		if (smc_link_usable(&lgr->lnk[i]))
1638ad6c111bSKarsten Graul 			lgr->lnk[i].link_is_asym = false;
16390a99be43SKarsten Graul 	if (lgr->type == new_type)
16400a99be43SKarsten Graul 		return;
1641ad6c111bSKarsten Graul 	lgr->type = new_type;
16420a99be43SKarsten Graul 
16430a99be43SKarsten Graul 	switch (lgr->type) {
16440a99be43SKarsten Graul 	case SMC_LGR_NONE:
16450a99be43SKarsten Graul 		lgr_type = "NONE";
16460a99be43SKarsten Graul 		break;
16470a99be43SKarsten Graul 	case SMC_LGR_SINGLE:
16480a99be43SKarsten Graul 		lgr_type = "SINGLE";
16490a99be43SKarsten Graul 		break;
16500a99be43SKarsten Graul 	case SMC_LGR_SYMMETRIC:
16510a99be43SKarsten Graul 		lgr_type = "SYMMETRIC";
16520a99be43SKarsten Graul 		break;
16530a99be43SKarsten Graul 	case SMC_LGR_ASYMMETRIC_PEER:
16540a99be43SKarsten Graul 		lgr_type = "ASYMMETRIC_PEER";
16550a99be43SKarsten Graul 		break;
16560a99be43SKarsten Graul 	case SMC_LGR_ASYMMETRIC_LOCAL:
16570a99be43SKarsten Graul 		lgr_type = "ASYMMETRIC_LOCAL";
16580a99be43SKarsten Graul 		break;
16590a99be43SKarsten Graul 	}
1660de2fea7bSTony Lu 	pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu state changed: "
16610a99be43SKarsten Graul 			    "%s, pnetid %.16s\n", SMC_LGR_ID_SIZE, &lgr->id,
1662de2fea7bSTony Lu 			    lgr->net->net_cookie, lgr_type, lgr->pnet_id);
1663ad6c111bSKarsten Graul }
1664ad6c111bSKarsten Graul 
1665ad6c111bSKarsten Graul /* set new lgr type and tag a link as asymmetric */
smcr_lgr_set_type_asym(struct smc_link_group * lgr,enum smc_lgr_type new_type,int asym_lnk_idx)1666ad6c111bSKarsten Graul void smcr_lgr_set_type_asym(struct smc_link_group *lgr,
1667ad6c111bSKarsten Graul 			    enum smc_lgr_type new_type, int asym_lnk_idx)
1668ad6c111bSKarsten Graul {
1669ad6c111bSKarsten Graul 	smcr_lgr_set_type(lgr, new_type);
1670ad6c111bSKarsten Graul 	lgr->lnk[asym_lnk_idx].link_is_asym = true;
1671ad6c111bSKarsten Graul }
1672ad6c111bSKarsten Graul 
1673b286a065SKarsten Graul /* abort connection, abort_work scheduled from tasklet context */
smc_conn_abort_work(struct work_struct * work)1674b286a065SKarsten Graul static void smc_conn_abort_work(struct work_struct *work)
1675b286a065SKarsten Graul {
1676b286a065SKarsten Graul 	struct smc_connection *conn = container_of(work,
1677b286a065SKarsten Graul 						   struct smc_connection,
1678b286a065SKarsten Graul 						   abort_work);
1679b286a065SKarsten Graul 	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
1680b286a065SKarsten Graul 
1681a18cee47SKarsten Graul 	lock_sock(&smc->sk);
1682b286a065SKarsten Graul 	smc_conn_kill(conn, true);
1683a18cee47SKarsten Graul 	release_sock(&smc->sk);
1684b286a065SKarsten Graul 	sock_put(&smc->sk); /* sock_hold done by schedulers of abort_work */
1685b286a065SKarsten Graul }
1686b286a065SKarsten Graul 
smcr_port_add(struct smc_ib_device * smcibdev,u8 ibport)16871f90a05dSKarsten Graul void smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport)
16881f90a05dSKarsten Graul {
16891f90a05dSKarsten Graul 	struct smc_link_group *lgr, *n;
16901f90a05dSKarsten Graul 
1691f5146e3eSGuangguan Wang 	spin_lock_bh(&smc_lgr_list.lock);
16921f90a05dSKarsten Graul 	list_for_each_entry_safe(lgr, n, &smc_lgr_list.list, list) {
1693c48254faSKarsten Graul 		struct smc_link *link;
1694c48254faSKarsten Graul 
16951f90a05dSKarsten Graul 		if (strncmp(smcibdev->pnetid[ibport - 1], lgr->pnet_id,
16961f90a05dSKarsten Graul 			    SMC_MAX_PNETID_LEN) ||
16971f90a05dSKarsten Graul 		    lgr->type == SMC_LGR_SYMMETRIC ||
16980237a3a6STony Lu 		    lgr->type == SMC_LGR_ASYMMETRIC_PEER ||
16990237a3a6STony Lu 		    !rdma_dev_access_netns(smcibdev->ibdev, lgr->net))
17001f90a05dSKarsten Graul 			continue;
1701c48254faSKarsten Graul 
170269b888e3SGuangguan Wang 		if (lgr->type == SMC_LGR_SINGLE && lgr->max_links <= 1)
170369b888e3SGuangguan Wang 			continue;
170469b888e3SGuangguan Wang 
1705c48254faSKarsten Graul 		/* trigger local add link processing */
1706c48254faSKarsten Graul 		link = smc_llc_usable_link(lgr);
1707c48254faSKarsten Graul 		if (link)
1708c48254faSKarsten Graul 			smc_llc_add_link_local(link);
17091f90a05dSKarsten Graul 	}
1710f5146e3eSGuangguan Wang 	spin_unlock_bh(&smc_lgr_list.lock);
17111f90a05dSKarsten Graul }
17121f90a05dSKarsten Graul 
1713541afa10SKarsten Graul /* link is down - switch connections to alternate link,
1714541afa10SKarsten Graul  * must be called under lgr->llc_conf_mutex lock
1715541afa10SKarsten Graul  */
smcr_link_down(struct smc_link * lnk)1716541afa10SKarsten Graul static void smcr_link_down(struct smc_link *lnk)
1717541afa10SKarsten Graul {
1718541afa10SKarsten Graul 	struct smc_link_group *lgr = lnk->lgr;
1719541afa10SKarsten Graul 	struct smc_link *to_lnk;
1720541afa10SKarsten Graul 	int del_link_id;
1721541afa10SKarsten Graul 
1722541afa10SKarsten Graul 	if (!lgr || lnk->state == SMC_LNK_UNUSED || list_empty(&lgr->list))
1723541afa10SKarsten Graul 		return;
1724541afa10SKarsten Graul 
1725c6f02ebeSKarsten Graul 	to_lnk = smc_switch_conns(lgr, lnk, true);
1726541afa10SKarsten Graul 	if (!to_lnk) { /* no backup link available */
17270a99be43SKarsten Graul 		smcr_link_clear(lnk, true);
1728541afa10SKarsten Graul 		return;
1729541afa10SKarsten Graul 	}
1730ad6c111bSKarsten Graul 	smcr_lgr_set_type(lgr, SMC_LGR_SINGLE);
1731541afa10SKarsten Graul 	del_link_id = lnk->link_id;
1732541afa10SKarsten Graul 
1733541afa10SKarsten Graul 	if (lgr->role == SMC_SERV) {
1734541afa10SKarsten Graul 		/* trigger local delete link processing */
17354dadd151SKarsten Graul 		smc_llc_srv_delete_link_local(to_lnk, del_link_id);
1736541afa10SKarsten Graul 	} else {
1737541afa10SKarsten Graul 		if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
1738541afa10SKarsten Graul 			/* another llc task is ongoing */
1739b5dd4d69SD. Wythe 			up_write(&lgr->llc_conf_mutex);
17406778a6beSKarsten Graul 			wait_event_timeout(lgr->llc_flow_waiter,
17416778a6beSKarsten Graul 				(list_empty(&lgr->list) ||
17426778a6beSKarsten Graul 				 lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE),
1743541afa10SKarsten Graul 				SMC_LLC_WAIT_TIME);
1744b5dd4d69SD. Wythe 			down_write(&lgr->llc_conf_mutex);
1745541afa10SKarsten Graul 		}
174668fd8942SKarsten Graul 		if (!list_empty(&lgr->list)) {
17476778a6beSKarsten Graul 			smc_llc_send_delete_link(to_lnk, del_link_id,
17486778a6beSKarsten Graul 						 SMC_LLC_REQ, true,
1749541afa10SKarsten Graul 						 SMC_LLC_DEL_LOST_PATH);
175068fd8942SKarsten Graul 			smcr_link_clear(lnk, true);
175168fd8942SKarsten Graul 		}
17526778a6beSKarsten Graul 		wake_up(&lgr->llc_flow_waiter);	/* wake up next waiter */
1753541afa10SKarsten Graul 	}
1754541afa10SKarsten Graul }
1755541afa10SKarsten Graul 
1756541afa10SKarsten Graul /* must be called under lgr->llc_conf_mutex lock */
smcr_link_down_cond(struct smc_link * lnk)1757541afa10SKarsten Graul void smcr_link_down_cond(struct smc_link *lnk)
1758541afa10SKarsten Graul {
1759a3a0e81bSTony Lu 	if (smc_link_downing(&lnk->state)) {
1760a3a0e81bSTony Lu 		trace_smcr_link_down(lnk, __builtin_return_address(0));
1761541afa10SKarsten Graul 		smcr_link_down(lnk);
1762541afa10SKarsten Graul 	}
1763a3a0e81bSTony Lu }
1764541afa10SKarsten Graul 
1765541afa10SKarsten Graul /* will get the lgr->llc_conf_mutex lock */
smcr_link_down_cond_sched(struct smc_link * lnk)1766541afa10SKarsten Graul void smcr_link_down_cond_sched(struct smc_link *lnk)
1767541afa10SKarsten Graul {
1768a3a0e81bSTony Lu 	if (smc_link_downing(&lnk->state)) {
1769a3a0e81bSTony Lu 		trace_smcr_link_down(lnk, __builtin_return_address(0));
1770*2627c3e8SGuangguan Wang 		smcr_link_hold(lnk); /* smcr_link_put in link_down_wrk */
1771*2627c3e8SGuangguan Wang 		if (!schedule_work(&lnk->link_down_wrk))
1772*2627c3e8SGuangguan Wang 			smcr_link_put(lnk);
1773541afa10SKarsten Graul 	}
1774a3a0e81bSTony Lu }
1775541afa10SKarsten Graul 
smcr_port_err(struct smc_ib_device * smcibdev,u8 ibport)1776541afa10SKarsten Graul void smcr_port_err(struct smc_ib_device *smcibdev, u8 ibport)
1777541afa10SKarsten Graul {
1778541afa10SKarsten Graul 	struct smc_link_group *lgr, *n;
1779541afa10SKarsten Graul 	int i;
1780541afa10SKarsten Graul 
1781541afa10SKarsten Graul 	list_for_each_entry_safe(lgr, n, &smc_lgr_list.list, list) {
1782541afa10SKarsten Graul 		if (strncmp(smcibdev->pnetid[ibport - 1], lgr->pnet_id,
1783541afa10SKarsten Graul 			    SMC_MAX_PNETID_LEN))
1784541afa10SKarsten Graul 			continue; /* lgr is not affected */
1785541afa10SKarsten Graul 		if (list_empty(&lgr->list))
1786541afa10SKarsten Graul 			continue;
1787541afa10SKarsten Graul 		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
1788541afa10SKarsten Graul 			struct smc_link *lnk = &lgr->lnk[i];
1789541afa10SKarsten Graul 
1790541afa10SKarsten Graul 			if (smc_link_usable(lnk) &&
1791541afa10SKarsten Graul 			    lnk->smcibdev == smcibdev && lnk->ibport == ibport)
1792541afa10SKarsten Graul 				smcr_link_down_cond_sched(lnk);
1793541afa10SKarsten Graul 		}
1794541afa10SKarsten Graul 	}
1795541afa10SKarsten Graul }
1796541afa10SKarsten Graul 
smc_link_down_work(struct work_struct * work)1797541afa10SKarsten Graul static void smc_link_down_work(struct work_struct *work)
1798541afa10SKarsten Graul {
1799541afa10SKarsten Graul 	struct smc_link *link = container_of(work, struct smc_link,
1800541afa10SKarsten Graul 					     link_down_wrk);
1801541afa10SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
1802541afa10SKarsten Graul 
1803541afa10SKarsten Graul 	if (list_empty(&lgr->list))
1804*2627c3e8SGuangguan Wang 		goto out;
18056778a6beSKarsten Graul 	wake_up_all(&lgr->llc_msg_waiter);
1806b5dd4d69SD. Wythe 	down_write(&lgr->llc_conf_mutex);
1807541afa10SKarsten Graul 	smcr_link_down(link);
1808b5dd4d69SD. Wythe 	up_write(&lgr->llc_conf_mutex);
1809*2627c3e8SGuangguan Wang 
1810*2627c3e8SGuangguan Wang out:
1811*2627c3e8SGuangguan Wang 	smcr_link_put(link); /* smcr_link_hold by schedulers of link_down_work */
1812541afa10SKarsten Graul }
1813541afa10SKarsten Graul 
smc_vlan_by_tcpsk_walk(struct net_device * lower_dev,struct netdev_nested_priv * priv)1814587acad4SKarsten Graul static int smc_vlan_by_tcpsk_walk(struct net_device *lower_dev,
1815587acad4SKarsten Graul 				  struct netdev_nested_priv *priv)
1816587acad4SKarsten Graul {
1817587acad4SKarsten Graul 	unsigned short *vlan_id = (unsigned short *)priv->data;
1818587acad4SKarsten Graul 
1819587acad4SKarsten Graul 	if (is_vlan_dev(lower_dev)) {
1820587acad4SKarsten Graul 		*vlan_id = vlan_dev_vlan_id(lower_dev);
1821587acad4SKarsten Graul 		return 1;
1822587acad4SKarsten Graul 	}
1823587acad4SKarsten Graul 
1824587acad4SKarsten Graul 	return 0;
1825587acad4SKarsten Graul }
1826587acad4SKarsten Graul 
1827587acad4SKarsten Graul /* Determine vlan of internal TCP socket. */
smc_vlan_by_tcpsk(struct socket * clcsock,struct smc_init_info * ini)1828bc36d2fcSKarsten Graul int smc_vlan_by_tcpsk(struct socket *clcsock, struct smc_init_info *ini)
18290cfdd8f9SUrsula Braun {
18300cfdd8f9SUrsula Braun 	struct dst_entry *dst = sk_dst_get(clcsock->sk);
1831587acad4SKarsten Graul 	struct netdev_nested_priv priv;
1832cb9d43f6SUrsula Braun 	struct net_device *ndev;
1833587acad4SKarsten Graul 	int rc = 0;
18340cfdd8f9SUrsula Braun 
1835bc36d2fcSKarsten Graul 	ini->vlan_id = 0;
18360cfdd8f9SUrsula Braun 	if (!dst) {
18370cfdd8f9SUrsula Braun 		rc = -ENOTCONN;
18380cfdd8f9SUrsula Braun 		goto out;
18390cfdd8f9SUrsula Braun 	}
18400cfdd8f9SUrsula Braun 	if (!dst->dev) {
18410cfdd8f9SUrsula Braun 		rc = -ENODEV;
18420cfdd8f9SUrsula Braun 		goto out_rel;
18430cfdd8f9SUrsula Braun 	}
18440cfdd8f9SUrsula Braun 
1845cb9d43f6SUrsula Braun 	ndev = dst->dev;
1846cb9d43f6SUrsula Braun 	if (is_vlan_dev(ndev)) {
1847bc36d2fcSKarsten Graul 		ini->vlan_id = vlan_dev_vlan_id(ndev);
1848cb9d43f6SUrsula Braun 		goto out_rel;
1849cb9d43f6SUrsula Braun 	}
1850cb9d43f6SUrsula Braun 
1851587acad4SKarsten Graul 	priv.data = (void *)&ini->vlan_id;
1852cb9d43f6SUrsula Braun 	rtnl_lock();
1853587acad4SKarsten Graul 	netdev_walk_all_lower_dev(ndev, smc_vlan_by_tcpsk_walk, &priv);
1854cb9d43f6SUrsula Braun 	rtnl_unlock();
18550cfdd8f9SUrsula Braun 
18560cfdd8f9SUrsula Braun out_rel:
18570cfdd8f9SUrsula Braun 	dst_release(dst);
18580cfdd8f9SUrsula Braun out:
18590cfdd8f9SUrsula Braun 	return rc;
18600cfdd8f9SUrsula Braun }
18610cfdd8f9SUrsula Braun 
smcr_lgr_match(struct smc_link_group * lgr,u8 smcr_version,u8 peer_systemid[],u8 peer_gid[],u8 peer_mac_v1[],enum smc_lgr_role role,u32 clcqpn,struct net * net)1862e49300a6SKarsten Graul static bool smcr_lgr_match(struct smc_link_group *lgr, u8 smcr_version,
1863e49300a6SKarsten Graul 			   u8 peer_systemid[],
1864e49300a6SKarsten Graul 			   u8 peer_gid[],
1865e49300a6SKarsten Graul 			   u8 peer_mac_v1[],
18660237a3a6STony Lu 			   enum smc_lgr_role role, u32 clcqpn,
18670237a3a6STony Lu 			   struct net *net)
18680cfdd8f9SUrsula Braun {
18690237a3a6STony Lu 	struct smc_link *lnk;
1870b9247544SKarsten Graul 	int i;
1871b9247544SKarsten Graul 
1872e49300a6SKarsten Graul 	if (memcmp(lgr->peer_systemid, peer_systemid, SMC_SYSTEMID_LEN) ||
1873b9247544SKarsten Graul 	    lgr->role != role)
1874b9247544SKarsten Graul 		return false;
1875b9247544SKarsten Graul 
1876b9247544SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
18770237a3a6STony Lu 		lnk = &lgr->lnk[i];
18780237a3a6STony Lu 
18790237a3a6STony Lu 		if (!smc_link_active(lnk))
1880b9247544SKarsten Graul 			continue;
18810237a3a6STony Lu 		/* use verbs API to check netns, instead of lgr->net */
18820237a3a6STony Lu 		if (!rdma_dev_access_netns(lnk->smcibdev->ibdev, net))
18830237a3a6STony Lu 			return false;
18840237a3a6STony Lu 		if ((lgr->role == SMC_SERV || lnk->peer_qpn == clcqpn) &&
18850237a3a6STony Lu 		    !memcmp(lnk->peer_gid, peer_gid, SMC_GID_SIZE) &&
1886e49300a6SKarsten Graul 		    (smcr_version == SMC_V2 ||
18870237a3a6STony Lu 		     !memcmp(lnk->peer_mac, peer_mac_v1, ETH_ALEN)))
1888b9247544SKarsten Graul 			return true;
1889b9247544SKarsten Graul 	}
1890b9247544SKarsten Graul 	return false;
18910cfdd8f9SUrsula Braun }
18920cfdd8f9SUrsula Braun 
smcd_lgr_match(struct smc_link_group * lgr,struct smcd_dev * smcismdev,struct smcd_gid * peer_gid)1893c6ba7c9bSHans Wippel static bool smcd_lgr_match(struct smc_link_group *lgr,
18947e5ef8ebSWen Gu 			   struct smcd_dev *smcismdev,
18957e5ef8ebSWen Gu 			   struct smcd_gid *peer_gid)
1896c6ba7c9bSHans Wippel {
18973706311eSWen Gu 	if (lgr->peer_gid.gid != peer_gid->gid ||
18983706311eSWen Gu 	    lgr->smcd != smcismdev)
18993706311eSWen Gu 		return false;
19003706311eSWen Gu 
19013706311eSWen Gu 	if (smc_ism_is_virtual(smcismdev) &&
19023706311eSWen Gu 	    lgr->peer_gid.gid_ext != peer_gid->gid_ext)
19033706311eSWen Gu 		return false;
19043706311eSWen Gu 
19053706311eSWen Gu 	return true;
19060cfdd8f9SUrsula Braun }
19070cfdd8f9SUrsula Braun 
19080cfdd8f9SUrsula Braun /* create a new SMC connection (and a new link group if necessary) */
smc_conn_create(struct smc_sock * smc,struct smc_init_info * ini)1909bc36d2fcSKarsten Graul int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini)
19100cfdd8f9SUrsula Braun {
19110cfdd8f9SUrsula Braun 	struct smc_connection *conn = &smc->conn;
19120237a3a6STony Lu 	struct net *net = sock_net(&smc->sk);
1913a2351c5dSUrsula Braun 	struct list_head *lgr_list;
19140cfdd8f9SUrsula Braun 	struct smc_link_group *lgr;
19150cfdd8f9SUrsula Braun 	enum smc_lgr_role role;
1916a0a62ee1SUrsula Braun 	spinlock_t *lgr_lock;
19170cfdd8f9SUrsula Braun 	int rc = 0;
19180cfdd8f9SUrsula Braun 
19195c21c4ccSUrsula Braun 	lgr_list = ini->is_smcd ? &ini->ism_dev[ini->ism_selected]->lgr_list :
19203fc64937SUrsula Braun 				  &smc_lgr_list.list;
19215c21c4ccSUrsula Braun 	lgr_lock = ini->is_smcd ? &ini->ism_dev[ini->ism_selected]->lgr_lock :
19223fc64937SUrsula Braun 				  &smc_lgr_list.lock;
19235ac54d87SUrsula Braun 	ini->first_contact_local = 1;
19240cfdd8f9SUrsula Braun 	role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
19255ac54d87SUrsula Braun 	if (role == SMC_CLNT && ini->first_contact_peer)
19260cfdd8f9SUrsula Braun 		/* create new link group as well */
19270cfdd8f9SUrsula Braun 		goto create;
19280cfdd8f9SUrsula Braun 
19290cfdd8f9SUrsula Braun 	/* determine if an existing link group can be reused */
1930a0a62ee1SUrsula Braun 	spin_lock_bh(lgr_lock);
1931a2351c5dSUrsula Braun 	list_for_each_entry(lgr, lgr_list, list) {
19320cfdd8f9SUrsula Braun 		write_lock_bh(&lgr->conns_lock);
1933bc36d2fcSKarsten Graul 		if ((ini->is_smcd ?
19345c21c4ccSUrsula Braun 		     smcd_lgr_match(lgr, ini->ism_dev[ini->ism_selected],
19357e5ef8ebSWen Gu 				    &ini->ism_peer_gid[ini->ism_selected]) :
1936e49300a6SKarsten Graul 		     smcr_lgr_match(lgr, ini->smcr_version,
1937e49300a6SKarsten Graul 				    ini->peer_systemid,
1938e49300a6SKarsten Graul 				    ini->peer_gid, ini->peer_mac, role,
19390237a3a6STony Lu 				    ini->ib_clcqpn, net)) &&
19400cfdd8f9SUrsula Braun 		    !lgr->sync_err &&
19410530bd6eSKarsten Graul 		    (ini->smcd_version == SMC_V2 ||
19420530bd6eSKarsten Graul 		     lgr->vlan_id == ini->vlan_id) &&
1943a9e44502SKarsten Graul 		    (role == SMC_CLNT || ini->is_smcd ||
19447f0620b9SGuangguan Wang 		    (lgr->conns_num < lgr->max_conns &&
19454940a1fdSD. Wythe 		      !bitmap_full(lgr->rtokens_used_mask, SMC_RMBS_PER_LGR_MAX)))) {
19460cfdd8f9SUrsula Braun 			/* link group found */
19475ac54d87SUrsula Braun 			ini->first_contact_local = 0;
19480cfdd8f9SUrsula Braun 			conn->lgr = lgr;
194956bc3b20SKarsten Graul 			rc = smc_lgr_register_conn(conn, false);
19500cfdd8f9SUrsula Braun 			write_unlock_bh(&lgr->conns_lock);
1951b9247544SKarsten Graul 			if (!rc && delayed_work_pending(&lgr->free_work))
1952b9247544SKarsten Graul 				cancel_delayed_work(&lgr->free_work);
19530cfdd8f9SUrsula Braun 			break;
19540cfdd8f9SUrsula Braun 		}
19550cfdd8f9SUrsula Braun 		write_unlock_bh(&lgr->conns_lock);
19560cfdd8f9SUrsula Braun 	}
1957a0a62ee1SUrsula Braun 	spin_unlock_bh(lgr_lock);
1958b9247544SKarsten Graul 	if (rc)
1959b9247544SKarsten Graul 		return rc;
19600cfdd8f9SUrsula Braun 
19615ac54d87SUrsula Braun 	if (role == SMC_CLNT && !ini->first_contact_peer &&
19625ac54d87SUrsula Braun 	    ini->first_contact_local) {
19630cfdd8f9SUrsula Braun 		/* Server reuses a link group, but Client wants to start
19640cfdd8f9SUrsula Braun 		 * a new one
19650cfdd8f9SUrsula Braun 		 * send out_of_sync decline, reason synchr. error
19660cfdd8f9SUrsula Braun 		 */
19677a62725aSKarsten Graul 		return SMC_CLC_DECL_SYNCERR;
19680cfdd8f9SUrsula Braun 	}
19690cfdd8f9SUrsula Braun 
19700cfdd8f9SUrsula Braun create:
19715ac54d87SUrsula Braun 	if (ini->first_contact_local) {
1972bc36d2fcSKarsten Graul 		rc = smc_lgr_create(smc, ini);
19730cfdd8f9SUrsula Braun 		if (rc)
19740cfdd8f9SUrsula Braun 			goto out;
197544808792SHuaping Zhou 		lgr = conn->lgr;
197644808792SHuaping Zhou 		write_lock_bh(&lgr->conns_lock);
197756bc3b20SKarsten Graul 		rc = smc_lgr_register_conn(conn, true);
197844808792SHuaping Zhou 		write_unlock_bh(&lgr->conns_lock);
197936595d8aSWen Gu 		if (rc) {
198036595d8aSWen Gu 			smc_lgr_cleanup_early(lgr);
1981b9247544SKarsten Graul 			goto out;
19820cfdd8f9SUrsula Braun 		}
198336595d8aSWen Gu 	}
198461f434b0SWen Gu 	smc_lgr_hold(conn->lgr); /* lgr_put in smc_conn_free() */
198520c9398dSWen Gu 	if (!conn->lgr->is_smcd)
198620c9398dSWen Gu 		smcr_link_hold(conn->lnk); /* link_put in smc_conn_free() */
198761f434b0SWen Gu 	conn->freed = 0;
19885f08318fSUrsula Braun 	conn->local_tx_ctrl.common.type = SMC_CDC_MSG_TYPE;
1989cbba07a7SKarsten Graul 	conn->local_tx_ctrl.len = SMC_WR_TX_SIZE;
1990de8474ebSStefan Raspl 	conn->urg_state = SMC_URG_READ;
1991349d4312SDust Li 	init_waitqueue_head(&conn->cdc_pend_tx_wq);
1992b286a065SKarsten Graul 	INIT_WORK(&smc->conn.abort_work, smc_conn_abort_work);
1993bc36d2fcSKarsten Graul 	if (ini->is_smcd) {
1994be244f28SHans Wippel 		conn->rx_off = sizeof(struct smcd_cdc_msg);
1995be244f28SHans Wippel 		smcd_cdc_rx_init(conn); /* init tasklet for this conn */
19962d2bfeb8SUrsula Braun 	} else {
19972d2bfeb8SUrsula Braun 		conn->rx_off = 0;
1998be244f28SHans Wippel 	}
19995f08318fSUrsula Braun #ifndef KERNEL_HAS_ATOMIC64
20005f08318fSUrsula Braun 	spin_lock_init(&conn->acurs_lock);
20015f08318fSUrsula Braun #endif
20020cfdd8f9SUrsula Braun 
20030cfdd8f9SUrsula Braun out:
20047a62725aSKarsten Graul 	return rc;
20050cfdd8f9SUrsula Braun }
2006cd6851f3SUrsula Braun 
200767161779SStefan Raspl #define SMCD_DMBE_SIZES		6 /* 0 -> 16KB, 1 -> 32KB, .. 6 -> 1MB */
200867161779SStefan Raspl #define SMCR_RMBE_SIZES		5 /* 0 -> 16KB, 1 -> 32KB, .. 5 -> 512KB */
200967161779SStefan Raspl 
201067161779SStefan Raspl /* convert the RMB size into the compressed notation (minimum 16K, see
201167161779SStefan Raspl  * SMCD/R_DMBE_SIZES.
20122f6becafSHans Wippel  * In contrast to plain ilog2, this rounds towards the next power of 2,
20132f6becafSHans Wippel  * so the socket application gets at least its desired sndbuf / rcvbuf size.
20142f6becafSHans Wippel  */
smc_compress_bufsize(int size,bool is_smcd,bool is_rmb)201567161779SStefan Raspl static u8 smc_compress_bufsize(int size, bool is_smcd, bool is_rmb)
20162f6becafSHans Wippel {
20172f6becafSHans Wippel 	u8 compressed;
20182f6becafSHans Wippel 
20192f6becafSHans Wippel 	if (size <= SMC_BUF_MIN_SIZE)
20202f6becafSHans Wippel 		return 0;
20212f6becafSHans Wippel 
202267161779SStefan Raspl 	size = (size - 1) >> 14;  /* convert to 16K multiple */
202367161779SStefan Raspl 	compressed = min_t(u8, ilog2(size) + 1,
202467161779SStefan Raspl 			   is_smcd ? SMCD_DMBE_SIZES : SMCR_RMBE_SIZES);
202567161779SStefan Raspl 
2026a9e46f95SGuangguan Wang #ifdef CONFIG_ARCH_NO_SG_CHAIN
202767161779SStefan Raspl 	if (!is_smcd && is_rmb)
202867161779SStefan Raspl 		/* RMBs are backed by & limited to max size of scatterlists */
2029a9e46f95SGuangguan Wang 		compressed = min_t(u8, compressed, ilog2((SG_MAX_SINGLE_ALLOC * PAGE_SIZE) >> 14));
2030a9e46f95SGuangguan Wang #endif
203167161779SStefan Raspl 
20322f6becafSHans Wippel 	return compressed;
20332f6becafSHans Wippel }
20342f6becafSHans Wippel 
20352f6becafSHans Wippel /* convert the RMB size from compressed notation into integer */
smc_uncompress_bufsize(u8 compressed)20362f6becafSHans Wippel int smc_uncompress_bufsize(u8 compressed)
20372f6becafSHans Wippel {
20382f6becafSHans Wippel 	u32 size;
20392f6becafSHans Wippel 
20402f6becafSHans Wippel 	size = 0x00000001 << (((int)compressed) + 14);
20412f6becafSHans Wippel 	return (int)size;
20422f6becafSHans Wippel }
20432f6becafSHans Wippel 
20443e034725SUrsula Braun /* try to reuse a sndbuf or rmb description slot for a certain
20453e034725SUrsula Braun  * buffer size; if not available, return NULL
2046cd6851f3SUrsula Braun  */
smc_buf_get_slot(int compressed_bufsize,struct rw_semaphore * lock,struct list_head * buf_list)20478437bda0SHans Wippel static struct smc_buf_desc *smc_buf_get_slot(int compressed_bufsize,
2048aff7bfedSD. Wythe 					     struct rw_semaphore *lock,
20493e034725SUrsula Braun 					     struct list_head *buf_list)
2050cd6851f3SUrsula Braun {
20513e034725SUrsula Braun 	struct smc_buf_desc *buf_slot;
2052cd6851f3SUrsula Braun 
2053aff7bfedSD. Wythe 	down_read(lock);
20543e034725SUrsula Braun 	list_for_each_entry(buf_slot, buf_list, list) {
20553e034725SUrsula Braun 		if (cmpxchg(&buf_slot->used, 0, 1) == 0) {
2056aff7bfedSD. Wythe 			up_read(lock);
20573e034725SUrsula Braun 			return buf_slot;
2058cd6851f3SUrsula Braun 		}
2059cd6851f3SUrsula Braun 	}
2060aff7bfedSD. Wythe 	up_read(lock);
2061cd6851f3SUrsula Braun 	return NULL;
2062cd6851f3SUrsula Braun }
2063cd6851f3SUrsula Braun 
2064952310ccSUrsula Braun /* one of the conditions for announcing a receiver's current window size is
2065952310ccSUrsula Braun  * that it "results in a minimum increase in the window size of 10% of the
2066952310ccSUrsula Braun  * receive buffer space" [RFC7609]
2067952310ccSUrsula Braun  */
smc_rmb_wnd_update_limit(int rmbe_size)2068952310ccSUrsula Braun static inline int smc_rmb_wnd_update_limit(int rmbe_size)
2069952310ccSUrsula Braun {
20706bf536ebSDust Li 	return max_t(int, rmbe_size / 10, SOCK_MIN_SNDBUF / 2);
2071952310ccSUrsula Braun }
2072952310ccSUrsula Braun 
2073b8d19945SWen Gu /* map an buf to a link */
smcr_buf_map_link(struct smc_buf_desc * buf_desc,bool is_rmb,struct smc_link * lnk)2074b9247544SKarsten Graul static int smcr_buf_map_link(struct smc_buf_desc *buf_desc, bool is_rmb,
2075b9247544SKarsten Graul 			     struct smc_link *lnk)
2076b9247544SKarsten Graul {
2077b8d19945SWen Gu 	int rc, i, nents, offset, buf_size, size, access_flags;
2078b8d19945SWen Gu 	struct scatterlist *sg;
2079b8d19945SWen Gu 	void *buf;
2080b9247544SKarsten Graul 
2081b9247544SKarsten Graul 	if (buf_desc->is_map_ib[lnk->link_idx])
2082b9247544SKarsten Graul 		return 0;
2083b9247544SKarsten Graul 
2084b8d19945SWen Gu 	if (buf_desc->is_vm) {
2085b8d19945SWen Gu 		buf = buf_desc->cpu_addr;
2086b8d19945SWen Gu 		buf_size = buf_desc->len;
2087b8d19945SWen Gu 		offset = offset_in_page(buf_desc->cpu_addr);
2088b8d19945SWen Gu 		nents = PAGE_ALIGN(buf_size + offset) / PAGE_SIZE;
2089b8d19945SWen Gu 	} else {
2090b8d19945SWen Gu 		nents = 1;
2091b8d19945SWen Gu 	}
2092b8d19945SWen Gu 
2093b8d19945SWen Gu 	rc = sg_alloc_table(&buf_desc->sgt[lnk->link_idx], nents, GFP_KERNEL);
2094b9247544SKarsten Graul 	if (rc)
2095b9247544SKarsten Graul 		return rc;
2096b8d19945SWen Gu 
2097b8d19945SWen Gu 	if (buf_desc->is_vm) {
2098b8d19945SWen Gu 		/* virtually contiguous buffer */
2099b8d19945SWen Gu 		for_each_sg(buf_desc->sgt[lnk->link_idx].sgl, sg, nents, i) {
2100b8d19945SWen Gu 			size = min_t(int, PAGE_SIZE - offset, buf_size);
2101b8d19945SWen Gu 			sg_set_page(sg, vmalloc_to_page(buf), size, offset);
2102b8d19945SWen Gu 			buf += size / sizeof(*buf);
2103b8d19945SWen Gu 			buf_size -= size;
2104b8d19945SWen Gu 			offset = 0;
2105b8d19945SWen Gu 		}
2106b8d19945SWen Gu 	} else {
2107b8d19945SWen Gu 		/* physically contiguous buffer */
2108b9247544SKarsten Graul 		sg_set_buf(buf_desc->sgt[lnk->link_idx].sgl,
2109b9247544SKarsten Graul 			   buf_desc->cpu_addr, buf_desc->len);
2110b8d19945SWen Gu 	}
2111b9247544SKarsten Graul 
2112b9247544SKarsten Graul 	/* map sg table to DMA address */
2113b9247544SKarsten Graul 	rc = smc_ib_buf_map_sg(lnk, buf_desc,
2114b9247544SKarsten Graul 			       is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
2115b9247544SKarsten Graul 	/* SMC protocol depends on mapping to one DMA address only */
2116b8d19945SWen Gu 	if (rc != nents) {
2117b9247544SKarsten Graul 		rc = -EAGAIN;
2118b9247544SKarsten Graul 		goto free_table;
2119b9247544SKarsten Graul 	}
2120b9247544SKarsten Graul 
21210ef69e78SGuangguan Wang 	buf_desc->is_dma_need_sync |=
21220ef69e78SGuangguan Wang 		smc_ib_is_sg_need_sync(lnk, buf_desc) << lnk->link_idx;
21230ef69e78SGuangguan Wang 
2124b8d19945SWen Gu 	if (is_rmb || buf_desc->is_vm) {
2125b8d19945SWen Gu 		/* create a new memory region for the RMB or vzalloced sndbuf */
2126b8d19945SWen Gu 		access_flags = is_rmb ?
2127b8d19945SWen Gu 			       IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE :
2128b8d19945SWen Gu 			       IB_ACCESS_LOCAL_WRITE;
2129b8d19945SWen Gu 
2130b8d19945SWen Gu 		rc = smc_ib_get_memory_region(lnk->roce_pd, access_flags,
2131b9247544SKarsten Graul 					      buf_desc, lnk->link_idx);
2132b9247544SKarsten Graul 		if (rc)
2133b9247544SKarsten Graul 			goto buf_unmap;
2134b8d19945SWen Gu 		smc_ib_sync_sg_for_device(lnk, buf_desc,
2135b8d19945SWen Gu 					  is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
2136b9247544SKarsten Graul 	}
2137b9247544SKarsten Graul 	buf_desc->is_map_ib[lnk->link_idx] = true;
2138b9247544SKarsten Graul 	return 0;
2139b9247544SKarsten Graul 
2140b9247544SKarsten Graul buf_unmap:
2141b9247544SKarsten Graul 	smc_ib_buf_unmap_sg(lnk, buf_desc,
2142b9247544SKarsten Graul 			    is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
2143b9247544SKarsten Graul free_table:
2144b9247544SKarsten Graul 	sg_free_table(&buf_desc->sgt[lnk->link_idx]);
2145b9247544SKarsten Graul 	return rc;
2146b9247544SKarsten Graul }
2147b9247544SKarsten Graul 
2148b8d19945SWen Gu /* register a new buf on IB device, rmb or vzalloced sndbuf
2149d5500667SKarsten Graul  * must be called under lgr->llc_conf_mutex lock
2150d5500667SKarsten Graul  */
smcr_link_reg_buf(struct smc_link * link,struct smc_buf_desc * buf_desc)2151b8d19945SWen Gu int smcr_link_reg_buf(struct smc_link *link, struct smc_buf_desc *buf_desc)
21527562a13dSKarsten Graul {
21537562a13dSKarsten Graul 	if (list_empty(&link->lgr->list))
21547562a13dSKarsten Graul 		return -ENOLINK;
2155b8d19945SWen Gu 	if (!buf_desc->is_reg_mr[link->link_idx]) {
2156b8d19945SWen Gu 		/* register memory region for new buf */
2157b8d19945SWen Gu 		if (buf_desc->is_vm)
2158b8d19945SWen Gu 			buf_desc->mr[link->link_idx]->iova =
2159b8d19945SWen Gu 				(uintptr_t)buf_desc->cpu_addr;
2160b8d19945SWen Gu 		if (smc_wr_reg_send(link, buf_desc->mr[link->link_idx])) {
2161b8d19945SWen Gu 			buf_desc->is_reg_err = true;
21627562a13dSKarsten Graul 			return -EFAULT;
21637562a13dSKarsten Graul 		}
2164b8d19945SWen Gu 		buf_desc->is_reg_mr[link->link_idx] = true;
21657562a13dSKarsten Graul 	}
21667562a13dSKarsten Graul 	return 0;
21677562a13dSKarsten Graul }
21687562a13dSKarsten Graul 
_smcr_buf_map_lgr(struct smc_link * lnk,struct rw_semaphore * lock,struct list_head * lst,bool is_rmb)2169aff7bfedSD. Wythe static int _smcr_buf_map_lgr(struct smc_link *lnk, struct rw_semaphore *lock,
2170fb33d277SKarsten Graul 			     struct list_head *lst, bool is_rmb)
2171fb33d277SKarsten Graul {
2172fb33d277SKarsten Graul 	struct smc_buf_desc *buf_desc, *bf;
2173fb33d277SKarsten Graul 	int rc = 0;
2174fb33d277SKarsten Graul 
2175aff7bfedSD. Wythe 	down_write(lock);
2176fb33d277SKarsten Graul 	list_for_each_entry_safe(buf_desc, bf, lst, list) {
2177fb33d277SKarsten Graul 		if (!buf_desc->used)
2178fb33d277SKarsten Graul 			continue;
2179fb33d277SKarsten Graul 		rc = smcr_buf_map_link(buf_desc, is_rmb, lnk);
2180fb33d277SKarsten Graul 		if (rc)
2181fb33d277SKarsten Graul 			goto out;
2182fb33d277SKarsten Graul 	}
2183fb33d277SKarsten Graul out:
2184aff7bfedSD. Wythe 	up_write(lock);
2185fb33d277SKarsten Graul 	return rc;
2186fb33d277SKarsten Graul }
2187fb33d277SKarsten Graul 
2188fb33d277SKarsten Graul /* map all used buffers of lgr for a new link */
smcr_buf_map_lgr(struct smc_link * lnk)2189fb33d277SKarsten Graul int smcr_buf_map_lgr(struct smc_link *lnk)
2190fb33d277SKarsten Graul {
2191fb33d277SKarsten Graul 	struct smc_link_group *lgr = lnk->lgr;
2192fb33d277SKarsten Graul 	int i, rc = 0;
2193fb33d277SKarsten Graul 
2194fb33d277SKarsten Graul 	for (i = 0; i < SMC_RMBE_SIZES; i++) {
2195fb33d277SKarsten Graul 		rc = _smcr_buf_map_lgr(lnk, &lgr->rmbs_lock,
2196fb33d277SKarsten Graul 				       &lgr->rmbs[i], true);
2197fb33d277SKarsten Graul 		if (rc)
2198fb33d277SKarsten Graul 			return rc;
2199fb33d277SKarsten Graul 		rc = _smcr_buf_map_lgr(lnk, &lgr->sndbufs_lock,
2200fb33d277SKarsten Graul 				       &lgr->sndbufs[i], false);
2201fb33d277SKarsten Graul 		if (rc)
2202fb33d277SKarsten Graul 			return rc;
2203fb33d277SKarsten Graul 	}
2204fb33d277SKarsten Graul 	return 0;
2205fb33d277SKarsten Graul }
2206fb33d277SKarsten Graul 
2207d5500667SKarsten Graul /* register all used buffers of lgr for a new link,
2208d5500667SKarsten Graul  * must be called under lgr->llc_conf_mutex lock
2209d5500667SKarsten Graul  */
smcr_buf_reg_lgr(struct smc_link * lnk)2210fb33d277SKarsten Graul int smcr_buf_reg_lgr(struct smc_link *lnk)
2211fb33d277SKarsten Graul {
2212fb33d277SKarsten Graul 	struct smc_link_group *lgr = lnk->lgr;
2213fb33d277SKarsten Graul 	struct smc_buf_desc *buf_desc, *bf;
2214fb33d277SKarsten Graul 	int i, rc = 0;
2215fb33d277SKarsten Graul 
2216b8d19945SWen Gu 	/* reg all RMBs for a new link */
2217aff7bfedSD. Wythe 	down_write(&lgr->rmbs_lock);
2218fb33d277SKarsten Graul 	for (i = 0; i < SMC_RMBE_SIZES; i++) {
2219fb33d277SKarsten Graul 		list_for_each_entry_safe(buf_desc, bf, &lgr->rmbs[i], list) {
2220fb33d277SKarsten Graul 			if (!buf_desc->used)
2221fb33d277SKarsten Graul 				continue;
2222b8d19945SWen Gu 			rc = smcr_link_reg_buf(lnk, buf_desc);
2223b8d19945SWen Gu 			if (rc) {
2224aff7bfedSD. Wythe 				up_write(&lgr->rmbs_lock);
2225fb33d277SKarsten Graul 				return rc;
2226fb33d277SKarsten Graul 			}
2227b8d19945SWen Gu 		}
2228b8d19945SWen Gu 	}
2229aff7bfedSD. Wythe 	up_write(&lgr->rmbs_lock);
2230b8d19945SWen Gu 
2231b8d19945SWen Gu 	if (lgr->buf_type == SMCR_PHYS_CONT_BUFS)
2232b8d19945SWen Gu 		return rc;
2233b8d19945SWen Gu 
2234b8d19945SWen Gu 	/* reg all vzalloced sndbufs for a new link */
2235aff7bfedSD. Wythe 	down_write(&lgr->sndbufs_lock);
2236b8d19945SWen Gu 	for (i = 0; i < SMC_RMBE_SIZES; i++) {
2237b8d19945SWen Gu 		list_for_each_entry_safe(buf_desc, bf, &lgr->sndbufs[i], list) {
2238b8d19945SWen Gu 			if (!buf_desc->used || !buf_desc->is_vm)
2239b8d19945SWen Gu 				continue;
2240b8d19945SWen Gu 			rc = smcr_link_reg_buf(lnk, buf_desc);
2241b8d19945SWen Gu 			if (rc) {
2242aff7bfedSD. Wythe 				up_write(&lgr->sndbufs_lock);
2243b8d19945SWen Gu 				return rc;
2244b8d19945SWen Gu 			}
2245b8d19945SWen Gu 		}
2246b8d19945SWen Gu 	}
2247aff7bfedSD. Wythe 	up_write(&lgr->sndbufs_lock);
2248b8d19945SWen Gu 	return rc;
2249b8d19945SWen Gu }
2250fb33d277SKarsten Graul 
smcr_new_buf_create(struct smc_link_group * lgr,bool is_rmb,int bufsize)2251c6ba7c9bSHans Wippel static struct smc_buf_desc *smcr_new_buf_create(struct smc_link_group *lgr,
2252b33982c3SUrsula Braun 						bool is_rmb, int bufsize)
2253b33982c3SUrsula Braun {
2254b33982c3SUrsula Braun 	struct smc_buf_desc *buf_desc;
2255b33982c3SUrsula Braun 
2256b33982c3SUrsula Braun 	/* try to alloc a new buffer */
2257b33982c3SUrsula Braun 	buf_desc = kzalloc(sizeof(*buf_desc), GFP_KERNEL);
2258b33982c3SUrsula Braun 	if (!buf_desc)
2259b33982c3SUrsula Braun 		return ERR_PTR(-ENOMEM);
2260b33982c3SUrsula Braun 
2261b8d19945SWen Gu 	switch (lgr->buf_type) {
2262b8d19945SWen Gu 	case SMCR_PHYS_CONT_BUFS:
2263b8d19945SWen Gu 	case SMCR_MIXED_BUFS:
22642ef4f27aSStefan Raspl 		buf_desc->order = get_order(bufsize);
22652ef4f27aSStefan Raspl 		buf_desc->pages = alloc_pages(GFP_KERNEL | __GFP_NOWARN |
22662ef4f27aSStefan Raspl 					      __GFP_NOMEMALLOC | __GFP_COMP |
2267b33982c3SUrsula Braun 					      __GFP_NORETRY | __GFP_ZERO,
22682ef4f27aSStefan Raspl 					      buf_desc->order);
2269b8d19945SWen Gu 		if (buf_desc->pages) {
2270b8d19945SWen Gu 			buf_desc->cpu_addr =
2271b8d19945SWen Gu 				(void *)page_address(buf_desc->pages);
2272b8d19945SWen Gu 			buf_desc->len = bufsize;
2273b8d19945SWen Gu 			buf_desc->is_vm = false;
2274b8d19945SWen Gu 			break;
2275b8d19945SWen Gu 		}
2276b8d19945SWen Gu 		if (lgr->buf_type == SMCR_PHYS_CONT_BUFS)
2277b8d19945SWen Gu 			goto out;
2278b8d19945SWen Gu 		fallthrough;	// try virtually continguous buf
2279b8d19945SWen Gu 	case SMCR_VIRT_CONT_BUFS:
2280b8d19945SWen Gu 		buf_desc->order = get_order(bufsize);
2281b8d19945SWen Gu 		buf_desc->cpu_addr = vzalloc(PAGE_SIZE << buf_desc->order);
2282b8d19945SWen Gu 		if (!buf_desc->cpu_addr)
2283b8d19945SWen Gu 			goto out;
2284b8d19945SWen Gu 		buf_desc->pages = NULL;
2285b8d19945SWen Gu 		buf_desc->len = bufsize;
2286b8d19945SWen Gu 		buf_desc->is_vm = true;
2287b8d19945SWen Gu 		break;
2288b8d19945SWen Gu 	}
2289b8d19945SWen Gu 	return buf_desc;
2290b8d19945SWen Gu 
2291b8d19945SWen Gu out:
2292b33982c3SUrsula Braun 	kfree(buf_desc);
2293b33982c3SUrsula Braun 	return ERR_PTR(-EAGAIN);
2294b33982c3SUrsula Braun }
2295b33982c3SUrsula Braun 
2296b9247544SKarsten Graul /* map buf_desc on all usable links,
2297b9247544SKarsten Graul  * unused buffers stay mapped as long as the link is up
2298b9247544SKarsten Graul  */
smcr_buf_map_usable_links(struct smc_link_group * lgr,struct smc_buf_desc * buf_desc,bool is_rmb)2299b9247544SKarsten Graul static int smcr_buf_map_usable_links(struct smc_link_group *lgr,
2300b9247544SKarsten Graul 				     struct smc_buf_desc *buf_desc, bool is_rmb)
2301b9247544SKarsten Graul {
2302e738455bSWen Gu 	int i, rc = 0, cnt = 0;
2303b9247544SKarsten Graul 
2304d5500667SKarsten Graul 	/* protect against parallel link reconfiguration */
2305f6421014SD. Wythe 	down_read(&lgr->llc_conf_mutex);
2306b9247544SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
2307b9247544SKarsten Graul 		struct smc_link *lnk = &lgr->lnk[i];
2308b9247544SKarsten Graul 
2309d854fcbfSKarsten Graul 		if (!smc_link_usable(lnk))
2310b9247544SKarsten Graul 			continue;
2311b9247544SKarsten Graul 		if (smcr_buf_map_link(buf_desc, is_rmb, lnk)) {
2312b9247544SKarsten Graul 			rc = -ENOMEM;
2313b9247544SKarsten Graul 			goto out;
2314b9247544SKarsten Graul 		}
2315e738455bSWen Gu 		cnt++;
2316b9247544SKarsten Graul 	}
2317b9247544SKarsten Graul out:
2318f6421014SD. Wythe 	up_read(&lgr->llc_conf_mutex);
2319e738455bSWen Gu 	if (!rc && !cnt)
2320e738455bSWen Gu 		rc = -EINVAL;
2321b9247544SKarsten Graul 	return rc;
2322b9247544SKarsten Graul }
2323b9247544SKarsten Graul 
smcd_new_buf_create(struct smc_link_group * lgr,bool is_dmb,int bufsize)2324c6ba7c9bSHans Wippel static struct smc_buf_desc *smcd_new_buf_create(struct smc_link_group *lgr,
2325c6ba7c9bSHans Wippel 						bool is_dmb, int bufsize)
2326c6ba7c9bSHans Wippel {
2327c6ba7c9bSHans Wippel 	struct smc_buf_desc *buf_desc;
2328c6ba7c9bSHans Wippel 	int rc;
2329c6ba7c9bSHans Wippel 
2330c6ba7c9bSHans Wippel 	/* try to alloc a new DMB */
2331c6ba7c9bSHans Wippel 	buf_desc = kzalloc(sizeof(*buf_desc), GFP_KERNEL);
2332c6ba7c9bSHans Wippel 	if (!buf_desc)
2333c6ba7c9bSHans Wippel 		return ERR_PTR(-ENOMEM);
2334c6ba7c9bSHans Wippel 	if (is_dmb) {
2335c6ba7c9bSHans Wippel 		rc = smc_ism_register_dmb(lgr, bufsize, buf_desc);
2336c6ba7c9bSHans Wippel 		if (rc) {
2337c6ba7c9bSHans Wippel 			kfree(buf_desc);
233896d6fdedSKarsten Graul 			if (rc == -ENOMEM)
233996d6fdedSKarsten Graul 				return ERR_PTR(-EAGAIN);
234096d6fdedSKarsten Graul 			if (rc == -ENOSPC)
234196d6fdedSKarsten Graul 				return ERR_PTR(-ENOSPC);
234296d6fdedSKarsten Graul 			return ERR_PTR(-EIO);
2343c6ba7c9bSHans Wippel 		}
2344be244f28SHans Wippel 		buf_desc->pages = virt_to_page(buf_desc->cpu_addr);
2345be244f28SHans Wippel 		/* CDC header stored in buf. So, pretend it was smaller */
2346be244f28SHans Wippel 		buf_desc->len = bufsize - sizeof(struct smcd_cdc_msg);
2347c6ba7c9bSHans Wippel 	} else {
2348c6ba7c9bSHans Wippel 		buf_desc->cpu_addr = kzalloc(bufsize, GFP_KERNEL |
2349c6ba7c9bSHans Wippel 					     __GFP_NOWARN | __GFP_NORETRY |
2350c6ba7c9bSHans Wippel 					     __GFP_NOMEMALLOC);
2351c6ba7c9bSHans Wippel 		if (!buf_desc->cpu_addr) {
2352c6ba7c9bSHans Wippel 			kfree(buf_desc);
2353c6ba7c9bSHans Wippel 			return ERR_PTR(-EAGAIN);
2354c6ba7c9bSHans Wippel 		}
2355c6ba7c9bSHans Wippel 		buf_desc->len = bufsize;
2356c6ba7c9bSHans Wippel 	}
2357c6ba7c9bSHans Wippel 	return buf_desc;
2358c6ba7c9bSHans Wippel }
2359c6ba7c9bSHans Wippel 
__smc_buf_create(struct smc_sock * smc,bool is_smcd,bool is_rmb)2360c6ba7c9bSHans Wippel static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)
2361cd6851f3SUrsula Braun {
23628437bda0SHans Wippel 	struct smc_buf_desc *buf_desc = ERR_PTR(-ENOMEM);
2363cd6851f3SUrsula Braun 	struct smc_connection *conn = &smc->conn;
2364cd6851f3SUrsula Braun 	struct smc_link_group *lgr = conn->lgr;
23653e034725SUrsula Braun 	struct list_head *buf_list;
2366833bac7eSGerd Bayer 	int bufsize, bufsize_comp;
2367aff7bfedSD. Wythe 	struct rw_semaphore *lock;	/* lock buffer list */
2368e0e4b8faSGuvenc Gulce 	bool is_dgraded = false;
2369cd6851f3SUrsula Braun 
23703e034725SUrsula Braun 	if (is_rmb)
23713e034725SUrsula Braun 		/* use socket recv buffer size (w/o overhead) as start value */
2372833bac7eSGerd Bayer 		bufsize = smc->sk.sk_rcvbuf / 2;
23733e034725SUrsula Braun 	else
2374cd6851f3SUrsula Braun 		/* use socket send buffer size (w/o overhead) as start value */
2375833bac7eSGerd Bayer 		bufsize = smc->sk.sk_sndbuf / 2;
23763e034725SUrsula Braun 
2377833bac7eSGerd Bayer 	for (bufsize_comp = smc_compress_bufsize(bufsize, is_smcd, is_rmb);
2378833bac7eSGerd Bayer 	     bufsize_comp >= 0; bufsize_comp--) {
23793e034725SUrsula Braun 		if (is_rmb) {
23803e034725SUrsula Braun 			lock = &lgr->rmbs_lock;
2381833bac7eSGerd Bayer 			buf_list = &lgr->rmbs[bufsize_comp];
2382cd6851f3SUrsula Braun 		} else {
23833e034725SUrsula Braun 			lock = &lgr->sndbufs_lock;
2384833bac7eSGerd Bayer 			buf_list = &lgr->sndbufs[bufsize_comp];
2385cd6851f3SUrsula Braun 		}
2386833bac7eSGerd Bayer 		bufsize = smc_uncompress_bufsize(bufsize_comp);
2387a3fe3d01SUrsula Braun 
23883e034725SUrsula Braun 		/* check for reusable slot in the link group */
2389833bac7eSGerd Bayer 		buf_desc = smc_buf_get_slot(bufsize_comp, lock, buf_list);
23903e034725SUrsula Braun 		if (buf_desc) {
23910ef69e78SGuangguan Wang 			buf_desc->is_dma_need_sync = 0;
2392194730a9SGuvenc Gulce 			SMC_STAT_RMB_SIZE(smc, is_smcd, is_rmb, bufsize);
2393194730a9SGuvenc Gulce 			SMC_STAT_BUF_REUSE(smc, is_smcd, is_rmb);
2394cd6851f3SUrsula Braun 			break; /* found reusable slot */
2395cd6851f3SUrsula Braun 		}
2396a3fe3d01SUrsula Braun 
2397c6ba7c9bSHans Wippel 		if (is_smcd)
2398c6ba7c9bSHans Wippel 			buf_desc = smcd_new_buf_create(lgr, is_rmb, bufsize);
2399c6ba7c9bSHans Wippel 		else
2400c6ba7c9bSHans Wippel 			buf_desc = smcr_new_buf_create(lgr, is_rmb, bufsize);
2401c6ba7c9bSHans Wippel 
2402b33982c3SUrsula Braun 		if (PTR_ERR(buf_desc) == -ENOMEM)
2403b33982c3SUrsula Braun 			break;
2404e0e4b8faSGuvenc Gulce 		if (IS_ERR(buf_desc)) {
2405e0e4b8faSGuvenc Gulce 			if (!is_dgraded) {
2406e0e4b8faSGuvenc Gulce 				is_dgraded = true;
2407194730a9SGuvenc Gulce 				SMC_STAT_RMB_DOWNGRADED(smc, is_smcd, is_rmb);
2408e0e4b8faSGuvenc Gulce 			}
2409cd6851f3SUrsula Braun 			continue;
2410e0e4b8faSGuvenc Gulce 		}
24113e034725SUrsula Braun 
2412194730a9SGuvenc Gulce 		SMC_STAT_RMB_ALLOC(smc, is_smcd, is_rmb);
2413194730a9SGuvenc Gulce 		SMC_STAT_RMB_SIZE(smc, is_smcd, is_rmb, bufsize);
24143e034725SUrsula Braun 		buf_desc->used = 1;
2415aff7bfedSD. Wythe 		down_write(lock);
24163e034725SUrsula Braun 		list_add(&buf_desc->list, buf_list);
2417aff7bfedSD. Wythe 		up_write(lock);
24183e034725SUrsula Braun 		break; /* found */
24193e034725SUrsula Braun 	}
24203e034725SUrsula Braun 
2421b33982c3SUrsula Braun 	if (IS_ERR(buf_desc))
242272b7f6c4SKarsten Graul 		return PTR_ERR(buf_desc);
24233e034725SUrsula Braun 
2424b9247544SKarsten Graul 	if (!is_smcd) {
2425b9247544SKarsten Graul 		if (smcr_buf_map_usable_links(lgr, buf_desc, is_rmb)) {
2426b8d19945SWen Gu 			smcr_buf_unuse(buf_desc, is_rmb, lgr);
2427b9247544SKarsten Graul 			return -ENOMEM;
2428b9247544SKarsten Graul 		}
2429b9247544SKarsten Graul 	}
2430b9247544SKarsten Graul 
24313e034725SUrsula Braun 	if (is_rmb) {
24323e034725SUrsula Braun 		conn->rmb_desc = buf_desc;
2433833bac7eSGerd Bayer 		conn->rmbe_size_comp = bufsize_comp;
2434833bac7eSGerd Bayer 		smc->sk.sk_rcvbuf = bufsize * 2;
24355f08318fSUrsula Braun 		atomic_set(&conn->bytes_to_rcv, 0);
2436be244f28SHans Wippel 		conn->rmbe_update_limit =
2437be244f28SHans Wippel 			smc_rmb_wnd_update_limit(buf_desc->len);
2438c6ba7c9bSHans Wippel 		if (is_smcd)
2439c6ba7c9bSHans Wippel 			smc_ism_set_conn(conn); /* map RMB/smcd_dev to conn */
2440cd6851f3SUrsula Braun 	} else {
24413e034725SUrsula Braun 		conn->sndbuf_desc = buf_desc;
2442833bac7eSGerd Bayer 		smc->sk.sk_sndbuf = bufsize * 2;
24433e034725SUrsula Braun 		atomic_set(&conn->sndbuf_space, bufsize);
2444cd6851f3SUrsula Braun 	}
24453e034725SUrsula Braun 	return 0;
24463e034725SUrsula Braun }
24473e034725SUrsula Braun 
smc_sndbuf_sync_sg_for_device(struct smc_connection * conn)244810428dd8SUrsula Braun void smc_sndbuf_sync_sg_for_device(struct smc_connection *conn)
244910428dd8SUrsula Braun {
24500ef69e78SGuangguan Wang 	if (!conn->sndbuf_desc->is_dma_need_sync)
24510ef69e78SGuangguan Wang 		return;
2452ea89c6c0SWen Gu 	if (!smc_conn_lgr_valid(conn) || conn->lgr->is_smcd ||
2453ea89c6c0SWen Gu 	    !smc_link_active(conn->lnk))
2454c6ba7c9bSHans Wippel 		return;
2455387707fdSKarsten Graul 	smc_ib_sync_sg_for_device(conn->lnk, conn->sndbuf_desc, DMA_TO_DEVICE);
245610428dd8SUrsula Braun }
245710428dd8SUrsula Braun 
smc_rmb_sync_sg_for_cpu(struct smc_connection * conn)245810428dd8SUrsula Braun void smc_rmb_sync_sg_for_cpu(struct smc_connection *conn)
245910428dd8SUrsula Braun {
2460b9247544SKarsten Graul 	int i;
246110428dd8SUrsula Braun 
24620ef69e78SGuangguan Wang 	if (!conn->rmb_desc->is_dma_need_sync)
24630ef69e78SGuangguan Wang 		return;
2464ea89c6c0SWen Gu 	if (!smc_conn_lgr_valid(conn) || conn->lgr->is_smcd)
2465c6ba7c9bSHans Wippel 		return;
2466b9247544SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
2467741a49a4SKarsten Graul 		if (!smc_link_active(&conn->lgr->lnk[i]))
2468b9247544SKarsten Graul 			continue;
2469b9247544SKarsten Graul 		smc_ib_sync_sg_for_cpu(&conn->lgr->lnk[i], conn->rmb_desc,
2470b9247544SKarsten Graul 				       DMA_FROM_DEVICE);
2471b9247544SKarsten Graul 	}
247210428dd8SUrsula Braun }
247310428dd8SUrsula Braun 
24743e034725SUrsula Braun /* create the send and receive buffer for an SMC socket;
24753e034725SUrsula Braun  * receive buffers are called RMBs;
24763e034725SUrsula Braun  * (even though the SMC protocol allows more than one RMB-element per RMB,
24773e034725SUrsula Braun  * the Linux implementation uses just one RMB-element per RMB, i.e. uses an
24783e034725SUrsula Braun  * extra RMB for every connection in a link group
24793e034725SUrsula Braun  */
smc_buf_create(struct smc_sock * smc,bool is_smcd)2480c6ba7c9bSHans Wippel int smc_buf_create(struct smc_sock *smc, bool is_smcd)
24813e034725SUrsula Braun {
24823e034725SUrsula Braun 	int rc;
24833e034725SUrsula Braun 
24843e034725SUrsula Braun 	/* create send buffer */
248521f6f41eSWen Gu 	if (is_smcd &&
248621f6f41eSWen Gu 	    smc_ism_support_dmb_nocopy(smc->conn.lgr->smcd))
248721f6f41eSWen Gu 		goto create_rmb;
248821f6f41eSWen Gu 
2489c6ba7c9bSHans Wippel 	rc = __smc_buf_create(smc, is_smcd, false);
24903e034725SUrsula Braun 	if (rc)
24913e034725SUrsula Braun 		return rc;
249221f6f41eSWen Gu 
249321f6f41eSWen Gu create_rmb:
24943e034725SUrsula Braun 	/* create rmb */
2495c6ba7c9bSHans Wippel 	rc = __smc_buf_create(smc, is_smcd, true);
249621f6f41eSWen Gu 	if (rc && smc->conn.sndbuf_desc) {
2497aff7bfedSD. Wythe 		down_write(&smc->conn.lgr->sndbufs_lock);
2498fd7f3a74SKarsten Graul 		list_del(&smc->conn.sndbuf_desc->list);
2499aff7bfedSD. Wythe 		up_write(&smc->conn.lgr->sndbufs_lock);
25006511aad3SHans Wippel 		smc_buf_free(smc->conn.lgr, false, smc->conn.sndbuf_desc);
25011d8df41dSUrsula Braun 		smc->conn.sndbuf_desc = NULL;
2502fd7f3a74SKarsten Graul 	}
25033e034725SUrsula Braun 	return rc;
2504cd6851f3SUrsula Braun }
2505bd4ad577SUrsula Braun 
smcd_buf_attach(struct smc_sock * smc)250621f6f41eSWen Gu int smcd_buf_attach(struct smc_sock *smc)
250721f6f41eSWen Gu {
250821f6f41eSWen Gu 	struct smc_connection *conn = &smc->conn;
250921f6f41eSWen Gu 	struct smcd_dev *smcd = conn->lgr->smcd;
251021f6f41eSWen Gu 	u64 peer_token = conn->peer_token;
251121f6f41eSWen Gu 	struct smc_buf_desc *buf_desc;
251221f6f41eSWen Gu 	int rc;
251321f6f41eSWen Gu 
251421f6f41eSWen Gu 	buf_desc = kzalloc(sizeof(*buf_desc), GFP_KERNEL);
251521f6f41eSWen Gu 	if (!buf_desc)
251621f6f41eSWen Gu 		return -ENOMEM;
251721f6f41eSWen Gu 
251821f6f41eSWen Gu 	/* The ghost sndbuf_desc describes the same memory region as
251921f6f41eSWen Gu 	 * peer RMB. Its lifecycle is consistent with the connection's
252021f6f41eSWen Gu 	 * and it will be freed with the connections instead of the
252121f6f41eSWen Gu 	 * link group.
252221f6f41eSWen Gu 	 */
252321f6f41eSWen Gu 	rc = smc_ism_attach_dmb(smcd, peer_token, buf_desc);
252421f6f41eSWen Gu 	if (rc)
252521f6f41eSWen Gu 		goto free;
252621f6f41eSWen Gu 
252721f6f41eSWen Gu 	smc->sk.sk_sndbuf = buf_desc->len;
252821f6f41eSWen Gu 	buf_desc->cpu_addr =
252921f6f41eSWen Gu 		(u8 *)buf_desc->cpu_addr + sizeof(struct smcd_cdc_msg);
253021f6f41eSWen Gu 	buf_desc->len -= sizeof(struct smcd_cdc_msg);
253121f6f41eSWen Gu 	conn->sndbuf_desc = buf_desc;
253221f6f41eSWen Gu 	conn->sndbuf_desc->used = 1;
253321f6f41eSWen Gu 	atomic_set(&conn->sndbuf_space, conn->sndbuf_desc->len);
253421f6f41eSWen Gu 	return 0;
253521f6f41eSWen Gu 
253621f6f41eSWen Gu free:
253721f6f41eSWen Gu 	kfree(buf_desc);
253821f6f41eSWen Gu 	return rc;
253921f6f41eSWen Gu }
254021f6f41eSWen Gu 
smc_rmb_reserve_rtoken_idx(struct smc_link_group * lgr)2541bd4ad577SUrsula Braun static inline int smc_rmb_reserve_rtoken_idx(struct smc_link_group *lgr)
2542bd4ad577SUrsula Braun {
2543bd4ad577SUrsula Braun 	int i;
2544bd4ad577SUrsula Braun 
2545bd4ad577SUrsula Braun 	for_each_clear_bit(i, lgr->rtokens_used_mask, SMC_RMBS_PER_LGR_MAX) {
2546bd4ad577SUrsula Braun 		if (!test_and_set_bit(i, lgr->rtokens_used_mask))
2547bd4ad577SUrsula Braun 			return i;
2548bd4ad577SUrsula Braun 	}
2549bd4ad577SUrsula Braun 	return -ENOSPC;
2550bd4ad577SUrsula Braun }
2551bd4ad577SUrsula Braun 
smc_rtoken_find_by_link(struct smc_link_group * lgr,int lnk_idx,u32 rkey)2552ba21abd2SKarsten Graul static int smc_rtoken_find_by_link(struct smc_link_group *lgr, int lnk_idx,
2553ba21abd2SKarsten Graul 				   u32 rkey)
2554ba21abd2SKarsten Graul {
2555ba21abd2SKarsten Graul 	int i;
2556ba21abd2SKarsten Graul 
2557ba21abd2SKarsten Graul 	for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
2558ba21abd2SKarsten Graul 		if (test_bit(i, lgr->rtokens_used_mask) &&
2559ba21abd2SKarsten Graul 		    lgr->rtokens[i][lnk_idx].rkey == rkey)
2560ba21abd2SKarsten Graul 			return i;
2561ba21abd2SKarsten Graul 	}
2562ba21abd2SKarsten Graul 	return -ENOENT;
2563ba21abd2SKarsten Graul }
2564ba21abd2SKarsten Graul 
2565ba21abd2SKarsten Graul /* set rtoken for a new link to an existing rmb */
smc_rtoken_set(struct smc_link_group * lgr,int link_idx,int link_idx_new,__be32 nw_rkey_known,__be64 nw_vaddr,__be32 nw_rkey)2566ba21abd2SKarsten Graul void smc_rtoken_set(struct smc_link_group *lgr, int link_idx, int link_idx_new,
2567ba21abd2SKarsten Graul 		    __be32 nw_rkey_known, __be64 nw_vaddr, __be32 nw_rkey)
2568ba21abd2SKarsten Graul {
2569ba21abd2SKarsten Graul 	int rtok_idx;
2570ba21abd2SKarsten Graul 
2571ba21abd2SKarsten Graul 	rtok_idx = smc_rtoken_find_by_link(lgr, link_idx, ntohl(nw_rkey_known));
2572ba21abd2SKarsten Graul 	if (rtok_idx == -ENOENT)
2573ba21abd2SKarsten Graul 		return;
2574ba21abd2SKarsten Graul 	lgr->rtokens[rtok_idx][link_idx_new].rkey = ntohl(nw_rkey);
2575ba21abd2SKarsten Graul 	lgr->rtokens[rtok_idx][link_idx_new].dma_addr = be64_to_cpu(nw_vaddr);
2576ba21abd2SKarsten Graul }
2577ba21abd2SKarsten Graul 
2578ba21abd2SKarsten Graul /* set rtoken for a new link whose link_id is given */
smc_rtoken_set2(struct smc_link_group * lgr,int rtok_idx,int link_id,__be64 nw_vaddr,__be32 nw_rkey)2579ba21abd2SKarsten Graul void smc_rtoken_set2(struct smc_link_group *lgr, int rtok_idx, int link_id,
2580ba21abd2SKarsten Graul 		     __be64 nw_vaddr, __be32 nw_rkey)
2581ba21abd2SKarsten Graul {
2582ba21abd2SKarsten Graul 	u64 dma_addr = be64_to_cpu(nw_vaddr);
2583ba21abd2SKarsten Graul 	u32 rkey = ntohl(nw_rkey);
2584ba21abd2SKarsten Graul 	bool found = false;
2585ba21abd2SKarsten Graul 	int link_idx;
2586ba21abd2SKarsten Graul 
2587ba21abd2SKarsten Graul 	for (link_idx = 0; link_idx < SMC_LINKS_PER_LGR_MAX; link_idx++) {
2588ba21abd2SKarsten Graul 		if (lgr->lnk[link_idx].link_id == link_id) {
2589ba21abd2SKarsten Graul 			found = true;
2590ba21abd2SKarsten Graul 			break;
2591ba21abd2SKarsten Graul 		}
2592ba21abd2SKarsten Graul 	}
2593ba21abd2SKarsten Graul 	if (!found)
2594ba21abd2SKarsten Graul 		return;
2595ba21abd2SKarsten Graul 	lgr->rtokens[rtok_idx][link_idx].rkey = rkey;
2596ba21abd2SKarsten Graul 	lgr->rtokens[rtok_idx][link_idx].dma_addr = dma_addr;
2597ba21abd2SKarsten Graul }
2598ba21abd2SKarsten Graul 
25994ed75de5SKarsten Graul /* add a new rtoken from peer */
smc_rtoken_add(struct smc_link * lnk,__be64 nw_vaddr,__be32 nw_rkey)2600387707fdSKarsten Graul int smc_rtoken_add(struct smc_link *lnk, __be64 nw_vaddr, __be32 nw_rkey)
2601bd4ad577SUrsula Braun {
2602387707fdSKarsten Graul 	struct smc_link_group *lgr = smc_get_lgr(lnk);
26034ed75de5SKarsten Graul 	u64 dma_addr = be64_to_cpu(nw_vaddr);
26044ed75de5SKarsten Graul 	u32 rkey = ntohl(nw_rkey);
2605bd4ad577SUrsula Braun 	int i;
2606bd4ad577SUrsula Braun 
2607bd4ad577SUrsula Braun 	for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
2608387707fdSKarsten Graul 		if (lgr->rtokens[i][lnk->link_idx].rkey == rkey &&
2609387707fdSKarsten Graul 		    lgr->rtokens[i][lnk->link_idx].dma_addr == dma_addr &&
2610bd4ad577SUrsula Braun 		    test_bit(i, lgr->rtokens_used_mask)) {
26114ed75de5SKarsten Graul 			/* already in list */
26124ed75de5SKarsten Graul 			return i;
26134ed75de5SKarsten Graul 		}
26144ed75de5SKarsten Graul 	}
26154ed75de5SKarsten Graul 	i = smc_rmb_reserve_rtoken_idx(lgr);
26164ed75de5SKarsten Graul 	if (i < 0)
26174ed75de5SKarsten Graul 		return i;
2618387707fdSKarsten Graul 	lgr->rtokens[i][lnk->link_idx].rkey = rkey;
2619387707fdSKarsten Graul 	lgr->rtokens[i][lnk->link_idx].dma_addr = dma_addr;
26204ed75de5SKarsten Graul 	return i;
26214ed75de5SKarsten Graul }
26224ed75de5SKarsten Graul 
2623e07d31dcSKarsten Graul /* delete an rtoken from all links */
smc_rtoken_delete(struct smc_link * lnk,__be32 nw_rkey)2624387707fdSKarsten Graul int smc_rtoken_delete(struct smc_link *lnk, __be32 nw_rkey)
26254ed75de5SKarsten Graul {
2626387707fdSKarsten Graul 	struct smc_link_group *lgr = smc_get_lgr(lnk);
26274ed75de5SKarsten Graul 	u32 rkey = ntohl(nw_rkey);
2628e07d31dcSKarsten Graul 	int i, j;
26294ed75de5SKarsten Graul 
26304ed75de5SKarsten Graul 	for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
2631387707fdSKarsten Graul 		if (lgr->rtokens[i][lnk->link_idx].rkey == rkey &&
26324ed75de5SKarsten Graul 		    test_bit(i, lgr->rtokens_used_mask)) {
2633e07d31dcSKarsten Graul 			for (j = 0; j < SMC_LINKS_PER_LGR_MAX; j++) {
2634e07d31dcSKarsten Graul 				lgr->rtokens[i][j].rkey = 0;
2635e07d31dcSKarsten Graul 				lgr->rtokens[i][j].dma_addr = 0;
2636e07d31dcSKarsten Graul 			}
26374ed75de5SKarsten Graul 			clear_bit(i, lgr->rtokens_used_mask);
2638bd4ad577SUrsula Braun 			return 0;
2639bd4ad577SUrsula Braun 		}
2640bd4ad577SUrsula Braun 	}
26414ed75de5SKarsten Graul 	return -ENOENT;
26424ed75de5SKarsten Graul }
26434ed75de5SKarsten Graul 
26444ed75de5SKarsten Graul /* save rkey and dma_addr received from peer during clc handshake */
smc_rmb_rtoken_handling(struct smc_connection * conn,struct smc_link * lnk,struct smc_clc_msg_accept_confirm * clc)26454ed75de5SKarsten Graul int smc_rmb_rtoken_handling(struct smc_connection *conn,
2646e07d31dcSKarsten Graul 			    struct smc_link *lnk,
26474ed75de5SKarsten Graul 			    struct smc_clc_msg_accept_confirm *clc)
26484ed75de5SKarsten Graul {
26493d9725a6SUrsula Braun 	conn->rtoken_idx = smc_rtoken_add(lnk, clc->r0.rmb_dma_addr,
26503d9725a6SUrsula Braun 					  clc->r0.rmb_rkey);
2651bd4ad577SUrsula Braun 	if (conn->rtoken_idx < 0)
2652bd4ad577SUrsula Braun 		return conn->rtoken_idx;
2653bd4ad577SUrsula Braun 	return 0;
2654bd4ad577SUrsula Braun }
26559fda3510SHans Wippel 
smc_core_going_away(void)2656c3d9494eSUrsula Braun static void smc_core_going_away(void)
2657c3d9494eSUrsula Braun {
2658c3d9494eSUrsula Braun 	struct smc_ib_device *smcibdev;
2659c3d9494eSUrsula Braun 	struct smcd_dev *smcd;
2660c3d9494eSUrsula Braun 
266192f3cb0eSUrsula Braun 	mutex_lock(&smc_ib_devices.mutex);
2662c3d9494eSUrsula Braun 	list_for_each_entry(smcibdev, &smc_ib_devices.list, list) {
2663c3d9494eSUrsula Braun 		int i;
2664c3d9494eSUrsula Braun 
2665c3d9494eSUrsula Braun 		for (i = 0; i < SMC_MAX_PORTS; i++)
2666c3d9494eSUrsula Braun 			set_bit(i, smcibdev->ports_going_away);
2667c3d9494eSUrsula Braun 	}
266892f3cb0eSUrsula Braun 	mutex_unlock(&smc_ib_devices.mutex);
2669c3d9494eSUrsula Braun 
267082087c03SUrsula Braun 	mutex_lock(&smcd_dev_list.mutex);
2671c3d9494eSUrsula Braun 	list_for_each_entry(smcd, &smcd_dev_list.list, list) {
2672c3d9494eSUrsula Braun 		smcd->going_away = 1;
2673c3d9494eSUrsula Braun 	}
267482087c03SUrsula Braun 	mutex_unlock(&smcd_dev_list.mutex);
2675c3d9494eSUrsula Braun }
2676c3d9494eSUrsula Braun 
26775421ec28SUrsula Braun /* Clean up all SMC link groups */
smc_lgrs_shutdown(void)26785421ec28SUrsula Braun static void smc_lgrs_shutdown(void)
26799fda3510SHans Wippel {
2680a2351c5dSUrsula Braun 	struct smcd_dev *smcd;
26819fda3510SHans Wippel 
2682c3d9494eSUrsula Braun 	smc_core_going_away();
2683c3d9494eSUrsula Braun 
26840b29ec64SUrsula Braun 	smc_smcr_terminate_all(NULL);
2685a2351c5dSUrsula Braun 
268682087c03SUrsula Braun 	mutex_lock(&smcd_dev_list.mutex);
2687a2351c5dSUrsula Braun 	list_for_each_entry(smcd, &smcd_dev_list.list, list)
26885421ec28SUrsula Braun 		smc_smcd_terminate_all(smcd);
268982087c03SUrsula Braun 	mutex_unlock(&smcd_dev_list.mutex);
26909fda3510SHans Wippel }
26915421ec28SUrsula Braun 
smc_core_reboot_event(struct notifier_block * this,unsigned long event,void * ptr)2692a33a803cSUrsula Braun static int smc_core_reboot_event(struct notifier_block *this,
2693a33a803cSUrsula Braun 				 unsigned long event, void *ptr)
2694a33a803cSUrsula Braun {
2695a33a803cSUrsula Braun 	smc_lgrs_shutdown();
269628a3b840SKarsten Graul 	smc_ib_unregister_client();
26978747716fSStefan Raspl 	smc_ism_exit();
2698a33a803cSUrsula Braun 	return 0;
2699a33a803cSUrsula Braun }
2700a33a803cSUrsula Braun 
2701a33a803cSUrsula Braun static struct notifier_block smc_reboot_notifier = {
2702a33a803cSUrsula Braun 	.notifier_call = smc_core_reboot_event,
2703a33a803cSUrsula Braun };
2704a33a803cSUrsula Braun 
smc_core_init(void)27056dabd405SUrsula Braun int __init smc_core_init(void)
27066dabd405SUrsula Braun {
2707a33a803cSUrsula Braun 	return register_reboot_notifier(&smc_reboot_notifier);
27086dabd405SUrsula Braun }
27096dabd405SUrsula Braun 
27105421ec28SUrsula Braun /* Called (from smc_exit) when module is removed */
smc_core_exit(void)27115421ec28SUrsula Braun void smc_core_exit(void)
27125421ec28SUrsula Braun {
2713a33a803cSUrsula Braun 	unregister_reboot_notifier(&smc_reboot_notifier);
27145421ec28SUrsula Braun 	smc_lgrs_shutdown();
27155421ec28SUrsula Braun }
2716