bufcache.c revision 1.1 1 /* $NetBSD: bufcache.c,v 1.1 1999/11/15 06:16:56 simonb 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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __RCSID("$NetBSD: bufcache.c,v 1.1 1999/11/15 06:16:56 simonb Exp $");
42 #endif /* not lint */
43
44 #include <sys/param.h>
45 #include <sys/buf.h>
46 #include <sys/mount.h>
47 #include <sys/queue.h>
48 #include <sys/time.h>
49 #include <sys/vnode.h>
50
51 #include <err.h>
52 #include <kvm.h>
53 #include <nlist.h>
54 #include <paths.h>
55 #include <stdlib.h>
56
57 #include "systat.h"
58 #include "extern.h"
59
60
61 /*
62 * Definitions for the buffer free lists (from sys/kern/vfs_bio.c).
63 */
64 #define BQUEUES 4 /* number of free buffer queues */
65
66 #define BQ_LOCKED 0 /* super-blocks &c */
67 #define BQ_LRU 1 /* lru, useful buffers */
68 #define BQ_AGE 2 /* rubbish */
69 #define BQ_EMPTY 3 /* buffer headers with no memory */
70
71 #define VCACHE_SIZE 50
72
73 struct vcache {
74 int vc_age;
75 struct vnode *vc_addr;
76 struct vnode vc_node;
77 };
78
79 struct ml_entry {
80 int ml_count;
81 long ml_size;
82 long ml_valid;
83 struct mount *ml_addr;
84 struct mount ml_mount;
85 LIST_ENTRY(ml_entry) ml_entries;
86 };
87
88 static struct nlist namelist[] = {
89 #define X_NBUF 0
90 { "_nbuf" },
91 #define X_BUF 1
92 { "_buf" },
93 #define X_BUFQUEUES 2
94 { "_bufqueues" },
95 #define X_BUFPAGES 3
96 { "_bufpages" },
97 { "" },
98 };
99
100 static struct vcache vcache[VCACHE_SIZE];
101 static LIST_HEAD(mount_list, ml_entry) mount_list;
102
103 static int nbuf, bufpages;
104 static void *bufaddr;
105 static struct buf *buf = NULL;
106 static TAILQ_HEAD(bqueues, buf) bufqueues[BQUEUES];
107
108 static void vc_init __P((void));
109 static void ml_init __P((void));
110 static struct vnode *vc_lookup __P((struct vnode *));
111 static struct mount *ml_lookup __P((struct mount *, int, int));
112
113
114 WINDOW *
115 openbufcache()
116 {
117
118 return (subwin(stdscr, LINES-5-1, 0, 5, 0));
119 }
120
121 void
122 closebufcache(w)
123 WINDOW *w;
124 {
125
126 if (w == NULL)
127 return;
128 wclear(w);
129 wrefresh(w);
130 delwin(w);
131 ml_init(); /* Clear out mount list */
132 if (buf != NULL)
133 free(buf);
134 }
135
136 void
137 labelbufcache()
138 {
139 mvwprintw(wnd, 0, 0, "There are %d buffers using %d kBytes of memory.",
140 nbuf, bufpages * CLBYTES / 1024);
141 wclrtoeol(wnd);
142 wmove(wnd, 1, 0);
143 wclrtoeol(wnd);
144 mvwaddstr(wnd, 2, 0,
145 "File System Bufs used kB in use Bufsize kB");
146 wclrtoeol(wnd);
147 }
148
149 void
150 showbufcache()
151 {
152 int tbuf, i, lastrow;
153 long tvalid, tsize;
154 struct ml_entry *ml;
155
156 tbuf = tvalid = tsize = 0;
157 lastrow = 3; /* Leave room for header. */
158 for (i = lastrow, ml = LIST_FIRST(&mount_list); ml != NULL;
159 i++, ml = LIST_NEXT(ml, ml_entries)) {
160
161 /* Display in window if enough room. */
162 if (i < getmaxy(wnd) - 2) {
163 mvwprintw(wnd, i, 0, "%-20.20s", ml->ml_addr == NULL ?
164 "NULL" : ml->ml_mount.mnt_stat.f_mntonname);
165 wprintw(wnd, " %6d %8ld %8ld", ml->ml_count,
166 ml->ml_valid / 1024, ml->ml_size / 1024);
167 wclrtoeol(wnd);
168 lastrow = i;
169 }
170
171 /* Update statistics. */
172 tbuf += ml->ml_count;
173 tvalid += ml->ml_valid / 1024;
174 tsize += ml->ml_size / 1024;
175 }
176
177 wclrtobot(wnd);
178 mvwprintw(wnd, lastrow + 2, 0, "%-20s %6d %8d %8d",
179 "Total:", tbuf, tvalid, tsize);
180 }
181
182 int
183 initbufcache()
184 {
185 if (namelist[X_NBUF].n_type == 0) {
186 if (kvm_nlist(kd, namelist)) {
187 nlisterr(namelist);
188 return(0);
189 }
190 if (namelist[X_NBUF].n_type == 0) {
191 error("namelist on %s failed", _PATH_UNIX);
192 return(0);
193 }
194 }
195
196 NREAD(X_NBUF, &nbuf, sizeof(nbuf));
197 NREAD(X_BUFPAGES, &bufpages, sizeof(bufpages));
198
199 buf = (struct buf *)malloc(nbuf * sizeof(struct buf));
200 if (buf == NULL)
201 errx(1, "malloc failed\n");
202 NREAD(X_BUF, &bufaddr, sizeof(bufaddr));
203
204 return(1);
205 }
206
207 void
208 fetchbufcache()
209 {
210 int i, count;
211 struct buf *bp;
212 struct vnode *vn;
213 struct mount *mt;
214 struct ml_entry *ml;
215
216 /* Re-read bufqueues lists and buffer cache headers */
217 NREAD(X_BUFQUEUES, bufqueues, sizeof(bufqueues));
218 KREAD(bufaddr, buf, sizeof(struct buf) * nbuf);
219
220 /* Initialise vnode cache and mount list. */
221 vc_init();
222 ml_init();
223 for (i = 0; i < BQUEUES; i++) {
224 for (bp = bufqueues[i].tqh_first; bp != NULL;
225 bp = bp->b_freelist.tqe_next) {
226 if (bp != NULL) {
227 bp = (struct buf *)((u_long)bp + (u_long)buf -
228 (u_long)bufaddr);
229
230 if (bp->b_vp != NULL) {
231 vn = vc_lookup(bp->b_vp);
232 if (vn == NULL)
233 errx(1,
234 "vc_lookup returns NULL!\n");
235 if (vn->v_mount != NULL)
236 mt = ml_lookup(vn->v_mount,
237 bp->b_bufsize,
238 bp->b_bcount);
239 }
240 }
241 }
242 }
243
244 /* simple sort - there's not that many entries */
245 do {
246 if ((ml = LIST_FIRST(&mount_list)) == NULL ||
247 LIST_NEXT(ml, ml_entries) == NULL)
248 break;
249
250 count = 0;
251 for (ml = LIST_FIRST(&mount_list); ml != NULL;
252 ml = LIST_NEXT(ml, ml_entries)) {
253 if (LIST_NEXT(ml, ml_entries) == NULL)
254 break;
255 if (ml->ml_count < LIST_NEXT(ml, ml_entries)->ml_count) {
256 ml = LIST_NEXT(ml, ml_entries);
257 LIST_REMOVE(ml, ml_entries);
258 LIST_INSERT_HEAD(&mount_list, ml, ml_entries);
259 count++;
260 }
261 }
262 } while (count != 0);
263 }
264
265 static void
266 vc_init()
267 {
268 int i;
269
270 /* vc_addr == NULL for unused cache entry. */
271 for (i = 0; i < VCACHE_SIZE; i++)
272 vcache[i].vc_addr = NULL;
273 }
274
275 static void
276 ml_init()
277 {
278 struct ml_entry *ml;
279
280 /* Throw out the current mount list and start again. */
281 while ((ml = LIST_FIRST(&mount_list)) != NULL) {
282 LIST_REMOVE(ml, ml_entries);
283 free(ml);
284 }
285 }
286
287
288 static struct vnode *
289 vc_lookup(vaddr)
290 struct vnode *vaddr;
291 {
292 struct vnode *ret;
293 int i, oldest, match;
294
295 ret = NULL;
296 oldest = match = 0;
297 for (i = 0; i < VCACHE_SIZE || vcache[i].vc_addr == NULL; i++) {
298 vcache[i].vc_age++;
299 if (vcache[i].vc_addr == NULL)
300 break;
301 if (vcache[i].vc_age < vcache[oldest].vc_age)
302 oldest = i;
303 if (vcache[i].vc_addr == vaddr) {
304 vcache[i].vc_age = 0;
305 match = i;
306 ret = &vcache[i].vc_node;
307 }
308 }
309
310 /* Find an entry in the cache? */
311 if (ret != NULL)
312 return(ret);
313
314 /* Go past the end of the cache? */
315 if (i >= VCACHE_SIZE)
316 i = oldest;
317
318 /* Read in new vnode and reset age counter. */
319 KREAD(vaddr, &vcache[i].vc_node, sizeof(struct vnode));
320 vcache[i].vc_addr = vaddr;
321 vcache[i].vc_age = 0;
322
323 return(&vcache[i].vc_node);
324 }
325
326 static struct mount *
327 ml_lookup(maddr, size, valid)
328 struct mount *maddr;
329 int size, valid;
330 {
331 struct ml_entry *ml;
332
333 for (ml = LIST_FIRST(&mount_list); ml != NULL;
334 ml = LIST_NEXT(ml, ml_entries))
335 if (ml->ml_addr == maddr) {
336 ml->ml_count++;
337 ml->ml_size += size;
338 ml->ml_valid += valid;
339 if (ml->ml_addr == NULL)
340 return(NULL);
341 else
342 return(&ml->ml_mount);
343 }
344
345 if ((ml = malloc(sizeof(struct ml_entry))) == NULL)
346 errx(1, "out of memory\n");
347 LIST_INSERT_HEAD(&mount_list, ml, ml_entries);
348 ml->ml_count = 1;
349 ml->ml_size = size;
350 ml->ml_valid = valid;
351 ml->ml_addr = maddr;
352 if (maddr == NULL)
353 return(NULL);
354
355 KREAD(maddr, &ml->ml_mount, sizeof(struct mount));
356 return(&ml->ml_mount);
357 }
358