From a52bba8e7a0e2f4fb2c5788a874119b93b4583ae Mon Sep 17 00:00:00 2001 From: liuxinwei Date: 2023年2月27日 19:02:34 +0800 Subject: [PATCH 1/3] memhunter: Add memhunter detector --- Makefile | 7 + common/chrdev.c | 8 + include/common.h | 1 + modules/memhunter/common.c | 227 +++++++++++++++ modules/memhunter/common.h | 128 +++++++++ modules/memhunter/filecache.c | 190 +++++++++++++ modules/memhunter/memcg.c | 513 ++++++++++++++++++++++++++++++++++ modules/memhunter/memcg.h | 54 ++++ modules/memhunter/memcg_dia.c | 30 ++ modules/memhunter/memhunter.c | 58 ++++ sysak_mods.c | 11 + 11 files changed, 1227 insertions(+) create mode 100644 modules/memhunter/common.c create mode 100644 modules/memhunter/common.h create mode 100644 modules/memhunter/filecache.c create mode 100644 modules/memhunter/memcg.c create mode 100644 modules/memhunter/memcg.h create mode 100644 modules/memhunter/memcg_dia.c create mode 100644 modules/memhunter/memhunter.c diff --git a/Makefile b/Makefile index a602c9d..2e1687e 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,13 @@ ifneq ($(wildcard $(MODULE_SRC)/modules/iosdiag/include/$(KERNEL_VERSION)),) sysak-objs += modules/iosdiag/iosdiag.o modules/iosdiag/rq_hang.o modules/iosdiag/virtio_blk.o modules/iosdiag/nvme.o modules/iosdiag/scsi.o endif endif +ifneq ($(findstring memhunter,$(TARGET_LIST)),) +sysak-objs += modules/memhunter/memhunter.o +sysak-objs += modules/memhunter/common.o +sysak-objs += modules/memhunter/memcg.o +sysak-objs += modules/memhunter/memcg_dia.o +sysak-objs += modules/memhunter/filecache.o +endif obj-m += sysak.o diff --git a/common/chrdev.c b/common/chrdev.c index 7d08a2d..78c431d 100644 --- a/common/chrdev.c +++ b/common/chrdev.c @@ -30,6 +30,11 @@ struct sysak_dev { struct cdev cdev; }; +int __attribute__((weak)) memhunter_handler_cmd(int cmd, unsigned long arg) +{ + return -ENOSYS; +} + int __attribute__((weak)) memleak_handler_cmd(int cmd, unsigned long arg) { return -ENOSYS; @@ -49,6 +54,9 @@ static long sysak_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case MEMLEAK_IOCTL_CMD: ret = memleak_handler_cmd(nr, arg); break; + case MEMHUNTER_IOCTL_CMD: + ret = memhunter_handler_cmd(nr, arg); + break; default: printk("defualt ioctl cmd =%d, nr = %d\n", type, nr); break; diff --git a/include/common.h b/include/common.h index b83a1af..836d6ed 100644 --- a/include/common.h +++ b/include/common.h @@ -10,6 +10,7 @@ enum SYSAK_IOCTL_CMD { MEMLEAK_IOCTL_CMD = 1, + MEMHUNTER_IOCTL_CMD = 2, }; #endif diff --git a/modules/memhunter/common.c b/modules/memhunter/common.c new file mode 100644 index 0000000..f69daaa --- /dev/null +++ b/modules/memhunter/common.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +int prepend(char **buffer, int *buflen, const char *str, int namelen) +{ + *buflen -= namelen; + if (*buflen < 0) + return -1; + *buffer -= namelen; + memcpy(*buffer, str, namelen); + return 0; +} + +static struct mount *inode2mount(struct inode *inode) +{ + struct list_head *pos; + struct mount *mount = NULL; + + if(inode && !_IS_ERR(inode)){ + pos = inode->i_sb->s_mounts.next; + if(pos && !_IS_ERR(pos)){ + mount = container_of(pos, struct mount, mnt_instance); + } + } + return mount; +} + + +static int mnt_has_parent(struct mount *mnt) +{ + return !!(mnt != mnt->mnt_parent); +} + +static struct dentry *__lock_parent(struct dentry *dentry) +{ + struct dentry *parent; + rcu_read_lock(); + spin_unlock(&dentry->d_lock); +again: + parent = READ_ONCE(dentry->d_parent); + spin_lock(&parent->d_lock); + if (unlikely(parent != dentry->d_parent)) { + spin_unlock(&parent->d_lock); + goto again; + } + rcu_read_unlock(); + if (parent != dentry) + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); + else + parent = NULL; + return parent; +} + +static inline struct dentry *lock_parent(struct dentry *dentry) +{ + struct dentry *parent = dentry->d_parent; + if (IS_ROOT(dentry)) + return NULL; + if (likely(spin_trylock(&parent->d_lock))) + return parent; + return __lock_parent(dentry); +} + +static inline void __dget_dlock(struct dentry *dentry) +{ + dentry->d_lockref.count++; +} + +static struct dentry *__d_find_alias(struct inode *inode) +{ + struct dentry *alias; + + if (S_ISDIR(inode->i_mode)) + return NULL; + +#ifndef LINUX_310 + hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) +#else + hlist_for_each_entry(alias, &inode->i_dentry, d_alias) +#endif + { + spin_lock(&alias->d_lock); + __dget_dlock(alias); + spin_unlock(&alias->d_lock); + return alias; + } + return NULL; +} + +static struct dentry *_d_find_alias(struct inode *inode) +{ + struct dentry *de = NULL; + + if (!hlist_empty(&inode->i_dentry)) { + spin_lock(&inode->i_lock); + de = __d_find_alias(inode); + spin_unlock(&inode->i_lock); + } + return de; +} + + + +static char *dentry_name(struct inode *inode, struct dentry *dentry, char *name, int len) +{ + struct mount *mnt; + struct mount *prev = NULL; + char *p; + char *tmp; + char *end; + int ret = 0; + + tmp = kmalloc(PATH_MAX, GFP_ATOMIC); + if (!tmp) + return NULL; + + end = name; + end[len - 1] = 0; + end = name + len - 1; + + mnt = inode2mount(inode); + do { + memset(tmp, 0, PATH_MAX); + p = dentry_path_raw(dentry, tmp, PATH_MAX); + //pr_err("%s-%d:inode %px mountpoint %px dentry:%px\n", p, strlen(p), inode, mnt->mnt_mountpoint, dentry); + ret = prepend(&end, &len, p, strlen(p)); + if (ret) + pr_err("prepend error\n"); + //pr_err("mnt:%px parent:%px end:%px len %d tmp:%px\n", mnt,mnt->mnt_parent, end, len, tmp); + prev = mnt; + dentry = mnt->mnt_mountpoint; + mnt = mnt->mnt_parent; + } while (mnt_has_parent(prev) && (dentry != mnt->mnt_mountpoint)); + + kfree(tmp); + memmove(name, end, strlen(end) + 1); + return name; +} + +int scan_inode_name(struct inode *inode, char *buf, int len, unsigned long *cached, int *deleted) +{ + struct dentry *dt; + struct dentry *parent; + + *cached = inode->i_data.nrpages; + dt = _d_find_alias(inode); + if (!dt) { + *deleted = 1; + return 0; + } + + spin_lock(&inode->i_lock); + spin_lock(&dt->d_lock); + parent = lock_parent(dt); + + dentry_name(inode, dt, buf, len); + *deleted = d_unlinked(dt); + + if (parent) + spin_unlock(&parent->d_lock); + spin_unlock(&dt->d_lock); + spin_unlock(&inode->i_lock); + dput(dt); + + return 0; +} + +void radix_init(struct radix_tree_root *root) +{ + INIT_RADIX_TREE(root, GFP_ATOMIC); +} + +int radix_insert(struct radix_tree_root *root, unsigned long key, void *ptr) +{ + return radix_tree_insert(root, key, ptr); +} + +void *radix_lookup(struct radix_tree_root *root, unsigned long key) +{ + return (void *)radix_tree_lookup(root, key); +} + +int radix_delete(struct radix_tree_root *root, unsigned long key) +{ + return radix_tree_delete(root, key); +} +#undef NR +#define NR (10) +int radix_delete_all(struct radix_tree_root *root, node_free_t free) +{ + int found, i; + unsigned long pos = 0; + struct radix_item *res[NR]; + + do { + found = radix_tree_gang_lookup(root, (void **)res, pos, NR); + for (i = 0; i < found; i++) { + radix_delete(root, res[i]->key); + if (free) + free(res[i]); + } + } while (found> 0); + + return 0; +} diff --git a/modules/memhunter/common.h b/modules/memhunter/common.h new file mode 100644 index 0000000..7b89d4e --- /dev/null +++ b/modules/memhunter/common.h @@ -0,0 +1,128 @@ +#ifndef __RADIX_TREE__ +#define __RADIX_TREE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE>= KERNEL_VERSION(4, 1, 9) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#define LINUX_310 +#include "memcontrol_7.h" +#endif + +#define NAME_LEN (1024) +struct radix_item { + unsigned long key; +}; +struct mount { + struct hlist_node mnt_hash; + struct mount *mnt_parent; + struct dentry *mnt_mountpoint; + struct vfsmount mnt; +#if LINUX_VERSION_CODE> KERNEL_VERSION(3, 14, 0) + union { + struct rcu_head mnt_rcu; + struct llist_node mnt_llist; + }; +#endif +#ifdef CONFIG_SMP + struct mnt_pcp __percpu *mnt_pcp; +#else + int mnt_count; + int mnt_writers; +#endif + struct list_head mnt_mounts; /* list of children, anchored here */ + struct list_head mnt_child; /* and going through their mnt_child */ + struct list_head mnt_instance; /* mount instance on sb->s_mounts */ +}; + +struct inode_item { + struct radix_item node; + struct list_head inode; + unsigned long i_ino; + int nr_pages; + int deleted:4; + int shmem:4; + unsigned long cached; + char *filename; +}; + +struct file_item_list { + struct list_head items_list; + char filename[NAME_LEN]; + unsigned long size; + unsigned long cached; + int deleted; +}; + +struct filecache_result_list { + int num; + char fsname[NAME_LEN]; + struct list_head file_items_list; +}; + +struct file_item { + char filename[NAME_LEN]; + unsigned long size; + unsigned long cached; + int deleted; +}; + +struct filecache_result { + int num; + char fsname[NAME_LEN]; + struct file_item *filecache_items; +}; + +typedef enum _memhunter_type { + MEMHUNTER_CACHE_TYPE_FILE = 1, + MEMHUNTER_CACHE_TYPE_MEMCG_DYING, + MEMHUNTER_CACHE_TYPE_MEMCG_ONE, +} memhunter_type; + +typedef void (*node_free_t)(void *args); + +static inline bool _IS_ERR(const void *ptr) +{ + if((unsigned long)ptr < 0xffff000000000000){ + return 1; + } + return IS_ERR_VALUE((unsigned long)ptr); +} +static inline int page_is_file_cache(struct page *page) +{ + return !PageSwapBacked(page); +} + +static inline int page_is_shmem(struct page *page) +{ + return !!(!page_is_file_cache(page) && !PageAnon(page)); +} +int prepend(char **buffer, int *buflen, const char *str, int namelen); +int scan_inode_name(struct inode *inode, char *buf, int len, unsigned long *cached, int *deleted); +void radix_init(struct radix_tree_root *root); +int radix_insert(struct radix_tree_root *root, unsigned long key, void *ptr); +void *radix_lookup(struct radix_tree_root *root, unsigned long key); +int radix_delete(struct radix_tree_root *root, unsigned long key); +int radix_delete_all(struct radix_tree_root *root, node_free_t free); +int filecache_scan(void); +int memcg_dying_scan(void); +int memcg_scan_one(void); +int filecache_main(unsigned long arg); +#endif diff --git a/modules/memhunter/filecache.c b/modules/memhunter/filecache.c new file mode 100644 index 0000000..8f17b4e --- /dev/null +++ b/modules/memhunter/filecache.c @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +static unsigned long totalCache = 0; +static char * fileName = NULL; +struct filecache_result_list result_list; + +static void dump_filecache_result(struct filecache_result __user *result) +{ + struct file_item *tmp, *fnext; + struct filecache_result res; + int count = 0; + int i = 0; + struct file_item_list *items, *tmp2; + int ret = 0; + + if(copy_from_user(&res, result, sizeof(res))) + { + pr_err("can not copy from user %d:%d\n",count,__LINE__); + ret = copy_to_user(result, &count, sizeof(count)); + return 0; + } + if (!res.num || !res.filecache_items) + { + pr_err("num %d ,items %p \n", res.num, res.filecache_items); + ret = copy_to_user(result, &count, sizeof(count)); + return 0; + } + + i = res.num> result_list.num ? result_list.num : res.num; + if(i <= 0) + { + i = copy_to_user(result, &count, sizeof(count)); + return i; + } + if((tmp = vmalloc(sizeof(struct file_item) * i)) == NULL) + { + pr_err("vmalloc error %d:%d\n",count,__LINE__); + ret = copy_to_user(result, &count, sizeof(count)); + return 0; + } + + fnext = tmp; + + list_for_each_entry_safe(items, tmp2, &(result_list.file_items_list), items_list) + { + pr_err("filename:%s size:%lu cached:%lu deleted:%d\n", items->filename, items->size, items->cached, items->deleted); + strcpy(fnext->filename, items->filename); + fnext->size = items->size; + fnext->cached = items->cached; + fnext->deleted = items->deleted; + count += 1; + fnext ++; + if(count>= i) + break; + } + res.num = count; + + i = copy_to_user(result, &count, sizeof(count)); + i = copy_to_user(res.filecache_items, tmp, sizeof(struct file_item) * count); + vfree(tmp); + return i; +} + +static void scan_super_block(struct super_block *sb, void * args) +{ + struct inode *inode, *next; + unsigned long cached; + int deleted; + struct file_item_list *tmp, *tmp2 = NULL; + +#ifdef LINUNX_310 + spinlock_t *sb_inode_lock = kallsyms_lookup_name("inode_sb_list_lock"); + spin_lock(sb_inode_lock); +#else + spin_lock(&sb->s_inode_list_lock); +#endif + list_for_each_entry_safe(inode, next, &sb->s_inodes, i_sb_list) { + if (S_ISREG(inode->i_mode)) { + scan_inode_name(inode, fileName, PATH_MAX, &cached, &deleted); + if (cached*4> 1024*50) + { + pr_err("filename:%s size:%lu cached:%lu deleted:%d\n", fileName, (unsigned long)inode->i_size, cached, deleted); + tmp2 = vzalloc(sizeof(struct file_item_list)); + if(!tmp2) + { + pr_err("vzalloc error: %d",__LINE__); + break; + } + strncpy(tmp2->filename, fileName,strlen(fileName)); + tmp2->size = (unsigned long)inode->i_size; + tmp2->cached = cached; + tmp2->deleted = deleted; + result_list.num += 1; + list_add_tail(&tmp2->items_list, &(result_list.file_items_list)); + } + } + } +#ifdef LINUX_310 + spin_unlock(sb_inode_lock); +#else + spin_unlock(&sb->s_inode_list_lock); +#endif +} + +static int scan_filesystem_type(char * fs) +{ + struct file_system_type * file_system; + + file_system = get_fs_type(fs); + if (!file_system) + return 0; + + iterate_supers_type(file_system, scan_super_block, NULL); + module_put(file_system->owner); + + return 0; +} + +int filecache_init(void) +{ + fileName = __getname(); + if(!fileName) + return 0; + result_list.num = 0; + INIT_LIST_HEAD(&(result_list.file_items_list)); + return 0; +} + +void filecache_exit(void) +{ + struct file_item_list *files_all, *tmp; + if (fileName) + __putname(fileName); + if(result_list.num) + { + list_for_each_entry_safe(files_all, tmp, &(result_list.file_items_list), items_list) + vfree(files_all); + } +} + +int filecache_scan(void) +{ + filecache_init(); + scan_filesystem_type("ext4"); + pr_err("total file cached %lu\n",totalCache); + totalCache = 0; + scan_filesystem_type("tmpfs"); + pr_err("total tmpfs %lu\n",totalCache); + filecache_exit(); + return 0; +} +int filecache_main(unsigned long arg) +{ + struct filecache_result *res = (struct filecache_result*)arg; + int ret = 0; + char fsname[NAME_LEN]; + memset(fsname, 0, sizeof(fsname)); + ret = copy_from_user(fsname, res->fsname, NAME_LEN); + pr_err("fsname:%s\n",fsname); + filecache_init(); + scan_filesystem_type(fsname); + pr_err("total file cached %lu\n",totalCache); + dump_filecache_result(res); + return 0; +} diff --git a/modules/memhunter/memcg.c b/modules/memhunter/memcg.c new file mode 100644 index 0000000..6f38a98 --- /dev/null +++ b/modules/memhunter/memcg.c @@ -0,0 +1,513 @@ +#include +#include +#include +#include +#include +#include "memcg.h" + +static struct mem_cgroup *(* _mem_cgroup_iter)(struct mem_cgroup *,struct mem_cgroup *,struct mem_cgroup_reclaim_cookie *); +static void (*_mem_cgroup_iter_break)(struct mem_cgroup*, struct mem_cgroup*); +static struct zone *(*_next_zone)(struct zone *zone); +static struct address_space *(*_page_mapping)(struct page *page); +struct pglist_data *(*_first_online_pgdat)(void); + +#define for_each_mem_cgroup(iter, start) \ + for (iter = _mem_cgroup_iter(start, NULL, NULL); \ + iter != NULL; \ + iter = _mem_cgroup_iter(start, iter, NULL)) +#define _for_each_zone(zone) \ + for (zone = (_first_online_pgdat())->node_zones; \ + zone; \ + zone = _next_zone(zone)) + +static void free_item(void *args) +{ + struct inode_item *item = (struct inode_item *)args; + kfree(item->filename); + kfree(item); +} + +static void memcg_free_item(struct memcg_item *cgitem) +{ + radix_delete_all(&cgitem->inode_root, free_item); +} + + +static int get_page_inode(struct memcg_item *cgitem, struct page *page) +{ + struct address_space *mapping; + struct inode *inode; + struct inode_item *item; + unsigned long cached; + int deleted; + int ret = 0; + char *fileName; + + fileName = kzalloc(PATH_MAX, GFP_ATOMIC); + if (!fileName) + return 0; + get_page(page); + if (PageAnon(page)) { + cgitem->anon++; + goto _skip; + } + if (!page_is_file_cache(page)) + cgitem->shmem++; + else + cgitem->file++; +#ifdef LINUX_310 + mapping = _page_mapping(page); +#else + mapping = page_mapping(page); +#endif + if (!mapping) + goto _skip; + inode = mapping->host; + if (!inode) + goto _skip; + + item = radix_lookup(&cgitem->inode_root, (unsigned long)inode); + if (item) { + item->nr_pages++; + goto _skip; + } + scan_inode_name(inode, fileName, PATH_MAX, &cached, &deleted); + if (!cached) + goto _skip; + + item = kzalloc(sizeof(*item), GFP_ATOMIC); + if (!item) + goto _skip; + item->i_ino = inode->i_ino; + item->nr_pages = 1; + item->filename = kmemdup(fileName, strlen(fileName) + 1, GFP_ATOMIC); + item->cached = cached; + item->deleted = deleted; + item->shmem = page_is_shmem(page); + item->node.key = (unsigned long)inode; + list_add_tail(&item->inode, &cgitem->head); + ret = radix_insert(&cgitem->inode_root, (unsigned long)inode, (void*)item); + if(ret) + pr_info("insert file:%s error\n", item->filename); + cgitem->num_file++; +_skip: + kfree(fileName); + put_page(page); + return 0; +} + + +static void get_lru_page(struct memcg_item *item, struct lruvec *vec) +{ + struct page *page, *tmp; + enum lru_list lru; + unsigned long flags; + +#ifdef LINUX_310 + struct zone *lruzone; + lruzone = vec->zone; + if (!lruzone) { + pr_err("lru zone error for memcg:%px cg:%s\n", item->memcg, item->cgname); + return; + } + if(lruzone) + spin_lock_irqsave(&lruzone->lru_lock, flags); +#else + struct pglist_data *pgdat; + pgdat = vec->pgdat; + if (!pgdat) { + pr_err("lru pgdata error for memcg:%px cg:%s\n", item->memcg, item->cgname); + return; + } + if(pgdat) + spin_lock_irqsave(&pgdat->lru_lock, flags); +#endif + for_each_lru(lru) { + struct list_head *list = &vec->lists[lru]; + list_for_each_entry_safe(page, tmp, list, lru) { + get_page_inode(item, page); + } + } +#ifdef LINUX_310 + if (lruzone) + spin_unlock_irqrestore(&lruzone->lru_lock, flags); +#else + if (pgdat) + spin_unlock_irqrestore(&pgdat->lru_lock, flags); +#endif +} + +static void get_memcg_page(struct memcg_item *item) +{ +#ifdef LINUX_310 + struct mem_cgroup_per_zone *mz; + struct zone *z; + _for_each_zone(z) { + if((unsigned)zone_to_nid(z)>= nr_node_ids) + continue; + mz = &item->memcg->info.nodeinfo[zone_to_nid(z)]->zoneinfo[zone_idx(z)]; + get_lru_page(item, &mz->lruvec); + } +#else + struct mem_cgroup_per_node *mz; + int nid; + for_each_node(nid) { + mz = mem_cgroup_nodeinfo(item->memcg, nid); + get_lru_page(item, &mz->lruvec); + } +#endif +} + +static void memcg_get_name(struct mem_cgroup *memcg, char *name, unsigned int len) +{ + char *end; + int pos; + struct cgroup *cg = memcg->css.cgroup; +#ifdef LINUX_310 + if (!cg) + return; + rcu_read_lock(); + cgroup_path(cg, name, PATH_MAX); + rcu_read_unlock(); + end = name+strlen("/sys/fs/cgroup/memory"); + memmove(end, name, strlen(name)+1); + prepend(&end, &len, "/sys/fs/cgroup/memory", strlen("/sys/fs/cgroup/memory")); + pr_err("cg:name: %s, len:%d\n",name,strlen(name)); +#else + struct kernfs_node *kn; + struct kernfs_node *pkn; + if (!cg|| !cg->kn) + return; + kn = cg->kn; + + kernfs_get(kn); + end = name + len - 1; + prepend(&end, &len, "0円", 1); + pkn = kn; + while (pkn) { + pos = prepend(&end, &len, pkn->name, strlen(pkn->name)); + if (pos) + break; + if ((pkn == pkn->parent) || !pkn->parent) + break; + pos = prepend(&end, &len, "/", 1); + if (pos) + break; + pkn = pkn->parent; + } + + prepend(&end, &len, "/sys/fs/cgroup/memory", strlen("/sys/fs/cgroup/memory")); + + kernfs_put(kn); + memmove(name, end, strlen(end) + 1); +#endif +} + +static struct memcg_item *memcg_init_item(struct mem_cgroup *cg) +{ + struct memcg_item *item = NULL; + char *fileName; + + fileName = kzalloc(PATH_MAX, GFP_ATOMIC); + if (!fileName) + return NULL; + item = kzalloc(sizeof(*item), GFP_ATOMIC); + if (!item) { + goto _out; + } + memcg_get_name(cg, fileName, PATH_MAX); + item->memcg = cg; +#ifdef LINUX_310 + item->size = cg->res.usage; + item->cgname = kmemdup(fileName, strlen(fileName) + 2, GFP_ATOMIC); + if(item->cgname) + item->cgname[strlen(fileName) + 1] = '0円'; +#else + item->size = page_counter_read(&cg->memory); + item->cgname = kmemdup_nul(fileName, strlen(fileName) + 1, GFP_ATOMIC); +#endif + INIT_LIST_HEAD(&item->head); + INIT_LIST_HEAD(&item->offline); + radix_init(&item->inode_root); +_out: + kfree(fileName); + return item; +} + +void memcg_dump_to_user(struct memcg_info *info, struct memcg_info_user __user *result) +{ + struct memcg_item *cgitem; + struct inode_item *item; + struct memcg_info_user res; + struct memcg_item_user *cgitem_u, *tmp, *user_head; + struct memcg_item_user tmp_memcg; + struct inode_item_user *tmp2, *free_tmp; + int count_m = 0, i = 0, j = 0, count_i; + int ret = 0; + + if(copy_from_user(&res, result, sizeof(res))) + { + pr_err("can not copy from user %d:%d\n",count_m,__LINE__); + ret = copy_to_user(result, &count_m, sizeof(count_m)); + return 0; + } + + if (!res.nr || !res.items) + { + pr_err("num %d ,items %p \n", res.nr, res.items); + ret = copy_to_user(result, &count_m, sizeof(count_m)); + return 0; + } + + i = res.nr> info->nr ? info->nr : res.nr; + if(i == 0) + { + ret = copy_to_user(result, &count_m, sizeof(count_m)); + return 0; + } + if((tmp = vzalloc(sizeof(struct memcg_item_user) * i)) == NULL) + { + pr_err("vmalloc error %d:%d\n",count_m,__LINE__); + ret = copy_to_user(result, &count_m, sizeof(count_m)); + return 0; + } + + cgitem_u = res.items; + user_head = tmp; + + list_for_each_entry(cgitem, &info->head, offline) { + if(count_m>= i) + break; + pr_err("cg:%s memory:%lu file:%lu anon:%lu shmem:%lu num_file:%d\n", cgitem->cgname, cgitem->size, cgitem->file, cgitem->anon, cgitem->shmem, cgitem->num_file); + strncpy(tmp->cgname, cgitem->cgname,strlen(cgitem->cgname)); + tmp->size = cgitem->size; + tmp->file = cgitem->file; + tmp->anon = cgitem->anon; + tmp->shmem = cgitem->shmem; + tmp->num_file = cgitem->num_file; + + j = INODE_LIMIT> tmp->num_file ? tmp->num_file : INODE_LIMIT; + if(j <= 0) + { + if(copy_from_user(&tmp_memcg, cgitem_u, sizeof(tmp_memcg))) + { + pr_err("can not copy from user %d:%d\n",count_m,__LINE__); + ret = copy_to_user(result, &count_m, sizeof(count_m)); + vfree(tmp->inode_items); + vfree(user_head); + return 0; + } + tmp->inode_items = tmp_memcg->inode_items; + copy_to_user(cgitem_u, tmp, sizeof(struct memcg_item_user)); + tmp++; + cgitem_u++; + count_m++; + continue; + } + if((tmp2 = vzalloc(sizeof(struct inode_item_user) * j)) == NULL) + { + pr_err("vmalloc error %d:%d\n",count_m,__LINE__); + ret = copy_to_user(result, &count_m, sizeof(count_m)); + vfree(user_head); + return 0; + } + free_tmp = tmp2; + count_i = 0; + + list_for_each_entry(item, &cgitem->head,inode) { + if(count_i>= j) + break; + pr_err("ino:%lu, filename:%s cached:%lu nr_pages:%d deleted:%d shmem:%d\n", item->i_ino, item->filename, item->cached, item->nr_pages, item->deleted, item->shmem); + tmp2->i_ino = item->i_ino; + strcpy(tmp2->filename, item->filename); + tmp2->cached = item->cached; + tmp2->nr_pages = item->nr_pages; + tmp2->deleted = item->deleted; + tmp2->shmem = item->shmem; + tmp2++; + count_i++; + } + if(copy_from_user(&tmp_memcg, cgitem_u, sizeof(tmp_memcg))) + { + pr_err("can not copy from user %d:%d\n",count_m,__LINE__); + ret = copy_to_user(result, &count_m, sizeof(count_m)); + vfree(tmp->inode_items); + vfree(user_head); + return 0; + } + copy_to_user(tmp_memcg.inode_items, free_tmp, sizeof(struct inode_item_user) * j); + tmp->inode_items = tmp_memcg->inode_items; + copy_to_user(cgitem_u, tmp, sizeof(struct memcg_item_user)); + vfree(free_tmp); + tmp++; + cgitem_u++; + count_m++; + } + i = copy_to_user(result, &count_m, sizeof(count_m)); + vfree(user_head); + return i; + +} + +void memcg_dump(struct memcg_info *info) +{ + struct memcg_item *cgitem; + struct inode_item *item; + int a = 0; + list_for_each_entry(cgitem, &info->head, offline) { + pr_err("cg:%s memory:%lu file:%lu anon:%lu shmem:%lu num_file:%d\n", cgitem->cgname, cgitem->size, cgitem->file, cgitem->anon, cgitem->shmem, cgitem->num_file); + list_for_each_entry(item, &cgitem->head,inode) { + pr_err("ino:%lu, filename:%s cached:%lu nr_pages:%d deleted:%d shmem:%d\n", item->i_ino, item->filename, item->cached, item->nr_pages, item->deleted, item->shmem); + a = 1; + } + } +} + +void memcg_free_all(struct memcg_info *info) +{ + struct memcg_item *cgitem; + struct memcg_item *tmp; + + list_for_each_entry_safe(cgitem, tmp, &info->head, offline) { + memcg_free_item(cgitem); + kfree(cgitem->cgname); + kfree(cgitem); + } +} + +static int memcg_init(void) +{ + + if (_mem_cgroup_iter && _mem_cgroup_iter_break) + return 0; + + _mem_cgroup_iter = kallsyms_lookup_name("mem_cgroup_iter"); + if (!_mem_cgroup_iter) { + pr_err("lookup mem cgroup iter error\n"); + return -1; + } + + _mem_cgroup_iter_break = kallsyms_lookup_name("mem_cgroup_iter_break"); + if (!_mem_cgroup_iter_break) { + pr_err("lookup iter break error\n"); + return -1; + } + _next_zone = kallsyms_lookup_name("next_zone"); + if (!_next_zone) { + pr_err("next_zone error\n"); + return -1; + } + + _first_online_pgdat = kallsyms_lookup_name("first_online_pgdat"); + if (!_first_online_pgdat) { + pr_err("first_online_pgdat error\n"); + return -1; + } + _page_mapping = kallsyms_lookup_name("page_mapping"); + if (!_page_mapping) { + pr_err("page_mapping error\n"); + return -1; + } + + return 0; +} + +int memcg_scan(struct memcg_info *info, struct mem_cgroup *start, int offline) +{ + struct mem_cgroup *iter = NULL; + struct memcg_item *item; + + if (memcg_init()) + return -1; + + for_each_mem_cgroup(iter, start) { +#ifdef LINUX_310 + if (offline && !(iter->css.flags&CSS_DYING)) +#else + if (offline && (iter->css.flags&CSS_ONLINE)) +#endif + continue; + item = memcg_init_item(iter); + if (!item) + continue; + get_memcg_page(item); + info->nr++; + list_add_tail(&item->offline, &info->head); + } + return 0; +} + +struct mem_cgroup *memcg_get_by_name(char *cgname) +{ + struct mem_cgroup *iter = NULL; + char *fileName; + + if (memcg_init()) + return 0; + fileName = kzalloc(PATH_MAX, GFP_ATOMIC); + if (!fileName) { + pr_err("alloc memory failed:%pF\n", __FUNCTION__); + return 0; + } + + for_each_mem_cgroup(iter, NULL) { + memcg_get_name(iter, fileName, PATH_MAX); + if (!strncmp(fileName, cgname, strlen(cgname))) { + pr_err("filename:%s, cgname:%s\n",fileName, cgname); + _mem_cgroup_iter_break(NULL, iter); + break; + } + } + + kfree(fileName); + return iter; +} +int memcg_dying_main(unsigned long arg) +{ + pr_err("try_to dump to user\n"); + struct memcg_info info; + struct mem_cgroup *memcg = NULL; + info.nr = 0; + char cgname_u[NAME_LEN]; + struct memcg_info_user *res = (struct memcg_info_user*)arg; + + memset(cgname_u, 0, sizeof(cgname_u)); + copy_from_user(cgname_u, res->cgname, NAME_LEN); + if(strlen(cgname_u)> 0) + { + if(cgname_u[strlen(cgname_u)-1] == '/') + cgname_u[strlen(cgname_u)-1] = '0円'; + pr_err("cgname:%s\n",cgname_u); + memcg = memcg_get_by_name(cgname_u); + } + + INIT_LIST_HEAD(&info.head); + memcg_scan(&info, memcg, 1); + memcg_dump_to_user(&info, (struct memcg_info_user*)arg); + memcg_free_all(&info); + + return 0; +} +int memcg_one_main(unsigned long arg) +{ + char *cgname = "/sys/fs/cgroup/memory/agent"; + struct mem_cgroup *memcg = NULL; + struct memcg_info info; + char cgname_u[NAME_LEN]; + struct memcg_info_user *res = (struct memcg_info_user*)arg; + info.nr = 0; + + memset(cgname_u, 0, sizeof(cgname_u)); + copy_from_user(cgname_u, res->cgname, NAME_LEN); + pr_err("cgname:%s\n",cgname_u); + if(strlen(cgname_u)> 0) + if(cgname_u[strlen(cgname_u)-1] == '/') + cgname_u[strlen(cgname_u)-1] = '0円'; + INIT_LIST_HEAD(&info.head); + memcg = memcg_get_by_name(cgname_u); + if(memcg != NULL) + memcg_scan(&info, memcg, 0); + memcg_dump_to_user(&info, (struct memcg_info_user*)arg); + memcg_free_all(&info); + return 0; +} diff --git a/modules/memhunter/memcg.h b/modules/memhunter/memcg.h new file mode 100644 index 0000000..88d85a9 --- /dev/null +++ b/modules/memhunter/memcg.h @@ -0,0 +1,54 @@ +#ifndef __MEMCG_FILE__ +#define __MEMCG_FILE__ +#include +#include "common.h" + +#define INODE_LIMIT (50) +struct memcg_info { + int nr ;/* number of memcg for dying*/ + struct list_head head; /* memcg offline*/ +}; + +struct memcg_item { + struct radix_tree_root inode_root; + struct list_head head; + struct list_head offline; + unsigned long anon; + unsigned long shmem; + unsigned long file; + unsigned long size; + int num_file; + struct mem_cgroup *memcg; + char *cgname; +}; + +struct inode_item_user { + unsigned long i_ino; + int nr_pages; + int deleted:4; + int shmem:4; + unsigned long cached; + char filename[NAME_LEN]; +}; +struct memcg_item_user { + struct inode_item_user *inode_items; + unsigned long anon; + unsigned long shmem; + unsigned long file; + unsigned long size; + int num_file; + char cgname[NAME_LEN]; +}; + +struct memcg_info_user { + int nr ;/* number of memcg for dying*/ + struct memcg_item_user* items; + char cgname[NAME_LEN]; +}; +void memcg_dump(struct memcg_info *info); +void memcg_free_all(struct memcg_info *info); +int memcg_scan(struct memcg_info *info, struct mem_cgroup *start, int offline); +int memcg_dying_main(unsigned long arg); +int memcg_one_main(unsigned long arg); +struct mem_cgroup *memcg_get_by_name(char *cgname); +#endif diff --git a/modules/memhunter/memcg_dia.c b/modules/memhunter/memcg_dia.c new file mode 100644 index 0000000..24a5cc8 --- /dev/null +++ b/modules/memhunter/memcg_dia.c @@ -0,0 +1,30 @@ +#include +#include "common.h" +#include "memcg.h" + +int memcg_dying_scan(void) +{ + int offline = 1; + struct memcg_info info; + + INIT_LIST_HEAD(&info.head); + memcg_scan(&info, NULL, offline); + memcg_dump(&info); + memcg_free_all(&info); + + return 0; +} + +int memcg_scan_one(void) +{ + char *cgname = "/sys/fs/cgroup/memory/agent"; + struct mem_cgroup *memcg; + struct memcg_info info; + + INIT_LIST_HEAD(&info.head); + memcg = memcg_get_by_name(cgname); + memcg_scan(&info, memcg, 0); + memcg_dump(&info); + memcg_free_all(&info); + return 0; +} diff --git a/modules/memhunter/memhunter.c b/modules/memhunter/memhunter.c new file mode 100644 index 0000000..b206619 --- /dev/null +++ b/modules/memhunter/memhunter.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "memcg.h" + +#define CHR_NAME "memhunter" +static DEFINE_MUTEX(dev_mutex); +static int memhunter_dev_major = -1; +static struct class *memhunter_dev_class = NULL; +static struct device *memhunter_dev = NULL; + +int memhunter_handler_cmd(unsigned int cmd, unsigned long arg) +{ + int ret = -EINVAL; + int type, nr; + printk("debug -- 1\n"); + + if (!mutex_trylock(&dev_mutex)) + return -EBUSY; + + type = _IOC_TYPE(cmd); + nr = _IOC_NR(cmd); + printk("type: %d\n", nr); + switch (nr) { + case MEMHUNTER_CACHE_TYPE_FILE: + ret = filecache_main(arg); + break; + case MEMHUNTER_CACHE_TYPE_MEMCG_DYING: + printk("dying\n"); + ret = memcg_dying_main(arg); + break; + case MEMHUNTER_CACHE_TYPE_MEMCG_ONE: + ret = memcg_one_main(arg); + break; + default: + printk("defualt ioctl cmd =%d, nr = %d\n", type, nr); + break; + } + + mutex_unlock(&dev_mutex); + return ret; +} + +static __init int memhunter_init(void) +{ + return 0; +} + +static __exit void memhunter_exit(void) +{ + return 0; +} + diff --git a/sysak_mods.c b/sysak_mods.c index 3e0dab8..5804e96 100644 --- a/sysak_mods.c +++ b/sysak_mods.c @@ -20,6 +20,16 @@ int __attribute__((weak)) memleak_uninit(void) return 0; } +int __attribute__((weak)) memhunter_init(void) +{ + return 0; +} + +int __attribute__((weak)) memhunter_uninit(void) +{ + return 0; +} + int __attribute__((weak)) trace_irqoff_init(void) { return 0; @@ -84,6 +94,7 @@ struct sysak_module sysak_modules[] = { { "mmap_trace", mmaptrace_init, mmaptrace_exit}, { "iosdiag", disk_hang_init, disk_hang_exit}, {"ulockcheck", ulockcheck_init, ulockcheck_exit}, + {"memhunter", memhunter_init, memhunter_uninit}, }; const int sysk_module_num = sizeof(sysak_modules) / sizeof(struct sysak_module); -- Gitee From ac1a9332eb55c6e1684a812cf2d7222940a34c35 Mon Sep 17 00:00:00 2001 From: liuxinwei Date: 2023年2月28日 15:58:07 +0800 Subject: [PATCH 2/3] memhunter: fix function type bugs --- modules/memhunter/filecache.c | 2 +- modules/memhunter/memcg.c | 6 +++--- modules/memhunter/memhunter.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/memhunter/filecache.c b/modules/memhunter/filecache.c index 8f17b4e..a8b5fc9 100644 --- a/modules/memhunter/filecache.c +++ b/modules/memhunter/filecache.c @@ -29,7 +29,7 @@ static unsigned long totalCache = 0; static char * fileName = NULL; struct filecache_result_list result_list; -static void dump_filecache_result(struct filecache_result __user *result) +static int dump_filecache_result(struct filecache_result __user *result) { struct file_item *tmp, *fnext; struct filecache_result res; diff --git a/modules/memhunter/memcg.c b/modules/memhunter/memcg.c index 6f38a98..aaed3d5 100644 --- a/modules/memhunter/memcg.c +++ b/modules/memhunter/memcg.c @@ -234,7 +234,7 @@ _out: return item; } -void memcg_dump_to_user(struct memcg_info *info, struct memcg_info_user __user *result) +int memcg_dump_to_user(struct memcg_info *info, struct memcg_info_user __user *result) { struct memcg_item *cgitem; struct inode_item *item; @@ -297,7 +297,7 @@ void memcg_dump_to_user(struct memcg_info *info, struct memcg_info_user __user * vfree(user_head); return 0; } - tmp->inode_items = tmp_memcg->inode_items; + tmp->inode_items = tmp_memcg.inode_items; copy_to_user(cgitem_u, tmp, sizeof(struct memcg_item_user)); tmp++; cgitem_u++; @@ -336,7 +336,7 @@ void memcg_dump_to_user(struct memcg_info *info, struct memcg_info_user __user * return 0; } copy_to_user(tmp_memcg.inode_items, free_tmp, sizeof(struct inode_item_user) * j); - tmp->inode_items = tmp_memcg->inode_items; + tmp->inode_items = tmp_memcg.inode_items; copy_to_user(cgitem_u, tmp, sizeof(struct memcg_item_user)); vfree(free_tmp); tmp++; diff --git a/modules/memhunter/memhunter.c b/modules/memhunter/memhunter.c index b206619..04b057b 100644 --- a/modules/memhunter/memhunter.c +++ b/modules/memhunter/memhunter.c @@ -53,6 +53,6 @@ static __init int memhunter_init(void) static __exit void memhunter_exit(void) { - return 0; + return ; } -- Gitee From db855a539a6e584316001fc76baf68d71a8c85b4 Mon Sep 17 00:00:00 2001 From: liuxinwei Date: Wed, 1 Mar 2023 15:21:17 +0800 Subject: [PATCH 3/3] memhunter: cnosider ver 3.10 --- modules/memhunter/common.h | 9 +- modules/memhunter/filecache.c | 2 +- modules/memhunter/memcg.c | 2 +- modules/memhunter/memcontrol_7.h | 284 +++++++++++++++++++++++++++++++ 4 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 modules/memhunter/memcontrol_7.h diff --git a/modules/memhunter/common.h b/modules/memhunter/common.h index 7b89d4e..f111575 100644 --- a/modules/memhunter/common.h +++ b/modules/memhunter/common.h @@ -105,14 +105,14 @@ static inline bool _IS_ERR(const void *ptr) } return IS_ERR_VALUE((unsigned long)ptr); } -static inline int page_is_file_cache(struct page *page) +static inline int _page_is_file_cache(struct page *page) { return !PageSwapBacked(page); } static inline int page_is_shmem(struct page *page) { - return !!(!page_is_file_cache(page) && !PageAnon(page)); + return !!(!_page_is_file_cache(page) && !PageAnon(page)); } int prepend(char **buffer, int *buflen, const char *str, int namelen); int scan_inode_name(struct inode *inode, char *buf, int len, unsigned long *cached, int *deleted); @@ -125,4 +125,9 @@ int filecache_scan(void); int memcg_dying_scan(void); int memcg_scan_one(void); int filecache_main(unsigned long arg); +#ifdef LINUX_310 +enum { + CSS_DYING = 0, /* this CSS is dying*/ +}; +#endif #endif diff --git a/modules/memhunter/filecache.c b/modules/memhunter/filecache.c index a8b5fc9..29639fd 100644 --- a/modules/memhunter/filecache.c +++ b/modules/memhunter/filecache.c @@ -93,7 +93,7 @@ static void scan_super_block(struct super_block *sb, void * args) int deleted; struct file_item_list *tmp, *tmp2 = NULL; -#ifdef LINUNX_310 +#ifdef LINUX_310 spinlock_t *sb_inode_lock = kallsyms_lookup_name("inode_sb_list_lock"); spin_lock(sb_inode_lock); #else diff --git a/modules/memhunter/memcg.c b/modules/memhunter/memcg.c index aaed3d5..b90043d 100644 --- a/modules/memhunter/memcg.c +++ b/modules/memhunter/memcg.c @@ -51,7 +51,7 @@ static int get_page_inode(struct memcg_item *cgitem, struct page *page) cgitem->anon++; goto _skip; } - if (!page_is_file_cache(page)) + if (!_page_is_file_cache(page)) cgitem->shmem++; else cgitem->file++; diff --git a/modules/memhunter/memcontrol_7.h b/modules/memhunter/memcontrol_7.h new file mode 100644 index 0000000..1ac50bd --- /dev/null +++ b/modules/memhunter/memcontrol_7.h @@ -0,0 +1,284 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mem_cgroup_lru_info; +enum drt_count_t { + DRT_0_50, + DRT_50_100, + DRT_100_200, + DRT_200_500, + DRT_500_1k, + DRT_1k_5k, + DRT_5k_10k, + DRT_10k_100k, + DRT_100k_INF, + DRT_COUNT, +}; +struct eventfd_ctx { + struct kref kref; + wait_queue_head_t wqh; + /* + * Every time that a write(2) is performed on an eventfd, the + * value of the __u64 being written is added to "count" and a + * wakeup is performed on "wqh". A read(2) will return the "count" + * value to userspace, and will reset "count" to zero. The kernel + * side eventfd_signal() also, adds to the "count" counter and + * issue a wakeup. + */ + __u64 count; + unsigned int flags; +}; +struct mem_cgroup_threshold { + struct eventfd_ctx *eventfd; + u64 threshold; +}; + +/* For threshold */ +struct mem_cgroup_threshold_ary { + /* An array index points to threshold just below or equal to usage. */ + int current_threshold; + /* Size of entries[] */ + unsigned int size; + /* Array of thresholds */ + struct mem_cgroup_threshold entries[0]; +}; + +struct mem_cgroup_thresholds { + /* Primary thresholds array */ + struct mem_cgroup_threshold_ary *primary; + /* + * * Spare threshold array. + * * This is needed to make mem_cgroup_unregister_event() "never fail". + * * It must be able to store at least primary->size - 1 entries. + * */ + struct mem_cgroup_threshold_ary *spare; +}; + +struct mem_cgroup_reclaim_iter { + /* + * last scanned hierarchy member. Valid only if last_dead_count + * matches memcg->dead_count of the hierarchy root group. + */ + struct mem_cgroup *last_visited; + unsigned long last_dead_count; + + /* scan generation, increased every round-trip */ + unsigned int generation; +}; +struct mem_cgroup_per_zone { + struct lruvec lruvec; + unsigned long lru_size[NR_LRU_LISTS]; + + struct mem_cgroup_reclaim_iter reclaim_iter[DEF_PRIORITY + 1]; + + struct rb_node tree_node; /* RB tree node */ + unsigned long long usage_in_excess;/* Set to the value by which */ + /* the soft limit is exceeded*/ + bool on_tree; + bool writeback; /* memcg kswapd reclaim writeback */ + bool dirty; /* memcg kswapd reclaim dirty */ + bool congested; /* memcg has many dirty pages */ + /* backed by a congested BDI */ + struct mem_cgroup *memcg; /* Back pointer, we cannot */ + /* use container_of */ + + unsigned long pages_scanned; /* since last reclaim */ + bool all_unreclaimable; /* All pages pinned */ +}; + +struct mem_cgroup_per_node { + struct mem_cgroup_per_zone zoneinfo[MAX_NR_ZONES]; +}; +struct mem_cgroup_lru_info { + struct mem_cgroup_per_node *nodeinfo[0]; +}; +struct mem_cgroup { + struct cgroup_subsys_state css; + /* + ┆* the counter to account for memory usage + ┆*/ + struct res_counter res; + +#ifdef CONFIG_MEM_DELAY + /* Memory delay measurement domain */ + struct memdelay_domain *memdelay_domain; +#endif + + /* vmpressure notifications */ + struct vmpressure vmpressure; + + union { + /* + ┆* the counter to account for mem+swap usage. + ┆*/ + struct res_counter memsw; + + /* + ┆* rcu_freeing is used only when freeing struct mem_cgroup, + ┆* so put it into a union to avoid wasting more memory. + ┆* It must be disjoint from the css field. It could be + ┆* in a union with the res field, but res plays a much + ┆* larger part in mem_cgroup life than memsw, and might + ┆* be of interest, even at time of free, when debugging. + ┆* So share rcu_head with the less interesting memsw. + ┆*/ + struct rcu_head rcu_freeing; + /* + ┆* We also need some space for a worker in deferred freeing. + ┆* By the time we call it, rcu_freeing is no longer in use. + ┆*/ + struct work_struct work_freeing; + }; + + /* + ┆* the counter to account for kernel memory usage. + ┆*/ + struct res_counter kmem; + /* + ┆* Should the accounting and control be hierarchical, per subtree? + ┆*/ + bool use_hierarchy; + unsigned long kmem_account_flags; /* See KMEM_ACCOUNTED_*, below */ + + int oom_kill; + bool oom_lock; + atomic_t under_oom; + atomic_t oom_wakeups; + + atomic_t refcnt; + + int swappiness; + + int priority; + + bool oom_kill_all; + bool use_priority_oom; + /* OOM-Killer disable */ + int oom_kill_disable; + + /* set when res.limit == memsw.limit */ + bool memsw_is_minimum; + + /* protect arrays of thresholds */ + struct mutex thresholds_lock; + + /* thresholds for memory usage. RCU-protected */ + struct mem_cgroup_thresholds thresholds; + + /* thresholds for mem+swap usage. RCU-protected */ + struct mem_cgroup_thresholds memsw_thresholds; + + /* For oom notifier event fd */ + struct list_head oom_notify; + +#ifdef CONFIG_CGROUP_WRITEBACK + struct list_head cgwb_list; + struct wb_domain cgwb_domain; +#endif + + /* + ┆* Should we move charges of a task when a task is moved into this + ┆* mem_cgroup ? And what type of charges should we move ? + ┆*/ + unsigned long move_charge_at_immigrate; + /* + ┆* set> 0 if pages under this cgroup are moving to other cgroup. + ┆*/ + atomic_t moving_account; + /* taken only while moving_account> 0 */ + spinlock_t move_lock; + struct task_struct *move_lock_task; + unsigned long move_lock_flags; + /* + ┆* percpu counter. + ┆*/ + struct mem_cgroup_stat_cpu __percpu *stat; + spinlock_t pcp_counter_lock; + +#ifdef CONFIG_CGROUP_WRITEBACK + int dirty_ratio; + int dirty_bg_ratio; +#endif + atomic_t wmark_ratio; + atomic64_t wmark_extra; + atomic_t force_empty_ctl; + + bool kswapd_stop; /* Protected by kswapds_spinlock */ + struct mutex kswapd_mutex; + wait_queue_head_t *kswapd_wait; + + atomic_t dead_count; +#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_INET) + struct tcp_memcontrol tcp_mem; +#endif +#if defined(CONFIG_MEMCG_KMEM) + /* analogous to slab_common's slab_caches list. per-memcg */ + struct list_head memcg_slab_caches; + /* Not a spinlock, we can take a lot of time walking the list */ + struct mutex slab_caches_mutex; + /* Index in the kmem_cache->memcg_params->memcg_caches array */ + int kmemcg_id; +#endif + + int last_scanned_node; +#if MAX_NUMNODES> 1 + nodemask_t scan_nodes; + atomic_t numainfo_events; + atomic_t numainfo_updating; +#endif + u64 direct_reclaim_time_count[DRT_COUNT]; + spinlock_t direct_reclaim_time_count_lock; + + u64 direct_reclaim_sched_time_histogram + [DRSTH_COUNT][DRSTH_TYPE_COUNT]; + spinlock_t direct_reclaim_sched_time_histogram_lock; + +#ifdef CONFIG_KIDLED + struct rw_semaphore idle_stats_rwsem; + unsigned long idle_scans; + struct kidled_scan_period scan_period; + int idle_stable_idx; + struct idle_page_stats idle_stats[KIDLED_STATS_NR_TYPE]; +#endif + + /* + * Per cgroup active and inactive list, similar to the + * per zone LRU lists. + * + * WARNING: This has to be the last element of the struct. Don't + * add new fields after this point. + */ + struct mem_cgroup_lru_info info; +}; -- Gitee

AltStyle によって変換されたページ (->オリジナル) /