[BUG] ath12k: Oops in cfg80211_register_wdev on single-wiphy IPQ9570

顾彬 gubin at hzcctech.net
Fri Oct 16 07:46:21 PDT 2026


Hello,

I am reporting a reproducible kernel Oops on an Askey SBE1V1K (Qualcomm
IPQ9570, 3x QCN9274 hw2.0 over PCIe, Wi-Fi 7 / BE19000) running a self-built
OpenWrt snapshot.

ath12k probes cleanly and exposes all three bands, but ANY attempt to
create a WiFi interface crashes the kernel. The call trace never enters
ath12k - it dies in the standard cfg80211 -> kernfs path, when
cfg80211_register_wdev() tries to create the "phy80211" sysfs symlink
for the newly registered netdev.

Root cause (verified in source AND in disassembly of the same vmlinux):
kernfs_root(parent) returns NULL, i.e. the netdev's sysfs node does not appear
to be linked into a kernfs tree. That NULL is then used as `root` and
dereferenced as spin_lock(&root->kernfs_idr_lock).


DEVICE / SOFTWARE
-----------------
  Device      : Askey SBE1V1K (Spectrum retail unit)
  SoC         : Qualcomm IPQ9570 (DT model "Askey SBE1V1K", askey,sbe1v1k)
  Radios      : 3x QCN9274 hw2.0 on PCIe
                (0001:01:00.0, 0002:01:00.0, 0003:01:00.0)
  RAM / flash : 2 GiB / 8 GiB eMMC
  Firmware    : OpenWrt SNAPSHOT, self-built from
                OneNAS-space/Askey_Spectrum_SBE1V1K
  Kernel      : 6.18.55 (aarch64, OpenWrt GCC 14.4.0 toolchain-55f076e4f3)
  mac80211    : backports via OpenWrt package/kernel/mac80211
                (kmod-ath12k / kmod-ath12k-wifi7 /
                 kmod-mac80211 / kmod-cfg80211 = 6.18.55.7.2-r5)
  ath12k FW   : WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
                (fw_version 0x160484db, built 2025-12-09)
  Board data  : ipq-wifi-askey_sbe1v1k 2026.10.14~8818bb39-r1
  wpad        : wpad-basic-mbedtls 2026.08.07~831364bf-r4
  Cmdline     : console=ttyMSM0,115200n8 rootwait
                root=/dev/mmcblk0p27 cma=512M

  NOTE ON CMA: the stock OpenWrt snapshot kernel for this target is built
  WITHOUT CONFIG_CMA, which makes ath12k fail much earlier
  ("qmi dma allocation failed (29360128 B)" / "failed to connect to WMI
  CONTROL service -71"). The image used here is a self-built kernel WITH
  CONFIG_CMA and cma=512M on the cmdline. That is a prerequisite to even reach
  the failure described below.


WHAT WORKS
----------
  - all 3 PCIe radios bind and load firmware; no errors in dmesg
  - iw phy wl0 info reports 3 bands (Band 1 / 2 / 4 = 2.4 / 5 / 6 GHz)
  - cal-pci-000{1,2,3}:01:00.0.bin are all generated
  - board-2.bin variants match the DTS qcom,calibration-variant
    entries exactly:
        board-2.bin: bus=pci,qmi-chip-id=0,qmi-board-id=255
                     variant=Askey-SBE1V1K_1
                     variant=Askey-SBE1V1K_2
                     variant=Askey-SBE1V1K_4
        DTS        : qcom,calibration-variant =
                     "Askey-SBE1V1K_1" / "_4" / "_2"
    (qmi-board-id=255 / board_id 0xff is this board's intended
     value, not an error)
  - CmaTotal 524288 kB / CmaFree 277504 kB
  - "phy count = 1" is NORMAL here: ath12k Wi-Fi 7 uses a
    single-wiphy / multi-radio model, all radios share one phy named
    wl0. The correct metric is the Band count from `iw phy wl0 info`,
    which is 3.

  Single-wiphy mapping seen on the device:
      /sys/class/ieee80211/wl0 ->
        ../../devices/platform/soc at 0/20000000.pcie/
          pci0002:00/0002:00:00.0/0002:01:00.0/ieee80211/wl0

  ubus call network.wireless status:
      radio0: band=2g ch=1  htmode=EHT20
      radio1: band=6g ch=1  htmode=EHT80
      radio2: band=5g ch=36 htmode=EHT80
      all three: "phy": "wl0", "up": true


WHAT FAILS
----------
  - `iw dev` is always EMPTY - no interface is ever created
  - ANY NL80211_CMD_NEW_INTERFACE crashes, including the minimal case:
        iw phy wl0 interface add mon0 type monitor
    hostapd bringing up an AP interface crashes identically; the last
    log lines before the Oops are:
        hostapd: Set new config for phy wl0.2: /var/run/hostapd-wl0.2.conf
        hostapd: Restart interface for phy wl0.2
        <OOPS>
  - After the Oops the kernel LEAKS RTNL - an unrelated operation such as
        ip link add dummy0 type dummy
    hangs forever (no output, no error). Only a reboot recovers.
    With panic_on_oops=1 (OpenWrt default) the board just reboots instead and
    the ring buffer is lost.


THE OOPS
--------
Unable to handle kernel access to user memory outside uaccess
routines at virtual address 0000000000000028
Mem abort info:
  ESR = 0x0000000096000005
  EC = 0x25: DABT (current EL), IL = 32 bits
  SET = 0, FnV = 0
  EA = 0, S1PTW = 0
  FSC = 0x05: level 1 translation fault
Data abort info:
  ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000
  CM = 0, WnR = 0, TnD = 0, TagAccess = 0
  GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
user pgtable: 4k pages, 39-bit VAs, pgdp=0000000046968000
 [0000000000000028] pgd=0000000000000000,
   p4d=0000000000000000, pud=0000000000000000
Internal error: Oops: 0000000096000005 [#1]  SMP
Modules linked in: pppoe ppp_async nft_flow_offload ...
  ath12k_wifi7(O) ath12k(O) ... mac80211(O) ... cfg80211(O) ...
  qrtr_mhi qrtr qmi_helpers(O) ... pwm_fan ...
CPU: 2 UID: 101 PID: 1872 Comm: hostapd
Tainted: G           O        6.18.55 #0 NONE
Hardware name: Askey SBE1V1K (DT)
pstate: 20400005 (nzCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : __kernfs_new_node.isra.0+0x80/0x214
lr : __kernfs_new_node.isra.0+0x68/0x214
sp : ffffffc08bf7b5e0
x29: ffffffc08bf7b5e0 x28: 0000000000000001 x27: ffffffc08bf7b8b0
x26: 000000000000a1ff x25: ffffffc080c28000 x24: 0000000000000004
x23: 0000000000000000 x22: 0000000000000000 x21: ffffff8012e99be0
x20: 0000000000000000 x19: ffffff8007ae7c38 x18: 0000000000000020
x17: 42555300302d7874 x16: 2f7365756575712f x15: 0008000500000000
x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000001
x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000
x8 : ffffff8007ae7cc0 x7 : 0000000000000000 x6 : 000000000000003f
x5 : 0000000000000040 x4 : 0000000000000028 x3 : 0000000000000004
x2 : 0000000000000001 x1 : 0000000000000028 x0 : 0000000000000000
Call trace:
 __kernfs_new_node.isra.0+0x80/0x214 (P)
 kernfs_new_node+0x48/0x80
 kernfs_create_link+0xc0/0x100
 sysfs_do_create_link_sd+0x88/0x120
 sysfs_create_link+0x20/0x40
 cfg80211_register_wdev+0x98/0x110      [cfg80211]
 cfg80211_register_netdevice+0x70/0xe4  [cfg80211]
 ieee80211_if_add+0x560/0x6a0           [mac80211]
 ieee80211_add_iface+0x44/0x104         [mac80211]
 nl80211_new_interface+0x1b8/0x52c      [cfg80211]
 genl_family_rcv_msg_doit+0xcc/0x140
 genl_rcv_msg+0x1b8/0x250
 netlink_rcv_skb+0x60/0x130
 genl_rcv+0x38/0x50
 netlink_unicast+0x210/0x308
 netlink_sendmsg+0x178/0x380
 ____sys_sendmsg+0x1e8/0x250
 ___sys_sendmsg+0x80/0xc8
 __sys_sendmsg+0x60/0xc4
 __arm64_sys_sendmsg+0x24/0x34
 invoke_syscall.constprop.0+0x4c/0xc0
 do_el0_svc+0x40/0xc0
 el0_svc+0x24/0xd0
 el0t_64_sync_handler+0xa0/0xf0
 el0t_64_sync+0x180/0x184
Code: aa0103e4 52800022 f90037e1 f9800091 (885ffc81)
---[ end trace 0000000000000000 ]---


ANALYSIS (source + disassembly of the very same vmlinux)
--------------------------------------------------------
Crash site, net/wireless/core.c, cfg80211_register_wdev():

    if (wdev->netdev &&
        sysfs_create_link(&wdev->netdev->dev.kobj, &rdev->wiphy.dev.kobj,
                          "phy80211"))
            pr_err("failed to add phy80211 symlink to netdev!\n");

Note this call has a GRACEFUL error path - it should never crash.

fs/sysfs/symlink.c, sysfs_do_create_link_sd():

    if (WARN_ON(!name || !parent))
            return -EINVAL;              /* parent == NULL -> graceful */
    ...
    if (!target)
            return -ENOENT;              /* invalid target -> graceful */
    kn = kernfs_create_link(parent, name, target);

Both arguments are validated with graceful exits, yet the kernel crashes, so
neither is NULL - the node itself must be invalid rather than absent.

fs/kernfs/dir.c, kernfs_new_node():

    kn = __kernfs_new_node(kernfs_root(parent), parent,
                           name, mode, uid, gid, flags);

Disassembly of __kernfs_new_node.isra.0 (0xffffffc0802c046c):

    ffffffc0802c047c:  mov  x20, x0            ; x20 = arg0 = root
    ffffffc0802c04d4:  add  x1, x20, #0x28
                       ; x1 = &root->kernfs_idr_lock  (LR = +0x68)
    ffffffc0802c04dc:  mov  x4, x1
    ffffffc0802c04e0:  mov  w2, #0x1
    ffffffc0802c04e4:  str  x1, [sp, #104]
    ffffffc0802c04e8:  prfm pstl1strm, [x4]
    ffffffc0802c04ec:  ldaxr w1, [x4]          ; <-- fault, PC = +0x80
    ffffffc0802c04f0:  eor  w3, w1, w0         ; x0 = 0
    ffffffc0802c04f8:  stxr w3, w2, [x4]       ; queued_spin_lock fast path

Register dump matches exactly: x20 = 0 (root == NULL), x1 = x4 = 0x28,
x0 = 0, and x26 = 0xa1ff = S_IFLNK|0777 (confirming the symlink path).

struct kernfs_root (6.18) puts kernfs_idr_lock exactly at offset 0x28:

    struct kernfs_root {
            struct kernfs_node      *kn;              /* 0x00 */
            unsigned int            flags;            /* 0x08 */
            struct idr              ino_idr;          /* 0x10, 0x18 bytes */
            spinlock_t              kernfs_idr_lock;  /* 0x28  <-- */
            u32                     last_id_lowbits;
            u32                     id_highbits;
            ...
    };

And kernfs_new_node (0xffffffc0802c1380) inlines kernfs_root() as:

    ffffffc0802c13b4:  ldr  x0, [x19, #8]      ; x0 = parent->__parent
    ffffffc0802c13b8:  cmp  x0, #0x0
    ffffffc0802c13bc:  csel x0, x0, x19, ne
                       ; x0 = parent->__parent ?: parent
    ffffffc0802c13c0:  ldr  x0, [x0, #80]
                       ; x0 = x0->dir.root  (offset 0x50)
    ffffffc0802c13c4:  bl   __kernfs_new_node.isra.0

i.e.  kernfs_root(parent) == (parent->__parent ?: parent)->dir.root
and dir.root is only populated once the node is actually linked into a kernfs
tree.

CONCLUSION: at cfg80211_register_wdev() time the netdev's sysfs node
(wdev->netdev->dev.kobj.sd) is not properly linked into sysfs, so
kernfs_root() returns NULL and that NULL is dereferenced as a spinlock.


RULED OUT
---------
  - ath12k driver fault ....... call trace never enters ath12k
  - hostapd / AP config ....... `iw phy wl0 interface add mon0 type monitor`
                                crashes identically (no hostapd involved)
  - BDF / calibration ......... board-2.bin variants match DTS; all three
                                cal-pci-*.bin generated;
                                board_id 0xff is intended
  - CMA / DMA ................. CmaTotal 524288 kB, CmaFree 277504 kB;
                                ath12k probes with zero errors
  - firmware version .......... loads on all 3 radios
  - regdb / 6 GHz ............. crashes on 5 GHz (wl0.2) and on the band-less
                                monitor iftype alike
  - interface type ............ AP and monitor both crash at the same site


QUESTIONS
---------
  1. Has anyone seen kernfs_root() == NULL (dir.root == 0) when cfg80211
     creates the "phy80211" symlink on an ath12k SINGLE-WIPHY multi-radio
     device? Is there a known ordering/lifetime issue between
     register_netdevice()/netdev_register_kobject() and
     cfg80211_register_wdev() for the composite wiphy case?
  2. Is this related to the upstream series
     "wifi: ath12k: Create symlink for each radio in a wiphy" (ath12k ML,
     2026-04)? That one addresses duplicate debugfs symlinks under
     single-wiphy; our failure is in sysfs, not debugfs, but the theme
     (per-radio symlinks in a single-wiphy setup) seems adjacent.
  3. Are there recommended patches or a known-good mac80211/backports
     combination for 3x QCN9274 hw2.0 in a single wiphy on IPQ957x?
  4. Is there a way to make ath12k register MULTIPLE WIPHYS (one per radio)
     instead of one composite wiphy, as a workaround?


DEBUGGING NOTE (may help others capture this class of Oops on OpenWrt)
----------------------------------------------------------------------
OpenWrt makes this Oops almost impossible to capture by default:
  - its logd drains the kernel ring buffer (SYSLOG_ACTION_READ_CLEAR), so
    `dmesg` only shows the boot-time portion (0-17 s here);
  - setting system. at system[0].log_file persisted only the first 5 lines of the
    Oops;
  - panic_on_oops defaults to 1, so the board reboots and the buffer is wiped.
What works:
    /etc/init.d/log stop
    echo 0 > /proc/sys/kernel/panic_on_oops
    <trigger>
    dmesg | grep -A 160 'MARKER'
The device stays up afterwards (it just leaks RTNL), which is how the full
backtrace above was obtained.

Any pointers would be greatly appreciated. The device is available for further
testing and I can provide more logs on request.



More information about the ath12k mailing list