From patchwork Sat Sep 26 15:48:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3474 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 5DD6D1C5FA3 for ; Sat, 26 Sep 2026 17:52:06 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26266-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26266-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 9174139101 for ; Sat, 26 Sep 2026 15:49:30 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 66EB23655E0; Sat, 26 Sep 2026 15:49:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="JQnx1h8f" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f43.google.com (mail-oo2-f43.google.com [74.125.231.171]) (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 E8D5F370D61 for ; Sat, 26 Sep 2026 15:49:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437765; cv=none; b=i9rZPEdzkNwOKrlMDb222EVIA6j87A8mFzTcCYjdK2QuQ3fGn2nFFHPK6ZqYOVunbOZAyEVPVC9O7tq/m0vqiH45woeURoRqN0i9byF9v5OhZN5B2As9zvX1dCs4+RUVVL73os72dKkWxr8XAXSvkj7Umi/Q+t58j8mm+7uV9gI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437765; c=relaxed/simple; bh=KS/8rL8UeoBvoNAjD7fEnF4xbmiricnrL+uNpL65yjc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=c3s3SfOmexhPe6jxqUg9XzOLowB/ef8nLDbWS7SsmU1hH6yMQXxbYnnA0JE2ZPMVqMGE3R3Va24Mju1gj5vcYK+P9XJRyvUVMLsvLwr6SO8bOsYHD1t8tBbrL9hYrikHuXG/N6y81IdC2fV/N1SqrRkKD+kEovDUxMTJrZF2dGE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=JQnx1h8f; arc=none smtp.client-ip=74.125.231.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f43.google.com with SMTP id 46e09a7af769-81b15bca7e0so202283a34.2 for ; Sat, 26 Sep 2026 08:49:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437761; x=1791042561; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ux2E3gKwjBl0LaPoZnc38g0A76GDt8ClA7XYMblBPIo=; b=JQnx1h8fPx6AoUcEDp7Rui3AMw/h33oGPrEbzL2veJvbnHgK/6RBpDI3k2Rvfaahnn 3Jsz2pFE6iZYQE+d/BFMMeV9733amF+qruar5nAx7yHIrPlBgjvGL5cnZltUDvKjwcOt WzODzdYOcNdXwl3QxWV1sl47cvu0EAYoDADO0NKCrN9L0QGp7NYudGs1mgY1W9n/4Pl/ PexBaP7tMA0mu467UKUuaFBr09lsBR3uAScivipcya84i701d674IZMieTOv4qbpRYN/ HFpq8gG58GTprNlUsjQnVRfBQgq1CF8pJoZh7HVX9uQbcCLh5HHuVuSgCKeqnoeA6wlE w6ww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437761; x=1791042561; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=ux2E3gKwjBl0LaPoZnc38g0A76GDt8ClA7XYMblBPIo=; b=tMmd/UoJOyU4/oMvv/EheWzAUuwPpTF6g5dv49W6R3hmxKnDzpjCVm1A2P6B1Q94Uq dv5QMP1AX2DjwRgDmF3iJ3yq2nJOkDu+CCIBLkOGpcrEvyWzuTAthryUIb0p9bNOdiE/ 5yhd53CNx1BkKMyaKler+mEGd7dWCuwupjN1FYpCEotlGagDHYJugggfH7zgUs8PY0Sa iOqAqhmq5biSA+Y3mFQ/Qkr5RTvkLArc2nN0e1N9JwKjg4dwyy9ig9HG0pn7Co8XUm2Z K5Q+prVmV1q1Xg7pyc0maKC1YvD5OeVErPmdEbM+ntvFwVrLCNx77No0A6MgnhQW8DHL 8l2A== X-Forwarded-Encrypted: i=1; AKwUvBxr7/x8lnJPQIY/VkK99MUrYZW0BO8jIrhQMLaScyTbHHDxSqKk/6R2w6CbfLBes06yzru55BMF5Qfblg==@lists.linux.dev X-Gm-Message-State: AFuF++n45WQU2kfjxArO+eyg9UEkZWVOxCBI6VC9ZlhXAX/dqaSoYB9F LVMTVpAJB8TL9mrSlhy8G95QgiwTZzSRjwVdqtBjgZonVFKeZYzUnl0z X-Gm-Gg: AYBFou10IOwtj2z/HuC4q7hKeilwxtQvBethVdFIU0gLz5S+BdlDI2lvLWAyZJvxIoc BvWkONzThOF0xYk51Po4CR49cyTEN3ZeA+7HP8lN17fuoT1lp5pRnUOIs/IYPmd7rdFkr6nWiXT K6DkWROYp5RMxQcYgC9bUvrYEsvzj/eemBQLCWALIZEIhTQ+UZAN1eYTRT0G6FOLeox/nyo8ctV uOGevIuQcG1TvPRs230Y2KWJTxN2C6DKDG6gokHu0eq3C8ngSqFv6mzt2qRnXsgaVuvPStbomp6 M/xlETT4BvZjTEcEF6pppIIUIN+QBHD3TRurIitWQEZ1muj08iQQkcSexhVIvaVHF6oiLkcYl47 77gX9c+7tCtuPxNehP9+4riWsDayDpDTiIj3CEPe1OexxtyP3ZiYbARNsQhjGFalsMlPb3F/Udg zWIqy/iYUwBkbjkPw5QcgqH9ioatMLoAoBxwSRTtj9+7P/8a5HCn5qFeKiNarAE/8aDfew+2cTx F+pKQGHDHm+/WhlWYlrrVBDv3rVbKkisvTAVs7d+36QMfdr+KQ0nzUq1WrXQomJ13IBmsJPol+M EKMa6KrjJ+mDb+DUKXgwwIT9cwAwuElhu3bvFw/XLtnLdyUcMrFa0EbwA2O/fxe7KKGCOc+LrN+ y/dRU+koZkmObTEmBbFGP X-Received: by 2002:a05:6830:43ac:b0:7e9:ea28:4c1f with SMTP id 46e09a7af769-81781790b5emr8931706a34.8.1790437760721; Sat, 26 Sep 2026 08:49:20 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:20 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:48:56 -0600 Subject: [PATCH net v4 01/15] net: stmmac: unwind the WoL IRQ after a safety IRQ request failure Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-1-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= The IRQ setup paths request the MAC IRQ, then the optional WoL IRQ, then the common safety IRQ. If requesting the safety IRQ fails, cleanup must release the WoL and MAC IRQs, but not the failed safety IRQ. The REQ_IRQ_ERR_SFTY case instead frees the safety IRQ and skips the WoL IRQ. Allocation fault injection during live XDP reopening reproduces a "Trying to free already-free IRQ" warning and leaves the WoL handler registered after the datapath resources have been released. A subsequent open can then fail to request that still-owned IRQ. Move safety IRQ cleanup before REQ_IRQ_ERR_SFTY and WoL IRQ cleanup after it. This restores reverse acquisition order for both shared and MSI IRQ setup, including unwind after failures in later per-queue IRQ requests. Fixes: 5c2215167d12 ("net: stmmac: Add driver support for common safety IRQ") Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 3f34d491c959..4f3d452c3503 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3797,13 +3797,13 @@ static void stmmac_free_irq(struct net_device *dev, free_irq(msi->sfty_ce_irq, dev); fallthrough; case REQ_IRQ_ERR_SFTY_CE: - if (priv->wol_irq > 0 && priv->wol_irq != dev->irq) - free_irq(priv->wol_irq, dev); - fallthrough; - case REQ_IRQ_ERR_SFTY: if (priv->sfty_irq > 0 && priv->sfty_irq != dev->irq) free_irq(priv->sfty_irq, dev); fallthrough; + case REQ_IRQ_ERR_SFTY: + if (priv->wol_irq > 0 && priv->wol_irq != dev->irq) + free_irq(priv->wol_irq, dev); + fallthrough; case REQ_IRQ_ERR_WOL: free_irq(dev->irq, dev); fallthrough; From patchwork Sat Sep 26 15:48:57 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3471 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sto.lore.kernel.org (sto.lore.kernel.org [172.232.135.74]) by mxe881.netcup.net (Postfix) with ESMTPS id 162351C5FAD for ; Sat, 26 Sep 2026 17:50:43 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.232.135.74) smtp.mailfrom=linux-sunxi+bounces-26268-noreply=patchwork.local@lists.linux.dev smtp.helo=sto.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.232.135.74 as permitted sender) client-ip=172.232.135.74; envelope-from=linux-sunxi+bounces-26268-noreply=patchwork.local@lists.linux.dev; helo=sto.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sto.lore.kernel.org (Postfix) with ESMTP id D2ED360A0E5 for ; Sat, 26 Sep 2026 15:49:49 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 25F2E36A36C; Sat, 26 Sep 2026 15:49:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Y+NFjYTo" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f36.google.com (mail-oo2-f36.google.com [74.125.231.164]) (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 285C73451C1 for ; Sat, 26 Sep 2026 15:49:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.164 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437774; cv=none; b=j6M0biS0hvIcDZGNihsi55g3KVVz0C+CP2teNW4GgmvoLNjdzxLDVDTjUBEtdqEcTDYVXlY2nxoov8p/eIOHyiiWJzVDvkakisX4tG89rkyGyAnd1mnxEFz97UDeT6fGUnTLeIuIM9J6R30Nb7W2Api+zBlQERJ0kgsU0egyz/g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437774; c=relaxed/simple; bh=U0usEgD6eBVSyAl+4wuE0PhgO7aYIcSIWGg7AGP8BEg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=REFQcx1+ga3y0A27AgnSDYz+a9BLaxECfvQKeUVPpfZ79aJpbd5qUcQT/d6tIrG5jSPw1eKGvvM8/5GFJNM2rRUze415FrbqPHs5x1cyL57oS2IJL+u3kUxmmvpTAMNmoqrfgiiy5ciMjMZL6t7siy08Pq7uoihUmML2oN0WWk0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Y+NFjYTo; arc=none smtp.client-ip=74.125.231.164 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f36.google.com with SMTP id 006d021491bc7-6d77b3455bbso209411eaf.0 for ; Sat, 26 Sep 2026 08:49:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437763; x=1791042563; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=B0ScVIX3sPn/yQi4OeJT/F76Vd8VPP2euaNmJMN8ERw=; b=Y+NFjYToQ8FUXSLrtY6k56IV5OdHwPHMUAAVpAfZsnx1LMY6jpx/SG6k0HDIe1UZpP u/1sKpn90KcbNaiuBSPpSnqvknfDwoOr48FUK8SUEn1GgNkzmyrMKD9SRMbu9B8nKsuM VO+aLbitoIGqciAG25ag8mpux8o4z0LZqDhH/vofwITpUG26mZuE0Xazzusy9PmadeiM HUg7WTyKQNKLj2tetF8RaIYlkJokSp8qvt0HmEIooIjBws7U+bCtNvmVFfMK2K+5DA3D G63IuEHZToTJvmooFRhoQjb+f5Cjr1XggNJnZZnXtj0Dp0RhxjebsdVRd+ruz1TU3mWa IfWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437763; x=1791042563; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=B0ScVIX3sPn/yQi4OeJT/F76Vd8VPP2euaNmJMN8ERw=; b=IYVGZHeKx3SrYkq5M4egBeii3FCIPZrV4X67M0NoEXJ1UOMHisbo4szQMiSaberKjR 3XdwccwAJB2D6O5+jl2DOJ8jtqfQHCra4En9BvN3CJO1Chfsv+Te2sksS9yBCcRuN+9j sHc7jAsshJ0p+gXJjYRFhFYd8sgQMRaaYvFbtO1vp6Pq28tJ7fyKx9nYkhvdF7Fi5JHv MWP0DXBgae0f/taK8chaGB6Upst2gSk1SMb6bfRcEY/u8wo3fWqlTYLRgBo67IztLYT7 ZBSv3MFS9e+WAYLYaNZlUVv9Ztz5+lI7zIFEG+szFvUlE4LkBhJAwj1ph6xePaZN92ER uLKA== X-Forwarded-Encrypted: i=1; AKwUvBxiSkzqhgbNETC8jVrjaeTWqGF+42S5F7kgcsQOr4NEFR5dAnpIJQ4mj9MZ4CHCd6nS+YFqWLf3daN9vw==@lists.linux.dev X-Gm-Message-State: AFuF++kNyRO3eHDqPD4590UjCb7s8NGpRvSB6QtViDqvmWc+bxgG3KxZ LYxPCZ4cC7W54Hyc7L5arUBJWk7sgAzbTvQC9rPvHfvDEhHckuMKnZtz X-Gm-Gg: AYBFou3otFf5t9KcuHjhCkXxFqQy/ltSbaTn6oR7VSKJhkRpS0b4ihfvxbW3gyrfcmX 3+2Q62f9BOA5ugoOfsWKgv33un4nJ7LiKuhC6xbyKrIqIH2ZJ5RYlB0W9S5TzM5ReDkd6LOsbLQ khNQzvfiTEnrRvNHQTZirwu5EBBIjtEFW075fo81Vv4+UjApxqjoW/wbPyllp7tgymV+3CVf+Sh lH1zzY2+rVhzywe1n6kWBTMQGm6EHwxXsXhmRs5gM1xGCbTPeWHh9uYccfpxXfC3JSKDyLi9yvy H/IK+qMIvOXTqDHDO5Lto169ypKxpC4akK0RTxW36+vAHtguZjLT0Run4gU5hTWcgu9FuZrwbnz 2gtH9vF8KFNNksACdilRm2azeOAf7DvaTBpDdQyrFcmtJszLBcjPm549TEq0tp8skIoVaiNnSxj GLSmeITz9be2DnYHelJCwt4hYTBaj0JbKGc/jZoKnEXVyArsW+yKbv7LU4ixEnutQzQFPzAcmaU QcgIeGOYclZ10aO2m1ooLr3a/LN3UDj6LmDxjK3dEx+DRCl9MgLfjJZ8IraWoZ8ikJH830z1XVY bvL4nxRMhh5F2D2eId3CtE4A+TV0mPI4lXTrNzvsL/5qCEVbqCn/DaabtrDe4T96iYVksOlgelX HFjQN9RyslYlrwqRcdFyg X-Received: by 2002:a05:6820:1ca6:b0:6d3:402e:1fca with SMTP id 006d021491bc7-6d43ebf0448mr8754440eaf.9.1790437762887; Sat, 26 Sep 2026 08:49:22 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:22 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:48:57 -0600 Subject: [PATCH net v4 02/15] net: stmmac: reuse the MDIO reset GPIO on resume Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-2-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= MDIO bus registration acquires the optional snps,reset GPIO through devres. A later bus reset must reuse that descriptor: requesting the same exclusive GPIO again returns -EBUSY while devres still owns it. Cache the successfully acquired descriptor for subsequent resets. Propagate acquisition errors without caching an error pointer. Fixes: 7c86f20d15b7 ("net: stmmac: use GPIO descriptors in stmmac_mdio_reset") Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 + drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 7582fca63741..c7c876408d85 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -287,6 +287,7 @@ struct stmmac_priv { unsigned int pause_time; struct mii_bus *mii; + struct gpio_desc *mdio_reset_gpio; struct stmmac_pcs *integrated_pcs; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index afe98ff5bdcb..07d5eab05996 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -386,14 +386,20 @@ int stmmac_mdio_reset(struct mii_bus *bus) #ifdef CONFIG_OF if (priv->device->of_node) { - struct gpio_desc *reset_gpio; + struct gpio_desc *reset_gpio = priv->mdio_reset_gpio; u32 delays[3] = { 0, 0, 0 }; - reset_gpio = devm_gpiod_get_optional(priv->device, - "snps,reset", - GPIOD_OUT_LOW); - if (IS_ERR(reset_gpio)) - return PTR_ERR(reset_gpio); + /* The bus reset also runs on resume. Devres retains the GPIO + * until unbind, so do not request an already owned line again. + */ + if (!reset_gpio) { + reset_gpio = devm_gpiod_get_optional(priv->device, + "snps,reset", + GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); + priv->mdio_reset_gpio = reset_gpio; + } device_property_read_u32_array(priv->device, "snps,reset-delays-us", From patchwork Sat Sep 26 15:48:58 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3476 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 1D4111C2AA6 for ; Sat, 26 Sep 2026 17:53:20 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26267-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26267-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id BE3303A8D1 for ; Sat, 26 Sep 2026 15:49:34 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 82707371D10; Sat, 26 Sep 2026 15:49:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="eJSnoELF" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f43.google.com (mail-oo2-f43.google.com [74.125.231.171]) (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 38D8C36B915 for ; Sat, 26 Sep 2026 15:49:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437768; cv=none; b=grnlNlIFuxrjFqOLH04S/7kH7NP6X8D47jQpGVvwI66+wDRAAJnZxBCyP+/hj10HpbQLeiTejRBixSi9DMT3gNxiAPzn7h9JeRuBmXVUFgvKUpjRigQs63rDNurqZM0AAB8leXOPLY5rQq//xebMlsG81GC2fY9+WZRoPuonC1k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437768; c=relaxed/simple; bh=M5M3PgGTSsBzpkQ0euUZJ5B4ccey2Neh1h31IhOxfnM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=XkXT0w4glC1AaBiXGmZ0NFtSUq4zCKKFlqAmoHvsd90QWyz62UooE0NhvkLuM9euN3CdtVeQol+AoABD7T6v5EXXwWlDAUTEotI+6ADql1jhS/1Z6YmuzORf8X3Lr4+hepZ/55Lub9jagUDjGl/bNouQLOHelIxK4rSGx8+qnzo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=eJSnoELF; arc=none smtp.client-ip=74.125.231.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f43.google.com with SMTP id 46e09a7af769-81b485c8368so89094a34.3 for ; Sat, 26 Sep 2026 08:49:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437765; x=1791042565; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Z7kcdEW7zqHkGJYzryRmaM6C2T9h9+G3wdmEwpizeis=; b=eJSnoELFmkPIbp9UqdIK6GXUZvGSfy4Attu0W8mWlN2RsVGqWTD8+uRbCoUSfD+ByW iuSPMf1tEoJkfxCpk/cZfL3eXVjm1mofTc6R5GjClsr7XrLP9bF//bEB35gsp3Hok8XV IOv3Jfe6Ohq91Cpm4W4NJEE+p6zdKkVKYWj2xeC7SoY89J0oHehY8SYdX/uSEI9/VH08 NIv3T4gWyou1RLfBt0+kNzlLTNF20tYwLkHtCbgZQk8RMgI5AKaOyxHBuTPSi1RrizVt j2ldr751RrC5tHD/TeAxBCFW05jv7ZgweKBBTeoPvgiQD0IuQXYySukiec067lBopD1w ktHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437765; x=1791042565; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Z7kcdEW7zqHkGJYzryRmaM6C2T9h9+G3wdmEwpizeis=; b=E8Nn1EMHYXG44fymTIGhcnaZUPNogy+iqsQjbx+nSlVuQRw4aIt7i7xWYZgPTXkum8 CWWKRZyXkZF28VJrSubfh2quZ+W2cZqPEewRDEV2whnazkQWBB5J7u+PLKySWTeY+bQR oKwh3+WQj6bM2HnsQFXkd2vhpXXcrAv/9/FOef4vhIptDKbr60okpAYxvnstjouposAE +h87bt/XaqDFc98jzbpCSpoh25IeCQRKTRGVWVQQYxUITpcFyRQcYJu2Z7QT32zzkLNd GBF1Q9tzPVJx7yBIaJi5r8sxJWckdSwyoIWCMErH1w3fRuXzkTzIC7gJtOSgwISW3NSJ SU/w== X-Forwarded-Encrypted: i=1; AKwUvBy13e5YWr4m7hy03hTj1+HIFGP4s3PMyWabcMz5OLqtgwi4P2284x5wzOi45xDLNeT5b+Pyaiy3ZTc/Ug==@lists.linux.dev X-Gm-Message-State: AFuF++lYnZzWbhTCpVBtlhWaCtgATmAQ8DW7YMoix6+0r6KxhPF7XMo/ vPDGLOB3RVGeyl7JFM4uKUxuy7faJqaUZy564wZT4Y2YGRwbl9voe6oC X-Gm-Gg: AYBFou3S0I2i48ZeUd3F64YkbzxRFy/SIafxCtyngZN4ugrJGaQh9uqClTjE2wsSjrH 5Eb3fRxGFtGLKuT/RMWX9AmKokbFkRr1u5U3Mvek9EhsVh6/cO6nUDdZX4lbgdqJXvJ2C8pQ3f1 DQPyCAOnj8Zoyhlv4xAD2V8jJjRiJX+v5+qrDruwx91lXa8tWf6rB3YFWV/BD1N0Ks1jddLSrD2 e0W4O568yR59r5cvMhqXQP241WASfBghf+X3Ao/t/7mp59V/mW/zwOuaxa1RkKk9juhWlHrBIfv xs2vCTDysCkwSmxBLra7vSXxKfw1Tgfu1Yxx92c59jJtzTjULjKhsGL7JbwRQRMDSRfsk4/uHkO lZEqCSDUP2wcS0HrJAxjWOeeakr4rn+WCgupTjW7/M2vRm5BZEqe+M2TAMlPmu+NSqMHfP1vBmL 4uG0oxtcUq/+3CkXl4dyK/VhBx6hORnjHDfbBJ67xmxFg0oXsXEQZFc/QnUAyp4xts9na3PHesZ w3Q4ZidJmck3LmdY/DvVge/fhzWTdtGGwBcZmGK9faOi75u9hV70wV0SbQSnEX2NvWvDqZr03h5 5y1N3vjCNrI/EoMDZA+oxmbtXog1xqFeyUVUjelbrpR/Pqalvr5w/YnWWn97+3IMgg51YYdz40t jqPhd2tBNwTHn0ZTKytRFWN1PSOv60kQ= X-Received: by 2002:a05:6830:a203:20b0:81b:8032:716d with SMTP id 46e09a7af769-81b8032721cmr86559a34.20.1790437765027; Sat, 26 Sep 2026 08:49:25 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:24 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:48:58 -0600 Subject: [PATCH net v4 03/15] net: phylink: allow stopping a suspended instance Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-3-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [-1.16 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[172.234.253.10:from]; SUSPICIOUS_RECIPS(1.50)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; MAILLIST(-0.15)[generic]; MIME_GOOD(-0.10)[text/plain]; BAD_REP_POLICIES(0.10)[]; HAS_LIST_UNSUB(-0.01)[]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; DBL_BLOCKED_OPENRESOLVER(0.00)[sea.lore.kernel.org:rdns,sea.lore.kernel.org:helo]; FROM_HAS_DN(0.00)[]; PRECEDENCE_BULK(0.00)[]; TAGGED_RCPT(0.00)[kernel,netdev]; FORGED_SENDER_MAILLIST(0.00)[]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; FREEMAIL_CC(0.00)[bootlin.com,d-silva.org,kernel.org,vger.kernel.org,st-md-mailman.stormreply.com,lists.infradead.org,uniontech.com,oss.qualcomm.com,163.com,gmail.com,lists.linux.dev]; RCVD_VIA_SMTP_AUTH(0.00)[]; FROM_NEQ_ENVFROM(0.00)[jameshilliard1@gmail.com,linux-sunxi@lists.linux.dev]; ASN(0.00)[asn:63949, ipnet:172.234.224.0/19, country:SG]; R_SPF_ALLOW(0.00)[+ip4:172.234.253.10]; RCPT_COUNT_GT_50(0.00)[54]; FREEMAIL_FROM(0.00)[gmail.com]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[74.125.231.171:received,100.90.174.1:received]; TO_DN_SOME(0.00)[]; RCVD_COUNT_FIVE(0.00)[6]; FREEMAIL_TO(0.00)[armlinux.org.uk,lunn.ch,gmail.com,davemloft.net,kernel.org,redhat.com,nxp.com,bootlin.com,foss.st.com,loongson.cn,iogearbox.net,fomichev.me,quicinc.com,synopsys.com,intel.com,googlemail.com,nvidia.com,sholland.org]; MIME_TRACE(0.00)[0:+]; TAGGED_FROM(0.00)[bounces-26267-noreply=patchwork.local]; RECEIVED_SPAMHAUS_PBL(0.00)[174.29.1.49:received]; MID_RHS_MATCH_FROM(0.00)[]; RCVD_TLS_LAST(0.00)[]; RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[172.234.253.10:from] X-Rspamd-Queue-Id: 1D4111C2AA6 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= If a network driver's system resume fails before phylink_resume(), the network device can remain administratively up with phylink suspended. Closing the interface then needs to terminate that suspended instance. Calling phylink_resume() merely to make phylink_stop() work is not a safe substitute: resume reconfigures the MAC and restarts link resolution, although the driver has not successfully restored the MAC. This is a missing suspend-to-stop transition, independent of the reason hardware restoration failed. No MAC recovery policy belongs in phylink; the driver still decides whether to retry resume or wait for an ordinary administrative down/up cycle. Without MAC Wake-on-LAN, phylink_suspend() has already called phylink_stop(). Do not repeat PHY, SFP and PCS shutdown. However, phylink_prepare_resume() may have powered that stopped PHY back up to provide the receive clock for MAC reset. Suspend it again if WoL permits, without repeating phy_stop() on a PHY which is already halted. With MAC Wake-on-LAN, suspend deliberately defers mac_link_down() and sets PHYLINK_DISABLE_MAC_WOL. Finish that deferred link-down, drain resolution work and clear the WoL disable bit while retaining PHYLINK_DISABLE_STOPPED. Otherwise a subsequent start cannot resolve the link. Also undo PHY speed control performed by phylink_suspend(). As Andrew Lunn pointed out, phylink_start() does not restore the advertised speeds that phylink_resume() normally restores. Track suspend-owned speed control and restore the saved advertisement from either resume or suspended stop, including when PHY shutdown has already completed. An explicit driver speed-down request, such as stmmac's close-time power saving, must remain in effect until its matching speed-up. Restore any suspend-owned reduction before applying that request, so it cannot save the reduced advertisement over the original one. The following stop must not undo the driver's new reduction. Document that a suspended instance can be stopped directly. This neither resumes the PHY nor reconfigures or brings up the MAC. Fixes: f97493657c63 ("net: phylink: add suspend/resume support") Signed-off-by: James Hilliard --- drivers/net/phy/phylink.c | 51 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 1bbcf46c8356..b7d30ca34031 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -78,6 +78,7 @@ struct phylink { bool link_failed; bool suspend_link_up; + bool suspend_speed_down; bool force_major_config; bool major_config_failed; bool mac_supports_eee_ops; @@ -2498,6 +2499,14 @@ void phylink_start(struct phylink *pl) } EXPORT_SYMBOL_GPL(phylink_start); +static void phylink_restore_suspend_speed(struct phylink *pl) +{ + if (pl->suspend_speed_down) { + phylink_speed_up(pl); + pl->suspend_speed_down = false; + } +} + /** * phylink_stop() - stop a phylink instance * @pl: a pointer to a &struct phylink returned from phylink_create() @@ -2509,11 +2518,30 @@ EXPORT_SYMBOL_GPL(phylink_start); * * This will synchronously bring down the link if the link is not already * down (in other words, it will trigger a mac_link_down() method call.) + * A suspended instance may be stopped without first calling phylink_resume(). + * In particular, closing a device after a failed resume must not restart the + * link or reconfigure the MAC just to finish shutting it down. + * Any PHY advertisement reduced by phylink_suspend() is restored as part + * of this transition. + * If phylink_prepare_resume() powered up an already stopped PHY, suspend + * it again when Wake-on-LAN permits. */ void phylink_stop(struct phylink *pl) { ASSERT_RTNL(); + /* Also undo PHY speed control when terminating a suspended instance. */ + phylink_restore_suspend_speed(pl); + + if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { + /* A failed MAC resume may have called phylink_prepare_resume() + * and powered the stopped PHY back up to supply its RX clock. + */ + if (pl->phydev) + phy_suspend(pl->phydev); + return; + } + if (pl->sfp_bus) sfp_upstream_stop(pl->sfp_bus); if (pl->phydev) @@ -2526,6 +2554,16 @@ void phylink_stop(struct phylink *pl) phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED); + if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) { + /* Finish the link-down deferred by MAC WoL, without restarting. */ + flush_work(&pl->resolve); + mutex_lock(&pl->state_mutex); + if (pl->suspend_link_up) + phylink_link_down(pl); + __clear_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state); + mutex_unlock(&pl->state_mutex); + } + pl->pcs_state = PCS_STATE_DOWN; phylink_pcs_disable(pl->pcs); @@ -2657,8 +2695,10 @@ void phylink_suspend(struct phylink *pl, bool mac_wol) phylink_stop(pl); } - if (phylink_phy_pm_speed_ctrl(pl)) + if (phylink_phy_pm_speed_ctrl(pl)) { phylink_speed_down(pl, false); + pl->suspend_speed_down = true; + } } EXPORT_SYMBOL_GPL(phylink_suspend); @@ -2698,8 +2738,7 @@ void phylink_resume(struct phylink *pl) { ASSERT_RTNL(); - if (phylink_phy_pm_speed_ctrl(pl)) - phylink_speed_up(pl); + phylink_restore_suspend_speed(pl); if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) { /* Wake-on-Lan enabled, MAC handling */ @@ -3616,6 +3655,12 @@ int phylink_speed_down(struct phylink *pl, bool sync) ASSERT_RTNL(); + /* An explicit request takes over from suspend-time speed control. + * Restore the original advertisement before saving it again, so a + * repeated speed-down cannot replace it with the reduced advertisement. + */ + phylink_restore_suspend_speed(pl); + if (!pl->sfp_bus && pl->phydev) ret = phy_speed_down(pl->phydev, sync); From patchwork Sat Sep 26 15:48:59 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3477 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id CCB5C1C2AA6 for ; Sat, 26 Sep 2026 17:55:15 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26269-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26269-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id F35EC327CB for ; Sat, 26 Sep 2026 15:49:49 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 29C2637B016; Sat, 26 Sep 2026 15:49:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="B611xEvn" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 177423783C8 for ; Sat, 26 Sep 2026 15:49:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437774; cv=none; b=Lsa8ZGI8DsShY8jfoeQL9rIH6TlHIh5f8PsMMtIOatonB0DsC4EEB98IhH90HqKcPTR3+cdfp6YaEcsc1Y3PHOTbxrWVFy7m2ErLlKUDrdaY+xaVgp7v0MAt8PUb+PZaE9UTVhIXXI33tu7OVdFQFYQRlL5N0WisY1vXKlgQnN8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437774; c=relaxed/simple; bh=2ChPtfS7fuQ6xmL5pBM0ED+Tz6MNWUwwNmCfWCfm6zM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=uT+JvImz+d3c+3qTrwMm5m4S5gmPLLlzW5JH8KqBKWgCDSsB1NjwYtw6Tktlr7hmOYVoYhDwRmktCfssCkW5zRd7ij1dw704TQHwEnaOwlUTGYaZc11vstvCTbZlTBxw4FbRoW1a6f+hrmF9PjIsyfujthIJboqwfXyqJAbxQ/M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=B611xEvn; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f42.google.com with SMTP id 46e09a7af769-81b8e8e9cb0so10065a34.3 for ; Sat, 26 Sep 2026 08:49:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437768; x=1791042568; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=pt1JZ0Xx4dSQpjlcwcv4OrOAetyhY0kdTpc42Knguac=; b=B611xEvndTs7NOgglix1ToB19FYuNlwEPqCvzyGATZN3mbXeLw+eXt26rLVNAC2X1t JGKF4tJsfKaF2k8UT3kos66iw4fIuZgdhdpAo4e/LJ9chKjIpMJ+RkfjCWe+tZqF6ZDe a30L3RcaYhnY0xXii6wTD2Tstox6nsEC8jnLL7OwDdDQ5q/JI+QGknx3hCgqF111izB2 Y53B6iGf5kX1y+1r2p6bT00ljfSMEDzdvZy7syCXetVEO0YRc0vyrKo2f1+ZwLcxKd/u bNTmdrRau4LqWAGn9AwhVSzAcPIUx5HBq4P83vnWgYGuyyPr3o9kyi6ZbZudgyC1YQLS 68Ig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437768; x=1791042568; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=pt1JZ0Xx4dSQpjlcwcv4OrOAetyhY0kdTpc42Knguac=; b=NqAnPg6yKSYcSHemncEsOJ6OMpKJCtsW6c4eiDrOGMfenCWwrkWwjYFfH3BLMOX8xP yBUY5/XfLUQ4hHwgPa5ahm/BTFuQNb2NTw3A0iBCMkcuju8IU355rxwDXOkerocGi/2E /Qi1l2hhdOjReV7/Cq+qIRFG3agWV7dbmdKlKHnoC/tnoWFn2l8DaKHF/mymJ8wkYw2A 5XxpvB8c5VD8IiQ9q0BKorrdEtRkA5tp1gYCWzNZKNxtlmEn8gdvIGfUBF5dUaKUVExY DQs3m1bT7o0BIqg0zkpU3WfM0LFlAqKC7yGuarhEZn5Fh2YsYMhvb66alXA9b9lWUtqd doMA== X-Forwarded-Encrypted: i=1; AKwUvBxaF6vumedhLYLsdsZDqBOofyA5NAwAPQT10ipnnVuL2HN4DQAv7ZzK9FJblyY4cA4CddQNVyibQgyhbQ==@lists.linux.dev X-Gm-Message-State: AFuF++mUXjuvv/HNlPP/fDeO3fZmHw3AwaQ2tAm0+rRXehk4eNa+cLIC yHpzGqKUxeE4IX+J+QT6LwPI+mUub0h4jJgf66pNNSzcaOU+jyB5N70b X-Gm-Gg: AYBFou1Zs2TGRGzakTiyHqNRygJOmI9WrOlWuyS3S3GwjDs6tyLzyORSSS7XGBQr8BE 3lyc0HNZi6TwFc8JlsTWjx2/cSfCKwVvkl2KFs5B8XrtzNXXjbY+UUAluih94QnVCkkA/bxT7BR fxblZR89wB0CiYQxUty3QdKpb+Il/yUX33/AU/6/U5Zsy4VStYKDI5p3ikefe05z1dMeJQvpIwP 0+4hL/Sd8n8akePx3EziGhldcoCEHfS4GBew+q104MWhhGeeQd+LU02s08fUGu3fx18KQguGqEN Vmjj0TBp1+t+gbNabG+RpQSDGRqMFRj54/fDchyZ+X4Cj/ekXFRHuInHd2ypvZ0PmgNXBGP8UYI i7iMxgTRQZfdpGEz9f9k/BUcdLxJh2UJDqbMkPghu9EJw53WggSNVDXvitypAxV2v6iqOu1RFcS uP5ULzfNdSz8xaXGxZhrEZZf3ccSzHoKcGyVrgpzmrZTArP4Vb9plTb92VmYdPDdPaa+7zrQz0s uJ9igTFGaVqCV9SdOjhZhVAq2k2hQvxiFrsvPszltcQzxcNOv9/OB7fGtCVmYjD45b5HRsFt3KT 07wIyzZJdfXqBFHDKkExjQV2DUej17G0rfZBIPxtqbjlIHO+5NM1i6S0R4zzxF07FhIE1OuSDrl xLJXdZ+V+jHP81AbrmzlX X-Received: by 2002:a05:6830:6734:b0:7dc:4a43:fb5c with SMTP id 46e09a7af769-81781294540mr9561085a34.11.1790437767719; Sat, 26 Sep 2026 08:49:27 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:26 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:48:59 -0600 Subject: [PATCH net v4 04/15] xsk: freeze deferred pool teardown during system sleep Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-4-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Pool destruction calls the driver under RTNL from system_wq. That queue is not frozen during system sleep, so ndo_bpf() can run after the device suspend callback has gated its clocks or after the noirq phase. Use the freezable workqueue for deferred pool release. Work already running completes before device suspend, and newly queued destruction waits until device resume. The pool, UMEM and netdev references remain owned by the work until then. This does not replace driver error handling after a failed resume. Fixes: 1c1efc2af158 ("xsk: Create and free buffer pool independently from umem") Signed-off-by: James Hilliard --- net/xdp/xsk_buff_pool.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index 9d2d94f1fb75..c58f56f24a9c 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -337,7 +337,10 @@ bool xp_put_pool(struct xsk_buff_pool *pool) if (refcount_dec_and_test(&pool->users)) { INIT_WORK(&pool->work, xp_release_deferred); - schedule_work(&pool->work); + /* Teardown calls ndo_bpf(), which may need powered hardware. + * RTNL alone does not exclude the device's system PM callbacks. + */ + queue_work(system_freezable_wq, &pool->work); return true; } From patchwork Sat Sep 26 15:49:00 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3472 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sto.lore.kernel.org (sto.lore.kernel.org [172.232.135.74]) by mxe881.netcup.net (Postfix) with ESMTPS id D80B71C1C7A for ; Sat, 26 Sep 2026 17:51:01 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.232.135.74) smtp.mailfrom=linux-sunxi+bounces-26270-noreply=patchwork.local@lists.linux.dev smtp.helo=sto.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.232.135.74 as permitted sender) client-ip=172.232.135.74; envelope-from=linux-sunxi+bounces-26270-noreply=patchwork.local@lists.linux.dev; helo=sto.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sto.lore.kernel.org (Postfix) with ESMTP id 0C5F460A5CE for ; Sat, 26 Sep 2026 15:49:59 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 940EA37B41F; Sat, 26 Sep 2026 15:49:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GmYINfBn" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f43.google.com (mail-oo2-f43.google.com [74.125.231.171]) (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 298553655E0 for ; Sat, 26 Sep 2026 15:49:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437775; cv=none; b=fCidYHJJfNXkFhyyzkt/etHXufRaKTamLZozwB/Yefy7aO/HlnDPWoXo9KE8a7HyaLxqtO6mrqPY78evjXeoi5LELM8lTMTob1cZKpvZHXgp0bWzEMuIQQzDoZYh6kTApex402HHInV4Qrth/2ZPlANFJcxx5U3CZ09nlDqWHg0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437775; c=relaxed/simple; bh=QLLzCqU0wCcXmeIHIV8TgQgW9BBj0BKW1s7r4z+lcPQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=sBPfnDyxmI70pAPchF11iVK+6QCmD+G1YJxceBW42ZDyjaqxy6486ks3Wk9LPQ4pQ2dr2pPPmb8RuAikkKhWo4qrvTVZkZlRsHPb00T8kC0ZMgH9t37YZDzaDsNMCpZWMKOY+Saf9YUNunrLbt9y6bUcFm78yGEOHaVCpnG5X2w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GmYINfBn; arc=none smtp.client-ip=74.125.231.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f43.google.com with SMTP id 46e09a7af769-81b36a8d5b5so120063a34.3 for ; Sat, 26 Sep 2026 08:49:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437770; x=1791042570; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=pXuc4JFXb8UPUC0fNxWljunUCLVVXc20qC1Nh0LbmgY=; b=GmYINfBn0g84AsfqE1hnQNDM+ziUNDMBpYSPJE2BlDr6JvtRvpaBShZB0LMP2M3dLi 9T0zNEAUNABwcZUHOI4VrgKHkF1LG5gHrz69zFKrRs9jzyyYp74k6zALdqc6GOUfBn3h ApP8ZG7fWVnf0BjxwiavnwzauZzyqSokc228V/aAV9DV4Ht08brjY8chr0/AP1m1cAqX PBRon7F9PsYWsTYt48uXxbslWGogD72UhPas2P9pY1LG4XUIYJo/FD/QSTaImXQ0PXIE ET725C2oDe5cDzTRrRNZuqCApqYZTCLecXWbdpT/5Y0yycSMU6njy16IwPugnUcDPw2B 01xg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437770; x=1791042570; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=pXuc4JFXb8UPUC0fNxWljunUCLVVXc20qC1Nh0LbmgY=; b=nAtjwpQ+qKhILMu6ilFpq90ti7+18UgGQ71WdkrtFkSM3wXTDyhuzMt6Bldsbsqoio 8htR9N0HLjIXHEl3faxS6L5D+unm6KtdTEY+cNCKoE869a8WHxrhYZEH/tCVZAMxIyNL FwkWZ6WkxIw2dq5MtIV5oAlsVYvMf+fBZxsjhT+5EkmXIPL5yDFeUkWqUNntS8tEr2yZ rm4VZZCmm9rODDGaogbISJfLwDjeP2qPB5WDirrfpSIDZ7ZtyW/HqiEAZqyXUPEDpaWV 4nl/o0Zj0bQrxmvlg+L98Ss0/2SuhPFH5OyqrRZPtUmozqbBVilrgfACaO65JzHJRuPj +15A== X-Forwarded-Encrypted: i=1; AKwUvBzrojIyT4lpHsPjKmK5V83OxS4YZK48Ukg9lt0yE550tEcbolTSj/RNFJLRtH9Zh+xf2SQ6QztDjkaOrg==@lists.linux.dev X-Gm-Message-State: AFuF++lRXdIVsJc1cAHEYdRs5z/DVySVRPsHlxfqPReAwXpfuwwtT6GC moYtadfykZI2frGOQWFtXHs/B/DXlnnlAYg9Xj2b9p72ZupwZByCQXgg X-Gm-Gg: AYBFou3zpuD9qinf0tUGtTqaN4fdvEDyIv+j5VFaD6dylDm0PlhlJkz94V4TmUefSMA uCikE9yCcSivq0XP09JfMxcU2nDklXR4l3qMaEr1lzGofi1eDWjR5plUOJswTbZFQ3ZZ+Xjf/Hk 4NhtEVNqNYY0X50+Vi+3neo2Zr4FiE+wx0yGjKkvf+C3n2zzfl4ExJAejz/keYxH0KqwTSZvU7j szIWeOf/uACaW4QzrZAgaWXrpb9vIkLxWv6lrBJOnjVnR3EuzKBgTLcIVRhnMD2Ev7Sh85KO5+k jl7Vzpmv1/hx7PQ5vzmE1XIEJEGAlg0L5yjhxBkto9y7ZqYwLG81M/XaXECGXBLSoR1l9q87lFb FW+QB32fu0DfwIbn475Lui3xsuE9KsMxxSgEPRo1o3djpXQ/0YW8/z8pwi4+43n/npNzsut55Yw CzOfWZAjCn7SIJa5jf8zRJ59xHpapQL+XDXb7Y2+kaZc511Op80OE/0v9FN9mEw0hcVBy+5aJOH 4hlGnMCTIF0r0FrnXs0f3N2pPW6UFEwOaTxNBTH1xWOWFbDdnRjSZAKTuHbgU2Z7nR/uThPGMI6 gu3YOrmgcaN+A6Be2GozA0nzuwzC+pDNIQwgaAgIoMIvgUG+4cCeNB5aKQ8QZrNqAn1fIPlOZyR Ia/EQimSH4nEp8K5O3uE0 X-Received: by 2002:a05:6830:334c:b0:819:60f4:56cd with SMTP id 46e09a7af769-81960f4adefmr3991885a34.6.1790437769815; Sat, 26 Sep 2026 08:49:29 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:29 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:49:00 -0600 Subject: [PATCH net v4 05/15] net: stmmac: serialize and retain PHC configuration across reset Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-5-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Keep the frequency correction, PEROUT requests and EXTTS selection independently of timestamp register contents. Cache PPS requests before converting units so they can be replayed after reset. Serialize timestamp writers and devlink timestamp-mode updates with a mutex, and let atomic gettime callers observe a reset gate under ptp_lock. Provide a common replay helper for later reset transactions, without changing PHC registration lifetime. Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 7 ++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 19 +++- drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c | 131 +++++++++++++++++++--- 3 files changed, 141 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index c7c876408d85..aa09b2defcd9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -337,6 +337,12 @@ struct stmmac_priv { int use_riwt; int irq_wake; rwlock_t ptp_lock; + /* Serialize PHC changes with a hardware reset; gettime uses ptp_lock. */ + struct mutex ptp_mutex; + bool ptp_blocked; + long ptp_scaled_ppm; + u32 ptp_perout; + u32 ptp_extts; /* Protects auxiliary snapshot registers from concurrent access. */ struct mutex aux_ts_lock; wait_queue_head_t tstamp_busy_wait; @@ -404,6 +410,7 @@ void stmmac_set_ethtool_ops(struct net_device *netdev); void stmmac_ptp_register(struct stmmac_priv *priv); void stmmac_ptp_unregister(struct stmmac_priv *priv); +int stmmac_ptp_restore(struct stmmac_priv *priv); int stmmac_xdp_open(struct net_device *dev); void stmmac_xdp_release(struct net_device *dev); int stmmac_get_phy_intf_sel(phy_interface_t interface); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 4f3d452c3503..8b0bc7c46df9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -941,6 +941,11 @@ static void stmmac_setup_ptp(struct stmmac_priv *priv) { int ret; + priv->ptp_scaled_ppm = 0; + priv->ptp_perout = 0; + priv->ptp_extts = 0; + priv->ptp_blocked = false; + ret = clk_prepare_enable(priv->plat->clk_ptp_ref); if (ret < 0) netdev_warn(priv->dev, @@ -6285,7 +6290,8 @@ static void stmmac_common_interrupt(struct stmmac_priv *priv) for (queue = 0; queue < queues_count; queue++) stmmac_host_mtl_irq_status(priv, priv->hw, queue); - stmmac_timestamp_interrupt(priv, priv); + if (!READ_ONCE(priv->ptp_blocked)) + stmmac_timestamp_interrupt(priv, priv); } } @@ -7696,6 +7702,14 @@ static int stmmac_dl_ts_coarse_set(struct devlink *dl, u32 id, { struct stmmac_devlink_priv *dl_priv = devlink_priv(dl); struct stmmac_priv *priv = dl_priv->stmmac_priv; + unsigned long flags; + + mutex_lock(&priv->ptp_mutex); + if (priv->ptp_blocked) { + mutex_unlock(&priv->ptp_mutex); + return -EBUSY; + } + write_lock_irqsave(&priv->ptp_lock, flags); priv->tsfupdt_coarse = ctx->val.vbool; @@ -7708,6 +7722,8 @@ static int stmmac_dl_ts_coarse_set(struct devlink *dl, u32 id, * reconfigure the systime, subsecond increment and addend. */ stmmac_update_subsecond_increment(priv); + write_unlock_irqrestore(&priv->ptp_lock, flags); + mutex_unlock(&priv->ptp_mutex); return 0; } @@ -8048,6 +8064,7 @@ static int __stmmac_dvr_probe(struct device *device, mutex_init(&priv->lock); rwlock_init(&priv->ptp_lock); + mutex_init(&priv->ptp_mutex); stmmac_fpe_init(priv); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c index 3bfcc9760dce..bc6fe0c87e01 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c @@ -12,6 +12,16 @@ #define PTP_SAFE_TIME_OFFSET_NS 500000 +static int stmmac_ptp_begin(struct stmmac_priv *priv) +{ + mutex_lock(&priv->ptp_mutex); + if (priv->ptp_blocked) { + mutex_unlock(&priv->ptp_mutex); + return -EBUSY; + } + return 0; +} + /** * stmmac_adjust_freq * @@ -28,14 +38,21 @@ static int stmmac_adjust_freq(struct ptp_clock_info *ptp, long scaled_ppm) container_of(ptp, struct stmmac_priv, ptp_clock_ops); unsigned long flags; u32 addend; + int ret; - addend = adjust_by_scaled_ppm(priv->default_addend, scaled_ppm); + ret = stmmac_ptp_begin(priv); + if (ret) + return ret; write_lock_irqsave(&priv->ptp_lock, flags); - stmmac_config_addend(priv, priv->ptpaddr, addend); + addend = adjust_by_scaled_ppm(priv->default_addend, scaled_ppm); + ret = stmmac_config_addend(priv, priv->ptpaddr, addend); + if (!ret) + priv->ptp_scaled_ppm = scaled_ppm; write_unlock_irqrestore(&priv->ptp_lock, flags); + mutex_unlock(&priv->ptp_mutex); - return 0; + return ret; } /** @@ -57,6 +74,10 @@ static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta) bool xmac, est_rst = false; int ret; + ret = stmmac_ptp_begin(priv); + if (ret) + return ret; + xmac = dwmac_is_xmac(priv->plat->core_type); if (delta < 0) { @@ -110,6 +131,7 @@ static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta) netdev_err(priv->dev, "failed to configure EST\n"); } + mutex_unlock(&priv->ptp_mutex); return 0; } @@ -128,14 +150,18 @@ static int stmmac_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts) container_of(ptp, struct stmmac_priv, ptp_clock_ops); unsigned long flags; u64 ns = 0; + int ret = 0; read_lock_irqsave(&priv->ptp_lock, flags); - stmmac_get_systime(priv, priv->ptpaddr, &ns); + if (priv->ptp_blocked) + ret = -EBUSY; + else + stmmac_get_systime(priv, priv->ptpaddr, &ns); read_unlock_irqrestore(&priv->ptp_lock, flags); *ts = ns_to_timespec64(ns); - return 0; + return ret; } /** @@ -153,25 +179,34 @@ static int stmmac_set_time(struct ptp_clock_info *ptp, struct stmmac_priv *priv = container_of(ptp, struct stmmac_priv, ptp_clock_ops); unsigned long flags; + int ret; + + ret = stmmac_ptp_begin(priv); + if (ret) + return ret; write_lock_irqsave(&priv->ptp_lock, flags); - stmmac_init_systime(priv, priv->ptpaddr, ts->tv_sec, ts->tv_nsec); + ret = stmmac_init_systime(priv, priv->ptpaddr, ts->tv_sec, ts->tv_nsec); write_unlock_irqrestore(&priv->ptp_lock, flags); + mutex_unlock(&priv->ptp_mutex); - return 0; + return ret; } -static int stmmac_enable(struct ptp_clock_info *ptp, - struct ptp_clock_request *rq, int on) +static int __stmmac_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) { struct stmmac_priv *priv = container_of(ptp, struct stmmac_priv, ptp_clock_ops); void __iomem *ptpaddr = priv->ptpaddr; - struct stmmac_pps_cfg *cfg; + struct stmmac_pps_cfg pps, saved, *cfg = &pps; int ret = -EOPNOTSUPP; unsigned long flags; u32 acr_value; + if (priv->plat->core_type == DWMAC_CORE_GMAC) + return dwmac1000_ptp_enable(ptp, rq, on); + switch (rq->type) { case PTP_CLK_REQ_PEROUT: { struct timespec64 curr_time; @@ -182,8 +217,6 @@ static int stmmac_enable(struct ptp_clock_info *ptp, if (rq->perout.flags) return -EOPNOTSUPP; - cfg = &priv->pps[rq->perout.index]; - cfg->start.tv_sec = rq->perout.start.sec; cfg->start.tv_nsec = rq->perout.start.nsec; @@ -213,6 +246,7 @@ static int stmmac_enable(struct ptp_clock_info *ptp, cfg->period.tv_sec = rq->perout.period.sec; cfg->period.tv_nsec = rq->perout.period.nsec; + saved = *cfg; write_lock_irqsave(&priv->ptp_lock, flags); ret = stmmac_flex_pps_config(priv, priv->ioaddr, @@ -220,6 +254,9 @@ static int stmmac_enable(struct ptp_clock_info *ptp, priv->sub_second_inc, priv->systime_flags); write_unlock_irqrestore(&priv->ptp_lock, flags); + /* Some cores convert cfg->start to binary rollover units. */ + if (!ret) + priv->pps[rq->perout.index] = saved; break; } case PTP_CLK_REQ_EXTTS: { @@ -265,6 +302,64 @@ static int stmmac_enable(struct ptp_clock_info *ptp, return ret; } +static int stmmac_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct stmmac_priv *priv = + container_of(ptp, struct stmmac_priv, ptp_clock_ops); + int ret; + + ret = stmmac_ptp_begin(priv); + if (ret) + return ret; + ret = __stmmac_enable(ptp, rq, on); + if (!ret) { + if (rq->type == PTP_CLK_REQ_PEROUT) { + if (on) + priv->ptp_perout |= BIT(rq->perout.index); + else + priv->ptp_perout &= ~BIT(rq->perout.index); + } else if (rq->type == PTP_CLK_REQ_EXTTS) { + priv->ptp_extts = on ? BIT(rq->extts.index) : 0; + } + } + mutex_unlock(&priv->ptp_mutex); + return ret; +} + +/* Called with ptp_mutex held and PHC access blocked across the MAC reset. */ +int stmmac_ptp_restore(struct stmmac_priv *priv) +{ + struct ptp_clock_request rq = { .type = PTP_CLK_REQ_EXTTS }; + unsigned long flags; + u64 ns = 0, period; + u32 addend; + int i, ret; + + write_lock_irqsave(&priv->ptp_lock, flags); + addend = adjust_by_scaled_ppm(priv->default_addend, priv->ptp_scaled_ppm); + ret = stmmac_config_addend(priv, priv->ptpaddr, addend); + for (i = 0; !ret && i < STMMAC_PPS_MAX; i++) { + struct stmmac_pps_cfg cfg = priv->pps[i]; + + if (!(priv->ptp_perout & BIT(i))) + continue; + stmmac_get_systime(priv, priv->ptpaddr, &ns); + period = timespec64_to_ns(&cfg.period); + /* Retain phase, but move an expired target into the future. */ + cfg.start = stmmac_calc_tas_basetime(timespec64_to_ktime(cfg.start), + ns + PTP_SAFE_TIME_OFFSET_NS, period); + ret = stmmac_flex_pps_config(priv, priv->ioaddr, i, &cfg, true, + priv->sub_second_inc, priv->systime_flags); + } + write_unlock_irqrestore(&priv->ptp_lock, flags); + if (ret || !priv->ptp_extts) + return ret; + + rq.extts.index = __ffs(priv->ptp_extts); + return __stmmac_enable(&priv->ptp_clock_ops, &rq, 1); +} + /** * stmmac_get_syncdevicetime * @device: current device time @@ -287,9 +382,15 @@ static int stmmac_getcrosststamp(struct ptp_clock_info *ptp, { struct stmmac_priv *priv = container_of(ptp, struct stmmac_priv, ptp_clock_ops); + int ret; - return get_device_system_crosststamp(stmmac_get_syncdevicetime, - priv, NULL, xtstamp); + ret = stmmac_ptp_begin(priv); + if (ret) + return ret; + ret = get_device_system_crosststamp(stmmac_get_syncdevicetime, + priv, NULL, xtstamp); + mutex_unlock(&priv->ptp_mutex); + return ret; } /* structure describing a PTP hardware clock */ @@ -323,7 +424,7 @@ const struct ptp_clock_info dwmac1000_ptp_clock_ops = { .adjtime = stmmac_adjust_time, .gettime64 = stmmac_get_time, .settime64 = stmmac_set_time, - .enable = dwmac1000_ptp_enable, + .enable = stmmac_enable, }; /** From patchwork Sat Sep 26 15:49:01 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3478 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id B6FE11C5FAB for ; Sat, 26 Sep 2026 17:56:02 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26272-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26272-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 2BD52333D4 for ; Sat, 26 Sep 2026 15:50:15 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 0379D36D9E0; Sat, 26 Sep 2026 15:49:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Q1DFBLmE" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 1CEAD377558 for ; Sat, 26 Sep 2026 15:49:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437780; cv=none; b=YPHVkkbvGvdqfC779hBGBICA5GSNUUM6T1q9MvkY7pWIiQLj5zaIkHnYaZbnaLR6VvNFI63TkMUqGxRDXCtL2jO50/C+0pA7rBLSYFHrlCqPzpn5ugEG0MuLxXqHFwBl/6Lux3m4lyeHh2UAxJY7XTksv5x8dm7Qe2qLPxrlRFo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437780; c=relaxed/simple; bh=Fs1fLcJcakNPYti2xesmjolkKKjRCOnYepmgSWO4EZk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Ojd8wysnltYaJReV18FD5ku9lrDhJPK637FGoFGNo9E7ohD7ZkHWQBY+Y/klJaqhJU3inuDUWrFbc0FqB+kIyjyQmxzrXxUoN5LMr0597r19oS0D2kAwEX07GOk8j5+suSZRGWiXnKsl85HWepp1R4MRYgkHIl9IAtuWEg3SzlA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Q1DFBLmE; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f42.google.com with SMTP id 006d021491bc7-6d797cfdd5fso186809eaf.2 for ; Sat, 26 Sep 2026 08:49:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437772; x=1791042572; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=XgjZN8ALEfDluGPl+zvIInMEXTqsuH7e7EhuIKCn+Ic=; b=Q1DFBLmEy3UUjuWvXJ2UbRiR5HAdtiNIfUx4eNR0OQGYBG1kWqBgdgBw4Qc5zEwu+0 kopyz+33piBTxz82TFBWWENowLYrLyl+2WmGXvAGHupIxyGf5wPMEDebcUaqrJ9yKP1W rYlqybhLOGtSOwlvvHniM/eb+hyYWD1VHr+EuNoSA7rwgVQXfmINTQsBG3BPf18O63iZ pG4YF+Xa2OsuQcf7dv/xBkPtjnv8n0eZh2VqVbrieBOMUlC5v2PKtRuDapxO3NrM/RER 4+9grDhyVjllDxTMLUn7IrZG1evRGQ7qdIepKIj0UyhSq0wkiAMga/zfepUlGP3i8bAo YpCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437772; x=1791042572; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=XgjZN8ALEfDluGPl+zvIInMEXTqsuH7e7EhuIKCn+Ic=; b=ACA0YpiXqUovv44nLRe7YNqQGdMz/4+OW4BOV0H7QEkDNg355cQLKdAde8ksBiB/tz SI6kCoVDnAk03pTLsCOKItKwT//4wxDzSVHfyF9u/Wa0N5YnjV7dDwXTeYYUGamhzPFw fdyHsO69XeToe8uaDn1z0wIkWdkeV2ZZUYfT6yk4fNBCIlm9WaVrNFjjWLKsH8OfU+8b 8bsYqB2Fmx2c2euNYaLSTEmti+g5CCxQ9sxhId0E2R6Xo2RWEYBFASBSSSYQ/BHZ2Pqv wqU3XDSDGnyD6JnPDI/V9ouj3sV3SQnZizZeOxxtjPOivIPOIEBJ/3GsMtdj1RGcM5S2 uSsA== X-Forwarded-Encrypted: i=1; AKwUvBxblayHaJqv5MbZrIw1812nehoHB4D5iffS0sZbeF+6ZNKCQKZ10aziB0npaKh2IahsH+Az+2IXMujzXg==@lists.linux.dev X-Gm-Message-State: AFuF++kPiydcspicUUKmTCmTmT4BzkHeLGlkUYUXOFTuHu/eOoG1fDu2 BGQdAmN/5PCH1Nz4n23P44mRLCuVQVu40va5JLWSkqcTK1sA3dWlGjL+ X-Gm-Gg: AYBFou2iQSilbtBmf3vBx8rrz0L9Ekz74ufLivJfCLezctIpN5A1U1p2UUqkYK8UN5t f0V4Un6ip1kjlOA7n76C/rX4mVwcfGpdMxf8qmk/CrvBTE7JlsAWsP7tYNI3sNeT+XBhLEjrhLe p16NMJGhuXaXYjZGJ7O1il1mFRL3uSft0Z7wZm5wIzQHMa6c3C7OIcNQGlvedtj2UPTUnEvTvQa GwEn/XT7/+2q0n4vulo6NIccUQOdRg4M5nTeuryhO3IYT2ZPuYyiI7ECkuY2lz1x/C6BBVnr4gc 27+7TuN08Zc1AdK/cd0YQuEItRvXPSnBtql8OK8PTQvBSQIwFl3zzJlev5AgnZKHcnWYoXbUz8n AQnmSyEExNqAuumuEQ13YeyHSlsx3WcUm+gvOmfjqr7MWbjGoc6BeTwCFzSto/nGKicBemKocDn 1BrfdIbbxcxtUarY5TVWXLOlyaMxFfdc9ytktnRAdA6bxI7VfRlyd3ihESZyL8bMi5JxlAars0G ursH7CJuIegbR2+58iMFNQkmo8VfAigM5Q17owP8q/r2gBboBO5cRdNZ7aEkl0oASozJDqC2Z+b VGQh+t/fS7wJjQuXNqQsQM4mzMZ7JMoqgsbzyTeULcgbls+wouLrUo1esrGwgg3RiMg1hn9WpzH AYxrX+rH315G43i/AkgUNo13dEVnzOFA= X-Received: by 2002:a05:6820:f02d:b0:6b4:117e:543 with SMTP id 006d021491bc7-6d43f7a6d39mr9544686eaf.21.1790437771939; Sat, 26 Sep 2026 08:49:31 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:31 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:49:01 -0600 Subject: [PATCH net v4 06/15] net: stmmac: leave the datapath running for normal-size MTU changes Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-6-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Changing an MTU at or below ETH_DATA_LEN does not change the receive buffer size or the MAC receive limit when the previous MTU was also in that range. Do not release and reopen the datapath for those changes. Besides avoiding unnecessary hardware resets and their failure paths, this keeps live AF_XDP pool bindings intact. Preparing replacement rings before stopping the old rings otherwise binds the pool to a temporary RXQ and consumes fill-ring entries while its current RXQ is still active. XDP already rejects jumbo MTUs, so all supported live XDP MTU changes can use this path without preparing replacement rings. Keep jumbo transitions on the existing reinitialization path for now. The later ownership and rollback changes address that path separately. Fixes: 3470079687448 ("net: ethernet: stmicro: stmmac: permit MTU change with interface up") Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 8b0bc7c46df9..bdebc5bfa2b8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -6166,7 +6166,12 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB)) return -EINVAL; - if (netif_running(dev)) { + /* Normal-size frames use the same buffers and MAC receive limits. + * In particular, do not disturb a live AF_XDP pool: XDP does not + * support jumbo frames, so it never needs the ring replacement below. + */ + if (netif_running(dev) && + (dev->mtu > ETH_DATA_LEN || mtu > ETH_DATA_LEN)) { netdev_dbg(priv->dev, "restarting interface to change its MTU\n"); /* Try to allocate the new DMA conf with the new mtu */ dma_conf = stmmac_setup_dma_desc(priv, mtu); From patchwork Sat Sep 26 15:49:02 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3480 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 793101C2AA6 for ; Sat, 26 Sep 2026 17:56:15 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26271-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26271-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id DF08D3D672 for ; Sat, 26 Sep 2026 15:50:14 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BA835370AC2; Sat, 26 Sep 2026 15:49:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="d+ST6ofx" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f43.google.com (mail-oo2-f43.google.com [74.125.231.171]) (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 3C7F92E2663 for ; Sat, 26 Sep 2026 15:49:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437780; cv=none; b=FqburIu3rI1LtkEd6bsIWlqdtP3SchQZ5Vk2YqJ4vQ958X17z2MPODYs5D/NAO5tWF0oHWz6QLPuCAv3jpX7qUlDUXdtFg4PdhVddT19rhVBtM2kq/DOehpte7kwzMirpAqRAp8t56GZbWpt0lagw1+A+XyZeGZz5mf9tgndLxI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437780; c=relaxed/simple; bh=UTFnmKMIa/HphfeRGv/w2u8lZ3AUss0q6DN+VLPgUAA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ecMqbZovxECh6yfA6+igJzYYnH5KwBFw4hHcFHncPCtuZX+IWcz7/cI4fsyq2qUK03f4m5U4Am5AWBcr0EWeh6dFF7AGcTMuOTHlpziYx4dtwuF0PiWWkTM5yFY8prEFpg6cbfYr70UaMqbcgqASKiIJbjd7tuNGGvUahEORJek= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=d+ST6ofx; arc=none smtp.client-ip=74.125.231.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f43.google.com with SMTP id 46e09a7af769-81b485c8368so89154a34.3 for ; Sat, 26 Sep 2026 08:49:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437774; x=1791042574; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=r2upeAgz4ZvptOHZYdf2cN5u9HukHte/X4VwE60m55w=; b=d+ST6ofxfVnLoJB6COZIEBsrZ+9Z1HoqwAN3njfVYIsB7YHODxXahVepZB+GiT39uC wC4qgYyVDNMw5UusNX7/FAOMdeBVcym6gLmbgZGni7BRAex0GIPVG4uXpfXXwM7CysS/ IX6sp7ZYPwIrP5dYzXvpux4wRDlVfFCM5LsAuZPF/nHGNLap/manONDhTapI0g2gfQuu Pi/0UFKkYOqDrw6ttVgKcmYXE2XPm0Drw+Shu5gIa8o8ao6b0MZHY6Nfl0UPbEsd1jR6 T0ztILv74S2twz9EhraDdyz7ZSloIIx5ujUD4eCk7JVN0jwHQGD3sdyKpQq6xhKPKs7c CMSg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437774; x=1791042574; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=r2upeAgz4ZvptOHZYdf2cN5u9HukHte/X4VwE60m55w=; b=hmumdOCLOz2VioVmhInM4T02PqJ93EeXmRJRjjOtZRtlNHrpKUUIqlEnkDD2J8O+9G PmQ1rzA1quaAouE9UUwOYbJV1U4H22UOOjhA+cYWfwx1Xaa2Bl/SbDTWt7sAFtQ/n3Yh d6uS1/s1WXeh85se4GzvLSGXEY4BhVa2ic36/CJKZxm4JZgkjBU57Wg/+geEB7PrGzOe MDykB6h+wMpU8ckFvVBSEoxpxqTXwbE45ZtwJXtkaOyX+fDNFuK3rjFfkETiIF4waupE vXSOnAjGyUfRZTltnxge5cFRH8gZSf3N3TXZGjyHiZ05T6j72jCBrxpiHE9Y129ViDdh aRtw== X-Forwarded-Encrypted: i=1; AKwUvBwDAdt3vcWwCByDrx0rwebneuDP02GAdoZFbB8GntzR7c6+9dS/K05QovuJSYmExpLhi9SPOzOe8QWuZg==@lists.linux.dev X-Gm-Message-State: AFuF++myawMZfndrWgtOqIiMrMH7VAoGf3r0VUG1MnkqpUpinK2l6TK/ lBs3QM9XnTVbKgPXjOnMZECu3xYA2BABPmpXSRs6SuNtchDJ1BZMY2gf X-Gm-Gg: AYBFou3ZIopIimDN9Y9Ox/HpuoiLyNpSwstjAeWXhlm6eaDwVneP4zaNYXnjRn0V5Zt 00fgtTS1cNTjiTloCG4ZgYS3e3tHWJtl8Nkrc0H7wAAow1GYLXOjXFiuD2n1mmNNY8yZptxCZ/j vFsI5vqbp7QaUxTWVNSGxAezQsCniXCnKJ8iDLZwXuf1gNS4f2aJmrApU1L3gmQvCeHtmwSzSlB /X3NCWbluWjQXS94J7aWToqITDdERSmjXZAkPxR23KBcnXWjbsA02s0clHUSvIxYiaZFWYEXXf7 gHp6T//kawG6BMq2lb+Ch8x+nXTIRxeCtWCGG6rwhj7wpjosB4wV3FjVAIIcGYFy3EawVBbq/i8 ER+roitkI9dKXl57huoBdRO6W6VNTiRfKurZGfs9Y8SymkfB+KlkXKsftIGiq9q44rm7K3VJaVc xIxzm67Iz68ZMqm8gAp++KWHOixxmupRHWJ5pE1L/3cBJDJqSfC9CeyHOFKg6UX6jIAMuqPl86z PPAy0VjLqvOi2QslZ8Hq4Ulf9biQ/B3Y6+TlLtK/Pvm+SRXDTCnBlE+/Aydt2lV/Ln15e8HvSkY C+s5EAKzXKeLIFAIR3aa1bTzpg29Jd9AJw09wMBE1ed5tBSYNlqp6bFFEbZQbCEP6e+Ew5KzSIa PE5OTIh1oUN/3+pMJUvdF X-Received: by 2002:a05:6830:628c:b0:804:bb22:6d42 with SMTP id 46e09a7af769-81781d92d4cmr10071685a34.17.1790437773995; Sat, 26 Sep 2026 08:49:33 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:33 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:49:02 -0600 Subject: [PATCH net v4 07/15] net: stmmac: unwind partially allocated DMA configurations Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-7-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [-1.16 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[172.234.253.10:from]; SUSPICIOUS_RECIPS(1.50)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; MAILLIST(-0.15)[generic]; MIME_GOOD(-0.10)[text/plain]; BAD_REP_POLICIES(0.10)[]; HAS_LIST_UNSUB(-0.01)[]; DBL_BLOCKED_OPENRESOLVER(0.00)[sea.lore.kernel.org:rdns,sea.lore.kernel.org:helo]; FORGED_SENDER_MAILLIST(0.00)[]; RCVD_COUNT_FIVE(0.00)[6]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; FROM_HAS_DN(0.00)[]; TAGGED_RCPT(0.00)[kernel,netdev]; PRECEDENCE_BULK(0.00)[]; FREEMAIL_CC(0.00)[bootlin.com,d-silva.org,kernel.org,vger.kernel.org,st-md-mailman.stormreply.com,lists.infradead.org,uniontech.com,oss.qualcomm.com,163.com,gmail.com,lists.linux.dev]; FROM_NEQ_ENVFROM(0.00)[jameshilliard1@gmail.com,linux-sunxi@lists.linux.dev]; ASN(0.00)[asn:63949, ipnet:172.234.224.0/19, country:SG]; MID_RHS_MATCH_FROM(0.00)[]; FREEMAIL_FROM(0.00)[gmail.com]; R_SPF_ALLOW(0.00)[+ip4:172.234.253.10]; TO_DN_SOME(0.00)[]; RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[172.234.253.10:from]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[100.90.174.1:received,74.125.231.171:received]; TAGGED_FROM(0.00)[bounces-26271-noreply=patchwork.local]; RCPT_COUNT_GT_50(0.00)[54]; RECEIVED_SPAMHAUS_PBL(0.00)[174.29.1.49:received]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; FREEMAIL_TO(0.00)[armlinux.org.uk,lunn.ch,gmail.com,davemloft.net,kernel.org,redhat.com,nxp.com,bootlin.com,foss.st.com,loongson.cn,iogearbox.net,fomichev.me,quicinc.com,synopsys.com,intel.com,googlemail.com,nvidia.com,sholland.org]; RCVD_TLS_LAST(0.00)[]; MIME_TRACE(0.00)[0:+]; RCVD_VIA_SMTP_AUTH(0.00)[] X-Rspamd-Queue-Id: 793101C2AA6 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= RX and TX allocation failure paths release all configured queues, even when the failing queue has not allocated its buffer array or descriptors. Only walk buffer arrays and free coherent memory when those allocations exist. If TX allocation fails after RX allocation succeeds, release the RX resources too. Propagate RXQ memory-model registration errors instead of continuing with an unusable RXQ. Clear XSK RXQ bindings before the RXQ goes away, release any saved partial packet, and clear released resource pointers. This also leaves configurations which are reused after close or XDP reconfiguration with a consistent empty state. The MTU transaction added later relies on preparation failures being fully unwound without touching the currently active configuration. Take ownership of saved partial RX state at poll entry by clearing the saved flag and skb pointer immediately. Preserve incomplete state if the next descriptor is still DMA-owned. A budget-one completion must not leave an already delivered or freed skb reachable by the new teardown cleanup. Fixes: 71fedb0198cb ("net: stmmac: break some functions into RX and TX scopes") Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 69 +++++++++++++++++------ 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index bdebc5bfa2b8..1cc68c1cf37d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1766,6 +1766,9 @@ static void dma_free_rx_skbufs(struct stmmac_priv *priv, struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; int i; + if (!rx_q->buf_pool) + return; + for (i = 0; i < dma_conf->dma_rx_size; i++) stmmac_free_rx_buffer(priv, rx_q, i); } @@ -1889,17 +1892,19 @@ static int __init_dma_rx_desc_rings(struct stmmac_priv *priv, rx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue); if (rx_q->xsk_pool) { - WARN_ON(xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq, - MEM_TYPE_XSK_BUFF_POOL, - NULL)); + ret = xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq, + MEM_TYPE_XSK_BUFF_POOL, NULL); + if (ret) + return ret; netdev_info(priv->dev, "Register MEM_TYPE_XSK_BUFF_POOL RxQ-%d\n", queue); xsk_pool_set_rxq_info(rx_q->xsk_pool, &rx_q->xdp_rxq); } else { - WARN_ON(xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq, - MEM_TYPE_PAGE_POOL, - rx_q->page_pool)); + ret = xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq, + MEM_TYPE_PAGE_POOL, rx_q->page_pool); + if (ret) + return ret; netdev_info(priv->dev, "Register MEM_TYPE_PAGE_POOL RxQ-%d\n", queue); @@ -1961,6 +1966,8 @@ static int init_dma_rx_desc_rings(struct net_device *dev, dma_free_rx_skbufs(priv, dma_conf, queue); rx_q->buf_alloc_num = 0; + if (rx_q->xsk_pool) + xsk_pool_set_rxq_info(rx_q->xsk_pool, NULL); rx_q->xsk_pool = NULL; queue--; @@ -2104,6 +2111,9 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv, tx_q->xsk_frames_done = 0; + if (!tx_q->tx_skbuff_dma || !tx_q->tx_skbuff) + return; + for (i = 0; i < dma_conf->dma_tx_size; i++) stmmac_free_tx_buffer(priv, dma_conf, queue, i); @@ -2142,10 +2152,16 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, void *addr; /* Release the DMA RX socket buffers */ - if (rx_q->xsk_pool) + if (rx_q->xsk_pool) { dma_free_rx_xskbufs(priv, dma_conf, queue); - else + xsk_pool_set_rxq_info(rx_q->xsk_pool, NULL); + } else { dma_free_rx_skbufs(priv, dma_conf, queue); + } + if (rx_q->state_saved) + dev_kfree_skb_any(rx_q->state.skb); + rx_q->state.skb = NULL; + rx_q->state_saved = 0; rx_q->buf_alloc_num = 0; rx_q->xsk_pool = NULL; @@ -2158,7 +2174,8 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, size = stmmac_get_rx_desc_size(priv) * dma_conf->dma_rx_size; - dma_free_coherent(priv->device, size, addr, rx_q->dma_rx_phy); + if (addr) + dma_free_coherent(priv->device, size, addr, rx_q->dma_rx_phy); if (xdp_rxq_info_is_reg(&rx_q->xdp_rxq)) xdp_rxq_info_unreg(&rx_q->xdp_rxq); @@ -2166,6 +2183,10 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, kfree(rx_q->buf_pool); if (rx_q->page_pool) page_pool_destroy(rx_q->page_pool); + rx_q->buf_pool = NULL; + rx_q->page_pool = NULL; + rx_q->dma_erx = NULL; + rx_q->dma_rx = NULL; } static void free_dma_rx_desc_resources(struct stmmac_priv *priv, @@ -2206,10 +2227,16 @@ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, size = stmmac_get_tx_desc_size(priv, tx_q) * dma_conf->dma_tx_size; - dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy); + if (addr) + dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy); kfree(tx_q->tx_skbuff_dma); kfree(tx_q->tx_skbuff); + tx_q->tx_skbuff_dma = NULL; + tx_q->tx_skbuff = NULL; + tx_q->dma_etx = NULL; + tx_q->dma_entx = NULL; + tx_q->dma_tx = NULL; } static void free_dma_tx_desc_resources(struct stmmac_priv *priv, @@ -2416,6 +2443,8 @@ static int alloc_dma_desc_resources(struct stmmac_priv *priv, return ret; ret = alloc_dma_tx_desc_resources(priv, dma_conf); + if (ret) + free_dma_rx_desc_resources(priv, dma_conf); return ret; } @@ -5726,6 +5755,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) struct sk_buff *skb = NULL; struct stmmac_xdp_buff ctx; int xdp_status = 0; + bool in_progress = rx_q->state_saved; int bufsz; dma_dir = page_pool_get_dma_dir(rx_q->page_pool); @@ -5740,6 +5770,14 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true, rx_q->dma_rx_phy, desc_size); } + if (in_progress) { + skb = rx_q->state.skb; + error = rx_q->state.error; + len = rx_q->state.len; + rx_q->state.skb = NULL; + rx_q->state_saved = false; + } + while (count < limit) { unsigned int buf1_len = 0, buf2_len = 0; enum pkt_hash_types hash_type; @@ -5748,12 +5786,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) int entry; u32 hash; - if (!count && rx_q->state_saved) { - skb = rx_q->state.skb; - error = rx_q->state.error; - len = rx_q->state.len; - } else { - rx_q->state_saved = false; + if (!in_progress) { skb = NULL; error = 0; len = 0; @@ -5787,6 +5820,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) prefetch(np); + in_progress = status & rx_not_ls; + if (priv->extend_desc) stmmac_rx_extended_status(priv, &priv->xstats, rx_q->dma_erx + entry); if (unlikely(status == discard_frame)) { @@ -5961,7 +5996,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) count++; } - if (status & rx_not_ls || skb) { + if (in_progress || skb) { rx_q->state_saved = true; rx_q->state.skb = skb; rx_q->state.error = error; From patchwork Sat Sep 26 15:49:03 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3473 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sto.lore.kernel.org (sto.lore.kernel.org [172.232.135.74]) by mxe881.netcup.net (Postfix) with ESMTPS id BA1021C5FA3 for ; Sat, 26 Sep 2026 17:51:55 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.232.135.74) smtp.mailfrom=linux-sunxi+bounces-26273-noreply=patchwork.local@lists.linux.dev smtp.helo=sto.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.232.135.74 as permitted sender) client-ip=172.232.135.74; envelope-from=linux-sunxi+bounces-26273-noreply=patchwork.local@lists.linux.dev; helo=sto.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sto.lore.kernel.org (Postfix) with ESMTP id 8D50260B5F6 for ; Sat, 26 Sep 2026 15:50:22 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 6DF6B37E5C7; Sat, 26 Sep 2026 15:49:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hbUeRd/F" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f39.google.com (mail-oo2-f39.google.com [74.125.231.167]) (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 B5D7A33F5BE for ; Sat, 26 Sep 2026 15:49:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.167 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437783; cv=none; b=KZLdxMUPQCpqONcmsrDwvvvEWdw17AC35x38sAf5lA5lU3rqY6v0tG/kA7/ayknSTgkgoZxSzFKmNe8X/V6GdFM8lRhdG6bAqQQo64BuzPULPwtqrPzml1+KaU8S5ahLHaxcA9BfrZv/KX0tr2zLFX0ftIfGPnpDUPcMJBkZvTQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437783; c=relaxed/simple; bh=PzKR5f9UeUjo2o2nbmLnYxxY1A3Ug0X1MglneQTgbwA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=UTpkDeu3hGxdP55ZKUPQuWlUYqTPONA0Ks9g0dUJ/zzFbB3+xsDWVLB3+WQKrjcl9iDvhU2eFG5ucAbV2ENFT/w/gcp/mKybxm0nBLyqvTwR2R7HiUijJayzPXl2tafO6dMzUoshf+WasP7uUeri9nSdSoSOAAtPejVtCWdWf4Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=hbUeRd/F; arc=none smtp.client-ip=74.125.231.167 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f39.google.com with SMTP id 46e09a7af769-7f4f0c89e33so1049228a34.0 for ; Sat, 26 Sep 2026 08:49:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437776; x=1791042576; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=cyrEFwRRzcImpbiMWvwbC+++5V6smbgLkq/nuIioBKo=; b=hbUeRd/FQAjgYg59WivfwSADbtz7JLdrRFK+sfgrgmL360bALu68O6yv9kbf+S/hW8 2FAWuKaptlmKrnq2/gI58J5cDlkjj7VkBFD/+51STZqclWI6sMalXhQoypVY5KwwTC1J qbuU90IT6aRae9G7phNHyviKCpXlnbsEG/dOOu1roPZ9NNbxMvsF0xveyYiKAIGIFWYy DGQHAJdGLsW8Ybb0ZBx1o57UybuZIWHfHKYf1zrRS9t8JM7MwYwVs1qKkKritScYfKaJ gug0gM+BJvKN1nXBExK537l8MXKilk4LZetaR8qHLq057cXVALmgPnkhOOst0vvVYv54 GqMw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437776; x=1791042576; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=cyrEFwRRzcImpbiMWvwbC+++5V6smbgLkq/nuIioBKo=; b=zQ77qy4eLBR11CF5Ap4zKu5hetwEVNzHo6FoqEVST4pcnfr9rwB3wxtESeQxuQe8Q6 pSTxA3RQz3SDlnArhcj+xxPwf72aLosO+orfLm4vw90LwTJXXuiM9clILdB3mb7bNgeg 9lXDeQXW5+sPMXgnp37T1uR0G10MBq2ai+tpk2dJzSJYlgED8JhUWQLen57PwVmQNlgS Oq9tfMSkW/PfdzBFD1UbN2VxoilnuI2dNxhZbpN08v5hboWIVDW0I7Gtj4nUz0enlYHx dHsLefoWCQL6iGICPxyuVndPWq3gWJkpOOpQ9+H8usBBMdX9QXm5QcKl7iny0TNHCB86 0zdQ== X-Forwarded-Encrypted: i=1; AKwUvBwGfF0JSpLFaDUMzXJkTBoKuHC9OAwwuL6zQ6y9BYnnryN7CzTowbd7Hy1KttqWlUmbUmFVDO3HN13Qfw==@lists.linux.dev X-Gm-Message-State: AFuF++neWsHIOepCPjXdgHDIYfxJ/zMg/e92x7TjQllTKrnKxnToKomb BoVgVW6q95zBBkpZ/sxQ46wMl2z3Xd7IsfrpyaeeJLMSNlQrLbzXSfyR X-Gm-Gg: AYBFou299aJ4yBDPlGR18ufXdT3mL3q4K8RWVd+88vhRXwNnTTh/mUqPR8olAyk+asq cqNPRlILtPPc0YLKhp2770EpglSdO6DVvosROZtgqMKXN4CKXsKsP5CBYNfJC24vi/KK4VOxkGF 9Q0W2kB9jngrS6JoaIKq2OYbyAZs8oun8MOCESLOeaC5aLf5twWhWJp0dC4Jq9D6+GbFbT3XaTW X7bQdF/8KW8COL1AWRo6RDri6H75V37D1KiNUPZlTueNoch0ZFwE9HtBtScLNcmGS5CekkzIgd2 NRY17ghCET+EKEVsRRqn0lh3Tv+8fj2AghQU9mmhWpNJNxQwTCx5+hwM4zWWSwpPqmcl77bcK1D GfU/n7L6vtnMy/HskXZqVRxft4o/o5SNI9PPmG3wRlNWDvfgqEp8noW2RCKYLXMx8bSnwuEoG9A 4wPzf4v3SLYCy3qbhcN3ezR54zN1gVgol19AE4G6Ghn3ytVyCU7hU1ZcXElQDFsrofQrTFvEjpo fhzbQo4ICtL3/NEupV/WbYvKjbEwhgapGMUOPze6wjjWZJtIWtjjs38tXYLaV9C66cwX/abwX9L k3P/WDklsqVL+MVo8D88vpmENadm5P6dI5toEVtSv9rK02VoOToME5UMiMGcGXY0P0/hXzI4d2s YyEu+bxuuSSFDeWu8BdENq3GwtnI08Og= X-Received: by 2002:a05:6830:621c:b0:802:4cf7:ea46 with SMTP id 46e09a7af769-81782a86ed9mr10728677a34.20.1790437776255; Sat, 26 Sep 2026 08:49:36 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:35 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:49:03 -0600 Subject: [PATCH net v4 08/15] net: stmmac: keep DMA configurations at stable addresses Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-8-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= The allocated DMA configuration contains RXQ metadata registered with XDP and referenced by AF_XDP pools. Copying the configuration into priv and freeing its original allocation leaves those references pointing at the old storage. Retain the allocated object instead and keep a pointer in priv, preserving ring sizes and per-queue settings while down. Use persistent channel objects for per-queue IRQ contexts instead of deriving priv from an embedded DMA configuration. XSK wakeup must also avoid accessing replaceable queue objects. Drain transmitters and NAPI poll tails before cancelling TX timers so a late rearm cannot outlive the configuration containing the timer. Update both open callers with the ownership change. Successful open keeps the new configuration and frees the empty old object. Failed open restores the old pointer before freeing the failed replacement. Detach around the existing MTU reopen so XDP transmit cannot enter while that pointer is being replaced, and reattach after success. The later MTU transaction replaces this reopen path with retained-resource rollback. The empty configuration remains allocated while down because ethtool, TC and the next open still use its ring sizes and per-queue settings. The probe-managed action frees the current object after netdev teardown. Cancel the software EEE timer after the poll/transmit drain as well: a TX completion may have passed its enable check before phylink cancelled it. GSO feature checks are not excluded by stopped queues or netdev detach. Read the immutable platform TBS capability instead of dereferencing the replaceable configuration from ndo_features_check(). Fixes: ba39b344e924 ("net: ethernet: stmicro: stmmac: generate stmmac dma conf before open") Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/chain_mode.c | 6 +- drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 4 +- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 +- .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 4 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 324 +++++++++++---------- .../net/ethernet/stmicro/stmmac/stmmac_selftests.c | 8 +- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 6 +- 7 files changed, 189 insertions(+), 165 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index 66025e2509e9..65243c5e539e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c @@ -48,7 +48,7 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb, while (len != 0) { tx_q->tx_skbuff[entry] = NULL; - entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size); + entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size); desc = tx_q->dma_tx + entry; if (len > bmax) { @@ -137,7 +137,7 @@ static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p) */ p->des3 = cpu_to_le32((unsigned int)(rx_q->dma_rx_phy + (((rx_q->dirty_rx) + 1) % - priv->dma_conf.dma_rx_size) * + priv->dma_conf->dma_rx_size) * sizeof(struct dma_desc))); } @@ -154,7 +154,7 @@ static void clean_desc3(struct stmmac_tx_queue *tx_q, struct dma_desc *p) */ p->des3 = cpu_to_le32((unsigned int)((tx_q->dma_tx_phy + ((tx_q->dirty_tx + 1) % - priv->dma_conf.dma_tx_size)) + priv->dma_conf->dma_tx_size)) * sizeof(struct dma_desc))); } diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index d2f0c321661d..0299d6a6c32b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -52,7 +52,7 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb, stmmac_prepare_tx_desc(priv, desc, 1, bmax, csum, STMMAC_RING_MODE, 0, false, skb->len); tx_q->tx_skbuff[entry] = NULL; - entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size); + entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size); if (priv->extend_desc) desc = (struct dma_desc *)(tx_q->dma_etx + entry); @@ -102,7 +102,7 @@ static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p) struct stmmac_priv *priv = rx_q->priv_data; /* Fill DES3 in case of RING mode */ - if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB) + if (priv->dma_conf->dma_buf_sz == BUF_SIZE_16KiB) p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB); } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index aa09b2defcd9..3365ed237953 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -280,7 +280,7 @@ struct stmmac_priv { int (*hwif_quirks)(struct stmmac_priv *priv); struct mutex lock; - struct stmmac_dma_conf dma_conf; + struct stmmac_dma_conf *dma_conf; /* Generic channel for NAPI */ struct stmmac_channel channel[STMMAC_CH_MAX]; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 1be5310ca766..994350fcdbfa 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -396,8 +396,8 @@ static void stmmac_get_ringparam(struct net_device *netdev, ring->rx_max_pending = DMA_MAX_RX_SIZE; ring->tx_max_pending = DMA_MAX_TX_SIZE; - ring->rx_pending = priv->dma_conf.dma_rx_size; - ring->tx_pending = priv->dma_conf.dma_tx_size; + ring->rx_pending = priv->dma_conf->dma_rx_size; + ring->tx_pending = priv->dma_conf->dma_tx_size; } static int stmmac_set_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 1cc68c1cf37d..c4ba97d57b47 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -79,7 +79,7 @@ static int phyaddr = -1; module_param(phyaddr, int, 0444); MODULE_PARM_DESC(phyaddr, "Physical device address"); -#define STMMAC_TX_THRESH(x) ((x)->dma_conf.dma_tx_size / 4) +#define STMMAC_TX_THRESH(x) ((x)->dma_conf->dma_tx_size / 4) /* Limit to make sure XDP TX and slow path can coexist */ #define STMMAC_XSK_TX_BUDGET_MAX 256 @@ -297,7 +297,7 @@ static void stmmac_disable_all_queues(struct stmmac_priv *priv) /* synchronize_rcu() needed for pending XDP buffers to drain */ for (queue = 0; queue < rx_queues_cnt; queue++) { - rx_q = &priv->dma_conf.rx_queue[queue]; + rx_q = &priv->dma_conf->rx_queue[queue]; if (rx_q->xsk_pool) { synchronize_rcu(); break; @@ -356,10 +356,10 @@ static void print_pkt(unsigned char *buf, int len) static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; return CIRC_SPACE(tx_q->cur_tx, tx_q->dirty_tx, - priv->dma_conf.dma_tx_size); + priv->dma_conf->dma_tx_size); } static size_t stmmac_get_tx_desc_size(struct stmmac_priv *priv, @@ -438,7 +438,7 @@ static void stmmac_set_queue_rx_buf_size(struct stmmac_priv *priv, if (rx_q->xsk_pool && rx_q->buf_alloc_num) buf_size = xsk_pool_get_rx_frame_size(rx_q->xsk_pool); else - buf_size = priv->dma_conf.dma_buf_sz; + buf_size = priv->dma_conf->dma_buf_sz; stmmac_set_dma_bfsize(priv, priv->ioaddr, buf_size, chan); } @@ -450,10 +450,10 @@ static void stmmac_set_queue_rx_buf_size(struct stmmac_priv *priv, */ static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue]; return CIRC_CNT(rx_q->cur_rx, rx_q->dirty_rx, - priv->dma_conf.dma_rx_size); + priv->dma_conf->dma_rx_size); } static bool stmmac_eee_tx_busy(struct stmmac_priv *priv) @@ -463,7 +463,7 @@ static bool stmmac_eee_tx_busy(struct stmmac_priv *priv) /* check if all TX queues have the work finished */ for (queue = 0; queue < tx_cnt; queue++) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; if (tx_q->dirty_tx != tx_q->cur_tx) return true; /* still unfinished work */ @@ -2134,7 +2134,7 @@ static void stmmac_free_tx_skbufs(struct stmmac_priv *priv) u8 queue; for (queue = 0; queue < tx_queue_cnt; queue++) - dma_free_tx_skbufs(priv, &priv->dma_conf, queue); + dma_free_tx_skbufs(priv, priv->dma_conf, queue); } /** @@ -2643,7 +2643,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv) /* configure all channels */ for (chan = 0; chan < rx_channels_count; chan++) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan]; + struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[chan]; qmode = priv->plat->rx_queues_cfg[chan].mode_to_use; @@ -2715,7 +2715,7 @@ static const struct xsk_tx_metadata_ops stmmac_xsk_tx_metadata_ops = { static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) { struct netdev_queue *nq = netdev_get_tx_queue(priv->dev, queue); - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue]; bool csum = !priv->plat->tx_queues_cfg[queue].coe_unsupported; struct xsk_buff_pool *pool = tx_q->xsk_pool; @@ -2804,7 +2804,7 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) xsk_tx_metadata_to_compl(meta, &tx_q->tx_skbuff_dma[entry].xsk_meta); - tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); + tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf->dma_tx_size); entry = tx_q->cur_tx; } u64_stats_update_begin(&txq_stats->napi_syncp); @@ -2852,7 +2852,7 @@ static void stmmac_bump_dma_threshold(struct stmmac_priv *priv, u32 chan) static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, bool *pending_packets) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue]; unsigned int bytes_compl = 0, pkts_compl = 0; unsigned int entry, xmits = 0, count = 0; @@ -2865,7 +2865,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, entry = tx_q->dirty_tx; /* Try to clean all TX complete frame in 1 shot */ - while ((entry != tx_q->cur_tx) && count < priv->dma_conf.dma_tx_size) { + while ((entry != tx_q->cur_tx) && count < priv->dma_conf->dma_tx_size) { struct xdp_frame *xdpf; struct sk_buff *skb; struct dma_desc *p; @@ -2972,7 +2972,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, stmmac_release_tx_desc(priv, p, priv->descriptor_mode); - entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size); + entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size); } tx_q->dirty_tx = entry; @@ -3040,13 +3040,13 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, */ static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan]; netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan)); stmmac_stop_tx_dma(priv, chan); - dma_free_tx_skbufs(priv, &priv->dma_conf, chan); - stmmac_clear_tx_descriptors(priv, &priv->dma_conf, chan); + dma_free_tx_skbufs(priv, priv->dma_conf, chan); + stmmac_clear_tx_descriptors(priv, priv->dma_conf, chan); stmmac_reset_tx_queue(priv, chan); stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, tx_q->dma_tx_phy, chan); @@ -3107,8 +3107,8 @@ static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan, u32 dir) { int status = stmmac_dma_interrupt_status(priv, priv->ioaddr, &priv->xstats, chan, dir); - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan]; - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[chan]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan]; struct stmmac_channel *ch = &priv->channel[chan]; struct napi_struct *rx_napi; struct napi_struct *tx_napi; @@ -3328,7 +3328,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) /* DMA RX Channel Configuration */ for (chan = 0; chan < rx_channels_count; chan++) { - rx_q = &priv->dma_conf.rx_queue[chan]; + rx_q = &priv->dma_conf->rx_queue[chan]; stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, rx_q->dma_rx_phy, chan); @@ -3339,7 +3339,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) /* DMA TX Channel Configuration */ for (chan = 0; chan < tx_channels_count; chan++) { - tx_q = &priv->dma_conf.tx_queue[chan]; + tx_q = &priv->dma_conf->tx_queue[chan]; stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, tx_q->dma_tx_phy, chan); @@ -3352,7 +3352,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; u32 tx_coal_timer = priv->tx_coal_timer[queue]; struct stmmac_channel *ch; struct napi_struct *napi; @@ -3420,7 +3420,7 @@ static void stmmac_init_coalesce(struct stmmac_priv *priv) u8 chan; for (chan = 0; chan < tx_channel_count; chan++) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan]; priv->tx_coal_frames[chan] = STMMAC_TX_FRAMES; priv->tx_coal_timer[chan] = STMMAC_COAL_TX_TIMER; @@ -3441,12 +3441,12 @@ static void stmmac_set_rings_length(struct stmmac_priv *priv) /* set TX ring length */ for (chan = 0; chan < tx_channels_count; chan++) stmmac_set_tx_ring_len(priv, priv->ioaddr, - (priv->dma_conf.dma_tx_size - 1), chan); + (priv->dma_conf->dma_tx_size - 1), chan); /* set RX ring length */ for (chan = 0; chan < rx_channels_count; chan++) stmmac_set_rx_ring_len(priv, priv->ioaddr, - (priv->dma_conf.dma_rx_size - 1), chan); + (priv->dma_conf->dma_rx_size - 1), chan); } /** @@ -3654,8 +3654,10 @@ static void stmmac_safety_feat_configuration(struct stmmac_priv *priv) static bool stmmac_tso_channel_permitted(struct stmmac_priv *priv, unsigned int chan) { - /* TSO and TBS cannot co-exist */ - return !(priv->dma_conf.tx_queue[chan].tbs & STMMAC_TBS_AVAIL); + /* TSO and TBS cannot co-exist. Feature checks also run while the + * datapath is detached, so use the lifetime-stable platform setting. + */ + return !priv->plat->tx_queues_cfg[chan].tbs_en; } /** @@ -3773,7 +3775,7 @@ static int stmmac_hw_setup(struct net_device *dev) /* TBS */ for (chan = 0; chan < tx_cnt; chan++) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan]; int enable = tx_q->tbs & STMMAC_TBS_AVAIL; stmmac_enable_tbs(priv, priv->ioaddr, enable, chan); @@ -3809,7 +3811,7 @@ static void stmmac_free_irq(struct net_device *dev, if (msi->tx_irq[j] > 0) { irq_set_affinity_hint(msi->tx_irq[j], NULL); free_irq(msi->tx_irq[j], - &priv->dma_conf.tx_queue[j]); + &priv->channel[j]); } } irq_idx = priv->plat->rx_queues_to_use; @@ -3819,7 +3821,7 @@ static void stmmac_free_irq(struct net_device *dev, if (msi->rx_irq[j] > 0) { irq_set_affinity_hint(msi->rx_irq[j], NULL); free_irq(msi->rx_irq[j], - &priv->dma_conf.rx_queue[j]); + &priv->channel[j]); } } @@ -3973,7 +3975,7 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev) sprintf(int_name, "%s:%s-%d", dev->name, "rx", i); ret = request_irq(msi->rx_irq[i], stmmac_msi_intr_rx, - 0, int_name, &priv->dma_conf.rx_queue[i]); + 0, int_name, &priv->channel[i]); if (unlikely(ret < 0)) { netdev_err(priv->dev, "%s: alloc rx-%d MSI %d (error: %d)\n", @@ -3997,7 +3999,7 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev) sprintf(int_name, "%s:%s-%d", dev->name, "tx", i); ret = request_irq(msi->tx_irq[i], stmmac_msi_intr_tx, - 0, int_name, &priv->dma_conf.tx_queue[i]); + 0, int_name, &priv->channel[i]); if (unlikely(ret < 0)) { netdev_err(priv->dev, "%s: alloc tx-%d MSI %d (error: %d)\n", @@ -4121,8 +4123,8 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) /* Chose the tx/rx size from the already defined one in the * priv struct. (if defined) */ - dma_conf->dma_tx_size = priv->dma_conf.dma_tx_size; - dma_conf->dma_rx_size = priv->dma_conf.dma_rx_size; + dma_conf->dma_tx_size = priv->dma_conf->dma_tx_size; + dma_conf->dma_rx_size = priv->dma_conf->dma_rx_size; if (!dma_conf->dma_tx_size) dma_conf->dma_tx_size = DMA_DEFAULT_TX_SIZE; @@ -4136,6 +4138,7 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) /* Setup per-TXQ tbs flag before TX descriptor alloc */ tx_q->tbs |= tbs_en ? STMMAC_TBS_AVAIL : 0; + tx_q->tbs |= priv->dma_conf->tx_queue[chan].tbs & STMMAC_TBS_EN; } ret = alloc_dma_desc_resources(priv, dma_conf); @@ -4178,10 +4181,8 @@ static int __stmmac_open(struct net_device *dev, u8 chan; int ret; - for (int i = 0; i < priv->plat->tx_queues_to_use; i++) - if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_EN) - dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs; - memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf)); + /* Keep RXQ metadata and timers at their registered addresses. */ + priv->dma_conf = dma_conf; /* The PHY is suspended when the interface is reopened without * disconnecting the PHY, e.g. on MTU change. IEEE 802.3 allows PHYs @@ -4222,7 +4223,7 @@ static int __stmmac_open(struct net_device *dev, phylink_stop(priv->phylink); for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) - hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + hrtimer_cancel(&priv->dma_conf->tx_queue[chan].txtimer); stmmac_release_ptp(priv); init_error: @@ -4232,6 +4233,7 @@ static int __stmmac_open(struct net_device *dev, static int stmmac_open(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + struct stmmac_dma_conf *old_conf = priv->dma_conf; struct stmmac_dma_conf *dma_conf; int ret; @@ -4261,7 +4263,7 @@ static int stmmac_open(struct net_device *dev) if (ret) goto err_serdes; - kfree(dma_conf); + kfree(old_conf); /* We may have called phylink_speed_down before */ phylink_speed_up(priv->phylink); @@ -4275,34 +4277,54 @@ static int stmmac_open(struct net_device *dev) err_runtime_pm: pm_runtime_put(priv->device); err_dma_resources: + priv->dma_conf = old_conf; free_dma_desc_resources(priv, dma_conf); kfree(dma_conf); return ret; } -static void __stmmac_release(struct net_device *dev) +static void stmmac_stop_tx_queues(struct stmmac_priv *priv) { - struct stmmac_priv *priv = netdev_priv(dev); u8 chan; - /* Stop and disconnect the PHY */ - phylink_stop(priv->phylink); + netif_tx_disable(priv->dev); - stmmac_disable_all_queues(priv); + /* A poll function can still arm a timer after napi_complete_done(). + * Drain those poll tails and in-flight transmitters before cancelling + * the timers, so none can be rearmed after their final cancellation. + */ + synchronize_net(); for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) - hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + hrtimer_cancel(&priv->dma_conf->tx_queue[chan].txtimer); +} + +/* Quiesce NAPI and transmit queues without releasing their resources. */ +static void stmmac_quiesce(struct stmmac_priv *priv) +{ + stmmac_disable_all_queues(priv); + stmmac_stop_tx_queues(priv); + /* A TX completion may have rearmed this after phylink stopped EEE. */ + timer_delete_sync(&priv->eee_ctrl_timer); +} - netif_tx_disable(dev); +static void __stmmac_release(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + phylink_stop(priv->phylink); + stmmac_quiesce(priv); /* Free the IRQ lines */ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); + /* TX error IRQs can restart a queue after the first quiescence. */ + stmmac_stop_tx_queues(priv); /* Stop TX/RX DMA and clear the descriptors */ stmmac_stop_all_dma(priv); /* Release and free the Rx/Tx resources */ - free_dma_desc_resources(priv, &priv->dma_conf); + free_dma_desc_resources(priv, priv->dma_conf); stmmac_release_ptp(priv); @@ -4356,7 +4378,7 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb, return false; stmmac_set_tx_owner(priv, p); - tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); + tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf->dma_tx_size); return true; } @@ -4376,7 +4398,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry, dma_addr_t des, int total_len, bool last_segment, u32 queue) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; struct dma_desc *desc; u32 buff_size; int tmp_len; @@ -4386,7 +4408,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry, while (tmp_len > 0) { dma_addr_t curr_addr; - *entry = STMMAC_NEXT_ENTRY(*entry, priv->dma_conf.dma_tx_size); + *entry = STMMAC_NEXT_ENTRY(*entry, priv->dma_conf->dma_tx_size); WARN_ON(tx_q->tx_skbuff[*entry]); if (tx_q->tbs & STMMAC_TBS_AVAIL) @@ -4410,7 +4432,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry, static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; /* The own bit must be the latest setting done when prepare the * descriptor and then barrier is needed to make sure that @@ -4563,7 +4585,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) nfrags = skb_shinfo(skb)->nr_frags; queue = skb_get_queue_mapping(skb); - tx_q = &priv->dma_conf.tx_queue[queue]; + tx_q = &priv->dma_conf->tx_queue[queue]; txq_stats = &priv->xstats.txq_stats[queue]; first_tx = tx_q->cur_tx; @@ -4601,7 +4623,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) stmmac_set_mss(priv, mss_desc, mss); tx_q->mss = mss; tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, - priv->dma_conf.dma_tx_size); + priv->dma_conf->dma_tx_size); WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]); } @@ -4672,7 +4694,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) /* Manage tx mitigation */ tx_packets = CIRC_CNT(tx_q->cur_tx + 1, first_tx, - priv->dma_conf.dma_tx_size); + priv->dma_conf->dma_tx_size); tx_q->tx_count_frames += tx_packets; if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en) @@ -4704,7 +4726,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) * ndo_start_xmit will fill this descriptor the next time it's * called and stmmac_tx_clean may clean up to this descriptor. */ - tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); + tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf->dma_tx_size); if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", @@ -4734,7 +4756,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) * segment. */ is_last_segment = CIRC_CNT(tx_q->cur_tx, first_entry, - priv->dma_conf.dma_tx_size) == 1; + priv->dma_conf->dma_tx_size) == 1; /* Complete the first descriptor before granting the DMA */ stmmac_prepare_tso_tx_desc(priv, first, 1, proto_hdr_len, 0, 1, @@ -4772,13 +4794,13 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) for (;;) { desc = stmmac_get_tx_desc(priv, tx_q, first_entry); stmmac_release_tx_desc(priv, desc, priv->descriptor_mode); - stmmac_free_tx_buffer(priv, &priv->dma_conf, queue, + stmmac_free_tx_buffer(priv, priv->dma_conf, queue, first_entry); if (first_entry == entry) break; first_entry = STMMAC_NEXT_ENTRY(first_entry, - priv->dma_conf.dma_tx_size); + priv->dma_conf->dma_tx_size); } error: dev_err(priv->device, "Tx dma map failed\n"); @@ -4863,7 +4885,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } - tx_q = &priv->dma_conf.tx_queue[queue]; + tx_q = &priv->dma_conf->tx_queue[queue]; first_tx = tx_q->cur_tx; /* Check if VLAN can be inserted by HW */ @@ -4939,7 +4961,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) unsigned int frag_size = skb_frag_size(frag); bool last_segment = (i == (nfrags - 1)); - entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size); + entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size); WARN_ON(tx_q->tx_skbuff[entry]); desc = stmmac_get_tx_desc(priv, tx_q, entry); @@ -4969,7 +4991,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) * This approach takes care about the fragments: desc is the first * element in case of no SG. */ - tx_packets = CIRC_CNT(entry + 1, first_tx, priv->dma_conf.dma_tx_size); + tx_packets = CIRC_CNT(entry + 1, first_tx, priv->dma_conf->dma_tx_size); tx_q->tx_count_frames += tx_packets; if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en) @@ -4997,7 +5019,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) * ndo_start_xmit will fill this descriptor the next time it's * called and stmmac_tx_clean may clean up to this descriptor. */ - entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size); + entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size); tx_q->cur_tx = entry; if (netif_msg_pktdata(priv)) { @@ -5112,7 +5134,7 @@ static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb) */ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue]; int dirty = stmmac_rx_dirty(priv, queue); unsigned int entry = rx_q->dirty_rx; gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN); @@ -5163,7 +5185,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) dma_wmb(); stmmac_set_rx_owner(priv, p, use_rx_wd); - entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_rx_size); + entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_rx_size); } rx_q->dirty_rx = entry; stmmac_set_queue_rx_tail_ptr(priv, rx_q, queue, rx_q->dirty_rx); @@ -5191,12 +5213,12 @@ static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv, /* First descriptor, not last descriptor and not split header */ if (status & rx_not_ls) - return priv->dma_conf.dma_buf_sz; + return priv->dma_conf->dma_buf_sz; plen = stmmac_get_rx_frame_len(priv, p, coe); /* First descriptor and last descriptor and not split header */ - return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen); + return min_t(unsigned int, priv->dma_conf->dma_buf_sz, plen); } static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv, @@ -5226,7 +5248,7 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv, /* Not GMAC4 and not last descriptor */ if (priv->plat->core_type != DWMAC_CORE_GMAC4 && (status & rx_not_ls)) - return priv->dma_conf.dma_buf_sz; + return priv->dma_conf->dma_buf_sz; /* GMAC4 or last descriptor */ plen = stmmac_get_rx_frame_len(priv, p, coe); @@ -5238,7 +5260,7 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, struct xdp_frame *xdpf, bool dma_map) { struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue]; - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; bool csum = !priv->plat->tx_queues_cfg[queue].coe_unsupported; unsigned int entry = tx_q->cur_tx; enum stmmac_txbuf_type buf_type; @@ -5304,7 +5326,7 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, stmmac_enable_dma_transmission(priv, priv->ioaddr, queue); - entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size); + entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size); tx_q->cur_tx = entry; return STMMAC_XDP_TX; @@ -5495,7 +5517,7 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue]; unsigned int entry = rx_q->dirty_rx; struct dma_desc *rx_desc = NULL; bool ret = true; @@ -5535,7 +5557,7 @@ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget) dma_wmb(); stmmac_set_rx_owner(priv, rx_desc, use_rx_wd); - entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_rx_size); + entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_rx_size); } if (rx_desc) { @@ -5559,7 +5581,7 @@ static struct stmmac_xdp_buff *xsk_buff_to_stmmac_ctx(struct xdp_buff *xdp) static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) { struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue]; - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue]; unsigned int count = 0, error = 0, len = 0; int dirty = stmmac_rx_dirty(priv, queue); unsigned int next_entry = rx_q->cur_rx; @@ -5576,7 +5598,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) netdev_dbg(priv->dev, "%s: descriptor ring:\n", __func__); desc_size = stmmac_get_rx_desc_size(priv); - stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true, + stmmac_display_ring(priv, rx_head, priv->dma_conf->dma_rx_size, true, rx_q->dma_rx_phy, desc_size); } while (count < limit) { @@ -5620,7 +5642,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) /* Prefetch the next RX descriptor */ next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx, - priv->dma_conf.dma_rx_size); + priv->dma_conf->dma_rx_size); if (unlikely(next_entry == rx_q->dirty_rx)) break; @@ -5745,7 +5767,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) { u32 rx_errors = 0, rx_dropped = 0, rx_bytes = 0, rx_packets = 0; struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue]; - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue]; struct stmmac_channel *ch = &priv->channel[queue]; unsigned int count = 0, error = 0, len = 0; int status = 0, coe = priv->hw->rx_csum; @@ -5759,7 +5781,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) int bufsz; dma_dir = page_pool_get_dma_dir(rx_q->page_pool); - bufsz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE; + bufsz = DIV_ROUND_UP(priv->dma_conf->dma_buf_sz, PAGE_SIZE) * PAGE_SIZE; if (netif_msg_rx_status(priv)) { void *rx_head = stmmac_get_rx_desc(priv, rx_q, 0); @@ -5767,7 +5789,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) netdev_dbg(priv->dev, "%s: descriptor ring:\n", __func__); desc_size = stmmac_get_rx_desc_size(priv); - stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true, + stmmac_display_ring(priv, rx_head, priv->dma_conf->dma_rx_size, true, rx_q->dma_rx_phy, desc_size); } if (in_progress) { @@ -5810,7 +5832,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) break; next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx, - priv->dma_conf.dma_rx_size); + priv->dma_conf->dma_rx_size); if (unlikely(next_entry == rx_q->dirty_rx)) break; @@ -5946,7 +5968,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) buf1_len, dma_dir); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, buf->page, buf->page_offset, buf1_len, - priv->dma_conf.dma_buf_sz); + priv->dma_conf->dma_buf_sz); buf->page = NULL; } @@ -5955,7 +5977,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) buf2_len, dma_dir); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, buf->sec_page, 0, buf2_len, - priv->dma_conf.dma_buf_sz); + priv->dma_conf->dma_buf_sz); buf->sec_page = NULL; } @@ -6180,6 +6202,7 @@ static void stmmac_set_rx_mode(struct net_device *dev) static int stmmac_change_mtu(struct net_device *dev, int new_mtu) { struct stmmac_priv *priv = netdev_priv(dev); + struct stmmac_dma_conf *old_conf = priv->dma_conf; int txfifosz = priv->plat->tx_fifo_size; struct stmmac_dma_conf *dma_conf; const int mtu = new_mtu; @@ -6216,19 +6239,22 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) return PTR_ERR(dma_conf); } + netif_device_detach(dev); __stmmac_release(dev); ret = __stmmac_open(dev, dma_conf); if (ret) { + priv->dma_conf = old_conf; free_dma_desc_resources(priv, dma_conf); kfree(dma_conf); netdev_err(priv->dev, "failed reopening the interface after MTU change\n"); return ret; } - kfree(dma_conf); + kfree(old_conf); stmmac_set_rx_mode(dev); + netif_device_attach(dev); } WRITE_ONCE(dev->mtu, mtu); @@ -6400,15 +6426,11 @@ static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id) static irqreturn_t stmmac_msi_intr_tx(int irq, void *data) { - struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)data; - struct stmmac_dma_conf *dma_conf; - int chan = tx_q->queue_index; - struct stmmac_priv *priv; + struct stmmac_channel *ch = data; + struct stmmac_priv *priv = ch->priv_data; + int chan = ch->index; int status; - dma_conf = container_of(tx_q, struct stmmac_dma_conf, tx_queue[chan]); - priv = container_of(dma_conf, struct stmmac_priv, dma_conf); - /* Check if adapter is up */ if (test_bit(STMMAC_DOWN, &priv->state)) return IRQ_HANDLED; @@ -6427,13 +6449,9 @@ static irqreturn_t stmmac_msi_intr_tx(int irq, void *data) static irqreturn_t stmmac_msi_intr_rx(int irq, void *data) { - struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)data; - struct stmmac_dma_conf *dma_conf; - int chan = rx_q->queue_index; - struct stmmac_priv *priv; - - dma_conf = container_of(rx_q, struct stmmac_dma_conf, rx_queue[chan]); - priv = container_of(dma_conf, struct stmmac_priv, dma_conf); + struct stmmac_channel *ch = data; + struct stmmac_priv *priv = ch->priv_data; + int chan = ch->index; /* Check if adapter is up */ if (test_bit(STMMAC_DOWN, &priv->state)) @@ -6588,45 +6606,48 @@ static int stmmac_rings_status_show(struct seq_file *seq, void *v) { struct net_device *dev = seq->private; struct stmmac_priv *priv = netdev_priv(dev); - u8 rx_count = priv->plat->rx_queues_to_use; - u8 tx_count = priv->plat->tx_queues_to_use; - u8 queue; + u8 rx_count, tx_count, queue; + rtnl_lock(); if ((dev->flags & IFF_UP) == 0) - return 0; + goto out_unlock; + rx_count = priv->plat->rx_queues_to_use; + tx_count = priv->plat->tx_queues_to_use; for (queue = 0; queue < rx_count; queue++) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue]; seq_printf(seq, "RX Queue %d:\n", queue); if (priv->extend_desc) { seq_printf(seq, "Extended descriptor ring:\n"); sysfs_display_ring((void *)rx_q->dma_erx, - priv->dma_conf.dma_rx_size, 1, seq, rx_q->dma_rx_phy); + priv->dma_conf->dma_rx_size, 1, seq, rx_q->dma_rx_phy); } else { seq_printf(seq, "Descriptor ring:\n"); sysfs_display_ring((void *)rx_q->dma_rx, - priv->dma_conf.dma_rx_size, 0, seq, rx_q->dma_rx_phy); + priv->dma_conf->dma_rx_size, 0, seq, rx_q->dma_rx_phy); } } for (queue = 0; queue < tx_count; queue++) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; seq_printf(seq, "TX Queue %d:\n", queue); if (priv->extend_desc) { seq_printf(seq, "Extended descriptor ring:\n"); sysfs_display_ring((void *)tx_q->dma_etx, - priv->dma_conf.dma_tx_size, 1, seq, tx_q->dma_tx_phy); + priv->dma_conf->dma_tx_size, 1, seq, tx_q->dma_tx_phy); } else if (!(tx_q->tbs & STMMAC_TBS_AVAIL)) { seq_printf(seq, "Descriptor ring:\n"); sysfs_display_ring((void *)tx_q->dma_tx, - priv->dma_conf.dma_tx_size, 0, seq, tx_q->dma_tx_phy); + priv->dma_conf->dma_tx_size, 0, seq, tx_q->dma_tx_phy); } } +out_unlock: + rtnl_unlock(); return 0; } DEFINE_SHOW_ATTRIBUTE(stmmac_rings_status); @@ -7038,6 +7059,10 @@ static int stmmac_xdp_xmit(struct net_device *dev, int num_frames, nq = netdev_get_tx_queue(priv->dev, queue); __netif_tx_lock(nq, cpu); + if (unlikely(!netif_device_present(dev) || netif_tx_queue_stopped(nq))) { + __netif_tx_unlock(nq); + return -ENETDOWN; + } /* Avoids TX time-out as we are sharing with slow path */ txq_trans_cond_update(nq); @@ -7071,31 +7096,31 @@ void stmmac_disable_rx_queue(struct stmmac_priv *priv, u32 queue) spin_unlock_irqrestore(&ch->lock, flags); stmmac_stop_rx_dma(priv, queue); - __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue); + __free_dma_rx_desc_resources(priv, priv->dma_conf, queue); } void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue]; struct stmmac_channel *ch = &priv->channel[queue]; unsigned long flags; int ret; - ret = __alloc_dma_rx_desc_resources(priv, &priv->dma_conf, queue); + ret = __alloc_dma_rx_desc_resources(priv, priv->dma_conf, queue); if (ret) { netdev_err(priv->dev, "Failed to alloc RX desc.\n"); return; } - ret = __init_dma_rx_desc_rings(priv, &priv->dma_conf, queue, GFP_KERNEL); + ret = __init_dma_rx_desc_rings(priv, priv->dma_conf, queue, GFP_KERNEL); if (ret) { - __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue); + __free_dma_rx_desc_resources(priv, priv->dma_conf, queue); netdev_err(priv->dev, "Failed to init RX desc.\n"); return; } stmmac_reset_rx_queue(priv, queue); - stmmac_clear_rx_descriptors(priv, &priv->dma_conf, queue); + stmmac_clear_rx_descriptors(priv, priv->dma_conf, queue); stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, rx_q->dma_rx_phy, queue); @@ -7121,31 +7146,31 @@ void stmmac_disable_tx_queue(struct stmmac_priv *priv, u32 queue) spin_unlock_irqrestore(&ch->lock, flags); stmmac_stop_tx_dma(priv, queue); - __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue); + __free_dma_tx_desc_resources(priv, priv->dma_conf, queue); } void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; struct stmmac_channel *ch = &priv->channel[queue]; unsigned long flags; int ret; - ret = __alloc_dma_tx_desc_resources(priv, &priv->dma_conf, queue); + ret = __alloc_dma_tx_desc_resources(priv, priv->dma_conf, queue); if (ret) { netdev_err(priv->dev, "Failed to alloc TX desc.\n"); return; } - ret = __init_dma_tx_desc_rings(priv, &priv->dma_conf, queue); + ret = __init_dma_tx_desc_rings(priv, priv->dma_conf, queue); if (ret) { - __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue); + __free_dma_tx_desc_resources(priv, priv->dma_conf, queue); netdev_err(priv->dev, "Failed to init TX desc.\n"); return; } stmmac_reset_tx_queue(priv, queue); - stmmac_clear_tx_descriptors(priv, &priv->dma_conf, queue); + stmmac_clear_tx_descriptors(priv, priv->dma_conf, queue); stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, tx_q->dma_tx_phy, queue); @@ -7165,25 +7190,18 @@ void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue) void stmmac_xdp_release(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); - u8 chan; - - /* Ensure tx function is not running */ - netif_tx_disable(dev); - /* Disable NAPI process */ - stmmac_disable_all_queues(priv); - - for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) - hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + stmmac_quiesce(priv); /* Free the IRQ lines */ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); + stmmac_stop_tx_queues(priv); /* Stop TX/RX DMA channels */ stmmac_stop_all_dma(priv); /* Release and free the Rx/Tx resources */ - free_dma_desc_resources(priv, &priv->dma_conf); + free_dma_desc_resources(priv, priv->dma_conf); /* Disable the MAC Rx/Tx */ stmmac_mac_set(priv, priv->ioaddr, false); @@ -7207,14 +7225,14 @@ int stmmac_xdp_open(struct net_device *dev) u8 chan; int ret; - ret = alloc_dma_desc_resources(priv, &priv->dma_conf); + ret = alloc_dma_desc_resources(priv, priv->dma_conf); if (ret < 0) { netdev_err(dev, "%s: DMA descriptors allocation failed\n", __func__); goto dma_desc_error; } - ret = init_dma_desc_rings(dev, &priv->dma_conf, GFP_KERNEL); + ret = init_dma_desc_rings(dev, priv->dma_conf, GFP_KERNEL); if (ret < 0) { netdev_err(dev, "%s: DMA descriptors initialization failed\n", __func__); @@ -7234,7 +7252,7 @@ int stmmac_xdp_open(struct net_device *dev) /* DMA RX Channel Configuration */ for (chan = 0; chan < rx_cnt; chan++) { - rx_q = &priv->dma_conf.rx_queue[chan]; + rx_q = &priv->dma_conf->rx_queue[chan]; stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, rx_q->dma_rx_phy, chan); @@ -7249,7 +7267,7 @@ int stmmac_xdp_open(struct net_device *dev) /* DMA TX Channel Configuration */ for (chan = 0; chan < tx_cnt; chan++) { - tx_q = &priv->dma_conf.tx_queue[chan]; + tx_q = &priv->dma_conf->tx_queue[chan]; stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, tx_q->dma_tx_phy, chan); @@ -7279,10 +7297,10 @@ int stmmac_xdp_open(struct net_device *dev) irq_error: for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) - hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + hrtimer_cancel(&priv->dma_conf->tx_queue[chan].txtimer); init_error: - free_dma_desc_resources(priv, &priv->dma_conf); + free_dma_desc_resources(priv, priv->dma_conf); dma_desc_error: return ret; } @@ -7290,8 +7308,6 @@ int stmmac_xdp_open(struct net_device *dev) int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags) { struct stmmac_priv *priv = netdev_priv(dev); - struct stmmac_rx_queue *rx_q; - struct stmmac_tx_queue *tx_q; struct stmmac_channel *ch; if (test_bit(STMMAC_DOWN, &priv->state) || @@ -7305,11 +7321,9 @@ int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags) queue >= priv->plat->tx_queues_to_use) return -EINVAL; - rx_q = &priv->dma_conf.rx_queue[queue]; - tx_q = &priv->dma_conf.tx_queue[queue]; ch = &priv->channel[queue]; - if (!rx_q->xsk_pool && !tx_q->xsk_pool) + if (!test_bit(queue, priv->af_xdp_zc_qps)) return -EINVAL; if (!napi_if_scheduled_mark_missed(&ch->rxtx_napi)) { @@ -7696,8 +7710,8 @@ int stmmac_reinit_ringparam(struct net_device *dev, u32 rx_size, u32 tx_size) if (netif_running(dev)) stmmac_release(dev); - priv->dma_conf.dma_rx_size = rx_size; - priv->dma_conf.dma_tx_size = tx_size; + priv->dma_conf->dma_rx_size = rx_size; + priv->dma_conf->dma_tx_size = tx_size; if (netif_running(dev)) ret = stmmac_open(dev); @@ -7872,6 +7886,13 @@ struct plat_stmmacenet_data *stmmac_plat_dat_alloc(struct device *dev) } EXPORT_SYMBOL_GPL(stmmac_plat_dat_alloc); +static void stmmac_free_dma_conf(void *data) +{ + struct stmmac_priv *priv = data; + + kfree(priv->dma_conf); +} + static int __stmmac_dvr_probe(struct device *device, struct plat_stmmacenet_data *plat_dat, struct stmmac_resources *res) @@ -7896,6 +7917,13 @@ static int __stmmac_dvr_probe(struct device *device, priv = netdev_priv(ndev); priv->device = device; priv->dev = ndev; + /* Keep ring sizes and per-queue settings even while the device is down. */ + priv->dma_conf = kzalloc_obj(*priv->dma_conf); + if (!priv->dma_conf) + return -ENOMEM; + ret = devm_add_action_or_reset(device, stmmac_free_dma_conf, priv); + if (ret) + return ret; for (i = 0; i < MTL_MAX_RX_QUEUES; i++) u64_stats_init(&priv->xstats.rxq_stats[i].napi_syncp); @@ -8258,7 +8286,6 @@ int stmmac_suspend(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct stmmac_priv *priv = netdev_priv(ndev); - u8 chan; if (!ndev || !netif_running(ndev)) goto suspend_bsp; @@ -8267,10 +8294,7 @@ int stmmac_suspend(struct device *dev) netif_device_detach(ndev); - stmmac_disable_all_queues(priv); - - for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) - hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + stmmac_quiesce(priv); if (priv->eee_sw_timer_en) { priv->tx_path_in_lpi_mode = false; @@ -8310,7 +8334,7 @@ EXPORT_SYMBOL_GPL(stmmac_suspend); static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue]; rx_q->cur_rx = 0; rx_q->dirty_rx = 0; @@ -8318,7 +8342,7 @@ static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue) static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; tx_q->cur_tx = 0; tx_q->dirty_tx = 0; @@ -8401,7 +8425,7 @@ int stmmac_resume(struct device *dev) stmmac_reset_queues_param(priv); stmmac_free_tx_skbufs(priv); - stmmac_clear_descriptors(priv, &priv->dma_conf); + stmmac_clear_descriptors(priv, priv->dma_conf); ret = stmmac_hw_setup(ndev); if (ret < 0) { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c index c25dc9f89270..2511339e55e4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c @@ -887,8 +887,8 @@ static int stmmac_test_flowctrl(struct stmmac_priv *priv) struct stmmac_channel *ch = &priv->channel[i]; u32 tail; - tail = priv->dma_conf.rx_queue[i].dma_rx_phy + - (priv->dma_conf.dma_rx_size * sizeof(struct dma_desc)); + tail = priv->dma_conf->rx_queue[i].dma_rx_phy + + (priv->dma_conf->dma_rx_size * sizeof(struct dma_desc)); stmmac_set_rx_tail_ptr(priv, priv->ioaddr, tail, i); stmmac_start_rx(priv, priv->ioaddr, i); @@ -1787,7 +1787,7 @@ static int stmmac_test_arpoffload(struct stmmac_priv *priv) static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue) { struct stmmac_packet_attrs attr = { }; - int size = priv->dma_conf.dma_buf_sz; + int size = priv->dma_conf->dma_buf_sz; if (!dwmac_is_xmac(priv->plat->core_type)) size -= NET_IP_ALIGN; @@ -1873,7 +1873,7 @@ static int stmmac_test_tbs(struct stmmac_priv *priv) /* Find first TBS enabled Queue, if any */ for (i = 0; i < priv->plat->tx_queues_to_use; i++) - if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_AVAIL) + if (priv->dma_conf->tx_queue[i].tbs & STMMAC_TBS_AVAIL) break; if (i >= priv->plat->tx_queues_to_use) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 42a00446e9b4..bb1dfe2702ac 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -1197,13 +1197,13 @@ static int tc_setup_etf(struct stmmac_priv *priv, return -EOPNOTSUPP; if (qopt->queue >= priv->plat->tx_queues_to_use) return -EINVAL; - if (!(priv->dma_conf.tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL)) + if (!(priv->dma_conf->tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL)) return -EINVAL; if (qopt->enable) - priv->dma_conf.tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN; + priv->dma_conf->tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN; else - priv->dma_conf.tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN; + priv->dma_conf->tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN; netdev_info(priv->dev, "%s ETF for Queue %d\n", qopt->enable ? "enabled" : "disabled", qopt->queue); From patchwork Sat Sep 26 15:49:04 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3475 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sin.lore.kernel.org (sin.lore.kernel.org [104.64.211.4]) by mxe881.netcup.net (Postfix) with ESMTPS id B2BAF1C2AA6 for ; Sat, 26 Sep 2026 17:52:46 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 104.64.211.4) smtp.mailfrom=linux-sunxi+bounces-26275-noreply=patchwork.local@lists.linux.dev smtp.helo=sin.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 104.64.211.4 as permitted sender) client-ip=104.64.211.4; envelope-from=linux-sunxi+bounces-26275-noreply=patchwork.local@lists.linux.dev; helo=sin.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sin.lore.kernel.org (Postfix) with ESMTP id 9E10E264BE for ; Sat, 26 Sep 2026 15:50:39 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A9B83380FCA; Sat, 26 Sep 2026 15:49:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="e9RZR4vg" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f39.google.com (mail-oo2-f39.google.com [74.125.231.167]) (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 C98BC3793D0 for ; Sat, 26 Sep 2026 15:49:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.167 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437787; cv=none; b=e6O/gteNCNBnGnbZlSVnv0JW0Rz7IgS9KNKTN+kmE4GUXUZiaEZdj0qsY50/5V6mBId5MVFOdCuV3ofZrP5IAi27nvLJkbvsonXhtPdULMuZEVDQNdfBfDKZ0Pdzr0m3fG0/OXgdRpR3jDaxENAt4mAUDj8hOBfNcTb2S7k1Rkw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437787; c=relaxed/simple; bh=QYZgFUIPbVdL1Or1COkJk/8mNxr76FljLzr30hkvvGg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VQniHJT51pekLwAljvlrck+demv3PBI/zSldBFMxVSMFRcKYb1mSJbvCO1RE2Ot2OlESsnPXGOZSjveDHh47UiXDObDBMXqbNdXSe5quCQqQzw9xDd2I6EbbUtPuW1tJNAcEmXcAolOEmt45SNoA0KnnsWgJG9PJR3KYAcqNhGQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=e9RZR4vg; arc=none smtp.client-ip=74.125.231.167 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f39.google.com with SMTP id 006d021491bc7-6c24d19ebcfso1022216eaf.3 for ; Sat, 26 Sep 2026 08:49:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437779; x=1791042579; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=vPb0nEPWL3asml1fL9I1iOUw0gKSkYXR2hPi85apTC4=; b=e9RZR4vg+hF/z4XFjfAzdYFzw87nufrolx9zYLvHNJuFoockLDfo+KTRoK/eqk0Ij5 vSz084BuqQoCCsWG3YKoyysh/btngggVfnhuj7MVb8mDSSOwqd99HJ9JPsl4/k+NizND p5zk2X33MtLSod2i0cqNc8W0F2ojCTSglzTNjLrShn4aGvptT+K2I6UpEoWZ1z/wdQEu Nd4N/OP8nz+xuLxEaSYn0aiyGh+wNphlCcMeujgzhSiIYWDKWD7um6lAAeyQIFUpsrAE X9SF8PEpnWT8fjenRFmTp7hIkzjGjivOzBcHAxP8PA7f+fBgujBx/17auT0gE826auAp HQ3A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437779; x=1791042579; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=vPb0nEPWL3asml1fL9I1iOUw0gKSkYXR2hPi85apTC4=; b=KJIAfZr1m6ygaSShF83ZoCL0Bt0ZjwTIk8WjmvyNNjTrtqZkeMTz54LgMhGGdrvMtR EwGs1X24byIxH71v2puYib4uzsSgavHi0CWKOUx0B4C6r2YhWhKS1yq9TIQQT8l6IPV/ x42wIZerVgAjQZ2nUmbMhSg0lJysJ0+va1E94hYOC5+7sfcJKMRZqGiZGLMY60RB4k1V Ij0vJgixeH8tcvYaU+mx0fmi7L1DtUzBOxalGwETTz07hxaQFLeOyll2Dqy7qHgKYY5E tH+933i5u/VbPgacgR1g+qYlQRANNX7uShMtNTYKpeeV7wZ5XgRl9m8IPfLve9tFPvNu EVrw== X-Forwarded-Encrypted: i=1; AKwUvByG1JUoFJJ39CMz9DboTSIP+uExGDYZOTcURcgop/tsOuFf38om8AEo4P90jJ5Rme07jIzSqs+W96uPbQ==@lists.linux.dev X-Gm-Message-State: AFuF++nfS04e2mx05FfF4uxkfkB9cg+mHE2EuDOCIwnuZspvkuYqg3Gf 0ZcpmbZhuageqbPLAD1e870v9oRPXauxGyXX++MXrc9IUQnqM+Q7Mc0B99/ETyTI X-Gm-Gg: AYBFou1q+7ETN7xNI3HLCMzYwrJ3OI0GRtW1JHGb8h9EZfGgAE7KG21VbB7c5DTcPU6 ped+t3mQDoNKdMUZS6SHTbM7oZflGlzb+An8xWYdTUu3ddNHP+0xd3vc7Q1MjgxECHikK2anuU4 3n6lQ7PZQv2V1ATKoSvY3QcawyYHoFKZPGgBb1Mu+9EdoXW6NvRjEn1+C7P4gkL5gGG209WD9Mu EAZXMsPkujhG0CJmeqYLi6YjhJEB7vRy//a+f5zF9iVB5EIZ7LW5yrJsW3IRVFcSsT//0yLa5FH RuCcEo+SUYKNnuHSt+V2pHqNydAoPE6EqvG73OJPiBPWHeyQ5ZYUrr6/GrtVOhrndSsKwkfHx43 zJcf5xWkzGCxht1jmmFl0voOY75gZhVMiQzs/5Sn6W8FbZkt2lsoxsofKdYZQspLDs+QectYkmt mrNPjiU+nYITN40tz6aJ/TfVSaOom2yqYBvYHjyW/HA5PFSbTrGoGQvFnnU7/xkMAP4ViqNZelk ZgV9m7xY4wyj18u1sINkh+K3mdZNBrobn6PAdSh5OzLlswzHHFPA+cR5aW87mWKsgMY9OrYfmq6 EYkbNMve6LymO3MJDJ5NX95MyaQa81S7ftBmdRiHm4d9y1f0lvAOxfTEA12hBn4nlLjw7ZrI0oF 2VWUEv4AlW79DgYDLSZ+X6QV6nrKQyV4= X-Received: by 2002:a05:6820:1c87:b0:6b1:705d:b29e with SMTP id 006d021491bc7-6d43f7a5e78mr8696086eaf.26.1790437778541; Sat, 26 Sep 2026 08:49:38 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:37 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:49:04 -0600 Subject: [PATCH net v4 09/15] net: stmmac: track datapath and power ownership across failed reopening Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-9-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Track allocated rings, IRQs and NAPI independently of IFF_UP and of system sleep. Leave failed live reopening detached while the successful ndo_open still owns its PHY attachment and runtime-PM reference; ordinary down/up can recover without duplicate NAPI shutdown or PM puts. Convert every live reopen consumer together: MTU, XDP program changes and ethtool ring/channel changes. Restore old settings on error and publish queue counts only after setup succeeds. XDP removal must release its program even if rebuilding the non-XDP datapath fails. Use phylink link replay for successful XDP rebuilding so the PHY is not stopped or renegotiated. Keep core sleep cleanup separate from platform and clock restoration. Serialize noirq clock ownership and retry pending power transitions before accessing MMIO. If restoration fails, block MAC, MDIO, PCS, IRQ and PHC register accesses and finish only software cleanup. Check MAC WoL viability before skipping an already completed core sleep sequence. Close must reattach the netdev so ndo_open can retry power restoration. Reject ethtool, timestamp, MAC-address, feature and qdisc installation requests while registers remain inaccessible. Drain receive-filter work under the address lock before power removal. VLAN and qdisc teardown still clear their software state without touching unpowered registers. Permit flower and u32 destruction while down or detached, clearing cached rules without disabling already-stopped NAPI or accessing registers. TC forgets deleted filters even when the driver returns an error, so rejecting destruction would leave stale rules to be replayed on recovery. Keep installation-only feature and RSS checks out of the destruction path. Guard non-netdev-detach consumers, including TC, descriptor readback, XDP transmission and reset work. Freeze deferred XSK teardown before sleep through the prerequisite XSK change. Preserve PHY ownership on failed ethtool reopen and stop a PHY temporarily resumed to supply the MAC reset clock. Fixes: 3470079687448 ("net: ethernet: stmicro: stmmac: permit MTU change with interface up") Fixes: 6896c2449a18 ("net: stmmac: Check stmmac_hw_setup() in stmmac_resume()") Fixes: ac746c8520d9 ("net: stmmac: enhance XDP ZC driver level switching performance") Fixes: aa042f60e496 ("net: stmmac: Add support to Ethtool get/set ring parameters") Fixes: 0366f7e06a6b ("net: stmmac: add ethtool support for get/set channels") Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c | 5 +- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 29 ++ .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 11 + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 557 ++++++++++++++++++--- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 15 + drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c | 3 +- .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 38 +- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 96 ++-- drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c | 6 +- drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c | 39 +- 10 files changed, 640 insertions(+), 159 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c index 4ede4420c93b..64a55c72c15c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c @@ -105,8 +105,9 @@ static int __maybe_unused tegra_mgbe_resume(struct device *dev) } err = stmmac_resume(dev); - if (err < 0) - clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks); + /* Core resume failure retains the suspended datapath for retry or + * close. Keep its register interface powered until that cleanup. + */ return err; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 3365ed237953..2fa7b18eb9a4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -258,6 +258,15 @@ struct stmmac_msi { char int_name_tx_irq[MTL_MAX_TX_QUEUES][IFNAMSIZ + 18]; }; +enum stmmac_datapath_state { + /* No IRQs or DMA allocations owned by a successful open. */ + STMMAC_DATAPATH_DOWN, + /* Resources allocated, NAPI enabled. */ + STMMAC_DATAPATH_RUNNING, + /* Resources retained, NAPI and DMA stopped; also after failed resume. */ + STMMAC_DATAPATH_SUSPENDED, +}; + struct stmmac_priv { /* Frequently used values are kept adjacent for cache effect */ u32 tx_coal_frames[MTL_MAX_TX_QUEUES]; @@ -281,6 +290,18 @@ struct stmmac_priv { struct mutex lock; struct stmmac_dma_conf *dma_conf; + /* IRQ/DMA ownership and NAPI state, serialized by RTNL. */ + enum stmmac_datapath_state datapath; + /* Core sleep sequence completed, independently of datapath ownership. */ + bool hw_suspended; + /* System PM blocks MMIO until power restoration has completed. */ + bool hw_unavailable; + bool bsp_suspended; + bool bus_clks_suspended; + bool ptp_clock_enabled; + bool ptp_clock_suspended; + /* Clock ownership can also change in noirq PM, without RTNL. */ + struct mutex pm_mutex; /* Generic channel for NAPI */ struct stmmac_channel channel[STMMAC_CH_MAX]; @@ -402,6 +423,7 @@ extern const struct dev_pm_ops stmmac_simple_pm_ops; int stmmac_mdio_unregister(struct net_device *ndev); int stmmac_mdio_register(struct net_device *ndev); int stmmac_mdio_reset(struct mii_bus *mii); +int stmmac_resume_clocks(struct stmmac_priv *priv); void stmmac_mdio_lock(struct stmmac_priv *priv); void stmmac_mdio_unlock(struct stmmac_priv *priv); int stmmac_pcs_setup(struct net_device *ndev); @@ -432,6 +454,13 @@ static inline bool stmmac_xdp_is_enabled(struct stmmac_priv *priv) return !!priv->xdp_prog; } +/* RTNL serializes TC callbacks with datapath and power transitions. */ +static inline bool stmmac_tc_active(struct stmmac_priv *priv) +{ + return priv->datapath == STMMAC_DATAPATH_RUNNING && + netif_device_present(priv->dev) && !priv->hw_unavailable; +} + void stmmac_disable_rx_queue(struct stmmac_priv *priv, u32 queue); void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue); void stmmac_disable_tx_queue(struct stmmac_priv *priv, u32 queue); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 994350fcdbfa..6742401671ff 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -1090,7 +1090,18 @@ static void stmmac_get_mm_stats(struct net_device *ndev, s->MACMergeHoldCount = mmc->mmc_tx_hold_req_cntr; } +static int stmmac_ethtool_begin(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + /* Close reattaches the netdev so open can retry power restoration. + * Presence alone does not make registers accessible after failed resume. + */ + return priv->hw_unavailable ? -EHOSTDOWN : 0; +} + static const struct ethtool_ops stmmac_ethtool_ops = { + .begin = stmmac_ethtool_begin, .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES, .get_drvinfo = stmmac_ethtool_getdrvinfo, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index c4ba97d57b47..22a65c1c1681 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -653,6 +653,9 @@ static int stmmac_hwtstamp_set(struct net_device *dev, u32 ts_master_en = 0; u32 ts_event_en = 0; + if (priv->hw_unavailable) + return -EHOSTDOWN; + if (!(priv->dma_cap.time_stamp || priv->adv_ts)) { NL_SET_ERR_MSG_MOD(extack, "No support for HW time stamping"); priv->hwts_tx_en = 0; @@ -946,12 +949,16 @@ static void stmmac_setup_ptp(struct stmmac_priv *priv) priv->ptp_extts = 0; priv->ptp_blocked = false; + mutex_lock(&priv->pm_mutex); ret = clk_prepare_enable(priv->plat->clk_ptp_ref); - if (ret < 0) + priv->ptp_clock_enabled = !ret; + mutex_unlock(&priv->pm_mutex); + if (ret < 0) { netdev_warn(priv->dev, "failed to enable PTP reference clock: %pe\n", ERR_PTR(ret)); - + return; + } if (stmmac_init_timestamping(priv) == 0) stmmac_ptp_register(priv); } @@ -959,7 +966,26 @@ static void stmmac_setup_ptp(struct stmmac_priv *priv) static void stmmac_release_ptp(struct stmmac_priv *priv) { stmmac_ptp_unregister(priv); - clk_disable_unprepare(priv->plat->clk_ptp_ref); + mutex_lock(&priv->pm_mutex); + if (priv->ptp_clock_enabled) { + clk_disable_unprepare(priv->plat->clk_ptp_ref); + priv->ptp_clock_enabled = false; + } + /* A later noirq resume must not reacquire a released reference. */ + priv->ptp_clock_suspended = false; + mutex_unlock(&priv->pm_mutex); +} + +/* ptp_mutex excludes configuration and crosstimestamp operations. The + * spinlock also excludes atomic clock reads while changing this gate. + */ +static void stmmac_block_ptp(struct stmmac_priv *priv, bool block) +{ + unsigned long flags; + + write_lock_irqsave(&priv->ptp_lock, flags); + WRITE_ONCE(priv->ptp_blocked, block); + write_unlock_irqrestore(&priv->ptp_lock, flags); } static void stmmac_legacy_serdes_power_down(struct stmmac_priv *priv) @@ -1064,6 +1090,9 @@ static void stmmac_mac_link_down(struct phylink_config *config, { struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); + if (READ_ONCE(priv->hw_unavailable)) + return; + stmmac_mac_set(priv, priv->ioaddr, false); if (priv->dma_cap.eee) stmmac_set_eee_pls(priv, priv->hw, false); @@ -1188,10 +1217,12 @@ static void stmmac_mac_disable_tx_lpi(struct phylink_config *config) netdev_dbg(priv->dev, "disable EEE\n"); priv->eee_sw_timer_en = false; timer_delete_sync(&priv->eee_ctrl_timer); - stmmac_set_lpi_mode(priv, priv->hw, STMMAC_LPI_DISABLE, false, 0); priv->tx_path_in_lpi_mode = false; - stmmac_set_eee_timer(priv, priv->hw, 0, STMMAC_DEFAULT_TWT_LS); + if (!READ_ONCE(priv->hw_unavailable)) { + stmmac_set_lpi_mode(priv, priv->hw, STMMAC_LPI_DISABLE, false, 0); + stmmac_set_eee_timer(priv, priv->hw, 0, STMMAC_DEFAULT_TWT_LS); + } mutex_unlock(&priv->lock); } @@ -3781,10 +3812,6 @@ static int stmmac_hw_setup(struct net_device *dev) stmmac_enable_tbs(priv, priv->ioaddr, enable, chan); } - /* Configure real RX and TX queues */ - netif_set_real_num_rx_queues(dev, priv->plat->rx_queues_to_use); - netif_set_real_num_tx_queues(dev, priv->plat->tx_queues_to_use); - /* Start the ball rolling... */ stmmac_start_all_dma(priv); @@ -4086,6 +4113,32 @@ static int stmmac_request_irq(struct net_device *dev) return ret; } +/* Drain registered handlers without disabling lines shared by other devices. */ +static void stmmac_synchronize_irq(struct stmmac_priv *priv) +{ + struct stmmac_msi *msi = priv->msi; + int irq = priv->dev->irq; + u32 i; + + synchronize_irq(irq); + if (priv->wol_irq > 0 && priv->wol_irq != irq) + synchronize_irq(priv->wol_irq); + if (priv->sfty_irq > 0 && priv->sfty_irq != irq) + synchronize_irq(priv->sfty_irq); + if (!msi) + return; + if (msi->sfty_ce_irq > 0 && msi->sfty_ce_irq != irq) + synchronize_irq(msi->sfty_ce_irq); + if (msi->sfty_ue_irq > 0 && msi->sfty_ue_irq != irq) + synchronize_irq(msi->sfty_ue_irq); + for (i = 0; i < priv->plat->rx_queues_to_use; i++) + if (msi->rx_irq[i] > 0) + synchronize_irq(msi->rx_irq[i]); + for (i = 0; i < priv->plat->tx_queues_to_use; i++) + if (msi->tx_irq[i] > 0) + synchronize_irq(msi->tx_irq[i]); +} + /** * stmmac_setup_dma_desc - Generate a dma_conf and allocate DMA queue * @priv: driver private structure @@ -4164,6 +4217,89 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) return ERR_PTR(ret); } +/* The freezer excludes pool teardown and userspace from noirq callbacks. + * Outside system sleep these flags are changed under RTNL. + */ +int stmmac_resume_clocks(struct stmmac_priv *priv) +{ + int ret = 0; + + mutex_lock(&priv->pm_mutex); + if (priv->bus_clks_suspended) { + ret = pm_runtime_force_resume(priv->device); + if (ret) + goto out; + priv->bus_clks_suspended = false; + } + if (priv->ptp_clock_suspended) { + ret = clk_prepare_enable(priv->plat->clk_ptp_ref); + if (ret) + goto out; + priv->ptp_clock_enabled = true; + priv->ptp_clock_suspended = false; + } +out: + mutex_unlock(&priv->pm_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(stmmac_resume_clocks); + +static int stmmac_resume_power(struct stmmac_priv *priv, bool system_resume) +{ + bool pending = priv->bus_clks_suspended || priv->bsp_suspended || + priv->ptp_clock_suspended; + int ret; + + ret = stmmac_resume_clocks(priv); + if (ret) + return ret; + if (priv->bsp_suspended && priv->plat->resume) { + ret = priv->plat->resume(priv->device, priv->plat->bsp_priv); + if (ret) + return ret; + priv->bsp_suspended = false; + } + /* A wrapper which powers the device outside these callbacks must + * complete its own resume before core register access is possible. + */ + if (system_resume || pending) + WRITE_ONCE(priv->hw_unavailable, false); + return priv->hw_unavailable ? -EHOSTDOWN : 0; +} + +/* Finish core sleep state only after its power dependencies are restored. */ +static int stmmac_resume_hw(struct stmmac_priv *priv) +{ + int ret; + + if (!priv->hw_suspended) + return 0; + if (priv->hw_unavailable) + return -EHOSTDOWN; + + /* Use the state installed by suspend, not a subsequently changed WoL + * setting. Clear PMT even when a different device caused the wakeup. + */ + if (priv->irq_wake) { + mutex_lock(&priv->lock); + stmmac_pmt(priv, priv->hw, 0); + mutex_unlock(&priv->lock); + priv->irq_wake = 0; + } else { + ret = pinctrl_pm_select_default_state(priv->device); + if (ret) + return ret; + if (priv->mii) { + ret = stmmac_mdio_reset(priv->mii); + if (ret) + return ret; + } + } + priv->hw_suspended = false; + + return 0; +} + /** * __stmmac_open - open entry point of the driver * @dev : pointer to the device structure. @@ -4205,28 +4341,42 @@ static int __stmmac_open(struct net_device *dev, stmmac_init_coalesce(priv); - phylink_start(priv->phylink); - stmmac_vlan_restore(priv); ret = stmmac_request_irq(dev); if (ret) goto irq_error; + /* Publish the topology only when no other fallible setup remains. + * The combined setter restores the old counts if an increase fails. + */ + ret = netif_set_real_num_queues(dev, priv->plat->tx_queues_to_use, + priv->plat->rx_queues_to_use); + if (ret) { + stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); + goto irq_error; + } + stmmac_enable_all_queues(priv); netif_tx_start_all_queues(priv->dev); stmmac_enable_all_dma_irq(priv); + priv->datapath = STMMAC_DATAPATH_RUNNING; + phylink_start(priv->phylink); return 0; irq_error: - phylink_stop(priv->phylink); - for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) hrtimer_cancel(&priv->dma_conf->tx_queue[chan].txtimer); stmmac_release_ptp(priv); init_error: + /* Undo phylink_prepare_resume() even if hardware setup failed before + * phylink_start(). Keep the PHY attachment and outer PM ownership. + */ + phylink_stop(priv->phylink); + stmmac_stop_all_dma(priv); + stmmac_mac_set(priv, priv->ioaddr, false); return ret; } @@ -4249,6 +4399,13 @@ static int stmmac_open(struct net_device *dev) if (ret < 0) goto err_dma_resources; + ret = stmmac_resume_power(priv, false); + if (ret) + goto err_runtime_pm; + ret = stmmac_resume_hw(priv); + if (ret) + goto err_runtime_pm; + ret = stmmac_init_phy(dev); if (ret) goto err_runtime_pm; @@ -4312,23 +4469,36 @@ static void __stmmac_release(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + /* A failed MTU reopen has already released the data path. */ + if (priv->datapath == STMMAC_DATAPATH_DOWN) + return; + phylink_stop(priv->phylink); - stmmac_quiesce(priv); + + /* Suspend retains the resources, but has already stopped activity. */ + if (priv->datapath == STMMAC_DATAPATH_RUNNING) + stmmac_quiesce(priv); + priv->datapath = STMMAC_DATAPATH_DOWN; /* Free the IRQ lines */ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); + /* TX error IRQs can restart a queue after the first quiescence. */ stmmac_stop_tx_queues(priv); - /* Stop TX/RX DMA and clear the descriptors */ - stmmac_stop_all_dma(priv); + /* Stop TX/RX DMA after draining IRQ handlers which can restart it. */ + if (!priv->hw_unavailable) { + stmmac_stop_all_dma(priv); + /* Link resolution need not have reached mac_link_up() yet. */ + stmmac_mac_set(priv, priv->ioaddr, false); + } /* Release and free the Rx/Tx resources */ free_dma_desc_resources(priv, priv->dma_conf); stmmac_release_ptp(priv); - if (stmmac_fpe_supported(priv)) + if (!priv->hw_unavailable && stmmac_fpe_supported(priv)) ethtool_mmsv_stop(&priv->fpe_cfg.mmsv); } @@ -4341,6 +4511,17 @@ static void __stmmac_release(struct net_device *dev) static int stmmac_release(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + int ret; + + /* Resume may have failed before restoring pins or disabling MAC wake. + * Complete that cleanup without restarting the link or the datapath. + * If it fails, keep hw_suspended set so a fresh open can retry it. + */ + ret = stmmac_resume_power(priv, false); + if (!ret) + ret = stmmac_resume_hw(priv); + if (ret) + netdev_err(dev, "failed to restore hardware sleep state: %d\n", ret); /* If the PHY or MAC has WoL enabled, then the PHY will not be * suspended when phylink_stop() is called below. Set the PHY @@ -4354,6 +4535,8 @@ static int stmmac_release(struct net_device *dev) stmmac_legacy_serdes_power_down(priv); phylink_disconnect_phy(priv->phylink); pm_runtime_put(priv->device); + /* Allow a fresh open after a failed MTU reopen or resume. */ + netif_device_attach(dev); return 0; } @@ -6185,6 +6368,9 @@ static void stmmac_set_rx_mode(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + if (READ_ONCE(priv->hw_unavailable)) + return; + stmmac_set_filter(priv, priv->hw, dev); } @@ -6247,6 +6433,11 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) priv->dma_conf = old_conf; free_dma_desc_resources(priv, dma_conf); kfree(dma_conf); + /* + * Keep the administrative state and PHY/PM ownership until + * ndo_stop(), but prevent use of the released data path. + */ + netif_device_detach(dev); netdev_err(priv->dev, "failed reopening the interface after MTU change\n"); return ret; } @@ -6290,6 +6481,9 @@ static int stmmac_set_features(struct net_device *netdev, { struct stmmac_priv *priv = netdev_priv(netdev); + if (priv->hw_unavailable) + return -EHOSTDOWN; + /* Keep the COE Type in case of csum is supporting */ if (features & NETIF_F_RXCSUM) priv->hw->rx_csum = priv->plat->rx_coe; @@ -6377,6 +6571,12 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) struct net_device *dev = (struct net_device *)dev_id; struct stmmac_priv *priv = netdev_priv(dev); + if (READ_ONCE(priv->hw_unavailable)) { + if (priv->irq_wake) + pm_wakeup_event(priv->device, 0); + return IRQ_NONE; + } + /* Check if adapter is up */ if (test_bit(STMMAC_DOWN, &priv->state)) return IRQ_HANDLED; @@ -6399,6 +6599,12 @@ static irqreturn_t stmmac_mac_interrupt(int irq, void *dev_id) struct net_device *dev = (struct net_device *)dev_id; struct stmmac_priv *priv = netdev_priv(dev); + if (READ_ONCE(priv->hw_unavailable)) { + if (priv->irq_wake) + pm_wakeup_event(priv->device, 0); + return IRQ_NONE; + } + /* Check if adapter is up */ if (test_bit(STMMAC_DOWN, &priv->state)) return IRQ_HANDLED; @@ -6414,6 +6620,9 @@ static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id) struct net_device *dev = (struct net_device *)dev_id; struct stmmac_priv *priv = netdev_priv(dev); + if (READ_ONCE(priv->hw_unavailable)) + return IRQ_NONE; + /* Check if adapter is up */ if (test_bit(STMMAC_DOWN, &priv->state)) return IRQ_HANDLED; @@ -6431,6 +6640,9 @@ static irqreturn_t stmmac_msi_intr_tx(int irq, void *data) int chan = ch->index; int status; + if (READ_ONCE(priv->hw_unavailable)) + return IRQ_NONE; + /* Check if adapter is up */ if (test_bit(STMMAC_DOWN, &priv->state)) return IRQ_HANDLED; @@ -6453,6 +6665,9 @@ static irqreturn_t stmmac_msi_intr_rx(int irq, void *data) struct stmmac_priv *priv = ch->priv_data; int chan = ch->index; + if (READ_ONCE(priv->hw_unavailable)) + return IRQ_NONE; + /* Check if adapter is up */ if (test_bit(STMMAC_DOWN, &priv->state)) return IRQ_HANDLED; @@ -6484,13 +6699,40 @@ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static int stmmac_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) { + struct flow_cls_common_offload *common = type_data; struct stmmac_priv *priv = cb_priv; + bool active = stmmac_tc_active(priv); int ret = -EOPNOTSUPP; + bool destroy; + + switch (type) { + case TC_SETUP_CLSU32: + destroy = ((struct tc_cls_u32_offload *)type_data)->command == + TC_CLSU32_DELETE_KNODE; + break; + case TC_SETUP_CLSFLOWER: + destroy = ((struct flow_cls_offload *)type_data)->command == + FLOW_CLS_DESTROY; + break; + default: + return ret; + } - if (!tc_cls_can_offload_and_chain0(priv->dev, type_data)) + if (common->chain_index) { + NL_SET_ERR_MSG(common->extack, "Driver supports only offload of chain 0"); + return ret; + } + if (!destroy && !tc_can_offload_extack(priv->dev, common->extack)) return ret; + if (!destroy && !active) + return -ENETDOWN; - __stmmac_disable_all_queues(priv); + /* TC discards deleted filters regardless of the callback result. Clear + * their cached state even while down or detached, without touching NAPI + * or inaccessible registers. Recovery must not replay deleted rules. + */ + if (active) + __stmmac_disable_all_queues(priv); switch (type) { case TC_SETUP_CLSU32: @@ -6503,7 +6745,8 @@ static int stmmac_setup_tc_block_cb(enum tc_setup_type type, void *type_data, break; } - stmmac_enable_all_queues(priv); + if (active) + stmmac_enable_all_queues(priv); return ret; } @@ -6558,6 +6801,9 @@ static int stmmac_set_mac_address(struct net_device *ndev, void *addr) struct stmmac_priv *priv = netdev_priv(ndev); int ret = 0; + if (priv->hw_unavailable) + return -EHOSTDOWN; + ret = pm_runtime_resume_and_get(priv->device); if (ret < 0) return ret; @@ -6609,8 +6855,9 @@ static int stmmac_rings_status_show(struct seq_file *seq, void *v) u8 rx_count, tx_count, queue; rtnl_lock(); - if ((dev->flags & IFF_UP) == 0) + if (priv->datapath == STMMAC_DATAPATH_DOWN) goto out_unlock; + rx_count = priv->plat->rx_queues_to_use; tx_count = priv->plat->tx_queues_to_use; @@ -6924,7 +7171,7 @@ static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double) hash = 0; } - if (!netif_running(priv->dev)) + if (!netif_running(priv->dev) || priv->hw_unavailable) return 0; return stmmac_update_vlan_hash(priv, priv->hw, hash, pmatch, is_double); @@ -6940,6 +7187,9 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid bool is_double = false; int ret; + if (priv->hw_unavailable) + return -EHOSTDOWN; + ret = pm_runtime_resume_and_get(priv->device); if (ret < 0) return ret; @@ -6982,9 +7232,12 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi bool is_double = false; int ret; - ret = pm_runtime_resume_and_get(priv->device); - if (ret < 0) - return ret; + /* Removal must update the cached filters even if power cannot return. */ + if (!priv->hw_unavailable) { + ret = pm_runtime_resume_and_get(priv->device); + if (ret < 0) + return ret; + } if (be16_to_cpu(proto) == ETH_P_8021AD) is_double = true; @@ -7009,7 +7262,8 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi priv->num_double_vlans = num_double_vlans; del_vlan_error: - pm_runtime_put(priv->device); + if (!priv->hw_unavailable) + pm_runtime_put(priv->device); return ret; } @@ -7029,6 +7283,18 @@ static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf) { struct stmmac_priv *priv = netdev_priv(dev); + if (bpf->command != XDP_SETUP_PROG && + bpf->command != XDP_SETUP_XSK_POOL) + return -EOPNOTSUPP; + + /* + * Pool removal must succeed even after a failed resume. Release the + * suspended rings before their pool or XDP buffer layout can change. + * Leave the interface detached until it is closed and reopened. + */ + if (priv->datapath == STMMAC_DATAPATH_SUSPENDED) + __stmmac_release(dev); + switch (bpf->command) { case XDP_SETUP_PROG: return stmmac_xdp_set_prog(priv, bpf->prog, bpf->extack); @@ -7191,7 +7457,13 @@ void stmmac_xdp_release(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + netif_device_detach(dev); + /* Pause MAC/PCS resolution, but keep the PHY and negotiation running. + * stmmac_xdp_open() completes this replay under the same RTNL lock. + */ + phylink_replay_link_begin(priv->phylink); stmmac_quiesce(priv); + priv->datapath = STMMAC_DATAPATH_DOWN; /* Free the IRQ lines */ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); @@ -7210,7 +7482,13 @@ void stmmac_xdp_release(struct net_device *dev) * watchdogs during reset */ netif_trans_update(dev); - netif_carrier_off(dev); + + if (stmmac_fpe_supported(priv)) + ethtool_mmsv_stop(&priv->fpe_cfg.mmsv); + + /* Keep PTP across the immediately following stmmac_xdp_open(). That + * function releases it if reopening fails, before returning DOWN. + */ } int stmmac_xdp_open(struct net_device *dev) @@ -7289,19 +7567,25 @@ int stmmac_xdp_open(struct net_device *dev) /* Enable NAPI process*/ stmmac_enable_all_queues(priv); - netif_carrier_on(dev); - netif_tx_start_all_queues(dev); stmmac_enable_all_dma_irq(priv); + priv->datapath = STMMAC_DATAPATH_RUNNING; + phylink_replay_link_end(priv->phylink); + netif_device_attach(dev); return 0; irq_error: - for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) - hrtimer_cancel(&priv->dma_conf->tx_queue[chan].txtimer); + stmmac_stop_tx_queues(priv); + stmmac_stop_all_dma(priv); + stmmac_mac_set(priv, priv->ioaddr, false); init_error: free_dma_desc_resources(priv, priv->dma_conf); dma_desc_error: + /* STOPPED keeps replay_end() from reconfiguring a failed MAC. */ + phylink_stop(priv->phylink); + phylink_replay_link_end(priv->phylink); + stmmac_release_ptp(priv); return ret; } @@ -7424,6 +7708,9 @@ static void stmmac_reset_subtask(struct stmmac_priv *priv) netdev_err(priv->dev, "Reset adapter.\n"); rtnl_lock(); + if (!netif_device_present(priv->dev)) + goto out_unlock; + netif_trans_update(priv->dev); while (test_and_set_bit(STMMAC_RESETING, &priv->state)) usleep_range(1000, 2000); @@ -7433,6 +7720,7 @@ static void stmmac_reset_subtask(struct stmmac_priv *priv) dev_open(priv->dev, NULL); clear_bit(STMMAC_DOWN, &priv->state); clear_bit(STMMAC_RESETING, &priv->state); +out_unlock: rtnl_unlock(); } @@ -7677,13 +7965,37 @@ static void stmmac_napi_del(struct net_device *dev) } } -int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt) +/* Rebuild only the datapath. The administratively-up device still owns its + * PHY attachment and runtime-PM reference, even if this reopen fails. + */ +static int stmmac_reopen(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); - int ret = 0, i; + struct stmmac_dma_conf *old_conf = priv->dma_conf; + struct stmmac_dma_conf *dma_conf; + int ret; - if (netif_running(dev)) - stmmac_release(dev); + dma_conf = stmmac_setup_dma_desc(priv, dev->mtu); + if (IS_ERR(dma_conf)) + return PTR_ERR(dma_conf); + + ret = __stmmac_open(dev, dma_conf); + if (ret) { + priv->dma_conf = old_conf; + free_dma_desc_resources(priv, dma_conf); + kfree(dma_conf); + return ret; + } + + kfree(old_conf); + netif_device_attach(dev); + return 0; +} + +static void stmmac_set_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt) +{ + struct stmmac_priv *priv = netdev_priv(dev); + int i; stmmac_napi_del(dev); @@ -7695,9 +8007,31 @@ int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt) rx_cnt); stmmac_napi_add(dev); +} + +int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt) +{ + struct stmmac_priv *priv = netdev_priv(dev); + u8 old_rx = priv->plat->rx_queues_to_use; + u8 old_tx = priv->plat->tx_queues_to_use; + int ret = 0; + + if (netif_running(dev)) { + if (!netif_device_present(dev)) + return -ENETDOWN; + netif_device_detach(dev); + __stmmac_release(dev); + } + + stmmac_set_queues(dev, rx_cnt, tx_cnt); if (netif_running(dev)) - ret = stmmac_open(dev); + ret = stmmac_reopen(dev); + if (ret) { + stmmac_set_queues(dev, old_rx, old_tx); + netdev_err(dev, "failed reopening after channel change: %pe; interface remains detached\n", + ERR_PTR(ret)); + } return ret; } @@ -7705,16 +8039,28 @@ int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt) int stmmac_reinit_ringparam(struct net_device *dev, u32 rx_size, u32 tx_size) { struct stmmac_priv *priv = netdev_priv(dev); + u32 old_rx = priv->dma_conf->dma_rx_size; + u32 old_tx = priv->dma_conf->dma_tx_size; int ret = 0; - if (netif_running(dev)) - stmmac_release(dev); + if (netif_running(dev)) { + if (!netif_device_present(dev)) + return -ENETDOWN; + netif_device_detach(dev); + __stmmac_release(dev); + } priv->dma_conf->dma_rx_size = rx_size; priv->dma_conf->dma_tx_size = tx_size; if (netif_running(dev)) - ret = stmmac_open(dev); + ret = stmmac_reopen(dev); + if (ret) { + priv->dma_conf->dma_rx_size = old_rx; + priv->dma_conf->dma_tx_size = old_tx; + netdev_err(dev, "failed reopening after ring change: %pe; interface remains detached\n", + ERR_PTR(ret)); + } return ret; } @@ -8131,6 +8477,7 @@ static int __stmmac_dvr_probe(struct device *device, stmmac_napi_add(ndev); mutex_init(&priv->lock); + mutex_init(&priv->pm_mutex); rwlock_init(&priv->ptp_lock); mutex_init(&priv->ptp_mutex); @@ -8278,30 +8625,43 @@ EXPORT_SYMBOL_GPL(stmmac_dvr_remove); /** * stmmac_suspend - suspend callback * @dev: device pointer - * Description: this is the function to suspend the device and it is called - * by the platform driver to stop the network queue, release the resources, - * program the PMT register (for WoL), clean and release driver resources. + * Description: stop network activity and program hardware for system sleep, + * preserving any datapath resources still owned for resume or close. */ int stmmac_suspend(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct stmmac_priv *priv = netdev_priv(ndev); + bool accessible; + int ret = 0; - if (!ndev || !netif_running(ndev)) + rtnl_lock(); + if (!netif_running(ndev)) + goto suspend_bsp; + + /* A failed datapath cannot provide a working MAC wake path. It may + * even have released its wake IRQ. Do not silently suspend without WoL. + */ + if (priv->wolopts && priv->datapath != STMMAC_DATAPATH_RUNNING) { + netdev_err(ndev, "cannot suspend failed datapath with MAC WoL enabled\n"); + rtnl_unlock(); + return -EBUSY; + } + if (priv->hw_suspended) goto suspend_bsp; mutex_lock(&priv->lock); netif_device_detach(ndev); - stmmac_quiesce(priv); + if (priv->datapath == STMMAC_DATAPATH_RUNNING) + stmmac_quiesce(priv); if (priv->eee_sw_timer_en) { priv->tx_path_in_lpi_mode = false; timer_delete_sync(&priv->eee_ctrl_timer); } - /* Stop TX/RX DMA */ stmmac_stop_all_dma(priv); stmmac_legacy_serdes_power_down(priv); @@ -8317,18 +8677,41 @@ int stmmac_suspend(struct device *dev) mutex_unlock(&priv->lock); - rtnl_lock(); phylink_suspend(priv->phylink, !!priv->wolopts); - rtnl_unlock(); + if (priv->datapath == STMMAC_DATAPATH_RUNNING) + priv->datapath = STMMAC_DATAPATH_SUSPENDED; + priv->hw_suspended = true; if (stmmac_fpe_supported(priv)) ethtool_mmsv_stop(&priv->fpe_cfg.mmsv); suspend_bsp: - if (priv->plat->suspend) - return priv->plat->suspend(dev, priv->plat->bsp_priv); + accessible = !priv->hw_unavailable; + mutex_lock(&priv->ptp_mutex); + stmmac_block_ptp(priv, true); + mutex_unlock(&priv->ptp_mutex); + /* Drain MDIO transactions before the platform can remove power. */ + if (priv->mii) + mutex_lock(&priv->mii->mdio_lock); + /* ndo_set_rx_mode() can run without RTNL under the address lock. */ + netif_addr_lock_bh(ndev); + WRITE_ONCE(priv->hw_unavailable, true); + netif_addr_unlock_bh(ndev); + if (priv->mii) + mutex_unlock(&priv->mii->mdio_lock); + if (priv->datapath == STMMAC_DATAPATH_SUSPENDED) { + stmmac_synchronize_irq(priv); + stmmac_stop_tx_queues(priv); + if (accessible) + stmmac_stop_all_dma(priv); + } + if (priv->plat->suspend && !priv->bsp_suspended) { + priv->bsp_suspended = true; + ret = priv->plat->suspend(dev, priv->plat->bsp_priv); + } + rtnl_unlock(); - return 0; + return ret; } EXPORT_SYMBOL_GPL(stmmac_suspend); @@ -8380,41 +8763,39 @@ int stmmac_resume(struct device *dev) struct stmmac_priv *priv = netdev_priv(ndev); int ret; - if (priv->plat->resume) { - ret = priv->plat->resume(dev, priv->plat->bsp_priv); - if (ret) - return ret; + rtnl_lock(); + ret = stmmac_resume_power(priv, true); + if (ret) + goto out_unlock; + + if (!netif_running(ndev)) { + ret = 0; + goto out_unlock; } - if (!netif_running(ndev)) - return 0; + if (priv->hw_suspended) { + ret = stmmac_resume_hw(priv); + if (ret) + goto out_unlock; - /* Power Down bit, into the PM register, is cleared - * automatically as soon as a magic packet or a Wake-up frame - * is received. Anyway, it's better to manually clear - * this bit because it can generate problems while resuming - * from another devices (e.g. serial console). - */ - if (priv->wolopts) { - mutex_lock(&priv->lock); - stmmac_pmt(priv, priv->hw, 0); - mutex_unlock(&priv->lock); - priv->irq_wake = 0; - } else { - pinctrl_pm_select_default_state(priv->device); - /* reset the phy so that it's ready */ - if (priv->mii) - stmmac_mdio_reset(priv->mii); + /* Terminate PM speed control without restarting a datapath + * whose IRQs or rings were released before system sleep. + */ + if (priv->datapath != STMMAC_DATAPATH_SUSPENDED) + phylink_stop(priv->phylink); + } + + if (priv->datapath != STMMAC_DATAPATH_SUSPENDED) { + ret = 0; + goto out_unlock; } if (!(priv->plat->flags & STMMAC_FLAG_SERDES_UP_AFTER_PHY_LINKUP)) { ret = stmmac_legacy_serdes_power_up(priv); if (ret < 0) - return ret; + goto out_unlock; } - rtnl_lock(); - /* Prepare the PHY to resume, ensuring that its clocks which are * necessary for the MAC DMA reset to complete are running */ @@ -8430,13 +8811,13 @@ int stmmac_resume(struct device *dev) ret = stmmac_hw_setup(ndev); if (ret < 0) { netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); - stmmac_legacy_serdes_power_down(priv); - mutex_unlock(&priv->lock); - rtnl_unlock(); - return ret; + goto error_stop_dma; } stmmac_init_timestamping(priv); + mutex_lock(&priv->ptp_mutex); + stmmac_block_ptp(priv, false); + mutex_unlock(&priv->ptp_mutex); stmmac_init_coalesce(priv); phylink_rx_clk_stop_block(priv->phylink); @@ -8455,11 +8836,25 @@ int stmmac_resume(struct device *dev) * workqueue thread, which will race with initialisation. */ phylink_resume(priv->phylink); - rtnl_unlock(); - + priv->datapath = STMMAC_DATAPATH_RUNNING; netif_device_attach(ndev); + rtnl_unlock(); return 0; + +error_stop_dma: + stmmac_stop_all_dma(priv); + stmmac_mac_set(priv, priv->ioaddr, false); + stmmac_legacy_serdes_power_down(priv); + mutex_unlock(&priv->lock); + /* + * Keep the suspended data path detached. A later resume may retry, or + * ndo_stop() can release its resources without disabling NAPI again. + */ +out_unlock: + rtnl_unlock(); + + return ret; } EXPORT_SYMBOL_GPL(stmmac_resume); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 07d5eab05996..136f1f74af57 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -135,6 +135,9 @@ static int stmmac_xgmac2_mdio_read_c22(struct mii_bus *bus, int phyaddr, struct stmmac_priv *priv = netdev_priv(bus->priv); u32 addr; + if (READ_ONCE(priv->hw_unavailable)) + return -EHOSTDOWN; + /* Until ver 2.20 XGMAC does not support C22 addr >= 4 */ if (priv->synopsys_id < DWXGMAC_CORE_2_20 && phyaddr > MII_XGMAC_MAX_C22ADDR) @@ -151,6 +154,9 @@ static int stmmac_xgmac2_mdio_read_c45(struct mii_bus *bus, int phyaddr, struct stmmac_priv *priv = netdev_priv(bus->priv); u32 addr; + if (READ_ONCE(priv->hw_unavailable)) + return -EHOSTDOWN; + stmmac_xgmac2_c45_format(priv, phyaddr, devad, phyreg, &addr); return stmmac_xgmac2_mdio_read(priv, addr, MII_XGMAC_BUSY); @@ -198,6 +204,9 @@ static int stmmac_xgmac2_mdio_write_c22(struct mii_bus *bus, int phyaddr, struct stmmac_priv *priv = netdev_priv(bus->priv); u32 addr; + if (READ_ONCE(priv->hw_unavailable)) + return -EHOSTDOWN; + /* Until ver 2.20 XGMAC does not support C22 addr >= 4 */ if (priv->synopsys_id < DWXGMAC_CORE_2_20 && phyaddr > MII_XGMAC_MAX_C22ADDR) @@ -215,6 +224,9 @@ static int stmmac_xgmac2_mdio_write_c45(struct mii_bus *bus, int phyaddr, struct stmmac_priv *priv = netdev_priv(bus->priv); u32 addr; + if (READ_ONCE(priv->hw_unavailable)) + return -EHOSTDOWN; + stmmac_xgmac2_c45_format(priv, phyaddr, devad, phyreg, &addr); return stmmac_xgmac2_mdio_write(priv, addr, MII_XGMAC_BUSY, @@ -248,6 +260,9 @@ static int stmmac_mdio_access(struct stmmac_priv *priv, unsigned int pa, u32 addr; int ret; + if (READ_ONCE(priv->hw_unavailable)) + return -EHOSTDOWN; + ret = pm_runtime_resume_and_get(priv->device); if (ret < 0) return ret; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c index df37af5ab837..7de83fb8f06f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c @@ -60,7 +60,8 @@ static void dwmac_integrated_pcs_disable(struct phylink_pcs *pcs) { struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs); - stmmac_mac_irq_modify(spcs->priv, spcs->int_mask, 0); + if (!READ_ONCE(spcs->priv->hw_unavailable)) + stmmac_mac_irq_modify(spcs->priv, spcs->int_mask, 0); } static void dwmac_integrated_pcs_get_state(struct phylink_pcs *pcs, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 6128ed1bd521..0d914d69379e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -957,14 +957,23 @@ static int __maybe_unused stmmac_pltfr_noirq_suspend(struct device *dev) if (!netif_running(ndev)) return 0; - if (!priv->wolopts) { - /* Disable clock in case of PWM is off */ - clk_disable_unprepare(priv->plat->clk_ptp_ref); + mutex_lock(&priv->pm_mutex); + if (!priv->irq_wake) { + /* A detached datapath may already have released its PTP clock. */ + if (priv->ptp_clock_enabled) { + clk_disable_unprepare(priv->plat->clk_ptp_ref); + priv->ptp_clock_enabled = false; + priv->ptp_clock_suspended = true; + } + priv->bus_clks_suspended = true; ret = pm_runtime_force_suspend(dev); - if (ret) + if (ret) { + mutex_unlock(&priv->pm_mutex); return ret; + } } + mutex_unlock(&priv->pm_mutex); return 0; } @@ -973,27 +982,8 @@ static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct stmmac_priv *priv = netdev_priv(ndev); - int ret; - - if (!netif_running(ndev)) - return 0; - if (!priv->wolopts) { - /* enable the clk previously disabled */ - ret = pm_runtime_force_resume(dev); - if (ret) - return ret; - - ret = clk_prepare_enable(priv->plat->clk_ptp_ref); - if (ret < 0) { - netdev_warn(priv->dev, - "failed to enable PTP reference clock: %pe\n", - ERR_PTR(ret)); - return ret; - } - } - - return 0; + return stmmac_resume_clocks(priv); } const struct dev_pm_ops stmmac_pltfr_pm_ops = { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index bb1dfe2702ac..9ded65750e89 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -11,6 +11,17 @@ #include "dwmac5.h" #include "stmmac.h" +static int tc_config_preemption(struct stmmac_priv *priv, + struct netlink_ext_ack *extack, u32 preemptible_tcs) +{ + /* Qdisc teardown must not access unpowered registers. */ + if (priv->hw_unavailable) + return 0; + + return stmmac_fpe_map_preemption_class(priv, priv->dev, extack, + preemptible_tcs); +} + static void tc_fill_all_pass_entry(struct stmmac_tc_entry *entry) { memset(entry, 0, sizeof(*entry)); @@ -171,18 +182,16 @@ static int tc_fill_entry(struct stmmac_priv *priv, static void tc_unfill_entry(struct stmmac_priv *priv, struct tc_cls_u32_offload *cls) { - struct stmmac_tc_entry *entry; + struct stmmac_tc_entry *entry, *frag; entry = tc_find_entry(priv, cls, false); if (!entry) return; - entry->in_use = false; - if (entry->frag_ptr) { - entry = entry->frag_ptr; - entry->is_frag = false; - entry->in_use = false; - } + frag = entry->frag_ptr; + if (frag) + memset(frag, 0, sizeof(*frag)); + memset(entry, 0, sizeof(*entry)); } static int tc_config_knode(struct stmmac_priv *priv, @@ -212,6 +221,9 @@ static int tc_delete_knode(struct stmmac_priv *priv, /* Set entry and fragments as not used */ tc_unfill_entry(priv, cls); + if (!stmmac_tc_active(priv)) + return 0; + return stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries, priv->tc_entries_max); } @@ -345,6 +357,8 @@ static int tc_setup_cbs(struct stmmac_priv *priv, return -EINVAL; if (!priv->dma_cap.av) return -EOPNOTSUPP; + if (priv->hw_unavailable && qopt->enable) + return -EHOSTDOWN; port_transmit_rate_kbps = qopt->idleslope - qopt->sendslope; @@ -380,10 +394,12 @@ static int tc_setup_cbs(struct stmmac_priv *priv, priv->plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_AVB; } else if (!qopt->enable) { - ret = stmmac_dma_qmode(priv, priv->ioaddr, queue, - MTL_QUEUE_DCB); - if (ret) - return ret; + if (!priv->hw_unavailable) { + ret = stmmac_dma_qmode(priv, priv->ioaddr, queue, + MTL_QUEUE_DCB); + if (ret) + return ret; + } priv->plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB; return 0; @@ -645,23 +661,21 @@ static int tc_del_flow(struct stmmac_priv *priv, struct flow_cls_offload *cls) { struct stmmac_flow_entry *entry = tc_find_flow(priv, cls, false); - int ret; + int ret = 0; if (!entry || !entry->in_use) return -ENOENT; - if (entry->is_l4) { - ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, false, - false, false, false, 0); - } else { - ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, false, - false, false, false, 0); + if (stmmac_tc_active(priv)) { + if (entry->is_l4) + ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, + false, false, false, false, 0); + else + ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, + false, false, false, false, 0); } - entry->in_use = false; - entry->cookie = 0; - entry->is_l4 = false; - entry->action = 0; + *entry = (struct stmmac_flow_entry) { .idx = entry->idx }; return ret; } @@ -744,7 +758,8 @@ static int tc_del_vlan_flow(struct stmmac_priv *priv, if (!entry || !entry->in_use || entry->type != STMMAC_RFS_T_VLAN) return -ENOENT; - stmmac_rx_queue_prio(priv, priv->hw, 0, entry->tc); + if (stmmac_tc_active(priv)) + stmmac_rx_queue_prio(priv, priv->hw, 0, entry->tc); entry->in_use = false; entry->cookie = 0; @@ -840,13 +855,13 @@ static int tc_del_ethtype_flow(struct stmmac_priv *priv, switch (entry->etype) { case ETH_P_LLDP: - stmmac_rx_queue_routing(priv, priv->hw, - PACKET_DCBCPQ, 0); + if (stmmac_tc_active(priv)) + stmmac_rx_queue_routing(priv, priv->hw, PACKET_DCBCPQ, 0); priv->rfs_entries_cnt[STMMAC_RFS_T_LLDP]--; break; case ETH_P_1588: - stmmac_rx_queue_routing(priv, priv->hw, - PACKET_PTPQ, 0); + if (stmmac_tc_active(priv)) + stmmac_rx_queue_routing(priv, priv->hw, PACKET_PTPQ, 0); priv->rfs_entries_cnt[STMMAC_RFS_T_1588]--; break; default: @@ -901,12 +916,12 @@ static int tc_setup_cls(struct stmmac_priv *priv, { int ret = 0; - /* When RSS is enabled, the filtering will be bypassed */ - if (priv->rss.enable) - return -EBUSY; - switch (cls->command) { case FLOW_CLS_REPLACE: + /* When RSS is enabled, the filtering will be bypassed. */ + if (priv->rss.enable) + return -EBUSY; + ret = tc_add_flow_cls(priv, cls); break; case FLOW_CLS_DESTROY: @@ -1015,6 +1030,8 @@ static int tc_taprio_configure(struct stmmac_priv *priv, if (qopt->cmd == TAPRIO_CMD_DESTROY) goto disable; + if (priv->hw_unavailable) + return -EHOSTDOWN; if (qopt->num_entries > dep) return -EINVAL; @@ -1098,8 +1115,7 @@ static int tc_taprio_configure(struct stmmac_priv *priv, goto disable; } - ret = stmmac_fpe_map_preemption_class(priv, priv->dev, extack, - qopt->mqprio.preemptible_tcs); + ret = tc_config_preemption(priv, extack, qopt->mqprio.preemptible_tcs); if (ret) goto disable; @@ -1109,8 +1125,9 @@ static int tc_taprio_configure(struct stmmac_priv *priv, if (priv->est) { mutex_lock(&priv->est_lock); priv->est->enable = false; - stmmac_est_configure(priv, priv, priv->est, - priv->plat->clk_ptp_rate); + if (!priv->hw_unavailable) + stmmac_est_configure(priv, priv, priv->est, + priv->plat->clk_ptp_rate); /* Reset taprio status */ for (i = 0; i < priv->plat->tx_queues_to_use; i++) { priv->xstats.max_sdu_txq_drop[i] = 0; @@ -1120,7 +1137,7 @@ static int tc_taprio_configure(struct stmmac_priv *priv, mutex_unlock(&priv->est_lock); } - err = stmmac_fpe_map_preemption_class(priv, priv->dev, extack, 0); + err = tc_config_preemption(priv, extack, 0); return qopt->cmd == TAPRIO_CMD_DESTROY ? err : ret; } @@ -1269,7 +1286,7 @@ static int stmmac_reset_tc_mqprio(struct net_device *ndev, netdev_reset_tc(ndev); netif_set_real_num_tx_queues(ndev, priv->plat->tx_queues_to_use); - return stmmac_fpe_map_preemption_class(priv, ndev, extack, 0); + return tc_config_preemption(priv, extack, 0); } static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv, @@ -1286,6 +1303,8 @@ static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv, if (!qopt->num_tc) return stmmac_reset_tc_mqprio(ndev, extack); + if (priv->hw_unavailable) + return -EHOSTDOWN; if (qopt->num_tc > ARRAY_SIZE(tc_to_txq)) return -EINVAL; @@ -1315,8 +1334,7 @@ static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv, if (err) goto error_reset_tc; - err = stmmac_fpe_map_preemption_class(priv, ndev, extack, - mqprio->preemptible_tcs); + err = tc_config_preemption(priv, extack, mqprio->preemptible_tcs); if (err) goto error_reset_num_tx_queues; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c index e24efe3bfedb..006882c14a8d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c @@ -114,6 +114,8 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev, struct mac_device_info *hw, __be16 proto, u16 vid) { + struct stmmac_priv *priv = netdev_priv(dev); + bool update_hw = netif_running(dev) && !priv->hw_unavailable; int i, ret = 0; /* Single Rx VLAN Filter */ @@ -121,7 +123,7 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev, if ((hw->vlan_filter[0] & VLAN_TAG_VID) == vid) { hw->vlan_filter[0] = 0; - if (netif_running(dev)) + if (update_hw) vlan_write_single(dev, 0); } return 0; @@ -132,7 +134,7 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev, if ((hw->vlan_filter[i] & VLAN_TAG_DATA_VEN) && ((hw->vlan_filter[i] & VLAN_TAG_DATA_VID) == vid)) { - if (netif_running(dev)) { + if (update_hw) { ret = vlan_write_filter(dev, hw, i, 0); if (ret) return ret; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c index d7e4db7224b0..7ecb7addd2ea 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c @@ -31,7 +31,8 @@ static int stmmac_xdp_enable_pool(struct stmmac_priv *priv, return err; } - need_update = netif_running(priv->dev) && stmmac_xdp_is_enabled(priv); + need_update = priv->datapath == STMMAC_DATAPATH_RUNNING && + stmmac_xdp_is_enabled(priv); if (need_update) { napi_disable(&ch->rx_napi); @@ -69,7 +70,8 @@ static int stmmac_xdp_disable_pool(struct stmmac_priv *priv, u16 queue) if (!pool) return -EINVAL; - need_update = netif_running(priv->dev) && stmmac_xdp_is_enabled(priv); + need_update = priv->datapath == STMMAC_DATAPATH_RUNNING && + stmmac_xdp_is_enabled(priv); if (need_update) { napi_disable(&ch->rxtx_napi); @@ -106,8 +108,9 @@ int stmmac_xdp_set_prog(struct stmmac_priv *priv, struct bpf_prog *prog, struct bpf_prog *old_prog; bool need_update; bool if_running; + int ret; - if_running = netif_running(dev); + if_running = priv->datapath == STMMAC_DATAPATH_RUNNING; if (prog && dev->mtu > ETH_DATA_LEN) { /* For now, the driver doesn't support XDP functionality with @@ -117,25 +120,41 @@ int stmmac_xdp_set_prog(struct stmmac_priv *priv, struct bpf_prog *prog, return -EOPNOTSUPP; } - if (!prog) - xdp_features_clear_redirect_target(dev); - need_update = !!priv->xdp_prog != !!prog; if (if_running && need_update) stmmac_xdp_release(dev); old_prog = xchg(&priv->xdp_prog, prog); - if (old_prog) - bpf_prog_put(old_prog); /* Disable RX SPH for XDP operation */ priv->sph_active = priv->sph_capable && !stmmac_xdp_is_enabled(priv); - if (if_running && need_update) - stmmac_xdp_open(dev); + if (if_running && need_update) { + ret = stmmac_xdp_open(dev); + if (ret) { + netdev_err(dev, "failed reopening after XDP change: %pe; interface remains detached\n", + ERR_PTR(ret)); + if (prog) { + /* The core retains the old program on error and drops + * the reference it passed for the proposed program. + */ + xchg(&priv->xdp_prog, old_prog); + priv->sph_active = priv->sph_capable && !old_prog; + return ret; + } + /* Uninstalling a BPF link must release its program even + * if the non-XDP datapath cannot be restarted. + */ + } + } + + if (old_prog) + bpf_prog_put(old_prog); if (prog) xdp_features_set_redirect_target(dev, false); + else + xdp_features_clear_redirect_target(dev); return 0; } From patchwork Sat Sep 26 15:49:05 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3481 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 6E4E01C2AA6 for ; Sat, 26 Sep 2026 17:56:20 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26274-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26274-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id CDEA1366D0 for ; Sat, 26 Sep 2026 15:50:29 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 075B637F732; Sat, 26 Sep 2026 15:49:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="QgTAszd9" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f43.google.com (mail-oo2-f43.google.com [74.125.231.171]) (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 696AD37CD53 for ; Sat, 26 Sep 2026 15:49:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437786; cv=none; b=Lp9VGRy6KNJ/8TzukTrx3DSJFpak0FWFQzIncOwawbf2Igu2eC+sv3kJRU62dpyxtlPyspHK5oRRwOxROw17eWh+z16wqAhfQIXBOP3X9OzT8YHkv/aK9FGnHqUfX0HpringiJhuyq8+DqYlGzxVddPtTeicUSZex3EiDisa8I8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437786; c=relaxed/simple; bh=7Ua19WHK3jdthCgj9xvgyVjQKuCb0g5nlx2k3CtuFFg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=mBid0M/1XU1JqMV1VaQU68obV4xLGhQi3ZHonIF0H+cp9gKZQv018dS4P7uFmrS0ft0xAKS+/mgWcj62Z/LuxM8/oQKWMAm1qD4jFZ8ro/6bg9mMFvkxbXuKulTKAPVicDUJZfSgpNT3RaWNNUFM3FU4klUAwaVDIGK2pxPdJQ0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=QgTAszd9; arc=none smtp.client-ip=74.125.231.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f43.google.com with SMTP id 46e09a7af769-81b36a8d5b5so120142a34.3 for ; Sat, 26 Sep 2026 08:49:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437780; x=1791042580; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=CDcUYAqS9g6d7QBEC66SaiDLKySPzBTN2KggUWEIauA=; b=QgTAszd9En0YhWYicy55dwZgsHPt/kgw6OTZOuKpGwPJ6zPdU7hoAODFe46mt49ou/ ES62LgPnxvnFGwUhFSjANJIR4zPwy49YhrCTdL4PWS1Y35RgP7W5Lciq+gysUUDyHRL0 LD9E6d1oTLczJASeN6iU19QrZpn8l4h7vnJ0JbDoYx+Tk2IfV5tiP//VsL3Q+ot3fUu6 yZs/AdWoH+i0TTo24oQdW2NPoFukv+pxzcpNhYwcpV8+FgaTWWh4d4rdlT6K62fvUGZP xuTxc1qKd+c5RXRi8Bc5BjrZX0BefcJcg9LOlYhlCqoDa9ha7LIn7nqYvx45OG7gXm2u E9xg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437780; x=1791042580; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=CDcUYAqS9g6d7QBEC66SaiDLKySPzBTN2KggUWEIauA=; b=vS9+e+qdlBv04P6sSgeOKUCjhDwQLLqSaVqjfB/0TnwAh1vs8rsCT7vjuxu2bv0pAj v5JL5CxYZ92mJBB1VCw/TYVHHhT1eYa6pGtBjhPVXDtvsb4r8PLhAsoQ1WXugWWH51A2 1fNqxmUuPByoLF+kpatga7x1qcCWamChjOfuKLMiebSOYlyZM1OOljrTaXRul+61Vz2A PWvfqeq+XVZdIIstDEPNS768VrJXZt/REk0PFvanLvNBS5GOTCEgXzjjtTtyX/OZypU/ CeIE7cQks6S/OnUSwnB/zPaWQe37hmanSL5L0plOTxJL2nIihDXCpoFSN4xiogNxnkdR Nc9w== X-Forwarded-Encrypted: i=1; AKwUvBwFhihkANjZWiHLeI1oKumDFGuQYjTJFetDP5u7oNdBqQpViQs1Ya4gClp/TlV3mHvh0fhnJvvdVooH+Q==@lists.linux.dev X-Gm-Message-State: AFuF++nlf9gGGyYKa+CdbfHByr1P3HFc7eK7WItZCPdkiNFFTC3F11fZ fivwVw9oSMdhv+A3lP8SQK8runnE6+GA+1uS4wGZywyWl58CC9lSR023 X-Gm-Gg: AYBFou3uBHnWlV2gd1jm5Gt4bKqVGppKumMCBRfWPKn4kicA/uIMIy5hNK9DU8b24Vl eN7Qh4R1Hy8OQrIhQueNEYdFCbJAeVqwMtppZ5pYwjNPkUximpbbPjLnSIEONggRuGCrn9AG7kn TI5I7hcn1RmiT9amGIuHcljMAWjwD77pSp5P5QUY2L8+vmRG90JSgdPDNdBDXDg+aWojuFM98UV S9KM178sFMJuqiIoNrL8A4sKpDLI/rSFEFUb+RXVXh9g+glSctjiZxIR6ks79WLv6lKCcSFhGxh y4GH+66j6lehurjHo8oX3awl4ICDo9U32vAvC/YkVEqu36zU/D1VzNwBP9FmDxap8oSnPYl1QX0 GgFDLDIFybPfq+PwsaJ/O2CjfzPMRrJSwzJYXeG7VPGXIyckrATUqJJnMR73RjJrEJGQcfXhdPb zSO/525YyI96tZ/s/f2m6oxUnF8ocxqBxcsaSOcEYFd1zdlpiEOjGctMfVoHEVci7GYr+cH9pNQ A1UHMjuUW3qjFT3XGckjblFYJxC75+nKx87Vb8gccbV4xjCoIypAY1QP5M9Rk+ZO1Vc1horLx/u 9tyQ3FLbV6F9von7wx5fLjt3Cu4byx8l1+KmqIB4to+h1mCuqtFmi6fUGWf4u6/uA4kS0qckSrp IulqoKo5rOETMkC96vtLAlu9coHia0SY= X-Received: by 2002:a05:6830:4129:b0:816:9a55:4776 with SMTP id 46e09a7af769-81915268e1fmr6113951a34.8.1790437780448; Sat, 26 Sep 2026 08:49:40 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:40 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:49:05 -0600 Subject: [PATCH net v4 10/15] net: stmmac: use the tracked datapath restart for XSK pool changes Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-10-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Replace the void per-queue enable helpers with the tracked XDP restart. Pause all queues and MAC link resolution while the pool bitmap and rings change, leaving the PHY running through phylink replay. Unwind a failed pool attachment without leaving NAPI over missing buffers. Pool removal must complete even if ordinary-ring rebuilding fails, after retiring all references to the departing pool. Preserve TBS configuration and return a detached interface to the ordinary down/up recovery path. Fixes: bba2556efad6 ("net: stmmac: Enable RX via AF_XDP zero-copy") Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 5 - drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 116 +++------------------- drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c | 44 ++++---- 3 files changed, 32 insertions(+), 133 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 2fa7b18eb9a4..b65f47db3c2e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -461,11 +461,6 @@ static inline bool stmmac_tc_active(struct stmmac_priv *priv) netif_device_present(priv->dev) && !priv->hw_unavailable; } -void stmmac_disable_rx_queue(struct stmmac_priv *priv, u32 queue); -void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue); -void stmmac_disable_tx_queue(struct stmmac_priv *priv, u32 queue); -void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue); -int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags); struct timespec64 stmmac_calc_tas_basetime(ktime_t old_base_time, ktime_t current_time, u64 cycle_time); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 22a65c1c1681..618e01772ab8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2268,6 +2268,7 @@ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, tx_q->dma_etx = NULL; tx_q->dma_entx = NULL; tx_q->dma_tx = NULL; + tx_q->xsk_pool = NULL; } static void free_dma_tx_desc_resources(struct stmmac_priv *priv, @@ -2756,6 +2757,12 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) bool work_done = true; u32 tx_set_ic_bit = 0; + /* Nothing can be submitted while the link is down. Let NAPI complete; + * userspace can retry ndo_xsk_wakeup() once carrier has returned. + */ + if (!netif_carrier_ok(priv->dev)) + return true; + /* Avoids TX time-out as we are sharing with slow path */ txq_trans_cond_update(nq); @@ -2770,8 +2777,7 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) /* We are sharing with slow path and stop XSK TX desc submission when * available TX ring is less than threshold. */ - if (unlikely(stmmac_tx_avail(priv, queue) < STMMAC_TX_XSK_AVAIL) || - !netif_carrier_ok(priv->dev)) { + if (unlikely(stmmac_tx_avail(priv, queue) < STMMAC_TX_XSK_AVAIL)) { work_done = false; break; } @@ -7352,107 +7358,6 @@ static int stmmac_xdp_xmit(struct net_device *dev, int num_frames, return nxmit; } -void stmmac_disable_rx_queue(struct stmmac_priv *priv, u32 queue) -{ - struct stmmac_channel *ch = &priv->channel[queue]; - unsigned long flags; - - spin_lock_irqsave(&ch->lock, flags); - stmmac_disable_dma_irq(priv, priv->ioaddr, queue, 1, 0); - spin_unlock_irqrestore(&ch->lock, flags); - - stmmac_stop_rx_dma(priv, queue); - __free_dma_rx_desc_resources(priv, priv->dma_conf, queue); -} - -void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue) -{ - struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue]; - struct stmmac_channel *ch = &priv->channel[queue]; - unsigned long flags; - int ret; - - ret = __alloc_dma_rx_desc_resources(priv, priv->dma_conf, queue); - if (ret) { - netdev_err(priv->dev, "Failed to alloc RX desc.\n"); - return; - } - - ret = __init_dma_rx_desc_rings(priv, priv->dma_conf, queue, GFP_KERNEL); - if (ret) { - __free_dma_rx_desc_resources(priv, priv->dma_conf, queue); - netdev_err(priv->dev, "Failed to init RX desc.\n"); - return; - } - - stmmac_reset_rx_queue(priv, queue); - stmmac_clear_rx_descriptors(priv, priv->dma_conf, queue); - - stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, - rx_q->dma_rx_phy, queue); - - stmmac_set_queue_rx_tail_ptr(priv, rx_q, queue, rx_q->buf_alloc_num); - - stmmac_set_queue_rx_buf_size(priv, rx_q, queue); - - stmmac_start_rx_dma(priv, queue); - - spin_lock_irqsave(&ch->lock, flags); - stmmac_enable_dma_irq(priv, priv->ioaddr, queue, 1, 0); - spin_unlock_irqrestore(&ch->lock, flags); -} - -void stmmac_disable_tx_queue(struct stmmac_priv *priv, u32 queue) -{ - struct stmmac_channel *ch = &priv->channel[queue]; - unsigned long flags; - - spin_lock_irqsave(&ch->lock, flags); - stmmac_disable_dma_irq(priv, priv->ioaddr, queue, 0, 1); - spin_unlock_irqrestore(&ch->lock, flags); - - stmmac_stop_tx_dma(priv, queue); - __free_dma_tx_desc_resources(priv, priv->dma_conf, queue); -} - -void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue) -{ - struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue]; - struct stmmac_channel *ch = &priv->channel[queue]; - unsigned long flags; - int ret; - - ret = __alloc_dma_tx_desc_resources(priv, priv->dma_conf, queue); - if (ret) { - netdev_err(priv->dev, "Failed to alloc TX desc.\n"); - return; - } - - ret = __init_dma_tx_desc_rings(priv, priv->dma_conf, queue); - if (ret) { - __free_dma_tx_desc_resources(priv, priv->dma_conf, queue); - netdev_err(priv->dev, "Failed to init TX desc.\n"); - return; - } - - stmmac_reset_tx_queue(priv, queue); - stmmac_clear_tx_descriptors(priv, priv->dma_conf, queue); - - stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, - tx_q->dma_tx_phy, queue); - - if (tx_q->tbs & STMMAC_TBS_AVAIL) - stmmac_enable_tbs(priv, priv->ioaddr, 1, queue); - - stmmac_set_queue_tx_tail_ptr(priv, tx_q, queue, 0); - - stmmac_start_tx_dma(priv, queue); - - spin_lock_irqsave(&ch->lock, flags); - stmmac_enable_dma_irq(priv, priv->ioaddr, queue, 0, 1); - spin_unlock_irqrestore(&ch->lock, flags); -} - void stmmac_xdp_release(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); @@ -7552,6 +7457,9 @@ int stmmac_xdp_open(struct net_device *dev) stmmac_set_queue_tx_tail_ptr(priv, tx_q, chan, 0); + if (tx_q->tbs & STMMAC_TBS_AVAIL) + stmmac_enable_tbs(priv, priv->ioaddr, 1, chan); + hrtimer_setup(&tx_q->txtimer, stmmac_tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); } @@ -7589,7 +7497,7 @@ int stmmac_xdp_open(struct net_device *dev) return ret; } -int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags) +static int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags) { struct stmmac_priv *priv = netdev_priv(dev); struct stmmac_channel *ch; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c index 7ecb7addd2ea..907ac49a1b76 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c @@ -9,7 +9,6 @@ static int stmmac_xdp_enable_pool(struct stmmac_priv *priv, struct xsk_buff_pool *pool, u16 queue) { - struct stmmac_channel *ch = &priv->channel[queue]; bool need_update; u32 frame_size; int err; @@ -34,23 +33,23 @@ static int stmmac_xdp_enable_pool(struct stmmac_priv *priv, need_update = priv->datapath == STMMAC_DATAPATH_RUNNING && stmmac_xdp_is_enabled(priv); - if (need_update) { - napi_disable(&ch->rx_napi); - napi_disable(&ch->tx_napi); - stmmac_disable_rx_queue(priv, queue); - stmmac_disable_tx_queue(priv, queue); - } + if (need_update) + stmmac_xdp_release(priv->dev); set_bit(queue, priv->af_xdp_zc_qps); if (need_update) { - stmmac_enable_rx_queue(priv, queue); - stmmac_enable_tx_queue(priv, queue); - napi_enable(&ch->rxtx_napi); - - err = stmmac_xsk_wakeup(priv->dev, queue, XDP_WAKEUP_RX); - if (err) + err = stmmac_xdp_open(priv->dev); + if (err) { + clear_bit(queue, priv->af_xdp_zc_qps); + xsk_pool_dma_unmap(pool, STMMAC_RX_DMA_ATTR); + netdev_err(priv->dev, "failed reopening after XSK pool attach: %pe; interface remains detached\n", + ERR_PTR(err)); return err; + } + + /* The pool is installed even if link resolution is still pending. */ + napi_schedule(&priv->channel[queue].rxtx_napi); } return 0; @@ -58,9 +57,9 @@ static int stmmac_xdp_enable_pool(struct stmmac_priv *priv, static int stmmac_xdp_disable_pool(struct stmmac_priv *priv, u16 queue) { - struct stmmac_channel *ch = &priv->channel[queue]; struct xsk_buff_pool *pool; bool need_update; + int err; if (queue >= priv->plat->rx_queues_to_use || queue >= priv->plat->tx_queues_to_use) @@ -73,24 +72,21 @@ static int stmmac_xdp_disable_pool(struct stmmac_priv *priv, u16 queue) need_update = priv->datapath == STMMAC_DATAPATH_RUNNING && stmmac_xdp_is_enabled(priv); - if (need_update) { - napi_disable(&ch->rxtx_napi); - stmmac_disable_rx_queue(priv, queue); - stmmac_disable_tx_queue(priv, queue); - synchronize_rcu(); - } + if (need_update) + stmmac_xdp_release(priv->dev); xsk_pool_dma_unmap(pool, STMMAC_RX_DMA_ATTR); clear_bit(queue, priv->af_xdp_zc_qps); if (need_update) { - stmmac_enable_rx_queue(priv, queue); - stmmac_enable_tx_queue(priv, queue); - napi_enable(&ch->rx_napi); - napi_enable(&ch->tx_napi); + err = stmmac_xdp_open(priv->dev); + if (err) + netdev_err(priv->dev, "failed reopening after XSK pool removal: %pe; interface remains detached\n", + ERR_PTR(err)); } + /* Socket teardown must be able to unmap and free the removed pool. */ return 0; } From patchwork Sat Sep 26 15:49:06 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3482 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 2FE011C2AA6 for ; Sat, 26 Sep 2026 17:58:12 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26276-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26276-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 415FC36EEB for ; Sat, 26 Sep 2026 15:50:49 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 2E5B2381E9A; Sat, 26 Sep 2026 15:49:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FcgLW/3z" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f43.google.com (mail-oo2-f43.google.com [74.125.231.171]) (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 16CBD369D6E for ; Sat, 26 Sep 2026 15:49:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437789; cv=none; b=tkjuXifjfgBHD2xp+C3yYrwLLxURCdKG/0ehV7lDeHPRFcO2jbKlJlzsC13kVgvj/l49Ku9juVAw28qU/NicKfmlwnuugyqLPOcacydp5CHMn9KhsuUycfR7yKK606/8G8yXsqvzkRStIUi5kPQsbUc0k/4FXM6t+m12aPEuPfE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437789; c=relaxed/simple; bh=AAtTdvZNrumwoPmEQLjH0UMJLifFnY3iZ+J8gFMDj0s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=lDLOe9E+NL9C8Rai3EDJuxG8PO2xu9s7qyv1Ql7oUjlFxdAgy+snXXHd4nNIm9iVX2M6NCqVs3hYeAgu8nCrdgNgf2ajMjB6U1t+4m/BOVyZ4N7seypKMU8NorO0jHgG/ItVnVx88V7CqSi+XCZAW5JIRTRbZkd8wqWrqrkQCy8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FcgLW/3z; arc=none smtp.client-ip=74.125.231.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f43.google.com with SMTP id 46e09a7af769-81b15bca7e0so202416a34.2 for ; Sat, 26 Sep 2026 08:49:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437783; x=1791042583; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Q30zsdAglVy5fQcIaWXAHFq5dI/FtpUN3Zb+euSKjCs=; b=FcgLW/3ze15mYPhJWo0l4JET4aVxpvweUAmisnH7gQWuCBXrKBwtjZl9muUBImz/+U RnA5Amn2qIihlhVK8BLFP//qoUqheNF3rmeF7SnXYXRi5LNF/qghGP/8jYPB89IkvJrC Krh2YfWya0XaVaB3HS26v6WcewUqwbkqiNAghSZr5dkOTqDGXUS0sZLHVpKZDwcgapWT SXFovrB+5zfuFeK5POR0ieZP1TDPp2GvLhqCNK8wDtBJ9q7fnyey7lJcC7T00K0nGGGt ar35BEodjSrGxQNlDSfCIJh1GCB9NOgvr5AvXjJkFox2xqr6eJBVXwmFD/G3ybEWUKEJ 52Dw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437783; x=1791042583; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Q30zsdAglVy5fQcIaWXAHFq5dI/FtpUN3Zb+euSKjCs=; b=b+p5rVhipisro7i/ZWRyRy+DB3wM9aVD2gjWuVnsQxTqaccq3EX5HZyLQVS3Vx2EJv k5kxCm0LUTISYynyczWbFv/sE/5QnXAHHR4+Wz0HcPGnsinY/Ih/hSSnptZuhzZyqf7E 9FQRwVdl8RxiSgtcLmm8OpUxvZ3cCrLoO7FVpQgsXnRp6mQD8qoV8CyicgKcBmnPRPaN c1rsBUG+QGhu8NX5QjxIkUeV0JQzxuCw3TVDWseGuPAo8G7m68z5V7K1AJua5GWRnJGn oe6f1Nzaw83uMLbCsK7ck3KBQIfiY6BuKkQ0hDSxr6LZNTNmtqZvCW6UHamqRCUDRpdm yedw== X-Forwarded-Encrypted: i=1; AKwUvBy2AAl812FPpl33hxy3/0sHXvWyKHaAJWas8y0zD6l3eVofolJEJAKEuchinC0F/F51bkDsZ/95y6PPmg==@lists.linux.dev X-Gm-Message-State: AFuF++meWdnIGvd+7rhk0BBLGobszuBmF0ltYiX70w0C90vd/RYa2owk AbsSAYMEu8NrxJ8Ra98nCeMj+u3PXfmSWpsFVoxR+l1zWvh0fhB7Cvnf X-Gm-Gg: AYBFou1HwDoxoG/4sJjRm+iT4p60mTsGG+fCfi5aITtjws+if2b30cU7vFtAL8s+gab sTSKrw7d718LsaeKDakze0DyycpnBHySb/0e7wWXgNt2q6b4Njq+2Ai3ZkVkvWMwjOORZnB9s5D iqcu7Ab9DLOfAj0CPWWkR/1L/Jwf1nbpt8sFxkF1vZ5+XbVD02K7z0+0obgm8qjYrwfIQmOEtpd /KA66fdEGcyjaZpt8PaQjePyebPIqRU8snRiZQycIAuaABK3w/gGcsCVplOom59JfvGepLayYfL XdBIhdw46tPfdtPPv8f/mmkySxmIH2N5+Tuz/klJDQpDR2yL/GN6V99ksRrw92ZICaSrAAv3UD3 IIV7+pVxjYRlxG1bgSlMPyPzgjWIehNRHDCH0KBVwlmwWp3bhd3/4CfGYs7cd718USAMnguXGpd rXzMIkF0U+TNoOXE4hLiuE2zkXGhFgK/zpl6qgNG/zT28trJF63rpfV/Tz+uC0Uqc7SxR2bJYLz K7bUaSsCnebG4mj/H0w2DhlRJlxo2l4q7xlbioGaZkpCsFT7QIazdbn3R89lWn5X3M/Z9DdxR3k SwqaM+JdvPaqu2r7pVqfcA53A7q+m1bXw0korLZtkQqWyi+I1DX7WuwiqDd9/EXfXQsKuvknHgQ pijBbbwFxAYSEtnm6pxYa X-Received: by 2002:a05:6830:3887:b0:7f5:17:3f2a with SMTP id 46e09a7af769-8178317d7ddmr9815575a34.15.1790437783091; Sat, 26 Sep 2026 08:49:43 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:42 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:49:06 -0600 Subject: [PATCH net v4 11/15] net: stmmac: restore TC offloads before restarting DMA Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-11-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [-1.16 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[172.234.253.10:from]; SUSPICIOUS_RECIPS(1.50)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; MAILLIST(-0.15)[generic]; MIME_GOOD(-0.10)[text/plain]; BAD_REP_POLICIES(0.10)[]; HAS_LIST_UNSUB(-0.01)[]; DBL_BLOCKED_OPENRESOLVER(0.00)[sea.lore.kernel.org:rdns,sea.lore.kernel.org:helo]; FORGED_SENDER_MAILLIST(0.00)[]; RCVD_COUNT_FIVE(0.00)[6]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; FROM_HAS_DN(0.00)[]; TAGGED_RCPT(0.00)[kernel,netdev]; PRECEDENCE_BULK(0.00)[]; FREEMAIL_CC(0.00)[bootlin.com,d-silva.org,kernel.org,vger.kernel.org,st-md-mailman.stormreply.com,lists.infradead.org,uniontech.com,oss.qualcomm.com,163.com,gmail.com,lists.linux.dev]; FROM_NEQ_ENVFROM(0.00)[jameshilliard1@gmail.com,linux-sunxi@lists.linux.dev]; ASN(0.00)[asn:63949, ipnet:172.234.224.0/19, country:SG]; MID_RHS_MATCH_FROM(0.00)[]; FREEMAIL_FROM(0.00)[gmail.com]; R_SPF_ALLOW(0.00)[+ip4:172.234.253.10]; TO_DN_SOME(0.00)[]; RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[172.234.253.10:from]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[100.90.174.1:received,74.125.231.171:received]; TAGGED_FROM(0.00)[bounces-26276-noreply=patchwork.local]; RCPT_COUNT_GT_50(0.00)[54]; RECEIVED_SPAMHAUS_PBL(0.00)[174.29.1.49:received]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; FREEMAIL_TO(0.00)[armlinux.org.uk,lunn.ch,gmail.com,davemloft.net,kernel.org,redhat.com,nxp.com,bootlin.com,foss.st.com,loongson.cn,iogearbox.net,fomichev.me,quicinc.com,synopsys.com,intel.com,googlemail.com,nvidia.com,sholland.org]; RCVD_TLS_LAST(0.00)[]; MIME_TRACE(0.00)[0:+]; RCVD_VIA_SMTP_AUTH(0.00)[] X-Rspamd-Queue-Id: 2FE011C2AA6 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= A DMA software reset loses MAC filters and the MTL gate schedule, not just the ring addresses. Restore those offloads during ordinary hardware setup and resume. The later live-XDP reset fallback and retained-ring MTU transaction will use the same restoration. Keep the parsed L3/L4 rule rather than only its cookie. Program from that saved definition both when installing a rule and after reset. Do not publish a partially programmed replacement; restore the previous slot when installation fails. Keep runtime VLAN priorities in the existing queue configuration and replay EtherType steering and the configured preemption/TC mapping. Preserve the additional fragment size. Restore EST after the timestamp counter is usable, advancing its saved base time by whole cycles when necessary. Delay DMA start until filters, timestamp state and the gate schedule have been restored. Propagate a replay error through the existing rollback or detached recovery path. Build and validate TAPRIO replacements separately, including the PHC time read, and publish the saved schedule only after hardware setup succeeds. Keep the previous schedule on rejection and attempt to restore it after a programming error. A rejected first install must not leave an enabled zero-cycle cache for PHC adjustment or reset replay. Serialize schedule publication with those consumers under the PTP mutex. Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 9 + drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c | 1 + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 25 +- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 286 +++++++++++++++------- 4 files changed, 226 insertions(+), 95 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index b65f47db3c2e..6abb0d1d4095 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -151,6 +151,9 @@ struct stmmac_fpe_cfg { struct ethtool_mmsv mmsv; const struct stmmac_fpe_reg *reg; u32 fpe_csr; /* MAC_FPE_CTRL_STS reg cache */ + u32 preemptible_tcs; + u32 add_frag_size; + bool mapping_configured; }; struct stmmac_tc_entry { @@ -194,6 +197,10 @@ struct stmmac_flow_entry { unsigned long cookie; unsigned long action; u8 ip_proto; + u32 ip4_src; + u32 ip4_dst; + u16 port_src; + u16 port_dst; int in_use; int idx; int is_l4; @@ -433,6 +440,8 @@ void stmmac_set_ethtool_ops(struct net_device *netdev); void stmmac_ptp_register(struct stmmac_priv *priv); void stmmac_ptp_unregister(struct stmmac_priv *priv); int stmmac_ptp_restore(struct stmmac_priv *priv); +int stmmac_tc_restore_filters(struct stmmac_priv *priv); +int stmmac_tc_restore_est(struct stmmac_priv *priv); int stmmac_xdp_open(struct net_device *dev); void stmmac_xdp_release(struct net_device *dev); int stmmac_get_phy_intf_sel(phy_interface_t interface); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c index c889204a7aa5..067ea1f5134b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c @@ -195,6 +195,7 @@ void stmmac_fpe_set_add_frag_size(struct stmmac_priv *priv, u32 add_frag_size) value = readl(ioaddr + reg->mtl_fpe_reg); writel(u32_replace_bits(value, add_frag_size, FPE_MTL_ADD_FRAG_SZ), ioaddr + reg->mtl_fpe_reg); + priv->fpe_cfg.add_frag_size = add_frag_size; } #define ALG_ERR_MSG "TX algorithm SP is not suitable for one-to-many mapping" diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 618e01772ab8..edcb9a025385 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3756,6 +3756,11 @@ static int stmmac_hw_setup(struct net_device *dev) if (ret) return ret; } + ret = stmmac_tc_restore_filters(priv); + if (ret) + return ret; + if (stmmac_fpe_supported(priv)) + stmmac_fpe_set_add_frag_size(priv, priv->fpe_cfg.add_frag_size); /* Initialize Safety Features */ stmmac_safety_feat_configuration(priv); @@ -3818,9 +3823,6 @@ static int stmmac_hw_setup(struct net_device *dev) stmmac_enable_tbs(priv, priv->ioaddr, enable, chan); } - /* Start the ball rolling... */ - stmmac_start_all_dma(priv); - phylink_rx_clk_stop_block(priv->phylink); stmmac_set_hw_vlan_mode(priv, priv->hw); phylink_rx_clk_stop_unblock(priv->phylink); @@ -4348,6 +4350,14 @@ static int __stmmac_open(struct net_device *dev, stmmac_init_coalesce(priv); stmmac_vlan_restore(priv); + mutex_lock(&priv->ptp_mutex); + ret = stmmac_tc_restore_est(priv); + mutex_unlock(&priv->ptp_mutex); + if (ret) + goto irq_error; + + /* All reset-sensitive offloads must be installed before DMA runs. */ + stmmac_start_all_dma(priv); ret = stmmac_request_irq(dev); if (ret) @@ -8722,10 +8732,14 @@ int stmmac_resume(struct device *dev) goto error_stop_dma; } - stmmac_init_timestamping(priv); mutex_lock(&priv->ptp_mutex); - stmmac_block_ptp(priv, false); + stmmac_init_timestamping(priv); + ret = stmmac_tc_restore_est(priv); + if (!ret) + stmmac_block_ptp(priv, false); mutex_unlock(&priv->ptp_mutex); + if (ret) + goto error_stop_dma; stmmac_init_coalesce(priv); phylink_rx_clk_stop_block(priv->phylink); @@ -8734,6 +8748,7 @@ int stmmac_resume(struct device *dev) stmmac_vlan_restore(priv); + stmmac_start_all_dma(priv); stmmac_enable_all_queues(priv); stmmac_enable_all_dma_irq(priv); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 9ded65750e89..de254919f6ab 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -14,12 +14,17 @@ static int tc_config_preemption(struct stmmac_priv *priv, struct netlink_ext_ack *extack, u32 preemptible_tcs) { - /* Qdisc teardown must not access unpowered registers. */ - if (priv->hw_unavailable) - return 0; + int ret = 0; - return stmmac_fpe_map_preemption_class(priv, priv->dev, extack, - preemptible_tcs); + /* Qdisc teardown still clears the saved mapping after failed resume. */ + if (!priv->hw_unavailable) + ret = stmmac_fpe_map_preemption_class(priv, priv->dev, extack, + preemptible_tcs); + if (!ret) { + priv->fpe_cfg.preemptible_tcs = preemptible_tcs; + priv->fpe_cfg.mapping_configured = true; + } + return ret; } static void tc_fill_all_pass_entry(struct stmmac_tc_entry *entry) @@ -519,31 +524,15 @@ static int tc_add_ip4_flow(struct stmmac_priv *priv, { struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; - bool inv = entry->action & STMMAC_FLOW_ACTION_DROP; struct flow_match_ipv4_addrs match; - u32 hw_match; - int ret; /* Nothing to do here */ if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) return -EINVAL; flow_rule_match_ipv4_addrs(rule, &match); - hw_match = ntohl(match.key->src) & ntohl(match.mask->src); - if (hw_match) { - ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, true, - false, true, inv, hw_match); - if (ret) - return ret; - } - - hw_match = ntohl(match.key->dst) & ntohl(match.mask->dst); - if (hw_match) { - ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, true, - false, false, inv, hw_match); - if (ret) - return ret; - } + entry->ip4_src = ntohl(match.key->src) & ntohl(match.mask->src); + entry->ip4_dst = ntohl(match.key->dst) & ntohl(match.mask->dst); return 0; } @@ -554,11 +543,7 @@ static int tc_add_ports_flow(struct stmmac_priv *priv, { struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; - bool inv = entry->action & STMMAC_FLOW_ACTION_DROP; struct flow_match_ports match; - u32 hw_match; - bool is_udp; - int ret; /* Nothing to do here */ if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_PORTS)) @@ -566,10 +551,7 @@ static int tc_add_ports_flow(struct stmmac_priv *priv, switch (entry->ip_proto) { case IPPROTO_TCP: - is_udp = false; - break; case IPPROTO_UDP: - is_udp = true; break; default: return -EINVAL; @@ -577,23 +559,46 @@ static int tc_add_ports_flow(struct stmmac_priv *priv, flow_rule_match_ports(rule, &match); - hw_match = ntohs(match.key->src) & ntohs(match.mask->src); - if (hw_match) { - ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, true, - is_udp, true, inv, hw_match); + entry->port_src = ntohs(match.key->src) & ntohs(match.mask->src); + entry->port_dst = ntohs(match.key->dst) & ntohs(match.mask->dst); + + entry->is_l4 = true; + return 0; +} + +static int tc_config_flow(struct stmmac_priv *priv, + const struct stmmac_flow_entry *entry) +{ + bool inv = entry->action & STMMAC_FLOW_ACTION_DROP; + bool udp = entry->ip_proto == IPPROTO_UDP; + int ret; + + /* Clear the whole slot, including matches removed by a replacement. */ + ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, false, + false, false, false, 0); + if (ret || !entry->in_use) + return ret; + if (entry->ip4_src) { + ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, true, + false, true, inv, entry->ip4_src); if (ret) return ret; } - - hw_match = ntohs(match.key->dst) & ntohs(match.mask->dst); - if (hw_match) { + if (entry->ip4_dst) { + ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, true, + false, false, inv, entry->ip4_dst); + if (ret) + return ret; + } + if (entry->port_src) { ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, true, - is_udp, false, inv, hw_match); + udp, true, inv, entry->port_src); if (ret) return ret; } - - entry->is_l4 = true; + if (entry->port_dst) + return stmmac_config_l4_filter(priv, priv->hw, entry->idx, true, + udp, false, inv, entry->port_dst); return 0; } @@ -629,6 +634,7 @@ static int tc_add_flow(struct stmmac_priv *priv, { struct stmmac_flow_entry *entry = tc_find_flow(priv, cls, false); struct flow_rule *rule = flow_cls_offload_flow_rule(cls); + struct stmmac_flow_entry new = {}; int i, ret; if (!entry) { @@ -637,23 +643,33 @@ static int tc_add_flow(struct stmmac_priv *priv, return -ENOENT; } - ret = tc_parse_flow_actions(priv, &rule->action, entry, + new.idx = entry->idx; + ret = tc_parse_flow_actions(priv, &rule->action, &new, cls->common.extack); if (ret) return ret; for (i = 0; i < ARRAY_SIZE(tc_flow_parsers); i++) { - ret = tc_flow_parsers[i].fn(priv, cls, entry); + ret = tc_flow_parsers[i].fn(priv, cls, &new); if (!ret) - entry->in_use = true; + new.in_use = true; else if (ret == -EOPNOTSUPP) return ret; } - if (!entry->in_use) + if (!new.in_use) return -EINVAL; - entry->cookie = cls->cookie; + ret = tc_config_flow(priv, &new); + if (ret) { + /* Do not publish a rule that was only partially programmed. */ + if (tc_config_flow(priv, entry)) + netdev_err(priv->dev, "failed to restore flower filter %d\n", + entry->idx); + return ret; + } + new.cookie = cls->cookie; + *entry = new; return 0; } @@ -739,6 +755,8 @@ static int tc_add_vlan_flow(struct stmmac_priv *priv, prio = BIT(match.key->vlan_priority); stmmac_rx_queue_prio(priv, priv->hw, prio, tc); + priv->plat->rx_queues_cfg[tc].prio = prio; + priv->plat->rx_queues_cfg[tc].use_prio = true; entry->in_use = true; entry->cookie = cls->cookie; @@ -760,6 +778,8 @@ static int tc_del_vlan_flow(struct stmmac_priv *priv, if (stmmac_tc_active(priv)) stmmac_rx_queue_prio(priv, priv->hw, 0, entry->tc); + priv->plat->rx_queues_cfg[entry->tc].prio = 0; + priv->plat->rx_queues_cfg[entry->tc].use_prio = true; entry->in_use = false; entry->cookie = 0; @@ -934,6 +954,45 @@ static int tc_setup_cls(struct stmmac_priv *priv, return ret; } +int stmmac_tc_restore_filters(struct stmmac_priv *priv) +{ + int i, ret; + + for (i = 0; i < priv->flow_entries_max; i++) { + struct stmmac_flow_entry *entry = &priv->flow_entries[i]; + + if (!entry->in_use) + continue; + ret = tc_config_flow(priv, entry); + if (ret) + return ret; + } + for (i = 0; i < priv->rfs_entries_total; i++) { + struct stmmac_rfs_entry *entry = &priv->rfs_entries[i]; + + if (!entry->in_use) + continue; + switch (entry->type) { + /* VLAN priorities are replayed by stmmac_mtl_configuration(). */ + case STMMAC_RFS_T_VLAN: + break; + case STMMAC_RFS_T_LLDP: + stmmac_rx_queue_routing(priv, priv->hw, PACKET_DCBCPQ, + entry->tc); + break; + case STMMAC_RFS_T_1588: + stmmac_rx_queue_routing(priv, priv->hw, PACKET_PTPQ, + entry->tc); + break; + } + } + /* The XGMAC callback also restores the runtime TC-to-queue mapping. */ + if (priv->fpe_cfg.mapping_configured) + return tc_config_preemption(priv, NULL, + priv->fpe_cfg.preemptible_tcs); + return 0; +} + struct timespec64 stmmac_calc_tas_basetime(ktime_t old_base_time, ktime_t current_time, u64 cycle_time) @@ -957,7 +1016,37 @@ struct timespec64 stmmac_calc_tas_basetime(ktime_t old_base_time, return time; } -static void tc_taprio_map_maxsdu_txq(struct stmmac_priv *priv, +/* Called after timestamp setup and before DMA starts, with ptp_mutex held. + * The PHC can still be blocked while reset state is being restored. + */ +int stmmac_tc_restore_est(struct stmmac_priv *priv) +{ + struct stmmac_est *est = priv->est; + struct timespec64 base; + unsigned long flags; + u64 now, cycle; + int ret; + + if (!est || !est->enable) + return 0; + mutex_lock(&priv->est_lock); + read_lock_irqsave(&priv->ptp_lock, flags); + ret = stmmac_get_systime(priv, priv->ptpaddr, &now); + read_unlock_irqrestore(&priv->ptp_lock, flags); + if (ret) + goto out; + cycle = (u64)est->ctr[1] * NSEC_PER_SEC + est->ctr[0]; + base = stmmac_calc_tas_basetime(ktime_set(est->btr_reserve[1], + est->btr_reserve[0]), now, cycle); + est->btr[0] = base.tv_nsec; + est->btr[1] = base.tv_sec; + ret = stmmac_est_configure(priv, priv, est, priv->plat->clk_ptp_rate); +out: + mutex_unlock(&priv->est_lock); + return ret; +} + +static void tc_taprio_map_maxsdu_txq(struct stmmac_est *est, struct tc_taprio_qopt_offload *qopt) { u32 num_tc = qopt->mqprio.qopt.num_tc; @@ -974,7 +1063,7 @@ static void tc_taprio_map_maxsdu_txq(struct stmmac_priv *priv, count = qopt->mqprio.qopt.count[i]; for (j = offset; j < offset + count; j++) - priv->est->max_sdu[j] = qopt->max_sdu[i] + ETH_HLEN - ETH_TLEN; + est->max_sdu[j] = qopt->max_sdu[i] + ETH_HLEN - ETH_TLEN; } } @@ -984,8 +1073,9 @@ static int tc_taprio_configure(struct stmmac_priv *priv, u32 size, wid = priv->dma_cap.estwid, dep = priv->dma_cap.estdep; struct netlink_ext_ack *extack = qopt->mqprio.extack; struct timespec64 time, current_time, qopt_time; + struct stmmac_est *est; ktime_t current_time_ns; - int err, i, ret = 0; + int i, ret; u64 ctr; if (qopt->base_time < 0) @@ -1040,34 +1130,27 @@ static int tc_taprio_configure(struct stmmac_priv *priv, if (qopt->cycle_time_extension >= BIT(wid + 7)) return -ERANGE; - if (!priv->est) { - priv->est = devm_kzalloc(priv->device, sizeof(*priv->est), - GFP_KERNEL); - if (!priv->est) - return -ENOMEM; - - mutex_init(&priv->est_lock); - } else { - mutex_lock(&priv->est_lock); - memset(priv->est, 0, sizeof(*priv->est)); - mutex_unlock(&priv->est_lock); - } + /* Build the replacement without changing the installed schedule. An + * entry rejected below must not leave an enabled, zero-cycle cache for + * PHC adjustment or reset replay to consume. + */ + est = kzalloc_obj(*est); + if (!est) + return -ENOMEM; size = qopt->num_entries; - - mutex_lock(&priv->est_lock); - priv->est->gcl_size = size; - priv->est->enable = qopt->cmd == TAPRIO_CMD_REPLACE; - mutex_unlock(&priv->est_lock); + est->gcl_size = size; + est->enable = true; for (i = 0; i < size; i++) { s64 delta_ns = qopt->entries[i].interval; u32 gates = qopt->entries[i].gate_mask; - if (delta_ns > GENMASK(wid - 1, 0)) - return -ERANGE; - if (gates > GENMASK(31 - wid, 0)) - return -ERANGE; + if (delta_ns > GENMASK(wid - 1, 0) || + gates > GENMASK(31 - wid, 0)) { + ret = -ERANGE; + goto free_est; + } switch (qopt->entries[i].command) { case TC_TAPRIO_CMD_SET_GATES: @@ -1079,47 +1162,69 @@ static int tc_taprio_configure(struct stmmac_priv *priv, gates &= ~BIT(0); break; default: - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto free_est; } - priv->est->gcl[i] = delta_ns | (gates << wid); + est->gcl[i] = delta_ns | (gates << wid); } - mutex_lock(&priv->est_lock); /* Adjust for real system time */ - priv->ptp_clock_ops.gettime64(&priv->ptp_clock_ops, ¤t_time); + ret = priv->ptp_clock_ops.gettime64(&priv->ptp_clock_ops, ¤t_time); + if (ret) + goto free_est; current_time_ns = timespec64_to_ktime(current_time); time = stmmac_calc_tas_basetime(qopt->base_time, current_time_ns, qopt->cycle_time); - priv->est->btr[0] = (u32)time.tv_nsec; - priv->est->btr[1] = (u32)time.tv_sec; + est->btr[0] = (u32)time.tv_nsec; + est->btr[1] = (u32)time.tv_sec; qopt_time = ktime_to_timespec64(qopt->base_time); - priv->est->btr_reserve[0] = (u32)qopt_time.tv_nsec; - priv->est->btr_reserve[1] = (u32)qopt_time.tv_sec; + est->btr_reserve[0] = (u32)qopt_time.tv_nsec; + est->btr_reserve[1] = (u32)qopt_time.tv_sec; ctr = qopt->cycle_time; - priv->est->ctr[0] = do_div(ctr, NSEC_PER_SEC); - priv->est->ctr[1] = (u32)ctr; + est->ctr[0] = do_div(ctr, NSEC_PER_SEC); + est->ctr[1] = (u32)ctr; - priv->est->ter = qopt->cycle_time_extension; + est->ter = qopt->cycle_time_extension; - tc_taprio_map_maxsdu_txq(priv, qopt); + tc_taprio_map_maxsdu_txq(est, qopt); - ret = stmmac_est_configure(priv, priv, priv->est, - priv->plat->clk_ptp_rate); - mutex_unlock(&priv->est_lock); + if (!priv->est) { + priv->est = devm_kzalloc(priv->device, sizeof(*priv->est), + GFP_KERNEL); + if (!priv->est) { + ret = -ENOMEM; + goto free_est; + } + mutex_init(&priv->est_lock); + } + + mutex_lock(&priv->est_lock); + ret = stmmac_est_configure(priv, priv, est, priv->plat->clk_ptp_rate); if (ret) { netdev_err(priv->dev, "failed to configure EST\n"); - goto disable; + goto restore; } ret = tc_config_preemption(priv, extack, qopt->mqprio.preemptible_tcs); if (ret) - goto disable; + goto restore; - return 0; + *priv->est = *est; + mutex_unlock(&priv->est_lock); +free_est: + kfree(est); + return ret; + +restore: + /* A failed hardware update must not publish the rejected schedule. */ + if (stmmac_est_configure(priv, priv, priv->est, priv->plat->clk_ptp_rate)) + netdev_err(priv->dev, "failed to restore EST\n"); + mutex_unlock(&priv->est_lock); + goto free_est; disable: if (priv->est) { @@ -1137,9 +1242,7 @@ static int tc_taprio_configure(struct stmmac_priv *priv, mutex_unlock(&priv->est_lock); } - err = tc_config_preemption(priv, extack, 0); - - return qopt->cmd == TAPRIO_CMD_DESTROY ? err : ret; + return tc_config_preemption(priv, extack, 0); } static void tc_taprio_stats(struct stmmac_priv *priv, @@ -1180,7 +1283,10 @@ static int tc_setup_taprio(struct stmmac_priv *priv, switch (qopt->cmd) { case TAPRIO_CMD_REPLACE: case TAPRIO_CMD_DESTROY: + /* Serialize cache publication with PHC adjustment and reset replay. */ + mutex_lock(&priv->ptp_mutex); err = tc_taprio_configure(priv, qopt); + mutex_unlock(&priv->ptp_mutex); break; case TAPRIO_CMD_STATS: tc_taprio_stats(priv, qopt); From patchwork Sat Sep 26 15:49:07 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3483 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 5EFB91C2AA6 for ; Sat, 26 Sep 2026 17:58:59 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26277-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26277-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 0CD9D3ECB5 for ; Sat, 26 Sep 2026 15:51:03 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4DDED3793CC; Sat, 26 Sep 2026 15:49:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CiBxOx0T" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f38.google.com (mail-oo2-f38.google.com [74.125.231.166]) (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 4AAA137F8BD for ; Sat, 26 Sep 2026 15:49:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.166 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437791; cv=none; b=p6zn4QXonYMk97BwsVnTXQsp5kkIFpiI4O2KFliS1UDtc1agELiMeVsERgnMmw52SDGmASFUQdwlIY3r/a4SR6hTIfc1JoF2HOTMaUtvyOKbQi4VDC15r5sgdKOuCngV8PDEFN8tdwpZniQKOf1tseBxQOZDCxHuxd3jPnwjq5c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437791; c=relaxed/simple; bh=D8q+JFl8ShBAwd8HatsVWk4ClB609jvEFQM0NLY9Qlk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=nzHczUM0mCNEYNOSoeKTh74VSk/2fqeIBC0BKC5P2+MpJDfglhxTAOG7Yy6TkBZWB+nptDbgnXsMd3OY13f85LU1SqQYLQ4e/vkZXziACydTUgtuYNWrecB4w1uXKXxjHw+taT3303qen89PgmmsZj5+knZVYjtdRJ6kawqDAWk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=CiBxOx0T; arc=none smtp.client-ip=74.125.231.166 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f38.google.com with SMTP id 46e09a7af769-7f4f0c89e34so1169913a34.1 for ; Sat, 26 Sep 2026 08:49:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437785; x=1791042585; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=v5J6qvfoNxCIHw/hgiMJQWPeLhNGA42IA+QA8tl0rh4=; b=CiBxOx0T6ejdMHkkz8PWzFwjH628PFpDR71zJA/CM6G/G//MPgiSylVyEOlirXYM4w s9HPJ3s0enzWp0kkfzBI5gHWZvNSsnutDKHhA4mb3fa7B1IXJRULlbJDVpMubqdNnQoi qBdnEgDT6eZOtH6ZOn3VBht1lFz7f0n8N4CE3nd8/Ch5YhKDKI1MXgxHEYz6od33+H0l a15BAQ0tOFcxDHq+YOyDnda1gyTpisFckRxE5IBct3y0sfEkzHHqFTXkAKJU/F3Ahwvp 7Yrq8i1sjkYa72sb+tclJeQJMmF9+nPXBSkd5dSf8hafMDoCQU1tRQJ9ag2nEjRefb5x u6rw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437785; x=1791042585; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=v5J6qvfoNxCIHw/hgiMJQWPeLhNGA42IA+QA8tl0rh4=; b=cFNm6b/CIWJk6ooH9PDqjmMWuYhNhN/mO2l1rSryqCOj+qVoMTx4HA6K9wDYea8ovl VqAJVhlHrJ7hZodSXhVtrQ0BlODIz2WJ5CgDtLWWO1wOAWsPZyocvft6oIxgSunVWB3I 3DPqU1FS7YoBI22X4GmUxszywT6hRJfLcJK3F4SSBI1Spic1Qz9nyZDEsRhdSVgmCw5T 17xt5eLhzaKoNU/BPn5NPrNviSSBk6rqAb2IYPaq1x2aUb8pVr0iT40HdTcUdMG3isVz DMaz9zo3WQEnoXZvEZT8cu87VXFN3J43XUEf8lBdVFqZzfR2LKnAIDtWPUjC9I7lVVXy Ow8Q== X-Forwarded-Encrypted: i=1; AKwUvBxD9xSgmkZiUdejqIsmVQ5V0P1O6oqfbvDTM0Du3V7A4d5hkKkq+NBQKAoueJAHApNvn6DBvC8/1Ln5fA==@lists.linux.dev X-Gm-Message-State: AFuF++kM30XDntvdw8AWryCyVSfQ3ByOdPJ0EoPcWSZ/Wp4jLqce6Vjc Y7hJVxh+1LlbYqyERObuXS5KenJu0M3QXFg69SFnBsXV2DOuuRnfqONE X-Gm-Gg: AYBFou2xJehTfqm3Zw6G6/FAKU87J997Z2VuHMmxsJ3TTqfyXrrVb2mfsXkmBJxDEFf WVSkAb/Rrat8haeu149utq/belJ1wqUPOAz4Fok9VteMAIZH/uN9Mi/XkKBnwRWgqp7DLN8+CD3 bZ9yxQ1AAEesabJGAs+MQNJRao6hyW0DbFPF7Ks876DinGwT0mxIib+Ge/E/FkUUaisZPiGzALQ XpRuJRs4cYDZvTJFnp6H1/dQKJlHrZWtxsVSg2Ie7X3FJyU+R40K9zpum4OdMAzr+orItZKyZJJ 4PVzaa0ShgyaDd3rq6buqf07s7RviK3Ka+jknn1v3Y9NFbI5h3Z8IckajN9J0LNsAiepgdtPRmI xyZmW7AGesLNwHPDq9L1DY3v1y9MH2bmM/2vHpxH5PU+q1nN5goP31Y1jsfB7prxJsSO+0ToMnr tc3n+NFVjyjbBA1MURCnM1oeT+ITXBKMuKQQw+ogvCwIb4O+27rOaGiXwqPPyMJuZMmnhS+ATR8 Kk2D6tmj60jdvgI9OhpFB5CfxW8T14EmQX5m2WRdKdMf8KmlrJiTQlL9nYwJ6D+Ks1t2dtRrdwv rWEzIHNgwjMqt8h+UFeDWNxRC/nzffjJRF8nK48YTGU8EAEO584fcM+9e6CdHSCpd0nEs1+X7MQ m1CR/ItEsDAOhJeX/qpqA X-Received: by 2002:a05:6830:6686:b0:7f4:eb76:881d with SMTP id 46e09a7af769-81780693ca6mr10527443a34.5.1790437785355; Sat, 26 Sep 2026 08:49:45 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:44 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:49:07 -0600 Subject: [PATCH net v4 12/15] xsk: allow drivers to retain DMA mappings independently of pools Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-12-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [-1.16 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[172.234.253.10:from]; SUSPICIOUS_RECIPS(1.50)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; MAILLIST(-0.15)[generic]; MIME_GOOD(-0.10)[text/plain]; BAD_REP_POLICIES(0.10)[]; HAS_LIST_UNSUB(-0.01)[]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; DBL_BLOCKED_OPENRESOLVER(0.00)[sea.lore.kernel.org:rdns,sea.lore.kernel.org:helo]; FROM_HAS_DN(0.00)[]; PRECEDENCE_BULK(0.00)[]; TAGGED_RCPT(0.00)[kernel,netdev]; FORGED_SENDER_MAILLIST(0.00)[]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; FREEMAIL_CC(0.00)[bootlin.com,d-silva.org,kernel.org,vger.kernel.org,st-md-mailman.stormreply.com,lists.infradead.org,uniontech.com,oss.qualcomm.com,163.com,gmail.com,lists.linux.dev]; RCVD_VIA_SMTP_AUTH(0.00)[]; FROM_NEQ_ENVFROM(0.00)[jameshilliard1@gmail.com,linux-sunxi@lists.linux.dev]; ASN(0.00)[asn:63949, ipnet:172.234.224.0/19, country:SG]; R_SPF_ALLOW(0.00)[+ip4:172.234.253.10]; RCPT_COUNT_GT_50(0.00)[54]; FREEMAIL_FROM(0.00)[gmail.com]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[74.125.231.166:received,100.90.174.1:received]; TO_DN_SOME(0.00)[]; RCVD_COUNT_FIVE(0.00)[6]; FREEMAIL_TO(0.00)[armlinux.org.uk,lunn.ch,gmail.com,davemloft.net,kernel.org,redhat.com,nxp.com,bootlin.com,foss.st.com,loongson.cn,iogearbox.net,fomichev.me,quicinc.com,synopsys.com,intel.com,googlemail.com,nvidia.com,sholland.org]; MIME_TRACE(0.00)[0:+]; TAGGED_FROM(0.00)[bounces-26277-noreply=patchwork.local]; RECEIVED_SPAMHAUS_PBL(0.00)[174.29.1.49:received]; MID_RHS_MATCH_FROM(0.00)[]; RCVD_TLS_LAST(0.00)[]; RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[172.234.253.10:from] X-Rspamd-Queue-Id: 5EFB91C2AA6 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= An unsuccessful device shutdown does not make its DMA memory safe to unmap. AF_XDP pool removal must nevertheless complete: the last socket release invokes the driver detach callback and then destroys the pool, regardless of the callback's return value. Provide an independent reference to the existing DMA mapping and its UMEM pages. A driver can take it while installing rings and release it after DMA has actually stopped. It does not retain pool metadata, fill or completion rings, or the pool users reference which triggers teardown. Keep the DMA device and the mapping's netdev lookup key allocated, without taking a netdev usage reference that would prevent unregister. Save the mapping attributes and unmap before dropping the last retained UMEM reference. Mapping reference operations are serialized by RTNL, like the existing mapping list operations. Signed-off-by: James Hilliard --- include/net/xdp_sock_drv.h | 23 ++++++++++++++++++++++ include/net/xsk_buff_pool.h | 4 ++++ net/xdp/xsk_buff_pool.c | 48 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/include/net/xdp_sock_drv.h b/include/net/xdp_sock_drv.h index d94aeb506379..819410797db3 100644 --- a/include/net/xdp_sock_drv.h +++ b/include/net/xdp_sock_drv.h @@ -95,6 +95,20 @@ static inline void xsk_pool_dma_unmap(struct xsk_buff_pool *pool, xp_dma_unmap(pool, attrs); } +/* RTNL must be held. Keep DMA mappings and pinned pages independently of + * the socket/pool lifetime, for rings whose DMA shutdown can fail. + * This does not keep pool metadata alive or postpone the detach callback. + */ +static inline struct xsk_dma_map *xsk_pool_dma_get(struct xsk_buff_pool *pool) +{ + return xp_dma_get(pool); +} + +static inline void xsk_pool_dma_put(struct xsk_dma_map *dma_map) +{ + xp_dma_put(dma_map); +} + static inline int xsk_pool_dma_map(struct xsk_buff_pool *pool, struct device *dev, unsigned long attrs) { @@ -432,6 +446,15 @@ static inline void xsk_pool_dma_unmap(struct xsk_buff_pool *pool, { } +static inline struct xsk_dma_map *xsk_pool_dma_get(struct xsk_buff_pool *pool) +{ + return NULL; +} + +static inline void xsk_pool_dma_put(struct xsk_dma_map *dma_map) +{ +} + static inline int xsk_pool_dma_map(struct xsk_buff_pool *pool, struct device *dev, unsigned long attrs) { diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h index a7df573784fd..25a09b098703 100644 --- a/include/net/xsk_buff_pool.h +++ b/include/net/xsk_buff_pool.h @@ -38,6 +38,8 @@ struct xsk_dma_map { dma_addr_t *dma_pages; struct device *dev; struct net_device *netdev; + struct xdp_umem *umem; + unsigned long attrs; refcount_t users; struct list_head list; /* Protected by the RTNL_LOCK */ u32 dma_pages_cnt; @@ -143,6 +145,8 @@ void xp_fill_cb(struct xsk_buff_pool *pool, struct xsk_cb_desc *desc); int xp_dma_map(struct xsk_buff_pool *pool, struct device *dev, unsigned long attrs, struct page **pages, u32 nr_pages); void xp_dma_unmap(struct xsk_buff_pool *pool, unsigned long attrs); +struct xsk_dma_map *xp_dma_get(struct xsk_buff_pool *pool); +void xp_dma_put(struct xsk_dma_map *dma_map); struct xdp_buff *xp_alloc(struct xsk_buff_pool *pool); u32 xp_alloc_batch(struct xsk_buff_pool *pool, struct xdp_buff **xdp, u32 max); bool xp_can_alloc(struct xsk_buff_pool *pool, u32 count); diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index c58f56f24a9c..8f95eddc91fc 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -360,7 +360,8 @@ static struct xsk_dma_map *xp_find_dma_map(struct xsk_buff_pool *pool) } static struct xsk_dma_map *xp_create_dma_map(struct device *dev, struct net_device *netdev, - u32 nr_pages, struct xdp_umem *umem) + u32 nr_pages, struct xdp_umem *umem, + unsigned long attrs) { struct xsk_dma_map *dma_map; @@ -376,6 +377,8 @@ static struct xsk_dma_map *xp_create_dma_map(struct device *dev, struct net_devi dma_map->netdev = netdev; dma_map->dev = dev; + dma_map->umem = umem; + dma_map->attrs = attrs; dma_map->dma_pages_cnt = nr_pages; refcount_set(&dma_map->users, 1); list_add(&dma_map->list, &umem->xsk_dma_list); @@ -430,6 +433,47 @@ void xp_dma_unmap(struct xsk_buff_pool *pool, unsigned long attrs) } EXPORT_SYMBOL(xp_dma_unmap); +struct xsk_dma_map *xp_dma_get(struct xsk_buff_pool *pool) +{ + struct xsk_dma_map *dma_map; + + ASSERT_RTNL(); + if (!pool->dma_pages) + return NULL; + dma_map = xp_find_dma_map(pool); + if (WARN_ON_ONCE(!dma_map)) + return NULL; + + refcount_inc(&dma_map->users); + xdp_get_umem(dma_map->umem); + get_device(dma_map->dev); + /* Keep the mapping's lookup key alive without preventing unregister. */ + get_device(&dma_map->netdev->dev); + return dma_map; +} +EXPORT_SYMBOL_GPL(xp_dma_get); + +void xp_dma_put(struct xsk_dma_map *dma_map) +{ + struct net_device *netdev; + struct xdp_umem *umem; + struct device *dev; + + ASSERT_RTNL(); + if (!dma_map) + return; + dev = dma_map->dev; + netdev = dma_map->netdev; + umem = dma_map->umem; + if (refcount_dec_and_test(&dma_map->users)) + __xp_dma_unmap(dma_map, dma_map->attrs); + /* Unmap before the final reference can unpin the UMEM pages. */ + xdp_put_umem(umem, false); + put_device(&netdev->dev); + put_device(dev); +} +EXPORT_SYMBOL_GPL(xp_dma_put); + static void xp_check_dma_contiguity(struct xsk_dma_map *dma_map) { u32 i; @@ -487,7 +531,7 @@ int xp_dma_map(struct xsk_buff_pool *pool, struct device *dev, return 0; } - dma_map = xp_create_dma_map(dev, pool->netdev, nr_pages, pool->umem); + dma_map = xp_create_dma_map(dev, pool->netdev, nr_pages, pool->umem, attrs); if (!dma_map) return -ENOMEM; From patchwork Sat Sep 26 15:49:08 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3485 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 1113D1C2AA6 for ; Sat, 26 Sep 2026 18:00:23 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26278-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26278-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 31F733814D for ; Sat, 26 Sep 2026 15:51:33 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 58020484257; Sat, 26 Sep 2026 15:49:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Edg1BOqR" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f41.google.com (mail-oo2-f41.google.com [74.125.231.169]) (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 8FC7237E5EB for ; Sat, 26 Sep 2026 15:49:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437795; cv=none; b=VLzu+B7iMaJDnNPA+lUQQFOHShJZdjnpDWzIRpcYf4D4qcMLf6w5EdLwDGT1LNRNYTLjIepf/bte+EQUuUlWOI2fwKSiaq4RGymobCjGIWnyoII6Cvc5JrTepRFlPl6IAjf4lTRkS5455sMykGxf0K7wtwYuw3gJ2trODIMS7Dk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437795; c=relaxed/simple; bh=MiaWJeXkOaEezEBn/b6Qw27INN6scjJvGVQ8ojwYegU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cEqnGmH4rPYGuWuIPERfyk1kMIgXivTzEEcrkoGtyKQHbHAWHBbSq0CWP3pdIwrlJl4+s1tdaQDIXt8ANZxrBYSbw+FFbSEK8UeBFfvk9eyamCUqemRjqlb9yURtwLRNzAJG5xbLJWj6b3zWUWfE02VjYxATmzuLf2wCuTvuZ3U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Edg1BOqR; arc=none smtp.client-ip=74.125.231.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f41.google.com with SMTP id 006d021491bc7-6d06a12d360so1044412eaf.2 for ; Sat, 26 Sep 2026 08:49:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437788; x=1791042588; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=F6XM2pMuwrLeRk4l/rBwOycnAd6l+sw0gT1Put9QZOo=; b=Edg1BOqRqDeLBrwVOwuW7rThmqwA+UPkkSllesryMaVYjSQsD8ZWnKflq6zbJD65gH HhJdVmj09X00hEWpfCYLjsegylm0HeAxLk7fo6X7VmShJ9Ff5lvl87nmP7x80k6Io8xW SiAig+LGxVyX6P55lBl9EeMuzQQOP3ERgHwDaQt+gn94cEGnAox++NQBuAc6XFzjSxPf xZu7gQndRb+wDt08PGDsI8J+HhcOKjxmQ8jMhLdm3+0cJ6aVXumX3MSb9Dr9ggC9O8W+ 6WAl/dv4kedX/NVTL2SfJBUYElIjhEBMmV3wh5tacqOU0FXqM31UKlyqsLsLXlaIvNNZ KyPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437788; x=1791042588; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=F6XM2pMuwrLeRk4l/rBwOycnAd6l+sw0gT1Put9QZOo=; b=d+41ODvJh8zbmX0xwcE5w3jlwo3Id7Va+3JXETed0vlJxVdrVQTNRTo2AQZcVsXbfl /IUCtjDqIPoOS1j6kveYjdMxgJuQFrV7Fq7y8IeKLJh2lIIq0590FqBi7HbAQ2cMWri1 2dZEvpd54Fr0zqbxUjA3+UCP4VGl6Wn2DdgBIxjqrH9XGTdWY9DyzFzaONSOc/99BSOS 3Ci1vsgfO9qctVgKQ2a+W7MEgzhl3REflBpIY+qXlnoPBS6LBRlgRib5yHFrFLfKCX5D uWh9rLR3HPCj4FpeM49CpC7GJEhZw9yJP1xVTXTLW0ye8RFKSoUchkAeAm/uMXCJ7K+N P1Hw== X-Forwarded-Encrypted: i=1; AKwUvByy42r5wiwHVlblJIPNIV10zvFRwFTrq3AiDx/YM8hIMxDCsPzmkWytoTdLzv3D66GxJrV492VKJItJvg==@lists.linux.dev X-Gm-Message-State: AFuF++n//yFIcZr7tpgFCb6bO+9TTEzi9Tb7oafIaANVAhFupZNn4HZ1 68flty5WIg252pWFZTjgiZYdp06U1dviaZxdP1MNjicC6pt4mrArq7li X-Gm-Gg: AYBFou25l05ophgGhswkS9LV0wF/eHc0dePKzNZjLP4Kr3MveVtl0OHHZBw+ayyAkTZ svPPc/TYpzNDDwtCTJwckKnnuEFqIxronyAAg/sncWY/5urWmLi43H9WHE02fMdMzzulaxuph2F PeW9lvf24fADbk6mchLvZSlADJpNXodt5cPW/PH505MAX2iXAPfTmz1EdYVW5iDtqcAgUwQ2M8d uyW+GK4kyUYFNcUo7Jf9UcjlLrvZHd3WUNHREVAYGMNdVjfvASQCMRNseZdz9f1any2l3VP6Dce /TU84/v2mzV4YDD9Oj4mh0rDsLQiS5KFUDpEEDphS2ej8g5JGOa8MPIiWzD9KOXuzye1uFIbXkV Zvdq6jC0z1Pi4kYVIobWMJc9StHQ0al+y0Qao90lfRmaKxczwouz4pvswK9Scrf2/m1lwwXcz0T NjWe3IAnua93Dkefw/PtpQmrEJ++67Ljo7WuEY4cc+FaKb3EJFmsNK0d+clgAxKs+AvpFdcUet+ yJtgMLEBvOkPhXskx29nquXNcPjFC6RgbdBkSwwSdjwIcqvbMwkd8q/Tch35I170z7BTpcOCc3y iupt3zuc5oNZ9u+Rl4oyM4QO9arwEZT9KivgmqxtEVz6/BSG05O//1M6/ZILHxUUG/6T2xM1Vl9 58xNOInJKZjzH03C7vj9C X-Received: by 2002:a05:6820:f09:b0:6d7:deb5:b41c with SMTP id 006d021491bc7-6d7deb5cd5emr721555eaf.44.1790437787505; Sat, 26 Sep 2026 08:49:47 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:47 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:49:08 -0600 Subject: [PATCH net v4 13/15] net: stmmac: retain DMA memory until hardware shutdown completes Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-13-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Clearing the DMA start bits requests a stop but need not complete an in-flight frame or descriptor writeback. Ordinary release, live XDP/XSK replacement and late open failures currently free the rings and buffers immediately afterwards. An XSK socket can then unmap and unpin the UMEM while hardware still has its addresses. Wait for stopped process states on the legacy and Allwinner DMA engines. For GMAC4 configurations represented by DSR0, also require its bus-busy bits to clear. Fall back to a completed global reset if the idle wait times out or the integration has no supported idle indication, including XGMAC and GMAC4 configurations with more than three channels. Prepare the PHY receive clock for that reset and restore PHC configuration before a live XDP restart which required it. As with MTU reset, continuous PHC time is not preserved on this fallback. Track configurations exposed to DMA separately from software datapath ownership. If both idle and reset fail, keep the rings, DMA mappings and backing memory until a subsequent successful reset. Do not overwrite retained buffers in an XDP reopen or change their ring/channel geometry. Record failed open replacements too, including ones which are no longer the active configuration pointer. A successful down/up reset retires them. Take independent XSK DMA/UMEM references when initializing RX rings. On failed shutdown, return only software buffer metadata to the departing pool and remove every driver pool pointer before socket teardown proceeds. The mapping references keep the actual memory pinned after the pool is gone. A later successful reset releases these references after freeing the descriptors and buffers that used them. Defer hard TX error recovery to process context instead of rewriting a ring in the IRQ handler immediately after clearing ST. Likewise, move resume-time TX cleanup and descriptor rebuilding after the reset succeeds. Do not let queued recovery work reopen an administratively closed device. There is no generic, guaranteed isolation mechanism across all stmmac integrations. If hardware still cannot stop or reset at removal, deliberately retain the DMA allocations and report the quarantine rather than expose recycled memory to DMA. Such an unrecoverable device can therefore retain memory, including pinned UMEM, until reboot. Rebuild retained RX descriptors after reset with buffer addresses and chain links written before ownership. GMAC4 and XGMAC secondary-address programming overwrites des3, so publishing OWN first would lose it. Fixes: ac746c8520d9 ("net: stmmac: enhance XDP ZC driver level switching performance") Fixes: bba2556efad6 ("net: stmmac: Enable RX via AF_XDP zero-copy") Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 17 + .../net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 1 + drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c | 1 + drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 2 + drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h | 6 + drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c | 18 + drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h | 2 + drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c | 20 + drivers/net/ethernet/stmicro/stmmac/hwif.h | 4 + drivers/net/ethernet/stmicro/stmmac/stmmac.h | 11 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 427 ++++++++++++++++----- 11 files changed, 415 insertions(+), 94 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index 48c52eb96233..422d3df21ed9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -163,6 +163,7 @@ static const struct emac_variant emac_variant_h6 = { #define EMAC_TX_CUR_DESC 0xB4 #define EMAC_TX_CUR_BUF 0xB8 #define EMAC_RX_DMA_STA 0xC0 +#define EMAC_DMA_STATE_MASK GENMASK(2, 0) #define EMAC_RX_CUR_DESC 0xC4 #define EMAC_RX_CUR_BUF 0xC8 @@ -425,6 +426,21 @@ static void sun8i_dwmac_dma_stop_rx(struct stmmac_priv *priv, writel(v, ioaddr + EMAC_RX_CTL1); } +static int sun8i_dwmac_dma_wait_idle(struct stmmac_priv *priv, + void __iomem *ioaddr) +{ + u32 value; + int ret; + + /* STOP (0) follows the frame transfer and descriptor close states. */ + ret = readl_poll_timeout(ioaddr + EMAC_TX_DMA_STA, value, + !(value & EMAC_DMA_STATE_MASK), 100, 100000); + if (ret) + return ret; + return readl_poll_timeout(ioaddr + EMAC_RX_DMA_STA, value, + !(value & EMAC_DMA_STATE_MASK), 100, 100000); +} + static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_extra_stats *x, u32 chan, @@ -553,6 +569,7 @@ static void sun8i_dwmac_dma_operation_mode_tx(struct stmmac_priv *priv, static const struct stmmac_dma_ops sun8i_dwmac_dma_ops = { .reset = sun8i_dwmac_dma_reset, + .wait_idle = sun8i_dwmac_dma_wait_idle, .init = sun8i_dwmac_dma_init, .init_rx_chan = sun8i_dwmac_dma_init_rx, .init_tx_chan = sun8i_dwmac_dma_init_tx, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index 3ac7a7949529..4cb7e6c16bdd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -252,6 +252,7 @@ static void dwmac1000_rx_watchdog(struct stmmac_priv *priv, const struct stmmac_dma_ops dwmac1000_dma_ops = { .reset = dwmac_dma_reset, + .wait_idle = dwmac_dma_wait_idle, .init_chan = dwmac1000_dma_init_channel, .init_rx_chan = dwmac1000_dma_init_rx, .init_tx_chan = dwmac1000_dma_init_tx, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c index 12b2bf2d739a..5ffd3c1471c4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c @@ -108,6 +108,7 @@ static void dwmac100_dma_diagnostic_fr(struct stmmac_extra_stats *x, const struct stmmac_dma_ops dwmac100_dma_ops = { .reset = dwmac_dma_reset, + .wait_idle = dwmac_dma_wait_idle, .init = dwmac100_dma_init, .init_rx_chan = dwmac100_dma_init_rx, .init_tx_chan = dwmac100_dma_init_tx, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 23ffe1adcd0d..fb0a4e156a7b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -568,6 +568,7 @@ static int dwmac4_enable_tbs(struct stmmac_priv *priv, void __iomem *ioaddr, const struct stmmac_dma_ops dwmac4_dma_ops = { .reset = dwmac4_dma_reset, + .wait_idle = dwmac4_dma_wait_idle, .init = dwmac4_dma_init, .init_chan = dwmac4_dma_init_channel, .deinit_chan = dwmac4_dma_deinit_channel, @@ -598,6 +599,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = { const struct stmmac_dma_ops dwmac410_dma_ops = { .reset = dwmac4_dma_reset, + .wait_idle = dwmac4_dma_wait_idle, .init = dwmac4_dma_init, .init_chan = dwmac410_dma_init_channel, .deinit_chan = dwmac410_dma_deinit_channel, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h index 43b036d4e95b..9352107204eb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h @@ -10,6 +10,12 @@ #ifndef __DWMAC4_DMA_H__ #define __DWMAC4_DMA_H__ +int dwmac4_dma_wait_idle(struct stmmac_priv *priv, void __iomem *ioaddr); + +#define DMA_DEBUG_STATUS0 0x0000100c +#define DMA_DEBUG_BUS_BUSY GENMASK(1, 0) +#define DMA_DEBUG_CH_STATE(ch) (GENMASK(15, 8) << ((ch) * 8)) + /* Define the max channel number used for tx (also rx). * dwmac4 accepts up to 8 channels for TX (and also 8 channels for RX */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index a0249715fafa..9af0565a9bca 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -26,6 +26,24 @@ int dwmac4_dma_reset(void __iomem *ioaddr) 10000, 1000000); } +int dwmac4_dma_wait_idle(struct stmmac_priv *priv, void __iomem *ioaddr) +{ + u32 channels = max(priv->plat->rx_queues_to_use, + priv->plat->tx_queues_to_use); + u32 mask = DMA_DEBUG_BUS_BUSY; + u32 value, chan; + + /* DSR0 describes channels 0..2 and outstanding AXI transactions. + * Other debug-register layouts require a successful reset instead. + */ + if (channels > 3) + return -EOPNOTSUPP; + for (chan = 0; chan < channels; chan++) + mask |= DMA_DEBUG_CH_STATE(chan); + return readl_poll_timeout(ioaddr + DMA_DEBUG_STATUS0, value, + !(value & mask), 100, 100000); +} + void dwmac4_set_rx_tail_ptr(struct stmmac_priv *priv, void __iomem *ioaddr, u32 tail_ptr, u32 chan) { diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index e1c37ac2c99d..970495bccfd2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -11,6 +11,8 @@ #ifndef __DWMAC_DMA_H__ #define __DWMAC_DMA_H__ +int dwmac_dma_wait_idle(struct stmmac_priv *priv, void __iomem *ioaddr); + /* DMA CRS Control and Status Register Mapping */ #define DMA_BUS_MODE 0x00001000 /* Bus Mode */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index a0383f9486c2..bb907db8fca1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -27,6 +27,26 @@ int dwmac_dma_reset(void __iomem *ioaddr) 10000, 200000); } +int dwmac_dma_wait_idle(struct stmmac_priv *priv, void __iomem *ioaddr) +{ + u32 channels = max(priv->plat->rx_queues_to_use, + priv->plat->tx_queues_to_use); + u32 value, chan; + int ret; + + /* CSR5 process states, not the latched process-stopped interrupts. + * Stopped is reached after the outstanding descriptor writeback. + */ + for (chan = 0; chan < channels; chan++) { + ret = readl_poll_timeout(ioaddr + DMA_CHAN_STATUS(chan), value, + !(value & (DMA_STATUS_TS_MASK | DMA_STATUS_RS_MASK)), + 100, 100000); + if (ret) + return ret; + } + return 0; +} + /* CSR1 enables the transmit DMA to check for new descriptor */ void dwmac_enable_dma_transmission(void __iomem *ioaddr, u32 chan) { diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 857f7562c6c6..3bc4d955ef5f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -205,6 +205,8 @@ struct stmmac_dma_ops { u32 chan); void (*stop_rx)(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan); + /* Called after stopping every channel; must also drain bus accesses. */ + int (*wait_idle)(struct stmmac_priv *priv, void __iomem *ioaddr); int (*dma_interrupt)(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_extra_stats *x, u32 chan, u32 dir); /* If supported then get the optional core features */ @@ -269,6 +271,8 @@ struct stmmac_dma_ops { stmmac_do_void_callback(__priv, dma, start_rx, __priv, __args) #define stmmac_stop_rx(__priv, __args...) \ stmmac_do_void_callback(__priv, dma, stop_rx, __priv, __args) +#define stmmac_dma_wait_idle(__priv, __args...) \ + stmmac_do_callback(__priv, dma, wait_idle, __priv, __args) #define stmmac_dma_interrupt_status(__priv, __args...) \ stmmac_do_callback(__priv, dma, dma_interrupt, __priv, __args) #define stmmac_get_hw_feature(__priv, __args...) \ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 6abb0d1d4095..e15bab9accce 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -120,6 +120,7 @@ struct stmmac_rx_queue { u32 queue_index; struct xdp_rxq_info xdp_rxq; struct xsk_buff_pool *xsk_pool; + struct xsk_dma_map *xsk_dma; struct page_pool *page_pool; struct stmmac_rx_buffer *buf_pool; struct stmmac_priv *priv_data; @@ -223,6 +224,10 @@ struct stmmac_rfs_entry { }; struct stmmac_dma_conf { + /* RTNL: all configurations exposed to DMA survive until stop/reset. */ + struct list_head list; + bool dma_owned; + bool retired; unsigned int dma_buf_sz; /* RX Queue */ @@ -266,11 +271,11 @@ struct stmmac_msi { }; enum stmmac_datapath_state { - /* No IRQs or DMA allocations owned by a successful open. */ + /* No IRQs or enabled NAPI; failed DMA shutdown may retain memory. */ STMMAC_DATAPATH_DOWN, /* Resources allocated, NAPI enabled. */ STMMAC_DATAPATH_RUNNING, - /* Resources retained, NAPI and DMA stopped; also after failed resume. */ + /* Resources retained, NAPI disabled, DMA stop requested. */ STMMAC_DATAPATH_SUSPENDED, }; @@ -297,6 +302,8 @@ struct stmmac_priv { struct mutex lock; struct stmmac_dma_conf *dma_conf; + struct list_head dma_confs; + bool dma_reset_needed; /* IRQ/DMA ownership and NAPI state, serialized by RTNL. */ enum stmmac_datapath_state datapath; /* Core sleep sequence completed, independently of datapath ownership. */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index edcb9a025385..4d70645c4a4f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -988,6 +988,24 @@ static void stmmac_block_ptp(struct stmmac_priv *priv, bool block) write_unlock_irqrestore(&priv->ptp_lock, flags); } +static int stmmac_restore_timestamping(struct stmmac_priv *priv) +{ + int ret; + + if (priv->plat->ptp_clk_freq_config) + priv->plat->ptp_clk_freq_config(priv); + if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp) || + !priv->plat->clk_ptp_rate) + return 0; + + ret = stmmac_init_tstamp_counter(priv, priv->systime_flags); + if (ret) + return ret; + if (priv->plat->flags & STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY) + stmmac_hwtstamp_correct_latency(priv, priv); + return stmmac_ptp_restore(priv); +} + static void stmmac_legacy_serdes_power_down(struct stmmac_priv *priv) { if (priv->plat->serdes_powerdown && priv->legacy_serdes_is_powered) @@ -1665,24 +1683,10 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv, stmmac_clear_tx_descriptors(priv, dma_conf, queue); } -/** - * stmmac_init_rx_buffers - init the RX descriptor buffer. - * @priv: driver private structure - * @dma_conf: structure to take the dma data - * @p: descriptor pointer - * @i: descriptor index - * @flags: gfp flag - * @queue: RX queue index - * Description: this function is called to allocate a receive buffer, perform - * the DMA mapping and init the descriptor. - */ -static int stmmac_init_rx_buffers(struct stmmac_priv *priv, - struct stmmac_dma_conf *dma_conf, - struct dma_desc *p, - int i, gfp_t flags, u32 queue) +static int stmmac_alloc_rx_buffer(struct stmmac_priv *priv, + struct stmmac_rx_queue *rx_q, + struct stmmac_rx_buffer *buf) { - struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; - struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN); if (priv->dma_cap.host_dma_width <= 32) @@ -1699,19 +1703,49 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp); if (!buf->sec_page) return -ENOMEM; - buf->sec_addr = page_pool_get_dma_addr(buf->sec_page); - stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true); - } else { - buf->sec_page = NULL; - stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, false); } + return 0; +} + +static void stmmac_init_rx_buffer_desc(struct stmmac_priv *priv, + struct stmmac_dma_conf *dma_conf, + struct dma_desc *p, + struct stmmac_rx_buffer *buf) +{ + if (buf->sec_page) + buf->sec_addr = page_pool_get_dma_addr(buf->sec_page); + stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, !!buf->sec_page); buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset; stmmac_set_desc_addr(priv, p, buf->addr); if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB) stmmac_init_desc3(priv, p); +} + +/** + * stmmac_init_rx_buffers - allocate a receive buffer and init its descriptor + * @priv: driver private structure + * @dma_conf: structure to take the dma data + * @p: descriptor pointer + * @i: descriptor index + * @flags: gfp flag + * @queue: RX queue index + */ +static int stmmac_init_rx_buffers(struct stmmac_priv *priv, + struct stmmac_dma_conf *dma_conf, + struct dma_desc *p, + int i, gfp_t flags, u32 queue) +{ + struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + int ret; + + ret = stmmac_alloc_rx_buffer(priv, rx_q, buf); + if (ret) + return ret; + stmmac_init_rx_buffer_desc(priv, dma_conf, p, buf); return 0; } @@ -1923,6 +1957,9 @@ static int __init_dma_rx_desc_rings(struct stmmac_priv *priv, rx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue); if (rx_q->xsk_pool) { + rx_q->xsk_dma = xsk_pool_dma_get(rx_q->xsk_pool); + if (!rx_q->xsk_dma) + return -EINVAL; ret = xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq, MEM_TYPE_XSK_BUFF_POOL, NULL); if (ret) @@ -2168,6 +2205,41 @@ static void stmmac_free_tx_skbufs(struct stmmac_priv *priv) dma_free_tx_skbufs(priv, priv->dma_conf, queue); } +/* Only after a successful DMA reset, and with all RX buffers prepared. */ +static void stmmac_reinit_dma_desc(struct stmmac_priv *priv) +{ + struct stmmac_dma_conf *dma_conf = priv->dma_conf; + u32 queue, i; + + stmmac_free_tx_skbufs(priv); + stmmac_reset_queues_param(priv); + init_dma_tx_desc_rings(priv->dev, dma_conf); + + for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) { + struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + + if (rx_q->state_saved) + dev_kfree_skb_any(rx_q->state.skb); + rx_q->state.skb = NULL; + rx_q->state_saved = 0; + rx_q->rx_count_frames = 0; + rx_q->buf_alloc_num = dma_conf->dma_rx_size; + + for (i = 0; i < dma_conf->dma_rx_size; i++) + stmmac_init_rx_buffer_desc(priv, dma_conf, + stmmac_get_rx_desc(priv, rx_q, i), + &rx_q->buf_pool[i]); + + if (priv->descriptor_mode == STMMAC_CHAIN_MODE) + stmmac_mode_init(priv, stmmac_get_rx_desc(priv, rx_q, 0), + rx_q->dma_rx_phy, dma_conf->dma_rx_size, + priv->extend_desc); + } + + /* Address programming can overwrite OWN in GMAC4/XGMAC descriptors. */ + stmmac_clear_descriptors(priv, dma_conf); +} + /** * __free_dma_rx_desc_resources - free RX dma desc resources (per queue) * @priv: private structure @@ -2214,6 +2286,9 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, kfree(rx_q->buf_pool); if (rx_q->page_pool) page_pool_destroy(rx_q->page_pool); + if (rx_q->xsk_dma) + xsk_pool_dma_put(rx_q->xsk_dma); + rx_q->xsk_dma = NULL; rx_q->buf_pool = NULL; rx_q->page_pool = NULL; rx_q->dma_erx = NULL; @@ -2223,11 +2298,10 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, static void free_dma_rx_desc_resources(struct stmmac_priv *priv, struct stmmac_dma_conf *dma_conf) { - u8 rx_count = priv->plat->rx_queues_to_use; u8 queue; /* Free RX queue resources */ - for (queue = 0; queue < rx_count; queue++) + for (queue = 0; queue < MTL_MAX_RX_QUEUES; queue++) __free_dma_rx_desc_resources(priv, dma_conf, queue); } @@ -2274,11 +2348,10 @@ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, static void free_dma_tx_desc_resources(struct stmmac_priv *priv, struct stmmac_dma_conf *dma_conf) { - u8 tx_count = priv->plat->tx_queues_to_use; u8 queue; /* Free TX queue resources */ - for (queue = 0; queue < tx_count; queue++) + for (queue = 0; queue < MTL_MAX_TX_QUEUES; queue++) __free_dma_tx_desc_resources(priv, dma_conf, queue); } @@ -2481,14 +2554,36 @@ static int alloc_dma_desc_resources(struct stmmac_priv *priv, return ret; } -/** - * free_dma_desc_resources - free dma desc resources - * @priv: private structure - * @dma_conf: structure to take the dma data - */ +static void stmmac_detach_xsk_buffers(struct stmmac_priv *priv, + struct stmmac_dma_conf *dma_conf) +{ + u32 queue; + + /* Socket teardown cannot retain the pool itself. Drop software-only + * references, but keep each xsk_dma reference: hardware can still reach + * the mapped UMEM pages even after the pool and its heads are freed. + */ + for (queue = 0; queue < MTL_MAX_RX_QUEUES; queue++) { + struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + + if (!rx_q->xsk_pool) + continue; + dma_free_rx_xskbufs(priv, dma_conf, queue); + xsk_pool_set_rxq_info(rx_q->xsk_pool, NULL); + rx_q->xsk_pool = NULL; + } + for (queue = 0; queue < MTL_MAX_TX_QUEUES; queue++) + dma_conf->tx_queue[queue].xsk_pool = NULL; +} + static void free_dma_desc_resources(struct stmmac_priv *priv, struct stmmac_dma_conf *dma_conf) { + if (dma_conf->dma_owned) { + stmmac_detach_xsk_buffers(priv, dma_conf); + return; + } + /* Release the DMA TX socket buffers */ free_dma_tx_desc_resources(priv, dma_conf); @@ -2498,6 +2593,42 @@ static void free_dma_desc_resources(struct stmmac_priv *priv, free_dma_rx_desc_resources(priv, dma_conf); } +static void stmmac_put_dma_conf(struct stmmac_priv *priv, + struct stmmac_dma_conf *dma_conf) +{ + free_dma_desc_resources(priv, dma_conf); + if (dma_conf->dma_owned) { + dma_conf->retired = true; + return; + } + list_del(&dma_conf->list); + kfree(dma_conf); +} + +/* A successful global reset is also the retirement fence for configurations + * retained by a previous failed close, open, or MTU rollback. + */ +static void stmmac_dma_reset_complete(struct stmmac_priv *priv) +{ + struct stmmac_dma_conf *dma_conf, *next; + + list_for_each_entry_safe(dma_conf, next, &priv->dma_confs, list) { + dma_conf->dma_owned = false; + if (dma_conf->retired) + stmmac_put_dma_conf(priv, dma_conf); + } +} + +static bool stmmac_dma_busy(struct stmmac_priv *priv) +{ + struct stmmac_dma_conf *dma_conf; + + list_for_each_entry(dma_conf, &priv->dma_confs, list) + if (dma_conf->dma_owned) + return true; + return false; +} + /** * stmmac_mac_enable_rx_queues - Enable MAC rx queues * @priv: driver private structure @@ -2524,6 +2655,7 @@ static void stmmac_mac_enable_rx_queues(struct stmmac_priv *priv) */ static void stmmac_start_rx_dma(struct stmmac_priv *priv, u32 chan) { + priv->dma_conf->dma_owned = true; netdev_dbg(priv->dev, "DMA RX processes started in channel %d\n", chan); stmmac_start_rx(priv, priv->ioaddr, chan); } @@ -2537,6 +2669,7 @@ static void stmmac_start_rx_dma(struct stmmac_priv *priv, u32 chan) */ static void stmmac_start_tx_dma(struct stmmac_priv *priv, u32 chan) { + priv->dma_conf->dma_owned = true; netdev_dbg(priv->dev, "DMA TX processes started in channel %d\n", chan); stmmac_start_tx(priv, priv->ioaddr, chan); } @@ -3072,25 +3205,17 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, * stmmac_tx_err - to manage the tx error * @priv: driver private structure * @chan: channel index - * Description: it cleans the descriptors and restarts the transmission - * in case of transmission errors. + * Description: stop submissions and request process-context DMA recovery. */ static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan]; - netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan)); - stmmac_stop_tx_dma(priv, chan); - dma_free_tx_skbufs(priv, priv->dma_conf, chan); - stmmac_clear_tx_descriptors(priv, priv->dma_conf, chan); - stmmac_reset_tx_queue(priv, chan); - stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, - tx_q->dma_tx_phy, chan); - stmmac_start_tx_dma(priv, chan); - priv->xstats.tx_errors++; - netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, chan)); + /* Recovery must wait for DMA before freeing or rewriting descriptors. + * Use the process-context reset path, not teardown in hard IRQ context. + */ + stmmac_global_err(priv); } /** @@ -3326,12 +3451,13 @@ static int stmmac_prereset_configure(struct stmmac_priv *priv) /** * stmmac_init_dma_engine - DMA init. * @priv: driver private structure + * @reinit: rebuild the retained rings after a successful reset * Description: * It inits the DMA invoking the specific MAC/GMAC callback. * Some DMA parameters can be passed from the platform; * in case of these are not passed a default is kept for the MAC or GMAC. */ -static int stmmac_init_dma_engine(struct stmmac_priv *priv) +static int stmmac_init_dma_engine(struct stmmac_priv *priv, bool reinit) { u8 rx_channels_count = priv->plat->rx_queues_to_use; u8 tx_channels_count = priv->plat->tx_queues_to_use; @@ -3350,6 +3476,19 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) netdev_err(priv->dev, "Failed to reset the dma\n"); return ret; } + stmmac_dma_reset_complete(priv); + priv->dma_reset_needed = false; + + if (reinit) { + stmmac_reinit_dma_desc(priv); + } else if (priv->datapath == STMMAC_DATAPATH_SUSPENDED) { + /* Suspend only requested a stop. Do not modify its descriptors + * or release pending TX buffers until this reset has completed. + */ + stmmac_reset_queues_param(priv); + stmmac_free_tx_skbufs(priv); + stmmac_clear_descriptors(priv, priv->dma_conf); + } /* DMA Configuration */ stmmac_dma_init(priv, priv->ioaddr, priv->plat->dma_cfg); @@ -3700,6 +3839,8 @@ static bool stmmac_tso_channel_permitted(struct stmmac_priv *priv, /** * stmmac_hw_setup - setup mac in a usable state. * @dev : pointer to the device structure. + * @reinit: rebuild retained descriptor rings after the DMA reset + * @keep_ptp: restore the registered PHC's configuration before starting DMA * Description: * this is the main function to setup the HW in a usable state because the * dma engine is reset, the core registers are configured (e.g. AXI, @@ -3709,7 +3850,7 @@ static bool stmmac_tso_channel_permitted(struct stmmac_priv *priv, * 0 on success and an appropriate (-)ve integer as defined in errno.h * file on failure. */ -static int stmmac_hw_setup(struct net_device *dev) +static int stmmac_hw_setup(struct net_device *dev, bool reinit, bool keep_ptp) { struct stmmac_priv *priv = netdev_priv(dev); u8 rx_cnt = priv->plat->rx_queues_to_use; @@ -3731,7 +3872,7 @@ static int stmmac_hw_setup(struct net_device *dev) phylink_rx_clk_stop_block(priv->phylink); /* DMA initialization and SW reset */ - ret = stmmac_init_dma_engine(priv); + ret = stmmac_init_dma_engine(priv, reinit); if (ret < 0) { phylink_rx_clk_stop_unblock(priv->phylink); netdev_err(priv->dev, "%s: DMA engine initialization failed\n", @@ -3823,6 +3964,15 @@ static int stmmac_hw_setup(struct net_device *dev) stmmac_enable_tbs(priv, priv->ioaddr, enable, chan); } + if (keep_ptp) { + ret = stmmac_restore_timestamping(priv); + if (ret) + return ret; + ret = stmmac_tc_restore_est(priv); + if (ret) + return ret; + } + phylink_rx_clk_stop_block(priv->phylink); stmmac_set_hw_vlan_mode(priv, priv->hw); phylink_rx_clk_stop_unblock(priv->phylink); @@ -4169,6 +4319,7 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) __func__); return ERR_PTR(-ENOMEM); } + list_add_tail(&dma_conf->list, &priv->dma_confs); len = mtu + ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN; @@ -4219,9 +4370,8 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) return dma_conf; init_error: - free_dma_desc_resources(priv, dma_conf); alloc_error: - kfree(dma_conf); + stmmac_put_dma_conf(priv, dma_conf); return ERR_PTR(ret); } @@ -4308,6 +4458,57 @@ static int stmmac_resume_hw(struct stmmac_priv *priv) return 0; } +/* NAPI, transmitters and IRQ handlers have already been drained. Clearing + * ST/SR only requests a stop: the current frame may still access memory. + * Keep the configuration DMA-owned unless hardware acknowledges idle/reset. + */ +static void stmmac_drain_dma(struct stmmac_priv *priv) +{ + struct stmmac_dma_conf *dma_conf; + int ret; + + if (priv->hw_unavailable) + return; + stmmac_stop_all_dma(priv); + stmmac_mac_set(priv, priv->ioaddr, false); + if (!stmmac_dma_busy(priv)) + return; + + /* A failed replacement may have programmed a different topology. Only + * a global reset can acknowledge all of those retired configurations. + */ + list_for_each_entry(dma_conf, &priv->dma_confs, list) + if (dma_conf != priv->dma_conf && dma_conf->dma_owned) + goto reset; + + ret = stmmac_dma_wait_idle(priv, priv->ioaddr); + if (!ret) { + priv->dma_conf->dma_owned = false; + return; + } + + /* Some integrations do not expose a usable idle indication. Reset is + * also the fallback after a stop timeout. It needs the PHY RX clock, + * even though phylink has already stopped link resolution. + */ +reset: + phylink_prepare_resume(priv->phylink); + mutex_lock(&priv->ptp_mutex); + stmmac_block_ptp(priv, true); + priv->dma_reset_needed = true; + phylink_rx_clk_stop_block(priv->phylink); + ret = stmmac_prereset_configure(priv); + if (!ret) + ret = stmmac_reset(priv); + phylink_rx_clk_stop_unblock(priv->phylink); + if (!ret) + stmmac_dma_reset_complete(priv); + else + netdev_err(priv->dev, "DMA shutdown failed: %pe; retaining DMA memory\n", + ERR_PTR(ret)); + mutex_unlock(&priv->ptp_mutex); +} + /** * __stmmac_open - open entry point of the driver * @dev : pointer to the device structure. @@ -4339,7 +4540,7 @@ static int __stmmac_open(struct net_device *dev, stmmac_reset_queues_param(priv); - ret = stmmac_hw_setup(dev); + ret = stmmac_hw_setup(dev, false, false); if (ret < 0) { netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); goto init_error; @@ -4391,8 +4592,9 @@ static int __stmmac_open(struct net_device *dev, * phylink_start(). Keep the PHY attachment and outer PM ownership. */ phylink_stop(priv->phylink); - stmmac_stop_all_dma(priv); - stmmac_mac_set(priv, priv->ioaddr, false); + stmmac_drain_dma(priv); + /* Reset fallback may have powered the stopped PHY up for its clock. */ + phylink_stop(priv->phylink); return ret; } @@ -4436,7 +4638,7 @@ static int stmmac_open(struct net_device *dev) if (ret) goto err_serdes; - kfree(old_conf); + stmmac_put_dma_conf(priv, old_conf); /* We may have called phylink_speed_down before */ phylink_speed_up(priv->phylink); @@ -4451,8 +4653,7 @@ static int stmmac_open(struct net_device *dev) pm_runtime_put(priv->device); err_dma_resources: priv->dma_conf = old_conf; - free_dma_desc_resources(priv, dma_conf); - kfree(dma_conf); + stmmac_put_dma_conf(priv, dma_conf); return ret; } @@ -4499,23 +4700,19 @@ static void __stmmac_release(struct net_device *dev) /* Free the IRQ lines */ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); - /* TX error IRQs can restart a queue after the first quiescence. */ + /* Drain any final IRQ-triggered network activity before DMA shutdown. */ stmmac_stop_tx_queues(priv); + if (!priv->hw_unavailable && stmmac_fpe_supported(priv)) + ethtool_mmsv_stop(&priv->fpe_cfg.mmsv); - /* Stop TX/RX DMA after draining IRQ handlers which can restart it. */ - if (!priv->hw_unavailable) { - stmmac_stop_all_dma(priv); - /* Link resolution need not have reached mac_link_up() yet. */ - stmmac_mac_set(priv, priv->ioaddr, false); - } + /* Only confirmed hardware shutdown permits releasing DMA memory. */ + stmmac_drain_dma(priv); + phylink_stop(priv->phylink); /* Release and free the Rx/Tx resources */ free_dma_desc_resources(priv, priv->dma_conf); stmmac_release_ptp(priv); - - if (!priv->hw_unavailable && stmmac_fpe_supported(priv)) - ethtool_mmsv_stop(&priv->fpe_cfg.mmsv); } /** @@ -6447,8 +6644,7 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) ret = __stmmac_open(dev, dma_conf); if (ret) { priv->dma_conf = old_conf; - free_dma_desc_resources(priv, dma_conf); - kfree(dma_conf); + stmmac_put_dma_conf(priv, dma_conf); /* * Keep the administrative state and PHY/PM ownership until * ndo_stop(), but prevent use of the released data path. @@ -6458,7 +6654,7 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) return ret; } - kfree(old_conf); + stmmac_put_dma_conf(priv, old_conf); stmmac_set_rx_mode(dev); netif_device_attach(dev); @@ -7383,24 +7579,19 @@ void stmmac_xdp_release(struct net_device *dev) /* Free the IRQ lines */ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); stmmac_stop_tx_queues(priv); + if (stmmac_fpe_supported(priv)) + ethtool_mmsv_stop(&priv->fpe_cfg.mmsv); - /* Stop TX/RX DMA channels */ - stmmac_stop_all_dma(priv); + stmmac_drain_dma(priv); /* Release and free the Rx/Tx resources */ free_dma_desc_resources(priv, priv->dma_conf); - /* Disable the MAC Rx/Tx */ - stmmac_mac_set(priv, priv->ioaddr, false); - /* set trans_start so we don't get spurious * watchdogs during reset */ netif_trans_update(dev); - if (stmmac_fpe_supported(priv)) - ethtool_mmsv_stop(&priv->fpe_cfg.mmsv); - /* Keep PTP across the immediately following stmmac_xdp_open(). That * function releases it if reopening fails, before returning DOWN. */ @@ -7418,6 +7609,14 @@ int stmmac_xdp_open(struct net_device *dev) u8 chan; int ret; + /* The old rings cannot be overwritten after a failed shutdown. Pool + * removal still completes, with their mappings held independently. + */ + if (stmmac_dma_busy(priv)) { + ret = -EBUSY; + goto dma_desc_error; + } + ret = alloc_dma_desc_resources(priv, priv->dma_conf); if (ret < 0) { netdev_err(dev, "%s: DMA descriptors allocation failed\n", @@ -7433,6 +7632,21 @@ int stmmac_xdp_open(struct net_device *dev) } stmmac_reset_queues_param(priv); + if (priv->dma_reset_needed) { + phylink_prepare_resume(priv->phylink); + mutex_lock(&priv->ptp_mutex); + ret = stmmac_hw_setup(dev, false, true); + if (!ret) + stmmac_block_ptp(priv, false); + mutex_unlock(&priv->ptp_mutex); + if (ret) { + stmmac_drain_dma(priv); + goto init_error; + } + stmmac_set_rx_mode(dev); + stmmac_vlan_restore(priv); + goto setup_timers; + } /* DMA CSR Channel configuration */ for (chan = 0; chan < dma_csr_ch; chan++) { @@ -7469,15 +7683,17 @@ int stmmac_xdp_open(struct net_device *dev) if (tx_q->tbs & STMMAC_TBS_AVAIL) stmmac_enable_tbs(priv, priv->ioaddr, 1, chan); - - hrtimer_setup(&tx_q->txtimer, stmmac_tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); } /* Enable the MAC Rx/Tx */ stmmac_mac_set(priv, priv->ioaddr, true); - /* Start Rx & Tx DMA Channels */ +setup_timers: + /* The reset path has also restored filters, PTP and the EST schedule. */ stmmac_start_all_dma(priv); + for (chan = 0; chan < tx_cnt; chan++) + hrtimer_setup(&priv->dma_conf->tx_queue[chan].txtimer, + stmmac_tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ret = stmmac_request_irq(dev); if (ret) @@ -7494,8 +7710,7 @@ int stmmac_xdp_open(struct net_device *dev) irq_error: stmmac_stop_tx_queues(priv); - stmmac_stop_all_dma(priv); - stmmac_mac_set(priv, priv->ioaddr, false); + stmmac_drain_dma(priv); init_error: free_dma_desc_resources(priv, priv->dma_conf); @@ -7626,7 +7841,7 @@ static void stmmac_reset_subtask(struct stmmac_priv *priv) netdev_err(priv->dev, "Reset adapter.\n"); rtnl_lock(); - if (!netif_device_present(priv->dev)) + if (!netif_device_present(priv->dev) || !netif_running(priv->dev)) goto out_unlock; netif_trans_update(priv->dev); @@ -7900,12 +8115,11 @@ static int stmmac_reopen(struct net_device *dev) ret = __stmmac_open(dev, dma_conf); if (ret) { priv->dma_conf = old_conf; - free_dma_desc_resources(priv, dma_conf); - kfree(dma_conf); + stmmac_put_dma_conf(priv, dma_conf); return ret; } - kfree(old_conf); + stmmac_put_dma_conf(priv, old_conf); netif_device_attach(dev); return 0; } @@ -7940,6 +8154,8 @@ int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt) netif_device_detach(dev); __stmmac_release(dev); } + if (stmmac_dma_busy(priv)) + return -EBUSY; stmmac_set_queues(dev, rx_cnt, tx_cnt); @@ -7967,6 +8183,8 @@ int stmmac_reinit_ringparam(struct net_device *dev, u32 rx_size, u32 tx_size) netif_device_detach(dev); __stmmac_release(dev); } + if (stmmac_dma_busy(priv)) + return -EBUSY; priv->dma_conf->dma_rx_size = rx_size; priv->dma_conf->dma_tx_size = tx_size; @@ -8153,8 +8371,20 @@ EXPORT_SYMBOL_GPL(stmmac_plat_dat_alloc); static void stmmac_free_dma_conf(void *data) { struct stmmac_priv *priv = data; + struct stmmac_dma_conf *dma_conf, *next; - kfree(priv->dma_conf); + list_for_each_entry_safe(dma_conf, next, &priv->dma_confs, list) { + list_del(&dma_conf->list); + /* A permanently unresponsive device must not DMA into recycled + * memory, even on unbind. There is no generic isolation mechanism + * for all stmmac integrations. Deliberately retain these allocations. + */ + if (dma_conf->dma_owned) { + dev_err(priv->device, "DMA still active on removal; DMA memory quarantined\n"); + continue; + } + kfree(dma_conf); + } } static int __stmmac_dvr_probe(struct device *device, @@ -8181,10 +8411,12 @@ static int __stmmac_dvr_probe(struct device *device, priv = netdev_priv(ndev); priv->device = device; priv->dev = ndev; + INIT_LIST_HEAD(&priv->dma_confs); /* Keep ring sizes and per-queue settings even while the device is down. */ priv->dma_conf = kzalloc_obj(*priv->dma_conf); if (!priv->dma_conf) return -ENOMEM; + list_add_tail(&priv->dma_conf->list, &priv->dma_confs); ret = devm_add_action_or_reset(device, stmmac_free_dma_conf, priv); if (ret) return ret; @@ -8508,12 +8740,28 @@ void stmmac_dvr_remove(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct stmmac_priv *priv = netdev_priv(ndev); + struct stmmac_dma_conf *dma_conf; + u32 queue; netdev_info(priv->dev, "%s: removing driver", __func__); pm_runtime_get_sync(dev); unregister_netdev(ndev); + rtnl_lock(); + /* A failed ndo_open has no matching ndo_stop. Its retained resources + * still need retirement, or software-only disconnection on timeout. + */ + list_for_each_entry(dma_conf, &priv->dma_confs, list) { + free_dma_desc_resources(priv, dma_conf); + for (queue = 0; queue < MTL_MAX_RX_QUEUES; queue++) { + struct xdp_rxq_info *rxq = &dma_conf->rx_queue[queue].xdp_rxq; + + if (xdp_rxq_info_is_reg(rxq)) + xdp_rxq_info_unreg(rxq); + } + } + rtnl_unlock(); #ifdef CONFIG_DEBUG_FS stmmac_exit_fs(ndev); @@ -8721,12 +8969,7 @@ int stmmac_resume(struct device *dev) mutex_lock(&priv->lock); - stmmac_reset_queues_param(priv); - - stmmac_free_tx_skbufs(priv); - stmmac_clear_descriptors(priv, priv->dma_conf); - - ret = stmmac_hw_setup(ndev); + ret = stmmac_hw_setup(ndev, false, false); if (ret < 0) { netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); goto error_stop_dma; From patchwork Sat Sep 26 15:49:09 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3479 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sin.lore.kernel.org (sin.lore.kernel.org [104.64.211.4]) by mxe881.netcup.net (Postfix) with ESMTPS id 566651C2AA6 for ; Sat, 26 Sep 2026 17:56:02 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 104.64.211.4) smtp.mailfrom=linux-sunxi+bounces-26279-noreply=patchwork.local@lists.linux.dev smtp.helo=sin.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 104.64.211.4 as permitted sender) client-ip=104.64.211.4; envelope-from=linux-sunxi+bounces-26279-noreply=patchwork.local@lists.linux.dev; helo=sin.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sin.lore.kernel.org (Postfix) with ESMTP id CBC2129EC6 for ; Sat, 26 Sep 2026 15:51:41 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 76C9B4854F1; Sat, 26 Sep 2026 15:49:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Z+mSygWO" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 3EFCC3859EE for ; Sat, 26 Sep 2026 15:49:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437796; cv=none; b=CbDNLJWjEqztX9QdK3EgTFFjoeW3N0w2O/kKSrbe4qdL7OpSEMUDU/XLzaXzpb1I7Kh8Gw6Sbv4bfstzQxePPsDhVVcllBG6SlqEisb4AvLztRN4OzXt++r4Yu2mwMhPUsvy9GY05uei2Q7PxhkyOUz1FBzWuA5zTJfH4GjqLDo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437796; c=relaxed/simple; bh=pXC6u2z4QzCd600gWVJtWtoFgabkjBB/NCPNcLpiYqI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=UfUjnqig+ozhOf2XkJQskzgslJMeiuJ3jceZFEZwyyiJRxstJ+WJvH31Qm/jwAMF+B42RtGYjyRtYAeEatfHw+9JMIT5HNrkwpsCLZDaRhOGhJhDesz7NbAW3sTKeYlxnNl/9mVSysm6PKrCt2JFGXKWdt729IRG40KQ65YDdWo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Z+mSygWO; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f42.google.com with SMTP id 46e09a7af769-81b8e8e9cb0so10256a34.3 for ; Sat, 26 Sep 2026 08:49:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437790; x=1791042590; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=5KjS+S3ybo0qYcHE4tpSaMdCm52N9S5u9el69I6IvdE=; b=Z+mSygWOuK93K8iBcfu7I1YJQ7wQTH6z62nbMW3yPz9bAcpwe3eMd5pDaJ79/f4Qly MTwB67DD+BAs96fx/vYBTHwuP+P/MHnJzODz1ETA8pZGQjcSjhZZ7RZ1/yvE4eIB5Cxs 4hUHbhmM+nBHUlGYMH4x8VgoFUZ5TU6WZps8VXjMZQMzvUd92KmzDs5lpbQVb/JDEBke IWQoCXJuvLr7jmurHCqbjCcwULu7dFQO7eJamYDGlRueleufRSbwjW5LGL5+Bu/NYkX+ 4/gn9k5nN8eF+sjRzS3UhPdb3g7jMzROngU/pmU/86UOsksjJr/xFTXiAVvWJzSmFdKI HJxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437790; x=1791042590; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=5KjS+S3ybo0qYcHE4tpSaMdCm52N9S5u9el69I6IvdE=; b=SDeMLKBeuLYnm3RmgM9K/VWBTbczTfwKg8q8TOLWp/E2b8FPfCV1sew7+tnWr8dRWf 2Gg4KBUyUnMB2FiZnStRDMGd0BIuExXJbv7pLOyuneKksnVIR7Oo5qkIcoIOavh0u+Kw WycTHc93l8LKCkPu2kO5kG0CRkOPdrf0sh8a4qdKw8CRYFpky8uF6rdrkMT55DtHc4Hv Yq5eFj5l4tsKdDvmeRqpPEWDzYtKYv/Zo4HrrWBdmdiz7tfwi8Ji2So/cMD9uWjjFxTR LaMpsdPm1Ra2Up1LvnfRQNWrdPIuilVkSBGWfIGOME8bVfoG/eidsu4jT3TCC3xG4crj 0Ulg== X-Forwarded-Encrypted: i=1; AKwUvBzET9jolzD4orkqTIU4BjQpk0fIb2FLg0oEn6aCiWFHE/EyG5AFj2aM6w1yX9zXthL9cce2DlS09VfCAw==@lists.linux.dev X-Gm-Message-State: AFuF++nVBawSgAo+ZLy51tFtqIb4Xjrm+GjasajTAeFtS6KXMWvfwklx 8xImHH1Pn/FySTH5DSdxY37cAhbRcQk5CSm4+C8moXlPUYNCngO5Dvf/ X-Gm-Gg: AYBFou1hnTb5ZP7dIb8Ng/uyWk/3JVq1sCSsQzTJ7jAMIgAouJzz0h8QBa+iE1QZd+z 4hvkXn/7oufWGwUkhqUaflgKDfvD4R+6zhM7ReJL++oEq/MCRhiLbuE0n7miyj7Ui3BMKYhZPgT k9dlRIYY6rIpRL6xQdtqOzFsMtBBj9KYk568Ca5F8i01hV1QRpihj3eBTrtKq1EQG7ooSqW1SS5 xf3SP8KDpgb1RRzJ8X/v8ZmXJpE5D2nAUNXSvZ4ICuH4EwhhQgda424cVb68ksV24dC1b9x0OXb yjc+jq2vRr3iMM4B8yXAEvR1FePIcNQ72555Y7YPBB88JLpaZHJD2KyjRRF3oZ1zr31z43h/oP8 ZCYVH9AlAfvJbM/fC5YnE8IIn1RKQaBxpq6ikBKTBJ2CQh7n4cU1CEYqQGR5xWwPVmBrmoYJPo6 kIBs5VB4uFPNVmdGa/JqsesiJ2UMQTaeu/eIe7CS1xXnv7+rZgrYRDNSlf0UhOD4+vNU2yrY7YQ OL8PUFie1pCwVPvk+a9GIaDShEGEkqcuk+67h9acYa2ZUDcu1hAQk0K8RncXWyd0ESMLHK0I3Ye mXtc/kVbPHBqmlNN7j2IJ1L0JhT6QuAewwRpImyAbG7lZ7TS7UD1fALZSAjCwRyzVejrIKwZYY3 2rM21wapdnHfdnEk1MAic X-Received: by 2002:a05:6830:6b07:b0:811:72c5:bd19 with SMTP id 46e09a7af769-81783684b1bmr9913596a34.25.1790437789641; Sat, 26 Sep 2026 08:49:49 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:49 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:49:09 -0600 Subject: [PATCH net v4 14/15] net: stmmac: prepare device-local DMA interrupt quiescence Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-14-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [-1.16 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[104.64.211.4:from]; SUSPICIOUS_RECIPS(1.50)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; MAILLIST(-0.15)[generic]; MIME_GOOD(-0.10)[text/plain]; BAD_REP_POLICIES(0.10)[]; HAS_LIST_UNSUB(-0.01)[]; DBL_BLOCKED_OPENRESOLVER(0.00)[sin.lore.kernel.org:rdns,sin.lore.kernel.org:helo]; FROM_HAS_DN(0.00)[]; FORGED_SENDER_MAILLIST(0.00)[]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; TAGGED_RCPT(0.00)[kernel,netdev]; PRECEDENCE_BULK(0.00)[]; FREEMAIL_CC(0.00)[bootlin.com,d-silva.org,kernel.org,vger.kernel.org,st-md-mailman.stormreply.com,lists.infradead.org,uniontech.com,oss.qualcomm.com,163.com,gmail.com,lists.linux.dev]; RCVD_COUNT_FIVE(0.00)[6]; FROM_NEQ_ENVFROM(0.00)[jameshilliard1@gmail.com,linux-sunxi@lists.linux.dev]; TAGGED_FROM(0.00)[bounces-26279-noreply=patchwork.local]; R_SPF_ALLOW(0.00)[+ip4:104.64.211.4:c]; FREEMAIL_FROM(0.00)[gmail.com]; TO_DN_SOME(0.00)[]; RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[104.64.211.4:from]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[100.90.174.1:received,74.125.231.170:received]; ASN(0.00)[asn:63949, ipnet:104.64.192.0/19, country:SG]; RCPT_COUNT_GT_50(0.00)[54]; RECEIVED_SPAMHAUS_PBL(0.00)[174.29.1.49:received]; MID_RHS_MATCH_FROM(0.00)[]; FREEMAIL_TO(0.00)[armlinux.org.uk,lunn.ch,gmail.com,davemloft.net,kernel.org,redhat.com,nxp.com,bootlin.com,foss.st.com,loongson.cn,iogearbox.net,fomichev.me,quicinc.com,synopsys.com,intel.com,googlemail.com,nvidia.com,sholland.org]; RCVD_TLS_LAST(0.00)[]; MIME_TRACE(0.00)[0:+]; RCVD_VIA_SMTP_AUTH(0.00)[] X-Rspamd-Queue-Id: 566651C2AA6 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Add DMA interrupt-mask accessors for the supported cores and a per-channel gate protected by the channel lock. A handler invoked on a shared IRQ can then mask newly enabled device sources without acknowledging pending events or touching rings being replaced. The retained-ring MTU transaction will save and restore these masks and synchronize the registered handlers. No interrupt-controller line needs to be disabled, so other devices sharing the IRQ remain serviceable. Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 11 ++++++++ .../net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 1 + drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c | 1 + drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 2 ++ drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h | 2 ++ drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c | 11 ++++++++ drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h | 2 ++ drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c | 10 ++++++++ drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c | 11 ++++++++ drivers/net/ethernet/stmicro/stmmac/hwif.h | 5 ++++ drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 ++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 30 ++++++++++++++++------ 12 files changed, 80 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index 422d3df21ed9..31bebb2a9bab 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -374,6 +374,16 @@ static void sun8i_dwmac_disable_dma_irq(struct stmmac_priv *priv, writel(value, ioaddr + EMAC_INT_EN); } +static u32 sun8i_dwmac_set_dma_irq_mask(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan, u32 mask) +{ + u32 old_mask = readl(ioaddr + EMAC_INT_EN); + + writel(mask, ioaddr + EMAC_INT_EN); + readl(ioaddr + EMAC_INT_EN); + return old_mask; +} + static void sun8i_dwmac_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan) { @@ -579,6 +589,7 @@ static const struct stmmac_dma_ops sun8i_dwmac_dma_ops = { .enable_dma_transmission = sun8i_dwmac_enable_dma_transmission, .enable_dma_irq = sun8i_dwmac_enable_dma_irq, .disable_dma_irq = sun8i_dwmac_disable_dma_irq, + .set_irq_mask = sun8i_dwmac_set_dma_irq_mask, .start_tx = sun8i_dwmac_dma_start_tx, .stop_tx = sun8i_dwmac_dma_stop_tx, .start_rx = sun8i_dwmac_dma_start_rx, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index 4cb7e6c16bdd..2285eac69071 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -264,6 +264,7 @@ const struct stmmac_dma_ops dwmac1000_dma_ops = { .enable_dma_reception = dwmac_enable_dma_reception, .enable_dma_irq = dwmac_enable_dma_irq, .disable_dma_irq = dwmac_disable_dma_irq, + .set_irq_mask = dwmac_set_dma_irq_mask, .start_tx = dwmac_dma_start_tx, .stop_tx = dwmac_dma_stop_tx, .start_rx = dwmac_dma_start_rx, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c index 5ffd3c1471c4..41579d10af3c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c @@ -118,6 +118,7 @@ const struct stmmac_dma_ops dwmac100_dma_ops = { .enable_dma_transmission = dwmac_enable_dma_transmission, .enable_dma_irq = dwmac_enable_dma_irq, .disable_dma_irq = dwmac_disable_dma_irq, + .set_irq_mask = dwmac_set_dma_irq_mask, .start_tx = dwmac_dma_start_tx, .stop_tx = dwmac_dma_stop_tx, .start_rx = dwmac_dma_start_rx, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index fb0a4e156a7b..86b23ce39d6b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -580,6 +580,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = { .dma_tx_mode = dwmac4_dma_tx_chan_op_mode, .enable_dma_irq = dwmac4_enable_dma_irq, .disable_dma_irq = dwmac4_disable_dma_irq, + .set_irq_mask = dwmac4_set_dma_irq_mask, .start_tx = dwmac4_dma_start_tx, .stop_tx = dwmac4_dma_stop_tx, .start_rx = dwmac4_dma_start_rx, @@ -611,6 +612,7 @@ const struct stmmac_dma_ops dwmac410_dma_ops = { .dma_tx_mode = dwmac4_dma_tx_chan_op_mode, .enable_dma_irq = dwmac4_enable_dma_irq, .disable_dma_irq = dwmac4_disable_dma_irq, + .set_irq_mask = dwmac4_set_dma_irq_mask, .start_tx = dwmac4_dma_start_tx, .stop_tx = dwmac4_dma_stop_tx, .start_rx = dwmac4_dma_start_rx, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h index 9352107204eb..edccc09f0b03 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h @@ -184,6 +184,8 @@ void dwmac4_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan, bool rx, bool tx); void dwmac4_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan, bool rx, bool tx); +u32 dwmac4_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan, u32 mask); void dwmac4_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan); void dwmac4_dma_stop_tx(struct stmmac_priv *priv, void __iomem *ioaddr, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index 9af0565a9bca..477bdb081c52 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -155,6 +155,17 @@ void dwmac4_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); } +u32 dwmac4_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan, u32 mask) +{ + const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs; + u32 old_mask = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); + + writel(mask, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); + readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); + return old_mask; +} + int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_extra_stats *x, u32 chan, u32 dir) { diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index 970495bccfd2..4726801253f5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -150,6 +150,8 @@ void dwmac_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan, bool rx, bool tx); void dwmac_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan, bool rx, bool tx); +u32 dwmac_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan, u32 mask); void dwmac_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan); void dwmac_dma_stop_tx(struct stmmac_priv *priv, void __iomem *ioaddr, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index bb907db8fca1..88d904ed4685 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -84,6 +84,16 @@ void dwmac_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr, writel(value, ioaddr + DMA_CHAN_INTR_ENA(chan)); } +u32 dwmac_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan, u32 mask) +{ + u32 old_mask = readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); + + writel(mask, ioaddr + DMA_CHAN_INTR_ENA(chan)); + readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); + return old_mask; +} + void dwmac_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan) { diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index ff83858ebc1f..30915d3f5230 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -256,6 +256,16 @@ static void dwxgmac2_disable_dma_irq(struct stmmac_priv *priv, writel(value, ioaddr + XGMAC_DMA_CH_INT_EN(chan)); } +static u32 dwxgmac2_set_dma_irq_mask(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan, u32 mask) +{ + u32 old_mask = readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan)); + + writel(mask, ioaddr + XGMAC_DMA_CH_INT_EN(chan)); + readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan)); + return old_mask; +} + static void dwxgmac2_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan) { @@ -604,6 +614,7 @@ const struct stmmac_dma_ops dwxgmac210_dma_ops = { .dma_tx_mode = dwxgmac2_dma_tx_mode, .enable_dma_irq = dwxgmac2_enable_dma_irq, .disable_dma_irq = dwxgmac2_disable_dma_irq, + .set_irq_mask = dwxgmac2_set_dma_irq_mask, .start_tx = dwxgmac2_dma_start_tx, .stop_tx = dwxgmac2_dma_stop_tx, .start_rx = dwxgmac2_dma_start_rx, diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 3bc4d955ef5f..e1630e84e91d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -197,6 +197,9 @@ struct stmmac_dma_ops { u32 chan, bool rx, bool tx); void (*disable_dma_irq)(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan, bool rx, bool tx); + /* Replace and flush the full interrupt enable mask; return the old mask. */ + u32 (*set_irq_mask)(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan, u32 mask); void (*start_tx)(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan); void (*stop_tx)(struct stmmac_priv *priv, void __iomem *ioaddr, @@ -263,6 +266,8 @@ struct stmmac_dma_ops { stmmac_do_void_callback(__priv, dma, enable_dma_irq, __priv, __args) #define stmmac_disable_dma_irq(__priv, __args...) \ stmmac_do_void_callback(__priv, dma, disable_dma_irq, __priv, __args) +#define stmmac_set_dma_irq_mask(__priv, __args...) \ + stmmac_do_callback(__priv, dma, set_irq_mask, __priv, __args) #define stmmac_start_tx(__priv, __args...) \ stmmac_do_void_callback(__priv, dma, start_tx, __priv, __args) #define stmmac_stop_tx(__priv, __args...) \ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index e15bab9accce..9e711acb81fb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -146,6 +146,8 @@ struct stmmac_channel { struct stmmac_priv *priv_data; spinlock_t lock; u32 index; + /* Protected by lock; IRQ handlers must not access the DMA rings. */ + bool irq_quiesced; }; struct stmmac_fpe_cfg { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 4d70645c4a4f..08049c05ffc4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3267,14 +3267,30 @@ static bool stmmac_safety_feat_interrupt(struct stmmac_priv *priv) static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan, u32 dir) { - int status = stmmac_dma_interrupt_status(priv, priv->ioaddr, - &priv->xstats, chan, dir); - struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[chan]; - struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan]; struct stmmac_channel *ch = &priv->channel[chan]; + struct stmmac_rx_queue *rx_q; + struct stmmac_tx_queue *tx_q; struct napi_struct *rx_napi; struct napi_struct *tx_napi; unsigned long flags; + int status; + + spin_lock_irqsave(&ch->lock, flags); + if (unlikely(ch->irq_quiesced)) { + /* A shared IRQ may still invoke us, and DMA initialization can + * restore interrupt enables. Mask them again without acknowledging + * pending events or accessing the configuration being replaced. + */ + stmmac_set_dma_irq_mask(priv, priv->ioaddr, chan, 0); + spin_unlock_irqrestore(&ch->lock, flags); + return 0; + } + spin_unlock_irqrestore(&ch->lock, flags); + + status = stmmac_dma_interrupt_status(priv, priv->ioaddr, + &priv->xstats, chan, dir); + rx_q = &priv->dma_conf->rx_queue[chan]; + tx_q = &priv->dma_conf->tx_queue[chan]; rx_napi = rx_q->xsk_pool ? &ch->rxtx_napi : &ch->rx_napi; tx_napi = tx_q->xsk_pool ? &ch->rxtx_napi : &ch->tx_napi; @@ -3995,8 +4011,7 @@ static void stmmac_free_irq(struct net_device *dev, for (j = irq_idx - 1; msi && j >= 0; j--) { if (msi->tx_irq[j] > 0) { irq_set_affinity_hint(msi->tx_irq[j], NULL); - free_irq(msi->tx_irq[j], - &priv->channel[j]); + free_irq(msi->tx_irq[j], &priv->channel[j]); } } irq_idx = priv->plat->rx_queues_to_use; @@ -4005,8 +4020,7 @@ static void stmmac_free_irq(struct net_device *dev, for (j = irq_idx - 1; msi && j >= 0; j--) { if (msi->rx_irq[j] > 0) { irq_set_affinity_hint(msi->rx_irq[j], NULL); - free_irq(msi->rx_irq[j], - &priv->channel[j]); + free_irq(msi->rx_irq[j], &priv->channel[j]); } } From patchwork Sat Sep 26 15:49:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3484 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id AAEF71C2AA6 for ; Sat, 26 Sep 2026 17:59:29 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26280-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26280-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id EBF834021E for ; Sat, 26 Sep 2026 15:52:12 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4550F379C37; Sat, 26 Sep 2026 15:50:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="lJZEj13l" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-oo2-f43.google.com (mail-oo2-f43.google.com [74.125.231.171]) (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 E1A2A485CE7 for ; Sat, 26 Sep 2026 15:49:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437801; cv=none; b=DpqTGjoj7wwK/mVitgU8ZeTPDyzmT+iSKGtmWXo7LnBUYOKB4TDxfVamJS1XsyWkPdpW0W4EbhbpvKEPGHLk/kAY2zMo1iEqiQzcpaU1WFw+kD4dwKfhDWjxyFtAycGliVcHpHlTpm9PEiMH6Y2KsQiD99jW/VYjcKibc+0GwaA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790437801; c=relaxed/simple; bh=j0GelKIR+8sdSlghOzo6HZis8yz0eoknRZijzt1nYiI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=E79hWo4twTA91GYg5dzrEPETtu8HdRQxWHSRvfwogRqol4Pdfh8Ur/R+p95qishTXIK9UjZ1FgrW050rjDQ2W2HIS4XPiTRpabktheoKYbhNKGNP0hVt3eeTJOFYaVBNku9dKNq7MtU22qNGU7MsVDAv6ocESxvnC6Rze6TNcO4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=lJZEj13l; arc=none smtp.client-ip=74.125.231.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo2-f43.google.com with SMTP id 46e09a7af769-81b15bca7dfso152823a34.0 for ; Sat, 26 Sep 2026 08:49:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790437796; x=1791042596; darn=lists.linux.dev; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=HLTMfxgLa8vC7MGW+mtlayhPdwf1DSymVq+0sQ7was0=; b=lJZEj13ltUMHrMmelvkTZKK0aOM1SDTWnyo7Tptbtwg+5oLKlevEU83Bd0hdfbK8E2 fV2eSsr7skKpbmMBqZ87qVPIOI06ahoFI39ZWBAqM9nQwyvznnKmc/Mi8+XCut2w6100 RyLT1PY1Wn5WEWJF4KNqa6uVZ7lUSaJ9IG7wX+ArKK1TaNq9JSD4N8FrHdBL4JaMs4S0 HLn7rQsva8xDo5mLjasJD6h/eqFyA8chbiLF9+z27guluhof/RDzvWvXGAMdvuBCIM9O 2Ypvzvz2M7ceOGkpLypYNb8evgwljMBSq3r4ssi1S5vfSBVpiiWL3UvYOsrb9pVWoucw NkzQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790437796; x=1791042596; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=HLTMfxgLa8vC7MGW+mtlayhPdwf1DSymVq+0sQ7was0=; b=l+y6nobwtYdo0ncJqdrMjX1ns8fOszx33PmcsqBvgXrxb7PdAyJNzs5NboraWa9scv mfnqBR2jMlTuUfPvkkdGbVW76OlN/6hzc0aC5bOyrQodSTRHgo7XNdfyYdevtfWe6KkX 5OYMHoCo/FIm2jdRy0+xUIcodR4TeDm0nM4fv7TJqR83yADTWAzlcSvQPKQHJKyp8AxL +udqhLG/VIij1HPUlCXbZO4P8u/OOBlvw7YY3AJRLvUDMtMUqxGkPRBvvlV6maC9vRvk iH8Qe8VzeqvsKm9WkemxLXfo5auRQN7+Lp9tdVnWrzC3B+fG9uR9DyY5xCQSkWOGln/X wbxQ== X-Forwarded-Encrypted: i=1; AKwUvBzc1ENN1EQKOBcz1UlUBkJqqArxGY+4s8NonHZlwPjaP+nqhy12Yupd1qTbAEi8KWyKyG0QhZ+QBDL33g==@lists.linux.dev X-Gm-Message-State: AFuF++m8N4fu2zw98tnB/gUIUZv4gKHsUDq9e7FKNqZM0vy+iXVUvt73 hkF8dv5S8ebSfU8RxJeRrNVc0vknDjkY6+qbi4837PEP2Iy3ZnmuudUm X-Gm-Gg: AYBFou2kdCAWERIjS++HYGIAVNcGSZw6CLdslriL02HjQZ4wSzCQ9OR1HnKKvhbY0iH LeQVd3vgmThj0SJipz/QMX89QwB5alwUcH+6ATxJu9OrJebzScr3Yqfa1P5mCVOePMhNdVw0gzv 9bv2E7iENxw3fraBjdYrwsQaP24v4iv3pvjVL9ElcIJMCogeqRVBVBlnB/2sCV2sRPMpBeRHuHH feAgg/gXYMmJhhAqydDfpw1PuGVcANgrgWOeOTQ5/JwnH1CsXjdWj8tjzusuB2c6fBCqFsECP3s XLQif4gGv60yln41ld+7Gn3ama0BBZXBmVrHz2C1AEQO6NdILTlj5wrJFWDNri56eXxzVF9lXZ+ IkbBbf4BD0SVg5JBDIE3VLcEfCWQl4HfcA7TVsC2Ki8y/6Sf5gvV2FOe/KmVk8XUrAroJqX5Ups rNCqfh6UedrsiGGGF0njr7DknUgzIW4M1Gk3N10IJd5kT6RfuhoKc+gkLrOooVl0y9uobVgKmp6 0UXsiGZKzLtTtlIWjxaNR8Oh2Lr0DgBi2qOQYTBdnnkIjJN4M/9x+lsZ/tCUH2Yye3GR0mvI2Rp P4OP/OUovEuFXi46hii/15k/O5HD33npt/z7LcrPOnwNROI8WmXKPsD03An2VQNSezn557s8fN7 MQIFGEtSAHSPnsOHSxn0+Mov3LCpPp9w= X-Received: by 2002:a05:6830:258a:b0:816:b03e:f9db with SMTP id 46e09a7af769-81780b974femr9021140a34.6.1790437791761; Sat, 26 Sep 2026 08:49:51 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-818e98b16d4sm5442366a34.26.2026.09.26.08.49.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Sep 2026 08:49:51 -0700 (PDT) From: James Hilliard Date: Sat, 26 Sep 2026 09:49:10 -0600 Subject: [PATCH net v4 15/15] net: stmmac: retain DMA resources across MTU changes Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20260926-submit-stmmac-reset-fixes-v1-v4-15-ec1c0250b3c9@gmail.com> References: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> In-Reply-To: <20260926-submit-stmmac-reset-fixes-v1-v4-0-ec1c0250b3c9@gmail.com> To: Russell King , Andrew Lunn , Heiner Kallweit , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Joakim Zhang , "Russell King (Oracle)" , Maxime Chevallier , Andrew Lunn , Maxime Coquelin , Alexandre Torgue , Christian Marangi , Tiezhu Yang , Huacai Chen , Alexei Starovoitov , Daniel Borkmann , Jesper Dangaard Brouer , John Fastabend , Stanislav Fomichev , Serge Semin , Suraj Jaiswal , Richard Cochran , Joao Pinto , Vladimir Oltean , Ong Boon Leong , Voon Weifeng , "Song, Yoong Siang" , Linus Walleij , Martin Blumenstingl , Magnus Karlsson , Maciej Fijalkowski , Simon Horman , =?utf-8?b?QmrDtnJuIFTDtnBlbA==?= , Thierry Reding , Jonathan Hunter , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Eric Dumazet Cc: Richard Genoud , Alastair D'Silva , Maxime Ripard , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, bpf@vger.kernel.org, ZhaoJinming , Lorenzo Bianconi , Ding Hui , James Hilliard , linux-tegra@vger.kernel.org, linux-sunxi@lists.linux.dev X-Mailer: b4 0.15.2 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Prepare the replacement configuration before quiescing the old datapath. Retain its rings and IRQ registrations until setup succeeds so rollback needs no new allocations or IRQ requests. Fill RX buffer holes before reset without altering active descriptors; rebuild the retained rings only after a successful reset. Mask the device DMA interrupt sources, gate shared-IRQ handlers and drain all registered handlers before the final transmitter and timer cancellation. Restore saved interrupt masks only after the selected rings and NAPI are ready. This leaves interrupt-controller lines available to unrelated devices. Program receive limits using the prospective MTU and restore the old MTU before rollback. Reapply PHC and TC state before starting DMA, preserving the PHC registration and packet timestamp filters. Continuous PHC time across the reset is not preserved. If rollback fails, leave the administratively-up interface detached in a distinct HALTED state: rings retained, NAPI disabled and IRQ registrations released. Close or a later down/up can finish cleanup and recovery without freeing IRQs twice. Retain potentially active DMA memory until hardware shutdown is confirmed. Fixes: 3470079687448 ("net: ethernet: stmicro: stmmac: permit MTU change with interface up") Signed-off-by: James Hilliard --- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 216 +++++++++++++++++----- 2 files changed, 173 insertions(+), 45 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 9e711acb81fb..e2b9b5b387c7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -279,6 +279,8 @@ enum stmmac_datapath_state { STMMAC_DATAPATH_RUNNING, /* Resources retained, NAPI disabled, DMA stop requested. */ STMMAC_DATAPATH_SUSPENDED, + /* Failed MTU rollback: rings retained, but no IRQs or running NAPI. */ + STMMAC_DATAPATH_HALTED, }; struct stmmac_priv { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 08049c05ffc4..8964f52c26ba 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -977,7 +977,7 @@ static void stmmac_release_ptp(struct stmmac_priv *priv) } /* ptp_mutex excludes configuration and crosstimestamp operations. The - * spinlock also excludes atomic clock reads while changing this gate. + * spinlock also excludes atomic gettime callers while changing this gate. */ static void stmmac_block_ptp(struct stmmac_priv *priv, bool block) { @@ -2205,6 +2205,30 @@ static void stmmac_free_tx_skbufs(struct stmmac_priv *priv) dma_free_tx_skbufs(priv, priv->dma_conf, queue); } +/* NAPI is stopped, but DMA may still be using the old rings. Fill holes in + * the software buffer array without changing any descriptors. If allocation + * fails, the old rings can continue unchanged. Otherwise rollback after a + * reset will not need to allocate buffers. + */ +static int stmmac_prepare_rx_buffers(struct stmmac_priv *priv) +{ + struct stmmac_dma_conf *dma_conf = priv->dma_conf; + u32 queue, i; + int ret; + + for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) { + struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + + for (i = 0; i < dma_conf->dma_rx_size; i++) { + ret = stmmac_alloc_rx_buffer(priv, rx_q, &rx_q->buf_pool[i]); + if (ret) + return ret; + } + } + + return 0; +} + /* Only after a successful DMA reset, and with all RX buffers prepared. */ static void stmmac_reinit_dma_desc(struct stmmac_priv *priv) { @@ -4311,14 +4335,38 @@ static void stmmac_synchronize_irq(struct stmmac_priv *priv) synchronize_irq(msi->tx_irq[i]); } +/* Keep the IRQ registrations, but prevent DMA handlers from using the rings. + * The caller drains handlers after quiescing every channel and restores their + * masks only once the active DMA configuration is ready again. + */ +static void stmmac_set_dma_irq_state(struct stmmac_priv *priv, bool enable, + u32 *irq_mask) +{ + u32 channels = max(priv->plat->rx_queues_to_use, + priv->plat->tx_queues_to_use); + u32 chan; + + for (chan = 0; chan < channels; chan++) { + struct stmmac_channel *ch = &priv->channel[chan]; + unsigned long flags; + + spin_lock_irqsave(&ch->lock, flags); + ch->irq_quiesced = !enable; + if (enable) + stmmac_set_dma_irq_mask(priv, priv->ioaddr, chan, + irq_mask[chan]); + else + irq_mask[chan] = stmmac_set_dma_irq_mask(priv, priv->ioaddr, + chan, 0); + spin_unlock_irqrestore(&ch->lock, flags); + } +} + /** - * stmmac_setup_dma_desc - Generate a dma_conf and allocate DMA queue - * @priv: driver private structure - * @mtu: MTU to setup the dma queue and buf with - * Description: Allocate and generate a dma_conf based on the provided MTU. - * Allocate the Tx/Rx DMA queue and init them. - * Return value: - * the dma_conf allocated struct on success and an appropriate ERR_PTR on failure. + * stmmac_setup_dma_desc - allocate and initialize a DMA configuration + * @priv: driver private structure + * @mtu: MTU to size the receive buffers for + * Return: the allocated configuration, or an ERR_PTR on failure */ static struct stmmac_dma_conf * stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) @@ -4544,7 +4592,7 @@ static int __stmmac_open(struct net_device *dev, priv->dma_conf = dma_conf; /* The PHY is suspended when the interface is reopened without - * disconnecting the PHY, e.g. on MTU change. IEEE 802.3 allows PHYs + * disconnecting the PHY, e.g. on an ethtool change. IEEE 802.3 allows PHYs * to stop their receive clock while powered down, but the DMA * software reset in stmmac_hw_setup() requires a running receive * clock, and phylink_start() below resumes the PHY only after the @@ -4699,20 +4747,22 @@ static void stmmac_quiesce(struct stmmac_priv *priv) static void __stmmac_release(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + enum stmmac_datapath_state state = priv->datapath; - /* A failed MTU reopen has already released the data path. */ + /* There may be no resources left after detached XDP reconfiguration. */ if (priv->datapath == STMMAC_DATAPATH_DOWN) return; phylink_stop(priv->phylink); - /* Suspend retains the resources, but has already stopped activity. */ + /* SUSPENDED and HALTED retain rings with NAPI already disabled. */ if (priv->datapath == STMMAC_DATAPATH_RUNNING) stmmac_quiesce(priv); priv->datapath = STMMAC_DATAPATH_DOWN; /* Free the IRQ lines */ - stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); + if (state != STMMAC_DATAPATH_HALTED) + stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); /* Drain any final IRQ-triggered network activity before DMA shutdown. */ stmmac_stop_tx_queues(priv); @@ -6601,6 +6651,109 @@ static void stmmac_set_rx_mode(struct net_device *dev) stmmac_set_filter(priv, priv->hw, dev); } +static int stmmac_reconfigure_mtu(struct net_device *dev, int mtu) +{ + struct stmmac_priv *priv = netdev_priv(dev); + struct stmmac_dma_conf *old_conf = priv->dma_conf; + struct stmmac_dma_conf *new_conf; + int old_mtu = dev->mtu; + int ret, restore_ret; + u32 irq_mask[STMMAC_CH_MAX]; + u32 chan; + + new_conf = stmmac_setup_dma_desc(priv, mtu); + if (IS_ERR(new_conf)) + return PTR_ERR(new_conf); + + mutex_lock(&priv->ptp_mutex); + stmmac_block_ptp(priv, true); + netif_device_detach(dev); + phylink_stop(priv->phylink); + stmmac_quiesce(priv); + if (stmmac_fpe_supported(priv)) + ethtool_mmsv_stop(&priv->fpe_cfg.mmsv); + + /* Drain handlers before the final TX stop and configuration swap, + * and keep the registrations for rollback. + */ + stmmac_set_dma_irq_state(priv, false, irq_mask); + stmmac_synchronize_irq(priv); + stmmac_stop_tx_queues(priv); + + ret = stmmac_prepare_rx_buffers(priv); + if (ret) + goto restart; + + stmmac_stop_all_dma(priv); + phylink_prepare_resume(priv->phylink); + + /* MAC receive limits must be programmed for the prospective MTU. */ + WRITE_ONCE(dev->mtu, mtu); + priv->dma_conf = new_conf; + stmmac_reset_queues_param(priv); + ret = stmmac_hw_setup(dev, false, true); + if (ret) { + stmmac_stop_all_dma(priv); + stmmac_mac_set(priv, priv->ioaddr, false); + priv->dma_conf = old_conf; + WRITE_ONCE(dev->mtu, old_mtu); + + /* Reuse the retained rings. Reinitialize them only after reset + * has completed, not merely after clearing the DMA enable bits. + */ + restore_ret = stmmac_hw_setup(dev, true, true); + if (restore_ret) { + stmmac_stop_all_dma(priv); + stmmac_mac_set(priv, priv->ioaddr, false); + /* Setup may have restored DMA interrupt enables. */ + stmmac_set_dma_irq_state(priv, false, irq_mask); + stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); + stmmac_stop_tx_queues(priv); + stmmac_stop_all_dma(priv); + memset(irq_mask, 0, sizeof(irq_mask)); + stmmac_set_dma_irq_state(priv, true, irq_mask); + priv->datapath = STMMAC_DATAPATH_HALTED; + netdev_err(dev, "MTU rollback failed: %pe; interface remains detached\n", + ERR_PTR(restore_ret)); + goto free_new; + } + } else { + /* Hardware setup completed its reset before using the new rings. + * The old DMA allocations can now be released safely. + */ + stmmac_put_dma_conf(priv, old_conf); + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) + hrtimer_setup(&new_conf->tx_queue[chan].txtimer, + stmmac_tx_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + } + + stmmac_set_rx_mode(dev); + stmmac_vlan_restore(priv); + stmmac_start_all_dma(priv); + +restart: + stmmac_block_ptp(priv, false); + mutex_unlock(&priv->ptp_mutex); + stmmac_enable_all_queues(priv); + stmmac_set_dma_irq_state(priv, true, irq_mask); + stmmac_enable_all_dma_irq(priv); + phylink_start(priv->phylink); + netif_device_attach(dev); + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) + stmmac_tx_timer_arm(priv, chan); + if (!ret) + return 0; + goto free_conf; + +free_new: + /* Failed rollback leaves the registered PHC inaccessible as well. */ + mutex_unlock(&priv->ptp_mutex); +free_conf: + stmmac_put_dma_conf(priv, new_conf); + return ret; +} + /** * stmmac_change_mtu - entry point to change MTU size for the device. * @dev : device pointer. @@ -6615,9 +6768,7 @@ static void stmmac_set_rx_mode(struct net_device *dev) static int stmmac_change_mtu(struct net_device *dev, int new_mtu) { struct stmmac_priv *priv = netdev_priv(dev); - struct stmmac_dma_conf *old_conf = priv->dma_conf; int txfifosz = priv->plat->tx_fifo_size; - struct stmmac_dma_conf *dma_conf; const int mtu = new_mtu; int ret; @@ -6643,35 +6794,9 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) */ if (netif_running(dev) && (dev->mtu > ETH_DATA_LEN || mtu > ETH_DATA_LEN)) { - netdev_dbg(priv->dev, "restarting interface to change its MTU\n"); - /* Try to allocate the new DMA conf with the new mtu */ - dma_conf = stmmac_setup_dma_desc(priv, mtu); - if (IS_ERR(dma_conf)) { - netdev_err(priv->dev, "failed allocating new dma conf for new MTU %d\n", - mtu); - return PTR_ERR(dma_conf); - } - - netif_device_detach(dev); - __stmmac_release(dev); - - ret = __stmmac_open(dev, dma_conf); - if (ret) { - priv->dma_conf = old_conf; - stmmac_put_dma_conf(priv, dma_conf); - /* - * Keep the administrative state and PHY/PM ownership until - * ndo_stop(), but prevent use of the released data path. - */ - netif_device_detach(dev); - netdev_err(priv->dev, "failed reopening the interface after MTU change\n"); + ret = stmmac_reconfigure_mtu(dev, mtu); + if (ret) return ret; - } - - stmmac_put_dma_conf(priv, old_conf); - - stmmac_set_rx_mode(dev); - netif_device_attach(dev); } WRITE_ONCE(dev->mtu, mtu); @@ -7514,11 +7639,12 @@ static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf) return -EOPNOTSUPP; /* - * Pool removal must succeed even after a failed resume. Release the - * suspended rings before their pool or XDP buffer layout can change. + * Pool removal must succeed after failed resume or MTU rollback. Release + * retained rings before their pool or XDP buffer layout can change. * Leave the interface detached until it is closed and reopened. */ - if (priv->datapath == STMMAC_DATAPATH_SUSPENDED) + if (priv->datapath == STMMAC_DATAPATH_SUSPENDED || + priv->datapath == STMMAC_DATAPATH_HALTED) __stmmac_release(dev); switch (bpf->command) {