Commit 3898f52c authored by David S. Miller's avatar David S. Miller

Merge branch 'net-smc-virt-contig-buffers'

Wen Gu says:

====================
net/smc: Introduce virtually contiguous buffers for SMC-R

On long-running enterprise production servers, high-order contiguous
memory pages are usually very rare and in most cases we can only get
fragmented pages.

When replacing TCP with SMC-R in such production scenarios, attempting
to allocate high-order physically contiguous sndbufs and RMBs may result
in frequent memory compaction, which will cause unexpected hung issue
and further stability risks.

So this patch set is aimed to allow SMC-R link group to use virtually
contiguous sndbufs and RMBs to avoid potential issues mentioned above.
Whether to use physically or virtually contiguous buffers can be set
by sysctl smcr_buf_type.

Note that using virtually contiguous buffers will bring an acceptable
performance regression, which can be mainly divided into two parts:

1) regression in data path, which is brought by additional address
   translation of sndbuf by RNIC in Tx. But in general, translating
   address through MTT is fast. According to qperf test, this part
   regression is basically less than 10% in latency and bandwidth.
   (see patch 5/6 for details)

2) regression in buffer initialization and destruction path, which is
   brought by additional MR operations of sndbufs. But thanks to link
   group buffer reuse mechanism, the impact of this kind of regression
   decreases as times of buffer reuse increases.

Patch set overview:
- Patch 1/6 and 2/6 mainly about simplifying and optimizing DMA sync
  operation, which will reduce overhead on the data path, especially
  when using virtually contiguous buffers;
- Patch 3/6 and 4/6 introduce a sysctl smcr_buf_type to set the type
  of buffers in new created link group;
- Patch 5/6 allows SMC-R to use virtually contiguous sndbufs and RMBs,
  including buffer creation, destruction, MR operation and access;
- patch 6/6 extends netlink attribute for buffer type of SMC-R link group;

