[MTD] generalise the handling of MTD-specific superblocks

Generalise the handling of MTD-specific superblocks so that JFFS2 and ROMFS
can both share it.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>

authored by David Howells and committed by David Woodhouse acaebfd8 33672f72

+283 -177
+1 -2
drivers/mtd/Makefile
··· 1 # 2 # Makefile for the memory technology device drivers. 3 # 4 - # $Id: Makefile.common,v 1.7 2005/07/11 10:39:27 gleixner Exp $ 5 6 # Core functionality. 7 - mtd-y := mtdcore.o 8 mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o 9 obj-$(CONFIG_MTD) += $(mtd-y) 10
··· 1 # 2 # Makefile for the memory technology device drivers. 3 # 4 5 # Core functionality. 6 + mtd-y := mtdcore.o mtdsuper.o 7 mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o 8 obj-$(CONFIG_MTD) += $(mtd-y) 9
+232
drivers/mtd/mtdsuper.c
···
··· 1 + /* MTD-based superblock management 2 + * 3 + * Copyright © 2001-2007 Red Hat, Inc. All Rights Reserved. 4 + * Written by: David Howells <dhowells@redhat.com> 5 + * David Woodhouse <dwmw2@infradead.org> 6 + * 7 + * This program is free software; you can redistribute it and/or 8 + * modify it under the terms of the GNU General Public License 9 + * as published by the Free Software Foundation; either version 10 + * 2 of the License, or (at your option) any later version. 11 + */ 12 + 13 + #include <linux/mtd/super.h> 14 + #include <linux/namei.h> 15 + #include <linux/ctype.h> 16 + 17 + /* 18 + * compare superblocks to see if they're equivalent 19 + * - they are if the underlying MTD device is the same 20 + */ 21 + static int get_sb_mtd_compare(struct super_block *sb, void *_mtd) 22 + { 23 + struct mtd_info *mtd = _mtd; 24 + 25 + if (sb->s_mtd == mtd) { 26 + DEBUG(2, "MTDSB: Match on device %d (\"%s\")\n", 27 + mtd->index, mtd->name); 28 + return 1; 29 + } 30 + 31 + DEBUG(2, "MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n", 32 + sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name); 33 + return 0; 34 + } 35 + 36 + /* 37 + * mark the superblock by the MTD device it is using 38 + * - set the device number to be the correct MTD block device for pesuperstence 39 + * of NFS exports 40 + */ 41 + static int get_sb_mtd_set(struct super_block *sb, void *_mtd) 42 + { 43 + struct mtd_info *mtd = _mtd; 44 + 45 + sb->s_mtd = mtd; 46 + sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, mtd->index); 47 + return 0; 48 + } 49 + 50 + /* 51 + * get a superblock on an MTD-backed filesystem 52 + */ 53 + static int get_sb_mtd_aux(struct file_system_type *fs_type, int flags, 54 + const char *dev_name, void *data, 55 + struct mtd_info *mtd, 56 + int (*fill_super)(struct super_block *, void *, int), 57 + struct vfsmount *mnt) 58 + { 59 + struct super_block *sb; 60 + int ret; 61 + 62 + sb = sget(fs_type, get_sb_mtd_compare, get_sb_mtd_set, mtd); 63 + if (IS_ERR(sb)) 64 + goto out_error; 65 + 66 + if (sb->s_root) 67 + goto already_mounted; 68 + 69 + /* fresh new superblock */ 70 + DEBUG(1, "MTDSB: New superblock for device %d (\"%s\")\n", 71 + mtd->index, mtd->name); 72 + 73 + ret = fill_super(sb, data, flags & MS_SILENT ? 1 : 0); 74 + if (ret < 0) { 75 + up_write(&sb->s_umount); 76 + deactivate_super(sb); 77 + return ret; 78 + } 79 + 80 + /* go */ 81 + sb->s_flags |= MS_ACTIVE; 82 + return simple_set_mnt(mnt, sb); 83 + 84 + /* new mountpoint for an already mounted superblock */ 85 + already_mounted: 86 + DEBUG(1, "MTDSB: Device %d (\"%s\") is already mounted\n", 87 + mtd->index, mtd->name); 88 + ret = simple_set_mnt(mnt, sb); 89 + goto out_put; 90 + 91 + out_error: 92 + ret = PTR_ERR(sb); 93 + out_put: 94 + put_mtd_device(mtd); 95 + return ret; 96 + } 97 + 98 + /* 99 + * get a superblock on an MTD-backed filesystem by MTD device number 100 + */ 101 + static int get_sb_mtd_nr(struct file_system_type *fs_type, int flags, 102 + const char *dev_name, void *data, int mtdnr, 103 + int (*fill_super)(struct super_block *, void *, int), 104 + struct vfsmount *mnt) 105 + { 106 + struct mtd_info *mtd; 107 + 108 + mtd = get_mtd_device(NULL, mtdnr); 109 + if (!mtd) { 110 + DEBUG(0, "MTDSB: Device #%u doesn't appear to exist\n", mtdnr); 111 + return -EINVAL; 112 + } 113 + 114 + return get_sb_mtd_aux(fs_type, flags, dev_name, data, mtd, fill_super, 115 + mnt); 116 + } 117 + 118 + /* 119 + * set up an MTD-based superblock 120 + */ 121 + int get_sb_mtd(struct file_system_type *fs_type, int flags, 122 + const char *dev_name, void *data, 123 + int (*fill_super)(struct super_block *, void *, int), 124 + struct vfsmount *mnt) 125 + { 126 + struct nameidata nd; 127 + int mtdnr, ret; 128 + 129 + if (!dev_name) 130 + return -EINVAL; 131 + 132 + DEBUG(2, "MTDSB: dev_name \"%s\"\n", dev_name); 133 + 134 + /* the preferred way of mounting in future; especially when 135 + * CONFIG_BLOCK=n - we specify the underlying MTD device by number or 136 + * by name, so that we don't require block device support to be present 137 + * in the kernel. */ 138 + if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { 139 + if (dev_name[3] == ':') { 140 + struct mtd_info *mtd; 141 + 142 + /* mount by MTD device name */ 143 + DEBUG(1, "MTDSB: mtd:%%s, name \"%s\"\n", 144 + dev_name + 4); 145 + 146 + for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { 147 + mtd = get_mtd_device(NULL, mtdnr); 148 + if (mtd) { 149 + if (!strcmp(mtd->name, dev_name + 4)) 150 + return get_sb_mtd_aux( 151 + fs_type, flags, 152 + dev_name, data, mtd, 153 + fill_super, mnt); 154 + 155 + put_mtd_device(mtd); 156 + } 157 + } 158 + 159 + printk(KERN_NOTICE "MTD:" 160 + " MTD device with name \"%s\" not found.\n", 161 + dev_name + 4); 162 + 163 + } else if (isdigit(dev_name[3])) { 164 + /* mount by MTD device number name */ 165 + char *endptr; 166 + 167 + mtdnr = simple_strtoul(dev_name + 3, &endptr, 0); 168 + if (!*endptr) { 169 + /* It was a valid number */ 170 + DEBUG(1, "MTDSB: mtd%%d, mtdnr %d\n", 171 + mtdnr); 172 + return get_sb_mtd_nr(fs_type, flags, 173 + dev_name, data, 174 + mtdnr, fill_super, mnt); 175 + } 176 + } 177 + } 178 + 179 + /* try the old way - the hack where we allowed users to mount 180 + * /dev/mtdblock$(n) but didn't actually _use_ the blockdev 181 + */ 182 + ret = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); 183 + 184 + DEBUG(1, "MTDSB: path_lookup() returned %d, inode %p\n", 185 + ret, nd.dentry ? nd.dentry->d_inode : NULL); 186 + 187 + if (ret) 188 + return ret; 189 + 190 + ret = -EINVAL; 191 + 192 + if (!S_ISBLK(nd.dentry->d_inode->i_mode)) 193 + goto out; 194 + 195 + if (nd.mnt->mnt_flags & MNT_NODEV) { 196 + ret = -EACCES; 197 + goto out; 198 + } 199 + 200 + if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) 201 + goto not_an_MTD_device; 202 + 203 + mtdnr = iminor(nd.dentry->d_inode); 204 + path_release(&nd); 205 + 206 + return get_sb_mtd_nr(fs_type, flags, dev_name, data, mtdnr, fill_super, 207 + mnt); 208 + 209 + not_an_MTD_device: 210 + if (!(flags & MS_SILENT)) 211 + printk(KERN_NOTICE 212 + "MTD: Attempt to mount non-MTD device \"%s\"\n", 213 + dev_name); 214 + out: 215 + path_release(&nd); 216 + return ret; 217 + 218 + } 219 + 220 + EXPORT_SYMBOL_GPL(get_sb_mtd); 221 + 222 + /* 223 + * destroy an MTD-based superblock 224 + */ 225 + void kill_mtd_super(struct super_block *sb) 226 + { 227 + generic_shutdown_super(sb); 228 + put_mtd_device(sb->s_mtd); 229 + sb->s_mtd = NULL; 230 + } 231 + 232 + EXPORT_SYMBOL_GPL(kill_mtd_super);
+19 -175
fs/jffs2/super.c
··· 19 #include <linux/mount.h> 20 #include <linux/jffs2.h> 21 #include <linux/pagemap.h> 22 - #include <linux/mtd/mtd.h> 23 #include <linux/ctype.h> 24 #include <linux/namei.h> 25 #include "compr.h" ··· 78 .sync_fs = jffs2_sync_fs, 79 }; 80 81 - static int jffs2_sb_compare(struct super_block *sb, void *data) 82 { 83 - struct jffs2_sb_info *p = data; 84 - struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); 85 - 86 - /* The superblocks are considered to be equivalent if the underlying MTD 87 - device is the same one */ 88 - if (c->mtd == p->mtd) { 89 - D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name)); 90 - return 1; 91 - } else { 92 - D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n", 93 - c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name)); 94 - return 0; 95 - } 96 - } 97 - 98 - static int jffs2_sb_set(struct super_block *sb, void *data) 99 - { 100 - struct jffs2_sb_info *p = data; 101 - 102 - /* For persistence of NFS exports etc. we use the same s_dev 103 - each time we mount the device, don't just use an anonymous 104 - device */ 105 - sb->s_fs_info = p; 106 - p->os_priv = sb; 107 - sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index); 108 - 109 - return 0; 110 - } 111 - 112 - static int jffs2_get_sb_mtd(struct file_system_type *fs_type, 113 - int flags, const char *dev_name, 114 - void *data, struct mtd_info *mtd, 115 - struct vfsmount *mnt) 116 - { 117 - struct super_block *sb; 118 struct jffs2_sb_info *c; 119 - int ret; 120 121 c = kzalloc(sizeof(*c), GFP_KERNEL); 122 if (!c) 123 return -ENOMEM; 124 - c->mtd = mtd; 125 126 - sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c); 127 128 - if (IS_ERR(sb)) 129 - goto out_error; 130 - 131 - if (sb->s_root) { 132 - /* New mountpoint for JFFS2 which is already mounted */ 133 - D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n", 134 - mtd->index, mtd->name)); 135 - ret = simple_set_mnt(mnt, sb); 136 - goto out_put; 137 - } 138 - 139 - D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n", 140 - mtd->index, mtd->name)); 141 - 142 - /* Initialize JFFS2 superblock locks, the further initialization will be 143 - * done later */ 144 init_MUTEX(&c->alloc_sem); 145 init_MUTEX(&c->erase_free_sem); 146 init_waitqueue_head(&c->erase_wait); ··· 107 spin_lock_init(&c->inocache_lock); 108 109 sb->s_op = &jffs2_super_operations; 110 - sb->s_flags = flags | MS_NOATIME; 111 sb->s_xattr = jffs2_xattr_handlers; 112 #ifdef CONFIG_JFFS2_FS_POSIX_ACL 113 sb->s_flags |= MS_POSIXACL; 114 #endif 115 - ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); 116 - 117 - if (ret) { 118 - /* Failure case... */ 119 - up_write(&sb->s_umount); 120 - deactivate_super(sb); 121 - return ret; 122 - } 123 - 124 - sb->s_flags |= MS_ACTIVE; 125 - return simple_set_mnt(mnt, sb); 126 - 127 - out_error: 128 - ret = PTR_ERR(sb); 129 - out_put: 130 - kfree(c); 131 - put_mtd_device(mtd); 132 - 133 - return ret; 134 - } 135 - 136 - static int jffs2_get_sb_mtdnr(struct file_system_type *fs_type, 137 - int flags, const char *dev_name, 138 - void *data, int mtdnr, 139 - struct vfsmount *mnt) 140 - { 141 - struct mtd_info *mtd; 142 - 143 - mtd = get_mtd_device(NULL, mtdnr); 144 - if (IS_ERR(mtd)) { 145 - D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr)); 146 - return PTR_ERR(mtd); 147 - } 148 - 149 - return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt); 150 } 151 152 static int jffs2_get_sb(struct file_system_type *fs_type, 153 int flags, const char *dev_name, 154 void *data, struct vfsmount *mnt) 155 { 156 - int err; 157 - struct nameidata nd; 158 - int mtdnr; 159 - 160 - if (!dev_name) 161 - return -EINVAL; 162 - 163 - D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name)); 164 - 165 - /* The preferred way of mounting in future; especially when 166 - CONFIG_BLK_DEV is implemented - we specify the underlying 167 - MTD device by number or by name, so that we don't require 168 - block device support to be present in the kernel. */ 169 - 170 - /* FIXME: How to do the root fs this way? */ 171 - 172 - if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { 173 - /* Probably mounting without the blkdev crap */ 174 - if (dev_name[3] == ':') { 175 - struct mtd_info *mtd; 176 - 177 - /* Mount by MTD device name */ 178 - D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4)); 179 - for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { 180 - mtd = get_mtd_device(NULL, mtdnr); 181 - if (!IS_ERR(mtd)) { 182 - if (!strcmp(mtd->name, dev_name+4)) 183 - return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt); 184 - put_mtd_device(mtd); 185 - } 186 - } 187 - printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4); 188 - } else if (isdigit(dev_name[3])) { 189 - /* Mount by MTD device number name */ 190 - char *endptr; 191 - 192 - mtdnr = simple_strtoul(dev_name+3, &endptr, 0); 193 - if (!*endptr) { 194 - /* It was a valid number */ 195 - D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr)); 196 - return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr, mnt); 197 - } 198 - } 199 - } 200 - 201 - /* Try the old way - the hack where we allowed users to mount 202 - /dev/mtdblock$(n) but didn't actually _use_ the blkdev */ 203 - 204 - err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); 205 - 206 - D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n", 207 - err, nd.dentry->d_inode)); 208 - 209 - if (err) 210 - return err; 211 - 212 - err = -EINVAL; 213 - 214 - if (!S_ISBLK(nd.dentry->d_inode->i_mode)) 215 - goto out; 216 - 217 - if (nd.mnt->mnt_flags & MNT_NODEV) { 218 - err = -EACCES; 219 - goto out; 220 - } 221 - 222 - if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) { 223 - if (!(flags & MS_SILENT)) 224 - printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n", 225 - dev_name); 226 - goto out; 227 - } 228 - 229 - mtdnr = iminor(nd.dentry->d_inode); 230 - path_release(&nd); 231 - 232 - return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr, mnt); 233 - 234 - out: 235 - path_release(&nd); 236 - return err; 237 } 238 239 static void jffs2_put_super (struct super_block *sb) ··· 155 struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); 156 if (!(sb->s_flags & MS_RDONLY)) 157 jffs2_stop_garbage_collect_thread(c); 158 - generic_shutdown_super(sb); 159 - put_mtd_device(c->mtd); 160 kfree(c); 161 } 162
··· 19 #include <linux/mount.h> 20 #include <linux/jffs2.h> 21 #include <linux/pagemap.h> 22 + #include <linux/mtd/super.h> 23 #include <linux/ctype.h> 24 #include <linux/namei.h> 25 #include "compr.h" ··· 78 .sync_fs = jffs2_sync_fs, 79 }; 80 81 + /* 82 + * fill in the superblock 83 + */ 84 + static int jffs2_fill_super(struct super_block *sb, void *data, int silent) 85 { 86 struct jffs2_sb_info *c; 87 + 88 + D1(printk(KERN_DEBUG "jffs2_get_sb_mtd():" 89 + " New superblock for device %d (\"%s\")\n", 90 + sb->s_mtd->index, sb->s_mtd->name)); 91 92 c = kzalloc(sizeof(*c), GFP_KERNEL); 93 if (!c) 94 return -ENOMEM; 95 96 + c->mtd = sb->s_mtd; 97 + c->os_priv = sb; 98 + sb->s_fs_info = c; 99 100 + /* Initialize JFFS2 superblock locks, the further initialization will 101 + * be done later */ 102 init_MUTEX(&c->alloc_sem); 103 init_MUTEX(&c->erase_free_sem); 104 init_waitqueue_head(&c->erase_wait); ··· 149 spin_lock_init(&c->inocache_lock); 150 151 sb->s_op = &jffs2_super_operations; 152 + sb->s_flags = sb->s_flags | MS_NOATIME; 153 sb->s_xattr = jffs2_xattr_handlers; 154 #ifdef CONFIG_JFFS2_FS_POSIX_ACL 155 sb->s_flags |= MS_POSIXACL; 156 #endif 157 + return jffs2_do_fill_super(sb, data, silent); 158 } 159 160 static int jffs2_get_sb(struct file_system_type *fs_type, 161 int flags, const char *dev_name, 162 void *data, struct vfsmount *mnt) 163 { 164 + return get_sb_mtd(fs_type, flags, dev_name, data, jffs2_fill_super, 165 + mnt); 166 } 167 168 static void jffs2_put_super (struct super_block *sb) ··· 310 struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); 311 if (!(sb->s_flags & MS_RDONLY)) 312 jffs2_stop_garbage_collect_thread(c); 313 + kill_mtd_super(sb); 314 kfree(c); 315 } 316
+1
include/linux/fs.h
··· 937 struct list_head s_files; 938 939 struct block_device *s_bdev; 940 struct list_head s_instances; 941 struct quota_info s_dquot; /* Diskquota specific options */ 942
··· 937 struct list_head s_files; 938 939 struct block_device *s_bdev; 940 + struct mtd_info *s_mtd; 941 struct list_head s_instances; 942 struct quota_info s_dquot; /* Diskquota specific options */ 943
+30
include/linux/mtd/super.h
···
··· 1 + /* MTD-based superblock handling 2 + * 3 + * Copyright © 2006 Red Hat, Inc. All Rights Reserved. 4 + * Written by David Howells (dhowells@redhat.com) 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License 8 + * as published by the Free Software Foundation; either version 9 + * 2 of the License, or (at your option) any later version. 10 + */ 11 + 12 + #ifndef __MTD_SUPER_H__ 13 + #define __MTD_SUPER_H__ 14 + 15 + #ifdef __KERNEL__ 16 + 17 + #include <linux/mtd/mtd.h> 18 + #include <linux/fs.h> 19 + #include <linux/mount.h> 20 + 21 + extern int get_sb_mtd(struct file_system_type *fs_type, int flags, 22 + const char *dev_name, void *data, 23 + int (*fill_super)(struct super_block *, void *, int), 24 + struct vfsmount *mnt); 25 + extern void kill_mtd_super(struct super_block *sb); 26 + 27 + 28 + #endif /* __KERNEL__ */ 29 + 30 + #endif /* __MTD_SUPER_H__ */