Merge branch 'linaro-android-3.10-lsk' of
authorAlex Shi <alex.shi@linaro.org>
Mon, 16 Mar 2015 07:52:47 +0000 (15:52 +0800)
committerAlex Shi <alex.shi@linaro.org>
Mon, 16 Mar 2015 07:52:47 +0000 (15:52 +0800)
git://android.git.linaro.org/kernel/linaro-android into linux-linaro-lsk-android

109 files changed:
Documentation/device-mapper/verity.txt
Documentation/filesystems/porting
Documentation/filesystems/proc.txt
Documentation/filesystems/squashfs.txt
android/configs/android-base.cfg
arch/alpha/kernel/osf_sys.c
arch/parisc/hpux/fs.c
arch/um/include/shared/frame_kern.h
arch/um/kernel/signal.c
arch/um/os-Linux/signal.c
arch/um/os-Linux/skas/process.c
arch/x86/um/signal.c
drivers/md/dm-verity.c
fs/adfs/super.c
fs/affs/super.c
fs/befs/linuxvfs.c
fs/btrfs/super.c
fs/cifs/cifsfs.c
fs/coda/dir.c
fs/coda/inode.c
fs/compat.c
fs/cramfs/inode.c
fs/debugfs/inode.c
fs/devpts/inode.c
fs/ecryptfs/file.c
fs/efs/super.c
fs/exportfs/expfs.c
fs/ext2/super.c
fs/ext3/super.c
fs/ext4/super.c
fs/f2fs/super.c
fs/fat/inode.c
fs/freevxfs/vxfs_super.c
fs/fuse/inode.c
fs/gfs2/export.c
fs/gfs2/super.c
fs/hfs/super.c
fs/hfsplus/super.c
fs/hpfs/super.c
fs/jffs2/super.c
fs/jfs/super.c
fs/minix/inode.c
fs/ncpfs/inode.c
fs/nfs/super.c
fs/nfsd/nfs4recover.c
fs/nfsd/vfs.c
fs/nilfs2/super.c
fs/ntfs/super.c
fs/ocfs2/super.c
fs/openpromfs/inode.c
fs/proc/base.c
fs/proc/root.c
fs/proc/task_mmu.c
fs/pstore/inode.c
fs/qnx4/inode.c
fs/qnx6/inode.c
fs/readdir.c
fs/reiserfs/super.c
fs/romfs/super.c
fs/squashfs/Kconfig
fs/squashfs/Makefile
fs/squashfs/block.c
fs/squashfs/cache.c
fs/squashfs/decompressor.c
fs/squashfs/decompressor.h
fs/squashfs/decompressor_multi.c [new file with mode: 0644]
fs/squashfs/decompressor_multi_percpu.c [new file with mode: 0644]
fs/squashfs/decompressor_single.c [new file with mode: 0644]
fs/squashfs/dir.c
fs/squashfs/file.c
fs/squashfs/file_cache.c [new file with mode: 0644]
fs/squashfs/file_direct.c [new file with mode: 0644]
fs/squashfs/lz4_wrapper.c [new file with mode: 0644]
fs/squashfs/lzo_wrapper.c
fs/squashfs/namei.c
fs/squashfs/page_actor.c [new file with mode: 0644]
fs/squashfs/page_actor.h [new file with mode: 0644]
fs/squashfs/squashfs.h
fs/squashfs/squashfs_fs.h
fs/squashfs/squashfs_fs_sb.h
fs/squashfs/super.c
fs/squashfs/xz_wrapper.c
fs/squashfs/zlib_wrapper.c
fs/sysv/inode.c
fs/ubifs/super.c
fs/udf/super.c
fs/ufs/super.c
fs/xfs/xfs_super.c
include/linux/fs.h
include/linux/lz4.h [new file with mode: 0644]
include/linux/mm.h
include/net/flow.h
include/net/net_namespace.h
lib/Kconfig
lib/Makefile
lib/lz4/Makefile [new file with mode: 0644]
lib/lz4/lz4_compress.c [new file with mode: 0644]
lib/lz4/lz4_decompress.c [new file with mode: 0644]
lib/lz4/lz4defs.h [new file with mode: 0644]
lib/lz4/lz4hc_compress.c [new file with mode: 0644]
mm/mlock.c
net/ipv4/fib_frontend.c
net/ipv4/fib_semantics.c
net/ipv4/ipmr.c
net/ipv4/netfilter/ipt_rpfilter.c
net/ipv4/ping.c
net/ipv6/ip6mr.c
net/ipv6/ping.c
net/ipv6/route.c

index 9884681535ee36bf03a7d7baaa54abb36360d53d..2929f6b1ccf12a363e4b01e223aa6aa50856aaaf 100644 (file)
@@ -10,7 +10,7 @@ Construction Parameters
     <version> <dev> <hash_dev>
     <data_block_size> <hash_block_size>
     <num_data_blocks> <hash_start_block>
-    <algorithm> <digest> <salt>
+    <algorithm> <digest> <salt> <mode>
 
 <version>
     This is the type of the on-disk hash format.
@@ -62,6 +62,16 @@ Construction Parameters
 <salt>
     The hexadecimal encoding of the salt value.
 
+<mode>
+    Optional. The mode of operation.
+
+    0 is the normal mode of operation where a corrupted block will result in an
+      I/O error.
+
+    1 is logging mode where corrupted blocks are logged and a uevent is sent to
+      notify user space.
+
+
 Theory of operation
 ===================
 
index 4db22f6491e026fee3ea01da970404c601a60684..85a4a033bae7815e422545273f6826c6e0e36cd8 100644 (file)
@@ -445,3 +445,6 @@ object doesn't exist.  It's remote/distributed ones that might care...
 [mandatory]
        FS_REVAL_DOT is gone; if you used to have it, add ->d_weak_revalidate()
 in your dentry operations instead.
+--
+[mandatory]
+       vfs_readdir() is gone; switch to iterate_dir() instead
index 7ce8ef7d764a8e4c128794085f98a09b80189cc0..be35913d282b8bcb5cd8b090894b15bad79f614d 100644 (file)
@@ -490,6 +490,10 @@ To clear the bits for the file mapped pages associated with the process
     > echo 3 > /proc/PID/clear_refs
 Any other value written to /proc/PID/clear_refs will have no effect.
 
+To reset the peak resident set size ("high water mark") to the process's
+current value:
+    > echo 5 > /proc/PID/clear_refs
+
 The /proc/pid/pagemap gives the PFN, which can be used to find the pageflags
 using /proc/kpageflags and number of times a page is mapped using
 /proc/kpagecount. For detailed explanation, see Documentation/vm/pagemap.txt.
index 403c090aca39954d378809b55b0585f74085422c..e5274f84dc5666b7e8e7d981fa4935d7cdc90b92 100644 (file)
@@ -2,10 +2,10 @@ SQUASHFS 4.0 FILESYSTEM
 =======================
 
 Squashfs is a compressed read-only filesystem for Linux.
-It uses zlib/lzo/xz compression to compress files, inodes and directories.
-Inodes in the system are very small and all blocks are packed to minimise
-data overhead. Block sizes greater than 4K are supported up to a maximum
-of 1Mbytes (default block size 128K).
+It uses zlib, lz4, lzo, or xz compression to compress files, inodes and
+directories.  Inodes in the system are very small and all blocks are packed to
+minimise data overhead. Block sizes greater than 4K are supported up to a
+maximum of 1Mbytes (default block size 128K).
 
 Squashfs is intended for general read-only filesystem use, for archival
 use (i.e. in cases where a .tar.gz file may be used), and in constrained
index d8503e450957b18b3c9971e3c2634b8841ef5fa1..6c08830ae6edfb2029a9a104994cb4300b21d4a3 100644 (file)
@@ -1,4 +1,6 @@
 #  KEEP ALPHABETICALLY SORTED
+# CONFIG_DEVKMEM is not set
+# CONFIG_DEVMEM is not set
 # CONFIG_INET_LRO is not set
 # CONFIG_MODULES is not set
 # CONFIG_OABI_COMPAT is not set
index b9e37ad6fa19ca58b762ab0337d0acd8ea6e30f3..1402fcc11c2c0443d18d59e21192f5af50ecb1c2 100644 (file)
@@ -96,6 +96,7 @@ struct osf_dirent {
 };
 
 struct osf_dirent_callback {
+       struct dir_context ctx;
        struct osf_dirent __user *dirent;
        long __user *basep;
        unsigned int count;
@@ -146,17 +147,17 @@ SYSCALL_DEFINE4(osf_getdirentries, unsigned int, fd,
 {
        int error;
        struct fd arg = fdget(fd);
-       struct osf_dirent_callback buf;
+       struct osf_dirent_callback buf = {
+               .ctx.actor = osf_filldir,
+               .dirent = dirent,
+               .basep = basep,
+               .count = count
+       };
 
        if (!arg.file)
                return -EBADF;
 
-       buf.dirent = dirent;
-       buf.basep = basep;
-       buf.count = count;
-       buf.error = 0;
-
-       error = vfs_readdir(arg.file, osf_filldir, &buf);
+       error = iterate_dir(arg.file, &buf.ctx);
        if (error >= 0)
                error = buf.error;
        if (count != buf.count)
index 838b479a42c4ebf0ea2a1e186a8f08891de37a2e..88d0962de65a8ca97c877b90dfb0b9ac59b0bfac 100644 (file)
@@ -60,6 +60,7 @@ struct hpux_dirent {
 };
 
 struct getdents_callback {
+       struct dir_context ctx;
        struct hpux_dirent __user *current_dir;
        struct hpux_dirent __user *previous;
        int count;
@@ -110,24 +111,23 @@ int hpux_getdents(unsigned int fd, struct hpux_dirent __user *dirent, unsigned i
 {
        struct fd arg;
        struct hpux_dirent __user * lastdirent;
-       struct getdents_callback buf;
+       struct getdents_callback buf = {
+               .ctx.actor = filldir,
+               .current_dir = dirent,
+               .count = count
+       };
        int error;
 
        arg = fdget(fd);
        if (!arg.file)
                return -EBADF;
 
-       buf.current_dir = dirent;
-       buf.previous = NULL;
-       buf.count = count;
-       buf.error = 0;
-
-       error = vfs_readdir(arg.file, filldir, &buf);
+       error = iterate_dir(arg.file, &buf.ctx);
        if (error >= 0)
                error = buf.error;
        lastdirent = buf.previous;
        if (lastdirent) {
-               if (put_user(arg.file->f_pos, &lastdirent->d_off))
+               if (put_user(buf.ctx.pos, &lastdirent->d_off))
                        error = -EFAULT;
                else
                        error = count - buf.count;
index e584e40ee8320f62e82e67abe6230a40ac75b19d..f2ca5702a4e2ae2b418382712c93aeed03736450 100644 (file)
@@ -6,13 +6,13 @@
 #ifndef __FRAME_KERN_H_
 #define __FRAME_KERN_H_
 
-extern int setup_signal_stack_sc(unsigned long stack_top, int sig, 
+extern int setup_signal_stack_sc(unsigned long stack_top, int sig,
                                 struct k_sigaction *ka,
-                                struct pt_regs *regs, 
+                                struct pt_regs *regs,
                                 sigset_t *mask);
-extern int setup_signal_stack_si(unsigned long stack_top, int sig, 
+extern int setup_signal_stack_si(unsigned long stack_top, int sig,
                                 struct k_sigaction *ka,
-                                struct pt_regs *regs, siginfo_t *info, 
+                                struct pt_regs *regs, struct siginfo *info,
                                 sigset_t *mask);
 
 #endif
index 3e831b3fd07bb4f547db12eadb6ccecb99df86c9..f57e02e7910f66a6346d7454f7fd612ec6d0735f 100644 (file)
@@ -19,7 +19,7 @@ EXPORT_SYMBOL(unblock_signals);
  * OK, we're invoking a handler
  */
 static void handle_signal(struct pt_regs *regs, unsigned long signr,
-                        struct k_sigaction *ka, siginfo_t *info)
+                        struct k_sigaction *ka, struct siginfo *info)
 {
        sigset_t *oldset = sigmask_to_save();
        int singlestep = 0;
@@ -71,7 +71,7 @@ static void handle_signal(struct pt_regs *regs, unsigned long signr,
 static int kern_do_signal(struct pt_regs *regs)
 {
        struct k_sigaction ka_copy;
-       siginfo_t info;
+       struct siginfo info;
        int sig, handled_sig = 0;
 
        while ((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0) {
index 9d9f1b4bf8269d89af35a08b5e24064337a410e4..905924b773d345b8f49de2905e62a546f6461cd9 100644 (file)
@@ -25,7 +25,7 @@ void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = {
        [SIGIO]         = sigio_handler,
        [SIGVTALRM]     = timer_handler };
 
-static void sig_handler_common(int sig, siginfo_t *si, mcontext_t *mc)
+static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc)
 {
        struct uml_pt_regs r;
        int save_errno = errno;
@@ -61,7 +61,7 @@ static void sig_handler_common(int sig, siginfo_t *si, mcontext_t *mc)
 static int signals_enabled;
 static unsigned int signals_pending;
 
-void sig_handler(int sig, siginfo_t *si, mcontext_t *mc)
+void sig_handler(int sig, struct siginfo *si, mcontext_t *mc)
 {
        int enabled;
 
@@ -120,7 +120,7 @@ void set_sigstack(void *sig_stack, int size)
                panic("enabling signal stack failed, errno = %d\n", errno);
 }
 
-static void (*handlers[_NSIG])(int sig, siginfo_t *si, mcontext_t *mc) = {
+static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = {
        [SIGSEGV] = sig_handler,
        [SIGBUS] = sig_handler,
        [SIGILL] = sig_handler,
@@ -162,7 +162,7 @@ static void hard_handler(int sig, siginfo_t *si, void *p)
                while ((sig = ffs(pending)) != 0){
                        sig--;
                        pending &= ~(1 << sig);
-                       (*handlers[sig])(sig, si, mc);
+                       (*handlers[sig])(sig, (struct siginfo *)si, mc);
                }
 
                /*
index 4625949bf1e4cd4ee9c424aa3eab1bfec6458ff7..908579f2b0ab14cf464777c0a45a37eb849d341f 100644 (file)
@@ -409,7 +409,7 @@ void userspace(struct uml_pt_regs *regs)
                if (WIFSTOPPED(status)) {
                        int sig = WSTOPSIG(status);
 
-                       ptrace(PTRACE_GETSIGINFO, pid, 0, &si);
+                       ptrace(PTRACE_GETSIGINFO, pid, 0, (struct siginfo *)&si);
 
                        switch (sig) {
                        case SIGSEGV:
@@ -417,7 +417,7 @@ void userspace(struct uml_pt_regs *regs)
                                    !ptrace_faultinfo) {
                                        get_skas_faultinfo(pid,
                                                           &regs->faultinfo);
-                                       (*sig_info[SIGSEGV])(SIGSEGV, &si,
+                                       (*sig_info[SIGSEGV])(SIGSEGV, (struct siginfo *)&si,
                                                             regs);
                                }
                                else handle_segv(pid, regs);
@@ -426,14 +426,14 @@ void userspace(struct uml_pt_regs *regs)
                                handle_trap(pid, regs, local_using_sysemu);
                                break;
                        case SIGTRAP:
-                               relay_signal(SIGTRAP, &si, regs);
+                               relay_signal(SIGTRAP, (struct siginfo *)&si, regs);
                                break;
                        case SIGVTALRM:
                                now = os_nsecs();
                                if (now < nsecs)
                                        break;
                                block_signals();
-                               (*sig_info[sig])(sig, &si, regs);
+                               (*sig_info[sig])(sig, (struct siginfo *)&si, regs);
                                unblock_signals();
                                nsecs = timer.it_value.tv_sec *
                                        UM_NSEC_PER_SEC +
@@ -447,7 +447,7 @@ void userspace(struct uml_pt_regs *regs)
                        case SIGFPE:
                        case SIGWINCH:
                                block_signals();
-                               (*sig_info[sig])(sig, &si, regs);
+                               (*sig_info[sig])(sig, (struct siginfo *)&si, regs);
                                unblock_signals();
                                break;
                        default:
index ae7319db18ee36d1e8aec541e30bbbb99cc77d08..5e04a1c899fa6563cd8809c84dd41cf02b1a950e 100644 (file)
@@ -508,7 +508,6 @@ int setup_signal_stack_si(unsigned long stack_top, int sig,
 {
        struct rt_sigframe __user *frame;
        int err = 0;
-       struct task_struct *me = current;
 
        frame = (struct rt_sigframe __user *)
                round_down(stack_top - sizeof(struct rt_sigframe), 16);
index 0d2e812d424bff13aeb2ae7905d6ff026ffec581..fe07d915e20b764106c37d227376c2480ba40bb9 100644 (file)
 
 #include <linux/module.h>
 #include <linux/device-mapper.h>
+#include <linux/reboot.h>
 #include <crypto/hash.h>
 
 #define DM_MSG_PREFIX                  "verity"
 
+#define DM_VERITY_ENV_LENGTH           42
+#define DM_VERITY_ENV_VAR_NAME         "VERITY_ERR_BLOCK_NR"
+
 #define DM_VERITY_IO_VEC_INLINE                16
 #define DM_VERITY_MEMPOOL_SIZE         4
 #define DM_VERITY_DEFAULT_PREFETCH_SIZE        262144
 
 #define DM_VERITY_MAX_LEVELS           63
+#define DM_VERITY_MAX_CORRUPTED_ERRS   100
 
 static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
 
 module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR);
 
+enum verity_mode {
+       DM_VERITY_MODE_EIO = 0,
+       DM_VERITY_MODE_LOGGING = 1,
+       DM_VERITY_MODE_RESTART = 2
+};
+
+enum verity_block_type {
+       DM_VERITY_BLOCK_TYPE_DATA,
+       DM_VERITY_BLOCK_TYPE_METADATA
+};
+
 struct dm_verity {
        struct dm_dev *data_dev;
        struct dm_dev *hash_dev;
@@ -54,6 +70,8 @@ struct dm_verity {
        unsigned digest_size;   /* digest size for the current hash algorithm */
        unsigned shash_descsize;/* the size of temporary space for crypto */
        int hash_failed;        /* set to 1 if hash of any block failed */
+       enum verity_mode mode;  /* mode for handling verification errors */
+       unsigned corrupted_errs;/* Number of errors for corrupted blocks */
 
        mempool_t *vec_mempool; /* mempool of bio vector */
 
@@ -179,6 +197,54 @@ static void verity_hash_at_level(struct dm_verity *v, sector_t block, int level,
                *offset = idx << (v->hash_dev_block_bits - v->hash_per_block_bits);
 }
 
+/*
+ * Handle verification errors.
+ */
+static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
+                                unsigned long long block)
+{
+       char verity_env[DM_VERITY_ENV_LENGTH];
+       char *envp[] = { verity_env, NULL };
+       const char *type_str = "";
+       struct mapped_device *md = dm_table_get_md(v->ti->table);
+
+       if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS)
+               goto out;
+
+       ++v->corrupted_errs;
+
+       switch (type) {
+       case DM_VERITY_BLOCK_TYPE_DATA:
+               type_str = "data";
+               break;
+       case DM_VERITY_BLOCK_TYPE_METADATA:
+               type_str = "metadata";
+               break;
+       default:
+               BUG();
+       }
+
+       DMERR_LIMIT("%s: %s block %llu is corrupted", v->data_dev->name,
+                type_str, block);
+
+       if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS)
+               DMERR("%s: reached maximum errors", v->data_dev->name);
+
+       snprintf(verity_env, DM_VERITY_ENV_LENGTH, "%s=%d,%llu",
+               DM_VERITY_ENV_VAR_NAME, type, block);
+
+       kobject_uevent_env(&disk_to_dev(dm_disk(md))->kobj, KOBJ_CHANGE, envp);
+
+out:
+       if (v->mode == DM_VERITY_MODE_LOGGING)
+               return 0;
+
+       if (v->mode == DM_VERITY_MODE_RESTART)
+               kernel_restart("dm-verity device corrupted");
+
+       return 1;
+}
+
 /*
  * Verify hash of a metadata block pertaining to the specified data block
  * ("block" argument) at a specified level ("level" argument).
@@ -256,11 +322,13 @@ static int verity_verify_level(struct dm_verity_io *io, sector_t block,
                        goto release_ret_r;
                }
                if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
-                       DMERR_LIMIT("metadata block %llu is corrupted",
-                               (unsigned long long)hash_block);
                        v->hash_failed = 1;
-                       r = -EIO;
-                       goto release_ret_r;
+
+                       if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_METADATA,
+                                             hash_block)) {
+                               r = -EIO;
+                               goto release_ret_r;
+                       }
                } else
                        aux->hash_verified = 1;
        }
@@ -377,10 +445,11 @@ test_block_hash:
                        return r;
                }
                if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
-                       DMERR_LIMIT("data block %llu is corrupted",
-                               (unsigned long long)(io->block + b));
                        v->hash_failed = 1;
-                       return -EIO;
+
+                       if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
+                                             io->block + b))
+                               return -EIO;
                }
        }
        BUG_ON(vector != io->io_vec_size);
@@ -689,8 +758,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
                goto bad;
        }
 
-       if (argc != 10) {
-               ti->error = "Invalid argument count: exactly 10 arguments required";
+       if (argc < 10 || argc > 11) {
+               ti->error = "Invalid argument count: 10-11 arguments required";
                r = -EINVAL;
                goto bad;
        }
@@ -811,6 +880,17 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
                }
        }
 
+       if (argc > 10) {
+               if (sscanf(argv[10], "%d%c", &num, &dummy) != 1 ||
+                       num < DM_VERITY_MODE_EIO ||
+                       num > DM_VERITY_MODE_RESTART) {
+                       ti->error = "Invalid mode";
+                       r = -EINVAL;
+                       goto bad;
+               }
+               v->mode = num;
+       }
+
        v->hash_per_block_bits =
                fls((1 << v->hash_dev_block_bits) / v->digest_size) - 1;
 
index 0ff4bae2c2a2c2372a75bc5486a4920672aef0bf..479ef5a9c5d36928dea13d11cc48be8bb8775546 100644 (file)
@@ -213,6 +213,7 @@ static int parse_options(struct super_block *sb, char *options)
 
 static int adfs_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        *flags |= MS_NODIRATIME;
        return parse_options(sb, data);
 }
index 45161a832bbc9e4aa2d7f6b72a950273e0af7062..01c6e07acbffe63ec4100c511ce888ef343a22a9 100644 (file)
@@ -549,6 +549,7 @@ affs_remount(struct super_block *sb, int *flags, char *data)
 
        pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data);
 
+       sync_filesystem(sb);
        *flags |= MS_NODIRATIME;
 
        memcpy(volume, sbi->s_volume, 32);
index f95dddced968f6f4509f1536b80462baa1c4393b..7192a7e9937356c4d61d5a9303cad85fd696262c 100644 (file)
@@ -908,6 +908,7 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
 static int
 befs_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        if (!(*flags & MS_RDONLY))
                return -EINVAL;
        return 0;
index f0857e092a3cb1af485604850052440579a1bbf1..461731c7fa3a3198c3756c748192521bd3a85c3c 100644 (file)
@@ -1239,6 +1239,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
        unsigned int old_metadata_ratio = fs_info->metadata_ratio;
        int ret;
 
+       sync_filesystem(sb);
        btrfs_remount_prepare(fs_info);
 
        ret = btrfs_parse_options(root, data);
index 3752b9f6d9e46e90876b18f4d14527f9262baff6..0d7daf714c4bf59727edfd79ffbf7388e20cf15f 100644 (file)
@@ -521,6 +521,7 @@ static int cifs_show_stats(struct seq_file *s, struct dentry *root)
 
 static int cifs_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        *flags |= MS_NODIRATIME;
        return 0;
 }
index b7d3a05c062c0517bdcee3aedf01619f60bddc7a..fc66861b359855d46b4fbdfae5a4fa1889906629 100644 (file)
@@ -391,8 +391,7 @@ static int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir)
        if (!host_file->f_op)
                return -ENOTDIR;
 
-       if (host_file->f_op->readdir)
-       {
+       if (host_file->f_op->readdir) {
                /* potemkin case: we were handed a directory inode.
                 * We can't use vfs_readdir because we have to keep the file
                 * position in sync between the coda_file and the host_file.
@@ -410,8 +409,20 @@ static int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir)
 
                coda_file->f_pos = host_file->f_pos;
                mutex_unlock(&host_inode->i_mutex);
-       }
-       else /* Venus: we must read Venus dirents from a file */
+       } else if (host_file->f_op->iterate) {
+               struct inode *host_inode = file_inode(host_file);
+               struct dir_context *ctx = buf;
+
+               mutex_lock(&host_inode->i_mutex);
+               ret = -ENOENT;
+               if (!IS_DEADDIR(host_inode)) {
+                       ret = host_file->f_op->iterate(host_file, ctx);
+                       file_accessed(host_file);
+               }
+               mutex_unlock(&host_inode->i_mutex);
+
+               coda_file->f_pos = ctx->pos;
+       } else /* Venus: we must read Venus dirents from a file */
                ret = coda_venus_readdir(coda_file, buf, filldir);
 
        return ret;
index 4dcc0d81a7aa511069869141d0d1470800a82e54..0aa4c4d75ec6d78fc8165ef3e7bec1ddf93cefe0 100644 (file)
@@ -96,6 +96,7 @@ void coda_destroy_inodecache(void)
 
 static int coda_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        *flags |= MS_NOATIME;
        return 0;
 }
index fc3b55dce184a2637fcbf14d1dc313e61714ad5d..6af20de2c1a3c29d5cc7c251d8fb3fa2182de445 100644 (file)
@@ -832,6 +832,7 @@ struct compat_old_linux_dirent {
 };
 
 struct compat_readdir_callback {
+       struct dir_context ctx;
        struct compat_old_linux_dirent __user *dirent;
        int result;
 };
@@ -873,15 +874,15 @@ asmlinkage long compat_sys_old_readdir(unsigned int fd,
 {
        int error;
        struct fd f = fdget(fd);
-       struct compat_readdir_callback buf;
+       struct compat_readdir_callback buf = {
+               .ctx.actor = compat_fillonedir,
+               .dirent = dirent
+       };
 
        if (!f.file)
                return -EBADF;
 
-       buf.result = 0;
-       buf.dirent = dirent;
-
-       error = vfs_readdir(f.file, compat_fillonedir, &buf);
+       error = iterate_dir(f.file, &buf.ctx);
        if (buf.result)
                error = buf.result;
 
@@ -897,6 +898,7 @@ struct compat_linux_dirent {
 };
 
 struct compat_getdents_callback {
+       struct dir_context ctx;
        struct compat_linux_dirent __user *current_dir;
        struct compat_linux_dirent __user *previous;
        int count;
@@ -951,7 +953,11 @@ asmlinkage long compat_sys_getdents(unsigned int fd,
 {
        struct fd f;
        struct compat_linux_dirent __user * lastdirent;
-       struct compat_getdents_callback buf;
+       struct compat_getdents_callback buf = {
+               .ctx.actor = compat_filldir,
+               .current_dir = dirent,
+               .count = count
+       };
        int error;
 
        if (!access_ok(VERIFY_WRITE, dirent, count))
@@ -961,17 +967,12 @@ asmlinkage long compat_sys_getdents(unsigned int fd,
        if (!f.file)
                return -EBADF;
 
-       buf.current_dir = dirent;
-       buf.previous = NULL;
-       buf.count = count;
-       buf.error = 0;
-
-       error = vfs_readdir(f.file, compat_filldir, &buf);
+       error = iterate_dir(f.file, &buf.ctx);
        if (error >= 0)
                error = buf.error;
        lastdirent = buf.previous;
        if (lastdirent) {
-               if (put_user(f.file->f_pos, &lastdirent->d_off))
+               if (put_user(buf.ctx.pos, &lastdirent->d_off))
                        error = -EFAULT;
                else
                        error = count - buf.count;
@@ -983,6 +984,7 @@ asmlinkage long compat_sys_getdents(unsigned int fd,
 #ifndef __ARCH_OMIT_COMPAT_SYS_GETDENTS64
 
 struct compat_getdents_callback64 {
+       struct dir_context ctx;
        struct linux_dirent64 __user *current_dir;
        struct linux_dirent64 __user *previous;
        int count;
@@ -1036,7 +1038,11 @@ asmlinkage long compat_sys_getdents64(unsigned int fd,
 {
        struct fd f;
        struct linux_dirent64 __user * lastdirent;
-       struct compat_getdents_callback64 buf;
+       struct compat_getdents_callback64 buf = {
+               .ctx.actor = compat_filldir64,
+               .current_dir = dirent,
+               .count = count
+       };
        int error;
 
        if (!access_ok(VERIFY_WRITE, dirent, count))
@@ -1046,17 +1052,12 @@ asmlinkage long compat_sys_getdents64(unsigned int fd,
        if (!f.file)
                return -EBADF;
 
-       buf.current_dir = dirent;
-       buf.previous = NULL;
-       buf.count = count;
-       buf.error = 0;
-
-       error = vfs_readdir(f.file, compat_filldir64, &buf);
+       error = iterate_dir(f.file, &buf.ctx);
        if (error >= 0)
                error = buf.error;
        lastdirent = buf.previous;
        if (lastdirent) {
-               typeof(lastdirent->d_off) d_off = f.file->f_pos;
+               typeof(lastdirent->d_off) d_off = buf.ctx.pos;
                if (__put_user_unaligned(d_off, &lastdirent->d_off))
                        error = -EFAULT;
                else
index 35b1c7bd18b758a30fc03fd4267151d18fcb2281..c0148585670d29968cace0dd342f05a7feb6660e 100644 (file)
@@ -227,6 +227,7 @@ static void cramfs_put_super(struct super_block *sb)
 
 static int cramfs_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        *flags |= MS_RDONLY;
        return 0;
 }
index c7c83ff0f752ce6a86d6e42a420e34ed996ae8ca..50c080a38547420a340816cc87f01d7768637437 100644 (file)
@@ -218,6 +218,7 @@ static int debugfs_remount(struct super_block *sb, int *flags, char *data)
        int err;
        struct debugfs_fs_info *fsi = sb->s_fs_info;
 
+       sync_filesystem(sb);
        err = debugfs_parse_options(data, &fsi->mount_opts);
        if (err)
                goto fail;
index a726b9f29cb71735eb99ada9367f4348da45eb3d..c71038079b47831bb14b58e375592044906091c3 100644 (file)
@@ -313,6 +313,7 @@ static int devpts_remount(struct super_block *sb, int *flags, char *data)
        struct pts_fs_info *fsi = DEVPTS_SB(sb);
        struct pts_mount_opts *opts = &fsi->mount_opts;
 
+       sync_filesystem(sb);
        err = parse_mount_options(data, PARSE_REMOUNT, opts);
 
        /*
index 9ff3664bb3ea460d139982f49d65378b4f16bf58..4912bf47226c952536d6962ba1e2680fce3267ec 100644 (file)
@@ -68,6 +68,7 @@ static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb,
 }
 
 struct ecryptfs_getdents_callback {
+       struct dir_context ctx;
        void *dirent;
        struct dentry *dentry;
        filldir_t filldir;
@@ -115,18 +116,19 @@ static int ecryptfs_readdir(struct file *file, void *dirent, filldir_t filldir)
        int rc;
        struct file *lower_file;
        struct inode *inode;
-       struct ecryptfs_getdents_callback buf;
+       struct ecryptfs_getdents_callback buf = {
+               .dirent = dirent,
+               .dentry = file->f_path.dentry,
+               .filldir = filldir,
+               .filldir_called = 0,
+               .entries_written = 0,
+               .ctx.actor = ecryptfs_filldir
+       };
 
        lower_file = ecryptfs_file_to_lower(file);
        lower_file->f_pos = file->f_pos;
        inode = file_inode(file);
-       memset(&buf, 0, sizeof(buf));
-       buf.dirent = dirent;
-       buf.dentry = file->f_path.dentry;
-       buf.filldir = filldir;
-       buf.filldir_called = 0;
-       buf.entries_written = 0;
-       rc = vfs_readdir(lower_file, ecryptfs_filldir, (void *)&buf);
+       rc = iterate_dir(lower_file, &buf.ctx);
        file->f_pos = lower_file->f_pos;
        if (rc < 0)
                goto out;
index c6f57a74a559da265bc8f062eab06488d8d7c00a..4709692cd7a048684f7f7ee512383445dcc7c01e 100644 (file)
@@ -113,6 +113,7 @@ static void efs_put_super(struct super_block *s)
 
 static int efs_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        *flags |= MS_RDONLY;
        return 0;
 }
index 262fc99409824327b316af89a945c65cae8c9888..43b448ddc3dc3e479e457d75720cebb6e512e22f 100644 (file)
@@ -212,6 +212,7 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
 }
 
 struct getdents_callback {
+       struct dir_context ctx;
        char *name;             /* name that was found. It already points to a
                                   buffer NAME_MAX+1 is size */
        unsigned long ino;      /* the inum we are looking for */
@@ -254,7 +255,11 @@ static int get_name(const struct path *path, char *name, struct dentry *child)
        struct inode *dir = path->dentry->d_inode;
        int error;
        struct file *file;
-       struct getdents_callback buffer;
+       struct getdents_callback buffer = {
+               .ctx.actor = filldir_one,
+               .name = name,
+               .ino = child->d_inode->i_ino
+       };
 
        error = -ENOTDIR;
        if (!dir || !S_ISDIR(dir->i_mode))
@@ -271,17 +276,14 @@ static int get_name(const struct path *path, char *name, struct dentry *child)
                goto out;
 
        error = -EINVAL;
-       if (!file->f_op->readdir)
+       if (!file->f_op->readdir && !file->f_op->iterate)
                goto out_close;
 
-       buffer.name = name;
-       buffer.ino = child->d_inode->i_ino;
-       buffer.found = 0;
        buffer.sequence = 0;
        while (1) {
                int old_seq = buffer.sequence;
 
-               error = vfs_readdir(file, filldir_one, &buffer);
+               error = iterate_dir(file, &buffer.ctx);
                if (buffer.found) {
                        error = 0;
                        break;
index 20d6697bd6386560a679dda5e8b8592a65d63798..d260115c0350e91be14eca760b813032e5325fca 100644 (file)
@@ -1254,6 +1254,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
        unsigned long old_sb_flags;
        int err;
 
+       sync_filesystem(sb);
        spin_lock(&sbi->s_lock);
 
        /* Store the old options */
index 882d4bdfd4283385378a94013973392155b9de6f..b1e30be1945b5a1ba5fc162647c3e616074d61ae 100644 (file)
@@ -2588,6 +2588,8 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
        int i;
 #endif
 
+       sync_filesystem(sb);
+
        /* Store the original options */
        old_sb_flags = sb->s_flags;
        old_opts.s_mount_opt = sbi->s_mount_opt;
index 21a0b43a7d3187507eec5c333b9d42d2df1986e9..913a6b27922060a6c5e4c342ce5a1b8e65a648b7 100644 (file)
@@ -4622,6 +4622,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 #endif
        char *orig_data = kstrdup(data, GFP_KERNEL);
 
+       sync_filesystem(sb);
+
        /* Store the original options */
        old_sb_flags = sb->s_flags;
        old_opts.s_mount_opt = sbi->s_mount_opt;
index 8555f7df82c796720c2c85ce0bd0af6248efa1c2..03ab8b830940b85b51be28eba51adf5621b740be 100644 (file)
@@ -239,7 +239,6 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
        if (test_opt(sbi, DISABLE_EXT_IDENTIFY))
                seq_puts(seq, ",disable_ext_identify");
 
-       seq_printf(seq, ",active_logs=%u", sbi->active_logs);
 
        return 0;
 }
index a14dd4c0528a90a0f9dce633078ecd160113d59a..ebdc6656c067232c523b3a6fd549a47c79a05439 100644 (file)
@@ -632,6 +632,8 @@ static int fat_remount(struct super_block *sb, int *flags, char *data)
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
        *flags |= MS_NODIRATIME | (sbi->options.isvfat ? 0 : MS_NOATIME);
 
+       sync_filesystem(sb);
+
        /* make sure we update state on remount. */
        new_rdonly = *flags & MS_RDONLY;
        if (new_rdonly != (sb->s_flags & MS_RDONLY)) {
index e37eb274e492a9dfd36d4f717d363b6932ae90b5..7ca8c75d50d3fdc5f9079599465e04f99058d45d 100644 (file)
@@ -124,6 +124,7 @@ vxfs_statfs(struct dentry *dentry, struct kstatfs *bufp)
 
 static int vxfs_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        *flags |= MS_RDONLY;
        return 0;
 }
index 39a986e1da9eff707e2e5c5fc543627d3f116d97..8aabbe2a398968bb806d5610c3e26005980b56dd 100644 (file)
@@ -135,6 +135,7 @@ static void fuse_evict_inode(struct inode *inode)
 
 static int fuse_remount_fs(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        if (*flags & MS_MANDLOCK)
                return -EINVAL;
 
index 9973df4ff565b6642247e44f854307a0d696f06a..7102c7a5af4dc777991c899ca043e19e6be4a68f 100644 (file)
@@ -88,7 +88,10 @@ static int gfs2_get_name(struct dentry *parent, char *name,
        struct inode *dir = parent->d_inode;
        struct inode *inode = child->d_inode;
        struct gfs2_inode *dip, *ip;
-       struct get_name_filldir gnfd;
+       struct get_name_filldir gnfd = {
+               .ctx.actor = get_name_filldir,
+               .name = name
+       };
        struct gfs2_holder gh;
        u64 offset = 0;
        int error;
@@ -106,7 +109,6 @@ static int gfs2_get_name(struct dentry *parent, char *name,
        *name = 0;
        gnfd.inum.no_addr = ip->i_no_addr;
        gnfd.inum.no_formal_ino = ip->i_no_formal_ino;
-       gnfd.name = name;
 
        error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &gh);
        if (error)
index e5639dec66c49dc7361ff0ed79828301031b675d..db7fff5a29c15b42de84674578a803bbe863daa7 100644 (file)
@@ -1142,6 +1142,8 @@ static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data)
        struct gfs2_tune *gt = &sdp->sd_tune;
        int error;
 
+       sync_filesystem(sb);
+
        spin_lock(&gt->gt_spin);
        args.ar_commit = gt->gt_logd_secs;
        args.ar_quota_quantum = gt->gt_quota_quantum;
index 2d2039e754cdb8e7182053ea5666ed64d14ca488..eee7206c38d18e1a3d3b1697fd429763e3fe9f5b 100644 (file)
@@ -112,6 +112,7 @@ static int hfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 
 static int hfs_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        *flags |= MS_NODIRATIME;
        if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
                return 0;
index 4c4d142cf890a4cb7949467150d9aeb357fd6942..1b9414f701e666273ffbadf47cbc66dd8b488835 100644 (file)
@@ -323,6 +323,7 @@ static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
 
 static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
                return 0;
        if (!(*flags & MS_RDONLY)) {
index 962e90c37aec64ec95773467f146951995c9adc1..d37f6ff8e07f6fe076e8541d11522ea607eba1e6 100644 (file)
@@ -395,6 +395,8 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
        struct hpfs_sb_info *sbi = hpfs_sb(s);
        char *new_opts = kstrdup(data, GFP_KERNEL);
        
+       sync_filesystem(s);
+
        *flags |= MS_NOATIME;
        
        hpfs_lock(s);
index 0defb1cc2a3520d6f5f67cc880f91d0e9fe5b244..0918f0e2e26608467356235c4267c336cccee87c 100644 (file)
@@ -243,6 +243,7 @@ static int jffs2_remount_fs(struct super_block *sb, int *flags, char *data)
        struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
        int err;
 
+       sync_filesystem(sb);
        err = jffs2_parse_options(c, data);
        if (err)
                return -EINVAL;
index 788e0a9c1fb09cfb6d80ee65e1747310c7c840d0..b7486dafa1cda8121016284d9df9eb6d64c6700e 100644 (file)
@@ -413,6 +413,7 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data)
        int flag = JFS_SBI(sb)->flag;
        int ret;
 
+       sync_filesystem(sb);
        if (!parse_options(data, sb, &newLVSize, &flag)) {
                return -EINVAL;
        }
index df122496f32821bd145490800467d41d02c87f63..a54d08865e598883a00f6eecf6988036e9f2afd9 100644 (file)
@@ -123,6 +123,7 @@ static int minix_remount (struct super_block * sb, int * flags, char * data)
        struct minix_sb_info * sbi = minix_sb(sb);
        struct minix_super_block * ms;
 
+       sync_filesystem(sb);
        ms = sbi->s_ms;
        if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
                return 0;
index 26910c8154da1be65c0318eb088d5d69d3e08baf..3f54348b926b807a343a1514f85d8204d94e7c4c 100644 (file)
@@ -99,6 +99,7 @@ static void destroy_inodecache(void)
 
 static int ncp_remount(struct super_block *sb, int *flags, char* data)
 {
+       sync_filesystem(sb);
        *flags |= MS_NODIRATIME;
        return 0;
 }
index 2d7525fbcf250225981ab521da638fc8f2a204d5..d85c1b8191459874b8e32cb98dfeb05a9a4e2c90 100644 (file)
@@ -2133,6 +2133,8 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
        struct nfs4_mount_data *options4 = (struct nfs4_mount_data *)raw_data;
        u32 nfsvers = nfss->nfs_client->rpc_ops->version;
 
+       sync_filesystem(sb);
+
        /*
         * Userspace mount programs that send binary options generally send
         * them populated with default values. We have no way to know which
index 4e9a21db867ae60afcc14a6ca362e978495c4a5c..105a3b080d1236c24afed6267354705fd8da5d94 100644 (file)
@@ -240,11 +240,16 @@ struct name_list {
        struct list_head list;
 };
 
+struct nfs4_dir_ctx {
+       struct dir_context ctx;
+       struct list_head names;
+};
+
 static int
 nfsd4_build_namelist(void *arg, const char *name, int namlen,
                loff_t offset, u64 ino, unsigned int d_type)
 {
-       struct list_head *names = arg;
+       struct nfs4_dir_ctx *ctx = arg;
        struct name_list *entry;
 
        if (namlen != HEXDIR_LEN - 1)
@@ -254,7 +259,7 @@ nfsd4_build_namelist(void *arg, const char *name, int namlen,
                return -ENOMEM;
        memcpy(entry->name, name, HEXDIR_LEN - 1);
        entry->name[HEXDIR_LEN - 1] = '\0';
-       list_add(&entry->list, names);
+       list_add(&entry->list, &ctx->names);
        return 0;
 }
 
@@ -263,7 +268,10 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn)
 {
        const struct cred *original_cred;
        struct dentry *dir = nn->rec_file->f_path.dentry;
-       LIST_HEAD(names);
+       struct nfs4_dir_ctx ctx = {
+               .ctx.actor = nfsd4_build_namelist,
+               .names = LIST_HEAD_INIT(ctx.names)
+       };
        int status;
 
        status = nfs4_save_creds(&original_cred);
@@ -276,11 +284,11 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn)
                return status;
        }
 
-       status = vfs_readdir(nn->rec_file, nfsd4_build_namelist, &names);
+       status = iterate_dir(nn->rec_file, &ctx.ctx);
        mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
-       while (!list_empty(&names)) {
+       while (!list_empty(&ctx.names)) {
                struct name_list *entry;
-               entry = list_entry(names.next, struct name_list, list);
+               entry = list_entry(ctx.names.next, struct name_list, list);
                if (!status) {
                        struct dentry *dentry;
                        dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1);
index 81325ba8660aedf61b9ddc39acd066154a1d9961..11224fa5ff25a3e349efe257c2ae36fce88e84b3 100644 (file)
@@ -1950,6 +1950,7 @@ struct buffered_dirent {
 };
 
 struct readdir_data {
+       struct dir_context ctx;
        char            *dirent;
        size_t          used;
        int             full;
@@ -1981,13 +1982,15 @@ static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen,
 static __be32 nfsd_buffered_readdir(struct file *file, filldir_t func,
                                    struct readdir_cd *cdp, loff_t *offsetp)
 {
-       struct readdir_data buf;
        struct buffered_dirent *de;
        int host_err;
        int size;
        loff_t offset;
+       struct readdir_data buf = {
+               .ctx.actor = nfsd_buffered_filldir,
+               .dirent = (void *)__get_free_page(GFP_KERNEL)
+       };
 
-       buf.dirent = (void *)__get_free_page(GFP_KERNEL);
        if (!buf.dirent)
                return nfserrno(-ENOMEM);
 
@@ -2001,7 +2004,7 @@ static __be32 nfsd_buffered_readdir(struct file *file, filldir_t func,
                buf.used = 0;
                buf.full = 0;
 
-               host_err = vfs_readdir(file, nfsd_buffered_filldir, &buf);
+               host_err = iterate_dir(file, &buf.ctx);
                if (buf.full)
                        host_err = 0;
 
index c7d1f9f18b094fb1f281fb0dfcb6b53328dc72fe..4b0a8d4a834501bf7cfba69e1ce4d5438807cbe4 100644 (file)
@@ -1114,6 +1114,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
        unsigned long old_mount_opt;
        int err;
 
+       sync_filesystem(sb);
        old_sb_flags = sb->s_flags;
        old_mount_opt = nilfs->ns_mount_opt;
 
index 82650d52d9168ee4f1e1b4282813afdaca5c7ec6..bd5610d482423a0ddf8efaf865f368d66d005e9f 100644 (file)
@@ -468,6 +468,8 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt)
 
        ntfs_debug("Entering with remount options string: %s", opt);
 
+       sync_filesystem(sb);
+
 #ifndef NTFS_RW
        /* For read-only compiled driver, enforce read-only flag. */
        *flags |= MS_RDONLY;
index 01b85165552b75f1cf2e17893bea1054eb19ff81..616bde667d8d29be41587939366886fa3d5bdaa2 100644 (file)
@@ -632,6 +632,8 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
        struct ocfs2_super *osb = OCFS2_SB(sb);
        u32 tmp;
 
+       sync_filesystem(sb);
+
        if (!ocfs2_parse_options(sb, data, &parsed_options, 1) ||
            !ocfs2_check_set_options(sb, &parsed_options)) {
                ret = -EINVAL;
index 75885ffde44e58a967799d3b461181e74bb8fd9c..f4026aba26f34c8da7251973fcf6a6fa3875d54d 100644 (file)
@@ -375,6 +375,7 @@ static struct inode *openprom_iget(struct super_block *sb, ino_t ino)
 
 static int openprom_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        *flags |= MS_NOATIME;
        return 0;
 }
index fb426d0bd322e2ab627fc390fe1f1f9c6fdb65ab..809d92a68a505e561087ae87e58a0397009c89be 100644 (file)
@@ -139,12 +139,6 @@ struct pid_entry {
                NULL, &proc_single_file_operations,     \
                { .proc_show = show } )
 
-/* ANDROID is for special files in /proc. */
-#define ANDROID(NAME, MODE, OTYPE)                     \
-       NOD(NAME, (S_IFREG|(MODE)),                     \
-               &proc_##OTYPE##_inode_operations,       \
-               &proc_##OTYPE##_operations, {})
-
 /*
  * Count the number of hardlinks for the pid_entry table, excluding the .
  * and .. links.
@@ -1006,35 +1000,6 @@ out:
        return err < 0 ? err : count;
 }
 
-static int oom_adjust_permission(struct inode *inode, int mask)
-{
-       uid_t uid;
-       struct task_struct *p;
-
-       p = get_proc_task(inode);
-       if(p) {
-               uid = task_uid(p);
-               put_task_struct(p);
-       }
-
-       /*
-        * System Server (uid == 1000) is granted access to oom_adj of all 
-        * android applications (uid > 10000) as and services (uid >= 1000)
-        */
-       if (p && (current_fsuid() == 1000) && (uid >= 1000)) {
-               if (inode->i_mode >> 6 & mask) {
-                       return 0;
-               }
-       }
-
-       /* Fall back to default. */
-       return generic_permission(inode, mask);
-}
-
-static const struct inode_operations proc_oom_adj_inode_operations = {
-       .permission     = oom_adjust_permission,
-};
-
 static const struct file_operations proc_oom_adj_operations = {
        .read           = oom_adj_read,
        .write          = oom_adj_write,
@@ -2784,8 +2749,8 @@ static const struct pid_entry tgid_base_stuff[] = {
        REG("cgroup",  S_IRUGO, proc_cgroup_operations),
 #endif
        INF("oom_score",  S_IRUGO, proc_oom_score),
-       ANDROID("oom_adj", S_IRUGO|S_IWUSR, oom_adj),
-       REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),
+       REG("oom_adj",    S_IRUSR, proc_oom_adj_operations),
+       REG("oom_score_adj", S_IRUSR, proc_oom_score_adj_operations),
 #ifdef CONFIG_AUDITSYSCALL
        REG("loginuid",   S_IWUSR|S_IRUGO, proc_loginuid_operations),
        REG("sessionid",  S_IRUGO, proc_sessionid_operations),
@@ -3141,8 +3106,8 @@ static const struct pid_entry tid_base_stuff[] = {
        REG("cgroup",  S_IRUGO, proc_cgroup_operations),
 #endif
        INF("oom_score", S_IRUGO, proc_oom_score),
-       REG("oom_adj",   S_IRUGO|S_IWUSR, proc_oom_adj_operations),
-       REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),
+       REG("oom_adj",   S_IRUSR, proc_oom_adj_operations),
+       REG("oom_score_adj", S_IRUSR, proc_oom_score_adj_operations),
 #ifdef CONFIG_AUDITSYSCALL
        REG("loginuid",  S_IWUSR|S_IRUGO, proc_loginuid_operations),
        REG("sessionid",  S_IRUGO, proc_sessionid_operations),
index 9459710c55aee0a287014eab85ab373cef3576f8..8ead812d989710593a4a130f31529284b6bb1494 100644 (file)
@@ -92,6 +92,8 @@ static int proc_parse_options(char *options, struct pid_namespace *pid)
 int proc_remount(struct super_block *sb, int *flags, char *data)
 {
        struct pid_namespace *pid = sb->s_fs_info;
+
+       sync_filesystem(sb);
        return !proc_parse_options(data, pid);
 }
 
index db17f98bc5647ee2f5076e1ec858126d1c94b1f2..154ebf8d7c1b5388ac81a7820712527784e5a021 100644 (file)
@@ -784,6 +784,7 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr,
 #define CLEAR_REFS_ALL 1
 #define CLEAR_REFS_ANON 2
 #define CLEAR_REFS_MAPPED 3
+#define CLEAR_REFS_MM_HIWATER_RSS 5
 
 static ssize_t clear_refs_write(struct file *file, const char __user *buf,
                                size_t count, loff_t *ppos)
@@ -803,7 +804,8 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf,
        rv = kstrtoint(strstrip(buffer), 10, &type);
        if (rv < 0)
                return rv;
-       if (type < CLEAR_REFS_ALL || type > CLEAR_REFS_MAPPED)
+       if ((type < CLEAR_REFS_ALL || type > CLEAR_REFS_MAPPED) &&
+           type != CLEAR_REFS_MM_HIWATER_RSS)
                return -EINVAL;
        task = get_proc_task(file_inode(file));
        if (!task)
@@ -814,6 +816,18 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf,
                        .pmd_entry = clear_refs_pte_range,
                        .mm = mm,
                };
+
+               if (type == CLEAR_REFS_MM_HIWATER_RSS) {
+                       /*
+                        * Writing 5 to /proc/pid/clear_refs resets the peak
+                        * resident set size to this mm's current rss value.
+                        */
+                       down_write(&mm->mmap_sem);
+                       reset_mm_hiwater_rss(mm);
+                       up_write(&mm->mmap_sem);
+                       goto out_mm;
+               }
+
                down_read(&mm->mmap_sem);
                for (vma = mm->mmap; vma; vma = vma->vm_next) {
                        clear_refs_walk.private = vma;
@@ -837,6 +851,7 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf,
                }
                flush_tlb_mm(mm);
                up_read(&mm->mmap_sem);
+out_mm:
                mmput(mm);
        }
        put_task_struct(task);
index 0a11045356d4bfcab3bfd0bc8922a13e45e12f14..a12e0829d8acc52604c1191c3f6146d34b444a15 100644 (file)
@@ -249,6 +249,7 @@ static void parse_options(char *options)
 
 static int pstore_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        parse_options(data);
 
        return 0;
index 2e8caa62da78a71cc1ee46ce49634aab9408a779..3410e9f657cadc3072786ebf71e28aa3982b6436 100644 (file)
@@ -46,6 +46,7 @@ static int qnx4_remount(struct super_block *sb, int *flags, char *data)
 {
        struct qnx4_sb_info *qs;
 
+       sync_filesystem(sb);
        qs = qnx4_sb(sb);
        qs->Version = QNX4_VERSION;
        *flags |= MS_RDONLY;
index 8d941edfefa156bedb93a1a074ccda0e9a042fad..65cdaab3ed49d554e4d618202e4d41d11718c714 100644 (file)
@@ -55,6 +55,7 @@ static int qnx6_show_options(struct seq_file *seq, struct dentry *root)
 
 static int qnx6_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        *flags |= MS_RDONLY;
        return 0;
 }
index fee38e04fae4ab09440c05808e539f72133c6976..d46eca8567a413a8f081b4c8a5272a60a2039437 100644 (file)
 
 #include <asm/uaccess.h>
 
-int vfs_readdir(struct file *file, filldir_t filler, void *buf)
+int iterate_dir(struct file *file, struct dir_context *ctx)
 {
        struct inode *inode = file_inode(file);
        int res = -ENOTDIR;
-       if (!file->f_op || !file->f_op->readdir)
+       if (!file->f_op || (!file->f_op->readdir && !file->f_op->iterate))
                goto out;
 
        res = security_file_permission(file, MAY_READ);
@@ -37,15 +37,21 @@ int vfs_readdir(struct file *file, filldir_t filler, void *buf)
 
        res = -ENOENT;
        if (!IS_DEADDIR(inode)) {
-               res = file->f_op->readdir(file, buf, filler);
+               if (file->f_op->iterate) {
+                       ctx->pos = file->f_pos;
+                       res = file->f_op->iterate(file, ctx);
+                       file->f_pos = ctx->pos;
+               } else {
+                       res = file->f_op->readdir(file, ctx, ctx->actor);
+                       ctx->pos = file->f_pos;
+               }
                file_accessed(file);
        }
        mutex_unlock(&inode->i_mutex);
 out:
        return res;
 }
-
-EXPORT_SYMBOL(vfs_readdir);
+EXPORT_SYMBOL(iterate_dir);
 
 /*
  * Traditional linux readdir() handling..
@@ -66,6 +72,7 @@ struct old_linux_dirent {
 };
 
 struct readdir_callback {
+       struct dir_context ctx;
        struct old_linux_dirent __user * dirent;
        int result;
 };
@@ -73,7 +80,7 @@ struct readdir_callback {
 static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
                      u64 ino, unsigned int d_type)
 {
-       struct readdir_callback * buf = (struct readdir_callback *) __buf;
+       struct readdir_callback *buf = (struct readdir_callback *) __buf;
        struct old_linux_dirent __user * dirent;
        unsigned long d_ino;
 
@@ -107,15 +114,15 @@ SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
 {
        int error;
        struct fd f = fdget(fd);
-       struct readdir_callback buf;
+       struct readdir_callback buf = {
+               .ctx.actor = fillonedir,
+               .dirent = dirent
+       };
 
        if (!f.file)
                return -EBADF;
 
-       buf.result = 0;
-       buf.dirent = dirent;
-
-       error = vfs_readdir(f.file, fillonedir, &buf);
+       error = iterate_dir(f.file, &buf.ctx);
        if (buf.result)
                error = buf.result;
 
@@ -137,6 +144,7 @@ struct linux_dirent {
 };
 
 struct getdents_callback {
+       struct dir_context ctx;
        struct linux_dirent __user * current_dir;
        struct linux_dirent __user * previous;
        int count;
@@ -191,7 +199,11 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
 {
        struct fd f;
        struct linux_dirent __user * lastdirent;
-       struct getdents_callback buf;
+       struct getdents_callback buf = {
+               .ctx.actor = filldir,
+               .count = count,
+               .current_dir = dirent
+       };
        int error;
 
        if (!access_ok(VERIFY_WRITE, dirent, count))
@@ -201,17 +213,12 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
        if (!f.file)
                return -EBADF;
 
-       buf.current_dir = dirent;
-       buf.previous = NULL;
-       buf.count = count;
-       buf.error = 0;
-
-       error = vfs_readdir(f.file, filldir, &buf);
+       error = iterate_dir(f.file, &buf.ctx);
        if (error >= 0)
                error = buf.error;
        lastdirent = buf.previous;
        if (lastdirent) {
-               if (put_user(f.file->f_pos, &lastdirent->d_off))
+               if (put_user(buf.ctx.pos, &lastdirent->d_off))
                        error = -EFAULT;
                else
                        error = count - buf.count;
@@ -221,6 +228,7 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
 }
 
 struct getdents_callback64 {
+       struct dir_context ctx;
        struct linux_dirent64 __user * current_dir;
        struct linux_dirent64 __user * previous;
        int count;
@@ -271,7 +279,11 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
 {
        struct fd f;
        struct linux_dirent64 __user * lastdirent;
-       struct getdents_callback64 buf;
+       struct getdents_callback64 buf = {
+               .ctx.actor = filldir64,
+               .count = count,
+               .current_dir = dirent
+       };
        int error;
 
        if (!access_ok(VERIFY_WRITE, dirent, count))
@@ -281,17 +293,12 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
        if (!f.file)
                return -EBADF;
 
-       buf.current_dir = dirent;
-       buf.previous = NULL;
-       buf.count = count;
-       buf.error = 0;
-
-       error = vfs_readdir(f.file, filldir64, &buf);
+       error = iterate_dir(f.file, &buf.ctx);
        if (error >= 0)
                error = buf.error;
        lastdirent = buf.previous;
        if (lastdirent) {
-               typeof(lastdirent->d_off) d_off = f.file->f_pos;
+               typeof(lastdirent->d_off) d_off = buf.ctx.pos;
                if (__put_user(d_off, &lastdirent->d_off))
                        error = -EFAULT;
                else
index e2e202a07b317cff5c84d8b51e3b66efafd689ea..460f762a85c0f1feada7b819f15614cf249b4ca2 100644 (file)
@@ -1317,6 +1317,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)
        int i;
 #endif
 
+       sync_filesystem(s);
        reiserfs_write_lock(s);
 
 #ifdef CONFIG_QUOTA
index 15cbc41ee3653133c6e7001aec75e754582362e6..ae839482c34159d4bd6ad2cd18536760c9a55dd2 100644 (file)
@@ -435,6 +435,7 @@ static int romfs_statfs(struct dentry *dentry, struct kstatfs *buf)
  */
 static int romfs_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        *flags |= MS_RDONLY;
        return 0;
 }
index c70111ebefd44aaacf7248e10a5e80fecc06582a..ffb093e72b6ca89a0f8d339dfad3f04da0c97e8e 100644 (file)
@@ -25,6 +25,78 @@ config SQUASHFS
 
          If unsure, say N.
 
+choice
+       prompt "File decompression options"
+       depends on SQUASHFS
+       help
+         Squashfs now supports two options for decompressing file
+         data.  Traditionally Squashfs has decompressed into an
+         intermediate buffer and then memcopied it into the page cache.
+         Squashfs now supports the ability to decompress directly into
+         the page cache.
+
+         If unsure, select "Decompress file data into an intermediate buffer"
+
+config SQUASHFS_FILE_CACHE
+       bool "Decompress file data into an intermediate buffer"
+       help
+         Decompress file data into an intermediate buffer and then
+         memcopy it into the page cache.
+
+config SQUASHFS_FILE_DIRECT
+       bool "Decompress files directly into the page cache"
+       help
+         Directly decompress file data into the page cache.
+         Doing so can significantly improve performance because
+         it eliminates a memcpy and it also removes the lock contention
+         on the single buffer.
+
+endchoice
+
+choice
+       prompt "Decompressor parallelisation options"
+       depends on SQUASHFS
+       help
+         Squashfs now supports three parallelisation options for
+         decompression.  Each one exhibits various trade-offs between
+         decompression performance and CPU and memory usage.
+
+         If in doubt, select "Single threaded compression"
+
+config SQUASHFS_DECOMP_SINGLE
+       bool "Single threaded compression"
+       help
+         Traditionally Squashfs has used single-threaded decompression.
+         Only one block (data or metadata) can be decompressed at any
+         one time.  This limits CPU and memory usage to a minimum.
+
+config SQUASHFS_DECOMP_MULTI
+       bool "Use multiple decompressors for parallel I/O"
+       help
+         By default Squashfs uses a single decompressor but it gives
+         poor performance on parallel I/O workloads when using multiple CPU
+         machines due to waiting on decompressor availability.
+
+         If you have a parallel I/O workload and your system has enough memory,
+         using this option may improve overall I/O performance.
+
+         This decompressor implementation uses up to two parallel
+         decompressors per core.  It dynamically allocates decompressors
+         on a demand basis.
+
+config SQUASHFS_DECOMP_MULTI_PERCPU
+       bool "Use percpu multiple decompressors for parallel I/O"
+       help
+         By default Squashfs uses a single decompressor but it gives
+         poor performance on parallel I/O workloads when using multiple CPU
+         machines due to waiting on decompressor availability.
+
+         This decompressor implementation uses a maximum of one
+         decompressor per core.  It uses percpu variables to ensure
+         decompression is load-balanced across the cores.
+
+endchoice
+
 config SQUASHFS_XATTR
        bool "Squashfs XATTR support"
        depends on SQUASHFS
@@ -48,6 +120,21 @@ config SQUASHFS_ZLIB
 
          If unsure, say Y.
 
+config SQUASHFS_LZ4
+       bool "Include support for LZ4 compressed file systems"
+       depends on SQUASHFS
+       select LZ4_DECOMPRESS
+       help
+         Saying Y here includes support for reading Squashfs file systems
+         compressed with LZ4 compression.  LZ4 compression is mainly
+         aimed at embedded systems with slower CPUs where the overheads
+         of zlib are too high.
+
+         LZ4 is not the standard compression used in Squashfs and so most
+         file systems will be readable without selecting this option.
+
+         If unsure, say N.
+
 config SQUASHFS_LZO
        bool "Include support for LZO compressed file systems"
        depends on SQUASHFS
index 110b0476f3b48a21e016dd8f4337476ddf37c3ce..246a6f329d8957df4b3bf48129b3f0e415be2611 100644 (file)
@@ -5,7 +5,13 @@
 obj-$(CONFIG_SQUASHFS) += squashfs.o
 squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
 squashfs-y += namei.o super.o symlink.o decompressor.o
+squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o
+squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o
+squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o
+squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o
+squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o
 squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o
+squashfs-$(CONFIG_SQUASHFS_LZ4) += lz4_wrapper.o
 squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o
 squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o
 squashfs-$(CONFIG_SQUASHFS_ZLIB) += zlib_wrapper.o
index fb50652e4e113f54a4447eb147fa56da59a8ba45..0cea9b9236d07c81d0cc46c0b22aeba334cc645d 100644 (file)
@@ -36,6 +36,7 @@
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "page_actor.h"
 
 /*
  * Read the metadata block length, this is stored in the first two
@@ -86,16 +87,16 @@ static struct buffer_head *get_block_length(struct super_block *sb,
  * generated a larger block - this does occasionally happen with compression
  * algorithms).
  */
-int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
-                       int length, u64 *next_index, int srclength, int pages)
+int squashfs_read_data(struct super_block *sb, u64 index, int length,
+               u64 *next_index, struct squashfs_page_actor *output)
 {
        struct squashfs_sb_info *msblk = sb->s_fs_info;
        struct buffer_head **bh;
        int offset = index & ((1 << msblk->devblksize_log2) - 1);
        u64 cur_index = index >> msblk->devblksize_log2;
-       int bytes, compressed, b = 0, k = 0, page = 0, avail;
+       int bytes, compressed, b = 0, k = 0, avail, i;
 
-       bh = kcalloc(((srclength + msblk->devblksize - 1)
+       bh = kcalloc(((output->length + msblk->devblksize - 1)
                >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
        if (bh == NULL)
                return -ENOMEM;
@@ -111,9 +112,9 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
                        *next_index = index + length;
 
                TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
-                       index, compressed ? "" : "un", length, srclength);
+                       index, compressed ? "" : "un", length, output->length);
 
-               if (length < 0 || length > srclength ||
+               if (length < 0 || length > output->length ||
                                (index + length) > msblk->bytes_used)
                        goto read_failure;
 
@@ -145,7 +146,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
                TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
                                compressed ? "" : "un", length);
 
-               if (length < 0 || length > srclength ||
+               if (length < 0 || length > output->length ||
                                        (index + length) > msblk->bytes_used)
                        goto block_release;
 
@@ -158,35 +159,36 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
                ll_rw_block(READ, b - 1, bh + 1);
        }
 
+       for (i = 0; i < b; i++) {
+               wait_on_buffer(bh[i]);
+               if (!buffer_uptodate(bh[i]))
+                       goto block_release;
+       }
+
        if (compressed) {
-               length = squashfs_decompress(msblk, buffer, bh, b, offset,
-                        length, srclength, pages);
+               length = squashfs_decompress(msblk, bh, b, offset, length,
+                       output);
                if (length < 0)
                        goto read_failure;
        } else {
                /*
                 * Block is uncompressed.
                 */
-               int i, in, pg_offset = 0;
-
-               for (i = 0; i < b; i++) {
-                       wait_on_buffer(bh[i]);
-                       if (!buffer_uptodate(bh[i]))
-                               goto block_release;
-               }
+               int in, pg_offset = 0;
+               void *data = squashfs_first_page(output);
 
                for (bytes = length; k < b; k++) {
                        in = min(bytes, msblk->devblksize - offset);
                        bytes -= in;
                        while (in) {
                                if (pg_offset == PAGE_CACHE_SIZE) {
-                                       page++;
+                                       data = squashfs_next_page(output);
                                        pg_offset = 0;
                                }
                                avail = min_t(int, in, PAGE_CACHE_SIZE -
                                                pg_offset);
-                               memcpy(buffer[page] + pg_offset,
-                                               bh[k]->b_data + offset, avail);
+                               memcpy(data + pg_offset, bh[k]->b_data + offset,
+                                               avail);
                                in -= avail;
                                pg_offset += avail;
                                offset += avail;
@@ -194,6 +196,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
                        offset = 0;
                        put_bh(bh[k]);
                }
+               squashfs_finish_page(output);
        }
 
        kfree(bh);
index af0b738025929b1c18ac884734131ea5e843e40c..1cb70a0b216844a136bf3b47ee6534d22496bb1c 100644 (file)
@@ -56,6 +56,7 @@
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
+#include "page_actor.h"
 
 /*
  * Look-up block in cache, and increment usage count.  If not in cache, read
@@ -119,9 +120,8 @@ struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,
                        entry->error = 0;
                        spin_unlock(&cache->lock);
 
-                       entry->length = squashfs_read_data(sb, entry->data,
-                               block, length, &entry->next_index,
-                               cache->block_size, cache->pages);
+                       entry->length = squashfs_read_data(sb, block, length,
+                               &entry->next_index, entry->actor);
 
                        spin_lock(&cache->lock);
 
@@ -220,6 +220,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache)
                                kfree(cache->entry[i].data[j]);
                        kfree(cache->entry[i].data);
                }
+               kfree(cache->entry[i].actor);
        }
 
        kfree(cache->entry);
@@ -280,6 +281,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries,
                                goto cleanup;
                        }
                }
+
+               entry->actor = squashfs_page_actor_init(entry->data,
+                                               cache->pages, 0);
+               if (entry->actor == NULL) {
+                       ERROR("Failed to allocate %s cache entry\n", name);
+                       goto cleanup;
+               }
        }
 
        return cache;
@@ -410,6 +418,7 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length)
        int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
        int i, res;
        void *table, *buffer, **data;
+       struct squashfs_page_actor *actor;
 
        table = buffer = kmalloc(length, GFP_KERNEL);
        if (table == NULL)
@@ -421,19 +430,28 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length)
                goto failed;
        }
 
+       actor = squashfs_page_actor_init(data, pages, length);
+       if (actor == NULL) {
+               res = -ENOMEM;
+               goto failed2;
+       }
+
        for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
                data[i] = buffer;
 
-       res = squashfs_read_data(sb, data, block, length |
-               SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages);
+       res = squashfs_read_data(sb, block, length |
+               SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);
 
        kfree(data);
+       kfree(actor);
 
        if (res < 0)
                goto failed;
 
        return table;
 
+failed2:
+       kfree(data);
 failed:
        kfree(table);
        return ERR_PTR(res);
index 3f6271d86abc48ba3132ccfdd5668e118799e249..e9034bf6e5ae27bb3e2379e0816e2b11d5ed41b1 100644 (file)
@@ -30,6 +30,7 @@
 #include "squashfs_fs_sb.h"
 #include "decompressor.h"
 #include "squashfs.h"
+#include "page_actor.h"
 
 /*
  * This file (and decompressor.h) implements a decompressor framework for
  */
 
 static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = {
-       NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
+       NULL, NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
 };
 
+#ifndef CONFIG_SQUASHFS_LZ4
+static const struct squashfs_decompressor squashfs_lz4_comp_ops = {
+       NULL, NULL, NULL, NULL, LZ4_COMPRESSION, "lz4", 0
+};
+#endif
+
 #ifndef CONFIG_SQUASHFS_LZO
 static const struct squashfs_decompressor squashfs_lzo_comp_ops = {
-       NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
+       NULL, NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
 };
 #endif
 
 #ifndef CONFIG_SQUASHFS_XZ
 static const struct squashfs_decompressor squashfs_xz_comp_ops = {
-       NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
+       NULL, NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
 };
 #endif
 
 #ifndef CONFIG_SQUASHFS_ZLIB
 static const struct squashfs_decompressor squashfs_zlib_comp_ops = {
-       NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
+       NULL, NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
 };
 #endif
 
 static const struct squashfs_decompressor squashfs_unknown_comp_ops = {
-       NULL, NULL, NULL, 0, "unknown", 0
+       NULL, NULL, NULL, NULL, 0, "unknown", 0
 };
 
 static const struct squashfs_decompressor *decompressor[] = {
        &squashfs_zlib_comp_ops,
+       &squashfs_lz4_comp_ops,
        &squashfs_lzo_comp_ops,
        &squashfs_xz_comp_ops,
        &squashfs_lzma_unsupported_comp_ops,
@@ -83,10 +91,11 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
 }
 
 
-void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
+static void *get_comp_opts(struct super_block *sb, unsigned short flags)
 {
        struct squashfs_sb_info *msblk = sb->s_fs_info;
-       void *strm, *buffer = NULL;
+       void *buffer = NULL, *comp_opts;
+       struct squashfs_page_actor *actor = NULL;
        int length = 0;
 
        /*
@@ -94,23 +103,46 @@ void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
         */
        if (SQUASHFS_COMP_OPTS(flags)) {
                buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
-               if (buffer == NULL)
-                       return ERR_PTR(-ENOMEM);
+               if (buffer == NULL) {
+                       comp_opts = ERR_PTR(-ENOMEM);
+                       goto out;
+               }
 
-               length = squashfs_read_data(sb, &buffer,
-                       sizeof(struct squashfs_super_block), 0, NULL,
-                       PAGE_CACHE_SIZE, 1);
+               actor = squashfs_page_actor_init(&buffer, 1, 0);
+               if (actor == NULL) {
+                       comp_opts = ERR_PTR(-ENOMEM);
+                       goto out;
+               }
+
+               length = squashfs_read_data(sb,
+                       sizeof(struct squashfs_super_block), 0, NULL, actor);
 
                if (length < 0) {
-                       strm = ERR_PTR(length);
-                       goto finished;
+                       comp_opts = ERR_PTR(length);
+                       goto out;
                }
        }
 
-       strm = msblk->decompressor->init(msblk, buffer, length);
+       comp_opts = squashfs_comp_opts(msblk, buffer, length);
 
-finished:
+out:
+       kfree(actor);
        kfree(buffer);
+       return comp_opts;
+}
+
+
+void *squashfs_decompressor_setup(struct super_block *sb, unsigned short flags)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       void *stream, *comp_opts = get_comp_opts(sb, flags);
+
+       if (IS_ERR(comp_opts))
+               return comp_opts;
+
+       stream = squashfs_decompressor_create(msblk, comp_opts);
+       if (IS_ERR(stream))
+               kfree(comp_opts);
 
-       return strm;
+       return stream;
 }
index 330073e29029950238da75d90fbd86a9dc8c881a..a25713c031a51213edc9dd5a32de73f64f75c44f 100644 (file)
  */
 
 struct squashfs_decompressor {
-       void    *(*init)(struct squashfs_sb_info *, void *, int);
+       void    *(*init)(struct squashfs_sb_info *, void *);
+       void    *(*comp_opts)(struct squashfs_sb_info *, void *, int);
        void    (*free)(void *);
-       int     (*decompress)(struct squashfs_sb_info *, void **,
-               struct buffer_head **, int, int, int, int, int);
+       int     (*decompress)(struct squashfs_sb_info *, void *,
+               struct buffer_head **, int, int, int,
+               struct squashfs_page_actor *);
        int     id;
        char    *name;
        int     supported;
 };
 
-static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
-       void *s)
+static inline void *squashfs_comp_opts(struct squashfs_sb_info *msblk,
+                                                       void *buff, int length)
 {
-       if (msblk->decompressor)
-               msblk->decompressor->free(s);
-}
-
-static inline int squashfs_decompress(struct squashfs_sb_info *msblk,
-       void **buffer, struct buffer_head **bh, int b, int offset, int length,
-       int srclength, int pages)
-{
-       return msblk->decompressor->decompress(msblk, buffer, bh, b, offset,
-               length, srclength, pages);
+       return msblk->decompressor->comp_opts ?
+               msblk->decompressor->comp_opts(msblk, buff, length) : NULL;
 }
 
 #ifdef CONFIG_SQUASHFS_XZ
 extern const struct squashfs_decompressor squashfs_xz_comp_ops;
 #endif
 
+#ifdef CONFIG_SQUASHFS_LZ4
+extern const struct squashfs_decompressor squashfs_lz4_comp_ops;
+#endif
+
 #ifdef CONFIG_SQUASHFS_LZO
 extern const struct squashfs_decompressor squashfs_lzo_comp_ops;
 #endif
diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c
new file mode 100644 (file)
index 0000000..d6008a6
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ *  Copyright (c) 2013
+ *  Minchan Kim <minchan@kernel.org>
+ *
+ *  This work is licensed under the terms of the GNU GPL, version 2. See
+ *  the COPYING file in the top-level directory.
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/cpumask.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "decompressor.h"
+#include "squashfs.h"
+
+/*
+ * This file implements multi-threaded decompression in the
+ * decompressor framework
+ */
+
+
+/*
+ * The reason that multiply two is that a CPU can request new I/O
+ * while it is waiting previous request.
+ */
+#define MAX_DECOMPRESSOR       (num_online_cpus() * 2)
+
+
+int squashfs_max_decompressors(void)
+{
+       return MAX_DECOMPRESSOR;
+}
+
+
+struct squashfs_stream {
+       void                    *comp_opts;
+       struct list_head        strm_list;
+       struct mutex            mutex;
+       int                     avail_decomp;
+       wait_queue_head_t       wait;
+};
+
+
+struct decomp_stream {
+       void *stream;
+       struct list_head list;
+};
+
+
+static void put_decomp_stream(struct decomp_stream *decomp_strm,
+                               struct squashfs_stream *stream)
+{
+       mutex_lock(&stream->mutex);
+       list_add(&decomp_strm->list, &stream->strm_list);
+       mutex_unlock(&stream->mutex);
+       wake_up(&stream->wait);
+}
+
+void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
+                               void *comp_opts)
+{
+       struct squashfs_stream *stream;
+       struct decomp_stream *decomp_strm = NULL;
+       int err = -ENOMEM;
+
+       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+       if (!stream)
+               goto out;
+
+       stream->comp_opts = comp_opts;
+       mutex_init(&stream->mutex);
+       INIT_LIST_HEAD(&stream->strm_list);
+       init_waitqueue_head(&stream->wait);
+
+       /*
+        * We should have a decompressor at least as default
+        * so if we fail to allocate new decompressor dynamically,
+        * we could always fall back to default decompressor and
+        * file system works.
+        */
+       decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL);
+       if (!decomp_strm)
+               goto out;
+
+       decomp_strm->stream = msblk->decompressor->init(msblk,
+                                               stream->comp_opts);
+       if (IS_ERR(decomp_strm->stream)) {
+               err = PTR_ERR(decomp_strm->stream);
+               goto out;
+       }
+
+       list_add(&decomp_strm->list, &stream->strm_list);
+       stream->avail_decomp = 1;
+       return stream;
+
+out:
+       kfree(decomp_strm);
+       kfree(stream);
+       return ERR_PTR(err);
+}
+
+
+void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
+{
+       struct squashfs_stream *stream = msblk->stream;
+       if (stream) {
+               struct decomp_stream *decomp_strm;
+
+               while (!list_empty(&stream->strm_list)) {
+                       decomp_strm = list_entry(stream->strm_list.prev,
+                                               struct decomp_stream, list);
+                       list_del(&decomp_strm->list);
+                       msblk->decompressor->free(decomp_strm->stream);
+                       kfree(decomp_strm);
+                       stream->avail_decomp--;
+               }
+               WARN_ON(stream->avail_decomp);
+               kfree(stream->comp_opts);
+               kfree(stream);
+       }
+}
+
+
+static struct decomp_stream *get_decomp_stream(struct squashfs_sb_info *msblk,
+                                       struct squashfs_stream *stream)
+{
+       struct decomp_stream *decomp_strm;
+
+       while (1) {
+               mutex_lock(&stream->mutex);
+
+               /* There is available decomp_stream */
+               if (!list_empty(&stream->strm_list)) {
+                       decomp_strm = list_entry(stream->strm_list.prev,
+                               struct decomp_stream, list);
+                       list_del(&decomp_strm->list);
+                       mutex_unlock(&stream->mutex);
+                       break;
+               }
+
+               /*
+                * If there is no available decomp and already full,
+                * let's wait for releasing decomp from other users.
+                */
+               if (stream->avail_decomp >= MAX_DECOMPRESSOR)
+                       goto wait;
+
+               /* Let's allocate new decomp */
+               decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL);
+               if (!decomp_strm)
+                       goto wait;
+
+               decomp_strm->stream = msblk->decompressor->init(msblk,
+                                               stream->comp_opts);
+               if (IS_ERR(decomp_strm->stream)) {
+                       kfree(decomp_strm);
+                       goto wait;
+               }
+
+               stream->avail_decomp++;
+               WARN_ON(stream->avail_decomp > MAX_DECOMPRESSOR);
+
+               mutex_unlock(&stream->mutex);
+               break;
+wait:
+               /*
+                * If system memory is tough, let's for other's
+                * releasing instead of hurting VM because it could
+                * make page cache thrashing.
+                */
+               mutex_unlock(&stream->mutex);
+               wait_event(stream->wait,
+                       !list_empty(&stream->strm_list));
+       }
+
+       return decomp_strm;
+}
+
+
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
+       int b, int offset, int length, struct squashfs_page_actor *output)
+{
+       int res;
+       struct squashfs_stream *stream = msblk->stream;
+       struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream);
+       res = msblk->decompressor->decompress(msblk, decomp_stream->stream,
+               bh, b, offset, length, output);
+       put_decomp_stream(decomp_stream, stream);
+       if (res < 0)
+               ERROR("%s decompression failed, data probably corrupt\n",
+                       msblk->decompressor->name);
+       return res;
+}
diff --git a/fs/squashfs/decompressor_multi_percpu.c b/fs/squashfs/decompressor_multi_percpu.c
new file mode 100644 (file)
index 0000000..23a9c28
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/percpu.h>
+#include <linux/buffer_head.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "decompressor.h"
+#include "squashfs.h"
+
+/*
+ * This file implements multi-threaded decompression using percpu
+ * variables, one thread per cpu core.
+ */
+
+struct squashfs_stream {
+       void            *stream;
+};
+
+void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
+                                               void *comp_opts)
+{
+       struct squashfs_stream *stream;
+       struct squashfs_stream __percpu *percpu;
+       int err, cpu;
+
+       percpu = alloc_percpu(struct squashfs_stream);
+       if (percpu == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       for_each_possible_cpu(cpu) {
+               stream = per_cpu_ptr(percpu, cpu);
+               stream->stream = msblk->decompressor->init(msblk, comp_opts);
+               if (IS_ERR(stream->stream)) {
+                       err = PTR_ERR(stream->stream);
+                       goto out;
+               }
+       }
+
+       kfree(comp_opts);
+       return (__force void *) percpu;
+
+out:
+       for_each_possible_cpu(cpu) {
+               stream = per_cpu_ptr(percpu, cpu);
+               if (!IS_ERR_OR_NULL(stream->stream))
+                       msblk->decompressor->free(stream->stream);
+       }
+       free_percpu(percpu);
+       return ERR_PTR(err);
+}
+
+void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
+{
+       struct squashfs_stream __percpu *percpu =
+                       (struct squashfs_stream __percpu *) msblk->stream;
+       struct squashfs_stream *stream;
+       int cpu;
+
+       if (msblk->stream) {
+               for_each_possible_cpu(cpu) {
+                       stream = per_cpu_ptr(percpu, cpu);
+                       msblk->decompressor->free(stream->stream);
+               }
+               free_percpu(percpu);
+       }
+}
+
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
+       int b, int offset, int length, struct squashfs_page_actor *output)
+{
+       struct squashfs_stream __percpu *percpu =
+                       (struct squashfs_stream __percpu *) msblk->stream;
+       struct squashfs_stream *stream = get_cpu_ptr(percpu);
+       int res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
+               offset, length, output);
+       put_cpu_ptr(stream);
+
+       if (res < 0)
+               ERROR("%s decompression failed, data probably corrupt\n",
+                       msblk->decompressor->name);
+
+       return res;
+}
+
+int squashfs_max_decompressors(void)
+{
+       return num_possible_cpus();
+}
diff --git a/fs/squashfs/decompressor_single.c b/fs/squashfs/decompressor_single.c
new file mode 100644 (file)
index 0000000..a6c7592
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "decompressor.h"
+#include "squashfs.h"
+
+/*
+ * This file implements single-threaded decompression in the
+ * decompressor framework
+ */
+
+struct squashfs_stream {
+       void            *stream;
+       struct mutex    mutex;
+};
+
+void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
+                                               void *comp_opts)
+{
+       struct squashfs_stream *stream;
+       int err = -ENOMEM;
+
+       stream = kmalloc(sizeof(*stream), GFP_KERNEL);
+       if (stream == NULL)
+               goto out;
+
+       stream->stream = msblk->decompressor->init(msblk, comp_opts);
+       if (IS_ERR(stream->stream)) {
+               err = PTR_ERR(stream->stream);
+               goto out;
+       }
+
+       kfree(comp_opts);
+       mutex_init(&stream->mutex);
+       return stream;
+
+out:
+       kfree(stream);
+       return ERR_PTR(err);
+}
+
+void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
+{
+       struct squashfs_stream *stream = msblk->stream;
+
+       if (stream) {
+               msblk->decompressor->free(stream->stream);
+               kfree(stream);
+       }
+}
+
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
+       int b, int offset, int length, struct squashfs_page_actor *output)
+{
+       int res;
+       struct squashfs_stream *stream = msblk->stream;
+
+       mutex_lock(&stream->mutex);
+       res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
+               offset, length, output);
+       mutex_unlock(&stream->mutex);
+
+       if (res < 0)
+               ERROR("%s decompression failed, data probably corrupt\n",
+                       msblk->decompressor->name);
+
+       return res;
+}
+
+int squashfs_max_decompressors(void)
+{
+       return 1;
+}
index 57dc70ebbb1993eb446f4571f19b0b6f7ea1d5ae..d8c2d747be28d183542a0f2bd4a5d18060436783 100644 (file)
@@ -54,6 +54,7 @@ static int get_dir_index_using_offset(struct super_block *sb,
 {
        struct squashfs_sb_info *msblk = sb->s_fs_info;
        int err, i, index, length = 0;
+       unsigned int size;
        struct squashfs_dir_index dir_index;
 
        TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %lld\n",
@@ -81,8 +82,14 @@ static int get_dir_index_using_offset(struct super_block *sb,
                         */
                        break;
 
+               size = le32_to_cpu(dir_index.size) + 1;
+
+               /* size should never be larger than SQUASHFS_NAME_LEN */
+               if (size > SQUASHFS_NAME_LEN)
+                       break;
+
                err = squashfs_read_metadata(sb, NULL, &index_start,
-                               &index_offset, le32_to_cpu(dir_index.size) + 1);
+                               &index_offset, size);
                if (err < 0)
                        break;
 
@@ -100,14 +107,13 @@ static int get_dir_index_using_offset(struct super_block *sb,
 }
 
 
-static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
+static int squashfs_readdir(struct file *file, struct dir_context *ctx)
 {
        struct inode *inode = file_inode(file);
        struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
        u64 block = squashfs_i(inode)->start + msblk->directory_table;
-       int offset = squashfs_i(inode)->offset, length, dir_count, size,
-                               type, err;
-       unsigned int inode_number;
+       int offset = squashfs_i(inode)->offset, length, err;
+       unsigned int inode_number, dir_count, size, type;
        struct squashfs_dir_header dirh;
        struct squashfs_dir_entry *dire;
 
@@ -127,11 +133,11 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
         * It also means that the external f_pos is offset by 3 from the
         * on-disk directory f_pos.
         */
-       while (file->f_pos < 3) {
+       while (ctx->pos < 3) {
                char *name;
                int i_ino;
 
-               if (file->f_pos == 0) {
+               if (ctx->pos == 0) {
                        name = ".";
                        size = 1;
                        i_ino = inode->i_ino;
@@ -141,24 +147,18 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
                        i_ino = squashfs_i(inode)->parent;
                }
 
-               TRACE("Calling filldir(%p, %s, %d, %lld, %d, %d)\n",
-                               dirent, name, size, file->f_pos, i_ino,
-                               squashfs_filetype_table[1]);
-
-               if (filldir(dirent, name, size, file->f_pos, i_ino,
-                               squashfs_filetype_table[1]) < 0) {
-                               TRACE("Filldir returned less than 0\n");
+               if (!dir_emit(ctx, name, size, i_ino,
+                               squashfs_filetype_table[1]))
                        goto finish;
-               }
 
-               file->f_pos += size;
+               ctx->pos += size;
        }
 
        length = get_dir_index_using_offset(inode->i_sb, &block, &offset,
                                squashfs_i(inode)->dir_idx_start,
                                squashfs_i(inode)->dir_idx_offset,
                                squashfs_i(inode)->dir_idx_cnt,
-                               file->f_pos);
+                               ctx->pos);
 
        while (length < i_size_read(inode)) {
                /*
@@ -198,7 +198,7 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
 
                        length += sizeof(*dire) + size;
 
-                       if (file->f_pos >= length)
+                       if (ctx->pos >= length)
                                continue;
 
                        dire->name[size] = '\0';
@@ -206,22 +206,15 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
                                ((short) le16_to_cpu(dire->inode_number));
                        type = le16_to_cpu(dire->type);
 
-                       TRACE("Calling filldir(%p, %s, %d, %lld, %x:%x, %d, %d)"
-                                       "\n", dirent, dire->name, size,
-                                       file->f_pos,
-                                       le32_to_cpu(dirh.start_block),
-                                       le16_to_cpu(dire->offset),
-                                       inode_number,
-                                       squashfs_filetype_table[type]);
+                       if (type > SQUASHFS_MAX_DIR_TYPE)
+                               goto failed_read;
 
-                       if (filldir(dirent, dire->name, size, file->f_pos,
+                       if (!dir_emit(ctx, dire->name, size,
                                        inode_number,
-                                       squashfs_filetype_table[type]) < 0) {
-                               TRACE("Filldir returned less than 0\n");
+                                       squashfs_filetype_table[type]))
                                goto finish;
-                       }
 
-                       file->f_pos = length;
+                       ctx->pos = length;
                }
        }
 
@@ -238,6 +231,6 @@ failed_read:
 
 const struct file_operations squashfs_dir_ops = {
        .read = generic_read_dir,
-       .readdir = squashfs_readdir,
+       .iterate = squashfs_readdir,
        .llseek = default_llseek,
 };
index 8ca62c28fe1249fd40d7b8e01612b1dedc5ca704..e5c9689062ba81fff5db08f50c2d39d53c4508d9 100644 (file)
@@ -370,77 +370,15 @@ static int read_blocklist(struct inode *inode, int index, u64 *block)
        return le32_to_cpu(size);
 }
 
-
-static int squashfs_readpage(struct file *file, struct page *page)
+/* Copy data into page cache  */
+void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer,
+       int bytes, int offset)
 {
        struct inode *inode = page->mapping->host;
        struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-       int bytes, i, offset = 0, sparse = 0;
-       struct squashfs_cache_entry *buffer = NULL;
        void *pageaddr;
-
-       int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
-       int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
-       int start_index = page->index & ~mask;
-       int end_index = start_index | mask;
-       int file_end = i_size_read(inode) >> msblk->block_log;
-
-       TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
-                               page->index, squashfs_i(inode)->start);
-
-       if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
-                                       PAGE_CACHE_SHIFT))
-               goto out;
-
-       if (index < file_end || squashfs_i(inode)->fragment_block ==
-                                       SQUASHFS_INVALID_BLK) {
-               /*
-                * Reading a datablock from disk.  Need to read block list
-                * to get location and block size.
-                */
-               u64 block = 0;
-               int bsize = read_blocklist(inode, index, &block);
-               if (bsize < 0)
-                       goto error_out;
-
-               if (bsize == 0) { /* hole */
-                       bytes = index == file_end ?
-                               (i_size_read(inode) & (msblk->block_size - 1)) :
-                                msblk->block_size;
-                       sparse = 1;
-               } else {
-                       /*
-                        * Read and decompress datablock.
-                        */
-                       buffer = squashfs_get_datablock(inode->i_sb,
-                                                               block, bsize);
-                       if (buffer->error) {
-                               ERROR("Unable to read page, block %llx, size %x"
-                                       "\n", block, bsize);
-                               squashfs_cache_put(buffer);
-                               goto error_out;
-                       }
-                       bytes = buffer->length;
-               }
-       } else {
-               /*
-                * Datablock is stored inside a fragment (tail-end packed
-                * block).
-                */
-               buffer = squashfs_get_fragment(inode->i_sb,
-                               squashfs_i(inode)->fragment_block,
-                               squashfs_i(inode)->fragment_size);
-
-               if (buffer->error) {
-                       ERROR("Unable to read page, block %llx, size %x\n",
-                               squashfs_i(inode)->fragment_block,
-                               squashfs_i(inode)->fragment_size);
-                       squashfs_cache_put(buffer);
-                       goto error_out;
-               }
-               bytes = i_size_read(inode) & (msblk->block_size - 1);
-               offset = squashfs_i(inode)->fragment_offset;
-       }
+       int i, mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
+       int start_index = page->index & ~mask, end_index = start_index | mask;
 
        /*
         * Loop copying datablock into pages.  As the datablock likely covers
@@ -451,7 +389,7 @@ static int squashfs_readpage(struct file *file, struct page *page)
        for (i = start_index; i <= end_index && bytes > 0; i++,
                        bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
                struct page *push_page;
-               int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE);
+               int avail = buffer ? min_t(int, bytes, PAGE_CACHE_SIZE) : 0;
 
                TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
 
@@ -475,11 +413,75 @@ skip_page:
                if (i != page->index)
                        page_cache_release(push_page);
        }
+}
+
+/* Read datablock stored packed inside a fragment (tail-end packed block) */
+static int squashfs_readpage_fragment(struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb,
+               squashfs_i(inode)->fragment_block,
+               squashfs_i(inode)->fragment_size);
+       int res = buffer->error;
+
+       if (res)
+               ERROR("Unable to read page, block %llx, size %x\n",
+                       squashfs_i(inode)->fragment_block,
+                       squashfs_i(inode)->fragment_size);
+       else
+               squashfs_copy_cache(page, buffer, i_size_read(inode) &
+                       (msblk->block_size - 1),
+                       squashfs_i(inode)->fragment_offset);
+
+       squashfs_cache_put(buffer);
+       return res;
+}
 
-       if (!sparse)
-               squashfs_cache_put(buffer);
+static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
+{
+       struct inode *inode = page->mapping->host;
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       int bytes = index == file_end ?
+                       (i_size_read(inode) & (msblk->block_size - 1)) :
+                        msblk->block_size;
 
+       squashfs_copy_cache(page, NULL, bytes, 0);
        return 0;
+}
+
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
+       int file_end = i_size_read(inode) >> msblk->block_log;
+       int res;
+       void *pageaddr;
+
+       TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
+                               page->index, squashfs_i(inode)->start);
+
+       if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+                                       PAGE_CACHE_SHIFT))
+               goto out;
+
+       if (index < file_end || squashfs_i(inode)->fragment_block ==
+                                       SQUASHFS_INVALID_BLK) {
+               u64 block = 0;
+               int bsize = read_blocklist(inode, index, &block);
+               if (bsize < 0)
+                       goto error_out;
+
+               if (bsize == 0)
+                       res = squashfs_readpage_sparse(page, index, file_end);
+               else
+                       res = squashfs_readpage_block(page, block, bsize);
+       } else
+               res = squashfs_readpage_fragment(page);
+
+       if (!res)
+               return 0;
 
 error_out:
        SetPageError(page);
diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c
new file mode 100644 (file)
index 0000000..f2310d2
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/mutex.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/* Read separately compressed datablock and memcopy into page cache */
+int squashfs_readpage_block(struct page *page, u64 block, int bsize)
+{
+       struct inode *i = page->mapping->host;
+       struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
+               block, bsize);
+       int res = buffer->error;
+
+       if (res)
+               ERROR("Unable to read page, block %llx, size %x\n", block,
+                       bsize);
+       else
+               squashfs_copy_cache(page, buffer, buffer->length, 0);
+
+       squashfs_cache_put(buffer);
+       return res;
+}
diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c
new file mode 100644 (file)
index 0000000..43e7a7e
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/mutex.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+#include "page_actor.h"
+
+static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
+       int pages, struct page **page);
+
+/* Read separately compressed datablock directly into page cache */
+int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
+
+{
+       struct inode *inode = target_page->mapping->host;
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+
+       int file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT;
+       int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
+       int start_index = target_page->index & ~mask;
+       int end_index = start_index | mask;
+       int i, n, pages, missing_pages, bytes, res = -ENOMEM;
+       struct page **page;
+       struct squashfs_page_actor *actor;
+       void *pageaddr;
+
+       if (end_index > file_end)
+               end_index = file_end;
+
+       pages = end_index - start_index + 1;
+
+       page = kmalloc_array(pages, sizeof(void *), GFP_KERNEL);
+       if (page == NULL)
+               return res;
+
+       /*
+        * Create a "page actor" which will kmap and kunmap the
+        * page cache pages appropriately within the decompressor
+        */
+       actor = squashfs_page_actor_init_special(page, pages, 0);
+       if (actor == NULL)
+               goto out;
+
+       /* Try to grab all the pages covered by the Squashfs block */
+       for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) {
+               page[i] = (n == target_page->index) ? target_page :
+                       grab_cache_page_nowait(target_page->mapping, n);
+
+               if (page[i] == NULL) {
+                       missing_pages++;
+                       continue;
+               }
+
+               if (PageUptodate(page[i])) {
+                       unlock_page(page[i]);
+                       page_cache_release(page[i]);
+                       page[i] = NULL;
+                       missing_pages++;
+               }
+       }
+
+       if (missing_pages) {
+               /*
+                * Couldn't get one or more pages, this page has either
+                * been VM reclaimed, but others are still in the page cache
+                * and uptodate, or we're racing with another thread in
+                * squashfs_readpage also trying to grab them.  Fall back to
+                * using an intermediate buffer.
+                */
+               res = squashfs_read_cache(target_page, block, bsize, pages,
+                                                               page);
+               if (res < 0)
+                       goto mark_errored;
+
+               goto out;
+       }
+
+       /* Decompress directly into the page cache buffers */
+       res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
+       if (res < 0)
+               goto mark_errored;
+
+       /* Last page may have trailing bytes not filled */
+       bytes = res % PAGE_CACHE_SIZE;
+       if (bytes) {
+               pageaddr = kmap_atomic(page[pages - 1]);
+               memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+               kunmap_atomic(pageaddr);
+       }
+
+       /* Mark pages as uptodate, unlock and release */
+       for (i = 0; i < pages; i++) {
+               flush_dcache_page(page[i]);
+               SetPageUptodate(page[i]);
+               unlock_page(page[i]);
+               if (page[i] != target_page)
+                       page_cache_release(page[i]);
+       }
+
+       kfree(actor);
+       kfree(page);
+
+       return 0;
+
+mark_errored:
+       /* Decompression failed, mark pages as errored.  Target_page is
+        * dealt with by the caller
+        */
+       for (i = 0; i < pages; i++) {
+               if (page[i] == NULL || page[i] == target_page)
+                       continue;
+               flush_dcache_page(page[i]);
+               SetPageError(page[i]);
+               unlock_page(page[i]);
+               page_cache_release(page[i]);
+       }
+
+out:
+       kfree(actor);
+       kfree(page);
+       return res;
+}
+
+
+static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
+       int pages, struct page **page)
+{
+       struct inode *i = target_page->mapping->host;
+       struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
+                                                block, bsize);
+       int bytes = buffer->length, res = buffer->error, n, offset = 0;
+       void *pageaddr;
+
+       if (res) {
+               ERROR("Unable to read page, block %llx, size %x\n", block,
+                       bsize);
+               goto out;
+       }
+
+       for (n = 0; n < pages && bytes > 0; n++,
+                       bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
+               int avail = min_t(int, bytes, PAGE_CACHE_SIZE);
+
+               if (page[n] == NULL)
+                       continue;
+
+               pageaddr = kmap_atomic(page[n]);
+               squashfs_copy_data(pageaddr, buffer, offset, avail);
+               memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
+               kunmap_atomic(pageaddr);
+               flush_dcache_page(page[n]);
+               SetPageUptodate(page[n]);
+               unlock_page(page[n]);
+               if (page[n] != target_page)
+                       page_cache_release(page[n]);
+       }
+
+out:
+       squashfs_cache_put(buffer);
+       return res;
+}
diff --git a/fs/squashfs/lz4_wrapper.c b/fs/squashfs/lz4_wrapper.c
new file mode 100644 (file)
index 0000000..c31e2bc
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/buffer_head.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/lz4.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs.h"
+#include "decompressor.h"
+#include "page_actor.h"
+
+#define LZ4_LEGACY     1
+
+struct lz4_comp_opts {
+       __le32 version;
+       __le32 flags;
+};
+
+struct squashfs_lz4 {
+       void *input;
+       void *output;
+};
+
+
+static void *lz4_comp_opts(struct squashfs_sb_info *msblk,
+       void *buff, int len)
+{
+       struct lz4_comp_opts *comp_opts = buff;
+
+       /* LZ4 compressed filesystems always have compression options */
+       if (comp_opts == NULL || len < sizeof(*comp_opts))
+               return ERR_PTR(-EIO);
+
+       if (le32_to_cpu(comp_opts->version) != LZ4_LEGACY) {
+               /* LZ4 format currently used by the kernel is the 'legacy'
+                * format */
+               ERROR("Unknown LZ4 version\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       return NULL;
+}
+
+
+static void *lz4_init(struct squashfs_sb_info *msblk, void *buff)
+{
+       int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
+       struct squashfs_lz4 *stream;
+
+       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+       if (stream == NULL)
+               goto failed;
+       stream->input = vmalloc(block_size);
+       if (stream->input == NULL)
+               goto failed2;
+       stream->output = vmalloc(block_size);
+       if (stream->output == NULL)
+               goto failed3;
+
+       return stream;
+
+failed3:
+       vfree(stream->input);
+failed2:
+       kfree(stream);
+failed:
+       ERROR("Failed to initialise LZ4 decompressor\n");
+       return ERR_PTR(-ENOMEM);
+}
+
+
+static void lz4_free(void *strm)
+{
+       struct squashfs_lz4 *stream = strm;
+
+       if (stream) {
+               vfree(stream->input);
+               vfree(stream->output);
+       }
+       kfree(stream);
+}
+
+
+static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
+       struct buffer_head **bh, int b, int offset, int length,
+       struct squashfs_page_actor *output)
+{
+       struct squashfs_lz4 *stream = strm;
+       void *buff = stream->input, *data;
+       int avail, i, bytes = length, res;
+       size_t dest_len = output->length;
+
+       for (i = 0; i < b; i++) {
+               avail = min(bytes, msblk->devblksize - offset);
+               memcpy(buff, bh[i]->b_data + offset, avail);
+               buff += avail;
+               bytes -= avail;
+               offset = 0;
+               put_bh(bh[i]);
+       }
+
+       res = lz4_decompress_unknownoutputsize(stream->input, length,
+                                       stream->output, &dest_len);
+       if (res)
+               return -EIO;
+
+       bytes = dest_len;
+       data = squashfs_first_page(output);
+       buff = stream->output;
+       while (data) {
+               if (bytes <= PAGE_CACHE_SIZE) {
+                       memcpy(data, buff, bytes);
+                       break;
+               }
+               memcpy(data, buff, PAGE_CACHE_SIZE);
+               buff += PAGE_CACHE_SIZE;
+               bytes -= PAGE_CACHE_SIZE;
+               data = squashfs_next_page(output);
+       }
+       squashfs_finish_page(output);
+
+       return dest_len;
+}
+
+const struct squashfs_decompressor squashfs_lz4_comp_ops = {
+       .init = lz4_init,
+       .comp_opts = lz4_comp_opts,
+       .free = lz4_free,
+       .decompress = lz4_uncompress,
+       .id = LZ4_COMPRESSION,
+       .name = "lz4",
+       .supported = 1
+};
index 00f4dfc5f0884cb6d77920130883521b04635784..244b9fbfff7b299195585320328cfc7540e6887e 100644 (file)
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "page_actor.h"
 
 struct squashfs_lzo {
        void    *input;
        void    *output;
 };
 
-static void *lzo_init(struct squashfs_sb_info *msblk, void *buff, int len)
+static void *lzo_init(struct squashfs_sb_info *msblk, void *buff)
 {
        int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
 
@@ -74,22 +75,16 @@ static void lzo_free(void *strm)
 }
 
 
-static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
-       struct buffer_head **bh, int b, int offset, int length, int srclength,
-       int pages)
+static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
+       struct buffer_head **bh, int b, int offset, int length,
+       struct squashfs_page_actor *output)
 {
-       struct squashfs_lzo *stream = msblk->stream;
-       void *buff = stream->input;
+       struct squashfs_lzo *stream = strm;
+       void *buff = stream->input, *data;
        int avail, i, bytes = length, res;
-       size_t out_len = srclength;
-
-       mutex_lock(&msblk->read_data_mutex);
+       size_t out_len = output->length;
 
        for (i = 0; i < b; i++) {
-               wait_on_buffer(bh[i]);
-               if (!buffer_uptodate(bh[i]))
-                       goto block_release;
-
                avail = min(bytes, msblk->devblksize - offset);
                memcpy(buff, bh[i]->b_data + offset, avail);
                buff += avail;
@@ -104,24 +99,24 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
                goto failed;
 
        res = bytes = (int)out_len;
-       for (i = 0, buff = stream->output; bytes && i < pages; i++) {
-               avail = min_t(int, bytes, PAGE_CACHE_SIZE);
-               memcpy(buffer[i], buff, avail);
-               buff += avail;
-               bytes -= avail;
+       data = squashfs_first_page(output);
+       buff = stream->output;
+       while (data) {
+               if (bytes <= PAGE_CACHE_SIZE) {
+                       memcpy(data, buff, bytes);
+                       break;
+               } else {
+                       memcpy(data, buff, PAGE_CACHE_SIZE);
+                       buff += PAGE_CACHE_SIZE;
+                       bytes -= PAGE_CACHE_SIZE;
+                       data = squashfs_next_page(output);
+               }
        }
+       squashfs_finish_page(output);
 
-       mutex_unlock(&msblk->read_data_mutex);
        return res;
 
-block_release:
-       for (; i < b; i++)
-               put_bh(bh[i]);
-
 failed:
-       mutex_unlock(&msblk->read_data_mutex);
-
-       ERROR("lzo decompression failed, data probably corrupt\n");
        return -EIO;
 }
 
index 7834a517f7f422cfb9bc35f997e8c69e4e91e52d..67cad77fefb4ac165bfd92fbba02db473a02e22e 100644 (file)
@@ -79,7 +79,8 @@ static int get_dir_index_using_name(struct super_block *sb,
                        int len)
 {
        struct squashfs_sb_info *msblk = sb->s_fs_info;
-       int i, size, length = 0, err;
+       int i, length = 0, err;
+       unsigned int size;
        struct squashfs_dir_index *index;
        char *str;
 
@@ -103,6 +104,8 @@ static int get_dir_index_using_name(struct super_block *sb,
 
 
                size = le32_to_cpu(index->size) + 1;
+               if (size > SQUASHFS_NAME_LEN)
+                       break;
 
                err = squashfs_read_metadata(sb, index->name, &index_start,
                                        &index_offset, size);
@@ -144,7 +147,8 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry,
        struct squashfs_dir_entry *dire;
        u64 block = squashfs_i(dir)->start + msblk->directory_table;
        int offset = squashfs_i(dir)->offset;
-       int err, length, dir_count, size;
+       int err, length;
+       unsigned int dir_count, size;
 
        TRACE("Entered squashfs_lookup [%llx:%x]\n", block, offset);
 
diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c
new file mode 100644 (file)
index 0000000..5a1c11f
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include "page_actor.h"
+
+/*
+ * This file contains implementations of page_actor for decompressing into
+ * an intermediate buffer, and for decompressing directly into the
+ * page cache.
+ *
+ * Calling code should avoid sleeping between calls to squashfs_first_page()
+ * and squashfs_finish_page().
+ */
+
+/* Implementation of page_actor for decompressing into intermediate buffer */
+static void *cache_first_page(struct squashfs_page_actor *actor)
+{
+       actor->next_page = 1;
+       return actor->buffer[0];
+}
+
+static void *cache_next_page(struct squashfs_page_actor *actor)
+{
+       if (actor->next_page == actor->pages)
+               return NULL;
+
+       return actor->buffer[actor->next_page++];
+}
+
+static void cache_finish_page(struct squashfs_page_actor *actor)
+{
+       /* empty */
+}
+
+struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
+       int pages, int length)
+{
+       struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
+
+       if (actor == NULL)
+               return NULL;
+
+       actor->length = length ? : pages * PAGE_CACHE_SIZE;
+       actor->buffer = buffer;
+       actor->pages = pages;
+       actor->next_page = 0;
+       actor->squashfs_first_page = cache_first_page;
+       actor->squashfs_next_page = cache_next_page;
+       actor->squashfs_finish_page = cache_finish_page;
+       return actor;
+}
+
+/* Implementation of page_actor for decompressing directly into page cache. */
+static void *direct_first_page(struct squashfs_page_actor *actor)
+{
+       actor->next_page = 1;
+       return actor->pageaddr = kmap_atomic(actor->page[0]);
+}
+
+static void *direct_next_page(struct squashfs_page_actor *actor)
+{
+       if (actor->pageaddr)
+               kunmap_atomic(actor->pageaddr);
+
+       return actor->pageaddr = actor->next_page == actor->pages ? NULL :
+               kmap_atomic(actor->page[actor->next_page++]);
+}
+
+static void direct_finish_page(struct squashfs_page_actor *actor)
+{
+       if (actor->pageaddr)
+               kunmap_atomic(actor->pageaddr);
+}
+
+struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
+       int pages, int length)
+{
+       struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
+
+       if (actor == NULL)
+               return NULL;
+
+       actor->length = length ? : pages * PAGE_CACHE_SIZE;
+       actor->page = page;
+       actor->pages = pages;
+       actor->next_page = 0;
+       actor->pageaddr = NULL;
+       actor->squashfs_first_page = direct_first_page;
+       actor->squashfs_next_page = direct_next_page;
+       actor->squashfs_finish_page = direct_finish_page;
+       return actor;
+}
diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h
new file mode 100644 (file)
index 0000000..26dd820
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef PAGE_ACTOR_H
+#define PAGE_ACTOR_H
+/*
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef CONFIG_SQUASHFS_FILE_DIRECT
+struct squashfs_page_actor {
+       void    **page;
+       int     pages;
+       int     length;
+       int     next_page;
+};
+
+static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page,
+       int pages, int length)
+{
+       struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
+
+       if (actor == NULL)
+               return NULL;
+
+       actor->length = length ? : pages * PAGE_CACHE_SIZE;
+       actor->page = page;
+       actor->pages = pages;
+       actor->next_page = 0;
+       return actor;
+}
+
+static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
+{
+       actor->next_page = 1;
+       return actor->page[0];
+}
+
+static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
+{
+       return actor->next_page == actor->pages ? NULL :
+               actor->page[actor->next_page++];
+}
+
+static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
+{
+       /* empty */
+}
+#else
+struct squashfs_page_actor {
+       union {
+               void            **buffer;
+               struct page     **page;
+       };
+       void    *pageaddr;
+       void    *(*squashfs_first_page)(struct squashfs_page_actor *);
+       void    *(*squashfs_next_page)(struct squashfs_page_actor *);
+       void    (*squashfs_finish_page)(struct squashfs_page_actor *);
+       int     pages;
+       int     length;
+       int     next_page;
+};
+
+extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int);
+extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page
+                                                        **, int, int);
+static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
+{
+       return actor->squashfs_first_page(actor);
+}
+static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
+{
+       return actor->squashfs_next_page(actor);
+}
+static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
+{
+       actor->squashfs_finish_page(actor);
+}
+#endif
+#endif
index d1266516ed08494e93f4dc2ef00fe66be9216cb5..887d6d270080a6d8d945868c5bc1265d3f38f93c 100644 (file)
 
 #define ERROR(s, args...)      pr_err("SQUASHFS error: "s, ## args)
 
-#define WARNING(s, args...)    pr_warning("SQUASHFS: "s, ## args)
+#define WARNING(s, args...)    pr_warn("SQUASHFS: "s, ## args)
 
 /* block.c */
-extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *,
-                               int, int);
+extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
+                               struct squashfs_page_actor *);
 
 /* cache.c */
 extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
@@ -48,7 +48,14 @@ extern void *squashfs_read_table(struct super_block *, u64, int);
 
 /* decompressor.c */
 extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int);
-extern void *squashfs_decompressor_init(struct super_block *, unsigned short);
+extern void *squashfs_decompressor_setup(struct super_block *, unsigned short);
+
+/* decompressor_xxx.c */
+extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *);
+extern void squashfs_decompressor_destroy(struct squashfs_sb_info *);
+extern int squashfs_decompress(struct squashfs_sb_info *, struct buffer_head **,
+       int, int, int, struct squashfs_page_actor *);
+extern int squashfs_max_decompressors(void);
 
 /* export.c */
 extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64,
@@ -59,6 +66,13 @@ extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *);
 extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
                                u64, u64, unsigned int);
 
+/* file.c */
+void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int,
+                               int);
+
+/* file_xxx.c */
+extern int squashfs_readpage_block(struct page *, u64, int);
+
 /* id.c */
 extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
 extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64,
index 9e2349d07cb1c98334fd19ffbeb5441361f65edf..506f4ba5b98309b9041cc99103312837b2724e58 100644 (file)
@@ -87,7 +87,7 @@
 #define SQUASHFS_COMP_OPTS(flags)              SQUASHFS_BIT(flags, \
                                                SQUASHFS_COMP_OPT)
 
-/* Max number of types and file types */
+/* Inode types including extended types */
 #define SQUASHFS_DIR_TYPE              1
 #define SQUASHFS_REG_TYPE              2
 #define SQUASHFS_SYMLINK_TYPE          3
 #define SQUASHFS_LFIFO_TYPE            13
 #define SQUASHFS_LSOCKET_TYPE          14
 
+/* Max type value stored in directory entry */
+#define SQUASHFS_MAX_DIR_TYPE          7
+
 /* Xattr types */
 #define SQUASHFS_XATTR_USER             0
 #define SQUASHFS_XATTR_TRUSTED          1
@@ -237,6 +240,7 @@ struct meta_index {
 #define LZMA_COMPRESSION       2
 #define LZO_COMPRESSION                3
 #define XZ_COMPRESSION         4
+#define LZ4_COMPRESSION                5
 
 struct squashfs_super_block {
        __le32                  s_magic;
index 52934a22f29665a00f082b24aa57ce915e875913..1da565cb50c3d0f1e652671dd7a81577b598ca2f 100644 (file)
@@ -50,6 +50,7 @@ struct squashfs_cache_entry {
        wait_queue_head_t       wait_queue;
        struct squashfs_cache   *cache;
        void                    **data;
+       struct squashfs_page_actor      *actor;
 };
 
 struct squashfs_sb_info {
@@ -63,10 +64,9 @@ struct squashfs_sb_info {
        __le64                                  *id_table;
        __le64                                  *fragment_index;
        __le64                                  *xattr_id_table;
-       struct mutex                            read_data_mutex;
        struct mutex                            meta_index_mutex;
        struct meta_index                       *meta_index;
-       void                                    *stream;
+       struct squashfs_stream                  *stream;
        __le64                                  *inode_lookup_table;
        u64                                     inode_table;
        u64                                     directory_table;
index 60553a9053ca82b7264862e8168b61df0ce7de89..5056babe00df93249465c22b8b6dc6d0ebc1723d 100644 (file)
@@ -27,6 +27,8 @@
  * the filesystem.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/fs.h>
 #include <linux/vfs.h>
 #include <linux/slab.h>
@@ -98,7 +100,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
        msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE);
        msblk->devblksize_log2 = ffz(~msblk->devblksize);
 
-       mutex_init(&msblk->read_data_mutex);
        mutex_init(&msblk->meta_index_mutex);
 
        /*
@@ -206,13 +207,14 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
                goto failed_mount;
 
        /* Allocate read_page block */
-       msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size);
+       msblk->read_page = squashfs_cache_init("data",
+               squashfs_max_decompressors(), msblk->block_size);
        if (msblk->read_page == NULL) {
                ERROR("Failed to allocate read_page block\n");
                goto failed_mount;
        }
 
-       msblk->stream = squashfs_decompressor_init(sb, flags);
+       msblk->stream = squashfs_decompressor_setup(sb, flags);
        if (IS_ERR(msblk->stream)) {
                err = PTR_ERR(msblk->stream);
                msblk->stream = NULL;
@@ -336,7 +338,7 @@ failed_mount:
        squashfs_cache_delete(msblk->block_cache);
        squashfs_cache_delete(msblk->fragment_cache);
        squashfs_cache_delete(msblk->read_page);
-       squashfs_decompressor_free(msblk, msblk->stream);
+       squashfs_decompressor_destroy(msblk);
        kfree(msblk->inode_lookup_table);
        kfree(msblk->fragment_index);
        kfree(msblk->id_table);
@@ -371,6 +373,7 @@ static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 
 static int squashfs_remount(struct super_block *sb, int *flags, char *data)
 {
+       sync_filesystem(sb);
        *flags |= MS_RDONLY;
        return 0;
 }
@@ -383,7 +386,7 @@ static void squashfs_put_super(struct super_block *sb)
                squashfs_cache_delete(sbi->block_cache);
                squashfs_cache_delete(sbi->fragment_cache);
                squashfs_cache_delete(sbi->read_page);
-               squashfs_decompressor_free(sbi, sbi->stream);
+               squashfs_decompressor_destroy(sbi);
                kfree(sbi->id_table);
                kfree(sbi->fragment_index);
                kfree(sbi->meta_index);
@@ -447,8 +450,7 @@ static int __init init_squashfs_fs(void)
                return err;
        }
 
-       printk(KERN_INFO "squashfs: version 4.0 (2009/01/31) "
-               "Phillip Lougher\n");
+       pr_info("version 4.0 (2009/01/31) Phillip Lougher\n");
 
        return 0;
 }
index 1760b7d108f66a55614102c43713ef315592374e..c609624e4b8a8cf88152310337c2533be7bec5cb 100644 (file)
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "page_actor.h"
 
 struct squashfs_xz {
        struct xz_dec *state;
        struct xz_buf buf;
 };
 
-struct comp_opts {
+struct disk_comp_opts {
        __le32 dictionary_size;
        __le32 flags;
 };
 
-static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff,
-       int len)
+struct comp_opts {
+       int dict_size;
+};
+
+static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk,
+       void *buff, int len)
 {
-       struct comp_opts *comp_opts = buff;
-       struct squashfs_xz *stream;
-       int dict_size = msblk->block_size;
-       int err, n;
+       struct disk_comp_opts *comp_opts = buff;
+       struct comp_opts *opts;
+       int err = 0, n;
+
+       opts = kmalloc(sizeof(*opts), GFP_KERNEL);
+       if (opts == NULL) {
+               err = -ENOMEM;
+               goto out2;
+       }
 
        if (comp_opts) {
                /* check compressor options are the expected length */
                if (len < sizeof(*comp_opts)) {
                        err = -EIO;
-                       goto failed;
+                       goto out;
                }
 
-               dict_size = le32_to_cpu(comp_opts->dictionary_size);
+               opts->dict_size = le32_to_cpu(comp_opts->dictionary_size);
 
                /* the dictionary size should be 2^n or 2^n+2^(n+1) */
-               n = ffs(dict_size) - 1;
-               if (dict_size != (1 << n) && dict_size != (1 << n) +
+               n = ffs(opts->dict_size) - 1;
+               if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) +
                                                (1 << (n + 1))) {
                        err = -EIO;
-                       goto failed;
+                       goto out;
                }
-       }
+       } else
+               /* use defaults */
+               opts->dict_size = max_t(int, msblk->block_size,
+                                                       SQUASHFS_METADATA_SIZE);
+
+       return opts;
+
+out:
+       kfree(opts);
+out2:
+       return ERR_PTR(err);
+}
+
 
