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

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       root       23: #include <signal.h>
                     24: 
1.1.1.3 ! root       25: #include <asm/system.h>
        !            26: 
        !            27: #include <linux/sched.h>
1.1       root       28: #include <linux/head.h>
                     29: #include <linux/kernel.h>
                     30: 
1.1.1.3 ! root       31: volatile void do_exit(long code);
        !            32: 
        !            33: static inline volatile void oom(void)
        !            34: {
        !            35:        printk("out of memory\n\r");
        !            36:        do_exit(SIGSEGV);
        !            37: }
1.1       root       38: 
                     39: #define invalidate() \
                     40: __asm__("movl %%eax,%%cr3"::"a" (0))
                     41: 
1.1.1.2   root       42: /* these are not to be changed without changing head.s etc */
1.1       root       43: #define LOW_MEM 0x100000
1.1.1.2   root       44: #define PAGING_MEMORY (15*1024*1024)
                     45: #define PAGING_PAGES (PAGING_MEMORY>>12)
1.1       root       46: #define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
1.1.1.2   root       47: #define USED 100
1.1       root       48: 
1.1.1.3 ! root       49: #define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
        !            50: current->start_code + current->end_code)
        !            51: 
1.1.1.2   root       52: static long HIGH_MEMORY = 0;
1.1       root       53: 
                     54: #define copy_page(from,to) \
                     55: __asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
                     56: 
