1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) Gao Xiang <xiang@kernel.org> 4 * 5 * For low-latency decompression algorithms (e.g. lz4), reserve consecutive 6 * per-CPU virtual memory (in pages) in advance to store such inplace I/O 7 * data if inplace decompression is failed (due to unmet inplace margin for 8 * example). 9 */ 10 #include "internal.h" 11 12 struct erofs_pcpubuf { 13 raw_spinlock_t lock; 14 void *ptr; 15 struct page **pages; 16 unsigned int nrpages; 17 }; 18 19 static DEFINE_PER_CPU(struct erofs_pcpubuf, erofs_pcb); 20 21 void *erofs_get_pcpubuf(unsigned int requiredpages) 22 __acquires(pcb->lock) 23 { 24 struct erofs_pcpubuf *pcb = &get_cpu_var(erofs_pcb); 25 26 raw_spin_lock(&pcb->lock); 27 /* check if the per-CPU buffer is too small */ 28 if (requiredpages > pcb->nrpages) { 29 raw_spin_unlock(&pcb->lock); 30 put_cpu_var(erofs_pcb); 31 /* (for sparse checker) pretend pcb->lock is still taken */ 32 __acquire(pcb->lock); 33 return NULL; 34 } 35 return pcb->ptr; 36 } 37 38 void erofs_put_pcpubuf(void *ptr) __releases(pcb->lock) 39 { 40 struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, smp_processor_id()); 41 42 DBG_BUGON(pcb->ptr != ptr); 43 raw_spin_unlock(&pcb->lock); 44 put_cpu_var(erofs_pcb); 45 } 46 47 /* the next step: support per-CPU page buffers hotplug */ 48 int erofs_pcpubuf_growsize(unsigned int nrpages) 49 { 50 static DEFINE_MUTEX(pcb_resize_mutex); 51 static unsigned int pcb_nrpages; 52 struct page *pagepool = NULL; 53 int delta, cpu, ret, i; 54 55 mutex_lock(&pcb_resize_mutex); 56 delta = nrpages - pcb_nrpages; 57 ret = 0; 58 /* avoid shrinking pcpubuf, since no idea how many fses rely on */ 59 if (delta <= 0) 60 goto out; 61 62 for_each_possible_cpu(cpu) { 63 struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, cpu); 64 struct page **pages, **oldpages; 65 void *ptr, *old_ptr; 66 67 pages = kmalloc_array(nrpages, sizeof(*pages), GFP_KERNEL); 68 if (!pages) { 69 ret = -ENOMEM; 70 break; 71 } 72 73 for (i = 0; i < nrpages; ++i) { 74 pages[i] = erofs_allocpage(&pagepool, GFP_KERNEL); 75 if (!pages[i]) { 76 ret = -ENOMEM; 77 oldpages = pages; 78 goto free_pagearray; 79 } 80 } 81 ptr = vmap(pages, nrpages, VM_MAP, PAGE_KERNEL); 82 if (!ptr) { 83 ret = -ENOMEM; 84 oldpages = pages; 85 goto free_pagearray; 86 } 87 raw_spin_lock(&pcb->lock); 88 old_ptr = pcb->ptr; 89 pcb->ptr = ptr; 90 oldpages = pcb->pages; 91 pcb->pages = pages; 92 i = pcb->nrpages; 93 pcb->nrpages = nrpages; 94 raw_spin_unlock(&pcb->lock); 95 96 if (!oldpages) { 97 DBG_BUGON(old_ptr); 98 continue; 99 } 100 101 if (old_ptr) 102 vunmap(old_ptr); 103 free_pagearray: 104 while (i) 105 erofs_pagepool_add(&pagepool, oldpages[--i]); 106 kfree(oldpages); 107 if (ret) 108 break; 109 } 110 pcb_nrpages = nrpages; 111 erofs_release_pages(&pagepool); 112 out: 113 mutex_unlock(&pcb_resize_mutex); 114 return ret; 115 } 116 117 void erofs_pcpubuf_init(void) 118 { 119 int cpu; 120 121 for_each_possible_cpu(cpu) { 122 struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, cpu); 123 124 raw_spin_lock_init(&pcb->lock); 125 } 126 } 127 128 void erofs_pcpubuf_exit(void) 129 { 130 int cpu, i; 131 132 for_each_possible_cpu(cpu) { 133 struct erofs_pcpubuf *pcb = &per_cpu(erofs_pcb, cpu); 134 135 if (pcb->ptr) { 136 vunmap(pcb->ptr); 137 pcb->ptr = NULL; 138 } 139 if (!pcb->pages) 140 continue; 141 142 for (i = 0; i < pcb->nrpages; ++i) 143 if (pcb->pages[i]) 144 put_page(pcb->pages[i]); 145 kfree(pcb->pages); 146 pcb->pages = NULL; 147 } 148 } 149