diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-10-04 20:23:58 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-10-04 20:23:58 -0400 |
commit | befad944e2312c18d855013ce154ca7d2b110ade (patch) | |
tree | 544f38cb6d0a2a35794a33cf9e37396787e7bea1 | |
parent | 010bd965f9711f801f2902ec3a6a2826c6656491 (diff) | |
parent | bdf800c6fdf5674999bc0228d5040cc0ae218fa8 (diff) |
Merge tag 'drm-fixes-2018-10-05' of git://anongit.freedesktop.org/drm/drm
Dave writes:
"amdgpu and two core fixes
Two fixes for amdgpu:
one corrects a use of process->mm
one fix for display code race condition that can result in a crash
Two core fixes:
One for a use-after-free in the leasing code
One for a cma/fbdev crash."
* tag 'drm-fixes-2018-10-05' of git://anongit.freedesktop.org/drm/drm:
drm/amdkfd: Fix incorrect use of process->mm
drm/amd/display: Signal hw_done() after waiting for flip_done()
drm/cma-helper: Fix crash in fbdev error path
drm: fix use-after-free read in drm_mode_create_lease_ioctl()
-rw-r--r-- | drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 37 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_client.c | 35 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_fb_cma_helper.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_lease.c | 6 | ||||
-rw-r--r-- | include/drm/drm_client.h | 5 |
7 files changed, 75 insertions, 26 deletions
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index ec0d62a16e53..4f22e745df51 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | |||
@@ -358,8 +358,8 @@ static int create_compute_queue_nocpsch(struct device_queue_manager *dqm, | |||
358 | struct queue *q, | 358 | struct queue *q, |
359 | struct qcm_process_device *qpd) | 359 | struct qcm_process_device *qpd) |
360 | { | 360 | { |
361 | int retval; | ||
362 | struct mqd_manager *mqd_mgr; | 361 | struct mqd_manager *mqd_mgr; |
362 | int retval; | ||
363 | 363 | ||
364 | mqd_mgr = dqm->ops.get_mqd_manager(dqm, KFD_MQD_TYPE_COMPUTE); | 364 | mqd_mgr = dqm->ops.get_mqd_manager(dqm, KFD_MQD_TYPE_COMPUTE); |
365 | if (!mqd_mgr) | 365 | if (!mqd_mgr) |
@@ -387,8 +387,12 @@ static int create_compute_queue_nocpsch(struct device_queue_manager *dqm, | |||
387 | if (!q->properties.is_active) | 387 | if (!q->properties.is_active) |
388 | return 0; | 388 | return 0; |
389 | 389 | ||
390 | retval = mqd_mgr->load_mqd(mqd_mgr, q->mqd, q->pipe, q->queue, | 390 | if (WARN(q->process->mm != current->mm, |
391 | &q->properties, q->process->mm); | 391 | "should only run in user thread")) |
392 | retval = -EFAULT; | ||
393 | else | ||
394 | retval = mqd_mgr->load_mqd(mqd_mgr, q->mqd, q->pipe, q->queue, | ||
395 | &q->properties, current->mm); | ||
392 | if (retval) | 396 | if (retval) |
393 | goto out_uninit_mqd; | 397 | goto out_uninit_mqd; |
394 | 398 | ||
@@ -545,9 +549,15 @@ static int update_queue(struct device_queue_manager *dqm, struct queue *q) | |||
545 | retval = map_queues_cpsch(dqm); | 549 | retval = map_queues_cpsch(dqm); |
546 | else if (q->properties.is_active && | 550 | else if (q->properties.is_active && |
547 | (q->properties.type == KFD_QUEUE_TYPE_COMPUTE || | 551 | (q->properties.type == KFD_QUEUE_TYPE_COMPUTE || |
548 | q->properties.type == KFD_QUEUE_TYPE_SDMA)) | 552 | q->properties.type == KFD_QUEUE_TYPE_SDMA)) { |
549 | retval = mqd_mgr->load_mqd(mqd_mgr, q->mqd, q->pipe, q->queue, | 553 | if (WARN(q->process->mm != current->mm, |
550 | &q->properties, q->process->mm); | 554 | "should only run in user thread")) |
555 | retval = -EFAULT; | ||
556 | else | ||
557 | retval = mqd_mgr->load_mqd(mqd_mgr, q->mqd, | ||
558 | q->pipe, q->queue, | ||
559 | &q->properties, current->mm); | ||
560 | } | ||
551 | 561 | ||
552 | out_unlock: | 562 | out_unlock: |
553 | dqm_unlock(dqm); | 563 | dqm_unlock(dqm); |
@@ -653,6 +663,7 @@ out: | |||
653 | static int restore_process_queues_nocpsch(struct device_queue_manager *dqm, | 663 | static int restore_process_queues_nocpsch(struct device_queue_manager *dqm, |
654 | struct qcm_process_device *qpd) | 664 | struct qcm_process_device *qpd) |
655 | { | 665 | { |
666 | struct mm_struct *mm = NULL; | ||
656 | struct queue *q; | 667 | struct queue *q; |
657 | struct mqd_manager *mqd_mgr; | 668 | struct mqd_manager *mqd_mgr; |
658 | struct kfd_process_device *pdd; | 669 | struct kfd_process_device *pdd; |
@@ -686,6 +697,15 @@ static int restore_process_queues_nocpsch(struct device_queue_manager *dqm, | |||
686 | kfd_flush_tlb(pdd); | 697 | kfd_flush_tlb(pdd); |
687 | } | 698 | } |
688 | 699 | ||
700 | /* Take a safe reference to the mm_struct, which may otherwise | ||
701 | * disappear even while the kfd_process is still referenced. | ||
702 | */ | ||
703 | mm = get_task_mm(pdd->process->lead_thread); | ||
704 | if (!mm) { | ||
705 | retval = -EFAULT; | ||
706 | goto out; | ||
707 | } | ||
708 | |||
689 | /* activate all active queues on the qpd */ | 709 | /* activate all active queues on the qpd */ |
690 | list_for_each_entry(q, &qpd->queues_list, list) { | 710 | list_for_each_entry(q, &qpd->queues_list, list) { |
691 | if (!q->properties.is_evicted) | 711 | if (!q->properties.is_evicted) |
@@ -700,14 +720,15 @@ static int restore_process_queues_nocpsch(struct device_queue_manager *dqm, | |||
700 | q->properties.is_evicted = false; | 720 | q->properties.is_evicted = false; |
701 | q->properties.is_active = true; | 721 | q->properties.is_active = true; |
702 | retval = mqd_mgr->load_mqd(mqd_mgr, q->mqd, q->pipe, | 722 | retval = mqd_mgr->load_mqd(mqd_mgr, q->mqd, q->pipe, |
703 | q->queue, &q->properties, | 723 | q->queue, &q->properties, mm); |
704 | q->process->mm); | ||
705 | if (retval) | 724 | if (retval) |
706 | goto out; | 725 | goto out; |
707 | dqm->queue_count++; | 726 | dqm->queue_count++; |
708 | } | 727 | } |
709 | qpd->evicted = 0; | 728 | qpd->evicted = 0; |
710 | out: | 729 | out: |
730 | if (mm) | ||
731 | mmput(mm); | ||
711 | dqm_unlock(dqm); | 732 | dqm_unlock(dqm); |
712 | return retval; | 733 | return retval; |
713 | } | 734 | } |
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 96875950845a..6903fe6c894b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | |||
@@ -4633,12 +4633,18 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) | |||
4633 | } | 4633 | } |
4634 | spin_unlock_irqrestore(&adev->ddev->event_lock, flags); | 4634 | spin_unlock_irqrestore(&adev->ddev->event_lock, flags); |
4635 | 4635 | ||
4636 | /* Signal HW programming completion */ | ||
4637 | drm_atomic_helper_commit_hw_done(state); | ||
4638 | 4636 | ||
4639 | if (wait_for_vblank) | 4637 | if (wait_for_vblank) |
4640 | drm_atomic_helper_wait_for_flip_done(dev, state); | 4638 | drm_atomic_helper_wait_for_flip_done(dev, state); |
4641 | 4639 | ||
4640 | /* | ||
4641 | * FIXME: | ||
4642 | * Delay hw_done() until flip_done() is signaled. This is to block | ||
4643 | * another commit from freeing the CRTC state while we're still | ||
4644 | * waiting on flip_done. | ||
4645 | */ | ||
4646 | drm_atomic_helper_commit_hw_done(state); | ||
4647 | |||
4642 | drm_atomic_helper_cleanup_planes(dev, state); | 4648 | drm_atomic_helper_cleanup_planes(dev, state); |
4643 | 4649 | ||
4644 | /* Finally, drop a runtime PM reference for each newly disabled CRTC, | 4650 | /* Finally, drop a runtime PM reference for each newly disabled CRTC, |
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index baff50a4c234..df31c3815092 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c | |||
@@ -63,20 +63,21 @@ static void drm_client_close(struct drm_client_dev *client) | |||
63 | EXPORT_SYMBOL(drm_client_close); | 63 | EXPORT_SYMBOL(drm_client_close); |
64 | 64 | ||
65 | /** | 65 | /** |
66 | * drm_client_new - Create a DRM client | 66 | * drm_client_init - Initialise a DRM client |
67 | * @dev: DRM device | 67 | * @dev: DRM device |
68 | * @client: DRM client | 68 | * @client: DRM client |
69 | * @name: Client name | 69 | * @name: Client name |
70 | * @funcs: DRM client functions (optional) | 70 | * @funcs: DRM client functions (optional) |
71 | * | 71 | * |
72 | * This initialises the client and opens a &drm_file. Use drm_client_add() to complete the process. | ||
72 | * The caller needs to hold a reference on @dev before calling this function. | 73 | * The caller needs to hold a reference on @dev before calling this function. |
73 | * The client is freed when the &drm_device is unregistered. See drm_client_release(). | 74 | * The client is freed when the &drm_device is unregistered. See drm_client_release(). |
74 | * | 75 | * |
75 | * Returns: | 76 | * Returns: |
76 | * Zero on success or negative error code on failure. | 77 | * Zero on success or negative error code on failure. |
77 | */ | 78 | */ |
78 | int drm_client_new(struct drm_device *dev, struct drm_client_dev *client, | 79 | int drm_client_init(struct drm_device *dev, struct drm_client_dev *client, |
79 | const char *name, const struct drm_client_funcs *funcs) | 80 | const char *name, const struct drm_client_funcs *funcs) |
80 | { | 81 | { |
81 | int ret; | 82 | int ret; |
82 | 83 | ||
@@ -95,10 +96,6 @@ int drm_client_new(struct drm_device *dev, struct drm_client_dev *client, | |||
95 | if (ret) | 96 | if (ret) |
96 | goto err_put_module; | 97 | goto err_put_module; |
97 | 98 | ||
98 | mutex_lock(&dev->clientlist_mutex); | ||
99 | list_add(&client->list, &dev->clientlist); | ||
100 | mutex_unlock(&dev->clientlist_mutex); | ||
101 | |||
102 | drm_dev_get(dev); | 99 | drm_dev_get(dev); |
103 | 100 | ||
104 | return 0; | 101 | return 0; |
@@ -109,13 +106,33 @@ err_put_module: | |||
109 | 106 | ||
110 | return ret; | 107 | return ret; |
111 | } | 108 | } |
112 | EXPORT_SYMBOL(drm_client_new); | 109 | EXPORT_SYMBOL(drm_client_init); |
110 | |||
111 | /** | ||
112 | * drm_client_add - Add client to the device list | ||
113 | * @client: DRM client | ||
114 | * | ||
115 | * Add the client to the &drm_device client list to activate its callbacks. | ||
116 | * @client must be initialized by a call to drm_client_init(). After | ||
117 | * drm_client_add() it is no longer permissible to call drm_client_release() | ||
118 | * directly (outside the unregister callback), instead cleanup will happen | ||
119 | * automatically on driver unload. | ||
120 | */ | ||
121 | void drm_client_add(struct drm_client_dev *client) | ||
122 | { | ||
123 | struct drm_device *dev = client->dev; | ||
124 | |||
125 | mutex_lock(&dev->clientlist_mutex); | ||
126 | list_add(&client->list, &dev->clientlist); | ||
127 | mutex_unlock(&dev->clientlist_mutex); | ||
128 | } | ||
129 | EXPORT_SYMBOL(drm_client_add); | ||
113 | 130 | ||
114 | /** | 131 | /** |
115 | * drm_client_release - Release DRM client resources | 132 | * drm_client_release - Release DRM client resources |
116 | * @client: DRM client | 133 | * @client: DRM client |
117 | * | 134 | * |
118 | * Releases resources by closing the &drm_file that was opened by drm_client_new(). | 135 | * Releases resources by closing the &drm_file that was opened by drm_client_init(). |
119 | * It is called automatically if the &drm_client_funcs.unregister callback is _not_ set. | 136 | * It is called automatically if the &drm_client_funcs.unregister callback is _not_ set. |
120 | * | 137 | * |
121 | * This function should only be called from the unregister callback. An exception | 138 | * This function should only be called from the unregister callback. An exception |
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 9da36a6271d3..9ac1f2e0f064 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c | |||
@@ -160,7 +160,7 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, | |||
160 | 160 | ||
161 | fb_helper = &fbdev_cma->fb_helper; | 161 | fb_helper = &fbdev_cma->fb_helper; |
162 | 162 | ||
163 | ret = drm_client_new(dev, &fb_helper->client, "fbdev", NULL); | 163 | ret = drm_client_init(dev, &fb_helper->client, "fbdev", NULL); |
164 | if (ret) | 164 | if (ret) |
165 | goto err_free; | 165 | goto err_free; |
166 | 166 | ||
@@ -169,6 +169,8 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, | |||
169 | if (ret) | 169 | if (ret) |
170 | goto err_client_put; | 170 | goto err_client_put; |
171 | 171 | ||
172 | drm_client_add(&fb_helper->client); | ||
173 | |||
172 | return fbdev_cma; | 174 | return fbdev_cma; |
173 | 175 | ||
174 | err_client_put: | 176 | err_client_put: |
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 16ec93b75dbf..515a7aec57ac 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c | |||
@@ -3218,12 +3218,14 @@ int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) | |||
3218 | if (!fb_helper) | 3218 | if (!fb_helper) |
3219 | return -ENOMEM; | 3219 | return -ENOMEM; |
3220 | 3220 | ||
3221 | ret = drm_client_new(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs); | 3221 | ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs); |
3222 | if (ret) { | 3222 | if (ret) { |
3223 | kfree(fb_helper); | 3223 | kfree(fb_helper); |
3224 | return ret; | 3224 | return ret; |
3225 | } | 3225 | } |
3226 | 3226 | ||
3227 | drm_client_add(&fb_helper->client); | ||
3228 | |||
3227 | fb_helper->preferred_bpp = preferred_bpp; | 3229 | fb_helper->preferred_bpp = preferred_bpp; |
3228 | 3230 | ||
3229 | drm_fbdev_client_hotplug(&fb_helper->client); | 3231 | drm_fbdev_client_hotplug(&fb_helper->client); |
diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c index b54fb78a283c..b82da96ded5c 100644 --- a/drivers/gpu/drm/drm_lease.c +++ b/drivers/gpu/drm/drm_lease.c | |||
@@ -566,14 +566,14 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev, | |||
566 | lessee_priv->is_master = 1; | 566 | lessee_priv->is_master = 1; |
567 | lessee_priv->authenticated = 1; | 567 | lessee_priv->authenticated = 1; |
568 | 568 | ||
569 | /* Hook up the fd */ | ||
570 | fd_install(fd, lessee_file); | ||
571 | |||
572 | /* Pass fd back to userspace */ | 569 | /* Pass fd back to userspace */ |
573 | DRM_DEBUG_LEASE("Returning fd %d id %d\n", fd, lessee->lessee_id); | 570 | DRM_DEBUG_LEASE("Returning fd %d id %d\n", fd, lessee->lessee_id); |
574 | cl->fd = fd; | 571 | cl->fd = fd; |
575 | cl->lessee_id = lessee->lessee_id; | 572 | cl->lessee_id = lessee->lessee_id; |
576 | 573 | ||
574 | /* Hook up the fd */ | ||
575 | fd_install(fd, lessee_file); | ||
576 | |||
577 | DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl succeeded\n"); | 577 | DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl succeeded\n"); |
578 | return 0; | 578 | return 0; |
579 | 579 | ||
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 989f8e52864d..971bb7853776 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h | |||
@@ -87,9 +87,10 @@ struct drm_client_dev { | |||
87 | struct drm_file *file; | 87 | struct drm_file *file; |
88 | }; | 88 | }; |
89 | 89 | ||
90 | int drm_client_new(struct drm_device *dev, struct drm_client_dev *client, | 90 | int drm_client_init(struct drm_device *dev, struct drm_client_dev *client, |
91 | const char *name, const struct drm_client_funcs *funcs); | 91 | const char *name, const struct drm_client_funcs *funcs); |
92 | void drm_client_release(struct drm_client_dev *client); | 92 | void drm_client_release(struct drm_client_dev *client); |
93 | void drm_client_add(struct drm_client_dev *client); | ||
93 | 94 | ||
94 | void drm_client_dev_unregister(struct drm_device *dev); | 95 | void drm_client_dev_unregister(struct drm_device *dev); |
95 | void drm_client_dev_hotplug(struct drm_device *dev); | 96 | void drm_client_dev_hotplug(struct drm_device *dev); |