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