fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / jffs2 / super.c
index b657c55..08a0e6c 100644 (file)
@@ -3,37 +3,38 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: super.c,v 1.90 2003/10/11 11:47:23 dwmw2 Exp $
+ * $Id: super.c,v 1.110 2005/11/07 11:14:42 gleixner Exp $
  *
  */
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/list.h>
 #include <linux/fs.h>
+#include <linux/err.h>
 #include <linux/mount.h>
 #include <linux/jffs2.h>
 #include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
 #include <linux/ctype.h>
 #include <linux/namei.h>
+#include "compr.h"
 #include "nodelist.h"
 
 static void jffs2_put_super(struct super_block *);
 
-static kmem_cache_t *jffs2_inode_cachep;
+static struct kmem_cache *jffs2_inode_cachep;
 
 static struct inode *jffs2_alloc_inode(struct super_block *sb)
 {
        struct jffs2_inode_info *ei;
-       ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL);
+       ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, GFP_KERNEL);
        if (!ei)
                return NULL;
        return &ei->vfs_inode;
@@ -44,17 +45,27 @@ static void jffs2_destroy_inode(struct inode *inode)
        kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
 }
 
-static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+static void jffs2_i_init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
 {
        struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo;
 
        if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
            SLAB_CTOR_CONSTRUCTOR) {
-               init_MUTEX_LOCKED(&ei->sem);
+               init_MUTEX(&ei->sem);
                inode_init_once(&ei->vfs_inode);
        }
 }
 
+static int jffs2_sync_fs(struct super_block *sb, int wait)
+{
+       struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
+       down(&c->alloc_sem);
+       jffs2_flush_wbuf_pad(c);
+       up(&c->alloc_sem);
+       return 0;
+}
+
 static struct super_operations jffs2_super_operations =
 {
        .alloc_inode =  jffs2_alloc_inode,
@@ -66,6 +77,7 @@ static struct super_operations jffs2_super_operations =
        .remount_fs =   jffs2_remount_fs,
        .clear_inode =  jffs2_clear_inode,
        .dirty_inode =  jffs2_dirty_inode,
+       .sync_fs =      jffs2_sync_fs,
 };
 
 static int jffs2_sb_compare(struct super_block *sb, void *data)
@@ -99,88 +111,104 @@ static int jffs2_sb_set(struct super_block *sb, void *data)
        return 0;
 }
 
-static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
-                                             int flags, const char *dev_name, 
-                                             void *data, struct mtd_info *mtd)
+static int jffs2_get_sb_mtd(struct file_system_type *fs_type,
+                           int flags, const char *dev_name,
+                           void *data, struct mtd_info *mtd,
+                           struct vfsmount *mnt)
 {
        struct super_block *sb;
        struct jffs2_sb_info *c;
        int ret;
 
-       c = kmalloc(sizeof(*c), GFP_KERNEL);
+       c = kzalloc(sizeof(*c), GFP_KERNEL);
        if (!c)
-               return ERR_PTR(-ENOMEM);
-       memset(c, 0, sizeof(*c));
+               return -ENOMEM;
        c->mtd = mtd;
 
        sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
 
        if (IS_ERR(sb))
-               goto out_put;
+               goto out_error;
 
        if (sb->s_root) {
                /* New mountpoint for JFFS2 which is already mounted */
                D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n",
                          mtd->index, mtd->name));
+               ret = simple_set_mnt(mnt, sb);
                goto out_put;
        }
 
        D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
                  mtd->index, mtd->name));
 
-       sb->s_op = &jffs2_super_operations;
-       sb->s_flags |= MS_NOATIME;
+       /* Initialize JFFS2 superblock locks, the further initialization will be
+        * done later */
+       init_MUTEX(&c->alloc_sem);
+       init_MUTEX(&c->erase_free_sem);
+       init_waitqueue_head(&c->erase_wait);
+       init_waitqueue_head(&c->inocache_wq);
+       spin_lock_init(&c->erase_completion_lock);
+       spin_lock_init(&c->inocache_lock);
 
