OSDN Git Service

cxl: Check if afu is not null in cxl_slbia
[uclinux-h8/linux.git] / mm / hugetlb.c
index 271e443..a8c3087 100644 (file)
@@ -40,6 +40,11 @@ int hugepages_treat_as_movable;
 int hugetlb_max_hstate __read_mostly;
 unsigned int default_hstate_idx;
 struct hstate hstates[HUGE_MAX_HSTATE];
+/*
+ * Minimum page order among possible hugepage sizes, set to a proper value
+ * at boot time.
+ */
+static unsigned int minimum_order __read_mostly = UINT_MAX;
 
 __initdata LIST_HEAD(huge_boot_pages);
 
@@ -212,8 +217,20 @@ static inline struct hugepage_subpool *subpool_vma(struct vm_area_struct *vma)
  * Region tracking -- allows tracking of reservations and instantiated pages
  *                    across the pages in a mapping.
  *
- * The region data structures are embedded into a resv_map and
- * protected by a resv_map's lock
+ * The region data structures are embedded into a resv_map and protected
+ * by a resv_map's lock.  The set of regions within the resv_map represent
+ * reservations for huge pages, or huge pages that have already been
+ * instantiated within the map.  The from and to elements are huge page
+ * indicies into the associated mapping.  from indicates the starting index
+ * of the region.  to represents the first index past the end of  the region.
+ *
+ * For example, a file region structure with from == 0 and to == 4 represents
+ * four huge pages in a mapping.  It is important to note that the to element
+ * represents the first element past the end of the region. This is used in
+ * arithmetic as 4(to) - 0(from) = 4 huge pages in the region.
+ *
+ * Interval notation of the form [from, to) will be used to indicate that
+ * the endpoint from is inclusive and to is exclusive.
  */
 struct file_region {
        struct list_head link;
@@ -221,10 +238,22 @@ struct file_region {
        long to;
 };
 
+/*
+ * Add the huge page range represented by [f, t) to the reserve
+ * map.  Existing regions will be expanded to accommodate the
+ * specified range.  We know only existing regions need to be
+ * expanded, because region_add is only called after region_chg
+ * with the same range.  If a new file_region structure must
+ * be allocated, it is done in region_chg.
+ *
+ * Return the number of new huge pages added to the map.  This
+ * number is greater than or equal to zero.
+ */
 static long region_add(struct resv_map *resv, long f, long t)
 {
        struct list_head *head = &resv->regions;
        struct file_region *rg, *nrg, *trg;
+       long add = 0;
 
        spin_lock(&resv->lock);
        /* Locate the region we are either in or before. */
@@ -250,16 +279,45 @@ static long region_add(struct resv_map *resv, long f, long t)
                if (rg->to > t)
                        t = rg->to;
                if (rg != nrg) {
+                       /* Decrement return value by the deleted range.
+                        * Another range will span this area so that by
+                        * end of routine add will be >= zero
+                        */
+                       add -= (rg->to - rg->from);
                        list_del(&rg->link);
                        kfree(rg);
                }
        }
+
+       add += (nrg->from - f);         /* Added to beginning of region */
        nrg->from = f;
+       add += t - nrg->to;             /* Added to end of region */
        nrg->to = t;
+
        spin_unlock(&resv->lock);
-       return 0;
+       VM_BUG_ON(add < 0);
+       return add;
 }
 
+/*
+ * Examine the existing reserve map and determine how many
+ * huge pages in the specified range [f, t) are NOT currently
+ * represented.  This routine is called before a subsequent
+ * call to region_add that will actually modify the reserve
+ * map to add the specified range [f, t).  region_chg does
+ * not change the number of huge pages represented by the
+ * map.  However, if the existing regions in the map can not
+ * be expanded to represent the new range, a new file_region
+ * structure is added to the map as a placeholder.  This is
+ * so that the subsequent region_add call will have all the
+ * regions it needs and will not fail.
+ *
+ * Returns the number of huge pages that need to be added
+ * to the existing reservation map for the range [f, t).
+ * This number is greater or equal to zero.  -ENOMEM is
+ * returned if a new file_region structure is needed and can
+ * not be allocated.
+ */
 static long region_chg(struct resv_map *resv, long f, long t)
 {
        struct list_head *head = &resv->regions;
@@ -326,6 +384,11 @@ out_nrg:
        return chg;
 }
 
+/*
+ * Truncate the reserve map at index 'end'.  Modify/truncate any
+ * region which contains end.  Delete any regions past end.
+ * Return the number of huge pages removed from the map.
+ */
 static long region_truncate(struct resv_map *resv, long end)
 {
        struct list_head *head = &resv->regions;
@@ -361,6 +424,10 @@ out:
        return chg;
 }
 
+/*
+ * Count and return the number of huge pages in the reserve map
+ * that intersect with the range [f, t).
+ */
 static long region_count(struct resv_map *resv, long f, long t)
 {
        struct list_head *head = &resv->regions;
@@ -908,7 +975,6 @@ static void update_and_free_page(struct hstate *h, struct page *page)
                destroy_compound_gigantic_page(page, huge_page_order(h));
                free_gigantic_page(page, huge_page_order(h));
        } else {
-               arch_release_hugepage(page);
                __free_pages(page, huge_page_order(h));
        }
 }
@@ -1093,10 +1159,6 @@ static struct page *alloc_fresh_huge_page_node(struct hstate *h, int nid)
                                                __GFP_REPEAT|__GFP_NOWARN,
                huge_page_order(h));
        if (page) {
-               if (arch_prepare_hugepage(page)) {
-                       __free_pages(page, huge_page_order(h));
-                       return NULL;
-               }
                prep_new_huge_page(h, page, nid);
        }
 
@@ -1188,19 +1250,13 @@ static void dissolve_free_huge_page(struct page *page)
  */
 void dissolve_free_huge_pages(unsigned long start_pfn, unsigned long end_pfn)
 {
-       unsigned int order = 8 * sizeof(void *);
        unsigned long pfn;
-       struct hstate *h;
 
        if (!hugepages_supported())
                return;
 
-       /* Set scan step to minimum hugepage size */
-       for_each_hstate(h)
-               if (order > huge_page_order(h))
-                       order = huge_page_order(h);
-       VM_BUG_ON(!IS_ALIGNED(start_pfn, 1 << order));
-       for (pfn = start_pfn; pfn < end_pfn; pfn += 1 << order)
+       VM_BUG_ON(!IS_ALIGNED(start_pfn, 1 << minimum_order));
+       for (pfn = start_pfn; pfn < end_pfn; pfn += 1 << minimum_order)
                dissolve_free_huge_page(pfn_to_page(pfn));
 }
 
@@ -1254,11 +1310,6 @@ static struct page *alloc_buddy_huge_page(struct hstate *h, int nid)
                        htlb_alloc_mask(h)|__GFP_COMP|__GFP_THISNODE|
                        __GFP_REPEAT|__GFP_NOWARN, huge_page_order(h));
 
