The recent "inteldrm suspend/resume regression" thread pointed out that suspend/resume was quite horribly broken and only worked somewhat if you didn't heavily use the "3D" acceleration stuff. Here's a diff that should fix most of the problems, by making sure userland programs are properly blocked if they try to use drm while we're suspending or resuming the machine.
I would like to see this diff tested some more by people who actually use all that eye candy. The thing to watch for is hangs when you try to suspend your machine. Thanks, Mark P.S. This seems to make hibernation (ZZZ) work with both inteldrm(4) and radeondrm(4) on my t400. Index: drmP.h =================================================================== RCS file: /cvs/src/sys/dev/pci/drm/drmP.h,v retrieving revision 1.169 diff -u -p -r1.169 drmP.h --- drmP.h 9 Mar 2014 07:42:29 -0000 1.169 +++ drmP.h 12 Mar 2014 21:38:43 -0000 @@ -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 */ Index: drm_drv.c =================================================================== RCS file: /cvs/src/sys/dev/pci/drm/drm_drv.c,v retrieving revision 1.124 diff -u -p -r1.124 drm_drv.c --- drm_drv.c 9 Mar 2014 07:42:29 -0000 1.124 +++ drm_drv.c 12 Mar 2014 21:38:43 -0000 @@ -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 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 flag 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 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); + mtx_leave(&dev->quiesce_mtx); + + return (error); +} + int drmread(dev_t kdev, struct uio *uio, int ioflag) { @@ -1224,10 +1288,26 @@ drm_fault(struct uvm_faultinfo *ufi, vad return(VM_PAGER_ERROR); } + mtx_enter(&dev->quiesce_mtx); + if (dev->quiesce) { + mtx_leave(&dev->quiesce_mtx); + uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, uobj, NULL); + return(VM_PAGER_OK); + } + 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); + mtx_leave(&dev->quiesce_mtx); + return (ret); } Index: i915/i915_drv.c =================================================================== RCS file: /cvs/src/sys/dev/pci/drm/i915/i915_drv.c,v retrieving revision 1.63 diff -u -p -r1.63 i915_drv.c --- i915/i915_drv.c 23 Feb 2014 09:36:52 -0000 1.63 +++ i915/i915_drv.c 12 Mar 2014 21:38:43 -0000 @@ -1113,24 +1113,29 @@ inteldrm_detach(struct device *self, int } 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 = { Index: radeon/radeon_kms.c =================================================================== RCS file: /cvs/src/sys/dev/pci/drm/radeon/radeon_kms.c,v retrieving revision 1.24 diff -u -p -r1.24 radeon_kms.c --- radeon/radeon_kms.c 25 Feb 2014 00:03:38 -0000 1.24 +++ radeon/radeon_kms.c 12 Mar 2014 21:38:43 -0000 @@ -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 *ar break; case DVACT_WAKEUP: radeon_resume_kms(rdev->ddev); + rv = config_activate_children(self, act); break; } - return (0); + return (rv); } /**