summaryrefslogtreecommitdiffstats
path: root/drivers/block
diff options
context:
space:
mode:
authorLars Ellenberg <lars.ellenberg@linbit.com>2017-05-11 04:21:46 -0400
committerJens Axboe <axboe@fb.com>2017-05-11 12:04:30 -0400
commita00ebd1cf12c378a1d4f7a1d6daf1d76c1eaad82 (patch)
tree98edd7dd6a0d901d705fe94374c983eda5524798 /drivers/block
parented6565e734249ef021d5c13ba34c167eb4e42f62 (diff)
drbd: fix request leak introduced by locking/atomic, kref: Kill kref_sub()
When killing kref_sub(), the unconditional additional kref_get() was not properly paired with the necessary kref_put(), causing a leak of struct drbd_requests (~ 224 Bytes) per submitted bio, and breaking DRBD in general, as the destructor of those "drbd_requests" does more than just the mempoll_free(). Fixes: bdfafc4ffdd2 ("locking/atomic, kref: Kill kref_sub()") Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com> Cc: stable@vger.kernel.org # v4.11 Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/drbd/drbd_req.c27
1 files changed, 15 insertions, 12 deletions
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index b5730e17b455..656624314f0d 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -315,24 +315,32 @@ void drbd_req_complete(struct drbd_request *req, struct bio_and_error *m)
315} 315}
316 316
317/* still holds resource->req_lock */ 317/* still holds resource->req_lock */
318static int drbd_req_put_completion_ref(struct drbd_request *req, struct bio_and_error *m, int put) 318static void drbd_req_put_completion_ref(struct drbd_request *req, struct bio_and_error *m, int put)
319{ 319{
320 struct drbd_device *device = req->device; 320 struct drbd_device *device = req->device;
321 D_ASSERT(device, m || (req->rq_state & RQ_POSTPONED)); 321 D_ASSERT(device, m || (req->rq_state & RQ_POSTPONED));
322 322
323 if (!put)
324 return;
325
323 if (!atomic_sub_and_test(put, &req->completion_ref)) 326 if (!atomic_sub_and_test(put, &req->completion_ref))
324 return 0; 327 return;
325 328
326 drbd_req_complete(req, m); 329 drbd_req_complete(req, m);
327 330
331 /* local completion may still come in later,
332 * we need to keep the req object around. */
333 if (req->rq_state & RQ_LOCAL_ABORTED)
334 return;
335
328 if (req->rq_state & RQ_POSTPONED) { 336 if (req->rq_state & RQ_POSTPONED) {
329 /* don't destroy the req object just yet, 337 /* don't destroy the req object just yet,
330 * but queue it for retry */ 338 * but queue it for retry */
331 drbd_restart_request(req); 339 drbd_restart_request(req);
332 return 0; 340 return;
333 } 341 }
334 342
335 return 1; 343 kref_put(&req->kref, drbd_req_destroy);
336} 344}
337 345
338static void set_if_null_req_next(struct drbd_peer_device *peer_device, struct drbd_request *req) 346static void set_if_null_req_next(struct drbd_peer_device *peer_device, struct drbd_request *req)
@@ -519,12 +527,8 @@ static void mod_rq_state(struct drbd_request *req, struct bio_and_error *m,
519 if (req->i.waiting) 527 if (req->i.waiting)
520 wake_up(&device->misc_wait); 528 wake_up(&device->misc_wait);
521 529
522 if (c_put) { 530 drbd_req_put_completion_ref(req, m, c_put);
523 if (drbd_req_put_completion_ref(req, m, c_put)) 531 kref_put(&req->kref, drbd_req_destroy);
524 kref_put(&req->kref, drbd_req_destroy);
525 } else {
526 kref_put(&req->kref, drbd_req_destroy);
527 }
528} 532}
529 533
530static void drbd_report_io_error(struct drbd_device *device, struct drbd_request *req) 534static void drbd_report_io_error(struct drbd_device *device, struct drbd_request *req)
@@ -1366,8 +1370,7 @@ nodata:
1366 } 1370 }
1367 1371
1368out: 1372out:
1369 if (drbd_req_put_completion_ref(req, &m, 1)) 1373 drbd_req_put_completion_ref(req, &m, 1);
1370 kref_put(&req->kref, drbd_req_destroy);
1371 spin_unlock_irq(&resource->req_lock); 1374 spin_unlock_irq(&resource->req_lock);
1372 1375
1373 /* Even though above is a kref_put(), this is safe. 1376 /* Even though above is a kref_put(), this is safe.