2019-06-01 11:08:42 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-04-09 13:57:01 +04:00
/*
* mm / percpu - km . c - kernel memory based chunk allocation
*
* Copyright ( C ) 2010 SUSE Linux Products GmbH
* Copyright ( C ) 2010 Tejun Heo < tj @ kernel . org >
*
* Chunks are allocated as a contiguous kernel memory using gfp
* allocation . This is to be used on nommu architectures .
*
* To use percpu - km ,
*
* - define CONFIG_NEED_PER_CPU_KM from the arch Kconfig .
*
* - CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK must not be defined . It ' s
* not compatible with PER_CPU_KM . EMBED_FIRST_CHUNK should work
* fine .
*
* - NUMA is not supported . When setting up the first chunk ,
* @ cpu_distance_fn should be NULL or report all CPUs to be nearer
* than or at LOCAL_DISTANCE .
*
* - It ' s best if the chunk size is power of two multiple of
* PAGE_SIZE . Because each chunk is allocated as a contiguous
* kernel memory block using alloc_pages ( ) , memory will be wasted if
* chunk size is not aligned . percpu - km code will whine about it .
*/
2010-09-03 20:22:48 +04:00
# if defined(CONFIG_SMP) && defined(CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK)
2010-04-09 13:57:01 +04:00
# error "contiguous percpu allocation is incompatible with paged first chunk"
# endif
# include <linux/log2.h>
2014-09-02 22:46:02 +04:00
static int pcpu_populate_chunk ( struct pcpu_chunk * chunk ,
2018-02-16 21:07:19 +03:00
int page_start , int page_end , gfp_t gfp )
2010-04-09 13:57:01 +04:00
{
return 0 ;
}
2014-09-02 22:46:02 +04:00
static void pcpu_depopulate_chunk ( struct pcpu_chunk * chunk ,
int page_start , int page_end )
2010-04-09 13:57:01 +04:00
{
/* nada */
}
2018-02-16 21:07:19 +03:00
static struct pcpu_chunk * pcpu_create_chunk ( gfp_t gfp )
2010-04-09 13:57:01 +04:00
{
const int nr_pages = pcpu_group_sizes [ 0 ] > > PAGE_SHIFT ;
struct pcpu_chunk * chunk ;
struct page * pages ;
2018-12-18 19:42:27 +03:00
unsigned long flags ;
2010-04-09 13:57:01 +04:00
int i ;
2018-02-16 21:07:19 +03:00
chunk = pcpu_alloc_chunk ( gfp ) ;
2010-04-09 13:57:01 +04:00
if ( ! chunk )
return NULL ;
2018-02-16 21:09:58 +03:00
pages = alloc_pages ( gfp , order_base_2 ( nr_pages ) ) ;
2010-04-09 13:57:01 +04:00
if ( ! pages ) {
pcpu_free_chunk ( chunk ) ;
return NULL ;
}
for ( i = 0 ; i < nr_pages ; i + + )
pcpu_set_page_chunk ( nth_page ( pages , i ) , chunk ) ;
chunk - > data = pages ;
2019-02-24 16:13:50 +03:00
chunk - > base_addr = page_address ( pages ) ;
2014-09-02 22:46:02 +04:00
2018-12-18 19:42:27 +03:00
spin_lock_irqsave ( & pcpu_lock , flags ) ;
2019-02-13 22:10:30 +03:00
pcpu_chunk_populated ( chunk , 0 , nr_pages ) ;
2018-12-18 19:42:27 +03:00
spin_unlock_irqrestore ( & pcpu_lock , flags ) ;
2014-09-02 22:46:02 +04:00
2017-06-20 02:28:31 +03:00
pcpu_stats_chunk_alloc ( ) ;
2017-06-20 02:28:32 +03:00
trace_percpu_create_chunk ( chunk - > base_addr ) ;
2017-06-20 02:28:31 +03:00
2010-04-09 13:57:01 +04:00
return chunk ;
}
static void pcpu_destroy_chunk ( struct pcpu_chunk * chunk )
{
const int nr_pages = pcpu_group_sizes [ 0 ] > > PAGE_SHIFT ;
2017-06-29 17:56:26 +03:00
if ( ! chunk )
return ;
2017-06-20 02:28:31 +03:00
pcpu_stats_chunk_dealloc ( ) ;
2017-06-20 02:28:32 +03:00
trace_percpu_destroy_chunk ( chunk - > base_addr ) ;
2017-06-20 02:28:31 +03:00
2017-06-29 17:56:26 +03:00
if ( chunk - > data )
2010-04-09 13:57:01 +04:00
__free_pages ( chunk - > data , order_base_2 ( nr_pages ) ) ;
pcpu_free_chunk ( chunk ) ;
}
static struct page * pcpu_addr_to_page ( void * addr )
{
return virt_to_page ( addr ) ;
}
static int __init pcpu_verify_alloc_info ( const struct pcpu_alloc_info * ai )
{
size_t nr_pages , alloc_pages ;
/* all units must be in a single group */
if ( ai - > nr_groups ! = 1 ) {
2016-03-18 00:19:53 +03:00
pr_crit ( " can't handle more than one group \n " ) ;
2010-04-09 13:57:01 +04:00
return - EINVAL ;
}
nr_pages = ( ai - > groups [ 0 ] . nr_units * ai - > unit_size ) > > PAGE_SHIFT ;
alloc_pages = roundup_pow_of_two ( nr_pages ) ;
if ( alloc_pages > nr_pages )
2016-03-18 00:19:53 +03:00
pr_warn ( " wasting %zu pages per chunk \n " ,
2016-03-18 00:19:50 +03:00
alloc_pages - nr_pages ) ;
2010-04-09 13:57:01 +04:00
return 0 ;
}