aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cramfs/inode.c
diff options
context:
space:
mode:
authorNicolas Pitre <nicolas.pitre@linaro.org>2017-10-13 16:09:23 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2017-10-15 00:47:21 -0400
commit99c18ce580c6cc6763e694b4ce320d7b226ab59b (patch)
tree4e717d01235265c06eb27b64d6b3618590dbc009 /fs/cramfs/inode.c
parent8a5776a5f49812d29fe4b2d0a2d71675c3facf3f (diff)
cramfs: direct memory access support
Small embedded systems typically execute the kernel code in place (XIP) directly from flash to save on precious RAM usage. This patch adds to cramfs the ability to consume filesystem data directly from flash as well. Cramfs is particularly well suited to this feature as it is very simple with low RAM usage, and with this feature it is possible to use it with no block device support and consequently even lower RAM usage. This patch was inspired by a similar patch from Shane Nay dated 17 years ago that used to be very popular in embedded circles but never made it into mainline. This is a cleaned-up implementation that uses far fewer ifdef's and gets the actual memory location for the filesystem image via MTD at run time. In the context of small IoT deployments, this functionality has become relevant and useful again. Signed-off-by: Nicolas Pitre <nico@linaro.org> Tested-by: Chris Brandt <chris.brandt@renesas.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/cramfs/inode.c')
-rw-r--r--fs/cramfs/inode.c212
1 files changed, 170 insertions, 42 deletions
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 7919967488cb..bcdccb7a820b 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -19,6 +19,8 @@
19#include <linux/init.h> 19#include <linux/init.h>
20#include <linux/string.h> 20#include <linux/string.h>
21#include <linux/blkdev.h> 21#include <linux/blkdev.h>
22#include <linux/mtd/mtd.h>
23#include <linux/mtd/super.h>
22#include <linux/slab.h> 24#include <linux/slab.h>
23#include <linux/vfs.h> 25#include <linux/vfs.h>
24#include <linux/mutex.h> 26#include <linux/mutex.h>
@@ -36,6 +38,9 @@ struct cramfs_sb_info {
36 unsigned long blocks; 38 unsigned long blocks;
37 unsigned long files; 39 unsigned long files;
38 unsigned long flags; 40 unsigned long flags;
41 void *linear_virt_addr;
42 resource_size_t linear_phys_addr;
43 size_t mtd_point_size;
39}; 44};
40 45
41static inline struct cramfs_sb_info *CRAMFS_SB(struct super_block *sb) 46static inline struct cramfs_sb_info *CRAMFS_SB(struct super_block *sb)
@@ -140,6 +145,9 @@ static struct inode *get_cramfs_inode(struct super_block *sb,
140 * BLKS_PER_BUF*PAGE_SIZE, so that the caller doesn't need to 145 * BLKS_PER_BUF*PAGE_SIZE, so that the caller doesn't need to
141 * worry about end-of-buffer issues even when decompressing a full 146 * worry about end-of-buffer issues even when decompressing a full
142 * page cache. 147 * page cache.
148 *
149 * Note: This is all optimized away at compile time when
150 * CONFIG_CRAMFS_BLOCKDEV=n.
143 */ 151 */
144#define READ_BUFFERS (2) 152#define READ_BUFFERS (2)
145/* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */ 153/* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */
@@ -160,10 +168,10 @@ static struct super_block *buffer_dev[READ_BUFFERS];
160static int next_buffer; 168static int next_buffer;
161 169
162/* 170/*
163 * Returns a pointer to a buffer containing at least LEN bytes of 171 * Populate our block cache and return a pointer to it.
164 * filesystem starting at byte offset OFFSET into the filesystem.
165 */ 172 */
166static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len) 173static void *cramfs_blkdev_read(struct super_block *sb, unsigned int offset,
174 unsigned int len)
167{ 175{
168 struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; 176 struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping;
169 struct page *pages[BLKS_PER_BUF]; 177 struct page *pages[BLKS_PER_BUF];
@@ -239,11 +247,49 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i
239 return read_buffers[buffer] + offset; 247 return read_buffers[buffer] + offset;
240} 248}
241 249
250/*
251 * Return a pointer to the linearly addressed cramfs image in memory.
252 */
253static void *cramfs_direct_read(struct super_block *sb, unsigned int offset,
254 unsigned int len)
255{
256 struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
257
258 if (!len)
259 return NULL;
260 if (len > sbi->size || offset > sbi->size - len)
261 return page_address(ZERO_PAGE(0));
262 return sbi->linear_virt_addr + offset;
263}
264
265/*
266 * Returns a pointer to a buffer containing at least LEN bytes of
267 * filesystem starting at byte offset OFFSET into the filesystem.
268 */
269static void *cramfs_read(struct super_block *sb, unsigned int offset,
270 unsigned int len)
271{
272 struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
273
274 if (IS_ENABLED(CONFIG_CRAMFS_MTD) && sbi->linear_virt_addr)
275 return cramfs_direct_read(sb, offset, len);
276 else if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV))
277 return cramfs_blkdev_read(sb, offset, len);
278 else
279 return NULL;
280}
281
242static void cramfs_kill_sb(struct super_block *sb) 282static void cramfs_kill_sb(struct super_block *sb)
243{ 283{
244 struct cramfs_sb_info *sbi = CRAMFS_SB(sb); 284 struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
245 285
246 kill_block_super(sb); 286 if (IS_ENABLED(CCONFIG_CRAMFS_MTD) && sb->s_mtd) {
287 if (sbi && sbi->mtd_point_size)
288 mtd_unpoint(sb->s_mtd, 0, sbi->mtd_point_size);
289 kill_mtd_super(sb);
290 } else if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV) && sb->s_bdev) {
291 kill_block_super(sb);
292 }
247 kfree(sbi); 293 kfree(sbi);
248} 294}
249 295
@@ -254,34 +300,24 @@ static int cramfs_remount(struct super_block *sb, int *flags, char *data)
254 return 0; 300 return 0;
255} 301}
256 302
257static int cramfs_fill_super(struct super_block *sb, void *data, int silent) 303static int cramfs_read_super(struct super_block *sb,
304 struct cramfs_super *super, int silent)
258{ 305{
259 int i; 306 struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
260 struct cramfs_super super;
261 unsigned long root_offset; 307 unsigned long root_offset;
262 struct cramfs_sb_info *sbi;
263 struct inode *root;
264
265 sb->s_flags |= MS_RDONLY;
266
267 sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL);
268 if (!sbi)
269 return -ENOMEM;
270 sb->s_fs_info = sbi;
271 308
272 /* Invalidate the read buffers on mount: think disk change.. */ 309 /* We don't know the real size yet */
273 mutex_lock(&read_mutex); 310 sbi->size = PAGE_SIZE;
274 for (i = 0; i < READ_BUFFERS; i++)
275 buffer_blocknr[i] = -1;
276 311
277 /* Read the first block and get the superblock from it */ 312 /* Read the first block and get the superblock from it */
278 memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super)); 313 mutex_lock(&read_mutex);
314 memcpy(super, cramfs_read(sb, 0, sizeof(*super)), sizeof(*super));
279 mutex_unlock(&read_mutex); 315 mutex_unlock(&read_mutex);
280 316
281 /* Do sanity checks on the superblock */ 317 /* Do sanity checks on the superblock */
282 if (super.magic != CRAMFS_MAGIC) { 318 if (super->magic != CRAMFS_MAGIC) {
283 /* check for wrong endianness */ 319 /* check for wrong endianness */
284 if (super.magic == CRAMFS_MAGIC_WEND) { 320 if (super->magic == CRAMFS_MAGIC_WEND) {
285 if (!silent) 321 if (!silent)
286 pr_err("wrong endianness\n"); 322 pr_err("wrong endianness\n");
287 return -EINVAL; 323 return -EINVAL;
@@ -289,10 +325,12 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
289 325
290 /* check at 512 byte offset */ 326 /* check at 512 byte offset */
291 mutex_lock(&read_mutex); 327 mutex_lock(&read_mutex);
292 memcpy(&super, cramfs_read(sb, 512, sizeof(super)), sizeof(super)); 328 memcpy(super,
329 cramfs_read(sb, 512, sizeof(*super)),
330 sizeof(*super));
293 mutex_unlock(&read_mutex); 331 mutex_unlock(&read_mutex);
294 if (super.magic != CRAMFS_MAGIC) { 332 if (super->magic != CRAMFS_MAGIC) {
295 if (super.magic == CRAMFS_MAGIC_WEND && !silent) 333 if (super->magic == CRAMFS_MAGIC_WEND && !silent)
296 pr_err("wrong endianness\n"); 334 pr_err("wrong endianness\n");
297 else if (!silent) 335 else if (!silent)
298 pr_err("wrong magic\n"); 336 pr_err("wrong magic\n");
@@ -301,34 +339,34 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
301 } 339 }
302 340
303 /* get feature flags first */ 341 /* get feature flags first */
304 if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) { 342 if (super->flags & ~CRAMFS_SUPPORTED_FLAGS) {
305 pr_err("unsupported filesystem features\n"); 343 pr_err("unsupported filesystem features\n");
306 return -EINVAL; 344 return -EINVAL;
307 } 345 }
308 346
309 /* Check that the root inode is in a sane state */ 347 /* Check that the root inode is in a sane state */
310 if (!S_ISDIR(super.root.mode)) { 348 if (!S_ISDIR(super->root.mode)) {
311 pr_err("root is not a directory\n"); 349 pr_err("root is not a directory\n");
312 return -EINVAL; 350 return -EINVAL;
313 } 351 }
314 /* correct strange, hard-coded permissions of mkcramfs */ 352 /* correct strange, hard-coded permissions of mkcramfs */
315 super.root.mode |= (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 353 super->root.mode |= 0555;
316 354
317 root_offset = super.root.offset << 2; 355 root_offset = super->root.offset << 2;
318 if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) { 356 if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) {
319 sbi->size = super.size; 357 sbi->size = super->size;
320 sbi->blocks = super.fsid.blocks; 358 sbi->blocks = super->fsid.blocks;
321 sbi->files = super.fsid.files; 359 sbi->files = super->fsid.files;
322 } else { 360 } else {
323 sbi->size = 1<<28; 361 sbi->size = 1<<28;
324 sbi->blocks = 0; 362 sbi->blocks = 0;
325 sbi->files = 0; 363 sbi->files = 0;
326 } 364 }
327 sbi->magic = super.magic; 365 sbi->magic = super->magic;
328 sbi->flags = super.flags; 366 sbi->flags = super->flags;
329 if (root_offset == 0) 367 if (root_offset == 0)
330 pr_info("empty filesystem"); 368 pr_info("empty filesystem");
331 else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && 369 else if (!(super->flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
332 ((root_offset != sizeof(struct cramfs_super)) && 370 ((root_offset != sizeof(struct cramfs_super)) &&
333 (root_offset != 512 + sizeof(struct cramfs_super)))) 371 (root_offset != 512 + sizeof(struct cramfs_super))))
334 { 372 {
@@ -336,9 +374,18 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
336 return -EINVAL; 374 return -EINVAL;
337 } 375 }
338 376
377 return 0;
378}
379
380static int cramfs_finalize_super(struct super_block *sb,
381 struct cramfs_inode *cramfs_root)
382{
383 struct inode *root;
384
339 /* Set it all up.. */ 385 /* Set it all up.. */
386 sb->s_flags |= MS_RDONLY;
340 sb->s_op = &cramfs_ops; 387 sb->s_op = &cramfs_ops;
341 root = get_cramfs_inode(sb, &super.root, 0); 388 root = get_cramfs_inode(sb, cramfs_root, 0);
342 if (IS_ERR(root)) 389 if (IS_ERR(root))
343 return PTR_ERR(root); 390 return PTR_ERR(root);
344 sb->s_root = d_make_root(root); 391 sb->s_root = d_make_root(root);
@@ -347,10 +394,79 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
347 return 0; 394 return 0;
348} 395}
349 396
397static int cramfs_blkdev_fill_super(struct super_block *sb, void *data,
398 int silent)
399{
400 struct cramfs_sb_info *sbi;
401 struct cramfs_super super;
402 int i, err;
403
404 sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL);
405 if (!sbi)
406 return -ENOMEM;
407 sb->s_fs_info = sbi;
408
409 /* Invalidate the read buffers on mount: think disk change.. */
410 for (i = 0; i < READ_BUFFERS; i++)
411 buffer_blocknr[i] = -1;
412
413 err = cramfs_read_super(sb, &super, silent);
414 if (err)
415 return err;
416 return cramfs_finalize_super(sb, &super.root);
417}
418
419static int cramfs_mtd_fill_super(struct super_block *sb, void *data,
420 int silent)
421{
422 struct cramfs_sb_info *sbi;
423 struct cramfs_super super;
424 int err;
425
426 sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL);
427 if (!sbi)
428 return -ENOMEM;
429 sb->s_fs_info = sbi;
430
431 /* Map only one page for now. Will remap it when fs size is known. */
432 err = mtd_point(sb->s_mtd, 0, PAGE_SIZE, &sbi->mtd_point_size,
433 &sbi->linear_virt_addr, &sbi->linear_phys_addr);
434 if (err || sbi->mtd_point_size != PAGE_SIZE) {
435 pr_err("unable to get direct memory access to mtd:%s\n",
436 sb->s_mtd->name);
437 return err ? : -ENODATA;
438 }
439
440 pr_info("checking physical address %pap for linear cramfs image\n",
441 &sbi->linear_phys_addr);
442 err = cramfs_read_super(sb, &super, silent);
443 if (err)
444 return err;
445
446 /* Remap the whole filesystem now */
447 pr_info("linear cramfs image on mtd:%s appears to be %lu KB in size\n",
448 sb->s_mtd->name, sbi->size/1024);
449 mtd_unpoint(sb->s_mtd, 0, PAGE_SIZE);
450 err = mtd_point(sb->s_mtd, 0, sbi->size, &sbi->mtd_point_size,
451 &sbi->linear_virt_addr, &sbi->linear_phys_addr);
452 if (err || sbi->mtd_point_size != sbi->size) {
453 pr_err("unable to get direct memory access to mtd:%s\n",
454 sb->s_mtd->name);
455 return err ? : -ENODATA;
456 }
457
458 return cramfs_finalize_super(sb, &super.root);
459}
460
350static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf) 461static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf)
351{ 462{
352 struct super_block *sb = dentry->d_sb; 463 struct super_block *sb = dentry->d_sb;
353 u64 id = huge_encode_dev(sb->s_bdev->bd_dev); 464 u64 id = 0;
465
466 if (sb->s_bdev)
467 id = huge_encode_dev(sb->s_bdev->bd_dev);
468 else if (sb->s_dev)
469 id = huge_encode_dev(sb->s_dev);
354 470
355 buf->f_type = CRAMFS_MAGIC; 471 buf->f_type = CRAMFS_MAGIC;
356 buf->f_bsize = PAGE_SIZE; 472 buf->f_bsize = PAGE_SIZE;
@@ -573,10 +689,22 @@ static const struct super_operations cramfs_ops = {
573 .statfs = cramfs_statfs, 689 .statfs = cramfs_statfs,
574}; 690};
575 691
576static struct dentry *cramfs_mount(struct file_system_type *fs_type, 692static struct dentry *cramfs_mount(struct file_system_type *fs_type, int flags,
577 int flags, const char *dev_name, void *data) 693 const char *dev_name, void *data)
578{ 694{
579 return mount_bdev(fs_type, flags, dev_name, data, cramfs_fill_super); 695 struct dentry *ret = ERR_PTR(-ENOPROTOOPT);
696
697 if (IS_ENABLED(CONFIG_CRAMFS_MTD)) {
698 ret = mount_mtd(fs_type, flags, dev_name, data,
699 cramfs_mtd_fill_super);
700 if (!IS_ERR(ret))
701 return ret;
702 }
703 if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) {
704 ret = mount_bdev(fs_type, flags, dev_name, data,
705 cramfs_blkdev_fill_super);
706 }
707 return ret;
580} 708}
581 709
582static struct file_system_type cramfs_fs_type = { 710static struct file_system_type cramfs_fs_type = {