Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4af2c261 authored by Prakash Gupta's avatar Prakash Gupta
Browse files

lowmemorykiller: fix scan_mutex contention



A livelock can be created in the system by lowmemorykiller trying to kill
the same task again and again.  Below is the livelock condition.

P0 -> binder_main_lock(W)
P1 -> binder_main_lock(T)-> mmap_sem(W)
P2 -> mmap_sem(T) -> lowmem_scan::scan_mutex(W)
P3 -> lowmem_scan::scan_mutex(T) -> send SIGKILL P0

Here multiple tasks are contending on scan_mutex, and binder_main_lock.
Change lowmem_scan mutex_lock with mutex_trylock, so in case of lowmem_scan
lock contention, task will be allowed to reclaim from other shrinkers. This
will also maintain the serialization of lowmemorykiller trigger.

If a task is pending MEMDIE'ing, remove sleep before falling back on other
shrinkers.

If the task selected to be killed is MEMDIE'ing and in un-interruptible
sleep state, do not repeat kill but fallback on other shrinkers without any
delay.

Change-Id: I12131622f7fa7b422c6d5d09f782af848300e412
Signed-off-by: default avatarPrakash Gupta <guptap@codeaurora.org>
parent 78f2fe78
Loading
Loading
Loading
Loading
+28 −3
Original line number Diff line number Diff line
@@ -211,6 +211,22 @@ static int test_task_flag(struct task_struct *p, int flag)
	return 0;
}

static int test_task_state(struct task_struct *p, int state)
{
	struct task_struct *t;

	for_each_thread(p, t) {
		task_lock(t);
		if (t->state & state) {
			task_unlock(t);
			return 1;
		}
		task_unlock(t);
	}

	return 0;
}

static DEFINE_MUTEX(scan_mutex);

int can_use_cma_pages(gfp_t gfp_mask)
@@ -398,7 +414,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
	int other_free;
	int other_file;

	if (mutex_lock_interruptible(&scan_mutex) < 0)
	if (!mutex_trylock(&scan_mutex))
		return 0;

	other_free = global_page_state(NR_FREE_PAGES);
@@ -457,8 +473,6 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
		if (time_before_eq(jiffies, lowmem_deathpending_timeout)) {
			if (test_task_flag(tsk, TIF_MEMDIE)) {
				rcu_read_unlock();
				/* give the system time to free up the memory */
				msleep_interruptible(20);
				mutex_unlock(&scan_mutex);
				return 0;
			}
@@ -495,6 +509,17 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
		long cache_limit = minfree * (long)(PAGE_SIZE / 1024);
		long free = other_free * (long)(PAGE_SIZE / 1024);
		trace_lowmemory_kill(selected, cache_size, cache_limit, free);

		if (test_task_flag(selected, TIF_MEMDIE) &&
		    (test_task_state(selected, TASK_UNINTERRUPTIBLE))) {
			lowmem_print(2, "'%s' (%d) is already killed\n",
				     selected->comm,
				     selected->pid);
			rcu_read_unlock();
			mutex_unlock(&scan_mutex);
			return 0;
		}

		lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \
				"   to free %ldkB on behalf of '%s' (%d) because\n" \
				"   cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \