mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
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/
This commit is contained in:
parent
8d4c21718c
commit
3bebfd53af
@ -837,6 +837,7 @@ static const struct drm_driver msm_driver = {
|
||||
.postclose = msm_postclose,
|
||||
.dumb_create = msm_gem_dumb_create,
|
||||
.dumb_map_offset = msm_gem_dumb_map_offset,
|
||||
.gem_prime_import = msm_gem_prime_import,
|
||||
.gem_prime_import_sg_table = msm_gem_prime_import_sg_table,
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
.debugfs_init = msm_debugfs_init,
|
||||
|
@ -269,6 +269,7 @@ void msm_gem_shrinker_cleanup(struct drm_device *dev);
|
||||
struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj);
|
||||
int msm_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map);
|
||||
void msm_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map);
|
||||
struct drm_gem_object *msm_gem_prime_import(struct drm_device *dev, struct dma_buf *buf);
|
||||
struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
|
||||
struct dma_buf_attachment *attach, struct sg_table *sg);
|
||||
struct dma_buf *msm_gem_prime_export(struct drm_gem_object *obj, int flags);
|
||||
|
@ -89,6 +89,7 @@ int msm_framebuffer_prepare(struct drm_framebuffer *fb, bool needs_dirtyfb)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
msm_gem_vma_get(fb->obj[i]);
|
||||
ret = msm_gem_get_and_pin_iova(fb->obj[i], vm, &msm_fb->iova[i]);
|
||||
drm_dbg_state(fb->dev, "FB[%u]: iova[%d]: %08llx (%d)\n",
|
||||
fb->base.id, i, msm_fb->iova[i], ret);
|
||||
@ -114,8 +115,10 @@ void msm_framebuffer_cleanup(struct drm_framebuffer *fb, bool needed_dirtyfb)
|
||||
|
||||
memset(msm_fb->iova, 0, sizeof(msm_fb->iova));
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
for (i = 0; i < n; i++) {
|
||||
msm_gem_unpin_iova(fb->obj[i], vm);
|
||||
msm_gem_vma_put(fb->obj[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb, int plane)
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "msm_drv.h"
|
||||
#include "msm_gem.h"
|
||||
#include "msm_gpu.h"
|
||||
#include "msm_kms.h"
|
||||
|
||||
static void update_device_mem(struct msm_drm_private *priv, ssize_t size)
|
||||
{
|
||||
@ -39,6 +40,7 @@ static void update_ctx_mem(struct drm_file *file, ssize_t size)
|
||||
|
||||
static int msm_gem_open(struct drm_gem_object *obj, struct drm_file *file)
|
||||
{
|
||||
msm_gem_vma_get(obj);
|
||||
update_ctx_mem(file, obj->size);
|
||||
return 0;
|
||||
}
|
||||
@ -46,33 +48,13 @@ static int msm_gem_open(struct drm_gem_object *obj, struct drm_file *file)
|
||||
static void put_iova_spaces(struct drm_gem_object *obj, struct drm_gpuvm *vm,
|
||||
bool close, const char *reason);
|
||||
|
||||
static void detach_vm(struct drm_gem_object *obj, struct drm_gpuvm *vm)
|
||||
{
|
||||
msm_gem_assert_locked(obj);
|
||||
drm_gpuvm_resv_assert_held(vm);
|
||||
|
||||
struct drm_gpuvm_bo *vm_bo = drm_gpuvm_bo_find(vm, obj);
|
||||
if (vm_bo) {
|
||||
struct drm_gpuva *vma;
|
||||
|
||||
drm_gpuvm_bo_for_each_va (vma, vm_bo) {
|
||||
if (vma->vm != vm)
|
||||
continue;
|
||||
msm_gem_vma_unmap(vma, "detach");
|
||||
msm_gem_vma_close(vma);
|
||||
break;
|
||||
}
|
||||
|
||||
drm_gpuvm_bo_put(vm_bo);
|
||||
}
|
||||
}
|
||||
|
||||
static void msm_gem_close(struct drm_gem_object *obj, struct drm_file *file)
|
||||
{
|
||||
struct msm_context *ctx = file->driver_priv;
|
||||
struct drm_exec exec;
|
||||
|
||||
update_ctx_mem(file, -obj->size);
|
||||
msm_gem_vma_put(obj);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
|
||||
msm_gem_lock_vm_and_obj(&exec, obj, ctx->vm);
|
||||
put_iova_spaces(obj, ctx->vm, true, "close");
|
||||
detach_vm(obj, ctx->vm);
|
||||
drm_exec_fini(&exec); /* drop locks */
|
||||
}
|
||||
|
||||
/*
|
||||
* Get/put for kms->vm VMA
|
||||
*/
|
||||
|
||||
void msm_gem_vma_get(struct drm_gem_object *obj)
|
||||
{
|
||||
atomic_inc(&to_msm_bo(obj)->vma_ref);
|
||||
}
|
||||
|
||||
void msm_gem_vma_put(struct drm_gem_object *obj)
|
||||
{
|
||||
struct msm_drm_private *priv = obj->dev->dev_private;
|
||||
struct drm_exec exec;
|
||||
|
||||
if (atomic_dec_return(&to_msm_bo(obj)->vma_ref))
|
||||
return;
|
||||
|
||||
if (!priv->kms)
|
||||
return;
|
||||
|
||||
msm_gem_lock_vm_and_obj(&exec, obj, priv->kms->vm);
|
||||
put_iova_spaces(obj, priv->kms->vm, true, "vma_put");
|
||||
drm_exec_fini(&exec); /* drop locks */
|
||||
}
|
||||
|
||||
@ -656,6 +662,13 @@ int msm_gem_set_iova(struct drm_gem_object *obj,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool is_kms_vm(struct drm_gpuvm *vm)
|
||||
{
|
||||
struct msm_drm_private *priv = vm->drm->dev_private;
|
||||
|
||||
return priv->kms && (priv->kms->vm == vm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unpin a iova by updating the reference counts. The memory isn't actually
|
||||
* 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)
|
||||
if (vma) {
|
||||
msm_gem_unpin_locked(obj);
|
||||
}
|
||||
detach_vm(obj, vm);
|
||||
if (!is_kms_vm(vm))
|
||||
put_iova_spaces(obj, vm, true, "close");
|
||||
drm_exec_fini(&exec); /* drop locks */
|
||||
}
|
||||
|
||||
|
@ -211,9 +211,37 @@ struct msm_gem_object {
|
||||
* Protected by LRU lock.
|
||||
*/
|
||||
int pin_count;
|
||||
|
||||
/**
|
||||
* @vma_ref: Reference count of VMA users.
|
||||
*
|
||||
* With the vm_bo/vma holding a reference to the GEM object, we'd
|
||||
* otherwise have to actively tear down a VMA when, for example,
|
||||
* a buffer is unpinned for scanout, vs. the pre-drm_gpuvm approach
|
||||
* where a VMA did not hold a reference to the BO, but instead was
|
||||
* implicitly torn down when the BO was freed.
|
||||
*
|
||||
* To regain the lazy VMA teardown, we use the @vma_ref. It is
|
||||
* incremented for any of the following:
|
||||
*
|
||||
* 1) the BO is exported as a dma_buf
|
||||
* 2) the BO has open userspace handle
|
||||
*
|
||||
* All of those conditions will hold an reference to the BO,
|
||||
* preventing it from being freed. So lazily keeping around the
|
||||
* VMA will not prevent the BO from being freed. (Or rather, the
|
||||
* reference loop is harmless in this case.)
|
||||
*
|
||||
* When the @vma_ref drops to zero, then kms->vm VMA will be
|
||||
* torn down.
|
||||
*/
|
||||
atomic_t vma_ref;
|
||||
};
|
||||
#define to_msm_bo(x) container_of(x, struct msm_gem_object, base)
|
||||
|
||||
void msm_gem_vma_get(struct drm_gem_object *obj);
|
||||
void msm_gem_vma_put(struct drm_gem_object *obj);
|
||||
|
||||
uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj);
|
||||
int msm_gem_prot(struct drm_gem_object *obj);
|
||||
int msm_gem_pin_vma_locked(struct drm_gem_object *obj, struct drm_gpuva *vma);
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <linux/dma-buf.h>
|
||||
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_prime.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
@ -42,19 +43,68 @@ void msm_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
|
||||
msm_gem_put_vaddr_locked(obj);
|
||||
}
|
||||
|
||||
static void msm_gem_dmabuf_release(struct dma_buf *dma_buf)
|
||||
{
|
||||
struct drm_gem_object *obj = dma_buf->priv;
|
||||
|
||||
msm_gem_vma_put(obj);
|
||||
drm_gem_dmabuf_release(dma_buf);
|
||||
}
|
||||
|
||||
static const struct dma_buf_ops msm_gem_prime_dmabuf_ops = {
|
||||
.attach = drm_gem_map_attach,
|
||||
.detach = drm_gem_map_detach,
|
||||
.map_dma_buf = drm_gem_map_dma_buf,
|
||||
.unmap_dma_buf = drm_gem_unmap_dma_buf,
|
||||
.release = msm_gem_dmabuf_release,
|
||||
.mmap = drm_gem_dmabuf_mmap,
|
||||
.vmap = drm_gem_dmabuf_vmap,
|
||||
.vunmap = drm_gem_dmabuf_vunmap,
|
||||
};
|
||||
|
||||
struct drm_gem_object *msm_gem_prime_import(struct drm_device *dev,
|
||||
struct dma_buf *buf)
|
||||
{
|
||||
if (buf->ops == &msm_gem_prime_dmabuf_ops) {
|
||||
struct drm_gem_object *obj = buf->priv;
|
||||
if (obj->dev == dev) {
|
||||
/*
|
||||
* Importing dmabuf exported from our own gem increases
|
||||
* refcount on gem itself instead of f_count of dmabuf.
|
||||
*/
|
||||
drm_gem_object_get(obj);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
return drm_gem_prime_import(dev, buf);
|
||||
}
|
||||
|
||||
struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
|
||||
struct dma_buf_attachment *attach, struct sg_table *sg)
|
||||
{
|
||||
return msm_gem_import(dev, attach->dmabuf, sg);
|
||||
}
|
||||
|
||||
|
||||
struct dma_buf *msm_gem_prime_export(struct drm_gem_object *obj, int flags)
|
||||
{
|
||||
if (to_msm_bo(obj)->flags & MSM_BO_NO_SHARE)
|
||||
return ERR_PTR(-EPERM);
|
||||
|
||||
return drm_gem_prime_export(obj, flags);
|
||||
msm_gem_vma_get(obj);
|
||||
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct dma_buf_export_info exp_info = {
|
||||
.exp_name = KBUILD_MODNAME, /* white lie for debug */
|
||||
.owner = dev->driver->fops->owner,
|
||||
.ops = &msm_gem_prime_dmabuf_ops,
|
||||
.size = obj->size,
|
||||
.flags = flags,
|
||||
.priv = obj,
|
||||
.resv = obj->resv,
|
||||
};
|
||||
|
||||
return drm_gem_dmabuf_export(dev, &exp_info);
|
||||
}
|
||||
|
||||
int msm_gem_prime_pin(struct drm_gem_object *obj)
|
||||
|
Loading…
Reference in New Issue
Block a user