*参照元 [#o2045ad6] #backlinks *説明 [#hb157032] -パス: [[linux-4.4.1/]] -パス: [[linux-4.4.1/mm/page_alloc.c]] -FIXME: これは何? --説明 **引数 [#u89957b1] - -unsigned long start -- -unsigned long end -- -unsigned migratetype -- **返り値 [#hce649ef] - -int -- **参考 [#i5d2b328] *実装 [#c3dd730c] /** * 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); - --[[linux-4.4.1/compact_control]] --[[linux-4.4.1/page_zone()]] --[[linux-4.4.1/pfn_to_page()]] --[[linux-4.4.1/INIT_LIST_HEAD()]] /* * 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; - --[[linux-4.4.1/start_isolate_page_range()]] --[[linux-4.4.1/pfn_max_align_down()]] --[[linux-4.4.1/pfn_max_align_up()]] ret = __alloc_contig_migrate_range(&cc, start, end); if (ret) goto done; - --[[linux-4.4.1/__alloc_contig_migrate_range()]] /* * 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); - --[[linux-4.4.1/lru_add_drain_all()]] --[[linux-4.4.1/drain_all_pages()]] order = 0; outer_start = start; while (!PageBuddy(pfn_to_page(outer_start))) { if (++order >= MAX_ORDER) { ret = -EBUSY; goto done; } outer_start &= ~0UL << order; } - --[[linux-4.4.1/PageBuddy()]] /* 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; } - --[[linux-4.4.1/test_pages_isolated()]] /* Grab isolated pages from freelists. */ outer_end = isolate_freepages_range(&cc, outer_start, end); if (!outer_end) { ret = -EBUSY; goto done; } - --[[linux-4.4.1/isolate_freepages_range()]] /* 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); - --[[linux-4.4.1/free_contig_range()]] done: undo_isolate_page_range(pfn_max_align_down(start), pfn_max_align_up(end), migratetype); - --[[linux-4.4.1/undo_isolate_page_range()]] return ret; } *コメント [#a2b5fb0a]