Commit 9f9d802a authored by Chuck Lever's avatar Chuck Lever Committed by Anna Schumaker

xprtrdma: Reset FRMRs when FAST_REG_MR is flushed by a disconnect

FAST_REG_MR Work Requests update a Memory Region's rkey. Rkey's are
used to block unwanted access to the memory controlled by an MR. The
rkey is passed to the receiver (the NFS server, in our case), and is
also used by xprtrdma to invalidate the MR when the RPC is complete.

When a FAST_REG_MR Work Request is flushed after a transport
disconnect, xprtrdma cannot tell whether the WR actually hit the
adapter or not. So it is indeterminant at that point whether the
existing rkey is still valid.

After the transport connection is re-established, the next
FAST_REG_MR or LOCAL_INV Work Request against that MR can sometimes
fail because the rkey value does not match what xprtrdma expects.

The only reliable way to recover in this case is to deregister and
register the MR before it is used again. These operations can be
done only in a process context, so handle it in the transport
connect worker.
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Tested-by: default avatarSteve Wise <swise@opengridcomputing.com>
Tested-by: default avatarShirley Ma <shirley.ma@oracle.com>
Tested-by: default avatarDevesh Sharma <devesh.sharma@emulex.com>
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent c2922c02
...@@ -61,6 +61,8 @@ ...@@ -61,6 +61,8 @@
# define RPCDBG_FACILITY RPCDBG_TRANS # define RPCDBG_FACILITY RPCDBG_TRANS
#endif #endif
static void rpcrdma_reset_frmrs(struct rpcrdma_ia *);
/* /*
* internal functions * internal functions
*/ */
...@@ -152,8 +154,10 @@ rpcrdma_sendcq_process_wc(struct ib_wc *wc) ...@@ -152,8 +154,10 @@ rpcrdma_sendcq_process_wc(struct ib_wc *wc)
if (wc->wr_id == 0ULL) if (wc->wr_id == 0ULL)
return; return;
if (wc->status != IB_WC_SUCCESS) if (wc->status != IB_WC_SUCCESS) {
frmr->r.frmr.fr_state = FRMR_IS_STALE;
return; return;
}
if (wc->opcode == IB_WC_FAST_REG_MR) if (wc->opcode == IB_WC_FAST_REG_MR)
frmr->r.frmr.fr_state = FRMR_IS_VALID; frmr->r.frmr.fr_state = FRMR_IS_VALID;
...@@ -881,6 +885,9 @@ rpcrdma_ep_connect(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia) ...@@ -881,6 +885,9 @@ rpcrdma_ep_connect(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia)
" status %i\n", __func__, rc); " status %i\n", __func__, rc);
rpcrdma_flush_cqs(ep); rpcrdma_flush_cqs(ep);
if (ia->ri_memreg_strategy == RPCRDMA_FRMR)
rpcrdma_reset_frmrs(ia);
xprt = container_of(ia, struct rpcrdma_xprt, rx_ia); xprt = container_of(ia, struct rpcrdma_xprt, rx_ia);
id = rpcrdma_create_id(xprt, ia, id = rpcrdma_create_id(xprt, ia,
(struct sockaddr *)&xprt->rx_data.addr); (struct sockaddr *)&xprt->rx_data.addr);
...@@ -1256,6 +1263,62 @@ rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf) ...@@ -1256,6 +1263,62 @@ rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf)
kfree(buf->rb_pool); kfree(buf->rb_pool);
} }
/* After a disconnect, a flushed FAST_REG_MR can leave an FRMR in
* an unusable state. Find FRMRs in this state and dereg / reg
* each. FRMRs that are VALID and attached to an rpcrdma_req are
* also torn down.
*
* This gives all in-use FRMRs a fresh rkey and leaves them INVALID.
*
* This is invoked only in the transport connect worker in order
* to serialize with rpcrdma_register_frmr_external().
*/
static void
rpcrdma_reset_frmrs(struct rpcrdma_ia *ia)
{
struct rpcrdma_xprt *r_xprt =
container_of(ia, struct rpcrdma_xprt, rx_ia);
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
struct list_head *pos;
struct rpcrdma_mw *r;
int rc;
list_for_each(pos, &buf->rb_all) {
r = list_entry(pos, struct rpcrdma_mw, mw_all);
if (r->r.frmr.fr_state == FRMR_IS_INVALID)
continue;
rc = ib_dereg_mr(r->r.frmr.fr_mr);
if (rc)
dprintk("RPC: %s: ib_dereg_mr failed %i\n",
__func__, rc);
ib_free_fast_reg_page_list(r->r.frmr.fr_pgl);
r->r.frmr.fr_mr = ib_alloc_fast_reg_mr(ia->ri_pd,
ia->ri_max_frmr_depth);
if (IS_ERR(r->r.frmr.fr_mr)) {
rc = PTR_ERR(r->r.frmr.fr_mr);
dprintk("RPC: %s: ib_alloc_fast_reg_mr"
" failed %i\n", __func__, rc);
continue;
}
r->r.frmr.fr_pgl = ib_alloc_fast_reg_page_list(
ia->ri_id->device,
ia->ri_max_frmr_depth);
if (IS_ERR(r->r.frmr.fr_pgl)) {
rc = PTR_ERR(r->r.frmr.fr_pgl);
dprintk("RPC: %s: "
"ib_alloc_fast_reg_page_list "
"failed %i\n", __func__, rc);
ib_dereg_mr(r->r.frmr.fr_mr);
continue;
}
r->r.frmr.fr_state = FRMR_IS_INVALID;
}
}
/* "*mw" can be NULL when rpcrdma_buffer_get_mrs() fails, leaving /* "*mw" can be NULL when rpcrdma_buffer_get_mrs() fails, leaving
* some req segments uninitialized. * some req segments uninitialized.
*/ */
...@@ -1575,7 +1638,7 @@ rpcrdma_register_frmr_external(struct rpcrdma_mr_seg *seg, ...@@ -1575,7 +1638,7 @@ rpcrdma_register_frmr_external(struct rpcrdma_mr_seg *seg,
dprintk("RPC: %s: Using frmr %p to map %d segments\n", dprintk("RPC: %s: Using frmr %p to map %d segments\n",
__func__, mw, i); __func__, mw, i);
if (unlikely(frmr->fr_state == FRMR_IS_VALID)) { if (unlikely(frmr->fr_state != FRMR_IS_INVALID)) {
dprintk("RPC: %s: frmr %x left valid, posting invalidate.\n", dprintk("RPC: %s: frmr %x left valid, posting invalidate.\n",
__func__, mr->rkey); __func__, mr->rkey);
/* Invalidate before using. */ /* Invalidate before using. */
......
...@@ -161,6 +161,7 @@ struct rpcrdma_rep { ...@@ -161,6 +161,7 @@ struct rpcrdma_rep {
enum rpcrdma_frmr_state { enum rpcrdma_frmr_state {
FRMR_IS_INVALID, /* ready to be used */ FRMR_IS_INVALID, /* ready to be used */
FRMR_IS_VALID, /* in use */ FRMR_IS_VALID, /* in use */
FRMR_IS_STALE, /* failed completion */
}; };
struct rpcrdma_frmr { struct rpcrdma_frmr {
......
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