diff options
author | Kristian H. Kristensen <hoegsberg@gmail.com> | 2019-03-20 10:09:08 -0700 |
---|---|---|
committer | Rob Clark <robdclark@chromium.org> | 2019-04-19 11:50:06 -0700 |
commit | 48e7f18392c66f9b69ebac11c54f1a2e033ced54 (patch) | |
tree | cf683d683a70b760650865d44656a699cccf9def | |
parent | fb076b15d50dee36c75eb6c447c1b1026ad12e2e (diff) |
drm/msm: Implement .gem_free_object_unlocked
We use a llist and a worker to delay the object cleanup. This avoids
taking mmap_sem and struct_mutex in the wrong order when calling
drm_gem_object_put_unlocked() from drm_gem_mmap().
Fixes lockdep problem with copy_from_user() in msm_ioctl_gem_submit().
Signed-off-by: Kristian H. Kristensen <hoegsberg@chromium.org>
Signed-off-by: Rob Clark <robdclark@chromium.org>
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.h | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gem.c | 35 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gem.h | 3 |
4 files changed, 46 insertions, 2 deletions
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 37f18b55d11f..154dab0775f3 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -458,6 +458,9 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) priv->wq = alloc_ordered_workqueue("msm", 0); + INIT_WORK(&priv->free_work, msm_gem_free_work); + init_llist_head(&priv->free_list); + INIT_LIST_HEAD(&priv->inactive_list); drm_mode_config_init(ddev); @@ -1026,7 +1029,7 @@ static struct drm_driver msm_driver = { .irq_uninstall = msm_irq_uninstall, .enable_vblank = msm_enable_vblank, .disable_vblank = msm_disable_vblank, - .gem_free_object = msm_gem_free_object, + .gem_free_object_unlocked = msm_gem_free_object, .gem_vm_ops = &vm_ops, .dumb_create = msm_gem_dumb_create, .dumb_map_offset = msm_gem_dumb_map_offset, diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index f09d12ddb3ac..bbf739d85381 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -185,6 +185,10 @@ struct msm_drm_private { /* list of GEM objects: */ struct list_head inactive_list; + /* worker for delayed free of objects: */ + struct work_struct free_work; + struct llist_head free_list; + struct workqueue_struct *wq; unsigned int num_planes; @@ -325,6 +329,7 @@ void msm_gem_kernel_put(struct drm_gem_object *bo, struct msm_gem_address_space *aspace, bool locked); struct drm_gem_object *msm_gem_import(struct drm_device *dev, struct dma_buf *dmabuf, struct sg_table *sgt); +void msm_gem_free_work(struct work_struct *work); __printf(2, 3) void msm_gem_object_set_name(struct drm_gem_object *bo, const char *fmt, ...); diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 76940a9da980..d4dae0f0238e 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -853,8 +853,18 @@ void msm_gem_describe_objects(struct list_head *list, struct seq_file *m) /* don't call directly! Use drm_gem_object_put() and friends */ void msm_gem_free_object(struct drm_gem_object *obj) { - struct drm_device *dev = obj->dev; struct msm_gem_object *msm_obj = to_msm_bo(obj); + struct drm_device *dev = obj->dev; + struct msm_drm_private *priv = dev->dev_private; + + if (llist_add(&msm_obj->freed, &priv->free_list)) + queue_work(priv->wq, &priv->free_work); +} + +static void free_object(struct msm_gem_object *msm_obj) +{ + struct drm_gem_object *obj = &msm_obj->base; + struct drm_device *dev = obj->dev; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); @@ -892,6 +902,29 @@ void msm_gem_free_object(struct drm_gem_object *obj) kfree(msm_obj); } +void msm_gem_free_work(struct work_struct *work) +{ + struct msm_drm_private *priv = + container_of(work, struct msm_drm_private, free_work); + struct drm_device *dev = priv->dev; + struct llist_node *freed; + struct msm_gem_object *msm_obj, *next; + + while ((freed = llist_del_all(&priv->free_list))) { + + mutex_lock(&dev->struct_mutex); + + llist_for_each_entry_safe(msm_obj, next, + freed, freed) + free_object(msm_obj); + + mutex_unlock(&dev->struct_mutex); + + if (need_resched()) + break; + } +} + /* convenience method to construct a GEM buffer object, and userspace handle */ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, uint32_t size, uint32_t flags, uint32_t *handle, diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 2064fac871b8..0617c44d99b0 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -84,6 +84,8 @@ struct msm_gem_object { struct list_head vmas; /* list of msm_gem_vma */ + struct llist_node freed; + /* normally (resv == &_resv) except for imported bo's */ struct reservation_object *resv; struct reservation_object _resv; @@ -133,6 +135,7 @@ enum msm_gem_lock { void msm_gem_purge(struct drm_gem_object *obj, enum msm_gem_lock subclass); void msm_gem_vunmap(struct drm_gem_object *obj, enum msm_gem_lock subclass); +void msm_gem_free_work(struct work_struct *work); /* Created per submit-ioctl, to track bo's and cmdstream bufs, etc, * associated with the cmdstream submission for synchronization (and |