[PATCH] madvise(MADV_REMOVE): remove pages from tmpfs shm backing store
authorBadari Pulavarty <pbadari@us.ibm.com>
Fri, 6 Jan 2006 08:10:38 +0000 (00:10 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Fri, 6 Jan 2006 16:33:22 +0000 (08:33 -0800)
Here is the patch to implement madvise(MADV_REMOVE) - which frees up a
given range of pages & its associated backing store.  Current
implementation supports only shmfs/tmpfs and other filesystems return
-ENOSYS.

"Some app allocates large tmpfs files, then when some task quits and some
client disconnect, some memory can be released.  However the only way to
release tmpfs-swap is to MADV_REMOVE". - Andrea Arcangeli

Databases want to use this feature to drop a section of their bufferpool
(shared memory segments) - without writing back to disk/swap space.

This feature is also useful for supporting hot-plug memory on UML.

Concerns raised by Andrew Morton:

- "We have no plan for holepunching!  If we _do_ have such a plan (or
  might in the future) then what would the API look like?  I think
  sys_holepunch(fd, start, len), so we should start out with that."

- Using madvise is very weird, because people will ask "why do I need to
  mmap my file before I can stick a hole in it?"

- None of the other madvise operations call into the filesystem in this
  manner.  A broad question is: is this capability an MM operation or a
  filesytem operation?  truncate, for example, is a filesystem operation
  which sometimes has MM side-effects.  madvise is an mm operation and with
  this patch, it gains FS side-effects, only they're really, really
  significant ones."

Comments:

- Andrea suggested the fs operation too but then it's more efficient to
  have it as a mm operation with fs side effects, because they don't
  immediatly know fd and physical offset of the range.  It's possible to
  fixup in userland and to use the fs operation but it's more expensive,
  the vmas are already in the kernel and we can use them.

Short term plan &  Future Direction:

- We seem to need this interface only for shmfs/tmpfs files in the short
  term.  We have to add hooks into the filesystem for correctness and
  completeness.  This is what this patch does.

- In the future, plan is to support both fs and mmap apis also.  This
  also involves (other) filesystem specific functions to be implemented.

- Current patch doesn't support VM_NONLINEAR - which can be addressed in
  the future.

Signed-off-by: Badari Pulavarty <pbadari@us.ibm.com>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: Andrea Arcangeli <andrea@suse.de>
Cc: Michael Kerrisk <mtk-manpages@gmx.net>
Cc: Ulrich Drepper <drepper@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
25 files changed:
include/asm-alpha/mman.h
include/asm-arm/mman.h
include/asm-arm26/mman.h
include/asm-cris/mman.h
include/asm-frv/mman.h
include/asm-h8300/mman.h
include/asm-i386/mman.h
include/asm-ia64/mman.h
include/asm-m32r/mman.h
include/asm-m68k/mman.h
include/asm-mips/mman.h
include/asm-parisc/mman.h
include/asm-powerpc/mman.h
include/asm-s390/mman.h
include/asm-sh/mman.h
include/asm-sparc/mman.h
include/asm-sparc64/mman.h
include/asm-v850/mman.h
include/asm-x86_64/mman.h
include/asm-xtensa/mman.h
include/linux/fs.h
include/linux/mm.h
mm/madvise.c
mm/memory.c
mm/shmem.c

index eb9c279045efe07573e308c5715eb7458c4e91df..f6439532a262d45e2ff47655edfd87cb9fcf55fb 100644 (file)
@@ -42,6 +42,7 @@
 #define MADV_WILLNEED  3               /* will need these pages */
 #define        MADV_SPACEAVAIL 5               /* ensure resources are available */
 #define MADV_DONTNEED  6               /* don't need these pages */
+#define MADV_REMOVE    7               /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 8e4f69c4fa5faa845479b68e4e3159374576f5f1..f0bebca2ac214a5b6b87dc8413c18b3cd9115ceb 100644 (file)
@@ -35,6 +35,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index cc27b824026564af2b61d9373923b9d6bebbd709..0ed7780541fa1b27abba46b8541bcb6cf583d1c6 100644 (file)
@@ -35,6 +35,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 8570e72b9502cd73d560785ba9acdb9b8525db4f..5a382b8bf3f738effedd851b6887f7ce4197817b 100644 (file)
@@ -37,6 +37,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index c684720dfbdd15fd5c312243e7a0ecc95c374b76..8af4a41c255e113b6bf38b37fb0eafb26e312f65 100644 (file)
@@ -35,6 +35,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 63f727a59850bfa64f3723d30d2dc7a324553ed6..744a8fb485c230780ff6256abc483a58bab647b2 100644 (file)
@@ -35,6 +35,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 196619a838543e044f9e81088e63d28ebf899ace..ba4941e6f643213f39bcff3379259cdadf8584d3 100644 (file)
@@ -35,6 +35,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 1c0a73af1461363a225eb0af75ecb7c5ba012694..828beb24a20ec7837fe7a00dde7c4b8b93bbe941 100644 (file)
@@ -43,6 +43,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 011f6d9ec5cc79c5271e2e7c7791decfa8e626a7..12e29747bc84a510163f6e948f3d6834047e0c7c 100644 (file)
@@ -37,6 +37,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index f831c4eeae6e0fd05c0cdd3552615cad83a1736c..ea262ab88b3bb547761227d0a039e7800674bc4c 100644 (file)
@@ -35,6 +35,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 62060957ba93a743d8b8ceaa11b11f2706ffb578..dd17c8bd62a1cbc4450db3b21035f8de94826639 100644 (file)
@@ -65,6 +65,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index e829607eb8bc2a4d2e77c48bc93362cc311ddeee..736b0abcac052b1ad468ff0362bfe8e3173c531a 100644 (file)
@@ -38,6 +38,7 @@
 #define MADV_SPACEAVAIL 5               /* insure that resources are reserved */
 #define MADV_VPS_PURGE  6               /* Purge pages from VM page cache */
 #define MADV_VPS_INHERIT 7              /* Inherit parents page size */
+#define MADV_REMOVE     8              /* remove these pages & resources */
 
 /* The range 12-64 is reserved for page size specification. */
 #define MADV_4K_PAGES   12              /* Use 4K pages  */
index f5e5342fcac52f94761089e9b4c12f0aebb23b96..a2e34c21b44f8f09455dad99aef78285d166da1a 100644 (file)
@@ -44,6 +44,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index ea86bd12204fe55032fe1418ed62924b606bda29..c8d5409b5d563c383e9fa4f8378504b41c992473 100644 (file)
@@ -43,6 +43,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3              /* pre-fault pages */
 #define MADV_DONTNEED  0x4              /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 3ebab5f79db794d34afa60a62ce8ec9a462a49c4..693bd55a37104f543a2c5e3a21878bbc5cd3d395 100644 (file)
@@ -35,6 +35,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 138eb81dd70d3fd5a1c0a9f2b1e266049e960d47..98435ad8619e0b0b5f5cd9aa882de51d6ea89948 100644 (file)
@@ -54,6 +54,7 @@
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
 #define MADV_FREE      0x5             /* (Solaris) contents can be freed */
+#define MADV_REMOVE    0x6             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 01cecf54357b876d3fe5d8f31c33ac181a8bb670..cb4b6156194dbe12dc92adfba8c3247400ebd0f8 100644 (file)
@@ -54,6 +54,7 @@
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
 #define MADV_FREE      0x5             /* (Solaris) contents can be freed */
+#define MADV_REMOVE    0x6             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index e2b90081b56f8ca2d7b3f9dc6a6012b249ee9add..edc79965193aa589e6810b35903fb7262c404927 100644 (file)
@@ -32,6 +32,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 78e60a4fd4ee543c58ba359f9488bddcc95741f4..d0e97b74f73591d0e430719f0861dc00dda1d224 100644 (file)
@@ -36,6 +36,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index 9a95a45df99634f08ef0d84fd9a8ed808ab0975b..082a7504925ed5bebeda7b910b25de6bc58075c1 100644 (file)
@@ -72,6 +72,7 @@
 #define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
 #define MADV_WILLNEED  0x3             /* pre-fault pages */
 #define MADV_DONTNEED  0x4             /* discard these pages */
+#define MADV_REMOVE    0x5             /* remove these pages & resources */
 
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
index ed9a41a71e8b57ff62ea245fbbe46e586d74c5ee..115e72be25d0cd7b7a7436e4d82f389b7cbf7c8f 100644 (file)
@@ -1050,6 +1050,7 @@ struct inode_operations {
        ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
        ssize_t (*listxattr) (struct dentry *, char *, size_t);
        int (*removexattr) (struct dentry *, const char *);
+       void (*truncate_range)(struct inode *, loff_t, loff_t);
 };
 
 struct seq_file;
index 92acae9f1f4cb14bf543e277b7228e5fec369a02..6c9be99429f31639e4a0b1809bbd6f758299202b 100644 (file)
@@ -690,6 +690,7 @@ static inline void unmap_shared_mapping_range(struct address_space *mapping,
 }
 
 extern int vmtruncate(struct inode * inode, loff_t offset);
+extern int vmtruncate_range(struct inode * inode, loff_t offset, loff_t end);
 extern int install_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, struct page *page, pgprot_t prot);
 extern int install_file_pte(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, unsigned long pgoff, pgprot_t prot);
 extern int __handle_mm_fault(struct mm_struct *mm,struct vm_area_struct *vma, unsigned long address, int write_access);
index 2b7cf0400a217cd453641eaf097d1291125e1431..ae0ae3ea299a7a83b72939236ef4931eb5ac16d0 100644 (file)
@@ -140,6 +140,36 @@ static long madvise_dontneed(struct vm_area_struct * vma,
        return 0;
 }
 
+/*
+ * Application wants to free up the pages and associated backing store.
+ * This is effectively punching a hole into the middle of a file.
+ *
+ * NOTE: Currently, only shmfs/tmpfs is supported for this operation.
+ * Other filesystems return -ENOSYS.
+ */
+static long madvise_remove(struct vm_area_struct *vma,
+                               unsigned long start, unsigned long end)
+{
+       struct address_space *mapping;
+        loff_t offset, endoff;
+
+       if (vma->vm_flags & (VM_LOCKED|VM_NONLINEAR|VM_HUGETLB))
+               return -EINVAL;
+
+       if (!vma->vm_file || !vma->vm_file->f_mapping
+               || !vma->vm_file->f_mapping->host) {
+                       return -EINVAL;
+       }
+
+       mapping = vma->vm_file->f_mapping;
+
+       offset = (loff_t)(start - vma->vm_start)
+                       + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
+       endoff = (loff_t)(end - vma->vm_start - 1)
+                       + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
+       return  vmtruncate_range(mapping->host, offset, endoff);
+}
+
 static long
 madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
                unsigned long start, unsigned long end, int behavior)
@@ -152,6 +182,9 @@ madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
        case MADV_RANDOM:
                error = madvise_behavior(vma, prev, start, end, behavior);
                break;
+       case MADV_REMOVE:
+               error = madvise_remove(vma, start, end);
+               break;
 
        case MADV_WILLNEED:
                error = madvise_willneed(vma, prev, start, end);
@@ -190,6 +223,8 @@ madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
  *             some pages ahead.
  *  MADV_DONTNEED - the application is finished with the given range,
  *             so the kernel can free resources associated with it.
+ *  MADV_REMOVE - the application wants to free up the given range of
+ *             pages and associated backing store.
  *
  * return values:
  *  zero    - success
index d8dde07a36566fc6463b7b9b95305d9b0505427e..e249088908c436940386cbf161f1b6d0c7c39c0b 100644 (file)
@@ -1770,9 +1770,32 @@ out_big:
 out_busy:
        return -ETXTBSY;
 }
-
 EXPORT_SYMBOL(vmtruncate);
 
+int vmtruncate_range(struct inode *inode, loff_t offset, loff_t end)
+{
+       struct address_space *mapping = inode->i_mapping;
+
+       /*
+        * If the underlying filesystem is not going to provide
+        * a way to truncate a range of blocks (punch a hole) -
+        * we should return failure right now.
+        */
+       if (!inode->i_op || !inode->i_op->truncate_range)
+               return -ENOSYS;
+
+       down(&inode->i_sem);
+       down_write(&inode->i_alloc_sem);
+       unmap_mapping_range(mapping, offset, (end - offset), 1);
+       truncate_inode_pages_range(mapping, offset, end);
+       inode->i_op->truncate_range(inode, offset, end);
+       up_write(&inode->i_alloc_sem);
+       up(&inode->i_sem);
+
+       return 0;
+}
+EXPORT_SYMBOL(vmtruncate_range);
+
 /* 
  * Primitive swap readahead code. We simply read an aligned block of
  * (1 << page_cluster) entries in the swap area. This method is chosen
index d9fc277940da5473e472087c115f5be10608c190..65c148efa2ed90f2f1921b2b9a7ddde5bde8d035 100644 (file)
@@ -457,7 +457,7 @@ static void shmem_free_pages(struct list_head *next)
        } while (next);
 }
 
-static void shmem_truncate(struct inode *inode)
+static void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end)
 {
        struct shmem_inode_info *info = SHMEM_I(inode);
        unsigned long idx;
@@ -475,18 +475,27 @@ static void shmem_truncate(struct inode *inode)
        long nr_swaps_freed = 0;
        int offset;
        int freed;
+       int punch_hole = 0;
 
        inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-       idx = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       idx = (start + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
        if (idx >= info->next_index)
                return;
 
        spin_lock(&info->lock);
        info->flags |= SHMEM_TRUNCATE;
-       limit = info->next_index;
-       info->next_index = idx;
+       if (likely(end == (loff_t) -1)) {
+               limit = info->next_index;
+               info->next_index = idx;
+       } else {
+               limit = (end + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+               if (limit > info->next_index)
+                       limit = info->next_index;
+               punch_hole = 1;
+       }
+
        topdir = info->i_indirect;
-       if (topdir && idx <= SHMEM_NR_DIRECT) {
+       if (topdir && idx <= SHMEM_NR_DIRECT && !punch_hole) {
                info->i_indirect = NULL;
                nr_pages_to_free++;
                list_add(&topdir->lru, &pages_to_free);
@@ -573,11 +582,12 @@ static void shmem_truncate(struct inode *inode)
                        set_page_private(subdir, page_private(subdir) - freed);
                        if (offset)
                                spin_unlock(&info->lock);
-                       BUG_ON(page_private(subdir) > offset);
+                       if (!punch_hole)
+                               BUG_ON(page_private(subdir) > offset);
                }
                if (offset)
                        offset = 0;
-               else if (subdir) {
+               else if (subdir && !page_private(subdir)) {
                        dir[diroff] = NULL;
                        nr_pages_to_free++;
                        list_add(&subdir->lru, &pages_to_free);
@@ -594,7 +604,7 @@ done2:
                 * Also, though shmem_getpage checks i_size before adding to
                 * cache, no recheck after: so fix the narrow window there too.
                 */
-               truncate_inode_pages(inode->i_mapping, inode->i_size);
+               truncate_inode_pages_range(inode->i_mapping, start, end);
        }
 
        spin_lock(&info->lock);
@@ -614,6 +624,11 @@ done2:
        }
 }
 
+static void shmem_truncate(struct inode *inode)
+{
+       shmem_truncate_range(inode, inode->i_size, (loff_t)-1);
+}
+
 static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
 {
        struct inode *inode = dentry->d_inode;
@@ -2083,6 +2098,7 @@ static struct file_operations shmem_file_operations = {
 static struct inode_operations shmem_inode_operations = {
        .truncate       = shmem_truncate,
        .setattr        = shmem_notify_change,
+       .truncate_range = shmem_truncate_range,
 };
 
 static struct inode_operations shmem_dir_inode_operations = {