From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 706AB4014AF; Wed, 25 Mar 2026 14:51:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774450279; cv=none; b=eo7M22l6oXbmrcoHpSnWa1GWHTbrMWbYz4wHMGZ9pQXKdvD3CHA2aQnXNMNtV1vw83oW+EfV1G639EplDX/oH0aru+7yEtBHpHHNT/2xUoW3ePcw+DDv1wHY/lfVgQenI7BTNQWI42aDG7QYicNTawXHohYNzzZtoSqpEk1iaws= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774450279; c=relaxed/simple; bh=y72tF5ZO1RpjsdtJDjYX62wJxRaUNOVIyHIWNuLrxeU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=jB6TDEXVVOpH/sNQtZa+K+1cr2+SiuNxLvGG5DRZaWXknPAcAlhrzH1PCQ1YZIs8usc3qq3jkvx7bSKlRgdxUtnTOwfY4mGz+ztrHR0DHkP67idPT88HEuqKBTbBtvamHdCf4QNcv9ybYWdm6eFXA4nugZNdSk+QubSLyWbEzHM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dJJPFq3g; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="dJJPFq3g" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 83603C2BCC6; Wed, 25 Mar 2026 14:51:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774450278; bh=y72tF5ZO1RpjsdtJDjYX62wJxRaUNOVIyHIWNuLrxeU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dJJPFq3gAqTeLQfuxDc0kmHVarENYUZTf8QFBDuUDBCirwA+h2W0ltYS/T91tMxwp kj4epmKvrl4BxWCZLBoegOuuJM2TzL2McuK5nT7WJa1rCmoss/QEJVDguUjXJuuTjp bd+btA1kvGIU/yssn7cvO/OFDw+F4s227EQG6gyInleasoWxEZzIIAPBB6IduluyFF TT8z9cwmpaJNUzjXmOJlLZRPeK5ZnYadDHp1u+7AnoGvM90dOkI956ylhk9rXH2Z0M kY96sLqiJSeD/MGx0dXlHrvdfc2SPfWZmqBgD7IB6jD7Wxbdk554P75r3e17+3Ao2P k2KJOzJvjd1mg== From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= To: netdev@vger.kernel.org, "David S. Miller" , Andrew Lunn , Donald Hunter , Eric Dumazet , Jakub Kicinski , Maxime Chevallier , Naveen Mamindlapalli , Paolo Abeni , Simon Horman Cc: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= , Danielle Ratson , Hariprasad Kelam , Ido Schimmel , Kory Maincent , Leon Romanovsky , Michael Chan , Oleksij Rempel , Pavan Chebbi , Piergiorgio Beruto , Russell King , Saeed Mahameed , Shuah Khan , Tariq Toukan , Willem de Bruijn , Kees Cook , linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-rdma@vger.kernel.org Subject: [PATCH net-next v2 07/12] ethtool: Add MAC loopback support via ethtool_ops Date: Wed, 25 Mar 2026 15:50:14 +0100 Message-ID: <20260325145022.2607545-8-bjorn@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260325145022.2607545-1-bjorn@kernel.org> References: <20260325145022.2607545-1-bjorn@kernel.org> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend struct ethtool_ops with three loopback callbacks that drivers can implement for MAC-level loopback control: - get_loopback(dev, name, id, entry): exact lookup by name and instance id, used by doit (single-entry GET) requests. - get_loopback_by_index(dev, index, entry): flat enumeration by index, used by dumpit (multi-entry GET) requests to iterate all loopback points on a device. - set_loopback(dev, entry, extack): apply a loopback configuration change. Returns 1 if hardware state changed, 0 if no-op. Wire the MAC component into loopback.c's dispatch functions. For dump enumeration, MAC entries are tried first via the driver's get_loopback_by_index() op, then MODULE/CMIS entries follow at the next index offset. Signed-off-by: Björn Töpel --- include/linux/ethtool.h | 10 ++++++++ net/ethtool/loopback.c | 56 ++++++++++++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 81a9c186564d..971be759a915 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -1168,6 +1168,9 @@ struct kernel_ethtool_ts_info { * @get_mm: Query the 802.3 MAC Merge layer state. * @set_mm: Set the 802.3 MAC Merge layer parameters. * @get_mm_stats: Query the 802.3 MAC Merge layer statistics. + * @get_loopback: Get the state of a loopback entry identified by name and id. + * @get_loopback_by_index: Get the state of a loopback entry by its index. + * @set_loopback: Set the loopback mode for a given entry. * * All operations are optional (i.e. the function pointer may be set * to %NULL) and callers must take this into account. Callers must @@ -1337,6 +1340,13 @@ struct ethtool_ops { int (*set_mm)(struct net_device *dev, struct ethtool_mm_cfg *cfg, struct netlink_ext_ack *extack); void (*get_mm_stats)(struct net_device *dev, struct ethtool_mm_stats *stats); + int (*get_loopback)(struct net_device *dev, const char *name, + u32 id, struct ethtool_loopback_entry *entry); + int (*get_loopback_by_index)(struct net_device *dev, u32 index, + struct ethtool_loopback_entry *entry); + int (*set_loopback)(struct net_device *dev, + const struct ethtool_loopback_entry *entry, + struct netlink_ext_ack *extack); }; int ethtool_check_ops(const struct ethtool_ops *ops); diff --git a/net/ethtool/loopback.c b/net/ethtool/loopback.c index 2d0ad62ce42f..60eb7b94a716 100644 --- a/net/ethtool/loopback.c +++ b/net/ethtool/loopback.c @@ -88,6 +88,10 @@ static int loopback_get(struct net_device *dev, struct ethtool_loopback_entry *entry) { switch (component) { + case ETHTOOL_LOOPBACK_COMPONENT_MAC: + if (!dev->ethtool_ops->get_loopback) + return -EOPNOTSUPP; + return dev->ethtool_ops->get_loopback(dev, name, id, entry); case ETHTOOL_LOOPBACK_COMPONENT_MODULE: return ethtool_cmis_get_loopback(dev, name, entry); default: @@ -95,10 +99,22 @@ static int loopback_get(struct net_device *dev, } } -static int loopback_get_by_index(struct net_device *dev, u32 index, +static int loopback_get_by_index(struct net_device *dev, + enum ethtool_loopback_component component, + u32 index, struct ethtool_loopback_entry *entry) { - return ethtool_cmis_get_loopback_by_index(dev, index, entry); + switch (component) { + case ETHTOOL_LOOPBACK_COMPONENT_MAC: + if (!dev->ethtool_ops->get_loopback_by_index) + return -EOPNOTSUPP; + return dev->ethtool_ops->get_loopback_by_index(dev, index, + entry); + case ETHTOOL_LOOPBACK_COMPONENT_MODULE: + return ethtool_cmis_get_loopback_by_index(dev, index, entry); + default: + return -EOPNOTSUPP; + } } static int loopback_prepare_data(const struct ethnl_req_info *req_base, @@ -118,7 +134,8 @@ static int loopback_prepare_data(const struct ethnl_req_info *req_base, ret = loopback_get(dev, req_info->component, req_info->id, req_info->name, &data->entry); else - ret = loopback_get_by_index(dev, req_info->index, &data->entry); + ret = loopback_get_by_index(dev, req_info->component, + req_info->index, &data->entry); ethnl_ops_complete(dev); @@ -235,6 +252,10 @@ static int __loopback_set(struct net_device *dev, struct netlink_ext_ack *extack) { switch (entry->component) { + case ETHTOOL_LOOPBACK_COMPONENT_MAC: + if (!dev->ethtool_ops->set_loopback) + return -EOPNOTSUPP; + return dev->ethtool_ops->set_loopback(dev, entry, extack); case ETHTOOL_LOOPBACK_COMPONENT_MODULE: return ethtool_cmis_set_loopback(dev, entry, extack); default: @@ -284,20 +305,31 @@ static int loopback_dump_one_dev(struct sk_buff *skb, { struct loopback_req_info *req_info = container_of(ctx->req_info, struct loopback_req_info, base); + /* pos_sub encodes: upper 16 bits = component phase, lower 16 = index + * within that component. dump_one_dev is called repeatedly with + * increasing pos_sub until all components are exhausted. + */ + enum ethtool_loopback_component phase = *pos_sub >> 16; + u32 idx = *pos_sub & 0xffff; int ret; - for (;; (*pos_sub)++) { - req_info->index = *pos_sub; - ret = ethnl_default_dump_one(skb, ctx->req_info->dev, ctx, - info); - if (ret == -EOPNOTSUPP) - break; - if (ret) - return ret; + for (; phase <= ETHTOOL_LOOPBACK_COMPONENT_MODULE; phase++) { + for (;; idx++) { + req_info->component = phase; + req_info->index = idx; + ret = ethnl_default_dump_one(skb, ctx->req_info->dev, + ctx, info); + if (ret == -EOPNOTSUPP) + break; + if (ret) { + *pos_sub = ((unsigned long)phase << 16) | idx; + return ret; + } + } + idx = 0; } *pos_sub = 0; - return 0; } -- 2.53.0