]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - mm/hugetlb.c
fbdev: Add fb_append_extra_logo()
[linux-2.6-omap-h63xx.git] / mm / hugetlb.c
index cb362f761f174b926c1c41b72ec2940c5d45763d..6912bbf33faacb0eaa14c1bba4654c5800818b3c 100644 (file)
@@ -27,6 +27,9 @@ unsigned long max_huge_pages;
 static struct list_head hugepage_freelists[MAX_NUMNODES];
 static unsigned int nr_huge_pages_node[MAX_NUMNODES];
 static unsigned int free_huge_pages_node[MAX_NUMNODES];
+static gfp_t htlb_alloc_mask = GFP_HIGHUSER;
+unsigned long hugepages_treat_as_movable;
+
 /*
  * Protects updates to hugepage_freelists, nr_huge_pages, and free_huge_pages
  */
@@ -66,14 +69,15 @@ static void enqueue_huge_page(struct page *page)
 static struct page *dequeue_huge_page(struct vm_area_struct *vma,
                                unsigned long address)
 {
-       int nid = numa_node_id();
+       int nid;
        struct page *page = NULL;
-       struct zonelist *zonelist = huge_zonelist(vma, address);
+       struct zonelist *zonelist = huge_zonelist(vma, address,
+                                               htlb_alloc_mask);
        struct zone **z;
 
        for (z = zonelist->zones; *z; z++) {
                nid = zone_to_nid(*z);
-               if (cpuset_zone_allowed_softwall(*z, GFP_HIGHUSER) &&
+               if (cpuset_zone_allowed_softwall(*z, htlb_alloc_mask) &&
                    !list_empty(&hugepage_freelists[nid]))
                        break;
        }
@@ -101,13 +105,20 @@ static void free_huge_page(struct page *page)
 
 static int alloc_fresh_huge_page(void)
 {
-       static int nid = 0;
+       static int prev_nid;
        struct page *page;
-       page = alloc_pages_node(nid, GFP_HIGHUSER|__GFP_COMP|__GFP_NOWARN,
-                                       HUGETLB_PAGE_ORDER);
-       nid = next_node(nid, node_online_map);
+       static DEFINE_SPINLOCK(nid_lock);
+       int nid;
+
+       spin_lock(&nid_lock);
+       nid = next_node(prev_nid, node_online_map);
        if (nid == MAX_NUMNODES)
                nid = first_node(node_online_map);
+       prev_nid = nid;
+       spin_unlock(&nid_lock);
+
+       page = alloc_pages_node(nid, htlb_alloc_mask|__GFP_COMP|__GFP_NOWARN,
+                                       HUGETLB_PAGE_ORDER);
        if (page) {
                set_compound_page_dtor(page, free_huge_page);
                spin_lock(&hugetlb_lock);
@@ -140,6 +151,8 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
        return page;
 
 fail:
+       if (vma->vm_flags & VM_MAYSHARE)
+               resv_huge_pages++;
        spin_unlock(&hugetlb_lock);
        return NULL;
 }
@@ -172,6 +185,17 @@ static int __init hugetlb_setup(char *s)
 }
 __setup("hugepages=", hugetlb_setup);
 
+static unsigned int cpuset_mems_nr(unsigned int *array)
+{
+       int node;
+       unsigned int nr = 0;
+
+       for_each_node_mask(node, cpuset_current_mems_allowed)
+               nr += array[node];
+
+       return nr;
+}
+
 #ifdef CONFIG_SYSCTL
 static void update_and_free_page(struct page *page)
 {
@@ -243,6 +267,19 @@ int hugetlb_sysctl_handler(struct ctl_table *table, int write,
        max_huge_pages = set_max_huge_pages(max_huge_pages);
        return 0;
 }
+
+int hugetlb_treat_movable_handler(struct ctl_table *table, int write,
+                       struct file *file, void __user *buffer,
+                       size_t *length, loff_t *ppos)
+{
+       proc_dointvec(table, write, file, buffer, length, ppos);
+       if (hugepages_treat_as_movable)
+               htlb_alloc_mask = GFP_HIGHUSER_MOVABLE;
+       else
+               htlb_alloc_mask = GFP_HIGHUSER;
+       return 0;
+}
+
 #endif /* CONFIG_SYSCTL */
 
 int hugetlb_report_meminfo(char *buf)
@@ -313,9 +350,10 @@ static void set_huge_ptep_writable(struct vm_area_struct *vma,
        pte_t entry;
 
        entry = pte_mkwrite(pte_mkdirty(*ptep));
-       ptep_set_access_flags(vma, address, ptep, entry, 1);
-       update_mmu_cache(vma, address, entry);
-       lazy_mmu_prot_update(entry);
+       if (ptep_set_access_flags(vma, address, ptep, entry, 1)) {
+               update_mmu_cache(vma, address, entry);
+               lazy_mmu_prot_update(entry);
+       }
 }
 
 
@@ -389,6 +427,8 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
                        continue;
 
                page = pte_page(pte);
+               if (pte_dirty(pte))
+                       set_page_dirty(page);
                list_add(&page->lru, &page_list);
        }
        spin_unlock(&mm->page_table_lock);
@@ -458,7 +498,7 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
        return VM_FAULT_MINOR;
 }
 
-int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma,
+static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma,
                        unsigned long address, pte_t *ptep, int write_access)
 {
        int ret = VM_FAULT_SIGBUS;
@@ -815,6 +855,26 @@ int hugetlb_reserve_pages(struct inode *inode, long from, long to)
        chg = region_chg(&inode->i_mapping->private_list, from, to);
        if (chg < 0)
                return chg;
+       /*
+        * When cpuset is configured, it breaks the strict hugetlb page
+        * reservation as the accounting is done on a global variable. Such
+        * reservation is completely rubbish in the presence of cpuset because
+        * the reservation is not checked against page availability for the
+        * current cpuset. Application can still potentially OOM'ed by kernel
+        * with lack of free htlb page in cpuset that the task is in.
+        * Attempt to enforce strict accounting with cpuset is almost
+        * impossible (or too ugly) because cpuset is too fluid that
+        * task or memory node can be dynamically moved between cpusets.
+        *
+        * The change of semantics for shared hugetlb mapping with cpuset is
+        * undesirable. However, in order to preserve some of the semantics,
+        * we fall back to check against current free page availability as
+        * a best attempt and hopefully to minimize the impact of changing
+        * semantics that cpuset has.
+        */
+       if (chg > cpuset_mems_nr(free_huge_pages_node))
+               return -ENOMEM;
+
        ret = hugetlb_acct_memory(chg);
        if (ret < 0)
                return ret;