Index: head/sys/compat/linprocfs/linprocfs.c =================================================================== --- head/sys/compat/linprocfs/linprocfs.c +++ head/sys/compat/linprocfs/linprocfs.c @@ -1252,10 +1252,6 @@ *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 @@ 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, Index: head/sys/fs/pseudofs/pseudofs.h =================================================================== --- head/sys/fs/pseudofs/pseudofs.h +++ head/sys/fs/pseudofs/pseudofs.h @@ -78,6 +78,7 @@ #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 Index: head/sys/fs/pseudofs/pseudofs_vnops.c =================================================================== --- head/sys/fs/pseudofs/pseudofs_vnops.c +++ head/sys/fs/pseudofs/pseudofs_vnops.c @@ -623,6 +623,50 @@ 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 @@ 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 @@ 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 @@ * 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);