I'm expecting an upstream fix for this problem soon, based on this discussion (lkml):
From Dexuan Cui:
"d1cd12108346: x86, pageattr: Prevent overflow in slow_virt_to_phys() for X86_PAE"
was unintentionally removed by the recent
"34437e67a672: x86/mm: Fix slow_virt_to_phys() to handle large PAT bit".
And, the variable 'phys_addr' was defined as "unsigned long" by mistake -- it should
be "phys_addr_t".
As a result, Hyper-V network driver in 32-PAE Linux guest can't work again.
+ /*
+ * pXX_pfn() returns unsigned long, which must be cast to phys_addr_t
+ * before being left-shifted PAGE_SHIFT bits -- this trick is to
+ * make 32-PAE kernel work correctly.
+ */
switch (level) {
case PG_LEVEL_1G:
- phys_addr = pud_pfn(*(pud_t *)pte) << PAGE_SHIFT;
+ phys_addr = (phys_addr_t)pud_pfn(*(pud_t *)pte) << PAGE_SHIFT; offset = virt_addr & ~PUD_PAGE_MASK; break;
case PG_LEVEL_2M:
- phys_addr = pmd_pfn(*(pmd_t *)pte) << PAGE_SHIFT;
+ phys_addr = (phys_addr_t)pmd_pfn(*(pmd_t *)pte) << PAGE_SHIFT; offset = virt_addr & ~PMD_PAGE_MASK; break;
default:
- phys_addr = pte_pfn(*pte) << PAGE_SHIFT;
+ phys_addr = (phys_addr_t)pte_pfn(*pte) << PAGE_SHIFT; offset = virt_addr & ~PAGE_MASK;
}
From Toshi Kani:
On Thu, 2016-02-25 at 01:58 -0800, Dexuan Cui wrote:
> "d1cd12108346: x86, pageattr: Prevent overflow in slow_virt_to_phys() for
> X86_PAE"
> was unintentionally removed by the recent
> "34437e67a672: x86/mm: Fix slow_virt_to_phys() to handle large PAT bit".
>
> And, the variable 'phys_addr' was defined as "unsigned long" by mistake
> -- it should
> be "phys_addr_t".
>
> As a result, Hyper-V network driver in 32-PAE Linux guest can't work
> again.
>
> Fixes: "commmit 34437e67a672: x86/mm: Fix slow_virt_to_phys() to handle
> large PAT bit"
> Signed-off-by: Dexuan Cui <email address hidden>
> Cc: Toshi Kani <email address hidden>
> Cc: Andrew Morton <email address hidden>
> Cc: Thomas Gleixner <email address hidden>
> Cc: K. Y. Srinivasan <email address hidden>
> Cc: Haiyang Zhang <email address hidden>
> Cc: <email address hidden>
> Cc: <email address hidden>
> Cc: <email address hidden>
> Cc: <email address hidden>
> Cc: <email address hidden>
> Cc: <email address hidden>
Thanks for the fix and adding the comment to explain the trick! The change
looks good to me.
I'm expecting an upstream fix for this problem soon, based on this discussion (lkml):
From Dexuan Cui:
"d1cd12108346: x86, pageattr: Prevent overflow in slow_virt_to_phys() for X86_PAE"
was unintentionally removed by the recent
"34437e67a672: x86/mm: Fix slow_virt_to_phys() to handle large PAT bit".
And, the variable 'phys_addr' was defined as "unsigned long" by mistake -- it should
be "phys_addr_t".
As a result, Hyper-V network driver in 32-PAE Linux guest can't work again.
Fixes: "commmit 34437e67a672: x86/mm: Fix slow_virt_to_phys() to handle large PAT bit" mm/pageattr. c | 14 ++++++++++----
Signed-off-by: Dexuan Cui <email address hidden>
Cc: Toshi Kani <email address hidden>
Cc: Andrew Morton <email address hidden>
Cc: Thomas Gleixner <email address hidden>
Cc: K. Y. Srinivasan <email address hidden>
Cc: Haiyang Zhang <email address hidden>
Cc: <email address hidden>
Cc: <email address hidden>
Cc: <email address hidden>
Cc: <email address hidden>
Cc: <email address hidden>
Cc: <email address hidden>
---
arch/x86/
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/arch/ x86/mm/ pageattr. c b/arch/ x86/mm/ pageattr. c x86/mm/ pageattr. c x86/mm/ pageattr. c pmd_address( unsigned long address) to_phys( void *__virt_addr)
index 2440814..9cf96d8 100644
--- a/arch/
+++ b/arch/
@@ -419,24 +419,30 @@ pmd_t *lookup_
phys_addr_t slow_virt_
{
unsigned long virt_addr = (unsigned long)__virt_addr;
- unsigned long phys_addr, offset;
+ phys_addr_t phys_addr;
+ unsigned long offset;
enum pg_level level;
pte_t *pte;
pte = lookup_ address( virt_addr, &level);
BUG_ON( !pte);
+ /* t)pud_pfn( *(pud_t *)pte) << PAGE_SHIFT;
offset = virt_addr & ~PUD_PAGE_MASK;
break; t)pmd_pfn( *(pmd_t *)pte) << PAGE_SHIFT;
offset = virt_addr & ~PMD_PAGE_MASK;
break; t)pte_pfn( *pte) << PAGE_SHIFT;
offset = virt_addr & ~PAGE_MASK;
+ * pXX_pfn() returns unsigned long, which must be cast to phys_addr_t
+ * before being left-shifted PAGE_SHIFT bits -- this trick is to
+ * make 32-PAE kernel work correctly.
+ */
switch (level) {
case PG_LEVEL_1G:
- phys_addr = pud_pfn(*(pud_t *)pte) << PAGE_SHIFT;
+ phys_addr = (phys_addr_
case PG_LEVEL_2M:
- phys_addr = pmd_pfn(*(pmd_t *)pte) << PAGE_SHIFT;
+ phys_addr = (phys_addr_
default:
- phys_addr = pte_pfn(*pte) << PAGE_SHIFT;
+ phys_addr = (phys_addr_
}
From Toshi Kani:
On Thu, 2016-02-25 at 01:58 -0800, Dexuan Cui wrote:
> "d1cd12108346: x86, pageattr: Prevent overflow in slow_virt_to_phys() for
> X86_PAE"
> was unintentionally removed by the recent
> "34437e67a672: x86/mm: Fix slow_virt_to_phys() to handle large PAT bit".
>
> And, the variable 'phys_addr' was defined as "unsigned long" by mistake
> -- it should
> be "phys_addr_t".
>
> As a result, Hyper-V network driver in 32-PAE Linux guest can't work
> again.
>
> Fixes: "commmit 34437e67a672: x86/mm: Fix slow_virt_to_phys() to handle
> large PAT bit"
> Signed-off-by: Dexuan Cui <email address hidden>
> Cc: Toshi Kani <email address hidden>
> Cc: Andrew Morton <email address hidden>
> Cc: Thomas Gleixner <email address hidden>
> Cc: K. Y. Srinivasan <email address hidden>
> Cc: Haiyang Zhang <email address hidden>
> Cc: <email address hidden>
> Cc: <email address hidden>
> Cc: <email address hidden>
> Cc: <email address hidden>
> Cc: <email address hidden>
> Cc: <email address hidden>
Thanks for the fix and adding the comment to explain the trick! The change
looks good to me.
Reviewed-by: Toshi Kani <email address hidden>