162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DMA-BUF sysfs statistics. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2021 Google LLC. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/dma-buf.h> 962306a36Sopenharmony_ci#include <linux/dma-resv.h> 1062306a36Sopenharmony_ci#include <linux/kobject.h> 1162306a36Sopenharmony_ci#include <linux/printk.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/sysfs.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "dma-buf-sysfs-stats.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define to_dma_buf_entry_from_kobj(x) container_of(x, struct dma_buf_sysfs_entry, kobj) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/** 2062306a36Sopenharmony_ci * DOC: overview 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * ``/sys/kernel/debug/dma_buf/bufinfo`` provides an overview of every DMA-BUF 2362306a36Sopenharmony_ci * in the system. However, since debugfs is not safe to be mounted in 2462306a36Sopenharmony_ci * production, procfs and sysfs can be used to gather DMA-BUF statistics on 2562306a36Sopenharmony_ci * production systems. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * The ``/proc/<pid>/fdinfo/<fd>`` files in procfs can be used to gather 2862306a36Sopenharmony_ci * information about DMA-BUF fds. Detailed documentation about the interface 2962306a36Sopenharmony_ci * is present in Documentation/filesystems/proc.rst. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Unfortunately, the existing procfs interfaces can only provide information 3262306a36Sopenharmony_ci * about the DMA-BUFs for which processes hold fds or have the buffers mmapped 3362306a36Sopenharmony_ci * into their address space. This necessitated the creation of the DMA-BUF sysfs 3462306a36Sopenharmony_ci * statistics interface to provide per-buffer information on production systems. 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * The interface at ``/sys/kernel/dmabuf/buffers`` exposes information about 3762306a36Sopenharmony_ci * every DMA-BUF when ``CONFIG_DMABUF_SYSFS_STATS`` is enabled. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * The following stats are exposed by the interface: 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * * ``/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name`` 4262306a36Sopenharmony_ci * * ``/sys/kernel/dmabuf/buffers/<inode_number>/size`` 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * The information in the interface can also be used to derive per-exporter 4562306a36Sopenharmony_ci * statistics. The data from the interface can be gathered on error conditions 4662306a36Sopenharmony_ci * or other important events to provide a snapshot of DMA-BUF usage. 4762306a36Sopenharmony_ci * It can also be collected periodically by telemetry to monitor various metrics. 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * Detailed documentation about the interface is present in 5062306a36Sopenharmony_ci * Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct dma_buf_stats_attribute { 5462306a36Sopenharmony_ci struct attribute attr; 5562306a36Sopenharmony_ci ssize_t (*show)(struct dma_buf *dmabuf, 5662306a36Sopenharmony_ci struct dma_buf_stats_attribute *attr, char *buf); 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci#define to_dma_buf_stats_attr(x) container_of(x, struct dma_buf_stats_attribute, attr) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic ssize_t dma_buf_stats_attribute_show(struct kobject *kobj, 6162306a36Sopenharmony_ci struct attribute *attr, 6262306a36Sopenharmony_ci char *buf) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct dma_buf_stats_attribute *attribute; 6562306a36Sopenharmony_ci struct dma_buf_sysfs_entry *sysfs_entry; 6662306a36Sopenharmony_ci struct dma_buf *dmabuf; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci attribute = to_dma_buf_stats_attr(attr); 6962306a36Sopenharmony_ci sysfs_entry = to_dma_buf_entry_from_kobj(kobj); 7062306a36Sopenharmony_ci dmabuf = sysfs_entry->dmabuf; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (!dmabuf || !attribute->show) 7362306a36Sopenharmony_ci return -EIO; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return attribute->show(dmabuf, attribute, buf); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic const struct sysfs_ops dma_buf_stats_sysfs_ops = { 7962306a36Sopenharmony_ci .show = dma_buf_stats_attribute_show, 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic ssize_t exporter_name_show(struct dma_buf *dmabuf, 8362306a36Sopenharmony_ci struct dma_buf_stats_attribute *attr, 8462306a36Sopenharmony_ci char *buf) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", dmabuf->exp_name); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic ssize_t size_show(struct dma_buf *dmabuf, 9062306a36Sopenharmony_ci struct dma_buf_stats_attribute *attr, 9162306a36Sopenharmony_ci char *buf) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci return sysfs_emit(buf, "%zu\n", dmabuf->size); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic struct dma_buf_stats_attribute exporter_name_attribute = 9762306a36Sopenharmony_ci __ATTR_RO(exporter_name); 9862306a36Sopenharmony_cistatic struct dma_buf_stats_attribute size_attribute = __ATTR_RO(size); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic struct attribute *dma_buf_stats_default_attrs[] = { 10162306a36Sopenharmony_ci &exporter_name_attribute.attr, 10262306a36Sopenharmony_ci &size_attribute.attr, 10362306a36Sopenharmony_ci NULL, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ciATTRIBUTE_GROUPS(dma_buf_stats_default); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void dma_buf_sysfs_release(struct kobject *kobj) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct dma_buf_sysfs_entry *sysfs_entry; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci sysfs_entry = to_dma_buf_entry_from_kobj(kobj); 11262306a36Sopenharmony_ci kfree(sysfs_entry); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic const struct kobj_type dma_buf_ktype = { 11662306a36Sopenharmony_ci .sysfs_ops = &dma_buf_stats_sysfs_ops, 11762306a36Sopenharmony_ci .release = dma_buf_sysfs_release, 11862306a36Sopenharmony_ci .default_groups = dma_buf_stats_default_groups, 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_civoid dma_buf_stats_teardown(struct dma_buf *dmabuf) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct dma_buf_sysfs_entry *sysfs_entry; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci sysfs_entry = dmabuf->sysfs_entry; 12662306a36Sopenharmony_ci if (!sysfs_entry) 12762306a36Sopenharmony_ci return; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci kobject_del(&sysfs_entry->kobj); 13062306a36Sopenharmony_ci kobject_put(&sysfs_entry->kobj); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* Statistics files do not need to send uevents. */ 13562306a36Sopenharmony_cistatic int dmabuf_sysfs_uevent_filter(const struct kobject *kobj) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic const struct kset_uevent_ops dmabuf_sysfs_no_uevent_ops = { 14162306a36Sopenharmony_ci .filter = dmabuf_sysfs_uevent_filter, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic struct kset *dma_buf_stats_kset; 14562306a36Sopenharmony_cistatic struct kset *dma_buf_per_buffer_stats_kset; 14662306a36Sopenharmony_ciint dma_buf_init_sysfs_statistics(void) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci dma_buf_stats_kset = kset_create_and_add("dmabuf", 14962306a36Sopenharmony_ci &dmabuf_sysfs_no_uevent_ops, 15062306a36Sopenharmony_ci kernel_kobj); 15162306a36Sopenharmony_ci if (!dma_buf_stats_kset) 15262306a36Sopenharmony_ci return -ENOMEM; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci dma_buf_per_buffer_stats_kset = kset_create_and_add("buffers", 15562306a36Sopenharmony_ci &dmabuf_sysfs_no_uevent_ops, 15662306a36Sopenharmony_ci &dma_buf_stats_kset->kobj); 15762306a36Sopenharmony_ci if (!dma_buf_per_buffer_stats_kset) { 15862306a36Sopenharmony_ci kset_unregister(dma_buf_stats_kset); 15962306a36Sopenharmony_ci return -ENOMEM; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_civoid dma_buf_uninit_sysfs_statistics(void) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci kset_unregister(dma_buf_per_buffer_stats_kset); 16862306a36Sopenharmony_ci kset_unregister(dma_buf_stats_kset); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ciint dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct dma_buf_sysfs_entry *sysfs_entry; 17462306a36Sopenharmony_ci int ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!dmabuf->exp_name) { 17762306a36Sopenharmony_ci pr_err("exporter name must not be empty if stats needed\n"); 17862306a36Sopenharmony_ci return -EINVAL; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci sysfs_entry = kzalloc(sizeof(struct dma_buf_sysfs_entry), GFP_KERNEL); 18262306a36Sopenharmony_ci if (!sysfs_entry) 18362306a36Sopenharmony_ci return -ENOMEM; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci sysfs_entry->kobj.kset = dma_buf_per_buffer_stats_kset; 18662306a36Sopenharmony_ci sysfs_entry->dmabuf = dmabuf; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci dmabuf->sysfs_entry = sysfs_entry; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* create the directory for buffer stats */ 19162306a36Sopenharmony_ci ret = kobject_init_and_add(&sysfs_entry->kobj, &dma_buf_ktype, NULL, 19262306a36Sopenharmony_ci "%lu", file_inode(file)->i_ino); 19362306a36Sopenharmony_ci if (ret) 19462306a36Sopenharmony_ci goto err_sysfs_dmabuf; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cierr_sysfs_dmabuf: 19962306a36Sopenharmony_ci kobject_put(&sysfs_entry->kobj); 20062306a36Sopenharmony_ci dmabuf->sysfs_entry = NULL; 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci} 203