summaryrefslogtreecommitdiff
path: root/sys/dev/pci/drm
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2014-03-13 12:45:05 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2014-03-13 12:45:05 +0000
commit5afd26a0f0a34c1687168d9dbc107bb81e1fd1de (patch)
tree0db7780de3d3cb7d0a7be32db36bf05feaa58bc6 /sys/dev/pci/drm
parent309ac7cc5f4d159bfbc4dc87eb49f18a170ec8c8 (diff)
Block userland from entering drm code (and make sure it is no longer in there)
before we truly start quiescing the device and don't unblock until we've finished waking the device up. Fixes issues with suspend/resume on inteldrm(4) and perhaps radeondrm(4) as well. i"it is the right place to stall" deraadt@
Diffstat (limited to 'sys/dev/pci/drm')
-rw-r--r--sys/dev/pci/drm/drmP.h6
-rw-r--r--sys/dev/pci/drm/drm_drv.c115
-rw-r--r--sys/dev/pci/drm/i915/i915_drv.c15
-rw-r--r--sys/dev/pci/drm/radeon/radeon_kms.c11
4 files changed, 126 insertions, 21 deletions
diff --git a/sys/dev/pci/drm/drmP.h b/sys/dev/pci/drm/drmP.h
index 4e8281f82cf..2de65ae06e6 100644
--- a/sys/dev/pci/drm/drmP.h
+++ b/sys/dev/pci/drm/drmP.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: drmP.h,v 1.169 2014/03/09 07:42:29 jsg Exp $ */
+/* $OpenBSD: drmP.h,v 1.170 2014/03/13 12:45:04 kettenis Exp $ */
/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*-
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
*/
@@ -785,6 +785,10 @@ struct drm_device {
bus_dma_tag_t dmat;
bus_space_tag_t bst;
+ struct mutex quiesce_mtx;
+ int quiesce;
+ int quiesce_count;
+
char *unique; /* Unique identifier: e.g., busid */
int unique_len; /* Length of unique field */
diff --git a/sys/dev/pci/drm/drm_drv.c b/sys/dev/pci/drm/drm_drv.c
index 5db10f52c4e..31b4812ce66 100644
--- a/sys/dev/pci/drm/drm_drv.c
+++ b/sys/dev/pci/drm/drm_drv.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: drm_drv.c,v 1.124 2014/03/09 07:42:29 jsg Exp $ */
+/* $OpenBSD: drm_drv.c,v 1.125 2014/03/13 12:45:04 kettenis Exp $ */
/*-
* Copyright 2007-2009 Owain G. Ainsworth <oga@openbsd.org>
* Copyright © 2008 Intel Corporation
@@ -63,8 +63,12 @@ int drm_lastclose(struct drm_device *);
void drm_attach(struct device *, struct device *, void *);
int drm_probe(struct device *, void *, void *);
int drm_detach(struct device *, int);
+void drm_quiesce(struct drm_device *);
+void drm_wakeup(struct drm_device *);
+int drm_activate(struct device *, int);
int drmprint(void *, const char *);
int drmsubmatch(struct device *, void *, void *);
+int drm_do_ioctl(struct drm_device *, int, u_long, caddr_t);
int drm_dequeue_event(struct drm_device *, struct drm_file *, size_t,
struct drm_pending_event **);
@@ -212,6 +216,7 @@ drm_attach(struct device *parent, struct device *self, void *aux)
rw_init(&dev->dev_lock, "drmdevlk");
mtx_init(&dev->event_lock, IPL_TTY);
+ mtx_init(&dev->quiesce_mtx, IPL_NONE);
TAILQ_INIT(&dev->maplist);
SPLAY_INIT(&dev->files);
@@ -293,9 +298,47 @@ drm_detach(struct device *self, int flags)
return 0;
}
+void
+drm_quiesce(struct drm_device *dev)
+{
+ mtx_enter(&dev->quiesce_mtx);
+ dev->quiesce = 1;
+ while (dev->quiesce_count > 0) {
+ msleep(&dev->quiesce_count, &dev->quiesce_mtx,
+ PZERO, "drmqui", 0);
+ }
+ mtx_leave(&dev->quiesce_mtx);
+}
+
+void
+drm_wakeup(struct drm_device *dev)
+{
+ mtx_enter(&dev->quiesce_mtx);
+ dev->quiesce = 0;
+ wakeup(&dev->quiesce);
+ mtx_leave(&dev->quiesce_mtx);
+}
+
+int
+drm_activate(struct device *self, int act)
+{
+ struct drm_device *dev = (struct drm_device *)self;
+
+ switch (act) {
+ case DVACT_QUIESCE:
+ drm_quiesce(dev);
+ break;
+ case DVACT_WAKEUP:
+ drm_wakeup(dev);
+ break;
+ }
+
+ return (0);
+}
+
struct cfattach drm_ca = {
sizeof(struct drm_device), drm_probe, drm_attach,
- drm_detach
+ drm_detach, drm_activate
};
struct cfdriver drm_cd = {
@@ -540,20 +583,13 @@ done:
return (retcode);
}
-/* drmioctl is called whenever a process performs an ioctl on /dev/drm.
- */
int
-drmioctl(dev_t kdev, u_long cmd, caddr_t data, int flags,
- struct proc *p)
+drm_do_ioctl(struct drm_device *dev, int minor, u_long cmd, caddr_t data)
{
- struct drm_device *dev = drm_get_device_from_kdev(kdev);
struct drm_file *file_priv;
- if (dev == NULL)
- return ENODEV;
-
DRM_LOCK();
- file_priv = drm_find_file_by_minor(dev, minor(kdev));
+ file_priv = drm_find_file_by_minor(dev, minor);
DRM_UNLOCK();
if (file_priv == NULL) {
DRM_ERROR("can't find authenticator\n");
@@ -715,6 +751,34 @@ drmioctl(dev_t kdev, u_long cmd, caddr_t data, int flags,
return (EINVAL);
}
+/* drmioctl is called whenever a process performs an ioctl on /dev/drm.
+ */
+int
+drmioctl(dev_t kdev, u_long cmd, caddr_t data, int flags, struct proc *p)
+{
+ struct drm_device *dev = drm_get_device_from_kdev(kdev);
+ int error;
+
+ if (dev == NULL)
+ return ENODEV;
+
+ mtx_enter(&dev->quiesce_mtx);
+ while (dev->quiesce)
+ msleep(&dev->quiesce, &dev->quiesce_mtx, PZERO, "drmioc", 0);
+ dev->quiesce_count++;
+ mtx_leave(&dev->quiesce_mtx);
+
+ error = drm_do_ioctl(dev, minor(kdev), cmd, data);
+
+ mtx_enter(&dev->quiesce_mtx);
+ dev->quiesce_count--;
+ if (dev->quiesce)
+ wakeup(&dev->quiesce_count);
+ mtx_leave(&dev->quiesce_mtx);
+
+ return (error);
+}
+
int
drmread(dev_t kdev, struct uio *uio, int ioflag)
{
@@ -1224,10 +1288,39 @@ drm_fault(struct uvm_faultinfo *ufi, vaddr_t vaddr, vm_page_t *pps,
return(VM_PAGER_ERROR);
}
+ /*
+ * We could end up here as the result of a copyin(9) or
+ * copyout(9) while handling an ioctl. So we must be careful
+ * not to deadlock. Therefore we only block if the quiesce
+ * count is zero, which guarantees we didn't enter from within
+ * an ioctl code path.
+ */
+ mtx_enter(&dev->quiesce_mtx);
+ if (dev->quiesce && dev->quiesce_count == 0) {
+ mtx_leave(&dev->quiesce_mtx);
+ uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, uobj, NULL);
+ mtx_enter(&dev->quiesce_mtx);
+ while (dev->quiesce) {
+ msleep(&dev->quiesce, &dev->quiesce_mtx,
+ PZERO, "drmflt", 0);
+ }
+ mtx_leave(&dev->quiesce_mtx);
+ return(VM_PAGER_REFAULT);
+ }
+ dev->quiesce_count++;
+ mtx_leave(&dev->quiesce_mtx);
+
/* Call down into driver to do the magic */
ret = dev->driver->gem_fault(obj, ufi, entry->offset + (vaddr -
entry->start), vaddr, pps, npages, centeridx,
access_type, flags);
+
+ mtx_enter(&dev->quiesce_mtx);
+ dev->quiesce_count--;
+ if (dev->quiesce)
+ wakeup(&dev->quiesce_count);
+ mtx_leave(&dev->quiesce_mtx);
+
return (ret);
}
diff --git a/sys/dev/pci/drm/i915/i915_drv.c b/sys/dev/pci/drm/i915/i915_drv.c
index 12a9d7cbffa..5ec94840747 100644
--- a/sys/dev/pci/drm/i915/i915_drv.c
+++ b/sys/dev/pci/drm/i915/i915_drv.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: i915_drv.c,v 1.63 2014/02/23 09:36:52 kettenis Exp $ */
+/* $OpenBSD: i915_drv.c,v 1.64 2014/03/13 12:45:04 kettenis Exp $ */
/*
* Copyright (c) 2008-2009 Owain G. Ainsworth <oga@openbsd.org>
*
@@ -1113,24 +1113,29 @@ inteldrm_detach(struct device *self, int flags)
}
int
-inteldrm_activate(struct device *arg, int act)
+inteldrm_activate(struct device *self, int act)
{
- struct inteldrm_softc *dev_priv = (struct inteldrm_softc *)arg;
- struct drm_device *dev = (struct drm_device *)dev_priv->drmdev;
+ struct inteldrm_softc *dev_priv = (struct inteldrm_softc *)self;
+ struct drm_device *dev = (struct drm_device *)dev_priv->drmdev;
+ int rv = 0;
switch (act) {
case DVACT_QUIESCE:
+ rv = config_activate_children(self, act);
i915_drm_freeze(dev);
break;
case DVACT_SUSPEND:
break;
+ case DVACT_RESUME:
+ break;
case DVACT_WAKEUP:
i915_drm_thaw(dev);
intel_fb_restore_mode(dev);
+ rv = config_activate_children(self, act);
break;
}
- return (0);
+ return (rv);
}
struct cfattach inteldrm_ca = {
diff --git a/sys/dev/pci/drm/radeon/radeon_kms.c b/sys/dev/pci/drm/radeon/radeon_kms.c
index 5d895ec56ad..67c42ac8a70 100644
--- a/sys/dev/pci/drm/radeon/radeon_kms.c
+++ b/sys/dev/pci/drm/radeon/radeon_kms.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: radeon_kms.c,v 1.24 2014/02/25 00:03:38 kettenis Exp $ */
+/* $OpenBSD: radeon_kms.c,v 1.25 2014/03/13 12:45:04 kettenis Exp $ */
/*
* Copyright 2008 Advanced Micro Devices, Inc.
* Copyright 2008 Red Hat Inc.
@@ -750,12 +750,14 @@ radeondrm_attachhook(void *xsc)
}
int
-radeondrm_activate_kms(struct device *arg, int act)
+radeondrm_activate_kms(struct device *self, int act)
{
- struct radeon_device *rdev = (struct radeon_device *)arg;
+ struct radeon_device *rdev = (struct radeon_device *)self;
+ int rv = 0;
switch (act) {
case DVACT_QUIESCE:
+ rv = config_activate_children(self, act);
radeon_suspend_kms(rdev->ddev);
break;
case DVACT_SUSPEND:
@@ -764,10 +766,11 @@ radeondrm_activate_kms(struct device *arg, int act)
break;
case DVACT_WAKEUP:
radeon_resume_kms(rdev->ddev);
+ rv = config_activate_children(self, act);
break;
}
- return (0);
+ return (rv);
}
/**