swap.c (7036B)
1 #include "u.h" 2 #include "lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "error.h" 7 8 static int canflush(Proc*, Segment*); 9 static void executeio(void); 10 static int needpages(void *v); 11 static void pageout(Proc*, Segment*); 12 static void pagepte(int, Page**); 13 static void pager(void *v); 14 15 Image swapimage; 16 static Page **iolist; 17 static int ioptr; 18 19 void 20 swapinit(void) 21 { 22 swapalloc.swmap = xalloc(conf.nswap); 23 swapalloc.top = &swapalloc.swmap[conf.nswap]; 24 swapalloc.alloc = swapalloc.swmap; 25 swapalloc.last = swapalloc.swmap; 26 swapalloc.free = conf.nswap; 27 iolist = xalloc(conf.nswppo*sizeof(Page*)); 28 if(swapalloc.swmap == 0 || iolist == 0) 29 panic("swapinit: not enough memory"); 30 31 swapimage.notext = 1; 32 } 33 34 ulong 35 newswap(void) 36 { 37 uchar *look; 38 39 lock(&swapalloc.lk); 40 41 if(swapalloc.free == 0){ 42 unlock(&swapalloc.lk); 43 return ~0; 44 } 45 46 look = memchr(swapalloc.last, 0, swapalloc.top-swapalloc.last); 47 if(look == 0) 48 panic("inconsistent swap"); 49 50 *look = 1; 51 swapalloc.last = look; 52 swapalloc.free--; 53 unlock(&swapalloc.lk); 54 return (look-swapalloc.swmap) * BY2PG; 55 } 56 57 void 58 putswap(Page *p) 59 { 60 uchar *idx; 61 62 lock(&swapalloc.lk); 63 idx = &swapalloc.swmap[((ulong)p)/BY2PG]; 64 if(--(*idx) == 0) { 65 swapalloc.free++; 66 if(idx < swapalloc.last) 67 swapalloc.last = idx; 68 } 69 if(*idx >= 254) 70 panic("putswap %#p == %ud", p, *idx); 71 unlock(&swapalloc.lk); 72 } 73 74 void 75 dupswap(Page *p) 76 { 77 lock(&swapalloc.lk); 78 if(++swapalloc.swmap[((ulong)p)/BY2PG] == 0) 79 panic("dupswap"); 80 unlock(&swapalloc.lk); 81 } 82 83 int 84 swapcount(ulong daddr) 85 { 86 return swapalloc.swmap[daddr/BY2PG]; 87 } 88 89 void 90 kickpager(void) 91 { 92 static int started; 93 94 if(started) 95 wakeup(&swapalloc.r); 96 else { 97 kproc("pager", pager, 0); 98 started = 1; 99 } 100 } 101 102 static void 103 pager(void *junk) 104 { 105 int i; 106 Segment *s; 107 Proc *p, *ep; 108 109 if(waserror()) 110 panic("pager: os error"); 111 112 p = proctab(0); 113 ep = &p[conf.nproc]; 114 115 loop: 116 up->psstate = "Idle"; 117 sleep(&swapalloc.r, needpages, 0); 118 119 while(needpages(junk)) { 120 121 if(swapimage.c) { 122 p++; 123 if(p >= ep) 124 p = proctab(0); 125 126 if(p->state == Dead || p->noswap) 127 continue; 128 129 if(!canqlock(&p->seglock)) 130 continue; /* process changing its segments */ 131 132 for(i = 0; i < NSEG; i++) { 133 if(!needpages(junk)){ 134 qunlock(&p->seglock); 135 goto loop; 136 } 137 138 if((s = p->seg[i])) { 139 switch(s->type&SG_TYPE) { 140 default: 141 break; 142 case SG_TEXT: 143 pageout(p, s); 144 break; 145 case SG_DATA: 146 case SG_BSS: 147 case SG_STACK: 148 case SG_SHARED: 149 up->psstate = "Pageout"; 150 pageout(p, s); 151 if(ioptr != 0) { 152 up->psstate = "I/O"; 153 executeio(); 154 } 155 break; 156 } 157 } 158 } 159 qunlock(&p->seglock); 160 } 161 else { 162 print("out of physical memory; no swap configured\n"); 163 if(!cpuserver || freebroken() == 0) 164 killbig("out of memory"); 165 166 /* Emulate the old system if no swap channel */ 167 tsleep(&up->sleep, return0, 0, 5000); 168 wakeup(&palloc.r); 169 } 170 } 171 goto loop; 172 } 173 174 static void 175 pageout(Proc *p, Segment *s) 176 { 177 int type, i, size; 178 Pte *l; 179 Page **pg, *entry; 180 181 if(!canqlock(&s->lk)) /* We cannot afford to wait, we will surely deadlock */ 182 return; 183 184 if(s->steal) { /* Protected by /dev/proc */ 185 qunlock(&s->lk); 186 return; 187 } 188 189 if(!canflush(p, s)) { /* Able to invalidate all tlbs with references */ 190 qunlock(&s->lk); 191 putseg(s); 192 return; 193 } 194 195 if(waserror()) { 196 qunlock(&s->lk); 197 putseg(s); 198 return; 199 } 200 201 /* Pass through the pte tables looking for memory pages to swap out */ 202 type = s->type&SG_TYPE; 203 size = s->mapsize; 204 for(i = 0; i < size; i++) { 205 l = s->map[i]; 206 if(l == 0) 207 continue; 208 for(pg = l->first; pg < l->last; pg++) { 209 entry = *pg; 210 if(pagedout(entry)) 211 continue; 212 213 if(entry->modref & PG_REF) { 214 entry->modref &= ~PG_REF; 215 continue; 216 } 217 218 pagepte(type, pg); 219 220 if(ioptr >= conf.nswppo) 221 goto out; 222 } 223 } 224 out: 225 poperror(); 226 qunlock(&s->lk); 227 putseg(s); 228 } 229 230 static int 231 canflush(Proc *p, Segment *s) 232 { 233 int i; 234 Proc *ep; 235 236 lock(&s->ref.lk); 237 if(s->ref.ref == 1) { /* Easy if we are the only user */ 238 s->ref.ref++; 239 unlock(&s->ref.lk); 240 return canpage(p); 241 } 242 s->ref.ref++; 243 unlock(&s->ref.lk); 244 245 /* Now we must do hardwork to ensure all processes which have tlb 246 * entries for this segment will be flushed if we succeed in paging it out 247 */ 248 p = proctab(0); 249 ep = &p[conf.nproc]; 250 while(p < ep) { 251 if(p->state != Dead) { 252 for(i = 0; i < NSEG; i++) 253 if(p->seg[i] == s) 254 if(!canpage(p)) 255 return 0; 256 } 257 p++; 258 } 259 return 1; 260 } 261 262 static void 263 pagepte(int type, Page **pg) 264 { 265 ulong daddr; 266 Page *outp; 267 268 outp = *pg; 269 switch(type) { 270 case SG_TEXT: /* Revert to demand load */ 271 putpage(outp); 272 *pg = 0; 273 break; 274 275 case SG_DATA: 276 case SG_BSS: 277 case SG_STACK: 278 case SG_SHARED: 279 /* 280 * get a new swap address and clear any pages 281 * referring to it from the cache 282 */ 283 daddr = newswap(); 284 if(daddr == ~0) 285 break; 286 cachedel(&swapimage, daddr); 287 288 lock(&outp->lk); 289 290 /* forget anything that it used to cache */ 291 uncachepage(outp); 292 293 /* 294 * incr the reference count to make sure it sticks around while 295 * being written 296 */ 297 outp->ref++; 298 299 /* 300 * enter it into the cache so that a fault happening 301 * during the write will grab the page from the cache 302 * rather than one partially written to the disk 303 */ 304 outp->daddr = daddr; 305 cachepage(outp, &swapimage); 306 *pg = (Page*)(daddr|PG_ONSWAP); 307 unlock(&outp->lk); 308 309 /* Add page to IO transaction list */ 310 iolist[ioptr++] = outp; 311 break; 312 } 313 } 314 315 void 316 pagersummary(void) 317 { 318 print("%lud/%lud memory %lud/%lud swap %d iolist\n", 319 palloc.user-palloc.freecount, 320 palloc.user, conf.nswap-swapalloc.free, conf.nswap, 321 ioptr); 322 } 323 324 static void 325 executeio(void) 326 { 327 Page *out; 328 int i, n; 329 Chan *c; 330 char *kaddr; 331 KMap *k; 332 333 c = swapimage.c; 334 335 for(i = 0; i < ioptr; i++) { 336 if(ioptr > conf.nswppo) 337 panic("executeio: ioptr %d > %d", ioptr, conf.nswppo); 338 out = iolist[i]; 339 k = kmap(out); 340 kaddr = (char*)VA(k); 341 342 if(waserror()) 343 panic("executeio: page out I/O error"); 344 345 n = devtab[c->type]->write(c, kaddr, BY2PG, out->daddr); 346 if(n != BY2PG) 347 nexterror(); 348 349 kunmap(k); 350 poperror(); 351 352 /* Free up the page after I/O */ 353 lock(&out->lk); 354 out->ref--; 355 unlock(&out->lk); 356 putpage(out); 357 } 358 ioptr = 0; 359 } 360 361 static int 362 needpages(void *v) 363 { 364 return palloc.freecount < swapalloc.headroom; 365 } 366 367 void 368 setswapchan(Chan *c) 369 { 370 uchar dirbuf[sizeof(Dir)+100]; 371 Dir d; 372 int n; 373 374 if(swapimage.c) { 375 if(swapalloc.free != conf.nswap){ 376 cclose(c); 377 error(Einuse); 378 } 379 cclose(swapimage.c); 380 } 381 382 /* 383 * if this isn't a file, set the swap space 384 * to be at most the size of the partition 385 */ 386 if(devtab[c->type]->dc != L'M'){ 387 n = devtab[c->type]->stat(c, dirbuf, sizeof dirbuf); 388 if(n <= 0){ 389 cclose(c); 390 error("stat failed in setswapchan"); 391 } 392 convM2D(dirbuf, n, &d, nil); 393 if(d.length < conf.nswap*BY2PG){ 394 conf.nswap = d.length/BY2PG; 395 swapalloc.top = &swapalloc.swmap[conf.nswap]; 396 swapalloc.free = conf.nswap; 397 } 398 } 399 400 swapimage.c = c; 401 } 402 403 int 404 swapfull(void) 405 { 406 return swapalloc.free < conf.nswap/10; 407 }