diff options
author | Rob Clark <robdclark@gmail.com> | 2013-09-14 14:01:55 -0400 |
---|---|---|
committer | Rob Clark <robdclark@gmail.com> | 2013-11-01 12:39:45 -0400 |
commit | edd4fc63a33eeeb922503b14e8040a3b028c76a5 (patch) | |
tree | 6916bd23f5af0045c7fad9e0fb73eff5990e02aa | |
parent | a862391871004bf8dea2299bb712aa93a512334a (diff) |
drm/msm: rework inactive-work
Re-arrange things a bit so that we can get work requested after a bo
fence passes, like pageflip, done before retiring bo's. Without any
sort of bo cache in userspace, some games can trigger hundred's of
transient bo's, which can cause retire to take a long time (5-10ms).
Obviously we want a bo cache.. but this cleanup will make things a
bit easier for atomic as well and makes things a bit cleaner.
Signed-off-by: Rob Clark <robdclark@gmail.com>
Acked-by: David Brown <davidb@codeaurora.org>
-rw-r--r-- | drivers/gpu/drm/msm/mdp4/mdp4_crtc.c | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.c | 30 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.h | 21 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gem.c | 35 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gem.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gpu.c | 4 |
6 files changed, 71 insertions, 33 deletions
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c index 5a68aab66fa2..1d52896dfa89 100644 --- a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c | |||
@@ -51,7 +51,7 @@ struct mdp4_crtc { | |||
51 | 51 | ||
52 | /* if there is a pending flip, these will be non-null: */ | 52 | /* if there is a pending flip, these will be non-null: */ |
53 | struct drm_pending_vblank_event *event; | 53 | struct drm_pending_vblank_event *event; |
54 | struct work_struct pageflip_work; | 54 | struct msm_fence_cb pageflip_cb; |
55 | 55 | ||
56 | /* the fb that we currently hold a scanout ref to: */ | 56 | /* the fb that we currently hold a scanout ref to: */ |
57 | struct drm_framebuffer *fb; | 57 | struct drm_framebuffer *fb; |
@@ -132,10 +132,10 @@ static void crtc_flush(struct drm_crtc *crtc) | |||
132 | mdp4_write(mdp4_kms, REG_MDP4_OVERLAY_FLUSH, flush); | 132 | mdp4_write(mdp4_kms, REG_MDP4_OVERLAY_FLUSH, flush); |
133 | } | 133 | } |
134 | 134 | ||
135 | static void pageflip_worker(struct work_struct *work) | 135 | static void pageflip_cb(struct msm_fence_cb *cb) |
136 | { | 136 | { |
137 | struct mdp4_crtc *mdp4_crtc = | 137 | struct mdp4_crtc *mdp4_crtc = |
138 | container_of(work, struct mdp4_crtc, pageflip_work); | 138 | container_of(cb, struct mdp4_crtc, pageflip_cb); |
139 | struct drm_crtc *crtc = &mdp4_crtc->base; | 139 | struct drm_crtc *crtc = &mdp4_crtc->base; |
140 | 140 | ||
141 | mdp4_plane_set_scanout(mdp4_crtc->plane, crtc->fb); | 141 | mdp4_plane_set_scanout(mdp4_crtc->plane, crtc->fb); |
@@ -397,8 +397,7 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc, | |||
397 | mdp4_crtc->event = event; | 397 | mdp4_crtc->event = event; |
398 | update_fb(crtc, true, new_fb); | 398 | update_fb(crtc, true, new_fb); |
399 | 399 | ||
400 | return msm_gem_queue_inactive_work(obj, | 400 | return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb); |
401 | &mdp4_crtc->pageflip_work); | ||
402 | } | 401 | } |
403 | 402 | ||
404 | static int mdp4_crtc_set_property(struct drm_crtc *crtc, | 403 | static int mdp4_crtc_set_property(struct drm_crtc *crtc, |
@@ -702,7 +701,7 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev, | |||
702 | ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64, | 701 | ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64, |
703 | "unref cursor", unref_cursor_worker); | 702 | "unref cursor", unref_cursor_worker); |
704 | 703 | ||
705 | INIT_WORK(&mdp4_crtc->pageflip_work, pageflip_worker); | 704 | INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb); |
706 | 705 | ||
707 | drm_crtc_init(dev, crtc, &mdp4_crtc_funcs); | 706 | drm_crtc_init(dev, crtc, &mdp4_crtc_funcs); |
708 | drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs); | 707 | drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs); |
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index e7ac95a38725..86537692e45c 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c | |||
@@ -187,6 +187,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags) | |||
187 | init_waitqueue_head(&priv->fence_event); | 187 | init_waitqueue_head(&priv->fence_event); |
188 | 188 | ||
189 | INIT_LIST_HEAD(&priv->inactive_list); | 189 | INIT_LIST_HEAD(&priv->inactive_list); |
190 | INIT_LIST_HEAD(&priv->fence_cbs); | ||
190 | 191 | ||
191 | drm_mode_config_init(dev); | 192 | drm_mode_config_init(dev); |
192 | 193 | ||
@@ -539,15 +540,36 @@ int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence, | |||
539 | return ret; | 540 | return ret; |
540 | } | 541 | } |
541 | 542 | ||
542 | /* call under struct_mutex */ | 543 | /* called from workqueue */ |
543 | void msm_update_fence(struct drm_device *dev, uint32_t fence) | 544 | void msm_update_fence(struct drm_device *dev, uint32_t fence) |
544 | { | 545 | { |
545 | struct msm_drm_private *priv = dev->dev_private; | 546 | struct msm_drm_private *priv = dev->dev_private; |
546 | 547 | ||
547 | if (fence > priv->completed_fence) { | 548 | mutex_lock(&dev->struct_mutex); |
548 | priv->completed_fence = fence; | 549 | priv->completed_fence = max(fence, priv->completed_fence); |
549 | wake_up_all(&priv->fence_event); | 550 | |
551 | while (!list_empty(&priv->fence_cbs)) { | ||
552 | struct msm_fence_cb *cb; | ||
553 | |||
554 | cb = list_first_entry(&priv->fence_cbs, | ||
555 | struct msm_fence_cb, work.entry); | ||
556 | |||
557 | if (cb->fence > priv->completed_fence) | ||
558 | break; | ||
559 | |||
560 | list_del_init(&cb->work.entry); | ||
561 | queue_work(priv->wq, &cb->work); | ||
550 | } | 562 | } |
563 | |||
564 | mutex_unlock(&dev->struct_mutex); | ||
565 | |||
566 | wake_up_all(&priv->fence_event); | ||
567 | } | ||
568 | |||
569 | void __msm_fence_worker(struct work_struct *work) | ||
570 | { | ||
571 | struct msm_fence_cb *cb = container_of(work, struct msm_fence_cb, work); | ||
572 | cb->func(cb); | ||
551 | } | 573 | } |
552 | 574 | ||
553 | /* | 575 | /* |
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 2c6bad5905e8..d39f0862b19e 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h | |||
@@ -73,6 +73,9 @@ struct msm_drm_private { | |||
73 | 73 | ||
74 | struct workqueue_struct *wq; | 74 | struct workqueue_struct *wq; |
75 | 75 | ||
76 | /* callbacks deferred until bo is inactive: */ | ||
77 | struct list_head fence_cbs; | ||
78 | |||
76 | /* registered IOMMU domains: */ | 79 | /* registered IOMMU domains: */ |
77 | unsigned int num_iommus; | 80 | unsigned int num_iommus; |
78 | struct iommu_domain *iommus[NUM_DOMAINS]; | 81 | struct iommu_domain *iommus[NUM_DOMAINS]; |
@@ -97,6 +100,20 @@ struct msm_format { | |||
97 | uint32_t pixel_format; | 100 | uint32_t pixel_format; |
98 | }; | 101 | }; |
99 | 102 | ||
103 | /* callback from wq once fence has passed: */ | ||
104 | struct msm_fence_cb { | ||
105 | struct work_struct work; | ||
106 | uint32_t fence; | ||
107 | void (*func)(struct msm_fence_cb *cb); | ||
108 | }; | ||
109 | |||
110 | void __msm_fence_worker(struct work_struct *work); | ||
111 | |||
112 | #define INIT_FENCE_CB(_cb, _func) do { \ | ||
113 | INIT_WORK(&(_cb)->work, __msm_fence_worker); \ | ||
114 | (_cb)->func = _func; \ | ||
115 | } while (0) | ||
116 | |||
100 | /* As there are different display controller blocks depending on the | 117 | /* As there are different display controller blocks depending on the |
101 | * snapdragon version, the kms support is split out and the appropriate | 118 | * snapdragon version, the kms support is split out and the appropriate |
102 | * implementation is loaded at runtime. The kms module is responsible | 119 | * implementation is loaded at runtime. The kms module is responsible |
@@ -160,8 +177,8 @@ int msm_gem_prime_pin(struct drm_gem_object *obj); | |||
160 | void msm_gem_prime_unpin(struct drm_gem_object *obj); | 177 | void msm_gem_prime_unpin(struct drm_gem_object *obj); |
161 | void *msm_gem_vaddr_locked(struct drm_gem_object *obj); | 178 | void *msm_gem_vaddr_locked(struct drm_gem_object *obj); |
162 | void *msm_gem_vaddr(struct drm_gem_object *obj); | 179 | void *msm_gem_vaddr(struct drm_gem_object *obj); |
163 | int msm_gem_queue_inactive_work(struct drm_gem_object *obj, | 180 | int msm_gem_queue_inactive_cb(struct drm_gem_object *obj, |
164 | struct work_struct *work); | 181 | struct msm_fence_cb *cb); |
165 | void msm_gem_move_to_active(struct drm_gem_object *obj, | 182 | void msm_gem_move_to_active(struct drm_gem_object *obj, |
166 | struct msm_gpu *gpu, bool write, uint32_t fence); | 183 | struct msm_gpu *gpu, bool write, uint32_t fence); |
167 | void msm_gem_move_to_inactive(struct drm_gem_object *obj); | 184 | void msm_gem_move_to_inactive(struct drm_gem_object *obj); |
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index ea2c96f9459b..291939de299e 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c | |||
@@ -309,7 +309,17 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id, | |||
309 | 309 | ||
310 | int msm_gem_get_iova(struct drm_gem_object *obj, int id, uint32_t *iova) | 310 | int msm_gem_get_iova(struct drm_gem_object *obj, int id, uint32_t *iova) |
311 | { | 311 | { |
312 | struct msm_gem_object *msm_obj = to_msm_bo(obj); | ||
312 | int ret; | 313 | int ret; |
314 | |||
315 | /* this is safe right now because we don't unmap until the | ||
316 | * bo is deleted: | ||
317 | */ | ||
318 | if (msm_obj->domain[id].iova) { | ||
319 | *iova = msm_obj->domain[id].iova; | ||
320 | return 0; | ||
321 | } | ||
322 | |||
313 | mutex_lock(&obj->dev->struct_mutex); | 323 | mutex_lock(&obj->dev->struct_mutex); |
314 | ret = msm_gem_get_iova_locked(obj, id, iova); | 324 | ret = msm_gem_get_iova_locked(obj, id, iova); |
315 | mutex_unlock(&obj->dev->struct_mutex); | 325 | mutex_unlock(&obj->dev->struct_mutex); |
@@ -379,8 +389,11 @@ void *msm_gem_vaddr(struct drm_gem_object *obj) | |||
379 | return ret; | 389 | return ret; |
380 | } | 390 | } |
381 | 391 | ||
382 | int msm_gem_queue_inactive_work(struct drm_gem_object *obj, | 392 | /* setup callback for when bo is no longer busy.. |
383 | struct work_struct *work) | 393 | * TODO probably want to differentiate read vs write.. |
394 | */ | ||
395 | int msm_gem_queue_inactive_cb(struct drm_gem_object *obj, | ||
396 | struct msm_fence_cb *cb) | ||
384 | { | 397 | { |
385 | struct drm_device *dev = obj->dev; | 398 | struct drm_device *dev = obj->dev; |
386 | struct msm_drm_private *priv = dev->dev_private; | 399 | struct msm_drm_private *priv = dev->dev_private; |
@@ -388,12 +401,13 @@ int msm_gem_queue_inactive_work(struct drm_gem_object *obj, | |||
388 | int ret = 0; | 401 | int ret = 0; |
389 | 402 | ||
390 | mutex_lock(&dev->struct_mutex); | 403 | mutex_lock(&dev->struct_mutex); |
391 | if (!list_empty(&work->entry)) { | 404 | if (!list_empty(&cb->work.entry)) { |
392 | ret = -EINVAL; | 405 | ret = -EINVAL; |
393 | } else if (is_active(msm_obj)) { | 406 | } else if (is_active(msm_obj)) { |
394 | list_add_tail(&work->entry, &msm_obj->inactive_work); | 407 | cb->fence = max(msm_obj->read_fence, msm_obj->write_fence); |
408 | list_add_tail(&cb->work.entry, &priv->fence_cbs); | ||
395 | } else { | 409 | } else { |
396 | queue_work(priv->wq, work); | 410 | queue_work(priv->wq, &cb->work); |
397 | } | 411 | } |
398 | mutex_unlock(&dev->struct_mutex); | 412 | mutex_unlock(&dev->struct_mutex); |
399 | 413 | ||
@@ -426,16 +440,6 @@ void msm_gem_move_to_inactive(struct drm_gem_object *obj) | |||
426 | msm_obj->write_fence = 0; | 440 | msm_obj->write_fence = 0; |
427 | list_del_init(&msm_obj->mm_list); | 441 | list_del_init(&msm_obj->mm_list); |
428 | list_add_tail(&msm_obj->mm_list, &priv->inactive_list); | 442 | list_add_tail(&msm_obj->mm_list, &priv->inactive_list); |
429 | |||
430 | while (!list_empty(&msm_obj->inactive_work)) { | ||
431 | struct work_struct *work; | ||
432 | |||
433 | work = list_first_entry(&msm_obj->inactive_work, | ||
434 | struct work_struct, entry); | ||
435 | |||
436 | list_del_init(&work->entry); | ||
437 | queue_work(priv->wq, work); | ||
438 | } | ||
439 | } | 443 | } |
440 | 444 | ||
441 | int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, | 445 | int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, |
@@ -604,7 +608,6 @@ static int msm_gem_new_impl(struct drm_device *dev, | |||
604 | reservation_object_init(msm_obj->resv); | 608 | reservation_object_init(msm_obj->resv); |
605 | 609 | ||
606 | INIT_LIST_HEAD(&msm_obj->submit_entry); | 610 | INIT_LIST_HEAD(&msm_obj->submit_entry); |
607 | INIT_LIST_HEAD(&msm_obj->inactive_work); | ||
608 | list_add_tail(&msm_obj->mm_list, &priv->inactive_list); | 611 | list_add_tail(&msm_obj->mm_list, &priv->inactive_list); |
609 | 612 | ||
610 | *obj = &msm_obj->base; | 613 | *obj = &msm_obj->base; |
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 0676f32e2c6a..f4f23a578d9d 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h | |||
@@ -45,9 +45,6 @@ struct msm_gem_object { | |||
45 | */ | 45 | */ |
46 | struct list_head submit_entry; | 46 | struct list_head submit_entry; |
47 | 47 | ||
48 | /* work defered until bo is inactive: */ | ||
49 | struct list_head inactive_work; | ||
50 | |||
51 | struct page **pages; | 48 | struct page **pages; |
52 | struct sg_table *sgt; | 49 | struct sg_table *sgt; |
53 | void *vaddr; | 50 | void *vaddr; |
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 3bab937965d1..4583d61556f5 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c | |||
@@ -268,6 +268,8 @@ static void retire_worker(struct work_struct *work) | |||
268 | struct drm_device *dev = gpu->dev; | 268 | struct drm_device *dev = gpu->dev; |
269 | uint32_t fence = gpu->funcs->last_fence(gpu); | 269 | uint32_t fence = gpu->funcs->last_fence(gpu); |
270 | 270 | ||
271 | msm_update_fence(gpu->dev, fence); | ||
272 | |||
271 | mutex_lock(&dev->struct_mutex); | 273 | mutex_lock(&dev->struct_mutex); |
272 | 274 | ||
273 | while (!list_empty(&gpu->active_list)) { | 275 | while (!list_empty(&gpu->active_list)) { |
@@ -287,8 +289,6 @@ static void retire_worker(struct work_struct *work) | |||
287 | } | 289 | } |
288 | } | 290 | } |
289 | 291 | ||
290 | msm_update_fence(gpu->dev, fence); | ||
291 | |||
292 | mutex_unlock(&dev->struct_mutex); | 292 | mutex_unlock(&dev->struct_mutex); |
293 | } | 293 | } |
294 | 294 | ||