From patchwork Thu Oct 12 06:16:10 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Pitre X-Patchwork-Id: 115582 Delivered-To: patch@linaro.org Received: by 10.140.22.163 with SMTP id 32csp1575791qgn; Wed, 11 Oct 2017 23:17:52 -0700 (PDT) X-Google-Smtp-Source: AOwi7QCthdB8owsxSKcFE287GqfwsPyVSkMoopVrOaoKqxobiOwl15psoq+hKzcdx/Np6epTrn/m X-Received: by 10.84.213.130 with SMTP id g2mr1280496pli.388.1507789072817; Wed, 11 Oct 2017 23:17:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1507789072; cv=none; d=google.com; s=arc-20160816; b=DMhh48X4gAE3DFiXTcmOLdbeV5H+cVrRhrgGKMQ5nGjHnhk2+9YVQEAtp/OLGdyqXd yJpbHdf450UDymo8gr1dkVBGPU8JPYKjQG5/swTsgo0hjSMzEVQa3apMZhvwgMNhQSEb CUVwd7cXtnOUFDyhwzK6QZqf+NwpTeKob2hEJtbqvo0iSbCFOcriu12vRZDdhKk7cqvb dCR93M9TTsioLroGv43PMg4mhnIp6H9GKL6FyfEkcAvw/IB9uQywOU+4hyEJvzq99iuB NInkEQ7RS9HIyeYtvT4PbVjrvtkiXM5wVbetvD7Vkbq+rTq6k1u0+oy6iKd71Bu1b27J tIsQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=JK/aSEba77PfRwDWYwMJAXUAFlg8R32mz2a0absQ4vQ=; b=Sg2vUjlndOjjIdewtE29868IyuZvMW3MPxzhk6BmrUUufzAUZprMKfZpAMdSX198wx 3rcMBcBGX8s2gDVyFQNxKlxBjAVnCRz/Q5PfFAs+/LyFHYHFgNbwradnnJ0HM/nmgn0K IYaNHyf0O8SZ1POK/ltpImcB5MJM+bQKQZyfp3qMUbP45sNeixwn69be3dAz+Y7kjX1N vyYof/b9L1fgal3RToMhrbkvunoNYYOz+1VJpi340ztLWPQ3g8RV95RaOb/9Spws2508 FIVGoo6NFydGM01hoyPfhzrq4RYaCNiMDSX1BBcYo4gt8Y/OjOJYogkVGiTcNjaGCOeq EGzw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@pobox.com header.s=sasl header.b=cITG+xWy; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id u2si11069414pgc.323.2017.10.11.23.17.52; Wed, 11 Oct 2017 23:17:52 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@pobox.com header.s=sasl header.b=cITG+xWy; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752969AbdJLGRv (ORCPT + 27 others); Thu, 12 Oct 2017 02:17:51 -0400 Received: from pb-smtp2.pobox.com ([64.147.108.71]:60093 "EHLO sasl.smtp.pobox.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751691AbdJLGQo (ORCPT ); Thu, 12 Oct 2017 02:16:44 -0400 Received: from sasl.smtp.pobox.com (unknown [127.0.0.1]) by pb-smtp2.pobox.com (Postfix) with ESMTP id 26E439C415; Thu, 12 Oct 2017 02:16:44 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=pobox.com; h=from:to:cc :subject:date:message-id:in-reply-to:references; s=sasl; bh=2FkA caip+G5QkX4j5rN1KlyXIIw=; b=cITG+xWy/dM682WkgPaknHMBS7faHS38wH2u ieumTsU9DTWriUtBaGKK6J/Zmb6FFMovjHo1AQoUXNezr+wIXnDI54qHNSAySUV9 AD6UqsClaCYtWybkRbybEcFj0ENwWKWJO5YS6ZwQUB/1n0fIYoK7WSPujJL0Vf9W FdHFiYg= Received: from pb-smtp2.nyi.icgroup.com (unknown [127.0.0.1]) by pb-smtp2.pobox.com (Postfix) with ESMTP id 1B83A9C411; Thu, 12 Oct 2017 02:16:44 -0400 (EDT) Received: from yoda.home (unknown [137.175.234.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by pb-smtp2.pobox.com (Postfix) with ESMTPSA id 6C7B09C407; Thu, 12 Oct 2017 02:16:43 -0400 (EDT) Received: from xanadu.home (xanadu.home [192.168.2.2]) by yoda.home (Postfix) with ESMTP id 8CCCD2DA0660; Thu, 12 Oct 2017 02:16:42 -0400 (EDT) From: Nicolas Pitre To: Alexander Viro , Christoph Hellwig Cc: linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, linux-embedded@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Brandt Subject: [PATCH v6 1/4] cramfs: direct memory access support Date: Thu, 12 Oct 2017 02:16:10 -0400 Message-Id: <20171012061613.28705-2-nicolas.pitre@linaro.org> X-Mailer: git-send-email 2.9.5 In-Reply-To: <20171012061613.28705-1-nicolas.pitre@linaro.org> References: <20171012061613.28705-1-nicolas.pitre@linaro.org> X-Pobox-Relay-ID: EAFAA3E2-AF14-11E7-81C9-575F0C78B957-78420484!pb-smtp2.pobox.com Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Small embedded systems typically execute the kernel code in place (XIP) directly from flash to save on precious RAM usage. This adds the ability to consume filesystem data directly from flash to the cramfs filesystem as well. Cramfs is particularly well suited to this feature as it is very simple and its RAM usage is already very low, and with this feature it is possible to use it with no block device support and 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 --- fs/cramfs/Kconfig | 30 +++++++- fs/cramfs/inode.c | 215 +++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 202 insertions(+), 43 deletions(-) -- 2.9.5 Tested-by: Chris Brandt Reviewed-by: Christoph Hellwig Signed-off-by: Nicolas Pitre Tested-by: Chris Brandt Reviewed-by: Christoph Hellwig Signed-off-by: Al Viro diff --git a/fs/cramfs/Kconfig b/fs/cramfs/Kconfig index 11b29d491b..ef86b06bc0 100644 --- a/fs/cramfs/Kconfig +++ b/fs/cramfs/Kconfig @@ -1,6 +1,5 @@ config CRAMFS tristate "Compressed ROM file system support (cramfs) (OBSOLETE)" - depends on BLOCK select ZLIB_INFLATE help Saying Y here includes support for CramFs (Compressed ROM File @@ -20,3 +19,32 @@ config CRAMFS in terms of performance and features. If unsure, say N. + +config CRAMFS_BLOCKDEV + bool "Support CramFs image over a regular block device" if EXPERT + depends on CRAMFS && BLOCK + default y + help + This option allows the CramFs driver to load data from a regular + block device such a disk partition or a ramdisk. + +config CRAMFS_MTD + bool "Support CramFs image directly mapped in physical memory" + depends on CRAMFS && MTD + default y if !CRAMFS_BLOCKDEV + help + This option allows the CramFs driver to load data directly from + a linear adressed memory range (usually non volatile memory + like flash) instead of going through the block device layer. + This saves some memory since no intermediate buffering is + necessary. + + The location of the CramFs image is determined by a + MTD device capable of direct memory mapping e.g. from + the 'physmap' map driver or a resulting MTD partition. + For example, this would mount the cramfs image stored in + the MTD partition named "xip_fs" on the /mnt mountpoint: + + mount -t cramfs mtd:xip_fs /mnt + + If unsure, say N. diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 7919967488..321a1fe17e 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -36,6 +38,9 @@ struct cramfs_sb_info { unsigned long blocks; unsigned long files; unsigned long flags; + void *linear_virt_addr; + resource_size_t linear_phys_addr; + size_t mtd_point_size; }; 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, * BLKS_PER_BUF*PAGE_SIZE, so that the caller doesn't need to * worry about end-of-buffer issues even when decompressing a full * page cache. + * + * Note: This is all optimized away at compile time when + * CONFIG_CRAMFS_BLOCKDEV=n. */ #define READ_BUFFERS (2) /* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */ @@ -160,10 +168,10 @@ static struct super_block *buffer_dev[READ_BUFFERS]; static int next_buffer; /* - * Returns a pointer to a buffer containing at least LEN bytes of - * filesystem starting at byte offset OFFSET into the filesystem. + * Populate our block cache and return a pointer from it. */ -static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len) +static void *cramfs_blkdev_read(struct super_block *sb, unsigned int offset, + unsigned int len) { struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; struct page *pages[BLKS_PER_BUF]; @@ -239,11 +247,52 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i return read_buffers[buffer] + offset; } +/* + * Return a pointer to the linearly addressed cramfs image in memory. + */ +static void *cramfs_direct_read(struct super_block *sb, unsigned int offset, + unsigned int len) +{ + struct cramfs_sb_info *sbi = CRAMFS_SB(sb); + + if (!len) + return NULL; + if (len > sbi->size || offset > sbi->size - len) + return page_address(ZERO_PAGE(0)); + return sbi->linear_virt_addr + offset; +} + +/* + * Returns a pointer to a buffer containing at least LEN bytes of + * filesystem starting at byte offset OFFSET into the filesystem. + */ +static void *cramfs_read(struct super_block *sb, unsigned int offset, + unsigned int len) +{ + struct cramfs_sb_info *sbi = CRAMFS_SB(sb); + + if (IS_ENABLED(CONFIG_CRAMFS_MTD) && sbi->linear_virt_addr) + return cramfs_direct_read(sb, offset, len); + else if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) + return cramfs_blkdev_read(sb, offset, len); + else + return NULL; +} + static void cramfs_kill_sb(struct super_block *sb) { struct cramfs_sb_info *sbi = CRAMFS_SB(sb); - kill_block_super(sb); + if (IS_ENABLED(CCONFIG_CRAMFS_MTD)) { + if (sbi->mtd_point_size) + mtd_unpoint(sb->s_mtd, 0, sbi->mtd_point_size); + if (sb->s_mtd) + kill_mtd_super(sb); + } + if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) { + if (sb->s_bdev) + kill_block_super(sb); + } kfree(sbi); } @@ -254,34 +303,24 @@ static int cramfs_remount(struct super_block *sb, int *flags, char *data) return 0; } -static int cramfs_fill_super(struct super_block *sb, void *data, int silent) +static int cramfs_read_super(struct super_block *sb, + struct cramfs_super *super, int silent) { - int i; - struct cramfs_super super; + struct cramfs_sb_info *sbi = CRAMFS_SB(sb); unsigned long root_offset; - struct cramfs_sb_info *sbi; - struct inode *root; - - sb->s_flags |= MS_RDONLY; - - sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); - if (!sbi) - return -ENOMEM; - sb->s_fs_info = sbi; - /* Invalidate the read buffers on mount: think disk change.. */ - mutex_lock(&read_mutex); - for (i = 0; i < READ_BUFFERS; i++) - buffer_blocknr[i] = -1; + /* We don't know the real size yet */ + sbi->size = PAGE_SIZE; /* Read the first block and get the superblock from it */ - memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super)); + mutex_lock(&read_mutex); + memcpy(super, cramfs_read(sb, 0, sizeof(*super)), sizeof(*super)); mutex_unlock(&read_mutex); /* Do sanity checks on the superblock */ - if (super.magic != CRAMFS_MAGIC) { + if (super->magic != CRAMFS_MAGIC) { /* check for wrong endianness */ - if (super.magic == CRAMFS_MAGIC_WEND) { + if (super->magic == CRAMFS_MAGIC_WEND) { if (!silent) pr_err("wrong endianness\n"); return -EINVAL; @@ -289,10 +328,12 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent) /* check at 512 byte offset */ mutex_lock(&read_mutex); - memcpy(&super, cramfs_read(sb, 512, sizeof(super)), sizeof(super)); + memcpy(super, + cramfs_read(sb, 512, sizeof(*super)), + sizeof(*super)); mutex_unlock(&read_mutex); - if (super.magic != CRAMFS_MAGIC) { - if (super.magic == CRAMFS_MAGIC_WEND && !silent) + if (super->magic != CRAMFS_MAGIC) { + if (super->magic == CRAMFS_MAGIC_WEND && !silent) pr_err("wrong endianness\n"); else if (!silent) pr_err("wrong magic\n"); @@ -301,34 +342,34 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent) } /* get feature flags first */ - if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) { + if (super->flags & ~CRAMFS_SUPPORTED_FLAGS) { pr_err("unsupported filesystem features\n"); return -EINVAL; } /* Check that the root inode is in a sane state */ - if (!S_ISDIR(super.root.mode)) { + if (!S_ISDIR(super->root.mode)) { pr_err("root is not a directory\n"); return -EINVAL; } /* correct strange, hard-coded permissions of mkcramfs */ - super.root.mode |= (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + super->root.mode |= 0555; - root_offset = super.root.offset << 2; - if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) { - sbi->size = super.size; - sbi->blocks = super.fsid.blocks; - sbi->files = super.fsid.files; + root_offset = super->root.offset << 2; + if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) { + sbi->size = super->size; + sbi->blocks = super->fsid.blocks; + sbi->files = super->fsid.files; } else { sbi->size = 1<<28; sbi->blocks = 0; sbi->files = 0; } - sbi->magic = super.magic; - sbi->flags = super.flags; + sbi->magic = super->magic; + sbi->flags = super->flags; if (root_offset == 0) pr_info("empty filesystem"); - else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && + else if (!(super->flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && ((root_offset != sizeof(struct cramfs_super)) && (root_offset != 512 + sizeof(struct cramfs_super)))) { @@ -336,9 +377,18 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent) return -EINVAL; } + return 0; +} + +static int cramfs_finalize_super(struct super_block *sb, + struct cramfs_inode *cramfs_root) +{ + struct inode *root; + /* Set it all up.. */ + sb->s_flags |= MS_RDONLY; sb->s_op = &cramfs_ops; - root = get_cramfs_inode(sb, &super.root, 0); + root = get_cramfs_inode(sb, cramfs_root, 0); if (IS_ERR(root)) return PTR_ERR(root); sb->s_root = d_make_root(root); @@ -347,10 +397,79 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent) return 0; } +static int cramfs_blkdev_fill_super(struct super_block *sb, void *data, + int silent) +{ + struct cramfs_sb_info *sbi; + struct cramfs_super super; + int i, err; + + sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + sb->s_fs_info = sbi; + + /* Invalidate the read buffers on mount: think disk change.. */ + for (i = 0; i < READ_BUFFERS; i++) + buffer_blocknr[i] = -1; + + err = cramfs_read_super(sb, &super, silent); + if (err) + return err; + return cramfs_finalize_super(sb, &super.root); +} + +static int cramfs_mtd_fill_super(struct super_block *sb, void *data, + int silent) +{ + struct cramfs_sb_info *sbi; + struct cramfs_super super; + int err; + + sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + sb->s_fs_info = sbi; + + /* Map only one page for now. Will remap it when fs size is known. */ + err = mtd_point(sb->s_mtd, 0, PAGE_SIZE, &sbi->mtd_point_size, + &sbi->linear_virt_addr, &sbi->linear_phys_addr); + if (err || sbi->mtd_point_size != PAGE_SIZE) { + pr_err("unable to get direct memory access to mtd:%s\n", + sb->s_mtd->name); + return err ? : -ENODATA; + } + + pr_info("checking physical address %pap for linear cramfs image\n", + &sbi->linear_phys_addr); + err = cramfs_read_super(sb, &super, silent); + if (err) + return err; + + /* Remap the whole filesystem now */ + pr_info("linear cramfs image on mtd:%s appears to be %lu KB in size\n", + sb->s_mtd->name, sbi->size/1024); + mtd_unpoint(sb->s_mtd, 0, PAGE_SIZE); + err = mtd_point(sb->s_mtd, 0, sbi->size, &sbi->mtd_point_size, + &sbi->linear_virt_addr, &sbi->linear_phys_addr); + if (err || sbi->mtd_point_size != sbi->size) { + pr_err("unable to get direct memory access to mtd:%s\n", + sb->s_mtd->name); + return err ? : -ENODATA; + } + + return cramfs_finalize_super(sb, &super.root); +} + static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; - u64 id = huge_encode_dev(sb->s_bdev->bd_dev); + u64 id = 0; + + if (sb->s_bdev) + id = huge_encode_dev(sb->s_bdev->bd_dev); + else if (sb->s_dev) + id = huge_encode_dev(sb->s_dev); buf->f_type = CRAMFS_MAGIC; buf->f_bsize = PAGE_SIZE; @@ -573,10 +692,22 @@ static const struct super_operations cramfs_ops = { .statfs = cramfs_statfs, }; -static struct dentry *cramfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static struct dentry *cramfs_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data) { - return mount_bdev(fs_type, flags, dev_name, data, cramfs_fill_super); + struct dentry *ret = ERR_PTR(-ENOPROTOOPT); + + if (IS_ENABLED(CONFIG_CRAMFS_MTD)) { + ret = mount_mtd(fs_type, flags, dev_name, data, + cramfs_mtd_fill_super); + if (!IS_ERR(ret)) + return ret; + } + if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) { + ret = mount_bdev(fs_type, flags, dev_name, data, + cramfs_blkdev_fill_super); + } + return ret; } static struct file_system_type cramfs_fs_type = { From patchwork Thu Oct 12 06:16:11 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Pitre X-Patchwork-Id: 115580 Delivered-To: patch@linaro.org Received: by 10.140.22.163 with SMTP id 32csp1575387qgn; Wed, 11 Oct 2017 23:17:16 -0700 (PDT) X-Google-Smtp-Source: AOwi7QB2ai6HbaGAe7Pq5DqHSBs7M0jaSteZe+RY4JmumFlRCmwOS4iRgpAs2WdxNk42jVdNX+UJ X-Received: by 10.159.195.7 with SMTP id bd7mr1295466plb.43.1507789035930; Wed, 11 Oct 2017 23:17:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1507789035; cv=none; d=google.com; s=arc-20160816; b=BgY61Kw/5KwT7zBo2nwIAQ0iF6FLJr7ti6C0v2g0jmtqNbHsMBbwvBCFXKLvytFb+f O1eXhQrSR7sx1srHLucvdh0ZqTO+i2jmYYXFdajVVDAjuFaQ33wTdzUB8VRTPmC19yiG wA7mZ2NdDULATG5i/0ZV9frdcFEzc78eaI6qFvb47mXb+nlkSHLUg1h1TNNwMFC92QrG WX2/SlWPiDsyJLO7GiQsNj9u26sNjyKTfaafTxXv2Qs53boxMvxKpdDYy56EqLrJn2H/ dbno5PpPBExvKvDS4FcZA175OwftV0QwjrfdrZ19B81IdPI3mvpoDATAPwdN2TUNF44H 5HUQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=Z+HRJ23rOIzeXeqSB7M/+Zo64jaZ1AawGbh/iPZLSbE=; b=LvCWU4tneExiqEop0nTggpJ8KKgq2F0mwZKpORZBnKjU08dkzeiPDh+SoO/0OYlO1U BAcSbefINul9itWL2g2nCjVDZIDpYLaKE5KudpMggilSwZrJAt/R5oJRcD9VAEjgtgF9 nvAZgYXnlmdd2E6Rsink6OoBaDcmXHN1mkIHUcyLISZ7c8SaeDYJcM+l3OZCMmAwL0zn nTSZb7+g2O6/ULNllzvwc7H8Miglo4uHIGnOTOLMcGk/Fq9+JGYYu4xD7rZ2k8qZ77q2 +/UqQTDnlDpLR9LddOBlKrgY6B7IKnO1LskD5sG6DgS9Y8CYNH3VicaFLd9VjWVXDlRJ wovQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@pobox.com header.s=sasl header.b=Ae2mkys3; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id y128si11879321pfy.408.2017.10.11.23.17.15; Wed, 11 Oct 2017 23:17:15 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@pobox.com header.s=sasl header.b=Ae2mkys3; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752665AbdJLGQ7 (ORCPT + 27 others); Thu, 12 Oct 2017 02:16:59 -0400 Received: from pb-smtp1.pobox.com ([64.147.108.70]:51486 "EHLO sasl.smtp.pobox.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752490AbdJLGQ5 (ORCPT ); Thu, 12 Oct 2017 02:16:57 -0400 Received: from sasl.smtp.pobox.com (unknown [127.0.0.1]) by pb-smtp1.pobox.com (Postfix) with ESMTP id D8C1993052; Thu, 12 Oct 2017 02:16:56 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=pobox.com; h=from:to:cc :subject:date:message-id:in-reply-to:references; s=sasl; bh=wKgB 1fP0SZxeBjXoh0ilOEfpiH0=; b=Ae2mkys30tAPYSZ1vocDKovtugefdzT4WL5f 9KXTinisyLfU5X5POU4q4xZQglzouI4EHsMKE27nd0m/Nsf4ObGflWuof+HgimE+ TV/jU9+LlrYf5fAtwiYVsiceA2zERlKa4an51XJxkb14awQBREUJjGcDEltXkeuM gnDmbww= Received: from pb-smtp1.nyi.icgroup.com (unknown [127.0.0.1]) by pb-smtp1.pobox.com (Postfix) with ESMTP id D16F193051; Thu, 12 Oct 2017 02:16:56 -0400 (EDT) Received: from yoda.home (unknown [137.175.234.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by pb-smtp1.pobox.com (Postfix) with ESMTPSA id 89D9793041; Thu, 12 Oct 2017 02:16:43 -0400 (EDT) Received: from xanadu.home (xanadu.home [192.168.2.2]) by yoda.home (Postfix) with ESMTP id AE2992DA0684; Thu, 12 Oct 2017 02:16:42 -0400 (EDT) From: Nicolas Pitre To: Alexander Viro , Christoph Hellwig Cc: linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, linux-embedded@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Brandt Subject: [PATCH v6 2/4] cramfs: implement uncompressed and arbitrary data block positioning Date: Thu, 12 Oct 2017 02:16:11 -0400 Message-Id: <20171012061613.28705-3-nicolas.pitre@linaro.org> X-Mailer: git-send-email 2.9.5 In-Reply-To: <20171012061613.28705-1-nicolas.pitre@linaro.org> References: <20171012061613.28705-1-nicolas.pitre@linaro.org> X-Pobox-Relay-ID: EB0D8D40-AF14-11E7-AC07-8EF31968708C-78420484!pb-smtp1.pobox.com Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Two new capabilities are introduced here: - The ability to store some blocks uncompressed. - The ability to locate blocks anywhere. Those capabilities can be used independently, but the combination opens the possibility for execute-in-place (XIP) of program text segments that must remain uncompressed, and in the MMU case, must have a specific alignment. It is even possible to still have the writable data segments from the same file compressed as they have to be copied into RAM anyway. This is achieved by giving special meanings to some unused block pointer bits while remaining compatible with legacy cramfs images. Signed-off-by: Nicolas Pitre Tested-by: Chris Brandt --- fs/cramfs/README | 31 ++++++++++++++- fs/cramfs/inode.c | 90 +++++++++++++++++++++++++++++++++--------- include/uapi/linux/cramfs_fs.h | 26 +++++++++++- 3 files changed, 126 insertions(+), 21 deletions(-) -- 2.9.5 diff --git a/fs/cramfs/README b/fs/cramfs/README index 9d4e7ea311..d71b27e0ff 100644 --- a/fs/cramfs/README +++ b/fs/cramfs/README @@ -49,17 +49,46 @@ same as the start of the (i+1)'th if there is one). The first immediately follows the last for the file. s are each 32 bits long. +When the CRAMFS_FLAG_EXT_BLOCK_POINTERS capability bit is set, each +'s top bits may contain special flags as follows: + +CRAMFS_BLK_FLAG_UNCOMPRESSED (bit 31): + The block data is not compressed and should be copied verbatim. + +CRAMFS_BLK_FLAG_DIRECT_PTR (bit 30): + The stores the actual block start offset and not + its end, shifted right by 2 bits. The block must therefore be + aligned to a 4-byte boundary. The block size is either blksize + if CRAMFS_BLK_FLAG_UNCOMPRESSED is also specified, otherwise + the compressed data length is included in the first 2 bytes of + the block data. This is used to allow discontiguous data layout + and specific data block alignments e.g. for XIP applications. + + The order of 's is a depth-first descent of the directory tree, i.e. the same order as `find -size +0 \( -type f -o -type l \) -print'. : The i'th is the output of zlib's compress function -applied to the i'th blksize-sized chunk of the input data. +applied to the i'th blksize-sized chunk of the input data if the +corresponding CRAMFS_BLK_FLAG_UNCOMPRESSED bit is not set, +otherwise it is the input data directly. (For the last of the file, the input may of course be smaller.) Each may be a different size. (See above.) + s are merely byte-aligned, not generally u32-aligned. +When CRAMFS_BLK_FLAG_DIRECT_PTR is specified then the corresponding + may be located anywhere and not necessarily contiguous with +the previous/next blocks. In that case it is minimally u32-aligned. +If CRAMFS_BLK_FLAG_UNCOMPRESSED is also specified then the size is always +blksize except for the last block which is limited by the file length. +If CRAMFS_BLK_FLAG_DIRECT_PTR is set and CRAMFS_BLK_FLAG_UNCOMPRESSED +is not set then the first 2 bytes of the block contains the size of the +remaining block data as this cannot be determined from the placement of +logically adjacent blocks. + Holes ----- diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 321a1fe17e..d3066a8534 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -621,34 +621,86 @@ static int cramfs_readpage(struct file *file, struct page *page) if (page->index < maxblock) { struct super_block *sb = inode->i_sb; - u32 blkptr_offset = OFFSET(inode) + page->index*4; - u32 start_offset, compr_len; + u32 blkptr_offset = OFFSET(inode) + page->index * 4; + u32 block_ptr, block_start, block_len; + bool uncompressed, direct; - start_offset = OFFSET(inode) + maxblock*4; mutex_lock(&read_mutex); - if (page->index) - start_offset = *(u32 *) cramfs_read(sb, blkptr_offset-4, - 4); - compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4) - - start_offset); - mutex_unlock(&read_mutex); + block_ptr = *(u32 *) cramfs_read(sb, blkptr_offset, 4); + uncompressed = (block_ptr & CRAMFS_BLK_FLAG_UNCOMPRESSED); + direct = (block_ptr & CRAMFS_BLK_FLAG_DIRECT_PTR); + block_ptr &= ~CRAMFS_BLK_FLAGS; + + if (direct) { + /* + * The block pointer is an absolute start pointer, + * shifted by 2 bits. The size is included in the + * first 2 bytes of the data block when compressed, + * or PAGE_SIZE otherwise. + */ + block_start = block_ptr << CRAMFS_BLK_DIRECT_PTR_SHIFT; + if (uncompressed) { + block_len = PAGE_SIZE; + /* if last block: cap to file length */ + if (page->index == maxblock - 1) + block_len = + offset_in_page(inode->i_size); + } else { + block_len = *(u16 *) + cramfs_read(sb, block_start, 2); + block_start += 2; + } + } else { + /* + * The block pointer indicates one past the end of + * the current block (start of next block). If this + * is the first block then it starts where the block + * pointer table ends, otherwise its start comes + * from the previous block's pointer. + */ + block_start = OFFSET(inode) + maxblock * 4; + if (page->index) + block_start = *(u32 *) + cramfs_read(sb, blkptr_offset - 4, 4); + /* Beware... previous ptr might be a direct ptr */ + if (unlikely(block_start & CRAMFS_BLK_FLAG_DIRECT_PTR)) { + /* See comments on earlier code. */ + u32 prev_start = block_start; + block_start = prev_start & ~CRAMFS_BLK_FLAGS; + block_start <<= CRAMFS_BLK_DIRECT_PTR_SHIFT; + if (prev_start & CRAMFS_BLK_FLAG_UNCOMPRESSED) { + block_start += PAGE_SIZE; + } else { + block_len = *(u16 *) + cramfs_read(sb, block_start, 2); + block_start += 2 + block_len; + } + } + block_start &= ~CRAMFS_BLK_FLAGS; + block_len = block_ptr - block_start; + } - if (compr_len == 0) + if (block_len == 0) ; /* hole */ - else if (unlikely(compr_len > (PAGE_SIZE << 1))) { - pr_err("bad compressed blocksize %u\n", - compr_len); + else if (unlikely(block_len > 2*PAGE_SIZE || + (uncompressed && block_len > PAGE_SIZE))) { + mutex_unlock(&read_mutex); + pr_err("bad data blocksize %u\n", block_len); goto err; + } else if (uncompressed) { + memcpy(pgdata, + cramfs_read(sb, block_start, block_len), + block_len); + bytes_filled = block_len; } else { - mutex_lock(&read_mutex); bytes_filled = cramfs_uncompress_block(pgdata, PAGE_SIZE, - cramfs_read(sb, start_offset, compr_len), - compr_len); - mutex_unlock(&read_mutex); - if (unlikely(bytes_filled < 0)) - goto err; + cramfs_read(sb, block_start, block_len), + block_len); } + mutex_unlock(&read_mutex); + if (unlikely(bytes_filled < 0)) + goto err; } memset(pgdata + bytes_filled, 0, PAGE_SIZE - bytes_filled); diff --git a/include/uapi/linux/cramfs_fs.h b/include/uapi/linux/cramfs_fs.h index e4611a9b92..ce2c885133 100644 --- a/include/uapi/linux/cramfs_fs.h +++ b/include/uapi/linux/cramfs_fs.h @@ -73,6 +73,7 @@ struct cramfs_super { #define CRAMFS_FLAG_HOLES 0x00000100 /* support for holes */ #define CRAMFS_FLAG_WRONG_SIGNATURE 0x00000200 /* reserved */ #define CRAMFS_FLAG_SHIFTED_ROOT_OFFSET 0x00000400 /* shifted root fs */ +#define CRAMFS_FLAG_EXT_BLOCK_POINTERS 0x00000800 /* block pointer extensions */ /* * Valid values in super.flags. Currently we refuse to mount @@ -82,7 +83,30 @@ struct cramfs_super { #define CRAMFS_SUPPORTED_FLAGS ( 0x000000ff \ | CRAMFS_FLAG_HOLES \ | CRAMFS_FLAG_WRONG_SIGNATURE \ - | CRAMFS_FLAG_SHIFTED_ROOT_OFFSET ) + | CRAMFS_FLAG_SHIFTED_ROOT_OFFSET \ + | CRAMFS_FLAG_EXT_BLOCK_POINTERS ) +/* + * Block pointer flags + * + * The maximum block offset that needs to be represented is roughly: + * + * (1 << CRAMFS_OFFSET_WIDTH) * 4 + + * (1 << CRAMFS_SIZE_WIDTH) / PAGE_SIZE * (4 + PAGE_SIZE) + * = 0x11004000 + * + * That leaves room for 3 flag bits in the block pointer table. + */ +#define CRAMFS_BLK_FLAG_UNCOMPRESSED (1 << 31) +#define CRAMFS_BLK_FLAG_DIRECT_PTR (1 << 30) + +#define CRAMFS_BLK_FLAGS ( CRAMFS_BLK_FLAG_UNCOMPRESSED \ + | CRAMFS_BLK_FLAG_DIRECT_PTR ) + +/* + * Direct blocks are at least 4-byte aligned. + * Pointers to direct blocks are shifted down by 2 bits. + */ +#define CRAMFS_BLK_DIRECT_PTR_SHIFT 2 #endif /* _UAPI__CRAMFS_H */ From patchwork Thu Oct 12 06:16:12 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Pitre X-Patchwork-Id: 115579 Delivered-To: patch@linaro.org Received: by 10.140.22.163 with SMTP id 32csp1575262qgn; Wed, 11 Oct 2017 23:17:03 -0700 (PDT) X-Google-Smtp-Source: AOwi7QBdAApN8XH7o9ox/Ke1WtWzCVmTwmblM8YrMhVYIrOlQdIwiYzrJYSHlU+XrIGOjhMx8oZg X-Received: by 10.98.24.20 with SMTP id 20mr1306625pfy.71.1507789023556; Wed, 11 Oct 2017 23:17:03 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1507789023; cv=none; d=google.com; s=arc-20160816; b=eOxPmaUoTLYyut0u1tTTTqBF0uP6J/URWGUdv1EmwI1dTtP8KD1cmf3mM3KZRnlSYR L1a4Mc071Y3jt2TFMB+dbJp6KDEWEVa0BnH1LE0ObrNb42+r7JfGzQLPy/W28RdC3CbA tQGYtDYvr1//AuGZouqimjQGs1wmiQ7KvYVIn7X7qh6Ry0zGZzhSl+RwR2ds1F+9qcqc uYYzP3BwZ8Qa44arPd9Fz1R7oIVFKU6t6VSL36gOUl/PfpmjhYcMaqGiIv8m3nEIfqu6 IRK48LMixW9gRUtxUP3KjLIC1i3QLbcD6LhcGOp0cMGWrkO/RpZd1hrUJdsPqAz0I1+9 EiBQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=gX3FrjGdk+GUQRYTLjnwGYlJloHbTgzGN5+F7Dc/Qno=; b=ppm04OH6y6+xAn7VcGMQQvNFj6igwzDSLgTLptNt9IdwmIWrRvtLaPJPyoMjOwmwXl icqhpj+wiAmuxxqEjpll64t8afQYuZo56Pc7oK1XjZcSuM/Mjnlv6abE/6XQVKuHdDJg 2mXy801m4guRQ9I3lCmqCr+jZvAOkp2rSbgk2URRLcoiZasyYLjl0IfOiTVIpSAZG+1e 2XBLUL2bmy/cotx1mRAvNVhLK5qeuPvMEYyTbvhsnENbNetIQNWV4pSKPtTmm34J/ZCa NaelTcFC3n8FHzi4u+cnyNAAxV6+lR7PwLpuvN0u7q4tplJNhsq5FC2ttqDTszgKr0a8 4OPg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@pobox.com header.s=sasl header.b=LZcoBDhf; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id y128si11879321pfy.408.2017.10.11.23.17.03; Wed, 11 Oct 2017 23:17:03 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@pobox.com header.s=sasl header.b=LZcoBDhf; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752840AbdJLGRB (ORCPT + 27 others); Thu, 12 Oct 2017 02:17:01 -0400 Received: from pb-smtp1.pobox.com ([64.147.108.70]:56067 "EHLO sasl.smtp.pobox.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752477AbdJLGQ5 (ORCPT ); Thu, 12 Oct 2017 02:16:57 -0400 Received: from sasl.smtp.pobox.com (unknown [127.0.0.1]) by pb-smtp1.pobox.com (Postfix) with ESMTP id 67A019304E; Thu, 12 Oct 2017 02:16:56 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=pobox.com; h=from:to:cc :subject:date:message-id:in-reply-to:references; s=sasl; bh=k0Q3 liuZPRMNPPmVCNLytOHIT4E=; b=LZcoBDhfVcjExqll1zcisM4xFYH7hJSjAFVp wBW4kURZrWr9D/uL4SOGrePIyeikFhFweddXJNcMdLG3+h9Wgyf92dsKIPMnZjM3 Y2IZB6ga19CKNSfdWiTPb+qzhPsX9pLbt6ucA60K0FIu3Igef7Q0ObDVF7R26AEP 99oHB+o= Received: from pb-smtp1.nyi.icgroup.com (unknown [127.0.0.1]) by pb-smtp1.pobox.com (Postfix) with ESMTP id 5E4619304D; Thu, 12 Oct 2017 02:16:56 -0400 (EDT) Received: from yoda.home (unknown [137.175.234.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by pb-smtp1.pobox.com (Postfix) with ESMTPSA id 90BF393043; Thu, 12 Oct 2017 02:16:43 -0400 (EDT) Received: from xanadu.home (xanadu.home [192.168.2.2]) by yoda.home (Postfix) with ESMTP id C4BF42DA06B8; Thu, 12 Oct 2017 02:16:42 -0400 (EDT) From: Nicolas Pitre To: Alexander Viro , Christoph Hellwig Cc: linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, linux-embedded@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Brandt Subject: [PATCH v6 3/4] cramfs: add mmap support Date: Thu, 12 Oct 2017 02:16:12 -0400 Message-Id: <20171012061613.28705-4-nicolas.pitre@linaro.org> X-Mailer: git-send-email 2.9.5 In-Reply-To: <20171012061613.28705-1-nicolas.pitre@linaro.org> References: <20171012061613.28705-1-nicolas.pitre@linaro.org> X-Pobox-Relay-ID: EB11998A-AF14-11E7-87D1-8EF31968708C-78420484!pb-smtp1.pobox.com Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org When cramfs in physical memory is used then we have the opportunity to map files directly from ROM, directly into user space, saving on RAM usage. This gives us Execute-In-Place (XIP) support. For a file to be mmap()-able, the map area has to correspond to a range of uncompressed and contiguous blocks, and in the MMU case it also has to be page aligned. A version of mkcramfs with appropriate support is necessary to create such a filesystem image. In the MMU case it may happen for a vma structure to extend beyond the actual file size. This is notably the case in binfmt_elf.c:elf_map(). Or the file's last block is shared with other files and cannot be mapped as is. Rather than refusing to mmap it, we do a "mixed" map and let the regular fault handler populate the unmapped area with RAM-backed pages. In practice the unmapped area is seldom accessed so page faults might never occur before this area is discarded. In the non-MMU case it is the get_unmapped_area method that is responsible for providing the address where the actual data can be found. No mapping is necessary of course. Signed-off-by: Nicolas Pitre Tested-by: Chris Brandt --- fs/cramfs/inode.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) -- 2.9.5 Reviewed-by: Christoph Hellwig diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index d3066a8534..d967904c53 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -15,7 +15,10 @@ #include #include +#include #include +#include +#include #include #include #include @@ -51,6 +54,7 @@ static inline struct cramfs_sb_info *CRAMFS_SB(struct super_block *sb) static const struct super_operations cramfs_ops; static const struct inode_operations cramfs_dir_inode_operations; static const struct file_operations cramfs_directory_operations; +static const struct file_operations cramfs_physmem_fops; static const struct address_space_operations cramfs_aops; static DEFINE_MUTEX(read_mutex); @@ -98,6 +102,10 @@ static struct inode *get_cramfs_inode(struct super_block *sb, case S_IFREG: inode->i_fop = &generic_ro_fops; inode->i_data.a_ops = &cramfs_aops; + if (IS_ENABLED(CONFIG_CRAMFS_MTD) && + CRAMFS_SB(sb)->flags & CRAMFS_FLAG_EXT_BLOCK_POINTERS && + CRAMFS_SB(sb)->linear_phys_addr) + inode->i_fop = &cramfs_physmem_fops; break; case S_IFDIR: inode->i_op = &cramfs_dir_inode_operations; @@ -279,6 +287,207 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, return NULL; } +/* + * For a mapping to be possible, we need a range of uncompressed and + * contiguous blocks. Return the offset for the first block and number of + * valid blocks for which that is true, or zero otherwise. + */ +static u32 cramfs_get_block_range(struct inode *inode, u32 pgoff, u32 *pages) +{ + struct cramfs_sb_info *sbi = CRAMFS_SB(inode->i_sb); + int i; + u32 *blockptrs, first_block_addr; + + /* + * We can dereference memory directly here as this code may be + * reached only when there is a direct filesystem image mapping + * available in memory. + */ + blockptrs = (u32 *)(sbi->linear_virt_addr + OFFSET(inode) + pgoff * 4); + first_block_addr = blockptrs[0] & ~CRAMFS_BLK_FLAGS; + i = 0; + do { + u32 block_off = i * (PAGE_SIZE >> CRAMFS_BLK_DIRECT_PTR_SHIFT); + u32 expect = (first_block_addr + block_off) | + CRAMFS_BLK_FLAG_DIRECT_PTR | + CRAMFS_BLK_FLAG_UNCOMPRESSED; + if (blockptrs[i] != expect) { + pr_debug("range: block %d/%d got %#x expects %#x\n", + pgoff+i, pgoff + *pages - 1, + blockptrs[i], expect); + if (i == 0) + return 0; + break; + } + } while (++i < *pages); + + *pages = i; + return first_block_addr << CRAMFS_BLK_DIRECT_PTR_SHIFT; +} + +#ifdef CONFIG_MMU + +/* + * Return true if the last page of a file in the filesystem image contains + * some other data that doesn't belong to that file. It is assumed that the + * last block is CRAMFS_BLK_FLAG_DIRECT_PTR | CRAMFS_BLK_FLAG_UNCOMPRESSED + * (verified by cramfs_get_block_range() and directly accessible in memory. + */ +static bool cramfs_last_page_is_shared(struct inode *inode) +{ + struct cramfs_sb_info *sbi = CRAMFS_SB(inode->i_sb); + u32 partial, last_page, blockaddr, *blockptrs; + char *tail_data; + + partial = offset_in_page(inode->i_size); + if (!partial) + return false; + last_page = inode->i_size >> PAGE_SHIFT; + blockptrs = (u32 *)(sbi->linear_virt_addr + OFFSET(inode)); + blockaddr = blockptrs[last_page] & ~CRAMFS_BLK_FLAGS; + blockaddr <<= CRAMFS_BLK_DIRECT_PTR_SHIFT; + tail_data = sbi->linear_virt_addr + blockaddr + partial; + return memchr_inv(tail_data, 0, PAGE_SIZE - partial) ? true : false; +} + +static int cramfs_physmem_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode = file_inode(file); + struct cramfs_sb_info *sbi = CRAMFS_SB(inode->i_sb); + unsigned int pages, max_pages, offset; + unsigned long address, pgoff = vma->vm_pgoff; + char *bailout_reason; + int ret; + + ret = generic_file_readonly_mmap(file, vma); + if (ret) + return ret; + + /* + * Now try to pre-populate ptes for this vma with a direct + * mapping avoiding memory allocation when possible. + */ + + /* Could COW work here? */ + bailout_reason = "vma is writable"; + if (vma->vm_flags & VM_WRITE) + goto bailout; + + max_pages = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + bailout_reason = "beyond file limit"; + if (pgoff >= max_pages) + goto bailout; + pages = min(vma_pages(vma), max_pages - pgoff); + + offset = cramfs_get_block_range(inode, pgoff, &pages); + bailout_reason = "unsuitable block layout"; + if (!offset) + goto bailout; + address = sbi->linear_phys_addr + offset; + bailout_reason = "data is not page aligned"; + if (!PAGE_ALIGNED(address)) + goto bailout; + + /* Don't map the last page if it contains some other data */ + if (pgoff + pages == max_pages && cramfs_last_page_is_shared(inode)) { + pr_debug("mmap: %s: last page is shared\n", + file_dentry(file)->d_name.name); + pages--; + } + + if (!pages) { + bailout_reason = "no suitable block remaining"; + goto bailout; + } + + if (pages == vma_pages(vma)) { + /* + * The entire vma is mappable. remap_pfn_range() will + * make it distinguishable from a non-direct mapping + * in /proc//maps by substituting the file offset + * with the actual physical address. + */ + ret = remap_pfn_range(vma, vma->vm_start, address >> PAGE_SHIFT, + pages * PAGE_SIZE, vma->vm_page_prot); + } else { + /* + * Let's create a mixed map if we can't map it all. + * The normal paging machinery will take care of the + * unpopulated ptes via cramfs_readpage(). + */ + int i; + vma->vm_flags |= VM_MIXEDMAP; + for (i = 0; i < pages && !ret; i++) { + unsigned long off = i * PAGE_SIZE; + pfn_t pfn = phys_to_pfn_t(address + off, PFN_DEV); + ret = vm_insert_mixed(vma, vma->vm_start + off, pfn); + } + } + + if (!ret) + pr_debug("mapped %s[%lu] at 0x%08lx (%u/%lu pages) " + "to vma 0x%08lx, page_prot 0x%llx\n", + file_dentry(file)->d_name.name, pgoff, + address, pages, vma_pages(vma), vma->vm_start, + (unsigned long long)pgprot_val(vma->vm_page_prot)); + return ret; + +bailout: + pr_debug("%s[%lu]: direct mmap impossible: %s\n", + file_dentry(file)->d_name.name, pgoff, bailout_reason); + /* Didn't manage any direct map, but normal paging is still possible */ + return 0; +} + +#else /* CONFIG_MMU */ + +static int cramfs_physmem_mmap(struct file *file, struct vm_area_struct *vma) +{ + return vma->vm_flags & (VM_SHARED | VM_MAYSHARE) ? 0 : -ENOSYS; +} + +static unsigned long cramfs_physmem_get_unmapped_area(struct file *file, + unsigned long addr, unsigned long len, + unsigned long pgoff, unsigned long flags) +{ + struct inode *inode = file_inode(file); + struct super_block *sb = inode->i_sb; + struct cramfs_sb_info *sbi = CRAMFS_SB(sb); + unsigned int pages, block_pages, max_pages, offset; + + pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; + max_pages = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + if (pgoff >= max_pages || pages > max_pages - pgoff) + return -EINVAL; + block_pages = pages; + offset = cramfs_get_block_range(inode, pgoff, &block_pages); + if (!offset || block_pages != pages) + return -ENOSYS; + addr = sbi->linear_phys_addr + offset; + pr_debug("get_unmapped for %s ofs %#lx siz %lu at 0x%08lx\n", + file_dentry(file)->d_name.name, pgoff*PAGE_SIZE, len, addr); + return addr; +} + +static unsigned int cramfs_physmem_mmap_capabilities(struct file *file) +{ + return NOMMU_MAP_COPY | NOMMU_MAP_DIRECT | + NOMMU_MAP_READ | NOMMU_MAP_EXEC; +} + +#endif /* CONFIG_MMU */ + +static const struct file_operations cramfs_physmem_fops = { + .llseek = generic_file_llseek, + .read_iter = generic_file_read_iter, + .splice_read = generic_file_splice_read, + .mmap = cramfs_physmem_mmap, +#ifndef CONFIG_MMU + .get_unmapped_area = cramfs_physmem_get_unmapped_area, + .mmap_capabilities = cramfs_physmem_mmap_capabilities, +#endif +}; + static void cramfs_kill_sb(struct super_block *sb) { struct cramfs_sb_info *sbi = CRAMFS_SB(sb); From patchwork Thu Oct 12 06:16:13 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Pitre X-Patchwork-Id: 115578 Delivered-To: patch@linaro.org Received: by 10.140.22.163 with SMTP id 32csp1575101qgn; Wed, 11 Oct 2017 23:16:51 -0700 (PDT) X-Google-Smtp-Source: AOwi7QD9D8ze6dO/xWoBatvXw8q7zyVr2fc9n0fDm0jvqggzDJLdA8/mjcNckAOmDfIlJlpk/PYu X-Received: by 10.101.93.65 with SMTP id e1mr1263300pgt.150.1507789010949; Wed, 11 Oct 2017 23:16:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1507789010; cv=none; d=google.com; s=arc-20160816; b=yMgOd1frHKvNNEAoqA/eco4XGKSDLpuN+8JKv6Sk6LfosiEO3Nv8A/B6eu8mVV6hzx +1ABcjf1nxsTYM9B8Dhua4gh12EQY7jTpzodjofV4IBOmvBroZeRNO7/mUJho8QaSNdu DnmGNZihHEK9WwbZu21N+0jPbvPrAUl3rJEOEhWtW+oy8q9eeUjHJ92LhXaZVIyKOxsV VGxaQEy2E1UIFaLGs1+qpvGZYZFy4YiVQd8eAP9jySJjXkFFM0OXuD6EJ6nYBFh+mlAo afCBMUCaK4pR45detc0OXeQoPdSVViwrCiy99WMLxP6cNBwEqvRy0qcJkdWJppXd8zwO dycg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=7uAOASj+qyvt4VixU9DrP7Bz9TCnWJ2J7MhnA1BLnk0=; b=OwtPdS5cAgR00/J8aBPTmfh93ab9uioheg64zgsvSIRQwPlaFsoS9Dx4njgMYAH1s5 mlclxg5XWQNxqUodtKGIEiUYszSLVXLRWuBJ7KmnmIhYggFKRZv3Gh04K8N8x2Tv5GT7 o7cyL9E1PA/INB6kR2jrrs7TliXRv6M9Wucyo0z3fRiTi3ISwfqB0iRCDnZam29YNd+R dEjaPufGlZCc1iIPFzHtQEL0s42x2n6aeUDMU+XIPJoYcKV+IKqyiZChSOrQTR8RzaX6 QI+kr9XLHid+QfoeeM11ss3xZYAd/LSwuSL08bMyWQp64AbLr1BKUwKsN/Mvz6g4dJQK OL4w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@pobox.com header.s=sasl header.b=Sq2wkXAA; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 64si2134909ply.756.2017.10.11.23.16.50; Wed, 11 Oct 2017 23:16:50 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@pobox.com header.s=sasl header.b=Sq2wkXAA; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752446AbdJLGQs (ORCPT + 27 others); Thu, 12 Oct 2017 02:16:48 -0400 Received: from pb-smtp2.pobox.com ([64.147.108.71]:51655 "EHLO sasl.smtp.pobox.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751659AbdJLGQo (ORCPT ); Thu, 12 Oct 2017 02:16:44 -0400 Received: from sasl.smtp.pobox.com (unknown [127.0.0.1]) by pb-smtp2.pobox.com (Postfix) with ESMTP id 23E809C414; Thu, 12 Oct 2017 02:16:44 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=pobox.com; h=from:to:cc :subject:date:message-id:in-reply-to:references; s=sasl; bh=qsrI 3vSj7UfszHua+aKmwwWh8oY=; b=Sq2wkXAARlZSoehN6pTwYbLIPN7Dl8QCMcZA GblwF61hkcFfgmQ8jNhlT7nd5EKgKZTj5oEgiOqDukXiHf0TYkgbL/FbM4UD2qv9 i5/h9zrZ4euJ9OY1K/STOiZALYEg4pTos6Ei2GweSVFUoh+hN+t0opc0tveIwQIF 7jTRGGU= Received: from pb-smtp2.nyi.icgroup.com (unknown [127.0.0.1]) by pb-smtp2.pobox.com (Postfix) with ESMTP id 1BC199C413; Thu, 12 Oct 2017 02:16:44 -0400 (EDT) Received: from yoda.home (unknown [137.175.234.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by pb-smtp2.pobox.com (Postfix) with ESMTPSA id A8A849C40C; Thu, 12 Oct 2017 02:16:43 -0400 (EDT) Received: from xanadu.home (xanadu.home [192.168.2.2]) by yoda.home (Postfix) with ESMTP id CF2A92DA06C1; Thu, 12 Oct 2017 02:16:42 -0400 (EDT) From: Nicolas Pitre To: Alexander Viro , Christoph Hellwig Cc: linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, linux-embedded@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Brandt Subject: [PATCH v6 4/4] cramfs: rehabilitate it Date: Thu, 12 Oct 2017 02:16:13 -0400 Message-Id: <20171012061613.28705-5-nicolas.pitre@linaro.org> X-Mailer: git-send-email 2.9.5 In-Reply-To: <20171012061613.28705-1-nicolas.pitre@linaro.org> References: <20171012061613.28705-1-nicolas.pitre@linaro.org> X-Pobox-Relay-ID: EB207162-AF14-11E7-B841-575F0C78B957-78420484!pb-smtp2.pobox.com Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Update documentation, pointer to latest tools, appoint myself as maintainer. Given it's been unloved for so long, I don't expect anyone will protest. Signed-off-by: Nicolas Pitre Tested-by: Chris Brandt --- Documentation/filesystems/cramfs.txt | 42 ++++++++++++++++++++++++++++++++++++ MAINTAINERS | 4 ++-- fs/cramfs/Kconfig | 9 +++++--- 3 files changed, 50 insertions(+), 5 deletions(-) -- 2.9.5 diff --git a/Documentation/filesystems/cramfs.txt b/Documentation/filesystems/cramfs.txt index 4006298f67..8e19a53d64 100644 --- a/Documentation/filesystems/cramfs.txt +++ b/Documentation/filesystems/cramfs.txt @@ -45,6 +45,48 @@ you can just change the #define in mkcramfs.c, so long as you don't mind the filesystem becoming unreadable to future kernels. +Memory Mapped cramfs image +-------------------------- + +The CRAMFS_MTD Kconfig option adds support for loading data directly from +a physical linear memory range (usually non volatile memory like Flash) +instead of going through the block device layer. This saves some memory +since no intermediate buffering is necessary to hold the data before +decompressing. + +And when data blocks are kept uncompressed and properly aligned, they will +automatically be mapped directly into user space whenever possible providing +eXecute-In-Place (XIP) from ROM of read-only segments. Data segments mapped +read-write (hence they have to be copied to RAM) may still be compressed in +the cramfs image in the same file along with non compressed read-only +segments. Both MMU and no-MMU systems are supported. This is particularly +handy for tiny embedded systems with very tight memory constraints. + +The location of the cramfs image in memory is system dependent. You must +know the proper physical address where the cramfs image is located and +configure an MTD device for it. Also, that MTD device must be supported +by a map driver that implements the "point" method. Examples of such +MTD drivers are cfi_cmdset_0001 (Intel/Sharp CFI flash) or physmap +(Flash device in physical memory map). MTD partitions based on such devices +are fine too. Then that device should be specified with the "mtd:" prefix +as the mount device argument. For example, to mount the MTD device named +"fs_partition" on the /mnt directory: + +$ mount -t cramfs mtd:fs_partition /mnt + +To boot a kernel with this as root filesystem, suffice to specify +something like "root=mtd:fs_partition" on the kernel command line. + + +Tools +----- + +A version of mkcramfs that can take advantage of the latest capabilities +described above can be found here: + +https://github.com/npitre/cramfs-tools + + For /usr/share/magic -------------------- diff --git a/MAINTAINERS b/MAINTAINERS index 65b0c88d5e..cd621c5f52 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3676,8 +3676,8 @@ F: drivers/cpuidle/* F: include/linux/cpuidle.h CRAMFS FILESYSTEM -W: http://sourceforge.net/projects/cramfs/ -S: Orphan / Obsolete +M: Nicolas Pitre +S: Maintained F: Documentation/filesystems/cramfs.txt F: fs/cramfs/ diff --git a/fs/cramfs/Kconfig b/fs/cramfs/Kconfig index ef86b06bc0..f937082f32 100644 --- a/fs/cramfs/Kconfig +++ b/fs/cramfs/Kconfig @@ -1,5 +1,5 @@ config CRAMFS - tristate "Compressed ROM file system support (cramfs) (OBSOLETE)" + tristate "Compressed ROM file system support (cramfs)" select ZLIB_INFLATE help Saying Y here includes support for CramFs (Compressed ROM File @@ -15,8 +15,11 @@ config CRAMFS cramfs. Note that the root file system (the one containing the directory /) cannot be compiled as a module. - This filesystem is obsoleted by SquashFS, which is much better - in terms of performance and features. + This filesystem is limited in capabilities and performance on + purpose to remain small and low on RAM usage. It is most suitable + for small embedded systems. If you have ample RAM to spare, you may + consider a more capable compressed filesystem such as SquashFS + which is much better in terms of performance and features. If unsure, say N.