1.1.1.2   root       57: static unsigned char mem_map [ PAGING_PAGES ] = {0,};
1.1       root       58: 
                     59: /*
                     60:  * Get physical address of first (actually last :-) free page, and mark it
                     61:  * used. If no free pages left, return 0.
                     62:  */
                     63: unsigned long get_free_page(void)
                     64: {
                     65: register unsigned long __res asm("ax");
                     66: 
1.1.1.2   root       67: __asm__("std ; repne ; scasb\n\t"
1.1       root       68:        "jne 1f\n\t"
1.1.1.2   root       69:        "movb $1,1(%%edi)\n\t"
1.1       root       70:        "sall $12,%%ecx\n\t"
1.1.1.2   root       71:        "addl %2,%%ecx\n\t"
1.1       root       72:        "movl %%ecx,%%edx\n\t"
                     73:        "movl $1024,%%ecx\n\t"
                     74:        "leal 4092(%%edx),%%edi\n\t"
                     75:        "rep ; stosl\n\t"
                     76:        "movl %%edx,%%eax\n"
                     77:        "1:"
                     78:        :"=a" (__res)
                     79:        :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
                     80:        "D" (mem_map+PAGING_PAGES-1)
                     81:        :"di","cx","dx");
                     82: return __res;
                     83: }
                     84: 
                     85: /*
                     86:  * Free a page of memory at physical address 'addr'. Used by
                     87:  * 'free_page_tables()'
                     88:  */
                     89: void free_page(unsigned long addr)
                     90: {
1.1.1.2   root       91:        if (addr < LOW_MEM) return;
1.1.1.3 ! root       92:        if (addr >= HIGH_MEMORY)
1.1       root       93:                panic("trying to free nonexistent page");
                     94:        addr -= LOW_MEM;
                     95:        addr >>= 12;
                     96:        if (mem_map[addr]--) return;
                     97:        mem_map[addr]=0;
                     98:        panic("trying to free free page");
                     99: }
                    100: 
                    101: /*
                    102:  * This function frees a continuos block of page tables, as needed
                    103:  * by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks.
                    104:  */
                    105: int free_page_tables(unsigned long from,unsigned long size)
                    106: {
                    107:        unsigned long *pg_table;
                    108:        unsigned long * dir, nr;
                    109: 
                    110:        if (from & 0x3fffff)
                    111:                panic("free_page_tables called with wrong alignment");
                    112:        if (!from)
                    113:                panic("Trying to free up swapper memory space");
                    114:        size = (size + 0x3fffff) >> 22;
                    115:        dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
                    116:        for ( ; size-->0 ; dir++) {
                    117:                if (!(1 & *dir))
                    118:                        continue;
                    119:                pg_table = (unsigned long *) (0xfffff000 & *dir);
                    120:                for (nr=0 ; nr<1024 ; nr++) {
                    121:                        if (1 & *pg_table)
                    122:                                free_page(0xfffff000 & *pg_table);
                    123:                        *pg_table = 0;
                    124:                        pg_table++;
                    125:                }
                    126:                free_page(0xfffff000 & *dir);
                    127:                *dir = 0;
                    128:        }
                    129:        invalidate();
                    130:        return 0;
                    131: }
                    132: 
                    133: /*
                    134:  *  Well, here is one of the most complicated functions in mm. It
                    135:  * copies a range of linerar addresses by copying only the pages.
                    136:  * Let's hope this is bug-free, 'cause this one I don't want to debug :-)
                    137:  *
                    138:  * Note! We don't copy just any chunks of memory - addresses have to
                    139:  * be divisible by 4Mb (one page-directory entry), as this makes the
                    140:  * function easier. It's used only by fork anyway.
                    141:  *
                    142:  * NOTE 2!! When from==0 we are copying kernel space for the first
                    143:  * fork(). Then we DONT want to copy a full page-directory entry, as
                    144:  * that would lead to some serious memory waste - we just copy the
                    145:  * first 160 pages - 640kB. Even that is more than we need, but it
                    146:  * doesn't take any more memory - we don't copy-on-write in the low
                    147:  * 1 Mb-range, so the pages can be shared with the kernel. Thus the
                    148:  * special case for nr=xxxx.
                    149:  */
                    150: int copy_page_tables(unsigned long from,unsigned long to,long size)
                    151: {
                    152:        unsigned long * from_page_table;
                    153:        unsigned long * to_page_table;
                    154:        unsigned long this_page;
                    155:        unsigned long * from_dir, * to_dir;
                    156:        unsigned long nr;
                    157: 
                    158:        if ((from&0x3fffff) || (to&0x3fffff))
                    159:                panic("copy_page_tables called with wrong alignment");
                    160:        from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
                    161:        to_dir = (unsigned long *) ((to>>20) & 0xffc);
                    162:        size = ((unsigned) (size+0x3fffff)) >> 22;
                    163:        for( ; size-->0 ; from_dir++,to_dir++) {
                    164:                if (1 & *to_dir)
                    165:                        panic("copy_page_tables: already exist");
                    166:                if (!(1 & *from_dir))
                    167:                        continue;
                    168:                from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
                    169:                if (!(to_page_table = (unsigned long *) get_free_page()))
                    170:                        return -1;      /* Out of memory, see freeing */
                    171:                *to_dir = ((unsigned long) to_page_table) | 7;
                    172:                nr = (from==0)?0xA0:1024;
                    173:                for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
                    174:                        this_page = *from_page_table;
                    175:                        if (!(1 & this_page))
                    176:                                continue;
                    177:                        this_page &= ~2;
                    178:                        *to_page_table = this_page;
                    179:                        if (this_page > LOW_MEM) {
                    180:                                *from_page_table = this_page;
                    181:                                this_page -= LOW_MEM;
                    182:                                this_page >>= 12;
                    183:                                mem_map[this_page]++;
                    184:                        }
                    185:                }
                    186:        }
                    187:        invalidate();
                    188:        return 0;
                    189: }
                    190: 
                    191: /*
                    192:  * This function puts a page in memory at the wanted address.
                    193:  * It returns the physical address of the page gotten, 0 if
                    194:  * out of memory (either when trying to access page-table or
                    195:  * page.)
                    196:  */
                    197: unsigned long put_page(unsigned long page,unsigned long address)
                    198: {
                    199:        unsigned long tmp, *page_table;
                    200: 
                    201: /* NOTE !!! This uses the fact that _pg_dir=0 */
                    202: 
1.1.1.3 ! root      203:        if (page < LOW_MEM || page >= HIGH_MEMORY)
1.1       root      204:                printk("Trying to put page %p at %p\n",page,address);
                    205:        if (mem_map[(page-LOW_MEM)>>12] != 1)
                    206:                printk("mem_map disagrees with %p at %p\n",page,address);
                    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;
                    213:                *page_table = tmp|7;
                    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: 
                    221: void un_wp_page(unsigned long * table_entry)
                    222: {
                    223:        unsigned long old_page,new_page;
                    224: 
                    225:        old_page = 0xfffff000 & *table_entry;
                    226:        if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
                    227:                *table_entry |= 2;
1.1.1.3 ! root      228:                invalidate();
1.1       root      229:                return;
                    230:        }
                    231:        if (!(new_page=get_free_page()))
1.1.1.3 ! root      232:                oom();
1.1       root      233:        if (old_page >= LOW_MEM)
                    234:                mem_map[MAP_NR(old_page)]--;
                    235:        *table_entry = new_page | 7;
1.1.1.3 ! root      236:        invalidate();
1.1       root      237:        copy_page(old_page,new_page);
                    238: }      
                    239: 
                    240: /*
                    241:  * This routine handles present pages, when users try to write
                    242:  * to a shared page. It is done by copying the page to a new address
                    243:  * and decrementing the shared-page counter for the old page.
1.1.1.3 ! root      244:  *
        !           245:  * If it's in code space we exit with a segment error.
1.1       root      246:  */
                    247: void do_wp_page(unsigned long error_code,unsigned long address)
                    248: {
1.1.1.3 ! root      249: #if 0
        !           250: /* we cannot do this yet: the estdio library writes to code space */
        !           251: /* stupid, stupid. I really want the libc.a from GNU */
        !           252:        if (CODE_SPACE(address))
        !           253:                do_exit(SIGSEGV);
        !           254: #endif
1.1       root      255:        un_wp_page((unsigned long *)
                    256:                (((address>>10) & 0xffc) + (0xfffff000 &
                    257:                *((unsigned long *) ((address>>20) &0xffc)))));
                    258: 
                    259: }
                    260: 
                    261: void write_verify(unsigned long address)
                    262: {
                    263:        unsigned long page;
                    264: 
                    265:        if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
                    266:                return;
                    267:        page &= 0xfffff000;
                    268:        page += ((address>>10) & 0xffc);
                    269:        if ((3 & *(unsigned long *) page) == 1)  /* non-writeable, present */
                    270:                un_wp_page((unsigned long *) page);
                    271:        return;
                    272: }
                    273: 
