18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mm/percpu-km.c - kernel memory based chunk allocation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 SUSE Linux Products GmbH 68c2ecf20Sopenharmony_ci * Copyright (C) 2010 Tejun Heo <tj@kernel.org> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Chunks are allocated as a contiguous kernel memory using gfp 98c2ecf20Sopenharmony_ci * allocation. This is to be used on nommu architectures. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * To use percpu-km, 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * - define CONFIG_NEED_PER_CPU_KM from the arch Kconfig. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * - CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK must not be defined. It's 168c2ecf20Sopenharmony_ci * not compatible with PER_CPU_KM. EMBED_FIRST_CHUNK should work 178c2ecf20Sopenharmony_ci * fine. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * - NUMA is not supported. When setting up the first chunk, 208c2ecf20Sopenharmony_ci * @cpu_distance_fn should be NULL or report all CPUs to be nearer 218c2ecf20Sopenharmony_ci * than or at LOCAL_DISTANCE. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * - It's best if the chunk size is power of two multiple of 248c2ecf20Sopenharmony_ci * PAGE_SIZE. Because each chunk is allocated as a contiguous 258c2ecf20Sopenharmony_ci * kernel memory block using alloc_pages(), memory will be wasted if 268c2ecf20Sopenharmony_ci * chunk size is not aligned. percpu-km code will whine about it. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#if defined(CONFIG_SMP) && defined(CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK) 308c2ecf20Sopenharmony_ci#error "contiguous percpu allocation is incompatible with paged first chunk" 318c2ecf20Sopenharmony_ci#endif 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/log2.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int pcpu_populate_chunk(struct pcpu_chunk *chunk, 368c2ecf20Sopenharmony_ci int page_start, int page_end, gfp_t gfp) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci return 0; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void pcpu_depopulate_chunk(struct pcpu_chunk *chunk, 428c2ecf20Sopenharmony_ci int page_start, int page_end) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci /* nada */ 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct pcpu_chunk *pcpu_create_chunk(enum pcpu_chunk_type type, 488c2ecf20Sopenharmony_ci gfp_t gfp) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci const int nr_pages = pcpu_group_sizes[0] >> PAGE_SHIFT; 518c2ecf20Sopenharmony_ci struct pcpu_chunk *chunk; 528c2ecf20Sopenharmony_ci struct page *pages; 538c2ecf20Sopenharmony_ci unsigned long flags; 548c2ecf20Sopenharmony_ci int i; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci chunk = pcpu_alloc_chunk(type, gfp); 578c2ecf20Sopenharmony_ci if (!chunk) 588c2ecf20Sopenharmony_ci return NULL; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci pages = alloc_pages(gfp, order_base_2(nr_pages)); 618c2ecf20Sopenharmony_ci if (!pages) { 628c2ecf20Sopenharmony_ci pcpu_free_chunk(chunk); 638c2ecf20Sopenharmony_ci return NULL; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci for (i = 0; i < nr_pages; i++) 678c2ecf20Sopenharmony_ci pcpu_set_page_chunk(nth_page(pages, i), chunk); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci chunk->data = pages; 708c2ecf20Sopenharmony_ci chunk->base_addr = page_address(pages); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci spin_lock_irqsave(&pcpu_lock, flags); 738c2ecf20Sopenharmony_ci pcpu_chunk_populated(chunk, 0, nr_pages); 748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pcpu_lock, flags); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci pcpu_stats_chunk_alloc(); 778c2ecf20Sopenharmony_ci trace_percpu_create_chunk(chunk->base_addr); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return chunk; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void pcpu_destroy_chunk(struct pcpu_chunk *chunk) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci const int nr_pages = pcpu_group_sizes[0] >> PAGE_SHIFT; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!chunk) 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci pcpu_stats_chunk_dealloc(); 908c2ecf20Sopenharmony_ci trace_percpu_destroy_chunk(chunk->base_addr); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (chunk->data) 938c2ecf20Sopenharmony_ci __free_pages(chunk->data, order_base_2(nr_pages)); 948c2ecf20Sopenharmony_ci pcpu_free_chunk(chunk); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic struct page *pcpu_addr_to_page(void *addr) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci return virt_to_page(addr); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int __init pcpu_verify_alloc_info(const struct pcpu_alloc_info *ai) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci size_t nr_pages, alloc_pages; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* all units must be in a single group */ 1078c2ecf20Sopenharmony_ci if (ai->nr_groups != 1) { 1088c2ecf20Sopenharmony_ci pr_crit("can't handle more than one group\n"); 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci nr_pages = (ai->groups[0].nr_units * ai->unit_size) >> PAGE_SHIFT; 1138c2ecf20Sopenharmony_ci alloc_pages = roundup_pow_of_two(nr_pages); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (alloc_pages > nr_pages) 1168c2ecf20Sopenharmony_ci pr_warn("wasting %zu pages per chunk\n", 1178c2ecf20Sopenharmony_ci alloc_pages - nr_pages); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 121