Module Name: src Committed By: riastradh Date: Sun Dec 19 00:58:11 UTC 2021
Modified Files: src/sys/external/bsd/drm2/drm: drm_cdevsw.c Log Message: Serialize drm_read so we can back out on uiomove without reordering. Upstream commit 9b2c0b7fb4ce79566d830d03ce7aa11cccc39f97. To generate a diff of this commit: cvs rdiff -u -r1.19 -r1.20 src/sys/external/bsd/drm2/drm/drm_cdevsw.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/external/bsd/drm2/drm/drm_cdevsw.c diff -u src/sys/external/bsd/drm2/drm/drm_cdevsw.c:1.19 src/sys/external/bsd/drm2/drm/drm_cdevsw.c:1.20 --- src/sys/external/bsd/drm2/drm/drm_cdevsw.c:1.19 Sun Dec 19 00:48:45 2021 +++ src/sys/external/bsd/drm2/drm/drm_cdevsw.c Sun Dec 19 00:58:11 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: drm_cdevsw.c,v 1.19 2021/12/19 00:48:45 riastradh Exp $ */ +/* $NetBSD: drm_cdevsw.c,v 1.20 2021/12/19 00:58:11 riastradh Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: drm_cdevsw.c,v 1.19 2021/12/19 00:48:45 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: drm_cdevsw.c,v 1.20 2021/12/19 00:58:11 riastradh Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -274,35 +274,82 @@ drm_read(struct file *fp, off_t *off, st int flags) { struct drm_file *const file = fp->f_data; + struct drm_device *const dev = file->minor->dev; struct drm_pending_event *event; bool first; - int error = 0; + int ret = 0; + + /* + * Only one event reader at a time, so that if copyout faults + * after dequeueing one event and we have to put the event + * back, another reader won't see out-of-order events. + */ + spin_lock(&dev->event_lock); + DRM_SPIN_WAIT_NOINTR_UNTIL(ret, &file->event_read_wq, &dev->event_lock, + file->event_read_lock == NULL); + if (ret) { + spin_unlock(&dev->event_lock); + /* XXX errno Linux->NetBSD */ + return -ret; + } + file->event_read_lock = curlwp; + spin_unlock(&dev->event_lock); for (first = true; ; first = false) { int f = 0; + off_t offset; + size_t resid; if (!first || ISSET(fp->f_flag, FNONBLOCK)) f |= FNONBLOCK; - /* XXX errno Linux->NetBSD */ - error = -drm_dequeue_event(file, uio->uio_resid, &event, f); - if (error) { - if ((error == EWOULDBLOCK) && !first) - error = 0; + ret = drm_dequeue_event(file, uio->uio_resid, &event, f); + if (ret) { + if ((ret == -EWOULDBLOCK) && !first) + ret = 0; break; } if (event == NULL) break; - error = uiomove(event->event, event->event->length, uio); - if (error) /* XXX Requeue the event? */ + + offset = uio->uio_offset; + resid = uio->uio_resid; + /* XXX errno NetBSD->Linux */ + ret = -uiomove(event->event, event->event->length, uio); + if (ret) { + /* + * Faulted on copyout. Put the event back and + * stop here. + */ + if (!first) { + /* + * Already transferred some events. + * Rather than back them all out, just + * say we succeeded at returning those. + */ + ret = 0; + } + uio->uio_offset = offset; + uio->uio_resid = resid; + drm_requeue_event(file, event); break; - (*event->destroy)(event); + } + kfree(event); } + /* Release the event read lock. */ + spin_lock(&dev->event_lock); + KASSERT(file->event_read_lock == curlwp); + file->event_read_lock = NULL; + DRM_SPIN_WAKEUP_ONE(&file->event_read_wq, &dev->event_lock); + spin_unlock(&dev->event_lock); + + /* XXX errno Linux->NetBSD */ + /* Success! */ - if (error == ERESTARTSYS) - error = ERESTART; - return error; + if (ret == ERESTARTSYS) + ret = ERESTART; + return -ret; } static int @@ -343,6 +390,19 @@ out: spin_unlock_irqrestore(&dev->event_ return ret; } +static void +drm_requeue_event(struct drm_file *file, struct drm_pending_event *event) +{ + struct drm_device *const dev = file->minor->dev; + unsigned long irqflags; + + spin_lock_irqsave(&dev->event_lock, irqflags); + list_add(&event->link, &file->event_list); + KASSERT(file->event_space >= event->event->length); + file->event_space -= event->event->length; + spin_unlock_irqrestore(&dev->event_lock, irqflags); +} + static int drm_ioctl_shim(struct file *fp, unsigned long cmd, void *data) {