BT_DBG("dev %p dlc %p", dev, dlc);
- write_lock_bh(&rfcomm_dev_lock);
- list_del_init(&dev->list);
- write_unlock_bh(&rfcomm_dev_lock);
+ /* Refcount should only hit zero when called from rfcomm_dev_del()
+ which will have taken us off the list. Everything else are
+ refcounting bugs. */
+ BUG_ON(!list_empty(&dev->list));
rfcomm_dlc_lock(dlc);
/* Detach DLC if it's owned by this dev */
tty_unregister_device(rfcomm_tty_driver, dev->id);
- /* Refcount should only hit zero when called from rfcomm_dev_del()
- which will have taken us off the list. Everything else are
- refcounting bugs. */
- BUG_ON(!list_empty(&dev->list));
-
kfree(dev);
/* It's safe to call module_put() here because socket still
{
BT_DBG("dev %p", dev);
- set_bit(RFCOMM_TTY_RELEASED, &dev->flags);
+ if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+ BUG_ON(1);
+ else
+ set_bit(RFCOMM_TTY_RELEASED, &dev->flags);
+
+ write_lock_bh(&rfcomm_dev_lock);
+ list_del_init(&dev->list);
+ write_unlock_bh(&rfcomm_dev_lock);
+
rfcomm_dev_put(dev);
}
if (dev->tty)
tty_vhangup(dev->tty);
- rfcomm_dev_del(dev);
+ if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
+ rfcomm_dev_del(dev);
rfcomm_dev_put(dev);
return 0;
}
if (dlc->state == BT_CLOSED) {
if (!dev->tty) {
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
- if (rfcomm_dev_get(dev->id) == NULL)
+ /* Drop DLC lock here to avoid deadlock
+ * 1. rfcomm_dev_get will take rfcomm_dev_lock
+ * but in rfcomm_dev_add there's lock order:
+ * rfcomm_dev_lock -> dlc lock
+ * 2. rfcomm_dev_put will deadlock if it's
+ * the last reference
+ */
+ rfcomm_dlc_unlock(dlc);
+ if (rfcomm_dev_get(dev->id) == NULL) {
+ rfcomm_dlc_lock(dlc);
return;
+ }
rfcomm_dev_del(dev);
- /* We have to drop DLC lock here, otherwise
- rfcomm_dev_put() will dead lock if it's
- the last reference. */
- rfcomm_dlc_unlock(dlc);
rfcomm_dev_put(dev);
rfcomm_dlc_lock(dlc);
}
BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened);
if (--dev->opened == 0) {
- device_move(dev->tty_dev, NULL);
+ if (dev->tty_dev->parent)
+ device_move(dev->tty_dev, NULL);
/* Close DLC and dettach TTY */
rfcomm_dlc_close(dev->dlc, 0);