Merge tag 'rdma-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[firefly-linux-kernel-4.4.55.git] / drivers / block / drbd / drbd_req.c
index 3779c8d2875bb00529853e1e2c4d6f6a5a083b17..09803d0d5207ce7fccffc5c4a3cb0229071566cd 100644 (file)
@@ -522,6 +522,13 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
                mod_rq_state(req, m, RQ_LOCAL_PENDING, RQ_LOCAL_COMPLETED);
                break;
 
+       case DISCARD_COMPLETED_NOTSUPP:
+       case DISCARD_COMPLETED_WITH_ERROR:
+               /* I'd rather not detach from local disk just because it
+                * failed a REQ_DISCARD. */
+               mod_rq_state(req, m, RQ_LOCAL_PENDING, RQ_LOCAL_COMPLETED);
+               break;
+
        case QUEUE_FOR_NET_READ:
                /* READ or READA, and
                 * no local disk,
@@ -1235,6 +1242,7 @@ void do_submit(struct work_struct *ws)
                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.
@@ -1273,6 +1281,25 @@ void do_submit(struct work_struct *ws)
                        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;
+                       }
+               }
        }
 }
 
@@ -1326,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;
@@ -1366,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;
@@ -1389,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);
 }