Commit 1412bb2b authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-smc-fixes'



Karsten Graul says:

====================
net/smc: fixes 2020-07-08

Please apply the following patch series for smc to netdev's net tree.

The patches fix problems found during more testing of SMC
functionality, resulting in hang conditions and unneeded link
deactivations. The clc module was hardened to be prepared for
possible future SMCD versions.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a42e6aee fb4f7926
Loading
Loading
Loading
Loading
+32 −13
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@

#define SMCR_CLC_ACCEPT_CONFIRM_LEN 68
#define SMCD_CLC_ACCEPT_CONFIRM_LEN 48
#define SMC_CLC_RECV_BUF_LEN	100

/* eye catcher "SMCR" EBCDIC for CLC messages */
static const char SMC_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xd9'};
@@ -36,7 +37,7 @@ static const char SMCD_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xc4'};
/* check if received message has a correct header length and contains valid
 * heading and trailing eyecatchers
 */
static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm)
static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm, bool check_trl)
{
	struct smc_clc_msg_proposal_prefix *pclc_prfx;
	struct smc_clc_msg_accept_confirm *clc;
@@ -49,12 +50,9 @@ static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm)
		return false;
	switch (clcm->type) {
	case SMC_CLC_PROPOSAL:
		if (clcm->path != SMC_TYPE_R && clcm->path != SMC_TYPE_D &&
		    clcm->path != SMC_TYPE_B)
			return false;
		pclc = (struct smc_clc_msg_proposal *)clcm;
		pclc_prfx = smc_clc_proposal_get_prefix(pclc);
		if (ntohs(pclc->hdr.length) !=
		if (ntohs(pclc->hdr.length) <
			sizeof(*pclc) + ntohs(pclc->iparea_offset) +
			sizeof(*pclc_prfx) +
			pclc_prfx->ipv6_prefixes_cnt *
@@ -86,7 +84,8 @@ static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm)
	default:
		return false;
	}
	if (memcmp(trl->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)) &&
	if (check_trl &&
	    memcmp(trl->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)) &&
	    memcmp(trl->eyecatcher, SMCD_EYECATCHER, sizeof(SMCD_EYECATCHER)))
		return false;
	return true;
@@ -276,7 +275,8 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
	struct msghdr msg = {NULL, 0};
	int reason_code = 0;
	struct kvec vec = {buf, buflen};
	int len, datlen;
	int len, datlen, recvlen;
	bool check_trl = true;
	int krflags;

	/* peek the first few bytes to determine length of data to receive
@@ -320,10 +320,7 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
	}
	datlen = ntohs(clcm->length);
	if ((len < sizeof(struct smc_clc_msg_hdr)) ||
	    (datlen > buflen) ||
	    (clcm->version != SMC_CLC_V1) ||
	    (clcm->path != SMC_TYPE_R && clcm->path != SMC_TYPE_D &&
	     clcm->path != SMC_TYPE_B) ||
	    (clcm->version < SMC_CLC_V1) ||
	    ((clcm->type != SMC_CLC_DECLINE) &&
	     (clcm->type != expected_type))) {
		smc->sk.sk_err = EPROTO;
@@ -331,16 +328,38 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
		goto out;
	}

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

	/* receive the complete CLC message */
	memset(&msg, 0, sizeof(struct msghdr));
	iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, datlen);
	if (datlen > buflen) {
		check_trl = false;
		recvlen = buflen;
	} else {
		recvlen = datlen;
	}
	iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, recvlen);
	krflags = MSG_WAITALL;
	len = sock_recvmsg(smc->clcsock, &msg, krflags);
	if (len < datlen || !smc_clc_msg_hdr_valid(clcm)) {
	if (len < recvlen || !smc_clc_msg_hdr_valid(clcm, check_trl)) {
		smc->sk.sk_err = EPROTO;
		reason_code = -EPROTO;
		goto out;
	}
	datlen -= len;
	while (datlen) {
		u8 tmp[SMC_CLC_RECV_BUF_LEN];

		vec.iov_base = &tmp;
		vec.iov_len = SMC_CLC_RECV_BUF_LEN;
		/* receive remaining proposal message */
		recvlen = datlen > SMC_CLC_RECV_BUF_LEN ?
						SMC_CLC_RECV_BUF_LEN : datlen;
		iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, recvlen);
		len = sock_recvmsg(smc->clcsock, &msg, krflags);
		datlen -= len;
	}
	if (clcm->type == SMC_CLC_DECLINE) {
		struct smc_clc_msg_decline *dclc;

+2 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#define SMC_CLC_V1		0x1		/* SMC version                */
#define SMC_TYPE_R		0		/* SMC-R only		      */
#define SMC_TYPE_D		1		/* SMC-D only		      */
#define SMC_TYPE_N		2		/* neither SMC-R nor SMC-D    */
#define SMC_TYPE_B		3		/* SMC-R and SMC-D	      */
#define CLC_WAIT_TIME		(6 * HZ)	/* max. wait time on clcsock  */
#define CLC_WAIT_TIME_SHORT	HZ		/* short wait time on clcsock */
@@ -46,6 +47,7 @@
#define SMC_CLC_DECL_ISMVLANERR	0x03090000  /* err to reg vlan id on ism dev  */
#define SMC_CLC_DECL_NOACTLINK	0x030a0000  /* no active smc-r link in lgr    */
#define SMC_CLC_DECL_NOSRVLINK	0x030b0000  /* SMC-R link from srv not found  */
#define SMC_CLC_DECL_VERSMISMAT	0x030c0000  /* SMC version mismatch	      */
#define SMC_CLC_DECL_SYNCERR	0x04000000  /* synchronization error          */
#define SMC_CLC_DECL_PEERDECL	0x05000000  /* peer declined during handshake */
#define SMC_CLC_DECL_INTERR	0x09990000  /* internal error		      */
+29 −22
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/reboot.h>
#include <linux/mutex.h>
#include <net/tcp.h>
#include <net/sock.h>
#include <rdma/ib_verbs.h>
@@ -247,7 +248,8 @@ static void smcr_lgr_link_deactivate_all(struct smc_link_group *lgr)
		if (smc_link_usable(lnk))
			lnk->state = SMC_LNK_INACTIVE;
	}
	wake_up_interruptible_all(&lgr->llc_waiter);
	wake_up_all(&lgr->llc_msg_waiter);
	wake_up_all(&lgr->llc_flow_waiter);
}

