aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorInki Dae <inki.dae@samsung.com>2013-12-20 05:16:24 -0500
committerInki Dae <inki.dae@samsung.com>2014-01-07 02:51:41 -0500
commit96f5421523dfdcfb67e66e0f51c4b64d2c12137c (patch)
tree6cbb770065605940e8d1d89b902c198a8dff4fb5
parentfe3c703c3d80bd4c2da0d47a7d56930926af7cbc (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>
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c21
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.c74
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.h3
4 files changed, 38 insertions, 61 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 7e074a4d2848..9d096a0c5f8d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -14,6 +14,8 @@
14#include <drm/drmP.h> 14#include <drm/drmP.h>
15#include <drm/drm_crtc_helper.h> 15#include <drm/drm_crtc_helper.h>
16 16
17#include <linux/anon_inodes.h>
18
17#include <drm/exynos_drm.h> 19#include <drm/exynos_drm.h>
18 20
19#include "exynos_drm_drv.h" 21#include "exynos_drm_drv.h"
@@ -152,9 +154,14 @@ static int exynos_drm_unload(struct drm_device *dev)
152 return 0; 154 return 0;
153} 155}
154 156
157static const struct file_operations exynos_drm_gem_fops = {
158 .mmap = exynos_drm_gem_mmap_buffer,
159};
160
155static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) 161static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
156{ 162{
157 struct drm_exynos_file_private *file_priv; 163 struct drm_exynos_file_private *file_priv;
164 struct file *anon_filp;
158 int ret; 165 int ret;
159 166
160 file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); 167 file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
@@ -169,6 +176,16 @@ static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
169 file->driver_priv = NULL; 176 file->driver_priv = NULL;
170 } 177 }
171 178
179 anon_filp = anon_inode_getfile("exynos_gem", &exynos_drm_gem_fops,
180 NULL, 0);
181 if (IS_ERR(anon_filp)) {
182 kfree(file_priv);
183 return PTR_ERR(anon_filp);
184 }
185
186 anon_filp->f_mode = FMODE_READ | FMODE_WRITE;
187 file_priv->anon_filp = anon_filp;
188
172 return ret; 189 return ret;
173} 190}
174 191
@@ -181,6 +198,7 @@ static void exynos_drm_preclose(struct drm_device *dev,
181static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) 198static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
182{ 199{
183 struct exynos_drm_private *private = dev->dev_private; 200 struct exynos_drm_private *private = dev->dev_private;
201 struct drm_exynos_file_private *file_priv;
184 struct drm_pending_vblank_event *v, *vt; 202 struct drm_pending_vblank_event *v, *vt;
185 struct drm_pending_event *e, *et; 203 struct drm_pending_event *e, *et;
186 unsigned long flags; 204 unsigned long flags;
@@ -206,6 +224,9 @@ static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
206 } 224 }
207 spin_unlock_irqrestore(&dev->event_lock, flags); 225 spin_unlock_irqrestore(&dev->event_lock, flags);
208 226
227 file_priv = file->driver_priv;
228 if (file_priv->anon_filp)
229 fput(file_priv->anon_filp);
209 230
210 kfree(file->driver_priv); 231 kfree(file->driver_priv);
211 file->driver_priv = NULL; 232 file->driver_priv = NULL;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index eaa19668bf00..0eaf5a27e120 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -226,6 +226,7 @@ struct exynos_drm_ipp_private {
226struct drm_exynos_file_private { 226struct drm_exynos_file_private {
227 struct exynos_drm_g2d_private *g2d_priv; 227 struct exynos_drm_g2d_private *g2d_priv;
228 struct exynos_drm_ipp_private *ipp_priv; 228 struct exynos_drm_ipp_private *ipp_priv;
229 struct file *anon_filp;
229}; 230};
230 231
231/* 232/*
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
341static struct drm_file *exynos_drm_find_drm_file(struct drm_device *drm_dev, 341int 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
356static 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
414static const struct file_operations exynos_drm_gem_fops = {
415 .mmap = exynos_drm_gem_mmap_buffer,
416};
417
418int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, 390int 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 }
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h
index 702ec3abe85c..fde860c7eba3 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h
@@ -122,6 +122,9 @@ int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
122int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, 122int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
123 struct drm_file *file_priv); 123 struct drm_file *file_priv);
124 124
125int exynos_drm_gem_mmap_buffer(struct file *filp,
126 struct vm_area_struct *vma);
127
125/* map user space allocated by malloc to pages. */ 128/* map user space allocated by malloc to pages. */
126int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, 129int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data,
127 struct drm_file *file_priv); 130 struct drm_file *file_priv);