xref: /plan9/sys/src/cmd/cfs/bcache.c (revision 41fb754a868aff65c81b63b0b07ae1fab2010fd9)
1 #include <u.h>
2 #include <libc.h>
3 #include "cformat.h"
4 #include "lru.h"
5 #include "bcache.h"
6 
7 int
bcinit(Bcache * bc,int f,int bsize)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 *
bcfind(Bcache * bc,ulong bno)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 	 *  get least recently used block
55 	 */
56 	b = (Bbuf*)bc->lnext;
57 out:
58 	/*
59 	 *  if dirty, write it out
60 	 */
61 	if(b->dirty)
62 		if(bcwrite(bc, b) < 0)
63 			warning("writing dirty page");
64 	lruref(bc, b);
65 	return b;
66 }
67 
68 /*
69  *  allocate a buffer block for a block.  it's guaranteed to be there till
70  *  the next Nbcache bcread's.
71  */
72 Bbuf *
bcalloc(Bcache * bc,ulong bno)73 bcalloc(Bcache *bc, ulong bno)
74 {
75 	Bbuf *b;
76 
77 	b = bcfind(bc, bno);
78 	bno &= ~Indbno;
79 	b->bno = bno;
80 	b->inuse = 1;
81 	return b;
82 }
83 
84 /*
85  *  read a block into a buffer cache.  it's guaranteed to be there till
86  *  the next Nbcache bcread's.
87  */
88 Bbuf *
bcread(Bcache * bc,ulong bno)89 bcread(Bcache *bc, ulong bno)
90 {
91 	Bbuf *b;
92 
93 	b = bcfind(bc, bno);
94 	bno &= ~Indbno;
95 	if(b->bno!=bno || !b->inuse)
96 		/*
97 		 *  read in the one we really want
98 		 */
99 		if(bread(bc, bno, b->data) < 0){
100 			b->inuse = 0;
101 			return 0;
102 		}
103 	b->bno = bno;
104 	b->inuse = 1;
105 	return b;
106 }
107 
108 /*
109  *  mark a page dirty, if it's already dirty force a write
110  *
111  *	N.B: ordering is important.
112  */
113 void
bcmark(Bcache * bc,Bbuf * b)114 bcmark(Bcache *bc, Bbuf *b)
115 {
116 	lruref(bc, b);
117 
118 	if(b->dirty){
119 		bcwrite(bc, b);
120 		return;
121 	}
122 
123 	b->dirty = 1;
124 	if(bc->dfirst)
125 		bc->dlast->next = b;
126 	else
127 		bc->dfirst = b;
128 	bc->dlast = b;
129 }
130 
131 /*
132  *  write out a page (and all preceding dirty ones)
133  */
134 int
bcwrite(Bcache * bc,Bbuf * b)135 bcwrite(Bcache *bc, Bbuf *b)
136 {
137 	Bbuf *nb;
138 
139 	/*
140 	 *  write out all preceding pages
141 	 */
142 	while(nb = bc->dfirst){
143 		if(bwrite(bc, nb->bno, nb->data) < 0)
144 			return -1;
145 		nb->dirty = 0;
146 		bc->dfirst = nb->next;
147 		nb->next = 0;
148 		if(nb == b)
149 			return 0;
150 	}
151 
152 	/*
153 	 *  write out this page
154 	 */
155 	if(bwrite(bc, b->bno, b->data) < 0)
156 		return -1;
157 	b->dirty = 0;
158 	b->next = 0;
159 	return 0;
160 }
161 
162 /*
163  *  write out all dirty pages (in order)
164  */
165 int
bcsync(Bcache * bc)166 bcsync(Bcache *bc)
167 {
168 	if(bc->dfirst)
169 		return bcwrite(bc, bc->dlast);
170 	return 0;
171 }
172 
173 /*
174  *  read a block from disk
175  */
176 int
bread(Bcache * bc,ulong bno,void * buf)177 bread(Bcache *bc, ulong bno, void *buf)
178 {
179 	uvlong x = (uvlong)bno * bc->bsize;
180 
181 	if(pread(bc->f, buf, bc->bsize, x) != bc->bsize)
182 		return -1;
183 	return 0;
184 }
185 
186 /*
187  *  write a block to disk
188  */
189 int
bwrite(Bcache * bc,ulong bno,void * buf)190 bwrite(Bcache *bc, ulong bno, void *buf)
191 {
192 	uvlong x = (uvlong)bno * bc->bsize;
193 
194 	if(pwrite(bc->f, buf, bc->bsize, x) != bc->bsize)
195 		return -1;
196 	return 0;
197 }
198