xref: /OK3568_Linux_fs/kernel/drivers/dma-buf/dma-buf-sysfs-stats.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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