xref: /freebsd/sys/compat/linprocfs/linprocfs.c (revision 6e66bfacfafa9263fcb33095921de6c346a6d86f)
1 /*
2  * Copyright (c) 2000 Dag-Erling Co�dan Sm�rgrav
3  * Copyright (c) 1999 Pierre Beyssac
4  * Copyright (c) 1993 Jan-Simon Pendry
5  * Copyright (c) 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *	@(#)procfs_status.c	8.4 (Berkeley) 6/15/94
40  *
41  * $FreeBSD$
42  */
43 
44 #include <sys/param.h>
45 #include <sys/blist.h>
46 #include <sys/dkstat.h>
47 #include <sys/jail.h>
48 #include <sys/kernel.h>
49 #include <sys/proc.h>
50 #include <sys/resourcevar.h>
51 #include <sys/sbuf.h>
52 #include <sys/systm.h>
53 #include <sys/tty.h>
54 #include <sys/vnode.h>
55 
56 #include <vm/vm.h>
57 #include <vm/pmap.h>
58 #include <vm/vm_map.h>
59 #include <vm/vm_param.h>
60 #include <vm/vm_object.h>
61 #include <vm/vm_zone.h>
62 #include <vm/swap_pager.h>
63 
64 #include <sys/exec.h>
65 #include <sys/user.h>
66 #include <sys/vmmeter.h>
67 
68 #include <machine/clock.h>
69 #include <machine/cputypes.h>
70 #include <machine/md_var.h>
71 
72 #include <compat/linux/linux_mib.h>
73 #include <compat/linprocfs/linprocfs.h>
74 
75 /*
76  * Various conversion macros
77  */
78 #define T2J(x) (((x) * 100) / (stathz ? stathz : hz))	/* ticks to jiffies */
79 #define T2S(x) ((x) / (stathz ? stathz : hz))		/* ticks to seconds */
80 #define B2K(x) ((x) >> 10)				/* bytes to kbytes */
81 #define B2P(x) ((x) >> PAGE_SHIFT)			/* bytes to pages */
82 #define P2B(x) ((x) << PAGE_SHIFT)			/* pages to bytes */
83 #define P2K(x) ((x) << (PAGE_SHIFT - 10))		/* pages to kbytes */
84 
85 int
86 linprocfs_domeminfo(curp, p, pfs, uio)
87 	struct proc *curp;
88 	struct proc *p;
89 	struct pfsnode *pfs;
90 	struct uio *uio;
91 {
92 	struct sbuf sb;
93 	char *ps;
94 	int r, xlen;
95 	unsigned long memtotal;		/* total memory in bytes */
96 	unsigned long memused;		/* used memory in bytes */
97 	unsigned long memfree;		/* free memory in bytes */
98 	unsigned long memshared;	/* shared memory ??? */
99 	unsigned long buffers, cached;	/* buffer / cache memory ??? */
100 	unsigned long swaptotal;	/* total swap space in bytes */
101 	unsigned long swapused;		/* used swap space in bytes */
102 	unsigned long swapfree;		/* free swap space in bytes */
103 	vm_object_t object;
104 
105 	if (uio->uio_rw != UIO_READ)
106 		return (EOPNOTSUPP);
107 
108 	memtotal = physmem * PAGE_SIZE;
109 	/*
110 	 * The correct thing here would be:
111 	 *
112 	memfree = cnt.v_free_count * PAGE_SIZE;
113 	memused = memtotal - memfree;
114 	 *
115 	 * but it might mislead linux binaries into thinking there
116 	 * is very little memory left, so we cheat and tell them that
117 	 * all memory that isn't wired down is free.
118 	 */
119 	memused = cnt.v_wire_count * PAGE_SIZE;
120 	memfree = memtotal - memused;
121 	if (swapblist == NULL) {
122 		swaptotal = 0;
123 		swapfree = 0;
124 	} else {
125 		swaptotal = swapblist->bl_blocks * 1024; /* XXX why 1024? */
126 		swapfree = swapblist->bl_root->u.bmu_avail * PAGE_SIZE;
127 	}
128 	swapused = swaptotal - swapfree;
129 	memshared = 0;
130 	for (object = TAILQ_FIRST(&vm_object_list); object != NULL;
131 	    object = TAILQ_NEXT(object, object_list))
132 		if (object->shadow_count > 1)
133 			memshared += object->resident_page_count;
134 	memshared *= PAGE_SIZE;
135 	/*
136 	 * We'd love to be able to write:
137 	 *
138 	buffers = bufspace;
139 	 *
140 	 * but bufspace is internal to vfs_bio.c and we don't feel
141 	 * like unstaticizing it just for linprocfs's sake.
142 	 */
143 	buffers = 0;
144 	cached = cnt.v_cache_count * PAGE_SIZE;
145 
146 	sbuf_new(&sb, NULL, 512, 0);
147 	sbuf_printf(&sb,
148 	    "        total:    used:    free:  shared: buffers:  cached:\n"
149 	    "Mem:  %lu %lu %lu %lu %lu %lu\n"
150 	    "Swap: %lu %lu %lu\n"
151 	    "MemTotal: %9lu kB\n"
152 	    "MemFree:  %9lu kB\n"
153 	    "MemShared:%9lu kB\n"
154 	    "Buffers:  %9lu kB\n"
155 	    "Cached:   %9lu kB\n"
156 	    "SwapTotal:%9lu kB\n"
157 	    "SwapFree: %9lu kB\n",
158 	    memtotal, memused, memfree, memshared, buffers, cached,
159 	    swaptotal, swapused, swapfree,
160 	    B2K(memtotal), B2K(memfree),
161 	    B2K(memshared), B2K(buffers), B2K(cached),
162 	    B2K(swaptotal), B2K(swapfree));
163 
164 	sbuf_finish(&sb);
165 	ps = sbuf_data(&sb) + uio->uio_offset;
166 	xlen = sbuf_len(&sb) -  uio->uio_offset;
167 	xlen = imin(xlen, uio->uio_resid);
168 	r = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
169 	sbuf_delete(&sb);
170 	return r;
171 }
172 
173 int
174 linprocfs_docpuinfo(curp, p, pfs, uio)
175 	struct proc *curp;
176 	struct proc *p;
177 	struct pfsnode *pfs;
178 	struct uio *uio;
179 {
180 	struct sbuf sb;
181 	char *ps;
182 	int r, xlen;
183 	int class, i, fqmhz, fqkhz;
184 
185 	/*
186          * We default the flags to include all non-conflicting flags,
187          * and the Intel versions of conflicting flags.
188 	 */
189         static char *flags[] = {
190 		"fpu",      "vme",     "de",       "pse",      "tsc",
191 		"msr",      "pae",     "mce",      "cx8",      "apic",
192 		"sep",      "sep",     "mtrr",     "pge",      "mca",
193 		"cmov",     "pat",     "pse36",    "pn",       "b19",
194 		"b20",      "b21",     "mmxext",   "mmx",      "fxsr",
195 		"xmm",      "b26",     "b27",      "b28",      "b29",
196 		"3dnowext", "3dnow"
197 	};
198 
199 	if (uio->uio_rw != UIO_READ)
200 		return (EOPNOTSUPP);
201 
202 	switch (cpu_class) {
203 	case CPUCLASS_286:
204 		class = 2;
205 		break;
206 	case CPUCLASS_386:
207 		class = 3;
208 		break;
209 	case CPUCLASS_486:
210 		class = 4;
211 		break;
212 	case CPUCLASS_586:
213 		class = 5;
214 		break;
215 	case CPUCLASS_686:
216 		class = 6;
217 		break;
218 	default:
219                 class = 0;
220 		break;
221 	}
222 
223 	sbuf_new(&sb, NULL, 512, 0);
224 	sbuf_printf(&sb,
225             "processor\t: %d\n"
226 	    "vendor_id\t: %.20s\n"
227 	    "cpu family\t: %d\n"
228 	    "model\t\t: %d\n"
229 	    "stepping\t: %d\n",
230 	    0, cpu_vendor, class, cpu, cpu_id & 0xf);
231 
232         sbuf_cat(&sb,
233             "flags\t\t:");
234 
235         if (!strcmp(cpu_vendor, "AuthenticAMD") && (class < 6)) {
236 		flags[16] = "fcmov";
237         } else if (!strcmp(cpu_vendor, "CyrixInstead")) {
238 		flags[24] = "cxmmx";
239         }
240 
241         for (i = 0; i < 32; i++)
242 		if (cpu_feature & (1 << i))
243 			sbuf_printf(&sb, " %s", flags[i]);
244 	sbuf_cat(&sb, "\n");
245         if (class >= 5) {
246 		fqmhz = (tsc_freq + 4999) / 1000000;
247 		fqkhz = ((tsc_freq + 4999) / 10000) % 100;
248 		sbuf_printf(&sb,
249 		    "cpu MHz\t\t: %d.%02d\n"
250 		    "bogomips\t: %d.%02d\n",
251 		    fqmhz, fqkhz, fqmhz, fqkhz);
252         }
253 
254 	sbuf_finish(&sb);
255 	ps = sbuf_data(&sb) + uio->uio_offset;
256 	xlen = sbuf_len(&sb) - uio->uio_offset;
257 	xlen = imin(xlen, uio->uio_resid);
258 	r = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
259 	sbuf_delete(&sb);
260 	return r;
261 }
262 
263 int
264 linprocfs_dostat(curp, p, pfs, uio)
265 	struct proc *curp;
266 	struct proc *p;
267 	struct pfsnode *pfs;
268 	struct uio *uio;
269 {
270 	struct sbuf sb;
271         char *ps;
272 	int r, xlen;
273 
274 	sbuf_new(&sb, NULL, 512, 0);
275 	sbuf_printf(&sb,
276 	    "cpu %ld %ld %ld %ld\n"
277 	    "disk 0 0 0 0\n"
278 	    "page %u %u\n"
279 	    "swap %u %u\n"
280 	    "intr %u\n"
281 	    "ctxt %u\n"
282 	    "btime %ld\n",
283 	    T2J(cp_time[CP_USER]),
284 	    T2J(cp_time[CP_NICE]),
285 	    T2J(cp_time[CP_SYS] /*+ cp_time[CP_INTR]*/),
286 	    T2J(cp_time[CP_IDLE]),
287 	    cnt.v_vnodepgsin,
288 	    cnt.v_vnodepgsout,
289 	    cnt.v_swappgsin,
290 	    cnt.v_swappgsout,
291 	    cnt.v_intr,
292 	    cnt.v_swtch,
293 	    boottime.tv_sec);
294 
295 	sbuf_finish(&sb);
296 	ps = sbuf_data(&sb) + uio->uio_offset;
297 	xlen = sbuf_len(&sb) -  uio->uio_offset;
298 	xlen = imin(xlen, uio->uio_resid);
299 	r = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
300 	sbuf_delete(&sb);
301 	return r;
302 }
303 
304 int
305 linprocfs_douptime(curp, p, pfs, uio)
306 	struct proc *curp;
307 	struct proc *p;
308 	struct pfsnode *pfs;
309 	struct uio *uio;
310 {
311 	struct sbuf sb;
312 	char *ps;
313 	int r, xlen;
314 	struct timeval tv;
315 
316 	getmicrouptime(&tv);
317 	sbuf_new(&sb, NULL, 64, 0);
318 	sbuf_printf(&sb, "%ld.%02ld %ld.%02ld\n",
319 	    tv.tv_sec, tv.tv_usec / 10000,
320 	    T2S(cp_time[CP_IDLE]), T2J(cp_time[CP_IDLE]) % 100);
321 	sbuf_finish(&sb);
322 	ps = sbuf_data(&sb) + uio->uio_offset;
323 	xlen = sbuf_len(&sb) -  uio->uio_offset;
324 	xlen = imin(xlen, uio->uio_resid);
325 	r = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
326 	sbuf_delete(&sb);
327 	return r;
328 }
329 
330 int
331 linprocfs_doversion(curp, p, pfs, uio)
332 	struct proc *curp;
333 	struct proc *p;
334 	struct pfsnode *pfs;
335 	struct uio *uio;
336 {
337 	struct sbuf sb;
338         char *ps;
339 	int r, xlen;
340 
341 	sbuf_new(&sb, NULL, 128, 0);
342 	sbuf_printf(&sb,
343 	    "%s version %s (des@freebsd.org) (gcc version " __VERSION__ ")"
344 	    " #4 Sun Dec 18 04:30:00 CET 1977\n",
345 	    linux_get_osname(curp),
346 	    linux_get_osrelease(curp));
347 	sbuf_finish(&sb);
348 	ps = sbuf_data(&sb) + uio->uio_offset;
349 	xlen = sbuf_len(&sb) -  uio->uio_offset;
350 	xlen = imin(xlen, uio->uio_resid);
351 	r = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
352 	sbuf_delete(&sb);
353 	return r;
354 }
355 
356 int
357 linprocfs_doprocstat(curp, p, pfs, uio)
358     	struct proc *curp;
359 	struct proc *p;
360 	struct pfsnode *pfs;
361 	struct uio *uio;
362 {
363 	struct kinfo_proc kp;
364 	struct sbuf sb;
365 	char *ps;
366 	int r, xlen;
367 
368 	fill_kinfo_proc(p, &kp);
369 	sbuf_new(&sb, NULL, 1024, 0);
370 	sbuf_printf(&sb, "%d", p->p_pid);
371 #define PS_ADD(name, fmt, arg) sbuf_printf(&sb, " " fmt, arg)
372 	PS_ADD("comm",		"(%s)",	p->p_comm);
373 	PS_ADD("statr",		"%c",	'0'); /* XXX */
374 	PS_ADD("ppid",		"%d",	p->p_pptr ? p->p_pptr->p_pid : 0);
375 	PS_ADD("pgrp",		"%d",	p->p_pgid);
376 	PS_ADD("session",	"%d",	p->p_session->s_sid);
377 	PS_ADD("tty",		"%d",	0); /* XXX */
378 	PS_ADD("tpgid",		"%d",	0); /* XXX */
379 	PS_ADD("flags",		"%u",	0); /* XXX */
380 	PS_ADD("minflt",	"%u",	0); /* XXX */
381 	PS_ADD("cminflt",	"%u",	0); /* XXX */
382 	PS_ADD("majflt",	"%u",	0); /* XXX */
383 	PS_ADD("cminflt",	"%u",	0); /* XXX */
384 	PS_ADD("utime",		"%d",	0); /* XXX */
385 	PS_ADD("stime",		"%d",	0); /* XXX */
386 	PS_ADD("cutime",	"%d",	0); /* XXX */
387 	PS_ADD("cstime",	"%d",	0); /* XXX */
388 	PS_ADD("counter",	"%d",	0); /* XXX */
389 	PS_ADD("priority",	"%d",	0); /* XXX */
390 	PS_ADD("timeout",	"%u",	0); /* XXX */
391 	PS_ADD("itrealvalue",	"%u",	0); /* XXX */
392 	PS_ADD("starttime",	"%d",	0); /* XXX */
393 	PS_ADD("vsize",		"%u",	kp.ki_size);
394 	PS_ADD("rss",		"%u",	P2K(kp.ki_rssize));
395 	PS_ADD("rlim",		"%u",	0); /* XXX */
396 	PS_ADD("startcode",	"%u",	(unsigned)0);
397 	PS_ADD("endcode",	"%u",	0); /* XXX */
398 	PS_ADD("startstack",	"%u",	0); /* XXX */
399 	PS_ADD("esp",		"%u",	0); /* XXX */
400 	PS_ADD("eip",		"%u",	0); /* XXX */
401 	PS_ADD("signal",	"%d",	0); /* XXX */
402 	PS_ADD("blocked",	"%d",	0); /* XXX */
403 	PS_ADD("sigignore",	"%d",	0); /* XXX */
404 	PS_ADD("sigcatch",	"%d",	0); /* XXX */
405 	PS_ADD("wchan",		"%u",	0); /* XXX */
406 	PS_ADD("nswap",		"%lu",	(long unsigned)0); /* XXX */
407 	PS_ADD("cnswap",	"%lu",	(long unsigned)0); /* XXX */
408 	PS_ADD("exitsignal",	"%d",	0); /* XXX */
409 	PS_ADD("processor",	"%d",	0); /* XXX */
410 #undef PS_ADD
411 	sbuf_putc(&sb, '\n');
412 
413 	sbuf_finish(&sb);
414 	ps = sbuf_data(&sb) + uio->uio_offset;
415 	xlen = sbuf_len(&sb) -  uio->uio_offset;
416 	xlen = imin(xlen, uio->uio_resid);
417 	r = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
418 	sbuf_delete(&sb);
419 	return r;
420 }
421 
422 /*
423  * Map process state to descriptive letter. Note that this does not
424  * quite correspond to what Linux outputs, but it's close enough.
425  */
426 static char *state_str[] = {
427 	"? (unknown)",
428 	"I (idle)",
429 	"R (running)",
430 	"S (sleeping)",
431 	"T (stopped)",
432 	"Z (zombie)",
433 	"W (waiting)",
434 	"M (mutex)"
435 };
436 
437 int
438 linprocfs_doprocstatus(curp, p, pfs, uio)
439     	struct proc *curp;
440 	struct proc *p;
441 	struct pfsnode *pfs;
442 	struct uio *uio;
443 {
444 	struct kinfo_proc kp;
445 	struct sbuf sb;
446 	char *ps;
447 	char *state;
448 	int i, r, xlen;
449 	segsz_t lsize;
450 
451 	sbuf_new(&sb, NULL, 1024, 0);
452 
453 	mtx_enter(&sched_lock, MTX_SPIN);
454 	if (p->p_stat > sizeof state_str / sizeof *state_str)
455 		state = state_str[0];
456 	else
457 		state = state_str[(int)p->p_stat];
458 	mtx_exit(&sched_lock, MTX_SPIN);
459 
460 	fill_kinfo_proc(p, &kp);
461 	sbuf_printf(&sb, "Name:\t%s\n",		p->p_comm); /* XXX escape */
462 	sbuf_printf(&sb, "State:\t%s\n",	state);
463 
464 	/*
465 	 * Credentials
466 	 */
467 	sbuf_printf(&sb, "Pid:\t%d\n",		p->p_pid);
468 	sbuf_printf(&sb, "PPid:\t%d\n",		p->p_pptr ? p->p_pptr->p_pid : 0);
469 	sbuf_printf(&sb, "Uid:\t%d %d %d %d\n", p->p_cred->p_ruid,
470 			                        p->p_ucred->cr_uid,
471 			                        p->p_cred->p_svuid,
472 			                        /* FreeBSD doesn't have fsuid */
473 				                p->p_ucred->cr_uid);
474 	sbuf_printf(&sb, "Gid:\t%d %d %d %d\n", p->p_cred->p_rgid,
475 			                        p->p_ucred->cr_gid,
476 			                        p->p_cred->p_svgid,
477 			                        /* FreeBSD doesn't have fsgid */
478 				                p->p_ucred->cr_gid);
479 	sbuf_cat(&sb, "Groups:\t");
480 	for (i = 0; i < p->p_ucred->cr_ngroups; i++)
481 		sbuf_printf(&sb, "%d ", p->p_ucred->cr_groups[i]);
482 	sbuf_putc(&sb, '\n');
483 
484 	/*
485 	 * Memory
486 	 *
487 	 * While our approximation of VmLib may not be accurate (I
488 	 * don't know of a simple way to verify it, and I'm not sure
489 	 * it has much meaning anyway), I believe it's good enough.
490 	 *
491 	 * The same code that could (I think) accurately compute VmLib
492 	 * could also compute VmLck, but I don't really care enough to
493 	 * implement it. Submissions are welcome.
494 	 */
495 	sbuf_printf(&sb, "VmSize:\t%8u kB\n",	B2K(kp.ki_size));
496 	sbuf_printf(&sb, "VmLck:\t%8u kB\n",	P2K(0)); /* XXX */
497 	sbuf_printf(&sb, "VmRss:\t%8u kB\n",	P2K(kp.ki_rssize));
498 	sbuf_printf(&sb, "VmData:\t%8u kB\n",	P2K(kp.ki_dsize));
499 	sbuf_printf(&sb, "VmStk:\t%8u kB\n",	P2K(kp.ki_ssize));
500 	sbuf_printf(&sb, "VmExe:\t%8u kB\n",	P2K(kp.ki_tsize));
501 	lsize = B2P(kp.ki_size) - kp.ki_dsize -
502 	    kp.ki_ssize - kp.ki_tsize - 1;
503 	sbuf_printf(&sb, "VmLib:\t%8u kB\n",	P2K(lsize));
504 
505 	/*
506 	 * Signal masks
507 	 *
508 	 * We support up to 128 signals, while Linux supports 32,
509 	 * but we only define 32 (the same 32 as Linux, to boot), so
510 	 * just show the lower 32 bits of each mask. XXX hack.
511 	 *
512 	 * NB: on certain platforms (Sparc at least) Linux actually
513 	 * supports 64 signals, but this code is a long way from
514 	 * running on anything but i386, so ignore that for now.
515 	 */
516 	sbuf_printf(&sb, "SigPnd:\t%08x\n",	p->p_siglist.__bits[0]);
517 	/*
518 	 * I can't seem to find out where the signal mask is in
519 	 * relation to struct proc, so SigBlk is left unimplemented.
520 	 */
521 	sbuf_printf(&sb, "SigBlk:\t%08x\n",	0); /* XXX */
522 	sbuf_printf(&sb, "SigIgn:\t%08x\n",	p->p_sigignore.__bits[0]);
523 	sbuf_printf(&sb, "SigCgt:\t%08x\n",	p->p_sigcatch.__bits[0]);
524 
525 	/*
526 	 * Linux also prints the capability masks, but we don't have
527 	 * capabilities yet, and when we do get them they're likely to
528 	 * be meaningless to Linux programs, so we lie. XXX
529 	 */
530 	sbuf_printf(&sb, "CapInh:\t%016x\n",	0);
531 	sbuf_printf(&sb, "CapPrm:\t%016x\n",	0);
532 	sbuf_printf(&sb, "CapEff:\t%016x\n",	0);
533 
534 	sbuf_finish(&sb);
535 	ps = sbuf_data(&sb) + uio->uio_offset;
536 	xlen = sbuf_len(&sb) -  uio->uio_offset;
537 	xlen = imin(xlen, uio->uio_resid);
538 	r = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
539 	sbuf_delete(&sb);
540 	return r;
541 }
542