Index: sys/compat/linprocfs/linprocfs.c =================================================================== --- sys/compat/linprocfs/linprocfs.c +++ 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: sys/fs/pseudofs/pseudofs.h =================================================================== --- sys/fs/pseudofs/pseudofs.h +++ 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: sys/fs/pseudofs/pseudofs_vnops.c =================================================================== --- sys/fs/pseudofs/pseudofs_vnops.c +++ sys/fs/pseudofs/pseudofs_vnops.c @@ -623,6 +623,37 @@ 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; + int error; + + ssh = arg; + + /* 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; + ssh->skip_bytes = 0; + } + + error = uiomove(__DECONST(void *, data), len, ssh->uio); + if (error != 0) + return (-error); + return (len); +} + /* * Read from a file */ @@ -636,7 +667,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); @@ -679,8 +711,16 @@ goto ret; } buflen = uio->uio_offset + uio->uio_resid; - if (buflen > PFS_MAXBUFSIZ) - buflen = PFS_MAXBUFSIZ; + 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); if (sb == NULL) { @@ -688,6 +728,14 @@ goto ret; } + if (pn->pn_flags & PFS_AUTODRAIN) { + ssh = (struct sbuf_seek_helper) { + .skip_bytes = uio->uio_offset, + .uio = uio, + }; + sbuf_set_drain(sb, pfs_sbuf_uio_drain, &ssh); + } + error = pn_fill(curthread, proc, pn, sb, uio); if (error) { @@ -700,9 +748,12 @@ * 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)) { + if (error == 0) + buflen = sbuf_len(sb); + error = uiomove_frombuf(sbuf_data(sb), buflen, uio); + } sbuf_delete(sb); ret: vn_lock(vn, locked | LK_RETRY);