Changeset View
Changeset View
Standalone View
Standalone View
sys/boot/common/bcache.c
/*- | /*- | ||||
* Copyright (c) 1998 Michael Smith <msmith@freebsd.org> | * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> | ||||
* Copyright 2015 Toomas Soome <tsoome@me.com> | |||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
* documentation and/or other materials provided with the distribution. | * documentation and/or other materials provided with the distribution. | ||||
* | * | ||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | ||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
emaste: This should go with the existing copyright statement | |||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
#include <sys/param.h> | |||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
/* | /* | ||||
* Simple LRU block cache | * Simple hashed block cache | ||||
*/ | */ | ||||
#include <sys/stdint.h> | #include <sys/stdint.h> | ||||
#include <stand.h> | #include <stand.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <bitstring.h> | #include <strings.h> | ||||
#include "bootstrap.h" | #include "bootstrap.h" | ||||
/* #define BCACHE_DEBUG */ | /* #define BCACHE_DEBUG */ | ||||
#ifdef BCACHE_DEBUG | #ifdef BCACHE_DEBUG | ||||
#define BCACHE_TIMEOUT 10 | |||||
# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) | # define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) | ||||
#else | #else | ||||
#define BCACHE_TIMEOUT 2 | |||||
# define DEBUG(fmt, args...) | # define DEBUG(fmt, args...) | ||||
#endif | #endif | ||||
struct bcachectl | struct bcachectl | ||||
{ | { | ||||
daddr_t bc_blkno; | daddr_t bc_blkno; | ||||
time_t bc_stamp; | |||||
int bc_count; | int bc_count; | ||||
}; | }; | ||||
static struct bcachectl *bcache_ctl; | /* | ||||
static caddr_t bcache_data; | * bcache per device node. cache is allocated on device first open and freed | ||||
static bitstr_t *bcache_miss; | * on last close, to save memory. The issue there is the size; biosdisk | ||||
static u_int bcache_nblks; | * supports up to 31 (0x1f) devices. Classic setup would use single disk | ||||
static u_int bcache_blksize; | * to boot from, but this has changed with zfs. | ||||
static u_int bcache_hits, bcache_misses, bcache_ops, bcache_bypasses; | */ | ||||
static u_int bcache_flushes; | struct bcache { | ||||
struct bcachectl *bcache_ctl; | |||||
caddr_t bcache_data; | |||||
u_int bcache_nblks; | |||||
size_t ra; | |||||
}; | |||||
static u_int bcache_total_nblks; /* set by bcache_init */ | |||||
static u_int bcache_blksize; /* set by bcache_init */ | |||||
static u_int bcache_numdev; /* set by bcache_add_dev */ | |||||
/* statistics */ | |||||
static u_int bcache_units; /* number of devices with cache */ | |||||
static u_int bcache_unit_nblks; /* nblocks per unit */ | |||||
static u_int bcache_hits; | |||||
static u_int bcache_misses; | |||||
static u_int bcache_ops; | |||||
static u_int bcache_bypasses; | |||||
static u_int bcache_bcount; | static u_int bcache_bcount; | ||||
static u_int bcache_rablks; | |||||
static void bcache_invalidate(daddr_t blkno); | #define BHASH(bc, blkno) ((blkno) & ((bc)->bcache_nblks - 1)) | ||||
static void bcache_insert(caddr_t buf, daddr_t blkno); | #define BCACHE_LOOKUP(bc, blkno) \ | ||||
static int bcache_lookup(caddr_t buf, daddr_t blkno); | ((bc)->bcache_ctl[BHASH((bc), (blkno))].bc_blkno != (blkno)) | ||||
#define BCACHE_READAHEAD 256 | |||||
#define BCACHE_MINREADAHEAD 32 | |||||
static void bcache_invalidate(struct bcache *bc, daddr_t blkno); | |||||
static void bcache_insert(struct bcache *bc, daddr_t blkno); | |||||
static void bcache_free_instance(struct bcache *bc); | |||||
/* | /* | ||||
* Initialise the cache for (nblks) of (bsize). | * Initialise the cache for (nblks) of (bsize). | ||||
*/ | */ | ||||
int | void | ||||
bcache_init(u_int nblks, size_t bsize) | bcache_init(u_int nblks, size_t bsize) | ||||
{ | { | ||||
/* discard any old contents */ | /* set up control data */ | ||||
if (bcache_data != NULL) { | bcache_total_nblks = nblks; | ||||
free(bcache_data); | |||||
bcache_data = NULL; | |||||
free(bcache_ctl); | |||||
} | |||||
/* Allocate control structures */ | |||||
bcache_nblks = nblks; | |||||
bcache_blksize = bsize; | bcache_blksize = bsize; | ||||
bcache_data = malloc(bcache_nblks * bcache_blksize); | |||||
bcache_ctl = (struct bcachectl *)malloc(bcache_nblks * sizeof(struct bcachectl)); | |||||
bcache_miss = bit_alloc((bcache_nblks + 1) / 2); | |||||
if ((bcache_data == NULL) || (bcache_ctl == NULL) || (bcache_miss == NULL)) { | |||||
if (bcache_miss) | |||||
free(bcache_miss); | |||||
if (bcache_ctl) | |||||
free(bcache_ctl); | |||||
if (bcache_data) | |||||
free(bcache_data); | |||||
bcache_data = NULL; | |||||
return(ENOMEM); | |||||
} | } | ||||
return(0); | |||||
} | |||||
/* | /* | ||||
* Flush the cache | * add number of devices to bcache. we have to divide cache space | ||||
* between the devices, so bcache_add_dev() can be used to set up the | |||||
* number. The issue is, we need to get the number before actual allocations. | |||||
* bcache_add_dev() is supposed to be called from device init() call, so the | |||||
* assumption is, devsw dv_init is called for plain devices first, and | |||||
* for zfs, last. | |||||
*/ | */ | ||||
void | void | ||||
bcache_flush(void) | bcache_add_dev(int devices) | ||||
{ | { | ||||
bcache_numdev += devices; | |||||
} | |||||
void * | |||||
bcache_allocate(void) | |||||
{ | |||||
u_int i; | u_int i; | ||||
struct bcache *bc = malloc(sizeof (struct bcache)); | |||||
int disks = bcache_numdev; | |||||
bcache_flushes++; | if (disks == 0) | ||||
disks = 1; /* safe guard */ | |||||
if (bc == NULL) { | |||||
Done Inline Actions129-136: basically we are rounding up the number to the next power of two. Perhaps we can just use fls (and import it if it's not already in libstand)? There is also an algorithm here: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 if there is no built-in function for this one. delphij: 129-136: basically we are rounding up the number to the next power of two. Perhaps we can just… | |||||
errno = ENOMEM; | |||||
return (bc); | |||||
} | |||||
/* | |||||
* the bcache block count must be power of 2 for hash function | |||||
*/ | |||||
i = fls(disks) - 1; /* highbit - 1 */ | |||||
if (disks > (1 << i)) /* next power of 2 */ | |||||
i++; | |||||
bc->bcache_nblks = bcache_total_nblks >> i; | |||||
bcache_unit_nblks = bc->bcache_nblks; | |||||
bc->bcache_data = malloc(bc->bcache_nblks * bcache_blksize); | |||||
if (bc->bcache_data == NULL) { | |||||
/* dont error out yet. fall back to 32 blocks and try again */ | |||||
bc->bcache_nblks = 32; | |||||
bc->bcache_data = malloc(bc->bcache_nblks * bcache_blksize); | |||||
} | |||||
Done Inline ActionsThis is the same code of bcache_free except bcache_units decrement. Can the code be refactored a little bit so we can reuse the same code for the two different codepaths? delphij: This is the same code of bcache_free except bcache_units decrement. Can the code be refactored… | |||||
bc->bcache_ctl = malloc(bc->bcache_nblks * sizeof(struct bcachectl)); | |||||
if ((bc->bcache_data == NULL) || (bc->bcache_ctl == NULL)) { | |||||
bcache_free_instance(bc); | |||||
errno = ENOMEM; | |||||
return(NULL); | |||||
} | |||||
/* Flush the cache */ | /* Flush the cache */ | ||||
for (i = 0; i < bcache_nblks; i++) { | for (i = 0; i < bc->bcache_nblks; i++) { | ||||
Not Done Inline ActionsWith warnings enabled from D4839.diff: /usr/home/emaste/src/freebsd/sys/boot/efi/loader/../../common/bcache.c:158:19: error: comparison of integers of different signs: 'int' and 'u_int' (aka 'unsigned int') [-Werror,-Wsign-compare] for (i = 0; i < bc->bcache_nblks; i++) { ~ ^ ~~~~~~~~~~~~~~~~ emaste: With warnings enabled from D4839.diff:
```
/usr/home/emaste/src/freebsd/sys/boot/efi/loader/../. | |||||
bcache_ctl[i].bc_count = -1; | bc->bcache_ctl[i].bc_count = -1; | ||||
bcache_ctl[i].bc_blkno = -1; | bc->bcache_ctl[i].bc_blkno = -1; | ||||
} | } | ||||
bcache_units++; | |||||
bc->ra = BCACHE_READAHEAD; /* optimistic read ahead */ | |||||
return (bc); | |||||
} | } | ||||
void | |||||
bcache_free(void *cache) | |||||
{ | |||||
struct bcache *bc = cache; | |||||
if (bc == NULL) | |||||
return; | |||||
bcache_free_instance(bc); | |||||
bcache_units--; | |||||
} | |||||
/* | /* | ||||
* Handle a write request; write directly to the disk, and populate the | * Handle a write request; write directly to the disk, and populate the | ||||
* cache with the new values. | * cache with the new values. | ||||
*/ | */ | ||||
static int | static int | ||||
write_strategy(void *devdata, int unit, int rw, daddr_t blk, size_t size, | write_strategy(void *devdata, int rw, daddr_t blk, size_t offset, | ||||
char *buf, size_t *rsize) | size_t size, char *buf, size_t *rsize) | ||||
{ | { | ||||
struct bcache_devdata *dd = (struct bcache_devdata *)devdata; | struct bcache_devdata *dd = (struct bcache_devdata *)devdata; | ||||
struct bcache *bc = dd->dv_cache; | |||||
daddr_t i, nblk; | daddr_t i, nblk; | ||||
int err; | |||||
nblk = size / bcache_blksize; | nblk = size / bcache_blksize; | ||||
/* Invalidate the blocks being written */ | /* Invalidate the blocks being written */ | ||||
for (i = 0; i < nblk; i++) { | for (i = 0; i < nblk; i++) { | ||||
bcache_invalidate(blk + i); | bcache_invalidate(bc, blk + i); | ||||
} | } | ||||
/* Write the blocks */ | /* Write the blocks */ | ||||
err = dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize); | return (dd->dv_strategy(dd->dv_devdata, rw, blk, offset, size, buf, rsize)); | ||||
/* Populate the block cache with the new data */ | |||||
if (err == 0) { | |||||
for (i = 0; i < nblk; i++) { | |||||
bcache_insert(buf + (i * bcache_blksize),blk + i); | |||||
} | } | ||||
} | |||||
return err; | |||||
} | |||||
/* | /* | ||||
* Handle a read request; fill in parts of the request that can | * Handle a read request; fill in parts of the request that can | ||||
* be satisfied by the cache, use the supplied strategy routine to do | * be satisfied by the cache, use the supplied strategy routine to do | ||||
* device I/O and then use the I/O results to populate the cache. | * device I/O and then use the I/O results to populate the cache. | ||||
Done Inline ActionsI'd just delete this code block. delphij: I'd just delete this code block. | |||||
*/ | */ | ||||
static int | static int | ||||
read_strategy(void *devdata, int unit, int rw, daddr_t blk, size_t size, | read_strategy(void *devdata, int rw, daddr_t blk, size_t offset, | ||||
char *buf, size_t *rsize) | size_t size, char *buf, size_t *rsize) | ||||
{ | { | ||||
struct bcache_devdata *dd = (struct bcache_devdata *)devdata; | struct bcache_devdata *dd = (struct bcache_devdata *)devdata; | ||||
int p_size, result; | struct bcache *bc = dd->dv_cache; | ||||
daddr_t p_blk, i, j, nblk; | size_t i, nblk, p_size, r_size, complete, ra; | ||||
int result; | |||||
daddr_t p_blk; | |||||
caddr_t p_buf; | caddr_t p_buf; | ||||
if (bc == NULL) { | |||||
errno = ENODEV; | |||||
return (-1); | |||||
} | |||||
if (rsize != NULL) | |||||
*rsize = 0; | |||||
nblk = size / bcache_blksize; | nblk = size / bcache_blksize; | ||||
if ((nblk == 0 && size != 0) || offset != 0) | |||||
nblk++; | |||||
result = 0; | result = 0; | ||||
complete = 1; | |||||
/* Satisfy any cache hits up front */ | /* Satisfy any cache hits up front, break on first miss */ | ||||
for (i = 0; i < nblk; i++) { | for (i = 0; i < nblk; i++) { | ||||
if (bcache_lookup(buf + (bcache_blksize * i), blk + i)) { | if (BCACHE_LOOKUP(bc, (daddr_t)(blk + i))) { | ||||
bit_set(bcache_miss, i); /* cache miss */ | bcache_misses += (nblk - i); | ||||
bcache_misses++; | complete = 0; | ||||
if (nblk - i > BCACHE_MINREADAHEAD && bc->ra > BCACHE_MINREADAHEAD) | |||||
bc->ra >>= 1; /* reduce read ahead */ | |||||
break; | |||||
} else { | } else { | ||||
bit_clear(bcache_miss, i); /* cache hit */ | |||||
bcache_hits++; | bcache_hits++; | ||||
} | } | ||||
} | } | ||||
/* Go back and fill in any misses XXX optimise */ | if (complete) { /* whole set was in cache, return it */ | ||||
p_blk = -1; | if (bc->ra < BCACHE_READAHEAD) | ||||
p_buf = NULL; | bc->ra <<= 1; /* increase read ahead */ | ||||
p_size = 0; | bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)) + offset, | ||||
for (i = 0; i < nblk; i++) { | buf, size); | ||||
if (bit_test(bcache_miss, i)) { | |||||
/* miss, add to pending transfer */ | |||||
if (p_blk == -1) { | |||||
p_blk = blk + i; | |||||
p_buf = buf + (bcache_blksize * i); | |||||
p_size = 1; | |||||
} else { | |||||
p_size++; | |||||
} | |||||
} else if (p_blk != -1) { | |||||
/* hit, complete pending transfer */ | |||||
result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, p_size * bcache_blksize, p_buf, NULL); | |||||
if (result != 0) | |||||
goto done; | goto done; | ||||
for (j = 0; j < p_size; j++) | |||||
bcache_insert(p_buf + (j * bcache_blksize), p_blk + j); | |||||
p_blk = -1; | |||||
} | } | ||||
/* | |||||
* Fill in any misses. From check we have i pointing to first missing | |||||
* block, read in all remaining blocks + readahead. | |||||
* We have space at least for nblk - i before bcache wraps. | |||||
*/ | |||||
p_blk = blk + i; | |||||
p_buf = bc->bcache_data + (bcache_blksize * BHASH(bc, p_blk)); | |||||
r_size = bc->bcache_nblks - BHASH(bc, p_blk); /* remaining blocks */ | |||||
p_size = MIN(r_size, nblk - i); /* read at least those blocks */ | |||||
ra = bc->bcache_nblks - BHASH(bc, p_blk + p_size); | |||||
Not Done Inline Actions/usr/home/emaste/src/freebsd/sys/boot/efi/loader/../../common/bcache.c:257:12: error: comparison of integers of different signs: 'int' and 'u_int' (aka 'unsigned int') [-Werror,-Wsign-compare] if (ra != bc->bcache_nblks) { /* do we have RA space? */ ~~ ^ ~~~~~~~~~~~~~~~~ emaste: ```
/usr/home/emaste/src/freebsd/sys/boot/efi/loader/../../common/bcache.c:257:12: error… | |||||
if (ra != bc->bcache_nblks) { /* do we have RA space? */ | |||||
ra = MIN(bc->ra, ra); | |||||
p_size += ra; | |||||
} | } | ||||
if (p_blk != -1) { | |||||
/* pending transfer left */ | /* invalidate bcache */ | ||||
result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, p_size * bcache_blksize, p_buf, NULL); | for (i = 0; i < p_size; i++) { | ||||
if (result != 0) | bcache_invalidate(bc, p_blk + i); | ||||
goto done; | |||||
for (j = 0; j < p_size; j++) | |||||
bcache_insert(p_buf + (j * bcache_blksize), p_blk + j); | |||||
} | } | ||||
r_size = 0; | |||||
result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, 0, | |||||
p_size * bcache_blksize, p_buf, &r_size); | |||||
if (result) | |||||
goto done; | |||||
r_size /= bcache_blksize; | |||||
for (i = 0; i < r_size; i++) | |||||
bcache_insert(bc, p_blk + i); | |||||
bcache_rablks += ra; | |||||
bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)) + offset, buf, | |||||
size); | |||||
done: | done: | ||||
if ((result == 0) && (rsize != NULL)) | if ((result == 0) && (rsize != NULL)) | ||||
*rsize = size; | *rsize = size; | ||||
return(result); | return(result); | ||||
} | } | ||||
/* | /* | ||||
* Requests larger than 1/2 the cache size will be bypassed and go | * Requests larger than 1/2 cache size will be bypassed and go | ||||
* directly to the disk. XXX tune this. | * directly to the disk. XXX tune this. | ||||
*/ | */ | ||||
int | int | ||||
bcache_strategy(void *devdata, int unit, int rw, daddr_t blk, size_t size, | bcache_strategy(void *devdata, int rw, daddr_t blk, size_t offset, | ||||
char *buf, size_t *rsize) | size_t size, char *buf, size_t *rsize) | ||||
{ | { | ||||
static int bcache_unit = -1; | |||||
struct bcache_devdata *dd = (struct bcache_devdata *)devdata; | struct bcache_devdata *dd = (struct bcache_devdata *)devdata; | ||||
struct bcache *bc = dd->dv_cache; | |||||
u_int bcache_nblks = 0; | |||||
int nblk, cblk, ret; | |||||
size_t csize, isize, total; | |||||
bcache_ops++; | bcache_ops++; | ||||
if(bcache_unit != unit) { | if (bc != NULL) | ||||
bcache_flush(); | bcache_nblks = bc->bcache_nblks; | ||||
bcache_unit = unit; | |||||
} | |||||
/* bypass large requests, or when the cache is inactive */ | /* bypass large requests, or when the cache is inactive */ | ||||
if ((bcache_data == NULL) || ((size * 2 / bcache_blksize) > bcache_nblks)) { | if (bc == NULL || | ||||
(offset == 0 && ((size * 2 / bcache_blksize) > bcache_nblks))) { | |||||
DEBUG("bypass %d from %d", size / bcache_blksize, blk); | DEBUG("bypass %d from %d", size / bcache_blksize, blk); | ||||
bcache_bypasses++; | bcache_bypasses++; | ||||
return(dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize)); | return (dd->dv_strategy(dd->dv_devdata, rw, blk, offset, size, buf, | ||||
rsize)); | |||||
} | } | ||||
/* normalize offset */ | |||||
while (offset >= bcache_blksize) { | |||||
blk++; | |||||
offset -= bcache_blksize; | |||||
} | |||||
switch (rw) { | switch (rw) { | ||||
case F_READ: | case F_READ: | ||||
return read_strategy(devdata, unit, rw, blk, size, buf, rsize); | nblk = size / bcache_blksize; | ||||
if (offset || (size != 0 && nblk == 0)) | |||||
nblk++; /* read at least one block */ | |||||
ret = 0; | |||||
total = 0; | |||||
while(size) { | |||||
cblk = bcache_nblks - BHASH(bc, blk); /* # of blocks left */ | |||||
cblk = MIN(cblk, nblk); | |||||
if (size <= bcache_blksize) | |||||
csize = size; | |||||
else { | |||||
csize = cblk * bcache_blksize; | |||||
if (offset) | |||||
csize -= (bcache_blksize - offset); | |||||
} | |||||
ret = read_strategy(devdata, rw, blk, offset, | |||||
csize, buf+total, &isize); | |||||
if (ret != 0) | |||||
return (ret); | |||||
blk += (offset+isize) / bcache_blksize; | |||||
offset = 0; | |||||
total += isize; | |||||
size -= isize; | |||||
nblk = size / bcache_blksize; | |||||
} | |||||
if (rsize) | |||||
*rsize = total; | |||||
return (ret); | |||||
case F_WRITE: | case F_WRITE: | ||||
return write_strategy(devdata, unit, rw, blk, size, buf, rsize); | return write_strategy(devdata, rw, blk, offset, size, buf, rsize); | ||||
} | } | ||||
return -1; | return -1; | ||||
} | } | ||||
/* | /* | ||||
* Insert a block into the cache. Retire the oldest block to do so, if required. | * Free allocated bcache instance | ||||
* | |||||
* XXX the LRU algorithm will fail after 2^31 blocks have been transferred. | |||||
*/ | */ | ||||
static void | static void | ||||
bcache_insert(caddr_t buf, daddr_t blkno) | bcache_free_instance(struct bcache *bc) | ||||
{ | { | ||||
time_t now; | if (bc != NULL) { | ||||
int cand, ocount; | if (bc->bcache_ctl) | ||||
u_int i; | free(bc->bcache_ctl); | ||||
if (bc->bcache_data) | |||||
time(&now); | free(bc->bcache_data); | ||||
cand = 0; /* assume the first block */ | free(bc); | ||||
ocount = bcache_ctl[0].bc_count; | |||||
/* find the oldest block */ | |||||
for (i = 1; i < bcache_nblks; i++) { | |||||
if (bcache_ctl[i].bc_blkno == blkno) { | |||||
/* reuse old entry */ | |||||
cand = i; | |||||
break; | |||||
} | } | ||||
if (bcache_ctl[i].bc_count < ocount) { | |||||
ocount = bcache_ctl[i].bc_count; | |||||
cand = i; | |||||
} | } | ||||
} | |||||
DEBUG("insert blk %d -> %d @ %d # %d", blkno, cand, now, bcache_bcount); | |||||
bcopy(buf, bcache_data + (bcache_blksize * cand), bcache_blksize); | |||||
bcache_ctl[cand].bc_blkno = blkno; | |||||
bcache_ctl[cand].bc_stamp = now; | |||||
bcache_ctl[cand].bc_count = bcache_bcount++; | |||||
} | |||||
/* | /* | ||||
* Look for a block in the cache. Blocks more than BCACHE_TIMEOUT seconds old | * Insert a block into the cache. | ||||
* may be stale (removable media) and thus are discarded. Copy the block out | |||||
* if successful and return zero, or return nonzero on failure. | |||||
*/ | */ | ||||
static int | static void | ||||
bcache_lookup(caddr_t buf, daddr_t blkno) | bcache_insert(struct bcache *bc, daddr_t blkno) | ||||
{ | { | ||||
time_t now; | u_int cand; | ||||
u_int i; | |||||
time(&now); | cand = BHASH(bc, blkno); | ||||
for (i = 0; i < bcache_nblks; i++) | DEBUG("insert blk %llu -> %u # %d", blkno, cand, bcache_bcount); | ||||
/* cache hit? */ | bc->bcache_ctl[cand].bc_blkno = blkno; | ||||
if ((bcache_ctl[i].bc_blkno == blkno) && ((bcache_ctl[i].bc_stamp + BCACHE_TIMEOUT) >= now)) { | bc->bcache_ctl[cand].bc_count = bcache_bcount++; | ||||
bcopy(bcache_data + (bcache_blksize * i), buf, bcache_blksize); | |||||
DEBUG("hit blk %d <- %d (now %d then %d)", blkno, i, now, bcache_ctl[i].bc_stamp); | |||||
return(0); | |||||
} | } | ||||
return(ENOENT); | |||||
} | |||||
/* | /* | ||||
* Invalidate a block from the cache. | * Invalidate a block from the cache. | ||||
*/ | */ | ||||
static void | static void | ||||
bcache_invalidate(daddr_t blkno) | bcache_invalidate(struct bcache *bc, daddr_t blkno) | ||||
{ | { | ||||
u_int i; | u_int i; | ||||
for (i = 0; i < bcache_nblks; i++) { | i = BHASH(bc, blkno); | ||||
if (bcache_ctl[i].bc_blkno == blkno) { | if (bc->bcache_ctl[i].bc_blkno == blkno) { | ||||
bcache_ctl[i].bc_count = -1; | bc->bcache_ctl[i].bc_count = -1; | ||||
bcache_ctl[i].bc_blkno = -1; | bc->bcache_ctl[i].bc_blkno = -1; | ||||
DEBUG("invalidate blk %d", blkno); | DEBUG("invalidate blk %llu", blkno); | ||||
break; | |||||
} | } | ||||
} | } | ||||
} | |||||
#ifndef BOOT2 | |||||
COMMAND_SET(bcachestat, "bcachestat", "get disk block cache stats", command_bcache); | COMMAND_SET(bcachestat, "bcachestat", "get disk block cache stats", command_bcache); | ||||
static int | static int | ||||
command_bcache(int argc, char *argv[]) | command_bcache(int argc, char *argv[]) | ||||
{ | { | ||||
u_int i; | if (argc != 1) { | ||||
Done Inline ActionsI think with this amount of contribution you should add yourself to the copyright header. delphij: I think with this amount of contribution you should add yourself to the copyright header. | |||||
command_errmsg = "wrong number of arguments"; | |||||
for (i = 0; i < bcache_nblks; i++) { | return(CMD_ERROR); | ||||
printf("%08jx %04x %04x|", (uintmax_t)bcache_ctl[i].bc_blkno, (unsigned int)bcache_ctl[i].bc_stamp & 0xffff, bcache_ctl[i].bc_count & 0xffff); | |||||
if (((i + 1) % 4) == 0) | |||||
printf("\n"); | |||||
} | } | ||||
printf("\n%d ops %d bypasses %d hits %d misses %d flushes\n", bcache_ops, bcache_bypasses, bcache_hits, bcache_misses, bcache_flushes); | |||||
printf("\ncache blocks: %d\n", bcache_total_nblks); | |||||
printf("cache blocksz: %d\n", bcache_blksize); | |||||
printf("cache readahead: %d\n", bcache_rablks); | |||||
printf("unit cache blocks: %d\n", bcache_unit_nblks); | |||||
printf("cached units: %d\n", bcache_units); | |||||
printf("%d ops %d bypasses %d hits %d misses\n", bcache_ops, | |||||
bcache_bypasses, bcache_hits, bcache_misses); | |||||
return(CMD_OK); | return(CMD_OK); | ||||
} | } | ||||
#endif |
This should go with the existing copyright statement