2012-06-19 09:17:45 +00:00
|
|
|
#ifndef _COMEDI_INTERNAL_H
|
|
|
|
#define _COMEDI_INTERNAL_H
|
|
|
|
|
2015-03-27 15:58:19 +00:00
|
|
|
#include <linux/compiler.h>
|
2012-06-19 09:17:46 +00:00
|
|
|
#include <linux/types.h>
|
|
|
|
|
2010-05-03 22:38:37 +00:00
|
|
|
/*
|
2012-04-13 13:12:53 +00:00
|
|
|
* various internal comedi stuff
|
2010-05-03 22:38:37 +00:00
|
|
|
*/
|
2015-03-27 15:58:19 +00:00
|
|
|
|
|
|
|
struct comedi_buf_map;
|
|
|
|
struct comedi_devconfig;
|
|
|
|
struct comedi_device;
|
|
|
|
struct comedi_insn;
|
|
|
|
struct comedi_rangeinfo;
|
|
|
|
struct comedi_subdevice;
|
|
|
|
struct device;
|
|
|
|
|
2010-05-03 22:50:09 +00:00
|
|
|
int do_rangeinfo_ioctl(struct comedi_device *dev,
|
|
|
|
struct comedi_rangeinfo __user *arg);
|
2013-04-04 13:58:50 +00:00
|
|
|
struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device);
|
2013-04-04 13:58:47 +00:00
|
|
|
void comedi_release_hardware_device(struct device *hardware_device);
|
2013-02-01 10:20:30 +00:00
|
|
|
int comedi_alloc_subdevice_minor(struct comedi_subdevice *s);
|
2013-02-01 10:20:29 +00:00
|
|
|
void comedi_free_subdevice_minor(struct comedi_subdevice *s);
|
2013-01-09 20:27:07 +00:00
|
|
|
|
2010-05-03 22:55:45 +00:00
|
|
|
int comedi_buf_alloc(struct comedi_device *dev, struct comedi_subdevice *s,
|
|
|
|
unsigned long new_size);
|
2014-05-06 12:12:10 +00:00
|
|
|
void comedi_buf_reset(struct comedi_subdevice *s);
|
2014-05-06 12:12:11 +00:00
|
|
|
bool comedi_buf_is_mmapped(struct comedi_subdevice *s);
|
2013-11-08 15:03:43 +00:00
|
|
|
void comedi_buf_map_get(struct comedi_buf_map *bm);
|
|
|
|
int comedi_buf_map_put(struct comedi_buf_map *bm);
|
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: [<ffffffffa00313f4>] comedi_mmap+0x57/0x1d9 [comedi]
but task is already holding lock:
(&mm->mmap_sem){++++++}, at: [<ffffffff810c96fe>] 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){++++++}:
[<ffffffff8106d0e8>] lock_acquire+0x97/0x105
[<ffffffff810ce3bc>] might_fault+0x6d/0x90
[<ffffffffa0031ffb>] do_devinfo_ioctl.isra.7+0x11e/0x14c [comedi]
[<ffffffffa003227f>] comedi_unlocked_ioctl+0x256/0xe48 [comedi]
[<ffffffff810f7fcd>] vfs_ioctl+0x18/0x34
[<ffffffff810f87fd>] do_vfs_ioctl+0x382/0x43c
[<ffffffff810f88f9>] sys_ioctl+0x42/0x65
[<ffffffff81415c62>] system_call_fastpath+0x16/0x1b
-> #0 (&dev->mutex#2){+.+.+.}:
[<ffffffff8106c528>] __lock_acquire+0x101d/0x1591
[<ffffffff8106d0e8>] lock_acquire+0x97/0x105
[<ffffffff8140c894>] mutex_lock_nested+0x46/0x2a4
[<ffffffffa00313f4>] comedi_mmap+0x57/0x1d9 [comedi]
[<ffffffff810d5816>] mmap_region+0x281/0x492
[<ffffffff810d5c92>] do_mmap_pgoff+0x26b/0x2a7
[<ffffffff810c971a>] vm_mmap_pgoff+0x5d/0x76
[<ffffffff810d493f>] sys_mmap_pgoff+0xc7/0x10d
[<ffffffff81004d36>] sys_mmap+0x16/0x20
[<ffffffff81415c62>] 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 <abbotti@mev.co.uk>
Cc: <stable@vger.kernel.org> # 3.14.x, 3.15.x
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-04-10 18:41:57 +00:00
|
|
|
struct comedi_buf_map *comedi_buf_map_from_subdev_get(
|
|
|
|
struct comedi_subdevice *s);
|
2014-05-06 12:12:06 +00:00
|
|
|
unsigned int comedi_buf_write_n_allocated(struct comedi_subdevice *s);
|
2013-11-08 15:03:28 +00:00
|
|
|
void comedi_device_cancel_all(struct comedi_device *dev);
|
2015-04-21 12:18:10 +00:00
|
|
|
bool comedi_can_auto_free_spriv(struct comedi_subdevice *s);
|
2012-04-13 13:12:53 +00:00
|
|
|
|
|
|
|
extern unsigned int comedi_default_buf_size_kb;
|
|
|
|
extern unsigned int comedi_default_buf_maxsize_kb;
|
2013-01-30 22:25:06 +00:00
|
|
|
|
|
|
|
/* drivers.c */
|
|
|
|
|
2012-06-19 09:17:46 +00:00
|
|
|
extern struct comedi_driver *comedi_drivers;
|
2013-06-27 13:50:58 +00:00
|
|
|
extern struct mutex comedi_drivers_list_lock;
|
2012-06-19 09:17:45 +00:00
|
|
|
|
2013-01-30 22:25:06 +00:00
|
|
|
int insn_inval(struct comedi_device *, struct comedi_subdevice *,
|
|
|
|
struct comedi_insn *, unsigned int *);
|
|
|
|
|
|
|
|
void comedi_device_detach(struct comedi_device *);
|
|
|
|
int comedi_device_attach(struct comedi_device *, struct comedi_devconfig *);
|
|
|
|
|
2013-01-30 22:25:31 +00:00
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
|
|
|
|
/* proc.c */
|
|
|
|
|
|
|
|
void comedi_proc_init(void);
|
|
|
|
void comedi_proc_cleanup(void);
|
|
|
|
#else
|
|
|
|
static inline void comedi_proc_init(void)
|
|
|
|
{
|
|
|
|
}
|
2014-07-18 21:28:12 +00:00
|
|
|
|
2013-01-30 22:25:31 +00:00
|
|
|
static inline void comedi_proc_cleanup(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-06-19 09:17:45 +00:00
|
|
|
#endif /* _COMEDI_INTERNAL_H */
|