The same information are shown in /proc/self/smaps. v2: account the task_diag_vma_stat struct in taskdiag_packet_size() Fix 8-byte alignment for vma and vma_stats // David Ahern Cc: David Ahern <dsahern@xxxxxxxxx> Signed-off-by: Andrey Vagin <avagin@xxxxxxxxxx> --- fs/proc/task_mmu.c | 14 +-------- include/linux/proc_fs.h | 17 +++++++++++ include/uapi/linux/task_diag.h | 25 +++++++++++++++++ kernel/taskdiag.c | 64 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 101 insertions(+), 19 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 6dee68d..c89f68c 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -435,18 +435,6 @@ const struct file_operations proc_tid_maps_operations = { #define PSS_SHIFT 12 #ifdef CONFIG_PROC_PAGE_MONITOR -struct mem_size_stats { - unsigned long resident; - unsigned long shared_clean; - unsigned long shared_dirty; - unsigned long private_clean; - unsigned long private_dirty; - unsigned long referenced; - unsigned long anonymous; - unsigned long anonymous_thp; - unsigned long swap; - u64 pss; -}; static void smaps_account(struct mem_size_stats *mss, struct page *page, unsigned long size, bool young, bool dirty) @@ -526,7 +514,7 @@ static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr, } #endif -static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, +int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, struct mm_walk *walk) { struct vm_area_struct *vma = walk->vma; diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 497e58c..62d0079 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -92,4 +92,21 @@ struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter); struct task_struct * task_next_child(struct task_struct *parent, struct task_struct *prev, unsigned int pos); +struct mem_size_stats { + unsigned long resident; + unsigned long shared_clean; + unsigned long shared_dirty; + unsigned long private_clean; + unsigned long private_dirty; + unsigned long referenced; + unsigned long anonymous; + unsigned long anonymous_thp; + unsigned long swap; + u64 pss; +}; + +struct mm_walk; +int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, + struct mm_walk *walk); + #endif /* _LINUX_PROC_FS_H */ diff --git a/include/uapi/linux/task_diag.h b/include/uapi/linux/task_diag.h index 943d8d1..73d33c8 100644 --- a/include/uapi/linux/task_diag.h +++ b/include/uapi/linux/task_diag.h @@ -11,6 +11,7 @@ enum { TASK_DIAG_CRED, TASK_DIAG_STAT, TASK_DIAG_VMA, + TASK_DIAG_VMA_STAT, /* other attributes */ TASK_DIAG_PID = 64, /* u32 */ @@ -23,6 +24,7 @@ enum { #define TASK_DIAG_SHOW_CRED (1ULL << TASK_DIAG_CRED) #define TASK_DIAG_SHOW_STAT (1ULL << TASK_DIAG_STAT) #define TASK_DIAG_SHOW_VMA (1ULL << TASK_DIAG_VMA) +#define TASK_DIAG_SHOW_VMA_STAT (1ULL << TASK_DIAG_VMA_STAT) enum { TASK_DIAG_RUNNING, @@ -96,6 +98,19 @@ struct task_diag_creds { #define TASK_DIAG_VMA_F_NOHUGEPAGE (1ULL << 26) #define TASK_DIAG_VMA_F_MERGEABLE (1ULL << 27) +struct task_diag_vma_stat { + __u64 resident; + __u64 shared_clean; + __u64 shared_dirty; + __u64 private_clean; + __u64 private_dirty; + __u64 referenced; + __u64 anonymous; + __u64 anonymous_thp; + __u64 swap; + __u64 pss; +} __attribute__((__aligned__(NLA_ALIGNTO))); + /* task_diag_vma must be NLA_ALIGN'ed */ struct task_diag_vma { __u64 start, end; @@ -108,6 +123,8 @@ struct task_diag_vma { __u16 vma_len; __u16 name_off; __u16 name_len; + __u16 stat_off; + __u16 stat_len; } __attribute__((__aligned__(NLA_ALIGNTO))); static inline char *task_diag_vma_name(struct task_diag_vma *vma) @@ -118,6 +135,14 @@ static inline char *task_diag_vma_name(struct task_diag_vma *vma) return ((char *)vma) + vma->name_off; } +static inline struct task_diag_vma_stat *task_diag_vma_stat(struct task_diag_vma *vma) +{ + if (!vma->stat_len) + return NULL; + + return ((void *)vma) + vma->stat_off; +} + #define TASK_DIAG_DUMP_ALL 0 #define TASK_DIAG_DUMP_CHILDREN 1 diff --git a/kernel/taskdiag.c b/kernel/taskdiag.c index c488c1b..8e00c3e 100644 --- a/kernel/taskdiag.c +++ b/kernel/taskdiag.c @@ -24,11 +24,17 @@ static size_t taskdiag_packet_size(u64 show_flags, int n_vma) size += nla_total_size(sizeof(struct taskstats)); if (show_flags & TASK_DIAG_SHOW_VMA && n_vma > 0) { + size_t entry_size; + /* * 128 is a schwag on average path length for maps; used to * ballpark initial memory allocation for genl msg */ - size += nla_total_size(sizeof(struct task_diag_vma) * n_vma + 128); + entry_size = sizeof(struct task_diag_vma) + 128; + + if (show_flags & TASK_DIAG_SHOW_VMA_STAT) + entry_size += sizeof(struct task_diag_vma_stat); + size += nla_total_size(entry_size * n_vma); } return size; @@ -292,8 +298,37 @@ out: return name; } +static void fill_diag_vma_stat(struct vm_area_struct *vma, struct task_diag_vma_stat *stat) +{ + struct task_diag_vma_stat tmp; + struct mem_size_stats mss; + struct mm_walk smaps_walk = { + .pmd_entry = smaps_pte_range, + .mm = vma->vm_mm, + .private = &mss, + }; + + memset(&mss, 0, sizeof mss); + memset(&tmp, 0, sizeof(tmp)); + + /* mmap_sem is held in m_start */ + walk_page_vma(vma, &smaps_walk); + + tmp.resident = mss.resident; + tmp.pss = mss.pss; + tmp.shared_clean = mss.shared_clean; + tmp.private_clean = mss.private_clean; + tmp.private_dirty = mss.private_dirty; + tmp.referenced = mss.referenced; + tmp.anonymous = mss.anonymous; + tmp.anonymous_thp = mss.anonymous_thp; + tmp.swap = mss.swap; + + memcpy(stat, &tmp, sizeof(*stat)); +} + static int fill_vma(struct task_struct *p, struct sk_buff *skb, - struct netlink_callback *cb, bool *progress) + struct netlink_callback *cb, bool *progress, u64 show_flags) { struct vm_area_struct *vma; struct mm_struct *mm; @@ -301,7 +336,7 @@ static int fill_vma(struct task_struct *p, struct sk_buff *skb, struct task_diag_vma *diag_vma; unsigned long mark = 0; char *page; - int i, rc = -EMSGSIZE; + int i, rc = -EMSGSIZE, size; if (cb) mark = cb->args[3]; @@ -316,6 +351,10 @@ static int fill_vma(struct task_struct *p, struct sk_buff *skb, return -ENOMEM; } + size = NLA_ALIGN(sizeof(struct task_diag_vma)); + if (show_flags & TASK_DIAG_SHOW_VMA_STAT) + size += NLA_ALIGN(sizeof(struct task_diag_vma_stat)); + down_read(&mm->mmap_sem); for (vma = mm->mmap; vma; vma = vma->vm_next, i++) { unsigned char *b = skb_tail_pointer(skb); @@ -328,13 +367,13 @@ static int fill_vma(struct task_struct *p, struct sk_buff *skb, /* setup pointer for next map */ if (attr == NULL) { - attr = nla_reserve(skb, TASK_DIAG_VMA, sizeof(*diag_vma)); + attr = nla_reserve(skb, TASK_DIAG_VMA, size); if (!attr) goto err; diag_vma = nla_data(attr); } else { - diag_vma = nla_reserve_nohdr(skb, sizeof(*diag_vma)); + diag_vma = nla_reserve_nohdr(skb, size); if (diag_vma == NULL) { nlmsg_trim(skb, b); @@ -344,6 +383,19 @@ static int fill_vma(struct task_struct *p, struct sk_buff *skb, fill_diag_vma(vma, diag_vma); + if (show_flags & TASK_DIAG_SHOW_VMA_STAT) { + struct task_diag_vma_stat *stat; + + stat = (void *) diag_vma + NLA_ALIGN(sizeof(struct task_diag_vma)); + + fill_diag_vma_stat(vma, stat); + diag_vma->stat_len = sizeof(struct task_diag_vma_stat); + diag_vma->stat_off = (void *) stat - (void *)diag_vma; + } else { + diag_vma->stat_len = 0; + diag_vma->stat_off = 0; + } + name = get_vma_name(vma, page); if (IS_ERR(name)) { nlmsg_trim(skb, b); @@ -441,7 +493,7 @@ static int task_diag_fill(struct task_struct *tsk, struct sk_buff *skb, if (show_flags & TASK_DIAG_SHOW_VMA) { if (i >= n) - err = fill_vma(tsk, skb, cb, &progress); + err = fill_vma(tsk, skb, cb, &progress, show_flags); if (err) goto err; i++; -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-api" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html