From nobody Fri Sep 25 12:43:31 2026 Received: from mail-lf2-f12.google.com (mail-lf2-f12.google.com [74.125.229.204]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6D60D37C0FF for ; Sat, 12 Sep 2026 20:29:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=74.125.229.204 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789244980; cv=pass; b=aeUDgO+jZwQ9buv42KcjsTgyiqZSxtcGKZu0mf+DHzt2TwZsFiIuRHpWwzfZtTTSsyNjGapxKjMebzJ3mmonnG9NIFI5r0Jnbm10JDx73eQmt2fL1c81KMMxv08hXDPxaUePjfX8PgeZeBbw5tQFPVPxx1KuQgM+6aYe5XHYHrg= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789244980; c=relaxed/simple; bh=3Z+rnR6y8+XrskA3hVov6v6LoLFko/3oEz6RpE1l2sc=; h=MIME-Version:From:In-Reply-To:References:Date:Message-ID:Subject: To:Cc:Content-Type; b=YNoBkpVusWUrtB3lP28iuNEl9dC14p4f9UjqvXr2lLzwACOItEGYTZOFl1y+WRzbxicohVPllbhR6g6BwlO9dhoJTqtiZl+P2R+ABb6Q57rQCC86EI1FaApIxpSMPMIVzeVhQ3+eG0YezAebDDDnnZ/6o51X/kNUXXQsA3erBf4= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=khu.ac.kr; spf=pass smtp.mailfrom=khu.ac.kr; dkim=pass (1024-bit key) header.d=khu.ac.kr header.i=@khu.ac.kr header.b=HCKv/l/h; arc=pass smtp.client-ip=74.125.229.204 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=khu.ac.kr Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=khu.ac.kr Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=khu.ac.kr header.i=@khu.ac.kr header.b="HCKv/l/h" Received: by mail-lf2-f12.google.com with SMTP id 2adb3069b0e04-5b79784a6edso754608e87.1 for ; Sat, 12 Sep 2026 13:29:38 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1789244976; cv=none; d=google.com; s=arc-20260327; b=rdYZobpfO7UWU3e2N4Hq8xHNFu1r0JQTOUryB5F6ENLuy5Dgx310RlroKFIaGaJquH EzypKo16AiI0H3w5RoYg0D0qCXzRx7o9zCpVIUPjQNx26SI07Y2nM+rBiHMjSMg3JRf0 YEB6+H5HsA2u2Nazc6LJiKaCGNMNkYxjrAz8VDXxINJv4NDgpz1CslzVd2KQq3f0ujbH jnbNALU3YI8FilFnSi8KLYFcYaBmiafUcU34e3H1+Vn73yk+SRjJDLT5xZXCGeRJCeHC SpIfqPRdYf8mZBriAIPzuMFSwGt6XbIDysQJUATmO+8+aK2jxKCArxlvaM1N+djH+uhr CNXg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:subject:message-id:date:references:in-reply-to:from :mime-version:dkim-signature; bh=RLAp5n84PhWI7Xy3vjJ/UrGvAVQA5MGrxcUoi3gqi6M=; fh=p0TJfVnDwACqMYEnuCGHZfGGECB3n0BR1bRrF1Fvs7A=; b=Kajrvn6u1gpK8rNkx626hycsiuvTajYL2Vrvr1KzhFj0mx424aRQ4ANagDNi87I11q d1u9WrrUg5Itaou6w9ufTC6UAUbkIjmmtmYGwdgJ6pkph/KO9uFr5fyeGE8A29WoZdmf 7SwKlnHZFRdUnIf8BT70CUidSUSiLqTZ4Kzf+VP5TCPJlapcwUf4Jr0GtsR5n86pPIkC M+xmt5S38j/1nJ9/3tr8oUFbwoD2ic2Ss6w459RMCafQA4vyYQYREWZtqbjnD70dvyDU rfqzc+utjcLYhysmQUoIXmZmdr5qTIhKZHAI/LRFC+xmKRhLKmVEGlBPjdfF0ixTJ/Un /9mw==; darn=vger.kernel.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=khu.ac.kr; s=google; t=1789244976; x=1789849776; darn=vger.kernel.org; h=content-type:cc:to:subject:message-id:date:references:in-reply-to :from:mime-version:from:to:cc:subject:date:message-id:reply-to :content-type; bh=RLAp5n84PhWI7Xy3vjJ/UrGvAVQA5MGrxcUoi3gqi6M=; b=HCKv/l/hkCjQzfrjvoD+DjKKduw/oClZ0SeZ7+jb4B5bSTkRxrwsKg+sFyHZjbduWE GO0GEVroFAtBmLJZemKgMUqd/5uyxZhWv3s+qR0BCNTzeFoyd9qoRx6FoWmGiaIJhCy2 /VDY4K0M0rtmhW6ME/BZ+EC7tbetD+sFj7DPs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789244976; x=1789849776; h=content-type:cc:to:subject:message-id:date:references:in-reply-to :from:mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to:content-type; bh=RLAp5n84PhWI7Xy3vjJ/UrGvAVQA5MGrxcUoi3gqi6M=; b=bUFYn9Nsrr3IymCKezw0Z/Y2EyyFuTE8atp76atItqx1caa/UULFx+X4RztAOr+AAM IAY3SSzAHO7kENzz2S/sQAKxwV5zRE88k9Qh8ZjCyfVF+IekXmKBoQi6rTLUrvNpiwAb P2Km2KKMgneEHzOCTsfLEk3uU2+Iy2PojGrmL7KN9PEBVFQfeX5oytB1hEk/adSdTktF mzWT573oo/EcalsDmG5YiI/wTbIDjXgm9YvqdZl4qoAzTikkVcyfEIp0YpsjZKGBEMKD v+URp+NHwztoduhgGwr0Vl4eCCecRqIW0hfpf47li5WIgYQRuetsVPJx4SIS42QzrNCB PqAg== X-Forwarded-Encrypted: i=1; AKwUvBwQbfpB50IeRkfHzyc6raVYjlUq1vTQQM7L6C58OVTvXO5w5K8mMXCaLW1gEMzE34p8V4JNnMQuRBP0Rac=@vger.kernel.org X-Gm-Message-State: AFuF++njZRS7yWtc53qwTsSPh//7uqrgarSaBiOVAtqFuj18ymSasv88 qVkc/mLhgIrD5H5N3b58+4+9Uhdlt+31TdsGfZns8FcsRtWXBTtzg2KCkGLbavru0UPTiT0MJ9x 1IneJxy6Mm+g1AXz3cSdMbF1Nzxp9S2oMuuCYQ8pNqsAvCxt3os8m X-Gm-Gg: AYBFou3tIAGBn8M9dOpJ5NX4gUlIn8Yf5ILLS6hq1OmS1s3lZGq3m/Nfx04pdJrtB3S 1NayRLvsossVqJ2feYE+dW+zf5ySAimPorROcjTKXgLIowOofxLbK+YscRUt181+0++Qfc6t7x4 6awAIFvyPWMWMX78Q+z0noFzKAHDZi6YopfSHW6QJ40e0AGt+dALN+tebunQW+LPeKL2txIlQn1 EpNy7TmJPSYNvRS8QRwjVlzpiq0viS3d55ysQsmVcqKih1CctBV3S0B1sbsVlOSj+Ila75WRWSZ 9TVEsbZouaWLgyVlpWmm/amAcUNvmUlabCK/bBvACjDjQHj699I= X-Received: by 2002:a05:6512:31c7:b0:5b6:186e:b79d with SMTP id 2adb3069b0e04-5b8a8efb04amr723267e87.57.1789244976231; Sat, 12 Sep 2026 13:29:36 -0700 (PDT) Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:29:35 -0400 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:28:14 -0400 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:20:09 -0400 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:09:50 -0400 From: kimwooseok <5mghybrid@khu.ac.kr> In-Reply-To: References: Date: Sat, 12 Sep 2026 16:29:35 -0400 X-Gm-Features: AcwNN1XLCOa6gaClJgemEvFiJiVPSixm8S5rZ4-E3hZh2AQgrnsujY7tPXvHltk Message-ID: Subject: [PATCH wireless 1/4] wifi: rtl8xxxu: free RX skb when URB submission fails To: Jes Sorensen Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A failed RX URB submission can leak the skb allocated for that attempt. rtl8xxxu_submit_rx_urb() allocates the buffer and stores it in urb.context, but if usb_submit_urb() fails, it only unanchors the URB. The RX worker queues ENOMEM/EAGAIN failures for retry with that buffer still attached. On the next attempt, rtl8xxxu_submit_rx_urb() allocates another skb and overwrites urb.context, losing the reference to the previous buffer. Stopping before the retry also leaks the buffer because pending-request cleanup frees only the URB. Fix the leak at the submission failure by making rtl8xxxu_submit_rx_urb() responsible for both allocating the skb and releasing it when submission fails. Free the newly allocated skb and clear urb.context before returning the error, so neither retry nor stop receives a pending URB that still owns a buffer. Remove the caller-side skb cleanup from start and the RX worker; those callers now handle only whether to retry or release the URB. Fixes: 26f1fad29ad9 ("New driver: rtl8xxxu (mac80211)") Assisted-by: GPT-6 Astra Signed-off-by: kimwooseok <5mghybrid@khu.ac.kr> Reviewed-by: Ping-Ke Shih --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index bddbd0990de72..795a5ec2f8cd4 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -5864,7 +5864,6 @@ static void rtl8xxxu_rx_urb_work(struct work_struct *= work) struct rtl8xxxu_priv *priv; struct rtl8xxxu_rx_urb *rx_urb, *tmp; struct list_head local; - struct sk_buff *skb; unsigned long flags; int ret; @@ -5896,8 +5895,6 @@ static void rtl8xxxu_rx_urb_work(struct work_struct *= work) default: dev_warn(&priv->udev->dev, "failed to requeue urb with error %i\n", ret); - skb =3D (struct sk_buff *)rx_urb->urb.context; - dev_kfree_skb(skb); usb_free_urb(&rx_urb->urb); } } @@ -6596,8 +6593,11 @@ static int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv, skb_size, rtl8xxxu_rx_complete, skb); usb_anchor_urb(&rx_urb->urb, &priv->rx_anchor); ret =3D usb_submit_urb(&rx_urb->urb, GFP_ATOMIC); - if (ret) + if (ret) { usb_unanchor_urb(&rx_urb->urb); + dev_kfree_skb(skb); + rx_urb->urb.context =3D NULL; + } return ret; } @@ -7410,7 +7410,6 @@ static int rtl8xxxu_start(struct ieee80211_hw *hw) struct rtl8xxxu_priv *priv =3D hw->priv; struct rtl8xxxu_rx_urb *rx_urb; struct rtl8xxxu_tx_urb *tx_urb; - struct sk_buff *skb; unsigned long flags; int ret, i; @@ -7461,13 +7460,8 @@ static int rtl8xxxu_start(struct ieee80211_hw *hw) rx_urb->hw =3D hw; ret =3D rtl8xxxu_submit_rx_urb(priv, rx_urb); - if (ret) { - if (ret !=3D -ENOMEM) { - skb =3D (struct sk_buff *)rx_urb->urb.context; - dev_kfree_skb(skb); - } + if (ret) rtl8xxxu_queue_rx_urb(priv, rx_urb); - } } schedule_delayed_work(&priv->ra_watchdog, 2 * HZ); --=20 2.48.1 From nobody Fri Sep 25 12:43:31 2026 Received: from mail-lf1-f42.google.com (mail-lf1-f42.google.com [209.85.167.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5B10137C0FF for ; Sat, 12 Sep 2026 20:29:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=209.85.167.42 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789244995; cv=pass; b=NnvTcYt0wlNBSbkCeYHVq82fsaeKVAiGqRm4an25QidCkuqmieQ+z+E9VO7rgTrzKfjL6d6Ye8E1kubr5Rv04Fny/FCKDPZFVCUpGYq+2Pwp2Z8WQB7h0vhewu1TMcSaGVVj6fCvadKwSSkgsyP7hnk/NiCCW5+DDLgrWBBZHss= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789244995; c=relaxed/simple; bh=/ikQ1kfuLVYm8B4nlxAmsk7qhp2ROAhGxUVQKgrQezg=; h=MIME-Version:From:In-Reply-To:References:Date:Message-ID:Subject: To:Cc:Content-Type; b=ZBhcgXMWyVpoHKiywqFuZ0AexZrpdFItgQY38KbUNiK4RUvU7D5HsDHsnntB9e7+NjPmaxE9IwtEbx8wHiNBF+Q8pm0yOgUa/VBLMMfFmF0kQJwh9wLAiykGJRX4pG+XglibCLvIxcoMtRDDlJuDFFzNVmdIDjx8QO1cp2LaIBI= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=khu.ac.kr; spf=pass smtp.mailfrom=khu.ac.kr; dkim=pass (1024-bit key) header.d=khu.ac.kr header.i=@khu.ac.kr header.b=KGgTrb2x; arc=pass smtp.client-ip=209.85.167.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=khu.ac.kr Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=khu.ac.kr Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=khu.ac.kr header.i=@khu.ac.kr header.b="KGgTrb2x" Received: by mail-lf1-f42.google.com with SMTP id 2adb3069b0e04-5b5e907853eso2108267e87.2 for ; Sat, 12 Sep 2026 13:29:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1789244990; cv=none; d=google.com; s=arc-20260327; b=DIqaec6G9e4JkqDYfh6g9fGcyRL0GLrvqxEki8nrIxW46YAJ781xBtkZlyvDuMiT+Q E0phvuAj1Lv0RkioKupy6CorvCO/1TtPvbvsDBbVCLA3J5SKuuUXIUeCmCHKQEkM+ebB CvN/qDYM6JhDcIzK/R5jD9lkKgdKg4lBXJQ+Got5T5eolhP1sbiG5Z4LTiO9TrvYUnWb bHR80xFxfDsQNxEp9VBUkpVV9/GB7/gkzySqNL7XT7RSjTHOTDA8Gy2N5Nxjp0RQmp11 0OF9WGe6FK+aoEBxghzyLUw5P0E87M4A7xXbWQ6REhQCx3vSFG4CcChH4+8w7e+0KtaF gr/Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:subject:message-id:date:references:in-reply-to:from :mime-version:dkim-signature; bh=mpGBWL5dTfQk5mc1h0ZVvwGMGlDDwNb+lFBx2tyur78=; fh=YAc17qq3cZXoh8gdA+SF/U4Sz4GgJ+OEGxqm5h5OpWc=; b=IWRiZBl8Qap1NZb86VuHK3Z7TiiyS53TKtl1Lg6xgfWFMl+BTJw20QKXz35eR9QDYn sLvrF/EmN5WL8jG4De31/mQ5vuwsHjgiP3/uo/0Vhf4duS36zsFlgs5s3zkZMeiHb6W3 cJqYjBykZYME/fFQ4FNm5GUVrr06DQ9dAcR5jnrsLgAkjob7Xa7diWdxp/UqUmL3Y63g ZTutLGhyVA6ihisC82+4pxRKJL1ZwC3jZ3rxQWEm8aD25mqEydBFqj3l0fARtiISmMxu K5Uw3EZotGUV6Q2sT22MHmO52gpXkjhKULRIeZG3UTeA17i+X0X5njzL2h5QtApnamth tGSA==; darn=vger.kernel.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=khu.ac.kr; s=google; t=1789244990; x=1789849790; darn=vger.kernel.org; h=content-type:cc:to:subject:message-id:date:references:in-reply-to :from:mime-version:from:to:cc:subject:date:message-id:reply-to :content-type; bh=mpGBWL5dTfQk5mc1h0ZVvwGMGlDDwNb+lFBx2tyur78=; b=KGgTrb2x/73gOqhKweRBk73/sNMjaMiBA9capmyv14/lEvXdE1Ec5CcLTuY26ukVdm xrwsu/rnfekNPmd0XPr0ZJ3lkhmZwO7A6JNJag2AiHYZs3ZnsnZyqjcvzKdWfmgzAQ87 1ax5XkKLhCGDCs8QvA5Kb8UKQgVj/hKZdHEFs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789244990; x=1789849790; h=content-type:cc:to:subject:message-id:date:references:in-reply-to :from:mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to:content-type; bh=mpGBWL5dTfQk5mc1h0ZVvwGMGlDDwNb+lFBx2tyur78=; b=r2onnF1X/XzHNqbyYhosM2+Ou7418BNMMxZ473BEgqwX5BMR35n2kpsC4PQwe/l3qC WSxSBHSKmHvpQhsChNPU2EVYznW7ABT3WG+Op3rVt7PVt/QxIp2O3QU86MpROEwGMb+x uZs8nxYtC5rQKbFm7NV0vjgHhMG1B/Upjja9q0GwY0qktlylkDz2gn5fubEoHYepALv6 sAof1QDF4KzMVqN1ZQGw5odAlhOWgUgt22+M/1UvjTP66QpXAe/Ik4sYxSREDlLqxJpY 1b7s0lSJu+6gUnj4scTllSZcBJ/sq4rBt3r3ojpdnV0STFHZKYE4914Zpuda4OQCkizH FUEw== X-Forwarded-Encrypted: i=1; AKwUvBx1bSNz66bc2SSOETXORV1jmp8cXIcH7ZctaH/ZlqGTUEAHcYDxcKgiQsJyIHvWlYekHtHGATBeQZYUfhY=@vger.kernel.org X-Gm-Message-State: AFuF++m46Ze9Tddxl2cvayakat1zjAe24FFX3ub/Xq7NCUIysOXBHg51 iLp2p0T2Mib/SU/kl4BoOcJKE09uq8Q2LOopKfTJLO9RIwUjVIdRwKQ6Cmds9s/HbVJeivTLzB/ vQ+KZG+fgWLFO6Xxuykp7e9XXoPcgjommGX45rsyvrg== X-Gm-Gg: AYBFou352AWQC+QLlsk8YG2zs5xqYwk1D+I6NyGsDIXueybVfBki2GFPNNMFsHSLpbo OkW1iI65x1G79Ltv6lsmrlqxBCqJItJh4NsonoeHfKNJ4O9uSq+i+oxWjidLFX6aMwmf0w11FTG RfKc8VfOweV0Un7RnDj1yAkhmQGiuqnOJuY7xOL2f3EssvfPFV+yxASBoczZ9f0g2a4RFSskbst YnSm86gZ9KLP8J6+lB4+eGWshv3iRnSfM9OU1a7d5EjyYryXx1aKVJxE5PL/ueV9pxvWGx30sQb n4/2lfv8+uVHB+/uX/EhuF62KnLEdA9TqcD8AFF2iH4blt6IV6Y= X-Received: by 2002:a05:6512:224e:b0:5b6:183d:2a72 with SMTP id 2adb3069b0e04-5b8a029db1emr2087880e87.18.1789244990207; Sat, 12 Sep 2026 13:29:50 -0700 (PDT) Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:29:49 -0400 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 15:28:18 -0500 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:20:12 -0400 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:09:51 -0400 From: kimwooseok <5mghybrid@khu.ac.kr> In-Reply-To: References: Date: Sat, 12 Sep 2026 16:29:49 -0400 X-Gm-Features: AcwNN1Ww8RECxvFUbnTVydmcsxe2gffbw6SmVBK6pP8Gb3uL_2aWnXSQENYPC-k Message-ID: Subject: [PATCH wireless 2/4] wifi: rtl8xxxu: unwind incomplete receive startup To: Jes Sorensen Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" rtl8xxxu_start() allocates and submits RX URBs one at a time. If a later allocation fails, earlier requests may already be active. After a successful submission, that allocation failure can also leave ret set to zero. The error path then frees TX resources and disables RX filters without draining the earlier RX requests, yet reports startup success. Separate pool allocation from submission so an allocation failure can be handled before any RX request is active. Introduce rtl8xxxu_alloc_rx_urbs() to allocate all 32 wrappers, then rtl8xxxu_start_rx() to submit the completed pool. Return ENOMEM for every RX or TX URB pool allocation failure so a partial allocation is reported as an error. Once submission begins, keep ENOMEM/EAGAIN failures queued for retry. For other submission errors, rtl8xxxu_start_rx() frees the unsubmitted requests and returns the error. Since earlier submissions may already be active at that point, route the outer start failure through the existing rtl8xxxu_stop() path. This drains queued work and active requests and cleans up RF state and TX resources together. Interrupt URB submission failure uses the same cleanup path. Fixes: 26f1fad29ad9 ("New driver: rtl8xxxu (mac80211)") Assisted-by: GPT-6 Astra Signed-off-by: kimwooseok <5mghybrid@khu.ac.kr> --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 99 ++++++++++++++------ 1 file changed, 71 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 795a5ec2f8cd4..1932a9ec1970c 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -58,6 +58,7 @@ MODULE_PARM_DESC(dma_agg_pages, "Set DMA aggregation pages (range 1-127, 0 to di #define RTL8XXXU_TX_URB_LOW_WATER 25 #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); @@ -5832,6 +5833,27 @@ static void rtl8xxxu_free_rx_resources(struct rtl8xxxu_priv *priv) spin_unlock_irqrestore(&priv->rx_urb_lock, flags); } +static 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 =3D 0; i < RTL8XXXU_RX_URBS; i++) { + rx_urb =3D kmalloc_obj(struct rtl8xxxu_rx_urb); + if (!rx_urb) + return -ENOMEM; + + usb_init_urb(&rx_urb->urb); + INIT_LIST_HEAD(&rx_urb->list); + rx_urb->hw =3D priv->hw; + list_add_tail(&rx_urb->list, &priv->rx_urb_pending_list); + priv->rx_urb_pending_count++; + } + + return 0; +} + static void rtl8xxxu_queue_rx_urb(struct rtl8xxxu_priv *priv, struct rtl8xxxu_rx_urb *rx_urb) { @@ -5900,6 +5922,44 @@ static void rtl8xxxu_rx_urb_work(struct work_struct *work) } } +static int rtl8xxxu_start_rx(struct rtl8xxxu_priv *priv) +{ + struct rtl8xxxu_rx_urb *rx_urb, *tmp; + unsigned long flags; + LIST_HEAD(local); + int ret; + + spin_lock_irqsave(&priv->rx_urb_lock, flags); + list_splice_init(&priv->rx_urb_pending_list, &local); + priv->rx_urb_pending_count =3D 0; + spin_unlock_irqrestore(&priv->rx_urb_lock, flags); + + list_for_each_entry_safe(rx_urb, tmp, &local, list) { + list_del_init(&rx_urb->list); + ret =3D rtl8xxxu_submit_rx_urb(priv, rx_urb); + switch (ret) { + case 0: + break; + case -ENOMEM: + case -EAGAIN: + rtl8xxxu_queue_rx_urb(priv, rx_urb); + break; + default: + usb_free_urb(&rx_urb->urb); + goto free_remaining; + } + } + + return 0; + +free_remaining: + list_for_each_entry_safe(rx_urb, tmp, &local, list) { + list_del(&rx_urb->list); + usb_free_urb(&rx_urb->urb); + } + return ret; +} + /* * The RTL8723BU/RTL8192EU vendor driver use coexistence table type * 0-7 to represent writing different combinations of register values @@ -7408,7 +7468,6 @@ static void rtl8xxxu_watchdog_callback(struct work_struct *work) static int rtl8xxxu_start(struct ieee80211_hw *hw) { struct rtl8xxxu_priv *priv =3D hw->priv; - struct rtl8xxxu_rx_urb *rx_urb; struct rtl8xxxu_tx_urb *tx_urb; unsigned long flags; int ret, i; @@ -7423,14 +7482,13 @@ static int rtl8xxxu_start(struct ieee80211_hw *hw) if (priv->usb_interrupts) { ret =3D rtl8xxxu_submit_int_urb(hw); if (ret) - goto exit; + goto error_out; } for (i =3D 0; i < RTL8XXXU_TX_URBS; i++) { tx_urb =3D kmalloc_obj(struct rtl8xxxu_tx_urb); if (!tx_urb) { - if (!i) - ret =3D -ENOMEM; + ret =3D -ENOMEM; goto error_out; } @@ -7441,31 +7499,21 @@ static int rtl8xxxu_start(struct ieee80211_hw *hw) priv->tx_urb_free_count++; } + ret =3D rtl8xxxu_alloc_rx_urbs(priv); + if (ret) + goto error_out; + priv->tx_stopped =3D false; spin_lock_irqsave(&priv->rx_urb_lock, flags); priv->shutdown =3D false; spin_unlock_irqrestore(&priv->rx_urb_lock, flags); - for (i =3D 0; i < RTL8XXXU_RX_URBS; i++) { - rx_urb =3D kmalloc_obj(struct rtl8xxxu_rx_urb); - if (!rx_urb) { - if (!i) - ret =3D -ENOMEM; - - goto error_out; - } - usb_init_urb(&rx_urb->urb); - INIT_LIST_HEAD(&rx_urb->list); - rx_urb->hw =3D hw; - - ret =3D rtl8xxxu_submit_rx_urb(priv, rx_urb); - if (ret) - rtl8xxxu_queue_rx_urb(priv, rx_urb); - } + ret =3D rtl8xxxu_start_rx(priv); + if (ret) + goto error_out; schedule_delayed_work(&priv->ra_watchdog, 2 * HZ); -exit: /* * Accept all data and mgmt frames */ @@ -7478,13 +7526,7 @@ exit: return ret; error_out: - rtl8xxxu_free_tx_resources(priv); - /* - * Disable all data and mgmt frames - */ - rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0x0000); - rtl8xxxu_write16(priv, REG_RXFLTMAP0, 0x0000); - + rtl8xxxu_stop(hw, false); return ret; } @@ -7820,6 +7862,7 @@ static int rtl8xxxu_probe(struct usb_interface *inter= face, spin_lock_init(&priv->tx_urb_lock); INIT_LIST_HEAD(&priv->rx_urb_pending_list); spin_lock_init(&priv->rx_urb_lock); + priv->shutdown =3D true; INIT_WORK(&priv->rx_urb_wq, rtl8xxxu_rx_urb_work); INIT_DELAYED_WORK(&priv->ra_watchdog, rtl8xxxu_watchdog_callback); INIT_DELAYED_WORK(&priv->update_beacon_work, rtl8xxxu_update_beacon_work_callback); --=20 2.48.1 From nobody Fri Sep 25 12:43:31 2026 Received: from mail-lf2-f12.google.com (mail-lf2-f12.google.com [74.125.229.204]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 77B8038B15B for ; Sat, 12 Sep 2026 20:30:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=74.125.229.204 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789245008; cv=pass; b=rgTl3bvTd9FG/hWsw0TSxwSlvnjcNgpo2ABUqsmmuDhfhPFXP63BODod9mBfxtf0Ldg5J20qOLvH3ii5SvRrmfRIgFCNMY/5CD9F5wPQQKssqOOO4PpHbmNOAZBLi9dZJIciI6gK6C0WerbNoxZj89SPQZW07ee33jyH0OA+vCI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789245008; c=relaxed/simple; bh=4BJFZkbbQM/TpEDNgfqVCNZkBAyQZLPvnY6GdgSNQTA=; h=MIME-Version:From:In-Reply-To:References:Date:Message-ID:Subject: To:Cc:Content-Type; b=WekLcVezPn9pKzt3Yf2ozvmFBh1wBB3++asaJdkVobNRJQ/jNEG33ivAvUHlYchSG7Mp7Epm0FT3gubwQbTvGTwzS14a97nxxOmD4q/V2dTlY3OWmFZWf6GU/e15O4HUp0LTTMteOWLSe9m/pcQuYZakdcd1ckWYgyuH1wIzViI= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=khu.ac.kr; spf=pass smtp.mailfrom=khu.ac.kr; dkim=pass (1024-bit key) header.d=khu.ac.kr header.i=@khu.ac.kr header.b=GlNoYTu4; arc=pass smtp.client-ip=74.125.229.204 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=khu.ac.kr Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=khu.ac.kr Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=khu.ac.kr header.i=@khu.ac.kr header.b="GlNoYTu4" Received: by mail-lf2-f12.google.com with SMTP id 2adb3069b0e04-5b5e4f13d7aso765270e87.1 for ; Sat, 12 Sep 2026 13:30:04 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1789245002; cv=none; d=google.com; s=arc-20260327; b=IzVvYc72lpCO9avgogDjNo3EvUFwCpBrQS6ALbGfGUDWzTLsuXiwWwRmGcYa7qoaMq uqjCC1nrUtYVP68D1S6kSHwn2YcDzlDyNpYDabMpYuLAqf6p2ru1zBk8eh2J0wDQFd19 IHHvrV4ycGlfkx9tXVzJuLGg5X0qPfQYxuntNhL5dKBNr2E0Vgulj+OhjSuUOzi59Rv/ C8tigl8ZJcKE8eYIA85FXsGcjCOAJkgr3BYOkxbxkY1rJpgj0BAMWYQ98OvE9TpYYKKd QYtRjI/hBX4TZKOBqQSxz5n2s4NKWSVN7pNC2bgFXQX+OSBeUIdM0cUW0yV78/3ohbod ZxnA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:subject:message-id:date:references:in-reply-to:from :mime-version:dkim-signature; bh=g6Pm8uj00vDZGpmdO+6bFaNiQGBkyOpHFptkpjaCoQs=; fh=waaIx5dEAVV2pkXZY/WNMv73vMBb2Fbaa9VRh+clABs=; b=c6Vqx0+LQnyR1JRjCCmzd150q0pBUyBH9tleJ9Jj0UiKg0qGtQCRZbNplr2CfsckfQ DKtl08WHGehDdxZ4PK1738ZtTmk4XZGtje7vNUB/4tXcDQInsUswqEJuJRbD7kazxbR4 woeDYY0FWOG9JNTUXE4ZONxzdU1R2x5X8ApBd1vZswytoJDFIG6P2fHqQqCUkLQauMcJ R4Gv+sYucxFPOmq7nc2dU3K2HFvvlmlJTZUs0xXAgwuEqpunLK4bVsfnjj6FZ/XJnykb 2nn4GQPloiEkY5yP/mqZZ1xwIvcUHYrOOmUjhBife+oleTAr2Sph0GmdoM59JbnPG+hh gP/g==; darn=vger.kernel.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=khu.ac.kr; s=google; t=1789245002; x=1789849802; darn=vger.kernel.org; h=content-type:cc:to:subject:message-id:date:references:in-reply-to :from:mime-version:from:to:cc:subject:date:message-id:reply-to :content-type; bh=g6Pm8uj00vDZGpmdO+6bFaNiQGBkyOpHFptkpjaCoQs=; b=GlNoYTu4/tJjOnuuIuVoCg7OKQrZlu3KDrl2/ZpI4abGmS26r8/D7u6Uf9jN+NAlDV HiUM4DWBGMAsukab+biS6AwkuM8BG27YyQ3+93PcOXpN3HZ2ieu1vzecwnsJ2qRqOjA6 5gePCXbv1HadLgNn5sOaL8i1+tIgW7dtzDw2M= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789245002; x=1789849802; h=content-type:cc:to:subject:message-id:date:references:in-reply-to :from:mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to:content-type; bh=g6Pm8uj00vDZGpmdO+6bFaNiQGBkyOpHFptkpjaCoQs=; b=RIkU9h1gzOWUMs12LKZZ0pgRT/cXreCoEv3iUSNpryMNGtuBdK043s+BnUHNumEt9u JhRCJy4kKZLueaWozrlMHVj1HozPISw3W2z+k57z0Z2qjj+gDjm84FeL+AL1D1jEN45J qpYGlr+U9fbrJ3ePyCPc4dmLL2vO4rUxdmDnVpiNXz5SN9Ujuk9ziIrznTc+SjeBLr90 oYI9VSRrWtiRl+FZdyroPbakW+1Yg03OCz8YCO+8tQHTIXNJi+5IHIovJdDbV7bLapvq a1Lh3SLKZlpA52kdJ6BX3O3oI3KUh1e4rsdgo8dyPuGvzPhfnu1ePJnzZ70qTFLeDv9h wDJw== X-Forwarded-Encrypted: i=1; AKwUvBzxZSi2WGWRCpiDurPn6Ca+w6psSboOQv0p4ja2e5+82SJWLMK8cENSL9YQw1OBX5ZIvFLqXiInXwHKgVg=@vger.kernel.org X-Gm-Message-State: AFuF++kHZ4weUUVXxbtt/EMkJWTxxUg6dpfkbUUMCfR7xbj3tNs/Hel8 uBoW4swwAPqnDoIyLicmw+JifvG7FrkyRbEZxwPSC+/i3656fkV33KffTOxA69kuV/m60olWlZ3 qQoWbQnIbO0lspZohpOvRGM9nMiMSdY8jKV3MTpxWH8U1jg3I5Vny X-Gm-Gg: AYBFou0BIzytYBfEKLtQfWg3uuR7uwS6OZJq5tBBwFSgk1SRS+u6oqEtz+0WIzfUgij apzQ/iX+w/M84r1hNkc3lr3/FY5aZ7vNltYLuZfZVHc5eiD7NLCIJlMnpZQNT2vkzpU8RU0MqUg qjgfDNK4IqJvbiqLLI6bNOKBQNQjVF6GwOMRPok5Dr9ojJ6W+P1M44bgcei7X7ykKRilrRnIzbl iUqq9gBXnQ6AMEBnM+biBPmS1KjUILoQpM3tkLlydTdV2BqtrH+6f6O+Y+gIfuYDBtoF7Jal6+v kV3GtML18M2whxo8NQ8YW3ia0HAj+lAV07+6SE7kR7KR2Sk7vEw= X-Received: by 2002:a05:6512:aca:b0:5b6:1a7c:27 with SMTP id 2adb3069b0e04-5b8a8ebef8bmr688989e87.42.1789245002235; Sat, 12 Sep 2026 13:30:02 -0700 (PDT) Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:30:00 -0400 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 13:28:22 -0700 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 15:20:17 -0500 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:09:52 -0400 From: kimwooseok <5mghybrid@khu.ac.kr> In-Reply-To: References: Date: Sat, 12 Sep 2026 16:30:00 -0400 X-Gm-Features: AcwNN1WT4aMWlmVlxnGTGx87l59oN-wznF_pEmsob68vpR5yn0wdR6MFNtw3CH4 Message-ID: Subject: [PATCH wireless 3/4] wifi: rtl8xxxu: preserve RX requests across recoverable transfer errors To: Jes Sorensen Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" rtl8xxxu normally reuses 32 RX URBs, scheduling the submission worker when more than eight completed requests have accumulated on the pending list. Completion errors free URBs instead. A finite error burst can therefore leave eight or fewer requests, which cannot reach that threshold after they all complete. With no request in flight and no worker pending or running, RX stays stopped even after the errors cease. To prevent these errors from shrinking the pool below the number needed for normal resubmission, retain URBs after EPROTO, EILSEQ, ETIME, EOVERFLOW, ECOMM and ENOSR completions. EHCI can report ENOSR for IN data-b= uffer errors, and FHCI maps RX buffer overrun to ECOMM. Free the failed transfer's skb and keep its URB on a separate retry list. Keeping the URBs is only part of the fix: the driver must also submit them again without waiting for nine requests to accumulate. When the first failed request enters the retry list, schedule delayed work for 100 ms. Further failures join that list while the work is pending. When it runs, move the collected requests to normal pending and schedule the submission worker even if only one request is waiting. Keeping failed requests separate until then prevents normal completions from triggering an immediate retry; successful RX keeps its existing batching. A retry can itself fail with ENOMEM/EAGAIN. Returning that request to normal pending would bring back the same threshold problem, so route temporary submission failures from both start and the RX worker through the delayed retry list as well. Serialize retry insertion and scheduling with shutdown so late completions cannot schedule fresh retries during stop. Cancel retry work first, then wait for submission work before killing active URBs, so a running worker cannot submit a request after the active requests have been drained. Cancellation and removal keep their release behavior. EPIPE endpoint-halt recovery remains outside this change because it requires quiescing requests and distinguishing recovery cancellation from shutdown. Fixes: 26f1fad29ad9 ("New driver: rtl8xxxu (mac80211)") Assisted-by: GPT-6 Astra Signed-off-by: kimwooseok <5mghybrid@khu.ac.kr> --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 71 +++++++++++++++++-- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 3 + 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 1932a9ec1970c..883c9a56f52a4 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -54,6 +54,7 @@ MODULE_PARM_DESC(dma_agg_pages, "Set DMA aggregation pages (range 1-127, 0 to di #define USB_VENDOR_ID_REALTEK 0x0bda #define RTL8XXXU_RX_URBS 32 #define RTL8XXXU_RX_URB_PENDING_WATER 8 +#define RTL8XXXU_RX_URB_RETRY_DELAY_MS 100 #define RTL8XXXU_TX_URBS 64 #define RTL8XXXU_TX_URB_LOW_WATER 25 #define RTL8XXXU_TX_URB_HIGH_WATER 32 @@ -5823,6 +5824,11 @@ static void rtl8xxxu_free_rx_resources(struct rtl8xxxu_priv *priv) spin_lock_irqsave(&priv->rx_urb_lock, flags); + list_splice_tail_init(&priv->rx_urb_retry_list, + &priv->rx_urb_pending_list); + priv->rx_urb_pending_count +=3D priv->rx_urb_retry_count; + priv->rx_urb_retry_count =3D 0; + list_for_each_entry_safe(rx_urb, tmp, &priv->rx_urb_pending_list, list) { list_del(&rx_urb->list); @@ -5881,6 +5887,47 @@ 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) +{ + struct rtl8xxxu_priv *priv =3D container_of(to_delayed_work(work), + struct rtl8xxxu_priv, + rx_urb_retry_wq); + unsigned long flags; + + spin_lock_irqsave(&priv->rx_urb_lock, flags); + + if (!priv->shutdown && priv->rx_urb_retry_count) { + list_splice_tail_init(&priv->rx_urb_retry_list, + &priv->rx_urb_pending_list); + priv->rx_urb_pending_count +=3D priv->rx_urb_retry_count; + priv->rx_urb_retry_count =3D 0; + /* An error must not leave a small batch waiting indefinitely. */ + schedule_work(&priv->rx_urb_wq); + } + + spin_unlock_irqrestore(&priv->rx_urb_lock, flags); +} + +static void rtl8xxxu_queue_rx_urb_retry(struct rtl8xxxu_priv *priv, + struct rtl8xxxu_rx_urb *rx_urb) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->rx_urb_lock, flags); + + if (!priv->shutdown) { + 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)); + } else { + usb_free_urb(&rx_urb->urb); + } + + spin_unlock_irqrestore(&priv->rx_urb_lock, flags); +} + static void rtl8xxxu_rx_urb_work(struct work_struct *work) { struct rtl8xxxu_priv *priv; @@ -5904,7 +5951,7 @@ static void rtl8xxxu_rx_urb_work(struct work_struct *= work) ret =3D rtl8xxxu_submit_rx_urb(priv, rx_urb); /* * If out of memory or temporary error, put it back on the - * queue and try again. Otherwise the device is dead/gone + * delayed queue and try again. Otherwise the device is dead/gone * and we should drop it. */ switch (ret) { @@ -5912,7 +5959,7 @@ static void rtl8xxxu_rx_urb_work(struct work_struct *= work) break; case -ENOMEM: case -EAGAIN: - rtl8xxxu_queue_rx_urb(priv, rx_urb); + rtl8xxxu_queue_rx_urb_retry(priv, rx_urb); break; default: dev_warn(&priv->udev->dev, @@ -5942,7 +5989,7 @@ static int rtl8xxxu_start_rx(struct rtl8xxxu_priv *pr= iv) break; case -ENOMEM: case -EAGAIN: - rtl8xxxu_queue_rx_urb(priv, rx_urb); + rtl8xxxu_queue_rx_urb_retry(priv, rx_urb); break; default: usb_free_urb(&rx_urb->urb); @@ -6618,7 +6665,20 @@ static void rtl8xxxu_rx_complete(struct urb *urb) rtl8xxxu_queue_rx_urb(priv, rx_urb); } else { dev_dbg(dev, "%s: status %i\n", __func__, urb->status); - goto cleanup; + switch (urb->status) { + case -EPROTO: + case -EILSEQ: + case -ETIME: + case -EOVERFLOW: + case -ECOMM: + case -ENOSR: + dev_kfree_skb(skb); + urb->context =3D NULL; + rtl8xxxu_queue_rx_urb_retry(priv, rx_urb); + return; + default: + goto cleanup; + } } return; @@ -7549,6 +7609,7 @@ static void rtl8xxxu_stop(struct ieee80211_hw *hw, bool suspend) * it drained via rtl8xxxu_submit_rx_urb(), so a worker still running * after the kill could submit a URB that escapes it. */ + cancel_delayed_work_sync(&priv->rx_urb_retry_wq); cancel_work_sync(&priv->rx_urb_wq); usb_kill_anchored_urbs(&priv->rx_anchor); @@ -7861,9 +7922,11 @@ static int rtl8xxxu_probe(struct usb_interface *interface, INIT_LIST_HEAD(&priv->tx_urb_free_list); spin_lock_init(&priv->tx_urb_lock); INIT_LIST_HEAD(&priv->rx_urb_pending_list); + INIT_LIST_HEAD(&priv->rx_urb_retry_list); spin_lock_init(&priv->rx_urb_lock); priv->shutdown =3D true; INIT_WORK(&priv->rx_urb_wq, rtl8xxxu_rx_urb_work); + INIT_DELAYED_WORK(&priv->rx_urb_retry_wq, rtl8xxxu_rx_urb_retry_work); INIT_DELAYED_WORK(&priv->ra_watchdog, rtl8xxxu_watchdog_callback); INIT_DELAYED_WORK(&priv->update_beacon_work, rtl8xxxu_update_beacon_work_callback); skb_queue_head_init(&priv->c2hcmd_queue); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index eeb18eb0e4c0f..ee55f6cc7f012 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1808,8 +1808,11 @@ struct rtl8xxxu_priv { spinlock_t rx_urb_lock; struct list_head rx_urb_pending_list; int rx_urb_pending_count; + struct list_head rx_urb_retry_list; + int rx_urb_retry_count; bool shutdown; struct work_struct rx_urb_wq; + struct delayed_work rx_urb_retry_wq; u8 mac_addr[ETH_ALEN]; char chip_name[8]; --=20 2.48.1 From nobody Fri Sep 25 12:43:31 2026 Received: from mail-lr2-f12.google.com (mail-lr2-f12.google.com [74.125.230.76]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 731883EE1DB for ; Sat, 12 Sep 2026 20:30:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=74.125.230.76 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789245021; cv=pass; b=t2Vn7UYBNVJOVWT7Tt1X8hoT+yhJcpnNQLPG+sAg1lfb2VSdAhLCKajFz5iFuVw0ExkFHmMmmqpLqHG2bZ30JyQE0mvAfLaLYzGkLiKZabdTgFvn1rypsYn0eNsZtDnT6dqKRCeMWDmBw45LV7TRxfoUQtGbsfUZy9hCWdyBVYM= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789245021; c=relaxed/simple; bh=TEbJMMzIIogJg9LII0HnjQZYE7NIbdU378YChfhTp+A=; h=MIME-Version:From:In-Reply-To:References:Date:Message-ID:Subject: To:Cc:Content-Type; b=d1bYNwoYGiBP2b2bFt9PDh+G4bHJohjqgylXsVS88mugnB9k7TuXyEKqbNtc49W87KkRRCSBqWK+6+shXwOeo+hpbPDJNd+rWAyS2cq4+U4HvppcL35e8VidzvmKZXNfXz2yYKskpdHcW+Tuw1DhvJKlU0JvAzvmjzhjP7JKwm4= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=khu.ac.kr; spf=pass smtp.mailfrom=khu.ac.kr; dkim=pass (1024-bit key) header.d=khu.ac.kr header.i=@khu.ac.kr header.b=ehjczFV9; arc=pass smtp.client-ip=74.125.230.76 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=khu.ac.kr Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=khu.ac.kr Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=khu.ac.kr header.i=@khu.ac.kr header.b="ehjczFV9" Received: by mail-lr2-f12.google.com with SMTP id 38308e7fff4ca-3a59bc470d6so8903081fa.0 for ; Sat, 12 Sep 2026 13:30:17 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1789245015; cv=none; d=google.com; s=arc-20260327; b=olAk/2diqS7JhJ1oiqVWtlktxzmXFQSc80FVVP/uqLnL6j6Qq/PtRDe3zqLx9pgMow zxavMWQYriSsHTzCdo0yEcZBAHU6AhxetVa/vedxdItw0NRxoGxz05PWvqzpryfhuxvz lmjNGRu9SFoNLImlWj+5CrhADAoKD3o+o0qtYwDpy6rFm8urBmK0mBPLErmXAQQSjOL7 PFdKxI9muUsKibp0dzZI3DlcKFScjgLJVttgEwzzoxQAbCqzxTYpw9OWWjpOqSvw5FZu /fOw4EBkxEieNGXWFiEnS+i0n3GTliNoW5ph8aVnKjbDKe6wJ8C+DONNWVrXB0xLIeW8 JRlw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:subject:message-id:date:references:in-reply-to:from :mime-version:dkim-signature; bh=wwwR6p6sEQJ3aQk1slQ0OuS5zPcXHPa1FuWl+bSWenA=; fh=VMEj+8mp+VxkptzKx14nk0mGiggC//OHaO7z4Tou++c=; b=GL04NXRH2V5yJbtvxjxErFQ9aIjgYqo7KaNpgkRvaJ77wFqYdoJmVvi1DiT5rxaiNm m595JlT7F2Q05pvXLpPnBGAEZGpT0lrqCNw7RtyHTFEwQEI2cPYy1rlddROYPJnGvFkH 6BaWD1jvMUPphkn6rfxr4tybShHsT0EvDGOS6Le51dn20ac+jqgNTyXRaBhsW/t2FmeL s4LrhTusSBMipt7hqExn5UyVJFxfyODRNr/J9JytEkp0f2ym5GOaQxpsDVGhvK2QuZQg d1TEcO/mQ2hpXE38362PmvKGBg81+8m32DaK4XWX1U3DQuNFElUkICiGhG4lBpv7opyQ dEXA==; darn=vger.kernel.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=khu.ac.kr; s=google; t=1789245015; x=1789849815; darn=vger.kernel.org; h=content-type:cc:to:subject:message-id:date:references:in-reply-to :from:mime-version:from:to:cc:subject:date:message-id:reply-to :content-type; bh=wwwR6p6sEQJ3aQk1slQ0OuS5zPcXHPa1FuWl+bSWenA=; b=ehjczFV9986wzZ/v+eyo5OOvrtGc7MYzbeTObprznoNXN6JuO3l1YgcjypzCpoa7Um KsELhGTmog6mV51FfCbTPcU0fA9ud/cTN3QWcZZQ07ZsOLoSr2vWIRRtkLh3V/FxaclH k5OsPvvayuqy7TssB8l4mH/8s/nIUV20ySp4o= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789245015; x=1789849815; h=content-type:cc:to:subject:message-id:date:references:in-reply-to :from:mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to:content-type; bh=wwwR6p6sEQJ3aQk1slQ0OuS5zPcXHPa1FuWl+bSWenA=; b=ZW2ZE/sTXiDzfNMaV0ScvTD9RJmRvxZCKcxie/q4VhBoG9oDF2Js7cIuO9dl6DYN8s roA6Y7GdXGthXRb7o3Dp1paq2gFOl+nZCfR4CpD2WNIm4oeiIG+mLexTbidRYu/LoEVF JRmtHL7mFaib/rcaSa4oWxnUB54EHNUGs1UJdI4kdtTrM8L4IyVKTvBjrm1dS1If/Pbt uQYujvXNT5k4qeaporPuT2BcH34QTw5PPDuNk9Q+uBOqtbdQFCD4mbk6DOoBquWbTzZH Dojsr4VMyDNL7QfNQGibmUSQYVXkKnelbrt3A+c0CN0VXozr+Iptu9RAh8fwsTYOgx6j 8sqg== X-Forwarded-Encrypted: i=1; AKwUvBxzpW5pouRgt3kUVmkJyEOsu6r1AKHYZE7RN+lOuMKHZ7B1fENH3FuXacVD9D6wPMz6plD3et1lccKS3SE=@vger.kernel.org X-Gm-Message-State: AFuF++mxQd1klX9ejZFR650qmTAiEEwvEnlRUENQ98wpeXv5uwVTG6ya z0NXvouPVe6+SFU04r2fuxjZmFg+2J/cT/90TI9swkET/l3jOw90cIm2K/36ICp7trnefUOkesm ufN4Sf6JLeMiKB2fI3fpXd8q+P0TGWNyjFzNgFrAxLw== X-Gm-Gg: AYBFou37TRovwWKAvT7rKXqp6WQQxonjGtZBz17Aam/9G+5cJBVA+0Oi4Erx72HkDFa q5hxZqRy21HspElVfBq0PoOStrAzCalf44EJKJK8cY/e7RJP8VQBQzEGYTlmwFSM6THnBiuclos PoAZ4QrxeiGEa3RFR2quY92yVnr38cB6CPbJoTOkpuBFZcLsHIWqxOi5Mwo3o6I7joj7+kYxzH+ lE5R62Br2raE2oT2DOLM+CSqsAjxVqLcz6CWetlgV4/hhAz8eNAXZO7KQp+bIy+GZgwaKlgFc+U Ll7kswKCKHO/w0Gsi6WeJbLy7EDpz8A0OW38N5hTVVsENrdOsR4= X-Received: by 2002:a05:6512:318e:b0:5b4:97db:b740 with SMTP id 2adb3069b0e04-5b8a03149d1mr4423685e87.20.1789245014804; Sat, 12 Sep 2026 13:30:14 -0700 (PDT) Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:30:13 -0400 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:28:26 -0400 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:20:20 -0400 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Received: from 77377267392 named unknown by gmailapi.google.com with HTTPREST; Sat, 12 Sep 2026 16:09:53 -0400 From: kimwooseok <5mghybrid@khu.ac.kr> In-Reply-To: References: Date: Sat, 12 Sep 2026 16:30:13 -0400 X-Gm-Features: AcwNN1X5amjBl7U2ctbrm9dqJOnYph90GlGZtflpKC3Lk7Jyp_TU-EfL2V0pPQA Message-ID: Subject: [PATCH wireless 4/4] wifi: rtl8xxxu: test RX ownership and recovery across failures To: Jes Sorensen Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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> --- .../wireless/realtek/rtl8xxxu/.kunitconfig | 16 + drivers/net/wireless/realtek/rtl8xxxu/Kconfig | 11 + .../net/wireless/realtek/rtl8xxxu/Makefile | 3 + drivers/net/wireless/realtek/rtl8xxxu/core.c | 66 ++- .../net/wireless/realtek/rtl8xxxu/rx-test.c | 461 ++++++++++++++++++ .../net/wireless/realtek/rtl8xxxu/rx-test.h | 27 + 6 files changed, 569 insertions(+), 15 deletions(-) create mode 100644 drivers/net/wireless/realtek/rtl8xxxu/.kunitconfig create mode 100644 drivers/net/wireless/realtek/rtl8xxxu/rx-test.c create mode 100644 drivers/net/wireless/realtek/rtl8xxxu/rx-test.h diff --git a/drivers/net/wireless/realtek/rtl8xxxu/.kunitconfig b/drivers/net/wireless/realtek/rtl8xxxu/.kunitconfig new file mode 100644 index 0000000000000..decf383c3d51e --- /dev/null +++ b/drivers/net/wireless/realtek/rtl8xxxu/.kunitconfig @@ -0,0 +1,16 @@ +CONFIG_MODULES=3Dy +CONFIG_KUNIT=3Dy +CONFIG_KUNIT_ALL_TESTS=3Dn +CONFIG_NET=3Dy +CONFIG_NETDEVICES=3Dy +CONFIG_WIRELESS=3Dy +CONFIG_CFG80211=3Dy +CONFIG_MAC80211=3Dy +CONFIG_WLAN=3Dy +CONFIG_WLAN_VENDOR_REALTEK=3Dy +CONFIG_USB_SUPPORT=3Dy +CONFIG_USB=3Dy +CONFIG_NEW_LEDS=3Dy +CONFIG_LEDS_CLASS=3Dy +CONFIG_RTL8XXXU=3Dy +CONFIG_RTL8XXXU_KUNIT_TEST=3Dy diff --git a/drivers/net/wireless/realtek/rtl8xxxu/Kconfig b/drivers/net/wireless/realtek/rtl8xxxu/Kconfig index 14d0343368ac0..e2c16d62293a4 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/Kconfig +++ b/drivers/net/wireless/realtek/rtl8xxxu/Kconfig @@ -35,3 +35,14 @@ config RTL8XXXU_UNTESTED author or reported to be working by third parties. Please report your results! + +config RTL8XXXU_KUNIT_TEST + tristate "KUnit tests for rtl8xxxu RX recovery" if !KUNIT_ALL_TESTS + depends on KUNIT && RTL8XXXU + default KUNIT_ALL_TESTS + help + Exercise RX buffer ownership, startup and recovery with the real + driver functions and controlled allocation and USB submission results. + These tests do not require a USB adapter. + + If unsure, say N. 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) +=3D rtl8xxxu.o rtl8xxxu-y :=3D core.o 8192e.o 8723b.o \ 8723a.o 8192c.o 8188f.o \ 8188e.o 8710b.o 8192f.o + +obj-$(CONFIG_RTL8XXXU_KUNIT_TEST) +=3D rtl8xxxu-rx-test.o +rtl8xxxu-rx-test-y :=3D 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 #include "regs.h" #include "rtl8xxxu.h" +#include "rx-test.h" +#include #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[] =3D { { .bitrate =3D 10, .hw_value =3D DESC_RATE_1M, .flags =3D 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); + 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 *pri= v) +{ + 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 *pri= v) { 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 =3D 0; i < RTL8XXXU_RX_URBS; i++) { - rx_urb =3D kmalloc_obj(struct rtl8xxxu_rx_urb); + rx_urb =3D 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 =3D 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 @@ free_remaining: } 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 =3D container_of(urb, struct rtl8xxxu_rx_urb, urb); @@ -6686,9 +6720,10 @@ cleanup: 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 =3D priv->fops; struct sk_buff *skb; @@ -6704,7 +6739,7 @@ static int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv, skb_size =3D IEEE80211_MAX_FRAME_LEN + rx_desc_sz; } - skb =3D __netdev_alloc_skb(NULL, skb_size, GFP_KERNEL); + skb =3D 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 =3D usb_submit_urb(&rx_urb->urb, GFP_ATOMIC); + ret =3D 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 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 +#include +#include + +#include "rtl8xxxu.h" +#include "rx-test.h" + +#define RX_TEST_URBS 32 +#define RX_TEST_SKBS 512 + +struct rx_test { + 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 =3D rx_current(); + struct rtl8xxxu_rx_urb *rx; + + ctx->alloc_calls++; + if (ctx->alloc_calls =3D=3D ctx->alloc_fail_at) + return NULL; + rx =3D kmalloc_obj(struct rtl8xxxu_rx_urb); + if (rx) + ctx->urbs[ctx->allocated++] =3D rx; + return rx; +} + +static struct sk_buff *rx_alloc_buffer(unsigned int size) +{ + struct rx_test *ctx =3D rx_current(); + struct sk_buff *skb; + + if (ctx->fail_skb) + return NULL; + skb =3D alloc_skb(size, GFP_KERNEL); + if (skb) { + /* Retain an observer reference to detect a lost driver reference. */ + skb_get(skb); + ctx->skbs[ctx->buffers++] =3D skb; + } + return skb; +} + +static int rx_submit(struct urb *urb, gfp_t flags) +{ + struct rx_test *ctx =3D rx_current(); + + ctx->submit_calls++; + if (!ctx->submit_fail_at || + ctx->submit_calls =3D=3D ctx->submit_fail_at) + return ctx->submit_error; + return 0; +} + +static void rx_schedule_retry(struct rtl8xxxu_priv *priv) +{ + struct rx_test *ctx =3D rx_current(); + + /* Model the single pending delayed-work ticket, not a submission. */ + if (!ctx->retry_pending) { + ctx->retry_pending =3D true; + ctx->retry_arms++; + } +} + +static void rx_observe_work(struct work_struct *work) +{ + struct rtl8xxxu_priv *priv; + struct rx_test *ctx; + + priv =3D container_of(work, struct rtl8xxxu_priv, rx_urb_wq); + ctx =3D container_of(priv, struct rx_test, priv); + atomic_inc(&ctx->normal_runs); +} + +static struct rx_test *rx_init(struct kunit *test, bool fake_timer) +{ + struct rx_test *ctx; + + ctx =3D kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return NULL; + test->priv =3D ctx; + ctx->hw.priv =3D &ctx->priv; + ctx->priv.hw =3D &ctx->hw; + ctx->priv.udev =3D &ctx->udev; + ctx->priv.fops =3D &ctx->fops; + ctx->fops.rx_desc_size =3D 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); + 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_get_observers(struct rx_test *ctx) +{ + unsigned int i; + + for (i =3D 0; i < ctx->allocated; i++) + usb_get_urb(&ctx->urbs[i]->urb); +} + +static int rx_pool(struct rx_test *ctx, unsigned int count) +{ + struct rtl8xxxu_rx_urb *rx; + unsigned int i; + int ret; + + ret =3D rtl8xxxu_alloc_rx_urbs(&ctx->priv); + rx_get_observers(ctx); + if (ret) + return ret; + + /* A reduced pool also tests recovery without the normal watermark. */ + for (i =3D count; i < ctx->allocated; i++) { + rx =3D ctx->urbs[i]; + list_del_init(&rx->list); + ctx->priv.rx_urb_pending_count--; + usb_free_urb(&rx->urb); + } + return 0; +} + +static void rx_giveback(struct rx_test *ctx, unsigned int i, int status) +{ + struct urb *urb =3D &ctx->urbs[i]->urb; + + /* The fake USB boundary does not emulate HCD giveback. */ + usb_unanchor_urb(urb); + urb->status =3D status; + urb->actual_length =3D 0; + rtl8xxxu_rx_complete(urb); +} + +static void rx_finish(struct kunit *test, struct rx_test *ctx) +{ + struct urb *urb; + unsigned int i; + + ctx->priv.shutdown =3D true; + cancel_delayed_work_sync(&ctx->priv.rx_urb_retry_wq); + cancel_work_sync(&ctx->priv.rx_urb_wq); + for (i =3D 0; i < ctx->allocated; i++) { + urb =3D &ctx->urbs[i]->urb; + if (urb->anchor) + rx_giveback(ctx, i, -ENOENT); + } + rtl8xxxu_free_rx_resources(&ctx->priv); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_pending_count, 0); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 0); + KUNIT_EXPECT_TRUE(test, usb_anchor_empty(&ctx->priv.rx_anchor)); + for (i =3D 0; i < ctx->allocated; i++) { + urb =3D &ctx->urbs[i]->urb; + KUNIT_EXPECT_EQ(test, refcount_read(&urb->kref.refcount), 1); + usb_free_urb(urb); + } + for (i =3D 0; i < ctx->buffers; i++) { + KUNIT_EXPECT_EQ(test, refcount_read(&ctx->skbs[i]->users), 1); + kfree_skb(ctx->skbs[i]); + } +} + +static void rx_retry_step(struct kunit *test, struct rx_test *ctx) +{ + KUNIT_ASSERT_TRUE(test, ctx->retry_pending); + ctx->retry_pending =3D false; + rtl8xxxu_rx_urb_retry_work(&ctx->priv.rx_urb_retry_wq.work); + flush_work(&ctx->priv.rx_urb_wq); + /* Execute the actual worker in the test task so boundary stubs apply. */ + rtl8xxxu_rx_urb_work(&ctx->priv.rx_urb_wq); +} + +static void rx_completion_submit_chain(struct kunit *test) +{ + static const int errors[] =3D { + -EPROTO, -EILSEQ, -ETIME, -EOVERFLOW, -ECOMM, -ENOSR, + }; + static const int submissions[] =3D { -ENOMEM, -EAGAIN }; + struct rx_test *ctx; + unsigned int e, f, i; + int runs; + + for (e =3D 0; e < ARRAY_SIZE(errors); e++) { + for (f =3D 0; f < ARRAY_SIZE(submissions); f++) { + ctx =3D rx_init(test, true); + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_EQ(test, rx_pool(ctx, RX_TEST_URBS), 0); + KUNIT_ASSERT_EQ(test, rtl8xxxu_start_rx(&ctx->priv), 0); + for (i =3D 0; i < RX_TEST_URBS; i++) + rx_giveback(ctx, i, errors[e]); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 32); + ctx->submit_error =3D submissions[f]; + rx_retry_step(test, ctx); + runs =3D atomic_read(&ctx->normal_runs); + flush_work(&ctx->priv.rx_urb_wq); + KUNIT_EXPECT_EQ(test, atomic_read(&ctx->normal_runs), runs); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_pending_count, 0); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 32); + KUNIT_EXPECT_TRUE(test, ctx->retry_pending); + for (i =3D 0; i < ctx->buffers; i++) + KUNIT_EXPECT_EQ(test, + refcount_read(&ctx->skbs[i]->users), 1); + ctx->submit_error =3D 0; + rx_retry_step(test, ctx); + KUNIT_EXPECT_FALSE(test, ctx->retry_pending); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 0); + KUNIT_EXPECT_FALSE(test, usb_anchor_empty(&ctx->priv.rx_anchor)); + rx_finish(test, ctx); + } + } +} + +static void rx_worker_batch_sizes(struct kunit *test) +{ + static const unsigned int sizes[] =3D { 1, 8, 9, 32 }; + struct rx_test *ctx; + unsigned int n, pass; + int runs; + + for (n =3D 0; n < ARRAY_SIZE(sizes); n++) { + ctx =3D rx_init(test, true); + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_EQ(test, rx_pool(ctx, sizes[n]), 0); + ctx->submit_error =3D -EAGAIN; + rtl8xxxu_rx_urb_work(&ctx->priv.rx_urb_wq); + for (pass =3D 0; pass < 3; pass++) { + runs =3D atomic_read(&ctx->normal_runs); + flush_work(&ctx->priv.rx_urb_wq); + KUNIT_EXPECT_EQ(test, atomic_read(&ctx->normal_runs), runs); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, sizes[n]); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_pending_count, 0); + rx_retry_step(test, ctx); + } + ctx->submit_error =3D 0; + rx_retry_step(test, ctx); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 0); + rx_finish(test, ctx); + } +} + +static void rx_skb_allocation_failure(struct kunit *test) +{ + struct rx_test *ctx; + + ctx =3D rx_init(test, true); + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_EQ(test, rx_pool(ctx, 8), 0); + ctx->fail_skb =3D true; + rtl8xxxu_rx_urb_work(&ctx->priv.rx_urb_wq); + KUNIT_EXPECT_EQ(test, ctx->submit_calls, 0); + KUNIT_EXPECT_EQ(test, ctx->buffers, 0); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 8); + ctx->fail_skb =3D false; + rx_retry_step(test, ctx); + KUNIT_EXPECT_EQ(test, ctx->submit_calls, 8); + rx_finish(test, ctx); +} + +static void rx_start_allocation_failure(struct kunit *test) +{ + static const unsigned int positions[] =3D { 1, 8, 32 }; + struct rx_test *ctx; + unsigned int i; + + for (i =3D 0; i < ARRAY_SIZE(positions); i++) { + ctx =3D rx_init(test, true); + KUNIT_ASSERT_NOT_NULL(test, ctx); + ctx->alloc_fail_at =3D positions[i]; + KUNIT_EXPECT_EQ(test, rtl8xxxu_alloc_rx_urbs(&ctx->priv), -ENOMEM); + rx_get_observers(ctx); + KUNIT_EXPECT_EQ(test, ctx->allocated, positions[i] - 1); + KUNIT_EXPECT_EQ(test, ctx->submit_calls, 0); + KUNIT_EXPECT_FALSE(test, ctx->retry_pending); + rx_finish(test, ctx); + } +} + +static void rx_start_temporary_failure(struct kunit *test) +{ + static const int errors[] =3D { -ENOMEM, -EAGAIN }; + struct rx_test *ctx; + unsigned int i; + + for (i =3D 0; i < ARRAY_SIZE(errors); i++) { + ctx =3D rx_init(test, true); + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_EQ(test, rx_pool(ctx, 32), 0); + ctx->submit_error =3D errors[i]; + KUNIT_EXPECT_EQ(test, rtl8xxxu_start_rx(&ctx->priv), 0); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 32); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_pending_count, 0); + KUNIT_EXPECT_TRUE(test, ctx->retry_pending); + ctx->submit_error =3D 0; + rx_retry_step(test, ctx); + rx_finish(test, ctx); + } +} + +static void rx_start_fatal_failure(struct kunit *test) +{ + static const unsigned int positions[] =3D { 1, 8, 32 }; + struct rx_test *ctx; + unsigned int i; + + for (i =3D 0; i < ARRAY_SIZE(positions); i++) { + ctx =3D rx_init(test, true); + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_EQ(test, rx_pool(ctx, 32), 0); + ctx->submit_fail_at =3D positions[i]; + ctx->submit_error =3D -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); + } +} + +static void rx_terminal_completion(struct kunit *test) +{ + static const int errors[] =3D { + -ENOENT, -ECONNRESET, -ENODEV, -ESHUTDOWN, -EPIPE, + }; + struct rx_test *ctx; + unsigned int i; + + for (i =3D 0; i < ARRAY_SIZE(errors); i++) { + ctx =3D rx_init(test, true); + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_EQ(test, rx_pool(ctx, 1), 0); + KUNIT_ASSERT_EQ(test, rtl8xxxu_start_rx(&ctx->priv), 0); + rx_giveback(ctx, 0, errors[i]); + KUNIT_EXPECT_FALSE(test, ctx->retry_pending); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 0); + rx_finish(test, ctx); + } +} + +static void rx_shutdown_completion(struct kunit *test) +{ + struct rx_test *ctx; + + ctx =3D rx_init(test, true); + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_EQ(test, rx_pool(ctx, 1), 0); + KUNIT_ASSERT_EQ(test, rtl8xxxu_start_rx(&ctx->priv), 0); + ctx->priv.shutdown =3D true; + rx_giveback(ctx, 0, -EPROTO); + KUNIT_EXPECT_FALSE(test, ctx->retry_pending); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 0); + rx_finish(test, ctx); +} + +static void rx_shutdown_pending_retry(struct kunit *test) +{ + struct rx_test *ctx; + + ctx =3D rx_init(test, true); + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_EQ(test, rx_pool(ctx, 8), 0); + ctx->submit_error =3D -ENOMEM; + rtl8xxxu_rx_urb_work(&ctx->priv.rx_urb_wq); + ctx->priv.shutdown =3D true; + rtl8xxxu_rx_urb_retry_work(&ctx->priv.rx_urb_retry_wq.work); + flush_work(&ctx->priv.rx_urb_wq); + KUNIT_EXPECT_EQ(test, atomic_read(&ctx->normal_runs), 0); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 8); + rx_finish(test, ctx); +} + +static void rx_shutdown_submission_failure(struct kunit *test) +{ + struct rx_test *ctx; + + ctx =3D rx_init(test, true); + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_EQ(test, rx_pool(ctx, 8), 0); + ctx->priv.shutdown =3D true; + ctx->submit_error =3D -ENOMEM; + rtl8xxxu_rx_urb_work(&ctx->priv.rx_urb_wq); + KUNIT_EXPECT_FALSE(test, ctx->retry_pending); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 0); + rx_finish(test, ctx); +} + +static void rx_real_delayed_bridge(struct kunit *test) +{ + struct rx_test *ctx; + + ctx =3D rx_init(test, false); + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_EQ(test, rx_pool(ctx, 1), 0); + KUNIT_ASSERT_EQ(test, rtl8xxxu_start_rx(&ctx->priv), 0); + rx_giveback(ctx, 0, -EPROTO); + flush_delayed_work(&ctx->priv.rx_urb_retry_wq); + flush_work(&ctx->priv.rx_urb_wq); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_retry_count, 0); + KUNIT_EXPECT_EQ(test, ctx->priv.rx_urb_pending_count, 1); + KUNIT_EXPECT_EQ(test, atomic_read(&ctx->normal_runs), 1); + rx_finish(test, ctx); +} + +static struct kunit_case rx_cases[] =3D { + KUNIT_CASE(rx_completion_submit_chain), + KUNIT_CASE(rx_worker_batch_sizes), + KUNIT_CASE(rx_skb_allocation_failure), + KUNIT_CASE(rx_start_allocation_failure), + KUNIT_CASE(rx_start_temporary_failure), + KUNIT_CASE(rx_start_fatal_failure), + KUNIT_CASE(rx_terminal_completion), + KUNIT_CASE(rx_shutdown_completion), + KUNIT_CASE(rx_shutdown_pending_retry), + KUNIT_CASE(rx_shutdown_submission_failure), + KUNIT_CASE(rx_real_delayed_bridge), + {} +}; + +static struct kunit_suite rx_suite =3D { + .name =3D "rtl8xxxu-rx", + .test_cases =3D rx_cases, +}; + +kunit_test_suite(rx_suite); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); +MODULE_DESCRIPTION("rtl8xxxu RX recovery KUnit tests"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rx-test.h b/drivers/net/wireless/realtek/rtl8xxxu/rx-test.h new file mode 100644 index 0000000000000..295befdfe8144 --- /dev/null +++ b/drivers/net/wireless/realtek/rtl8xxxu/rx-test.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef RTL8XXXU_RX_TEST_H +#define RTL8XXXU_RX_TEST_H + +#include +#include + +struct rtl8xxxu_priv; +struct rtl8xxxu_rx_urb; +struct sk_buff; +struct urb; +struct work_struct; + +VISIBLE_IF_KUNIT struct rtl8xxxu_rx_urb *rtl8xxxu_alloc_rx_urb(void); +VISIBLE_IF_KUNIT struct sk_buff *rtl8xxxu_alloc_rx_skb(unsigned int size); +VISIBLE_IF_KUNIT int rtl8xxxu_rx_usb_submit(struct urb *urb, gfp_t flags); +VISIBLE_IF_KUNIT void rtl8xxxu_schedule_rx_retry(struct rtl8xxxu_priv *pri= v); +VISIBLE_IF_KUNIT int rtl8xxxu_alloc_rx_urbs(struct rtl8xxxu_priv *priv); +VISIBLE_IF_KUNIT int rtl8xxxu_start_rx(struct rtl8xxxu_priv *priv); +VISIBLE_IF_KUNIT int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv, + struct rtl8xxxu_rx_urb *rx_urb); +VISIBLE_IF_KUNIT void rtl8xxxu_free_rx_resources(struct rtl8xxxu_priv *pri= v); +VISIBLE_IF_KUNIT void rtl8xxxu_rx_complete(struct urb *urb); +VISIBLE_IF_KUNIT void rtl8xxxu_rx_urb_work(struct work_struct *work); +VISIBLE_IF_KUNIT void rtl8xxxu_rx_urb_retry_work(struct work_struct *work); + +#endif --=20 2.48.1