aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2018-05-01 11:37:14 -0400
committerAnna Schumaker <Anna.Schumaker@Netapp.com>2018-05-01 13:29:43 -0400
commit054f155721d7af1f343ed52bea246626d8450ca8 (patch)
tree9ab5642f2453d5f73b8603a6cf2059803c715d23
parent98de9ce6f6660d02aa72d7b9b17696fa68a2ed9b (diff)
xprtrdma: Fix list corruption / DMAR errors during MR recovery
The ro_release_mr methods check whether mr->mr_list is empty. Therefore, be sure to always use list_del_init when removing an MR linked into a list using that field. Otherwise, when recovering from transport failures or device removal, list corruption can result, or MRs can get mapped or unmapped an odd number of times, resulting in IOMMU-related failures. In general this fix is appropriate back to v4.8. However, code changes since then make it impossible to apply this patch directly to stable kernels. The fix would have to be applied by hand or reworked for kernels earlier than v4.16. Backport guidance -- there are several cases: - When creating an MR, initialize mr_list so that using list_empty on an as-yet-unused MR is safe. - When an MR is being handled by the remote invalidation path, ensure that mr_list is reinitialized when it is removed from rl_registered. - When an MR is being handled by rpcrdma_destroy_mrs, it is removed from mr_all, but it may still be on an rl_registered list. In that case, the MR needs to be removed from that list before being released. - Other cases are covered by using list_del_init in rpcrdma_mr_pop. Fixes: 9d6b04097882 ('xprtrdma: Place registered MWs on a ... ') Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
-rw-r--r--net/sunrpc/xprtrdma/fmr_ops.c5
-rw-r--r--net/sunrpc/xprtrdma/frwr_ops.c9
-rw-r--r--net/sunrpc/xprtrdma/verbs.c5
-rw-r--r--net/sunrpc/xprtrdma/xprt_rdma.h2
4 files changed, 10 insertions, 11 deletions
diff --git a/net/sunrpc/xprtrdma/fmr_ops.c b/net/sunrpc/xprtrdma/fmr_ops.c
index 5cc68a824f45..f2f63959fddd 100644
--- a/net/sunrpc/xprtrdma/fmr_ops.c
+++ b/net/sunrpc/xprtrdma/fmr_ops.c
@@ -72,6 +72,7 @@ fmr_op_init_mr(struct rpcrdma_ia *ia, struct rpcrdma_mr *mr)
72 if (IS_ERR(mr->fmr.fm_mr)) 72 if (IS_ERR(mr->fmr.fm_mr))
73 goto out_fmr_err; 73 goto out_fmr_err;
74 74
75 INIT_LIST_HEAD(&mr->mr_list);
75 return 0; 76 return 0;
76 77
77out_fmr_err: 78out_fmr_err:
@@ -102,10 +103,6 @@ fmr_op_release_mr(struct rpcrdma_mr *mr)
102 LIST_HEAD(unmap_list); 103 LIST_HEAD(unmap_list);
103 int rc; 104 int rc;
104 105
105 /* Ensure MW is not on any rl_registered list */
106 if (!list_empty(&mr->mr_list))
107 list_del(&mr->mr_list);
108
109 kfree(mr->fmr.fm_physaddrs); 106 kfree(mr->fmr.fm_physaddrs);
110 kfree(mr->mr_sg); 107 kfree(mr->mr_sg);
111 108
diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c
index c5743a0960be..c59c5c788db0 100644
--- a/net/sunrpc/xprtrdma/frwr_ops.c
+++ b/net/sunrpc/xprtrdma/frwr_ops.c
@@ -110,6 +110,7 @@ frwr_op_init_mr(struct rpcrdma_ia *ia, struct rpcrdma_mr *mr)
110 if (!mr->mr_sg) 110 if (!mr->mr_sg)
111 goto out_list_err; 111 goto out_list_err;
112 112
113 INIT_LIST_HEAD(&mr->mr_list);
113 sg_init_table(mr->mr_sg, depth); 114 sg_init_table(mr->mr_sg, depth);
114 init_completion(&frwr->fr_linv_done); 115 init_completion(&frwr->fr_linv_done);
115 return 0; 116 return 0;
@@ -133,10 +134,6 @@ frwr_op_release_mr(struct rpcrdma_mr *mr)
133{ 134{
134 int rc; 135 int rc;
135 136
136 /* Ensure MR is not on any rl_registered list */
137 if (!list_empty(&mr->mr_list))
138 list_del(&mr->mr_list);
139
140 rc = ib_dereg_mr(mr->frwr.fr_mr); 137 rc = ib_dereg_mr(mr->frwr.fr_mr);
141 if (rc) 138 if (rc)
142 pr_err("rpcrdma: final ib_dereg_mr for %p returned %i\n", 139 pr_err("rpcrdma: final ib_dereg_mr for %p returned %i\n",
@@ -195,7 +192,7 @@ frwr_op_recover_mr(struct rpcrdma_mr *mr)
195 return; 192 return;
196 193
197out_release: 194out_release:
198 pr_err("rpcrdma: FRWR reset failed %d, %p release\n", rc, mr); 195 pr_err("rpcrdma: FRWR reset failed %d, %p released\n", rc, mr);
199 r_xprt->rx_stats.mrs_orphaned++; 196 r_xprt->rx_stats.mrs_orphaned++;
200 197
201 spin_lock(&r_xprt->rx_buf.rb_mrlock); 198 spin_lock(&r_xprt->rx_buf.rb_mrlock);
@@ -476,7 +473,7 @@ frwr_op_reminv(struct rpcrdma_rep *rep, struct list_head *mrs)
476 473
477 list_for_each_entry(mr, mrs, mr_list) 474 list_for_each_entry(mr, mrs, mr_list)
478 if (mr->mr_handle == rep->rr_inv_rkey) { 475 if (mr->mr_handle == rep->rr_inv_rkey) {
479 list_del(&mr->mr_list); 476 list_del_init(&mr->mr_list);
480 trace_xprtrdma_remoteinv(mr); 477 trace_xprtrdma_remoteinv(mr);
481 mr->frwr.fr_state = FRWR_IS_INVALID; 478 mr->frwr.fr_state = FRWR_IS_INVALID;
482 rpcrdma_mr_unmap_and_put(mr); 479 rpcrdma_mr_unmap_and_put(mr);
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index fe5eaca2d197..c345d365af88 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -1254,6 +1254,11 @@ rpcrdma_mrs_destroy(struct rpcrdma_buffer *buf)
1254 list_del(&mr->mr_all); 1254 list_del(&mr->mr_all);
1255 1255
1256 spin_unlock(&buf->rb_mrlock); 1256 spin_unlock(&buf->rb_mrlock);
1257
1258 /* Ensure MW is not on any rl_registered list */
1259 if (!list_empty(&mr->mr_list))
1260 list_del(&mr->mr_list);
1261
1257 ia->ri_ops->ro_release_mr(mr); 1262 ia->ri_ops->ro_release_mr(mr);
1258 count++; 1263 count++;
1259 spin_lock(&buf->rb_mrlock); 1264 spin_lock(&buf->rb_mrlock);
diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h
index 3d3b423fa9c1..cb41b12a3bf8 100644
--- a/net/sunrpc/xprtrdma/xprt_rdma.h
+++ b/net/sunrpc/xprtrdma/xprt_rdma.h
@@ -380,7 +380,7 @@ rpcrdma_mr_pop(struct list_head *list)
380 struct rpcrdma_mr *mr; 380 struct rpcrdma_mr *mr;
381 381
382 mr = list_first_entry(list, struct rpcrdma_mr, mr_list); 382 mr = list_first_entry(list, struct rpcrdma_mr, mr_list);
383 list_del(&mr->mr_list); 383 list_del_init(&mr->mr_list);
384 return mr; 384 return mr;
385} 385}
386 386