[PATCH] hugepage: Strict page reservation for hugepage inodes
[linux-2.6.git] / fs / hugetlbfs / inode.c
index b351952..1a1c2fc 100644 (file)
@@ -56,48 +56,10 @@ static void huge_pagevec_release(struct pagevec *pvec)
        pagevec_reinit(pvec);
 }
 
-/*
- * huge_pages_needed tries to determine the number of new huge pages that
- * will be required to fully populate this VMA.  This will be equal to
- * the size of the VMA in huge pages minus the number of huge pages
- * (covered by this VMA) that are found in the page cache.
- *
- * Result is in bytes to be compatible with is_hugepage_mem_enough()
- */
-static unsigned long
-huge_pages_needed(struct address_space *mapping, struct vm_area_struct *vma)
-{
-       int i;
-       struct pagevec pvec;
-       unsigned long start = vma->vm_start;
-       unsigned long end = vma->vm_end;
-       unsigned long hugepages = (end - start) >> HPAGE_SHIFT;
-       pgoff_t next = vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT);
-       pgoff_t endpg = next + hugepages;
-
-       pagevec_init(&pvec, 0);
-       while (next < endpg) {
-               if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE))
-                       break;
-               for (i = 0; i < pagevec_count(&pvec); i++) {
-                       struct page *page = pvec.pages[i];
-                       if (page->index > next)
-                               next = page->index;
-                       if (page->index >= endpg)
-                               break;
-                       next++;
-                       hugepages--;
-               }
-               huge_pagevec_release(&pvec);
-       }
-       return hugepages << HPAGE_SHIFT;
-}
-
 static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
 {
        struct inode *inode = file->f_dentry->d_inode;
-       struct address_space *mapping = inode->i_mapping;
-       unsigned long bytes;
+       struct hugetlbfs_inode_info *info = HUGETLBFS_I(inode);
        loff_t len, vma_len;
        int ret;
 
@@ -113,10 +75,6 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
        if (vma->vm_end - vma->vm_start < HPAGE_SIZE)
                return -EINVAL;
 
-       bytes = huge_pages_needed(mapping, vma);
-       if (!is_hugepage_mem_enough(bytes))
-               return -ENOMEM;
-
        vma_len = (loff_t)(vma->vm_end - vma->vm_start);
 
        mutex_lock(&inode->i_mutex);
@@ -129,6 +87,10 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
        if (!(vma->vm_flags & VM_WRITE) && len > inode->i_size)
                goto out;
 
+       if (vma->vm_flags & VM_MAYSHARE)
+               if (hugetlb_extend_reservation(info, len >> HPAGE_SHIFT) != 0)
+                       goto out;
+
        ret = 0;
        hugetlb_prefault_arch_hook(vma->vm_mm);
        if (inode->i_size < len)
@@ -227,13 +189,18 @@ static void truncate_huge_page(struct page *page)
        put_page(page);
 }
 
-static void truncate_hugepages(struct address_space *mapping, loff_t lstart)
+static void truncate_hugepages(struct inode *inode, loff_t lstart)
 {
+       struct address_space *mapping = &inode->i_data;
        const pgoff_t start = lstart >> HPAGE_SHIFT;
        struct pagevec pvec;
        pgoff_t next;
        int i;
 
+       hugetlb_truncate_reservation(HUGETLBFS_I(inode),
+                                    lstart >> HPAGE_SHIFT);
+       if (!mapping->nrpages)
+               return;
        pagevec_init(&pvec, 0);
        next = start;
        while (1) {
@@ -262,8 +229,7 @@ static void truncate_hugepages(struct address_space *mapping, loff_t lstart)
 
 static void hugetlbfs_delete_inode(struct inode *inode)
 {
-       if (inode->i_data.nrpages)
-               truncate_hugepages(&inode->i_data, 0);
+       truncate_hugepages(inode, 0);
        clear_inode(inode);
 }
 
@@ -296,8 +262,7 @@ static void hugetlbfs_forget_inode(struct inode *inode)
        inode->i_state |= I_FREEING;
        inodes_stat.nr_inodes--;
        spin_unlock(&inode_lock);
-       if (inode->i_data.nrpages)
-               truncate_hugepages(&inode->i_data, 0);
+       truncate_hugepages(inode, 0);
        clear_inode(inode);
        destroy_inode(inode);
 }
@@ -356,7 +321,7 @@ static int hugetlb_vmtruncate(struct inode *inode, loff_t offset)
        if (!prio_tree_empty(&mapping->i_mmap))
                hugetlb_vmtruncate_list(&mapping->i_mmap, pgoff);
        spin_unlock(&mapping->i_mmap_lock);
-       truncate_hugepages(mapping, offset);
+       truncate_hugepages(inode, offset);
        return 0;
 }
 
@@ -573,6 +538,7 @@ static struct inode *hugetlbfs_alloc_inode(struct super_block *sb)
                hugetlbfs_inc_free_inodes(sbinfo);
                return NULL;
        }
+       p->prereserved_hpages = 0;
        return &p->vfs_inode;
 }
 
@@ -805,9 +771,6 @@ struct file *hugetlb_zero_setup(size_t size)
        if (!can_do_hugetlb_shm())
                return ERR_PTR(-EPERM);
 
-       if (!is_hugepage_mem_enough(size))
-               return ERR_PTR(-ENOMEM);
-
        if (!user_shm_lock(size, current->user))
                return ERR_PTR(-ENOMEM);
 
@@ -831,6 +794,11 @@ struct file *hugetlb_zero_setup(size_t size)
        if (!inode)
                goto out_file;
 
+       error = -ENOMEM;
+       if (hugetlb_extend_reservation(HUGETLBFS_I(inode),
+                                      size >> HPAGE_SHIFT) != 0)
+               goto out_inode;
+
        d_instantiate(dentry, inode);
        inode->i_size = size;
        inode->i_nlink = 0;
@@ -841,6 +809,8 @@ struct file *hugetlb_zero_setup(size_t size)
        file->f_mode = FMODE_WRITE | FMODE_READ;
        return file;
 
+out_inode:
+       iput(inode);
 out_file:
        put_filp(file);
 out_dentry: