svn commit: r367362 - in head/sys: compat/linprocfs fs/pseudofs

Conrad Meyer cem at FreeBSD.org
Thu Nov 5 06:48:52 UTC 2020


Author: cem
Date: Thu Nov  5 06:48:51 2020
New Revision: 367362
URL: https://svnweb.freebsd.org/changeset/base/367362

Log:
  Add sbuf streaming mode to pseudofs(9), use in linprocfs(5)
  
  Add a pseudofs node flag 'PFS_AUTODRAIN', which automatically emits sbuf
  contents to the caller when the sbuf buffer fills.  This is only
  permissible if the corresponding PFS node fill function can sleep
  whenever it appends to the sbuf.
  
  linprocfs' /proc/self/maps node happens to meet this requirement.
  Streaming out the file as it is composed avoids truncating the output
  and also avoids preallocating a very large buffer.
  
  Reviewed by:	markj; earlier version: emaste, kib, trasz
  Differential Revision:	https://reviews.freebsd.org/D27047

Modified:
  head/sys/compat/linprocfs/linprocfs.c
  head/sys/fs/pseudofs/pseudofs.h
  head/sys/fs/pseudofs/pseudofs_vnops.c

Modified: head/sys/compat/linprocfs/linprocfs.c
==============================================================================
--- head/sys/compat/linprocfs/linprocfs.c	Thu Nov  5 04:19:48 2020	(r367361)
+++ head/sys/compat/linprocfs/linprocfs.c	Thu Nov  5 06:48:51 2020	(r367362)
@@ -1252,10 +1252,6 @@ linprocfs_doprocmaps(PFS_FILL_ARGS)
 		    *name ? "     " : " ",
 		    name
 		    );
-		if (error == -1) {
-			linux_msg(td, "cannot fill /proc/self/maps; "
-			    "consider bumping PFS_MAXBUFSIZ");
-		}
 		if (freename)
 			free(freename, M_TEMP);
 		vm_map_lock_read(map);
@@ -1890,7 +1886,7 @@ linprocfs_init(PFS_INIT_ARGS)
 	pfs_create_link(dir, "exe", &procfs_doprocfile,
 	    NULL, &procfs_notsystem, NULL, 0);
 	pfs_create_file(dir, "maps", &linprocfs_doprocmaps,
-	    NULL, NULL, NULL, PFS_RD);
+	    NULL, NULL, NULL, PFS_RD | PFS_AUTODRAIN);
 	pfs_create_file(dir, "mem", &linprocfs_doprocmem,
 	    procfs_attr_rw, &procfs_candebug, NULL, PFS_RDWR | PFS_RAW);
 	pfs_create_file(dir, "mounts", &linprocfs_domtab,

Modified: head/sys/fs/pseudofs/pseudofs.h
==============================================================================
--- head/sys/fs/pseudofs/pseudofs.h	Thu Nov  5 04:19:48 2020	(r367361)
+++ head/sys/fs/pseudofs/pseudofs.h	Thu Nov  5 06:48:51 2020	(r367362)
@@ -78,6 +78,7 @@ typedef enum {
 #define PFS_RAW		(PFS_RAWRD|PFS_RAWWR)
 #define PFS_PROCDEP	0x0010	/* process-dependent */
 #define PFS_NOWAIT	0x0020 /* allow malloc to fail */
+#define PFS_AUTODRAIN	0x0040	/* sbuf_print can sleep to drain */
 
 /*
  * Data structures

Modified: head/sys/fs/pseudofs/pseudofs_vnops.c
==============================================================================
--- head/sys/fs/pseudofs/pseudofs_vnops.c	Thu Nov  5 04:19:48 2020	(r367361)
+++ head/sys/fs/pseudofs/pseudofs_vnops.c	Thu Nov  5 06:48:51 2020	(r367362)
@@ -623,6 +623,50 @@ pfs_open(struct vop_open_args *va)
 	PFS_RETURN (0);
 }
 
+struct sbuf_seek_helper {
+	off_t		skip_bytes;
+	struct uio	*uio;
+};
+
+static int
+pfs_sbuf_uio_drain(void *arg, const char *data, int len)
+{
+	struct sbuf_seek_helper *ssh;
+	struct uio *uio;
+	int error, skipped;
+
+	ssh = arg;
+	uio = ssh->uio;
+	skipped = 0;
+
+	/* Need to discard first uio_offset bytes. */
+	if (ssh->skip_bytes > 0) {
+		if (ssh->skip_bytes >= len) {
+			ssh->skip_bytes -= len;
+			return (len);
+		}
+
+		data += ssh->skip_bytes;
+		len -= ssh->skip_bytes;
+		skipped = ssh->skip_bytes;
+		ssh->skip_bytes = 0;
+	}
+
+	error = uiomove(__DECONST(void *, data), len, uio);
+	if (error != 0)
+		return (-error);
+
+	/*
+	 * The fill function has more to emit, but the reader is finished.
+	 * This is similar to the truncated read case for non-draining PFS
+	 * sbufs, and should be handled appropriately in fill-routines.
+	 */
+	if (uio->uio_resid == 0)
+		return (-ENOBUFS);
+
+	return (skipped + len);
+}
+
 /*
  * Read from a file
  */
@@ -636,7 +680,8 @@ pfs_read(struct vop_read_args *va)
 	struct proc *proc;
 	struct sbuf *sb = NULL;
 	int error, locked;
-	off_t buflen;
+	off_t buflen, buflim;
+	struct sbuf_seek_helper ssh;
 
 	PFS_TRACE(("%s", pn->pn_name));
 	pfs_assert_not_owned(pn);
@@ -678,16 +723,30 @@ pfs_read(struct vop_read_args *va)
 		error = EINVAL;
 		goto ret;
 	}
