Add page_state info to the per-node meminfo file in sysfs. This is mostly just for informational purposes. The lack of this information was brought up recently during a discussion regarding pagecache clearing, and I put this patch together to test out one of the suggestions. It seems like interesting info to have, so I'm submitting the patch.
Unified · Split
| @@ -39,13 +39,25 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) · 38 unmodified lines | ||
| 39 | 39 | int n; |
| 40 | 40 | int nid = dev->id; |
| 41 | 41 | struct sysinfo i; |
| 42 | + struct page_state ps; | |
| 42 | 43 | unsigned long inactive; |
| 43 | 44 | unsigned long active; |
| 44 | 45 | unsigned long free; |
| 45 | 46 | |
| 46 | 47 | si_meminfo_node(&i, nid); |
| 48 | + get_page_state_node(&ps, nid); | |
| 47 | 49 | __get_zone_counts(&active, &inactive, &free, NODE_DATA(nid)); |
| 48 | 50 | |
| 51 | + /* Check for negative values in these approximate counters */ | |
| 52 | + if ((long)ps.nr_dirty < 0) | |
| 53 | + ps.nr_dirty = 0; | |
| 54 | + if ((long)ps.nr_writeback < 0) | |
| 55 | + ps.nr_writeback = 0; | |
| 56 | + if ((long)ps.nr_mapped < 0) | |
| 57 | + ps.nr_mapped = 0; | |
| 58 | + if ((long)ps.nr_slab < 0) | |
| 59 | + ps.nr_slab = 0; | |
| 60 | + | |
| 49 | 61 | n = sprintf(buf, "\n" |
| 50 | 62 | "Node %d MemTotal: %8lu kB\n" |
| 51 | 63 | "Node %d MemFree: %8lu kB\n" |
| @@ -55,7 +67,11 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) · 3 unmodified lines | ||
| 55 | 67 | "Node %d HighTotal: %8lu kB\n" |
| 56 | 68 | "Node %d HighFree: %8lu kB\n" |
| 57 | 69 | "Node %d LowTotal: %8lu kB\n" |
| 58 | − "Node %d LowFree: %8lu kB\n", | |
| 70 | + "Node %d LowFree: %8lu kB\n" | |
| 71 | + "Node %d Dirty: %8lu kB\n" | |
| 72 | + "Node %d Writeback: %8lu kB\n" | |
| 73 | + "Node %d Mapped: %8lu kB\n" | |
| 74 | + "Node %d Slab: %8lu kB\n", | |
| 59 | 75 | nid, K(i.totalram), |
| 60 | 76 | nid, K(i.freeram), |
| 61 | 77 | nid, K(i.totalram - i.freeram), |
| @@ -64,7 +80,11 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) · 2 unmodified lines | ||
| 64 | 80 | nid, K(i.totalhigh), |
| 65 | 81 | nid, K(i.freehigh), |
| 66 | 82 | nid, K(i.totalram - i.totalhigh), |
| 67 | − nid, K(i.freeram - i.freehigh)); | |
| 83 | + nid, K(i.freeram - i.freehigh), | |
| 84 | + nid, K(ps.nr_dirty), | |
| 85 | + nid, K(ps.nr_writeback), | |
| 86 | + nid, K(ps.nr_mapped), | |
| 87 | + nid, K(ps.nr_slab)); | |
| 68 | 88 | n += hugetlb_report_node_meminfo(nid, buf + n); |
| 69 | 89 | return n; |
| 70 | 90 | } |
| @@ -134,6 +134,7 @@ struct page_state { · 133 unmodified lines | ||
| 134 | 134 | }; |
| 135 | 135 | |
| 136 | 136 | extern void get_page_state(struct page_state *ret); |
| 137 | +extern void get_page_state_node(struct page_state *ret, int node); | |
| 137 | 138 | extern void get_full_page_state(struct page_state *ret); |
| 138 | 139 | extern unsigned long __read_page_state(unsigned long offset); |
| 139 | 140 | extern void __mod_page_state(unsigned long offset, unsigned long delta); |
| @@ -1130,19 +1130,20 @@ EXPORT_SYMBOL(nr_pagecache); · 1129 unmodified lines | ||
| 1130 | 1130 | DEFINE_PER_CPU(long, nr_pagecache_local) = 0; |
| 1131 | 1131 | #endif |
| 1132 | 1132 | |
| 1133 | −void __get_page_state(struct page_state *ret, int nr) | |
| 1133 | +void __get_page_state(struct page_state *ret, int nr, cpumask_t *cpumask) | |
| 1134 | 1134 | { |
| 1135 | 1135 | int cpu = 0; |
| 1136 | 1136 | |
| 1137 | 1137 | memset(ret, 0, sizeof(*ret)); |
| 1138 | + cpus_and(*cpumask, *cpumask, cpu_online_map); | |
| 1138 | 1139 | |
| 1139 | − cpu = first_cpu(cpu_online_map); | |
| 1140 | + cpu = first_cpu(*cpumask); | |
| 1140 | 1141 | while (cpu < NR_CPUS) { |
| 1141 | 1142 | unsigned long *in, *out, off; |
| 1142 | 1143 | |
| 1143 | 1144 | in = (unsigned long *)&per_cpu(page_states, cpu); |
| 1144 | 1145 | |
| 1145 | − cpu = next_cpu(cpu, cpu_online_map); | |
| 1146 | + cpu = next_cpu(cpu, *cpumask); | |
| 1146 | 1147 | |
| 1147 | 1148 | if (cpu < NR_CPUS) |
| 1148 | 1149 | prefetch(&per_cpu(page_states, cpu)); |
| @@ -1153,19 +1154,33 @@ void __get_page_state(struct page_state *ret, int nr) · 4 unmodified lines | ||
| 1153 | 1154 | } |
| 1154 | 1155 | } |
| 1155 | 1156 | |
| 1157 | +void get_page_state_node(struct page_state *ret, int node) | |
| 1158 | +{ | |
| 1159 | + int nr; | |
| 1160 | + cpumask_t mask = node_to_cpumask(node); | |
| 1161 | + | |
| 1162 | + nr = offsetof(struct page_state, GET_PAGE_STATE_LAST); | |
| 1163 | + nr /= sizeof(unsigned long); | |
| 1164 | + | |
| 1165 | + __get_page_state(ret, nr+1, &mask); | |
| 1166 | +} | |
| 1167 | + | |
| 1156 | 1168 | void get_page_state(struct page_state *ret) |
| 1157 | 1169 | { |
| 1158 | 1170 | int nr; |
| 1171 | + cpumask_t mask = CPU_MASK_ALL; | |
| 1159 | 1172 | |
| 1160 | 1173 | nr = offsetof(struct page_state, GET_PAGE_STATE_LAST); |
| 1161 | 1174 | nr /= sizeof(unsigned long); |
| 1162 | 1175 | |
| 1163 | − __get_page_state(ret, nr + 1); | |
| 1176 | + __get_page_state(ret, nr + 1, &mask); | |
| 1164 | 1177 | } |
| 1165 | 1178 | |
| 1166 | 1179 | void get_full_page_state(struct page_state *ret) |
| 1167 | 1180 | { |
| 1168 | − __get_page_state(ret, sizeof(*ret) / sizeof(unsigned long)); | |
| 1181 | + cpumask_t mask = CPU_MASK_ALL; | |
| 1182 | + | |
| 1183 | + __get_page_state(ret, sizeof(*ret) / sizeof(unsigned long), &mask); | |
| 1169 | 1184 | } |
| 1170 | 1185 | |
| 1171 | 1186 | unsigned long __read_page_state(unsigned long offset) |