]> git.hungrycats.org Git - linux/commitdiff
[PATCH] x86_64: change_page_attr logic fixes from Andrea
authorAndi Kleen <ak@suse.de>
Tue, 11 Jan 2005 09:49:19 +0000 (01:49 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Tue, 11 Jan 2005 09:49:19 +0000 (01:49 -0800)
change_page_attr logic fixes from Andrea

This avoids reference counting leaks and adds BUGs for more wrong cases.

Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/i386/mm/pageattr.c
arch/x86_64/mm/pageattr.c

index 7647cde233a7980ea85b9d1dcd9eb0edb6690add..46c86395fe40d9544828df1bb3d2446d4e632f51 100644 (file)
@@ -120,27 +120,35 @@ __change_page_attr(struct page *page, pgprot_t prot)
        kpte_page = virt_to_page(kpte);
        if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) { 
                if ((pte_val(*kpte) & _PAGE_PSE) == 0) { 
-                       pte_t old = *kpte;
-                       pte_t standard = mk_pte(page, PAGE_KERNEL); 
                        set_pte_atomic(kpte, mk_pte(page, prot)); 
-                       if (pte_same(old,standard))
-                               get_page(kpte_page);
                } else {
                        struct page *split = split_large_page(address, prot); 
                        if (!split)
                                return -ENOMEM;
-                       get_page(kpte_page);
                        set_pmd_pte(kpte,address,mk_pte(split, PAGE_KERNEL));
+                       kpte_page = split;
                }       
+               get_page(kpte_page);
        } else if ((pte_val(*kpte) & _PAGE_PSE) == 0) { 
                set_pte_atomic(kpte, mk_pte(page, PAGE_KERNEL));
                __put_page(kpte_page);
-       }
+       } else
+               BUG();
 
-       if (cpu_has_pse && (page_count(kpte_page) == 1)) {
-               list_add(&kpte_page->lru, &df_list);
-               revert_page(kpte_page, address);
-       } 
+       /*
+        * If the pte was reserved, it means it was created at boot
+        * time (not via split_large_page) and in turn we must not
+        * replace it with a largepage.
+        */
+       if (!PageReserved(kpte_page)) {
+               /* memleak and potential failed 2M page regeneration */
+               BUG_ON(!page_count(kpte_page));
+
+               if (cpu_has_pse && (page_count(kpte_page) == 1)) {
+                       list_add(&kpte_page->lru, &df_list);
+                       revert_page(kpte_page, address);
+               }
+       }
        return 0;
 } 
 
index 05074f1777304162d671d02bf5a433ae85e8b39b..5b3cd2000a54e435237657325e1ba00a97f85bbc 100644 (file)
@@ -131,28 +131,36 @@ __change_page_attr(unsigned long address, unsigned long pfn, pgprot_t prot,
        kpte_flags = pte_val(*kpte); 
        if (pgprot_val(prot) != pgprot_val(ref_prot)) { 
                if ((kpte_flags & _PAGE_PSE) == 0) { 
-                       pte_t old = *kpte;
-                       pte_t standard = pfn_pte(pfn, ref_prot);
-
                        set_pte(kpte, pfn_pte(pfn, prot));
-                       if (pte_same(old,standard))
-                               get_page(kpte_page);
                } else {
+                       /*
+                        * split_large_page will take the reference for this change_page_attr
+                        * on the split page.
+                        */
                        struct page *split = split_large_page(address, prot, ref_prot); 
                        if (!split)
                                return -ENOMEM;
-                       get_page(split);
                        set_pte(kpte,mk_pte(split, ref_prot));
+                       kpte_page = split;
                }       
+               get_page(kpte_page);
        } else if ((kpte_flags & _PAGE_PSE) == 0) { 
                set_pte(kpte, pfn_pte(pfn, ref_prot));
                __put_page(kpte_page);
-       }
+       } else
+               BUG();
 
-       if (page_count(kpte_page) == 1) {
+       /* on x86-64 the direct mapping set at boot is not using 4k pages */
+       BUG_ON(PageReserved(kpte_page));
+
+       switch (page_count(kpte_page)) {
+       case 1:
                save_page(address, kpte_page);               
                revert_page(address, ref_prot);
-       } 
+               break;
+       case 0:
+               BUG(); /* memleak and failed 2M page regeneration */
+       }
        return 0;
 }