|
@@ -52,7 +52,7 @@ static void _drbd_start_io_acct(struct drbd_device *device, struct drbd_request
|
|
|
static void _drbd_end_io_acct(struct drbd_device *device, struct drbd_request *req)
|
|
|
{
|
|
|
int rw = bio_data_dir(req->master_bio);
|
|
|
- unsigned long duration = jiffies - req->start_time;
|
|
|
+ unsigned long duration = jiffies - req->start_jif;
|
|
|
int cpu;
|
|
|
cpu = part_stat_lock();
|
|
|
part_stat_add(cpu, &device->vdisk->part0, ticks[rw], duration);
|
|
@@ -66,7 +66,7 @@ static struct drbd_request *drbd_req_new(struct drbd_device *device,
|
|
|
{
|
|
|
struct drbd_request *req;
|
|
|
|
|
|
- req = mempool_alloc(drbd_request_mempool, GFP_NOIO);
|
|
|
+ req = mempool_alloc(drbd_request_mempool, GFP_NOIO | __GFP_ZERO);
|
|
|
if (!req)
|
|
|
return NULL;
|
|
|
|
|
@@ -84,6 +84,8 @@ static struct drbd_request *drbd_req_new(struct drbd_device *device,
|
|
|
|
|
|
INIT_LIST_HEAD(&req->tl_requests);
|
|
|
INIT_LIST_HEAD(&req->w.list);
|
|
|
+ INIT_LIST_HEAD(&req->req_pending_master_completion);
|
|
|
+ INIT_LIST_HEAD(&req->req_pending_local);
|
|
|
|
|
|
/* one reference to be put by __drbd_make_request */
|
|
|
atomic_set(&req->completion_ref, 1);
|
|
@@ -92,6 +94,19 @@ static struct drbd_request *drbd_req_new(struct drbd_device *device,
|
|
|
return req;
|
|
|
}
|
|
|
|
|
|
+static void drbd_remove_request_interval(struct rb_root *root,
|
|
|
+ struct drbd_request *req)
|
|
|
+{
|
|
|
+ struct drbd_device *device = req->device;
|
|
|
+ struct drbd_interval *i = &req->i;
|
|
|
+
|
|
|
+ drbd_remove_interval(root, i);
|
|
|
+
|
|
|
+ /* Wake up any processes waiting for this request to complete. */
|
|
|
+ if (i->waiting)
|
|
|
+ wake_up(&device->misc_wait);
|
|
|
+}
|
|
|
+
|
|
|
void drbd_req_destroy(struct kref *kref)
|
|
|
{
|
|
|
struct drbd_request *req = container_of(kref, struct drbd_request, kref);
|
|
@@ -107,14 +122,30 @@ void drbd_req_destroy(struct kref *kref)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- /* remove it from the transfer log.
|
|
|
- * well, only if it had been there in the first
|
|
|
- * place... if it had not (local only or conflicting
|
|
|
- * and never sent), it should still be "empty" as
|
|
|
- * initialized in drbd_req_new(), so we can list_del() it
|
|
|
- * here unconditionally */
|
|
|
+ /* If called from mod_rq_state (expected normal case) or
|
|
|
+ * drbd_send_and_submit (the less likely normal path), this holds the
|
|
|
+ * req_lock, and req->tl_requests will typicaly be on ->transfer_log,
|
|
|
+ * though it may be still empty (never added to the transfer log).
|
|
|
+ *
|
|
|
+ * If called from do_retry(), we do NOT hold the req_lock, but we are
|
|
|
+ * still allowed to unconditionally list_del(&req->tl_requests),
|
|
|
+ * because it will be on a local on-stack list only. */
|
|
|
list_del_init(&req->tl_requests);
|
|
|
|
|
|
+ /* finally remove the request from the conflict detection
|
|
|
+ * respective block_id verification interval tree. */
|
|
|
+ if (!drbd_interval_empty(&req->i)) {
|
|
|
+ struct rb_root *root;
|
|
|
+
|
|
|
+ if (s & RQ_WRITE)
|
|
|
+ root = &device->write_requests;
|
|
|
+ else
|
|
|
+ root = &device->read_requests;
|
|
|
+ drbd_remove_request_interval(root, req);
|
|
|
+ } else if (s & (RQ_NET_MASK & ~RQ_NET_DONE) && req->i.size != 0)
|
|
|
+ drbd_err(device, "drbd_req_destroy: Logic BUG: interval empty, but: rq_state=0x%x, sect=%llu, size=%u\n",
|
|
|
+ s, (unsigned long long)req->i.sector, req->i.size);
|
|
|
+
|
|
|
/* if it was a write, we may have to set the corresponding
|
|
|
* bit(s) out-of-sync first. If it had a local part, we need to
|
|
|
* release the reference to the activity log. */
|
|
@@ -188,19 +219,6 @@ void complete_master_bio(struct drbd_device *device,
|
|
|
}
|
|
|
|
|
|
|
|
|
-static void drbd_remove_request_interval(struct rb_root *root,
|
|
|
- struct drbd_request *req)
|
|
|
-{
|
|
|
- struct drbd_device *device = req->device;
|
|
|
- struct drbd_interval *i = &req->i;
|
|
|
-
|
|
|
- drbd_remove_interval(root, i);
|
|
|
-
|
|
|
- /* Wake up any processes waiting for this request to complete. */
|
|
|
- if (i->waiting)
|
|
|
- wake_up(&device->misc_wait);
|
|
|
-}
|
|
|
-
|
|
|
/* 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),
|
|
@@ -254,18 +272,6 @@ void drbd_req_complete(struct drbd_request *req, struct bio_and_error *m)
|
|
|
ok = (s & RQ_LOCAL_OK) || (s & RQ_NET_OK);
|
|
|
error = PTR_ERR(req->private_bio);
|
|
|
|
|
|
- /* remove the request from the conflict detection
|
|
|
- * respective block_id verification hash */
|
|
|
- if (!drbd_interval_empty(&req->i)) {
|
|
|
- struct rb_root *root;
|
|
|
-
|
|
|
- if (rw == WRITE)
|
|
|
- root = &device->write_requests;
|
|
|
- else
|
|
|
- root = &device->read_requests;
|
|
|
- drbd_remove_request_interval(root, req);
|
|
|
- }
|
|
|
-
|
|
|
/* Before we can signal completion to the upper layers,
|
|
|
* we may need to close the current transfer log epoch.
|
|
|
* We are within the request lock, so we can simply compare
|
|
@@ -301,9 +307,24 @@ 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;
|
|
|
+ /* We leave it in the tree, to be able to verify later
|
|
|
+ * write-acks in protocol != C during resync.
|
|
|
+ * But we mark it as "complete", so it won't be counted as
|
|
|
+ * conflict in a multi-primary setup. */
|
|
|
+ req->i.completed = true;
|
|
|
}
|
|
|
+
|
|
|
+ if (req->i.waiting)
|
|
|
+ wake_up(&device->misc_wait);
|
|
|
+
|
|
|
+ /* Either we are about to complete to upper layers,
|
|
|
+ * or we will restart this request.
|
|
|
+ * In either case, the request object will be destroyed soon,
|
|
|
+ * so better remove it from all lists. */
|
|
|
+ list_del_init(&req->req_pending_master_completion);
|
|
|
}
|
|
|
|
|
|
+/* still holds resource->req_lock */
|
|
|
static int drbd_req_put_completion_ref(struct drbd_request *req, struct bio_and_error *m, int put)
|
|
|
{
|
|
|
struct drbd_device *device = req->device;
|
|
@@ -324,12 +345,91 @@ static int drbd_req_put_completion_ref(struct drbd_request *req, struct bio_and_
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+static void set_if_null_req_next(struct drbd_peer_device *peer_device, struct drbd_request *req)
|
|
|
+{
|
|
|
+ struct drbd_connection *connection = peer_device ? peer_device->connection : NULL;
|
|
|
+ if (!connection)
|
|
|
+ return;
|
|
|
+ if (connection->req_next == NULL)
|
|
|
+ connection->req_next = req;
|
|
|
+}
|
|
|
+
|
|
|
+static void advance_conn_req_next(struct drbd_peer_device *peer_device, struct drbd_request *req)
|
|
|
+{
|
|
|
+ struct drbd_connection *connection = peer_device ? peer_device->connection : NULL;
|
|
|
+ if (!connection)
|
|
|
+ return;
|
|
|
+ if (connection->req_next != req)
|
|
|
+ return;
|
|
|
+ list_for_each_entry_continue(req, &connection->transfer_log, tl_requests) {
|
|
|
+ const unsigned s = req->rq_state;
|
|
|
+ if (s & RQ_NET_QUEUED)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (&req->tl_requests == &connection->transfer_log)
|
|
|
+ req = NULL;
|
|
|
+ connection->req_next = req;
|
|
|
+}
|
|
|
+
|
|
|
+static void set_if_null_req_ack_pending(struct drbd_peer_device *peer_device, struct drbd_request *req)
|
|
|
+{
|
|
|
+ struct drbd_connection *connection = peer_device ? peer_device->connection : NULL;
|
|
|
+ if (!connection)
|
|
|
+ return;
|
|
|
+ if (connection->req_ack_pending == NULL)
|
|
|
+ connection->req_ack_pending = req;
|
|
|
+}
|
|
|
+
|
|
|
+static void advance_conn_req_ack_pending(struct drbd_peer_device *peer_device, struct drbd_request *req)
|
|
|
+{
|
|
|
+ struct drbd_connection *connection = peer_device ? peer_device->connection : NULL;
|
|
|
+ if (!connection)
|
|
|
+ return;
|
|
|
+ if (connection->req_ack_pending != req)
|
|
|
+ return;
|
|
|
+ list_for_each_entry_continue(req, &connection->transfer_log, tl_requests) {
|
|
|
+ const unsigned s = req->rq_state;
|
|
|
+ if ((s & RQ_NET_SENT) && (s & RQ_NET_PENDING))
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (&req->tl_requests == &connection->transfer_log)
|
|
|
+ req = NULL;
|
|
|
+ connection->req_ack_pending = req;
|
|
|
+}
|
|
|
+
|
|
|
+static void set_if_null_req_not_net_done(struct drbd_peer_device *peer_device, struct drbd_request *req)
|
|
|
+{
|
|
|
+ struct drbd_connection *connection = peer_device ? peer_device->connection : NULL;
|
|
|
+ if (!connection)
|
|
|
+ return;
|
|
|
+ if (connection->req_not_net_done == NULL)
|
|
|
+ connection->req_not_net_done = req;
|
|
|
+}
|
|
|
+
|
|
|
+static void advance_conn_req_not_net_done(struct drbd_peer_device *peer_device, struct drbd_request *req)
|
|
|
+{
|
|
|
+ struct drbd_connection *connection = peer_device ? peer_device->connection : NULL;
|
|
|
+ if (!connection)
|
|
|
+ return;
|
|
|
+ if (connection->req_not_net_done != req)
|
|
|
+ return;
|
|
|
+ list_for_each_entry_continue(req, &connection->transfer_log, tl_requests) {
|
|
|
+ const unsigned s = req->rq_state;
|
|
|
+ if ((s & RQ_NET_SENT) && !(s & RQ_NET_DONE))
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (&req->tl_requests == &connection->transfer_log)
|
|
|
+ req = NULL;
|
|
|
+ connection->req_not_net_done = req;
|
|
|
+}
|
|
|
+
|
|
|
/* I'd like this to be the only place that manipulates
|
|
|
* req->completion_ref and req->kref. */
|
|
|
static void mod_rq_state(struct drbd_request *req, struct bio_and_error *m,
|
|
|
int clear, int set)
|
|
|
{
|
|
|
struct drbd_device *device = req->device;
|
|
|
+ struct drbd_peer_device *peer_device = first_peer_device(device);
|
|
|
unsigned s = req->rq_state;
|
|
|
int c_put = 0;
|
|
|
int k_put = 0;
|
|
@@ -356,14 +456,23 @@ static void mod_rq_state(struct drbd_request *req, struct bio_and_error *m,
|
|
|
atomic_inc(&req->completion_ref);
|
|
|
}
|
|
|
|
|
|
- if (!(s & RQ_NET_QUEUED) && (set & RQ_NET_QUEUED))
|
|
|
+ if (!(s & RQ_NET_QUEUED) && (set & RQ_NET_QUEUED)) {
|
|
|
atomic_inc(&req->completion_ref);
|
|
|
+ set_if_null_req_next(peer_device, req);
|
|
|
+ }
|
|
|
|
|
|
if (!(s & RQ_EXP_BARR_ACK) && (set & RQ_EXP_BARR_ACK))
|
|
|
kref_get(&req->kref); /* wait for the DONE */
|
|
|
|
|
|
- if (!(s & RQ_NET_SENT) && (set & RQ_NET_SENT))
|
|
|
- atomic_add(req->i.size >> 9, &device->ap_in_flight);
|
|
|
+ if (!(s & RQ_NET_SENT) && (set & RQ_NET_SENT)) {
|
|
|
+ /* potentially already completed in the asender thread */
|
|
|
+ if (!(s & RQ_NET_DONE)) {
|
|
|
+ atomic_add(req->i.size >> 9, &device->ap_in_flight);
|
|
|
+ set_if_null_req_not_net_done(peer_device, req);
|
|
|
+ }
|
|
|
+ if (s & RQ_NET_PENDING)
|
|
|
+ set_if_null_req_ack_pending(peer_device, req);
|
|
|
+ }
|
|
|
|
|
|
if (!(s & RQ_COMPLETION_SUSP) && (set & RQ_COMPLETION_SUSP))
|
|
|
atomic_inc(&req->completion_ref);
|
|
@@ -386,20 +495,34 @@ static void mod_rq_state(struct drbd_request *req, struct bio_and_error *m,
|
|
|
++k_put;
|
|
|
else
|
|
|
++c_put;
|
|
|
+ list_del_init(&req->req_pending_local);
|
|
|
}
|
|
|
|
|
|
if ((s & RQ_NET_PENDING) && (clear & RQ_NET_PENDING)) {
|
|
|
dec_ap_pending(device);
|
|
|
++c_put;
|
|
|
+ req->acked_jif = jiffies;
|
|
|
+ advance_conn_req_ack_pending(peer_device, req);
|
|
|
}
|
|
|
|
|
|
- if ((s & RQ_NET_QUEUED) && (clear & RQ_NET_QUEUED))
|
|
|
+ if ((s & RQ_NET_QUEUED) && (clear & RQ_NET_QUEUED)) {
|
|
|
++c_put;
|
|
|
+ advance_conn_req_next(peer_device, req);
|
|
|
+ }
|
|
|
|
|
|
- if ((s & RQ_EXP_BARR_ACK) && !(s & RQ_NET_DONE) && (set & RQ_NET_DONE)) {
|
|
|
- if (req->rq_state & RQ_NET_SENT)
|
|
|
+ if (!(s & RQ_NET_DONE) && (set & RQ_NET_DONE)) {
|
|
|
+ if (s & RQ_NET_SENT)
|
|
|
atomic_sub(req->i.size >> 9, &device->ap_in_flight);
|
|
|
- ++k_put;
|
|
|
+ if (s & RQ_EXP_BARR_ACK)
|
|
|
+ ++k_put;
|
|
|
+ req->net_done_jif = jiffies;
|
|
|
+
|
|
|
+ /* in ahead/behind mode, or just in case,
|
|
|
+ * before we finally destroy this request,
|
|
|
+ * the caching pointers must not reference it anymore */
|
|
|
+ advance_conn_req_next(peer_device, req);
|
|
|
+ advance_conn_req_ack_pending(peer_device, req);
|
|
|
+ advance_conn_req_not_net_done(peer_device, req);
|
|
|
}
|
|
|
|
|
|
/* potentially complete and destroy */
|
|
@@ -439,6 +562,19 @@ static void drbd_report_io_error(struct drbd_device *device, struct drbd_request
|
|
|
bdevname(device->ldev->backing_bdev, b));
|
|
|
}
|
|
|
|
|
|
+/* Helper for HANDED_OVER_TO_NETWORK.
|
|
|
+ * Is this a protocol A write (neither WRITE_ACK nor RECEIVE_ACK expected)?
|
|
|
+ * Is it also still "PENDING"?
|
|
|
+ * --> If so, clear PENDING and set NET_OK below.
|
|
|
+ * If it is a protocol A write, but not RQ_PENDING anymore, neg-ack was faster
|
|
|
+ * (and we must not set RQ_NET_OK) */
|
|
|
+static inline bool is_pending_write_protocol_A(struct drbd_request *req)
|
|
|
+{
|
|
|
+ return (req->rq_state &
|
|
|
+ (RQ_WRITE|RQ_NET_PENDING|RQ_EXP_WRITE_ACK|RQ_EXP_RECEIVE_ACK))
|
|
|
+ == (RQ_WRITE|RQ_NET_PENDING);
|
|
|
+}
|
|
|
+
|
|
|
/* obviously this could be coded as many single functions
|
|
|
* instead of one huge switch,
|
|
|
* or by putting the code directly in the respective locations
|
|
@@ -454,7 +590,9 @@ static void drbd_report_io_error(struct drbd_device *device, struct drbd_request
|
|
|
int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
|
|
struct bio_and_error *m)
|
|
|
{
|
|
|
- struct drbd_device *device = req->device;
|
|
|
+ struct drbd_device *const device = req->device;
|
|
|
+ struct drbd_peer_device *const peer_device = first_peer_device(device);
|
|
|
+ struct drbd_connection *const connection = peer_device ? peer_device->connection : NULL;
|
|
|
struct net_conf *nc;
|
|
|
int p, rv = 0;
|
|
|
|
|
@@ -477,7 +615,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
|
|
* and from w_read_retry_remote */
|
|
|
D_ASSERT(device, !(req->rq_state & RQ_NET_MASK));
|
|
|
rcu_read_lock();
|
|
|
- nc = rcu_dereference(first_peer_device(device)->connection->net_conf);
|
|
|
+ nc = rcu_dereference(connection->net_conf);
|
|
|
p = nc->wire_protocol;
|
|
|
rcu_read_unlock();
|
|
|
req->rq_state |=
|
|
@@ -549,7 +687,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
|
|
D_ASSERT(device, (req->rq_state & RQ_LOCAL_MASK) == 0);
|
|
|
mod_rq_state(req, m, 0, RQ_NET_QUEUED);
|
|
|
req->w.cb = w_send_read_req;
|
|
|
- drbd_queue_work(&first_peer_device(device)->connection->sender_work,
|
|
|
+ drbd_queue_work(&connection->sender_work,
|
|
|
&req->w);
|
|
|
break;
|
|
|
|
|
@@ -585,23 +723,23 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
|
|
D_ASSERT(device, req->rq_state & RQ_NET_PENDING);
|
|
|
mod_rq_state(req, m, 0, RQ_NET_QUEUED|RQ_EXP_BARR_ACK);
|
|
|
req->w.cb = w_send_dblock;
|
|
|
- drbd_queue_work(&first_peer_device(device)->connection->sender_work,
|
|
|
+ drbd_queue_work(&connection->sender_work,
|
|
|
&req->w);
|
|
|
|
|
|
/* close the epoch, in case it outgrew the limit */
|
|
|
rcu_read_lock();
|
|
|
- nc = rcu_dereference(first_peer_device(device)->connection->net_conf);
|
|
|
+ nc = rcu_dereference(connection->net_conf);
|
|
|
p = nc->max_epoch_size;
|
|
|
rcu_read_unlock();
|
|
|
- if (first_peer_device(device)->connection->current_tle_writes >= p)
|
|
|
- start_new_tl_epoch(first_peer_device(device)->connection);
|
|
|
+ if (connection->current_tle_writes >= p)
|
|
|
+ start_new_tl_epoch(connection);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case QUEUE_FOR_SEND_OOS:
|
|
|
mod_rq_state(req, m, 0, RQ_NET_QUEUED);
|
|
|
req->w.cb = w_send_out_of_sync;
|
|
|
- drbd_queue_work(&first_peer_device(device)->connection->sender_work,
|
|
|
+ drbd_queue_work(&connection->sender_work,
|
|
|
&req->w);
|
|
|
break;
|
|
|
|
|
@@ -615,18 +753,16 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
|
|
|
|
|
case HANDED_OVER_TO_NETWORK:
|
|
|
/* assert something? */
|
|
|
- if (bio_data_dir(req->master_bio) == WRITE &&
|
|
|
- !(req->rq_state & (RQ_EXP_RECEIVE_ACK | RQ_EXP_WRITE_ACK))) {
|
|
|
+ if (is_pending_write_protocol_A(req))
|
|
|
/* this is what is dangerous about protocol A:
|
|
|
* pretend it was successfully written on the peer. */
|
|
|
- if (req->rq_state & RQ_NET_PENDING)
|
|
|
- mod_rq_state(req, m, RQ_NET_PENDING, RQ_NET_OK);
|
|
|
- /* else: neg-ack was faster... */
|
|
|
- /* it is still not yet RQ_NET_DONE until the
|
|
|
- * corresponding epoch barrier got acked as well,
|
|
|
- * so we know what to dirty on connection loss */
|
|
|
- }
|
|
|
- mod_rq_state(req, m, RQ_NET_QUEUED, RQ_NET_SENT);
|
|
|
+ mod_rq_state(req, m, RQ_NET_QUEUED|RQ_NET_PENDING,
|
|
|
+ RQ_NET_SENT|RQ_NET_OK);
|
|
|
+ else
|
|
|
+ mod_rq_state(req, m, RQ_NET_QUEUED, RQ_NET_SENT);
|
|
|
+ /* It is still not yet RQ_NET_DONE until the
|
|
|
+ * corresponding epoch barrier got acked as well,
|
|
|
+ * so we know what to dirty on connection loss. */
|
|
|
break;
|
|
|
|
|
|
case OOS_HANDED_TO_NETWORK:
|
|
@@ -658,12 +794,13 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
|
|
case WRITE_ACKED_BY_PEER_AND_SIS:
|
|
|
req->rq_state |= RQ_NET_SIS;
|
|
|
case WRITE_ACKED_BY_PEER:
|
|
|
- D_ASSERT(device, req->rq_state & RQ_EXP_WRITE_ACK);
|
|
|
- /* protocol C; successfully written on peer.
|
|
|
+ /* Normal operation protocol C: successfully written on peer.
|
|
|
+ * During resync, even in protocol != C,
|
|
|
+ * we requested an explicit write ack anyways.
|
|
|
+ * Which means we cannot even assert anything here.
|
|
|
* Nothing more to do here.
|
|
|
* We want to keep the tl in place for all protocols, to cater
|
|
|
* for volatile write-back caches on lower level devices. */
|
|
|
-
|
|
|
goto ack_common;
|
|
|
case RECV_ACKED_BY_PEER:
|
|
|
D_ASSERT(device, req->rq_state & RQ_EXP_RECEIVE_ACK);
|
|
@@ -671,7 +808,6 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
|
|
* see also notes above in HANDED_OVER_TO_NETWORK about
|
|
|
* protocol != C */
|
|
|
ack_common:
|
|
|
- D_ASSERT(device, req->rq_state & RQ_NET_PENDING);
|
|
|
mod_rq_state(req, m, RQ_NET_PENDING, RQ_NET_OK);
|
|
|
break;
|
|
|
|
|
@@ -714,7 +850,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
|
|
|
|
|
get_ldev(device); /* always succeeds in this call path */
|
|
|
req->w.cb = w_restart_disk_io;
|
|
|
- drbd_queue_work(&first_peer_device(device)->connection->sender_work,
|
|
|
+ drbd_queue_work(&connection->sender_work,
|
|
|
&req->w);
|
|
|
break;
|
|
|
|
|
@@ -736,7 +872,8 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
|
|
|
|
|
mod_rq_state(req, m, RQ_COMPLETION_SUSP, RQ_NET_QUEUED|RQ_NET_PENDING);
|
|
|
if (req->w.cb) {
|
|
|
- drbd_queue_work(&first_peer_device(device)->connection->sender_work,
|
|
|
+ /* w.cb expected to be w_send_dblock, or w_send_read_req */
|
|
|
+ drbd_queue_work(&connection->sender_work,
|
|
|
&req->w);
|
|
|
rv = req->rq_state & RQ_WRITE ? MR_WRITE : MR_READ;
|
|
|
} /* else: FIXME can this happen? */
|
|
@@ -769,7 +906,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
|
|
break;
|
|
|
|
|
|
case QUEUE_AS_DRBD_BARRIER:
|
|
|
- start_new_tl_epoch(first_peer_device(device)->connection);
|
|
|
+ start_new_tl_epoch(connection);
|
|
|
mod_rq_state(req, m, 0, RQ_NET_OK|RQ_NET_DONE);
|
|
|
break;
|
|
|
};
|
|
@@ -886,6 +1023,9 @@ static void maybe_pull_ahead(struct drbd_device *device)
|
|
|
connection->agreed_pro_version < 96)
|
|
|
return;
|
|
|
|
|
|
+ if (on_congestion == OC_PULL_AHEAD && device->state.conn == C_AHEAD)
|
|
|
+ return; /* nothing to do ... */
|
|
|
+
|
|
|
/* If I don't even have good local storage, we can not reasonably try
|
|
|
* to pull ahead of the peer. We also need the local reference to make
|
|
|
* sure device->act_log is there.
|
|
@@ -1021,6 +1161,7 @@ drbd_submit_req_private_bio(struct drbd_request *req)
|
|
|
* stable storage, and this is a WRITE, we may not even submit
|
|
|
* this bio. */
|
|
|
if (get_ldev(device)) {
|
|
|
+ req->pre_submit_jif = jiffies;
|
|
|
if (drbd_insert_fault(device,
|
|
|
rw == WRITE ? DRBD_FAULT_DT_WR
|
|
|
: rw == READ ? DRBD_FAULT_DT_RD
|
|
@@ -1035,10 +1176,14 @@ drbd_submit_req_private_bio(struct drbd_request *req)
|
|
|
|
|
|
static void drbd_queue_write(struct drbd_device *device, struct drbd_request *req)
|
|
|
{
|
|
|
- spin_lock(&device->submit.lock);
|
|
|
+ spin_lock_irq(&device->resource->req_lock);
|
|
|
list_add_tail(&req->tl_requests, &device->submit.writes);
|
|
|
- spin_unlock(&device->submit.lock);
|
|
|
+ list_add_tail(&req->req_pending_master_completion,
|
|
|
+ &device->pending_master_completion[1 /* WRITE */]);
|
|
|
+ spin_unlock_irq(&device->resource->req_lock);
|
|
|
queue_work(device->submit.wq, &device->submit.worker);
|
|
|
+ /* do_submit() may sleep internally on al_wait, too */
|
|
|
+ wake_up(&device->al_wait);
|
|
|
}
|
|
|
|
|
|
/* returns the new drbd_request pointer, if the caller is expected to
|
|
@@ -1047,7 +1192,7 @@ static void drbd_queue_write(struct drbd_device *device, struct drbd_request *re
|
|
|
* Returns ERR_PTR(-ENOMEM) if we cannot allocate a drbd_request.
|
|
|
*/
|
|
|
static struct drbd_request *
|
|
|
-drbd_request_prepare(struct drbd_device *device, struct bio *bio, unsigned long start_time)
|
|
|
+drbd_request_prepare(struct drbd_device *device, struct bio *bio, unsigned long start_jif)
|
|
|
{
|
|
|
const int rw = bio_data_dir(bio);
|
|
|
struct drbd_request *req;
|
|
@@ -1062,7 +1207,7 @@ drbd_request_prepare(struct drbd_device *device, struct bio *bio, unsigned long
|
|
|
bio_endio(bio, -ENOMEM);
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
}
|
|
|
- req->start_time = start_time;
|
|
|
+ req->start_jif = start_jif;
|
|
|
|
|
|
if (!get_ldev(device)) {
|
|
|
bio_put(req->private_bio);
|
|
@@ -1075,10 +1220,12 @@ drbd_request_prepare(struct drbd_device *device, struct bio *bio, unsigned long
|
|
|
if (rw == WRITE && req->private_bio && req->i.size
|
|
|
&& !test_bit(AL_SUSPENDED, &device->flags)) {
|
|
|
if (!drbd_al_begin_io_fastpath(device, &req->i)) {
|
|
|
+ atomic_inc(&device->ap_actlog_cnt);
|
|
|
drbd_queue_write(device, req);
|
|
|
return NULL;
|
|
|
}
|
|
|
req->rq_state |= RQ_IN_ACT_LOG;
|
|
|
+ req->in_actlog_jif = jiffies;
|
|
|
}
|
|
|
|
|
|
return req;
|
|
@@ -1086,11 +1233,13 @@ drbd_request_prepare(struct drbd_device *device, struct bio *bio, unsigned long
|
|
|
|
|
|
static void drbd_send_and_submit(struct drbd_device *device, struct drbd_request *req)
|
|
|
{
|
|
|
+ struct drbd_resource *resource = device->resource;
|
|
|
const int rw = bio_rw(req->master_bio);
|
|
|
struct bio_and_error m = { NULL, };
|
|
|
bool no_remote = false;
|
|
|
+ bool submit_private_bio = false;
|
|
|
|
|
|
- spin_lock_irq(&device->resource->req_lock);
|
|
|
+ spin_lock_irq(&resource->req_lock);
|
|
|
if (rw == WRITE) {
|
|
|
/* This may temporarily give up the req_lock,
|
|
|
* but will re-aquire it before it returns here.
|
|
@@ -1148,13 +1297,18 @@ static void drbd_send_and_submit(struct drbd_device *device, struct drbd_request
|
|
|
no_remote = true;
|
|
|
}
|
|
|
|
|
|
+ /* If it took the fast path in drbd_request_prepare, add it here.
|
|
|
+ * The slow path has added it already. */
|
|
|
+ if (list_empty(&req->req_pending_master_completion))
|
|
|
+ list_add_tail(&req->req_pending_master_completion,
|
|
|
+ &device->pending_master_completion[rw == WRITE]);
|
|
|
if (req->private_bio) {
|
|
|
/* needs to be marked within the same spinlock */
|
|
|
+ list_add_tail(&req->req_pending_local,
|
|
|
+ &device->pending_completion[rw == WRITE]);
|
|
|
_req_mod(req, TO_BE_SUBMITTED);
|
|
|
/* but we need to give up the spinlock to submit */
|
|
|
- spin_unlock_irq(&device->resource->req_lock);
|
|
|
- drbd_submit_req_private_bio(req);
|
|
|
- spin_lock_irq(&device->resource->req_lock);
|
|
|
+ submit_private_bio = true;
|
|
|
} else if (no_remote) {
|
|
|
nodata:
|
|
|
if (__ratelimit(&drbd_ratelimit_state))
|
|
@@ -1167,15 +1321,23 @@ nodata:
|
|
|
out:
|
|
|
if (drbd_req_put_completion_ref(req, &m, 1))
|
|
|
kref_put(&req->kref, drbd_req_destroy);
|
|
|
- spin_unlock_irq(&device->resource->req_lock);
|
|
|
-
|
|
|
+ spin_unlock_irq(&resource->req_lock);
|
|
|
+
|
|
|
+ /* Even though above is a kref_put(), this is safe.
|
|
|
+ * As long as we still need to submit our private bio,
|
|
|
+ * we hold a completion ref, and the request cannot disappear.
|
|
|
+ * If however this request did not even have a private bio to submit
|
|
|
+ * (e.g. remote read), req may already be invalid now.
|
|
|
+ * That's why we cannot check on req->private_bio. */
|
|
|
+ if (submit_private_bio)
|
|
|
+ drbd_submit_req_private_bio(req);
|
|
|
if (m.bio)
|
|
|
complete_master_bio(device, &m);
|
|
|
}
|
|
|
|
|
|
-void __drbd_make_request(struct drbd_device *device, struct bio *bio, unsigned long start_time)
|
|
|
+void __drbd_make_request(struct drbd_device *device, struct bio *bio, unsigned long start_jif)
|
|
|
{
|
|
|
- struct drbd_request *req = drbd_request_prepare(device, bio, start_time);
|
|
|
+ struct drbd_request *req = drbd_request_prepare(device, bio, start_jif);
|
|
|
if (IS_ERR_OR_NULL(req))
|
|
|
return;
|
|
|
drbd_send_and_submit(device, req);
|
|
@@ -1194,6 +1356,8 @@ static void submit_fast_path(struct drbd_device *device, struct list_head *incom
|
|
|
continue;
|
|
|
|
|
|
req->rq_state |= RQ_IN_ACT_LOG;
|
|
|
+ req->in_actlog_jif = jiffies;
|
|
|
+ atomic_dec(&device->ap_actlog_cnt);
|
|
|
}
|
|
|
|
|
|
list_del_init(&req->tl_requests);
|
|
@@ -1203,7 +1367,8 @@ static void submit_fast_path(struct drbd_device *device, struct list_head *incom
|
|
|
|
|
|
static bool prepare_al_transaction_nonblock(struct drbd_device *device,
|
|
|
struct list_head *incoming,
|
|
|
- struct list_head *pending)
|
|
|
+ struct list_head *pending,
|
|
|
+ struct list_head *later)
|
|
|
{
|
|
|
struct drbd_request *req, *tmp;
|
|
|
int wake = 0;
|
|
@@ -1212,45 +1377,105 @@ static bool prepare_al_transaction_nonblock(struct drbd_device *device,
|
|
|
spin_lock_irq(&device->al_lock);
|
|
|
list_for_each_entry_safe(req, tmp, incoming, tl_requests) {
|
|
|
err = drbd_al_begin_io_nonblock(device, &req->i);
|
|
|
+ if (err == -ENOBUFS)
|
|
|
+ break;
|
|
|
if (err == -EBUSY)
|
|
|
wake = 1;
|
|
|
if (err)
|
|
|
- continue;
|
|
|
- req->rq_state |= RQ_IN_ACT_LOG;
|
|
|
- list_move_tail(&req->tl_requests, pending);
|
|
|
+ list_move_tail(&req->tl_requests, later);
|
|
|
+ else
|
|
|
+ list_move_tail(&req->tl_requests, pending);
|
|
|
}
|
|
|
spin_unlock_irq(&device->al_lock);
|
|
|
if (wake)
|
|
|
wake_up(&device->al_wait);
|
|
|
-
|
|
|
return !list_empty(pending);
|
|
|
}
|
|
|
|
|
|
+void send_and_submit_pending(struct drbd_device *device, struct list_head *pending)
|
|
|
+{
|
|
|
+ struct drbd_request *req, *tmp;
|
|
|
+
|
|
|
+ list_for_each_entry_safe(req, tmp, pending, tl_requests) {
|
|
|
+ req->rq_state |= RQ_IN_ACT_LOG;
|
|
|
+ req->in_actlog_jif = jiffies;
|
|
|
+ atomic_dec(&device->ap_actlog_cnt);
|
|
|
+ list_del_init(&req->tl_requests);
|
|
|
+ drbd_send_and_submit(device, req);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void do_submit(struct work_struct *ws)
|
|
|
{
|
|
|
struct drbd_device *device = container_of(ws, struct drbd_device, submit.worker);
|
|
|
- LIST_HEAD(incoming);
|
|
|
- LIST_HEAD(pending);
|
|
|
- struct drbd_request *req, *tmp;
|
|
|
+ LIST_HEAD(incoming); /* from drbd_make_request() */
|
|
|
+ LIST_HEAD(pending); /* to be submitted after next AL-transaction commit */
|
|
|
+ LIST_HEAD(busy); /* blocked by resync requests */
|
|
|
+
|
|
|
+ /* grab new incoming requests */
|
|
|
+ spin_lock_irq(&device->resource->req_lock);
|
|
|
+ list_splice_tail_init(&device->submit.writes, &incoming);
|
|
|
+ spin_unlock_irq(&device->resource->req_lock);
|
|
|
|
|
|
for (;;) {
|
|
|
- spin_lock(&device->submit.lock);
|
|
|
- list_splice_tail_init(&device->submit.writes, &incoming);
|
|
|
- spin_unlock(&device->submit.lock);
|
|
|
+ DEFINE_WAIT(wait);
|
|
|
|
|
|
+ /* move used-to-be-busy back to front of incoming */
|
|
|
+ list_splice_init(&busy, &incoming);
|
|
|
submit_fast_path(device, &incoming);
|
|
|
if (list_empty(&incoming))
|
|
|
break;
|
|
|
|
|
|
-skip_fast_path:
|
|
|
- wait_event(device->al_wait, prepare_al_transaction_nonblock(device, &incoming, &pending));
|
|
|
- /* Maybe more was queued, while we prepared the transaction?
|
|
|
- * Try to stuff them into this transaction as well.
|
|
|
- * Be strictly non-blocking here, no wait_event, we already
|
|
|
- * have something to commit.
|
|
|
- * Stop if we don't make any more progres.
|
|
|
- */
|
|
|
for (;;) {
|
|
|
+ prepare_to_wait(&device->al_wait, &wait, TASK_UNINTERRUPTIBLE);
|
|
|
+
|
|
|
+ list_splice_init(&busy, &incoming);
|
|
|
+ prepare_al_transaction_nonblock(device, &incoming, &pending, &busy);
|
|
|
+ if (!list_empty(&pending))
|
|
|
+ break;
|
|
|
+
|
|
|
+ schedule();
|
|
|
+
|
|
|
+ /* If all currently "hot" activity log extents are kept busy by
|
|
|
+ * incoming requests, we still must not totally starve new
|
|
|
+ * requests to "cold" extents.
|
|
|
+ * Something left on &incoming means there had not been
|
|
|
+ * enough update slots available, and the activity log
|
|
|
+ * has been marked as "starving".
|
|
|
+ *
|
|
|
+ * Try again now, without looking for new requests,
|
|
|
+ * effectively blocking all new requests until we made
|
|
|
+ * at least _some_ progress with what we currently have.
|
|
|
+ */
|
|
|
+ if (!list_empty(&incoming))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Nothing moved to pending, but nothing left
|
|
|
+ * on incoming: all moved to busy!
|
|
|
+ * Grab new and iterate. */
|
|
|
+ spin_lock_irq(&device->resource->req_lock);
|
|
|
+ list_splice_tail_init(&device->submit.writes, &incoming);
|
|
|
+ spin_unlock_irq(&device->resource->req_lock);
|
|
|
+ }
|
|
|
+ finish_wait(&device->al_wait, &wait);
|
|
|
+
|
|
|
+ /* If the transaction was full, before all incoming requests
|
|
|
+ * had been processed, skip ahead to commit, and iterate
|
|
|
+ * without splicing in more incoming requests from upper layers.
|
|
|
+ *
|
|
|
+ * Else, if all incoming have been processed,
|
|
|
+ * they have become either "pending" (to be submitted after
|
|
|
+ * next transaction commit) or "busy" (blocked by resync).
|
|
|
+ *
|
|
|
+ * Maybe more was queued, while we prepared the transaction?
|
|
|
+ * Try to stuff those into this transaction as well.
|
|
|
+ * Be strictly non-blocking here,
|
|
|
+ * we already have something to commit.
|
|
|
+ *
|
|
|
+ * Commit if we don't make any more progres.
|
|
|
+ */
|
|
|
+
|
|
|
+ while (list_empty(&incoming)) {
|
|
|
LIST_HEAD(more_pending);
|
|
|
LIST_HEAD(more_incoming);
|
|
|
bool made_progress;
|
|
@@ -1260,55 +1485,32 @@ skip_fast_path:
|
|
|
if (list_empty(&device->submit.writes))
|
|
|
break;
|
|
|
|
|
|
- spin_lock(&device->submit.lock);
|
|
|
+ spin_lock_irq(&device->resource->req_lock);
|
|
|
list_splice_tail_init(&device->submit.writes, &more_incoming);
|
|
|
- spin_unlock(&device->submit.lock);
|
|
|
+ spin_unlock_irq(&device->resource->req_lock);
|
|
|
|
|
|
if (list_empty(&more_incoming))
|
|
|
break;
|
|
|
|
|
|
- made_progress = prepare_al_transaction_nonblock(device, &more_incoming, &more_pending);
|
|
|
+ made_progress = prepare_al_transaction_nonblock(device, &more_incoming, &more_pending, &busy);
|
|
|
|
|
|
list_splice_tail_init(&more_pending, &pending);
|
|
|
list_splice_tail_init(&more_incoming, &incoming);
|
|
|
-
|
|
|
if (!made_progress)
|
|
|
break;
|
|
|
}
|
|
|
- drbd_al_begin_io_commit(device, false);
|
|
|
-
|
|
|
- list_for_each_entry_safe(req, tmp, &pending, tl_requests) {
|
|
|
- list_del_init(&req->tl_requests);
|
|
|
- drbd_send_and_submit(device, req);
|
|
|
- }
|
|
|
|
|
|
- /* If all currently hot activity log extents are kept busy by
|
|
|
- * incoming requests, we still must not totally starve new
|
|
|
- * requests to cold extents. In that case, prepare one request
|
|
|
- * in blocking mode. */
|
|
|
- list_for_each_entry_safe(req, tmp, &incoming, tl_requests) {
|
|
|
- list_del_init(&req->tl_requests);
|
|
|
- req->rq_state |= RQ_IN_ACT_LOG;
|
|
|
- if (!drbd_al_begin_io_prepare(device, &req->i)) {
|
|
|
- /* Corresponding extent was hot after all? */
|
|
|
- drbd_send_and_submit(device, req);
|
|
|
- } else {
|
|
|
- /* Found a request to a cold extent.
|
|
|
- * Put on "pending" list,
|
|
|
- * and try to cumulate with more. */
|
|
|
- list_add(&req->tl_requests, &pending);
|
|
|
- goto skip_fast_path;
|
|
|
- }
|
|
|
- }
|
|
|
+ drbd_al_begin_io_commit(device);
|
|
|
+ send_and_submit_pending(device, &pending);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void drbd_make_request(struct request_queue *q, struct bio *bio)
|
|
|
{
|
|
|
struct drbd_device *device = (struct drbd_device *) q->queuedata;
|
|
|
- unsigned long start_time;
|
|
|
+ unsigned long start_jif;
|
|
|
|
|
|
- start_time = jiffies;
|
|
|
+ start_jif = jiffies;
|
|
|
|
|
|
/*
|
|
|
* what we "blindly" assume:
|
|
@@ -1316,7 +1518,7 @@ void drbd_make_request(struct request_queue *q, struct bio *bio)
|
|
|
D_ASSERT(device, IS_ALIGNED(bio->bi_iter.bi_size, 512));
|
|
|
|
|
|
inc_ap_bio(device);
|
|
|
- __drbd_make_request(device, bio, start_time);
|
|
|
+ __drbd_make_request(device, bio, start_jif);
|
|
|
}
|
|
|
|
|
|
/* This is called by bio_add_page().
|
|
@@ -1353,36 +1555,13 @@ int drbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bvm, struct
|
|
|
return limit;
|
|
|
}
|
|
|
|
|
|
-static void find_oldest_requests(
|
|
|
- struct drbd_connection *connection,
|
|
|
- struct drbd_device *device,
|
|
|
- struct drbd_request **oldest_req_waiting_for_peer,
|
|
|
- struct drbd_request **oldest_req_waiting_for_disk)
|
|
|
-{
|
|
|
- struct drbd_request *r;
|
|
|
- *oldest_req_waiting_for_peer = NULL;
|
|
|
- *oldest_req_waiting_for_disk = NULL;
|
|
|
- list_for_each_entry(r, &connection->transfer_log, tl_requests) {
|
|
|
- const unsigned s = r->rq_state;
|
|
|
- if (!*oldest_req_waiting_for_peer
|
|
|
- && ((s & RQ_NET_MASK) && !(s & RQ_NET_DONE)))
|
|
|
- *oldest_req_waiting_for_peer = r;
|
|
|
-
|
|
|
- if (!*oldest_req_waiting_for_disk
|
|
|
- && (s & RQ_LOCAL_PENDING) && r->device == device)
|
|
|
- *oldest_req_waiting_for_disk = r;
|
|
|
-
|
|
|
- if (*oldest_req_waiting_for_peer && *oldest_req_waiting_for_disk)
|
|
|
- break;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
void request_timer_fn(unsigned long data)
|
|
|
{
|
|
|
struct drbd_device *device = (struct drbd_device *) data;
|
|
|
struct drbd_connection *connection = first_peer_device(device)->connection;
|
|
|
- struct drbd_request *req_disk, *req_peer; /* oldest request */
|
|
|
+ struct drbd_request *req_read, *req_write, *req_peer; /* oldest request */
|
|
|
struct net_conf *nc;
|
|
|
+ unsigned long oldest_submit_jif;
|
|
|
unsigned long ent = 0, dt = 0, et, nt; /* effective timeout = ko_count * timeout */
|
|
|
unsigned long now;
|
|
|
|
|
@@ -1403,14 +1582,31 @@ void request_timer_fn(unsigned long data)
|
|
|
return; /* Recurring timer stopped */
|
|
|
|
|
|
now = jiffies;
|
|
|
+ nt = now + et;
|
|
|
|
|
|
spin_lock_irq(&device->resource->req_lock);
|
|
|
- find_oldest_requests(connection, device, &req_peer, &req_disk);
|
|
|
- if (req_peer == NULL && req_disk == NULL) {
|
|
|
- spin_unlock_irq(&device->resource->req_lock);
|
|
|
- mod_timer(&device->request_timer, now + et);
|
|
|
- return;
|
|
|
- }
|
|
|
+ req_read = list_first_entry_or_null(&device->pending_completion[0], struct drbd_request, req_pending_local);
|
|
|
+ req_write = list_first_entry_or_null(&device->pending_completion[1], struct drbd_request, req_pending_local);
|
|
|
+ req_peer = connection->req_not_net_done;
|
|
|
+ /* maybe the oldest request waiting for the peer is in fact still
|
|
|
+ * blocking in tcp sendmsg */
|
|
|
+ if (!req_peer && connection->req_next && connection->req_next->pre_send_jif)
|
|
|
+ req_peer = connection->req_next;
|
|
|
+
|
|
|
+ /* evaluate the oldest peer request only in one timer! */
|
|
|
+ if (req_peer && req_peer->device != device)
|
|
|
+ req_peer = NULL;
|
|
|
+
|
|
|
+ /* do we have something to evaluate? */
|
|
|
+ if (req_peer == NULL && req_write == NULL && req_read == NULL)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ oldest_submit_jif =
|
|
|
+ (req_write && req_read)
|
|
|
+ ? ( time_before(req_write->pre_submit_jif, req_read->pre_submit_jif)
|
|
|
+ ? req_write->pre_submit_jif : req_read->pre_submit_jif )
|
|
|
+ : req_write ? req_write->pre_submit_jif
|
|
|
+ : req_read ? req_read->pre_submit_jif : now;
|
|
|
|
|
|
/* The request is considered timed out, if
|
|
|
* - we have some effective timeout from the configuration,
|
|
@@ -1429,13 +1625,13 @@ void request_timer_fn(unsigned long data)
|
|
|
* to expire twice (worst case) to become effective. Good enough.
|
|
|
*/
|
|
|
if (ent && req_peer &&
|
|
|
- time_after(now, req_peer->start_time + ent) &&
|
|
|
+ time_after(now, req_peer->pre_send_jif + ent) &&
|
|
|
!time_in_range(now, connection->last_reconnect_jif, connection->last_reconnect_jif + ent)) {
|
|
|
drbd_warn(device, "Remote failed to finish a request within ko-count * timeout\n");
|
|
|
_drbd_set_state(_NS(device, conn, C_TIMEOUT), CS_VERBOSE | CS_HARD, NULL);
|
|
|
}
|
|
|
- if (dt && req_disk &&
|
|
|
- time_after(now, req_disk->start_time + dt) &&
|
|
|
+ if (dt && oldest_submit_jif != now &&
|
|
|
+ time_after(now, oldest_submit_jif + dt) &&
|
|
|
!time_in_range(now, device->last_reattach_jif, device->last_reattach_jif + dt)) {
|
|
|
drbd_warn(device, "Local backing device failed to meet the disk-timeout\n");
|
|
|
__drbd_chk_io_error(device, DRBD_FORCE_DETACH);
|
|
@@ -1443,11 +1639,12 @@ void request_timer_fn(unsigned long data)
|
|
|
|
|
|
/* Reschedule timer for the nearest not already expired timeout.
|
|
|
* Fallback to now + min(effective network timeout, disk timeout). */
|
|
|
- ent = (ent && req_peer && time_before(now, req_peer->start_time + ent))
|
|
|
- ? req_peer->start_time + ent : now + et;
|
|
|
- dt = (dt && req_disk && time_before(now, req_disk->start_time + dt))
|
|
|
- ? req_disk->start_time + dt : now + et;
|
|
|
+ ent = (ent && req_peer && time_before(now, req_peer->pre_send_jif + ent))
|
|
|
+ ? req_peer->pre_send_jif + ent : now + et;
|
|
|
+ dt = (dt && oldest_submit_jif != now && time_before(now, oldest_submit_jif + dt))
|
|
|
+ ? oldest_submit_jif + dt : now + et;
|
|
|
nt = time_before(ent, dt) ? ent : dt;
|
|
|
+out:
|
|
|
spin_unlock_irq(&connection->resource->req_lock);
|
|
|
mod_timer(&device->request_timer, nt);
|
|
|
}
|