Skip to content

Commit

Permalink
Merge branch 'net-smc-fixes'
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jul 19, 2020
2 parents 9b87377 + 1ad2405 commit a463fa2
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 133 deletions.
12 changes: 8 additions & 4 deletions net/smc/af_smc.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,10 @@ EXPORT_SYMBOL_GPL(smc_proto6);

static void smc_restore_fallback_changes(struct smc_sock *smc)
{
smc->clcsock->file->private_data = smc->sk.sk_socket;
smc->clcsock->file = NULL;
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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down
105 changes: 22 additions & 83 deletions net/smc/smc_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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 */
}
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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) &&
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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;
}

Expand Down
5 changes: 5 additions & 0 deletions net/smc/smc_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 13 additions & 3 deletions net/smc/smc_ib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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;
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions net/smc/smc_ib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit a463fa2

Please sign in to comment.