[PATCH 6/9] drm/nouveau: Convert event handler list to RCU

2013-08-27 Thread Peter Hurley
Lockdep report [1] correctly identifies a potential deadlock between
nouveau_drm_vblank_enable() and nouveau_drm_vblank_handler() due
to inverted lock order of event->lock with drm core's
dev->vblank_time_lock.

Fix vblank event deadlock by converting event handler list to RCU.
List updates remain serialized by event->lock.
List traversal & handler execution is lockless which prevents
inverted lock order problems with the drm core.
Safe deletion of the event handler is accomplished with
synchronize_rcu() for those handlers stored in nouveau_object-based
containers (nouveau_drm & nv50_/nvc0_software_context); otherwise,
with kfree_rcu (for nouveau_connector & uevent temporary handlers).

[1]
==
[ INFO: possible circular locking dependency detected ]
3.10.0-0+tip-xeon+lockdep #0+tip Not tainted
---
swapper/7/0 is trying to acquire lock:
 (&(&dev->vblank_time_lock)->rlock){-.}, at: [] 
drm_handle_vblank+0x69/0x400 [drm]

but task is already holding lock:
 (&(&event->lock)->rlock){-.}, at: [] 
nouveau_event_trigger+0x4d/0xd0 [nouveau]

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #1 (&(&event->lock)->rlock){-.}:
   [] lock_acquire+0x92/0x1f0
   [] _raw_spin_lock_irqsave+0x46/0x60
   [] nouveau_event_get+0x27/0xa0 [nouveau]
   [] nouveau_drm_vblank_enable+0x8d/0xf0 [nouveau]
   [] drm_vblank_get+0xf8/0x2c0 [drm]
   [] drm_wait_vblank+0x84/0x6f0 [drm]
   [] drm_ioctl+0x559/0x690 [drm]
   [] do_vfs_ioctl+0x97/0x590
   [] SyS_ioctl+0x91/0xb0
   [] system_call_fastpath+0x16/0x1b

-> #0 (&(&dev->vblank_time_lock)->rlock){-.}:
   [] __lock_acquire+0x1c43/0x1d30
   [] lock_acquire+0x92/0x1f0
   [] _raw_spin_lock_irqsave+0x46/0x60
   [] drm_handle_vblank+0x69/0x400 [drm]
   [] nouveau_drm_vblank_handler+0x27/0x30 [nouveau]
   [] nouveau_event_trigger+0x90/0xd0 [nouveau]
   [] nv50_disp_intr+0xdd/0x230 [nouveau]
   [] nouveau_mc_intr+0xa1/0x100 [nouveau]
   [] handle_irq_event_percpu+0x6c/0x3d0
   [] handle_irq_event+0x48/0x70
   [] handle_fasteoi_irq+0x5a/0x100
   [] handle_irq+0x22/0x40
   [] do_IRQ+0x5a/0xd0
   [] ret_from_intr+0x0/0x13
   [] arch_cpu_idle+0x26/0x30
   [] cpu_startup_entry+0xce/0x410
   [] start_secondary+0x255/0x25c

other info that might help us debug this:

 Possible unsafe locking scenario:

   CPU0CPU1
   
  lock(&(&event->lock)->rlock);
   lock(&(&dev->vblank_time_lock)->rlock);
   lock(&(&event->lock)->rlock);
  lock(&(&dev->vblank_time_lock)->rlock);

 *** DEADLOCK ***

1 lock held by swapper/7/0:
 #0:  (&(&event->lock)->rlock){-.}, at: [] 
nouveau_event_trigger+0x4d/0xd0 [nouveau]

stack backtrace:
CPU: 7 PID: 0 Comm: swapper/7 Not tainted 3.10.0-0+tip-xeon+lockdep #0+tip
Hardware name: Dell Inc. Precision WorkStation T5400  /0RW203, BIOS A11 
04/30/2012
 821ccf60 8802bfdc3b08 81755f39 8802bfdc3b58
 8174f526 0002 8802bfdc3be8 8802b330a7f8
 8802b330a820 8802b330a7f8  0001
Call Trace:
   [] dump_stack+0x19/0x1b
 [] print_circular_bug+0x1fb/0x20c
 [] __lock_acquire+0x1c43/0x1d30
 [] ? trace_hardirqs_off+0xd/0x10
 [] ? flat_send_IPI_mask+0x88/0xa0
 [] lock_acquire+0x92/0x1f0
 [] ? drm_handle_vblank+0x69/0x400 [drm]
 [] _raw_spin_lock_irqsave+0x46/0x60
 [] ? drm_handle_vblank+0x69/0x400 [drm]
 [] drm_handle_vblank+0x69/0x400 [drm]
 [] nouveau_drm_vblank_handler+0x27/0x30 [nouveau]
 [] nouveau_event_trigger+0x90/0xd0 [nouveau]
 [] nv50_disp_intr+0xdd/0x230 [nouveau]
 [] ? _raw_spin_unlock_irqrestore+0x42/0x80
 [] ? __hrtimer_start_range_ns+0x16c/0x530
 [] nouveau_mc_intr+0xa1/0x100 [nouveau]
 [] handle_irq_event_percpu+0x6c/0x3d0
 [] handle_irq_event+0x48/0x70
 [] ? handle_fasteoi_irq+0x1e/0x100
 [] handle_fasteoi_irq+0x5a/0x100
 [] handle_irq+0x22/0x40
 [] do_IRQ+0x5a/0xd0
 [] common_interrupt+0x6f/0x6f
   [] ? default_idle+0x1d/0x290
 [] ? default_idle+0x1b/0x290
 [] arch_cpu_idle+0x26/0x30
 [] cpu_startup_entry+0xce/0x410
 [] ? clockevents_register_device+0xdc/0x140
 [] start_secondary+0x255/0x25c

Reported-by: Dave Jones 
Signed-off-by: Peter Hurley 
---
 drivers/gpu/drm/nouveau/core/core/event.c  | 22 +++---
 .../gpu/drm/nouveau/core/engine/software/nv50.c|  1 +
 .../gpu/drm/nouveau/core/engine/software/nvc0.c|  1 +
 drivers/gpu/drm/nouveau/core/include/core/event.h  |  1 +
 drivers/gpu/drm/nouveau/nouveau_connector.c|  2 +-
 drivers/gpu/drm/nouveau/nouveau_drm.c  |  1 +
 6 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/core/core/event.c 
b/drivers/gpu/drm/nouveau/core/core/event.c
index 4cd1ebe..ce0a0ef 100644
--- a/drive

[PATCH 6/9] drm/nouveau: Convert event handler list to RCU

2013-08-27 Thread Peter Hurley
Lockdep report [1] correctly identifies a potential deadlock between
nouveau_drm_vblank_enable() and nouveau_drm_vblank_handler() due
to inverted lock order of event->lock with drm core's
dev->vblank_time_lock.

Fix vblank event deadlock by converting event handler list to RCU.
List updates remain serialized by event->lock.
List traversal & handler execution is lockless which prevents
inverted lock order problems with the drm core.
Safe deletion of the event handler is accomplished with
synchronize_rcu() for those handlers stored in nouveau_object-based
containers (nouveau_drm & nv50_/nvc0_software_context); otherwise,
with kfree_rcu (for nouveau_connector & uevent temporary handlers).

[1]
==
[ INFO: possible circular locking dependency detected ]
3.10.0-0+tip-xeon+lockdep #0+tip Not tainted
---
swapper/7/0 is trying to acquire lock:
 (&(&dev->vblank_time_lock)->rlock){-.}, at: [] 
drm_handle_vblank+0x69/0x400 [drm]

but task is already holding lock:
 (&(&event->lock)->rlock){-.}, at: [] 
nouveau_event_trigger+0x4d/0xd0 [nouveau]

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #1 (&(&event->lock)->rlock){-.}:
   [] lock_acquire+0x92/0x1f0
   [] _raw_spin_lock_irqsave+0x46/0x60
   [] nouveau_event_get+0x27/0xa0 [nouveau]
   [] nouveau_drm_vblank_enable+0x8d/0xf0 [nouveau]
   [] drm_vblank_get+0xf8/0x2c0 [drm]
   [] drm_wait_vblank+0x84/0x6f0 [drm]
   [] drm_ioctl+0x559/0x690 [drm]
   [] do_vfs_ioctl+0x97/0x590
   [] SyS_ioctl+0x91/0xb0
   [] system_call_fastpath+0x16/0x1b

-> #0 (&(&dev->vblank_time_lock)->rlock){-.}:
   [] __lock_acquire+0x1c43/0x1d30
   [] lock_acquire+0x92/0x1f0
   [] _raw_spin_lock_irqsave+0x46/0x60
   [] drm_handle_vblank+0x69/0x400 [drm]
   [] nouveau_drm_vblank_handler+0x27/0x30 [nouveau]
   [] nouveau_event_trigger+0x90/0xd0 [nouveau]
   [] nv50_disp_intr+0xdd/0x230 [nouveau]
   [] nouveau_mc_intr+0xa1/0x100 [nouveau]
   [] handle_irq_event_percpu+0x6c/0x3d0
   [] handle_irq_event+0x48/0x70
   [] handle_fasteoi_irq+0x5a/0x100
   [] handle_irq+0x22/0x40
   [] do_IRQ+0x5a/0xd0
   [] ret_from_intr+0x0/0x13
   [] arch_cpu_idle+0x26/0x30
   [] cpu_startup_entry+0xce/0x410
   [] start_secondary+0x255/0x25c

other info that might help us debug this:

 Possible unsafe locking scenario:

   CPU0CPU1
   
  lock(&(&event->lock)->rlock);
   lock(&(&dev->vblank_time_lock)->rlock);
   lock(&(&event->lock)->rlock);
  lock(&(&dev->vblank_time_lock)->rlock);

 *** DEADLOCK ***

1 lock held by swapper/7/0:
 #0:  (&(&event->lock)->rlock){-.}, at: [] 
nouveau_event_trigger+0x4d/0xd0 [nouveau]

stack backtrace:
CPU: 7 PID: 0 Comm: swapper/7 Not tainted 3.10.0-0+tip-xeon+lockdep #0+tip
Hardware name: Dell Inc. Precision WorkStation T5400  /0RW203, BIOS A11 
04/30/2012
 821ccf60 8802bfdc3b08 81755f39 8802bfdc3b58
 8174f526 0002 8802bfdc3be8 8802b330a7f8
 8802b330a820 8802b330a7f8  0001
Call Trace:
   [] dump_stack+0x19/0x1b
 [] print_circular_bug+0x1fb/0x20c
 [] __lock_acquire+0x1c43/0x1d30
 [] ? trace_hardirqs_off+0xd/0x10
 [] ? flat_send_IPI_mask+0x88/0xa0
 [] lock_acquire+0x92/0x1f0
 [] ? drm_handle_vblank+0x69/0x400 [drm]
 [] _raw_spin_lock_irqsave+0x46/0x60
 [] ? drm_handle_vblank+0x69/0x400 [drm]
 [] drm_handle_vblank+0x69/0x400 [drm]
 [] nouveau_drm_vblank_handler+0x27/0x30 [nouveau]
 [] nouveau_event_trigger+0x90/0xd0 [nouveau]
 [] nv50_disp_intr+0xdd/0x230 [nouveau]
 [] ? _raw_spin_unlock_irqrestore+0x42/0x80
 [] ? __hrtimer_start_range_ns+0x16c/0x530
 [] nouveau_mc_intr+0xa1/0x100 [nouveau]
 [] handle_irq_event_percpu+0x6c/0x3d0
 [] handle_irq_event+0x48/0x70
 [] ? handle_fasteoi_irq+0x1e/0x100
 [] handle_fasteoi_irq+0x5a/0x100
 [] handle_irq+0x22/0x40
 [] do_IRQ+0x5a/0xd0
 [] common_interrupt+0x6f/0x6f
   [] ? default_idle+0x1d/0x290
 [] ? default_idle+0x1b/0x290
 [] arch_cpu_idle+0x26/0x30
 [] cpu_startup_entry+0xce/0x410
 [] ? clockevents_register_device+0xdc/0x140
 [] start_secondary+0x255/0x25c

Reported-by: Dave Jones 
Signed-off-by: Peter Hurley 
---
 drivers/gpu/drm/nouveau/core/core/event.c  | 22 +++---
 .../gpu/drm/nouveau/core/engine/software/nv50.c|  1 +
 .../gpu/drm/nouveau/core/engine/software/nvc0.c|  1 +
 drivers/gpu/drm/nouveau/core/include/core/event.h  |  1 +
 drivers/gpu/drm/nouveau/nouveau_connector.c|  2 +-
 drivers/gpu/drm/nouveau/nouveau_drm.c  |  1 +
 6 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/core/core/event.c 
b/drivers/gpu/drm/nouveau/core/core/event.c
index 4cd1ebe..ce0a0ef 100644
--- a/drive