| #include <u.h> |
| #include <libc.h> |
| #include <auth.h> |
| #include <fcall.h> |
| #include "dat.h" |
| #include "fns.h" |
| |
| /* |
| * We used to use 100 i/o buffers of size 2kb (Sectorsize). |
| * Unfortunately, reading 2kb at a time often hopping around |
| * the disk doesn't let us get near the disk bandwidth. |
| * |
| * Based on a trace of iobuf address accesses taken while |
| * tarring up a Plan 9 distribution CD, we now use 16 128kb |
| * buffers. This works for ISO9660 because data is required |
| * to be laid out contiguously; effectively we're doing agressive |
| * readahead. Because the buffers are so big and the typical |
| * disk accesses so concentrated, it's okay that we have so few |
| * of them. |
| * |
| * If this is used to access multiple discs at once, it's not clear |
| * how gracefully the scheme degrades, but I'm not convinced |
| * it's worth worrying about. -rsc |
| */ |
| |
| /* trying a larger value to get greater throughput - geoff */ |
| #define BUFPERCLUST 256 /* sectors/cluster; was 64, 64*Sectorsize = 128kb */ |
| #define NCLUST 16 |
| |
| int nclust = NCLUST; |
| |
| static Ioclust* iohead; |
| static Ioclust* iotail; |
| |
| static Ioclust* getclust(Xdata*, long); |
| static void putclust(Ioclust*); |
| static void xread(Ioclust*); |
| |
| void |
| iobuf_init(void) |
| { |
| int i, j, n; |
| Ioclust *c; |
| Iobuf *b; |
| uchar *mem; |
| |
| n = nclust*sizeof(Ioclust) + |
| nclust*BUFPERCLUST*(sizeof(Iobuf)+Sectorsize); |
| mem = sbrk(n); |
| if(mem == (void*)-1) |
| panic(0, "iobuf_init"); |
| memset(mem, 0, n); |
| |
| for(i=0; i<nclust; i++){ |
| c = (Ioclust*)mem; |
| mem += sizeof(Ioclust); |
| c->addr = -1; |
| c->prev = iotail; |
| if(iotail) |
| iotail->next = c; |
| iotail = c; |
| if(iohead == nil) |
| iohead = c; |
| |
| c->buf = (Iobuf*)mem; |
| mem += BUFPERCLUST*sizeof(Iobuf); |
| c->iobuf = mem; |
| mem += BUFPERCLUST*Sectorsize; |
| for(j=0; j<BUFPERCLUST; j++){ |
| b = &c->buf[j]; |
| b->clust = c; |
| b->addr = -1; |
| b->iobuf = c->iobuf+j*Sectorsize; |
| } |
| } |
| } |
| |
| void |
| purgebuf(Xdata *dev) |
| { |
| Ioclust *p; |
| |
| for(p=iohead; p!=nil; p=p->next) |
| if(p->dev == dev){ |
| p->addr = -1; |
| p->busy = 0; |
| } |
| } |
| |
| static Ioclust* |
| getclust(Xdata *dev, long addr) |
| { |
| Ioclust *c, *f; |
| |
| f = nil; |
| for(c=iohead; c; c=c->next){ |
| if(!c->busy) |
| f = c; |
| if(c->addr == addr && c->dev == dev){ |
| c->busy++; |
| return c; |
| } |
| } |
| |
| if(f == nil) |
| panic(0, "out of buffers"); |
| |
| f->addr = addr; |
| f->dev = dev; |
| f->busy++; |
| if(waserror()){ |
| f->addr = -1; /* stop caching */ |
| putclust(f); |
| nexterror(); |
| } |
| xread(f); |
| poperror(); |
| return f; |
| } |
| |
| static void |
| putclust(Ioclust *c) |
| { |
| if(c->busy <= 0) |
| panic(0, "putbuf"); |
| c->busy--; |
| |
| /* Link onto head for LRU */ |
| if(c == iohead) |
| return; |
| c->prev->next = c->next; |
| |
| if(c->next) |
| c->next->prev = c->prev; |
| else |
| iotail = c->prev; |
| |
| c->prev = nil; |
| c->next = iohead; |
| iohead->prev = c; |
| iohead = c; |
| } |
| |
| Iobuf* |
| getbuf(Xdata *dev, ulong addr) |
| { |
| int off; |
| Ioclust *c; |
| |
| off = addr%BUFPERCLUST; |
| c = getclust(dev, addr - off); |
| if(c->nbuf < off){ |
| c->busy--; |
| error("I/O read error"); |
| } |
| return &c->buf[off]; |
| } |
| |
| void |
| putbuf(Iobuf *b) |
| { |
| putclust(b->clust); |
| } |
| |
| static void |
| xread(Ioclust *c) |
| { |
| int n; |
| Xdata *dev; |
| |
| dev = c->dev; |
| seek(dev->dev, (vlong)c->addr * Sectorsize, 0); |
| n = readn(dev->dev, c->iobuf, BUFPERCLUST*Sectorsize); |
| if(n < Sectorsize) |
| error("I/O read error"); |
| c->nbuf = n/Sectorsize; |
| } |