aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-11-09 07:35:07 -0500
committerThomas Gleixner <tglx@linutronix.de>2017-11-12 09:10:27 -0500
commitb24591e2fcf852ad7ad2ccf745c8220bf378d312 (patch)
tree4ba3fdd27fb8652b937f02893c961e83e6a84b9f
parentdf27067e6040b51188184876253d93da002433aa (diff)
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
-rw-r--r--include/linux/timer.h1
-rw-r--r--kernel/time/timer.c45
2 files changed, 39 insertions, 7 deletions
diff --git a/include/linux/timer.h b/include/linux/timer.h
index 9f8895decb82..37b5e2f74d21 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -203,6 +203,7 @@ extern void add_timer_on(struct timer_list *timer, int cpu);
203extern int del_timer(struct timer_list * timer); 203extern int del_timer(struct timer_list * timer);
204extern int mod_timer(struct timer_list *timer, unsigned long expires); 204extern int mod_timer(struct timer_list *timer, unsigned long expires);
205extern int mod_timer_pending(struct timer_list *timer, unsigned long expires); 205extern int mod_timer_pending(struct timer_list *timer, unsigned long expires);
206extern int timer_reduce(struct timer_list *timer, unsigned long expires);
206 207
207/* 208/*
208 * The jiffies value which is added to now, when there is no timer 209 * The jiffies value which is added to now, when there is no timer
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index fbb1f85327bf..af0b8bae4502 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -929,8 +929,11 @@ static struct timer_base *lock_timer_base(struct timer_list *timer,
929 } 929 }
930} 930}
931 931
932#define MOD_TIMER_PENDING_ONLY 0x01
933#define MOD_TIMER_REDUCE 0x02
934
932static inline int 935static 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)
934{ 937{
935 struct timer_base *base, *new_base; 938 struct timer_base *base, *new_base;
936 unsigned int idx = UINT_MAX; 939 unsigned int idx = UINT_MAX;
@@ -950,7 +953,11 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
950 * larger granularity than you would get from adding a new 953 * larger granularity than you would get from adding a new
951 * timer with this expiry. 954 * timer with this expiry.
952 */ 955 */
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)
954 return 1; 961 return 1;
955 962
956 /* 963 /*
@@ -962,6 +969,12 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
962 base = lock_timer_base(timer, &flags); 969 base = lock_timer_base(timer, &flags);
963 forward_timer_base(base); 970 forward_timer_base(base);
964 971
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
965 clk = base->clk; 978 clk = base->clk;
966 idx = calc_wheel_index(expires, clk); 979 idx = calc_wheel_index(expires, clk);
967 980
@@ -971,7 +984,10 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
971 * subsequent call will exit in the expires check above. 984 * subsequent call will exit in the expires check above.
972 */ 985 */
973 if (idx == timer_get_idx(timer)) { 986 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;
975 ret = 1; 991 ret = 1;
976 goto out_unlock; 992 goto out_unlock;
977 } 993 }
@@ -981,7 +997,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
981 } 997 }
982 998
983 ret = detach_if_pending(timer, base, false); 999 ret = detach_if_pending(timer, base, false);
984 if (!ret && pending_only) 1000 if (!ret && (options & MOD_TIMER_PENDING_ONLY))
985 goto out_unlock; 1001 goto out_unlock;
986 1002
987 debug_activate(timer, expires); 1003 debug_activate(timer, expires);
@@ -1042,7 +1058,7 @@ out_unlock:
1042 */ 1058 */
1043int mod_timer_pending(struct timer_list *timer, unsigned long expires) 1059int mod_timer_pending(struct timer_list *timer, unsigned long expires)
1044{ 1060{
1045 return __mod_timer(timer, expires, true); 1061 return __mod_timer(timer, expires, MOD_TIMER_PENDING_ONLY);
1046} 1062}
1047EXPORT_SYMBOL(mod_timer_pending); 1063EXPORT_SYMBOL(mod_timer_pending);
1048 1064
@@ -1068,11 +1084,26 @@ EXPORT_SYMBOL(mod_timer_pending);
1068 */ 1084 */
1069int mod_timer(struct timer_list *timer, unsigned long expires) 1085int mod_timer(struct timer_list *timer, unsigned long expires)
1070{ 1086{
1071 return __mod_timer(timer, expires, false); 1087 return __mod_timer(timer, expires, 0);
1072} 1088}
1073EXPORT_SYMBOL(mod_timer); 1089EXPORT_SYMBOL(mod_timer);
1074 1090
1075/** 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 */
1100int timer_reduce(struct timer_list *timer, unsigned long expires)
1101{
1102 return __mod_timer(timer, expires, MOD_TIMER_REDUCE);
1103}
1104EXPORT_SYMBOL(timer_reduce);
1105
1106/**
1076 * add_timer - start a timer 1107 * add_timer - start a timer
1077 * @timer: the timer to be added 1108 * @timer: the timer to be added
1078 * 1109 *
@@ -1754,7 +1785,7 @@ signed long __sched schedule_timeout(signed long timeout)
1754 1785
1755 timer.task = current; 1786 timer.task = current;
1756 timer_setup_on_stack(&timer.timer, process_timeout, 0); 1787 timer_setup_on_stack(&timer.timer, process_timeout, 0);
1757 __mod_timer(&timer.timer, expire, false); 1788 __mod_timer(&timer.timer, expire, 0);
1758 schedule(); 1789 schedule();
1759 del_singleshot_timer_sync(&timer.timer); 1790 del_singleshot_timer_sync(&timer.timer);
1760 1791