@@ -217,6 +217,62 @@ io_entity_sched_data(struct io_entity *entity)
return &iog_of(parent_entity(entity))->sched_data;
}
+static inline void set_late_preemption(struct elevator_queue *eq,
+ struct io_queue *active_ioq, struct io_queue *new_ioq)
+{
+ struct io_group *new_iog;
+
+ if (!active_ioq)
+ return;
+
+ /* For the time being, set late preempt only if new queue is sync */
+ if (!elv_ioq_sync(new_ioq))
+ return;
+
+ new_iog = ioq_to_io_group(new_ioq);
+
+ if (ioq_to_io_group(active_ioq) != new_iog
+ && !new_iog->late_preemption) {
+ new_iog->late_preemption = 1;
+ elv_log_ioq(eq->efqd, new_ioq, "set late preempt");
+ }
+}
+
+static inline void reset_late_preemption(struct elevator_queue *eq,
+ struct io_group *iog, struct io_queue *ioq)
+{
+ if (iog->late_preemption) {
+ iog->late_preemption = 0;
+ elv_log_ioq(eq->efqd, ioq, "reset late preempt");
+ }
+}
+
+static inline void
+check_late_preemption(struct elevator_queue *eq, struct io_queue *ioq)
+{
+ struct io_group *iog = ioq_to_io_group(ioq);
+
+ if (!iog->late_preemption)
+ return;
+
+ /*
+ * If a sync queue got queued in a group where other writers are are
+ * queued and at the time of queuing some other reader was running
+ * in anohter group, then this reader will not preempt the reader in
+ * another group. Side affect of this is that once this group gets
+ * scheduled, writer will start running and will not get preempted,
+ * as it should have been.
+ *
+ * Don't expire the writer right now otherwise writers might get
+ * completely starved. Let it just do one dispatch round and then
+ * expire. Mark the queue for expiry.
+ */
+ if (!elv_ioq_sync(ioq) && iog->sched_data.nr_sync) {
+ elv_mark_ioq_must_expire(ioq);
+ elv_log_ioq(eq->efqd, ioq, "late preempt, must expire");
+ }
+}
+
#else /* GROUP_IOSCHED */
#define for_each_entity(entity) \
for (; entity != NULL; entity = NULL)
@@ -248,6 +304,20 @@ io_entity_sched_data(struct io_entity *entity)
return &efqd->root_group->sched_data;
}
+
+static inline void set_late_preemption(struct elevator_queue *eq,
+ struct io_queue *active_ioq, struct io_queue *new_ioq)
+{
+}
+
+static inline void reset_late_preemption(struct elevator_queue *eq,
+ struct io_group *iog, struct io_queue *ioq)
+{
+}
+
+static inline void
+check_late_preemption(struct elevator_queue *eq, struct io_queue *ioq) { }
+
#endif /* GROUP_IOSCHED */
static inline void
@@ -578,11 +648,14 @@ static void dequeue_io_entity(struct io_entity *entity)
{
struct io_service_tree *st = entity->st;
struct io_sched_data *sd = io_entity_sched_data(entity);
+ struct io_queue *ioq = ioq_of(entity);
__dequeue_io_entity(st, entity);
entity->on_st = 0;
st->nr_active--;
sd->nr_active--;
+ if (ioq && elv_ioq_sync(ioq) && !elv_ioq_class_idle(ioq))
+ sd->nr_sync--;
debug_update_stats_dequeue(entity);
if (vdisktime_gt(entity->vdisktime, st->min_vdisktime))
@@ -627,6 +700,7 @@ static void enqueue_io_entity(struct io_entity *entity)
{
struct io_service_tree *st;
struct io_sched_data *sd = io_entity_sched_data(entity);
+ struct io_queue *ioq = ioq_of(entity);
if (entity->on_idle_st)
dequeue_io_entity_idle(entity);
@@ -642,6 +716,9 @@ static void enqueue_io_entity(struct io_entity *entity)
st = entity->st;
st->nr_active++;
sd->nr_active++;
+ /* Keep a track of how many sync queues are backlogged on this group */
+ if (ioq && elv_ioq_sync(ioq) && !elv_ioq_class_idle(ioq))
+ sd->nr_sync++;
entity->on_st = 1;
place_entity(st, entity, 0);
__enqueue_io_entity(st, entity, 0);
@@ -1986,6 +2063,7 @@ __elv_set_active_ioq(struct elv_fq_data *efqd, struct io_queue *ioq, int coop)
elv_clear_ioq_must_dispatch(ioq);
elv_clear_iog_wait_busy_done(iog);
elv_mark_ioq_slice_new(ioq);
+ elv_clear_ioq_must_expire(ioq);
del_timer(&efqd->idle_slice_timer);
}
@@ -2102,6 +2180,10 @@ void elv_ioq_slice_expired(struct request_queue *q, struct io_queue *ioq)
elv_clear_iog_wait_request(iog);
elv_clear_iog_wait_busy(iog);
elv_clear_iog_wait_busy_done(iog);
+ elv_clear_ioq_must_expire(ioq);
+
+ if (elv_ioq_sync(ioq))
+ reset_late_preemption(q->elevator, iog, ioq);
/*
* Queue got expired before even a single request completed or
@@ -2305,6 +2387,15 @@ void elv_ioq_request_add(struct request_queue *q, struct request *rq)
*/
elv_preempt_queue(q, ioq);
__blk_run_queue(q);
+ } else {
+ /*
+ * Request came in a queue which is not active and we did not
+ * decide to preempt the active queue. It is possible that
+ * active queue belonged to a different group and we did not
+ * allow preemption. Keep a track of this event so that once
+ * this group is ready to dispatch, we can do some more checks
+ */
+ set_late_preemption(eq, elv_active_ioq(eq), ioq);
}
}
@@ -2447,6 +2538,7 @@ void *elv_select_ioq(struct request_queue *q, int force)
{
struct io_queue *new_ioq = NULL, *ioq = elv_active_ioq(q->elevator);
struct io_group *iog;
+ struct elv_fq_data *efqd = q->elevator->efqd;
if (!elv_nr_busy_ioq(q->elevator))
return NULL;
@@ -2471,6 +2563,12 @@ void *elv_select_ioq(struct request_queue *q, int force)
goto expire;
}
+ /* This queue has been marked for expiry. Try to expire it */
+ if (elv_ioq_must_expire(ioq)) {
+ elv_log_ioq(efqd, ioq, "select: ioq must_expire. expire");
+ goto expire;
+ }
+
/* We are waiting for this group to become busy before it expires.*/
if (elv_iog_wait_busy(iog)) {
ioq = NULL;
@@ -2555,6 +2653,8 @@ expire:
new_queue:
ioq = elv_set_active_ioq(q, new_ioq);
keep_queue:
+ if (ioq)
+ check_late_preemption(q->elevator, ioq);
return ioq;
}
@@ -43,6 +43,7 @@ struct io_sched_data {
struct io_entity *active_entity;
int nr_active;
struct io_service_tree service_tree[IO_IOPRIO_CLASSES];
+ int nr_sync;
};
struct io_entity {
@@ -132,6 +133,7 @@ struct io_group {
/* Store cgroup path */
char path[128];
#endif
+ int late_preemption;
};
struct io_cgroup {
@@ -227,6 +229,7 @@ enum elv_queue_state_flags {
ELV_QUEUE_FLAG_idle_window, /* elevator slice idling enabled */
ELV_QUEUE_FLAG_slice_new, /* no requests dispatched in slice */
ELV_QUEUE_FLAG_sync, /* synchronous queue */
+ ELV_QUEUE_FLAG_must_expire, /* expire queue even slice is left */
};
#define ELV_IO_QUEUE_FLAG_FNS(name) \
@@ -249,6 +252,7 @@ ELV_IO_QUEUE_FLAG_FNS(must_dispatch)
ELV_IO_QUEUE_FLAG_FNS(idle_window)
ELV_IO_QUEUE_FLAG_FNS(slice_new)
ELV_IO_QUEUE_FLAG_FNS(sync)
+ELV_IO_QUEUE_FLAG_FNS(must_expire)
#ifdef CONFIG_GROUP_IOSCHED