Skip to content

Commit 3bebfd5

Browse files
author
Rob Clark
committed
drm/msm: Defer VMA unmap for fb unpins
With the conversion to drm_gpuvm, we lost the lazy VMA cleanup, which means that fb cleanup/unpin when pageflipping to new scanout buffers immediately unmaps the scanout buffer. This is costly (with tlbinv, it can be 4-6ms for a 1080p scanout buffer, and more for higher resolutions)! To avoid this, introduce a vma_ref, which is incremented whenever userspace has a GEM handle or dma-buf fd. When unpinning if the vm is the kms->vm we defer tearing down the VMA until the vma_ref drops to zero. If the buffer is still part of a flip-chain then userspace will be holding some sort of reference to the BO, either via a GEM handle and/or dma-buf fd. So this avoids unmapping the VMA when there is a strong possibility that it will be needed again. Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com> Tested-by: Antonino Maniscalco <antomani103@gmail.com> Reviewed-by: Antonino Maniscalco <antomani103@gmail.com> Patchwork: https://patchwork.freedesktop.org/patch/661538/
1 parent 8d4c217 commit 3bebfd5

6 files changed

Lines changed: 123 additions & 26 deletions

File tree

drivers/gpu/drm/msm/msm_drv.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,7 @@ static const struct drm_driver msm_driver = {
837837
.postclose = msm_postclose,
838838
.dumb_create = msm_gem_dumb_create,
839839
.dumb_map_offset = msm_gem_dumb_map_offset,
840+
.gem_prime_import = msm_gem_prime_import,
840841
.gem_prime_import_sg_table = msm_gem_prime_import_sg_table,
841842
#ifdef CONFIG_DEBUG_FS
842843
.debugfs_init = msm_debugfs_init,

drivers/gpu/drm/msm/msm_drv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ void msm_gem_shrinker_cleanup(struct drm_device *dev);
269269
struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj);
270270
int msm_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map);
271271
void msm_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map);
272+
struct drm_gem_object *msm_gem_prime_import(struct drm_device *dev, struct dma_buf *buf);
272273
struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
273274
struct dma_buf_attachment *attach, struct sg_table *sg);
274275
struct dma_buf *msm_gem_prime_export(struct drm_gem_object *obj, int flags);

drivers/gpu/drm/msm/msm_fb.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ int msm_framebuffer_prepare(struct drm_framebuffer *fb, bool needs_dirtyfb)
8989
return 0;
9090

9191
for (i = 0; i < n; i++) {
92+
msm_gem_vma_get(fb->obj[i]);
9293
ret = msm_gem_get_and_pin_iova(fb->obj[i], vm, &msm_fb->iova[i]);
9394
drm_dbg_state(fb->dev, "FB[%u]: iova[%d]: %08llx (%d)\n",
9495
fb->base.id, i, msm_fb->iova[i], ret);
@@ -114,8 +115,10 @@ void msm_framebuffer_cleanup(struct drm_framebuffer *fb, bool needed_dirtyfb)
114115

115116
memset(msm_fb->iova, 0, sizeof(msm_fb->iova));
116117

117-
for (i = 0; i < n; i++)
118+
for (i = 0; i < n; i++) {
118119
msm_gem_unpin_iova(fb->obj[i], vm);
120+
msm_gem_vma_put(fb->obj[i]);
121+
}
119122
}
120123

121124
uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb, int plane)

drivers/gpu/drm/msm/msm_gem.c

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "msm_drv.h"
2020
#include "msm_gem.h"
2121
#include "msm_gpu.h"
22+
#include "msm_kms.h"
2223

2324
static void update_device_mem(struct msm_drm_private *priv, ssize_t size)
2425
{
@@ -39,40 +40,21 @@ static void update_ctx_mem(struct drm_file *file, ssize_t size)
3940

4041
static int msm_gem_open(struct drm_gem_object *obj, struct drm_file *file)
4142
{
43+
msm_gem_vma_get(obj);
4244
update_ctx_mem(file, obj->size);
4345
return 0;
4446
}
4547

4648
static void put_iova_spaces(struct drm_gem_object *obj, struct drm_gpuvm *vm,
4749
bool close, const char *reason);
4850

49-
static void detach_vm(struct drm_gem_object *obj, struct drm_gpuvm *vm)
50-
{
51-
msm_gem_assert_locked(obj);
52-
drm_gpuvm_resv_assert_held(vm);
53-
54-
struct drm_gpuvm_bo *vm_bo = drm_gpuvm_bo_find(vm, obj);
55-
if (vm_bo) {
56-
struct drm_gpuva *vma;
57-
58-
drm_gpuvm_bo_for_each_va (vma, vm_bo) {
59-
if (vma->vm != vm)
60-
continue;
61-
msm_gem_vma_unmap(vma, "detach");
62-
msm_gem_vma_close(vma);
63-
break;
64-
}
65-
66-
drm_gpuvm_bo_put(vm_bo);
67-
}
68-
}
69-
7051
static void msm_gem_close(struct drm_gem_object *obj, struct drm_file *file)
7152
{
7253
struct msm_context *ctx = file->driver_priv;
7354
struct drm_exec exec;
7455

7556
update_ctx_mem(file, -obj->size);
57+
msm_gem_vma_put(obj);
7658

7759
/*
7860
* If VM isn't created yet, nothing to cleanup. And in fact calling
@@ -99,7 +81,31 @@ static void msm_gem_close(struct drm_gem_object *obj, struct drm_file *file)
9981

10082
msm_gem_lock_vm_and_obj(&exec, obj, ctx->vm);
10183
put_iova_spaces(obj, ctx->vm, true, "close");
102-
detach_vm(obj, ctx->vm);
84+
drm_exec_fini(&exec); /* drop locks */
85+
}
86+
87+
/*
88+
* Get/put for kms->vm VMA
89+
*/
90+
91+
void msm_gem_vma_get(struct drm_gem_object *obj)
92+
{
93+
atomic_inc(&to_msm_bo(obj)->vma_ref);
94+
}
95+
96+
void msm_gem_vma_put(struct drm_gem_object *obj)
97+
{
98+
struct msm_drm_private *priv = obj->dev->dev_private;
99+
struct drm_exec exec;
100+
101+
if (atomic_dec_return(&to_msm_bo(obj)->vma_ref))
102+
return;
103+
104+
if (!priv->kms)
105+
return;
106+
107+
msm_gem_lock_vm_and_obj(&exec, obj, priv->kms->vm);
108+
put_iova_spaces(obj, priv->kms->vm, true, "vma_put");
103109
drm_exec_fini(&exec); /* drop locks */
104110
}
105111

@@ -656,6 +662,13 @@ int msm_gem_set_iova(struct drm_gem_object *obj,
656662
return ret;
657663
}
658664

665+
static bool is_kms_vm(struct drm_gpuvm *vm)
666+
{
667+
struct msm_drm_private *priv = vm->drm->dev_private;
668+
669+
return priv->kms && (priv->kms->vm == vm);
670+
}
671+
659672
/*
660673
* Unpin a iova by updating the reference counts. The memory isn't actually
661674
* purged until something else (shrinker, mm_notifier, destroy, etc) decides
@@ -671,7 +684,8 @@ void msm_gem_unpin_iova(struct drm_gem_object *obj, struct drm_gpuvm *vm)
671684
if (vma) {
672685
msm_gem_unpin_locked(obj);
673686
}
674-
detach_vm(obj, vm);
687+
if (!is_kms_vm(vm))
688+
put_iova_spaces(obj, vm, true, "close");
675689
drm_exec_fini(&exec); /* drop locks */
676690
}
677691

