ANDROID: Track per-process dmabuf RSS HWM
A per-process high watermark counter for dmabuf memory is useful for detecting bursty / transient allocations causing memory pressure spikes that don't appear in the dmabuf RSS counter when userspace reacts to memory pressure and reads RSS after buffers have already been freed. The /proc/<pid>/dmabuf_rss_hwm file in procfs now reports the maximum value of /proc/<pid>/dmabuf_rss during the lifetime of the process. The value of /proc/<pid>/dmabuf_rss_hwm can be reset to the current value of /proc/<pid>/dmabuf_rss by writing "0" to the file. Bug: 424648392 Change-Id: I184d83d48ec63b805b712f19e121199a63095965 Signed-off-by: T.J. Mercier <tjmercier@google.com>
This commit is contained in:
committed by
Suren Baghdasaryan
parent
f44d593749
commit
bddab7cf5d
@@ -187,6 +187,14 @@ static int new_task_dmabuf_record(struct task_struct *task, struct dma_buf *dmab
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
task->dmabuf_info->rss += dmabuf->size;
|
task->dmabuf_info->rss += dmabuf->size;
|
||||||
|
/*
|
||||||
|
* task->dmabuf_info->lock protects against concurrent writers, so no
|
||||||
|
* worries about stale rss_hwm between the read and write, and we don't
|
||||||
|
* need to cmpxchg here.
|
||||||
|
*/
|
||||||
|
if (task->dmabuf_info->rss > task->dmabuf_info->rss_hwm)
|
||||||
|
task->dmabuf_info->rss_hwm = task->dmabuf_info->rss;
|
||||||
|
|
||||||
rec->dmabuf = dmabuf;
|
rec->dmabuf = dmabuf;
|
||||||
rec->refcnt = 1;
|
rec->refcnt = 1;
|
||||||
list_add(&rec->node, &task->dmabuf_info->dmabufs);
|
list_add(&rec->node, &task->dmabuf_info->dmabufs);
|
||||||
|
@@ -3321,6 +3321,82 @@ static int proc_dmabuf_rss_show(struct seq_file *m, struct pid_namespace *ns,
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int proc_dmabuf_rss_hwm_show(struct seq_file *m, void *v)
|
||||||
|
{
|
||||||
|
struct inode *inode = m->private;
|
||||||
|
struct task_struct *task;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
task = get_proc_task(inode);
|
||||||
|
if (!task)
|
||||||
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (!task->dmabuf_info) {
|
||||||
|
pr_err("%s dmabuf accounting record was not allocated\n", __func__);
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(task->flags & PF_KTHREAD))
|
||||||
|
seq_printf(m, "%lld\n", READ_ONCE(task->dmabuf_info->rss_hwm));
|
||||||
|
else
|
||||||
|
seq_puts(m, "0\n");
|
||||||
|
|
||||||
|
out:
|
||||||
|
put_task_struct(task);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int proc_dmabuf_rss_hwm_open(struct inode *inode, struct file *filp)
|
||||||
|
{
|
||||||
|
return single_open(filp, proc_dmabuf_rss_hwm_show, inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
proc_dmabuf_rss_hwm_write(struct file *file, const char __user *buf,
|
||||||
|
size_t count, loff_t *offset)
|
||||||
|
{
|
||||||
|
struct inode *inode = file_inode(file);
|
||||||
|
struct task_struct *task;
|
||||||
|
unsigned long long val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtoull_from_user(buf, count, 10, &val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (val != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
task = get_proc_task(inode);
|
||||||
|
if (!task)
|
||||||
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (!task->dmabuf_info) {
|
||||||
|
pr_err("%s dmabuf accounting record was not allocated\n", __func__);
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock(&task->dmabuf_info->lock);
|
||||||
|
task->dmabuf_info->rss_hwm = task->dmabuf_info->rss;
|
||||||
|
spin_unlock(&task->dmabuf_info->lock);
|
||||||
|
|
||||||
|
out:
|
||||||
|
put_task_struct(task);
|
||||||
|
|
||||||
|
return ret < 0 ? ret : count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations proc_dmabuf_rss_hwm_operations = {
|
||||||
|
.open = proc_dmabuf_rss_hwm_open,
|
||||||
|
.write = proc_dmabuf_rss_hwm_write,
|
||||||
|
.read = seq_read,
|
||||||
|
.llseek = seq_lseek,
|
||||||
|
.release = single_release,
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3448,6 +3524,7 @@ static const struct pid_entry tgid_base_stuff[] = {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_DMA_SHARED_BUFFER
|
#ifdef CONFIG_DMA_SHARED_BUFFER
|
||||||
ONE("dmabuf_rss", S_IRUGO, proc_dmabuf_rss_show),
|
ONE("dmabuf_rss", S_IRUGO, proc_dmabuf_rss_show),
|
||||||
|
REG("dmabuf_rss_hwm", S_IRUGO|S_IWUSR, proc_dmabuf_rss_hwm_operations),
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -659,8 +659,8 @@ struct task_dma_buf_record {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct task_dma_buf_info - Holds a RSS counter, and a list of dmabufs for all
|
* struct task_dma_buf_info - Holds RSS and RSS HWM counters, and a list of
|
||||||
* tasks that share both mm_struct and files_struct.
|
* dmabufs for all tasks that share both mm_struct and files_struct.
|
||||||
*
|
*
|
||||||
* @rss: The sum of all dmabuf memory referenced by the tasks via memory
|
* @rss: The sum of all dmabuf memory referenced by the tasks via memory
|
||||||
* mappings or file descriptors in bytes. Buffers referenced more than
|
* mappings or file descriptors in bytes. Buffers referenced more than
|
||||||
@@ -668,12 +668,15 @@ struct task_dma_buf_record {
|
|||||||
* of both mmaps and FDs) only cause the buffer to be accounted to the
|
* of both mmaps and FDs) only cause the buffer to be accounted to the
|
||||||
* process once. Partial mappings cause the full size of the buffer to be
|
* process once. Partial mappings cause the full size of the buffer to be
|
||||||
* accounted, regardless of the size of the mapping.
|
* accounted, regardless of the size of the mapping.
|
||||||
|
* @rss_hwm: The maximum value of @rss over the lifetime of this struct. (Unless,
|
||||||
|
* reset by userspace.)
|
||||||
* @refcnt: The number of tasks sharing this struct.
|
* @refcnt: The number of tasks sharing this struct.
|
||||||
* @lock: Lock protecting writes for @rss, and reads/writes for @dmabufs.
|
* @lock: Lock protecting writes for @rss, and reads/writes for @dmabufs.
|
||||||
* @dmabufs: List of all dmabufs referenced by the tasks.
|
* @dmabufs: List of all dmabufs referenced by the tasks.
|
||||||
*/
|
*/
|
||||||
struct task_dma_buf_info {
|
struct task_dma_buf_info {
|
||||||
s64 rss;
|
s64 rss;
|
||||||
|
s64 rss_hwm;
|
||||||
refcount_t refcnt;
|
refcount_t refcnt;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
struct list_head dmabufs;
|
struct list_head dmabufs;
|
||||||
|
@@ -2314,6 +2314,7 @@ static int copy_dmabuf_info(u64 clone_flags, struct task_struct *p)
|
|||||||
if (current->dmabuf_info) {
|
if (current->dmabuf_info) {
|
||||||
spin_lock(¤t->dmabuf_info->lock);
|
spin_lock(¤t->dmabuf_info->lock);
|
||||||
p->dmabuf_info->rss = current->dmabuf_info->rss;
|
p->dmabuf_info->rss = current->dmabuf_info->rss;
|
||||||
|
p->dmabuf_info->rss_hwm = current->dmabuf_info->rss;
|
||||||
list_for_each_entry(rec, ¤t->dmabuf_info->dmabufs, node) {
|
list_for_each_entry(rec, ¤t->dmabuf_info->dmabufs, node) {
|
||||||
copy = kmalloc(sizeof(*copy), GFP_KERNEL);
|
copy = kmalloc(sizeof(*copy), GFP_KERNEL);
|
||||||
if (!copy) {
|
if (!copy) {
|
||||||
@@ -2328,6 +2329,7 @@ static int copy_dmabuf_info(u64 clone_flags, struct task_struct *p)
|
|||||||
spin_unlock(¤t->dmabuf_info->lock);
|
spin_unlock(¤t->dmabuf_info->lock);
|
||||||
} else {
|
} else {
|
||||||
p->dmabuf_info->rss = 0;
|
p->dmabuf_info->rss = 0;
|
||||||
|
p->dmabuf_info->rss_hwm = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Reference in New Issue
Block a user