|
@@ -596,7 +596,7 @@ static int oom_reaper(void *unused)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static void wake_oom_reaper(struct task_struct *tsk)
|
|
|
|
|
|
+void wake_oom_reaper(struct task_struct *tsk)
|
|
{
|
|
{
|
|
if (!oom_reaper_th)
|
|
if (!oom_reaper_th)
|
|
return;
|
|
return;
|
|
@@ -614,46 +614,6 @@ static void wake_oom_reaper(struct task_struct *tsk)
|
|
wake_up(&oom_reaper_wait);
|
|
wake_up(&oom_reaper_wait);
|
|
}
|
|
}
|
|
|
|
|
|
-/* Check if we can reap the given task. This has to be called with stable
|
|
|
|
- * tsk->mm
|
|
|
|
- */
|
|
|
|
-void try_oom_reaper(struct task_struct *tsk)
|
|
|
|
-{
|
|
|
|
- struct mm_struct *mm = tsk->mm;
|
|
|
|
- struct task_struct *p;
|
|
|
|
-
|
|
|
|
- if (!mm)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * There might be other threads/processes which are either not
|
|
|
|
- * dying or even not killable.
|
|
|
|
- */
|
|
|
|
- if (atomic_read(&mm->mm_users) > 1) {
|
|
|
|
- rcu_read_lock();
|
|
|
|
- for_each_process(p) {
|
|
|
|
- if (!process_shares_mm(p, mm))
|
|
|
|
- continue;
|
|
|
|
- if (fatal_signal_pending(p))
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * If the task is exiting make sure the whole thread group
|
|
|
|
- * is exiting and cannot acces mm anymore.
|
|
|
|
- */
|
|
|
|
- if (signal_group_exit(p->signal))
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- /* Give up */
|
|
|
|
- rcu_read_unlock();
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- rcu_read_unlock();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- wake_oom_reaper(tsk);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static int __init oom_init(void)
|
|
static int __init oom_init(void)
|
|
{
|
|
{
|
|
oom_reaper_th = kthread_run(oom_reaper, NULL, "oom_reaper");
|
|
oom_reaper_th = kthread_run(oom_reaper, NULL, "oom_reaper");
|
|
@@ -665,10 +625,6 @@ static int __init oom_init(void)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
subsys_initcall(oom_init)
|
|
subsys_initcall(oom_init)
|
|
-#else
|
|
|
|
-static void wake_oom_reaper(struct task_struct *tsk)
|
|
|
|
-{
|
|
|
|
-}
|
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -745,6 +701,81 @@ void oom_killer_enable(void)
|
|
oom_killer_disabled = false;
|
|
oom_killer_disabled = false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static inline bool __task_will_free_mem(struct task_struct *task)
|
|
|
|
+{
|
|
|
|
+ struct signal_struct *sig = task->signal;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * A coredumping process may sleep for an extended period in exit_mm(),
|
|
|
|
+ * so the oom killer cannot assume that the process will promptly exit
|
|
|
|
+ * and release memory.
|
|
|
|
+ */
|
|
|
|
+ if (sig->flags & SIGNAL_GROUP_COREDUMP)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (sig->flags & SIGNAL_GROUP_EXIT)
|
|
|
|
+ return true;
|
|
|
|
+
|
|
|
|
+ if (thread_group_empty(task) && (task->flags & PF_EXITING))
|
|
|
|
+ return true;
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Checks whether the given task is dying or exiting and likely to
|
|
|
|
+ * release its address space. This means that all threads and processes
|
|
|
|
+ * sharing the same mm have to be killed or exiting.
|
|
|
|
+ */
|
|
|
|
+bool task_will_free_mem(struct task_struct *task)
|
|
|
|
+{
|
|
|
|
+ struct mm_struct *mm;
|
|
|
|
+ struct task_struct *p;
|
|
|
|
+ bool ret;
|
|
|
|
+
|
|
|
|
+ if (!__task_will_free_mem(task))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If the process has passed exit_mm we have to skip it because
|
|
|
|
+ * we have lost a link to other tasks sharing this mm, we do not
|
|
|
|
+ * have anything to reap and the task might then get stuck waiting
|
|
|
|
+ * for parent as zombie and we do not want it to hold TIF_MEMDIE
|
|
|
|
+ */
|
|
|
|
+ p = find_lock_task_mm(task);
|
|
|
|
+ if (!p)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ mm = p->mm;
|
|
|
|
+ if (atomic_read(&mm->mm_users) <= 1) {
|
|
|
|
+ task_unlock(p);
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* pin the mm to not get freed and reused */
|
|
|
|
+ atomic_inc(&mm->mm_count);
|
|
|
|
+ task_unlock(p);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * This is really pessimistic but we do not have any reliable way
|
|
|
|
+ * to check that external processes share with our mm
|
|
|
|
+ */
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ for_each_process(p) {
|
|
|
|
+ if (!process_shares_mm(p, mm))
|
|
|
|
+ continue;
|
|
|
|
+ if (same_thread_group(task, p))
|
|
|
|
+ continue;
|
|
|
|
+ ret = __task_will_free_mem(p);
|
|
|
|
+ if (!ret)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+ mmdrop(mm);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Must be called while holding a reference to p, which will be released upon
|
|
* Must be called while holding a reference to p, which will be released upon
|
|
* returning.
|
|
* returning.
|
|
@@ -766,15 +797,12 @@ void oom_kill_process(struct oom_control *oc, struct task_struct *p,
|
|
* If the task is already exiting, don't alarm the sysadmin or kill
|
|
* If the task is already exiting, don't alarm the sysadmin or kill
|
|
* its children or threads, just set TIF_MEMDIE so it can die quickly
|
|
* its children or threads, just set TIF_MEMDIE so it can die quickly
|
|
*/
|
|
*/
|
|
- task_lock(p);
|
|
|
|
- if (p->mm && task_will_free_mem(p)) {
|
|
|
|
|
|
+ if (task_will_free_mem(p)) {
|
|
mark_oom_victim(p);
|
|
mark_oom_victim(p);
|
|
- try_oom_reaper(p);
|
|
|
|
- task_unlock(p);
|
|
|
|
|
|
+ wake_oom_reaper(p);
|
|
put_task_struct(p);
|
|
put_task_struct(p);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- task_unlock(p);
|
|
|
|
|
|
|
|
if (__ratelimit(&oom_rs))
|
|
if (__ratelimit(&oom_rs))
|
|
dump_header(oc, p);
|
|
dump_header(oc, p);
|
|
@@ -944,10 +972,9 @@ bool out_of_memory(struct oom_control *oc)
|
|
* But don't select if current has already released its mm and cleared
|
|
* But don't select if current has already released its mm and cleared
|
|
* TIF_MEMDIE flag at exit_mm(), otherwise an OOM livelock may occur.
|
|
* TIF_MEMDIE flag at exit_mm(), otherwise an OOM livelock may occur.
|
|
*/
|
|
*/
|
|
- if (current->mm &&
|
|
|
|
- (fatal_signal_pending(current) || task_will_free_mem(current))) {
|
|
|
|
|
|
+ if (current->mm && task_will_free_mem(current)) {
|
|
mark_oom_victim(current);
|
|
mark_oom_victim(current);
|
|
- try_oom_reaper(current);
|
|
|
|
|
|
+ wake_oom_reaper(current);
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|