1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * DMA-BUF sysfs statistics.
4 *
5 * Copyright (C) 2021 Google LLC.
6 */
7
8 #include <linux/dma-buf.h>
9 #include <linux/dma-resv.h>
10 #include <linux/kobject.h>
11 #include <linux/printk.h>
12 #include <linux/slab.h>
13 #include <linux/sysfs.h>
14 #include <linux/workqueue.h>
15
16 #include <trace/hooks/dmabuf.h>
17
18 #include "dma-buf-sysfs-stats.h"
19
20 #define to_dma_buf_entry_from_kobj(x) container_of(x, struct dma_buf_sysfs_entry, kobj)
21
22 struct dma_buf_stats_attribute {
23 struct attribute attr;
24 ssize_t (*show)(struct dma_buf *dmabuf,
25 struct dma_buf_stats_attribute *attr, char *buf);
26 };
27 #define to_dma_buf_stats_attr(x) container_of(x, struct dma_buf_stats_attribute, attr)
28
dma_buf_stats_attribute_show(struct kobject * kobj,struct attribute * attr,char * buf)29 static ssize_t dma_buf_stats_attribute_show(struct kobject *kobj,
30 struct attribute *attr,
31 char *buf)
32 {
33 struct dma_buf_stats_attribute *attribute;
34 struct dma_buf_sysfs_entry *sysfs_entry;
35 struct dma_buf *dmabuf;
36
37 attribute = to_dma_buf_stats_attr(attr);
38 sysfs_entry = to_dma_buf_entry_from_kobj(kobj);
39 dmabuf = sysfs_entry->dmabuf;
40
41 if (!dmabuf || !attribute->show)
42 return -EIO;
43
44 return attribute->show(dmabuf, attribute, buf);
45 }
46
47 static const struct sysfs_ops dma_buf_stats_sysfs_ops = {
48 .show = dma_buf_stats_attribute_show,
49 };
50
exporter_name_show(struct dma_buf * dmabuf,struct dma_buf_stats_attribute * attr,char * buf)51 static ssize_t exporter_name_show(struct dma_buf *dmabuf,
52 struct dma_buf_stats_attribute *attr,
53 char *buf)
54 {
55 return sysfs_emit(buf, "%s\n", dmabuf->exp_name);
56 }
57
size_show(struct dma_buf * dmabuf,struct dma_buf_stats_attribute * attr,char * buf)58 static ssize_t size_show(struct dma_buf *dmabuf,
59 struct dma_buf_stats_attribute *attr,
60 char *buf)
61 {
62 return sysfs_emit(buf, "%zu\n", dmabuf->size);
63 }
64
65 static struct dma_buf_stats_attribute exporter_name_attribute =
66 __ATTR_RO(exporter_name);
67 static struct dma_buf_stats_attribute size_attribute = __ATTR_RO(size);
68
69 static struct attribute *dma_buf_stats_default_attrs[] = {
70 &exporter_name_attribute.attr,
71 &size_attribute.attr,
72 NULL,
73 };
74 ATTRIBUTE_GROUPS(dma_buf_stats_default);
75
dma_buf_sysfs_release(struct kobject * kobj)76 static void dma_buf_sysfs_release(struct kobject *kobj)
77 {
78 struct dma_buf_sysfs_entry *sysfs_entry;
79
80 sysfs_entry = to_dma_buf_entry_from_kobj(kobj);
81 kfree(sysfs_entry);
82 }
83
84 static struct kobj_type dma_buf_ktype = {
85 .sysfs_ops = &dma_buf_stats_sysfs_ops,
86 .release = dma_buf_sysfs_release,
87 .default_groups = dma_buf_stats_default_groups,
88 };
89
dma_buf_stats_teardown(struct dma_buf * dmabuf)90 void dma_buf_stats_teardown(struct dma_buf *dmabuf)
91 {
92 struct dma_buf_sysfs_entry *sysfs_entry;
93 bool skip_sysfs_release = false;
94
95 sysfs_entry = dmabuf->sysfs_entry;
96 if (!sysfs_entry)
97 return;
98
99 trace_android_rvh_dma_buf_stats_teardown(sysfs_entry, &skip_sysfs_release);
100 if (!skip_sysfs_release) {
101 kobject_del(&sysfs_entry->kobj);
102 kobject_put(&sysfs_entry->kobj);
103 }
104 }
105
106 /*
107 * Statistics files do not need to send uevents.
108 */
dmabuf_sysfs_uevent_filter(struct kset * kset,struct kobject * kobj)109 static int dmabuf_sysfs_uevent_filter(struct kset *kset, struct kobject *kobj)
110 {
111 return 0;
112 }
113
114 static const struct kset_uevent_ops dmabuf_sysfs_no_uevent_ops = {
115 .filter = dmabuf_sysfs_uevent_filter,
116 };
117
118 static struct kset *dma_buf_stats_kset;
119 static struct kset *dma_buf_per_buffer_stats_kset;
dma_buf_init_sysfs_statistics(void)120 int dma_buf_init_sysfs_statistics(void)
121 {
122 dma_buf_stats_kset = kset_create_and_add("dmabuf",
123 &dmabuf_sysfs_no_uevent_ops,
124 kernel_kobj);
125 if (!dma_buf_stats_kset)
126 return -ENOMEM;
127
128 dma_buf_per_buffer_stats_kset = kset_create_and_add("buffers",
129 &dmabuf_sysfs_no_uevent_ops,
130 &dma_buf_stats_kset->kobj);
131 if (!dma_buf_per_buffer_stats_kset) {
132 kset_unregister(dma_buf_stats_kset);
133 return -ENOMEM;
134 }
135
136 return 0;
137 }
138
dma_buf_uninit_sysfs_statistics(void)139 void dma_buf_uninit_sysfs_statistics(void)
140 {
141 kset_unregister(dma_buf_per_buffer_stats_kset);
142 kset_unregister(dma_buf_stats_kset);
143 }
144
145 struct dma_buf_create_sysfs_entry {
146 struct dma_buf *dmabuf;
147 struct work_struct work;
148 };
149
150 union dma_buf_create_sysfs_work_entry {
151 struct dma_buf_create_sysfs_entry create_entry;
152 struct dma_buf_sysfs_entry sysfs_entry;
153 };
154
sysfs_add_workfn(struct work_struct * work)155 static void sysfs_add_workfn(struct work_struct *work)
156 {
157 struct dma_buf_create_sysfs_entry *create_entry =
158 container_of(work, struct dma_buf_create_sysfs_entry, work);
159 struct dma_buf *dmabuf = create_entry->dmabuf;
160
161 /*
162 * A dmabuf is ref-counted via its file member. If this handler holds the only
163 * reference to the dmabuf, there is no need for sysfs kobject creation. This is an
164 * optimization and a race; when the reference count drops to 1 immediately after
165 * this check it is not harmful as the sysfs entry will still get cleaned up in
166 * dma_buf_stats_teardown, which won't get called until the final dmabuf reference
167 * is released, and that can't happen until the end of this function.
168 */
169 if (file_count(dmabuf->file) > 1) {
170 dmabuf->sysfs_entry->dmabuf = dmabuf;
171 /*
172 * kobject_init_and_add expects kobject to be zero-filled, but we have populated it
173 * to trigger this work function.
174 */
175 memset(&dmabuf->sysfs_entry->kobj, 0, sizeof(dmabuf->sysfs_entry->kobj));
176 dmabuf->sysfs_entry->kobj.kset = dma_buf_per_buffer_stats_kset;
177 if (kobject_init_and_add(&dmabuf->sysfs_entry->kobj, &dma_buf_ktype, NULL,
178 "%lu", file_inode(dmabuf->file)->i_ino)) {
179 kobject_put(&dmabuf->sysfs_entry->kobj);
180 dmabuf->sysfs_entry = NULL;
181 }
182 } else {
183 /*
184 * Free the sysfs_entry and reset the pointer so dma_buf_stats_teardown doesn't
185 * attempt to operate on it.
186 */
187 kfree(dmabuf->sysfs_entry);
188 dmabuf->sysfs_entry = NULL;
189 }
190 dma_buf_put(dmabuf);
191 }
192
dma_buf_stats_setup(struct dma_buf * dmabuf)193 int dma_buf_stats_setup(struct dma_buf *dmabuf)
194 {
195 struct dma_buf_create_sysfs_entry *create_entry;
196 union dma_buf_create_sysfs_work_entry *work_entry;
197
198 if (!dmabuf || !dmabuf->file)
199 return -EINVAL;
200
201 if (!dmabuf->exp_name) {
202 pr_err("exporter name must not be empty if stats needed\n");
203 return -EINVAL;
204 }
205
206 work_entry = kmalloc(sizeof(union dma_buf_create_sysfs_work_entry), GFP_KERNEL);
207 if (!work_entry)
208 return -ENOMEM;
209
210 dmabuf->sysfs_entry = &work_entry->sysfs_entry;
211
212 create_entry = &work_entry->create_entry;
213 create_entry->dmabuf = dmabuf;
214
215 INIT_WORK(&create_entry->work, sysfs_add_workfn);
216 get_dma_buf(dmabuf); /* This reference will be dropped in sysfs_add_workfn. */
217 schedule_work(&create_entry->work);
218
219 return 0;
220 }
221