drbd: evaluate disk and network timeout on different requests
authorLars Ellenberg <lars.ellenberg@linbit.com>
Mon, 28 Apr 2014 16:43:31 +0000 (18:43 +0200)
committerJens Axboe <axboe@fb.com>
Wed, 30 Apr 2014 19:46:55 +0000 (13:46 -0600)
Just because it is the oldest not yet completed request
does not make it the oldest request waiting for disk.
Or waiting for the peer.

And we completely missed already completed requests
that would still hold references to activity log extents,
waiting only for the barrier ack.

Find two oldest not yet completely processed requests,
one that is still waiting for local completion,
and one that is still waiting for some response from the peer.
These may or may not be the same request object.

Then separately apply the network and disk timeouts, respectively.

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
drivers/block/drbd/drbd_req.c

index 99411bf9d901e4527ec7a19224b5a71904c1d5e3..09803d0d5207ce7fccffc5c4a3cb0229071566cd 100644 (file)
@@ -1353,23 +1353,35 @@ int drbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bvm, struct
        return limit;
 }
 
-static struct drbd_request *find_oldest_request(struct drbd_connection *connection)
+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)
 {
-       /* Walk the transfer log,
-        * and find the oldest not yet completed request */
        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) {
-               if (atomic_read(&r->completion_ref))
-                       return r;
+               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;
        }
-       return NULL;
 }
 
 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; /* oldest request */
+       struct drbd_request *req_disk, *req_peer; /* oldest request */
        struct net_conf *nc;
        unsigned long ent = 0, dt = 0, et, nt; /* effective timeout = ko_count * timeout */
        unsigned long now;
@@ -1393,8 +1405,8 @@ void request_timer_fn(unsigned long data)
        now = jiffies;
 
        spin_lock_irq(&device->resource->req_lock);
-       req = find_oldest_request(connection);
-       if (!req) {
+       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;
@@ -1416,19 +1428,26 @@ void request_timer_fn(unsigned long data)
         * ~198 days with 250 HZ, we have a window where the timeout would need
         * to expire twice (worst case) to become effective. Good enough.
         */
-       if (ent && req->rq_state & RQ_NET_PENDING &&
-                time_after(now, req->start_time + ent) &&
+       if (ent && req_peer &&
+                time_after(now, req_peer->start_time + 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->rq_state & RQ_LOCAL_PENDING && req->device == device &&
-                time_after(now, req->start_time + dt) &&
+       if (dt && req_disk &&
+                time_after(now, req_disk->start_time + 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);
        }
-       nt = (time_after(now, req->start_time + et) ? now : req->start_time) + et;
+
+       /* 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;
+       nt = time_before(ent, dt) ? ent : dt;
        spin_unlock_irq(&connection->resource->req_lock);
        mod_timer(&device->request_timer, nt);
 }
This page took 0.033724 seconds and 5 git commands to generate.