Netdev List
 help / color / mirror / Atom feed
* Re: [RFC PATCH net-next v2 1/2] tcp: Add net.ipv4.tcp_purge_receive_queue sysctl
From: Leon Hwang @ 2026-07-15 15:48 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: netdev, David S . Miller, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Jonathan Corbet, Shuah Khan, Neal Cardwell,
	Kuniyuki Iwashima, Ido Schimmel, Ilpo Järvinen,
	Chia-Yu Chang, Yung Chih Su, Wyatt Feng, Jason Xing, Lance Yang,
	Jiayuan Chen, linux-doc, linux-kernel, linux-kselftest
In-Reply-To: <CANn89iLV9pyCKU4vyNMzW5FFv4=Xcq_pgvZaFhzJHAAbCK0h7w@mail.gmail.com>

On 2026/7/15 23:30, Eric Dumazet wrote:
> On Wed, Jul 15, 2026 at 5:26 PM Leon Hwang <leon.hwang@linux.dev> wrote:
>>
>> On 2026/7/15 23:15, Eric Dumazet wrote:
>>> On Wed, Jul 15, 2026 at 4:54 PM Leon Hwang <leon.hwang@linux.dev> wrote:
>>
>> [...]
>>
>>>> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
>>>> index 61045a8886e4..4f1027173e95 100644
>>>> --- a/net/ipv4/tcp_input.c
>>>> +++ b/net/ipv4/tcp_input.c
>>>> @@ -4853,6 +4853,7 @@ void tcp_done_with_error(struct sock *sk, int err)
>>>>  /* When we get a reset we do this. */
>>>>  void tcp_reset(struct sock *sk, struct sk_buff *skb)
>>>>  {
>>>> +       const struct net *net = sock_net(sk);
>>>>         int err;
>>>>
>>>>         trace_tcp_receive_reset(sk);
>>>> @@ -4869,6 +4870,27 @@ void tcp_reset(struct sock *sk, struct sk_buff *skb)
>>>>                 err = ECONNREFUSED;
>>>>                 break;
>>>>         case TCP_CLOSE_WAIT:
>>>> +               /* RFC9293 3.10.7.4. Other States
>>>> +                *   Second, check the RST bit:
>>>> +                *     CLOSE-WAIT STATE
>>>> +                *
>>>> +                * If the RST bit is set, then any outstanding RECEIVEs and
>>>> +                * SEND should receive "reset" responses.  All segment queues
>>>> +                * should be flushed.  Users should also receive an unsolicited
>>>> +                * general "connection reset" signal.  Enter the CLOSED state,
>>>> +                * delete the TCB, and return.
>>>> +                *
>>>> +                * If net.ipv4.tcp_purge_receive_queue is enabled,
>>>> +                * sk_receive_queue will be flushed too.
>>>> +                */
>>>> +               if (unlikely(READ_ONCE(net->ipv4.sysctl_tcp_purge_receive_queue))) {
>>>> +                       struct tcp_sock *tp = tcp_sk(sk);
>>>> +
>>>> +                       skb_queue_purge(&sk->sk_receive_queue);
>>>> +                       WRITE_ONCE(tp->copied_seq, tp->rcv_nxt);
>>>> +                       WRITE_ONCE(tp->urg_data, 0);
>>>> +                       sk_set_peek_off(sk, -1);
>>>> +               }
>>>>                 err = EPIPE;
>>>>                 break;
>>>>         case TCP_CLOSE:
>>>> --
>>>> 2.55.0
>>>>
>>>
>>> My thoughts are:
>>>
>>> out_of_order_queue has been forgotten. skbs could be there and still
>>> 'block devmem'
>>>
>>> WRITE_ONCE(tp->copied_seq, tp->rcv_nxt) is certainly wrong, because
>>> read() will return 0, instead of -1 (errno = EPIPE or ECONNRESET)
>>> So the application will not know a RST was received :/
>>>
>>> I think that BSD and linux implementations have historically retained
>>> acknowledged,
>>> buffered receive data upon RST to allow applications to drain data
>>> already ACKed prior to the reset.
>>>
>>> Adding a narrow sysctl specifically for CLOSE_WAIT creates
>>> inconsistent behavior across TCP states.
>>
>>
>> Got it. I won't pursue this sysctl approach in the future. Thanks for
>> the review.
> 
> My intention was not to kill your proposal, only to start a conversation...

Thanks for clarifying. I agree this approach needs more thought.

Thanks,
Leon


^ permalink raw reply

* [syzbot] [net?] KASAN: slab-use-after-free Read in llc_conn_ac_send_sabme_cmd_p_set_x
From: syzbot @ 2026-07-15 15:55 UTC (permalink / raw)
  To: davem, edumazet, horms, kuba, linux-kernel, netdev, pabeni,
	syzkaller-bugs

Hello,

syzbot found the following issue on:

HEAD commit:    a13c140cc289 Linux 7.2-rc3
git tree:       upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=144c3b89580000
kernel config:  https://syzkaller.appspot.com/x/.config?x=d69870d5e052935f
dashboard link: https://syzkaller.appspot.com/bug?extid=44efda9647c52be29d9c
compiler:       gcc (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for Debian) 2.44

Unfortunately, I don't have any reproducer for this issue yet.

Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/2052a80016f0/disk-a13c140c.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/0d06ec9d98a4/vmlinux-a13c140c.xz
kernel image: https://storage.googleapis.com/syzbot-assets/8cfe7c92eaf6/bzImage-a13c140c.xz

IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+44efda9647c52be29d9c@syzkaller.appspotmail.com

==================================================================
BUG: KASAN: slab-use-after-free in llc_conn_ac_send_sabme_cmd_p_set_x+0x442/0x470 net/llc/llc_c_ac.c:782
Read of size 1 at addr ffff8880370c8010 by task kworker/u8:0/11804

CPU: 0 UID: 0 PID: 11804 Comm: kworker/u8:0 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 06/10/2026
Workqueue: events_unbound toggle_allocation_gate
Call Trace:
 <IRQ>
 __dump_stack lib/dump_stack.c:94 [inline]
 dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
 print_address_description mm/kasan/report.c:378 [inline]
 print_report+0x13d/0x4b0 mm/kasan/report.c:482
 kasan_report+0xdf/0x1c0 mm/kasan/report.c:595
 llc_conn_ac_send_sabme_cmd_p_set_x+0x442/0x470 net/llc/llc_c_ac.c:782
 llc_exec_conn_trans_actions net/llc/llc_conn.c:437 [inline]
 llc_conn_service net/llc/llc_conn.c:362 [inline]
 llc_conn_state_process+0x3ec/0x1480 net/llc/llc_conn.c:66
 llc_process_tmr_ev net/llc/llc_c_ac.c:1442 [inline]
 llc_conn_tmr_common_cb+0x2d4/0x880 net/llc/llc_c_ac.c:1325
 call_timer_fn+0x19a/0x610 kernel/time/timer.c:1748
 expire_timers kernel/time/timer.c:1799 [inline]
 __run_timers+0x757/0xb00 kernel/time/timer.c:2374
 __run_timer_base kernel/time/timer.c:2386 [inline]
 __run_timer_base kernel/time/timer.c:2378 [inline]
 run_timer_base+0x114/0x190 kernel/time/timer.c:2395
 run_timer_softirq+0x1a/0x50 kernel/time/timer.c:2405
 handle_softirqs+0x1ea/0x9b0 kernel/softirq.c:622
 __do_softirq kernel/softirq.c:656 [inline]
 invoke_softirq kernel/softirq.c:496 [inline]
 __irq_exit_rcu+0x162/0x210 kernel/softirq.c:735
 irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
 instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
 sysvec_apic_timer_interrupt+0xa3/0xc0 arch/x86/kernel/apic/apic.c:1062
 </IRQ>
 <TASK>
 asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:finish_task_switch.isra.0+0x2cd/0x10c0 kernel/sched/core.c:5361
Code: ab 0a 00 00 41 c7 87 20 0e 00 00 00 00 00 00 e9 47 04 00 00 49 8d 7f 48 e8 30 b4 e6 09 e8 1b a9 3c 00 fb 48 8d bb f8 16 00 00 <48> b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 0f b6 04 02 84
RSP: 0018:ffffc90005097578 EFLAGS: 00000202
RAX: 0000000000056db3 RBX: ffff8880741c8000 RCX: 0000000000000040
RDX: 0000000000000000 RSI: ffffffff8e1a3c59 RDI: ffff8880741c96f8
RBP: ffffc900050975d0 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: ffffffff8e891480
R13: 0000000000000001 R14: ffffffff91227064 R15: ffff8880b843b4c0
 context_switch kernel/sched/core.c:5513 [inline]
 __schedule+0x1264/0x6730 kernel/sched/core.c:7234
 preempt_schedule_irq+0x50/0x90 kernel/sched/core.c:7556
 irqentry_exit_to_kernel_mode_preempt include/linux/irq-entry-common.h:468 [inline]
 irqentry_exit_to_kernel_mode include/linux/irq-entry-common.h:539 [inline]
 irqentry_exit+0x205/0xa00 kernel/entry/common.c:167
 asm_sysvec_reschedule_ipi+0x1a/0x20 arch/x86/include/asm/idtentry.h:679
RIP: 0010:__sanitizer_cov_trace_pc+0x0/0x70 kernel/kcov.c:210
Code: 56 e1 5e 00 48 89 df 5b e9 9d 5c 64 00 be 03 00 00 00 5b e9 32 5a 13 03 66 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 <f3> 0f 1e fa 65 8b 05 85 a3 59 12 48 8b 34 24 65 48 8b 15 61 a3 59
RSP: 0018:ffffc90005097850 EFLAGS: 00000206
RAX: 0000000000000000 RBX: ffffc900050979b0 RCX: ffffffff8bb86f00
RDX: 0000000000008000 RSI: 0000000000000000 RDI: ffff8880741c8000
RBP: ffffc900050979d4 R08: 0000000000000005 R09: 0000000000000000
R10: 0000000000008000 R11: 0000000000000000 R12: ffffc900050979fc
R13: 0000000000008000 R14: 0000000000008000 R15: 0000000000000ffa
 insn_get_modrm+0xee/0x790 arch/x86/lib/insn.c:396
 insn_get_sib arch/x86/lib/insn.c:466 [inline]
 insn_get_displacement+0x2f9/0x960 arch/x86/lib/insn.c:507
 insn_get_immediate+0x84/0x1e00 arch/x86/lib/insn.c:673
 insn_get_length arch/x86/lib/insn.c:745 [inline]
 insn_decode+0x21d/0x320 arch/x86/lib/insn.c:784
 __smp_text_poke_batch_add+0x174/0x790 arch/x86/kernel/alternative.c:3103
 arch_jump_label_transform_queue+0xaf/0x120 arch/x86/kernel/jump_label.c:138
 __jump_label_update+0xd7/0x380 kernel/jump_label.c:513
 jump_label_update+0x37a/0x550 kernel/jump_label.c:910
 static_key_disable_cpuslocked+0x162/0x1c0 kernel/jump_label.c:240
 static_key_disable+0x1a/0x20 kernel/jump_label.c:248
 toggle_allocation_gate mm/kfence/core.c:918 [inline]
 toggle_allocation_gate+0x149/0x2d0 mm/kfence/core.c:903
 process_one_work+0xa23/0x1940 kernel/workqueue.c:3322
 process_scheduled_works kernel/workqueue.c:3405 [inline]
 worker_thread+0x5ef/0xe50 kernel/workqueue.c:3486
 kthread+0x370/0x450 kernel/kthread.c:436
 ret_from_fork+0x72b/0xd50 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
 </TASK>

Allocated by task 12543:
 kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
 kasan_save_track+0x14/0x30 mm/kasan/common.c:78
 poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
 __kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:415
 kasan_kmalloc include/linux/kasan.h:263 [inline]
 __kmalloc_cache_noprof+0x2e5/0x6c0 mm/slub.c:5515
 _kmalloc_noprof include/linux/slab.h:969 [inline]
 _kzalloc_noprof include/linux/slab.h:1290 [inline]
 llc_sap_alloc net/llc/llc_core.c:29 [inline]
 llc_sap_open+0x120/0x3b0 net/llc/llc_core.c:96
 llc_ui_autobind.isra.0+0x2ae/0x580 net/llc/af_llc.c:301
 llc_ui_connect+0x582/0xdb0 net/llc/af_llc.c:498
 __sys_connect_file+0x141/0x1a0 net/socket.c:2135
 __sys_connect+0x141/0x170 net/socket.c:2154
 __do_sys_connect net/socket.c:2160 [inline]
 __se_sys_connect net/socket.c:2157 [inline]
 __x64_sys_connect+0x72/0xb0 net/socket.c:2157
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Freed by task 0:
 kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
 kasan_save_track+0x14/0x30 mm/kasan/common.c:78
 kasan_save_free_info+0x3b/0x70 mm/kasan/generic.c:584
 poison_slab_object mm/kasan/common.c:253 [inline]
 __kasan_slab_free+0x5f/0x80 mm/kasan/common.c:285
 kasan_slab_free include/linux/kasan.h:235 [inline]
 slab_free_hook mm/slub.c:2705 [inline]
 __rcu_free_sheaf_prepare+0x5d/0x2f0 mm/slub.c:2956
 rcu_free_sheaf+0x1a/0xe0 mm/slub.c:6004
 rcu_do_batch kernel/rcu/tree.c:2645 [inline]
 rcu_core+0x5a2/0x10d0 kernel/rcu/tree.c:2897
 handle_softirqs+0x1ea/0x9b0 kernel/softirq.c:622
 __do_softirq kernel/softirq.c:656 [inline]
 invoke_softirq kernel/softirq.c:496 [inline]
 __irq_exit_rcu+0x162/0x210 kernel/softirq.c:735
 irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
 instr_sysvec_call_function_single arch/x86/kernel/smp.c:267 [inline]
 sysvec_call_function_single+0xa3/0xc0 arch/x86/kernel/smp.c:267
 asm_sysvec_call_function_single+0x1a/0x20 arch/x86/include/asm/idtentry.h:681

The buggy address belongs to the object at ffff8880370c8000
 which belongs to the cache kmalloc-2k of size 2048
The buggy address is located 16 bytes inside of
 freed 2048-byte region [ffff8880370c8000, ffff8880370c8800)

The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff8880370cd000 pfn:0x370c8
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0xfff00000000240(workingset|head|node=0|zone=1|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 00fff00000000240 ffff88813fe22000 ffffea0000c73e10 ffffea0000a2e810
raw: ffff8880370cd000 0000000800080007 00000000f5000000 0000000000000000
head: 00fff00000000240 ffff88813fe22000 ffffea0000c73e10 ffffea0000a2e810
head: ffff8880370cd000 0000000800080007 00000000f5000000 0000000000000000
head: 00fff00000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd2820(GFP_ATOMIC|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5615, tgid 5615 (syz-executor), ts 66637688496, free_ts 48968356257
 set_page_owner include/linux/page_owner.h:32 [inline]
 post_alloc_hook+0xfd/0x120 mm/page_alloc.c:1859
 prep_new_page mm/page_alloc.c:1867 [inline]
 get_page_from_freelist+0xf48/0x3530 mm/page_alloc.c:3946
 __alloc_frozen_pages_noprof+0x299/0x2dc0 mm/page_alloc.c:5304
 alloc_slab_page mm/slub.c:3294 [inline]
 allocate_slab mm/slub.c:3408 [inline]
 new_slab+0xa2/0x650 mm/slub.c:3454
 refill_objects+0xe3/0x410 mm/slub.c:7338
 refill_sheaf mm/slub.c:2832 [inline]
 __pcs_replace_empty_main+0x376/0x680 mm/slub.c:4703
 alloc_from_pcs mm/slub.c:4801 [inline]
 slab_alloc_node mm/slub.c:4933 [inline]
 __do_kmalloc_node mm/slub.c:5361 [inline]
 __kmalloc_node_track_caller_noprof+0x679/0x830 mm/slub.c:5497
 kmalloc_reserve+0xe8/0x350 net/core/skbuff.c:637
 pskb_expand_head+0x249/0x10e0 net/core/skbuff.c:2306
 netlink_trim+0x22d/0x2f0 net/netlink/af_netlink.c:1300
 netlink_broadcast_filtered+0x6d/0xf20 net/netlink/af_netlink.c:1518
 nlmsg_multicast_filtered include/net/netlink.h:1165 [inline]
 nlmsg_multicast include/net/netlink.h:1184 [inline]
 nlmsg_notify+0xb1/0x290 net/netlink/af_netlink.c:2599
 rtnl_notify net/core/rtnetlink.c:963 [inline]
 rtmsg_ifinfo_send net/core/rtnetlink.c:4553 [inline]
 rtmsg_ifinfo_event net/core/rtnetlink.c:4569 [inline]
 rtmsg_ifinfo_event net/core/rtnetlink.c:4556 [inline]
 rtmsg_ifinfo+0x177/0x1b0 net/core/rtnetlink.c:4575
 __dev_notify_flags+0x24c/0x2e0 net/core/dev.c:9789
 rtnl_configure_link+0x1b5/0x280 net/core/rtnetlink.c:3679
 rtnl_newlink_create net/core/rtnetlink.c:3923 [inline]
 __rtnl_newlink net/core/rtnetlink.c:4044 [inline]
 rtnl_newlink+0x153b/0x2380 net/core/rtnetlink.c:4159
page last free pid 5271 tgid 5271 stack trace:
 reset_page_owner include/linux/page_owner.h:25 [inline]
 __free_pages_prepare mm/page_alloc.c:1406 [inline]
 __free_frozen_pages+0x79f/0x1090 mm/page_alloc.c:2950
 qlink_free mm/kasan/quarantine.c:163 [inline]
 qlist_free_all+0x47/0xf0 mm/kasan/quarantine.c:179
 kasan_quarantine_reduce+0x1a0/0x1f0 mm/kasan/quarantine.c:286
 __kasan_slab_alloc+0x69/0x90 mm/kasan/common.c:350
 kasan_slab_alloc include/linux/kasan.h:253 [inline]
 slab_post_alloc_hook mm/slub.c:4612 [inline]
 slab_alloc_node mm/slub.c:4945 [inline]
 kmem_cache_alloc_node_noprof+0x27d/0x6b0 mm/slub.c:5017
 __alloc_skb+0x140/0x710 net/core/skbuff.c:704
 alloc_skb include/linux/skbuff.h:1384 [inline]
 alloc_skb_with_frags+0xdd/0x760 net/core/skbuff.c:6769
 sock_alloc_send_pskb+0x801/0x980 net/core/sock.c:3010
 unix_dgram_sendmsg+0x3c7/0x1810 net/unix/af_unix.c:2136
 unix_seqpacket_sendmsg+0x12a/0x1d0 net/unix/af_unix.c:2537
 sock_sendmsg_nosec net/socket.c:775 [inline]
 __sock_sendmsg net/socket.c:790 [inline]
 sock_write_iter+0x53a/0x5b0 net/socket.c:1241
 do_iter_readv_writev+0x6f7/0x930 fs/read_write.c:828
 vfs_writev+0x360/0xdd0 fs/read_write.c:1058
 do_writev+0x28a/0x340 fs/read_write.c:1104
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Memory state around the buggy address:
 ffff8880370c7f00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
 ffff8880370c7f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff8880370c8000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
                         ^
 ffff8880370c8080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
 ffff8880370c8100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
----------------
Code disassembly (best guess), 2 bytes skipped:
   0:	00 00                	add    %al,(%rax)
   2:	41 c7 87 20 0e 00 00 	movl   $0x0,0xe20(%r15)
   9:	00 00 00 00
   d:	e9 47 04 00 00       	jmp    0x459
  12:	49 8d 7f 48          	lea    0x48(%r15),%rdi
  16:	e8 30 b4 e6 09       	call   0x9e6b44b
  1b:	e8 1b a9 3c 00       	call   0x3ca93b
  20:	fb                   	sti
  21:	48 8d bb f8 16 00 00 	lea    0x16f8(%rbx),%rdi
* 28:	48 b8 00 00 00 00 00 	movabs $0xdffffc0000000000,%rax <-- trapping instruction
  2f:	fc ff df
  32:	48 89 fa             	mov    %rdi,%rdx
  35:	48 c1 ea 03          	shr    $0x3,%rdx
  39:	0f b6 04 02          	movzbl (%rdx,%rax,1),%eax
  3d:	84                   	.byte 0x84


---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.

syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.

If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title

If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)

If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report

If you want to undo deduplication, reply with:
#syz undup

^ permalink raw reply

* Re: RFC: symmetric SET_MODULE_EEPROM_BY_PAGE with i2c_address for non-SFF pages
From: Andrew Lunn @ 2026-07-15 15:58 UTC (permalink / raw)
  To: Ayoub Kaanich; +Cc: Russell King, netdev@vger.kernel.org, Dr. Lars Völker
In-Reply-To: <AS4PR08MB8021EAE9D0196FA87849F74DB7F82@AS4PR08MB8021.eurprd08.prod.outlook.com>

> The issue is that automotive PHYs have extra functions, not
> available in standard SFP 1000BASET/Fiber modules.

But this is not a fiber module. It is a copper module. How are you
setting the connector byte at 130? Do you have an RJ45?
SFF8024_CONNECTOR_RJ45? The other option is SFF8024_CONNECTOR_UNSPEC
and transceiver byte 134, bit 3 e1000_base_t True to mean twisted
pair.

> Namely:
> 
> * 1000BASET1 PHY needs to be assigned a role (either master or slave), a standard NIC have no concept of this.

Nothing special here:

ethtool -s devname [master-slave preferred-master|preferred-slave|forced-master|forced-slave]

Standard NICs do have this concept. I cannot say if NICs using
firmware actually implement this API, but any NIC using Linux to drive
the hardware probably does, and it is a standard part of phylib, and
802.3 registers in C22 and C45. There are helpers for PHY drivers to
set the bits.

> * The PHY have multiple operational speeds, for example 100BASET1 and 1000BASET1

Again, nothing special. The PHY driver should enumerate what the
capabilities of the PHY are, there are bits defined in 802.3 for this,
so nothing special needed. From that, the MAC can decide how it
configures itself. Again, nothing special.

> In order to set those configurations, we need to send an I2C command
> to the SFP module; to inform it in which mode the user wants it to
> work (the entire config is just few bytes of data)

How are you intercepting the ethtool netlink messages at the top of
the MAC? You need to intercept the ksetting_set/get calls.

Sorry, but for Mainline, you need to follow Mainline way of doing
things.

Linux is however open source. Feel free to fork Linux, make whatever
changes you want, and ship your fork to your customers.

	Andrew

^ permalink raw reply

* Re: [RFC PATCH 01/10] net: stmmac: move XPCS lifetime management to platform drivers
From: Andrew Lunn @ 2026-07-15 16:09 UTC (permalink / raw)
  To: Coia Prant
  Cc: Maxime Chevallier, kuba, davem, edumazet, pabeni, andrew+netdev,
	robh, krzk+dt, heiko, netdev, linux-rockchip, devicetree,
	linux-arm-kernel, linux-phy, Christian Marangi
In-Reply-To: <CALj3r0gWyfHHp0S4=71M1-TjgHKcgGax4Lr0DzPi37Q69-iCwA@mail.gmail.com>

> I actually spent all night debugging call traces caused by the current
> stmmac PCS lifetime management, and it was not a pleasant experience.
> The code feels like accumulated technical debt that should be cleaned
> up.

Russell King will strongly agree with you, if he was around. The
phylink integration into stmmac causes a lot of pain, and he wanted to
rip it all out and start again. But doing that without breaking stuff
is going to be interesting.

If you feel up to it, have a go. But take a good look around first,
make sure you understand the different variant, internal vs external
PCS.

	Andrew

^ permalink raw reply

* Re: [PATCH] sctp: diag: fix uninitialized stack leak via INET_DIAG_LOCALS/PEERS
From: Xin Long @ 2026-07-15 16:15 UTC (permalink / raw)
  To: MingXuan
  Cc: Marcelo Ricardo Leitner, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, linux-sctp, netdev,
	linux-kernel, stable
In-Reply-To: <20260715033536.64963-1-omeux327@gmail.com>

On Tue, Jul 14, 2026 at 11:35 PM MingXuan <omeux327@gmail.com> wrote:
>
> inet_diag_msg_sctpladdrs_fill() copies sizeof(union sctp_addr) (28 bytes,
> the size of sockaddr_in6) from each sctp_sockaddr_entry.a into the netlink
> INET_DIAG_LOCALS attribute and then only zeroes the bytes from offset 28 to
> sizeof(sockaddr_storage).  The same pattern is used by
> inet_diag_msg_sctpaddrs_fill() for INET_DIAG_PEERS.
>
> The IPv4 address-filling helpers sctp_v4_from_addr_param() and
> sctp_v4_from_skb() only initialize the sockaddr_in portion (16 bytes) of the
> union sctp_addr; the trailing 12 bytes (offset 16..27, the sockaddr_in6-only
> region) are left uninitialized.  Those bytes are propagated verbatim through
> sctp_add_bind_addr() (which copies sizeof(union sctp_addr)=28 bytes) and then
> copied straight to userspace by the diag fill functions, leaking 12 bytes of
> kernel stack residue per local/peer address to any process that can issue a
> SOCK_DIAG_BY_FAMILY dump for IPPROTO_SCTP.
>
> Fix it by computing the actually-initialized length of the address from its
> sa_family (struct sockaddr_in for AF_INET, the whole union otherwise) and
> copying only that many bytes into an already-zeroed sockaddr_storage slot, so
> the uninitialized tail is never read and never reaches userspace.
>
> Fixes: 8f840e47f190cbe61a96945c13e9551048d42cef ("sctp: add the sctp_diag.c file")
> Cc: stable@vger.kernel.org
> Signed-off-by: MingXuan <omeux327@gmail.com>
> ---
>  net/sctp/diag.c | 17 ++++++++++++-----
>  1 file changed, 12 insertions(+), 5 deletions(-)
>
> diff --git a/net/sctp/diag.c b/net/sctp/diag.c
> index d758f5c3e06e..12557e924cc2 100644
> --- a/net/sctp/diag.c
> +++ b/net/sctp/diag.c
> @@ -85,8 +85,12 @@ static int inet_diag_msg_sctpladdrs_fill(struct sk_buff *skb,
>         info = nla_data(attr);
>         rcu_read_lock();
>         list_for_each_entry_rcu(laddr, address_list, list) {
> -               memcpy(info, &laddr->a, sizeof(laddr->a));
> -               memset(info + sizeof(laddr->a), 0, addrlen - sizeof(laddr->a));
> +               size_t addr_len = laddr->a.sa.sa_family == AF_INET ?
> +                                 sizeof(struct sockaddr_in) :
> +                                 sizeof(struct sockaddr_in6);
> +
> +               memset(info, 0, addrlen);
> +               memcpy(info, &laddr->a, addr_len);
>                 info += addrlen;
>
>                 if (!--addrcnt)
> @@ -113,9 +117,12 @@ static int inet_diag_msg_sctpaddrs_fill(struct sk_buff *skb,
>         info = nla_data(attr);
>         list_for_each_entry(from, &asoc->peer.transport_addr_list,
>                             transports) {
> -               memcpy(info, &from->ipaddr, sizeof(from->ipaddr));
> -               memset(info + sizeof(from->ipaddr), 0,
> -                      addrlen - sizeof(from->ipaddr));
> +               size_t addr_len = from->ipaddr.sa.sa_family == AF_INET ?
> +                                 sizeof(struct sockaddr_in) :
> +                                 sizeof(struct sockaddr_in6);
> +
> +               memset(info, 0, addrlen);
> +               memcpy(info, &from->ipaddr, addr_len);
>                 info += addrlen;
>         }
>
> --
> 2.50.1 (Apple Git-155)
>
Acked-by: Xin Long <lucien.xin@gmail.com>

^ permalink raw reply

* Re: [RFC PATCH 07/10] net: pcs: xpcs: add Rockchip RK3568 platform glue driver
From: Andrew Lunn @ 2026-07-15 16:18 UTC (permalink / raw)
  To: Coia Prant
  Cc: Maxime Chevallier, kuba, davem, edumazet, pabeni, andrew+netdev,
	robh, krzk+dt, heiko, netdev, linux-rockchip, devicetree,
	linux-arm-kernel, linux-phy
In-Reply-To: <CALj3r0h4cFd-tp9-UVxihL9-a1W7_4nvnKoXW_cP5WECKc66Hw@mail.gmail.com>

> This is intentional. The SerDes is attached to the XPCS node because
> on RK3568, a single SerDes serves all four XPCS MII ports in QSGMII
> mode.

This is not the only device supporting QSGMII, and so the issues you
are addressing should be common to many QSGMII implementations.

Please take a step back. What would a generic solution look like?

Can the reference counting be placed into the core somewhere? The
common clock framework allows a clock to be enabled and disabled by
multiple consumers, and the core clock code does the reference
counting, only calling into the clock driver when state change is
actually needed. Maybe the generic PHY core needs something similar?

	Andrew

^ permalink raw reply

* Re: [PATCH net-next] net: macb: add support for 1000BASE-X autonegotiation to PCS
From: Charles Perry @ 2026-07-15 16:29 UTC (permalink / raw)
  To: Nathan Whitehorn; +Cc: netdev, charles.perry
In-Reply-To: <20260714200904.70428-1-nwhitehorn@pa.msu.edu>

On Tue, Jul 14, 2026 at 04:09:04PM -0400, Nathan Whitehorn wrote:
> The current PCS code unconditionally uses SGMII autonegotiation, though
> the hardware supports both SGMII and 1000BASE-X modes. Decouple the
> choice of PCS enablement from use of the SGMII mode when running at
> gigabit rates and announce to phylink that 1000BASE-X is a supported
> operating mode. This enables direct attachment of the PCS to e.g. an
> SFP.
> 
> Tested and developed on Microchip Polarfire SoC hardware.
> 
> Signed-off-by: Nathan Whitehorn <nwhitehorn@pa.msu.edu>
> ---
>  drivers/net/ethernet/cadence/macb_main.c | 31 ++++++++++++++++++------
>  1 file changed, 24 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
> index d394f1f43b68..284a3b03f8c7 100644
> --- a/drivers/net/ethernet/cadence/macb_main.c
> +++ b/drivers/net/ethernet/cadence/macb_main.c
> @@ -583,7 +583,13 @@ static void macb_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
>  
>  static void macb_pcs_an_restart(struct phylink_pcs *pcs)
>  {
> -	/* Not supported */
> +	struct macb *bp = container_of(pcs, struct macb, phylink_sgmii_pcs);
> +	u32 old, new;
> +
> +	old = gem_readl(bp, PCSCNTRL);
> +	new = old | BMCR_ANRESTART;
> +	if (old != new)
> +		gem_writel(bp, PCSCNTRL, new);

This bit is self-clearing so I don't think the old != new check is
required.

Also, can you comment on why AN restart is needed for this patch? Is it a
requirement for 1000base-x of is it because we now have sgmii and
1000base-x?

>  }
>  
>  static int macb_pcs_config(struct phylink_pcs *pcs,
> @@ -750,7 +756,9 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode,
>  		ctrl &= ~(GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL));
>  		ncr &= ~GEM_BIT(ENABLE_HS_MAC);
>  
> -		if (state->interface == PHY_INTERFACE_MODE_SGMII) {
> +		if (state->interface == PHY_INTERFACE_MODE_1000BASEX) {
> +			ctrl |= GEM_BIT(PCSSEL);
> +		} else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
>  			ctrl |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL);
>  		} else if (state->interface == PHY_INTERFACE_MODE_10GBASER) {
>  			ctrl |= GEM_BIT(PCSSEL);
> @@ -957,7 +965,8 @@ static struct phylink_pcs *macb_mac_select_pcs(struct phylink_config *config,
>  
>  	if (interface == PHY_INTERFACE_MODE_10GBASER)
>  		return &bp->phylink_usx_pcs;
> -	else if (interface == PHY_INTERFACE_MODE_SGMII)
> +	else if (interface == PHY_INTERFACE_MODE_1000BASEX ||
> +	         interface == PHY_INTERFACE_MODE_SGMII)
>  		return &bp->phylink_sgmii_pcs;
>  	else
>  		return NULL;
> @@ -1025,13 +1034,15 @@ static int macb_mii_probe(struct net_device *dev)
>  	struct macb *bp = netdev_priv(dev);
>  
>  	bp->phylink_sgmii_pcs.ops = &macb_phylink_pcs_ops;
> +	bp->phylink_sgmii_pcs.poll = true;

Why is this needed for 1000base-x?

Thanks,
Charles

^ permalink raw reply

* Re: [PATCH net-next v4 00/15][pull request] Introduce iXD driver
From: Tony Nguyen @ 2026-07-15 16:32 UTC (permalink / raw)
  To: Larysa Zaremba
  Cc: davem, kuba, pabeni, edumazet, andrew+netdev, netdev,
	przemyslaw.kitszel, aleksander.lobakin, sridhar.samudrala,
	michal.swiatkowski, maciej.fijalkowski, emil.s.tantilov,
	madhu.chittim, joshua.a.hay, jacob.e.keller,
	jayaprakash.shanmugam, jiri, horms, corbet, richardcochran,
	linux-doc
In-Reply-To: <alUuiqh_VkGhQp7T@soc-5CG4396X81.clients.intel.com>



On 7/13/2026 11:29 AM, Larysa Zaremba wrote:
> I have addressed Sashiko's feedback for each patch.
> 
> Those are the only 2 important changes that came out of that.
> Please, notice that the first one is not a functional regression, but a memory
> usage issue and I tested the change.

...
> Other changes are of much lesser importance. Look at particular patches for
> details on nice-to-haves and false positives.

I'll be submitting a v5 with these changes squashed in.

Thanks,
Tony

pw-bot: changes-requested



^ permalink raw reply

* Re: [PATCH net-next] net: macb: add support for 1000BASE-X autonegotiation to PCS
From: Nathan Whitehorn @ 2026-07-15 16:35 UTC (permalink / raw)
  To: Charles Perry; +Cc: netdev
In-Reply-To: <ale1VbrKF/ls5Zt4@bby-cbu-swbuild03.eng.microchip.com>



On 7/15/26 12:29, Charles Perry wrote:
> On Tue, Jul 14, 2026 at 04:09:04PM -0400, Nathan Whitehorn wrote:
>> The current PCS code unconditionally uses SGMII autonegotiation, though
>> the hardware supports both SGMII and 1000BASE-X modes. Decouple the
>> choice of PCS enablement from use of the SGMII mode when running at
>> gigabit rates and announce to phylink that 1000BASE-X is a supported
>> operating mode. This enables direct attachment of the PCS to e.g. an
>> SFP.
>>
>> Tested and developed on Microchip Polarfire SoC hardware.
>>
>> Signed-off-by: Nathan Whitehorn <nwhitehorn@pa.msu.edu>
>> ---
>>   drivers/net/ethernet/cadence/macb_main.c | 31 ++++++++++++++++++------
>>   1 file changed, 24 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
>> index d394f1f43b68..284a3b03f8c7 100644
>> --- a/drivers/net/ethernet/cadence/macb_main.c
>> +++ b/drivers/net/ethernet/cadence/macb_main.c
>> @@ -583,7 +583,13 @@ static void macb_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
>>   
>>   static void macb_pcs_an_restart(struct phylink_pcs *pcs)
>>   {
>> -	/* Not supported */
>> +	struct macb *bp = container_of(pcs, struct macb, phylink_sgmii_pcs);
>> +	u32 old, new;
>> +
>> +	old = gem_readl(bp, PCSCNTRL);
>> +	new = old | BMCR_ANRESTART;
>> +	if (old != new)
>> +		gem_writel(bp, PCSCNTRL, new);
> This bit is self-clearing so I don't think the old != new check is
> required.

Ah, OK, good to know.

> Also, can you comment on why AN restart is needed for this patch? Is it a
> requirement for 1000base-x of is it because we now have sgmii and
> 1000base-x?

Our equipment works well enough without this, and it is just here for 
completeness rather than an actual need. I am happy to drop it from the 
patch if you prefer.

>
>>   }
>>   
>>   static int macb_pcs_config(struct phylink_pcs *pcs,
>> @@ -750,7 +756,9 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode,
>>   		ctrl &= ~(GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL));
>>   		ncr &= ~GEM_BIT(ENABLE_HS_MAC);
>>   
>> -		if (state->interface == PHY_INTERFACE_MODE_SGMII) {
>> +		if (state->interface == PHY_INTERFACE_MODE_1000BASEX) {
>> +			ctrl |= GEM_BIT(PCSSEL);
>> +		} else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
>>   			ctrl |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL);
>>   		} else if (state->interface == PHY_INTERFACE_MODE_10GBASER) {
>>   			ctrl |= GEM_BIT(PCSSEL);
>> @@ -957,7 +965,8 @@ static struct phylink_pcs *macb_mac_select_pcs(struct phylink_config *config,
>>   
>>   	if (interface == PHY_INTERFACE_MODE_10GBASER)
>>   		return &bp->phylink_usx_pcs;
>> -	else if (interface == PHY_INTERFACE_MODE_SGMII)
>> +	else if (interface == PHY_INTERFACE_MODE_1000BASEX ||
>> +	         interface == PHY_INTERFACE_MODE_SGMII)
>>   		return &bp->phylink_sgmii_pcs;
>>   	else
>>   		return NULL;
>> @@ -1025,13 +1034,15 @@ static int macb_mii_probe(struct net_device *dev)
>>   	struct macb *bp = netdev_priv(dev);
>>   
>>   	bp->phylink_sgmii_pcs.ops = &macb_phylink_pcs_ops;
>> +	bp->phylink_sgmii_pcs.poll = true;
> Why is this needed for 1000base-x?

Without this, the hardware does not detect link state changes after 
initialization. I am assuming that in the normal SGMII use-case with an 
external onboard PHY etc., those state changes either don't have or are 
signaled by monitoring the PHY, so that support for this wasn't needed. 
With removable modules, fiber, etc., state changes are suddenly both 
possible and otherwise unsignaled. It is possible -- likely, actually -- 
that there is some better way to do this by properly monitoring an 
interrupt somewhere, but I am working without documentation so am not 
sure how to implement that and was trying to keep the patch short. 
Polling worked well enough as an alternative.

Thank you for looking through this!
-Nathan

>
> Thanks,
> Charles

-- 
Nathan Whitehorn (he/him)
Associate Professor
Department of Physics and Astronomy
Michigan State University
Biomedical and Physical Sciences 3225
East Lansing, MI 48824
(517) 884-5563



^ permalink raw reply

* Re: [PATCH net] net: hip04: fix RX buffer leak on build_skb failure
From: Jacob Keller @ 2026-07-15 16:51 UTC (permalink / raw)
  To: Fan Wu
  Cc: Fan Wu, netdev, przemyslaw.kitszel, Simon Horman, shenjian15,
	salil.mehta, dingtianhong, andrew+netdev, davem, edumazet, kuba,
	pabeni, linux-kernel, stable
In-Reply-To: <190A7669-E0EC-4257-B65E-817FFA1ACCDC@zju.edu.cn>

On 7/15/2026 12:30 AM, Fan Wu wrote:
> Hi Jacob,
> 
> Thanks for the review. I agree this is a rare allocation-failure path.
> 
> The leak is nevertheless deterministic from the ownership contract and
> the driver's control flow. __build_skb() documents that, on failure, it
> returns NULL without freeing the caller-provided data.
> 
> hip04_rx_poll() currently jumps to refill before dma_unmap_single().
> Refill then replaces rx_buf[rx_head] and rx_phys[rx_head], so the old
> fragment and its streaming DMA mapping are no longer reachable. The
> stop and free-ring paths only operate on entries still referenced by
> those arrays.
> 
> The fix leaves the slot, descriptor, rx_head, and rx_cnt_remaining
> unchanged, and returns budget. This tells NAPI that RX work remains
> outstanding and retries build_skb() using the same buffer. The additional
> skb_free_frag() handles the separate case where a replacement fragment
> was allocated but dma_map_single() failed before it was installed.
> 
> I hope this clarifies why the failure path is a deterministic leak even
> though I do not have a dynamic reproduction on HIP04 hardware.
> 

Thanks,
Jake

> Thanks,
> Fan
> 


^ permalink raw reply

* Re: [PATCH net] net: hip04: fix RX buffer leak on build_skb failure
From: Jacob Keller @ 2026-07-15 16:52 UTC (permalink / raw)
  To: Fan Wu, netdev
  Cc: przemyslaw.kitszel, horms, shenjian15, salil.mehta, dingtianhong,
	andrew+netdev, davem, edumazet, kuba, pabeni, linux-kernel,
	stable
In-Reply-To: <20260712142729.2057636-1-fanwu01@zju.edu.cn>

On 7/12/2026 7:27 AM, Fan Wu wrote:
> When build_skb() fails in hip04_rx_poll(), the driver jumps to the
> refill path without releasing the current RX buffer and its DMA mapping.
> Installing a replacement buffer then overwrites the slot references and
> leaks both resources.
> 
> Keep the current slot intact and return budget so NAPI retries the same
> buffer.  Also free a newly allocated RX fragment when dma_map_single()
> fails.
> 
> This issue was found by an in-house static analysis tool.
> 
> Fixes: 701a0fd52318 ("hip04_eth: fix missing error handle for build_skb failed")
> Cc: stable@vger.kernel.org
> Assisted-by: Codex:gpt-5.5
> Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
> ---
> 

Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>

^ permalink raw reply

* Re: [PATCH net-next] net: macb: add support for 1000BASE-X autonegotiation to PCS
From: Andrew Lunn @ 2026-07-15 17:03 UTC (permalink / raw)
  To: Nathan Whitehorn; +Cc: Charles Perry, netdev
In-Reply-To: <ab0883e3-fc15-41f7-a851-07bc4697aa42@pa.msu.edu>

> > > @@ -1025,13 +1034,15 @@ static int macb_mii_probe(struct net_device *dev)
> > >   	struct macb *bp = netdev_priv(dev);
> > >   	bp->phylink_sgmii_pcs.ops = &macb_phylink_pcs_ops;
> > > +	bp->phylink_sgmii_pcs.poll = true;
> > Why is this needed for 1000base-x?
> 
> Without this, the hardware does not detect link state changes after
> initialization. I am assuming that in the normal SGMII use-case with an
> external onboard PHY etc., those state changes either don't have or are
> signaled by monitoring the PHY, so that support for this wasn't needed. With
> removable modules, fiber, etc., state changes are suddenly both possible and
> otherwise unsignaled. It is possible -- likely, actually -- that there is
> some better way to do this by properly monitoring an interrupt somewhere,
> but I am working without documentation so am not sure how to implement that
> and was trying to keep the patch short. Polling worked well enough as an
> alternative.

This does not sound quiet correct. A PCS has two options.

1) As you say, it implements interrupts, and calls
phylink_pcs_change() on each interrupt to let phylink know something
happened.

2) You set poll true, and phylink will poll the PCS every second.

A PHY generally only reports on its media side changing state. I also
don't think phylink will poll the PCS if the PHY changes state.

I don't see anywhere macb calls phylink_pcs_change(), so polling does
seem correct.

Please break this patch up. You can add polling in a patch of its own,
with the commit message explaining why it is needed.

     Andrew

^ permalink raw reply

* Re: [PATCH iwl] ice: acquire NVM lock around each flash read
From: Tony Nguyen @ 2026-07-15 17:04 UTC (permalink / raw)
  To: Robert Malz
  Cc: Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Alexander Lobakin, Jacob Keller,
	Jesse Brandeburg, intel-wired-lan, netdev, linux-kernel
In-Reply-To: <CADcc-bxLMg1NujF+jE2rOXGc38quv_XqZfv5K+yGQFfhc03avA@mail.gmail.com>



On 7/15/2026 3:22 AM, Robert Malz wrote:

...

>>> -             if (status)
>>> +             if (status) {
>>> +                     /* ice_release_nvm() issues an AQ command that would
>>> +                      * overwrite sq_last_status, which some callers
>>> +                      * inspect after a failed read. Preserve the read's
>>> +                      * AQ error across the release.
>>> +                      */
>>> +                     aq_err = hw->adminq.sq_last_status;
>>> +
>>> +                     ice_release_nvm(hw);
>>> +                     hw->adminq.sq_last_status = aq_err;
>>
>> Does restoring hw->adminq.sq_last_status here without holding the Admin
>> Queue
>> lock risk overwriting the status of a concurrent AdminQ command?
>> If another thread acquires the lock and sends a command immediately after
>> ice_release_nvm(hw), this lockless write could corrupt its error status.
>>
>> [TN] I don't believe the AQ lock will work as desired we can't guarantee
>> that we will have the lock directly following the release in order to
>> restore the AQ error. Similar to the NVM lock issue, I think this is a
>> small window but wanted to bring this here in case others had
>> thoughts/comments on this.
> 
> [RM] Agree, this can cause issues. We can't drop sq_last_status as
> ice_discover_flash_size depends on it.
> Proposed fix: drop the save/restore sq_last_status and propagate the
> read AQ error through a real return path instead of the shared global.
> I could add optional enum libie_aq_err *read_aq_err out param to
> ice_read_flat_nvm(), capture sq_last_status before the
> ice_release_nvm, and have ice_discover_flash_size() test that instead.
> The log-only callers (devlink/ethtool) don't care about it.
> Let me know what you think about it.
This sounds good to me.

Thanks,
Tony

^ permalink raw reply

* Re: [PATCH net] cxgb4: consider EtherType when validating filter priority ordering
From: Simon Horman @ 2026-07-15 17:21 UTC (permalink / raw)
  To: harshitha.vr
  Cc: Simon Horman, netdev, davem, kuba, edumazet, pabeni,
	andrew+netdev, bharat
In-Reply-To: <20260708131650.14747-1-harshitha.vr@chelsio.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
--- Patch [1]: [PATCH net] cxgb4: consider EtherType when validating filter priority ordering ---
commit eeb9ac7dfbda27ed06d0d26da6184a96f69d0da6
Author: Harshita V Rajput <harshitha.vr@chelsio.com>

cxgb4: consider EtherType when validating filter priority ordering

This commit updates cxgb4_filter_prio_in_range() and cxgb4_get_free_ftid()
to consider EtherType during priority validation. It allows adjacent filters
with different EtherTypes to be inserted regardless of their priority order,
as they would not match the same packets.

> diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
> index 657d96b9e2f66..8462455d9330f 100644
> --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
> +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c

[ ... ]

> @@ -536,15 +536,24 @@ static bool cxgb4_filter_prio_in_range(struct tid_info *t, u32 idx, u8 nslots,
>  	if (!prev_fe->fs.type)
>  		prev_fe = &prev_tab[prev_ftid];
>  
> -	if ((prev_fe->valid && prev_fe->fs.tc_prio > prio) ||
> -	    (next_fe->valid && next_fe->fs.tc_prio < prio))
> +	/* Filters with different EtherTypes can never match the same
> +	 * packet, so skip the priority check between them.
> +	 */
> +	if (prev_fe->valid && prev_fe->fs.tc_prio > prio &&

[Severity: High]
This is a pre-existing issue, but by only checking the valid flag here,
could concurrent filter insertions bypass the priority checks entirely?

When a filter is allocated, its bit is set in the hardware map but its valid
flag remains 0 until the firmware replies asynchronously (where filter_rpl
sets f->valid = 1).

If a concurrent TC filter insertion occurs (e.g., via unlocked TC or parallel
shared block updates across different ports on the same adapter),
cxgb4_get_free_ftid() finds the pending rule as a neighbor using
find_last_bit() or find_next_bit(), but skips the priority check because
prev_fe->valid or next_fe->valid is 0.

Should this logic also check the f->pending flag to prevent a second thread
from inserting a conflicting filter at an index that violates priority
ordering?

> +	    (!prev_fe->fs.mask.ethtype || !ethtype ||
> +	     prev_fe->fs.val.ethtype == ethtype))
> +		return false;

[Severity: High]
Does suppressing the priority check against immediate neighbors with a
different ethtype break the global sorting invariant of the TCAM filter array?

Since cxgb4_filter_prio_in_range() only validates against immediate allocated
neighbors, skipping the check allows out-of-order insertions. Once the table
contains interleaved EtherTypes, could this lead to hardware priority
inversions?

For example:
Index 0: IPv4 prio 5
Index 1: IPv6 prio 2

If a new IPv4 prio 3 rule is inserted at Index 2, it only checks against
Index 1. Since Index 1 is IPv6, the check is skipped. This inserts the prio 3
IPv4 rule after the prio 5 IPv4 rule, resulting in priority inversion since
hardware evaluates TCAM filters sequentially by index.

Additionally, does this broken transitivity affect cxgb4_get_free_ftid()
when inserting into the HASH region?

cxgb4_get_free_ftid() incorrectly assumes that find_last_bit() in the
HPFILTER region yields the rule with the highest tc_prio:

cxgb4_get_free_ftid() {
	...
	ftid = find_last_bit(t->hpftid_bmap, t->nhpftids);
	if (ftid < t->nhpftids) {
		f = &t->hpftid_tab[ftid];
		if (f->valid && tc_prio < f->fs.tc_prio)
			break;
	}
	...
}

With interleaved priorities, this assumption no longer holds.

> +
> +	if (next_fe->valid && next_fe->fs.tc_prio < prio &&
> +	    (!next_fe->fs.mask.ethtype || !ethtype ||
> +	     next_fe->fs.val.ethtype == ethtype))
>  		return false;
>  
>  	return true;

^ permalink raw reply

* Re: [PATCH net] cxgb4: consider EtherType when validating filter priority ordering
From: Simon Horman @ 2026-07-15 17:23 UTC (permalink / raw)
  To: harshitha.vr
  Cc: Simon Horman, netdev, davem, kuba, edumazet, pabeni,
	andrew+netdev, bharat
In-Reply-To: <20260708131650.14747-1-harshitha.vr@chelsio.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
https://netdev-ai.bots.linux.dev/sashiko/
---
cxgb4: consider EtherType when validating filter priority ordering

The commit relaxes cxgb4_filter_prio_in_range() so that neighbouring
filters with a different matched EtherType no longer block insertion of
a new filter. Both the callee and cxgb4_get_free_ftid() gain a new
ethtype argument, and callers in the tc flower, matchall, and u32 paths
are updated accordingly.

> diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
> index 657d96b9e2f6..8462455d9330 100644
> --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
> +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c

[ ... ]

> @@ -536,15 +536,24 @@ static bool cxgb4_filter_prio_in_range(struct tid_info *t, u32 idx, u8 nslots,
>  	if (!prev_fe->fs.type)
>  		prev_fe = &prev_tab[prev_ftid];
>  
> -	if ((prev_fe->valid && prev_fe->fs.tc_prio > prio) ||
> -	    (next_fe->valid && next_fe->fs.tc_prio < prio))
> +	/* Filters with different EtherTypes can never match the same
> +	 * packet, so skip the priority check between them.
> +	 */
> +	if (prev_fe->valid && prev_fe->fs.tc_prio > prio &&
> +	    (!prev_fe->fs.mask.ethtype || !ethtype ||
> +	     prev_fe->fs.val.ethtype == ethtype))
> +		return false;
> +
> +	if (next_fe->valid && next_fe->fs.tc_prio < prio &&
> +	    (!next_fe->fs.mask.ethtype || !ethtype ||
> +	     next_fe->fs.val.ethtype == ethtype))
>  		return false;

[Low]
Does this comparison handle neighbouring filters that use a partial
ethtype mask?

The check treats two filters as targeting different EtherTypes when
prev_fe->fs.val.ethtype != ethtype, but it consults only the neighbour's
mask and never the new filter's mask. If a neighbour is inserted with,
for example, val.ethtype = 0x0801 and mask.ethtype = 0xFFFF, and the new
filter is inserted with val.ethtype = 0x0800 and (a hypothetically
propagated) mask.ethtype = 0xFF00, both filters can in principle match a
packet with EtherType 0x0801, yet the equality test 0x0801 == 0x0800
evaluates false and permits an out-of-order insertion.

A masked form such as:

    ((prev_fe->fs.val.ethtype ^ new_val) &
     prev_fe->fs.mask.ethtype & new_mask) == 0

would cover partial masks, but the current cxgb4_get_free_ftid() API
only carries the new filter's ethtype value and not its mask, so a
masked check cannot be expressed. Was the partial-mask case considered,
and should the new argument include the mask as well?

> +		return false;
> +
>  	return true;
>  }
>  
>  int cxgb4_get_free_ftid(struct net_device *dev, u8 family, bool hash_en,
> -			u32 tc_prio)
> +			u32 tc_prio, u32 ethtype)
>  {

[ ... ]

> diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
> index 3307e5042681..07d9170cdb6f 100644
> --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
> +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
> @@ -885,8 +885,8 @@ int cxgb4_flow_rule_replace(struct net_device *dev, struct flow_rule *rule,
>  	 * rule. Only insert rule if its prio doesn't conflict with
>  	 * existing rules.
>  	 */
> -	fidx = cxgb4_get_free_ftid(dev, inet_family, fs->hash,
> -				   tc_prio);
> +	fidx = cxgb4_get_free_ftid(dev, inet_family, fs->hash, tc_prio,
> +				   fs->mask.ethtype ? fs->val.ethtype : 0);

Related to the question above: the caller here discards fs->mask.ethtype
before it reaches cxgb4_filter_prio_in_range(), so a new filter with a
partial ethtype mask is compared as if its value were fully specified.
Is that intentional, or should the mask be plumbed through as well?

^ permalink raw reply

* Re: [PATCH net] nfp: Check resource mutex allocation
From: Simon Horman @ 2026-07-15 17:32 UTC (permalink / raw)
  To: Ruoyu Wang
  Cc: kuba, andrew+netdev, davem, edumazet, pabeni, oss-drivers, netdev,
	linux-kernel
In-Reply-To: <20260708143408.3168425-1-ruoyuw560@gmail.com>

On Wed, Jul 08, 2026 at 10:34:08PM +0800, Ruoyu Wang wrote:
> nfp_cpp_resource_find() allocates a CPP mutex handle for the matching
> resource-table entry and then reports success.  nfp_resource_try_acquire()
> immediately passes that handle to nfp_cpp_mutex_trylock().
> 
> However, nfp_cpp_mutex_alloc() returns NULL on failure.  If that happens
> for a matching table entry, the resource lookup still returns success and
> the following trylock dereferences a NULL mutex pointer while opening the
> resource.
> 
> nfp_resource_acquire() already treats failure to allocate the table mutex
> as -ENOMEM.  Do the same for the resource mutex and fail the lookup before
> publishing the rest of the resource handle.
> 
> This issue was found by a static analysis checker and confirmed by
> manual source review.
> 
> Fixes: f01a2161577d ("nfp: add support for resources")
> Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>

Reviewed-by: Simon Horman <horms@kernel.org>


^ permalink raw reply

* Re: [PATCH net] pds_core: fix auxiliary device add/del races
From: Eric Joyner @ 2026-07-15 17:33 UTC (permalink / raw)
  To: Pavan Chebbi, Nikhil P. Rao
  Cc: netdev, kuba, brett.creeley, andrew+netdev, davem, edumazet,
	pabeni
In-Reply-To: <CALs4sv1dBCLDtWBrYLizmKH=E4+ZNgNCmyubqd46Yk9OHodrbw@mail.gmail.com>

On 7/15/2026 6:23 AM, Pavan Chebbi wrote:
> On Wed, Jul 15, 2026 at 2:38 AM Nikhil P. Rao <nikhil.rao@amd.com> wrote:
>>
>> Two paths add or delete the same slot (pf->vfs[vf_id].padev): a VF's
>> pdsc_reset_done() and the PF's devlink enable_vnet/disable_vnet handler.
>> They serialize on config_lock, but neither guards the slot under it
>> correctly.
>>
>> add() registers and stores a new auxiliary device without first checking
>> the slot, so a second add of an already-populated slot leaks the first
>> device. del() makes that check outside config_lock, so two concurrent
>> dels can both pass it; the first clears the slot, and the second
>> dereferences a NULL pointer.
>>
>> Check and update the slot under config_lock in both paths.
>>
>> Fixes: b699bdc720c0 ("pds_core: specify auxiliary_device to be created")
>> Reported-by: sashiko-bot@kernel.org # Running on a local machine
>> Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
>> Reviewed-by: Brett Creeley <brett.creeley@amd.com>
>> ---
>>  drivers/net/ethernet/amd/pds_core/auxbus.c | 17 ++++++++++++++---
>>  1 file changed, 14 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/amd/pds_core/auxbus.c b/drivers/net/ethernet/amd/pds_core/auxbus.c
>> index 73b3481220b1..3acafe10a6d5 100644
>> --- a/drivers/net/ethernet/amd/pds_core/auxbus.c
>> +++ b/drivers/net/ethernet/amd/pds_core/auxbus.c
>> @@ -177,17 +177,21 @@ void pdsc_auxbus_dev_del(struct pdsc *cf, struct pdsc *pf,
>>  {
>>         struct pds_auxiliary_dev *padev;
>>
>> -       if (!*pd_ptr)
>> -               return;
>> -
>>         mutex_lock(&pf->config_lock);
>>
>> +       /* A concurrent del may have already torn this device down and
>> +        * cleared it.
>> +        */

I don't know if this one is AI-generated, but it looked good to me; I appreciate
the comment that it is specifically supposed to guard against a scenario. Though
arguably it's a common enough pattern that it may not need it.

>>         padev = *pd_ptr;
>> +       if (!padev)
>> +               goto out_unlock;
>> +
>>         pds_client_unregister(pf, padev->client_id);
>>         auxiliary_device_delete(&padev->aux_dev);
>>         auxiliary_device_uninit(&padev->aux_dev);
>>         *pd_ptr = NULL;
>>
>> +out_unlock:
>>         mutex_unlock(&pf->config_lock);
>>  }
>>
>> @@ -210,6 +214,13 @@ int pdsc_auxbus_dev_add(struct pdsc *cf, struct pdsc *pf,
>>
>>         mutex_lock(&pf->config_lock);
>>
>> +       /* Nothing to do if the aux device is already present.  This also
>> +        * guards against a second add overwriting *pd_ptr and leaking the
>> +        * first, symmetric with the check in pdsc_auxbus_dev_del().
>> +        */
> 
> Are these AI-generated-appearing comments really necessary?
> Anyway, it is your driver.
> Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>

This is a bit flippant, but if the kernel is going to send all of our patches
through an AI, we can at least be nice to the AI the mailing list uses and
reduce its credit usage by embedding comments from our AI in it so it doesn't
have to figure things out every time. :p

But I get your point, this one in particular seems gratuitous, and the language
it uses is recognizably AI-generated which is off-putting to me, too, though
maybe I'm also just sensitive to them. I think we review these comments with the
view that unless they're wrong, they're harmless to leave in; but instead we
should view these added comments more critically.

- Eric

> 
>> +       if (*pd_ptr)
>> +               goto out_unlock;
>> +
>>         mask = BIT_ULL(PDSC_S_FW_DEAD) |
>>                BIT_ULL(PDSC_S_STOPPING_DRIVER);
>>         if (cf->state & mask) {
>> --
>> 2.43.0
>>
>>


^ permalink raw reply

* Re: [PATCH net-next] net: macb: add support for 1000BASE-X autonegotiation to PCS
From: Charles Perry @ 2026-07-15 17:52 UTC (permalink / raw)
  To: Nathan Whitehorn; +Cc: Charles Perry, netdev
In-Reply-To: <ab0883e3-fc15-41f7-a851-07bc4697aa42@pa.msu.edu>

On Wed, Jul 15, 2026 at 12:35:48PM -0400, Nathan Whitehorn wrote:
> 
> 
> On 7/15/26 12:29, Charles Perry wrote:
> > On Tue, Jul 14, 2026 at 04:09:04PM -0400, Nathan Whitehorn wrote:
> > > The current PCS code unconditionally uses SGMII autonegotiation, though
> > > the hardware supports both SGMII and 1000BASE-X modes. Decouple the
> > > choice of PCS enablement from use of the SGMII mode when running at
> > > gigabit rates and announce to phylink that 1000BASE-X is a supported
> > > operating mode. This enables direct attachment of the PCS to e.g. an
> > > SFP.
> > > 
> > > Tested and developed on Microchip Polarfire SoC hardware.
> > > 
> > > Signed-off-by: Nathan Whitehorn <nwhitehorn@pa.msu.edu>
> > > ---
> > >   drivers/net/ethernet/cadence/macb_main.c | 31 ++++++++++++++++++------
> > >   1 file changed, 24 insertions(+), 7 deletions(-)
> > > 
> > > diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
> > > index d394f1f43b68..284a3b03f8c7 100644
> > > --- a/drivers/net/ethernet/cadence/macb_main.c
> > > +++ b/drivers/net/ethernet/cadence/macb_main.c
> > > @@ -583,7 +583,13 @@ static void macb_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
> > >   static void macb_pcs_an_restart(struct phylink_pcs *pcs)
> > >   {
> > > -	/* Not supported */
> > > +	struct macb *bp = container_of(pcs, struct macb, phylink_sgmii_pcs);
> > > +	u32 old, new;
> > > +
> > > +	old = gem_readl(bp, PCSCNTRL);
> > > +	new = old | BMCR_ANRESTART;
> > > +	if (old != new)
> > > +		gem_writel(bp, PCSCNTRL, new);
> > This bit is self-clearing so I don't think the old != new check is
> > required.
> 
> Ah, OK, good to know.
> 
> > Also, can you comment on why AN restart is needed for this patch? Is it a
> > requirement for 1000base-x of is it because we now have sgmii and
> > 1000base-x?
> 
> Our equipment works well enough without this, and it is just here for
> completeness rather than an actual need. I am happy to drop it from the
> patch if you prefer.
> 

Well it does look like the ->pcs_an_restart() callback was made
specifically for 1000base-x because phylink_pcs_an_restart() in phylink.c
checks for phy_interface_mode_is_8023z():

https://elixir.bootlin.com/linux/v7.1.3/source/drivers/net/phy/phylink.c#L1028

Maybe you actually need this.

Thanks,
Charles


^ permalink raw reply

* [PATCH net-next v5 01/15] virtchnl: move virtchnl and virtchnl2 headers to 'include/linux/net/intel'
From: Tony Nguyen @ 2026-07-15 18:00 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Victor Raj, larysa.zaremba, przemyslaw.kitszel,
	aleksander.lobakin, sridhar.samudrala, michal.swiatkowski,
	maciej.fijalkowski, emil.s.tantilov, madhu.chittim, joshua.a.hay,
	jacob.e.keller, jayaprakash.shanmugam, jiri, horms, corbet,
	richardcochran, skhan, linux-doc, Samuel Salin
In-Reply-To: <20260715180042.1972010-1-anthony.l.nguyen@intel.com>

From: Victor Raj <victor.raj@intel.com>

virtchnl2 headers will be used by both idpf and ixd drivers, so they have
to be moved to an include directory. On top of that, it would be useful to
place all iavf headers together with other intel networking headers.

Move abovementioned intel header files into 'include/linux/net/intel'.

Suggested-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Signed-off-by: Victor Raj <victor.raj@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
---
 MAINTAINERS                                                   | 3 +--
 drivers/net/ethernet/intel/i40e/i40e.h                        | 2 +-
 drivers/net/ethernet/intel/i40e/i40e_common.c                 | 2 +-
 drivers/net/ethernet/intel/i40e/i40e_prototype.h              | 2 +-
 drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h            | 2 +-
 drivers/net/ethernet/intel/iavf/iavf.h                        | 2 +-
 drivers/net/ethernet/intel/iavf/iavf_common.c                 | 2 +-
 drivers/net/ethernet/intel/iavf/iavf_prototype.h              | 3 ++-
 drivers/net/ethernet/intel/iavf/iavf_types.h                  | 4 +---
 drivers/net/ethernet/intel/ice/ice.h                          | 2 +-
 drivers/net/ethernet/intel/ice/ice_common.h                   | 2 +-
 drivers/net/ethernet/intel/ice/ice_vf_lib.h                   | 2 +-
 drivers/net/ethernet/intel/ice/virt/virtchnl.h                | 2 +-
 drivers/net/ethernet/intel/idpf/idpf.h                        | 2 +-
 drivers/net/ethernet/intel/idpf/idpf_txrx.h                   | 2 +-
 drivers/net/ethernet/intel/idpf/idpf_virtchnl.h               | 2 +-
 include/linux/{avf => net/intel}/virtchnl.h                   | 0
 .../intel/idpf => include/linux/net/intel}/virtchnl2.h        | 0
 .../idpf => include/linux/net/intel}/virtchnl2_lan_desc.h     | 0
 19 files changed, 17 insertions(+), 19 deletions(-)
 rename include/linux/{avf => net/intel}/virtchnl.h (100%)
 rename {drivers/net/ethernet/intel/idpf => include/linux/net/intel}/virtchnl2.h (100%)
 rename {drivers/net/ethernet/intel/idpf => include/linux/net/intel}/virtchnl2_lan_desc.h (100%)

diff --git a/MAINTAINERS b/MAINTAINERS
index f3218abefd0c..5f7b638b49ae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13023,8 +13023,7 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue.git
 F:	Documentation/networking/device_drivers/ethernet/intel/
 F:	drivers/net/ethernet/intel/
 F:	drivers/net/ethernet/intel/*/
-F:	include/linux/avf/virtchnl.h
-F:	include/linux/net/intel/*/
+F:	include/linux/net/intel/
 
 INTEL ETHERNET PROTOCOL DRIVER FOR RDMA
 M:	Tatyana Nikolova <tatyana.e.nikolova@intel.com>
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 83e780919ac9..1b6a8fbaa648 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -8,8 +8,8 @@
 #include <linux/pci.h>
 #include <linux/ptp_clock_kernel.h>
 #include <linux/types.h>
-#include <linux/avf/virtchnl.h>
 #include <linux/net/intel/i40e_client.h>
+#include <linux/net/intel/virtchnl.h>
 #include <net/devlink.h>
 #include <net/pkt_cls.h>
 #include <net/udp_tunnel.h>
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index 59f5c1e810eb..8dadfef2c09f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -1,10 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright(c) 2013 - 2021 Intel Corporation. */
 
-#include <linux/avf/virtchnl.h>
 #include <linux/bitfield.h>
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
+#include <linux/net/intel/virtchnl.h>
 #include <linux/pci.h>
 #include "i40e_adminq_cmd.h"
 #include "i40e_devids.h"
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index 26bb7bffe361..e3d57550090e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -5,7 +5,7 @@
 #define _I40E_PROTOTYPE_H_
 
 #include <linux/ethtool.h>
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
 #include "i40e_debug.h"
 #include "i40e_type.h"
 
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
index f558b45725c8..4e119c0502f3 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
@@ -4,7 +4,7 @@
 #ifndef _I40E_VIRTCHNL_PF_H_
 #define _I40E_VIRTCHNL_PF_H_
 
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
 #include <linux/netdevice.h>
 #include "i40e_type.h"
 
diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h
index 050f8241ef5e..dc31202b2a94 100644
--- a/drivers/net/ethernet/intel/iavf/iavf.h
+++ b/drivers/net/ethernet/intel/iavf/iavf.h
@@ -27,6 +27,7 @@
 #include <linux/etherdevice.h>
 #include <linux/socket.h>
 #include <linux/jiffies.h>
+#include <linux/net/intel/virtchnl.h>
 #include <net/ip6_checksum.h>
 #include <net/pkt_cls.h>
 #include <net/pkt_sched.h>
@@ -37,7 +38,6 @@
 #include <net/net_shaper.h>
 
 #include "iavf_type.h"
-#include <linux/avf/virtchnl.h>
 #include "iavf_txrx.h"
 #include "iavf_fdir.h"
 #include "iavf_adv_rss.h"
diff --git a/drivers/net/ethernet/intel/iavf/iavf_common.c b/drivers/net/ethernet/intel/iavf/iavf_common.c
index 614a886bca99..277193a97d91 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_common.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_common.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright(c) 2013 - 2018 Intel Corporation. */
 
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
 #include <linux/bitfield.h>
 #include "iavf_type.h"
 #include "iavf_adminq.h"
diff --git a/drivers/net/ethernet/intel/iavf/iavf_prototype.h b/drivers/net/ethernet/intel/iavf/iavf_prototype.h
index 7f9f9dbf959a..1b1f6ede3920 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_prototype.h
+++ b/drivers/net/ethernet/intel/iavf/iavf_prototype.h
@@ -4,9 +4,10 @@
 #ifndef _IAVF_PROTOTYPE_H_
 #define _IAVF_PROTOTYPE_H_
 
+#include <linux/net/intel/virtchnl.h>
+
 #include "iavf_type.h"
 #include "iavf_alloc.h"
-#include <linux/avf/virtchnl.h>
 
 /* Prototypes for shared code functions that are not in
  * the standard function pointer structures.  These are
diff --git a/drivers/net/ethernet/intel/iavf/iavf_types.h b/drivers/net/ethernet/intel/iavf/iavf_types.h
index a095855122bf..35d6d8fcca04 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_types.h
+++ b/drivers/net/ethernet/intel/iavf/iavf_types.h
@@ -4,9 +4,7 @@
 #ifndef _IAVF_TYPES_H_
 #define _IAVF_TYPES_H_
 
-#include "iavf_types.h"
-
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
 #include <linux/ptp_clock_kernel.h>
 
 /* structure used to queue PTP commands for processing */
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index f72bb1aa4067..566d1988836c 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -36,7 +36,7 @@
 #include <linux/bpf.h>
 #include <linux/btf.h>
 #include <linux/auxiliary_bus.h>
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
 #include <linux/cpu_rmap.h>
 #include <linux/dim.h>
 #include <linux/gnss.h>
diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h
index 9f5344212195..d1d674ca644f 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.h
+++ b/drivers/net/ethernet/intel/ice/ice_common.h
@@ -5,13 +5,13 @@
 #define _ICE_COMMON_H_
 
 #include <linux/bitfield.h>
+#include <linux/net/intel/virtchnl.h>
 
 #include "ice.h"
 #include "ice_type.h"
 #include "ice_nvm.h"
 #include "ice_flex_pipe.h"
 #include "ice_parser.h"
-#include <linux/avf/virtchnl.h>
 #include "ice_switch.h"
 #include "ice_fdir.h"
 
diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.h b/drivers/net/ethernet/intel/ice/ice_vf_lib.h
index 7a9c75d1d07c..fa436b3b1eac 100644
--- a/drivers/net/ethernet/intel/ice/ice_vf_lib.h
+++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.h
@@ -8,9 +8,9 @@
 #include <linux/hashtable.h>
 #include <linux/bitmap.h>
 #include <linux/mutex.h>
+#include <linux/net/intel/virtchnl.h>
 #include <linux/pci.h>
 #include <net/devlink.h>
-#include <linux/avf/virtchnl.h>
 #include "ice_type.h"
 #include "ice_flow.h"
 #include "virt/fdir.h"
diff --git a/drivers/net/ethernet/intel/ice/virt/virtchnl.h b/drivers/net/ethernet/intel/ice/virt/virtchnl.h
index 71bb456e2d71..d11789b3ae1f 100644
--- a/drivers/net/ethernet/intel/ice/virt/virtchnl.h
+++ b/drivers/net/ethernet/intel/ice/virt/virtchnl.h
@@ -7,7 +7,7 @@
 #include <linux/types.h>
 #include <linux/bitops.h>
 #include <linux/if_ether.h>
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
 #include "ice_vf_lib.h"
 
 /* Restrict number of MAC Addr and VLAN that non-trusted VF can programmed */
diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index ec1b75f039bb..984944bab28b 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -23,8 +23,8 @@ struct idpf_rss_data;
 
 #include <linux/net/intel/iidc_rdma.h>
 #include <linux/net/intel/iidc_rdma_idpf.h>
+#include <linux/net/intel/virtchnl2.h>
 
-#include "virtchnl2.h"
 #include "idpf_txrx.h"
 #include "idpf_controlq.h"
 
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
index 4be5b3b6d3ed..08b048664029 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
@@ -5,6 +5,7 @@
 #define _IDPF_TXRX_H_
 
 #include <linux/dim.h>
+#include <linux/net/intel/virtchnl2_lan_desc.h>
 
 #include <net/libeth/cache.h>
 #include <net/libeth/types.h>
@@ -13,7 +14,6 @@
 #include <net/xdp.h>
 
 #include "idpf_lan_txrx.h"
-#include "virtchnl2_lan_desc.h"
 
 #define IDPF_LARGE_MAX_Q			256
 #define IDPF_MAX_Q				16
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
index 6876e3ed9d1b..972dd88cf3d2 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
@@ -4,7 +4,7 @@
 #ifndef _IDPF_VIRTCHNL_H_
 #define _IDPF_VIRTCHNL_H_
 
-#include "virtchnl2.h"
+#include <linux/net/intel/virtchnl2.h>
 
 #define IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC	(60 * 1000)
 #define IDPF_VC_XN_IDX_M		GENMASK(7, 0)
diff --git a/include/linux/avf/virtchnl.h b/include/linux/net/intel/virtchnl.h
similarity index 100%
rename from include/linux/avf/virtchnl.h
rename to include/linux/net/intel/virtchnl.h
diff --git a/drivers/net/ethernet/intel/idpf/virtchnl2.h b/include/linux/net/intel/virtchnl2.h
similarity index 100%
rename from drivers/net/ethernet/intel/idpf/virtchnl2.h
rename to include/linux/net/intel/virtchnl2.h
diff --git a/drivers/net/ethernet/intel/idpf/virtchnl2_lan_desc.h b/include/linux/net/intel/virtchnl2_lan_desc.h
similarity index 100%
rename from drivers/net/ethernet/intel/idpf/virtchnl2_lan_desc.h
rename to include/linux/net/intel/virtchnl2_lan_desc.h
-- 
2.47.1


^ permalink raw reply related

* [PATCH net-next v5 00/15][pull request] Introduce iXD driver
From: Tony Nguyen @ 2026-07-15 18:00 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Tony Nguyen, larysa.zaremba, przemyslaw.kitszel,
	aleksander.lobakin, sridhar.samudrala, michal.swiatkowski,
	maciej.fijalkowski, emil.s.tantilov, madhu.chittim, joshua.a.hay,
	jacob.e.keller, jayaprakash.shanmugam, jiri, horms, corbet,
	richardcochran, skhan, linux-doc

Larysa Zaremba says:

This patch series adds the iXD driver, which supports the Intel(R)
Control Plane PCI Function on Intel E2100 and later IPUs and FNICs.
It facilitates a centralized control over multiple IDPF PFs/VFs/SFs
exposed by the same card. The reason for the separation is to be able
to offload the control plane to the host different from where the data
plane is running.

This is the first phase in the release of this driver where we implement the
initialization of the core PCI driver. Subsequent phases will implement
advanced features like usage of idpf ethernet aux device, link management,
NVM update via devlink, switchdev port representors, data and exception path,
flow rule programming, etc.

The first phase entails the following aspects:

1. Additional libie functionalities:
Patches 1-5 introduce additional common library API for drivers to
communicate with the control plane through mailbox communication.
A control queue is a hardware interface which is used by the driver
to interact with other subsystems (like firmware). The library APIs
allow the driver to setup and configure the control queues to send and
receive virtchnl messages. The library has an internal bookkeeping
(XN API) mechanism to keep track of the send messages. It supports both
synchronous as well as asynchronous way of handling the messages. The
library also handles the timeout internally for synchronous messages
using events. This reduces the driver's overhead in handling the timeout
error cases.

The current patch series supports only APIs that are needed for device
initialization. These include APIs in the libie_pci module:
* Allocating/freeing the DMA memory and mapping the MMIO regions for
  BAR0, read/write APIs for drivers to access the MMIO memory

and libie_cp module:
* Control queue initialization and configuration
* Transport initialization for bookkeeping
* Blocking and asynchronous mailbox transactions

Once the mailbox is initialized, the drivers can send and receive virtchnl
messages to/from the control plane.

The modules above are not supposed to be linked with the main libie library,
but do share the folder with it.

2. idpf:
Patches 6-11 refactor the idpf driver to use the libie APIs for control
queue configuration, virtchnl transaction, device initialization
and reset and adjust related code accordingly.

3. ixd:
Patches 12-15 add the ixd driver and implement multiple pieces of the
initialization flow as follows:
* Add the ability to load
* A reset is issued to ensure a clean device state, followed by
  initialization of the mailbox
* Device capabilities:
  As part of initialization, the driver has to determine what the device is
  capable of (ex. max queues, vports, etc). This information is obtained from
  the firmware and stored by the driver.
* Enable initial support for the devlink interface
---
v5:
* Add length validation to libie_ctlq_add()
* Add memory barrier before writing tail in libie_ctlq_send()
* Reduce larger then needed size allocation in libie_ctlq_xn_init_dma()
* Clarify error message on dev init failure in idpf_probe()
* Nullify adapter->asq and adapter->arq on failure in idpf_init_dflt_mbx()
* Correct virtchnl version compatibility checks in ixd_handle_vc_ver()
* Misc documentation fixes

v4: https://lore.kernel.org/netdev/0a3ceda1-a29b-4784-975b-d8c89f145d10@intel.com/
* use check_add_overflow() for MMIO offset+size bounds check in libie_pci
* replace iounmap() with pci_iounmap() for MMIO regions in libie_pci
* fix missing rx_buf->offset when constructing ctlq Rx message address
* always zero Rx descriptor before reallocation
* do not call complete() inside spinlock, theoretical use-after-free
* reset async xn state to IDLE before freeing in libie_ctlq_xn_shutdown()
* zero control queue Tx descriptor before freeing memory
* remove unused ctx field from struct libie_ctlq_xn_clean_params
* roll back merging of idpf_deinit_dflt_mbx() and xnm shutdown,
  those calls are separate again (so idpf refactoring becomes more trivial)
* introduce idpf_send_vf_reset_msg() for one-way VF reset message bypassing
  XN manager, which is shut down by now
* guard libie_ctlq_xn_shutdown() against NULL xnm in idpf_vc_event_task(),
  this is needed if reset fails and reschedules
* validate response sizes in idpf more
* fix MAINTAINERS glob pattern for include/linux/net/intel/
* roll back linux/{net/intel -> intel}/* change (1st patch)
* use pci_iomap_range() to map regions in libie_pci
* improve libie_pci commit message
* remove broken dead code for getting RSS from idpf (new patch!)
* use maximum alignment for control queue stack buffer in ixd
* fix ixd_ctlq_clean_sq() usage
* do not register ixd devlink prematurely
* improve receive buffer size validation in idpf

v3: https://lore.kernel.org/netdev/20260515224443.2772147-1-anthony.l.nguyen@intel.com/
- lock libie ctlq transaction earlier on Rx path
- improve ctlq Rx buffer size validation both in idpf and libie
- fix RSS LUT buf size (will also go to stable separately)
- improve xnm shutdown handling
- skip zero-sized BAR regions instread of returning error in idpf
- unify treatment of initialized and uninitialized ctlq messages

v2: https://lore.kernel.org/netdev/20260403194938.3577011-1-anthony.l.nguyen@intel.com/
- improved failure handling and input validation
- removed cp version from devlink in ixd

v1: https://lore.kernel.org/netdev/20260317230905.847744-1-anthony.l.nguyen@intel.com/

The following are changes since commit f6f3b36c15ed44de1fbb44e645e4fae8c4a4453e:
  net: ethernet: qualcomm: remove unneeded 'fast_io' parameter in regmap_config
and are available in the git repository at:
  git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue 200GbE

Amritha Nambiar (1):
  ixd: add devlink support

Larysa Zaremba (6):
  idpf: remove unused code for getting RSS info from device
  idpf: make mbx_task queueing and cancelling more consistent
  idpf: print a debug message and bail in case of non-event ctlq message
  ixd: add basic driver framework for Intel(R) Control Plane Function
  ixd: add reset checks and initialize the mailbox
  ixd: add the core initialization

Pavan Kumar Linga (4):
  libeth: allow to create fill queues without NAPI
  idpf: remove 'vport_params_reqd' field
  idpf: refactor idpf to use libie_pci APIs
  idpf: refactor idpf to use libie control queues

Phani R Burra (3):
  libie: add PCI device initialization helpers to libie
  libie: add control queue support
  libie: add bookkeeping support for control queue messages

Victor Raj (1):
  virtchnl: move virtchnl and virtchnl2 headers to
    'include/linux/net/intel'

 .../device_drivers/ethernet/index.rst         |    1 +
 .../device_drivers/ethernet/intel/ixd.rst     |   39 +
 Documentation/networking/devlink/index.rst    |    1 +
 Documentation/networking/devlink/ixd.rst      |   30 +
 MAINTAINERS                                   |    3 +-
 drivers/net/ethernet/intel/Kconfig            |    2 +
 drivers/net/ethernet/intel/Makefile           |    1 +
 drivers/net/ethernet/intel/i40e/i40e.h        |    2 +-
 drivers/net/ethernet/intel/i40e/i40e_common.c |    2 +-
 .../net/ethernet/intel/i40e/i40e_prototype.h  |    2 +-
 .../ethernet/intel/i40e/i40e_virtchnl_pf.h    |    2 +-
 drivers/net/ethernet/intel/iavf/iavf.h        |    2 +-
 drivers/net/ethernet/intel/iavf/iavf_common.c |    2 +-
 .../net/ethernet/intel/iavf/iavf_prototype.h  |    3 +-
 drivers/net/ethernet/intel/iavf/iavf_types.h  |    4 +-
 drivers/net/ethernet/intel/ice/ice.h          |    2 +-
 drivers/net/ethernet/intel/ice/ice_common.h   |    2 +-
 drivers/net/ethernet/intel/ice/ice_vf_lib.h   |    2 +-
 .../net/ethernet/intel/ice/virt/virtchnl.h    |    2 +-
 drivers/net/ethernet/intel/idpf/Kconfig       |    1 +
 drivers/net/ethernet/intel/idpf/Makefile      |    2 -
 drivers/net/ethernet/intel/idpf/idpf.h        |   99 +-
 .../net/ethernet/intel/idpf/idpf_controlq.c   |  621 ------
 .../net/ethernet/intel/idpf/idpf_controlq.h   |  144 --
 .../ethernet/intel/idpf/idpf_controlq_api.h   |  177 --
 .../ethernet/intel/idpf/idpf_controlq_setup.c |  169 --
 drivers/net/ethernet/intel/idpf/idpf_dev.c    |  101 +-
 .../net/ethernet/intel/idpf/idpf_ethtool.c    |   28 +-
 drivers/net/ethernet/intel/idpf/idpf_idc.c    |   38 +-
 drivers/net/ethernet/intel/idpf/idpf_lib.c    |   69 +-
 drivers/net/ethernet/intel/idpf/idpf_main.c   |  115 +-
 drivers/net/ethernet/intel/idpf/idpf_mem.h    |   20 -
 drivers/net/ethernet/intel/idpf/idpf_txrx.c   |    4 +-
 drivers/net/ethernet/intel/idpf/idpf_txrx.h   |    4 +-
 drivers/net/ethernet/intel/idpf/idpf_vf_dev.c |   96 +-
 .../net/ethernet/intel/idpf/idpf_virtchnl.c   | 1883 +++++++----------
 .../net/ethernet/intel/idpf/idpf_virtchnl.h   |  107 +-
 .../ethernet/intel/idpf/idpf_virtchnl_ptp.c   |  312 +--
 drivers/net/ethernet/intel/ixd/Kconfig        |   15 +
 drivers/net/ethernet/intel/ixd/Makefile       |   13 +
 drivers/net/ethernet/intel/ixd/ixd.h          |   59 +
 drivers/net/ethernet/intel/ixd/ixd_ctlq.c     |  146 ++
 drivers/net/ethernet/intel/ixd/ixd_ctlq.h     |   34 +
 drivers/net/ethernet/intel/ixd/ixd_dev.c      |   89 +
 drivers/net/ethernet/intel/ixd/ixd_devlink.c  |   97 +
 drivers/net/ethernet/intel/ixd/ixd_devlink.h  |   50 +
 drivers/net/ethernet/intel/ixd/ixd_lan_regs.h |   68 +
 drivers/net/ethernet/intel/ixd/ixd_lib.c      |  174 ++
 drivers/net/ethernet/intel/ixd/ixd_main.c     |  147 ++
 drivers/net/ethernet/intel/ixd/ixd_virtchnl.c |  190 ++
 drivers/net/ethernet/intel/ixd/ixd_virtchnl.h |   12 +
 drivers/net/ethernet/intel/libeth/rx.c        |   12 +-
 drivers/net/ethernet/intel/libie/Kconfig      |   14 +
 drivers/net/ethernet/intel/libie/Makefile     |    8 +
 drivers/net/ethernet/intel/libie/controlq.c   | 1223 +++++++++++
 drivers/net/ethernet/intel/libie/pci.c        |  209 ++
 include/linux/net/intel/libie/controlq.h      |  424 ++++
 include/linux/net/intel/libie/pci.h           |   56 +
 include/linux/{avf => net/intel}/virtchnl.h   |    0
 .../linux/net/intel}/virtchnl2.h              |    0
 .../linux/net/intel}/virtchnl2_lan_desc.h     |    0
 include/net/libeth/rx.h                       |    4 +-
 62 files changed, 4241 insertions(+), 2897 deletions(-)
 create mode 100644 Documentation/networking/device_drivers/ethernet/intel/ixd.rst
 create mode 100644 Documentation/networking/devlink/ixd.rst
 delete mode 100644 drivers/net/ethernet/intel/idpf/idpf_controlq.c
 delete mode 100644 drivers/net/ethernet/intel/idpf/idpf_controlq.h
 delete mode 100644 drivers/net/ethernet/intel/idpf/idpf_controlq_api.h
 delete mode 100644 drivers/net/ethernet/intel/idpf/idpf_controlq_setup.c
 delete mode 100644 drivers/net/ethernet/intel/idpf/idpf_mem.h
 create mode 100644 drivers/net/ethernet/intel/ixd/Kconfig
 create mode 100644 drivers/net/ethernet/intel/ixd/Makefile
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd.h
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_ctlq.c
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_ctlq.h
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_dev.c
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_devlink.c
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_devlink.h
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_lan_regs.h
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_lib.c
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_main.c
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_virtchnl.c
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_virtchnl.h
 create mode 100644 drivers/net/ethernet/intel/libie/controlq.c
 create mode 100644 drivers/net/ethernet/intel/libie/pci.c
 create mode 100644 include/linux/net/intel/libie/controlq.h
 create mode 100644 include/linux/net/intel/libie/pci.h
 rename include/linux/{avf => net/intel}/virtchnl.h (100%)
 rename {drivers/net/ethernet/intel/idpf => include/linux/net/intel}/virtchnl2.h (100%)
 rename {drivers/net/ethernet/intel/idpf => include/linux/net/intel}/virtchnl2_lan_desc.h (100%)

-- 
2.47.1


^ permalink raw reply

* [PATCH net-next v5 02/15] libie: add PCI device initialization helpers to libie
From: Tony Nguyen @ 2026-07-15 18:00 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Phani R Burra, anthony.l.nguyen, larysa.zaremba,
	przemyslaw.kitszel, aleksander.lobakin, sridhar.samudrala,
	michal.swiatkowski, maciej.fijalkowski, emil.s.tantilov,
	madhu.chittim, joshua.a.hay, jacob.e.keller,
	jayaprakash.shanmugam, jiri, horms, corbet, richardcochran, skhan,
	linux-doc, Bharath R, Samuel Salin
In-Reply-To: <20260715180042.1972010-1-anthony.l.nguyen@intel.com>

From: Phani R Burra <phani.r.burra@intel.com>

idpf and ixd drivers serve different PCI functions on the same device,
therefore their PCI configuration flow is very similar.

Add support functions for idpf and ixd to configure PCI functionality and
access MMIO space. Add a mapping list which can be traversed by a driver,
e.g. to pass certain I/O mappings to the auxbus devices. Such list is also
traversed by the libie_pci_get_mmio_addr() helper, which allows for easier
memory access.

Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Signed-off-by: Phani R Burra <phani.r.burra@intel.com>
Co-developed-by: Victor Raj <victor.raj@intel.com>
Signed-off-by: Victor Raj <victor.raj@intel.com>
Co-developed-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Signed-off-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Co-developed-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Tested-by: Bharath R <bharath.r@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Co-developed-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
---
 drivers/net/ethernet/intel/libie/Kconfig  |   6 +
 drivers/net/ethernet/intel/libie/Makefile |   4 +
 drivers/net/ethernet/intel/libie/pci.c    | 209 ++++++++++++++++++++++
 include/linux/net/intel/libie/pci.h       |  56 ++++++
 4 files changed, 275 insertions(+)
 create mode 100644 drivers/net/ethernet/intel/libie/pci.c
 create mode 100644 include/linux/net/intel/libie/pci.h

diff --git a/drivers/net/ethernet/intel/libie/Kconfig b/drivers/net/ethernet/intel/libie/Kconfig
index 70831c7e336e..500a95c944a8 100644
--- a/drivers/net/ethernet/intel/libie/Kconfig
+++ b/drivers/net/ethernet/intel/libie/Kconfig
@@ -23,3 +23,9 @@ config LIBIE_FWLOG
 	  for it. Firmware logging is using admin queue interface to communicate
 	  with the device. Debugfs is a user interface used to config logging
 	  and dump all collected logs.
+
+config LIBIE_PCI
+	tristate
+	help
+	  Helper functions for management of PCI resources belonging
+	  to networking devices.
diff --git a/drivers/net/ethernet/intel/libie/Makefile b/drivers/net/ethernet/intel/libie/Makefile
index db57fc6780ea..a28509cb9086 100644
--- a/drivers/net/ethernet/intel/libie/Makefile
+++ b/drivers/net/ethernet/intel/libie/Makefile
@@ -12,3 +12,7 @@ libie_adminq-y			:= adminq.o
 obj-$(CONFIG_LIBIE_FWLOG) 	+= libie_fwlog.o
 
 libie_fwlog-y			:= fwlog.o
+
+obj-$(CONFIG_LIBIE_PCI)		+= libie_pci.o
+
+libie_pci-y			:= pci.o
diff --git a/drivers/net/ethernet/intel/libie/pci.c b/drivers/net/ethernet/intel/libie/pci.c
new file mode 100644
index 000000000000..a0d3535d6529
--- /dev/null
+++ b/drivers/net/ethernet/intel/libie/pci.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2025 Intel Corporation */
+
+#include <linux/net/intel/libie/pci.h>
+
+/**
+ * libie_find_mmio_region - find MMIO region containing a range
+ * @mmio_list: list that contains MMIO region info
+ * @offset: range start offset
+ * @size: range size
+ * @bar_idx: BAR index containing the range to search
+ *
+ * Return: pointer to a MMIO region overlapping with the range in any way or
+ *	   NULL if no such region is mapped.
+ */
+static struct libie_pci_mmio_region *
+libie_find_mmio_region(const struct list_head *mmio_list,
+		       resource_size_t offset, resource_size_t size,
+		       int bar_idx)
+{
+	resource_size_t end_offset = offset + size;
+	struct libie_pci_mmio_region *mr;
+
+	list_for_each_entry(mr, mmio_list, list) {
+		resource_size_t mr_end = mr->offset + mr->size;
+		resource_size_t mr_start = mr->offset;
+
+		if (mr->bar_idx != bar_idx)
+			continue;
+		if (offset < mr_end && end_offset > mr_start)
+			return mr;
+	}
+
+	return NULL;
+}
+
+/**
+ * __libie_pci_get_mmio_addr - get the MMIO virtual address
+ * @mmio_info: contains list of MMIO regions
+ * @offset: register offset to find
+ * @num_args: number of additional arguments present
+ *
+ * This function finds the virtual address of a register offset by iterating
+ * through the non-linear MMIO regions that are mapped by the driver.
+ *
+ * Return: valid MMIO virtual address or NULL.
+ */
+void __iomem *__libie_pci_get_mmio_addr(struct libie_mmio_info *mmio_info,
+					resource_size_t offset,
+					int num_args, ...)
+{
+	struct libie_pci_mmio_region *mr;
+	int bar_idx = 0;
+	va_list args;
+
+	if (num_args) {
+		va_start(args, num_args);
+		bar_idx = va_arg(args, int);
+		va_end(args);
+	}
+
+	list_for_each_entry(mr, &mmio_info->mmio_list, list)
+		if (bar_idx == mr->bar_idx && offset >= mr->offset &&
+		    offset < mr->offset + mr->size) {
+			offset -= mr->offset;
+
+			return mr->addr + offset;
+		}
+
+	return NULL;
+}
+EXPORT_SYMBOL_NS_GPL(__libie_pci_get_mmio_addr, "LIBIE_PCI");
+
+/**
+ * __libie_pci_map_mmio_region - map PCI device MMIO region
+ * @mmio_info: struct to store the mapped MMIO region
+ * @offset: MMIO region start offset
+ * @size: MMIO region size
+ * @num_args: number of additional arguments present
+ *
+ * Return: true on success, false on memory map failure.
+ */
+bool __libie_pci_map_mmio_region(struct libie_mmio_info *mmio_info,
+				 resource_size_t offset,
+				 resource_size_t size, int num_args, ...)
+{
+	struct pci_dev *pdev = mmio_info->pdev;
+	struct libie_pci_mmio_region *mr;
+	resource_size_t end_offset;
+	void __iomem *va;
+	int bar_idx = 0;
+	va_list args;
+
+	if (num_args) {
+		va_start(args, num_args);
+		bar_idx = va_arg(args, int);
+		va_end(args);
+	}
+
+	/* pci_iomap_range() would silently map less in such case */
+	if (!size || check_add_overflow(offset, size, &end_offset) ||
+	    end_offset > pci_resource_len(pdev, bar_idx))
+		return false;
+
+	mr = libie_find_mmio_region(&mmio_info->mmio_list, offset, size,
+				    bar_idx);
+	if (mr) {
+		pci_warn(pdev,
+			 "Mapping of BAR%u (offset=%llu, size=%llu) intersecting region (offset=%llu, size=%llu) already exists\n",
+			 bar_idx, (unsigned long long)mr->offset,
+			 (unsigned long long)mr->size,
+			 (unsigned long long)offset, (unsigned long long)size);
+		return mr->offset <= offset &&
+		       mr->offset + mr->size >= end_offset;
+	}
+
+	va = pci_iomap_range(mmio_info->pdev, bar_idx, offset, size);
+	if (!va) {
+		pci_err(pdev, "Failed to map BAR%u region\n", bar_idx);
+		return false;
+	}
+
+	mr = kvzalloc_obj(*mr);
+	if (!mr) {
+		pci_iounmap(pdev, va);
+		return false;
+	}
+
+	mr->addr = va;
+	mr->offset = offset;
+	mr->size = size;
+	mr->bar_idx = bar_idx;
+
+	list_add_tail(&mr->list, &mmio_info->mmio_list);
+
+	return true;
+}
+EXPORT_SYMBOL_NS_GPL(__libie_pci_map_mmio_region, "LIBIE_PCI");
+
+/**
+ * libie_pci_unmap_fltr_regs - unmap selected PCI device MMIO regions
+ * @mmio_info: contains list of MMIO regions to unmap
+ * @fltr: returns true, if region is to be unmapped
+ */
+void libie_pci_unmap_fltr_regs(struct libie_mmio_info *mmio_info,
+			       bool (*fltr)(struct libie_mmio_info *mmio_info,
+					    struct libie_pci_mmio_region *reg))
+{
+	struct libie_pci_mmio_region *mr, *tmp;
+
+	list_for_each_entry_safe(mr, tmp, &mmio_info->mmio_list, list) {
+		if (!fltr(mmio_info, mr))
+			continue;
+		pci_iounmap(mmio_info->pdev, mr->addr);
+		list_del(&mr->list);
+		kvfree(mr);
+	}
+}
+EXPORT_SYMBOL_NS_GPL(libie_pci_unmap_fltr_regs, "LIBIE_PCI");
+
+/**
+ * libie_pci_unmap_all_mmio_regions - unmap all PCI device MMIO regions
+ * @mmio_info: contains list of MMIO regions to unmap
+ */
+void libie_pci_unmap_all_mmio_regions(struct libie_mmio_info *mmio_info)
+{
+	struct libie_pci_mmio_region *mr, *tmp;
+
+	list_for_each_entry_safe(mr, tmp, &mmio_info->mmio_list, list) {
+		pci_iounmap(mmio_info->pdev, mr->addr);
+		list_del(&mr->list);
+		kvfree(mr);
+	}
+}
+EXPORT_SYMBOL_NS_GPL(libie_pci_unmap_all_mmio_regions, "LIBIE_PCI");
+
+/**
+ * libie_pci_init_dev - enable and reserve PCI regions of the device
+ * @pdev: PCI device information
+ *
+ * Return: %0 on success, -%errno on failure.
+ */
+int libie_pci_init_dev(struct pci_dev *pdev)
+{
+	int err;
+
+	err = pcim_enable_device(pdev);
+	if (err)
+		return err;
+
+	for (int bar = 0; bar < PCI_STD_NUM_BARS; bar++)
+		if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
+			err = pcim_request_region(pdev, bar, pci_name(pdev));
+			if (err)
+				return err;
+		}
+
+	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+	if (err)
+		return err;
+
+	pci_set_master(pdev);
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(libie_pci_init_dev, "LIBIE_PCI");
+
+MODULE_DESCRIPTION("Common Ethernet PCI library");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/net/intel/libie/pci.h b/include/linux/net/intel/libie/pci.h
new file mode 100644
index 000000000000..effd072c55c8
--- /dev/null
+++ b/include/linux/net/intel/libie/pci.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2025 Intel Corporation */
+
+#ifndef __LIBIE_PCI_H
+#define __LIBIE_PCI_H
+
+#include <linux/pci.h>
+
+/**
+ * struct libie_pci_mmio_region - structure for MMIO region info
+ * @list: used to add a MMIO region to the list of MMIO regions in
+ *	  libie_mmio_info
+ * @addr: virtual address of MMIO region start
+ * @offset: start offset of the MMIO region
+ * @size: size of the MMIO region
+ * @bar_idx: BAR index to which the MMIO region belongs to
+ */
+struct libie_pci_mmio_region {
+	struct list_head	list;
+	void __iomem		*addr;
+	resource_size_t		offset;
+	resource_size_t		size;
+	u16			bar_idx;
+};
+
+/**
+ * struct libie_mmio_info - contains list of MMIO regions
+ * @pdev: PCI device pointer
+ * @mmio_list: list of MMIO regions
+ */
+struct libie_mmio_info {
+	struct pci_dev		*pdev;
+	struct list_head	mmio_list;
+};
+
+#define libie_pci_map_mmio_region(mmio_info, offset, size, ...)	\
+	__libie_pci_map_mmio_region(mmio_info, offset, size,		\
+				     COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__)
+
+#define libie_pci_get_mmio_addr(mmio_info, offset, ...)		\
+	__libie_pci_get_mmio_addr(mmio_info, offset,			\
+				   COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__)
+
+bool __libie_pci_map_mmio_region(struct libie_mmio_info *mmio_info,
+				 resource_size_t offset, resource_size_t size,
+				 int num_args, ...);
+void __iomem *__libie_pci_get_mmio_addr(struct libie_mmio_info *mmio_info,
+					resource_size_t offset,
+					int num_args, ...);
+void libie_pci_unmap_all_mmio_regions(struct libie_mmio_info *mmio_info);
+void libie_pci_unmap_fltr_regs(struct libie_mmio_info *mmio_info,
+			       bool (*fltr)(struct libie_mmio_info *mmio_info,
+					    struct libie_pci_mmio_region *reg));
+int libie_pci_init_dev(struct pci_dev *pdev);
+
+#endif /* __LIBIE_PCI_H */
-- 
2.47.1


^ permalink raw reply related

* [PATCH net-next v5 03/15] libeth: allow to create fill queues without NAPI
From: Tony Nguyen @ 2026-07-15 18:00 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Pavan Kumar Linga, anthony.l.nguyen, larysa.zaremba,
	przemyslaw.kitszel, aleksander.lobakin, sridhar.samudrala,
	michal.swiatkowski, maciej.fijalkowski, emil.s.tantilov,
	joshua.a.hay, jacob.e.keller, jayaprakash.shanmugam, jiri, horms,
	corbet, richardcochran, skhan, linux-doc, Bharath R, Samuel Salin
In-Reply-To: <20260715180042.1972010-1-anthony.l.nguyen@intel.com>

From: Pavan Kumar Linga <pavan.kumar.linga@intel.com>

Control queues can utilize libeth_rx fill queues, despite working outside
of NAPI context. The only problem is standard fill queues requiring NAPI
that provides them with the device pointer.

Introduce a way to provide the device directly without using NAPI.

Suggested-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Tested-by: Bharath R <bharath.r@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
---
 drivers/net/ethernet/intel/libeth/rx.c | 12 ++++++++----
 include/net/libeth/rx.h                |  4 +++-
 2 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/libeth/rx.c b/drivers/net/ethernet/intel/libeth/rx.c
index 62521a1f4ec9..0c1a565a1b3a 100644
--- a/drivers/net/ethernet/intel/libeth/rx.c
+++ b/drivers/net/ethernet/intel/libeth/rx.c
@@ -145,25 +145,29 @@ static bool libeth_rx_page_pool_params_zc(struct libeth_fq *fq,
 /**
  * libeth_rx_fq_create - create a PP with the default libeth settings
  * @fq: buffer queue struct to fill
- * @napi: &napi_struct covering this PP (no usage outside its poll loops)
+ * @napi_dev: &napi_struct for NAPI (data) queues, &device for others
  *
  * Return: %0 on success, -%errno on failure.
  */
-int libeth_rx_fq_create(struct libeth_fq *fq, struct napi_struct *napi)
+int libeth_rx_fq_create(struct libeth_fq *fq, void *napi_dev)
 {
+	struct napi_struct *napi = fq->no_napi ? NULL : napi_dev;
 	struct page_pool_params pp = {
 		.flags		= PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
 		.order		= LIBETH_RX_PAGE_ORDER,
 		.pool_size	= fq->count,
 		.nid		= fq->nid,
-		.dev		= napi->dev->dev.parent,
-		.netdev		= napi->dev,
+		.dev		= napi ? napi->dev->dev.parent : napi_dev,
+		.netdev		= napi ? napi->dev : NULL,
 		.napi		= napi,
 	};
 	struct libeth_fqe *fqes;
 	struct page_pool *pool;
 	int ret;
 
+	if (!pp.netdev && fq->type == LIBETH_FQE_MTU)
+		return -EINVAL;
+
 	pp.dma_dir = fq->xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
 
 	if (!fq->hsplit)
diff --git a/include/net/libeth/rx.h b/include/net/libeth/rx.h
index 5d991404845e..0e736846c5e8 100644
--- a/include/net/libeth/rx.h
+++ b/include/net/libeth/rx.h
@@ -69,6 +69,7 @@ enum libeth_fqe_type {
  * @type: type of the buffers this queue has
  * @hsplit: flag whether header split is enabled
  * @xdp: flag indicating whether XDP is enabled
+ * @no_napi: the queue is not a data queue and does not have NAPI
  * @buf_len: HW-writeable length per each buffer
  * @nid: ID of the closest NUMA node with memory
  */
@@ -85,12 +86,13 @@ struct libeth_fq {
 	enum libeth_fqe_type	type:2;
 	bool			hsplit:1;
 	bool			xdp:1;
+	bool			no_napi:1;
 
 	u32			buf_len;
 	int			nid;
 };
 
-int libeth_rx_fq_create(struct libeth_fq *fq, struct napi_struct *napi);
+int libeth_rx_fq_create(struct libeth_fq *fq, void *napi_dev);
 void libeth_rx_fq_destroy(struct libeth_fq *fq);
 
 /**
-- 
2.47.1


^ permalink raw reply related

* [PATCH net-next v5 04/15] libie: add control queue support
From: Tony Nguyen @ 2026-07-15 18:00 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Phani R Burra, anthony.l.nguyen, larysa.zaremba,
	przemyslaw.kitszel, aleksander.lobakin, sridhar.samudrala,
	michal.swiatkowski, maciej.fijalkowski, emil.s.tantilov,
	madhu.chittim, joshua.a.hay, jacob.e.keller,
	jayaprakash.shanmugam, jiri, horms, corbet, richardcochran, skhan,
	linux-doc, Samuel Salin, Bharath R
In-Reply-To: <20260715180042.1972010-1-anthony.l.nguyen@intel.com>

From: Phani R Burra <phani.r.burra@intel.com>

Libie will now support control queue setup and configuration APIs. These
are mainly used for mailbox communication between drivers and control
plane.

Make use of the libeth_rx page pool support for managing controlq buffers.

Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Signed-off-by: Phani R Burra <phani.r.burra@intel.com>
Co-developed-by: Victor Raj <victor.raj@intel.com>
Signed-off-by: Victor Raj <victor.raj@intel.com>
Co-developed-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Signed-off-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Co-developed-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Tested-by: Bharath R <bharath.r@intel.com>
Co-developed-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
---
 drivers/net/ethernet/intel/libie/Kconfig    |   8 +
 drivers/net/ethernet/intel/libie/Makefile   |   4 +
 drivers/net/ethernet/intel/libie/controlq.c | 616 ++++++++++++++++++++
 include/linux/net/intel/libie/controlq.h    | 249 ++++++++
 4 files changed, 877 insertions(+)
 create mode 100644 drivers/net/ethernet/intel/libie/controlq.c
 create mode 100644 include/linux/net/intel/libie/controlq.h

diff --git a/drivers/net/ethernet/intel/libie/Kconfig b/drivers/net/ethernet/intel/libie/Kconfig
index 500a95c944a8..9c5fdebb6766 100644
--- a/drivers/net/ethernet/intel/libie/Kconfig
+++ b/drivers/net/ethernet/intel/libie/Kconfig
@@ -15,6 +15,14 @@ config LIBIE_ADMINQ
 	  Helper functions used by Intel Ethernet drivers for administration
 	  queue command interface (aka adminq).
 
+config LIBIE_CP
+	tristate
+	select LIBETH
+	select LIBIE_PCI
+	help
+	  Common helper routines to communicate with the device Control Plane
+	  using virtchnl2 or related mailbox protocols.
+
 config LIBIE_FWLOG
 	tristate
 	select LIBIE_ADMINQ
diff --git a/drivers/net/ethernet/intel/libie/Makefile b/drivers/net/ethernet/intel/libie/Makefile
index a28509cb9086..3065aa057798 100644
--- a/drivers/net/ethernet/intel/libie/Makefile
+++ b/drivers/net/ethernet/intel/libie/Makefile
@@ -9,6 +9,10 @@ obj-$(CONFIG_LIBIE_ADMINQ) 	+= libie_adminq.o
 
 libie_adminq-y			:= adminq.o
 
+obj-$(CONFIG_LIBIE_CP)		+= libie_cp.o
+
+libie_cp-y			:= controlq.o
+
 obj-$(CONFIG_LIBIE_FWLOG) 	+= libie_fwlog.o
 
 libie_fwlog-y			:= fwlog.o
diff --git a/drivers/net/ethernet/intel/libie/controlq.c b/drivers/net/ethernet/intel/libie/controlq.c
new file mode 100644
index 000000000000..a3aa241f03b9
--- /dev/null
+++ b/drivers/net/ethernet/intel/libie/controlq.c
@@ -0,0 +1,616 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2025 Intel Corporation */
+
+#include <linux/bitfield.h>
+#include <net/libeth/rx.h>
+
+#include <linux/net/intel/libie/controlq.h>
+
+#define LIBIE_CTLQ_DESC_QWORD0(sz)			\
+	(LIBIE_CTLQ_DESC_FLAG_BUF |			\
+	 LIBIE_CTLQ_DESC_FLAG_RD |			\
+	 FIELD_PREP(LIBIE_CTLQ_DESC_DATA_LEN, sz))
+
+/**
+ * libie_ctlq_free_fq - free fill queue resources, including buffers
+ * @ctlq: Rx control queue whose resources need to be freed
+ */
+static void libie_ctlq_free_fq(struct libie_ctlq_info *ctlq)
+{
+	struct libeth_fq fq = {
+		.fqes		= ctlq->rx_fqes,
+		.pp		= ctlq->pp,
+	};
+
+	for (u32 ntc = ctlq->next_to_clean; ntc != ctlq->next_to_post; ) {
+		page_pool_put_full_netmem(fq.pp, fq.fqes[ntc].netmem, false);
+
+		if (++ntc >= ctlq->ring_len)
+			ntc = 0;
+	}
+
+	libeth_rx_fq_destroy(&fq);
+}
+
+/**
+ * libie_ctlq_init_fq - initialize fill queue for an Rx controlq
+ * @ctlq: control queue that needs a Rx buffer allocation
+ *
+ * Return: %0 on success, -%errno on failure
+ */
+static int libie_ctlq_init_fq(struct libie_ctlq_info *ctlq)
+{
+	struct libeth_fq fq = {
+		.count		= ctlq->ring_len,
+		.truesize	= LIBIE_CTLQ_MAX_BUF_LEN,
+		.nid		= NUMA_NO_NODE,
+		.type		= LIBETH_FQE_SHORT,
+		.hsplit		= true,
+		.no_napi	= true,
+	};
+	int err;
+
+	err = libeth_rx_fq_create(&fq, ctlq->dev);
+	if (err)
+		return err;
+
+	ctlq->pp = fq.pp;
+	ctlq->rx_fqes = fq.fqes;
+	ctlq->truesize = fq.truesize;
+
+	return 0;
+}
+
+/**
+ * libie_ctlq_prep_rx_desc - prepare the descriptor with a new address
+ * @desc: descriptor to (re)initialize
+ * @addr: physical address to put into descriptor
+ * @mem_truesize: size of the accessible memory
+ */
+static void libie_ctlq_prep_rx_desc(struct libie_ctlq_desc *desc,
+				    dma_addr_t addr, u32 mem_truesize)
+{
+	u64 qword;
+
+	qword = LIBIE_CTLQ_DESC_QWORD0(mem_truesize);
+	desc->qword0 = cpu_to_le64(qword);
+
+	qword = FIELD_PREP(LIBIE_CTLQ_DESC_DATA_ADDR_HIGH,
+			   upper_32_bits(addr)) |
+		FIELD_PREP(LIBIE_CTLQ_DESC_DATA_ADDR_LOW,
+			   lower_32_bits(addr));
+	desc->qword3 = cpu_to_le64(qword);
+}
+
+/**
+ * libie_ctlq_post_rx_buffs - post buffers to descriptor ring
+ * @ctlq: control queue that requires Rx descriptor ring to be initialized with
+ *	  new Rx buffers
+ *
+ * The caller must make sure that calls to libie_ctlq_post_rx_buffs()
+ * and libie_ctlq_recv() for each queue are either serialized
+ * or used under ctlq->lock.
+ *
+ * Return: %0 on success, -%ENOMEM if any buffer could not be allocated
+ */
+int libie_ctlq_post_rx_buffs(struct libie_ctlq_info *ctlq)
+{
+	u32 ntp = ctlq->next_to_post, ntc = ctlq->next_to_clean, num_to_post;
+	const struct libeth_fq_fp fq = {
+		.pp		= ctlq->pp,
+		.fqes		= ctlq->rx_fqes,
+		.truesize	= ctlq->truesize,
+		.count		= ctlq->ring_len,
+	};
+	int ret = 0;
+
+	num_to_post = (ntc > ntp ? 0 : ctlq->ring_len) + ntc - ntp - 1;
+
+	while (num_to_post--) {
+		dma_addr_t addr;
+
+		ctlq->descs[ntp] = (struct libie_ctlq_desc) {};
+
+		addr = libeth_rx_alloc(&fq, ntp);
+		if (unlikely(addr == DMA_MAPPING_ERROR)) {
+			ret = -ENOMEM;
+			goto post_bufs;
+		}
+
+		libie_ctlq_prep_rx_desc(&ctlq->descs[ntp], addr, fq.truesize);
+
+		if (unlikely(++ntp == ctlq->ring_len))
+			ntp = 0;
+	}
+
+post_bufs:
+	if (likely(ctlq->next_to_post != ntp)) {
+		ctlq->next_to_post = ntp;
+
+		writel(ntp, ctlq->reg.tail);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_post_rx_buffs, "LIBIE_CP");
+
+/**
+ * libie_ctlq_free_tx_msgs - Free Tx control queue messages
+ * @ctlq: Tx control queue being destroyed
+ * @num_msgs: number of messages allocated so far
+ */
+static void libie_ctlq_free_tx_msgs(struct libie_ctlq_info *ctlq,
+				    u32 num_msgs)
+{
+	for (u32 i = 0; i < num_msgs; i++)
+		kfree(ctlq->tx_msg[i]);
+
+	kvfree(ctlq->tx_msg);
+}
+
+/**
+ * libie_ctlq_alloc_tx_msgs - Allocate Tx control queue messages
+ * @ctlq: Tx control queue being created
+ *
+ * Return: %0 on success, -%ENOMEM on allocation error
+ */
+static int libie_ctlq_alloc_tx_msgs(struct libie_ctlq_info *ctlq)
+{
+	ctlq->tx_msg = kvzalloc_objs(*ctlq->tx_msg, ctlq->ring_len,
+				     GFP_KERNEL);
+	if (!ctlq->tx_msg)
+		return -ENOMEM;
+
+	for (u32 i = 0; i < ctlq->ring_len; i++) {
+		ctlq->tx_msg[i] = kzalloc_obj(*ctlq->tx_msg[i]);
+		if (!ctlq->tx_msg[i]) {
+			libie_ctlq_free_tx_msgs(ctlq, i);
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * libie_cp_free_dma_mem - Free the previously allocated DMA memory
+ * @dev: device information
+ * @mem: DMA memory information
+ */
+static void libie_cp_free_dma_mem(struct device *dev,
+				  struct libie_cp_dma_mem *mem)
+{
+	dma_free_coherent(dev, mem->size, mem->va, mem->pa);
+	mem->va = NULL;
+}
+
+/**
+ * libie_ctlq_dealloc_ring_res - Free memory allocated for control queue
+ * @ctlq: control queue that requires its ring memory to be freed
+ *
+ * Free the memory used by the ring, buffers and other related structures.
+ */
+static void libie_ctlq_dealloc_ring_res(struct libie_ctlq_info *ctlq)
+{
+	struct libie_cp_dma_mem *dma = &ctlq->ring_mem;
+
+	if (ctlq->type == LIBIE_CTLQ_TYPE_TX)
+		libie_ctlq_free_tx_msgs(ctlq, ctlq->ring_len);
+	else
+		libie_ctlq_free_fq(ctlq);
+
+	libie_cp_free_dma_mem(ctlq->dev, dma);
+}
+
+/**
+ * libie_cp_alloc_dma_mem - Allocate a DMA memory
+ * @dev: device information
+ * @mem: memory for DMA information to be stored
+ * @size: size of the memory to allocate
+ *
+ * Return: virtual address of DMA memory or NULL.
+ */
+static void *libie_cp_alloc_dma_mem(struct device *dev,
+				    struct libie_cp_dma_mem *mem, u32 size)
+{
+	size = ALIGN(size, SZ_4K);
+
+	mem->va = dma_alloc_coherent(dev, size, &mem->pa, GFP_KERNEL);
+	mem->size = size;
+
+	return mem->va;
+}
+
+/**
+ * libie_ctlq_alloc_queue_res - allocate memory for descriptor ring and bufs
+ * @ctlq: control queue that requires its ring resources to be allocated
+ *
+ * Return: %0 on success, -%errno on failure
+ */
+static int libie_ctlq_alloc_queue_res(struct libie_ctlq_info *ctlq)
+{
+	size_t size = array_size(ctlq->ring_len, sizeof(*ctlq->descs));
+	struct libie_cp_dma_mem *dma = &ctlq->ring_mem;
+	int err = -ENOMEM;
+
+	if (!libie_cp_alloc_dma_mem(ctlq->dev, dma, size))
+		return -ENOMEM;
+
+	ctlq->descs = dma->va;
+
+	if (ctlq->type == LIBIE_CTLQ_TYPE_TX) {
+		if (libie_ctlq_alloc_tx_msgs(ctlq))
+			goto free_dma_mem;
+	} else {
+		err = libie_ctlq_init_fq(ctlq);
+		if (err)
+			goto free_dma_mem;
+
+		err = libie_ctlq_post_rx_buffs(ctlq);
+		if (err) {
+			libie_ctlq_free_fq(ctlq);
+			goto free_dma_mem;
+		}
+	}
+
+	return 0;
+
+free_dma_mem:
+	libie_cp_free_dma_mem(ctlq->dev, dma);
+
+	return err;
+}
+
+/**
+ * libie_ctlq_init_regs - Initialize control queue registers
+ * @ctlq: control queue that needs to be initialized
+ *
+ * Initialize registers. The caller is expected to have already initialized the
+ * descriptor ring memory and buffer memory.
+ */
+static void libie_ctlq_init_regs(struct libie_ctlq_info *ctlq)
+{
+	u32 dword;
+
+	if (ctlq->type == LIBIE_CTLQ_TYPE_RX)
+		writel(ctlq->ring_len - 1, ctlq->reg.tail);
+
+	writel(0, ctlq->reg.head);
+	writel(lower_32_bits(ctlq->ring_mem.pa), ctlq->reg.addr_low);
+	writel(upper_32_bits(ctlq->ring_mem.pa), ctlq->reg.addr_high);
+
+	dword = FIELD_PREP(LIBIE_CTLQ_MBX_ATQ_LEN, ctlq->ring_len) |
+		ctlq->reg.len_ena_mask;
+	writel(dword, ctlq->reg.len);
+}
+
+/**
+ * libie_find_ctlq - find the controlq for the given id and type
+ * @ctx: controlq context structure
+ * @type: type of controlq to find
+ * @id: controlq id to find
+ *
+ * Return: control queue info pointer on success, NULL on failure
+ */
+struct libie_ctlq_info *libie_find_ctlq(struct libie_ctlq_ctx *ctx,
+					enum virtchnl2_queue_type type,
+					int id)
+{
+	struct libie_ctlq_info *cq;
+
+	guard(spinlock)(&ctx->ctlqs_lock);
+
+	list_for_each_entry(cq, &ctx->ctlqs, list)
+		if (cq->qid == id && cq->type == type)
+			return cq;
+
+	return NULL;
+}
+EXPORT_SYMBOL_NS_GPL(libie_find_ctlq, "LIBIE_CP");
+
+/**
+ * libie_ctlq_add - add one control queue
+ * @ctx: controlq context information
+ * @qinfo: information that requires for queue creation
+ *
+ * Allocate and initialize a control queue and add it to the control queue list.
+ * The ctlq parameter will be allocated/initialized and passed back to the
+ * caller if no errors occur.
+ *
+ * Note: libie_ctlq_init must be called prior to any calls to libie_ctlq_add.
+ *
+ * Return: added control queue info pointer on success, error pointer on failure
+ */
+static struct libie_ctlq_info *
+libie_ctlq_add(struct libie_ctlq_ctx *ctx,
+	       const struct libie_ctlq_create_info *qinfo)
+{
+	struct libie_ctlq_info *ctlq;
+
+	if (qinfo->id != LIBIE_CTLQ_MBX_ID ||
+	    qinfo->len > FIELD_MAX(LIBIE_CTLQ_MBX_ATQ_LEN))
+		return ERR_PTR(-EOPNOTSUPP);
+
+	/* libie_ctlq_init was not called */
+	scoped_guard(spinlock, &ctx->ctlqs_lock)
+		if (!ctx->ctlqs.next)
+			return ERR_PTR(-EINVAL);
+
+	ctlq = kvzalloc_obj(*ctlq);
+	if (!ctlq)
+		return ERR_PTR(-ENOMEM);
+
+	ctlq->type = qinfo->type;
+	ctlq->qid = qinfo->id;
+	ctlq->ring_len = qinfo->len;
+	ctlq->dev = &ctx->mmio_info.pdev->dev;
+	ctlq->reg = qinfo->reg;
+
+	if (libie_ctlq_alloc_queue_res(ctlq)) {
+		kvfree(ctlq);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	libie_ctlq_init_regs(ctlq);
+
+	spin_lock_init(&ctlq->lock);
+
+	scoped_guard(spinlock, &ctx->ctlqs_lock)
+		list_add(&ctlq->list, &ctx->ctlqs);
+
+	return ctlq;
+}
+
+/**
+ * libie_ctlq_remove - deallocate and remove specified control queue
+ * @ctx: libie context information
+ * @ctlq: specific control queue that needs to be removed
+ */
+static void libie_ctlq_remove(struct libie_ctlq_ctx *ctx,
+			      struct libie_ctlq_info *ctlq)
+{
+	scoped_guard(spinlock, &ctx->ctlqs_lock)
+		list_del(&ctlq->list);
+
+	libie_ctlq_dealloc_ring_res(ctlq);
+	kvfree(ctlq);
+}
+
+/**
+ * libie_ctlq_init - main initialization routine for all control queues
+ * @ctx: libie context information
+ * @qinfo: array of structs containing info for each queue to be initialized
+ * @numq: number of queues to initialize
+ *
+ * This initializes queue list and adds any number and any type of control
+ * queues. This is an all or nothing routine; if one fails, all previously
+ * allocated queues will be destroyed. This must be called prior to using
+ * the individual add/remove APIs.
+ *
+ * Return: %0 on success, -%errno on failure
+ */
+int libie_ctlq_init(struct libie_ctlq_ctx *ctx,
+		    const struct libie_ctlq_create_info *qinfo,
+		     u32 numq)
+{
+	INIT_LIST_HEAD(&ctx->ctlqs);
+	spin_lock_init(&ctx->ctlqs_lock);
+
+	for (u32 i = 0; i < numq; i++) {
+		struct libie_ctlq_info *ctlq;
+
+		ctlq = libie_ctlq_add(ctx, &qinfo[i]);
+		if (IS_ERR(ctlq)) {
+			libie_ctlq_deinit(ctx);
+			return PTR_ERR(ctlq);
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_init, "LIBIE_CP");
+
+/**
+ * libie_ctlq_deinit - destroy all control queues
+ * @ctx: libie CP context information
+ */
+void libie_ctlq_deinit(struct libie_ctlq_ctx *ctx)
+{
+	struct libie_ctlq_info *ctlq, *tmp;
+
+	list_for_each_entry_safe(ctlq, tmp, &ctx->ctlqs, list)
+		libie_ctlq_remove(ctx, ctlq);
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_deinit, "LIBIE_CP");
+
+/**
+ * libie_ctlq_tx_desc_from_msg - initialize a Tx descriptor from a message
+ * @desc: descriptor to be initialized
+ * @msg: filled control queue message
+ */
+static void libie_ctlq_tx_desc_from_msg(struct libie_ctlq_desc *desc,
+					const struct libie_ctlq_msg *msg)
+{
+	const struct libie_cp_dma_mem *dma = &msg->send_mem;
+	u64 qword;
+
+	qword = FIELD_PREP(LIBIE_CTLQ_DESC_FLAGS, msg->flags) |
+		FIELD_PREP(LIBIE_CTLQ_DESC_INFRA_OPCODE, msg->opcode) |
+		FIELD_PREP(LIBIE_CTLQ_DESC_PFID_VFID, msg->func_id);
+	desc->qword0 = cpu_to_le64(qword);
+
+	qword = FIELD_PREP(LIBIE_CTLQ_DESC_VIRTCHNL_OPCODE,
+			   msg->chnl_opcode) |
+		FIELD_PREP(LIBIE_CTLQ_DESC_VIRTCHNL_MSG_RET_VAL,
+			   msg->chnl_retval);
+	desc->qword1 = cpu_to_le64(qword);
+
+	qword = FIELD_PREP(LIBIE_CTLQ_DESC_MSG_PARAM0, msg->param0) |
+		FIELD_PREP(LIBIE_CTLQ_DESC_SW_COOKIE,
+			   msg->sw_cookie) |
+		FIELD_PREP(LIBIE_CTLQ_DESC_VIRTCHNL_FLAGS,
+			   msg->virt_flags);
+	desc->qword2 = cpu_to_le64(qword);
+
+	if (likely(msg->data_len)) {
+		desc->qword0 |=
+			cpu_to_le64(LIBIE_CTLQ_DESC_QWORD0(msg->data_len));
+		qword = FIELD_PREP(LIBIE_CTLQ_DESC_DATA_ADDR_HIGH,
+				   upper_32_bits(dma->pa)) |
+			FIELD_PREP(LIBIE_CTLQ_DESC_DATA_ADDR_LOW,
+				   lower_32_bits(dma->pa));
+	} else {
+		qword = msg->addr_param;
+	}
+
+	desc->qword3 = cpu_to_le64(qword);
+}
+
+/**
+ * libie_ctlq_send_desc_avail - get number of free descriptors on a Tx ctlq
+ * @ctlq: specific control queue which is going be used for sending messages
+ *
+ * The caller must hold ctlq->lock. Any dependent sending must be done
+ * in the same critical section.
+ *
+ * Return: number of available descriptors/messages on a given control queue.
+ */
+u32 libie_ctlq_send_desc_avail(const struct libie_ctlq_info *ctlq)
+{
+	u32 ntu = ctlq->next_to_use, ntc = ctlq->next_to_clean;
+
+	return (ntc > ntu ? 0 : ctlq->ring_len) + ntc - ntu - 1;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_send_desc_avail, "LIBIE_CP");
+
+/**
+ * libie_ctlq_send - send a message to Control Plane or Peer
+ * @ctlq: specific control queue which is used for sending a message
+ * @num_q_msg: number of messages present to send on @ctlq,
+ *	       positive and no greater than the number of available descriptors
+ *
+ * The caller must fill in @num_q_msg Tx messages starting at ntu beforehand.
+ *
+ * The caller must hold ctlq->lock. The intended pattern is to first check
+ * the number of descriptors available, then fill in the messages and perform
+ * send within a single critical section.
+ */
+void libie_ctlq_send(struct libie_ctlq_info *ctlq, u32 num_q_msg)
+{
+	u32 ntu = ctlq->next_to_use;
+
+	for (int i = 0; i < num_q_msg; i++) {
+		struct libie_ctlq_msg *msg = ctlq->tx_msg[ntu];
+		struct libie_ctlq_desc *desc;
+
+		desc = &ctlq->descs[ntu];
+		libie_ctlq_tx_desc_from_msg(desc, msg);
+
+		if (unlikely(++ntu == ctlq->ring_len))
+			ntu = 0;
+	}
+	dma_wmb();
+	writel(ntu, ctlq->reg.tail);
+	ctlq->next_to_use = ntu;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_send, "LIBIE_CP");
+
+/**
+ * libie_ctlq_fill_rx_msg - fill in a message from Rx descriptor and buffer
+ * @msg: message to be filled in
+ * @desc: received descriptor
+ * @rx_buf: fill queue buffer associated with the descriptor
+ */
+static void libie_ctlq_fill_rx_msg(struct libie_ctlq_msg *msg,
+				   const struct libie_ctlq_desc *desc,
+				    struct libeth_fqe *rx_buf)
+{
+	u64 qword = le64_to_cpu(desc->qword0);
+
+	msg->flags = FIELD_GET(LIBIE_CTLQ_DESC_FLAGS, qword);
+	msg->opcode = FIELD_GET(LIBIE_CTLQ_DESC_INFRA_OPCODE, qword);
+	msg->data_len = FIELD_GET(LIBIE_CTLQ_DESC_DATA_LEN, qword);
+	msg->hw_retval = FIELD_GET(LIBIE_CTLQ_DESC_HW_RETVAL, qword);
+
+	qword = le64_to_cpu(desc->qword1);
+	msg->chnl_opcode =
+		FIELD_GET(LIBIE_CTLQ_DESC_VIRTCHNL_OPCODE, qword);
+	msg->chnl_retval =
+		FIELD_GET(LIBIE_CTLQ_DESC_VIRTCHNL_MSG_RET_VAL, qword);
+
+	qword = le64_to_cpu(desc->qword2);
+	msg->param0 =
+		FIELD_GET(LIBIE_CTLQ_DESC_MSG_PARAM0, qword);
+	msg->sw_cookie =
+		FIELD_GET(LIBIE_CTLQ_DESC_SW_COOKIE, qword);
+	msg->virt_flags =
+		FIELD_GET(LIBIE_CTLQ_DESC_VIRTCHNL_FLAGS, qword);
+
+	if (likely(msg->data_len)) {
+		if (unlikely(msg->data_len > LIBIE_CTLQ_MAX_BUF_LEN)) {
+			msg->data_len = LIBIE_CTLQ_MAX_BUF_LEN;
+			msg->chnl_retval = U32_MAX;
+		}
+		msg->recv_mem = (struct kvec) {
+			.iov_base = netmem_address(rx_buf->netmem) +
+				    rx_buf->offset,
+			.iov_len = msg->data_len,
+		};
+		libeth_rx_sync_for_cpu(rx_buf, msg->data_len);
+	} else {
+		msg->recv_mem = (struct kvec) {};
+		msg->addr_param = le64_to_cpu(desc->qword3);
+		page_pool_put_full_netmem(netmem_get_pp(rx_buf->netmem),
+					  rx_buf->netmem, false);
+	}
+}
+
+/**
+ * libie_ctlq_recv - receive control queue message call back
+ * @ctlq: control queue that needs to processed for receive
+ * @msg: array of received control queue messages on this q;
+ *	 needs to be pre-allocated by caller for as many messages as requested
+ * @num_q_msg: number of messages that can be stored in msg buffer,
+ *	       no greater than number of posted buffers
+ *
+ * Caller is expected to free buffers.
+ *
+ * The caller must make sure that calls to libie_ctlq_post_rx_buffs()
+ * and libie_ctlq_recv() for each queue are either serialized
+ * or used under ctlq->lock.
+ *
+ * Return: number of messages received
+ */
+u32 libie_ctlq_recv(struct libie_ctlq_info *ctlq, struct libie_ctlq_msg *msg,
+		    u32 num_q_msg)
+{
+	u32 ntc, i;
+
+	ntc = ctlq->next_to_clean;
+
+	for (i = 0; i < num_q_msg; i++) {
+		const struct libie_ctlq_desc *desc = &ctlq->descs[ntc];
+		struct libeth_fqe *rx_buf = &ctlq->rx_fqes[ntc];
+		u64 qword;
+
+		qword = le64_to_cpu(desc->qword0);
+		if (!FIELD_GET(LIBIE_CTLQ_DESC_FLAG_DD, qword))
+			break;
+
+		dma_rmb();
+
+		libie_ctlq_fill_rx_msg(&msg[i], desc, rx_buf);
+
+		if (unlikely(++ntc == ctlq->ring_len))
+			ntc = 0;
+	}
+
+	ctlq->next_to_clean = ntc;
+
+	return i;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_recv, "LIBIE_CP");
+
+MODULE_DESCRIPTION("Control Plane communication API");
+MODULE_IMPORT_NS("LIBETH");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/net/intel/libie/controlq.h b/include/linux/net/intel/libie/controlq.h
new file mode 100644
index 000000000000..a176a0d2ba33
--- /dev/null
+++ b/include/linux/net/intel/libie/controlq.h
@@ -0,0 +1,249 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2025 Intel Corporation */
+
+#ifndef __LIBIE_CONTROLQ_H
+#define __LIBIE_CONTROLQ_H
+
+#include <net/libeth/rx.h>
+
+#include <linux/net/intel/libie/pci.h>
+#include <linux/net/intel/virtchnl2.h>
+
+/* Default mailbox control queue */
+#define LIBIE_CTLQ_MBX_ID			-1
+#define LIBIE_CTLQ_MAX_BUF_LEN			SZ_4K
+
+#define LIBIE_CTLQ_TYPE_TX			0
+#define LIBIE_CTLQ_TYPE_RX			1
+
+/* Opcode used to send controlq message to the control plane */
+#define LIBIE_CTLQ_SEND_MSG_TO_CP		0x801
+#define LIBIE_CTLQ_SEND_MSG_TO_PEER		0x804
+
+/**
+ * struct libie_ctlq_ctx - contains controlq info and MMIO region info
+ * @mmio_info: MMIO region info structure
+ * @ctlqs: list that stores all the control queues
+ * @ctlqs_lock: lock for control queue list
+ */
+struct libie_ctlq_ctx {
+	struct libie_mmio_info	mmio_info;
+	struct list_head	ctlqs;
+	spinlock_t		ctlqs_lock;	/* protects the ctlqs list */
+};
+
+/**
+ * struct libie_ctlq_reg - structure representing virtual addresses of the
+ *			    controlq registers and masks
+ * @head: controlq head register address
+ * @tail: controlq tail register address
+ * @len: register address to write controlq length and enable bit
+ * @addr_high: register address to write the upper 32b of ring physical address
+ * @addr_low: register address to write the lower 32b of ring physical address
+ * @len_mask: mask to read the controlq length
+ * @len_ena_mask: mask to write the controlq enable bit
+ * @head_mask: mask to read the head value
+ */
+struct libie_ctlq_reg {
+	void __iomem	*head;
+	void __iomem	*tail;
+	void __iomem	*len;
+	void __iomem	*addr_high;
+	void __iomem	*addr_low;
+	u32		len_mask;
+	u32		len_ena_mask;
+	u32		head_mask;
+};
+
+/**
+ * struct libie_cp_dma_mem - structure for DMA memory
+ * @va: virtual address
+ * @pa: physical address
+ * @size: memory size
+ */
+struct libie_cp_dma_mem {
+	void		*va;
+	dma_addr_t	pa;
+	size_t		size;
+};
+
+/**
+ * struct libie_ctlq_msg - control queue message data
+ * @flags: refer to 'Flags sub-structure' definitions
+ * @opcode: infrastructure message opcode
+ * @data_len: size of the payload
+ * @func_id: queue id for the secondary mailbox queue, 0 for default mailbox
+ * @hw_retval: execution status from the HW
+ * @chnl_opcode: virtchnl message opcode
+ * @chnl_retval: virtchnl return value
+ * @param0: indirect message raw parameter0
+ * @sw_cookie: used to verify the response of the sent virtchnl message
+ * @virt_flags: virtchnl capability flags
+ * @addr_param: additional parameters in place of the address, given no buffer
+ * @recv_mem: virtual address and size of the buffer that contains
+ *	      the indirect response
+ * @send_mem: physical and virtual address of the DMA buffer,
+ *	      used for sending
+ */
+struct libie_ctlq_msg {
+	u16			flags;
+	u16			opcode;
+	u16			data_len;
+	union {
+		u16		func_id;
+		u16		hw_retval;
+	};
+	u32			chnl_opcode;
+	u32			chnl_retval;
+	u32			param0;
+	u16			sw_cookie;
+	u16			virt_flags;
+	u64			addr_param;
+	union {
+		struct kvec	recv_mem;
+		struct	libie_cp_dma_mem send_mem;
+	};
+};
+
+/**
+ * struct libie_ctlq_create_info - control queue create information
+ * @type: control queue type (Rx or Tx)
+ * @id: queue offset passed as input, -1 for default mailbox
+ * @reg: registers accessed by control queue
+ * @len: controlq length
+ */
+struct libie_ctlq_create_info {
+	enum virtchnl2_queue_type	type;
+	int				id;
+	struct libie_ctlq_reg		reg;
+	u16				len;
+};
+
+/**
+ * struct libie_ctlq_info - control queue information
+ * @list: used to add a controlq to the list of queues in libie_ctlq_ctx
+ * @type: control queue type
+ * @qid: queue identifier
+ * @lock: control queue lock
+ * @ring_mem: descriptor ring DMA memory
+ * @descs: array of descriptors
+ * @rx_fqes: array of controlq Rx buffers
+ * @tx_msg: Tx messages sent to hardware
+ * @reg: registers used by control queue
+ * @dev: device that owns this control queue
+ * @pp: page pool for controlq Rx buffers
+ * @truesize: size to allocate per buffer
+ * @next_to_use: next available slot to send buffer
+ * @next_to_clean: next descriptor to be cleaned
+ * @next_to_post: next available slot to post buffers to after receive
+ * @ring_len: length of the descriptor ring
+ */
+struct libie_ctlq_info {
+	struct list_head		list;
+	enum virtchnl2_queue_type	type;
+	int				qid;
+	spinlock_t			lock;	/* for concurrent processing */
+	struct libie_cp_dma_mem	ring_mem;
+	struct libie_ctlq_desc		*descs;
+	union {
+		struct libeth_fqe		*rx_fqes;
+		struct libie_ctlq_msg		**tx_msg;
+	};
+	struct libie_ctlq_reg		reg;
+	struct device			*dev;
+	struct page_pool		*pp;
+	u32				truesize;
+	u32				next_to_clean;
+	union {
+		u32			next_to_use;
+		u32			next_to_post;
+	};
+	u32				ring_len;
+};
+
+#define LIBIE_CTLQ_MBX_ATQ_LEN			GENMASK(9, 0)
+
+/* Flags sub-structure
+ * |0  |1  |2  |3  |4  |5  |6  |7  |8  |9  |10 |11 |12 |13 |14 |15 |
+ * |DD |CMP|ERR|  * RSV *  |FTYPE  | *RSV* |RD |VFC|BUF|  HOST_ID  |
+ */
+ /* libie controlq descriptor qword0 details */
+#define LIBIE_CTLQ_DESC_FLAG_DD		BIT(0)
+#define LIBIE_CTLQ_DESC_FLAG_CMP		BIT(1)
+#define LIBIE_CTLQ_DESC_FLAG_ERR		BIT(2)
+#define LIBIE_CTLQ_DESC_FLAG_FTYPE_VM		BIT(6)
+#define LIBIE_CTLQ_DESC_FLAG_FTYPE_PF		BIT(7)
+#define LIBIE_CTLQ_DESC_FLAG_FTYPE		GENMASK(7, 6)
+#define LIBIE_CTLQ_DESC_FLAG_RD		BIT(10)
+#define LIBIE_CTLQ_DESC_FLAG_VFC		BIT(11)
+#define LIBIE_CTLQ_DESC_FLAG_BUF		BIT(12)
+#define LIBIE_CTLQ_DESC_FLAG_HOST_ID		GENMASK(15, 13)
+
+#define LIBIE_CTLQ_DESC_FLAGS			GENMASK(15, 0)
+#define LIBIE_CTLQ_DESC_INFRA_OPCODE		GENMASK_ULL(31, 16)
+#define LIBIE_CTLQ_DESC_DATA_LEN		GENMASK_ULL(47, 32)
+#define LIBIE_CTLQ_DESC_HW_RETVAL		GENMASK_ULL(63, 48)
+
+#define LIBIE_CTLQ_DESC_PFID_VFID		GENMASK_ULL(63, 48)
+
+/* libie controlq descriptor qword1 details */
+#define LIBIE_CTLQ_DESC_VIRTCHNL_OPCODE	GENMASK(27, 0)
+#define LIBIE_CTLQ_DESC_VIRTCHNL_DESC_TYPE	GENMASK_ULL(31, 28)
+#define LIBIE_CTLQ_DESC_VIRTCHNL_MSG_RET_VAL	GENMASK_ULL(63, 32)
+
+/* libie controlq descriptor qword2 details */
+#define LIBIE_CTLQ_DESC_MSG_PARAM0		GENMASK_ULL(31, 0)
+#define LIBIE_CTLQ_DESC_SW_COOKIE		GENMASK_ULL(47, 32)
+#define LIBIE_CTLQ_DESC_VIRTCHNL_FLAGS		GENMASK_ULL(63, 48)
+
+/* libie controlq descriptor qword3 details */
+#define LIBIE_CTLQ_DESC_DATA_ADDR_HIGH		GENMASK_ULL(31, 0)
+#define LIBIE_CTLQ_DESC_DATA_ADDR_LOW		GENMASK_ULL(63, 32)
+
+/**
+ * struct libie_ctlq_desc - control queue descriptor format
+ * @qword0: flags, message opcode, data length etc
+ * @qword1: virtchnl opcode, descriptor type and return value
+ * @qword2: indirect message parameters
+ * @qword3: indirect message buffer address
+ */
+struct libie_ctlq_desc {
+	__le64			qword0;
+	__le64			qword1;
+	__le64			qword2;
+	__le64			qword3;
+};
+
+/**
+ * libie_ctlq_release_rx_buf - Release Rx buffer for a specific control queue
+ * @rx_buf: Rx buffer to be freed
+ *
+ * Driver uses this function to post back the Rx buffer after the usage.
+ */
+static inline void libie_ctlq_release_rx_buf(struct kvec *rx_buf)
+{
+	netmem_ref netmem;
+
+	if (!rx_buf->iov_base)
+		return;
+
+	netmem = virt_to_netmem(rx_buf->iov_base);
+	page_pool_put_full_netmem(netmem_get_pp(netmem), netmem, false);
+}
+
+int libie_ctlq_init(struct libie_ctlq_ctx *ctx,
+		    const struct libie_ctlq_create_info *qinfo,  u32 numq);
+void libie_ctlq_deinit(struct libie_ctlq_ctx *ctx);
+
+struct libie_ctlq_info *libie_find_ctlq(struct libie_ctlq_ctx *ctx,
+					enum virtchnl2_queue_type type,
+					  int id);
+
+u32 libie_ctlq_send_desc_avail(const struct libie_ctlq_info *ctlq);
+void libie_ctlq_send(struct libie_ctlq_info *ctlq, u32 num_q_msg);
+u32 libie_ctlq_recv(struct libie_ctlq_info *ctlq, struct libie_ctlq_msg *msg,
+		    u32 num_q_msg);
+
+int libie_ctlq_post_rx_buffs(struct libie_ctlq_info *ctlq);
+
+#endif /* __LIBIE_CONTROLQ_H */
-- 
2.47.1


^ permalink raw reply related

* [PATCH net-next v5 05/15] libie: add bookkeeping support for control queue messages
From: Tony Nguyen @ 2026-07-15 18:00 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Phani R Burra, anthony.l.nguyen, larysa.zaremba,
	przemyslaw.kitszel, aleksander.lobakin, sridhar.samudrala,
	michal.swiatkowski, maciej.fijalkowski, emil.s.tantilov,
	madhu.chittim, joshua.a.hay, jacob.e.keller,
	jayaprakash.shanmugam, jiri, horms, corbet, richardcochran, skhan,
	linux-doc, Bharath R, Samuel Salin
In-Reply-To: <20260715180042.1972010-1-anthony.l.nguyen@intel.com>

From: Phani R Burra <phani.r.burra@intel.com>

Small send control queue message buffers are managed and reused by
libie itself, bigger send buffers are consumed. All are tracked with
the unique transaction (Xn) ids until they receive response or time out.

Responses can be received out of order, therefore transactions are stored
in an array and tracked though a bitmap. Rx buffers utilize page_pool.

Pre-allocated DMA memory is used where possible. It reduces the driver
overhead in handling memory allocation/free and message timeouts.

Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Signed-off-by: Phani R Burra <phani.r.burra@intel.com>
Co-developed-by: Victor Raj <victor.raj@intel.com>
Signed-off-by: Victor Raj <victor.raj@intel.com>
Co-developed-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Tested-by: Bharath R <bharath.r@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Co-developed-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
---
 drivers/net/ethernet/intel/libie/controlq.c | 607 ++++++++++++++++++++
 include/linux/net/intel/libie/controlq.h    | 175 ++++++
 2 files changed, 782 insertions(+)

diff --git a/drivers/net/ethernet/intel/libie/controlq.c b/drivers/net/ethernet/intel/libie/controlq.c
index a3aa241f03b9..0a179957bfef 100644
--- a/drivers/net/ethernet/intel/libie/controlq.c
+++ b/drivers/net/ethernet/intel/libie/controlq.c
@@ -611,6 +611,613 @@ u32 libie_ctlq_recv(struct libie_ctlq_info *ctlq, struct libie_ctlq_msg *msg,
 }
 EXPORT_SYMBOL_NS_GPL(libie_ctlq_recv, "LIBIE_CP");
 
+/**
+ * libie_ctlq_xn_pop_free - get a free Xn entry from the free list
+ * @xnm: Xn transaction manager
+ *
+ * Retrieve a free Xn entry from the free list.
+ *
+ * Return: valid Xn entry pointer or NULL if there are no free Xn entries.
+ */
+static struct libie_ctlq_xn *
+libie_ctlq_xn_pop_free(struct libie_ctlq_xn_manager *xnm)
+{
+	struct libie_ctlq_xn *xn;
+	u32 free_idx;
+
+	guard(spinlock)(&xnm->free_xns_bm_lock);
+
+	if (unlikely(xnm->shutdown))
+		return NULL;
+
+	free_idx = find_next_bit(xnm->free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES,
+				 0);
+	if (free_idx == LIBIE_CTLQ_MAX_XN_ENTRIES)
+		return NULL;
+
+	__clear_bit(free_idx, xnm->free_xns_bm);
+	xn = &xnm->ring[free_idx];
+	xn->cookie = xnm->cookie++;
+
+	return xn;
+}
+
+/**
+ * __libie_ctlq_xn_push_free - unsafely push a Xn entry into the free list
+ * @xnm: Xn transaction manager
+ * @xn: xn entry to be added into the free list
+ *
+ * Return: whether xnm destruction can be triggered by the caller
+ */
+static bool __libie_ctlq_xn_push_free(struct libie_ctlq_xn_manager *xnm,
+				      struct libie_ctlq_xn *xn)
+{
+	__set_bit(xn->index, xnm->free_xns_bm);
+
+	if (unlikely(xnm->shutdown) &&
+	    bitmap_full(xnm->free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES))
+		return true;
+
+	return false;
+}
+
+/**
+ * libie_ctlq_xn_push_free - push a Xn entry into the free list
+ * @xnm: Xn transaction manager
+ * @xn: xn entry to be added into the free list, not locked
+ *
+ * Safely add a used Xn entry back to the free list.
+ */
+static void libie_ctlq_xn_push_free(struct libie_ctlq_xn_manager *xnm,
+				    struct libie_ctlq_xn *xn)
+{
+	bool can_destroy;
+
+	scoped_guard(spinlock, &xnm->free_xns_bm_lock)
+		can_destroy = __libie_ctlq_xn_push_free(xnm, xn);
+
+	if (can_destroy)
+		complete(&xnm->can_destroy);
+}
+
+/**
+ * libie_ctlq_xn_deinit_dma - free the DMA memory allocated for send messages
+ * @dev: device pointer
+ * @xnm: pointer to the transaction manager
+ * @num_entries: number of Xn entries to free the DMA for
+ */
+static void libie_ctlq_xn_deinit_dma(struct device *dev,
+				     struct libie_ctlq_xn_manager *xnm,
+				     u32 num_entries)
+{
+	for (u32 i = 0; i < num_entries; i++) {
+		struct libie_ctlq_xn *xn = &xnm->ring[i];
+
+		libie_cp_free_dma_mem(dev, xn->dma_mem);
+		kfree(xn->dma_mem);
+	}
+}
+
+/**
+ * libie_ctlq_xn_init_dma - pre-allocate DMA memory for send messages that use
+ * stack variables
+ * @dev: device pointer
+ * @xnm: pointer to transaction manager
+ *
+ * Return: %0 on success or error if memory allocation fails
+ */
+static int libie_ctlq_xn_init_dma(struct device *dev,
+				  struct libie_ctlq_xn_manager *xnm)
+{
+	u32 i;
+
+	for (i = 0; i < LIBIE_CTLQ_MAX_XN_ENTRIES; i++) {
+		struct libie_ctlq_xn *xn = &xnm->ring[i];
+		struct libie_cp_dma_mem *dma_mem;
+
+		dma_mem = kzalloc_obj(*dma_mem);
+		if (!dma_mem)
+			goto dealloc_dma;
+
+		dma_mem->va = libie_cp_alloc_dma_mem(dev, dma_mem,
+						     LIBIE_CP_TX_COPYBREAK);
+		if (!dma_mem->va) {
+			kfree(dma_mem);
+			goto dealloc_dma;
+		}
+
+		xn->dma_mem = dma_mem;
+	}
+
+	return 0;
+
+dealloc_dma:
+	libie_ctlq_xn_deinit_dma(dev, xnm, i);
+
+	return -ENOMEM;
+}
+
+/**
+ * libie_ctlq_xn_process_recv - process Xn data in receive message
+ * @params: Xn receive param information to handle a receive message
+ * @ctlq_msg: received control queue message
+ *
+ * Process a control queue receive message and send a complete event
+ * notification.
+ *
+ * Return: true if a message has been processed, false otherwise.
+ */
+static bool
+libie_ctlq_xn_process_recv(struct libie_ctlq_xn_recv_params *params,
+			   struct libie_ctlq_msg *ctlq_msg)
+{
+	struct libie_ctlq_xn_manager *xnm = params->xnm;
+	struct libie_ctlq_xn *xn;
+	u16 msg_cookie, xn_index;
+	struct kvec *response;
+	int status;
+	u16 data;
+
+	data = ctlq_msg->sw_cookie;
+	xn_index = FIELD_GET(LIBIE_CTLQ_XN_INDEX_M, data);
+	msg_cookie = FIELD_GET(LIBIE_CTLQ_XN_COOKIE_M, data);
+	status = ctlq_msg->chnl_retval ? -EFAULT : 0;
+
+	xn = &xnm->ring[xn_index];
+	spin_lock(&xn->xn_lock);
+	if (ctlq_msg->chnl_opcode != xn->virtchnl_opcode ||
+	    msg_cookie != xn->cookie) {
+		spin_unlock(&xn->xn_lock);
+		return false;
+	}
+
+	if (xn->state != LIBIE_CTLQ_XN_ASYNC &&
+	    xn->state != LIBIE_CTLQ_XN_WAITING) {
+		spin_unlock(&xn->xn_lock);
+		return false;
+	}
+
+	response = &ctlq_msg->recv_mem;
+	if (xn->state == LIBIE_CTLQ_XN_ASYNC) {
+		xn->resp_cb(xn->send_ctx, response, status);
+		libie_ctlq_release_rx_buf(response);
+		xn->state = LIBIE_CTLQ_XN_IDLE;
+		spin_unlock(&xn->xn_lock);
+		libie_ctlq_xn_push_free(xnm, xn);
+
+		return true;
+	}
+
+	xn->recv_mem = *response;
+	xn->state = status ? LIBIE_CTLQ_XN_COMPLETED_FAILED :
+			     LIBIE_CTLQ_XN_COMPLETED_SUCCESS;
+
+	complete(&xn->cmd_completion_event);
+	spin_unlock(&xn->xn_lock);
+
+	return true;
+}
+
+/**
+ * libie_xn_check_async_timeout - Check for asynchronous message timeouts
+ * @xnm: Xn transaction manager
+ *
+ * Call the corresponding callback to notify the caller about the timeout.
+ */
+static void libie_xn_check_async_timeout(struct libie_ctlq_xn_manager *xnm)
+{
+	u32 idx;
+
+	for_each_clear_bit(idx, xnm->free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES) {
+		struct libie_ctlq_xn *xn = &xnm->ring[idx];
+		u64 timeout_ms;
+
+		spin_lock(&xn->xn_lock);
+
+		timeout_ms = ktime_ms_delta(ktime_get(), xn->timestamp);
+		if (xn->state != LIBIE_CTLQ_XN_ASYNC ||
+		    timeout_ms < xn->timeout_ms) {
+			spin_unlock(&xn->xn_lock);
+			continue;
+		}
+
+		xn->resp_cb(xn->send_ctx, NULL, -ETIMEDOUT);
+		xn->state = LIBIE_CTLQ_XN_IDLE;
+		spin_unlock(&xn->xn_lock);
+		libie_ctlq_xn_push_free(xnm, xn);
+	}
+}
+
+/**
+ * libie_ctlq_xn_recv - process control queue receive message
+ * @params: Xn receive param information to handle a receive message
+ *
+ * Process a receive message and update the receive queue buffer.
+ *
+ * Return: remaining budget.
+ */
+u32 libie_ctlq_xn_recv(struct libie_ctlq_xn_recv_params *params)
+{
+	struct libie_ctlq_msg ctlq_msg;
+	u32 budget = params->budget;
+
+	while (budget && libie_ctlq_recv(params->ctlq, &ctlq_msg, 1)) {
+		budget--;
+		if (!libie_ctlq_xn_process_recv(params, &ctlq_msg))
+			params->ctlq_msg_handler(params->xnm->ctx, &ctlq_msg);
+	}
+
+	libie_ctlq_post_rx_buffs(params->ctlq);
+	libie_xn_check_async_timeout(params->xnm);
+
+	return budget;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_recv, "LIBIE_CP");
+
+/**
+ * libie_cp_map_dma_mem - map a given virtual address for DMA
+ * @dev: device information
+ * @va: virtual address to be mapped
+ * @size: size of the memory
+ * @direction: DMA direction either from/to device
+ * @dma_mem: memory for DMA information to be stored
+ *
+ * Return: true on success, false on DMA map failure.
+ */
+static bool libie_cp_map_dma_mem(struct device *dev, void *va, size_t size,
+				 int direction,
+				  struct libie_cp_dma_mem *dma_mem)
+{
+	dma_mem->pa = dma_map_single(dev, va, size, direction);
+
+	return dma_mapping_error(dev, dma_mem->pa) ? false : true;
+}
+
+/**
+ * libie_cp_unmap_dma_mem - unmap previously mapped DMA address
+ * @dev: device information
+ * @dma_mem: DMA memory information
+ */
+static void libie_cp_unmap_dma_mem(struct device *dev,
+				   const struct libie_cp_dma_mem *dma_mem)
+{
+	dma_unmap_single(dev, dma_mem->pa, dma_mem->size,
+			 dma_mem->direction);
+}
+
+/**
+ * libie_ctlq_xn_process_send - process and send a control queue message
+ * @params: Xn send param information for sending a control queue message
+ * @xn: Assigned Xn entry for tracking the control queue message
+ *
+ * Return: %0 on success, -%errno on failure.
+ */
+static
+int libie_ctlq_xn_process_send(struct libie_ctlq_xn_send_params *params,
+			       struct libie_ctlq_xn *xn)
+{
+	size_t buf_len = params->send_buf.iov_len;
+	struct device *dev = params->ctlq->dev;
+	void *buf = params->send_buf.iov_base;
+	struct libie_cp_dma_mem *dma_mem;
+	u16 cookie;
+
+	if (!buf || !buf_len)
+		return -EOPNOTSUPP;
+
+	if (libie_cp_can_send_onstack(buf_len)) {
+		dma_mem = xn->dma_mem;
+		memcpy(dma_mem->va, buf, buf_len);
+	} else {
+		dma_mem = &xn->send_dma_mem;
+		dma_mem->va = buf;
+		dma_mem->size = buf_len;
+		dma_mem->direction = DMA_TO_DEVICE;
+
+		if (!libie_cp_map_dma_mem(dev, buf, buf_len, DMA_TO_DEVICE,
+					  dma_mem))
+			return -ENOMEM;
+	}
+
+	cookie = FIELD_PREP(LIBIE_CTLQ_XN_COOKIE_M, xn->cookie) |
+		 FIELD_PREP(LIBIE_CTLQ_XN_INDEX_M, xn->index);
+
+	scoped_guard(spinlock, &params->ctlq->lock) {
+		struct libie_ctlq_info *ctlq = params->ctlq;
+		struct libie_ctlq_msg *ctlq_msg;
+
+		if (!libie_ctlq_send_desc_avail(ctlq)) {
+			if (!libie_cp_can_send_onstack(buf_len))
+				libie_cp_unmap_dma_mem(dev, dma_mem);
+
+			return -EBUSY;
+		}
+
+		ctlq_msg = ctlq->tx_msg[ctlq->next_to_use];
+		if (params->ctlq_msg)
+			*ctlq_msg = *params->ctlq_msg;
+		else
+			/* Unused ctlq messages are already zeroed */
+			ctlq_msg->opcode = LIBIE_CTLQ_SEND_MSG_TO_CP;
+
+		ctlq_msg->sw_cookie = cookie;
+		ctlq_msg->send_mem = *dma_mem;
+		ctlq_msg->data_len = buf_len;
+		ctlq_msg->chnl_opcode = params->chnl_opcode;
+		libie_ctlq_send(params->ctlq, 1);
+	}
+
+	return 0;
+}
+
+/**
+ * libie_ctlq_xn_send - Function to send a control queue message
+ * @params: Xn send param information for sending a control queue message
+ *
+ * Send a control queue (mailbox or config) message.
+ * Based on the params value, the call can be completed synchronously or
+ * asynchronously.
+ *
+ * Return: %0 on success, -%errno on failure.
+ */
+int libie_ctlq_xn_send(struct libie_ctlq_xn_send_params *params)
+{
+	bool free_send = !libie_cp_can_send_onstack(params->send_buf.iov_len);
+	struct libie_ctlq_xn *xn;
+	int ret;
+
+	if (params->send_buf.iov_len > LIBIE_CTLQ_MAX_BUF_LEN) {
+		ret = -EINVAL;
+		goto free_buf;
+	}
+
+	xn = libie_ctlq_xn_pop_free(params->xnm);
+	/* no free transactions available */
+	if (unlikely(!xn)) {
+		ret = -EAGAIN;
+		goto free_buf;
+	}
+
+	spin_lock(&xn->xn_lock);
+	if (xn->state == LIBIE_CTLQ_XN_SHUTDOWN) {
+		ret = -ENXIO;
+		goto unlock_xn;
+	}
+
+	xn->state = params->resp_cb ? LIBIE_CTLQ_XN_ASYNC :
+				      LIBIE_CTLQ_XN_WAITING;
+	xn->ctlq = params->ctlq;
+	xn->virtchnl_opcode = params->chnl_opcode;
+
+	if (params->resp_cb) {
+		xn->send_ctx = params->send_ctx;
+		xn->resp_cb = params->resp_cb;
+		xn->timeout_ms = params->timeout_ms;
+		xn->timestamp = ktime_get();
+	}
+
+	ret = libie_ctlq_xn_process_send(params, xn);
+	if (ret)
+		goto release_xn;
+	else
+		free_send = false;
+
+	spin_unlock(&xn->xn_lock);
+
+	if (params->resp_cb)
+		return 0;
+
+	wait_for_completion_timeout(&xn->cmd_completion_event,
+				    msecs_to_jiffies(params->timeout_ms));
+
+	spin_lock(&xn->xn_lock);
+	switch (xn->state) {
+	case LIBIE_CTLQ_XN_WAITING:
+		ret = -ETIMEDOUT;
+		break;
+	case LIBIE_CTLQ_XN_COMPLETED_SUCCESS:
+		params->recv_mem = xn->recv_mem;
+		break;
+	default:
+		ret = -EBADMSG;
+		break;
+	}
+
+	/* Free the receive buffer in case of failure. On timeout, receive
+	 * buffer is not allocated.
+	 */
+	if (ret && ret != -ETIMEDOUT)
+		libie_ctlq_release_rx_buf(&xn->recv_mem);
+
+release_xn:
+	xn->state = LIBIE_CTLQ_XN_IDLE;
+	reinit_completion(&xn->cmd_completion_event);
+unlock_xn:
+	spin_unlock(&xn->xn_lock);
+	libie_ctlq_xn_push_free(params->xnm, xn);
+free_buf:
+	if (free_send)
+		params->rel_tx_buf(params->send_buf.iov_base);
+
+	return ret;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_send, "LIBIE_CP");
+
+/**
+ * libie_ctlq_xn_send_clean - cleanup the send control queue message buffers
+ * @params: Xn clean param information for send complete handling
+ *
+ * Cleanup the send buffers for the given control queue, if force is set, then
+ * clear all the outstanding send messages irrespective their send status.
+ * Force should be used during deinit or reset.
+ *
+ * Return: number of send buffers cleaned.
+ */
+u32 libie_ctlq_xn_send_clean(const struct libie_ctlq_xn_clean_params *params)
+{
+	struct libie_ctlq_info *ctlq = params->ctlq;
+	struct device *dev = ctlq->dev;
+	u32 ntc, i;
+
+	spin_lock(&ctlq->lock);
+	ntc = ctlq->next_to_clean;
+
+	for (i = 0; i < params->num_msgs; i++) {
+		struct libie_ctlq_msg *msg = ctlq->tx_msg[ntc];
+		struct libie_ctlq_desc *desc;
+		u64 qword;
+
+		desc = &ctlq->descs[ntc];
+		qword = le64_to_cpu(desc->qword0);
+
+		if (!FIELD_GET(LIBIE_CTLQ_DESC_FLAG_DD, qword) &&
+		    !(unlikely(params->force) && msg->data_len))
+			break;
+
+		desc->qword0 = 0;
+
+		dma_mb();
+
+		if (!libie_cp_can_send_onstack(msg->data_len)) {
+			libie_cp_unmap_dma_mem(dev, &msg->send_mem);
+			params->rel_tx_buf(msg->send_mem.va);
+		}
+
+		memset(msg, 0, sizeof(*msg));
+
+		if (unlikely(++ntc == ctlq->ring_len))
+			ntc = 0;
+	}
+
+	ctlq->next_to_clean = ntc;
+	spin_unlock(&ctlq->lock);
+
+	return i;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_send_clean, "LIBIE_CP");
+
+/**
+ * libie_ctlq_xn_shutdown - terminate control queue transactions
+ * @xnm: pointer to the transaction manager
+ *
+ * Synchronously terminate existing transactions and stop accepting new ones.
+ */
+void libie_ctlq_xn_shutdown(struct libie_ctlq_xn_manager *xnm)
+{
+	bool must_wait = false;
+	u32 i;
+
+	/* Should be no new clear bits after this */
+	spin_lock(&xnm->free_xns_bm_lock);
+	xnm->shutdown = true;
+
+	for_each_clear_bit(i, xnm->free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES) {
+		struct libie_ctlq_xn *xn = &xnm->ring[i];
+
+		spin_lock(&xn->xn_lock);
+
+		switch (xn->state) {
+		/* if an idle xn is not free, it is about to be either
+		 * freed or initialized, prevent the latter and wait
+		 */
+		case LIBIE_CTLQ_XN_IDLE:
+			xn->state = LIBIE_CTLQ_XN_SHUTDOWN;
+			fallthrough;
+		/* waiting thread possibly needs a push to return the xn,
+		 * transaction will be reported as timed out
+		 */
+		case LIBIE_CTLQ_XN_WAITING:
+			complete(&xn->cmd_completion_event);
+			fallthrough;
+		/* these states will return the xn soon */
+		case LIBIE_CTLQ_XN_COMPLETED_SUCCESS:
+		case LIBIE_CTLQ_XN_COMPLETED_FAILED:
+		case LIBIE_CTLQ_XN_SHUTDOWN:
+			must_wait = true;
+			break;
+		/* no thread should reference async xns at this point */
+		case LIBIE_CTLQ_XN_ASYNC:
+			xn->state = LIBIE_CTLQ_XN_IDLE;
+			__libie_ctlq_xn_push_free(xnm, xn);
+			break;
+		}
+
+		spin_unlock(&xn->xn_lock);
+	}
+
+	spin_unlock(&xnm->free_xns_bm_lock);
+
+	if (must_wait)
+		wait_for_completion(&xnm->can_destroy);
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_shutdown, "LIBIE_CP");
+
+/**
+ * libie_ctlq_xn_deinit - deallocate and free the transaction manager resources
+ * @xnm: pointer to the transaction manager
+ * @ctx: controlq context structure
+ *
+ * All Rx processing must be stopped beforehand.
+ */
+void libie_ctlq_xn_deinit(struct libie_ctlq_xn_manager *xnm,
+			  struct libie_ctlq_ctx *ctx)
+{
+	libie_ctlq_xn_shutdown(xnm);
+	libie_ctlq_xn_deinit_dma(&ctx->mmio_info.pdev->dev, xnm,
+				 LIBIE_CTLQ_MAX_XN_ENTRIES);
+	kfree(xnm);
+	libie_ctlq_deinit(ctx);
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_deinit, "LIBIE_CP");
+
+/**
+ * libie_ctlq_xn_init - initialize the Xn transaction manager
+ * @params: Xn init param information for allocating Xn manager resources
+ *
+ * Return: %0 on success, -%errno on failure.
+ */
+int libie_ctlq_xn_init(struct libie_ctlq_xn_init_params *params)
+{
+	struct libie_ctlq_xn_manager *xnm;
+	int ret;
+
+	ret = libie_ctlq_init(params->ctx, params->cctlq_info, params->num_qs);
+	if (ret)
+		return ret;
+
+	xnm = kzalloc_obj(*xnm);
+	if (!xnm)
+		goto ctlq_deinit;
+
+	ret = libie_ctlq_xn_init_dma(&params->ctx->mmio_info.pdev->dev, xnm);
+	if (ret)
+		goto free_xnm;
+
+	spin_lock_init(&xnm->free_xns_bm_lock);
+	init_completion(&xnm->can_destroy);
+	bitmap_fill(xnm->free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES);
+
+	for (u32 i = 0; i < LIBIE_CTLQ_MAX_XN_ENTRIES; i++) {
+		struct libie_ctlq_xn *xn = &xnm->ring[i];
+
+		xn->index = i;
+		init_completion(&xn->cmd_completion_event);
+		spin_lock_init(&xn->xn_lock);
+	}
+	xnm->ctx = params->ctx;
+	params->xnm = xnm;
+
+	return 0;
+
+free_xnm:
+	kfree(xnm);
+ctlq_deinit:
+	libie_ctlq_deinit(params->ctx);
+
+	return -ENOMEM;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_init, "LIBIE_CP");
+
 MODULE_DESCRIPTION("Control Plane communication API");
 MODULE_IMPORT_NS("LIBETH");
 MODULE_LICENSE("GPL");
diff --git a/include/linux/net/intel/libie/controlq.h b/include/linux/net/intel/libie/controlq.h
index a176a0d2ba33..5c574c8b8a79 100644
--- a/include/linux/net/intel/libie/controlq.h
+++ b/include/linux/net/intel/libie/controlq.h
@@ -20,6 +20,8 @@
 #define LIBIE_CTLQ_SEND_MSG_TO_CP		0x801
 #define LIBIE_CTLQ_SEND_MSG_TO_PEER		0x804
 
+#define LIBIE_CP_TX_COPYBREAK		128
+
 /**
  * struct libie_ctlq_ctx - contains controlq info and MMIO region info
  * @mmio_info: MMIO region info structure
@@ -60,11 +62,13 @@ struct libie_ctlq_reg {
  * @va: virtual address
  * @pa: physical address
  * @size: memory size
+ * @direction: memory to device or device to memory
  */
 struct libie_cp_dma_mem {
 	void		*va;
 	dma_addr_t	pa;
 	size_t		size;
+	int		direction;
 };
 
 /**
@@ -246,4 +250,175 @@ u32 libie_ctlq_recv(struct libie_ctlq_info *ctlq, struct libie_ctlq_msg *msg,
 
 int libie_ctlq_post_rx_buffs(struct libie_ctlq_info *ctlq);
 
+/* Only 8 bits are available in descriptor for Xn index */
+#define LIBIE_CTLQ_MAX_XN_ENTRIES		256
+#define LIBIE_CTLQ_XN_COOKIE_M			GENMASK(15, 8)
+#define LIBIE_CTLQ_XN_INDEX_M			GENMASK(7, 0)
+
+/**
+ * enum libie_ctlq_xn_state - Transaction state of a virtchnl message
+ * @LIBIE_CTLQ_XN_IDLE: transaction is available to use
+ * @LIBIE_CTLQ_XN_WAITING: waiting for transaction to complete
+ * @LIBIE_CTLQ_XN_COMPLETED_SUCCESS: transaction completed with success
+ * @LIBIE_CTLQ_XN_COMPLETED_FAILED: transaction completed with failure
+ * @LIBIE_CTLQ_XN_ASYNC: asynchronous virtchnl message transaction type
+ * @LIBIE_CTLQ_XN_SHUTDOWN: transaction cannot be used anymore
+ */
+enum libie_ctlq_xn_state {
+	LIBIE_CTLQ_XN_IDLE = 0,
+	LIBIE_CTLQ_XN_WAITING,
+	LIBIE_CTLQ_XN_COMPLETED_SUCCESS,
+	LIBIE_CTLQ_XN_COMPLETED_FAILED,
+	LIBIE_CTLQ_XN_ASYNC,
+	LIBIE_CTLQ_XN_SHUTDOWN,
+};
+
+/**
+ * struct libie_ctlq_xn - structure representing a virtchnl transaction entry
+ * @resp_cb: callback to handle the response of an asynchronous virtchnl message
+ * @xn_lock: lock to protect the transaction entry state
+ * @ctlq: send control queue information
+ * @cmd_completion_event: signal when a reply is available
+ * @dma_mem: DMA memory of send buffer that use stack variable
+ * @send_dma_mem: DMA memory of send buffer
+ * @recv_mem: receive buffer
+ * @send_ctx: context for callback function
+ * @timeout_ms: Xn transaction timeout in msecs
+ * @timestamp: timestamp to record the Xn send
+ * @virtchnl_opcode: virtchnl command opcode used for Xn transaction
+ * @state: transaction state of a virtchnl message
+ * @cookie: unique message identifier
+ * @index: index of the transaction entry
+ */
+struct libie_ctlq_xn {
+	void (*resp_cb)(void *ctx, struct kvec *mem, int status);
+	spinlock_t			xn_lock;	/* protects state */
+	struct libie_ctlq_info		*ctlq;
+	struct completion		cmd_completion_event;
+	struct libie_cp_dma_mem	*dma_mem;
+	struct libie_cp_dma_mem	send_dma_mem;
+	struct kvec			recv_mem;
+	void				*send_ctx;
+	u64				timeout_ms;
+	ktime_t				timestamp;
+	u32				virtchnl_opcode;
+	enum libie_ctlq_xn_state	state;
+	u8				cookie;
+	u8				index;
+};
+
+/**
+ * struct libie_ctlq_xn_manager - structure representing the array of virtchnl
+ *				   transaction entries
+ * @ctx: pointer to controlq context structure
+ * @free_xns_bm_lock: lock to protect the free Xn entries bit map
+ * @free_xns_bm: bitmap that represents the free Xn entries
+ * @ring: array of Xn entries
+ * @can_destroy: completion triggered by the last returned transaction
+ * @shutdown: shows the transactions the xnm shutdown is waiting for them
+ * @cookie: unique message identifier
+ */
+struct libie_ctlq_xn_manager {
+	struct libie_ctlq_ctx	*ctx;
+	spinlock_t		free_xns_bm_lock;	/* get/check entries */
+	DECLARE_BITMAP(free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES);
+	struct libie_ctlq_xn	ring[LIBIE_CTLQ_MAX_XN_ENTRIES];
+	struct completion	can_destroy;
+	bool			shutdown;
+	u8			cookie;
+};
+
+/**
+ * struct libie_ctlq_xn_send_params - structure representing send Xn entry
+ * @resp_cb: callback to handle the response of an asynchronous virtchnl message
+ * @rel_tx_buf: driver entry point for freeing the send buffer after send
+ * @xnm: Xn manager to process Xn entries
+ * @ctlq: send control queue information
+ * @ctlq_msg: control queue message information
+ * @send_buf: represents the buffer that carries outgoing information
+ * @recv_mem: receive buffer
+ * @send_ctx: context for call back function
+ * @timeout_ms: virtchnl transaction timeout in msecs
+ * @chnl_opcode: virtchnl message opcode
+ */
+struct libie_ctlq_xn_send_params {
+	void (*resp_cb)(void *ctx, struct kvec *mem, int status);
+	void (*rel_tx_buf)(const void *buf_va);
+	struct libie_ctlq_xn_manager		*xnm;
+	struct libie_ctlq_info			*ctlq;
+	struct libie_ctlq_msg			*ctlq_msg;
+	struct kvec				send_buf;
+	struct kvec				recv_mem;
+	void					*send_ctx;
+	u64					timeout_ms;
+	u32					chnl_opcode;
+};
+
+/**
+ * libie_cp_can_send_onstack - can a message be sent using a stack variable
+ * @size: ctlq data buffer size
+ *
+ * Return: %true if the message size is small enough for caller to pass
+ *	   an on-stack buffer, %false if kmalloc is needed
+ */
+static inline bool libie_cp_can_send_onstack(u32 size)
+{
+	return size <= LIBIE_CP_TX_COPYBREAK;
+}
+
+/**
+ * struct libie_ctlq_xn_recv_params - structure representing receive Xn entry
+ * @ctlq_msg_handler: callback to handle a message originated from the peer
+ * @xnm: Xn manager to process Xn entries
+ * @ctlq: control queue information
+ * @budget: maximum number of messages to process
+ */
+struct libie_ctlq_xn_recv_params {
+	void (*ctlq_msg_handler)(struct libie_ctlq_ctx *ctx,
+				 struct libie_ctlq_msg *msg);
+	struct libie_ctlq_xn_manager		*xnm;
+	struct libie_ctlq_info			*ctlq;
+	u32					budget;
+};
+
+/**
+ * struct libie_ctlq_xn_clean_params - Data structure used for cleaning the
+ * control queue messages
+ * @rel_tx_buf: driver entry point for freeing the send buffer after send
+ * @ctlq: control queue information
+ * @send_ctx: context for call back function
+ * @num_msgs: number of messages to be cleaned
+ * @force: clean even if DD is not yet set
+ */
+struct libie_ctlq_xn_clean_params {
+	void (*rel_tx_buf)(const void *buf_va);
+	struct libie_ctlq_info			*ctlq;
+	void					*send_ctx;
+	u16					num_msgs;
+	bool					force;
+};
+
+/**
+ * struct libie_ctlq_xn_init_params - Data structure used for initializing the
+ * Xn transaction manager
+ * @cctlq_info: control queue information
+ * @ctx: pointer to controlq context structure
+ * @xnm: Xn manager to process Xn entries
+ * @num_qs: number of control queues needs to initialized
+ */
+struct libie_ctlq_xn_init_params {
+	struct libie_ctlq_create_info		*cctlq_info;
+	struct libie_ctlq_ctx			*ctx;
+	struct libie_ctlq_xn_manager		*xnm;
+	u32					num_qs;
+};
+
+int libie_ctlq_xn_init(struct libie_ctlq_xn_init_params *params);
+void libie_ctlq_xn_deinit(struct libie_ctlq_xn_manager *xnm,
+			  struct libie_ctlq_ctx *ctx);
+void libie_ctlq_xn_shutdown(struct libie_ctlq_xn_manager *xnm);
+int libie_ctlq_xn_send(struct libie_ctlq_xn_send_params *params);
+u32 libie_ctlq_xn_recv(struct libie_ctlq_xn_recv_params *params);
+u32 libie_ctlq_xn_send_clean(const struct libie_ctlq_xn_clean_params *params);
+
 #endif /* __LIBIE_CONTROLQ_H */
-- 
2.47.1


^ permalink raw reply related

* [PATCH net-next v5 06/15] idpf: remove 'vport_params_reqd' field
From: Tony Nguyen @ 2026-07-15 18:00 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Pavan Kumar Linga, anthony.l.nguyen, larysa.zaremba,
	przemyslaw.kitszel, aleksander.lobakin, sridhar.samudrala,
	michal.swiatkowski, maciej.fijalkowski, emil.s.tantilov,
	joshua.a.hay, jacob.e.keller, jayaprakash.shanmugam, jiri, horms,
	corbet, richardcochran, skhan, linux-doc, Samuel Salin
In-Reply-To: <20260715180042.1972010-1-anthony.l.nguyen@intel.com>

From: Pavan Kumar Linga <pavan.kumar.linga@intel.com>

While sending a create vport message to the device control plane, a create
vport virtchnl message is prepared with all the required info to initialize
the vport. This info is stored in the adapter struct but never used
thereafter. So, remove the said field.

Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Reviewed-by: Madhu Chittim <madhu.chittim@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
---
 drivers/net/ethernet/intel/idpf/idpf.h        |  2 --
 drivers/net/ethernet/intel/idpf/idpf_lib.c    |  2 --
 .../net/ethernet/intel/idpf/idpf_virtchnl.c   | 30 +++++++------------
 3 files changed, 10 insertions(+), 24 deletions(-)

diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index 984944bab28b..c5e47e79a641 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -638,7 +638,6 @@ struct idpf_vc_xn_manager;
  * @avail_queues: Device given queue limits
  * @vports: Array to store vports created by the driver
  * @netdevs: Associated Vport netdevs
- * @vport_params_reqd: Vport params requested
  * @vport_params_recvd: Vport params received
  * @vport_ids: Array of device given vport identifiers
  * @singleq_pt_lkup: Lookup table for singleq RX ptypes
@@ -697,7 +696,6 @@ struct idpf_adapter {
 	struct idpf_avail_queue_info avail_queues;
 	struct idpf_vport **vports;
 	struct net_device **netdevs;
-	struct virtchnl2_create_vport **vport_params_reqd;
 	struct virtchnl2_create_vport **vport_params_recvd;
 	u32 *vport_ids;
 
diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
index cf966fe6c759..d88ca59edf97 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
@@ -1109,8 +1109,6 @@ static void idpf_vport_rel(struct idpf_vport *vport)
 
 	kfree(adapter->vport_params_recvd[idx]);
 	adapter->vport_params_recvd[idx] = NULL;
-	kfree(adapter->vport_params_reqd[idx]);
-	adapter->vport_params_reqd[idx] = NULL;
 
 	kfree(vport);
 	adapter->num_alloc_vports--;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index be66f9b2e101..d4546d62cca9 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -1557,14 +1557,10 @@ int idpf_send_create_vport_msg(struct idpf_adapter *adapter,
 	ssize_t reply_sz;
 
 	buf_size = sizeof(struct virtchnl2_create_vport);
-	if (!adapter->vport_params_reqd[idx]) {
-		adapter->vport_params_reqd[idx] = kzalloc(buf_size,
-							  GFP_KERNEL);
-		if (!adapter->vport_params_reqd[idx])
-			return -ENOMEM;
-	}
+	vport_msg = kzalloc(buf_size, GFP_KERNEL);
+	if (!vport_msg)
+		return -ENOMEM;
 
-	vport_msg = adapter->vport_params_reqd[idx];
 	vport_msg->vport_type = cpu_to_le16(VIRTCHNL2_VPORT_TYPE_DEFAULT);
 	vport_msg->vport_index = cpu_to_le16(idx);
 
@@ -1581,8 +1577,7 @@ int idpf_send_create_vport_msg(struct idpf_adapter *adapter,
 	err = idpf_vport_calc_total_qs(adapter, idx, vport_msg, max_q);
 	if (err) {
 		dev_err(&adapter->pdev->dev, "Enough queues are not available");
-
-		return err;
+		goto rel_buf;
 	}
 
 	if (!adapter->vport_params_recvd[idx]) {
@@ -1590,7 +1585,7 @@ int idpf_send_create_vport_msg(struct idpf_adapter *adapter,
 							   GFP_KERNEL);
 		if (!adapter->vport_params_recvd[idx]) {
 			err = -ENOMEM;
-			goto free_vport_params;
+			goto rel_buf;
 		}
 	}
 
@@ -1606,13 +1601,15 @@ int idpf_send_create_vport_msg(struct idpf_adapter *adapter,
 		goto free_vport_params;
 	}
 
+	kfree(vport_msg);
+
 	return 0;
 
 free_vport_params:
 	kfree(adapter->vport_params_recvd[idx]);
 	adapter->vport_params_recvd[idx] = NULL;
-	kfree(adapter->vport_params_reqd[idx]);
-	adapter->vport_params_reqd[idx] = NULL;
+rel_buf:
+	kfree(vport_msg);
 
 	return err;
 }
@@ -3418,8 +3415,6 @@ static void idpf_vport_params_buf_rel(struct idpf_adapter *adapter)
 {
 	kfree(adapter->vport_params_recvd);
 	adapter->vport_params_recvd = NULL;
-	kfree(adapter->vport_params_reqd);
-	adapter->vport_params_reqd = NULL;
 	kfree(adapter->vport_ids);
 	adapter->vport_ids = NULL;
 }
@@ -3434,15 +3429,10 @@ static int idpf_vport_params_buf_alloc(struct idpf_adapter *adapter)
 {
 	u16 num_max_vports = idpf_get_max_vports(adapter);
 
-	adapter->vport_params_reqd = kzalloc_objs(*adapter->vport_params_reqd,
-						  num_max_vports);
-	if (!adapter->vport_params_reqd)
-		return -ENOMEM;
-
 	adapter->vport_params_recvd = kzalloc_objs(*adapter->vport_params_recvd,
 						   num_max_vports);
 	if (!adapter->vport_params_recvd)
-		goto err_mem;
+		return -ENOMEM;
 
 	adapter->vport_ids = kcalloc(num_max_vports, sizeof(u32), GFP_KERNEL);
 	if (!adapter->vport_ids)
-- 
2.47.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox