参照元

説明

引数

返り値

参考

実装

/**
 * alloc_contig_range() -- tries to allocate given range of pages
 * @start:      start PFN to allocate
 * @end:        one-past-the-last PFN to allocate
 * @migratetype:        migratetype of the underlaying pageblocks (either
 *                      #MIGRATE_MOVABLE or #MIGRATE_CMA).  All pageblocks
 *                      in range must have the same migratetype and it must
 *                      be either of the two.
 *
 * The PFN range does not have to be pageblock or MAX_ORDER_NR_PAGES
 * aligned, however it's the caller's responsibility to guarantee that
 * we are the only thread that changes migrate type of pageblocks the
 * pages fall in.
 *
 * The PFN range must belong to a single zone.
 *                     
 * Returns zero on success or negative error code.  On success all
 * pages which PFN is in [start, end) are allocated for the caller and
 * need to be freed with free_contig_range().
 */     
int alloc_contig_range(unsigned long start, unsigned long end,
                       unsigned migratetype)
{
        unsigned long outer_start, outer_end;
        unsigned int order;
        int ret = 0;

        struct compact_control cc = {
                .nr_migratepages = 0,
                .order = -1,
                .zone = page_zone(pfn_to_page(start)),
                .mode = MIGRATE_SYNC,
                .ignore_skip_hint = true,
        };
        INIT_LIST_HEAD(&cc.migratepages);
        /*
         * What we do here is we mark all pageblocks in range as
         * MIGRATE_ISOLATE.  Because pageblock and max order pages may
         * have different sizes, and due to the way page allocator
         * work, we align the range to biggest of the two pages so
         * that page allocator won't try to merge buddies from
         * different pageblocks and change MIGRATE_ISOLATE to some
         * other migration type.
         *
         * Once the pageblocks are marked as MIGRATE_ISOLATE, we
         * migrate the pages from an unaligned range (ie. pages that
         * we are interested in).  This will put all the pages in
         * range back to page allocator as MIGRATE_ISOLATE.
         *
         * When this is done, we take the pages in range from page
         * allocator removing them from the buddy system.  This way
         * page allocator will never consider using them.
         *
         * This lets us mark the pageblocks back as
         * MIGRATE_CMA/MIGRATE_MOVABLE so that free pages in the
         * aligned range but not in the unaligned, original range are
         * put back to page allocator so that buddy can use them.
         */

        ret = start_isolate_page_range(pfn_max_align_down(start),
                                       pfn_max_align_up(end), migratetype,
                                       false);
        if (ret)
                return ret;
        ret = __alloc_contig_migrate_range(&cc, start, end);
        if (ret)
                goto done;
        /*
         * Pages from [start, end) are within a MAX_ORDER_NR_PAGES
         * aligned blocks that are marked as MIGRATE_ISOLATE.  What's
         * more, all pages in [start, end) are free in page allocator.
         * What we are going to do is to allocate all pages from
         * [start, end) (that is remove them from page allocator).
         *
         * The only problem is that pages at the beginning and at the
         * end of interesting range may be not aligned with pages that
         * page allocator holds, ie. they can be part of higher order
         * pages.  Because of this, we reserve the bigger range and
         * once this is done free the pages we are not interested in.
         *
         * We don't have to hold zone->lock here because the pages are
         * isolated thus they won't get removed from buddy.
         */

        lru_add_drain_all();
        drain_all_pages(cc.zone);
        order = 0;
        outer_start = start;
        while (!PageBuddy(pfn_to_page(outer_start))) {
                if (++order >= MAX_ORDER) {
                        ret = -EBUSY;
                        goto done;
                }
                outer_start &= ~0UL << order;
        }
        /* Make sure the range is really isolated. */
        if (test_pages_isolated(outer_start, end, false)) {
                pr_info("%s: [%lx, %lx) PFNs busy\n",
                        __func__, outer_start, end);
                ret = -EBUSY;
                goto done;
        }
        /* Grab isolated pages from freelists. */
        outer_end = isolate_freepages_range(&cc, outer_start, end);
        if (!outer_end) {
                ret = -EBUSY;
                goto done;
        }
        /* Free head and tail (if any) */
        if (start != outer_start)
                free_contig_range(outer_start, start - outer_start);
        if (end != outer_end)
                free_contig_range(end, outer_end - end);
done:
        undo_isolate_page_range(pfn_max_align_down(start),
                                pfn_max_align_up(end), migratetype);
        return ret;
}

コメント


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS