From 34da5e6770ac06df770a0355b417155e6e84e263 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 26 Jul 2013 13:10:22 +0900 Subject: driver core: replace strict_strto*() with kstrto*() The usage of strict_strto*() is not preferred, because strict_strto*() is obsolete. Thus, kstrto*() should be used. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 2b7813ec6d0..ddd14ce6654 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -469,7 +469,7 @@ store_soft_offline_page(struct device *dev, u64 pfn; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (strict_strtoull(buf, 0, &pfn) < 0) + if (kstrtoull(buf, 0, &pfn) < 0) return -EINVAL; pfn >>= PAGE_SHIFT; if (!pfn_valid(pfn)) @@ -488,7 +488,7 @@ store_hard_offline_page(struct device *dev, u64 pfn; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (strict_strtoull(buf, 0, &pfn) < 0) + if (kstrtoull(buf, 0, &pfn) < 0) return -EINVAL; pfn >>= PAGE_SHIFT; ret = memory_failure(pfn, 0, 0); -- cgit v1.2.3-70-g09d2 From b1eaef3da5a7bbc48fa9dbefaa2a28e45367fdb1 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 20 Aug 2013 12:12:57 -0500 Subject: drivers: base: move mutex lock out of add_memory_section() There is no point in releasing the mutex for each section that is added during boot time. Just hold it over the entire initialization loop. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index ddd14ce6654..ed3ada244a0 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -613,8 +613,6 @@ static int add_memory_section(int nid, struct mem_section *section, int scn_nr = __section_nr(section); int ret = 0; - mutex_lock(&mem_sysfs_mutex); - if (context == BOOT) { /* same memory block ? */ if (mem_p && *mem_p) @@ -643,7 +641,6 @@ static int add_memory_section(int nid, struct mem_section *section, ret = register_mem_sect_under_node(mem, nid); } - mutex_unlock(&mem_sysfs_mutex); return ret; } @@ -653,7 +650,13 @@ static int add_memory_section(int nid, struct mem_section *section, */ int register_new_memory(int nid, struct mem_section *section) { - return add_memory_section(nid, section, NULL, MEM_OFFLINE, HOTPLUG); + int ret; + + mutex_lock(&mem_sysfs_mutex); + ret = add_memory_section(nid, section, NULL, MEM_OFFLINE, HOTPLUG); + mutex_unlock(&mem_sysfs_mutex); + + return ret; } #ifdef CONFIG_MEMORY_HOTREMOVE @@ -746,6 +749,7 @@ int __init memory_dev_init(void) * Create entries for memory sections that were found * during boot and have been initialized */ + mutex_lock(&mem_sysfs_mutex); for (i = 0; i < NR_MEM_SECTIONS; i++) { if (!present_section_nr(i)) continue; @@ -757,6 +761,7 @@ int __init memory_dev_init(void) if (!ret) ret = err; } + mutex_unlock(&mem_sysfs_mutex); out: if (ret) -- cgit v1.2.3-70-g09d2 From 879f1bec8e136c7bc71e38715e62a73b75f91d78 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 20 Aug 2013 12:12:58 -0500 Subject: drivers: base: remove unneeded variable The error variable is not needed. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index ed3ada244a0..b31d998a63a 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -565,16 +565,13 @@ static const struct attribute_group *memory_memblk_attr_groups[] = { static int register_memory(struct memory_block *memory) { - int error; - memory->dev.bus = &memory_subsys; memory->dev.id = memory->start_section_nr / sections_per_block; memory->dev.release = memory_block_release; memory->dev.groups = memory_memblk_attr_groups; memory->dev.offline = memory->state == MEM_OFFLINE; - error = device_register(&memory->dev); - return error; + return device_register(&memory->dev); } static int init_memory_block(struct memory_block **memory, -- cgit v1.2.3-70-g09d2 From df2b717c667d2cab37d1bbd585e891f10ed2bca4 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 20 Aug 2013 12:12:59 -0500 Subject: drivers: base: use device get/put functions Use the [get|put]_device functions for ref'ing the memory block device rather than the kobject functions which should be hidden away by the device layer. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index b31d998a63a..e97519bc3d0 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -616,14 +616,14 @@ static int add_memory_section(int nid, struct mem_section *section, if (scn_nr >= (*mem_p)->start_section_nr && scn_nr <= (*mem_p)->end_section_nr) { mem = *mem_p; - kobject_get(&mem->dev.kobj); + get_device(&mem->dev); } } else mem = find_memory_block(section); if (mem) { mem->section_count++; - kobject_put(&mem->dev.kobj); + put_device(&mem->dev); } else { ret = init_memory_block(&mem, section, state); /* store memory_block pointer for next loop */ @@ -663,7 +663,7 @@ unregister_memory(struct memory_block *memory) BUG_ON(memory->dev.bus != &memory_subsys); /* drop the ref. we got in remove_memory_block() */ - kobject_put(&memory->dev.kobj); + put_device(&memory->dev); device_unregister(&memory->dev); } @@ -680,7 +680,7 @@ static int remove_memory_block(unsigned long node_id, if (mem->section_count == 0) unregister_memory(mem); else - kobject_put(&mem->dev.kobj); + put_device(&mem->dev); mutex_unlock(&mem_sysfs_mutex); return 0; -- cgit v1.2.3-70-g09d2 From d7f80530ad0a71615f54607128c30d2422bf4c02 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 20 Aug 2013 12:13:00 -0500 Subject: drivers: base: unshare add_memory_section() from hotplug add_memory_section() is currently called from both boot time and run time via hotplug and there is a lot of nastiness to allow for shared code including an enum parameter to convey the calling context to add_memory_section(). This patch is the first step in breaking up the messy code sharing by pulling the hotplug path for add_memory_section() directly into register_new_memory(). Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index e97519bc3d0..2e9a68c6467 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -647,12 +647,25 @@ static int add_memory_section(int nid, struct mem_section *section, */ int register_new_memory(int nid, struct mem_section *section) { - int ret; + int ret = 0; + struct memory_block *mem; mutex_lock(&mem_sysfs_mutex); - ret = add_memory_section(nid, section, NULL, MEM_OFFLINE, HOTPLUG); - mutex_unlock(&mem_sysfs_mutex); + mem = find_memory_block(section); + if (mem) { + mem->section_count++; + put_device(&mem->dev); + } else { + ret = init_memory_block(&mem, section, MEM_OFFLINE); + if (ret) + goto out; + } + + if (mem->section_count == sections_per_block) + ret = register_mem_sect_under_node(mem, nid); +out: + mutex_unlock(&mem_sysfs_mutex); return ret; } -- cgit v1.2.3-70-g09d2 From 37a7bd6255b415afe197489b5cd1f9568a7ae058 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 20 Aug 2013 12:13:01 -0500 Subject: drivers: base: reduce add_memory_section() for boot-time only Now that add_memory_section() is only called from boot time, reduce the logic and remove the enum. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 41 ++++++++++++++--------------------------- include/linux/memory.h | 1 - 2 files changed, 14 insertions(+), 28 deletions(-) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 2e9a68c6467..a9e9c0902cc 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -602,40 +602,29 @@ static int init_memory_block(struct memory_block **memory, return ret; } -static int add_memory_section(int nid, struct mem_section *section, - struct memory_block **mem_p, - unsigned long state, enum mem_add_context context) +static int add_memory_section(struct mem_section *section, + struct memory_block **mem_p) { struct memory_block *mem = NULL; int scn_nr = __section_nr(section); int ret = 0; - if (context == BOOT) { - /* same memory block ? */ - if (mem_p && *mem_p) - if (scn_nr >= (*mem_p)->start_section_nr && - scn_nr <= (*mem_p)->end_section_nr) { - mem = *mem_p; - get_device(&mem->dev); - } - } else - mem = find_memory_block(section); + if (mem_p && *mem_p) { + if (scn_nr >= (*mem_p)->start_section_nr && + scn_nr <= (*mem_p)->end_section_nr) { + mem = *mem_p; + get_device(&mem->dev); + } + } if (mem) { mem->section_count++; put_device(&mem->dev); } else { - ret = init_memory_block(&mem, section, state); + ret = init_memory_block(&mem, section, MEM_ONLINE); /* store memory_block pointer for next loop */ - if (!ret && context == BOOT) - if (mem_p) - *mem_p = mem; - } - - if (!ret) { - if (context == HOTPLUG && - mem->section_count == sections_per_block) - ret = register_mem_sect_under_node(mem, nid); + if (!ret && mem_p) + *mem_p = mem; } return ret; @@ -764,10 +753,8 @@ int __init memory_dev_init(void) if (!present_section_nr(i)) continue; /* don't need to reuse memory_block if only one per block */ - err = add_memory_section(0, __nr_to_section(i), - (sections_per_block == 1) ? NULL : &mem, - MEM_ONLINE, - BOOT); + err = add_memory_section(__nr_to_section(i), + (sections_per_block == 1) ? NULL : &mem); if (!ret) ret = err; } diff --git a/include/linux/memory.h b/include/linux/memory.h index 85c31a8e290..4c89fb0c8f4 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -125,7 +125,6 @@ extern struct memory_block *find_memory_block_hinted(struct mem_section *, struct memory_block *); extern struct memory_block *find_memory_block(struct mem_section *); #define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION< Date: Tue, 20 Aug 2013 12:13:02 -0500 Subject: drivers: base: remove improper get/put in add_memory_section() The path through add_memory_section() when the memory block already exists uses flawed refcounting logic. A get_device() is done on a memory block using a pointer that might not be valid as we dropped our previous reference and didn't obtain a new reference in the proper way. Lets stop pretending and just remove the get/put. The mem_sysfs_mutex, which we hold over the entire init loop now, will prevent the memory blocks from disappearing from under us. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index a9e9c0902cc..9438d541b5c 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -613,14 +613,12 @@ static int add_memory_section(struct mem_section *section, if (scn_nr >= (*mem_p)->start_section_nr && scn_nr <= (*mem_p)->end_section_nr) { mem = *mem_p; - get_device(&mem->dev); } } - if (mem) { + if (mem) mem->section_count++; - put_device(&mem->dev); - } else { + else { ret = init_memory_block(&mem, section, MEM_ONLINE); /* store memory_block pointer for next loop */ if (!ret && mem_p) -- cgit v1.2.3-70-g09d2 From cb5e39b8038be913030a7b01d4396cfa5f9ded7b Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 20 Aug 2013 12:13:03 -0500 Subject: drivers: base: refactor add_memory_section() to add_memory_block() Right now memory_dev_init() maintains the memory block pointer between iterations of add_memory_section(). This is nasty. This patch refactors add_memory_section() to become add_memory_block(). The refactoring pulls the section scanning out of memory_dev_init() and simplifies the signature. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 9438d541b5c..0187fe483d7 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -602,32 +602,31 @@ static int init_memory_block(struct memory_block **memory, return ret; } -static int add_memory_section(struct mem_section *section, - struct memory_block **mem_p) +static int add_memory_block(int base_section_nr) { - struct memory_block *mem = NULL; - int scn_nr = __section_nr(section); - int ret = 0; - - if (mem_p && *mem_p) { - if (scn_nr >= (*mem_p)->start_section_nr && - scn_nr <= (*mem_p)->end_section_nr) { - mem = *mem_p; - } - } + struct memory_block *mem; + int i, ret, section_count = 0, section_nr; - if (mem) - mem->section_count++; - else { - ret = init_memory_block(&mem, section, MEM_ONLINE); - /* store memory_block pointer for next loop */ - if (!ret && mem_p) - *mem_p = mem; + for (i = base_section_nr; + (i < base_section_nr + sections_per_block) && i < NR_MEM_SECTIONS; + i++) { + if (!present_section_nr(i)) + continue; + if (section_count == 0) + section_nr = i; + section_count++; } - return ret; + if (section_count == 0) + return 0; + ret = init_memory_block(&mem, __nr_to_section(section_nr), MEM_ONLINE); + if (ret) + return ret; + mem->section_count = section_count; + return 0; } + /* * need an interface for the VM to add new memory regions, * but without onlining it. @@ -733,7 +732,6 @@ int __init memory_dev_init(void) int ret; int err; unsigned long block_sz; - struct memory_block *mem = NULL; ret = subsys_system_register(&memory_subsys, memory_root_attr_groups); if (ret) @@ -747,12 +745,8 @@ int __init memory_dev_init(void) * during boot and have been initialized */ mutex_lock(&mem_sysfs_mutex); - for (i = 0; i < NR_MEM_SECTIONS; i++) { - if (!present_section_nr(i)) - continue; - /* don't need to reuse memory_block if only one per block */ - err = add_memory_section(__nr_to_section(i), - (sections_per_block == 1) ? NULL : &mem); + for (i = 0; i < NR_MEM_SECTIONS; i += sections_per_block) { + err = add_memory_block(i); if (!ret) ret = err; } -- cgit v1.2.3-70-g09d2 From fa2be40fe7c0aa3b7accbf6dfa9ef0976e191d4c Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 20 Aug 2013 16:05:05 -0500 Subject: 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 Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 127 +++++++++++++++++++++---------------------------- include/linux/memory.h | 13 ++--- 2 files changed, 58 insertions(+), 82 deletions(-) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 0187fe483d7..2a38cd2da2e 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -261,9 +260,8 @@ memory_block_action(unsigned long phys_index, unsigned long action, int online_t return ret; } -static int __memory_block_change_state(struct memory_block *mem, - unsigned long to_state, unsigned long from_state_req, - int online_type) +static int memory_block_change_state(struct memory_block *mem, + unsigned long to_state, unsigned long from_state_req) { int ret = 0; @@ -273,105 +271,91 @@ static int __memory_block_change_state(struct memory_block *mem, if (to_state == MEM_OFFLINE) mem->state = MEM_GOING_OFFLINE; - ret = memory_block_action(mem->start_section_nr, to_state, online_type); + ret = memory_block_action(mem->start_section_nr, to_state, + mem->online_type); + mem->state = ret ? from_state_req : to_state; + return ret; } +/* The device lock serializes operations on memory_subsys_[online|offline] */ static int memory_subsys_online(struct device *dev) { struct memory_block *mem = container_of(dev, struct memory_block, dev); int ret; - mutex_lock(&mem->state_mutex); + if (mem->state == MEM_ONLINE) + return 0; - ret = mem->state == MEM_ONLINE ? 0 : - __memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, - ONLINE_KEEP); + /* + * If we are called from store_mem_state(), online_type will be + * set >= 0 Otherwise we were called from the device online + * attribute and need to set the online_type. + */ + if (mem->online_type < 0) + mem->online_type = ONLINE_KEEP; + + ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); + + /* clear online_type */ + mem->online_type = -1; - mutex_unlock(&mem->state_mutex); return ret; } static int memory_subsys_offline(struct device *dev) { struct memory_block *mem = container_of(dev, struct memory_block, dev); - int ret; - mutex_lock(&mem->state_mutex); - - ret = mem->state == MEM_OFFLINE ? 0 : - __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); - - mutex_unlock(&mem->state_mutex); - return ret; -} + if (mem->state == MEM_OFFLINE) + return 0; -static int __memory_block_change_state_uevent(struct memory_block *mem, - unsigned long to_state, unsigned long from_state_req, - int online_type) -{ - int ret = __memory_block_change_state(mem, to_state, from_state_req, - online_type); - if (!ret) { - switch (mem->state) { - case MEM_OFFLINE: - kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); - break; - case MEM_ONLINE: - kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); - break; - default: - break; - } - } - return ret; + return memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); } -static int memory_block_change_state(struct memory_block *mem, - unsigned long to_state, unsigned long from_state_req, - int online_type) -{ - int ret; - - mutex_lock(&mem->state_mutex); - ret = __memory_block_change_state_uevent(mem, to_state, from_state_req, - online_type); - mutex_unlock(&mem->state_mutex); - - return ret; -} static ssize_t store_mem_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct memory_block *mem; - bool offline; - int ret = -EINVAL; + int ret, online_type; mem = container_of(dev, struct memory_block, dev); lock_device_hotplug(); - if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) { - offline = false; - ret = memory_block_change_state(mem, MEM_ONLINE, - MEM_OFFLINE, ONLINE_KERNEL); - } else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) { - offline = false; - ret = memory_block_change_state(mem, MEM_ONLINE, - MEM_OFFLINE, ONLINE_MOVABLE); - } else if (!strncmp(buf, "online", min_t(int, count, 6))) { - offline = false; - ret = memory_block_change_state(mem, MEM_ONLINE, - MEM_OFFLINE, ONLINE_KEEP); - } else if(!strncmp(buf, "offline", min_t(int, count, 7))) { - offline = true; - ret = memory_block_change_state(mem, MEM_OFFLINE, - MEM_ONLINE, -1); + if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) + online_type = ONLINE_KERNEL; + else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) + online_type = ONLINE_MOVABLE; + else if (!strncmp(buf, "online", min_t(int, count, 6))) + online_type = ONLINE_KEEP; + else if (!strncmp(buf, "offline", min_t(int, count, 7))) + online_type = -1; + else + return -EINVAL; + + switch (online_type) { + case ONLINE_KERNEL: + case ONLINE_MOVABLE: + case ONLINE_KEEP: + /* + * mem->online_type is not protected so there can be a + * race here. However, when racing online, the first + * will succeed and the second will just return as the + * block will already be online. The online type + * could be either one, but that is expected. + */ + mem->online_type = online_type; + ret = device_online(&mem->dev); + break; + case -1: + ret = device_offline(&mem->dev); + break; + default: + ret = -EINVAL; /* should never happen */ } - if (!ret) - dev->offline = offline; unlock_device_hotplug(); @@ -592,7 +576,6 @@ static int init_memory_block(struct memory_block **memory, mem->end_section_nr = mem->start_section_nr + sections_per_block - 1; mem->state = state; mem->section_count++; - mutex_init(&mem->state_mutex); start_pfn = section_nr_to_pfn(mem->start_section_nr); mem->phys_device = arch_get_memory_phys_device(start_pfn); diff --git a/include/linux/memory.h b/include/linux/memory.h index 4c89fb0c8f4..9a6bbf76452 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -25,16 +25,9 @@ struct memory_block { unsigned long start_section_nr; unsigned long end_section_nr; - unsigned long state; - int section_count; - - /* - * This serializes all state change requests. It isn't - * held during creation because the control files are - * created long after the critical areas during - * initialization. - */ - struct mutex state_mutex; + unsigned long state; /* serialized by the dev->lock */ + int section_count; /* serialized by mem_sysfs_mutex */ + int online_type; /* for passing data to online routine */ int phys_device; /* to which fru does this belong? */ void *hw; /* optional pointer to fw/hw data */ int (*phys_callback)(struct memory_block *); -- cgit v1.2.3-70-g09d2 From 7315f0ccfc283ae998ca4d8102de83bba21936fa Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Wed, 28 Aug 2013 14:38:27 +0800 Subject: drivers/base/memory.c: introduce help macro to_memory_block Introduce help macro to_memory_block to hide the conversion(device-->memory_block), just clean up. Reviewed-by: Yasuaki Ishimatsu Signed-off-by: Gu Zheng Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 2a38cd2da2e..69e09a1b62a 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -29,6 +29,8 @@ static DEFINE_MUTEX(mem_sysfs_mutex); #define MEMORY_CLASS_NAME "memory" +#define to_memory_block(dev) container_of(dev, struct memory_block, dev) + static int sections_per_block; static inline int base_memory_block_id(int section_nr) @@ -76,7 +78,7 @@ EXPORT_SYMBOL(unregister_memory_isolate_notifier); static void memory_block_release(struct device *dev) { - struct memory_block *mem = container_of(dev, struct memory_block, dev); + struct memory_block *mem = to_memory_block(dev); kfree(mem); } @@ -109,8 +111,7 @@ static unsigned long get_memory_block_size(void) static ssize_t show_mem_start_phys_index(struct device *dev, struct device_attribute *attr, char *buf) { - struct memory_block *mem = - container_of(dev, struct memory_block, dev); + struct memory_block *mem = to_memory_block(dev); unsigned long phys_index; phys_index = mem->start_section_nr / sections_per_block; @@ -120,8 +121,7 @@ static ssize_t show_mem_start_phys_index(struct device *dev, static ssize_t show_mem_end_phys_index(struct device *dev, struct device_attribute *attr, char *buf) { - struct memory_block *mem = - container_of(dev, struct memory_block, dev); + struct memory_block *mem = to_memory_block(dev); unsigned long phys_index; phys_index = mem->end_section_nr / sections_per_block; @@ -136,8 +136,7 @@ static ssize_t show_mem_removable(struct device *dev, { unsigned long i, pfn; int ret = 1; - struct memory_block *mem = - container_of(dev, struct memory_block, dev); + struct memory_block *mem = to_memory_block(dev); for (i = 0; i < sections_per_block; i++) { pfn = section_nr_to_pfn(mem->start_section_nr + i); @@ -153,8 +152,7 @@ static ssize_t show_mem_removable(struct device *dev, static ssize_t show_mem_state(struct device *dev, struct device_attribute *attr, char *buf) { - struct memory_block *mem = - container_of(dev, struct memory_block, dev); + struct memory_block *mem = to_memory_block(dev); ssize_t len = 0; /* @@ -282,7 +280,7 @@ static int memory_block_change_state(struct memory_block *mem, /* The device lock serializes operations on memory_subsys_[online|offline] */ static int memory_subsys_online(struct device *dev) { - struct memory_block *mem = container_of(dev, struct memory_block, dev); + struct memory_block *mem = to_memory_block(dev); int ret; if (mem->state == MEM_ONLINE) @@ -306,7 +304,7 @@ static int memory_subsys_online(struct device *dev) static int memory_subsys_offline(struct device *dev) { - struct memory_block *mem = container_of(dev, struct memory_block, dev); + struct memory_block *mem = to_memory_block(dev); if (mem->state == MEM_OFFLINE) return 0; @@ -318,11 +316,9 @@ static ssize_t store_mem_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct memory_block *mem; + struct memory_block *mem = to_memory_block(dev); int ret, online_type; - mem = container_of(dev, struct memory_block, dev); - lock_device_hotplug(); if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) @@ -376,8 +372,7 @@ store_mem_state(struct device *dev, static ssize_t show_phys_device(struct device *dev, struct device_attribute *attr, char *buf) { - struct memory_block *mem = - container_of(dev, struct memory_block, dev); + struct memory_block *mem = to_memory_block(dev); return sprintf(buf, "%d\n", mem->phys_device); } @@ -509,7 +504,7 @@ struct memory_block *find_memory_block_hinted(struct mem_section *section, put_device(&hint->dev); if (!dev) return NULL; - return container_of(dev, struct memory_block, dev); + return to_memory_block(dev); } /* -- cgit v1.2.3-70-g09d2 From 21ea9f5ace3a7317cc3ba1fbc749758021a83136 Mon Sep 17 00:00:00 2001 From: Russ Anderson Date: Wed, 28 Aug 2013 16:35:18 -0700 Subject: drivers/base/memory.c: fix show_mem_removable() to handle missing sections "cat /sys/devices/system/memory/memory*/removable" crashed the system. The problem is that show_mem_removable() is passing a bad pfn to is_mem_section_removable(), which causes if (!node_online(page_to_nid(page))) to blow up. Why is it passing in a bad pfn? The reason is that show_mem_removable() will loop sections_per_block times. sections_per_block is 16, but mem->section_count is 8, indicating holes in this memory block. Checking that the memory section is present before checking to see if the memory section is removable fixes the problem. harp5-sys:~ # cat /sys/devices/system/memory/memory*/removable 0 1 1 1 1 1 1 1 1 1 1 1 1 1 BUG: unable to handle kernel paging request at ffffea00c3200000 IP: [] is_pageblock_removable_nolock+0x1/0x90 PGD 83ffd4067 PUD 37bdfce067 PMD 0 Oops: 0000 [#1] SMP Modules linked in: autofs4 binfmt_misc rdma_ucm rdma_cm iw_cm ib_addr ib_srp scsi_transport_srp scsi_tgt ib_ipoib ib_cm ib_uverbs ib_umad iw_cxgb3 cxgb3 mdio mlx4_en mlx4_ib ib_sa mlx4_core ib_mthca ib_mad ib_core fuse nls_iso8859_1 nls_cp437 vfat fat joydev loop hid_generic usbhid hid hwperf(O) numatools(O) dm_mod iTCO_wdt ipv6 iTCO_vendor_support igb i2c_i801 ioatdma i2c_algo_bit ehci_pci pcspkr lpc_ich i2c_core ehci_hcd ptp sg mfd_core dca rtc_cmos pps_core mperf button xhci_hcd sd_mod crc_t10dif usbcore usb_common scsi_dh_emc scsi_dh_hp_sw scsi_dh_alua scsi_dh_rdac scsi_dh gru(O) xvma(O) xfs crc32c libcrc32c thermal sata_nv processor piix mptsas mptscsih scsi_transport_sas mptbase megaraid_sas fan thermal_sys hwmon ext3 jbd ata_piix ahci libahci libata scsi_mod CPU: 4 PID: 5991 Comm: cat Tainted: G O 3.11.0-rc5-rja-uv+ #10 Hardware name: SGI UV2000/ROMLEY, BIOS SGI UV 2000/3000 series BIOS 01/15/2013 task: ffff88081f034580 ti: ffff880820022000 task.ti: ffff880820022000 RIP: 0010:[] [] is_pageblock_removable_nolock+0x1/0x90 RSP: 0018:ffff880820023df8 EFLAGS: 00010287 RAX: 0000000000040000 RBX: ffffea00c3200000 RCX: 0000000000000004 RDX: ffffea00c30b0000 RSI: 00000000001c0000 RDI: ffffea00c3200000 RBP: ffff880820023e38 R08: 0000000000000000 R09: 0000000000000001 R10: 0000000000000000 R11: 0000000000000001 R12: ffffea00c33c0000 R13: 0000160000000000 R14: 6db6db6db6db6db7 R15: 0000000000000001 FS: 00007ffff7fb2700(0000) GS:ffff88083fc80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffea00c3200000 CR3: 000000081b954000 CR4: 00000000000407e0 Call Trace: show_mem_removable+0x41/0x70 dev_attr_show+0x2a/0x60 sysfs_read_file+0xf7/0x1c0 vfs_read+0xc8/0x130 SyS_read+0x5d/0xa0 system_call_fastpath+0x16/0x1b Signed-off-by: Russ Anderson Cc: Greg Kroah-Hartman Cc: "Rafael J. Wysocki" Cc: Yinghai Lu Reviewed-by: Yasuaki Ishimatsu Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/memory.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 2b7813ec6d0..ec386ee9cb2 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -141,6 +141,8 @@ static ssize_t show_mem_removable(struct device *dev, container_of(dev, struct memory_block, dev); for (i = 0; i < sections_per_block; i++) { + if (!present_section_nr(mem->start_section_nr + i)) + continue; pfn = section_nr_to_pfn(mem->start_section_nr + i); ret &= is_mem_section_removable(pfn, PAGES_PER_SECTION); } -- cgit v1.2.3-70-g09d2 From 5e33bc4165f3edd558d9633002465a95230effc1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 28 Aug 2013 21:41:01 +0200 Subject: driver core / ACPI: Avoid device hot remove locking issues device_hotplug_lock is held around the acpi_bus_trim() call in acpi_scan_hot_remove() which generally removes devices (it removes ACPI device objects at least, but it may also remove "physical" device objects through .detach() callbacks of ACPI scan handlers). Thus, potentially, device sysfs attributes are removed under that lock and to remove those attributes it is necessary to hold the s_active references of their directory entries for writing. On the other hand, the execution of a .show() or .store() callback from a sysfs attribute is carried out with that attribute's s_active reference held for reading. Consequently, if any device sysfs attribute that may be removed from within acpi_scan_hot_remove() through acpi_bus_trim() has a .store() or .show() callback which acquires device_hotplug_lock, the execution of that callback may deadlock with the removal of the attribute. [Unfortunately, the "online" device attribute of CPUs and memory blocks is one of them.] To avoid such deadlocks, make all of the sysfs attribute callbacks that need to lock device hotplug, for example store_online(), use a special function, lock_device_hotplug_sysfs(), to lock device hotplug and return the result of that function immediately if it is not zero. This will cause the s_active reference of the directory entry in question to be released and the syscall to be restarted if device_hotplug_lock cannot be acquired. [show_online() actually doesn't need to lock device hotplug, but it is useful to serialize it with respect to device_offline() and device_online() for the same device (in case user space attempts to run them concurrently) which can be done with the help of device_lock().] Reported-by: Yasuaki Ishimatsu Reported-and-tested-by: Gu Zheng Suggested-by: Tejun Heo Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Acked-by: Toshi Kani --- drivers/base/core.c | 43 ++++++++++++++++++++++++++++--------------- drivers/base/memory.c | 4 +++- include/linux/device.h | 1 + 3 files changed, 32 insertions(+), 16 deletions(-) (limited to 'drivers/base/memory.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 8856d74545d..ac419a15fcd 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -49,6 +49,28 @@ static struct kobject *dev_kobj; struct kobject *sysfs_dev_char_kobj; struct kobject *sysfs_dev_block_kobj; +static DEFINE_MUTEX(device_hotplug_lock); + +void lock_device_hotplug(void) +{ + mutex_lock(&device_hotplug_lock); +} + +void unlock_device_hotplug(void) +{ + mutex_unlock(&device_hotplug_lock); +} + +int lock_device_hotplug_sysfs(void) +{ + if (mutex_trylock(&device_hotplug_lock)) + return 0; + + /* Avoid busy looping (5 ms of sleep should do). */ + msleep(5); + return restart_syscall(); +} + #ifdef CONFIG_BLOCK static inline int device_is_not_partition(struct device *dev) { @@ -408,9 +430,9 @@ static ssize_t show_online(struct device *dev, struct device_attribute *attr, { bool val; - lock_device_hotplug(); + device_lock(dev); val = !dev->offline; - unlock_device_hotplug(); + device_unlock(dev); return sprintf(buf, "%u\n", val); } @@ -424,7 +446,10 @@ static ssize_t store_online(struct device *dev, struct device_attribute *attr, if (ret < 0) return ret; - lock_device_hotplug(); + ret = lock_device_hotplug_sysfs(); + if (ret) + return ret; + ret = val ? device_online(dev) : device_offline(dev); unlock_device_hotplug(); return ret < 0 ? ret : count; @@ -1479,18 +1504,6 @@ EXPORT_SYMBOL_GPL(put_device); EXPORT_SYMBOL_GPL(device_create_file); EXPORT_SYMBOL_GPL(device_remove_file); -static DEFINE_MUTEX(device_hotplug_lock); - -void lock_device_hotplug(void) -{ - mutex_lock(&device_hotplug_lock); -} - -void unlock_device_hotplug(void) -{ - mutex_unlock(&device_hotplug_lock); -} - static int device_check_offline(struct device *dev, void *not_used) { int ret; diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 2b7813ec6d0..6f4c99ff1ce 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -351,7 +351,9 @@ store_mem_state(struct device *dev, mem = container_of(dev, struct memory_block, dev); - lock_device_hotplug(); + ret = lock_device_hotplug_sysfs(); + if (ret) + return ret; if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) { offline = false; diff --git a/include/linux/device.h b/include/linux/device.h index 22b546a5859..545a0428512 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -895,6 +895,7 @@ static inline bool device_supports_offline(struct device *dev) extern void lock_device_hotplug(void); extern void unlock_device_hotplug(void); +extern int lock_device_hotplug_sysfs(void); extern int device_offline(struct device *dev); extern int device_online(struct device *dev); /* -- cgit v1.2.3-70-g09d2