summaryrefslogtreecommitdiffstats
path: root/fs/super.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/super.c')
-rw-r--r--fs/super.c199
1 files changed, 113 insertions, 86 deletions
diff --git a/fs/super.c b/fs/super.c
index 22c26a07e..84ef3ffb8 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -95,28 +95,37 @@ struct vfsmount *lookup_vfsmnt(kdev_t dev)
/* NOTREACHED */
}
-struct vfsmount *add_vfsmnt(kdev_t dev, const char *dev_name, const char *dir_name)
+static struct vfsmount *add_vfsmnt(struct super_block *sb,
+ const char *dev_name, const char *dir_name)
{
struct vfsmount *lptr;
- char *tmp;
+ char *tmp, *name;
lptr = (struct vfsmount *)kmalloc(sizeof(struct vfsmount), GFP_KERNEL);
- if (!lptr)
- return NULL;
+ if (!lptr)
+ goto out;
memset(lptr, 0, sizeof(struct vfsmount));
- lptr->mnt_dev = dev;
+ lptr->mnt_sb = sb;
+ lptr->mnt_dev = sb->s_dev;
+ lptr->mnt_flags = sb->s_flags;
sema_init(&lptr->mnt_sem, 1);
+
+ /* N.B. Is it really OK to have a vfsmount without names? */
if (dev_name && !IS_ERR(tmp = getname(dev_name))) {
- if ((lptr->mnt_devname =
- (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL)
- strcpy(lptr->mnt_devname, tmp);
+ name = (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL);
+ if (name) {
+ strcpy(name, tmp);
+ lptr->mnt_devname = name;
+ }
putname(tmp);
}
if (dir_name && !IS_ERR(tmp = getname(dir_name))) {
- if ((lptr->mnt_dirname =
- (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL)
- strcpy(lptr->mnt_dirname, tmp);
+ name = (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL);
+ if (name) {
+ strcpy(name, tmp);
+ lptr->mnt_dirname = name;
+ }
putname(tmp);
}
@@ -126,10 +135,11 @@ struct vfsmount *add_vfsmnt(kdev_t dev, const char *dev_name, const char *dir_na
vfsmnttail->mnt_next = lptr;
vfsmnttail = lptr;
}
- return (lptr);
+out:
+ return lptr;
}
-void remove_vfsmnt(kdev_t dev)
+static void remove_vfsmnt(kdev_t dev)
{
struct vfsmount *lptr, *tofree;
@@ -282,6 +292,7 @@ static struct proc_fs_info {
{ MS_SYNCHRONOUS, ",sync" },
{ MS_MANDLOCK, ",mand" },
{ MS_NOATIME, ",noatime" },
+ { MS_NODIRATIME, ",nodiratime" },
#ifdef MS_NOSUB /* Can't find this except in mount.c */
{ MS_NOSUB, ",nosub" },
#endif
@@ -419,6 +430,11 @@ repeat:
current->state = TASK_RUNNING;
}
+/*
+ * Note: check the dirty flag before waiting, so we don't
+ * hold up the sync while mounting a device. (The newly
+ * mounted device won't need syncing.)
+ */
void sync_supers(kdev_t dev)
{
struct super_block * sb;
@@ -428,6 +444,9 @@ void sync_supers(kdev_t dev)
continue;
if (dev && sb->s_dev != dev)
continue;
+ if (!sb->s_dirt)
+ continue;
+ /* N.B. Should lock the superblock while writing */
wait_on_super(sb);
if (!sb->s_dev || !sb->s_dirt)
continue;
@@ -444,13 +463,14 @@ struct super_block * get_super(kdev_t dev)
if (!dev)
return NULL;
+restart:
s = 0+super_blocks;
while (s < NR_SUPER+super_blocks)
if (s->s_dev == dev) {
wait_on_super(s);
if (s->s_dev == dev)
return s;
- s = 0+super_blocks;
+ goto restart;
} else
s++;
return NULL;
@@ -487,6 +507,23 @@ out:
return err;
}
+/*
+ * Find a super_block with no device assigned.
+ */
+static struct super_block *get_empty_super(void)
+{
+ struct super_block *s = 0+super_blocks;
+
+ for (; s < NR_SUPER+super_blocks; s++) {
+ if (s->s_dev)
+ continue;
+ if (!s->s_lock)
+ return s;
+ printk("VFS: empty superblock %p locked!\n", s);
+ }
+ return NULL;
+}
+
static struct super_block * read_super(kdev_t dev,const char *name,int flags,
void *data, int silent)
{
@@ -494,33 +531,39 @@ static struct super_block * read_super(kdev_t dev,const char *name,int flags,
struct file_system_type *type;
if (!dev)
- return NULL;
+ goto out_null;
check_disk_change(dev);
s = get_super(dev);
if (s)
- return s;
- if (!(type = get_fs_type(name))) {
+ goto out;
+
+ type = get_fs_type(name);
+ if (!type) {
printk("VFS: on device %s: get_fs_type(%s) failed\n",
kdevname(dev), name);
- return NULL;
- }
- for (s = 0+super_blocks ;; s++) {
- if (s >= NR_SUPER+super_blocks)
- return NULL;
- if (!(s->s_dev))
- break;
+ goto out;
}
+ s = get_empty_super();
+ if (!s)
+ goto out;
s->s_dev = dev;
s->s_flags = flags;
- if (!type->read_super(s,data, silent)) {
- s->s_dev = 0;
- return NULL;
- }
- s->s_dev = dev;
- s->s_rd_only = 0;
s->s_dirt = 0;
+ /* N.B. Should lock superblock now ... */
+ if (!type->read_super(s, data, silent))
+ goto out_fail;
+ s->s_dev = dev; /* N.B. why do this again?? */
+ s->s_rd_only = 0;
s->s_type = type;
+out:
return s;
+
+ /* N.B. s_dev should be cleared in type->read_super */
+out_fail:
+ s->s_dev = 0;
+out_null:
+ s = NULL;
+ goto out;
}
/*
@@ -583,17 +626,16 @@ static void d_mount(struct dentry *covered, struct dentry *dentry)
dentry->d_covers = covered;
}
-static int do_umount(kdev_t dev,int unmount_root)
+static int do_umount(kdev_t dev, int unmount_root)
{
struct super_block * sb;
int retval;
+ retval = -ENOENT;
sb = get_super(dev);
- if (!sb)
- return -ENOENT;
+ if (!sb || !sb->s_root)
+ goto out;
- if (!sb->s_root)
- return -ENOENT;
/*
* Before checking whether the filesystem is still busy,
* make sure the kernel doesn't hold any quotafiles open
@@ -639,7 +681,6 @@ static int do_umount(kdev_t dev,int unmount_root)
sb->s_op->put_super(sb);
}
remove_vfsmnt(dev);
- retval = 0;
out:
return retval;
}
@@ -761,7 +802,7 @@ int fs_may_mount(kdev_t dev)
int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data)
{
- struct dentry * dir_d = NULL;
+ struct dentry * dir_d;
struct super_block * sb;
struct vfsmount *vfsmnt;
int error;
@@ -786,15 +827,13 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha
goto dput_and_out;
/*
- * Check whether to read the super block
+ * Note: If the superblock already exists,
+ * read_super just does a get_super().
*/
- sb = get_super(dev);
- if (!sb || !sb->s_root) {
- error = -EINVAL;
- sb = read_super(dev,type,flags,data,0);
- if (!sb)
- goto dput_and_out;
- }
+ error = -EINVAL;
+ sb = read_super(dev, type, flags, data, 0);
+ if (!sb)
+ goto dput_and_out;
/*
* We may have slept while reading the super block,
@@ -805,20 +844,19 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha
goto dput_and_out;
error = -ENOMEM;
- vfsmnt = add_vfsmnt(dev, dev_name, dir_name);
- if (vfsmnt) {
- vfsmnt->mnt_sb = sb;
- vfsmnt->mnt_flags = flags;
- d_mount(dir_d, sb->s_root);
- error = 0;
- goto out; /* we don't dput(dir) - see umount */
- }
+ vfsmnt = add_vfsmnt(sb, dev_name, dir_name);
+ if (!vfsmnt)
+ goto dput_and_out;
+ d_mount(dir_d, sb->s_root);
+ error = 0; /* we don't dput(dir_d) - see umount */
-dput_and_out:
- dput(dir_d);
out:
up(&mount_sem);
return error;
+
+dput_and_out:
+ dput(dir_d);
+ goto out;
}
@@ -1040,34 +1078,27 @@ __initfunc(static void do_mount_root(void))
int retval;
#ifdef CONFIG_ROOT_NFS
- if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR)
- if (nfs_root_init(nfs_root_name, nfs_root_addrs) < 0) {
- printk(KERN_ERR "Root-NFS: Unable to contact NFS "
- "server for root fs, using /dev/fd0 instead\n");
- ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);
- }
if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
ROOT_DEV = 0;
if ((fs_type = get_fs_type("nfs"))) {
- sb = &super_blocks[0];
- while (sb->s_dev) sb++;
+ sb = get_empty_super(); /* "can't fail" */
sb->s_dev = get_unnamed_dev();
sb->s_flags = root_mountflags & ~MS_RDONLY;
- if (nfs_root_mount(sb) >= 0) {
- sb->s_rd_only = 0;
- sb->s_dirt = 0;
- sb->s_type = fs_type;
- current->fs->root = dget(sb->s_root);
- current->fs->pwd = dget(sb->s_root);
- ROOT_DEV = sb->s_dev;
- printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n");
- vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/");
- if (!vfsmnt)
- panic("VFS: add_vfsmnt failed for NFS root.\n");
- vfsmnt->mnt_sb = sb;
- vfsmnt->mnt_flags = sb->s_flags;
- return;
+ vfsmnt = add_vfsmnt(sb, "/dev/root", "/");
+ if (vfsmnt) {
+ if (nfs_root_mount(sb) >= 0) {
+ sb->s_rd_only = 0;
+ sb->s_dirt = 0;
+ sb->s_type = fs_type;
+ current->fs->root = dget(sb->s_root);
+ current->fs->pwd = dget(sb->s_root);
+ ROOT_DEV = sb->s_dev;
+ printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n");
+ return;
+ }
+ remove_vfsmnt(sb->s_dev);
}
+ put_unnamed_dev(sb->s_dev);
sb->s_dev = 0;
}
if (!ROOT_DEV) {
@@ -1121,12 +1152,10 @@ __initfunc(static void do_mount_root(void))
printk ("VFS: Mounted root (%s filesystem)%s.\n",
fs_type->name,
(sb->s_flags & MS_RDONLY) ? " readonly" : "");
- vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/");
- if (!vfsmnt)
- panic("VFS: add_vfsmnt failed for root fs");
- vfsmnt->mnt_sb = sb;
- vfsmnt->mnt_flags = root_mountflags;
- return;
+ vfsmnt = add_vfsmnt(sb, "/dev/root", "/");
+ if (vfsmnt)
+ return;
+ panic("VFS: add_vfsmnt failed for root fs");
}
}
panic("VFS: Unable to mount root fs on %s",
@@ -1210,10 +1239,8 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old))
return error;
}
remove_vfsmnt(old_root_dev);
- vfsmnt = add_vfsmnt(old_root_dev,"/dev/root.old",put_old);
+ vfsmnt = add_vfsmnt(old_root->d_sb, "/dev/root.old", put_old);
if (vfsmnt) {
- vfsmnt->mnt_sb = old_root->d_inode->i_sb;
- vfsmnt->mnt_flags = vfsmnt->mnt_sb->s_flags;
d_mount(dir_d,old_root);
return 0;
}