Commit | Line | Data |
---|---|---|
3b04f6f0 C |
1 | /* vmmem.c 4.7 81/07/09 */ |
2 | ||
3 | #include "../h/param.h" | |
4 | #include "../h/systm.h" | |
5 | #include "../h/pte.h" | |
6 | #include "../h/cmap.h" | |
7 | #include "../h/dir.h" | |
8 | #include "../h/user.h" | |
9 | #include "../h/proc.h" | |
10 | #include "../h/mtpr.h" | |
11 | #include "../h/text.h" | |
12 | #include "../h/vm.h" | |
13 | #include "../h/file.h" | |
14 | #include "../h/inode.h" | |
15 | #include "../h/buf.h" | |
16 | #include "../h/mount.h" | |
17 | #include "../h/trace.h" | |
18 | #include "../h/map.h" | |
19 | ||
20 | /* | |
21 | * Allocate memory, and always succeed | |
22 | * by jolting page-out daemon | |
23 | * so as to obtain page frames. | |
24 | * To be used in conjunction with vmemfree(). | |
25 | */ | |
26 | vmemall(pte, size, p, type) | |
27 | register struct pte *pte; | |
28 | int size; | |
29 | struct proc *p; | |
30 | { | |
31 | register int m; | |
32 | ||
33 | if (size <= 0 || size > maxmem) | |
34 | panic("vmemall size"); | |
35 | while (size > 0) { | |
36 | if (freemem < desfree) | |
37 | wakeup((caddr_t)&proc[2]); /* jolt daemon */ | |
38 | while (freemem == 0) | |
39 | sleep((caddr_t)&freemem, PSWP+2); | |
40 | m = imin(size, freemem); | |
41 | (void) memall(pte, m, p, type); | |
42 | size -= m; | |
43 | pte += m; | |
44 | } | |
45 | if (freemem < desfree) | |
46 | wakeup((caddr_t)&proc[2]); /* jolt daemon */ | |
47 | /* | |
48 | * Always succeeds, but return success for | |
49 | * vgetu and vgetpt (e.g.) which call either | |
50 | * memall or vmemall depending on context. | |
51 | */ | |
52 | return (1); | |
53 | } | |
54 | ||
55 | /* | |
56 | * Free valid and reclaimable page frames belonging to the | |
57 | * count pages starting at pte. If a page is valid | |
58 | * or reclaimable and locked (but not a system page), then | |
59 | * we simply mark the page as c_gone and let the pageout | |
60 | * daemon free the page when it is through with it. | |
61 | * If a page is reclaimable, and already in the free list, then | |
62 | * we mark the page as c_gone, and (of course) don't free it. | |
63 | * | |
64 | * Determines the largest contiguous cluster of | |
65 | * valid pages and frees them in one call to memfree. | |
66 | */ | |
67 | vmemfree(pte, count) | |
68 | register struct pte *pte; | |
69 | register int count; | |
70 | { | |
71 | register struct cmap *c; | |
72 | register struct pte *spte; | |
73 | register int j; | |
74 | int size, pcnt, fileno; | |
75 | ||
76 | if (count % CLSIZE) | |
77 | panic("vmemfree"); | |
78 | for (size = 0, pcnt = 0; count > 0; pte += CLSIZE, count -= CLSIZE) { | |
79 | if (pte->pg_fod == 0 && pte->pg_pfnum) { | |
80 | c = &cmap[pgtocm(pte->pg_pfnum)]; | |
81 | pcnt += CLSIZE; | |
82 | if (c->c_lock && c->c_type != CSYS) { | |
83 | for (j = 0; j < CLSIZE; j++) | |
84 | *(int *)(pte+j) &= (PG_PROT|PG_VREADM); | |
85 | c->c_gone = 1; | |
86 | goto free; | |
87 | } | |
88 | if (c->c_free) { | |
89 | pcnt -= CLSIZE; | |
90 | for (j = 0; j < CLSIZE; j++) | |
91 | *(int *)(pte+j) &= (PG_PROT|PG_VREADM); | |
92 | if (c->c_type == CTEXT) | |
93 | distpte(&text[c->c_ndx], (int)c->c_page, pte); | |
94 | c->c_gone = 1; | |
95 | goto free; | |
96 | } | |
97 | if (size == 0) | |
98 | spte = pte; | |
99 | size += CLSIZE; | |
100 | continue; | |
101 | } | |
102 | if (pte->pg_fod) { | |
103 | fileno = ((struct fpte *)pte)->pg_fileno; | |
104 | if (fileno < NOFILE) | |
105 | if ((u.u_vrpages[fileno] -= CLSIZE) <= 0) { | |
106 | if (u.u_vrpages[fileno] < 0) | |
107 | panic("vmemfree vrpages"); | |
108 | if (--u.u_ofile[fileno]->f_inode->i_vfdcnt < 0) | |
109 | panic("vmemfree vfdcnt"); | |
110 | } | |
111 | for (j = 0; j < CLSIZE; j++) | |
112 | *(int *)(pte+j) &= (PG_PROT|PG_VREADM); | |
113 | } | |
114 | free: | |
115 | if (size) { | |
116 | memfree(spte, size, 1); | |
117 | size = 0; | |
118 | } | |
119 | } | |
120 | if (size) | |
121 | memfree(spte, size, 1); | |
122 | return (pcnt); | |
123 | } | |
124 | ||
125 | /* | |
126 | * Unlink a page frame from the free list - | |
127 | * | |
128 | * Performed if the page being reclaimed | |
129 | * is in the free list. | |
130 | */ | |
131 | munlink(pf) | |
132 | unsigned pf; | |
133 | { | |
134 | register int next, prev; | |
135 | ||
136 | next = cmap[pgtocm(pf)].c_next; | |
137 | prev = cmap[pgtocm(pf)].c_prev; | |
138 | cmap[prev].c_next = next; | |
139 | cmap[next].c_prev = prev; | |
140 | cmap[pgtocm(pf)].c_free = 0; | |
141 | if (freemem < minfree) | |
142 | wakeup((caddr_t)&proc[2]); /* jolt paging daemon */ | |
143 | freemem -= CLSIZE; | |
144 | } | |
145 | ||
146 | /* | |
147 | * Allocate memory - | |
148 | * | |
149 | * The free list appears as a doubly linked list | |
150 | * in the core map with cmap[0] serving as a header. | |
151 | */ | |
152 | memall(pte, size, p, type) | |
153 | register struct pte *pte; | |
154 | int size; | |
155 | struct proc *p; | |
156 | { | |
157 | register struct cmap *c; | |
158 | register struct pte *rpte; | |
159 | register struct proc *rp; | |
160 | int i, j, next, curpos; | |
161 | unsigned pf; | |
162 | struct cmap *c1, *c2; | |
163 | ||
164 | if (size % CLSIZE) | |
165 | panic("memall"); | |
166 | if (size > freemem) | |
167 | return (0); | |
168 | #ifdef TRACE | |
169 | trace(TR_MALL, size, u.u_procp->p_pid); | |
170 | #endif | |
171 | for (i = size; i > 0; i -= CLSIZE) { | |
172 | curpos = cmap[CMHEAD].c_next; | |
173 | c = &cmap[curpos]; | |
174 | if (c->c_free == 0) | |
175 | panic("dup mem alloc"); | |
176 | if (cmtopg(curpos) > maxfree) | |
177 | panic("bad mem alloc"); | |
178 | if (c->c_gone == 0 && c->c_type != CSYS) { | |
179 | if (c->c_type == CTEXT) | |
180 | rp = text[c->c_ndx].x_caddr; | |
181 | else | |
182 | rp = &proc[c->c_ndx]; | |
183 | while (rp->p_flag & SNOVM) | |
184 | rp = rp->p_xlink; | |
185 | switch (c->c_type) { | |
186 | ||
187 | case CTEXT: | |
188 | rpte = tptopte(rp, c->c_page); | |
189 | break; | |
190 | ||
191 | case CDATA: | |
192 | rpte = dptopte(rp, c->c_page); | |
193 | break; | |
194 | ||
195 | case CSTACK: | |
196 | rpte = sptopte(rp, c->c_page); | |
197 | break; | |
198 | } | |
199 | zapcl(rpte, pg_pfnum) = 0; | |
200 | if (c->c_type == CTEXT) | |
201 | distpte(&text[c->c_ndx], (int)c->c_page, rpte); | |
202 | } | |
203 | switch (type) { | |
204 | ||
205 | case CSYS: | |
206 | c->c_ndx = p->p_ndx; | |
207 | break; | |
208 | ||
209 | case CTEXT: | |
210 | c->c_page = vtotp(p, ptetov(p, pte)); | |
211 | c->c_ndx = p->p_textp - &text[0]; | |
212 | break; | |
213 | ||
214 | case CDATA: | |
215 | c->c_page = vtodp(p, ptetov(p, pte)); | |
216 | c->c_ndx = p->p_ndx; | |
217 | break; | |
218 | ||
219 | case CSTACK: | |
220 | c->c_page = vtosp(p, ptetov(p, pte)); | |
221 | c->c_ndx = p->p_ndx; | |
222 | break; | |
223 | } | |
224 | if (c->c_blkno) { | |
225 | /* | |
226 | * This is very like munhash(), except | |
227 | * that we really don't want to bother | |
228 | * to calculate a dev to pass to it. | |
229 | */ | |
230 | j = CMHASH(c->c_blkno); | |
231 | c1 = &cmap[cmhash[j]]; | |
232 | if (c1 == c) | |
233 | cmhash[j] = c1->c_hlink; | |
234 | else { | |
235 | for (;;) { | |
236 | if (c1 == ecmap) | |
237 | panic("memall ecmap"); | |
238 | c2 = c1; | |
239 | c1 = &cmap[c2->c_hlink]; | |
240 | if (c1 == c) | |
241 | break; | |
242 | } | |
243 | c2->c_hlink = c1->c_hlink; | |
244 | } | |
245 | if (mfind(c->c_mdev == MSWAPX ? | |
246 | swapdev : mount[c->c_mdev].m_dev, | |
247 | (daddr_t)c->c_blkno)) | |
248 | panic("memall mfind"); | |
249 | c1->c_mdev = 0; | |
250 | c1->c_blkno = 0; | |
251 | c1->c_hlink = 0; | |
252 | } | |
253 | pf = cmtopg(curpos); | |
254 | for (j = 0; j < CLSIZE; j++) | |
255 | *(int *)pte++ = pf++; | |
256 | c->c_free = 0; | |
257 | c->c_gone = 0; | |
258 | if (c->c_intrans || c->c_want) | |
259 | panic("memall intrans|want"); | |
260 | c->c_lock = 1; | |
261 | c->c_type = type; | |
262 | freemem -= CLSIZE; | |
263 | next = c->c_next; | |
264 | cmap[CMHEAD].c_next = next; | |
265 | cmap[next].c_prev = CMHEAD; | |
266 | } | |
267 | return (size); | |
268 | } | |
269 | ||
270 | /* | |
271 | * Free memory - | |
272 | * | |
273 | * The page frames being returned are inserted | |
274 | * to the head/tail of the free list depending | |
275 | * on whether there is any possible future use of them. | |
276 | * | |
277 | * If the freemem count had been zero, | |
278 | * the processes sleeping for memory | |
279 | * are awakened. | |
280 | */ | |
281 | memfree(pte, size, detach) | |
282 | register struct pte *pte; | |
283 | register int size; | |
284 | { | |
285 | register int i, j, prev, next; | |
286 | register struct cmap *c; | |
287 | ||
288 | if (size % CLSIZE) | |
289 | panic("memfree"); | |
290 | if (freemem < CLSIZE * KLMAX) | |
291 | wakeup((caddr_t)&freemem); | |
292 | while (size > 0) { | |
293 | size -= CLSIZE; | |
294 | i = pte->pg_pfnum; | |
295 | if (i < firstfree || i > maxfree) | |
296 | panic("bad mem free"); | |
297 | i = pgtocm(i); | |
298 | c = &cmap[i]; | |
299 | if (c->c_free) | |
300 | panic("dup mem free"); | |
301 | if (detach && c->c_type != CSYS) { | |
302 | for (j = 0; j < CLSIZE; j++) | |
303 | *(int *)(pte+j) &= (PG_PROT|PG_VREADM); | |
304 | c->c_gone = 1; | |
305 | } | |
306 | if (detach && c->c_blkno == 0) { | |
307 | next = cmap[CMHEAD].c_next; | |
308 | cmap[next].c_prev = i; | |
309 | c->c_prev = CMHEAD; | |
310 | c->c_next = next; | |
311 | cmap[CMHEAD].c_next = i; | |
312 | } else { | |
313 | prev = cmap[CMHEAD].c_prev; | |
314 | cmap[prev].c_next = i; | |
315 | c->c_next = CMHEAD; | |
316 | c->c_prev = prev; | |
317 | cmap[CMHEAD].c_prev = i; | |
318 | } | |
319 | c->c_free = 1; | |
320 | freemem += CLSIZE; | |
321 | pte += CLSIZE; | |
322 | } | |
323 | } | |
324 | ||
325 | /* | |
326 | * Allocate wired-down (non-paged) pages in kernel virtual memory. | |
327 | */ | |
328 | caddr_t | |
329 | wmemall(pmemall, n) | |
330 | int (*pmemall)(), n; | |
331 | { | |
332 | int npg; | |
333 | caddr_t va; | |
334 | register int a; | |
335 | ||
336 | npg = btoc(n); | |
337 | a = rmalloc(kernelmap, npg); | |
338 | if (a == 0) | |
339 | return (0); | |
340 | if ((*pmemall)(&Usrptmap[a], npg, &proc[0], CSYS) == 0) { | |
341 | rmfree(kernelmap, npg, a); | |
342 | return (0); | |
343 | } | |
344 | va = (caddr_t) kmxtob(a); | |
345 | vmaccess(&Usrptmap[a], va, npg); | |
346 | return (va); | |
347 | } | |
348 | ||
349 | wmemfree(va, n) | |
350 | caddr_t va; | |
351 | int n; | |
352 | { | |
353 | register int a; | |
354 | int npg; | |
355 | ||
356 | a = btokmx((struct pte *) va); | |
357 | npg = btoc(n); | |
358 | (void) memfree(&Usrptmap[a], npg, 0); | |
359 | rmfree(kernelmap, npg, a); | |
360 | } | |
361 | ||
362 | /* | |
363 | * Enter clist block c on the hash chains. | |
364 | * It contains file system block bn from device dev. | |
365 | * Dev must either be a mounted file system or the swap device | |
366 | * so we panic if getfsx() cannot find it. | |
367 | */ | |
368 | mhash(c, dev, bn) | |
369 | register struct cmap *c; | |
370 | dev_t dev; | |
371 | daddr_t bn; | |
372 | { | |
373 | register int i = CMHASH(bn); | |
374 | ||
375 | c->c_hlink = cmhash[i]; | |
376 | cmhash[i] = c - cmap; | |
377 | c->c_blkno = bn; | |
378 | i = getfsx(dev); | |
379 | if (i == -1) | |
380 | panic("mhash"); | |
381 | c->c_mdev = i; | |
382 | } | |
383 | ||
384 | /* | |
385 | * Pull the clist entry of <dev,bn> off the hash chains. | |
386 | * We have checked before calling (using mfind) that the | |
387 | * entry really needs to be unhashed, so panic if we can't | |
388 | * find it (can't happen). | |
389 | */ | |
390 | munhash(dev, bn) | |
391 | dev_t dev; | |
392 | daddr_t bn; | |
393 | { | |
394 | register int i = CMHASH(bn); | |
395 | register struct cmap *c1, *c2; | |
396 | ||
397 | c1 = &cmap[cmhash[i]]; | |
398 | if (c1 == ecmap) | |
399 | panic("munhash"); | |
400 | if (c1->c_blkno == bn && getfsx(dev) == c1->c_mdev) | |
401 | cmhash[i] = c1->c_hlink; | |
402 | else { | |
403 | for (;;) { | |
404 | c2 = c1; | |
405 | c1 = &cmap[c2->c_hlink]; | |
406 | if (c1 == ecmap) | |
407 | panic("munhash"); | |
408 | if (c1->c_blkno == bn && getfsx(dev) == c1->c_mdev) | |
409 | break; | |
410 | } | |
411 | c2->c_hlink = c1->c_hlink; | |
412 | } | |
413 | if (mfind(dev, bn)) | |
414 | panic("munhash mfind"); | |
415 | c1->c_mdev = 0; | |
416 | c1->c_blkno = 0; | |
417 | c1->c_hlink = 0; | |
418 | } | |
419 | ||
420 | /* | |
421 | * Look for block bn of device dev in the free pool. | |
422 | * Currently it should not be possible to find it unless it is | |
423 | * c_free and c_gone, although this may later not be true. | |
424 | * (This is because active texts are locked against file system | |
425 | * writes by the system.) | |
426 | */ | |
427 | struct cmap * | |
428 | mfind(dev, bn) | |
429 | dev_t dev; | |
430 | daddr_t bn; | |
431 | { | |
432 | register struct cmap *c1 = &cmap[cmhash[CMHASH(bn)]]; | |
433 | ||
434 | while (c1 != ecmap) { | |
435 | if (c1->c_blkno == bn && c1->c_mdev == getfsx(dev)) | |
436 | return (c1); | |
437 | c1 = &cmap[c1->c_hlink]; | |
438 | } | |
439 | return ((struct cmap *)0); | |
440 | } | |
441 | ||
442 | /* | |
443 | * Purge blocks from device dev from incore cache | |
444 | * before umount(). | |
445 | */ | |
446 | mpurge(mdev) | |
447 | int mdev; | |
448 | { | |
449 | register struct cmap *c1, *c2; | |
450 | register int i; | |
451 | ||
452 | for (i = 0; i < CMHSIZ; i++) { | |
453 | more: | |
454 | c1 = &cmap[cmhash[i]]; | |
455 | if (c1 == ecmap) | |
456 | continue; | |
457 | if (c1->c_mdev == mdev) | |
458 | cmhash[i] = c1->c_hlink; | |
459 | else { | |
460 | for (;;) { | |
461 | c2 = c1; | |
462 | c1 = &cmap[c1->c_hlink]; | |
463 | if (c1 == ecmap) | |
464 | goto cont; | |
465 | if (c1->c_mdev == mdev) | |
466 | break; | |
467 | } | |
468 | c2->c_hlink = c1->c_hlink; | |
469 | } | |
470 | c1->c_mdev = 0; | |
471 | c1->c_blkno = 0; | |
472 | c1->c_hlink = 0; | |
473 | goto more; | |
474 | cont: | |
475 | ; | |
476 | } | |
477 | } | |
478 | ||
479 | /* | |
480 | * Initialize core map | |
481 | */ | |
482 | meminit(first, last) | |
483 | int first, last; | |
484 | { | |
485 | register int i; | |
486 | register struct cmap *c; | |
487 | ||
488 | firstfree = clrnd(first); | |
489 | maxfree = clrnd(last - (CLSIZE - 1)); | |
490 | freemem = maxfree - firstfree; | |
491 | ecmx = ecmap - cmap; | |
492 | if (ecmx < freemem / CLSIZE) | |
493 | freemem = ecmx * CLSIZE; | |
494 | for (i = 1; i <= freemem / CLSIZE; i++) { | |
495 | cmap[i-1].c_next = i; | |
496 | c = &cmap[i]; | |
497 | c->c_prev = i-1; | |
498 | c->c_free = 1; | |
499 | c->c_gone = 1; | |
500 | c->c_type = CSYS; | |
501 | c->c_mdev = 0; | |
502 | c->c_blkno = 0; | |
503 | } | |
504 | cmap[freemem / CLSIZE].c_next = CMHEAD; | |
505 | for (i = 0; i < CMHSIZ; i++) | |
506 | cmhash[i] = ecmx; | |
507 | cmap[CMHEAD].c_prev = freemem / CLSIZE; | |
508 | cmap[CMHEAD].c_type = CSYS; | |
509 | avefree = freemem; | |
510 | hand = 0; | |
511 | } | |
512 | ||
513 | /* | |
514 | * Wait for frame pf to become unlocked | |
515 | * if it is currently locked. | |
516 | * | |
517 | * THIS ROUTINE SHOULD TAKE A CMAP STRUCTURE AS ARGUMENT. | |
518 | */ | |
519 | mwait(pf) | |
520 | unsigned pf; | |
521 | { | |
522 | ||
523 | mlock(pf); | |
524 | munlock(pf); | |
525 | } | |
526 | ||
527 | /* | |
528 | * Lock a page frame. | |
529 | * | |
530 | * THIS ROUTINE SHOULD TAKE A CMAP STRUCTURE AS ARGUMENT. | |
531 | */ | |
532 | mlock(pf) | |
533 | unsigned pf; | |
534 | { | |
535 | register struct cmap *c = &cmap[pgtocm(pf)]; | |
536 | ||
537 | while (c->c_lock) { | |
538 | c->c_want = 1; | |
539 | sleep((caddr_t)c, PSWP+1); | |
540 | } | |
541 | c->c_lock = 1; | |
542 | } | |
543 | ||
544 | /* | |
545 | * Unlock a page frame. | |
546 | * | |
547 | * THIS ROUTINE SHOULD TAKE A CMAP STRUCTURE AS ARGUMENT. | |
548 | */ | |
549 | munlock(pf) | |
550 | unsigned pf; | |
551 | { | |
552 | register struct cmap *c = &cmap[pgtocm(pf)]; | |
553 | ||
554 | if (c->c_lock == 0) | |
555 | panic("dup page unlock"); | |
556 | if (c->c_want) | |
557 | wakeup((caddr_t)c); | |
558 | c->c_lock = 0; | |
559 | c->c_want = 0; | |
560 | } | |
561 | ||
562 | /* | |
563 | * Lock a virtual segment. | |
564 | * | |
565 | * For each cluster of pages, if the cluster is not valid, | |
566 | * touch it to fault it in, otherwise just lock page frame. | |
567 | * Called from physio to ensure that the pages | |
568 | * participating in raw i/o are valid and locked. | |
569 | * We use SDLYU to keep pagein from unlocking pages, | |
570 | * so they make it safely back here locked. | |
571 | */ | |
572 | vslock(base, count) | |
573 | caddr_t base; | |
574 | { | |
575 | register unsigned v; | |
576 | register int npf; | |
577 | register struct pte *pte; | |
578 | ||
579 | u.u_procp->p_flag |= SDLYU; | |
580 | v = btop(base); | |
581 | pte = vtopte(u.u_procp, v); | |
582 | npf = btoc(count + ((int)base & CLOFSET)); | |
583 | while (npf > 0) { | |
584 | if (pte->pg_v) | |
585 | mlock(pte->pg_pfnum); | |
586 | else | |
587 | if (fubyte((caddr_t)ctob(v)) < 0) | |
588 | panic("vslock"); | |
589 | pte += CLSIZE; | |
590 | v += CLSIZE; | |
591 | npf -= CLSIZE; | |
592 | } | |
593 | u.u_procp->p_flag &= ~SDLYU; | |
594 | } | |
595 | ||
596 | /* | |
597 | * Unlock a virtual segment. | |
598 | */ | |
599 | vsunlock(base, count, rw) | |
600 | caddr_t base; | |
601 | { | |
602 | register struct pte *pte; | |
603 | register int npf; | |
604 | ||
605 | pte = vtopte(u.u_procp, btop(base)); | |
606 | npf = btoc(count + ((int)base & CLOFSET)); | |
607 | while (npf > 0) { | |
608 | munlock(pte->pg_pfnum); | |
609 | if (rw == B_READ) /* Reading from device writes memory */ | |
610 | pte->pg_m = 1; | |
611 | pte += CLSIZE; | |
612 | npf -= CLSIZE; | |
613 | } | |
614 | } |