diff options
-rw-r--r-- | mm/memcontrol.c | 83 |
1 files changed, 46 insertions, 37 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 0980bbf6438d..04250cbf46c6 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -1916,15 +1916,18 @@ mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg, | |||
1916 | return SKIP; | 1916 | return SKIP; |
1917 | } | 1917 | } |
1918 | 1918 | ||
1919 | static DEFINE_SPINLOCK(memcg_oom_lock); | ||
1920 | |||
1919 | /* | 1921 | /* |
1920 | * Check OOM-Killer is already running under our hierarchy. | 1922 | * Check OOM-Killer is already running under our hierarchy. |
1921 | * If someone is running, return false. | 1923 | * If someone is running, return false. |
1922 | * Has to be called with memcg_oom_lock | ||
1923 | */ | 1924 | */ |
1924 | static bool mem_cgroup_oom_lock(struct mem_cgroup *memcg) | 1925 | static bool mem_cgroup_oom_trylock(struct mem_cgroup *memcg) |
1925 | { | 1926 | { |
1926 | struct mem_cgroup *iter, *failed = NULL; | 1927 | struct mem_cgroup *iter, *failed = NULL; |
1927 | 1928 | ||
1929 | spin_lock(&memcg_oom_lock); | ||
1930 | |||
1928 | for_each_mem_cgroup_tree(iter, memcg) { | 1931 | for_each_mem_cgroup_tree(iter, memcg) { |
1929 | if (iter->oom_lock) { | 1932 | if (iter->oom_lock) { |
1930 | /* | 1933 | /* |
@@ -1938,33 +1941,33 @@ static bool mem_cgroup_oom_lock(struct mem_cgroup *memcg) | |||
1938 | iter->oom_lock = true; | 1941 | iter->oom_lock = true; |
1939 | } | 1942 | } |
1940 | 1943 | ||
1941 | if (!failed) | 1944 | if (failed) { |
1942 | return true; | 1945 | /* |
1943 | 1946 | * OK, we failed to lock the whole subtree so we have | |
1944 | /* | 1947 | * to clean up what we set up to the failing subtree |
1945 | * OK, we failed to lock the whole subtree so we have to clean up | 1948 | */ |
1946 | * what we set up to the failing subtree | 1949 | for_each_mem_cgroup_tree(iter, memcg) { |
1947 | */ | 1950 | if (iter == failed) { |
1948 | for_each_mem_cgroup_tree(iter, memcg) { | 1951 | mem_cgroup_iter_break(memcg, iter); |
1949 | if (iter == failed) { | 1952 | break; |
1950 | mem_cgroup_iter_break(memcg, iter); | 1953 | } |
1951 | break; | 1954 | iter->oom_lock = false; |
1952 | } | 1955 | } |
1953 | iter->oom_lock = false; | ||
1954 | } | 1956 | } |
1955 | return false; | 1957 | |
1958 | spin_unlock(&memcg_oom_lock); | ||
1959 | |||
1960 | return !failed; | ||
1956 | } | 1961 | } |
1957 | 1962 | ||
1958 | /* | 1963 | static void mem_cgroup_oom_unlock(struct mem_cgroup *memcg) |
1959 | * Has to be called with memcg_oom_lock | ||
1960 | */ | ||
1961 | static int mem_cgroup_oom_unlock(struct mem_cgroup *memcg) | ||
1962 | { | 1964 | { |
1963 | struct mem_cgroup *iter; | 1965 | struct mem_cgroup *iter; |
1964 | 1966 | ||
1967 | spin_lock(&memcg_oom_lock); | ||
1965 | for_each_mem_cgroup_tree(iter, memcg) | 1968 | for_each_mem_cgroup_tree(iter, memcg) |
1966 | iter->oom_lock = false; | 1969 | iter->oom_lock = false; |
1967 | return 0; | 1970 | spin_unlock(&memcg_oom_lock); |
1968 | } | 1971 | } |
1969 | 1972 | ||
1970 | static void mem_cgroup_mark_under_oom(struct mem_cgroup *memcg) | 1973 | static void mem_cgroup_mark_under_oom(struct mem_cgroup *memcg) |
@@ -1988,7 +1991,6 @@ static void mem_cgroup_unmark_under_oom(struct mem_cgroup *memcg) | |||
1988 | atomic_add_unless(&iter->under_oom, -1, 0); | 1991 | atomic_add_unless(&iter->under_oom, -1, 0); |
1989 | } | 1992 | } |
1990 | 1993 | ||
1991 | static DEFINE_SPINLOCK(memcg_oom_lock); | ||
1992 | static DECLARE_WAIT_QUEUE_HEAD(memcg_oom_waitq); | 1994 | static DECLARE_WAIT_QUEUE_HEAD(memcg_oom_waitq); |
1993 | 1995 | ||
1994 | struct oom_wait_info { | 1996 | struct oom_wait_info { |
@@ -2035,45 +2037,52 @@ static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, | |||
2035 | int order) | 2037 | int order) |
2036 | { | 2038 | { |
2037 | struct oom_wait_info owait; | 2039 | struct oom_wait_info owait; |
2038 | bool locked, need_to_kill; | 2040 | bool locked; |
2039 | 2041 | ||
2040 | owait.memcg = memcg; | 2042 | owait.memcg = memcg; |
2041 | owait.wait.flags = 0; | 2043 | owait.wait.flags = 0; |
2042 | owait.wait.func = memcg_oom_wake_function; | 2044 | owait.wait.func = memcg_oom_wake_function; |
2043 | owait.wait.private = current; | 2045 | owait.wait.private = current; |
2044 | INIT_LIST_HEAD(&owait.wait.task_list); | 2046 | INIT_LIST_HEAD(&owait.wait.task_list); |
2045 | need_to_kill = true; | ||
2046 | mem_cgroup_mark_under_oom(memcg); | ||
2047 | 2047 | ||
2048 | /* At first, try to OOM lock hierarchy under memcg.*/ | ||
2049 | spin_lock(&memcg_oom_lock); | ||
2050 | locked = mem_cgroup_oom_lock(memcg); | ||
2051 | /* | 2048 | /* |
2049 | * As with any blocking lock, a contender needs to start | ||
2050 | * listening for wakeups before attempting the trylock, | ||
2051 | * otherwise it can miss the wakeup from the unlock and sleep | ||
2052 | * indefinitely. This is just open-coded because our locking | ||
2053 | * is so particular to memcg hierarchies. | ||
2054 | * | ||
2052 | * Even if signal_pending(), we can't quit charge() loop without | 2055 | * Even if signal_pending(), we can't quit charge() loop without |
2053 | * accounting. So, UNINTERRUPTIBLE is appropriate. But SIGKILL | 2056 | * accounting. So, UNINTERRUPTIBLE is appropriate. But SIGKILL |
2054 | * under OOM is always welcomed, use TASK_KILLABLE here. | 2057 | * under OOM is always welcomed, use TASK_KILLABLE here. |
2055 | */ | 2058 | */ |
2056 | prepare_to_wait(&memcg_oom_waitq, &owait.wait, TASK_KILLABLE); | 2059 | prepare_to_wait(&memcg_oom_waitq, &owait.wait, TASK_KILLABLE); |
2057 | if (!locked || memcg->oom_kill_disable) | 2060 | mem_cgroup_mark_under_oom(memcg); |
2058 | need_to_kill = false; | 2061 | |
2062 | locked = mem_cgroup_oom_trylock(memcg); | ||
2063 | |||
2059 | if (locked) | 2064 | if (locked) |
2060 | mem_cgroup_oom_notify(memcg); | 2065 | mem_cgroup_oom_notify(memcg); |
2061 | spin_unlock(&memcg_oom_lock); | ||
2062 | 2066 | ||
2063 | if (need_to_kill) { | 2067 | if (locked && !memcg->oom_kill_disable) { |
2068 | mem_cgroup_unmark_under_oom(memcg); | ||
2064 | finish_wait(&memcg_oom_waitq, &owait.wait); | 2069 | finish_wait(&memcg_oom_waitq, &owait.wait); |
2065 | mem_cgroup_out_of_memory(memcg, mask, order); | 2070 | mem_cgroup_out_of_memory(memcg, mask, order); |
2066 | } else { | 2071 | } else { |
2067 | schedule(); | 2072 | schedule(); |
2073 | mem_cgroup_unmark_under_oom(memcg); | ||
2068 | finish_wait(&memcg_oom_waitq, &owait.wait); | 2074 | finish_wait(&memcg_oom_waitq, &owait.wait); |
2069 | } | 2075 | } |
2070 | spin_lock(&memcg_oom_lock); | ||
2071 | if (locked) | ||
2072 | mem_cgroup_oom_unlock(memcg); | ||
2073 | memcg_wakeup_oom(memcg); | ||
2074 | spin_unlock(&memcg_oom_lock); | ||
2075 | 2076 | ||
2076 | mem_cgroup_unmark_under_oom(memcg); | 2077 | if (locked) { |
2078 | mem_cgroup_oom_unlock(memcg); | ||
2079 | /* | ||
2080 | * There is no guarantee that an OOM-lock contender | ||
2081 | * sees the wakeups triggered by the OOM kill | ||
2082 | * uncharges. Wake any sleepers explicitely. | ||
2083 | */ | ||
2084 | memcg_oom_recover(memcg); | ||
2085 | } | ||
2077 | 2086 | ||
2078 | if (test_thread_flag(TIF_MEMDIE) || fatal_signal_pending(current)) | 2087 | if (test_thread_flag(TIF_MEMDIE) || fatal_signal_pending(current)) |
2079 | return false; | 2088 | return false; |