This patch changes dump_kmeminfo() to report overcommit information similar
to that displayed under the proc/meminfo file. It may be useful to indicate
memory over commitment abuse, for example with forced vmcores from system
hangs due to shortage of memory. The intended output is as follows:
crash> kmem -i
PAGES TOTAL PERCENTAGE
TOTAL MEM 1965332 7.5 GB ----
FREE 78080 305 MB 3% of TOTAL MEM
USED 1887252 7.2 GB 96% of TOTAL MEM
SHARED 789954 3 GB 40% of TOTAL MEM
BUFFERS 110606 432.1 MB 5% of TOTAL MEM
CACHED 1212645 4.6 GB 61% of TOTAL MEM
SLAB 146563 572.5 MB 7% of TOTAL MEM
TOTAL SWAP 1970175 7.5 GB ----
SWAP USED 5 20 KB 0% of TOTAL SWAP
SWAP FREE 1970170 7.5 GB 99% of TOTAL SWAP
COMMIT LIMIT 2952841 11.3 GB ----
COMMITTED 1150595 4.4 GB 38% of TOTAL LIMIT
Tested under 3.16.4-200.fc20.x86_64 only.
Though this should work under RHEL4 (2.6.9-5) and above.
Signed-off-by: Aaron Tomlin <atomlin(a)redhat.com>
Suggested-by: Alexis Solanas <alexis(a)redhat.com>
---
defs.h | 2 +
help.c | 33 ++++++------
kernel.c | 11 ++++
memory.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------
symbols.c | 4 ++
5 files changed, 187 insertions(+), 44 deletions(-)
diff --git a/defs.h b/defs.h
index 2e52bc4..a37f10d 100644
--- a/defs.h
+++ b/defs.h
@@ -1923,6 +1923,8 @@ struct offset_table { /* stash of commonly-used
offsets */
long kernfs_node_parent;
long kmem_cache_cpu_partial;
long kmem_cache_cpu_cache;
+ long atomic_t_counter;
+ long percpu_counter_struct_count;
};
struct size_table { /* stash of commonly-used sizes */
diff --git a/help.c b/help.c
index 6aa3e20..bedd7c3 100644
--- a/help.c
+++ b/help.c
@@ -5587,23 +5587,26 @@ char *help_kmem[] = {
"\nEXAMPLES",
" Display memory usage information:\n",
" %s> kmem -i",
-" PAGES TOTAL PERCENTAGE",
-" TOTAL MEM 63602 248.4 MB ----",
-" FREE 993 3.9 MB 1% of TOTAL MEM",
-" USED 62609 244.6 MB 98% of TOTAL MEM",
-" SHARED 34035 132.9 MB 53% of TOTAL MEM",
-" BUFFERS 10928 42.7 MB 17% of TOTAL MEM",
-" CACHED 35249 137.7 MB 55% of TOTAL MEM",
-" SLAB 2823 11 MB 4% of TOTAL MEM",
+" PAGES TOTAL PERCENTAGE",
+" TOTAL MEM 63602 248.4 MB ----",
+" FREE 993 3.9 MB 1% of TOTAL MEM",
+" USED 62609 244.6 MB 98% of TOTAL MEM",
+" SHARED 34035 132.9 MB 53% of TOTAL MEM",
+" BUFFERS 10928 42.7 MB 17% of TOTAL MEM",
+" CACHED 35249 137.7 MB 55% of TOTAL MEM",
+" SLAB 2823 11 MB 4% of TOTAL MEM",
" ",
-" TOTAL HIGH 0 0 0% of TOTAL MEM",
-" FREE HIGH 0 0 0% of TOTAL HIGH",
-" TOTAL LOW 63602 248.4 MB 100% of TOTAL MEM",
-" FREE LOW 993 3.9 MB 1% of TOTAL LOW",
+" TOTAL HIGH 0 0 0% of TOTAL MEM",
+" FREE HIGH 0 0 0% of TOTAL HIGH",
+" TOTAL LOW 63602 248.4 MB 100% of TOTAL MEM",
+" FREE LOW 993 3.9 MB 1% of TOTAL LOW",
" ",
-" TOTAL SWAP 129792 507 MB ----",
-" SWAP USED 14727 57.5 MB 11% of TOTAL SWAP",
-" SWAP FREE 115065 449.5 MB 88% of TOTAL SWAP",
+" TOTAL SWAP 129792 507 MB ----",
+" SWAP USED 14727 57.5 MB 11% of TOTAL SWAP",
+" SWAP FREE 115065 449.5 MB 88% of TOTAL SWAP",
+" ",
+" COMMIT LIMIT 2952841 11.3 GB ----",
+" COMMITTED 1158600 4.4 GB 39% of TOTAL LIMIT",
" ",
" ZONE NAME FREE ACTIVE INACTIVE_DIRTY INACTIVE_CLEAN
MIN/LOW/HIGH",
" 0 DMA 240 1166 7 161 128/256/384
",
diff --git a/kernel.c b/kernel.c
index 1cb0967..4732d71 100644
--- a/kernel.c
+++ b/kernel.c
@@ -264,6 +264,12 @@ kernel_init()
&kt->__per_cpu_offset[0]);
kt->flags |= PER_CPU_OFF;
}
+
+ if (STRUCT_EXISTS("percpu_counter"))
+ if (MEMBER_EXISTS("percpu_counter", "count"))
+ MEMBER_OFFSET_INIT(percpu_counter_struct_count,
+ "percpu_counter", "count");
+
if (STRUCT_EXISTS("runqueue")) {
rqstruct = "runqueue";
rq_timestamp_name = "timestamp_last_tick";
@@ -383,6 +389,11 @@ kernel_init()
STRUCT_SIZE_INIT(spinlock_t, "spinlock_t");
verify_spinlock();
+ if (STRUCT_EXISTS("atomic_t"))
+ if (MEMBER_EXISTS("atomic_t", "counter"))
+ MEMBER_OFFSET_INIT(atomic_t_counter,
+ "atomic_t", "counter");
+
STRUCT_SIZE_INIT(list_head, "list_head");
MEMBER_OFFSET_INIT(list_head_next, "list_head", "next");
MEMBER_OFFSET_INIT(list_head_prev, "list_head", "prev");
diff --git a/memory.c b/memory.c
index 3ac928d..cc76324 100644
--- a/memory.c
+++ b/memory.c
@@ -227,6 +227,7 @@ static int vm_area_page_dump(ulong, ulong, ulong, ulong, ulong,
struct reference *);
static void rss_page_types_init(void);
static int dump_swap_info(ulong, ulong *, ulong *);
+static int get_hugetlb_total_pages(ulong *);
static void swap_info_init(void);
static char *get_swapdev(ulong, char *);
static void fill_swap_info(ulong);
@@ -391,6 +392,14 @@ vm_init(void)
kernel_symbol_exists("vmap_area_list"))
vt->flags |= USE_VMAP_AREA;
+ if (kernel_symbol_exists("hstates")) {
+ STRUCT_SIZE_INIT(hstate, "hstate");
+ MEMBER_OFFSET_INIT(hstate_order, "hstate", "order");
+ MEMBER_OFFSET_INIT(hstate_nr_huge_pages, "hstate",
"nr_huge_pages");
+ MEMBER_OFFSET_INIT(hstate_free_huge_pages, "hstate",
"free_huge_pages");
+ MEMBER_OFFSET_INIT(hstate_name, "hstate", "name");
+ }
+
MEMBER_OFFSET_INIT(page_next, "page", "next");
if (VALID_MEMBER(page_next))
MEMBER_OFFSET_INIT(page_prev, "page", "prev");
@@ -5854,20 +5863,13 @@ dump_hstates()
option_not_supported('h');
}
- if (INVALID_MEMBER(hstate_name)) {
- STRUCT_SIZE_INIT(hstate, "hstate");
- MEMBER_OFFSET_INIT(hstate_order, "hstate", "order");
- MEMBER_OFFSET_INIT(hstate_nr_huge_pages, "hstate",
"nr_huge_pages");
- MEMBER_OFFSET_INIT(hstate_free_huge_pages, "hstate",
"free_huge_pages");
- MEMBER_OFFSET_INIT(hstate_name, "hstate", "name");
- if (INVALID_SIZE(hstate) ||
- INVALID_MEMBER(hstate_order) ||
- INVALID_MEMBER(hstate_name) ||
- INVALID_MEMBER(hstate_nr_huge_pages) ||
- INVALID_MEMBER(hstate_free_huge_pages)) {
- error(INFO, "hstate structure or members have changed\n");
- option_not_supported('h');
- }
+ if (INVALID_SIZE(hstate) ||
+ INVALID_MEMBER(hstate_order) ||
+ INVALID_MEMBER(hstate_name) ||
+ INVALID_MEMBER(hstate_nr_huge_pages) ||
+ INVALID_MEMBER(hstate_free_huge_pages)) {
+ error(INFO, "hstate structure or members have changed\n");
+ option_not_supported('h');
}
fprintf(fp, "%s",
@@ -7653,7 +7655,7 @@ bailout:
* by /proc/meminfo, and then some...
*/
-char *kmeminfo_hdr = " PAGES TOTAL PERCENTAGE\n";
+char *kmeminfo_hdr = " PAGES TOTAL PERCENTAGE\n";
static void
dump_kmeminfo(void)
@@ -7670,6 +7672,11 @@ dump_kmeminfo(void)
ulong freehighmem_pages;
ulong totallowmem_pages;
ulong freelowmem_pages;
+ ulong allowed;
+ long committed;
+ ulong overcommit_kbytes = 0;
+ int overcommit_ratio;
+ ulong hugetlb_total_pages;
long nr_file_pages, nr_slab;
ulong swapper_space_nrpages;
ulong pct;
@@ -7720,7 +7727,7 @@ dump_kmeminfo(void)
} else
totalram_pages = get_totalram;
- fprintf(fp, "%10s %7ld %11s ----\n", "TOTAL MEM",
+ fprintf(fp, "%13s %7ld %11s ----\n", "TOTAL MEM",
totalram_pages, pages_to_size(totalram_pages, buf));
/*
@@ -7731,12 +7738,12 @@ dump_kmeminfo(void)
vt->dump_free_pages(&meminfo);
freeram_pages = meminfo.retval;
pct = (freeram_pages * 100)/totalram_pages;
- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n",
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL MEM\n",
"FREE", freeram_pages, pages_to_size(freeram_pages, buf), pct);
used_pages = totalram_pages - freeram_pages;
pct = (used_pages * 100)/totalram_pages;
- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n",
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL MEM\n",
"USED", used_pages, pages_to_size(used_pages, buf), pct);
/*
@@ -7745,7 +7752,7 @@ dump_kmeminfo(void)
* pages that have a count of greater than 1.
*/
pct = (shared_pages * 100)/totalram_pages;
- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n",
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL MEM\n",
"SHARED", shared_pages, pages_to_size(shared_pages, buf), pct);
subtract_buffer_pages = 0;
@@ -7762,7 +7769,7 @@ dump_kmeminfo(void)
buffer_pages = 0;
pct = (buffer_pages * 100)/totalram_pages;
- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n",
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL MEM\n",
"BUFFERS", buffer_pages, pages_to_size(buffer_pages, buf), pct);
if (CRASHDEBUG(1))
@@ -7816,7 +7823,7 @@ dump_kmeminfo(void)
pct = (page_cache_size * 100)/totalram_pages;
- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n",
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL MEM\n",
"CACHED", page_cache_size,
pages_to_size(page_cache_size, buf), pct);
@@ -7826,7 +7833,7 @@ dump_kmeminfo(void)
*/
pct = (get_slabs * 100)/totalram_pages;
- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n",
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL MEM\n",
"SLAB", get_slabs, pages_to_size(get_slabs, buf), pct);
if (symbol_exists("totalhigh_pages")) {
@@ -7851,7 +7858,7 @@ dump_kmeminfo(void)
pct = totalhigh_pages ?
(totalhigh_pages * 100)/totalram_pages : 0;
- fprintf(fp, "\n%10s %7ld %11s %3ld%% of TOTAL MEM\n",
+ fprintf(fp, "\n%13s %7ld %11s %3ld%% of TOTAL MEM\n",
"TOTAL HIGH", totalhigh_pages,
pages_to_size(totalhigh_pages, buf), pct);
@@ -7860,19 +7867,19 @@ dump_kmeminfo(void)
freehighmem_pages = meminfo.retval;
pct = freehighmem_pages ?
(freehighmem_pages * 100)/totalhigh_pages : 0;
- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL HIGH\n",
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL HIGH\n",
"FREE HIGH", freehighmem_pages,
pages_to_size(freehighmem_pages, buf), pct);
totallowmem_pages = totalram_pages - totalhigh_pages;
pct = (totallowmem_pages * 100)/totalram_pages;
- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n",
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL MEM\n",
"TOTAL LOW", totallowmem_pages,
pages_to_size(totallowmem_pages, buf), pct);
freelowmem_pages = freeram_pages - freehighmem_pages;
pct = (freelowmem_pages * 100)/totallowmem_pages;
- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL LOW\n",
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL LOW\n",
"FREE LOW", freelowmem_pages,
pages_to_size(freelowmem_pages, buf), pct);
}
@@ -7884,18 +7891,18 @@ dump_kmeminfo(void)
if (symbol_exists("swapper_space") ||
symbol_exists("swapper_spaces")) {
if (dump_swap_info(RETURN_ON_ERROR, &totalswap_pages,
&totalused_pages)) {
- fprintf(fp, "%10s %7ld %11s ----\n",
+ fprintf(fp, "%13s %7ld %11s ----\n",
"TOTAL SWAP", totalswap_pages,
pages_to_size(totalswap_pages, buf));
pct = totalswap_pages ? (totalused_pages * 100) /
totalswap_pages : 100;
- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL SWAP\n",
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL SWAP\n",
"SWAP USED", totalused_pages,
pages_to_size(totalused_pages, buf), pct);
pct = totalswap_pages ?
((totalswap_pages - totalused_pages) *
100) / totalswap_pages : 0;
- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL SWAP\n",
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL SWAP\n",
"SWAP FREE",
totalswap_pages - totalused_pages,
pages_to_size(totalswap_pages - totalused_pages,
@@ -7905,6 +7912,72 @@ dump_kmeminfo(void)
"swap_info[%ld].swap_map at %lx is inaccessible\n",
totalused_pages, totalswap_pages);
}
+ /*
+ * Show committed memory
+ */
+ if (kernel_symbol_exists("sysctl_overcommit_memory")) {
+
+ fprintf(fp, "\n");
+ if (kernel_symbol_exists("sysctl_overcommit_kbytes"))
+ get_symbol_data("sysctl_overcommit_kbytes",
+ sizeof(ulong), &overcommit_kbytes);
+
+ if (overcommit_kbytes)
+ allowed = overcommit_kbytes >>
+ (machdep->pageshift - 10);
+ else {
+ get_symbol_data("sysctl_overcommit_ratio",
+ sizeof(int),
+ &overcommit_ratio);
+
+ if (!get_hugetlb_total_pages(&hugetlb_total_pages))
+ goto bailout;
+
+ allowed = ((totalram_pages - hugetlb_total_pages)
+ * overcommit_ratio / 100);
+ }
+ if (symbol_exists("vm_committed_as")) {
+ if (INVALID_MEMBER(percpu_counter_struct_count))
+ goto bailout;
+
+ readmem(symbol_value("vm_committed_as") +
+ OFFSET(percpu_counter_struct_count),
+ KVADDR,
+ &committed,
+ sizeof(long),
+ "percpu_counter count",
+ FAULT_ON_ERROR);
+
+ /* Ensure always positive */
+ if (committed < 0)
+ committed = 0;
+ } else {
+ if (INVALID_MEMBER(atomic_t_counter))
+ goto bailout;
+
+ readmem(symbol_value("vm_committed_space") +
+ OFFSET(atomic_t_counter), KVADDR,
+ &committed, sizeof(int),
+ "atomic_t counter",
+ FAULT_ON_ERROR);
+ }
+ allowed += totalswap_pages;
+ fprintf(fp, "%13s %7ld %11s ----\n",
+ "COMMIT LIMIT", allowed,
+ pages_to_size(allowed, buf));
+
+ if (allowed) {
+ pct = committed ? ((committed * 100)
+ / allowed) : 0;
+ fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL LIMIT\n",
+ "COMMITTED", committed,
+ pages_to_size(committed, buf), pct);
+ } else
+ fprintf(fp, "%13s %7ld %11s ----\n",
+ "COMMITTED", committed,
+ pages_to_size(committed, buf));
+ }
+bailout:
dump_zone_page_usage();
}
@@ -14734,6 +14807,56 @@ next_physpage(ulonglong paddr, ulonglong *nextpaddr)
return FALSE;
}
+static int
+get_hugetlb_total_pages(ulong *nr_total_pages)
+{
+ ulong hstate_p;
+ int i, len;
+ ulong nr_huge_pages;
+ uint horder;
+
+ *nr_total_pages = 0;
+ if (kernel_symbol_exists("hstates")) {
+
+ if (INVALID_SIZE(hstate) ||
+ INVALID_MEMBER(hstate_order) ||
+ INVALID_MEMBER(hstate_nr_huge_pages))
+ return FALSE;
+
+ len = get_array_length("hstates", NULL, 0);
+ hstate_p = symbol_value("hstates");
+
+ for (i = 0; i < len; i++) {
+ hstate_p = hstate_p +
+ (SIZE(hstate) * i);
+
+ readmem(hstate_p + OFFSET(hstate_order),
+ KVADDR,
+ &horder, sizeof(uint),
+ "hstate_order",
+ FAULT_ON_ERROR);
+
+ readmem(hstate_p + OFFSET(hstate_nr_huge_pages),
+ KVADDR,
+ &nr_huge_pages, sizeof(ulong),
+ "hstate_nr_huge_pages",
+ FAULT_ON_ERROR);
+
+ *nr_total_pages += nr_huge_pages * (1 << horder);
+ }
+ } else if (kernel_symbol_exists("nr_huge_pages")) {
+ unsigned long hpage_shift = 21;
+
+ if ((machine_type("X86") && !(machdep->flags & PAE)))
+ hpage_shift = 22;
+ get_symbol_data("nr_huge_pages",
+ sizeof(ulong), &nr_huge_pages);
+ *nr_total_pages = nr_huge_pages * ((1 << hpage_shift) /
+ machdep->pagesize);
+ }
+ return TRUE;
+}
+
/*
* Display swap statistics.
*/
diff --git a/symbols.c b/symbols.c
index cebff52..fcda4e8 100644
--- a/symbols.c
+++ b/symbols.c
@@ -9417,6 +9417,10 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(ktime_t_sec));
fprintf(fp, " ktime_t_nsec: %ld\n",
OFFSET(ktime_t_nsec));
+ fprintf(fp, " atomic_t_counter: %ld\n",
+ OFFSET(atomic_t_counter));
+ fprintf(fp, " percpu_counter_struct_count: %ld\n",
+ OFFSET(percpu_counter_struct_count));
fprintf(fp, "\n size_table:\n");
fprintf(fp, " page: %ld\n", SIZE(page));
--
1.9.3