summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTzafrir Cohen <tzafrir.cohen@xorcom.com>2011-12-14 19:27:27 +0000
committerTzafrir Cohen <tzafrir.cohen@xorcom.com>2011-12-14 19:27:27 +0000
commit60c26bfcc63c3c302fc58ad91cef630c6598212a (patch)
tree1be4cddd39dfd7574b49e0fa64e75a7404feab58
parenta0266a7a37a4306d7c3a6ebe80d1d9efab65c1a5 (diff)
DAHDI-linux: Fix "surprise removal" problems
* Added a nodev_*() file_operations that handle system calls from user-space after surprise device removal. Signed-off-by: Oron Peled <oron.peled@xorcom.com> Acked-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com> Acked-by: Shaun Ruffell <sruffell@digium.com> git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@10381 a0bf4364-ded3-4de4-8d8a-66a801d63aff
-rw-r--r--drivers/dahdi/dahdi-base.c151
1 files changed, 122 insertions, 29 deletions
diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c
index 453b782..c690b94 100644
--- a/drivers/dahdi/dahdi-base.c
+++ b/drivers/dahdi/dahdi-base.c
@@ -2150,6 +2150,8 @@ static unsigned long _chan_cleanup(struct dahdi_chan *pos, unsigned long data)
return 0;
}
+static const struct file_operations nodev_fops;
+
static void dahdi_chan_unreg(struct dahdi_chan *chan)
{
unsigned long flags;
@@ -2164,6 +2166,11 @@ static void dahdi_chan_unreg(struct dahdi_chan *chan)
"%s: surprise removal: chan %d\n",
__func__, chan->channo);
chan->file->private_data = NULL;
+ chan->file->f_op = &nodev_fops;
+ /*
+ * From now on, any file_operations for this device
+ * would call the nodev_fops methods.
+ */
}
spin_lock_irqsave(&chan->lock, flags);
@@ -2240,14 +2247,12 @@ static ssize_t dahdi_chan_read(struct file *file, char __user *usrbuf,
count &= 0xffff;
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.
+ /*
+ * This should never happen. Surprise device removal
+ * should lead us to the nodev_* file_operations
*/
msleep(5);
+ module_printk(KERN_ERR, "%s: NODEV\n", __func__);
return -ENODEV;
}
@@ -2363,14 +2368,12 @@ static ssize_t dahdi_chan_write(struct file *file, const char __user *usrbuf,
count &= 0xffff;
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.
+ /*
+ * This should never happen. Surprise device removal
+ * should lead us to the nodev_* file_operations
*/
msleep(5);
+ module_printk(KERN_ERR, "%s: NODEV\n", __func__);
return -ENODEV;
}
@@ -5500,13 +5503,12 @@ static int dahdi_ioctl_iomux(struct file *file, unsigned long data)
wait_result = 0;
prepare_to_wait(&chan->waitq, &wait, TASK_INTERRUPTIBLE);
if (unlikely(!chan->file->private_data)) {
- static int rate_limit;
-
- if ((rate_limit++ % 1000) == 0)
- module_printk(KERN_NOTICE,
- "%s: (%d) nodev\n",
- __func__, rate_limit);
+ /*
+ * This should never happen. Surprise device removal
+ * should lead us to the nodev_* file_operations
+ */
msleep(5);
+ module_printk(KERN_ERR, "%s: NODEV\n", __func__);
ret = -ENODEV;
break;
}
@@ -9171,12 +9173,12 @@ static unsigned int dahdi_timer_poll(struct file *file, struct poll_table_struct
ret |= POLLPRI;
spin_unlock_irqrestore(&dahdi_timer_lock, flags);
} else {
- static int rate_limit;
-
- if ((rate_limit++ % 1000) == 0)
- module_printk(KERN_NOTICE, "%s: (%d) nodev\n",
- __func__, rate_limit);
+ /*
+ * This should never happen. Surprise device removal
+ * should lead us to the nodev_* file_operations
+ */
msleep(5);
+ module_printk(KERN_ERR, "%s: NODEV\n", __func__);
return POLLERR | POLLHUP | POLLRDHUP | POLLNVAL | POLLPRI;
}
return ret;
@@ -9191,12 +9193,12 @@ dahdi_chan_poll(struct file *file, struct poll_table_struct *wait_table)
unsigned long flags;
if (unlikely(!c)) {
- static int rate_limit;
-
- if ((rate_limit++ % 1000) == 0)
- module_printk(KERN_NOTICE, "%s: (%d) nodev\n",
- __func__, rate_limit);
- msleep(20);
+ /*
+ * This should never happen. Surprise device removal
+ * should lead us to the nodev_* file_operations
+ */
+ msleep(5);
+ module_printk(KERN_ERR, "%s: NODEV\n", __func__);
return POLLERR | POLLHUP | POLLRDHUP | POLLNVAL | POLLPRI;
}
@@ -9833,6 +9835,97 @@ static const struct file_operations dahdi_fops = {
.poll = dahdi_poll,
};
+/*
+ * DAHDI stability should not depend on the calling process behaviour.
+ * In case of suprise device removal, we should be able to return
+ * sane results (-ENODEV) even after the underlying device was released.
+ *
+ * This should be OK even if the calling process (hint, hint Asterisk)
+ * ignores the system calls return value.
+ *
+ * We simply use dummy file_operations to implement this.
+ */
+
+/*
+ * Common behaviour called from all other nodev_*() file_operations
+ */
+static int nodev_common(const char msg[])
+{
+ if (printk_ratelimit()) {
+ module_printk(KERN_NOTICE,
+ "nodev: %s: process %d still calling\n",
+ msg, current->tgid);
+ }
+ msleep(5);
+ return -ENODEV;
+}
+
+static ssize_t nodev_chan_read(struct file *file, char __user *usrbuf,
+ size_t count, loff_t *ppos)
+{
+ return nodev_common("read");
+}
+
+static ssize_t nodev_chan_write(struct file *file, const char __user *usrbuf,
+ size_t count, loff_t *ppos)
+{
+ return nodev_common("write");
+}
+
+static unsigned int
+nodev_chan_poll(struct file *file, struct poll_table_struct *wait_table)
+{
+ return nodev_common("poll");
+}
+
+static long
+nodev_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long data)
+{
+ switch (cmd) {
+ case DAHDI_GETEVENT: /* Get event on queue */
+ /*
+ * Hint the bugger that the channel is gone for good
+ */
+ put_user(DAHDI_EVENT_REMOVED, (int __user *)data);
+ break;
+ }
+ return nodev_common("ioctl");
+}
+
+#ifndef HAVE_UNLOCKED_IOCTL
+static int nodev_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long data)
+{
+ return nodev_unlocked_ioctl(file, cmd, data);
+}
+#endif
+
+#ifdef HAVE_COMPAT_IOCTL
+static long nodev_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long data)
+{
+ if (cmd == DAHDI_SFCONFIG)
+ return -ENOTTY; /* Not supported yet */
+
+ return nodev_unlocked_ioctl(file, cmd, data);
+}
+#endif
+
+static const struct file_operations nodev_fops = {
+ .owner = THIS_MODULE,
+#ifdef HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = nodev_unlocked_ioctl,
+#ifdef HAVE_COMPAT_IOCTL
+ .compat_ioctl = nodev_ioctl_compat,
+#endif
+#else
+ .ioctl = nodev_ioctl,
+#endif
+ .read = nodev_chan_read,
+ .write = nodev_chan_write,
+ .poll = nodev_chan_poll,
+};
+
static const struct file_operations dahdi_chan_fops = {
.owner = THIS_MODULE,
.open = dahdi_open,