Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
	QLock;
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 > 400*MB)
117
		maxcache = 20*MAXCACHE;
118
	else if(conf.npage*BY2PG > 200*MB)
119
		maxcache = 10*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: %#llux.%#lux %d %d %s (%d %c)\n",
153
	s, m->qid.path, m->qid.vers, m->type, m->dev, c->path->s, nb, ct ? 'C' : 'N');
154
 
155
	for(e = m->list; e; e = e->next) {
156
		pprint("\t%4d %5lud %4d %#p\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
	qlock(&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
			qunlock(&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
	qunlock(&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
		qlock(&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
		qunlock(&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
}