From f60db8c4246ac7c33448fad173bed85354b7d75e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:54:50 +0200 Subject: TTY: rfcomm/tty, add tty_port And use tty from there. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/rfcomm/tty.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 4bf54b377255..97c2a087a9f1 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -48,6 +48,7 @@ static struct tty_driver *rfcomm_tty_driver; struct rfcomm_dev { + struct tty_port port; struct list_head list; atomic_t refcnt; @@ -64,7 +65,6 @@ struct rfcomm_dev { uint modem_status; struct rfcomm_dlc *dlc; - struct tty_struct *tty; wait_queue_head_t wait; struct work_struct wakeup_task; @@ -252,6 +252,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) atomic_set(&dev->opened, 0); + tty_port_init(&dev->port); init_waitqueue_head(&dev->wait); INIT_WORK(&dev->wakeup_task, rfcomm_tty_wakeup); @@ -440,8 +441,8 @@ 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); @@ -559,7 +560,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,7 +586,7 @@ 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 @@ -605,7 +606,7 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) rfcomm_dlc_lock(dlc); } } else - tty_hangup(dev->tty); + tty_hangup(dev->port.tty); } } @@ -618,8 +619,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 = @@ -634,7 +635,7 @@ 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; + struct tty_struct *tty = dev->port.tty; if (!tty) return; @@ -644,7 +645,7 @@ static void rfcomm_tty_wakeup(struct work_struct *work) 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; @@ -697,7 +698,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); @@ -762,7 +763,7 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) 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)) { -- cgit v1.2.3 From 67054019289ff11a7ffbc1a5d3c95aeb36e2f03e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:54:51 +0200 Subject: TTY: rfcomm/tty, use tty_port refcounting Switch the refcounting from manual atomic plays with refcounter to the one offered by tty_port. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/rfcomm/tty.c | 58 +++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 97c2a087a9f1..da4f54515775 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -50,7 +50,6 @@ 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; @@ -85,8 +84,17 @@ 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 +121,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 +148,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 +235,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); @@ -253,6 +246,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *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); @@ -332,7 +326,7 @@ static void rfcomm_dev_del(struct rfcomm_dev *dev) list_del_init(&dev->list); spin_unlock(&rfcomm_dev_lock); - rfcomm_dev_put(dev); + tty_port_put(&dev->port); } /* ---- Send buffer ---- */ @@ -349,12 +343,12 @@ static void rfcomm_wfree(struct sk_buff *skb) 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); + 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; @@ -433,7 +427,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; } @@ -446,7 +440,7 @@ static int rfcomm_release_dev(void __user *arg) if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) rfcomm_dev_del(dev); - rfcomm_dev_put(dev); + tty_port_put(&dev->port); return 0; } @@ -524,7 +518,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; } @@ -592,7 +586,7 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) * 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); @@ -602,7 +596,7 @@ 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 @@ -771,11 +765,11 @@ 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); } } - rfcomm_dev_put(dev); + tty_port_put(&dev->port); } static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) @@ -1084,7 +1078,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); } } -- cgit v1.2.3 From b2c4be398bf771a09f84eae6cf12cbd685384b8d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:54:52 +0200 Subject: TTY: rfcomm/tty, remove work for tty_wakeup tty_wakeup is safe to be called from all contexts. No need to schedule a work for that. Let us call it directly like in other drivers. This allows us to kill another member of rfcomm_dev structure. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/rfcomm/tty.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index da4f54515775..0433d2632334 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -65,7 +65,6 @@ struct rfcomm_dev { struct rfcomm_dlc *dlc; wait_queue_head_t wait; - struct work_struct wakeup_task; struct device *tty_dev; @@ -81,8 +80,6 @@ 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 ---- */ /* @@ -248,7 +245,6 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) 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); @@ -340,9 +336,10 @@ 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); + if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags) && tty) + tty_wakeup(tty); tty_port_put(&dev->port); } @@ -625,18 +622,6 @@ 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->port.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->port.tty; @@ -753,7 +738,6 @@ 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; -- cgit v1.2.3 From f997a01e3272f08cbbf77392b846878332dafc22 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:54:53 +0200 Subject: TTY: rfcomm/tty, use count from tty_port This means converting an atomic counter to a counter protected by lock. This is the first step needed to convert the rest of the code to the tty_port helpers. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/rfcomm/tty.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 0433d2632334..d1820ff14aee 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -54,7 +54,6 @@ struct rfcomm_dev { char name[12]; int id; unsigned long flags; - atomic_t opened; int err; bdaddr_t src; @@ -240,8 +239,6 @@ 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); @@ -311,12 +308,17 @@ 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); @@ -651,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; @@ -666,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; @@ -724,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); @@ -751,7 +762,8 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) tty_port_put(&dev->port); } - } + } else + spin_unlock_irqrestore(&dev->port.lock, flags); tty_port_put(&dev->port); } -- cgit v1.2.3 From d29f3ef39be4eec0362b985305fc526d9be318cf Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 3 May 2012 22:24:08 +0100 Subject: tty_lock: Localise the lock In each remaining case the tty_lock is associated with a specific tty. This means we can now lock on a per tty basis. We do need tty_lock_pair() for the pty case. Uglier but still a step in the right direction. [fixed up calls in 3 missing drivers - gregkh] Signed-off-by: Alan Cox Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/staging/serial/68360serial.c | 4 +-- drivers/tty/amiserial.c | 12 +++---- drivers/tty/cyclades.c | 2 +- drivers/tty/n_r3964.c | 11 +++--- drivers/tty/pty.c | 23 +++++++------ drivers/tty/serial/crisv10.c | 4 +-- drivers/tty/synclink.c | 4 +-- drivers/tty/synclink_gt.c | 4 +-- drivers/tty/synclinkmp.c | 4 +-- drivers/tty/tty_io.c | 67 +++++++++++++++++++++--------------- drivers/tty/tty_ldisc.c | 30 ++++++++-------- drivers/tty/tty_mutex.c | 60 ++++++++++++++++++++++++-------- drivers/tty/tty_port.c | 6 ++-- include/linux/tty.h | 23 ++++++++----- net/bluetooth/rfcomm/tty.c | 4 +-- 15 files changed, 155 insertions(+), 103 deletions(-) (limited to 'net') diff --git a/drivers/staging/serial/68360serial.c b/drivers/staging/serial/68360serial.c index daf0b1d0dc28..23ee50e25e44 100644 --- a/drivers/staging/serial/68360serial.c +++ b/drivers/staging/serial/68360serial.c @@ -1859,9 +1859,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready blocking: ttys%d, count = %d\n", info->line, state->count); #endif - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } current->state = TASK_RUNNING; remove_wait_queue(&info->open_wait, &wait); diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 6cc4358f68c1..b88a65c49240 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1033,7 +1033,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); - tty_lock(); + tty_lock(tty); tmp.line = tty->index; tmp.port = state->port; tmp.flags = state->tport.flags; @@ -1042,7 +1042,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, tmp.close_delay = state->tport.close_delay; tmp.closing_wait = state->tport.closing_wait; tmp.custom_divisor = state->custom_divisor; - tty_unlock(); + tty_unlock(tty); if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0; @@ -1059,12 +1059,12 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; - tty_lock(); + tty_lock(tty); change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) || new_serial.custom_divisor != state->custom_divisor; if (new_serial.irq || new_serial.port != state->port || new_serial.xmit_fifo_size != state->xmit_fifo_size) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1084,7 +1084,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, } if (new_serial.baud_base < 9600) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1116,7 +1116,7 @@ check_and_exit: } } else retval = startup(tty, state); - tty_unlock(); + tty_unlock(tty); return retval; } diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index e61cabdd69df..6984e1a2686a 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -1599,7 +1599,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->port.close_wait, + wait_event_interruptible_tty(tty, info->port.close_wait, !(info->port.flags & ASYNC_CLOSING)); return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 5c6c31459a2f..656ad93bbc96 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -1065,7 +1065,8 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, TRACE_L("read()"); - tty_lock(); + /* FIXME: should use a private lock */ + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1077,7 +1078,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, goto unlock; } /* block until there is a message: */ - wait_event_interruptible_tty(pInfo->read_wait, + wait_event_interruptible_tty(tty, pInfo->read_wait, (pMsg = remove_msg(pInfo, pClient))); } @@ -1107,7 +1108,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, } ret = -EPERM; unlock: - tty_unlock(); + tty_unlock(tty); return ret; } @@ -1156,7 +1157,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, pHeader->locks = 0; pHeader->owner = NULL; - tty_lock(); + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1175,7 +1176,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, add_tx_queue(pInfo, pHeader); trigger_transmit(pInfo); - tty_unlock(); + tty_unlock(tty); return 0; } diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 5505ffc91da4..d6fa8429f3ff 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -47,6 +47,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&tty->read_wait); wake_up_interruptible(&tty->write_wait); tty->packet = 0; + /* Review - krefs on tty_link ?? */ if (!tty->link) return; tty->link->packet = 0; @@ -62,9 +63,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&devpts_mutex); } #endif - tty_unlock(); tty_vhangup(tty->link); - tty_lock(); } } @@ -622,26 +621,29 @@ static int ptmx_open(struct inode *inode, struct file *filp) return retval; /* find a device that is not in use. */ - tty_lock(); + mutex_lock(&devpts_mutex); index = devpts_new_index(inode); - tty_unlock(); if (index < 0) { retval = index; goto err_file; } + mutex_unlock(&devpts_mutex); + mutex_lock(&tty_mutex); mutex_lock(&devpts_mutex); tty = tty_init_dev(ptm_driver, index); - mutex_unlock(&devpts_mutex); - tty_lock(); - mutex_unlock(&tty_mutex); if (IS_ERR(tty)) { retval = PTR_ERR(tty); goto out; } + /* The tty returned here is locked so we can safely + drop the mutex */ + mutex_unlock(&devpts_mutex); + mutex_unlock(&tty_mutex); + set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ tty_add_file(tty, filp); @@ -654,16 +656,17 @@ static int ptmx_open(struct inode *inode, struct file *filp) if (retval) goto err_release; - tty_unlock(); + tty_unlock(tty); return 0; err_release: - tty_unlock(); + tty_unlock(tty); tty_release(inode, filp); return retval; out: + mutex_unlock(&tty_mutex); devpts_kill_index(inode, index); - tty_unlock(); err_file: + mutex_unlock(&devpts_mutex); tty_free_file(filp); return retval; } diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 80b6b1b1f725..b431a5164f45 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -4052,9 +4052,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready blocking: ttyS%d, count = %d\n", info->line, info->count); #endif - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 593d40ad0a6b..5ed0daae6564 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -3338,9 +3338,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("%s(%d):block_til_ready blocking on %s count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index aa1debf97cc7..45b43f11ca39 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3336,9 +3336,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index a3dddc12d2fe..4a1e4f07765b 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -3357,9 +3357,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, printk("%s(%d):%s block_til_ready() count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index b425c79675ad..9e930c009bf2 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -185,6 +185,7 @@ void free_tty_struct(struct tty_struct *tty) put_device(tty->dev); kfree(tty->write_buf); tty_buffer_free_all(tty); + tty->magic = 0xDEADDEAD; kfree(tty); } @@ -573,7 +574,7 @@ void __tty_hangup(struct tty_struct *tty) } spin_unlock(&redirect_lock); - tty_lock(); + tty_lock(tty); /* some functions below drop BTM, so we need this bit */ set_bit(TTY_HUPPING, &tty->flags); @@ -666,7 +667,7 @@ void __tty_hangup(struct tty_struct *tty) clear_bit(TTY_HUPPING, &tty->flags); tty_ldisc_enable(tty); - tty_unlock(); + tty_unlock(tty); if (f) fput(f); @@ -1103,12 +1104,12 @@ void tty_write_message(struct tty_struct *tty, char *msg) { if (tty) { mutex_lock(&tty->atomic_write_lock); - tty_lock(); + tty_lock(tty); if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) { - tty_unlock(); + tty_unlock(tty); tty->ops->write(tty, msg, strlen(msg)); } else - tty_unlock(); + tty_unlock(tty); tty_write_unlock(tty); } return; @@ -1403,6 +1404,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) } initialize_tty_struct(tty, driver, idx); + tty_lock(tty); retval = tty_driver_install_tty(driver, tty); if (retval < 0) goto err_deinit_tty; @@ -1415,9 +1417,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) retval = tty_ldisc_setup(tty, tty->link); if (retval) goto err_release_tty; + /* Return the tty locked so that it cannot vanish under the caller */ return tty; err_deinit_tty: + tty_unlock(tty); deinitialize_tty_struct(tty); free_tty_struct(tty); err_module_put: @@ -1426,6 +1430,7 @@ err_module_put: /* call the tty release_tty routine to clean out this slot */ err_release_tty: + tty_unlock(tty); printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); @@ -1628,7 +1633,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty_paranoia_check(tty, inode, __func__)) return 0; - tty_lock(); + tty_lock(tty); check_tty_count(tty, __func__); __tty_fasync(-1, filp, 0); @@ -1637,10 +1642,11 @@ int tty_release(struct inode *inode, struct file *filp) pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; + /* Review: parallel close */ o_tty = tty->link; if (tty_release_checks(tty, o_tty, idx)) { - tty_unlock(); + tty_unlock(tty); return 0; } @@ -1652,7 +1658,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty->ops->close) tty->ops->close(tty, filp); - tty_unlock(); + tty_unlock(tty); /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the @@ -1675,7 +1681,7 @@ int tty_release(struct inode *inode, struct file *filp) opens on /dev/tty */ mutex_lock(&tty_mutex); - tty_lock(); + tty_lock_pair(tty, o_tty); tty_closing = tty->count <= 1; o_tty_closing = o_tty && (o_tty->count <= (pty_master ? 1 : 0)); @@ -1706,7 +1712,7 @@ int tty_release(struct inode *inode, struct file *filp) printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", __func__, tty_name(tty, buf)); - tty_unlock(); + tty_unlock_pair(tty, o_tty); mutex_unlock(&tty_mutex); schedule(); } @@ -1769,7 +1775,7 @@ int tty_release(struct inode *inode, struct file *filp) /* check whether both sides are closing ... */ if (!tty_closing || (o_tty && !o_tty_closing)) { - tty_unlock(); + tty_unlock_pair(tty, o_tty); return 0; } @@ -1782,14 +1788,16 @@ int tty_release(struct inode *inode, struct file *filp) tty_ldisc_release(tty, o_tty); /* * The release_tty function takes care of the details of clearing - * the slots and preserving the termios structure. + * the slots and preserving the termios structure. The tty_unlock_pair + * should be safe as we keep a kref while the tty is locked (so the + * unlock never unlocks a freed tty). */ release_tty(tty, idx); + tty_unlock_pair(tty, o_tty); /* Make this pty number available for reallocation */ if (devpts) devpts_kill_index(inode, idx); - tty_unlock(); return 0; } @@ -1893,6 +1901,9 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand + * + * Note: the tty_unlock/lock cases without a ref are only safe due to + * tty_mutex */ static int tty_open(struct inode *inode, struct file *filp) @@ -1916,8 +1927,7 @@ retry_open: retval = 0; mutex_lock(&tty_mutex); - tty_lock(); - + /* This is protected by the tty_mutex */ tty = tty_open_current_tty(device, filp); if (IS_ERR(tty)) { retval = PTR_ERR(tty); @@ -1938,17 +1948,19 @@ retry_open: } if (tty) { + tty_lock(tty); retval = tty_reopen(tty); - if (retval) + if (retval < 0) { + tty_unlock(tty); tty = ERR_PTR(retval); - } else + } + } else /* Returns with the tty_lock held for now */ tty = tty_init_dev(driver, index); mutex_unlock(&tty_mutex); if (driver) tty_driver_kref_put(driver); if (IS_ERR(tty)) { - tty_unlock(); retval = PTR_ERR(tty); goto err_file; } @@ -1977,7 +1989,7 @@ retry_open: printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, retval, tty->name); #endif - tty_unlock(); /* need to call tty_release without BTM */ + tty_unlock(tty); /* need to call tty_release without BTM */ tty_release(inode, filp); if (retval != -ERESTARTSYS) return retval; @@ -1989,17 +2001,15 @@ retry_open: /* * Need to reset f_op in case a hangup happened. */ - tty_lock(); if (filp->f_op == &hung_up_tty_fops) filp->f_op = &tty_fops; - tty_unlock(); goto retry_open; } - tty_unlock(); + tty_unlock(tty); mutex_lock(&tty_mutex); - tty_lock(); + tty_lock(tty); spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && @@ -2007,11 +2017,10 @@ retry_open: tty->session == NULL) __proc_set_tty(current, tty); spin_unlock_irq(¤t->sighand->siglock); - tty_unlock(); + tty_unlock(tty); mutex_unlock(&tty_mutex); return 0; err_unlock: - tty_unlock(); mutex_unlock(&tty_mutex); /* after locks to avoid deadlock */ if (!IS_ERR_OR_NULL(driver)) @@ -2094,10 +2103,13 @@ out: static int tty_fasync(int fd, struct file *filp, int on) { + struct tty_struct *tty = file_tty(filp); int retval; - tty_lock(); + + tty_lock(tty); retval = __tty_fasync(fd, filp, on); - tty_unlock(); + tty_unlock(tty); + return retval; } @@ -2934,6 +2946,7 @@ void initialize_tty_struct(struct tty_struct *tty, tty->pgrp = NULL; tty->overrun_time = jiffies; tty_buffer_init(tty); + mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); mutex_init(&tty->ldisc_mutex); init_waitqueue_head(&tty->write_wait); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 24b95db75d84..fa65cde395a4 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -567,7 +567,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); - tty_lock(); + tty_lock(tty); /* * We need to look at the tty locking here for pty/tty pairs * when both sides try to change in parallel. @@ -581,12 +581,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) */ if (tty->ldisc->ops->num == ldisc) { - tty_unlock(); + tty_unlock(tty); tty_ldisc_put(new_ldisc); return 0; } - tty_unlock(); + tty_unlock(tty); /* * Problem: What do we do if this blocks ? * We could deadlock here @@ -594,7 +594,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_wait_until_sent(tty, 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* @@ -604,10 +604,10 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); wait_event(tty_ldisc_wait, test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); } @@ -622,7 +622,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) o_ldisc = tty->ldisc; - tty_unlock(); + tty_unlock(tty); /* * Make sure we don't change while someone holds a * reference to the line discipline. The TTY_LDISC bit @@ -649,7 +649,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) retval = tty_ldisc_wait_idle(tty, 5 * HZ); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* handle wait idle failure locked */ @@ -664,7 +664,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) clear_bit(TTY_LDISC_CHANGING, &tty->flags); mutex_unlock(&tty->ldisc_mutex); tty_ldisc_put(new_ldisc); - tty_unlock(); + tty_unlock(tty); return -EIO; } @@ -707,7 +707,7 @@ enable: if (o_work) schedule_work(&o_tty->buf.work); mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); return retval; } @@ -815,11 +815,11 @@ void tty_ldisc_hangup(struct tty_struct *tty) * need to wait for another function taking the BTM */ clear_bit(TTY_LDISC, &tty->flags); - tty_unlock(); + tty_unlock(tty); cancel_work_sync(&tty->buf.work); mutex_unlock(&tty->ldisc_mutex); retry: - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* At this point we have a closed ldisc and we want to @@ -830,7 +830,7 @@ retry: if (atomic_read(&tty->ldisc->users) != 1) { char cur_n[TASK_COMM_LEN], tty_n[64]; long timeout = 3 * HZ; - tty_unlock(); + tty_unlock(tty); while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { timeout = MAX_SCHEDULE_TIMEOUT; @@ -911,10 +911,10 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) * race with the set_ldisc code path. */ - tty_unlock(); + tty_unlock(tty); tty_ldisc_halt(tty); tty_ldisc_flush_works(tty); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 9ff986c32a21..69adc80c98cd 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -4,29 +4,59 @@ #include #include -/* - * The 'big tty mutex' - * - * This mutex is taken and released by tty_lock() and tty_unlock(), - * replacing the older big kernel lock. - * It can no longer be taken recursively, and does not get - * released implicitly while sleeping. - * - * Don't use in new code. - */ -static DEFINE_MUTEX(big_tty_mutex); +/* Legacy tty mutex glue */ /* * Getting the big tty mutex. */ -void __lockfunc tty_lock(void) + +void __lockfunc tty_lock(struct tty_struct *tty) { - mutex_lock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "L Bad %p\n", tty); + WARN_ON(1); + return; + } + tty_kref_get(tty); + mutex_lock(&tty->legacy_mutex); } EXPORT_SYMBOL(tty_lock); -void __lockfunc tty_unlock(void) +void __lockfunc tty_unlock(struct tty_struct *tty) { - mutex_unlock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "U Bad %p\n", tty); + WARN_ON(1); + return; + } + mutex_unlock(&tty->legacy_mutex); + tty_kref_put(tty); } EXPORT_SYMBOL(tty_unlock); + +/* + * Getting the big tty mutex for a pair of ttys with lock ordering + * On a non pty/tty pair tty2 can be NULL which is just fine. + */ +void __lockfunc tty_lock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + if (tty < tty2) { + tty_lock(tty); + tty_lock(tty2); + } else { + if (tty2 && tty2 != tty) + tty_lock(tty2); + tty_lock(tty); + } +} +EXPORT_SYMBOL(tty_lock_pair); + +void __lockfunc tty_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + tty_unlock(tty); + if (tty2 && tty2 != tty) + tty_unlock(tty2); +} +EXPORT_SYMBOL(tty_unlock_pair); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index bf6e238146ae..d9cca95a5452 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -230,7 +230,7 @@ int tty_port_block_til_ready(struct tty_port *port, /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(port->close_wait, + wait_event_interruptible_tty(tty, port->close_wait, !(port->flags & ASYNC_CLOSING)); if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; @@ -296,9 +296,9 @@ int tty_port_block_til_ready(struct tty_port *port, retval = -ERESTARTSYS; break; } - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } finish_wait(&port->open_wait, &wait); diff --git a/include/linux/tty.h b/include/linux/tty.h index 9f47ab540f65..4990ef2b1fb7 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -268,6 +268,7 @@ struct tty_struct { struct mutex ldisc_mutex; struct tty_ldisc *ldisc; + struct mutex legacy_mutex; struct mutex termios_mutex; spinlock_t ctrl_lock; /* Termios values are protected by the termios mutex */ @@ -605,8 +606,12 @@ extern long vt_compat_ioctl(struct tty_struct *tty, /* tty_mutex.c */ /* functions for preparation of BKL removal */ -extern void __lockfunc tty_lock(void) __acquires(tty_lock); -extern void __lockfunc tty_unlock(void) __releases(tty_lock); +extern void __lockfunc tty_lock(struct tty_struct *tty); +extern void __lockfunc tty_unlock(struct tty_struct *tty); +extern void __lockfunc tty_lock_pair(struct tty_struct *tty, + struct tty_struct *tty2); +extern void __lockfunc tty_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2); /* * this shall be called only from where BTM is held (like close) @@ -621,9 +626,9 @@ extern void __lockfunc tty_unlock(void) __releases(tty_lock); static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, long timeout) { - tty_unlock(); /* tty->ops->close holds the BTM, drop it while waiting */ + tty_unlock(tty); /* tty->ops->close holds the BTM, drop it while waiting */ tty_wait_until_sent(tty, timeout); - tty_lock(); + tty_lock(tty); } /* @@ -638,16 +643,16 @@ static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, * * Do not use in new code. */ -#define wait_event_interruptible_tty(wq, condition) \ +#define wait_event_interruptible_tty(tty, wq, condition) \ ({ \ int __ret = 0; \ if (!(condition)) { \ - __wait_event_interruptible_tty(wq, condition, __ret); \ + __wait_event_interruptible_tty(tty, wq, condition, __ret); \ } \ __ret; \ }) -#define __wait_event_interruptible_tty(wq, condition, ret) \ +#define __wait_event_interruptible_tty(tty, wq, condition, ret) \ do { \ DEFINE_WAIT(__wait); \ \ @@ -656,9 +661,9 @@ do { \ if (condition) \ break; \ if (!signal_pending(current)) { \ - tty_unlock(); \ + tty_unlock(tty); \ schedule(); \ - tty_lock(); \ + tty_lock(tty); \ continue; \ } \ ret = -ERESTARTSYS; \ diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index d1820ff14aee..aa5d73b786ac 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -710,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); -- cgit v1.2.3