diff options
author | Jukka Rissanen <jukka.rissanen@linux.intel.com> | 2014-06-18 15:37:11 +0200 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2014-07-03 17:42:44 +0200 |
commit | 7f118253820fc3ad38659485adb3ebdfe64820e1 (patch) | |
tree | cd67fbc21f11ab11d5db2dd8ab885ca15d98ff2e /net/bluetooth/6lowpan.c | |
parent | Bluetooth: 6LoWPAN: Count module usage (diff) | |
download | linux-7f118253820fc3ad38659485adb3ebdfe64820e1.tar.xz linux-7f118253820fc3ad38659485adb3ebdfe64820e1.zip |
Bluetooth: 6LoWPAN: Remove network devices when unloading
When the module is unloaded, unregister the network device
so that the system does not try to access non-existing device.
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth/6lowpan.c')
-rw-r--r-- | net/bluetooth/6lowpan.c | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index ba6c64163685..5a7f81df603c 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -607,6 +607,17 @@ static void ifup(struct net_device *netdev) rtnl_unlock(); } +static void ifdown(struct net_device *netdev) +{ + int err; + + rtnl_lock(); + err = dev_close(netdev); + if (err < 0) + BT_INFO("iface %s cannot be closed (%d)", netdev->name, err); + rtnl_unlock(); +} + static void do_notify_peers(struct work_struct *work) { struct lowpan_dev *dev = container_of(work, struct lowpan_dev, @@ -829,6 +840,8 @@ static void chan_close_cb(struct l2cap_chan *chan) cancel_delayed_work_sync(&dev->notify_peers); + ifdown(dev->netdev); + if (!removed) { INIT_WORK(&entry->delete_netdev, delete_netdev); schedule_work(&entry->delete_netdev); @@ -1186,6 +1199,43 @@ static const struct file_operations lowpan_control_fops = { .release = single_release, }; +static void disconnect_devices(void) +{ + struct lowpan_dev *entry, *tmp, *new_dev; + struct list_head devices; + unsigned long flags; + + INIT_LIST_HEAD(&devices); + + /* We make a separate list of devices because the unregister_netdev() + * will call device_event() which will also want to modify the same + * devices list. + */ + + read_lock_irqsave(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { + new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC); + if (!new_dev) + break; + + new_dev->netdev = entry->netdev; + INIT_LIST_HEAD(&new_dev->list); + + list_add(&new_dev->list, &devices); + } + + read_unlock_irqrestore(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp, &devices, list) { + ifdown(entry->netdev); + BT_DBG("Unregistering netdev %s %p", + entry->netdev->name, entry->netdev); + unregister_netdev(entry->netdev); + kfree(entry); + } +} + static int device_event(struct notifier_block *unused, unsigned long event, void *ptr) { @@ -1202,6 +1252,8 @@ static int device_event(struct notifier_block *unused, list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { if (entry->netdev == netdev) { + BT_DBG("Unregistered netdev %s %p", + netdev->name, netdev); list_del(&entry->list); kfree(entry); break; @@ -1240,6 +1292,8 @@ static void __exit bt_6lowpan_exit(void) l2cap_chan_put(listen_chan); } + disconnect_devices(); + unregister_netdevice_notifier(&bt_6lowpan_dev_notifier); } |