xref: /openbmc/linux/fs/erofs/pcpubuf.c (revision a279aded)
152488734SGao Xiang // SPDX-License-Identifier: GPL-2.0-only
252488734SGao Xiang /*
352488734SGao Xiang  * Copyright (C) Gao Xiang <xiang@kernel.org>
452488734SGao Xiang  *
552488734SGao Xiang  * For low-latency decompression algorithms (e.g. lz4), reserve consecutive
652488734SGao Xiang  * per-CPU virtual memory (in pages) in advance to store such inplace I/O
752488734SGao Xiang  * data if inplace decompression is failed (due to unmet inplace margin for
852488734SGao Xiang  * example).
952488734SGao Xiang  */
1052488734SGao Xiang #include "internal.h"
1152488734SGao Xiang 
1252488734SGao Xiang struct erofs_pcpubuf {
1352488734SGao Xiang 	raw_spinlock_t lock;
1452488734SGao Xiang 	void *ptr;
1552488734SGao Xiang 	struct page **pages;
1652488734SGao Xiang 	unsigned int nrpages;
1752488734SGao Xiang };
1852488734SGao Xiang 
1952488734SGao Xiang static DEFINE_PER_CPU(struct erofs_pcpubuf, erofs_pcb);
2052488734SGao Xiang 
erofs_get_pcpubuf(unsigned int requiredpages)2152488734SGao Xiang void *erofs_get_pcpubuf(unsigned int requiredpages)
2252488734SGao Xiang 	__acquires(pcb->lock)
2352488734SGao Xiang {
2452488734SGao Xiang 	struct erofs_pcpubuf *pcb = &get_cpu_var(erofs_pcb);
2552488734SGao Xiang 
2652488734SGao Xiang 	raw_spin_lock(&pcb->lock);
2752488734SGao Xiang 	/* check if the per-CPU buffer is too small */
2852488734SGao Xiang 	if (requiredpages > pcb->nrpages) {
2952488734SGao Xiang 		raw_spin_unlock(&pcb->lock);
3052488734SGao Xiang 		put_cpu_var(erofs_pcb);
3152488734SGao Xiang 		/* (for sparse checker) pretend pcb->lock is still taken */
3252488734SGao Xiang 		__acquire(pcb->lock);
3352488734SGao Xiang 		return NULL;
3452488734SGao Xiang 	}
3552488734SGao Xiang 	return pcb->ptr;
3652488734SGao Xiang }
3752488734SGao Xiang 
erofs_put_pcpubuf(void * ptr)3852488734SGao Xiang void erofs_put_pcpubuf(void *ptr) __releases(pcb->lock)
3952488734SGao Xiang {
4052488734SGao Xiang 	struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, smp_processor_id());
4152488734SGao Xiang 
4252488734SGao Xiang 	DBG_BUGON(pcb->ptr != ptr);
4352488734SGao Xiang 	raw_spin_unlock(&pcb->lock);
4452488734SGao Xiang 	put_cpu_var(erofs_pcb);
4552488734SGao Xiang }
4652488734SGao Xiang 
4752488734SGao Xiang /* the next step: support per-CPU page buffers hotplug */
erofs_pcpubuf_growsize(unsigned int nrpages)4852488734SGao Xiang int erofs_pcpubuf_growsize(unsigned int nrpages)
4952488734SGao Xiang {
5052488734SGao Xiang 	static DEFINE_MUTEX(pcb_resize_mutex);
5152488734SGao Xiang 	static unsigned int pcb_nrpages;
52eaa9172aSGao Xiang 	struct page *pagepool = NULL;
5352488734SGao Xiang 	int delta, cpu, ret, i;
5452488734SGao Xiang 
5552488734SGao Xiang 	mutex_lock(&pcb_resize_mutex);
5652488734SGao Xiang 	delta = nrpages - pcb_nrpages;
5752488734SGao Xiang 	ret = 0;
5852488734SGao Xiang 	/* avoid shrinking pcpubuf, since no idea how many fses rely on */
5952488734SGao Xiang 	if (delta <= 0)
6052488734SGao Xiang 		goto out;
6152488734SGao Xiang 
6252488734SGao Xiang 	for_each_possible_cpu(cpu) {
6352488734SGao Xiang 		struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, cpu);
6452488734SGao Xiang 		struct page **pages, **oldpages;
6552488734SGao Xiang 		void *ptr, *old_ptr;
6652488734SGao Xiang 
6752488734SGao Xiang 		pages = kmalloc_array(nrpages, sizeof(*pages), GFP_KERNEL);
6852488734SGao Xiang 		if (!pages) {
6952488734SGao Xiang 			ret = -ENOMEM;
7052488734SGao Xiang 			break;
7152488734SGao Xiang 		}
7252488734SGao Xiang 
7352488734SGao Xiang 		for (i = 0; i < nrpages; ++i) {
7452488734SGao Xiang 			pages[i] = erofs_allocpage(&pagepool, GFP_KERNEL);
7552488734SGao Xiang 			if (!pages[i]) {
7652488734SGao Xiang 				ret = -ENOMEM;
7752488734SGao Xiang 				oldpages = pages;
7852488734SGao Xiang 				goto free_pagearray;
7952488734SGao Xiang 			}
8052488734SGao Xiang 		}
8152488734SGao Xiang 		ptr = vmap(pages, nrpages, VM_MAP, PAGE_KERNEL);
8252488734SGao Xiang 		if (!ptr) {
8352488734SGao Xiang 			ret = -ENOMEM;
8452488734SGao Xiang 			oldpages = pages;
8552488734SGao Xiang 			goto free_pagearray;
8652488734SGao Xiang 		}
8752488734SGao Xiang 		raw_spin_lock(&pcb->lock);
8852488734SGao Xiang 		old_ptr = pcb->ptr;
8952488734SGao Xiang 		pcb->ptr = ptr;
9052488734SGao Xiang 		oldpages = pcb->pages;
9152488734SGao Xiang 		pcb->pages = pages;
9252488734SGao Xiang 		i = pcb->nrpages;
9352488734SGao Xiang 		pcb->nrpages = nrpages;
9452488734SGao Xiang 		raw_spin_unlock(&pcb->lock);
9552488734SGao Xiang 
9652488734SGao Xiang 		if (!oldpages) {
9752488734SGao Xiang 			DBG_BUGON(old_ptr);
9852488734SGao Xiang 			continue;
9952488734SGao Xiang 		}
10052488734SGao Xiang 
10152488734SGao Xiang 		if (old_ptr)
10252488734SGao Xiang 			vunmap(old_ptr);
10352488734SGao Xiang free_pagearray:
10452488734SGao Xiang 		while (i)
105eaa9172aSGao Xiang 			erofs_pagepool_add(&pagepool, oldpages[--i]);
10652488734SGao Xiang 		kfree(oldpages);
10752488734SGao Xiang 		if (ret)
10852488734SGao Xiang 			break;
10952488734SGao Xiang 	}
11052488734SGao Xiang 	pcb_nrpages = nrpages;
111eaa9172aSGao Xiang 	erofs_release_pages(&pagepool);
11252488734SGao Xiang out:
11352488734SGao Xiang 	mutex_unlock(&pcb_resize_mutex);
11452488734SGao Xiang 	return ret;
11552488734SGao Xiang }
11652488734SGao Xiang 
erofs_pcpubuf_init(void)117*a279adedSYangtao Li void __init erofs_pcpubuf_init(void)
11852488734SGao Xiang {
11952488734SGao Xiang 	int cpu;
12052488734SGao Xiang 
12152488734SGao Xiang 	for_each_possible_cpu(cpu) {
12252488734SGao Xiang 		struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, cpu);
12352488734SGao Xiang 
12452488734SGao Xiang 		raw_spin_lock_init(&pcb->lock);
12552488734SGao Xiang 	}
12652488734SGao Xiang }
12752488734SGao Xiang 
erofs_pcpubuf_exit(void)12852488734SGao Xiang void erofs_pcpubuf_exit(void)
12952488734SGao Xiang {
13052488734SGao Xiang 	int cpu, i;
13152488734SGao Xiang 
13252488734SGao Xiang 	for_each_possible_cpu(cpu) {
13352488734SGao Xiang 		struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, cpu);
13452488734SGao Xiang 
13552488734SGao Xiang 		if (pcb->ptr) {
13652488734SGao Xiang 			vunmap(pcb->ptr);
13752488734SGao Xiang 			pcb->ptr = NULL;
13852488734SGao Xiang 		}
13952488734SGao Xiang 		if (!pcb->pages)
14052488734SGao Xiang 			continue;
14152488734SGao Xiang 
14252488734SGao Xiang 		for (i = 0; i < pcb->nrpages; ++i)
14352488734SGao Xiang 			if (pcb->pages[i])
14452488734SGao Xiang 				put_page(pcb->pages[i]);
14552488734SGao Xiang 		kfree(pcb->pages);
14652488734SGao Xiang 		pcb->pages = NULL;
14752488734SGao Xiang 	}
14852488734SGao Xiang }
149