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 | |||
