diff --git a/net/can/j1939/bus.c b/net/can/j1939/bus.c index cdc3c0a71937..3f3ee6199204 100644 --- a/net/can/j1939/bus.c +++ b/net/can/j1939/bus.c @@ -96,6 +96,16 @@ void j1939_ecu_unmap(struct j1939_ecu *ecu) write_unlock_bh(&ecu->priv->lock); } +void j1939_ecu_cancel_all(struct j1939_priv *priv) +{ + struct j1939_ecu *ecu, *tmp; + + write_lock_bh(&priv->lock); + list_for_each_entry_safe(ecu, tmp, &priv->ecus, list) + j1939_ecu_timer_cancel(ecu); + write_unlock_bh(&priv->lock); +} + void j1939_ecu_unmap_all(struct j1939_priv *priv) { int i; @@ -131,18 +141,22 @@ static enum hrtimer_restart j1939_ecu_timer_handler(struct hrtimer *hrtimer) container_of(hrtimer, struct j1939_ecu, ac_timer); struct j1939_priv *priv = ecu->priv; - write_lock_bh(&priv->lock); - /* TODO: can we test if ecu->addr is unicast before starting - * the timer? - */ - j1939_ecu_map_locked(ecu); - - /* The corresponding j1939_ecu_get() is in - * j1939_ecu_timer_start(). - */ - j1939_ecu_put(ecu); - write_unlock_bh(&priv->lock); - + /* We've asked to stop from j1939_netdev_stop(). */ + if (unlikely(atomic_read(&priv->stop))) + j1939_ecu_put(ecu); + else { + write_lock_bh(&priv->lock); + /* TODO: can we test if ecu->addr is unicast before starting + * the timer? + */ + j1939_ecu_map_locked(ecu); + + /* The corresponding j1939_ecu_get() is in + * j1939_ecu_timer_start(). + */ + j1939_ecu_put(ecu); + write_unlock_bh(&priv->lock); + } return HRTIMER_NORESTART; } diff --git a/net/can/j1939/j1939-priv.h b/net/can/j1939/j1939-priv.h index cf26352d1d8c..1e8f018b7c8b 100644 --- a/net/can/j1939/j1939-priv.h +++ b/net/can/j1939/j1939-priv.h @@ -60,6 +60,11 @@ struct j1939_priv { /* segments need a lock to protect the above list */ rwlock_t lock; + /* Used to avoid deadlock between j1939_ecu_cancel_all() + * and timer callbacks. + */ + atomic_t stop; + struct net_device *ndev; netdevice_tracker dev_tracker; @@ -204,6 +209,7 @@ struct j1939_ecu *j1939_ecu_create_locked(struct j1939_priv *priv, name_t name); void j1939_ecu_timer_start(struct j1939_ecu *ecu); void j1939_ecu_timer_cancel(struct j1939_ecu *ecu); +void j1939_ecu_cancel_all(struct j1939_priv *priv); void j1939_ecu_unmap_all(struct j1939_priv *priv); struct j1939_priv *j1939_netdev_start(struct net_device *ndev); diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c index 5e5e6c228f22..22ea2b000185 100644 --- a/net/can/j1939/main.c +++ b/net/can/j1939/main.c @@ -306,6 +306,8 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev) void j1939_netdev_stop(struct j1939_priv *priv) { + atomic_set(&priv->stop, 1); + j1939_ecu_cancel_all(priv); kref_put_mutex(&priv->rx_kref, __j1939_rx_release, &j1939_netdev_lock); j1939_priv_put(priv); }