patch-2.1.79 linux/mm/memory.c
Next file: linux/mm/page_alloc.c
Previous file: linux/mm/filemap.c
Back to the patch index
Back to the overall index
- Lines: 264
- Date:
Mon Jan 12 14:33:20 1998
- Orig file:
v2.1.78/linux/mm/memory.c
- Orig date:
Mon Dec 1 12:04:15 1997
diff -u --recursive --new-file v2.1.78/linux/mm/memory.c linux/mm/memory.c
@@ -175,100 +175,16 @@
return 0;
}
-static inline void copy_one_pte(pte_t * old_pte, pte_t * new_pte, int cow)
-{
- pte_t pte = *old_pte;
- unsigned long page_nr;
-
- if (pte_none(pte))
- return;
- if (!pte_present(pte)) {
- swap_duplicate(pte_val(pte));
- set_pte(new_pte, pte);
- return;
- }
- page_nr = MAP_NR(pte_page(pte));
- if (page_nr >= max_mapnr || PageReserved(mem_map+page_nr)) {
- set_pte(new_pte, pte);
- return;
- }
- if (cow)
- pte = pte_wrprotect(pte);
- if (delete_from_swap_cache(&mem_map[page_nr]))
- pte = pte_mkdirty(pte);
- set_pte(new_pte, pte_mkold(pte));
- set_pte(old_pte, pte);
- atomic_inc(&mem_map[page_nr].count);
-}
-
-static inline int copy_pte_range(pmd_t *dst_pmd, pmd_t *src_pmd, unsigned long address, unsigned long size, int cow)
-{
- pte_t * src_pte, * dst_pte;
- unsigned long end;
-
- if (pmd_none(*src_pmd))
- return 0;
- if (pmd_bad(*src_pmd)) {
- printk("copy_pte_range: bad pmd (%08lx)\n", pmd_val(*src_pmd));
- pmd_clear(src_pmd);
- return 0;
- }
- src_pte = pte_offset(src_pmd, address);
- if (pmd_none(*dst_pmd)) {
- if (!pte_alloc(dst_pmd, 0))
- return -ENOMEM;
- }
- dst_pte = pte_offset(dst_pmd, address);
- address &= ~PMD_MASK;
- end = address + size;
- if (end >= PMD_SIZE)
- end = PMD_SIZE;
- do {
- /* I would like to switch arguments here, to make it
- * consistent with copy_xxx_range and memcpy syntax.
- */
- copy_one_pte(src_pte++, dst_pte++, cow);
- address += PAGE_SIZE;
- } while (address < end);
- return 0;
-}
-
-static inline int copy_pmd_range(pgd_t *dst_pgd, pgd_t *src_pgd, unsigned long address, unsigned long size, int cow)
-{
- pmd_t * src_pmd, * dst_pmd;
- unsigned long end;
- int error = 0;
-
- if (pgd_none(*src_pgd))
- return 0;
- if (pgd_bad(*src_pgd)) {
- printk("copy_pmd_range: bad pgd (%08lx)\n", pgd_val(*src_pgd));
- pgd_clear(src_pgd);
- return 0;
- }
- src_pmd = pmd_offset(src_pgd, address);
- if (pgd_none(*dst_pgd)) {
- if (!pmd_alloc(dst_pgd, 0))
- return -ENOMEM;
- }
- dst_pmd = pmd_offset(dst_pgd, address);
- address &= ~PGDIR_MASK;
- end = address + size;
- if (end > PGDIR_SIZE)
- end = PGDIR_SIZE;
- do {
- error = copy_pte_range(dst_pmd++, src_pmd++, address, end - address, cow);
- if (error)
- break;
- address = (address + PMD_SIZE) & PMD_MASK;
- } while (address < end);
- return error;
-}
+#define PTE_TABLE_MASK ((PTRS_PER_PTE-1) * sizeof(pte_t))
+#define PMD_TABLE_MASK ((PTRS_PER_PMD-1) * sizeof(pmd_t))
/*
* copy one vm_area from one task to the other. Assumes the page tables
* already present in the new task to be cleared in the whole range
* covered by this vma.
+ *
+ * 08Jan98 Merged into one routine from several inline routines to reduce
+ * variable count and make things faster. -jj
*/
int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
struct vm_area_struct *vma)
@@ -276,18 +192,103 @@
pgd_t * src_pgd, * dst_pgd;
unsigned long address = vma->vm_start;
unsigned long end = vma->vm_end;
- int error = 0, cow;
+ unsigned long cow = (vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE;
+
+ src_pgd = pgd_offset(src, address)-1;
+ dst_pgd = pgd_offset(dst, address)-1;
+
+ for (;;) {
+ pmd_t * src_pmd, * dst_pmd;
- cow = (vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE;
- src_pgd = pgd_offset(src, address);
- dst_pgd = pgd_offset(dst, address);
- while (address < end) {
- error = copy_pmd_range(dst_pgd++, src_pgd++, address, end - address, cow);
- if (error)
- break;
- address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ src_pgd++; dst_pgd++;
+
+ /* copy_pmd_range */
+
+ if (pgd_none(*src_pgd))
+ goto skip_copy_pmd_range;
+ if (pgd_bad(*src_pgd)) {
+ printk("copy_pmd_range: bad pgd (%08lx)\n",
+ pgd_val(*src_pgd));
+ pgd_clear(src_pgd);
+skip_copy_pmd_range: address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ if (address >= end)
+ goto out;
+ continue;
+ }
+ if (pgd_none(*dst_pgd)) {
+ if (!pmd_alloc(dst_pgd, 0))
+ goto nomem;
+ }
+
+ src_pmd = pmd_offset(src_pgd, address);
+ dst_pmd = pmd_offset(dst_pgd, address);
+
+ do {
+ pte_t * src_pte, * dst_pte;
+
+ /* copy_pte_range */
+
+ if (pmd_none(*src_pmd))
+ goto skip_copy_pte_range;
+ if (pmd_bad(*src_pmd)) {
+ printk("copy_pte_range: bad pmd (%08lx)\n", pmd_val(*src_pmd));
+ pmd_clear(src_pmd);
+skip_copy_pte_range: address = (address + PMD_SIZE) & PMD_MASK;
+ if (address >= end)
+ goto out;
+ goto cont_copy_pmd_range;
+ }
+ if (pmd_none(*dst_pmd)) {
+ if (!pte_alloc(dst_pmd, 0))
+ goto nomem;
+ }
+
+ src_pte = pte_offset(src_pmd, address);
+ dst_pte = pte_offset(dst_pmd, address);
+
+ do {
+ pte_t pte = *src_pte;
+ unsigned long page_nr;
+
+ /* copy_one_pte */
+
+ if (pte_none(pte))
+ goto cont_copy_pte_range;
+ if (!pte_present(pte)) {
+ swap_duplicate(pte_val(pte));
+ set_pte(dst_pte, pte);
+ goto cont_copy_pte_range;
+ }
+ page_nr = MAP_NR(pte_page(pte));
+ if (page_nr >= max_mapnr ||
+ PageReserved(mem_map+page_nr)) {
+ set_pte(dst_pte, pte);
+ goto cont_copy_pte_range;
+ }
+ if (cow)
+ pte = pte_wrprotect(pte);
+ if (delete_from_swap_cache(&mem_map[page_nr]))
+ pte = pte_mkdirty(pte);
+ set_pte(dst_pte, pte_mkold(pte));
+ set_pte(src_pte, pte);
+ atomic_inc(&mem_map[page_nr].count);
+
+cont_copy_pte_range: address += PAGE_SIZE;
+ if (address >= end)
+ goto out;
+ src_pte++;
+ dst_pte++;
+ } while ((unsigned long)src_pte & PTE_TABLE_MASK);
+
+cont_copy_pmd_range: src_pmd++;
+ dst_pmd++;
+ } while ((unsigned long)src_pmd & PMD_TABLE_MASK);
}
- return error;
+out:
+ return 0;
+
+nomem:
+ return -ENOMEM;
}
/*
@@ -299,7 +300,11 @@
unsigned long addr = pte_page(page);
if (MAP_NR(addr) >= max_mapnr || PageReserved(mem_map+MAP_NR(addr)))
return 0;
- free_page(addr);
+ /*
+ * free_page() used to be able to clear swap cache
+ * entries. We may now have to do it manually.
+ */
+ free_page_and_swap_cache(addr);
return 1;
}
swap_free(pte_val(page));
@@ -542,7 +547,7 @@
static void put_page(pte_t * page_table, pte_t pte)
{
if (!pte_none(*page_table)) {
- free_page(pte_page(pte));
+ free_page_and_swap_cache(pte_page(pte));
return;
}
/* no need for flush_tlb */
@@ -609,7 +614,8 @@
{
pte_t pte;
unsigned long old_page, new_page;
-
+ struct page * page_map;
+
new_page = __get_free_page(GFP_KERNEL);
pte = *page_table;
if (!pte_present(pte))
@@ -620,6 +626,11 @@
if (MAP_NR(old_page) >= max_mapnr)
goto bad_wp_page;
tsk->min_flt++;
+
+ page_map = mem_map + MAP_NR(old_page);
+ if (PageSwapCache(page_map))
+ delete_from_swap_cache(page_map);
+
/*
* Do we need to copy?
*/
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov