#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
 
 #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
 
 #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
 
 #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
 
 #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
 
 #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
 
 #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
 
 #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
 
 #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
 
 #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
 
 #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
 
 #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  */
 
 #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
 
 #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
 
 #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
 
 #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
 
 #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
 
 #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
 
 #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
 
 #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
 
        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;
 
 }
 
 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);
 
        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)
        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);
  *             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
 
 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
 
        } 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;
        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);
                        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);
                 * 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);
        }
 }
 
+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;
 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 = {