1*479ab7f0SSascha Wildner /*-
2*479ab7f0SSascha Wildner * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3*479ab7f0SSascha Wildner * All rights reserved.
4*479ab7f0SSascha Wildner *
5*479ab7f0SSascha Wildner * Redistribution and use in source and binary forms, with or without
6*479ab7f0SSascha Wildner * modification, are permitted provided that the following conditions
7*479ab7f0SSascha Wildner * are met:
8*479ab7f0SSascha Wildner * 1. Redistributions of source code must retain the above copyright
9*479ab7f0SSascha Wildner * notice, this list of conditions and the following disclaimer.
10*479ab7f0SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
11*479ab7f0SSascha Wildner * notice, this list of conditions and the following disclaimer in the
12*479ab7f0SSascha Wildner * documentation and/or other materials provided with the distribution.
13*479ab7f0SSascha Wildner *
14*479ab7f0SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*479ab7f0SSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*479ab7f0SSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*479ab7f0SSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*479ab7f0SSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*479ab7f0SSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*479ab7f0SSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*479ab7f0SSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*479ab7f0SSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*479ab7f0SSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*479ab7f0SSascha Wildner * SUCH DAMAGE.
25*479ab7f0SSascha Wildner *
26*479ab7f0SSascha Wildner * $FreeBSD: src/sys/boot/common/bcache.c,v 1.12 2003/08/25 23:30:41 obrien Exp $
27*479ab7f0SSascha Wildner */
28*479ab7f0SSascha Wildner
29*479ab7f0SSascha Wildner /*
30*479ab7f0SSascha Wildner * Simple LRU block cache
31*479ab7f0SSascha Wildner */
32*479ab7f0SSascha Wildner
33*479ab7f0SSascha Wildner #include <stand.h>
34*479ab7f0SSascha Wildner #include <string.h>
35*479ab7f0SSascha Wildner #include <bitstring.h>
36*479ab7f0SSascha Wildner
37*479ab7f0SSascha Wildner #include "bootstrap.h"
38*479ab7f0SSascha Wildner
39*479ab7f0SSascha Wildner /* #define BCACHE_DEBUG */
40*479ab7f0SSascha Wildner
41*479ab7f0SSascha Wildner #ifdef BCACHE_DEBUG
42*479ab7f0SSascha Wildner #define BCACHE_TIMEOUT 10
43*479ab7f0SSascha Wildner # define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
44*479ab7f0SSascha Wildner #else
45*479ab7f0SSascha Wildner #define BCACHE_TIMEOUT 2
46*479ab7f0SSascha Wildner # define DEBUG(fmt, args...)
47*479ab7f0SSascha Wildner #endif
48*479ab7f0SSascha Wildner
49*479ab7f0SSascha Wildner
50*479ab7f0SSascha Wildner struct bcachectl
51*479ab7f0SSascha Wildner {
52*479ab7f0SSascha Wildner daddr_t bc_blkno;
53*479ab7f0SSascha Wildner time_t bc_stamp;
54*479ab7f0SSascha Wildner int bc_count;
55*479ab7f0SSascha Wildner };
56*479ab7f0SSascha Wildner
57*479ab7f0SSascha Wildner static struct bcachectl *bcache_ctl;
58*479ab7f0SSascha Wildner static caddr_t bcache_data;
59*479ab7f0SSascha Wildner static bitstr_t *bcache_miss;
60*479ab7f0SSascha Wildner static u_int bcache_nblks;
61*479ab7f0SSascha Wildner static u_int bcache_blksize;
62*479ab7f0SSascha Wildner static u_int bcache_hits, bcache_misses, bcache_ops, bcache_bypasses;
63*479ab7f0SSascha Wildner static u_int bcache_flushes;
64*479ab7f0SSascha Wildner static u_int bcache_bcount;
65*479ab7f0SSascha Wildner
66*479ab7f0SSascha Wildner static void bcache_invalidate(daddr_t blkno);
67*479ab7f0SSascha Wildner static void bcache_insert(caddr_t buf, daddr_t blkno);
68*479ab7f0SSascha Wildner static int bcache_lookup(caddr_t buf, daddr_t blkno);
69*479ab7f0SSascha Wildner
70*479ab7f0SSascha Wildner /*
71*479ab7f0SSascha Wildner * Initialise the cache for (nblks) of (bsize).
72*479ab7f0SSascha Wildner */
73*479ab7f0SSascha Wildner int
bcache_init(u_int nblks,size_t bsize)74*479ab7f0SSascha Wildner bcache_init(u_int nblks, size_t bsize)
75*479ab7f0SSascha Wildner {
76*479ab7f0SSascha Wildner /* discard any old contents */
77*479ab7f0SSascha Wildner if (bcache_data != NULL) {
78*479ab7f0SSascha Wildner free(bcache_data);
79*479ab7f0SSascha Wildner bcache_data = NULL;
80*479ab7f0SSascha Wildner free(bcache_ctl);
81*479ab7f0SSascha Wildner }
82*479ab7f0SSascha Wildner
83*479ab7f0SSascha Wildner /* Allocate control structures */
84*479ab7f0SSascha Wildner bcache_nblks = nblks;
85*479ab7f0SSascha Wildner bcache_blksize = bsize;
86*479ab7f0SSascha Wildner bcache_data = malloc(bcache_nblks * bcache_blksize);
87*479ab7f0SSascha Wildner bcache_ctl = (struct bcachectl *)malloc(bcache_nblks * sizeof(struct bcachectl));
88*479ab7f0SSascha Wildner bcache_miss = bit_alloc((bcache_nblks + 1) / 2);
89*479ab7f0SSascha Wildner if ((bcache_data == NULL) || (bcache_ctl == NULL) || (bcache_miss == NULL)) {
90*479ab7f0SSascha Wildner if (bcache_miss)
91*479ab7f0SSascha Wildner free(bcache_miss);
92*479ab7f0SSascha Wildner if (bcache_ctl)
93*479ab7f0SSascha Wildner free(bcache_ctl);
94*479ab7f0SSascha Wildner if (bcache_data)
95*479ab7f0SSascha Wildner free(bcache_data);
96*479ab7f0SSascha Wildner bcache_data = NULL;
97*479ab7f0SSascha Wildner return(ENOMEM);
98*479ab7f0SSascha Wildner }
99*479ab7f0SSascha Wildner
100*479ab7f0SSascha Wildner return(0);
101*479ab7f0SSascha Wildner }
102*479ab7f0SSascha Wildner
103*479ab7f0SSascha Wildner /*
104*479ab7f0SSascha Wildner * Flush the cache
105*479ab7f0SSascha Wildner */
106*479ab7f0SSascha Wildner void
bcache_flush(void)107*479ab7f0SSascha Wildner bcache_flush(void)
108*479ab7f0SSascha Wildner {
109*479ab7f0SSascha Wildner u_int i;
110*479ab7f0SSascha Wildner
111*479ab7f0SSascha Wildner bcache_flushes++;
112*479ab7f0SSascha Wildner
113*479ab7f0SSascha Wildner /* Flush the cache */
114*479ab7f0SSascha Wildner for (i = 0; i < bcache_nblks; i++) {
115*479ab7f0SSascha Wildner bcache_ctl[i].bc_count = -1;
116*479ab7f0SSascha Wildner bcache_ctl[i].bc_blkno = -1;
117*479ab7f0SSascha Wildner }
118*479ab7f0SSascha Wildner }
119*479ab7f0SSascha Wildner
120*479ab7f0SSascha Wildner /*
121*479ab7f0SSascha Wildner * Handle a write request; write directly to the disk, and populate the
122*479ab7f0SSascha Wildner * cache with the new values.
123*479ab7f0SSascha Wildner */
124*479ab7f0SSascha Wildner static int
write_strategy(void * devdata,int unit,int rw,daddr_t blk,size_t size,char * buf,size_t * rsize)125*479ab7f0SSascha Wildner write_strategy(void *devdata, int unit, int rw, daddr_t blk, size_t size,
126*479ab7f0SSascha Wildner char *buf, size_t *rsize)
127*479ab7f0SSascha Wildner {
128*479ab7f0SSascha Wildner struct bcache_devdata *dd = (struct bcache_devdata *)devdata;
129*479ab7f0SSascha Wildner daddr_t i, nblk;
130*479ab7f0SSascha Wildner int err;
131*479ab7f0SSascha Wildner
132*479ab7f0SSascha Wildner nblk = size / bcache_blksize;
133*479ab7f0SSascha Wildner
134*479ab7f0SSascha Wildner /* Invalidate the blocks being written */
135*479ab7f0SSascha Wildner for (i = 0; i < nblk; i++) {
136*479ab7f0SSascha Wildner bcache_invalidate(blk + i);
137*479ab7f0SSascha Wildner }
138*479ab7f0SSascha Wildner
139*479ab7f0SSascha Wildner /* Write the blocks */
140*479ab7f0SSascha Wildner err = dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize);
141*479ab7f0SSascha Wildner
142*479ab7f0SSascha Wildner /* Populate the block cache with the new data */
143*479ab7f0SSascha Wildner if (err == 0) {
144*479ab7f0SSascha Wildner for (i = 0; i < nblk; i++) {
145*479ab7f0SSascha Wildner bcache_insert(buf + (i * bcache_blksize),blk + i);
146*479ab7f0SSascha Wildner }
147*479ab7f0SSascha Wildner }
148*479ab7f0SSascha Wildner
149*479ab7f0SSascha Wildner return err;
150*479ab7f0SSascha Wildner }
151*479ab7f0SSascha Wildner
152*479ab7f0SSascha Wildner /*
153*479ab7f0SSascha Wildner * Handle a read request; fill in parts of the request that can
154*479ab7f0SSascha Wildner * be satisfied by the cache, use the supplied strategy routine to do
155*479ab7f0SSascha Wildner * device I/O and then use the I/O results to populate the cache.
156*479ab7f0SSascha Wildner */
157*479ab7f0SSascha Wildner static int
read_strategy(void * devdata,int unit,int rw,daddr_t blk,size_t size,char * buf,size_t * rsize)158*479ab7f0SSascha Wildner read_strategy(void *devdata, int unit, int rw, daddr_t blk, size_t size,
159*479ab7f0SSascha Wildner char *buf, size_t *rsize)
160*479ab7f0SSascha Wildner {
161*479ab7f0SSascha Wildner struct bcache_devdata *dd = (struct bcache_devdata *)devdata;
162*479ab7f0SSascha Wildner int p_size, result;
163*479ab7f0SSascha Wildner daddr_t p_blk, i, j, nblk;
164*479ab7f0SSascha Wildner caddr_t p_buf;
165*479ab7f0SSascha Wildner
166*479ab7f0SSascha Wildner nblk = size / bcache_blksize;
167*479ab7f0SSascha Wildner result = 0;
168*479ab7f0SSascha Wildner
169*479ab7f0SSascha Wildner /* Satisfy any cache hits up front */
170*479ab7f0SSascha Wildner for (i = 0; i < nblk; i++) {
171*479ab7f0SSascha Wildner if (bcache_lookup(buf + (bcache_blksize * i), blk + i)) {
172*479ab7f0SSascha Wildner bit_set(bcache_miss, i); /* cache miss */
173*479ab7f0SSascha Wildner bcache_misses++;
174*479ab7f0SSascha Wildner } else {
175*479ab7f0SSascha Wildner bit_clear(bcache_miss, i); /* cache hit */
176*479ab7f0SSascha Wildner bcache_hits++;
177*479ab7f0SSascha Wildner }
178*479ab7f0SSascha Wildner }
179*479ab7f0SSascha Wildner
180*479ab7f0SSascha Wildner /* Go back and fill in any misses XXX optimise */
181*479ab7f0SSascha Wildner p_blk = -1;
182*479ab7f0SSascha Wildner p_buf = NULL;
183*479ab7f0SSascha Wildner p_size = 0;
184*479ab7f0SSascha Wildner for (i = 0; i < nblk; i++) {
185*479ab7f0SSascha Wildner if (bit_test(bcache_miss, i)) {
186*479ab7f0SSascha Wildner /* miss, add to pending transfer */
187*479ab7f0SSascha Wildner if (p_blk == -1) {
188*479ab7f0SSascha Wildner p_blk = blk + i;
189*479ab7f0SSascha Wildner p_buf = buf + (bcache_blksize * i);
190*479ab7f0SSascha Wildner p_size = 1;
191*479ab7f0SSascha Wildner } else {
192*479ab7f0SSascha Wildner p_size++;
193*479ab7f0SSascha Wildner }
194*479ab7f0SSascha Wildner } else if (p_blk != -1) {
195*479ab7f0SSascha Wildner /* hit, complete pending transfer */
196*479ab7f0SSascha Wildner result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, p_size * bcache_blksize, p_buf, NULL);
197*479ab7f0SSascha Wildner if (result != 0)
198*479ab7f0SSascha Wildner goto done;
199*479ab7f0SSascha Wildner for (j = 0; j < p_size; j++)
200*479ab7f0SSascha Wildner bcache_insert(p_buf + (j * bcache_blksize), p_blk + j);
201*479ab7f0SSascha Wildner p_blk = -1;
202*479ab7f0SSascha Wildner }
203*479ab7f0SSascha Wildner }
204*479ab7f0SSascha Wildner if (p_blk != -1) {
205*479ab7f0SSascha Wildner /* pending transfer left */
206*479ab7f0SSascha Wildner result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, p_size * bcache_blksize, p_buf, NULL);
207*479ab7f0SSascha Wildner if (result != 0)
208*479ab7f0SSascha Wildner goto done;
209*479ab7f0SSascha Wildner for (j = 0; j < p_size; j++)
210*479ab7f0SSascha Wildner bcache_insert(p_buf + (j * bcache_blksize), p_blk + j);
211*479ab7f0SSascha Wildner }
212*479ab7f0SSascha Wildner
213*479ab7f0SSascha Wildner done:
214*479ab7f0SSascha Wildner if ((result == 0) && (rsize != NULL))
215*479ab7f0SSascha Wildner *rsize = size;
216*479ab7f0SSascha Wildner return(result);
217*479ab7f0SSascha Wildner }
218*479ab7f0SSascha Wildner
219*479ab7f0SSascha Wildner /*
220*479ab7f0SSascha Wildner * Requests larger than 1/2 the cache size will be bypassed and go
221*479ab7f0SSascha Wildner * directly to the disk. XXX tune this.
222*479ab7f0SSascha Wildner */
223*479ab7f0SSascha Wildner int
bcache_strategy(void * devdata,int unit,int rw,daddr_t blk,size_t size,char * buf,size_t * rsize)224*479ab7f0SSascha Wildner bcache_strategy(void *devdata, int unit, int rw, daddr_t blk, size_t size,
225*479ab7f0SSascha Wildner char *buf, size_t *rsize)
226*479ab7f0SSascha Wildner {
227*479ab7f0SSascha Wildner static int bcache_unit = -1;
228*479ab7f0SSascha Wildner struct bcache_devdata *dd = (struct bcache_devdata *)devdata;
229*479ab7f0SSascha Wildner
230*479ab7f0SSascha Wildner bcache_ops++;
231*479ab7f0SSascha Wildner
232*479ab7f0SSascha Wildner if(bcache_unit != unit) {
233*479ab7f0SSascha Wildner bcache_flush();
234*479ab7f0SSascha Wildner bcache_unit = unit;
235*479ab7f0SSascha Wildner }
236*479ab7f0SSascha Wildner
237*479ab7f0SSascha Wildner /* bypass large requests, or when the cache is inactive */
238*479ab7f0SSascha Wildner if ((bcache_data == NULL) || ((size * 2 / bcache_blksize) > bcache_nblks)) {
239*479ab7f0SSascha Wildner DEBUG("bypass %d from %d", size / bcache_blksize, blk);
240*479ab7f0SSascha Wildner bcache_bypasses++;
241*479ab7f0SSascha Wildner return(dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize));
242*479ab7f0SSascha Wildner }
243*479ab7f0SSascha Wildner
244*479ab7f0SSascha Wildner switch (rw) {
245*479ab7f0SSascha Wildner case F_READ:
246*479ab7f0SSascha Wildner return read_strategy(devdata, unit, rw, blk, size, buf, rsize);
247*479ab7f0SSascha Wildner case F_WRITE:
248*479ab7f0SSascha Wildner return write_strategy(devdata, unit, rw, blk, size, buf, rsize);
249*479ab7f0SSascha Wildner }
250*479ab7f0SSascha Wildner return -1;
251*479ab7f0SSascha Wildner }
252*479ab7f0SSascha Wildner
253*479ab7f0SSascha Wildner
254*479ab7f0SSascha Wildner /*
255*479ab7f0SSascha Wildner * Insert a block into the cache. Retire the oldest block to do so, if required.
256*479ab7f0SSascha Wildner *
257*479ab7f0SSascha Wildner * XXX the LRU algorithm will fail after 2^31 blocks have been transferred.
258*479ab7f0SSascha Wildner */
259*479ab7f0SSascha Wildner static void
bcache_insert(caddr_t buf,daddr_t blkno)260*479ab7f0SSascha Wildner bcache_insert(caddr_t buf, daddr_t blkno)
261*479ab7f0SSascha Wildner {
262*479ab7f0SSascha Wildner time_t now;
263*479ab7f0SSascha Wildner int cand, ocount;
264*479ab7f0SSascha Wildner u_int i;
265*479ab7f0SSascha Wildner
266*479ab7f0SSascha Wildner time(&now);
267*479ab7f0SSascha Wildner cand = 0; /* assume the first block */
268*479ab7f0SSascha Wildner ocount = bcache_ctl[0].bc_count;
269*479ab7f0SSascha Wildner
270*479ab7f0SSascha Wildner /* find the oldest block */
271*479ab7f0SSascha Wildner for (i = 1; i < bcache_nblks; i++) {
272*479ab7f0SSascha Wildner if (bcache_ctl[i].bc_blkno == blkno) {
273*479ab7f0SSascha Wildner /* reuse old entry */
274*479ab7f0SSascha Wildner cand = i;
275*479ab7f0SSascha Wildner break;
276*479ab7f0SSascha Wildner }
277*479ab7f0SSascha Wildner if (bcache_ctl[i].bc_count < ocount) {
278*479ab7f0SSascha Wildner ocount = bcache_ctl[i].bc_count;
279*479ab7f0SSascha Wildner cand = i;
280*479ab7f0SSascha Wildner }
281*479ab7f0SSascha Wildner }
282*479ab7f0SSascha Wildner
283*479ab7f0SSascha Wildner DEBUG("insert blk %d -> %d @ %d # %d", blkno, cand, now, bcache_bcount);
284*479ab7f0SSascha Wildner bcopy(buf, bcache_data + (bcache_blksize * cand), bcache_blksize);
285*479ab7f0SSascha Wildner bcache_ctl[cand].bc_blkno = blkno;
286*479ab7f0SSascha Wildner bcache_ctl[cand].bc_stamp = now;
287*479ab7f0SSascha Wildner bcache_ctl[cand].bc_count = bcache_bcount++;
288*479ab7f0SSascha Wildner }
289*479ab7f0SSascha Wildner
290*479ab7f0SSascha Wildner /*
291*479ab7f0SSascha Wildner * Look for a block in the cache. Blocks more than BCACHE_TIMEOUT seconds old
292*479ab7f0SSascha Wildner * may be stale (removable media) and thus are discarded. Copy the block out
293*479ab7f0SSascha Wildner * if successful and return zero, or return nonzero on failure.
294*479ab7f0SSascha Wildner */
295*479ab7f0SSascha Wildner static int
bcache_lookup(caddr_t buf,daddr_t blkno)296*479ab7f0SSascha Wildner bcache_lookup(caddr_t buf, daddr_t blkno)
297*479ab7f0SSascha Wildner {
298*479ab7f0SSascha Wildner time_t now;
299*479ab7f0SSascha Wildner u_int i;
300*479ab7f0SSascha Wildner
301*479ab7f0SSascha Wildner time(&now);
302*479ab7f0SSascha Wildner
303*479ab7f0SSascha Wildner for (i = 0; i < bcache_nblks; i++)
304*479ab7f0SSascha Wildner /* cache hit? */
305*479ab7f0SSascha Wildner if ((bcache_ctl[i].bc_blkno == blkno) && ((bcache_ctl[i].bc_stamp + BCACHE_TIMEOUT) >= now)) {
306*479ab7f0SSascha Wildner bcopy(bcache_data + (bcache_blksize * i), buf, bcache_blksize);
307*479ab7f0SSascha Wildner DEBUG("hit blk %d <- %d (now %d then %d)", blkno, i, now, bcache_ctl[i].bc_stamp);
308*479ab7f0SSascha Wildner return(0);
309*479ab7f0SSascha Wildner }
310*479ab7f0SSascha Wildner return(ENOENT);
311*479ab7f0SSascha Wildner }
312*479ab7f0SSascha Wildner
313*479ab7f0SSascha Wildner /*
314*479ab7f0SSascha Wildner * Invalidate a block from the cache.
315*479ab7f0SSascha Wildner */
316*479ab7f0SSascha Wildner static void
bcache_invalidate(daddr_t blkno)317*479ab7f0SSascha Wildner bcache_invalidate(daddr_t blkno)
318*479ab7f0SSascha Wildner {
319*479ab7f0SSascha Wildner u_int i;
320*479ab7f0SSascha Wildner
321*479ab7f0SSascha Wildner for (i = 0; i < bcache_nblks; i++) {
322*479ab7f0SSascha Wildner if (bcache_ctl[i].bc_blkno == blkno) {
323*479ab7f0SSascha Wildner bcache_ctl[i].bc_count = -1;
324*479ab7f0SSascha Wildner bcache_ctl[i].bc_blkno = -1;
325*479ab7f0SSascha Wildner DEBUG("invalidate blk %d", blkno);
326*479ab7f0SSascha Wildner break;
327*479ab7f0SSascha Wildner }
328*479ab7f0SSascha Wildner }
329*479ab7f0SSascha Wildner }
330*479ab7f0SSascha Wildner
331*479ab7f0SSascha Wildner COMMAND_SET(bcachestat, "bcachestat", "get disk block cache stats", command_bcache);
332*479ab7f0SSascha Wildner
333*479ab7f0SSascha Wildner static int
command_bcache(int argc,char * argv[])334*479ab7f0SSascha Wildner command_bcache(int argc, char *argv[])
335*479ab7f0SSascha Wildner {
336*479ab7f0SSascha Wildner u_int i;
337*479ab7f0SSascha Wildner
338*479ab7f0SSascha Wildner for (i = 0; i < bcache_nblks; i++) {
339*479ab7f0SSascha Wildner printf("%08x %04x %04x|", bcache_ctl[i].bc_blkno, (unsigned int)bcache_ctl[i].bc_stamp & 0xffff, bcache_ctl[i].bc_count & 0xffff);
340*479ab7f0SSascha Wildner if (((i + 1) % 4) == 0)
341*479ab7f0SSascha Wildner printf("\n");
342*479ab7f0SSascha Wildner }
343*479ab7f0SSascha Wildner printf("\n%u ops %u bypasses %u hits %u misses %u flushes\n", bcache_ops, bcache_bypasses, bcache_hits, bcache_misses, bcache_flushes);
344*479ab7f0SSascha Wildner return(CMD_OK);
345*479ab7f0SSascha Wildner }
346*479ab7f0SSascha Wildner
347