Module Name:    src
Committed By:   riastradh
Date:           Mon May 22 14:07:24 UTC 2023

Modified Files:
        src/share/man/man9: uiomove.9
        src/sys/kern: subr_copy.c
        src/sys/sys: systm.h

Log Message:
uiomove(9): Add uiopeek/uioskip operations.

This allows a caller to grab some data, consume part of it, and
atomically update the uio with only the amount it consumed.  This
way, the caller can use a buffer of a size that doesn't depend on how
much it will actually consume, which it may not know in advance --
e.g., because it depends on how much an underlying hardware tty
device will accept before it decides it has had too much.

Proposed on tech-kern:
https://mail-index.netbsd.org/tech-kern/2023/05/09/msg028883.html

(Opinions were divided between `uioadvance' and `uioskip'.  I stuck
with `uioskip' because that was less work for me.)


To generate a diff of this commit:
cvs rdiff -u -r1.20 -r1.21 src/share/man/man9/uiomove.9
cvs rdiff -u -r1.18 -r1.19 src/sys/kern/subr_copy.c
cvs rdiff -u -r1.301 -r1.302 src/sys/sys/systm.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/share/man/man9/uiomove.9
diff -u src/share/man/man9/uiomove.9:1.20 src/share/man/man9/uiomove.9:1.21
--- src/share/man/man9/uiomove.9:1.20	Sun Sep  1 19:08:35 2019
+++ src/share/man/man9/uiomove.9	Mon May 22 14:07:24 2023
@@ -1,4 +1,4 @@
-.\"	$NetBSD: uiomove.9,v 1.20 2019/09/01 19:08:35 wiz Exp $
+.\"	$NetBSD: uiomove.9,v 1.21 2023/05/22 14:07:24 riastradh Exp $
 .\"
 .\" Copyright (c) 1996 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -24,7 +24,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd April 26, 2010
+.Dd May 9, 2023
 .Dt UIOMOVE 9
 .Os
 .Sh NAME
@@ -34,6 +34,10 @@
 .In sys/systm.h
 .Ft int
 .Fn uiomove "void *buf" "size_t n" "struct uio *uio"
+.Ft int
+.Fn uiopeek "void *buf" "size_t n" "struct uio *uio"
+.Ft void
+.Fn uioskip "void *buf" "size_t n" "struct uio *uio"
 .Sh DESCRIPTION
 The
 .Fn uiomove
@@ -140,10 +144,35 @@ to point that much farther into the regi
 This allows multiple calls to
 .Fn uiomove
 to easily be used to fill or drain the region of data.
+.Pp
+The
+.Fn uiopeek
+function copies up to
+.Fa n
+bytes of data without updating
+.Fa uio ;
+the
+.Fn uioskip
+function updates
+.Fa uio
+without copying any data, and is guaranteed never to sleep or fault
+even if the buffers are in userspace and memory access via
+.Fn uiomove
+or
+.Fn uiopeek
+would trigger paging.
+A successful
+.Fn uiomove buf n uio
+call is equivalent to a successful
+.Fn uiopeek buf n uio
+followed by
+.Fn uioskip n uio .
 .Sh RETURN VALUES
 Upon successful completion,
 .Fn uiomove
-returns 0.
+and
+.Fn uiopeek
+return 0.
 If a bad address is encountered,
 .Er EFAULT
 is returned.

Index: src/sys/kern/subr_copy.c
diff -u src/sys/kern/subr_copy.c:1.18 src/sys/kern/subr_copy.c:1.19
--- src/sys/kern/subr_copy.c:1.18	Tue Apr 11 10:22:04 2023
+++ src/sys/kern/subr_copy.c	Mon May 22 14:07:24 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: subr_copy.c,v 1.18 2023/04/11 10:22:04 riastradh Exp $	*/
+/*	$NetBSD: subr_copy.c,v 1.19 2023/05/22 14:07:24 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 1997, 1998, 1999, 2002, 2007, 2008, 2019
@@ -80,7 +80,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_copy.c,v 1.18 2023/04/11 10:22:04 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_copy.c,v 1.19 2023/05/22 14:07:24 riastradh Exp $");
 
 #define	__UFETCHSTORE_PRIVATE
 #define	__UCAS_PRIVATE
@@ -166,6 +166,93 @@ uiomove_frombuf(void *buf, size_t buflen
 	return (uiomove((char *)buf + offset, buflen - offset, uio));
 }
 
+int
+uiopeek(void *buf, size_t n, struct uio *uio)
+{
+	struct vmspace *vm = uio->uio_vmspace;
+	struct iovec *iov;
+	size_t cnt;
+	int error = 0;
+	char *cp = buf;
+	size_t resid = uio->uio_resid;
+	int iovcnt = uio->uio_iovcnt;
+	char *base;
+	size_t len;
+
+	KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE);
+
+	if (n == 0 || resid == 0)
+		return 0;
+	iov = uio->uio_iov;
+	base = iov->iov_base;
+	len = iov->iov_len;
+
+	while (n > 0 && resid > 0) {
+		KASSERT(iovcnt > 0);
+		cnt = len;
+		if (cnt == 0) {
+			KASSERT(iovcnt > 1);
+			iov++;
+			iovcnt--;
+			base = iov->iov_base;
+			len = iov->iov_len;
+			continue;
+		}
+		if (cnt > n)
+			cnt = n;
+		if (!VMSPACE_IS_KERNEL_P(vm)) {
+			preempt_point();
+		}
+
+		if (uio->uio_rw == UIO_READ) {
+			error = copyout_vmspace(vm, cp, base, cnt);
+		} else {
+			error = copyin_vmspace(vm, base, cp, cnt);
+		}
+		if (error) {
+			break;
+		}
+		base += cnt;
+		len -= cnt;
+		resid -= cnt;
+		cp += cnt;
+		KDASSERT(cnt <= n);
+		n -= cnt;
+	}
+
+	return error;
+}
+
+void
+uioskip(size_t n, struct uio *uio)
+{
+	struct iovec *iov;
+	size_t cnt;
+
+	KASSERTMSG(n <= uio->uio_resid, "n=%zu resid=%zu", n, uio->uio_resid);
+
+	KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE);
+	while (n > 0 && uio->uio_resid) {
+		KASSERT(uio->uio_iovcnt > 0);
+		iov = uio->uio_iov;
+		cnt = iov->iov_len;
+		if (cnt == 0) {
+			KASSERT(uio->uio_iovcnt > 1);
+			uio->uio_iov++;
+			uio->uio_iovcnt--;
+			continue;
+		}
+		if (cnt > n)
+			cnt = n;
+		iov->iov_base = (char *)iov->iov_base + cnt;
+		iov->iov_len -= cnt;
+		uio->uio_resid -= cnt;
+		uio->uio_offset += cnt;
+		KDASSERT(cnt <= n);
+		n -= cnt;
+	}
+}
+
 /*
  * Give next character to user as result of read.
  */

Index: src/sys/sys/systm.h
diff -u src/sys/sys/systm.h:1.301 src/sys/sys/systm.h:1.302
--- src/sys/sys/systm.h:1.301	Wed Jun 16 11:55:10 2021
+++ src/sys/sys/systm.h	Mon May 22 14:07:24 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: systm.h,v 1.301 2021/06/16 11:55:10 rin Exp $	*/
+/*	$NetBSD: systm.h,v 1.302 2023/05/22 14:07:24 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 1982, 1988, 1991, 1993
@@ -620,6 +620,8 @@ void	trace_exit(register_t, const struct
 
 int	uiomove(void *, size_t, struct uio *);
 int	uiomove_frombuf(void *, size_t, struct uio *);
+int	uiopeek(void *, size_t, struct uio *);
+void	uioskip(size_t, struct uio *);
 
 #ifdef _KERNEL
 int	setjmp(label_t *) __returns_twice;

Reply via email to