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 Original line 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);
		__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,
 * Get next queue for service. Unless we have a queue preemption,
 * we'll simply select the first cfqq in the service tree.
 * 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);
	cfqq = rb_entry(n, struct cfq_queue, rb_node);


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

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


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


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


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


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