Blame view

drivers/dma-buf/sw_sync.c 8.87 KB
1867a23b1   Gustavo Padovan   staging/android: ...
1
  /*
e912c881f   Gustavo Padovan   staging/android: ...
2
   * Sync File validation framework
1867a23b1   Gustavo Padovan   staging/android: ...
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   *
   * Copyright (C) 2012 Google, Inc.
   *
   * This software is licensed under the terms of the GNU General Public
   * License version 2, as published by the Free Software Foundation, and
   * may be copied, distributed, and modified under those terms.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   */
  
  #include <linux/file.h>
  #include <linux/fs.h>
  #include <linux/uaccess.h>
aff9da10e   Gustavo Padovan   staging/android: ...
20
  #include <linux/slab.h>
1867a23b1   Gustavo Padovan   staging/android: ...
21
  #include <linux/sync_file.h>
1fe82e2e1   Gustavo Padovan   staging/android: ...
22
  #include "sync_debug.h"
1867a23b1   Gustavo Padovan   staging/android: ...
23

aff9da10e   Gustavo Padovan   staging/android: ...
24
  #define CREATE_TRACE_POINTS
a04f915eb   Gustavo Padovan   staging/android: ...
25
  #include "sync_trace.h"
aff9da10e   Gustavo Padovan   staging/android: ...
26

fc0c9a03b   Gustavo Padovan   staging/android: ...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
  /*
   * SW SYNC validation framework
   *
   * A sync object driver that uses a 32bit counter to coordinate
   * synchronization.  Useful when there is no hardware primitive backing
   * the synchronization.
   *
   * To start the framework just open:
   *
   * <debugfs>/sync/sw_sync
   *
   * That will create a sync timeline, all fences created under this timeline
   * file descriptor will belong to the this timeline.
   *
   * The 'sw_sync' file can be opened many times as to create different
   * timelines.
   *
   * Fences can be created with SW_SYNC_IOC_CREATE_FENCE ioctl with struct
   * sw_sync_ioctl_create_fence as parameter.
   *
   * To increment the timeline counter, SW_SYNC_IOC_INC ioctl should be used
   * with the increment as u32. This will update the last signaled value
   * from the timeline and signal any fence that has a seqno smaller or equal
   * to it.
   *
   * struct sw_sync_ioctl_create_fence
   * @value:	the seqno to initialise the fence with
   * @name:	the name of the new sync point
   * @fence:	return the fd of the new sync_file with the created fence
   */
6f65aa892   Gustavo Padovan   staging/android: ...
57
58
59
60
61
62
63
64
65
66
  struct sw_sync_create_fence_data {
  	__u32	value;
  	char	name[32];
  	__s32	fence; /* fd of new fence */
  };
  
  #define SW_SYNC_IOC_MAGIC	'W'
  
  #define SW_SYNC_IOC_CREATE_FENCE	_IOWR(SW_SYNC_IOC_MAGIC, 0,\
  		struct sw_sync_create_fence_data)
fc0c9a03b   Gustavo Padovan   staging/android: ...
67

6f65aa892   Gustavo Padovan   staging/android: ...
68
  #define SW_SYNC_IOC_INC			_IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
aff9da10e   Gustavo Padovan   staging/android: ...
69
70
71
72
73
74
75
76
77
78
79
  static const struct fence_ops timeline_fence_ops;
  
  static inline struct sync_pt *fence_to_sync_pt(struct fence *fence)
  {
  	if (fence->ops != &timeline_fence_ops)
  		return NULL;
  	return container_of(fence, struct sync_pt, base);
  }
  
  /**
   * sync_timeline_create() - creates a sync object
aff9da10e   Gustavo Padovan   staging/android: ...
80
81
82
83
84
   * @name:	sync_timeline name
   *
   * Creates a new sync_timeline. Returns the sync_timeline object or NULL in
   * case of error.
   */
b9bc2b7b6   Gustavo Padovan   staging/android: ...
85
  struct sync_timeline *sync_timeline_create(const char *name)
aff9da10e   Gustavo Padovan   staging/android: ...
86
87
88
89
90
91
92
93
94
95
  {
  	struct sync_timeline *obj;
  
  	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
  	if (!obj)
  		return NULL;
  
  	kref_init(&obj->kref);
  	obj->context = fence_context_alloc(1);
  	strlcpy(obj->name, name, sizeof(obj->name));
aff9da10e   Gustavo Padovan   staging/android: ...
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  
  	INIT_LIST_HEAD(&obj->child_list_head);
  	INIT_LIST_HEAD(&obj->active_list_head);
  	spin_lock_init(&obj->child_list_lock);
  
  	sync_timeline_debug_add(obj);
  
  	return obj;
  }
  
  static void sync_timeline_free(struct kref *kref)
  {
  	struct sync_timeline *obj =
  		container_of(kref, struct sync_timeline, kref);
  
  	sync_timeline_debug_remove(obj);
  
  	kfree(obj);
  }
  
  static void sync_timeline_get(struct sync_timeline *obj)
  {
  	kref_get(&obj->kref);
  }
  
  static void sync_timeline_put(struct sync_timeline *obj)
  {
  	kref_put(&obj->kref, sync_timeline_free);
  }
  
  /**
aff9da10e   Gustavo Padovan   staging/android: ...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
   * sync_timeline_signal() - signal a status change on a sync_timeline
   * @obj:	sync_timeline to signal
   * @inc:	num to increment on timeline->value
   *
   * A sync implementation should call this any time one of it's fences
   * has signaled or has an error condition.
   */
  static void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc)
  {
  	unsigned long flags;
  	struct sync_pt *pt, *next;
  
  	trace_sync_timeline(obj);
  
  	spin_lock_irqsave(&obj->child_list_lock, flags);
  
  	obj->value += inc;
  
  	list_for_each_entry_safe(pt, next, &obj->active_list_head,
  				 active_list) {
  		if (fence_is_signaled_locked(&pt->base))
  			list_del_init(&pt->active_list);
  	}
  
  	spin_unlock_irqrestore(&obj->child_list_lock, flags);
  }
  
  /**
   * sync_pt_create() - creates a sync pt
   * @parent:	fence's parent sync_timeline
   * @size:	size to allocate for this pt
   * @inc:	value of the fence
   *
   * Creates a new sync_pt as a child of @parent.  @size bytes will be
   * allocated allowing for implementation specific data to be kept after
   * the generic sync_timeline struct. Returns the sync_pt object or
   * NULL in case of error.
   */
  static struct sync_pt *sync_pt_create(struct sync_timeline *obj, int size,
  			     unsigned int value)
  {
  	unsigned long flags;
  	struct sync_pt *pt;
  
  	if (size < sizeof(*pt))
  		return NULL;
  
  	pt = kzalloc(size, GFP_KERNEL);
  	if (!pt)
  		return NULL;
  
  	spin_lock_irqsave(&obj->child_list_lock, flags);
  	sync_timeline_get(obj);
  	fence_init(&pt->base, &timeline_fence_ops, &obj->child_list_lock,
  		   obj->context, value);
  	list_add_tail(&pt->child_list, &obj->child_list_head);
  	INIT_LIST_HEAD(&pt->active_list);
  	spin_unlock_irqrestore(&obj->child_list_lock, flags);
  	return pt;
  }
  
  static const char *timeline_fence_get_driver_name(struct fence *fence)
  {
b9bc2b7b6   Gustavo Padovan   staging/android: ...
190
  	return "sw_sync";
aff9da10e   Gustavo Padovan   staging/android: ...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  }
  
  static const char *timeline_fence_get_timeline_name(struct fence *fence)
  {
  	struct sync_timeline *parent = fence_parent(fence);
  
  	return parent->name;
  }
  
  static void timeline_fence_release(struct fence *fence)
  {
  	struct sync_pt *pt = fence_to_sync_pt(fence);
  	struct sync_timeline *parent = fence_parent(fence);
  	unsigned long flags;
  
  	spin_lock_irqsave(fence->lock, flags);
  	list_del(&pt->child_list);
a4ebee657   Gustavo Padovan   staging/android: ...
208
  	if (!list_empty(&pt->active_list))
aff9da10e   Gustavo Padovan   staging/android: ...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  		list_del(&pt->active_list);
  	spin_unlock_irqrestore(fence->lock, flags);
  
  	sync_timeline_put(parent);
  	fence_free(fence);
  }
  
  static bool timeline_fence_signaled(struct fence *fence)
  {
  	struct sync_timeline *parent = fence_parent(fence);
  
  	return (fence->seqno > parent->value) ? false : true;
  }
  
  static bool timeline_fence_enable_signaling(struct fence *fence)
  {
  	struct sync_pt *pt = fence_to_sync_pt(fence);
  	struct sync_timeline *parent = fence_parent(fence);
  
  	if (timeline_fence_signaled(fence))
  		return false;
  
  	list_add_tail(&pt->active_list, &parent->active_list_head);
  	return true;
  }
  
  static void timeline_fence_value_str(struct fence *fence,
  				    char *str, int size)
  {
  	snprintf(str, size, "%d", fence->seqno);
  }
  
  static void timeline_fence_timeline_value_str(struct fence *fence,
  					     char *str, int size)
  {
  	struct sync_timeline *parent = fence_parent(fence);
  
  	snprintf(str, size, "%d", parent->value);
  }
  
  static const struct fence_ops timeline_fence_ops = {
  	.get_driver_name = timeline_fence_get_driver_name,
  	.get_timeline_name = timeline_fence_get_timeline_name,
  	.enable_signaling = timeline_fence_enable_signaling,
  	.signaled = timeline_fence_signaled,
  	.wait = fence_default_wait,
  	.release = timeline_fence_release,
  	.fence_value_str = timeline_fence_value_str,
  	.timeline_value_str = timeline_fence_timeline_value_str,
  };
