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