summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2011-01-21 05:28:28 +0000
committerShaun Ruffell <sruffell@digium.com>2011-01-21 05:28:28 +0000
commit3896c2af9b2d8f6de9c7ac84f434658f8bb3f295 (patch)
treef60ed588a7ea38721212963daf2e10a850112eb3
parent9218cae027097ecf0cf2a01811ab443051e18b4b (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.c70
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: