11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * JFFS2 -- Journalling Flash File System, Version 2. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 2001-2003 Red Hat, Inc. 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * Created by David Woodhouse <dwmw2@infradead.org> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * For licensing information, see the file 'LICENCE' in this directory. 91da177e4SLinus Torvalds * 10182ec4eeSThomas Gleixner * $Id: super.c,v 1.110 2005/11/07 11:14:42 gleixner Exp $ 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include <linux/config.h> 151da177e4SLinus Torvalds #include <linux/kernel.h> 161da177e4SLinus Torvalds #include <linux/module.h> 171da177e4SLinus Torvalds #include <linux/slab.h> 181da177e4SLinus Torvalds #include <linux/init.h> 191da177e4SLinus Torvalds #include <linux/list.h> 201da177e4SLinus Torvalds #include <linux/fs.h> 211da177e4SLinus Torvalds #include <linux/mount.h> 221da177e4SLinus Torvalds #include <linux/jffs2.h> 231da177e4SLinus Torvalds #include <linux/pagemap.h> 241da177e4SLinus Torvalds #include <linux/mtd/mtd.h> 251da177e4SLinus Torvalds #include <linux/ctype.h> 261da177e4SLinus Torvalds #include <linux/namei.h> 271da177e4SLinus Torvalds #include "compr.h" 281da177e4SLinus Torvalds #include "nodelist.h" 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds static void jffs2_put_super(struct super_block *); 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds static kmem_cache_t *jffs2_inode_cachep; 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds static struct inode *jffs2_alloc_inode(struct super_block *sb) 351da177e4SLinus Torvalds { 361da177e4SLinus Torvalds struct jffs2_inode_info *ei; 371da177e4SLinus Torvalds ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL); 381da177e4SLinus Torvalds if (!ei) 391da177e4SLinus Torvalds return NULL; 401da177e4SLinus Torvalds return &ei->vfs_inode; 411da177e4SLinus Torvalds } 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds static void jffs2_destroy_inode(struct inode *inode) 441da177e4SLinus Torvalds { 451da177e4SLinus Torvalds kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode)); 461da177e4SLinus Torvalds } 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) 491da177e4SLinus Torvalds { 501da177e4SLinus Torvalds struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo; 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == 531da177e4SLinus Torvalds SLAB_CTOR_CONSTRUCTOR) { 5421eeb7aaSThomas Gleixner init_MUTEX(&ei->sem); 551da177e4SLinus Torvalds inode_init_once(&ei->vfs_inode); 561da177e4SLinus Torvalds } 571da177e4SLinus Torvalds } 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds static int jffs2_sync_fs(struct super_block *sb, int wait) 601da177e4SLinus Torvalds { 611da177e4SLinus Torvalds struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds down(&c->alloc_sem); 641da177e4SLinus Torvalds jffs2_flush_wbuf_pad(c); 651da177e4SLinus Torvalds up(&c->alloc_sem); 661da177e4SLinus Torvalds return 0; 671da177e4SLinus Torvalds } 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds static struct super_operations jffs2_super_operations = 701da177e4SLinus Torvalds { 711da177e4SLinus Torvalds .alloc_inode = jffs2_alloc_inode, 721da177e4SLinus Torvalds .destroy_inode =jffs2_destroy_inode, 731da177e4SLinus Torvalds .read_inode = jffs2_read_inode, 741da177e4SLinus Torvalds .put_super = jffs2_put_super, 751da177e4SLinus Torvalds .write_super = jffs2_write_super, 761da177e4SLinus Torvalds .statfs = jffs2_statfs, 771da177e4SLinus Torvalds .remount_fs = jffs2_remount_fs, 781da177e4SLinus Torvalds .clear_inode = jffs2_clear_inode, 791da177e4SLinus Torvalds .dirty_inode = jffs2_dirty_inode, 801da177e4SLinus Torvalds .sync_fs = jffs2_sync_fs, 811da177e4SLinus Torvalds }; 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds static int jffs2_sb_compare(struct super_block *sb, void *data) 841da177e4SLinus Torvalds { 851da177e4SLinus Torvalds struct jffs2_sb_info *p = data; 861da177e4SLinus Torvalds struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds /* The superblocks are considered to be equivalent if the underlying MTD 891da177e4SLinus Torvalds device is the same one */ 901da177e4SLinus Torvalds if (c->mtd == p->mtd) { 911da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name)); 921da177e4SLinus Torvalds return 1; 931da177e4SLinus Torvalds } else { 941da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n", 951da177e4SLinus Torvalds c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name)); 961da177e4SLinus Torvalds return 0; 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds } 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds static int jffs2_sb_set(struct super_block *sb, void *data) 1011da177e4SLinus Torvalds { 1021da177e4SLinus Torvalds struct jffs2_sb_info *p = data; 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds /* For persistence of NFS exports etc. we use the same s_dev 1051da177e4SLinus Torvalds each time we mount the device, don't just use an anonymous 1061da177e4SLinus Torvalds device */ 1071da177e4SLinus Torvalds sb->s_fs_info = p; 1081da177e4SLinus Torvalds p->os_priv = sb; 1091da177e4SLinus Torvalds sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index); 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds return 0; 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, 1151da177e4SLinus Torvalds int flags, const char *dev_name, 1161da177e4SLinus Torvalds void *data, struct mtd_info *mtd) 1171da177e4SLinus Torvalds { 1181da177e4SLinus Torvalds struct super_block *sb; 1191da177e4SLinus Torvalds struct jffs2_sb_info *c; 1201da177e4SLinus Torvalds int ret; 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds c = kmalloc(sizeof(*c), GFP_KERNEL); 1231da177e4SLinus Torvalds if (!c) 1241da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 1251da177e4SLinus Torvalds memset(c, 0, sizeof(*c)); 1261da177e4SLinus Torvalds c->mtd = mtd; 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c); 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds if (IS_ERR(sb)) 1311da177e4SLinus Torvalds goto out_put; 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds if (sb->s_root) { 1341da177e4SLinus Torvalds /* New mountpoint for JFFS2 which is already mounted */ 1351da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n", 1361da177e4SLinus Torvalds mtd->index, mtd->name)); 1371da177e4SLinus Torvalds goto out_put; 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n", 1411da177e4SLinus Torvalds mtd->index, mtd->name)); 1421da177e4SLinus Torvalds 143b6220598SArtem B. Bityuckiy /* Initialize JFFS2 superblock locks, the further initialization will be 144b6220598SArtem B. Bityuckiy * done later */ 145b6220598SArtem B. Bityuckiy init_MUTEX(&c->alloc_sem); 146b6220598SArtem B. Bityuckiy init_MUTEX(&c->erase_free_sem); 147b6220598SArtem B. Bityuckiy init_waitqueue_head(&c->erase_wait); 148b6220598SArtem B. Bityuckiy init_waitqueue_head(&c->inocache_wq); 149b6220598SArtem B. Bityuckiy spin_lock_init(&c->erase_completion_lock); 150b6220598SArtem B. Bityuckiy spin_lock_init(&c->inocache_lock); 151b6220598SArtem B. Bityuckiy 1521da177e4SLinus Torvalds sb->s_op = &jffs2_super_operations; 1531da177e4SLinus Torvalds sb->s_flags = flags | MS_NOATIME; 154*aa98d7cfSKaiGai Kohei sb->s_xattr = jffs2_xattr_handlers; 155*aa98d7cfSKaiGai Kohei #ifdef CONFIG_JFFS2_FS_POSIX_ACL 156*aa98d7cfSKaiGai Kohei sb->s_flags |= MS_POSIXACL; 157*aa98d7cfSKaiGai Kohei #endif 1589b04c997STheodore Ts'o ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds if (ret) { 1611da177e4SLinus Torvalds /* Failure case... */ 1621da177e4SLinus Torvalds up_write(&sb->s_umount); 1631da177e4SLinus Torvalds deactivate_super(sb); 1641da177e4SLinus Torvalds return ERR_PTR(ret); 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds sb->s_flags |= MS_ACTIVE; 1681da177e4SLinus Torvalds return sb; 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds out_put: 1711da177e4SLinus Torvalds kfree(c); 1721da177e4SLinus Torvalds put_mtd_device(mtd); 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds return sb; 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type, 1781da177e4SLinus Torvalds int flags, const char *dev_name, 1791da177e4SLinus Torvalds void *data, int mtdnr) 1801da177e4SLinus Torvalds { 1811da177e4SLinus Torvalds struct mtd_info *mtd; 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds mtd = get_mtd_device(NULL, mtdnr); 1841da177e4SLinus Torvalds if (!mtd) { 1851da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr)); 1861da177e4SLinus Torvalds return ERR_PTR(-EINVAL); 1871da177e4SLinus Torvalds } 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); 1901da177e4SLinus Torvalds } 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds static struct super_block *jffs2_get_sb(struct file_system_type *fs_type, 1931da177e4SLinus Torvalds int flags, const char *dev_name, 1941da177e4SLinus Torvalds void *data) 1951da177e4SLinus Torvalds { 1961da177e4SLinus Torvalds int err; 1971da177e4SLinus Torvalds struct nameidata nd; 1981da177e4SLinus Torvalds int mtdnr; 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds if (!dev_name) 2011da177e4SLinus Torvalds return ERR_PTR(-EINVAL); 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name)); 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds /* The preferred way of mounting in future; especially when 2061da177e4SLinus Torvalds CONFIG_BLK_DEV is implemented - we specify the underlying 2071da177e4SLinus Torvalds MTD device by number or by name, so that we don't require 2081da177e4SLinus Torvalds block device support to be present in the kernel. */ 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds /* FIXME: How to do the root fs this way? */ 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { 2131da177e4SLinus Torvalds /* Probably mounting without the blkdev crap */ 2141da177e4SLinus Torvalds if (dev_name[3] == ':') { 2151da177e4SLinus Torvalds struct mtd_info *mtd; 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds /* Mount by MTD device name */ 2181da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4)); 2191da177e4SLinus Torvalds for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { 2201da177e4SLinus Torvalds mtd = get_mtd_device(NULL, mtdnr); 2211da177e4SLinus Torvalds if (mtd) { 2221da177e4SLinus Torvalds if (!strcmp(mtd->name, dev_name+4)) 2231da177e4SLinus Torvalds return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); 2241da177e4SLinus Torvalds put_mtd_device(mtd); 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4); 2281da177e4SLinus Torvalds } else if (isdigit(dev_name[3])) { 2291da177e4SLinus Torvalds /* Mount by MTD device number name */ 2301da177e4SLinus Torvalds char *endptr; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds mtdnr = simple_strtoul(dev_name+3, &endptr, 0); 2331da177e4SLinus Torvalds if (!*endptr) { 2341da177e4SLinus Torvalds /* It was a valid number */ 2351da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr)); 2361da177e4SLinus Torvalds return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); 2371da177e4SLinus Torvalds } 2381da177e4SLinus Torvalds } 2391da177e4SLinus Torvalds } 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds /* Try the old way - the hack where we allowed users to mount 2421da177e4SLinus Torvalds /dev/mtdblock$(n) but didn't actually _use_ the blkdev */ 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n", 2471da177e4SLinus Torvalds err, nd.dentry->d_inode)); 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds if (err) 2501da177e4SLinus Torvalds return ERR_PTR(err); 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds err = -EINVAL; 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds if (!S_ISBLK(nd.dentry->d_inode->i_mode)) 2551da177e4SLinus Torvalds goto out; 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds if (nd.mnt->mnt_flags & MNT_NODEV) { 2581da177e4SLinus Torvalds err = -EACCES; 2591da177e4SLinus Torvalds goto out; 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) { 2639b04c997STheodore Ts'o if (!(flags & MS_SILENT)) 2641da177e4SLinus Torvalds printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n", 2651da177e4SLinus Torvalds dev_name); 2661da177e4SLinus Torvalds goto out; 2671da177e4SLinus Torvalds } 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds mtdnr = iminor(nd.dentry->d_inode); 2701da177e4SLinus Torvalds path_release(&nd); 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds out: 2751da177e4SLinus Torvalds path_release(&nd); 2761da177e4SLinus Torvalds return ERR_PTR(err); 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds static void jffs2_put_super (struct super_block *sb) 2801da177e4SLinus Torvalds { 2811da177e4SLinus Torvalds struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds down(&c->alloc_sem); 2861da177e4SLinus Torvalds jffs2_flush_wbuf_pad(c); 2871da177e4SLinus Torvalds up(&c->alloc_sem); 288e631ddbaSFerenc Havasi 289e631ddbaSFerenc Havasi jffs2_sum_exit(c); 290e631ddbaSFerenc Havasi 2911da177e4SLinus Torvalds jffs2_free_ino_caches(c); 2921da177e4SLinus Torvalds jffs2_free_raw_node_refs(c); 2934ce1f562SFerenc Havasi if (jffs2_blocks_use_vmalloc(c)) 2941da177e4SLinus Torvalds vfree(c->blocks); 2951da177e4SLinus Torvalds else 2961da177e4SLinus Torvalds kfree(c->blocks); 2971da177e4SLinus Torvalds jffs2_flash_cleanup(c); 2981da177e4SLinus Torvalds kfree(c->inocache_list); 299*aa98d7cfSKaiGai Kohei jffs2_clear_xattr_subsystem(c); 3001da177e4SLinus Torvalds if (c->mtd->sync) 3011da177e4SLinus Torvalds c->mtd->sync(c->mtd); 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); 3041da177e4SLinus Torvalds } 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds static void jffs2_kill_sb(struct super_block *sb) 3071da177e4SLinus Torvalds { 3081da177e4SLinus Torvalds struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); 309a69dde91SArtem B. Bityuckiy if (!(sb->s_flags & MS_RDONLY)) 310a69dde91SArtem B. Bityuckiy jffs2_stop_garbage_collect_thread(c); 3111da177e4SLinus Torvalds generic_shutdown_super(sb); 3121da177e4SLinus Torvalds put_mtd_device(c->mtd); 3131da177e4SLinus Torvalds kfree(c); 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds static struct file_system_type jffs2_fs_type = { 3171da177e4SLinus Torvalds .owner = THIS_MODULE, 3181da177e4SLinus Torvalds .name = "jffs2", 3191da177e4SLinus Torvalds .get_sb = jffs2_get_sb, 3201da177e4SLinus Torvalds .kill_sb = jffs2_kill_sb, 3211da177e4SLinus Torvalds }; 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds static int __init init_jffs2_fs(void) 3241da177e4SLinus Torvalds { 3251da177e4SLinus Torvalds int ret; 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds printk(KERN_INFO "JFFS2 version 2.2." 3282f82ce1eSAndrew Victor #ifdef CONFIG_JFFS2_FS_WRITEBUFFER 3291da177e4SLinus Torvalds " (NAND)" 3301da177e4SLinus Torvalds #endif 331e631ddbaSFerenc Havasi #ifdef CONFIG_JFFS2_SUMMARY 332e631ddbaSFerenc Havasi " (SUMMARY) " 333e631ddbaSFerenc Havasi #endif 3341da177e4SLinus Torvalds " (C) 2001-2003 Red Hat, Inc.\n"); 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds jffs2_inode_cachep = kmem_cache_create("jffs2_i", 3371da177e4SLinus Torvalds sizeof(struct jffs2_inode_info), 338fffb60f9SPaul Jackson 0, (SLAB_RECLAIM_ACCOUNT| 339fffb60f9SPaul Jackson SLAB_MEM_SPREAD), 3401da177e4SLinus Torvalds jffs2_i_init_once, NULL); 3411da177e4SLinus Torvalds if (!jffs2_inode_cachep) { 3421da177e4SLinus Torvalds printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n"); 3431da177e4SLinus Torvalds return -ENOMEM; 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds ret = jffs2_compressors_init(); 3461da177e4SLinus Torvalds if (ret) { 3471da177e4SLinus Torvalds printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n"); 3481da177e4SLinus Torvalds goto out; 3491da177e4SLinus Torvalds } 3501da177e4SLinus Torvalds ret = jffs2_create_slab_caches(); 3511da177e4SLinus Torvalds if (ret) { 3521da177e4SLinus Torvalds printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); 3531da177e4SLinus Torvalds goto out_compressors; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds ret = register_filesystem(&jffs2_fs_type); 3561da177e4SLinus Torvalds if (ret) { 3571da177e4SLinus Torvalds printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n"); 3581da177e4SLinus Torvalds goto out_slab; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds return 0; 3611da177e4SLinus Torvalds 3621da177e4SLinus Torvalds out_slab: 3631da177e4SLinus Torvalds jffs2_destroy_slab_caches(); 3641da177e4SLinus Torvalds out_compressors: 3651da177e4SLinus Torvalds jffs2_compressors_exit(); 3661da177e4SLinus Torvalds out: 3671da177e4SLinus Torvalds kmem_cache_destroy(jffs2_inode_cachep); 3681da177e4SLinus Torvalds return ret; 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds static void __exit exit_jffs2_fs(void) 3721da177e4SLinus Torvalds { 3731da177e4SLinus Torvalds unregister_filesystem(&jffs2_fs_type); 3741da177e4SLinus Torvalds jffs2_destroy_slab_caches(); 3751da177e4SLinus Torvalds jffs2_compressors_exit(); 3761da177e4SLinus Torvalds kmem_cache_destroy(jffs2_inode_cachep); 3771da177e4SLinus Torvalds } 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds module_init(init_jffs2_fs); 3801da177e4SLinus Torvalds module_exit(exit_jffs2_fs); 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds MODULE_DESCRIPTION("The Journalling Flash File System, v2"); 3831da177e4SLinus Torvalds MODULE_AUTHOR("Red Hat, Inc."); 3841da177e4SLinus Torvalds MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for 3851da177e4SLinus Torvalds // the sake of this tag. It's Free Software. 386