[svn-commits] sruffell: branch linux/sruffell/chan_list_refactoring r9255 - /linux/team/sru...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Sep 2 12:40:50 CDT 2010


Author: sruffell
Date: Thu Sep  2 12:40:45 2010
New Revision: 9255

URL: http://svnview.digium.com/svn/dahdi?view=rev&rev=9255
Log:
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

Signed-off-by: Shaun Ruffell <sruffell at digium.com>

Modified:
    linux/team/sruffell/chan_list_refactoring/drivers/dahdi/dahdi-base.c

Modified: linux/team/sruffell/chan_list_refactoring/drivers/dahdi/dahdi-base.c
URL: http://svnview.digium.com/svn/dahdi/linux/team/sruffell/chan_list_refactoring/drivers/dahdi/dahdi-base.c?view=diff&rev=9255&r1=9254&r2=9255
==============================================================================
--- linux/team/sruffell/chan_list_refactoring/drivers/dahdi/dahdi-base.c (original)
+++ linux/team/sruffell/chan_list_refactoring/drivers/dahdi/dahdi-base.c Thu Sep  2 12:40:45 2010
@@ -2000,6 +2000,15 @@
 
 	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 +2062,10 @@
 	write_unlock_irqrestore(&chan_lock, flags);
 }
 
-static ssize_t dahdi_chan_read(struct file *file, char __user *usrbuf, size_t count, int unit)
-{
-	struct dahdi_chan *chan = chans[unit];
+static ssize_t dahdi_chan_read(struct file *file, char __user *usrbuf,
+			       size_t count)
+{
+	struct dahdi_chan *chan = file->private_data;
 	int amnt;
 	int res, rv;
 	int oldbuf,x;
@@ -2064,10 +2074,19 @@
 	/* Make sure count never exceeds 65k, and make sure it's unsigned */
 	count &= 0xffff;
 
-	if (!chan)
-		return -EINVAL;
-
-	if (count < 1)
+	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 (unlikely(count < 1))
 		return -EINVAL;
 
 	for (;;) {
@@ -2179,19 +2198,29 @@
 	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 (count < 1) {
+	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 (unlikely(count < 1)) {
 		return -EINVAL;
 	}
 
@@ -2777,6 +2806,7 @@
 			}
 			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 @@
 		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 @@
 			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 @@
 		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 @@
 			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, unit);
+		return dahdi_chan_write(file, usrbuf, count);
+	}
+	return dahdi_chan_write(file, usrbuf, count);
 
 }
 
@@ -5913,6 +5943,12 @@
 		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:




More information about the svn-commits mailing list