* Please backport: DSA taggers OOB read on PACKET_QDISC_BYPASS TX
From: Doruk Tan Ozturk @ 2026-07-14 16:46 UTC (permalink / raw)
To: stable; +Cc: olteanv, andrew, f.fainelli, davem, edumazet, kuba, pabeni,
netdev
Please backport the following mainline commits to the stable trees.
Reason: the ocelot, ksz and sja1105 DSA taggers dereference
eth_hdr(skb)/skb_mac_header(skb) on their TX paths. skb->mac_header is
not set on the AF_PACKET SOCK_RAW + PACKET_QDISC_BYPASS transmit path
(packet_direct_xmit() -> netdev_start_xmit(), which bypasses the
dev_hard_start_xmit() reset from 6d1ccff62780), so eth_hdr(skb) resolves
~64 KB out of bounds -> out-of-bounds read. The fixes below make these
taggers read the header from skb->data instead. Reproducible with an
unmodified CONFIG_NET_DSA_LOOP=y kernel by sending on a raw packet
socket with PACKET_QDISC_BYPASS set.
These commits went into v6.4 without a Cc: stable tag because they were
made as preparation for reverting 6d1ccff62780 and the bug was assumed
to be future-only; it is not -- the bypass path was always unaffected by
that reset. 6.6.y and 6.12.y already carry them.
Prerequisite (helper, not a fix on its own; needed or the ocelot and
sja1105 fixes will not build on pre-v6.4 trees):
1f5020acb33f ("net: vlan: introduce skb_vlan_eth_hdr()")
Fixes, in mainline order:
eabb1494c9f2 ("net: dsa: tag_ocelot: do not rely on skb_mac_header() for VLAN xmit")
499b2491d550 ("net: dsa: tag_ksz: do not rely on skb_mac_header() in TX paths")
f9346f00b5af ("net: dsa: tag_sja1105: don't rely on skb_mac_header() in TX paths")
0bcf2e4aca6c ("net: dsa: tag_ocelot: call only the relevant portion of __skb_vlan_pop() on TX")
Not all fixes apply to all trees (the vulnerable code was introduced at
different times). Per tree:
6.1.y: 1f5020acb33f, eabb1494c9f2, 499b2491d550, f9346f00b5af, 0bcf2e4aca6c
5.15.y: 1f5020acb33f, 499b2491d550, f9346f00b5af
(tag_ocelot has no ocelot_xmit_get_vlan_info() before v5.16)
5.10.y: 499b2491d550
(sja1105_pvid_tag_control_pkt() is v5.15+; ocelot is v5.16+;
skb_eth_hdr() already present, so no prerequisite needed)
Ordering: apply 1f5020acb33f before eabb1494c9f2/f9346f00b5af, and
eabb1494c9f2 before 0bcf2e4aca6c.
5.4.y is EOL and also lacks skb_eth_hdr(); not requested.
Thanks,
Doruk Ozturk
^ permalink raw reply
* [PATCH net v3] nfc: llcp: reject PDUs shorter than the LLCP header
From: Doruk Tan Ozturk @ 2026-07-14 16:46 UTC (permalink / raw)
To: david
Cc: vadim.fedorenko, horms, david.laight.linux, oe-linux-nfc, netdev,
linux-kernel, stable
Every LLCP PDU begins with a two-byte header (DSAP/SSAP + PTYPE), but the
receive path never checked that a frame is at least LLCP_HEADER_SIZE bytes
before parsing it.
nfc_llcp_rx_skb() reads the header via nfc_llcp_ptype()/nfc_llcp_dsap()/
nfc_llcp_ssap(), which dereference pdu->data[0] and pdu->data[1], and a
CONNECT or CC PDU then computes
tlv_array_len = skb->len - LLCP_HEADER_SIZE;
as a size_t and hands it to the TLV walk. When the frame is shorter than
the header the subtraction wraps to a huge value and the walk runs far
past the buffer, an out-of-bounds read.
A nearby NFC device can reach this without authentication; LLCP link
activation happens automatically after NFC-DEP.
Guard the common receive choke point __nfc_llcp_recv(), shared by both the
target (nfc_llcp_data_received()) and initiator (nfc_llcp_recv()) paths, so
a short skb is dropped before the rx_work worker parses it. Use
pskb_may_pull() rather than a skb->len test so the two header bytes are
guaranteed to sit in the skb linear area even for a non-linear skb,
matching how the sibling NCI and HCI receive paths validate their headers.
Reproduced with a KFENCE out-of-bounds read via /dev/virtual_nci on
linux-next.
Found by 0sec automated security-research tooling (https://0sec.ai).
Fixes: d646960f7986 ("NFC: Initial LLCP support")
Cc: stable@vger.kernel.org
Suggested-by: David Laight <david.laight.linux@gmail.com>
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
---
v3: use pskb_may_pull() so the guard also covers non-linear skbs and
guarantees the header bytes are in the linear area (David Laight).
v2: move the guard into __nfc_llcp_recv() so both the target and
initiator receive paths are covered by a single check.
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index aed5fe1afef0..e3b2627cb089 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -1565,6 +1565,11 @@ static void nfc_llcp_rx_work(struct work_struct *work)
static void __nfc_llcp_recv(struct nfc_llcp_local *local, struct sk_buff *skb)
{
+ if (!pskb_may_pull(skb, LLCP_HEADER_SIZE)) {
+ kfree_skb(skb);
+ return;
+ }
+
local->rx_pending = skb;
timer_delete(&local->link_timer);
schedule_work(&local->rx_work);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH net v2] nfc: llcp: reject PDUs shorter than the LLCP header
From: Doruk Tan Ozturk @ 2026-07-14 16:46 UTC (permalink / raw)
To: david.laight.linux
Cc: david, vadim.fedorenko, horms, oe-linux-nfc, netdev, linux-kernel,
stable
In-Reply-To: <20260713221556.13a830b8@pumpkin>
> Is there a similar problem with non-linear skb?
> Maybe they can't get into this code, but who knows what can happen
> with unusual configs.
Good question. Today every skb that reaches __nfc_llcp_recv() is
linear: the target path (nci_rx_data_packet -> nci_add_rx_data_frag ->
nfc_tm_data_received) and the initiator path (nfc_data_exchange ->
nfc_llcp_recv) both build the frame with alloc_skb()/nci_skb_alloc()
plus skb_put()/skb_put_data(), and NCI reassembly uses skb_cow_head()
and skb_push() into the linear area. Nothing on the NFC receive side
attaches page frags or a frag_list, so skb->len == skb_headlen() and the
v2 skb->len test was in fact sufficient for the in-tree drivers.
But relying on that is fragile: the parser reads the header out of the
linear area (pdu->data[0]/data[1]) while skb->len is the total length,
so a non-linear skb with a short linear head would slip past a skb->len
test and still over-read the linear buffer. pskb_may_pull() is the
right guard here -- it also covers the non-linear case, and it matches
how the sibling NCI and HCI receive paths already validate their
headers.
I will send a v3 that uses:
if (!pskb_may_pull(skb, LLCP_HEADER_SIZE)) {
kfree_skb(skb);
return;
}
That is strictly stronger than the v2 check and does not reject any
valid frame -- pskb_may_pull() pulls the two header bytes into the
linear area when needed.
Thanks,
Doruk
^ permalink raw reply
* Re: [PATCH] idpf: disable PCIe PTM on device removal
From: Tantilov, Emil S @ 2026-07-14 16:36 UTC (permalink / raw)
To: Myeonghun Pak, Tony Nguyen, Przemek Kitszel, intel-wired-lan
Cc: Milena Olech, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, linux-kernel, Ijae Kim
In-Reply-To: <20260714081124.90962-1-mhun512@gmail.com>
On 7/14/2026 1:11 AM, Myeonghun Pak wrote:
> idpf_probe() enables PCIe Precision Time Measurement with
> pci_enable_ptm(pdev, NULL), which programs the PTM control bits and sets
> pdev->ptm_enabled when the bus/controller supports it. The teardown path
> in idpf_remove() releases the workqueues, vports, mutexes and the adapter
> memory but never calls pci_disable_ptm(), so PTM is left enabled on the
> device after the driver detaches.
>
> This leaves the PCI core's software PTM state and the device's PTM control
> bits set with no bound driver. pcim_enable_device() only arranges for
> pci_disable_device() on teardown and does not undo the PTM enable, so it
> is not a substitute here.
>
> Pair the enable with pci_disable_ptm(pdev) in idpf_remove(), matching the
> igc and mlx5 drivers which already disable PTM on their remove paths.
>
> Fixes: 8d5e12c5921c ("idpf: add initial PTP support")
> Co-developed-by: Ijae Kim <ae878000@gmail.com>
> Signed-off-by: Ijae Kim <ae878000@gmail.com>
> Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
> ---
> drivers/net/ethernet/intel/idpf/idpf_main.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c
> index 0dd741dcfc..3d3471d3f7 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf_main.c
> +++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
> @@ -159,6 +159,7 @@ static void idpf_remove(struct pci_dev *pdev)
> mutex_destroy(&adapter->queue_lock);
> mutex_destroy(&adapter->vc_buf_lock);
>
> + pci_disable_ptm(pdev);
> pci_set_drvdata(pdev, NULL);
> kfree(adapter);
> }
I think another call will also be needed in idpf_probe() in the error
path, following pci_enable_ptm().
Thanks,
Emil
^ permalink raw reply
* Re: [PATCH 2/2] net: wwan: qcom_bam_dmux: Alloc RX buffers as a single coherent block
From: Vishnu Santhosh @ 2026-07-14 16:31 UTC (permalink / raw)
To: Jagielski, Jedrzej, Stephan Gerhold, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Loic Poulain, Sergey Ryazanov,
Johannes Berg
Cc: linux-arm-msm@vger.kernel.org, netdev@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
chris.lew@oss.qualcomm.com, Deepak Kumar Singh
In-Reply-To: <PH0PR11MB59022A1E06830365E6D224B4F0F92@PH0PR11MB5902.namprd11.prod.outlook.com>
On 14-07-2026 01:25 pm, Jagielski, Jedrzej wrote:
> From: Vishnu Santhosh <vishnu.santhosh@oss.qualcomm.com>
> Sent: Tuesday, July 14, 2026 7:33 AM
>
>> On Qualcomm SoCs where the modem (e.g. the mDSP on Shikra, VMID 43 /
>> NAV) is the AXI master for BAM-DMUX RX transfers and the XPU enforces
>> per-region access control, each individually DMA-mapped RX buffer
>> requires its own XPU resource group (RG). With ~16 RGs available, the
>> 32 per-buffer dma_map_single() calls exhaust the table and the first
>> inbound transfer faults with an XPU violation.
>>
>> BAM-DMUX is a singleton (exactly one instance per SoC), so the
>> destination VMID does not need to be a DT property; it is looked up
> >from the compatible string's match data instead. Add struct
>> bam_dmux_data with a single vmid field, and a shikra_data instance
>> hardcoding QCOM_SCM_VMID_NAV for qcom,shikra-bam-dmux.
>>
>> When match data is present, allocate all BAM_DMUX_NUM_SKB RX buffers as
>> a single contiguous dma_alloc_coherent() block and SCM-assign that
>> block to HLOS plus the VMID once at probe. This reduces RG consumption
> >from 32 to 1. The block is never reclaimed across a modem power cycle
>> (bam_dmux_power_off() does not touch it), so the probe-time assignment
>> covers every subsequent restart without re-assigning or reclaiming. It
>> is reclaimed to HLOS only once, at remove or on a probe error, and if
>> that reclaim fails it is leaked rather than returned to the page
>> allocator.
>>
>> Each rx_skbs[] slot is pre-assigned its virtual and DMA address from
>> the block, so no per-buffer mapping is needed at power-on. Because the
>> coherent block is not page-backed, received payload is copied into a
>> regular netdev skb before handoff to the network stack; this is an
>> unavoidable extra copy on the XPU-enforced RX path.
>>
>> Platforms without match data are unaffected: rx_virt stays NULL, no
>> coherent memory is allocated, and the per-buffer dma_map_single() path
>> is unchanged.
>>
>> Co-developed-by: Deepak Kumar Singh <deepak.singh@oss.qualcomm.com>
>> Signed-off-by: Deepak Kumar Singh <deepak.singh@oss.qualcomm.com>
>> Signed-off-by: Vishnu Santhosh <vishnu.santhosh@oss.qualcomm.com>
>> ---
>> drivers/net/wwan/Kconfig | 1 +
>> drivers/net/wwan/qcom_bam_dmux.c | 134 ++++++++++++++++++++++++++++++++++++---
>> 2 files changed, 125 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig
>> index 958dbc7347fa84ee869439bf8b503037faab8bef..1b133c56231615269698140187ca3141dfe48dbf 100644
>> --- a/drivers/net/wwan/Kconfig
>> +++ b/drivers/net/wwan/Kconfig
>> @@ -65,6 +65,7 @@ config MHI_WWAN_MBIM
>> config QCOM_BAM_DMUX
>> tristate "Qualcomm BAM-DMUX WWAN network driver"
>> depends on (DMA_ENGINE && PM && QCOM_SMEM_STATE) || COMPILE_TEST
>> + select QCOM_SCM
>> help
>> The BAM Data Multiplexer provides access to the network data channels
>> of modems integrated into many older Qualcomm SoCs, e.g. Qualcomm
>> diff --git a/drivers/net/wwan/qcom_bam_dmux.c b/drivers/net/wwan/qcom_bam_dmux.c
>> index cc6ace8d64371eb8d00c638a39b234ee540b83c9..247230b720e6011876d5c429badbb5a1f34fc576 100644
>> --- a/drivers/net/wwan/qcom_bam_dmux.c
>> +++ b/drivers/net/wwan/qcom_bam_dmux.c
>> @@ -9,10 +9,12 @@
>> #include <linux/completion.h>
>> #include <linux/dma-mapping.h>
>> #include <linux/dmaengine.h>
>> +#include <linux/firmware/qcom/qcom_scm.h>
>> #include <linux/if_arp.h>
>> #include <linux/interrupt.h>
>> #include <linux/module.h>
>> #include <linux/netdevice.h>
>> +#include <linux/of.h>
>> #include <linux/platform_device.h>
>> #include <linux/pm_runtime.h>
>> #include <linux/soc/qcom/smem_state.h>
>> @@ -62,6 +64,7 @@ struct bam_dmux_skb_dma {
>> struct bam_dmux *dmux;
>> struct sk_buff *skb;
>> dma_addr_t addr;
>> + void *rx_virt; /* non-NULL: slot in the coherent RX block */
>> };
>>
>> struct bam_dmux {
>> @@ -75,6 +78,10 @@ struct bam_dmux {
>> struct completion pc_ack_completion;
>>
>> struct dma_chan *rx, *tx;
>> + /* Single coherent block backing all RX buffers, NULL if unused */
>> + void *rx_buf;
>> + dma_addr_t rx_buf_dma;
>> + u64 rx_buf_perms; /* SCM source-VMID bitmask of rx_buf */
>> struct bam_dmux_skb_dma rx_skbs[BAM_DMUX_NUM_SKB];
>> struct bam_dmux_skb_dma tx_skbs[BAM_DMUX_NUM_SKB];
>> spinlock_t tx_lock; /* Protect tx_skbs, tx_next_skb */
>> @@ -92,6 +99,10 @@ struct bam_dmux_netdev {
>> u8 ch;
>> };
>>
>> +struct bam_dmux_data {
>> + u32 vmid;
>> +};
>> +
> do we need to introduce dedicated struct just to cover u32?
This is currently tied up in the open discussion on
whether the VMID should come from match data or an optional
qcom,vmid DT property instead. If match data stays, I agree
a bare u32 value works without a dedicated struct and I'll simplify
it that way.
>
>> static void bam_dmux_pc_vote(struct bam_dmux *dmux, bool enable)
>> {
>> reinit_completion(&dmux->pc_ack_completion);
>> @@ -111,6 +122,9 @@ static bool bam_dmux_skb_dma_map(struct bam_dmux_skb_dma *skb_dma,
>> {
>> struct device *dev = skb_dma->dmux->dev;
>>
>> + if (skb_dma->rx_virt) /* coherent RX slot: addr pre-assigned */
>> + return true;
>> +
>> skb_dma->addr = dma_map_single(dev, skb_dma->skb->data, skb_dma->skb->len, dir);
>> if (dma_mapping_error(dev, skb_dma->addr)) {
>> dev_err(dev, "Failed to DMA map buffer\n");
>> @@ -124,6 +138,9 @@ static bool bam_dmux_skb_dma_map(struct bam_dmux_skb_dma *skb_dma,
>> static void bam_dmux_skb_dma_unmap(struct bam_dmux_skb_dma *skb_dma,
>> enum dma_data_direction dir)
>> {
>> + if (skb_dma->rx_virt) /* coherent RX slot: nothing to unmap */
>> + return;
>> +
>> dma_unmap_single(skb_dma->dmux->dev, skb_dma->addr, skb_dma->skb->len, dir);
>> skb_dma->addr = 0;
>> }
>> @@ -468,9 +485,10 @@ static bool bam_dmux_skb_dma_submit_rx(struct bam_dmux_skb_dma *skb_dma)
>> {
>> struct bam_dmux *dmux = skb_dma->dmux;
>> struct dma_async_tx_descriptor *desc;
>> + size_t len = skb_dma->rx_virt ? BAM_DMUX_BUFFER_SIZE : skb_dma->skb->len;
> please stick to RCT
> please fix it here and for the following where RCT is violated
I will fix all the declaration ordering in v2.
>
>> desc = dmaengine_prep_slave_single(dmux->rx, skb_dma->addr,
>> - skb_dma->skb->len, DMA_DEV_TO_MEM,
>> + len, DMA_DEV_TO_MEM,
>> DMA_PREP_INTERRUPT);
>> if (!desc) {
>> dev_err(dmux->dev, "Failed to prepare RX DMA buffer\n");
>> @@ -485,6 +503,10 @@ static bool bam_dmux_skb_dma_submit_rx(struct bam_dmux_skb_dma *skb_dma)
>>
>> static bool bam_dmux_skb_dma_queue_rx(struct bam_dmux_skb_dma *skb_dma, gfp_t gfp)
>> {
>> + /* Coherent RX slots have rx_virt and addr pre-assigned at probe. */
>> + if (skb_dma->rx_virt)
>> + return bam_dmux_skb_dma_submit_rx(skb_dma);
>> +
>> if (!skb_dma->skb) {
>> skb_dma->skb = __netdev_alloc_skb(NULL, BAM_DMUX_BUFFER_SIZE, gfp);
>> if (!skb_dma->skb)
>> @@ -499,9 +521,10 @@ static bool bam_dmux_skb_dma_queue_rx(struct bam_dmux_skb_dma *skb_dma, gfp_t gf
>> static void bam_dmux_cmd_data(struct bam_dmux_skb_dma *skb_dma)
>> {
>> struct bam_dmux *dmux = skb_dma->dmux;
>> - struct sk_buff *skb = skb_dma->skb;
>> - struct bam_dmux_hdr *hdr = (struct bam_dmux_hdr *)skb->data;
>> + struct bam_dmux_hdr *hdr = skb_dma->rx_virt ? skb_dma->rx_virt :
>> + (struct bam_dmux_hdr *)skb_dma->skb->data;
>> struct net_device *netdev = dmux->netdevs[hdr->ch];
>> + struct sk_buff *skb;
>>
>> if (!netdev || !netif_running(netdev)) {
>> dev_warn(dmux->dev, "Data for inactive channel %u\n", hdr->ch);
>> @@ -514,10 +537,18 @@ static void bam_dmux_cmd_data(struct bam_dmux_skb_dma *skb_dma)
>> return;
>> }
>>
>> - skb_dma->skb = NULL; /* Hand over to network stack */
>> -
>> - skb_pull(skb, sizeof(*hdr));
>> - skb_trim(skb, hdr->len);
>> + if (skb_dma->rx_virt) {
>> + /* Coherent block is not page-backed: copy out to a real skb */
>> + skb = netdev_alloc_skb(netdev, hdr->len);
>> + if (!skb)
>> + return;
>> + skb_put_data(skb, (u8 *)skb_dma->rx_virt + sizeof(*hdr), hdr->len);
>> + } else {
>> + skb = skb_dma->skb;
>> + skb_dma->skb = NULL; /* Hand over to network stack */
>> + skb_pull(skb, sizeof(*hdr));
>> + skb_trim(skb, hdr->len);
>> + }
>> skb->dev = netdev;
>>
>> /* Only Raw-IP/QMAP is supported by this driver */
>> @@ -574,10 +605,14 @@ static void bam_dmux_rx_callback(void *data)
>> {
>> struct bam_dmux_skb_dma *skb_dma = data;
>> struct bam_dmux *dmux = skb_dma->dmux;
>> - struct sk_buff *skb = skb_dma->skb;
>> - struct bam_dmux_hdr *hdr = (struct bam_dmux_hdr *)skb->data;
>> + struct bam_dmux_hdr *hdr;
>>
>> - bam_dmux_skb_dma_unmap(skb_dma, DMA_FROM_DEVICE);
>> + if (skb_dma->rx_virt) {
>> + hdr = skb_dma->rx_virt; /* coherent RX: no skb to unmap */
>> + } else {
>> + bam_dmux_skb_dma_unmap(skb_dma, DMA_FROM_DEVICE);
>> + hdr = (struct bam_dmux_hdr *)skb_dma->skb->data;
>> + }
>>
>> if (hdr->magic != BAM_DMUX_HDR_MAGIC) {
>> dev_err(dmux->dev, "Invalid magic in header: %#x\n", hdr->magic);
>> @@ -644,6 +679,9 @@ static void bam_dmux_free_skbs(struct bam_dmux_skb_dma skbs[],
>> for (i = 0; i < BAM_DMUX_NUM_SKB; i++) {
>> struct bam_dmux_skb_dma *skb_dma = &skbs[i];
>>
>> + if (skb_dma->rx_virt) /* coherent block freed at remove */
>> + continue;
>> +
>> if (skb_dma->addr)
>> bam_dmux_skb_dma_unmap(skb_dma, dir);
>> if (skb_dma->skb) {
>> @@ -762,6 +800,71 @@ static int __maybe_unused bam_dmux_runtime_resume(struct device *dev)
>> return 0;
>> }
>>
>> +static int bam_dmux_alloc_coherent_rx(struct bam_dmux *dmux)
>> +{
>> + struct device *dev = dmux->dev;
>> + const struct bam_dmux_data *data = of_device_get_match_data(dev);
>> + size_t size = BAM_DMUX_NUM_SKB * BAM_DMUX_BUFFER_SIZE;
>> + u64 src = BIT_ULL(QCOM_SCM_VMID_HLOS);
>> + struct qcom_scm_vmperm dst[2];
>> + int i, ret;
>> +
>> + if (!data)
>> + return 0;
> is there actually any chance to really trigger that check?
> or just theoretical case?
Yes, it's reachable. The generic "qcom,bam-dmux" entry in
bam_dmux_of_match[] has no .data, so any platform probing via
that fallback compatible (i.e. every existing non-Shikra board
using this driver today) gets NULL here and takes the unmodified
per-buffer dma_map_single() path.
This is the intended gate that keeps the new coherent-block
allocation opt-in to Shikra only.
>
>> +
>> + if (!qcom_scm_is_available())
>> + return -EPROBE_DEFER;
>> +
>> + dst[0].vmid = QCOM_SCM_VMID_HLOS;
>> + dst[0].perm = QCOM_SCM_PERM_RW;
>> + dst[1].vmid = data->vmid;
>> + dst[1].perm = QCOM_SCM_PERM_RW;
>> +
>> + dmux->rx_buf = dma_alloc_coherent(dev, size, &dmux->rx_buf_dma, GFP_KERNEL);
>> + if (!dmux->rx_buf)
>> + return -ENOMEM;
>> +
>> + for (i = 0; i < BAM_DMUX_NUM_SKB; i++) {
>> + dmux->rx_skbs[i].rx_virt = dmux->rx_buf + i * BAM_DMUX_BUFFER_SIZE;
>> + dmux->rx_skbs[i].addr = dmux->rx_buf_dma + i * BAM_DMUX_BUFFER_SIZE;
>> + }
>> +
>> + ret = qcom_scm_assign_mem(dmux->rx_buf_dma, size, &src, dst, ARRAY_SIZE(dst));
>> + if (ret) {
>> + dev_err(dev, "SCM assign RX block failed: %d\n", ret);
>> + dma_free_coherent(dev, size, dmux->rx_buf, dmux->rx_buf_dma);
>> + dmux->rx_buf = NULL;
>> + return ret;
>> + }
>> + dmux->rx_buf_perms = src;
>> +
>> + return 0;
>> +}
>> +
>> +static void bam_dmux_free_coherent_rx(struct bam_dmux *dmux)
>> +{
>> + struct qcom_scm_vmperm hlos = {
>> + .vmid = QCOM_SCM_VMID_HLOS,
>> + .perm = QCOM_SCM_PERM_RW,
>> + };
>> + size_t size = BAM_DMUX_NUM_SKB * BAM_DMUX_BUFFER_SIZE;
>> +
>> + if (!dmux->rx_buf)
>> + return;
>> +
>> + if (dmux->rx_buf_perms) {
>> + if (qcom_scm_assign_mem(dmux->rx_buf_dma, size, &dmux->rx_buf_perms,
>> + &hlos, 1)) {
>> + dev_err(dmux->dev, "SCM reclaim RX block failed; leaking\n");
>> + return;
>> + }
>> + dmux->rx_buf_perms = 0;
>> + }
>> +
>> + dma_free_coherent(dmux->dev, size, dmux->rx_buf, dmux->rx_buf_dma);
>> + dmux->rx_buf = NULL;
>> +}
>> +
>> static int bam_dmux_probe(struct platform_device *pdev)
>> {
>> struct device *dev = &pdev->dev;
>> @@ -809,6 +912,10 @@ static int bam_dmux_probe(struct platform_device *pdev)
>> dmux->tx_skbs[i].dmux = dmux;
>> }
>>
>> + ret = bam_dmux_alloc_coherent_rx(dmux);
>> + if (ret)
>> + return ret;
>> +
>> /* Runtime PM manages our own power vote.
>> * Note that the RX path may be active even if we are runtime suspended,
>> * since it is controlled by the remote side.
>> @@ -845,6 +952,7 @@ static int bam_dmux_probe(struct platform_device *pdev)
>> err_disable_pm:
>> pm_runtime_disable(dev);
>> pm_runtime_dont_use_autosuspend(dev);
>> + bam_dmux_free_coherent_rx(dmux);
>> return ret;
>> }
>>
>> @@ -879,13 +987,19 @@ static void bam_dmux_remove(struct platform_device *pdev)
>> disable_irq(dmux->pc_irq);
>> bam_dmux_power_off(dmux);
>> bam_dmux_free_skbs(dmux->tx_skbs, DMA_TO_DEVICE);
>> + bam_dmux_free_coherent_rx(dmux);
>> }
>>
>> static const struct dev_pm_ops bam_dmux_pm_ops = {
>> SET_RUNTIME_PM_OPS(bam_dmux_runtime_suspend, bam_dmux_runtime_resume, NULL)
>> };
>>
>> +static const struct bam_dmux_data shikra_data = {
>> + .vmid = QCOM_SCM_VMID_NAV,
>> +};
>> +
>> static const struct of_device_id bam_dmux_of_match[] = {
>> + { .compatible = "qcom,shikra-bam-dmux", .data = &shikra_data },
>> { .compatible = "qcom,bam-dmux" },
>> { /* sentinel */ }
>> };
>>
>> --
>> 2.34.1
>
^ permalink raw reply
* Re: [PATCH net v4 3/3] net/smc: bound the send length to the send buffer in smc_tx_sendmsg()
From: Dust Li @ 2026-07-14 16:20 UTC (permalink / raw)
To: hexlabsecurity, David S. Miller, Sidraya Jayagond, Eric Dumazet,
D. Wythe, Jakub Kicinski, Simon Horman, Wenjia Zhang, Paolo Abeni
Cc: Stefan Raspl, Wen Gu, linux-kernel, netdev, Mahanta Jambigi,
Tony Lu, Ursula Braun, linux-s390, linux-rdma
In-Reply-To: <20260705-b4-disp-28a1bbca-v4-3-be089b98acc6@proton.me>
On 2026-07-05 02:54:07, Bryam Vargas via B4 Relay wrote:
>From: Bryam Vargas <hexlabsecurity@proton.me>
>
>On the SMC-D DMB-merge (nocopy) path, smc_cdc_msg_recv_action()
>advances conn->sndbuf_space from the peer's wire-controlled consumer
>cursor via smc_curs_diff(), which can return more than sndbuf_desc->len;
>a forged cursor drives sndbuf_space past the send buffer, and over many
>CDC messages overflows the signed counter negative. smc_tx_sendmsg()
>reads it as the write space and does a wrap-around copy whose second
>chunk is not re-bounded to sndbuf_desc->len, spilling the local
>sender's outbound data past the send buffer at a peer-controlled
>length: a heap out-of-bounds write. The nearby len > sndbuf_desc->len
>test only feeds SMC_STAT_RMB_TX_SIZE_SMALL on the user length; it does
>not bound the copy.
>
>Bound the write space to sndbuf_desc->len at the consumer, treating a
>negative (sign-overflowed) value as out of range too, so the copy can
>never exceed the ring. This enforces the documented
>0 <= sndbuf_space <= sndbuf_desc->len invariant where it is race-free
>against the CDC tasklet; conforming peers are unaffected.
>
>Fixes: cc0ab806fc52 ("net/smc: adapt cursor update when sndbuf and peer DMB are merged")
>Cc: stable@vger.kernel.org
>Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
Best regards,
Dust
>---
> net/smc/smc_tx.c | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
>diff --git a/net/smc/smc_tx.c b/net/smc/smc_tx.c
>index 3144b4b1fe29..5916f02060fb 100644
>--- a/net/smc/smc_tx.c
>+++ b/net/smc/smc_tx.c
>@@ -233,6 +233,19 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len)
> /* initialize variables for 1st iteration of subsequent loop */
> /* could be just 1 byte, even after smc_tx_wait above */
> writespace = atomic_read(&conn->sndbuf_space);
>+ /* sndbuf_space is advanced from the peer's wire-controlled
>+ * consumer cursor on the SMC-D DMB-merge path; a forged cursor
>+ * can inflate it past the send buffer, or overflow the signed
>+ * accumulator to a negative value across many CDC messages
>+ * (which a plain "> len" check would miss before the size_t
>+ * cast below turns it huge). Bound it to the send buffer in
>+ * either case so the wrap-around write cannot run past
>+ * sndbuf_desc->len. This enforces the documented
>+ * 0 <= sndbuf_space <= sndbuf_desc->len invariant at the
>+ * producer, race-free against the CDC tasklet.
>+ */
>+ if (writespace < 0 || writespace > conn->sndbuf_desc->len)
>+ writespace = conn->sndbuf_desc->len;
> /* not more than what user space asked for */
> copylen = min_t(size_t, send_remaining, writespace);
> /* determine start of sndbuf */
>
>--
>2.43.0
>
^ permalink raw reply
* Re: [PATCH net v4 2/3] net/smc: bound the receive length to the RMB in smc_rx_recvmsg()
From: Dust Li @ 2026-07-14 16:20 UTC (permalink / raw)
To: hexlabsecurity, David S. Miller, Sidraya Jayagond, Eric Dumazet,
D. Wythe, Jakub Kicinski, Simon Horman, Wenjia Zhang, Paolo Abeni
Cc: Stefan Raspl, Wen Gu, linux-kernel, netdev, Mahanta Jambigi,
Tony Lu, Ursula Braun, linux-s390, linux-rdma
In-Reply-To: <20260705-b4-disp-28a1bbca-v4-2-be089b98acc6@proton.me>
On 2026-07-05 02:54:06, Bryam Vargas via B4 Relay wrote:
>From: Bryam Vargas <hexlabsecurity@proton.me>
>
>conn->bytes_to_rcv is accumulated in the receive tasklet from the
>peer's wire-controlled producer cursor via smc_curs_diff(), whose
>differing-wrap branch can exceed rmb_desc->len; a forged cursor drives
>bytes_to_rcv past the RMB, and over many CDC messages overflows the
>signed counter negative. smc_rx_recvmsg() reads it as the readable
>length and does a wrap-around copy whose second chunk is not re-bounded
>to rmb_desc->len, reading past the RMB into adjacent kernel memory and
>disclosing it to the peer. The nearby readable >= rmb_desc->len test
>only feeds SMC_STAT_RMB_RX_FULL on a separate earlier read; it does not
>bound the copy.
>
>Bound the readable length to rmb_desc->len at the consumer, treating a
>negative (sign-overflowed) value as out of range too, so the copy can
>never exceed the ring. This enforces the documented
>0 <= bytes_to_rcv <= rmb_desc->len invariant where it is race-free
>against the producer update in the tasklet; conforming peers are
>unaffected.
>
>Fixes: 952310ccf2d8 ("smc: receive data from RMBE")
>Cc: stable@vger.kernel.org
>Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
Best regards,
Dust
>---
> net/smc/smc_rx.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
>diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c
>index c1d9b923938d..f461cf10b085 100644
>--- a/net/smc/smc_rx.c
>+++ b/net/smc/smc_rx.c
>@@ -442,6 +442,18 @@ int smc_rx_recvmsg(struct smc_sock *smc, struct msghdr *msg,
> /* initialize variables for 1st iteration of subsequent loop */
> /* could be just 1 byte, even after waiting on data above */
> readable = smc_rx_data_available(conn, peeked_bytes);
>+ /* bytes_to_rcv is accumulated from the peer's wire-controlled
>+ * producer cursor; a forged cursor can drive it past the RMB,
>+ * or overflow the signed accumulator to a negative value across
>+ * many CDC messages (which a plain "> len" check would miss
>+ * before the size_t cast below turns it huge). Bound it to the
>+ * RMB in either case so the wrap-around copy cannot run past
>+ * rmb_desc->len. This enforces the documented
>+ * 0 <= bytes_to_rcv <= rmb_desc->len invariant at the consumer,
>+ * race-free against the producer update in the receive tasklet.
>+ */
>+ if (readable < 0 || readable > conn->rmb_desc->len)
>+ readable = conn->rmb_desc->len;
> splbytes = atomic_read(&conn->splice_pending);
> if (!readable || (msg && splbytes)) {
> if (splbytes)
>
>--
>2.43.0
>
^ permalink raw reply
* Re: [PATCH net v4 1/3] net/smc: bound the wire-controlled producer cursor to the RMB
From: Dust Li @ 2026-07-14 16:19 UTC (permalink / raw)
To: hexlabsecurity, David S. Miller, Sidraya Jayagond, Eric Dumazet,
D. Wythe, Jakub Kicinski, Simon Horman, Wenjia Zhang, Paolo Abeni
Cc: Stefan Raspl, Wen Gu, linux-kernel, netdev, Mahanta Jambigi,
Tony Lu, Ursula Braun, linux-s390, linux-rdma
In-Reply-To: <20260705-b4-disp-28a1bbca-v4-1-be089b98acc6@proton.me>
On 2026-07-05 02:54:05, Bryam Vargas via B4 Relay wrote:
>From: Bryam Vargas <hexlabsecurity@proton.me>
>
>smcr_cdc_msg_to_host() and smcd_cdc_msg_to_host() import a peer's
>producer cursor from the wire into conn->local_rx_ctrl.prod without
>bounding it against the receive buffer. The urgent-data path in
>smc_cdc_msg_recv_action() then uses that count as a raw index into the
>RMB, so a peer that advertises a producer cursor past rmb_desc->len
>reads out of bounds of the RMB allocation in the receive tasklet and
>can disclose adjacent kernel memory.
>
>Bound the producer cursor count to rmb_desc->len at the wire-to-host
>conversion, for both SMC-R and SMC-D. Bound only the producer cursor:
>the consumer cursor indexes the peer's RMB and is bounded by
>peer_rmbe_size, so clamping it to our rmb_desc->len would under-credit
>peer_rmbe_space and stall transmit to a peer with a larger RMB.
>Conforming peers are unaffected.
>
>Fixes: de8474eb9d50 ("net/smc: urgent data support")
>Cc: stable@vger.kernel.org
>Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
Best regards,
Dust
>---
> net/smc/smc_cdc.h | 27 ++++++++++++++++++++++++---
> 1 file changed, 24 insertions(+), 3 deletions(-)
>
>diff --git a/net/smc/smc_cdc.h b/net/smc/smc_cdc.h
>index 696cc11f2303..ca76ef630356 100644
>--- a/net/smc/smc_cdc.h
>+++ b/net/smc/smc_cdc.h
>@@ -221,7 +221,8 @@ static inline void smc_host_msg_to_cdc(struct smc_cdc_msg *peer,
>
> static inline void smc_cdc_cursor_to_host(union smc_host_cursor *local,
> union smc_cdc_cursor *peer,
>- struct smc_connection *conn)
>+ struct smc_connection *conn,
>+ int max_count)
> {
> union smc_host_cursor temp, old;
> union smc_cdc_cursor net;
>@@ -235,6 +236,15 @@ static inline void smc_cdc_cursor_to_host(union smc_host_cursor *local,
> if ((old.wrap == temp.wrap) &&
> (old.count > temp.count))
> return;
>+ /* The peer producer cursor is wire-controlled and is later used as a
>+ * raw index into our RMB by the urgent path; bound its count to the
>+ * RMB. max_count == 0 leaves the consumer cursor unbounded here: it
>+ * indexes the peer's RMB (bounded by peer_rmbe_size, not our
>+ * rmb_desc->len), so clamping it to rmb_desc->len would under-credit
>+ * peer_rmbe_space and stall transmit to peers with a larger RMB.
>+ */
>+ if (max_count && temp.count > max_count)
>+ temp.count = max_count;
> smc_curs_copy(local, &temp, conn);
> }
>
>@@ -246,8 +256,13 @@ static inline void smcr_cdc_msg_to_host(struct smc_host_cdc_msg *local,
> local->len = peer->len;
> local->seqno = ntohs(peer->seqno);
> local->token = ntohl(peer->token);
>- smc_cdc_cursor_to_host(&local->prod, &peer->prod, conn);
>- smc_cdc_cursor_to_host(&local->cons, &peer->cons, conn);
>+ /* bound the wire-controlled producer cursor to our RMB (used as a raw
>+ * index by the urgent path); leave the consumer cursor unbounded -- it
>+ * indexes the peer's RMB and is bounded by peer_rmbe_size.
>+ */
>+ smc_cdc_cursor_to_host(&local->prod, &peer->prod, conn,
>+ conn->rmb_desc->len);
>+ smc_cdc_cursor_to_host(&local->cons, &peer->cons, conn, 0);
> local->prod_flags = peer->prod_flags;
> local->conn_state_flags = peer->conn_state_flags;
> }
>@@ -260,6 +275,12 @@ static inline void smcd_cdc_msg_to_host(struct smc_host_cdc_msg *local,
>
> temp.wrap = peer->prod.wrap;
> temp.count = peer->prod.count;
>+ /* the peer producer cursor is wire-controlled and is used as a raw
>+ * index into our RMB by the urgent path; bound it to the RMB. The
>+ * consumer cursor below indexes the peer's RMB and is left unbounded.
>+ */
>+ if (temp.count > conn->rmb_desc->len)
>+ temp.count = conn->rmb_desc->len;
> smc_curs_copy(&local->prod, &temp, conn);
>
> temp.wrap = peer->cons.wrap;
>
>--
>2.43.0
>
^ permalink raw reply
* Re: [PATCH net v4 0/3] net/smc: bound wire-controlled CDC cursors against the local buffers
From: Dust Li @ 2026-07-14 16:18 UTC (permalink / raw)
To: Bryam Vargas
Cc: Wenjia Zhang, D . Wythe, Sidraya Jayagond, Eric Dumazet,
David S . Miller, Mahanta Jambigi, Wen Gu, Simon Horman,
Ursula Braun, Stefan Raspl, Tony Lu, Paolo Abeni, Jakub Kicinski,
netdev, linux-s390, linux-rdma, linux-kernel
In-Reply-To: <20260711104315.82912-1-hexlabsecurity@proton.me>
On 2026-07-11 10:43:26, Bryam Vargas wrote:
>On Tue, 7 Jul 2026 17:29:04 +0800, Dust Li wrote:
>> Are you planning to land these clamps first, and then follow up with a
>> separate validate/abort series?
>
>Yes -- clamp series to net (Cc: stable), then the wire-boundary validate/abort to
>net-next, which is the split from your v3 review. If you'd rather have the
>validate/abort as the primary fix, or both in one series, say so and I'll
>restructure it.
>
>> Looking at your earlier A/B test, it simulates this logic in userspace to
>> demonstrate the bug, but it doesn't actually trigger the bug in our
>> current kernel.
>
>Right -- the earlier one replayed the smc_curs_diff/copy arithmetic over a kmalloc
>buffer. I built the end-to-end version: two AF_SMC sockets over the SMC-D loopback
>(dibs), CONFIG_SMC=m with KASAN, receive path unmodified. Only the sender's on-wire
>producer cursor is forged, modelling what a misbehaving peer sends:
>
> cdc.prod.wrap = curs.wrap;
> cdc.prod.count = curs.count;
>+ if (forge) { /* peer just bumps the wrap, count stays 0 */
>+ static u16 w;
>+ cdc.prod.wrap = ++w;
>+ cdc.prod.count = 0;
>+ }
>
>The client sends six 1-byte messages, the server recvs into a 2 MB buffer.
>rmb_desc->len = 65504; the three arms on 7.2-rc1:
>
> honest (no forge) recv 6 clean
> forged, patch 2/3 clamp on recv 65504 clean (== rmb_desc->len)
> forged, no clamp recv 393024 KASAN
>
>In the last arm bytes_to_rcv reaches 6*len, so smc_rx_recvmsg()'s second wrap-around
>chunk (copylen - first_chunk = 393024 - 65504) is read from ring offset 0, past the
>RMB page:
>
> BUG: KASAN: slab-use-after-free in _copy_to_iter
> Read of size 327520 ... smc_rx_recvmsg <- smc_recvmsg <- __sys_recvfrom
>
>(use-after-free rather than out-of-bounds only because the over-read lands in a freed
>adjacent slab.) Happy to send the driver.
>
>> the security risk here doesn't seem high to me, since SMC is only meant to
>> be deployed in trusted environments.
>
>Agreed it's low urgency there. The reason I'd still keep the bound in stable: it's a
>peer-driven out-of-bounds read of kernel memory that a buggy, not only malicious,
>peer can hit, and the clamp never resets an honest connection. The stable tag is
>your call.
>
>> once this is actually triggered, it means the data we've been handing to
>> userspace is already wrong ... the connection should be terminated. So I
>> don't really see much value in merging the bound-clamp patches first.
>
>I'm not arguing against the abort -- a bad CDC means the connection can't be trusted
>and should go down, and that's the net-next work. Two points on it.
>
>The predicate has to test the accumulator, not the cursor. Every forged CDC here
>carries count == 0, which is in [0, rmb_desc->len), so it passes any per-cursor
>input check, including patch 1/3; only bytes_to_rcv goes out of range. A
>cursor-boundary abort wouldn't catch this vector.
>
>And placement: if the abort is queued (queue_work -> smc_conn_kill) after the
>atomic_add, a recvmsg() under lock_sock can still read the inflated accumulator in
>the window before teardown runs. A synchronous check that bails before the
>atomic_add avoids that, and so does the consumer clamp.
>
>If you'd prefer a single accumulator-abort in place of the -stable clamp, I'll
>respin it that way and run the same A/B.
Hi Bryam,
Thanks for the detailed explanation — I think you're right. To me the
key point here is that no matter how the peer misbehaves, we should
never let it cause a panic or memory corruption on our side.
So I think your current clamp can stay. In the net-next version we
can add the abort logic, and additionally, it would be best to emit a
warning log whenever the clamp detects an anomaly here.
Best regards, Dust
^ permalink raw reply
* Re: [PATCH 0/2] net: wwan: qcom_bam_dmux: Alloc RX buffers as a single coherent block
From: Vishnu Santhosh @ 2026-07-14 16:08 UTC (permalink / raw)
To: Jagielski, Jedrzej, Stephan Gerhold, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Loic Poulain, Sergey Ryazanov,
Johannes Berg
Cc: linux-arm-msm@vger.kernel.org, netdev@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
chris.lew@oss.qualcomm.com, Deepak Kumar Singh
In-Reply-To: <PH0PR11MB5902886DEDB417349E5E6E44F0F92@PH0PR11MB5902.namprd11.prod.outlook.com>
On 14-07-2026 01:23 pm, Jagielski, Jedrzej wrote:
> From: Vishnu Santhosh <vishnu.santhosh@oss.qualcomm.com>
> Sent: Tuesday, July 14, 2026 7:33 AM
>
>> On platforms where the modem DMAs into the BAM-DMUX RX data buffers and
>> the XPU (eXternal Protection Unit) enforces per-region access control,
>> each individually DMA-mapped RX buffer consumes an XPU resource group.
>> With only ~16 groups available on Shikra (mDSP, VMID 43 / NAV), the
>> per-buffer mappings exhaust the table and inbound transfers fault.
>>
>> This series adds a qcom,shikra-bam-dmux compatible and have the driver
>> select QCOM_SCM_VMID_NAV internally via that compatible's match data.
>> When matched, the driver allocates all RX buffers as a single
>> contiguous coherent block and SCM-assigns it to HLOS plus the VMID
>> once at probe, consuming one XPU resource group instead of many.
>>
>> Platforms that do not use the qcom,shikra-bam-dmux compatible are
>> unaffected: the existing per-buffer dma_map_single() path is
>> unchanged.
>>
>> Signed-off-by: Vishnu Santhosh <vishnu.santhosh@oss.qualcomm.com>
>> ---
>> Vishnu Santhosh (2):
>> dt-bindings: net: qcom,bam-dmux: Add qcom,shikra-bam-dmux compatible
>> net: wwan: qcom_bam_dmux: Alloc RX buffers as a single coherent block
>>
>> .../devicetree/bindings/net/qcom,bam-dmux.yaml | 8 +-
>> drivers/net/wwan/Kconfig | 1 +
>> drivers/net/wwan/qcom_bam_dmux.c | 134 +++++++++++++++++++--
>> 3 files changed, 132 insertions(+), 11 deletions(-)
>> ---
>> base-commit: 3b029c035b34bbc693405ddf759f0e9b920c27f1
>> change-id: 20260714-qcom-bam-dmux-vmid-ext-d9289db310c1
>>
>> Best regards,
>> --
>> Vishnu Santhosh <vishnu.santhosh@oss.qualcomm.com>
> Hi Vishnu
> you need to specify target tree, net-next for this series i believe
> please refer to[1]
>
> [1]
> https://docs.kernel.org/process/maintainer-netdev.html#indicating-target-tree
>
Thanks Jedrzej. I will address the comments and will send out the next revision
specifying the target tree as net-next.
Thanks,
Vishnu
^ permalink raw reply
* Re: [PATCH net] octeontx2: Fix Klocwork issues in AF and PF drivers
From: Vadim Fedorenko @ 2026-07-14 15:50 UTC (permalink / raw)
To: Ratheesh Kannoth, davem, linux-kernel, naveenm, netdev, sgoutham
Cc: andrew+netdev, edumazet, kuba, pabeni, richardcochran,
Suman Ghosh
In-Reply-To: <20260714020241.1810407-1-rkannoth@marvell.com>
On 14.07.2026 03:02, Ratheesh Kannoth wrote:
> From: Suman Ghosh <sumang@marvell.com>
>
> Fix null dereference, uninitialized variable, and error-path resource
> leak findings reported by Klocwork across the OcteonTX2 AF and PF code.
>
> Fixes: 818ed8933bd1 ("octeontx2-af: Re-enable MAC TX in otx2_stop processing")
> Cc: Naveen Mamindlapalli <naveenm@marvell.com>
> Signed-off-by: Suman Ghosh <sumang@marvell.com>
> Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
> ---
> drivers/net/ethernet/marvell/octeontx2/af/cgx.c | 12 +++++++++---
> .../net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c | 8 +++++---
> drivers/net/ethernet/marvell/octeontx2/af/npc.h | 6 +++---
> drivers/net/ethernet/marvell/octeontx2/af/ptp.c | 11 ++++++++++-
> drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c | 2 +-
> drivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c | 2 +-
> .../net/ethernet/marvell/octeontx2/af/rvu_debugfs.c | 8 +++++++-
> drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 2 +-
> drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c | 2 +-
> .../net/ethernet/marvell/octeontx2/nic/otx2_flows.c | 1 +
> drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 3 +++
> drivers/net/ethernet/marvell/octeontx2/nic/qos.c | 1 +
> 12 files changed, 43 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
> index 2e94d5105016..70c8ef5f0be0 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
> @@ -491,12 +491,19 @@ int cgx_lmac_addr_max_entries_get(u8 cgx_id, u8 lmac_id)
> u64 cgx_lmac_addr_get(u8 cgx_id, u8 lmac_id)
> {
> struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
> - struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev);
> struct mac_ops *mac_ops;
> + struct lmac *lmac;
> int index;
> u64 cfg;
> int id;
>
> + if (!cgx_dev)
> + return 0;
> +
> + lmac = lmac_pdata(lmac_id, cgx_dev);
> + if (!lmac)
> + return 0;
> +
this 2 checks are added for impossible cases. both values are checked for
existence in a signle caller of cgx_lmac_addr_get(). Please, do not add
defensive code for no reason. I didn't check other changes, but I believe they
are of the same quality coming from yet-anothener-anaylizing tool.
Please, provide stack traces if these checks are really needed.
[...]
^ permalink raw reply
* Re: [PATCH 2/2] net: wwan: qcom_bam_dmux: Alloc RX buffers as a single coherent block
From: Vishnu Santhosh @ 2026-07-14 15:47 UTC (permalink / raw)
To: Stephan Gerhold
Cc: Stephan Gerhold, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Loic Poulain, Sergey Ryazanov, Johannes Berg,
linux-arm-msm, netdev, devicetree, linux-kernel, chris.lew,
Deepak Kumar Singh
In-Reply-To: <alXm0rp3NK62G3-3@linaro.org>
On 14-07-2026 01:05 pm, Stephan Gerhold wrote:
> On Tue, Jul 14, 2026 at 11:02:32AM +0530, Vishnu Santhosh wrote:
>> On Qualcomm SoCs where the modem (e.g. the mDSP on Shikra, VMID 43 /
>> NAV) is the AXI master for BAM-DMUX RX transfers and the XPU enforces
>> per-region access control, each individually DMA-mapped RX buffer
>> requires its own XPU resource group (RG). With ~16 RGs available, the
>> 32 per-buffer dma_map_single() calls exhaust the table and the first
>> inbound transfer faults with an XPU violation.
>>
>> BAM-DMUX is a singleton (exactly one instance per SoC), so the
>> destination VMID does not need to be a DT property; it is looked up
>> from the compatible string's match data instead. Add struct
>> bam_dmux_data with a single vmid field, and a shikra_data instance
>> hardcoding QCOM_SCM_VMID_NAV for qcom,shikra-bam-dmux.
>>
>> When match data is present, allocate all BAM_DMUX_NUM_SKB RX buffers as
>> a single contiguous dma_alloc_coherent() block and SCM-assign that
>> block to HLOS plus the VMID once at probe. This reduces RG consumption
>> from 32 to 1. The block is never reclaimed across a modem power cycle
>> (bam_dmux_power_off() does not touch it), so the probe-time assignment
>> covers every subsequent restart without re-assigning or reclaiming. It
>> is reclaimed to HLOS only once, at remove or on a probe error, and if
>> that reclaim fails it is leaked rather than returned to the page
>> allocator.
>>
>> Each rx_skbs[] slot is pre-assigned its virtual and DMA address from
>> the block, so no per-buffer mapping is needed at power-on. Because the
>> coherent block is not page-backed, received payload is copied into a
>> regular netdev skb before handoff to the network stack; this is an
>> unavoidable extra copy on the XPU-enforced RX path.
>>
>> Platforms without match data are unaffected: rx_virt stays NULL, no
>> coherent memory is allocated, and the per-buffer dma_map_single() path
>> is unchanged.
>>
>> Co-developed-by: Deepak Kumar Singh <deepak.singh@oss.qualcomm.com>
>> Signed-off-by: Deepak Kumar Singh <deepak.singh@oss.qualcomm.com>
>> Signed-off-by: Vishnu Santhosh <vishnu.santhosh@oss.qualcomm.com>
> So how do you handle TX buffers? Right now, they are just passed on from
> the net subsystem. There can be up to 32 TX buffers in progress as well.
>
> Overall, I have mixed feelings about this patch. It looks reasonably
> simple, but fundamentally I don't understand why we need to go back to
> the old days of implementing protection using a highly limited MPU (in
> your case: the xPU).
>
> Why does the setup of BAM-DMUX differ e.g. from the setup for the crypto
> engine? Crypto is also using bam-dma, but it avoids this inflexibility
> by making use of the &apps_smmu. Is BAM-DMUX not covered by the SMMU? Or
> did you just decide to bypass the SMMU in this case? (If so: Why?)
>
> If you had BAM-DMUX mapped using the SMMU you would get all of this for
> free. No changes would be needed in the BAM-DMUX driver ...
>
> Thanks,
> Stephan
Thanks for pointing out. We were seeing XPU violations on descriptor
FIFO accesses in the RX path. This series resolves the RX-side faults,
which is why we posted it. It appears that the TX path requires equivalent
handling, and we'll include the necessary TX changes in v2 of this series.
Currently, the SMMU does not cover this A2 BAM instance on Shikra, which
is why we opted for the SCM-assign approach. I'll check with the hardware
team to better understand why BAM-DMUX is not behind the SMMU, gather the
relevant details, and get back.
Thanks,
Vishnu
^ permalink raw reply
* Re: [PATCH net 1/2] net: macb: reprogram TBQP after shuffling the TX ring on link-up
From: Taedcke, Christian @ 2026-07-14 15:35 UTC (permalink / raw)
To: Théo Lebrun, Kevin Hao
Cc: christian.taedcke, Conor Dooley, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
Robert Hancock, netdev, linux-kernel, linux-rt-devel, stable
In-Reply-To: <DJUXYXEQMUJ4.31H82KQMG29UC@bootlin.com>
Hello Kevin & Théo,
i recorded some traces and include 2 of them here.
On 7/10/2026 3:56 PM, Théo Lebrun wrote:
> Hello Kevin & Christian,
>
> On Wed Jul 8, 2026 at 5:05 AM CEST, Kevin Hao wrote:
>>> I agree that the TRM says the transmit pointer is reset while TE is low. My
>>> question is whether this describes an internal pointer being reloaded from TBQP,
>>> or whether TBQP itself is restored to the original ring base.
>>
>> The Zynq UltraScale TRM [1] describes the receive-buffer queue pointer as follows:
>>
>> An internal counter represents the receive-buffer queue pointer and it is not
>> visible through the CPU interface.
>>
>> I could not find a similar description for the transmit-buffer queue pointer,
>> but I believe it behaves the same way. From a software perspective, it should
>> be safe to assume that the TBQP is reset to point to the start of the transmit
>> descriptor list upon reset. This assumption is supported by the description
>> of the transmit_q_ptr (GEM) Register [2]:
>>
>> Reading this register returns the location of the descriptor currently being accessed.
>> Since the DMA handles two frames at once, this may not necessarily be pointing to the
>> current frame being transmitted.
>>
>> [1] https://docs.amd.com/v/u/en-US/ug1085-zynq-ultrascale-trm
>> [2] https://docs.amd.com/r/en-US/ug1087-zynq-ultrascale-registers/transmit_q_ptr-GEM-Register
>
> For what it's worth, I agree with Kevin.
>
> It should be rather easy to detect if the patch is needed, with more
> logging. Dump TBQP before link-down & dump it at link-up. The code
> expects TBQP to reset to the ring start automatically whereas this
> commit message says the TBQP after link-up is some offset into the ring.
These traces were captured on a zynqmp device. The patches in this series were not applied.
The traces contain information from both tx queues (q0 and q1).
tbqp contains the value returned by queue_readl(queue, TBQP).
base is lower_32_bits(queue->tx_ring_dma).
tbqp_ctrl contains macb_tx_desc(queue, tbqp_idx)->ctrl.
tail_ctrl contains macb_tx_desc(queue, queue->tx_tail)->ctrl.
At the beginning the link is up and communication over ethernet is working.
The cpu is not at 100% load, everything worked fine.
Trace 1 (everythink works as expected, no high cpu load):
The ethernet link goes down.
Trace from macb_mac_link_down():
kworker/0:4-1679 [000] .N... 141.796925: macb_tx_hw_state: macb_tx_hw_state q0 linkdown_pre_te tbqp=67cd0030 base=67cd0000 ncr=0010001c tsr=00000021 imr=3fffffff head=2 tail=2 tbqp_idx=343 tbqp_ctrl=80000000(used=1) tail_ctrl=80000000(used=1)
kworker/0:4-1679 [000] ..... 141.796958: macb_tx_hw_state: macb_tx_hw_state q1 linkdown_pre_te tbqp=67cda598 base=67cd8000 ncr=0010001c tsr=00000021 imr=00000ce6 head=3985 tail=3985 tbqp_idx=59 tbqp_ctrl=80000036(used=1) tail_ctrl=80000000(used=1)
ctrl = macb_readl(bp, NCR) & ~(MACB_BIT(RE) | MACB_BIT(TE));
macb_writel(bp, NCR, ctrl);
kworker/0:4-1679 [000] ..... 141.796961: macb_tx_hw_state: macb_tx_hw_state q0 linkdown_post_te tbqp=67cd0030 base=67cd0000 ncr=00100010 tsr=00000021 imr=3fffffff head=2 tail=2 tbqp_idx=343 tbqp_ctrl=80000000(used=1) tail_ctrl=80000000(used=1)
kworker/0:4-1679 [000] ..... 141.796964: macb_tx_hw_state: macb_tx_hw_state q1 linkdown_post_te tbqp=67cd8000 base=67cd8000 ncr=00100010 tsr=00000021 imr=00000ce6 head=3985 tail=3985 tbqp_idx=170 tbqp_ctrl=0000800f(used=0) tail_ctrl=80000000(used=1)
Ethernet link goes up.
Trace from macb_mac_link_up():
kworker/0:3-190 [000] ..... 152.966203: macb_tx_hw_state: macb_tx_hw_state q0 linkup_post_shuffle tbqp=67cd0030 base=67cd0000 ncr=00100010 tsr=00000021 imr=3ffff305 head=0 tail=0 tbqp_idx=343 tbqp_ctrl=80000000(used=1) tail_ctrl=80018040(used=1)
kworker/0:3-190 [000] ..... 152.966206: macb_tx_hw_state: macb_tx_hw_state q1 linkup_post_shuffle tbqp=67cd8000 base=67cd8000 ncr=00100010 tsr=00000021 imr=00000004 head=0 tail=0 tbqp_idx=170 tbqp_ctrl=0000800f(used=0) tail_ctrl=0000800f(used=0)
macb_writel(bp, NCR, ctrl | MACB_BIT(RE) | MACB_BIT(TE));
kworker/0:3-190 [000] ..... 152.966209: macb_tx_hw_state: macb_tx_hw_state q0 linkup_post_te tbqp=67cd0030 base=67cd0000 ncr=0010001c tsr=00000021 imr=3ffff305 head=0 tail=0 tbqp_idx=343 tbqp_ctrl=80000000(used=1) tail_ctrl=80018040(used=1)
kworker/0:3-190 [000] ..... 152.966211: macb_tx_hw_state: macb_tx_hw_state q1 linkup_post_te tbqp=67cd8000 base=67cd8000 ncr=0010001c tsr=00000021 imr=00000004 head=0 tail=0 tbqp_idx=170 tbqp_ctrl=0000800f(used=0) tail_ctrl=0000800f(used=0)
CPU load is normal, no issue in this trace.
tbqp on q0 is not reset to base. But in this case it did not result in the interrupt storm.
Trace 2 (results in interrupt storm):
The ethernet link goes down.
Trace from macb_mac_link_down():
kworker/0:3-95 [000] .N... 459.682957: macb_tx_hw_state: macb_tx_hw_state q0 linkdown_pre_te tbqp=67cd2cb8 base=67cd0000 ncr=0010001c tsr=00000021 imr=3fffffff head=4061 tail=4061 tbqp_idx=306 tbqp_ctrl=0000800f(used=0) tail_ctrl=80000000(used=1)
kworker/0:3-95 [000] ..... 459.682991: macb_tx_hw_state: macb_tx_hw_state q1 linkdown_pre_te tbqp=67cd85a0 base=67cd8000 ncr=0010001c tsr=00000021 imr=00000ce6 head=60 tail=60 tbqp_idx=230 tbqp_ctrl=80000036(used=1) tail_ctrl=80000000(used=1)
ctrl = macb_readl(bp, NCR) & ~(MACB_BIT(RE) | MACB_BIT(TE));
macb_writel(bp, NCR, ctrl);
kworker/0:3-95 [000] ..... 459.682994: macb_tx_hw_state: macb_tx_hw_state q0 linkdown_post_te tbqp=67cd2cb8 base=67cd0000 ncr=00100010 tsr=00000021 imr=3fffffff head=4061 tail=4061 tbqp_idx=306 tbqp_ctrl=0000800f(used=0) tail_ctrl=80000000(used=1)
kworker/0:3-95 [000] ..... 459.682996: macb_tx_hw_state: macb_tx_hw_state q1 linkdown_post_te tbqp=67cd8000 base=67cd8000 ncr=00100010 tsr=00000021 imr=00000ce6 head=60 tail=60 tbqp_idx=170 tbqp_ctrl=80000036(used=1) tail_ctrl=80000000(used=1)
Ethernet link goes up.
Trace from macb_mac_link_up():
kworker/0:3-95 [000] ..... 470.292877: macb_tx_hw_state: macb_tx_hw_state q0 linkup_post_shuffle tbqp=67cd2cb8 base=67cd0000 ncr=00100010 tsr=00000021 imr=3ffff305 head=0 tail=0 tbqp_idx=306 tbqp_ctrl=0000800f(used=0) tail_ctrl=0000800f(used=0)
kworker/0:3-95 [000] ..... 470.292879: macb_tx_hw_state: macb_tx_hw_state q1 linkup_post_shuffle tbqp=67cd8000 base=67cd8000 ncr=00100010 tsr=00000021 imr=00000004 head=0 tail=0 tbqp_idx=170 tbqp_ctrl=80000036(used=1) tail_ctrl=80018040(used=1)
macb_writel(bp, NCR, ctrl | MACB_BIT(RE) | MACB_BIT(TE));
kworker/0:3-95 [000] ..... 470.292882: macb_tx_hw_state: macb_tx_hw_state q0 linkup_post_te tbqp=67cd2cb8 base=67cd0000 ncr=0010001c tsr=00000021 imr=3ffff305 head=0 tail=0 tbqp_idx=306 tbqp_ctrl=0000800f(used=0) tail_ctrl=0000800f(used=0)
kworker/0:3-95 [000] ..... 470.292884: macb_tx_hw_state: macb_tx_hw_state q1 linkup_post_te tbqp=67cd8000 base=67cd8000 ncr=0010001c tsr=00000021 imr=00000004 head=0 tail=0 tbqp_idx=170 tbqp_ctrl=80000036(used=1) tail_ctrl=80018040(used=1)
After that sequence one CPU core is at 100% load continously executing the macb irq handler.
The issue seems to be q0. tbqp of q1 is properly reset to its base address.
tbqp of q0 is not changed as expected in link down when tx is disabled.
Are there any other details i could put into the trace to determine the root cause?
I suspect that some packet is still in transmission/ being processed and this might be the
reason why tbqp of q0 is not being reset. But i do not see that in this trace.
>
> Lastly, the cover letter mentions that [PATCH 1/2] alone isn't enough.
> But it doesn't mention that [PATCH 2/2] alone doesn't solve the issue.
> This would be a useful test as well.
>
> On Tue Jul 7, 2026 at 3:36 PM CEST, Taedcke, Christian wrote:
>> Thank you for the quick review! This is my first Linux kernel
>> contribution, so I appreciate your feedback here.
>
> Welcome!
>
> Thanks,
>
> --
> Théo Lebrun, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com/
>
Regards,
Christian
^ permalink raw reply
* [PATCH v4 5/5] vhost/vsock: add VHOST_RESET_OWNER ioctl
From: Andrey Drobyshev @ 2026-07-14 15:16 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, virtualization, netdev, sgarzare, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den, andrey.drobyshev
In-Reply-To: <20260714151638.143019-1-andrey.drobyshev@virtuozzo.com>
From: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
This ioctl is needed for QEMU's CPR (checkpoint-restore) migration of
the guest with vhost-vsock device. For this to work, we need to reset
the device ownership on the source side by calling RESET_OWNER, and then
claim it on the dest side by calling SET_OWNER. We expect not to lose any
AF_VSOCK connection while this happens.
To that end, unlike the release path, RESET_OWNER keeps the guest CID
hashed: established connections survive, and host sends issued while
the device is between owners simply stay on send_pkt_queue until the
next device start drains them.
Since the device stays reachable through the CID hash, the lockless
send/cancel paths can race with the worker teardown in
vhost_workers_free(). The previous commit ("vhost: synchronize with
RCU readers when freeing workers") makes that safe.
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
drivers/vhost/vsock.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index d5022d21120b..86f25ff80722 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -903,6 +903,29 @@ static int vhost_vsock_set_features(struct vhost_vsock *vsock, u64 features)
return -EFAULT;
}
+static long vhost_vsock_reset_owner(struct vhost_vsock *vsock)
+{
+ struct vhost_iotlb *umem;
+ long err;
+
+ mutex_lock(&vsock->dev.mutex);
+ err = vhost_dev_check_owner(&vsock->dev);
+ if (err)
+ goto done;
+ umem = vhost_dev_reset_owner_prepare();
+ if (!umem) {
+ err = -ENOMEM;
+ goto done;
+ }
+ vhost_vsock_drop_backends(vsock);
+ vhost_vsock_flush(vsock);
+ vhost_dev_stop(&vsock->dev);
+ vhost_dev_reset_owner(&vsock->dev, umem);
+done:
+ mutex_unlock(&vsock->dev.mutex);
+ return err;
+}
+
static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
unsigned long arg)
{
@@ -946,6 +969,8 @@ static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
return -EOPNOTSUPP;
vhost_set_backend_features(&vsock->dev, features);
return 0;
+ case VHOST_RESET_OWNER:
+ return vhost_vsock_reset_owner(vsock);
default:
mutex_lock(&vsock->dev.mutex);
r = vhost_dev_ioctl(&vsock->dev, ioctl, argp);
--
2.47.1
^ permalink raw reply related
* [PATCH v4 4/5] vhost: synchronize with RCU readers when freeing workers
From: Andrey Drobyshev @ 2026-07-14 15:16 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, virtualization, netdev, sgarzare, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den, andrey.drobyshev
In-Reply-To: <20260714151638.143019-1-andrey.drobyshev@virtuozzo.com>
vhost_vq_work_queue() only holds the RCU read lock while it dereferences
vq->worker and queues work on it. vhost_workers_free() however clears
the vq->worker pointers and immediately frees the workers, without
waiting for a grace period. A caller that fetched the worker right
before the pointer was cleared can therefore still be queueing work on
it while it is freed. And even when the queueing itself wins the race,
the work is never run, so its VHOST_WORK_QUEUED bit stays set and all
future attempts to queue it are silently skipped.
None of the current callers can actually hit this: net and scsi stop
their virtqueues before the workers are freed, and vsock unhashes the
device and does synchronize_rcu() of its own in vhost_vsock_dev_release()
before the workers go away. But the upcoming VHOST_RESET_OWNER support
in vhost-vsock keeps the device hashed while its workers are freed, so
the lockless send/cancel paths become able to race with the teardown.
Close this the way vhost_worker_killed() already does: clear the
vq->worker pointers, wait for a grace period, run whatever the last
readers may have queued, and only then free the workers. The
synchronize_rcu() is skipped if the device has no workers, so cleanup of
devices which never got an owner stays cheap.
Suggested-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
drivers/vhost/vhost.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 4c525b3e16ea..0d1414d40f4e 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -729,6 +729,21 @@ static void vhost_workers_free(struct vhost_dev *dev)
for (i = 0; i < dev->nvqs; i++)
rcu_assign_pointer(dev->vqs[i]->worker, NULL);
+
+ /*
+ * vhost_vq_work_queue() reads vq->worker under rcu_read_lock(), so a
+ * caller that fetched a worker before we cleared the pointers above
+ * may still be about to queue work on it. Wait for those RCU readers
+ * to finish before freeing the worker, then run whatever they queued
+ * so nothing is left with VHOST_WORK_QUEUED set. Mirrors
+ * vhost_worker_killed().
+ */
+ if (!xa_empty(&dev->worker_xa)) {
+ synchronize_rcu();
+ xa_for_each(&dev->worker_xa, i, worker)
+ vhost_run_work_list(worker);
+ }
+
/*
* Free the default worker we created and cleanup workers userspace
* created but couldn't clean up (it forgot or crashed).
--
2.47.1
^ permalink raw reply related
* [PATCH v4 3/5] vhost/vsock: re-scan TX virtqueue on device start
From: Andrey Drobyshev @ 2026-07-14 15:16 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, virtualization, netdev, sgarzare, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den, andrey.drobyshev
In-Reply-To: <20260714151638.143019-1-andrey.drobyshev@virtuozzo.com>
During QEMU CPR live-update (and VHOST_RESET_OWNER in general) the guest
keeps running while the host drops and later re-attaches vhost backends.
If the guest adds a buffer to the TX virtqueue (guest->host) and kicks
while the backend is temporarily NULL (between vhost_vsock_drop_backends()
and the next vhost_vsock_start()), then the kick is delivered to the
vhost worker, handle_tx_kick() sees a NULL backend and returns, and the
kick signal is consumed. The buffer is then left in the ring.
Then upon device start vhost_vsock_start() only re-kicks the RX send
worker, never the TX VQ, so the buffer is processed only if the guest
happens to kick again. But if the guest itself is now waiting for data
from the host, it will never kick TX VQ again, and we end up in a
deadlock.
The issue itself is pre-existing, but it only manifests during a device
pause caused by VHOST_RESET_OWNER. Namely, the deadlock is reproduced
during active host->guest socat data transfer under multiple consecutive
CPR live-update's.
To fix this, in vhost_vsock_start(), after kicking the RX send worker, also
queue the TX vq poll so any buffers the guest enqueued while we were paused
get scanned.
The VHOST_RESET_OWNER ioctl itself is implemented in the following
patch, thus this patch is a preparation to support VHOST_RESET_OWNER.
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
---
drivers/vhost/vsock.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index 27169a09e87e..d5022d21120b 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -646,6 +646,13 @@ static int vhost_vsock_start(struct vhost_vsock *vsock)
*/
vhost_vq_work_queue(&vsock->vqs[VSOCK_VQ_RX], &vsock->send_pkt_work);
+ /* The guest may have added TX buffers while the device was stopped
+ * (e.g. across VHOST_RESET_OWNER) and their kicks got consumed by
+ * the NULL-backend window. Re-scan the TX VQ, mirroring the RX
+ * send-worker kick above.
+ */
+ vhost_poll_queue(&vsock->vqs[VSOCK_VQ_TX].poll);
+
mutex_unlock(&vsock->dev.mutex);
return 0;
--
2.47.1
^ permalink raw reply related
* [PATCH v4 2/5] vhost/vsock: suppress EHOSTUNREACH fast-fail during CPR pause
From: Andrey Drobyshev @ 2026-07-14 15:16 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, virtualization, netdev, sgarzare, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den, andrey.drobyshev
In-Reply-To: <20260714151638.143019-1-andrey.drobyshev@virtuozzo.com>
Earlier commit bb26ed5f3a8b ("vhost/vsock: Refuse the connection
immediately when guest isn't ready") added a fast-fail in
vhost_transport_send_pkt(). It rejects every host send with -EHOSTUNREACH
until the destination calls SET_RUNNING(1). The fast-fail condition checks
whether device's backends are dropped, and if they're, the guest is
considered to be not ready.
However, there might be other reasons for backends to be nulled. In
particular, when QEMU is performing CPR (checkpoint-restore) migration,
device ownership is being RESET and SET again, which leads to backends
drop and reattach. If we end up connecting during this window, an
AF_VSOCK client gets -EHOSTUNREACH, which is wrong.
Add an 'ever_started' flag which is set once in vhost_vsock_start() and is
never cleared. The behaviour changes to:
* When device was never started -> flag is unset -> no listener can
exist yet -> fast-fail;
* Once the device starts -> flag is set -> we don't fast-fail ->
we queue and preserve during any later stop / CPR pause.
The VHOST_RESET_OWNER ioctl is implemented in a following patch, and
without RESET_OWNER the problem we fix here isn't manifesting - thus
this patch is a preparation to support RESET_OWNER.
Important caveat: after the first start, a connect during any stopped
window is queued instead of fast-failed. That was the behaviour before
the patch bb26ed5f3a8b, and we're restoring it now. However we still
keep the behaviour originally intended by that commit (i.e. fast-fail if
there's no real listener yet) while fixing the CPR path.
Suggested-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Denis V. Lunev <den@openvz.org>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
---
drivers/vhost/vsock.c | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index b12221ce6faf..27169a09e87e 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -61,6 +61,7 @@ struct vhost_vsock {
u32 guest_cid;
bool seqpacket_allow;
+ bool ever_started; /* set on first SET_RUNNING(1); never cleared */
};
static u32 vhost_transport_get_local_cid(void)
@@ -302,17 +303,12 @@ vhost_transport_send_pkt(struct sk_buff *skb, struct net *net)
return -ENODEV;
}
- /* Fast-fail if the guest hasn't enabled the RX vq yet. Queuing the packet
- * and making the caller wait is pointless: even if the guest manages to init
- * within the timeout, it'll immediately reply with RST, because there's no
- * listener on the port yet.
- *
- * vhost_vq_get_backend() without vq->mutex is acceptable here: locking
- * the mutex would be too expensive in this hot path, and we already have
- * all the outcomes covered: if the backend becomes NULL right after the check,
- * vhost_transport_do_send_pkt() will check it under the mutex anyway.
+ /* Fast-fail until the guest first enables the device (SET_RUNNING(1)).
+ * Before that there is no listener, so queuing is pointless.
+ * 'ever_started' is never cleared, so once we're up we keep queuing
+ * across later stop / CPR-pause windows.
*/
- if (unlikely(!data_race(vhost_vq_get_backend(&vsock->vqs[VSOCK_VQ_RX])))) {
+ if (unlikely(!READ_ONCE(vsock->ever_started))) {
rcu_read_unlock();
kfree_skb(skb);
return -EHOSTUNREACH;
@@ -640,6 +636,11 @@ static int vhost_vsock_start(struct vhost_vsock *vsock)
mutex_unlock(&vq->mutex);
}
+ /* Set 'ever_started' flag on the first start; never cleared, so send_pkt
+ * keeps queuing (instead of fast-failing) on later stop / CPR pauses.
+ */
+ WRITE_ONCE(vsock->ever_started, true);
+
/* Some packets may have been queued before the device was started,
* let's kick the send worker to send them.
*/
@@ -728,6 +729,7 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
vsock->guest_cid = 0; /* no CID assigned yet */
vsock->seqpacket_allow = false;
+ vsock->ever_started = false;
atomic_set(&vsock->queued_replies, 0);
--
2.47.1
^ permalink raw reply related
* [PATCH v4 1/5] vhost/vsock: split out vhost_vsock_drop_backends helper
From: Andrey Drobyshev @ 2026-07-14 15:16 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, virtualization, netdev, sgarzare, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den, andrey.drobyshev
In-Reply-To: <20260714151638.143019-1-andrey.drobyshev@virtuozzo.com>
From: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Split the actual backend dropping part from vhost_vsock_stop. We're
going to need it for the VHOST_RESET_OWNER implementation in the
following patch, when vsock->dev.mutex is already taken and owner is
checked.
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Reviewed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
---
drivers/vhost/vsock.c | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index 9aaab6bb8061..b12221ce6faf 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -664,9 +664,24 @@ static int vhost_vsock_start(struct vhost_vsock *vsock)
return ret;
}
-static int vhost_vsock_stop(struct vhost_vsock *vsock, bool check_owner)
+static void vhost_vsock_drop_backends(struct vhost_vsock *vsock)
{
+ struct vhost_virtqueue *vq;
size_t i;
+
+ lockdep_assert_held(&vsock->dev.mutex);
+
+ for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) {
+ vq = &vsock->vqs[i];
+
+ mutex_lock(&vq->mutex);
+ vhost_vq_set_backend(vq, NULL);
+ mutex_unlock(&vq->mutex);
+ }
+}
+
+static int vhost_vsock_stop(struct vhost_vsock *vsock, bool check_owner)
+{
int ret = 0;
mutex_lock(&vsock->dev.mutex);
@@ -677,14 +692,7 @@ static int vhost_vsock_stop(struct vhost_vsock *vsock, bool check_owner)
goto err;
}
- for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) {
- struct vhost_virtqueue *vq = &vsock->vqs[i];
-
- mutex_lock(&vq->mutex);
- vhost_vq_set_backend(vq, NULL);
- mutex_unlock(&vq->mutex);
- }
-
+ vhost_vsock_drop_backends(vsock);
err:
mutex_unlock(&vsock->dev.mutex);
return ret;
--
2.47.1
^ permalink raw reply related
* [PATCH v4 0/5] vhost/vsock: add support for VHOST_RESET_OWNER and CPR migration
From: Andrey Drobyshev @ 2026-07-14 15:16 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, virtualization, netdev, sgarzare, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den, andrey.drobyshev
The main change since v3: as suggested by Stefano, the worker teardown
race is now fixed where it actually lives, i.e. in vhost_workers_free(),
instead of being guarded from the vsock side. The new patch 4 makes the
teardown wait out the RCU readers which might still be queueing work on
a worker, and then run whatever they queued, before the workers are
freed.
With the teardown itself made safe, queueing work on a stopped device
is harmless again: the work handlers check the backend under vq->mutex
and simply return. So the backend guards in send_pkt()/cancel_pkt()
and the synchronize_rcu() in reset_owner() from v3 are no longer needed
and are gone.
v3 -> v4:
* Patch 2:
- rename 'started' -> 'ever_started';
- reword commit message;
* Patch 3: reword commit message and code comment;
* Patch 4 (NEW) ("vhost: synchronize with RCU readers when freeing workers"):
- fix the vq->worker UAF and the stuck VHOST_WORK_QUEUED bit
generically in vhost_workers_free();
* Patch 4 -> 5 ("vhost/vsock: add VHOST_RESET_OWNER ioctl"):
- drop the backend guards in send_pkt()/cancel_pkt() and the
synchronize_rcu() in reset_owner() - covered by patch 4 now;
- make vhost_vsock_reset_owner() return long;
- reword commit message.
v3: https://lore.kernel.org/virtualization/20260625155416.480669-1-andrey.drobyshev@virtuozzo.com
Andrey Drobyshev (3):
vhost/vsock: suppress EHOSTUNREACH fast-fail during CPR pause
vhost/vsock: re-scan TX virtqueue on device start
vhost: synchronize with RCU readers when freeing workers
Pavel Tikhomirov (2):
vhost/vsock: split out vhost_vsock_drop_backends helper
vhost/vsock: add VHOST_RESET_OWNER ioctl
drivers/vhost/vhost.c | 15 ++++++++++
drivers/vhost/vsock.c | 80 +++++++++++++++++++++++++++++++++++++++------------
2 files changed, 76 insertions(+), 19 deletions(-)
--
2.47.1
^ permalink raw reply
* Re: [PATCH net-next 1/2] dt-bindings: net: add DAPU Telecom DAP8211R(I) PHY binding
From: Artem Shimko @ 2026-07-14 15:11 UTC (permalink / raw)
To: Rob Herring
Cc: netdev, Andrew Lunn, Heiner Kallweit, Russell King,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Krzysztof Kozlowski, Conor Dooley, linux-kernel, devicetree
In-Reply-To: <20260713172244.GA2381778-robh@kernel.org>
Hi Rob,
On Mon, Jul 13, 2026 at 8:22 PM Rob Herring <robh@kernel.org> wrote:
> This would also work:
>
> multipleOf: 150
> maximum: 2250
Yeah, great
> With this fixed,
It will be fixed in v2.
Thanks for your review!
--
Best Regards,
Artem
^ permalink raw reply
* Re: [PATCH net-next] net: Convert %pK back to %p
From: Sebastian Andrzej Siewior @ 2026-07-14 15:09 UTC (permalink / raw)
To: Kees Cook
Cc: linux-atm-general, linux-can, linux-sctp, netdev, David S. Miller,
Eric Dumazet, Herbert Xu, Jakub Kicinski, Kuniyuki Iwashima,
Marc Kleine-Budde, Marcelo Ricardo Leitner, Neal Cardwell,
Oliver Hartkopp, Paolo Abeni, Remi Denis-Courmont, Simon Horman,
Steffen Klassert, Willem de Bruijn, Xin Long, Petr Mladek,
Thomas Weißschuh
In-Reply-To: <202607090916.7731D36D@keescook>
tl;dr: Do the networking folks mind switch it to 0 instead the pointer?
On 2026-07-09 09:18:44 [-0700], Kees Cook wrote:
> On Mon, Jul 06, 2026 at 09:38:24AM +0200, Sebastian Andrzej Siewior wrote:
> > This is a revert of commit 71338aa7d050c ("net: convert %p usage to
> > %pK") which is from 2011. Back then the default behaviour for %p was to
> > print the pointer. The %pK modifier was introduced to be able to control
> > the behaviour of specific pointer output without changing the behaviour
> > of %p for everyone. It was dedicated to avoid leaking pointers via
> > /proc.
>
> Given the policy on bare %p, and that there are so few in this list (15
> files), how about review those that can just simply be removed or
> switched to %pS, etc:
> https://docs.kernel.org/process/deprecated.html#p-format-specifier
It is not a new use, but an old one ;)
The pointers are data pointers of sockets and so on, not code. So using
%pS will reveal the exact pointers even with hashing enabled (in case
you think about changing the behaviour for __sprint_symbol() for cases
where kallsyms fails to resolve the symbol).
The things here are "reports" such as /proc/net/icmp where you get
|# cat /proc/net/icmp
| sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops
| 53: 00000000:C9F2 00000000:0000 07 00000000:00000000 00:00000000 00000000 1000 0 4569 2 000000001145b7f6 0
so this is probably considered as ABI. lsof, lsfd (util-linux) are using
this file. So I don't think this entry can be removed. These kind of
files have usually a flexible ABI and are fine with adding new
attributes but not removing existing ones.
In this cases we usually put 0 if we remove an entry.
The pointer in icmp has been added int commit c319b4d76b9e5 ("net: ipv4:
add IPPROTO_ICMP socket kind") and no explanation why. But the order is
the same as in the tcp or raw file. I traced the tcp pointer inclusion
back to 2.3.15pre3 with no explanation. It just appeared with bunch of
other changes so maybe making debug a bit easier.
Anyway, given all this, do the networking folks mind switch it to 0
instead the pointer?
Sebastian
^ permalink raw reply
* Re: [PATCH v2] sctp: fix auth_chunk_list capacity check in sctp_auth_ep_add_chunkid
From: Xin Long @ 2026-07-14 14:56 UTC (permalink / raw)
To: 寒泉
Cc: marcelo.leitner, davem, edumazet, kuba, pabeni, horms, linux-sctp,
netdev, linux-kernel
In-Reply-To: <20260713032021.3491702-1-zhoujian.zja@antgroup.com>
On Sun, Jul 12, 2026 at 11:21 PM 寒泉 <eilaimemedsnaimel@gmail.com> wrote:
>
> From: HanQuan <eilaimemedsnaimel@gmail.com>
>
> sctp_auth_ep_add_chunkid() uses SCTP_NUM_CHUNK_TYPES (20) as the
> capacity limit for ep->auth_chunk_list, allowing it to hold up to
> 20 chunk entries (param_hdr.length up to 24). However, the copy
> destination asoc->c.auth_chunks in struct sctp_cookie is only
> SCTP_AUTH_MAX_CHUNKS (16) entries (20 bytes). When more than 16
> chunks are added, sctp_association_init() memcpy overflows the
> destination by up to 4 bytes.
>
> Fix by using SCTP_AUTH_MAX_CHUNKS as the capacity limit, matching
> the destination capacity.
>
> Fixes: 1f485649f529 ("[SCTP]: Implement SCTP-AUTH internals")
> Signed-off-by: HanQuan <eilaimemedsnaimel@gmail.com>
> ---
> net/sctp/auth.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/net/sctp/auth.c b/net/sctp/auth.c
> index be9782760f50..c901d373af80 100644
> --- a/net/sctp/auth.c
> +++ b/net/sctp/auth.c
> @@ -672,7 +672,7 @@ int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id)
> /* Check if we can add this chunk to the array */
> param_len = ntohs(p->param_hdr.length);
> nchunks = param_len - sizeof(struct sctp_paramhdr);
> - if (nchunks == SCTP_NUM_CHUNK_TYPES)
> + if (nchunks == SCTP_AUTH_MAX_CHUNKS)
> return -EINVAL;
>
> p->chunks[nchunks] = chunk_id;
> --
> 2.43.0
>
Acked-by: Xin Long <lucien.xin@gmail.com>
^ permalink raw reply
* Re: [PATCH] idpf: disable PCIe PTM on device removal
From: Tantilov, Emil S @ 2026-07-14 14:54 UTC (permalink / raw)
To: Myeonghun Pak, Tony Nguyen, Przemek Kitszel, intel-wired-lan
Cc: Milena Olech, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, linux-kernel, Ijae Kim
In-Reply-To: <20260714081124.90962-1-mhun512@gmail.com>
On 7/14/2026 1:11 AM, Myeonghun Pak wrote:
> idpf_probe() enables PCIe Precision Time Measurement with
> pci_enable_ptm(pdev, NULL), which programs the PTM control bits and sets
> pdev->ptm_enabled when the bus/controller supports it. The teardown path
> in idpf_remove() releases the workqueues, vports, mutexes and the adapter
> memory but never calls pci_disable_ptm(), so PTM is left enabled on the
> device after the driver detaches.
>
> This leaves the PCI core's software PTM state and the device's PTM control
> bits set with no bound driver. pcim_enable_device() only arranges for
> pci_disable_device() on teardown and does not undo the PTM enable, so it
> is not a substitute here.
>
> Pair the enable with pci_disable_ptm(pdev) in idpf_remove(), matching the
> igc and mlx5 drivers which already disable PTM on their remove paths.
>
> Fixes: 8d5e12c5921c ("idpf: add initial PTP support")
> Co-developed-by: Ijae Kim <ae878000@gmail.com>
> Signed-off-by: Ijae Kim <ae878000@gmail.com>
> Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
> ---
> drivers/net/ethernet/intel/idpf/idpf_main.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c
> index 0dd741dcfc..3d3471d3f7 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf_main.c
> +++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
> @@ -159,6 +159,7 @@ static void idpf_remove(struct pci_dev *pdev)
> mutex_destroy(&adapter->queue_lock);
> mutex_destroy(&adapter->vc_buf_lock);
>
> + pci_disable_ptm(pdev);
> pci_set_drvdata(pdev, NULL);
> kfree(adapter);
> }
Reviewed-by: Emil Tantilov <emil.s.tantilov@intel.com>
^ permalink raw reply
* Re: [PATCH net 1/1] net: smc: fix splice entry lifetime imbalance in smc_rx_splice
From: Sidraya Jayagond @ 2026-07-14 14:35 UTC (permalink / raw)
To: Ren Wei, linux-rdma, linux-s390, netdev
Cc: alibuda, dust.li, wenjia, mjambigi, tonylu, guwen, ubraun,
stefan.raspl, davem, yuantan098, zcliangcn, bird, lx24,
d4n.for.sec
In-Reply-To: <430a9dd9-ecfb-4465-9eeb-f854fbbc2e61@linux.ibm.com>
On 16/06/26 7:57 pm, Sidraya Jayagond wrote:
>
>
> On 10/06/26 11:24 pm, Ren Wei wrote:
>> From: Daming Li <d4n.for.sec@gmail.com>
>>
>> smc_rx_splice() hands candidate pages to splice_to_pipe() without taking
>> references for the lifetime of each splice entry first. That breaks the
>> splice ownership contract in the VM-backed RMB path.
>>
>> splice_to_pipe() drops unqueued entries through spd_release(), while
>> queued entries are later dropped through the pipe buffer release
>> callback. The current code only tries to take page references after the
>> splice succeeds, and it derives the number of queued VM pages from a
>> mutated offset value. This can underflow page refcounts and trigger a
>> use-after-free. It also leaves the socket lifetime imbalanced in the
>> multi-page VM case, where one sock_hold() can be followed by multiple
>> sock_put() calls.
>>
>> Fix this by taking the page and socket references for every candidate
>> splice entry before calling splice_to_pipe(), and by releasing the
>> matching private state, page reference, and socket reference from
>> smc_rx_spd_release() for entries that never get queued. This makes the
>> SMC splice path follow the normal splice lifetime rules and removes the
>> broken post-splice VM page counting entirely.
>>
>> Fixes: 9014db202cb7 ("smc: add support for splice()")
>> Cc: stable@vger.kernel.org
>> Reported-by: Yuan Tan <yuantan098@gmail.com>
>> Reported-by: Zhengchuan Liang <zcliangcn@gmail.com>
>> Reported-by: Xin Liu <bird@lzu.edu.cn>
>> Assisted-by: Codex:GPT-5.4
>> Co-developed-by: Liu Xiao <lx24@stu.ynu.edu.cn>
>> Signed-off-by: Liu Xiao <lx24@stu.ynu.edu.cn>
>> Signed-off-by: Daming Li <d4n.for.sec@gmail.com>
>> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
>> ---
>> net/smc/smc_rx.c | 21 +++++++++++----------
>> 1 file changed, 11 insertions(+), 10 deletions(-)
>>
>> diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c
>> index c1d9b923938d..88aee0d93597 100644
>> --- a/net/smc/smc_rx.c
>> +++ b/net/smc/smc_rx.c
>> @@ -150,18 +150,23 @@ static const struct pipe_buf_operations smc_pipe_ops = {
>> static void smc_rx_spd_release(struct splice_pipe_desc *spd,
>> unsigned int i)
>> {
>> + struct smc_spd_priv *priv = (struct smc_spd_priv *)spd->partial[i].private;
>> + struct sock *sk = &priv->smc->sk;
>> +
>> + kfree(priv);
>> put_page(spd->pages[i]);
>> + sock_put(sk);
>> }
>>
>> static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
>> struct smc_sock *smc)
>> {
>> struct smc_link_group *lgr = smc->conn.lgr;
>> - int offset = offset_in_page(src);
>> struct partial_page *partial;
>> struct splice_pipe_desc spd;
>> struct smc_spd_priv **priv;
>> struct page **pages;
>> + int offset = offset_in_page(src);
>> int bytes, nr_pages;
>> int i;
>>
>> @@ -209,6 +214,10 @@ static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
>> offset = 0;
>> }
>> }
>> + for (i = 0; i < nr_pages; i++) {
>> + get_page(pages[i]);
>> + sock_hold(&smc->sk);
>> + }
>> spd.nr_pages_max = nr_pages;
>> spd.nr_pages = nr_pages;
>> spd.pages = pages;
>> @@ -217,16 +226,8 @@ static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
>> spd.spd_release = smc_rx_spd_release;
>>
>> bytes = splice_to_pipe(pipe, &spd);
>> - if (bytes > 0) {
>> - sock_hold(&smc->sk);
>> - if (!lgr->is_smcd && smc->conn.rmb_desc->is_vm) {
>> - for (i = 0; i < PAGE_ALIGN(bytes + offset) / PAGE_SIZE; i++)
>> - get_page(pages[i]);
>> - } else {
>> - get_page(smc->conn.rmb_desc->pages);
>> - }
>> + if (bytes > 0)
>> atomic_add(bytes, &smc->conn.splice_pending);
>> - }
>> kfree(priv);
>> kfree(partial);
>> kfree(pages);
> Code changes looks good to me.
> Reviewed-by: Sidraya Jayagond <sidraya@linux.ibm.com>
>
Hi Ren wei,
will you be sending v2 patch fixing minor nit-picks suggested by Dust Li?
^ permalink raw reply
* [PATCH rdma-next v2 14/14] RDMA/selftests: Add rxe_netns_names test
From: Jiri Pirko @ 2026-07-14 14:29 UTC (permalink / raw)
To: linux-rdma
Cc: cgroups, netdev, linux-s390, linux-kselftest, jgg, leon, parav,
mbloch, cmeiohas, roman.gushchin, bvanassche, zyjzyj2000, shuah,
tj, mkoutny, hannes, alibuda, dust.li, sidraya, wenjia,
yanjun.zhu, cui.tao
In-Reply-To: <20260714142927.1298897-1-jiri@resnulli.us>
From: Jiri Pirko <jiri@nvidia.com>
Add a kselftest script that exercises per-netns RDMA device naming
with RXE. Cover duplicate names across namespaces, move conflict
handling, move-with-rename, and same-namespace rename requests.
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
v1->v2:
- fixed ktap_set_plan
- s/RXE_A/RXE_SAME/ in dup rename
---
tools/testing/selftests/rdma/Makefile | 3 +-
tools/testing/selftests/rdma/config | 2 +
.../testing/selftests/rdma/rxe_netns_names.sh | 282 ++++++++++++++++++
3 files changed, 286 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/rdma/rxe_netns_names.sh
diff --git a/tools/testing/selftests/rdma/Makefile b/tools/testing/selftests/rdma/Makefile
index 07af7f15c1bf..a91c14c45006 100644
--- a/tools/testing/selftests/rdma/Makefile
+++ b/tools/testing/selftests/rdma/Makefile
@@ -3,6 +3,7 @@ TEST_PROGS := rxe_rping_between_netns.sh \
rxe_ipv6.sh \
rxe_socket_with_netns.sh \
rxe_test_NETDEV_UNREGISTER.sh \
- rxe_sent_rcvd_bytes.sh
+ rxe_sent_rcvd_bytes.sh \
+ rxe_netns_names.sh
include ../lib.mk
diff --git a/tools/testing/selftests/rdma/config b/tools/testing/selftests/rdma/config
index 4ffb814e253b..e1ff54ec0f57 100644
--- a/tools/testing/selftests/rdma/config
+++ b/tools/testing/selftests/rdma/config
@@ -1,3 +1,5 @@
CONFIG_TUN
CONFIG_VETH
+CONFIG_DUMMY
+CONFIG_NET_NS
CONFIG_RDMA_RXE
diff --git a/tools/testing/selftests/rdma/rxe_netns_names.sh b/tools/testing/selftests/rdma/rxe_netns_names.sh
new file mode 100755
index 000000000000..a4fabf86bdf7
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_netns_names.sh
@@ -0,0 +1,282 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Exercise RDMA device name handling across network namespaces.
+
+source "$(dirname "$0")/../kselftest/ktap_helpers.sh"
+
+NAME_PREFIX="rxe_netns_names_$$"
+NETDEV_PREFIX="rxn$$"
+NS1="${NAME_PREFIX}ns1"
+NS2="${NAME_PREFIX}ns2"
+RXE_A="${NAME_PREFIX}rxe_a"
+RXE_B="${NAME_PREFIX}rxe_b"
+RXE_SAME="${NAME_PREFIX}rxe_same"
+RXE_NEW="${NAME_PREFIX}rxe_new"
+DUMMY_A="${NETDEV_PREFIX}a"
+DUMMY_B="${NETDEV_PREFIX}b"
+OLD_MODE=""
+MODE_CHANGED=0
+MODS=("dummy" "rdma_rxe")
+TEST_SAME_NAMES="same RDMA device name can exist in two net namespaces"
+TEST_MOVE_CONFLICT="move without rename fails on destination name conflict"
+TEST_MOVE_RENAME="move then rename succeeds"
+TEST_COMBINED_MOVE_RENAME="move with requested destination name succeeds"
+TEST_SAME_NETNS_DUP_RENAME="same-netns rename rejects duplicate name"
+TEST_TEARDOWN_RETURN="netns delete returns device to init_net and renames on conflict"
+
+ksft_skip()
+{
+ ktap_skip_all "$*"
+ exit "$KSFT_SKIP"
+}
+
+fail()
+{
+ ktap_exit_fail_msg "$*"
+}
+
+need_cmd()
+{
+ command -v "$1" >/dev/null 2>&1 || ksft_skip "missing command: $1"
+}
+
+rdma_ns()
+{
+ local ns=$1
+
+ shift
+ ip netns exec "$ns" rdma "$@"
+}
+
+rdma_dev_exists()
+{
+ local ns=$1
+ local dev=$2
+
+ if [ -n "$ns" ]; then
+ rdma_ns "$ns" dev show "$dev" >/dev/null 2>&1
+ else
+ rdma dev show "$dev" >/dev/null 2>&1
+ fi
+}
+
+add_dummy()
+{
+ local netdev=$1
+
+ ip link add "$netdev" type dummy || return 1
+ ip link set "$netdev" up || return 1
+}
+
+add_rxe()
+{
+ local dev=$1
+ local netdev=$2
+
+ rdma link add "$dev" type rxe netdev "$netdev"
+}
+
+rdma_dev_on_netdev()
+{
+ local netdev=$1
+
+ rdma link show 2>/dev/null | awk -v want="$netdev" '
+ {
+ for (i = 1; i < NF; i++)
+ if ($i == "netdev" && $(i + 1) == want) {
+ dev = $2
+ sub(/\/.*/, "", dev)
+ print dev
+ exit
+ }
+ }'
+}
+
+wait_rdma_dev_on_netdev()
+{
+ local netdev=$1
+ local dev
+ local i
+
+ for i in $(seq 1 50); do
+ dev=$(rdma_dev_on_netdev "$netdev")
+ if [ -n "$dev" ]; then
+ echo "$dev"
+ return 0
+ fi
+ sleep 0.1
+ done
+
+ return 1
+}
+
+setup_devs()
+{
+ cleanup_devs
+
+ add_dummy "$DUMMY_A" || return 1
+ add_dummy "$DUMMY_B" || return 1
+
+ add_rxe "$RXE_A" "$DUMMY_A" || return 1
+ add_rxe "$RXE_B" "$DUMMY_B" || return 1
+}
+
+cleanup_devs()
+{
+ ip link del "$DUMMY_A" 2>/dev/null
+ ip link del "$DUMMY_B" 2>/dev/null
+}
+
+setup()
+{
+ OLD_MODE=$(rdma system show 2>/dev/null |
+ sed -n 's/.*netns \([^ ]*\).*/\1/p')
+ [ -n "$OLD_MODE" ] || ksft_skip "failed to read RDMA netns mode"
+
+ rdma system set netns exclusive >/dev/null 2>&1 ||
+ ksft_skip "rdma netns exclusive mode is not supported"
+ MODE_CHANGED=1
+
+ ip netns add "$NS1" || return 1
+ ip netns add "$NS2" || return 1
+}
+
+cleanup()
+{
+ cleanup_devs
+
+ ip netns del "$NS1" 2>/dev/null
+ ip netns del "$NS2" 2>/dev/null
+
+ if [ "$MODE_CHANGED" -eq 1 ]; then
+ rdma system set netns "$OLD_MODE" 2>/dev/null
+ fi
+
+ for m in "${MODS[@]}"; do
+ modprobe -r "$m" 2>/dev/null
+ done
+}
+
+rdma_supports_combined_move_rename()
+{
+ rdma dev help 2>&1 | grep -Eq 'netns .*name|name .*netns'
+}
+
+[ "$(id -u)" -eq 0 ] || ksft_skip "must be run as root"
+need_cmd ip
+need_cmd rdma
+need_cmd modprobe
+
+trap cleanup EXIT
+
+for m in "${MODS[@]}"; do
+ modinfo "$m" >/dev/null 2>&1 || ksft_skip "module $m not found"
+ modprobe "$m" || fail "failed to load $m"
+done
+
+setup || fail "failed to create net namespaces"
+
+ktap_print_header
+ktap_set_plan 6
+
+if setup_devs &&
+ rdma dev set "$RXE_A" netns "$NS1" &&
+ rdma_ns "$NS1" dev set "$RXE_A" name "$RXE_SAME" &&
+ rdma dev set "$RXE_B" netns "$NS2" &&
+ rdma_ns "$NS2" dev set "$RXE_B" name "$RXE_SAME" &&
+ rdma_dev_exists "$NS1" "$RXE_SAME" &&
+ rdma_dev_exists "$NS2" "$RXE_SAME"; then
+ ktap_test_pass "$TEST_SAME_NAMES"
+else
+ ktap_test_fail "$TEST_SAME_NAMES"
+fi
+cleanup_devs
+
+if ! setup_devs ||
+ ! rdma dev set "$RXE_A" netns "$NS1" ||
+ ! rdma_ns "$NS1" dev set "$RXE_A" name "$RXE_SAME" ||
+ ! rdma dev set "$RXE_B" netns "$NS2" ||
+ ! rdma_ns "$NS2" dev set "$RXE_B" name "$RXE_SAME"; then
+ ktap_test_fail "$TEST_MOVE_CONFLICT"
+elif rdma_ns "$NS1" dev set "$RXE_SAME" netns "$NS2" >/dev/null 2>&1; then
+ ktap_test_fail "$TEST_MOVE_CONFLICT"
+elif rdma_dev_exists "$NS1" "$RXE_SAME" &&
+ rdma_dev_exists "$NS2" "$RXE_SAME"; then
+ ktap_test_pass "$TEST_MOVE_CONFLICT"
+else
+ ktap_test_fail "$TEST_MOVE_CONFLICT"
+fi
+cleanup_devs
+
+if ! setup_devs; then
+ ktap_test_fail "$TEST_MOVE_RENAME"
+elif rdma dev set "$RXE_A" netns "$NS2" &&
+ rdma_ns "$NS2" dev set "$RXE_A" name "$RXE_NEW"; then
+ if rdma_dev_exists "$NS2" "$RXE_NEW" &&
+ ! rdma_dev_exists "" "$RXE_A"; then
+ ktap_test_pass "$TEST_MOVE_RENAME"
+ else
+ ktap_test_fail "$TEST_MOVE_RENAME"
+ fi
+else
+ ktap_test_fail "$TEST_MOVE_RENAME"
+fi
+cleanup_devs
+
+if ! rdma_supports_combined_move_rename; then
+ ktap_test_skip "$TEST_COMBINED_MOVE_RENAME"
+elif ! setup_devs; then
+ ktap_test_fail "$TEST_COMBINED_MOVE_RENAME"
+elif rdma dev set "$RXE_A" netns "$NS2" name "$RXE_NEW"; then
+ if rdma_dev_exists "$NS2" "$RXE_NEW" &&
+ ! rdma_dev_exists "" "$RXE_A"; then
+ ktap_test_pass "$TEST_COMBINED_MOVE_RENAME"
+ else
+ ktap_test_fail "$TEST_COMBINED_MOVE_RENAME"
+ fi
+else
+ ktap_test_fail "$TEST_COMBINED_MOVE_RENAME"
+fi
+cleanup_devs
+
+if ! setup_devs; then
+ ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME"
+elif rdma dev set "$RXE_A" name "$RXE_SAME" &&
+ rdma dev set "$RXE_B" name "$RXE_NEW"; then
+ if rdma dev set "$RXE_SAME" name "$RXE_NEW" >/dev/null 2>&1; then
+ ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME"
+ elif rdma_dev_exists "" "$RXE_SAME" &&
+ rdma_dev_exists "" "$RXE_NEW"; then
+ ktap_test_pass "$TEST_SAME_NETNS_DUP_RENAME"
+ else
+ ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME"
+ fi
+else
+ ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME"
+fi
+cleanup_devs
+
+if ! setup_devs; then
+ ktap_test_fail "$TEST_TEARDOWN_RETURN"
+elif ! rdma dev set "$RXE_A" name "$RXE_SAME" ||
+ ! rdma dev set "$RXE_B" netns "$NS2" ||
+ ! rdma_ns "$NS2" dev set "$RXE_B" name "$RXE_SAME" ||
+ ! rdma_dev_exists "$NS2" "$RXE_SAME"; then
+ ktap_test_fail "$TEST_TEARDOWN_RETURN"
+else
+ ip netns del "$NS2"
+ returned=$(wait_rdma_dev_on_netdev "$DUMMY_B")
+ ktap_print_msg "device returned to init_net as '${returned:-<missing>}'"
+ if rdma_dev_exists "" "$RXE_SAME" &&
+ [ -n "$returned" ] &&
+ [ "$returned" != "$RXE_SAME" ] &&
+ [ "${returned#ibdev}" != "$returned" ]; then
+ ktap_test_pass "$TEST_TEARDOWN_RETURN"
+ else
+ ktap_test_fail "$TEST_TEARDOWN_RETURN"
+ fi
+fi
+cleanup_devs
+
+ktap_finished
--
2.54.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox