From b34aa86f12e8848ba453215602c8c50fa63c4cb3 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 10 Apr 2014 19:41:57 +0100 Subject: [PATCH] staging: comedi: fix circular locking dependency in comedi_mmap() Mmapping a comedi data buffer with lockdep checking enabled produced the following kernel debug messages: ====================================================== [ INFO: possible circular locking dependency detected ] 3.5.0-rc3-ija1+ #9 Tainted: G C ------------------------------------------------------- comedi_test/4160 is trying to acquire lock: (&dev->mutex#2){+.+.+.}, at: [] comedi_mmap+0x57/0x1d9 [comedi] but task is already holding lock: (&mm->mmap_sem){++++++}, at: [] vm_mmap_pgoff+0x41/0x76 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (&mm->mmap_sem){++++++}: [] lock_acquire+0x97/0x105 [] might_fault+0x6d/0x90 [] do_devinfo_ioctl.isra.7+0x11e/0x14c [comedi] [] comedi_unlocked_ioctl+0x256/0xe48 [comedi] [] vfs_ioctl+0x18/0x34 [] do_vfs_ioctl+0x382/0x43c [] sys_ioctl+0x42/0x65 [] system_call_fastpath+0x16/0x1b -> #0 (&dev->mutex#2){+.+.+.}: [] __lock_acquire+0x101d/0x1591 [] lock_acquire+0x97/0x105 [] mutex_lock_nested+0x46/0x2a4 [] comedi_mmap+0x57/0x1d9 [comedi] [] mmap_region+0x281/0x492 [] do_mmap_pgoff+0x26b/0x2a7 [] vm_mmap_pgoff+0x5d/0x76 [] sys_mmap_pgoff+0xc7/0x10d [] sys_mmap+0x16/0x20 [] system_call_fastpath+0x16/0x1b other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&mm->mmap_sem); lock(&dev->mutex#2); lock(&mm->mmap_sem); lock(&dev->mutex#2); *** DEADLOCK *** To avoid the circular dependency, just try to get the lock in `comedi_mmap()` instead of blocking. Since the comedi device's main mutex is heavily used, do a down-read of its `attach_lock` rwsemaphore instead. Trying to down-read `attach_lock` should only fail if some task has down-write locked it, and that is only done while the comedi device is being attached to or detached from a low-level hardware device. Unfortunately, acquiring the `attach_lock` doesn't prevent another task replacing the comedi data buffer we are trying to mmap. The details of the buffer are held in a `struct comedi_buf_map` and pointed to by `s->async->buf_map` where `s` is the comedi subdevice whose buffer we are trying to map. The `struct comedi_buf_map` is already reference counted with a `struct kref`, so we can stop it being freed prematurely. Modify `comedi_mmap()` to call new function `comedi_buf_map_from_subdev_get()` to read the subdevice's current buffer map pointer and increment its reference instead of accessing `async->buf_map` directly. Call `comedi_buf_map_put()` to decrement the reference once the buffer map structure has been dealt with. (Note that `comedi_buf_map_put()` does nothing if passed a NULL pointer.) `comedi_buf_map_from_subdev_get()` checks the subdevice's buffer map pointer has been set and the buffer map has been initialized enough for `comedi_mmap()` to deal with it (specifically, check the `n_pages` member has been set to a non-zero value). If all is well, the buffer map's reference is incremented and a pointer to it is returned. The comedi subdevice's spin-lock is used to protect the checks. Also use the spin-lock in `__comedi_buf_alloc()` and `__comedi_buf_free()` to protect changes to the subdevice's buffer map structure pointer and the buffer map structure's `n_pages` member. (This checking of `n_pages` is a bit clunky and I [Ian Abbott] plan to deal with it in the future.) Signed-off-by: Ian Abbott Cc: # 3.14.x, 3.15.x Signed-off-by: Greg Kroah-Hartman --- Reading git-format-patch failed