diff options
Diffstat (limited to 'net/bluetooth/rfcomm/tty.c')
-rw-r--r-- | net/bluetooth/rfcomm/tty.c | 13 |
1 files changed, 12 insertions, 1 deletions
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index c3f749abb2d0..c9191871c1e0 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c | |||
@@ -566,11 +566,22 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) | |||
566 | if (dlc->state == BT_CLOSED) { | 566 | if (dlc->state == BT_CLOSED) { |
567 | if (!dev->tty) { | 567 | if (!dev->tty) { |
568 | if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { | 568 | if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { |
569 | if (rfcomm_dev_get(dev->id) == NULL) | 569 | /* Drop DLC lock here to avoid deadlock |
570 | * 1. rfcomm_dev_get will take rfcomm_dev_lock | ||
571 | * but in rfcomm_dev_add there's lock order: | ||
572 | * rfcomm_dev_lock -> dlc lock | ||
573 | * 2. rfcomm_dev_put will deadlock if it's | ||
574 | * the last reference | ||
575 | */ | ||
576 | rfcomm_dlc_unlock(dlc); | ||
577 | if (rfcomm_dev_get(dev->id) == NULL) { | ||
578 | rfcomm_dlc_lock(dlc); | ||
570 | return; | 579 | return; |
580 | } | ||
571 | 581 | ||
572 | rfcomm_dev_del(dev); | 582 | rfcomm_dev_del(dev); |
573 | rfcomm_dev_put(dev); | 583 | rfcomm_dev_put(dev); |
584 | rfcomm_dlc_lock(dlc); | ||
574 | } | 585 | } |
575 | } else | 586 | } else |
576 | tty_hangup(dev->tty); | 587 | tty_hangup(dev->tty); |