* [PATCH net] hv_netvsc: use kmap_local_page in netvsc_copy_to_send_buf
@ 2026-06-02 15:52 LeantionX
2026-06-02 22:54 ` [EXTERNAL] " Haiyang Zhang
2026-06-03 16:38 ` [PATCH net v2] " Anton Leontev
0 siblings, 2 replies; 5+ messages in thread
From: LeantionX @ 2026-06-02 15:52 UTC (permalink / raw)
To: netdev
Cc: linux-hyperv, kys, haiyangz, wei.liu, decui, longli,
andrew+netdev, kuba, pabeni, edumazet, davem, stable,
linux-kernel, Anton Leontev
From: Anton Leontev <leontyevantony@gmail.com>
netvsc_copy_to_send_buf() copies skb fragment pages into the shared
VMBus send buffer using phys_to_virt() on the fragment PFN. On 32-bit
x86 with CONFIG_HIGHMEM=y, phys_to_virt() (i.e. __va()) is only valid
for LOWMEM addresses below 896 MiB. For a HIGHMEM page it returns an
address that has no kernel page table entry and lies outside the
kernel direct map, so the subsequent memcpy() faults. As this happens
on the transmit softirq path, the fault is fatal.
A HIGHMEM fragment reaches this path whenever the page backing an skb
fragment lives above the LOWMEM boundary, which is common on a 32-bit
guest with several GiB of RAM (for example when the in-kernel NFS
server splices page cache pages directly into the reply skb).
Map the fragment page on demand with kmap_local_page()/kunmap_local()
instead. Using pfn_to_page() on pb[i].pfn maps exactly the page
described by the page buffer entry. On configurations without HIGHMEM
(amd64, i386 without CONFIG_HIGHMEM) kmap_local_page() reduces to
page_address(), so this is a no-op there.
Fixes: c25aaf814a63 ("hyperv: Enable sendbuf mechanism on the send path")
Cc: stable@vger.kernel.org
Signed-off-by: Anton Leontev <leontyevantony@gmail.com>
---
drivers/net/hyperv/netvsc.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 59e95341f9b1..6984f6c97257 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -12,6 +12,7 @@
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mm.h>
+#include <linux/highmem.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -965,11 +966,13 @@ static void netvsc_copy_to_send_buf(struct netvsc_device *net_device,
}
for (i = 0; i < page_count; i++) {
- char *src = phys_to_virt(pb[i].pfn << HV_HYP_PAGE_SHIFT);
+ struct page *page = pfn_to_page(pb[i].pfn);
+ char *src = kmap_local_page(page);
u32 offset = pb[i].offset;
u32 len = pb[i].len;
memcpy(dest, (src + offset), len);
+ kunmap_local(src);
dest += len;
}
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread* RE: [EXTERNAL] [PATCH net] hv_netvsc: use kmap_local_page in netvsc_copy_to_send_buf
2026-06-02 15:52 [PATCH net] hv_netvsc: use kmap_local_page in netvsc_copy_to_send_buf LeantionX
@ 2026-06-02 22:54 ` Haiyang Zhang
2026-06-03 5:29 ` Anton Leontev
2026-06-03 16:38 ` [PATCH net v2] " Anton Leontev
1 sibling, 1 reply; 5+ messages in thread
From: Haiyang Zhang @ 2026-06-02 22:54 UTC (permalink / raw)
To: LeantionX, netdev
Cc: linux-hyperv, KY Srinivasan, wei.liu, Dexuan Cui, Long Li,
andrew+netdev, kuba, pabeni, edumazet, davem, stable,
linux-kernel
> -----Original Message-----
> From: LeantionX <leontyevantony@gmail.com>
> Sent: Tuesday, June 2, 2026 11:52 AM
> To: netdev@vger.kernel.org
> Cc: linux-hyperv@vger.kernel.org; KY Srinivasan <kys@microsoft.com>;
> Haiyang Zhang <haiyangz@microsoft.com>; wei.liu@kernel.org; Dexuan Cui
> <DECUI@microsoft.com>; Long Li <longli@microsoft.com>;
> andrew+netdev@lunn.ch; kuba@kernel.org; pabeni@redhat.com;
> edumazet@google.com; davem@davemloft.net; stable@vger.kernel.org; linux-
> kernel@vger.kernel.org; Anton Leontev <leontyevantony@gmail.com>
> Subject: [EXTERNAL] [PATCH net] hv_netvsc: use kmap_local_page in
> netvsc_copy_to_send_buf
>
> [You don't often get email from leontyevantony@gmail.com. Learn why this
> is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> From: Anton Leontev <leontyevantony@gmail.com>
>
> netvsc_copy_to_send_buf() copies skb fragment pages into the shared
> VMBus send buffer using phys_to_virt() on the fragment PFN. On 32-bit
> x86 with CONFIG_HIGHMEM=y, phys_to_virt() (i.e. __va()) is only valid
> for LOWMEM addresses below 896 MiB. For a HIGHMEM page it returns an
> address that has no kernel page table entry and lies outside the
> kernel direct map, so the subsequent memcpy() faults. As this happens
> on the transmit softirq path, the fault is fatal.
Please include the stack trace in patch description.
> A HIGHMEM fragment reaches this path whenever the page backing an skb
> fragment lives above the LOWMEM boundary, which is common on a 32-bit
> guest with several GiB of RAM (for example when the in-kernel NFS
> server splices page cache pages directly into the reply skb).
>
> Map the fragment page on demand with kmap_local_page()/kunmap_local()
> instead. Using pfn_to_page() on pb[i].pfn maps exactly the page
> described by the page buffer entry. On configurations without HIGHMEM
> (amd64, i386 without CONFIG_HIGHMEM) kmap_local_page() reduces to
> page_address(), so this is a no-op there.
So, on 64bit kernel, it has no performance impact?
Thanks,
- Haiyang
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [EXTERNAL] [PATCH net] hv_netvsc: use kmap_local_page in netvsc_copy_to_send_buf
2026-06-02 22:54 ` [EXTERNAL] " Haiyang Zhang
@ 2026-06-03 5:29 ` Anton Leontev
0 siblings, 0 replies; 5+ messages in thread
From: Anton Leontev @ 2026-06-03 5:29 UTC (permalink / raw)
To: Haiyang Zhang
Cc: netdev, linux-hyperv, KY Srinivasan, wei.liu, Dexuan Cui,
Long Li, andrew+netdev, kuba, pabeni, edumazet, davem, stable,
linux-kernel
>
>
>
> > -----Original Message-----
> > From: LeantionX <leontyevantony@gmail.com>
> > Sent: Tuesday, June 2, 2026 11:52 AM
> > To: netdev@vger.kernel.org
> > Cc: linux-hyperv@vger.kernel.org; KY Srinivasan <kys@microsoft.com>;
> > Haiyang Zhang <haiyangz@microsoft.com>; wei.liu@kernel.org; Dexuan Cui
> > <DECUI@microsoft.com>; Long Li <longli@microsoft.com>;
> > andrew+netdev@lunn.ch; kuba@kernel.org; pabeni@redhat.com;
> > edumazet@google.com; davem@davemloft.net; stable@vger.kernel.org; linux-
> > kernel@vger.kernel.org; Anton Leontev <leontyevantony@gmail.com>
> > Subject: [EXTERNAL] [PATCH net] hv_netvsc: use kmap_local_page in
> > netvsc_copy_to_send_buf
> >
> > [You don't often get email from leontyevantony@gmail.com. Learn why this
> > is important at https://aka.ms/LearnAboutSenderIdentification ]
> >
> > From: Anton Leontev <leontyevantony@gmail.com>
> >
> > netvsc_copy_to_send_buf() copies skb fragment pages into the shared
> > VMBus send buffer using phys_to_virt() on the fragment PFN. On 32-bit
> > x86 with CONFIG_HIGHMEM=y, phys_to_virt() (i.e. __va()) is only valid
> > for LOWMEM addresses below 896 MiB. For a HIGHMEM page it returns an
> > address that has no kernel page table entry and lies outside the
> > kernel direct map, so the subsequent memcpy() faults. As this happens
> > on the transmit softirq path, the fault is fatal.
> Please include the stack trace in patch description.
>
> > A HIGHMEM fragment reaches this path whenever the page backing an skb
> > fragment lives above the LOWMEM boundary, which is common on a 32-bit
> > guest with several GiB of RAM (for example when the in-kernel NFS
> > server splices page cache pages directly into the reply skb).
> >
> > Map the fragment page on demand with kmap_local_page()/kunmap_local()
> > instead. Using pfn_to_page() on pb[i].pfn maps exactly the page
> > described by the page buffer entry. On configurations without HIGHMEM
> > (amd64, i386 without CONFIG_HIGHMEM) kmap_local_page() reduces to
> > page_address(), so this is a no-op there.
>
> So, on 64bit kernel, it has no performance impact?
>
> Thanks,
> - Haiyang
>
Correct. On 64-bit (and any !CONFIG_HIGHMEM config) all pages are
permanently present in the kernel direct map, so kmap_local_page()
folds to page_address() and kunmap_local() is a no-op. The generated
code is therefore equivalent to the previous direct-map access, with
no extra mapping cost on the tx path.
The kmap is only meaningful on 32-bit CONFIG_HIGHMEM, where the
fragment page may live above the LOWMEM boundary and the old
phys_to_virt() result is invalid.
Thanks,
Anton
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v2] hv_netvsc: use kmap_local_page in netvsc_copy_to_send_buf
2026-06-02 15:52 [PATCH net] hv_netvsc: use kmap_local_page in netvsc_copy_to_send_buf LeantionX
2026-06-02 22:54 ` [EXTERNAL] " Haiyang Zhang
@ 2026-06-03 16:38 ` Anton Leontev
1 sibling, 0 replies; 5+ messages in thread
From: Anton Leontev @ 2026-06-03 16:38 UTC (permalink / raw)
To: netdev
Cc: linux-hyperv, haiyangz, kys, wei.liu, decui, longli, kuba,
pabeni, edumazet, davem, stable, linux-kernel, Anton Leontev
netvsc_copy_to_send_buf() copies skb fragment pages into the shared
VMBus send buffer using phys_to_virt() on the fragment PFN. On 32-bit
x86 with CONFIG_HIGHMEM=y, phys_to_virt() (i.e. __va()) is only valid
for LOWMEM addresses below 896 MiB. For a HIGHMEM page it returns an
address that has no kernel page table entry and lies outside the
kernel direct map, so the subsequent memcpy() faults. As this happens
on the transmit softirq path, the fault is fatal.
A HIGHMEM fragment reaches this path whenever the page backing an skb
fragment lives above the LOWMEM boundary, which is common on a 32-bit
guest with several GiB of RAM (for example when the in-kernel NFS
server splices page cache pages directly into the reply skb).
pb[i].pfn is a Hyper-V PFN at HV_HYP_PAGE_SIZE (4K) granularity. The
physical address is reconstructed first and phys_to_page() is used to
obtain the native struct page, with offset_in_page() added so the
in-page offset stays correct where PAGE_SIZE > HV_HYP_PAGE_SIZE (e.g.
arm64 with 64K pages). The page is then mapped on demand with
kmap_local_page()/kunmap_local(). On !CONFIG_HIGHMEM configs
kmap_local_page() reduces to page_address(), so this is a no-op there.
Fixes: c25aaf814a63 ("hyperv: Enable sendbuf mechanism on the send path")
Cc: stable@vger.kernel.org
Signed-off-by: Anton Leontev <leontyevantony@gmail.com>
---
v2:
- Reconstruct the physical address from the Hyper-V PFN and use
phys_to_page() + offset_in_page() instead of pfn_to_page() on the
raw PFN, correct where PAGE_SIZE > 4K (e.g. arm64 64K pages).
Reported by Haiyang Zhang.
- Built for i386 (CONFIG_HIGHMEM) and arm64 (64K pages).
drivers/net/hyperv/netvsc.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 59e95341f9b1..2038d9f5c9f9 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -12,6 +12,7 @@
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mm.h>
+#include <linux/highmem.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -965,11 +966,14 @@ static void netvsc_copy_to_send_buf(struct netvsc_device *net_device,
}
for (i = 0; i < page_count; i++) {
- char *src = phys_to_virt(pb[i].pfn << HV_HYP_PAGE_SHIFT);
- u32 offset = pb[i].offset;
+ phys_addr_t paddr = pb[i].pfn << HV_HYP_PAGE_SHIFT;
+ struct page *page = phys_to_page(paddr);
+ u32 offset = offset_in_page(paddr) + pb[i].offset;
u32 len = pb[i].len;
+ char *src = kmap_local_page(page);
memcpy(dest, (src + offset), len);
+ kunmap_local(src);
dest += len;
}
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v2] hv_netvsc: use kmap_local_page in netvsc_copy_to_send_buf
@ 2026-06-03 17:25 Anton Leontev
0 siblings, 0 replies; 5+ messages in thread
From: Anton Leontev @ 2026-06-03 17:25 UTC (permalink / raw)
To: netdev
Cc: linux-hyperv, haiyangz, kys, wei.liu, decui, longli, kuba,
pabeni, edumazet, davem, stable, linux-kernel, Anton Leontev
netvsc_copy_to_send_buf() copies skb fragment pages into the shared
VMBus send buffer using phys_to_virt() on the fragment PFN. On 32-bit
x86 with CONFIG_HIGHMEM=y, phys_to_virt() (i.e. __va()) is only valid
for LOWMEM addresses below 896 MiB. For a HIGHMEM page it returns an
address that has no kernel page table entry and lies outside the
kernel direct map, so the subsequent memcpy() faults. As this happens
on the transmit softirq path, the fault is fatal.
A HIGHMEM fragment reaches this path whenever the page backing an skb
fragment lives above the LOWMEM boundary, which is common on a 32-bit
guest with several GiB of RAM (for example when the in-kernel NFS
server splices page cache pages directly into the reply skb).
pb[i].pfn is a Hyper-V PFN at HV_HYP_PAGE_SIZE (4K) granularity. The
physical address is reconstructed first and phys_to_page() is used to
obtain the native struct page, with offset_in_page() added so the
in-page offset stays correct where PAGE_SIZE > HV_HYP_PAGE_SIZE (e.g.
arm64 with 64K pages). The page is then mapped on demand with
kmap_local_page()/kunmap_local(). On !CONFIG_HIGHMEM configs
kmap_local_page() reduces to page_address(), so this is a no-op there.
Fixes: c25aaf814a63 ("hyperv: Enable sendbuf mechanism on the send path")
Cc: stable@vger.kernel.org
Signed-off-by: Anton Leontev <leontyevantony@gmail.com>
---
v2:
- Reconstruct the physical address from the Hyper-V PFN and use
phys_to_page() + offset_in_page() instead of pfn_to_page() on the
raw PFN, correct where PAGE_SIZE > 4K (e.g. arm64 64K pages).
Reported by Haiyang Zhang.
- Built for i386 (CONFIG_HIGHMEM) and arm64 (64K pages).
drivers/net/hyperv/netvsc.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 59e95341f9b1..2038d9f5c9f9 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -12,6 +12,7 @@
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mm.h>
+#include <linux/highmem.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -965,11 +966,14 @@ static void netvsc_copy_to_send_buf(struct netvsc_device *net_device,
}
for (i = 0; i < page_count; i++) {
- char *src = phys_to_virt(pb[i].pfn << HV_HYP_PAGE_SHIFT);
- u32 offset = pb[i].offset;
+ phys_addr_t paddr = pb[i].pfn << HV_HYP_PAGE_SHIFT;
+ struct page *page = phys_to_page(paddr);
+ u32 offset = offset_in_page(paddr) + pb[i].offset;
u32 len = pb[i].len;
+ char *src = kmap_local_page(page);
memcpy(dest, (src + offset), len);
+ kunmap_local(src);
dest += len;
}
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-06-03 17:25 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-02 15:52 [PATCH net] hv_netvsc: use kmap_local_page in netvsc_copy_to_send_buf LeantionX
2026-06-02 22:54 ` [EXTERNAL] " Haiyang Zhang
2026-06-03 5:29 ` Anton Leontev
2026-06-03 16:38 ` [PATCH net v2] " Anton Leontev
2026-06-03 17:25 Anton Leontev
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®