• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Allwinner SoCs display driver.
3  *
4  * Copyright (C) 2018 Allwinner.
5  *
6  * This file is licensed under the terms of the GNU General Public
7  * License version 2.  This program is licensed "as is" without any
8  * warranty of any kind, whether express or implied.
9  */
10 
11 /*
12  * This driver is modified from sw_sync, because we cannot
13  * access sw_sync through debugfs after Android-11.
14  */
15 
16 #include <linux/dma-fence.h>
17 #include <linux/file.h>
18 #include <linux/fs.h>
19 #include <linux/uaccess.h>
20 #include <linux/sched.h>
21 #include <linux/slab.h>
22 #include <linux/sync_file.h>
23 #include <linux/miscdevice.h>
24 
25 /*
26  * struct syncfence_create_data
27  * @value:	the seqno to initialise the fence with
28  * @name:	the name of the new sync point
29  * @fence:	return the fd of the new sync_file with the created fence
30  */
31 struct syncfence_create_data {
32 	__u32   value;
33 	char    name[32];
34 	__s32   fence;
35 };
36 
37 #define SYNCFENCE_IOC_MAGIC	'Z'
38 #define SYNCFENCE_IOC_CREATE_FENCE \
39 	_IOWR(SYNCFENCE_IOC_MAGIC, 0, struct syncfence_create_data)
40 #define SYNCFENCE_IOC_INC _IOW(SYNCFENCE_IOC_MAGIC, 1, __u32)
41 
42 struct fence_timeline {
43 	struct kref ref;
44 	char name[32];
45 
46 	u64 context;
47 	int value;
48 	struct list_head pt_list;
49 	spinlock_t lock;
50 
51 	struct list_head timeline_list;
52 };
53 
54 struct syncfence {
55 	struct dma_fence base;
56 	struct list_head link;
57 };
58 
59 static LIST_HEAD(timeline_list_head);
60 static DEFINE_SPINLOCK(timeline_list_lock);
61 
62 static const struct dma_fence_ops timeline_fence_ops;
63 
dma_fence_to_syncfence(struct dma_fence * fence)64 static inline struct syncfence *dma_fence_to_syncfence(struct dma_fence *fence)
65 {
66 	if (fence->ops != &timeline_fence_ops)
67 		return NULL;
68 	return container_of(fence, struct syncfence, base);
69 }
70 
dma_fence_parent(struct dma_fence * fence)71 static inline struct fence_timeline *dma_fence_parent(struct dma_fence *fence)
72 {
73 	return container_of(fence->lock, struct fence_timeline, lock);
74 }
75 
fence_timeline_create(const char * name)76 static struct fence_timeline *fence_timeline_create(const char *name)
77 {
78 	unsigned long flags;
79 	struct fence_timeline *timeline;
80 
81 	timeline = kzalloc(sizeof(*timeline), GFP_KERNEL);
82 	if (!timeline)
83 		return NULL;
84 
85 	kref_init(&timeline->ref);
86 	timeline->context = dma_fence_context_alloc(1);
87 	strlcpy(timeline->name, name, sizeof(timeline->name));
88 	INIT_LIST_HEAD(&timeline->pt_list);
89 
90 	/* add the new timeline into timeline_list_head */
91 	spin_lock_irqsave(&timeline_list_lock, flags);
92 	list_add_tail(&timeline->timeline_list, &timeline_list_head);
93 	spin_unlock_irqrestore(&timeline_list_lock, flags);
94 
95 	return timeline;
96 }
97 
fence_timeline_free(struct kref * ref)98 static void fence_timeline_free(struct kref *ref)
99 {
100 	unsigned long flags;
101 	struct fence_timeline *timeline =
102 		container_of(ref, struct fence_timeline, ref);
103 
104 	/* remove it from timeline_list_head */
105 	spin_lock_irqsave(&timeline_list_lock, flags);
106 	list_del(&timeline->timeline_list);
107 	spin_unlock_irqrestore(&timeline_list_lock, flags);
108 
109 	kfree(timeline);
110 }
111 
fence_timeline_get(struct fence_timeline * timeline)112 static void fence_timeline_get(struct fence_timeline *timeline)
113 {
114 	kref_get(&timeline->ref);
115 }
116 
fence_timeline_put(struct fence_timeline * timeline)117 static void fence_timeline_put(struct fence_timeline *timeline)
118 {
119 	kref_put(&timeline->ref, fence_timeline_free);
120 }
121 
timeline_fence_get_driver_name(struct dma_fence * fence)122 static const char *timeline_fence_get_driver_name(struct dma_fence *fence)
123 {
124 	return "syncfence";
125 }
126 
timeline_fence_get_timeline_name(struct dma_fence * fence)127 static const char *timeline_fence_get_timeline_name(struct dma_fence *fence)
128 {
129 	struct fence_timeline *parent = dma_fence_parent(fence);
130 	return parent->name;
131 }
132 
timeline_fence_release(struct dma_fence * fence)133 static void timeline_fence_release(struct dma_fence *fence)
134 {
135 	unsigned long flags;
136 	struct syncfence *sf = dma_fence_to_syncfence(fence);
137 	struct fence_timeline *parent = dma_fence_parent(fence);
138 
139 	spin_lock_irqsave(fence->lock, flags);
140 	if (!list_empty(&sf->link))
141 		list_del(&sf->link);
142 	spin_unlock_irqrestore(fence->lock, flags);
143 
144 	fence_timeline_put(parent);
145 	dma_fence_free(fence);
146 }
147 
timeline_fence_signaled(struct dma_fence * fence)148 static bool timeline_fence_signaled(struct dma_fence *fence)
149 {
150 	struct fence_timeline *parent = dma_fence_parent(fence);
151 	return !__dma_fence_is_later(fence->seqno, parent->value, fence->ops);
152 }
153 
timeline_fence_enable_signaling(struct dma_fence * fence)154 static bool timeline_fence_enable_signaling(struct dma_fence *fence)
155 {
156 	return true;
157 }
158 
timeline_fence_value_str(struct dma_fence * fence,char * str,int size)159 static void timeline_fence_value_str(struct dma_fence *fence,
160 		char *str, int size)
161 {
162 	snprintf(str, size, "%lld", fence->seqno);
163 }
164 
timeline_fence_timeline_value_str(struct dma_fence * fence,char * str,int size)165 static void timeline_fence_timeline_value_str(struct dma_fence *fence,
166 		char *str, int size)
167 {
168 	struct fence_timeline *parent = dma_fence_parent(fence);
169 	snprintf(str, size, "%d", parent->value);
170 }
171 
172 static const struct dma_fence_ops timeline_fence_ops = {
173 	.get_driver_name = timeline_fence_get_driver_name,
174 	.get_timeline_name = timeline_fence_get_timeline_name,
175 	.enable_signaling = timeline_fence_enable_signaling,
176 	.signaled = timeline_fence_signaled,
177 	.release = timeline_fence_release,
178 	.fence_value_str = timeline_fence_value_str,
179 	.timeline_value_str = timeline_fence_timeline_value_str,
180 };
181 
fence_timeline_signal(struct fence_timeline * timeline,unsigned int inc)182 static void fence_timeline_signal(struct fence_timeline *timeline, unsigned int inc)
183 {
184 	struct syncfence *pt, *next;
185 
186 	spin_lock_irq(&timeline->lock);
187 
188 	timeline->value += inc;
189 
190 	list_for_each_entry_safe(pt, next, &timeline->pt_list, link) {
191 		if (!timeline_fence_signaled(&pt->base))
192 			continue;
193 
194 		list_del_init(&pt->link);
195 		dma_fence_signal_locked(&pt->base);
196 	}
197 
198 	spin_unlock_irq(&timeline->lock);
199 }
200 
syncfence_create(struct fence_timeline * timeline,unsigned int value)201 static struct syncfence *syncfence_create(struct fence_timeline *timeline,
202 		unsigned int value)
203 {
204 	struct syncfence *fence;
205 
206 	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
207 	if (!fence)
208 		return NULL;
209 
210 	fence_timeline_get(timeline);
211 	dma_fence_init(&fence->base, &timeline_fence_ops, &timeline->lock,
212 			timeline->context, value);
213 	INIT_LIST_HEAD(&fence->link);
214 
215 	spin_lock_irq(&timeline->lock);
216 	if (!dma_fence_is_signaled_locked(&fence->base)) {
217 		list_add_tail(&fence->link, &timeline->pt_list);
218 	}
219 
220 	spin_unlock_irq(&timeline->lock);
221 
222 	return fence;
223 }
224 
syncfence_open(struct inode * inode,struct file * file)225 static int syncfence_open(struct inode *inode, struct file *file)
226 {
227 	struct fence_timeline *timeline;
228 	char name[64];
229 
230 	sprintf(name, "syncfence-%d", task_pid_nr(current));
231 	timeline = fence_timeline_create(name);
232 	if (!timeline)
233 		return -ENOMEM;
234 
235 	file->private_data = timeline;
236 	return 0;
237 }
238 
syncfence_release(struct inode * inode,struct file * file)239 static int syncfence_release(struct inode *inode, struct file *file)
240 {
241 	struct fence_timeline *timeline = file->private_data;
242 	struct syncfence *fence, *next;
243 
244 	spin_lock_irq(&timeline->lock);
245 
246 	list_for_each_entry_safe(fence, next, &timeline->pt_list, link) {
247 		dma_fence_set_error(&fence->base, -ENOENT);
248 		dma_fence_signal_locked(&fence->base);
249 	}
250 
251 	spin_unlock_irq(&timeline->lock);
252 
253 	fence_timeline_put(timeline);
254 	return 0;
255 }
256 
syncfence_ioctl_create_fence(struct fence_timeline * timeline,unsigned long arg)257 static long syncfence_ioctl_create_fence(struct fence_timeline *timeline,
258 		unsigned long arg)
259 {
260 	int fd = get_unused_fd_flags(O_CLOEXEC);
261 	int err;
262 	struct syncfence *fence;
263 	struct sync_file *sync_file;
264 	struct syncfence_create_data data;
265 
266 	if (fd < 0)
267 		return fd;
268 
269 	if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
270 		err = -EFAULT;
271 		goto err;
272 	}
273 
274 	fence = syncfence_create(timeline, data.value);
275 	if (!fence) {
276 		err = -ENOMEM;
277 		goto err;
278 	}
279 
280 	sync_file = sync_file_create(&fence->base);
281 	dma_fence_put(&fence->base);
282 	if (!sync_file) {
283 		err = -ENOMEM;
284 		goto err;
285 	}
286 
287 	data.fence = fd;
288 	if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
289 		fput(sync_file->file);
290 		err = -EFAULT;
291 		goto err;
292 	}
293 
294 	fd_install(fd, sync_file->file);
295 
296 	return 0;
297 
298 err:
299 	put_unused_fd(fd);
300 	return err;
301 }
302 
syncfence_ioctl_inc(struct fence_timeline * timeline,unsigned long arg)303 static long syncfence_ioctl_inc(struct fence_timeline *timeline, unsigned long arg)
304 {
305 	u32 value;
306 
307 	if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
308 		return -EFAULT;
309 
310 	while (value > INT_MAX)  {
311 		fence_timeline_signal(timeline, INT_MAX);
312 		value -= INT_MAX;
313 	}
314 
315 	fence_timeline_signal(timeline, value);
316 
317 	return 0;
318 }
319 
syncfence_ioctl(struct file * file,unsigned int cmd,unsigned long arg)320 static long syncfence_ioctl(struct file *file, unsigned int cmd,
321 		unsigned long arg)
322 {
323 	struct fence_timeline *timeline = file->private_data;
324 
325 	switch (cmd) {
326 	case SYNCFENCE_IOC_CREATE_FENCE:
327 		return syncfence_ioctl_create_fence(timeline, arg);
328 
329 	case SYNCFENCE_IOC_INC:
330 		return syncfence_ioctl_inc(timeline, arg);
331 
332 	default:
333 		return -ENOTTY;
334 	}
335 }
336 
syncfence_debug_show(struct device * dev,struct device_attribute * attr,char * buf)337 static ssize_t syncfence_debug_show(struct device *dev,
338 		struct device_attribute *attr, char *buf)
339 {
340 	unsigned long flags;
341 	ssize_t printed_count = 0;
342 	struct fence_timeline *timeline, *next_timeline;
343 	struct syncfence *pt, *next;
344 
345 	spin_lock_irqsave(&timeline_list_lock, flags);
346 
347 	list_for_each_entry_safe(timeline, next_timeline, &timeline_list_head, timeline_list) {
348 		printed_count += snprintf(buf+printed_count, PAGE_SIZE-printed_count,
349 				"\n+ timeline: %s [%d]:\n", timeline->name, timeline->value);
350 
351 		spin_lock_irq(&timeline->lock);
352 		list_for_each_entry_safe(pt, next, &timeline->pt_list, link) {
353 			bool signaled = timeline_fence_signaled(&pt->base);
354 
355 			printed_count += snprintf(buf+printed_count, PAGE_SIZE-printed_count,
356 					"\t%lld [%d]\n", pt->base.seqno, signaled);
357 		}
358 		spin_unlock_irq(&timeline->lock);
359 	}
360 
361 	spin_unlock_irqrestore(&timeline_list_lock, flags);
362 	return printed_count;
363 }
364 
365 static DEVICE_ATTR(debug, 0444, syncfence_debug_show, NULL);
366 
367 static struct attribute *syncfence_attrs[] = {
368 	&dev_attr_debug.attr,
369 	NULL
370 };
371 
372 static const struct attribute_group syncfence_attr_group = {
373 	.attrs = syncfence_attrs
374 };
375 
376 static const struct attribute_group *syncfence_attr_groups[] = {
377 	&syncfence_attr_group,
378 	NULL
379 };
380 
381 static struct file_operations syncfence_ops = {
382 	.owner          = THIS_MODULE,
383 	.open           = syncfence_open,
384 	.release        = syncfence_release,
385 	.unlocked_ioctl = syncfence_ioctl,
386 	.compat_ioctl   = syncfence_ioctl,
387 };
388 
389 struct miscdevice syncfence_device = {
390 	.minor  = MISC_DYNAMIC_MINOR,
391 	.name   = "syncfence",
392 	.fops   = &syncfence_ops,
393 	.groups = syncfence_attr_groups,
394 };
395 
syncfence_init(void)396 int syncfence_init(void)
397 {
398 	int result = 0;
399 	result = misc_register(&syncfence_device);
400 	if (result)
401 		pr_err("Error %d adding syncfence", result);
402 	return 0;
403 }
404 
syncfence_exit(void)405 void syncfence_exit(void)
406 {
407 	misc_deregister(&syncfence_device);
408 }
409 
410