-       dict_size = max_t(int, dict_size, SQUASHFS_METADATA_SIZE);
+static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff)
+{
+       struct comp_opts *comp_opts = buff;
+       struct squashfs_xz *stream;
+       int err;
 
        stream = kmalloc(sizeof(*stream), GFP_KERNEL);
        if (stream == NULL) {
@@ -77,7 +103,7 @@ static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff,
                goto failed;
        }
 
-       stream->state = xz_dec_init(XZ_PREALLOC, dict_size);
+       stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size);
        if (stream->state == NULL) {
                kfree(stream);
                err = -ENOMEM;
@@ -103,42 +129,37 @@ static void squashfs_xz_free(void *strm)
 }
 
 
-static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer,
-       struct buffer_head **bh, int b, int offset, int length, int srclength,
-       int pages)
+static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
+       struct buffer_head **bh, int b, int offset, int length,
+       struct squashfs_page_actor *output)
 {
        enum xz_ret xz_err;
-       int avail, total = 0, k = 0, page = 0;
-       struct squashfs_xz *stream = msblk->stream;
-
-       mutex_lock(&msblk->read_data_mutex);
+       int avail, total = 0, k = 0;
+       struct squashfs_xz *stream = strm;
 
        xz_dec_reset(stream->state);
        stream->buf.in_pos = 0;
        stream->buf.in_size = 0;
        stream->buf.out_pos = 0;
        stream->buf.out_size = PAGE_CACHE_SIZE;
-       stream->buf.out = buffer[page++];
+       stream->buf.out = squashfs_first_page(output);
 
        do {
                if (stream->buf.in_pos == stream->buf.in_size && k < b) {
                        avail = min(length, msblk->devblksize - offset);
                        length -= avail;
-                       wait_on_buffer(bh[k]);
-                       if (!buffer_uptodate(bh[k]))
-                               goto release_mutex;
-
                        stream->buf.in = bh[k]->b_data + offset;
                        stream->buf.in_size = avail;
                        stream->buf.in_pos = 0;
                        offset = 0;
                }
 
-               if (stream->buf.out_pos == stream->buf.out_size
-                                                       && page < pages) {
-                       stream->buf.out = buffer[page++];
-                       stream->buf.out_pos = 0;
-                       total += PAGE_CACHE_SIZE;
+               if (stream->buf.out_pos == stream->buf.out_size) {
+                       stream->buf.out = squashfs_next_page(output);
+                       if (stream->buf.out != NULL) {
+                               stream->buf.out_pos = 0;
+                               total += PAGE_CACHE_SIZE;
+                       }
                }
 
                xz_err = xz_dec_run(stream->state, &stream->buf);
@@ -147,23 +168,14 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer,
                        put_bh(bh[k++]);
        } while (xz_err == XZ_OK);
 
-       if (xz_err != XZ_STREAM_END) {
-               ERROR("xz_dec_run error, data probably corrupt\n");
-               goto release_mutex;
-       }
-
-       if (k < b) {
-               ERROR("xz_uncompress error, input remaining\n");
-               goto release_mutex;
-       }
+       squashfs_finish_page(output);
 
-       total += stream->buf.out_pos;
-       mutex_unlock(&msblk->read_data_mutex);
-       return total;
+       if (xz_err != XZ_STREAM_END || k < b)
+               goto out;
 
-release_mutex:
-       mutex_unlock(&msblk->read_data_mutex);
+       return total + stream->buf.out_pos;
 
+out:
        for (; k < b; k++)
                put_bh(bh[k]);
 
@@ -172,6 +184,7 @@ release_mutex:
 
 const struct squashfs_decompressor squashfs_xz_comp_ops = {
        .init = squashfs_xz_init,
+       .comp_opts = squashfs_xz_comp_opts,
        .free = squashfs_xz_free,
        .decompress = squashfs_xz_uncompress,
        .id = XZ_COMPRESSION,
index 55d918fd2d862605beb85dae54c3a177b776381d..8727caba6882209ad62102c0148a09b64421626a 100644 (file)
@@ -32,8 +32,9 @@
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "page_actor.h"
 
-static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len)
+static void *zlib_init(struct squashfs_sb_info *dummy, void *buff)
 {
        z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL);
        if (stream == NULL)
@@ -61,44 +62,37 @@ static void zlib_free(void *strm)
 }
 
 
-static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
-       struct buffer_head **bh, int b, int offset, int length, int srclength,
-       int pages)
+static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
+       struct buffer_head **bh, int b, int offset, int length,
+       struct squashfs_page_actor *output)
 {
-       int zlib_err, zlib_init = 0;
-       int k = 0, page = 0;
-       z_stream *stream = msblk->stream;
-
-       mutex_lock(&msblk->read_data_mutex);
+       int zlib_err, zlib_init = 0, k = 0;
+       z_stream *stream = strm;
 
-       stream->avail_out = 0;
+       stream->avail_out = PAGE_CACHE_SIZE;
+       stream->next_out = squashfs_first_page(output);
        stream->avail_in = 0;
 
        do {
                if (stream->avail_in == 0 && k < b) {
                        int avail = min(length, msblk->devblksize - offset);
                        length -= avail;
-                       wait_on_buffer(bh[k]);
-                       if (!buffer_uptodate(bh[k]))
-                               goto release_mutex;
-
                        stream->next_in = bh[k]->b_data + offset;
                        stream->avail_in = avail;
                        offset = 0;
                }
 
-               if (stream->avail_out == 0 && page < pages) {
-                       stream->next_out = buffer[page++];
-                       stream->avail_out = PAGE_CACHE_SIZE;
+               if (stream->avail_out == 0) {
+                       stream->next_out = squashfs_next_page(output);
+                       if (stream->next_out != NULL)
+                               stream->avail_out = PAGE_CACHE_SIZE;
                }
 
                if (!zlib_init) {
                        zlib_err = zlib_inflateInit(stream);
                        if (zlib_err != Z_OK) {
-                               ERROR("zlib_inflateInit returned unexpected "
-                                       "result 0x%x, srclength %d\n",
-                                       zlib_err, srclength);
-                               goto release_mutex;
+                               squashfs_finish_page(output);
+                               goto out;
                        }
                        zlib_init = 1;
                }
@@ -109,29 +103,21 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
                        put_bh(bh[k++]);
        } while (zlib_err == Z_OK);
 
-       if (zlib_err != Z_STREAM_END) {
-               ERROR("zlib_inflate error, data probably corrupt\n");
-               goto release_mutex;
-       }
+       squashfs_finish_page(output);
 
-       zlib_err = zlib_inflateEnd(stream);
-       if (zlib_err != Z_OK) {
-               ERROR("zlib_inflate error, data probably corrupt\n");
-               goto release_mutex;
-       }
+       if (zlib_err != Z_STREAM_END)
+               goto out;
 
-       if (k < b) {
-               ERROR("zlib_uncompress error, data remaining\n");
-               goto release_mutex;
-       }
+       zlib_err = zlib_inflateEnd(stream);
+       if (zlib_err != Z_OK)
+               goto out;
 
-       length = stream->total_out;
-       mutex_unlock(&msblk->read_data_mutex);
-       return length;
+       if (k < b)
+               goto out;
 
-release_mutex:
-       mutex_unlock(&msblk->read_data_mutex);
+       return stream->total_out;
 
+out:
        for (; k < b; k++)
                put_bh(bh[k]);
 
index c327d4ee1235494e05ae1587b86ca357577b2292..4742e58f3fc520328a965b7a512fbcb3d0ec02a9 100644 (file)
@@ -60,6 +60,7 @@ static int sysv_remount(struct super_block *sb, int *flags, char *data)
 {
        struct sysv_sb_info *sbi = SYSV_SB(sb);
 
+       sync_filesystem(sb);
        if (sbi->s_forced_ro)
                *flags |= MS_RDONLY;
        return 0;
index 05115d719408f73085a09a1ef752839ceca50d19..3bf098a82dac66da2a7421b926270c0b56ff3c6b 100644 (file)
@@ -1840,6 +1840,7 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
        int err;
        struct ubifs_info *c = sb->s_fs_info;
 
+       sync_filesystem(sb);
        dbg_gen("old flags %#lx, new flags %#x", sb->s_flags, *flags);
 
        err = ubifs_parse_options(c, data, 1);
index 839a2bad7f45b693db4ed478598b997c42077712..32f5297dfbaba1a5f591b6e6f28402ddf24d56ad 100644 (file)
@@ -629,6 +629,7 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
        struct udf_options uopt;
        struct udf_sb_info *sbi = UDF_SB(sb);
        int error = 0;
+       sync_filesystem(sb);
 
        if (sbi->s_lvid_bh) {
                int write_rev = le16_to_cpu(udf_sb_lvidiu(sbi)->minUDFWriteRev);
index 329f2f53b7ed655b2ef525331f7c75a714d58de9..b8c6791f046fbe4195d97697bbe8a6ba29be03a0 100644 (file)
@@ -1280,6 +1280,7 @@ static int ufs_remount (struct super_block *sb, int *mount_flags, char *data)
        unsigned new_mount_opt, ufstype;
        unsigned flags;
 
+       sync_filesystem(sb);
        lock_ufs(sb);
        mutex_lock(&UFS_SB(sb)->s_lock);
        uspi = UFS_SB(sb)->s_uspi;
index 3033ba5e9762f19609a7f86d5117f64eb70f7bd4..478c0ad5e36fcd20d0cc077d6f49b8b44fe2b402 100644 (file)
@@ -1218,6 +1218,7 @@ xfs_fs_remount(
        char                    *p;
        int                     error;
 
+       sync_filesystem(sb);
        while ((p = strsep(&options, ",")) != NULL) {
                int token;
 
index d57bc5df7225af64e2a59f2756f988e7680e6cdb..9a3437377f718c2e24308a18513fe15d7c6e626d 100644 (file)
@@ -1506,6 +1506,17 @@ int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags);
  * to have different dirent layouts depending on the binary type.
  */
 typedef int (*filldir_t)(void *, const char *, int, loff_t, u64, unsigned);
+struct dir_context {
+       const filldir_t actor;
+       loff_t pos;
+};
+
+static inline bool dir_emit(struct dir_context *ctx,
+                           const char *name, int namelen,
+                           u64 ino, unsigned type)
+{
+       return ctx->actor(ctx, name, namelen, ctx->pos, ino, type) == 0;
+}
 struct block_device_operations;
 
 /* These macros are for out of kernel modules to test that
@@ -1522,6 +1533,7 @@ struct file_operations {
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        int (*readdir) (struct file *, void *, filldir_t);
+       int (*iterate) (struct file *, struct dir_context *);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
@@ -2495,6 +2507,7 @@ loff_t inode_get_bytes(struct inode *inode);
 void inode_set_bytes(struct inode *inode, loff_t bytes);
 
 extern int vfs_readdir(struct file *, filldir_t, void *);
+extern int iterate_dir(struct file *, struct dir_context *);
 
 extern int vfs_stat(const char __user *, struct kstat *);
 extern int vfs_lstat(const char __user *, struct kstat *);
diff --git a/include/linux/lz4.h b/include/linux/lz4.h
new file mode 100644 (file)
index 0000000..4356686
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef __LZ4_H__
+#define __LZ4_H__
+/*
+ * LZ4 Kernel Interface
+ *
+ * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define LZ4_MEM_COMPRESS       (4096 * sizeof(unsigned char *))
+#define LZ4HC_MEM_COMPRESS     (65538 * sizeof(unsigned char *))
+
+/*
+ * lz4_compressbound()
+ * Provides the maximum size that LZ4 may output in a "worst case" scenario
+ * (input data not compressible)
+ */
+static inline size_t lz4_compressbound(size_t isize)
+{
+       return isize + (isize / 255) + 16;
+}
+
+/*
+ * lz4_compress()
+ *     src     : source address of the original data
+ *     src_len : size of the original data
+ *     dst     : output buffer address of the compressed data
+ *             This requires 'dst' of size LZ4_COMPRESSBOUND.
+ *     dst_len : is the output size, which is returned after compress done
+ *     workmem : address of the working memory.
+ *             This requires 'workmem' of size LZ4_MEM_COMPRESS.
+ *     return  : Success if return 0
+ *               Error if return (< 0)
+ *     note :  Destination buffer and workmem must be already allocated with
+ *             the defined size.
+ */
+int lz4_compress(const unsigned char *src, size_t src_len,
+               unsigned char *dst, size_t *dst_len, void *wrkmem);
+
+ /*
+  * lz4hc_compress()
+  *     src     : source address of the original data
+  *     src_len : size of the original data
+  *     dst     : output buffer address of the compressed data
+  *            This requires 'dst' of size LZ4_COMPRESSBOUND.
+  *     dst_len : is the output size, which is returned after compress done
+  *     workmem : address of the working memory.
+  *            This requires 'workmem' of size LZ4HC_MEM_COMPRESS.
+  *     return  : Success if return 0
+  *               Error if return (< 0)
+  *     note :  Destination buffer and workmem must be already allocated with
+  *             the defined size.
+  */
+int lz4hc_compress(const unsigned char *src, size_t src_len,
+               unsigned char *dst, size_t *dst_len, void *wrkmem);
+
+/*
+ * lz4_decompress()
+ *     src     : source address of the compressed data
+ *     src_len : is the input size, whcih is returned after decompress done
+ *     dest    : output buffer address of the decompressed data
+ *     actual_dest_len: is the size of uncompressed data, supposing it's known
+ *     return  : Success if return 0
+ *               Error if return (< 0)
+ *     note :  Destination buffer must be already allocated.
+ *             slightly faster than lz4_decompress_unknownoutputsize()
+ */
+int lz4_decompress(const unsigned char *src, size_t *src_len,
+               unsigned char *dest, size_t actual_dest_len);
+
+/*
+ * lz4_decompress_unknownoutputsize()
+ *     src     : source address of the compressed data
+ *     src_len : is the input size, therefore the compressed size
+ *     dest    : output buffer address of the decompressed data
+ *     dest_len: is the max size of the destination buffer, which is
+ *                     returned with actual size of decompressed data after
+ *                     decompress done
+ *     return  : Success if return 0
+ *               Error if return (< 0)
+ *     note :  Destination buffer must be already allocated.
+ */
+int lz4_decompress_unknownoutputsize(const unsigned char *src, size_t src_len,
+               unsigned char *dest, size_t *dest_len);
+#endif
index 2122edf525adc9fd6cf9d4d8bfbe871db2ff88e1..ab40149553dc1e63a611ebc8202dd1fd6b573ef9 100644 (file)
@@ -1184,6 +1184,11 @@ static inline void update_hiwater_vm(struct mm_struct *mm)
                mm->hiwater_vm = mm->total_vm;
 }
 
+static inline void reset_mm_hiwater_rss(struct mm_struct *mm)
+{
+       mm->hiwater_rss = get_mm_rss(mm);
+}
+
 static inline void setmax_mm_hiwater_rss(unsigned long *maxrss,
                                         struct mm_struct *mm)
 {
index c91e2aae3fb125c1ec704ebacd5f19cac02bdc3d..1426681f7cf38691ff268ebdd92e4b226fc8cb44 100644 (file)
 #include <linux/atomic.h>
 #include <linux/uidgid.h>
 
+/*
+ * ifindex generation is per-net namespace, and loopback is
+ * always the 1st device in ns (see net_dev_init), thus any
+ * loopback device should get ifindex 1
+ */
+
+#define LOOPBACK_IFINDEX       1
+
 struct flowi_common {
        int     flowic_oif;
        int     flowic_iif;
@@ -85,7 +93,7 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif,
                                      kuid_t uid)
 {
        fl4->flowi4_oif = oif;
-       fl4->flowi4_iif = 0;
+       fl4->flowi4_iif = LOOPBACK_IFINDEX;
        fl4->flowi4_mark = mark;
        fl4->flowi4_tos = tos;
        fl4->flowi4_scope = scope;
index b176978274828206b784e7003e04c871bef582a3..b064d6dd14fba44ef4c8d48bc911bb231a671368 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/list.h>
 #include <linux/sysctl.h>
 
+#include <net/flow.h>
 #include <net/netns/core.h>
 #include <net/netns/mib.h>
 #include <net/netns/unix.h>
@@ -120,14 +121,6 @@ struct net {
        atomic_t                rt_genid;
 };
 
-/*
- * ifindex generation is per-net namespace, and loopback is
- * always the 1st device in ns (see net_dev_init), thus any
- * loopback device should get ifindex 1
- */
-
-#define LOOPBACK_IFINDEX       1
-
 #include <linux/seq_file_net.h>
 
 /* Init's network namespace */
index fe01d418b09ae4d13a15b59039ef27602d1ad0f3..06d94d885877f4f3bdc19edde8c3744332ad9415 100644 (file)
@@ -189,6 +189,15 @@ config LZO_COMPRESS
 config LZO_DECOMPRESS
        tristate
 
+config LZ4_COMPRESS
+       tristate
+
+config LZ4HC_COMPRESS
+       tristate
+
+config LZ4_DECOMPRESS
+       tristate
+
 source "lib/xz/Kconfig"
 
 #
index 3def8e1ce90512bb033fdc1baa7f40aa5a82335b..438665a8306b61f52175bf9b908902c1fa432a15 100644 (file)
@@ -76,6 +76,9 @@ obj-$(CONFIG_REED_SOLOMON) += reed_solomon/
 obj-$(CONFIG_BCH) += bch.o
 obj-$(CONFIG_LZO_COMPRESS) += lzo/
 obj-$(CONFIG_LZO_DECOMPRESS) += lzo/
+obj-$(CONFIG_LZ4_COMPRESS) += lz4/
+obj-$(CONFIG_LZ4HC_COMPRESS) += lz4/
+obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/
 obj-$(CONFIG_XZ_DEC) += xz/
 obj-$(CONFIG_RAID6_PQ) += raid6/
 
diff --git a/lib/lz4/Makefile b/lib/lz4/Makefile
new file mode 100644 (file)
index 0000000..8085d04
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_LZ4_COMPRESS) += lz4_compress.o
+obj-$(CONFIG_LZ4HC_COMPRESS) += lz4hc_compress.o
+obj-$(CONFIG_LZ4_DECOMPRESS) += lz4_decompress.o
diff --git a/lib/lz4/lz4_compress.c b/lib/lz4/lz4_compress.c
new file mode 100644 (file)
index 0000000..fd94058
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * LZ4 - Fast LZ compression algorithm
+ * Copyright (C) 2011-2012, Yann Collet.
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You can contact the author at :
+ * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
+ * - LZ4 source repository : http://code.google.com/p/lz4/
+ *
+ *  Changed for kernel use by:
+ *  Chanho Min <chanho.min@lge.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/lz4.h>
+#include <asm/unaligned.h>
+#include "lz4defs.h"
+
+/*
+ * LZ4_compressCtx :
+ * -----------------
+ * Compress 'isize' bytes from 'source' into an output buffer 'dest' of
+ * maximum size 'maxOutputSize'.  * If it cannot achieve it, compression
+ * will stop, and result of the function will be zero.
+ * return : the number of bytes written in buffer 'dest', or 0 if the
+ * compression fails
+ */
+static inline int lz4_compressctx(void *ctx,
+               const char *source,
+               char *dest,
+               int isize,
+               int maxoutputsize)
+{
+       HTYPE *hashtable = (HTYPE *)ctx;
+       const u8 *ip = (u8 *)source;
+#if LZ4_ARCH64
+       const BYTE * const base = ip;
+#else
+       const int base = 0;
+#endif
+       const u8 *anchor = ip;
+       const u8 *const iend = ip + isize;
+       const u8 *const mflimit = iend - MFLIMIT;
+       #define MATCHLIMIT (iend - LASTLITERALS)
+
+       u8 *op = (u8 *) dest;
+       u8 *const oend = op + maxoutputsize;
+       int length;
+       const int skipstrength = SKIPSTRENGTH;
+       u32 forwardh;
+       int lastrun;
+
+       /* Init */
+       if (isize < MINLENGTH)
+               goto _last_literals;
+
+       memset((void *)hashtable, 0, LZ4_MEM_COMPRESS);
+
+       /* First Byte */
+       hashtable[LZ4_HASH_VALUE(ip)] = ip - base;
+       ip++;
+       forwardh = LZ4_HASH_VALUE(ip);
+
+       /* Main Loop */
+       for (;;) {
+               int findmatchattempts = (1U << skipstrength) + 3;
+               const u8 *forwardip = ip;
+               const u8 *ref;
+               u8 *token;
+
+               /* Find a match */
+               do {
+                       u32 h = forwardh;
+                       int step = findmatchattempts++ >> skipstrength;
+                       ip = forwardip;
+                       forwardip = ip + step;
+
+                       if (unlikely(forwardip > mflimit))
+                               goto _last_literals;
+
+                       forwardh = LZ4_HASH_VALUE(forwardip);
+                       ref = base + hashtable[h];
+                       hashtable[h] = ip - base;
+               } while ((ref < ip - MAX_DISTANCE) || (A32(ref) != A32(ip)));
+
+               /* Catch up */
+               while ((ip > anchor) && (ref > (u8 *)source) &&
+                       unlikely(ip[-1] == ref[-1])) {
+                       ip--;
+                       ref--;
+               }
+
+               /* Encode Literal length */
+               length = (int)(ip - anchor);
+               token = op++;
+               /* check output limit */
+               if (unlikely(op + length + (2 + 1 + LASTLITERALS) +
+                       (length >> 8) > oend))
+                       return 0;
+
+               if (length >= (int)RUN_MASK) {
+                       int len;
+                       *token = (RUN_MASK << ML_BITS);
+                       len = length - RUN_MASK;
+                       for (; len > 254 ; len -= 255)
+                               *op++ = 255;
+                       *op++ = (u8)len;
+               } else
+                       *token = (length << ML_BITS);
+
+               /* Copy Literals */
+               LZ4_BLINDCOPY(anchor, op, length);
+_next_match:
+               /* Encode Offset */
+               LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref));
+
+               /* Start Counting */
+               ip += MINMATCH;
+               /* MinMatch verified */
+               ref += MINMATCH;
+               anchor = ip;
+               while (likely(ip < MATCHLIMIT - (STEPSIZE - 1))) {
+                       #if LZ4_ARCH64
+                       u64 diff = A64(ref) ^ A64(ip);
+                       #else
+                       u32 diff = A32(ref) ^ A32(ip);
+                       #endif
+                       if (!diff) {
+                               ip += STEPSIZE;
+                               ref += STEPSIZE;
+                               continue;
+                       }
+                       ip += LZ4_NBCOMMONBYTES(diff);
+                       goto _endcount;
+               }
+               #if LZ4_ARCH64
+               if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) {
+                       ip += 4;
+                       ref += 4;
+               }
+               #endif
+               if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) {
+                       ip += 2;
+                       ref += 2;
+               }
+               if ((ip < MATCHLIMIT) && (*ref == *ip))
+                       ip++;
+_endcount:
+               /* Encode MatchLength */
+               length = (int)(ip - anchor);
+               /* Check output limit */
+               if (unlikely(op + (1 + LASTLITERALS) + (length >> 8) > oend))
+                       return 0;
+               if (length >= (int)ML_MASK) {
+                       *token += ML_MASK;
+                       length -= ML_MASK;
+                       for (; length > 509 ; length -= 510) {
+                               *op++ = 255;
+                               *op++ = 255;
+                       }
+                       if (length > 254) {
+                               length -= 255;
+                               *op++ = 255;
+                       }
+                       *op++ = (u8)length;
+               } else
+                       *token += length;
+
+               /* Test end of chunk */
+               if (ip > mflimit) {
+                       anchor = ip;
+                       break;
+               }
+
+               /* Fill table */
+               hashtable[LZ4_HASH_VALUE(ip-2)] = ip - 2 - base;
+
+               /* Test next position */
+               ref = base + hashtable[LZ4_HASH_VALUE(ip)];
+               hashtable[LZ4_HASH_VALUE(ip)] = ip - base;
+               if ((ref > ip - (MAX_DISTANCE + 1)) && (A32(ref) == A32(ip))) {
+                       token = op++;
+                       *token = 0;
+                       goto _next_match;
+               }
+
+               /* Prepare next loop */
+               anchor = ip++;
+               forwardh = LZ4_HASH_VALUE(ip);
+       }
+
+_last_literals:
+       /* Encode Last Literals */
+       lastrun = (int)(iend - anchor);
+       if (((char *)op - dest) + lastrun + 1
+               + ((lastrun + 255 - RUN_MASK) / 255) > (u32)maxoutputsize)
+               return 0;
+
+       if (lastrun >= (int)RUN_MASK) {
+               *op++ = (RUN_MASK << ML_BITS);
+               lastrun -= RUN_MASK;
+               for (; lastrun > 254 ; lastrun -= 255)
+                       *op++ = 255;
+               *op++ = (u8)lastrun;
+       } else
+               *op++ = (lastrun << ML_BITS);
+       memcpy(op, anchor, iend - anchor);
+       op += iend - anchor;
+
+       /* End */
+       return (int)(((char *)op) - dest);
+}
+
+static inline int lz4_compress64kctx(void *ctx,
+               const char *source,
+               char *dest,
+               int isize,
+               int maxoutputsize)
+{
+       u16 *hashtable = (u16 *)ctx;
+       const u8 *ip = (u8 *) source;
+       const u8 *anchor = ip;
+       const u8 *const base = ip;
+       const u8 *const iend = ip + isize;
+       const u8 *const mflimit = iend - MFLIMIT;
+       #define MATCHLIMIT (iend - LASTLITERALS)
+
+       u8 *op = (u8 *) dest;
+       u8 *const oend = op + maxoutputsize;
+       int len, length;
+       const int skipstrength = SKIPSTRENGTH;
+       u32 forwardh;
+       int lastrun;
+
+       /* Init */
+       if (isize < MINLENGTH)
+               goto _last_literals;
+
+       memset((void *)hashtable, 0, LZ4_MEM_COMPRESS);
+
+       /* First Byte */
+       ip++;
+       forwardh = LZ4_HASH64K_VALUE(ip);
+
+       /* Main Loop */
+       for (;;) {
+               int findmatchattempts = (1U << skipstrength) + 3;
+               const u8 *forwardip = ip;
+               const u8 *ref;
+               u8 *token;
+
+               /* Find a match */
+               do {
+                       u32 h = forwardh;
+                       int step = findmatchattempts++ >> skipstrength;
+                       ip = forwardip;
+                       forwardip = ip + step;
+
+                       if (forwardip > mflimit)
+                               goto _last_literals;
+
+                       forwardh = LZ4_HASH64K_VALUE(forwardip);
+                       ref = base + hashtable[h];
+                       hashtable[h] = (u16)(ip - base);
+               } while (A32(ref) != A32(ip));
+
+               /* Catch up */
+               while ((ip > anchor) && (ref > (u8 *)source)
+                       && (ip[-1] == ref[-1])) {
+                       ip--;
+                       ref--;
+               }
+
+               /* Encode Literal length */
+               length = (int)(ip - anchor);
+               token = op++;
+               /* Check output limit */
+               if (unlikely(op + length + (2 + 1 + LASTLITERALS)
+                       + (length >> 8) > oend))
+                       return 0;
+               if (length >= (int)RUN_MASK) {
+                       *token = (RUN_MASK << ML_BITS);
+                       len = length - RUN_MASK;
+                       for (; len > 254 ; len -= 255)
+                               *op++ = 255;
+                       *op++ = (u8)len;
+               } else
+                       *token = (length << ML_BITS);
+
+               /* Copy Literals */
+               LZ4_BLINDCOPY(anchor, op, length);
+
+_next_match:
+               /* Encode Offset */
+               LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref));
+
+               /* Start Counting */
+               ip += MINMATCH;
+               /* MinMatch verified */
+               ref += MINMATCH;
+               anchor = ip;
+
+               while (ip < MATCHLIMIT - (STEPSIZE - 1)) {
+                       #if LZ4_ARCH64
+                       u64 diff = A64(ref) ^ A64(ip);
+                       #else
+                       u32 diff = A32(ref) ^ A32(ip);
+                       #endif
+
+                       if (!diff) {
+                               ip += STEPSIZE;
+                               ref += STEPSIZE;
+                               continue;
+                       }
+                       ip += LZ4_NBCOMMONBYTES(diff);
+                       goto _endcount;
+               }
+               #if LZ4_ARCH64
+               if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) {
+                       ip += 4;
+                       ref += 4;
+               }
+               #endif
+               if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) {
+                       ip += 2;
+                       ref += 2;
+               }
+               if ((ip < MATCHLIMIT) && (*ref == *ip))
+                       ip++;
+_endcount:
+
+               /* Encode MatchLength */
+               len = (int)(ip - anchor);
+               /* Check output limit */
+               if (unlikely(op + (1 + LASTLITERALS) + (len >> 8) > oend))
+                       return 0;
+               if (len >= (int)ML_MASK) {
+                       *token += ML_MASK;
+                       len -= ML_MASK;
+                       for (; len > 509 ; len -= 510) {
+                               *op++ = 255;
+                               *op++ = 255;
+                       }
+                       if (len > 254) {
+                               len -= 255;
+                               *op++ = 255;
+                       }
+                       *op++ = (u8)len;
+               } else
+                       *token += len;
+
+               /* Test end of chunk */
+               if (ip > mflimit) {
+                       anchor = ip;
+                       break;
+               }
+
+               /* Fill table */
+               hashtable[LZ4_HASH64K_VALUE(ip-2)] = (u16)(ip - 2 - base);
+
+               /* Test next position */
+               ref = base + hashtable[LZ4_HASH64K_VALUE(ip)];
+               hashtable[LZ4_HASH64K_VALUE(ip)] = (u16)(ip - base);
+               if (A32(ref) == A32(ip)) {
+                       token = op++;
+                       *token = 0;
+                       goto _next_match;
+               }
+
+               /* Prepare next loop */
+               anchor = ip++;
+               forwardh = LZ4_HASH64K_VALUE(ip);
+       }
+
+_last_literals:
+       /* Encode Last Literals */
+       lastrun = (int)(iend - anchor);
+       if (op + lastrun + 1 + (lastrun - RUN_MASK + 255) / 255 > oend)
+               return 0;
+       if (lastrun >= (int)RUN_MASK) {
+               *op++ = (RUN_MASK << ML_BITS);
+               lastrun -= RUN_MASK;
+               for (; lastrun > 254 ; lastrun -= 255)
+                       *op++ = 255;
+               *op++ = (u8)lastrun;
+       } else
+               *op++ = (lastrun << ML_BITS);
+       memcpy(op, anchor, iend - anchor);
+       op += iend - anchor;
+       /* End */
+       return (int)(((char *)op) - dest);
+}
+
+int lz4_compress(const unsigned char *src, size_t src_len,
+                       unsigned char *dst, size_t *dst_len, void *wrkmem)
+{
+       int ret = -1;
+       int out_len = 0;
+
+       if (src_len < LZ4_64KLIMIT)
+               out_len = lz4_compress64kctx(wrkmem, src, dst, src_len,
+                               lz4_compressbound(src_len));
+       else
+               out_len = lz4_compressctx(wrkmem, src, dst, src_len,
+                               lz4_compressbound(src_len));
+
+       if (out_len < 0)
+               goto exit;
+
+       *dst_len = out_len;
+
+       return 0;
+exit:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(lz4_compress);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LZ4 compressor");
diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c
new file mode 100644 (file)
index 0000000..677d1ea
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * LZ4 Decompressor for Linux kernel
+ *
+ * Copyright (C) 2013 LG Electronics Co., Ltd. (http://www.lge.com/)
+ *
+ * Based on LZ4 implementation by Yann Collet.
+ *
+ * LZ4 - Fast LZ compression algorithm
+ * Copyright (C) 2011-2012, Yann Collet.
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You can contact the author at :
+ *  - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
+ *  - LZ4 source repository : http://code.google.com/p/lz4/
+ */
+
+#ifndef STATIC
+#include <linux/module.h>
+#include <linux/kernel.h>
+#endif
+#include <linux/lz4.h>
+
+#include <asm/unaligned.h>
+
+#include "lz4defs.h"
+
+static int lz4_uncompress(const char *source, char *dest, int osize)
+{
+       const BYTE *ip = (const BYTE *) source;
+       const BYTE *ref;
+       BYTE *op = (BYTE *) dest;
+       BYTE * const oend = op + osize;
+       BYTE *cpy;
+       unsigned token;
+       size_t length;
+       size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0};
+#if LZ4_ARCH64
+       size_t dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
+#endif
+
+       while (1) {
+
+               /* get runlength */
+               token = *ip++;
+               length = (token >> ML_BITS);
+               if (length == RUN_MASK) {
+                       size_t len;
+
+                       len = *ip++;
+                       for (; len == 255; length += 255)
+                               len = *ip++;
+                       length += len;
+               }
+
+               /* copy literals */
+               cpy = op + length;
+               if (unlikely(cpy > oend - COPYLENGTH)) {
+                       /*
+                        * Error: not enough place for another match
+                        * (min 4) + 5 literals
+                        */
+                       if (cpy != oend)
+                               goto _output_error;
+
+                       memcpy(op, ip, length);
+                       ip += length;
+                       break; /* EOF */
+               }
+               LZ4_WILDCOPY(ip, op, cpy);
+               ip -= (op - cpy);
+               op = cpy;
+
+               /* get offset */
+               LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip);
+               ip += 2;
+
+               /* Error: offset create reference outside destination buffer */
+               if (unlikely(ref < (BYTE *const) dest))
+                       goto _output_error;
+
+               /* get matchlength */
+               length = token & ML_MASK;
+               if (length == ML_MASK) {
+                       for (; *ip == 255; length += 255)
+                               ip++;
+                       length += *ip++;
+               }
+
+               /* copy repeated sequence */
+               if (unlikely((op - ref) < STEPSIZE)) {
+#if LZ4_ARCH64
+                       size_t dec64 = dec64table[op - ref];
+#else
+                       const int dec64 = 0;
+#endif
+                       op[0] = ref[0];
+                       op[1] = ref[1];
+                       op[2] = ref[2];
+                       op[3] = ref[3];
+                       op += 4;
+                       ref += 4;
+                       ref -= dec32table[op-ref];
+                       PUT4(ref, op);
+                       op += STEPSIZE - 4;
+                       ref -= dec64;
+               } else {
+                       LZ4_COPYSTEP(ref, op);
+               }
+               cpy = op + length - (STEPSIZE - 4);
+               if (cpy > (oend - COPYLENGTH)) {
+
+                       /* Error: request to write beyond destination buffer */
+                       if (cpy > oend)
+                               goto _output_error;
+                       LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
+                       while (op < cpy)
+                               *op++ = *ref++;
+                       op = cpy;
+                       /*
+                        * Check EOF (should never happen, since last 5 bytes
+                        * are supposed to be literals)
+                        */
+                       if (op == oend)
+                               goto _output_error;
+                       continue;
+               }
+               LZ4_SECURECOPY(ref, op, cpy);
+               op = cpy; /* correction */
+       }
+       /* end of decoding */
+       return (int) (((char *)ip) - source);
+
+       /* write overflow error detected */
+_output_error:
+       return (int) (-(((char *)ip) - source));
+}
+
+static int lz4_uncompress_unknownoutputsize(const char *source, char *dest,
+                               int isize, size_t maxoutputsize)
+{
+       const BYTE *ip = (const BYTE *) source;
+       const BYTE *const iend = ip + isize;
+       const BYTE *ref;
+
+
+       BYTE *op = (BYTE *) dest;
+       BYTE * const oend = op + maxoutputsize;
+       BYTE *cpy;
+
+       size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0};
+#if LZ4_ARCH64
+       size_t dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
+#endif
+
+       /* Main Loop */
+       while (ip < iend) {
+
+               unsigned token;
+               size_t length;
+
+               /* get runlength */
+               token = *ip++;
+               length = (token >> ML_BITS);
+               if (length == RUN_MASK) {
+                       int s = 255;
+                       while ((ip < iend) && (s == 255)) {
+                               s = *ip++;
+                               length += s;
+                       }
+               }
+               /* copy literals */
+               cpy = op + length;
+               if ((cpy > oend - COPYLENGTH) ||
+                       (ip + length > iend - COPYLENGTH)) {
+
+                       if (cpy > oend)
+                               goto _output_error;/* writes beyond buffer */
+
+                       if (ip + length != iend)
+                               goto _output_error;/*
+                                                   * Error: LZ4 format requires
+                                                   * to consume all input
+                                                   * at this stage
+                                                   */
+                       memcpy(op, ip, length);
+                       op += length;
+                       break;/* Necessarily EOF, due to parsing restrictions */
+               }
+               LZ4_WILDCOPY(ip, op, cpy);
+               ip -= (op - cpy);
+               op = cpy;
+
+               /* get offset */
+               LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip);
+               ip += 2;
+               if (ref < (BYTE * const) dest)
+                       goto _output_error;
+                       /*
+                        * Error : offset creates reference
+                        * outside of destination buffer
+                        */
+
+               /* get matchlength */
+               length = (token & ML_MASK);
+               if (length == ML_MASK) {
+                       while (ip < iend) {
+                               int s = *ip++;
+                               length += s;
+                               if (s == 255)
+                                       continue;
+                               break;
+                       }
+               }
+
+               /* copy repeated sequence */
+               if (unlikely((op - ref) < STEPSIZE)) {
+#if LZ4_ARCH64
+                       size_t dec64 = dec64table[op - ref];
+#else
+                       const int dec64 = 0;
+#endif
+                               op[0] = ref[0];
+                               op[1] = ref[1];
+                               op[2] = ref[2];
+                               op[3] = ref[3];
+                               op += 4;
+                               ref += 4;
+                               ref -= dec32table[op - ref];
+                               PUT4(ref, op);
+                               op += STEPSIZE - 4;
+                               ref -= dec64;
+               } else {
+                       LZ4_COPYSTEP(ref, op);
+               }
+               cpy = op + length - (STEPSIZE-4);
+               if (cpy > oend - COPYLENGTH) {
+                       if (cpy > oend)
+                               goto _output_error; /* write outside of buf */
+
+                       LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
+                       while (op < cpy)
+                               *op++ = *ref++;
+                       op = cpy;
+                       /*
+                        * Check EOF (should never happen, since last 5 bytes
+                        * are supposed to be literals)
+                        */
+                       if (op == oend)
+                               goto _output_error;
+                       continue;
+               }
+               LZ4_SECURECOPY(ref, op, cpy);
+               op = cpy; /* correction */
+       }
+       /* end of decoding */
+       return (int) (((char *) op) - dest);
+
+       /* write overflow error detected */
+_output_error:
+       return (int) (-(((char *) ip) - source));
+}
+
+int lz4_decompress(const unsigned char *src, size_t *src_len,
+               unsigned char *dest, size_t actual_dest_len)
+{
+       int ret = -1;
+       int input_len = 0;
+
+       input_len = lz4_uncompress(src, dest, actual_dest_len);
+       if (input_len < 0)
+               goto exit_0;
+       *src_len = input_len;
+
+       return 0;
+exit_0:
+       return ret;
+}
+#ifndef STATIC
+EXPORT_SYMBOL_GPL(lz4_decompress);
+#endif
+
+int lz4_decompress_unknownoutputsize(const unsigned char *src, size_t src_len,
+               unsigned char *dest, size_t *dest_len)
+{
+       int ret = -1;
+       int out_len = 0;
+
+       out_len = lz4_uncompress_unknownoutputsize(src, dest, src_len,
+                                       *dest_len);
+       if (out_len < 0)
+               goto exit_0;
+       *dest_len = out_len;
+
+       return 0;
+exit_0:
+       return ret;
+}
+#ifndef STATIC
+EXPORT_SYMBOL_GPL(lz4_decompress_unknownoutputsize);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LZ4 Decompressor");
+#endif
diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h
new file mode 100644 (file)
index 0000000..abcecdc
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * lz4defs.h -- architecture specific defines
+ *
+ * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * Detects 64 bits mode
+ */
+#if (defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) \
+       || defined(__ppc64__) || defined(__LP64__))
+#define LZ4_ARCH64 1
+#else
+#define LZ4_ARCH64 0
+#endif
+
+/*
+ * Architecture-specific macros
+ */
+#define BYTE   u8
+typedef struct _U16_S { u16 v; } U16_S;
+typedef struct _U32_S { u32 v; } U32_S;
+typedef struct _U64_S { u64 v; } U64_S;
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)            \
+       || defined(CONFIG_ARM) && __LINUX_ARM_ARCH__ >= 6       \
+       && defined(ARM_EFFICIENT_UNALIGNED_ACCESS)
+
+#define A16(x) (((U16_S *)(x))->v)
+#define A32(x) (((U32_S *)(x))->v)
+#define A64(x) (((U64_S *)(x))->v)
+
+#define PUT4(s, d) (A32(d) = A32(s))
+#define PUT8(s, d) (A64(d) = A64(s))
+#define LZ4_WRITE_LITTLEENDIAN_16(p, v)        \
+       do {    \
+               A16(p) = v; \
+               p += 2; \
+       } while (0)
+#else /* CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS */
+
+#define A64(x) get_unaligned((u64 *)&(((U16_S *)(x))->v))
+#define A32(x) get_unaligned((u32 *)&(((U16_S *)(x))->v))
+#define A16(x) get_unaligned((u16 *)&(((U16_S *)(x))->v))
+
+#define PUT4(s, d) \
+       put_unaligned(get_unaligned((const u32 *) s), (u32 *) d)
+#define PUT8(s, d) \
+       put_unaligned(get_unaligned((const u64 *) s), (u64 *) d)
+
+#define LZ4_WRITE_LITTLEENDIAN_16(p, v)        \
+       do {    \
+               put_unaligned(v, (u16 *)(p)); \
+               p += 2; \
+       } while (0)
+#endif
+
+#define COPYLENGTH 8
+#define ML_BITS  4
+#define ML_MASK  ((1U << ML_BITS) - 1)
+#define RUN_BITS (8 - ML_BITS)
+#define RUN_MASK ((1U << RUN_BITS) - 1)
+#define MEMORY_USAGE   14
+#define MINMATCH       4
+#define SKIPSTRENGTH   6
+#define LASTLITERALS   5
+#define MFLIMIT                (COPYLENGTH + MINMATCH)
+#define MINLENGTH      (MFLIMIT + 1)
+#define MAXD_LOG       16
+#define MAXD           (1 << MAXD_LOG)
+#define MAXD_MASK      (u32)(MAXD - 1)
+#define MAX_DISTANCE   (MAXD - 1)
+#define HASH_LOG       (MAXD_LOG - 1)
+#define HASHTABLESIZE  (1 << HASH_LOG)
+#define MAX_NB_ATTEMPTS        256
+#define OPTIMAL_ML     (int)((ML_MASK-1)+MINMATCH)
+#define LZ4_64KLIMIT   ((1<<16) + (MFLIMIT - 1))
+#define HASHLOG64K     ((MEMORY_USAGE - 2) + 1)
+#define HASH64KTABLESIZE       (1U << HASHLOG64K)
+#define LZ4_HASH_VALUE(p)      (((A32(p)) * 2654435761U) >> \
+                               ((MINMATCH * 8) - (MEMORY_USAGE-2)))
+#define LZ4_HASH64K_VALUE(p)   (((A32(p)) * 2654435761U) >> \
+                               ((MINMATCH * 8) - HASHLOG64K))
+#define HASH_VALUE(p)          (((A32(p)) * 2654435761U) >> \
+                               ((MINMATCH * 8) - HASH_LOG))
+
+#if LZ4_ARCH64/* 64-bit */
+#define STEPSIZE 8
+
+#define LZ4_COPYSTEP(s, d)     \
+       do {                    \
+               PUT8(s, d);     \
+               d += 8;         \
+               s += 8;         \
+       } while (0)
+
+#define LZ4_COPYPACKET(s, d)   LZ4_COPYSTEP(s, d)
+
+#define LZ4_SECURECOPY(s, d, e)                        \
+       do {                                    \
+               if (d < e) {                    \
+                       LZ4_WILDCOPY(s, d, e);  \
+               }                               \
+       } while (0)
+#define HTYPE u32
+
+#ifdef __BIG_ENDIAN
+#define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3)
+#else
+#define LZ4_NBCOMMONBYTES(val) (__builtin_ctzll(val) >> 3)
+#endif
+
+#else  /* 32-bit */
+#define STEPSIZE 4
+
+#define LZ4_COPYSTEP(s, d)     \
+       do {                    \
+               PUT4(s, d);     \
+               d += 4;         \
+               s += 4;         \
+       } while (0)
+
+#define LZ4_COPYPACKET(s, d)           \
+       do {                            \
+               LZ4_COPYSTEP(s, d);     \
+               LZ4_COPYSTEP(s, d);     \
+       } while (0)
+
+#define LZ4_SECURECOPY LZ4_WILDCOPY
+#define HTYPE const u8*
+
+#ifdef __BIG_ENDIAN
+#define LZ4_NBCOMMONBYTES(val) (__builtin_clz(val) >> 3)
+#else
+#define LZ4_NBCOMMONBYTES(val) (__builtin_ctz(val) >> 3)
+#endif
+
+#endif
+
+#define LZ4_READ_LITTLEENDIAN_16(d, s, p) \
+       (d = s - get_unaligned_le16(p))
+
+#define LZ4_WILDCOPY(s, d, e)          \
+       do {                            \
+               LZ4_COPYPACKET(s, d);   \
+       } while (d < e)
+
+#define LZ4_BLINDCOPY(s, d, l) \
+       do {    \
+               u8 *e = (d) + l;        \
+               LZ4_WILDCOPY(s, d, e);  \
+               d = e;  \
+       } while (0)
diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c
new file mode 100644 (file)
index 0000000..eb1a74f
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+ * LZ4 HC - High Compression Mode of LZ4
+ * Copyright (C) 2011-2012, Yann Collet.
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You can contact the author at :
+ * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
+ * - LZ4 source repository : http://code.google.com/p/lz4/
+ *
+ *  Changed for kernel use by:
+ *  Chanho Min <chanho.min@lge.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/lz4.h>
+#include <asm/unaligned.h>
+#include "lz4defs.h"
+
+struct lz4hc_data {
+       const u8 *base;
+       HTYPE hashtable[HASHTABLESIZE];
+       u16 chaintable[MAXD];
+       const u8 *nexttoupdate;
+} __attribute__((__packed__));
+
+static inline int lz4hc_init(struct lz4hc_data *hc4, const u8 *base)
+{
+       memset((void *)hc4->hashtable, 0, sizeof(hc4->hashtable));
+       memset(hc4->chaintable, 0xFF, sizeof(hc4->chaintable));
+
+#if LZ4_ARCH64
+       hc4->nexttoupdate = base + 1;
+#else
+       hc4->nexttoupdate = base;
+#endif
+       hc4->base = base;
+       return 1;
+}
+
+/* Update chains up to ip (excluded) */
+static inline void lz4hc_insert(struct lz4hc_data *hc4, const u8 *ip)
+{
+       u16 *chaintable = hc4->chaintable;
+       HTYPE *hashtable  = hc4->hashtable;
+#if LZ4_ARCH64
+       const BYTE * const base = hc4->base;
+#else
+       const int base = 0;
+#endif
+
+       while (hc4->nexttoupdate < ip) {
+               const u8 *p = hc4->nexttoupdate;
+               size_t delta = p - (hashtable[HASH_VALUE(p)] + base);
+               if (delta > MAX_DISTANCE)
+                       delta = MAX_DISTANCE;
+               chaintable[(size_t)(p) & MAXD_MASK] = (u16)delta;
+               hashtable[HASH_VALUE(p)] = (p) - base;
+               hc4->nexttoupdate++;
+       }
+}
+
+static inline size_t lz4hc_commonlength(const u8 *p1, const u8 *p2,
+               const u8 *const matchlimit)
+{
+       const u8 *p1t = p1;
+
+       while (p1t < matchlimit - (STEPSIZE - 1)) {
+#if LZ4_ARCH64
+               u64 diff = A64(p2) ^ A64(p1t);
+#else
+               u32 diff = A32(p2) ^ A32(p1t);
+#endif
+               if (!diff) {
+                       p1t += STEPSIZE;
+                       p2 += STEPSIZE;
+                       continue;
+               }
+               p1t += LZ4_NBCOMMONBYTES(diff);
+               return p1t - p1;
+       }
+#if LZ4_ARCH64
+       if ((p1t < (matchlimit-3)) && (A32(p2) == A32(p1t))) {
+               p1t += 4;
+               p2 += 4;
+       }
+#endif
+
+       if ((p1t < (matchlimit - 1)) && (A16(p2) == A16(p1t))) {
+               p1t += 2;
+               p2 += 2;
+       }
+       if ((p1t < matchlimit) && (*p2 == *p1t))
+               p1t++;
+       return p1t - p1;
+}
+
+static inline int lz4hc_insertandfindbestmatch(struct lz4hc_data *hc4,
+               const u8 *ip, const u8 *const matchlimit, const u8 **matchpos)
+{
+       u16 *const chaintable = hc4->chaintable;
+       HTYPE *const hashtable = hc4->hashtable;
+       const u8 *ref;
+#if LZ4_ARCH64
+       const BYTE * const base = hc4->base;
+#else
+       const int base = 0;
+#endif
+       int nbattempts = MAX_NB_ATTEMPTS;
+       size_t repl = 0, ml = 0;
+       u16 delta;
+
+       /* HC4 match finder */
+       lz4hc_insert(hc4, ip);
+       ref = hashtable[HASH_VALUE(ip)] + base;
+
+       /* potential repetition */
+       if (ref >= ip-4) {
+               /* confirmed */
+               if (A32(ref) == A32(ip)) {
+                       delta = (u16)(ip-ref);
+                       repl = ml  = lz4hc_commonlength(ip + MINMATCH,
+                                       ref + MINMATCH, matchlimit) + MINMATCH;
+                       *matchpos = ref;
+               }
+               ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
+       }
+
+       while ((ref >= ip - MAX_DISTANCE) && nbattempts) {
+               nbattempts--;
+               if (*(ref + ml) == *(ip + ml)) {
+                       if (A32(ref) == A32(ip)) {
+                               size_t mlt =
+                                       lz4hc_commonlength(ip + MINMATCH,
+                                       ref + MINMATCH, matchlimit) + MINMATCH;
+                               if (mlt > ml) {
+                                       ml = mlt;
+                                       *matchpos = ref;
+                               }
+                       }
+               }
+               ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
+       }
+
+       /* Complete table */
+       if (repl) {
+               const BYTE *ptr = ip;
+               const BYTE *end;
+               end = ip + repl - (MINMATCH-1);
+               /* Pre-Load */
+               while (ptr < end - delta) {
+                       chaintable[(size_t)(ptr) & MAXD_MASK] = delta;
+                       ptr++;
+               }
+               do {
+                       chaintable[(size_t)(ptr) & MAXD_MASK] = delta;
+                       /* Head of chain */
+                       hashtable[HASH_VALUE(ptr)] = (ptr) - base;
+                       ptr++;
+               } while (ptr < end);
+               hc4->nexttoupdate = end;
+       }
+
+       return (int)ml;
+}
+
+static inline int lz4hc_insertandgetwidermatch(struct lz4hc_data *hc4,
+       const u8 *ip, const u8 *startlimit, const u8 *matchlimit, int longest,
+       const u8 **matchpos, const u8 **startpos)
+{
+       u16 *const chaintable = hc4->chaintable;
+       HTYPE *const hashtable = hc4->hashtable;
+#if LZ4_ARCH64
+       const BYTE * const base = hc4->base;
+#else
+       const int base = 0;
+#endif
+       const u8 *ref;
+       int nbattempts = MAX_NB_ATTEMPTS;
+       int delta = (int)(ip - startlimit);
+
+       /* First Match */
+       lz4hc_insert(hc4, ip);
+       ref = hashtable[HASH_VALUE(ip)] + base;
+
+       while ((ref >= ip - MAX_DISTANCE) && (ref >= hc4->base)
+               && (nbattempts)) {
+               nbattempts--;
+               if (*(startlimit + longest) == *(ref - delta + longest)) {
+                       if (A32(ref) == A32(ip)) {
+                               const u8 *reft = ref + MINMATCH;
+                               const u8 *ipt = ip + MINMATCH;
+                               const u8 *startt = ip;
+
+                               while (ipt < matchlimit-(STEPSIZE - 1)) {
+                                       #if LZ4_ARCH64
+                                       u64 diff = A64(reft) ^ A64(ipt);
+                                       #else
+                                       u32 diff = A32(reft) ^ A32(ipt);
+                                       #endif
+
+                                       if (!diff) {
+                                               ipt += STEPSIZE;
+                                               reft += STEPSIZE;
+                                               continue;
+                                       }
+                                       ipt += LZ4_NBCOMMONBYTES(diff);
+                                       goto _endcount;
+                               }
+                               #if LZ4_ARCH64
+                               if ((ipt < (matchlimit - 3))
+                                       && (A32(reft) == A32(ipt))) {
+                                       ipt += 4;
+                                       reft += 4;
+                               }
+                               ipt += 2;
+                               #endif
+                               if ((ipt < (matchlimit - 1))
+                                       && (A16(reft) == A16(ipt))) {
+                                       reft += 2;
+                               }
+                               if ((ipt < matchlimit) && (*reft == *ipt))
+                                       ipt++;
+_endcount:
+                               reft = ref;
+
+                               while ((startt > startlimit)
+                                       && (reft > hc4->base)
+                                       && (startt[-1] == reft[-1])) {
+                                       startt--;
+                                       reft--;
+                               }
+
+                               if ((ipt - startt) > longest) {
+                                       longest = (int)(ipt - startt);
+                                       *matchpos = reft;
+                                       *startpos = startt;
+                               }
+                       }
+               }
+               ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
+       }
+       return longest;
+}
+
+static inline int lz4_encodesequence(const u8 **ip, u8 **op, const u8 **anchor,
+               int ml, const u8 *ref)
+{
+       int length, len;
+       u8 *token;
+
+       /* Encode Literal length */
+       length = (int)(*ip - *anchor);
+       token = (*op)++;
+       if (length >= (int)RUN_MASK) {
+               *token = (RUN_MASK << ML_BITS);
+               len = length - RUN_MASK;
+               for (; len > 254 ; len -= 255)
+                       *(*op)++ = 255;
+               *(*op)++ = (u8)len;
+       } else
+               *token = (length << ML_BITS);
+
+       /* Copy Literals */
+       LZ4_BLINDCOPY(*anchor, *op, length);
+
+       /* Encode Offset */
+       LZ4_WRITE_LITTLEENDIAN_16(*op, (u16)(*ip - ref));
+
+       /* Encode MatchLength */
+       len = (int)(ml - MINMATCH);
+       if (len >= (int)ML_MASK) {
+               *token += ML_MASK;
+               len -= ML_MASK;
+               for (; len > 509 ; len -= 510) {
+                       *(*op)++ = 255;
+                       *(*op)++ = 255;
+               }
+               if (len > 254) {
+                       len -= 255;
+                       *(*op)++ = 255;
+               }
+               *(*op)++ = (u8)len;
+       } else
+               *token += len;
+
+       /* Prepare next loop */
+       *ip += ml;
+       *anchor = *ip;
+
+       return 0;
+}
+
+static int lz4_compresshcctx(struct lz4hc_data *ctx,
+               const char *source,
+               char *dest,
+               int isize)
+{
+       const u8 *ip = (const u8 *)source;
+       const u8 *anchor = ip;
+       const u8 *const iend = ip + isize;
+       const u8 *const mflimit = iend - MFLIMIT;
+       const u8 *const matchlimit = (iend - LASTLITERALS);
+
+       u8 *op = (u8 *)dest;
+
+       int ml, ml2, ml3, ml0;
+       const u8 *ref = NULL;
+       const u8 *start2 = NULL;
+       const u8 *ref2 = NULL;
+       const u8 *start3 = NULL;
+       const u8 *ref3 = NULL;
+       const u8 *start0;
+       const u8 *ref0;
+       int lastrun;
+
+       ip++;
+
+       /* Main Loop */
+       while (ip < mflimit) {
+               ml = lz4hc_insertandfindbestmatch(ctx, ip, matchlimit, (&ref));
+               if (!ml) {
+                       ip++;
+                       continue;
+               }
+
+               /* saved, in case we would skip too much */
+               start0 = ip;
+               ref0 = ref;
+               ml0 = ml;
+_search2:
+               if (ip+ml < mflimit)
+                       ml2 = lz4hc_insertandgetwidermatch(ctx, ip + ml - 2,
+                               ip + 1, matchlimit, ml, &ref2, &start2);
+               else
+                       ml2 = ml;
+               /* No better match */
+               if (ml2 == ml) {
+                       lz4_encodesequence(&ip, &op, &anchor, ml, ref);
+                       continue;
+               }
+
+               if (start0 < ip) {
+                       /* empirical */
+                       if (start2 < ip + ml0) {
+                               ip = start0;
+                               ref = ref0;
+                               ml = ml0;
+                       }
+               }
+               /*
+                * Here, start0==ip
+                * First Match too small : removed
+                */
+               if ((start2 - ip) < 3) {
+                       ml = ml2;
+                       ip = start2;
+                       ref = ref2;
+                       goto _search2;
+               }
+
+_search3:
+               /*
+                * Currently we have :
+                * ml2 > ml1, and
+                * ip1+3 <= ip2 (usually < ip1+ml1)
+                */
+               if ((start2 - ip) < OPTIMAL_ML) {
+                       int correction;
+                       int new_ml = ml;
+                       if (new_ml > OPTIMAL_ML)
+                               new_ml = OPTIMAL_ML;
+                       if (ip + new_ml > start2 + ml2 - MINMATCH)
+                               new_ml = (int)(start2 - ip) + ml2 - MINMATCH;
+                       correction = new_ml - (int)(start2 - ip);
+                       if (correction > 0) {
+                               start2 += correction;
+                               ref2 += correction;
+                               ml2 -= correction;
+                       }
+               }
+               /*
+                * Now, we have start2 = ip+new_ml,
+                * with new_ml=min(ml, OPTIMAL_ML=18)
+                */
+               if (start2 + ml2 < mflimit)
+                       ml3 = lz4hc_insertandgetwidermatch(ctx,
+                               start2 + ml2 - 3, start2, matchlimit,
+                               ml2, &ref3, &start3);
+               else
+                       ml3 = ml2;
+
+               /* No better match : 2 sequences to encode */
+               if (ml3 == ml2) {
+                       /* ip & ref are known; Now for ml */
+                       if (start2 < ip+ml)
+                               ml = (int)(start2 - ip);
+
+                       /* Now, encode 2 sequences */
+                       lz4_encodesequence(&ip, &op, &anchor, ml, ref);
+                       ip = start2;
+                       lz4_encodesequence(&ip, &op, &anchor, ml2, ref2);
+                       continue;
+               }
+
+               /* Not enough space for match 2 : remove it */
+               if (start3 < ip + ml + 3) {
+                       /*
+                        * can write Seq1 immediately ==> Seq2 is removed,
+                        * so Seq3 becomes Seq1
+                        */
+                       if (start3 >= (ip + ml)) {
+                               if (start2 < ip + ml) {
+                                       int correction =
+                                               (int)(ip + ml - start2);
+                                       start2 += correction;
+                                       ref2 += correction;
+                                       ml2 -= correction;
+                                       if (ml2 < MINMATCH) {
+                                               start2 = start3;
+                                               ref2 = ref3;
+                                               ml2 = ml3;
+                                       }
+                               }
+
+                               lz4_encodesequence(&ip, &op, &anchor, ml, ref);
+                               ip  = start3;
+                               ref = ref3;
+                               ml  = ml3;
+
+                               start0 = start2;
+                               ref0 = ref2;
+                               ml0 = ml2;
+                               goto _search2;
+                       }
+
+                       start2 = start3;
+                       ref2 = ref3;
+                       ml2 = ml3;
+                       goto _search3;
+               }
+
+               /*
+                * OK, now we have 3 ascending matches; let's write at least
+                * the first one ip & ref are known; Now for ml
+                */
+               if (start2 < ip + ml) {
+                       if ((start2 - ip) < (int)ML_MASK) {
+                               int correction;
+                               if (ml > OPTIMAL_ML)
+                                       ml = OPTIMAL_ML;
+                               if (ip + ml > start2 + ml2 - MINMATCH)
+                                       ml = (int)(start2 - ip) + ml2
+                                               - MINMATCH;
+                               correction = ml - (int)(start2 - ip);
+                               if (correction > 0) {
+                                       start2 += correction;
+                                       ref2 += correction;
+                                       ml2 -= correction;
+                               }
+                       } else
+                               ml = (int)(start2 - ip);
+               }
+               lz4_encodesequence(&ip, &op, &anchor, ml, ref);
+
+               ip = start2;
+               ref = ref2;
+               ml = ml2;
+
+               start2 = start3;
+               ref2 = ref3;
+               ml2 = ml3;
+
+               goto _search3;
+       }
+
+       /* Encode Last Literals */
+       lastrun = (int)(iend - anchor);
+       if (lastrun >= (int)RUN_MASK) {
+               *op++ = (RUN_MASK << ML_BITS);
+               lastrun -= RUN_MASK;
+               for (; lastrun > 254 ; lastrun -= 255)
+                       *op++ = 255;
+               *op++ = (u8) lastrun;
+       } else
+               *op++ = (lastrun << ML_BITS);
+       memcpy(op, anchor, iend - anchor);
+       op += iend - anchor;
+       /* End */
+       return (int) (((char *)op) - dest);
+}
+
+int lz4hc_compress(const unsigned char *src, size_t src_len,
+                       unsigned char *dst, size_t *dst_len, void *wrkmem)
+{
+       int ret = -1;
+       int out_len = 0;
+
+       struct lz4hc_data *hc4 = (struct lz4hc_data *)wrkmem;
+       lz4hc_init(hc4, (const u8 *)src);
+       out_len = lz4_compresshcctx((struct lz4hc_data *)hc4, (const u8 *)src,
+               (char *)dst, (int)src_len);
+
+       if (out_len < 0)
+               goto exit;
+
+       *dst_len = out_len;
+       return 0;
+
+exit:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(lz4hc_compress);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LZ4HC compressor");
index 3dcea72277be8988ce2df2f185eadb2fc8b05d7a..b680dfd7dbf294d9d13b7a05a78ce29558f5d6e4 100644 (file)
 
 int can_do_mlock(void)
 {
-       if (capable(CAP_IPC_LOCK))
-               return 1;
        if (rlimit(RLIMIT_MEMLOCK) != 0)
                return 1;
+       if (capable(CAP_IPC_LOCK))
+               return 1;
        return 0;
 }
 EXPORT_SYMBOL(can_do_mlock);
index ea47f2fc3ea47cf5057805db9f5f0d25543f553b..4a7cf03a192cb764eca620e3a544ea09a697bf07 100644 (file)
@@ -250,7 +250,7 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
        bool dev_match;
 
        fl4.flowi4_oif = 0;
-       fl4.flowi4_iif = oif;
+       fl4.flowi4_iif = oif ? : LOOPBACK_IFINDEX;
        fl4.daddr = src;
        fl4.saddr = dst;
        fl4.flowi4_tos = tos;
index bc773a10dca63bf8ee83f60ed9b36ff8987683b3..d00d8f9650e98b99d460da8a0a36bf8b37bedb91 100644 (file)
@@ -629,6 +629,7 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
                                .daddr = nh->nh_gw,
                                .flowi4_scope = cfg->fc_scope + 1,
                                .flowi4_oif = nh->nh_oif,
+                               .flowi4_iif = LOOPBACK_IFINDEX,
                        };
 
                        /* It is not necessary, but requires a bit of thinking */
