FreeBSD/Linux Kernel Cross Reference
sys/port/cache.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 enum
9 {
10 NHASH = 128,
11 MAXCACHE = 1024*1024,
12 NFILE = 4096,
13 NEXTENT = 200, /* extent allocation size */
14 };
15
16 typedef struct Extent Extent;
17 struct Extent
18 {
19 int bid;
20 ulong start;
21 int len;
22 Page *cache;
23 Extent *next;
24 };
25
26 typedef struct Mntcache Mntcache;
27 struct Mntcache
28 {
29 Qid qid;
30 int dev;
31 int type;
32 QLock;
33 Extent *list;
34 Mntcache *hash;
35 Mntcache *prev;
36 Mntcache *next;
37 };
38
39 typedef struct Cache Cache;
40 struct Cache
41 {
42 Lock;
43 int pgno;
44 Mntcache *head;
45 Mntcache *tail;
46 Mntcache *hash[NHASH];
47 };
48
49 typedef struct Ecache Ecache;
50 struct Ecache
51 {
52 Lock;
53 int total;
54 int free;
55 Extent* head;
56 };
57
58 static Image fscache;
59 static Cache cache;
60 static Ecache ecache;
61 static int maxcache = MAXCACHE;
62
63 static void
64 extentfree(Extent* e)
65 {
66 lock(&ecache);
67 e->next = ecache.head;
68 ecache.head = e;
69 ecache.free++;
70 unlock(&ecache);
71 }
72
73 static Extent*
74 extentalloc(void)
75 {
76 Extent *e;
77 int i;
78
79 lock(&ecache);
80 if(ecache.head == nil){
81 e = xalloc(NEXTENT*sizeof(Extent));
82 if(e == nil){
83 unlock(&ecache);
84 return nil;
85 }
86 for(i = 0; i < NEXTENT; i++){
87 e->next = ecache.head;
88 ecache.head = e;
89 e++;
90 }
91 ecache.free += NEXTENT;
92 ecache.total += NEXTENT;
93 }
94
95 e = ecache.head;
96 ecache.head = e->next;
97 memset(e, 0, sizeof(Extent));
98 ecache.free--;
99 unlock(&ecache);
100
101 return e;
102 }
103
104 void
105 cinit(void)
106 {
107 int i;
108 Mntcache *m;
109
110 cache.head = xalloc(sizeof(Mntcache)*NFILE);
111 m = cache.head;
112 if (m == nil)
113 panic("cinit: no memory");
114
115 /* a better algorithm would be nice */
116 // if(conf.npage*BY2PG > 200*MB)
117 // maxcache = 10*MAXCACHE;
118 // if(conf.npage*BY2PG > 400*MB)
119 // maxcache = 50*MAXCACHE;
120
121 for(i = 0; i < NFILE-1; i++) {
122 m->next = m+1;
123 m->prev = m-1;
124 m++;
125 }
126
127 cache.tail = m;
128 cache.tail->next = 0;
129 cache.head->prev = 0;
130
131 fscache.notext = 1;
132 }
133
134 void
135 cprint(Chan *c, Mntcache *m, char *s)
136 {
137 ulong o;
138 int nb, ct;
139 Extent *e;
140
141 nb = 0;
142 ct = 1;
143 o = 0;
144 if(m->list)
145 o = m->list->start;
146 for(e = m->list; e; e = e->next) {
147 nb += e->len;
148 if(o != e->start)
149 ct = 0;
150 o = e->start+e->len;
151 }
152 pprint("%s: %#lux.%#lux %d %d %s (%d %c)\n",
153 s, m->qid.path, m->qid.vers, m->type, m->dev, c->path, nb, ct ? 'C' : 'N');
154
155 for(e = m->list; e; e = e->next) {
156 pprint("\t%4d %5d %4d %lux\n",
157 e->bid, e->start, e->len, e->cache);
158 }
159 }
160
161 Page*
162 cpage(Extent *e)
163 {
164 /* Easy consistency check */
165 if(e->cache->daddr != e->bid)
166 return 0;
167
168 return lookpage(&fscache, e->bid);
169 }
170
171 void
172 cnodata(Mntcache *m)
173 {
174 Extent *e, *n;
175
176 /*
177 * Invalidate all extent data
178 * Image lru will waste the pages
179 */
180 for(e = m->list; e; e = n) {
181 n = e->next;
182 extentfree(e);
183 }
184 m->list = 0;
185 }
186
187 void
188 ctail(Mntcache *m)
189 {
190 /* Unlink and send to the tail */
191 if(m->prev)
192 m->prev->next = m->next;
193 else
194 cache.head = m->next;
195 if(m->next)
196 m->next->prev = m->prev;
197 else
198 cache.tail = m->prev;
199
200 if(cache.tail) {
201 m->prev = cache.tail;
202 cache.tail->next = m;
203 m->next = 0;
204 cache.tail = m;
205 }
206 else {
207 cache.head = m;
208 cache.tail = m;
209 m->prev = 0;
210 m->next = 0;
211 }
212 }
213
214 void
215 copen(Chan *c)
216 {
217 int h;
218 Extent *e, *next;
219 Mntcache *m, *f, **l;
220
221 /* directories aren't cacheable and append-only files confuse us */
222 if(c->qid.type&(QTDIR|QTAPPEND))
223 return;
224
225 h = c->qid.path%NHASH;
226 lock(&cache);
227 for(m = cache.hash[h]; m; m = m->hash) {
228 if(m->qid.path == c->qid.path)
229 if(m->qid.type == c->qid.type)
230 if(m->dev == c->dev && m->type == c->type) {
231 c->mcp = m;
232 ctail(m);
233 unlock(&cache);
234
235 /* File was updated, invalidate cache */
236 if(m->qid.vers != c->qid.vers) {
237 m->qid.vers = c->qid.vers;
238 qlock(m);
239 cnodata(m);
240 qunlock(m);
241 }
242 return;
243 }
244 }
245
246 /* LRU the cache headers */
247 m = cache.head;
248 l = &cache.hash[m->qid.path%NHASH];
249 for(f = *l; f; f = f->hash) {
250 if(f == m) {
251 *l = m->hash;
252 break;
253 }
254 l = &f->hash;
255 }
256
257 m->qid = c->qid;
258 m->dev = c->dev;
259 m->type = c->type;
260
261 l = &cache.hash[h];
262 m->hash = *l;
263 *l = m;
264 ctail(m);
265
266 qlock(m);
267 c->mcp = m;
268 e = m->list;
269 m->list = 0;
270 unlock(&cache);
271
272 while(e) {
273 next = e->next;
274 extentfree(e);
275 e = next;
276 }
277 qunlock(m);
278 }
279
280 static int
281 cdev(Mntcache *m, Chan *c)
282 {
283 if(m->qid.path != c->qid.path)
284 return 0;
285 if(m->qid.type != c->qid.type)
286 return 0;
287 if(m->dev != c->dev)
288 return 0;
289 if(m->type != c->type)
290 return 0;
291 if(m->qid.vers != c->qid.vers)
292 return 0;
293 return 1;
294 }
295
296 int
297 cread(Chan *c, uchar *buf, int len, vlong off)
298 {
299 KMap *k;
300 Page *p;
301 Mntcache *m;
302 Extent *e, **t;
303 int o, l, total;
304 ulong offset;
305
306 if(off+len > maxcache)
307 return 0;
308
309 m = c->mcp;
310 if(m == 0)
311 return 0;
312
313 qlock(m);
314 if(cdev(m, c) == 0) {
315 qunlock(m);
316 return 0;
317 }
318
319 offset = off;
320 t = &m->list;
321 for(e = *t; e; e = e->next) {
322 if(offset >= e->start && offset < e->start+e->len)
323 break;
324 t = &e->next;
325 }
326
327 if(e == 0) {
328 qunlock(m);
329 return 0;
330 }
331
332 total = 0;
333 while(len) {
334 p = cpage(e);
335 if(p == 0) {
336 *t = e->next;
337 extentfree(e);
338 qunlock(m);
339 return total;
340 }
341
342 o = offset - e->start;
343 l = len;
344 if(l > e->len-o)
345 l = e->len-o;
346
347 k = kmap(p);
348 if(waserror()) {
349 kunmap(k);
350 putpage(p);
351 qunlock(m);
352 nexterror();
353 }
354
355 memmove(buf, (uchar*)VA(k) + o, l);
356
357 poperror();
358 kunmap(k);
359
360 putpage(p);
361
362 buf += l;
363 len -= l;
364 offset += l;
365 total += l;
366 t = &e->next;
367 e = e->next;
368 if(e == 0 || e->start != offset)
369 break;
370 }
371
372 qunlock(m);
373 return total;
374 }
375
376 Extent*
377 cchain(uchar *buf, ulong offset, int len, Extent **tail)
378 {
379 int l;
380 Page *p;
381 KMap *k;
382 Extent *e, *start, **t;
383
384 start = 0;
385 *tail = 0;
386 t = &start;
387 while(len) {
388 e = extentalloc();
389 if(e == 0)
390 break;
391
392 p = auxpage();
393 if(p == 0) {
394 extentfree(e);
395 break;
396 }
397 l = len;
398 if(l > BY2PG)
399 l = BY2PG;
400
401 e->cache = p;
402 e->start = offset;
403 e->len = l;
404
405 lock(&cache);
406 e->bid = cache.pgno;
407 cache.pgno += BY2PG;
408 /* wrap the counter; low bits are unused by pghash but checked by lookpage */
409 if((cache.pgno & ~(BY2PG-1)) == 0){
410 if(cache.pgno == BY2PG-1){
411 print("cache wrapped\n");
412 cache.pgno = 0;
413 }else
414 cache.pgno++;
415 }
416 unlock(&cache);
417
418 p->daddr = e->bid;
419 k = kmap(p);
420 if(waserror()) { /* buf may be virtual */
421 kunmap(k);
422 nexterror();
423 }
424 memmove((void*)VA(k), buf, l);
425 poperror();
426 kunmap(k);
427
428 cachepage(p, &fscache);
429 putpage(p);
430
431 buf += l;
432 offset += l;
433 len -= l;
434
435 *t = e;
436 *tail = e;
437 t = &e->next;
438 }
439
440 return start;
441 }
442
443 int
444 cpgmove(Extent *e, uchar *buf, int boff, int len)
445 {
446 Page *p;
447 KMap *k;
448
449 p = cpage(e);
450 if(p == 0)
451 return 0;
452
453 k = kmap(p);
454 if(waserror()) { /* Since buf may be virtual */
455 kunmap(k);
456 nexterror();
457 }
458
459 memmove((uchar*)VA(k)+boff, buf, len);
460
461 poperror();
462 kunmap(k);
463 putpage(p);
464
465 return 1;
466 }
467
468 void
469 cupdate(Chan *c, uchar *buf, int len, vlong off)
470 {
471 Mntcache *m;
472 Extent *tail;
473 Extent *e, *f, *p;
474 int o, ee, eblock;
475 ulong offset;
476
477 if(off > maxcache || len == 0)
478 return;
479
480 m = c->mcp;
481 if(m == 0)
482 return;
483 qlock(m);
484 if(cdev(m, c) == 0) {
485 qunlock(m);
486 return;
487 }
488
489 /*
490 * Find the insertion point
491 */
492 offset = off;
493 p = 0;
494 for(f = m->list; f; f = f->next) {
495 if(f->start > offset)
496 break;
497 p = f;
498 }
499
500 /* trim if there is a successor */
501 eblock = offset+len;
502 if(f != 0 && eblock > f->start) {
503 len -= (eblock - f->start);
504 if(len <= 0) {
505 qunlock(m);
506 return;
507 }
508 }
509
510 if(p == 0) { /* at the head */
511 e = cchain(buf, offset, len, &tail);
512 if(e != 0) {
513 m->list = e;
514 tail->next = f;
515 }
516 qunlock(m);
517 return;
518 }
519
520 /* trim to the predecessor */
521 ee = p->start+p->len;
522 if(offset < ee) {
523 o = ee - offset;
524 len -= o;
525 if(len <= 0) {
526 qunlock(m);
527 return;
528 }
529 buf += o;
530 offset += o;
531 }
532
533 /* try and pack data into the predecessor */
534 if(offset == ee && p->len < BY2PG) {
535 o = len;
536 if(o > BY2PG - p->len)
537 o = BY2PG - p->len;
538 if(cpgmove(p, buf, p->len, o)) {
539 p->len += o;
540 buf += o;
541 len -= o;
542 offset += o;
543 if(len <= 0) {
544 if(f && p->start + p->len > f->start) print("CACHE: p->start=%uld p->len=%d f->start=%uld\n", p->start, p->len, f->start);
545 qunlock(m);
546 return;
547 }
548 }
549 }
550
551 e = cchain(buf, offset, len, &tail);
552 if(e != 0) {
553 p->next = e;
554 tail->next = f;
555 }
556 qunlock(m);
557 }
558
559 void
560 cwrite(Chan* c, uchar *buf, int len, vlong off)
561 {
562 int o, eo;
563 Mntcache *m;
564 ulong eblock, ee;
565 Extent *p, *f, *e, *tail;
566 ulong offset;
567
568 if(off > maxcache || len == 0)
569 return;
570
571 m = c->mcp;
572 if(m == 0)
573 return;
574
575 qlock(m);
576 if(cdev(m, c) == 0) {
577 qunlock(m);
578 return;
579 }
580
581 offset = off;
582 m->qid.vers++;
583 c->qid.vers++;
584
585 p = 0;
586 for(f = m->list; f; f = f->next) {
587 if(f->start >= offset)
588 break;
589 p = f;
590 }
591
592 if(p != 0) {
593 ee = p->start+p->len;
594 eo = offset - p->start;
595 /* pack in predecessor if there is space */
596 if(offset <= ee && eo < BY2PG) {
597 o = len;
598 if(o > BY2PG - eo)
599 o = BY2PG - eo;
600 if(cpgmove(p, buf, eo, o)) {
601 if(eo+o > p->len)
602 p->len = eo+o;
603 buf += o;
604 len -= o;
605 offset += o;
606 }
607 }
608 }
609
610 /* free the overlap -- it's a rare case */
611 eblock = offset+len;
612 while(f && f->start < eblock) {
613 e = f->next;
614 extentfree(f);
615 f = e;
616 }
617
618 /* link the block (if any) into the middle */
619 e = cchain(buf, offset, len, &tail);
620 if(e != 0) {
621 tail->next = f;
622 f = e;
623 }
624
625 if(p == 0)
626 m->list = f;
627 else
628 p->next = f;
629 qunlock(m);
630 }
Cache object: da61b6bf6e1d23486e0eff2851e20651
|