From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) (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 3BD864734F7 for ; Tue, 1 Sep 2026 08:30:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.44 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788251434; cv=none; b=FUjxdpvJFK1ubCWGPzB36/fGYfTWv9Y8Q/VwKB23/fzRoLD7iWMys0BXTKfH6RtKnsMK+tGZf6KCHx1gqGLJszA4dAJuDs8Em/UjVkz87oYJOSO8xmzYKbrk1O3AMEapRliy/wiqhyQy9hf9WrHNPT8C0Ls/cCF1XHdm3orhRYU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788251434; c=relaxed/simple; bh=Uy7P1hFgMzyHDTtxqH1mm6G4FHdl8e+SoPnghgSBiuM=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jdf7LMePJtQU61tD8jihnqybQfsA8lWH3WJsfQlYzK1CZ+NH5aLsgTEcp/IyttBN6WLoH5lLOsG8Fgq3wD3CZJo8KWroLc9V6+8YMi6O8kKs7auxpUc3DmZdQbQgXQs1BcesKR2JG3soiYe4ZtiyGjxA8eLA78PD23WQQ5J/Auw= 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=e/rlO8p/; arc=none smtp.client-ip=209.85.128.44 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 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="e/rlO8p/" Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-49b96837ca3so24906455e9.3 for ; Tue, 01 Sep 2026 01:30:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788251430; x=1788856230; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to:content-type; bh=2HZi6igNNcqvFjQiOZ0hxc12kwxhpH+s4rFAv99wT4E=; b=e/rlO8p/OM5Hy27994zDGQCO4bekOcDuXiSQQLIHaOYiOQA3LNirRMT/DktvY/quoF XdU7PTNL9IPebluGIr8UO2l3F9VFvj1ywWpiTvPK9M932b9GoI1g5ItWqov6p2QHAuQA +Pbh7TtM1P8dry5hpbjAoI2xLuhVCrKOEQYYWcRGnBQKUTC6yLB55RXzeRkOcz0Mo2ZC u0YsPH5ONrtZZH0XJ+WViKJRN2UAJL60pJW4JHA3v6RmKBlQq+j3nsgBIJPi6TO8GfhX ZGUBLyBAjjvNGSbqkX0PzY8Ew5xm2NKVE09RPQd3bQng8bQeO4uRunDAN4oirU1rqfWl iiiQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788251430; x=1788856230; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to:content-type; bh=2HZi6igNNcqvFjQiOZ0hxc12kwxhpH+s4rFAv99wT4E=; b=bPqMeGh12gNkgZxaIoVBh7UXiYBWOf9cKRlVtJRKoeYqbj5xquKOU6wzzQ4tZKKYmh EinVpcularO55JlAdz9jyIltpKr2GhJPAdPM1l+7kvL2yYTMOFjcv1mkvfZyuKCDCUlh kIATE4vbi13sAliwqWCmxrGV0tKTvl0VucepWakauGN7MxYxVk6V9tivPJhFatMvlLOl +lOgn3YSJee+B+Rp/pi2t31Vcm0L6vUgDWdZ3z1Z93sE3qetDSHoM4cqteo5O/bWjzC6 IFUJTNaCtASx+PV9v2WMuLsxO5xwzV1O/i/MRL34Td6+AfG2hNxOkfy3qUlb3rZSMevL vSSg== X-Forwarded-Encrypted: i=1; AHgh+RripsyqXzkO+98KYqvwDIxqEBH26qDTgN/45ZSMsNst5xvVXpc4UN3pKONQnIVqv9e17vIkg8U9kYk=@vger.kernel.org X-Gm-Message-State: AFuF++mO715+e34VDojK2mPF4ppYcOS76tQpK2uJEJ6Qp5pBCDl8J8MW o4aX57G9w3p3QuLt6APiF2FUrZoOpis8gd1DyLLONy3pXrba5jqVwYi9 X-Gm-Gg: AR+sD10oWVkxRT2aK2u/0YnjhfX3FPWEqYU4UTojzesGHOJrcE3ph92oL+agbOFOhyu lotBSM8yPjUhzJ/UKoOepCWGy918SiTuKDqvRXOcb9Fn0JJzMns1Y6ggT5FVXXr95RpU4mArNwL SgwKvde6k8u8ObREWrX/R0+bU2aHN4b9vICKiqfCPlNDNM22bluye5P184utKOqNwH5kJeTOzZo ChH1K4nQsCiTQoO/ExLvDYTu/WYd+4OA08F0bruTreoRwvYtrpX+ew1Shq1V5GSkBhABlw4XLPY i2mQ59gaEVTt9NuLZ8aMntOVT0EXi/CyYxDQQ4FiZC9DoszIjff3F4PyOaggqHzvxgPR4fFvTbB 0YCeBFUKYapk7nopmpfN0k/BF9C6AULsYLmwjglvG8U9E0LTix5E29aGmHJMSg6oVZRNFba3N8Q OO1D4vnCeMuVcplXq0MiHWq8CI5B/uZ/m6oUp6xuL1nWrqYyksZaw3WX1xYp56LFFwy/5w/Jf+l S1PvXp/A49JNA6OxcFXi7ZNZvbB5fKcqJ1jDuUC8PmcXHZe9fLk X-Received: by 2002:a05:600c:3155:b0:49b:910c:7703 with SMTP id 5b1f17b1804b1-49b91c3dcb4mr475751855e9.9.1788251429893; Tue, 01 Sep 2026 01:30:29 -0700 (PDT) Received: from Ansuel-XPS24.localdomain (host-79-26-252-140.retail.telecomitalia.it. [79.26.252.140]) by smtp.googlemail.com with ESMTPSA id 5b1f17b1804b1-49cdce08862sm51459635e9.1.2026.09.01.01.30.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Sep 2026 01:30:28 -0700 (PDT) From: Christian Marangi To: Maxime Chevallier , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Simon Horman , Jonathan Corbet , Shuah Khan , Randy Dunlap , Christian Marangi , Lorenzo Bianconi , Heiner Kallweit , Russell King , Philipp Zabel , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt , netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, llvm@lists.linux.dev Subject: [PATCH net-next v15 06/12] net: phylink: support late PCS provider attach Date: Tue, 1 Sep 2026 10:29:21 +0200 Message-ID: <20260901083001.8044-7-ansuelsmth@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260901083001.8044-1-ansuelsmth@gmail.com> References: <20260901083001.8044-1-ansuelsmth@gmail.com> Precedence: bulk X-Mailing-List: linux-doc@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add support for late PCS provider attachment to a phylink instance. Similar to FWNODE_PCS_PROVIDER_DEL, FWNODE_PCS_PROVIDER_ADD is added to address the case where a PCS provider is added after a phylink instance is created and started. The PCS notifier will emit the event FWNODE_PCS_PROVIDER_ADD every time a new PCS provider is added. If a related PCS is found, then such PCS is added to the phylink instance PCS list. Such PCS is then linked to the phylink instance and the supported interfaces of the phylink instance get refreshed. If we are in a major_config_failed scenario an interface reconfiguration is triggered in the next phylink resolve. In the example scenario where the link was previously torn down due to removal of PCS, the link will be established again as the PCS came back and is now available to phylink. Signed-off-by: Christian Marangi --- drivers/net/pcs/pcs.c | 22 +++++++++++ drivers/net/phy/phylink.c | 82 +++++++++++++++++++++++++++++++-------- include/linux/pcs/pcs.h | 31 +++++++++++++++ 3 files changed, 118 insertions(+), 17 deletions(-) diff --git a/drivers/net/pcs/pcs.c b/drivers/net/pcs/pcs.c index 25b3248e8b93..fef560eddf2a 100644 --- a/drivers/net/pcs/pcs.c +++ b/drivers/net/pcs/pcs.c @@ -69,6 +69,10 @@ fwnode_pcs_add_provider(struct fwnode_handle *fwnode, mutex_unlock(&fwnode_pcs_mutex); + blocking_notifier_call_chain(&fwnode_pcs_notify_list, + FWNODE_PCS_PROVIDER_ADD, + pp); + pr_debug("Added pcs provider from %pfwf\n", fwnode); return pp; @@ -201,6 +205,24 @@ struct phylink_pcs *fwnode_pcs_get(const struct fwnode_handle *fwnode, unsigned } EXPORT_SYMBOL_GPL(fwnode_pcs_get); +struct phylink_pcs *fwnode_pcs_get_from_provider(struct fwnode_pcs_provider *provider, + const struct fwnode_handle *fwnode, + int index) +{ + struct fwnode_reference_args pcsspec; + struct phylink_pcs *pcs; + int ret; + + ret = fwnode_parse_pcsspec(fwnode, index, NULL, &pcsspec); + if (ret) + return ERR_PTR(ret); + + pcs = __fwnode_pcs_get_from_pcsspec_provider(&pcsspec, provider); + fwnode_handle_put(pcsspec.fwnode); + return pcs; +} +EXPORT_SYMBOL_GPL(fwnode_pcs_get_from_provider); + bool fwnode_pcs_matches_provider(struct fwnode_pcs_provider *provider, const struct fwnode_handle *fwnode, struct phylink_pcs *pl_pcs) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index e88c1effe880..a68d77ba5728 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1926,6 +1926,27 @@ int phylink_set_fixed_link(struct phylink *pl, } EXPORT_SYMBOL_GPL(phylink_set_fixed_link); +static void phylink_add_pcs(struct phylink *pl, struct phylink_pcs *pcs) +{ + struct phylink_pcs *tmp; + + /* + * Make sure state mutex is locked to protect concurrent + * access to phylink instance PCS list from + * initial fill_available_pcs and late PCS attach + */ + lockdep_assert_held(&pl->state_mutex); + + list_for_each_entry(tmp, &pl->pcs_list, list) + if (tmp == pcs) + return; + + list_add_tail(&pcs->list, &pl->pcs_list); + + /* Link PCS to phylink */ + pcs->phylink = pl; +} + static int phylink_fill_available_pcs(struct phylink *pl, struct phylink_config *config) { @@ -1958,7 +1979,7 @@ static int phylink_fill_available_pcs(struct phylink *pl, if (!pcs) continue; - list_add_tail(&pcs->list, &pl->pcs_list); + phylink_add_pcs(pl, pcs); } out: @@ -2004,27 +2025,57 @@ static int pcs_provider_notify(struct notifier_block *self, struct fwnode_pcs_provider *pp = data; struct phylink_pcs *pcs, *tmp; bool resolve = false; + int count, i; - rtnl_lock(); + /* + * On PCS provider deletion hold rtnl lock as one of + * PCS can be currently in use by the phylink instance + * and ethtool OPs can reference it. + */ + if (val == FWNODE_PCS_PROVIDER_DEL) + rtnl_lock(); mutex_lock(&pl->state_mutex); - /* - * Loop all the PCS for phylink instance and check if - * this notification is relevant for some of them. - */ - list_for_each_entry_safe(pcs, tmp, &pl->pcs_list, list) { - if (!fwnode_pcs_matches_provider(pp, pl->fwnode, pcs)) - continue; + switch (val) { + case FWNODE_PCS_PROVIDER_ADD: + count = fwnode_phylink_pcs_count(pl->fwnode); + for (i = 0; i < count; i++) { + pcs = fwnode_pcs_get_from_provider(pp, pl->fwnode, i); + if (IS_ERR(pcs)) + continue; + + phylink_add_pcs(pl, pcs); + resolve = true; + } + + /* Force an interface reconfig if major config fail */ + if (resolve && pl->major_config_failed) + pl->force_major_config = true; - phylink_del_pcs(pl, pcs); - resolve = true; + break; + case FWNODE_PCS_PROVIDER_DEL: + /* + * Loop all the PCS for phylink instance and check if + * this notification is relevant for some of them. + */ + list_for_each_entry_safe(pcs, tmp, &pl->pcs_list, list) { + if (!fwnode_pcs_matches_provider(pp, pl->fwnode, pcs)) + continue; + + phylink_del_pcs(pl, pcs); + resolve = true; + } + break; } /* Exit early if nothing has changed */ if (!resolve) { mutex_unlock(&pl->state_mutex); - rtnl_unlock(); + + if (val == FWNODE_PCS_PROVIDER_DEL) + rtnl_unlock(); + return NOTIFY_DONE; } @@ -2038,7 +2089,8 @@ static int pcs_provider_notify(struct notifier_block *self, mutex_unlock(&pl->state_mutex); - rtnl_unlock(); + if (val == FWNODE_PCS_PROVIDER_DEL) + rtnl_unlock(); phylink_run_resolve(pl); @@ -2146,10 +2198,6 @@ struct phylink *phylink_create(struct phylink_config *config, mutex_lock(&pl->state_mutex); - /* Link available PCS to phylink */ - list_for_each_entry(pcs, &pl->pcs_list, list) - pcs->phylink = pl; - phy_interface_copy(pl->supported_interfaces, pl->config->supported_interfaces); diff --git a/include/linux/pcs/pcs.h b/include/linux/pcs/pcs.h index 83e0a2ad4b09..00a636c82c65 100644 --- a/include/linux/pcs/pcs.h +++ b/include/linux/pcs/pcs.h @@ -5,6 +5,7 @@ #include enum fwnode_pcs_notify_event { + FWNODE_PCS_PROVIDER_ADD, FWNODE_PCS_PROVIDER_DEL, }; @@ -50,6 +51,28 @@ int unregister_fwnode_pcs_notifier(struct notifier_block *nb); struct phylink_pcs *fwnode_pcs_get(const struct fwnode_handle *fwnode, unsigned int index); +/** + * fwnode_pcs_get_from_provider() - Retrieve a PCS from a specific provider + * @provider: PCS provider to use + * @fwnode: firmware node + * @index: index fwnode PCS handle in firmware node + * + * Get a PCS from the firmware node at index specifically provided by + * passed PCS provider. + * + * Unlike fwnode_pcs_get(), this function does not search the global list of + * PCS providers. The caller must provide the provider responsible for the + * referenced PCS. + * + * Returns: a pointer to the phylink_pcs or a negative error pointer. Can + * return -ENODEV if the PCS is not present in global providers list (either + * due to driver still needs to be probed or it failed to probe/removed) or + * can return -EINVAL if the PCS provider doesn't expose any PCS for the fwnode. + */ +struct phylink_pcs *fwnode_pcs_get_from_provider(struct fwnode_pcs_provider *provider, + const struct fwnode_handle *fwnode, + int index); + /** * fwnode_pcs_matches_provider() - Check whether a PCS belongs to a provider * @provider: PCS provider to check @@ -120,6 +143,14 @@ static inline struct phylink_pcs *fwnode_pcs_get(const struct fwnode_handle *fwn return ERR_PTR(-ENOENT); } +static inline struct phylink_pcs * +fwnode_pcs_get_from_provider(struct fwnode_pcs_provider *provider, + const struct fwnode_handle *fwnode, + int index) +{ + return ERR_PTR(-ENOENT); +} + static inline bool fwnode_pcs_matches_provider(struct fwnode_pcs_provider *provider, const struct fwnode_handle *fwnode, struct phylink_pcs *pl_pcs) -- 2.53.0