diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-04-05 19:41:22 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-04-05 19:41:22 -0400 |
| commit | 9efe21cb82b5dbe3b0b2ae4de4eccc64ecb94e95 (patch) | |
| tree | 7ff8833745d2f268f897f6fa4a27263b4a572245 /mm/percpu.c | |
| parent | de18836e447c2dc30120c0919b8db8ddc0401cc4 (diff) | |
| parent | 0221c81b1b8eb0cbb6b30a0ced52ead32d2b4e4c (diff) | |
Merge branch 'linus' into irq/threaded
Conflicts:
include/linux/irq.h
kernel/irq/handle.c
Diffstat (limited to 'mm/percpu.c')
| -rw-r--r-- | mm/percpu.c | 130 |
1 files changed, 115 insertions, 15 deletions
diff --git a/mm/percpu.c b/mm/percpu.c index bfe6a3afaf45..1aa5d8fbca12 100644 --- a/mm/percpu.c +++ b/mm/percpu.c | |||
| @@ -46,7 +46,8 @@ | |||
| 46 | * - define CONFIG_HAVE_DYNAMIC_PER_CPU_AREA | 46 | * - define CONFIG_HAVE_DYNAMIC_PER_CPU_AREA |
| 47 | * | 47 | * |
| 48 | * - define __addr_to_pcpu_ptr() and __pcpu_ptr_to_addr() to translate | 48 | * - define __addr_to_pcpu_ptr() and __pcpu_ptr_to_addr() to translate |
| 49 | * regular address to percpu pointer and back | 49 | * regular address to percpu pointer and back if they need to be |
| 50 | * different from the default | ||
| 50 | * | 51 | * |
| 51 | * - use pcpu_setup_first_chunk() during percpu area initialization to | 52 | * - use pcpu_setup_first_chunk() during percpu area initialization to |
| 52 | * setup the first chunk containing the kernel static percpu area | 53 | * setup the first chunk containing the kernel static percpu area |
| @@ -67,11 +68,24 @@ | |||
| 67 | #include <linux/workqueue.h> | 68 | #include <linux/workqueue.h> |
| 68 | 69 | ||
| 69 | #include <asm/cacheflush.h> | 70 | #include <asm/cacheflush.h> |
| 71 | #include <asm/sections.h> | ||
| 70 | #include <asm/tlbflush.h> | 72 | #include <asm/tlbflush.h> |
| 71 | 73 | ||
| 72 | #define PCPU_SLOT_BASE_SHIFT 5 /* 1-31 shares the same slot */ | 74 | #define PCPU_SLOT_BASE_SHIFT 5 /* 1-31 shares the same slot */ |
| 73 | #define PCPU_DFL_MAP_ALLOC 16 /* start a map with 16 ents */ | 75 | #define PCPU_DFL_MAP_ALLOC 16 /* start a map with 16 ents */ |
| 74 | 76 | ||
| 77 | /* default addr <-> pcpu_ptr mapping, override in asm/percpu.h if necessary */ | ||
| 78 | #ifndef __addr_to_pcpu_ptr | ||
| 79 | #define __addr_to_pcpu_ptr(addr) \ | ||
| 80 | (void *)((unsigned long)(addr) - (unsigned long)pcpu_base_addr \ | ||
| 81 | + (unsigned long)__per_cpu_start) | ||
| 82 | #endif | ||
| 83 | #ifndef __pcpu_ptr_to_addr | ||
| 84 | #define __pcpu_ptr_to_addr(ptr) \ | ||
| 85 | (void *)((unsigned long)(ptr) + (unsigned long)pcpu_base_addr \ | ||
| 86 | - (unsigned long)__per_cpu_start) | ||
| 87 | #endif | ||
| 88 | |||
| 75 | struct pcpu_chunk { | 89 | struct pcpu_chunk { |
| 76 | struct list_head list; /* linked to pcpu_slot lists */ | 90 | struct list_head list; /* linked to pcpu_slot lists */ |
| 77 | struct rb_node rb_node; /* key is chunk->vm->addr */ | 91 | struct rb_node rb_node; /* key is chunk->vm->addr */ |
| @@ -1013,8 +1027,8 @@ EXPORT_SYMBOL_GPL(free_percpu); | |||
| 1013 | * @get_page_fn: callback to fetch page pointer | 1027 | * @get_page_fn: callback to fetch page pointer |
| 1014 | * @static_size: the size of static percpu area in bytes | 1028 | * @static_size: the size of static percpu area in bytes |
| 1015 | * @reserved_size: the size of reserved percpu area in bytes | 1029 | * @reserved_size: the size of reserved percpu area in bytes |
| 1016 | * @unit_size: unit size in bytes, must be multiple of PAGE_SIZE, -1 for auto | ||
| 1017 | * @dyn_size: free size for dynamic allocation in bytes, -1 for auto | 1030 | * @dyn_size: free size for dynamic allocation in bytes, -1 for auto |
| 1031 | * @unit_size: unit size in bytes, must be multiple of PAGE_SIZE, -1 for auto | ||
| 1018 | * @base_addr: mapped address, NULL for auto | 1032 | * @base_addr: mapped address, NULL for auto |
| 1019 | * @populate_pte_fn: callback to allocate pagetable, NULL if unnecessary | 1033 | * @populate_pte_fn: callback to allocate pagetable, NULL if unnecessary |
| 1020 | * | 1034 | * |
| @@ -1039,14 +1053,14 @@ EXPORT_SYMBOL_GPL(free_percpu); | |||
| 1039 | * limited offset range for symbol relocations to guarantee module | 1053 | * limited offset range for symbol relocations to guarantee module |
| 1040 | * percpu symbols fall inside the relocatable range. | 1054 | * percpu symbols fall inside the relocatable range. |
| 1041 | * | 1055 | * |
| 1056 | * @dyn_size, if non-negative, determines the number of bytes | ||
| 1057 | * available for dynamic allocation in the first chunk. Specifying | ||
| 1058 | * non-negative value makes percpu leave alone the area beyond | ||
| 1059 | * @static_size + @reserved_size + @dyn_size. | ||
| 1060 | * | ||
| 1042 | * @unit_size, if non-negative, specifies unit size and must be | 1061 | * @unit_size, if non-negative, specifies unit size and must be |
| 1043 | * aligned to PAGE_SIZE and equal to or larger than @static_size + | 1062 | * aligned to PAGE_SIZE and equal to or larger than @static_size + |
| 1044 | * @reserved_size + @dyn_size. | 1063 | * @reserved_size + if non-negative, @dyn_size. |
| 1045 | * | ||
| 1046 | * @dyn_size, if non-negative, limits the number of bytes available | ||
| 1047 | * for dynamic allocation in the first chunk. Specifying non-negative | ||
| 1048 | * value make percpu leave alone the area beyond @static_size + | ||
| 1049 | * @reserved_size + @dyn_size. | ||
| 1050 | * | 1064 | * |
| 1051 | * Non-null @base_addr means that the caller already allocated virtual | 1065 | * Non-null @base_addr means that the caller already allocated virtual |
| 1052 | * region for the first chunk and mapped it. percpu must not mess | 1066 | * region for the first chunk and mapped it. percpu must not mess |
| @@ -1069,12 +1083,14 @@ EXPORT_SYMBOL_GPL(free_percpu); | |||
| 1069 | */ | 1083 | */ |
| 1070 | size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn, | 1084 | size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn, |
| 1071 | size_t static_size, size_t reserved_size, | 1085 | size_t static_size, size_t reserved_size, |
| 1072 | ssize_t unit_size, ssize_t dyn_size, | 1086 | ssize_t dyn_size, ssize_t unit_size, |
| 1073 | void *base_addr, | 1087 | void *base_addr, |
| 1074 | pcpu_populate_pte_fn_t populate_pte_fn) | 1088 | pcpu_populate_pte_fn_t populate_pte_fn) |
| 1075 | { | 1089 | { |
| 1076 | static struct vm_struct first_vm; | 1090 | static struct vm_struct first_vm; |
| 1077 | static int smap[2], dmap[2]; | 1091 | static int smap[2], dmap[2]; |
| 1092 | size_t size_sum = static_size + reserved_size + | ||
| 1093 | (dyn_size >= 0 ? dyn_size : 0); | ||
| 1078 | struct pcpu_chunk *schunk, *dchunk = NULL; | 1094 | struct pcpu_chunk *schunk, *dchunk = NULL; |
| 1079 | unsigned int cpu; | 1095 | unsigned int cpu; |
| 1080 | int nr_pages; | 1096 | int nr_pages; |
| @@ -1085,20 +1101,18 @@ size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn, | |||
| 1085 | ARRAY_SIZE(dmap) >= PCPU_DFL_MAP_ALLOC); | 1101 | ARRAY_SIZE(dmap) >= PCPU_DFL_MAP_ALLOC); |
| 1086 | BUG_ON(!static_size); | 1102 | BUG_ON(!static_size); |
| 1087 | if (unit_size >= 0) { | 1103 | if (unit_size >= 0) { |
| 1088 | BUG_ON(unit_size < static_size + reserved_size + | 1104 | BUG_ON(unit_size < size_sum); |
| 1089 | (dyn_size >= 0 ? dyn_size : 0)); | ||
| 1090 | BUG_ON(unit_size & ~PAGE_MASK); | 1105 | BUG_ON(unit_size & ~PAGE_MASK); |
| 1091 | } else { | 1106 | BUG_ON(unit_size < PCPU_MIN_UNIT_SIZE); |
| 1092 | BUG_ON(dyn_size >= 0); | 1107 | } else |
| 1093 | BUG_ON(base_addr); | 1108 | BUG_ON(base_addr); |
| 1094 | } | ||
| 1095 | BUG_ON(base_addr && populate_pte_fn); | 1109 | BUG_ON(base_addr && populate_pte_fn); |
| 1096 | 1110 | ||
| 1097 | if (unit_size >= 0) | 1111 | if (unit_size >= 0) |
| 1098 | pcpu_unit_pages = unit_size >> PAGE_SHIFT; | 1112 | pcpu_unit_pages = unit_size >> PAGE_SHIFT; |
| 1099 | else | 1113 | else |
| 1100 | pcpu_unit_pages = max_t(int, PCPU_MIN_UNIT_SIZE >> PAGE_SHIFT, | 1114 | pcpu_unit_pages = max_t(int, PCPU_MIN_UNIT_SIZE >> PAGE_SHIFT, |
| 1101 | PFN_UP(static_size + reserved_size)); | 1115 | PFN_UP(size_sum)); |
| 1102 | 1116 | ||
| 1103 | pcpu_unit_size = pcpu_unit_pages << PAGE_SHIFT; | 1117 | pcpu_unit_size = pcpu_unit_pages << PAGE_SHIFT; |
| 1104 | pcpu_chunk_size = num_possible_cpus() * pcpu_unit_size; | 1118 | pcpu_chunk_size = num_possible_cpus() * pcpu_unit_size; |
| @@ -1224,3 +1238,89 @@ size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn, | |||
| 1224 | pcpu_base_addr = (void *)pcpu_chunk_addr(schunk, 0, 0); | 1238 | pcpu_base_addr = (void *)pcpu_chunk_addr(schunk, 0, 0); |
| 1225 | return pcpu_unit_size; | 1239 | return pcpu_unit_size; |
| 1226 | } | 1240 | } |
| 1241 | |||
| 1242 | /* | ||
| 1243 | * Embedding first chunk setup helper. | ||
| 1244 | */ | ||
| 1245 | static void *pcpue_ptr __initdata; | ||
| 1246 | static size_t pcpue_size __initdata; | ||
| 1247 | static size_t pcpue_unit_size __initdata; | ||
| 1248 | |||
| 1249 | static struct page * __init pcpue_get_page(unsigned int cpu, int pageno) | ||
| 1250 | { | ||
| 1251 | size_t off = (size_t)pageno << PAGE_SHIFT; | ||
| 1252 | |||
| 1253 | if (off >= pcpue_size) | ||
| 1254 | return NULL; | ||
| 1255 | |||
| 1256 | return virt_to_page(pcpue_ptr + cpu * pcpue_unit_size + off); | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | /** | ||
| 1260 | * pcpu_embed_first_chunk - embed the first percpu chunk into bootmem | ||
| 1261 | * @static_size: the size of static percpu area in bytes | ||
| 1262 | * @reserved_size: the size of reserved percpu area in bytes | ||
| 1263 | * @dyn_size: free size for dynamic allocation in bytes, -1 for auto | ||
| 1264 | * @unit_size: unit size in bytes, must be multiple of PAGE_SIZE, -1 for auto | ||
| 1265 | * | ||
| 1266 | * This is a helper to ease setting up embedded first percpu chunk and | ||
| 1267 | * can be called where pcpu_setup_first_chunk() is expected. | ||
| 1268 | * | ||
| 1269 | * If this function is used to setup the first chunk, it is allocated | ||
| 1270 | * as a contiguous area using bootmem allocator and used as-is without | ||
| 1271 | * being mapped into vmalloc area. This enables the first chunk to | ||
| 1272 | * piggy back on the linear physical mapping which often uses larger | ||
| 1273 | * page size. | ||
| 1274 | * | ||
| 1275 | * When @dyn_size is positive, dynamic area might be larger than | ||
| 1276 | * specified to fill page alignment. Also, when @dyn_size is auto, | ||
| 1277 | * @dyn_size does not fill the whole first chunk but only what's | ||
| 1278 | * necessary for page alignment after static and reserved areas. | ||
| 1279 | * | ||
| 1280 | * If the needed size is smaller than the minimum or specified unit | ||
| 1281 | * size, the leftover is returned to the bootmem allocator. | ||
| 1282 | * | ||
| 1283 | * RETURNS: | ||
| 1284 | * The determined pcpu_unit_size which can be used to initialize | ||
| 1285 | * percpu access on success, -errno on failure. | ||
| 1286 | */ | ||
| 1287 | ssize_t __init pcpu_embed_first_chunk(size_t static_size, size_t reserved_size, | ||
| 1288 | ssize_t dyn_size, ssize_t unit_size) | ||
| 1289 | { | ||
| 1290 | unsigned int cpu; | ||
| 1291 | |||
| 1292 | /* determine parameters and allocate */ | ||
| 1293 | pcpue_size = PFN_ALIGN(static_size + reserved_size + | ||
| 1294 | (dyn_size >= 0 ? dyn_size : 0)); | ||
| 1295 | if (dyn_size != 0) | ||
| 1296 | dyn_size = pcpue_size - static_size - reserved_size; | ||
| 1297 | |||
| 1298 | if (unit_size >= 0) { | ||
| 1299 | BUG_ON(unit_size < pcpue_size); | ||
| 1300 | pcpue_unit_size = unit_size; | ||
| 1301 | } else | ||
| 1302 | pcpue_unit_size = max_t(size_t, pcpue_size, PCPU_MIN_UNIT_SIZE); | ||
| 1303 | |||
| 1304 | pcpue_ptr = __alloc_bootmem_nopanic( | ||
| 1305 | num_possible_cpus() * pcpue_unit_size, | ||
| 1306 | PAGE_SIZE, __pa(MAX_DMA_ADDRESS)); | ||
| 1307 | if (!pcpue_ptr) | ||
| 1308 | return -ENOMEM; | ||
| 1309 | |||
| 1310 | /* return the leftover and copy */ | ||
| 1311 | for_each_possible_cpu(cpu) { | ||
| 1312 | void *ptr = pcpue_ptr + cpu * pcpue_unit_size; | ||
| 1313 | |||
| 1314 | free_bootmem(__pa(ptr + pcpue_size), | ||
| 1315 | pcpue_unit_size - pcpue_size); | ||
| 1316 | memcpy(ptr, __per_cpu_load, static_size); | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | /* we're ready, commit */ | ||
| 1320 | pr_info("PERCPU: Embedded %zu pages at %p, static data %zu bytes\n", | ||
| 1321 | pcpue_size >> PAGE_SHIFT, pcpue_ptr, static_size); | ||
| 1322 | |||
| 1323 | return pcpu_setup_first_chunk(pcpue_get_page, static_size, | ||
| 1324 | reserved_size, dyn_size, | ||
| 1325 | pcpue_unit_size, pcpue_ptr, NULL); | ||
| 1326 | } | ||