static void smc_lgr_free(struct smc_link_group *lgr);
@@ -1130,18 +1132,19 @@ static void smcr_link_up(struct smc_link_group *lgr,
			return;
		if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
			/* some other llc task is ongoing */
			wait_event_interruptible_timeout(lgr->llc_waiter,
				(lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE),
			wait_event_timeout(lgr->llc_flow_waiter,
				(list_empty(&lgr->list) ||
				 lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE),
				SMC_LLC_WAIT_TIME);
		}
		if (list_empty(&lgr->list) ||
		    !smc_ib_port_active(smcibdev, ibport))
			return; /* lgr or device no longer active */
		/* lgr or device no longer active? */
		if (!list_empty(&lgr->list) &&
		    smc_ib_port_active(smcibdev, ibport))
			link = smc_llc_usable_link(lgr);
		if (!link)
			return;
		smc_llc_send_add_link(link, smcibdev->mac[ibport - 1], gid,
				      NULL, SMC_LLC_REQ);
		if (link)
			smc_llc_send_add_link(link, smcibdev->mac[ibport - 1],
					      gid, NULL, SMC_LLC_REQ);
		wake_up(&lgr->llc_flow_waiter);	/* wake up next waiter */
	}
}

@@ -1195,13 +1198,17 @@ static void smcr_link_down(struct smc_link *lnk)
		if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
			/* another llc task is ongoing */
			mutex_unlock(&lgr->llc_conf_mutex);
			wait_event_interruptible_timeout(lgr->llc_waiter,
				(lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE),
			wait_event_timeout(lgr->llc_flow_waiter,
				(list_empty(&lgr->list) ||
				 lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE),
				SMC_LLC_WAIT_TIME);
			mutex_lock(&lgr->llc_conf_mutex);
		}
		smc_llc_send_delete_link(to_lnk, del_link_id, SMC_LLC_REQ, true,
		if (!list_empty(&lgr->list))
			smc_llc_send_delete_link(to_lnk, del_link_id,
						 SMC_LLC_REQ, true,
						 SMC_LLC_DEL_LOST_PATH);
		wake_up(&lgr->llc_flow_waiter);	/* wake up next waiter */
	}
}

