|
21 | 21 | #include <linux/mm.h>
|
22 | 22 | #include <linux/stat.h>
|
23 | 23 | #include <linux/slab.h>
|
| 24 | +#include <linux/xarray.h> |
24 | 25 |
|
25 | 26 | #include <linux/atomic.h>
|
26 | 27 | #include <linux/uaccess.h>
|
@@ -74,6 +75,13 @@ static struct bus_type memory_subsys = {
|
74 | 75 | .offline = memory_subsys_offline,
|
75 | 76 | };
|
76 | 77 |
|
| 78 | +/* |
| 79 | + * Memory blocks are cached in a local radix tree to avoid |
| 80 | + * a costly linear search for the corresponding device on |
| 81 | + * the subsystem bus. |
| 82 | + */ |
| 83 | +static DEFINE_XARRAY(memory_blocks); |
| 84 | + |
77 | 85 | static BLOCKING_NOTIFIER_HEAD(memory_chain);
|
78 | 86 |
|
79 | 87 | int register_memory_notifier(struct notifier_block *nb)
|
@@ -489,22 +497,23 @@ int __weak arch_get_memory_phys_device(unsigned long start_pfn)
|
489 | 497 | return 0;
|
490 | 498 | }
|
491 | 499 |
|
492 |
| -/* A reference for the returned memory block device is acquired. */ |
| 500 | +/* |
| 501 | + * A reference for the returned memory block device is acquired. |
| 502 | + * |
| 503 | + * Called under device_hotplug_lock. |
| 504 | + */ |
493 | 505 | static struct memory_block *find_memory_block_by_id(unsigned long block_id)
|
494 | 506 | {
|
495 |
| - struct device *dev; |
| 507 | + struct memory_block *mem; |
496 | 508 |
|
497 |
| - dev = subsys_find_device_by_id(&memory_subsys, block_id, NULL); |
498 |
| - return dev ? to_memory_block(dev) : NULL; |
| 509 | + mem = xa_load(&memory_blocks, block_id); |
| 510 | + if (mem) |
| 511 | + get_device(&mem->dev); |
| 512 | + return mem; |
499 | 513 | }
|
500 | 514 |
|
501 | 515 | /*
|
502 |
| - * For now, we have a linear search to go find the appropriate |
503 |
| - * memory_block corresponding to a particular phys_index. If |
504 |
| - * this gets to be a real problem, we can always use a radix |
505 |
| - * tree or something here. |
506 |
| - * |
507 |
| - * This could be made generic for all device subsystems. |
| 516 | + * Called under device_hotplug_lock. |
508 | 517 | */
|
509 | 518 | struct memory_block *find_memory_block(struct mem_section *section)
|
510 | 519 | {
|
@@ -548,9 +557,16 @@ int register_memory(struct memory_block *memory)
|
548 | 557 | memory->dev.offline = memory->state == MEM_OFFLINE;
|
549 | 558 |
|
550 | 559 | ret = device_register(&memory->dev);
|
551 |
| - if (ret) |
| 560 | + if (ret) { |
552 | 561 | put_device(&memory->dev);
|
553 |
| - |
| 562 | + return ret; |
| 563 | + } |
| 564 | + ret = xa_err(xa_store(&memory_blocks, memory->dev.id, memory, |
| 565 | + GFP_KERNEL)); |
| 566 | + if (ret) { |
| 567 | + put_device(&memory->dev); |
| 568 | + device_unregister(&memory->dev); |
| 569 | + } |
554 | 570 | return ret;
|
555 | 571 | }
|
556 | 572 |
|
@@ -604,6 +620,8 @@ static void unregister_memory(struct memory_block *memory)
|
604 | 620 | if (WARN_ON_ONCE(memory->dev.bus != &memory_subsys))
|
605 | 621 | return;
|
606 | 622 |
|
| 623 | + WARN_ON(xa_erase(&memory_blocks, memory->dev.id) == NULL); |
| 624 | + |
607 | 625 | /* drop the ref. we got via find_memory_block() */
|
608 | 626 | put_device(&memory->dev);
|
609 | 627 | device_unregister(&memory->dev);
|
@@ -750,6 +768,8 @@ void __init memory_dev_init(void)
|
750 | 768 | *
|
751 | 769 | * In case func() returns an error, walking is aborted and the error is
|
752 | 770 | * returned.
|
| 771 | + * |
| 772 | + * Called under device_hotplug_lock. |
753 | 773 | */
|
754 | 774 | int walk_memory_blocks(unsigned long start, unsigned long size,
|
755 | 775 | void *arg, walk_memory_blocks_func_t func)
|
|
0 commit comments