v1->v2:
- Patch 5/6 fixes build issue on 32bit;
- Patch 3/6 adds description of new sysctl in smc-sysctl.rst;
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2acd1022 ddefb2d2
......@@ -21,3 +21,16 @@ autocorking_size - INTEGER
know how/when to uncork their sockets.
Default: 64K
smcr_buf_type - INTEGER
Controls which type of sndbufs and RMBs to use in later newly created
SMC-R link group. Only for SMC-R.
Default: 0 (physically contiguous sndbufs and RMBs)
Possible values:
- 0 - Use physically contiguous buffers
- 1 - Use virtually contiguous buffers
- 2 - Mixed use of the two types. Try physically contiguous buffers first.
If not available, use virtually contiguous buffers then.
......@@ -18,5 +18,6 @@ struct netns_smc {
struct ctl_table_header *smc_hdr;
#endif
unsigned int sysctl_autocorking_size;
unsigned int sysctl_smcr_buf_type;
};
#endif
......@@ -124,6 +124,7 @@ enum {
SMC_NLA_LGR_R_V2, /* nest */
SMC_NLA_LGR_R_NET_COOKIE, /* u64 */
SMC_NLA_LGR_R_PAD, /* flag */
SMC_NLA_LGR_R_BUF_TYPE, /* u8 */
__SMC_NLA_LGR_R_MAX,
SMC_NLA_LGR_R_MAX = __SMC_NLA_LGR_R_MAX - 1
};
......
......@@ -487,6 +487,29 @@ static void smc_copy_sock_settings_to_smc(struct smc_sock *smc)
smc_copy_sock_settings(&smc->sk, smc->clcsock->sk, SK_FLAGS_CLC_TO_SMC);
}
/* register the new vzalloced sndbuf on all links */
static int smcr_lgr_reg_sndbufs(struct smc_link *link,
struct smc_buf_desc *snd_desc)
{
struct smc_link_group *lgr = link->lgr;
int i, rc = 0;
if (!snd_desc->is_vm)
return -EINVAL;
/* protect against parallel smcr_link_reg_buf() */
mutex_lock(&lgr->llc_conf_mutex);
for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
if (!smc_link_active(&lgr->lnk[i]))
continue;
rc = smcr_link_reg_buf(&lgr->lnk[i], snd_desc);
if (rc)
break;
}
mutex_unlock(&lgr->llc_conf_mutex);
return rc;
}
/* register the new rmb on all links */
static int smcr_lgr_reg_rmbs(struct smc_link *link,
struct smc_buf_desc *rmb_desc)
......@@ -498,13 +521,13 @@ static int smcr_lgr_reg_rmbs(struct smc_link *link,
if (rc)
return rc;
/* protect against parallel smc_llc_cli_rkey_exchange() and
* parallel smcr_link_reg_rmb()
* parallel smcr_link_reg_buf()
*/
mutex_lock(&lgr->llc_conf_mutex);
for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
if (!smc_link_active(&lgr->lnk[i]))
continue;
rc = smcr_link_reg_rmb(&lgr->lnk[i], rmb_desc);
rc = smcr_link_reg_buf(&lgr->lnk[i], rmb_desc);
if (rc)
goto out;
}
......@@ -550,8 +573,15 @@ static int smcr_clnt_conf_first_link(struct smc_sock *smc)
smc_wr_remember_qp_attr(link);
if (smcr_link_reg_rmb(link, smc->conn.rmb_desc))
return SMC_CLC_DECL_ERR_REGRMB;
/* reg the sndbuf if it was vzalloced */
if (smc->conn.sndbuf_desc->is_vm) {
if (smcr_link_reg_buf(link, smc->conn.sndbuf_desc))
return SMC_CLC_DECL_ERR_REGBUF;
}
/* reg the rmb */
if (smcr_link_reg_buf(link, smc->conn.rmb_desc))
return SMC_CLC_DECL_ERR_REGBUF;
/* confirm_rkey is implicit on 1st contact */
smc->conn.rmb_desc->is_conf_rkey = true;
......@@ -1221,12 +1251,18 @@ static int smc_connect_rdma(struct smc_sock *smc,
goto connect_abort;
}
} else {
/* reg sendbufs if they were vzalloced */
if (smc->conn.sndbuf_desc->is_vm) {
if (smcr_lgr_reg_sndbufs(link, smc->conn.sndbuf_desc)) {
reason_code = SMC_CLC_DECL_ERR_REGBUF;
goto connect_abort;
}
}
if (smcr_lgr_reg_rmbs(link, smc->conn.rmb_desc)) {
reason_code = SMC_CLC_DECL_ERR_REGRMB;
reason_code = SMC_CLC_DECL_ERR_REGBUF;
goto connect_abort;
}
}
smc_rmb_sync_sg_for_device(&smc->conn);
if (aclc->hdr.version > SMC_V1) {
struct smc_clc_msg_accept_confirm_v2 *clc_v2 =
......@@ -1750,8 +1786,15 @@ static int smcr_serv_conf_first_link(struct smc_sock *smc)
struct smc_llc_qentry *qentry;
int rc;
if (smcr_link_reg_rmb(link, smc->conn.rmb_desc))
return SMC_CLC_DECL_ERR_REGRMB;
/* reg the sndbuf if it was vzalloced*/
if (smc->conn.sndbuf_desc->is_vm) {
if (smcr_link_reg_buf(link, smc->conn.sndbuf_desc))
return SMC_CLC_DECL_ERR_REGBUF;
}
/* reg the rmb */
if (smcr_link_reg_buf(link, smc->conn.rmb_desc))
return SMC_CLC_DECL_ERR_REGBUF;
/* send CONFIRM LINK request to client over the RoCE fabric */
rc = smc_llc_send_confirm_link(link, SMC_LLC_REQ);
......@@ -2110,10 +2153,15 @@ static int smc_listen_rdma_reg(struct smc_sock *new_smc, bool local_first)
struct smc_connection *conn = &new_smc->conn;
if (!local_first) {
/* reg sendbufs if they were vzalloced */
if (conn->sndbuf_desc->is_vm) {
if (smcr_lgr_reg_sndbufs(conn->lnk,
conn->sndbuf_desc))
return SMC_CLC_DECL_ERR_REGBUF;
}
if (smcr_lgr_reg_rmbs(conn->lnk, conn->rmb_desc))
return SMC_CLC_DECL_ERR_REGRMB;
return SMC_CLC_DECL_ERR_REGBUF;
}
smc_rmb_sync_sg_for_device(&new_smc->conn);
return 0;
}
......
......@@ -1034,7 +1034,7 @@ static int smc_clc_send_confirm_accept(struct smc_sock *smc,
ETH_ALEN);
hton24(clc->r0.qpn, link->roce_qp->qp_num);
clc->r0.rmb_rkey =
htonl(conn->rmb_desc->mr_rx[link->link_idx]->rkey);
htonl(conn->rmb_desc->mr[link->link_idx]->rkey);
clc->r0.rmbe_idx = 1; /* for now: 1 RMB = 1 RMBE */
clc->r0.rmbe_alert_token = htonl(conn->alert_token_local);
switch (clc->hdr.type) {
......@@ -1046,7 +1046,9 @@ static int smc_clc_send_confirm_accept(struct smc_sock *smc,
break;
}
clc->r0.rmbe_size = conn->rmbe_size_short;
clc->r0.rmb_dma_addr = cpu_to_be64((u64)sg_dma_address
clc->r0.rmb_dma_addr = conn->rmb_desc->is_vm ?
cpu_to_be64((uintptr_t)conn->rmb_desc->cpu_addr) :
cpu_to_be64((u64)sg_dma_address
(conn->rmb_desc->sgt[link->link_idx].sgl));
hton24(clc->r0.psn, link->psn_initial);
if (version == SMC_V1) {
......
......@@ -62,7 +62,7 @@
#define SMC_CLC_DECL_INTERR 0x09990000 /* internal error */
#define SMC_CLC_DECL_ERR_RTOK 0x09990001 /* rtoken handling failed */
#define SMC_CLC_DECL_ERR_RDYLNK 0x09990002 /* ib ready link failed */
#define SMC_CLC_DECL_ERR_REGRMB 0x09990003 /* reg rmb failed */
#define SMC_CLC_DECL_ERR_REGBUF 0x09990003 /* reg rdma bufs failed */
#define SMC_FIRST_CONTACT_MASK 0b10 /* first contact bit within typev2 */
......
......@@ -347,6 +347,8 @@ static int smc_nl_fill_lgr(struct smc_link_group *lgr,
goto errattr;
if (nla_put_u8(skb, SMC_NLA_LGR_R_TYPE, lgr->type))
goto errattr;
if (nla_put_u8(skb, SMC_NLA_LGR_R_BUF_TYPE, lgr->buf_type))
goto errattr;
if (nla_put_u8(skb, SMC_NLA_LGR_R_VLAN_ID, lgr->vlan_id))
goto errattr;
if (nla_put_u64_64bit(skb, SMC_NLA_LGR_R_NET_COOKIE,
......@@ -907,6 +909,7 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
lgr->net = smc_ib_net(lnk->smcibdev);
lgr_list = &smc_lgr_list.list;
lgr_lock = &smc_lgr_list.lock;
lgr->buf_type = lgr->net->smc.sysctl_smcr_buf_type;
atomic_inc(&lgr_cnt);
}
smc->conn.lgr = lgr;
......@@ -1086,34 +1089,37 @@ struct smc_link *smc_switch_conns(struct smc_link_group *lgr,
return NULL;
}
static void smcr_buf_unuse(struct smc_buf_desc *rmb_desc,
static void smcr_buf_unuse(struct smc_buf_desc *buf_desc, bool is_rmb,
struct smc_link_group *lgr)
{
struct mutex *lock; /* lock buffer list */
int rc;
if (rmb_desc->is_conf_rkey && !list_empty(&lgr->list)) {
if (is_rmb && buf_desc->is_conf_rkey && !list_empty(&lgr->list)) {
/* unregister rmb with peer */
rc = smc_llc_flow_initiate(lgr, SMC_LLC_FLOW_RKEY);
if (!rc) {
/* protect against smc_llc_cli_rkey_exchange() */
mutex_lock(&lgr->llc_conf_mutex);
smc_llc_do_delete_rkey(lgr, rmb_desc);
rmb_desc->is_conf_rkey = false;
smc_llc_do_delete_rkey(lgr, buf_desc);
buf_desc->is_conf_rkey = false;
mutex_unlock(&lgr->llc_conf_mutex);
smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
}
}
if (rmb_desc->is_reg_err) {
if (buf_desc->is_reg_err) {
/* buf registration failed, reuse not possible */
mutex_lock(&lgr->rmbs_lock);
list_del(&rmb_desc->list);
mutex_unlock(&lgr->rmbs_lock);
lock = is_rmb ? &lgr->rmbs_lock :
&lgr->sndbufs_lock;
mutex_lock(lock);
list_del(&buf_desc->list);
mutex_unlock(lock);
smc_buf_free(lgr, true, rmb_desc);
smc_buf_free(lgr, is_rmb, buf_desc);
} else {
rmb_desc->used = 0;
memset(rmb_desc->cpu_addr, 0, rmb_desc->len);
buf_desc->used = 0;
memset(buf_desc->cpu_addr, 0, buf_desc->len);
}
}
......@@ -1121,15 +1127,23 @@ static void smc_buf_unuse(struct smc_connection *conn,
struct smc_link_group *lgr)
{
if (conn->sndbuf_desc) {
if (!lgr->is_smcd && conn->sndbuf_desc->is_vm) {
smcr_buf_unuse(conn->sndbuf_desc, false, lgr);
} else {
conn->sndbuf_desc->used = 0;
memset(conn->sndbuf_desc->cpu_addr, 0, conn->sndbuf_desc->len);
memset(conn->sndbuf_desc->cpu_addr, 0,
conn->sndbuf_desc->len);
}
if (conn->rmb_desc && lgr->is_smcd) {
}
if (conn->rmb_desc) {
if (!lgr->is_smcd) {
smcr_buf_unuse(conn->rmb_desc, true, lgr);
} else {
conn->rmb_desc->used = 0;
memset(conn->rmb_desc->cpu_addr, 0, conn->rmb_desc->len +
memset(conn->rmb_desc->cpu_addr, 0,
conn->rmb_desc->len +
sizeof(struct smcd_cdc_msg));
} else if (conn->rmb_desc) {
smcr_buf_unuse(conn->rmb_desc, lgr);
}
}
}
......@@ -1177,20 +1191,21 @@ void smc_conn_free(struct smc_connection *conn)
static void smcr_buf_unmap_link(struct smc_buf_desc *buf_desc, bool is_rmb,
struct smc_link *lnk)
{
if (is_rmb)
if (is_rmb || buf_desc->is_vm)
buf_desc->is_reg_mr[lnk->link_idx] = false;
if (!buf_desc->is_map_ib[lnk->link_idx])
return;
if (is_rmb) {
if (buf_desc->mr_rx[lnk->link_idx]) {
smc_ib_put_memory_region(
buf_desc->mr_rx[lnk->link_idx]);
buf_desc->mr_rx[lnk->link_idx] = NULL;
if ((is_rmb || buf_desc->is_vm) &&
buf_desc->mr[lnk->link_idx]) {
smc_ib_put_memory_region(buf_desc->mr[lnk->link_idx]);
buf_desc->mr[lnk->link_idx] = NULL;
}
if (is_rmb)
smc_ib_buf_unmap_sg(lnk, buf_desc, DMA_FROM_DEVICE);
} else {
else
smc_ib_buf_unmap_sg(lnk, buf_desc, DMA_TO_DEVICE);
}
sg_free_table(&buf_desc->sgt[lnk->link_idx]);
buf_desc->is_map_ib[lnk->link_idx] = false;
}
......@@ -1279,8 +1294,10 @@ static void smcr_buf_free(struct smc_link_group *lgr, bool is_rmb,
for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
smcr_buf_unmap_link(buf_desc, is_rmb, &lgr->lnk[i]);
if (buf_desc->pages)
if (!buf_desc->is_vm && buf_desc->pages)
__free_pages(buf_desc->pages, buf_desc->order);
else if (buf_desc->is_vm && buf_desc->cpu_addr)
vfree(buf_desc->cpu_addr);
kfree(buf_desc);
}
......@@ -1992,39 +2009,69 @@ static inline int smc_rmb_wnd_update_limit(int rmbe_size)
return max_t(int, rmbe_size / 10, SOCK_MIN_SNDBUF / 2);
}
/* map an rmb buf to a link */
/* map an buf to a link */
static int smcr_buf_map_link(struct smc_buf_desc *buf_desc, bool is_rmb,
struct smc_link *lnk)
{
int rc;
int rc, i, nents, offset, buf_size, size, access_flags;
struct scatterlist *sg;
void *buf;
if (buf_desc->is_map_ib[lnk->link_idx])
return 0;
rc = sg_alloc_table(&buf_desc->sgt[lnk->link_idx], 1, GFP_KERNEL);
if (buf_desc->is_vm) {
buf = buf_desc->cpu_addr;
buf_size = buf_desc->len;
offset = offset_in_page(buf_desc->cpu_addr);
nents = PAGE_ALIGN(buf_size + offset) / PAGE_SIZE;
} else {
nents = 1;
}
rc = sg_alloc_table(&buf_desc->sgt[lnk->link_idx], nents, GFP_KERNEL);
if (rc)
return rc;
if (buf_desc->is_vm) {
/* virtually contiguous buffer */
for_each_sg(buf_desc->sgt[lnk->link_idx].sgl, sg, nents, i) {
size = min_t(int, PAGE_SIZE - offset, buf_size);
sg_set_page(sg, vmalloc_to_page(buf), size, offset);
buf += size / sizeof(*buf);
buf_size -= size;
offset = 0;
}
} else {
/* physically contiguous buffer */
sg_set_buf(buf_desc->sgt[lnk->link_idx].sgl,
buf_desc->cpu_addr, buf_desc->len);
}
/* map sg table to DMA address */
rc = smc_ib_buf_map_sg(lnk, buf_desc,
is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
/* SMC protocol depends on mapping to one DMA address only */
if (rc != 1) {
if (rc != nents) {
rc = -EAGAIN;
goto free_table;
}
/* create a new memory region for the RMB */
if (is_rmb) {
rc = smc_ib_get_memory_region(lnk->roce_pd,
IB_ACCESS_REMOTE_WRITE |
IB_ACCESS_LOCAL_WRITE,
buf_desc->is_dma_need_sync |=
smc_ib_is_sg_need_sync(lnk, buf_desc) << lnk->link_idx;
if (is_rmb || buf_desc->is_vm) {
/* create a new memory region for the RMB or vzalloced sndbuf */
access_flags = is_rmb ?
IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE :
IB_ACCESS_LOCAL_WRITE;
rc = smc_ib_get_memory_region(lnk->roce_pd, access_flags,
buf_desc, lnk->link_idx);
if (rc)
goto buf_unmap;
smc_ib_sync_sg_for_device(lnk, buf_desc, DMA_FROM_DEVICE);
smc_ib_sync_sg_for_device(lnk, buf_desc,
is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
buf_desc->is_map_ib[lnk->link_idx] = true;
return 0;
......@@ -2037,20 +2084,23 @@ static int smcr_buf_map_link(struct smc_buf_desc *buf_desc, bool is_rmb,
return rc;
}
/* register a new rmb on IB device,
/* register a new buf on IB device, rmb or vzalloced sndbuf
* must be called under lgr->llc_conf_mutex lock
*/
int smcr_link_reg_rmb(struct smc_link *link, struct smc_buf_desc *rmb_desc)
int smcr_link_reg_buf(struct smc_link *link, struct smc_buf_desc *buf_desc)
{
if (list_empty(&link->lgr->list))
return -ENOLINK;
if (!rmb_desc->is_reg_mr[link->link_idx]) {
/* register memory region for new rmb */
if (smc_wr_reg_send(link, rmb_desc->mr_rx[link->link_idx])) {
rmb_desc->is_reg_err = true;
if (!buf_desc->is_reg_mr[link->link_idx]) {
/* register memory region for new buf */
if (buf_desc->is_vm)
buf_desc->mr[link->link_idx]->iova =
(uintptr_t)buf_desc->cpu_addr;
if (smc_wr_reg_send(link, buf_desc->mr[link->link_idx])) {
buf_desc->is_reg_err = true;
return -EFAULT;
}
rmb_desc->is_reg_mr[link->link_idx] = true;
buf_desc->is_reg_mr[link->link_idx] = true;
}
return 0;
}
......@@ -2102,18 +2152,38 @@ int smcr_buf_reg_lgr(struct smc_link *lnk)
struct smc_buf_desc *buf_desc, *bf;
int i, rc = 0;
/* reg all RMBs for a new link */
mutex_lock(&lgr->rmbs_lock);
for (i = 0; i < SMC_RMBE_SIZES; i++) {
list_for_each_entry_safe(buf_desc, bf, &lgr->rmbs[i], list) {
if (!buf_desc->used)
continue;
rc = smcr_link_reg_rmb(lnk, buf_desc);
if (rc)
goto out;
rc = smcr_link_reg_buf(lnk, buf_desc);
if (rc) {
mutex_unlock(&lgr->rmbs_lock);
return rc;
}
}
}
out:
mutex_unlock(&lgr->rmbs_lock);
if (lgr->buf_type == SMCR_PHYS_CONT_BUFS)
return rc;
/* reg all vzalloced sndbufs for a new link */
mutex_lock(&lgr->sndbufs_lock);
for (i = 0; i < SMC_RMBE_SIZES; i++) {
list_for_each_entry_safe(buf_desc, bf, &lgr->sndbufs[i], list) {
if (!buf_desc->used || !buf_desc->is_vm)
continue;
rc = smcr_link_reg_buf(lnk, buf_desc);
if (rc) {
mutex_unlock(&lgr->sndbufs_lock);
return rc;
}
}
}
mutex_unlock(&lgr->sndbufs_lock);
return rc;
}
......@@ -2127,18 +2197,39 @@ static struct smc_buf_desc *smcr_new_buf_create(struct smc_link_group *lgr,
if (!buf_desc)
return ERR_PTR(-ENOMEM);
switch (lgr->buf_type) {
case SMCR_PHYS_CONT_BUFS:
case SMCR_MIXED_BUFS:
buf_desc->order = get_order(bufsize);
buf_desc->pages = alloc_pages(GFP_KERNEL | __GFP_NOWARN |
__GFP_NOMEMALLOC | __GFP_COMP |
__GFP_NORETRY | __GFP_ZERO,
buf_desc->order);
if (!buf_desc->pages) {
kfree(buf_desc);
return ERR_PTR(-EAGAIN);
if (buf_desc->pages) {
buf_desc->cpu_addr =
(void *)page_address(buf_desc->pages);
buf_desc->len = bufsize;
buf_desc->is_vm = false;
break;
}
buf_desc->cpu_addr = (void *)page_address(buf_desc->pages);
if (lgr->buf_type == SMCR_PHYS_CONT_BUFS)
goto out;
fallthrough; // try virtually continguous buf
case SMCR_VIRT_CONT_BUFS:
buf_desc->order = get_order(bufsize);
buf_desc->cpu_addr = vzalloc(PAGE_SIZE << buf_desc->order);
if (!buf_desc->cpu_addr)
goto out;
buf_desc->pages = NULL;
buf_desc->len = bufsize;
buf_desc->is_vm = true;
break;
}
return buf_desc;
out:
kfree(buf_desc);
return ERR_PTR(-EAGAIN);
}
/* map buf_desc on all usable links,
......@@ -2234,6 +2325,7 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)
/* check for reusable slot in the link group */
buf_desc = smc_buf_get_slot(bufsize_short, lock, buf_list);
if (buf_desc) {
buf_desc->is_dma_need_sync = 0;
SMC_STAT_RMB_SIZE(smc, is_smcd, is_rmb, bufsize);
SMC_STAT_BUF_REUSE(smc, is_smcd, is_rmb);
break; /* found reusable slot */
......@@ -2268,7 +2360,7 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)
if (!is_smcd) {
if (smcr_buf_map_usable_links(lgr, buf_desc, is_rmb)) {
smcr_buf_unuse(buf_desc, lgr);
smcr_buf_unuse(buf_desc, is_rmb, lgr);
return -ENOMEM;
}
}
......@@ -2290,16 +2382,10 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)
return 0;
}
void smc_sndbuf_sync_sg_for_cpu(struct smc_connection *conn)
{
if (!smc_conn_lgr_valid(conn) || 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->sndbuf_desc->is_dma_need_sync)
return;
if (!smc_conn_lgr_valid(conn) || conn->lgr->is_smcd ||
!smc_link_active(conn->lnk))
return;
......@@ -2310,26 +2396,14 @@ void smc_rmb_sync_sg_for_cpu(struct smc_connection *conn)
{
int i;
if (!smc_conn_lgr_valid(conn) || conn->lgr->is_smcd)
if (!conn->rmb_desc->is_dma_need_sync)
return;
for (i = 0; i < SMC_LINKS_PER_LGR_MAX; 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);
}
}
void smc_rmb_sync_sg_for_device(struct smc_connection *conn)
{
int i;
if (!smc_conn_lgr_valid(conn) || conn->lgr->is_smcd)
return;
for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
if (!smc_link_active(&conn->lgr->lnk[i]))
continue;
smc_ib_sync_sg_for_device(&conn->lgr->lnk[i], conn->rmb_desc,
smc_ib_sync_sg_for_cpu(&conn->lgr->lnk[i], conn->rmb_desc,
DMA_FROM_DEVICE);
}
}
......
......@@ -168,9 +168,11 @@ struct smc_buf_desc {
struct { /* SMC-R */
struct sg_table sgt[SMC_LINKS_PER_LGR_MAX];
/* virtual buffer */
struct ib_mr *mr_rx[SMC_LINKS_PER_LGR_MAX];
/* for rmb only: memory region
struct ib_mr *mr[SMC_LINKS_PER_LGR_MAX];
/* memory region: for rmb and
* vzalloced sndbuf
* incl. rkey provided to peer
* and lkey provided to local
*/
u32 order; /* allocation order */
......@@ -180,8 +182,11 @@ struct smc_buf_desc {
/* mem region registered */
u8 is_map_ib[SMC_LINKS_PER_LGR_MAX];
/* mem region mapped to lnk */
u8 is_dma_need_sync;
u8 is_reg_err;
/* buffer registration err */
u8 is_vm;
/* virtually contiguous */
};
struct { /* SMC-D */
unsigned short sba_idx;
......@@ -216,6 +221,12 @@ enum smc_lgr_type { /* redundancy state of lgr */
SMC_LGR_ASYMMETRIC_LOCAL, /* local has 1, peer 2 active RNICs */
};
enum smcr_buf_type { /* types of SMC-R sndbufs and RMBs */
SMCR_PHYS_CONT_BUFS = 0,
SMCR_VIRT_CONT_BUFS = 1,
SMCR_MIXED_BUFS = 2,
};
enum smc_llc_flowtype {
SMC_LLC_FLOW_NONE = 0,
SMC_LLC_FLOW_ADD_LINK = 2,
......@@ -277,6 +288,7 @@ struct smc_link_group {
/* used rtoken elements */
u8 next_link_id;
enum smc_lgr_type type;
enum smcr_buf_type buf_type;
/* redundancy state */
u8 pnet_id[SMC_MAX_PNETID_LEN + 1];
/* pnet id of this lgr */
......@@ -513,10 +525,8 @@ void smc_rtoken_set(struct smc_link_group *lgr, int link_idx, int link_idx_new,
__be32 nw_rkey_known, __be64 nw_vaddr, __be32 nw_rkey);
void smc_rtoken_set2(struct smc_link_group *lgr, int rtok_idx, int link_id,
__be64 nw_vaddr, __be32 nw_rkey);
void smc_sndbuf_sync_sg_for_cpu(struct smc_connection *conn);
void smc_sndbuf_sync_sg_for_device(struct smc_connection *conn);
void smc_rmb_sync_sg_for_cpu(struct smc_connection *conn);
void smc_rmb_sync_sg_for_device(struct smc_connection *conn);
int smc_vlan_by_tcpsk(struct socket *clcsock, struct smc_init_info *ini);
void smc_conn_free(struct smc_connection *conn);
......@@ -537,7 +547,7 @@ int smcr_buf_reg_lgr(struct smc_link *lnk);
void smcr_lgr_set_type(struct smc_link_group *lgr, enum smc_lgr_type new_type);
void smcr_lgr_set_type_asym(struct smc_link_group *lgr,
enum smc_lgr_type new_type, int asym_lnk_idx);
int smcr_link_reg_rmb(struct smc_link *link, struct smc_buf_desc *rmb_desc);
int smcr_link_reg_buf(struct smc_link *link, struct smc_buf_desc *rmb_desc);
struct smc_link *smc_switch_conns(struct smc_link_group *lgr,
struct smc_link *from_lnk, bool is_dev_err);
void smcr_link_down_cond(struct smc_link *lnk);
......
......@@ -698,7 +698,7 @@ static int smc_ib_map_mr_sg(struct smc_buf_desc *buf_slot, u8 link_idx)
int sg_num;
/* map the largest prefix of a dma mapped SG list */
sg_num = ib_map_mr_sg(buf_slot->mr_rx[link_idx],
sg_num = ib_map_mr_sg(buf_slot->mr[link_idx],
buf_slot->sgt[link_idx].sgl,
buf_slot->sgt[link_idx].orig_nents,
&offset, PAGE_SIZE);
......@@ -710,25 +710,49 @@ static int smc_ib_map_mr_sg(struct smc_buf_desc *buf_slot, u8 link_idx)
int smc_ib_get_memory_region(struct ib_pd *pd, int access_flags,
struct smc_buf_desc *buf_slot, u8 link_idx)
{
if (buf_slot->mr_rx[link_idx])
if (buf_slot->mr[link_idx])
return 0; /* already done */
buf_slot->mr_rx[link_idx] =
buf_slot->mr[link_idx] =
ib_alloc_mr(pd, IB_MR_TYPE_MEM_REG, 1 << buf_slot->order);
if (IS_ERR(buf_slot->mr_rx[link_idx])) {
if (IS_ERR(buf_slot->mr[link_idx])) {
int rc;
rc = PTR_ERR(buf_slot->mr_rx[link_idx]);
buf_slot->mr_rx[link_idx] = NULL;
rc = PTR_ERR(buf_slot->mr[link_idx]);
buf_slot->mr[link_idx] = NULL;
return rc;
}
if (smc_ib_map_mr_sg(buf_slot, link_idx) != 1)
if (smc_ib_map_mr_sg(buf_slot, link_idx) !=
buf_slot->sgt[link_idx].orig_nents)
return -EINVAL;
return 0;
}
bool smc_ib_is_sg_need_sync(struct smc_link *lnk,
struct smc_buf_desc *buf_slot)
{
struct scatterlist *sg;
unsigned int i;
bool ret = false;
/* for now there is just one DMA address */
for_each_sg(buf_slot->sgt[lnk->link_idx].sgl, sg,
buf_slot->sgt[lnk->link_idx].nents, i) {
if (!sg_dma_len(sg))
break;
if (dma_need_sync(lnk->smcibdev->ibdev->dma_device,
sg_dma_address(sg))) {
ret = true;
goto out;
}
}
out:
return ret;
}
/* synchronize buffer usage for cpu access */
void smc_ib_sync_sg_for_cpu(struct smc_link *lnk,
struct smc_buf_desc *buf_slot,
......@@ -737,6 +761,9 @@ void smc_ib_sync_sg_for_cpu(struct smc_link *lnk,
struct scatterlist *sg;
unsigned int i;
if (!(buf_slot->is_dma_need_sync & (1U << lnk->link_idx)))
return;
/* for now there is just one DMA address */
for_each_sg(buf_slot->sgt[lnk->link_idx].sgl, sg,
buf_slot->sgt[lnk->link_idx].nents, i) {
......@@ -757,6 +784,9 @@ void smc_ib_sync_sg_for_device(struct smc_link *lnk,
struct scatterlist *sg;
unsigned int i;
if (!(buf_slot->is_dma_need_sync & (1U << lnk->link_idx)))
return;
/* for now there is just one DMA address */
for_each_sg(buf_slot->sgt[lnk->link_idx].sgl, sg,
buf_slot->sgt[lnk->link_idx].nents, i) {
......
......@@ -102,6 +102,8 @@ long smc_ib_setup_per_ibdev(struct smc_ib_device *smcibdev);
int smc_ib_get_memory_region(struct ib_pd *pd, int access_flags,
struct smc_buf_desc *buf_slot, u8 link_idx);
void smc_ib_put_memory_region(struct ib_mr *mr);
bool smc_ib_is_sg_need_sync(struct smc_link *lnk,
struct smc_buf_desc *buf_slot);
void smc_ib_sync_sg_for_cpu(struct smc_link *lnk,
struct smc_buf_desc *buf_slot,
enum dma_data_direction data_direction);
......
......@@ -505,19 +505,22 @@ static int smc_llc_send_confirm_rkey(struct smc_link *send_link,
if (smc_link_active(link) && link != send_link) {
rkeyllc->rtoken[rtok_ix].link_id = link->link_id;
rkeyllc->rtoken[rtok_ix].rmb_key =
htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
rkeyllc->rtoken[rtok_ix].rmb_vaddr = cpu_to_be64(
(u64)sg_dma_address(
rmb_desc->sgt[link->link_idx].sgl));
htonl(rmb_desc->mr[link->link_idx]->rkey);
rkeyllc->rtoken[rtok_ix].rmb_vaddr = rmb_desc->is_vm ?
cpu_to_be64((uintptr_t)rmb_desc->cpu_addr) :
cpu_to_be64((u64)sg_dma_address
(rmb_desc->sgt[link->link_idx].sgl));
rtok_ix++;
}
}
/* rkey of send_link is in rtoken[0] */
rkeyllc->rtoken[0].num_rkeys = rtok_ix - 1;
rkeyllc->rtoken[0].rmb_key =
htonl(rmb_desc->mr_rx[send_link->link_idx]->rkey);
rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64(
(u64)sg_dma_address(rmb_desc->sgt[send_link->link_idx].sgl));
htonl(rmb_desc->mr[send_link->link_idx]->rkey);
rkeyllc->rtoken[0].rmb_vaddr = rmb_desc->is_vm ?
cpu_to_be64((uintptr_t)rmb_desc->cpu_addr) :
cpu_to_be64((u64)sg_dma_address
(rmb_desc->sgt[send_link->link_idx].sgl));
/* send llc message */
rc = smc_wr_tx_send(send_link, pend);
put_out:
......@@ -544,7 +547,7 @@ static int smc_llc_send_delete_rkey(struct smc_link *link,
rkeyllc->hd.common.llc_type = SMC_LLC_DELETE_RKEY;
smc_llc_init_msg_hdr(&rkeyllc->hd, link->lgr, sizeof(*rkeyllc));
rkeyllc->num_rkeys = 1;
rkeyllc->rkey[0] = htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
rkeyllc->rkey[0] = htonl(rmb_desc->mr[link->link_idx]->rkey);
/* send llc message */
rc = smc_wr_tx_send(link, pend);
put_out:
......@@ -614,9 +617,10 @@ static int smc_llc_fill_ext_v2(struct smc_llc_msg_add_link_v2_ext *ext,
if (!buf_pos)
break;
rmb = buf_pos;
ext->rt[i].rmb_key = htonl(rmb->mr_rx[prim_lnk_idx]->rkey);
ext->rt[i].rmb_key_new = htonl(rmb->mr_rx[lnk_idx]->rkey);
ext->rt[i].rmb_vaddr_new =
ext->rt[i].rmb_key = htonl(rmb->mr[prim_lnk_idx]->rkey);
ext->rt[i].rmb_key_new = htonl(rmb->mr[lnk_idx]->rkey);
ext->rt[i].rmb_vaddr_new = rmb->is_vm ?
cpu_to_be64((uintptr_t)rmb->cpu_addr) :
cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl));
buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos);
while (buf_pos && !(buf_pos)->used)
......@@ -852,9 +856,10 @@ static int smc_llc_add_link_cont(struct smc_link *link,
}
rmb = *buf_pos;
addc_llc->rt[i].rmb_key = htonl(rmb->mr_rx[prim_lnk_idx]->rkey);
addc_llc->rt[i].rmb_key_new = htonl(rmb->mr_rx[lnk_idx]->rkey);
addc_llc->rt[i].rmb_vaddr_new =
addc_llc->rt[i].rmb_key = htonl(rmb->mr[prim_lnk_idx]->rkey);
addc_llc->rt[i].rmb_key_new = htonl(rmb->mr[lnk_idx]->rkey);
addc_llc->rt[i].rmb_vaddr_new = rmb->is_vm ?
cpu_to_be64((uintptr_t)rmb->cpu_addr) :
cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl));
(*num_rkeys_todo)--;
......
......@@ -145,35 +145,93 @@ static void smc_rx_spd_release(struct splice_pipe_desc *spd,
static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
struct smc_sock *smc)
{
struct smc_link_group *lgr = smc->conn.lgr;
int offset = offset_in_page(src);
struct partial_page *partial;
struct splice_pipe_desc spd;
struct partial_page partial;
struct smc_spd_priv *priv;
int bytes;
struct smc_spd_priv **priv;
struct page **pages;
int bytes, nr_pages;
int i;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
nr_pages = !lgr->is_smcd && smc->conn.rmb_desc->is_vm ?
PAGE_ALIGN(len + offset) / PAGE_SIZE : 1;
pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL);
if (!pages)
goto out;
partial = kcalloc(nr_pages, sizeof(*partial), GFP_KERNEL);
if (!partial)
goto out_page;
priv = kcalloc(nr_pages, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->len = len;
priv->smc = smc;
partial.offset = src - (char *)smc->conn.rmb_desc->cpu_addr;
partial.len = len;
partial.private = (unsigned long)priv;
spd.nr_pages_max = 1;
spd.nr_pages = 1;
spd.pages = &smc->conn.rmb_desc->pages;
spd.partial = &partial;
goto out_part;
for (i = 0; i < nr_pages; i++) {
priv[i] = kzalloc(sizeof(**priv), GFP_KERNEL);
if (!priv[i])
goto out_priv;
}
if (lgr->is_smcd ||
(!lgr->is_smcd && !smc->conn.rmb_desc->is_vm)) {
/* smcd or smcr that uses physically contiguous RMBs */
priv[0]->len = len;
priv[0]->smc = smc;
partial[0].offset = src - (char *)smc->conn.rmb_desc->cpu_addr;
partial[0].len = len;
partial[0].private = (unsigned long)priv[0];
pages[0] = smc->conn.rmb_desc->pages;
} else {
int size, left = len;
void *buf = src;
/* smcr that uses virtually contiguous RMBs*/
for (i = 0; i < nr_pages; i++) {
size = min_t(int, PAGE_SIZE - offset, left);
priv[i]->len = size;
priv[i]->smc = smc;
pages[i] = vmalloc_to_page(buf);
partial[i].offset = offset;
partial[i].len = size;
partial[i].private = (unsigned long)priv[i];
buf += size / sizeof(*buf);
left -= size;
offset = 0;
}
}
spd.nr_pages_max = nr_pages;
spd.nr_pages = nr_pages;
spd.pages = pages;
spd.partial = partial;
spd.ops = &smc_pipe_ops;
spd.spd_release = smc_rx_spd_release;
bytes = splice_to_pipe(pipe, &spd);
if (bytes > 0) {
sock_hold(&smc->sk);
if (!lgr->is_smcd && smc->conn.rmb_desc->is_vm) {
for (i = 0; i < PAGE_ALIGN(bytes + offset) / PAGE_SIZE; i++)
get_page(pages[i]);
} else {
get_page(smc->conn.rmb_desc->pages);
}
atomic_add(bytes, &smc->conn.splice_pending);
}
kfree(priv);
kfree(partial);
kfree(pages);
return bytes;
out_priv:
for (i = (i - 1); i >= 0; i--)
kfree(priv[i]);
kfree(priv);
out_part:
kfree(partial);
out_page:
kfree(pages);
out:
return -ENOMEM;
}
static int smc_rx_data_available_and_no_splice_pend(struct smc_connection *conn)
......@@ -413,7 +471,6 @@ int smc_rx_recvmsg(struct smc_sock *smc, struct msghdr *msg,
if (rc < 0) {
if (!read_done)
read_done = -EFAULT;
smc_rmb_sync_sg_for_device(conn);
goto out;
}
}
......@@ -427,7 +484,6 @@ int smc_rx_recvmsg(struct smc_sock *smc, struct msghdr *msg,
chunk_len_sum += chunk_len;
chunk_off = 0; /* modulo offset in recv ring buffer */
}
smc_rmb_sync_sg_for_device(conn);
/* update cursors */
if (!(flags & MSG_PEEK)) {
......
......@@ -15,6 +15,7 @@
#include <net/net_namespace.h>
#include "smc.h"
#include "smc_core.h"
#include "smc_sysctl.h"
static struct ctl_table smc_table[] = {
......@@ -25,6 +26,15 @@ static struct ctl_table smc_table[] = {
.mode = 0644,
.proc_handler = proc_douintvec,
},
{
.procname = "smcr_buf_type",
.data = &init_net.smc.sysctl_smcr_buf_type,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_douintvec_minmax,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_TWO,
},
{ }
};
......@@ -49,6 +59,7 @@ int __net_init smc_sysctl_net_init(struct net *net)
goto err_reg;
net->smc.sysctl_autocorking_size = SMC_AUTOCORKING_DEFAULT_SIZE;
net->smc.sysctl_smcr_buf_type = SMCR_PHYS_CONT_BUFS;
return 0;
......
......@@ -246,7 +246,6 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len)
tx_cnt_prep);
chunk_len_sum = chunk_len;
chunk_off = tx_cnt_prep;
smc_sndbuf_sync_sg_for_cpu(conn);
for (chunk = 0; chunk < 2; chunk++) {
rc = memcpy_from_msg(sndbuf_base + chunk_off,
msg, chunk_len);
......@@ -384,6 +383,7 @@ static int smcr_tx_rdma_writes(struct smc_connection *conn, size_t len,
dma_addr_t dma_addr =
sg_dma_address(conn->sndbuf_desc->sgt[link->link_idx].sgl);
u64 virt_addr = (uintptr_t)conn->sndbuf_desc->cpu_addr;
int src_len_sum = src_len, dst_len_sum = dst_len;
int sent_count = src_off;
int srcchunk, dstchunk;
......@@ -396,7 +396,7 @@ static int smcr_tx_rdma_writes(struct smc_connection *conn, size_t len,
u64 base_addr = dma_addr;
if (dst_len < link->qp_attr.cap.max_inline_data) {
base_addr = (uintptr_t)conn->sndbuf_desc->cpu_addr;
base_addr = virt_addr;
wr->wr.send_flags |= IB_SEND_INLINE;
} else {
wr->wr.send_flags &= ~IB_SEND_INLINE;
......@@ -404,8 +404,12 @@ static int smcr_tx_rdma_writes(struct smc_connection *conn, size_t len,
num_sges = 0;
for (srcchunk = 0; srcchunk < 2; srcchunk++) {
sge[srcchunk].addr = base_addr + src_off;
sge[srcchunk].addr = conn->sndbuf_desc->is_vm ?
(virt_addr + src_off) : (base_addr + src_off);
sge[srcchunk].length = src_len;
if (conn->sndbuf_desc->is_vm)
sge[srcchunk].lkey =
conn->sndbuf_desc->mr[link->link_idx]->lkey;
num_sges++;
src_off += src_len;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment