Changeset View
Changeset View
Standalone View
Standalone View
head/sys/boot/common/bcache.c
Show First 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | return; | ||||
bcache_units--; | 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 rw, daddr_t blk, size_t offset, | write_strategy(void *devdata, int rw, daddr_t blk, size_t size, | ||||
size_t size, char *buf, size_t *rsize) | 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; | struct bcache *bc = dd->dv_cache; | ||||
daddr_t i, nblk; | daddr_t i, nblk; | ||||
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(bc, blk + i); | bcache_invalidate(bc, blk + i); | ||||
} | } | ||||
/* Write the blocks */ | /* Write the blocks */ | ||||
return (dd->dv_strategy(dd->dv_devdata, rw, blk, offset, size, buf, rsize)); | return (dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize)); | ||||
} | } | ||||
/* | /* | ||||
* 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. | ||||
*/ | */ | ||||
static int | static int | ||||
read_strategy(void *devdata, int rw, daddr_t blk, size_t offset, | read_strategy(void *devdata, int rw, daddr_t blk, size_t size, | ||||
size_t size, char *buf, size_t *rsize) | 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; | struct bcache *bc = dd->dv_cache; | ||||
size_t i, nblk, p_size, r_size, complete, ra; | size_t i, nblk, p_size, r_size, complete, ra; | ||||
int result; | int result; | ||||
daddr_t p_blk; | daddr_t p_blk; | ||||
caddr_t p_buf; | caddr_t p_buf; | ||||
if (bc == NULL) { | if (bc == NULL) { | ||||
errno = ENODEV; | errno = ENODEV; | ||||
return (-1); | return (-1); | ||||
} | } | ||||
if (rsize != NULL) | if (rsize != NULL) | ||||
*rsize = 0; | *rsize = 0; | ||||
nblk = size / bcache_blksize; | nblk = size / bcache_blksize; | ||||
if ((nblk == 0 && size != 0) || offset != 0) | if (nblk == 0 && size != 0) | ||||
nblk++; | nblk++; | ||||
result = 0; | result = 0; | ||||
complete = 1; | complete = 1; | ||||
/* Satisfy any cache hits up front, break on first miss */ | /* 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(bc, (daddr_t)(blk + i))) { | if (BCACHE_LOOKUP(bc, (daddr_t)(blk + i))) { | ||||
bcache_misses += (nblk - i); | bcache_misses += (nblk - i); | ||||
complete = 0; | complete = 0; | ||||
if (nblk - i > BCACHE_MINREADAHEAD && bc->ra > BCACHE_MINREADAHEAD) | if (nblk - i > BCACHE_MINREADAHEAD && bc->ra > BCACHE_MINREADAHEAD) | ||||
bc->ra >>= 1; /* reduce read ahead */ | bc->ra >>= 1; /* reduce read ahead */ | ||||
break; | break; | ||||
} else { | } else { | ||||
bcache_hits++; | bcache_hits++; | ||||
} | } | ||||
} | } | ||||
if (complete) { /* whole set was in cache, return it */ | if (complete) { /* whole set was in cache, return it */ | ||||
if (bc->ra < BCACHE_READAHEAD) | if (bc->ra < BCACHE_READAHEAD) | ||||
bc->ra <<= 1; /* increase read ahead */ | bc->ra <<= 1; /* increase read ahead */ | ||||
bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)) + offset, | bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)), buf, size); | ||||
buf, size); | |||||
goto done; | goto done; | ||||
} | } | ||||
/* | /* | ||||
* Fill in any misses. From check we have i pointing to first missing | * Fill in any misses. From check we have i pointing to first missing | ||||
* block, read in all remaining blocks + readahead. | * block, read in all remaining blocks + readahead. | ||||
* We have space at least for nblk - i before bcache wraps. | * We have space at least for nblk - i before bcache wraps. | ||||
*/ | */ | ||||
Show All 18 Lines | bcache_invalidate(bc, p_blk + i); | ||||
/* | /* | ||||
* with read-ahead, it may happen we are attempting to read past | * with read-ahead, it may happen we are attempting to read past | ||||
* disk end, as bcache has no information about disk size. | * disk end, as bcache has no information about disk size. | ||||
* in such case we should get partial read if some blocks can be | * in such case we should get partial read if some blocks can be | ||||
* read or error, if no blocks can be read. | * read or error, if no blocks can be read. | ||||
* in either case we should return the data in bcache and only | * in either case we should return the data in bcache and only | ||||
* return error if there is no data. | * return error if there is no data. | ||||
*/ | */ | ||||
result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, 0, | result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, | ||||
p_size * bcache_blksize, p_buf, &r_size); | p_size * bcache_blksize, p_buf, &r_size); | ||||
r_size /= bcache_blksize; | r_size /= bcache_blksize; | ||||
for (i = 0; i < r_size; i++) | for (i = 0; i < r_size; i++) | ||||
bcache_insert(bc, p_blk + i); | bcache_insert(bc, p_blk + i); | ||||
/* update ra statistics */ | /* update ra statistics */ | ||||
if (r_size != 0) { | if (r_size != 0) { | ||||
if (r_size < p_size) | if (r_size < p_size) | ||||
bcache_rablks += (p_size - r_size); | bcache_rablks += (p_size - r_size); | ||||
else | else | ||||
bcache_rablks += ra; | bcache_rablks += ra; | ||||
} | } | ||||
/* check how much data can we copy */ | /* check how much data can we copy */ | ||||
for (i = 0; i < nblk; i++) { | for (i = 0; i < nblk; i++) { | ||||
if (BCACHE_LOOKUP(bc, (daddr_t)(blk + i))) | if (BCACHE_LOOKUP(bc, (daddr_t)(blk + i))) | ||||
break; | break; | ||||
} | } | ||||
if (size > i * bcache_blksize) | if (size > i * bcache_blksize) | ||||
size = i * bcache_blksize; | size = i * bcache_blksize; | ||||
if (size != 0) { | if (size != 0) { | ||||
bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)) + offset, | bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)), buf, size); | ||||
buf, size); | |||||
result = 0; | result = 0; | ||||
} | } | ||||
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 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 rw, daddr_t blk, size_t offset, | bcache_strategy(void *devdata, int rw, daddr_t blk, size_t size, | ||||
size_t size, char *buf, size_t *rsize) | 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; | struct bcache *bc = dd->dv_cache; | ||||
u_int bcache_nblks = 0; | u_int bcache_nblks = 0; | ||||
int nblk, cblk, ret; | int nblk, cblk, ret; | ||||
size_t csize, isize, total; | size_t csize, isize, total; | ||||
bcache_ops++; | bcache_ops++; | ||||
if (bc != NULL) | if (bc != NULL) | ||||
bcache_nblks = bc->bcache_nblks; | bcache_nblks = bc->bcache_nblks; | ||||
/* bypass large requests, or when the cache is inactive */ | /* bypass large requests, or when the cache is inactive */ | ||||
if (bc == NULL || | if (bc == NULL || | ||||
(offset == 0 && ((size * 2 / bcache_blksize) > bcache_nblks))) { | ((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, offset, size, buf, | return (dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize)); | ||||
rsize)); | |||||
} | } | ||||
/* normalize offset */ | |||||
while (offset >= bcache_blksize) { | |||||
blk++; | |||||
offset -= bcache_blksize; | |||||
} | |||||
switch (rw) { | switch (rw) { | ||||
case F_READ: | case F_READ: | ||||
nblk = size / bcache_blksize; | nblk = size / bcache_blksize; | ||||
if (offset || (size != 0 && nblk == 0)) | if (size != 0 && nblk == 0) | ||||
nblk++; /* read at least one block */ | nblk++; /* read at least one block */ | ||||
ret = 0; | ret = 0; | ||||
total = 0; | total = 0; | ||||
while(size) { | while(size) { | ||||
cblk = bcache_nblks - BHASH(bc, blk); /* # of blocks left */ | cblk = bcache_nblks - BHASH(bc, blk); /* # of blocks left */ | ||||
cblk = MIN(cblk, nblk); | cblk = MIN(cblk, nblk); | ||||
if (size <= bcache_blksize) | if (size <= bcache_blksize) | ||||
csize = size; | csize = size; | ||||
else { | else | ||||
csize = cblk * bcache_blksize; | csize = cblk * bcache_blksize; | ||||
if (offset) | |||||
csize -= (bcache_blksize - offset); | |||||
} | |||||
ret = read_strategy(devdata, rw, blk, offset, | ret = read_strategy(devdata, rw, blk, csize, buf+total, &isize); | ||||
csize, buf+total, &isize); | |||||
/* | /* | ||||
* we may have error from read ahead, if we have read some data | * we may have error from read ahead, if we have read some data | ||||
* return partial read. | * return partial read. | ||||
*/ | */ | ||||
if (ret != 0 || isize == 0) { | if (ret != 0 || isize == 0) { | ||||
if (total != 0) | if (total != 0) | ||||
ret = 0; | ret = 0; | ||||
break; | break; | ||||
} | } | ||||
blk += (offset+isize) / bcache_blksize; | blk += isize / bcache_blksize; | ||||
offset = 0; | |||||
total += isize; | total += isize; | ||||
size -= isize; | size -= isize; | ||||
nblk = size / bcache_blksize; | nblk = size / bcache_blksize; | ||||
} | } | ||||
if (rsize) | if (rsize) | ||||
*rsize = total; | *rsize = total; | ||||
return (ret); | return (ret); | ||||
case F_WRITE: | case F_WRITE: | ||||
return write_strategy(devdata, rw, blk, offset, size, buf, rsize); | return write_strategy(devdata, rw, blk, size, buf, rsize); | ||||
} | } | ||||
return -1; | return -1; | ||||
} | } | ||||
/* | /* | ||||
* Free allocated bcache instance | * Free allocated bcache instance | ||||
*/ | */ | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 63 Lines • Show Last 20 Lines |