162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) Gao Xiang <xiang@kernel.org> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * For low-latency decompression algorithms (e.g. lz4), reserve consecutive 662306a36Sopenharmony_ci * per-CPU virtual memory (in pages) in advance to store such inplace I/O 762306a36Sopenharmony_ci * data if inplace decompression is failed (due to unmet inplace margin for 862306a36Sopenharmony_ci * example). 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include "internal.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistruct erofs_pcpubuf { 1362306a36Sopenharmony_ci raw_spinlock_t lock; 1462306a36Sopenharmony_ci void *ptr; 1562306a36Sopenharmony_ci struct page **pages; 1662306a36Sopenharmony_ci unsigned int nrpages; 1762306a36Sopenharmony_ci}; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct erofs_pcpubuf, erofs_pcb); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_civoid *erofs_get_pcpubuf(unsigned int requiredpages) 2262306a36Sopenharmony_ci __acquires(pcb->lock) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct erofs_pcpubuf *pcb = &get_cpu_var(erofs_pcb); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci raw_spin_lock(&pcb->lock); 2762306a36Sopenharmony_ci /* check if the per-CPU buffer is too small */ 2862306a36Sopenharmony_ci if (requiredpages > pcb->nrpages) { 2962306a36Sopenharmony_ci raw_spin_unlock(&pcb->lock); 3062306a36Sopenharmony_ci put_cpu_var(erofs_pcb); 3162306a36Sopenharmony_ci /* (for sparse checker) pretend pcb->lock is still taken */ 3262306a36Sopenharmony_ci __acquire(pcb->lock); 3362306a36Sopenharmony_ci return NULL; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci return pcb->ptr; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_civoid erofs_put_pcpubuf(void *ptr) __releases(pcb->lock) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, smp_processor_id()); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci DBG_BUGON(pcb->ptr != ptr); 4362306a36Sopenharmony_ci raw_spin_unlock(&pcb->lock); 4462306a36Sopenharmony_ci put_cpu_var(erofs_pcb); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* the next step: support per-CPU page buffers hotplug */ 4862306a36Sopenharmony_ciint erofs_pcpubuf_growsize(unsigned int nrpages) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci static DEFINE_MUTEX(pcb_resize_mutex); 5162306a36Sopenharmony_ci static unsigned int pcb_nrpages; 5262306a36Sopenharmony_ci struct page *pagepool = NULL; 5362306a36Sopenharmony_ci int delta, cpu, ret, i; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci mutex_lock(&pcb_resize_mutex); 5662306a36Sopenharmony_ci delta = nrpages - pcb_nrpages; 5762306a36Sopenharmony_ci ret = 0; 5862306a36Sopenharmony_ci /* avoid shrinking pcpubuf, since no idea how many fses rely on */ 5962306a36Sopenharmony_ci if (delta <= 0) 6062306a36Sopenharmony_ci goto out; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 6362306a36Sopenharmony_ci struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, cpu); 6462306a36Sopenharmony_ci struct page **pages, **oldpages; 6562306a36Sopenharmony_ci void *ptr, *old_ptr; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci pages = kmalloc_array(nrpages, sizeof(*pages), GFP_KERNEL); 6862306a36Sopenharmony_ci if (!pages) { 6962306a36Sopenharmony_ci ret = -ENOMEM; 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci for (i = 0; i < nrpages; ++i) { 7462306a36Sopenharmony_ci pages[i] = erofs_allocpage(&pagepool, GFP_KERNEL); 7562306a36Sopenharmony_ci if (!pages[i]) { 7662306a36Sopenharmony_ci ret = -ENOMEM; 7762306a36Sopenharmony_ci oldpages = pages; 7862306a36Sopenharmony_ci goto free_pagearray; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci ptr = vmap(pages, nrpages, VM_MAP, PAGE_KERNEL); 8262306a36Sopenharmony_ci if (!ptr) { 8362306a36Sopenharmony_ci ret = -ENOMEM; 8462306a36Sopenharmony_ci oldpages = pages; 8562306a36Sopenharmony_ci goto free_pagearray; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci raw_spin_lock(&pcb->lock); 8862306a36Sopenharmony_ci old_ptr = pcb->ptr; 8962306a36Sopenharmony_ci pcb->ptr = ptr; 9062306a36Sopenharmony_ci oldpages = pcb->pages; 9162306a36Sopenharmony_ci pcb->pages = pages; 9262306a36Sopenharmony_ci i = pcb->nrpages; 9362306a36Sopenharmony_ci pcb->nrpages = nrpages; 9462306a36Sopenharmony_ci raw_spin_unlock(&pcb->lock); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (!oldpages) { 9762306a36Sopenharmony_ci DBG_BUGON(old_ptr); 9862306a36Sopenharmony_ci continue; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (old_ptr) 10262306a36Sopenharmony_ci vunmap(old_ptr); 10362306a36Sopenharmony_cifree_pagearray: 10462306a36Sopenharmony_ci while (i) 10562306a36Sopenharmony_ci erofs_pagepool_add(&pagepool, oldpages[--i]); 10662306a36Sopenharmony_ci kfree(oldpages); 10762306a36Sopenharmony_ci if (ret) 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci pcb_nrpages = nrpages; 11162306a36Sopenharmony_ci erofs_release_pages(&pagepool); 11262306a36Sopenharmony_ciout: 11362306a36Sopenharmony_ci mutex_unlock(&pcb_resize_mutex); 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_civoid __init erofs_pcpubuf_init(void) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int cpu; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 12262306a36Sopenharmony_ci struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, cpu); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci raw_spin_lock_init(&pcb->lock); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_civoid erofs_pcpubuf_exit(void) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci int cpu, i; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 13362306a36Sopenharmony_ci struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, cpu); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (pcb->ptr) { 13662306a36Sopenharmony_ci vunmap(pcb->ptr); 13762306a36Sopenharmony_ci pcb->ptr = NULL; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci if (!pcb->pages) 14062306a36Sopenharmony_ci continue; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci for (i = 0; i < pcb->nrpages; ++i) 14362306a36Sopenharmony_ci if (pcb->pages[i]) 14462306a36Sopenharmony_ci put_page(pcb->pages[i]); 14562306a36Sopenharmony_ci kfree(pcb->pages); 14662306a36Sopenharmony_ci pcb->pages = NULL; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci} 149