xref: /plan9-contrib/sys/src/cmd/cfs/bcache.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include <u.h>
2 #include <libc.h>
3 #include "cformat.h"
4 #include "lru.h"
5 #include "bcache.h"
6 
7 int
8 bcinit(Bcache *bc, int f, int bsize)
9 {
10 	Bbuf *b;
11 
12 	/*
13 	 *  allocate space for all buffers
14 	 *  point all buffers into outer space
15 	 */
16 	bc->dfirst = 0;
17 	bc->bsize = bsize;
18 	bc->f = f;
19 	lruinit(bc);
20 	for(b = bc->bb; b < &bc->bb[Nbcache]; b++){
21 		b->inuse = 0;
22 		b->next = 0;
23 		b->dirty = 0;
24 		if(b->data == 0)
25 			b->data = (char *)malloc(bc->bsize);
26 		if(b->data == 0)
27 			return -1;
28 		lruadd(bc, b);
29 	}
30 
31 	return 0;
32 }
33 
34 /*
35  *  Find a buffer for block b.  If it's dirty, write it out.
36  */
37 Bbuf *
38 bcfind(Bcache *bc, ulong bno)
39 {
40 	Bbuf *b;
41 
42 	if(bno == Notabno)
43 		error("bcfind: Notabno");
44 	bno &= ~Indbno;
45 
46 	/*
47 	 *  if we already have a buffer for this bno, use it
48 	 */
49 	for(b = bc->bb; b < &bc->bb[Nbcache]; b++){
50 		if(b->inuse && b->bno==bno)
51 			goto out;
52 	}
53 
54 	/*
55 	 *  get least recently used block
56 	 */
57 	b = (Bbuf*)bc->lnext;
58 out:
59 	/*
60 	 *  if dirty, write it out
61 	 */
62 	if(b->dirty){
63 		if(bcwrite(bc, b) < 0)
64 			warning("writing dirty page");
65 	}
66 	lruref(bc, b);
67 	return b;
68 }
69 
70 /*
71  *  allocate a buffer block for a block.  it's guaranteed to be there till
72  *  the next Nbcache bcread's.
73  */
74 Bbuf *
75 bcalloc(Bcache *bc, ulong bno)
76 {
77 	Bbuf *b;
78 
79 	b = bcfind(bc, bno);
80 	bno &= ~Indbno;
81 	b->bno = bno;
82 	b->inuse = 1;
83 	return b;
84 }
85 
86 /*
87  *  read a block into a buffer cache.  it's guaranteed to be there till
88  *  the next Nbcache bcread's.
89  */
90 Bbuf *
91 bcread(Bcache *bc, ulong bno)
92 {
93 	Bbuf *b;
94 
95 	b = bcfind(bc, bno);
96 	bno &= ~Indbno;
97 	if(b->bno!=bno || !b->inuse){
98 		/*
99 		 *  read in the one we really want
100 		 */
101 		if(bread(bc, bno, b->data) < 0){
102 			b->inuse = 0;
103 			return 0;
104 		}
105 	}
106 	b->bno = bno;
107 	b->inuse = 1;
108 	return b;
109 }
110 
111 /*
112  *  mark a page dirty, if it's already dirty force a write
113  *
114  *	N.B: ordering is important.
115  */
116 void
117 bcmark(Bcache *bc, Bbuf *b)
118 {
119 	lruref(bc, b);
120 
121 	if(b->dirty){
122 		bcwrite(bc, b);
123 		return;
124 	}
125 
126 	b->dirty = 1;
127 	if(bc->dfirst)
128 		bc->dlast->next = b;
129 	else
130 		bc->dfirst = b;
131 	bc->dlast = b;
132 }
133 
134 /*
135  *  write out a page (and all preceeding dirty ones)
136  */
137 int
138 bcwrite(Bcache *bc, Bbuf *b)
139 {
140 	Bbuf *nb;
141 
142 	/*
143 	 *  write out all preceeding pages
144 	 */
145 	while(nb = bc->dfirst){
146 		if(bwrite(bc, nb->bno, nb->data) < 0)
147 			return -1;
148 		nb->dirty = 0;
149 		bc->dfirst = nb->next;
150 		nb->next = 0;
151 		if(nb == b)
152 			return 0;
153 	}
154 
155 	/*
156 	 *  write out this page
157 	 */
158 	if(bwrite(bc, b->bno, b->data) < 0)
159 		return -1;
160 	b->dirty = 0;
161 	b->next = 0;
162 	return 0;
163 }
164 
165 /*
166  *  write out all dirty pages (in order)
167  */
168 int
169 bcsync(Bcache *bc)
170 {
171 	if(bc->dfirst)
172 		return bcwrite(bc, bc->dlast);
173 	return 0;
174 }
175 
176 /*
177  *  read a block from disk
178  */
179 int
180 bread(Bcache *bc, ulong bno, void *buf)
181 {
182 	ulong x = bno*bc->bsize;
183 
184 	if(seek(bc->f, x, 0) != x)
185 		return -1;
186 	if(read(bc->f, buf, bc->bsize) != bc->bsize)
187 		return -1;
188 	return 0;
189 }
190 
191 /*
192  *  write a block to disk
193  */
194 int
195 bwrite(Bcache *bc, ulong bno, void *buf)
196 {
197 	ulong x = bno*bc->bsize;
198 
199 	if(seek(bc->f, x, 0) != x)
200 		return -1;
201 	if(write(bc->f, buf, bc->bsize) != bc->bsize)
202 		return -1;
203 	return 0;
204 }
205