aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/memory.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-05-08 08:18:37 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-05-12 08:14:45 -0400
commit4960e05e22604ee270a023f968e0e4f9bd0c6fef (patch)
treeabdc39a81c6ab9868bc75f0f50ddedb7fcb10f9b /drivers/base/memory.c
parente2ff39400d81233374e780b133496a2296643d7d (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/memory.c')
-rw-r--r--drivers/base/memory.c112
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
40static int memory_subsys_online(struct device *dev);
41static int memory_subsys_offline(struct device *dev);
42
40static struct bus_type memory_subsys = { 43static 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
45static BLOCKING_NOTIFIER_HEAD(memory_chain); 50static 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; 304static 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
319static 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
333static 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 }
307out:
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 */
685int offline_memory_block(struct memory_block *mem) 743int 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;