unsigned int idx = UINT_MAX;
        int ret = 0;
 
-       BUG_ON(!timer->function);
+       debug_assert_init(timer);
 
        /*
         * This is a common optimization triggered by the networking code - if
                 * dequeue/enqueue dance.
                 */
                base = lock_timer_base(timer, &flags);
+               /*
+                * Has @timer been shutdown? This needs to be evaluated
+                * while holding base lock to prevent a race against the
+                * shutdown code.
+                */
+               if (!timer->function)
+                       goto out_unlock;
+
                forward_timer_base(base);
 
                if (timer_pending(timer) && (options & MOD_TIMER_REDUCE) &&
                }
        } else {
                base = lock_timer_base(timer, &flags);
+               /*
+                * Has @timer been shutdown? This needs to be evaluated
+                * while holding base lock to prevent a race against the
+                * shutdown code.
+                */
+               if (!timer->function)
+                       goto out_unlock;
+
                forward_timer_base(base);
        }
 
  * mod_timer_pending() is the same for pending timers as mod_timer(), but
  * will not activate inactive timers.
  *
+ * If @timer->function == NULL then the start operation is silently
+ * discarded.
+ *
  * Return:
- * * %0 - The timer was inactive and not modified
+ * * %0 - The timer was inactive and not modified or was in
+ *       shutdown state and the operation was discarded
  * * %1 - The timer was active and requeued to expire at @expires
  */
 int mod_timer_pending(struct timer_list *timer, unsigned long expires)
  * same timer, then mod_timer() is the only safe way to modify the timeout,
  * since add_timer() cannot modify an already running timer.
  *
+ * If @timer->function == NULL then the start operation is silently
+ * discarded. In this case the return value is 0 and meaningless.
+ *
  * Return:
- * * %0 - The timer was inactive and started
+ * * %0 - The timer was inactive and started or was in shutdown
+ *       state and the operation was discarded
  * * %1 - The timer was active and requeued to expire at @expires or
  *       the timer was active and not modified because @expires did
  *       not change the effective expiry time
  * modify an enqueued timer if that would reduce the expiration time. If
  * @timer is not enqueued it starts the timer.
  *
+ * If @timer->function == NULL then the start operation is silently
+ * discarded.
+ *
  * Return:
- * * %0 - The timer was inactive and started
+ * * %0 - The timer was inactive and started or was in shutdown
+ *       state and the operation was discarded
  * * %1 - The timer was active and requeued to expire at @expires or
  *       the timer was active and not modified because @expires
  *       did not change the effective expiry time such that the
  * The @timer->expires and @timer->function fields must be set prior
  * to calling this function.
  *
+ * If @timer->function == NULL then the start operation is silently
+ * discarded.
+ *
  * If @timer->expires is already in the past @timer will be queued to
  * expire at the next timer tick.
  *
        struct timer_base *new_base, *base;
        unsigned long flags;
 
-       if (WARN_ON_ONCE(timer_pending(timer) || !timer->function))
+       debug_assert_init(timer);
+
+       if (WARN_ON_ONCE(timer_pending(timer)))
                return;
 
        new_base = get_timer_cpu_base(timer->flags, cpu);
         * wrong base locked.  See lock_timer_base().
         */
        base = lock_timer_base(timer, &flags);
+       /*
+        * Has @timer been shutdown? This needs to be evaluated while
+        * holding base lock to prevent a race against the shutdown code.
+        */
+       if (!timer->function)
+               goto out_unlock;
+
        if (base != new_base) {
                timer->flags |= TIMER_MIGRATING;
 
 
        debug_timer_activate(timer);
        internal_add_timer(base, timer);
+out_unlock:
        raw_spin_unlock_irqrestore(&base->lock, flags);
 }
 EXPORT_SYMBOL_GPL(add_timer_on);
 
                fn = timer->function;
 
+               if (WARN_ON_ONCE(!fn)) {
+                       /* Should never happen. Emphasis on should! */
+                       base->running_timer = NULL;
+                       continue;
+               }
+
                if (timer->flags & TIMER_IRQSAFE) {
                        raw_spin_unlock(&base->lock);
                        call_timer_fn(timer, fn, baseclk);