index 56d079b63ad3fe552db8dab66e9e18ae9edf2b3c..9fc74ec238deade4aeb249e3c63b819c3b015919 100644 (file)
@@ -454,7 +454,7 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
        struct mr_table *mrt;
        struct flowi4 fl4 = {
                .flowi4_oif     = dev->ifindex,
-               .flowi4_iif     = skb->skb_iif,
+               .flowi4_iif     = skb->skb_iif ? : LOOPBACK_IFINDEX,
                .flowi4_mark    = skb->mark,
        };
        int err;
index c49dcd0284a06c6bb4b4e6787af5888289ba50f3..4bfaedf9b34e5248aa6f8133e2c3bb0e555062e9 100644 (file)
@@ -89,11 +89,8 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)
        if (ipv4_is_multicast(iph->daddr)) {
                if (ipv4_is_zeronet(iph->saddr))
                        return ipv4_is_local_multicast(iph->daddr) ^ invert;
-               flow.flowi4_iif = 0;
-       } else {
-               flow.flowi4_iif = LOOPBACK_IFINDEX;
        }
-
+       flow.flowi4_iif = LOOPBACK_IFINDEX;
        flow.daddr = iph->saddr;
        flow.saddr = rpfilter_get_saddr(iph->daddr);
        flow.flowi4_oif = 0;
