18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Shared Memory Communications over RDMA (SMC-R) and RoCE
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Basic Transport Functions exploiting Infiniband API
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright IBM Corp. 2016
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *  Author(s):  Ursula Braun <ubraun@linux.vnet.ibm.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/socket.h>
138c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
148c2ecf20Sopenharmony_ci#include <linux/random.h>
158c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
168c2ecf20Sopenharmony_ci#include <linux/wait.h>
178c2ecf20Sopenharmony_ci#include <linux/reboot.h>
188c2ecf20Sopenharmony_ci#include <linux/mutex.h>
198c2ecf20Sopenharmony_ci#include <net/tcp.h>
208c2ecf20Sopenharmony_ci#include <net/sock.h>
218c2ecf20Sopenharmony_ci#include <rdma/ib_verbs.h>
228c2ecf20Sopenharmony_ci#include <rdma/ib_cache.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "smc.h"
258c2ecf20Sopenharmony_ci#include "smc_clc.h"
268c2ecf20Sopenharmony_ci#include "smc_core.h"
278c2ecf20Sopenharmony_ci#include "smc_ib.h"
288c2ecf20Sopenharmony_ci#include "smc_wr.h"
298c2ecf20Sopenharmony_ci#include "smc_llc.h"
308c2ecf20Sopenharmony_ci#include "smc_cdc.h"
318c2ecf20Sopenharmony_ci#include "smc_close.h"
328c2ecf20Sopenharmony_ci#include "smc_ism.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define SMC_LGR_NUM_INCR		256
358c2ecf20Sopenharmony_ci#define SMC_LGR_FREE_DELAY_SERV		(600 * HZ)
368c2ecf20Sopenharmony_ci#define SMC_LGR_FREE_DELAY_CLNT		(SMC_LGR_FREE_DELAY_SERV + 10 * HZ)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic struct smc_lgr_list smc_lgr_list = {	/* established link groups */
398c2ecf20Sopenharmony_ci	.lock = __SPIN_LOCK_UNLOCKED(smc_lgr_list.lock),
408c2ecf20Sopenharmony_ci	.list = LIST_HEAD_INIT(smc_lgr_list.list),
418c2ecf20Sopenharmony_ci	.num = 0,
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic atomic_t lgr_cnt = ATOMIC_INIT(0); /* number of existing link groups */
458c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(lgrs_deleted);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic void smc_buf_free(struct smc_link_group *lgr, bool is_rmb,
488c2ecf20Sopenharmony_ci			 struct smc_buf_desc *buf_desc);
498c2ecf20Sopenharmony_cistatic void __smc_lgr_terminate(struct smc_link_group *lgr, bool soft);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void smc_link_down_work(struct work_struct *work);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* return head of link group list and its lock for a given link group */
548c2ecf20Sopenharmony_cistatic inline struct list_head *smc_lgr_list_head(struct smc_link_group *lgr,
558c2ecf20Sopenharmony_ci						  spinlock_t **lgr_lock)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	if (lgr->is_smcd) {
588c2ecf20Sopenharmony_ci		*lgr_lock = &lgr->smcd->lgr_lock;
598c2ecf20Sopenharmony_ci		return &lgr->smcd->lgr_list;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	*lgr_lock = &smc_lgr_list.lock;
638c2ecf20Sopenharmony_ci	return &smc_lgr_list.list;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void smc_lgr_schedule_free_work(struct smc_link_group *lgr)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	/* client link group creation always follows the server link group
698c2ecf20Sopenharmony_ci	 * creation. For client use a somewhat higher removal delay time,
708c2ecf20Sopenharmony_ci	 * otherwise there is a risk of out-of-sync link groups.
718c2ecf20Sopenharmony_ci	 */
728c2ecf20Sopenharmony_ci	if (!lgr->freeing) {
738c2ecf20Sopenharmony_ci		mod_delayed_work(system_wq, &lgr->free_work,
748c2ecf20Sopenharmony_ci				 (!lgr->is_smcd && lgr->role == SMC_CLNT) ?
758c2ecf20Sopenharmony_ci						SMC_LGR_FREE_DELAY_CLNT :
768c2ecf20Sopenharmony_ci						SMC_LGR_FREE_DELAY_SERV);
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* Register connection's alert token in our lookup structure.
818c2ecf20Sopenharmony_ci * To use rbtrees we have to implement our own insert core.
828c2ecf20Sopenharmony_ci * Requires @conns_lock
838c2ecf20Sopenharmony_ci * @smc		connection to register
848c2ecf20Sopenharmony_ci * Returns 0 on success, != otherwise.
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_cistatic void smc_lgr_add_alert_token(struct smc_connection *conn)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct rb_node **link, *parent = NULL;
898c2ecf20Sopenharmony_ci	u32 token = conn->alert_token_local;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	link = &conn->lgr->conns_all.rb_node;
928c2ecf20Sopenharmony_ci	while (*link) {
938c2ecf20Sopenharmony_ci		struct smc_connection *cur = rb_entry(*link,
948c2ecf20Sopenharmony_ci					struct smc_connection, alert_node);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci		parent = *link;
978c2ecf20Sopenharmony_ci		if (cur->alert_token_local > token)
988c2ecf20Sopenharmony_ci			link = &parent->rb_left;
998c2ecf20Sopenharmony_ci		else
1008c2ecf20Sopenharmony_ci			link = &parent->rb_right;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci	/* Put the new node there */
1038c2ecf20Sopenharmony_ci	rb_link_node(&conn->alert_node, parent, link);
1048c2ecf20Sopenharmony_ci	rb_insert_color(&conn->alert_node, &conn->lgr->conns_all);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* assign an SMC-R link to the connection */
1088c2ecf20Sopenharmony_cistatic int smcr_lgr_conn_assign_link(struct smc_connection *conn, bool first)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	enum smc_link_state expected = first ? SMC_LNK_ACTIVATING :
1118c2ecf20Sopenharmony_ci				       SMC_LNK_ACTIVE;
1128c2ecf20Sopenharmony_ci	int i, j;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* do link balancing */
1158c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
1168c2ecf20Sopenharmony_ci		struct smc_link *lnk = &conn->lgr->lnk[i];
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		if (lnk->state != expected || lnk->link_is_asym)
1198c2ecf20Sopenharmony_ci			continue;
1208c2ecf20Sopenharmony_ci		if (conn->lgr->role == SMC_CLNT) {
1218c2ecf20Sopenharmony_ci			conn->lnk = lnk; /* temporary, SMC server assigns link*/
1228c2ecf20Sopenharmony_ci			break;
1238c2ecf20Sopenharmony_ci		}
1248c2ecf20Sopenharmony_ci		if (conn->lgr->conns_num % 2) {
1258c2ecf20Sopenharmony_ci			for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) {
1268c2ecf20Sopenharmony_ci				struct smc_link *lnk2;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci				lnk2 = &conn->lgr->lnk[j];
1298c2ecf20Sopenharmony_ci				if (lnk2->state == expected &&
1308c2ecf20Sopenharmony_ci				    !lnk2->link_is_asym) {
1318c2ecf20Sopenharmony_ci					conn->lnk = lnk2;
1328c2ecf20Sopenharmony_ci					break;
1338c2ecf20Sopenharmony_ci				}
1348c2ecf20Sopenharmony_ci			}
1358c2ecf20Sopenharmony_ci		}
1368c2ecf20Sopenharmony_ci		if (!conn->lnk)
1378c2ecf20Sopenharmony_ci			conn->lnk = lnk;
1388c2ecf20Sopenharmony_ci		break;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci	if (!conn->lnk)
1418c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_NOACTLINK;
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/* Register connection in link group by assigning an alert token
1468c2ecf20Sopenharmony_ci * registered in a search tree.
1478c2ecf20Sopenharmony_ci * Requires @conns_lock
1488c2ecf20Sopenharmony_ci * Note that '0' is a reserved value and not assigned.
1498c2ecf20Sopenharmony_ci */
1508c2ecf20Sopenharmony_cistatic int smc_lgr_register_conn(struct smc_connection *conn, bool first)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
1538c2ecf20Sopenharmony_ci	static atomic_t nexttoken = ATOMIC_INIT(0);
1548c2ecf20Sopenharmony_ci	int rc;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (!conn->lgr->is_smcd) {
1578c2ecf20Sopenharmony_ci		rc = smcr_lgr_conn_assign_link(conn, first);
1588c2ecf20Sopenharmony_ci		if (rc)
1598c2ecf20Sopenharmony_ci			return rc;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci	/* find a new alert_token_local value not yet used by some connection
1628c2ecf20Sopenharmony_ci	 * in this link group
1638c2ecf20Sopenharmony_ci	 */
1648c2ecf20Sopenharmony_ci	sock_hold(&smc->sk); /* sock_put in smc_lgr_unregister_conn() */
1658c2ecf20Sopenharmony_ci	while (!conn->alert_token_local) {
1668c2ecf20Sopenharmony_ci		conn->alert_token_local = atomic_inc_return(&nexttoken);
1678c2ecf20Sopenharmony_ci		if (smc_lgr_find_conn(conn->alert_token_local, conn->lgr))
1688c2ecf20Sopenharmony_ci			conn->alert_token_local = 0;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci	smc_lgr_add_alert_token(conn);
1718c2ecf20Sopenharmony_ci	conn->lgr->conns_num++;
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/* Unregister connection and reset the alert token of the given connection<
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_cistatic void __smc_lgr_unregister_conn(struct smc_connection *conn)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
1808c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = conn->lgr;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	rb_erase(&conn->alert_node, &lgr->conns_all);
1838c2ecf20Sopenharmony_ci	lgr->conns_num--;
1848c2ecf20Sopenharmony_ci	conn->alert_token_local = 0;
1858c2ecf20Sopenharmony_ci	sock_put(&smc->sk); /* sock_hold in smc_lgr_register_conn() */
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/* Unregister connection from lgr
1898c2ecf20Sopenharmony_ci */
1908c2ecf20Sopenharmony_cistatic void smc_lgr_unregister_conn(struct smc_connection *conn)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = conn->lgr;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (!lgr)
1958c2ecf20Sopenharmony_ci		return;
1968c2ecf20Sopenharmony_ci	write_lock_bh(&lgr->conns_lock);
1978c2ecf20Sopenharmony_ci	if (conn->alert_token_local) {
1988c2ecf20Sopenharmony_ci		__smc_lgr_unregister_conn(conn);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci	write_unlock_bh(&lgr->conns_lock);
2018c2ecf20Sopenharmony_ci	conn->lgr = NULL;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_civoid smc_lgr_cleanup_early(struct smc_connection *conn)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = conn->lgr;
2078c2ecf20Sopenharmony_ci	spinlock_t *lgr_lock;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (!lgr)
2108c2ecf20Sopenharmony_ci		return;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	smc_conn_free(conn);
2138c2ecf20Sopenharmony_ci	smc_lgr_list_head(lgr, &lgr_lock);
2148c2ecf20Sopenharmony_ci	spin_lock_bh(lgr_lock);
2158c2ecf20Sopenharmony_ci	/* do not use this link group for new connections */
2168c2ecf20Sopenharmony_ci	if (!list_empty(&lgr->list))
2178c2ecf20Sopenharmony_ci		list_del_init(&lgr->list);
2188c2ecf20Sopenharmony_ci	spin_unlock_bh(lgr_lock);
2198c2ecf20Sopenharmony_ci	__smc_lgr_terminate(lgr, true);
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic void smcr_lgr_link_deactivate_all(struct smc_link_group *lgr)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	int i;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
2278c2ecf20Sopenharmony_ci		struct smc_link *lnk = &lgr->lnk[i];
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		if (smc_link_sendable(lnk))
2308c2ecf20Sopenharmony_ci			lnk->state = SMC_LNK_INACTIVE;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci	wake_up_all(&lgr->llc_msg_waiter);
2338c2ecf20Sopenharmony_ci	wake_up_all(&lgr->llc_flow_waiter);
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic void smc_lgr_free(struct smc_link_group *lgr);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic void smc_lgr_free_work(struct work_struct *work)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = container_of(to_delayed_work(work),
2418c2ecf20Sopenharmony_ci						  struct smc_link_group,
2428c2ecf20Sopenharmony_ci						  free_work);
2438c2ecf20Sopenharmony_ci	spinlock_t *lgr_lock;
2448c2ecf20Sopenharmony_ci	bool conns;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	smc_lgr_list_head(lgr, &lgr_lock);
2478c2ecf20Sopenharmony_ci	spin_lock_bh(lgr_lock);
2488c2ecf20Sopenharmony_ci	if (lgr->freeing) {
2498c2ecf20Sopenharmony_ci		spin_unlock_bh(lgr_lock);
2508c2ecf20Sopenharmony_ci		return;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci	read_lock_bh(&lgr->conns_lock);
2538c2ecf20Sopenharmony_ci	conns = RB_EMPTY_ROOT(&lgr->conns_all);
2548c2ecf20Sopenharmony_ci	read_unlock_bh(&lgr->conns_lock);
2558c2ecf20Sopenharmony_ci	if (!conns) { /* number of lgr connections is no longer zero */
2568c2ecf20Sopenharmony_ci		spin_unlock_bh(lgr_lock);
2578c2ecf20Sopenharmony_ci		return;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci	list_del_init(&lgr->list); /* remove from smc_lgr_list */
2608c2ecf20Sopenharmony_ci	lgr->freeing = 1; /* this instance does the freeing, no new schedule */
2618c2ecf20Sopenharmony_ci	spin_unlock_bh(lgr_lock);
2628c2ecf20Sopenharmony_ci	cancel_delayed_work(&lgr->free_work);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (!lgr->is_smcd && !lgr->terminating)
2658c2ecf20Sopenharmony_ci		smc_llc_send_link_delete_all(lgr, true,
2668c2ecf20Sopenharmony_ci					     SMC_LLC_DEL_PROG_INIT_TERM);
2678c2ecf20Sopenharmony_ci	if (lgr->is_smcd && !lgr->terminating)
2688c2ecf20Sopenharmony_ci		smc_ism_signal_shutdown(lgr);
2698c2ecf20Sopenharmony_ci	if (!lgr->is_smcd)
2708c2ecf20Sopenharmony_ci		smcr_lgr_link_deactivate_all(lgr);
2718c2ecf20Sopenharmony_ci	smc_lgr_free(lgr);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void smc_lgr_terminate_work(struct work_struct *work)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
2778c2ecf20Sopenharmony_ci						  terminate_work);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	__smc_lgr_terminate(lgr, true);
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/* return next unique link id for the lgr */
2838c2ecf20Sopenharmony_cistatic u8 smcr_next_link_id(struct smc_link_group *lgr)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	u8 link_id;
2868c2ecf20Sopenharmony_ci	int i;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	while (1) {
2898c2ecf20Sopenharmony_ciagain:
2908c2ecf20Sopenharmony_ci		link_id = ++lgr->next_link_id;
2918c2ecf20Sopenharmony_ci		if (!link_id)	/* skip zero as link_id */
2928c2ecf20Sopenharmony_ci			link_id = ++lgr->next_link_id;
2938c2ecf20Sopenharmony_ci		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
2948c2ecf20Sopenharmony_ci			if (smc_link_usable(&lgr->lnk[i]) &&
2958c2ecf20Sopenharmony_ci			    lgr->lnk[i].link_id == link_id)
2968c2ecf20Sopenharmony_ci				goto again;
2978c2ecf20Sopenharmony_ci		}
2988c2ecf20Sopenharmony_ci		break;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci	return link_id;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ciint smcr_link_init(struct smc_link_group *lgr, struct smc_link *lnk,
3048c2ecf20Sopenharmony_ci		   u8 link_idx, struct smc_init_info *ini)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	u8 rndvec[3];
3078c2ecf20Sopenharmony_ci	int rc;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	get_device(&ini->ib_dev->ibdev->dev);
3108c2ecf20Sopenharmony_ci	atomic_inc(&ini->ib_dev->lnk_cnt);
3118c2ecf20Sopenharmony_ci	lnk->link_id = smcr_next_link_id(lgr);
3128c2ecf20Sopenharmony_ci	lnk->lgr = lgr;
3138c2ecf20Sopenharmony_ci	lnk->link_idx = link_idx;
3148c2ecf20Sopenharmony_ci	lnk->smcibdev = ini->ib_dev;
3158c2ecf20Sopenharmony_ci	lnk->ibport = ini->ib_port;
3168c2ecf20Sopenharmony_ci	lnk->path_mtu = ini->ib_dev->pattr[ini->ib_port - 1].active_mtu;
3178c2ecf20Sopenharmony_ci	smc_llc_link_set_uid(lnk);
3188c2ecf20Sopenharmony_ci	INIT_WORK(&lnk->link_down_wrk, smc_link_down_work);
3198c2ecf20Sopenharmony_ci	if (!ini->ib_dev->initialized) {
3208c2ecf20Sopenharmony_ci		rc = (int)smc_ib_setup_per_ibdev(ini->ib_dev);
3218c2ecf20Sopenharmony_ci		if (rc)
3228c2ecf20Sopenharmony_ci			goto out;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci	get_random_bytes(rndvec, sizeof(rndvec));
3258c2ecf20Sopenharmony_ci	lnk->psn_initial = rndvec[0] + (rndvec[1] << 8) +
3268c2ecf20Sopenharmony_ci		(rndvec[2] << 16);
3278c2ecf20Sopenharmony_ci	rc = smc_ib_determine_gid(lnk->smcibdev, lnk->ibport,
3288c2ecf20Sopenharmony_ci				  ini->vlan_id, lnk->gid, &lnk->sgid_index);
3298c2ecf20Sopenharmony_ci	if (rc)
3308c2ecf20Sopenharmony_ci		goto out;
3318c2ecf20Sopenharmony_ci	rc = smc_llc_link_init(lnk);
3328c2ecf20Sopenharmony_ci	if (rc)
3338c2ecf20Sopenharmony_ci		goto out;
3348c2ecf20Sopenharmony_ci	rc = smc_wr_alloc_link_mem(lnk);
3358c2ecf20Sopenharmony_ci	if (rc)
3368c2ecf20Sopenharmony_ci		goto clear_llc_lnk;
3378c2ecf20Sopenharmony_ci	rc = smc_ib_create_protection_domain(lnk);
3388c2ecf20Sopenharmony_ci	if (rc)
3398c2ecf20Sopenharmony_ci		goto free_link_mem;
3408c2ecf20Sopenharmony_ci	rc = smc_ib_create_queue_pair(lnk);
3418c2ecf20Sopenharmony_ci	if (rc)
3428c2ecf20Sopenharmony_ci		goto dealloc_pd;
3438c2ecf20Sopenharmony_ci	rc = smc_wr_create_link(lnk);
3448c2ecf20Sopenharmony_ci	if (rc)
3458c2ecf20Sopenharmony_ci		goto destroy_qp;
3468c2ecf20Sopenharmony_ci	lnk->state = SMC_LNK_ACTIVATING;
3478c2ecf20Sopenharmony_ci	return 0;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cidestroy_qp:
3508c2ecf20Sopenharmony_ci	smc_ib_destroy_queue_pair(lnk);
3518c2ecf20Sopenharmony_cidealloc_pd:
3528c2ecf20Sopenharmony_ci	smc_ib_dealloc_protection_domain(lnk);
3538c2ecf20Sopenharmony_cifree_link_mem:
3548c2ecf20Sopenharmony_ci	smc_wr_free_link_mem(lnk);
3558c2ecf20Sopenharmony_ciclear_llc_lnk:
3568c2ecf20Sopenharmony_ci	smc_llc_link_clear(lnk, false);
3578c2ecf20Sopenharmony_ciout:
3588c2ecf20Sopenharmony_ci	put_device(&ini->ib_dev->ibdev->dev);
3598c2ecf20Sopenharmony_ci	memset(lnk, 0, sizeof(struct smc_link));
3608c2ecf20Sopenharmony_ci	lnk->state = SMC_LNK_UNUSED;
3618c2ecf20Sopenharmony_ci	if (!atomic_dec_return(&ini->ib_dev->lnk_cnt))
3628c2ecf20Sopenharmony_ci		wake_up(&ini->ib_dev->lnks_deleted);
3638c2ecf20Sopenharmony_ci	return rc;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci/* create a new SMC link group */
3678c2ecf20Sopenharmony_cistatic int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	struct smc_link_group *lgr;
3708c2ecf20Sopenharmony_ci	struct list_head *lgr_list;
3718c2ecf20Sopenharmony_ci	struct smc_link *lnk;
3728c2ecf20Sopenharmony_ci	spinlock_t *lgr_lock;
3738c2ecf20Sopenharmony_ci	u8 link_idx;
3748c2ecf20Sopenharmony_ci	int rc = 0;
3758c2ecf20Sopenharmony_ci	int i;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (ini->is_smcd && ini->vlan_id) {
3788c2ecf20Sopenharmony_ci		if (smc_ism_get_vlan(ini->ism_dev[ini->ism_selected],
3798c2ecf20Sopenharmony_ci				     ini->vlan_id)) {
3808c2ecf20Sopenharmony_ci			rc = SMC_CLC_DECL_ISMVLANERR;
3818c2ecf20Sopenharmony_ci			goto out;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	lgr = kzalloc(sizeof(*lgr), GFP_KERNEL);
3868c2ecf20Sopenharmony_ci	if (!lgr) {
3878c2ecf20Sopenharmony_ci		rc = SMC_CLC_DECL_MEM;
3888c2ecf20Sopenharmony_ci		goto ism_put_vlan;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci	lgr->tx_wq = alloc_workqueue("smc_tx_wq-%*phN", 0, 0,
3918c2ecf20Sopenharmony_ci				     SMC_LGR_ID_SIZE, &lgr->id);
3928c2ecf20Sopenharmony_ci	if (!lgr->tx_wq) {
3938c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3948c2ecf20Sopenharmony_ci		goto free_lgr;
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci	lgr->is_smcd = ini->is_smcd;
3978c2ecf20Sopenharmony_ci	lgr->sync_err = 0;
3988c2ecf20Sopenharmony_ci	lgr->terminating = 0;
3998c2ecf20Sopenharmony_ci	lgr->freeing = 0;
4008c2ecf20Sopenharmony_ci	lgr->vlan_id = ini->vlan_id;
4018c2ecf20Sopenharmony_ci	mutex_init(&lgr->sndbufs_lock);
4028c2ecf20Sopenharmony_ci	mutex_init(&lgr->rmbs_lock);
4038c2ecf20Sopenharmony_ci	rwlock_init(&lgr->conns_lock);
4048c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_RMBE_SIZES; i++) {
4058c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&lgr->sndbufs[i]);
4068c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&lgr->rmbs[i]);
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci	lgr->next_link_id = 0;
4098c2ecf20Sopenharmony_ci	smc_lgr_list.num += SMC_LGR_NUM_INCR;
4108c2ecf20Sopenharmony_ci	memcpy(&lgr->id, (u8 *)&smc_lgr_list.num, SMC_LGR_ID_SIZE);
4118c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&lgr->free_work, smc_lgr_free_work);
4128c2ecf20Sopenharmony_ci	INIT_WORK(&lgr->terminate_work, smc_lgr_terminate_work);
4138c2ecf20Sopenharmony_ci	lgr->conns_all = RB_ROOT;
4148c2ecf20Sopenharmony_ci	if (ini->is_smcd) {
4158c2ecf20Sopenharmony_ci		/* SMC-D specific settings */
4168c2ecf20Sopenharmony_ci		get_device(&ini->ism_dev[ini->ism_selected]->dev);
4178c2ecf20Sopenharmony_ci		lgr->peer_gid = ini->ism_peer_gid[ini->ism_selected];
4188c2ecf20Sopenharmony_ci		lgr->smcd = ini->ism_dev[ini->ism_selected];
4198c2ecf20Sopenharmony_ci		lgr_list = &ini->ism_dev[ini->ism_selected]->lgr_list;
4208c2ecf20Sopenharmony_ci		lgr_lock = &lgr->smcd->lgr_lock;
4218c2ecf20Sopenharmony_ci		lgr->smc_version = ini->smcd_version;
4228c2ecf20Sopenharmony_ci		lgr->peer_shutdown = 0;
4238c2ecf20Sopenharmony_ci		atomic_inc(&ini->ism_dev[ini->ism_selected]->lgr_cnt);
4248c2ecf20Sopenharmony_ci	} else {
4258c2ecf20Sopenharmony_ci		/* SMC-R specific settings */
4268c2ecf20Sopenharmony_ci		lgr->role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
4278c2ecf20Sopenharmony_ci		memcpy(lgr->peer_systemid, ini->ib_lcl->id_for_peer,
4288c2ecf20Sopenharmony_ci		       SMC_SYSTEMID_LEN);
4298c2ecf20Sopenharmony_ci		memcpy(lgr->pnet_id, ini->ib_dev->pnetid[ini->ib_port - 1],
4308c2ecf20Sopenharmony_ci		       SMC_MAX_PNETID_LEN);
4318c2ecf20Sopenharmony_ci		smc_llc_lgr_init(lgr, smc);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		link_idx = SMC_SINGLE_LINK;
4348c2ecf20Sopenharmony_ci		lnk = &lgr->lnk[link_idx];
4358c2ecf20Sopenharmony_ci		rc = smcr_link_init(lgr, lnk, link_idx, ini);
4368c2ecf20Sopenharmony_ci		if (rc)
4378c2ecf20Sopenharmony_ci			goto free_wq;
4388c2ecf20Sopenharmony_ci		lgr_list = &smc_lgr_list.list;
4398c2ecf20Sopenharmony_ci		lgr_lock = &smc_lgr_list.lock;
4408c2ecf20Sopenharmony_ci		atomic_inc(&lgr_cnt);
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci	smc->conn.lgr = lgr;
4438c2ecf20Sopenharmony_ci	spin_lock_bh(lgr_lock);
4448c2ecf20Sopenharmony_ci	list_add_tail(&lgr->list, lgr_list);
4458c2ecf20Sopenharmony_ci	spin_unlock_bh(lgr_lock);
4468c2ecf20Sopenharmony_ci	return 0;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cifree_wq:
4498c2ecf20Sopenharmony_ci	destroy_workqueue(lgr->tx_wq);
4508c2ecf20Sopenharmony_cifree_lgr:
4518c2ecf20Sopenharmony_ci	kfree(lgr);
4528c2ecf20Sopenharmony_ciism_put_vlan:
4538c2ecf20Sopenharmony_ci	if (ini->is_smcd && ini->vlan_id)
4548c2ecf20Sopenharmony_ci		smc_ism_put_vlan(ini->ism_dev[ini->ism_selected], ini->vlan_id);
4558c2ecf20Sopenharmony_ciout:
4568c2ecf20Sopenharmony_ci	if (rc < 0) {
4578c2ecf20Sopenharmony_ci		if (rc == -ENOMEM)
4588c2ecf20Sopenharmony_ci			rc = SMC_CLC_DECL_MEM;
4598c2ecf20Sopenharmony_ci		else
4608c2ecf20Sopenharmony_ci			rc = SMC_CLC_DECL_INTERR;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci	return rc;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int smc_write_space(struct smc_connection *conn)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	int buffer_len = conn->peer_rmbe_size;
4688c2ecf20Sopenharmony_ci	union smc_host_cursor prod;
4698c2ecf20Sopenharmony_ci	union smc_host_cursor cons;
4708c2ecf20Sopenharmony_ci	int space;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	smc_curs_copy(&prod, &conn->local_tx_ctrl.prod, conn);
4738c2ecf20Sopenharmony_ci	smc_curs_copy(&cons, &conn->local_rx_ctrl.cons, conn);
4748c2ecf20Sopenharmony_ci	/* determine rx_buf space */
4758c2ecf20Sopenharmony_ci	space = buffer_len - smc_curs_diff(buffer_len, &cons, &prod);
4768c2ecf20Sopenharmony_ci	return space;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic int smc_switch_cursor(struct smc_sock *smc, struct smc_cdc_tx_pend *pend,
4808c2ecf20Sopenharmony_ci			     struct smc_wr_buf *wr_buf)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct smc_connection *conn = &smc->conn;
4838c2ecf20Sopenharmony_ci	union smc_host_cursor cons, fin;
4848c2ecf20Sopenharmony_ci	int rc = 0;
4858c2ecf20Sopenharmony_ci	int diff;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	smc_curs_copy(&conn->tx_curs_sent, &conn->tx_curs_fin, conn);
4888c2ecf20Sopenharmony_ci	smc_curs_copy(&fin, &conn->local_tx_ctrl_fin, conn);
4898c2ecf20Sopenharmony_ci	/* set prod cursor to old state, enforce tx_rdma_writes() */
4908c2ecf20Sopenharmony_ci	smc_curs_copy(&conn->local_tx_ctrl.prod, &fin, conn);
4918c2ecf20Sopenharmony_ci	smc_curs_copy(&cons, &conn->local_rx_ctrl.cons, conn);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if (smc_curs_comp(conn->peer_rmbe_size, &cons, &fin) < 0) {
4948c2ecf20Sopenharmony_ci		/* cons cursor advanced more than fin, and prod was set
4958c2ecf20Sopenharmony_ci		 * fin above, so now prod is smaller than cons. Fix that.
4968c2ecf20Sopenharmony_ci		 */
4978c2ecf20Sopenharmony_ci		diff = smc_curs_diff(conn->peer_rmbe_size, &fin, &cons);
4988c2ecf20Sopenharmony_ci		smc_curs_add(conn->sndbuf_desc->len,
4998c2ecf20Sopenharmony_ci			     &conn->tx_curs_sent, diff);
5008c2ecf20Sopenharmony_ci		smc_curs_add(conn->sndbuf_desc->len,
5018c2ecf20Sopenharmony_ci			     &conn->tx_curs_fin, diff);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci		smp_mb__before_atomic();
5048c2ecf20Sopenharmony_ci		atomic_add(diff, &conn->sndbuf_space);
5058c2ecf20Sopenharmony_ci		smp_mb__after_atomic();
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci		smc_curs_add(conn->peer_rmbe_size,
5088c2ecf20Sopenharmony_ci			     &conn->local_tx_ctrl.prod, diff);
5098c2ecf20Sopenharmony_ci		smc_curs_add(conn->peer_rmbe_size,
5108c2ecf20Sopenharmony_ci			     &conn->local_tx_ctrl_fin, diff);
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci	/* recalculate, value is used by tx_rdma_writes() */
5138c2ecf20Sopenharmony_ci	atomic_set(&smc->conn.peer_rmbe_space, smc_write_space(conn));
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (smc->sk.sk_state != SMC_INIT &&
5168c2ecf20Sopenharmony_ci	    smc->sk.sk_state != SMC_CLOSED) {
5178c2ecf20Sopenharmony_ci		rc = smcr_cdc_msg_send_validation(conn, pend, wr_buf);
5188c2ecf20Sopenharmony_ci		if (!rc) {
5198c2ecf20Sopenharmony_ci			queue_delayed_work(conn->lgr->tx_wq, &conn->tx_work, 0);
5208c2ecf20Sopenharmony_ci			smc->sk.sk_data_ready(&smc->sk);
5218c2ecf20Sopenharmony_ci		}
5228c2ecf20Sopenharmony_ci	} else {
5238c2ecf20Sopenharmony_ci		smc_wr_tx_put_slot(conn->lnk,
5248c2ecf20Sopenharmony_ci				   (struct smc_wr_tx_pend_priv *)pend);
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci	return rc;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistruct smc_link *smc_switch_conns(struct smc_link_group *lgr,
5308c2ecf20Sopenharmony_ci				  struct smc_link *from_lnk, bool is_dev_err)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	struct smc_link *to_lnk = NULL;
5338c2ecf20Sopenharmony_ci	struct smc_cdc_tx_pend *pend;
5348c2ecf20Sopenharmony_ci	struct smc_connection *conn;
5358c2ecf20Sopenharmony_ci	struct smc_wr_buf *wr_buf;
5368c2ecf20Sopenharmony_ci	struct smc_sock *smc;
5378c2ecf20Sopenharmony_ci	struct rb_node *node;
5388c2ecf20Sopenharmony_ci	int i, rc = 0;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/* link is inactive, wake up tx waiters */
5418c2ecf20Sopenharmony_ci	smc_wr_wakeup_tx_wait(from_lnk);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
5448c2ecf20Sopenharmony_ci		if (!smc_link_active(&lgr->lnk[i]) || i == from_lnk->link_idx)
5458c2ecf20Sopenharmony_ci			continue;
5468c2ecf20Sopenharmony_ci		if (is_dev_err && from_lnk->smcibdev == lgr->lnk[i].smcibdev &&
5478c2ecf20Sopenharmony_ci		    from_lnk->ibport == lgr->lnk[i].ibport) {
5488c2ecf20Sopenharmony_ci			continue;
5498c2ecf20Sopenharmony_ci		}
5508c2ecf20Sopenharmony_ci		to_lnk = &lgr->lnk[i];
5518c2ecf20Sopenharmony_ci		break;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci	if (!to_lnk || !smc_wr_tx_link_hold(to_lnk)) {
5548c2ecf20Sopenharmony_ci		smc_lgr_terminate_sched(lgr);
5558c2ecf20Sopenharmony_ci		return NULL;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ciagain:
5588c2ecf20Sopenharmony_ci	read_lock_bh(&lgr->conns_lock);
5598c2ecf20Sopenharmony_ci	for (node = rb_first(&lgr->conns_all); node; node = rb_next(node)) {
5608c2ecf20Sopenharmony_ci		conn = rb_entry(node, struct smc_connection, alert_node);
5618c2ecf20Sopenharmony_ci		if (conn->lnk != from_lnk)
5628c2ecf20Sopenharmony_ci			continue;
5638c2ecf20Sopenharmony_ci		smc = container_of(conn, struct smc_sock, conn);
5648c2ecf20Sopenharmony_ci		/* conn->lnk not yet set in SMC_INIT state */
5658c2ecf20Sopenharmony_ci		if (smc->sk.sk_state == SMC_INIT)
5668c2ecf20Sopenharmony_ci			continue;
5678c2ecf20Sopenharmony_ci		if (smc->sk.sk_state == SMC_CLOSED ||
5688c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_PEERCLOSEWAIT1 ||
5698c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_PEERCLOSEWAIT2 ||
5708c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_APPFINCLOSEWAIT ||
5718c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_APPCLOSEWAIT1 ||
5728c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_APPCLOSEWAIT2 ||
5738c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_PEERFINCLOSEWAIT ||
5748c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_PEERABORTWAIT ||
5758c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_PROCESSABORT) {
5768c2ecf20Sopenharmony_ci			spin_lock_bh(&conn->send_lock);
5778c2ecf20Sopenharmony_ci			conn->lnk = to_lnk;
5788c2ecf20Sopenharmony_ci			spin_unlock_bh(&conn->send_lock);
5798c2ecf20Sopenharmony_ci			continue;
5808c2ecf20Sopenharmony_ci		}
5818c2ecf20Sopenharmony_ci		sock_hold(&smc->sk);
5828c2ecf20Sopenharmony_ci		read_unlock_bh(&lgr->conns_lock);
5838c2ecf20Sopenharmony_ci		/* pre-fetch buffer outside of send_lock, might sleep */
5848c2ecf20Sopenharmony_ci		rc = smc_cdc_get_free_slot(conn, to_lnk, &wr_buf, NULL, &pend);
5858c2ecf20Sopenharmony_ci		if (rc)
5868c2ecf20Sopenharmony_ci			goto err_out;
5878c2ecf20Sopenharmony_ci		/* avoid race with smcr_tx_sndbuf_nonempty() */
5888c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->send_lock);
5898c2ecf20Sopenharmony_ci		conn->lnk = to_lnk;
5908c2ecf20Sopenharmony_ci		rc = smc_switch_cursor(smc, pend, wr_buf);
5918c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->send_lock);
5928c2ecf20Sopenharmony_ci		sock_put(&smc->sk);
5938c2ecf20Sopenharmony_ci		if (rc)
5948c2ecf20Sopenharmony_ci			goto err_out;
5958c2ecf20Sopenharmony_ci		goto again;
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci	read_unlock_bh(&lgr->conns_lock);
5988c2ecf20Sopenharmony_ci	smc_wr_tx_link_put(to_lnk);
5998c2ecf20Sopenharmony_ci	return to_lnk;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cierr_out:
6028c2ecf20Sopenharmony_ci	smcr_link_down_cond_sched(to_lnk);
6038c2ecf20Sopenharmony_ci	smc_wr_tx_link_put(to_lnk);
6048c2ecf20Sopenharmony_ci	return NULL;
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic void smcr_buf_unuse(struct smc_buf_desc *rmb_desc,
6088c2ecf20Sopenharmony_ci			   struct smc_link_group *lgr)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	int rc;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (rmb_desc->is_conf_rkey && !list_empty(&lgr->list)) {
6138c2ecf20Sopenharmony_ci		/* unregister rmb with peer */
6148c2ecf20Sopenharmony_ci		rc = smc_llc_flow_initiate(lgr, SMC_LLC_FLOW_RKEY);
6158c2ecf20Sopenharmony_ci		if (!rc) {
6168c2ecf20Sopenharmony_ci			/* protect against smc_llc_cli_rkey_exchange() */
6178c2ecf20Sopenharmony_ci			mutex_lock(&lgr->llc_conf_mutex);
6188c2ecf20Sopenharmony_ci			smc_llc_do_delete_rkey(lgr, rmb_desc);
6198c2ecf20Sopenharmony_ci			rmb_desc->is_conf_rkey = false;
6208c2ecf20Sopenharmony_ci			mutex_unlock(&lgr->llc_conf_mutex);
6218c2ecf20Sopenharmony_ci			smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
6228c2ecf20Sopenharmony_ci		}
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	if (rmb_desc->is_reg_err) {
6268c2ecf20Sopenharmony_ci		/* buf registration failed, reuse not possible */
6278c2ecf20Sopenharmony_ci		mutex_lock(&lgr->rmbs_lock);
6288c2ecf20Sopenharmony_ci		list_del(&rmb_desc->list);
6298c2ecf20Sopenharmony_ci		mutex_unlock(&lgr->rmbs_lock);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci		smc_buf_free(lgr, true, rmb_desc);
6328c2ecf20Sopenharmony_ci	} else {
6338c2ecf20Sopenharmony_ci		rmb_desc->used = 0;
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_cistatic void smc_buf_unuse(struct smc_connection *conn,
6388c2ecf20Sopenharmony_ci			  struct smc_link_group *lgr)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	if (conn->sndbuf_desc)
6418c2ecf20Sopenharmony_ci		conn->sndbuf_desc->used = 0;
6428c2ecf20Sopenharmony_ci	if (conn->rmb_desc && lgr->is_smcd)
6438c2ecf20Sopenharmony_ci		conn->rmb_desc->used = 0;
6448c2ecf20Sopenharmony_ci	else if (conn->rmb_desc)
6458c2ecf20Sopenharmony_ci		smcr_buf_unuse(conn->rmb_desc, lgr);
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci/* remove a finished connection from its link group */
6498c2ecf20Sopenharmony_civoid smc_conn_free(struct smc_connection *conn)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = conn->lgr;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (!lgr)
6548c2ecf20Sopenharmony_ci		return;
6558c2ecf20Sopenharmony_ci	if (lgr->is_smcd) {
6568c2ecf20Sopenharmony_ci		if (!list_empty(&lgr->list))
6578c2ecf20Sopenharmony_ci			smc_ism_unset_conn(conn);
6588c2ecf20Sopenharmony_ci		tasklet_kill(&conn->rx_tsklet);
6598c2ecf20Sopenharmony_ci	} else {
6608c2ecf20Sopenharmony_ci		smc_cdc_wait_pend_tx_wr(conn);
6618c2ecf20Sopenharmony_ci		if (current_work() != &conn->abort_work)
6628c2ecf20Sopenharmony_ci			cancel_work_sync(&conn->abort_work);
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci	if (!list_empty(&lgr->list)) {
6658c2ecf20Sopenharmony_ci		smc_buf_unuse(conn, lgr); /* allow buffer reuse */
6668c2ecf20Sopenharmony_ci		smc_lgr_unregister_conn(conn);
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (!lgr->conns_num)
6708c2ecf20Sopenharmony_ci		smc_lgr_schedule_free_work(lgr);
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci/* unregister a link from a buf_desc */
6748c2ecf20Sopenharmony_cistatic void smcr_buf_unmap_link(struct smc_buf_desc *buf_desc, bool is_rmb,
6758c2ecf20Sopenharmony_ci				struct smc_link *lnk)
6768c2ecf20Sopenharmony_ci{
6778c2ecf20Sopenharmony_ci	if (is_rmb)
6788c2ecf20Sopenharmony_ci		buf_desc->is_reg_mr[lnk->link_idx] = false;
6798c2ecf20Sopenharmony_ci	if (!buf_desc->is_map_ib[lnk->link_idx])
6808c2ecf20Sopenharmony_ci		return;
6818c2ecf20Sopenharmony_ci	if (is_rmb) {
6828c2ecf20Sopenharmony_ci		if (buf_desc->mr_rx[lnk->link_idx]) {
6838c2ecf20Sopenharmony_ci			smc_ib_put_memory_region(
6848c2ecf20Sopenharmony_ci					buf_desc->mr_rx[lnk->link_idx]);
6858c2ecf20Sopenharmony_ci			buf_desc->mr_rx[lnk->link_idx] = NULL;
6868c2ecf20Sopenharmony_ci		}
6878c2ecf20Sopenharmony_ci		smc_ib_buf_unmap_sg(lnk, buf_desc, DMA_FROM_DEVICE);
6888c2ecf20Sopenharmony_ci	} else {
6898c2ecf20Sopenharmony_ci		smc_ib_buf_unmap_sg(lnk, buf_desc, DMA_TO_DEVICE);
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci	sg_free_table(&buf_desc->sgt[lnk->link_idx]);
6928c2ecf20Sopenharmony_ci	buf_desc->is_map_ib[lnk->link_idx] = false;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci/* unmap all buffers of lgr for a deleted link */
6968c2ecf20Sopenharmony_cistatic void smcr_buf_unmap_lgr(struct smc_link *lnk)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = lnk->lgr;
6998c2ecf20Sopenharmony_ci	struct smc_buf_desc *buf_desc, *bf;
7008c2ecf20Sopenharmony_ci	int i;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_RMBE_SIZES; i++) {
7038c2ecf20Sopenharmony_ci		mutex_lock(&lgr->rmbs_lock);
7048c2ecf20Sopenharmony_ci		list_for_each_entry_safe(buf_desc, bf, &lgr->rmbs[i], list)
7058c2ecf20Sopenharmony_ci			smcr_buf_unmap_link(buf_desc, true, lnk);
7068c2ecf20Sopenharmony_ci		mutex_unlock(&lgr->rmbs_lock);
7078c2ecf20Sopenharmony_ci		mutex_lock(&lgr->sndbufs_lock);
7088c2ecf20Sopenharmony_ci		list_for_each_entry_safe(buf_desc, bf, &lgr->sndbufs[i],
7098c2ecf20Sopenharmony_ci					 list)
7108c2ecf20Sopenharmony_ci			smcr_buf_unmap_link(buf_desc, false, lnk);
7118c2ecf20Sopenharmony_ci		mutex_unlock(&lgr->sndbufs_lock);
7128c2ecf20Sopenharmony_ci	}
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic void smcr_rtoken_clear_link(struct smc_link *lnk)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = lnk->lgr;
7188c2ecf20Sopenharmony_ci	int i;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
7218c2ecf20Sopenharmony_ci		lgr->rtokens[i][lnk->link_idx].rkey = 0;
7228c2ecf20Sopenharmony_ci		lgr->rtokens[i][lnk->link_idx].dma_addr = 0;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci/* must be called under lgr->llc_conf_mutex lock */
7278c2ecf20Sopenharmony_civoid smcr_link_clear(struct smc_link *lnk, bool log)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	struct smc_ib_device *smcibdev;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	if (!lnk->lgr || lnk->state == SMC_LNK_UNUSED)
7328c2ecf20Sopenharmony_ci		return;
7338c2ecf20Sopenharmony_ci	lnk->peer_qpn = 0;
7348c2ecf20Sopenharmony_ci	smc_llc_link_clear(lnk, log);
7358c2ecf20Sopenharmony_ci	smcr_buf_unmap_lgr(lnk);
7368c2ecf20Sopenharmony_ci	smcr_rtoken_clear_link(lnk);
7378c2ecf20Sopenharmony_ci	smc_ib_modify_qp_error(lnk);
7388c2ecf20Sopenharmony_ci	smc_wr_free_link(lnk);
7398c2ecf20Sopenharmony_ci	smc_ib_destroy_queue_pair(lnk);
7408c2ecf20Sopenharmony_ci	smc_ib_dealloc_protection_domain(lnk);
7418c2ecf20Sopenharmony_ci	smc_wr_free_link_mem(lnk);
7428c2ecf20Sopenharmony_ci	put_device(&lnk->smcibdev->ibdev->dev);
7438c2ecf20Sopenharmony_ci	smcibdev = lnk->smcibdev;
7448c2ecf20Sopenharmony_ci	memset(lnk, 0, sizeof(struct smc_link));
7458c2ecf20Sopenharmony_ci	lnk->state = SMC_LNK_UNUSED;
7468c2ecf20Sopenharmony_ci	if (!atomic_dec_return(&smcibdev->lnk_cnt))
7478c2ecf20Sopenharmony_ci		wake_up(&smcibdev->lnks_deleted);
7488c2ecf20Sopenharmony_ci}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_cistatic void smcr_buf_free(struct smc_link_group *lgr, bool is_rmb,
7518c2ecf20Sopenharmony_ci			  struct smc_buf_desc *buf_desc)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	int i;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
7568c2ecf20Sopenharmony_ci		smcr_buf_unmap_link(buf_desc, is_rmb, &lgr->lnk[i]);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	if (buf_desc->pages)
7598c2ecf20Sopenharmony_ci		__free_pages(buf_desc->pages, buf_desc->order);
7608c2ecf20Sopenharmony_ci	kfree(buf_desc);
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic void smcd_buf_free(struct smc_link_group *lgr, bool is_dmb,
7648c2ecf20Sopenharmony_ci			  struct smc_buf_desc *buf_desc)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	if (is_dmb) {
7678c2ecf20Sopenharmony_ci		/* restore original buf len */
7688c2ecf20Sopenharmony_ci		buf_desc->len += sizeof(struct smcd_cdc_msg);
7698c2ecf20Sopenharmony_ci		smc_ism_unregister_dmb(lgr->smcd, buf_desc);
7708c2ecf20Sopenharmony_ci	} else {
7718c2ecf20Sopenharmony_ci		kfree(buf_desc->cpu_addr);
7728c2ecf20Sopenharmony_ci	}
7738c2ecf20Sopenharmony_ci	kfree(buf_desc);
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cistatic void smc_buf_free(struct smc_link_group *lgr, bool is_rmb,
7778c2ecf20Sopenharmony_ci			 struct smc_buf_desc *buf_desc)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	if (lgr->is_smcd)
7808c2ecf20Sopenharmony_ci		smcd_buf_free(lgr, is_rmb, buf_desc);
7818c2ecf20Sopenharmony_ci	else
7828c2ecf20Sopenharmony_ci		smcr_buf_free(lgr, is_rmb, buf_desc);
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic void __smc_lgr_free_bufs(struct smc_link_group *lgr, bool is_rmb)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	struct smc_buf_desc *buf_desc, *bf_desc;
7888c2ecf20Sopenharmony_ci	struct list_head *buf_list;
7898c2ecf20Sopenharmony_ci	int i;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_RMBE_SIZES; i++) {
7928c2ecf20Sopenharmony_ci		if (is_rmb)
7938c2ecf20Sopenharmony_ci			buf_list = &lgr->rmbs[i];
7948c2ecf20Sopenharmony_ci		else
7958c2ecf20Sopenharmony_ci			buf_list = &lgr->sndbufs[i];
7968c2ecf20Sopenharmony_ci		list_for_each_entry_safe(buf_desc, bf_desc, buf_list,
7978c2ecf20Sopenharmony_ci					 list) {
7988c2ecf20Sopenharmony_ci			list_del(&buf_desc->list);
7998c2ecf20Sopenharmony_ci			smc_buf_free(lgr, is_rmb, buf_desc);
8008c2ecf20Sopenharmony_ci		}
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic void smc_lgr_free_bufs(struct smc_link_group *lgr)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	/* free send buffers */
8078c2ecf20Sopenharmony_ci	__smc_lgr_free_bufs(lgr, false);
8088c2ecf20Sopenharmony_ci	/* free rmbs */
8098c2ecf20Sopenharmony_ci	__smc_lgr_free_bufs(lgr, true);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci/* remove a link group */
8138c2ecf20Sopenharmony_cistatic void smc_lgr_free(struct smc_link_group *lgr)
8148c2ecf20Sopenharmony_ci{
8158c2ecf20Sopenharmony_ci	int i;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	if (!lgr->is_smcd) {
8188c2ecf20Sopenharmony_ci		mutex_lock(&lgr->llc_conf_mutex);
8198c2ecf20Sopenharmony_ci		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
8208c2ecf20Sopenharmony_ci			if (lgr->lnk[i].state != SMC_LNK_UNUSED)
8218c2ecf20Sopenharmony_ci				smcr_link_clear(&lgr->lnk[i], false);
8228c2ecf20Sopenharmony_ci		}
8238c2ecf20Sopenharmony_ci		mutex_unlock(&lgr->llc_conf_mutex);
8248c2ecf20Sopenharmony_ci		smc_llc_lgr_clear(lgr);
8258c2ecf20Sopenharmony_ci	}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	smc_lgr_free_bufs(lgr);
8288c2ecf20Sopenharmony_ci	destroy_workqueue(lgr->tx_wq);
8298c2ecf20Sopenharmony_ci	if (lgr->is_smcd) {
8308c2ecf20Sopenharmony_ci		smc_ism_put_vlan(lgr->smcd, lgr->vlan_id);
8318c2ecf20Sopenharmony_ci		put_device(&lgr->smcd->dev);
8328c2ecf20Sopenharmony_ci		if (!atomic_dec_return(&lgr->smcd->lgr_cnt))
8338c2ecf20Sopenharmony_ci			wake_up(&lgr->smcd->lgrs_deleted);
8348c2ecf20Sopenharmony_ci	} else {
8358c2ecf20Sopenharmony_ci		if (!atomic_dec_return(&lgr_cnt))
8368c2ecf20Sopenharmony_ci			wake_up(&lgrs_deleted);
8378c2ecf20Sopenharmony_ci	}
8388c2ecf20Sopenharmony_ci	kfree(lgr);
8398c2ecf20Sopenharmony_ci}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_cistatic void smcd_unregister_all_dmbs(struct smc_link_group *lgr)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci	int i;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_RMBE_SIZES; i++) {
8468c2ecf20Sopenharmony_ci		struct smc_buf_desc *buf_desc;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci		list_for_each_entry(buf_desc, &lgr->rmbs[i], list) {
8498c2ecf20Sopenharmony_ci			buf_desc->len += sizeof(struct smcd_cdc_msg);
8508c2ecf20Sopenharmony_ci			smc_ism_unregister_dmb(lgr->smcd, buf_desc);
8518c2ecf20Sopenharmony_ci		}
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_cistatic void smc_sk_wake_ups(struct smc_sock *smc)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	smc->sk.sk_write_space(&smc->sk);
8588c2ecf20Sopenharmony_ci	smc->sk.sk_data_ready(&smc->sk);
8598c2ecf20Sopenharmony_ci	smc->sk.sk_state_change(&smc->sk);
8608c2ecf20Sopenharmony_ci}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci/* kill a connection */
8638c2ecf20Sopenharmony_cistatic void smc_conn_kill(struct smc_connection *conn, bool soft)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	if (conn->lgr->is_smcd && conn->lgr->peer_shutdown)
8688c2ecf20Sopenharmony_ci		conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
8698c2ecf20Sopenharmony_ci	else
8708c2ecf20Sopenharmony_ci		smc_close_abort(conn);
8718c2ecf20Sopenharmony_ci	conn->killed = 1;
8728c2ecf20Sopenharmony_ci	smc->sk.sk_err = ECONNABORTED;
8738c2ecf20Sopenharmony_ci	smc_sk_wake_ups(smc);
8748c2ecf20Sopenharmony_ci	if (conn->lgr->is_smcd) {
8758c2ecf20Sopenharmony_ci		smc_ism_unset_conn(conn);
8768c2ecf20Sopenharmony_ci		if (soft)
8778c2ecf20Sopenharmony_ci			tasklet_kill(&conn->rx_tsklet);
8788c2ecf20Sopenharmony_ci		else
8798c2ecf20Sopenharmony_ci			tasklet_unlock_wait(&conn->rx_tsklet);
8808c2ecf20Sopenharmony_ci	} else {
8818c2ecf20Sopenharmony_ci		smc_cdc_wait_pend_tx_wr(conn);
8828c2ecf20Sopenharmony_ci	}
8838c2ecf20Sopenharmony_ci	smc_lgr_unregister_conn(conn);
8848c2ecf20Sopenharmony_ci	smc_close_active_abort(smc);
8858c2ecf20Sopenharmony_ci}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_cistatic void smc_lgr_cleanup(struct smc_link_group *lgr)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	if (lgr->is_smcd) {
8908c2ecf20Sopenharmony_ci		smc_ism_signal_shutdown(lgr);
8918c2ecf20Sopenharmony_ci		smcd_unregister_all_dmbs(lgr);
8928c2ecf20Sopenharmony_ci	} else {
8938c2ecf20Sopenharmony_ci		u32 rsn = lgr->llc_termination_rsn;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci		if (!rsn)
8968c2ecf20Sopenharmony_ci			rsn = SMC_LLC_DEL_PROG_INIT_TERM;
8978c2ecf20Sopenharmony_ci		smc_llc_send_link_delete_all(lgr, false, rsn);
8988c2ecf20Sopenharmony_ci		smcr_lgr_link_deactivate_all(lgr);
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci/* terminate link group
9038c2ecf20Sopenharmony_ci * @soft: true if link group shutdown can take its time
9048c2ecf20Sopenharmony_ci *	  false if immediate link group shutdown is required
9058c2ecf20Sopenharmony_ci */
9068c2ecf20Sopenharmony_cistatic void __smc_lgr_terminate(struct smc_link_group *lgr, bool soft)
9078c2ecf20Sopenharmony_ci{
9088c2ecf20Sopenharmony_ci	struct smc_connection *conn;
9098c2ecf20Sopenharmony_ci	struct smc_sock *smc;
9108c2ecf20Sopenharmony_ci	struct rb_node *node;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	if (lgr->terminating)
9138c2ecf20Sopenharmony_ci		return;	/* lgr already terminating */
9148c2ecf20Sopenharmony_ci	/* cancel free_work sync, will terminate when lgr->freeing is set */
9158c2ecf20Sopenharmony_ci	cancel_delayed_work(&lgr->free_work);
9168c2ecf20Sopenharmony_ci	lgr->terminating = 1;
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	/* kill remaining link group connections */
9198c2ecf20Sopenharmony_ci	read_lock_bh(&lgr->conns_lock);
9208c2ecf20Sopenharmony_ci	node = rb_first(&lgr->conns_all);
9218c2ecf20Sopenharmony_ci	while (node) {
9228c2ecf20Sopenharmony_ci		read_unlock_bh(&lgr->conns_lock);
9238c2ecf20Sopenharmony_ci		conn = rb_entry(node, struct smc_connection, alert_node);
9248c2ecf20Sopenharmony_ci		smc = container_of(conn, struct smc_sock, conn);
9258c2ecf20Sopenharmony_ci		sock_hold(&smc->sk); /* sock_put below */
9268c2ecf20Sopenharmony_ci		lock_sock(&smc->sk);
9278c2ecf20Sopenharmony_ci		smc_conn_kill(conn, soft);
9288c2ecf20Sopenharmony_ci		release_sock(&smc->sk);
9298c2ecf20Sopenharmony_ci		sock_put(&smc->sk); /* sock_hold above */
9308c2ecf20Sopenharmony_ci		read_lock_bh(&lgr->conns_lock);
9318c2ecf20Sopenharmony_ci		node = rb_first(&lgr->conns_all);
9328c2ecf20Sopenharmony_ci	}
9338c2ecf20Sopenharmony_ci	read_unlock_bh(&lgr->conns_lock);
9348c2ecf20Sopenharmony_ci	smc_lgr_cleanup(lgr);
9358c2ecf20Sopenharmony_ci	smc_lgr_free(lgr);
9368c2ecf20Sopenharmony_ci}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci/* unlink link group and schedule termination */
9398c2ecf20Sopenharmony_civoid smc_lgr_terminate_sched(struct smc_link_group *lgr)
9408c2ecf20Sopenharmony_ci{
9418c2ecf20Sopenharmony_ci	spinlock_t *lgr_lock;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	smc_lgr_list_head(lgr, &lgr_lock);
9448c2ecf20Sopenharmony_ci	spin_lock_bh(lgr_lock);
9458c2ecf20Sopenharmony_ci	if (list_empty(&lgr->list) || lgr->terminating || lgr->freeing) {
9468c2ecf20Sopenharmony_ci		spin_unlock_bh(lgr_lock);
9478c2ecf20Sopenharmony_ci		return;	/* lgr already terminating */
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci	list_del_init(&lgr->list);
9508c2ecf20Sopenharmony_ci	lgr->freeing = 1;
9518c2ecf20Sopenharmony_ci	spin_unlock_bh(lgr_lock);
9528c2ecf20Sopenharmony_ci	schedule_work(&lgr->terminate_work);
9538c2ecf20Sopenharmony_ci}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci/* Called when peer lgr shutdown (regularly or abnormally) is received */
9568c2ecf20Sopenharmony_civoid smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid, unsigned short vlan)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	struct smc_link_group *lgr, *l;
9598c2ecf20Sopenharmony_ci	LIST_HEAD(lgr_free_list);
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	/* run common cleanup function and build free list */
9628c2ecf20Sopenharmony_ci	spin_lock_bh(&dev->lgr_lock);
9638c2ecf20Sopenharmony_ci	list_for_each_entry_safe(lgr, l, &dev->lgr_list, list) {
9648c2ecf20Sopenharmony_ci		if ((!peer_gid || lgr->peer_gid == peer_gid) &&
9658c2ecf20Sopenharmony_ci		    (vlan == VLAN_VID_MASK || lgr->vlan_id == vlan)) {
9668c2ecf20Sopenharmony_ci			if (peer_gid) /* peer triggered termination */
9678c2ecf20Sopenharmony_ci				lgr->peer_shutdown = 1;
9688c2ecf20Sopenharmony_ci			list_move(&lgr->list, &lgr_free_list);
9698c2ecf20Sopenharmony_ci			lgr->freeing = 1;
9708c2ecf20Sopenharmony_ci		}
9718c2ecf20Sopenharmony_ci	}
9728c2ecf20Sopenharmony_ci	spin_unlock_bh(&dev->lgr_lock);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	/* cancel the regular free workers and actually free lgrs */
9758c2ecf20Sopenharmony_ci	list_for_each_entry_safe(lgr, l, &lgr_free_list, list) {
9768c2ecf20Sopenharmony_ci		list_del_init(&lgr->list);
9778c2ecf20Sopenharmony_ci		schedule_work(&lgr->terminate_work);
9788c2ecf20Sopenharmony_ci	}
9798c2ecf20Sopenharmony_ci}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci/* Called when an SMCD device is removed or the smc module is unloaded */
9828c2ecf20Sopenharmony_civoid smc_smcd_terminate_all(struct smcd_dev *smcd)
9838c2ecf20Sopenharmony_ci{
9848c2ecf20Sopenharmony_ci	struct smc_link_group *lgr, *lg;
9858c2ecf20Sopenharmony_ci	LIST_HEAD(lgr_free_list);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	spin_lock_bh(&smcd->lgr_lock);
9888c2ecf20Sopenharmony_ci	list_splice_init(&smcd->lgr_list, &lgr_free_list);
9898c2ecf20Sopenharmony_ci	list_for_each_entry(lgr, &lgr_free_list, list)
9908c2ecf20Sopenharmony_ci		lgr->freeing = 1;
9918c2ecf20Sopenharmony_ci	spin_unlock_bh(&smcd->lgr_lock);
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	list_for_each_entry_safe(lgr, lg, &lgr_free_list, list) {
9948c2ecf20Sopenharmony_ci		list_del_init(&lgr->list);
9958c2ecf20Sopenharmony_ci		__smc_lgr_terminate(lgr, false);
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	if (atomic_read(&smcd->lgr_cnt))
9998c2ecf20Sopenharmony_ci		wait_event(smcd->lgrs_deleted, !atomic_read(&smcd->lgr_cnt));
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci/* Called when an SMCR device is removed or the smc module is unloaded.
10038c2ecf20Sopenharmony_ci * If smcibdev is given, all SMCR link groups using this device are terminated.
10048c2ecf20Sopenharmony_ci * If smcibdev is NULL, all SMCR link groups are terminated.
10058c2ecf20Sopenharmony_ci */
10068c2ecf20Sopenharmony_civoid smc_smcr_terminate_all(struct smc_ib_device *smcibdev)
10078c2ecf20Sopenharmony_ci{
10088c2ecf20Sopenharmony_ci	struct smc_link_group *lgr, *lg;
10098c2ecf20Sopenharmony_ci	LIST_HEAD(lgr_free_list);
10108c2ecf20Sopenharmony_ci	int i;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	spin_lock_bh(&smc_lgr_list.lock);
10138c2ecf20Sopenharmony_ci	if (!smcibdev) {
10148c2ecf20Sopenharmony_ci		list_splice_init(&smc_lgr_list.list, &lgr_free_list);
10158c2ecf20Sopenharmony_ci		list_for_each_entry(lgr, &lgr_free_list, list)
10168c2ecf20Sopenharmony_ci			lgr->freeing = 1;
10178c2ecf20Sopenharmony_ci	} else {
10188c2ecf20Sopenharmony_ci		list_for_each_entry_safe(lgr, lg, &smc_lgr_list.list, list) {
10198c2ecf20Sopenharmony_ci			for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
10208c2ecf20Sopenharmony_ci				if (lgr->lnk[i].smcibdev == smcibdev)
10218c2ecf20Sopenharmony_ci					smcr_link_down_cond_sched(&lgr->lnk[i]);
10228c2ecf20Sopenharmony_ci			}
10238c2ecf20Sopenharmony_ci		}
10248c2ecf20Sopenharmony_ci	}
10258c2ecf20Sopenharmony_ci	spin_unlock_bh(&smc_lgr_list.lock);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	list_for_each_entry_safe(lgr, lg, &lgr_free_list, list) {
10288c2ecf20Sopenharmony_ci		list_del_init(&lgr->list);
10298c2ecf20Sopenharmony_ci		smc_llc_set_termination_rsn(lgr, SMC_LLC_DEL_OP_INIT_TERM);
10308c2ecf20Sopenharmony_ci		__smc_lgr_terminate(lgr, false);
10318c2ecf20Sopenharmony_ci	}
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	if (smcibdev) {
10348c2ecf20Sopenharmony_ci		if (atomic_read(&smcibdev->lnk_cnt))
10358c2ecf20Sopenharmony_ci			wait_event(smcibdev->lnks_deleted,
10368c2ecf20Sopenharmony_ci				   !atomic_read(&smcibdev->lnk_cnt));
10378c2ecf20Sopenharmony_ci	} else {
10388c2ecf20Sopenharmony_ci		if (atomic_read(&lgr_cnt))
10398c2ecf20Sopenharmony_ci			wait_event(lgrs_deleted, !atomic_read(&lgr_cnt));
10408c2ecf20Sopenharmony_ci	}
10418c2ecf20Sopenharmony_ci}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci/* set new lgr type and clear all asymmetric link tagging */
10448c2ecf20Sopenharmony_civoid smcr_lgr_set_type(struct smc_link_group *lgr, enum smc_lgr_type new_type)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	char *lgr_type = "";
10478c2ecf20Sopenharmony_ci	int i;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
10508c2ecf20Sopenharmony_ci		if (smc_link_usable(&lgr->lnk[i]))
10518c2ecf20Sopenharmony_ci			lgr->lnk[i].link_is_asym = false;
10528c2ecf20Sopenharmony_ci	if (lgr->type == new_type)
10538c2ecf20Sopenharmony_ci		return;
10548c2ecf20Sopenharmony_ci	lgr->type = new_type;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	switch (lgr->type) {
10578c2ecf20Sopenharmony_ci	case SMC_LGR_NONE:
10588c2ecf20Sopenharmony_ci		lgr_type = "NONE";
10598c2ecf20Sopenharmony_ci		break;
10608c2ecf20Sopenharmony_ci	case SMC_LGR_SINGLE:
10618c2ecf20Sopenharmony_ci		lgr_type = "SINGLE";
10628c2ecf20Sopenharmony_ci		break;
10638c2ecf20Sopenharmony_ci	case SMC_LGR_SYMMETRIC:
10648c2ecf20Sopenharmony_ci		lgr_type = "SYMMETRIC";
10658c2ecf20Sopenharmony_ci		break;
10668c2ecf20Sopenharmony_ci	case SMC_LGR_ASYMMETRIC_PEER:
10678c2ecf20Sopenharmony_ci		lgr_type = "ASYMMETRIC_PEER";
10688c2ecf20Sopenharmony_ci		break;
10698c2ecf20Sopenharmony_ci	case SMC_LGR_ASYMMETRIC_LOCAL:
10708c2ecf20Sopenharmony_ci		lgr_type = "ASYMMETRIC_LOCAL";
10718c2ecf20Sopenharmony_ci		break;
10728c2ecf20Sopenharmony_ci	}
10738c2ecf20Sopenharmony_ci	pr_warn_ratelimited("smc: SMC-R lg %*phN state changed: "
10748c2ecf20Sopenharmony_ci			    "%s, pnetid %.16s\n", SMC_LGR_ID_SIZE, &lgr->id,
10758c2ecf20Sopenharmony_ci			    lgr_type, lgr->pnet_id);
10768c2ecf20Sopenharmony_ci}
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci/* set new lgr type and tag a link as asymmetric */
10798c2ecf20Sopenharmony_civoid smcr_lgr_set_type_asym(struct smc_link_group *lgr,
10808c2ecf20Sopenharmony_ci			    enum smc_lgr_type new_type, int asym_lnk_idx)
10818c2ecf20Sopenharmony_ci{
10828c2ecf20Sopenharmony_ci	smcr_lgr_set_type(lgr, new_type);
10838c2ecf20Sopenharmony_ci	lgr->lnk[asym_lnk_idx].link_is_asym = true;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci/* abort connection, abort_work scheduled from tasklet context */
10878c2ecf20Sopenharmony_cistatic void smc_conn_abort_work(struct work_struct *work)
10888c2ecf20Sopenharmony_ci{
10898c2ecf20Sopenharmony_ci	struct smc_connection *conn = container_of(work,
10908c2ecf20Sopenharmony_ci						   struct smc_connection,
10918c2ecf20Sopenharmony_ci						   abort_work);
10928c2ecf20Sopenharmony_ci	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	lock_sock(&smc->sk);
10958c2ecf20Sopenharmony_ci	smc_conn_kill(conn, true);
10968c2ecf20Sopenharmony_ci	release_sock(&smc->sk);
10978c2ecf20Sopenharmony_ci	sock_put(&smc->sk); /* sock_hold done by schedulers of abort_work */
10988c2ecf20Sopenharmony_ci}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_civoid smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport)
11018c2ecf20Sopenharmony_ci{
11028c2ecf20Sopenharmony_ci	struct smc_link_group *lgr, *n;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	spin_lock_bh(&smc_lgr_list.lock);
11058c2ecf20Sopenharmony_ci	list_for_each_entry_safe(lgr, n, &smc_lgr_list.list, list) {
11068c2ecf20Sopenharmony_ci		struct smc_link *link;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci		if (strncmp(smcibdev->pnetid[ibport - 1], lgr->pnet_id,
11098c2ecf20Sopenharmony_ci			    SMC_MAX_PNETID_LEN) ||
11108c2ecf20Sopenharmony_ci		    lgr->type == SMC_LGR_SYMMETRIC ||
11118c2ecf20Sopenharmony_ci		    lgr->type == SMC_LGR_ASYMMETRIC_PEER)
11128c2ecf20Sopenharmony_ci			continue;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci		/* trigger local add link processing */
11158c2ecf20Sopenharmony_ci		link = smc_llc_usable_link(lgr);
11168c2ecf20Sopenharmony_ci		if (link)
11178c2ecf20Sopenharmony_ci			smc_llc_add_link_local(link);
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci	spin_unlock_bh(&smc_lgr_list.lock);
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci/* link is down - switch connections to alternate link,
11238c2ecf20Sopenharmony_ci * must be called under lgr->llc_conf_mutex lock
11248c2ecf20Sopenharmony_ci */
11258c2ecf20Sopenharmony_cistatic void smcr_link_down(struct smc_link *lnk)
11268c2ecf20Sopenharmony_ci{
11278c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = lnk->lgr;
11288c2ecf20Sopenharmony_ci	struct smc_link *to_lnk;
11298c2ecf20Sopenharmony_ci	int del_link_id;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	if (!lgr || lnk->state == SMC_LNK_UNUSED || list_empty(&lgr->list))
11328c2ecf20Sopenharmony_ci		return;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	to_lnk = smc_switch_conns(lgr, lnk, true);
11358c2ecf20Sopenharmony_ci	if (!to_lnk) { /* no backup link available */
11368c2ecf20Sopenharmony_ci		smcr_link_clear(lnk, true);
11378c2ecf20Sopenharmony_ci		return;
11388c2ecf20Sopenharmony_ci	}
11398c2ecf20Sopenharmony_ci	smcr_lgr_set_type(lgr, SMC_LGR_SINGLE);
11408c2ecf20Sopenharmony_ci	del_link_id = lnk->link_id;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	if (lgr->role == SMC_SERV) {
11438c2ecf20Sopenharmony_ci		/* trigger local delete link processing */
11448c2ecf20Sopenharmony_ci		smc_llc_srv_delete_link_local(to_lnk, del_link_id);
11458c2ecf20Sopenharmony_ci	} else {
11468c2ecf20Sopenharmony_ci		if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
11478c2ecf20Sopenharmony_ci			/* another llc task is ongoing */
11488c2ecf20Sopenharmony_ci			mutex_unlock(&lgr->llc_conf_mutex);
11498c2ecf20Sopenharmony_ci			wait_event_timeout(lgr->llc_flow_waiter,
11508c2ecf20Sopenharmony_ci				(list_empty(&lgr->list) ||
11518c2ecf20Sopenharmony_ci				 lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE),
11528c2ecf20Sopenharmony_ci				SMC_LLC_WAIT_TIME);
11538c2ecf20Sopenharmony_ci			mutex_lock(&lgr->llc_conf_mutex);
11548c2ecf20Sopenharmony_ci		}
11558c2ecf20Sopenharmony_ci		if (!list_empty(&lgr->list)) {
11568c2ecf20Sopenharmony_ci			smc_llc_send_delete_link(to_lnk, del_link_id,
11578c2ecf20Sopenharmony_ci						 SMC_LLC_REQ, true,
11588c2ecf20Sopenharmony_ci						 SMC_LLC_DEL_LOST_PATH);
11598c2ecf20Sopenharmony_ci			smcr_link_clear(lnk, true);
11608c2ecf20Sopenharmony_ci		}
11618c2ecf20Sopenharmony_ci		wake_up(&lgr->llc_flow_waiter);	/* wake up next waiter */
11628c2ecf20Sopenharmony_ci	}
11638c2ecf20Sopenharmony_ci}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci/* must be called under lgr->llc_conf_mutex lock */
11668c2ecf20Sopenharmony_civoid smcr_link_down_cond(struct smc_link *lnk)
11678c2ecf20Sopenharmony_ci{
11688c2ecf20Sopenharmony_ci	if (smc_link_downing(&lnk->state))
11698c2ecf20Sopenharmony_ci		smcr_link_down(lnk);
11708c2ecf20Sopenharmony_ci}
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci/* will get the lgr->llc_conf_mutex lock */
11738c2ecf20Sopenharmony_civoid smcr_link_down_cond_sched(struct smc_link *lnk)
11748c2ecf20Sopenharmony_ci{
11758c2ecf20Sopenharmony_ci	if (smc_link_downing(&lnk->state))
11768c2ecf20Sopenharmony_ci		schedule_work(&lnk->link_down_wrk);
11778c2ecf20Sopenharmony_ci}
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_civoid smcr_port_err(struct smc_ib_device *smcibdev, u8 ibport)
11808c2ecf20Sopenharmony_ci{
11818c2ecf20Sopenharmony_ci	struct smc_link_group *lgr, *n;
11828c2ecf20Sopenharmony_ci	int i;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	list_for_each_entry_safe(lgr, n, &smc_lgr_list.list, list) {
11858c2ecf20Sopenharmony_ci		if (strncmp(smcibdev->pnetid[ibport - 1], lgr->pnet_id,
11868c2ecf20Sopenharmony_ci			    SMC_MAX_PNETID_LEN))
11878c2ecf20Sopenharmony_ci			continue; /* lgr is not affected */
11888c2ecf20Sopenharmony_ci		if (list_empty(&lgr->list))
11898c2ecf20Sopenharmony_ci			continue;
11908c2ecf20Sopenharmony_ci		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
11918c2ecf20Sopenharmony_ci			struct smc_link *lnk = &lgr->lnk[i];
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci			if (smc_link_usable(lnk) &&
11948c2ecf20Sopenharmony_ci			    lnk->smcibdev == smcibdev && lnk->ibport == ibport)
11958c2ecf20Sopenharmony_ci				smcr_link_down_cond_sched(lnk);
11968c2ecf20Sopenharmony_ci		}
11978c2ecf20Sopenharmony_ci	}
11988c2ecf20Sopenharmony_ci}
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_cistatic void smc_link_down_work(struct work_struct *work)
12018c2ecf20Sopenharmony_ci{
12028c2ecf20Sopenharmony_ci	struct smc_link *link = container_of(work, struct smc_link,
12038c2ecf20Sopenharmony_ci					     link_down_wrk);
12048c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = link->lgr;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	if (list_empty(&lgr->list))
12078c2ecf20Sopenharmony_ci		return;
12088c2ecf20Sopenharmony_ci	wake_up_all(&lgr->llc_msg_waiter);
12098c2ecf20Sopenharmony_ci	mutex_lock(&lgr->llc_conf_mutex);
12108c2ecf20Sopenharmony_ci	smcr_link_down(link);
12118c2ecf20Sopenharmony_ci	mutex_unlock(&lgr->llc_conf_mutex);
12128c2ecf20Sopenharmony_ci}
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_cistatic int smc_vlan_by_tcpsk_walk(struct net_device *lower_dev,
12158c2ecf20Sopenharmony_ci				  struct netdev_nested_priv *priv)
12168c2ecf20Sopenharmony_ci{
12178c2ecf20Sopenharmony_ci	unsigned short *vlan_id = (unsigned short *)priv->data;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	if (is_vlan_dev(lower_dev)) {
12208c2ecf20Sopenharmony_ci		*vlan_id = vlan_dev_vlan_id(lower_dev);
12218c2ecf20Sopenharmony_ci		return 1;
12228c2ecf20Sopenharmony_ci	}
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	return 0;
12258c2ecf20Sopenharmony_ci}
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci/* Determine vlan of internal TCP socket. */
12288c2ecf20Sopenharmony_ciint smc_vlan_by_tcpsk(struct socket *clcsock, struct smc_init_info *ini)
12298c2ecf20Sopenharmony_ci{
12308c2ecf20Sopenharmony_ci	struct dst_entry *dst = sk_dst_get(clcsock->sk);
12318c2ecf20Sopenharmony_ci	struct netdev_nested_priv priv;
12328c2ecf20Sopenharmony_ci	struct net_device *ndev;
12338c2ecf20Sopenharmony_ci	int rc = 0;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	ini->vlan_id = 0;
12368c2ecf20Sopenharmony_ci	if (!dst) {
12378c2ecf20Sopenharmony_ci		rc = -ENOTCONN;
12388c2ecf20Sopenharmony_ci		goto out;
12398c2ecf20Sopenharmony_ci	}
12408c2ecf20Sopenharmony_ci	if (!dst->dev) {
12418c2ecf20Sopenharmony_ci		rc = -ENODEV;
12428c2ecf20Sopenharmony_ci		goto out_rel;
12438c2ecf20Sopenharmony_ci	}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	ndev = dst->dev;
12468c2ecf20Sopenharmony_ci	if (is_vlan_dev(ndev)) {
12478c2ecf20Sopenharmony_ci		ini->vlan_id = vlan_dev_vlan_id(ndev);
12488c2ecf20Sopenharmony_ci		goto out_rel;
12498c2ecf20Sopenharmony_ci	}
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	priv.data = (void *)&ini->vlan_id;
12528c2ecf20Sopenharmony_ci	rtnl_lock();
12538c2ecf20Sopenharmony_ci	netdev_walk_all_lower_dev(ndev, smc_vlan_by_tcpsk_walk, &priv);
12548c2ecf20Sopenharmony_ci	rtnl_unlock();
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ciout_rel:
12578c2ecf20Sopenharmony_ci	dst_release(dst);
12588c2ecf20Sopenharmony_ciout:
12598c2ecf20Sopenharmony_ci	return rc;
12608c2ecf20Sopenharmony_ci}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_cistatic bool smcr_lgr_match(struct smc_link_group *lgr,
12638c2ecf20Sopenharmony_ci			   struct smc_clc_msg_local *lcl,
12648c2ecf20Sopenharmony_ci			   enum smc_lgr_role role, u32 clcqpn)
12658c2ecf20Sopenharmony_ci{
12668c2ecf20Sopenharmony_ci	int i;
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	if (memcmp(lgr->peer_systemid, lcl->id_for_peer, SMC_SYSTEMID_LEN) ||
12698c2ecf20Sopenharmony_ci	    lgr->role != role)
12708c2ecf20Sopenharmony_ci		return false;
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
12738c2ecf20Sopenharmony_ci		if (!smc_link_active(&lgr->lnk[i]))
12748c2ecf20Sopenharmony_ci			continue;
12758c2ecf20Sopenharmony_ci		if ((lgr->role == SMC_SERV || lgr->lnk[i].peer_qpn == clcqpn) &&
12768c2ecf20Sopenharmony_ci		    !memcmp(lgr->lnk[i].peer_gid, &lcl->gid, SMC_GID_SIZE) &&
12778c2ecf20Sopenharmony_ci		    !memcmp(lgr->lnk[i].peer_mac, lcl->mac, sizeof(lcl->mac)))
12788c2ecf20Sopenharmony_ci			return true;
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci	return false;
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_cistatic bool smcd_lgr_match(struct smc_link_group *lgr,
12848c2ecf20Sopenharmony_ci			   struct smcd_dev *smcismdev, u64 peer_gid)
12858c2ecf20Sopenharmony_ci{
12868c2ecf20Sopenharmony_ci	return lgr->peer_gid == peer_gid && lgr->smcd == smcismdev;
12878c2ecf20Sopenharmony_ci}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci/* create a new SMC connection (and a new link group if necessary) */
12908c2ecf20Sopenharmony_ciint smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini)
12918c2ecf20Sopenharmony_ci{
12928c2ecf20Sopenharmony_ci	struct smc_connection *conn = &smc->conn;
12938c2ecf20Sopenharmony_ci	struct list_head *lgr_list;
12948c2ecf20Sopenharmony_ci	struct smc_link_group *lgr;
12958c2ecf20Sopenharmony_ci	enum smc_lgr_role role;
12968c2ecf20Sopenharmony_ci	spinlock_t *lgr_lock;
12978c2ecf20Sopenharmony_ci	int rc = 0;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	lgr_list = ini->is_smcd ? &ini->ism_dev[ini->ism_selected]->lgr_list :
13008c2ecf20Sopenharmony_ci				  &smc_lgr_list.list;
13018c2ecf20Sopenharmony_ci	lgr_lock = ini->is_smcd ? &ini->ism_dev[ini->ism_selected]->lgr_lock :
13028c2ecf20Sopenharmony_ci				  &smc_lgr_list.lock;
13038c2ecf20Sopenharmony_ci	ini->first_contact_local = 1;
13048c2ecf20Sopenharmony_ci	role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
13058c2ecf20Sopenharmony_ci	if (role == SMC_CLNT && ini->first_contact_peer)
13068c2ecf20Sopenharmony_ci		/* create new link group as well */
13078c2ecf20Sopenharmony_ci		goto create;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	/* determine if an existing link group can be reused */
13108c2ecf20Sopenharmony_ci	spin_lock_bh(lgr_lock);
13118c2ecf20Sopenharmony_ci	list_for_each_entry(lgr, lgr_list, list) {
13128c2ecf20Sopenharmony_ci		write_lock_bh(&lgr->conns_lock);
13138c2ecf20Sopenharmony_ci		if ((ini->is_smcd ?
13148c2ecf20Sopenharmony_ci		     smcd_lgr_match(lgr, ini->ism_dev[ini->ism_selected],
13158c2ecf20Sopenharmony_ci				    ini->ism_peer_gid[ini->ism_selected]) :
13168c2ecf20Sopenharmony_ci		     smcr_lgr_match(lgr, ini->ib_lcl, role, ini->ib_clcqpn)) &&
13178c2ecf20Sopenharmony_ci		    !lgr->sync_err &&
13188c2ecf20Sopenharmony_ci		    (ini->smcd_version == SMC_V2 ||
13198c2ecf20Sopenharmony_ci		     lgr->vlan_id == ini->vlan_id) &&
13208c2ecf20Sopenharmony_ci		    (role == SMC_CLNT || ini->is_smcd ||
13218c2ecf20Sopenharmony_ci		    (lgr->conns_num < SMC_RMBS_PER_LGR_MAX &&
13228c2ecf20Sopenharmony_ci		      !bitmap_full(lgr->rtokens_used_mask, SMC_RMBS_PER_LGR_MAX)))) {
13238c2ecf20Sopenharmony_ci			/* link group found */
13248c2ecf20Sopenharmony_ci			ini->first_contact_local = 0;
13258c2ecf20Sopenharmony_ci			conn->lgr = lgr;
13268c2ecf20Sopenharmony_ci			rc = smc_lgr_register_conn(conn, false);
13278c2ecf20Sopenharmony_ci			write_unlock_bh(&lgr->conns_lock);
13288c2ecf20Sopenharmony_ci			if (!rc && delayed_work_pending(&lgr->free_work))
13298c2ecf20Sopenharmony_ci				cancel_delayed_work(&lgr->free_work);
13308c2ecf20Sopenharmony_ci			break;
13318c2ecf20Sopenharmony_ci		}
13328c2ecf20Sopenharmony_ci		write_unlock_bh(&lgr->conns_lock);
13338c2ecf20Sopenharmony_ci	}
13348c2ecf20Sopenharmony_ci	spin_unlock_bh(lgr_lock);
13358c2ecf20Sopenharmony_ci	if (rc)
13368c2ecf20Sopenharmony_ci		return rc;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	if (role == SMC_CLNT && !ini->first_contact_peer &&
13398c2ecf20Sopenharmony_ci	    ini->first_contact_local) {
13408c2ecf20Sopenharmony_ci		/* Server reuses a link group, but Client wants to start
13418c2ecf20Sopenharmony_ci		 * a new one
13428c2ecf20Sopenharmony_ci		 * send out_of_sync decline, reason synchr. error
13438c2ecf20Sopenharmony_ci		 */
13448c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_SYNCERR;
13458c2ecf20Sopenharmony_ci	}
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_cicreate:
13488c2ecf20Sopenharmony_ci	if (ini->first_contact_local) {
13498c2ecf20Sopenharmony_ci		rc = smc_lgr_create(smc, ini);
13508c2ecf20Sopenharmony_ci		if (rc)
13518c2ecf20Sopenharmony_ci			goto out;
13528c2ecf20Sopenharmony_ci		lgr = conn->lgr;
13538c2ecf20Sopenharmony_ci		write_lock_bh(&lgr->conns_lock);
13548c2ecf20Sopenharmony_ci		rc = smc_lgr_register_conn(conn, true);
13558c2ecf20Sopenharmony_ci		write_unlock_bh(&lgr->conns_lock);
13568c2ecf20Sopenharmony_ci		if (rc)
13578c2ecf20Sopenharmony_ci			goto out;
13588c2ecf20Sopenharmony_ci	}
13598c2ecf20Sopenharmony_ci	conn->local_tx_ctrl.common.type = SMC_CDC_MSG_TYPE;
13608c2ecf20Sopenharmony_ci	conn->local_tx_ctrl.len = SMC_WR_TX_SIZE;
13618c2ecf20Sopenharmony_ci	conn->urg_state = SMC_URG_READ;
13628c2ecf20Sopenharmony_ci	init_waitqueue_head(&conn->cdc_pend_tx_wq);
13638c2ecf20Sopenharmony_ci	INIT_WORK(&smc->conn.abort_work, smc_conn_abort_work);
13648c2ecf20Sopenharmony_ci	if (ini->is_smcd) {
13658c2ecf20Sopenharmony_ci		conn->rx_off = sizeof(struct smcd_cdc_msg);
13668c2ecf20Sopenharmony_ci		smcd_cdc_rx_init(conn); /* init tasklet for this conn */
13678c2ecf20Sopenharmony_ci	} else {
13688c2ecf20Sopenharmony_ci		conn->rx_off = 0;
13698c2ecf20Sopenharmony_ci	}
13708c2ecf20Sopenharmony_ci#ifndef KERNEL_HAS_ATOMIC64
13718c2ecf20Sopenharmony_ci	spin_lock_init(&conn->acurs_lock);
13728c2ecf20Sopenharmony_ci#endif
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ciout:
13758c2ecf20Sopenharmony_ci	return rc;
13768c2ecf20Sopenharmony_ci}
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci/* convert the RMB size into the compressed notation - minimum 16K.
13798c2ecf20Sopenharmony_ci * In contrast to plain ilog2, this rounds towards the next power of 2,
13808c2ecf20Sopenharmony_ci * so the socket application gets at least its desired sndbuf / rcvbuf size.
13818c2ecf20Sopenharmony_ci */
13828c2ecf20Sopenharmony_cistatic u8 smc_compress_bufsize(int size)
13838c2ecf20Sopenharmony_ci{
13848c2ecf20Sopenharmony_ci	u8 compressed;
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	if (size <= SMC_BUF_MIN_SIZE)
13878c2ecf20Sopenharmony_ci		return 0;
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	size = (size - 1) >> 14;
13908c2ecf20Sopenharmony_ci	compressed = ilog2(size) + 1;
13918c2ecf20Sopenharmony_ci	if (compressed >= SMC_RMBE_SIZES)
13928c2ecf20Sopenharmony_ci		compressed = SMC_RMBE_SIZES - 1;
13938c2ecf20Sopenharmony_ci	return compressed;
13948c2ecf20Sopenharmony_ci}
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci/* convert the RMB size from compressed notation into integer */
13978c2ecf20Sopenharmony_ciint smc_uncompress_bufsize(u8 compressed)
13988c2ecf20Sopenharmony_ci{
13998c2ecf20Sopenharmony_ci	u32 size;
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	size = 0x00000001 << (((int)compressed) + 14);
14028c2ecf20Sopenharmony_ci	return (int)size;
14038c2ecf20Sopenharmony_ci}
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci/* try to reuse a sndbuf or rmb description slot for a certain
14068c2ecf20Sopenharmony_ci * buffer size; if not available, return NULL
14078c2ecf20Sopenharmony_ci */
14088c2ecf20Sopenharmony_cistatic struct smc_buf_desc *smc_buf_get_slot(int compressed_bufsize,
14098c2ecf20Sopenharmony_ci					     struct mutex *lock,
14108c2ecf20Sopenharmony_ci					     struct list_head *buf_list)
14118c2ecf20Sopenharmony_ci{
14128c2ecf20Sopenharmony_ci	struct smc_buf_desc *buf_slot;
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	mutex_lock(lock);
14158c2ecf20Sopenharmony_ci	list_for_each_entry(buf_slot, buf_list, list) {
14168c2ecf20Sopenharmony_ci		if (cmpxchg(&buf_slot->used, 0, 1) == 0) {
14178c2ecf20Sopenharmony_ci			mutex_unlock(lock);
14188c2ecf20Sopenharmony_ci			return buf_slot;
14198c2ecf20Sopenharmony_ci		}
14208c2ecf20Sopenharmony_ci	}
14218c2ecf20Sopenharmony_ci	mutex_unlock(lock);
14228c2ecf20Sopenharmony_ci	return NULL;
14238c2ecf20Sopenharmony_ci}
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci/* one of the conditions for announcing a receiver's current window size is
14268c2ecf20Sopenharmony_ci * that it "results in a minimum increase in the window size of 10% of the
14278c2ecf20Sopenharmony_ci * receive buffer space" [RFC7609]
14288c2ecf20Sopenharmony_ci */
14298c2ecf20Sopenharmony_cistatic inline int smc_rmb_wnd_update_limit(int rmbe_size)
14308c2ecf20Sopenharmony_ci{
14318c2ecf20Sopenharmony_ci	return max_t(int, rmbe_size / 10, SOCK_MIN_SNDBUF / 2);
14328c2ecf20Sopenharmony_ci}
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci/* map an rmb buf to a link */
14358c2ecf20Sopenharmony_cistatic int smcr_buf_map_link(struct smc_buf_desc *buf_desc, bool is_rmb,
14368c2ecf20Sopenharmony_ci			     struct smc_link *lnk)
14378c2ecf20Sopenharmony_ci{
14388c2ecf20Sopenharmony_ci	int rc;
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	if (buf_desc->is_map_ib[lnk->link_idx])
14418c2ecf20Sopenharmony_ci		return 0;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	rc = sg_alloc_table(&buf_desc->sgt[lnk->link_idx], 1, GFP_KERNEL);
14448c2ecf20Sopenharmony_ci	if (rc)
14458c2ecf20Sopenharmony_ci		return rc;
14468c2ecf20Sopenharmony_ci	sg_set_buf(buf_desc->sgt[lnk->link_idx].sgl,
14478c2ecf20Sopenharmony_ci		   buf_desc->cpu_addr, buf_desc->len);
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	/* map sg table to DMA address */
14508c2ecf20Sopenharmony_ci	rc = smc_ib_buf_map_sg(lnk, buf_desc,
14518c2ecf20Sopenharmony_ci			       is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
14528c2ecf20Sopenharmony_ci	/* SMC protocol depends on mapping to one DMA address only */
14538c2ecf20Sopenharmony_ci	if (rc != 1) {
14548c2ecf20Sopenharmony_ci		rc = -EAGAIN;
14558c2ecf20Sopenharmony_ci		goto free_table;
14568c2ecf20Sopenharmony_ci	}
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	/* create a new memory region for the RMB */
14598c2ecf20Sopenharmony_ci	if (is_rmb) {
14608c2ecf20Sopenharmony_ci		rc = smc_ib_get_memory_region(lnk->roce_pd,
14618c2ecf20Sopenharmony_ci					      IB_ACCESS_REMOTE_WRITE |
14628c2ecf20Sopenharmony_ci					      IB_ACCESS_LOCAL_WRITE,
14638c2ecf20Sopenharmony_ci					      buf_desc, lnk->link_idx);
14648c2ecf20Sopenharmony_ci		if (rc)
14658c2ecf20Sopenharmony_ci			goto buf_unmap;
14668c2ecf20Sopenharmony_ci		smc_ib_sync_sg_for_device(lnk, buf_desc, DMA_FROM_DEVICE);
14678c2ecf20Sopenharmony_ci	}
14688c2ecf20Sopenharmony_ci	buf_desc->is_map_ib[lnk->link_idx] = true;
14698c2ecf20Sopenharmony_ci	return 0;
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_cibuf_unmap:
14728c2ecf20Sopenharmony_ci	smc_ib_buf_unmap_sg(lnk, buf_desc,
14738c2ecf20Sopenharmony_ci			    is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
14748c2ecf20Sopenharmony_cifree_table:
14758c2ecf20Sopenharmony_ci	sg_free_table(&buf_desc->sgt[lnk->link_idx]);
14768c2ecf20Sopenharmony_ci	return rc;
14778c2ecf20Sopenharmony_ci}
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci/* register a new rmb on IB device,
14808c2ecf20Sopenharmony_ci * must be called under lgr->llc_conf_mutex lock
14818c2ecf20Sopenharmony_ci */
14828c2ecf20Sopenharmony_ciint smcr_link_reg_rmb(struct smc_link *link, struct smc_buf_desc *rmb_desc)
14838c2ecf20Sopenharmony_ci{
14848c2ecf20Sopenharmony_ci	if (list_empty(&link->lgr->list))
14858c2ecf20Sopenharmony_ci		return -ENOLINK;
14868c2ecf20Sopenharmony_ci	if (!rmb_desc->is_reg_mr[link->link_idx]) {
14878c2ecf20Sopenharmony_ci		/* register memory region for new rmb */
14888c2ecf20Sopenharmony_ci		if (smc_wr_reg_send(link, rmb_desc->mr_rx[link->link_idx])) {
14898c2ecf20Sopenharmony_ci			rmb_desc->is_reg_err = true;
14908c2ecf20Sopenharmony_ci			return -EFAULT;
14918c2ecf20Sopenharmony_ci		}
14928c2ecf20Sopenharmony_ci		rmb_desc->is_reg_mr[link->link_idx] = true;
14938c2ecf20Sopenharmony_ci	}
14948c2ecf20Sopenharmony_ci	return 0;
14958c2ecf20Sopenharmony_ci}
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_cistatic int _smcr_buf_map_lgr(struct smc_link *lnk, struct mutex *lock,
14988c2ecf20Sopenharmony_ci			     struct list_head *lst, bool is_rmb)
14998c2ecf20Sopenharmony_ci{
15008c2ecf20Sopenharmony_ci	struct smc_buf_desc *buf_desc, *bf;
15018c2ecf20Sopenharmony_ci	int rc = 0;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	mutex_lock(lock);
15048c2ecf20Sopenharmony_ci	list_for_each_entry_safe(buf_desc, bf, lst, list) {
15058c2ecf20Sopenharmony_ci		if (!buf_desc->used)
15068c2ecf20Sopenharmony_ci			continue;
15078c2ecf20Sopenharmony_ci		rc = smcr_buf_map_link(buf_desc, is_rmb, lnk);
15088c2ecf20Sopenharmony_ci		if (rc)
15098c2ecf20Sopenharmony_ci			goto out;
15108c2ecf20Sopenharmony_ci	}
15118c2ecf20Sopenharmony_ciout:
15128c2ecf20Sopenharmony_ci	mutex_unlock(lock);
15138c2ecf20Sopenharmony_ci	return rc;
15148c2ecf20Sopenharmony_ci}
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci/* map all used buffers of lgr for a new link */
15178c2ecf20Sopenharmony_ciint smcr_buf_map_lgr(struct smc_link *lnk)
15188c2ecf20Sopenharmony_ci{
15198c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = lnk->lgr;
15208c2ecf20Sopenharmony_ci	int i, rc = 0;
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_RMBE_SIZES; i++) {
15238c2ecf20Sopenharmony_ci		rc = _smcr_buf_map_lgr(lnk, &lgr->rmbs_lock,
15248c2ecf20Sopenharmony_ci				       &lgr->rmbs[i], true);
15258c2ecf20Sopenharmony_ci		if (rc)
15268c2ecf20Sopenharmony_ci			return rc;
15278c2ecf20Sopenharmony_ci		rc = _smcr_buf_map_lgr(lnk, &lgr->sndbufs_lock,
15288c2ecf20Sopenharmony_ci				       &lgr->sndbufs[i], false);
15298c2ecf20Sopenharmony_ci		if (rc)
15308c2ecf20Sopenharmony_ci			return rc;
15318c2ecf20Sopenharmony_ci	}
15328c2ecf20Sopenharmony_ci	return 0;
15338c2ecf20Sopenharmony_ci}
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci/* register all used buffers of lgr for a new link,
15368c2ecf20Sopenharmony_ci * must be called under lgr->llc_conf_mutex lock
15378c2ecf20Sopenharmony_ci */
15388c2ecf20Sopenharmony_ciint smcr_buf_reg_lgr(struct smc_link *lnk)
15398c2ecf20Sopenharmony_ci{
15408c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = lnk->lgr;
15418c2ecf20Sopenharmony_ci	struct smc_buf_desc *buf_desc, *bf;
15428c2ecf20Sopenharmony_ci	int i, rc = 0;
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	mutex_lock(&lgr->rmbs_lock);
15458c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_RMBE_SIZES; i++) {
15468c2ecf20Sopenharmony_ci		list_for_each_entry_safe(buf_desc, bf, &lgr->rmbs[i], list) {
15478c2ecf20Sopenharmony_ci			if (!buf_desc->used)
15488c2ecf20Sopenharmony_ci				continue;
15498c2ecf20Sopenharmony_ci			rc = smcr_link_reg_rmb(lnk, buf_desc);
15508c2ecf20Sopenharmony_ci			if (rc)
15518c2ecf20Sopenharmony_ci				goto out;
15528c2ecf20Sopenharmony_ci		}
15538c2ecf20Sopenharmony_ci	}
15548c2ecf20Sopenharmony_ciout:
15558c2ecf20Sopenharmony_ci	mutex_unlock(&lgr->rmbs_lock);
15568c2ecf20Sopenharmony_ci	return rc;
15578c2ecf20Sopenharmony_ci}
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_cistatic struct smc_buf_desc *smcr_new_buf_create(struct smc_link_group *lgr,
15608c2ecf20Sopenharmony_ci						bool is_rmb, int bufsize)
15618c2ecf20Sopenharmony_ci{
15628c2ecf20Sopenharmony_ci	struct smc_buf_desc *buf_desc;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	/* try to alloc a new buffer */
15658c2ecf20Sopenharmony_ci	buf_desc = kzalloc(sizeof(*buf_desc), GFP_KERNEL);
15668c2ecf20Sopenharmony_ci	if (!buf_desc)
15678c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	buf_desc->order = get_order(bufsize);
15708c2ecf20Sopenharmony_ci	buf_desc->pages = alloc_pages(GFP_KERNEL | __GFP_NOWARN |
15718c2ecf20Sopenharmony_ci				      __GFP_NOMEMALLOC | __GFP_COMP |
15728c2ecf20Sopenharmony_ci				      __GFP_NORETRY | __GFP_ZERO,
15738c2ecf20Sopenharmony_ci				      buf_desc->order);
15748c2ecf20Sopenharmony_ci	if (!buf_desc->pages) {
15758c2ecf20Sopenharmony_ci		kfree(buf_desc);
15768c2ecf20Sopenharmony_ci		return ERR_PTR(-EAGAIN);
15778c2ecf20Sopenharmony_ci	}
15788c2ecf20Sopenharmony_ci	buf_desc->cpu_addr = (void *)page_address(buf_desc->pages);
15798c2ecf20Sopenharmony_ci	buf_desc->len = bufsize;
15808c2ecf20Sopenharmony_ci	return buf_desc;
15818c2ecf20Sopenharmony_ci}
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci/* map buf_desc on all usable links,
15848c2ecf20Sopenharmony_ci * unused buffers stay mapped as long as the link is up
15858c2ecf20Sopenharmony_ci */
15868c2ecf20Sopenharmony_cistatic int smcr_buf_map_usable_links(struct smc_link_group *lgr,
15878c2ecf20Sopenharmony_ci				     struct smc_buf_desc *buf_desc, bool is_rmb)
15888c2ecf20Sopenharmony_ci{
15898c2ecf20Sopenharmony_ci	int i, rc = 0, cnt = 0;
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	/* protect against parallel link reconfiguration */
15928c2ecf20Sopenharmony_ci	mutex_lock(&lgr->llc_conf_mutex);
15938c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
15948c2ecf20Sopenharmony_ci		struct smc_link *lnk = &lgr->lnk[i];
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci		if (!smc_link_usable(lnk))
15978c2ecf20Sopenharmony_ci			continue;
15988c2ecf20Sopenharmony_ci		if (smcr_buf_map_link(buf_desc, is_rmb, lnk)) {
15998c2ecf20Sopenharmony_ci			rc = -ENOMEM;
16008c2ecf20Sopenharmony_ci			goto out;
16018c2ecf20Sopenharmony_ci		}
16028c2ecf20Sopenharmony_ci		cnt++;
16038c2ecf20Sopenharmony_ci	}
16048c2ecf20Sopenharmony_ciout:
16058c2ecf20Sopenharmony_ci	mutex_unlock(&lgr->llc_conf_mutex);
16068c2ecf20Sopenharmony_ci	if (!rc && !cnt)
16078c2ecf20Sopenharmony_ci		rc = -EINVAL;
16088c2ecf20Sopenharmony_ci	return rc;
16098c2ecf20Sopenharmony_ci}
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci#define SMCD_DMBE_SIZES		6 /* 0 -> 16KB, 1 -> 32KB, .. 6 -> 1MB */
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_cistatic struct smc_buf_desc *smcd_new_buf_create(struct smc_link_group *lgr,
16148c2ecf20Sopenharmony_ci						bool is_dmb, int bufsize)
16158c2ecf20Sopenharmony_ci{
16168c2ecf20Sopenharmony_ci	struct smc_buf_desc *buf_desc;
16178c2ecf20Sopenharmony_ci	int rc;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	if (smc_compress_bufsize(bufsize) > SMCD_DMBE_SIZES)
16208c2ecf20Sopenharmony_ci		return ERR_PTR(-EAGAIN);
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci	/* try to alloc a new DMB */
16238c2ecf20Sopenharmony_ci	buf_desc = kzalloc(sizeof(*buf_desc), GFP_KERNEL);
16248c2ecf20Sopenharmony_ci	if (!buf_desc)
16258c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
16268c2ecf20Sopenharmony_ci	if (is_dmb) {
16278c2ecf20Sopenharmony_ci		rc = smc_ism_register_dmb(lgr, bufsize, buf_desc);
16288c2ecf20Sopenharmony_ci		if (rc) {
16298c2ecf20Sopenharmony_ci			kfree(buf_desc);
16308c2ecf20Sopenharmony_ci			if (rc == -ENOMEM)
16318c2ecf20Sopenharmony_ci				return ERR_PTR(-EAGAIN);
16328c2ecf20Sopenharmony_ci			if (rc == -ENOSPC)
16338c2ecf20Sopenharmony_ci				return ERR_PTR(-ENOSPC);
16348c2ecf20Sopenharmony_ci			return ERR_PTR(-EIO);
16358c2ecf20Sopenharmony_ci		}
16368c2ecf20Sopenharmony_ci		buf_desc->pages = virt_to_page(buf_desc->cpu_addr);
16378c2ecf20Sopenharmony_ci		/* CDC header stored in buf. So, pretend it was smaller */
16388c2ecf20Sopenharmony_ci		buf_desc->len = bufsize - sizeof(struct smcd_cdc_msg);
16398c2ecf20Sopenharmony_ci	} else {
16408c2ecf20Sopenharmony_ci		buf_desc->cpu_addr = kzalloc(bufsize, GFP_KERNEL |
16418c2ecf20Sopenharmony_ci					     __GFP_NOWARN | __GFP_NORETRY |
16428c2ecf20Sopenharmony_ci					     __GFP_NOMEMALLOC);
16438c2ecf20Sopenharmony_ci		if (!buf_desc->cpu_addr) {
16448c2ecf20Sopenharmony_ci			kfree(buf_desc);
16458c2ecf20Sopenharmony_ci			return ERR_PTR(-EAGAIN);
16468c2ecf20Sopenharmony_ci		}
16478c2ecf20Sopenharmony_ci		buf_desc->len = bufsize;
16488c2ecf20Sopenharmony_ci	}
16498c2ecf20Sopenharmony_ci	return buf_desc;
16508c2ecf20Sopenharmony_ci}
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_cistatic int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)
16538c2ecf20Sopenharmony_ci{
16548c2ecf20Sopenharmony_ci	struct smc_buf_desc *buf_desc = ERR_PTR(-ENOMEM);
16558c2ecf20Sopenharmony_ci	struct smc_connection *conn = &smc->conn;
16568c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = conn->lgr;
16578c2ecf20Sopenharmony_ci	struct list_head *buf_list;
16588c2ecf20Sopenharmony_ci	int bufsize, bufsize_short;
16598c2ecf20Sopenharmony_ci	struct mutex *lock;	/* lock buffer list */
16608c2ecf20Sopenharmony_ci	int sk_buf_size;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	if (is_rmb)
16638c2ecf20Sopenharmony_ci		/* use socket recv buffer size (w/o overhead) as start value */
16648c2ecf20Sopenharmony_ci		sk_buf_size = smc->sk.sk_rcvbuf / 2;
16658c2ecf20Sopenharmony_ci	else
16668c2ecf20Sopenharmony_ci		/* use socket send buffer size (w/o overhead) as start value */
16678c2ecf20Sopenharmony_ci		sk_buf_size = smc->sk.sk_sndbuf / 2;
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	for (bufsize_short = smc_compress_bufsize(sk_buf_size);
16708c2ecf20Sopenharmony_ci	     bufsize_short >= 0; bufsize_short--) {
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci		if (is_rmb) {
16738c2ecf20Sopenharmony_ci			lock = &lgr->rmbs_lock;
16748c2ecf20Sopenharmony_ci			buf_list = &lgr->rmbs[bufsize_short];
16758c2ecf20Sopenharmony_ci		} else {
16768c2ecf20Sopenharmony_ci			lock = &lgr->sndbufs_lock;
16778c2ecf20Sopenharmony_ci			buf_list = &lgr->sndbufs[bufsize_short];
16788c2ecf20Sopenharmony_ci		}
16798c2ecf20Sopenharmony_ci		bufsize = smc_uncompress_bufsize(bufsize_short);
16808c2ecf20Sopenharmony_ci		if ((1 << get_order(bufsize)) > SG_MAX_SINGLE_ALLOC)
16818c2ecf20Sopenharmony_ci			continue;
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci		/* check for reusable slot in the link group */
16848c2ecf20Sopenharmony_ci		buf_desc = smc_buf_get_slot(bufsize_short, lock, buf_list);
16858c2ecf20Sopenharmony_ci		if (buf_desc) {
16868c2ecf20Sopenharmony_ci			memset(buf_desc->cpu_addr, 0, bufsize);
16878c2ecf20Sopenharmony_ci			break; /* found reusable slot */
16888c2ecf20Sopenharmony_ci		}
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci		if (is_smcd)
16918c2ecf20Sopenharmony_ci			buf_desc = smcd_new_buf_create(lgr, is_rmb, bufsize);
16928c2ecf20Sopenharmony_ci		else
16938c2ecf20Sopenharmony_ci			buf_desc = smcr_new_buf_create(lgr, is_rmb, bufsize);
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci		if (PTR_ERR(buf_desc) == -ENOMEM)
16968c2ecf20Sopenharmony_ci			break;
16978c2ecf20Sopenharmony_ci		if (IS_ERR(buf_desc))
16988c2ecf20Sopenharmony_ci			continue;
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci		buf_desc->used = 1;
17018c2ecf20Sopenharmony_ci		mutex_lock(lock);
17028c2ecf20Sopenharmony_ci		list_add(&buf_desc->list, buf_list);
17038c2ecf20Sopenharmony_ci		mutex_unlock(lock);
17048c2ecf20Sopenharmony_ci		break; /* found */
17058c2ecf20Sopenharmony_ci	}
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci	if (IS_ERR(buf_desc))
17088c2ecf20Sopenharmony_ci		return PTR_ERR(buf_desc);
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	if (!is_smcd) {
17118c2ecf20Sopenharmony_ci		if (smcr_buf_map_usable_links(lgr, buf_desc, is_rmb)) {
17128c2ecf20Sopenharmony_ci			smcr_buf_unuse(buf_desc, lgr);
17138c2ecf20Sopenharmony_ci			return -ENOMEM;
17148c2ecf20Sopenharmony_ci		}
17158c2ecf20Sopenharmony_ci	}
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	if (is_rmb) {
17188c2ecf20Sopenharmony_ci		conn->rmb_desc = buf_desc;
17198c2ecf20Sopenharmony_ci		conn->rmbe_size_short = bufsize_short;
17208c2ecf20Sopenharmony_ci		smc->sk.sk_rcvbuf = bufsize * 2;
17218c2ecf20Sopenharmony_ci		atomic_set(&conn->bytes_to_rcv, 0);
17228c2ecf20Sopenharmony_ci		conn->rmbe_update_limit =
17238c2ecf20Sopenharmony_ci			smc_rmb_wnd_update_limit(buf_desc->len);
17248c2ecf20Sopenharmony_ci		if (is_smcd)
17258c2ecf20Sopenharmony_ci			smc_ism_set_conn(conn); /* map RMB/smcd_dev to conn */
17268c2ecf20Sopenharmony_ci	} else {
17278c2ecf20Sopenharmony_ci		conn->sndbuf_desc = buf_desc;
17288c2ecf20Sopenharmony_ci		smc->sk.sk_sndbuf = bufsize * 2;
17298c2ecf20Sopenharmony_ci		atomic_set(&conn->sndbuf_space, bufsize);
17308c2ecf20Sopenharmony_ci	}
17318c2ecf20Sopenharmony_ci	return 0;
17328c2ecf20Sopenharmony_ci}
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_civoid smc_sndbuf_sync_sg_for_cpu(struct smc_connection *conn)
17358c2ecf20Sopenharmony_ci{
17368c2ecf20Sopenharmony_ci	if (!conn->lgr || conn->lgr->is_smcd || !smc_link_active(conn->lnk))
17378c2ecf20Sopenharmony_ci		return;
17388c2ecf20Sopenharmony_ci	smc_ib_sync_sg_for_cpu(conn->lnk, conn->sndbuf_desc, DMA_TO_DEVICE);
17398c2ecf20Sopenharmony_ci}
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_civoid smc_sndbuf_sync_sg_for_device(struct smc_connection *conn)
17428c2ecf20Sopenharmony_ci{
17438c2ecf20Sopenharmony_ci	if (!conn->lgr || conn->lgr->is_smcd || !smc_link_active(conn->lnk))
17448c2ecf20Sopenharmony_ci		return;
17458c2ecf20Sopenharmony_ci	smc_ib_sync_sg_for_device(conn->lnk, conn->sndbuf_desc, DMA_TO_DEVICE);
17468c2ecf20Sopenharmony_ci}
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_civoid smc_rmb_sync_sg_for_cpu(struct smc_connection *conn)
17498c2ecf20Sopenharmony_ci{
17508c2ecf20Sopenharmony_ci	int i;
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	if (!conn->lgr || conn->lgr->is_smcd)
17538c2ecf20Sopenharmony_ci		return;
17548c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
17558c2ecf20Sopenharmony_ci		if (!smc_link_active(&conn->lgr->lnk[i]))
17568c2ecf20Sopenharmony_ci			continue;
17578c2ecf20Sopenharmony_ci		smc_ib_sync_sg_for_cpu(&conn->lgr->lnk[i], conn->rmb_desc,
17588c2ecf20Sopenharmony_ci				       DMA_FROM_DEVICE);
17598c2ecf20Sopenharmony_ci	}
17608c2ecf20Sopenharmony_ci}
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_civoid smc_rmb_sync_sg_for_device(struct smc_connection *conn)
17638c2ecf20Sopenharmony_ci{
17648c2ecf20Sopenharmony_ci	int i;
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	if (!conn->lgr || conn->lgr->is_smcd)
17678c2ecf20Sopenharmony_ci		return;
17688c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
17698c2ecf20Sopenharmony_ci		if (!smc_link_active(&conn->lgr->lnk[i]))
17708c2ecf20Sopenharmony_ci			continue;
17718c2ecf20Sopenharmony_ci		smc_ib_sync_sg_for_device(&conn->lgr->lnk[i], conn->rmb_desc,
17728c2ecf20Sopenharmony_ci					  DMA_FROM_DEVICE);
17738c2ecf20Sopenharmony_ci	}
17748c2ecf20Sopenharmony_ci}
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci/* create the send and receive buffer for an SMC socket;
17778c2ecf20Sopenharmony_ci * receive buffers are called RMBs;
17788c2ecf20Sopenharmony_ci * (even though the SMC protocol allows more than one RMB-element per RMB,
17798c2ecf20Sopenharmony_ci * the Linux implementation uses just one RMB-element per RMB, i.e. uses an
17808c2ecf20Sopenharmony_ci * extra RMB for every connection in a link group
17818c2ecf20Sopenharmony_ci */
17828c2ecf20Sopenharmony_ciint smc_buf_create(struct smc_sock *smc, bool is_smcd)
17838c2ecf20Sopenharmony_ci{
17848c2ecf20Sopenharmony_ci	int rc;
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	/* create send buffer */
17878c2ecf20Sopenharmony_ci	rc = __smc_buf_create(smc, is_smcd, false);
17888c2ecf20Sopenharmony_ci	if (rc)
17898c2ecf20Sopenharmony_ci		return rc;
17908c2ecf20Sopenharmony_ci	/* create rmb */
17918c2ecf20Sopenharmony_ci	rc = __smc_buf_create(smc, is_smcd, true);
17928c2ecf20Sopenharmony_ci	if (rc) {
17938c2ecf20Sopenharmony_ci		mutex_lock(&smc->conn.lgr->sndbufs_lock);
17948c2ecf20Sopenharmony_ci		list_del(&smc->conn.sndbuf_desc->list);
17958c2ecf20Sopenharmony_ci		mutex_unlock(&smc->conn.lgr->sndbufs_lock);
17968c2ecf20Sopenharmony_ci		smc_buf_free(smc->conn.lgr, false, smc->conn.sndbuf_desc);
17978c2ecf20Sopenharmony_ci		smc->conn.sndbuf_desc = NULL;
17988c2ecf20Sopenharmony_ci	}
17998c2ecf20Sopenharmony_ci	return rc;
18008c2ecf20Sopenharmony_ci}
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_cistatic inline int smc_rmb_reserve_rtoken_idx(struct smc_link_group *lgr)
18038c2ecf20Sopenharmony_ci{
18048c2ecf20Sopenharmony_ci	int i;
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	for_each_clear_bit(i, lgr->rtokens_used_mask, SMC_RMBS_PER_LGR_MAX) {
18078c2ecf20Sopenharmony_ci		if (!test_and_set_bit(i, lgr->rtokens_used_mask))
18088c2ecf20Sopenharmony_ci			return i;
18098c2ecf20Sopenharmony_ci	}
18108c2ecf20Sopenharmony_ci	return -ENOSPC;
18118c2ecf20Sopenharmony_ci}
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_cistatic int smc_rtoken_find_by_link(struct smc_link_group *lgr, int lnk_idx,
18148c2ecf20Sopenharmony_ci				   u32 rkey)
18158c2ecf20Sopenharmony_ci{
18168c2ecf20Sopenharmony_ci	int i;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
18198c2ecf20Sopenharmony_ci		if (test_bit(i, lgr->rtokens_used_mask) &&
18208c2ecf20Sopenharmony_ci		    lgr->rtokens[i][lnk_idx].rkey == rkey)
18218c2ecf20Sopenharmony_ci			return i;
18228c2ecf20Sopenharmony_ci	}
18238c2ecf20Sopenharmony_ci	return -ENOENT;
18248c2ecf20Sopenharmony_ci}
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci/* set rtoken for a new link to an existing rmb */
18278c2ecf20Sopenharmony_civoid smc_rtoken_set(struct smc_link_group *lgr, int link_idx, int link_idx_new,
18288c2ecf20Sopenharmony_ci		    __be32 nw_rkey_known, __be64 nw_vaddr, __be32 nw_rkey)
18298c2ecf20Sopenharmony_ci{
18308c2ecf20Sopenharmony_ci	int rtok_idx;
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	rtok_idx = smc_rtoken_find_by_link(lgr, link_idx, ntohl(nw_rkey_known));
18338c2ecf20Sopenharmony_ci	if (rtok_idx == -ENOENT)
18348c2ecf20Sopenharmony_ci		return;
18358c2ecf20Sopenharmony_ci	lgr->rtokens[rtok_idx][link_idx_new].rkey = ntohl(nw_rkey);
18368c2ecf20Sopenharmony_ci	lgr->rtokens[rtok_idx][link_idx_new].dma_addr = be64_to_cpu(nw_vaddr);
18378c2ecf20Sopenharmony_ci}
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci/* set rtoken for a new link whose link_id is given */
18408c2ecf20Sopenharmony_civoid smc_rtoken_set2(struct smc_link_group *lgr, int rtok_idx, int link_id,
18418c2ecf20Sopenharmony_ci		     __be64 nw_vaddr, __be32 nw_rkey)
18428c2ecf20Sopenharmony_ci{
18438c2ecf20Sopenharmony_ci	u64 dma_addr = be64_to_cpu(nw_vaddr);
18448c2ecf20Sopenharmony_ci	u32 rkey = ntohl(nw_rkey);
18458c2ecf20Sopenharmony_ci	bool found = false;
18468c2ecf20Sopenharmony_ci	int link_idx;
18478c2ecf20Sopenharmony_ci
18488c2ecf20Sopenharmony_ci	for (link_idx = 0; link_idx < SMC_LINKS_PER_LGR_MAX; link_idx++) {
18498c2ecf20Sopenharmony_ci		if (lgr->lnk[link_idx].link_id == link_id) {
18508c2ecf20Sopenharmony_ci			found = true;
18518c2ecf20Sopenharmony_ci			break;
18528c2ecf20Sopenharmony_ci		}
18538c2ecf20Sopenharmony_ci	}
18548c2ecf20Sopenharmony_ci	if (!found)
18558c2ecf20Sopenharmony_ci		return;
18568c2ecf20Sopenharmony_ci	lgr->rtokens[rtok_idx][link_idx].rkey = rkey;
18578c2ecf20Sopenharmony_ci	lgr->rtokens[rtok_idx][link_idx].dma_addr = dma_addr;
18588c2ecf20Sopenharmony_ci}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci/* add a new rtoken from peer */
18618c2ecf20Sopenharmony_ciint smc_rtoken_add(struct smc_link *lnk, __be64 nw_vaddr, __be32 nw_rkey)
18628c2ecf20Sopenharmony_ci{
18638c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = smc_get_lgr(lnk);
18648c2ecf20Sopenharmony_ci	u64 dma_addr = be64_to_cpu(nw_vaddr);
18658c2ecf20Sopenharmony_ci	u32 rkey = ntohl(nw_rkey);
18668c2ecf20Sopenharmony_ci	int i;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
18698c2ecf20Sopenharmony_ci		if (lgr->rtokens[i][lnk->link_idx].rkey == rkey &&
18708c2ecf20Sopenharmony_ci		    lgr->rtokens[i][lnk->link_idx].dma_addr == dma_addr &&
18718c2ecf20Sopenharmony_ci		    test_bit(i, lgr->rtokens_used_mask)) {
18728c2ecf20Sopenharmony_ci			/* already in list */
18738c2ecf20Sopenharmony_ci			return i;
18748c2ecf20Sopenharmony_ci		}
18758c2ecf20Sopenharmony_ci	}
18768c2ecf20Sopenharmony_ci	i = smc_rmb_reserve_rtoken_idx(lgr);
18778c2ecf20Sopenharmony_ci	if (i < 0)
18788c2ecf20Sopenharmony_ci		return i;
18798c2ecf20Sopenharmony_ci	lgr->rtokens[i][lnk->link_idx].rkey = rkey;
18808c2ecf20Sopenharmony_ci	lgr->rtokens[i][lnk->link_idx].dma_addr = dma_addr;
18818c2ecf20Sopenharmony_ci	return i;
18828c2ecf20Sopenharmony_ci}
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci/* delete an rtoken from all links */
18858c2ecf20Sopenharmony_ciint smc_rtoken_delete(struct smc_link *lnk, __be32 nw_rkey)
18868c2ecf20Sopenharmony_ci{
18878c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = smc_get_lgr(lnk);
18888c2ecf20Sopenharmony_ci	u32 rkey = ntohl(nw_rkey);
18898c2ecf20Sopenharmony_ci	int i, j;
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
18928c2ecf20Sopenharmony_ci		if (lgr->rtokens[i][lnk->link_idx].rkey == rkey &&
18938c2ecf20Sopenharmony_ci		    test_bit(i, lgr->rtokens_used_mask)) {
18948c2ecf20Sopenharmony_ci			for (j = 0; j < SMC_LINKS_PER_LGR_MAX; j++) {
18958c2ecf20Sopenharmony_ci				lgr->rtokens[i][j].rkey = 0;
18968c2ecf20Sopenharmony_ci				lgr->rtokens[i][j].dma_addr = 0;
18978c2ecf20Sopenharmony_ci			}
18988c2ecf20Sopenharmony_ci			clear_bit(i, lgr->rtokens_used_mask);
18998c2ecf20Sopenharmony_ci			return 0;
19008c2ecf20Sopenharmony_ci		}
19018c2ecf20Sopenharmony_ci	}
19028c2ecf20Sopenharmony_ci	return -ENOENT;
19038c2ecf20Sopenharmony_ci}
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci/* save rkey and dma_addr received from peer during clc handshake */
19068c2ecf20Sopenharmony_ciint smc_rmb_rtoken_handling(struct smc_connection *conn,
19078c2ecf20Sopenharmony_ci			    struct smc_link *lnk,
19088c2ecf20Sopenharmony_ci			    struct smc_clc_msg_accept_confirm *clc)
19098c2ecf20Sopenharmony_ci{
19108c2ecf20Sopenharmony_ci	conn->rtoken_idx = smc_rtoken_add(lnk, clc->r0.rmb_dma_addr,
19118c2ecf20Sopenharmony_ci					  clc->r0.rmb_rkey);
19128c2ecf20Sopenharmony_ci	if (conn->rtoken_idx < 0)
19138c2ecf20Sopenharmony_ci		return conn->rtoken_idx;
19148c2ecf20Sopenharmony_ci	return 0;
19158c2ecf20Sopenharmony_ci}
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_cistatic void smc_core_going_away(void)
19188c2ecf20Sopenharmony_ci{
19198c2ecf20Sopenharmony_ci	struct smc_ib_device *smcibdev;
19208c2ecf20Sopenharmony_ci	struct smcd_dev *smcd;
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	mutex_lock(&smc_ib_devices.mutex);
19238c2ecf20Sopenharmony_ci	list_for_each_entry(smcibdev, &smc_ib_devices.list, list) {
19248c2ecf20Sopenharmony_ci		int i;
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci		for (i = 0; i < SMC_MAX_PORTS; i++)
19278c2ecf20Sopenharmony_ci			set_bit(i, smcibdev->ports_going_away);
19288c2ecf20Sopenharmony_ci	}
19298c2ecf20Sopenharmony_ci	mutex_unlock(&smc_ib_devices.mutex);
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	mutex_lock(&smcd_dev_list.mutex);
19328c2ecf20Sopenharmony_ci	list_for_each_entry(smcd, &smcd_dev_list.list, list) {
19338c2ecf20Sopenharmony_ci		smcd->going_away = 1;
19348c2ecf20Sopenharmony_ci	}
19358c2ecf20Sopenharmony_ci	mutex_unlock(&smcd_dev_list.mutex);
19368c2ecf20Sopenharmony_ci}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci/* Clean up all SMC link groups */
19398c2ecf20Sopenharmony_cistatic void smc_lgrs_shutdown(void)
19408c2ecf20Sopenharmony_ci{
19418c2ecf20Sopenharmony_ci	struct smcd_dev *smcd;
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	smc_core_going_away();
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	smc_smcr_terminate_all(NULL);
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci	mutex_lock(&smcd_dev_list.mutex);
19488c2ecf20Sopenharmony_ci	list_for_each_entry(smcd, &smcd_dev_list.list, list)
19498c2ecf20Sopenharmony_ci		smc_smcd_terminate_all(smcd);
19508c2ecf20Sopenharmony_ci	mutex_unlock(&smcd_dev_list.mutex);
19518c2ecf20Sopenharmony_ci}
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_cistatic int smc_core_reboot_event(struct notifier_block *this,
19548c2ecf20Sopenharmony_ci				 unsigned long event, void *ptr)
19558c2ecf20Sopenharmony_ci{
19568c2ecf20Sopenharmony_ci	smc_lgrs_shutdown();
19578c2ecf20Sopenharmony_ci	smc_ib_unregister_client();
19588c2ecf20Sopenharmony_ci	return 0;
19598c2ecf20Sopenharmony_ci}
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_cistatic struct notifier_block smc_reboot_notifier = {
19628c2ecf20Sopenharmony_ci	.notifier_call = smc_core_reboot_event,
19638c2ecf20Sopenharmony_ci};
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ciint __init smc_core_init(void)
19668c2ecf20Sopenharmony_ci{
19678c2ecf20Sopenharmony_ci	return register_reboot_notifier(&smc_reboot_notifier);
19688c2ecf20Sopenharmony_ci}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci/* Called (from smc_exit) when module is removed */
19718c2ecf20Sopenharmony_civoid smc_core_exit(void)
19728c2ecf20Sopenharmony_ci{
19738c2ecf20Sopenharmony_ci	unregister_reboot_notifier(&smc_reboot_notifier);
19748c2ecf20Sopenharmony_ci	smc_lgrs_shutdown();
19758c2ecf20Sopenharmony_ci}
1976