diff options
-rw-r--r-- | drivers/base/memory.c | 127 | ||||
-rw-r--r-- | include/linux/memory.h | 13 |
2 files changed, 58 insertions, 82 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 | ||
diff --git a/include/linux/memory.h b/include/linux/memory.h index 4c89fb0c8f4c..9a6bbf76452d 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h | |||
@@ -25,16 +25,9 @@ | |||
25 | struct memory_block { | 25 | struct memory_block { |
26 | unsigned long start_section_nr; | 26 | unsigned long start_section_nr; |
27 | unsigned long end_section_nr; | 27 | unsigned long end_section_nr; |
28 | unsigned long state; | 28 | unsigned long state; /* serialized by the dev->lock */ |
29 | int section_count; | 29 | int section_count; /* serialized by mem_sysfs_mutex */ |
30 | 30 | int online_type; /* for passing data to online routine */ | |
31 | /* | ||
32 | * This serializes all state change requests. It isn't | ||
33 | * held during creation because the control files are | ||
34 | * created long after the critical areas during | ||
35 | * initialization. | ||
36 | */ | ||
37 | struct mutex state_mutex; | ||
38 | int phys_device; /* to which fru does this belong? */ | 31 | int phys_device; /* to which fru does this belong? */ |
39 | void *hw; /* optional pointer to fw/hw data */ | 32 | void *hw; /* optional pointer to fw/hw data */ |
40 | int (*phys_callback)(struct memory_block *); | 33 | int (*phys_callback)(struct memory_block *); |