diff options
| author | Seth Jennings <sjenning@linux.vnet.ibm.com> | 2013-08-20 17:05:05 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-21 14:52:20 -0400 |
| commit | fa2be40fe7c0aa3b7accbf6dfa9ef0976e191d4c (patch) | |
| tree | a51ae32d86315a68029a65fe028a2ade240050df /drivers/base | |
| parent | cb5e39b8038be913030a7b01d4396cfa5f9ded7b (diff) | |
drivers: base: use standard device online/offline for state change
There are two ways to set the online/offline state for a memory block:
echo 0|1 > online and echo online|online_kernel|online_movable|offline >
state.
The state attribute can online a memory block with extra data, the
"online type", where the online attribute uses a default online type of
ONLINE_KEEP, same as echo online > state.
Currently there is a state_mutex that provides consistency between the
memory block state and the underlying memory.
The problem is that this code does a lot of things that the common
device layer can do for us, such as the serialization of the
online/offline handlers using the device lock, setting the dev->offline
field, and calling kobject_uevent().
This patch refactors the online/offline code to allow the common
device_[online|offline] functions to be used. The result is a simpler
and more common code path for the two state setting mechanisms. It also
removes the state_mutex from the struct memory_block as the memory block
device lock provides the state consistency.
No functional change is intended by this patch.
Signed-off-by: Seth Jennings <sjenning@linux.vnet.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base')
| -rw-r--r-- | drivers/base/memory.c | 127 |
1 files changed, 55 insertions, 72 deletions
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 0187fe483d7c..2a38cd2da2e5 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | #include <linux/capability.h> | 16 | #include <linux/capability.h> |
| 17 | #include <linux/device.h> | 17 | #include <linux/device.h> |
| 18 | #include <linux/memory.h> | 18 | #include <linux/memory.h> |
| 19 | #include <linux/kobject.h> | ||
| 20 | #include <linux/memory_hotplug.h> | 19 | #include <linux/memory_hotplug.h> |
| 21 | #include <linux/mm.h> | 20 | #include <linux/mm.h> |
| 22 | #include <linux/mutex.h> | 21 | #include <linux/mutex.h> |
| @@ -261,9 +260,8 @@ memory_block_action(unsigned long phys_index, unsigned long action, int online_t | |||
| 261 | return ret; | 260 | return ret; |
| 262 | } | 261 | } |
| 263 | 262 | ||
| 264 | static int __memory_block_change_state(struct memory_block *mem, | 263 | static int memory_block_change_state(struct memory_block *mem, |
| 265 | unsigned long to_state, unsigned long from_state_req, | 264 | unsigned long to_state, unsigned long from_state_req) |
| 266 | int online_type) | ||
| 267 | { | 265 | { |
| 268 | int ret = 0; | 266 | int ret = 0; |
| 269 | 267 | ||
| @@ -273,105 +271,91 @@ static int __memory_block_change_state(struct memory_block *mem, | |||
| 273 | if (to_state == MEM_OFFLINE) | 271 | if (to_state == MEM_OFFLINE) |
| 274 | mem->state = MEM_GOING_OFFLINE; | 272 | mem->state = MEM_GOING_OFFLINE; |
| 275 | 273 | ||
| 276 | ret = memory_block_action(mem->start_section_nr, to_state, online_type); | 274 | ret = memory_block_action(mem->start_section_nr, to_state, |
| 275 | mem->online_type); | ||
| 276 | |||
| 277 | mem->state = ret ? from_state_req : to_state; | 277 | mem->state = ret ? from_state_req : to_state; |
| 278 | |||
| 278 | return ret; | 279 | return ret; |
| 279 | } | 280 | } |
| 280 | 281 | ||
| 282 | /* The device lock serializes operations on memory_subsys_[online|offline] */ | ||
| 281 | static int memory_subsys_online(struct device *dev) | 283 | static int memory_subsys_online(struct device *dev) |
| 282 | { | 284 | { |
| 283 | struct memory_block *mem = container_of(dev, struct memory_block, dev); | 285 | struct memory_block *mem = container_of(dev, struct memory_block, dev); |
| 284 | int ret; | 286 | int ret; |
| 285 | 287 | ||
| 286 | mutex_lock(&mem->state_mutex); | 288 | if (mem->state == MEM_ONLINE) |
| 289 | return 0; | ||
| 287 | 290 | ||
| 288 | ret = mem->state == MEM_ONLINE ? 0 : | 291 | /* |
| 289 | __memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, | 292 | * If we are called from store_mem_state(), online_type will be |
| 290 | ONLINE_KEEP); | 293 | * set >= 0 Otherwise we were called from the device online |
| 294 | * attribute and need to set the online_type. | ||
| 295 | */ | ||
| 296 | if (mem->online_type < 0) | ||
| 297 | mem->online_type = ONLINE_KEEP; | ||
| 298 | |||
| 299 | ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); | ||
| 300 | |||
| 301 | /* clear online_type */ | ||
| 302 | mem->online_type = -1; | ||
| 291 | 303 | ||
| 292 | mutex_unlock(&mem->state_mutex); | ||
| 293 | return ret; | 304 | return ret; |
| 294 | } | 305 | } |
| 295 | 306 | ||
| 296 | static int memory_subsys_offline(struct device *dev) | 307 | static int memory_subsys_offline(struct device *dev) |
| 297 | { | 308 | { |
| 298 | struct memory_block *mem = container_of(dev, struct memory_block, dev); | 309 | struct memory_block *mem = container_of(dev, struct memory_block, dev); |
| 299 | int ret; | ||
| 300 | 310 | ||
| 301 | mutex_lock(&mem->state_mutex); | 311 | if (mem->state == MEM_OFFLINE) |
| 302 | 312 | return 0; | |
| 303 | ret = mem->state == MEM_OFFLINE ? 0 : | ||
| 304 | __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); | ||
| 305 | |||
| 306 | mutex_unlock(&mem->state_mutex); | ||
| 307 | return ret; | ||
| 308 | } | ||
| 309 | 313 | ||
| 310 | static int __memory_block_change_state_uevent(struct memory_block *mem, | 314 | return memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); |
| 311 | unsigned long to_state, unsigned long from_state_req, | ||
| 312 | int online_type) | ||
| 313 | { | ||
| 314 | int ret = __memory_block_change_state(mem, to_state, from_state_req, | ||
| 315 | online_type); | ||
| 316 | if (!ret) { | ||
| 317 | switch (mem->state) { | ||
| 318 | case MEM_OFFLINE: | ||
| 319 | kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); | ||
| 320 | break; | ||
| 321 | case MEM_ONLINE: | ||
| 322 | kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); | ||
| 323 | break; | ||
| 324 | default: | ||
| 325 | break; | ||
| 326 | } | ||
| 327 | } | ||
| 328 | return ret; | ||
| 329 | } | 315 | } |
| 330 | 316 | ||
| 331 | static int memory_block_change_state(struct memory_block *mem, | ||
| 332 | unsigned long to_state, unsigned long from_state_req, | ||
| 333 | int online_type) | ||
| 334 | { | ||
| 335 | int ret; | ||
| 336 | |||
| 337 | mutex_lock(&mem->state_mutex); | ||
| 338 | ret = __memory_block_change_state_uevent(mem, to_state, from_state_req, | ||
| 339 | online_type); | ||
| 340 | mutex_unlock(&mem->state_mutex); | ||
| 341 | |||
| 342 | return ret; | ||
| 343 | } | ||
| 344 | static ssize_t | 317 | static ssize_t |
| 345 | store_mem_state(struct device *dev, | 318 | store_mem_state(struct device *dev, |
| 346 | struct device_attribute *attr, const char *buf, size_t count) | 319 | struct device_attribute *attr, const char *buf, size_t count) |
| 347 | { | 320 | { |
| 348 | struct memory_block *mem; | 321 | struct memory_block *mem; |
| 349 | bool offline; | 322 | int ret, online_type; |
| 350 | int ret = -EINVAL; | ||
| 351 | 323 | ||
| 352 | mem = container_of(dev, struct memory_block, dev); | 324 | mem = container_of(dev, struct memory_block, dev); |
| 353 | 325 | ||
| 354 | lock_device_hotplug(); | 326 | lock_device_hotplug(); |
| 355 | 327 | ||
| 356 | if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) { | 328 | if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) |
| 357 | offline = false; | 329 | online_type = ONLINE_KERNEL; |
| 358 | ret = memory_block_change_state(mem, MEM_ONLINE, | 330 | else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) |
| 359 | MEM_OFFLINE, ONLINE_KERNEL); | 331 | online_type = ONLINE_MOVABLE; |
| 360 | } else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) { | 332 | else if (!strncmp(buf, "online", min_t(int, count, 6))) |
| 361 | offline = false; | 333 | online_type = ONLINE_KEEP; |
| 362 | ret = memory_block_change_state(mem, MEM_ONLINE, | 334 | else if (!strncmp(buf, "offline", min_t(int, count, 7))) |
| 363 | MEM_OFFLINE, ONLINE_MOVABLE); | 335 | online_type = -1; |
| 364 | } else if (!strncmp(buf, "online", min_t(int, count, 6))) { | 336 | else |
| 365 | offline = false; | 337 | return -EINVAL; |
| 366 | ret = memory_block_change_state(mem, MEM_ONLINE, | 338 | |
| 367 | MEM_OFFLINE, ONLINE_KEEP); | 339 | switch (online_type) { |
| 368 | } else if(!strncmp(buf, "offline", min_t(int, count, 7))) { | 340 | case ONLINE_KERNEL: |
| 369 | offline = true; | 341 | case ONLINE_MOVABLE: |
| 370 | ret = memory_block_change_state(mem, MEM_OFFLINE, | 342 | case ONLINE_KEEP: |
| 371 | MEM_ONLINE, -1); | 343 | /* |
| 344 | * mem->online_type is not protected so there can be a | ||
| 345 | * race here. However, when racing online, the first | ||
| 346 | * will succeed and the second will just return as the | ||
| 347 | * block will already be online. The online type | ||
| 348 | * could be either one, but that is expected. | ||
| 349 | */ | ||
| 350 | mem->online_type = online_type; | ||
| 351 | ret = device_online(&mem->dev); | ||
| 352 | break; | ||
| 353 | case -1: | ||
| 354 | ret = device_offline(&mem->dev); | ||
| 355 | break; | ||
| 356 | default: | ||
| 357 | ret = -EINVAL; /* should never happen */ | ||
| 372 | } | 358 | } |
| 373 | if (!ret) | ||
| 374 | dev->offline = offline; | ||
| 375 | 359 | ||
| 376 | unlock_device_hotplug(); | 360 | unlock_device_hotplug(); |
| 377 | 361 | ||
| @@ -592,7 +576,6 @@ static int init_memory_block(struct memory_block **memory, | |||
| 592 | mem->end_section_nr = mem->start_section_nr + sections_per_block - 1; | 576 | mem->end_section_nr = mem->start_section_nr + sections_per_block - 1; |
| 593 | mem->state = state; | 577 | mem->state = state; |
| 594 | mem->section_count++; | 578 | mem->section_count++; |
| 595 | mutex_init(&mem->state_mutex); | ||
| 596 | start_pfn = section_nr_to_pfn(mem->start_section_nr); | 579 | start_pfn = section_nr_to_pfn(mem->start_section_nr); |
| 597 | mem->phys_device = arch_get_memory_phys_device(start_pfn); | 580 | mem->phys_device = arch_get_memory_phys_device(start_pfn); |
| 598 | 581 | ||
