Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/exynos: use a new anon file for exynos gem mmaper

This patch resolves potential deadlock issue that can be incurred
by changing file->f_op and filp->private_data to exynos specific
mapper ops and gem object temporarily.

To resolve this issue, this patch creates a new anon file dedicated
to exynos specific mmaper, and making it used instead of existing one.

Signed-off-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

Inki Dae 96f54215 fe3c703c

+38 -61
+21
drivers/gpu/drm/exynos/exynos_drm_drv.c
··· 14 14 #include <drm/drmP.h> 15 15 #include <drm/drm_crtc_helper.h> 16 16 17 + #include <linux/anon_inodes.h> 18 + 17 19 #include <drm/exynos_drm.h> 18 20 19 21 #include "exynos_drm_drv.h" ··· 154 152 return 0; 155 153 } 156 154 155 + static const struct file_operations exynos_drm_gem_fops = { 156 + .mmap = exynos_drm_gem_mmap_buffer, 157 + }; 158 + 157 159 static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) 158 160 { 159 161 struct drm_exynos_file_private *file_priv; 162 + struct file *anon_filp; 160 163 int ret; 161 164 162 165 file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); ··· 176 169 file->driver_priv = NULL; 177 170 } 178 171 172 + anon_filp = anon_inode_getfile("exynos_gem", &exynos_drm_gem_fops, 173 + NULL, 0); 174 + if (IS_ERR(anon_filp)) { 175 + kfree(file_priv); 176 + return PTR_ERR(anon_filp); 177 + } 178 + 179 + anon_filp->f_mode = FMODE_READ | FMODE_WRITE; 180 + file_priv->anon_filp = anon_filp; 181 + 179 182 return ret; 180 183 } 181 184 ··· 198 181 static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) 199 182 { 200 183 struct exynos_drm_private *private = dev->dev_private; 184 + struct drm_exynos_file_private *file_priv; 201 185 struct drm_pending_vblank_event *v, *vt; 202 186 struct drm_pending_event *e, *et; 203 187 unsigned long flags; ··· 224 206 } 225 207 spin_unlock_irqrestore(&dev->event_lock, flags); 226 208 209 + file_priv = file->driver_priv; 210 + if (file_priv->anon_filp) 211 + fput(file_priv->anon_filp); 227 212 228 213 kfree(file->driver_priv); 229 214 file->driver_priv = NULL;
+1
drivers/gpu/drm/exynos/exynos_drm_drv.h
··· 226 226 struct drm_exynos_file_private { 227 227 struct exynos_drm_g2d_private *g2d_priv; 228 228 struct exynos_drm_ipp_private *ipp_priv; 229 + struct file *anon_filp; 229 230 }; 230 231 231 232 /*
+13 -61
drivers/gpu/drm/exynos/exynos_drm_gem.c
··· 338 338 &args->offset); 339 339 } 340 340 341 - static struct drm_file *exynos_drm_find_drm_file(struct drm_device *drm_dev, 342 - struct file *filp) 343 - { 344 - struct drm_file *file_priv; 345 - 346 - /* find current process's drm_file from filelist. */ 347 - list_for_each_entry(file_priv, &drm_dev->filelist, lhead) 348 - if (file_priv->filp == filp) 349 - return file_priv; 350 - 351 - WARN_ON(1); 352 - 353 - return ERR_PTR(-EFAULT); 354 - } 355 - 356 - static int exynos_drm_gem_mmap_buffer(struct file *filp, 341 + int exynos_drm_gem_mmap_buffer(struct file *filp, 357 342 struct vm_area_struct *vma) 358 343 { 359 344 struct drm_gem_object *obj = filp->private_data; 360 345 struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); 361 346 struct drm_device *drm_dev = obj->dev; 362 347 struct exynos_drm_gem_buf *buffer; 363 - struct drm_file *file_priv; 364 348 unsigned long vm_size; 365 349 int ret; 350 + 351 + WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex)); 366 352 367 353 vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; 368 354 vma->vm_private_data = obj; 369 355 vma->vm_ops = drm_dev->driver->gem_vm_ops; 370 - 371 - /* restore it to driver's fops. */ 372 - filp->f_op = fops_get(drm_dev->driver->fops); 373 - 374 - file_priv = exynos_drm_find_drm_file(drm_dev, filp); 375 - if (IS_ERR(file_priv)) 376 - return PTR_ERR(file_priv); 377 - 378 - /* restore it to drm_file. */ 379 - filp->private_data = file_priv; 380 356 381 357 update_vm_cache_attr(exynos_gem_obj, vma); 382 358 ··· 387 411 return 0; 388 412 } 389 413 390 - static const struct file_operations exynos_drm_gem_fops = { 391 - .mmap = exynos_drm_gem_mmap_buffer, 392 - }; 393 - 394 414 int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, 395 415 struct drm_file *file_priv) 396 416 { 417 + struct drm_exynos_file_private *exynos_file_priv; 397 418 struct drm_exynos_gem_mmap *args = data; 398 419 struct drm_gem_object *obj; 420 + struct file *anon_filp; 399 421 unsigned long addr; 400 422 401 423 if (!(dev->driver->driver_features & DRIVER_GEM)) { ··· 401 427 return -ENODEV; 402 428 } 403 429 430 + mutex_lock(&dev->struct_mutex); 431 + 404 432 obj = drm_gem_object_lookup(dev, file_priv, args->handle); 405 433 if (!obj) { 406 434 DRM_ERROR("failed to lookup gem object.\n"); 435 + mutex_unlock(&dev->struct_mutex); 407 436 return -EINVAL; 408 437 } 409 438 410 - /* 411 - * We have to use gem object and its fops for specific mmaper, 412 - * but vm_mmap() can deliver only filp. So we have to change 413 - * filp->f_op and filp->private_data temporarily, then restore 414 - * again. So it is important to keep lock until restoration the 415 - * settings to prevent others from misuse of filp->f_op or 416 - * filp->private_data. 417 - */ 418 - mutex_lock(&dev->struct_mutex); 439 + exynos_file_priv = file_priv->driver_priv; 440 + anon_filp = exynos_file_priv->anon_filp; 441 + anon_filp->private_data = obj; 419 442 420 - /* 421 - * Set specific mmper's fops. And it will be restored by 422 - * exynos_drm_gem_mmap_buffer to dev->driver->fops. 423 - * This is used to call specific mapper temporarily. 424 - */ 425 - file_priv->filp->f_op = &exynos_drm_gem_fops; 426 - 427 - /* 428 - * Set gem object to private_data so that specific mmaper 429 - * can get the gem object. And it will be restored by 430 - * exynos_drm_gem_mmap_buffer to drm_file. 431 - */ 432 - file_priv->filp->private_data = obj; 433 - 434 - addr = vm_mmap(file_priv->filp, 0, args->size, 435 - PROT_READ | PROT_WRITE, MAP_SHARED, 0); 443 + addr = vm_mmap(anon_filp, 0, args->size, PROT_READ | PROT_WRITE, 444 + MAP_SHARED, 0); 436 445 437 446 drm_gem_object_unreference(obj); 438 447 439 448 if (IS_ERR_VALUE(addr)) { 440 - /* check filp->f_op, filp->private_data are restored */ 441 - if (file_priv->filp->f_op == &exynos_drm_gem_fops) { 442 - file_priv->filp->f_op = fops_get(dev->driver->fops); 443 - file_priv->filp->private_data = file_priv; 444 - } 445 449 mutex_unlock(&dev->struct_mutex); 446 450 return (int)addr; 447 451 }
+3
drivers/gpu/drm/exynos/exynos_drm_gem.h
··· 122 122 int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, 123 123 struct drm_file *file_priv); 124 124 125 + int exynos_drm_gem_mmap_buffer(struct file *filp, 126 + struct vm_area_struct *vma); 127 + 125 128 /* map user space allocated by malloc to pages. */ 126 129 int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, 127 130 struct drm_file *file_priv);