Skip to content

Commit

Permalink
drbd: factor out master_bio completion and drbd_request destruction p…
Browse files Browse the repository at this point in the history
…aths

In preparation for multiple connections and reference counting,
separate the code paths for completion of the master bio
and destruction of the request object.

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
  • Loading branch information
Lars Ellenberg authored and Philipp Reisner committed Nov 8, 2012
1 parent 8d6cdd7 commit 6870ca6
Showing 1 changed file with 58 additions and 36 deletions.
94 changes: 58 additions & 36 deletions drivers/block/drbd/drbd_req.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,44 @@ static void maybe_wakeup_conflicting_requests(struct drbd_request *req)
wake_up(&req->w.mdev->misc_wait);
}

static
void req_may_be_done(struct drbd_request *req)
{
const unsigned long s = req->rq_state;
struct drbd_conf *mdev = req->w.mdev;
int rw = req->rq_state & RQ_WRITE ? WRITE : READ;

/* req->master_bio still present means: Not yet completed.
*
* Unless this is RQ_POSTPONED, which will cause _req_is_done() to
* queue it on the retry workqueue instead of destroying it.
*/
if (req->master_bio && !(s & RQ_POSTPONED))
return;

/* Local still pending, even though master_bio is already completed?
* may happen for RQ_LOCAL_ABORTED requests. */
if (s & RQ_LOCAL_PENDING)
return;

if ((s & RQ_NET_MASK) == 0 || (s & RQ_NET_DONE)) {
/* this is disconnected (local only) operation,
* or protocol A, B, or C P_BARRIER_ACK,
* or killed from the transfer log due to connection loss. */
_req_is_done(mdev, req, rw);
}
/* else: network part and not DONE yet. that is
* protocol A, B, or C, barrier ack still pending... */
}

/* Helper for __req_mod().
* Set m->bio to the master bio, if it is fit to be completed,
* or leave it alone (it is initialized to NULL in __req_mod),
* if it has already been completed, or cannot be completed yet.
* If m->bio is set, the error status to be returned is placed in m->error.
*/
void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m)
static
void req_may_be_completed(struct drbd_request *req, struct bio_and_error *m)
{
const unsigned long s = req->rq_state;
struct drbd_conf *mdev = req->w.mdev;
Expand Down Expand Up @@ -309,26 +340,15 @@ void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m)
D_ASSERT(s & RQ_NET_DONE);
}
}

if (s & RQ_LOCAL_PENDING)
return;

if ((s & RQ_NET_MASK) == 0 || (s & RQ_NET_DONE)) {
/* this is disconnected (local only) operation,
* or protocol A, B, or C P_BARRIER_ACK,
* or killed from the transfer log due to connection loss. */
_req_is_done(mdev, req, rw);
}
/* else: network part and not DONE yet. that is
* protocol A, B, or C, barrier ack still pending... */
req_may_be_done(req);
}

static void _req_may_be_done_not_susp(struct drbd_request *req, struct bio_and_error *m)
static void req_may_be_completed_not_susp(struct drbd_request *req, struct bio_and_error *m)
{
struct drbd_conf *mdev = req->w.mdev;

if (!drbd_suspended(mdev))
_req_may_be_done(req, m);
req_may_be_completed(req, m);
}

/* obviously this could be coded as many single functions
Expand Down Expand Up @@ -395,14 +415,14 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
req->rq_state &= ~RQ_LOCAL_PENDING;

maybe_wakeup_conflicting_requests(req);
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
put_ldev(mdev);
break;

case ABORT_DISK_IO:
req->rq_state |= RQ_LOCAL_ABORTED;
if (req->rq_state & RQ_WRITE)
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
else
goto goto_queue_for_net_read;
break;
Expand All @@ -413,15 +433,15 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,

__drbd_chk_io_error(mdev, false);
maybe_wakeup_conflicting_requests(req);
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
put_ldev(mdev);
break;

case READ_AHEAD_COMPLETED_WITH_ERROR:
/* it is legal to fail READA */
req->rq_state |= RQ_LOCAL_COMPLETED;
req->rq_state &= ~RQ_LOCAL_PENDING;
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
put_ldev(mdev);
break;

Expand All @@ -441,7 +461,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
/* no point in retrying if there is no good remote data,
* or we have no connection. */
if (mdev->state.pdsk != D_UP_TO_DATE) {
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
break;
}

Expand All @@ -458,8 +478,9 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
/* from __drbd_make_request
* or from bio_endio during read io-error recovery */

/* so we can verify the handle in the answer packet
* corresponding hlist_del is in _req_may_be_done() */
/* So we can verify the handle in the answer packet.
* Corresponding drbd_remove_request_interval is in
* req_may_be_completed() */
D_ASSERT(drbd_interval_empty(&req->i));
drbd_insert_interval(&mdev->read_requests, &req->i);

Expand All @@ -477,7 +498,8 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
/* assert something? */
/* from __drbd_make_request only */

/* corresponding hlist_del is in _req_may_be_done() */
/* Corresponding drbd_remove_request_interval is in
* req_may_be_completed() */
D_ASSERT(drbd_interval_empty(&req->i));
drbd_insert_interval(&mdev->write_requests, &req->i);

Expand Down Expand Up @@ -539,7 +561,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
req->rq_state &= ~RQ_NET_QUEUED;
/* if we did it right, tl_clear should be scheduled only after
* this, so this should not be necessary! */
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
break;

case HANDED_OVER_TO_NETWORK:
Expand All @@ -562,15 +584,15 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
}
req->rq_state &= ~RQ_NET_QUEUED;
req->rq_state |= RQ_NET_SENT;
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
break;

case OOS_HANDED_TO_NETWORK:
/* Was not set PENDING, no longer QUEUED, so is now DONE
* as far as this connection is concerned. */
req->rq_state &= ~RQ_NET_QUEUED;
req->rq_state |= RQ_NET_DONE;
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
break;

case CONNECTION_LOST_WHILE_PENDING:
Expand All @@ -591,7 +613,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
if (!(req->rq_state & RQ_NET_QUEUED)) {
if (p)
goto goto_read_retry_local;
_req_may_be_done(req, m); /* Allowed while state.susp */
req_may_be_completed(req, m); /* Allowed while state.susp */
}
break;

Expand Down Expand Up @@ -624,7 +646,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
atomic_sub(req->i.size >> 9, &mdev->ap_in_flight);
req->rq_state &= ~RQ_NET_PENDING;
maybe_wakeup_conflicting_requests(req);
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
break;

case POSTPONE_WRITE:
Expand All @@ -636,7 +658,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
D_ASSERT(req->rq_state & RQ_NET_PENDING);
req->rq_state |= RQ_POSTPONED;
maybe_wakeup_conflicting_requests(req);
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
break;

case NEG_ACKED:
Expand All @@ -654,13 +676,13 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
goto goto_read_retry_local;

maybe_wakeup_conflicting_requests(req);
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
/* else: done by HANDED_OVER_TO_NETWORK */
break;

goto_read_retry_local:
if (!drbd_may_do_local_read(mdev, req->i.sector, req->i.size)) {
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
break;
}
D_ASSERT(!(req->rq_state & RQ_LOCAL_PENDING));
Expand All @@ -675,7 +697,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
if (!(req->rq_state & RQ_LOCAL_COMPLETED))
break;

_req_may_be_done(req, m); /* Allowed while state.susp */
req_may_be_completed(req, m); /* Allowed while state.susp */
break;

case RESTART_FROZEN_DISK_IO:
Expand All @@ -696,8 +718,8 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
case RESEND:
/* If RQ_NET_OK is already set, we got a P_WRITE_ACK or P_RECV_ACK
before the connection loss (B&C only); only P_BARRIER_ACK was missing.
Trowing them out of the TL here by pretending we got a BARRIER_ACK
We ensure that the peer was not rebooted */
Throwing them out of the TL here by pretending we got a BARRIER_ACK.
During connection handshake, we ensure that the peer was not rebooted. */
if (!(req->rq_state & RQ_NET_OK)) {
if (req->w.cb) {
drbd_queue_work(&mdev->tconn->data.work, &req->w);
Expand All @@ -723,15 +745,15 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
if (!(req->rq_state & (RQ_EXP_RECEIVE_ACK | RQ_EXP_WRITE_ACK)))
atomic_sub(req->i.size>>9, &mdev->ap_in_flight);
}
_req_may_be_done(req, m); /* Allowed while state.susp */
req_may_be_done(req); /* Allowed while state.susp */
break;

case DATA_RECEIVED:
D_ASSERT(req->rq_state & RQ_NET_PENDING);
dec_ap_pending(mdev);
req->rq_state &= ~RQ_NET_PENDING;
req->rq_state |= (RQ_NET_OK|RQ_NET_DONE);
_req_may_be_done_not_susp(req, m);
req_may_be_completed_not_susp(req, m);
break;
};

Expand Down

0 comments on commit 6870ca6

Please sign in to comment.