diff options
author | Wen Congyang <wency@cn.fujitsu.com> | 2013-02-22 19:32:54 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-23 20:50:12 -0500 |
commit | bbc76be67c2c0c12548937a07ea3643c32a95b8c (patch) | |
tree | a0a7087d26b94a58a7a37a4f6574165dc99f7eb4 /mm/memory_hotplug.c | |
parent | 6677e3eaf4d78abd7b09133414c05dc3ec353e7f (diff) |
memory-hotplug: remove redundant codes
offlining memory blocks and checking whether memory blocks are offlined
are very similar. This patch introduces a new function to remove
redundant codes.
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
Signed-off-by: Tang Chen <tangchen@cn.fujitsu.com>
Reviewed-by: Kamezawa Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Jiang Liu <jiang.liu@huawei.com>
Cc: Jianguo Wu <wujianguo@huawei.com>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
Cc: Wu Jianguo <wujianguo@huawei.com>
Cc: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/memory_hotplug.c')
-rw-r--r-- | mm/memory_hotplug.c | 129 |
1 files changed, 82 insertions, 47 deletions
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 5d350f5c68e5..e5b91b12cec3 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c | |||
@@ -1380,20 +1380,26 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages) | |||
1380 | return __offline_pages(start_pfn, start_pfn + nr_pages, 120 * HZ); | 1380 | return __offline_pages(start_pfn, start_pfn + nr_pages, 120 * HZ); |
1381 | } | 1381 | } |
1382 | 1382 | ||
1383 | int remove_memory(u64 start, u64 size) | 1383 | /** |
1384 | * walk_memory_range - walks through all mem sections in [start_pfn, end_pfn) | ||
1385 | * @start_pfn: start pfn of the memory range | ||
1386 | * @end_pfn: end pft of the memory range | ||
1387 | * @arg: argument passed to func | ||
1388 | * @func: callback for each memory section walked | ||
1389 | * | ||
1390 | * This function walks through all present mem sections in range | ||
1391 | * [start_pfn, end_pfn) and call func on each mem section. | ||
1392 | * | ||
1393 | * Returns the return value of func. | ||
1394 | */ | ||
1395 | static int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, | ||
1396 | void *arg, int (*func)(struct memory_block *, void *)) | ||
1384 | { | 1397 | { |
1385 | struct memory_block *mem = NULL; | 1398 | struct memory_block *mem = NULL; |
1386 | struct mem_section *section; | 1399 | struct mem_section *section; |
1387 | unsigned long start_pfn, end_pfn; | ||
1388 | unsigned long pfn, section_nr; | 1400 | unsigned long pfn, section_nr; |
1389 | int ret; | 1401 | int ret; |
1390 | int return_on_error = 0; | ||
1391 | int retry = 0; | ||
1392 | |||
1393 | start_pfn = PFN_DOWN(start); | ||
1394 | end_pfn = start_pfn + PFN_DOWN(size); | ||
1395 | 1402 | ||
1396 | repeat: | ||
1397 | for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) { | 1403 | for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) { |
1398 | section_nr = pfn_to_section_nr(pfn); | 1404 | section_nr = pfn_to_section_nr(pfn); |
1399 | if (!present_section_nr(section_nr)) | 1405 | if (!present_section_nr(section_nr)) |
@@ -1410,22 +1416,76 @@ repeat: | |||
1410 | if (!mem) | 1416 | if (!mem) |
1411 | continue; | 1417 | continue; |
1412 | 1418 | ||
1413 | ret = offline_memory_block(mem); | 1419 | ret = func(mem, arg); |
1414 | if (ret) { | 1420 | if (ret) { |
1415 | if (return_on_error) { | 1421 | kobject_put(&mem->dev.kobj); |
1416 | kobject_put(&mem->dev.kobj); | 1422 | return ret; |
1417 | return ret; | ||
1418 | } else { | ||
1419 | retry = 1; | ||
1420 | } | ||
1421 | } | 1423 | } |
1422 | } | 1424 | } |
1423 | 1425 | ||
1424 | if (mem) | 1426 | if (mem) |
1425 | kobject_put(&mem->dev.kobj); | 1427 | kobject_put(&mem->dev.kobj); |
1426 | 1428 | ||
1427 | if (retry) { | 1429 | return 0; |
1428 | return_on_error = 1; | 1430 | } |
1431 | |||
1432 | /** | ||
1433 | * offline_memory_block_cb - callback function for offlining memory block | ||
1434 | * @mem: the memory block to be offlined | ||
1435 | * @arg: buffer to hold error msg | ||
1436 | * | ||
1437 | * Always return 0, and put the error msg in arg if any. | ||
1438 | */ | ||
1439 | static int offline_memory_block_cb(struct memory_block *mem, void *arg) | ||
1440 | { | ||
1441 | int *ret = arg; | ||
1442 | int error = offline_memory_block(mem); | ||
1443 | |||
1444 | if (error != 0 && *ret == 0) | ||
1445 | *ret = error; | ||
1446 | |||
1447 | return 0; | ||
1448 | } | ||
1449 | |||
1450 | static int is_memblock_offlined_cb(struct memory_block *mem, void *arg) | ||
1451 | { | ||
1452 | int ret = !is_memblock_offlined(mem); | ||
1453 | |||
1454 | if (unlikely(ret)) | ||
1455 | pr_warn("removing memory fails, because memory " | ||
1456 | "[%#010llx-%#010llx] is onlined\n", | ||
1457 | PFN_PHYS(section_nr_to_pfn(mem->start_section_nr)), | ||
1458 | PFN_PHYS(section_nr_to_pfn(mem->end_section_nr + 1))-1); | ||
1459 | |||
1460 | return ret; | ||
1461 | } | ||
1462 | |||
1463 | int remove_memory(u64 start, u64 size) | ||
1464 | { | ||
1465 | unsigned long start_pfn, end_pfn; | ||
1466 | int ret = 0; | ||
1467 | int retry = 1; | ||
1468 | |||
1469 | start_pfn = PFN_DOWN(start); | ||
1470 | end_pfn = start_pfn + PFN_DOWN(size); | ||
1471 | |||
1472 | /* | ||
1473 | * When CONFIG_MEMCG is on, one memory block may be used by other | ||
1474 | * blocks to store page cgroup when onlining pages. But we don't know | ||
1475 | * in what order pages are onlined. So we iterate twice to offline | ||
1476 | * memory: | ||
1477 | * 1st iterate: offline every non primary memory block. | ||
1478 | * 2nd iterate: offline primary (i.e. first added) memory block. | ||
1479 | */ | ||
1480 | repeat: | ||
1481 | walk_memory_range(start_pfn, end_pfn, &ret, | ||
1482 | offline_memory_block_cb); | ||
1483 | if (ret) { | ||
1484 | if (!retry) | ||
1485 | return ret; | ||
1486 | |||
1487 | retry = 0; | ||
1488 | ret = 0; | ||
1429 | goto repeat; | 1489 | goto repeat; |
1430 | } | 1490 | } |
1431 | 1491 | ||
@@ -1443,38 +1503,13 @@ repeat: | |||
1443 | * memory blocks are offlined. | 1503 | * memory blocks are offlined. |
1444 | */ | 1504 | */ |
1445 | 1505 | ||
1446 | mem = NULL; | 1506 | ret = walk_memory_range(start_pfn, end_pfn, NULL, |
1447 | for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) { | 1507 | is_memblock_offlined_cb); |
1448 | section_nr = pfn_to_section_nr(pfn); | 1508 | if (ret) { |
1449 | if (!present_section_nr(section_nr)) | 1509 | unlock_memory_hotplug(); |
1450 | continue; | 1510 | return ret; |
1451 | |||
1452 | section = __nr_to_section(section_nr); | ||
1453 | /* same memblock? */ | ||
1454 | if (mem) | ||
1455 | if ((section_nr >= mem->start_section_nr) && | ||
1456 | (section_nr <= mem->end_section_nr)) | ||
1457 | continue; | ||
1458 | |||
1459 | mem = find_memory_block_hinted(section, mem); | ||
1460 | if (!mem) | ||
1461 | continue; | ||
1462 | |||
1463 | ret = is_memblock_offlined(mem); | ||
1464 | if (!ret) { | ||
1465 | pr_warn("removing memory fails, because memory " | ||
1466 | "[%#010llx-%#010llx] is onlined\n", | ||
1467 | PFN_PHYS(section_nr_to_pfn(mem->start_section_nr)), | ||
1468 | PFN_PHYS(section_nr_to_pfn(mem->end_section_nr + 1)) - 1); | ||
1469 | |||
1470 | kobject_put(&mem->dev.kobj); | ||
1471 | unlock_memory_hotplug(); | ||
1472 | return ret; | ||
1473 | } | ||
1474 | } | 1511 | } |
1475 | 1512 | ||
1476 | if (mem) | ||
1477 | kobject_put(&mem->dev.kobj); | ||
1478 | unlock_memory_hotplug(); | 1513 | unlock_memory_hotplug(); |
1479 | 1514 | ||
1480 | return 0; | 1515 | return 0; |