Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 89877e42827f16fa5f86b1df0c2860b1 > files > 2356

kernel-2.6.18-128.1.10.el5.src.rpm

From: Pete Zaitcev <zaitcev@redhat.com>
Date: Wed, 20 Aug 2008 14:40:07 -0600
Subject: [usb] removing bus with an open file causes an oops
Message-id: 20080820144007.65cbd065.zaitcev@redhat.com
O-Subject: [RHEL 5.3 patch] bz#450786 Oops in usbdev_read
Bugzilla: 450786
RH-Acked-by: Rik van Riel <riel@redhat.com>

Stratus found that an oops happens in usbdev_read if a USB bus is removed.
Apparently, we forgot to dissociate two structures with dissimilar
lifetimes: inode and usb_device. The struct usb_device disappears
upon a disconnect. But an open in progress may have inode referenced
already and so it does not disappear when we drop the reference
a dentry had for it. Notice that usbfs_mutex provides the exclusion,
so the zeroing of i_private by itself does not race other CPU.
It was plainly forgotten.

This fix is not yet upstream. I consulted with Al Viro about the
issue and he says that although the i_private=NULL is correct,
a better solution would be to pin the struct usb_device (the embedded
struct device really) from usbfs. This is more involved than I would
like for RHEL 5, in fact I haven't wrote the code for it yet.
It needs things like:

<viro> f) that open() loses checks for ->...dentry->d_inode - just check the state
<viro> now, we need to make sure that bus count won't get screwed by races
<viro> and carefully checks if simple_dir_... stuff will work with that
<viro> check, even

I'll get there eventually.

The patch was tested to fix the problem by Stratus.

Please ack.

-- Pete

diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 58b4b10..ccba5bf 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -529,6 +529,7 @@ static void fs_remove_file (struct dentry *dentry)
 	mutex_lock_nested(&parent->d_inode->i_mutex, I_MUTEX_PARENT);
 	if (usbfs_positive(dentry)) {
 		if (dentry->d_inode) {
+			dentry->d_inode->i_private = NULL;
 			if (S_ISDIR(dentry->d_inode->i_mode))
 				usbfs_rmdir(parent->d_inode, dentry);
 			else