diff options
Diffstat (limited to 'fs/super.c')
-rw-r--r-- | fs/super.c | 199 |
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; } |