1 /* $NetBSD: bufcache.c,v 1.27 2016/10/24 00:40:17 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Simon Burge. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __RCSID("$NetBSD: bufcache.c,v 1.27 2016/10/24 00:40:17 christos Exp $"); 35 #endif /* not lint */ 36 37 #include <sys/param.h> 38 #include <sys/buf.h> 39 #include <sys/mount.h> 40 #include <sys/sysctl.h> 41 #include <sys/vnode.h> 42 43 #include <uvm/uvm_extern.h> 44 45 #include <err.h> 46 #include <errno.h> 47 #include <inttypes.h> 48 #include <math.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 #include <stdbool.h> 53 54 #include <miscfs/specfs/specdev.h> 55 56 #include "systat.h" 57 #include "extern.h" 58 59 #define VCACHE_SIZE 50 60 #define PAGEINFO_ROWS 5 61 62 struct vcache { 63 int vc_age; 64 struct vnode *vc_addr; 65 struct vnode vc_node; 66 }; 67 68 struct ml_entry { 69 u_int ml_count; 70 u_long ml_size; 71 u_long ml_valid; 72 struct mount *ml_addr; 73 LIST_ENTRY(ml_entry) ml_entries; 74 struct mount ml_mount; 75 }; 76 77 static struct vcache vcache[VCACHE_SIZE]; 78 static LIST_HEAD(mount_list, ml_entry) mount_list; 79 80 static uint64_t bufmem; 81 static u_int nbuf, pgwidth, kbwidth; 82 static struct uvmexp_sysctl uvmexp; 83 84 static void vc_init(void); 85 static void ml_init(void); 86 static struct vnode *vc_lookup(struct vnode *); 87 static struct mount *ml_lookup(struct mount *, int, int); 88 static void fetchuvmexp(void); 89 90 91 WINDOW * 92 openbufcache(void) 93 { 94 95 return (subwin(stdscr, -1, 0, 5, 0)); 96 } 97 98 void 99 closebufcache(WINDOW *w) 100 { 101 102 if (w == NULL) 103 return; 104 wclear(w); 105 wrefresh(w); 106 delwin(w); 107 ml_init(); /* Clear out mount list */ 108 } 109 110 void 111 labelbufcache(void) 112 { 113 int i; 114 115 for (i = 0; i <= PAGEINFO_ROWS; i++) { 116 wmove(wnd, i, 0); 117 wclrtoeol(wnd); 118 } 119 mvwaddstr(wnd, PAGEINFO_ROWS + 1, 0, "File System Bufs used" 120 " % kB in use % Bufsize kB % Util %"); 121 wclrtoeol(wnd); 122 } 123 124 void 125 showbufcache(void) 126 { 127 int tbuf, i, lastrow; 128 double tvalid, tsize; 129 struct ml_entry *ml; 130 size_t len; 131 static int mib[] = { -1, 0 }; 132 133 if (mib[0] == -1) { 134 len = __arraycount(mib); 135 if (sysctlnametomib("vm.bufmem", mib, &len) == -1) 136 error("can't get \"vm.bufmem\" mib: %s", 137 strerror(errno)); 138 } 139 len = sizeof(bufmem); 140 if (sysctl(mib, 2, &bufmem, &len, NULL, 0) == -1) 141 error("can't get \"vm.bufmem\": %s", strerror(errno)); 142 143 mvwprintw(wnd, 0, 0, 144 " %*d metadata buffers using %*"PRIu64" kBytes of " 145 "memory (%2.0f%%).", 146 pgwidth, nbuf, kbwidth, bufmem / 1024, 147 ((bufmem * 100.0) + 0.5) / getpagesize() / uvmexp.npages); 148 wclrtoeol(wnd); 149 mvwprintw(wnd, 1, 0, 150 " %*" PRIu64 " pages for cached file data using %*" 151 PRIu64 " kBytes of memory (%2.0f%%).", 152 pgwidth, uvmexp.filepages, 153 kbwidth, uvmexp.filepages * getpagesize() / 1024, 154 (uvmexp.filepages * 100 + 0.5) / uvmexp.npages); 155 wclrtoeol(wnd); 156 mvwprintw(wnd, 2, 0, 157 " %*" PRIu64 " pages for executables using %*" 158 PRIu64 " kBytes of memory (%2.0f%%).", 159 pgwidth, uvmexp.execpages, 160 kbwidth, uvmexp.execpages * getpagesize() / 1024, 161 (uvmexp.execpages * 100 + 0.5) / uvmexp.npages); 162 wclrtoeol(wnd); 163 mvwprintw(wnd, 3, 0, 164 " %*" PRIu64 " pages for anon (non-file) data %*" 165 PRIu64 " kBytes of memory (%2.0f%%).", 166 pgwidth, uvmexp.anonpages, 167 kbwidth, uvmexp.anonpages * getpagesize() / 1024, 168 (uvmexp.anonpages * 100 + 0.5) / uvmexp.npages); 169 wclrtoeol(wnd); 170 mvwprintw(wnd, 4, 0, 171 " %*" PRIu64 " free pages %*" 172 PRIu64 " kBytes of memory (%2.0f%%).", 173 pgwidth, uvmexp.free, 174 kbwidth, uvmexp.free * getpagesize() / 1024, 175 (uvmexp.free * 100 + 0.5) / uvmexp.npages); 176 wclrtoeol(wnd); 177 178 if (nbuf == 0 || bufmem == 0) { 179 wclrtobot(wnd); 180 return; 181 } 182 183 tbuf = 0; 184 tvalid = tsize = 0; 185 lastrow = PAGEINFO_ROWS + 2; /* Leave room for header. */ 186 for (i = lastrow, ml = LIST_FIRST(&mount_list); ml != NULL; 187 i++, ml = LIST_NEXT(ml, ml_entries)) { 188 189 int cnt = ml->ml_count; 190 double v = ml->ml_valid; 191 double s = ml->ml_size; 192 193 /* Display in window if enough room. */ 194 if (i < getmaxy(wnd) - 2) { 195 mvwprintw(wnd, i, 0, "%-20.20s", ml->ml_addr == NULL ? 196 "NULL" : ml->ml_mount.mnt_stat.f_mntonname); 197 wprintw(wnd, 198 " %6d %3d %8ld %3.0f %8ld %3.0f %3.0f", 199 cnt, (100 * cnt) / nbuf, 200 (long)(v/1024), 100 * v / bufmem, 201 (long)(s/1024), 100 * s / bufmem, 202 100 * v / s); 203 wclrtoeol(wnd); 204 lastrow = i; 205 } 206 207 /* Update statistics. */ 208 tbuf += cnt; 209 tvalid += v; 210 tsize += s; 211 } 212 213 wclrtobot(wnd); 214 mvwprintw(wnd, lastrow + 2, 0, 215 "%-20s %6d %3d %8ld %3.0f %8ld %3.0f %3.0f", 216 "Total:", tbuf, (100 * tbuf) / nbuf, 217 (long)(tvalid/1024), 100 * tvalid / bufmem, 218 (long)(tsize/1024), 100 * tsize / bufmem, 219 tsize != 0 ? ((100 * tvalid) / tsize) : 0); 220 } 221 222 int 223 initbufcache(void) 224 { 225 fetchuvmexp(); 226 pgwidth = (int)(floor(log10((double)uvmexp.npages)) + 1); 227 kbwidth = (int)(floor(log10(uvmexp.npages * getpagesize() / 1024.0)) + 228 1); 229 230 return(1); 231 } 232 233 static void 234 fetchuvmexp(void) 235 { 236 int mib[2]; 237 size_t size; 238 239 /* Re-read pages used for vnodes & executables */ 240 size = sizeof(uvmexp); 241 mib[0] = CTL_VM; 242 mib[1] = VM_UVMEXP2; 243 if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0) { 244 error("can't get uvmexp: %s\n", strerror(errno)); 245 memset(&uvmexp, 0, sizeof(uvmexp)); 246 } 247 } 248 249 void 250 fetchbufcache(void) 251 { 252 int count; 253 struct buf_sysctl *bp, *buffers; 254 struct vnode *vn; 255 struct ml_entry *ml; 256 int mib[6]; 257 size_t size; 258 int extraslop = 0; 259 260 /* Re-read pages used for vnodes & executables */ 261 fetchuvmexp(); 262 263 /* Initialise vnode cache and mount list. */ 264 vc_init(); 265 ml_init(); 266 267 /* Get metadata buffers */ 268 size = 0; 269 buffers = NULL; 270 mib[0] = CTL_KERN; 271 mib[1] = KERN_BUF; 272 mib[2] = KERN_BUF_ALL; 273 mib[3] = KERN_BUF_ALL; 274 mib[4] = (int)sizeof(struct buf_sysctl); 275 mib[5] = INT_MAX; /* we want them all */ 276 again: 277 if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0) { 278 error("can't get buffers size: %s\n", strerror(errno)); 279 return; 280 } 281 if (size == 0) 282 return; 283 284 size += extraslop * sizeof(struct buf_sysctl); 285 buffers = malloc(size); 286 if (buffers == NULL) { 287 error("can't allocate buffers: %s\n", strerror(errno)); 288 return; 289 } 290 if (sysctl(mib, 6, buffers, &size, NULL, 0) < 0) { 291 free(buffers); 292 if (extraslop == 0) { 293 extraslop = 100; 294 goto again; 295 } 296 error("can't get buffers: %s\n", strerror(errno)); 297 return; 298 } 299 300 nbuf = size / sizeof(struct buf_sysctl); 301 for (bp = buffers; bp < buffers + nbuf; bp++) { 302 if (UINT64TOPTR(bp->b_vp) != NULL) { 303 struct mount *mp; 304 vn = vc_lookup(UINT64TOPTR(bp->b_vp)); 305 if (vn == NULL) 306 break; 307 308 mp = vn->v_mount; 309 /* 310 * References to mounted-on vnodes should be 311 * counted towards the mounted filesystem. 312 */ 313 if (vn->v_type == VBLK && vn->v_specnode != NULL) { 314 specnode_t sn; 315 specdev_t sd; 316 if (!KREAD(vn->v_specnode, &sn, sizeof(sn))) 317 continue; 318 if (!KREAD(sn.sn_dev, &sd, sizeof(sd))) 319 continue; 320 if (sd.sd_mountpoint) 321 mp = sd.sd_mountpoint; 322 } 323 if (mp != NULL) 324 (void)ml_lookup(mp, bp->b_bufsize, 325 bp->b_bcount); 326 } 327 } 328 329 /* simple sort - there's not that many entries */ 330 do { 331 if ((ml = LIST_FIRST(&mount_list)) == NULL || 332 LIST_NEXT(ml, ml_entries) == NULL) 333 break; 334 335 count = 0; 336 for (ml = LIST_FIRST(&mount_list); ml != NULL; 337 ml = LIST_NEXT(ml, ml_entries)) { 338 if (LIST_NEXT(ml, ml_entries) == NULL) 339 break; 340 if (ml->ml_count < LIST_NEXT(ml, ml_entries)->ml_count) { 341 ml = LIST_NEXT(ml, ml_entries); 342 LIST_REMOVE(ml, ml_entries); 343 LIST_INSERT_HEAD(&mount_list, ml, ml_entries); 344 count++; 345 } 346 } 347 } while (count != 0); 348 349 free(buffers); 350 } 351 352 static void 353 vc_init(void) 354 { 355 int i; 356 357 /* vc_addr == NULL for unused cache entry. */ 358 for (i = 0; i < VCACHE_SIZE; i++) 359 vcache[i].vc_addr = NULL; 360 } 361 362 static void 363 ml_init(void) 364 { 365 struct ml_entry *ml; 366 367 /* Throw out the current mount list and start again. */ 368 while ((ml = LIST_FIRST(&mount_list)) != NULL) { 369 LIST_REMOVE(ml, ml_entries); 370 free(ml); 371 } 372 } 373 374 375 static struct vnode * 376 vc_lookup(struct vnode *vaddr) 377 { 378 struct vnode *ret; 379 size_t i, oldest; 380 381 ret = NULL; 382 oldest = 0; 383 for (i = 0; i < VCACHE_SIZE; i++) { 384 if (vcache[i].vc_addr == NULL) 385 break; 386 vcache[i].vc_age++; 387 if (vcache[i].vc_age < vcache[oldest].vc_age) 388 oldest = i; 389 if (vcache[i].vc_addr == vaddr) { 390 vcache[i].vc_age = 0; 391 ret = &vcache[i].vc_node; 392 } 393 } 394 395 /* Find an entry in the cache? */ 396 if (ret != NULL) 397 return(ret); 398 399 /* Go past the end of the cache? */ 400 if (i >= VCACHE_SIZE) 401 i = oldest; 402 403 /* Read in new vnode and reset age counter. */ 404 if (KREAD(vaddr, &vcache[i].vc_node, sizeof(struct vnode)) == 0) 405 return NULL; 406 vcache[i].vc_addr = vaddr; 407 vcache[i].vc_age = 0; 408 409 return(&vcache[i].vc_node); 410 } 411 412 static struct mount * 413 ml_lookup(struct mount *maddr, int size, int valid) 414 { 415 struct ml_entry *ml; 416 417 for (ml = LIST_FIRST(&mount_list); ml != NULL; 418 ml = LIST_NEXT(ml, ml_entries)) 419 if (ml->ml_addr == maddr) { 420 ml->ml_count++; 421 ml->ml_size += size; 422 ml->ml_valid += valid; 423 if (ml->ml_addr == NULL) 424 return(NULL); 425 else 426 return(&ml->ml_mount); 427 } 428 429 if ((ml = malloc(sizeof(struct ml_entry))) == NULL) { 430 error("out of memory"); 431 die(0); 432 } 433 LIST_INSERT_HEAD(&mount_list, ml, ml_entries); 434 ml->ml_count = 1; 435 ml->ml_size = size; 436 ml->ml_valid = valid; 437 ml->ml_addr = maddr; 438 if (maddr == NULL) 439 return(NULL); 440 441 KREAD(maddr, &ml->ml_mount, sizeof(struct mount)); 442 return(&ml->ml_mount); 443 } 444