x86: Fix CPA memtype reserving in the set_pages_array cases
PATCH 1/1 x86: Fix CPA memtype reserving in the set_pages_array cases
Bug #13884
The code was incorrectly reserving memtypes using the page virtual address instead of the physical address. Furthermore, the code was not ignoring highmem pages as it ought to.
--- arch/x86/mm/pageattr.c | 30 +++++++++++++++++++++--------- 1 files changed, 21 insertions(+), 9 deletions(-)
arch/x86/mm/pageattr.c +21 -9
@@ -590,9 +590,12 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)unsigned int level;pte_t *kpte, old_pte; -if (cpa->flags &CPA_PAGES_ARRAY ) - address = (unsigned long )page_address (cpa->pages[cpa->curpage]); -else if (cpa->flags &CPA_ARRAY ) +if (cpa->flags &CPA_PAGES_ARRAY ) { +struct page *page = cpa->pages[cpa->curpage]; +if (unlikely (PageHighMem (page))) +return 0 ; + address = (unsigned long )page_address (page); + }else if (cpa->flags &CPA_ARRAY ) address = cpa->vaddr[cpa->curpage];else address = *cpa->vaddr;
@@ -696,9 +699,12 @@ static int cpa_process_alias(struct cpa_data *cpa)* No need to redo, when the primary call touched the direct * mapping already: */ -if (cpa->flags &CPA_PAGES_ARRAY ) - vaddr = (unsigned long )page_address (cpa->pages[cpa->curpage]); -else if (cpa->flags &CPA_ARRAY ) +if (cpa->flags &CPA_PAGES_ARRAY ) { +struct page *page = cpa->pages[cpa->curpage]; +if (unlikely (PageHighMem (page))) +return 0 ; + vaddr = (unsigned long )page_address (page); + }else if (cpa->flags &CPA_ARRAY ) vaddr = cpa->vaddr[cpa->curpage];else vaddr = *cpa->vaddr;
@@ -1118,7 +1124,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray)int free_idx;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;if (reserve_memtype (start, end, _PAGE_CACHE_UC_MINUS,NULL )) goto err_out;
@@ -1131,7 +1139,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray) err_out: free_idx = i;for (i =0 ; i < free_idx; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end); }
@@ -1160,7 +1170,9 @@ int set_pages_array_wb(struct page **pages, int addrinarray)return retval;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end); }
Signature · 1 line
1.5.4.3
Quoted 18 lines · Thomas Hellstrom
Bug #13884
The code was incorrectly reserving memtypes using the page virtual address instead of the physical address. Furthermore, the code was not ignoring highmem pages as it ought to.Signed-off-by Thomas Hellstrom <[email protected]>--- arch/x86/mm/pageattr.c | 30 +++++++++++++++++++++--------- 1 files changed, 21 insertions(+), 9 deletions(-)arch/x86/mm/pageattr.c +0
-0@@ -590,9 +590,12 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)unsigned int level;pte_t *kpte, old_pte;
arch/x86/mm/pageattr.c static int __change_page_attr(struct cpa_data *cpa, int primary) · line 599
Quoted 10 lines
arch/x86/mm/pageattr.c +6
-3-if (cpa->flags &CPA_PAGES_ARRAY ) - address = (unsigned long )page_address (cpa->pages[cpa->curpage]); -else if (cpa->flags &CPA_ARRAY ) +if (cpa->flags &CPA_PAGES_ARRAY ) { +struct page *page = cpa->pages[cpa->curpage]; +if (unlikely (PageHighMem (page))) +return 0 ; + address = (unsigned long )page_address (page); + }else if (cpa->flags &CPA_ARRAY ) address = cpa->vaddr[cpa->curpage];
hm, i'm missing a description about how this bug was triggered. How did you end up getting highmem pages to a cpa call?
Quoted 19 lines
arch/x86/mm/pageattr.c +6
-3else address = *cpa->vaddr;@@ -696,9 +699,12 @@ static int cpa_process_alias(struct cpa_data *cpa)* No need to redo, when the primary call touched the direct * mapping already: */ -if (cpa->flags &CPA_PAGES_ARRAY ) - vaddr = (unsigned long )page_address (cpa->pages[cpa->curpage]); -else if (cpa->flags &CPA_ARRAY ) +if (cpa->flags &CPA_PAGES_ARRAY ) { +struct page *page = cpa->pages[cpa->curpage]; +if (unlikely (PageHighMem (page))) +return 0 ; + vaddr = (unsigned long )page_address (page); + }else if (cpa->flags &CPA_ARRAY ) vaddr = cpa->vaddr[cpa->curpage];else vaddr = *cpa->vaddr;
arch/x86/mm/pageattr.c int set_pages_array_uc(struct page **pages, int addrinarray) · line 1132
Quoted 10 lines
arch/x86/mm/pageattr.c +3
-1@@ -1118,7 +1124,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray)int free_idx;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;if (reserve_memtype (start, end, _PAGE_CACHE_UC_MINUS,NULL )) goto err_out;
ok, that's a bug introduced in .29 but which was latent until now:drivers/char/agp/generic.c now uses it plus (indirectly) a number of AGP drivers, since:
commit 07613ba2f464f59949266f4337b75b91eb610795
Author: Dave Airlie <[email protected]>
Date: Fri Jun 12 14:11:41 2009 +1000
agp: switch AGP to use page array instead of unsigned long array
I dont see how it can end up with highmem pages though. All the graphics apperture allocations happen to lowmem AFAICS. Did GEM add the possibility for user pages (highmem amongst them) ending up in that pool? Which code does that?
Quoted 12 lines
arch/x86/mm/pageattr.c +3
-1@@ -1131,7 +1139,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray) err_out: free_idx = i;for (i =0 ; i < free_idx; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end); }
arch/x86/mm/pageattr.c int set_pages_array_wb(struct page **pages, int addrinarray) · line 1177
Quoted 9 lines
arch/x86/mm/pageattr.c +3
-1@@ -1160,7 +1170,9 @@ int set_pages_array_wb(struct page **pages, int addrinarray)return retval;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end);
In any case it's a must-have fix for .31. Possibly even a backport tag is needed, in case a distro does a .30 kernel with more recent graphics bits.
Ingo
Quoted 4 lines
hm, i'm missing a description about how this bug was triggered. How did you end up getting highmem pages to a cpa call?
GEM and TTM both allocate page arrays and just pass them to cpa, we don't know what type of pages the allocator gives us back and we really shouldn't have to, so having cpa ignore highmem pages is certainly the right option.
GEM just uses shmem code to alloc the pages and TTM has its own allocator.
Dave.
Quoted 86 lines
Quoted 20 lines
arch/x86/mm/pageattr.c +6
-3else address = *cpa->vaddr;@@ -696,9 +699,12 @@ static int cpa_process_alias(struct cpa_data *cpa)* No need to redo, when the primary call touched the direct * mapping already: */ -if (cpa->flags &CPA_PAGES_ARRAY ) - vaddr = (unsigned long )page_address (cpa->pages[cpa->curpage]); -else if (cpa->flags &CPA_ARRAY ) +if (cpa->flags &CPA_PAGES_ARRAY ) { +struct page *page = cpa->pages[cpa->curpage]; +if (unlikely (PageHighMem (page))) +return 0 ; + vaddr = (unsigned long )page_address (page); + }else if (cpa->flags &CPA_ARRAY ) vaddr = cpa->vaddr[cpa->curpage];else vaddr = *cpa->vaddr;arch/x86/mm/pageattr.c int set_pages_array_uc(struct page **pages, int addrinarray) · line 1132
Quoted 10 lines
arch/x86/mm/pageattr.c +3
-1@@ -1118,7 +1124,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray)int free_idx;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;if (reserve_memtype (start, end, _PAGE_CACHE_UC_MINUS,NULL )) goto err_out;ok, that's a bug introduced in .29 but which was latent until now:
drivers/char/agp/generic.cnow uses it plus (indirectly) a number of AGP drivers, since:commit
07613ba2f464f59949266f4337b75b91eb610795
Author: Dave Airlie <[email protected]>
Date: Fri Jun 12 14:11:41 2009 +1000agp: switch AGP to use page array instead of unsigned long array
I dont see how it can end up with highmem pages though. All the graphics apperture allocations happen to lowmem AFAICS. Did GEM add the possibility for user pages (highmem amongst them) ending up in that pool? Which code does that?
Quoted 12 lines
arch/x86/mm/pageattr.c +3
-1@@ -1131,7 +1139,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray) err_out: free_idx = i;for (i =0 ; i < free_idx; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end); }arch/x86/mm/pageattr.c int set_pages_array_wb(struct page **pages, int addrinarray) · line 1177
Quoted 9 lines
arch/x86/mm/pageattr.c +3
-1@@ -1160,7 +1170,9 @@ int set_pages_array_wb(struct page **pages, int addrinarray)return retval;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end);In any case it's a must-have fix for .31. Possibly even a backport tag is needed, in case a distro does a .30 kernel with more recent graphics bits.
Ingo
------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day trial. Simplify your report design, integration and deployment - and focus on what you do best, core application coding. Discover what's new with Crystal Reports now. http://p.sf.net/sfu/bobj-july
--
_______________________________________________
Dri-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-devel
Quoted 9 lines · Dave Airlie
Quoted 2 lines
hm, i'm missing a description about how this bug was triggered. How did you end up getting highmem pages to a cpa call?
GEM and TTM both allocate page arrays and just pass them to cpa,
we don't know what type of pages the allocator gives us back and we really shouldn't have to, so having cpa ignore highmem pages is certainly the right option.GEM just uses shmem code to alloc the pages and TTM has its own allocator.
Yes, Dave is right.
Although I'm not 100% sure the TTM code I was using that triggered this has made it into 2.6.31.
Old AGP uses (GFP_KERNEL | GFP_DMA32 | __GFP_ZERO), which (correct me if I'm wrong) never hands back highmem pages. This means that Intel's GEM is the only likely user for 2.6.31.
/Thomas
Quoted 81 lines
Dave.
Quoted 79 lines
Quoted 22 lines
arch/x86/mm/pageattr.c +6
-3else address = *cpa->vaddr;@@ -696,9 +699,12 @@ static int cpa_process_alias(struct cpa_data *cpa)* No need to redo, when the primary call touched the direct * mapping already: */ -if (cpa->flags &CPA_PAGES_ARRAY ) - vaddr = (unsigned long )page_address (cpa->pages[cpa->curpage]); -else if (cpa->flags &CPA_ARRAY ) +if (cpa->flags &CPA_PAGES_ARRAY ) { +struct page *page = cpa->pages[cpa->curpage]; +if (unlikely (PageHighMem (page))) +return 0 ; + vaddr = (unsigned long )page_address (page); + }else if (cpa->flags &CPA_ARRAY ) vaddr = cpa->vaddr[cpa->curpage];else vaddr = *cpa->vaddr;arch/x86/mm/pageattr.c int set_pages_array_uc(struct page **pages, int addrinarray) · line 1132
Quoted 10 lines
arch/x86/mm/pageattr.c +3
-1@@ -1118,7 +1124,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray)int free_idx;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;if (reserve_memtype (start, end, _PAGE_CACHE_UC_MINUS,NULL )) goto err_out;ok, that's a bug introduced in .29 but which was latent until now:
drivers/char/agp/generic.cnow uses it plus (indirectly) a number of AGP drivers, since:commit
07613ba2f464f59949266f4337b75b91eb610795
Author: Dave Airlie <[email protected]>
Date: Fri Jun 12 14:11:41 2009 +1000agp: switch AGP to use page array instead of unsigned long array
I dont see how it can end up with highmem pages though. All the graphics apperture allocations happen to lowmem AFAICS. Did GEM add the possibility for user pages (highmem amongst them) ending up in that pool? Which code does that?
Quoted 12 lines
arch/x86/mm/pageattr.c +3
-1@@ -1131,7 +1139,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray) err_out: free_idx = i;for (i =0 ; i < free_idx; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end); }arch/x86/mm/pageattr.c int set_pages_array_wb(struct page **pages, int addrinarray) · line 1178
Quoted 10 lines
arch/x86/mm/pageattr.c +3
-1@@ -1160,7 +1170,9 @@ int set_pages_array_wb(struct page **pages, int addrinarray)return retval;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end);In any case it's a must-have fix for .31. Possibly even a backport tag is needed, in case a distro does a .30 kernel with more recent graphics bits.
Ingo
------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day trial. Simplify your report design, integration and deployment - and focus on what you do best, core application coding. Discover what's new with Crystal Reports now. http://p.sf.net/sfu/bobj-july
--
_______________________________________________
Dri-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-devel
Quoted 13 lines · Thomas Hellstrom
Quoted 4 lines · Dave Airlie
Quoted 3 lines
hm, i'm missing a description about how this bug was triggered. How did you end up getting highmem pages to a cpa call?
Quoted 8 lines
GEM and TTM both allocate page arrays and just pass them to cpa, we don't know what type of pages the allocator gives us back and we really shouldn't have to, so having cpa ignore highmem pages is certainly the right option.
GEM just uses shmem code to alloc the pages and TTM has its own allocator.
Quoted 7 lines
Yes, Dave is right.
Although I'm not 100% sure the TTM code I was using that triggered this has made it into 2.6.31.
Old AGP uses (GFP_KERNEL|GFP_DMA32|__GFP_ZERO), which (correct me if I'm wrong) never hands back highmem pages. This means that Intel's GEM is the only likely user for 2.6.31.
(please dont top-post) I'm not sure you folks noticed this bit of my mail:
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
ok, that's a bug introduced in .29 but which was latent until now:
drivers/char/agp/generic.cnow uses it plus (indirectly) a number of AGP drivers, since:commit
07613ba2f464f59949266f4337b75b91eb610795
Author: Dave Airlie <[email protected]>
Date: Fri Jun 12 14:11:41 2009 +1000
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
agp: switch AGP to use page array instead of unsigned long array
I dont see how it can end up with highmem pages though. All the graphics apperture allocations happen to lowmem AFAICS. Did GEM add the possibility for user pages (highmem amongst them) ending up in that pool? Which code does that?
Ingo
Quoted 30 lines · Ingo Molnar
Quoted 8 lines · Thomas Hellstrom
Quoted 6 lines · Dave Airlie
Quoted 3 lines
hm, i'm missing a description about how this bug was triggered. How did you end up getting highmem pages to a cpa call?
GEM and TTM both allocate page arrays and just pass them to cpa,
we don't know what type of pages the allocator gives us back and we really shouldn't have to, so having cpa ignore highmem pages is certainly the right option.Quoted 9 lines
Quoted 4 lines
GEM just uses shmem code to alloc the pages and TTM has its own allocator.
Yes, Dave is right.
Although I'm not 100% sure the TTM code I was using that triggered this has made it into 2.6.31.
Old AGP uses (GFP_KERNEL|GFP_DMA32|__GFP_ZERO), which (correct me if I'm wrong) never hands back highmem pages. This means that Intel's GEM is the only likely user for 2.6.31.(please dont top-post) I'm not sure you folks noticed this bit of my mail:
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
ok, that's a bug introduced in .29 but which was latent until now:
drivers/char/agp/generic.cnow uses it plus (indirectly) a number of AGP drivers, since:commit
07613ba2f464f59949266f4337b75b91eb610795
Author: Dave Airlie <[email protected]>
Date: Fri Jun 12 14:11:41 2009 +1000
Quoted 7 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
agp: switch AGP to use page array instead of unsigned long array
I dont see how it can end up with highmem pages though. All the graphics apperture allocations happen to lowmem AFAICS. Did GEM add the possibility for user pages (highmem amongst them) ending up in that pool? Which code does that?
Ingo
Sorry, my bad.
The TTM code I tested is not in yet, and after double-checking it looks like Intel's gem is not changing caching policy before binding to AGP.
This means the highmem problems that I saw were triggered by a combination of the virtual->physical bugfix and code that's not in the kernel yet, and since it's an optimization of the current code it's not likely to land in 2.6.31. The highmem fixes could thus, AFAICT be stripped out of the patch, unless GFP_DMA32 on a highmem system can actually hand back highmem pages, in which case AGP will not work correctly.
As for highmem use in the future, the TTM page arrays are populated using fault(), which means that there will be an overhead ordering the pages so that we can use the set_pages_array() interface instead of set_memory() that we use today. Therefore, if possible, I'd prefer if we could pass arrays containing highmem pages to the set_pages_array() interface.
There are no aliased mappings since
1) Any user space mappings to these pages are killed before changing caching policy.
2) The pages are allocated and owned by the driver.
3) kmap_atomic_prot() and vmap() are used to map these pages in kernel space.
Code is in ttm_tt.c ttm_bo.c and ttm_bo_util.c
Thanks.
/Thomas
Quoted 71 lines · Thomas Hellstrom
Quoted 39 lines · Ingo Molnar
Quoted 13 lines · Thomas Hellstrom
Quoted 11 lines · Dave Airlie
Quoted 3 lines
hm, i'm missing a description about how this bug was triggered. How did you end up getting highmem pages to a cpa call?
GEM and TTM both allocate page arrays and just pass them to cpa, we don't know what type of pages the allocator gives us back and we really shouldn't have to, so having cpa ignore highmem pages is certainly the right option.
GEM just uses shmem code to alloc the pages and TTM has its own allocator.
Quoted 8 lines
Yes, Dave is right.
Although I'm not 100% sure the TTM code I was using that triggered this has made it into 2.6.31.
Old AGP uses (GFP_KERNEL|GFP_DMA32|__GFP_ZERO), which (correct me if I'm wrong) never hands back highmem pages. This means that Intel's GEM is the only likely user for 2.6.31.(please dont top-post) I'm not sure you folks noticed this bit of my mail:
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
ok, that's a bug introduced in .29 but which was latent until
now:drivers/char/agp/generic.cnow uses it plus (indirectly) a number of AGP drivers, since:commit
07613ba2f464f59949266f4337b75b91eb610795
Author: Dave Airlie <[email protected]>
Date: Fri Jun 12 14:11:41 2009 +1000agp: switch AGP to use page array instead of unsigned long array
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
I dont see how it can end up with highmem pages though. All the graphics apperture allocations happen to lowmem AFAICS. Did GEM add the possibility for user pages (highmem amongst them) ending up in that pool? Which code does that?
Ingo
Sorry, my bad.
The TTM code I tested is not in yet, and after double-checking it looks like Intel's gem is not changing caching policy before binding to AGP.
This means the highmem problems that I saw were triggered by a combination of the
virtual->physicalbugfix and code that's not in the kernel yet, and since it's an optimization of the current code it's not likely to land in 2.6.31. The highmem fixes could thus, AFAICT be stripped out of the patch, unlessGFP_DMA32on a highmem system can actually hand back highmem pages, in which case AGP will not work correctly.As for highmem use in the future, the TTM page arrays are populated using
fault(), which means that there will be an overhead ordering the pages so that we can use theset_pages_array()interface instead ofset_memory()that we use today. Therefore, if possible, I'd prefer if we could pass arrays containing highmem pages to theset_pages_array()interface.There are no aliased mappings since
Quoted 7 lines
1) Any user space mappings to these pages are killed before changing caching policy.
2) The pages are allocated and owned by the driver.
3)kmap_atomic_prot()andvmap()are used to map these pages in kernel space.Code is in
ttm_tt.cttm_bo.candttm_bo_util.c
ok - thanks for the explanation. Since you intentionally want to use highmem pages (and your use is safe) i concur with your original patch in its entirety - even if that planned highmem use is not upstream yet. Will get it .31-wards ASAP.
Ingo
Suresh Siddha
Acked-byQuoted 4 lines · Ingo Molnar
ok - thanks for the explanation. Since you intentionally want to use highmem pages (and your use is safe) i concur with your original patch in its entirety - even if that planned highmem use is not upstream yet. Will get it .31-wards ASAP.
While none of the drivers in 2.6.30 use this interface, it will be good if we backport this to 2.6.30-stable aswell.
Also, now that we have set_pages_array interface, do we still need the set_memory_array_uc interface? Removing that should clean up the cpa code a bit.
thanks,
suresh
CPA interfaces WAS x86: Fix CPA memtype reserving in the set_pages_array cases
Suresh Siddha skrev:
Quoted 9 lines
Also, now that we have
set_pages_arrayinterface, do we still need theset_memory_array_ucinterface? Removing that should clean up the cpa code a bit.thanks,
suresh
This might be a good time to discuss how graphics drivers will use this interface in the not too distant future. These are my thoughts.
1) A pool of uc / wc pages: Jerome Glisse and Dave Airlie have been working on this. It will hugely speed up the allocation and freeing of transient uc / wc buffers. Pages will be allocated and freed in chunks of a couple of megabytes, and caching attributes are changed using the set_pages_array() interface. The pool will sit in the TTM driver. 1a) Using large pages: With such a pool it would be beneficial to allocate and free large pages to avoid page splitting in the CPA code. There is no code in the pool implementation for that yet, but I think the set_memory_array_xx will be the best interface for that, although I guess set_pages_array_xx would work.
2) Optimize cache flushing:
2a) I think both the TTM code and AGP code flushes the cache before changing from wb, to make sure the device sees the data. Therefore with these callers, I don't think the CPA code needs to flush the cache again, and it might be beneficial with a "cache_already_flushed" bool to some of the function apis. 2b) Use self-snooping where available regardless of the value of the "cache_already_flushed" bool.
3) set_pages_array_np():
This is a bit controversial, but will enable a certain valuable class of graphics optimizations. The idea is to, instead of marking the PTEs as wc or uc, marking them as non-present, np. This means that the TTM code does not need to bother with CPA if it wants to make quick cached reads from graphics buffers. Just use kmap_atomic() and make sure to clflush before unmap. The caveat is that all np pages must be marked wb before hibernation since the hibernation code will try to read from the linear kernel map. But we have to deal with that today for uc / wc highmem pages today as well since the hibernation code will try to kmap() them with wb page attributes.
It would be nice to have some comments on these general ideas before starting to work on them.
/Thomas
Quoted 10 lines · Dave Airlie
Quoted 2 lines
hm, i'm missing a description about how this bug was triggered. How did you end up getting highmem pages to a cpa call?
GEM and TTM both allocate page arrays and just pass them to cpa, we don't know what type of pages the allocator gives us back and we really shouldn't have to, so having cpa ignore highmem pages is certainly the right option.
GEM just uses shmem code to alloc the pages and TTM has its own allocator.
Neither of my questions was answered though: do highmem pages ever get passed in and what were the effects of the bug and how was it noticed?
If no highmem pages can be passed in then the right solution is not to ignore them silently but to add a WARN_ONCE() to enforce that they are not used in the future either.
If they are used today then i'd like to know where exactly and double check that all the cache attributes are consistent: as a highmem page might be visible to user-space as well or might be mapped via the vmalloc space, etc.
And yes, if it happens and all the other mapping aliases are fine then ignoring them is the right solution.
Ingo
[tip:x86/urgent] x86: Fix CPA memtype reserving in the set_pages_array*() cases
In reply to Thomas HellstromCommit-ID: 9e5b74fef5a7e8e408ba514db8dee5320e633d40 Gitweb: http://git.kernel.org/tip/9e5b74fef5a7e8e408ba514db8dee5320e633d40 Author: Thomas Hellstrom <[email protected]> AuthorDate: Mon, 3 Aug 2009 09:25:45 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Mon, 3 Aug 2009 12:47:56 +0200
x86: Fix CPA memtype reserving in the set_pages_array*() cases
The code was incorrectly reserving memtypes using the page virtual address instead of the physical address. Furthermore, the code was not ignoring highmem pages as it ought to.
( upstream does not pass in highmem pages yet - but upcoming
graphics code will do it and there's no reason to not handle this properly in the CPA APIs.)
LKML-Reference: <[email protected]>
--- arch/x86/mm/pageattr.c | 30 +++++++++++++++++++++--------- 1 files changed, 21 insertions(+), 9 deletions(-)
arch/x86/mm/pageattr.c +21 -9
@@ -591,9 +591,12 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)unsigned int level;pte_t *kpte, old_pte; -if (cpa->flags &CPA_PAGES_ARRAY ) - address = (unsigned long )page_address (cpa->pages[cpa->curpage]); -else if (cpa->flags &CPA_ARRAY ) +if (cpa->flags &CPA_PAGES_ARRAY ) { +struct page *page = cpa->pages[cpa->curpage]; +if (unlikely (PageHighMem (page))) +return 0 ; + address = (unsigned long )page_address (page); + }else if (cpa->flags &CPA_ARRAY ) address = cpa->vaddr[cpa->curpage];else address = *cpa->vaddr;
@@ -697,9 +700,12 @@ static int cpa_process_alias(struct cpa_data *cpa)* No need to redo, when the primary call touched the direct * mapping already: */ -if (cpa->flags &CPA_PAGES_ARRAY ) - vaddr = (unsigned long )page_address (cpa->pages[cpa->curpage]); -else if (cpa->flags &CPA_ARRAY ) +if (cpa->flags &CPA_PAGES_ARRAY ) { +struct page *page = cpa->pages[cpa->curpage]; +if (unlikely (PageHighMem (page))) +return 0 ; + vaddr = (unsigned long )page_address (page); + }else if (cpa->flags &CPA_ARRAY ) vaddr = cpa->vaddr[cpa->curpage];else vaddr = *cpa->vaddr;
@@ -1122,7 +1128,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray)int free_idx;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;if (reserve_memtype (start, end, _PAGE_CACHE_UC_MINUS,NULL )) goto err_out;
@@ -1135,7 +1143,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray) err_out: free_idx = i;for (i =0 ; i < free_idx; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end); }
@@ -1164,7 +1174,9 @@ int set_pages_array_wb(struct page **pages, int addrinarray)return retval;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end); }
[tip:x86/urgent] x86: Fix CPA memtype reserving in the set_pages_array*() cases
In reply to Thomas HellstromCommit-ID: 8523acfe40efc1a8d3da8f473ca67cb195b06f0c Gitweb: http://git.kernel.org/tip/8523acfe40efc1a8d3da8f473ca67cb195b06f0c Author: Thomas Hellstrom <[email protected]> AuthorDate: Mon, 3 Aug 2009 09:25:45 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Mon, 3 Aug 2009 19:36:09 +0200
x86: Fix CPA memtype reserving in the set_pages_array*() cases
The code was incorrectly reserving memtypes using the page virtual address instead of the physical address. Furthermore, the code was not ignoring highmem pages as it ought to.
( upstream does not pass in highmem pages yet - but upcoming
graphics code will do it and there's no reason to not handle this properly in the CPA APIs.)
LKML-Reference: <[email protected]>
--- arch/x86/mm/pageattr.c | 30 +++++++++++++++++++++--------- 1 files changed, 21 insertions(+), 9 deletions(-)
arch/x86/mm/pageattr.c +21 -9
@@ -591,9 +591,12 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)unsigned int level;pte_t *kpte, old_pte; -if (cpa->flags &CPA_PAGES_ARRAY ) - address = (unsigned long )page_address (cpa->pages[cpa->curpage]); -else if (cpa->flags &CPA_ARRAY ) +if (cpa->flags &CPA_PAGES_ARRAY ) { +struct page *page = cpa->pages[cpa->curpage]; +if (unlikely (PageHighMem (page))) +return 0 ; + address = (unsigned long )page_address (page); + }else if (cpa->flags &CPA_ARRAY ) address = cpa->vaddr[cpa->curpage];else address = *cpa->vaddr;
@@ -697,9 +700,12 @@ static int cpa_process_alias(struct cpa_data *cpa)* No need to redo, when the primary call touched the direct * mapping already: */ -if (cpa->flags &CPA_PAGES_ARRAY ) - vaddr = (unsigned long )page_address (cpa->pages[cpa->curpage]); -else if (cpa->flags &CPA_ARRAY ) +if (cpa->flags &CPA_PAGES_ARRAY ) { +struct page *page = cpa->pages[cpa->curpage]; +if (unlikely (PageHighMem (page))) +return 0 ; + vaddr = (unsigned long )page_address (page); + }else if (cpa->flags &CPA_ARRAY ) vaddr = cpa->vaddr[cpa->curpage];else vaddr = *cpa->vaddr;
@@ -1122,7 +1128,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray)int free_idx;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;if (reserve_memtype (start, end, _PAGE_CACHE_UC_MINUS,NULL )) goto err_out;
@@ -1135,7 +1143,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray) err_out: free_idx = i;for (i =0 ; i < free_idx; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end); }
@@ -1164,7 +1174,9 @@ int set_pages_array_wb(struct page **pages, int addrinarray)return retval;for (i =0 ; i < addrinarray; i++) { - start = (unsigned long )page_address (pages[i]); +if (PageHighMem (pages[i])) +continue ; + start =page_to_pfn (pages[i]) <<PAGE_SHIFT ; end = start +PAGE_SIZE ;free_memtype (start, end); }