Index: common/bcache.c =================================================================== --- common/bcache.c (revision 226057) +++ common/bcache.c (working copy) @@ -41,11 +41,11 @@ __FBSDID("$FreeBSD$"); /* #define BCACHE_DEBUG */ +#define BCACHE_TIMEOUT 5 + #ifdef BCACHE_DEBUG -#define BCACHE_TIMEOUT 10 # define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) #else -#define BCACHE_TIMEOUT 2 # define DEBUG(fmt, args...) #endif @@ -53,8 +53,8 @@ __FBSDID("$FreeBSD$"); struct bcachectl { daddr_t bc_blkno; + int bc_hits; time_t bc_stamp; - int bc_count; }; static struct bcachectl *bcache_ctl; @@ -62,11 +62,17 @@ static caddr_t bcache_data; static bitstr_t *bcache_miss; static u_int bcache_nblks; static u_int bcache_blksize; -static u_int bcache_hits, bcache_misses, bcache_ops, bcache_bypasses; +static u_int bcache_blocks; +static u_int bcache_hits; +static u_int bcache_misses; +static u_int bcache_ops; +static u_int bcache_bypasses; static u_int bcache_flushes; -static u_int bcache_bcount; +static u_int bcache_replacements; +static u_int bcache_expirations; static void bcache_invalidate(daddr_t blkno); +static void bcache_invalidate_by_idx(u_int i); static void bcache_insert(caddr_t buf, daddr_t blkno); static int bcache_lookup(caddr_t buf, daddr_t blkno); @@ -115,8 +121,9 @@ bcache_flush(void) /* Flush the cache */ for (i = 0; i < bcache_nblks; i++) { - bcache_ctl[i].bc_count = -1; bcache_ctl[i].bc_blkno = -1; + bcache_ctl[i].bc_hits = 0; + bcache_ctl[i].bc_stamp = 0; } } @@ -219,9 +226,10 @@ read_strategy(void *devdata, int unit, int rw, dad return(result); } -/* - * Requests larger than 1/2 the cache size will be bypassed and go - * directly to the disk. XXX tune this. + +/* + * Requests larger than 1/16 the cache size will be bypassed and go + * directly to the disk. */ int bcache_strategy(void *devdata, int unit, int rw, daddr_t blk, size_t size, @@ -231,6 +239,7 @@ bcache_strategy(void *devdata, int unit, int rw, d struct bcache_devdata *dd = (struct bcache_devdata *)devdata; bcache_ops++; + bcache_blocks += size / bcache_blksize; if(bcache_unit != unit) { bcache_flush(); @@ -238,7 +247,7 @@ bcache_strategy(void *devdata, int unit, int rw, d } /* bypass large requests, or when the cache is inactive */ - if ((bcache_data == NULL) || ((size * 2 / bcache_blksize) > bcache_nblks)) { + if ((bcache_data == NULL) || ((size * 16 / bcache_blksize) > bcache_nblks)) { DEBUG("bypass %d from %d", size / bcache_blksize, blk); bcache_bypasses++; return(dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize)); @@ -255,39 +264,58 @@ bcache_strategy(void *devdata, int unit, int rw, d /* - * Insert a block into the cache. Retire the oldest block to do so, if required. - * - * XXX the LRU algorithm will fail after 2^31 blocks have been transferred. + * Insert a block into the cache. Retire the least frequently used block + * to do so, if required. */ static void bcache_insert(caddr_t buf, daddr_t blkno) { time_t now; - int cand, ocount; - u_int i; + int cand, candhits; + u_int i, replace; time(&now); cand = 0; /* assume the first block */ - ocount = bcache_ctl[0].bc_count; + replace = 0; + candhits = bcache_ctl[cand].bc_hits; /* find the oldest block */ for (i = 1; i < bcache_nblks; i++) { if (bcache_ctl[i].bc_blkno == blkno) { /* reuse old entry */ cand = i; + replace = 1; break; } - if (bcache_ctl[i].bc_count < ocount) { - ocount = bcache_ctl[i].bc_count; + if (bcache_ctl[i].bc_blkno == -1) { + /* unused entry */ cand = i; + replace = 0; + break; } + if (bcache_ctl[i].bc_stamp + BCACHE_TIMEOUT < now) { + /* expired entry */ + bcache_invalidate_by_idx(i); + bcache_expirations++; + cand = i; + replace = 0; + break; + } + if (bcache_ctl[i].bc_hits < candhits) { + candhits = bcache_ctl[i].bc_hits; + cand = i; + replace = 1; + } } + + if (replace) + bcache_replacements++; 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_hits = 0; bcache_ctl[cand].bc_stamp = now; - bcache_ctl[cand].bc_count = bcache_bcount++; } /* @@ -303,13 +331,21 @@ bcache_lookup(caddr_t buf, daddr_t blkno) time(&now); - for (i = 0; i < bcache_nblks; i++) - /* cache hit? */ - if ((bcache_ctl[i].bc_blkno == blkno) && ((bcache_ctl[i].bc_stamp + BCACHE_TIMEOUT) >= now)) { + for (i = 0; i < bcache_nblks; i++) { + if (bcache_ctl[i].bc_blkno == blkno) { + if (bcache_ctl[i].bc_stamp + BCACHE_TIMEOUT < now) { + /* expired entry */ + bcache_invalidate_by_idx(i); + bcache_expirations++; + return(ENOENT); + } + bcache_ctl[i].bc_hits++; + bcache_ctl[i].bc_stamp = now; 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); } @@ -322,28 +358,45 @@ bcache_invalidate(daddr_t blkno) u_int i; for (i = 0; i < bcache_nblks; i++) { - if (bcache_ctl[i].bc_blkno == blkno) { - bcache_ctl[i].bc_count = -1; - bcache_ctl[i].bc_blkno = -1; - DEBUG("invalidate blk %d", blkno); - break; - } + if (bcache_ctl[i].bc_blkno == blkno) + bcache_invalidate_by_idx(i); } } +static void +bcache_invalidate_by_idx(u_int i) +{ + + DEBUG("invalidate blk %d", blkno); + bcache_ctl[i].bc_blkno = -1; + bcache_ctl[i].bc_hits = 0; + bcache_ctl[i].bc_stamp = 0; +} + COMMAND_SET(bcachestat, "bcachestat", "get disk block cache stats", command_bcache); static int command_bcache(int argc, char *argv[]) { + time_t now; u_int i; + int ratio; + time(&now); + for (i = 0; i < bcache_nblks; i++) { - 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); + printf("%08jx %02x|", (uintmax_t)bcache_ctl[i].bc_blkno, bcache_ctl[i].bc_hits); 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); + if (bcache_hits > 0) + ratio = (bcache_blocks * 100) / (bcache_hits * 100); + else + ratio = 0; + printf("\n%d blocks, %d blocks hit, %d blocks missed, hit ratio %d%%,\n" + "%d ops, %d bypasses, %d flushes, %d replacements, %d expirations\n", + bcache_blocks, bcache_hits, bcache_misses, ratio, + bcache_ops, bcache_bypasses, bcache_flushes, bcache_replacements, bcache_expirations); return(CMD_OK); } Index: i386/libi386/biosdisk.c =================================================================== --- i386/libi386/biosdisk.c (revision 226057) +++ i386/libi386/biosdisk.c (working copy) @@ -133,6 +133,8 @@ static int nbdinfo = 0; static int bd_getgeom(struct open_disk *od); static int bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest); +static int bd_cached_read(struct open_disk *od, daddr_t dblk, int blks, + caddr_t dest); static int bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest); @@ -470,7 +472,7 @@ bd_printbsdslice(struct open_disk *od, daddr_t off int i; /* read disklabel */ - if (bd_read(od, offset + LABELSECTOR, 1, buf)) + if (bd_cached_read(od, offset + LABELSECTOR, 1, buf)) return; lp =(struct disklabel *)(&buf[0]); if (lp->d_magic != DISKMAGIC) { @@ -536,7 +538,7 @@ bd_open(struct open_file *f, ...) va_end(ap); if ((error = bd_opendisk(&od, dev))) return(error); - + /* * Save our context */ @@ -614,7 +616,7 @@ bd_open_mbr(struct open_disk *od, struct i386_devd * Find the slice in the DOS slice table. */ od->od_nslices = 0; - if (bd_read(od, 0, 1, buf)) { + if (bd_cached_read(od, 0, 1, buf)) { DEBUG("error reading MBR"); return (EIO); } @@ -706,7 +708,7 @@ bd_open_mbr(struct open_disk *od, struct i386_devd DEBUG("opening raw slice"); } else { - if (bd_read(od, sector + LABELSECTOR, 1, buf)) { + if (bd_cached_read(od, sector + LABELSECTOR, 1, buf)) { DEBUG("error reading disklabel"); return (EIO); } @@ -755,7 +757,7 @@ bd_checkextended(struct open_disk *od, int slicenu goto done; if (dp->dp_typ != DOSPTYP_EXT) goto done; - if (bd_read(od, (daddr_t)dp->dp_start, 1, buf)) + if (bd_cached_read(od, (daddr_t)dp->dp_start, 1, buf)) goto done; if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { DEBUG("no magic in extended table"); @@ -867,7 +869,7 @@ bd_open_gpt(struct open_disk *od, struct i386_devd error = 0; /* First, read the MBR and see if we have a PMBR. */ - if (bd_read(od, 0, 1, tbl)) { + if (bd_cached_read(od, 0, 1, tbl)) { DEBUG("error reading MBR"); return (EIO); } @@ -889,7 +891,7 @@ bd_open_gpt(struct open_disk *od, struct i386_devd return (EINVAL); /* Read primary GPT table header. */ - if (bd_read(od, 1, 1, gpt)) { + if (bd_cached_read(od, 1, 1, gpt)) { DEBUG("error reading GPT header"); return (EIO); } @@ -907,7 +909,7 @@ bd_open_gpt(struct open_disk *od, struct i386_devd entries_per_sec = BIOSDISK_SECSIZE / hdr->hdr_entsz; elba = hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec; for (lba = hdr->hdr_lba_table; lba < elba; lba++) { - if (bd_read(od, lba, 1, tbl)) { + if (bd_cached_read(od, lba, 1, tbl)) { DEBUG("error reading GPT table"); return (EIO); } @@ -926,7 +928,7 @@ bd_open_gpt(struct open_disk *od, struct i386_devd od->od_partitions = malloc(part * sizeof(struct gpt_part)); part = 0; for (lba = hdr->hdr_lba_table; lba < elba; lba++) { - if (bd_read(od, lba, 1, tbl)) { + if (bd_cached_read(od, lba, 1, tbl)) { DEBUG("error reading GPT table"); error = EIO; goto out; @@ -1064,6 +1066,25 @@ bd_strategy(void *devdata, int rw, daddr_t dblk, s return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize)); } +static int +bd_cached_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) +{ + struct bcache_devdata bcd; + struct i386_devdesc dev; + size_t size; + + dev.d_kind.biosdisk.data = od; + dev.d_kind.biosdisk.slice = -1; + dev.d_kind.biosdisk.partition = -1; + + bcd.dv_strategy = bd_realstrategy; + bcd.dv_devdata = &dev; + + size = blks * BIOSDISK_SECSIZE; + + return(bcache_strategy(&bcd, od->od_unit, F_READ, dblk, size, dest, &size)); +} + static int bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) { @@ -1084,6 +1105,9 @@ bd_realstrategy(void *devdata, int rw, daddr_t dbl if (rsize) *rsize = 0; + if (dblk == 0) + printf("[bd_realstrategy: reading 0] "); + switch(rw){ case F_READ: DEBUG("read %d from %lld to %p", blks, dblk, buf); @@ -1127,7 +1151,8 @@ bd_realstrategy(void *devdata, int rw, daddr_t dbl } /* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */ -#define FLOPPY_BOUNCEBUF 18 +//#define FLOPPY_BOUNCEBUF 18 +#define FLOPPY_BOUNCEBUF 64 static int bd_edd_io(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest, int write) @@ -1226,12 +1251,16 @@ bd_io(struct open_disk *od, daddr_t dblk, int blks } while (resid > 0) { +#if 0 /* * Play it safe and don't cross track boundaries. * (XXX this is probably unnecessary) */ sec = dblk % od->od_sec; /* offset into track */ x = min(od->od_sec - sec, resid); +#else + x = resid; +#endif if (maxfer > 0) x = min(x, maxfer); /* fit bounce buffer */ @@ -1270,9 +1299,15 @@ bd_io(struct open_disk *od, daddr_t dblk, int blks if (write) DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x, p, VTOP(p), dblk, result ? "failed" : "ok"); - else + else { DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x, dblk, p, VTOP(p), result ? "failed" : "ok"); + +#if 0 + if (x <= 8) + printf("%lld->%lld ", dblk, dblk + x); +#endif + } if (result) { return(-1); } Index: i386/loader/main.c =================================================================== --- i386/loader/main.c (revision 226057) +++ i386/loader/main.c (working copy) @@ -140,7 +140,7 @@ main(void) /* * Initialise the block cache */ - bcache_init(32, 512); /* 16k cache XXX tune this */ + bcache_init(64, 512); /* 32kB cache */ /* * Special handling for PXE and CD booting.