1*1da177e4SLinus Torvalds /* 2*1da177e4SLinus Torvalds * JFFS2 -- Journalling Flash File System, Version 2. 3*1da177e4SLinus Torvalds * 4*1da177e4SLinus Torvalds * Copyright (C) 2001-2003 Red Hat, Inc. 5*1da177e4SLinus Torvalds * 6*1da177e4SLinus Torvalds * Created by David Woodhouse <dwmw2@infradead.org> 7*1da177e4SLinus Torvalds * 8*1da177e4SLinus Torvalds * For licensing information, see the file 'LICENCE' in this directory. 9*1da177e4SLinus Torvalds * 10*1da177e4SLinus Torvalds * $Id: super.c,v 1.104 2004/11/23 15:37:31 gleixner Exp $ 11*1da177e4SLinus Torvalds * 12*1da177e4SLinus Torvalds */ 13*1da177e4SLinus Torvalds 14*1da177e4SLinus Torvalds #include <linux/config.h> 15*1da177e4SLinus Torvalds #include <linux/kernel.h> 16*1da177e4SLinus Torvalds #include <linux/module.h> 17*1da177e4SLinus Torvalds #include <linux/slab.h> 18*1da177e4SLinus Torvalds #include <linux/init.h> 19*1da177e4SLinus Torvalds #include <linux/list.h> 20*1da177e4SLinus Torvalds #include <linux/fs.h> 21*1da177e4SLinus Torvalds #include <linux/mount.h> 22*1da177e4SLinus Torvalds #include <linux/jffs2.h> 23*1da177e4SLinus Torvalds #include <linux/pagemap.h> 24*1da177e4SLinus Torvalds #include <linux/mtd/mtd.h> 25*1da177e4SLinus Torvalds #include <linux/ctype.h> 26*1da177e4SLinus Torvalds #include <linux/namei.h> 27*1da177e4SLinus Torvalds #include "compr.h" 28*1da177e4SLinus Torvalds #include "nodelist.h" 29*1da177e4SLinus Torvalds 30*1da177e4SLinus Torvalds static void jffs2_put_super(struct super_block *); 31*1da177e4SLinus Torvalds 32*1da177e4SLinus Torvalds static kmem_cache_t *jffs2_inode_cachep; 33*1da177e4SLinus Torvalds 34*1da177e4SLinus Torvalds static struct inode *jffs2_alloc_inode(struct super_block *sb) 35*1da177e4SLinus Torvalds { 36*1da177e4SLinus Torvalds struct jffs2_inode_info *ei; 37*1da177e4SLinus Torvalds ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL); 38*1da177e4SLinus Torvalds if (!ei) 39*1da177e4SLinus Torvalds return NULL; 40*1da177e4SLinus Torvalds return &ei->vfs_inode; 41*1da177e4SLinus Torvalds } 42*1da177e4SLinus Torvalds 43*1da177e4SLinus Torvalds static void jffs2_destroy_inode(struct inode *inode) 44*1da177e4SLinus Torvalds { 45*1da177e4SLinus Torvalds kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode)); 46*1da177e4SLinus Torvalds } 47*1da177e4SLinus Torvalds 48*1da177e4SLinus Torvalds static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) 49*1da177e4SLinus Torvalds { 50*1da177e4SLinus Torvalds struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo; 51*1da177e4SLinus Torvalds 52*1da177e4SLinus Torvalds if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == 53*1da177e4SLinus Torvalds SLAB_CTOR_CONSTRUCTOR) { 54*1da177e4SLinus Torvalds init_MUTEX_LOCKED(&ei->sem); 55*1da177e4SLinus Torvalds inode_init_once(&ei->vfs_inode); 56*1da177e4SLinus Torvalds } 57*1da177e4SLinus Torvalds } 58*1da177e4SLinus Torvalds 59*1da177e4SLinus Torvalds static int jffs2_sync_fs(struct super_block *sb, int wait) 60*1da177e4SLinus Torvalds { 61*1da177e4SLinus Torvalds struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); 62*1da177e4SLinus Torvalds 63*1da177e4SLinus Torvalds down(&c->alloc_sem); 64*1da177e4SLinus Torvalds jffs2_flush_wbuf_pad(c); 65*1da177e4SLinus Torvalds up(&c->alloc_sem); 66*1da177e4SLinus Torvalds return 0; 67*1da177e4SLinus Torvalds } 68*1da177e4SLinus Torvalds 69*1da177e4SLinus Torvalds static struct super_operations jffs2_super_operations = 70*1da177e4SLinus Torvalds { 71*1da177e4SLinus Torvalds .alloc_inode = jffs2_alloc_inode, 72*1da177e4SLinus Torvalds .destroy_inode =jffs2_destroy_inode, 73*1da177e4SLinus Torvalds .read_inode = jffs2_read_inode, 74*1da177e4SLinus Torvalds .put_super = jffs2_put_super, 75*1da177e4SLinus Torvalds .write_super = jffs2_write_super, 76*1da177e4SLinus Torvalds .statfs = jffs2_statfs, 77*1da177e4SLinus Torvalds .remount_fs = jffs2_remount_fs, 78*1da177e4SLinus Torvalds .clear_inode = jffs2_clear_inode, 79*1da177e4SLinus Torvalds .dirty_inode = jffs2_dirty_inode, 80*1da177e4SLinus Torvalds .sync_fs = jffs2_sync_fs, 81*1da177e4SLinus Torvalds }; 82*1da177e4SLinus Torvalds 83*1da177e4SLinus Torvalds static int jffs2_sb_compare(struct super_block *sb, void *data) 84*1da177e4SLinus Torvalds { 85*1da177e4SLinus Torvalds struct jffs2_sb_info *p = data; 86*1da177e4SLinus Torvalds struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); 87*1da177e4SLinus Torvalds 88*1da177e4SLinus Torvalds /* The superblocks are considered to be equivalent if the underlying MTD 89*1da177e4SLinus Torvalds device is the same one */ 90*1da177e4SLinus Torvalds if (c->mtd == p->mtd) { 91*1da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name)); 92*1da177e4SLinus Torvalds return 1; 93*1da177e4SLinus Torvalds } else { 94*1da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n", 95*1da177e4SLinus Torvalds c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name)); 96*1da177e4SLinus Torvalds return 0; 97*1da177e4SLinus Torvalds } 98*1da177e4SLinus Torvalds } 99*1da177e4SLinus Torvalds 100*1da177e4SLinus Torvalds static int jffs2_sb_set(struct super_block *sb, void *data) 101*1da177e4SLinus Torvalds { 102*1da177e4SLinus Torvalds struct jffs2_sb_info *p = data; 103*1da177e4SLinus Torvalds 104*1da177e4SLinus Torvalds /* For persistence of NFS exports etc. we use the same s_dev 105*1da177e4SLinus Torvalds each time we mount the device, don't just use an anonymous 106*1da177e4SLinus Torvalds device */ 107*1da177e4SLinus Torvalds sb->s_fs_info = p; 108*1da177e4SLinus Torvalds p->os_priv = sb; 109*1da177e4SLinus Torvalds sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index); 110*1da177e4SLinus Torvalds 111*1da177e4SLinus Torvalds return 0; 112*1da177e4SLinus Torvalds } 113*1da177e4SLinus Torvalds 114*1da177e4SLinus Torvalds static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, 115*1da177e4SLinus Torvalds int flags, const char *dev_name, 116*1da177e4SLinus Torvalds void *data, struct mtd_info *mtd) 117*1da177e4SLinus Torvalds { 118*1da177e4SLinus Torvalds struct super_block *sb; 119*1da177e4SLinus Torvalds struct jffs2_sb_info *c; 120*1da177e4SLinus Torvalds int ret; 121*1da177e4SLinus Torvalds 122*1da177e4SLinus Torvalds c = kmalloc(sizeof(*c), GFP_KERNEL); 123*1da177e4SLinus Torvalds if (!c) 124*1da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 125*1da177e4SLinus Torvalds memset(c, 0, sizeof(*c)); 126*1da177e4SLinus Torvalds c->mtd = mtd; 127*1da177e4SLinus Torvalds 128*1da177e4SLinus Torvalds sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c); 129*1da177e4SLinus Torvalds 130*1da177e4SLinus Torvalds if (IS_ERR(sb)) 131*1da177e4SLinus Torvalds goto out_put; 132*1da177e4SLinus Torvalds 133*1da177e4SLinus Torvalds if (sb->s_root) { 134*1da177e4SLinus Torvalds /* New mountpoint for JFFS2 which is already mounted */ 135*1da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n", 136*1da177e4SLinus Torvalds mtd->index, mtd->name)); 137*1da177e4SLinus Torvalds goto out_put; 138*1da177e4SLinus Torvalds } 139*1da177e4SLinus Torvalds 140*1da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n", 141*1da177e4SLinus Torvalds mtd->index, mtd->name)); 142*1da177e4SLinus Torvalds 143*1da177e4SLinus Torvalds sb->s_op = &jffs2_super_operations; 144*1da177e4SLinus Torvalds sb->s_flags = flags | MS_NOATIME; 145*1da177e4SLinus Torvalds 146*1da177e4SLinus Torvalds ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0); 147*1da177e4SLinus Torvalds 148*1da177e4SLinus Torvalds if (ret) { 149*1da177e4SLinus Torvalds /* Failure case... */ 150*1da177e4SLinus Torvalds up_write(&sb->s_umount); 151*1da177e4SLinus Torvalds deactivate_super(sb); 152*1da177e4SLinus Torvalds return ERR_PTR(ret); 153*1da177e4SLinus Torvalds } 154*1da177e4SLinus Torvalds 155*1da177e4SLinus Torvalds sb->s_flags |= MS_ACTIVE; 156*1da177e4SLinus Torvalds return sb; 157*1da177e4SLinus Torvalds 158*1da177e4SLinus Torvalds out_put: 159*1da177e4SLinus Torvalds kfree(c); 160*1da177e4SLinus Torvalds put_mtd_device(mtd); 161*1da177e4SLinus Torvalds 162*1da177e4SLinus Torvalds return sb; 163*1da177e4SLinus Torvalds } 164*1da177e4SLinus Torvalds 165*1da177e4SLinus Torvalds static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type, 166*1da177e4SLinus Torvalds int flags, const char *dev_name, 167*1da177e4SLinus Torvalds void *data, int mtdnr) 168*1da177e4SLinus Torvalds { 169*1da177e4SLinus Torvalds struct mtd_info *mtd; 170*1da177e4SLinus Torvalds 171*1da177e4SLinus Torvalds mtd = get_mtd_device(NULL, mtdnr); 172*1da177e4SLinus Torvalds if (!mtd) { 173*1da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr)); 174*1da177e4SLinus Torvalds return ERR_PTR(-EINVAL); 175*1da177e4SLinus Torvalds } 176*1da177e4SLinus Torvalds 177*1da177e4SLinus Torvalds return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); 178*1da177e4SLinus Torvalds } 179*1da177e4SLinus Torvalds 180*1da177e4SLinus Torvalds static struct super_block *jffs2_get_sb(struct file_system_type *fs_type, 181*1da177e4SLinus Torvalds int flags, const char *dev_name, 182*1da177e4SLinus Torvalds void *data) 183*1da177e4SLinus Torvalds { 184*1da177e4SLinus Torvalds int err; 185*1da177e4SLinus Torvalds struct nameidata nd; 186*1da177e4SLinus Torvalds int mtdnr; 187*1da177e4SLinus Torvalds 188*1da177e4SLinus Torvalds if (!dev_name) 189*1da177e4SLinus Torvalds return ERR_PTR(-EINVAL); 190*1da177e4SLinus Torvalds 191*1da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name)); 192*1da177e4SLinus Torvalds 193*1da177e4SLinus Torvalds /* The preferred way of mounting in future; especially when 194*1da177e4SLinus Torvalds CONFIG_BLK_DEV is implemented - we specify the underlying 195*1da177e4SLinus Torvalds MTD device by number or by name, so that we don't require 196*1da177e4SLinus Torvalds block device support to be present in the kernel. */ 197*1da177e4SLinus Torvalds 198*1da177e4SLinus Torvalds /* FIXME: How to do the root fs this way? */ 199*1da177e4SLinus Torvalds 200*1da177e4SLinus Torvalds if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { 201*1da177e4SLinus Torvalds /* Probably mounting without the blkdev crap */ 202*1da177e4SLinus Torvalds if (dev_name[3] == ':') { 203*1da177e4SLinus Torvalds struct mtd_info *mtd; 204*1da177e4SLinus Torvalds 205*1da177e4SLinus Torvalds /* Mount by MTD device name */ 206*1da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4)); 207*1da177e4SLinus Torvalds for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { 208*1da177e4SLinus Torvalds mtd = get_mtd_device(NULL, mtdnr); 209*1da177e4SLinus Torvalds if (mtd) { 210*1da177e4SLinus Torvalds if (!strcmp(mtd->name, dev_name+4)) 211*1da177e4SLinus Torvalds return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); 212*1da177e4SLinus Torvalds put_mtd_device(mtd); 213*1da177e4SLinus Torvalds } 214*1da177e4SLinus Torvalds } 215*1da177e4SLinus Torvalds printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4); 216*1da177e4SLinus Torvalds } else if (isdigit(dev_name[3])) { 217*1da177e4SLinus Torvalds /* Mount by MTD device number name */ 218*1da177e4SLinus Torvalds char *endptr; 219*1da177e4SLinus Torvalds 220*1da177e4SLinus Torvalds mtdnr = simple_strtoul(dev_name+3, &endptr, 0); 221*1da177e4SLinus Torvalds if (!*endptr) { 222*1da177e4SLinus Torvalds /* It was a valid number */ 223*1da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr)); 224*1da177e4SLinus Torvalds return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); 225*1da177e4SLinus Torvalds } 226*1da177e4SLinus Torvalds } 227*1da177e4SLinus Torvalds } 228*1da177e4SLinus Torvalds 229*1da177e4SLinus Torvalds /* Try the old way - the hack where we allowed users to mount 230*1da177e4SLinus Torvalds /dev/mtdblock$(n) but didn't actually _use_ the blkdev */ 231*1da177e4SLinus Torvalds 232*1da177e4SLinus Torvalds err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); 233*1da177e4SLinus Torvalds 234*1da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n", 235*1da177e4SLinus Torvalds err, nd.dentry->d_inode)); 236*1da177e4SLinus Torvalds 237*1da177e4SLinus Torvalds if (err) 238*1da177e4SLinus Torvalds return ERR_PTR(err); 239*1da177e4SLinus Torvalds 240*1da177e4SLinus Torvalds err = -EINVAL; 241*1da177e4SLinus Torvalds 242*1da177e4SLinus Torvalds if (!S_ISBLK(nd.dentry->d_inode->i_mode)) 243*1da177e4SLinus Torvalds goto out; 244*1da177e4SLinus Torvalds 245*1da177e4SLinus Torvalds if (nd.mnt->mnt_flags & MNT_NODEV) { 246*1da177e4SLinus Torvalds err = -EACCES; 247*1da177e4SLinus Torvalds goto out; 248*1da177e4SLinus Torvalds } 249*1da177e4SLinus Torvalds 250*1da177e4SLinus Torvalds if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) { 251*1da177e4SLinus Torvalds if (!(flags & MS_VERBOSE)) /* Yes I mean this. Strangely */ 252*1da177e4SLinus Torvalds printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n", 253*1da177e4SLinus Torvalds dev_name); 254*1da177e4SLinus Torvalds goto out; 255*1da177e4SLinus Torvalds } 256*1da177e4SLinus Torvalds 257*1da177e4SLinus Torvalds mtdnr = iminor(nd.dentry->d_inode); 258*1da177e4SLinus Torvalds path_release(&nd); 259*1da177e4SLinus Torvalds 260*1da177e4SLinus Torvalds return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); 261*1da177e4SLinus Torvalds 262*1da177e4SLinus Torvalds out: 263*1da177e4SLinus Torvalds path_release(&nd); 264*1da177e4SLinus Torvalds return ERR_PTR(err); 265*1da177e4SLinus Torvalds } 266*1da177e4SLinus Torvalds 267*1da177e4SLinus Torvalds static void jffs2_put_super (struct super_block *sb) 268*1da177e4SLinus Torvalds { 269*1da177e4SLinus Torvalds struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); 270*1da177e4SLinus Torvalds 271*1da177e4SLinus Torvalds D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); 272*1da177e4SLinus Torvalds 273*1da177e4SLinus Torvalds if (!(sb->s_flags & MS_RDONLY)) 274*1da177e4SLinus Torvalds jffs2_stop_garbage_collect_thread(c); 275*1da177e4SLinus Torvalds down(&c->alloc_sem); 276*1da177e4SLinus Torvalds jffs2_flush_wbuf_pad(c); 277*1da177e4SLinus Torvalds up(&c->alloc_sem); 278*1da177e4SLinus Torvalds jffs2_free_ino_caches(c); 279*1da177e4SLinus Torvalds jffs2_free_raw_node_refs(c); 280*1da177e4SLinus Torvalds if (c->mtd->flags & MTD_NO_VIRTBLOCKS) 281*1da177e4SLinus Torvalds vfree(c->blocks); 282*1da177e4SLinus Torvalds else 283*1da177e4SLinus Torvalds kfree(c->blocks); 284*1da177e4SLinus Torvalds jffs2_flash_cleanup(c); 285*1da177e4SLinus Torvalds kfree(c->inocache_list); 286*1da177e4SLinus Torvalds if (c->mtd->sync) 287*1da177e4SLinus Torvalds c->mtd->sync(c->mtd); 288*1da177e4SLinus Torvalds 289*1da177e4SLinus Torvalds D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); 290*1da177e4SLinus Torvalds } 291*1da177e4SLinus Torvalds 292*1da177e4SLinus Torvalds static void jffs2_kill_sb(struct super_block *sb) 293*1da177e4SLinus Torvalds { 294*1da177e4SLinus Torvalds struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); 295*1da177e4SLinus Torvalds generic_shutdown_super(sb); 296*1da177e4SLinus Torvalds put_mtd_device(c->mtd); 297*1da177e4SLinus Torvalds kfree(c); 298*1da177e4SLinus Torvalds } 299*1da177e4SLinus Torvalds 300*1da177e4SLinus Torvalds static struct file_system_type jffs2_fs_type = { 301*1da177e4SLinus Torvalds .owner = THIS_MODULE, 302*1da177e4SLinus Torvalds .name = "jffs2", 303*1da177e4SLinus Torvalds .get_sb = jffs2_get_sb, 304*1da177e4SLinus Torvalds .kill_sb = jffs2_kill_sb, 305*1da177e4SLinus Torvalds }; 306*1da177e4SLinus Torvalds 307*1da177e4SLinus Torvalds static int __init init_jffs2_fs(void) 308*1da177e4SLinus Torvalds { 309*1da177e4SLinus Torvalds int ret; 310*1da177e4SLinus Torvalds 311*1da177e4SLinus Torvalds printk(KERN_INFO "JFFS2 version 2.2." 312*1da177e4SLinus Torvalds #ifdef CONFIG_JFFS2_FS_NAND 313*1da177e4SLinus Torvalds " (NAND)" 314*1da177e4SLinus Torvalds #endif 315*1da177e4SLinus Torvalds " (C) 2001-2003 Red Hat, Inc.\n"); 316*1da177e4SLinus Torvalds 317*1da177e4SLinus Torvalds jffs2_inode_cachep = kmem_cache_create("jffs2_i", 318*1da177e4SLinus Torvalds sizeof(struct jffs2_inode_info), 319*1da177e4SLinus Torvalds 0, SLAB_RECLAIM_ACCOUNT, 320*1da177e4SLinus Torvalds jffs2_i_init_once, NULL); 321*1da177e4SLinus Torvalds if (!jffs2_inode_cachep) { 322*1da177e4SLinus Torvalds printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n"); 323*1da177e4SLinus Torvalds return -ENOMEM; 324*1da177e4SLinus Torvalds } 325*1da177e4SLinus Torvalds ret = jffs2_compressors_init(); 326*1da177e4SLinus Torvalds if (ret) { 327*1da177e4SLinus Torvalds printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n"); 328*1da177e4SLinus Torvalds goto out; 329*1da177e4SLinus Torvalds } 330*1da177e4SLinus Torvalds ret = jffs2_create_slab_caches(); 331*1da177e4SLinus Torvalds if (ret) { 332*1da177e4SLinus Torvalds printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); 333*1da177e4SLinus Torvalds goto out_compressors; 334*1da177e4SLinus Torvalds } 335*1da177e4SLinus Torvalds ret = register_filesystem(&jffs2_fs_type); 336*1da177e4SLinus Torvalds if (ret) { 337*1da177e4SLinus Torvalds printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n"); 338*1da177e4SLinus Torvalds goto out_slab; 339*1da177e4SLinus Torvalds } 340*1da177e4SLinus Torvalds return 0; 341*1da177e4SLinus Torvalds 342*1da177e4SLinus Torvalds out_slab: 343*1da177e4SLinus Torvalds jffs2_destroy_slab_caches(); 344*1da177e4SLinus Torvalds out_compressors: 345*1da177e4SLinus Torvalds jffs2_compressors_exit(); 346*1da177e4SLinus Torvalds out: 347*1da177e4SLinus Torvalds kmem_cache_destroy(jffs2_inode_cachep); 348*1da177e4SLinus Torvalds return ret; 349*1da177e4SLinus Torvalds } 350*1da177e4SLinus Torvalds 351*1da177e4SLinus Torvalds static void __exit exit_jffs2_fs(void) 352*1da177e4SLinus Torvalds { 353*1da177e4SLinus Torvalds unregister_filesystem(&jffs2_fs_type); 354*1da177e4SLinus Torvalds jffs2_destroy_slab_caches(); 355*1da177e4SLinus Torvalds jffs2_compressors_exit(); 356*1da177e4SLinus Torvalds kmem_cache_destroy(jffs2_inode_cachep); 357*1da177e4SLinus Torvalds } 358*1da177e4SLinus Torvalds 359*1da177e4SLinus Torvalds module_init(init_jffs2_fs); 360*1da177e4SLinus Torvalds module_exit(exit_jffs2_fs); 361*1da177e4SLinus Torvalds 362*1da177e4SLinus Torvalds MODULE_DESCRIPTION("The Journalling Flash File System, v2"); 363*1da177e4SLinus Torvalds MODULE_AUTHOR("Red Hat, Inc."); 364*1da177e4SLinus Torvalds MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for 365*1da177e4SLinus Torvalds // the sake of this tag. It's Free Software. 366