Annotation of linux/mm/memory.c, revision 1.1.1.4

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.