for (i = 0; i < NR_VM_EVENT_ITEMS; i++)
ret[i] += this->event[i];
}
}
/* * Accumulate the vm event counters across all CPUs. * The result is unavoidably approximate - it can change * during and after execution of this function.
*/ void all_vm_events(unsignedlong *ret)
{
cpus_read_lock();
sum_vm_events(ret);
cpus_read_unlock();
}
EXPORT_SYMBOL_GPL(all_vm_events);
/* * Fold the foreign cpu events into our own. * * This is adding to the events on one processor * but keeps the global counts constant.
*/ void vm_events_fold_cpu(int cpu)
{ struct vm_event_state *fold_state = &per_cpu(vm_event_states, cpu); int i;
for (i = 0; i < NR_VM_EVENT_ITEMS; i++) {
count_vm_events(i, fold_state->event[i]);
fold_state->event[i] = 0;
}
}
#endif/* CONFIG_VM_EVENT_COUNTERS */
/* * Manage combined zone based / global counters * * vm_stat contains the global counters
*/
atomic_long_t vm_zone_stat[NR_VM_ZONE_STAT_ITEMS] __cacheline_aligned_in_smp;
atomic_long_t vm_node_stat[NR_VM_NODE_STAT_ITEMS] __cacheline_aligned_in_smp;
atomic_long_t vm_numa_event[NR_VM_NUMA_EVENT_ITEMS] __cacheline_aligned_in_smp;
EXPORT_SYMBOL(vm_zone_stat);
EXPORT_SYMBOL(vm_node_stat);
#ifdef CONFIG_NUMA staticvoid fold_vm_zone_numa_events(struct zone *zone)
{ unsignedlong zone_numa_events[NR_VM_NUMA_EVENT_ITEMS] = { 0, }; int cpu; enum numa_stat_item item;
int calculate_pressure_threshold(struct zone *zone)
{ int threshold; int watermark_distance;
/* * As vmstats are not up to date, there is drift between the estimated * and real values. For high thresholds and a high number of CPUs, it * is possible for the min watermark to be breached while the estimated * value looks fine. The pressure threshold is a reduced value such * that even the maximum amount of drift will not accidentally breach * the min watermark
*/
watermark_distance = low_wmark_pages(zone) - min_wmark_pages(zone);
threshold = max(1, (int)(watermark_distance / num_online_cpus()));
/* * Maximum threshold is 125
*/
threshold = min(125, threshold);
return threshold;
}
int calculate_normal_threshold(struct zone *zone)
{ int threshold; int mem; /* memory in 128 MB units */
/* * The threshold scales with the number of processors and the amount * of memory per zone. More memory means that we can defer updates for * longer, more processors could lead to more contention. * fls() is used to have a cheap way of logarithmic scaling. * * Some sample thresholds: * * Threshold Processors (fls) Zonesize fls(mem)+1 * ------------------------------------------------------------------ * 8 1 1 0.9-1 GB 4 * 16 2 2 0.9-1 GB 4 * 20 2 2 1-2 GB 5 * 24 2 2 2-4 GB 6 * 28 2 2 4-8 GB 7 * 32 2 2 8-16 GB 8 * 4 2 2 <128M 1 * 30 4 3 2-4 GB 5 * 48 4 3 8-16 GB 8 * 32 8 4 1-2 GB 4 * 32 8 4 0.9-1GB 4 * 10 16 5 <128M 1 * 40 16 5 900M 4 * 70 64 7 2-4 GB 5 * 84 64 7 4-8 GB 6 * 108 512 9 4-8 GB 6 * 125 1024 10 8-16 GB 8 * 125 1024 10 16-32 GB 9
*/
mem = zone_managed_pages(zone) >> (27 - PAGE_SHIFT);
/* * Maximum threshold is 125
*/
threshold = min(125, threshold);
return threshold;
}
/* * Refresh the thresholds for each zone.
*/ void refresh_zone_stat_thresholds(void)
{ struct pglist_data *pgdat; struct zone *zone; int cpu; int threshold;
/* Zero current pgdat thresholds */
for_each_online_pgdat(pgdat) {
for_each_online_cpu(cpu) {
per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold = 0;
}
}
/* Base nodestat threshold on the largest populated zone. */
pgdat_threshold = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold;
per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold
= max(threshold, pgdat_threshold);
}
/* * Only set percpu_drift_mark if there is a danger that * NR_FREE_PAGES reports the low watermark is ok when in fact * the min watermark could be breached by an allocation
*/
tolerate_drift = low_wmark_pages(zone) - min_wmark_pages(zone);
max_drift = num_online_cpus() * threshold; if (max_drift > tolerate_drift)
zone->percpu_drift_mark = high_wmark_pages(zone) +
max_drift;
}
}
void set_pgdat_percpu_threshold(pg_data_t *pgdat, int (*calculate_pressure)(struct zone *))
{ struct zone *zone; int cpu; int threshold; int i;
for (i = 0; i < pgdat->nr_zones; i++) {
zone = &pgdat->node_zones[i]; if (!zone->percpu_drift_mark) continue;
/* * For use when we know that interrupts are disabled, * or when we know that preemption is disabled and that * particular counter cannot be updated from interrupt context.
*/ void __mod_zone_page_state(struct zone *zone, enum zone_stat_item item, long delta)
{ struct per_cpu_zonestat __percpu *pcp = zone->per_cpu_zonestats;
s8 __percpu *p = pcp->vm_stat_diff + item; long x; long t;
/* * Accurate vmstat updates require a RMW. On !PREEMPT_RT kernels, * atomicity is provided by IRQs being disabled -- either explicitly * or via local_lock_irq. On PREEMPT_RT, local_lock_irq only disables * CPU migrations and preemption potentially corrupts a counter so * disable preemption.
*/
preempt_disable_nested();
x = delta + __this_cpu_read(*p);
t = __this_cpu_read(pcp->stat_threshold);
if (unlikely(abs(x) > t)) {
zone_page_state_add(x, zone, item);
x = 0;
}
__this_cpu_write(*p, x);
void __mod_node_page_state(struct pglist_data *pgdat, enum node_stat_item item, long delta)
{ struct per_cpu_nodestat __percpu *pcp = pgdat->per_cpu_nodestats;
s8 __percpu *p = pcp->vm_node_stat_diff + item; long x; long t;
if (vmstat_item_in_bytes(item)) { /* * Only cgroups use subpage accounting right now; at * the global level, these items still change in * multiples of whole pages. Store them as pages * internally to keep the per-cpu counters compact.
*/
VM_WARN_ON_ONCE(delta & (PAGE_SIZE - 1));
delta >>= PAGE_SHIFT;
}
/* See __mod_node_page_state */
preempt_disable_nested();
x = delta + __this_cpu_read(*p);
t = __this_cpu_read(pcp->stat_threshold);
if (unlikely(abs(x) > t)) {
node_page_state_add(x, pgdat, item);
x = 0;
}
__this_cpu_write(*p, x);
/* * Optimized increment and decrement functions. * * These are only for a single page and therefore can take a struct page * * argument instead of struct zone *. This allows the inclusion of the code * generated for page_zone(page) into the optimized functions. * * No overflow check is necessary and therefore the differential can be * incremented or decremented in place which may allow the compilers to * generate better code. * The increment or decrement is known and therefore one boundary check can * be omitted. * * NOTE: These functions are very performance sensitive. Change only * with care. * * Some processors have inc/dec instructions that are atomic vs an interrupt. * However, the code must first determine the differential location in a zone * based on the processor number and then inc/dec the counter. There is no * guarantee without disabling preemption that the processor will not change * in between and therefore the atomicity vs. interrupt cannot be exploited * in a useful way here.
*/ void __inc_zone_state(struct zone *zone, enum zone_stat_item item)
{ struct per_cpu_zonestat __percpu *pcp = zone->per_cpu_zonestats;
s8 __percpu *p = pcp->vm_stat_diff + item;
s8 v, t;
/* See __mod_node_page_state */
preempt_disable_nested();
v = __this_cpu_inc_return(*p);
t = __this_cpu_read(pcp->stat_threshold); if (unlikely(v > t)) {
s8 overstep = t >> 1;
#ifdef CONFIG_HAVE_CMPXCHG_LOCAL /* * If we have cmpxchg_local support then we do not need to incur the overhead * that comes with local_irq_save/restore if we use this_cpu_cmpxchg. * * mod_state() modifies the zone counter state through atomic per cpu * operations. * * Overstep mode specifies how overstep should handled: * 0 No overstepping * 1 Overstepping half of threshold * -1 Overstepping minus half of threshold
*/ staticinlinevoid mod_zone_state(struct zone *zone, enum zone_stat_item item, long delta, int overstep_mode)
{ struct per_cpu_zonestat __percpu *pcp = zone->per_cpu_zonestats;
s8 __percpu *p = pcp->vm_stat_diff + item; long n, t, z;
s8 o;
o = this_cpu_read(*p); do {
z = 0; /* overflow to zone counters */
/* * The fetching of the stat_threshold is racy. We may apply * a counter threshold to the wrong the cpu if we get * rescheduled while executing here. However, the next * counter update will apply the threshold again and * therefore bring the counter under the threshold again. * * Most of the time the thresholds are the same anyways * for all cpus in a zone.
*/
t = this_cpu_read(pcp->stat_threshold);
n = delta + (long)o;
if (abs(n) > t) { int os = overstep_mode * (t >> 1) ;
/* Overflow must be added to zone counters */
z = n + os;
n = -os;
}
} while (!this_cpu_try_cmpxchg(*p, &o, n));
if (z)
zone_page_state_add(z, zone, item);
}
void mod_zone_page_state(struct zone *zone, enum zone_stat_item item, long delta)
{
mod_zone_state(zone, item, delta, 0);
}
EXPORT_SYMBOL(mod_zone_page_state);
staticinlinevoid mod_node_state(struct pglist_data *pgdat, enum node_stat_item item, int delta, int overstep_mode)
{ struct per_cpu_nodestat __percpu *pcp = pgdat->per_cpu_nodestats;
s8 __percpu *p = pcp->vm_node_stat_diff + item; long n, t, z;
s8 o;
if (vmstat_item_in_bytes(item)) { /* * Only cgroups use subpage accounting right now; at * the global level, these items still change in * multiples of whole pages. Store them as pages * internally to keep the per-cpu counters compact.
*/
VM_WARN_ON_ONCE(delta & (PAGE_SIZE - 1));
delta >>= PAGE_SHIFT;
}
o = this_cpu_read(*p); do {
z = 0; /* overflow to node counters */
/* * The fetching of the stat_threshold is racy. We may apply * a counter threshold to the wrong the cpu if we get * rescheduled while executing here. However, the next * counter update will apply the threshold again and * therefore bring the counter under the threshold again. * * Most of the time the thresholds are the same anyways * for all cpus in a node.
*/
t = this_cpu_read(pcp->stat_threshold);
n = delta + (long)o;
if (abs(n) > t) { int os = overstep_mode * (t >> 1) ;
/* Overflow must be added to node counters */
z = n + os;
n = -os;
}
} while (!this_cpu_try_cmpxchg(*p, &o, n));
/* * Fold a differential into the global counters. * Returns the number of counters updated.
*/ staticint fold_diff(int *zone_diff, int *node_diff)
{ int i; int changes = 0;
for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) if (zone_diff[i]) {
atomic_long_add(zone_diff[i], &vm_zone_stat[i]);
changes++;
}
for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) if (node_diff[i]) {
atomic_long_add(node_diff[i], &vm_node_stat[i]);
changes++;
} return changes;
}
/* * Update the zone counters for the current cpu. * * Note that refresh_cpu_vm_stats strives to only access * node local memory. The per cpu pagesets on remote zones are placed * in the memory local to the processor using that pageset. So the * loop over all zones will access a series of cachelines local to * the processor. * * The call to zone_page_state_add updates the cachelines with the * statistics in the remote zone struct as well as the global cachelines * with the global counters. These could cause remote node cache line * bouncing and will have to be only done when necessary. * * The function returns the number of global counters updated.
*/ staticint refresh_cpu_vm_stats(bool do_pagesets)
{ struct pglist_data *pgdat; struct zone *zone; int i; int global_zone_diff[NR_VM_ZONE_STAT_ITEMS] = { 0, }; int global_node_diff[NR_VM_NODE_STAT_ITEMS] = { 0, }; int changes = 0;
changes += decay_pcp_high(zone, this_cpu_ptr(pcp)); #ifdef CONFIG_NUMA /* * Deal with draining the remote pageset of this * processor * * Check if there are pages remaining in this pageset * if not then there is nothing to expire.
*/ if (!__this_cpu_read(pcp->expire) ||
!__this_cpu_read(pcp->count)) continue;
/* * We never drain zones local to this processor.
*/ if (zone_to_nid(zone) == numa_node_id()) {
__this_cpu_write(pcp->expire, 0); continue;
}
if (__this_cpu_dec_return(pcp->expire)) {
changes++; continue;
}
if (__this_cpu_read(pcp->count)) {
drain_zone_pages(zone, this_cpu_ptr(pcp));
changes++;
} #endif
}
}
/* * Fold the data for an offline cpu into the global array. * There cannot be any access by the offline cpu and therefore * synchronization is simplified.
*/ void cpu_vm_stats_fold(int cpu)
{ struct pglist_data *pgdat; struct zone *zone; int i; int global_zone_diff[NR_VM_ZONE_STAT_ITEMS] = { 0, }; int global_node_diff[NR_VM_NODE_STAT_ITEMS] = { 0, };
/* * this is only called if !populated_zone(zone), which implies no other users of * pset->vm_stat_diff[] exist.
*/ void drain_zonestat(struct zone *zone, struct per_cpu_zonestat *pzstats)
{ unsignedlong v; int i;
for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) { if (pzstats->vm_stat_diff[i]) {
v = pzstats->vm_stat_diff[i];
pzstats->vm_stat_diff[i] = 0;
zone_page_state_add(v, zone, i);
}
}
#ifdef CONFIG_NUMA for (i = 0; i < NR_VM_NUMA_EVENT_ITEMS; i++) { if (pzstats->vm_numa_event[i]) {
v = pzstats->vm_numa_event[i];
pzstats->vm_numa_event[i] = 0;
zone_numa_event_add(v, zone, i);
}
} #endif
} #endif
#ifdef CONFIG_NUMA /* * Determine the per node value of a stat item. This function * is called frequently in a NUMA machine, so try to be as * frugal as possible.
*/ unsignedlong sum_zone_node_page_state(int node, enum zone_stat_item item)
{ struct zone *zones = NODE_DATA(node)->node_zones; int i; unsignedlong count = 0;
for (i = 0; i < MAX_NR_ZONES; i++)
count += zone_page_state(zones + i, item);
return count;
}
/* Determine the per node value of a numa stat item. */ unsignedlong sum_zone_numa_event_state(int node, enum numa_stat_item item)
{ struct zone *zones = NODE_DATA(node)->node_zones; unsignedlong count = 0; int i;
for (i = 0; i < MAX_NR_ZONES; i++)
count += zone_numa_event_state(zones + i, item);
return count;
}
/* * Determine the per node value of a stat item.
*/ unsignedlong node_page_state_pages(struct pglist_data *pgdat, enum node_stat_item item)
{ long x = atomic_long_read(&pgdat->vm_stat[item]); #ifdef CONFIG_SMP if (x < 0)
x = 0; #endif return x;
}
/* * Count number of pages "struct page" and "struct page_ext" consume. * nr_memmap_boot_pages: # of pages allocated by boot allocator * nr_memmap_pages: # of pages that were allocated by buddy allocator
*/ static atomic_long_t nr_memmap_boot_pages = ATOMIC_LONG_INIT(0); static atomic_long_t nr_memmap_pages = ATOMIC_LONG_INIT(0);
/* * Calculate the number of free pages in a zone, how many contiguous * pages are free and how many are large enough to satisfy an allocation of * the target size. Note that this function makes no attempt to estimate * how many suitable free blocks there *might* be if MOVABLE pages were * migrated. Calculating that is possible, but expensive and can be * figured out from userspace
*/ staticvoid fill_contig_page_info(struct zone *zone, unsignedint suitable_order, struct contig_page_info *info)
{ unsignedint order;
for (order = 0; order < NR_PAGE_ORDERS; order++) { unsignedlong blocks;
/* * Count number of free blocks. * * Access to nr_free is lockless as nr_free is used only for * diagnostic purposes. Use data_race to avoid KCSAN warning.
*/
blocks = data_race(zone->free_area[order].nr_free);
info->free_blocks_total += blocks;
/* Count the suitable free blocks */ if (order >= suitable_order)
info->free_blocks_suitable += blocks <<
(order - suitable_order);
}
}
/* * A fragmentation index only makes sense if an allocation of a requested * size would fail. If that is true, the fragmentation index indicates * whether external fragmentation or a lack of memory was the problem. * The value can be used to determine if page reclaim or compaction * should be used
*/ staticint __fragmentation_index(unsignedint order, struct contig_page_info *info)
{ unsignedlong requested = 1UL << order;
if (WARN_ON_ONCE(order > MAX_PAGE_ORDER)) return 0;
if (!info->free_blocks_total) return 0;
/* Fragmentation index only makes sense when a request would fail */ if (info->free_blocks_suitable) return -1000;
/* * Index is between 0 and 1 so return within 3 decimal places * * 0 => allocation would fail due to lack of memory * 1 => allocation would fail due to fragmentation
*/ return 1000 - div_u64( (1000+(div_u64(info->free_pages * 1000ULL, requested))), info->free_blocks_total);
}
/* * Calculates external fragmentation within a zone wrt the given order. * It is defined as the percentage of pages found in blocks of size * less than 1 << order. It returns values in range [0, 100].
*/ unsignedint extfrag_for_order(struct zone *zone, unsignedint order)
{ struct contig_page_info info;
fill_contig_page_info(zone, order, &info); if (info.free_pages == 0) return 0;
/* Same as __fragmentation index but allocs contig_page_info on stack */ int fragmentation_index(struct zone *zone, unsignedint order)
{ struct contig_page_info info;
/* * Walk zones in a node and print using a callback. * If @assert_populated is true, only use callback for zones that are populated.
*/ staticvoid walk_zones_in_node(struct seq_file *m, pg_data_t *pgdat, bool assert_populated, bool nolock, void (*print)(struct seq_file *m, pg_data_t *, struct zone *))
{ struct zone *zone; struct zone *node_zones = pgdat->node_zones; unsignedlong flags;
for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) { if (assert_populated && !populated_zone(zone)) continue;
if (!nolock)
spin_lock_irqsave(&zone->lock, flags);
print(m, pgdat, zone); if (!nolock)
spin_unlock_irqrestore(&zone->lock, flags);
}
} #endif
#ifdef CONFIG_PROC_FS staticvoid frag_show_print(struct seq_file *m, pg_data_t *pgdat, struct zone *zone)
{ int order;
seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name); for (order = 0; order < NR_PAGE_ORDERS; ++order) /* * Access to nr_free is lockless as nr_free is used only for * printing purposes. Use data_race to avoid KCSAN warning.
*/
seq_printf(m, "%6lu ", data_race(zone->free_area[order].nr_free));
seq_putc(m, '\n');
}
/* * This walks the free areas for each zone.
*/ staticint frag_show(struct seq_file *m, void *arg)
{
pg_data_t *pgdat = (pg_data_t *)arg;
walk_zones_in_node(m, pgdat, true, false, frag_show_print); return 0;
}
staticvoid pagetypeinfo_showfree_print(struct seq_file *m,
pg_data_t *pgdat, struct zone *zone)
{ int order, mtype;
for (mtype = 0; mtype < MIGRATE_TYPES; mtype++) {
seq_printf(m, "Node %4d, zone %8s, type %12s ",
pgdat->node_id,
zone->name,
migratetype_names[mtype]); for (order = 0; order < NR_PAGE_ORDERS; ++order) { unsignedlong freecount = 0; struct free_area *area; struct list_head *curr; bool overflow = false;
area = &(zone->free_area[order]);
list_for_each(curr, &area->free_list[mtype]) { /* * Cap the free_list iteration because it might * be really large and we are under a spinlock * so a long time spent here could trigger a * hard lockup detector. Anyway this is a * debugging tool so knowing there is a handful * of pages of this order should be more than * sufficient.
*/ if (++freecount >= 100000) {
overflow = true; break;
}
}
seq_printf(m, "%s%6lu ", overflow ? ">" : "", freecount);
spin_unlock_irq(&zone->lock);
cond_resched();
spin_lock_irq(&zone->lock);
}
seq_putc(m, '\n');
}
}
/* Print out the free pages at each order for each migatetype */ staticvoid pagetypeinfo_showfree(struct seq_file *m, void *arg)
{ int order;
pg_data_t *pgdat = (pg_data_t *)arg;
/* Print header */
seq_printf(m, "%-43s ", "Free pages count per migrate type at order"); for (order = 0; order < NR_PAGE_ORDERS; ++order)
seq_printf(m, "%6d ", order);
seq_putc(m, '\n');
/* Print out the number of pageblocks for each migratetype */ staticvoid pagetypeinfo_showblockcount(struct seq_file *m, void *arg)
{ int mtype;
pg_data_t *pgdat = (pg_data_t *)arg;
seq_printf(m, "\n%-23s", "Number of blocks type "); for (mtype = 0; mtype < MIGRATE_TYPES; mtype++)
seq_printf(m, "%12s ", migratetype_names[mtype]);
seq_putc(m, '\n');
walk_zones_in_node(m, pgdat, true, false,
pagetypeinfo_showblockcount_print);
}
/* * Print out the number of pageblocks for each migratetype that contain pages * of other types. This gives an indication of how well fallbacks are being * contained by rmqueue_fallback(). It requires information from PAGE_OWNER * to determine what is going on
*/ staticvoid pagetypeinfo_showmixedcount(struct seq_file *m, pg_data_t *pgdat)
{ #ifdef CONFIG_PAGE_OWNER int mtype;
if (!static_branch_unlikely(&page_owner_inited)) return;
/* * This prints out statistics in relation to grouping pages by mobility. * It is expensive to collect so do not constantly read the file.
*/ staticint pagetypeinfo_show(struct seq_file *m, void *arg)
{
pg_data_t *pgdat = (pg_data_t *)arg;
/* check memoryless node */ if (!node_state(pgdat->node_id, N_MEMORY)) return 0;
/* * Output information about zones in @pgdat. All zones are printed regardless * of whether they are populated or not: lowmem_reserve_ratio operates on the * set of all zones and userspace would not be aware of such zones if they are * suppressed here (zoneinfo displays the effect of lowmem_reserve_ratio).
*/ staticint zoneinfo_show(struct seq_file *m, void *arg)
{
pg_data_t *pgdat = (pg_data_t *)arg;
walk_zones_in_node(m, pgdat, false, false, zoneinfo_show_print); return 0;
}
staticconststruct seq_operations zoneinfo_op = {
.start = frag_start, /* iterate over all zones. The same as in
* fragmentation. */
.next = frag_next,
.stop = frag_stop,
.show = zoneinfo_show,
};
BUILD_BUG_ON(ARRAY_SIZE(vmstat_text) != NR_VMSTAT_ITEMS);
fold_vm_numa_events();
v = kmalloc_array(NR_VMSTAT_ITEMS, sizeof(unsignedlong), GFP_KERNEL);
m->private = v; if (!v) return ERR_PTR(-ENOMEM); for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
v[i] = global_zone_page_state(i);
v += NR_VM_ZONE_STAT_ITEMS;
#ifdef CONFIG_NUMA for (i = 0; i < NR_VM_NUMA_EVENT_ITEMS; i++)
v[i] = global_numa_event_state(i);
v += NR_VM_NUMA_EVENT_ITEMS; #endif
for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) {
v[i] = global_node_page_state_pages(i); if (vmstat_item_print_in_thp(i))
v[i] /= HPAGE_PMD_NR;
}
v += NR_VM_NODE_STAT_ITEMS;
global_dirty_limits(v + NR_DIRTY_BG_THRESHOLD,
v + NR_DIRTY_THRESHOLD);
v[NR_MEMMAP_PAGES] = atomic_long_read(&nr_memmap_pages);
v[NR_MEMMAP_BOOT_PAGES] = atomic_long_read(&nr_memmap_boot_pages);
v += NR_VM_STAT_ITEMS;
if (off == NR_VMSTAT_ITEMS - 1) { /* * We've come to the end - add any deprecated counters to avoid * breaking userspace which might depend on them being present.
*/
seq_puts(m, "nr_unstable 0\n");
} return 0;
}
staticint vmstat_refresh(conststruct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos)
{ long val; int err; int i;
/* * The regular update, every sysctl_stat_interval, may come later * than expected: leaving a significant amount in per_cpu buckets. * This is particularly misleading when checking a quantity of HUGE * pages, immediately after running a test. /proc/sys/vm/stat_refresh, * which can equally be echo'ed to or cat'ted from (by root), * can be used to update the stats just before reading them. * * Oh, and since global_zone_page_state() etc. are so careful to hide * transiently negative values, report an error here if any of * the stats is negative, so we know to go looking for imbalance.
*/
err = schedule_on_each_cpu(refresh_vm_stats); if (err) return err; for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) { /* * Skip checking stats known to go negative occasionally.
*/ switch (i) { case NR_ZONE_WRITE_PENDING: case NR_FREE_CMA_PAGES: continue;
}
val = atomic_long_read(&vm_zone_stat[i]); if (val < 0) {
pr_warn("%s: %s %ld\n",
__func__, zone_stat_name(i), val);
}
} for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) { /* * Skip checking stats known to go negative occasionally.
*/ switch (i) { case NR_WRITEBACK: continue;
}
val = atomic_long_read(&vm_node_stat[i]); if (val < 0) {
pr_warn("%s: %s %ld\n",
__func__, node_stat_name(i), val);
}
} if (write)
*ppos += *lenp; else
*lenp = 0; return 0;
} #endif/* CONFIG_PROC_FS */
staticvoid vmstat_update(struct work_struct *w)
{ if (refresh_cpu_vm_stats(true)) { /* * Counters were updated so we expect more updates * to occur in the future. Keep on running the * update worker thread.
*/
queue_delayed_work_on(smp_processor_id(), mm_percpu_wq,
this_cpu_ptr(&vmstat_work),
round_jiffies_relative(sysctl_stat_interval));
}
}
/* * Check if the diffs for a certain cpu indicate that * an update is needed.
*/ staticbool need_update(int cpu)
{
pg_data_t *last_pgdat = NULL; struct zone *zone;
/* * The fast way of checking if there are any vmstat diffs.
*/ if (memchr_inv(pzstats->vm_stat_diff, 0, sizeof(pzstats->vm_stat_diff))) returntrue;
if (last_pgdat == zone->zone_pgdat) continue;
last_pgdat = zone->zone_pgdat;
n = per_cpu_ptr(zone->zone_pgdat->per_cpu_nodestats, cpu); if (memchr_inv(n->vm_node_stat_diff, 0, sizeof(n->vm_node_stat_diff))) returntrue;
} returnfalse;
}
/* * Switch off vmstat processing and then fold all the remaining differentials * until the diffs stay at zero. The function is used by NOHZ and can only be * invoked when tick processing is not active.
*/ void quiet_vmstat(void)
{ if (system_state != SYSTEM_RUNNING) return;
if (!delayed_work_pending(this_cpu_ptr(&vmstat_work))) return;
if (!need_update(smp_processor_id())) return;
/* * Just refresh counters and do not care about the pending delayed * vmstat_update. It doesn't fire that often to matter and canceling * it would be too expensive from this path. * vmstat_shepherd will take care about that for us.
*/
refresh_cpu_vm_stats(false);
}
/* * Shepherd worker thread that checks the * differentials of processors that have their worker * threads for vm statistics updates disabled because of * inactivity.
*/ staticvoid vmstat_shepherd(struct work_struct *w);
staticvoid vmstat_shepherd(struct work_struct *w)
{ int cpu;
cpus_read_lock(); /* Check processors whose vmstat worker threads have been disabled */
for_each_online_cpu(cpu) { struct delayed_work *dw = &per_cpu(vmstat_work, cpu);
/* * In kernel users of vmstat counters either require the precise value and * they are using zone_page_state_snapshot interface or they can live with * an imprecision as the regular flushing can happen at arbitrary time and * cumulative error can grow (see calculate_normal_threshold). * * From that POV the regular flushing can be postponed for CPUs that have * been isolated from the kernel interference without critical * infrastructure ever noticing. Skip regular flushing from vmstat_shepherd * for all isolated CPUs to avoid interference with the isolated workload.
*/ if (cpu_is_isolated(cpu)) continue;
if (!delayed_work_pending(dw) && need_update(cpu))
queue_delayed_work_on(cpu, mm_percpu_wq, dw, 0);
/* * For secondary CPUs during CPU hotplug scenarios, * vmstat_cpu_online() will enable the work. * mm/vmstat:online enables and disables vmstat_work * symmetrically during CPU hotplug events.
*/ if (!cpu_online(cpu))
disable_delayed_work_sync(&per_cpu(vmstat_work, cpu));
}
/* * Return an index indicating how much of the available free memory is * unusable for an allocation of the requested size.
*/ staticint unusable_free_index(unsignedint order, struct contig_page_info *info)
{ /* No free memory is interpreted as all free memory is unusable */ if (info->free_pages == 0) return 1000;
/* * Index should be a value between 0 and 1. Return a value to 3 * decimal places. * * 0 => no fragmentation * 1 => high fragmentation
*/ return div_u64((info->free_pages - (info->free_blocks_suitable << order)) * 1000ULL, info->free_pages);
}
staticvoid unusable_show_print(struct seq_file *m,
pg_data_t *pgdat, struct zone *zone)
{ unsignedint order; int index; struct contig_page_info info;
seq_printf(m, "Node %d, zone %8s ",
pgdat->node_id,
zone->name); for (order = 0; order < NR_PAGE_ORDERS; ++order) {
fill_contig_page_info(zone, order, &info);
index = unusable_free_index(order, &info);
seq_printf(m, "%d.%03d ", index / 1000, index % 1000);
}
seq_putc(m, '\n');
}
/* * Display unusable free space index * * The unusable free space index measures how much of the available free * memory cannot be used to satisfy an allocation of a given size and is a * value between 0 and 1. The higher the value, the more of free memory is * unusable and by implication, the worse the external fragmentation is. This * can be expressed as a percentage by multiplying by 100.
*/ staticint unusable_show(struct seq_file *m, void *arg)
{
pg_data_t *pgdat = (pg_data_t *)arg;
/* check memoryless node */ if (!node_state(pgdat->node_id, N_MEMORY)) return 0;
staticvoid extfrag_show_print(struct seq_file *m,
pg_data_t *pgdat, struct zone *zone)
{ unsignedint order; int index;
/* Alloc on stack as interrupts are disabled for zone walk */ struct contig_page_info info;
seq_printf(m, "Node %d, zone %8s ",
pgdat->node_id,
zone->name); for (order = 0; order < NR_PAGE_ORDERS; ++order) {
fill_contig_page_info(zone, order, &info);
index = __fragmentation_index(order, &info);
seq_printf(m, "%2d.%03d ", index / 1000, index % 1000);
}
seq_putc(m, '\n');
}
/* * Display fragmentation index for orders that allocations would fail for
*/ staticint extfrag_show(struct seq_file *m, void *arg)
{
pg_data_t *pgdat = (pg_data_t *)arg;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.