]> Pileus Git - ~andy/linux/blobdiff - net/bluetooth/rfcomm/tty.c
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[~andy/linux] / net / bluetooth / rfcomm / tty.c
index 4bf54b37725565031211110253a07920d52df433..aa5d73b786aca23793f7691bbab7f86ceb2e96d1 100644 (file)
 static struct tty_driver *rfcomm_tty_driver;
 
 struct rfcomm_dev {
+       struct tty_port         port;
        struct list_head        list;
-       atomic_t                refcnt;
 
        char                    name[12];
        int                     id;
        unsigned long           flags;
-       atomic_t                opened;
        int                     err;
 
        bdaddr_t                src;
@@ -64,9 +63,7 @@ struct rfcomm_dev {
        uint                    modem_status;
 
        struct rfcomm_dlc       *dlc;
-       struct tty_struct       *tty;
        wait_queue_head_t       wait;
-       struct work_struct      wakeup_task;
 
        struct device           *tty_dev;
 
@@ -82,11 +79,18 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
 static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
 static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig);
 
-static void rfcomm_tty_wakeup(struct work_struct *work);
-
 /* ---- Device functions ---- */
-static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
+
+/*
+ * The reason this isn't actually a race, as you no doubt have a little voice
+ * screaming at you in your head, is that the refcount should never actually
+ * reach zero unless the device has already been taken off the list, in
+ * rfcomm_dev_del(). And if that's not true, we'll hit the BUG() in
+ * rfcomm_dev_destruct() anyway.
+ */
+static void rfcomm_dev_destruct(struct tty_port *port)
 {
+       struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
        struct rfcomm_dlc *dlc = dev->dlc;
 
        BT_DBG("dev %p dlc %p", dev, dlc);
@@ -113,23 +117,9 @@ static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
        module_put(THIS_MODULE);
 }
 
-static inline void rfcomm_dev_hold(struct rfcomm_dev *dev)
-{
-       atomic_inc(&dev->refcnt);
-}
-
-static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
-{
-       /* The reason this isn't actually a race, as you no
-          doubt have a little voice screaming at you in your
-          head, is that the refcount should never actually
-          reach zero unless the device has already been taken
-          off the list, in rfcomm_dev_del(). And if that's not
-          true, we'll hit the BUG() in rfcomm_dev_destruct()
-          anyway. */
-       if (atomic_dec_and_test(&dev->refcnt))
-               rfcomm_dev_destruct(dev);
-}
+static const struct tty_port_operations rfcomm_port_ops = {
+       .destruct = rfcomm_dev_destruct,
+};
 
 static struct rfcomm_dev *__rfcomm_dev_get(int id)
 {
@@ -154,7 +144,7 @@ static inline struct rfcomm_dev *rfcomm_dev_get(int id)
                if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
                        dev = NULL;
                else
-                       rfcomm_dev_hold(dev);
+                       tty_port_get(&dev->port);
        }
 
        spin_unlock(&rfcomm_dev_lock);
@@ -241,7 +231,6 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
        sprintf(dev->name, "rfcomm%d", dev->id);
 
        list_add(&dev->list, head);
-       atomic_set(&dev->refcnt, 1);
 
        bacpy(&dev->src, &req->src);
        bacpy(&dev->dst, &req->dst);
@@ -250,10 +239,9 @@ 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);
-
+       tty_port_init(&dev->port);
+       dev->port.ops = &rfcomm_port_ops;
        init_waitqueue_head(&dev->wait);
-       INIT_WORK(&dev->wakeup_task, rfcomm_tty_wakeup);
 
        skb_queue_head_init(&dev->pending);
 
@@ -320,18 +308,23 @@ free:
 
 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));
 
-       if (atomic_read(&dev->opened) > 0)
+       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);
 
        spin_lock(&rfcomm_dev_lock);
        list_del_init(&dev->list);
        spin_unlock(&rfcomm_dev_lock);
 
-       rfcomm_dev_put(dev);
+       tty_port_put(&dev->port);
 }
 
 /* ---- Send buffer ---- */
@@ -345,15 +338,16 @@ static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
 static void rfcomm_wfree(struct sk_buff *skb)
 {
        struct rfcomm_dev *dev = (void *) skb->sk;
+       struct tty_struct *tty = dev->port.tty;
        atomic_sub(skb->truesize, &dev->wmem_alloc);
-       if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
-               queue_work(system_nrt_wq, &dev->wakeup_task);
-       rfcomm_dev_put(dev);
+       if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags) && tty)
+               tty_wakeup(tty);
+       tty_port_put(&dev->port);
 }
 
 static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev)
 {
-       rfcomm_dev_hold(dev);
+       tty_port_get(&dev->port);
        atomic_add(skb->truesize, &dev->wmem_alloc);
        skb->sk = (void *) dev;
        skb->destructor = rfcomm_wfree;
@@ -432,7 +426,7 @@ static int rfcomm_release_dev(void __user *arg)
                return -ENODEV;
 
        if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) {
-               rfcomm_dev_put(dev);
+               tty_port_put(&dev->port);
                return -EPERM;
        }
 
@@ -440,12 +434,12 @@ static int rfcomm_release_dev(void __user *arg)
                rfcomm_dlc_close(dev->dlc, 0);
 
        /* Shut down TTY synchronously before freeing rfcomm_dev */
-       if (dev->tty)
-               tty_vhangup(dev->tty);
+       if (dev->port.tty)
+               tty_vhangup(dev->port.tty);
 
        if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
                rfcomm_dev_del(dev);
