diff options
author | Roland Dreier <rolandd@cisco.com> | 2006-05-09 13:50:29 -0400 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2006-05-09 13:50:29 -0400 |
commit | a3285aa4eecd722508dab01c4932b11b4ba80134 (patch) | |
tree | 1d12d6bbba7f42939536ccdba14f7738f2a26793 /drivers/infiniband/hw | |
parent | d945e1df28ca07642b3e1a9b9d07074ba5f76be0 (diff) |
IB/mthca: Fix race in reference counting
Fix races in in destroying various objects. If a destroy routine
waits for an object to become free by doing
wait_event(&obj->wait, !atomic_read(&obj->refcount));
/* now clean up and destroy the object */
and another place drops a reference to the object by doing
if (atomic_dec_and_test(&obj->refcount))
wake_up(&obj->wait);
then this is susceptible to a race where the wait_event() and final
freeing of the object occur between the atomic_dec_and_test() and the
wake_up(). And this is a use-after-free, since wake_up() will be
called on part of the already-freed object.
Fix this in mthca by replacing the atomic_t refcounts with plain old
integers protected by a spinlock. This makes it possible to do the
decrement of the reference count and the wake_up() so that it appears
as a single atomic operation to the code waiting on the wait queue.
While touching this code, also simplify mthca_cq_clean(): the CQ being
cleaned cannot go away, because it still has a QP attached to it. So
there's no reason to be paranoid and look up the CQ by number; it's
perfectly safe to use the pointer that the callers already have.
Signed-off-by: Roland Dreier <rolandd@cisco.com>
Diffstat (limited to 'drivers/infiniband/hw')
-rw-r--r-- | drivers/infiniband/hw/mthca/mthca_cq.c | 41 | ||||
-rw-r--r-- | drivers/infiniband/hw/mthca/mthca_dev.h | 2 | ||||
-rw-r--r-- | drivers/infiniband/hw/mthca/mthca_provider.h | 22 | ||||
-rw-r--r-- | drivers/infiniband/hw/mthca/mthca_qp.c | 31 | ||||
-rw-r--r-- | drivers/infiniband/hw/mthca/mthca_srq.c | 23 |
5 files changed, 74 insertions, 45 deletions
diff --git a/drivers/infiniband/hw/mthca/mthca_cq.c b/drivers/infiniband/hw/mthca/mthca_cq.c index 312cf90731ea..205854e9c662 100644 --- a/drivers/infiniband/hw/mthca/mthca_cq.c +++ b/drivers/infiniband/hw/mthca/mthca_cq.c | |||
@@ -238,9 +238,9 @@ void mthca_cq_event(struct mthca_dev *dev, u32 cqn, | |||
238 | spin_lock(&dev->cq_table.lock); | 238 | spin_lock(&dev->cq_table.lock); |
239 | 239 | ||
240 | cq = mthca_array_get(&dev->cq_table.cq, cqn & (dev->limits.num_cqs - 1)); | 240 | cq = mthca_array_get(&dev->cq_table.cq, cqn & (dev->limits.num_cqs - 1)); |
241 | |||
242 | if (cq) | 241 | if (cq) |
243 | atomic_inc(&cq->refcount); | 242 | ++cq->refcount; |
243 | |||
244 | spin_unlock(&dev->cq_table.lock); | 244 | spin_unlock(&dev->cq_table.lock); |
245 | 245 | ||
246 | if (!cq) { | 246 | if (!cq) { |
@@ -254,8 +254,10 @@ void mthca_cq_event(struct mthca_dev *dev, u32 cqn, | |||
254 | if (cq->ibcq.event_handler) | 254 | if (cq->ibcq.event_handler) |
255 | cq->ibcq.event_handler(&event, cq->ibcq.cq_context); | 255 | cq->ibcq.event_handler(&event, cq->ibcq.cq_context); |
256 | 256 | ||
257 | if (atomic_dec_and_test(&cq->refcount)) | 257 | spin_lock(&dev->cq_table.lock); |
258 | if (!--cq->refcount) | ||
258 | wake_up(&cq->wait); | 259 | wake_up(&cq->wait); |
260 | spin_unlock(&dev->cq_table.lock); | ||
259 | } | 261 | } |
260 | 262 | ||
261 | static inline int is_recv_cqe(struct mthca_cqe *cqe) | 263 | static inline int is_recv_cqe(struct mthca_cqe *cqe) |
@@ -267,23 +269,13 @@ static inline int is_recv_cqe(struct mthca_cqe *cqe) | |||
267 | return !(cqe->is_send & 0x80); | 269 | return !(cqe->is_send & 0x80); |
268 | } | 270 | } |
269 | 271 | ||
270 | void mthca_cq_clean(struct mthca_dev *dev, u32 cqn, u32 qpn, | 272 | void mthca_cq_clean(struct mthca_dev *dev, struct mthca_cq *cq, u32 qpn, |
271 | struct mthca_srq *srq) | 273 | struct mthca_srq *srq) |
272 | { | 274 | { |
273 | struct mthca_cq *cq; | ||
274 | struct mthca_cqe *cqe; | 275 | struct mthca_cqe *cqe; |
275 | u32 prod_index; | 276 | u32 prod_index; |
276 | int nfreed = 0; | 277 | int nfreed = 0; |
277 | 278 | ||
278 | spin_lock_irq(&dev->cq_table.lock); | ||
279 | cq = mthca_array_get(&dev->cq_table.cq, cqn & (dev->limits.num_cqs - 1)); | ||
280 | if (cq) | ||
281 | atomic_inc(&cq->refcount); | ||
282 | spin_unlock_irq(&dev->cq_table.lock); | ||
283 | |||
284 | if (!cq) | ||
285 | return; | ||
286 | |||
287 | spin_lock_irq(&cq->lock); | 279 | spin_lock_irq(&cq->lock); |
288 | 280 | ||
289 | /* | 281 | /* |
@@ -301,7 +293,7 @@ void mthca_cq_clean(struct mthca_dev *dev, u32 cqn, u32 qpn, | |||
301 | 293 | ||
302 | if (0) | 294 | if (0) |
303 | mthca_dbg(dev, "Cleaning QPN %06x from CQN %06x; ci %d, pi %d\n", | 295 | mthca_dbg(dev, "Cleaning QPN %06x from CQN %06x; ci %d, pi %d\n", |
304 | qpn, cqn, cq->cons_index, prod_index); | 296 | qpn, cq->cqn, cq->cons_index, prod_index); |
305 | 297 | ||
306 | /* | 298 | /* |
307 | * Now sweep backwards through the CQ, removing CQ entries | 299 | * Now sweep backwards through the CQ, removing CQ entries |
@@ -325,8 +317,6 @@ void mthca_cq_clean(struct mthca_dev *dev, u32 cqn, u32 qpn, | |||
325 | } | 317 | } |
326 | 318 | ||
327 | spin_unlock_irq(&cq->lock); | 319 | spin_unlock_irq(&cq->lock); |
328 | if (atomic_dec_and_test(&cq->refcount)) | ||
329 | wake_up(&cq->wait); | ||
330 | } | 320 | } |
331 | 321 | ||
332 | void mthca_cq_resize_copy_cqes(struct mthca_cq *cq) | 322 | void mthca_cq_resize_copy_cqes(struct mthca_cq *cq) |
@@ -821,7 +811,7 @@ int mthca_init_cq(struct mthca_dev *dev, int nent, | |||
821 | } | 811 | } |
822 | 812 | ||
823 | spin_lock_init(&cq->lock); | 813 | spin_lock_init(&cq->lock); |
824 | atomic_set(&cq->refcount, 1); | 814 | cq->refcount = 1; |
825 | init_waitqueue_head(&cq->wait); | 815 | init_waitqueue_head(&cq->wait); |
826 | 816 | ||
827 | memset(cq_context, 0, sizeof *cq_context); | 817 | memset(cq_context, 0, sizeof *cq_context); |
@@ -896,6 +886,17 @@ err_out: | |||
896 | return err; | 886 | return err; |
897 | } | 887 | } |
898 | 888 | ||
889 | static inline int get_cq_refcount(struct mthca_dev *dev, struct mthca_cq *cq) | ||
890 | { | ||
891 | int c; | ||
892 | |||
893 | spin_lock_irq(&dev->cq_table.lock); | ||
894 | c = cq->refcount; | ||
895 | spin_unlock_irq(&dev->cq_table.lock); | ||
896 | |||
897 | return c; | ||
898 | } | ||
899 | |||
899 | void mthca_free_cq(struct mthca_dev *dev, | 900 | void mthca_free_cq(struct mthca_dev *dev, |
900 | struct mthca_cq *cq) | 901 | struct mthca_cq *cq) |
901 | { | 902 | { |
@@ -929,6 +930,7 @@ void mthca_free_cq(struct mthca_dev *dev, | |||
929 | spin_lock_irq(&dev->cq_table.lock); | 930 | spin_lock_irq(&dev->cq_table.lock); |
930 | mthca_array_clear(&dev->cq_table.cq, | 931 | mthca_array_clear(&dev->cq_table.cq, |
931 | cq->cqn & (dev->limits.num_cqs - 1)); | 932 | cq->cqn & (dev->limits.num_cqs - 1)); |
933 | --cq->refcount; | ||
932 | spin_unlock_irq(&dev->cq_table.lock); | 934 | spin_unlock_irq(&dev->cq_table.lock); |
933 | 935 | ||
934 | if (dev->mthca_flags & MTHCA_FLAG_MSI_X) | 936 | if (dev->mthca_flags & MTHCA_FLAG_MSI_X) |
@@ -936,8 +938,7 @@ void mthca_free_cq(struct mthca_dev *dev, | |||
936 | else | 938 | else |
937 | synchronize_irq(dev->pdev->irq); | 939 | synchronize_irq(dev->pdev->irq); |
938 | 940 | ||
939 | atomic_dec(&cq->refcount); | 941 | wait_event(cq->wait, !get_cq_refcount(dev, cq)); |
940 | wait_event(cq->wait, !atomic_read(&cq->refcount)); | ||
941 | 942 | ||
942 | if (cq->is_kernel) { | 943 | if (cq->is_kernel) { |
943 | mthca_free_cq_buf(dev, &cq->buf, cq->ibcq.cqe); | 944 | mthca_free_cq_buf(dev, &cq->buf, cq->ibcq.cqe); |
diff --git a/drivers/infiniband/hw/mthca/mthca_dev.h b/drivers/infiniband/hw/mthca/mthca_dev.h index 4c1dcb4c1822..f8160b8de090 100644 --- a/drivers/infiniband/hw/mthca/mthca_dev.h +++ b/drivers/infiniband/hw/mthca/mthca_dev.h | |||
@@ -496,7 +496,7 @@ void mthca_free_cq(struct mthca_dev *dev, | |||
496 | void mthca_cq_completion(struct mthca_dev *dev, u32 cqn); | 496 | void mthca_cq_completion(struct mthca_dev *dev, u32 cqn); |
497 | void mthca_cq_event(struct mthca_dev *dev, u32 cqn, | 497 | void mthca_cq_event(struct mthca_dev *dev, u32 cqn, |
498 | enum ib_event_type event_type); | 498 | enum ib_event_type event_type); |
499 | void mthca_cq_clean(struct mthca_dev *dev, u32 cqn, u32 qpn, | 499 | void mthca_cq_clean(struct mthca_dev *dev, struct mthca_cq *cq, u32 qpn, |
500 | struct mthca_srq *srq); | 500 | struct mthca_srq *srq); |
501 | void mthca_cq_resize_copy_cqes(struct mthca_cq *cq); | 501 | void mthca_cq_resize_copy_cqes(struct mthca_cq *cq); |
502 | int mthca_alloc_cq_buf(struct mthca_dev *dev, struct mthca_cq_buf *buf, int nent); | 502 | int mthca_alloc_cq_buf(struct mthca_dev *dev, struct mthca_cq_buf *buf, int nent); |
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.h b/drivers/infiniband/hw/mthca/mthca_provider.h index 6676a786d690..179a8f610d0f 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.h +++ b/drivers/infiniband/hw/mthca/mthca_provider.h | |||
@@ -139,11 +139,12 @@ struct mthca_ah { | |||
139 | * a qp may be locked, with the send cq locked first. No other | 139 | * a qp may be locked, with the send cq locked first. No other |
140 | * nesting should be done. | 140 | * nesting should be done. |
141 | * | 141 | * |
142 | * Each struct mthca_cq/qp also has an atomic_t ref count. The | 142 | * Each struct mthca_cq/qp also has an ref count, protected by the |
143 | * pointer from the cq/qp_table to the struct counts as one reference. | 143 | * corresponding table lock. The pointer from the cq/qp_table to the |
144 | * This reference also is good for access through the consumer API, so | 144 | * struct counts as one reference. This reference also is good for |
145 | * modifying the CQ/QP etc doesn't need to take another reference. | 145 | * access through the consumer API, so modifying the CQ/QP etc doesn't |
146 | * Access because of a completion being polled does need a reference. | 146 | * need to take another reference. Access to a QP because of a |
147 | * completion being polled does not need a reference either. | ||
147 | * | 148 | * |
148 | * Finally, each struct mthca_cq/qp has a wait_queue_head_t for the | 149 | * Finally, each struct mthca_cq/qp has a wait_queue_head_t for the |
149 | * destroy function to sleep on. | 150 | * destroy function to sleep on. |
@@ -159,8 +160,9 @@ struct mthca_ah { | |||
159 | * - decrement ref count; if zero, wake up waiters | 160 | * - decrement ref count; if zero, wake up waiters |
160 | * | 161 | * |
161 | * To destroy a CQ/QP, we can do the following: | 162 | * To destroy a CQ/QP, we can do the following: |
162 | * - lock cq/qp_table, remove pointer, unlock cq/qp_table lock | 163 | * - lock cq/qp_table |
163 | * - decrement ref count | 164 | * - remove pointer and decrement ref count |
165 | * - unlock cq/qp_table lock | ||
164 | * - wait_event until ref count is zero | 166 | * - wait_event until ref count is zero |
165 | * | 167 | * |
166 | * It is the consumer's responsibilty to make sure that no QP | 168 | * It is the consumer's responsibilty to make sure that no QP |
@@ -197,7 +199,7 @@ struct mthca_cq_resize { | |||
197 | struct mthca_cq { | 199 | struct mthca_cq { |
198 | struct ib_cq ibcq; | 200 | struct ib_cq ibcq; |
199 | spinlock_t lock; | 201 | spinlock_t lock; |
200 | atomic_t refcount; | 202 | int refcount; |
201 | int cqn; | 203 | int cqn; |
202 | u32 cons_index; | 204 | u32 cons_index; |
203 | struct mthca_cq_buf buf; | 205 | struct mthca_cq_buf buf; |
@@ -217,7 +219,7 @@ struct mthca_cq { | |||
217 | struct mthca_srq { | 219 | struct mthca_srq { |
218 | struct ib_srq ibsrq; | 220 | struct ib_srq ibsrq; |
219 | spinlock_t lock; | 221 | spinlock_t lock; |
220 | atomic_t refcount; | 222 | int refcount; |
221 | int srqn; | 223 | int srqn; |
222 | int max; | 224 | int max; |
223 | int max_gs; | 225 | int max_gs; |
@@ -254,7 +256,7 @@ struct mthca_wq { | |||
254 | 256 | ||
255 | struct mthca_qp { | 257 | struct mthca_qp { |
256 | struct ib_qp ibqp; | 258 | struct ib_qp ibqp; |
257 | atomic_t refcount; | 259 | int refcount; |
258 | u32 qpn; | 260 | u32 qpn; |
259 | int is_direct; | 261 | int is_direct; |
260 | u8 port; /* for SQP and memfree use only */ | 262 | u8 port; /* for SQP and memfree use only */ |
diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index f37b0e367323..19765f6f8d58 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c | |||
@@ -240,7 +240,7 @@ void mthca_qp_event(struct mthca_dev *dev, u32 qpn, | |||
240 | spin_lock(&dev->qp_table.lock); | 240 | spin_lock(&dev->qp_table.lock); |
241 | qp = mthca_array_get(&dev->qp_table.qp, qpn & (dev->limits.num_qps - 1)); | 241 | qp = mthca_array_get(&dev->qp_table.qp, qpn & (dev->limits.num_qps - 1)); |
242 | if (qp) | 242 | if (qp) |
243 | atomic_inc(&qp->refcount); | 243 | ++qp->refcount; |
244 | spin_unlock(&dev->qp_table.lock); | 244 | spin_unlock(&dev->qp_table.lock); |
245 | 245 | ||
246 | if (!qp) { | 246 | if (!qp) { |
@@ -257,8 +257,10 @@ void mthca_qp_event(struct mthca_dev *dev, u32 qpn, | |||
257 | if (qp->ibqp.event_handler) | 257 | if (qp->ibqp.event_handler) |
258 | qp->ibqp.event_handler(&event, qp->ibqp.qp_context); | 258 | qp->ibqp.event_handler(&event, qp->ibqp.qp_context); |
259 | 259 | ||
260 | if (atomic_dec_and_test(&qp->refcount)) | 260 | spin_lock(&dev->qp_table.lock); |
261 | if (!--qp->refcount) | ||
261 | wake_up(&qp->wait); | 262 | wake_up(&qp->wait); |
263 | spin_unlock(&dev->qp_table.lock); | ||
262 | } | 264 | } |
263 | 265 | ||
264 | static int to_mthca_state(enum ib_qp_state ib_state) | 266 | static int to_mthca_state(enum ib_qp_state ib_state) |
@@ -833,10 +835,10 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) | |||
833 | * entries and reinitialize the QP. | 835 | * entries and reinitialize the QP. |
834 | */ | 836 | */ |
835 | if (new_state == IB_QPS_RESET && !qp->ibqp.uobject) { | 837 | if (new_state == IB_QPS_RESET && !qp->ibqp.uobject) { |
836 | mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq)->cqn, qp->qpn, | 838 | mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq), qp->qpn, |
837 | qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); | 839 | qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); |
838 | if (qp->ibqp.send_cq != qp->ibqp.recv_cq) | 840 | if (qp->ibqp.send_cq != qp->ibqp.recv_cq) |
839 | mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq)->cqn, qp->qpn, | 841 | mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq), qp->qpn, |
840 | qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); | 842 | qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); |
841 | 843 | ||
842 | mthca_wq_init(&qp->sq); | 844 | mthca_wq_init(&qp->sq); |
@@ -1096,7 +1098,7 @@ static int mthca_alloc_qp_common(struct mthca_dev *dev, | |||
1096 | int ret; | 1098 | int ret; |
1097 | int i; | 1099 | int i; |
1098 | 1100 | ||
1099 | atomic_set(&qp->refcount, 1); | 1101 | qp->refcount = 1; |
1100 | init_waitqueue_head(&qp->wait); | 1102 | init_waitqueue_head(&qp->wait); |
1101 | qp->state = IB_QPS_RESET; | 1103 | qp->state = IB_QPS_RESET; |
1102 | qp->atomic_rd_en = 0; | 1104 | qp->atomic_rd_en = 0; |
@@ -1318,6 +1320,17 @@ int mthca_alloc_sqp(struct mthca_dev *dev, | |||
1318 | return err; | 1320 | return err; |
1319 | } | 1321 | } |
1320 | 1322 | ||
1323 | static inline int get_qp_refcount(struct mthca_dev *dev, struct mthca_qp *qp) | ||
1324 | { | ||
1325 | int c; | ||
1326 | |||
1327 | spin_lock_irq(&dev->qp_table.lock); | ||
1328 | c = qp->refcount; | ||
1329 | spin_unlock_irq(&dev->qp_table.lock); | ||
1330 | |||
1331 | return c; | ||
1332 | } | ||
1333 | |||
1321 | void mthca_free_qp(struct mthca_dev *dev, | 1334 | void mthca_free_qp(struct mthca_dev *dev, |
1322 | struct mthca_qp *qp) | 1335 | struct mthca_qp *qp) |
1323 | { | 1336 | { |
@@ -1339,14 +1352,14 @@ void mthca_free_qp(struct mthca_dev *dev, | |||
1339 | spin_lock(&dev->qp_table.lock); | 1352 | spin_lock(&dev->qp_table.lock); |
1340 | mthca_array_clear(&dev->qp_table.qp, | 1353 | mthca_array_clear(&dev->qp_table.qp, |
1341 | qp->qpn & (dev->limits.num_qps - 1)); | 1354 | qp->qpn & (dev->limits.num_qps - 1)); |
1355 | --qp->refcount; | ||
1342 | spin_unlock(&dev->qp_table.lock); | 1356 | spin_unlock(&dev->qp_table.lock); |
1343 | 1357 | ||
1344 | if (send_cq != recv_cq) | 1358 | if (send_cq != recv_cq) |
1345 | spin_unlock(&recv_cq->lock); | 1359 | spin_unlock(&recv_cq->lock); |
1346 | spin_unlock_irq(&send_cq->lock); | 1360 | spin_unlock_irq(&send_cq->lock); |
1347 | 1361 | ||
1348 | atomic_dec(&qp->refcount); | 1362 | wait_event(qp->wait, !get_qp_refcount(dev, qp)); |
1349 | wait_event(qp->wait, !atomic_read(&qp->refcount)); | ||
1350 | 1363 | ||
1351 | if (qp->state != IB_QPS_RESET) | 1364 | if (qp->state != IB_QPS_RESET) |
1352 | mthca_MODIFY_QP(dev, qp->state, IB_QPS_RESET, qp->qpn, 0, | 1365 | mthca_MODIFY_QP(dev, qp->state, IB_QPS_RESET, qp->qpn, 0, |
@@ -1358,10 +1371,10 @@ void mthca_free_qp(struct mthca_dev *dev, | |||
1358 | * unref the mem-free tables and free the QPN in our table. | 1371 | * unref the mem-free tables and free the QPN in our table. |
1359 | */ | 1372 | */ |
1360 | if (!qp->ibqp.uobject) { | 1373 | if (!qp->ibqp.uobject) { |
1361 | mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq)->cqn, qp->qpn, | 1374 | mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq), qp->qpn, |
1362 | qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); | 1375 | qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); |
1363 | if (qp->ibqp.send_cq != qp->ibqp.recv_cq) | 1376 | if (qp->ibqp.send_cq != qp->ibqp.recv_cq) |
1364 | mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq)->cqn, qp->qpn, | 1377 | mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq), qp->qpn, |
1365 | qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); | 1378 | qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); |
1366 | 1379 | ||
1367 | mthca_free_memfree(dev, qp); | 1380 | mthca_free_memfree(dev, qp); |
diff --git a/drivers/infiniband/hw/mthca/mthca_srq.c b/drivers/infiniband/hw/mthca/mthca_srq.c index adcaf85355ae..1ea433291fa7 100644 --- a/drivers/infiniband/hw/mthca/mthca_srq.c +++ b/drivers/infiniband/hw/mthca/mthca_srq.c | |||
@@ -241,7 +241,7 @@ int mthca_alloc_srq(struct mthca_dev *dev, struct mthca_pd *pd, | |||
241 | goto err_out_mailbox; | 241 | goto err_out_mailbox; |
242 | 242 | ||
243 | spin_lock_init(&srq->lock); | 243 | spin_lock_init(&srq->lock); |
244 | atomic_set(&srq->refcount, 1); | 244 | srq->refcount = 1; |
245 | init_waitqueue_head(&srq->wait); | 245 | init_waitqueue_head(&srq->wait); |
246 | 246 | ||
247 | if (mthca_is_memfree(dev)) | 247 | if (mthca_is_memfree(dev)) |
@@ -308,6 +308,17 @@ err_out: | |||
308 | return err; | 308 | return err; |
309 | } | 309 | } |
310 | 310 | ||
311 | static inline int get_srq_refcount(struct mthca_dev *dev, struct mthca_srq *srq) | ||
312 | { | ||
313 | int c; | ||
314 | |||
315 | spin_lock_irq(&dev->srq_table.lock); | ||
316 | c = srq->refcount; | ||
317 | spin_unlock_irq(&dev->srq_table.lock); | ||
318 | |||
319 | return c; | ||
320 | } | ||
321 | |||
311 | void mthca_free_srq(struct mthca_dev *dev, struct mthca_srq *srq) | 322 | void mthca_free_srq(struct mthca_dev *dev, struct mthca_srq *srq) |
312 | { | 323 | { |
313 | struct mthca_mailbox *mailbox; | 324 | struct mthca_mailbox *mailbox; |
@@ -329,10 +340,10 @@ void mthca_free_srq(struct mthca_dev *dev, struct mthca_srq *srq) | |||
329 | spin_lock_irq(&dev->srq_table.lock); | 340 | spin_lock_irq(&dev->srq_table.lock); |
330 | mthca_array_clear(&dev->srq_table.srq, | 341 | mthca_array_clear(&dev->srq_table.srq, |
331 | srq->srqn & (dev->limits.num_srqs - 1)); | 342 | srq->srqn & (dev->limits.num_srqs - 1)); |
343 | --srq->refcount; | ||
332 | spin_unlock_irq(&dev->srq_table.lock); | 344 | spin_unlock_irq(&dev->srq_table.lock); |
333 | 345 | ||
334 | atomic_dec(&srq->refcount); | 346 | wait_event(srq->wait, !get_srq_refcount(dev, srq)); |
335 | wait_event(srq->wait, !atomic_read(&srq->refcount)); | ||
336 | 347 | ||
337 | if (!srq->ibsrq.uobject) { | 348 | if (!srq->ibsrq.uobject) { |
338 | mthca_free_srq_buf(dev, srq); | 349 | mthca_free_srq_buf(dev, srq); |
@@ -414,7 +425,7 @@ void mthca_srq_event(struct mthca_dev *dev, u32 srqn, | |||
414 | spin_lock(&dev->srq_table.lock); | 425 | spin_lock(&dev->srq_table.lock); |
415 | srq = mthca_array_get(&dev->srq_table.srq, srqn & (dev->limits.num_srqs - 1)); | 426 | srq = mthca_array_get(&dev->srq_table.srq, srqn & (dev->limits.num_srqs - 1)); |
416 | if (srq) | 427 | if (srq) |
417 | atomic_inc(&srq->refcount); | 428 | ++srq->refcount; |
418 | spin_unlock(&dev->srq_table.lock); | 429 | spin_unlock(&dev->srq_table.lock); |
419 | 430 | ||
420 | if (!srq) { | 431 | if (!srq) { |
@@ -431,8 +442,10 @@ void mthca_srq_event(struct mthca_dev *dev, u32 srqn, | |||
431 | srq->ibsrq.event_handler(&event, srq->ibsrq.srq_context); | 442 | srq->ibsrq.event_handler(&event, srq->ibsrq.srq_context); |
432 | 443 | ||
433 | out: | 444 | out: |
434 | if (atomic_dec_and_test(&srq->refcount)) | 445 | spin_lock(&dev->srq_table.lock); |
446 | if (!--srq->refcount) | ||
435 | wake_up(&srq->wait); | 447 | wake_up(&srq->wait); |
448 | spin_unlock(&dev->srq_table.lock); | ||
436 | } | 449 | } |
437 | 450 | ||
438 | /* | 451 | /* |