1.1.1.3 ! root      274: void get_empty_page(unsigned long address)
        !           275: {
        !           276:        unsigned long tmp;
        !           277: 
        !           278:        if (!(tmp=get_free_page()) || !put_page(tmp,address)) {
        !           279:                free_page(tmp);         /* 0 is ok - ignored */
        !           280:                oom();
        !           281:        }
        !           282: }
        !           283: 
        !           284: /*
        !           285:  * try_to_share() checks the page at address "address" in the task "p",
        !           286:  * to see if it exists, and if it is clean. If so, share it with the current
        !           287:  * task.
        !           288:  *
        !           289:  * NOTE! This assumes we have checked that p != current, and that they
        !           290:  * share the same executable.
        !           291:  */
        !           292: static int try_to_share(unsigned long address, struct task_struct * p)
        !           293: {
        !           294:        unsigned long from;
        !           295:        unsigned long to;
        !           296:        unsigned long from_page;
        !           297:        unsigned long to_page;
        !           298:        unsigned long phys_addr;
        !           299: 
        !           300:        from_page = to_page = ((address>>20) & 0xffc);
        !           301:        from_page += ((p->start_code>>20) & 0xffc);
        !           302:        to_page += ((current->start_code>>20) & 0xffc);
        !           303: /* is there a page-directory at from? */
        !           304:        from = *(unsigned long *) from_page;
        !           305:        if (!(from & 1))
        !           306:                return 0;
        !           307:        from &= 0xfffff000;
        !           308:        from_page = from + ((address>>10) & 0xffc);
        !           309:        phys_addr = *(unsigned long *) from_page;
        !           310: /* is the page clean and present? */
        !           311:        if ((phys_addr & 0x41) != 0x01)
        !           312:                return 0;
        !           313:        phys_addr &= 0xfffff000;
        !           314:        if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM)
        !           315:                return 0;
        !           316:        to = *(unsigned long *) to_page;
        !           317:        if (!(to & 1))
        !           318:                if (to = get_free_page())
        !           319:                        *(unsigned long *) to_page = to | 7;
        !           320:                else
        !           321:                        oom();
        !           322:        to &= 0xfffff000;
        !           323:        to_page = to + ((address>>10) & 0xffc);
        !           324:        if (1 & *(unsigned long *) to_page)
        !           325:                panic("try_to_share: to_page already exists");
        !           326: /* share them: write-protect */
        !           327:        *(unsigned long *) from_page &= ~2;
        !           328:        *(unsigned long *) to_page = *(unsigned long *) from_page;
        !           329:        invalidate();
        !           330:        phys_addr -= LOW_MEM;
        !           331:        phys_addr >>= 12;
        !           332:        mem_map[phys_addr]++;
        !           333:        return 1;
        !           334: }
        !           335: 
        !           336: /*
        !           337:  * share_page() tries to find a process that could share a page with
        !           338:  * the current one. Address is the address of the wanted page relative
        !           339:  * to the current data space.
        !           340:  *
        !           341:  * We first check if it is at all feasible by checking executable->i_count.
        !           342:  * It should be >1 if there are other tasks sharing this inode.
        !           343:  */
        !           344: static int share_page(unsigned long address)
        !           345: {
        !           346:        struct task_struct ** p;
        !           347: 
        !           348:        if (!current->executable)
        !           349:                return 0;
        !           350:        if (current->executable->i_count < 2)
        !           351:                return 0;
        !           352:        for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
        !           353:                if (!*p)
        !           354:                        continue;
        !           355:                if (current == *p)
        !           356:                        continue;
        !           357:                if ((*p)->executable != current->executable)
        !           358:                        continue;
        !           359:                if (try_to_share(address,*p))
        !           360:                        return 1;
        !           361:        }
        !           362:        return 0;
        !           363: }
        !           364: 
