net: ethtool: serialize broadcast notification sequence allocation

ethnl_bcast_seq is a global counter stamped into the nlmsg_seq field
of every multicast notification, allowing userspace to detect dropped
messages. Today the ordering is achieved by using rtnl_lock().

Moving forward we will want ethtool ops to run under just the netdev
instance lock so to establish ordering we need a separate lock
for notifications. With the netdev instance locks operations on
different devices may bypass each other but the expectation is
that it should not matter. What we need to prevent is:
 - notification IDs getting out of order
 - operations on one device getting out of order

For simplicity defer allocating the ID of the notification right
before the notification is delivered. This removes the need for
special handling in ethnl_rss_create_send_ntf().

Reviewed-by: Eric Dumazet <edumazet@google.com>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Link: https://patch.msgid.link/20260605002912.3456868-2-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2026-06-04 17:29:01 -07:00
parent 9415471e01
commit 8845484367
3 changed files with 17 additions and 13 deletions

View File

@@ -13,6 +13,12 @@
static struct genl_family ethtool_genl_family;
static bool ethnl_ok __read_mostly;
/* Serializes broadcast notification sequence allocation with the multicast
* send, so that userspace observes nlmsg_seq monotonic in receive order
* regardless of which lock the caller holds (rtnl or instance lock).
*/
static DEFINE_MUTEX(ethnl_bcast_lock);
static u32 ethnl_bcast_seq;
#define ETHTOOL_FLAGS_BASIC (ETHTOOL_FLAG_COMPACT_BITSETS | \
@@ -82,12 +88,6 @@ static void ethnl_sock_priv_destroy(void *priv)
}
}
u32 ethnl_bcast_seq_next(void)
{
ASSERT_RTNL();
return ++ethnl_bcast_seq;
}
int ethnl_ops_begin(struct net_device *dev)
{
int ret;
@@ -329,8 +329,7 @@ void *ethnl_dump_put(struct sk_buff *skb, struct netlink_callback *cb, u8 cmd)
void *ethnl_bcastmsg_put(struct sk_buff *skb, u8 cmd)
{
return genlmsg_put(skb, 0, ++ethnl_bcast_seq, &ethtool_genl_family, 0,
cmd);
return genlmsg_put(skb, 0, 0, &ethtool_genl_family, 0, cmd);
}
void *ethnl_unicast_put(struct sk_buff *skb, u32 portid, u32 seq, u8 cmd)
@@ -340,8 +339,15 @@ void *ethnl_unicast_put(struct sk_buff *skb, u32 portid, u32 seq, u8 cmd)
int ethnl_multicast(struct sk_buff *skb, struct net_device *dev)
{
return genlmsg_multicast_netns(&ethtool_genl_family, dev_net(dev), skb,
0, ETHNL_MCGRP_MONITOR, GFP_KERNEL);
struct nlmsghdr *nlh = nlmsg_hdr(skb);
int ret;
mutex_lock(&ethnl_bcast_lock);
nlh->nlmsg_seq = ++ethnl_bcast_seq;
ret = genlmsg_multicast_netns(&ethtool_genl_family, dev_net(dev), skb,
0, ETHNL_MCGRP_MONITOR, GFP_KERNEL);
mutex_unlock(&ethnl_bcast_lock);
return ret;
}
/* GET request helpers */
@@ -1081,7 +1087,7 @@ void ethnl_notify(struct net_device *dev, unsigned int cmd,
{
if (unlikely(!ethnl_ok))
return;
ASSERT_RTNL();
netdev_assert_locked_ops_compat(dev);
if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) &&
ethnl_notify_handlers[cmd]))

View File

@@ -10,7 +10,6 @@
struct ethnl_req_info;
u32 ethnl_bcast_seq_next(void);
int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
const struct nlattr *nest, struct net *net,
struct netlink_ext_ack *extack,

View File

@@ -995,7 +995,6 @@ ethnl_rss_create_send_ntf(const struct sk_buff *rsp, struct net_device *dev)
nlh = nlmsg_hdr(ntf);
/* Convert the reply into a notification */
nlh->nlmsg_pid = 0;
nlh->nlmsg_seq = ethnl_bcast_seq_next();
genl_hdr = nlmsg_data(nlh);
genl_hdr->cmd = ETHTOOL_MSG_RSS_CREATE_NTF;