Commit ece3150d authored by Gianluca Anzolin's avatar Gianluca Anzolin Committed by Gustavo Padovan

Bluetooth: Fix the reference counting of tty_port

The tty_port can be released in two cases: when we get a HUP in the
functions rfcomm_tty_hangup() and rfcomm_dev_state_change(). Or when the
user releases the device in rfcomm_release_dev().

In these cases we set the flag RFCOMM_TTY_RELEASED so that no other
function can get a reference to the tty_port.
The use of !test_and_set_bit(RFCOMM_TTY_RELEASED) ensures that the
'initial' tty_port reference is only dropped once.

The rfcomm_dev_del function is removed becase it isn't used anymore.
Signed-off-by: default avatarGianluca Anzolin <gianluca@sottospazio.it>
Reviewed-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Signed-off-by: default avatarGustavo Padovan <gustavo.padovan@collabora.co.uk>
parent cad348a1
...@@ -324,23 +324,6 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) ...@@ -324,23 +324,6 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
return err; return err;
} }
static void rfcomm_dev_del(struct rfcomm_dev *dev)
{
unsigned long flags;
BT_DBG("dev %p", dev);
BUG_ON(test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags));
spin_lock_irqsave(&dev->port.lock, flags);
if (dev->port.count > 0) {
spin_unlock_irqrestore(&dev->port.lock, flags);
return;
}
spin_unlock_irqrestore(&dev->port.lock, flags);
tty_port_put(&dev->port);
}
/* ---- Send buffer ---- */ /* ---- Send buffer ---- */
static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc) static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
{ {
...@@ -454,8 +437,9 @@ static int rfcomm_release_dev(void __user *arg) ...@@ -454,8 +437,9 @@ static int rfcomm_release_dev(void __user *arg)
tty_kref_put(tty); tty_kref_put(tty);
} }
if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) if (!test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
rfcomm_dev_del(dev); tty_port_put(&dev->port);
tty_port_put(&dev->port); tty_port_put(&dev->port);
return 0; return 0;
} }
...@@ -607,6 +591,9 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) ...@@ -607,6 +591,9 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
* rfcomm_dev_lock -> dlc lock * rfcomm_dev_lock -> dlc lock
* 2. tty_port_put will deadlock if it's * 2. tty_port_put will deadlock if it's
* the last reference * the last reference
*
* FIXME: when we release the lock anything
* could happen to dev, even its destruction
*/ */
rfcomm_dlc_unlock(dlc); rfcomm_dlc_unlock(dlc);
if (rfcomm_dev_get(dev->id) == NULL) { if (rfcomm_dev_get(dev->id) == NULL) {
...@@ -614,7 +601,10 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) ...@@ -614,7 +601,10 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
return; return;
} }
rfcomm_dev_del(dev); if (!test_and_set_bit(RFCOMM_TTY_RELEASED,
&dev->flags))
tty_port_put(&dev->port);
tty_port_put(&dev->port); tty_port_put(&dev->port);
rfcomm_dlc_lock(dlc); rfcomm_dlc_lock(dlc);
} }
...@@ -741,16 +731,10 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) ...@@ -741,16 +731,10 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
{ {
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
if (!dev)
return;
BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc,
dev->port.count); dev->port.count);
tty_port_close(&dev->port, tty, filp); tty_port_close(&dev->port, tty, filp);
if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
tty_port_put(&dev->port);
} }
static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
...@@ -1050,17 +1034,11 @@ static void rfcomm_tty_hangup(struct tty_struct *tty) ...@@ -1050,17 +1034,11 @@ static void rfcomm_tty_hangup(struct tty_struct *tty)
BT_DBG("tty %p dev %p", tty, dev); BT_DBG("tty %p dev %p", tty, dev);
if (!dev)
return;
tty_port_hangup(&dev->port); tty_port_hangup(&dev->port);
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) &&
if (rfcomm_dev_get(dev->id) == NULL) !test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
return;
rfcomm_dev_del(dev);
tty_port_put(&dev->port); tty_port_put(&dev->port);
}
} }
static int rfcomm_tty_tiocmget(struct tty_struct *tty) static int rfcomm_tty_tiocmget(struct tty_struct *tty)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment