Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec
[sfrench/cifs-2.6.git] / kernel / time / timer.c
index ee1a88d8afb2cf3c629c626c82ff912a68f19471..af0b8bae45027042ff153172522b659fb605cf1d 100644 (file)
@@ -610,7 +610,7 @@ static bool timer_fixup_init(void *addr, enum debug_obj_state state)
 }
 
 /* Stub timer callback for improperly used timers. */
-static void stub_timer(unsigned long data)
+static void stub_timer(struct timer_list *unused)
 {
        WARN_ON(1);
 }
@@ -626,7 +626,7 @@ static bool timer_fixup_activate(void *addr, enum debug_obj_state state)
 
        switch (state) {
        case ODEBUG_STATE_NOTAVAILABLE:
-               setup_timer(timer, stub_timer, 0);
+               timer_setup(timer, stub_timer, 0);
                return true;
 
        case ODEBUG_STATE_ACTIVE:
@@ -665,7 +665,7 @@ static bool timer_fixup_assert_init(void *addr, enum debug_obj_state state)
 
        switch (state) {
        case ODEBUG_STATE_NOTAVAILABLE:
-               setup_timer(timer, stub_timer, 0);
+               timer_setup(timer, stub_timer, 0);
                return true;
        default:
                return false;
@@ -929,8 +929,11 @@ static struct timer_base *lock_timer_base(struct timer_list *timer,
        }
 }
 
+#define MOD_TIMER_PENDING_ONLY         0x01
+#define MOD_TIMER_REDUCE               0x02
+
 static inline int
-__mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
+__mod_timer(struct timer_list *timer, unsigned long expires, unsigned int options)
 {
        struct timer_base *base, *new_base;
        unsigned int idx = UINT_MAX;
@@ -950,7 +953,11 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
                 * larger granularity than you would get from adding a new
                 * timer with this expiry.
                 */
-               if (timer->expires == expires)
+               long diff = timer->expires - expires;
+
+               if (!diff)
+                       return 1;
+               if (options & MOD_TIMER_REDUCE && diff <= 0)
                        return 1;
 
                /*
@@ -962,6 +969,12 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
                base = lock_timer_base(timer, &flags);
                forward_timer_base(base);
 
+               if (timer_pending(timer) && (options & MOD_TIMER_REDUCE) &&
+                   time_before_eq(timer->expires, expires)) {
+                       ret = 1;
+                       goto out_unlock;
+               }
+
                clk = base->clk;
                idx = calc_wheel_index(expires, clk);
 
@@ -971,7 +984,10 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
                 * subsequent call will exit in the expires check above.
                 */
                if (idx == timer_get_idx(timer)) {
-                       timer->expires = expires;
+                       if (!(options & MOD_TIMER_REDUCE))
+                               timer->expires = expires;
+                       else if (time_after(timer->expires, expires))
+                               timer->expires = expires;
                        ret = 1;
                        goto out_unlock;
                }
@@ -981,7 +997,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
        }
 
        ret = detach_if_pending(timer, base, false);
-       if (!ret && pending_only)
+       if (!ret && (options & MOD_TIMER_PENDING_ONLY))
                goto out_unlock;
 
        debug_activate(timer, expires);
@@ -1042,7 +1058,7 @@ out_unlock:
  */
 int mod_timer_pending(struct timer_list *timer, unsigned long expires)
 {
-       return __mod_timer(timer, expires, true);
+       return __mod_timer(timer, expires, MOD_TIMER_PENDING_ONLY);
 }
 EXPORT_SYMBOL(mod_timer_pending);
 
@@ -1068,10 +1084,25 @@ EXPORT_SYMBOL(mod_timer_pending);
  */
 int mod_timer(struct timer_list *timer, unsigned long expires)
 {
-       return __mod_timer(timer, expires, false);
+       return __mod_timer(timer, expires, 0);
 }
 EXPORT_SYMBOL(mod_timer);
 
+/**
+ * timer_reduce - Modify a timer's timeout if it would reduce the timeout
+ * @timer:     The timer to be modified
+ * @expires:   New timeout in jiffies
+ *
+ * timer_reduce() is very similar to mod_timer(), except that it will only
+ * modify a running timer if that would reduce the expiration time (it will
+ * start a timer that isn't running).
+ */
+int timer_reduce(struct timer_list *timer, unsigned long expires)
+{
+       return __mod_timer(timer, expires, MOD_TIMER_REDUCE);
+}
+EXPORT_SYMBOL(timer_reduce);
+
 /**
  * add_timer - start a timer
  * @timer: the timer to be added
@@ -1754,7 +1785,7 @@ signed long __sched schedule_timeout(signed long timeout)
 
        timer.task = current;
        timer_setup_on_stack(&timer.timer, process_timeout, 0);
-       __mod_timer(&timer.timer, expire, false);
+       __mod_timer(&timer.timer, expire, 0);
        schedule();
        del_singleshot_timer_sync(&timer.timer);