diff --git a/drivers/staging/android/ion/heaps/ion_system_heap.c b/drivers/staging/android/ion/heaps/ion_system_heap.c index 333608288de6..1612af0c4fa4 100644 --- a/drivers/staging/android/ion/heaps/ion_system_heap.c +++ b/drivers/staging/android/ion/heaps/ion_system_heap.c @@ -153,8 +153,19 @@ void free_buffer_page(struct ion_system_heap *heap, ion_page_pool_free_immediate(pool, page); else ion_page_pool_free(pool, page); + +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + mod_node_page_state(page_pgdat(page), NR_UNRECLAIMABLE_PAGES, + -(1 << pool->order)); +#endif } else { __free_pages(page, order); + +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + mod_node_page_state(page_pgdat(page), NR_UNRECLAIMABLE_PAGES, + -(1 << order)); +#endif + } } @@ -314,6 +325,12 @@ static int ion_system_heap_allocate(struct ion_heap *heap, sz = (1 << info->order) * PAGE_SIZE; +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + mod_node_page_state(page_pgdat(info->page), + NR_UNRECLAIMABLE_PAGES, + (1 << (info->order))); +#endif + if (info->from_pool) { list_add_tail(&info->list, &pages_from_pool); } else { diff --git a/gen_headers_arm.bp b/gen_headers_arm.bp index 12e3b33055e2..2cabb644ae9f 100644 --- a/gen_headers_arm.bp +++ b/gen_headers_arm.bp @@ -547,6 +547,7 @@ gen_headers_out_arm = [ "linux/synclink.h", "linux/sysctl.h", "linux/sysinfo.h", + "linux/sysstats.h", "linux/target_core_user.h", "linux/taskstats.h", "linux/tcp.h", diff --git a/gen_headers_arm64.bp b/gen_headers_arm64.bp index d985b4f885dd..675d7d78c612 100644 --- a/gen_headers_arm64.bp +++ b/gen_headers_arm64.bp @@ -542,6 +542,7 @@ gen_headers_out_arm64 = [ "linux/synclink.h", "linux/sysctl.h", "linux/sysinfo.h", + "linux/sysstats.h", "linux/target_core_user.h", "linux/taskstats.h", "linux/tcp.h", diff --git a/include/linux/mm_types_task.h b/include/linux/mm_types_task.h index c1bc6731125c..2db5b5028ca5 100644 --- a/include/linux/mm_types_task.h +++ b/include/linux/mm_types_task.h @@ -45,6 +45,9 @@ enum { MM_ANONPAGES, /* Resident anonymous pages */ MM_SWAPENTS, /* Anonymous swap entries */ MM_SHMEMPAGES, /* Resident shared memory pages */ +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + MM_UNRECLAIMABLE, /* Unreclaimable pages, e.g. shared with HW */ +#endif NR_MM_COUNTERS }; diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 560df1ae3a3e..8b38347aaf18 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -259,6 +259,9 @@ enum node_stat_item { NR_DIRTIED, /* page dirtyings since bootup */ NR_WRITTEN, /* page writings since bootup */ NR_KERNEL_MISC_RECLAIMABLE, /* reclaimable non-slab kernel pages */ +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + NR_UNRECLAIMABLE_PAGES, +#endif NR_VM_NODE_STAT_ITEMS }; diff --git a/include/uapi/linux/sysstats.h b/include/uapi/linux/sysstats.h new file mode 100644 index 000000000000..77a9599f2dec --- /dev/null +++ b/include/uapi/linux/sysstats.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#ifndef _LINUX_SYSSTATS_H +#define _LINUX_SYSSTATS_H + +#include +#include +#include + +#define SYSSTATS_VERSION 1 + +/* + * Data shared between user space and kernel space + * Each member is aligned to a 8 byte boundary. + * All values in KB. + */ +struct sys_memstats { + __u64 version; + __u64 memtotal; + __u64 vmalloc_total; + __u64 reclaimable; + __u64 zram_compressed; + __u64 swap_used; + __u64 swap_total; +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + __u64 unreclaimable; +#endif + __u64 buffer; + __u64 slab_reclaimable; + __u64 slab_unreclaimable; + __u64 free_cma; + __u64 file_mapped; + __u64 swapcache; + __u64 pagetable; + __u64 kernelstack; + __u64 shmem; + __u64 dma_nr_free_pages; + __u64 dma_nr_active_anon; + __u64 dma_nr_inactive_anon; + __u64 dma_nr_active_file; + __u64 dma_nr_inactive_file; + __u64 normal_nr_free_pages; + __u64 normal_nr_active_anon; + __u64 normal_nr_inactive_anon; + __u64 normal_nr_active_file; + __u64 normal_nr_inactive_file; + __u64 movable_nr_free_pages; + __u64 movable_nr_active_anon; + __u64 movable_nr_inactive_anon; + __u64 movable_nr_active_file; + __u64 movable_nr_inactive_file; + __u64 highmem_nr_free_pages; + __u64 highmem_nr_active_anon; + __u64 highmem_nr_inactive_anon; + __u64 highmem_nr_active_file; + __u64 highmem_nr_inactive_file; + /* version 1 ends here */ +}; + +/* + * Commands sent from userspace + * Not versioned. New commands should only be inserted at the enum's end. + */ + +enum { + SYSSTATS_CMD_UNSPEC = __CGROUPSTATS_CMD_MAX, /* Reserved */ + SYSSTATS_CMD_GET, /* user->kernel request/get-response */ + SYSSTATS_CMD_NEW, /* kernel->user event */ +}; + +#define SYSSTATS_CMD_UNSPEC SYSSTATS_CMD_UNSPEC +#define SYSSTATS_CMD_GET SYSSTATS_CMD_GET +#define SYSSTATS_CMD_NEW SYSSTATS_CMD_NEW + +enum { + SYSSTATS_TYPE_UNSPEC = 0, /* Reserved */ + SYSSTATS_TYPE_SYSMEM_STATS, /* contains name + memory stats */ +}; + +#define SYSSTATS_TYPE_UNSPEC SYSSTATS_TYPE_UNSPEC +#define SYSSTATS_TYPE_SYSMEM_STATS SYSSTATS_TYPE_SYSMEM_STATS + +enum { + SYSSTATS_CMD_ATTR_UNSPEC = 0, + SYSSTATS_CMD_ATTR_SYSMEM_STATS, +}; + +#define SYSSTATS_CMD_ATTR_UNSPEC SYSSTATS_CMD_ATTR_UNSPEC +#define SYSSTATS_CMD_ATTR_SYSMEM_STATS SYSSTATS_CMD_ATTR_SYSMEM_STATS + +#endif /* _LINUX_SYSSTATS_H */ diff --git a/kernel/fork.c b/kernel/fork.c index f89e8c85f2b8..5139510abbb0 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -134,6 +134,9 @@ static const char * const resident_page_types[] = { NAMED_ARRAY_INDEX(MM_ANONPAGES), NAMED_ARRAY_INDEX(MM_SWAPENTS), NAMED_ARRAY_INDEX(MM_SHMEMPAGES), +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + NAMED_ARRAY_INDEX(MM_UNRECLAIMABLE), +#endif }; DEFINE_PER_CPU(unsigned long, process_counts) = 0; @@ -663,7 +666,15 @@ static void check_mm(struct mm_struct *mm) "Please make sure 'struct resident_page_types[]' is updated as well"); for (i = 0; i < NR_MM_COUNTERS; i++) { - long x = atomic_long_read(&mm->rss_stat.count[i]); + long x; + +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + /* MM_UNRECLAIMABLE could be freed later in exit_files */ + if (i == MM_UNRECLAIMABLE) + continue; +#endif + + x = atomic_long_read(&mm->rss_stat.count[i]); if (unlikely(x)) pr_alert("BUG: Bad rss-counter state mm:%p type:%s val:%ld\n", diff --git a/kernel/taskstats.c b/kernel/taskstats.c index e2ac0e37c4ae..1457a92b1252 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -13,7 +13,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -48,6 +51,11 @@ static const struct nla_policy cgroupstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX [CGROUPSTATS_CMD_ATTR_FD] = { .type = NLA_U32 }, }; +static const struct nla_policy + sysstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1] = { + [SYSSTATS_CMD_ATTR_SYSMEM_STATS] = { .type = NLA_U32 }, +}; + struct listener { struct list_head list; pid_t pid; @@ -386,6 +394,144 @@ err: return NULL; } +#define K(x) ((x) << (PAGE_SHIFT - 10)) +#ifndef CONFIG_NUMA +static void sysstats_fill_zoneinfo(struct sys_memstats *stats) +{ + pg_data_t *pgdat; + struct zone *zone; + struct zone *node_zones; + unsigned long zspages = 0; + + pgdat = NODE_DATA(0); + node_zones = pgdat->node_zones; + + for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) { + if (!populated_zone(zone)) + continue; + + zspages += zone_page_state(zone, NR_ZSPAGES); + if (!strcmp(zone->name, "DMA")) { + stats->dma_nr_free_pages = + K(zone_page_state(zone, NR_FREE_PAGES)); + stats->dma_nr_active_anon = + K(zone_page_state(zone, NR_ZONE_ACTIVE_ANON)); + stats->dma_nr_inactive_anon = + K(zone_page_state(zone, NR_ZONE_INACTIVE_ANON)); + stats->dma_nr_active_file = + K(zone_page_state(zone, NR_ZONE_ACTIVE_FILE)); + stats->dma_nr_inactive_file = + K(zone_page_state(zone, NR_ZONE_INACTIVE_FILE)); + } else if (!strcmp(zone->name, "Normal")) { + stats->normal_nr_free_pages = + K(zone_page_state(zone, NR_FREE_PAGES)); + stats->normal_nr_active_anon = + K(zone_page_state(zone, NR_ZONE_ACTIVE_ANON)); + stats->normal_nr_inactive_anon = + K(zone_page_state(zone, NR_ZONE_INACTIVE_ANON)); + stats->normal_nr_active_file = + K(zone_page_state(zone, NR_ZONE_ACTIVE_FILE)); + stats->normal_nr_inactive_file = + K(zone_page_state(zone, NR_ZONE_INACTIVE_FILE)); + } else if (!strcmp(zone->name, "HighMem")) { + stats->highmem_nr_free_pages = + K(zone_page_state(zone, NR_FREE_PAGES)); + stats->highmem_nr_active_anon = + K(zone_page_state(zone, NR_ZONE_ACTIVE_ANON)); + stats->highmem_nr_inactive_anon = + K(zone_page_state(zone, NR_ZONE_INACTIVE_ANON)); + stats->highmem_nr_active_file = + K(zone_page_state(zone, NR_ZONE_ACTIVE_FILE)); + stats->highmem_nr_inactive_file = + K(zone_page_state(zone, NR_ZONE_INACTIVE_FILE)); + } else if (!strcmp(zone->name, "Movable")) { + stats->movable_nr_free_pages = + K(zone_page_state(zone, NR_FREE_PAGES)); + stats->movable_nr_active_anon = + K(zone_page_state(zone, NR_ZONE_ACTIVE_ANON)); + stats->movable_nr_inactive_anon = + K(zone_page_state(zone, NR_ZONE_INACTIVE_ANON)); + stats->movable_nr_active_file = + K(zone_page_state(zone, NR_ZONE_ACTIVE_FILE)); + stats->movable_nr_inactive_file = + K(zone_page_state(zone, NR_ZONE_INACTIVE_FILE)); + } + } + stats->zram_compressed = K(zspages); +} +#elif +static void sysstats_fill_zoneinfo(struct sys_memstats *stats) +{ +} +#endif + +static void sysstats_build(struct sys_memstats *stats) +{ + struct sysinfo i; + + si_meminfo(&i); + si_swapinfo(&i); + + stats->version = SYSSTATS_VERSION; + stats->memtotal = K(i.totalram); + stats->reclaimable = + global_node_page_state(NR_KERNEL_MISC_RECLAIMABLE) >> 10; + stats->swap_used = K(i.totalswap - i.freeswap); + stats->swap_total = K(i.totalswap); + stats->vmalloc_total = K(vmalloc_nr_pages()); +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + stats->unreclaimable = + K(global_node_page_state(NR_UNRECLAIMABLE_PAGES)); +#endif + stats->buffer = K(i.bufferram); + stats->swapcache = K(total_swapcache_pages()); + stats->slab_reclaimable = + K(global_node_page_state(NR_SLAB_RECLAIMABLE)); + stats->slab_unreclaimable = + K(global_node_page_state(NR_SLAB_UNRECLAIMABLE)); + stats->free_cma = K(global_zone_page_state(NR_FREE_CMA_PAGES)); + stats->file_mapped = K(global_node_page_state(NR_FILE_MAPPED)); + stats->kernelstack = global_zone_page_state(NR_KERNEL_STACK_KB); + stats->pagetable = K(global_zone_page_state(NR_PAGETABLE)); + stats->shmem = K(i.sharedram); + sysstats_fill_zoneinfo(stats); +} +#undef K + +static int sysstats_user_cmd(struct sk_buff *skb, struct genl_info *info) +{ + int rc = 0; + struct sk_buff *rep_skb; + struct sys_memstats *stats; + struct nlattr *na; + size_t size; + + size = nla_total_size(sizeof(struct sys_memstats)); + + rc = prepare_reply(info, SYSSTATS_CMD_NEW, &rep_skb, + size); + if (rc < 0) + goto err; + + na = nla_reserve(rep_skb, SYSSTATS_TYPE_SYSMEM_STATS, + sizeof(struct sys_memstats)); + if (na == NULL) { + nlmsg_free(rep_skb); + rc = -EMSGSIZE; + goto err; + } + + stats = nla_data(na); + memset(stats, 0, sizeof(*stats)); + + sysstats_build(stats); + + rc = send_reply(rep_skb, info); + +err: + return rc; +} + static int cgroupstats_user_cmd(struct sk_buff *skb, struct genl_info *info) { int rc = 0; @@ -659,6 +805,12 @@ static const struct genl_ops taskstats_ops[] = { /* policy enforced later */ .flags = GENL_CMD_CAP_HASPOL, }, + { + .cmd = SYSSTATS_CMD_GET, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = sysstats_user_cmd, + /* policy enforced later */ + }, }; static int taskstats_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, @@ -673,6 +825,9 @@ static int taskstats_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, case CGROUPSTATS_CMD_GET: policy = cgroupstats_cmd_get_policy; break; + case SYSSTATS_CMD_GET: + policy = sysstats_cmd_get_policy; + break; default: return -EINVAL; } diff --git a/mm/Kconfig b/mm/Kconfig index f2fa89f11ae8..e3de9157cf22 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -758,6 +758,16 @@ config PERCPU_STATS information includes global and per chunk statistics, which can be used to help understand percpu memory usage. +config MM_STAT_UNRECLAIMABLE_PAGES + default y + depends on QGKI + bool "Enable counter for unreclaimable pages" + help + Unreclaimable pages counters accounts the pages that cannot be + reclaimed under memory pressure. These include mm rss and global + counter. This option allows the enabling of the mm rss and global + unreclaimables pages counters on QGKI systems. + config HAVE_USERSPACE_LOW_MEMORY_KILLER bool "Configure reclaim paths if there is a userspace LMK" default n diff --git a/mm/vmstat.c b/mm/vmstat.c index ee82ac8523bb..61026e1289fe 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -1170,6 +1170,9 @@ const char * const vmstat_text[] = { "nr_dirtied", "nr_written", "nr_kernel_misc_reclaimable", +#ifdef CONFIG_MM_STAT_UNRECLAIMABLE_PAGES + "nr_unreclaimable_pages", +#endif /* enum writeback_stat_item counters */ "nr_dirty_threshold",