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