-       rfcomm_dev_put(dev);
+       tty_port_put(&dev->port);
        return 0;
 }
 
@@ -523,7 +517,7 @@ static int rfcomm_get_dev_info(void __user *arg)
        if (copy_to_user(arg, &di, sizeof(di)))
                err = -EFAULT;
 
-       rfcomm_dev_put(dev);
+       tty_port_put(&dev->port);
        return err;
 }
 
@@ -559,7 +553,7 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
                return;
        }
 
-       tty = dev->tty;
+       tty = dev->port.tty;
        if (!tty || !skb_queue_empty(&dev->pending)) {
                skb_queue_tail(&dev->pending, skb);
                return;
@@ -585,13 +579,13 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
        wake_up_interruptible(&dev->wait);
 
        if (dlc->state == BT_CLOSED) {
-               if (!dev->tty) {
+               if (!dev->port.tty) {
                        if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
                                /* 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
+                                * 2. tty_port_put will deadlock if it's
                                 *    the last reference
                                 */
                                rfcomm_dlc_unlock(dlc);
@@ -601,11 +595,11 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
                                }
 
                                rfcomm_dev_del(dev);
-                               rfcomm_dev_put(dev);
+                               tty_port_put(&dev->port);
                                rfcomm_dlc_lock(dlc);
                        }
                } else
-                       tty_hangup(dev->tty);
+                       tty_hangup(dev->port.tty);
        }
 }
 
@@ -618,8 +612,8 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
        BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig);
 
        if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) {
-               if (dev->tty && !C_CLOCAL(dev->tty))
-                       tty_hangup(dev->tty);
+               if (dev->port.tty && !C_CLOCAL(dev->port.tty))
+                       tty_hangup(dev->port.tty);
        }
 
        dev->modem_status =
@@ -630,21 +624,9 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
 }
 
 /* ---- TTY functions ---- */
-static void rfcomm_tty_wakeup(struct work_struct *work)
-{
-       struct rfcomm_dev *dev = container_of(work, struct rfcomm_dev,
-                                                               wakeup_task);
-       struct tty_struct *tty = dev->tty;
-       if (!tty)
-               return;
-
-       BT_DBG("dev %p tty %p", dev, tty);
-       tty_wakeup(tty);
-}
-
 static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev)
 {
-       struct tty_struct *tty = dev->tty;
+       struct tty_struct *tty = dev->port.tty;
        struct sk_buff *skb;
        int inserted = 0;
 
@@ -671,6 +653,7 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
        DECLARE_WAITQUEUE(wait, current);
        struct rfcomm_dev *dev;
        struct rfcomm_dlc *dlc;
+       unsigned long flags;
        int err, id;
 
        id = tty->index;
@@ -686,10 +669,14 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
                return -ENODEV;
 
        BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst),
-                               dev->channel, atomic_read(&dev->opened));
+                               dev->channel, dev->port.count);
 
-       if (atomic_inc_return(&dev->opened) > 1)
+       spin_lock_irqsave(&dev->port.lock, flags);
+       if (++dev->port.count > 1) {
+               spin_unlock_irqrestore(&dev->port.lock, flags);
                return 0;
+       }
+       spin_unlock_irqrestore(&dev->port.lock, flags);
 
        dlc = dev->dlc;
 
@@ -697,7 +684,7 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
 
        rfcomm_dlc_lock(dlc);
        tty->driver_data = dev;
-       dev->tty = tty;
+       dev->port.tty = tty;
        rfcomm_dlc_unlock(dlc);
        set_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
 
@@ -723,9 +710,9 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
                        break;
                }
 
-               tty_unlock();
+               tty_unlock(tty);
                schedule();
-               tty_lock();
+               tty_lock(tty);
        }
        set_current_state(TASK_RUNNING);
        remove_wait_queue(&dev->wait, &wait);
@@ -744,13 +731,17 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
 static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
 {
        struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+       unsigned long flags;
+
        if (!dev)
                return;
 
        BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc,
-                                               atomic_read(&dev->opened));
+                                               dev->port.count);
 
-       if (atomic_dec_and_test(&dev->opened)) {
+       spin_lock_irqsave(&dev->port.lock, flags);
+       if (!--dev->port.count) {
+               spin_unlock_irqrestore(&dev->port.lock, flags);
                if (dev->tty_dev->parent)
                        device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);
 
@@ -758,11 +749,10 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
                rfcomm_dlc_close(dev->dlc, 0);
 
                clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
-               cancel_work_sync(&dev->wakeup_task);
 
                rfcomm_dlc_lock(dev->dlc);
                tty->driver_data = NULL;
-               dev->tty = NULL;
+               dev->port.tty = NULL;
                rfcomm_dlc_unlock(dev->dlc);
 
                if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) {
@@ -770,11 +760,12 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
                        list_del_init(&dev->list);
                        spin_unlock(&rfcomm_dev_lock);
 
-                       rfcomm_dev_put(dev);
+                       tty_port_put(&dev->port);
                }
-       }
+       } else
+               spin_unlock_irqrestore(&dev->port.lock, flags);
 
-       rfcomm_dev_put(dev);
+       tty_port_put(&dev->port);
 }
 
 static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
@@ -1083,7 +1074,7 @@ static void rfcomm_tty_hangup(struct tty_struct *tty)
                if (rfcomm_dev_get(dev->id) == NULL)
                        return;
                rfcomm_dev_del(dev);
-               rfcomm_dev_put(dev);
+               tty_port_put(&dev->port);
        }
 }