diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 1e4100bb0b65..111c6c858247 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -53,7 +53,7 @@ struct rfcomm_dev { char name[12]; int id; unsigned long flags; - int opened; + atomic_t opened; int err; bdaddr_t src; @@ -256,6 +256,8 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) dev->flags = req->flags & ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC)); + atomic_set(&dev->opened, 0); + init_waitqueue_head(&dev->wait); tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev); @@ -325,10 +327,10 @@ static void rfcomm_dev_del(struct rfcomm_dev *dev) { BT_DBG("dev %p", dev); - if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) - BUG_ON(1); - else - set_bit(RFCOMM_TTY_RELEASED, &dev->flags); + BUG_ON(test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags)); + + if (atomic_read(&dev->opened) > 0) + return; write_lock_bh(&rfcomm_dev_lock); list_del_init(&dev->list); @@ -684,9 +686,10 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) if (!dev) return -ENODEV; - BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened); + BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), + dev->channel, atomic_read(&dev->opened)); - if (dev->opened++ != 0) + if (atomic_inc_return(&dev->opened) > 1) return 0; dlc = dev->dlc; @@ -742,9 +745,10 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) if (!dev) return; - BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened); + BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, + atomic_read(&dev->opened)); - if (--dev->opened == 0) { + if (atomic_dec_and_test(&dev->opened)) { if (dev->tty_dev->parent) device_move(dev->tty_dev, NULL); @@ -758,6 +762,14 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) tty->driver_data = NULL; dev->tty = NULL; rfcomm_dlc_unlock(dev->dlc); + + if (test_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); + } } rfcomm_dev_put(dev);