Skip to content

Commit 99c18ce

Browse files
Nicolas PitreAl Viro
authored andcommitted
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 <[email protected]> Tested-by: Chris Brandt <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent 8a5776a commit 99c18ce

File tree

2 files changed

+199
-43
lines changed

2 files changed

+199
-43
lines changed

fs/cramfs/Kconfig

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
config CRAMFS
22
tristate "Compressed ROM file system support (cramfs) (OBSOLETE)"
3-
depends on BLOCK
43
select ZLIB_INFLATE
54
help
65
Saying Y here includes support for CramFs (Compressed ROM File
@@ -20,3 +19,32 @@ config CRAMFS
2019
in terms of performance and features.
2120

2221
If unsure, say N.
22+
23+
config CRAMFS_BLOCKDEV
24+
bool "Support CramFs image over a regular block device" if EXPERT
25+
depends on CRAMFS && BLOCK
26+
default y
27+
help
28+
This option allows the CramFs driver to load data from a regular
29+
block device such a disk partition or a ramdisk.
30+
31+
config CRAMFS_MTD
32+
bool "Support CramFs image directly mapped in physical memory"
33+
depends on CRAMFS && MTD
34+
default y if !CRAMFS_BLOCKDEV
35+
help
36+
This option allows the CramFs driver to load data directly from
37+
a linear adressed memory range (usually non volatile memory
38+
like flash) instead of going through the block device layer.
39+
This saves some memory since no intermediate buffering is
40+
necessary.
41+
42+
The location of the CramFs image is determined by a
43+
MTD device capable of direct memory mapping e.g. from
44+
the 'physmap' map driver or a resulting MTD partition.
45+
For example, this would mount the cramfs image stored in
46+
the MTD partition named "xip_fs" on the /mnt mountpoint:
47+
48+
mount -t cramfs mtd:xip_fs /mnt
49+
50+
If unsure, say N.

fs/cramfs/inode.c

Lines changed: 170 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include <linux/init.h>
2020
#include <linux/string.h>
2121
#include <linux/blkdev.h>
22+
#include <linux/mtd/mtd.h>
23+
#include <linux/mtd/super.h>
2224
#include <linux/slab.h>
2325
#include <linux/vfs.h>
2426
#include <linux/mutex.h>
@@ -36,6 +38,9 @@ struct cramfs_sb_info {
3638
unsigned long blocks;
3739
unsigned long files;
3840
unsigned long flags;
41+
void *linear_virt_addr;
42+
resource_size_t linear_phys_addr;
43+
size_t mtd_point_size;
3944
};
4045

4146
static 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,
140145
* BLKS_PER_BUF*PAGE_SIZE, so that the caller doesn't need to
141146
* worry about end-of-buffer issues even when decompressing a full
142147
* page cache.
148+
*
149+
* Note: This is all optimized away at compile time when
150+
* CONFIG_CRAMFS_BLOCKDEV=n.
143151
*/
144152
#define READ_BUFFERS (2)
145153
/* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */
@@ -160,10 +168,10 @@ static struct super_block *buffer_dev[READ_BUFFERS];
160168
static int next_buffer;
161169

162170
/*
163-
* Returns a pointer to a buffer containing at least LEN bytes of
164-
* filesystem starting at byte offset OFFSET into the filesystem.
171+
* Populate our block cache and return a pointer to it.
165172
*/
166-
static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len)
173+
static void *cramfs_blkdev_read(struct super_block *sb, unsigned int offset,
174+
unsigned int len)
167175
{
168176
struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping;
169177
struct page *pages[BLKS_PER_BUF];
@@ -239,11 +247,49 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i
239247
return read_buffers[buffer] + offset;
240248
}
241249

250+
/*
251+
* Return a pointer to the linearly addressed cramfs image in memory.
252+
*/
253+
static 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+
*/
269+
static 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+
242282
static void cramfs_kill_sb(struct super_block *sb)
243283
{
244284
struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
245285

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+
}
247293
kfree(sbi);
248294
}
249295

@@ -254,45 +300,37 @@ static int cramfs_remount(struct super_block *sb, int *flags, char *data)
254300
return 0;
255301
}
256302

