swap.c 7.4 KB

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