* [PATCH net-next v11 0/2] net: mana: add ethtool private flag for full-page RX buffers
From: Dipayaan Roy @ 2026-07-01 14:15 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, andrew+netdev, davem, edumazet,
kuba, pabeni, leon, longli, kotaranov, horms, shradhagupta,
ssengar, ernis, shirazsaleem, linux-hyperv, netdev, linux-kernel,
linux-rdma, stephen, jacob.e.keller, dipayanroy, leitao, kees,
john.fastabend, hawk, bpf, daniel, ast, sdf, yury.norov,
pavan.chebbi
On some ARM64 platforms with 4K PAGE_SIZE, utilizing page_pool
fragments for allocation in the RX refill path (~2kB buffer per fragment)
causes 15-20% throughput regression under high connection counts
(>16 TCP streams at 180+ Gbps). Using full-page buffers on these
platforms shows no regression and restores line-rate performance.
This behavior is observed on a single platform; other platforms
perform better with page_pool fragments, indicating this is not a
page_pool issue but platform-specific.
This series adds an ethtool private flag "full-page-rx" to let the
user opt in to one RX buffer per page:
ethtool --set-priv-flags eth0 full-page-rx on
There is no behavioral change by default. The flag can be persisted
via udev rule for affected platforms.
This series depends on the following fixes now merged in net-next:
commit 17bfe0a8c014 ("net: mana: Add NULL guards in teardown path to prevent panic on attach failure")
commit 5b05aa36ee24 ("net: mana: Skip redundant detach on already-detached port")
Changes in v11:
- Rebased on net-next
Changes in v10:
- Rebased on net-next which now includes the prerequisite fixes.
- Recovery logic in mana_set_priv_flags() leverages the idempotent
mana_detach() from the merged fixes.
Changes in v9:
- Added correct tree.
Changes in v8:
- Fixed queue_reset_work recovery by restoring port_is_up before
scheduling reset so the handler can properly re-attach.
- Simplified "err && schedule_port_reset" to "schedule_port_reset".
Changes in v7:
- Rebased onto net-next.
- Retained private flag approach after David Wei's testing on
Grace (ARM64) confirmed that fragment mode outperforms
full-page mode on other platforms, validating this is a
single-platform workaround rather than a generic issue.
Changes in v6:
- Added missed maintainers.
Changes in v5:
- Split prep refactor into separate patch (patch 1/2)
Changes in v4:
- Dropping the smbios string parsing and add ethtool priv flag
to reconfigure the queues with full page rx buffers.
Changes in v3:
- changed u8* to char*
Changes in v2:
- separate reading string index and the string, remove inline.
Dipayaan Roy (2):
net: mana: refactor mana_get_strings() and mana_get_sset_count() to
use switch
net: mana: force full-page RX buffers via ethtool private flag
drivers/net/ethernet/microsoft/mana/mana_en.c | 22 ++-
.../ethernet/microsoft/mana/mana_ethtool.c | 178 +++++++++++++++---
include/net/mana/mana.h | 8 +
3 files changed, 177 insertions(+), 31 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH net 2/2] octeon_ep_vf: fix skb frags overflow in the RX path
From: Maciej Fijalkowski @ 2026-07-01 14:17 UTC (permalink / raw)
To: Maoyi Xie
Cc: Veerasenareddy Burru, Sathesh Edara, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, linux-kernel
In-Reply-To: <20260701112825.1653044-3-maoyixie.tju@gmail.com>
On Wed, Jul 01, 2026 at 07:28:25PM +0800, Maoyi Xie wrote:
> __octep_vf_oq_process_rx() has the same unbounded fragment loop as the PF
> driver. buff_info->len comes from the device response header, and one
> fragment is added per buffer_size chunk with no check against
> MAX_SKB_FRAGS. A long packet yields about 18 fragments, one past the
> default MAX_SKB_FRAGS of 17, so skb_add_rx_frag() writes past
> shinfo->frags[].
>
> The driver now drops a packet that would need more fragments than the skb
> can hold. It drains the descriptors the same way the build_skb failure
> path does.
>
> Fixes: 1cd3b407977c ("octeon_ep_vf: add Tx/Rx processing and interrupt support")
> Co-developed-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
> Signed-off-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
> Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
> ---
> .../ethernet/marvell/octeon_ep_vf/octep_vf_rx.c | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> index d982474082423..2e666df26b4c3 100644
> --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> @@ -463,6 +463,23 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
>
> shinfo = skb_shinfo(skb);
> data_len = buff_info->len - oq->max_single_buffer_size;
> + if (DIV_ROUND_UP(data_len, oq->buffer_size) > MAX_SKB_FRAGS) {
> + dev_kfree_skb_any(skb);
> + while (data_len) {
> + dma_unmap_page(oq->dev, oq->desc_ring[read_idx].buffer_ptr,
> + PAGE_SIZE, DMA_FROM_DEVICE);
> + buff_info = (struct octep_vf_rx_buffer *)
> + &oq->buff_info[read_idx];
> + buff_info->page = NULL;
> + if (data_len < oq->buffer_size)
> + data_len = 0;
> + else
> + data_len -= oq->buffer_size;
> + desc_used++;
> + read_idx = octep_vf_oq_next_idx(oq, read_idx);
> + }
> + continue;
> + }
same suggestion/question here pluse there seems to be a bunch of repeated
code between linear and non-linear skb processing paths...
> while (data_len) {
> dma_unmap_page(oq->dev, oq->desc_ring[read_idx].buffer_ptr,
> PAGE_SIZE, DMA_FROM_DEVICE);
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH net] netfilter: nf_nat_masquerade: recalculate TCP TS offset when port is randomized
From: Florian Westphal @ 2026-07-01 14:17 UTC (permalink / raw)
To: xietangxin
Cc: Pablo Neira Ayuso, Phil Sutter, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, gaoxingwang, huyizhen,
netfilter-devel, coreteam, netdev, linux-kernel, stable
In-Reply-To: <0ad60f06-387e-49bc-9e26-3dcebf182cb4@h-partners.com>
xietangxin <xietangxin@h-partners.com> wrote:
> Shifting the helper down to nf_nat_l4proto_unique_tuple() as you suggested
> encounters a structural roadblock. we don't have access to the skb there.
> Adding skb to all intermediate callers (like nf_nat_setup_info, get_unique_tuple)
> would severely pollute the core NAT APIs.
Right, propagating the skb is too much code churn.
> would it be acceptable to place this logic in nf_nat_inet_fn() before do_nat?
>
> 963 do_nat:
> ..here
This is hit for every packet, not just the first one after
nf_nat_setup_info(). I suggest a slightly earlier spot in the
same function.
936 ret = e->hooks[i].hook(e->hooks[i].priv, skb,
937 state);
938 if (ret != NF_ACCEPT)
939 return ret;
940 if (nf_nat_initialized(ct, maniptype))
941 goto do_nat;
942 }
943 null_bind:
944 ret = nf_nat_alloc_null_binding(ct, state->hook);
945 if (ret != NF_ACCEPT)
946 return ret;
.... Here.
947 } else {
This spot runs only for new connections, right after a nf_nat_setup_info() call.
^ permalink raw reply
* Re: [PATCH net] netfilter: nf_nat_masquerade: recalculate TCP TS offset when port is randomized
From: xietangxin @ 2026-07-01 14:11 UTC (permalink / raw)
To: Jiayuan Chen, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: gaoxingwang, huyizhen, netfilter-devel, coreteam, netdev,
linux-kernel, stable
In-Reply-To: <1813a806-9250-492a-981d-07eb7f597f68@linux.dev>
On 7/1/2026 9:44 AM, Jiayuan Chen wrote:
>
> On 6/29/26 5:34 PM, xietangxin wrote:
>> Problem observed in Kubernetes environments where MASQUERADE target with
>> --random-fully is configured by default. after commit
>> 165573e41f2f ("tcp: secure_seq: add back ports to TS offset") TCP short
>> connection QPS dropped from ~20000 to ~10000. This added source and
>> destination ports into TS offset calculation.
>>
>> However, with MASQUERADE --random-fully, when multiple internal connections
>> (e.g sport 10000,20000) are mapped to the same external port (e.g 30000),
>> their TS offsets are calculated as ts_offset(10000) and ts_offset(20000).
>> If the server reuses the TIME_WAIT slot from the first connection, there is
>> a chance that ts_offset(20000) < ts_offset(10000), breaking TSval
>> monotonicity for the same 4-tuple and causing RST packets:
>> Client -> Server 24870 -> 80 [SYN] TSval=2294041168
>> Server -> Client 80 -> 24870 [ACK] TSecr=2846236456
>> Client -> Server 24870 -> 80 [RST] Seq=855605690
>>
>> After nf_nat_setup_info() successfully assigns a new randomized
>> source port, recalculate the TS offset using the new port and
>> update the SYN packet's TSval accordingly.
>>
>> Test results on 4U4G VM with
>> `./wrk -t8 -c200 -H "Connection: close" -d10s --latency http://5.5.5.5:80`
>> Before:
>> random:10712 req/s, random-fully:10986 req/s
>> After:
>> random:21463 req/s, random-fully:19181 req/s
>>
>> Fixes: 165573e41f2f ("tcp: secure_seq: add back ports to TS offset")
>> Cc: stable@vger.kernel.org
>
>
> I'd treat it as a feature not a fix.
I prefer it as a bugfix, because after commit
165573e41f2f ("tcp: secure_seq: add back ports to TS offset") TCP short
connection QPS dropped from ~20000 to ~10000 with MASQUERADE --random-fully,
>
>
>> Closes:https://lore.kernel.org/all/92935c00-e0be-4591-ac44-5978c7804d57@yeah.net/
>> Signed-off-by: xietangxin <xietangxin@h-partners.com>
>> ---
>> net/netfilter/nf_nat_masquerade.c | 91 ++++++++++++++++++++++++++++++-
>> 1 file changed, 89 insertions(+), 2 deletions(-)
>>
>> diff --git a/net/netfilter/nf_nat_masquerade.c b/net/netfilter/nf_nat_masquerade.c
>> index 4de6e0a51701..8c9ca5a051cc 100644
>> --- a/net/netfilter/nf_nat_masquerade.c
>> +++ b/net/netfilter/nf_nat_masquerade.c
>> @@ -6,8 +6,11 @@
>> #include <linux/netfilter.h>
>> #include <linux/netfilter_ipv4.h>
>> #include <linux/netfilter_ipv6.h>
>> +#include <linux/tcp.h>
>> +#include <net/tcp.h>
>> #include <net/netfilter/nf_nat_masquerade.h>
>> +#include <net/secure_seq.h>
>> struct masq_dev_work {
>> struct work_struct work;
>> @@ -24,6 +27,76 @@ static DEFINE_MUTEX(masq_mutex);
>> static unsigned int masq_refcnt __read_mostly;
>> static atomic_t masq_worker_count __read_mostly;
>> +static __be32 *tcp_ts_option_ptr(const struct sk_buff *skb)
>> +{
>> + const struct tcphdr *th;
>> + unsigned char *ptr;
>> + unsigned char opsize;
>> + unsigned int optlen, offset;
>> +
>> + th = tcp_hdr(skb);
>> + optlen = (th->doff - 5) * 4;
>> + ptr = (unsigned char *)(th + 1);
>> + offset = 0;
>> +
>> + while (offset < optlen) {
>> + unsigned char opcode = ptr[offset];
>> +
>> + if (opcode == TCPOPT_EOL)
>> + break;
>> + if (opcode == TCPOPT_NOP) {
>> + offset++;
>> + continue;
>> + }
>> +
>> + if (offset + 1 >= optlen)
>> + break;
>> +
>> + opsize = ptr[offset + 1];
>> + if (opsize < 2 || offset + opsize > optlen)
>> + break;
>> +
>> + if (opcode == TCPOPT_TIMESTAMP && opsize == TCPOLEN_TIMESTAMP)
>> + return (__be32 *)(ptr + offset + 2);
>> +
>> + offset += opsize;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +static void masquerade_update_tcp_ts_offset(struct nf_conn *ct, struct sk_buff *skb)
>> +{
>> + __be32 *tsptr;
>> + struct net *net;
>> + struct tcphdr *th;
>> + struct tcp_sock *tp;
>> + union tcp_seq_and_ts_off st;
>> + struct nf_conntrack_tuple *tuple;
>> +
>> + th = tcp_hdr(skb);
>> + net = nf_ct_net(ct);
>> + tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
>> +
>
> why use reply not original, or do I miss something ?
>
>
We use IP_CT_DIR_REPLY here because we need the post-NAT (translated)
4-tuple to correctly recalculate the new ts_offset
Best regards,
Tangxin Xie
^ permalink raw reply
* Re: [PATCH net] netfilter: nf_nat_masquerade: recalculate TCP TS offset when port is randomized
From: xietangxin @ 2026-07-01 14:09 UTC (permalink / raw)
To: Florian Westphal
Cc: Pablo Neira Ayuso, Phil Sutter, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, gaoxingwang, huyizhen,
netfilter-devel, coreteam, netdev, linux-kernel, stable
In-Reply-To: <akKN4cywAsFRdefX@strlen.de>
On 6/29/2026 11:23 PM, Florian Westphal wrote:
> xietangxin <xietangxin@h-partners.com> wrote:
>> Problem observed in Kubernetes environments where MASQUERADE target with
>> --random-fully is configured by default. after commit
>> 165573e41f2f ("tcp: secure_seq: add back ports to TS offset") TCP short
>> connection QPS dropped from ~20000 to ~10000. This added source and
>> destination ports into TS offset calculation.
>>
>> However, with MASQUERADE --random-fully, when multiple internal connections
>> (e.g sport 10000,20000) are mapped to the same external port (e.g 30000),
>> their TS offsets are calculated as ts_offset(10000) and ts_offset(20000).
>> If the server reuses the TIME_WAIT slot from the first connection, there is
>> a chance that ts_offset(20000) < ts_offset(10000), breaking TSval
>> monotonicity for the same 4-tuple and causing RST packets:
>> Client -> Server 24870 -> 80 [SYN] TSval=2294041168
>> Server -> Client 80 -> 24870 [ACK] TSecr=2846236456
>> Client -> Server 24870 -> 80 [RST] Seq=855605690
>>
>> After nf_nat_setup_info() successfully assigns a new randomized
>> source port, recalculate the TS offset using the new port and
>> update the SYN packet's TSval accordingly.
>
> I don't think this is related to masquerade but to snat (port address
> rewrite) in general.
>
> I think you could place your new helper in nf_nat_core.c and call it
> from nf_nat_l4proto_unique_tuple() once we've found a usable tuple:
>
> 668 another_round:
> 669 for (i = 0; i < attempts; i++, off++) {
> 670 *keyptr = htons(min + off % range_size);
> 671 if (!nf_nat_used_tuple_harder(tuple, ct, attempts - i))
>
> ... here.
> 672 return;
> 673 }
>
Hi Florian,
Thank you for the insightful feedback. You are absolutely right that
this issue is releated to SNAT with port rewrite, rather masquerade.
Shifting the helper down to nf_nat_l4proto_unique_tuple() as you suggested
encounters a structural roadblock. we don't have access to the skb there.
Adding skb to all intermediate callers (like nf_nat_setup_info, get_unique_tuple)
would severely pollute the core NAT APIs.
would it be acceptable to place this logic in nf_nat_inet_fn() before do_nat?
963 do_nat:
..here
964 return nf_nat_packet(ct, ctinfo, state->hook, skb);
965
966 oif_changed:
967 nf_ct_kill_acct(ct, ctinfo, skb);
968 return NF_DROP;
969 }
Best regards,
Tangxin Xie
^ permalink raw reply
* Re: [PATCH net-next V4 4/6] devlink: Apply eswitch mode boot defaults
From: Jiri Pirko @ 2026-07-01 14:09 UTC (permalink / raw)
To: Mark Bloch
Cc: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc
In-Reply-To: <1d4ca929-82b8-4891-9058-1451bf71a660@nvidia.com>
Wed, Jul 01, 2026 at 02:57:21PM +0200, mbloch@nvidia.com wrote:
>
>
>On 01/07/2026 12:48, Jiri Pirko wrote:
>> Mon, Jun 29, 2026 at 08:20:59PM +0200, mbloch@nvidia.com wrote:
>>> Apply parsed devlink_eswitch_mode= defaults after devlink registration
>>> and after successful reload.
>>>
>>> devl_register() may still be called before the device is ready for an
>>
>> How so? I would assume that driver calls devl_register only after
>> everything is up and running and ready. If not, isn't it a bug?
>>
>
>You would think so :)
>
>Some drivers, mlx5 included, call devl_register() while holding the
>devlink instance lock and then finish setting up state before releasing
>the lock.
>
>In v3 I tried to enforce exactly that model, move devl_register() to
>be the last thing the driver does. Jakub pushed back on making that a
>general rule. So in v4 I changed the approach. devl_register() only
>schedules the work, and the actual eswitch mode change can run only
>after the driver releases the devlink lock.
Wouldn't it make sense to use a completion instead of loop-reschedule of
delayed work?
>
>Mark
>
>>
>>> eswitch mode change, so keep a per-devlink delayed work item and pending
>>> flag for the registration path. Registration queues the work, and the
>>> worker tries to take the devlink instance lock.
>>>
>>> If the lock is busy, the worker requeues itself with a delay.
>>>
>>> For successful reloads that performed DRIVER_REINIT, devlink_reload()
>>> already holds the devlink instance lock and the driver has completed
>>> reload_up(). Clear pending work and apply the default directly from the
>>> reload path instead of queueing work.
>>>
>>> If a user sets eswitch mode through netlink before the pending
>>> registration work runs, clear the pending flag so the queued default does
>>> not override that user request. Cancel pending default apply work when
>>> freeing the devlink instance.
>>
>> These AI generated code descriptive messages are generally not very
>> useful :(
>>
>
^ permalink raw reply
* Re: [PATCH v2 4/7] mlxsw: don't keep pci_device_id
From: Petr Machata @ 2026-07-01 13:57 UTC (permalink / raw)
To: Gary Guo
Cc: Bjorn Helgaas, Zhenzhong Duan, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Damien Le Moal,
Niklas Cassel, GOTO Masanori, YOKOTA Hiroshi,
James E.J. Bottomley, Martin K. Petersen, Vaibhav Gupta,
Jens Taprogge, Ido Schimmel, Petr Machata, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-pci, driver-core, linux-kernel, linux-ide, linux-scsi,
industrypack-devel, netdev
In-Reply-To: <20260630-pci_id_fix-v2-4-b834a98c0af2@garyguo.net>
Gary Guo <gary@garyguo.net> writes:
> pci_device_id is not guaranteed to live longer than probe due to presence
> of dynamic ID. This stored ID is unused so remove it.
>
> Signed-off-by: Gary Guo <gary@garyguo.net>
> ---
> drivers/net/ethernet/mellanox/mlxsw/pci.c | 11 ++++-------
> 1 file changed, 4 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c
> index 0da85d36647d..bfe3268dfdc1 100644
> --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c
> +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c
> @@ -1768,7 +1767,6 @@ static void mlxsw_pci_mbox_free(struct mlxsw_pci *mlxsw_pci,
> }
>
> static int mlxsw_pci_sys_ready_wait(struct mlxsw_pci *mlxsw_pci,
> - const struct pci_device_id *id,
> u32 *p_sys_status)
> {
> unsigned long end;
I see, we used this to detect whether we are on SwitchX-2. Support far
that was dropped ages ago in commit b0d80c013b04 ("mlxsw: Remove
Mellanox SwitchX-2 ASIC support").
Good cleanup, thanks.
Reviewed-by: Petr Machata <petrm@nvidia.com>
^ permalink raw reply
* [PATCH 4/4] wlcore: allocate aggregation and firmware log buffers with kzalloc()
From: Mike Rapoport (Microsoft) @ 2026-07-01 13:59 UTC (permalink / raw)
To: Johannes Berg
Cc: Brian Norris, Francesco Dolcini, Jakub Kicinski, Mike Rapoport,
b43-dev, libertas-dev, linux-kernel, linux-mm, linux-wireless,
netdev
In-Reply-To: <20260701-b4-drivers-wireless-v1-0-60264cdf2efe@kernel.org>
wlcore_alloc_hw() uses __get_free_pages() to allocate TX aggregation
and firmware log buffers used for software data staging.
These buffer can be allocated with kmalloc() as there's nothing special
about them to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of __get_free_pages() with kzalloc() and free_pages() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/net/wireless/ti/wlcore/main.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index be583ae331c0..5595f7a1fc0c 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -6354,7 +6354,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
struct ieee80211_hw *hw;
struct wl1271 *wl;
int i, j, ret;
- unsigned int order;
hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
if (!hw) {
@@ -6434,8 +6433,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
mutex_init(&wl->flush_mutex);
init_completion(&wl->nvs_loading_complete);
- order = get_order(aggr_buf_size);
- wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
+ wl->aggr_buf = kmalloc(round_up(aggr_buf_size, PAGE_SIZE), GFP_KERNEL);
if (!wl->aggr_buf) {
ret = -ENOMEM;
goto err_wq;
@@ -6449,7 +6447,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
}
/* Allocate one page for the FW log */
- wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL);
+ wl->fwlog = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!wl->fwlog) {
ret = -ENOMEM;
goto err_dummy_packet;
@@ -6474,13 +6472,13 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
kfree(wl->mbox);
err_fwlog:
- free_page((unsigned long)wl->fwlog);
+ kfree(wl->fwlog);
err_dummy_packet:
dev_kfree_skb(wl->dummy_packet);
err_aggr:
- free_pages((unsigned long)wl->aggr_buf, order);
+ kfree(wl->aggr_buf);
err_wq:
destroy_workqueue(wl->freezable_wq);
@@ -6509,9 +6507,9 @@ int wlcore_free_hw(struct wl1271 *wl)
kfree(wl->buffer_32);
kfree(wl->mbox);
- free_page((unsigned long)wl->fwlog);
+ kfree(wl->fwlog);
dev_kfree_skb(wl->dummy_packet);
- free_pages((unsigned long)wl->aggr_buf, get_order(wl->aggr_buf_size));
+ kfree(wl->aggr_buf);
wl1271_debugfs_exit(wl);
--
2.53.0
^ permalink raw reply related
* [PATCH 3/4] mwifiex: debugfs: use kzalloc() to allocate formatting buffers
From: Mike Rapoport (Microsoft) @ 2026-07-01 13:59 UTC (permalink / raw)
To: Johannes Berg
Cc: Brian Norris, Francesco Dolcini, Jakub Kicinski, Mike Rapoport,
b43-dev, libertas-dev, linux-kernel, linux-mm, linux-wireless,
netdev
In-Reply-To: <20260701-b4-drivers-wireless-v1-0-60264cdf2efe@kernel.org>
mwifiex debugfs functions allocate buffers for formatting debug output
text using get_zeroed_page().
These buffers can be allocated with kmalloc() as there's nothing special
about them to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of get_zeroed_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/net/wireless/marvell/mwifiex/debugfs.c | 62 +++++++++++---------------
1 file changed, 27 insertions(+), 35 deletions(-)
diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c
index 9deaf59dcb62..573768b6ad91 100644
--- a/drivers/net/wireless/marvell/mwifiex/debugfs.c
+++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c
@@ -6,6 +6,7 @@
*/
#include <linux/debugfs.h>
+#include <linux/slab.h>
#include "main.h"
#include "11n.h"
@@ -67,8 +68,8 @@ mwifiex_info_read(struct file *file, char __user *ubuf,
struct net_device *netdev = priv->netdev;
struct netdev_hw_addr *ha;
struct netdev_queue *txq;
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- char *p = (char *) page, fmt[64];
+ char *page = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ char *p = page, fmt[64];
struct mwifiex_bss_info info;
ssize_t ret;
int i = 0;
@@ -133,11 +134,10 @@ mwifiex_info_read(struct file *file, char __user *ubuf,
}
p += sprintf(p, "\n");
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
- (unsigned long) p - page);
+ ret = simple_read_from_buffer(ubuf, count, ppos, page, p - page);
free_and_exit:
- free_page(page);
+ kfree(page);
return ret;
}
@@ -168,8 +168,8 @@ mwifiex_getlog_read(struct file *file, char __user *ubuf,
{
struct mwifiex_private *priv =
(struct mwifiex_private *) file->private_data;
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- char *p = (char *) page;
+ char *page = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ char *p = page;
ssize_t ret;
struct mwifiex_ds_get_stats stats;
@@ -220,11 +220,10 @@ mwifiex_getlog_read(struct file *file, char __user *ubuf,
stats.bcn_miss_cnt);
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
- (unsigned long) p - page);
+ ret = simple_read_from_buffer(ubuf, count, ppos, page, p - page);
free_and_exit:
- free_page(page);
+ kfree(page);
return ret;
}
@@ -247,8 +246,8 @@ mwifiex_histogram_read(struct file *file, char __user *ubuf,
ssize_t ret;
struct mwifiex_histogram_data *phist_data;
int i, value;
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- char *p = (char *)page;
+ char *page = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ char *p = page;
if (!p)
return -ENOMEM;
@@ -309,11 +308,10 @@ mwifiex_histogram_read(struct file *file, char __user *ubuf,
i, value);
}
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
- (unsigned long)p - page);
+ ret = simple_read_from_buffer(ubuf, count, ppos, page, p - page);
free_and_exit:
- free_page(page);
+ kfree(page);
return ret;
}
@@ -383,8 +381,8 @@ mwifiex_debug_read(struct file *file, char __user *ubuf,
{
struct mwifiex_private *priv =
(struct mwifiex_private *) file->private_data;
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- char *p = (char *) page;
+ char *page = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ char *p = page;
ssize_t ret;
if (!p)
@@ -396,11 +394,10 @@ mwifiex_debug_read(struct file *file, char __user *ubuf,
p += mwifiex_debug_info_to_buffer(priv, p, &info);
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
- (unsigned long) p - page);
+ ret = simple_read_from_buffer(ubuf, count, ppos, page, p - page);
free_and_exit:
- free_page(page);
+ kfree(page);
return ret;
}
@@ -457,8 +454,7 @@ mwifiex_regrdwr_read(struct file *file, char __user *ubuf,
{
struct mwifiex_private *priv =
(struct mwifiex_private *) file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *) addr;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
int pos = 0, ret = 0;
u32 reg_value;
@@ -497,7 +493,7 @@ mwifiex_regrdwr_read(struct file *file, char __user *ubuf,
ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
done:
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -511,8 +507,7 @@ mwifiex_debug_mask_read(struct file *file, char __user *ubuf,
{
struct mwifiex_private *priv =
(struct mwifiex_private *)file->private_data;
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)page;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
size_t ret = 0;
int pos = 0;
@@ -523,7 +518,7 @@ mwifiex_debug_mask_read(struct file *file, char __user *ubuf,
priv->adapter->debug_mask);
ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
- free_page(page);
+ kfree(buf);
return ret;
}
@@ -652,8 +647,7 @@ mwifiex_memrw_read(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
struct mwifiex_private *priv = (void *)file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
int ret, pos = 0;
if (!buf)
@@ -663,7 +657,7 @@ mwifiex_memrw_read(struct file *file, char __user *ubuf,
priv->mem_rw.value);
ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -719,8 +713,7 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
{
struct mwifiex_private *priv =
(struct mwifiex_private *) file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *) addr;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
int pos, ret, i;
u8 value[MAX_EEPROM_DATA];
@@ -749,7 +742,7 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
done:
ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
out_free:
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -820,8 +813,7 @@ mwifiex_hscfg_read(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
struct mwifiex_private *priv = (void *)file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
int pos, ret;
struct mwifiex_ds_hs_cfg hscfg;
@@ -836,7 +828,7 @@ mwifiex_hscfg_read(struct file *file, char __user *ubuf,
ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
- free_page(addr);
+ kfree(buf);
return ret;
}
--
2.53.0
^ permalink raw reply related
* [PATCH 2/4] libertas: debugfs: use kzalloc() to allocate formatting buffers
From: Mike Rapoport (Microsoft) @ 2026-07-01 13:59 UTC (permalink / raw)
To: Johannes Berg
Cc: Brian Norris, Francesco Dolcini, Jakub Kicinski, Mike Rapoport,
b43-dev, libertas-dev, linux-kernel, linux-mm, linux-wireless,
netdev
In-Reply-To: <20260701-b4-drivers-wireless-v1-0-60264cdf2efe@kernel.org>
libertas debugfs functions allocate buffers for formatting debug
output text using get_zeroed_page().
These buffers can be allocated with kmalloc() as there's nothing special
about them to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of get_zeroed_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/net/wireless/marvell/libertas/debugfs.c | 39 ++++++++++---------------
1 file changed, 16 insertions(+), 23 deletions(-)
diff --git a/drivers/net/wireless/marvell/libertas/debugfs.c b/drivers/net/wireless/marvell/libertas/debugfs.c
index 9ebd69134940..9428f954837a 100644
--- a/drivers/net/wireless/marvell/libertas/debugfs.c
+++ b/drivers/net/wireless/marvell/libertas/debugfs.c
@@ -35,8 +35,7 @@ static ssize_t lbs_dev_info(struct file *file, char __user *userbuf,
{
struct lbs_private *priv = file->private_data;
size_t pos = 0;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
ssize_t res;
if (!buf)
return -ENOMEM;
@@ -48,7 +47,7 @@ static ssize_t lbs_dev_info(struct file *file, char __user *userbuf,
res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
- free_page(addr);
+ kfree(buf);
return res;
}
@@ -96,8 +95,7 @@ static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
ssize_t ret;
size_t pos = 0;
struct sleep_params sp;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -113,7 +111,7 @@ static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
out_unlock:
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -165,8 +163,7 @@ static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf,
struct lbs_private *priv = file->private_data;
ssize_t ret;
size_t pos = 0;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -174,7 +171,7 @@ static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf,
ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -228,7 +225,7 @@ static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
u8 freq;
int events = 0;
- buf = (char *)get_zeroed_page(GFP_KERNEL);
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -261,7 +258,7 @@ static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
kfree(subscribed);
out_page:
- free_page((unsigned long)buf);
+ kfree(buf);
return ret;
}
@@ -436,8 +433,7 @@ static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
struct lbs_private *priv = file->private_data;
ssize_t pos = 0;
int ret;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
u32 val = 0;
if (!buf)
@@ -450,7 +446,7 @@ static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
priv->mac_offset, val);
ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
}
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -506,8 +502,7 @@ static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
struct lbs_private *priv = file->private_data;
ssize_t pos = 0;
int ret;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
u32 val;
if (!buf)
@@ -520,7 +515,7 @@ static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
priv->bbp_offset, val);
ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
}
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -578,8 +573,7 @@ static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
struct lbs_private *priv = file->private_data;
ssize_t pos = 0;
int ret;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
u32 val;
if (!buf)
@@ -592,7 +586,7 @@ static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
priv->rf_offset, val);
ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
}
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -812,8 +806,7 @@ static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf,
char *p;
int i;
struct debug_data *d;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
+ char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -836,7 +829,7 @@ static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf,
res = simple_read_from_buffer(userbuf, count, ppos, p, pos);
- free_page(addr);
+ kfree(buf);
return res;
}
--
2.53.0
^ permalink raw reply related
* [PATCH 1/4] b43, b43legacy: debugfs: use kzalloc() to allocate formatting buffers
From: Mike Rapoport (Microsoft) @ 2026-07-01 13:59 UTC (permalink / raw)
To: Johannes Berg
Cc: Brian Norris, Francesco Dolcini, Jakub Kicinski, Mike Rapoport,
b43-dev, libertas-dev, linux-kernel, linux-mm, linux-wireless,
netdev
In-Reply-To: <20260701-b4-drivers-wireless-v1-0-60264cdf2efe@kernel.org>
b43* debugfs functions allocate 16 KiB buffers for formatting debug output
text using __get_free_pages().
kzalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object and for 16 Kib
allocation kzalloc() will anyway delegate it to buddy.
Replace use of __get_free_pages() with kzalloc().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/net/wireless/broadcom/b43/debugfs.c | 12 +++++-------
drivers/net/wireless/broadcom/b43legacy/debugfs.c | 12 +++++-------
2 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/drivers/net/wireless/broadcom/b43/debugfs.c b/drivers/net/wireless/broadcom/b43/debugfs.c
index acddae68947a..31a1ff00c1a4 100644
--- a/drivers/net/wireless/broadcom/b43/debugfs.c
+++ b/drivers/net/wireless/broadcom/b43/debugfs.c
@@ -495,7 +495,6 @@ static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
ssize_t ret;
char *buf;
const size_t bufsize = 1024 * 16; /* 16 kiB buffer */
- const size_t buforder = get_order(bufsize);
int err = 0;
if (!count)
@@ -518,15 +517,14 @@ static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
dfile = fops_to_dfs_file(dev, dfops);
if (!dfile->buffer) {
- buf = (char *)__get_free_pages(GFP_KERNEL, buforder);
+ buf = kzalloc(bufsize, GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto out_unlock;
}
- memset(buf, 0, bufsize);
ret = dfops->read(dev, buf, bufsize);
if (ret <= 0) {
- free_pages((unsigned long)buf, buforder);
+ kfree(buf);
err = ret;
goto out_unlock;
}
@@ -538,7 +536,7 @@ static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
dfile->buffer,
dfile->data_len);
if (*ppos >= dfile->data_len) {
- free_pages((unsigned long)dfile->buffer, buforder);
+ kfree(dfile->buffer);
dfile->buffer = NULL;
dfile->data_len = 0;
}
@@ -577,7 +575,7 @@ static ssize_t b43_debugfs_write(struct file *file,
goto out_unlock;
}
- buf = (char *)get_zeroed_page(GFP_KERNEL);
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto out_unlock;
@@ -591,7 +589,7 @@ static ssize_t b43_debugfs_write(struct file *file,
goto out_freepage;
out_freepage:
- free_page((unsigned long)buf);
+ kfree(buf);
out_unlock:
mutex_unlock(&dev->wl->mutex);
diff --git a/drivers/net/wireless/broadcom/b43legacy/debugfs.c b/drivers/net/wireless/broadcom/b43legacy/debugfs.c
index 3ad99124d522..a04d90d7307c 100644
--- a/drivers/net/wireless/broadcom/b43legacy/debugfs.c
+++ b/drivers/net/wireless/broadcom/b43legacy/debugfs.c
@@ -192,7 +192,6 @@ static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,
ssize_t ret;
char *buf;
const size_t bufsize = 1024 * 16; /* 16 KiB buffer */
- const size_t buforder = get_order(bufsize);
int err = 0;
if (!count)
@@ -215,12 +214,11 @@ static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,
dfile = fops_to_dfs_file(dev, dfops);
if (!dfile->buffer) {
- buf = (char *)__get_free_pages(GFP_KERNEL, buforder);
+ buf = kzalloc(bufsize, GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto out_unlock;
}
- memset(buf, 0, bufsize);
if (dfops->take_irqlock) {
spin_lock_irq(&dev->wl->irq_lock);
ret = dfops->read(dev, buf, bufsize);
@@ -228,7 +226,7 @@ static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,
} else
ret = dfops->read(dev, buf, bufsize);
if (ret <= 0) {
- free_pages((unsigned long)buf, buforder);
+ kfree(buf);
err = ret;
goto out_unlock;
}
@@ -240,7 +238,7 @@ static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,
dfile->buffer,
dfile->data_len);
if (*ppos >= dfile->data_len) {
- free_pages((unsigned long)dfile->buffer, buforder);
+ kfree(dfile->buffer);
dfile->buffer = NULL;
dfile->data_len = 0;
}
@@ -279,7 +277,7 @@ static ssize_t b43legacy_debugfs_write(struct file *file,
goto out_unlock;
}
- buf = (char *)get_zeroed_page(GFP_KERNEL);
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto out_unlock;
@@ -298,7 +296,7 @@ static ssize_t b43legacy_debugfs_write(struct file *file,
goto out_freepage;
out_freepage:
- free_page((unsigned long)buf);
+ kfree(buf);
out_unlock:
mutex_unlock(&dev->wl->mutex);
--
2.53.0
^ permalink raw reply related
* [PATCH 0/4] drivers/net: replace __get_free_pages() with kmalloc()
From: Mike Rapoport (Microsoft) @ 2026-07-01 13:59 UTC (permalink / raw)
To: Johannes Berg
Cc: Brian Norris, Francesco Dolcini, Jakub Kicinski, Mike Rapoport,
b43-dev, libertas-dev, linux-kernel, linux-mm, linux-wireless,
netdev
This is a (small) part of larger work of replacing page allocator calls
with kmalloc.
My initial intention a few month ago was to remove ugly casts [1], but then
willy pointed out that Linus objected to something like this [2] and it
looks like more than a decade old technical debt.
Largely, anything that doesn't need struct page (or a memdesc in the
future) should just use kmalloc() or kvmalloc() to allocate memory.
kmalloc() guarantees alignment, physical contiguity and working
virt_to_phys() and beside nicer API that returns void * on alloc and
doesn't require to know the allocation size on free, kmalloc() provides
better debugging capabilities than page allocator.
Another thing is that touching these allocation sites gives the reviewers
opportunity to see if a PAGE_SIZE buffer is actually needed or maybe
another size is appropriate.
For larger allocations that don't need physically contiguous memory
kvmalloc() can be a better option that __get_free_pages() because under
memory pressure it's is easier to allocate several order-0 pages than a
physically contiguous chunk with the same number of pages.
And last, but not least, removing needless calls to page allocator should
help with memdesc (aka project folio) conversion. There will be way less
places to audit to see if the user was actually using struct page.
Also in git:
https://git.kernel.org/pub/scm/linux/kernel/git/rppt/linux.git gfp-to-kmalloc/drivers-net-wireless
[1] https://lore.kernel.org/all/20251018093002.3660549-1-rppt@kernel.org/
[2] https://lore.kernel.org/all/CA+55aFwp4iy4rtX2gE2WjBGFL=NxMVnoFeHqYa2j1dYOMMGqxg@mail.gmail.com/
---
Changes in v2:
- split out wireless drivers from a larger set
- use kzalloc() instead of kmalloc() + memset in b43legacy
v1: https://patch.msgid.link/20260630-b4-drivers-net-v1-0-672162a91f37@kernel.org
---
Mike Rapoport (Microsoft) (4):
b43, b43legacy: debugfs: use kzalloc() to allocate formatting buffers
libertas: debugfs: use kzalloc() to allocate formatting buffers
mwifiex: debugfs: use kzalloc() to allocate formatting buffers
wlcore: allocate aggregation and firmware log buffers with kzalloc()
drivers/net/wireless/broadcom/b43/debugfs.c | 12 ++---
drivers/net/wireless/broadcom/b43legacy/debugfs.c | 12 ++---
drivers/net/wireless/marvell/libertas/debugfs.c | 39 ++++++--------
drivers/net/wireless/marvell/mwifiex/debugfs.c | 62 ++++++++++-------------
drivers/net/wireless/ti/wlcore/main.c | 14 +++--
5 files changed, 59 insertions(+), 80 deletions(-)
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260630-b4-drivers-wireless-5294524fab46
Best regards,
--
Sincerely yours,
Mike.
^ permalink raw reply
* [PATCH 4/4] sfc: use kmalloc() to allocate logging buffer
From: Mike Rapoport (Microsoft) @ 2026-07-01 13:57 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Manish Chopra, Paolo Abeni
Cc: Edward Cree, Przemek Kitszel, Sudarsana Kalluru, Tony Nguyen,
Mike Rapoport, intel-wired-lan, linux-kernel, linux-mm,
linux-net-drivers, netdev
In-Reply-To: <20260701-b4-drivers-ethernet-v1-0-58776615db6e@kernel.org>
efx_mcdi_init() allocates a logging buffer for MCDI firmware
communication diagnostics.
This buffer can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of __get_free_page() with kmalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/net/ethernet/sfc/mcdi.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c
index e65db9b70724..b806d3d90c42 100644
--- a/drivers/net/ethernet/sfc/mcdi.c
+++ b/drivers/net/ethernet/sfc/mcdi.c
@@ -7,6 +7,7 @@
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/atomic.h>
+#include <linux/slab.h>
#include "net_driver.h"
#include "nic.h"
#include "io.h"
@@ -71,7 +72,7 @@ int efx_mcdi_init(struct efx_nic *efx)
mcdi->efx = efx;
#ifdef CONFIG_SFC_MCDI_LOGGING
/* consuming code assumes buffer is page-sized */
- mcdi->logging_buffer = (char *)__get_free_page(GFP_KERNEL);
+ mcdi->logging_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!mcdi->logging_buffer)
goto fail1;
mcdi->logging_enabled = mcdi_logging_default;
@@ -112,7 +113,7 @@ int efx_mcdi_init(struct efx_nic *efx)
return 0;
fail2:
#ifdef CONFIG_SFC_MCDI_LOGGING
- free_page((unsigned long)mcdi->logging_buffer);
+ kfree(mcdi->logging_buffer);
fail1:
#endif
kfree(efx->mcdi);
@@ -138,7 +139,7 @@ void efx_mcdi_fini(struct efx_nic *efx)
return;
#ifdef CONFIG_SFC_MCDI_LOGGING
- free_page((unsigned long)efx->mcdi->iface.logging_buffer);
+ kfree(efx->mcdi->iface.logging_buffer);
#endif
kfree(efx->mcdi);
--
2.53.0
^ permalink raw reply related
* [PATCH 3/4] sfc/siena: use kmalloc() to allocate logging buffer
From: Mike Rapoport (Microsoft) @ 2026-07-01 13:57 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Manish Chopra, Paolo Abeni
Cc: Edward Cree, Przemek Kitszel, Sudarsana Kalluru, Tony Nguyen,
Mike Rapoport, intel-wired-lan, linux-kernel, linux-mm,
linux-net-drivers, netdev
In-Reply-To: <20260701-b4-drivers-ethernet-v1-0-58776615db6e@kernel.org>
efx_siena_mcdi_init() allocates a logging buffer for MCDI firmware
communication diagnostics.
This buffer can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of __get_free_page() with kmalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/net/ethernet/sfc/siena/mcdi.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/sfc/siena/mcdi.c b/drivers/net/ethernet/sfc/siena/mcdi.c
index 4d0d6bd5d3d1..048c1e6017c0 100644
--- a/drivers/net/ethernet/sfc/siena/mcdi.c
+++ b/drivers/net/ethernet/sfc/siena/mcdi.c
@@ -7,6 +7,7 @@
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/atomic.h>
+#include <linux/slab.h>
#include "net_driver.h"
#include "nic.h"
#include "io.h"
@@ -73,7 +74,7 @@ int efx_siena_mcdi_init(struct efx_nic *efx)
mcdi->efx = efx;
#ifdef CONFIG_SFC_SIENA_MCDI_LOGGING
/* consuming code assumes buffer is page-sized */
- mcdi->logging_buffer = (char *)__get_free_page(GFP_KERNEL);
+ mcdi->logging_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!mcdi->logging_buffer)
goto fail1;
mcdi->logging_enabled = efx_siena_mcdi_logging_default;
@@ -116,7 +117,7 @@ int efx_siena_mcdi_init(struct efx_nic *efx)
return 0;
fail2:
#ifdef CONFIG_SFC_SIENA_MCDI_LOGGING
- free_page((unsigned long)mcdi->logging_buffer);
+ kfree(mcdi->logging_buffer);
fail1:
#endif
kfree(efx->mcdi);
@@ -142,7 +143,7 @@ void efx_siena_mcdi_fini(struct efx_nic *efx)
return;
#ifdef CONFIG_SFC_SIENA_MCDI_LOGGING
- free_page((unsigned long)efx->mcdi->iface.logging_buffer);
+ kfree(efx->mcdi->iface.logging_buffer);
#endif
kfree(efx->mcdi);
--
2.53.0
^ permalink raw reply related
* [PATCH 2/4] ice: use kzalloc() to allocate staging buffer for reading from GNSS
From: Mike Rapoport (Microsoft) @ 2026-07-01 13:57 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Manish Chopra, Paolo Abeni
Cc: Edward Cree, Przemek Kitszel, Sudarsana Kalluru, Tony Nguyen,
Mike Rapoport, intel-wired-lan, linux-kernel, linux-mm,
linux-net-drivers, netdev
In-Reply-To: <20260701-b4-drivers-ethernet-v1-0-58776615db6e@kernel.org>
ice_gnss_read() uses get_zeroed_page() to allocate a staging buffer for
reading GNSS module data via I2C bus.
This buffer can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of get_zeroed_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/net/ethernet/intel/ice/ice_gnss.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_gnss.c b/drivers/net/ethernet/intel/ice/ice_gnss.c
index 8fd954f1ebd6..7d21c3417b0b 100644
--- a/drivers/net/ethernet/intel/ice/ice_gnss.c
+++ b/drivers/net/ethernet/intel/ice/ice_gnss.c
@@ -2,6 +2,7 @@
/* Copyright (C) 2021-2022, Intel Corporation. */
#include "ice.h"
+#include <linux/slab.h>
#include "ice_lib.h"
/**
@@ -124,7 +125,7 @@ static void ice_gnss_read(struct kthread_work *work)
data_len = min_t(typeof(data_len), data_len, PAGE_SIZE);
- buf = (char *)get_zeroed_page(GFP_KERNEL);
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto requeue;
@@ -151,7 +152,7 @@ static void ice_gnss_read(struct kthread_work *work)
count, i);
delay = ICE_GNSS_TIMER_DELAY_TIME;
free_buf:
- free_page((unsigned long)buf);
+ kfree(buf);
requeue:
kthread_queue_delayed_work(gnss->kworker, &gnss->read_work, delay);
if (err)
--
2.53.0
^ permalink raw reply related
* [PATCH 1/4] bnx2x: use kzalloc() to allocate mac filtering list
From: Mike Rapoport (Microsoft) @ 2026-07-01 13:57 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Manish Chopra, Paolo Abeni
Cc: Edward Cree, Przemek Kitszel, Sudarsana Kalluru, Tony Nguyen,
Mike Rapoport, intel-wired-lan, linux-kernel, linux-mm,
linux-net-drivers, netdev
In-Reply-To: <20260701-b4-drivers-ethernet-v1-0-58776615db6e@kernel.org>
bnx2x_mcast_enqueue_cmd() allocates memory for mac filtering list using
__get_free_pages().
This memory can be allocated with kzalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of __get_free_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
index 07a908a2c72f..d560524d317d 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
@@ -26,6 +26,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/crc32c.h>
+#include <linux/slab.h>
#include "bnx2x.h"
#include "bnx2x_cmn.h"
#include "bnx2x_sp.h"
@@ -2664,7 +2665,7 @@ static void bnx2x_free_groups(struct list_head *mcast_group_list)
struct bnx2x_mcast_elem_group,
mcast_group_link);
list_del(¤t_mcast_group->mcast_group_link);
- free_page((unsigned long)current_mcast_group);
+ kfree(current_mcast_group);
}
}
@@ -2713,8 +2714,7 @@ static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp,
total_elems = BNX2X_MCAST_BINS_NUM;
}
while (total_elems > 0) {
- elem_group = (struct bnx2x_mcast_elem_group *)
- __get_free_page(GFP_ATOMIC | __GFP_ZERO);
+ elem_group = kzalloc(PAGE_SIZE, GFP_ATOMIC);
if (!elem_group) {
bnx2x_free_groups(&new_cmd->group_head);
kfree(new_cmd);
--
2.53.0
^ permalink raw reply related
* [PATCH 0/4] drivers/net/ethernet: replace __get_free_pages() with kmalloc()
From: Mike Rapoport (Microsoft) @ 2026-07-01 13:57 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Manish Chopra, Paolo Abeni
Cc: Edward Cree, Przemek Kitszel, Sudarsana Kalluru, Tony Nguyen,
Mike Rapoport, intel-wired-lan, linux-kernel, linux-mm,
linux-net-drivers, netdev
This is a (small) part of larger work of replacing page allocator calls
with kmalloc.
My initial intention a few month ago was to remove ugly casts [1], but then
willy pointed out that Linus objected to something like this [2] and it
looks like more than a decade old technical debt.
Largely, anything that doesn't need struct page (or a memdesc in the
future) should just use kmalloc() or kvmalloc() to allocate memory.
kmalloc() guarantees alignment, physical contiguity and working
virt_to_phys() and beside nicer API that returns void * on alloc and
doesn't require to know the allocation size on free, kmalloc() provides
better debugging capabilities than page allocator.
Another thing is that touching these allocation sites gives the reviewers
opportunity to see if a PAGE_SIZE buffer is actually needed or maybe
another size is appropriate.
For larger allocations that don't need physically contiguous memory
kvmalloc() can be a better option that __get_free_pages() because under
memory pressure it's is easier to allocate several order-0 pages than a
physically contiguous chunk with the same number of pages.
And last, but not least, removing needless calls to page allocator should
help with memdesc (aka project folio) conversion. There will be way less
places to audit to see if the user was actually using struct page.
Also in git:
https://git.kernel.org/pub/scm/linux/kernel/git/rppt/linux.git gfp-to-kmalloc/drivers-net-ethernet
[1] https://lore.kernel.org/all/20251018093002.3660549-1-rppt@kernel.org/
[2] https://lore.kernel.org/all/CA+55aFwp4iy4rtX2gE2WjBGFL=NxMVnoFeHqYa2j1dYOMMGqxg@mail.gmail.com/
---
v2 changes:
- split out ethernet drivers from a larger set
v1: https://patch.msgid.link/20260630-b4-drivers-net-v1-0-672162a91f37@kernel.org
---
Mike Rapoport (Microsoft) (4):
bnx2x: use kzalloc() to allocate mac filtering list
ice: use kzalloc() to allocate staging buffer for reading from GNSS
sfc/siena: use kmalloc() to allocate logging buffer
sfc: use kmalloc() to allocate logging buffer
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c | 6 +++---
drivers/net/ethernet/intel/ice/ice_gnss.c | 5 +++--
drivers/net/ethernet/sfc/mcdi.c | 7 ++++---
drivers/net/ethernet/sfc/siena/mcdi.c | 7 ++++---
4 files changed, 14 insertions(+), 11 deletions(-)
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260630-b4-drivers-ethernet-b5e085b98ab1
Best regards,
--
Sincerely yours,
Mike.
^ permalink raw reply
* Re: [PATCH v1 net-next 00/10] net: fib_rules: RTNL-less RTM_NEWRULE and RTM_DELRULE.
From: Ido Schimmel @ 2026-07-01 13:38 UTC (permalink / raw)
To: Kuniyuki Iwashima
Cc: David Ahern, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Kuniyuki Iwashima, netdev
In-Reply-To: <20260629181226.1929658-1-kuniyu@google.com>
On Mon, Jun 29, 2026 at 06:10:52PM +0000, Kuniyuki Iwashima wrote:
> RTM_NEWRULE and RTM_DELRULE acquire rtnl_net_lock(), but this is
> only for fib_unmerge() in IPv4.
>
> Since commit d954a67a7dfa ("ipv4: fib_rule: Move fib4_rules_exit()
> to ->exit()."), RTM_DELRULE no longer needs RTNL.
>
> fib_unmerge() is one-time event for each netns, so we only need
> RTNL for the first IPv4 rule.
>
> This series introduces per-fib_rules_ops mutex and drops RTNL
> from fib_rules code except for the first IPv4 RTM_NEWRULE.
LGTM, thanks:
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
A few nits that can be addressed in a follow-up:
1. Patch #3:
The comment at the top of netns_ipv4 suggests that we should document
the new lock in
Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst
Related: Did you consider moving this lock under
CONFIG_IP_MULTIPLE_TABLES?
2. Patch #5:
Sashiko suggests a mutex_destroy() in fib_rules_unregister():
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260629181226.1929658-1-kuniyu%40google.com?part=5
^ permalink raw reply
* [PATCH iwl-net v3 2/2] ice: preserve uplink DFLT Rx rule on switchdev release
From: Petr Oros @ 2026-07-01 13:36 UTC (permalink / raw)
To: netdev
Cc: Petr Oros, Tony Nguyen, Przemek Kitszel, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ivan Vecera, Jacob Keller, Alice Michael, Simon Horman,
Martyna Szapar-Mudlaw, Marcin Szycik, intel-wired-lan,
linux-kernel
In-Reply-To: <20260701133601.2118382-1-poros@redhat.com>
When the uplink PF is promiscuous, ice_vsi_sync_fltr() installs an
ICE_SW_LKUP_DFLT catch-all Rx rule on the uplink VSI. Entering switchdev
re-affirms it through the idempotent ice_set_dflt_vsi(), but
ice_eswitch_release_env() removed both the Rx and Tx DFLT rules
unconditionally on teardown. That clobbered a promisc-owned Rx rule: it
disappeared while IFF_PROMISC was still set and the sync path was not
retriggered, leaving the uplink without the catch-all the netdev
requested.
Skip the Rx DFLT removal when the uplink is promiscuous, both in
ice_eswitch_release_env() and the err_def_tx unwind of
ice_eswitch_setup_env(); the Tx leg, owned by switchdev, is still removed.
Test the live netdev->flags, the same value ena_rx_filtering() ->
ice_cfg_vlan_pruning() above already keys on, so the preserved rule and
the pruning state stay consistent, including for a promisc change made
while switchdev ran (which never reached the gated filter sync).
Fixes: 5c07be96d8b3 ("ice: Avoid setting default Rx VSI twice in switchdev setup")
Signed-off-by: Petr Oros <poros@redhat.com>
---
v3:
- Corrected the Fixes tag from 1a1c40df2e80 ("ice: set and release
switchdev environment") to 5c07be96d8b3 ("ice: Avoid setting default
Rx VSI twice in switchdev setup"), the commit that made
ice_eswitch_setup_env() use the idempotent ice_set_dflt_vsi(); before
it a pre-existing promisc DFLT rule made setup fail with -EEXIST so the
release path was never reached. No code change.
v2: https://lore.kernel.org/all/20260622113428.2565255-3-poros@redhat.com/
v1: https://lore.kernel.org/all/deef5756e534ef06c12d910c5305d3fd205d30a0.1781786935.git.poros@redhat.com/
---
drivers/net/ethernet/intel/ice/ice_eswitch.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_eswitch.c b/drivers/net/ethernet/intel/ice/ice_eswitch.c
index c30e27bbfe6e25..07e2016fb9481f 100644
--- a/drivers/net/ethernet/intel/ice/ice_eswitch.c
+++ b/drivers/net/ethernet/intel/ice/ice_eswitch.c
@@ -66,8 +66,10 @@ static int ice_eswitch_setup_env(struct ice_pf *pf)
ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx, false,
ICE_FLTR_TX);
err_def_tx:
- ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx, false,
- ICE_FLTR_RX);
+ /* keep the Rx DFLT rule if the uplink is promiscuous (see release_env) */
+ if (!(uplink_vsi->netdev->flags & IFF_PROMISC))
+ ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx,
+ false, ICE_FLTR_RX);
err_def_rx:
ice_vsi_del_vlan_zero(uplink_vsi);
err_vlan_zero:
@@ -276,8 +278,16 @@ static void ice_eswitch_release_env(struct ice_pf *pf)
vlan_ops->ena_rx_filtering(uplink_vsi);
ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx, false,
ICE_FLTR_TX);
- ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx, false,
- ICE_FLTR_RX);
+
+ /* Keep the Rx DFLT rule if the uplink is promiscuous; it must outlive
+ * the session. Test the live netdev->flags, the same value
+ * ena_rx_filtering() -> ice_cfg_vlan_pruning() above keys its decision
+ * on, so the preserved DFLT rule and the pruning state stay consistent.
+ */
+ if (!(uplink_vsi->netdev->flags & IFF_PROMISC))
+ ice_cfg_dflt_vsi(uplink_vsi->port_info, uplink_vsi->idx,
+ false, ICE_FLTR_RX);
+
ice_fltr_add_mac_and_broadcast(uplink_vsi,
uplink_vsi->port_info->mac.perm_addr,
ICE_FWD_TO_VSI);
--
2.54.0
^ permalink raw reply related
* [PATCH iwl-net v3 1/2] ice: skip per-VLAN promisc rules when default VSI Rx rule is set
From: Petr Oros @ 2026-07-01 13:36 UTC (permalink / raw)
To: netdev
Cc: Petr Oros, Tony Nguyen, Przemek Kitszel, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ivan Vecera, Jacob Keller, Alice Michael, Simon Horman,
Martyna Szapar-Mudlaw, Marcin Szycik, intel-wired-lan,
linux-kernel
In-Reply-To: <20260701133601.2118382-1-poros@redhat.com>
When an ice port in a vlan-filtering bridge goes promiscuous (typical for
bond slaves), the driver installs a per-VLAN ICE_SW_LKUP_PROMISC_VLAN rule
for every VID on top of the broad ICE_SW_LKUP_DFLT VSI Rx rule. Each rule
consumes one of the ~32K Flow Lookup Unit (FLU) entries the device shares
across PFs, so a wide trunk (vid 2-4094) over several PFs overruns the
pool: firmware rejects further Add Switch Rules with ENOSPC (AQ 0x10) and
the DFLT Rx rule itself fails to install:
ice 0000:5c:00.1: Failed to set VSI 14 as the default forwarding
VSI, error -5
ice 0000:5c:00.1 ens1f1: Error -5 setting default VSI 14 Rx rule
Once a switch context is overrun the retries can also come back as ENOENT
(AQ 0x2), which has misled triage toward a perceived recipe binding defect
rather than a capacity issue.
The DFLT rule already catches every packet on the port regardless of VLAN
tag, so the per-VLAN promisc expansion is redundant while it is installed.
Skip it at the two sites that drive it, ice_set_promisc() and
ice_vlan_rx_add_vid(), keyed on ice_is_vsi_dflt_vsi() rather than the
netdev IFF_PROMISC flag so a failed or LAG-suppressed DFLT install still
falls back to the per-VLAN rules.
IFF_ALLMULTI and IFF_PROMISC can reach ice_vsi_sync_fltr() in separate
passes (a bridge join sets them through separate calls), so the allmulti
pass may expand the per-VID rules before the DFLT rule exists. Drop those
now-redundant rules right after ice_set_dflt_vsi() installs the DFLT rule;
ice_vsi_exit_dflt_promisc() reinstates them when promisc is cleared.
ice_vsi_sync_fltr() subscribed multicast promiscuity only inside the
"default VSI not yet in use" branch, so a promiscuous VSI that finds the
default VSI rule already present (owned by another VSI, or preserved
across a switchdev session) ended up in unicast promisc with no multicast
subscription. Issue ice_set_promisc(ICE_MCAST_PROMISC_BITS) whenever the
netdev is promiscuous; it is idempotent and returns 0 if the rule is
already present.
Fixes: 1273f89578f2 ("ice: Fix broken IFF_ALLMULTI handling")
Signed-off-by: Petr Oros <poros@redhat.com>
---
v3:
- Dropped the two vid=0 ICE_SW_LKUP_PROMISC <-> ICE_SW_LKUP_PROMISC_VLAN
recipe-swap guards in ice_vlan_rx_add_vid() and ice_vlan_rx_kill_vid();
each swap is net-zero and guarding the demote stranded the vid=0 rule
in ICE_SW_LKUP_PROMISC_VLAN when the last VLAN was removed under the
DFLT rule. Reported by review.
- Drop the now-redundant per-VID multicast promisc rules right after
ice_set_dflt_vsi(). A bridge join raises IFF_ALLMULTI and IFF_PROMISC
in separate sync passes, so the allmulti pass expands the per-VID rules
before the DFLT rule exists; the cleanup keeps them from lingering and
exhausting the FLU pool. ice_vsi_exit_dflt_promisc() reinstates them on
promisc off. Reported by review.
- Issue ice_set_promisc(ICE_MCAST_PROMISC_BITS) whenever the netdev is
promiscuous, not only when this VSI installs the default VSI rule, so
multicast promisc is not lost when the rule is already in use (owned by
another VSI, or preserved across a switchdev session). Reported by
review.
- Hoisted the combined VLAN promisc mask in ice_clear_promisc() into a
local for alignment. Dropped Aleksandr's Reviewed-by since the code
changed.
v2: https://lore.kernel.org/all/20260622113428.2565255-2-poros@redhat.com/
v1: https://lore.kernel.org/all/89efbea9831175e6f57e9fe8557f7a0e48e050b7.1781786935.git.poros@redhat.com/
---
drivers/net/ethernet/intel/ice/ice_main.c | 111 ++++++++++++++++++----
1 file changed, 90 insertions(+), 21 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index b43d420ece99ca..a84de6cf6eb078 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -274,7 +274,8 @@ static int ice_set_promisc(struct ice_vsi *vsi, u8 promisc_m)
if (vsi->type != ICE_VSI_PF)
return 0;
- if (ice_vsi_has_non_zero_vlans(vsi)) {
+ /* skip per-VID expansion; the DFLT Rx rule already covers every VID */
+ if (ice_vsi_has_non_zero_vlans(vsi) && !ice_is_vsi_dflt_vsi(vsi)) {
promisc_m |= (ICE_PROMISC_VLAN_RX | ICE_PROMISC_VLAN_TX);
status = ice_fltr_set_vlan_vsi_promisc(&vsi->back->hw, vsi,
promisc_m);
@@ -304,9 +305,20 @@ static int ice_clear_promisc(struct ice_vsi *vsi, u8 promisc_m)
return 0;
if (ice_vsi_has_non_zero_vlans(vsi)) {
- promisc_m |= (ICE_PROMISC_VLAN_RX | ICE_PROMISC_VLAN_TX);
+ u8 vlan_promisc_m = promisc_m | ICE_PROMISC_VLAN_RX |
+ ICE_PROMISC_VLAN_TX;
+ int vid0_status;
+
+ /* set time used either recipe (per-VID PROMISC_VLAN, or vid=0
+ * PROMISC via the ice_set_promisc() else branch), so clear
+ * both; clearing an absent rule succeeds
+ */
status = ice_fltr_clear_vlan_vsi_promisc(&vsi->back->hw, vsi,
- promisc_m);
+ vlan_promisc_m);
+ vid0_status = ice_fltr_clear_vsi_promisc(&vsi->back->hw,
+ vsi->idx, promisc_m, 0);
+ if (!status)
+ status = vid0_status;
} else {
status = ice_fltr_clear_vsi_promisc(&vsi->back->hw, vsi->idx,
promisc_m, 0);
@@ -317,6 +329,59 @@ static int ice_clear_promisc(struct ice_vsi *vsi, u8 promisc_m)
return status;
}
+/**
+ * ice_vsi_exit_dflt_promisc - drop the default VSI Rx rule on promisc off
+ * @vsi: the VSI leaving promiscuous mode
+ *
+ * For an IFF_ALLMULTI VSI with VLANs the per-VID multicast rules are
+ * reinstated before the default rule is cleared so coverage never lapses;
+ * the then redundant vid=0 rule is dropped best-effort. The callees log
+ * their own failures, so error returns are not re-logged here.
+ *
+ * Return: 0 on success, negative on error with the default rule left in place.
+ */
+static int ice_vsi_exit_dflt_promisc(struct ice_vsi *vsi)
+{
+ struct ice_vsi_vlan_ops *vlan_ops = ice_get_compat_vsi_vlan_ops(vsi);
+ struct net_device *netdev = vsi->netdev;
+ struct ice_hw *hw = &vsi->back->hw;
+ bool restore_mc;
+ int err;
+
+ restore_mc = (vsi->current_netdev_flags & IFF_ALLMULTI) &&
+ ice_vsi_has_non_zero_vlans(vsi);
+
+ if (restore_mc) {
+ err = ice_fltr_set_vlan_vsi_promisc(hw, vsi,
+ ICE_MCAST_VLAN_PROMISC_BITS);
+ if (err && err != -EEXIST)
+ return err;
+ }
+
+ err = ice_clear_dflt_vsi(vsi);
+ if (err)
+ return err;
+
+ if (netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+ vlan_ops->ena_rx_filtering(vsi);
+
+ if (restore_mc)
+ ice_fltr_clear_vsi_promisc(hw, vsi->idx, ICE_MCAST_PROMISC_BITS,
+ 0);
+
+ return 0;
+}
+
+/* Drop the per-VID multicast promisc rules, redundant once the default
+ * VSI Rx rule covers every VID. A no-op when the VSI has no VLANs.
+ */
+static void ice_vsi_clear_vlan_mc_promisc(struct ice_vsi *vsi)
+{
+ if (ice_vsi_has_non_zero_vlans(vsi))
+ ice_fltr_clear_vlan_vsi_promisc(&vsi->back->hw, vsi,
+ ICE_MCAST_VLAN_PROMISC_BITS);
+}
+
/**
* ice_vsi_sync_fltr - Update the VSI filter list to the HW
* @vsi: ptr to the VSI
@@ -429,30 +494,35 @@ static int ice_vsi_sync_fltr(struct ice_vsi *vsi)
err = 0;
vlan_ops->dis_rx_filtering(vsi);
- /* promiscuous mode implies allmulticast so
- * that VSIs that are in promiscuous mode are
- * subscribed to multicast packets coming to
- * the port
+ /* DFLT now covers every VID; drop the per-VID
+ * multicast promisc rules a prior IFF_ALLMULTI
+ * pass may have installed (separate passes on a
+ * bridge join) so they do not linger and exhaust
+ * the FLU pool. exit_dflt_promisc() reinstates
+ * them on promisc off.
*/
- err = ice_set_promisc(vsi,
- ICE_MCAST_PROMISC_BITS);
- if (err)
- goto out_promisc;
+ ice_vsi_clear_vlan_mc_promisc(vsi);
}
+
+ /* Promiscuous mode implies allmulticast. Subscribe
+ * the VSI to all multicast even when the default VSI
+ * rule is already in use and the block above is
+ * skipped (it may be owned by another VSI, or
+ * preserved across a switchdev session); the unicast
+ * catch-all does not cover the multicast subscription.
+ */
+ err = ice_set_promisc(vsi, ICE_MCAST_PROMISC_BITS);
+ if (err)
+ goto out_promisc;
} else {
/* Clear Rx filter to remove traffic from wire */
if (ice_is_vsi_dflt_vsi(vsi)) {
- err = ice_clear_dflt_vsi(vsi);
+ err = ice_vsi_exit_dflt_promisc(vsi);
if (err) {
- netdev_err(netdev, "Error %d clearing default VSI %i Rx rule\n",
- err, vsi->vsi_num);
vsi->current_netdev_flags |=
IFF_PROMISC;
goto out_promisc;
}
- if (vsi->netdev->features &
- NETIF_F_HW_VLAN_CTAG_FILTER)
- vlan_ops->ena_rx_filtering(vsi);
}
/* disable allmulti here, but only if allmulti is not
@@ -3676,10 +3746,9 @@ int ice_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
while (test_and_set_bit(ICE_CFG_BUSY, vsi->state))
usleep_range(1000, 2000);
- /* Add multicast promisc rule for the VLAN ID to be added if
- * all-multicast is currently enabled.
- */
- if (vsi->current_netdev_flags & IFF_ALLMULTI) {
+ /* skip the per-VID rule when the DFLT Rx rule already covers this VID */
+ if ((vsi->current_netdev_flags & IFF_ALLMULTI) &&
+ !ice_is_vsi_dflt_vsi(vsi)) {
ret = ice_fltr_set_vsi_promisc(&vsi->back->hw, vsi->idx,
ICE_MCAST_VLAN_PROMISC_BITS,
vid);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v6 1/9] block: partitions: of: Skip child nodes without reg property
From: Loic Poulain @ 2026-07-01 13:35 UTC (permalink / raw)
To: Rob Herring
Cc: Ulf Hansson, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Konrad Dybcio, Jens Axboe, Johannes Berg, Jeff Johnson,
Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Srinivas Kandagatla,
Andrew Lunn, Heiner Kallweit, Russell King, Saravana Kannan,
Christian Marangi, linux-mmc, devicetree, linux-kernel,
linux-arm-msm, linux-block, linux-wireless, ath10k,
linux-bluetooth, netdev, daniel, stable, Bartosz Golaszewski
In-Reply-To: <CAL_JsqKFjk-mdaAAOzNB6rFiJbw5gd4eDpRBLQL-4q+uJKnp3g@mail.gmail.com>
On Tue, Jun 30, 2026 at 11:46 PM Rob Herring <robh@kernel.org> wrote:
>
> On Tue, Jun 30, 2026 at 2:59 PM Loic Poulain
> <loic.poulain@oss.qualcomm.com> wrote:
> >
> > Hi Rob,
> >
> > On Tue, Jun 30, 2026 at 8:02 PM Rob Herring <robh@kernel.org> wrote:
> > >
> > > On Mon, Jun 29, 2026 at 10:55:20AM +0200, Loic Poulain wrote:
> > > > Child nodes of a fixed-partitions node are not necessarily partition
> > > > entries, for example an nvmem-layout node has no reg property. The
> > > > current code passes a NULL reg pointer and uninitialized len to the
> > > > length check, which can result in a kernel panic or silent failure to
> > > > register any partitions.
> > >
> > > That does not sound right to me. A fixed-partitions node should only be
> > > defining partitions with address ranges. I would expect a partition node
> > > could be nvmem-layout, but not the whole address range. If you wanted
> > > the latter, then just do:
> > >
> > > partitions {
> > > ...
> > > };
> > >
> > > nvmem-layout {
> > > ...
> > > };
> >
> > In our case, the nvmem-layout needs to be associated with a specific
> > eMMC hardware partition, nvmem cells can be a simple sub-range within
> > the global eMMC, each hardware partition (boot0, boot1, user...)
> > having its own address spaces.
> >
> > That said, your point about not abusing fixed-partitions is valid. I
> > initially dropped the compatible = "fixed-partitions" from the
> > partitions-boot1 node when it only carries an nvmem-layout and no
> > actual partition entries, making it a plain named container node. But
> > it's a bit fragile if we want to support both nvmem-layout and
> > fixed-partitions.
> >
> > Regarding your expectation of a partition node being a nvmem-layout,
> > do you mean that the nvmem-layout should live under a fixed-partitions
> > node? Something along these lines:
> >
> > partitions-boot1 {
> > compatible = "fixed-partitions";
> > #address-cells = <1>;
> > #size-cells = <1>;
> >
> > nvmem@4400 {
>
> partition@4400
>
> > reg = <0x4400 0x1000>;
> >
> > nvmem-layout {
> > compatible = "fixed-layout";
> > #address-cells = <1>;
> > #size-cells = <1>;
> >
> > wifi_mac_addr: mac-addr@0 {
> > compatible = "mac-base";
> > reg = <0x0 0x6>;
> > #nvmem-cell-cells = <1>;
> > };
> > [...]
>
> Either this or replacing "fixed-partitions" with "fixed-layout" if you
> want to make the whole boot1 partition nvmem-layout looks like the
> right way to me.
Well, now I think both approaches make sense. We should support a
fixed-layout on the entire hw-part/block, while also allowing it
within individual logical partitions.
Support for the former would only require a small rework/addition in
this series (to have the hw boot partition a fixed-layout) . The
latter could come in a follow-up series, as it would require some
additional fwnode logic.
>
> > That makes some sense, this would require extra work for the
> > emmc/block layer to also associate fwnodes with logical partitions,
> > not just the whole disk/hw (hw part), Is that the direction you'd like
> > us to go?
>
> Yes.
>
> > Also, Note that regardless of which approach we settle on, this
> > specific fix/patch remains necessary to validate the partition node
> > and prevent NULL-deref.
>
> Fair enough, though the reasoning for it would be different and
> perhaps should give a warning.
Sure.
Thanks,
Loic
^ permalink raw reply
* [PATCH iwl-net v3 0/2] ice: fix DFLT Rx rule handling for promisc and switchdev
From: Petr Oros @ 2026-07-01 13:35 UTC (permalink / raw)
To: netdev
Cc: Petr Oros, Tony Nguyen, Przemek Kitszel, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ivan Vecera, Jacob Keller, Alice Michael, Simon Horman,
Martyna Szapar-Mudlaw, Marcin Szycik, intel-wired-lan,
linux-kernel
Two fixes for the uplink default VSI Rx rule (DFLT) on E810 when the
netdev is in IFF_PROMISC.
Patch 1 drops the redundant per-VLAN promisc expansion that exhausts
the FLU pool on a wide VLAN trunk across several PFs.
Patch 2 keeps the DFLT Rx rule across a switchdev teardown instead of
clobbering the promisc state the operator asked for.
Changes since v2:
- Patch 1: dropped the two vid=0 ICE_SW_LKUP_PROMISC <->
ICE_SW_LKUP_PROMISC_VLAN recipe-swap guards; each swap is net-zero and
guarding the demote stranded the vid=0 rule when the last VLAN was
removed under the DFLT rule.
- Patch 1: drop the now-redundant per-VID multicast promisc rules right
after ice_set_dflt_vsi(). A bridge join raises IFF_ALLMULTI and
IFF_PROMISC in separate sync passes, so the allmulti pass expands the
per-VID rules before the DFLT rule exists; the cleanup keeps them from
lingering and exhausting the FLU pool. exit_dflt_promisc() reinstates
them on promisc off.
- Patch 1: issue ice_set_promisc(ICE_MCAST_PROMISC_BITS) whenever the
netdev is promiscuous, not only when this VSI installs the default VSI
rule, so multicast promisc is not lost when the rule is already in use
(owned by another VSI, or preserved across a switchdev session).
- Patch 1: hoisted the combined VLAN promisc mask in ice_clear_promisc()
into a local for alignment. Dropped Aleksandr's Reviewed-by since the
code changed.
- Patch 2: corrected the Fixes tag to 5c07be96d8b3, the commit that made
ice_eswitch_setup_env() use the idempotent ice_set_dflt_vsi(); before
it a pre-existing promisc DFLT rule made setup fail with -EEXIST so the
release path was never reached.
Link to v2:
https://lore.kernel.org/all/20260622113428.2565255-1-poros@redhat.com/
Link to v1:
https://lore.kernel.org/all/cover.1781786935.git.poros@redhat.com/
Petr Oros (2):
ice: skip per-VLAN promisc rules when default VSI Rx rule is set
ice: preserve uplink DFLT Rx rule on switchdev release
drivers/net/ethernet/intel/ice/ice_eswitch.c | 18 ++-
drivers/net/ethernet/intel/ice/ice_main.c | 111 +++++++++++++++----
2 files changed, 104 insertions(+), 25 deletions(-)
--
2.54.0
^ permalink raw reply
* Re: [PATCH net 1/2] octeon_ep: fix skb frags overflow in the RX path
From: Maciej Fijalkowski @ 2026-07-01 13:31 UTC (permalink / raw)
To: Maoyi Xie
Cc: Veerasenareddy Burru, Sathesh Edara, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, linux-kernel
In-Reply-To: <20260701112825.1653044-2-maoyixie.tju@gmail.com>
On Wed, Jul 01, 2026 at 07:28:24PM +0800, Maoyi Xie wrote:
> __octep_oq_process_rx() builds an skb for a multi-buffer packet by adding
> one fragment per buffer_size chunk:
>
> data_len = buff_info->len - oq->max_single_buffer_size;
> while (data_len) {
> ...
> skb_add_rx_frag(skb, shinfo->nr_frags, buff_info->page, 0,
> buff_info->len, buff_info->len);
> ...
> }
>
> buff_info->len comes from the device response header
> (be64_to_cpu(resp_hw->length)). Nothing bounds the fragment count against
> MAX_SKB_FRAGS. data_len can be close to 65535. buffer_size defaults to
> about 3776 on 4K pages, so a full packet yields about 18 fragments. That
> is one more than the default MAX_SKB_FRAGS of 17, so skb_add_rx_frag()
> writes past shinfo->frags[].
>
> The driver now drops a packet that would need more fragments than the skb
> can hold. octep_oq_drop_rx() consumes its descriptors, as on the build_skb
> failure path. The same class was fixed in other RX paths, including
> commit 5ffcb7b890f6 ("net: atlantic: fix fragment overflow handling in RX
> path") and commit f0813bcd2d9d ("net: wwan: t7xx: fix potential skb->frags
> overflow in RX path").
>
> Fixes: 37d79d059606 ("octeon_ep: add Tx/Rx processing and interrupt support")
> Co-developed-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
> Signed-off-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
> Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
> ---
> drivers/net/ethernet/marvell/octeon_ep/octep_rx.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
> index e6ebc7e44a00c..4ee911b6c0107 100644
> --- a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
> +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
> @@ -476,6 +476,12 @@ static int __octep_oq_process_rx(struct octep_device *oct,
> skb_put(skb, oq->max_single_buffer_size);
> shinfo = skb_shinfo(skb);
> data_len = buff_info->len - oq->max_single_buffer_size;
> + if (DIV_ROUND_UP(data_len, oq->buffer_size) > MAX_SKB_FRAGS) {
> + dev_kfree_skb_any(skb);
> + octep_oq_drop_rx(oq, buff_info,
> + &read_idx, &desc_used);
nit: would it make sense to pass data_len to octep_oq_drop_rx() to avoid
re-calculation here
But besides, wouldn't this be better to do even before build_skb() call ?
> + continue;
> + }
> while (data_len) {
> buff_info = (struct octep_rx_buffer *)
> &oq->buff_info[read_idx];
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH v2 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes
From: Konrad Dybcio @ 2026-07-01 13:31 UTC (permalink / raw)
To: George Moussalem, Jens Axboe, Ulf Hansson, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Bjorn Andersson,
Konrad Dybcio, Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc
In-Reply-To: <SN7PR19MB6736B784D6A16CBA531DBD5C9DF62@SN7PR19MB6736.namprd19.prod.outlook.com>
On 7/1/26 3:15 PM, George Moussalem wrote:
> On 7/1/26 14:40, Konrad Dybcio wrote:
>> On 6/29/26 3:01 PM, George Moussalem via B4 Relay wrote:
>>> From: George Moussalem <george.moussalem@outlook.com>
>>>
>>> The Bluetooth subsystem (BTSS) on the IPQ5018 SoC supports setting power
>>> modes which are required to be configured through a Secure Channel
>>> Manager (SCM) call to TrustZone. However, not all Trusted Execution
>>> Environment (QSEE) images support this call, so first check if the call
>>> is available.
>>>
>>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>>> ---
>>
>> I'm amazed changing this setting is a secure operation
>>
>> [...]
>>
>>> +/**
>>> + * qcom_scm_pas_set_bluetooth_power_mode() - Configure power optimization mode
>>> + * for the Bluetooth subsystem (BTSS)
>>> + * @pas_id: peripheral authentication service id
>>> + * @val: 0x0 for normal operation, 0x4 for ECO mode
>>
>> If there's just two values, maybe we should make this take a `bool eco_mode`?
>
> that was the direction I was going in initially but then thought that
> there may be more (undocumented) power modes I'm unaware off so changed
> it to u32. I'll change it back to bool.
We can always change back if needed
Konrad
^ permalink raw reply
* [PATCH RFC net-next] net: hns3: add VF representor for tc flower queue selection
From: Jijie Shao @ 2026-07-01 13:08 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: shenjian15, liuyonglong, chenhao418, yangshuaisong, netdev,
linux-kernel, shaojijie
Background
The hns3 driver supports configuring FD (Flow Director) rules via
ethtool to specify queue mapping or drop actions for traffic received
by the PF. ethtool also supports specifying a vf parameter to
configure the same rules for VF traffic (ethtool -U eth0 flow-type
tcp4 vf 1 queue 3).
The community recommends using tc flower to replace ethtool ntuple/rss
flow configuration. tc flower already supports FD rule configuration
on the PF side, but there is currently no mechanism to configure
equivalent rules for VF traffic through tc flower.
The hns3 FD engine is a TCAM-based match+action engine, not a
switching or forwarding engine. It matches packets by the DST_VPORT
tuple (vport_id) in TCAM key metadata, with actions being queue
selection or packet drop. FD rules are configured by the PF driver
and sent to firmware. Since firmware has visibility into all PF and
VF vport resources, the PF can configure queue selection and drop
rules on behalf of VFs.
Motivation
We would like to add tc flower support for configuring queue selection
and drop rules on VF traffic in the hns3 driver. We investigated
several approaches:
Approach A (REDIRECT + QUEUE_MAPPING dual action):
Configure tc flower on the PF netdev, using FLOW_ACTION_REDIRECT to
specify the VF device and FLOW_ACTION_RX_QUEUE_MAPPING for the queue.
However, REDIRECT semantically means "redirect the packet to another
device" (forwarding), while the hns3 FD engine does not forward
packets -- it only matches packets and selects a queue or drops them.
The semantics do not align.
Approach B (MARK encoding vf_id):
Use FLOW_ACTION_MARK high bits to encode the VF ID and
QUEUE_MAPPING for the queue number. But MARK is designed for packet
marking for downstream processing. Encoding driver-internal
parameters in it is a semantic abuse with no community precedent.
Approach C (VF Representor):
Create a representor net_device for each VF. TC flower rules are
configured on the representor, whose identity implicitly encodes
the VF. This approach has clear semantics -- "configure a rule for
VF1" means operating on VF1's representor -- and has precedent
in other drivers. Considering code complexity, semantic correctness,
and alignment with community conventions, we chose Approach C.
This RFC patch presents our initial implementation to gather feedback
on whether this approach is reasonable and what improvements are needed.
Approach
Introduce VF representor net_devices to provide tc flower capability
equivalent to ethtool "flow-type ... vf N queue M".
The PF creates one representor net_device (e.g. eno2_rep0) per VF at
SRIOV enable time, serving as the tc flower rule configuration handle.
The representor is NOT a switch port and does NOT participate in
datapath forwarding -- its ndo_start_xmit simply drops packets.
When a tc flower rule is configured on the representor, the PF driver
automatically extracts the corresponding VF vport from the
representor's cb_priv (struct hclge_vf_rep) and sets DST_VPORT in
the TCAM key to that VF's vport_id, so the FD engine only matches
packets destined for that VF.
Implementation
- Extract hclge_add_cls_flower_common() from hclge_add_cls_flower(),
accepting a struct hclge_vport pointer to derive vport_id and
num_tqps, shared by both PF and VF representor paths.
- Set rule->vf_id to vport->vport_id (0 for PF, hardware-assigned
vport_id for VF) for TCAM DST_VPORT matching and FD counter
differentiation.
- Queue ID is validated against the VF's actual queue count
(vport->nic.kinfo.num_tqps).
- VF representor does NOT support implicit TC selection via classid
(HCLGE_FD_ACTION_SELECT_TC). Only explicit actions are supported:
- FLOW_ACTION_RX_QUEUE_MAPPING: queue selection
- FLOW_ACTION_DROP: packet drop
- VF representor creation failure does not roll back SRIOV. The VF
datapath works independently and is not affected.
- ndo_get_phys_port_name returns "pfNvfM" for user-space
identification.
This is an RFC patch for early review. It has been through basic
verification but is not intended for formal submission. Further
review and testing are required.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
drivers/net/ethernet/hisilicon/hns3/Makefile | 2 +-
drivers/net/ethernet/hisilicon/hns3/hnae3.h | 2 +
.../net/ethernet/hisilicon/hns3/hns3_enet.c | 16 +-
.../ethernet/hisilicon/hns3/hns3pf/hclge_fd.c | 48 +++++-
.../ethernet/hisilicon/hns3/hns3pf/hclge_fd.h | 7 +
.../hisilicon/hns3/hns3pf/hclge_main.c | 2 +
.../hisilicon/hns3/hns3pf/hclge_main.h | 6 +
.../hisilicon/hns3/hns3pf/hclge_vf_rep.c | 159 ++++++++++++++++++
.../hisilicon/hns3/hns3pf/hclge_vf_rep.h | 21 +++
9 files changed, 251 insertions(+), 12 deletions(-)
create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_vf_rep.c
create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_vf_rep.h
diff --git a/drivers/net/ethernet/hisilicon/hns3/Makefile b/drivers/net/ethernet/hisilicon/hns3/Makefile
index 5785d4c5709e..6fd83c959495 100644
--- a/drivers/net/ethernet/hisilicon/hns3/Makefile
+++ b/drivers/net/ethernet/hisilicon/hns3/Makefile
@@ -24,6 +24,6 @@ hclgevf-objs = hns3vf/hclgevf_main.o hns3vf/hclgevf_mbx.o hns3vf/hclgevf_devlin
obj-$(CONFIG_HNS3_HCLGE) += hclge.o hclge-common.o
hclge-objs = hns3pf/hclge_main.o hns3pf/hclge_mdio.o hns3pf/hclge_tm.o hns3pf/hclge_regs.o \
hns3pf/hclge_mbx.o hns3pf/hclge_err.o hns3pf/hclge_debugfs.o hns3pf/hclge_ptp.o hns3pf/hclge_devlink.o \
- hns3pf/hclge_fd.o
+ hns3pf/hclge_fd.o hns3pf/hclge_vf_rep.o
hclge-$(CONFIG_HNS3_DCB) += hns3pf/hclge_dcb.o
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index a8798eecd9fb..d2c1c73617a4 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -796,6 +796,8 @@ struct hnae3_ae_ops {
int (*get_link_diagnosis_info)(struct hnae3_handle *handle,
u32 *status_code);
void (*clean_vf_config)(struct hnae3_ae_dev *ae_dev, int num_vfs);
+ int (*create_vf_reps)(struct hnae3_ae_dev *ae_dev, int num_vfs);
+ void (*destroy_vf_reps)(struct hnae3_ae_dev *ae_dev);
int (*get_dscp_prio)(struct hnae3_handle *handle, u8 dscp,
u8 *tc_map_mode, u8 *priority);
void (*get_wol)(struct hnae3_handle *handle,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 6ecb32e28e79..f393e9f81fa6 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -3211,6 +3211,7 @@ static void hns3_remove(struct pci_dev *pdev)
**/
static int hns3_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
+ struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev);
int ret;
if (!(hns3_is_phys_func(pdev) && IS_ENABLED(CONFIG_PCI_IOV))) {
@@ -3220,13 +3221,24 @@ static int hns3_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
if (num_vfs) {
ret = pci_enable_sriov(pdev, num_vfs);
- if (ret)
+ if (ret) {
dev_err(&pdev->dev, "SRIOV enable failed %d\n", ret);
- else
+ } else {
+ if (ae_dev->ops->create_vf_reps) {
+ ret = ae_dev->ops->create_vf_reps(ae_dev,
+ num_vfs);
+ if (ret)
+ dev_warn(&pdev->dev,
+ "failed to create VF representors: %d\n",
+ ret);
+ }
return num_vfs;
+ }
} else if (!pci_vfs_assigned(pdev)) {
int num_vfs_pre = pci_num_vf(pdev);
+ if (ae_dev->ops->destroy_vf_reps)
+ ae_dev->ops->destroy_vf_reps(ae_dev);
pci_disable_sriov(pdev);
hns3_clean_vf_config(pdev, num_vfs_pre);
} else {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c
index 2fccb0a870b5..8cd9a1a8eff1 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c
@@ -6,6 +6,7 @@
#include <net/vxlan.h>
#include "hclge_fd.h"
#include "hclge_main.h"
+#include "hclge_vf_rep.h"
static const struct key_info meta_data_key_info[] = {
{ PACKET_TYPE_ID, 6 },
@@ -2285,6 +2286,7 @@ static int hclge_get_cls_key_ip_tos(const struct flow_rule *flow,
}
static int hclge_get_tc_flower_action(struct hclge_dev *hdev,
+ struct hclge_vport *vport,
struct flow_cls_offload *cls_flower,
struct hclge_fd_rule *rule)
{
@@ -2292,10 +2294,17 @@ static int hclge_get_tc_flower_action(struct hclge_dev *hdev,
struct netlink_ext_ack *extack = cls_flower->common.extack;
struct hnae3_handle *handle = &hdev->vport[0].nic;
struct flow_action *action = &flow->action;
+ u16 num_tqps = vport->nic.kinfo.num_tqps;
struct flow_action_entry *act;
int tc;
if (!flow_action_has_entries(&flow->action)) {
+ if (vport != &hdev->vport[0]) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "VF does not support select TC action");
+ return -EOPNOTSUPP;
+ }
+
tc = tc_classid_to_hwtc(handle->netdev, cls_flower->classid);
if (tc < 0 || tc > hdev->tc_max) {
NL_SET_ERR_MSG_FMT_MOD(extack,
@@ -2311,11 +2320,10 @@ static int hclge_get_tc_flower_action(struct hclge_dev *hdev,
act = &action->entries[0];
switch (act->id) {
case FLOW_ACTION_RX_QUEUE_MAPPING:
- if (act->rx_queue >= handle->kinfo.num_tqps) {
+ if (act->rx_queue >= num_tqps) {
NL_SET_ERR_MSG_FMT_MOD(extack,
"queue id (%u) should be less than %u",
- act->rx_queue,
- handle->kinfo.num_tqps);
+ act->rx_queue, num_tqps);
return -EINVAL;
}
@@ -2423,12 +2431,11 @@ static int hclge_check_cls_flower(struct hclge_dev *hdev,
return 0;
}
-int hclge_add_cls_flower(struct hnae3_handle *handle,
- struct flow_cls_offload *cls_flower)
+static int hclge_add_cls_flower_common(struct hclge_dev *hdev,
+ struct hclge_vport *vport,
+ struct flow_cls_offload *cls_flower)
{
struct netlink_ext_ack *extack = cls_flower->common.extack;
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
struct hclge_fd_rule *rule;
int ret;
@@ -2451,14 +2458,14 @@ int hclge_add_cls_flower(struct hnae3_handle *handle,
return ret;
}
- ret = hclge_get_tc_flower_action(hdev, cls_flower, rule);
+ ret = hclge_get_tc_flower_action(hdev, vport, cls_flower, rule);
if (ret) {
kfree(rule);
return ret;
}
rule->location = cls_flower->common.prio - 1;
- rule->vf_id = 0;
+ rule->vf_id = vport->vport_id;
rule->cls_flower.cookie = cls_flower->cookie;
rule->rule_type = HCLGE_FD_TC_FLOWER_ACTIVE;
@@ -2469,6 +2476,21 @@ int hclge_add_cls_flower(struct hnae3_handle *handle,
return ret;
}
+int hclge_add_cls_flower(struct hnae3_handle *handle,
+ struct flow_cls_offload *cls_flower)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+
+ return hclge_add_cls_flower_common(vport->back, vport, cls_flower);
+}
+
+int hclge_add_cls_flower_vf(struct hclge_vf_rep *vf_rep,
+ struct flow_cls_offload *cls_flower)
+{
+ return hclge_add_cls_flower_common(vf_rep->hdev, vf_rep->vport,
+ cls_flower);
+}
+
static struct hclge_fd_rule *hclge_find_cls_flower(struct hclge_dev *hdev,
unsigned long cookie)
{
@@ -2522,6 +2544,14 @@ int hclge_del_cls_flower(struct hnae3_handle *handle,
return 0;
}
+int hclge_del_cls_flower_vf(struct hclge_vf_rep *vf_rep,
+ struct flow_cls_offload *cls_flower)
+{
+ struct hclge_dev *hdev = vf_rep->hdev;
+
+ return hclge_del_cls_flower(&hdev->vport[0].nic, cls_flower);
+}
+
static void hclge_sync_fd_list(struct hclge_dev *hdev, struct hlist_head *hlist)
{
struct hclge_fd_rule *rule;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h
index 2f66cc9c3c65..200d81ce12c8 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h
@@ -6,6 +6,8 @@
struct hnae3_handle;
struct hclge_dev;
+struct hclge_vf_rep;
+struct flow_cls_offload;
int hclge_init_fd_config(struct hclge_dev *hdev);
int hclge_add_fd_entry(struct hnae3_handle *handle, struct ethtool_rxnfc *cmd);
@@ -26,6 +28,11 @@ int hclge_add_cls_flower(struct hnae3_handle *handle,
int hclge_del_cls_flower(struct hnae3_handle *handle,
struct flow_cls_offload *cls_flower);
bool hclge_is_cls_flower_active(struct hnae3_handle *handle);
+
+int hclge_add_cls_flower_vf(struct hclge_vf_rep *vf_rep,
+ struct flow_cls_offload *cls_flower);
+int hclge_del_cls_flower_vf(struct hclge_vf_rep *vf_rep,
+ struct flow_cls_offload *cls_flower);
int hclge_clear_arfs_rules(struct hclge_dev *hdev);
void hclge_sync_fd_table(struct hclge_dev *hdev);
void hclge_rfs_filter_expire(struct hclge_dev *hdev);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index fc8587c80813..5f88187f1ca8 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -10534,6 +10534,8 @@ static const struct hnae3_ae_ops hclge_ops = {
.get_ts_info = hclge_ptp_get_ts_info,
.get_link_diagnosis_info = hclge_get_link_diagnosis_info,
.clean_vf_config = hclge_clean_vport_config,
+ .create_vf_reps = hclge_create_vf_reps,
+ .destroy_vf_reps = hclge_destroy_vf_reps,
.get_dscp_prio = hclge_get_dscp_prio,
.get_wol = hclge_get_wol,
.set_wol = hclge_set_wol,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
index 7419481422c3..4f11b9c38e69 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
@@ -17,6 +17,9 @@
#include "hnae3.h"
#include "hclge_comm_rss.h"
#include "hclge_comm_tqp_stats.h"
+#include "hclge_vf_rep.h"
+
+struct hclge_vf_rep;
#define HCLGE_MOD_VERSION "1.0"
#define HCLGE_DRIVER_NAME "hclge"
@@ -935,6 +938,9 @@ struct hclge_dev {
bool cur_promisc;
int num_alloc_vfs; /* Actual number of VFs allocated */
+ struct hclge_vf_rep **vf_reps;
+ u16 num_vf_reps;
+
struct hclge_comm_tqp *htqp;
struct hclge_vport *vport;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_vf_rep.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_vf_rep.c
new file mode 100644
index 000000000000..c51336040d6f
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_vf_rep.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (c) 2026 Hisilicon Limited.
+
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <net/flow_offload.h>
+
+#include "hclge_main.h"
+#include "hclge_vf_rep.h"
+#include "hclge_fd.h"
+
+static netdev_tx_t hclge_vf_rep_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+}
+
+static int hclge_vf_rep_get_phys_port_name(struct net_device *dev,
+ char *buf, size_t len)
+{
+ struct hclge_vf_rep *vf_rep = netdev_priv(dev);
+ struct hclge_dev *hdev = vf_rep->hdev;
+ int rc;
+
+ rc = snprintf(buf, len, "pf%uvf%u", PCI_FUNC(hdev->pdev->devfn),
+ vf_rep->vport->vport_id - 1);
+ if (rc >= len)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int hclge_vf_rep_setup_tc_block_cb(enum tc_setup_type type,
+ void *type_data, void *cb_priv)
+{
+ struct flow_cls_offload *cls_flower = type_data;
+ struct hclge_vf_rep *vf_rep = cb_priv;
+
+ if (!tc_cls_can_offload_and_chain0(vf_rep->netdev, type_data))
+ return -EOPNOTSUPP;
+
+ switch (type) {
+ case TC_SETUP_CLSFLOWER:
+ switch (cls_flower->command) {
+ case FLOW_CLS_REPLACE:
+ return hclge_add_cls_flower_vf(vf_rep, cls_flower);
+ case FLOW_CLS_DESTROY:
+ return hclge_del_cls_flower_vf(vf_rep, cls_flower);
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static LIST_HEAD(hclge_vf_rep_block_cb_list);
+
+static int hclge_vf_rep_setup_tc(struct net_device *dev,
+ enum tc_setup_type type, void *type_data)
+{
+ struct hclge_vf_rep *vf_rep = netdev_priv(dev);
+
+ switch (type) {
+ case TC_SETUP_BLOCK:
+ return flow_block_cb_setup_simple(type_data,
+ &hclge_vf_rep_block_cb_list,
+ hclge_vf_rep_setup_tc_block_cb,
+ vf_rep, vf_rep, true);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct net_device_ops hclge_vf_rep_netdev_ops = {
+ .ndo_start_xmit = hclge_vf_rep_xmit,
+ .ndo_get_phys_port_name = hclge_vf_rep_get_phys_port_name,
+ .ndo_setup_tc = hclge_vf_rep_setup_tc,
+};
+
+static void hclge_vf_rep_net_setup(struct net_device *ndev)
+{
+ ndev->netdev_ops = &hclge_vf_rep_netdev_ops;
+ ndev->needs_free_netdev = true;
+ ndev->features |= NETIF_F_HW_TC;
+}
+
+int hclge_create_vf_reps(struct hnae3_ae_dev *ae_dev, int num_vfs)
+{
+ struct hclge_dev *hdev = ae_dev->priv;
+ struct hclge_vf_rep *vf_rep;
+ struct net_device *ndev;
+ char name[IFNAMSIZ];
+ int ret, i;
+
+ if (!num_vfs)
+ return 0;
+
+ hdev->vf_reps = kcalloc(num_vfs, sizeof(struct hclge_vf_rep *),
+ GFP_KERNEL);
+ if (!hdev->vf_reps)
+ return -ENOMEM;
+
+ for (i = 0; i < num_vfs; i++) {
+ snprintf(name, IFNAMSIZ, "%s_rep%d",
+ hdev->vport[0].nic.netdev->name, i);
+ ndev = alloc_netdev(sizeof(struct hclge_vf_rep), name,
+ NET_NAME_UNKNOWN, ether_setup);
+ if (!ndev) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ hclge_vf_rep_net_setup(ndev);
+
+ vf_rep = netdev_priv(ndev);
+ vf_rep->hdev = hdev;
+ vf_rep->vport = &hdev->vport[i + HCLGE_VF_VPORT_START_NUM];
+ vf_rep->netdev = ndev;
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ free_netdev(ndev);
+ goto err;
+ }
+
+ hdev->vf_reps[i] = vf_rep;
+ }
+
+ hdev->num_vf_reps = num_vfs;
+ return 0;
+
+err:
+ while (i--)
+ unregister_netdev(hdev->vf_reps[i]->netdev);
+ kfree(hdev->vf_reps);
+ hdev->vf_reps = NULL;
+ return ret;
+}
+
+void hclge_destroy_vf_reps(struct hnae3_ae_dev *ae_dev)
+{
+ struct hclge_dev *hdev = ae_dev->priv;
+ int i;
+
+ if (!hdev->vf_reps)
+ return;
+
+ for (i = 0; i < hdev->num_vf_reps; i++) {
+ if (hdev->vf_reps[i])
+ unregister_netdev(hdev->vf_reps[i]->netdev);
+ }
+
+ kfree(hdev->vf_reps);
+ hdev->vf_reps = NULL;
+ hdev->num_vf_reps = 0;
+}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_vf_rep.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_vf_rep.h
new file mode 100644
index 000000000000..fb2080ae627b
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_vf_rep.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2026 Hisilicon Limited. */
+
+#ifndef __HCLGE_VF_REP_H
+#define __HCLGE_VF_REP_H
+
+struct hnae3_ae_dev;
+struct hclge_dev;
+struct hclge_vport;
+struct net_device;
+
+struct hclge_vf_rep {
+ struct hclge_dev *hdev;
+ struct hclge_vport *vport;
+ struct net_device *netdev;
+};
+
+int hclge_create_vf_reps(struct hnae3_ae_dev *ae_dev, int num_vfs);
+void hclge_destroy_vf_reps(struct hnae3_ae_dev *ae_dev);
+
+#endif
base-commit: 1c664ec4b9ea827b609d296921ed5bad8a40a158
--
2.33.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