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