diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-06-28 06:58:05 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-06-28 06:58:05 -0400 |
commit | a204dbc61b7f4cb1a7e2cb3ad057b135164782da (patch) | |
tree | f82151c04a30f49c3dee8926d184575ad2e7b1e2 /drivers/base | |
parent | 45e00374db944b1c12987b501bcaa279b3e36d93 (diff) | |
parent | 08f502c1c343031f0d126bd00e87dede38269d12 (diff) |
Merge branch 'acpi-hotplug'
* acpi-hotplug:
ACPI: Do not use CONFIG_ACPI_HOTPLUG_MEMORY_MODULE
ACPI / cpufreq: Add ACPI processor device IDs to acpi-cpufreq
Memory hotplug: Move alternative function definitions to header
ACPI / processor: Fix potential NULL pointer dereference in acpi_processor_add()
Memory hotplug / ACPI: Simplify memory removal
ACPI / scan: Add second pass of companion offlining to hot-remove code
Driver core / MM: Drop offline_memory_block()
ACPI / processor: Pass processor object handle to acpi_bind_one()
ACPI: Drop removal_type field from struct acpi_device
Driver core / memory: Simplify __memory_block_change_state()
ACPI / processor: Initialize per_cpu(processors, pr->id) properly
CPU: Fix sysfs cpu/online of offlined CPUs
Driver core: Introduce offline/online callbacks for memory blocks
ACPI / memhotplug: Bind removable memory blocks to ACPI device nodes
ACPI / processor: Use common hotplug infrastructure
ACPI / hotplug: Use device offline/online for graceful hot-removal
Driver core: Use generic offline/online for CPU offline/online
Driver core: Add offline/online device operations
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/core.c | 130 | ||||
-rw-r--r-- | drivers/base/cpu.c | 101 | ||||
-rw-r--r-- | drivers/base/memory.c | 114 |
3 files changed, 248 insertions, 97 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 2499cefdcdf2..2166f34b7d84 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -403,6 +403,36 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, | |||
403 | static struct device_attribute uevent_attr = | 403 | static struct device_attribute uevent_attr = |
404 | __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent); | 404 | __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent); |
405 | 405 | ||
406 | static ssize_t show_online(struct device *dev, struct device_attribute *attr, | ||
407 | char *buf) | ||
408 | { | ||
409 | bool val; | ||
410 | |||
411 | lock_device_hotplug(); | ||
412 | val = !dev->offline; | ||
413 | unlock_device_hotplug(); | ||
414 | return sprintf(buf, "%u\n", val); | ||
415 | } | ||
416 | |||
417 | static ssize_t store_online(struct device *dev, struct device_attribute *attr, | ||
418 | const char *buf, size_t count) | ||
419 | { | ||
420 | bool val; | ||
421 | int ret; | ||
422 | |||
423 | ret = strtobool(buf, &val); | ||
424 | if (ret < 0) | ||
425 | return ret; | ||
426 | |||
427 | lock_device_hotplug(); | ||
428 | ret = val ? device_online(dev) : device_offline(dev); | ||
429 | unlock_device_hotplug(); | ||
430 | return ret < 0 ? ret : count; | ||
431 | } | ||
432 | |||
433 | static struct device_attribute online_attr = | ||
434 | __ATTR(online, S_IRUGO | S_IWUSR, show_online, store_online); | ||
435 | |||
406 | static int device_add_attributes(struct device *dev, | 436 | static int device_add_attributes(struct device *dev, |
407 | struct device_attribute *attrs) | 437 | struct device_attribute *attrs) |
408 | { | 438 | { |
@@ -516,6 +546,12 @@ static int device_add_attrs(struct device *dev) | |||
516 | if (error) | 546 | if (error) |
517 | goto err_remove_type_groups; | 547 | goto err_remove_type_groups; |
518 | 548 | ||
549 | if (device_supports_offline(dev) && !dev->offline_disabled) { | ||
550 | error = device_create_file(dev, &online_attr); | ||
551 | if (error) | ||
552 | goto err_remove_type_groups; | ||
553 | } | ||
554 | |||
519 | return 0; | 555 | return 0; |
520 | 556 | ||
521 | err_remove_type_groups: | 557 | err_remove_type_groups: |
@@ -536,6 +572,7 @@ static void device_remove_attrs(struct device *dev) | |||
536 | struct class *class = dev->class; | 572 | struct class *class = dev->class; |
537 | const struct device_type *type = dev->type; | 573 | const struct device_type *type = dev->type; |
538 | 574 | ||
575 | device_remove_file(dev, &online_attr); | ||
539 | device_remove_groups(dev, dev->groups); | 576 | device_remove_groups(dev, dev->groups); |
540 | 577 | ||
541 | if (type) | 578 | if (type) |
@@ -1433,6 +1470,99 @@ EXPORT_SYMBOL_GPL(put_device); | |||
1433 | EXPORT_SYMBOL_GPL(device_create_file); | 1470 | EXPORT_SYMBOL_GPL(device_create_file); |
1434 | EXPORT_SYMBOL_GPL(device_remove_file); | 1471 | EXPORT_SYMBOL_GPL(device_remove_file); |
1435 | 1472 | ||
1473 | static DEFINE_MUTEX(device_hotplug_lock); | ||
1474 | |||
1475 | void lock_device_hotplug(void) | ||
1476 | { | ||
1477 | mutex_lock(&device_hotplug_lock); | ||
1478 | } | ||
1479 | |||
1480 | void unlock_device_hotplug(void) | ||
1481 | { | ||
1482 | mutex_unlock(&device_hotplug_lock); | ||
1483 | } | ||
1484 | |||
1485 | static int device_check_offline(struct device *dev, void *not_used) | ||
1486 | { | ||
1487 | int ret; | ||
1488 | |||
1489 | ret = device_for_each_child(dev, NULL, device_check_offline); | ||
1490 | if (ret) | ||
1491 | return ret; | ||
1492 | |||
1493 | return device_supports_offline(dev) && !dev->offline ? -EBUSY : 0; | ||
1494 | } | ||
1495 | |||
1496 | /** | ||
1497 | * device_offline - Prepare the device for hot-removal. | ||
1498 | * @dev: Device to be put offline. | ||
1499 | * | ||
1500 | * Execute the device bus type's .offline() callback, if present, to prepare | ||
1501 | * the device for a subsequent hot-removal. If that succeeds, the device must | ||
1502 | * not be used until either it is removed or its bus type's .online() callback | ||
1503 | * is executed. | ||
1504 | * | ||
1505 | * Call under device_hotplug_lock. | ||
1506 | */ | ||
1507 | int device_offline(struct device *dev) | ||
1508 | { | ||
1509 | int ret; | ||
1510 | |||
1511 | if (dev->offline_disabled) | ||
1512 | return -EPERM; | ||
1513 | |||
1514 | ret = device_for_each_child(dev, NULL, device_check_offline); | ||
1515 | if (ret) | ||
1516 | return ret; | ||
1517 | |||
1518 | device_lock(dev); | ||
1519 | if (device_supports_offline(dev)) { | ||
1520 | if (dev->offline) { | ||
1521 | ret = 1; | ||
1522 | } else { | ||
1523 | ret = dev->bus->offline(dev); | ||
1524 | if (!ret) { | ||
1525 | kobject_uevent(&dev->kobj, KOBJ_OFFLINE); | ||
1526 | dev->offline = true; | ||
1527 | } | ||
1528 | } | ||
1529 | } | ||
1530 | device_unlock(dev); | ||
1531 | |||
1532 | return ret; | ||
1533 | } | ||
1534 | |||
1535 | /** | ||
1536 | * device_online - Put the device back online after successful device_offline(). | ||
1537 | * @dev: Device to be put back online. | ||
1538 | * | ||
1539 | * If device_offline() has been successfully executed for @dev, but the device | ||
1540 | * has not been removed subsequently, execute its bus type's .online() callback | ||
1541 | * to indicate that the device can be used again. | ||
1542 | * | ||
1543 | * Call under device_hotplug_lock. | ||
1544 | */ | ||
1545 | int device_online(struct device *dev) | ||
1546 | { | ||
1547 | int ret = 0; | ||
1548 | |||
1549 | device_lock(dev); | ||
1550 | if (device_supports_offline(dev)) { | ||
1551 | if (dev->offline) { | ||
1552 | ret = dev->bus->online(dev); | ||
1553 | if (!ret) { | ||
1554 | kobject_uevent(&dev->kobj, KOBJ_ONLINE); | ||
1555 | dev->offline = false; | ||
1556 | } | ||
1557 | } else { | ||
1558 | ret = 1; | ||
1559 | } | ||
1560 | } | ||
1561 | device_unlock(dev); | ||
1562 | |||
1563 | return ret; | ||
1564 | } | ||
1565 | |||
1436 | struct root_device { | 1566 | struct root_device { |
1437 | struct device dev; | 1567 | struct device dev; |
1438 | struct module *owner; | 1568 | struct module *owner; |
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 3d48fc887ef4..1d110dc6f0c1 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c | |||
@@ -13,17 +13,21 @@ | |||
13 | #include <linux/gfp.h> | 13 | #include <linux/gfp.h> |
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/percpu.h> | 15 | #include <linux/percpu.h> |
16 | #include <linux/acpi.h> | ||
16 | 17 | ||
17 | #include "base.h" | 18 | #include "base.h" |
18 | 19 | ||
19 | struct bus_type cpu_subsys = { | ||
20 | .name = "cpu", | ||
21 | .dev_name = "cpu", | ||
22 | }; | ||
23 | EXPORT_SYMBOL_GPL(cpu_subsys); | ||
24 | |||
25 | static DEFINE_PER_CPU(struct device *, cpu_sys_devices); | 20 | static DEFINE_PER_CPU(struct device *, cpu_sys_devices); |
26 | 21 | ||
22 | static int cpu_subsys_match(struct device *dev, struct device_driver *drv) | ||
23 | { | ||
24 | /* ACPI style match is the only one that may succeed. */ | ||
25 | if (acpi_driver_match_device(dev, drv)) | ||
26 | return 1; | ||
27 | |||
28 | return 0; | ||
29 | } | ||
30 | |||
27 | #ifdef CONFIG_HOTPLUG_CPU | 31 | #ifdef CONFIG_HOTPLUG_CPU |
28 | static void change_cpu_under_node(struct cpu *cpu, | 32 | static void change_cpu_under_node(struct cpu *cpu, |
29 | unsigned int from_nid, unsigned int to_nid) | 33 | unsigned int from_nid, unsigned int to_nid) |
@@ -34,69 +38,45 @@ static void change_cpu_under_node(struct cpu *cpu, | |||
34 | cpu->node_id = to_nid; | 38 | cpu->node_id = to_nid; |
35 | } | 39 | } |
36 | 40 | ||
37 | static ssize_t show_online(struct device *dev, | 41 | static int __ref cpu_subsys_online(struct device *dev) |
38 | struct device_attribute *attr, | ||
39 | char *buf) | ||
40 | { | 42 | { |
41 | struct cpu *cpu = container_of(dev, struct cpu, dev); | 43 | struct cpu *cpu = container_of(dev, struct cpu, dev); |
44 | int cpuid = dev->id; | ||
45 | int from_nid, to_nid; | ||
46 | int ret; | ||
42 | 47 | ||
43 | return sprintf(buf, "%u\n", !!cpu_online(cpu->dev.id)); | 48 | cpu_hotplug_driver_lock(); |
49 | |||
50 | from_nid = cpu_to_node(cpuid); | ||
51 | ret = cpu_up(cpuid); | ||
52 | /* | ||
53 | * When hot adding memory to memoryless node and enabling a cpu | ||
54 | * on the node, node number of the cpu may internally change. | ||
55 | */ | ||
56 | to_nid = cpu_to_node(cpuid); | ||
57 | if (from_nid != to_nid) | ||
58 | change_cpu_under_node(cpu, from_nid, to_nid); | ||
59 | |||
60 | cpu_hotplug_driver_unlock(); | ||
61 | return ret; | ||
44 | } | 62 | } |
45 | 63 | ||
46 | static ssize_t __ref store_online(struct device *dev, | 64 | static int cpu_subsys_offline(struct device *dev) |
47 | struct device_attribute *attr, | ||
48 | const char *buf, size_t count) | ||
49 | { | 65 | { |
50 | struct cpu *cpu = container_of(dev, struct cpu, dev); | 66 | int ret; |
51 | int cpuid = cpu->dev.id; | ||
52 | int from_nid, to_nid; | ||
53 | ssize_t ret; | ||
54 | 67 | ||
55 | cpu_hotplug_driver_lock(); | 68 | cpu_hotplug_driver_lock(); |
56 | switch (buf[0]) { | 69 | ret = cpu_down(dev->id); |
57 | case '0': | ||
58 | ret = cpu_down(cpuid); | ||
59 | if (!ret) | ||
60 | kobject_uevent(&dev->kobj, KOBJ_OFFLINE); | ||
61 | break; | ||
62 | case '1': | ||
63 | from_nid = cpu_to_node(cpuid); | ||
64 | ret = cpu_up(cpuid); | ||
65 | |||
66 | /* | ||
67 | * When hot adding memory to memoryless node and enabling a cpu | ||
68 | * on the node, node number of the cpu may internally change. | ||
69 | */ | ||
70 | to_nid = cpu_to_node(cpuid); | ||
71 | if (from_nid != to_nid) | ||
72 | change_cpu_under_node(cpu, from_nid, to_nid); | ||
73 | |||
74 | if (!ret) | ||
75 | kobject_uevent(&dev->kobj, KOBJ_ONLINE); | ||
76 | break; | ||
77 | default: | ||
78 | ret = -EINVAL; | ||
79 | } | ||
80 | cpu_hotplug_driver_unlock(); | 70 | cpu_hotplug_driver_unlock(); |
81 | |||
82 | if (ret >= 0) | ||
83 | ret = count; | ||
84 | return ret; | 71 | return ret; |
85 | } | 72 | } |
86 | static DEVICE_ATTR(online, 0644, show_online, store_online); | ||
87 | 73 | ||
88 | static void __cpuinit register_cpu_control(struct cpu *cpu) | ||
89 | { | ||
90 | device_create_file(&cpu->dev, &dev_attr_online); | ||
91 | } | ||
92 | void unregister_cpu(struct cpu *cpu) | 74 | void unregister_cpu(struct cpu *cpu) |
93 | { | 75 | { |
94 | int logical_cpu = cpu->dev.id; | 76 | int logical_cpu = cpu->dev.id; |
95 | 77 | ||
96 | unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu)); | 78 | unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu)); |
97 | 79 | ||
98 | device_remove_file(&cpu->dev, &dev_attr_online); | ||
99 | |||
100 | device_unregister(&cpu->dev); | 80 | device_unregister(&cpu->dev); |
101 | per_cpu(cpu_sys_devices, logical_cpu) = NULL; | 81 | per_cpu(cpu_sys_devices, logical_cpu) = NULL; |
102 | return; | 82 | return; |
@@ -123,12 +103,19 @@ static DEVICE_ATTR(probe, S_IWUSR, NULL, cpu_probe_store); | |||
123 | static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store); | 103 | static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store); |
124 | #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ | 104 | #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ |
125 | 105 | ||
126 | #else /* ... !CONFIG_HOTPLUG_CPU */ | ||
127 | static inline void register_cpu_control(struct cpu *cpu) | ||
128 | { | ||
129 | } | ||
130 | #endif /* CONFIG_HOTPLUG_CPU */ | 106 | #endif /* CONFIG_HOTPLUG_CPU */ |
131 | 107 | ||
108 | struct bus_type cpu_subsys = { | ||
109 | .name = "cpu", | ||
110 | .dev_name = "cpu", | ||
111 | .match = cpu_subsys_match, | ||
112 | #ifdef CONFIG_HOTPLUG_CPU | ||
113 | .online = cpu_subsys_online, | ||
114 | .offline = cpu_subsys_offline, | ||
115 | #endif | ||
116 | }; | ||
117 | EXPORT_SYMBOL_GPL(cpu_subsys); | ||
118 | |||
132 | #ifdef CONFIG_KEXEC | 119 | #ifdef CONFIG_KEXEC |
133 | #include <linux/kexec.h> | 120 | #include <linux/kexec.h> |
134 | 121 | ||
@@ -277,12 +264,12 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) | |||
277 | cpu->dev.id = num; | 264 | cpu->dev.id = num; |
278 | cpu->dev.bus = &cpu_subsys; | 265 | cpu->dev.bus = &cpu_subsys; |
279 | cpu->dev.release = cpu_device_release; | 266 | cpu->dev.release = cpu_device_release; |
267 | cpu->dev.offline_disabled = !cpu->hotpluggable; | ||
268 | cpu->dev.offline = !cpu_online(num); | ||
280 | #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE | 269 | #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE |
281 | cpu->dev.bus->uevent = arch_cpu_uevent; | 270 | cpu->dev.bus->uevent = arch_cpu_uevent; |
282 | #endif | 271 | #endif |
283 | error = device_register(&cpu->dev); | 272 | error = device_register(&cpu->dev); |
284 | if (!error && cpu->hotpluggable) | ||
285 | register_cpu_control(cpu); | ||
286 | if (!error) | 273 | if (!error) |
287 | per_cpu(cpu_sys_devices, num) = &cpu->dev; | 274 | per_cpu(cpu_sys_devices, num) = &cpu->dev; |
288 | if (!error) | 275 | if (!error) |
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 14f8a6954da0..4ebf97f99fae 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c | |||
@@ -37,9 +37,14 @@ static inline int base_memory_block_id(int section_nr) | |||
37 | return section_nr / sections_per_block; | 37 | return section_nr / sections_per_block; |
38 | } | 38 | } |
39 | 39 | ||
40 | static int memory_subsys_online(struct device *dev); | ||
41 | static int memory_subsys_offline(struct device *dev); | ||
42 | |||
40 | static struct bus_type memory_subsys = { | 43 | static struct bus_type memory_subsys = { |
41 | .name = MEMORY_CLASS_NAME, | 44 | .name = MEMORY_CLASS_NAME, |
42 | .dev_name = MEMORY_CLASS_NAME, | 45 | .dev_name = MEMORY_CLASS_NAME, |
46 | .online = memory_subsys_online, | ||
47 | .offline = memory_subsys_offline, | ||
43 | }; | 48 | }; |
44 | 49 | ||
45 | static BLOCKING_NOTIFIER_HEAD(memory_chain); | 50 | static BLOCKING_NOTIFIER_HEAD(memory_chain); |
@@ -88,6 +93,7 @@ int register_memory(struct memory_block *memory) | |||
88 | memory->dev.bus = &memory_subsys; | 93 | memory->dev.bus = &memory_subsys; |
89 | memory->dev.id = memory->start_section_nr / sections_per_block; | 94 | memory->dev.id = memory->start_section_nr / sections_per_block; |
90 | memory->dev.release = memory_block_release; | 95 | memory->dev.release = memory_block_release; |
96 | memory->dev.offline = memory->state == MEM_OFFLINE; | ||
91 | 97 | ||
92 | error = device_register(&memory->dev); | 98 | error = device_register(&memory->dev); |
93 | return error; | 99 | return error; |
@@ -278,33 +284,64 @@ static int __memory_block_change_state(struct memory_block *mem, | |||
278 | { | 284 | { |
279 | int ret = 0; | 285 | int ret = 0; |
280 | 286 | ||
281 | if (mem->state != from_state_req) { | 287 | if (mem->state != from_state_req) |
282 | ret = -EINVAL; | 288 | return -EINVAL; |
283 | goto out; | ||
284 | } | ||
285 | 289 | ||
286 | if (to_state == MEM_OFFLINE) | 290 | if (to_state == MEM_OFFLINE) |
287 | mem->state = MEM_GOING_OFFLINE; | 291 | mem->state = MEM_GOING_OFFLINE; |
288 | 292 | ||
289 | ret = memory_block_action(mem->start_section_nr, to_state, online_type); | 293 | ret = memory_block_action(mem->start_section_nr, to_state, online_type); |
294 | mem->state = ret ? from_state_req : to_state; | ||
295 | return ret; | ||
296 | } | ||
290 | 297 | ||
291 | if (ret) { | 298 | static int memory_subsys_online(struct device *dev) |
292 | mem->state = from_state_req; | 299 | { |
293 | goto out; | 300 | struct memory_block *mem = container_of(dev, struct memory_block, dev); |
294 | } | 301 | int ret; |
295 | 302 | ||
296 | mem->state = to_state; | 303 | mutex_lock(&mem->state_mutex); |
297 | switch (mem->state) { | 304 | |
298 | case MEM_OFFLINE: | 305 | ret = mem->state == MEM_ONLINE ? 0 : |
299 | kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); | 306 | __memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, |
300 | break; | 307 | ONLINE_KEEP); |
301 | case MEM_ONLINE: | 308 | |
302 | kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); | 309 | mutex_unlock(&mem->state_mutex); |
303 | break; | 310 | return ret; |
304 | default: | 311 | } |
305 | break; | 312 | |
313 | static int memory_subsys_offline(struct device *dev) | ||
314 | { | ||
315 | struct memory_block *mem = container_of(dev, struct memory_block, dev); | ||
316 | int ret; | ||
317 | |||
318 | mutex_lock(&mem->state_mutex); | ||
319 | |||
320 | ret = mem->state == MEM_OFFLINE ? 0 : | ||
321 | __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); | ||
322 | |||
323 | mutex_unlock(&mem->state_mutex); | ||
324 | return ret; | ||
325 | } | ||
326 | |||
327 | static int __memory_block_change_state_uevent(struct memory_block *mem, | ||
328 | unsigned long to_state, unsigned long from_state_req, | ||
329 | int online_type) | ||
330 | { | ||
331 | int ret = __memory_block_change_state(mem, to_state, from_state_req, | ||
332 | online_type); | ||
333 | if (!ret) { | ||
334 | switch (mem->state) { | ||
335 | case MEM_OFFLINE: | ||
336 | kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); | ||
337 | break; | ||
338 | case MEM_ONLINE: | ||
339 | kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); | ||
340 | break; | ||
341 | default: | ||
342 | break; | ||
343 | } | ||
306 | } | 344 | } |
307 | out: | ||
308 | return ret; | 345 | return ret; |
309 | } | 346 | } |
310 | 347 | ||
@@ -315,8 +352,8 @@ static int memory_block_change_state(struct memory_block *mem, | |||
315 | int ret; | 352 | int ret; |
316 | 353 | ||
317 | mutex_lock(&mem->state_mutex); | 354 | mutex_lock(&mem->state_mutex); |
318 | ret = __memory_block_change_state(mem, to_state, from_state_req, | 355 | ret = __memory_block_change_state_uevent(mem, to_state, from_state_req, |
319 | online_type); | 356 | online_type); |
320 | mutex_unlock(&mem->state_mutex); | 357 | mutex_unlock(&mem->state_mutex); |
321 | 358 | ||
322 | return ret; | 359 | return ret; |
@@ -326,22 +363,34 @@ store_mem_state(struct device *dev, | |||
326 | struct device_attribute *attr, const char *buf, size_t count) | 363 | struct device_attribute *attr, const char *buf, size_t count) |
327 | { | 364 | { |
328 | struct memory_block *mem; | 365 | struct memory_block *mem; |
366 | bool offline; | ||
329 | int ret = -EINVAL; | 367 | int ret = -EINVAL; |
330 | 368 | ||
331 | mem = container_of(dev, struct memory_block, dev); | 369 | mem = container_of(dev, struct memory_block, dev); |
332 | 370 | ||
333 | if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) | 371 | lock_device_hotplug(); |
372 | |||
373 | if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) { | ||
374 | offline = false; | ||
334 | ret = memory_block_change_state(mem, MEM_ONLINE, | 375 | ret = memory_block_change_state(mem, MEM_ONLINE, |
335 | MEM_OFFLINE, ONLINE_KERNEL); | 376 | MEM_OFFLINE, ONLINE_KERNEL); |
336 | else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) | 377 | } else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) { |
378 | offline = false; | ||
337 | ret = memory_block_change_state(mem, MEM_ONLINE, | 379 | ret = memory_block_change_state(mem, MEM_ONLINE, |
338 | MEM_OFFLINE, ONLINE_MOVABLE); | 380 | MEM_OFFLINE, ONLINE_MOVABLE); |
339 | else if (!strncmp(buf, "online", min_t(int, count, 6))) | 381 | } else if (!strncmp(buf, "online", min_t(int, count, 6))) { |
382 | offline = false; | ||
340 | ret = memory_block_change_state(mem, MEM_ONLINE, | 383 | ret = memory_block_change_state(mem, MEM_ONLINE, |
341 | MEM_OFFLINE, ONLINE_KEEP); | 384 | MEM_OFFLINE, ONLINE_KEEP); |
342 | else if(!strncmp(buf, "offline", min_t(int, count, 7))) | 385 | } else if(!strncmp(buf, "offline", min_t(int, count, 7))) { |
386 | offline = true; | ||
343 | ret = memory_block_change_state(mem, MEM_OFFLINE, | 387 | ret = memory_block_change_state(mem, MEM_OFFLINE, |
344 | MEM_ONLINE, -1); | 388 | MEM_ONLINE, -1); |
389 | } | ||
390 | if (!ret) | ||
391 | dev->offline = offline; | ||
392 | |||
393 | unlock_device_hotplug(); | ||
345 | 394 | ||
346 | if (ret) | 395 | if (ret) |
347 | return ret; | 396 | return ret; |
@@ -679,21 +728,6 @@ int unregister_memory_section(struct mem_section *section) | |||
679 | } | 728 | } |
680 | #endif /* CONFIG_MEMORY_HOTREMOVE */ | 729 | #endif /* CONFIG_MEMORY_HOTREMOVE */ |
681 | 730 | ||
682 | /* | ||
683 | * offline one memory block. If the memory block has been offlined, do nothing. | ||
684 | */ | ||
685 | int offline_memory_block(struct memory_block *mem) | ||
686 | { | ||
687 | int ret = 0; | ||
688 | |||
689 | mutex_lock(&mem->state_mutex); | ||
690 | if (mem->state != MEM_OFFLINE) | ||
691 | ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); | ||
692 | mutex_unlock(&mem->state_mutex); | ||
693 | |||
694 | return ret; | ||
695 | } | ||
696 | |||
697 | /* return true if the memory block is offlined, otherwise, return false */ | 731 | /* return true if the memory block is offlined, otherwise, return false */ |
698 | bool is_memblock_offlined(struct memory_block *mem) | 732 | bool is_memblock_offlined(struct memory_block *mem) |
699 | { | 733 | { |