Regression after commit 19809c2da28a ("mm, vmalloc: use __GFP_HIGHMEM implicitly")
Subsystems (5)
A user with i386 instead of AMD64 machine reports [1] that commit 19809c2da28a ("mm, vmalloc: use __GFP_HIGHMEM implicitly”) causes a regression. BUG_ON(PageHighMem(pg)) in drivers/media/common/saa7146/saa7146_core.c always gets triggered after that commit.
Commit 704b862f9efd ("mm/vmalloc.c: don't unconditonally use __GFP_HIGHMEM”) adjusts the mask logic, now the __GFP_HIGHMEM only gets applied when there is no GFP_DMA or GFP_DMA32.
So I tried to adjust its malloc to "__vmalloc(nr_pages * sizeof(struct scatterlist), GFP_KERNEL | GFP_DMA | __GFP_ZERO, PAGE_KERNEL)”, but both GFP_DMA or GFP_DMA32 still trigger the BUG_ON(PageHighMem()) macro.
Also there are other BUG_ON(PageHighMem()) in drivers/media, I think they will get hit by same regression in 32bit machine too.
[1] https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1742316
Kai-Heng
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 2 lines · Kai Heng Feng
A user with i386 instead of AMD64 machine reports [1] that commit
19809c2da28a("mm, vmalloc: use__GFP_HIGHMEMimplicitlya??) causes a regression. BUG_ON(PageHighMem(pg)) indrivers/media/common/saa7146/saa7146_core.calways gets triggered after that commit.
Well, the BUG_ON is wrong. You can absolutely have pages which are both HighMem and under the 4GB boundary. Only the first 896MB (iirc) are LowMem, and the next 3GB of pages are available to vmalloc_32().
Quoted 1 line
Also there are other BUG_ON(PageHighMem()) in
drivers/media, I think they will get hit by same regression in 32bit machine too.
I fixed one of them. I think the other three are also bogus, but it's hard to say; the comments say "DMA to HighMem might not work", and they probably mean "above the 4GB boundary", but I really don't know.
(since two drivers now have this code, maybe it should be part of the core MM API? Or maybe there's already something better they should be using?)
drivers/media/common/saa7146/saa7146_core.c +1 -1
@@ -160,7 +160,7 @@ static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) pg =vmalloc_to_page (virt);if (NULL == pg) goto err; -BUG_ON (PageHighMem (pg)); +BUG_ON (page_to_pfn (pg) >= (1 << (32 -PAGE_SHIFT )));sg_set_page (&sglist[i], pg,PAGE_SIZE ,0 ); }return sglist;
drivers/media/v4l2-core/videobuf-dma-sg.c +1 -1
@@ -77,7 +77,7 @@ static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, pg =vmalloc_to_page (virt);if (NULL == pg) goto err; -BUG_ON (PageHighMem (pg)); +BUG_ON (page_to_pfn (pg) >= (1 << (32 -PAGE_SHIFT )));sg_set_page (&sglist[i], pg,PAGE_SIZE ,0 ); }return sglist;
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 8 lines · Matthew Wilcox
Quoted 2 lines · Kai Heng Feng
A user with i386 instead of AMD64 machine reports [1] that commit
19809c2da28a("mm, vmalloc: use__GFP_HIGHMEMimplicitlya??) causes a regression. BUG_ON(PageHighMem(pg)) indrivers/media/common/saa7146/saa7146_core.calways gets triggered after that commit.Well, the
BUG_ONis wrong. You can absolutely have pages which are both HighMem and under the 4GB boundary. Only the first 896MB (iirc) are LowMem, and the next 3GB of pages are available tovmalloc_32().
Quoted 9 lines
Quoted 1 line
Also there are other BUG_ON(PageHighMem()) in
drivers/media, I think they will get hit by same regression in 32bit machine too.I fixed one of them. I think the other three are also bogus, but it's hard to say; the comments say "DMA to HighMem might not work", and they probably mean "above the 4GB boundary", but I really don't know.
(since two drivers now have this code, maybe it should be part of the core MM API? Or maybe there's already something better they should be using?)
The comment at the top said the function was copied from videobuf-dma-sg.c so might be worth making it common.
Quoted 19 lines
drivers/media/common/saa7146/saa7146_core.c +1
-1@@ -160,7 +160,7 @@ static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) pg =vmalloc_to_page (virt);if (NULL == pg) goto err; -BUG_ON (PageHighMem (pg)); +BUG_ON (page_to_pfn (pg) >= (1 << (32 -PAGE_SHIFT )));sg_set_page (&sglist[i], pg,PAGE_SIZE ,0 ); }return sglist;drivers/media/v4l2-core/videobuf-dma-sg.c +0
-0
drivers/media/v4l2-core/videobuf-dma-sg.c static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, · line 83
Quoted 8 lines
drivers/media/v4l2-core/videobuf-dma-sg.c +1
-1@@ -77,7 +77,7 @@ static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, pg =vmalloc_to_page (virt);if (NULL == pg) goto err; -BUG_ON (PageHighMem (pg)); +BUG_ON (page_to_pfn (pg) >= (1 << (32 -PAGE_SHIFT )));sg_set_page (&sglist[i], pg,PAGE_SIZE ,0 ); }return sglist;
the vzalloc in this function needs to be switched to vmalloc32 if it actually wants to guarantee 32-bit memory.
Thanks,
Laura
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 6 lines · Laura Abbott
Quoted 6 lines
+++
b/drivers/media/v4l2-core/videobuf-dma-sg.cdrivers/media/common/saa7146/saa7146_core.c +0
-1@@ -77,7 +77,7 @@ static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, pg =vmalloc_to_page (virt);if (NULL == pg) goto err; -BUG_ON (PageHighMem (pg));
Quoted 8 lines
drivers/media/common/saa7146/saa7146_core.c static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) · line 166
Quoted 5 lines
drivers/media/common/saa7146/saa7146_core.c +1
-0+BUG_ON (page_to_pfn (pg) >= (1 << (32 -PAGE_SHIFT )));sg_set_page (&sglist[i], pg,PAGE_SIZE ,0 ); }return sglist;the vzalloc in this function needs to be switched to vmalloc32 if it actually wants to guarantee 32-bit memory.
Whoops, you got confused between the sglist allocation and the allocation of the pages which will be mapped ...
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 16 lines · Matthew Wilcox
Quoted 6 lines · Laura Abbott
Quoted 6 lines
+++
b/drivers/media/v4l2-core/videobuf-dma-sg.cdrivers/media/common/saa7146/saa7146_core.c +0
-1@@ -77,7 +77,7 @@ static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, pg =vmalloc_to_page (virt);if (NULL == pg) goto err; -BUG_ON (PageHighMem (pg));Quoted 8 lines
drivers/media/common/saa7146/saa7146_core.c static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) · line 166
Quoted 5 lines
drivers/media/common/saa7146/saa7146_core.c +1
-0+BUG_ON (page_to_pfn (pg) >= (1 << (32 -PAGE_SHIFT )));sg_set_page (&sglist[i], pg,PAGE_SIZE ,0 ); }return sglist;the vzalloc in this function needs to be switched to vmalloc32 if it actually wants to guarantee 32-bit memory.
Quoted 3 lines
Whoops, you got confused between the sglist allocation and the allocation of the pages which will be mapped ...
Ah yeah, clearly need more coffee this morning.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 7 lines · Matthew Wilcox
Quoted 2 lines · Kai Heng Feng
A user with i386 instead of AMD64 machine reports [1] that commit
19809c2da28a("mm, vmalloc: use__GFP_HIGHMEMimplicitlya??) causes a regression. BUG_ON(PageHighMem(pg)) indrivers/media/common/saa7146/saa7146_core.calways gets triggered after that commit.Well, the
BUG_ONis wrong. You can absolutely have pages which are both HighMem and under the 4GB boundary. Only the first 896MB (iirc) are LowMem, and the next 3GB of pages are available tovmalloc_32().
... nevertheless, 19809c2da28a does in fact break vmalloc_32 on 32-bit. Look:
#if defined(CONFIG_64BIT) && defined(CONFIG_ZONE_DMA32) #define GFP_VMALLOC32 GFP_DMA32 | GFP_KERNEL #elif defined(CONFIG_64BIT) && defined(CONFIG_ZONE_DMA) #define GFP_VMALLOC32 GFP_DMA | GFP_KERNEL #else
#define GFP_VMALLOC32 GFP_KERNEL
#endif
So we pass in GFP_KERNEL to __vmalloc_node, which calls __vmalloc_node_range which calls __vmalloc_area_node, which ORs in __GFP_HIGHMEM.
So ... we could enable ZONE_DMA32 on 32-bit architectures. I don't know what side-effects that might have; it's clearly only been tested on 64-bit architectures so far.
It might be best to just revert 19809c2da28a and the follow-on 704b862f9efd.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 5 lines · Matthew Wilcox
So ... we could enable
ZONE_DMA32on 32-bit architectures. I don't know what side-effects that might have; it's clearly only been tested on 64-bit architectures so far.It might be best to just revert
19809c2da28aand the follow-on704b862f9efd.
Alternatively, try this. It passes in GFP_DMA32 from vmalloc_32, regardless of whether ZONE_DMA32 exists or not. If ZONE_DMA32 doesn't exist, then we clear it in __vmalloc_area_node(), after using it to determine that we shouldn't set __GFP_HIGHMEM.
mm/vmalloc.c +11 -12
@@ -1669,10 +1669,11 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,struct page **pages;unsigned int nr_pages, array_size, i;const gfp_t nested_gfp = (gfp_mask &GFP_RECLAIM_MASK ) | __GFP_ZERO; -const gfp_t alloc_mask = gfp_mask | __GFP_NOWARN; -const gfp_t highmem_mask = (gfp_mask & (GFP_DMA |GFP_DMA32 )) ? -0 : - __GFP_HIGHMEM; +gfp_t alloc_mask = gfp_mask | __GFP_NOWARN; +if (!(alloc_mask &GFP_ZONEMASK )) + alloc_mask |= __GFP_HIGHMEM; +if (!IS_ENABLED (CONFIG_ZONE_DMA32 ) && (alloc_mask & __GFP_DMA32)) + alloc_mask &= ~__GFP_DMA32; nr_pages =get_vm_area_size (area) >>PAGE_SHIFT ; array_size = (nr_pages *sizeof (struct page *));
@@ -1680,7 +1681,7 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, area->nr_pages = nr_pages;/* Please note that the recursion is strictly bounded. */ if (array_size >PAGE_SIZE ) { - pages =__vmalloc_node (array_size,1 , nested_gfp|highmem_mask, + pages =__vmalloc_node (array_size,1 , nested_gfp|__GFP_HIGHMEM,PAGE_KERNEL , node, area->caller); }else { pages =kmalloc_node (array_size, nested_gfp, node);
@@ -1696,9 +1697,9 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,struct page *page;if (node ==NUMA_NO_NODE ) - page =alloc_page (alloc_mask|highmem_mask); + page =alloc_page (alloc_mask);else - page =alloc_pages_node (node, alloc_mask|highmem_mask,0 ); + page =alloc_pages_node (node, alloc_mask,0 );if (unlikely (!page)) {/* Successfully allocated i pages, free them in __vunmap() */
@@ -1706,7 +1707,7 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, goto fail; } area->pages[i] = page; -if (gfpflags_allow_blocking (gfp_mask|highmem_mask)) +if (gfpflags_allow_blocking (gfp_mask))cond_resched (); }
@@ -1942,12 +1943,10 @@ void *vmalloc_exec(unsigned long size) NUMA_NO_NODE, __builtin_return_address(0)); } -#if defined(CONFIG_64BIT ) && defined(CONFIG_ZONE_DMA32 ) -#define GFP_VMALLOC32 GFP_DMA32 | GFP_KERNEL -#elif defined(CONFIG_64BIT ) && defined(CONFIG_ZONE_DMA ) +#if defined(CONFIG_64BIT ) && !defined(CONFIG_ZONE_DMA32 )#define GFP_VMALLOC32 GFP_DMA | GFP_KERNEL#else -#define GFP_VMALLOC32 GFP_KERNEL +#define GFP_VMALLOC32 GFP_DMA32 | GFP_KERNEL#endif /**
Hi Matthew,
Quoted 5 lines · Matthew Wilcox
Alternatively, try this. It passes in
GFP_DMA32fromvmalloc_32, regardless of whetherZONE_DMA32exists or not. IfZONE_DMA32doesn't exist, then we clear it in__vmalloc_area_node(), after using it to determine that we shouldn't set__GFP_HIGHMEM.
IIUC, I need to let drivers/media drivers start using vmalloc_32() with your patch, right?
Kai-Heng
Quoted 60 lines
mm/vmalloc.c +5
-4@@ -1669,10 +1669,11 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, struct page **pages;unsigned int nr_pages, array_size, i;const gfp_t nested_gfp = (gfp_mask &GFP_RECLAIM_MASK ) | __GFP_ZERO; -const gfp_t alloc_mask = gfp_mask | __GFP_NOWARN; -const gfp_t highmem_mask = (gfp_mask & (GFP_DMA |GFP_DMA32 )) ? -0 : - __GFP_HIGHMEM; +gfp_t alloc_mask = gfp_mask | __GFP_NOWARN; +if (!(alloc_mask &GFP_ZONEMASK )) + alloc_mask |= __GFP_HIGHMEM; +if (!IS_ENABLED (CONFIG_ZONE_DMA32 ) && (alloc_mask & __GFP_DMA32)) + alloc_mask &= ~__GFP_DMA32; nr_pages =get_vm_area_size (area) >>PAGE_SHIFT ;mm/vmalloc.c +1
-1array_size = (nr_pages *sizeof (struct page *));@@ -1680,7 +1681,7 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, area->nr_pages = nr_pages;/* Please note that the recursion is strictly bounded. */ if (array_size >PAGE_SIZE ) { - pages =__vmalloc_node (array_size,1 , nested_gfp|highmem_mask, + pages =__vmalloc_node (array_size,1 , nested_gfp|__GFP_HIGHMEM,PAGE_KERNEL , node, area->caller); }else {mm/vmalloc.c +2
-2pages =kmalloc_node (array_size, nested_gfp, node);@@ -1696,9 +1697,9 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, struct page *page;if (node ==NUMA_NO_NODE ) - page =alloc_page (alloc_mask|highmem_mask); + page =alloc_page (alloc_mask);else - page =alloc_pages_node (node, alloc_mask|highmem_mask,0 ); + page =alloc_pages_node (node, alloc_mask,0 );if (unlikely (!page)) {mm/vmalloc.c +1
-2/* Successfully allocated i pages, free them in __vunmap() */ @@ -1706,7 +1707,7 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, goto fail; } area->pages[i] = page; -if (gfpflags_allow_blocking (gfp_mask|highmem_mask)) +if (gfpflags_allow_blocking (gfp_mask))cond_resched (); }@@ -1942,12 +1943,10 @@ void *vmalloc_exec(unsigned long size) NUMA_NO_NODE, __builtin_return_address(0)); } -#if defined(CONFIG_64BIT ) && defined(CONFIG_ZONE_DMA32 )
mm/vmalloc.c void *vmalloc_exec(unsigned long size) · line 1952
Quoted 10 lines
mm/vmalloc.c +2
-3-#define GFP_VMALLOC32 GFP_DMA32 | GFP_KERNEL -#elif defined(CONFIG_64BIT ) && defined(CONFIG_ZONE_DMA ) +#if defined(CONFIG_64BIT ) && !defined(CONFIG_ZONE_DMA32 )#define GFP_VMALLOC32 GFP_DMA | GFP_KERNEL#else -#define GFP_VMALLOC32 GFP_KERNEL +#define GFP_VMALLOC32 GFP_DMA32 | GFP_KERNEL#endif /**
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 10 lines · Kai Heng Feng
Hi Matthew,
Quoted 5 lines · Matthew Wilcox
Alternatively, try this. It passes in
GFP_DMA32fromvmalloc_32, regardless of whetherZONE_DMA32exists or not. IfZONE_DMA32doesn't exist, then we clear it in__vmalloc_area_node(), after using it to determine that we shouldn't set__GFP_HIGHMEM.IIUC, I need to let
drivers/mediadrivers start usingvmalloc_32()with your patch, right?
Hopefully those that need to already are. Otherwise they're broken on 64-bit. I do see several places already using vmalloc_32().
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 19 lines · Matthew Wilcox
Quoted 7 lines · Matthew Wilcox
Quoted 2 lines · Kai Heng Feng
A user with i386 instead of AMD64 machine reports [1] that commit
19809c2da28a("mm, vmalloc: use__GFP_HIGHMEMimplicitly”) causes a regression. BUG_ON(PageHighMem(pg)) indrivers/media/common/saa7146/saa7146_core.calways gets triggered after that commit.Well, the
BUG_ONis wrong. You can absolutely have pages which are both HighMem and under the 4GB boundary. Only the first 896MB (iirc) are LowMem, and the next 3GB of pages are available tovmalloc_32().... nevertheless,
19809c2da28adoes in fact breakvmalloc_32on 32-bit. Look:#if
defined(CONFIG_64BIT)&&defined(CONFIG_ZONE_DMA32)#defineGFP_VMALLOC32GFP_DMA32|GFP_KERNEL#elifdefined(CONFIG_64BIT)&&defined(CONFIG_ZONE_DMA)#defineGFP_VMALLOC32GFP_DMA|GFP_KERNEL#else
#defineGFP_VMALLOC32GFP_KERNEL
#endif
Quoted 2 lines
So we pass in
GFP_KERNELto__vmalloc_node, which calls__vmalloc_node_rangewhich calls__vmalloc_area_node, which ORs in__GFP_HIGHMEM.
Dohh. I have missed this. I was convinced that we always add GFP_DMA32 when doing vmalloc_32. Sorry about that. The above definition looks quite weird to be honest. First of all do we have any 64b system without both DMA and DMA32 zones? If yes, what is the actual semantic of vmalloc_32? Or is there any magic forcing GFP_KERNEL into low 32b?
Also I would expect that __GFP_DMA32 should do the right thing on 32b systems. So something like the below should do the trick
---
mm/vmalloc.c +2 -1
@@ -1947,7 +1947,8 @@ void *vmalloc_exec(unsigned long size)#elif defined(CONFIG_64BIT ) && defined(CONFIG_ZONE_DMA )#define GFP_VMALLOC32 GFP_DMA | GFP_KERNEL#else -#define GFP_VMALLOC32 GFP_KERNEL +/* This should be only 32b systems */ +#define GFP_VMALLOC32 GFP_DMA32 | GFP_KERNEL#endif /**
Signature · 2 lines
Michal Hocko SUSE Labs
Quoted 11 lines · Michal Hocko
Quoted 10 lines · Matthew Wilcox
... nevertheless,
19809c2da28adoes in fact breakvmalloc_32on 32-bit. Look:#if
defined(CONFIG_64BIT)&&defined(CONFIG_ZONE_DMA32)#defineGFP_VMALLOC32GFP_DMA32|GFP_KERNEL#elifdefined(CONFIG_64BIT)&&defined(CONFIG_ZONE_DMA)#defineGFP_VMALLOC32GFP_DMA|GFP_KERNEL#else
#defineGFP_VMALLOC32GFP_KERNEL
#endif
Quoted 8 lines
Quoted 2 lines
So we pass in
GFP_KERNELto__vmalloc_node, which calls__vmalloc_node_rangewhich calls__vmalloc_area_node, which ORs in__GFP_HIGHMEM.Dohh. I have missed this. I was convinced that we always add
GFP_DMA32when doingvmalloc_32. Sorry about that. The above definition looks quite weird to be honest. First of all do we have any 64b system without both DMA and DMA32 zones? If yes, what is the actual semantic ofvmalloc_32? Or is there any magic forcingGFP_KERNELinto low 32b?
mmzone.h has the following, which may be inaccurate / out of date:
* parisc, ia64, sparc <4G
* s390 <2G
* arm Various
* alpha Unlimited or 0-16MB. *
* i386, x86_64 and multiple other arches
* <16M.
It claims ZONE_DMA32 is x86-64 only, which is incorrect; it's now used by arm64, ia64, mips, powerpc, tile.
Quoted 2 lines
Also I would expect that
__GFP_DMA32should do the right thing on 32b systems. So something like the below should do the trick
Oh, I see. Because we have:
#ifdef CONFIG_ZONE_DMA32
#define OPT_ZONE_DMA32 ZONE_DMA32
#else
#define OPT_ZONE_DMA32 ZONE_NORMAL
#endif
we'll end up allocating from ZONE_NORMAL if a non-DMA32 architecture asks for GFP_DMA32 memory. Thanks; I missed that.
I'd recommend this instead then:
#if defined(CONFIG_64BIT) && !defined(CONFIG_ZONE_DMA32) #define GFP_VMALLOC32 GFP_DMA | GFP_KERNEL #else
#define GFP_VMALLOC32 GFP_DMA32 | GFP_KERNEL #endif
I think it's clearer than the three-way #if.
Now, longer-term, perhaps we should do the following:
#ifdef CONFIG_ZONE_DMA32
#define OPT_ZONE_DMA32 ZONE_DMA32
#elif defined(CONFIG_64BIT)
#define OPT_ZONE_DMA OPT_ZONE_DMA
#else
#define OPT_ZONE_DMA32 ZONE_NORMAL
#endif
Then we wouldn't need the ifdef here and could always use GFP_DMA32 | GFP_KERNEL. Would need to audit current users and make sure they wouldn't be broken by such a change.
I noticed a mistake in 704b862f9efd;
- pages = __vmalloc_node(array_size, 1, nested_gfp|__GFP_HIGHMEM, + pages = __vmalloc_node(array_size, 1, nested_gfp|highmem_mask,
We should unconditionally use __GFP_HIGHMEM here instead of highmem_mask because this is where we allocate the array to hold the struct page pointers. This can be allocated from highmem, and does not need to be allocated from ZONE_NORMAL.
Similarly,
- if (gfpflags_allow_blocking(gfp_mask))
+ if (gfpflags_allow_blocking(gfp_mask|highmem_mask))
is not needed (it's not *wrong*, it was just an unnecessary change).
Quoted 24 lines
mm/vmalloc.c +2
-1@@ -1947,7 +1947,8 @@ void *vmalloc_exec(unsigned long size)#elif defined(CONFIG_64BIT ) && defined(CONFIG_ZONE_DMA )#define GFP_VMALLOC32 GFP_DMA | GFP_KERNEL#else -#define GFP_VMALLOC32 GFP_KERNEL +/* This should be only 32b systems */ +#define GFP_VMALLOC32 GFP_DMA32 | GFP_KERNEL#endif /** Signature · 9 lines
-- Michal Hocko SUSE Labs -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 10 lines · Matthew Wilcox
Now, longer-term, perhaps we should do the following:
#ifdef
CONFIG_ZONE_DMA32
#defineOPT_ZONE_DMA32ZONE_DMA32
#elifdefined(CONFIG_64BIT)
#defineOPT_ZONE_DMAOPT_ZONE_DMA
#else
#defineOPT_ZONE_DMA32ZONE_NORMAL
#endif
Quoted 3 lines
Then we wouldn't need the ifdef here and could always use
GFP_DMA32|GFP_KERNEL. Would need to audit current users and make sure they wouldn't be broken by such a change.
Argh, I forgot to say the most important thing. (For those newly invited to the party, we're talking about drivers/media, in particular drivers/media/common/saa7146/saa7146_core.c, functions saa7146_vmalloc_build_pgtable and vmalloc_to_sg)
I think we're missing a function in our DMA API. These drivers don't actually need physical memory below the 4GB mark. They need DMA addresses which are below the 4GB mark. For machines with IOMMUs, this can mean no restrictions on physical memory. If we don't have an IOMMU, then a bounce buffer could be used (but would be slow) -- like the swiotlb. So we should endeavour to allocate memory below the 4GB boundary on systems with no IOMMU, but can allocate memory anywhere on systems with an IOMMU.
For consistent / coherent memory, we have an allocation function. But we don't have an allocation function for streaming memory, which is what these drivers want. They also flush the DMA memory and then access the memory through a different virtual mapping, which I'm not sure is going to work well on virtually-indexed caches like SPARC and PA-RISC (maybe not MIPS either?)
I think we want something like
struct scatterlist *dma_alloc_sg(struct device *dev, int *nents); void dma_free_sg(struct device *dev, struct scatterlist *sg, int nents);
That lets individual architectures decide where to allocate, and handle the tradeoff between allocating below 4GB and using bounce buffers.
I don't have a good answer to synchronising between device-view of memory and CPU-view-through-vmalloc though. They're already calling dma_sync_*_for_cpu(); do they need to also call a new vflush(void *p, unsigned long len) function which can be a no-op on x86 and flushes the range on SPARC/PA-RISC/... ?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 11 lines · Matthew Wilcox
Quoted 9 lines · Matthew Wilcox
Now, longer-term, perhaps we should do the following:
#ifdef
CONFIG_ZONE_DMA32
#defineOPT_ZONE_DMA32ZONE_DMA32
#elifdefined(CONFIG_64BIT)
#defineOPT_ZONE_DMAOPT_ZONE_DMA
#else
#defineOPT_ZONE_DMA32ZONE_NORMAL
#endif
Quoted 6 lines
For consistent / coherent memory, we have an allocation function. But we don't have an allocation function for streaming memory, which is what these drivers want. They also flush the DMA memory and then access the memory through a different virtual mapping, which I'm not sure is going to work well on virtually-indexed caches like SPARC and PA-RISC (maybe not MIPS either?)
Perhaps I (and a number of other people ...) have misunderstood the semantics of GFP_DMA32. Perhaps GFP_DMA32 is not "allocate memory below 4GB", perhaps it's "allocate memory which can be mapped below 4GB". Machines with an IOMMU can use ZONE_NORMAL. Machines with no IOMMU can choose to allocate memory with a physical address below 4GB.
After all, it has 'DMA' right there in the name. If someone's relying on it to allocate physical memory below 4GB, they're arguably misusing it.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 19 lines · Matthew Wilcox
Quoted 11 lines · Matthew Wilcox
Quoted 9 lines · Matthew Wilcox
Now, longer-term, perhaps we should do the following:
#ifdef
CONFIG_ZONE_DMA32
#defineOPT_ZONE_DMA32ZONE_DMA32
#elifdefined(CONFIG_64BIT)
#defineOPT_ZONE_DMAOPT_ZONE_DMA
#else
#defineOPT_ZONE_DMA32ZONE_NORMAL
#endifQuoted 6 lines
For consistent / coherent memory, we have an allocation function. But we don't have an allocation function for streaming memory, which is what these drivers want. They also flush the DMA memory and then access the memory through a different virtual mapping, which I'm not sure is going to work well on virtually-indexed caches like SPARC and PA-RISC (maybe not MIPS either?)
Quoted 3 lines
Perhaps I (and a number of other people ...) have misunderstood the semantics of
GFP_DMA32. PerhapsGFP_DMA32is not "allocate memory below 4GB", perhaps it's "allocate memory which can be mapped below 4GB".
Well, GFP_DMA32 is clearly under-documented. But I _believe_ the intention was to really return a physical memory within 32b address range.
Quoted 2 lines
Machines with an IOMMU can use
ZONE_NORMAL. Machines with no IOMMU can choose to allocate memory with a physical address below 4GB.
This would be something for the higher level allocator I think. The page allocator is largely unaware of IOMMU or any remapping and that is good IMHO.
Quoted 1 line
After all, it has 'DMA' right there in the name.
The name is misnomer following GFP_DMA which is arguably a better fit. GFP_MEM32 would be a better name.
Btw. I believe the GFP_VMALLOC32 shows that our GFP_DM32 needs some love. The user shouldn't really care about lowmem zones layout. GFP_DMA32 should simply use the appropriate zone regardless the arch specific details.
Signature · 2 lines
Michal Hocko SUSE Labs
[I am crawling over a large backlog after vacation so I will get to
other emails in this thread later. Let's just fix the regression
first. The patch with the full changelog is at the end of this email.
CC Andrew - the original report is http://lkml.kernel.org/r/[email protected]]
Quoted 27 lines · Matthew Wilcox
Quoted 11 lines · Michal Hocko
Quoted 10 lines · Matthew Wilcox
... nevertheless,
19809c2da28adoes in fact breakvmalloc_32on 32-bit. Look:#if
defined(CONFIG_64BIT)&&defined(CONFIG_ZONE_DMA32)#defineGFP_VMALLOC32GFP_DMA32|GFP_KERNEL#elifdefined(CONFIG_64BIT)&&defined(CONFIG_ZONE_DMA)#defineGFP_VMALLOC32GFP_DMA|GFP_KERNEL#else
#defineGFP_VMALLOC32GFP_KERNEL
#endifQuoted 8 lines
Quoted 2 lines
So we pass in
GFP_KERNELto__vmalloc_node, which calls__vmalloc_node_rangewhich calls__vmalloc_area_node, which ORs in__GFP_HIGHMEM.Dohh. I have missed this. I was convinced that we always add
GFP_DMA32when doingvmalloc_32. Sorry about that. The above definition looks quite weird to be honest. First of all do we have any 64b system without both DMA and DMA32 zones? If yes, what is the actual semantic ofvmalloc_32? Or is there any magic forcingGFP_KERNELinto low 32b?
mmzone.hhas the following, which may be inaccurate / out of date:* parisc, ia64, sparc <4G * s390 <2G * arm Various * alpha Unlimited or 0-16MB.
Quoted 6 lines
*
* i386,x86_64and multiple other arches
* <16M.It claims
ZONE_DMA32is x86-64 only, which is incorrect; it's now used by arm64, ia64, mips, powerpc, tile.
yes, nobody seem to keep this one in sync.
Quoted 5 lines
Quoted 2 lines
Also I would expect that
__GFP_DMA32should do the right thing on 32b systems. So something like the below should do the trickOh, I see. Because we have:
Quoted 8 lines
#ifdef
CONFIG_ZONE_DMA32
#defineOPT_ZONE_DMA32ZONE_DMA32
#else
#defineOPT_ZONE_DMA32ZONE_NORMAL
#endifwe'll end up allocating from
ZONE_NORMALif a non-DMA32 architecture asks forGFP_DMA32memory. Thanks; I missed that.
yep
Quoted 9 lines
I'd recommend this instead then:
#if
defined(CONFIG_64BIT)&& !defined(CONFIG_ZONE_DMA32) #defineGFP_VMALLOC32GFP_DMA|GFP_KERNEL#else
#defineGFP_VMALLOC32GFP_DMA32|GFP_KERNEL#endifI think it's clearer than the three-way #if.
I do not have a strong opinion here. I just wanted the change to be obvious without meddling with the 64b ifdefs much. Follow up cleanups are certainly possible.
Quoted 10 lines
Now, longer-term, perhaps we should do the following:
#ifdef
CONFIG_ZONE_DMA32
#defineOPT_ZONE_DMA32ZONE_DMA32
#elifdefined(CONFIG_64BIT)
#defineOPT_ZONE_DMAOPT_ZONE_DMA
#else
#defineOPT_ZONE_DMA32ZONE_NORMAL
#endif
Quoted 3 lines
Then we wouldn't need the ifdef here and could always use
GFP_DMA32|GFP_KERNEL. Would need to audit current users and make sure they wouldn't be broken by such a change.
I am pretty sure improvements are possible.
Quoted 9 lines
I noticed a mistake in
704b862f9efd;(quoted diff) +1
-1- pages = __vmalloc_node(array_size, 1, nested_gfp|__GFP_HIGHMEM, + pages = __vmalloc_node(array_size, 1, nested_gfp|highmem_mask,We should unconditionally use
__GFP_HIGHMEMhere instead ofhighmem_maskbecause this is where we allocate the array to hold the struct page pointers. This can be allocated from highmem, and does not need to be allocated fromZONE_NORMAL.
You seem to be right. nested_gfp doesn't include zone modifiers. Care to send a patch?
Quoted 6 lines
Similarly,
(quoted diff) +1
-1- if (gfpflags_allow_blocking(gfp_mask)) + if (gfpflags_allow_blocking(gfp_mask|highmem_mask))is not needed (it's not *wrong*, it was just an unnecessary change).
yes. highmem_mask has no influence on the blocking behavior.
The fix for the regressions should be