-       if (page && arch_prepare_hugepage(page)) {
-               __free_pages(page, huge_page_order(h));
-               page = NULL;
-       }
-
        spin_lock(&hugetlb_lock);
        if (page) {
                INIT_LIST_HEAD(&page->lru);
@@ -1423,46 +1474,56 @@ static void return_unused_surplus_pages(struct hstate *h,
 }
 
 /*
- * Determine if the huge page at addr within the vma has an associated
- * reservation.  Where it does not we will need to logically increase
- * reservation and actually increase subpool usage before an allocation
- * can occur.  Where any new reservation would be required the
- * reservation change is prepared, but not committed.  Once the page
- * has been allocated from the subpool and instantiated the change should
- * be committed via vma_commit_reservation.  No action is required on
- * failure.
+ * vma_needs_reservation and vma_commit_reservation are used by the huge
+ * page allocation routines to manage reservations.
+ *
+ * vma_needs_reservation is called to determine if the huge page at addr
+ * within the vma has an associated reservation.  If a reservation is
+ * needed, the value 1 is returned.  The caller is then responsible for
+ * managing the global reservation and subpool usage counts.  After
+ * the huge page has been allocated, vma_commit_reservation is called
+ * to add the page to the reservation map.
+ *
+ * In the normal case, vma_commit_reservation returns the same value
+ * as the preceding vma_needs_reservation call.  The only time this
+ * is not the case is if a reserve map was changed between calls.  It
+ * is the responsibility of the caller to notice the difference and
+ * take appropriate action.
  */
-static long vma_needs_reservation(struct hstate *h,
-                       struct vm_area_struct *vma, unsigned long addr)
+static long __vma_reservation_common(struct hstate *h,
+                               struct vm_area_struct *vma, unsigned long addr,
+                               bool commit)
 {
        struct resv_map *resv;
        pgoff_t idx;
-       long chg;
+       long ret;
 
        resv = vma_resv_map(vma);
        if (!resv)
                return 1;
 
        idx = vma_hugecache_offset(h, vma, addr);
-       chg = region_chg(resv, idx, idx + 1);
+       if (commit)
+               ret = region_add(resv, idx, idx + 1);
+       else
+               ret = region_chg(resv, idx, idx + 1);
 
        if (vma->vm_flags & VM_MAYSHARE)
-               return chg;
+               return ret;
        else
-               return chg < 0 ? chg : 0;
+               return ret < 0 ? ret : 0;
 }
-static void vma_commit_reservation(struct hstate *h,
+
+static long vma_needs_reservation(struct hstate *h,
                        struct vm_area_struct *vma, unsigned long addr)
 {
-       struct resv_map *resv;
-       pgoff_t idx;
-
-       resv = vma_resv_map(vma);
-       if (!resv)
-               return;
+       return __vma_reservation_common(h, vma, addr, false);
+}
 
-       idx = vma_hugecache_offset(h, vma, addr);
-       region_add(resv, idx, idx + 1);
+static long vma_commit_reservation(struct hstate *h,
+                       struct vm_area_struct *vma, unsigned long addr)
+{
+       return __vma_reservation_common(h, vma, addr, true);
 }
 
 static struct page *alloc_huge_page(struct vm_area_struct *vma,
@@ -1471,7 +1532,7 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
        struct hugepage_subpool *spool = subpool_vma(vma);
        struct hstate *h = hstate_vma(vma);
        struct page *page;
-       long chg;
+       long chg, commit;
        int ret, idx;
        struct hugetlb_cgroup *h_cg;
 
@@ -1512,7 +1573,22 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
 
        set_page_private(page, (unsigned long)spool);
 
-       vma_commit_reservation(h, vma, addr);
+       commit = vma_commit_reservation(h, vma, addr);
+       if (unlikely(chg > commit)) {
+               /*
+                * The page was added to the reservation map between
+                * vma_needs_reservation and vma_commit_reservation.
+                * This indicates a race with hugetlb_reserve_pages.
+                * Adjust for the subpool count incremented above AND
+                * in hugetlb_reserve_pages for the same page.  Also,
+                * the reservation count added in hugetlb_reserve_pages
+                * no longer applies.
+                */
+               long rsv_adjust;
+
+               rsv_adjust = hugepage_subpool_put_pages(spool, 1);
+               hugetlb_acct_memory(h, -rsv_adjust);
+       }
        return page;
 
 out_uncharge_cgroup:
@@ -1627,10 +1703,14 @@ static void __init hugetlb_init_hstates(void)
        struct hstate *h;
 
        for_each_hstate(h) {
+               if (minimum_order > huge_page_order(h))
+                       minimum_order = huge_page_order(h);
+
                /* oversize hugepages were init'ed in early boot */
                if (!hstate_is_gigantic(h))
                        hugetlb_hstate_alloc_pages(h);
        }
+       VM_BUG_ON(minimum_order == UINT_MAX);
 }
 
 static char * __init memfmt(char *buf, unsigned long n)
@@ -3626,8 +3706,24 @@ int hugetlb_reserve_pages(struct inode *inode,
         * consumed reservations are stored in the map. Hence, nothing
         * else has to be done for private mappings here
         */
-       if (!vma || vma->vm_flags & VM_MAYSHARE)
-               region_add(resv_map, from, to);
+       if (!vma || vma->vm_flags & VM_MAYSHARE) {
+               long add = region_add(resv_map, from, to);
+
+               if (unlikely(chg > add)) {
+                       /*
+                        * pages in this range were added to the reserve
+                        * map between region_chg and region_add.  This
+                        * indicates a race with alloc_huge_page.  Adjust
+                        * the subpool and reserve counts modified above
+                        * based on the difference.
+                        */
+                       long rsv_adjust;
+
+                       rsv_adjust = hugepage_subpool_put_pages(spool,
+                                                               chg - add);
+                       hugetlb_acct_memory(h, -rsv_adjust);
+               }
+       }
        return 0;
 out_err:
        if (vma && is_vma_resv_set(vma, HPAGE_RESV_OWNER))
@@ -3789,6 +3885,11 @@ pte_t *huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
 {
        return NULL;
 }
+
+int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep)
+{
+       return 0;
+}
 #define want_pmd_share()       (0)
 #endif /* CONFIG_ARCH_WANT_HUGE_PMD_SHARE */