diff --git a/net/can/j1939/bus.c b/net/can/j1939/bus.c index cdc3c0a71937..91a7b91ca00a 100644 --- a/net/can/j1939/bus.c +++ b/net/can/j1939/bus.c @@ -14,17 +14,27 @@ #include "j1939-priv.h" -static void __j1939_ecu_release(struct kref *kref) +static void __j1939_ecu_work(struct work_struct *work) { - struct j1939_ecu *ecu = container_of(kref, struct j1939_ecu, kref); + struct j1939_ecu *ecu = container_of(work, struct j1939_ecu, work); struct j1939_priv *priv = ecu->priv; + write_lock(&priv->lock); list_del(&ecu->list); netdev_put(priv->ndev, &ecu->priv_dev_tracker); kfree(ecu); + write_unlock(&priv->lock); + j1939_priv_put(priv); } +static void __j1939_ecu_release(struct kref *kref) +{ + struct j1939_ecu *ecu = container_of(kref, struct j1939_ecu, kref); + + schedule_work(&ecu->work); +} + void j1939_ecu_put(struct j1939_ecu *ecu) { kref_put(&ecu->kref, __j1939_ecu_release); @@ -46,27 +56,23 @@ static bool j1939_ecu_is_mapped_locked(struct j1939_ecu *ecu) /* ECU device interface */ /* map ECU to a bus address space */ -static void j1939_ecu_map_locked(struct j1939_ecu *ecu) +static void j1939_ecu_map_atomic(struct j1939_ecu *ecu) { struct j1939_priv *priv = ecu->priv; struct j1939_addr_ent *ent; - lockdep_assert_held(&priv->lock); - if (!j1939_address_is_unicast(ecu->addr)) return; ent = &priv->ents[ecu->addr]; - if (ent->ecu) { - netdev_warn(priv->ndev, "Trying to map already mapped ECU, addr: 0x%02x, name: 0x%016llx. Skip it.\n", + if (cmpxchg(&ent->ecu, NULL, ecu) == NULL) { + j1939_ecu_get(ecu); + ent->nusers += ecu->nusers; + } else + netdev_warn(priv->ndev, "Trying to map already mapped ECU, " + "addr: 0x%02x, name: 0x%016llx. Skip it.\n", ecu->addr, ecu->name); - return; - } - - j1939_ecu_get(ecu); - ent->ecu = ecu; - ent->nusers += ecu->nusers; } /* unmap ECU from a bus address space */ @@ -96,6 +102,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; @@ -129,19 +145,16 @@ static enum hrtimer_restart j1939_ecu_timer_handler(struct hrtimer *hrtimer) { struct j1939_ecu *ecu = 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); + j1939_ecu_map_atomic(ecu); /* The corresponding j1939_ecu_get() is in * j1939_ecu_timer_start(). */ j1939_ecu_put(ecu); - write_unlock_bh(&priv->lock); return HRTIMER_NORESTART; } @@ -156,6 +169,7 @@ struct j1939_ecu *j1939_ecu_create_locked(struct j1939_priv *priv, name_t name) if (!ecu) return ERR_PTR(-ENOMEM); kref_init(&ecu->kref); + INIT_WORK(&ecu->work, __j1939_ecu_work); netdev_hold(priv->ndev, &ecu->priv_dev_tracker, gfp_any()); ecu->addr = J1939_IDLE_ADDR; ecu->name = name; diff --git a/net/can/j1939/j1939-priv.h b/net/can/j1939/j1939-priv.h index cf26352d1d8c..e970a572ee1d 100644 --- a/net/can/j1939/j1939-priv.h +++ b/net/can/j1939/j1939-priv.h @@ -30,6 +30,7 @@ enum j1939_sk_errqueue_type { /* j1939 devices */ struct j1939_ecu { + struct work_struct work; struct list_head list; name_t name; u8 addr; @@ -204,6 +205,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..1f68ba803700 100644 --- a/net/can/j1939/main.c +++ b/net/can/j1939/main.c @@ -306,6 +306,7 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev) void j1939_netdev_stop(struct j1939_priv *priv) { + j1939_ecu_cancel_all(priv); kref_put_mutex(&priv->rx_kref, __j1939_rx_release, &j1939_netdev_lock); j1939_priv_put(priv); }