Re: 2.1.47 oops (crash)

Bill Hawes (whawes@star.net)
Wed, 30 Jul 1997 13:58:27 -0400


This is a multi-part message in MIME format.
--------------94D05CF22FF7C25C178E4A5B
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Attached is a patch for fs/dcache.c that I think will guard against the
two oops reported recently. It appears that if a dentry with no parent
is unused but still hashed, calling shrink_dcache will free the dentry
and then try to free the parent.

If dentries aren't supposed to get into this state, maybe we can search
for one too many hash insertions.

I also made a couple of changes in fs/super.c -- there appears to be a
small race in do_umount, so it's safer to clear sb->s_root before
calling d_umount().

Regards,
Bill
--------------94D05CF22FF7C25C178E4A5B
Content-Type: text/plain; charset=us-ascii; name="super_47-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="super_47-patch"

--- linux-2.1.47/fs/dcache.c.old Thu Jul 24 17:36:06 1997
+++ linux-2.1.47/fs/dcache.c Wed Jul 30 13:34:16 1997
@@ -121,7 +121,10 @@
}
parent = dentry->d_parent;
d_free(dentry);
- dput(parent);
+ if (dentry != parent)
+ dput(parent);
+ else
+ printk("shrink_dcache: no parent??\n");
}
}
}
--- linux-2.1.47/fs/super.c.old Thu Jul 24 17:36:08 1997
+++ linux-2.1.47/fs/super.c Wed Jul 30 12:59:37 1997
@@ -575,6 +575,7 @@
static int do_umount(kdev_t dev,int unmount_root)
{
struct super_block * sb;
+ struct dentry * root;
int retval;

sb = get_super(dev);
@@ -599,8 +600,7 @@
quota_off(dev, -1);
fsync_dev(dev);
retval = do_remount_sb(sb, MS_RDONLY, 0);
- if (retval)
- return retval;
+ return retval;
}
return 0;
}
@@ -615,8 +615,9 @@
return -EBUSY;

/* clean up dcache .. */
- d_umount(sb->s_root);
- sb->s_root = NULL;
+ root = sb->s_root;
+ sb->s_root = NULL; /* d_umount() may block ... */
+ d_umount(root);

if (sb->s_op) {
if (sb->s_op->write_super && sb->s_dirt)
@@ -628,15 +629,27 @@
return 0;
}

+/*
+ * There is a little kludge here with the empty inode. The current
+ * vfs release functions only use the r_dev field in the inode so
+ * we give them the info they need without using a real inode.
+ * If any other fields are ever needed by any block device release
+ * functions, they should be faked here. -- jrs
+ */
static int umount_dev(kdev_t dev)
{
int retval;
struct inode * inode = get_empty_inode();

- inode->i_rdev = dev;
+ retval = -ENOMEM;
+ if (!inode)
+ goto out;
+
+ retval = -ENXIO;
if (MAJOR(dev) >= MAX_BLKDEV)
- return -ENXIO;
+ goto out_iput;

+ inode->i_rdev = dev;
fsync_dev(dev);
retval = do_umount(dev,0);
if (!retval) {
@@ -646,21 +659,16 @@
put_unnamed_dev(dev);
}
}
+out_iput:
iput(inode);
+out:
return retval;
}

/*
* Now umount can handle mount points as well as block devices.
* This is important for filesystems which use unnamed block devices.
- *
- * There is a little kludge here with the dummy_inode. The current
- * vfs release functions only use the r_dev field in the inode so
- * we give them the info they need without using a real inode.
- * If any other fields are ever needed by any block device release
- * functions, they should be faked here. -- jrs
*/
-
asmlinkage int sys_umount(char * name)
{
struct dentry * dentry;
@@ -674,18 +682,22 @@
retval = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
struct inode * inode = dentry->d_inode;
- kdev_t dev = inode->i_rdev;
+ kdev_t dev = 0;

- retval = 0;
- if (S_ISBLK(inode->i_mode)) {
- if (IS_NODEV(inode))
- retval = -EACCES;
- } else {
- struct super_block *sb = inode->i_sb;
- retval = -EINVAL;
- if (sb && inode == sb->s_root->d_inode) {
- dev = sb->s_dev;
- retval = 0;
+ retval = -EINVAL;
+ if (inode) {
+ dev = inode->i_rdev;
+ retval = 0;
+ if (S_ISBLK(inode->i_mode)) {
+ if (IS_NODEV(inode))
+ retval = -EACCES;
+ } else {
+ struct super_block *sb = inode->i_sb;
+ retval = -EINVAL;
+ if (sb && inode == sb->s_root->d_inode) {
+ dev = sb->s_dev;
+ retval = 0;
+ }
}
}
dput(dentry);
@@ -805,11 +817,15 @@
dentry = namei(dir);
retval = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
- struct super_block * sb = dentry->d_inode->i_sb;
+ struct inode * inode = dentry->d_inode;
+ struct super_block * sb;

retval = -EINVAL;
- if (dentry == sb->s_root)
- retval = do_remount_sb(sb, flags, data);
+ if (inode) {
+ sb = inode->i_sb;
+ if (sb && dentry == sb->s_root)
+ retval = do_remount_sb(sb, flags, data);
+ }
dput(dentry);
}
return retval;

--------------94D05CF22FF7C25C178E4A5B--