diff options
author | Shaun Ruffell <sruffell@digium.com> | 2011-01-21 05:28:28 +0000 |
---|---|---|
committer | Shaun Ruffell <sruffell@digium.com> | 2011-01-21 05:28:28 +0000 |
commit | 3896c2af9b2d8f6de9c7ac84f434658f8bb3f295 (patch) | |
tree | f60ed588a7ea38721212963daf2e10a850112eb3 | |
parent | 9218cae027097ecf0cf2a01811ab443051e18b4b (diff) |
dahdi: Be more tolerant of surprise removal of channels.
Enable DAHDI to detect if an operation on a file handle refers to a
channel that may have been unregistered. This can occur, for example,
when a board driver is hot-swapped out in a live system.
This patch ensures that file->private_data is always properly set for
any open channel, and it's set back to NULL when a channel is
unregistered. This way file->private_data can be used to check whether
it's valid to perform an operation on the channel. (NOTE: There is
still a race condition here if the driver was unbound on one processor
during the window of time between when file->private_data was checked
and the system call finishes).
Also, since DAHDI should only return -ENODEV on read or write when there
was a surprise device removal on a running system this sleep can prevent
the system from becoming unresponsive if the userspace application does
not check for the -ENODEV error and constantly tries to call read with
elevated privileges.
(issue #17669)
Reported by: tzafrir
Tested by: sruffell
Review: https://reviewboard.asterisk.org/r/905/
Signed-off-by: Shaun Ruffell <sruffell@digium.com>
Origin: http://svnview.digium.com/svn/dahdi?view=rev&rev=9353
git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/branches/2.4@9662 a0bf4364-ded3-4de4-8d8a-66a801d63aff
-rw-r--r-- | drivers/dahdi/dahdi-base.c | 70 |
1 files changed, 53 insertions, 17 deletions
diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c index 1c4dc8f..5e1137d 100644 --- a/drivers/dahdi/dahdi-base.c +++ b/drivers/dahdi/dahdi-base.c @@ -47,6 +47,7 @@ #include <linux/moduleparam.h> #include <linux/sched.h> #include <linux/list.h> +#include <linux/delay.h> #ifdef HAVE_UNLOCKED_IOCTL #include <linux/smp_lock.h> @@ -2000,6 +2001,15 @@ static void dahdi_chan_unreg(struct dahdi_chan *chan) might_sleep(); + /* In the case of surprise removal of hardware, make sure any open + * file handles to this channel are disassociated with the actual + * dahdi_chan. */ + if (chan->file) { + chan->file->private_data = NULL; + if (chan->span) + module_put(chan->span->ops->owner); + } + release_echocan(chan->ec_factory); #ifdef CONFIG_DAHDI_NET @@ -2053,9 +2063,10 @@ static void dahdi_chan_unreg(struct dahdi_chan *chan) write_unlock_irqrestore(&chan_lock, flags); } -static ssize_t dahdi_chan_read(struct file *file, char __user *usrbuf, size_t count, int unit) +static ssize_t dahdi_chan_read(struct file *file, char __user *usrbuf, + size_t count) { - struct dahdi_chan *chan = chans[unit]; + struct dahdi_chan *chan = file->private_data; int amnt; int res, rv; int oldbuf,x; @@ -2064,10 +2075,19 @@ static ssize_t dahdi_chan_read(struct file *file, char __user *usrbuf, size_t co /* Make sure count never exceeds 65k, and make sure it's unsigned */ count &= 0xffff; - if (!chan) - return -EINVAL; + if (unlikely(!chan)) { + /* We would typically be here because of surprise hardware + * removal or driver unbinding while a user space application + * has a channel open. Most telephony applications are run at + * elevated priorities so this sleep can prevent the high + * priority threads from consuming the CPU if they're not + * expecting surprise device removal. + */ + msleep(5); + return -ENODEV; + } - if (count < 1) + if (unlikely(count < 1)) return -EINVAL; for (;;) { @@ -2179,21 +2199,30 @@ static int num_filled_bufs(struct dahdi_chan *chan) return range1 + range2; } -static ssize_t dahdi_chan_write(struct file *file, const char __user *usrbuf, size_t count, int unit) +static ssize_t dahdi_chan_write(struct file *file, const char __user *usrbuf, + size_t count) { unsigned long flags; - struct dahdi_chan *chan = chans[unit]; + struct dahdi_chan *chan = file->private_data; int res, amnt, oldbuf, rv, x; /* Make sure count never exceeds 65k, and make sure it's unsigned */ count &= 0xffff; - if (!chan) - return -EINVAL; + if (unlikely(!chan)) { + /* We would typically be here because of surprise hardware + * removal or driver unbinding while a user space application + * has a channel open. Most telephony applications are run at + * elevated priorities so this sleep can prevent the high + * priority threads from consuming the CPU if they're not + * expecting surprise device removal. + */ + msleep(5); + return -ENODEV; + } - if (count < 1) { + if (unlikely(count < 1)) return -EINVAL; - } for (;;) { spin_lock_irqsave(&chan->lock, flags); @@ -2777,6 +2806,7 @@ static int dahdi_specchan_open(struct file *file, int unit) } if (!res) { chan->file = file; + file->private_data = chan; spin_unlock_irqrestore(&chan->lock, flags); } else { spin_unlock_irqrestore(&chan->lock, flags); @@ -2941,7 +2971,7 @@ static ssize_t dahdi_read(struct file *file, char __user *usrbuf, size_t count, chan = file->private_data; if (!chan) return -EINVAL; - return dahdi_chan_read(file, usrbuf, count, chan->channo); + return dahdi_chan_read(file, usrbuf, count); } if (unit == 255) { @@ -2950,12 +2980,12 @@ static ssize_t dahdi_read(struct file *file, char __user *usrbuf, size_t count, module_printk(KERN_NOTICE, "No pseudo channel structure to read?\n"); return -EINVAL; } - return dahdi_chan_read(file, usrbuf, count, chan->channo); + return dahdi_chan_read(file, usrbuf, count); } if (count < 0) return -EINVAL; - return dahdi_chan_read(file, usrbuf, count, unit); + return dahdi_chan_read(file, usrbuf, count); } static ssize_t dahdi_write(struct file *file, const char __user *usrbuf, size_t count, loff_t *ppos) @@ -2973,7 +3003,7 @@ static ssize_t dahdi_write(struct file *file, const char __user *usrbuf, size_t chan = file->private_data; if (!chan) return -EINVAL; - return dahdi_chan_write(file, usrbuf, count, chan->channo); + return dahdi_chan_write(file, usrbuf, count); } if (unit == 255) { chan = file->private_data; @@ -2981,9 +3011,9 @@ static ssize_t dahdi_write(struct file *file, const char __user *usrbuf, size_t module_printk(KERN_NOTICE, "No pseudo channel structure to read?\n"); return -EINVAL; } - return dahdi_chan_write(file, usrbuf, count, chan->channo); + return dahdi_chan_write(file, usrbuf, count); } - return dahdi_chan_write(file, usrbuf, count, unit); + return dahdi_chan_write(file, usrbuf, count); } @@ -5918,6 +5948,12 @@ static int dahdi_ioctl(struct inode *inode, struct file *file, ret = dahdi_chanandpseudo_ioctl(file, cmd, data, chan->channo); goto unlock_exit; } + + if (!file->private_data) { + ret = -ENXIO; + goto unlock_exit; + } + ret = dahdi_chan_ioctl(file, cmd, data, unit); unlock_exit: |