[PATCH] xip: ext2: execute in place
Carsten Otte [Fri, 24 Jun 2005 05:05:26 +0000 (22:05 -0700)]
These are the ext2 related parts.  Ext2 now uses the xip_* file operations
along with the get_xip_page aop when mounted with -o xip.

Signed-off-by: Carsten Otte <cotte@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

fs/Kconfig
fs/ext2/Makefile
fs/ext2/ext2.h
fs/ext2/file.c
fs/ext2/inode.c
fs/ext2/namei.c
fs/ext2/super.c
fs/ext2/xip.c [new file with mode: 0644]
fs/ext2/xip.h [new file with mode: 0644]
include/linux/ext2_fs.h
include/linux/fs.h

index 5c704d0..8157f2e 100644 (file)
@@ -50,6 +50,23 @@ config EXT2_FS_SECURITY
          If you are not using a security module that requires using
          extended attributes for file security labels, say N.
 
+config EXT2_FS_XIP
+       bool "Ext2 execute in place support"
+       depends on EXT2_FS
+       help
+         Execute in place can be used on memory-backed block devices. If you
+         enable this option, you can select to mount block devices which are
+         capable of this feature without using the page cache.
+
+         If you do not use a block device that is capable of using this,
+         or if unsure, say N.
+
+config FS_XIP
+# execute in place
+       bool
+       depends on EXT2_FS_XIP
+       default y
+
 config EXT3_FS
        tristate "Ext3 journalling file system support"
        help
index ee240a1..c5d02da 100644 (file)
@@ -10,3 +10,4 @@ ext2-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
 ext2-$(CONFIG_EXT2_FS_XATTR)    += xattr.o xattr_user.o xattr_trusted.o
 ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o
 ext2-$(CONFIG_EXT2_FS_SECURITY)         += xattr_security.o
+ext2-$(CONFIG_EXT2_FS_XIP)      += xip.o
index 8f0fd72..eed521d 100644 (file)
@@ -147,9 +147,11 @@ extern struct file_operations ext2_dir_operations;
 /* file.c */
 extern struct inode_operations ext2_file_inode_operations;
 extern struct file_operations ext2_file_operations;
+extern struct file_operations ext2_xip_file_operations;
 
 /* inode.c */
 extern struct address_space_operations ext2_aops;
+extern struct address_space_operations ext2_aops_xip;
 extern struct address_space_operations ext2_nobh_aops;
 
 /* namei.c */
index f5e8614..2b3d572 100644 (file)
@@ -55,6 +55,24 @@ struct file_operations ext2_file_operations = {
        .sendfile       = generic_file_sendfile,
 };
 
+#ifdef CONFIG_EXT2_FS_XIP
+struct file_operations ext2_xip_file_operations = {
+       .llseek         = generic_file_llseek,
+       .read           = do_sync_read,
+       .write          = do_sync_write,
+       .aio_read       = xip_file_aio_read,
+       .aio_write      = xip_file_aio_write,
+       .ioctl          = ext2_ioctl,
+       .mmap           = xip_file_mmap,
+       .open           = generic_file_open,
+       .release        = ext2_release_file,
+       .fsync          = ext2_sync_file,
+       .readv          = xip_file_readv,
+       .writev         = xip_file_writev,
+       .sendfile       = xip_file_sendfile,
+};
+#endif
+
 struct inode_operations ext2_file_inode_operations = {
        .truncate       = ext2_truncate,
 #ifdef CONFIG_EXT2_FS_XATTR
index a50d9db..53dceb0 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/mpage.h>
 #include "ext2.h"
 #include "acl.h"
+#include "xip.h"
 
 MODULE_AUTHOR("Remy Card and others");
 MODULE_DESCRIPTION("Second Extended Filesystem");
@@ -594,6 +595,16 @@ out:
        if (err)
                goto cleanup;
 
+       if (ext2_use_xip(inode->i_sb)) {
+               /*
+                * we need to clear the block
+                */
+               err = ext2_clear_xip_target (inode,
+                       le32_to_cpu(chain[depth-1].key));
+               if (err)
+                       goto cleanup;
+       }
+
        if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0)
                goto changed;
 
@@ -691,6 +702,11 @@ struct address_space_operations ext2_aops = {
        .writepages             = ext2_writepages,
 };
 
