Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 533e4ed3 authored by Colin Cross's avatar Colin Cross Committed by Alistair Strachan
Browse files

ANDROID: mm: add a field to store names for private anonymous memory



Userspace processes often have multiple allocators that each do
anonymous mmaps to get memory.  When examining memory usage of
individual processes or systems as a whole, it is useful to be
able to break down the various heaps that were allocated by
each layer and examine their size, RSS, and physical memory
usage.

This patch adds a user pointer to the shared union in
vm_area_struct that points to a null terminated string inside
the user process containing a name for the vma.  vmas that
point to the same address will be merged, but vmas that
point to equivalent strings at different addresses will
not be merged.

Userspace can set the name for a region of memory by calling
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start, len, (unsigned long)name);
Setting the name to NULL clears it.

The names of named anonymous vmas are shown in /proc/pid/maps
as [anon:<name>] and in /proc/pid/smaps in a new "Name" field
that is only present for named vmas.  If the userspace pointer
is no longer valid all or part of the name will be replaced
with "<fault>".

The idea to store a userspace pointer to reduce the complexity
within mm (at the expense of the complexity of reading
/proc/pid/mem) came from Dave Hansen.  This results in no
runtime overhead in the mm subsystem other than comparing
the anon_name pointers when considering vma merging.  The pointer
is stored in a union with fieds that are only used on file-backed
mappings, so it does not increase memory usage.

Includes fix from Jed Davis <jld@mozilla.com> for typo in
prctl_set_vma_anon_name, which could attempt to set the name
across two vmas at the same time due to a typo, which might
corrupt the vma list.  Fix it to use tmp instead of end to limit
the name setting to a single vma at a time.

Bug: 120441514
Change-Id: I9aa7b6b5ef536cd780599ba4e2fba8ceebe8b59f
Signed-off-by: default avatarDmitry Shmidt <dimitrysh@google.com>
[AmitP: Fix get_user_pages_remote() call to align with upstream commit
        5b56d49f ("mm: add locked parameter to get_user_pages_remote()")]
Signed-off-by: default avatarAmit Pundir <amit.pundir@linaro.org>
parent 0c39a6a6
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -398,6 +398,8 @@ is not associated with a file:
 [stack]                  = the stack of the main process
 [vdso]                   = the "virtual dynamic shared object",
                            the kernel system call handler
 [anon:<name>]            = an anonymous mapping that has been
                            named by userspace

 or if empty, the mapping is anonymous.

@@ -426,6 +428,7 @@ KernelPageSize: 4 kB
MMUPageSize:           4 kB
Locked:                0 kB
VmFlags: rd ex mr mw me dw
Name:           name from userspace

the first of these lines shows the same information as is displayed for the
mapping in /proc/PID/maps.  The remaining lines show the size of the mapping
@@ -498,6 +501,9 @@ Note that there is no guarantee that every flag and associated mnemonic will
be present in all further kernel releases. Things get changed, the flags may
be vanished or the reverse -- new added.

The "Name" field will only be present on a mapping that has been named by
userspace, and will show the name passed in by userspace.

This file is only present if the CONFIG_MMU kernel configuration option is
enabled.

+63 −1
Original line number Diff line number Diff line
@@ -123,6 +123,56 @@ static void release_task_mempolicy(struct proc_maps_private *priv)
}
#endif

static void seq_print_vma_name(struct seq_file *m, struct vm_area_struct *vma)
{
	const char __user *name = vma_get_anon_name(vma);
	struct mm_struct *mm = vma->vm_mm;

	unsigned long page_start_vaddr;
	unsigned long page_offset;
	unsigned long num_pages;
	unsigned long max_len = NAME_MAX;
	int i;

	page_start_vaddr = (unsigned long)name & PAGE_MASK;
	page_offset = (unsigned long)name - page_start_vaddr;
	num_pages = DIV_ROUND_UP(page_offset + max_len, PAGE_SIZE);

	seq_puts(m, "[anon:");

	for (i = 0; i < num_pages; i++) {
		int len;
		int write_len;
		const char *kaddr;
		long pages_pinned;
		struct page *page;

		pages_pinned = get_user_pages_remote(current, mm,
				page_start_vaddr, 1, 0, &page, NULL, NULL);
		if (pages_pinned < 1) {
			seq_puts(m, "<fault>]");
			return;
		}

		kaddr = (const char *)kmap(page);
		len = min(max_len, PAGE_SIZE - page_offset);
		write_len = strnlen(kaddr + page_offset, len);
		seq_write(m, kaddr + page_offset, write_len);
		kunmap(page);
		put_page(page);

		/* if strnlen hit a null terminator then we're done */
		if (write_len != len)
			break;

		max_len -= len;
		page_offset = 0;
		page_start_vaddr += PAGE_SIZE;
	}

	seq_putc(m, ']');
}

static void vma_stop(struct proc_maps_private *priv)
{
	struct mm_struct *mm = priv->mm;
@@ -344,8 +394,15 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma)
			goto done;
		}

		if (is_stack(vma))
		if (is_stack(vma)) {
			name = "[stack]";
			goto done;
		}

		if (vma_get_anon_name(vma)) {
			seq_pad(m, ' ');
			seq_print_vma_name(m, vma);
		}
	}

done:
@@ -782,6 +839,11 @@ static int show_smap(struct seq_file *m, void *v)
	smap_gather_stats(vma, &mss);

	show_map_vma(m, vma);
	if (vma_get_anon_name(vma)) {
		seq_puts(m, "Name:           ");
		seq_print_vma_name(m, vma);
		seq_putc(m, '\n');
	}

	SEQ_PUT_DEC("Size:           ", vma->vm_end - vma->vm_start);
	SEQ_PUT_DEC(" kB\nKernelPageSize: ", vma_kernel_pagesize(vma));
+6 −3
Original line number Diff line number Diff line
@@ -890,7 +890,8 @@ static int userfaultfd_release(struct inode *inode, struct file *file)
				 new_flags, vma->anon_vma,
				 vma->vm_file, vma->vm_pgoff,
				 vma_policy(vma),
				 NULL_VM_UFFD_CTX);
				 NULL_VM_UFFD_CTX,
				 vma_get_anon_name(vma));
		if (prev)
			vma = prev;
		else
@@ -1423,7 +1424,8 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
		prev = vma_merge(mm, prev, start, vma_end, new_flags,
				 vma->anon_vma, vma->vm_file, vma->vm_pgoff,
				 vma_policy(vma),
				 ((struct vm_userfaultfd_ctx){ ctx }));
				 ((struct vm_userfaultfd_ctx){ ctx }),
				 vma_get_anon_name(vma));
		if (prev) {
			vma = prev;
			goto next;
@@ -1581,7 +1583,8 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
		prev = vma_merge(mm, prev, start, vma_end, new_flags,
				 vma->anon_vma, vma->vm_file, vma->vm_pgoff,
				 vma_policy(vma),
				 NULL_VM_UFFD_CTX);
				 NULL_VM_UFFD_CTX,
				 vma_get_anon_name(vma));
		if (prev) {
			vma = prev;
			goto next;
+1 −1
Original line number Diff line number Diff line
@@ -2227,7 +2227,7 @@ static inline int vma_adjust(struct vm_area_struct *vma, unsigned long start,
extern struct vm_area_struct *vma_merge(struct mm_struct *,
	struct vm_area_struct *prev, unsigned long addr, unsigned long end,
	unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t,
	struct mempolicy *, struct vm_userfaultfd_ctx);
	struct mempolicy *, struct vm_userfaultfd_ctx, const char __user *);
extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *);
extern int __split_vma(struct mm_struct *, struct vm_area_struct *,
	unsigned long addr, int new_below);
+20 −4
Original line number Diff line number Diff line
@@ -290,11 +290,18 @@ struct vm_area_struct {
	/*
	 * For areas with an address space and backing store,
	 * linkage into the address_space->i_mmap interval tree.
	 *
	 * For private anonymous mappings, a pointer to a null terminated string
	 * in the user process containing the name given to the vma, or NULL
	 * if unnamed.
	 */
	union {
		struct {
			struct rb_node rb;
			unsigned long rb_subtree_last;
		} shared;
		const char __user *anon_name;
	};

	/*
	 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
@@ -649,4 +656,13 @@ typedef struct {
	unsigned long val;
} swp_entry_t;

/* Return the name for an anonymous mapping or NULL for a file-backed mapping */
static inline const char __user *vma_get_anon_name(struct vm_area_struct *vma)
{
	if (vma->vm_file)
		return NULL;

	return vma->anon_name;
}

#endif /* _LINUX_MM_TYPES_H */
Loading