Merge tag 'tty-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull TTY updates from Greg Kroah-Hartman: "Here's the big TTY/serial driver pull request for the 3.5-rc1 merge window. Nothing major in here, just lots of incremental changes from Alan and Jiri reworking some tty core things to behave better and to get a more solid grasp on some of the nasty tty locking issues. There are a few tty and serial driver updates in here as well. All of this has been in the linux-next releases for a while with no problems. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>" * tag 'tty-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (115 commits) serial: bfin_uart: Make MMR access compatible with 32 bits bf609 style controller. serial: bfin_uart: RTS and CTS MMRs can be either 16-bit width or 32-bit width. serial: bfin_uart: narrow the reboot condition in DMA tx interrupt serial: bfin_uart: Adapt bf5xx serial driver to bf60x serial4 controller. Revert "serial_core: Update buffer overrun statistics." tty: hvc_xen: NULL dereference on allocation failure tty: Fix LED error return tty: Allow uart_register/unregister/register tty: move global ldisc idle waitqueue to the individual ldisc serial8250-em: Add DT support serial8250-em: clk_get() IS_ERR() error handling fix serial_core: Update buffer overrun statistics. tty: drop the pty lock during hangup cris: fix missing tty arg in wait_event_interruptible_tty call tty/amiserial: Add missing argument for tty_unlock() tty_lock: Localise the lock pty: Lock the devpts bits privately tty_lock: undo the old tty_lock use on the ctty serial8250-em: Emma Mobile UART driver V2 Add missing call to uart_update_timeout() ...
This commit is contained in:
@@ -48,13 +48,12 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user