From owner-svn-src-all@freebsd.org Thu Nov 5 06:48:52 2020 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 4E1EE44CAE0; Thu, 5 Nov 2020 06:48:52 +0000 (UTC) (envelope-from cem@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4CRYyX1Mmmz4rMF; Thu, 5 Nov 2020 06:48:52 +0000 (UTC) (envelope-from cem@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 11C1A1AB56; Thu, 5 Nov 2020 06:48:52 +0000 (UTC) (envelope-from cem@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id 0A56mpxQ028208; Thu, 5 Nov 2020 06:48:51 GMT (envelope-from cem@FreeBSD.org) Received: (from cem@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id 0A56mphH028205; Thu, 5 Nov 2020 06:48:51 GMT (envelope-from cem@FreeBSD.org) Message-Id: <202011050648.0A56mphH028205@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: cem set sender to cem@FreeBSD.org using -f From: Conrad Meyer Date: Thu, 5 Nov 2020 06:48:51 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r367362 - in head/sys: compat/linprocfs fs/pseudofs X-SVN-Group: head X-SVN-Commit-Author: cem X-SVN-Commit-Paths: in head/sys: compat/linprocfs fs/pseudofs X-SVN-Commit-Revision: 367362 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.33 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Nov 2020 06:48:52 -0000 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);