machine.c revision 1.6
1/*	$OpenBSD: machine.c,v 1.6 1997/09/09 14:58:21 millert Exp $	*/
2
3/*
4 * top - a top users display for Unix
5 *
6 * SYNOPSIS:  For an OpenBSD system
7 *
8 * DESCRIPTION:
9 * This is the machine-dependent module for OpenBSD
10 * Tested on:
11 *	i386
12 *
13 * LIBS: -lkvm
14 *
15 * TERMCAP: -ltermlib
16 *
17 * CFLAGS: -DHAVE_GETOPT
18 *
19 * AUTHOR:  Thorsten Lockert <tholo@sigmasoft.com>
20 *          Adapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu>
21 *          Patch for process wait display by Jarl F. Greipsland <jarle@idt.unit.no>
22 */
23
24#include <sys/types.h>
25#include <sys/signal.h>
26#include <sys/param.h>
27
28#define LASTPID
29#define DOSWAP
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <limits.h>
35#include <err.h>
36#include <nlist.h>
37#include <math.h>
38#include <kvm.h>
39#include <unistd.h>
40#include <sys/errno.h>
41#include <sys/sysctl.h>
42#include <sys/dir.h>
43#include <sys/dkstat.h>
44#include <sys/file.h>
45#include <sys/time.h>
46#include <sys/resource.h>
47
48#ifdef DOSWAP
49#include <err.h>
50#include <sys/map.h>
51#include <sys/conf.h>
52#endif
53
54static int check_nlist __P((struct nlist *));
55static int getkval __P((unsigned long, int *, int, char *));
56static int swapmode __P((int *, int *));
57
58#include "top.h"
59#include "display.h"
60#include "machine.h"
61#include "utils.h"
62
63/* get_process_info passes back a handle.  This is what it looks like: */
64
65struct handle
66{
67    struct kinfo_proc **next_proc;	/* points to next valid proc pointer */
68    int remaining;		/* number of pointers remaining */
69};
70
71/* declarations for load_avg */
72#include "loadavg.h"
73
74#define PP(pp, field) ((pp)->kp_proc . field)
75#define EP(pp, field) ((pp)->kp_eproc . field)
76#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
77
78/* what we consider to be process size: */
79#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
80
81/* definitions for indices in the nlist array */
82#define X_CP_TIME	0
83#define X_HZ		1
84
85#ifdef DOSWAP
86#define	VM_SWAPMAP	2
87#define	VM_NSWAPMAP	3
88#define	VM_SWDEVT	4
89#define	VM_NSWAP	5
90#define	VM_NSWDEV	6
91#define	VM_DMMAX	7
92#define	VM_NISWAP	8
93#define	VM_NISWDEV	9
94
95#define	X_LASTPID	10
96#elif defined(LASTPID)
97#define	X_LASTPID	2
98#endif
99
100static struct nlist nlst[] = {
101    { "_cp_time" },		/* 0 */
102    { "_hz" },			/* 1 */
103#ifdef DOSWAP
104    { "_swapmap" },		/* 2 */
105    { "_nswapmap" },		/* 3 */
106    { "_swdevt" },		/* 4 */
107    { "_nswap" },		/* 5 */
108    { "_nswdev" },		/* 6 */
109    { "_dmmax" },		/* 7 */
110    { "_niswap" },		/* 8 */
111    { "_niswdev" },		/* 9 */
112#endif
113#ifdef LASTPID
114    { "_lastpid" },		/* 2 / 10 */
115#endif
116    { 0 }
117};
118
119/*
120 *  These definitions control the format of the per-process area
121 */
122
123static char header[] =
124  "  PID X        PRI NICE  SIZE   RES STATE WAIT     TIME    CPU COMMAND";
125/* 0123456   -- field to fill in starts at header+6 */
126#define UNAME_START 6
127
128#define Proc_format \
129	"%5d %-8.8s %3d %4d %5s %5s %-5s %-6.6s %6s %5.2f%% %.14s"
130
131
132/* process state names for the "STATE" column of the display */
133/* the extra nulls in the string "run" are for adding a slash and
134   the processor number when needed */
135
136char *state_abbrev[] =
137{
138    "", "start", "run\0\0\0", "sleep", "stop", "zomb",
139};
140
141
142static kvm_t *kd;
143
144/* these are retrieved from the kernel in _init */
145
146static          int hz;
147
148/* these are offsets obtained via nlist and used in the get_ functions */
149
150static unsigned long cp_time_offset;
151#ifdef LASTPID
152static unsigned long lastpid_offset;
153static pid_t lastpid;
154#endif
155
156/* these are for calculating cpu state percentages */
157static int cp_time[CPUSTATES];
158static int cp_old[CPUSTATES];
159static int cp_diff[CPUSTATES];
160
161/* these are for detailing the process states */
162
163int process_states[7];
164char *procstatenames[] = {
165    "", " starting, ", " running, ", " idle, ", " stopped, ", " zombie, ",
166    NULL
167};
168
169/* these are for detailing the cpu states */
170
171int cpu_states[CPUSTATES];
172char *cpustatenames[] = {
173    "user", "nice", "system", "interrupt", "idle", NULL
174};
175
176/* these are for detailing the memory statistics */
177
178int memory_stats[8];
179char *memorynames[] = {
180    "Real: ", "K/", "K act/tot  ", "Free: ", "K  ",
181#ifdef DOSWAP
182    "Swap: ", "K/", "K used/tot",
183#endif
184    NULL
185};
186
187/* these are for keeping track of the proc array */
188
189static int nproc;
190static int onproc = -1;
191static int pref_len;
192static struct kinfo_proc *pbase;
193static struct kinfo_proc **pref;
194
195/* these are for getting the memory statistics */
196
197static int pageshift;		/* log base 2 of the pagesize */
198
199/* define pagetok in terms of pageshift */
200
201#define pagetok(size) ((size) << pageshift)
202
203int
204machine_init(statics)
205
206struct statics *statics;
207
208{
209    register int i = 0;
210    register int pagesize;
211    char errbuf[_POSIX2_LINE_MAX];
212
213    if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) {
214	warnx("%s", errbuf);
215	return(-1);
216    }
217
218    /* get the list of symbols we want to access in the kernel */
219    if (kvm_nlist(kd, nlst) <= 0) {
220	warnx("nlist failed");
221	return(-1);
222    }
223
224    /* make sure they were all found */
225    if (i > 0 && check_nlist(nlst) > 0)
226	return(-1);
227
228    /* get the symbol values out of kmem */
229    (void) getkval(nlst[X_HZ].n_value,     (int *)(&hz),	sizeof(hz),
230	    nlst[X_HZ].n_name);
231
232    /* stash away certain offsets for later use */
233    cp_time_offset = nlst[X_CP_TIME].n_value;
234#ifdef LASTPID
235    lastpid_offset = nlst[X_LASTPID].n_value;
236#endif
237
238    pbase = NULL;
239    pref = NULL;
240    onproc = -1;
241    nproc = 0;
242
243    /* get the page size with "getpagesize" and calculate pageshift from it */
244    pagesize = getpagesize();
245    pageshift = 0;
246    while (pagesize > 1)
247    {
248	pageshift++;
249	pagesize >>= 1;
250    }
251
252    /* we only need the amount of log(2)1024 for our conversion */
253    pageshift -= LOG1024;
254
255    /* fill in the statics information */
256    statics->procstate_names = procstatenames;
257    statics->cpustate_names = cpustatenames;
258    statics->memory_names = memorynames;
259
260    /* all done! */
261    return(0);
262}
263
264char *format_header(uname_field)
265
266register char *uname_field;
267
268{
269    register char *ptr;
270
271    ptr = header + UNAME_START;
272    while (*uname_field != '\0')
273    {
274	*ptr++ = *uname_field++;
275    }
276
277    return(header);
278}
279
280void
281get_system_info(si)
282
283struct system_info *si;
284
285{
286    int total;
287
288    /* get the cp_time array */
289    (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
290		   "_cp_time");
291#ifdef LASTPID
292    (void) getkval(lastpid_offset, (int *)&lastpid, sizeof(lastpid),
293		   "!");
294#endif
295
296    /* convert load averages to doubles */
297    {
298	register int i;
299	register double *infoloadp;
300	struct loadavg sysload;
301	size_t size = sizeof(sysload);
302	static int mib[] = { CTL_VM, VM_LOADAVG };
303
304	if (sysctl(mib, 2, &sysload, &size, NULL, 0) < 0) {
305	    warn("sysctl failed");
306	    bzero(&total, sizeof(total));
307	}
308
309	infoloadp = si->load_avg;
310	for (i = 0; i < 3; i++)
311	    *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
312    }
313
314    /* convert cp_time counts to percentages */
315    total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
316
317    /* sum memory statistics */
318    {
319	struct vmtotal total;
320	size_t size = sizeof(total);
321	static int mib[] = { CTL_VM, VM_METER };
322
323	/* get total -- systemwide main memory usage structure */
324	if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) {
325	    warn("sysctl failed");
326	    bzero(&total, sizeof(total));
327	}
328	/* convert memory stats to Kbytes */
329	memory_stats[0] = -1;
330	memory_stats[1] = pagetok(total.t_arm);
331	memory_stats[2] = pagetok(total.t_rm);
332	memory_stats[3] = -1;
333	memory_stats[4] = pagetok(total.t_free);
334	memory_stats[5] = -1;
335#ifdef DOSWAP
336	if (!swapmode(&memory_stats[6], &memory_stats[7])) {
337	    memory_stats[6] = 0;
338	    memory_stats[7] = 0;
339	}
340#endif
341    }
342
343    /* set arrays and strings */
344    si->cpustates = cpu_states;
345    si->memory = memory_stats;
346#ifdef LASTPID
347    if (lastpid > 0)
348	si->last_pid = lastpid;
349    else
350#endif
351	si->last_pid = -1;
352}
353
354static struct handle handle;
355
356caddr_t get_process_info(si, sel, compare)
357
358struct system_info *si;
359struct process_select *sel;
360int (*compare) __P((const void *, const void *));
361
362{
363    register int i;
364    register int total_procs;
365    register int active_procs;
366    register struct kinfo_proc **prefp;
367    register struct kinfo_proc *pp;
368
369    /* these are copied out of sel for speed */
370    int show_idle;
371    int show_system;
372    int show_uid;
373    int show_command;
374
375
376    if ((pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc)) == NULL) {
377	warnx("%s", kvm_geterr(kd));
378	quit(23);
379    }
380    if (nproc > onproc)
381	pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
382		* (onproc = nproc));
383    if (pref == NULL) {
384	warnx("Out of memory.");
385	quit(23);
386    }
387    /* get a pointer to the states summary array */
388    si->procstates = process_states;
389
390    /* set up flags which define what we are going to select */
391    show_idle = sel->idle;
392    show_system = sel->system;
393    show_uid = sel->uid != -1;
394    show_command = sel->command != NULL;
395
396    /* count up process states and get pointers to interesting procs */
397    total_procs = 0;
398    active_procs = 0;
399    memset((char *)process_states, 0, sizeof(process_states));
400    prefp = pref;
401    for (pp = pbase, i = 0; i < nproc; pp++, i++)
402    {
403	/*
404	 *  Place pointers to each valid proc structure in pref[].
405	 *  Process slots that are actually in use have a non-zero
406	 *  status field.  Processes with SSYS set are system
407	 *  processes---these get ignored unless show_sysprocs is set.
408	 */
409	if (PP(pp, p_stat) != 0 &&
410	    (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0)))
411	{
412	    total_procs++;
413	    process_states[(unsigned char) PP(pp, p_stat)]++;
414	    if ((PP(pp, p_stat) != SZOMB) &&
415		(show_idle || (PP(pp, p_pctcpu) != 0) ||
416		 (PP(pp, p_stat) == SRUN)) &&
417		(!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid))
418	    {
419		*prefp++ = pp;
420		active_procs++;
421	    }
422	}
423    }
424
425    /* if requested, sort the "interesting" processes */
426    if (compare != NULL)
427    {
428	qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare);
429    }
430
431    /* remember active and total counts */
432    si->p_total = total_procs;
433    si->p_active = pref_len = active_procs;
434
435    /* pass back a handle */
436    handle.next_proc = pref;
437    handle.remaining = active_procs;
438    return((caddr_t)&handle);
439}
440
441char fmt[MAX_COLS];		/* static area where result is built */
442
443char *format_next_process(handle, get_userid)
444
445caddr_t handle;
446char *(*get_userid)();
447
448{
449    register struct kinfo_proc *pp;
450    register int cputime;
451    register double pct;
452    struct handle *hp;
453    char waddr[sizeof(void *) * 2 + 3];	/* Hexify void pointer */
454    char *p_wait;
455
456    /* find and remember the next proc structure */
457    hp = (struct handle *)handle;
458    pp = *(hp->next_proc++);
459    hp->remaining--;
460
461
462    /* get the process's user struct and set cputime */
463    if ((PP(pp, p_flag) & P_INMEM) == 0) {
464	/*
465	 * Print swapped processes as <pname>
466	 */
467	char *comm = PP(pp, p_comm);
468#define COMSIZ sizeof(PP(pp, p_comm))
469	char buf[COMSIZ];
470	(void) strncpy(buf, comm, COMSIZ);
471	comm[0] = '<';
472	(void) strncpy(&comm[1], buf, COMSIZ - 2);
473	comm[COMSIZ - 2] = '\0';
474	(void) strncat(comm, ">", COMSIZ - 1);
475	comm[COMSIZ - 1] = '\0';
476    }
477
478    cputime = (PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks)) / hz;
479
480    /* calculate the base for cpu percentages */
481    pct = pctdouble(PP(pp, p_pctcpu));
482
483    if (PP(pp, p_wchan))
484        if (PP(pp, p_wmesg))
485	    p_wait = EP(pp, e_wmesg);
486	else {
487	    snprintf(waddr, sizeof(waddr), "%lx",
488		(unsigned long)(PP(pp, p_wchan)) & ~KERNBASE);
489	    p_wait = waddr;
490        }
491    else
492	p_wait = "-";
493
494    /* format this entry */
495    snprintf(fmt, MAX_COLS,
496	    Proc_format,
497	    PP(pp, p_pid),
498	    (*get_userid)(EP(pp, e_pcred.p_ruid)),
499	    PP(pp, p_priority) - PZERO,
500	    PP(pp, p_nice) - NZERO,
501	    format_k(pagetok(PROCSIZE(pp))),
502	    format_k(pagetok(VP(pp, vm_rssize))),
503	    (PP(pp, p_stat) == SSLEEP && PP(pp, p_slptime) > MAXSLP)
504	     ? "idle" : state_abbrev[(unsigned char) PP(pp, p_stat)],
505	    p_wait,
506	    format_time(cputime),
507	    100.0 * pct,
508	    printable(PP(pp, p_comm)));
509
510    /* return the result */
511    return(fmt);
512}
513
514
515/*
516 * check_nlist(nlst) - checks the nlist to see if any symbols were not
517 *		found.  For every symbol that was not found, a one-line
518 *		message is printed to stderr.  The routine returns the
519 *		number of symbols NOT found.
520 */
521
522static int check_nlist(nlst)
523
524register struct nlist *nlst;
525
526{
527    register int i;
528
529    /* check to see if we got ALL the symbols we requested */
530    /* this will write one line to stderr for every symbol not found */
531
532    i = 0;
533    while (nlst->n_name != NULL)
534    {
535	if (nlst->n_type == 0)
536	{
537	    /* this one wasn't found */
538	    (void) fprintf(stderr, "kernel: no symbol named `%s'\n",
539			   nlst->n_name);
540	    i = 1;
541	}
542	nlst++;
543    }
544
545    return(i);
546}
547
548
549/*
550 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
551 *	"offset" is the byte offset into the kernel for the desired value,
552 *  	"ptr" points to a buffer into which the value is retrieved,
553 *  	"size" is the size of the buffer (and the object to retrieve),
554 *  	"refstr" is a reference string used when printing error meessages,
555 *	    if "refstr" starts with a '!', then a failure on read will not
556 *  	    be fatal (this may seem like a silly way to do things, but I
557 *  	    really didn't want the overhead of another argument).
558 *
559 */
560
561static int getkval(offset, ptr, size, refstr)
562
563unsigned long offset;
564int *ptr;
565int size;
566char *refstr;
567
568{
569    if (kvm_read(kd, offset, (char *) ptr, size) != size)
570    {
571	if (*refstr == '!')
572	{
573	    return(0);
574	}
575	else
576	{
577	    warn("kvm_read for %s", refstr);
578	    quit(23);
579	}
580    }
581    return(1);
582}
583
584/* comparison routine for qsort */
585
586/*
587 *  proc_compare - comparison function for "qsort"
588 *	Compares the resource consumption of two processes using five
589 *  	distinct keys.  The keys (in descending order of importance) are:
590 *  	percent cpu, cpu ticks, state, resident set size, total virtual
591 *  	memory usage.  The process states are ordered as follows (from least
592 *  	to most important):  zombie, sleep, stop, start, run.  The array
593 *  	declaration below maps a process state index into a number that
594 *  	reflects this ordering.
595 */
596
597static unsigned char sorted_state[] =
598{
599    0,	/* not used		*/
600    4,	/* start		*/
601    5,	/* run			*/
602    2,	/* sleep		*/
603    3,	/* stop			*/
604    1	/* zombie		*/
605};
606
607int
608proc_compare(v1, v2)
609
610const void *v1, *v2;
611
612{
613    register struct proc **pp1 = (struct proc **)v1;
614    register struct proc **pp2 = (struct proc **)v2;
615    register struct kinfo_proc *p1;
616    register struct kinfo_proc *p2;
617    register int result;
618    register pctcpu lresult;
619
620    /* remove one level of indirection */
621    p1 = *(struct kinfo_proc **) pp1;
622    p2 = *(struct kinfo_proc **) pp2;
623
624    /* compare percent cpu (pctcpu) */
625    if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0)
626    {
627	/* use cpticks to break the tie */
628	if ((result = PP(p2, p_cpticks) - PP(p1, p_cpticks)) == 0)
629	{
630	    /* use process state to break the tie */
631	    if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] -
632			  sorted_state[(unsigned char) PP(p1, p_stat)])  == 0)
633	    {
634		/* use priority to break the tie */
635		if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0)
636		{
637		    /* use resident set size (rssize) to break the tie */
638		    if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
639		    {
640			/* use total memory to break the tie */
641			result = PROCSIZE(p2) - PROCSIZE(p1);
642		    }
643		}
644	    }
645	}
646    }
647    else
648    {
649	result = lresult < 0 ? -1 : 1;
650    }
651
652    return(result);
653}
654
655
656/*
657 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
658 *		the process does not exist.
659 *		It is EXTREMLY IMPORTANT that this function work correctly.
660 *		If top runs setuid root (as in SVR4), then this function
661 *		is the only thing that stands in the way of a serious
662 *		security problem.  It validates requests for the "kill"
663 *		and "renice" commands.
664 */
665
666int proc_owner(pid)
667
668pid_t pid;
669
670{
671    register int cnt;
672    register struct kinfo_proc **prefp;
673    register struct kinfo_proc *pp;
674
675    prefp = pref;
676    cnt = pref_len;
677    while (--cnt >= 0)
678    {
679	pp = *prefp++;
680	if (PP(pp, p_pid) == pid)
681	{
682	    return((int)EP(pp, e_pcred.p_ruid));
683	}
684    }
685    return(-1);
686}
687
688#ifdef DOSWAP
689/*
690 * swapmode is based on a program called swapinfo written
691 * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
692 */
693
694#define	SVAR(var) __STRING(var)	/* to force expansion */
695#define	KGET(idx, var)							\
696	KGET1(idx, &var, sizeof(var), SVAR(var))
697#define	KGET1(idx, p, s, msg)						\
698	KGET2(nlst[idx].n_value, p, s, msg)
699#define	KGET2(addr, p, s, msg)						\
700	if (kvm_read(kd, (u_long)(addr), p, s) != s)			\
701		warnx("cannot read %s: %s", msg, kvm_geterr(kd))
702
703static int
704swapmode(used, total)
705int *used;
706int *total;
707{
708	int nswap, nswdev, dmmax, nswapmap, niswap, niswdev;
709	int s, e, i, l, nfree;
710	struct swdevt *sw;
711	long *perdev;
712	struct map *swapmap, *kswapmap;
713	struct mapent *mp, *freemp;
714
715	KGET(VM_NSWAP, nswap);
716	KGET(VM_NSWDEV, nswdev);
717	KGET(VM_DMMAX, dmmax);
718	KGET(VM_NSWAPMAP, nswapmap);
719	KGET(VM_SWAPMAP, kswapmap);	/* kernel `swapmap' is a pointer */
720	if ((sw = malloc(nswdev * sizeof(*sw))) == NULL ||
721	    (perdev = malloc(nswdev * sizeof(*perdev))) == NULL ||
722	    (freemp = mp = malloc(nswapmap * sizeof(*mp))) == NULL)
723		err(1, "malloc");
724	KGET1(VM_SWDEVT, sw, nswdev * sizeof(*sw), "swdevt");
725	KGET2((long)kswapmap, mp, nswapmap * sizeof(*mp), "swapmap");
726
727	/* Supports sequential swap */
728	if (nlst[VM_NISWAP].n_value != 0) {
729		KGET(VM_NISWAP, niswap);
730		KGET(VM_NISWDEV, niswdev);
731	} else {
732		niswap = nswap;
733		niswdev = nswdev;
734	}
735
736	/* First entry in map is `struct map'; rest are mapent's. */
737	swapmap = (struct map *)mp;
738	if (nswapmap != swapmap->m_limit - (struct mapent *)kswapmap)
739		errx(1, "panic: nswapmap goof");
740
741	/* Count up swap space. */
742	nfree = 0;
743	memset(perdev, 0, nswdev * sizeof(*perdev));
744	for (mp++; mp->m_addr != 0; mp++) {
745		s = mp->m_addr;			/* start of swap region */
746		e = mp->m_addr + mp->m_size;	/* end of region */
747		nfree += mp->m_size;
748
749		/*
750		 * Swap space is split up among the configured disks.
751		 *
752		 * For interleaved swap devices, the first dmmax blocks
753		 * of swap space some from the first disk, the next dmmax
754		 * blocks from the next, and so on up to niswap blocks.
755		 *
756		 * Sequential swap devices follow the interleaved devices
757		 * (i.e. blocks starting at niswap) in the order in which
758		 * they appear in the swdev table.  The size of each device
759		 * will be a multiple of dmmax.
760		 *
761		 * The list of free space joins adjacent free blocks,
762		 * ignoring device boundries.  If we want to keep track
763		 * of this information per device, we'll just have to
764		 * extract it ourselves.  We know that dmmax-sized chunks
765		 * cannot span device boundaries (interleaved or sequential)
766		 * so we loop over such chunks assigning them to devices.
767		 */
768		i = -1;
769		while (s < e) {		/* XXX this is inefficient */
770			int bound = roundup(s+1, dmmax);
771
772			if (bound > e)
773				bound = e;
774			if (bound <= niswap) {
775				/* Interleaved swap chunk. */
776				if (i == -1)
777					i = (s / dmmax) % niswdev;
778				perdev[i] += bound - s;
779				if (++i >= niswdev)
780					i = 0;
781			} else {
782				/* Sequential swap chunk. */
783				if (i < niswdev) {
784					i = niswdev;
785					l = niswap + sw[i].sw_nblks;
786				}
787				while (s >= l) {
788					/* XXX don't die on bogus blocks */
789					if (i == nswdev-1)
790						break;
791					l += sw[++i].sw_nblks;
792				}
793				perdev[i] += bound - s;
794			}
795			s = bound;
796		}
797	}
798
799	*total = 0;
800	for (i = 0; i < nswdev; i++) {
801		int xsize, xfree;
802
803		xsize = sw[i].sw_nblks;
804		xfree = perdev[i];
805		*total += xsize;
806	}
807
808	/*
809	 * If only one partition has been set up via swapon(8), we don't
810	 * need to bother with totals.
811	 */
812#if DEV_BSHIFT < 10
813	*used = (*total - nfree) >> (10 - DEV_BSHIFT);
814	*total >>= 10 - DEV_BSHIFT;
815#elif DEV_BSHIFT > 10
816	*used = (*total - nfree) >> (DEV_BSHIFT - 10);
817	*total >>= DEV_BSHIFT - 10;
818#endif
819	free (sw); free (freemp); free (perdev);
820	return 1;
821}
822#endif
823