-       ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
+       sb->s_op = &jffs2_super_operations;
+       sb->s_flags = flags | MS_NOATIME;
+       sb->s_xattr = jffs2_xattr_handlers;
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+       sb->s_flags |= MS_POSIXACL;
+#endif
+       ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
 
        if (ret) {
                /* Failure case... */
                up_write(&sb->s_umount);
                deactivate_super(sb);
-               return ERR_PTR(ret);
+               return ret;
        }
 
        sb->s_flags |= MS_ACTIVE;
-       return sb;
+       return simple_set_mnt(mnt, sb);
 
+out_error:
+       ret = PTR_ERR(sb);
  out_put:
        kfree(c);
        put_mtd_device(mtd);
 
-       return sb;
+       return ret;
 }
 
-static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type,
-                                             int flags, const char *dev_name, 
-                                             void *data, int mtdnr)
+static int jffs2_get_sb_mtdnr(struct file_system_type *fs_type,
+                             int flags, const char *dev_name,
+                             void *data, int mtdnr,
+                             struct vfsmount *mnt)
 {
        struct mtd_info *mtd;
 
        mtd = get_mtd_device(NULL, mtdnr);
-       if (!mtd) {
+       if (IS_ERR(mtd)) {
                D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr));
-               return ERR_PTR(-EINVAL);
+               return PTR_ERR(mtd);
        }
 
-       return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
+       return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt);
 }
 
-static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
-                                       int flags, const char *dev_name,
-                                       void *data)
+static int jffs2_get_sb(struct file_system_type *fs_type,
+                       int flags, const char *dev_name,
+                       void *data, struct vfsmount *mnt)
 {
        int err;
        struct nameidata nd;
        int mtdnr;
 
        if (!dev_name)
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
 
        D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name));
 
        /* The preferred way of mounting in future; especially when
           CONFIG_BLK_DEV is implemented - we specify the underlying
-          MTD device by number or by name, so that we don't require 
+          MTD device by number or by name, so that we don't require
           block device support to be present in the kernel. */
 
        /* FIXME: How to do the root fs this way? */
@@ -194,9 +222,9 @@ static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
                        D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4));
                        for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) {
                                mtd = get_mtd_device(NULL, mtdnr);
-                               if (mtd) {
+                               if (!IS_ERR(mtd)) {
                                        if (!strcmp(mtd->name, dev_name+4))
-                                               return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
+                                               return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt);
                                        put_mtd_device(mtd);
                                }
                        }
@@ -204,17 +232,17 @@ static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
                } else if (isdigit(dev_name[3])) {
                        /* Mount by MTD device number name */
                        char *endptr;
-                       
+
                        mtdnr = simple_strtoul(dev_name+3, &endptr, 0);
                        if (!*endptr) {
                                /* It was a valid number */
                                D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr));
-                               return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
+                               return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr, mnt);
                        }
                }
        }
 
-       /* Try the old way - the hack where we allowed users to mount 
+       /* Try the old way - the hack where we allowed users to mount
           /dev/mtdblock$(n) but didn't actually _use_ the blkdev */
 
        err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
@@ -223,7 +251,7 @@ static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
                  err, nd.dentry->d_inode));
 
        if (err)
-               return ERR_PTR(err);
+               return err;
 
        err = -EINVAL;
 
@@ -236,7 +264,7 @@ static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
        }
 
        if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) {
-               if (!(flags & MS_VERBOSE)) /* Yes I mean this. Strangely */
+               if (!(flags & MS_SILENT))
                        printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n",
                               dev_name);
                goto out;
@@ -245,11 +273,11 @@ static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
        mtdnr = iminor(nd.dentry->d_inode);
        path_release(&nd);
 
-       return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
+       return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr, mnt);
 
 out:
        path_release(&nd);
-       return ERR_PTR(err);
+       return err;
 }
 
 static void jffs2_put_super (struct super_block *sb)
@@ -258,16 +286,21 @@ static void jffs2_put_super (struct super_block *sb)
 
        D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
 
