From 098d7f128a4e53cb64930628915ac767785e0e60 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 16 Oct 2007 23:25:55 -0700 Subject: oom: add per-zone locking OOM killer synchronization should be done with zone granularity so that memory policy and cpuset allocations may have their corresponding zones locked and allow parallel kills for other OOM conditions that may exist elsewhere in the system. DMA allocations can be targeted at the zone level, which would not be possible if locking was done in nodes or globally. Synchronization shall be done with a variation of "trylocks." The goal is to put the current task to sleep and restart the failed allocation attempt later if the trylock fails. Otherwise, the OOM killer is invoked. Each zone in the zonelist that __alloc_pages() was called with is checked for the newly-introduced ZONE_OOM_LOCKED flag. If any zone has this flag present, the "trylock" to serialize the OOM killer fails and returns zero. Otherwise, all the zones have ZONE_OOM_LOCKED set and the try_set_zone_oom() function returns non-zero. Cc: Andrea Arcangeli Cc: Christoph Lameter Signed-off-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/oom_kill.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'mm/oom_kill.c') diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 1a7a4ef04e27..6e999c88c503 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -27,6 +27,7 @@ #include int sysctl_panic_on_oom; +static DEFINE_MUTEX(zone_scan_mutex); /* #define DEBUG */ /** @@ -374,6 +375,57 @@ int unregister_oom_notifier(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(unregister_oom_notifier); +/* + * Try to acquire the OOM killer lock for the zones in zonelist. Returns zero + * if a parallel OOM killing is already taking place that includes a zone in + * the zonelist. Otherwise, locks all zones in the zonelist and returns 1. + */ +int try_set_zone_oom(struct zonelist *zonelist) +{ + struct zone **z; + int ret = 1; + + z = zonelist->zones; + + mutex_lock(&zone_scan_mutex); + do { + if (zone_is_oom_locked(*z)) { + ret = 0; + goto out; + } + } while (*(++z) != NULL); + + /* + * Lock each zone in the zonelist under zone_scan_mutex so a parallel + * invocation of try_set_zone_oom() doesn't succeed when it shouldn't. + */ + z = zonelist->zones; + do { + zone_set_flag(*z, ZONE_OOM_LOCKED); + } while (*(++z) != NULL); +out: + mutex_unlock(&zone_scan_mutex); + return ret; +} + +/* + * Clears the ZONE_OOM_LOCKED flag for all zones in the zonelist so that failed + * allocation attempts with zonelists containing them may now recall the OOM + * killer, if necessary. + */ +void clear_zonelist_oom(struct zonelist *zonelist) +{ + struct zone **z; + + z = zonelist->zones; + + mutex_lock(&zone_scan_mutex); + do { + zone_clear_flag(*z, ZONE_OOM_LOCKED); + } while (*(++z) != NULL); + mutex_unlock(&zone_scan_mutex); +} + /** * out_of_memory - kill the "best" process when we run out of memory * -- cgit v1.2.3