1867a23b1   Gustavo Padovan   staging/android: ...
259
260
261
262
263
264
265
266
267
268
269
270
271
  /*
   * *WARNING*
   *
   * improper use of this can result in deadlocking kernel drivers from userspace.
   */
  
  /* opening sw_sync create a new sync obj */
  static int sw_sync_debugfs_open(struct inode *inode, struct file *file)
  {
  	struct sync_timeline *obj;
  	char task_comm[TASK_COMM_LEN];
  
  	get_task_comm(task_comm, current);
b9bc2b7b6   Gustavo Padovan   staging/android: ...
272
  	obj = sync_timeline_create(task_comm);
1867a23b1   Gustavo Padovan   staging/android: ...
273
274
275
276
277
278
279
280
281
282
283
  	if (!obj)
  		return -ENOMEM;
  
  	file->private_data = obj;
  
  	return 0;
  }
  
  static int sw_sync_debugfs_release(struct inode *inode, struct file *file)
  {
  	struct sync_timeline *obj = file->private_data;
711102325   Gustavo Padovan   staging/android: ...
284
285
286
  	smp_wmb();
  
  	sync_timeline_put(obj);
1867a23b1   Gustavo Padovan   staging/android: ...
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
  	return 0;
  }
  
  static long sw_sync_ioctl_create_fence(struct sync_timeline *obj,
  				       unsigned long arg)
  {
  	int fd = get_unused_fd_flags(O_CLOEXEC);
  	int err;
  	struct sync_pt *pt;
  	struct sync_file *sync_file;
  	struct sw_sync_create_fence_data data;
  
  	if (fd < 0)
  		return fd;
  
  	if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
  		err = -EFAULT;
  		goto err;
  	}
  
  	pt = sync_pt_create(obj, sizeof(*pt), data.value);
  	if (!pt) {
  		err = -ENOMEM;
  		goto err;
  	}
  
  	sync_file = sync_file_create(&pt->base);
  	if (!sync_file) {
  		fence_put(&pt->base);
  		err = -ENOMEM;
  		goto err;
  	}
  
  	data.fence = fd;
  	if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
  		fput(sync_file->file);
  		err = -EFAULT;
  		goto err;
  	}
  
  	fd_install(fd, sync_file->file);
  
  	return 0;
  
  err:
  	put_unused_fd(fd);
  	return err;
  }
  
  static long sw_sync_ioctl_inc(struct sync_timeline *obj, unsigned long arg)
  {
  	u32 value;
  
  	if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
  		return -EFAULT;
  
  	sync_timeline_signal(obj, value);
  
  	return 0;
  }
  
  static long sw_sync_ioctl(struct file *file, unsigned int cmd,
  			  unsigned long arg)
  {
  	struct sync_timeline *obj = file->private_data;
  
  	switch (cmd) {
  	case SW_SYNC_IOC_CREATE_FENCE:
  		return sw_sync_ioctl_create_fence(obj, arg);
  
  	case SW_SYNC_IOC_INC:
  		return sw_sync_ioctl_inc(obj, arg);
  
  	default:
  		return -ENOTTY;
  	}
  }
  
  const struct file_operations sw_sync_debugfs_fops = {
  	.open           = sw_sync_debugfs_open,
  	.release        = sw_sync_debugfs_release,
  	.unlocked_ioctl = sw_sync_ioctl,
  	.compat_ioctl	= sw_sync_ioctl,
  };