blob: 01acd06aaaad92d47edeb889a6c393d956d8c5f1 [file] [log] [blame]
#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 = malloc(n);
if(mem == (void*)0)
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;
}