2021-01-19 23:00:52

by Hridya Valsaraju

[permalink] [raw]
Subject: [PATCH v2] 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 <[email protected]>
---
Changes in v2:
-Move statistics to /sys/kernel/dmabuf/buffers in oder to allow addition
of other DMA-BUF-related sysfs stats in future. Based on feedback from
Daniel Vetter.
-Each attachment has its own directory to represent attaching devices as
symlinks and to introduce map_count as a separate file. Based on
feedback from Daniel Vetter and Christian König. Thank you both!
-Commit messages updated to point to userspace code in AOSP that will
read the DMA-BUF sysfs stats.

.../ABI/testing/sysfs-kernel-dmabuf-buffers | 52 ++++
drivers/dma-buf/Kconfig | 11 +
drivers/dma-buf/Makefile | 1 +
drivers/dma-buf/dma-buf-sysfs-stats.c | 283 ++++++++++++++++++
drivers/dma-buf/dma-buf-sysfs-stats.h | 62 ++++
drivers/dma-buf/dma-buf.c | 37 +++
include/linux/dma-buf.h | 20 ++
7 files changed, 466 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
create mode 100644 drivers/dma-buf/dma-buf-sysfs-stats.c
create mode 100644 drivers/dma-buf/dma-buf-sysfs-stats.h

diff --git a/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers b/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
new file mode 100644
index 000000000000..6f7c65209f07
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
@@ -0,0 +1,52 @@
+What: /sys/kernel/dmabuf/buffers
+Date: January 2021
+KernelVersion: v5.12
+Contact: Hridya Valsaraju <[email protected]>
+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 <[email protected]>
+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 <[email protected]>
+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 <[email protected]>
+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 <[email protected]>
+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 <[email protected]>
+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 <[email protected]>
+Description: This file is read-only and contains a map_counter indicating the
+ number of distinct device mappings of the attachment.
diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig
index 4f8224a6ac95..27e6a2dafeaa 100644
--- a/drivers/dma-buf/Kconfig
+++ b/drivers/dma-buf/Kconfig
@@ -64,6 +64,17 @@ menuconfig DMABUF_HEAPS
allows userspace to allocate dma-bufs that can be shared
between drivers.

+menuconfig DMABUF_SYSFS_STATS
+ bool "DMA-BUF sysfs statistics"
+ select DMA_SHARED_BUFFER
+ help
+ Choose this option to enable DMA-BUF sysfs statistics
+ in location /sys/kernel/dmabuf/buffers.
+
+ /sys/kernel/dmabuf/buffers/<inode_number> will contain
+ statistics for the DMA-BUF with the unique inode number
+ <inode_number>.
+
source "drivers/dma-buf/heaps/Kconfig"

endmenu
diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
index 995e05f609ff..40d81f23cacf 100644
--- a/drivers/dma-buf/Makefile
+++ b/drivers/dma-buf/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_DMABUF_HEAPS) += heaps/
obj-$(CONFIG_SYNC_FILE) += sync_file.o
obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
obj-$(CONFIG_UDMABUF) += udmabuf.o
+obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o

dmabuf_selftests-y := \
selftest.o \
diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.c b/drivers/dma-buf/dma-buf-sysfs-stats.c
new file mode 100644
index 000000000000..61f85c3d16a5
--- /dev/null
+++ b/drivers/dma-buf/dma-buf-sysfs-stats.c
@@ -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;
+}
diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.h b/drivers/dma-buf/dma-buf-sysfs-stats.h
new file mode 100644
index 000000000000..5f4703249117
--- /dev/null
+++ b/drivers/dma-buf/dma-buf-sysfs-stats.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * DMA-BUF sysfs statistics.
+ *
+ * Copyright (C) 2021 Google LLC.
+ */
+
+#ifndef _DMA_BUF_SYSFS_STATS_H
+#define _DMA_BUF_SYSFS_STATS_H
+
+#ifdef CONFIG_DMABUF_SYSFS_STATS
+
+int dma_buf_init_sysfs_statistics(void);
+void dma_buf_uninit_sysfs_statistics(void);
+
+int dma_buf_stats_setup(struct dma_buf *dmabuf);
+int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
+ unsigned int uid);
+static inline void dma_buf_update_attachment_map_count(struct dma_buf_attachment *attach,
+ int delta)
+{
+ struct dma_buf_attach_sysfs_entry *entry = attach->sysfs_entry;
+
+ entry->map_counter += delta;
+}
+void dma_buf_stats_teardown(struct dma_buf *dmabuf);
+void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach);
+static inline unsigned int dma_buf_update_attach_uid(struct dma_buf *dmabuf)
+{
+ struct dma_buf_sysfs_entry *entry = dmabuf->sysfs_entry;
+
+ return entry->attachment_uid++;
+}
+#else
+
+static inline int dma_buf_init_sysfs_statistics(void)
+{
+ return 0;
+}
+
+static inline void dma_buf_uninit_sysfs_statistics(void) {}
+
+static inline int dma_buf_stats_setup(struct dma_buf *dmabuf)
+{
+ return 0;
+}
+static inline int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
+ unsigned int uid)
+{
+ return 0;
+}
+
+static inline void dma_buf_stats_teardown(struct dma_buf *dmabuf) {}
+static inline void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach) {}
+static inline void dma_buf_update_attachment_map_count(struct dma_buf_attachment *attach,
+ int delta) {}
+static inline unsigned int dma_buf_update_attach_uid(struct dma_buf *dmabuf)
+{
+ return 0;
+}
+#endif
+#endif // _DMA_BUF_SYSFS_STATS_H
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 9ad6397aaa97..29f9ea18eb47 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -29,6 +29,8 @@
#include <uapi/linux/dma-buf.h>
#include <uapi/linux/magic.h>

+#include "dma-buf-sysfs-stats.h"
+
static inline int is_dma_buf_file(struct file *);

struct dma_buf_list {
@@ -79,6 +81,7 @@ static void dma_buf_release(struct dentry *dentry)
if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
dma_resv_fini(dmabuf->resv);

+ dma_buf_stats_teardown(dmabuf);
module_put(dmabuf->owner);
kfree(dmabuf->name);
kfree(dmabuf);
@@ -579,6 +582,10 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
file->f_mode |= FMODE_LSEEK;
dmabuf->file = file;

+ ret = dma_buf_stats_setup(dmabuf);
+ if (ret)
+ goto err_sysfs;
+
mutex_init(&dmabuf->lock);
INIT_LIST_HEAD(&dmabuf->attachments);

@@ -588,6 +595,14 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)

return dmabuf;

+err_sysfs:
+ /*
+ * Set file->f_path.dentry->d_fsdata to NULL so that when
+ * dma_buf_release() gets invoked by dentry_ops, it exits
+ * early before calling the release() dma_buf op.
+ */
+ file->f_path.dentry->d_fsdata = NULL;
+ fput(file);
err_dmabuf:
kfree(dmabuf);
err_module:
@@ -692,6 +707,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
{
struct dma_buf_attachment *attach;
int ret;
+ unsigned int attach_uid;

if (WARN_ON(!dmabuf || !dev))
return ERR_PTR(-EINVAL);
@@ -717,8 +733,13 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
}
dma_resv_lock(dmabuf->resv, NULL);
list_add(&attach->node, &dmabuf->attachments);
+ attach_uid = dma_buf_update_attach_uid(dmabuf);
dma_resv_unlock(dmabuf->resv);

+ ret = dma_buf_attach_stats_setup(attach, attach_uid);
+ if (ret)
+ goto err_sysfs;
+
/* When either the importer or the exporter can't handle dynamic
* mappings we cache the mapping here to avoid issues with the
* reservation object lock.
@@ -745,6 +766,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
dma_resv_unlock(attach->dmabuf->resv);
attach->sgt = sgt;
attach->dir = DMA_BIDIRECTIONAL;
+ dma_buf_update_attachment_map_count(attach, 1 /* delta */);
}

return attach;
@@ -761,6 +783,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
if (dma_buf_is_dynamic(attach->dmabuf))
dma_resv_unlock(attach->dmabuf->resv);

+err_sysfs:
dma_buf_detach(dmabuf, attach);
return ERR_PTR(ret);
}
@@ -799,6 +822,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
dma_resv_lock(attach->dmabuf->resv, NULL);

dmabuf->ops->unmap_dma_buf(attach, attach->sgt, attach->dir);
+ dma_buf_update_attachment_map_count(attach, -1 /* delta */);

if (dma_buf_is_dynamic(attach->dmabuf)) {
dma_buf_unpin(attach);
@@ -812,6 +836,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
if (dmabuf->ops->detach)
dmabuf->ops->detach(dmabuf, attach);

+ dma_buf_attach_stats_teardown(attach);
kfree(attach);
}
EXPORT_SYMBOL_GPL(dma_buf_detach);
@@ -938,6 +963,9 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
}
#endif /* CONFIG_DMA_API_DEBUG */

+ if (!IS_ERR(sg_table))
+ dma_buf_update_attachment_map_count(attach, 1 /* delta */);
+
return sg_table;
}
EXPORT_SYMBOL_GPL(dma_buf_map_attachment);
@@ -975,6 +1003,8 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
if (dma_buf_is_dynamic(attach->dmabuf) &&
!IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY))
dma_buf_unpin(attach);
+
+ dma_buf_update_attachment_map_count(attach, -1 /* delta */);
}
EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);

@@ -1412,6 +1442,12 @@ static inline void dma_buf_uninit_debugfs(void)

static int __init dma_buf_init(void)
{
+ int ret;
+
+ ret = dma_buf_init_sysfs_statistics();
+ if (ret)
+ return ret;
+
dma_buf_mnt = kern_mount(&dma_buf_fs_type);
if (IS_ERR(dma_buf_mnt))
return PTR_ERR(dma_buf_mnt);
@@ -1427,5 +1463,6 @@ static void __exit dma_buf_deinit(void)
{
dma_buf_uninit_debugfs();
kern_unmount(dma_buf_mnt);
+ dma_buf_uninit_sysfs_statistics();
}
__exitcall(dma_buf_deinit);
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index cf72699cb2bc..4ae5cc38a4a7 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -294,6 +294,9 @@ struct dma_buf_ops {
* @poll: for userspace poll support
* @cb_excl: for userspace poll support
* @cb_shared: for userspace poll support
+ * @sysfs_entry: for exposing information about this buffer in sysfs.
+ * The attachment_uid member of @sysfs_entry is protected by dma_resv lock
+ * and is incremented on each attach.
*
* This represents a shared buffer, created by calling dma_buf_export(). The
* userspace representation is a normal file descriptor, which can be created by
@@ -329,6 +332,15 @@ struct dma_buf {

__poll_t active;
} cb_excl, cb_shared;
+#ifdef CONFIG_DMABUF_SYSFS_STATS
+ /* for sysfs stats */
+ struct dma_buf_sysfs_entry {
+ struct kobject kobj;
+ struct dma_buf *dmabuf;
+ unsigned int attachment_uid;
+ struct kset *attach_stats_kset;
+ } *sysfs_entry;
+#endif
};

/**
@@ -378,6 +390,7 @@ struct dma_buf_attach_ops {
* @importer_ops: importer operations for this attachment, if provided
* dma_buf_map/unmap_attachment() must be called with the dma_resv lock held.
* @importer_priv: importer specific attachment data.
+ * @sysfs_entry: For exposing information about this attachment in sysfs.
*
* This structure holds the attachment information between the dma_buf buffer
* and its user device(s). The list contains one attachment struct per device
@@ -398,6 +411,13 @@ struct dma_buf_attachment {
const struct dma_buf_attach_ops *importer_ops;
void *importer_priv;
void *priv;
+#ifdef CONFIG_DMABUF_SYSFS_STATS
+ /* for sysfs stats */
+ struct dma_buf_attach_sysfs_entry {
+ struct kobject kobj;
+ unsigned int map_counter;
+ } *sysfs_entry;
+#endif
};

/**
--
2.30.0.284.gd98b1dd5eaa7-goog


2021-01-20 13:13:34

by Christian König

[permalink] [raw]
Subject: Re: [Linaro-mm-sig] [PATCH v2] dmabuf: Add the capability to expose DMA-BUF stats in sysfs

Am 19.01.21 um 23:57 schrieb Hridya Valsaraju:
> 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 <[email protected]>
> ---
> Changes in v2:
> -Move statistics to /sys/kernel/dmabuf/buffers in oder to allow addition
> of other DMA-BUF-related sysfs stats in future. Based on feedback from
> Daniel Vetter.
> -Each attachment has its own directory to represent attaching devices as
> symlinks and to introduce map_count as a separate file. Based on
> feedback from Daniel Vetter and Christian König. Thank you both!
> -Commit messages updated to point to userspace code in AOSP that will
> read the DMA-BUF sysfs stats.
>
> .../ABI/testing/sysfs-kernel-dmabuf-buffers | 52 ++++
> drivers/dma-buf/Kconfig | 11 +
> drivers/dma-buf/Makefile | 1 +
> drivers/dma-buf/dma-buf-sysfs-stats.c | 283 ++++++++++++++++++
> drivers/dma-buf/dma-buf-sysfs-stats.h | 62 ++++
> drivers/dma-buf/dma-buf.c | 37 +++
> include/linux/dma-buf.h | 20 ++
> 7 files changed, 466 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> create mode 100644 drivers/dma-buf/dma-buf-sysfs-stats.c
> create mode 100644 drivers/dma-buf/dma-buf-sysfs-stats.h
>
> diff --git a/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers b/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> new file mode 100644
> index 000000000000..6f7c65209f07
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> @@ -0,0 +1,52 @@
> +What: /sys/kernel/dmabuf/buffers
> +Date: January 2021
> +KernelVersion: v5.12
> +Contact: Hridya Valsaraju <[email protected]>
> +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 <[email protected]>
> +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 <[email protected]>
> +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 <[email protected]>
> +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 <[email protected]>
> +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 <[email protected]>
> +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 <[email protected]>
> +Description: This file is read-only and contains a map_counter indicating the
> + number of distinct device mappings of the attachment.
> diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig
> index 4f8224a6ac95..27e6a2dafeaa 100644
> --- a/drivers/dma-buf/Kconfig
> +++ b/drivers/dma-buf/Kconfig
> @@ -64,6 +64,17 @@ menuconfig DMABUF_HEAPS
> allows userspace to allocate dma-bufs that can be shared
> between drivers.
>
> +menuconfig DMABUF_SYSFS_STATS
> + bool "DMA-BUF sysfs statistics"
> + select DMA_SHARED_BUFFER
> + help
> + Choose this option to enable DMA-BUF sysfs statistics
> + in location /sys/kernel/dmabuf/buffers.
> +
> + /sys/kernel/dmabuf/buffers/<inode_number> will contain
> + statistics for the DMA-BUF with the unique inode number
> + <inode_number>.
> +
> source "drivers/dma-buf/heaps/Kconfig"
>
> endmenu
> diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
> index 995e05f609ff..40d81f23cacf 100644
> --- a/drivers/dma-buf/Makefile
> +++ b/drivers/dma-buf/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_DMABUF_HEAPS) += heaps/
> obj-$(CONFIG_SYNC_FILE) += sync_file.o
> obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
> obj-$(CONFIG_UDMABUF) += udmabuf.o
> +obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o
>
> dmabuf_selftests-y := \
> selftest.o \
> diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.c b/drivers/dma-buf/dma-buf-sysfs-stats.c
> new file mode 100644
> index 000000000000..61f85c3d16a5
> --- /dev/null
> +++ b/drivers/dma-buf/dma-buf-sysfs-stats.c
> @@ -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;
> +}
> diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.h b/drivers/dma-buf/dma-buf-sysfs-stats.h
> new file mode 100644
> index 000000000000..5f4703249117
> --- /dev/null
> +++ b/drivers/dma-buf/dma-buf-sysfs-stats.h
> @@ -0,0 +1,62 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * DMA-BUF sysfs statistics.
> + *
> + * Copyright (C) 2021 Google LLC.
> + */
> +
> +#ifndef _DMA_BUF_SYSFS_STATS_H
> +#define _DMA_BUF_SYSFS_STATS_H
> +
> +#ifdef CONFIG_DMABUF_SYSFS_STATS
> +
> +int dma_buf_init_sysfs_statistics(void);
> +void dma_buf_uninit_sysfs_statistics(void);
> +
> +int dma_buf_stats_setup(struct dma_buf *dmabuf);
> +int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
> + unsigned int uid);
> +static inline void dma_buf_update_attachment_map_count(struct dma_buf_attachment *attach,
> + int delta)
> +{
> + struct dma_buf_attach_sysfs_entry *entry = attach->sysfs_entry;
> +
> + entry->map_counter += delta;
> +}
> +void dma_buf_stats_teardown(struct dma_buf *dmabuf);
> +void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach);
> +static inline unsigned int dma_buf_update_attach_uid(struct dma_buf *dmabuf)
> +{
> + struct dma_buf_sysfs_entry *entry = dmabuf->sysfs_entry;
> +
> + return entry->attachment_uid++;
> +}
> +#else
> +
> +static inline int dma_buf_init_sysfs_statistics(void)
> +{
> + return 0;
> +}
> +
> +static inline void dma_buf_uninit_sysfs_statistics(void) {}
> +
> +static inline int dma_buf_stats_setup(struct dma_buf *dmabuf)
> +{
> + return 0;
> +}
> +static inline int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
> + unsigned int uid)
> +{
> + return 0;
> +}
> +
> +static inline void dma_buf_stats_teardown(struct dma_buf *dmabuf) {}
> +static inline void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach) {}
> +static inline void dma_buf_update_attachment_map_count(struct dma_buf_attachment *attach,
> + int delta) {}
> +static inline unsigned int dma_buf_update_attach_uid(struct dma_buf *dmabuf)
> +{
> + return 0;
> +}
> +#endif
> +#endif // _DMA_BUF_SYSFS_STATS_H
> diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
> index 9ad6397aaa97..29f9ea18eb47 100644
> --- a/drivers/dma-buf/dma-buf.c
> +++ b/drivers/dma-buf/dma-buf.c
> @@ -29,6 +29,8 @@
> #include <uapi/linux/dma-buf.h>
> #include <uapi/linux/magic.h>
>
> +#include "dma-buf-sysfs-stats.h"
> +
> static inline int is_dma_buf_file(struct file *);
>
> struct dma_buf_list {
> @@ -79,6 +81,7 @@ static void dma_buf_release(struct dentry *dentry)
> if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
> dma_resv_fini(dmabuf->resv);
>
> + dma_buf_stats_teardown(dmabuf);
> module_put(dmabuf->owner);
> kfree(dmabuf->name);
> kfree(dmabuf);
> @@ -579,6 +582,10 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
> file->f_mode |= FMODE_LSEEK;
> dmabuf->file = file;
>
> + ret = dma_buf_stats_setup(dmabuf);
> + if (ret)
> + goto err_sysfs;
> +
> mutex_init(&dmabuf->lock);
> INIT_LIST_HEAD(&dmabuf->attachments);
>
> @@ -588,6 +595,14 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
>
> return dmabuf;
>
> +err_sysfs:
> + /*
> + * Set file->f_path.dentry->d_fsdata to NULL so that when
> + * dma_buf_release() gets invoked by dentry_ops, it exits
> + * early before calling the release() dma_buf op.
> + */
> + file->f_path.dentry->d_fsdata = NULL;
> + fput(file);
> err_dmabuf:
> kfree(dmabuf);
> err_module:
> @@ -692,6 +707,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> {
> struct dma_buf_attachment *attach;
> int ret;
> + unsigned int attach_uid;
>
> if (WARN_ON(!dmabuf || !dev))
> return ERR_PTR(-EINVAL);
> @@ -717,8 +733,13 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> }
> dma_resv_lock(dmabuf->resv, NULL);
> list_add(&attach->node, &dmabuf->attachments);
> + attach_uid = dma_buf_update_attach_uid(dmabuf);
> dma_resv_unlock(dmabuf->resv);
>
> + ret = dma_buf_attach_stats_setup(attach, attach_uid);
> + if (ret)
> + goto err_sysfs;
> +
> /* When either the importer or the exporter can't handle dynamic
> * mappings we cache the mapping here to avoid issues with the
> * reservation object lock.
> @@ -745,6 +766,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> dma_resv_unlock(attach->dmabuf->resv);
> attach->sgt = sgt;
> attach->dir = DMA_BIDIRECTIONAL;
> + dma_buf_update_attachment_map_count(attach, 1 /* delta */);
> }
>
> return attach;
> @@ -761,6 +783,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> if (dma_buf_is_dynamic(attach->dmabuf))
> dma_resv_unlock(attach->dmabuf->resv);
>
> +err_sysfs:
> dma_buf_detach(dmabuf, attach);
> return ERR_PTR(ret);
> }
> @@ -799,6 +822,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
> dma_resv_lock(attach->dmabuf->resv, NULL);
>
> dmabuf->ops->unmap_dma_buf(attach, attach->sgt, attach->dir);
> + dma_buf_update_attachment_map_count(attach, -1 /* delta */);
>
> if (dma_buf_is_dynamic(attach->dmabuf)) {
> dma_buf_unpin(attach);
> @@ -812,6 +836,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
> if (dmabuf->ops->detach)
> dmabuf->ops->detach(dmabuf, attach);
>
> + dma_buf_attach_stats_teardown(attach);
> kfree(attach);
> }
> EXPORT_SYMBOL_GPL(dma_buf_detach);
> @@ -938,6 +963,9 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
> }
> #endif /* CONFIG_DMA_API_DEBUG */
>
> + if (!IS_ERR(sg_table))
> + dma_buf_update_attachment_map_count(attach, 1 /* delta */);
> +
> return sg_table;
> }
> EXPORT_SYMBOL_GPL(dma_buf_map_attachment);
> @@ -975,6 +1003,8 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
> if (dma_buf_is_dynamic(attach->dmabuf) &&
> !IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY))
> dma_buf_unpin(attach);
> +
> + dma_buf_update_attachment_map_count(attach, -1 /* delta */);
> }
> EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
>
> @@ -1412,6 +1442,12 @@ static inline void dma_buf_uninit_debugfs(void)
>
> static int __init dma_buf_init(void)
> {
> + int ret;
> +
> + ret = dma_buf_init_sysfs_statistics();
> + if (ret)
> + return ret;
> +
> dma_buf_mnt = kern_mount(&dma_buf_fs_type);
> if (IS_ERR(dma_buf_mnt))
> return PTR_ERR(dma_buf_mnt);
> @@ -1427,5 +1463,6 @@ static void __exit dma_buf_deinit(void)
> {
> dma_buf_uninit_debugfs();
> kern_unmount(dma_buf_mnt);
> + dma_buf_uninit_sysfs_statistics();
> }
> __exitcall(dma_buf_deinit);
> diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
> index cf72699cb2bc..4ae5cc38a4a7 100644
> --- a/include/linux/dma-buf.h
> +++ b/include/linux/dma-buf.h
> @@ -294,6 +294,9 @@ struct dma_buf_ops {
> * @poll: for userspace poll support
> * @cb_excl: for userspace poll support
> * @cb_shared: for userspace poll support
> + * @sysfs_entry: for exposing information about this buffer in sysfs.
> + * The attachment_uid member of @sysfs_entry is protected by dma_resv lock
> + * and is incremented on each attach.
> *
> * This represents a shared buffer, created by calling dma_buf_export(). The
> * userspace representation is a normal file descriptor, which can be created by
> @@ -329,6 +332,15 @@ struct dma_buf {
>
> __poll_t active;
> } cb_excl, cb_shared;
> +#ifdef CONFIG_DMABUF_SYSFS_STATS
> + /* for sysfs stats */
> + struct dma_buf_sysfs_entry {
> + struct kobject kobj;
> + struct dma_buf *dmabuf;
> + unsigned int attachment_uid;
> + struct kset *attach_stats_kset;
> + } *sysfs_entry;
> +#endif

Why not directly embed that?

> };
>
> /**
> @@ -378,6 +390,7 @@ struct dma_buf_attach_ops {
> * @importer_ops: importer operations for this attachment, if provided
> * dma_buf_map/unmap_attachment() must be called with the dma_resv lock held.
> * @importer_priv: importer specific attachment data.
> + * @sysfs_entry: For exposing information about this attachment in sysfs.
> *
> * This structure holds the attachment information between the dma_buf buffer
> * and its user device(s). The list contains one attachment struct per device
> @@ -398,6 +411,13 @@ struct dma_buf_attachment {
> const struct dma_buf_attach_ops *importer_ops;
> void *importer_priv;
> void *priv;
> +#ifdef CONFIG_DMABUF_SYSFS_STATS
> + /* for sysfs stats */
> + struct dma_buf_attach_sysfs_entry {
> + struct kobject kobj;
> + unsigned int map_counter;
> + } *sysfs_entry;
> +#endif

Same question here.

Apart from that the general approach looks solid to me now.

But somebody with more sysfs background should check if everything there
is the right thing to do.

Regards,
Christian.



> };
>
> /**

2021-01-20 13:18:49

by Daniel Vetter

[permalink] [raw]
Subject: Re: [Linaro-mm-sig] [PATCH v2] dmabuf: Add the capability to expose DMA-BUF stats in sysfs

On Wed, Jan 20, 2021 at 1:22 PM Christian König
<[email protected]> wrote:
>
> Am 19.01.21 um 23:57 schrieb Hridya Valsaraju:
> > 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 <[email protected]>
> > ---
> > Changes in v2:
> > -Move statistics to /sys/kernel/dmabuf/buffers in oder to allow addition
> > of other DMA-BUF-related sysfs stats in future. Based on feedback from
> > Daniel Vetter.
> > -Each attachment has its own directory to represent attaching devices as
> > symlinks and to introduce map_count as a separate file. Based on
> > feedback from Daniel Vetter and Christian König. Thank you both!
> > -Commit messages updated to point to userspace code in AOSP that will
> > read the DMA-BUF sysfs stats.
> >
> > .../ABI/testing/sysfs-kernel-dmabuf-buffers | 52 ++++
> > drivers/dma-buf/Kconfig | 11 +
> > drivers/dma-buf/Makefile | 1 +
> > drivers/dma-buf/dma-buf-sysfs-stats.c | 283 ++++++++++++++++++
> > drivers/dma-buf/dma-buf-sysfs-stats.h | 62 ++++
> > drivers/dma-buf/dma-buf.c | 37 +++
> > include/linux/dma-buf.h | 20 ++
> > 7 files changed, 466 insertions(+)
> > create mode 100644 Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> > create mode 100644 drivers/dma-buf/dma-buf-sysfs-stats.c
> > create mode 100644 drivers/dma-buf/dma-buf-sysfs-stats.h
> >
> > diff --git a/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers b/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> > new file mode 100644
> > index 000000000000..6f7c65209f07
> > --- /dev/null
> > +++ b/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> > @@ -0,0 +1,52 @@
> > +What: /sys/kernel/dmabuf/buffers
> > +Date: January 2021
> > +KernelVersion: v5.12
> > +Contact: Hridya Valsaraju <[email protected]>
> > +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 <[email protected]>
> > +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 <[email protected]>
> > +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 <[email protected]>
> > +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 <[email protected]>
> > +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 <[email protected]>
> > +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 <[email protected]>
> > +Description: This file is read-only and contains a map_counter indicating the
> > + number of distinct device mappings of the attachment.
> > diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig
> > index 4f8224a6ac95..27e6a2dafeaa 100644
> > --- a/drivers/dma-buf/Kconfig
> > +++ b/drivers/dma-buf/Kconfig
> > @@ -64,6 +64,17 @@ menuconfig DMABUF_HEAPS
> > allows userspace to allocate dma-bufs that can be shared
> > between drivers.
> >
> > +menuconfig DMABUF_SYSFS_STATS
> > + bool "DMA-BUF sysfs statistics"
> > + select DMA_SHARED_BUFFER
> > + help
> > + Choose this option to enable DMA-BUF sysfs statistics
> > + in location /sys/kernel/dmabuf/buffers.
> > +
> > + /sys/kernel/dmabuf/buffers/<inode_number> will contain
> > + statistics for the DMA-BUF with the unique inode number
> > + <inode_number>.
> > +
> > source "drivers/dma-buf/heaps/Kconfig"
> >
> > endmenu
> > diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
> > index 995e05f609ff..40d81f23cacf 100644
> > --- a/drivers/dma-buf/Makefile
> > +++ b/drivers/dma-buf/Makefile
> > @@ -6,6 +6,7 @@ obj-$(CONFIG_DMABUF_HEAPS) += heaps/
> > obj-$(CONFIG_SYNC_FILE) += sync_file.o
> > obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
> > obj-$(CONFIG_UDMABUF) += udmabuf.o
> > +obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o
> >
> > dmabuf_selftests-y := \
> > selftest.o \
> > diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.c b/drivers/dma-buf/dma-buf-sysfs-stats.c
> > new file mode 100644
> > index 000000000000..61f85c3d16a5
> > --- /dev/null
> > +++ b/drivers/dma-buf/dma-buf-sysfs-stats.c
> > @@ -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;
> > +}
> > diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.h b/drivers/dma-buf/dma-buf-sysfs-stats.h
> > new file mode 100644
> > index 000000000000..5f4703249117
> > --- /dev/null
> > +++ b/drivers/dma-buf/dma-buf-sysfs-stats.h
> > @@ -0,0 +1,62 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * DMA-BUF sysfs statistics.
> > + *
> > + * Copyright (C) 2021 Google LLC.
> > + */
> > +
> > +#ifndef _DMA_BUF_SYSFS_STATS_H
> > +#define _DMA_BUF_SYSFS_STATS_H
> > +
> > +#ifdef CONFIG_DMABUF_SYSFS_STATS
> > +
> > +int dma_buf_init_sysfs_statistics(void);
> > +void dma_buf_uninit_sysfs_statistics(void);
> > +
> > +int dma_buf_stats_setup(struct dma_buf *dmabuf);
> > +int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
> > + unsigned int uid);
> > +static inline void dma_buf_update_attachment_map_count(struct dma_buf_attachment *attach,
> > + int delta)
> > +{
> > + struct dma_buf_attach_sysfs_entry *entry = attach->sysfs_entry;
> > +
> > + entry->map_counter += delta;
> > +}
> > +void dma_buf_stats_teardown(struct dma_buf *dmabuf);
> > +void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach);
> > +static inline unsigned int dma_buf_update_attach_uid(struct dma_buf *dmabuf)
> > +{
> > + struct dma_buf_sysfs_entry *entry = dmabuf->sysfs_entry;
> > +
> > + return entry->attachment_uid++;
> > +}
> > +#else
> > +
> > +static inline int dma_buf_init_sysfs_statistics(void)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline void dma_buf_uninit_sysfs_statistics(void) {}
> > +
> > +static inline int dma_buf_stats_setup(struct dma_buf *dmabuf)
> > +{
> > + return 0;
> > +}
> > +static inline int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
> > + unsigned int uid)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline void dma_buf_stats_teardown(struct dma_buf *dmabuf) {}
> > +static inline void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach) {}
> > +static inline void dma_buf_update_attachment_map_count(struct dma_buf_attachment *attach,
> > + int delta) {}
> > +static inline unsigned int dma_buf_update_attach_uid(struct dma_buf *dmabuf)
> > +{
> > + return 0;
> > +}
> > +#endif
> > +#endif // _DMA_BUF_SYSFS_STATS_H
> > diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
> > index 9ad6397aaa97..29f9ea18eb47 100644
> > --- a/drivers/dma-buf/dma-buf.c
> > +++ b/drivers/dma-buf/dma-buf.c
> > @@ -29,6 +29,8 @@
> > #include <uapi/linux/dma-buf.h>
> > #include <uapi/linux/magic.h>
> >
> > +#include "dma-buf-sysfs-stats.h"
> > +
> > static inline int is_dma_buf_file(struct file *);
> >
> > struct dma_buf_list {
> > @@ -79,6 +81,7 @@ static void dma_buf_release(struct dentry *dentry)
> > if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
> > dma_resv_fini(dmabuf->resv);
> >
> > + dma_buf_stats_teardown(dmabuf);
> > module_put(dmabuf->owner);
> > kfree(dmabuf->name);
> > kfree(dmabuf);
> > @@ -579,6 +582,10 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
> > file->f_mode |= FMODE_LSEEK;
> > dmabuf->file = file;
> >
> > + ret = dma_buf_stats_setup(dmabuf);
> > + if (ret)
> > + goto err_sysfs;
> > +
> > mutex_init(&dmabuf->lock);
> > INIT_LIST_HEAD(&dmabuf->attachments);
> >
> > @@ -588,6 +595,14 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
> >
> > return dmabuf;
> >
> > +err_sysfs:
> > + /*
> > + * Set file->f_path.dentry->d_fsdata to NULL so that when
> > + * dma_buf_release() gets invoked by dentry_ops, it exits
> > + * early before calling the release() dma_buf op.
> > + */
> > + file->f_path.dentry->d_fsdata = NULL;
> > + fput(file);
> > err_dmabuf:
> > kfree(dmabuf);
> > err_module:
> > @@ -692,6 +707,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > {
> > struct dma_buf_attachment *attach;
> > int ret;
> > + unsigned int attach_uid;
> >
> > if (WARN_ON(!dmabuf || !dev))
> > return ERR_PTR(-EINVAL);
> > @@ -717,8 +733,13 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > }
> > dma_resv_lock(dmabuf->resv, NULL);
> > list_add(&attach->node, &dmabuf->attachments);
> > + attach_uid = dma_buf_update_attach_uid(dmabuf);
> > dma_resv_unlock(dmabuf->resv);
> >
> > + ret = dma_buf_attach_stats_setup(attach, attach_uid);
> > + if (ret)
> > + goto err_sysfs;
> > +
> > /* When either the importer or the exporter can't handle dynamic
> > * mappings we cache the mapping here to avoid issues with the
> > * reservation object lock.
> > @@ -745,6 +766,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > dma_resv_unlock(attach->dmabuf->resv);
> > attach->sgt = sgt;
> > attach->dir = DMA_BIDIRECTIONAL;
> > + dma_buf_update_attachment_map_count(attach, 1 /* delta */);
> > }
> >
> > return attach;
> > @@ -761,6 +783,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > if (dma_buf_is_dynamic(attach->dmabuf))
> > dma_resv_unlock(attach->dmabuf->resv);
> >
> > +err_sysfs:
> > dma_buf_detach(dmabuf, attach);
> > return ERR_PTR(ret);
> > }
> > @@ -799,6 +822,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
> > dma_resv_lock(attach->dmabuf->resv, NULL);
> >
> > dmabuf->ops->unmap_dma_buf(attach, attach->sgt, attach->dir);
> > + dma_buf_update_attachment_map_count(attach, -1 /* delta */);
> >
> > if (dma_buf_is_dynamic(attach->dmabuf)) {
> > dma_buf_unpin(attach);
> > @@ -812,6 +836,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
> > if (dmabuf->ops->detach)
> > dmabuf->ops->detach(dmabuf, attach);
> >
> > + dma_buf_attach_stats_teardown(attach);
> > kfree(attach);
> > }
> > EXPORT_SYMBOL_GPL(dma_buf_detach);
> > @@ -938,6 +963,9 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
> > }
> > #endif /* CONFIG_DMA_API_DEBUG */
> >
> > + if (!IS_ERR(sg_table))
> > + dma_buf_update_attachment_map_count(attach, 1 /* delta */);
> > +
> > return sg_table;
> > }
> > EXPORT_SYMBOL_GPL(dma_buf_map_attachment);
> > @@ -975,6 +1003,8 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
> > if (dma_buf_is_dynamic(attach->dmabuf) &&
> > !IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY))
> > dma_buf_unpin(attach);
> > +
> > + dma_buf_update_attachment_map_count(attach, -1 /* delta */);
> > }
> > EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
> >
> > @@ -1412,6 +1442,12 @@ static inline void dma_buf_uninit_debugfs(void)
> >
> > static int __init dma_buf_init(void)
> > {
> > + int ret;
> > +
> > + ret = dma_buf_init_sysfs_statistics();
> > + if (ret)
> > + return ret;
> > +
> > dma_buf_mnt = kern_mount(&dma_buf_fs_type);
> > if (IS_ERR(dma_buf_mnt))
> > return PTR_ERR(dma_buf_mnt);
> > @@ -1427,5 +1463,6 @@ static void __exit dma_buf_deinit(void)
> > {
> > dma_buf_uninit_debugfs();
> > kern_unmount(dma_buf_mnt);
> > + dma_buf_uninit_sysfs_statistics();
> > }
> > __exitcall(dma_buf_deinit);
> > diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
> > index cf72699cb2bc..4ae5cc38a4a7 100644
> > --- a/include/linux/dma-buf.h
> > +++ b/include/linux/dma-buf.h
> > @@ -294,6 +294,9 @@ struct dma_buf_ops {
> > * @poll: for userspace poll support
> > * @cb_excl: for userspace poll support
> > * @cb_shared: for userspace poll support
> > + * @sysfs_entry: for exposing information about this buffer in sysfs.
> > + * The attachment_uid member of @sysfs_entry is protected by dma_resv lock
> > + * and is incremented on each attach.
> > *
> > * This represents a shared buffer, created by calling dma_buf_export(). The
> > * userspace representation is a normal file descriptor, which can be created by
> > @@ -329,6 +332,15 @@ struct dma_buf {
> >
> > __poll_t active;
> > } cb_excl, cb_shared;
> > +#ifdef CONFIG_DMABUF_SYSFS_STATS
> > + /* for sysfs stats */
> > + struct dma_buf_sysfs_entry {
> > + struct kobject kobj;
> > + struct dma_buf *dmabuf;
> > + unsigned int attachment_uid;
> > + struct kset *attach_stats_kset;
> > + } *sysfs_entry;
> > +#endif
>
> Why not directly embed that?
>
> > };
> >
> > /**
> > @@ -378,6 +390,7 @@ struct dma_buf_attach_ops {
> > * @importer_ops: importer operations for this attachment, if provided
> > * dma_buf_map/unmap_attachment() must be called with the dma_resv lock held.
> > * @importer_priv: importer specific attachment data.
> > + * @sysfs_entry: For exposing information about this attachment in sysfs.
> > *
> > * This structure holds the attachment information between the dma_buf buffer
> > * and its user device(s). The list contains one attachment struct per device
> > @@ -398,6 +411,13 @@ struct dma_buf_attachment {
> > const struct dma_buf_attach_ops *importer_ops;
> > void *importer_priv;
> > void *priv;
> > +#ifdef CONFIG_DMABUF_SYSFS_STATS
> > + /* for sysfs stats */
> > + struct dma_buf_attach_sysfs_entry {
> > + struct kobject kobj;
> > + unsigned int map_counter;
> > + } *sysfs_entry;
> > +#endif
>
> Same question here.
>
> Apart from that the general approach looks solid to me now.
>
> But somebody with more sysfs background should check if everything there
> is the right thing to do.

So apparently Android is completely revamping all of this, but feeding
it to upstream in pieces, and that doesn't work. Here's another part:

https://lore.kernel.org/dri-devel/CAKMK7uE3xF80AsJ1zGfSM-KTry=ikJ-S-Dn6nK8ZAvCSWw2FHQ@mail.gmail.com/

I think we need an overall integrated solution here, or we're just
building uapi that won't last (but we still have to support it
forever).