index 9b405b8d1895d9ad122b030e0ad917f95812d203..58c3a3cea4df18141380d564024b2602ebaf3a81 100644 (file)
@@ -256,6 +256,9 @@ int ping_init_sock(struct sock *sk)
        kgid_t low, high;
        int ret = 0;
 
+       if (sk->sk_family == AF_INET6)
+               inet6_sk(sk)->ipv6only = 1;
+
        inet_get_ping_group_range_net(net, &low, &high);
        if (gid_lte(low, group) && gid_lte(group, high))
                return 0;
@@ -302,6 +305,11 @@ int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk,
                if (addr_len < sizeof(*addr))
                        return -EINVAL;
 
+               if (addr->sin_family != AF_INET &&
+                   !(addr->sin_family == AF_UNSPEC &&
+                     addr->sin_addr.s_addr == htonl(INADDR_ANY)))
+                       return -EAFNOSUPPORT;
+
                pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n",
                         sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port));
 
@@ -326,6 +334,9 @@ int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk,
                if (addr_len < sizeof(*addr))
                        return -EINVAL;
 
+               if (addr->sin6_family != AF_INET6)
+                       return -EAFNOSUPPORT;
+
                pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n",
                         sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port));
 
@@ -708,7 +719,7 @@ int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                if (msg->msg_namelen < sizeof(*usin))
                        return -EINVAL;
                if (usin->sin_family != AF_INET)