-       if (!(sb->s_flags & MS_RDONLY))
-               jffs2_stop_garbage_collect_thread(c);
        down(&c->alloc_sem);
        jffs2_flush_wbuf_pad(c);
        up(&c->alloc_sem);
+
+       jffs2_sum_exit(c);
+
        jffs2_free_ino_caches(c);
        jffs2_free_raw_node_refs(c);
-       kfree(c->blocks);
-       jffs2_nand_flash_cleanup(c);
+       if (jffs2_blocks_use_vmalloc(c))
+               vfree(c->blocks);
+       else
+               kfree(c->blocks);
+       jffs2_flash_cleanup(c);
        kfree(c->inocache_list);
+       jffs2_clear_xattr_subsystem(c);
        if (c->mtd->sync)
                c->mtd->sync(c->mtd);
 
@@ -277,6 +310,8 @@ static void jffs2_put_super (struct super_block *sb)
 static void jffs2_kill_sb(struct super_block *sb)
 {
        struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+       if (!(sb->s_flags & MS_RDONLY))
+               jffs2_stop_garbage_collect_thread(c);
        generic_shutdown_super(sb);
        put_mtd_device(c->mtd);
        kfree(c);
@@ -293,29 +328,45 @@ static int __init init_jffs2_fs(void)
 {
        int ret;
 
+       /* Paranoia checks for on-medium structures. If we ask GCC
+          to pack them with __attribute__((packed)) then it _also_
+          assumes that they're not aligned -- so it emits crappy
+          code on some architectures. Ideally we want an attribute
+          which means just 'no padding', without the alignment
+          thing. But GCC doesn't have that -- we have to just
+          hope the structs are the right sizes, instead. */
+       BUILD_BUG_ON(sizeof(struct jffs2_unknown_node) != 12);
+       BUILD_BUG_ON(sizeof(struct jffs2_raw_dirent) != 40);
+       BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68);
+       BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32);
+
        printk(KERN_INFO "JFFS2 version 2.2."
-#ifdef CONFIG_FS_JFFS2_NAND
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
               " (NAND)"
 #endif
-              " (C) 2001-2003 Red Hat, Inc.\n");
+#ifdef CONFIG_JFFS2_SUMMARY
+              " (SUMMARY) "
+#endif
+              " (C) 2001-2006 Red Hat, Inc.\n");
 
        jffs2_inode_cachep = kmem_cache_create("jffs2_i",
                                             sizeof(struct jffs2_inode_info),
-                                            0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
+                                            0, (SLAB_RECLAIM_ACCOUNT|
+                                               SLAB_MEM_SPREAD),
                                             jffs2_i_init_once, NULL);
        if (!jffs2_inode_cachep) {
                printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
                return -ENOMEM;
        }
-       ret = jffs2_zlib_init();
+       ret = jffs2_compressors_init();
        if (ret) {
-               printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
+               printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
                goto out;
        }
        ret = jffs2_create_slab_caches();
        if (ret) {
                printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
-               goto out_zlib;
+               goto out_compressors;
        }
        ret = register_filesystem(&jffs2_fs_type);
        if (ret) {
@@ -326,9 +377,10 @@ static int __init init_jffs2_fs(void)
 
  out_slab:
        jffs2_destroy_slab_caches();
- out_zlib:
-       jffs2_zlib_exit();
+ out_compressors:
+       jffs2_compressors_exit();
  out:
+       kmem_cache_destroy(jffs2_inode_cachep);
        return ret;
 }
 
@@ -336,7 +388,7 @@ static void __exit exit_jffs2_fs(void)
 {
        unregister_filesystem(&jffs2_fs_type);
        jffs2_destroy_slab_caches();
-       jffs2_zlib_exit();
+       jffs2_compressors_exit();
        kmem_cache_destroy(jffs2_inode_cachep);
 }
 
@@ -345,5 +397,5 @@ module_exit(exit_jffs2_fs);
 
 MODULE_DESCRIPTION("The Journalling Flash File System, v2");
 MODULE_AUTHOR("Red Hat, Inc.");
-MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for 
+MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for
                       // the sake of this tag. It's Free Software.