Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzi...
[powerpc.git] / mm / hugetlb.c
index 3992bd5..6121b57 100644 (file)
@@ -132,7 +132,7 @@ static void free_huge_page(struct page *page)
        }
        spin_unlock(&hugetlb_lock);
        if (mapping)
-               hugetlb_put_quota(mapping);
+               hugetlb_put_quota(mapping, 1);
        set_page_private(page, 0);
 }
 
@@ -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,27 +375,32 @@ 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,
                                    unsigned long addr)
 {
        struct page *page;
+       struct address_space *mapping = vma->vm_file->f_mapping;
 
        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) vma->vm_file->f_mapping);
+               set_page_private(page, (unsigned long) mapping);
        }
        return page;
 }
@@ -737,15 +742,13 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
                set_huge_ptep_writable(vma, address, ptep);
                return 0;
        }
-       if (hugetlb_get_quota(vma->vm_file->f_mapping))
-               return VM_FAULT_SIGBUS;
 
        page_cache_get(old_page);
        new_page = alloc_huge_page(vma, address);
 
-       if (!new_page) {
+       if (IS_ERR(new_page)) {
                page_cache_release(old_page);
-               return VM_FAULT_OOM;
+               return -PTR_ERR(new_page);
        }
 
        spin_unlock(&mm->page_table_lock);
@@ -789,18 +792,16 @@ retry:
                size = i_size_read(mapping->host) >> HPAGE_SHIFT;
                if (idx >= size)
                        goto out;
-               if (hugetlb_get_quota(mapping))
-                       goto out;
                page = alloc_huge_page(vma, address);
-               if (!page) {
-                       hugetlb_put_quota(mapping);
-                       ret = VM_FAULT_OOM;
+               if (IS_ERR(page)) {
+                       ret = -PTR_ERR(page);
                        goto out;
                }
                clear_huge_page(page, address);
 
                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));
 }