diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/Makefile | 1 | ||||
-rw-r--r-- | drivers/base/cpu.c | 46 | ||||
-rw-r--r-- | drivers/base/iommu.c | 100 | ||||
-rw-r--r-- | drivers/base/memory.c | 19 | ||||
-rw-r--r-- | drivers/base/node.c | 107 | ||||
-rw-r--r-- | drivers/base/topology.c | 4 |
6 files changed, 266 insertions, 11 deletions
diff --git a/drivers/base/Makefile b/drivers/base/Makefile index c66637392bbc..b5b8ba512b28 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile | |||
@@ -11,6 +11,7 @@ obj-$(CONFIG_FW_LOADER) += firmware_class.o | |||
11 | obj-$(CONFIG_NUMA) += node.o | 11 | obj-$(CONFIG_NUMA) += node.o |
12 | obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o | 12 | obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o |
13 | obj-$(CONFIG_SMP) += topology.o | 13 | obj-$(CONFIG_SMP) += topology.o |
14 | obj-$(CONFIG_IOMMU_API) += iommu.o | ||
14 | ifeq ($(CONFIG_SYSFS),y) | 15 | ifeq ($(CONFIG_SYSFS),y) |
15 | obj-$(CONFIG_MODULES) += module.o | 16 | obj-$(CONFIG_MODULES) += module.o |
16 | endif | 17 | endif |
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 64f5d54f7edc..719ee5c1c8d9 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c | |||
@@ -109,7 +109,7 @@ static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL); | |||
109 | */ | 109 | */ |
110 | static ssize_t print_cpus_map(char *buf, cpumask_t *map) | 110 | static ssize_t print_cpus_map(char *buf, cpumask_t *map) |
111 | { | 111 | { |
112 | int n = cpulist_scnprintf(buf, PAGE_SIZE-2, *map); | 112 | int n = cpulist_scnprintf(buf, PAGE_SIZE-2, map); |
113 | 113 | ||
114 | buf[n++] = '\n'; | 114 | buf[n++] = '\n'; |
115 | buf[n] = '\0'; | 115 | buf[n] = '\0'; |
@@ -128,10 +128,54 @@ print_cpus_func(online); | |||
128 | print_cpus_func(possible); | 128 | print_cpus_func(possible); |
129 | print_cpus_func(present); | 129 | print_cpus_func(present); |
130 | 130 | ||
131 | /* | ||
132 | * Print values for NR_CPUS and offlined cpus | ||
133 | */ | ||
134 | static ssize_t print_cpus_kernel_max(struct sysdev_class *class, char *buf) | ||
135 | { | ||
136 | int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1); | ||
137 | return n; | ||
138 | } | ||
139 | static SYSDEV_CLASS_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL); | ||
140 | |||
141 | /* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */ | ||
142 | unsigned int total_cpus; | ||
143 | |||
144 | static ssize_t print_cpus_offline(struct sysdev_class *class, char *buf) | ||
145 | { | ||
146 | int n = 0, len = PAGE_SIZE-2; | ||
147 | cpumask_var_t offline; | ||
148 | |||
149 | /* display offline cpus < nr_cpu_ids */ | ||
150 | if (!alloc_cpumask_var(&offline, GFP_KERNEL)) | ||
151 | return -ENOMEM; | ||
152 | cpumask_complement(offline, cpu_online_mask); | ||
153 | n = cpulist_scnprintf(buf, len, offline); | ||
154 | free_cpumask_var(offline); | ||
155 | |||
156 | /* display offline cpus >= nr_cpu_ids */ | ||
157 | if (total_cpus && nr_cpu_ids < total_cpus) { | ||
158 | if (n && n < len) | ||
159 | buf[n++] = ','; | ||
160 | |||
161 | if (nr_cpu_ids == total_cpus-1) | ||
162 | n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids); | ||
163 | else | ||
164 | n += snprintf(&buf[n], len - n, "%d-%d", | ||
165 | nr_cpu_ids, total_cpus-1); | ||
166 | } | ||
167 | |||
168 | n += snprintf(&buf[n], len - n, "\n"); | ||
169 | return n; | ||
170 | } | ||
171 | static SYSDEV_CLASS_ATTR(offline, 0444, print_cpus_offline, NULL); | ||
172 | |||
131 | static struct sysdev_class_attribute *cpu_state_attr[] = { | 173 | static struct sysdev_class_attribute *cpu_state_attr[] = { |
132 | &attr_online_map, | 174 | &attr_online_map, |
133 | &attr_possible_map, | 175 | &attr_possible_map, |
134 | &attr_present_map, | 176 | &attr_present_map, |
177 | &attr_kernel_max, | ||
178 | &attr_offline, | ||
135 | }; | 179 | }; |
136 | 180 | ||
137 | static int cpu_states_init(void) | 181 | static int cpu_states_init(void) |
diff --git a/drivers/base/iommu.c b/drivers/base/iommu.c new file mode 100644 index 000000000000..5e039d4f877c --- /dev/null +++ b/drivers/base/iommu.c | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2008 Advanced Micro Devices, Inc. | ||
3 | * Author: Joerg Roedel <joerg.roedel@amd.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published | ||
7 | * by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
17 | */ | ||
18 | |||
19 | #include <linux/bug.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/iommu.h> | ||
23 | |||
24 | static struct iommu_ops *iommu_ops; | ||
25 | |||
26 | void register_iommu(struct iommu_ops *ops) | ||
27 | { | ||
28 | if (iommu_ops) | ||
29 | BUG(); | ||
30 | |||
31 | iommu_ops = ops; | ||
32 | } | ||
33 | |||
34 | bool iommu_found() | ||
35 | { | ||
36 | return iommu_ops != NULL; | ||
37 | } | ||
38 | EXPORT_SYMBOL_GPL(iommu_found); | ||
39 | |||
40 | struct iommu_domain *iommu_domain_alloc(void) | ||
41 | { | ||
42 | struct iommu_domain *domain; | ||
43 | int ret; | ||
44 | |||
45 | domain = kmalloc(sizeof(*domain), GFP_KERNEL); | ||
46 | if (!domain) | ||
47 | return NULL; | ||
48 | |||
49 | ret = iommu_ops->domain_init(domain); | ||
50 | if (ret) | ||
51 | goto out_free; | ||
52 | |||
53 | return domain; | ||
54 | |||
55 | out_free: | ||
56 | kfree(domain); | ||
57 | |||
58 | return NULL; | ||
59 | } | ||
60 | EXPORT_SYMBOL_GPL(iommu_domain_alloc); | ||
61 | |||
62 | void iommu_domain_free(struct iommu_domain *domain) | ||
63 | { | ||
64 | iommu_ops->domain_destroy(domain); | ||
65 | kfree(domain); | ||
66 | } | ||
67 | EXPORT_SYMBOL_GPL(iommu_domain_free); | ||
68 | |||
69 | int iommu_attach_device(struct iommu_domain *domain, struct device *dev) | ||
70 | { | ||
71 | return iommu_ops->attach_dev(domain, dev); | ||
72 | } | ||
73 | EXPORT_SYMBOL_GPL(iommu_attach_device); | ||
74 | |||
75 | void iommu_detach_device(struct iommu_domain *domain, struct device *dev) | ||
76 | { | ||
77 | iommu_ops->detach_dev(domain, dev); | ||
78 | } | ||
79 | EXPORT_SYMBOL_GPL(iommu_detach_device); | ||
80 | |||
81 | int iommu_map_range(struct iommu_domain *domain, unsigned long iova, | ||
82 | phys_addr_t paddr, size_t size, int prot) | ||
83 | { | ||
84 | return iommu_ops->map(domain, iova, paddr, size, prot); | ||
85 | } | ||
86 | EXPORT_SYMBOL_GPL(iommu_map_range); | ||
87 | |||
88 | void iommu_unmap_range(struct iommu_domain *domain, unsigned long iova, | ||
89 | size_t size) | ||
90 | { | ||
91 | iommu_ops->unmap(domain, iova, size); | ||
92 | } | ||
93 | EXPORT_SYMBOL_GPL(iommu_unmap_range); | ||
94 | |||
95 | phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, | ||
96 | unsigned long iova) | ||
97 | { | ||
98 | return iommu_ops->iova_to_phys(domain, iova); | ||
99 | } | ||
100 | EXPORT_SYMBOL_GPL(iommu_iova_to_phys); | ||
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 5260e9e0df48..989429cfed88 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c | |||
@@ -347,8 +347,9 @@ static inline int memory_probe_init(void) | |||
347 | * section belongs to... | 347 | * section belongs to... |
348 | */ | 348 | */ |
349 | 349 | ||
350 | static int add_memory_block(unsigned long node_id, struct mem_section *section, | 350 | static int add_memory_block(int nid, struct mem_section *section, |
351 | unsigned long state, int phys_device) | 351 | unsigned long state, int phys_device, |
352 | enum mem_add_context context) | ||
352 | { | 353 | { |
353 | struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL); | 354 | struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL); |
354 | int ret = 0; | 355 | int ret = 0; |
@@ -370,6 +371,10 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section, | |||
370 | ret = mem_create_simple_file(mem, phys_device); | 371 | ret = mem_create_simple_file(mem, phys_device); |
371 | if (!ret) | 372 | if (!ret) |
372 | ret = mem_create_simple_file(mem, removable); | 373 | ret = mem_create_simple_file(mem, removable); |
374 | if (!ret) { | ||
375 | if (context == HOTPLUG) | ||
376 | ret = register_mem_sect_under_node(mem, nid); | ||
377 | } | ||
373 | 378 | ||
374 | return ret; | 379 | return ret; |
375 | } | 380 | } |
@@ -382,7 +387,7 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section, | |||
382 | * | 387 | * |
383 | * This could be made generic for all sysdev classes. | 388 | * This could be made generic for all sysdev classes. |
384 | */ | 389 | */ |
385 | static struct memory_block *find_memory_block(struct mem_section *section) | 390 | struct memory_block *find_memory_block(struct mem_section *section) |
386 | { | 391 | { |
387 | struct kobject *kobj; | 392 | struct kobject *kobj; |
388 | struct sys_device *sysdev; | 393 | struct sys_device *sysdev; |
@@ -411,6 +416,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, | |||
411 | struct memory_block *mem; | 416 | struct memory_block *mem; |
412 | 417 | ||
413 | mem = find_memory_block(section); | 418 | mem = find_memory_block(section); |
419 | unregister_mem_sect_under_nodes(mem); | ||
414 | mem_remove_simple_file(mem, phys_index); | 420 | mem_remove_simple_file(mem, phys_index); |
415 | mem_remove_simple_file(mem, state); | 421 | mem_remove_simple_file(mem, state); |
416 | mem_remove_simple_file(mem, phys_device); | 422 | mem_remove_simple_file(mem, phys_device); |
@@ -424,9 +430,9 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, | |||
424 | * need an interface for the VM to add new memory regions, | 430 | * need an interface for the VM to add new memory regions, |
425 | * but without onlining it. | 431 | * but without onlining it. |
426 | */ | 432 | */ |
427 | int register_new_memory(struct mem_section *section) | 433 | int register_new_memory(int nid, struct mem_section *section) |
428 | { | 434 | { |
429 | return add_memory_block(0, section, MEM_OFFLINE, 0); | 435 | return add_memory_block(nid, section, MEM_OFFLINE, 0, HOTPLUG); |
430 | } | 436 | } |
431 | 437 | ||
432 | int unregister_memory_section(struct mem_section *section) | 438 | int unregister_memory_section(struct mem_section *section) |
@@ -458,7 +464,8 @@ int __init memory_dev_init(void) | |||
458 | for (i = 0; i < NR_MEM_SECTIONS; i++) { | 464 | for (i = 0; i < NR_MEM_SECTIONS; i++) { |
459 | if (!present_section_nr(i)) | 465 | if (!present_section_nr(i)) |
460 | continue; | 466 | continue; |
461 | err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE, 0); | 467 | err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE, |
468 | 0, BOOT); | ||
462 | if (!ret) | 469 | if (!ret) |
463 | ret = err; | 470 | ret = err; |
464 | } | 471 | } |
diff --git a/drivers/base/node.c b/drivers/base/node.c index f5207090885a..43fa90b837ee 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
7 | #include <linux/init.h> | 7 | #include <linux/init.h> |
8 | #include <linux/mm.h> | 8 | #include <linux/mm.h> |
9 | #include <linux/memory.h> | ||
9 | #include <linux/node.h> | 10 | #include <linux/node.h> |
10 | #include <linux/hugetlb.h> | 11 | #include <linux/hugetlb.h> |
11 | #include <linux/cpumask.h> | 12 | #include <linux/cpumask.h> |
@@ -30,8 +31,8 @@ static ssize_t node_read_cpumap(struct sys_device *dev, int type, char *buf) | |||
30 | BUILD_BUG_ON((NR_CPUS/32 * 9) > (PAGE_SIZE-1)); | 31 | BUILD_BUG_ON((NR_CPUS/32 * 9) > (PAGE_SIZE-1)); |
31 | 32 | ||
32 | len = type? | 33 | len = type? |
33 | cpulist_scnprintf(buf, PAGE_SIZE-2, *mask): | 34 | cpulist_scnprintf(buf, PAGE_SIZE-2, mask) : |
34 | cpumask_scnprintf(buf, PAGE_SIZE-2, *mask); | 35 | cpumask_scnprintf(buf, PAGE_SIZE-2, mask); |
35 | buf[len++] = '\n'; | 36 | buf[len++] = '\n'; |
36 | buf[len] = '\0'; | 37 | buf[len] = '\0'; |
37 | return len; | 38 | return len; |
@@ -248,6 +249,105 @@ int unregister_cpu_under_node(unsigned int cpu, unsigned int nid) | |||
248 | return 0; | 249 | return 0; |
249 | } | 250 | } |
250 | 251 | ||
252 | #ifdef CONFIG_MEMORY_HOTPLUG_SPARSE | ||
253 | #define page_initialized(page) (page->lru.next) | ||
254 | |||
255 | static int get_nid_for_pfn(unsigned long pfn) | ||
256 | { | ||
257 | struct page *page; | ||
258 | |||
259 | if (!pfn_valid_within(pfn)) | ||
260 | return -1; | ||
261 | page = pfn_to_page(pfn); | ||
262 | if (!page_initialized(page)) | ||
263 | return -1; | ||
264 | return pfn_to_nid(pfn); | ||
265 | } | ||
266 | |||
267 | /* register memory section under specified node if it spans that node */ | ||
268 | int register_mem_sect_under_node(struct memory_block *mem_blk, int nid) | ||
269 | { | ||
270 | unsigned long pfn, sect_start_pfn, sect_end_pfn; | ||
271 | |||
272 | if (!mem_blk) | ||
273 | return -EFAULT; | ||
274 | if (!node_online(nid)) | ||
275 | return 0; | ||
276 | sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index); | ||
277 | sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1; | ||
278 | for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) { | ||
279 | int page_nid; | ||
280 | |||
281 | page_nid = get_nid_for_pfn(pfn); | ||
282 | if (page_nid < 0) | ||
283 | continue; | ||
284 | if (page_nid != nid) | ||
285 | continue; | ||
286 | return sysfs_create_link_nowarn(&node_devices[nid].sysdev.kobj, | ||
287 | &mem_blk->sysdev.kobj, | ||
288 | kobject_name(&mem_blk->sysdev.kobj)); | ||
289 | } | ||
290 | /* mem section does not span the specified node */ | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | /* unregister memory section under all nodes that it spans */ | ||
295 | int unregister_mem_sect_under_nodes(struct memory_block *mem_blk) | ||
296 | { | ||
297 | nodemask_t unlinked_nodes; | ||
298 | unsigned long pfn, sect_start_pfn, sect_end_pfn; | ||
299 | |||
300 | if (!mem_blk) | ||
301 | return -EFAULT; | ||
302 | nodes_clear(unlinked_nodes); | ||
303 | sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index); | ||
304 | sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1; | ||
305 | for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) { | ||
306 | unsigned int nid; | ||
307 | |||
308 | nid = get_nid_for_pfn(pfn); | ||
309 | if (nid < 0) | ||
310 | continue; | ||
311 | if (!node_online(nid)) | ||
312 | continue; | ||
313 | if (node_test_and_set(nid, unlinked_nodes)) | ||
314 | continue; | ||
315 | sysfs_remove_link(&node_devices[nid].sysdev.kobj, | ||
316 | kobject_name(&mem_blk->sysdev.kobj)); | ||
317 | } | ||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static int link_mem_sections(int nid) | ||
322 | { | ||
323 | unsigned long start_pfn = NODE_DATA(nid)->node_start_pfn; | ||
324 | unsigned long end_pfn = start_pfn + NODE_DATA(nid)->node_spanned_pages; | ||
325 | unsigned long pfn; | ||
326 | int err = 0; | ||
327 | |||
328 | for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) { | ||
329 | unsigned long section_nr = pfn_to_section_nr(pfn); | ||
330 | struct mem_section *mem_sect; | ||
331 | struct memory_block *mem_blk; | ||
332 | int ret; | ||
333 | |||
334 | if (!present_section_nr(section_nr)) | ||
335 | continue; | ||
336 | mem_sect = __nr_to_section(section_nr); | ||
337 | mem_blk = find_memory_block(mem_sect); | ||
338 | ret = register_mem_sect_under_node(mem_blk, nid); | ||
339 | if (!err) | ||
340 | err = ret; | ||
341 | |||
342 | /* discard ref obtained in find_memory_block() */ | ||
343 | kobject_put(&mem_blk->sysdev.kobj); | ||
344 | } | ||
345 | return err; | ||
346 | } | ||
347 | #else | ||
348 | static int link_mem_sections(int nid) { return 0; } | ||
349 | #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */ | ||
350 | |||
251 | int register_one_node(int nid) | 351 | int register_one_node(int nid) |
252 | { | 352 | { |
253 | int error = 0; | 353 | int error = 0; |
@@ -267,6 +367,9 @@ int register_one_node(int nid) | |||
267 | if (cpu_to_node(cpu) == nid) | 367 | if (cpu_to_node(cpu) == nid) |
268 | register_cpu_under_node(cpu, nid); | 368 | register_cpu_under_node(cpu, nid); |
269 | } | 369 | } |
370 | |||
371 | /* link memory sections under this node */ | ||
372 | error = link_mem_sections(nid); | ||
270 | } | 373 | } |
271 | 374 | ||
272 | return error; | 375 | return error; |
diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 199cd97e32e6..a8bc1cbcfa7c 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c | |||
@@ -49,8 +49,8 @@ static ssize_t show_cpumap(int type, cpumask_t *mask, char *buf) | |||
49 | 49 | ||
50 | if (len > 1) { | 50 | if (len > 1) { |
51 | n = type? | 51 | n = type? |
52 | cpulist_scnprintf(buf, len-2, *mask): | 52 | cpulist_scnprintf(buf, len-2, mask) : |
53 | cpumask_scnprintf(buf, len-2, *mask); | 53 | cpumask_scnprintf(buf, len-2, mask); |
54 | buf[n++] = '\n'; | 54 | buf[n++] = '\n'; |
55 | buf[n] = '\0'; | 55 | buf[n] = '\0'; |
56 | } | 56 | } |