Commit a463fa2c 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-16

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

The patches address problems caused by late or unexpected link layer
control packets, dma sync calls for unmapped memory, freed buffers
that are not removed from the buffer list and a possible null pointer
access that results in a crash.

v1->v2: in patch 4, improve patch description and correct the comment
        for the new mutex
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9b873778 1ad24058
Loading
Loading
Loading
Loading
+8 −4
Original line number Diff line number Diff line
@@ -126,9 +126,11 @@ EXPORT_SYMBOL_GPL(smc_proto6);

static void smc_restore_fallback_changes(struct smc_sock *smc)
{
	if (smc->clcsock->file) { /* non-accepted sockets have no file yet */
		smc->clcsock->file->private_data = smc->sk.sk_socket;
		smc->clcsock->file = NULL;
	}
}

static int __smc_release(struct smc_sock *smc)
{
@@ -352,7 +354,7 @@ static int smcr_lgr_reg_rmbs(struct smc_link *link,
	 */
	mutex_lock(&lgr->llc_conf_mutex);
	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
		if (lgr->lnk[i].state != SMC_LNK_ACTIVE)
		if (!smc_link_active(&lgr->lnk[i]))
			continue;
		rc = smcr_link_reg_rmb(&lgr->lnk[i], rmb_desc);
		if (rc)
@@ -632,7 +634,9 @@ static int smc_connect_rdma(struct smc_sock *smc,
		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
			struct smc_link *l = &smc->conn.lgr->lnk[i];

			if (l->peer_qpn == ntoh24(aclc->qpn)) {
			if (l->peer_qpn == ntoh24(aclc->qpn) &&
			    !memcmp(l->peer_gid, &aclc->lcl.gid, SMC_GID_SIZE) &&
			    !memcmp(l->peer_mac, &aclc->lcl.mac, sizeof(l->peer_mac))) {
				link = l;
				break;
			}
+22 −83
Original line number Diff line number Diff line
@@ -45,18 +45,10 @@ static struct smc_lgr_list smc_lgr_list = { /* established link groups */
static atomic_t lgr_cnt = ATOMIC_INIT(0); /* number of existing link groups */
static DECLARE_WAIT_QUEUE_HEAD(lgrs_deleted);

struct smc_ib_up_work {
	struct work_struct	work;
	struct smc_link_group	*lgr;
	struct smc_ib_device	*smcibdev;
	u8			ibport;
};

static void smc_buf_free(struct smc_link_group *lgr, bool is_rmb,
			 struct smc_buf_desc *buf_desc);
static void __smc_lgr_terminate(struct smc_link_group *lgr, bool soft);

static void smc_link_up_work(struct work_struct *work);
static void smc_link_down_work(struct work_struct *work);

/* return head of link group list and its lock for a given link group */
@@ -326,7 +318,6 @@ int smcr_link_init(struct smc_link_group *lgr, struct smc_link *lnk,

	get_device(&ini->ib_dev->ibdev->dev);
	atomic_inc(&ini->ib_dev->lnk_cnt);
	lnk->state = SMC_LNK_ACTIVATING;
	lnk->link_id = smcr_next_link_id(lgr);
	lnk->lgr = lgr;
	lnk->link_idx = link_idx;
@@ -362,6 +353,7 @@ int smcr_link_init(struct smc_link_group *lgr, struct smc_link *lnk,
	rc = smc_wr_create_link(lnk);
	if (rc)
		goto destroy_qp;
	lnk->state = SMC_LNK_ACTIVATING;
	return 0;

destroy_qp:
@@ -550,8 +542,7 @@ struct smc_link *smc_switch_conns(struct smc_link_group *lgr,
	smc_wr_wakeup_tx_wait(from_lnk);

	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
		if (lgr->lnk[i].state != SMC_LNK_ACTIVE ||
		    i == from_lnk->link_idx)
		if (!smc_link_active(&lgr->lnk[i]) || i == from_lnk->link_idx)
			continue;
		if (is_dev_err && from_lnk->smcibdev == lgr->lnk[i].smcibdev &&
		    from_lnk->ibport == lgr->lnk[i].ibport) {
@@ -1106,67 +1097,23 @@ static void smc_conn_abort_work(struct work_struct *work)
	sock_put(&smc->sk); /* sock_hold done by schedulers of abort_work */
}

/* link is up - establish alternate link if applicable */
static void smcr_link_up(struct smc_link_group *lgr,
			 struct smc_ib_device *smcibdev, u8 ibport)
{
	struct smc_link *link = NULL;

	if (list_empty(&lgr->list) ||
	    lgr->type == SMC_LGR_SYMMETRIC ||
	    lgr->type == SMC_LGR_ASYMMETRIC_PEER)
		return;

	if (lgr->role == SMC_SERV) {
		/* trigger local add link processing */
		link = smc_llc_usable_link(lgr);
		if (!link)
			return;
		smc_llc_srv_add_link_local(link);
	} else {
		/* invite server to start add link processing */
		u8 gid[SMC_GID_SIZE];

		if (smc_ib_determine_gid(smcibdev, ibport, lgr->vlan_id, gid,
					 NULL))
			return;
		if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
			/* some other llc task is ongoing */
			wait_event_timeout(lgr->llc_flow_waiter,
				(list_empty(&lgr->list) ||
				 lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE),
				SMC_LLC_WAIT_TIME);
		}
		/* 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)
			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 */
	}
}

void smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport)
{
	struct smc_ib_up_work *ib_work;
	struct smc_link_group *lgr, *n;

	list_for_each_entry_safe(lgr, n, &smc_lgr_list.list, list) {
		struct smc_link *link;

		if (strncmp(smcibdev->pnetid[ibport - 1], lgr->pnet_id,
			    SMC_MAX_PNETID_LEN) ||
		    lgr->type == SMC_LGR_SYMMETRIC ||
		    lgr->type == SMC_LGR_ASYMMETRIC_PEER)
			continue;
		ib_work = kmalloc(sizeof(*ib_work), GFP_KERNEL);
		if (!ib_work)
			continue;
		INIT_WORK(&ib_work->work, smc_link_up_work);
		ib_work->lgr = lgr;
		ib_work->smcibdev = smcibdev;
		ib_work->ibport = ibport;
		schedule_work(&ib_work->work);

		/* trigger local add link processing */
		link = smc_llc_usable_link(lgr);
		if (link)
			smc_llc_add_link_local(link);
	}
}

@@ -1204,10 +1151,12 @@ static void smcr_link_down(struct smc_link *lnk)
				SMC_LLC_WAIT_TIME);
			mutex_lock(&lgr->llc_conf_mutex);
		}
		if (!list_empty(&lgr->list))
		if (!list_empty(&lgr->list)) {
			smc_llc_send_delete_link(to_lnk, del_link_id,
						 SMC_LLC_REQ, true,
						 SMC_LLC_DEL_LOST_PATH);
			smcr_link_clear(lnk, true);
		}
		wake_up(&lgr->llc_flow_waiter);	/* wake up next waiter */
	}
}
@@ -1247,20 +1196,6 @@ void smcr_port_err(struct smc_ib_device *smcibdev, u8 ibport)
	}
}

static void smc_link_up_work(struct work_struct *work)
{
	struct smc_ib_up_work *ib_work = container_of(work,
						      struct smc_ib_up_work,
						      work);
	struct smc_link_group *lgr = ib_work->lgr;

	if (list_empty(&lgr->list))
		goto out;
	smcr_link_up(lgr, ib_work->smcibdev, ib_work->ibport);
out:
	kfree(ib_work);
}

static void smc_link_down_work(struct work_struct *work)
{
	struct smc_link *link = container_of(work, struct smc_link,
@@ -1333,7 +1268,7 @@ static bool smcr_lgr_match(struct smc_link_group *lgr,
		return false;

	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
		if (lgr->lnk[i].state != SMC_LNK_ACTIVE)
		if (!smc_link_active(&lgr->lnk[i]))
			continue;
		if ((lgr->role == SMC_SERV || lgr->lnk[i].peer_qpn == clcqpn) &&
		    !memcmp(lgr->lnk[i].peer_gid, &lcl->gid, SMC_GID_SIZE) &&
@@ -1781,14 +1716,14 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)

void smc_sndbuf_sync_sg_for_cpu(struct smc_connection *conn)
{
	if (!conn->lgr || conn->lgr->is_smcd || !smc_link_usable(conn->lnk))
	if (!conn->lgr || conn->lgr->is_smcd || !smc_link_active(conn->lnk))
		return;
	smc_ib_sync_sg_for_cpu(conn->lnk, conn->sndbuf_desc, DMA_TO_DEVICE);
}

void smc_sndbuf_sync_sg_for_device(struct smc_connection *conn)
{
	if (!conn->lgr || conn->lgr->is_smcd || !smc_link_usable(conn->lnk))
	if (!conn->lgr || conn->lgr->is_smcd || !smc_link_active(conn->lnk))
		return;
	smc_ib_sync_sg_for_device(conn->lnk, conn->sndbuf_desc, DMA_TO_DEVICE);
}
@@ -1800,7 +1735,7 @@ void smc_rmb_sync_sg_for_cpu(struct smc_connection *conn)
	if (!conn->lgr || conn->lgr->is_smcd)
		return;
	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
		if (!smc_link_usable(&conn->lgr->lnk[i]))
		if (!smc_link_active(&conn->lgr->lnk[i]))
			continue;
		smc_ib_sync_sg_for_cpu(&conn->lgr->lnk[i], conn->rmb_desc,
				       DMA_FROM_DEVICE);
@@ -1814,7 +1749,7 @@ void smc_rmb_sync_sg_for_device(struct smc_connection *conn)
	if (!conn->lgr || conn->lgr->is_smcd)
		return;
	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
		if (!smc_link_usable(&conn->lgr->lnk[i]))
		if (!smc_link_active(&conn->lgr->lnk[i]))
			continue;
		smc_ib_sync_sg_for_device(&conn->lgr->lnk[i], conn->rmb_desc,
					  DMA_FROM_DEVICE);
@@ -1837,8 +1772,12 @@ int smc_buf_create(struct smc_sock *smc, bool is_smcd)
		return rc;
	/* create rmb */
	rc = __smc_buf_create(smc, is_smcd, true);
	if (rc)
	if (rc) {
		mutex_lock(&smc->conn.lgr->sndbufs_lock);
		list_del(&smc->conn.sndbuf_desc->list);
		mutex_unlock(&smc->conn.lgr->sndbufs_lock);
		smc_buf_free(smc->conn.lgr, false, smc->conn.sndbuf_desc);
	}
	return rc;
}

+5 −0
Original line number Diff line number Diff line
@@ -349,6 +349,11 @@ static inline bool smc_link_usable(struct smc_link *lnk)
	return true;
}

static inline bool smc_link_active(struct smc_link *lnk)
{
	return lnk->state == SMC_LNK_ACTIVE;
}

struct smc_sock;
struct smc_clc_msg_accept_confirm;
struct smc_clc_msg_local;
+13 −3
Original line number Diff line number Diff line
@@ -506,6 +506,10 @@ long smc_ib_setup_per_ibdev(struct smc_ib_device *smcibdev)
	int cqe_size_order, smc_order;
	long rc;

	mutex_lock(&smcibdev->mutex);
	rc = 0;
	if (smcibdev->initialized)
		goto out;
	/* the calculated number of cq entries fits to mlx5 cq allocation */
	cqe_size_order = cache_line_size() == 128 ? 7 : 6;
	smc_order = MAX_ORDER - cqe_size_order - 1;
@@ -517,7 +521,7 @@ long smc_ib_setup_per_ibdev(struct smc_ib_device *smcibdev)
	rc = PTR_ERR_OR_ZERO(smcibdev->roce_cq_send);
	if (IS_ERR(smcibdev->roce_cq_send)) {
		smcibdev->roce_cq_send = NULL;
		return rc;
		goto out;
	}
	smcibdev->roce_cq_recv = ib_create_cq(smcibdev->ibdev,
					      smc_wr_rx_cq_handler, NULL,
@@ -529,21 +533,26 @@ long smc_ib_setup_per_ibdev(struct smc_ib_device *smcibdev)
	}
	smc_wr_add_dev(smcibdev);
	smcibdev->initialized = 1;
	return rc;
	goto out;

err:
	ib_destroy_cq(smcibdev->roce_cq_send);
out:
	mutex_unlock(&smcibdev->mutex);
	return rc;
}

static void smc_ib_cleanup_per_ibdev(struct smc_ib_device *smcibdev)
{
	mutex_lock(&smcibdev->mutex);
	if (!smcibdev->initialized)
		return;
		goto out;
	smcibdev->initialized = 0;
	ib_destroy_cq(smcibdev->roce_cq_recv);
	ib_destroy_cq(smcibdev->roce_cq_send);
	smc_wr_remove_dev(smcibdev);
out:
	mutex_unlock(&smcibdev->mutex);
}

static struct ib_client smc_ib_client;
@@ -566,6 +575,7 @@ 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);
	mutex_init(&smcibdev->mutex);
	mutex_lock(&smc_ib_devices.mutex);
	list_add_tail(&smcibdev->list, &smc_ib_devices.list);
	mutex_unlock(&smc_ib_devices.mutex);
+1 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ struct smc_ib_device { /* ib-device infos for smc */
	DECLARE_BITMAP(ports_going_away, SMC_MAX_PORTS);
	atomic_t		lnk_cnt;	/* number of links on ibdev */
	wait_queue_head_t	lnks_deleted;	/* wait 4 removal of all links*/
	struct mutex		mutex;		/* protect dev setup+cleanup */
};

struct smc_buf_desc;
Loading