1 /*
2  *
3  * (C) COPYRIGHT 2011-2017 ARM Limited. All rights reserved.
4  *
5  * This program is free software and is provided to you under the terms of the
6  * GNU General Public License version 2 as published by the Free Software
7  * Foundation, and any use by you of this program is subject to the terms
8  * of such GNU licence.
9  *
10  * A copy of the licence is included with the program, and can also be obtained
11  * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
12  * Boston, MA  02110-1301, USA.
13  *
14  */
15 
16 
17 
18 #include <linux/atomic.h>
19 #include <linux/list.h>
20 #include <linux/spinlock.h>
21 #include <mali_kbase_fence_defs.h>
22 #include <mali_kbase_fence.h>
23 #include <mali_kbase.h>
24 
25 /* Spin lock protecting all Mali fences as fence->lock. */
26 static DEFINE_SPINLOCK(kbase_fence_lock);
27 
28 static const char *
29 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0))
kbase_fence_get_driver_name(struct fence *fence)30 kbase_fence_get_driver_name(struct fence *fence)
31 #else
32 kbase_fence_get_driver_name(struct dma_fence *fence)
33 #endif
34 {
35 	return kbase_drv_name;
36 }
37 
38 static const char *
39 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0))
kbase_fence_get_timeline_name(struct fence *fence)40 kbase_fence_get_timeline_name(struct fence *fence)
41 #else
42 kbase_fence_get_timeline_name(struct dma_fence *fence)
43 #endif
44 {
45 	return kbase_timeline_name;
46 }
47 
48 static bool
49 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0))
kbase_fence_enable_signaling(struct fence *fence)50 kbase_fence_enable_signaling(struct fence *fence)
51 #else
52 kbase_fence_enable_signaling(struct dma_fence *fence)
53 #endif
54 {
55 	return true;
56 }
57 
58 static void
59 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0))
kbase_fence_fence_value_str(struct fence *fence, char *str, int size)60 kbase_fence_fence_value_str(struct fence *fence, char *str, int size)
61 #else
62 kbase_fence_fence_value_str(struct dma_fence *fence, char *str, int size)
63 #endif
64 {
65 #if (KERNEL_VERSION(5, 1, 0) > LINUX_VERSION_CODE)
66 	snprintf(str, size, "%u", fence->seqno);
67 #else
68 	snprintf(str, size, "%llu", fence->seqno);
69 #endif
70 }
71 
72 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0))
73 const struct fence_ops kbase_fence_ops = {
74 	.wait = fence_default_wait,
75 #else
76 const struct dma_fence_ops kbase_fence_ops = {
77 	.wait = dma_fence_default_wait,
78 #endif
79 	.get_driver_name = kbase_fence_get_driver_name,
80 	.get_timeline_name = kbase_fence_get_timeline_name,
81 	.enable_signaling = kbase_fence_enable_signaling,
82 	.fence_value_str = kbase_fence_fence_value_str
83 };
84 
85 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0))
86 struct fence *
kbase_fence_out_new(struct kbase_jd_atom *katom)87 kbase_fence_out_new(struct kbase_jd_atom *katom)
88 #else
89 struct dma_fence *
90 kbase_fence_out_new(struct kbase_jd_atom *katom)
91 #endif
92 {
93 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0))
94 	struct fence *fence;
95 #else
96 	struct dma_fence *fence;
97 #endif
98 
99 	WARN_ON(katom->dma_fence.fence);
100 
101 	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
102 	if (!fence)
103 		return NULL;
104 
105 	dma_fence_init(fence,
106 		       &kbase_fence_ops,
107 		       &kbase_fence_lock,
108 		       katom->dma_fence.context,
109 		       atomic_inc_return(&katom->dma_fence.seqno));
110 
111 	katom->dma_fence.fence = fence;
112 
113 	return fence;
114 }
115 
116 bool
kbase_fence_free_callbacks(struct kbase_jd_atom *katom)117 kbase_fence_free_callbacks(struct kbase_jd_atom *katom)
118 {
119 	struct kbase_fence_cb *cb, *tmp;
120 	bool res = false;
121 
122 	lockdep_assert_held(&katom->kctx->jctx.lock);
123 
124 	/* Clean up and free callbacks. */
125 	list_for_each_entry_safe(cb, tmp, &katom->dma_fence.callbacks, node) {
126 		bool ret;
127 
128 		/* Cancel callbacks that hasn't been called yet. */
129 		ret = dma_fence_remove_callback(cb->fence, &cb->fence_cb);
130 		if (ret) {
131 			int ret;
132 
133 			/* Fence had not signaled, clean up after
134 			 * canceling.
135 			 */
136 			ret = atomic_dec_return(&katom->dma_fence.dep_count);
137 
138 			if (unlikely(ret == 0))
139 				res = true;
140 		}
141 
142 		/*
143 		 * Release the reference taken in
144 		 * kbase_fence_add_callback().
145 		 */
146 		dma_fence_put(cb->fence);
147 		list_del(&cb->node);
148 		kfree(cb);
149 	}
150 
151 	return res;
152 }
153 
154 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0))
155 int
kbase_fence_add_callback(struct kbase_jd_atom *katom, struct fence *fence, fence_func_t callback)156 kbase_fence_add_callback(struct kbase_jd_atom *katom,
157 			 struct fence *fence,
158 			 fence_func_t callback)
159 #else
160 int
161 kbase_fence_add_callback(struct kbase_jd_atom *katom,
162 			 struct dma_fence *fence,
163 			 dma_fence_func_t callback)
164 #endif
165 {
166 	int err = 0;
167 	struct kbase_fence_cb *kbase_fence_cb;
168 
169 	if (!fence)
170 		return -EINVAL;
171 
172 	kbase_fence_cb = kmalloc(sizeof(*kbase_fence_cb), GFP_KERNEL);
173 	if (!kbase_fence_cb)
174 		return -ENOMEM;
175 
176 	kbase_fence_cb->fence = fence;
177 	kbase_fence_cb->katom = katom;
178 	INIT_LIST_HEAD(&kbase_fence_cb->node);
179 
180 	err = dma_fence_add_callback(fence, &kbase_fence_cb->fence_cb,
181 				     callback);
182 	if (err == -ENOENT) {
183 		/* Fence signaled, clear the error and return */
184 		err = 0;
185 		kfree(kbase_fence_cb);
186 	} else if (err) {
187 		kfree(kbase_fence_cb);
188 	} else {
189 		/*
190 		 * Get reference to fence that will be kept until callback gets
191 		 * cleaned up in kbase_fence_free_callbacks().
192 		 */
193 		dma_fence_get(fence);
194 		atomic_inc(&katom->dma_fence.dep_count);
195 		/* Add callback to katom's list of callbacks */
196 		list_add(&kbase_fence_cb->node, &katom->dma_fence.callbacks);
197 	}
198 
199 	return err;
200 }
201