@ -113,6 +113,7 @@ struct pcpu_chunk {
void * data ; /* chunk data */
int first_free ; /* no free below this */
bool immutable ; /* no [de]population allowed */
int nr_populated ; /* # of populated pages */
unsigned long populated [ ] ; /* populated bitmap */
} ;
@ -161,6 +162,12 @@ static DEFINE_MUTEX(pcpu_alloc_mutex); /* chunk create/destroy, [de]pop */
static struct list_head * pcpu_slot __read_mostly ; /* chunk list slots */
/*
* The number of empty populated pages , protected by pcpu_lock . The
* reserved chunk doesn ' t contribute to the count .
*/
static int pcpu_nr_empty_pop_pages ;
/* reclaim work to release fully free chunks, scheduled from free path */
static void pcpu_reclaim ( struct work_struct * work ) ;
static DECLARE_WORK ( pcpu_reclaim_work , pcpu_reclaim ) ;
@ -295,6 +302,38 @@ static void pcpu_mem_free(void *ptr, size_t size)
vfree ( ptr ) ;
}
/**
* pcpu_count_occupied_pages - count the number of pages an area occupies
* @ chunk : chunk of interest
* @ i : index of the area in question
*
* Count the number of pages chunk ' s @ i ' th area occupies . When the area ' s
* start and / or end address isn ' t aligned to page boundary , the straddled
* page is included in the count iff the rest of the page is free .
*/
static int pcpu_count_occupied_pages ( struct pcpu_chunk * chunk , int i )
{
int off = chunk - > map [ i ] & ~ 1 ;
int end = chunk - > map [ i + 1 ] & ~ 1 ;
if ( ! PAGE_ALIGNED ( off ) & & i > 0 ) {
int prev = chunk - > map [ i - 1 ] ;
if ( ! ( prev & 1 ) & & prev < = round_down ( off , PAGE_SIZE ) )
off = round_down ( off , PAGE_SIZE ) ;
}
if ( ! PAGE_ALIGNED ( end ) & & i + 1 < chunk - > map_used ) {
int next = chunk - > map [ i + 1 ] ;
int nend = chunk - > map [ i + 2 ] & ~ 1 ;
if ( ! ( next & 1 ) & & nend > = round_up ( end , PAGE_SIZE ) )
end = round_up ( end , PAGE_SIZE ) ;
}
return max_t ( int , PFN_DOWN ( end ) - PFN_UP ( off ) , 0 ) ;
}
/**
* pcpu_chunk_relocate - put chunk in the appropriate chunk slot
* @ chunk : chunk of interest
@ -483,6 +522,7 @@ static int pcpu_fit_in_area(struct pcpu_chunk *chunk, int off, int this_size,
* @ size : wanted size in bytes
* @ align : wanted align
* @ pop_only : allocate only from the populated area
* @ occ_pages_p : out param for the number of pages the area occupies
*
* Try to allocate @ size bytes area aligned at @ align from @ chunk .
* Note that this function only allocates the offset . It doesn ' t
@ -498,7 +538,7 @@ static int pcpu_fit_in_area(struct pcpu_chunk *chunk, int off, int this_size,
* found .
*/
static int pcpu_alloc_area ( struct pcpu_chunk * chunk , int size , int align ,
bool pop_only )
bool pop_only , int * occ_pages_p )
{
int oslot = pcpu_chunk_slot ( chunk ) ;
int max_contig = 0 ;
@ -587,6 +627,7 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align,
chunk - > free_size - = size ;
* p | = 1 ;
* occ_pages_p = pcpu_count_occupied_pages ( chunk , i ) ;
pcpu_chunk_relocate ( chunk , oslot ) ;
return off ;
}
@ -602,6 +643,7 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align,
* pcpu_free_area - free area to a pcpu_chunk
* @ chunk : chunk of interest
* @ freeme : offset of area to free
* @ occ_pages_p : out param for the number of pages the area occupies
*
* Free area starting from @ freeme to @ chunk . Note that this function
* only modifies the allocation map . It doesn ' t depopulate or unmap
@ -610,7 +652,8 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk, int size, int align,
* CONTEXT :
* pcpu_lock .
*/
static void pcpu_free_area ( struct pcpu_chunk * chunk , int freeme )
static void pcpu_free_area ( struct pcpu_chunk * chunk , int freeme ,
int * occ_pages_p )
{
int oslot = pcpu_chunk_slot ( chunk ) ;
int off = 0 ;
@ -641,6 +684,8 @@ static void pcpu_free_area(struct pcpu_chunk *chunk, int freeme)
* p = off & = ~ 1 ;
chunk - > free_size + = ( p [ 1 ] & ~ 1 ) - off ;
* occ_pages_p = pcpu_count_occupied_pages ( chunk , i ) ;
/* merge with next? */
if ( ! ( p [ 1 ] & 1 ) )
to_free + + ;
@ -696,6 +741,50 @@ static void pcpu_free_chunk(struct pcpu_chunk *chunk)
pcpu_mem_free ( chunk , pcpu_chunk_struct_size ) ;
}
/**
* pcpu_chunk_populated - post - population bookkeeping
* @ chunk : pcpu_chunk which got populated
* @ page_start : the start page
* @ page_end : the end page
*
* Pages in [ @ page_start , @ page_end ) have been populated to @ chunk . Update
* the bookkeeping information accordingly . Must be called after each
* successful population .
*/
static void pcpu_chunk_populated ( struct pcpu_chunk * chunk ,
int page_start , int page_end )
{
int nr = page_end - page_start ;
lockdep_assert_held ( & pcpu_lock ) ;
bitmap_set ( chunk - > populated , page_start , nr ) ;
chunk - > nr_populated + = nr ;
pcpu_nr_empty_pop_pages + = nr ;
}
/**
* pcpu_chunk_depopulated - post - depopulation bookkeeping
* @ chunk : pcpu_chunk which got depopulated
* @ page_start : the start page
* @ page_end : the end page
*
* Pages in [ @ page_start , @ page_end ) have been depopulated from @ chunk .
* Update the bookkeeping information accordingly . Must be called after
* each successful depopulation .
*/
static void pcpu_chunk_depopulated ( struct pcpu_chunk * chunk ,
int page_start , int page_end )
{
int nr = page_end - page_start ;
lockdep_assert_held ( & pcpu_lock ) ;
bitmap_clear ( chunk - > populated , page_start , nr ) ;
chunk - > nr_populated - = nr ;
pcpu_nr_empty_pop_pages - = nr ;
}
/*
* Chunk management implementation .
*
@ -772,6 +861,7 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved,
struct pcpu_chunk * chunk ;
const char * err ;
bool is_atomic = ! ( gfp & GFP_KERNEL ) ;
int occ_pages = 0 ;
int slot , off , new_alloc , cpu , ret ;
unsigned long flags ;
void __percpu * ptr ;
@ -812,7 +902,8 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved,
spin_lock_irqsave ( & pcpu_lock , flags ) ;
}
off = pcpu_alloc_area ( chunk , size , align , is_atomic ) ;
off = pcpu_alloc_area ( chunk , size , align , is_atomic ,
& occ_pages ) ;
if ( off > = 0 )
goto area_found ;
@ -845,7 +936,8 @@ restart:
goto restart ;
}
off = pcpu_alloc_area ( chunk , size , align , is_atomic ) ;
off = pcpu_alloc_area ( chunk , size , align , is_atomic ,
& occ_pages ) ;
if ( off > = 0 )
goto area_found ;
}
@ -899,17 +991,20 @@ area_found:
spin_lock_irqsave ( & pcpu_lock , flags ) ;
if ( ret ) {
mutex_unlock ( & pcpu_alloc_mutex ) ;
pcpu_free_area ( chunk , off ) ;
pcpu_free_area ( chunk , off , & occ_pages ) ;
err = " failed to populate " ;
goto fail_unlock ;
}
bitmap_set ( chunk - > populated , rs , re - rs ) ;
pcpu_chunk_populated ( chunk , rs , re ) ;
spin_unlock_irqrestore ( & pcpu_lock , flags ) ;
}
mutex_unlock ( & pcpu_alloc_mutex ) ;
}
if ( chunk ! = pcpu_reserved_chunk )
pcpu_nr_empty_pop_pages - = occ_pages ;
/* clear the areas and return address relative to base address */
for_each_possible_cpu ( cpu )
memset ( ( void * ) pcpu_chunk_addr ( chunk , cpu , 0 ) + off , 0 , size ) ;
@ -1019,7 +1114,9 @@ static void pcpu_reclaim(struct work_struct *work)
pcpu_for_each_pop_region ( chunk , rs , re , 0 , pcpu_unit_pages ) {
pcpu_depopulate_chunk ( chunk , rs , re ) ;
bitmap_clear ( chunk - > populated , rs , re - rs ) ;
spin_lock_irq ( & pcpu_lock ) ;
pcpu_chunk_depopulated ( chunk , rs , re ) ;
spin_unlock_irq ( & pcpu_lock ) ;
}
pcpu_destroy_chunk ( chunk ) ;
}
@ -1041,7 +1138,7 @@ void free_percpu(void __percpu *ptr)
void * addr ;
struct pcpu_chunk * chunk ;
unsigned long flags ;
int off ;
int off , occ_pages ;
if ( ! ptr )
return ;
@ -1055,7 +1152,10 @@ void free_percpu(void __percpu *ptr)
chunk = pcpu_chunk_addr_search ( addr ) ;
off = addr - chunk - > base_addr ;
pcpu_free_area ( chunk , off ) ;
pcpu_free_area ( chunk , off , & occ_pages ) ;
if ( chunk ! = pcpu_reserved_chunk )
pcpu_nr_empty_pop_pages + = occ_pages ;
/* if there are more than one fully free chunks, wake up grim reaper */
if ( chunk - > free_size = = pcpu_unit_size ) {
@ -1459,6 +1559,7 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
schunk - > map_alloc = ARRAY_SIZE ( smap ) ;
schunk - > immutable = true ;
bitmap_fill ( schunk - > populated , pcpu_unit_pages ) ;
schunk - > nr_populated = pcpu_unit_pages ;
if ( ai - > reserved_size ) {
schunk - > free_size = ai - > reserved_size ;
@ -1488,6 +1589,7 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
dchunk - > map_alloc = ARRAY_SIZE ( dmap ) ;
dchunk - > immutable = true ;
bitmap_fill ( dchunk - > populated , pcpu_unit_pages ) ;
dchunk - > nr_populated = pcpu_unit_pages ;
dchunk - > contig_hint = dchunk - > free_size = dyn_size ;
dchunk - > map [ 0 ] = 1 ;
@ -1498,6 +1600,8 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
/* link the first chunk in */
pcpu_first_chunk = dchunk ? : schunk ;
pcpu_nr_empty_pop_pages + =
pcpu_count_occupied_pages ( pcpu_first_chunk , 1 ) ;
pcpu_chunk_relocate ( pcpu_first_chunk , - 1 ) ;
/* we're done */