diff options
Diffstat (limited to 'drivers/base/memory.c')
-rw-r--r-- | drivers/base/memory.c | 21 |
1 files changed, 13 insertions, 8 deletions
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index af9c911cd6b5..2804aed3f416 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c | |||
@@ -219,6 +219,7 @@ static bool pages_correctly_reserved(unsigned long start_pfn) | |||
219 | /* | 219 | /* |
220 | * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is | 220 | * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is |
221 | * OK to have direct references to sparsemem variables in here. | 221 | * OK to have direct references to sparsemem variables in here. |
222 | * Must already be protected by mem_hotplug_begin(). | ||
222 | */ | 223 | */ |
223 | static int | 224 | static int |
224 | memory_block_action(unsigned long phys_index, unsigned long action, int online_type) | 225 | memory_block_action(unsigned long phys_index, unsigned long action, int online_type) |
@@ -228,7 +229,7 @@ memory_block_action(unsigned long phys_index, unsigned long action, int online_t | |||
228 | struct page *first_page; | 229 | struct page *first_page; |
229 | int ret; | 230 | int ret; |
230 | 231 | ||
231 | start_pfn = phys_index << PFN_SECTION_SHIFT; | 232 | start_pfn = section_nr_to_pfn(phys_index); |
232 | first_page = pfn_to_page(start_pfn); | 233 | first_page = pfn_to_page(start_pfn); |
233 | 234 | ||
234 | switch (action) { | 235 | switch (action) { |
@@ -286,6 +287,7 @@ static int memory_subsys_online(struct device *dev) | |||
286 | if (mem->online_type < 0) | 287 | if (mem->online_type < 0) |
287 | mem->online_type = MMOP_ONLINE_KEEP; | 288 | mem->online_type = MMOP_ONLINE_KEEP; |
288 | 289 | ||
290 | /* Already under protection of mem_hotplug_begin() */ | ||
289 | ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); | 291 | ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); |
290 | 292 | ||
291 | /* clear online_type */ | 293 | /* clear online_type */ |
@@ -328,17 +330,19 @@ store_mem_state(struct device *dev, | |||
328 | goto err; | 330 | goto err; |
329 | } | 331 | } |
330 | 332 | ||
333 | /* | ||
334 | * Memory hotplug needs to hold mem_hotplug_begin() for probe to find | ||
335 | * the correct memory block to online before doing device_online(dev), | ||
336 | * which will take dev->mutex. Take the lock early to prevent an | ||
337 | * inversion, memory_subsys_online() callbacks will be implemented by | ||
338 | * assuming it's already protected. | ||
339 | */ | ||
340 | mem_hotplug_begin(); | ||
341 | |||
331 | switch (online_type) { | 342 | switch (online_type) { |
332 | case MMOP_ONLINE_KERNEL: | 343 | case MMOP_ONLINE_KERNEL: |
333 | case MMOP_ONLINE_MOVABLE: | 344 | case MMOP_ONLINE_MOVABLE: |
334 | case MMOP_ONLINE_KEEP: | 345 | case MMOP_ONLINE_KEEP: |
335 | /* | ||
336 | * mem->online_type is not protected so there can be a | ||
337 | * race here. However, when racing online, the first | ||
338 | * will succeed and the second will just return as the | ||
339 | * block will already be online. The online type | ||
340 | * could be either one, but that is expected. | ||
341 | */ | ||
342 | mem->online_type = online_type; | 346 | mem->online_type = online_type; |
343 | ret = device_online(&mem->dev); | 347 | ret = device_online(&mem->dev); |
344 | break; | 348 | break; |
@@ -349,6 +353,7 @@ store_mem_state(struct device *dev, | |||
349 | ret = -EINVAL; /* should never happen */ | 353 | ret = -EINVAL; /* should never happen */ |
350 | } | 354 | } |
351 | 355 | ||
356 | mem_hotplug_done(); | ||
352 | err: | 357 | err: |
353 | unlock_device_hotplug(); | 358 | unlock_device_hotplug(); |
354 | 359 | ||