mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Ping-Ke Shih <pkshih@realtek.com>
To: "5mghybrid@khu.ac.kr" <5mghybrid@khu.ac.kr>,
	"linux-wireless@vger.kernel.org" <linux-wireless@vger.kernel.org>
Cc: Jes Sorensen <Jes.Sorensen@gmail.com>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Subject: RE: [PATCH rtw-next v2 4/4] wifi: rtl8xxxu: test RX ownership and recovery across failures
Date: Thu, 17 Sep 2026 06:27:05 +0000	[thread overview]
Message-ID: <0f0a4af91524425982d37b32ac8326d4@realtek.com> (raw)
In-Reply-To: <20260913-codex-rtw-rx-v2-v2-4-f09c964e0b96@khu.ac.kr>

kimwooseok via B4 Relay <devnull+5mghybrid.khu.ac.kr@kernel.org> wrote:
> From: kimwooseok <5mghybrid@khu.ac.kr>
> 
> Add 11 KUnit cases for the RX allocation, submission, completion and
> retry paths. Cover each retryable completion followed by ENOMEM/EAGAIN,
> batch sizes 1, 8, 9 and 32, skb allocation failure, startup failure
> positions, cancellation and shutdown.
> 
> Run the actual RX helpers and worker with task-scoped stubs for
> allocation and USB submission. Observer references check that the driver
> releases its URB and skb references. A delayed-work case checks that
> one retry request schedules the submission worker.
> 
> Assisted-by: GPT-6 Astra
> Signed-off-by: kimwooseok <5mghybrid@khu.ac.kr>
> ---
>  drivers/net/wireless/realtek/rtl8xxxu/.kunitconfig |  16 +
>  drivers/net/wireless/realtek/rtl8xxxu/Kconfig      |  11 +
>  drivers/net/wireless/realtek/rtl8xxxu/Makefile     |   3 +
>  drivers/net/wireless/realtek/rtl8xxxu/core.c       |  66 ++-
>  drivers/net/wireless/realtek/rtl8xxxu/rx-test.c    | 461 +++++++++++++++++++++
>  drivers/net/wireless/realtek/rtl8xxxu/rx-test.h    |  27 ++
>  6 files changed, 569 insertions(+), 15 deletions(-)
> 

[...]

> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/Makefile
> b/drivers/net/wireless/realtek/rtl8xxxu/Makefile
> index 580a2fa675ee2..a592a81197857 100644
> --- a/drivers/net/wireless/realtek/rtl8xxxu/Makefile
> +++ b/drivers/net/wireless/realtek/rtl8xxxu/Makefile
> @@ -4,3 +4,6 @@ obj-$(CONFIG_RTL8XXXU)  += rtl8xxxu.o
>  rtl8xxxu-y     := core.o 8192e.o 8723b.o \
>                    8723a.o 8192c.o 8188f.o \
>                    8188e.o 8710b.o 8192f.o
> +
> +obj-$(CONFIG_RTL8XXXU_KUNIT_TEST) += rtl8xxxu-rx-test.o
> +rtl8xxxu-rx-test-y := rx-test.o
> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c
> b/drivers/net/wireless/realtek/rtl8xxxu/core.c
> index 883c9a56f52a4..323411e7f5e3b 100644
> --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
> +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
> @@ -17,6 +17,8 @@
>  #include <linux/iopoll.h>

move '#include <kunit/static_stub.h>' here.

