1029 lines · 26.8 KB · C
RawDownload
// 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/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 struct bus_type node_subsys = {	.name = "node",	.dev_name = "node",};  static ssize_t node_read_cpumap(struct device *dev, bool list, char *buf){	ssize_t n;	cpumask_var_t mask;	struct node *node_dev = to_node(dev); 	/* 2008/04/07: buf currently PAGE_SIZE, need 9 chars per 32 bits. */	BUILD_BUG_ON((NR_CPUS/32 * 9) > (PAGE_SIZE-1)); 	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_to_pagebuf(list, buf, mask);	free_cpumask_var(mask); 	return n;} static inline ssize_t node_read_cpumask(struct device *dev,				struct device_attribute *attr, char *buf){	return node_read_cpumap(dev, false, buf);}static inline ssize_t node_read_cpulist(struct device *dev,				struct device_attribute *attr, char *buf){	return node_read_cpumap(dev, true, buf);} static DEVICE_ATTR(cpumap,  S_IRUGO, node_read_cpumask, NULL);static DEVICE_ATTR(cpulist, S_IRUGO, node_read_cpulist, NULL); /** * 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 * @hmem_attrs: Heterogeneous memory performance attributes */struct node_access_nodes {	struct device		dev;	struct list_head	list_node;	unsigned		access;#ifdef CONFIG_HMEM_REPORTING	struct node_hmem_attrs	hmem_attrs;#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,						       unsigned 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(name) 						   \static ssize_t name##_show(struct device *dev,				   \			   struct device_attribute *attr,		   \			   char *buf)					   \{									   \	return sprintf(buf, "%u\n", to_access_nodes(dev)->hmem_attrs.name); \}									   \static DEVICE_ATTR_RO(name); 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 * @hmem_attrs: Heterogeneous memory performance attributes * @access: The access class the for the given attributes */void node_set_perf_attrs(unsigned int nid, struct node_hmem_attrs *hmem_attrs,			 unsigned 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->hmem_attrs = *hmem_attrs;	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;		}	}} /** * 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 sprintf(buf, fmt "\n", to_cache_info(dev)->cache_attrs.name);\}									\DEVICE_ATTR_RO(name); CACHE_ATTR(size, "%llu")CACHE_ATTR(line_size, "%u")CACHE_ATTR(indexing, "%u")CACHE_ATTR(write_policy, "%u") static struct attribute *cache_attrs[] = {	&dev_attr_indexing.attr,	&dev_attr_size.attr,	&dev_attr_line_size.attr,	&dev_attr_write_policy.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; 	dev->parent = &node->dev;	dev->release = node_cache_release;	if (dev_set_name(dev, "memory_side_cache"))		goto free_dev; 	if (device_register(dev))		goto free_name; 	pm_runtime_no_callbacks(dev);	node->cache_dev = dev;	return;free_name:	kfree_const(dev->kobj.name);free_dev:	kfree(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;	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 free_cache; 	info->cache_attrs = *cache_attrs;	if (device_register(dev)) {		dev_warn(&node->dev, "failed to add cache level:%d\n",			 cache_attrs->level);		goto free_name;	}	pm_runtime_no_callbacks(dev);	list_add_tail(&info->node, &node->cache_attrs);	return;free_name:	kfree_const(dev->kobj.name);free_cache:	kfree(info);} 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);}#elsestatic 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 n;	int nid = dev->id;	struct pglist_data *pgdat = NODE_DATA(nid);	struct sysinfo i;	unsigned long sreclaimable, sunreclaimable; 	si_meminfo_node(&i, nid);	sreclaimable = node_page_state(pgdat, NR_SLAB_RECLAIMABLE);	sunreclaimable = node_page_state(pgdat, NR_SLAB_UNRECLAIMABLE);	n = sprintf(buf,		       "Node %d MemTotal:       %8lu kB\n"		       "Node %d MemFree:        %8lu kB\n"		       "Node %d MemUsed:        %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(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	n += sprintf(buf + n,		       "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	n += sprintf(buf + n,		       "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"		       "Node %d PageTables:     %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"#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, sum_zone_node_page_state(nid, NR_KERNEL_STACK_KB),		       nid, K(sum_zone_node_page_state(nid, NR_PAGETABLE)),		       nid, K(node_page_state(pgdat, NR_UNSTABLE_NFS)),		       nid, K(sum_zone_node_page_state(nid, NR_BOUNCE)),		       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) *				       HPAGE_PMD_NR),		       nid, K(node_page_state(pgdat, NR_SHMEM_THPS) *				       HPAGE_PMD_NR),		       nid, K(node_page_state(pgdat, NR_SHMEM_PMDMAPPED) *				       HPAGE_PMD_NR)#endif		       );	n += hugetlb_report_node_meminfo(nid, buf + n);	return n;} #undef Kstatic DEVICE_ATTR(meminfo, S_IRUGO, node_read_meminfo, NULL); static ssize_t node_read_numastat(struct device *dev,				struct device_attribute *attr, char *buf){	return sprintf(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_state(dev->id, NUMA_HIT),		       sum_zone_numa_state(dev->id, NUMA_MISS),		       sum_zone_numa_state(dev->id, NUMA_FOREIGN),		       sum_zone_numa_state(dev->id, NUMA_INTERLEAVE_HIT),		       sum_zone_numa_state(dev->id, NUMA_LOCAL),		       sum_zone_numa_state(dev->id, NUMA_OTHER));}static DEVICE_ATTR(numastat, S_IRUGO, 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 n = 0; 	for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)		n += sprintf(buf+n, "%s %lu\n", vmstat_text[i],			     sum_zone_node_page_state(nid, i)); #ifdef CONFIG_NUMA	for (i = 0; i < NR_VM_NUMA_STAT_ITEMS; i++)		n += sprintf(buf+n, "%s %lu\n",			     vmstat_text[i + NR_VM_ZONE_STAT_ITEMS],			     sum_zone_numa_state(nid, i));

Lines 1–500 of 1029.