diff options
author | Inki Dae <inki.dae@samsung.com> | 2013-12-20 05:16:24 -0500 |
---|---|---|
committer | Inki Dae <inki.dae@samsung.com> | 2014-01-07 02:51:41 -0500 |
commit | 96f5421523dfdcfb67e66e0f51c4b64d2c12137c (patch) | |
tree | 6cbb770065605940e8d1d89b902c198a8dff4fb5 /drivers/gpu/drm/exynos/exynos_drm_gem.c | |
parent | fe3c703c3d80bd4c2da0d47a7d56930926af7cbc (diff) |
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>
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_gem.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_gem.c | 74 |
1 files changed, 13 insertions, 61 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 1ade191d84f4..49b8c9b22902 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c | |||
@@ -338,46 +338,22 @@ int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, | |||
338 | &args->offset); | 338 | &args->offset); |
339 | } | 339 | } |
340 | 340 | ||
341 | static struct drm_file *exynos_drm_find_drm_file(struct drm_device *drm_dev, | 341 | int exynos_drm_gem_mmap_buffer(struct file *filp, |
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, | ||
357 | struct vm_area_struct *vma) | 342 | struct vm_area_struct *vma) |
358 | { | 343 | { |
359 | struct drm_gem_object *obj = filp->private_data; | 344 | struct drm_gem_object *obj = filp->private_data; |
360 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); | 345 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); |
361 | struct drm_device *drm_dev = obj->dev; | 346 | struct drm_device *drm_dev = obj->dev; |
362 | struct exynos_drm_gem_buf *buffer; | 347 | struct exynos_drm_gem_buf *buffer; |
363 | struct drm_file *file_priv; | ||
364 | unsigned long vm_size; | 348 | unsigned long vm_size; |
365 | int ret; | 349 | int ret; |
366 | 350 | ||
351 | WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex)); | ||
352 | |||
367 | vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; | 353 | vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; |
368 | vma->vm_private_data = obj; | 354 | vma->vm_private_data = obj; |
369 | vma->vm_ops = drm_dev->driver->gem_vm_ops; | 355 | vma->vm_ops = drm_dev->driver->gem_vm_ops; |
370 | 356 | ||
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 | |||
381 | update_vm_cache_attr(exynos_gem_obj, vma); | 357 | update_vm_cache_attr(exynos_gem_obj, vma); |
382 | 358 | ||
383 | vm_size = vma->vm_end - vma->vm_start; | 359 | vm_size = vma->vm_end - vma->vm_start; |
@@ -411,15 +387,13 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, | |||
411 | return 0; | 387 | return 0; |
412 | } | 388 | } |
413 | 389 | ||
414 | static const struct file_operations exynos_drm_gem_fops = { | ||
415 | .mmap = exynos_drm_gem_mmap_buffer, | ||
416 | }; | ||
417 | |||
418 | int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, | 390 | int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, |
419 | struct drm_file *file_priv) | 391 | struct drm_file *file_priv) |
420 | { | 392 | { |
393 | struct drm_exynos_file_private *exynos_file_priv; | ||
421 | struct drm_exynos_gem_mmap *args = data; | 394 | struct drm_exynos_gem_mmap *args = data; |
422 | struct drm_gem_object *obj; | 395 | struct drm_gem_object *obj; |
396 | struct file *anon_filp; | ||
423 | unsigned long addr; | 397 | unsigned long addr; |
424 | 398 | ||
425 | if (!(dev->driver->driver_features & DRIVER_GEM)) { | 399 | if (!(dev->driver->driver_features & DRIVER_GEM)) { |
@@ -427,47 +401,25 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, | |||
427 | return -ENODEV; | 401 | return -ENODEV; |
428 | } | 402 | } |
429 | 403 | ||
404 | mutex_lock(&dev->struct_mutex); | ||
405 | |||
430 | obj = drm_gem_object_lookup(dev, file_priv, args->handle); | 406 | obj = drm_gem_object_lookup(dev, file_priv, args->handle); |
431 | if (!obj) { | 407 | if (!obj) { |
432 | DRM_ERROR("failed to lookup gem object.\n"); | 408 | DRM_ERROR("failed to lookup gem object.\n"); |
409 | mutex_unlock(&dev->struct_mutex); | ||
433 | return -EINVAL; | 410 | return -EINVAL; |
434 | } | 411 | } |
435 | 412 | ||
436 | /* | 413 | exynos_file_priv = file_priv->driver_priv; |
437 | * We have to use gem object and its fops for specific mmaper, | 414 | anon_filp = exynos_file_priv->anon_filp; |
438 | * but vm_mmap() can deliver only filp. So we have to change | 415 | anon_filp->private_data = obj; |
439 | * filp->f_op and filp->private_data temporarily, then restore | ||
440 | * again. So it is important to keep lock until restoration the | ||
441 | * settings to prevent others from misuse of filp->f_op or | ||
442 | * filp->private_data. | ||
443 | */ | ||
444 | mutex_lock(&dev->struct_mutex); | ||
445 | |||
446 | /* | ||
447 | * Set specific mmper's fops. And it will be restored by | ||
448 | * exynos_drm_gem_mmap_buffer to dev->driver->fops. | ||
449 | * This is used to call specific mapper temporarily. | ||
450 | */ | ||
451 | file_priv->filp->f_op = &exynos_drm_gem_fops; | ||
452 | |||
453 | /* | ||
454 | * Set gem object to private_data so that specific mmaper | ||
455 | * can get the gem object. And it will be restored by | ||
456 | * exynos_drm_gem_mmap_buffer to drm_file. | ||
457 | */ | ||
458 | file_priv->filp->private_data = obj; | ||
459 | 416 | ||
460 | addr = vm_mmap(file_priv->filp, 0, args->size, | 417 | addr = vm_mmap(anon_filp, 0, args->size, PROT_READ | PROT_WRITE, |
461 | PROT_READ | PROT_WRITE, MAP_SHARED, 0); | 418 | MAP_SHARED, 0); |
462 | 419 | ||
463 | drm_gem_object_unreference(obj); | 420 | drm_gem_object_unreference(obj); |
464 | 421 | ||
465 | if (IS_ERR_VALUE(addr)) { | 422 | if (IS_ERR_VALUE(addr)) { |
466 | /* check filp->f_op, filp->private_data are restored */ | ||
467 | if (file_priv->filp->f_op == &exynos_drm_gem_fops) { | ||
468 | file_priv->filp->f_op = fops_get(dev->driver->fops); | ||
469 | file_priv->filp->private_data = file_priv; | ||
470 | } | ||
471 | mutex_unlock(&dev->struct_mutex); | 423 | mutex_unlock(&dev->struct_mutex); |
472 | return (int)addr; | 424 | return (int)addr; |
473 | } | 425 | } |