drivers/gpu/drm/msm/msm_gem.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,37 @@ struct msm_gem_object {
211211
* Protected by LRU lock.
212212
*/
213213
int pin_count;
214+
215+
/**
216+
* @vma_ref: Reference count of VMA users.
217+
*
218+
* With the vm_bo/vma holding a reference to the GEM object, we'd
219+
* otherwise have to actively tear down a VMA when, for example,
220+
* a buffer is unpinned for scanout, vs. the pre-drm_gpuvm approach
221+
* where a VMA did not hold a reference to the BO, but instead was
222+
* implicitly torn down when the BO was freed.
223+
*
224+
* To regain the lazy VMA teardown, we use the @vma_ref. It is
225+
* incremented for any of the following:
226+
*
227+
* 1) the BO is exported as a dma_buf
228+
* 2) the BO has open userspace handle
229+
*
230+
* All of those conditions will hold an reference to the BO,
231+
* preventing it from being freed. So lazily keeping around the
232+
* VMA will not prevent the BO from being freed. (Or rather, the
233+
* reference loop is harmless in this case.)
234+
*
235+
* When the @vma_ref drops to zero, then kms->vm VMA will be
236+
* torn down.
237+
*/
238+
atomic_t vma_ref;
214239
};
215240
#define to_msm_bo(x) container_of(x, struct msm_gem_object, base)
216241

242+
void msm_gem_vma_get(struct drm_gem_object *obj);
243+
void msm_gem_vma_put(struct drm_gem_object *obj);
244+
217245
uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj);
218246
int msm_gem_prot(struct drm_gem_object *obj);
219247
int msm_gem_pin_vma_locked(struct drm_gem_object *obj, struct drm_gpuva *vma);

drivers/gpu/drm/msm/msm_gem_prime.c

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <linux/dma-buf.h>
88

9+
#include <drm/drm_drv.h>
910
#include <drm/drm_prime.h>
1011

1112
#include "msm_drv.h"
@@ -42,19 +43,68 @@ void msm_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
4243
msm_gem_put_vaddr_locked(obj);
4344
}
4445

46+
static void msm_gem_dmabuf_release(struct dma_buf *dma_buf)
47+
{
48+
struct drm_gem_object *obj = dma_buf->priv;
49+
50+
msm_gem_vma_put(obj);
51+
drm_gem_dmabuf_release(dma_buf);
52+
}
53+
54+
static const struct dma_buf_ops msm_gem_prime_dmabuf_ops = {
55+
.attach = drm_gem_map_attach,
56+
.detach = drm_gem_map_detach,
57+
.map_dma_buf = drm_gem_map_dma_buf,
58+
.unmap_dma_buf = drm_gem_unmap_dma_buf,
59+
.release = msm_gem_dmabuf_release,
60+
.mmap = drm_gem_dmabuf_mmap,
61+
.vmap = drm_gem_dmabuf_vmap,
62+
.vunmap = drm_gem_dmabuf_vunmap,
63+
};
64+
65+
struct drm_gem_object *msm_gem_prime_import(struct drm_device *dev,
66+
struct dma_buf *buf)
67+
{
68+
if (buf->ops == &msm_gem_prime_dmabuf_ops) {
69+
struct drm_gem_object *obj = buf->priv;
70+
if (obj->dev == dev) {
71+
/*
72+
* Importing dmabuf exported from our own gem increases
73+
* refcount on gem itself instead of f_count of dmabuf.
74+
*/
75+
drm_gem_object_get(obj);
76+
return obj;
77+
}
78+
}
79+
80+
return drm_gem_prime_import(dev, buf);
81+
}
82+
4583
struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
4684
struct dma_buf_attachment *attach, struct sg_table *sg)
4785
{
4886
return msm_gem_import(dev, attach->dmabuf, sg);
4987
}
5088

51-
5289
struct dma_buf *msm_gem_prime_export(struct drm_gem_object *obj, int flags)
5390
{
5491
if (to_msm_bo(obj)->flags & MSM_BO_NO_SHARE)
5592
return ERR_PTR(-EPERM);
5693

57-
return drm_gem_prime_export(obj, flags);
94+
msm_gem_vma_get(obj);
95+
96+
struct drm_device *dev = obj->dev;
97+
struct dma_buf_export_info exp_info = {
98+
.exp_name = KBUILD_MODNAME, /* white lie for debug */
99+
.owner = dev->driver->fops->owner,
100+
.ops = &msm_gem_prime_dmabuf_ops,
101+
.size = obj->size,
102+
.flags = flags,
103+
.priv = obj,
104+
.resv = obj->resv,
105+
};
106+
107+
return drm_gem_dmabuf_export(dev, &exp_info);
58108
}
59109

60110
int msm_gem_prime_pin(struct drm_gem_object *obj)

0 commit comments

Comments
 (0)