forked from torvalds/linux
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
dmabuf: Add the capability to expose DMA-BUF stats in sysfs
This patch allows statistics to be enabled for each DMA-BUF in sysfs by enabling the config CONFIG_DMABUF_SYSFS_STATS. The following stats will be exposed by the interface: /sys/kernel/dmabuf/buffers/<inode_number>/exporter_name /sys/kernel/dmabuf/buffers/<inode_number>/size /sys/kernel/dmabuf/buffers/<inode_number>/attachments/<attach_uid>/device /sys/kernel/dmabuf/buffers/<inode_number>/attachments/<attach_uid>/map_counter The inode_number is unique for each DMA-BUF and was added earlier [1] in order to allow userspace to track DMA-BUF usage across different processes. Currently, this information is exposed in /sys/kernel/debug/dma_buf/bufinfo. However, since debugfs is considered unsafe to be mounted in production, it is being duplicated in sysfs. This information will be used to derive DMA-BUF per-exporter stats and per-device usage stats for Android Bug reports. The corresponding userspace changes can be found at [2]. Telemetry tools will also capture this information(along with other memory metrics) periodically as well as on important events like a foreground app kill (which might have been triggered by Low Memory Killer). It will also contribute to provide a snapshot of the system memory usage on other events such as OOM kills and Application Not Responding events. A shell script that can be run on a classic Linux environment to read out the DMA-BUF statistics can be found at [3](suggested by John Stultz). The patch contains the following improvements over the previous version: 1) Each attachment is represented by its own directory to allow creating a symlink to the importing device and to also provide room for future expansion. 2) The number of distinct mappings of each attachment is exposed in a separate file. 3) The per-buffer statistics are now in /sys/kernel/dmabuf/buffers inorder to make the interface expandable in future. All of the improvements above are based on suggestions/feedback from Daniel Vetter and Christian König. [1]: https://lore.kernel.org/patchwork/patch/1088791/ [2]: https://android-review.googlesource.com/q/topic:%22dmabuf-sysfs%22+(status:open%20OR%20status:merged) [3]: https://android-review.googlesource.com/c/platform/system/memory/libmeminfo/+/1549734 Signed-off-by: Hridya Valsaraju <hridya@google.com>
- Loading branch information
1 parent
45dfb8a
commit 1ce52d5
Showing
7 changed files
with
466 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
What: /sys/kernel/dmabuf/buffers | ||
Date: January 2021 | ||
KernelVersion: v5.12 | ||
Contact: Hridya Valsaraju <hridya@google.com> | ||
Description: The /sys/kernel/dmabuf/buffers directory contains a | ||
snapshot of the internal state of every DMA-BUF. | ||
/sys/kernel/dmabuf/buffers/<inode_number> will contain the | ||
statistics for the DMA-BUF with the unique inode number | ||
<inode_number> | ||
Users: kernel memory tuning/debugging tools | ||
|
||
What: /sys/kernel/dmabuf/buffers/<inode_number>/exporter_name | ||
Date: January 2021 | ||
KernelVersion: v5.12 | ||
Contact: Hridya Valsaraju <hridya@google.com> | ||
Description: This file is read-only and contains the name of the exporter of | ||
the DMA-BUF. | ||
|
||
What: /sys/kernel/dmabuf/buffers/<inode_number>/size | ||
Date: January 2021 | ||
KernelVersion: v5.12 | ||
Contact: Hridya Valsaraju <hridya@google.com> | ||
Description: This file is read-only and specifies the size of the DMA-BUF in | ||
bytes. | ||
|
||
What: /sys/kernel/dmabuf/buffers/<inode_number>/attachments | ||
Date: January 2021 | ||
KernelVersion: v5.12 | ||
Contact: Hridya Valsaraju <hridya@google.com> | ||
Description: This directory will contain subdirectories representing every | ||
attachment of the DMA-BUF. | ||
|
||
What: /sys/kernel/dmabuf/buffers/<inode_number>/attachments/<attachment_uid> | ||
Date: January 2021 | ||
KernelVersion: v5.12 | ||
Contact: Hridya Valsaraju <hridya@google.com> | ||
Description: This directory will contain information on the attaching device | ||
and the number of current distinct device mappings. | ||
|
||
What: /sys/kernel/dmabuf/buffers/<inode_number>/attachments/<attachment_uid>/device | ||
Date: January 2021 | ||
KernelVersion: v5.12 | ||
Contact: Hridya Valsaraju <hridya@google.com> | ||
Description: This file is read-only and is a symlink to the attaching devices's | ||
sysfs entry. | ||
|
||
What: /sys/kernel/dmabuf/buffers/<inode_number>/attachments/<attachment_uid>/map_counter | ||
Date: January 2021 | ||
KernelVersion: v5.12 | ||
Contact: Hridya Valsaraju <hridya@google.com> | ||
Description: This file is read-only and contains a map_counter indicating the | ||
number of distinct device mappings of the attachment. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,283 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
/* | ||
* DMA-BUF sysfs statistics. | ||
* | ||
* Copyright (C) 2021 Google LLC. | ||
*/ | ||
|
||
#include <linux/dma-buf.h> | ||
#include <linux/dma-resv.h> | ||
#include <linux/kobject.h> | ||
#include <linux/printk.h> | ||
#include <linux/slab.h> | ||
#include <linux/sysfs.h> | ||
|
||
#define to_dma_buf_entry_from_kobj(x) container_of(x, struct dma_buf_sysfs_entry, kobj) | ||
|
||
struct dma_buf_stats_attribute { | ||
struct attribute attr; | ||
ssize_t (*show)(struct dma_buf *dmabuf, | ||
struct dma_buf_stats_attribute *attr, char *buf); | ||
}; | ||
#define to_dma_buf_stats_attr(x) container_of(x, struct dma_buf_stats_attribute, attr) | ||
|
||
static ssize_t dma_buf_stats_attribute_show(struct kobject *kobj, | ||
struct attribute *attr, | ||
char *buf) | ||
{ | ||
struct dma_buf_stats_attribute *attribute; | ||
struct dma_buf_sysfs_entry *sysfs_entry; | ||
struct dma_buf *dmabuf; | ||
|
||
attribute = to_dma_buf_stats_attr(attr); | ||
sysfs_entry = to_dma_buf_entry_from_kobj(kobj); | ||
dmabuf = sysfs_entry->dmabuf; | ||
|
||
if (!dmabuf || !attribute->show) | ||
return -EIO; | ||
|
||
return attribute->show(dmabuf, attribute, buf); | ||
} | ||
|
||
static const struct sysfs_ops dma_buf_stats_sysfs_ops = { | ||
.show = dma_buf_stats_attribute_show, | ||
}; | ||
|
||
static ssize_t exporter_name_show(struct dma_buf *dmabuf, | ||
struct dma_buf_stats_attribute *attr, | ||
char *buf) | ||
{ | ||
return sysfs_emit(buf, "%s\n", dmabuf->exp_name); | ||
} | ||
|
||
static ssize_t size_show(struct dma_buf *dmabuf, | ||
struct dma_buf_stats_attribute *attr, | ||
char *buf) | ||
{ | ||
return sysfs_emit(buf, "%zu\n", dmabuf->size); | ||
} | ||
|
||
static struct dma_buf_stats_attribute exporter_name_attribute = | ||
__ATTR_RO(exporter_name); | ||
static struct dma_buf_stats_attribute size_attribute = __ATTR_RO(size); | ||
|
||
static struct attribute *dma_buf_stats_default_attrs[] = { | ||
&exporter_name_attribute.attr, | ||
&size_attribute.attr, | ||
NULL, | ||
}; | ||
ATTRIBUTE_GROUPS(dma_buf_stats_default); | ||
|
||
static void dma_buf_sysfs_release(struct kobject *kobj) | ||
{ | ||
struct dma_buf_sysfs_entry *sysfs_entry; | ||
|
||
sysfs_entry = to_dma_buf_entry_from_kobj(kobj); | ||
kfree(sysfs_entry); | ||
} | ||
|
||
static struct kobj_type dma_buf_ktype = { | ||
.sysfs_ops = &dma_buf_stats_sysfs_ops, | ||
.release = dma_buf_sysfs_release, | ||
.default_groups = dma_buf_stats_default_groups, | ||
}; | ||
|
||
#define to_dma_buf_attach_entry_from_kobj(x) container_of(x, struct dma_buf_attach_sysfs_entry, kobj) | ||
|
||
struct dma_buf_attach_stats_attribute { | ||
struct attribute attr; | ||
ssize_t (*show)(struct dma_buf_attach_sysfs_entry *sysfs_entry, | ||
struct dma_buf_attach_stats_attribute *attr, char *buf); | ||
}; | ||
#define to_dma_buf_attach_stats_attr(x) container_of(x, struct dma_buf_attach_stats_attribute, attr) | ||
|
||
static ssize_t dma_buf_attach_stats_attribute_show(struct kobject *kobj, | ||
struct attribute *attr, | ||
char *buf) | ||
{ | ||
struct dma_buf_attach_stats_attribute *attribute; | ||
struct dma_buf_attach_sysfs_entry *sysfs_entry; | ||
|
||
attribute = to_dma_buf_attach_stats_attr(attr); | ||
sysfs_entry = to_dma_buf_attach_entry_from_kobj(kobj); | ||
|
||
if (!attribute->show) | ||
return -EIO; | ||
|
||
return attribute->show(sysfs_entry, attribute, buf); | ||
} | ||
|
||
static const struct sysfs_ops dma_buf_attach_stats_sysfs_ops = { | ||
.show = dma_buf_attach_stats_attribute_show, | ||
}; | ||
|
||
static ssize_t map_counter_show(struct dma_buf_attach_sysfs_entry *sysfs_entry, | ||
struct dma_buf_attach_stats_attribute *attr, | ||
char *buf) | ||
{ | ||
return sysfs_emit(buf, "%u\n", sysfs_entry->map_counter); | ||
} | ||
|
||
static struct dma_buf_attach_stats_attribute map_counter_attribute = | ||
__ATTR_RO(map_counter); | ||
|
||
static struct attribute *dma_buf_attach_stats_default_attrs[] = { | ||
&map_counter_attribute.attr, | ||
NULL, | ||
}; | ||
ATTRIBUTE_GROUPS(dma_buf_attach_stats_default); | ||
|
||
static void dma_buf_attach_sysfs_release(struct kobject *kobj) | ||
{ | ||
struct dma_buf_attach_sysfs_entry *sysfs_entry; | ||
|
||
sysfs_entry = to_dma_buf_attach_entry_from_kobj(kobj); | ||
kfree(sysfs_entry); | ||
} | ||
|
||
static struct kobj_type dma_buf_attach_ktype = { | ||
.sysfs_ops = &dma_buf_attach_stats_sysfs_ops, | ||
.release = dma_buf_attach_sysfs_release, | ||
.default_groups = dma_buf_attach_stats_default_groups, | ||
}; | ||
|
||
void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach) | ||
{ | ||
struct dma_buf_attach_sysfs_entry *sysfs_entry; | ||
|
||
sysfs_entry = attach->sysfs_entry; | ||
if (!sysfs_entry) | ||
return; | ||
|
||
sysfs_delete_link(&sysfs_entry->kobj, &attach->dev->kobj, "device"); | ||
|
||
kobject_del(&sysfs_entry->kobj); | ||
kobject_put(&sysfs_entry->kobj); | ||
} | ||
|
||
int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach, | ||
unsigned int uid) | ||
{ | ||
struct dma_buf_attach_sysfs_entry *sysfs_entry; | ||
int ret; | ||
struct dma_buf *dmabuf; | ||
|
||
if (!attach) | ||
return -EINVAL; | ||
|
||
dmabuf = attach->dmabuf; | ||
|
||
sysfs_entry = kzalloc(sizeof(struct dma_buf_attach_sysfs_entry), | ||
GFP_KERNEL); | ||
if (!sysfs_entry) | ||
return -ENOMEM; | ||
|
||
sysfs_entry->kobj.kset = dmabuf->sysfs_entry->attach_stats_kset; | ||
|
||
attach->sysfs_entry = sysfs_entry; | ||
|
||
ret = kobject_init_and_add(&sysfs_entry->kobj, &dma_buf_attach_ktype, | ||
NULL, "%u", uid); | ||
if (ret) | ||
goto kobj_err; | ||
|
||
ret = sysfs_create_link(&sysfs_entry->kobj, &attach->dev->kobj, | ||
"device"); | ||
if (ret) | ||
goto link_err; | ||
|
||
return 0; | ||
|
||
link_err: | ||
kobject_del(&sysfs_entry->kobj); | ||
kobj_err: | ||
kobject_put(&sysfs_entry->kobj); | ||
attach->sysfs_entry = NULL; | ||
|
||
return ret; | ||
} | ||
void dma_buf_stats_teardown(struct dma_buf *dmabuf) | ||
{ | ||
struct dma_buf_sysfs_entry *sysfs_entry; | ||
|
||
sysfs_entry = dmabuf->sysfs_entry; | ||
if (!sysfs_entry) | ||
return; | ||
|
||
kset_unregister(sysfs_entry->attach_stats_kset); | ||
kobject_del(&sysfs_entry->kobj); | ||
kobject_put(&sysfs_entry->kobj); | ||
} | ||
|
||
static struct kset *dma_buf_stats_kset; | ||
static struct kset *dma_buf_per_buffer_stats_kset; | ||
int dma_buf_init_sysfs_statistics(void) | ||
{ | ||
dma_buf_stats_kset = kset_create_and_add("dmabuf", NULL, kernel_kobj); | ||
if (!dma_buf_stats_kset) | ||
return -ENOMEM; | ||
|
||
dma_buf_per_buffer_stats_kset = kset_create_and_add("buffers", NULL, | ||
&dma_buf_stats_kset->kobj); | ||
if (!dma_buf_per_buffer_stats_kset) { | ||
kset_unregister(dma_buf_stats_kset); | ||
return -ENOMEM; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
void dma_buf_uninit_sysfs_statistics(void) | ||
{ | ||
kset_unregister(dma_buf_per_buffer_stats_kset); | ||
kset_unregister(dma_buf_stats_kset); | ||
} | ||
|
||
int dma_buf_stats_setup(struct dma_buf *dmabuf) | ||
{ | ||
struct dma_buf_sysfs_entry *sysfs_entry; | ||
int ret; | ||
struct kset *attach_stats_kset; | ||
|
||
if (!dmabuf || !dmabuf->file) | ||
return -EINVAL; | ||
|
||
if (!dmabuf->exp_name) { | ||
pr_err("exporter name must not be empty if stats needed\n"); | ||
return -EINVAL; | ||
} | ||
|
||
sysfs_entry = kzalloc(sizeof(struct dma_buf_sysfs_entry), GFP_KERNEL); | ||
if (!sysfs_entry) | ||
return -ENOMEM; | ||
|
||
sysfs_entry->kobj.kset = dma_buf_per_buffer_stats_kset; | ||
sysfs_entry->dmabuf = dmabuf; | ||
|
||
dmabuf->sysfs_entry = sysfs_entry; | ||
|
||
/* create the directory for buffer stats */ | ||
ret = kobject_init_and_add(&sysfs_entry->kobj, &dma_buf_ktype, NULL, | ||
"%lu", file_inode(dmabuf->file)->i_ino); | ||
if (ret) | ||
goto err_sysfs_dmabuf; | ||
|
||
/* create the directory for attachment stats */ | ||
attach_stats_kset = kset_create_and_add("attachments", NULL, | ||
&sysfs_entry->kobj); | ||
if (!attach_stats_kset) { | ||
ret = -ENOMEM; | ||
goto err_sysfs_attach; | ||
} | ||
|
||
sysfs_entry->attach_stats_kset = attach_stats_kset; | ||
|
||
return 0; | ||
|
||
err_sysfs_attach: | ||
kobject_del(&sysfs_entry->kobj); | ||
err_sysfs_dmabuf: | ||
kobject_put(&sysfs_entry->kobj); | ||
dmabuf->sysfs_entry = NULL; | ||
return ret; | ||
} |
Oops, something went wrong.