+struct address_space_operations ext2_aops_xip = {
+       .bmap                   = ext2_bmap,
+       .get_xip_page           = ext2_get_xip_page,
+};
+
 struct address_space_operations ext2_nobh_aops = {
        .readpage               = ext2_readpage,
        .readpages              = ext2_readpages,
@@ -910,7 +926,9 @@ void ext2_truncate (struct inode * inode)
        iblock = (inode->i_size + blocksize-1)
                                        >> EXT2_BLOCK_SIZE_BITS(inode->i_sb);
 
-       if (test_opt(inode->i_sb, NOBH))
+       if (mapping_is_xip(inode->i_mapping))
+               xip_truncate_page(inode->i_mapping, inode->i_size);
+       else if (test_opt(inode->i_sb, NOBH))
                nobh_truncate_page(inode->i_mapping, inode->i_size);
        else
                block_truncate_page(inode->i_mapping,
@@ -1110,11 +1128,16 @@ void ext2_read_inode (struct inode * inode)
 
        if (S_ISREG(inode->i_mode)) {
                inode->i_op = &ext2_file_inode_operations;
-               inode->i_fop = &ext2_file_operations;
-               if (test_opt(inode->i_sb, NOBH))
+               if (ext2_use_xip(inode->i_sb)) {
+                       inode->i_mapping->a_ops = &ext2_aops_xip;
+                       inode->i_fop = &ext2_xip_file_operations;
+               } else if (test_opt(inode->i_sb, NOBH)) {
                        inode->i_mapping->a_ops = &ext2_nobh_aops;
-               else
+                       inode->i_fop = &ext2_file_operations;
+               } else {
                        inode->i_mapping->a_ops = &ext2_aops;
+                       inode->i_fop = &ext2_file_operations;
+               }
        } else if (S_ISDIR(inode->i_mode)) {
                inode->i_op = &ext2_dir_inode_operations;
                inode->i_fop = &ext2_dir_operations;
index 3176b3d..c551395 100644 (file)
@@ -34,6 +34,7 @@
 #include "ext2.h"
 #include "xattr.h"
 #include "acl.h"
+#include "xip.h"
 
 /*
  * Couple of helper functions - make the code slightly cleaner.
@@ -127,11 +128,16 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, st
        int err = PTR_ERR(inode);
        if (!IS_ERR(inode)) {
                inode->i_op = &ext2_file_inode_operations;
-               inode->i_fop = &ext2_file_operations;
-               if (test_opt(inode->i_sb, NOBH))
+               if (ext2_use_xip(inode->i_sb)) {
+                       inode->i_mapping->a_ops = &ext2_aops_xip;
+                       inode->i_fop = &ext2_xip_file_operations;
+               } else if (test_opt(inode->i_sb, NOBH)) {
                        inode->i_mapping->a_ops = &ext2_nobh_aops;
-               else
+                       inode->i_fop = &ext2_file_operations;
+               } else {
                        inode->i_mapping->a_ops = &ext2_aops;
+                       inode->i_fop = &ext2_file_operations;
+               }
                mark_inode_dirty(inode);
                err = ext2_add_nondir(dentry, inode);
        }
index 661c3d9..876e391 100644 (file)
@@ -31,6 +31,7 @@
 #include "ext2.h"
 #include "xattr.h"
 #include "acl.h"
+#include "xip.h"
 
 static void ext2_sync_super(struct super_block *sb,
                            struct ext2_super_block *es);
@@ -257,7 +258,7 @@ enum {
        Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
        Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro,
        Opt_nouid32, Opt_check, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_nobh,
-       Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl,
+       Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_xip,
        Opt_ignore, Opt_err,
 };
 
@@ -286,6 +287,7 @@ static match_table_t tokens = {
        {Opt_nouser_xattr, "nouser_xattr"},
        {Opt_acl, "acl"},
        {Opt_noacl, "noacl"},
+       {Opt_xip, "xip"},
        {Opt_ignore, "grpquota"},
        {Opt_ignore, "noquota"},
        {Opt_ignore, "quota"},
@@ -397,6 +399,13 @@ static int parse_options (char * options,
                        printk("EXT2 (no)acl options not supported\n");
                        break;
 #endif
+               case Opt_xip:
+#ifdef CONFIG_EXT2_FS_XIP
+                       set_opt (sbi->s_mount_opt, XIP);
+#else
+                       printk("EXT2 xip option not supported\n");
+#endif
+                       break;
                case Opt_ignore:
                        break;
                default:
@@ -640,6 +649,9 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
                ((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ?
                 MS_POSIXACL : 0);
 
+       ext2_xip_verify_sb(sb); /* see if bdev supports xip, unset
+                                   EXT2_MOUNT_XIP if not */
+
        if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV &&
            (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) ||
             EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
@@ -668,6 +680,13 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
 
        blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
 
+       if ((ext2_use_xip(sb)) && ((blocksize != PAGE_SIZE) ||
+                                 (sb->s_blocksize != blocksize))) {
+               if (!silent)
+                       printk("XIP: Unsupported blocksize\n");
+               goto failed_mount;
+       }
+
        /* If the blocksize doesn't match, re-read the thing.. */
        if (sb->s_blocksize != blocksize) {
                brelse(bh);
@@ -916,6 +935,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
 {
        struct ext2_sb_info * sbi = EXT2_SB(sb);
        struct ext2_super_block * es;
+       unsigned long old_mount_opt = sbi->s_mount_opt;
 
        /*
         * Allow the "check" option to be passed as a remount option.
@@ -927,6 +947,11 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
                ((sbi->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
 
        es = sbi->s_es;
+       if (((sbi->s_mount_opt & EXT2_MOUNT_XIP) !=
+           (old_mount_opt & EXT2_MOUNT_XIP)) &&
+           invalidate_inodes(sb))
+               ext2_warning(sb, __FUNCTION__, "busy inodes while remounting "\
+                            "xip remain in cache (no functional problem)");
        if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
                return 0;
        if (*flags & MS_RDONLY) {
diff --git a/fs/ext2/xip.c b/fs/ext2/xip.c
new file mode 100644 (file)
index 0000000..d44431d
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *  linux/fs/ext2/xip.c
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Carsten Otte (cotte@de.ibm.com)
+ */
+
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/buffer_head.h>
+#include <linux/ext2_fs_sb.h>
+#include <linux/ext2_fs.h>
+#include "ext2.h"
+#include "xip.h"
+
+static inline int
+__inode_direct_access(struct inode *inode, sector_t sector, unsigned long *data) {
+       BUG_ON(!inode->i_sb->s_bdev->bd_disk->fops->direct_access);
+       return inode->i_sb->s_bdev->bd_disk->fops
+               ->direct_access(inode->i_sb->s_bdev,sector,data);
+}
+
+int
+ext2_clear_xip_target(struct inode *inode, int block) {
+       sector_t sector = block*(PAGE_SIZE/512);
+       unsigned long data;
+       int rc;
+
+       rc = __inode_direct_access(inode, sector, &data);
+       if (rc)
+               return rc;
+       clear_page((void*)data);
+       return 0;
+}
+
+void ext2_xip_verify_sb(struct super_block *sb)
+{
+       struct ext2_sb_info *sbi = EXT2_SB(sb);
+
+       if ((sbi->s_mount_opt & EXT2_MOUNT_XIP)) {
+               if ((sb->s_bdev == NULL) ||
+                       sb->s_bdev->bd_disk == NULL ||
+                       sb->s_bdev->bd_disk->fops == NULL ||
+                       sb->s_bdev->bd_disk->fops->direct_access == NULL) {
+                       sbi->s_mount_opt &= (~EXT2_MOUNT_XIP);
+                       ext2_warning(sb, __FUNCTION__,
+                               "ignoring xip option - not supported by bdev");
+               }
+       }
+}
+
+struct page*
+ext2_get_xip_page(struct address_space *mapping, sector_t blockno,
+                  int create)
+{
+       int rc;
+       unsigned long data;
+       struct buffer_head tmp;
+
+       tmp.b_state = 0;
+       tmp.b_blocknr = 0;
+       rc = ext2_get_block(mapping->host, blockno/(PAGE_SIZE/512) , &tmp,
+                               create);
+       if (rc)
+               return ERR_PTR(rc);
+       if (tmp.b_blocknr == 0) {
+               /* SPARSE block */
+               BUG_ON(create);
+               return ERR_PTR(-ENODATA);
+       }
+
+       rc = __inode_direct_access
+               (mapping->host,tmp.b_blocknr*(PAGE_SIZE/512) ,&data);
+       if (rc)
+               return ERR_PTR(rc);
+
+       SetPageUptodate(virt_to_page(data));
+       return virt_to_page(data);
+}
diff --git a/fs/ext2/xip.h b/fs/ext2/xip.h
new file mode 100644 (file)
index 0000000..aa85331
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *  linux/fs/ext2/xip.h
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Carsten Otte (cotte@de.ibm.com)
+ */
+
+#ifdef CONFIG_EXT2_FS_XIP
+extern void ext2_xip_verify_sb (struct super_block *);
+extern int ext2_clear_xip_target (struct inode *, int);
+
+static inline int ext2_use_xip (struct super_block *sb)
+{
+       struct ext2_sb_info *sbi = EXT2_SB(sb);
+       return (sbi->s_mount_opt & EXT2_MOUNT_XIP);
+}
+struct page* ext2_get_xip_page (struct address_space *, sector_t, int);
+#define mapping_is_xip(map) unlikely(map->a_ops->get_xip_page)
+#else
+#define mapping_is_xip(map)                    0
+#define ext2_xip_verify_sb(sb)                 do { } while (0)
+#define ext2_use_xip(sb)                       0
+#define ext2_clear_xip_target(inode, chain)    0
+#define ext2_get_xip_page                      NULL
+#endif
index fab4352..a657130 100644 (file)
@@ -300,18 +300,19 @@ struct ext2_inode {
 /*
  * Mount flags
  */
-#define EXT2_MOUNT_CHECK               0x0001  /* Do mount-time checks */
-#define EXT2_MOUNT_OLDALLOC            0x0002  /* Don't use the new Orlov allocator */
-#define EXT2_MOUNT_GRPID               0x0004  /* Create files with directory's group */
-#define EXT2_MOUNT_DEBUG               0x0008  /* Some debugging messages */
-#define EXT2_MOUNT_ERRORS_CONT         0x0010  /* Continue on errors */
-#define EXT2_MOUNT_ERRORS_RO           0x0020  /* Remount fs ro on errors */
-#define EXT2_MOUNT_ERRORS_PANIC                0x0040  /* Panic on errors */
-#define EXT2_MOUNT_MINIX_DF            0x0080  /* Mimics the Minix statfs */
-#define EXT2_MOUNT_NOBH                        0x0100  /* No buffer_heads */
-#define EXT2_MOUNT_NO_UID32            0x0200  /* Disable 32-bit UIDs */
-#define EXT2_MOUNT_XATTR_USER          0x4000  /* Extended user attributes */
-#define EXT2_MOUNT_POSIX_ACL           0x8000  /* POSIX Access Control Lists */
+#define EXT2_MOUNT_CHECK               0x000001  /* Do mount-time checks */
+#define EXT2_MOUNT_OLDALLOC            0x000002  /* Don't use the new Orlov allocator */
+#define EXT2_MOUNT_GRPID               0x000004  /* Create files with directory's group */
+#define EXT2_MOUNT_DEBUG               0x000008  /* Some debugging messages */
+#define EXT2_MOUNT_ERRORS_CONT         0x000010  /* Continue on errors */
+#define EXT2_MOUNT_ERRORS_RO           0x000020  /* Remount fs ro on errors */
+#define EXT2_MOUNT_ERRORS_PANIC                0x000040  /* Panic on errors */
+#define EXT2_MOUNT_MINIX_DF            0x000080  /* Mimics the Minix statfs */
+#define EXT2_MOUNT_NOBH                        0x000100  /* No buffer_heads */
+#define EXT2_MOUNT_NO_UID32            0x000200  /* Disable 32-bit UIDs */
+#define EXT2_MOUNT_XATTR_USER          0x004000  /* Extended user attributes */
+#define EXT2_MOUNT_POSIX_ACL           0x008000  /* POSIX Access Control Lists */
+#define EXT2_MOUNT_XIP                 0x010000  /* Execute in place */
 
 #define clear_opt(o, opt)              o &= ~EXT2_MOUNT_##opt
 #define set_opt(o, opt)                        o |= EXT2_MOUNT_##opt
index 79c0faf..7e05018 100644 (file)
@@ -1513,6 +1513,11 @@ extern ssize_t xip_file_aio_write(struct kiocb *iocb, const char __user *buf,
 extern ssize_t xip_file_writev(struct file *file, const struct iovec *iov,
                               unsigned long nr_segs, loff_t *ppos);
 extern int xip_truncate_page(struct address_space *mapping, loff_t from);
+#else
+static inline int xip_truncate_page(struct address_space *mapping, loff_t from)
+{
+       return 0;
+}
 #endif
 
 static inline void do_generic_file_read(struct file * filp, loff_t *ppos,