1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright 2015 Advanced Micro Devices, Inc.
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Permission is hereby granted, free of charge, to any person obtaining a
5*4882a593Smuzhiyun * copy of this software and associated documentation files (the "Software"),
6*4882a593Smuzhiyun * to deal in the Software without restriction, including without limitation
7*4882a593Smuzhiyun * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*4882a593Smuzhiyun * and/or sell copies of the Software, and to permit persons to whom the
9*4882a593Smuzhiyun * Software is furnished to do so, subject to the following conditions:
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * The above copyright notice and this permission notice shall be included in
12*4882a593Smuzhiyun * all copies or substantial portions of the Software.
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15*4882a593Smuzhiyun * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*4882a593Smuzhiyun * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17*4882a593Smuzhiyun * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18*4882a593Smuzhiyun * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19*4882a593Smuzhiyun * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20*4882a593Smuzhiyun * OTHER DEALINGS IN THE SOFTWARE.
21*4882a593Smuzhiyun *
22*4882a593Smuzhiyun */
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #include <linux/kthread.h>
25*4882a593Smuzhiyun #include <linux/module.h>
26*4882a593Smuzhiyun #include <linux/sched.h>
27*4882a593Smuzhiyun #include <linux/slab.h>
28*4882a593Smuzhiyun #include <linux/wait.h>
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #include <drm/gpu_scheduler.h>
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun static struct kmem_cache *sched_fence_slab;
33*4882a593Smuzhiyun
drm_sched_fence_slab_init(void)34*4882a593Smuzhiyun static int __init drm_sched_fence_slab_init(void)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun sched_fence_slab = kmem_cache_create(
37*4882a593Smuzhiyun "drm_sched_fence", sizeof(struct drm_sched_fence), 0,
38*4882a593Smuzhiyun SLAB_HWCACHE_ALIGN, NULL);
39*4882a593Smuzhiyun if (!sched_fence_slab)
40*4882a593Smuzhiyun return -ENOMEM;
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun return 0;
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
drm_sched_fence_slab_fini(void)45*4882a593Smuzhiyun static void __exit drm_sched_fence_slab_fini(void)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun rcu_barrier();
48*4882a593Smuzhiyun kmem_cache_destroy(sched_fence_slab);
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun
drm_sched_fence_scheduled(struct drm_sched_fence * fence)51*4882a593Smuzhiyun void drm_sched_fence_scheduled(struct drm_sched_fence *fence)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun int ret = dma_fence_signal(&fence->scheduled);
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun if (!ret)
56*4882a593Smuzhiyun DMA_FENCE_TRACE(&fence->scheduled,
57*4882a593Smuzhiyun "signaled from irq context\n");
58*4882a593Smuzhiyun else
59*4882a593Smuzhiyun DMA_FENCE_TRACE(&fence->scheduled,
60*4882a593Smuzhiyun "was already signaled\n");
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun
drm_sched_fence_finished(struct drm_sched_fence * fence)63*4882a593Smuzhiyun void drm_sched_fence_finished(struct drm_sched_fence *fence)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun int ret = dma_fence_signal(&fence->finished);
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun if (!ret)
68*4882a593Smuzhiyun DMA_FENCE_TRACE(&fence->finished,
69*4882a593Smuzhiyun "signaled from irq context\n");
70*4882a593Smuzhiyun else
71*4882a593Smuzhiyun DMA_FENCE_TRACE(&fence->finished,
72*4882a593Smuzhiyun "was already signaled\n");
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun
drm_sched_fence_get_driver_name(struct dma_fence * fence)75*4882a593Smuzhiyun static const char *drm_sched_fence_get_driver_name(struct dma_fence *fence)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun return "drm_sched";
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun
drm_sched_fence_get_timeline_name(struct dma_fence * f)80*4882a593Smuzhiyun static const char *drm_sched_fence_get_timeline_name(struct dma_fence *f)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun struct drm_sched_fence *fence = to_drm_sched_fence(f);
83*4882a593Smuzhiyun return (const char *)fence->sched->name;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun /**
87*4882a593Smuzhiyun * drm_sched_fence_free - free up the fence memory
88*4882a593Smuzhiyun *
89*4882a593Smuzhiyun * @rcu: RCU callback head
90*4882a593Smuzhiyun *
91*4882a593Smuzhiyun * Free up the fence memory after the RCU grace period.
92*4882a593Smuzhiyun */
drm_sched_fence_free(struct rcu_head * rcu)93*4882a593Smuzhiyun static void drm_sched_fence_free(struct rcu_head *rcu)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun struct dma_fence *f = container_of(rcu, struct dma_fence, rcu);
96*4882a593Smuzhiyun struct drm_sched_fence *fence = to_drm_sched_fence(f);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun kmem_cache_free(sched_fence_slab, fence);
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun /**
102*4882a593Smuzhiyun * drm_sched_fence_release_scheduled - callback that fence can be freed
103*4882a593Smuzhiyun *
104*4882a593Smuzhiyun * @f: fence
105*4882a593Smuzhiyun *
106*4882a593Smuzhiyun * This function is called when the reference count becomes zero.
107*4882a593Smuzhiyun * It just RCU schedules freeing up the fence.
108*4882a593Smuzhiyun */
drm_sched_fence_release_scheduled(struct dma_fence * f)109*4882a593Smuzhiyun static void drm_sched_fence_release_scheduled(struct dma_fence *f)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun struct drm_sched_fence *fence = to_drm_sched_fence(f);
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun dma_fence_put(fence->parent);
114*4882a593Smuzhiyun call_rcu(&fence->finished.rcu, drm_sched_fence_free);
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun /**
118*4882a593Smuzhiyun * drm_sched_fence_release_finished - drop extra reference
119*4882a593Smuzhiyun *
120*4882a593Smuzhiyun * @f: fence
121*4882a593Smuzhiyun *
122*4882a593Smuzhiyun * Drop the extra reference from the scheduled fence to the base fence.
123*4882a593Smuzhiyun */
drm_sched_fence_release_finished(struct dma_fence * f)124*4882a593Smuzhiyun static void drm_sched_fence_release_finished(struct dma_fence *f)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun struct drm_sched_fence *fence = to_drm_sched_fence(f);
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun dma_fence_put(&fence->scheduled);
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun static const struct dma_fence_ops drm_sched_fence_ops_scheduled = {
132*4882a593Smuzhiyun .get_driver_name = drm_sched_fence_get_driver_name,
133*4882a593Smuzhiyun .get_timeline_name = drm_sched_fence_get_timeline_name,
134*4882a593Smuzhiyun .release = drm_sched_fence_release_scheduled,
135*4882a593Smuzhiyun };
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun static const struct dma_fence_ops drm_sched_fence_ops_finished = {
138*4882a593Smuzhiyun .get_driver_name = drm_sched_fence_get_driver_name,
139*4882a593Smuzhiyun .get_timeline_name = drm_sched_fence_get_timeline_name,
140*4882a593Smuzhiyun .release = drm_sched_fence_release_finished,
141*4882a593Smuzhiyun };
142*4882a593Smuzhiyun
to_drm_sched_fence(struct dma_fence * f)143*4882a593Smuzhiyun struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun if (f->ops == &drm_sched_fence_ops_scheduled)
146*4882a593Smuzhiyun return container_of(f, struct drm_sched_fence, scheduled);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (f->ops == &drm_sched_fence_ops_finished)
149*4882a593Smuzhiyun return container_of(f, struct drm_sched_fence, finished);
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun return NULL;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun EXPORT_SYMBOL(to_drm_sched_fence);
154*4882a593Smuzhiyun
drm_sched_fence_create(struct drm_sched_entity * entity,void * owner)155*4882a593Smuzhiyun struct drm_sched_fence *drm_sched_fence_create(struct drm_sched_entity *entity,
156*4882a593Smuzhiyun void *owner)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun struct drm_sched_fence *fence = NULL;
159*4882a593Smuzhiyun unsigned seq;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun fence = kmem_cache_zalloc(sched_fence_slab, GFP_KERNEL);
162*4882a593Smuzhiyun if (fence == NULL)
163*4882a593Smuzhiyun return NULL;
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun fence->owner = owner;
166*4882a593Smuzhiyun fence->sched = entity->rq->sched;
167*4882a593Smuzhiyun spin_lock_init(&fence->lock);
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun seq = atomic_inc_return(&entity->fence_seq);
170*4882a593Smuzhiyun dma_fence_init(&fence->scheduled, &drm_sched_fence_ops_scheduled,
171*4882a593Smuzhiyun &fence->lock, entity->fence_context, seq);
172*4882a593Smuzhiyun dma_fence_init(&fence->finished, &drm_sched_fence_ops_finished,
173*4882a593Smuzhiyun &fence->lock, entity->fence_context + 1, seq);
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun return fence;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun module_init(drm_sched_fence_slab_init);
179*4882a593Smuzhiyun module_exit(drm_sched_fence_slab_fini);
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun MODULE_DESCRIPTION("DRM GPU scheduler");
182*4882a593Smuzhiyun MODULE_LICENSE("GPL and additional rights");
183