mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
net: rmnet: no longer rely on RTNL in rmnet_fill_info()
rmnet_fill_info() used rmnet_get_port_rtnl(), and thus required RTNL. Now that priv->mux_id and port->data_format are properly annotated, rmnet_fill_info() can simply fetch the port under rcu_read_lock(). rmnet_get_port_rcu() was only used from the data path and thus used rcu_dereference_bh(). Relax its lockdep condition so that it can also be called from process context under a plain rcu_read_lock(), and use it from rmnet_fill_info(). Note that rmnet_get_port_rcu() checks real_dev->rx_handler before returning rx_handler_data: this is what makes the lockless lookup safe against rmnet_unregister_real_device(), which clears rx_handler, waits for a grace period in netdev_rx_handler_unregister(), and only then frees the port. While at it, add missing const qualifiers. Signed-off-by: Eric Dumazet <edumazet@google.com> Cc: Sean Tranchetti <sean.tranchetti@oss.qualcomm.com> Reviewed-by: Subash Abhinov Kasiviswanathan <subash.a.kasiviswanathan@oss.qualcomm.com> Link: https://patch.msgid.link/20260916132231.1780091-4-edumazet@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
0067187dbe
commit
d7c9ba103b
|
|
@ -377,32 +377,24 @@ static size_t rmnet_get_size(const struct net_device *dev)
|
|||
|
||||
static int rmnet_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(dev);
|
||||
struct net_device *real_dev;
|
||||
const struct rmnet_priv *priv = netdev_priv(dev);
|
||||
const struct rmnet_port *port;
|
||||
struct ifla_rmnet_flags f;
|
||||
struct rmnet_port *port;
|
||||
|
||||
real_dev = priv->real_dev;
|
||||
if (nla_put_u16(skb, IFLA_RMNET_MUX_ID, READ_ONCE(priv->mux_id)))
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (nla_put_u16(skb, IFLA_RMNET_MUX_ID, priv->mux_id))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (rmnet_is_real_dev_registered(real_dev)) {
|
||||
port = rmnet_get_port_rtnl(real_dev);
|
||||
f.flags = port->data_format;
|
||||
} else {
|
||||
f.flags = 0;
|
||||
}
|
||||
rcu_read_lock();
|
||||
port = rmnet_get_port_rcu(priv->real_dev);
|
||||
f.flags = port ? READ_ONCE(port->data_format) : 0;
|
||||
rcu_read_unlock();
|
||||
|
||||
f.mask = ~0;
|
||||
|
||||
if (nla_put(skb, IFLA_RMNET_FLAGS, sizeof(f), &f))
|
||||
goto nla_put_failure;
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
struct rtnl_link_ops rmnet_link_ops __read_mostly = {
|
||||
|
|
@ -419,12 +411,16 @@ struct rtnl_link_ops rmnet_link_ops __read_mostly = {
|
|||
.fill_info = rmnet_fill_info,
|
||||
};
|
||||
|
||||
struct rmnet_port *rmnet_get_port_rcu(struct net_device *real_dev)
|
||||
/* Can be called from a RCU read-side critical section, with or
|
||||
* without BH disabled.
|
||||
*/
|
||||
struct rmnet_port *rmnet_get_port_rcu(const struct net_device *real_dev)
|
||||
{
|
||||
if (rmnet_is_real_dev_registered(real_dev))
|
||||
return rcu_dereference_bh(real_dev->rx_handler_data);
|
||||
else
|
||||
if (!rmnet_is_real_dev_registered(real_dev))
|
||||
return NULL;
|
||||
|
||||
return rcu_dereference_check(real_dev->rx_handler_data,
|
||||
rcu_read_lock_bh_held());
|
||||
}
|
||||
|
||||
struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id)
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ struct rmnet_priv {
|
|||
struct rmnet_priv_stats stats;
|
||||
};
|
||||
|
||||
struct rmnet_port *rmnet_get_port_rcu(struct net_device *real_dev);
|
||||
struct rmnet_port *rmnet_get_port_rcu(const struct net_device *real_dev);
|
||||
struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id);
|
||||
int rmnet_add_bridge(struct net_device *rmnet_dev,
|
||||
struct net_device *slave_dev,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user