2021-04-09 22:06:30 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( C ) Gao Xiang < xiang @ kernel . org >
*
* For low - latency decompression algorithms ( e . g . lz4 ) , reserve consecutive
* per - CPU virtual memory ( in pages ) in advance to store such inplace I / O
* data if inplace decompression is failed ( due to unmet inplace margin for
* example ) .
*/
# include "internal.h"
struct erofs_pcpubuf {
raw_spinlock_t lock ;
void * ptr ;
struct page * * pages ;
unsigned int nrpages ;
} ;
static DEFINE_PER_CPU ( struct erofs_pcpubuf , erofs_pcb ) ;
void * erofs_get_pcpubuf ( unsigned int requiredpages )
__acquires ( pcb - > lock )
{
struct erofs_pcpubuf * pcb = & get_cpu_var ( erofs_pcb ) ;
raw_spin_lock ( & pcb - > lock ) ;
/* check if the per-CPU buffer is too small */
if ( requiredpages > pcb - > nrpages ) {
raw_spin_unlock ( & pcb - > lock ) ;
put_cpu_var ( erofs_pcb ) ;
/* (for sparse checker) pretend pcb->lock is still taken */
__acquire ( pcb - > lock ) ;
return NULL ;
}
return pcb - > ptr ;
}
void erofs_put_pcpubuf ( void * ptr ) __releases ( pcb - > lock )
{
struct erofs_pcpubuf * pcb = & per_cpu ( erofs_pcb , smp_processor_id ( ) ) ;
DBG_BUGON ( pcb - > ptr ! = ptr ) ;
raw_spin_unlock ( & pcb - > lock ) ;
put_cpu_var ( erofs_pcb ) ;
}
/* the next step: support per-CPU page buffers hotplug */
int erofs_pcpubuf_growsize ( unsigned int nrpages )
{
static DEFINE_MUTEX ( pcb_resize_mutex ) ;
static unsigned int pcb_nrpages ;
2021-10-22 12:01:20 +03:00
struct page * pagepool = NULL ;
2021-04-09 22:06:30 +03:00
int delta , cpu , ret , i ;
mutex_lock ( & pcb_resize_mutex ) ;
delta = nrpages - pcb_nrpages ;
ret = 0 ;
/* avoid shrinking pcpubuf, since no idea how many fses rely on */
if ( delta < = 0 )
goto out ;
for_each_possible_cpu ( cpu ) {
struct erofs_pcpubuf * pcb = & per_cpu ( erofs_pcb , cpu ) ;
struct page * * pages , * * oldpages ;
void * ptr , * old_ptr ;
pages = kmalloc_array ( nrpages , sizeof ( * pages ) , GFP_KERNEL ) ;
if ( ! pages ) {
ret = - ENOMEM ;
break ;
}
for ( i = 0 ; i < nrpages ; + + i ) {
pages [ i ] = erofs_allocpage ( & pagepool , GFP_KERNEL ) ;
if ( ! pages [ i ] ) {
ret = - ENOMEM ;
oldpages = pages ;
goto free_pagearray ;
}
}
ptr = vmap ( pages , nrpages , VM_MAP , PAGE_KERNEL ) ;
if ( ! ptr ) {
ret = - ENOMEM ;
oldpages = pages ;
goto free_pagearray ;
}
raw_spin_lock ( & pcb - > lock ) ;
old_ptr = pcb - > ptr ;
pcb - > ptr = ptr ;
oldpages = pcb - > pages ;
pcb - > pages = pages ;
i = pcb - > nrpages ;
pcb - > nrpages = nrpages ;
raw_spin_unlock ( & pcb - > lock ) ;
if ( ! oldpages ) {
DBG_BUGON ( old_ptr ) ;
continue ;
}
if ( old_ptr )
vunmap ( old_ptr ) ;
free_pagearray :
while ( i )
2021-10-22 12:01:20 +03:00
erofs_pagepool_add ( & pagepool , oldpages [ - - i ] ) ;
2021-04-09 22:06:30 +03:00
kfree ( oldpages ) ;
if ( ret )
break ;
}
pcb_nrpages = nrpages ;
2021-10-22 12:01:20 +03:00
erofs_release_pages ( & pagepool ) ;
2021-04-09 22:06:30 +03:00
out :
mutex_unlock ( & pcb_resize_mutex ) ;
return ret ;
}
2023-03-03 09:37:31 +03:00
void __init erofs_pcpubuf_init ( void )
2021-04-09 22:06:30 +03:00
{
int cpu ;
for_each_possible_cpu ( cpu ) {
struct erofs_pcpubuf * pcb = & per_cpu ( erofs_pcb , cpu ) ;
raw_spin_lock_init ( & pcb - > lock ) ;
}
}
void erofs_pcpubuf_exit ( void )
{
int cpu , i ;
for_each_possible_cpu ( cpu ) {
struct erofs_pcpubuf * pcb = & per_cpu ( erofs_pcb , cpu ) ;
if ( pcb - > ptr ) {
vunmap ( pcb - > ptr ) ;
pcb - > ptr = NULL ;
}
if ( ! pcb - > pages )
continue ;
for ( i = 0 ; i < pcb - > nrpages ; + + i )
if ( pcb - > pages [ i ] )
put_page ( pcb - > pages [ i ] ) ;
kfree ( pcb - > pages ) ;
pcb - > pages = NULL ;
}
}