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

Commit 6a1fdaa3 authored by Vinayak Menon's avatar Vinayak Menon
Browse files

mm: zbud: prevent softirq during zbud alloc, free and reclaim



The following deadlock is observed.

Core 2 waiting on mapping->tree_lock which is taken by core 6

do_raw_spin_lock
raw_spin_lock_irq
atomic_cmpxchg
page_freeze_refs
__remove_mapping
shrink_page_list
shrink_inactive_list
shrink_list
shrink_lruvec
shrink_zone
shrink_zones
do_try_to_free_pages
try_to_free_pages(?, ?, ?, ?)
__perform_reclaim
__alloc_pages_direct_reclaim
__alloc_pages_slowpath
__alloc_pages_nodemask
alloc_kmem_pages_node
alloc_thread_info_node
dup_task_struct
copy_process.part.56
do_fork
sys_clone
el0_svc_naked

Core 6 after taking mapping->tree_lock is waiting on zbud pool lock
which is held by core 5

zbud_alloc
zcache_store_page
__cleancache_put_page
cleancache_put_page
__delete_from_page_cache
spin_unlock_irq
__remove_mapping
shrink_page_list
shrink_inactive_list
shrink_list
shrink_lruvec
shrink_zone
bitmap_zero
__nodes_clear
kswapd_shrink_zone.constprop.58
balance_pgdat
kswapd_try_to_sleep
kswapd
kthread
ret_from_fork

Core 5 after taking zbud pool lock from zbud_free received an IRQ, and
after IRQ exit, softirqs were scheduled and end_page_writeback tried to
lock on mapping->tree_lock which is already held by Core 6. Deadlock.

do_raw_spin_lock
raw_spin_lock_irqsave
test_clear_page_writeba
end_page_writeback
ext4_finish_bio
ext4_end_bio
bio_endio
blk_update_request
end_clone_bio
bio_endio
blk_update_request
blk_update_bidi_request
blk_end_bidi_request
blk_end_request
mmc_blk_cmdq_complete_r
mmc_cmdq_softirq_done
blk_done_softirq
static_key_count
static_key_false
trace_softirq_exit
__do_softirq()
tick_irq_exit
irq_exit()
set_irq_regs
__handle_domain_irq
gic_handle_irq
el1_irq
exception
__list_del_entry
list_del
zbud_free
zcache_load_page
__cleancache_get_page(?

So protect zbud_alloc/free/reclaim with spink_lock_bh

CRs-Fixed: 986783
Change-Id: Ib0605b38e7371c29316ed81e43549a0b9503d531
Signed-off-by: default avatarVinayak Menon <vinmenon@codeaurora.org>
parent 7cf3ab10
Loading
Loading
Loading
Loading
+13 −13
Original line number Diff line number Diff line
@@ -349,7 +349,7 @@ int zbud_alloc(struct zbud_pool *pool, size_t size, gfp_t gfp,
	if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE)
		return -ENOSPC;
	chunks = size_to_chunks(size);
	spin_lock(&pool->lock);
	spin_lock_bh(&pool->lock);

	/* First, try to find an unbuddied zbud page. */
	zhdr = NULL;
@@ -368,11 +368,11 @@ int zbud_alloc(struct zbud_pool *pool, size_t size, gfp_t gfp,
	}

	/* Couldn't find unbuddied zbud page, create new one */
	spin_unlock(&pool->lock);
	spin_unlock_bh(&pool->lock);
	page = alloc_page(gfp);
	if (!page)
		return -ENOMEM;
	spin_lock(&pool->lock);
	spin_lock_bh(&pool->lock);
	pool->pages_nr++;
	zhdr = init_zbud_page(page);
	bud = FIRST;
@@ -400,7 +400,7 @@ found:
	*handle = encode_handle(zhdr, bud);
	if ((gfp & __GFP_ZERO) && found)
		memset((void *)*handle, 0, size);
	spin_unlock(&pool->lock);
	spin_unlock_bh(&pool->lock);

	return 0;
}
@@ -420,7 +420,7 @@ void zbud_free(struct zbud_pool *pool, unsigned long handle)
	struct zbud_header *zhdr;
	int freechunks;

	spin_lock(&pool->lock);
	spin_lock_bh(&pool->lock);
	zhdr = handle_to_zbud_header(handle);

	/* If first buddy, handle will be page aligned */
@@ -431,7 +431,7 @@ void zbud_free(struct zbud_pool *pool, unsigned long handle)

	if (zhdr->under_reclaim) {
		/* zbud page is under reclaim, reclaim will free */
		spin_unlock(&pool->lock);
		spin_unlock_bh(&pool->lock);
		return;
	}

@@ -449,7 +449,7 @@ void zbud_free(struct zbud_pool *pool, unsigned long handle)
		list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
	}

	spin_unlock(&pool->lock);
	spin_unlock_bh(&pool->lock);
}

#define list_tail_entry(ptr, type, member) \
@@ -496,10 +496,10 @@ int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries)
	struct zbud_header *zhdr;
	unsigned long first_handle = 0, last_handle = 0;

	spin_lock(&pool->lock);
	spin_lock_bh(&pool->lock);
	if (!pool->ops || !pool->ops->evict || list_empty(&pool->lru) ||
			retries == 0) {
		spin_unlock(&pool->lock);
		spin_unlock_bh(&pool->lock);
		return -EINVAL;
	}
	for (i = 0; i < retries; i++) {
@@ -518,7 +518,7 @@ int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries)
			first_handle = encode_handle(zhdr, FIRST);
		if (zhdr->last_chunks)
			last_handle = encode_handle(zhdr, LAST);
		spin_unlock(&pool->lock);
		spin_unlock_bh(&pool->lock);

		/* Issue the eviction callback(s) */
		if (first_handle) {
@@ -532,7 +532,7 @@ int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries)
				goto next;
		}
next:
		spin_lock(&pool->lock);
		spin_lock_bh(&pool->lock);
		zhdr->under_reclaim = false;
		if (zhdr->first_chunks == 0 && zhdr->last_chunks == 0) {
			/*
@@ -541,7 +541,7 @@ next:
			 */
			free_zbud_page(zhdr);
			pool->pages_nr--;
			spin_unlock(&pool->lock);
			spin_unlock_bh(&pool->lock);
			return 0;
		} else if (zhdr->first_chunks == 0 ||
				zhdr->last_chunks == 0) {
@@ -556,7 +556,7 @@ next:
		/* add to beginning of LRU */
		list_add(&zhdr->lru, &pool->lru);
	}
	spin_unlock(&pool->lock);
	spin_unlock_bh(&pool->lock);
	return -EAGAIN;
}