-	buflen = uio->uio_offset + uio->uio_resid;
-	if (buflen > PFS_MAXBUFSIZ)
-		buflen = PFS_MAXBUFSIZ;
+	buflen = uio->uio_offset + uio->uio_resid + 1;
+	if (pn->pn_flags & PFS_AUTODRAIN)
+		/*
+		 * We can use a smaller buffer if we can stream output to the
+		 * consumer.
+		 */
+		buflim = PAGE_SIZE;
+	else
+		buflim = PFS_MAXBUFSIZ;
+	if (buflen > buflim)
+		buflen = buflim;
 
-	sb = sbuf_new(sb, NULL, buflen + 1, 0);
+	sb = sbuf_new(sb, NULL, buflen, 0);
 	if (sb == NULL) {
 		error = EIO;
 		goto ret;
 	}
 
+	if (pn->pn_flags & PFS_AUTODRAIN) {
+		ssh.skip_bytes = uio->uio_offset;
+		ssh.uio = uio;
+		sbuf_set_drain(sb, pfs_sbuf_uio_drain, &ssh);
+	}
+
 	error = pn_fill(curthread, proc, pn, sb, uio);
 
 	if (error) {
@@ -700,9 +759,23 @@ pfs_read(struct vop_read_args *va)
 	 * the data length. Then just use the full length because an
 	 * overflowed sbuf must be full.
 	 */
-	if (sbuf_finish(sb) == 0)
-		buflen = sbuf_len(sb);
-	error = uiomove_frombuf(sbuf_data(sb), buflen, uio);
+	error = sbuf_finish(sb);
+	if ((pn->pn_flags & PFS_AUTODRAIN)) {
+		/*
+		 * ENOBUFS just indicates early termination of the fill
+		 * function as the caller's buffer was already filled.  Squash
+		 * to zero.
+		 */
+		if (uio->uio_resid == 0 && error == ENOBUFS)
+			error = 0;
+	} else {
+		if (error == 0)
+			buflen = sbuf_len(sb);
+		else
+			/* The trailing byte is not valid. */
+			buflen--;
+		error = uiomove_frombuf(sbuf_data(sb), buflen, uio);
+	}
 	sbuf_delete(sb);
 ret:
 	vn_lock(vn, locked | LK_RETRY);


More information about the svn-src-head mailing list