|
|
1.1.1.2 root 1: /*
2: * linux/mm/memory.c
3: *
4: * (C) 1991 Linus Torvalds
5: */
6:
1.1.1.3 root 7: /*
8: * demand-loading started 01.12.91 - seems it is high on the list of
9: * things wanted, and it should be easy to implement. - Linus
10: */
11:
12: /*
13: * Ok, demand-loading was easy, shared pages a little bit tricker. Shared
14: * pages started 02.12.91, seems to work. - Linus.
15: *
16: * Tested sharing by executing about 30 /bin/sh: under the old kernel it
17: * would have taken more than the 6M I have free, but it worked well as
18: * far as I could see.
19: *
20: * Also corrected some "invalidate()"s - I wasn't doing enough of them.
21: */
22:
1.1.1.4 root 23: /*
24: * Real VM (paging to/from disk) started 18.12.91. Much more work and
25: * thought has to go into this. Oh, well..
26: * 19.12.91 - works, somewhat. Sometimes I get faults, don't know why.
27: * Found it. Everything seems to work now.
28: * 20.12.91 - Ok, making the swap-device changeable like the root.
29: */
30:
1.1 root 31: #include <signal.h>
32:
1.1.1.3 root 33: #include <asm/system.h>
34:
35: #include <linux/sched.h>
1.1 root 36: #include <linux/head.h>
37: #include <linux/kernel.h>
38:
1.1.1.3 root 39: #define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
40: current->start_code + current->end_code)
41:
1.1.1.4 root 42: unsigned long HIGH_MEMORY = 0;
1.1 root 43:
44: #define copy_page(from,to) \
45: __asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
46:
1.1.1.5 ! root 47: #define CHECK_LAST_NR 16
! 48:
! 49: static unsigned long last_pages[CHECK_LAST_NR] = { 0, };
! 50:
1.1.1.4 root 51: unsigned char mem_map [ PAGING_PAGES ] = {0,};
1.1 root 52:
53: /*
54: * Free a page of memory at physical address 'addr'. Used by
55: * 'free_page_tables()'
56: */
57: void free_page(unsigned long addr)
58: {
1.1.1.2 root 59: if (addr < LOW_MEM) return;
1.1.1.5 ! root 60: if (addr < HIGH_MEMORY) {
! 61: addr -= LOW_MEM;
! 62: addr >>= 12;
! 63: if (mem_map[addr]--)
! 64: return;
! 65: mem_map[addr]=0;
! 66: }
! 67: printk("trying to free free page: memory probably corrupted");
1.1 root 68: }
69:
70: /*
71: * This function frees a continuos block of page tables, as needed
72: * by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks.
73: */
74: int free_page_tables(unsigned long from,unsigned long size)
75: {
1.1.1.5 ! root 76: unsigned long page;
! 77: unsigned long page_dir;
1.1 root 78: unsigned long *pg_table;
79: unsigned long * dir, nr;
80:
81: if (from & 0x3fffff)
82: panic("free_page_tables called with wrong alignment");
83: if (!from)
84: panic("Trying to free up swapper memory space");
85: size = (size + 0x3fffff) >> 22;
86: dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
87: for ( ; size-->0 ; dir++) {
1.1.1.5 ! root 88: if (!(page_dir = *dir))
1.1 root 89: continue;
90: *dir = 0;
1.1.1.5 ! root 91: if (!(page_dir & 1)) {
! 92: printk("free_page_tables: bad page directory.");
! 93: continue;
! 94: }
! 95: pg_table = (unsigned long *) (0xfffff000 & page_dir);
! 96: for (nr=0 ; nr<1024 ; nr++,pg_table++) {
! 97: if (!(page = *pg_table))
! 98: continue;
! 99: *pg_table = 0;
! 100: if (1 & page)
! 101: free_page(0xfffff000 & page);
! 102: else
! 103: swap_free(page >> 1);
! 104: }
! 105: free_page(0xfffff000 & page_dir);
1.1 root 106: }
107: invalidate();
1.1.1.5 ! root 108: for (page = 0; page < CHECK_LAST_NR ; page++)
! 109: last_pages[page] = 0;
1.1 root 110: return 0;
111: }
112:
113: /*
114: * Well, here is one of the most complicated functions in mm. It
115: * copies a range of linerar addresses by copying only the pages.
116: * Let's hope this is bug-free, 'cause this one I don't want to debug :-)
117: *
118: * Note! We don't copy just any chunks of memory - addresses have to
119: * be divisible by 4Mb (one page-directory entry), as this makes the
120: * function easier. It's used only by fork anyway.
121: *
122: * NOTE 2!! When from==0 we are copying kernel space for the first
123: * fork(). Then we DONT want to copy a full page-directory entry, as
124: * that would lead to some serious memory waste - we just copy the
125: * first 160 pages - 640kB. Even that is more than we need, but it
126: * doesn't take any more memory - we don't copy-on-write in the low
127: * 1 Mb-range, so the pages can be shared with the kernel. Thus the
128: * special case for nr=xxxx.
129: */
130: int copy_page_tables(unsigned long from,unsigned long to,long size)
131: {
132: unsigned long * from_page_table;
133: unsigned long * to_page_table;
134: unsigned long this_page;
135: unsigned long * from_dir, * to_dir;
1.1.1.4 root 136: unsigned long new_page;
1.1 root 137: unsigned long nr;
138:
139: if ((from&0x3fffff) || (to&0x3fffff))
140: panic("copy_page_tables called with wrong alignment");
141: from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
142: to_dir = (unsigned long *) ((to>>20) & 0xffc);
143: size = ((unsigned) (size+0x3fffff)) >> 22;
144: for( ; size-->0 ; from_dir++,to_dir++) {
1.1.1.5 ! root 145: if (*to_dir)
! 146: printk("copy_page_tables: already exist, "
! 147: "probable memory corruption\n");
! 148: if (!*from_dir)
! 149: continue;
! 150: if (!(1 & *from_dir)) {
! 151: printk("copy_page_tables: page table swapped out, "
! 152: "probable memory corruption");
! 153: *from_dir = 0;
1.1 root 154: continue;
1.1.1.5 ! root 155: }
1.1 root 156: from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
157: if (!(to_page_table = (unsigned long *) get_free_page()))
158: return -1; /* Out of memory, see freeing */
159: *to_dir = ((unsigned long) to_page_table) | 7;
160: nr = (from==0)?0xA0:1024;
161: for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
162: this_page = *from_page_table;
1.1.1.4 root 163: if (!this_page)
1.1 root 164: continue;
1.1.1.4 root 165: if (!(1 & this_page)) {
166: if (!(new_page = get_free_page()))
167: return -1;
168: read_swap_page(this_page>>1, (char *) new_page);
169: *to_page_table = this_page;
170: *from_page_table = new_page | (PAGE_DIRTY | 7);
171: continue;
172: }
1.1 root 173: this_page &= ~2;
174: *to_page_table = this_page;
175: if (this_page > LOW_MEM) {
176: *from_page_table = this_page;
177: this_page -= LOW_MEM;
178: this_page >>= 12;
179: mem_map[this_page]++;
180: }
181: }
182: }
183: invalidate();
184: return 0;
185: }
186:
187: /*
188: * This function puts a page in memory at the wanted address.
189: * It returns the physical address of the page gotten, 0 if
190: * out of memory (either when trying to access page-table or
191: * page.)
192: */
1.1.1.4 root 193: static unsigned long put_page(unsigned long page,unsigned long address)
1.1 root 194: {
195: unsigned long tmp, *page_table;
196:
197: /* NOTE !!! This uses the fact that _pg_dir=0 */
198:
1.1.1.5 ! root 199: if (page < LOW_MEM || page >= HIGH_MEMORY) {
! 200: printk("put_page: trying to put page %p at %p\n",page,address);
! 201: return 0;
! 202: }
! 203: if (mem_map[(page-LOW_MEM)>>12] != 1) {
1.1 root 204: printk("mem_map disagrees with %p at %p\n",page,address);
1.1.1.5 ! root 205: return 0;
! 206: }
1.1 root 207: page_table = (unsigned long *) ((address>>20) & 0xffc);
208: if ((*page_table)&1)
209: page_table = (unsigned long *) (0xfffff000 & *page_table);
210: else {
211: if (!(tmp=get_free_page()))
212: return 0;
1.1.1.4 root 213: *page_table = tmp | 7;
1.1 root 214: page_table = (unsigned long *) tmp;
215: }
216: page_table[(address>>12) & 0x3ff] = page | 7;
1.1.1.3 root 217: /* no need for invalidate */
1.1 root 218: return page;
219: }
220:
1.1.1.4 root 221: /*
222: * The previous function doesn't work very well if you also want to mark
223: * the page dirty: exec.c wants this, as it has earlier changed the page,
224: * and we want the dirty-status to be correct (for VM). Thus the same
225: * routine, but this time we mark it dirty too.
226: */
227: unsigned long put_dirty_page(unsigned long page, unsigned long address)
228: {
229: unsigned long tmp, *page_table;
230:
231: /* NOTE !!! This uses the fact that _pg_dir=0 */
232:
233: if (page < LOW_MEM || page >= HIGH_MEMORY)
1.1.1.5 ! root 234: printk("put_dirty_page: trying to put page %p at %p\n",page,address);
1.1.1.4 root 235: if (mem_map[(page-LOW_MEM)>>12] != 1)
236: printk("mem_map disagrees with %p at %p\n",page,address);
237: page_table = (unsigned long *) ((address>>20) & 0xffc);
238: if ((*page_table)&1)
239: page_table = (unsigned long *) (0xfffff000 & *page_table);
240: else {
241: if (!(tmp=get_free_page()))
242: return 0;
243: *page_table = tmp|7;
244: page_table = (unsigned long *) tmp;
245: }
246: page_table[(address>>12) & 0x3ff] = page | (PAGE_DIRTY | 7);
247: /* no need for invalidate */
248: return page;
249: }
250:
1.1 root 251: void un_wp_page(unsigned long * table_entry)
252: {
1.1.1.5 ! root 253: unsigned long old_page;
! 254: unsigned long new_page = 0;
! 255: unsigned long dirty;
! 256:
! 257: repeat:
! 258: old_page = *table_entry;
! 259: dirty = old_page & PAGE_DIRTY;
! 260: if (!(old_page & 1)) {
! 261: if (new_page)
! 262: free_page(new_page);
! 263: return;
! 264: }
! 265: old_page &= 0xfffff000;
! 266: if (old_page >= HIGH_MEMORY) {
! 267: if (new_page)
! 268: free_page(new_page);
! 269: printk("bad page address\n\r");
! 270: do_exit(SIGSEGV);
! 271: }
1.1 root 272: if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
273: *table_entry |= 2;
1.1.1.3 root 274: invalidate();
1.1.1.5 ! root 275: if (new_page)
! 276: free_page(new_page);
1.1 root 277: return;
278: }
1.1.1.5 ! root 279: if (!new_page) {
! 280: if (!(new_page=get_free_page()))
! 281: oom();
! 282: goto repeat;
! 283: }
1.1.1.4 root 284: copy_page(old_page,new_page);
1.1.1.5 ! root 285: *table_entry = new_page | dirty | 7;
! 286: free_page(old_page);
1.1.1.3 root 287: invalidate();
1.1 root 288: }
289:
290: /*
291: * This routine handles present pages, when users try to write
292: * to a shared page. It is done by copying the page to a new address
293: * and decrementing the shared-page counter for the old page.
1.1.1.3 root 294: *
295: * If it's in code space we exit with a segment error.
1.1 root 296: */
297: void do_wp_page(unsigned long error_code,unsigned long address)
298: {
1.1.1.4 root 299: if (address < TASK_SIZE)
300: printk("\n\rBAD! KERNEL MEMORY WP-ERR!\n\r");
1.1.1.5 ! root 301: if (address - current->start_code >= TASK_SIZE) {
1.1.1.4 root 302: printk("Bad things happen: page error in do_wp_page\n\r");
303: do_exit(SIGSEGV);
304: }
1.1 root 305: un_wp_page((unsigned long *)
306: (((address>>10) & 0xffc) + (0xfffff000 &
307: *((unsigned long *) ((address>>20) &0xffc)))));
308:
309: }
310:
311: void write_verify(unsigned long address)
312: {
313: unsigned long page;
314:
315: if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
316: return;
317: page &= 0xfffff000;
318: page += ((address>>10) & 0xffc);
319: if ((3 & *(unsigned long *) page) == 1) /* non-writeable, present */
320: un_wp_page((unsigned long *) page);
321: return;
322: }
323:
1.1.1.3 root 324: void get_empty_page(unsigned long address)
325: {
326: unsigned long tmp;
327:
328: if (!(tmp=get_free_page()) || !put_page(tmp,address)) {
329: free_page(tmp); /* 0 is ok - ignored */
330: oom();
331: }
332: }
333:
334: /*
335: * try_to_share() checks the page at address "address" in the task "p",
336: * to see if it exists, and if it is clean. If so, share it with the current
337: * task.
338: *
339: * NOTE! This assumes we have checked that p != current, and that they
1.1.1.4 root 340: * share the same executable or library.
1.1.1.3 root 341: */
342: static int try_to_share(unsigned long address, struct task_struct * p)
343: {
344: unsigned long from;
345: unsigned long to;
346: unsigned long from_page;
347: unsigned long to_page;
348: unsigned long phys_addr;
349:
350: from_page = to_page = ((address>>20) & 0xffc);
351: from_page += ((p->start_code>>20) & 0xffc);
352: to_page += ((current->start_code>>20) & 0xffc);
353: /* is there a page-directory at from? */
354: from = *(unsigned long *) from_page;
355: if (!(from & 1))
356: return 0;
357: from &= 0xfffff000;
358: from_page = from + ((address>>10) & 0xffc);
359: phys_addr = *(unsigned long *) from_page;
360: /* is the page clean and present? */
361: if ((phys_addr & 0x41) != 0x01)
362: return 0;
363: phys_addr &= 0xfffff000;
364: if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM)
365: return 0;
366: to = *(unsigned long *) to_page;
1.1.1.5 ! root 367: if (!(to & 1)) {
1.1.1.3 root 368: if (to = get_free_page())
369: *(unsigned long *) to_page = to | 7;
370: else
371: oom();
1.1.1.5 ! root 372: }
1.1.1.3 root 373: to &= 0xfffff000;
374: to_page = to + ((address>>10) & 0xffc);
375: if (1 & *(unsigned long *) to_page)
376: panic("try_to_share: to_page already exists");
377: /* share them: write-protect */
378: *(unsigned long *) from_page &= ~2;
379: *(unsigned long *) to_page = *(unsigned long *) from_page;
380: invalidate();
381: phys_addr -= LOW_MEM;
382: phys_addr >>= 12;
383: mem_map[phys_addr]++;
384: return 1;
385: }
386:
387: /*
388: * share_page() tries to find a process that could share a page with
389: * the current one. Address is the address of the wanted page relative
390: * to the current data space.
391: *
392: * We first check if it is at all feasible by checking executable->i_count.
393: * It should be >1 if there are other tasks sharing this inode.
394: */
1.1.1.5 ! root 395: static int share_page(struct inode * inode, unsigned long address)
1.1.1.3 root 396: {
397: struct task_struct ** p;
398:
1.1.1.4 root 399: if (inode->i_count < 2 || !inode)
1.1.1.3 root 400: return 0;
401: for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
402: if (!*p)
403: continue;
404: if (current == *p)
405: continue;
1.1.1.4 root 406: if (address < LIBRARY_OFFSET) {
407: if (inode != (*p)->executable)
408: continue;
409: } else {
410: if (inode != (*p)->library)
411: continue;
412: }
1.1.1.3 root 413: if (try_to_share(address,*p))
414: return 1;
415: }
416: return 0;
417: }
418:
1.1.1.5 ! root 419: void do_no_page(unsigned long error_code,
! 420: unsigned long address, struct task_struct *tsk)
1.1 root 421: {
1.1.1.5 ! root 422: static unsigned int last_checked = 0;
1.1.1.3 root 423: int nr[4];
1.1 root 424: unsigned long tmp;
1.1.1.3 root 425: unsigned long page;
426: int block,i;
1.1.1.5 ! root 427: struct inode * inode;
1.1 root 428:
1.1.1.5 ! root 429: /* Trashing ? Make it interruptible, but don't penalize otherwise */
! 430: for (i = 0; i < CHECK_LAST_NR; i++)
! 431: if ((address & 0xfffff000) == last_pages[i]) {
! 432: current->counter = 0;
! 433: schedule();
! 434: }
! 435: last_checked++;
! 436: if (last_checked >= CHECK_LAST_NR)
! 437: last_checked = 0;
! 438: last_pages[last_checked] = address & 0xfffff000;
1.1.1.4 root 439: if (address < TASK_SIZE)
440: printk("\n\rBAD!! KERNEL PAGE MISSING\n\r");
1.1.1.5 ! root 441: if (address - tsk->start_code >= TASK_SIZE) {
1.1.1.4 root 442: printk("Bad things happen: nonexistent page error in do_no_page\n\r");
443: do_exit(SIGSEGV);
444: }
445: page = *(unsigned long *) ((address >> 20) & 0xffc);
1.1.1.5 ! root 446: /* check the page directory: make a page dir entry if no such exists */
1.1.1.4 root 447: if (page & 1) {
448: page &= 0xfffff000;
449: page += (address >> 10) & 0xffc;
450: tmp = *(unsigned long *) page;
451: if (tmp && !(1 & tmp)) {
452: swap_in((unsigned long *) page);
453: return;
454: }
1.1.1.5 ! root 455: } else {
! 456: if (page)
! 457: printk("do_no_page: bad page directory\n");
! 458: if (!(page = get_free_page()))
! 459: oom();
! 460: page |= 7;
! 461: *(unsigned long *) ((address >> 20) & 0xffc) = page;
1.1.1.4 root 462: }
1.1.1.3 root 463: address &= 0xfffff000;
1.1.1.5 ! root 464: tmp = address - tsk->start_code;
1.1.1.4 root 465: if (tmp >= LIBRARY_OFFSET ) {
1.1.1.5 ! root 466: inode = tsk->library;
1.1.1.4 root 467: block = 1 + (tmp-LIBRARY_OFFSET) / BLOCK_SIZE;
1.1.1.5 ! root 468: } else if (tmp < tsk->end_data) {
! 469: inode = tsk->executable;
1.1.1.4 root 470: block = 1 + tmp / BLOCK_SIZE;
471: } else {
472: inode = NULL;
473: block = 0;
474: }
475: if (!inode) {
1.1.1.3 root 476: get_empty_page(address);
477: return;
478: }
1.1.1.5 ! root 479: if (tsk == current)
1.1.1.4 root 480: if (share_page(inode,tmp))
1.1.1.3 root 481: return;
482: if (!(page = get_free_page()))
483: oom();
484: /* remember that 1 block is used for header */
485: for (i=0 ; i<4 ; block++,i++)
1.1.1.4 root 486: nr[i] = bmap(inode,block);
487: bread_page(page,inode->i_dev,nr);
1.1.1.5 ! root 488: i = tmp + 4096 - tsk->end_data;
1.1.1.4 root 489: if (i>4095)
490: i = 0;
1.1.1.3 root 491: tmp = page + 4096;
492: while (i-- > 0) {
493: tmp--;
494: *(char *)tmp = 0;
495: }
496: if (put_page(page,address))
497: return;
498: free_page(page);
499: oom();
1.1 root 500: }
501:
1.1.1.2 root 502: void mem_init(long start_mem, long end_mem)
503: {
504: int i;
505:
1.1.1.5 ! root 506: swap_device = 0;
! 507: swap_file = NULL;
1.1.1.2 root 508: HIGH_MEMORY = end_mem;
509: for (i=0 ; i<PAGING_PAGES ; i++)
510: mem_map[i] = USED;
511: i = MAP_NR(start_mem);
512: end_mem -= start_mem;
513: end_mem >>= 12;
514: while (end_mem-->0)
515: mem_map[i++]=0;
516: }
517:
1.1.1.4 root 518: void show_mem(void)
1.1 root 519: {
1.1.1.4 root 520: int i,j,k,free=0,total=0;
521: int shared=0;
522: unsigned long * pg_tbl;
523:
524: printk("Mem-info:\n\r");
525: for(i=0 ; i<PAGING_PAGES ; i++) {
526: if (mem_map[i] == USED)
527: continue;
528: total++;
529: if (!mem_map[i])
530: free++;
531: else
532: shared += mem_map[i]-1;
533: }
534: printk("%d free pages of %d\n\r",free,total);
535: printk("%d pages shared\n\r",shared);
536: k = 0;
537: for(i=4 ; i<1024 ;) {
1.1 root 538: if (1&pg_dir[i]) {
1.1.1.4 root 539: if (pg_dir[i]>HIGH_MEMORY) {
540: printk("page directory[%d]: %08X\n\r",
541: i,pg_dir[i]);
1.1.1.5 ! root 542: i++;
1.1.1.4 root 543: continue;
544: }
545: if (pg_dir[i]>LOW_MEM)
546: free++,k++;
547: pg_tbl=(unsigned long *) (0xfffff000 & pg_dir[i]);
548: for(j=0 ; j<1024 ; j++)
549: if ((pg_tbl[j]&1) && pg_tbl[j]>LOW_MEM)
550: if (pg_tbl[j]>HIGH_MEMORY)
551: printk("page_dir[%d][%d]: %08X\n\r",
552: i,j, pg_tbl[j]);
553: else
554: k++,free++;
555: }
556: i++;
557: if (!(i&15) && k) {
558: k++,free++; /* one page/process for task_struct */
559: printk("Process %d: %d pages\n\r",(i>>4)-1,k);
560: k = 0;
1.1 root 561: }
562: }
1.1.1.4 root 563: printk("Memory found: %d (%d)\n\r",free-shared,total);
1.1 root 564: }
1.1.1.5 ! root 565:
! 566:
! 567: /* This routine handles page faults. It determines the address,
! 568: and the problem then passes it off to one of the appropriate
! 569: routines. */
! 570: void do_page_fault (unsigned long *esp, unsigned long error_code)
! 571: {
! 572: unsigned long address;
! 573: /* get the address */
! 574:
! 575: __asm__ ("movl %%cr2,%0":"=r" (address));
! 576: if (!(error_code & 1)) {
! 577: do_no_page(error_code, address, current);
! 578: return;
! 579: } else {
! 580: do_wp_page(error_code, address);
! 581: return;
! 582: }
! 583: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.