void breading(unsigned long address) { int nr[4]; unsigned long tmp; unsigned long page; int block, i; address &= 0xfffff000; tmp = address - current->start_code; if(!current->executable || tmp >= current->end_data) { get_empty_page(address); return; } if(share_page(tmp)) { return; } if(!(page = get_free_page())) { oom(); } block = 1 + tmp/BLOCK_SIZE; for(i = 0; i < 4; block++, i++) { nr[i] = bmap(current->executable, block); } bread_page(page, current->executable->i_dev, nr); i = tmp + 4096 - current->end_data; tmp = page + 4096; while(i-- > 0) { tmp--; *(char *)tmp = 0; } if(put_page(page, address)) { return ; } free_page(page); oom(); } static int try_to_share(unsigned long address, struct task_struct * p) { unsigned long from; unsigned long to; unsigned long from_page; unsigned long to_page; unsigned long phys_addr; from_page = to_page = ((address>>20) & 0xffc); from_page += ((p->start_code>>20) & 0xffc); to_page += ((current->start_code>>20) & 0xffc); /* is there a page-directory at from? */ from = *(unsigned long *) from_page; if (!(from & 1)) return 0; from &= 0xfffff000; from_page = from + ((address>>10) & 0xffc); phys_addr = *(unsigned long *) from_page; /* is the page clean and present? */ if ((phys_addr & 0x41) != 0x01) return 0; phys_addr &= 0xfffff000; if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM) return 0; to = *(unsigned long *) to_page; if (!(to & 1)) if (to = get_free_page()) *(unsigned long *) to_page = to | 7; else oom(); to &= 0xfffff000; to_page = to + ((address>>10) & 0xffc); if (1 & *(unsigned long *) to_page) panic("try_to_share: to_page already exists"); /* share them: write-protect */ *(unsigned long *) from_page &= ~2; *(unsigned long *) to_page = *(unsigned long *) from_page; invalidate(); phys_addr -= LOW_MEM; phys_addr >>= 12; mem_map[phys_addr]++; return 1; } /* * share_page() tries to find a process that could share a page with * the current one. Address is the address of the wanted page relative * to the current data space. * * We first check if it is at all feasible by checking executable->i_count. * It should be >1 if there are other tasks sharing this inode. */ static int share_page(unsigned long address) { struct task_struct ** p; if (!current->executable) return 0; if (current->executable->i_count < 2) return 0; for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { if (!*p) continue; if (current == *p) continue; if ((*p)->executable != current->executable) continue; if (try_to_share(address,*p)) return 1; } return 0; }