diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-05-08 08:18:37 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-05-12 08:14:45 -0400 |
commit | 4960e05e22604ee270a023f968e0e4f9bd0c6fef (patch) | |
tree | abdc39a81c6ab9868bc75f0f50ddedb7fcb10f9b /drivers/base | |
parent | e2ff39400d81233374e780b133496a2296643d7d (diff) |
Driver core: Introduce offline/online callbacks for memory blocks
Introduce .offline() and .online() callbacks for memory_subsys
that will allow the generic device_offline() and device_online()
to be used with device objects representing memory blocks. That,
in turn, allows the ACPI subsystem to use device_offline() to put
removable memory blocks offline, if possible, before removing
memory modules holding them.
The 'online' sysfs attribute of memory block devices will attempt to
put them offline if 0 is written to it and will attempt to apply the
previously used online type when onlining them (i.e. when 1 is
written to it).
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Vasilis Liaskovitis <vasilis.liaskovitis@profitbricks.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Toshi Kani <toshi.kani@hp.com>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/memory.c | 112 |
1 files changed, 87 insertions, 25 deletions
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 14f8a6954da0..c8f3b63fcacd 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,70 @@ 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); |
290 | |||
291 | if (ret) { | 294 | if (ret) { |
292 | mem->state = from_state_req; | 295 | mem->state = from_state_req; |
293 | goto out; | 296 | } else { |
297 | mem->state = to_state; | ||
298 | if (to_state == MEM_ONLINE) | ||
299 | mem->last_online = online_type; | ||
294 | } | 300 | } |
301 | return ret; | ||
302 | } | ||
295 | 303 | ||
296 | mem->state = to_state; | 304 | static int memory_subsys_online(struct device *dev) |
297 | switch (mem->state) { | 305 | { |
298 | case MEM_OFFLINE: | 306 | struct memory_block *mem = container_of(dev, struct memory_block, dev); |
299 | kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); | 307 | int ret; |
300 | break; | 308 | |
301 | case MEM_ONLINE: | 309 | mutex_lock(&mem->state_mutex); |
302 | kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); | 310 | |
303 | break; | 311 | ret = mem->state == MEM_ONLINE ? 0 : |
304 | default: | 312 | __memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, |
305 | break; | 313 | mem->last_online); |
314 | |||
315 | mutex_unlock(&mem->state_mutex); | ||
316 | return ret; | ||
317 | } | ||
318 | |||
319 | static int memory_subsys_offline(struct device *dev) | ||
320 | { | ||
321 | struct memory_block *mem = container_of(dev, struct memory_block, dev); | ||
322 | int ret; | ||
323 | |||
324 | mutex_lock(&mem->state_mutex); | ||
325 | |||
326 | ret = mem->state == MEM_OFFLINE ? 0 : | ||
327 | __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); | ||
328 | |||
329 | mutex_unlock(&mem->state_mutex); | ||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | static int __memory_block_change_state_uevent(struct memory_block *mem, | ||
334 | unsigned long to_state, unsigned long from_state_req, | ||
335 | int online_type) | ||
336 | { | ||
337 | int ret = __memory_block_change_state(mem, to_state, from_state_req, | ||
338 | online_type); | ||
339 | if (!ret) { | ||
340 | switch (mem->state) { | ||
341 | case MEM_OFFLINE: | ||
342 | kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); | ||
343 | break; | ||
344 | case MEM_ONLINE: | ||
345 | kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); | ||
346 | break; | ||
347 | default: | ||
348 | break; | ||
349 | } | ||
306 | } | 350 | } |
307 | out: | ||
308 | return ret; | 351 | return ret; |
309 | } | 352 | } |
310 | 353 | ||
@@ -315,8 +358,8 @@ static int memory_block_change_state(struct memory_block *mem, | |||
315 | int ret; | 358 | int ret; |
316 | 359 | ||
317 | mutex_lock(&mem->state_mutex); | 360 | mutex_lock(&mem->state_mutex); |
318 | ret = __memory_block_change_state(mem, to_state, from_state_req, | 361 | ret = __memory_block_change_state_uevent(mem, to_state, from_state_req, |
319 | online_type); | 362 | online_type); |
320 | mutex_unlock(&mem->state_mutex); | 363 | mutex_unlock(&mem->state_mutex); |
321 | 364 | ||
322 | return ret; | 365 | return ret; |
@@ -326,22 +369,34 @@ store_mem_state(struct device *dev, | |||
326 | struct device_attribute *attr, const char *buf, size_t count) | 369 | struct device_attribute *attr, const char *buf, size_t count) |
327 | { | 370 | { |
328 | struct memory_block *mem; | 371 | struct memory_block *mem; |
372 | bool offline; | ||
329 | int ret = -EINVAL; | 373 | int ret = -EINVAL; |
330 | 374 | ||
331 | mem = container_of(dev, struct memory_block, dev); | 375 | mem = container_of(dev, struct memory_block, dev); |
332 | 376 | ||
333 | if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) | 377 | lock_device_hotplug(); |
378 | |||
379 | if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) { | ||
380 | offline = false; | ||
334 | ret = memory_block_change_state(mem, MEM_ONLINE, | 381 | ret = memory_block_change_state(mem, MEM_ONLINE, |
335 | MEM_OFFLINE, ONLINE_KERNEL); | 382 | MEM_OFFLINE, ONLINE_KERNEL); |
336 | else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) | 383 | } else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) { |
384 | offline = false; | ||
337 | ret = memory_block_change_state(mem, MEM_ONLINE, | 385 | ret = memory_block_change_state(mem, MEM_ONLINE, |
338 | MEM_OFFLINE, ONLINE_MOVABLE); | 386 | MEM_OFFLINE, ONLINE_MOVABLE); |
339 | else if (!strncmp(buf, "online", min_t(int, count, 6))) | 387 | } else if (!strncmp(buf, "online", min_t(int, count, 6))) { |
388 | offline = false; | ||
340 | ret = memory_block_change_state(mem, MEM_ONLINE, | 389 | ret = memory_block_change_state(mem, MEM_ONLINE, |
341 | MEM_OFFLINE, ONLINE_KEEP); | 390 | MEM_OFFLINE, ONLINE_KEEP); |
342 | else if(!strncmp(buf, "offline", min_t(int, count, 7))) | 391 | } else if(!strncmp(buf, "offline", min_t(int, count, 7))) { |
392 | offline = true; | ||
343 | ret = memory_block_change_state(mem, MEM_OFFLINE, | 393 | ret = memory_block_change_state(mem, MEM_OFFLINE, |
344 | MEM_ONLINE, -1); | 394 | MEM_ONLINE, -1); |
395 | } | ||
396 | if (!ret) | ||
397 | dev->offline = offline; | ||
398 | |||
399 | unlock_device_hotplug(); | ||
345 | 400 | ||
346 | if (ret) | 401 | if (ret) |
347 | return ret; | 402 | return ret; |
@@ -563,6 +618,7 @@ static int init_memory_block(struct memory_block **memory, | |||
563 | base_memory_block_id(scn_nr) * sections_per_block; | 618 | base_memory_block_id(scn_nr) * sections_per_block; |
564 | mem->end_section_nr = mem->start_section_nr + sections_per_block - 1; | 619 | mem->end_section_nr = mem->start_section_nr + sections_per_block - 1; |
565 | mem->state = state; | 620 | mem->state = state; |
621 | mem->last_online = ONLINE_KEEP; | ||
566 | mem->section_count++; | 622 | mem->section_count++; |
567 | mutex_init(&mem->state_mutex); | 623 | mutex_init(&mem->state_mutex); |
568 | start_pfn = section_nr_to_pfn(mem->start_section_nr); | 624 | start_pfn = section_nr_to_pfn(mem->start_section_nr); |
@@ -681,14 +737,20 @@ int unregister_memory_section(struct mem_section *section) | |||
681 | 737 | ||
682 | /* | 738 | /* |
683 | * offline one memory block. If the memory block has been offlined, do nothing. | 739 | * offline one memory block. If the memory block has been offlined, do nothing. |
740 | * | ||
741 | * Call under device_hotplug_lock. | ||
684 | */ | 742 | */ |
685 | int offline_memory_block(struct memory_block *mem) | 743 | int offline_memory_block(struct memory_block *mem) |
686 | { | 744 | { |
687 | int ret = 0; | 745 | int ret = 0; |
688 | 746 | ||
689 | mutex_lock(&mem->state_mutex); | 747 | mutex_lock(&mem->state_mutex); |
690 | if (mem->state != MEM_OFFLINE) | 748 | if (mem->state != MEM_OFFLINE) { |
691 | ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); | 749 | ret = __memory_block_change_state_uevent(mem, MEM_OFFLINE, |
750 | MEM_ONLINE, -1); | ||
751 | if (!ret) | ||
752 | mem->dev.offline = true; | ||
753 | } | ||
692 | mutex_unlock(&mem->state_mutex); | 754 | mutex_unlock(&mem->state_mutex); |
693 | 755 | ||
694 | return ret; | 756 | return ret; |