|
|
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.4 ! root 47: unsigned char mem_map [ PAGING_PAGES ] = {0,};
1.1 root 48:
49: /*
50: * Free a page of memory at physical address 'addr'. Used by
51: * 'free_page_tables()'
52: */
53: void free_page(unsigned long addr)
54: {
1.1.1.2 root 55: if (addr < LOW_MEM) return;
1.1.1.3 root 56: if (addr >= HIGH_MEMORY)
1.1 root 57: panic("trying to free nonexistent page");
58: addr -= LOW_MEM;
59: addr >>= 12;
60: if (mem_map[addr]--) return;
61: mem_map[addr]=0;
62: panic("trying to free free page");
63: }
64:
65: /*
66: * This function frees a continuos block of page tables, as needed
67: * by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks.
68: */
69: int free_page_tables(unsigned long from,unsigned long size)
70: {
71: unsigned long *pg_table;
72: unsigned long * dir, nr;
73:
74: if (from & 0x3fffff)
75: panic("free_page_tables called with wrong alignment");
76: if (!from)
77: panic("Trying to free up swapper memory space");
78: size = (size + 0x3fffff) >> 22;
79: dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
80: for ( ; size-->0 ; dir++) {
81: if (!(1 & *dir))
82: continue;
83: pg_table = (unsigned long *) (0xfffff000 & *dir);
84: for (nr=0 ; nr<1024 ; nr++) {
1.1.1.4 ! root 85: if (*pg_table) {
! 86: if (1 & *pg_table)
! 87: free_page(0xfffff000 & *pg_table);
! 88: else
! 89: swap_free(*pg_table >> 1);
! 90: *pg_table = 0;
! 91: }
1.1 root 92: pg_table++;
93: }
94: free_page(0xfffff000 & *dir);
95: *dir = 0;
96: }
97: invalidate();
98: return 0;
99: }
100:
101: /*
102: * Well, here is one of the most complicated functions in mm. It
103: * copies a range of linerar addresses by copying only the pages.
104: * Let's hope this is bug-free, 'cause this one I don't want to debug :-)
105: *
106: * Note! We don't copy just any chunks of memory - addresses have to
107: * be divisible by 4Mb (one page-directory entry), as this makes the
108: * function easier. It's used only by fork anyway.
109: *
110: * NOTE 2!! When from==0 we are copying kernel space for the first
111: * fork(). Then we DONT want to copy a full page-directory entry, as
112: * that would lead to some serious memory waste - we just copy the
113: * first 160 pages - 640kB. Even that is more than we need, but it
114: * doesn't take any more memory - we don't copy-on-write in the low
115: * 1 Mb-range, so the pages can be shared with the kernel. Thus the
116: * special case for nr=xxxx.
117: */
118: int copy_page_tables(unsigned long from,unsigned long to,long size)
119: {
120: unsigned long * from_page_table;
121: unsigned long * to_page_table;
122: unsigned long this_page;
123: unsigned long * from_dir, * to_dir;
1.1.1.4 ! root 124: unsigned long new_page;
1.1 root 125: unsigned long nr;
126:
127: if ((from&0x3fffff) || (to&0x3fffff))
128: panic("copy_page_tables called with wrong alignment");
129: from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
130: to_dir = (unsigned long *) ((to>>20) & 0xffc);
131: size = ((unsigned) (size+0x3fffff)) >> 22;
132: for( ; size-->0 ; from_dir++,to_dir++) {
133: if (1 & *to_dir)
134: panic("copy_page_tables: already exist");
135: if (!(1 & *from_dir))
136: continue;
137: from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
138: if (!(to_page_table = (unsigned long *) get_free_page()))
139: return -1; /* Out of memory, see freeing */
140: *to_dir = ((unsigned long) to_page_table) | 7;
141: nr = (from==0)?0xA0:1024;
142: for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
143: this_page = *from_page_table;
1.1.1.4 ! root 144: if (!this_page)
1.1 root 145: continue;
1.1.1.4 ! root 146: if (!(1 & this_page)) {
! 147: if (!(new_page = get_free_page()))
! 148: return -1;
! 149: read_swap_page(this_page>>1, (char *) new_page);
! 150: *to_page_table = this_page;
! 151: *from_page_table = new_page | (PAGE_DIRTY | 7);
! 152: continue;
! 153: }
1.1 root 154: this_page &= ~2;
155: *to_page_table = this_page;
156: if (this_page > LOW_MEM) {
157: *from_page_table = this_page;
158: this_page -= LOW_MEM;
159: this_page >>= 12;
160: mem_map[this_page]++;
161: }
162: }
163: }
164: invalidate();
165: return 0;
166: }
167:
168: /*
169: * This function puts a page in memory at the wanted address.
170: * It returns the physical address of the page gotten, 0 if
171: * out of memory (either when trying to access page-table or
172: * page.)
173: */
1.1.1.4 ! root 174: static unsigned long put_page(unsigned long page,unsigned long address)
1.1 root 175: {
176: unsigned long tmp, *page_table;
177:
178: /* NOTE !!! This uses the fact that _pg_dir=0 */
179:
1.1.1.3 root 180: if (page < LOW_MEM || page >= HIGH_MEMORY)
1.1 root 181: printk("Trying to put page %p at %p\n",page,address);
182: if (mem_map[(page-LOW_MEM)>>12] != 1)
183: printk("mem_map disagrees with %p at %p\n",page,address);
184: page_table = (unsigned long *) ((address>>20) & 0xffc);
185: if ((*page_table)&1)
186: page_table = (unsigned long *) (0xfffff000 & *page_table);
187: else {
188: if (!(tmp=get_free_page()))
189: return 0;
1.1.1.4 ! root 190: *page_table = tmp | 7;
1.1 root 191: page_table = (unsigned long *) tmp;
192: }
193: page_table[(address>>12) & 0x3ff] = page | 7;
1.1.1.3 root 194: /* no need for invalidate */
1.1 root 195: return page;
196: }
197:
1.1.1.4 ! root 198: /*
! 199: * The previous function doesn't work very well if you also want to mark
! 200: * the page dirty: exec.c wants this, as it has earlier changed the page,
! 201: * and we want the dirty-status to be correct (for VM). Thus the same
! 202: * routine, but this time we mark it dirty too.
! 203: */
! 204: unsigned long put_dirty_page(unsigned long page, unsigned long address)
! 205: {
! 206: unsigned long tmp, *page_table;
! 207:
! 208: /* NOTE !!! This uses the fact that _pg_dir=0 */
! 209:
! 210: if (page < LOW_MEM || page >= HIGH_MEMORY)
! 211: printk("Trying to put page %p at %p\n",page,address);
! 212: if (mem_map[(page-LOW_MEM)>>12] != 1)
! 213: printk("mem_map disagrees with %p at %p\n",page,address);
! 214: page_table = (unsigned long *) ((address>>20) & 0xffc);
! 215: if ((*page_table)&1)
! 216: page_table = (unsigned long *) (0xfffff000 & *page_table);
! 217: else {
! 218: if (!(tmp=get_free_page()))
! 219: return 0;
! 220: *page_table = tmp|7;
! 221: page_table = (unsigned long *) tmp;
! 222: }
! 223: page_table[(address>>12) & 0x3ff] = page | (PAGE_DIRTY | 7);
! 224: /* no need for invalidate */
! 225: return page;
! 226: }
! 227:
1.1 root 228: void un_wp_page(unsigned long * table_entry)
229: {
230: unsigned long old_page,new_page;
231:
232: old_page = 0xfffff000 & *table_entry;
233: if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
234: *table_entry |= 2;
1.1.1.3 root 235: invalidate();
1.1 root 236: return;
237: }
238: if (!(new_page=get_free_page()))
1.1.1.3 root 239: oom();
1.1 root 240: if (old_page >= LOW_MEM)
241: mem_map[MAP_NR(old_page)]--;
1.1.1.4 ! root 242: copy_page(old_page,new_page);
1.1 root 243: *table_entry = new_page | 7;
1.1.1.3 root 244: invalidate();
1.1 root 245: }
246:
247: /*
248: * This routine handles present pages, when users try to write
249: * to a shared page. It is done by copying the page to a new address
250: * and decrementing the shared-page counter for the old page.
1.1.1.3 root 251: *
252: * If it's in code space we exit with a segment error.
1.1 root 253: */
254: void do_wp_page(unsigned long error_code,unsigned long address)
255: {
1.1.1.4 ! root 256: if (address < TASK_SIZE)
! 257: printk("\n\rBAD! KERNEL MEMORY WP-ERR!\n\r");
! 258: if (address - current->start_code > TASK_SIZE) {
! 259: printk("Bad things happen: page error in do_wp_page\n\r");
! 260: do_exit(SIGSEGV);
! 261: }
1.1.1.3 root 262: #if 0
263: /* we cannot do this yet: the estdio library writes to code space */
264: /* stupid, stupid. I really want the libc.a from GNU */
265: if (CODE_SPACE(address))
266: do_exit(SIGSEGV);
267: #endif
1.1 root 268: un_wp_page((unsigned long *)
269: (((address>>10) & 0xffc) + (0xfffff000 &
270: *((unsigned long *) ((address>>20) &0xffc)))));
271:
272: }
273:
274: void write_verify(unsigned long address)
275: {
276: unsigned long page;
277:
278: if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
279: return;
280: page &= 0xfffff000;
281: page += ((address>>10) & 0xffc);
282: if ((3 & *(unsigned long *) page) == 1) /* non-writeable, present */
283: un_wp_page((unsigned long *) page);
284: return;
285: }
286:
1.1.1.3 root 287: void get_empty_page(unsigned long address)
288: {
289: unsigned long tmp;
290:
291: if (!(tmp=get_free_page()) || !put_page(tmp,address)) {
292: free_page(tmp); /* 0 is ok - ignored */
293: oom();
294: }
295: }
296:
297: /*
298: * try_to_share() checks the page at address "address" in the task "p",
299: * to see if it exists, and if it is clean. If so, share it with the current
300: * task.
301: *
302: * NOTE! This assumes we have checked that p != current, and that they
1.1.1.4 ! root 303: * share the same executable or library.
1.1.1.3 root 304: */
305: static int try_to_share(unsigned long address, struct task_struct * p)
306: {
307: unsigned long from;
308: unsigned long to;
309: unsigned long from_page;
310: unsigned long to_page;
311: unsigned long phys_addr;
312:
313: from_page = to_page = ((address>>20) & 0xffc);
314: from_page += ((p->start_code>>20) & 0xffc);
315: to_page += ((current->start_code>>20) & 0xffc);
316: /* is there a page-directory at from? */
317: from = *(unsigned long *) from_page;
318: if (!(from & 1))
319: return 0;
320: from &= 0xfffff000;
321: from_page = from + ((address>>10) & 0xffc);
322: phys_addr = *(unsigned long *) from_page;
323: /* is the page clean and present? */
324: if ((phys_addr & 0x41) != 0x01)
325: return 0;
326: phys_addr &= 0xfffff000;
327: if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM)
328: return 0;
329: to = *(unsigned long *) to_page;
330: if (!(to & 1))
331: if (to = get_free_page())
332: *(unsigned long *) to_page = to | 7;
333: else
334: oom();
335: to &= 0xfffff000;
336: to_page = to + ((address>>10) & 0xffc);
337: if (1 & *(unsigned long *) to_page)
338: panic("try_to_share: to_page already exists");
339: /* share them: write-protect */
340: *(unsigned long *) from_page &= ~2;
341: *(unsigned long *) to_page = *(unsigned long *) from_page;
342: invalidate();
343: phys_addr -= LOW_MEM;
344: phys_addr >>= 12;
345: mem_map[phys_addr]++;
346: return 1;
347: }
348:
349: /*
350: * share_page() tries to find a process that could share a page with
351: * the current one. Address is the address of the wanted page relative
352: * to the current data space.
353: *
354: * We first check if it is at all feasible by checking executable->i_count.
355: * It should be >1 if there are other tasks sharing this inode.
356: */
1.1.1.4 ! root 357: static int share_page(struct m_inode * inode, unsigned long address)
1.1.1.3 root 358: {
359: struct task_struct ** p;
360:
1.1.1.4 ! root 361: if (inode->i_count < 2 || !inode)
1.1.1.3 root 362: return 0;
363: for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
364: if (!*p)
365: continue;
366: if (current == *p)
367: continue;
1.1.1.4 ! root 368: if (address < LIBRARY_OFFSET) {
! 369: if (inode != (*p)->executable)
! 370: continue;
! 371: } else {
! 372: if (inode != (*p)->library)
! 373: continue;
! 374: }
1.1.1.3 root 375: if (try_to_share(address,*p))
376: return 1;
377: }
378: return 0;
379: }
380:
1.1 root 381: void do_no_page(unsigned long error_code,unsigned long address)
382: {
1.1.1.3 root 383: int nr[4];
1.1 root 384: unsigned long tmp;
1.1.1.3 root 385: unsigned long page;
386: int block,i;
1.1.1.4 ! root 387: struct m_inode * inode;
1.1 root 388:
1.1.1.4 ! root 389: if (address < TASK_SIZE)
! 390: printk("\n\rBAD!! KERNEL PAGE MISSING\n\r");
! 391: if (address - current->start_code > TASK_SIZE) {
! 392: printk("Bad things happen: nonexistent page error in do_no_page\n\r");
! 393: do_exit(SIGSEGV);
! 394: }
! 395: page = *(unsigned long *) ((address >> 20) & 0xffc);
! 396: if (page & 1) {
! 397: page &= 0xfffff000;
! 398: page += (address >> 10) & 0xffc;
! 399: tmp = *(unsigned long *) page;
! 400: if (tmp && !(1 & tmp)) {
! 401: swap_in((unsigned long *) page);
! 402: return;
! 403: }
! 404: }
1.1.1.3 root 405: address &= 0xfffff000;
406: tmp = address - current->start_code;
1.1.1.4 ! root 407: if (tmp >= LIBRARY_OFFSET ) {
! 408: inode = current->library;
! 409: block = 1 + (tmp-LIBRARY_OFFSET) / BLOCK_SIZE;
! 410: } else if (tmp < current->end_data) {
! 411: inode = current->executable;
! 412: block = 1 + tmp / BLOCK_SIZE;
! 413: } else {
! 414: inode = NULL;
! 415: block = 0;
! 416: }
! 417: if (!inode) {
1.1.1.3 root 418: get_empty_page(address);
419: return;
420: }
1.1.1.4 ! root 421: if (share_page(inode,tmp))
1.1.1.3 root 422: return;
423: if (!(page = get_free_page()))
424: oom();
425: /* remember that 1 block is used for header */
426: for (i=0 ; i<4 ; block++,i++)
1.1.1.4 ! root 427: nr[i] = bmap(inode,block);
! 428: bread_page(page,inode->i_dev,nr);
1.1.1.3 root 429: i = tmp + 4096 - current->end_data;
1.1.1.4 ! root 430: if (i>4095)
! 431: i = 0;
1.1.1.3 root 432: tmp = page + 4096;
433: while (i-- > 0) {
434: tmp--;
435: *(char *)tmp = 0;
436: }
437: if (put_page(page,address))
438: return;
439: free_page(page);
440: oom();
1.1 root 441: }
442:
1.1.1.2 root 443: void mem_init(long start_mem, long end_mem)
444: {
445: int i;
446:
447: HIGH_MEMORY = end_mem;
448: for (i=0 ; i<PAGING_PAGES ; i++)
449: mem_map[i] = USED;
450: i = MAP_NR(start_mem);
451: end_mem -= start_mem;
452: end_mem >>= 12;
453: while (end_mem-->0)
454: mem_map[i++]=0;
455: }
456:
1.1.1.4 ! root 457: void show_mem(void)
1.1 root 458: {
1.1.1.4 ! root 459: int i,j,k,free=0,total=0;
! 460: int shared=0;
! 461: unsigned long * pg_tbl;
! 462:
! 463: printk("Mem-info:\n\r");
! 464: for(i=0 ; i<PAGING_PAGES ; i++) {
! 465: if (mem_map[i] == USED)
! 466: continue;
! 467: total++;
! 468: if (!mem_map[i])
! 469: free++;
! 470: else
! 471: shared += mem_map[i]-1;
! 472: }
! 473: printk("%d free pages of %d\n\r",free,total);
! 474: printk("%d pages shared\n\r",shared);
! 475: k = 0;
! 476: for(i=4 ; i<1024 ;) {
1.1 root 477: if (1&pg_dir[i]) {
1.1.1.4 ! root 478: if (pg_dir[i]>HIGH_MEMORY) {
! 479: printk("page directory[%d]: %08X\n\r",
! 480: i,pg_dir[i]);
! 481: continue;
! 482: }
! 483: if (pg_dir[i]>LOW_MEM)
! 484: free++,k++;
! 485: pg_tbl=(unsigned long *) (0xfffff000 & pg_dir[i]);
! 486: for(j=0 ; j<1024 ; j++)
! 487: if ((pg_tbl[j]&1) && pg_tbl[j]>LOW_MEM)
! 488: if (pg_tbl[j]>HIGH_MEMORY)
! 489: printk("page_dir[%d][%d]: %08X\n\r",
! 490: i,j, pg_tbl[j]);
! 491: else
! 492: k++,free++;
! 493: }
! 494: i++;
! 495: if (!(i&15) && k) {
! 496: k++,free++; /* one page/process for task_struct */
! 497: printk("Process %d: %d pages\n\r",(i>>4)-1,k);
! 498: k = 0;
1.1 root 499: }
500: }
1.1.1.4 ! root 501: printk("Memory found: %d (%d)\n\r",free-shared,total);
1.1 root 502: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.