1.1       root      365: void do_no_page(unsigned long error_code,unsigned long address)
                    366: {
1.1.1.3 ! root      367:        int nr[4];
1.1       root      368:        unsigned long tmp;
1.1.1.3 ! root      369:        unsigned long page;
        !           370:        int block,i;
1.1       root      371: 
1.1.1.3 ! root      372:        address &= 0xfffff000;
        !           373:        tmp = address - current->start_code;
        !           374:        if (!current->executable || tmp >= current->end_data) {
        !           375:                get_empty_page(address);
        !           376:                return;
        !           377:        }
        !           378:        if (share_page(tmp))
        !           379:                return;
        !           380:        if (!(page = get_free_page()))
        !           381:                oom();
        !           382: /* remember that 1 block is used for header */
        !           383:        block = 1 + tmp/BLOCK_SIZE;
        !           384:        for (i=0 ; i<4 ; block++,i++)
        !           385:                nr[i] = bmap(current->executable,block);
        !           386:        bread_page(page,current->executable->i_dev,nr);
        !           387:        i = tmp + 4096 - current->end_data;
        !           388:        tmp = page + 4096;
        !           389:        while (i-- > 0) {
        !           390:                tmp--;
        !           391:                *(char *)tmp = 0;
        !           392:        }
        !           393:        if (put_page(page,address))
        !           394:                return;
        !           395:        free_page(page);
        !           396:        oom();
1.1       root      397: }
                    398: 
1.1.1.2   root      399: void mem_init(long start_mem, long end_mem)
                    400: {
                    401:        int i;
                    402: 
                    403:        HIGH_MEMORY = end_mem;
                    404:        for (i=0 ; i<PAGING_PAGES ; i++)
                    405:                mem_map[i] = USED;
                    406:        i = MAP_NR(start_mem);
                    407:        end_mem -= start_mem;
                    408:        end_mem >>= 12;
                    409:        while (end_mem-->0)
                    410:                mem_map[i++]=0;
                    411: }
                    412: 
1.1       root      413: void calc_mem(void)
                    414: {
                    415:        int i,j,k,free=0;
                    416:        long * pg_tbl;
                    417: 
                    418:        for(i=0 ; i<PAGING_PAGES ; i++)
                    419:                if (!mem_map[i]) free++;
                    420:        printk("%d pages free (of %d)\n\r",free,PAGING_PAGES);
                    421:        for(i=2 ; i<1024 ; i++) {
                    422:                if (1&pg_dir[i]) {
                    423:                        pg_tbl=(long *) (0xfffff000 & pg_dir[i]);
                    424:                        for(j=k=0 ; j<1024 ; j++)
                    425:                                if (pg_tbl[j]&1)
                    426:                                        k++;
                    427:                        printk("Pg-dir[%d] uses %d pages\n",i,k);
                    428:                }
                    429:        }
                    430: }

unix.superglobalmegacorp.com

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