diff --git a/sys/dev/drm/drm_gem.c b/sys/dev/drm/drm_gem.c --- a/sys/dev/drm/drm_gem.c +++ b/sys/dev/drm/drm_gem.c @@ -529,18 +529,24 @@ EXPORT_SYMBOL(drm_gem_create_mmap_offset_size); void drm_gem_free_mmap_offset(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; struct drm_gem_mm *mm = dev->mm_private; struct drm_hash_item *list; + /* Serialize against concurrent drm_gem_create_mmap_offset*(). */ + mutex_lock(&dev->object_name_lock); if (!obj->on_map) - return; + goto out; list = &obj->map_list; drm_ht_remove_item(&mm->offset_hash, list); free_unr(mm->idxunr, list->key); obj->on_map = false; drm_vma_offset_remove(dev->vma_offset_manager, &obj->vma_node); +out: + mutex_unlock(&dev->object_name_lock); } EXPORT_SYMBOL(drm_gem_free_mmap_offset); /** @@ -567,19 +573,28 @@ EXPORT_SYMBOL(drm_gem_free_mmap_offset); int drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size) { struct drm_device *dev = obj->dev; struct drm_gem_mm *mm = dev->mm_private; int ret = 0; + /* + * The legacy offset_hash/idxunr publication is not RCU-safe on + * DragonFly and is consumed under DRM_LOCK by + * drm_gem_object_from_offset(). Two DRM_UNLOCKED MAP_DUMB (or + * i915 MMAP_GTT) calls race here otherwise. object_name_lock is + * LK_CANRECURSE and sleepable (alloc_unr may sleep). + */ + mutex_lock(&dev->object_name_lock); if (obj->on_map) - return (0); + goto out_unlocked_vma; obj->map_list.key = alloc_unr(mm->idxunr); ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list); if (ret != 0) { DRM_ERROR("failed to add to map hash\n"); free_unr(mm->idxunr, obj->map_list.key); - return (ret); + mutex_unlock(&dev->object_name_lock); + return (ret); } obj->on_map = true; + mutex_unlock(&dev->object_name_lock); return drm_vma_offset_add(dev->vma_offset_manager, &obj->vma_node, size / PAGE_SIZE); +out_unlocked_vma: + mutex_unlock(&dev->object_name_lock); + return (0); } EXPORT_SYMBOL(drm_gem_create_mmap_offset_size);