aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/vc4/vc4_gem.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2017-10-24 16:51:05 +1000
committerDave Airlie <airlied@redhat.com>2017-10-24 16:51:05 +1000
commitfef1aa48f488fedbbecd28995f1fd9a2ea2a7ef2 (patch)
tree77b71123bc0adc9f72328d952eff48d14235c3fd /drivers/gpu/drm/vc4/vc4_gem.c
parent3b677e43c1191de4717357ffeb85a30b967045e4 (diff)
parentaf0c8c10564aac5b6d67308129ec09c4ad5db476 (diff)
Merge tag 'drm-misc-next-2017-10-20' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
Final drm-misc feature pull for 4.15: UAPI Changes: - new madvise ioctl for vc4 (Boris) Core Changes: - plane commit tracking fixes (Maarten) - vgaarb improvements for fancy new platforms (aka ppc64 and arm64) by Bjorn Helgaas Driver Changes: - pile of new panel drivers: Toshiba LT089AC19000, Innolux AT043TN24 - more sun4i work to support A10/A20 Tcon and hdmi outputs - vc4: fix sleep in irq handler by making it threaded (Eric) - udl probe/edid read fixes (Robert Tarasov) And a bunch of misc small cleanups/refactors and doc fixes all over. * tag 'drm-misc-next-2017-10-20' of git://anongit.freedesktop.org/drm/drm-misc: (32 commits) drm/vc4: Fix sleeps during the IRQ handler for DSI transactions. drm/vc4: Add the DRM_IOCTL_VC4_GEM_MADVISE ioctl drm/panel: simple: add Toshiba LT089AC19000 dma-fence: remove duplicate word in comment drm/panel: simple: add delays for Innolux AT043TN24 drm/panel: simple: add bus flags for Innolux AT043TN24 drm/panel: simple: fix vertical timings for Innolux AT043TN24 drm/atomic-helper: check that drivers call drm_crtc_vblank_off drm: some KMS todo ideas vgaarb: Factor out EFI and fallback default device selection vgaarb: Select a default VGA device even if there's no legacy VGA drm/bridge: adv7511: Fix a use after free drm/sun4i: Add support for A20 display pipeline components drm/sun4i: Add support for A10 display pipeline components drm/sun4i: hdmi: Support HDMI controller on A10 drm/sun4i: tcon: Add support for A10 TCON drm/sun4i: backend: Support output muxing drm/sun4i: tcon: Move out the tcon0 common setup drm/sun4i: tcon: Don't rely on encoders to set the TCON mode drm/sun4i: tcon: Don't rely on encoders to enable the TCON ...
Diffstat (limited to 'drivers/gpu/drm/vc4/vc4_gem.c')
-rw-r--r--drivers/gpu/drm/vc4/vc4_gem.c156
1 files changed, 150 insertions, 6 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
index d0c6bfb68c4e..e00ac2f3a264 100644
--- a/drivers/gpu/drm/vc4/vc4_gem.c
+++ b/drivers/gpu/drm/vc4/vc4_gem.c
@@ -188,11 +188,22 @@ vc4_save_hang_state(struct drm_device *dev)
continue;
for (j = 0; j < exec[i]->bo_count; j++) {
+ bo = to_vc4_bo(&exec[i]->bo[j]->base);
+
+ /* Retain BOs just in case they were marked purgeable.
+ * This prevents the BO from being purged before
+ * someone had a chance to dump the hang state.
+ */
+ WARN_ON(!refcount_read(&bo->usecnt));
+ refcount_inc(&bo->usecnt);
drm_gem_object_get(&exec[i]->bo[j]->base);
kernel_state->bo[j + prev_idx] = &exec[i]->bo[j]->base;
}
list_for_each_entry(bo, &exec[i]->unref_list, unref_head) {
+ /* No need to retain BOs coming from the ->unref_list
+ * because they are naturally unpurgeable.
+ */
drm_gem_object_get(&bo->base.base);
kernel_state->bo[j + prev_idx] = &bo->base.base;
j++;
@@ -233,6 +244,26 @@ vc4_save_hang_state(struct drm_device *dev)
state->fdbgs = V3D_READ(V3D_FDBGS);
state->errstat = V3D_READ(V3D_ERRSTAT);
+ /* We need to turn purgeable BOs into unpurgeable ones so that
+ * userspace has a chance to dump the hang state before the kernel
+ * decides to purge those BOs.
+ * Note that BO consistency at dump time cannot be guaranteed. For
+ * example, if the owner of these BOs decides to re-use them or mark
+ * them purgeable again there's nothing we can do to prevent it.
+ */
+ for (i = 0; i < kernel_state->user_state.bo_count; i++) {
+ struct vc4_bo *bo = to_vc4_bo(kernel_state->bo[i]);
+
+ if (bo->madv == __VC4_MADV_NOTSUPP)
+ continue;
+
+ mutex_lock(&bo->madv_lock);
+ if (!WARN_ON(bo->madv == __VC4_MADV_PURGED))
+ bo->madv = VC4_MADV_WILLNEED;
+ refcount_dec(&bo->usecnt);
+ mutex_unlock(&bo->madv_lock);
+ }
+
spin_lock_irqsave(&vc4->job_lock, irqflags);
if (vc4->hang_state) {
spin_unlock_irqrestore(&vc4->job_lock, irqflags);
@@ -639,9 +670,6 @@ vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec,
* The command validator needs to reference BOs by their index within
* the submitted job's BO list. This does the validation of the job's
* BO list and reference counting for the lifetime of the job.
- *
- * Note that this function doesn't need to unreference the BOs on
- * failure, because that will happen at vc4_complete_exec() time.
*/
static int
vc4_cl_lookup_bos(struct drm_device *dev,
@@ -693,16 +721,47 @@ vc4_cl_lookup_bos(struct drm_device *dev,
DRM_DEBUG("Failed to look up GEM BO %d: %d\n",
i, handles[i]);
ret = -EINVAL;
- spin_unlock(&file_priv->table_lock);
- goto fail;
+ break;
}
+
drm_gem_object_get(bo);
exec->bo[i] = (struct drm_gem_cma_object *)bo;
}
spin_unlock(&file_priv->table_lock);
+ if (ret)
+ goto fail_put_bo;
+
+ for (i = 0; i < exec->bo_count; i++) {
+ ret = vc4_bo_inc_usecnt(to_vc4_bo(&exec->bo[i]->base));
+ if (ret)
+ goto fail_dec_usecnt;
+ }
+
+ kvfree(handles);
+ return 0;
+
+fail_dec_usecnt:
+ /* Decrease usecnt on acquired objects.
+ * We cannot rely on vc4_complete_exec() to release resources here,
+ * because vc4_complete_exec() has no information about which BO has
+ * had its ->usecnt incremented.
+ * To make things easier we just free everything explicitly and set
+ * exec->bo to NULL so that vc4_complete_exec() skips the 'BO release'
+ * step.
+ */
+ for (i-- ; i >= 0; i--)
+ vc4_bo_dec_usecnt(to_vc4_bo(&exec->bo[i]->base));
+
+fail_put_bo:
+ /* Release any reference to acquired objects. */
+ for (i = 0; i < exec->bo_count && exec->bo[i]; i++)
+ drm_gem_object_put_unlocked(&exec->bo[i]->base);
+
fail:
kvfree(handles);
+ kvfree(exec->bo);
+ exec->bo = NULL;
return ret;
}
@@ -833,8 +892,12 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
dma_fence_signal(exec->fence);
if (exec->bo) {
- for (i = 0; i < exec->bo_count; i++)
+ for (i = 0; i < exec->bo_count; i++) {
+ struct vc4_bo *bo = to_vc4_bo(&exec->bo[i]->base);
+
+ vc4_bo_dec_usecnt(bo);
drm_gem_object_put_unlocked(&exec->bo[i]->base);
+ }
kvfree(exec->bo);
}
@@ -1098,6 +1161,9 @@ vc4_gem_init(struct drm_device *dev)
INIT_WORK(&vc4->job_done_work, vc4_job_done_work);
mutex_init(&vc4->power_lock);
+
+ INIT_LIST_HEAD(&vc4->purgeable.list);
+ mutex_init(&vc4->purgeable.lock);
}
void
@@ -1121,3 +1187,81 @@ vc4_gem_destroy(struct drm_device *dev)
if (vc4->hang_state)
vc4_free_hang_state(dev, vc4->hang_state);
}
+
+int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_vc4_gem_madvise *args = data;
+ struct drm_gem_object *gem_obj;
+ struct vc4_bo *bo;
+ int ret;
+
+ switch (args->madv) {
+ case VC4_MADV_DONTNEED:
+ case VC4_MADV_WILLNEED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (args->pad != 0)
+ return -EINVAL;
+
+ gem_obj = drm_gem_object_lookup(file_priv, args->handle);
+ if (!gem_obj) {
+ DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
+ return -ENOENT;
+ }
+
+ bo = to_vc4_bo(gem_obj);
+
+ /* Only BOs exposed to userspace can be purged. */
+ if (bo->madv == __VC4_MADV_NOTSUPP) {
+ DRM_DEBUG("madvise not supported on this BO\n");
+ ret = -EINVAL;
+ goto out_put_gem;
+ }
+
+ /* Not sure it's safe to purge imported BOs. Let's just assume it's
+ * not until proven otherwise.
+ */
+ if (gem_obj->import_attach) {
+ DRM_DEBUG("madvise not supported on imported BOs\n");
+ ret = -EINVAL;
+ goto out_put_gem;
+ }
+
+ mutex_lock(&bo->madv_lock);
+
+ if (args->madv == VC4_MADV_DONTNEED && bo->madv == VC4_MADV_WILLNEED &&
+ !refcount_read(&bo->usecnt)) {
+ /* If the BO is about to be marked as purgeable, is not used
+ * and is not already purgeable or purged, add it to the
+ * purgeable list.
+ */
+ vc4_bo_add_to_purgeable_pool(bo);
+ } else if (args->madv == VC4_MADV_WILLNEED &&
+ bo->madv == VC4_MADV_DONTNEED &&
+ !refcount_read(&bo->usecnt)) {
+ /* The BO has not been purged yet, just remove it from
+ * the purgeable list.
+ */
+ vc4_bo_remove_from_purgeable_pool(bo);
+ }
+
+ /* Save the purged state. */
+ args->retained = bo->madv != __VC4_MADV_PURGED;
+
+ /* Update internal madv state only if the bo was not purged. */
+ if (bo->madv != __VC4_MADV_PURGED)
+ bo->madv = args->madv;
+
+ mutex_unlock(&bo->madv_lock);
+
+ ret = 0;
+
+out_put_gem:
+ drm_gem_object_put_unlocked(gem_obj);
+
+ return ret;
+}