diff --git a/include/net/x25.h b/include/net/x25.h index 414f3fd99345..410e35b94546 100644 --- a/include/net/x25.h +++ b/include/net/x25.h @@ -145,6 +145,7 @@ struct x25_neigh { struct timer_list t20timer; unsigned long global_facil_mask; refcount_t refcnt; + struct work_struct kill_work; }; struct x25_sock { diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 033e7d059f58..12a9b3460e07 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1764,6 +1764,16 @@ static struct notifier_block x25_dev_notifier = { .notifier_call = x25_device_event, }; +static void x25_kill_by_neigh_work(struct work_struct *work) +{ + struct x25_neigh *nb = container_of(work, struct x25_neigh, kill_work); + + x25_kill_by_neigh(nb); +} + +/* Inside x25_link_device_up or wherever x25_neigh is allocated/initialized: */ +INIT_WORK(&nb->kill_work, x25_kill_by_neigh_work); + void x25_kill_by_neigh(struct x25_neigh *nb) { struct sock *s; diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c index d1e8cd81c214..4bc2ab1dd314 100644 --- a/net/x25/x25_link.c +++ b/net/x25/x25_link.c @@ -254,7 +254,7 @@ void x25_link_terminated(struct x25_neigh *nb) x25_stop_t20timer(nb); /* Out of order: clear existing virtual calls (X.25 03/93 4.6.3) */ - x25_kill_by_neigh(nb); + schedule_work(&nb->kill_work); } /* @@ -310,20 +310,23 @@ static void __x25_remove_neigh(struct x25_neigh *nb) void x25_link_device_down(struct net_device *dev) { struct x25_neigh *nb; - struct list_head *entry, *tmp; + LIST_HEAD(release_list); write_lock_bh(&x25_neigh_list_lock); - - list_for_each_safe(entry, tmp, &x25_neigh_list) { - nb = list_entry(entry, struct x25_neigh, node); - + list_for_each_entry_safe(nb, tmp, &x25_neigh_list, node) { if (nb->dev == dev) { - __x25_remove_neigh(nb); - dev_put(dev); + list_del_init(&nb->node); + list_add(&nb->node, &release_list); } } - write_unlock_bh(&x25_neigh_list_lock); + + list_for_each_entry_safe(nb, tmp, &release_list, node) { + list_del(&nb->node); + cancel_work_sync(&nb->kill_work); /* Safe to sleep here! */ + dev_put(dev); + x25_neigh_put(nb); + } } /* @@ -405,17 +408,18 @@ int x25_subscr_ioctl(unsigned int cmd, void __user *arg) void __exit x25_link_free(void) { struct x25_neigh *nb; - struct list_head *entry, *tmp; + LIST_HEAD(release_list); write_lock_bh(&x25_neigh_list_lock); + list_splice_init(&x25_neigh_list, &release_list); + write_unlock_bh(&x25_neigh_list_lock); - list_for_each_safe(entry, tmp, &x25_neigh_list) { - struct net_device *dev; + list_for_each_entry_safe(nb, tmp, &release_list, node) { + struct net_device *dev = nb->dev; - nb = list_entry(entry, struct x25_neigh, node); - dev = nb->dev; - __x25_remove_neigh(nb); + list_del(&nb->node); + cancel_work_sync(&nb->kill_work); dev_put(dev); + x25_neigh_put(nb); } - write_unlock_bh(&x25_neigh_list_lock); }