diff options
author | Jukka Rissanen <jukka.rissanen@linux.intel.com> | 2014-06-18 09:37:11 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2014-07-03 11:42:44 -0400 |
commit | 7f118253820fc3ad38659485adb3ebdfe64820e1 (patch) | |
tree | cd67fbc21f11ab11d5db2dd8ab885ca15d98ff2e /net/bluetooth/6lowpan.c | |
parent | 18d93c176641cf7a3c0c452a6f03f46b5373d370 (diff) |
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) | |||
607 | rtnl_unlock(); | 607 | rtnl_unlock(); |
608 | } | 608 | } |
609 | 609 | ||
610 | static void ifdown(struct net_device *netdev) | ||
611 | { | ||
612 | int err; | ||
613 | |||
614 | rtnl_lock(); | ||
615 | err = dev_close(netdev); | ||
616 | if (err < 0) | ||
617 | BT_INFO("iface %s cannot be closed (%d)", netdev->name, err); | ||
618 | rtnl_unlock(); | ||
619 | } | ||
620 | |||
610 | static void do_notify_peers(struct work_struct *work) | 621 | static void do_notify_peers(struct work_struct *work) |
611 | { | 622 | { |
612 | struct lowpan_dev *dev = container_of(work, struct lowpan_dev, | 623 | struct lowpan_dev *dev = container_of(work, struct lowpan_dev, |
@@ -829,6 +840,8 @@ static void chan_close_cb(struct l2cap_chan *chan) | |||
829 | 840 | ||
830 | cancel_delayed_work_sync(&dev->notify_peers); | 841 | cancel_delayed_work_sync(&dev->notify_peers); |
831 | 842 | ||
843 | ifdown(dev->netdev); | ||
844 | |||
832 | if (!removed) { | 845 | if (!removed) { |
833 | INIT_WORK(&entry->delete_netdev, delete_netdev); | 846 | INIT_WORK(&entry->delete_netdev, delete_netdev); |
834 | schedule_work(&entry->delete_netdev); | 847 | schedule_work(&entry->delete_netdev); |
@@ -1186,6 +1199,43 @@ static const struct file_operations lowpan_control_fops = { | |||
1186 | .release = single_release, | 1199 | .release = single_release, |
1187 | }; | 1200 | }; |
1188 | 1201 | ||
1202 | static void disconnect_devices(void) | ||
1203 | { | ||
1204 | struct lowpan_dev *entry, *tmp, *new_dev; | ||
1205 | struct list_head devices; | ||
1206 | unsigned long flags; | ||
1207 | |||
1208 | INIT_LIST_HEAD(&devices); | ||
1209 | |||
1210 | /* We make a separate list of devices because the unregister_netdev() | ||
1211 | * will call device_event() which will also want to modify the same | ||
1212 | * devices list. | ||
1213 | */ | ||
1214 | |||
1215 | read_lock_irqsave(&devices_lock, flags); | ||
1216 | |||
1217 | list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { | ||
1218 | new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC); | ||
1219 | if (!new_dev) | ||
1220 | break; | ||
1221 | |||
1222 | new_dev->netdev = entry->netdev; | ||
1223 | INIT_LIST_HEAD(&new_dev->list); | ||
1224 | |||
1225 | list_add(&new_dev->list, &devices); | ||
1226 | } | ||
1227 | |||
1228 | read_unlock_irqrestore(&devices_lock, flags); | ||
1229 | |||
1230 | list_for_each_entry_safe(entry, tmp, &devices, list) { | ||
1231 | ifdown(entry->netdev); | ||
1232 | BT_DBG("Unregistering netdev %s %p", | ||
1233 | entry->netdev->name, entry->netdev); | ||
1234 | unregister_netdev(entry->netdev); | ||
1235 | kfree(entry); | ||
1236 | } | ||
1237 | } | ||
1238 | |||
1189 | static int device_event(struct notifier_block *unused, | 1239 | static int device_event(struct notifier_block *unused, |
1190 | unsigned long event, void *ptr) | 1240 | unsigned long event, void *ptr) |
1191 | { | 1241 | { |
@@ -1202,6 +1252,8 @@ static int device_event(struct notifier_block *unused, | |||
1202 | list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, | 1252 | list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, |
1203 | list) { | 1253 | list) { |
1204 | if (entry->netdev == netdev) { | 1254 | if (entry->netdev == netdev) { |
1255 | BT_DBG("Unregistered netdev %s %p", | ||
1256 | netdev->name, netdev); | ||
1205 | list_del(&entry->list); | 1257 | list_del(&entry->list); |
1206 | kfree(entry); | 1258 | kfree(entry); |
1207 | break; | 1259 | break; |
@@ -1240,6 +1292,8 @@ static void __exit bt_6lowpan_exit(void) | |||
1240 | l2cap_chan_put(listen_chan); | 1292 | l2cap_chan_put(listen_chan); |
1241 | } | 1293 | } |
1242 | 1294 | ||
1295 | disconnect_devices(); | ||
1296 | |||
1243 | unregister_netdevice_notifier(&bt_6lowpan_dev_notifier); | 1297 | unregister_netdevice_notifier(&bt_6lowpan_dev_notifier); |
1244 | } | 1298 | } |
1245 | 1299 | ||