diff options
author | Eli Cohen <eli@dev.mellanox.co.il> | 2012-01-03 23:36:48 -0500 |
---|---|---|
committer | Roland Dreier <roland@purestorage.com> | 2012-01-03 23:36:48 -0500 |
commit | e214a0fe2b382fa302c036ecd6e6ffe99e3b9875 (patch) | |
tree | 6cfd92715630e3406521f700a3df3cf3295e7770 /drivers/infiniband | |
parent | 5f0a6e2d503896062f641639dacfe5055c2f593b (diff) |
IB/uverbs: Protect QP multicast list
Userspace verbs multicast attach/detach operations on a QP are done
while holding the rwsem of the QP for reading. That's not sufficient
since a reader lock allows more than one reader to acquire the
lock. However, multicast attach/detach does list manipulation that
can corrupt the list if multiple threads run in parallel.
Fix this by acquiring the rwsem as a writer to serialize attach/detach
operations. Add idr_write_qp() and put_qp_write() to encapsulate
this.
This fixes oops seen when running applications that perform multicast
joins/leaves.
Reported by: Mike Dubman <miked@mellanox.com>
Signed-off-by: Eli Cohen <eli@mellanox.com>
Cc: <stable@kernel.org>
Signed-off-by: Roland Dreier <roland@purestorage.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/core/uverbs_cmd.c | 21 |
1 files changed, 17 insertions, 4 deletions
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 254f1649c734..e3db8ef4fe4e 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c | |||
@@ -241,11 +241,24 @@ static struct ib_qp *idr_read_qp(int qp_handle, struct ib_ucontext *context) | |||
241 | return idr_read_obj(&ib_uverbs_qp_idr, qp_handle, context, 0); | 241 | return idr_read_obj(&ib_uverbs_qp_idr, qp_handle, context, 0); |
242 | } | 242 | } |
243 | 243 | ||
244 | static struct ib_qp *idr_write_qp(int qp_handle, struct ib_ucontext *context) | ||
245 | { | ||
246 | struct ib_uobject *uobj; | ||
247 | |||
248 | uobj = idr_write_uobj(&ib_uverbs_qp_idr, qp_handle, context); | ||
249 | return uobj ? uobj->object : NULL; | ||
250 | } | ||
251 | |||
244 | static void put_qp_read(struct ib_qp *qp) | 252 | static void put_qp_read(struct ib_qp *qp) |
245 | { | 253 | { |
246 | put_uobj_read(qp->uobject); | 254 | put_uobj_read(qp->uobject); |
247 | } | 255 | } |
248 | 256 | ||
257 | static void put_qp_write(struct ib_qp *qp) | ||
258 | { | ||
259 | put_uobj_write(qp->uobject); | ||
260 | } | ||
261 | |||
249 | static struct ib_srq *idr_read_srq(int srq_handle, struct ib_ucontext *context) | 262 | static struct ib_srq *idr_read_srq(int srq_handle, struct ib_ucontext *context) |
250 | { | 263 | { |
251 | return idr_read_obj(&ib_uverbs_srq_idr, srq_handle, context, 0); | 264 | return idr_read_obj(&ib_uverbs_srq_idr, srq_handle, context, 0); |
@@ -2375,7 +2388,7 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file, | |||
2375 | if (copy_from_user(&cmd, buf, sizeof cmd)) | 2388 | if (copy_from_user(&cmd, buf, sizeof cmd)) |
2376 | return -EFAULT; | 2389 | return -EFAULT; |
2377 | 2390 | ||
2378 | qp = idr_read_qp(cmd.qp_handle, file->ucontext); | 2391 | qp = idr_write_qp(cmd.qp_handle, file->ucontext); |
2379 | if (!qp) | 2392 | if (!qp) |
2380 | return -EINVAL; | 2393 | return -EINVAL; |
2381 | 2394 | ||
@@ -2404,7 +2417,7 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file, | |||
2404 | kfree(mcast); | 2417 | kfree(mcast); |
2405 | 2418 | ||
2406 | out_put: | 2419 | out_put: |
2407 | put_qp_read(qp); | 2420 | put_qp_write(qp); |
2408 | 2421 | ||
2409 | return ret ? ret : in_len; | 2422 | return ret ? ret : in_len; |
2410 | } | 2423 | } |
@@ -2422,7 +2435,7 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file, | |||
2422 | if (copy_from_user(&cmd, buf, sizeof cmd)) | 2435 | if (copy_from_user(&cmd, buf, sizeof cmd)) |
2423 | return -EFAULT; | 2436 | return -EFAULT; |
2424 | 2437 | ||
2425 | qp = idr_read_qp(cmd.qp_handle, file->ucontext); | 2438 | qp = idr_write_qp(cmd.qp_handle, file->ucontext); |
2426 | if (!qp) | 2439 | if (!qp) |
2427 | return -EINVAL; | 2440 | return -EINVAL; |
2428 | 2441 | ||
@@ -2441,7 +2454,7 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file, | |||
2441 | } | 2454 | } |
2442 | 2455 | ||
2443 | out_put: | 2456 | out_put: |
2444 | put_qp_read(qp); | 2457 | put_qp_write(qp); |
2445 | 2458 | ||
2446 | return ret ? ret : in_len; | 2459 | return ret ? ret : in_len; |
2447 | } | 2460 | } |