257-
static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
303+
static int cramfs_read_super(struct super_block *sb,
304+
struct cramfs_super *super, int silent)
258305
{
259-
int i;
260-
struct cramfs_super super;
306+
struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
261307
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;
271308

272-
/* Invalidate the read buffers on mount: think disk change.. */
273-
mutex_lock(&read_mutex);
274-
for (i = 0; i < READ_BUFFERS; i++)
275-
buffer_blocknr[i] = -1;
309+
/* We don't know the real size yet */
310+
sbi->size = PAGE_SIZE;
276311

277312
/* 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));
279315
mutex_unlock(&read_mutex);
280316

281317
/* Do sanity checks on the superblock */
282-
if (super.magic != CRAMFS_MAGIC) {
318+
if (super->magic != CRAMFS_MAGIC) {
283319
/* check for wrong endianness */
284-
if (super.magic == CRAMFS_MAGIC_WEND) {
320+
if (super->magic == CRAMFS_MAGIC_WEND) {
285321
if (!silent)
286322
pr_err("wrong endianness\n");
287323
return -EINVAL;
288324
}
289325

290326
/* check at 512 byte offset */
291327
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));
293331
mutex_unlock(&read_mutex);
294-
if (super.magic != CRAMFS_MAGIC) {
295-
if (super.magic == CRAMFS_MAGIC_WEND && !silent)
332+
if (super->magic != CRAMFS_MAGIC) {
333+
if (super->magic == CRAMFS_MAGIC_WEND && !silent)
296334
pr_err("wrong endianness\n");
297335
else if (!silent)
298336
pr_err("wrong magic\n");
@@ -301,44 +339,53 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
301339
}
302340

303341
/* get feature flags first */
304-
if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) {
342+
if (super->flags & ~CRAMFS_SUPPORTED_FLAGS) {
305343
pr_err("unsupported filesystem features\n");
306344
return -EINVAL;
307345
}
308346

309347
/* Check that the root inode is in a sane state */
310-
if (!S_ISDIR(super.root.mode)) {
348+
if (!S_ISDIR(super->root.mode)) {
311349
pr_err("root is not a directory\n");
312350
return -EINVAL;
313351
}
314352
/* 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;
316354

317-
root_offset = super.root.offset << 2;
318-
if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
319-
sbi->size = super.size;
320-
sbi->blocks = super.fsid.blocks;
321-
sbi->files = super.fsid.files;
355+
root_offset = super->root.offset << 2;
356+
if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) {
357+
sbi->size = super->size;
358+
sbi->blocks = super->fsid.blocks;
359+
sbi->files = super->fsid.files;
322360
} else {
323361
sbi->size = 1<<28;
324362
sbi->blocks = 0;
325363
sbi->files = 0;
326364
}
327-
sbi->magic = super.magic;
328-
sbi->flags = super.flags;
365+
sbi->magic = super->magic;
366+
sbi->flags = super->flags;
329367
if (root_offset == 0)
330368
pr_info("empty filesystem");
331-
else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
369+
else if (!(super->flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
332370
((root_offset != sizeof(struct cramfs_super)) &&
333371
(root_offset != 512 + sizeof(struct cramfs_super))))
334372
{
335373
pr_err("bad root offset %lu\n", root_offset);
336374
return -EINVAL;
337375
}
338376

377+
return 0;
378+
}
379+
380+
static int cramfs_finalize_super(struct super_block *sb,
381+
struct cramfs_inode *cramfs_root)
382+
{
383+
struct inode *root;
384+
339385
/* Set it all up.. */
386+
sb->s_flags |= MS_RDONLY;
340387
sb->s_op = &cramfs_ops;
341-
root = get_cramfs_inode(sb, &super.root, 0);
388+
root = get_cramfs_inode(sb, cramfs_root, 0);
342389
if (IS_ERR(root))
343390
return PTR_ERR(root);
344391
sb->s_root = d_make_root(root);
@@ -347,10 +394,79 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
347394
return 0;
348395
}
349396

397+
static 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+
419+
static 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+
350461
static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf)
351462
{
352463
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);
354470

355471
buf->f_type = CRAMFS_MAGIC;
356472
buf->f_bsize = PAGE_SIZE;
@@ -573,10 +689,22 @@ static const struct super_operations cramfs_ops = {
573689
.statfs = cramfs_statfs,
574690
};
575691

576-
static struct dentry *cramfs_mount(struct file_system_type *fs_type,
577-
int flags, const char *dev_name, void *data)
692+
static struct dentry *cramfs_mount(struct file_system_type *fs_type, int flags,
693+
const char *dev_name, void *data)
578694
{
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;
580708
}
581709

582710
static struct file_system_type cramfs_fs_type = {

0 commit comments

Comments
 (0)