--- /dev/null
+From 537d59af73d894750cff14f90fe2b6d77fbab15b Mon Sep 17 00:00:00 2001
+From: Dave Young <hidave.darkstar@gmail.com>
+Date: Sun, 1 Jun 2008 23:50:52 -0700
+Subject: [PATCH] bluetooth: rfcomm_dev_state_change deadlock fix
+
+From: Dave Young <hidave.darkstar@gmail.com>
+
+commit 537d59af73d894750cff14f90fe2b6d77fbab15b in mainline
+
+There's logic in __rfcomm_dlc_close:
+ rfcomm_dlc_lock(d);
+ d->state = BT_CLOSED;
+ d->state_changed(d, err);
+ rfcomm_dlc_unlock(d);
+
+In rfcomm_dev_state_change, it's possible that rfcomm_dev_put try to
+take the dlc lock, then we will deadlock.
+
+Here fixed it by unlock dlc before rfcomm_dev_get in
+rfcomm_dev_state_change.
+
+why not unlock just before rfcomm_dev_put? it's because there's
+another problem. rfcomm_dev_get/rfcomm_dev_del will take
+rfcomm_dev_lock, but in rfcomm_dev_add the lock order is :
+rfcomm_dev_lock --> dlc lock
+
+so I unlock dlc before the taken of rfcomm_dev_lock.
+
+Actually it's a regression caused by commit
+1905f6c736cb618e07eca0c96e60e3c024023428 ("bluetooth :
+__rfcomm_dlc_close lock fix"), the dlc state_change could be two
+callbacks : rfcomm_sk_state_change and rfcomm_dev_state_change. I
+missed the rfcomm_sk_state_change that time.
+
+Thanks Arjan van de Ven <arjan@linux.intel.com> for the effort in
+commit 4c8411f8c115def968820a4df6658ccfd55d7f1a ("bluetooth: fix
+locking bug in the rfcomm socket cleanup handling") but he missed the
+rfcomm_dev_state_change lock issue.
+
+Signed-off-by: Dave Young <hidave.darkstar@gmail.com>
+Acked-by: Marcel Holtmann <marcel@holtmann.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ net/bluetooth/rfcomm/tty.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+--- a/net/bluetooth/rfcomm/tty.c
++++ b/net/bluetooth/rfcomm/tty.c
+@@ -566,11 +566,22 @@ static void rfcomm_dev_state_change(stru
+ 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);
+ rfcomm_dev_put(dev);
++ rfcomm_dlc_lock(dlc);
+ }
+ } else
+ tty_hangup(dev->tty);