Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzi...
[powerpc.git] / mm / hugetlb.c
index 1e31746..6121b57 100644 (file)
@@ -328,7 +328,7 @@ free:
  * allocated to satisfy the reservation must be explicitly freed if they were
  * never used.
  */
-void return_unused_surplus_pages(unsigned long unused_resv_pages)
+static void return_unused_surplus_pages(unsigned long unused_resv_pages)
 {
        static int nid = -1;
        struct page *page;
@@ -367,7 +367,7 @@ static struct page *alloc_huge_page_shared(struct vm_area_struct *vma,
        spin_lock(&hugetlb_lock);
        page = dequeue_huge_page(vma, addr);
        spin_unlock(&hugetlb_lock);
-       return page;
+       return page ? page : ERR_PTR(-VM_FAULT_OOM);
 }
 
 static struct page *alloc_huge_page_private(struct vm_area_struct *vma,
@@ -375,13 +375,16 @@ static struct page *alloc_huge_page_private(struct vm_area_struct *vma,
 {
        struct page *page = NULL;
 
+       if (hugetlb_get_quota(vma->vm_file->f_mapping, 1))
+               return ERR_PTR(-VM_FAULT_SIGBUS);
+
        spin_lock(&hugetlb_lock);
        if (free_huge_pages > resv_huge_pages)
                page = dequeue_huge_page(vma, addr);
        spin_unlock(&hugetlb_lock);
        if (!page)
                page = alloc_buddy_huge_page(vma, addr);
-       return page;
+       return page ? page : ERR_PTR(-VM_FAULT_OOM);
 }
 
 static struct page *alloc_huge_page(struct vm_area_struct *vma,
@@ -390,19 +393,16 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
        struct page *page;
        struct address_space *mapping = vma->vm_file->f_mapping;
 
-       if (hugetlb_get_quota(mapping, 1))
-               return ERR_PTR(-VM_FAULT_SIGBUS);
-
        if (vma->vm_flags & VM_MAYSHARE)
                page = alloc_huge_page_shared(vma, addr);
        else
                page = alloc_huge_page_private(vma, addr);
-       if (page) {
+
+       if (!IS_ERR(page)) {
                set_page_refcounted(page);
                set_page_private(page, (unsigned long) mapping);
-               return page;
-       } else
-               return ERR_PTR(-VM_FAULT_OOM);
+       }
+       return page;
 }
 
 static int __init hugetlb_init(void)
@@ -801,6 +801,7 @@ retry:
 
                if (vma->vm_flags & VM_SHARED) {
                        int err;
+                       struct inode *inode = mapping->host;
 
                        err = add_to_page_cache(page, mapping, idx, GFP_KERNEL);
                        if (err) {
@@ -809,6 +810,10 @@ retry:
                                        goto retry;
                                goto out;
                        }
+
+                       spin_lock(&inode->i_lock);
+                       inode->i_blocks += BLOCKS_PER_HUGEPAGE;
+                       spin_unlock(&inode->i_lock);
                } else
                        lock_page(page);
        }
@@ -1148,6 +1153,8 @@ int hugetlb_reserve_pages(struct inode *inode, long from, long to)
        if (chg < 0)
                return chg;
 
+       if (hugetlb_get_quota(inode->i_mapping, chg))
+               return -ENOSPC;
        ret = hugetlb_acct_memory(chg);
        if (ret < 0)
                return ret;
@@ -1158,5 +1165,11 @@ int hugetlb_reserve_pages(struct inode *inode, long from, long to)
 void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed)
 {
        long chg = region_truncate(&inode->i_mapping->private_list, offset);
-       hugetlb_acct_memory(freed - chg);
+
+       spin_lock(&inode->i_lock);
+       inode->i_blocks -= BLOCKS_PER_HUGEPAGE * freed;
+       spin_unlock(&inode->i_lock);
+
+       hugetlb_put_quota(inode->i_mapping, (chg - freed));
+       hugetlb_acct_memory(-(chg - freed));
 }