>  #include "regs.h"
>  #include "rtl8xxxu.h"
> +#include "rx-test.h"
> +#include <kunit/static_stub.h>
> 
>  #define DRIVER_NAME "rtl8xxxu"
> 
> @@ -60,8 +62,7 @@ MODULE_PARM_DESC(dma_agg_pages, "Set DMA aggregation pages (range 1-127, 0 to di
>  #define RTL8XXXU_TX_URB_HIGH_WATER     32
> 
>  static void rtl8xxxu_stop(struct ieee80211_hw *hw, bool suspend);
> -static int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv,
> -                                 struct rtl8xxxu_rx_urb *rx_urb);
> +
> 
>  static struct ieee80211_rate rtl8xxxu_rates[] = {
>         { .bitrate = 10, .hw_value = DESC_RATE_1M, .flags = 0 },
> @@ -5817,7 +5818,36 @@ void jaguar2_rx_parse_phystats(struct rtl8xxxu_priv *priv,
>         }
>  }
> 
> -static void rtl8xxxu_free_rx_resources(struct rtl8xxxu_priv *priv)
> +VISIBLE_IF_KUNIT struct rtl8xxxu_rx_urb *rtl8xxxu_alloc_rx_urb(void)
> +{
> +       KUNIT_STATIC_STUB_REDIRECT(rtl8xxxu_alloc_rx_urb);

an empty line, and also apply to following funtions.

> +       return kmalloc_obj(struct rtl8xxxu_rx_urb);
> +}
> +EXPORT_SYMBOL_IF_KUNIT(rtl8xxxu_alloc_rx_urb);
> +
> +VISIBLE_IF_KUNIT struct sk_buff *rtl8xxxu_alloc_rx_skb(unsigned int size)
> +{
> +       KUNIT_STATIC_STUB_REDIRECT(rtl8xxxu_alloc_rx_skb, size);
> +       return __netdev_alloc_skb(NULL, size, GFP_KERNEL);
> +}
> +EXPORT_SYMBOL_IF_KUNIT(rtl8xxxu_alloc_rx_skb);
> +
> +VISIBLE_IF_KUNIT int rtl8xxxu_rx_usb_submit(struct urb *urb, gfp_t flags)
> +{
> +       KUNIT_STATIC_STUB_REDIRECT(rtl8xxxu_rx_usb_submit, urb, flags);
> +       return usb_submit_urb(urb, flags);
> +}
> +EXPORT_SYMBOL_IF_KUNIT(rtl8xxxu_rx_usb_submit);
> +
> +VISIBLE_IF_KUNIT void rtl8xxxu_schedule_rx_retry(struct rtl8xxxu_priv *priv)
> +{
> +       KUNIT_STATIC_STUB_REDIRECT(rtl8xxxu_schedule_rx_retry, priv);
> +       queue_delayed_work(system_wq, &priv->rx_urb_retry_wq,
> +                          msecs_to_jiffies(RTL8XXXU_RX_URB_RETRY_DELAY_MS));
> +}
> +EXPORT_SYMBOL_IF_KUNIT(rtl8xxxu_schedule_rx_retry);
> +
> +VISIBLE_IF_KUNIT void rtl8xxxu_free_rx_resources(struct rtl8xxxu_priv *priv)
>  {
>         struct rtl8xxxu_rx_urb *rx_urb, *tmp;
>         unsigned long flags;
> @@ -5838,15 +5868,16 @@ static void rtl8xxxu_free_rx_resources(struct rtl8xxxu_priv *priv)
> 
>         spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
>  }
> +EXPORT_SYMBOL_IF_KUNIT(rtl8xxxu_free_rx_resources);
> 
> -static int rtl8xxxu_alloc_rx_urbs(struct rtl8xxxu_priv *priv)
> +VISIBLE_IF_KUNIT int rtl8xxxu_alloc_rx_urbs(struct rtl8xxxu_priv *priv)
>  {
>         struct rtl8xxxu_rx_urb *rx_urb;
>         int i;
> 
>         /* No RX work is active until the complete pool has been allocated. */
>         for (i = 0; i < RTL8XXXU_RX_URBS; i++) {
> -               rx_urb = kmalloc_obj(struct rtl8xxxu_rx_urb);
> +               rx_urb = rtl8xxxu_alloc_rx_urb();
>                 if (!rx_urb)
>                         return -ENOMEM;
> 
> @@ -5859,6 +5890,7 @@ static int rtl8xxxu_alloc_rx_urbs(struct rtl8xxxu_priv *priv)
> 
>         return 0;
>  }
> +EXPORT_SYMBOL_IF_KUNIT(rtl8xxxu_alloc_rx_urbs);
> 
>  static void rtl8xxxu_queue_rx_urb(struct rtl8xxxu_priv *priv,
>                                   struct rtl8xxxu_rx_urb *rx_urb)
> @@ -5887,7 +5919,7 @@ static void rtl8xxxu_queue_rx_urb(struct rtl8xxxu_priv *priv,
>         spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
>  }
> 
> -static void rtl8xxxu_rx_urb_retry_work(struct work_struct *work)
> +VISIBLE_IF_KUNIT void rtl8xxxu_rx_urb_retry_work(struct work_struct *work)
>  {
>         struct rtl8xxxu_priv *priv = container_of(to_delayed_work(work),
>                                                 struct rtl8xxxu_priv,
> @@ -5907,6 +5939,7 @@ static void rtl8xxxu_rx_urb_retry_work(struct work_struct *work)
> 
>         spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
>  }
> +EXPORT_SYMBOL_IF_KUNIT(rtl8xxxu_rx_urb_retry_work);
> 
>  static void rtl8xxxu_queue_rx_urb_retry(struct rtl8xxxu_priv *priv,
>                                         struct rtl8xxxu_rx_urb *rx_urb)
> @@ -5919,8 +5952,7 @@ static void rtl8xxxu_queue_rx_urb_retry(struct rtl8xxxu_priv *priv,
>                 list_add_tail(&rx_urb->list, &priv->rx_urb_retry_list);
>                 priv->rx_urb_retry_count++;
>                 /* Keep normal completions from bypassing the error backoff. */
> -               queue_delayed_work(system_wq, &priv->rx_urb_retry_wq,
> -                                  msecs_to_jiffies(RTL8XXXU_RX_URB_RETRY_DELAY_MS));
> +               rtl8xxxu_schedule_rx_retry(priv);
>         } else {
>                 usb_free_urb(&rx_urb->urb);
>         }
> @@ -5928,7 +5960,7 @@ static void rtl8xxxu_queue_rx_urb_retry(struct rtl8xxxu_priv *priv,
>         spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
>  }
> 
> -static void rtl8xxxu_rx_urb_work(struct work_struct *work)
> +VISIBLE_IF_KUNIT void rtl8xxxu_rx_urb_work(struct work_struct *work)
>  {
>         struct rtl8xxxu_priv *priv;
>         struct rtl8xxxu_rx_urb *rx_urb, *tmp;
> @@ -5968,8 +6000,9 @@ static void rtl8xxxu_rx_urb_work(struct work_struct *work)
>                 }
>         }
>  }
> +EXPORT_SYMBOL_IF_KUNIT(rtl8xxxu_rx_urb_work);
> 
> -static int rtl8xxxu_start_rx(struct rtl8xxxu_priv *priv)
> +VISIBLE_IF_KUNIT int rtl8xxxu_start_rx(struct rtl8xxxu_priv *priv)
>  {
>         struct rtl8xxxu_rx_urb *rx_urb, *tmp;
>         unsigned long flags;
> @@ -6006,6 +6039,7 @@ static int rtl8xxxu_start_rx(struct rtl8xxxu_priv *priv)
>         }
>         return ret;
>  }
> +EXPORT_SYMBOL_IF_KUNIT(rtl8xxxu_start_rx);
> 
>  /*
>   * The RTL8723BU/RTL8192EU vendor driver use coexistence table type
> @@ -6646,7 +6680,7 @@ int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
>         return RX_TYPE_DATA_PKT;
>  }
> 
> -static void rtl8xxxu_rx_complete(struct urb *urb)
> +VISIBLE_IF_KUNIT void rtl8xxxu_rx_complete(struct urb *urb)
>  {
>         struct rtl8xxxu_rx_urb *rx_urb =
>                 container_of(urb, struct rtl8xxxu_rx_urb, urb);
> @@ -6686,9 +6720,10 @@ static void rtl8xxxu_rx_complete(struct urb *urb)
>         usb_free_urb(urb);
>         dev_kfree_skb(skb);
>  }
> +EXPORT_SYMBOL_IF_KUNIT(rtl8xxxu_rx_complete);
> 
> -static int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv,
> -                                 struct rtl8xxxu_rx_urb *rx_urb)
> +VISIBLE_IF_KUNIT int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv,
> +                                           struct rtl8xxxu_rx_urb *rx_urb)
>  {
>         struct rtl8xxxu_fileops *fops = priv->fops;
>         struct sk_buff *skb;
> @@ -6704,7 +6739,7 @@ static int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv,
>                 skb_size = IEEE80211_MAX_FRAME_LEN + rx_desc_sz;
>         }
> 
> -       skb = __netdev_alloc_skb(NULL, skb_size, GFP_KERNEL);
> +       skb = rtl8xxxu_alloc_rx_skb(skb_size);
>         if (!skb)
>                 return -ENOMEM;
> 
> @@ -6712,7 +6747,7 @@ static int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv,
>         usb_fill_bulk_urb(&rx_urb->urb, priv->udev, priv->pipe_in, skb->data,
>                           skb_size, rtl8xxxu_rx_complete, skb);
>         usb_anchor_urb(&rx_urb->urb, &priv->rx_anchor);
> -       ret = usb_submit_urb(&rx_urb->urb, GFP_ATOMIC);
> +       ret = rtl8xxxu_rx_usb_submit(&rx_urb->urb, GFP_ATOMIC);
>         if (ret) {
>                 usb_unanchor_urb(&rx_urb->urb);
>                 dev_kfree_skb(skb);
> @@ -6720,6 +6755,7 @@ static int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv,
>         }
>         return ret;
>  }
> +EXPORT_SYMBOL_IF_KUNIT(rtl8xxxu_submit_rx_urb);
> 
>  static void rtl8xxxu_int_complete(struct urb *urb)
>  {
> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rx-test.c

Prefer test-rx.c. 

Regarding sometime we add test cases for TX, the file name can
be test-tx.c

> b/drivers/net/wireless/realtek/rtl8xxxu/rx-test.c
> new file mode 100644
> index 0000000000000..aedddcb049cdf
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtl8xxxu/rx-test.c
> @@ -0,0 +1,461 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include <kunit/static_stub.h>
> +#include <kunit/test.h>
> +#include <linux/usb.h>
> +
> +#include "rtl8xxxu.h"
> +#include "rx-test.h"
> +
> +#define RX_TEST_URBS 32
> +#define RX_TEST_SKBS 512
> +
> +struct rx_test {

I'm not familiar to kunittest, so I don't quit understand how it uses its
own context. I'll review this patch in detail in the future, and currently
I only point out some surface opinons.

> +       struct rtl8xxxu_priv priv;
> +       struct ieee80211_hw hw;
> +       struct rtl8xxxu_fileops fops;
> +       struct usb_device udev;
> +       struct rtl8xxxu_rx_urb *urbs[RX_TEST_URBS];
> +       struct sk_buff *skbs[RX_TEST_SKBS];
> +       unsigned int allocated;
> +       unsigned int buffers;
> +       unsigned int alloc_calls;
> +       unsigned int alloc_fail_at;
> +       unsigned int submit_calls;
> +       unsigned int submit_fail_at;
> +       int submit_error;
> +       bool fail_skb;
> +       bool retry_pending;
> +       unsigned int retry_arms;
> +       atomic_t normal_runs;
> +};
> +
> +static struct rx_test *rx_current(void)
> +{
> +       return kunit_get_current_test()->priv;
> +}
> +
> +static struct rtl8xxxu_rx_urb *rx_alloc_object(void)
> +{
> +       struct rx_test *ctx = rx_current();

maybe function name can point out 'ctx', like rx_current_test_ctx().

> +       struct rtl8xxxu_rx_urb *rx;
> +
> +       ctx->alloc_calls++;
> +       if (ctx->alloc_calls == ctx->alloc_fail_at)
> +               return NULL;

empty line 

> +       rx = kmalloc_obj(struct rtl8xxxu_rx_urb);
> +       if (rx)
> +               ctx->urbs[ctx->allocated++] = rx;

empty line. (please review this file yourself to add proper empty lines)

> +       return rx;
> +}
> +

[...]

> +static struct rx_test *rx_init(struct kunit *test, bool fake_timer)
> +{
> +       struct rx_test *ctx;
> +
> +       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
> +       if (!ctx)
> +               return NULL;
> +       test->priv = ctx;
> +       ctx->hw.priv = &ctx->priv;
> +       ctx->priv.hw = &ctx->hw;
> +       ctx->priv.udev = &ctx->udev;
> +       ctx->priv.fops = &ctx->fops;
> +       ctx->fops.rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16);
> +       spin_lock_init(&ctx->priv.rx_urb_lock);
> +       INIT_LIST_HEAD(&ctx->priv.rx_urb_pending_list);
> +       INIT_LIST_HEAD(&ctx->priv.rx_urb_retry_list);
> +       init_usb_anchor(&ctx->priv.rx_anchor);
> +       INIT_WORK(&ctx->priv.rx_urb_wq, rx_observe_work);
> +       INIT_DELAYED_WORK(&ctx->priv.rx_urb_retry_wq,
> +                         rtl8xxxu_rx_urb_retry_work);
> +       atomic_set(&ctx->normal_runs, 0);
> +
> +       kunit_activate_static_stub(test, rtl8xxxu_alloc_rx_urb, rx_alloc_object);

Add prefix to sub, like:

rx_alloc_object  ->  fake_rtl8xxxu_alloc_rx_urb, or
                 ->  stub_rtl8xxxu_alloc_rx_urb

> +       kunit_activate_static_stub(test, rtl8xxxu_alloc_rx_skb, rx_alloc_buffer);
> +       kunit_activate_static_stub(test, rtl8xxxu_rx_usb_submit, rx_submit);
> +       if (fake_timer)
> +               kunit_activate_static_stub(test, rtl8xxxu_schedule_rx_retry,
> +                                          rx_schedule_retry);
> +       return ctx;
> +}
> +

[...]

> +
> +static void rx_start_fatal_failure(struct kunit *test)
> +{
> +       static const unsigned int positions[] = { 1, 8, 32 };

A personal random thought: if the values can be random, maybe create
another test item with random values for unpredicted corner cases.
(not only for this test item)

> +       struct rx_test *ctx;
> +       unsigned int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(positions); i++) {
> +               ctx = rx_init(test, true);
> +               KUNIT_ASSERT_NOT_NULL(test, ctx);
> +               KUNIT_ASSERT_EQ(test, rx_pool(ctx, 32), 0);
> +               ctx->submit_fail_at = positions[i];
> +               ctx->submit_error = -ENODEV;
> +               KUNIT_EXPECT_EQ(test, rtl8xxxu_start_rx(&ctx->priv), -ENODEV);
> +               KUNIT_EXPECT_EQ(test, ctx->submit_calls, positions[i]);
> +               KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_pending_count, 0);
> +               KUNIT_EXPECT_FALSE(test, ctx->retry_pending);
> +               rx_finish(test, ctx);
> +       }
> +}
> +



  reply	other threads:[~2026-09-17  6:27 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-13  7:33 [PATCH rtw-next v2 0/4] wifi: rtl8xxxu: keep RX requests available across transient errors kimwooseok via B4 Relay
2026-09-13  7:33 ` [PATCH rtw-next v2 1/4] wifi: rtl8xxxu: free RX skb when URB submission fails kimwooseok via B4 Relay
2026-09-17  1:18   ` Ping-Ke Shih
2026-09-17  1:57     ` ‍김우석[학생](전자정보대학 전자공학과)
2026-09-17  7:31       ` ‍김우석[학생](전자정보대학 전자공학과)
2026-09-13  7:33 ` [PATCH rtw-next v2 2/4] wifi: rtl8xxxu: unwind incomplete receive startup kimwooseok via B4 Relay
2026-09-17  3:20   ` Ping-Ke Shih
2026-09-17  7:27     ` ‍김우석[학생](전자정보대학 전자공학과)
2026-09-13  7:33 ` [PATCH rtw-next v2 3/4] wifi: rtl8xxxu: preserve RX requests across recoverable transfer errors kimwooseok via B4 Relay
2026-09-17  3:45   ` Ping-Ke Shih
2026-09-17  7:29     ` ‍김우석[학생](전자정보대학 전자공학과)
2026-09-17  7:52       ` Ping-Ke Shih
2026-09-13  7:33 ` [PATCH rtw-next v2 4/4] wifi: rtl8xxxu: test RX ownership and recovery across failures kimwooseok via B4 Relay
2026-09-17  6:27   ` Ping-Ke Shih [this message]
2026-09-17  7:30     ` ‍김우석[학생](전자정보대학 전자공학과)
2026-09-18  7:40 ` [PATCH rtw-next v2 0/4] wifi: rtl8xxxu: keep RX requests available across transient errors Ping-Ke Shih

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=0f0a4af91524425982d37b32ac8326d4@realtek.com \
    --to=pkshih@realtek.com \
    --cc=5mghybrid@khu.ac.kr \
    --cc=Jes.Sorensen@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®