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 Cc: Subash Abhinov Kasiviswanathan Cc: Sean Tranchetti --- .../ethernet/qualcomm/rmnet/rmnet_config.c | 38 +++++++++---------- .../ethernet/qualcomm/rmnet/rmnet_config.h | 2 +- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c index 248a9d822409b7cbf3739c085487644b7025dd37..b930f638ec448de839806852a74fc4b2f8888e43 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -371,32 +371,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 = { @@ -413,12 +405,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) diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h index f50fae1c6bdd5d4edfb18476e2ca44057921a472..5adda0323dda776d69d50d22bc75456b43442e3d 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h @@ -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, -- 2.55.0.1032.g73a4cd73de-goog