1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Media device request objects |
4 | * |
5 | * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
6 | * Copyright (C) 2018 Intel Corporation |
7 | * Copyright (C) 2018 Google, Inc. |
8 | * |
9 | * Author: Hans Verkuil <hans.verkuil@cisco.com> |
10 | * Author: Sakari Ailus <sakari.ailus@linux.intel.com> |
11 | */ |
12 | |
13 | #include <linux/anon_inodes.h> |
14 | #include <linux/file.h> |
15 | #include <linux/refcount.h> |
16 | |
17 | #include <media/media-device.h> |
18 | #include <media/media-request.h> |
19 | |
20 | static const char * const request_state[] = { |
21 | [MEDIA_REQUEST_STATE_IDLE] = "idle" , |
22 | [MEDIA_REQUEST_STATE_VALIDATING] = "validating" , |
23 | [MEDIA_REQUEST_STATE_QUEUED] = "queued" , |
24 | [MEDIA_REQUEST_STATE_COMPLETE] = "complete" , |
25 | [MEDIA_REQUEST_STATE_CLEANING] = "cleaning" , |
26 | [MEDIA_REQUEST_STATE_UPDATING] = "updating" , |
27 | }; |
28 | |
29 | static const char * |
30 | media_request_state_str(enum media_request_state state) |
31 | { |
32 | BUILD_BUG_ON(ARRAY_SIZE(request_state) != NR_OF_MEDIA_REQUEST_STATE); |
33 | |
34 | if (WARN_ON(state >= ARRAY_SIZE(request_state))) |
35 | return "invalid" ; |
36 | return request_state[state]; |
37 | } |
38 | |
39 | static void media_request_clean(struct media_request *req) |
40 | { |
41 | struct media_request_object *obj, *obj_safe; |
42 | |
43 | /* Just a sanity check. No other code path is allowed to change this. */ |
44 | WARN_ON(req->state != MEDIA_REQUEST_STATE_CLEANING); |
45 | WARN_ON(req->updating_count); |
46 | WARN_ON(req->access_count); |
47 | |
48 | list_for_each_entry_safe(obj, obj_safe, &req->objects, list) { |
49 | media_request_object_unbind(obj); |
50 | media_request_object_put(obj); |
51 | } |
52 | |
53 | req->updating_count = 0; |
54 | req->access_count = 0; |
55 | WARN_ON(req->num_incomplete_objects); |
56 | req->num_incomplete_objects = 0; |
57 | wake_up_interruptible_all(&req->poll_wait); |
58 | } |
59 | |
60 | static void media_request_release(struct kref *kref) |
61 | { |
62 | struct media_request *req = |
63 | container_of(kref, struct media_request, kref); |
64 | struct media_device *mdev = req->mdev; |
65 | |
66 | dev_dbg(mdev->dev, "request: release %s\n" , req->debug_str); |
67 | |
68 | /* No other users, no need for a spinlock */ |
69 | req->state = MEDIA_REQUEST_STATE_CLEANING; |
70 | |
71 | media_request_clean(req); |
72 | |
73 | if (mdev->ops->req_free) |
74 | mdev->ops->req_free(req); |
75 | else |
76 | kfree(objp: req); |
77 | } |
78 | |
79 | void media_request_put(struct media_request *req) |
80 | { |
81 | kref_put(kref: &req->kref, release: media_request_release); |
82 | } |
83 | EXPORT_SYMBOL_GPL(media_request_put); |
84 | |
85 | static int media_request_close(struct inode *inode, struct file *filp) |
86 | { |
87 | struct media_request *req = filp->private_data; |
88 | |
89 | media_request_put(req); |
90 | return 0; |
91 | } |
92 | |
93 | static __poll_t media_request_poll(struct file *filp, |
94 | struct poll_table_struct *wait) |
95 | { |
96 | struct media_request *req = filp->private_data; |
97 | unsigned long flags; |
98 | __poll_t ret = 0; |
99 | |
100 | if (!(poll_requested_events(p: wait) & EPOLLPRI)) |
101 | return 0; |
102 | |
103 | poll_wait(filp, wait_address: &req->poll_wait, p: wait); |
104 | spin_lock_irqsave(&req->lock, flags); |
105 | if (req->state == MEDIA_REQUEST_STATE_COMPLETE) { |
106 | ret = EPOLLPRI; |
107 | goto unlock; |
108 | } |
109 | if (req->state != MEDIA_REQUEST_STATE_QUEUED) { |
110 | ret = EPOLLERR; |
111 | goto unlock; |
112 | } |
113 | |
114 | unlock: |
115 | spin_unlock_irqrestore(lock: &req->lock, flags); |
116 | return ret; |
117 | } |
118 | |
119 | static long media_request_ioctl_queue(struct media_request *req) |
120 | { |
121 | struct media_device *mdev = req->mdev; |
122 | enum media_request_state state; |
123 | unsigned long flags; |
124 | int ret; |
125 | |
126 | dev_dbg(mdev->dev, "request: queue %s\n" , req->debug_str); |
127 | |
128 | /* |
129 | * Ensure the request that is validated will be the one that gets queued |
130 | * next by serialising the queueing process. This mutex is also used |
131 | * to serialize with canceling a vb2 queue and with setting values such |
132 | * as controls in a request. |
133 | */ |
134 | mutex_lock(&mdev->req_queue_mutex); |
135 | |
136 | media_request_get(req); |
137 | |
138 | spin_lock_irqsave(&req->lock, flags); |
139 | if (req->state == MEDIA_REQUEST_STATE_IDLE) |
140 | req->state = MEDIA_REQUEST_STATE_VALIDATING; |
141 | state = req->state; |
142 | spin_unlock_irqrestore(lock: &req->lock, flags); |
143 | if (state != MEDIA_REQUEST_STATE_VALIDATING) { |
144 | dev_dbg(mdev->dev, |
145 | "request: unable to queue %s, request in state %s\n" , |
146 | req->debug_str, media_request_state_str(state)); |
147 | media_request_put(req); |
148 | mutex_unlock(lock: &mdev->req_queue_mutex); |
149 | return -EBUSY; |
150 | } |
151 | |
152 | ret = mdev->ops->req_validate(req); |
153 | |
154 | /* |
155 | * If the req_validate was successful, then we mark the state as QUEUED |
156 | * and call req_queue. The reason we set the state first is that this |
157 | * allows req_queue to unbind or complete the queued objects in case |
158 | * they are immediately 'consumed'. State changes from QUEUED to another |
159 | * state can only happen if either the driver changes the state or if |
160 | * the user cancels the vb2 queue. The driver can only change the state |
161 | * after each object is queued through the req_queue op (and note that |
162 | * that op cannot fail), so setting the state to QUEUED up front is |
163 | * safe. |
164 | * |
165 | * The other reason for changing the state is if the vb2 queue is |
166 | * canceled, and that uses the req_queue_mutex which is still locked |
167 | * while req_queue is called, so that's safe as well. |
168 | */ |
169 | spin_lock_irqsave(&req->lock, flags); |
170 | req->state = ret ? MEDIA_REQUEST_STATE_IDLE |
171 | : MEDIA_REQUEST_STATE_QUEUED; |
172 | spin_unlock_irqrestore(lock: &req->lock, flags); |
173 | |
174 | if (!ret) |
175 | mdev->ops->req_queue(req); |
176 | |
177 | mutex_unlock(lock: &mdev->req_queue_mutex); |
178 | |
179 | if (ret) { |
180 | dev_dbg(mdev->dev, "request: can't queue %s (%d)\n" , |
181 | req->debug_str, ret); |
182 | media_request_put(req); |
183 | } |
184 | |
185 | return ret; |
186 | } |
187 | |
188 | static long media_request_ioctl_reinit(struct media_request *req) |
189 | { |
190 | struct media_device *mdev = req->mdev; |
191 | unsigned long flags; |
192 | |
193 | spin_lock_irqsave(&req->lock, flags); |
194 | if (req->state != MEDIA_REQUEST_STATE_IDLE && |
195 | req->state != MEDIA_REQUEST_STATE_COMPLETE) { |
196 | dev_dbg(mdev->dev, |
197 | "request: %s not in idle or complete state, cannot reinit\n" , |
198 | req->debug_str); |
199 | spin_unlock_irqrestore(lock: &req->lock, flags); |
200 | return -EBUSY; |
201 | } |
202 | if (req->access_count) { |
203 | dev_dbg(mdev->dev, |
204 | "request: %s is being accessed, cannot reinit\n" , |
205 | req->debug_str); |
206 | spin_unlock_irqrestore(lock: &req->lock, flags); |
207 | return -EBUSY; |
208 | } |
209 | req->state = MEDIA_REQUEST_STATE_CLEANING; |
210 | spin_unlock_irqrestore(lock: &req->lock, flags); |
211 | |
212 | media_request_clean(req); |
213 | |
214 | spin_lock_irqsave(&req->lock, flags); |
215 | req->state = MEDIA_REQUEST_STATE_IDLE; |
216 | spin_unlock_irqrestore(lock: &req->lock, flags); |
217 | |
218 | return 0; |
219 | } |
220 | |
221 | static long media_request_ioctl(struct file *filp, unsigned int cmd, |
222 | unsigned long arg) |
223 | { |
224 | struct media_request *req = filp->private_data; |
225 | |
226 | switch (cmd) { |
227 | case MEDIA_REQUEST_IOC_QUEUE: |
228 | return media_request_ioctl_queue(req); |
229 | case MEDIA_REQUEST_IOC_REINIT: |
230 | return media_request_ioctl_reinit(req); |
231 | default: |
232 | return -ENOIOCTLCMD; |
233 | } |
234 | } |
235 | |
236 | static const struct file_operations request_fops = { |
237 | .owner = THIS_MODULE, |
238 | .poll = media_request_poll, |
239 | .unlocked_ioctl = media_request_ioctl, |
240 | #ifdef CONFIG_COMPAT |
241 | .compat_ioctl = media_request_ioctl, |
242 | #endif /* CONFIG_COMPAT */ |
243 | .release = media_request_close, |
244 | }; |
245 | |
246 | struct media_request * |
247 | media_request_get_by_fd(struct media_device *mdev, int request_fd) |
248 | { |
249 | struct fd f; |
250 | struct media_request *req; |
251 | |
252 | if (!mdev || !mdev->ops || |
253 | !mdev->ops->req_validate || !mdev->ops->req_queue) |
254 | return ERR_PTR(error: -EBADR); |
255 | |
256 | f = fdget(fd: request_fd); |
257 | if (!f.file) |
258 | goto err_no_req_fd; |
259 | |
260 | if (f.file->f_op != &request_fops) |
261 | goto err_fput; |
262 | req = f.file->private_data; |
263 | if (req->mdev != mdev) |
264 | goto err_fput; |
265 | |
266 | /* |
267 | * Note: as long as someone has an open filehandle of the request, |
268 | * the request can never be released. The fdget() above ensures that |
269 | * even if userspace closes the request filehandle, the release() |
270 | * fop won't be called, so the media_request_get() always succeeds |
271 | * and there is no race condition where the request was released |
272 | * before media_request_get() is called. |
273 | */ |
274 | media_request_get(req); |
275 | fdput(fd: f); |
276 | |
277 | return req; |
278 | |
279 | err_fput: |
280 | fdput(fd: f); |
281 | |
282 | err_no_req_fd: |
283 | dev_dbg(mdev->dev, "cannot find request_fd %d\n" , request_fd); |
284 | return ERR_PTR(error: -EINVAL); |
285 | } |
286 | EXPORT_SYMBOL_GPL(media_request_get_by_fd); |
287 | |
288 | int media_request_alloc(struct media_device *mdev, int *alloc_fd) |
289 | { |
290 | struct media_request *req; |
291 | struct file *filp; |
292 | int fd; |
293 | int ret; |
294 | |
295 | /* Either both are NULL or both are non-NULL */ |
296 | if (WARN_ON(!mdev->ops->req_alloc ^ !mdev->ops->req_free)) |
297 | return -ENOMEM; |
298 | |
299 | if (mdev->ops->req_alloc) |
300 | req = mdev->ops->req_alloc(mdev); |
301 | else |
302 | req = kzalloc(size: sizeof(*req), GFP_KERNEL); |
303 | if (!req) |
304 | return -ENOMEM; |
305 | |
306 | fd = get_unused_fd_flags(O_CLOEXEC); |
307 | if (fd < 0) { |
308 | ret = fd; |
309 | goto err_free_req; |
310 | } |
311 | |
312 | filp = anon_inode_getfile(name: "request" , fops: &request_fops, NULL, O_CLOEXEC); |
313 | if (IS_ERR(ptr: filp)) { |
314 | ret = PTR_ERR(ptr: filp); |
315 | goto err_put_fd; |
316 | } |
317 | |
318 | filp->private_data = req; |
319 | req->mdev = mdev; |
320 | req->state = MEDIA_REQUEST_STATE_IDLE; |
321 | req->num_incomplete_objects = 0; |
322 | kref_init(kref: &req->kref); |
323 | INIT_LIST_HEAD(list: &req->objects); |
324 | spin_lock_init(&req->lock); |
325 | init_waitqueue_head(&req->poll_wait); |
326 | req->updating_count = 0; |
327 | req->access_count = 0; |
328 | |
329 | *alloc_fd = fd; |
330 | |
331 | snprintf(buf: req->debug_str, size: sizeof(req->debug_str), fmt: "%u:%d" , |
332 | atomic_inc_return(v: &mdev->request_id), fd); |
333 | dev_dbg(mdev->dev, "request: allocated %s\n" , req->debug_str); |
334 | |
335 | fd_install(fd, file: filp); |
336 | |
337 | return 0; |
338 | |
339 | err_put_fd: |
340 | put_unused_fd(fd); |
341 | |
342 | err_free_req: |
343 | if (mdev->ops->req_free) |
344 | mdev->ops->req_free(req); |
345 | else |
346 | kfree(objp: req); |
347 | |
348 | return ret; |
349 | } |
350 | |
351 | static void media_request_object_release(struct kref *kref) |
352 | { |
353 | struct media_request_object *obj = |
354 | container_of(kref, struct media_request_object, kref); |
355 | struct media_request *req = obj->req; |
356 | |
357 | if (WARN_ON(req)) |
358 | media_request_object_unbind(obj); |
359 | obj->ops->release(obj); |
360 | } |
361 | |
362 | struct media_request_object * |
363 | media_request_object_find(struct media_request *req, |
364 | const struct media_request_object_ops *ops, |
365 | void *priv) |
366 | { |
367 | struct media_request_object *obj; |
368 | struct media_request_object *found = NULL; |
369 | unsigned long flags; |
370 | |
371 | if (WARN_ON(!ops || !priv)) |
372 | return NULL; |
373 | |
374 | spin_lock_irqsave(&req->lock, flags); |
375 | list_for_each_entry(obj, &req->objects, list) { |
376 | if (obj->ops == ops && obj->priv == priv) { |
377 | media_request_object_get(obj); |
378 | found = obj; |
379 | break; |
380 | } |
381 | } |
382 | spin_unlock_irqrestore(lock: &req->lock, flags); |
383 | return found; |
384 | } |
385 | EXPORT_SYMBOL_GPL(media_request_object_find); |
386 | |
387 | void media_request_object_put(struct media_request_object *obj) |
388 | { |
389 | kref_put(kref: &obj->kref, release: media_request_object_release); |
390 | } |
391 | EXPORT_SYMBOL_GPL(media_request_object_put); |
392 | |
393 | void media_request_object_init(struct media_request_object *obj) |
394 | { |
395 | obj->ops = NULL; |
396 | obj->req = NULL; |
397 | obj->priv = NULL; |
398 | obj->completed = false; |
399 | INIT_LIST_HEAD(list: &obj->list); |
400 | kref_init(kref: &obj->kref); |
401 | } |
402 | EXPORT_SYMBOL_GPL(media_request_object_init); |
403 | |
404 | int media_request_object_bind(struct media_request *req, |
405 | const struct media_request_object_ops *ops, |
406 | void *priv, bool is_buffer, |
407 | struct media_request_object *obj) |
408 | { |
409 | unsigned long flags; |
410 | int ret = -EBUSY; |
411 | |
412 | if (WARN_ON(!ops->release)) |
413 | return -EBADR; |
414 | |
415 | spin_lock_irqsave(&req->lock, flags); |
416 | |
417 | if (WARN_ON(req->state != MEDIA_REQUEST_STATE_UPDATING && |
418 | req->state != MEDIA_REQUEST_STATE_QUEUED)) |
419 | goto unlock; |
420 | |
421 | obj->req = req; |
422 | obj->ops = ops; |
423 | obj->priv = priv; |
424 | |
425 | if (is_buffer) |
426 | list_add_tail(new: &obj->list, head: &req->objects); |
427 | else |
428 | list_add(new: &obj->list, head: &req->objects); |
429 | req->num_incomplete_objects++; |
430 | ret = 0; |
431 | |
432 | unlock: |
433 | spin_unlock_irqrestore(lock: &req->lock, flags); |
434 | return ret; |
435 | } |
436 | EXPORT_SYMBOL_GPL(media_request_object_bind); |
437 | |
438 | void media_request_object_unbind(struct media_request_object *obj) |
439 | { |
440 | struct media_request *req = obj->req; |
441 | unsigned long flags; |
442 | bool completed = false; |
443 | |
444 | if (WARN_ON(!req)) |
445 | return; |
446 | |
447 | spin_lock_irqsave(&req->lock, flags); |
448 | list_del(entry: &obj->list); |
449 | obj->req = NULL; |
450 | |
451 | if (req->state == MEDIA_REQUEST_STATE_COMPLETE) |
452 | goto unlock; |
453 | |
454 | if (WARN_ON(req->state == MEDIA_REQUEST_STATE_VALIDATING)) |
455 | goto unlock; |
456 | |
457 | if (req->state == MEDIA_REQUEST_STATE_CLEANING) { |
458 | if (!obj->completed) |
459 | req->num_incomplete_objects--; |
460 | goto unlock; |
461 | } |
462 | |
463 | if (WARN_ON(!req->num_incomplete_objects)) |
464 | goto unlock; |
465 | |
466 | req->num_incomplete_objects--; |
467 | if (req->state == MEDIA_REQUEST_STATE_QUEUED && |
468 | !req->num_incomplete_objects) { |
469 | req->state = MEDIA_REQUEST_STATE_COMPLETE; |
470 | completed = true; |
471 | wake_up_interruptible_all(&req->poll_wait); |
472 | } |
473 | |
474 | unlock: |
475 | spin_unlock_irqrestore(lock: &req->lock, flags); |
476 | if (obj->ops->unbind) |
477 | obj->ops->unbind(obj); |
478 | if (completed) |
479 | media_request_put(req); |
480 | } |
481 | EXPORT_SYMBOL_GPL(media_request_object_unbind); |
482 | |
483 | void media_request_object_complete(struct media_request_object *obj) |
484 | { |
485 | struct media_request *req = obj->req; |
486 | unsigned long flags; |
487 | bool completed = false; |
488 | |
489 | spin_lock_irqsave(&req->lock, flags); |
490 | if (obj->completed) |
491 | goto unlock; |
492 | obj->completed = true; |
493 | if (WARN_ON(!req->num_incomplete_objects) || |
494 | WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED)) |
495 | goto unlock; |
496 | |
497 | if (!--req->num_incomplete_objects) { |
498 | req->state = MEDIA_REQUEST_STATE_COMPLETE; |
499 | wake_up_interruptible_all(&req->poll_wait); |
500 | completed = true; |
501 | } |
502 | unlock: |
503 | spin_unlock_irqrestore(lock: &req->lock, flags); |
504 | if (completed) |
505 | media_request_put(req); |
506 | } |
507 | EXPORT_SYMBOL_GPL(media_request_object_complete); |
508 | |