1088 lines · 28.2 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 inline ssize_t cpumap_read(struct file *file, struct kobject *kobj,				  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 BIN_ATTR_RO(cpumap, 0); static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj,				   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 BIN_ATTR_RO(cpulist, 0); /** * 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 int		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 int 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 sysfs_emit(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 int 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 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") 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; 	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);}#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 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 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			     ,			     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, 0UL,			     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)),			     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			    );	len += hugetlb_report_node_meminfo(buf, len, nid);	return len;} #undef Kstatic 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),

Lines 1–500 of 1088.