Skip to content

Commit

Permalink
drbd: fix request leak introduced by locking/atomic, kref: Kill kref_…
Browse files Browse the repository at this point in the history
…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: bdfafc4 ("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>
  • Loading branch information
Lars Ellenberg authored and Jens Axboe committed May 11, 2017
1 parent ed6565e commit a00ebd1
Showing 1 changed file with 15 additions and 12 deletions.
27 changes: 15 additions & 12 deletions drivers/block/drbd/drbd_req.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,24 +315,32 @@ void drbd_req_complete(struct drbd_request *req, struct bio_and_error *m)
}

/* still holds resource->req_lock */
static int drbd_req_put_completion_ref(struct drbd_request *req, struct bio_and_error *m, int put)
static void drbd_req_put_completion_ref(struct drbd_request *req, struct bio_and_error *m, int put)
{
struct drbd_device *device = req->device;
D_ASSERT(device, m || (req->rq_state & RQ_POSTPONED));

if (!put)
return;

if (!atomic_sub_and_test(put, &req->completion_ref))
return 0;
return;

drbd_req_complete(req, m);

/* local completion may still come in later,
* we need to keep the req object around. */
if (req->rq_state & RQ_LOCAL_ABORTED)
return;

if (req->rq_state & RQ_POSTPONED) {
/* don't destroy the req object just yet,
* but queue it for retry */
drbd_restart_request(req);
return 0;
return;
}

return 1;
kref_put(&req->kref, drbd_req_destroy);
}

static void set_if_null_req_next(struct drbd_peer_device *peer_device, struct drbd_request *req)
Expand Down Expand Up @@ -519,12 +527,8 @@ static void mod_rq_state(struct drbd_request *req, struct bio_and_error *m,
if (req->i.waiting)
wake_up(&device->misc_wait);

if (c_put) {
if (drbd_req_put_completion_ref(req, m, c_put))
kref_put(&req->kref, drbd_req_destroy);
} else {
kref_put(&req->kref, drbd_req_destroy);
}
drbd_req_put_completion_ref(req, m, c_put);
kref_put(&req->kref, drbd_req_destroy);
}

static void drbd_report_io_error(struct drbd_device *device, struct drbd_request *req)
Expand Down Expand Up @@ -1366,8 +1370,7 @@ static void drbd_send_and_submit(struct drbd_device *device, struct drbd_request
}

out:
if (drbd_req_put_completion_ref(req, &m, 1))
kref_put(&req->kref, drbd_req_destroy);
drbd_req_put_completion_ref(req, &m, 1);
spin_unlock_irq(&resource->req_lock);

/* Even though above is a kref_put(), this is safe.
Expand Down

0 comments on commit a00ebd1

Please sign in to comment.