@@ -1262,7 +1269,7 @@ static void smc_link_down_work(struct work_struct *work)

	if (list_empty(&lgr->list))
		return;
	wake_up_interruptible_all(&lgr->llc_waiter);
	wake_up_all(&lgr->llc_msg_waiter);
	mutex_lock(&lgr->llc_conf_mutex);
	smcr_link_down(link);
	mutex_unlock(&lgr->llc_conf_mutex);
@@ -1955,20 +1962,20 @@ static void smc_core_going_away(void)
	struct smc_ib_device *smcibdev;
	struct smcd_dev *smcd;

	spin_lock(&smc_ib_devices.lock);
	mutex_lock(&smc_ib_devices.mutex);
	list_for_each_entry(smcibdev, &smc_ib_devices.list, list) {
		int i;

		for (i = 0; i < SMC_MAX_PORTS; i++)
			set_bit(i, smcibdev->ports_going_away);
	}
	spin_unlock(&smc_ib_devices.lock);
	mutex_unlock(&smc_ib_devices.mutex);

	spin_lock(&smcd_dev_list.lock);
	mutex_lock(&smcd_dev_list.mutex);
	list_for_each_entry(smcd, &smcd_dev_list.list, list) {
		smcd->going_away = 1;
	}
	spin_unlock(&smcd_dev_list.lock);
	mutex_unlock(&smcd_dev_list.mutex);
}

/* Clean up all SMC link groups */
@@ -1980,10 +1987,10 @@ static void smc_lgrs_shutdown(void)

	smc_smcr_terminate_all(NULL);

	spin_lock(&smcd_dev_list.lock);
	mutex_lock(&smcd_dev_list.mutex);
	list_for_each_entry(smcd, &smcd_dev_list.list, list)
		smc_smcd_terminate_all(smcd);
	spin_unlock(&smcd_dev_list.lock);
	mutex_unlock(&smcd_dev_list.mutex);
}

static int smc_core_reboot_event(struct notifier_block *this,
+3 −1
Original line number Diff line number Diff line
@@ -262,8 +262,10 @@ struct smc_link_group {
			struct work_struct	llc_del_link_work;
			struct work_struct	llc_event_work;
						/* llc event worker */
			wait_queue_head_t	llc_waiter;
			wait_queue_head_t	llc_flow_waiter;
						/* w4 next llc event */
			wait_queue_head_t	llc_msg_waiter;
						/* w4 next llc msg */
			struct smc_llc_flow	llc_flow_lcl;
						/* llc local control field */
			struct smc_llc_flow	llc_flow_rmt;
+6 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/workqueue.h>
#include <linux/scatterlist.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_cache.h>

@@ -33,7 +34,7 @@
#define SMC_QP_RNR_RETRY			7 /* 7: infinite */

struct smc_ib_devices smc_ib_devices = {	/* smc-registered ib devices */
	.lock = __SPIN_LOCK_UNLOCKED(smc_ib_devices.lock),
	.mutex = __MUTEX_INITIALIZER(smc_ib_devices.mutex),
	.list = LIST_HEAD_INIT(smc_ib_devices.list),
};

@@ -565,9 +566,9 @@ static int smc_ib_add_dev(struct ib_device *ibdev)
	INIT_WORK(&smcibdev->port_event_work, smc_ib_port_event_work);
	atomic_set(&smcibdev->lnk_cnt, 0);
	init_waitqueue_head(&smcibdev->lnks_deleted);
	spin_lock(&smc_ib_devices.lock);
	mutex_lock(&smc_ib_devices.mutex);
	list_add_tail(&smcibdev->list, &smc_ib_devices.list);
	spin_unlock(&smc_ib_devices.lock);
	mutex_unlock(&smc_ib_devices.mutex);
	ib_set_client_data(ibdev, &smc_ib_client, smcibdev);
	INIT_IB_EVENT_HANDLER(&smcibdev->event_handler, smcibdev->ibdev,
			      smc_ib_global_event_handler);
@@ -602,9 +603,9 @@ static void smc_ib_remove_dev(struct ib_device *ibdev, void *client_data)
{
	struct smc_ib_device *smcibdev = client_data;

	spin_lock(&smc_ib_devices.lock);
	mutex_lock(&smc_ib_devices.mutex);
	list_del_init(&smcibdev->list); /* remove from smc_ib_devices */
	spin_unlock(&smc_ib_devices.lock);
	mutex_unlock(&smc_ib_devices.mutex);
	pr_warn_ratelimited("smc: removing ib device %s\n",
			    smcibdev->ibdev->name);
	smc_smcr_terminate_all(smcibdev);
Loading