mirror of
https://github.com/team-infusion-developers/android_kernel_samsung_msm8976.git
synced 2024-10-31 18:09:19 +00:00
8b283f35d3
There are two paths to reach core free function of buddy allocator, __free_one_page(), one is free_one_page()->__free_one_page() and the other is free_hot_cold_page()->free_pcppages_bulk()->__free_one_page(). Each paths has race condition causing serious problems. At first, this patch is focused on first type of freepath. And then, following patch will solve the problem in second type of freepath. In the first type of freepath, we got migratetype of freeing page without holding the zone lock, so it could be racy. There are two cases of this race. 1. pages are added to isolate buddy list after restoring orignal migratetype CPU1 CPU2 get migratetype => return MIGRATE_ISOLATE call free_one_page() with MIGRATE_ISOLATE grab the zone lock unisolate pageblock release the zone lock grab the zone lock call __free_one_page() with MIGRATE_ISOLATE freepage go into isolate buddy list, although pageblock is already unisolated This may cause two problems. One is that we can't use this page anymore until next isolation attempt of this pageblock, because freepage is on isolate buddy list. The other is that freepage accouting could be wrong due to merging between different buddy list. Freepages on isolate buddy list aren't counted as freepage, but ones on normal buddy list are counted as freepage. If merge happens, buddy freepage on normal buddy list is inevitably moved to isolate buddy list without any consideration of freepage accouting so it could be incorrect. 2. pages are added to normal buddy list while pageblock is isolated. It is similar with above case. This also may cause two problems. One is that we can't keep these freepages from being allocated. Although this pageblock is isolated, freepage would be added to normal buddy list so that it could be allocated without any restriction. And the other problem is same as case 1, that it, incorrect freepage accouting. This race condition would be prevented by checking migratetype again with holding the zone lock. Because it is somewhat heavy operation and it isn't needed in common case, we want to avoid rechecking as much as possible. So this patch introduce new variable, nr_isolate_pageblock in struct zone to check if there is isolated pageblock. With this, we can avoid to re-check migratetype in common case and do it only if there is isolated pageblock or migratetype is MIGRATE_ISOLATE. This solve above mentioned problems. Cc: <stable@vger.kernel.org> Acked-by: Minchan Kim <minchan@kernel.org> Acked-by: Michal Nazarewicz <mina86@mina86.com> Acked-by: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Joonsoo Kim <iamjoonsoo.kim@lge.com> Patch-mainline: linux-kernel @ 10/31/2014 16:25:27 [vinmenon@codeaurora.org: get_pfnblock_migratetype replaced by get_pageblock_migratetype plus fixed merge conflicts] Signed-off-by: Vinayak Menon <vinmenon@codeaurora.org> CRs-fixed: 720761 Change-Id: I8b4a47b8986900f9e0f5364692b5914d19bf189c
76 lines
2.1 KiB
C
76 lines
2.1 KiB
C
#ifndef __LINUX_PAGEISOLATION_H
|
|
#define __LINUX_PAGEISOLATION_H
|
|
|
|
#ifdef CONFIG_MEMORY_ISOLATION
|
|
static inline bool has_isolate_pageblock(struct zone *zone)
|
|
{
|
|
return zone->nr_isolate_pageblock;
|
|
}
|
|
static inline bool is_migrate_isolate_page(struct page *page)
|
|
{
|
|
return get_pageblock_migratetype(page) == MIGRATE_ISOLATE;
|
|
}
|
|
static inline bool is_migrate_isolate(int migratetype)
|
|
{
|
|
return migratetype == MIGRATE_ISOLATE;
|
|
}
|
|
#else
|
|
static inline bool has_isolate_pageblock(struct zone *zone)
|
|
{
|
|
return false;
|
|
}
|
|
static inline bool is_migrate_isolate_page(struct page *page)
|
|
{
|
|
return false;
|
|
}
|
|
static inline bool is_migrate_isolate(int migratetype)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool has_unmovable_pages(struct zone *zone, struct page *page, int count,
|
|
bool skip_hwpoisoned_pages);
|
|
void set_pageblock_migratetype(struct page *page, int migratetype);
|
|
int move_freepages_block(struct zone *zone, struct page *page,
|
|
int migratetype);
|
|
int move_freepages(struct zone *zone,
|
|
struct page *start_page, struct page *end_page,
|
|
int migratetype);
|
|
|
|
/*
|
|
* Changes migrate type in [start_pfn, end_pfn) to be MIGRATE_ISOLATE.
|
|
* If specified range includes migrate types other than MOVABLE or CMA,
|
|
* this will fail with -EBUSY.
|
|
*
|
|
* For isolating all pages in the range finally, the caller have to
|
|
* free all pages in the range. test_page_isolated() can be used for
|
|
* test it.
|
|
*/
|
|
int
|
|
start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
|
|
unsigned migratetype, bool skip_hwpoisoned_pages);
|
|
|
|
/*
|
|
* Changes MIGRATE_ISOLATE to MIGRATE_MOVABLE.
|
|
* target range is [start_pfn, end_pfn)
|
|
*/
|
|
int
|
|
undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
|
|
unsigned migratetype);
|
|
|
|
/*
|
|
* Test all pages in [start_pfn, end_pfn) are isolated or not.
|
|
*/
|
|
int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn,
|
|
bool skip_hwpoisoned_pages);
|
|
|
|
/*
|
|
* Internal functions. Changes pageblock's migrate type.
|
|
*/
|
|
int set_migratetype_isolate(struct page *page, bool skip_hwpoisoned_pages);
|
|
void unset_migratetype_isolate(struct page *page, unsigned migratetype);
|
|
struct page *alloc_migrate_target(struct page *page, unsigned long private,
|
|
int **resultp);
|
|
|
|
#endif
|