<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Kernel on 猫猫鱼的小窝</title>
    <link>https://csdn.fjh1997.top/tags/kernel/</link>
    <description>Recent content from 猫猫鱼的小窝</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    
    <managingEditor>xxx@example.com (catcatyu)</managingEditor>
    <webMaster>xxx@example.com (catcatyu)</webMaster>
    
    <copyright>本博客所有文章除特别声明外，均采用 BY-NC-SA 许可协议。转载请注明出处！</copyright>
    
    <lastBuildDate>Sun, 08 Mar 2026 22:00:00 +0800</lastBuildDate>
    
    
    <atom:link href="https://csdn.fjh1997.top/tags/kernel/atom.xml" rel="self" type="application/rss&#43;xml" />
    

    
    

    <item>
      <title>Fenvi AX1800 MT7921AU 在monitor mode下 监听不到beacon管理帧BUG修复</title>
      <link>https://csdn.fjh1997.top/posts/20260308.html</link>
      <pubDate>Sun, 08 Mar 2026 22:00:00 &#43;0800</pubDate>
      <author>xxx@example.com (catcatyu)</author>
      <guid>https://csdn.fjh1997.top/posts/20260308.html</guid>
      <description>
        <![CDATA[<h1>Fenvi AX1800 MT7921AU 在monitor mode下 监听不到beacon管理帧BUG修复</h1><p>作者：catcatyu（xxx@example.com）</p>
        
          <h2 id="起因">
<a class="header-anchor" href="#%e8%b5%b7%e5%9b%a0"></a>
起因
</h2><p>我有一个 Fenvi AX1800 USB 无线网卡（芯片 MediaTek MT7921AU，驱动 mt76），平时用来做无线安全研究。某天升级内核后发现，网卡在监控模式下抓不到管理帧了。</p>
<p>具体表现是：当无线网卡同时存在 managed 接口和 monitor 接口时（即并存模式），monitor 接口完全抓不到任何帧。而在内核 6.12 及以前，这一切都是正常的。</p>
<p>在 GitHub 上搜索发现已经有人报告了同样的问题：<a href="https://github.com/morrownr/USB-WiFi/issues/682" target="_blank" rel="noopener noreferrer nofollow">USB-WiFi#682</a>
，但一直没有修复。于是决定自己动手调试。</p>
<h2 id="环境搭建">
<a class="header-anchor" href="#%e7%8e%af%e5%a2%83%e6%90%ad%e5%bb%ba"></a>
环境搭建
</h2><p>我的调试环境是：</p>
<ul>
<li><strong>宿主机</strong>：Windows + VMware Workstation</li>
<li><strong>虚拟机</strong>：Ubuntu 24.04 LTS（默认内核 6.8）</li>
<li><strong>无线网卡</strong>：Fenvi AX1800（MT7921AU），通过 USB 穿透到虚拟机</li>
<li><strong>内核版本</strong>：通过 Ubuntu Mainline Kernel 安装多个版本进行对比测试</li>
</ul>
<p>选择 Ubuntu 的原因是 <a href="https://kernel.ubuntu.com/mainline/" target="_blank" rel="noopener noreferrer nofollow">Ubuntu Mainline Kernel Archive</a>
 提供了预编译的 <code>.deb</code> 内核包，可以非常方便地安装任意版本的内核来测试，不需要自己从头编译整个内核。</p>
<p>安装方法很简单，以 6.13 为例：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /tmp
</span></span><span class="line"><span class="cl">wget https://kernel.ubuntu.com/mainline/v6.13/amd64/linux-headers-6.13.0-061300-generic_6.13.0-061300.202501192034_amd64.deb
</span></span><span class="line"><span class="cl">wget https://kernel.ubuntu.com/mainline/v6.13/amd64/linux-headers-6.13.0-061300_6.13.0-061300.202501192034_all.deb
</span></span><span class="line"><span class="cl">wget https://kernel.ubuntu.com/mainline/v6.13/amd64/linux-image-unsigned-6.13.0-061300-generic_6.13.0-061300.202501192034_amd64.deb
</span></span><span class="line"><span class="cl">wget https://kernel.ubuntu.com/mainline/v6.13/amd64/linux-modules-6.13.0-061300-generic_6.13.0-061300.202501192034_amd64.deb
</span></span><span class="line"><span class="cl">sudo dpkg -i *.deb
</span></span><span class="line"><span class="cl">sudo reboot
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="基线测试确认-bug-存在">
<a class="header-anchor" href="#%e5%9f%ba%e7%ba%bf%e6%b5%8b%e8%af%95%e7%a1%ae%e8%ae%a4-bug-%e5%ad%98%e5%9c%a8"></a>
基线测试：确认 Bug 存在
</h2><p>先在默认的 6.8 内核上确认网卡功能正常。</p>
<h3 id="测试方法">
<a class="header-anchor" href="#%e6%b5%8b%e8%af%95%e6%96%b9%e6%b3%95"></a>
测试方法
</h3><p>两种场景分别测试：</p>
<p><strong>场景一：纯 Monitor 模式</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ip link <span class="nb">set</span> wlx18d6c70a4715 down
</span></span><span class="line"><span class="cl">sudo iw dev wlx18d6c70a4715 <span class="nb">set</span> <span class="nb">type</span> monitor
</span></span><span class="line"><span class="cl">sudo ip link <span class="nb">set</span> wlx18d6c70a4715 up
</span></span><span class="line"><span class="cl">sudo timeout <span class="m">5</span> tcpdump -i wlx18d6c70a4715 -c <span class="m">50</span> <span class="s1">&#39;type mgt&#39;</span> -n
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>场景二：Managed + Monitor 并存</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ip link <span class="nb">set</span> wlx18d6c70a4715 up  <span class="c1"># managed 模式</span>
</span></span><span class="line"><span class="cl">sudo iw dev wlx18d6c70a4715 interface add mon0 <span class="nb">type</span> monitor
</span></span><span class="line"><span class="cl">sudo ip link <span class="nb">set</span> mon0 up
</span></span><span class="line"><span class="cl">sudo timeout <span class="m">5</span> tcpdump -i mon0 -c <span class="m">50</span> <span class="s1">&#39;type mgt&#39;</span> -n
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="68-基线结果">
<a class="header-anchor" href="#68-%e5%9f%ba%e7%ba%bf%e7%bb%93%e6%9e%9c"></a>
6.8 基线结果
</h3><table>
  <thead>
      <tr>
          <th>场景</th>
          <th>结果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>纯 Monitor</td>
          <td>✅ 正常抓到管理帧</td>
      </tr>
      <tr>
          <td>Managed + Monitor</td>
          <td>✅ 正常抓到管理帧</td>
      </tr>
  </tbody>
</table>
<p>一切正常。然后切换到 6.13 内核。</p>
<h3 id="613-测试结果">
<a class="header-anchor" href="#613-%e6%b5%8b%e8%af%95%e7%bb%93%e6%9e%9c"></a>
6.13 测试结果
</h3><table>
  <thead>
      <tr>
          <th>场景</th>
          <th>结果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>纯 Monitor</td>
          <td>✅ 正常</td>
      </tr>
      <tr>
          <td>Managed + Monitor</td>
          <td>❌ 0 帧</td>
      </tr>
  </tbody>
</table>
<p>Bug 确认。并存模式下完全抓不到帧。</p>
<h2 id="定位根因">
<a class="header-anchor" href="#%e5%ae%9a%e4%bd%8d%e6%a0%b9%e5%9b%a0"></a>
定位根因
</h2><h3 id="快速编译策略">
<a class="header-anchor" href="#%e5%bf%ab%e9%80%9f%e7%bc%96%e8%af%91%e7%ad%96%e7%95%a5"></a>
快速编译策略
</h3><p>调试内核 bug 最头疼的就是编译时间。完整编译一个内核可能需要几十分钟甚至几个小时。但实际上我们只需要修改 <code>mac80211</code> 模块，所以可以只编译这一个模块：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /root/kernel/linux-6.13
</span></span><span class="line"><span class="cl">cp /boot/config-<span class="k">$(</span>uname -r<span class="k">)</span> .config
</span></span><span class="line"><span class="cl">make olddefconfig
</span></span><span class="line"><span class="cl">make modules_prepare
</span></span><span class="line"><span class="cl">cp /lib/modules/<span class="k">$(</span>uname -r<span class="k">)</span>/build/Module.symvers .
</span></span><span class="line"><span class="cl">make <span class="nv">M</span><span class="o">=</span>net/mac80211 modules  <span class="c1"># 只编译 mac80211，大约 30 秒</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>然后热替换模块，不需要重启：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ip link <span class="nb">set</span> wlx18d6c70a4715 down
</span></span><span class="line"><span class="cl">sudo rmmod mt7921u mt7921_common mt792x_lib mt76_connac_lib mt76_usb mt76 mac80211
</span></span><span class="line"><span class="cl">sudo insmod net/mac80211/mac80211.ko
</span></span><span class="line"><span class="cl">sudo modprobe mt7921u
</span></span></code></pre></td></tr></table>
</div>
</div><p>这样每次修改代码到测试验证的周期只需要不到一分钟。</p>
<h3 id="分析代码变更">
<a class="header-anchor" href="#%e5%88%86%e6%9e%90%e4%bb%a3%e7%a0%81%e5%8f%98%e6%9b%b4"></a>
分析代码变更
</h3><p>问题出在 <code>net/mac80211/tx.c</code> 的 <code>ieee80211_monitor_start_xmit()</code> 函数。这是监控模式下发送（注入）帧的入口。</p>
<p>通过 <code>git log</code> 追踪，找到了引入回归的 commit：</p>
<blockquote>
<p><code>0a44dfc07074</code> (&ldquo;wifi: mac80211: simplify non-chanctx drivers&rdquo;)</p>
</blockquote>
<p>这个 commit 在&quot;简化&quot;代码时，删除了一个关键的 fallback 路径。修改前的逻辑（简化表示）：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">chanctx_conf</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">chandef</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">chanctx_conf</span><span class="o">-&gt;</span><span class="n">def</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">chandef</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">local</span><span class="o">-&gt;</span><span class="n">_oper_chandef</span><span class="p">;</span>  <span class="c1">// fallback
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>修改后：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">chanctx_conf</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">chandef</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">chanctx_conf</span><span class="o">-&gt;</span><span class="n">def</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">local</span><span class="o">-&gt;</span><span class="n">emulate_chanctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">chandef</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">local</span><span class="o">-&gt;</span><span class="n">hw</span><span class="p">.</span><span class="n">conf</span><span class="p">.</span><span class="n">chandef</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="k">goto</span> <span class="n">fail_rcu</span><span class="p">;</span>  <span class="c1">// 直接失败！
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>问题在于：对于使用&quot;真正的&quot;channel context 的驱动（如 mt76），当 monitor 接口和 managed 接口并存时，monitor 的虚拟接口（sdata）并不会被分配自己的 chanctx，即使系统中存在一个来自 managed 接口的活跃 chanctx。</p>
<p>之前的代码会 fallback 到 <code>local-&gt;_oper_chandef</code>，所以能正常工作。新代码直接 <code>goto fail_rcu</code>，静默丢弃了所有帧。</p>
<p>后来的 commit <code>d594cc6f2c58</code> (&ldquo;wifi: mac80211: restore non-chanctx injection behaviour&rdquo;) 修复了使用 <code>emulate_chanctx</code> 的驱动，但<strong>明确留下了真正 chanctx 驱动未修复</strong>。</p>
<h3 id="修复方案">
<a class="header-anchor" href="#%e4%bf%ae%e5%a4%8d%e6%96%b9%e6%a1%88"></a>
修复方案
</h3><p>修复思路很直接：当 monitor 接口没有 chanctx 时，从 <code>local-&gt;chanctx_list</code> 中取第一个可用的 chanctx 作为 fallback。这个模式在 <code>ieee80211_hw_conf_chan()</code> 中已经被使用过了，是安全的。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">chanctx_conf</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">chandef</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">chanctx_conf</span><span class="o">-&gt;</span><span class="n">def</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">local</span><span class="o">-&gt;</span><span class="n">emulate_chanctx</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">chandef</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">local</span><span class="o">-&gt;</span><span class="n">hw</span><span class="p">.</span><span class="n">conf</span><span class="p">.</span><span class="n">chandef</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">     * For real chanctx drivers (e.g. mt76), the monitor
</span></span></span><span class="line"><span class="cl"><span class="cm">     * interface may not have a chanctx assigned when running
</span></span></span><span class="line"><span class="cl"><span class="cm">     * concurrently with another interface. Fall back to any
</span></span></span><span class="line"><span class="cl"><span class="cm">     * active chanctx so that injection can still work on the
</span></span></span><span class="line"><span class="cl"><span class="cm">     * operating channel.
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">ieee80211_chanctx</span> <span class="o">*</span><span class="n">ctx</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">ctx</span> <span class="o">=</span> <span class="nf">list_first_entry_or_null</span><span class="p">(</span><span class="o">&amp;</span><span class="n">local</span><span class="o">-&gt;</span><span class="n">chanctx_list</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="k">struct</span> <span class="n">ieee80211_chanctx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="n">list</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">chandef</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">conf</span><span class="p">.</span><span class="n">def</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="k">goto</span> <span class="n">fail_rcu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="跨版本验证">
<a class="header-anchor" href="#%e8%b7%a8%e7%89%88%e6%9c%ac%e9%aa%8c%e8%af%81"></a>
跨版本验证
</h2><p>仅修复一个版本是不够的。我需要确认这个补丁能修复所有受影响的版本。</p>
<h3 id="源码级分析">
<a class="header-anchor" href="#%e6%ba%90%e7%a0%81%e7%ba%a7%e5%88%86%e6%9e%90"></a>
源码级分析
</h3><p>为了避免在每个版本上都走一遍完整的&quot;安装内核→重启→编译→测试&quot;流程，我先从 kernel.org 下载了各版本的 <code>tx.c</code> 源码进行对比分析：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 下载各版本的 tx.c 进行对比</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> ver in 6.13 6.14 6.15 6.16 6.17 6.18 6.19<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    curl -o tx_<span class="si">${</span><span class="nv">ver</span><span class="si">}</span>.c <span class="s2">&#34;https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/net/mac80211/tx.c?h=v</span><span class="si">${</span><span class="nv">ver</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>分析发现：</p>
<ul>
<li><strong>6.13–6.18</strong>：没有 <code>emulate_chanctx</code> 分支，直接 <code>if/else goto fail_rcu</code></li>
<li><strong>6.19+</strong>：有 <code>emulate_chanctx</code> 分支（来自 <code>d594cc6f2c58</code>），但 else 仍然 <code>goto fail_rcu</code></li>
</ul>
<p>两种变体都需要同样的修复，只是上下文略有不同。我的补丁基于 6.19+ 的代码编写，通过 <code>Fixes:</code> 标签和 <code>Cc: stable@vger.kernel.org</code> 来触发自动回溯。</p>
<h3 id="发现第二个-bug">
<a class="header-anchor" href="#%e5%8f%91%e7%8e%b0%e7%ac%ac%e4%ba%8c%e4%b8%aa-bug"></a>
发现第二个 Bug
</h3><p>在 6.18 内核上测试时，发现了一个意外情况：<strong>即使是纯 Monitor 模式也抓不到帧</strong>。这跟 6.13 的表现不同（6.13 纯 Monitor 是正常的）。</p>
<p>经过排查，发现 6.17–6.18 的 mt76 驱动中多了一行：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// drivers/net/wireless/mediatek/mt76/mt792x_core.c
</span></span></span><span class="line"><span class="cl"><span class="nf">ieee80211_hw_set</span><span class="p">(</span><span class="n">hw</span><span class="p">,</span> <span class="n">NO_VIRTUAL_MONITOR</span><span class="p">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>这个标志告诉 mac80211 不要创建虚拟 monitor 接口，导致整个监控模式都不工作。这个标志在 6.19 中被 revert 了。</p>
<p>所以实际上有<strong>两个独立的 Bug</strong>：</p>
<pre class="mermaid">
  graph TD
    A[6.12 及之前: 一切正常] --> B[6.13: tx.c chanctx fallback 被删除]
    B --> C[6.13-6.16: 纯 Monitor ✅ / 并存 ❌]
    B --> D[6.17-6.18: 额外添加 NO_VIRTUAL_MONITOR]
    D --> E[6.17-6.18: 纯 Monitor ❌ / 并存 ❌]
    D --> F[6.19: NO_VIRTUAL_MONITOR 被 revert]
    F --> G[6.19+: 纯 Monitor ✅ / 并存 ❌]
</pre><h3 id="完整测试结果">
<a class="header-anchor" href="#%e5%ae%8c%e6%95%b4%e6%b5%8b%e8%af%95%e7%bb%93%e6%9e%9c"></a>
完整测试结果
</h3><p>在代表性版本上进行了实际测试：</p>
<p><strong>补丁前：</strong></p>
<table>
  <thead>
      <tr>
          <th>内核版本</th>
          <th>纯 Monitor</th>
          <th>Managed + Monitor</th>
          <th>原因</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>6.8</td>
          <td>✅ 正常</td>
          <td>✅ 正常</td>
          <td>基线</td>
      </tr>
      <tr>
          <td>6.13</td>
          <td>✅ 正常</td>
          <td>❌ 0 帧</td>
          <td>Bug 1</td>
      </tr>
      <tr>
          <td>6.17</td>
          <td>❌ 0 帧</td>
          <td>❌ 0 帧</td>
          <td>Bug 1 + Bug 2</td>
      </tr>
      <tr>
          <td>6.18</td>
          <td>❌ 0 帧</td>
          <td>❌ 0 帧</td>
          <td>Bug 1 + Bug 2</td>
      </tr>
      <tr>
          <td>6.19</td>
          <td>✅ 正常</td>
          <td>❌ 0 帧</td>
          <td>Bug 1</td>
      </tr>
      <tr>
          <td>7.0-rc2</td>
          <td>✅ 正常</td>
          <td>❌ 0 帧</td>
          <td>Bug 1</td>
      </tr>
  </tbody>
</table>
<p><strong>补丁后（tx.c 修复）：</strong></p>
<table>
  <thead>
      <tr>
          <th>内核版本</th>
          <th>纯 Monitor</th>
          <th>Managed + Monitor</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>6.13</td>
          <td>✅ 正常</td>
          <td>✅ ~37 帧/5秒</td>
      </tr>
      <tr>
          <td>6.19</td>
          <td>✅ 正常</td>
          <td>✅ ~39 帧/5秒</td>
      </tr>
      <tr>
          <td>7.0-rc2</td>
          <td>✅ 正常</td>
          <td>✅ ~33 帧/5秒</td>
      </tr>
  </tbody>
</table>
<h2 id="提交上游补丁">
<a class="header-anchor" href="#%e6%8f%90%e4%ba%a4%e4%b8%8a%e6%b8%b8%e8%a1%a5%e4%b8%81"></a>
提交上游补丁
</h2><p>确认修复有效后，按照 Linux 内核贡献流程提交补丁。</p>
<h3 id="生成补丁">
<a class="header-anchor" href="#%e7%94%9f%e6%88%90%e8%a1%a5%e4%b8%81"></a>
生成补丁
</h3><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /root/kernel/linux-7.0-rc2
</span></span><span class="line"><span class="cl"><span class="c1"># 编辑 net/mac80211/tx.c 应用修复</span>
</span></span><span class="line"><span class="cl">git add net/mac80211/tx.c
</span></span><span class="line"><span class="cl">git commit -s  <span class="c1"># Signed-off-by 是必须的</span>
</span></span><span class="line"><span class="cl">git format-patch -1
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="checkpatch-检查">
<a class="header-anchor" href="#checkpatch-%e6%a3%80%e6%9f%a5"></a>
checkpatch 检查
</h3><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./scripts/checkpatch.pl 0001-wifi-mac80211-fix-monitor-mode-frame-capture-for-rea.patch
</span></span><span class="line"><span class="cl"><span class="c1"># total: 0 errors, 0 warnings, 41 lines checked</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="获取维护者列表">
<a class="header-anchor" href="#%e8%8e%b7%e5%8f%96%e7%bb%b4%e6%8a%a4%e8%80%85%e5%88%97%e8%a1%a8"></a>
获取维护者列表
</h3><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./scripts/get_maintainer.pl 0001-wifi-mac80211-fix-monitor-mode-frame-capture-for-rea.patch
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出的维护者和邮件列表就是你需要抄送的对象。</p>
<h3 id="发送补丁">
<a class="header-anchor" href="#%e5%8f%91%e9%80%81%e8%a1%a5%e4%b8%81"></a>
发送补丁
</h3><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git send-email <span class="se">\
</span></span></span><span class="line"><span class="cl">    --to<span class="o">=</span>linux-wireless@vger.kernel.org <span class="se">\
</span></span></span><span class="line"><span class="cl">    --cc<span class="o">=</span>johannes@sipsolutions.net <span class="se">\
</span></span></span><span class="line"><span class="cl">    --cc<span class="o">=</span>stable@vger.kernel.org <span class="se">\
</span></span></span><span class="line"><span class="cl">    0001-wifi-mac80211-fix-monitor-mode-frame-capture-for-rea.patch
</span></span></code></pre></td></tr></table>
</div>
</div><p>补丁中的关键标签：</p>
<ul>
<li><code>Fixes: 0a44dfc07074</code> — 指明修复哪个 commit 引入的 bug</li>
<li><code>Cc: stable@vger.kernel.org</code> — 请求回溯到受影响的 stable 分支</li>
</ul>
<p>补丁已发送到邮件列表：<a href="https://lore.kernel.org/linux-wireless/20260308164510.5927-1-fjhhz1997@gmail.com/T/#u" target="_blank" rel="noopener noreferrer nofollow">lore.kernel.org 链接</a>
</p>
<h2 id="调试过程中的坑">
<a class="header-anchor" href="#%e8%b0%83%e8%af%95%e8%bf%87%e7%a8%8b%e4%b8%ad%e7%9a%84%e5%9d%91"></a>
调试过程中的坑
</h2><h3 id="1-modulesymvers-缺失">
<a class="header-anchor" href="#1-modulesymvers-%e7%bc%ba%e5%a4%b1"></a>
1. Module.symvers 缺失
</h3><p>只编译单个模块时，如果不复制 <code>Module.symvers</code>，加载时会报 <code>undefined symbol</code> 错误：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cp /lib/modules/<span class="k">$(</span>uname -r<span class="k">)</span>/build/Module.symvers .
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="2-模块热替换的顺序">
<a class="header-anchor" href="#2-%e6%a8%a1%e5%9d%97%e7%83%ad%e6%9b%bf%e6%8d%a2%e7%9a%84%e9%a1%ba%e5%ba%8f"></a>
2. 模块热替换的顺序
</h3><p>卸载模块时要注意依赖关系，需要先卸载依赖 mac80211 的驱动模块：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 查看依赖关系</span>
</span></span><span class="line"><span class="cl">lsmod <span class="p">|</span> grep mac80211
</span></span><span class="line"><span class="cl"><span class="c1"># mt7921u → mt7921_common → mt792x_lib → mt76_connac_lib → mt76 → mac80211</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 按依赖顺序卸载</span>
</span></span><span class="line"><span class="cl">sudo rmmod mt7921u mt7921_common mt792x_lib mt76_connac_lib mt76_usb mt76 mac80211
</span></span><span class="line"><span class="cl"><span class="c1"># 加载修改后的模块</span>
</span></span><span class="line"><span class="cl">sudo insmod net/mac80211/mac80211.ko
</span></span><span class="line"><span class="cl">sudo modprobe mt7921u  <span class="c1"># 自动加载整个 mt76 驱动链</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="3-脏模块状态">
<a class="header-anchor" href="#3-%e8%84%8f%e6%a8%a1%e5%9d%97%e7%8a%b6%e6%80%81"></a>
3. 脏模块状态
</h3><p>多次热替换模块后，偶尔会出现模块状态异常（表现为补丁明明应该生效但实际没效果）。遇到这种情况，做一次完整的卸载-重载循环即可：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo rmmod mt7921u mt7921_common mt792x_lib mt76_connac_lib mt76_usb mt76 mac80211 cfg80211
</span></span><span class="line"><span class="cl">sudo modprobe mac80211  <span class="c1"># 加载原版</span>
</span></span><span class="line"><span class="cl">sudo rmmod mac80211
</span></span><span class="line"><span class="cl">sudo insmod net/mac80211/mac80211.ko  <span class="c1"># 加载修改版</span>
</span></span><span class="line"><span class="cl">sudo modprobe mt7921u
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="4-补丁的版本兼容性">
<a class="header-anchor" href="#4-%e8%a1%a5%e4%b8%81%e7%9a%84%e7%89%88%e6%9c%ac%e5%85%bc%e5%ae%b9%e6%80%a7"></a>
4. 补丁的版本兼容性
</h3><p>我的补丁是基于最新主线（7.0-rc2）编写的，其中包含 <code>emulate_chanctx</code> 分支。但 6.13–6.18 的内核没有这个分支，所以补丁不能直接 <code>git apply</code>。</p>
<p>不过这不影响上游提交——补丁只需要能在当前主线上 apply 即可。stable 团队在回溯时会自行处理冲突，或者我可以在需要时提供针对特定版本的 backport。</p>
<h2 id="总结">
<a class="header-anchor" href="#%e6%80%bb%e7%bb%93"></a>
总结
</h2><p>这次调试的收获：</p>
<ol>
<li><strong>只编译单个模块</strong>是调试内核 bug 的高效方法，配合模块热替换可以做到分钟级的修改-测试循环</li>
<li>看似一个 bug 实际可能是<strong>多个独立回归的叠加</strong>，需要跨版本源码对比才能理清</li>
<li>Ubuntu Mainline Kernel 配合 VMware 快照是一个非常方便的多内核测试环境</li>
<li>Linux 内核的补丁提交流程其实并不复杂：<code>git format-patch</code> → <code>checkpatch.pl</code> → <code>get_maintainer.pl</code> → <code>git send-email</code></li>
</ol>

        
        <hr><p>本文2026-03-08首发于<a href='https://csdn.fjh1997.top/'>猫猫鱼的小窝</a>，最后修改于2026-03-08</p>]]>
      </description>
      
    </item>
    
  </channel>
</rss>
