Skip to content

Commit b24591e

Browse files
dhowellsKAGA-KOKO
authored andcommitted
timers: Add a function to start/reduce a timer
Add a function, similar to mod_timer(), that will start a timer if it isn't running and will modify it if it is running and has an expiry time longer than the new time. If the timer is running with an expiry time that's the same or sooner, no change is made. The function looks like: int timer_reduce(struct timer_list *timer, unsigned long expires); This can be used by code such as networking code to make it easier to share a timer for multiple timeouts. For instance, in upcoming AF_RXRPC code, the rxrpc_call struct will maintain a number of timeouts: unsigned long ack_at; unsigned long resend_at; unsigned long ping_at; unsigned long expect_rx_by; unsigned long expect_req_by; unsigned long expect_term_by; each of which is set independently of the others. With timer reduction available, when the code needs to set one of the timeouts, it only needs to look at that timeout and then call timer_reduce() to modify the timer, starting it or bringing it forward if necessary. There is no need to refer to the other timeouts to see which is earliest and no need to take any lock other than, potentially, the timer lock inside timer_reduce(). Note, that this does not protect against concurrent invocations of any of the timer functions. As an example, the expect_rx_by timeout above, which terminates a call if we don't get a packet from the server within a certain time window, would be set something like this: unsigned long now = jiffies; unsigned long expect_rx_by = now + packet_receive_timeout; WRITE_ONCE(call->expect_rx_by, expect_rx_by); timer_reduce(&call->timer, expect_rx_by); The timer service code (which might, say, be in a work function) would then check all the timeouts to see which, if any, had triggered, deal with those: t = READ_ONCE(call->ack_at); if (time_after_eq(now, t)) { cmpxchg(&call->ack_at, t, now + MAX_JIFFY_OFFSET); set_bit(RXRPC_CALL_EV_ACK, &call->events); } and then restart the timer if necessary by finding the soonest timeout that hasn't yet passed and then calling timer_reduce(). The disadvantage of doing things this way rather than comparing the timers each time and calling mod_timer() is that you *will* take timer events unless you can finish what you're doing and delete the timer in time. The advantage of doing things this way is that you don't need to use a lock to work out when the next timer should be set, other than the timer's own lock - which you might not have to take. [ tglx: Fixed weird formatting and adopted it to pending changes ] Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: keyrings@vger.kernel.org Cc: linux-afs@lists.infradead.org Link: https://lkml.kernel.org/r/151023090769.23050.1801643667223880753.stgit@warthog.procyon.org.uk
1 parent df27067 commit b24591e

File tree

2 files changed

+39
-7
lines changed

2 files changed

+39
-7
lines changed

include/linux/timer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ extern void add_timer_on(struct timer_list *timer, int cpu);
203203
extern int del_timer(struct timer_list * timer);
204204
extern int mod_timer(struct timer_list *timer, unsigned long expires);
205205
extern int mod_timer_pending(struct timer_list *timer, unsigned long expires);
206+
extern int timer_reduce(struct timer_list *timer, unsigned long expires);
206207

207208
/*
208209
* The jiffies value which is added to now, when there is no timer

kernel/time/timer.c

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -929,8 +929,11 @@ static struct timer_base *lock_timer_base(struct timer_list *timer,
929929
}
930930
}
931931

932+
#define MOD_TIMER_PENDING_ONLY 0x01
933+
#define MOD_TIMER_REDUCE 0x02
934+
932935
static inline int
933-
__mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
936+
__mod_timer(struct timer_list *timer, unsigned long expires, unsigned int options)
934937
{
935938
struct timer_base *base, *new_base;
936939
unsigned int idx = UINT_MAX;
@@ -950,7 +953,11 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
950953
* larger granularity than you would get from adding a new
951954
* timer with this expiry.
952955
*/
953-
if (timer->expires == expires)
956+
long diff = timer->expires - expires;
957+
958+
if (!diff)
959+
return 1;
960+
if (options & MOD_TIMER_REDUCE && diff <= 0)
954961
return 1;
955962

956963
/*
@@ -962,6 +969,12 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
962969
base = lock_timer_base(timer, &flags);
963970
forward_timer_base(base);
964971

972+
if (timer_pending(timer) && (options & MOD_TIMER_REDUCE) &&
973+
time_before_eq(timer->expires, expires)) {
974+
ret = 1;
975+
goto out_unlock;
976+
}
977+
965978
clk = base->clk;
966979
idx = calc_wheel_index(expires, clk);
967980

@@ -971,7 +984,10 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
971984
* subsequent call will exit in the expires check above.
972985
*/
973986
if (idx == timer_get_idx(timer)) {
974-
timer->expires = expires;
987+
if (!(options & MOD_TIMER_REDUCE))
988+
timer->expires = expires;
989+
else if (time_after(timer->expires, expires))
990+
timer->expires = expires;
975991
ret = 1;
976992
goto out_unlock;
977993
}
@@ -981,7 +997,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
981997
}
982998

983999
ret = detach_if_pending(timer, base, false);
984-
if (!ret && pending_only)
1000+
if (!ret && (options & MOD_TIMER_PENDING_ONLY))
9851001
goto out_unlock;
9861002

9871003
debug_activate(timer, expires);
@@ -1042,7 +1058,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
10421058
*/
10431059
int mod_timer_pending(struct timer_list *timer, unsigned long expires)
10441060
{
1045-
return __mod_timer(timer, expires, true);
1061+
return __mod_timer(timer, expires, MOD_TIMER_PENDING_ONLY);
10461062
}
10471063
EXPORT_SYMBOL(mod_timer_pending);
10481064

@@ -1068,10 +1084,25 @@ EXPORT_SYMBOL(mod_timer_pending);
10681084
*/
10691085
int mod_timer(struct timer_list *timer, unsigned long expires)
10701086
{
1071-
return __mod_timer(timer, expires, false);
1087+
return __mod_timer(timer, expires, 0);
10721088
}
10731089
EXPORT_SYMBOL(mod_timer);
10741090

1091+
/**
1092+
* timer_reduce - Modify a timer's timeout if it would reduce the timeout
1093+
* @timer: The timer to be modified
1094+
* @expires: New timeout in jiffies
1095+
*
1096+
* timer_reduce() is very similar to mod_timer(), except that it will only
1097+
* modify a running timer if that would reduce the expiration time (it will
1098+
* start a timer that isn't running).
1099+
*/
1100+
int timer_reduce(struct timer_list *timer, unsigned long expires)
1101+
{
1102+
return __mod_timer(timer, expires, MOD_TIMER_REDUCE);
1103+
}
1104+
EXPORT_SYMBOL(timer_reduce);
1105+
10751106
/**
10761107
* add_timer - start a timer
10771108
* @timer: the timer to be added
@@ -1754,7 +1785,7 @@ signed long __sched schedule_timeout(signed long timeout)
17541785

17551786
timer.task = current;
17561787
timer_setup_on_stack(&timer.timer, process_timeout, 0);
1757-
__mod_timer(&timer.timer, expire, false);
1788+
__mod_timer(&timer.timer, expire, 0);
17581789
schedule();
17591790
del_singleshot_timer_sync(&timer.timer);
17601791

0 commit comments

Comments
 (0)