-                       return -EINVAL;
+                       return -EAFNOSUPPORT;
                daddr = usin->sin_addr.s_addr;
                /* no remote port */
        } else {
index 2c84072b1da73dbc31612bebe4b8be06eddd1e69..63cf7cded36deaa31f281942dd5fce71498ebce8 100644 (file)
@@ -701,7 +701,7 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb,
        struct mr6_table *mrt;
        struct flowi6 fl6 = {
                .flowi6_oif     = dev->ifindex,
-               .flowi6_iif     = skb->skb_iif,
+               .flowi6_iif     = skb->skb_iif ? : LOOPBACK_IFINDEX,
                .flowi6_mark    = skb->mark,
        };
        int err;
index 5f0d294b36cd206549792ac8c8f88d2735fb8a14..857201f4936fa6435d66265ba772a397bb83300a 100644 (file)
@@ -126,9 +126,10 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 
        if (msg->msg_name) {
                struct sockaddr_in6 *u = (struct sockaddr_in6 *) msg->msg_name;
-               if (msg->msg_namelen < sizeof(struct sockaddr_in6) ||
-                   u->sin6_family != AF_INET6) {
+               if (msg->msg_namelen < sizeof(*u))
                        return -EINVAL;
+               if (u->sin6_family != AF_INET6) {
+                       return -EAFNOSUPPORT;
                }
                if (sk->sk_bound_dev_if &&
                    sk->sk_bound_dev_if != u->sin6_scope_id) {
index ee84a3a3b69deadb0532043e5537a66b7e0e0057..35cf8929b1789ea5e4c844261029561691ce0bd5 100644 (file)
@@ -2635,6 +2635,9 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh)
        if (tb[RTA_OIF])
                oif = nla_get_u32(tb[RTA_OIF]);
 
+       if (tb[RTA_MARK])
+               fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
+
        if (tb[RTA_UID])
                fl6.flowi6_uid = make_kuid(current_user_ns(),
                                           nla_get_u32(tb[RTA_UID]));