1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include "dat.h" 6 #include "fns.h" 7 8 /* 9 * We used to use 100 i/o buffers of size 2kb (Sectorsize). 10 * Unfortunately, reading 2kb at a time often hopping around 11 * the disk doesn't let us get near the disk bandwidth. 12 * 13 * Based on a trace of iobuf address accesses taken while 14 * tarring up a Plan 9 distribution CD, we now use 16 128kb 15 * buffers. This works for ISO9660 because data is required 16 * to be laid out contiguously; effectively we're doing agressive 17 * readahead. Because the buffers are so big and the typical 18 * disk accesses so concentrated, it's okay that we have so few 19 * of them. 20 * 21 * If this is used to access multiple discs at once, it's not clear 22 * how gracefully the scheme degrades, but I'm not convinced 23 * it's worth worrying about. -rsc 24 */ 25 26 #define BUFPERCLUST 64 /* 64*Sectorsize = 128kb */ 27 #define NCLUST 16 28 29 static Ioclust* iohead; 30 static Ioclust* iotail; 31 static Ioclust* getclust(Xdata*, long); 32 static void putclust(Ioclust*); 33 static void xread(Ioclust*); 34 35 void 36 iobuf_init(void) 37 { 38 int i, j, n; 39 Ioclust *c; 40 Iobuf *b; 41 uchar *mem; 42 43 n = NCLUST*sizeof(Ioclust)+NCLUST*BUFPERCLUST*(sizeof(Iobuf)+Sectorsize); 44 mem = sbrk(n); 45 if(mem == (void*)-1) 46 panic(0, "iobuf_init"); 47 memset(mem, 0, n); 48 49 for(i=0; i<NCLUST; i++){ 50 c = (Ioclust*)mem; 51 mem += sizeof(Ioclust); 52 c->addr = -1; 53 c->prev = iotail; 54 if(iotail) 55 iotail->next = c; 56 iotail = c; 57 if(iohead == nil) 58 iohead = c; 59 60 c->buf = (Iobuf*)mem; 61 mem += BUFPERCLUST*sizeof(Iobuf); 62 c->iobuf = mem; 63 mem += BUFPERCLUST*Sectorsize; 64 for(j=0; j<BUFPERCLUST; j++){ 65 b = &c->buf[j]; 66 b->clust = c; 67 b->addr = -1; 68 b->iobuf = c->iobuf+j*Sectorsize; 69 } 70 } 71 } 72 73 void 74 purgebuf(Xdata *dev) 75 { 76 Ioclust *p; 77 78 for(p=iohead; p!=nil; p=p->next) 79 if(p->dev == dev){ 80 p->addr = -1; 81 p->busy = 0; 82 } 83 } 84 85 static Ioclust* 86 getclust(Xdata *dev, long addr) 87 { 88 Ioclust *c, *f; 89 90 f = nil; 91 for(c=iohead; c; c=c->next){ 92 if(!c->busy) 93 f = c; 94 if(c->addr == addr && c->dev == dev){ 95 c->busy++; 96 return c; 97 } 98 } 99 100 if(f == nil) 101 panic(0, "out of buffers"); 102 103 f->addr = addr; 104 f->dev = dev; 105 f->busy++; 106 if(waserror()){ 107 f->addr = -1; /* stop caching */ 108 putclust(f); 109 nexterror(); 110 } 111 xread(f); 112 poperror(); 113 return f; 114 } 115 116 static void 117 putclust(Ioclust *c) 118 { 119 if(c->busy <= 0) 120 panic(0, "putbuf"); 121 c->busy--; 122 123 /* Link onto head for LRU */ 124 if(c == iohead) 125 return; 126 c->prev->next = c->next; 127 128 if(c->next) 129 c->next->prev = c->prev; 130 else 131 iotail = c->prev; 132 133 c->prev = nil; 134 c->next = iohead; 135 iohead->prev = c; 136 iohead = c; 137 } 138 139 Iobuf* 140 getbuf(Xdata *dev, long addr) 141 { 142 int off; 143 Ioclust *c; 144 145 off = addr%BUFPERCLUST; 146 c = getclust(dev, addr - off); 147 if(c->nbuf < off){ 148 c->busy--; 149 error("I/O read error"); 150 } 151 return &c->buf[off]; 152 } 153 154 void 155 putbuf(Iobuf *b) 156 { 157 putclust(b->clust); 158 } 159 160 static void 161 xread(Ioclust *c) 162 { 163 int n; 164 vlong addr; 165 Xdata *dev; 166 167 dev = c->dev; 168 addr = c->addr; 169 seek(dev->dev, addr*Sectorsize, 0); 170 n = readn(dev->dev, c->iobuf, BUFPERCLUST*Sectorsize); 171 if(n < Sectorsize) 172 error("I/O read error"); 173 c->nbuf = n/Sectorsize; 174 } 175