aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/memory.c127
-rw-r--r--include/linux/memory.h13
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
264static int __memory_block_change_state(struct memory_block *mem, 263static 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] */
281static int memory_subsys_online(struct device *dev) 283static 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
296static int memory_subsys_offline(struct device *dev) 307static 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
310static 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
331static 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}
344static ssize_t 317static ssize_t
345store_mem_state(struct device *dev, 318store_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 @@
25struct memory_block { 25struct 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 *);