diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index d07cb31a36eaa6608610a5d6326de25d7df9300c..c0acd86c84153185cfc8c18716a25dffe0928d65 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -2216,12 +2216,25 @@ static void do_retry(struct work_struct *ws) struct drbd_conf *mdev = req->w.mdev; struct bio *bio = req->master_bio; unsigned long start_time = req->start_time; - - /* We have exclusive access to this request object. - * If it had not been RQ_POSTPONED, the code path which queued - * it here would have completed and freed it already. + bool expected; + + expected = + expect(atomic_read(&req->completion_ref) == 0) && + expect(req->rq_state & RQ_POSTPONED) && + expect((req->rq_state & RQ_LOCAL_PENDING) == 0 || + (req->rq_state & RQ_LOCAL_ABORTED) != 0); + + if (!expected) + dev_err(DEV, "req=%p completion_ref=%d rq_state=%x\n", + req, atomic_read(&req->completion_ref), + req->rq_state); + + /* We still need to put one kref associated with the + * "completion_ref" going zero in the code path that queued it + * here. The request object may still be referenced by a + * frozen local req->private_bio, in case we force-detached. */ - mempool_free(req, drbd_request_mempool); + kref_put(&req->kref, drbd_req_destroy); /* A single suspended or otherwise blocking device may stall * all others as well. Fortunately, this code path is to diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index f2ba43e78377352fca65bfd1c2971bfb794f529a..c45479aaff8ec255ca7f029cc3b38544dd9df3a2 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -92,7 +92,7 @@ static struct drbd_request *drbd_req_new(struct drbd_conf *mdev, return req; } -static void drbd_req_destroy(struct kref *kref) +void drbd_req_destroy(struct kref *kref) { struct drbd_request *req = container_of(kref, struct drbd_request, kref); struct drbd_conf *mdev = req->w.mdev; @@ -152,10 +152,7 @@ static void drbd_req_destroy(struct kref *kref) } } - if (s & RQ_POSTPONED) - drbd_restart_request(req); - else - mempool_free(req, drbd_request_mempool); + mempool_free(req, drbd_request_mempool); } static void wake_all_senders(struct drbd_tconn *tconn) { @@ -292,10 +289,6 @@ void drbd_req_complete(struct drbd_request *req, struct bio_and_error *m) m->error = ok ? 0 : (error ?: -EIO); m->bio = req->master_bio; req->master_bio = NULL; - } else { - /* Assert that this will be drbd_req_destroy()ed - * with this very invokation. */ - D_ASSERT(atomic_read(&req->kref.refcount) == 1); } } @@ -320,6 +313,14 @@ static int drbd_req_put_completion_ref(struct drbd_request *req, struct bio_and_ /* else */ drbd_req_complete(req, m); + + 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 1; } diff --git a/drivers/block/drbd/drbd_req.h b/drivers/block/drbd/drbd_req.h index 90e5a1eea7273764ce7c81909bdd20aa598638d2..9611713c358077d78914dac8587279f49f5faabb 100644 --- a/drivers/block/drbd/drbd_req.h +++ b/drivers/block/drbd/drbd_req.h @@ -267,6 +267,7 @@ struct bio_and_error { int error; }; +extern void drbd_req_destroy(struct kref *kref); extern void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m); extern int __req_mod(struct drbd_request *req, enum drbd_req_event what,