Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e7be9ed authored by Oleg Nesterov's avatar Oleg Nesterov Committed by Jens Axboe
Browse files

cfq_idle_class_timer: add paranoid checks for jiffies overflow



In theory, if the queue was idle long enough, cfq_idle_class_timer may have
a false (and very long) timeout because jiffies can wrap into the past wrt
->last_end_request.

Signed-off-by: default avatarOleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: default avatarJens Axboe <jens.axboe@oracle.com>
parent b70c864d
Loading
Loading
Loading
Loading
+17 −11
Original line number Diff line number Diff line
@@ -789,6 +789,20 @@ static inline void cfq_slice_expired(struct cfq_data *cfqd, int timed_out)
		__cfq_slice_expired(cfqd, cfqq, timed_out);
}

static int start_idle_class_timer(struct cfq_data *cfqd)
{
	unsigned long end = cfqd->last_end_request + CFQ_IDLE_GRACE;
	unsigned long now = jiffies;

	if (time_before(now, end) &&
	    time_after_eq(now, cfqd->last_end_request)) {
		mod_timer(&cfqd->idle_class_timer, end);
		return 1;
	}

	return 0;
}

/*
 * Get next queue for service. Unless we have a queue preemption,
 * we'll simply select the first cfqq in the service tree.
@@ -805,20 +819,15 @@ static struct cfq_queue *cfq_get_next_queue(struct cfq_data *cfqd)
	cfqq = rb_entry(n, struct cfq_queue, rb_node);

	if (cfq_class_idle(cfqq)) {
		unsigned long end;

		/*
		 * if we have idle queues and no rt or be queues had
		 * pending requests, either allow immediate service if
		 * the grace period has passed or arm the idle grace
		 * timer
		 */
		end = cfqd->last_end_request + CFQ_IDLE_GRACE;
		if (time_before(jiffies, end)) {
			mod_timer(&cfqd->idle_class_timer, end);
		if (start_idle_class_timer(cfqd))
			cfqq = NULL;
	}
	}

	return cfqq;
}
@@ -2036,17 +2045,14 @@ static void cfq_idle_slice_timer(unsigned long data)
static void cfq_idle_class_timer(unsigned long data)
{
	struct cfq_data *cfqd = (struct cfq_data *) data;
	unsigned long flags, end;
	unsigned long flags;

	spin_lock_irqsave(cfqd->queue->queue_lock, flags);

	/*
	 * race with a non-idle queue, reset timer
	 */
	end = cfqd->last_end_request + CFQ_IDLE_GRACE;
	if (!time_after_eq(jiffies, end))
		mod_timer(&cfqd->idle_class_timer, end);
	else
	if (!start_idle_class_timer(cfqd))
		cfq_schedule_dispatch(cfqd);

	spin_unlock_irqrestore(cfqd->queue->queue_lock, flags);