]> www.infradead.org Git - users/jedix/linux-maple.git/commit
timers: Use proper base migration in add_timer_on()
authorHonglei Wang <honglei.wang@oracle.com>
Thu, 23 Feb 2017 02:50:42 +0000 (10:50 +0800)
committerChuck Anderson <chuck.anderson@oracle.com>
Mon, 6 Mar 2017 04:00:39 +0000 (20:00 -0800)
commit2a0665516eed8a467501c8409c93085d6e299bcf
tree08de87c9d05b133f8a3a57d796e4e22d5543ddd1
parent7e0f0d2f6bf3127834e117162891fe1b4401a02e
timers: Use proper base migration in add_timer_on()

Orabug 25079989 hit timer race condition which is fixed by upstream
commit 22b886dd (timers: Use proper base migration in add_timer_on()).

Commit 22b886dd is base on v4.4 code whose definition of timer_list
is different from uek4.

It'll have to modify the timer_list struct if backport it directly
with all his base commits. That's a big changeset and there will be
kABI check issue.

So this patch implements same logic (which is used in __mod_timer()
for the same race condition as well) as 22b886dd and works with uek4
timer_list struct.

Following is the description of commit 22b886dd:

timers: Use proper base migration in add_timer_on()

Regardless of the previous CPU a timer was on, add_timer_on()
currently simply sets timer->flags to the new CPU.  As the caller must
be seeing the timer as idle, this is locally fine, but the timer
leaving the old base while unlocked can lead to race conditions as
follows.

Let's say timer was on cpu 0.

  cpu 0                      cpu 1
  -----------------------------------------------------------------------------
  del_timer(timer) succeeds
                                        del_timer(timer)
                                          lock_timer_base(timer) locks cpu_0_base
  add_timer_on(timer, 1)
    spin_lock(&cpu_1_base->lock)
    timer->flags set to cpu_1_base
    operates on @timer                    operates on @timer

This triggered with mod_delayed_work_on() which contains
"if (del_timer()) add_timer_on()" sequence eventually leading to the
following oops.

  BUG: unable to handle kernel NULL pointer dereference at            (null)
  IP: [<ffffffff810ca6e9>] detach_if_pending+0x69/0x1a0
  ...
  Workqueue: wqthrash wqthrash_workfunc [wqthrash]
  task: ffff8800172ca680 ti: ffff8800172d0000 task.ti: ffff8800172d0000
  RIP: 0010:[<ffffffff810ca6e9>]  [<ffffffff810ca6e9>] detach_if_pending+0x69/0x1a0
  ...
  Call Trace:
   [<ffffffff810cb0b4>] del_timer+0x44/0x60
   [<ffffffff8106e836>] try_to_grab_pending+0xb6/0x160
   [<ffffffff8106e913>] mod_delayed_work_on+0x33/0x80
   [<ffffffffa0000081>] wqthrash_workfunc+0x61/0x90 [wqthrash]
   [<ffffffff8106dba8>] process_one_work+0x1e8/0x650
   [<ffffffff8106e05e>] worker_thread+0x4e/0x450
   [<ffffffff810746af>] kthread+0xef/0x110
   [<ffffffff8185980f>] ret_from_fork+0x3f/0x70

Fix it by updating add_timer_on() to perform proper migration as
__mod_timer() does.

Orabug: 25079989

Signed-off-by: Honglei Wang <honglei.wang@oracle.com>
Acked-by: Joe Jin <joe.jin@oracle.com>
kernel/time/timer.c