mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00

simplifies the act of creating a pte which addresses the first page in a folio and reduces the amount of plumbing which architecture must implement to provide this. - The 8 patch series "Misc folio patches for 6.16" from Matthew Wilcox is a shower of largely unrelated folio infrastructure changes which clean things up and better prepare us for future work. - The 3 patch series "memory,x86,acpi: hotplug memory alignment advisement" from Gregory Price adds early-init code to prevent x86 from leaving physical memory unused when physical address regions are not aligned to memory block size. - The 2 patch series "mm/compaction: allow more aggressive proactive compaction" from Michal Clapinski provides some tuning of the (sadly, hard-coded (more sadly, not auto-tuned)) thresholds for our invokation of proactive compaction. In a simple test case, the reduction of a guest VM's memory consumption was dramatic. - The 8 patch series "Minor cleanups and improvements to swap freeing code" from Kemeng Shi provides some code cleaups and a small efficiency improvement to this part of our swap handling code. - The 6 patch series "ptrace: introduce PTRACE_SET_SYSCALL_INFO API" from Dmitry Levin adds the ability for a ptracer to modify syscalls arguments. At this time we can alter only "system call information that are used by strace system call tampering, namely, syscall number, syscall arguments, and syscall return value. This series should have been incorporated into mm.git's "non-MM" branch, but I goofed. - The 3 patch series "fs/proc: extend the PAGEMAP_SCAN ioctl to report guard regions" from Andrei Vagin extends the info returned by the PAGEMAP_SCAN ioctl against /proc/pid/pagemap. This permits CRIU to more efficiently get at the info about guard regions. - The 2 patch series "Fix parameter passed to page_mapcount_is_type()" from Gavin Shan implements that fix. No runtime effect is expected because validate_page_before_insert() happens to fix up this error. - The 3 patch series "kernel/events/uprobes: uprobe_write_opcode() rewrite" from David Hildenbrand basically brings uprobe text poking into the current decade. Remove a bunch of hand-rolled implementation in favor of using more current facilities. - The 3 patch series "mm/ptdump: Drop assumption that pxd_val() is u64" from Anshuman Khandual provides enhancements and generalizations to the pte dumping code. This might be needed when 128-bit Page Table Descriptors are enabled for ARM. - The 12 patch series "Always call constructor for kernel page tables" from Kevin Brodsky "ensures that the ctor/dtor is always called for kernel pgtables, as it already is for user pgtables". This permits the addition of more functionality such as "insert hooks to protect page tables". This change does result in various architectures performing unnecesary work, but this is fixed up where it is anticipated to occur. - The 9 patch series "Rust support for mm_struct, vm_area_struct, and mmap" from Alice Ryhl adds plumbing to permit Rust access to core MM structures. - The 3 patch series "fix incorrectly disallowed anonymous VMA merges" from Lorenzo Stoakes takes advantage of some VMA merging opportunities which we've been missing for 15 years. - The 4 patch series "mm/madvise: batch tlb flushes for MADV_DONTNEED and MADV_FREE" from SeongJae Park optimizes process_madvise()'s TLB flushing. Instead of flushing each address range in the provided iovec, we batch the flushing across all the iovec entries. The syscall's cost was approximately halved with a microbenchmark which was designed to load this particular operation. - The 6 patch series "Track node vacancy to reduce worst case allocation counts" from Sidhartha Kumar makes the maple tree smarter about its node preallocation. stress-ng mmap performance increased by single-digit percentages and the amount of unnecessarily preallocated memory was dramaticelly reduced. - The 3 patch series "mm/gup: Minor fix, cleanup and improvements" from Baoquan He removes a few unnecessary things which Baoquan noted when reading the code. - The 3 patch series ""Enhance sysfs handling for memory hotplug in weighted interleave" from Rakie Kim "enhances the weighted interleave policy in the memory management subsystem by improving sysfs handling, fixing memory leaks, and introducing dynamic sysfs updates for memory hotplug support". Fixes things on error paths which we are unlikely to hit. - The 7 patch series "mm/damon: auto-tune DAMOS for NUMA setups including tiered memory" from SeongJae Park introduces new DAMOS quota goal metrics which eliminate the manual tuning which is required when utilizing DAMON for memory tiering. - The 5 patch series "mm/vmalloc.c: code cleanup and improvements" from Baoquan He provides cleanups and small efficiency improvements which Baoquan found via code inspection. - The 2 patch series "vmscan: enforce mems_effective during demotion" from Gregory Price "changes reclaim to respect cpuset.mems_effective during demotion when possible". because "presently, reclaim explicitly ignores cpuset.mems_effective when demoting, which may cause the cpuset settings to violated." "This is useful for isolating workloads on a multi-tenant system from certain classes of memory more consistently." - The 2 patch series ""Clean up split_huge_pmd_locked() and remove unnecessary folio pointers" from Gavin Guo provides minor cleanups and efficiency gains in in the huge page splitting and migrating code. - The 3 patch series "Use kmem_cache for memcg alloc" from Huan Yang creates a slab cache for `struct mem_cgroup', yielding improved memory utilization. - The 4 patch series "add max arg to swappiness in memory.reclaim and lru_gen" from Zhongkun He adds a new "max" argument to the "swappiness=" argument for memory.reclaim MGLRU's lru_gen. This directs proactive reclaim to reclaim from only anon folios rather than file-backed folios. - The 17 patch series "kexec: introduce Kexec HandOver (KHO)" from Mike Rapoport is the first step on the path to permitting the kernel to maintain existing VMs while replacing the host kernel via file-based kexec. At this time only memblock's reserve_mem is preserved. - The 7 patch series "mm: Introduce for_each_valid_pfn()" from David Woodhouse provides and uses a smarter way of looping over a pfn range. By skipping ranges of invalid pfns. - The 2 patch series "sched/numa: Skip VMA scanning on memory pinned to one NUMA node via cpuset.mems" from Libo Chen removes a lot of pointless VMA scanning when a task is pinned a single NUMA mode. Dramatic performance benefits were seen in some real world cases. - The 2 patch series "JFS: Implement migrate_folio for jfs_metapage_aops" from Shivank Garg addresses a warning which occurs during memory compaction when using JFS. - The 4 patch series "move all VMA allocation, freeing and duplication logic to mm" from Lorenzo Stoakes moves some VMA code from kernel/fork.c into the more appropriate mm/vma.c. - The 6 patch series "mm, swap: clean up swap cache mapping helper" from Kairui Song provides code consolidation and cleanups related to the folio_index() function. - The 2 patch series "mm/gup: Cleanup memfd_pin_folios()" from Vishal Moola does that. - The 8 patch series "memcg: Fix test_memcg_min/low test failures" from Waiman Long addresses some bogus failures which are being reported by the test_memcontrol selftest. - The 3 patch series "eliminate mmap() retry merge, add .mmap_prepare hook" from Lorenzo Stoakes commences the deprecation of file_operations.mmap() in favor of the new file_operations.mmap_prepare(). The latter is more restrictive and prevents drivers from messing with things in ways which, amongst other problems, may defeat VMA merging. - The 4 patch series "memcg: decouple memcg and objcg stocks"" from Shakeel Butt decouples the per-cpu memcg charge cache from the objcg's one. This is a step along the way to making memcg and objcg charging NMI-safe, which is a BPF requirement. - The 6 patch series "mm/damon: minor fixups and improvements for code, tests, and documents" from SeongJae Park is "yet another batch of miscellaneous DAMON changes. Fix and improve minor problems in code, tests and documents." - The 7 patch series "memcg: make memcg stats irq safe" from Shakeel Butt converts memcg stats to be irq safe. Another step along the way to making memcg charging and stats updates NMI-safe, a BPF requirement. - The 4 patch series "Let unmap_hugepage_range() and several related functions take folio instead of page" from Fan Ni provides folio conversions in the hugetlb code. -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQTTMBEPP41GrTpTJgfdBJ7gKXxAjgUCaDt5qgAKCRDdBJ7gKXxA ju6XAP9nTiSfRz8Cz1n5LJZpFKEGzLpSihCYyR6P3o1L9oe3mwEAlZ5+XAwk2I5x Qqb/UGMEpilyre1PayQqOnct3aSL9Ao= =tYYm -----END PGP SIGNATURE----- Merge tag 'mm-stable-2025-05-31-14-50' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm Pull MM updates from Andrew Morton: - "Add folio_mk_pte()" from Matthew Wilcox simplifies the act of creating a pte which addresses the first page in a folio and reduces the amount of plumbing which architecture must implement to provide this. - "Misc folio patches for 6.16" from Matthew Wilcox is a shower of largely unrelated folio infrastructure changes which clean things up and better prepare us for future work. - "memory,x86,acpi: hotplug memory alignment advisement" from Gregory Price adds early-init code to prevent x86 from leaving physical memory unused when physical address regions are not aligned to memory block size. - "mm/compaction: allow more aggressive proactive compaction" from Michal Clapinski provides some tuning of the (sadly, hard-coded (more sadly, not auto-tuned)) thresholds for our invokation of proactive compaction. In a simple test case, the reduction of a guest VM's memory consumption was dramatic. - "Minor cleanups and improvements to swap freeing code" from Kemeng Shi provides some code cleaups and a small efficiency improvement to this part of our swap handling code. - "ptrace: introduce PTRACE_SET_SYSCALL_INFO API" from Dmitry Levin adds the ability for a ptracer to modify syscalls arguments. At this time we can alter only "system call information that are used by strace system call tampering, namely, syscall number, syscall arguments, and syscall return value. This series should have been incorporated into mm.git's "non-MM" branch, but I goofed. - "fs/proc: extend the PAGEMAP_SCAN ioctl to report guard regions" from Andrei Vagin extends the info returned by the PAGEMAP_SCAN ioctl against /proc/pid/pagemap. This permits CRIU to more efficiently get at the info about guard regions. - "Fix parameter passed to page_mapcount_is_type()" from Gavin Shan implements that fix. No runtime effect is expected because validate_page_before_insert() happens to fix up this error. - "kernel/events/uprobes: uprobe_write_opcode() rewrite" from David Hildenbrand basically brings uprobe text poking into the current decade. Remove a bunch of hand-rolled implementation in favor of using more current facilities. - "mm/ptdump: Drop assumption that pxd_val() is u64" from Anshuman Khandual provides enhancements and generalizations to the pte dumping code. This might be needed when 128-bit Page Table Descriptors are enabled for ARM. - "Always call constructor for kernel page tables" from Kevin Brodsky ensures that the ctor/dtor is always called for kernel pgtables, as it already is for user pgtables. This permits the addition of more functionality such as "insert hooks to protect page tables". This change does result in various architectures performing unnecesary work, but this is fixed up where it is anticipated to occur. - "Rust support for mm_struct, vm_area_struct, and mmap" from Alice Ryhl adds plumbing to permit Rust access to core MM structures. - "fix incorrectly disallowed anonymous VMA merges" from Lorenzo Stoakes takes advantage of some VMA merging opportunities which we've been missing for 15 years. - "mm/madvise: batch tlb flushes for MADV_DONTNEED and MADV_FREE" from SeongJae Park optimizes process_madvise()'s TLB flushing. Instead of flushing each address range in the provided iovec, we batch the flushing across all the iovec entries. The syscall's cost was approximately halved with a microbenchmark which was designed to load this particular operation. - "Track node vacancy to reduce worst case allocation counts" from Sidhartha Kumar makes the maple tree smarter about its node preallocation. stress-ng mmap performance increased by single-digit percentages and the amount of unnecessarily preallocated memory was dramaticelly reduced. - "mm/gup: Minor fix, cleanup and improvements" from Baoquan He removes a few unnecessary things which Baoquan noted when reading the code. - ""Enhance sysfs handling for memory hotplug in weighted interleave" from Rakie Kim "enhances the weighted interleave policy in the memory management subsystem by improving sysfs handling, fixing memory leaks, and introducing dynamic sysfs updates for memory hotplug support". Fixes things on error paths which we are unlikely to hit. - "mm/damon: auto-tune DAMOS for NUMA setups including tiered memory" from SeongJae Park introduces new DAMOS quota goal metrics which eliminate the manual tuning which is required when utilizing DAMON for memory tiering. - "mm/vmalloc.c: code cleanup and improvements" from Baoquan He provides cleanups and small efficiency improvements which Baoquan found via code inspection. - "vmscan: enforce mems_effective during demotion" from Gregory Price changes reclaim to respect cpuset.mems_effective during demotion when possible. because presently, reclaim explicitly ignores cpuset.mems_effective when demoting, which may cause the cpuset settings to violated. This is useful for isolating workloads on a multi-tenant system from certain classes of memory more consistently. - "Clean up split_huge_pmd_locked() and remove unnecessary folio pointers" from Gavin Guo provides minor cleanups and efficiency gains in in the huge page splitting and migrating code. - "Use kmem_cache for memcg alloc" from Huan Yang creates a slab cache for `struct mem_cgroup', yielding improved memory utilization. - "add max arg to swappiness in memory.reclaim and lru_gen" from Zhongkun He adds a new "max" argument to the "swappiness=" argument for memory.reclaim MGLRU's lru_gen. This directs proactive reclaim to reclaim from only anon folios rather than file-backed folios. - "kexec: introduce Kexec HandOver (KHO)" from Mike Rapoport is the first step on the path to permitting the kernel to maintain existing VMs while replacing the host kernel via file-based kexec. At this time only memblock's reserve_mem is preserved. - "mm: Introduce for_each_valid_pfn()" from David Woodhouse provides and uses a smarter way of looping over a pfn range. By skipping ranges of invalid pfns. - "sched/numa: Skip VMA scanning on memory pinned to one NUMA node via cpuset.mems" from Libo Chen removes a lot of pointless VMA scanning when a task is pinned a single NUMA mode. Dramatic performance benefits were seen in some real world cases. - "JFS: Implement migrate_folio for jfs_metapage_aops" from Shivank Garg addresses a warning which occurs during memory compaction when using JFS. - "move all VMA allocation, freeing and duplication logic to mm" from Lorenzo Stoakes moves some VMA code from kernel/fork.c into the more appropriate mm/vma.c. - "mm, swap: clean up swap cache mapping helper" from Kairui Song provides code consolidation and cleanups related to the folio_index() function. - "mm/gup: Cleanup memfd_pin_folios()" from Vishal Moola does that. - "memcg: Fix test_memcg_min/low test failures" from Waiman Long addresses some bogus failures which are being reported by the test_memcontrol selftest. - "eliminate mmap() retry merge, add .mmap_prepare hook" from Lorenzo Stoakes commences the deprecation of file_operations.mmap() in favor of the new file_operations.mmap_prepare(). The latter is more restrictive and prevents drivers from messing with things in ways which, amongst other problems, may defeat VMA merging. - "memcg: decouple memcg and objcg stocks"" from Shakeel Butt decouples the per-cpu memcg charge cache from the objcg's one. This is a step along the way to making memcg and objcg charging NMI-safe, which is a BPF requirement. - "mm/damon: minor fixups and improvements for code, tests, and documents" from SeongJae Park is yet another batch of miscellaneous DAMON changes. Fix and improve minor problems in code, tests and documents. - "memcg: make memcg stats irq safe" from Shakeel Butt converts memcg stats to be irq safe. Another step along the way to making memcg charging and stats updates NMI-safe, a BPF requirement. - "Let unmap_hugepage_range() and several related functions take folio instead of page" from Fan Ni provides folio conversions in the hugetlb code. * tag 'mm-stable-2025-05-31-14-50' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (285 commits) mm: pcp: increase pcp->free_count threshold to trigger free_high mm/hugetlb: convert use of struct page to folio in __unmap_hugepage_range() mm/hugetlb: refactor __unmap_hugepage_range() to take folio instead of page mm/hugetlb: refactor unmap_hugepage_range() to take folio instead of page mm/hugetlb: pass folio instead of page to unmap_ref_private() memcg: objcg stock trylock without irq disabling memcg: no stock lock for cpu hot-unplug memcg: make __mod_memcg_lruvec_state re-entrant safe against irqs memcg: make count_memcg_events re-entrant safe against irqs memcg: make mod_memcg_state re-entrant safe against irqs memcg: move preempt disable to callers of memcg_rstat_updated memcg: memcg_rstat_updated re-entrant safe against irqs mm: khugepaged: decouple SHMEM and file folios' collapse selftests/eventfd: correct test name and improve messages alloc_tag: check mem_profiling_support in alloc_tag_init Docs/damon: update titles and brief introductions to explain DAMOS selftests/damon/_damon_sysfs: read tried regions directories in order mm/damon/tests/core-kunit: add a test for damos_set_filters_default_reject() mm/damon/paddr: remove unused variable, folio_list, in damon_pa_stat() mm/damon/sysfs-schemes: fix wrong comment on damons_sysfs_quota_goal_metric_strs ...
991 lines
26 KiB
C
991 lines
26 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Basic Node interface support
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/memory.h>
|
|
#include <linux/mempolicy.h>
|
|
#include <linux/vmstat.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/node.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <linux/compaction.h>
|
|
#include <linux/cpumask.h>
|
|
#include <linux/topology.h>
|
|
#include <linux/nodemask.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/slab.h>
|
|
|
|
static const struct bus_type node_subsys = {
|
|
.name = "node",
|
|
.dev_name = "node",
|
|
};
|
|
|
|
static inline ssize_t cpumap_read(struct file *file, struct kobject *kobj,
|
|
const struct bin_attribute *attr, char *buf,
|
|
loff_t off, size_t count)
|
|
{
|
|
struct device *dev = kobj_to_dev(kobj);
|
|
struct node *node_dev = to_node(dev);
|
|
cpumask_var_t mask;
|
|
ssize_t n;
|
|
|
|
if (!alloc_cpumask_var(&mask, GFP_KERNEL))
|
|
return 0;
|
|
|
|
cpumask_and(mask, cpumask_of_node(node_dev->dev.id), cpu_online_mask);
|
|
n = cpumap_print_bitmask_to_buf(buf, mask, off, count);
|
|
free_cpumask_var(mask);
|
|
|
|
return n;
|
|
}
|
|
|
|
static const BIN_ATTR_RO(cpumap, CPUMAP_FILE_MAX_BYTES);
|
|
|
|
static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj,
|
|
const struct bin_attribute *attr, char *buf,
|
|
loff_t off, size_t count)
|
|
{
|
|
struct device *dev = kobj_to_dev(kobj);
|
|
struct node *node_dev = to_node(dev);
|
|
cpumask_var_t mask;
|
|
ssize_t n;
|
|
|
|
if (!alloc_cpumask_var(&mask, GFP_KERNEL))
|
|
return 0;
|
|
|
|
cpumask_and(mask, cpumask_of_node(node_dev->dev.id), cpu_online_mask);
|
|
n = cpumap_print_list_to_buf(buf, mask, off, count);
|
|
free_cpumask_var(mask);
|
|
|
|
return n;
|
|
}
|
|
|
|
static const BIN_ATTR_RO(cpulist, CPULIST_FILE_MAX_BYTES);
|
|
|
|
/**
|
|
* struct node_access_nodes - Access class device to hold user visible
|
|
* relationships to other nodes.
|
|
* @dev: Device for this memory access class
|
|
* @list_node: List element in the node's access list
|
|
* @access: The access class rank
|
|
* @coord: Heterogeneous memory performance coordinates
|
|
*/
|
|
struct node_access_nodes {
|
|
struct device dev;
|
|
struct list_head list_node;
|
|
unsigned int access;
|
|
#ifdef CONFIG_HMEM_REPORTING
|
|
struct access_coordinate coord;
|
|
#endif
|
|
};
|
|
#define to_access_nodes(dev) container_of(dev, struct node_access_nodes, dev)
|
|
|
|
static struct attribute *node_init_access_node_attrs[] = {
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute *node_targ_access_node_attrs[] = {
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group initiators = {
|
|
.name = "initiators",
|
|
.attrs = node_init_access_node_attrs,
|
|
};
|
|
|
|
static const struct attribute_group targets = {
|
|
.name = "targets",
|
|
.attrs = node_targ_access_node_attrs,
|
|
};
|
|
|
|
static const struct attribute_group *node_access_node_groups[] = {
|
|
&initiators,
|
|
&targets,
|
|
NULL,
|
|
};
|
|
|
|
static void node_remove_accesses(struct node *node)
|
|
{
|
|
struct node_access_nodes *c, *cnext;
|
|
|
|
list_for_each_entry_safe(c, cnext, &node->access_list, list_node) {
|
|
list_del(&c->list_node);
|
|
device_unregister(&c->dev);
|
|
}
|
|
}
|
|
|
|
static void node_access_release(struct device *dev)
|
|
{
|
|
kfree(to_access_nodes(dev));
|
|
}
|
|
|
|
static struct node_access_nodes *node_init_node_access(struct node *node,
|
|
enum access_coordinate_class access)
|
|
{
|
|
struct node_access_nodes *access_node;
|
|
struct device *dev;
|
|
|
|
list_for_each_entry(access_node, &node->access_list, list_node)
|
|
if (access_node->access == access)
|
|
return access_node;
|
|
|
|
access_node = kzalloc(sizeof(*access_node), GFP_KERNEL);
|
|
if (!access_node)
|
|
return NULL;
|
|
|
|
access_node->access = access;
|
|
dev = &access_node->dev;
|
|
dev->parent = &node->dev;
|
|
dev->release = node_access_release;
|
|
dev->groups = node_access_node_groups;
|
|
if (dev_set_name(dev, "access%u", access))
|
|
goto free;
|
|
|
|
if (device_register(dev))
|
|
goto free_name;
|
|
|
|
pm_runtime_no_callbacks(dev);
|
|
list_add_tail(&access_node->list_node, &node->access_list);
|
|
return access_node;
|
|
free_name:
|
|
kfree_const(dev->kobj.name);
|
|
free:
|
|
kfree(access_node);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef CONFIG_HMEM_REPORTING
|
|
#define ACCESS_ATTR(property) \
|
|
static ssize_t property##_show(struct device *dev, \
|
|
struct device_attribute *attr, \
|
|
char *buf) \
|
|
{ \
|
|
return sysfs_emit(buf, "%u\n", \
|
|
to_access_nodes(dev)->coord.property); \
|
|
} \
|
|
static DEVICE_ATTR_RO(property)
|
|
|
|
ACCESS_ATTR(read_bandwidth);
|
|
ACCESS_ATTR(read_latency);
|
|
ACCESS_ATTR(write_bandwidth);
|
|
ACCESS_ATTR(write_latency);
|
|
|
|
static struct attribute *access_attrs[] = {
|
|
&dev_attr_read_bandwidth.attr,
|
|
&dev_attr_read_latency.attr,
|
|
&dev_attr_write_bandwidth.attr,
|
|
&dev_attr_write_latency.attr,
|
|
NULL,
|
|
};
|
|
|
|
/**
|
|
* node_set_perf_attrs - Set the performance values for given access class
|
|
* @nid: Node identifier to be set
|
|
* @coord: Heterogeneous memory performance coordinates
|
|
* @access: The access class the for the given attributes
|
|
*/
|
|
void node_set_perf_attrs(unsigned int nid, struct access_coordinate *coord,
|
|
enum access_coordinate_class access)
|
|
{
|
|
struct node_access_nodes *c;
|
|
struct node *node;
|
|
int i;
|
|
|
|
if (WARN_ON_ONCE(!node_online(nid)))
|
|
return;
|
|
|
|
node = node_devices[nid];
|
|
c = node_init_node_access(node, access);
|
|
if (!c)
|
|
return;
|
|
|
|
c->coord = *coord;
|
|
for (i = 0; access_attrs[i] != NULL; i++) {
|
|
if (sysfs_add_file_to_group(&c->dev.kobj, access_attrs[i],
|
|
"initiators")) {
|
|
pr_info("failed to add performance attribute to node %d\n",
|
|
nid);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* When setting CPU access coordinates, update mempolicy */
|
|
if (access == ACCESS_COORDINATE_CPU) {
|
|
if (mempolicy_set_node_perf(nid, coord)) {
|
|
pr_info("failed to set mempolicy attrs for node %d\n",
|
|
nid);
|
|
}
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(node_set_perf_attrs);
|
|
|
|
/**
|
|
* struct node_cache_info - Internal tracking for memory node caches
|
|
* @dev: Device represeting the cache level
|
|
* @node: List element for tracking in the node
|
|
* @cache_attrs:Attributes for this cache level
|
|
*/
|
|
struct node_cache_info {
|
|
struct device dev;
|
|
struct list_head node;
|
|
struct node_cache_attrs cache_attrs;
|
|
};
|
|
#define to_cache_info(device) container_of(device, struct node_cache_info, dev)
|
|
|
|
#define CACHE_ATTR(name, fmt) \
|
|
static ssize_t name##_show(struct device *dev, \
|
|
struct device_attribute *attr, \
|
|
char *buf) \
|
|
{ \
|
|
return sysfs_emit(buf, fmt "\n", \
|
|
to_cache_info(dev)->cache_attrs.name); \
|
|
} \
|
|
static DEVICE_ATTR_RO(name);
|
|
|
|
CACHE_ATTR(size, "%llu")
|
|
CACHE_ATTR(line_size, "%u")
|
|
CACHE_ATTR(indexing, "%u")
|
|
CACHE_ATTR(write_policy, "%u")
|
|
CACHE_ATTR(address_mode, "%#x")
|
|
|
|
static struct attribute *cache_attrs[] = {
|
|
&dev_attr_indexing.attr,
|
|
&dev_attr_size.attr,
|
|
&dev_attr_line_size.attr,
|
|
&dev_attr_write_policy.attr,
|
|
&dev_attr_address_mode.attr,
|
|
NULL,
|
|
};
|
|
ATTRIBUTE_GROUPS(cache);
|
|
|
|
static void node_cache_release(struct device *dev)
|
|
{
|
|
kfree(dev);
|
|
}
|
|
|
|
static void node_cacheinfo_release(struct device *dev)
|
|
{
|
|
struct node_cache_info *info = to_cache_info(dev);
|
|
kfree(info);
|
|
}
|
|
|
|
static void node_init_cache_dev(struct node *node)
|
|
{
|
|
struct device *dev;
|
|
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev)
|
|
return;
|
|
|
|
device_initialize(dev);
|
|
dev->parent = &node->dev;
|
|
dev->release = node_cache_release;
|
|
if (dev_set_name(dev, "memory_side_cache"))
|
|
goto put_device;
|
|
|
|
if (device_add(dev))
|
|
goto put_device;
|
|
|
|
pm_runtime_no_callbacks(dev);
|
|
node->cache_dev = dev;
|
|
return;
|
|
put_device:
|
|
put_device(dev);
|
|
}
|
|
|
|
/**
|
|
* node_add_cache() - add cache attribute to a memory node
|
|
* @nid: Node identifier that has new cache attributes
|
|
* @cache_attrs: Attributes for the cache being added
|
|
*/
|
|
void node_add_cache(unsigned int nid, struct node_cache_attrs *cache_attrs)
|
|
{
|
|
struct node_cache_info *info;
|
|
struct device *dev;
|
|
struct node *node;
|
|
|
|
if (!node_online(nid) || !node_devices[nid])
|
|
return;
|
|
|
|
node = node_devices[nid];
|
|
list_for_each_entry(info, &node->cache_attrs, node) {
|
|
if (info->cache_attrs.level == cache_attrs->level) {
|
|
dev_warn(&node->dev,
|
|
"attempt to add duplicate cache level:%d\n",
|
|
cache_attrs->level);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!node->cache_dev)
|
|
node_init_cache_dev(node);
|
|
if (!node->cache_dev)
|
|
return;
|
|
|
|
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
|
if (!info)
|
|
return;
|
|
|
|
dev = &info->dev;
|
|
device_initialize(dev);
|
|
dev->parent = node->cache_dev;
|
|
dev->release = node_cacheinfo_release;
|
|
dev->groups = cache_groups;
|
|
if (dev_set_name(dev, "index%d", cache_attrs->level))
|
|
goto put_device;
|
|
|
|
info->cache_attrs = *cache_attrs;
|
|
if (device_add(dev)) {
|
|
dev_warn(&node->dev, "failed to add cache level:%d\n",
|
|
cache_attrs->level);
|
|
goto put_device;
|
|
}
|
|
pm_runtime_no_callbacks(dev);
|
|
list_add_tail(&info->node, &node->cache_attrs);
|
|
return;
|
|
put_device:
|
|
put_device(dev);
|
|
}
|
|
|
|
static void node_remove_caches(struct node *node)
|
|
{
|
|
struct node_cache_info *info, *next;
|
|
|
|
if (!node->cache_dev)
|
|
return;
|
|
|
|
list_for_each_entry_safe(info, next, &node->cache_attrs, node) {
|
|
list_del(&info->node);
|
|
device_unregister(&info->dev);
|
|
}
|
|
device_unregister(node->cache_dev);
|
|
}
|
|
|
|
static void node_init_caches(unsigned int nid)
|
|
{
|
|
INIT_LIST_HEAD(&node_devices[nid]->cache_attrs);
|
|
}
|
|
#else
|
|
static void node_init_caches(unsigned int nid) { }
|
|
static void node_remove_caches(struct node *node) { }
|
|
#endif
|
|
|
|
#define K(x) ((x) << (PAGE_SHIFT - 10))
|
|
static ssize_t node_read_meminfo(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int len = 0;
|
|
int nid = dev->id;
|
|
struct pglist_data *pgdat = NODE_DATA(nid);
|
|
struct sysinfo i;
|
|
unsigned long sreclaimable, sunreclaimable;
|
|
unsigned long swapcached = 0;
|
|
|
|
si_meminfo_node(&i, nid);
|
|
sreclaimable = node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B);
|
|
sunreclaimable = node_page_state_pages(pgdat, NR_SLAB_UNRECLAIMABLE_B);
|
|
#ifdef CONFIG_SWAP
|
|
swapcached = node_page_state_pages(pgdat, NR_SWAPCACHE);
|
|
#endif
|
|
len = sysfs_emit_at(buf, len,
|
|
"Node %d MemTotal: %8lu kB\n"
|
|
"Node %d MemFree: %8lu kB\n"
|
|
"Node %d MemUsed: %8lu kB\n"
|
|
"Node %d SwapCached: %8lu kB\n"
|
|
"Node %d Active: %8lu kB\n"
|
|
"Node %d Inactive: %8lu kB\n"
|
|
"Node %d Active(anon): %8lu kB\n"
|
|
"Node %d Inactive(anon): %8lu kB\n"
|
|
"Node %d Active(file): %8lu kB\n"
|
|
"Node %d Inactive(file): %8lu kB\n"
|
|
"Node %d Unevictable: %8lu kB\n"
|
|
"Node %d Mlocked: %8lu kB\n",
|
|
nid, K(i.totalram),
|
|
nid, K(i.freeram),
|
|
nid, K(i.totalram - i.freeram),
|
|
nid, K(swapcached),
|
|
nid, K(node_page_state(pgdat, NR_ACTIVE_ANON) +
|
|
node_page_state(pgdat, NR_ACTIVE_FILE)),
|
|
nid, K(node_page_state(pgdat, NR_INACTIVE_ANON) +
|
|
node_page_state(pgdat, NR_INACTIVE_FILE)),
|
|
nid, K(node_page_state(pgdat, NR_ACTIVE_ANON)),
|
|
nid, K(node_page_state(pgdat, NR_INACTIVE_ANON)),
|
|
nid, K(node_page_state(pgdat, NR_ACTIVE_FILE)),
|
|
nid, K(node_page_state(pgdat, NR_INACTIVE_FILE)),
|
|
nid, K(node_page_state(pgdat, NR_UNEVICTABLE)),
|
|
nid, K(sum_zone_node_page_state(nid, NR_MLOCK)));
|
|
|
|
#ifdef CONFIG_HIGHMEM
|
|
len += sysfs_emit_at(buf, len,
|
|
"Node %d HighTotal: %8lu kB\n"
|
|
"Node %d HighFree: %8lu kB\n"
|
|
"Node %d LowTotal: %8lu kB\n"
|
|
"Node %d LowFree: %8lu kB\n",
|
|
nid, K(i.totalhigh),
|
|
nid, K(i.freehigh),
|
|
nid, K(i.totalram - i.totalhigh),
|
|
nid, K(i.freeram - i.freehigh));
|
|
#endif
|
|
len += sysfs_emit_at(buf, len,
|
|
"Node %d Dirty: %8lu kB\n"
|
|
"Node %d Writeback: %8lu kB\n"
|
|
"Node %d FilePages: %8lu kB\n"
|
|
"Node %d Mapped: %8lu kB\n"
|
|
"Node %d AnonPages: %8lu kB\n"
|
|
"Node %d Shmem: %8lu kB\n"
|
|
"Node %d KernelStack: %8lu kB\n"
|
|
#ifdef CONFIG_SHADOW_CALL_STACK
|
|
"Node %d ShadowCallStack:%8lu kB\n"
|
|
#endif
|
|
"Node %d PageTables: %8lu kB\n"
|
|
"Node %d SecPageTables: %8lu kB\n"
|
|
"Node %d NFS_Unstable: %8lu kB\n"
|
|
"Node %d Bounce: %8lu kB\n"
|
|
"Node %d WritebackTmp: %8lu kB\n"
|
|
"Node %d KReclaimable: %8lu kB\n"
|
|
"Node %d Slab: %8lu kB\n"
|
|
"Node %d SReclaimable: %8lu kB\n"
|
|
"Node %d SUnreclaim: %8lu kB\n"
|
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
|
"Node %d AnonHugePages: %8lu kB\n"
|
|
"Node %d ShmemHugePages: %8lu kB\n"
|
|
"Node %d ShmemPmdMapped: %8lu kB\n"
|
|
"Node %d FileHugePages: %8lu kB\n"
|
|
"Node %d FilePmdMapped: %8lu kB\n"
|
|
#endif
|
|
#ifdef CONFIG_UNACCEPTED_MEMORY
|
|
"Node %d Unaccepted: %8lu kB\n"
|
|
#endif
|
|
,
|
|
nid, K(node_page_state(pgdat, NR_FILE_DIRTY)),
|
|
nid, K(node_page_state(pgdat, NR_WRITEBACK)),
|
|
nid, K(node_page_state(pgdat, NR_FILE_PAGES)),
|
|
nid, K(node_page_state(pgdat, NR_FILE_MAPPED)),
|
|
nid, K(node_page_state(pgdat, NR_ANON_MAPPED)),
|
|
nid, K(i.sharedram),
|
|
nid, node_page_state(pgdat, NR_KERNEL_STACK_KB),
|
|
#ifdef CONFIG_SHADOW_CALL_STACK
|
|
nid, node_page_state(pgdat, NR_KERNEL_SCS_KB),
|
|
#endif
|
|
nid, K(node_page_state(pgdat, NR_PAGETABLE)),
|
|
nid, K(node_page_state(pgdat, NR_SECONDARY_PAGETABLE)),
|
|
nid, 0UL,
|
|
nid, 0UL,
|
|
nid, K(node_page_state(pgdat, NR_WRITEBACK_TEMP)),
|
|
nid, K(sreclaimable +
|
|
node_page_state(pgdat, NR_KERNEL_MISC_RECLAIMABLE)),
|
|
nid, K(sreclaimable + sunreclaimable),
|
|
nid, K(sreclaimable),
|
|
nid, K(sunreclaimable)
|
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
|
,
|
|
nid, K(node_page_state(pgdat, NR_ANON_THPS)),
|
|
nid, K(node_page_state(pgdat, NR_SHMEM_THPS)),
|
|
nid, K(node_page_state(pgdat, NR_SHMEM_PMDMAPPED)),
|
|
nid, K(node_page_state(pgdat, NR_FILE_THPS)),
|
|
nid, K(node_page_state(pgdat, NR_FILE_PMDMAPPED))
|
|
#endif
|
|
#ifdef CONFIG_UNACCEPTED_MEMORY
|
|
,
|
|
nid, K(sum_zone_node_page_state(nid, NR_UNACCEPTED))
|
|
#endif
|
|
);
|
|
len += hugetlb_report_node_meminfo(buf, len, nid);
|
|
return len;
|
|
}
|
|
|
|
#undef K
|
|
static DEVICE_ATTR(meminfo, 0444, node_read_meminfo, NULL);
|
|
|
|
static ssize_t node_read_numastat(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
fold_vm_numa_events();
|
|
return sysfs_emit(buf,
|
|
"numa_hit %lu\n"
|
|
"numa_miss %lu\n"
|
|
"numa_foreign %lu\n"
|
|
"interleave_hit %lu\n"
|
|
"local_node %lu\n"
|
|
"other_node %lu\n",
|
|
sum_zone_numa_event_state(dev->id, NUMA_HIT),
|
|
sum_zone_numa_event_state(dev->id, NUMA_MISS),
|
|
sum_zone_numa_event_state(dev->id, NUMA_FOREIGN),
|
|
sum_zone_numa_event_state(dev->id, NUMA_INTERLEAVE_HIT),
|
|
sum_zone_numa_event_state(dev->id, NUMA_LOCAL),
|
|
sum_zone_numa_event_state(dev->id, NUMA_OTHER));
|
|
}
|
|
static DEVICE_ATTR(numastat, 0444, node_read_numastat, NULL);
|
|
|
|
static ssize_t node_read_vmstat(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int nid = dev->id;
|
|
struct pglist_data *pgdat = NODE_DATA(nid);
|
|
int i;
|
|
int len = 0;
|
|
|
|
for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
|
|
len += sysfs_emit_at(buf, len, "%s %lu\n",
|
|
zone_stat_name(i),
|
|
sum_zone_node_page_state(nid, i));
|
|
|
|
#ifdef CONFIG_NUMA
|
|
fold_vm_numa_events();
|
|
for (i = 0; i < NR_VM_NUMA_EVENT_ITEMS; i++)
|
|
len += sysfs_emit_at(buf, len, "%s %lu\n",
|
|
numa_stat_name(i),
|
|
sum_zone_numa_event_state(nid, i));
|
|
|
|
#endif
|
|
for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) {
|
|
unsigned long pages = node_page_state_pages(pgdat, i);
|
|
|
|
if (vmstat_item_print_in_thp(i))
|
|
pages /= HPAGE_PMD_NR;
|
|
len += sysfs_emit_at(buf, len, "%s %lu\n", node_stat_name(i),
|
|
pages);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
static DEVICE_ATTR(vmstat, 0444, node_read_vmstat, NULL);
|
|
|
|
static ssize_t node_read_distance(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int nid = dev->id;
|
|
int len = 0;
|
|
int i;
|
|
|
|
/*
|
|
* buf is currently PAGE_SIZE in length and each node needs 4 chars
|
|
* at the most (distance + space or newline).
|
|
*/
|
|
BUILD_BUG_ON(MAX_NUMNODES * 4 > PAGE_SIZE);
|
|
|
|
for_each_online_node(i) {
|
|
len += sysfs_emit_at(buf, len, "%s%d",
|
|
i ? " " : "", node_distance(nid, i));
|
|
}
|
|
|
|
len += sysfs_emit_at(buf, len, "\n");
|
|
return len;
|
|
}
|
|
static DEVICE_ATTR(distance, 0444, node_read_distance, NULL);
|
|
|
|
static struct attribute *node_dev_attrs[] = {
|
|
&dev_attr_meminfo.attr,
|
|
&dev_attr_numastat.attr,
|
|
&dev_attr_distance.attr,
|
|
&dev_attr_vmstat.attr,
|
|
NULL
|
|
};
|
|
|
|
static const struct bin_attribute *node_dev_bin_attrs[] = {
|
|
&bin_attr_cpumap,
|
|
&bin_attr_cpulist,
|
|
NULL
|
|
};
|
|
|
|
static const struct attribute_group node_dev_group = {
|
|
.attrs = node_dev_attrs,
|
|
.bin_attrs_new = node_dev_bin_attrs,
|
|
};
|
|
|
|
static const struct attribute_group *node_dev_groups[] = {
|
|
&node_dev_group,
|
|
#ifdef CONFIG_HAVE_ARCH_NODE_DEV_GROUP
|
|
&arch_node_dev_group,
|
|
#endif
|
|
#ifdef CONFIG_MEMORY_FAILURE
|
|
&memory_failure_attr_group,
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
static void node_device_release(struct device *dev)
|
|
{
|
|
kfree(to_node(dev));
|
|
}
|
|
|
|
/*
|
|
* register_node - Setup a sysfs device for a node.
|
|
* @num - Node number to use when creating the device.
|
|
*
|
|
* Initialize and register the node device.
|
|
*/
|
|
static int register_node(struct node *node, int num)
|
|
{
|
|
int error;
|
|
|
|
node->dev.id = num;
|
|
node->dev.bus = &node_subsys;
|
|
node->dev.release = node_device_release;
|
|
node->dev.groups = node_dev_groups;
|
|
error = device_register(&node->dev);
|
|
|
|
if (error) {
|
|
put_device(&node->dev);
|
|
} else {
|
|
hugetlb_register_node(node);
|
|
compaction_register_node(node);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* unregister_node - unregister a node device
|
|
* @node: node going away
|
|
*
|
|
* Unregisters a node device @node. All the devices on the node must be
|
|
* unregistered before calling this function.
|
|
*/
|
|
void unregister_node(struct node *node)
|
|
{
|
|
hugetlb_unregister_node(node);
|
|
compaction_unregister_node(node);
|
|
node_remove_accesses(node);
|
|
node_remove_caches(node);
|
|
device_unregister(&node->dev);
|
|
}
|
|
|
|
struct node *node_devices[MAX_NUMNODES];
|
|
|
|
/*
|
|
* register cpu under node
|
|
*/
|
|
int register_cpu_under_node(unsigned int cpu, unsigned int nid)
|
|
{
|
|
int ret;
|
|
struct device *obj;
|
|
|
|
if (!node_online(nid))
|
|
return 0;
|
|
|
|
obj = get_cpu_device(cpu);
|
|
if (!obj)
|
|
return 0;
|
|
|
|
ret = sysfs_create_link(&node_devices[nid]->dev.kobj,
|
|
&obj->kobj,
|
|
kobject_name(&obj->kobj));
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sysfs_create_link(&obj->kobj,
|
|
&node_devices[nid]->dev.kobj,
|
|
kobject_name(&node_devices[nid]->dev.kobj));
|
|
}
|
|
|
|
/**
|
|
* register_memory_node_under_compute_node - link memory node to its compute
|
|
* node for a given access class.
|
|
* @mem_nid: Memory node number
|
|
* @cpu_nid: Cpu node number
|
|
* @access: Access class to register
|
|
*
|
|
* Description:
|
|
* For use with platforms that may have separate memory and compute nodes.
|
|
* This function will export node relationships linking which memory
|
|
* initiator nodes can access memory targets at a given ranked access
|
|
* class.
|
|
*/
|
|
int register_memory_node_under_compute_node(unsigned int mem_nid,
|
|
unsigned int cpu_nid,
|
|
enum access_coordinate_class access)
|
|
{
|
|
struct node *init_node, *targ_node;
|
|
struct node_access_nodes *initiator, *target;
|
|
int ret;
|
|
|
|
if (!node_online(cpu_nid) || !node_online(mem_nid))
|
|
return -ENODEV;
|
|
|
|
init_node = node_devices[cpu_nid];
|
|
targ_node = node_devices[mem_nid];
|
|
initiator = node_init_node_access(init_node, access);
|
|
target = node_init_node_access(targ_node, access);
|
|
if (!initiator || !target)
|
|
return -ENOMEM;
|
|
|
|
ret = sysfs_add_link_to_group(&initiator->dev.kobj, "targets",
|
|
&targ_node->dev.kobj,
|
|
dev_name(&targ_node->dev));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = sysfs_add_link_to_group(&target->dev.kobj, "initiators",
|
|
&init_node->dev.kobj,
|
|
dev_name(&init_node->dev));
|
|
if (ret)
|
|
goto err;
|
|
|
|
return 0;
|
|
err:
|
|
sysfs_remove_link_from_group(&initiator->dev.kobj, "targets",
|
|
dev_name(&targ_node->dev));
|
|
return ret;
|
|
}
|
|
|
|
int unregister_cpu_under_node(unsigned int cpu, unsigned int nid)
|
|
{
|
|
struct device *obj;
|
|
|
|
if (!node_online(nid))
|
|
return 0;
|
|
|
|
obj = get_cpu_device(cpu);
|
|
if (!obj)
|
|
return 0;
|
|
|
|
sysfs_remove_link(&node_devices[nid]->dev.kobj,
|
|
kobject_name(&obj->kobj));
|
|
sysfs_remove_link(&obj->kobj,
|
|
kobject_name(&node_devices[nid]->dev.kobj));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_MEMORY_HOTPLUG
|
|
static int __ref get_nid_for_pfn(unsigned long pfn)
|
|
{
|
|
#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
|
|
if (system_state < SYSTEM_RUNNING)
|
|
return early_pfn_to_nid(pfn);
|
|
#endif
|
|
return pfn_to_nid(pfn);
|
|
}
|
|
|
|
static void do_register_memory_block_under_node(int nid,
|
|
struct memory_block *mem_blk,
|
|
enum meminit_context context)
|
|
{
|
|
int ret;
|
|
|
|
memory_block_add_nid(mem_blk, nid, context);
|
|
|
|
ret = sysfs_create_link_nowarn(&node_devices[nid]->dev.kobj,
|
|
&mem_blk->dev.kobj,
|
|
kobject_name(&mem_blk->dev.kobj));
|
|
if (ret && ret != -EEXIST)
|
|
dev_err_ratelimited(&node_devices[nid]->dev,
|
|
"can't create link to %s in sysfs (%d)\n",
|
|
kobject_name(&mem_blk->dev.kobj), ret);
|
|
|
|
ret = sysfs_create_link_nowarn(&mem_blk->dev.kobj,
|
|
&node_devices[nid]->dev.kobj,
|
|
kobject_name(&node_devices[nid]->dev.kobj));
|
|
if (ret && ret != -EEXIST)
|
|
dev_err_ratelimited(&mem_blk->dev,
|
|
"can't create link to %s in sysfs (%d)\n",
|
|
kobject_name(&node_devices[nid]->dev.kobj),
|
|
ret);
|
|
}
|
|
|
|
/* register memory section under specified node if it spans that node */
|
|
static int register_mem_block_under_node_early(struct memory_block *mem_blk,
|
|
void *arg)
|
|
{
|
|
unsigned long memory_block_pfns = memory_block_size_bytes() / PAGE_SIZE;
|
|
unsigned long start_pfn = section_nr_to_pfn(mem_blk->start_section_nr);
|
|
unsigned long end_pfn = start_pfn + memory_block_pfns - 1;
|
|
int nid = *(int *)arg;
|
|
unsigned long pfn;
|
|
|
|
for (pfn = start_pfn; pfn <= end_pfn; pfn++) {
|
|
int page_nid;
|
|
|
|
/*
|
|
* memory block could have several absent sections from start.
|
|
* skip pfn range from absent section
|
|
*/
|
|
if (!pfn_in_present_section(pfn)) {
|
|
pfn = round_down(pfn + PAGES_PER_SECTION,
|
|
PAGES_PER_SECTION) - 1;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* We need to check if page belongs to nid only at the boot
|
|
* case because node's ranges can be interleaved.
|
|
*/
|
|
page_nid = get_nid_for_pfn(pfn);
|
|
if (page_nid < 0)
|
|
continue;
|
|
if (page_nid != nid)
|
|
continue;
|
|
|
|
do_register_memory_block_under_node(nid, mem_blk, MEMINIT_EARLY);
|
|
return 0;
|
|
}
|
|
/* mem section does not span the specified node */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* During hotplug we know that all pages in the memory block belong to the same
|
|
* node.
|
|
*/
|
|
static int register_mem_block_under_node_hotplug(struct memory_block *mem_blk,
|
|
void *arg)
|
|
{
|
|
int nid = *(int *)arg;
|
|
|
|
do_register_memory_block_under_node(nid, mem_blk, MEMINIT_HOTPLUG);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Unregister a memory block device under the node it spans. Memory blocks
|
|
* with multiple nodes cannot be offlined and therefore also never be removed.
|
|
*/
|
|
void unregister_memory_block_under_nodes(struct memory_block *mem_blk)
|
|
{
|
|
if (mem_blk->nid == NUMA_NO_NODE)
|
|
return;
|
|
|
|
sysfs_remove_link(&node_devices[mem_blk->nid]->dev.kobj,
|
|
kobject_name(&mem_blk->dev.kobj));
|
|
sysfs_remove_link(&mem_blk->dev.kobj,
|
|
kobject_name(&node_devices[mem_blk->nid]->dev.kobj));
|
|
}
|
|
|
|
void register_memory_blocks_under_node(int nid, unsigned long start_pfn,
|
|
unsigned long end_pfn,
|
|
enum meminit_context context)
|
|
{
|
|
walk_memory_blocks_func_t func;
|
|
|
|
if (context == MEMINIT_HOTPLUG)
|
|
func = register_mem_block_under_node_hotplug;
|
|
else
|
|
func = register_mem_block_under_node_early;
|
|
|
|
walk_memory_blocks(PFN_PHYS(start_pfn), PFN_PHYS(end_pfn - start_pfn),
|
|
(void *)&nid, func);
|
|
return;
|
|
}
|
|
#endif /* CONFIG_MEMORY_HOTPLUG */
|
|
|
|
int __register_one_node(int nid)
|
|
{
|
|
int error;
|
|
int cpu;
|
|
struct node *node;
|
|
|
|
node = kzalloc(sizeof(struct node), GFP_KERNEL);
|
|
if (!node)
|
|
return -ENOMEM;
|
|
|
|
INIT_LIST_HEAD(&node->access_list);
|
|
node_devices[nid] = node;
|
|
|
|
error = register_node(node_devices[nid], nid);
|
|
|
|
/* link cpu under this node */
|
|
for_each_present_cpu(cpu) {
|
|
if (cpu_to_node(cpu) == nid)
|
|
register_cpu_under_node(cpu, nid);
|
|
}
|
|
|
|
node_init_caches(nid);
|
|
|
|
return error;
|
|
}
|
|
|
|
void unregister_one_node(int nid)
|
|
{
|
|
if (!node_devices[nid])
|
|
return;
|
|
|
|
unregister_node(node_devices[nid]);
|
|
node_devices[nid] = NULL;
|
|
}
|
|
|
|
/*
|
|
* node states attributes
|
|
*/
|
|
|
|
struct node_attr {
|
|
struct device_attribute attr;
|
|
enum node_states state;
|
|
};
|
|
|
|
static ssize_t show_node_state(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct node_attr *na = container_of(attr, struct node_attr, attr);
|
|
|
|
return sysfs_emit(buf, "%*pbl\n",
|
|
nodemask_pr_args(&node_states[na->state]));
|
|
}
|
|
|
|
#define _NODE_ATTR(name, state) \
|
|
{ __ATTR(name, 0444, show_node_state, NULL), state }
|
|
|
|
static struct node_attr node_state_attr[] = {
|
|
[N_POSSIBLE] = _NODE_ATTR(possible, N_POSSIBLE),
|
|
[N_ONLINE] = _NODE_ATTR(online, N_ONLINE),
|
|
[N_NORMAL_MEMORY] = _NODE_ATTR(has_normal_memory, N_NORMAL_MEMORY),
|
|
#ifdef CONFIG_HIGHMEM
|
|
[N_HIGH_MEMORY] = _NODE_ATTR(has_high_memory, N_HIGH_MEMORY),
|
|
#endif
|
|
[N_MEMORY] = _NODE_ATTR(has_memory, N_MEMORY),
|
|
[N_CPU] = _NODE_ATTR(has_cpu, N_CPU),
|
|
[N_GENERIC_INITIATOR] = _NODE_ATTR(has_generic_initiator,
|
|
N_GENERIC_INITIATOR),
|
|
};
|
|
|
|
static struct attribute *node_state_attrs[] = {
|
|
&node_state_attr[N_POSSIBLE].attr.attr,
|
|
&node_state_attr[N_ONLINE].attr.attr,
|
|
&node_state_attr[N_NORMAL_MEMORY].attr.attr,
|
|
#ifdef CONFIG_HIGHMEM
|
|
&node_state_attr[N_HIGH_MEMORY].attr.attr,
|
|
#endif
|
|
&node_state_attr[N_MEMORY].attr.attr,
|
|
&node_state_attr[N_CPU].attr.attr,
|
|
&node_state_attr[N_GENERIC_INITIATOR].attr.attr,
|
|
NULL
|
|
};
|
|
|
|
static const struct attribute_group memory_root_attr_group = {
|
|
.attrs = node_state_attrs,
|
|
};
|
|
|
|
static const struct attribute_group *cpu_root_attr_groups[] = {
|
|
&memory_root_attr_group,
|
|
NULL,
|
|
};
|
|
|
|
void __init node_dev_init(void)
|
|
{
|
|
int ret, i;
|
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(node_state_attr) != NR_NODE_STATES);
|
|
BUILD_BUG_ON(ARRAY_SIZE(node_state_attrs)-1 != NR_NODE_STATES);
|
|
|
|
ret = subsys_system_register(&node_subsys, cpu_root_attr_groups);
|
|
if (ret)
|
|
panic("%s() failed to register subsystem: %d\n", __func__, ret);
|
|
|
|
/*
|
|
* Create all node devices, which will properly link the node
|
|
* to applicable memory block devices and already created cpu devices.
|
|
*/
|
|
for_each_online_node(i) {
|
|
ret = register_one_node(i);
|
|
if (ret)
|
|
panic("%s() failed to add node: %d\n", __func__, ret);
|
|
}
|
|
}
|