I think aside from having the full android side covered (but on
upstream drivers, and including dma-buf sharing, not just only
virtio-gpu) we also need to figure out how this will fit into other
tracking efforts like cgroups. Otherwise we end up with one way of
tracking gpu memory usage for android, implemented by out-of-tree
drivers mostly. Another one for servers in cgroups, implemented by amd
and intel, and then probably a third one for cros
virtualization/containers, just because. That doesn't work too well.
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

2021-01-20 19:30:08

by Hridya Valsaraju

[permalink] [raw]
Subject: Re: [Linaro-mm-sig] [PATCH v2] dmabuf: Add the capability to expose DMA-BUF stats in sysfs

On Wed, Jan 20, 2021 at 4:22 AM Christian König
<[email protected]> wrote:
>
> Am 19.01.21 um 23:57 schrieb Hridya Valsaraju:
> > 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 <[email protected]>
> > ---
> > Changes in v2:
> > -Move statistics to /sys/kernel/dmabuf/buffers in oder to allow addition
> > of other DMA-BUF-related sysfs stats in future. Based on feedback from
> > Daniel Vetter.
> > -Each attachment has its own directory to represent attaching devices as
> > symlinks and to introduce map_count as a separate file. Based on
> > feedback from Daniel Vetter and Christian König. Thank you both!
> > -Commit messages updated to point to userspace code in AOSP that will
> > read the DMA-BUF sysfs stats.
> >
> > .../ABI/testing/sysfs-kernel-dmabuf-buffers | 52 ++++
> > drivers/dma-buf/Kconfig | 11 +
> > drivers/dma-buf/Makefile | 1 +
> > drivers/dma-buf/dma-buf-sysfs-stats.c | 283 ++++++++++++++++++
> > drivers/dma-buf/dma-buf-sysfs-stats.h | 62 ++++
> > drivers/dma-buf/dma-buf.c | 37 +++
> > include/linux/dma-buf.h | 20 ++
> > 7 files changed, 466 insertions(+)
> > create mode 100644 Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> > create mode 100644 drivers/dma-buf/dma-buf-sysfs-stats.c
> > create mode 100644 drivers/dma-buf/dma-buf-sysfs-stats.h
> >
> > diff --git a/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers b/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> > new file mode 100644
> > index 000000000000..6f7c65209f07
> > --- /dev/null
> > +++ b/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> > @@ -0,0 +1,52 @@
> > +What: /sys/kernel/dmabuf/buffers
> > +Date: January 2021
> > +KernelVersion: v5.12
> > +Contact: Hridya Valsaraju <[email protected]>
> > +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 <[email protected]>
> > +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 <[email protected]>
> > +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 <[email protected]>
> > +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 <[email protected]>
> > +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 <[email protected]>
> > +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 <[email protected]>
> > +Description: This file is read-only and contains a map_counter indicating the
> > + number of distinct device mappings of the attachment.
> > diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig
> > index 4f8224a6ac95..27e6a2dafeaa 100644
> > --- a/drivers/dma-buf/Kconfig
> > +++ b/drivers/dma-buf/Kconfig
> > @@ -64,6 +64,17 @@ menuconfig DMABUF_HEAPS
> > allows userspace to allocate dma-bufs that can be shared
> > between drivers.
> >
> > +menuconfig DMABUF_SYSFS_STATS
> > + bool "DMA-BUF sysfs statistics"
> > + select DMA_SHARED_BUFFER
> > + help
> > + Choose this option to enable DMA-BUF sysfs statistics
> > + in location /sys/kernel/dmabuf/buffers.
> > +
> > + /sys/kernel/dmabuf/buffers/<inode_number> will contain
> > + statistics for the DMA-BUF with the unique inode number
> > + <inode_number>.
> > +
> > source "drivers/dma-buf/heaps/Kconfig"
> >
> > endmenu
> > diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
> > index 995e05f609ff..40d81f23cacf 100644
> > --- a/drivers/dma-buf/Makefile
> > +++ b/drivers/dma-buf/Makefile
> > @@ -6,6 +6,7 @@ obj-$(CONFIG_DMABUF_HEAPS) += heaps/
> > obj-$(CONFIG_SYNC_FILE) += sync_file.o
> > obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
> > obj-$(CONFIG_UDMABUF) += udmabuf.o
> > +obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o
> >
> > dmabuf_selftests-y := \
> > selftest.o \
> > diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.c b/drivers/dma-buf/dma-buf-sysfs-stats.c
> > new file mode 100644
> > index 000000000000..61f85c3d16a5
> > --- /dev/null
> > +++ b/drivers/dma-buf/dma-buf-sysfs-stats.c
> > @@ -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;
> > +}
> > diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.h b/drivers/dma-buf/dma-buf-sysfs-stats.h
> > new file mode 100644
> > index 000000000000..5f4703249117
> > --- /dev/null
> > +++ b/drivers/dma-buf/dma-buf-sysfs-stats.h
> > @@ -0,0 +1,62 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * DMA-BUF sysfs statistics.
> > + *
> > + * Copyright (C) 2021 Google LLC.
> > + */
> > +
> > +#ifndef _DMA_BUF_SYSFS_STATS_H
> > +#define _DMA_BUF_SYSFS_STATS_H
> > +
> > +#ifdef CONFIG_DMABUF_SYSFS_STATS
> > +
> > +int dma_buf_init_sysfs_statistics(void);
> > +void dma_buf_uninit_sysfs_statistics(void);
> > +
> > +int dma_buf_stats_setup(struct dma_buf *dmabuf);
> > +int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
> > + unsigned int uid);
> > +static inline void dma_buf_update_attachment_map_count(struct dma_buf_attachment *attach,
> > + int delta)
> > +{
> > + struct dma_buf_attach_sysfs_entry *entry = attach->sysfs_entry;
> > +
> > + entry->map_counter += delta;
> > +}
> > +void dma_buf_stats_teardown(struct dma_buf *dmabuf);
> > +void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach);
> > +static inline unsigned int dma_buf_update_attach_uid(struct dma_buf *dmabuf)
> > +{
> > + struct dma_buf_sysfs_entry *entry = dmabuf->sysfs_entry;
> > +
> > + return entry->attachment_uid++;
> > +}
> > +#else
> > +
> > +static inline int dma_buf_init_sysfs_statistics(void)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline void dma_buf_uninit_sysfs_statistics(void) {}
> > +
> > +static inline int dma_buf_stats_setup(struct dma_buf *dmabuf)
> > +{
> > + return 0;
> > +}
> > +static inline int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
> > + unsigned int uid)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline void dma_buf_stats_teardown(struct dma_buf *dmabuf) {}
> > +static inline void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach) {}
> > +static inline void dma_buf_update_attachment_map_count(struct dma_buf_attachment *attach,
> > + int delta) {}
> > +static inline unsigned int dma_buf_update_attach_uid(struct dma_buf *dmabuf)
> > +{
> > + return 0;
> > +}
> > +#endif
> > +#endif // _DMA_BUF_SYSFS_STATS_H
> > diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
> > index 9ad6397aaa97..29f9ea18eb47 100644
> > --- a/drivers/dma-buf/dma-buf.c
> > +++ b/drivers/dma-buf/dma-buf.c
> > @@ -29,6 +29,8 @@
> > #include <uapi/linux/dma-buf.h>
> > #include <uapi/linux/magic.h>
> >
> > +#include "dma-buf-sysfs-stats.h"
> > +
> > static inline int is_dma_buf_file(struct file *);
> >
> > struct dma_buf_list {
> > @@ -79,6 +81,7 @@ static void dma_buf_release(struct dentry *dentry)
> > if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
> > dma_resv_fini(dmabuf->resv);
> >
> > + dma_buf_stats_teardown(dmabuf);
> > module_put(dmabuf->owner);
> > kfree(dmabuf->name);
> > kfree(dmabuf);
> > @@ -579,6 +582,10 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
> > file->f_mode |= FMODE_LSEEK;
> > dmabuf->file = file;
> >
> > + ret = dma_buf_stats_setup(dmabuf);
> > + if (ret)
> > + goto err_sysfs;
> > +
> > mutex_init(&dmabuf->lock);
> > INIT_LIST_HEAD(&dmabuf->attachments);
> >
> > @@ -588,6 +595,14 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
> >
> > return dmabuf;
> >
> > +err_sysfs:
> > + /*
> > + * Set file->f_path.dentry->d_fsdata to NULL so that when
> > + * dma_buf_release() gets invoked by dentry_ops, it exits
> > + * early before calling the release() dma_buf op.
> > + */
> > + file->f_path.dentry->d_fsdata = NULL;
> > + fput(file);
> > err_dmabuf:
> > kfree(dmabuf);
> > err_module:
> > @@ -692,6 +707,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > {
> > struct dma_buf_attachment *attach;
> > int ret;
> > + unsigned int attach_uid;
> >
> > if (WARN_ON(!dmabuf || !dev))
> > return ERR_PTR(-EINVAL);
> > @@ -717,8 +733,13 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > }
> > dma_resv_lock(dmabuf->resv, NULL);
> > list_add(&attach->node, &dmabuf->attachments);
> > + attach_uid = dma_buf_update_attach_uid(dmabuf);
> > dma_resv_unlock(dmabuf->resv);
> >
> > + ret = dma_buf_attach_stats_setup(attach, attach_uid);
> > + if (ret)
> > + goto err_sysfs;
> > +
> > /* When either the importer or the exporter can't handle dynamic
> > * mappings we cache the mapping here to avoid issues with the
> > * reservation object lock.
> > @@ -745,6 +766,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > dma_resv_unlock(attach->dmabuf->resv);
> > attach->sgt = sgt;
> > attach->dir = DMA_BIDIRECTIONAL;
> > + dma_buf_update_attachment_map_count(attach, 1 /* delta */);
> > }
> >
> > return attach;
> > @@ -761,6 +783,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > if (dma_buf_is_dynamic(attach->dmabuf))
> > dma_resv_unlock(attach->dmabuf->resv);
> >
> > +err_sysfs:
> > dma_buf_detach(dmabuf, attach);
> > return ERR_PTR(ret);
> > }
> > @@ -799,6 +822,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
> > dma_resv_lock(attach->dmabuf->resv, NULL);
> >
> > dmabuf->ops->unmap_dma_buf(attach, attach->sgt, attach->dir);
> > + dma_buf_update_attachment_map_count(attach, -1 /* delta */);
> >
> > if (dma_buf_is_dynamic(attach->dmabuf)) {
> > dma_buf_unpin(attach);
> > @@ -812,6 +836,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
> > if (dmabuf->ops->detach)
> > dmabuf->ops->detach(dmabuf, attach);
> >
> > + dma_buf_attach_stats_teardown(attach);
> > kfree(attach);
> > }
> > EXPORT_SYMBOL_GPL(dma_buf_detach);
> > @@ -938,6 +963,9 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
> > }
> > #endif /* CONFIG_DMA_API_DEBUG */
> >
> > + if (!IS_ERR(sg_table))
> > + dma_buf_update_attachment_map_count(attach, 1 /* delta */);
> > +
> > return sg_table;
> > }
> > EXPORT_SYMBOL_GPL(dma_buf_map_attachment);
> > @@ -975,6 +1003,8 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
> > if (dma_buf_is_dynamic(attach->dmabuf) &&
> > !IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY))
> > dma_buf_unpin(attach);
> > +
> > + dma_buf_update_attachment_map_count(attach, -1 /* delta */);
> > }
> > EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
> >
> > @@ -1412,6 +1442,12 @@ static inline void dma_buf_uninit_debugfs(void)
> >
> > static int __init dma_buf_init(void)
> > {
> > + int ret;
> > +
> > + ret = dma_buf_init_sysfs_statistics();
> > + if (ret)
> > + return ret;
> > +
> > dma_buf_mnt = kern_mount(&dma_buf_fs_type);
> > if (IS_ERR(dma_buf_mnt))
> > return PTR_ERR(dma_buf_mnt);
> > @@ -1427,5 +1463,6 @@ static void __exit dma_buf_deinit(void)
> > {
> > dma_buf_uninit_debugfs();
> > kern_unmount(dma_buf_mnt);
> > + dma_buf_uninit_sysfs_statistics();
> > }
> > __exitcall(dma_buf_deinit);
> > diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
> > index cf72699cb2bc..4ae5cc38a4a7 100644
> > --- a/include/linux/dma-buf.h
> > +++ b/include/linux/dma-buf.h
> > @@ -294,6 +294,9 @@ struct dma_buf_ops {
> > * @poll: for userspace poll support
> > * @cb_excl: for userspace poll support
> > * @cb_shared: for userspace poll support
> > + * @sysfs_entry: for exposing information about this buffer in sysfs.
> > + * The attachment_uid member of @sysfs_entry is protected by dma_resv lock
> > + * and is incremented on each attach.
> > *
> > * This represents a shared buffer, created by calling dma_buf_export(). The
> > * userspace representation is a normal file descriptor, which can be created by
> > @@ -329,6 +332,15 @@ struct dma_buf {
> >
> > __poll_t active;
> > } cb_excl, cb_shared;
> > +#ifdef CONFIG_DMABUF_SYSFS_STATS
> > + /* for sysfs stats */
> > + struct dma_buf_sysfs_entry {
> > + struct kobject kobj;
> > + struct dma_buf *dmabuf;
> > + unsigned int attachment_uid;
> > + struct kset *attach_stats_kset;
> > + } *sysfs_entry;
> > +#endif
>
> Why not directly embed that?

Thank you for the review Christian! Kobjects need to act as reference
counters to the objects they are embedded in. Since ref-counting for
struct dma_buf is already handled by its 'file' member, we cannot
embed the kobject directly into struct dma_buf.

>
> > };
> >
> > /**
> > @@ -378,6 +390,7 @@ struct dma_buf_attach_ops {
> > * @importer_ops: importer operations for this attachment, if provided
> > * dma_buf_map/unmap_attachment() must be called with the dma_resv lock held.
> > * @importer_priv: importer specific attachment data.
> > + * @sysfs_entry: For exposing information about this attachment in sysfs.
> > *
> > * This structure holds the attachment information between the dma_buf buffer
> > * and its user device(s). The list contains one attachment struct per device
> > @@ -398,6 +411,13 @@ struct dma_buf_attachment {
> > const struct dma_buf_attach_ops *importer_ops;
> > void *importer_priv;
> > void *priv;
> > +#ifdef CONFIG_DMABUF_SYSFS_STATS
> > + /* for sysfs stats */
> > + struct dma_buf_attach_sysfs_entry {
> > + struct kobject kobj;
> > + unsigned int map_counter;
> > + } *sysfs_entry;
> > +#endif
>
> Same question here.

It is not directly embedded into struct dma_buf_attachment for the
same reason as above since I do not think that it is correct to have
the kobject act as reference count for struct dma_buf_attachment.

Thanks again for all the reviews!

Regards,
Hridya



>
> Apart from that the general approach looks solid to me now.
>
> But somebody with more sysfs background should check if everything there
> is the right thing to do.
>
> Regards,
> Christian.
>
>
>
> > };
> >
> > /**

2021-01-20 20:45:10

by Hridya Valsaraju

[permalink] [raw]
Subject: Re: [Linaro-mm-sig] [PATCH v2] dmabuf: Add the capability to expose DMA-BUF stats in sysfs

On Wed, Jan 20, 2021 at 4:42 AM Daniel Vetter <[email protected]> wrote:
>
> On Wed, Jan 20, 2021 at 1:22 PM Christian König
> <[email protected]> wrote:
> >
> > Am 19.01.21 um 23:57 schrieb Hridya Valsaraju:
> > > 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 <[email protected]>
> > > ---
> > > Changes in v2:
> > > -Move statistics to /sys/kernel/dmabuf/buffers in oder to allow addition
> > > of other DMA-BUF-related sysfs stats in future. Based on feedback from
> > > Daniel Vetter.
> > > -Each attachment has its own directory to represent attaching devices as
> > > symlinks and to introduce map_count as a separate file. Based on
> > > feedback from Daniel Vetter and Christian König. Thank you both!
> > > -Commit messages updated to point to userspace code in AOSP that will
> > > read the DMA-BUF sysfs stats.
> > >
> > > .../ABI/testing/sysfs-kernel-dmabuf-buffers | 52 ++++
> > > drivers/dma-buf/Kconfig | 11 +
> > > drivers/dma-buf/Makefile | 1 +
> > > drivers/dma-buf/dma-buf-sysfs-stats.c | 283 ++++++++++++++++++
> > > drivers/dma-buf/dma-buf-sysfs-stats.h | 62 ++++
> > > drivers/dma-buf/dma-buf.c | 37 +++
> > > include/linux/dma-buf.h | 20 ++
> > > 7 files changed, 466 insertions(+)
> > > create mode 100644 Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> > > create mode 100644 drivers/dma-buf/dma-buf-sysfs-stats.c
> > > create mode 100644 drivers/dma-buf/dma-buf-sysfs-stats.h
> > >
> > > diff --git a/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers b/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> > > new file mode 100644
> > > index 000000000000..6f7c65209f07
> > > --- /dev/null
> > > +++ b/Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers
> > > @@ -0,0 +1,52 @@
> > > +What: /sys/kernel/dmabuf/buffers
> > > +Date: January 2021
> > > +KernelVersion: v5.12
> > > +Contact: Hridya Valsaraju <[email protected]>
> > > +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 <[email protected]>
> > > +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 <[email protected]>
> > > +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 <[email protected]>
> > > +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 <[email protected]>
> > > +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 <[email protected]>
> > > +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 <[email protected]>
> > > +Description: This file is read-only and contains a map_counter indicating the
> > > + number of distinct device mappings of the attachment.
> > > diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig
> > > index 4f8224a6ac95..27e6a2dafeaa 100644
> > > --- a/drivers/dma-buf/Kconfig
> > > +++ b/drivers/dma-buf/Kconfig
> > > @@ -64,6 +64,17 @@ menuconfig DMABUF_HEAPS
> > > allows userspace to allocate dma-bufs that can be shared
> > > between drivers.
> > >
> > > +menuconfig DMABUF_SYSFS_STATS
> > > + bool "DMA-BUF sysfs statistics"
> > > + select DMA_SHARED_BUFFER
> > > + help
> > > + Choose this option to enable DMA-BUF sysfs statistics
> > > + in location /sys/kernel/dmabuf/buffers.
> > > +
> > > + /sys/kernel/dmabuf/buffers/<inode_number> will contain
> > > + statistics for the DMA-BUF with the unique inode number
> > > + <inode_number>.
> > > +
> > > source "drivers/dma-buf/heaps/Kconfig"
> > >
> > > endmenu
> > > diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
> > > index 995e05f609ff..40d81f23cacf 100644
> > > --- a/drivers/dma-buf/Makefile
> > > +++ b/drivers/dma-buf/Makefile
> > > @@ -6,6 +6,7 @@ obj-$(CONFIG_DMABUF_HEAPS) += heaps/
> > > obj-$(CONFIG_SYNC_FILE) += sync_file.o
> > > obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
> > > obj-$(CONFIG_UDMABUF) += udmabuf.o
> > > +obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o
> > >
> > > dmabuf_selftests-y := \
> > > selftest.o \
> > > diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.c b/drivers/dma-buf/dma-buf-sysfs-stats.c
> > > new file mode 100644
> > > index 000000000000..61f85c3d16a5
> > > --- /dev/null
> > > +++ b/drivers/dma-buf/dma-buf-sysfs-stats.c
> > > @@ -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;
> > > +}
> > > diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.h b/drivers/dma-buf/dma-buf-sysfs-stats.h
> > > new file mode 100644
> > > index 000000000000..5f4703249117
> > > --- /dev/null
> > > +++ b/drivers/dma-buf/dma-buf-sysfs-stats.h
> > > @@ -0,0 +1,62 @@
> > > +/* SPDX-License-Identifier: GPL-2.0-only */
> > > +/*
> > > + * DMA-BUF sysfs statistics.
> > > + *
> > > + * Copyright (C) 2021 Google LLC.
> > > + */
> > > +
> > > +#ifndef _DMA_BUF_SYSFS_STATS_H
> > > +#define _DMA_BUF_SYSFS_STATS_H
> > > +
> > > +#ifdef CONFIG_DMABUF_SYSFS_STATS
> > > +
> > > +int dma_buf_init_sysfs_statistics(void);
> > > +void dma_buf_uninit_sysfs_statistics(void);
> > > +
> > > +int dma_buf_stats_setup(struct dma_buf *dmabuf);
> > > +int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
> > > + unsigned int uid);
> > > +static inline void dma_buf_update_attachment_map_count(struct dma_buf_attachment *attach,
> > > + int delta)
> > > +{
> > > + struct dma_buf_attach_sysfs_entry *entry = attach->sysfs_entry;
> > > +
> > > + entry->map_counter += delta;
> > > +}
> > > +void dma_buf_stats_teardown(struct dma_buf *dmabuf);
> > > +void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach);
> > > +static inline unsigned int dma_buf_update_attach_uid(struct dma_buf *dmabuf)
> > > +{
> > > + struct dma_buf_sysfs_entry *entry = dmabuf->sysfs_entry;
> > > +
> > > + return entry->attachment_uid++;
> > > +}
> > > +#else
> > > +
> > > +static inline int dma_buf_init_sysfs_statistics(void)
> > > +{
> > > + return 0;
> > > +}
> > > +
> > > +static inline void dma_buf_uninit_sysfs_statistics(void) {}
> > > +
> > > +static inline int dma_buf_stats_setup(struct dma_buf *dmabuf)
> > > +{
> > > + return 0;
> > > +}
> > > +static inline int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
> > > + unsigned int uid)
> > > +{
> > > + return 0;
> > > +}
> > > +
> > > +static inline void dma_buf_stats_teardown(struct dma_buf *dmabuf) {}
> > > +static inline void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach) {}
> > > +static inline void dma_buf_update_attachment_map_count(struct dma_buf_attachment *attach,
> > > + int delta) {}
> > > +static inline unsigned int dma_buf_update_attach_uid(struct dma_buf *dmabuf)
> > > +{
> > > + return 0;
> > > +}
> > > +#endif
> > > +#endif // _DMA_BUF_SYSFS_STATS_H
> > > diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
> > > index 9ad6397aaa97..29f9ea18eb47 100644
> > > --- a/drivers/dma-buf/dma-buf.c
> > > +++ b/drivers/dma-buf/dma-buf.c
> > > @@ -29,6 +29,8 @@
> > > #include <uapi/linux/dma-buf.h>
> > > #include <uapi/linux/magic.h>
> > >
> > > +#include "dma-buf-sysfs-stats.h"
> > > +
> > > static inline int is_dma_buf_file(struct file *);
> > >
> > > struct dma_buf_list {
> > > @@ -79,6 +81,7 @@ static void dma_buf_release(struct dentry *dentry)
> > > if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
> > > dma_resv_fini(dmabuf->resv);
> > >
> > > + dma_buf_stats_teardown(dmabuf);
> > > module_put(dmabuf->owner);
> > > kfree(dmabuf->name);
> > > kfree(dmabuf);
> > > @@ -579,6 +582,10 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
> > > file->f_mode |= FMODE_LSEEK;
> > > dmabuf->file = file;
> > >
> > > + ret = dma_buf_stats_setup(dmabuf);
> > > + if (ret)
> > > + goto err_sysfs;
> > > +
> > > mutex_init(&dmabuf->lock);
> > > INIT_LIST_HEAD(&dmabuf->attachments);
> > >
> > > @@ -588,6 +595,14 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
> > >
> > > return dmabuf;
> > >
> > > +err_sysfs:
> > > + /*
> > > + * Set file->f_path.dentry->d_fsdata to NULL so that when
> > > + * dma_buf_release() gets invoked by dentry_ops, it exits
> > > + * early before calling the release() dma_buf op.
> > > + */
> > > + file->f_path.dentry->d_fsdata = NULL;
> > > + fput(file);
> > > err_dmabuf:
> > > kfree(dmabuf);
> > > err_module:
> > > @@ -692,6 +707,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > > {
> > > struct dma_buf_attachment *attach;
> > > int ret;
> > > + unsigned int attach_uid;
> > >
> > > if (WARN_ON(!dmabuf || !dev))
> > > return ERR_PTR(-EINVAL);
> > > @@ -717,8 +733,13 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > > }
> > > dma_resv_lock(dmabuf->resv, NULL);
> > > list_add(&attach->node, &dmabuf->attachments);
> > > + attach_uid = dma_buf_update_attach_uid(dmabuf);
> > > dma_resv_unlock(dmabuf->resv);
> > >
> > > + ret = dma_buf_attach_stats_setup(attach, attach_uid);
> > > + if (ret)
> > > + goto err_sysfs;
> > > +
> > > /* When either the importer or the exporter can't handle dynamic
> > > * mappings we cache the mapping here to avoid issues with the
> > > * reservation object lock.
> > > @@ -745,6 +766,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > > dma_resv_unlock(attach->dmabuf->resv);
> > > attach->sgt = sgt;
> > > attach->dir = DMA_BIDIRECTIONAL;
> > > + dma_buf_update_attachment_map_count(attach, 1 /* delta */);
> > > }
> > >
> > > return attach;
> > > @@ -761,6 +783,7 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
> > > if (dma_buf_is_dynamic(attach->dmabuf))
> > > dma_resv_unlock(attach->dmabuf->resv);
> > >
> > > +err_sysfs:
> > > dma_buf_detach(dmabuf, attach);
> > > return ERR_PTR(ret);
> > > }
> > > @@ -799,6 +822,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
> > > dma_resv_lock(attach->dmabuf->resv, NULL);
> > >
> > > dmabuf->ops->unmap_dma_buf(attach, attach->sgt, attach->dir);
> > > + dma_buf_update_attachment_map_count(attach, -1 /* delta */);
> > >
> > > if (dma_buf_is_dynamic(attach->dmabuf)) {
> > > dma_buf_unpin(attach);
> > > @@ -812,6 +836,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
> > > if (dmabuf->ops->detach)
> > > dmabuf->ops->detach(dmabuf, attach);
> > >
> > > + dma_buf_attach_stats_teardown(attach);
> > > kfree(attach);
> > > }
> > > EXPORT_SYMBOL_GPL(dma_buf_detach);
> > > @@ -938,6 +963,9 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
> > > }
> > > #endif /* CONFIG_DMA_API_DEBUG */
> > >
> > > + if (!IS_ERR(sg_table))
> > > + dma_buf_update_attachment_map_count(attach, 1 /* delta */);
> > > +
> > > return sg_table;
> > > }
> > > EXPORT_SYMBOL_GPL(dma_buf_map_attachment);
> > > @@ -975,6 +1003,8 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
> > > if (dma_buf_is_dynamic(attach->dmabuf) &&
> > > !IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY))
> > > dma_buf_unpin(attach);
> > > +
> > > + dma_buf_update_attachment_map_count(attach, -1 /* delta */);
> > > }
> > > EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
> > >
> > > @@ -1412,6 +1442,12 @@ static inline void dma_buf_uninit_debugfs(void)
> > >
> > > static int __init dma_buf_init(void)
> > > {
> > > + int ret;
> > > +
> > > + ret = dma_buf_init_sysfs_statistics();
> > > + if (ret)
> > > + return ret;
> > > +
> > > dma_buf_mnt = kern_mount(&dma_buf_fs_type);
> > > if (IS_ERR(dma_buf_mnt))
> > > return PTR_ERR(dma_buf_mnt);
> > > @@ -1427,5 +1463,6 @@ static void __exit dma_buf_deinit(void)
> > > {
> > > dma_buf_uninit_debugfs();
> > > kern_unmount(dma_buf_mnt);
> > > + dma_buf_uninit_sysfs_statistics();
> > > }
> > > __exitcall(dma_buf_deinit);
> > > diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
> > > index cf72699cb2bc..4ae5cc38a4a7 100644
> > > --- a/include/linux/dma-buf.h
> > > +++ b/include/linux/dma-buf.h
> > > @@ -294,6 +294,9 @@ struct dma_buf_ops {
> > > * @poll: for userspace poll support
> > > * @cb_excl: for userspace poll support
> > > * @cb_shared: for userspace poll support
> > > + * @sysfs_entry: for exposing information about this buffer in sysfs.
> > > + * The attachment_uid member of @sysfs_entry is protected by dma_resv lock
> > > + * and is incremented on each attach.
> > > *
> > > * This represents a shared buffer, created by calling dma_buf_export(). The
> > > * userspace representation is a normal file descriptor, which can be created by
> > > @@ -329,6 +332,15 @@ struct dma_buf {
> > >
> > > __poll_t active;
> > > } cb_excl, cb_shared;
> > > +#ifdef CONFIG_DMABUF_SYSFS_STATS
> > > + /* for sysfs stats */
> > > + struct dma_buf_sysfs_entry {
> > > + struct kobject kobj;
> > > + struct dma_buf *dmabuf;
> > > + unsigned int attachment_uid;
> > > + struct kset *attach_stats_kset;
> > > + } *sysfs_entry;
> > > +#endif
> >
> > Why not directly embed that?
> >
> > > };
> > >
> > > /**
> > > @@ -378,6 +390,7 @@ struct dma_buf_attach_ops {
> > > * @importer_ops: importer operations for this attachment, if provided
> > > * dma_buf_map/unmap_attachment() must be called with the dma_resv lock held.
> > > * @importer_priv: importer specific attachment data.
> > > + * @sysfs_entry: For exposing information about this attachment in sysfs.
> > > *
> > > * This structure holds the attachment information between the dma_buf buffer
> > > * and its user device(s). The list contains one attachment struct per device
> > > @@ -398,6 +411,13 @@ struct dma_buf_attachment {
> > > const struct dma_buf_attach_ops *importer_ops;
> > > void *importer_priv;
> > > void *priv;
> > > +#ifdef CONFIG_DMABUF_SYSFS_STATS
> > > + /* for sysfs stats */
> > > + struct dma_buf_attach_sysfs_entry {
> > > + struct kobject kobj;
> > > + unsigned int map_counter;
> > > + } *sysfs_entry;
> > > +#endif
> >
> > Same question here.
> >
> > Apart from that the general approach looks solid to me now.
> >
> > But somebody with more sysfs background should check if everything there
> > is the right thing to do.
>
> So apparently Android is completely revamping all of this, but feeding
> it to upstream in pieces, and that doesn't work. Here's another part:
>
> https://lore.kernel.org/dri-devel/CAKMK7uE3xF80AsJ1zGfSM-KTry=ikJ-S-Dn6nK8ZAvCSWw2FHQ@mail.gmail.com/

Thank you for taking a look and the guidance so far Daniel! The above
patch (which adds tracepoints to track total GPU memory allocations)
is independent of the DMA-BUF sysfs stats effort. I agree that the
value from the tracepoint will include memory that is shared as
DMA-BUFs. However, there is no interdependency between the two
efforts. I have linked to the AOSP code that will parse the DMA-BUF
sysfs statistics in the commit message. We do have plans to expand the
sysfs interface itself in future(thank you for suggesting to make it
expandable in v1!) but when we do, we will make sure to send the
userspace code that uses it as well so that you can get the full
picture.

Regards,
Hridya

>
> I think we need an overall integrated solution here, or we're just
> building uapi that won't last (but we still have to support it
> forever).
>
> I think aside from having the full android side covered (but on
> upstream drivers, and including dma-buf sharing, not just only
> virtio-gpu) we also need to figure out how this will fit into other
> tracking efforts like cgroups. Otherwise we end up with one way of
> tracking gpu memory usage for android, implemented by out-of-tree
> drivers mostly. Another one for servers in cgroups, implemented by amd
> and intel, and then probably a third one for cros
> virtualization/containers, just because. That doesn't work too well.
> -Daniel
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch

2021-01-26 15:00:34

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v2] dmabuf: Add the capability to expose DMA-BUF stats in sysfs

Hi Hridya,

I love your patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.11-rc5]
[cannot apply to next-20210125]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/0day-ci/linux/commits/Hridya-Valsaraju/dmabuf-Add-the-capability-to-expose-DMA-BUF-stats-in-sysfs/20210120-172216
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 45dfb8a5659ad286c28fa59008271dbc4e5e3f2d
config: x86_64-allyesconfig (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce (this is a W=1 build):
# https://github.com/0day-ci/linux/commit/1ce52d5b375d9055a8ca6a7d78f7f1c256680190
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Hridya-Valsaraju/dmabuf-Add-the-capability-to-expose-DMA-BUF-stats-in-sysfs/20210120-172216
git checkout 1ce52d5b375d9055a8ca6a7d78f7f1c256680190
# save the attached .config to linux build tree
make W=1 ARCH=x86_64

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All warnings (new ones prefixed by >>):

>> drivers/dma-buf/dma-buf-sysfs-stats.c:144:6: warning: no previous prototype for 'dma_buf_attach_stats_teardown' [-Wmissing-prototypes]
144 | void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/dma-buf/dma-buf-sysfs-stats.c:158:5: warning: no previous prototype for 'dma_buf_attach_stats_setup' [-Wmissing-prototypes]
158 | int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/dma-buf/dma-buf-sysfs-stats.c:199:6: warning: no previous prototype for 'dma_buf_stats_teardown' [-Wmissing-prototypes]
199 | void dma_buf_stats_teardown(struct dma_buf *dmabuf)
| ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/dma-buf/dma-buf-sysfs-stats.c:214:5: warning: no previous prototype for 'dma_buf_init_sysfs_statistics' [-Wmissing-prototypes]
214 | int dma_buf_init_sysfs_statistics(void)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/dma-buf/dma-buf-sysfs-stats.c:230:6: warning: no previous prototype for 'dma_buf_uninit_sysfs_statistics' [-Wmissing-prototypes]
230 | void dma_buf_uninit_sysfs_statistics(void)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/dma-buf/dma-buf-sysfs-stats.c:236:5: warning: no previous prototype for 'dma_buf_stats_setup' [-Wmissing-prototypes]
236 | int dma_buf_stats_setup(struct dma_buf *dmabuf)
| ^~~~~~~~~~~~~~~~~~~


vim +/dma_buf_attach_stats_teardown +144 drivers/dma-buf/dma-buf-sysfs-stats.c

143
> 144 void dma_buf_attach_stats_teardown(struct dma_buf_attachment *attach)
145 {
146 struct dma_buf_attach_sysfs_entry *sysfs_entry;
147
148 sysfs_entry = attach->sysfs_entry;
149 if (!sysfs_entry)
150 return;
151
152 sysfs_delete_link(&sysfs_entry->kobj, &attach->dev->kobj, "device");
153
154 kobject_del(&sysfs_entry->kobj);
155 kobject_put(&sysfs_entry->kobj);
156 }
157
> 158 int dma_buf_attach_stats_setup(struct dma_buf_attachment *attach,
159 unsigned int uid)
160 {
161 struct dma_buf_attach_sysfs_entry *sysfs_entry;
162 int ret;
163 struct dma_buf *dmabuf;
164
165 if (!attach)
166 return -EINVAL;
167
168 dmabuf = attach->dmabuf;
169
170 sysfs_entry = kzalloc(sizeof(struct dma_buf_attach_sysfs_entry),
171 GFP_KERNEL);
172 if (!sysfs_entry)
173 return -ENOMEM;
174
175 sysfs_entry->kobj.kset = dmabuf->sysfs_entry->attach_stats_kset;
176
177 attach->sysfs_entry = sysfs_entry;
178
179 ret = kobject_init_and_add(&sysfs_entry->kobj, &dma_buf_attach_ktype,
180 NULL, "%u", uid);
181 if (ret)
182 goto kobj_err;
183
184 ret = sysfs_create_link(&sysfs_entry->kobj, &attach->dev->kobj,
185 "device");
186 if (ret)
187 goto link_err;
188
189 return 0;
190
191 link_err:
192 kobject_del(&sysfs_entry->kobj);
193 kobj_err:
194 kobject_put(&sysfs_entry->kobj);
195 attach->sysfs_entry = NULL;
196
197 return ret;
198 }
> 199 void dma_buf_stats_teardown(struct dma_buf *dmabuf)
200 {
201 struct dma_buf_sysfs_entry *sysfs_entry;
202
203 sysfs_entry = dmabuf->sysfs_entry;
204 if (!sysfs_entry)
205 return;
206
207 kset_unregister(sysfs_entry->attach_stats_kset);
208 kobject_del(&sysfs_entry->kobj);
209 kobject_put(&sysfs_entry->kobj);
210 }
211
212 static struct kset *dma_buf_stats_kset;
213 static struct kset *dma_buf_per_buffer_stats_kset;
> 214 int dma_buf_init_sysfs_statistics(void)
215 {
216 dma_buf_stats_kset = kset_create_and_add("dmabuf", NULL, kernel_kobj);
217 if (!dma_buf_stats_kset)
218 return -ENOMEM;
219
220 dma_buf_per_buffer_stats_kset = kset_create_and_add("buffers", NULL,
221 &dma_buf_stats_kset->kobj);
222 if (!dma_buf_per_buffer_stats_kset) {
223 kset_unregister(dma_buf_stats_kset);
224 return -ENOMEM;
225 }
226
227 return 0;
228 }
229
> 230 void dma_buf_uninit_sysfs_statistics(void)
231 {
232 kset_unregister(dma_buf_per_buffer_stats_kset);
233 kset_unregister(dma_buf_stats_kset);
234 }
235
> 236 int dma_buf_stats_setup(struct dma_buf *dmabuf)

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (5.77 kB)
.config.gz (76.03 kB)
Download all attachments