From: Nicolas Pitre <nicolas.pitre@linaro.org>
To: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org, linux-embedded@vger.kernel.org,
linux-kernel@vger.kernel.org,
Chris Brandt <Chris.Brandt@renesas.com>
Subject: [PATCH 4/5] cramfs: add mmap support
Date: Fri, 11 Aug 2017 15:22:51 -0400 [thread overview]
Message-ID: <20170811192252.19062-5-nicolas.pitre@linaro.org> (raw)
In-Reply-To: <20170811192252.19062-1-nicolas.pitre@linaro.org>
When cramfs_physmem 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.
Signed-off-by: Nicolas Pitre <nico@linaro.org>
---
fs/cramfs/inode.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 149 insertions(+)
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index b825ae162c..5aedbd224e 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
+#include <linux/ramfs.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/blkdev.h>
@@ -49,6 +50,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);
@@ -96,6 +98,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_PHYSMEM) &&
+ 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;
@@ -277,6 +283,149 @@ 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 if that
+ * verifies, or zero otherwise.
+ */
+static u32 cramfs_get_block_range(struct inode *inode, u32 pgoff, u32 pages)
+{
+ struct super_block *sb = inode->i_sb;
+ struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
+ int i;
+ u32 *blockptrs, blockaddr;
+
+ /*
+ * 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);
+ blockaddr = blockptrs[0] & ~CRAMFS_BLK_FLAGS;
+ i = 0;
+ do {
+ u32 expect = blockaddr + i * (PAGE_SIZE >> 2);
+ expect |= CRAMFS_BLK_FLAG_DIRECT_PTR|CRAMFS_BLK_FLAG_UNCOMPRESSED;
+ pr_debug("range: block %d/%d got %#x expects %#x\n",
+ pgoff+i, pgoff+pages-1, blockptrs[i], expect);
+ if (blockptrs[i] != expect)
+ return 0;
+ } while (++i < pages);
+
+ /* stored "direct" block ptrs are shifted down by 2 bits */
+ return blockaddr << 2;
+}
+
+static int cramfs_physmem_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct inode *inode = file_inode(file);
+ struct super_block *sb = inode->i_sb;
+ struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
+ unsigned int pages, max_pages, offset;
+ unsigned long length, address;
+ char *fail_reason;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_MMU))
+ return vma->vm_flags & (VM_SHARED | VM_MAYSHARE) ? 0 : -ENOSYS;
+
+ if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
+ return -EINVAL;
+
+ vma->vm_ops = &generic_file_vm_ops;
+ if (vma->vm_flags & VM_WRITE)
+ return 0;
+
+ length = vma->vm_end - vma->vm_start;
+ pages = (length + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ max_pages = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ if (vma->vm_pgoff >= max_pages || pages > max_pages - vma->vm_pgoff)
+ return -EINVAL;
+
+ offset = cramfs_get_block_range(inode, vma->vm_pgoff, pages);
+ fail_reason = "unsuitable block layout";
+ if (!offset)
+ goto fail;
+ address = sbi->linear_phys_addr + offset;
+ fail_reason = "data is not page aligned";
+ if (!PAGE_ALIGNED(address))
+ goto fail;
+
+ /* Don't map a partial page if it contains some other data */
+ if (unlikely(vma->vm_pgoff + pages == max_pages)) {
+ unsigned int partial = offset_in_page(inode->i_size);
+ if (partial) {
+ char *data = sbi->linear_virt_addr + offset;
+ data += (pages - 1) * PAGE_SIZE + partial;
+ fail_reason = "last partial page is shared";
+ while ((unsigned long)data & 7)
+ if (*data++ != 0)
+ goto fail;
+ while (offset_in_page(data)) {
+ if (*(u64 *)data != 0)
+ goto fail;
+ data += 8;
+ }
+ }
+ }
+
+ ret = remap_pfn_range(vma, vma->vm_start, address >> PAGE_SHIFT,
+ length, vma->vm_page_prot);
+ if (ret)
+ return ret;
+ pr_debug("mapped %s at 0x%08lx, length %lu to vma 0x%08lx, "
+ "page_prot 0x%llx\n", file_dentry(file)->d_name.name,
+ address, length, vma->vm_start,
+ (unsigned long long)pgprot_val(vma->vm_page_prot));
+ return 0;
+
+fail:
+ pr_debug("%s: direct mmap failed: %s\n",
+ file_dentry(file)->d_name.name, fail_reason);
+ return 0;
+}
+
+#ifndef CONFIG_MMU
+
+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, 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;
+ offset = cramfs_get_block_range(inode, pgoff, pages);
+ if (!offset)
+ 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 cramfs_physmem_mmap_capabilities(struct file *file)
+{
+ return NOMMU_MAP_COPY | NOMMU_MAP_DIRECT | NOMMU_MAP_READ | NOMMU_MAP_EXEC;
+}
+#endif
+
+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_blkdev_kill_sb(struct super_block *sb)
{
struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
--
2.9.4
next prev parent reply other threads:[~2017-08-11 19:31 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-08-11 19:22 [PATCH 0/5] cramfs refresh for embedded usage Nicolas Pitre
2017-08-11 19:22 ` [PATCH 1/5] cramfs: direct memory access support Nicolas Pitre
2017-08-12 7:49 ` Christoph Hellwig
2017-08-14 2:29 ` Nicolas Pitre
2017-08-11 19:22 ` [PATCH 2/5] cramfs: make cramfs_physmem usable as root fs Nicolas Pitre
2017-08-11 19:22 ` [PATCH 3/5] cramfs: implement uncompressed and arbitrary data block positioning Nicolas Pitre
2017-08-11 19:22 ` Nicolas Pitre [this message]
2017-08-11 19:22 ` [PATCH 5/5] cramfs: rehabilitate it Nicolas Pitre
2017-08-14 17:11 ` [PATCH 0/5] cramfs refresh for embedded usage Chris Brandt
2017-08-14 17:31 ` Nicolas Pitre
2017-08-14 18:01 ` Chris Brandt
2017-08-14 18:17 ` Nicolas Pitre
2017-08-14 18:37 ` Chris Brandt
2017-08-15 4:10 ` Nicolas Pitre
2017-08-15 11:00 ` Chris Brandt
2017-08-16 5:10 ` Nicolas Pitre
2017-08-16 11:08 ` Chris Brandt
2017-08-16 14:29 ` Nicolas Pitre
2017-08-16 15:12 ` Chris Brandt
2017-08-17 1:17 ` Nicolas Pitre
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170811192252.19062-5-nicolas.pitre@linaro.org \
--to=nicolas.pitre@linaro.org \
--cc=Chris.Brandt@renesas.com \
--cc=linux-embedded@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).