1 // SPDX-License-Identifier: MIT
2 
3 /*
4  * Copyright © 2019 Intel Corporation
5  */
6 
7 #include "i915_sw_fence_work.h"
8 
9 static void fence_work(struct work_struct *work)
10 {
11 	struct dma_fence_work *f = container_of(work, typeof(*f), work);
12 	int err;
13 
14 	err = f->ops->work(f);
15 	if (err)
16 		dma_fence_set_error(&f->dma, err);
17 	dma_fence_signal(&f->dma);
18 	dma_fence_put(&f->dma);
19 }
20 
21 static int __i915_sw_fence_call
22 fence_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
23 {
24 	struct dma_fence_work *f = container_of(fence, typeof(*f), chain);
25 
26 	switch (state) {
27 	case FENCE_COMPLETE:
28 		if (fence->error)
29 			dma_fence_set_error(&f->dma, fence->error);
30 
31 		if (!f->dma.error) {
32 			dma_fence_get(&f->dma);
33 			queue_work(system_unbound_wq, &f->work);
34 		} else {
35 			dma_fence_signal(&f->dma);
36 		}
37 		break;
38 
39 	case FENCE_FREE:
40 		dma_fence_put(&f->dma);
41 		break;
42 	}
43 
44 	return NOTIFY_DONE;
45 }
46 
47 static const char *get_driver_name(struct dma_fence *fence)
48 {
49 	return "dma-fence";
50 }
51 
52 static const char *get_timeline_name(struct dma_fence *fence)
53 {
54 	struct dma_fence_work *f = container_of(fence, typeof(*f), dma);
55 
56 	return f->ops->name ?: "work";
57 }
58 
59 static void fence_release(struct dma_fence *fence)
60 {
61 	struct dma_fence_work *f = container_of(fence, typeof(*f), dma);
62 
63 	if (f->ops->release)
64 		f->ops->release(f);
65 
66 	i915_sw_fence_fini(&f->chain);
67 
68 	BUILD_BUG_ON(offsetof(typeof(*f), dma));
69 	dma_fence_free(&f->dma);
70 }
71 
72 static const struct dma_fence_ops fence_ops = {
73 	.get_driver_name = get_driver_name,
74 	.get_timeline_name = get_timeline_name,
75 	.release = fence_release,
76 };
77 
78 void dma_fence_work_init(struct dma_fence_work *f,
79 			 const struct dma_fence_work_ops *ops)
80 {
81 	spin_lock_init(&f->lock);
82 	dma_fence_init(&f->dma, &fence_ops, &f->lock, 0, 0);
83 	i915_sw_fence_init(&f->chain, fence_notify);
84 	INIT_WORK(&f->work, fence_work);
85 
86 	f->ops = ops;
87 }
88 
89 int dma_fence_work_chain(struct dma_fence_work *f, struct dma_fence *signal)
90 {
91 	if (!signal)
92 		return 0;
93 
94 	return __i915_sw_fence_await_dma_fence(&f->chain, signal, &f->cb);
95 }
96