2009-12-11 23:28:17

by Greg KH

[permalink] [raw]
Subject: [GIT PATCH] TTY patches for 2.6.33-git

Here's the big TTY patchset for your .33-git tree.

Lots of tiny things, and one driver that had been broken for a long time
is removed.

Please pull from:
master.kernel.org:/pub/scm/linux/kernel/git/gregkh/tty-2.6.git/

Patches will be sent to the linux-kernel mailing list, if anyone wants
to see them.

thanks,

greg k-h

------------

Documentation/serial/hayes-esp.txt | 154 --
Documentation/serial/tty.txt | 9 +-
arch/xtensa/platforms/iss/console.c | 2 +-
drivers/char/Kconfig | 13 -
drivers/char/Makefile | 1 -
drivers/char/bfin_jtag_comm.c | 2 +-
drivers/char/epca.c | 2 +-
drivers/char/esp.c | 2533 ----------------------------------
drivers/char/isicom.c | 115 +--
drivers/char/istallion.c | 185 +--
drivers/char/moxa.c | 289 ++---
drivers/char/mxser.c | 248 ++--
drivers/char/pcmcia/ipwireless/tty.c | 2 +-
drivers/char/pty.c | 2 +-
drivers/char/riscom8.c | 89 +-
drivers/char/stallion.c | 129 +--
drivers/char/tty_io.c | 151 ++-
drivers/char/tty_ldisc.c | 23 +-
drivers/char/tty_port.c | 97 ++-
drivers/mmc/card/sdio_uart.c | 303 +++--
drivers/serial/8250.c | 24 +-
drivers/serial/jsm/jsm.h | 8 -
drivers/serial/jsm/jsm_driver.c | 48 +-
drivers/serial/jsm/jsm_neo.c | 8 -
drivers/serial/jsm/jsm_tty.c | 6 +-
drivers/serial/pxa.c | 13 +-
drivers/serial/serial_core.c | 33 +-
drivers/usb/serial/opticon.c | 7 +-
drivers/usb/serial/usb-serial.c | 83 +-
fs/devpts/inode.c | 16 +-
include/linux/Kbuild | 1 -
include/linux/hayesesp.h | 114 --
include/linux/isicom.h | 1 +
include/linux/tty.h | 25 +-
include/linux/usb/serial.h | 3 -
kernel/exit.c | 2 +-
36 files changed, 921 insertions(+), 3820 deletions(-)
delete mode 100644 Documentation/serial/hayes-esp.txt
delete mode 100644 drivers/char/esp.c
delete mode 100644 include/linux/hayesesp.h

---------------

Alan Cox (41):
tty: esp: remove broken driver
tty: istallion: Kill off the BKL ioctl
tty: stallion: kill BKL ioctl
tty_port: add "tty_port_open" helper
tty_port: coding style cleaning pass
usb_serial: Use the shutdown() operation
usb_serial: Kill port mutex
opticon: Fix resume logic
tty_port: Move hupcl handling
sdio_uart: use tty_port
sdio_uart: refcount the tty objects
sdio_uart: Move the open lock
tty: sdio_uart: Switch to the open/close helpers
tty: sdio_uart: Fix termios handling
tty: sdio_uart: Style fixes
tty: sdio_uart: add modem functionality
tty: sdio_uart: Fix the locking on "func" for new code
tty: tty_port: Change the buffer allocator locking
tty: riscom8: switch to the tty_port_open API
tty: tty_port: Add IO_ERROR bit handling
tty: tty_port: Move the IO_ERROR clear
tty: stallion: Convert to the tty_port_open/close methods
tty: istallion: tty port open/close methods
tty: tty_port: Add a kref object to the tty port
tty: isicom: switch to the new tty_port_open helper
tty: isicom: sort out the board init logic
tty: mxser: use the tty_port_open method
tty: mxser: Use the new locking rules to fix setserial properly
tty: isicom: fix deadlock on shutdown
tty: moxa: Use more tty_port ops
tty: moxa: rework the locking a bit
tty: moxa: Locking clean up
tty: moxa: Kill off the throttle method
tty: moxa: Fix modem op locking
tty: moxa: Kill the use of lock_kernel
tty: moxa: split open lock
tty: push the BKL down into the handlers a bit
tty: Push the lock down further into the ldisc code
tty: Push the bkl down a bit in the hangup code
tty: Move the leader test in disassociate
tty: split the lock up a bit further

Alexey Dobriyan (1):
tty: const: constify remaining tty_operations

Andr? Goddard Rosa (3):
serial: fix NULL pointer dereference
serial: cascade needless conditionals
serial, 8250: calculate irqflags bitmask before loop

Breno Leitao (1):
jsm: adding EEH handlers

Breno Leit?o (7):
jsm: IRQ handlers doesn't need to have IRQ_DISABLED enabled
jsm: Rewriting a bad log message
jsm: remove the ch_custom_speed field
jsm: removing ch_old_baud field
jsm: Remove ch_cpstime field
jsm: Removing unused jsm_channel->ch_wopen field
jsm: removing the field jsm_board->intr_count

Ian Jackson (1):
Serial: Do not read IIR in serial8250_start_tx when UART_BUG_TXEN

Nicolas Pitre (1):
sdio_uart: Fix oops caused by the previous changeset

Sukadev Bhattiprolu (1):
devpts_get_tty() should validate inode

Tilman Schmidt (1):
tty: docs: serial/tty, add to ldisc methods

Uwe Kleine-K?nig (1):
Serial: pxa: work around Errata #75


2009-12-11 23:28:47

by Greg KH

[permalink] [raw]
Subject: [PATCH 01/58] jsm: IRQ handlers doesn't need to have IRQ_DISABLED enabled

From: Breno Leitão <[email protected]>

Currently jsm is showing the following message when loaded:

IRQ 432/JSM: IRQF_DISABLED is not guaranteed on shared IRQs

It's because the request_irq() is called using IRQF_DISABLED
and IRQF_SHARED.
Actually there is no need to use IRQF_DISABLED in this driver.

Signed-off-by: Breno Leitão <[email protected]>
Cc: Scott Kilau <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/jsm/jsm_driver.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c
index b3604aa..48326f7 100644
--- a/drivers/serial/jsm/jsm_driver.c
+++ b/drivers/serial/jsm/jsm_driver.c
@@ -123,7 +123,7 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device
}

rc = request_irq(brd->irq, brd->bd_ops->intr,
- IRQF_DISABLED|IRQF_SHARED, "JSM", brd);
+ IRQF_SHARED, "JSM", brd);
if (rc) {
printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq);
goto out_iounmap;
--
1.6.5.5

2009-12-11 23:29:07

by Greg KH

[permalink] [raw]
Subject: [PATCH 02/58] jsm: Rewriting a bad log message

From: Breno Leitão <[email protected]>

Actually jsm displays "Device Added" 8 times (for a 8 port device).
This silly patch just makes things more informative, showing
the port (instead of the device) that was added.

Signed-off-by: Breno Leitão <[email protected]>
Cc: Scott Kilau <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/jsm/jsm_tty.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c
index 7439c03..6423dfb 100644
--- a/drivers/serial/jsm/jsm_tty.c
+++ b/drivers/serial/jsm/jsm_tty.c
@@ -472,7 +472,7 @@ int __devinit jsm_uart_port_init(struct jsm_board *brd)
if (uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port))
printk(KERN_INFO "jsm: add device failed\n");
else
- printk(KERN_INFO "Added device \n");
+ printk(KERN_INFO "jsm: Port %d added\n", i);
}

jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
--
1.6.5.5

2009-12-11 23:28:54

by Greg KH

[permalink] [raw]
Subject: [PATCH 03/58] jsm: remove the ch_custom_speed field

From: Breno Leitão <[email protected]>

Currently the ch_custom_speed field exists but is never used,
so, this patch removes it.

Signed-off-by: Breno Leitão <[email protected]>
Cc: Scott Kilau <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/jsm/jsm.h | 1 -
drivers/serial/jsm/jsm_neo.c | 4 ----
2 files changed, 0 insertions(+), 5 deletions(-)

diff --git a/drivers/serial/jsm/jsm.h b/drivers/serial/jsm/jsm.h
index 4e5f3bd..afcbee2 100644
--- a/drivers/serial/jsm/jsm.h
+++ b/drivers/serial/jsm/jsm.h
@@ -216,7 +216,6 @@ struct jsm_channel {
u8 ch_startc; /* Start character */

u32 ch_old_baud; /* Cache of the current baud */
- u32 ch_custom_speed;/* Custom baud, if set */

u32 ch_wopen; /* Waiting for open process cnt */

diff --git a/drivers/serial/jsm/jsm_neo.c b/drivers/serial/jsm/jsm_neo.c
index b4b124e..088e702 100644
--- a/drivers/serial/jsm/jsm_neo.c
+++ b/drivers/serial/jsm/jsm_neo.c
@@ -957,10 +957,6 @@ static void neo_param(struct jsm_channel *ch)
ch->ch_old_baud = 0;
return;

- } else if (ch->ch_custom_speed) {
- baud = ch->ch_custom_speed;
- if (ch->ch_flags & CH_BAUD0)
- ch->ch_flags &= ~(CH_BAUD0);
} else {
int i;
unsigned int cflag;
--
1.6.5.5

2009-12-11 23:37:35

by Greg KH

[permalink] [raw]
Subject: [PATCH 04/58] jsm: removing ch_old_baud field

From: Breno Leitão <[email protected]>

Currently the field jsm_channel->ch_old_baud is not used, just
assigned in a lot of places but never used. This patches removes
this field.

Signed-off-by: Breno Leitão <[email protected]>
Cc: Scott Kilau <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/jsm/jsm.h | 2 --
drivers/serial/jsm/jsm_neo.c | 2 --
drivers/serial/jsm/jsm_tty.c | 2 --
3 files changed, 0 insertions(+), 6 deletions(-)

diff --git a/drivers/serial/jsm/jsm.h b/drivers/serial/jsm/jsm.h
index afcbee2..d413e4a 100644
--- a/drivers/serial/jsm/jsm.h
+++ b/drivers/serial/jsm/jsm.h
@@ -215,8 +215,6 @@ struct jsm_channel {
u8 ch_stopc; /* Stop character */
u8 ch_startc; /* Start character */

- u32 ch_old_baud; /* Cache of the current baud */
-
u32 ch_wopen; /* Waiting for open process cnt */

u8 ch_mostat; /* FEP output modem status */
diff --git a/drivers/serial/jsm/jsm_neo.c b/drivers/serial/jsm/jsm_neo.c
index 088e702..bee3727 100644
--- a/drivers/serial/jsm/jsm_neo.c
+++ b/drivers/serial/jsm/jsm_neo.c
@@ -954,7 +954,6 @@ static void neo_param(struct jsm_channel *ch)
ch->ch_flags |= (CH_BAUD0);
ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
neo_assert_modem_signals(ch);
- ch->ch_old_baud = 0;
return;

} else {
@@ -1041,7 +1040,6 @@ static void neo_param(struct jsm_channel *ch)
quot = ch->ch_bd->bd_dividend / baud;

if (quot != 0) {
- ch->ch_old_baud = baud;
writeb(UART_LCR_DLAB, &ch->ch_neo_uart->lcr);
writeb((quot & 0xff), &ch->ch_neo_uart->txrx);
writeb((quot >> 8), &ch->ch_neo_uart->ier);
diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c
index 6423dfb..1bcad59 100644
--- a/drivers/serial/jsm/jsm_tty.c
+++ b/drivers/serial/jsm/jsm_tty.c
@@ -296,8 +296,6 @@ static void jsm_tty_close(struct uart_port *port)
bd->bd_ops->assert_modem_signals(channel);
}

- channel->ch_old_baud = 0;
-
/* Turn off UART interrupts for this port */
channel->ch_bd->bd_ops->uart_off(channel);

--
1.6.5.5

2009-12-11 23:28:59

by Greg KH

[permalink] [raw]
Subject: [PATCH 05/58] jsm: Remove ch_cpstime field

From: Breno Leitão <[email protected]>

Currently the field jsm_channel->ch_cpstime is defined but never
used, so this patch removes it.

Signed-off-by: Breno Leitão <[email protected]>
Cc: Scott Kilau <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/jsm/jsm.h | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/drivers/serial/jsm/jsm.h b/drivers/serial/jsm/jsm.h
index d413e4a..8c14351 100644
--- a/drivers/serial/jsm/jsm.h
+++ b/drivers/serial/jsm/jsm.h
@@ -206,8 +206,6 @@ struct jsm_channel {

u64 ch_close_delay; /* How long we should drop RTS/DTR for */

- u64 ch_cpstime; /* Time for CPS calculations */
-
tcflag_t ch_c_iflag; /* channel iflags */
tcflag_t ch_c_cflag; /* channel cflags */
tcflag_t ch_c_oflag; /* channel oflags */
--
1.6.5.5

2009-12-11 23:37:14

by Greg KH

[permalink] [raw]
Subject: [PATCH 06/58] jsm: Removing unused jsm_channel->ch_wopen field

From: Breno Leitão <[email protected]>

Currently the jsm_channel->ch_wopen field is defined and never
used. So, this patch removes it.

Signed-off-by: Breno Leitão <[email protected]>
Cc: Scott Kilau <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/jsm/jsm.h | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/drivers/serial/jsm/jsm.h b/drivers/serial/jsm/jsm.h
index 8c14351..484c927 100644
--- a/drivers/serial/jsm/jsm.h
+++ b/drivers/serial/jsm/jsm.h
@@ -213,8 +213,6 @@ struct jsm_channel {
u8 ch_stopc; /* Stop character */
u8 ch_startc; /* Start character */

- u32 ch_wopen; /* Waiting for open process cnt */
-
u8 ch_mostat; /* FEP output modem status */
u8 ch_mistat; /* FEP input modem status */

--
1.6.5.5

2009-12-11 23:42:13

by Greg KH

[permalink] [raw]
Subject: [PATCH 07/58] jsm: removing the field jsm_board->intr_count

From: Breno Leitão <[email protected]>

Currently there is a field in the jsm_board structure to cont
the number of interrupt that the card recevived, but it's not
working properly when the IRQ line is shared, and also nowhere
else this field is used. So, This patch is removing it.

Signed-off-by: Breno Leitão <[email protected]>
Cc: Scott Kilau <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/jsm/jsm.h | 1 -
drivers/serial/jsm/jsm_neo.c | 2 --
2 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/drivers/serial/jsm/jsm.h b/drivers/serial/jsm/jsm.h
index 484c927..38a509c 100644
--- a/drivers/serial/jsm/jsm.h
+++ b/drivers/serial/jsm/jsm.h
@@ -138,7 +138,6 @@ struct jsm_board
u32 nasync; /* Number of ports on card */

u32 irq; /* Interrupt request number */
- u64 intr_count; /* Count of interrupts */

u64 membase; /* Start of base memory of the card */
u64 membase_end; /* End of base memory of the card */
diff --git a/drivers/serial/jsm/jsm_neo.c b/drivers/serial/jsm/jsm_neo.c
index bee3727..7960d96 100644
--- a/drivers/serial/jsm/jsm_neo.c
+++ b/drivers/serial/jsm/jsm_neo.c
@@ -1117,8 +1117,6 @@ static irqreturn_t neo_intr(int irq, void *voidbrd)
unsigned long lock_flags2;
int outofloop_count = 0;

- brd->intr_count++;
-
/* Lock out the slow poller from running on this board. */
spin_lock_irqsave(&brd->bd_intr_lock, lock_flags);

--
1.6.5.5

2009-12-11 23:42:20

by Greg KH

[permalink] [raw]
Subject: [PATCH 08/58] jsm: adding EEH handlers

From: Breno Leitao <[email protected]>

Adding EEH handlers for the serial jsm driver. This patch adds
the PCI error handlers and also register them to be called when
a error is detected.

Signed-off-by: Breno Leitao <[email protected]>
Acked-by: Scott Kilau <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/jsm/jsm_driver.c | 46 +++++++++++++++++++++++++++++++++++++++
drivers/serial/jsm/jsm_tty.c | 2 +-
2 files changed, 47 insertions(+), 1 deletions(-)

diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c
index 48326f7..108c3e0 100644
--- a/drivers/serial/jsm/jsm_driver.c
+++ b/drivers/serial/jsm/jsm_driver.c
@@ -48,6 +48,17 @@ struct uart_driver jsm_uart_driver = {
.nr = NR_PORTS,
};

+static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t state);
+static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev);
+static void jsm_io_resume(struct pci_dev *pdev);
+
+static struct pci_error_handlers jsm_err_handler = {
+ .error_detected = jsm_io_error_detected,
+ .slot_reset = jsm_io_slot_reset,
+ .resume = jsm_io_resume,
+};
+
int jsm_debug;
module_param(jsm_debug, int, 0);
MODULE_PARM_DESC(jsm_debug, "Driver debugging level");
@@ -164,6 +175,7 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device
}

pci_set_drvdata(pdev, brd);
+ pci_save_state(pdev);

return 0;
out_free_irq:
@@ -222,8 +234,42 @@ static struct pci_driver jsm_driver = {
.id_table = jsm_pci_tbl,
.probe = jsm_probe_one,
.remove = __devexit_p(jsm_remove_one),
+ .err_handler = &jsm_err_handler,
};

+static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t state)
+{
+ struct jsm_board *brd = pci_get_drvdata(pdev);
+
+ jsm_remove_uart_port(brd);
+
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev)
+{
+ int rc;
+
+ rc = pci_enable_device(pdev);
+
+ if (rc)
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ pci_set_master(pdev);
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void jsm_io_resume(struct pci_dev *pdev)
+{
+ struct jsm_board *brd = pci_get_drvdata(pdev);
+
+ pci_restore_state(pdev);
+
+ jsm_uart_port_init(brd);
+}
+
static int __init jsm_init_module(void)
{
int rc;
diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c
index 1bcad59..cd95e21 100644
--- a/drivers/serial/jsm/jsm_tty.c
+++ b/drivers/serial/jsm/jsm_tty.c
@@ -430,7 +430,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd)
return 0;
}

-int __devinit jsm_uart_port_init(struct jsm_board *brd)
+int jsm_uart_port_init(struct jsm_board *brd)
{
int i;
unsigned int line;
--
1.6.5.5

2009-12-11 23:42:05

by Greg KH

[permalink] [raw]
Subject: [PATCH 09/58] tty: const: constify remaining tty_operations

From: Alexey Dobriyan <[email protected]>

Signed-off-by: Alexey Dobriyan <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
arch/xtensa/platforms/iss/console.c | 2 +-
drivers/char/bfin_jtag_comm.c | 2 +-
drivers/char/epca.c | 2 +-
drivers/char/pcmcia/ipwireless/tty.c | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/xtensa/platforms/iss/console.c b/arch/xtensa/platforms/iss/console.c
index 4c559cf..e60a1f5 100644
--- a/arch/xtensa/platforms/iss/console.c
+++ b/arch/xtensa/platforms/iss/console.c
@@ -196,7 +196,7 @@ static const struct file_operations rs_proc_fops = {
.release = single_release,
};

-static struct tty_operations serial_ops = {
+static const struct tty_operations serial_ops = {
.open = rs_open,
.close = rs_close,
.write = rs_write,
diff --git a/drivers/char/bfin_jtag_comm.c b/drivers/char/bfin_jtag_comm.c
index 1d7c34c..2628c74 100644
--- a/drivers/char/bfin_jtag_comm.c
+++ b/drivers/char/bfin_jtag_comm.c
@@ -226,7 +226,7 @@ bfin_jc_wait_until_sent(struct tty_struct *tty, int timeout)
}
}

-static struct tty_operations bfin_jc_ops = {
+static const struct tty_operations bfin_jc_ops = {
.open = bfin_jc_open,
.close = bfin_jc_close,
.write = bfin_jc_write,
diff --git a/drivers/char/epca.c b/drivers/char/epca.c
index dde5134..17b044a 100644
--- a/drivers/char/epca.c
+++ b/drivers/char/epca.c
@@ -935,7 +935,7 @@ static int info_open(struct tty_struct *tty, struct file *filp)
return 0;
}

-static struct tty_operations info_ops = {
+static const struct tty_operations info_ops = {
.open = info_open,
.ioctl = info_ioctl,
};
diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c
index 674b3ab..2bb7874 100644
--- a/drivers/char/pcmcia/ipwireless/tty.c
+++ b/drivers/char/pcmcia/ipwireless/tty.c
@@ -603,7 +603,7 @@ void ipwireless_tty_free(struct ipw_tty *tty)
}
}

-static struct tty_operations tty_ops = {
+static const struct tty_operations tty_ops = {
.open = ipw_open,
.close = ipw_close,
.hangup = ipw_hangup,
--
1.6.5.5

2009-12-11 23:38:16

by Greg KH

[permalink] [raw]
Subject: [PATCH 10/58] tty: esp: remove broken driver

From: Alan Cox <[email protected]>

The ESP driver has been marked broken for years. It's an old ISA device
that clearly nobody cares about any more. Remove it

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
Documentation/serial/hayes-esp.txt | 154 ---
drivers/char/Kconfig | 13 -
drivers/char/Makefile | 1 -
drivers/char/esp.c | 2533 ------------------------------------
include/linux/Kbuild | 1 -
include/linux/hayesesp.h | 114 --
6 files changed, 0 insertions(+), 2816 deletions(-)
delete mode 100644 Documentation/serial/hayes-esp.txt
delete mode 100644 drivers/char/esp.c
delete mode 100644 include/linux/hayesesp.h

diff --git a/Documentation/serial/hayes-esp.txt b/Documentation/serial/hayes-esp.txt
deleted file mode 100644
index 09b5d58..0000000
--- a/Documentation/serial/hayes-esp.txt
+++ /dev/null
@@ -1,154 +0,0 @@
-HAYES ESP DRIVER VERSION 2.1
-
-A big thanks to the people at Hayes, especially Alan Adamson. Their support
-has enabled me to provide enhancements to the driver.
-
-Please report your experiences with this driver to me ([email protected]). I
-am looking for both positive and negative feedback.
-
-*** IMPORTANT CHANGES FOR 2.1 ***
-Support for PIO mode. Five situations will cause PIO mode to be used:
-1) A multiport card is detected. PIO mode will always be used. (8 port cards
-do not support DMA).
-2) The DMA channel is set to an invalid value (anything other than 1 or 3).
-3) The DMA buffer/channel could not be allocated. The port will revert to PIO
-mode until it is reopened.
-4) Less than a specified number of bytes need to be transferred to/from the
-FIFOs. PIO mode will be used for that transfer only.
-5) A port needs to do a DMA transfer and another port is already using the
-DMA channel. PIO mode will be used for that transfer only.
-
-Since the Hayes ESP seems to conflict with other cards (notably sound cards)
-when using DMA, DMA is turned off by default. To use DMA, it must be turned
-on explicitly, either with the "dma=" option described below or with
-setserial. A multiport card can be forced into DMA mode by using setserial;
-however, most multiport cards don't support DMA.
-
-The latest version of setserial allows the enhanced configuration of the ESP
-card to be viewed and modified.
-***
-
-This package contains the files needed to compile a module to support the Hayes
-ESP card. The drivers are basically a modified version of the serial drivers.
-
-Features:
-
-- Uses the enhanced mode of the ESP card, allowing a wider range of
- interrupts and features than compatibility mode
-- Uses DMA and 16 bit PIO mode to transfer data to and from the ESP's FIFOs,
- reducing CPU load
-- Supports primary and secondary ports
-
-
-If the driver is compiled as a module, the IRQs to use can be specified by
-using the irq= option. The format is:
-
-irq=[0x100],[0x140],[0x180],[0x200],[0x240],[0x280],[0x300],[0x380]
-
-The address in brackets is the base address of the card. The IRQ of
-nonexistent cards can be set to 0. If an IRQ of a card that does exist is set
-to 0, the driver will attempt to guess at the correct IRQ. For example, to set
-the IRQ of the card at address 0x300 to 12, the insmod command would be:
-
-insmod esp irq=0,0,0,0,0,0,12,0
-
-The custom divisor can be set by using the divisor= option. The format is the
-same as for the irq= option. Each divisor value is a series of hex digits,
-with each digit representing the divisor to use for a corresponding port. The
-divisor value is constructed RIGHT TO LEFT. Specifying a nonzero divisor value
-will automatically set the spd_cust flag. To calculate the divisor to use for
-a certain baud rate, divide the port's base baud (generally 921600) by the
-desired rate. For example, to set the divisor of the primary port at 0x300 to
-4 and the divisor of the secondary port at 0x308 to 8, the insmod command would
-be:
-
-insmod esp divisor=0,0,0,0,0,0,0x84,0
-
-The dma= option can be used to set the DMA channel. The channel can be either
-1 or 3. Specifying any other value will force the driver to use PIO mode.
-For example, to set the DMA channel to 3, the insmod command would be:
-
-insmod esp dma=3
-
-The rx_trigger= and tx_trigger= options can be used to set the FIFO trigger
-levels. They specify when the ESP card should send an interrupt. Larger
-values will decrease the number of interrupts; however, a value too high may
-result in data loss. Valid values are 1 through 1023, with 768 being the
-default. For example, to set the receive trigger level to 512 bytes and the
-transmit trigger level to 700 bytes, the insmod command would be:
-
-insmod esp rx_trigger=512 tx_trigger=700
-
-The flow_off= and flow_on= options can be used to set the hardware flow off/
-flow on levels. The flow on level must be lower than the flow off level, and
-the flow off level should be higher than rx_trigger. Valid values are 1
-through 1023, with 1016 being the default flow off level and 944 being the
-default flow on level. For example, to set the flow off level to 1000 bytes
-and the flow on level to 935 bytes, the insmod command would be:
-
-insmod esp flow_off=1000 flow_on=935
-
-The rx_timeout= option can be used to set the receive timeout value. This
-value indicates how long after receiving the last character that the ESP card
-should wait before signalling an interrupt. Valid values are 0 though 255,
-with 128 being the default. A value too high will increase latency, and a
-value too low will cause unnecessary interrupts. For example, to set the
-receive timeout to 255, the insmod command would be:
-
-insmod esp rx_timeout=255
-
-The pio_threshold= option sets the threshold (in number of characters) for
-using PIO mode instead of DMA mode. For example, if this value is 32,
-transfers of 32 bytes or less will always use PIO mode.
-
-insmod esp pio_threshold=32
-
-Multiple options can be listed on the insmod command line by separating each
-option with a space. For example:
-
-insmod esp dma=3 trigger=512
-
-The esp module can be automatically loaded when needed. To cause this to
-happen, add the following lines to /etc/modprobe.conf (replacing the last line
-with options for your configuration):
-
-alias char-major-57 esp
-alias char-major-58 esp
-options esp irq=0,0,0,0,0,0,3,0 divisor=0,0,0,0,0,0,0x4,0
-
-You may also need to run 'depmod -a'.
-
-Devices must be created manually. To create the devices, note the output from
-the module after it is inserted. The output will appear in the location where
-kernel messages usually appear (usually /var/adm/messages). Create two devices
-for each 'tty' mentioned, one with major of 57 and the other with major of 58.
-The minor number should be the same as the tty number reported. The commands
-would be (replace ? with the tty number):
-
-mknod /dev/ttyP? c 57 ?
-mknod /dev/cup? c 58 ?
-
-For example, if the following line appears:
-
-Oct 24 18:17:23 techno kernel: ttyP8 at 0x0140 (irq = 3) is an ESP primary port
-
-...two devices should be created:
-
-mknod /dev/ttyP8 c 57 8
-mknod /dev/cup8 c 58 8
-
-You may need to set the permissions on the devices:
-
-chmod 666 /dev/ttyP*
-chmod 666 /dev/cup*
-
-The ESP module and the serial module should not conflict (they can be used at
-the same time). After the ESP module has been loaded the ports on the ESP card
-will no longer be accessible by the serial driver.
-
-If I/O errors are experienced when accessing the port, check for IRQ and DMA
-conflicts ('cat /proc/interrupts' and 'cat /proc/dma' for a list of IRQs and
-DMAs currently in use).
-
-Enjoy!
-Andrew J. Robinson <[email protected]>
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 6aad99e..6f31c94 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -201,19 +201,6 @@ config DIGIEPCA
To compile this driver as a module, choose M here: the
module will be called epca.

-config ESPSERIAL
- tristate "Hayes ESP serial port support"
- depends on SERIAL_NONSTANDARD && ISA && ISA_DMA_API && BROKEN
- help
- This is a driver which supports Hayes ESP serial ports. Both single
- port cards and multiport cards are supported. Make sure to read
- <file:Documentation/hayes-esp.txt>.
-
- To compile this driver as a module, choose M here: the
- module will be called esp.
-
- If unsure, say N.
-
config MOXA_INTELLIO
tristate "Moxa Intellio support"
depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 19a79dd..f957edf 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -18,7 +18,6 @@ obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o
obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o
obj-$(CONFIG_AUDIT) += tty_audit.o
obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o
-obj-$(CONFIG_ESPSERIAL) += esp.o
obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o
diff --git a/drivers/char/esp.c b/drivers/char/esp.c
deleted file mode 100644
index b19d43c..0000000
--- a/drivers/char/esp.c
+++ /dev/null
@@ -1,2533 +0,0 @@
-/*
- * esp.c - driver for Hayes ESP serial cards
- *
- * --- Notices from serial.c, upon which this driver is based ---
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now
- * much more extensible to support other serial cards based on the
- * 16450/16550A UART's. Added support for the AST FourPort and the
- * Accent Async board.
- *
- * set_serial_info fixed to set the flags, custom divisor, and uart
- * type fields. Fix suggested by Michael K. Johnson 12/12/92.
- *
- * 11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis <[email protected]>
- *
- * 03/96: Modularised by Angelo Haritsis <[email protected]>
- *
- * rs_set_termios fixed to look also for changes of the input
- * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
- * Bernd Anhäupl 05/17/96.
- *
- * --- End of notices from serial.c ---
- *
- * Support for the ESP serial card by Andrew J. Robinson
- * <[email protected]> (Card detection routine taken from a patch
- * by Dennis J. Boylan). Patches to allow use with 2.1.x contributed
- * by Chris Faylor.
- *
- * Most recent changes: (Andrew J. Robinson)
- * Support for PIO mode. This allows the driver to work properly with
- * multiport cards.
- *
- * Arnaldo Carvalho de Melo <[email protected]> -
- * several cleanups, use module_init/module_exit, etc
- *
- * This module exports the following rs232 io functions:
- *
- * int espserial_init(void);
- */
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/serialP.h>
-#include <linux/serial_reg.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/bitops.h>
-
-#include <asm/system.h>
-#include <linux/io.h>
-
-#include <asm/dma.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-
-#include <linux/hayesesp.h>
-
-#define NR_PORTS 64 /* maximum number of ports */
-#define NR_PRIMARY 8 /* maximum number of primary ports */
-#define REGION_SIZE 8 /* size of io region to request */
-
-/* The following variables can be set by giving module options */
-static int irq[NR_PRIMARY]; /* IRQ for each base port */
-static unsigned int divisor[NR_PRIMARY]; /* custom divisor for each port */
-static unsigned int dma = ESP_DMA_CHANNEL; /* DMA channel */
-static unsigned int rx_trigger = ESP_RX_TRIGGER;
-static unsigned int tx_trigger = ESP_TX_TRIGGER;
-static unsigned int flow_off = ESP_FLOW_OFF;
-static unsigned int flow_on = ESP_FLOW_ON;
-static unsigned int rx_timeout = ESP_RX_TMOUT;
-static unsigned int pio_threshold = ESP_PIO_THRESHOLD;
-
-MODULE_LICENSE("GPL");
-
-module_param_array(irq, int, NULL, 0);
-module_param_array(divisor, uint, NULL, 0);
-module_param(dma, uint, 0);
-module_param(rx_trigger, uint, 0);
-module_param(tx_trigger, uint, 0);
-module_param(flow_off, uint, 0);
-module_param(flow_on, uint, 0);
-module_param(rx_timeout, uint, 0);
-module_param(pio_threshold, uint, 0);
-
-/* END */
-
-static char *dma_buffer;
-static int dma_bytes;
-static struct esp_pio_buffer *free_pio_buf;
-
-#define DMA_BUFFER_SZ 1024
-
-#define WAKEUP_CHARS 1024
-
-static char serial_name[] __initdata = "ESP serial driver";
-static char serial_version[] __initdata = "2.2";
-
-static struct tty_driver *esp_driver;
-
-/*
- * Serial driver configuration section. Here are the various options:
- *
- * SERIAL_PARANOIA_CHECK
- * Check the magic number for the esp_structure where
- * ever possible.
- */
-
-#undef SERIAL_PARANOIA_CHECK
-#define SERIAL_DO_RESTART
-
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-
-#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
-#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
- tty->name, info->port.flags, \
- serial_driver.refcount, \
- info->port.count, tty->count, s)
-#else
-#define DBG_CNT(s)
-#endif
-
-static struct esp_struct *ports;
-
-static void change_speed(struct esp_struct *info);
-static void rs_wait_until_sent(struct tty_struct *, int);
-
-/*
- * The ESP card has a clock rate of 14.7456 MHz (that is, 2**ESPC_SCALE
- * times the normal 1.8432 Mhz clock of most serial boards).
- */
-#define BASE_BAUD ((1843200 / 16) * (1 << ESPC_SCALE))
-
-/* Standard COM flags (except for COM4, because of the 8514 problem) */
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
-
-static inline int serial_paranoia_check(struct esp_struct *info,
- char *name, const char *routine)
-{
-#ifdef SERIAL_PARANOIA_CHECK
- static const char badmagic[] = KERN_WARNING
- "Warning: bad magic number for serial struct (%s) in %s\n";
- static const char badinfo[] = KERN_WARNING
- "Warning: null esp_struct for (%s) in %s\n";
-
- if (!info) {
- printk(badinfo, name, routine);
- return 1;
- }
- if (info->magic != ESP_MAGIC) {
- printk(badmagic, name, routine);
- return 1;
- }
-#endif
- return 0;
-}
-
-static inline unsigned int serial_in(struct esp_struct *info, int offset)
-{
- return inb(info->io_port + offset);
-}
-
-static inline void serial_out(struct esp_struct *info, int offset,
- unsigned char value)
-{
- outb(value, info->io_port+offset);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------
- */
-static void rs_stop(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_stop"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
- if (info->IER & UART_IER_THRI) {
- info->IER &= ~UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static void rs_start(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_start"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
- if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
- info->IER |= UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines. All of the following
- * subroutines are declared as inline and are folded into
- * rs_interrupt(). They were separated out for readability's sake.
- *
- * Note: rs_interrupt() is a "fast" interrupt, which means that it
- * runs with interrupts turned off. People who may want to modify
- * rs_interrupt() should try to keep the interrupt handler as fast as
- * possible. After you are done making modifications, it is not a bad
- * idea to do:
- *
- * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
- *
- * and look at the resulting assemble code in serial.s.
- *
- * - Ted Ts'o ([email protected]), 7-Mar-93
- * -----------------------------------------------------------------------
- */
-
-static DEFINE_SPINLOCK(pio_lock);
-
-static inline struct esp_pio_buffer *get_pio_buffer(void)
-{
- struct esp_pio_buffer *buf;
- unsigned long flags;
-
- spin_lock_irqsave(&pio_lock, flags);
- if (free_pio_buf) {
- buf = free_pio_buf;
- free_pio_buf = buf->next;
- } else {
- buf = kmalloc(sizeof(struct esp_pio_buffer), GFP_ATOMIC);
- }
- spin_unlock_irqrestore(&pio_lock, flags);
- return buf;
-}
-
-static inline void release_pio_buffer(struct esp_pio_buffer *buf)
-{
- unsigned long flags;
- spin_lock_irqsave(&pio_lock, flags);
- buf->next = free_pio_buf;
- free_pio_buf = buf;
- spin_unlock_irqrestore(&pio_lock, flags);
-}
-
-static inline void receive_chars_pio(struct esp_struct *info, int num_bytes)
-{
- struct tty_struct *tty = info->port.tty;
- int i;
- struct esp_pio_buffer *pio_buf;
- struct esp_pio_buffer *err_buf;
- unsigned char status_mask;
-
- pio_buf = get_pio_buffer();
-
- if (!pio_buf)
- return;
-
- err_buf = get_pio_buffer();
-
- if (!err_buf) {
- release_pio_buffer(pio_buf);
- return;
- }
-
- status_mask = (info->read_status_mask >> 2) & 0x07;
-
- for (i = 0; i < num_bytes - 1; i += 2) {
- *((unsigned short *)(pio_buf->data + i)) =
- inw(info->io_port + UART_ESI_RX);
- err_buf->data[i] = serial_in(info, UART_ESI_RWS);
- err_buf->data[i + 1] = (err_buf->data[i] >> 3) & status_mask;
- err_buf->data[i] &= status_mask;
- }
-
- if (num_bytes & 0x0001) {
- pio_buf->data[num_bytes - 1] = serial_in(info, UART_ESI_RX);
- err_buf->data[num_bytes - 1] =
- (serial_in(info, UART_ESI_RWS) >> 3) & status_mask;
- }
-
- /* make sure everything is still ok since interrupts were enabled */
- tty = info->port.tty;
-
- if (!tty) {
- release_pio_buffer(pio_buf);
- release_pio_buffer(err_buf);
- info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;
- return;
- }
-
- status_mask = (info->ignore_status_mask >> 2) & 0x07;
-
- for (i = 0; i < num_bytes; i++) {
- if (!(err_buf->data[i] & status_mask)) {
- int flag = 0;
-
- if (err_buf->data[i] & 0x04) {
- flag = TTY_BREAK;
- if (info->port.flags & ASYNC_SAK)
- do_SAK(tty);
- } else if (err_buf->data[i] & 0x02)
- flag = TTY_FRAME;
- else if (err_buf->data[i] & 0x01)
- flag = TTY_PARITY;
- tty_insert_flip_char(tty, pio_buf->data[i], flag);
- }
- }
-
- tty_schedule_flip(tty);
-
- info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;
- release_pio_buffer(pio_buf);
- release_pio_buffer(err_buf);
-}
-
-static void program_isa_dma(int dma, int dir, unsigned long addr, int len)
-{
- unsigned long flags;
-
- flags = claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
- set_dma_mode(dma, dir);
- set_dma_addr(dma, addr);
- set_dma_count(dma, len);
- enable_dma(dma);
- release_dma_lock(flags);
-}
-
-static void receive_chars_dma(struct esp_struct *info, int num_bytes)
-{
- info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;
- dma_bytes = num_bytes;
- info->stat_flags |= ESP_STAT_DMA_RX;
-
- program_isa_dma(dma, DMA_MODE_READ, isa_virt_to_bus(dma_buffer),
- dma_bytes);
- serial_out(info, UART_ESI_CMD1, ESI_START_DMA_RX);
-}
-
-static inline void receive_chars_dma_done(struct esp_struct *info,
- int status)
-{
- struct tty_struct *tty = info->port.tty;
- int num_bytes;
- unsigned long flags;
-
- flags = claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
-
- info->stat_flags &= ~ESP_STAT_DMA_RX;
- num_bytes = dma_bytes - get_dma_residue(dma);
- release_dma_lock(flags);
-
- info->icount.rx += num_bytes;
-
- if (num_bytes > 0) {
- tty_insert_flip_string(tty, dma_buffer, num_bytes - 1);
-
- status &= (0x1c & info->read_status_mask);
-
- /* Is the status significant or do we throw the last byte ? */
- if (!(status & info->ignore_status_mask)) {
- int statflag = 0;
-
- if (status & 0x10) {
- statflag = TTY_BREAK;
- (info->icount.brk)++;
- if (info->port.flags & ASYNC_SAK)
- do_SAK(tty);
- } else if (status & 0x08) {
- statflag = TTY_FRAME;
- info->icount.frame++;
- } else if (status & 0x04) {
- statflag = TTY_PARITY;
- info->icount.parity++;
- }
- tty_insert_flip_char(tty, dma_buffer[num_bytes - 1],
- statflag);
- }
- tty_schedule_flip(tty);
- }
-
- if (dma_bytes != num_bytes) {
- num_bytes = dma_bytes - num_bytes;
- dma_bytes = 0;
- receive_chars_dma(info, num_bytes);
- } else
- dma_bytes = 0;
-}
-
-/* Caller must hold info->lock */
-
-static inline void transmit_chars_pio(struct esp_struct *info,
- int space_avail)
-{
- int i;
- struct esp_pio_buffer *pio_buf;
-
- pio_buf = get_pio_buffer();
-
- if (!pio_buf)
- return;
-
- while (space_avail && info->xmit_cnt) {
- if (info->xmit_tail + space_avail <= ESP_XMIT_SIZE) {
- memcpy(pio_buf->data,
- &(info->xmit_buf[info->xmit_tail]),
- space_avail);
- } else {
- i = ESP_XMIT_SIZE - info->xmit_tail;
- memcpy(pio_buf->data,
- &(info->xmit_buf[info->xmit_tail]), i);
- memcpy(&(pio_buf->data[i]), info->xmit_buf,
- space_avail - i);
- }
-
- info->xmit_cnt -= space_avail;
- info->xmit_tail = (info->xmit_tail + space_avail) &
- (ESP_XMIT_SIZE - 1);
-
- for (i = 0; i < space_avail - 1; i += 2) {
- outw(*((unsigned short *)(pio_buf->data + i)),
- info->io_port + UART_ESI_TX);
- }
-
- if (space_avail & 0x0001)
- serial_out(info, UART_ESI_TX,
- pio_buf->data[space_avail - 1]);
-
- if (info->xmit_cnt) {
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
- space_avail = serial_in(info, UART_ESI_STAT1) << 8;
- space_avail |= serial_in(info, UART_ESI_STAT2);
-
- if (space_avail > info->xmit_cnt)
- space_avail = info->xmit_cnt;
- }
- }
-
- if (info->xmit_cnt < WAKEUP_CHARS) {
- if (info->port.tty)
- tty_wakeup(info->port.tty);
-
-#ifdef SERIAL_DEBUG_INTR
- printk("THRE...");
-#endif
-
- if (info->xmit_cnt <= 0) {
- info->IER &= ~UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1,
- ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
- }
-
- release_pio_buffer(pio_buf);
-}
-
-/* Caller must hold info->lock */
-static inline void transmit_chars_dma(struct esp_struct *info, int num_bytes)
-{
- dma_bytes = num_bytes;
-
- if (info->xmit_tail + dma_bytes <= ESP_XMIT_SIZE) {
- memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]),
- dma_bytes);
- } else {
- int i = ESP_XMIT_SIZE - info->xmit_tail;
- memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]),
- i);
- memcpy(&(dma_buffer[i]), info->xmit_buf, dma_bytes - i);
- }
-
- info->xmit_cnt -= dma_bytes;
- info->xmit_tail = (info->xmit_tail + dma_bytes) & (ESP_XMIT_SIZE - 1);
-
- if (info->xmit_cnt < WAKEUP_CHARS) {
- if (info->port.tty)
- tty_wakeup(info->port.tty);
-
-#ifdef SERIAL_DEBUG_INTR
- printk("THRE...");
-#endif
-
- if (info->xmit_cnt <= 0) {
- info->IER &= ~UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
- }
-
- info->stat_flags |= ESP_STAT_DMA_TX;
-
- program_isa_dma(dma, DMA_MODE_WRITE, isa_virt_to_bus(dma_buffer),
- dma_bytes);
- serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX);
-}
-
-static inline void transmit_chars_dma_done(struct esp_struct *info)
-{
- int num_bytes;
- unsigned long flags;
-
- flags = claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
-
- num_bytes = dma_bytes - get_dma_residue(dma);
- info->icount.tx += dma_bytes;
- release_dma_lock(flags);
-
- if (dma_bytes != num_bytes) {
- dma_bytes -= num_bytes;
- memmove(dma_buffer, dma_buffer + num_bytes, dma_bytes);
-
- program_isa_dma(dma, DMA_MODE_WRITE,
- isa_virt_to_bus(dma_buffer), dma_bytes);
-
- serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX);
- } else {
- dma_bytes = 0;
- info->stat_flags &= ~ESP_STAT_DMA_TX;
- }
-}
-
-static void check_modem_status(struct esp_struct *info)
-{
- int status;
-
- serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
- status = serial_in(info, UART_ESI_STAT2);
-
- if (status & UART_MSR_ANY_DELTA) {
- /* update input line counters */
- if (status & UART_MSR_TERI)
- info->icount.rng++;
- if (status & UART_MSR_DDSR)
- info->icount.dsr++;
- if (status & UART_MSR_DDCD)
- info->icount.dcd++;
- if (status & UART_MSR_DCTS)
- info->icount.cts++;
- wake_up_interruptible(&info->port.delta_msr_wait);
- }
-
- if ((info->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
-#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
- printk("ttys%d CD now %s...", info->line,
- (status & UART_MSR_DCD) ? "on" : "off");
-#endif
- if (status & UART_MSR_DCD)
- wake_up_interruptible(&info->port.open_wait);
- else {
-#ifdef SERIAL_DEBUG_OPEN
- printk("scheduling hangup...");
-#endif
- tty_hangup(info->port.tty);
- }
- }
-}
-
-/*
- * This is the serial driver's interrupt routine
- */
-static irqreturn_t rs_interrupt_single(int irq, void *dev_id)
-{
- struct esp_struct *info;
- unsigned err_status;
- unsigned int scratch;
-
-#ifdef SERIAL_DEBUG_INTR
- printk("rs_interrupt_single(%d)...", irq);
-#endif
- info = (struct esp_struct *)dev_id;
- err_status = 0;
- scratch = serial_in(info, UART_ESI_SID);
-
- spin_lock(&info->lock);
-
- if (!info->port.tty) {
- spin_unlock(&info->lock);
- return IRQ_NONE;
- }
-
- if (scratch & 0x04) { /* error */
- serial_out(info, UART_ESI_CMD1, ESI_GET_ERR_STAT);
- err_status = serial_in(info, UART_ESI_STAT1);
- serial_in(info, UART_ESI_STAT2);
-
- if (err_status & 0x01)
- info->stat_flags |= ESP_STAT_RX_TIMEOUT;
-
- if (err_status & 0x20) /* UART status */
- check_modem_status(info);
-
- if (err_status & 0x80) /* Start break */
- wake_up_interruptible(&info->break_wait);
- }
-
- if ((scratch & 0x88) || /* DMA completed or timed out */
- (err_status & 0x1c) /* receive error */) {
- if (info->stat_flags & ESP_STAT_DMA_RX)
- receive_chars_dma_done(info, err_status);
- else if (info->stat_flags & ESP_STAT_DMA_TX)
- transmit_chars_dma_done(info);
- }
-
- if (!(info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) &&
- ((scratch & 0x01) || (info->stat_flags & ESP_STAT_RX_TIMEOUT)) &&
- (info->IER & UART_IER_RDI)) {
- int num_bytes;
-
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_RX_AVAIL);
- num_bytes = serial_in(info, UART_ESI_STAT1) << 8;
- num_bytes |= serial_in(info, UART_ESI_STAT2);
-
- num_bytes = tty_buffer_request_room(info->port.tty, num_bytes);
-
- if (num_bytes) {
- if (dma_bytes ||
- (info->stat_flags & ESP_STAT_USE_PIO) ||
- (num_bytes <= info->config.pio_threshold))
- receive_chars_pio(info, num_bytes);
- else
- receive_chars_dma(info, num_bytes);
- }
- }
-
- if (!(info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) &&
- (scratch & 0x02) && (info->IER & UART_IER_THRI)) {
- if ((info->xmit_cnt <= 0) || info->port.tty->stopped) {
- info->IER &= ~UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- } else {
- int num_bytes;
-
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
- num_bytes = serial_in(info, UART_ESI_STAT1) << 8;
- num_bytes |= serial_in(info, UART_ESI_STAT2);
-
- if (num_bytes > info->xmit_cnt)
- num_bytes = info->xmit_cnt;
-
- if (num_bytes) {
- if (dma_bytes ||
- (info->stat_flags & ESP_STAT_USE_PIO) ||
- (num_bytes <= info->config.pio_threshold))
- transmit_chars_pio(info, num_bytes);
- else
- transmit_chars_dma(info, num_bytes);
- }
- }
- }
-
- info->last_active = jiffies;
-
-#ifdef SERIAL_DEBUG_INTR
- printk("end.\n");
-#endif
- spin_unlock(&info->lock);
- return IRQ_HANDLED;
-}
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-/*
- * ---------------------------------------------------------------
- * Low level utility subroutines for the serial driver: routines to
- * figure out the appropriate timeout for an interrupt chain, routines
- * to initialize and startup a serial port, and routines to shutdown a
- * serial port. Useful stuff like that.
- *
- * Caller should hold lock
- * ---------------------------------------------------------------
- */
-
-static void esp_basic_init(struct esp_struct *info)
-{
- /* put ESPC in enhanced mode */
- serial_out(info, UART_ESI_CMD1, ESI_SET_MODE);
-
- if (info->stat_flags & ESP_STAT_NEVER_DMA)
- serial_out(info, UART_ESI_CMD2, 0x01);
- else
- serial_out(info, UART_ESI_CMD2, 0x31);
-
- /* disable interrupts for now */
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, 0x00);
-
- /* set interrupt and DMA channel */
- serial_out(info, UART_ESI_CMD1, ESI_SET_IRQ);
-
- if (info->stat_flags & ESP_STAT_NEVER_DMA)
- serial_out(info, UART_ESI_CMD2, 0x01);
- else
- serial_out(info, UART_ESI_CMD2, (dma << 4) | 0x01);
-
- serial_out(info, UART_ESI_CMD1, ESI_SET_ENH_IRQ);
-
- if (info->line % 8) /* secondary port */
- serial_out(info, UART_ESI_CMD2, 0x0d); /* shared */
- else if (info->irq == 9)
- serial_out(info, UART_ESI_CMD2, 0x02);
- else
- serial_out(info, UART_ESI_CMD2, info->irq);
-
- /* set error status mask (check this) */
- serial_out(info, UART_ESI_CMD1, ESI_SET_ERR_MASK);
-
- if (info->stat_flags & ESP_STAT_NEVER_DMA)
- serial_out(info, UART_ESI_CMD2, 0xa1);
- else
- serial_out(info, UART_ESI_CMD2, 0xbd);
-
- serial_out(info, UART_ESI_CMD2, 0x00);
-
- /* set DMA timeout */
- serial_out(info, UART_ESI_CMD1, ESI_SET_DMA_TMOUT);
- serial_out(info, UART_ESI_CMD2, 0xff);
-
- /* set FIFO trigger levels */
- serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER);
- serial_out(info, UART_ESI_CMD2, info->config.rx_trigger >> 8);
- serial_out(info, UART_ESI_CMD2, info->config.rx_trigger);
- serial_out(info, UART_ESI_CMD2, info->config.tx_trigger >> 8);
- serial_out(info, UART_ESI_CMD2, info->config.tx_trigger);
-
- /* Set clock scaling and wait states */
- serial_out(info, UART_ESI_CMD1, ESI_SET_PRESCALAR);
- serial_out(info, UART_ESI_CMD2, 0x04 | ESPC_SCALE);
-
- /* set reinterrupt pacing */
- serial_out(info, UART_ESI_CMD1, ESI_SET_REINTR);
- serial_out(info, UART_ESI_CMD2, 0xff);
-}
-
-static int startup(struct esp_struct *info)
-{
- unsigned long flags;
- int retval = 0;
- unsigned int num_chars;
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (info->port.flags & ASYNC_INITIALIZED)
- goto out;
-
- if (!info->xmit_buf) {
- info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_ATOMIC);
- retval = -ENOMEM;
- if (!info->xmit_buf)
- goto out;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "starting up ttys%d (irq %d)...",
- info->line, info->irq);
-#endif
-
- /* Flush the RX buffer. Using the ESI flush command may cause */
- /* wild interrupts, so read all the data instead. */
-
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_RX_AVAIL);
- num_chars = serial_in(info, UART_ESI_STAT1) << 8;
- num_chars |= serial_in(info, UART_ESI_STAT2);
-
- while (num_chars > 1) {
- inw(info->io_port + UART_ESI_RX);
- num_chars -= 2;
- }
-
- if (num_chars)
- serial_in(info, UART_ESI_RX);
-
- /* set receive character timeout */
- serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2, info->config.rx_timeout);
-
- /* clear all flags except the "never DMA" flag */
- info->stat_flags &= ESP_STAT_NEVER_DMA;
-
- if (info->stat_flags & ESP_STAT_NEVER_DMA)
- info->stat_flags |= ESP_STAT_USE_PIO;
-
- spin_unlock_irqrestore(&info->lock, flags);
-
- /*
- * Allocate the IRQ
- */
-
- retval = request_irq(info->irq, rs_interrupt_single, IRQF_SHARED,
- "esp serial", info);
-
- if (retval) {
- if (capable(CAP_SYS_ADMIN)) {
- if (info->port.tty)
- set_bit(TTY_IO_ERROR,
- &info->port.tty->flags);
- retval = 0;
- }
- goto out_unlocked;
- }
-
- if (!(info->stat_flags & ESP_STAT_USE_PIO) && !dma_buffer) {
- dma_buffer = (char *)__get_dma_pages(
- GFP_KERNEL, get_order(DMA_BUFFER_SZ));
-
- /* use PIO mode if DMA buf/chan cannot be allocated */
- if (!dma_buffer)
- info->stat_flags |= ESP_STAT_USE_PIO;
- else if (request_dma(dma, "esp serial")) {
- free_pages((unsigned long)dma_buffer,
- get_order(DMA_BUFFER_SZ));
- dma_buffer = NULL;
- info->stat_flags |= ESP_STAT_USE_PIO;
- }
-
- }
-
- info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, info->MCR);
-
- /*
- * Finally, enable interrupts
- */
- /* info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; */
- info->IER = UART_IER_RLSI | UART_IER_RDI | UART_IER_DMA_TMOUT |
- UART_IER_DMA_TC;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
-
- if (info->port.tty)
- clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- spin_unlock_irqrestore(&info->lock, flags);
-
- /*
- * Set up the tty->alt_speed kludge
- */
- if (info->port.tty) {
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- info->port.tty->alt_speed = 57600;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- info->port.tty->alt_speed = 115200;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- info->port.tty->alt_speed = 230400;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- info->port.tty->alt_speed = 460800;
- }
-
- /*
- * set the speed of the serial port
- */
- change_speed(info);
- info->port.flags |= ASYNC_INITIALIZED;
- return 0;
-
-out:
- spin_unlock_irqrestore(&info->lock, flags);
-out_unlocked:
- return retval;
-}
-
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void shutdown(struct esp_struct *info)
-{
- unsigned long flags, f;
-
- if (!(info->port.flags & ASYNC_INITIALIZED))
- return;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("Shutting down serial port %d (irq %d)....", info->line,
- info->irq);
-#endif
-
- spin_lock_irqsave(&info->lock, flags);
- /*
- * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
- * here so the queue might never be waken up
- */
- wake_up_interruptible(&info->port.delta_msr_wait);
- wake_up_interruptible(&info->break_wait);
-
- /* stop a DMA transfer on the port being closed */
- /* DMA lock is higher priority always */
- if (info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) {
- f = claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
- release_dma_lock(f);
-
- dma_bytes = 0;
- }
-
- /*
- * Free the IRQ
- */
- free_irq(info->irq, info);
-
- if (dma_buffer) {
- struct esp_struct *current_port = ports;
-
- while (current_port) {
- if ((current_port != info) &&
- (current_port->port.flags & ASYNC_INITIALIZED))
- break;
-
- current_port = current_port->next_port;
- }
-
- if (!current_port) {
- free_dma(dma);
- free_pages((unsigned long)dma_buffer,
- get_order(DMA_BUFFER_SZ));
- dma_buffer = NULL;
- }
- }
-
- if (info->xmit_buf) {
- free_page((unsigned long) info->xmit_buf);
- info->xmit_buf = NULL;
- }
-
- info->IER = 0;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, 0x00);
-
- if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL))
- info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
-
- info->MCR &= ~UART_MCR_OUT2;
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, info->MCR);
-
- if (info->port.tty)
- set_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
- info->port.flags &= ~ASYNC_INITIALIZED;
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void change_speed(struct esp_struct *info)
-{
- unsigned short port;
- int quot = 0;
- unsigned cflag, cval;
- int baud, bits;
- unsigned char flow1 = 0, flow2 = 0;
- unsigned long flags;
-
- if (!info->port.tty || !info->port.tty->termios)
- return;
- cflag = info->port.tty->termios->c_cflag;
- port = info->io_port;
-
- /* byte size and parity */
- switch (cflag & CSIZE) {
- case CS5: cval = 0x00; bits = 7; break;
- case CS6: cval = 0x01; bits = 8; break;
- case CS7: cval = 0x02; bits = 9; break;
- case CS8: cval = 0x03; bits = 10; break;
- default: cval = 0x00; bits = 7; break;
- }
- if (cflag & CSTOPB) {
- cval |= 0x04;
- bits++;
- }
- if (cflag & PARENB) {
- cval |= UART_LCR_PARITY;
- bits++;
- }
- if (!(cflag & PARODD))
- cval |= UART_LCR_EPAR;
-#ifdef CMSPAR
- if (cflag & CMSPAR)
- cval |= UART_LCR_SPAR;
-#endif
- baud = tty_get_baud_rate(info->port.tty);
- if (baud == 38400 &&
- ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
- quot = info->custom_divisor;
- else {
- if (baud == 134) /* Special case since 134 is really 134.5 */
- quot = (2*BASE_BAUD / 269);
- else if (baud)
- quot = BASE_BAUD / baud;
- }
- /* If the quotient is ever zero, default to 9600 bps */
- if (!quot)
- quot = BASE_BAUD / 9600;
-
- if (baud) {
- /* Actual rate */
- baud = BASE_BAUD/quot;
- tty_encode_baud_rate(info->port.tty, baud, baud);
- }
- info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50);
-
- /* CTS flow control flag and modem status interrupts */
- /* info->IER &= ~UART_IER_MSI; */
- if (cflag & CRTSCTS) {
- info->port.flags |= ASYNC_CTS_FLOW;
- /* info->IER |= UART_IER_MSI; */
- flow1 = 0x04;
- flow2 = 0x10;
- } else
- info->port.flags &= ~ASYNC_CTS_FLOW;
- if (cflag & CLOCAL)
- info->port.flags &= ~ASYNC_CHECK_CD;
- else
- info->port.flags |= ASYNC_CHECK_CD;
-
- /*
- * Set up parity check flag
- */
- info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
- if (I_INPCK(info->port.tty))
- info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty))
- info->read_status_mask |= UART_LSR_BI;
-
- info->ignore_status_mask = 0;
-#if 0
- /* This should be safe, but for some broken bits of hardware... */
- if (I_IGNPAR(info->port.tty)) {
- info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
- info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
- }
-#endif
- if (I_IGNBRK(info->port.tty)) {
- info->ignore_status_mask |= UART_LSR_BI;
- info->read_status_mask |= UART_LSR_BI;
- /*
- * If we're ignore parity and break indicators, ignore
- * overruns too. (For real raw support).
- */
- if (I_IGNPAR(info->port.tty)) {
- info->ignore_status_mask |= UART_LSR_OE | \
- UART_LSR_PE | UART_LSR_FE;
- info->read_status_mask |= UART_LSR_OE | \
- UART_LSR_PE | UART_LSR_FE;
- }
- }
-
- if (I_IXOFF(info->port.tty))
- flow1 |= 0x81;
-
- spin_lock_irqsave(&info->lock, flags);
- /* set baud */
- serial_out(info, UART_ESI_CMD1, ESI_SET_BAUD);
- serial_out(info, UART_ESI_CMD2, quot >> 8);
- serial_out(info, UART_ESI_CMD2, quot & 0xff);
-
- /* set data bits, parity, etc. */
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_LCR);
- serial_out(info, UART_ESI_CMD2, cval);
-
- /* Enable flow control */
- serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_CNTL);
- serial_out(info, UART_ESI_CMD2, flow1);
- serial_out(info, UART_ESI_CMD2, flow2);
-
- /* set flow control characters (XON/XOFF only) */
- if (I_IXOFF(info->port.tty)) {
- serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_CHARS);
- serial_out(info, UART_ESI_CMD2, START_CHAR(info->port.tty));
- serial_out(info, UART_ESI_CMD2, STOP_CHAR(info->port.tty));
- serial_out(info, UART_ESI_CMD2, 0x10);
- serial_out(info, UART_ESI_CMD2, 0x21);
- switch (cflag & CSIZE) {
- case CS5:
- serial_out(info, UART_ESI_CMD2, 0x1f);
- break;
- case CS6:
- serial_out(info, UART_ESI_CMD2, 0x3f);
- break;
- case CS7:
- case CS8:
- serial_out(info, UART_ESI_CMD2, 0x7f);
- break;
- default:
- serial_out(info, UART_ESI_CMD2, 0xff);
- break;
- }
- }
-
- /* Set high/low water */
- serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_LVL);
- serial_out(info, UART_ESI_CMD2, info->config.flow_off >> 8);
- serial_out(info, UART_ESI_CMD2, info->config.flow_off);
- serial_out(info, UART_ESI_CMD2, info->config.flow_on >> 8);
- serial_out(info, UART_ESI_CMD2, info->config.flow_on);
-
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static int rs_put_char(struct tty_struct *tty, unsigned char ch)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
- int ret = 0;
-
- if (serial_paranoia_check(info, tty->name, "rs_put_char"))
- return 0;
-
- if (!info->xmit_buf)
- return 0;
-
- spin_lock_irqsave(&info->lock, flags);
- if (info->xmit_cnt < ESP_XMIT_SIZE - 1) {
- info->xmit_buf[info->xmit_head++] = ch;
- info->xmit_head &= ESP_XMIT_SIZE-1;
- info->xmit_cnt++;
- ret = 1;
- }
- spin_unlock_irqrestore(&info->lock, flags);
- return ret;
-}
-
-static void rs_flush_chars(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_flush_chars"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf)
- goto out;
-
- if (!(info->IER & UART_IER_THRI)) {
- info->IER |= UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
-out:
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static int rs_write(struct tty_struct *tty,
- const unsigned char *buf, int count)
-{
- int c, t, ret = 0;
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_write"))
- return 0;
-
- if (!info->xmit_buf)
- return 0;
-
- while (1) {
- /* Thanks to R. Wolff for suggesting how to do this with */
- /* interrupts enabled */
-
- c = count;
- t = ESP_XMIT_SIZE - info->xmit_cnt - 1;
-
- if (t < c)
- c = t;
-
- t = ESP_XMIT_SIZE - info->xmit_head;
-
- if (t < c)
- c = t;
-
- if (c <= 0)
- break;
-
- memcpy(info->xmit_buf + info->xmit_head, buf, c);
-
- info->xmit_head = (info->xmit_head + c) & (ESP_XMIT_SIZE-1);
- info->xmit_cnt += c;
- buf += c;
- count -= c;
- ret += c;
- }
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (info->xmit_cnt && !tty->stopped && !(info->IER & UART_IER_THRI)) {
- info->IER |= UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
-
- spin_unlock_irqrestore(&info->lock, flags);
- return ret;
-}
-
-static int rs_write_room(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- int ret;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_write_room"))
- return 0;
-
- spin_lock_irqsave(&info->lock, flags);
-
- ret = ESP_XMIT_SIZE - info->xmit_cnt - 1;
- if (ret < 0)
- ret = 0;
- spin_unlock_irqrestore(&info->lock, flags);
- return ret;
-}
-
-static int rs_chars_in_buffer(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
-
- if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer"))
- return 0;
- return info->xmit_cnt;
-}
-
-static void rs_flush_buffer(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
- return;
- spin_lock_irqsave(&info->lock, flags);
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- spin_unlock_irqrestore(&info->lock, flags);
- tty_wakeup(tty);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_throttle()
- *
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void rs_throttle(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
-
- printk("throttle %s: %d....\n", tty_name(tty, buf),
- tty_chars_in_buffer(tty));
-#endif
-
- if (serial_paranoia_check(info, tty->name, "rs_throttle"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
- info->IER &= ~UART_IER_RDI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2, 0x00);
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static void rs_unthrottle(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
-
- printk(KERN_DEBUG "unthrottle %s: %d....\n", tty_name(tty, buf),
- tty_chars_in_buffer(tty));
-#endif
-
- if (serial_paranoia_check(info, tty->name, "rs_unthrottle"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
- info->IER |= UART_IER_RDI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2, info->config.rx_timeout);
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static int get_serial_info(struct esp_struct *info,
- struct serial_struct __user *retinfo)
-{
- struct serial_struct tmp;
-
- lock_kernel();
- memset(&tmp, 0, sizeof(tmp));
- tmp.type = PORT_16550A;
- tmp.line = info->line;
- tmp.port = info->io_port;
- tmp.irq = info->irq;
- tmp.flags = info->port.flags;
- tmp.xmit_fifo_size = 1024;
- tmp.baud_base = BASE_BAUD;
- tmp.close_delay = info->close_delay;
- tmp.closing_wait = info->closing_wait;
- tmp.custom_divisor = info->custom_divisor;
- tmp.hub6 = 0;
- unlock_kernel();
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
- return 0;
-}
-
-static int get_esp_config(struct esp_struct *info,
- struct hayes_esp_config __user *retinfo)
-{
- struct hayes_esp_config tmp;
-
- if (!retinfo)
- return -EFAULT;
-
- memset(&tmp, 0, sizeof(tmp));
- lock_kernel();
- tmp.rx_timeout = info->config.rx_timeout;
- tmp.rx_trigger = info->config.rx_trigger;
- tmp.tx_trigger = info->config.tx_trigger;
- tmp.flow_off = info->config.flow_off;
- tmp.flow_on = info->config.flow_on;
- tmp.pio_threshold = info->config.pio_threshold;
- tmp.dma_channel = (info->stat_flags & ESP_STAT_NEVER_DMA ? 0 : dma);
- unlock_kernel();
-
- return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
-}
-
-static int set_serial_info(struct esp_struct *info,
- struct serial_struct __user *new_info)
-{
- struct serial_struct new_serial;
- struct esp_struct old_info;
- unsigned int change_irq;
- int retval = 0;
- struct esp_struct *current_async;
-
- if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
- return -EFAULT;
- old_info = *info;
-
- if ((new_serial.type != PORT_16550A) ||
- (new_serial.hub6) ||
- (info->io_port != new_serial.port) ||
- (new_serial.baud_base != BASE_BAUD) ||
- (new_serial.irq > 15) ||
- (new_serial.irq < 2) ||
- (new_serial.irq == 6) ||
- (new_serial.irq == 8) ||
- (new_serial.irq == 13))
- return -EINVAL;
-
- change_irq = new_serial.irq != info->irq;
-
- if (change_irq && (info->line % 8))
- return -EINVAL;
-
- if (!capable(CAP_SYS_ADMIN)) {
- if (change_irq ||
- (new_serial.close_delay != info->close_delay) ||
- ((new_serial.flags & ~ASYNC_USR_MASK) !=
- (info->port.flags & ~ASYNC_USR_MASK)))
- return -EPERM;
- info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) |
- (new_serial.flags & ASYNC_USR_MASK));
- info->custom_divisor = new_serial.custom_divisor;
- } else {
- if (new_serial.irq == 2)
- new_serial.irq = 9;
-
- if (change_irq) {
- current_async = ports;
-
- while (current_async) {
- if ((current_async->line >= info->line) &&
- (current_async->line < (info->line + 8))) {
- if (current_async == info) {
- if (current_async->port.count > 1)
- return -EBUSY;
- } else if (current_async->port.count)
- return -EBUSY;
- }
-
- current_async = current_async->next_port;
- }
- }
-
- /*
- * OK, past this point, all the error checking has been done.
- * At this point, we start making changes.....
- */
-
- info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) |
- (new_serial.flags & ASYNC_FLAGS));
- info->custom_divisor = new_serial.custom_divisor;
- info->close_delay = new_serial.close_delay * HZ/100;
- info->closing_wait = new_serial.closing_wait * HZ/100;
-
- if (change_irq) {
- /*
- * We need to shutdown the serial port at the old
- * port/irq combination.
- */
- shutdown(info);
-
- current_async = ports;
-
- while (current_async) {
- if ((current_async->line >= info->line) &&
- (current_async->line < (info->line + 8)))
- current_async->irq = new_serial.irq;
-
- current_async = current_async->next_port;
- }
-
- serial_out(info, UART_ESI_CMD1, ESI_SET_ENH_IRQ);
- if (info->irq == 9)
- serial_out(info, UART_ESI_CMD2, 0x02);
- else
- serial_out(info, UART_ESI_CMD2, info->irq);
- }
- }
-
- if (info->port.flags & ASYNC_INITIALIZED) {
- if (((old_info.port.flags & ASYNC_SPD_MASK) !=
- (info->port.flags & ASYNC_SPD_MASK)) ||
- (old_info.custom_divisor != info->custom_divisor)) {
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- info->port.tty->alt_speed = 57600;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- info->port.tty->alt_speed = 115200;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- info->port.tty->alt_speed = 230400;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- info->port.tty->alt_speed = 460800;
- change_speed(info);
- }
- } else
- retval = startup(info);
-
- return retval;
-}
-
-static int set_esp_config(struct esp_struct *info,
- struct hayes_esp_config __user *new_info)
-{
- struct hayes_esp_config new_config;
- unsigned int change_dma;
- int retval = 0;
- struct esp_struct *current_async;
- unsigned long flags;
-
- /* Perhaps a non-sysadmin user should be able to do some of these */
- /* operations. I haven't decided yet. */
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- if (copy_from_user(&new_config, new_info, sizeof(new_config)))
- return -EFAULT;
-
- if ((new_config.flow_on >= new_config.flow_off) ||
- (new_config.rx_trigger < 1) ||
- (new_config.tx_trigger < 1) ||
- (new_config.flow_off < 1) ||
- (new_config.flow_on < 1) ||
- (new_config.rx_trigger > 1023) ||
- (new_config.tx_trigger > 1023) ||
- (new_config.flow_off > 1023) ||
- (new_config.flow_on > 1023) ||
- (new_config.pio_threshold < 0) ||
- (new_config.pio_threshold > 1024))
- return -EINVAL;
-
- if ((new_config.dma_channel != 1) && (new_config.dma_channel != 3))
- new_config.dma_channel = 0;
-
- if (info->stat_flags & ESP_STAT_NEVER_DMA)
- change_dma = new_config.dma_channel;
- else
- change_dma = (new_config.dma_channel != dma);
-
- if (change_dma) {
- if (new_config.dma_channel) {
- /* PIO mode to DMA mode transition OR */
- /* change current DMA channel */
- current_async = ports;
-
- while (current_async) {
- if (current_async == info) {
- if (current_async->port.count > 1)
- return -EBUSY;
- } else if (current_async->port.count)
- return -EBUSY;
-
- current_async = current_async->next_port;
- }
-
- shutdown(info);
- dma = new_config.dma_channel;
- info->stat_flags &= ~ESP_STAT_NEVER_DMA;
-
- /* all ports must use the same DMA channel */
-
- spin_lock_irqsave(&info->lock, flags);
- current_async = ports;
-
- while (current_async) {
- esp_basic_init(current_async);
- current_async = current_async->next_port;
- }
- spin_unlock_irqrestore(&info->lock, flags);
- } else {
- /* DMA mode to PIO mode only */
- if (info->port.count > 1)
- return -EBUSY;
-
- shutdown(info);
- spin_lock_irqsave(&info->lock, flags);
- info->stat_flags |= ESP_STAT_NEVER_DMA;
- esp_basic_init(info);
- spin_unlock_irqrestore(&info->lock, flags);
- }
- }
-
- info->config.pio_threshold = new_config.pio_threshold;
-
- if ((new_config.flow_off != info->config.flow_off) ||
- (new_config.flow_on != info->config.flow_on)) {
- info->config.flow_off = new_config.flow_off;
- info->config.flow_on = new_config.flow_on;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_LVL);
- serial_out(info, UART_ESI_CMD2, new_config.flow_off >> 8);
- serial_out(info, UART_ESI_CMD2, new_config.flow_off);
- serial_out(info, UART_ESI_CMD2, new_config.flow_on >> 8);
- serial_out(info, UART_ESI_CMD2, new_config.flow_on);
- spin_unlock_irqrestore(&info->lock, flags);
- }
-
- if ((new_config.rx_trigger != info->config.rx_trigger) ||
- (new_config.tx_trigger != info->config.tx_trigger)) {
- info->config.rx_trigger = new_config.rx_trigger;
- info->config.tx_trigger = new_config.tx_trigger;
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER);
- serial_out(info, UART_ESI_CMD2,
- new_config.rx_trigger >> 8);
- serial_out(info, UART_ESI_CMD2, new_config.rx_trigger);
- serial_out(info, UART_ESI_CMD2,
- new_config.tx_trigger >> 8);
- serial_out(info, UART_ESI_CMD2, new_config.tx_trigger);
- spin_unlock_irqrestore(&info->lock, flags);
- }
-
- if (new_config.rx_timeout != info->config.rx_timeout) {
- info->config.rx_timeout = new_config.rx_timeout;
- spin_lock_irqsave(&info->lock, flags);
-
- if (info->IER & UART_IER_RDI) {
- serial_out(info, UART_ESI_CMD1,
- ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2,
- new_config.rx_timeout);
- }
-
- spin_unlock_irqrestore(&info->lock, flags);
- }
-
- if (!(info->port.flags & ASYNC_INITIALIZED))
- retval = startup(info);
-
- return retval;
-}
-
-/*
- * get_lsr_info - get line status register info
- *
- * Purpose: Let user call ioctl() to get info when the UART physically
- * is emptied. On bus types like RS485, the transmitter must
- * release the bus after transmitting. This must be done when
- * the transmit shift register is empty, not be done when the
- * transmit holding register is empty. This functionality
- * allows an RS485 driver to be written in user space.
- */
-static int get_lsr_info(struct esp_struct *info, unsigned int __user *value)
-{
- unsigned char status;
- unsigned int result;
- unsigned long flags;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
- status = serial_in(info, UART_ESI_STAT1);
- spin_unlock_irqrestore(&info->lock, flags);
- result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
- return put_user(result, value);
-}
-
-
-static int esp_tiocmget(struct tty_struct *tty, struct file *file)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned char control, status;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, __func__))
- return -ENODEV;
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
-
- control = info->MCR;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
- status = serial_in(info, UART_ESI_STAT2);
- spin_unlock_irqrestore(&info->lock, flags);
-
- return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
- | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
- | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
- | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
- | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
- | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
-}
-
-static int esp_tiocmset(struct tty_struct *tty, struct file *file,
- unsigned int set, unsigned int clear)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, __func__))
- return -ENODEV;
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (set & TIOCM_RTS)
- info->MCR |= UART_MCR_RTS;
- if (set & TIOCM_DTR)
- info->MCR |= UART_MCR_DTR;
-
- if (clear & TIOCM_RTS)
- info->MCR &= ~UART_MCR_RTS;
- if (clear & TIOCM_DTR)
- info->MCR &= ~UART_MCR_DTR;
-
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, info->MCR);
-
- spin_unlock_irqrestore(&info->lock, flags);
- return 0;
-}
-
-/*
- * rs_break() --- routine which turns the break handling on or off
- */
-static int esp_break(struct tty_struct *tty, int break_state)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "esp_break"))
- return -EINVAL;
-
- if (break_state == -1) {
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
- serial_out(info, UART_ESI_CMD2, 0x01);
- spin_unlock_irqrestore(&info->lock, flags);
-
- /* FIXME - new style wait needed here */
- interruptible_sleep_on(&info->break_wait);
- } else {
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
- serial_out(info, UART_ESI_CMD2, 0x00);
- spin_unlock_irqrestore(&info->lock, flags);
- }
- return 0;
-}
-
-static int rs_ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct esp_struct *info = tty->driver_data;
- struct async_icount cprev, cnow; /* kernel counter temps */
- struct serial_icounter_struct __user *p_cuser; /* user space */
- void __user *argp = (void __user *)arg;
- unsigned long flags;
- int ret;
-
- if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
- return -ENODEV;
-
- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
- (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT) &&
- (cmd != TIOCGHAYESESP) && (cmd != TIOCSHAYESESP)) {
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
- }
-
- switch (cmd) {
- case TIOCGSERIAL:
- return get_serial_info(info, argp);
- case TIOCSSERIAL:
- lock_kernel();
- ret = set_serial_info(info, argp);
- unlock_kernel();
- return ret;
- case TIOCSERGWILD:
- return put_user(0L, (unsigned long __user *)argp);
- case TIOCSERGETLSR: /* Get line status register */
- return get_lsr_info(info, argp);
- case TIOCSERSWILD:
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- return 0;
- /*
- * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
- * - mask passed in arg for lines of interest
- * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
- * Caller should use TIOCGICOUNT to see which one it was
- */
- case TIOCMIWAIT:
- spin_lock_irqsave(&info->lock, flags);
- cprev = info->icount; /* note the counters on entry */
- spin_unlock_irqrestore(&info->lock, flags);
- while (1) {
- /* FIXME: convert to new style wakeup */
- interruptible_sleep_on(&info->port.delta_msr_wait);
- /* see if a signal did it */
- if (signal_pending(current))
- return -ERESTARTSYS;
- spin_lock_irqsave(&info->lock, flags);
- cnow = info->icount; /* atomic copy */
- spin_unlock_irqrestore(&info->lock, flags);
- if (cnow.rng == cprev.rng &&
- cnow.dsr == cprev.dsr &&
- cnow.dcd == cprev.dcd &&
- cnow.cts == cprev.cts)
- return -EIO; /* no change => error */
- if (((arg & TIOCM_RNG) &&
- (cnow.rng != cprev.rng)) ||
- ((arg & TIOCM_DSR) &&
- (cnow.dsr != cprev.dsr)) ||
- ((arg & TIOCM_CD) &&
- (cnow.dcd != cprev.dcd)) ||
- ((arg & TIOCM_CTS) &&
- (cnow.cts != cprev.cts))) {
- return 0;
- }
- cprev = cnow;
- }
- /* NOTREACHED */
- /*
- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
- * Return: write counters to the user passed counter struct
- * NB: both 1->0 and 0->1 transitions are counted except for
- * RI where only 0->1 is counted.
- */
- case TIOCGICOUNT:
- spin_lock_irqsave(&info->lock, flags);
- cnow = info->icount;
- spin_unlock_irqrestore(&info->lock, flags);
- p_cuser = argp;
- if (put_user(cnow.cts, &p_cuser->cts) ||
- put_user(cnow.dsr, &p_cuser->dsr) ||
- put_user(cnow.rng, &p_cuser->rng) ||
- put_user(cnow.dcd, &p_cuser->dcd))
- return -EFAULT;
- return 0;
- case TIOCGHAYESESP:
- return get_esp_config(info, argp);
- case TIOCSHAYESESP:
- lock_kernel();
- ret = set_esp_config(info, argp);
- unlock_kernel();
- return ret;
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- change_speed(info);
-
- spin_lock_irqsave(&info->lock, flags);
-
- /* Handle transition to B0 status */
- if ((old_termios->c_cflag & CBAUD) &&
- !(tty->termios->c_cflag & CBAUD)) {
- info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, info->MCR);
- }
-
- /* Handle transition away from B0 status */
- if (!(old_termios->c_cflag & CBAUD) &&
- (tty->termios->c_cflag & CBAUD)) {
- info->MCR |= (UART_MCR_DTR | UART_MCR_RTS);
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, info->MCR);
- }
-
- spin_unlock_irqrestore(&info->lock, flags);
-
- /* Handle turning of CRTSCTS */
- if ((old_termios->c_cflag & CRTSCTS) &&
- !(tty->termios->c_cflag & CRTSCTS)) {
- rs_start(tty);
- }
-}
-
-/*
- * ------------------------------------------------------------
- * rs_close()
- *
- * This routine is called when the serial port gets closed. First, we
- * wait for the last remaining data to be sent. Then, we unlink its
- * async structure from the interrupt chain if necessary, and we free
- * that IRQ if nothing is left in the chain.
- * ------------------------------------------------------------
- */
-static void rs_close(struct tty_struct *tty, struct file *filp)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (tty_hung_up_p(filp)) {
- DBG_CNT("before DEC-hung");
- goto out;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "rs_close ttys%d, count = %d\n",
- info->line, info->port.count);
-#endif
- if (tty->count == 1 && info->port.count != 1) {
- /*
- * Uh, oh. tty->count is 1, which means that the tty
- * structure will be freed. Info->count should always
- * be one in these conditions. If it's greater than
- * one, we've got real problems, since it means the
- * serial port won't be shutdown.
- */
- printk(KERN_DEBUG "rs_close: bad serial port count; tty->count is 1, info->port.count is %d\n", info->port.count);
- info->port.count = 1;
- }
- if (--info->port.count < 0) {
- printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n",
- info->line, info->port.count);
- info->port.count = 0;
- }
- if (info->port.count) {
- DBG_CNT("before DEC-2");
- goto out;
- }
- info->port.flags |= ASYNC_CLOSING;
-
- spin_unlock_irqrestore(&info->lock, flags);
- /*
- * Now we wait for the transmit buffer to clear; and we notify
- * the line discipline to only process XON/XOFF characters.
- */
- tty->closing = 1;
- if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, info->closing_wait);
- /*
- * At this point we stop accepting input. To do this, we
- * disable the receive line status interrupts, and tell the
- * interrupt driver to stop checking the data ready bit in the
- * line status register.
- */
- /* info->IER &= ~UART_IER_RLSI; */
- info->IER &= ~UART_IER_RDI;
- info->read_status_mask &= ~UART_LSR_DR;
- if (info->port.flags & ASYNC_INITIALIZED) {
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
-
- /* disable receive timeout */
- serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2, 0x00);
-
- spin_unlock_irqrestore(&info->lock, flags);
-
- /*
- * Before we drop DTR, make sure the UART transmitter
- * has completely drained; this is especially
- * important if there is a transmit FIFO!
- */
- rs_wait_until_sent(tty, info->timeout);
- }
- shutdown(info);
- rs_flush_buffer(tty);
- tty_ldisc_flush(tty);
- tty->closing = 0;
- info->port.tty = NULL;
-
- if (info->port.blocked_open) {
- if (info->close_delay)
- msleep_interruptible(jiffies_to_msecs(info->close_delay));
- wake_up_interruptible(&info->port.open_wait);
- }
- info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
- wake_up_interruptible(&info->port.close_wait);
- return;
-
-out:
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long orig_jiffies, char_time;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
- return;
-
- orig_jiffies = jiffies;
- char_time = ((info->timeout - HZ / 50) / 1024) / 5;
-
- if (!char_time)
- char_time = 1;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
-
- while ((serial_in(info, UART_ESI_STAT1) != 0x03) ||
- (serial_in(info, UART_ESI_STAT2) != 0xff)) {
-
- spin_unlock_irqrestore(&info->lock, flags);
- msleep_interruptible(jiffies_to_msecs(char_time));
-
- if (signal_pending(current))
- return;
-
- if (timeout && time_after(jiffies, orig_jiffies + timeout))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
- }
- spin_unlock_irqrestore(&info->lock, flags);
- set_current_state(TASK_RUNNING);
-}
-
-/*
- * esp_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-static void esp_hangup(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
-
- if (serial_paranoia_check(info, tty->name, "esp_hangup"))
- return;
-
- rs_flush_buffer(tty);
- shutdown(info);
- info->port.count = 0;
- info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
- info->port.tty = NULL;
- wake_up_interruptible(&info->port.open_wait);
-}
-
-static int esp_carrier_raised(struct tty_port *port)
-{
- struct esp_struct *info = container_of(port, struct esp_struct, port);
- serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
- if (serial_in(info, UART_ESI_STAT2) & UART_MSR_DCD)
- return 1;
- return 0;
-}
-
-/*
- * ------------------------------------------------------------
- * esp_open() and friends
- * ------------------------------------------------------------
- */
-static int block_til_ready(struct tty_struct *tty, struct file *filp,
- struct esp_struct *info)
-{
- DECLARE_WAITQUEUE(wait, current);
- int retval;
- int do_clocal = 0;
- unsigned long flags;
- int cd;
- struct tty_port *port = &info->port;
-
- /*
- * If the device is in the middle of being closed, then block
- * until it's done, and then try again.
- */
- if (tty_hung_up_p(filp) ||
- (port->flags & ASYNC_CLOSING)) {
- if (port->flags & ASYNC_CLOSING)
- interruptible_sleep_on(&port->close_wait);
-#ifdef SERIAL_DO_RESTART
- if (port->flags & ASYNC_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
-#else
- return -EAGAIN;
-#endif
- }
-
- /*
- * If non-blocking mode is set, or the port is not enabled,
- * then make the check up front and then exit.
- */
- if ((filp->f_flags & O_NONBLOCK) ||
- (tty->flags & (1 << TTY_IO_ERROR))) {
- port->flags |= ASYNC_NORMAL_ACTIVE;
- return 0;
- }
-
- if (tty->termios->c_cflag & CLOCAL)
- do_clocal = 1;
-
- /*
- * Block waiting for the carrier detect and the line to become
- * free (i.e., not in use by the callout). While we are in
- * this loop, port->count is dropped by one, so that
- * rs_close() knows when to free things. We restore it upon
- * exit, either normal or abnormal.
- */
- retval = 0;
- add_wait_queue(&port->open_wait, &wait);
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "block_til_ready before block: ttys%d, count = %d\n",
- info->line, port->count);
-#endif
- spin_lock_irqsave(&info->lock, flags);
- if (!tty_hung_up_p(filp))
- port->count--;
- port->blocked_open++;
- while (1) {
- if ((tty->termios->c_cflag & CBAUD)) {
- unsigned int scratch;
-
- serial_out(info, UART_ESI_CMD1, ESI_READ_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- scratch = serial_in(info, UART_ESI_STAT1);
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2,
- scratch | UART_MCR_DTR | UART_MCR_RTS);
- }
- set_current_state(TASK_INTERRUPTIBLE);
- if (tty_hung_up_p(filp) ||
- !(port->flags & ASYNC_INITIALIZED)) {
-#ifdef SERIAL_DO_RESTART
- if (port->flags & ASYNC_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
-#else
- retval = -EAGAIN;
-#endif
- break;
- }
-
- cd = tty_port_carrier_raised(port);
-
- if (!(port->flags & ASYNC_CLOSING) &&
- (do_clocal))
- break;
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "block_til_ready blocking: ttys%d, count = %d\n",
- info->line, port->count);
-#endif
- spin_unlock_irqrestore(&info->lock, flags);
- schedule();
- spin_lock_irqsave(&info->lock, flags);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&port->open_wait, &wait);
- if (!tty_hung_up_p(filp))
- port->count++;
- port->blocked_open--;
- spin_unlock_irqrestore(&info->lock, flags);
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "block_til_ready after blocking: ttys%d, count = %d\n",
- info->line, port->count);
-#endif
- if (retval)
- return retval;
- port->flags |= ASYNC_NORMAL_ACTIVE;
- return 0;
-}
-
-/*
- * This routine is called whenever a serial port is opened. It
- * enables interrupts for a serial port, linking in its async structure into
- * the IRQ chain. It also performs the serial-specific
- * initialization for the tty structure.
- */
-static int esp_open(struct tty_struct *tty, struct file *filp)
-{
- struct esp_struct *info;
- int retval, line;
- unsigned long flags;
-
- line = tty->index;
- if ((line < 0) || (line >= NR_PORTS))
- return -ENODEV;
-
- /* find the port in the chain */
-
- info = ports;
-
- while (info && (info->line != line))
- info = info->next_port;
-
- if (!info) {
- serial_paranoia_check(info, tty->name, "esp_open");
- return -ENODEV;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "esp_open %s, count = %d\n", tty->name, info->port.count);
-#endif
- spin_lock_irqsave(&info->lock, flags);
- info->port.count++;
- tty->driver_data = info;
- info->port.tty = tty;
-
- spin_unlock_irqrestore(&info->lock, flags);
-
- /*
- * Start up serial port
- */
- retval = startup(info);
- if (retval)
- return retval;
-
- retval = block_til_ready(tty, filp, info);
- if (retval) {
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "esp_open returning after block_til_ready with %d\n",
- retval);
-#endif
- return retval;
- }
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "esp_open %s successful...", tty->name);
-#endif
- return 0;
-}
-
-/*
- * ---------------------------------------------------------------------
- * espserial_init() and friends
- *
- * espserial_init() is called at boot-time to initialize the serial driver.
- * ---------------------------------------------------------------------
- */
-
-/*
- * This routine prints out the appropriate serial driver version
- * number, and identifies which options were configured into this
- * driver.
- */
-
-static void __init show_serial_version(void)
-{
- printk(KERN_INFO "%s version %s (DMA %u)\n",
- serial_name, serial_version, dma);
-}
-
-/*
- * This routine is called by espserial_init() to initialize a specific serial
- * port.
- */
-static int autoconfig(struct esp_struct *info)
-{
- int port_detected = 0;
- unsigned long flags;
-
- if (!request_region(info->io_port, REGION_SIZE, "esp serial"))
- return -EIO;
-
- spin_lock_irqsave(&info->lock, flags);
- /*
- * Check for ESP card
- */
-
- if (serial_in(info, UART_ESI_BASE) == 0xf3) {
- serial_out(info, UART_ESI_CMD1, 0x00);
- serial_out(info, UART_ESI_CMD1, 0x01);
-
- if ((serial_in(info, UART_ESI_STAT2) & 0x70) == 0x20) {
- port_detected = 1;
-
- if (!(info->irq)) {
- serial_out(info, UART_ESI_CMD1, 0x02);
-
- if (serial_in(info, UART_ESI_STAT1) & 0x01)
- info->irq = 3;
- else
- info->irq = 4;
- }
-
-
- /* put card in enhanced mode */
- /* this prevents access through */
- /* the "old" IO ports */
- esp_basic_init(info);
-
- /* clear out MCR */
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, 0x00);
- }
- }
- if (!port_detected)
- release_region(info->io_port, REGION_SIZE);
-
- spin_unlock_irqrestore(&info->lock, flags);
- return (port_detected);
-}
-
-static const struct tty_operations esp_ops = {
- .open = esp_open,
- .close = rs_close,
- .write = rs_write,
- .put_char = rs_put_char,
- .flush_chars = rs_flush_chars,
- .write_room = rs_write_room,
- .chars_in_buffer = rs_chars_in_buffer,
- .flush_buffer = rs_flush_buffer,
- .ioctl = rs_ioctl,
- .throttle = rs_throttle,
- .unthrottle = rs_unthrottle,
- .set_termios = rs_set_termios,
- .stop = rs_stop,
- .start = rs_start,
- .hangup = esp_hangup,
- .break_ctl = esp_break,
- .wait_until_sent = rs_wait_until_sent,
- .tiocmget = esp_tiocmget,
- .tiocmset = esp_tiocmset,
-};
-
-static const struct tty_port_operations esp_port_ops = {
- .esp_carrier_raised,
-};
-
-/*
- * The serial driver boot-time initialization code!
- */
-static int __init espserial_init(void)
-{
- int i, offset;
- struct esp_struct *info;
- struct esp_struct *last_primary = NULL;
- int esp[] = { 0x100, 0x140, 0x180, 0x200, 0x240, 0x280, 0x300, 0x380 };
-
- esp_driver = alloc_tty_driver(NR_PORTS);
- if (!esp_driver)
- return -ENOMEM;
-
- for (i = 0; i < NR_PRIMARY; i++) {
- if (irq[i] != 0) {
- if ((irq[i] < 2) || (irq[i] > 15) || (irq[i] == 6) ||
- (irq[i] == 8) || (irq[i] == 13))
- irq[i] = 0;
- else if (irq[i] == 2)
- irq[i] = 9;
- }
- }
-
- if ((dma != 1) && (dma != 3))
- dma = 0;
-
- if ((rx_trigger < 1) || (rx_trigger > 1023))
- rx_trigger = 768;
-
- if ((tx_trigger < 1) || (tx_trigger > 1023))
- tx_trigger = 768;
-
- if ((flow_off < 1) || (flow_off > 1023))
- flow_off = 1016;
-
- if ((flow_on < 1) || (flow_on > 1023))
- flow_on = 944;
-
- if ((rx_timeout < 0) || (rx_timeout > 255))
- rx_timeout = 128;
-
- if (flow_on >= flow_off)
- flow_on = flow_off - 1;
-
- show_serial_version();
-
- /* Initialize the tty_driver structure */
-
- esp_driver->owner = THIS_MODULE;
- esp_driver->name = "ttyP";
- esp_driver->major = ESP_IN_MAJOR;
- esp_driver->minor_start = 0;
- esp_driver->type = TTY_DRIVER_TYPE_SERIAL;
- esp_driver->subtype = SERIAL_TYPE_NORMAL;
- esp_driver->init_termios = tty_std_termios;
- esp_driver->init_termios.c_cflag =
- B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- esp_driver->init_termios.c_ispeed = 9600;
- esp_driver->init_termios.c_ospeed = 9600;
- esp_driver->flags = TTY_DRIVER_REAL_RAW;
- tty_set_operations(esp_driver, &esp_ops);
- if (tty_register_driver(esp_driver)) {
- printk(KERN_ERR "Couldn't register esp serial driver");
- put_tty_driver(esp_driver);
- return 1;
- }
-
- info = kzalloc(sizeof(struct esp_struct), GFP_KERNEL);
-
- if (!info) {
- printk(KERN_ERR "Couldn't allocate memory for esp serial device information\n");
- tty_unregister_driver(esp_driver);
- put_tty_driver(esp_driver);
- return 1;
- }
-
- spin_lock_init(&info->lock);
- /* rx_trigger, tx_trigger are needed by autoconfig */
- info->config.rx_trigger = rx_trigger;
- info->config.tx_trigger = tx_trigger;
-
- i = 0;
- offset = 0;
-
- do {
- tty_port_init(&info->port);
- info->port.ops = &esp_port_ops;
- info->io_port = esp[i] + offset;
- info->irq = irq[i];
- info->line = (i * 8) + (offset / 8);
-
- if (!autoconfig(info)) {
- i++;
- offset = 0;
- continue;
- }
-
- info->custom_divisor = (divisor[i] >> (offset / 2)) & 0xf;
- info->port.flags = STD_COM_FLAGS;
- if (info->custom_divisor)
- info->port.flags |= ASYNC_SPD_CUST;
- info->magic = ESP_MAGIC;
- info->close_delay = 5*HZ/10;
- info->closing_wait = 30*HZ;
- info->config.rx_timeout = rx_timeout;
- info->config.flow_on = flow_on;
- info->config.flow_off = flow_off;
- info->config.pio_threshold = pio_threshold;
- info->next_port = ports;
- init_waitqueue_head(&info->break_wait);
- ports = info;
- printk(KERN_INFO "ttyP%d at 0x%04x (irq = %d) is an ESP ",
- info->line, info->io_port, info->irq);
-
- if (info->line % 8) {
- printk("secondary port\n");
- /* 8 port cards can't do DMA */
- info->stat_flags |= ESP_STAT_NEVER_DMA;
-
- if (last_primary)
- last_primary->stat_flags |= ESP_STAT_NEVER_DMA;
- } else {
- printk("primary port\n");
- last_primary = info;
- irq[i] = info->irq;
- }
-
- if (!dma)
- info->stat_flags |= ESP_STAT_NEVER_DMA;
-
- info = kzalloc(sizeof(struct esp_struct), GFP_KERNEL);
- if (!info) {
- printk(KERN_ERR "Couldn't allocate memory for esp serial device information\n");
- /* allow use of the already detected ports */
- return 0;
- }
-
- spin_lock_init(&info->lock);
- /* rx_trigger, tx_trigger are needed by autoconfig */
- info->config.rx_trigger = rx_trigger;
- info->config.tx_trigger = tx_trigger;
-
- if (offset == 56) {
- i++;
- offset = 0;
- } else {
- offset += 8;
- }
- } while (i < NR_PRIMARY);
-
- /* free the last port memory allocation */
- kfree(info);
-
- return 0;
-}
-
-static void __exit espserial_exit(void)
-{
- int e1;
- struct esp_struct *temp_async;
- struct esp_pio_buffer *pio_buf;
-
- e1 = tty_unregister_driver(esp_driver);
- if (e1)
- printk(KERN_ERR "esp: failed to unregister driver (%d)\n", e1);
- put_tty_driver(esp_driver);
-
- while (ports) {
- if (ports->io_port)
- release_region(ports->io_port, REGION_SIZE);
- temp_async = ports->next_port;
- kfree(ports);
- ports = temp_async;
- }
-
- if (dma_buffer)
- free_pages((unsigned long)dma_buffer,
- get_order(DMA_BUFFER_SZ));
-
- while (free_pio_buf) {
- pio_buf = free_pio_buf->next;
- kfree(free_pio_buf);
- free_pio_buf = pio_buf;
- }
-}
-
-module_init(espserial_init);
-module_exit(espserial_exit);
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 5a53857..f72914d 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -214,7 +214,6 @@ unifdef-y += futex.h
unifdef-y += fs.h
unifdef-y += gameport.h
unifdef-y += generic_serial.h
-unifdef-y += hayesesp.h
unifdef-y += hdlcdrv.h
unifdef-y += hdlc.h
unifdef-y += hdreg.h
diff --git a/include/linux/hayesesp.h b/include/linux/hayesesp.h
deleted file mode 100644
index 92b08cf..0000000
--- a/include/linux/hayesesp.h
+++ /dev/null
@@ -1,114 +0,0 @@
-#ifndef HAYESESP_H
-#define HAYESESP_H
-
-struct hayes_esp_config {
- short flow_on;
- short flow_off;
- short rx_trigger;
- short tx_trigger;
- short pio_threshold;
- unsigned char rx_timeout;
- char dma_channel;
-};
-
-#ifdef __KERNEL__
-
-#define ESP_DMA_CHANNEL 0
-#define ESP_RX_TRIGGER 768
-#define ESP_TX_TRIGGER 768
-#define ESP_FLOW_OFF 1016
-#define ESP_FLOW_ON 944
-#define ESP_RX_TMOUT 128
-#define ESP_PIO_THRESHOLD 32
-
-#define ESP_IN_MAJOR 57 /* major dev # for dial in */
-#define ESP_OUT_MAJOR 58 /* major dev # for dial out */
-#define ESPC_SCALE 3
-#define UART_ESI_BASE 0x00
-#define UART_ESI_SID 0x01
-#define UART_ESI_RX 0x02
-#define UART_ESI_TX 0x02
-#define UART_ESI_CMD1 0x04
-#define UART_ESI_CMD2 0x05
-#define UART_ESI_STAT1 0x04
-#define UART_ESI_STAT2 0x05
-#define UART_ESI_RWS 0x07
-
-#define UART_IER_DMA_TMOUT 0x80
-#define UART_IER_DMA_TC 0x08
-
-#define ESI_SET_IRQ 0x04
-#define ESI_SET_DMA_TMOUT 0x05
-#define ESI_SET_SRV_MASK 0x06
-#define ESI_SET_ERR_MASK 0x07
-#define ESI_SET_FLOW_CNTL 0x08
-#define ESI_SET_FLOW_CHARS 0x09
-#define ESI_SET_FLOW_LVL 0x0a
-#define ESI_SET_TRIGGER 0x0b
-#define ESI_SET_RX_TIMEOUT 0x0c
-#define ESI_SET_FLOW_TMOUT 0x0d
-#define ESI_WRITE_UART 0x0e
-#define ESI_READ_UART 0x0f
-#define ESI_SET_MODE 0x10
-#define ESI_GET_ERR_STAT 0x12
-#define ESI_GET_UART_STAT 0x13
-#define ESI_GET_RX_AVAIL 0x14
-#define ESI_GET_TX_AVAIL 0x15
-#define ESI_START_DMA_RX 0x16
-#define ESI_START_DMA_TX 0x17
-#define ESI_ISSUE_BREAK 0x1a
-#define ESI_FLUSH_RX 0x1b
-#define ESI_FLUSH_TX 0x1c
-#define ESI_SET_BAUD 0x1d
-#define ESI_SET_ENH_IRQ 0x1f
-#define ESI_SET_REINTR 0x20
-#define ESI_SET_PRESCALAR 0x23
-#define ESI_NO_COMMAND 0xff
-
-#define ESP_STAT_RX_TIMEOUT 0x01
-#define ESP_STAT_DMA_RX 0x02
-#define ESP_STAT_DMA_TX 0x04
-#define ESP_STAT_NEVER_DMA 0x08
-#define ESP_STAT_USE_PIO 0x10
-
-#define ESP_MAGIC 0x53ee
-#define ESP_XMIT_SIZE 4096
-
-struct esp_struct {
- int magic;
- struct tty_port port;
- spinlock_t lock;
- int io_port;
- int irq;
- int read_status_mask;
- int ignore_status_mask;
- int timeout;
- int stat_flags;
- int custom_divisor;
- int close_delay;
- unsigned short closing_wait;
- unsigned short closing_wait2;
- int IER; /* Interrupt Enable Register */
- int MCR; /* Modem control register */
- unsigned long last_active;
- int line;
- unsigned char *xmit_buf;
- int xmit_head;
- int xmit_tail;
- int xmit_cnt;
- wait_queue_head_t break_wait;
- struct async_icount icount; /* kernel counters for the 4 input interrupts */
- struct hayes_esp_config config; /* port configuration */
- struct esp_struct *next_port; /* For the linked list */
-};
-
-struct esp_pio_buffer {
- unsigned char data[1024];
- struct esp_pio_buffer *next;
-};
-
-#endif /* __KERNEL__ */
-
-
-#endif /* ESP_H */
-
--
1.6.5.5

2009-12-11 23:38:07

by Greg KH

[permalink] [raw]
Subject: [PATCH 11/58] tty: istallion: Kill off the BKL ioctl

From: Alan Cox <[email protected]>

Fairly trivial as the BKL push down into the methods has already been done.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/istallion.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index 402838f..babfd44 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -621,7 +621,7 @@ static int stli_brdinit(struct stlibrd *brdp);
static int stli_startbrd(struct stlibrd *brdp);
static ssize_t stli_memread(struct file *fp, char __user *buf, size_t count, loff_t *offp);
static ssize_t stli_memwrite(struct file *fp, const char __user *buf, size_t count, loff_t *offp);
-static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
+static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg);
static void stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp);
static void stli_poll(unsigned long arg);
static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp);
@@ -704,7 +704,7 @@ static const struct file_operations stli_fsiomem = {
.owner = THIS_MODULE,
.read = stli_memread,
.write = stli_memwrite,
- .ioctl = stli_memioctl,
+ .unlocked_ioctl = stli_memioctl,
};

/*****************************************************************************/
@@ -4311,7 +4311,7 @@ static int stli_getbrdstruct(struct stlibrd __user *arg)
* reset it, and start/stop it.
*/

-static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
struct stlibrd *brdp;
int brdnr, rc, done;
@@ -4356,7 +4356,7 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un
* Now handle the board specific ioctls. These all depend on the
* minor number of the device they were called from.
*/
- brdnr = iminor(ip);
+ brdnr = iminor(fp->f_dentry->d_inode);
if (brdnr >= STL_MAXBRDS)
return -ENODEV;
brdp = stli_brds[brdnr];
--
1.6.5.5

2009-12-11 23:40:25

by Greg KH

[permalink] [raw]
Subject: [PATCH 12/58] tty: stallion: kill BKL ioctl

From: Alan Cox <[email protected]>

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/stallion.c | 13 +++++++------
1 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index db6dcfa..b4ba5ed 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -407,7 +407,7 @@ static unsigned int stl_baudrates[] = {
* Declare all those functions in this driver!
*/

-static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
+static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg);
static int stl_brdinit(struct stlbrd *brdp);
static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp);
static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp);
@@ -607,7 +607,7 @@ static unsigned int sc26198_baudtable[] = {
*/
static const struct file_operations stl_fsiomem = {
.owner = THIS_MODULE,
- .ioctl = stl_memioctl,
+ .unlocked_ioctl = stl_memioctl,
};

static struct class *stallion_class;
@@ -2486,18 +2486,19 @@ static int stl_getbrdstruct(struct stlbrd __user *arg)
* collection.
*/

-static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
int brdnr, rc;
void __user *argp = (void __user *)arg;

- pr_debug("stl_memioctl(ip=%p,fp=%p,cmd=%x,arg=%lx)\n", ip, fp, cmd,arg);
+ pr_debug("stl_memioctl(fp=%p,cmd=%x,arg=%lx)\n", fp, cmd,arg);

- brdnr = iminor(ip);
+ brdnr = iminor(fp->f_dentry->d_inode);
if (brdnr >= STL_MAXBRDS)
return -ENODEV;
rc = 0;

+ lock_kernel();
switch (cmd) {
case COM_GETPORTSTATS:
rc = stl_getportstats(NULL, NULL, argp);
@@ -2518,7 +2519,7 @@ static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, uns
rc = -ENOIOCTLCMD;
break;
}
-
+ unlock_kernel();
return rc;
}

--
1.6.5.5

2009-12-11 23:40:31

by Greg KH

[permalink] [raw]
Subject: [PATCH 13/58] tty_port: add "tty_port_open" helper

From: Alan Cox <[email protected]>

For the moment this just moves the USB logic over and fixes the 'what if
we open and hangup at the same time' race noticed by Oliver Neukum.

Signed-off-by: Alan Cox <[email protected]>
Cc: Alan Stern <[email protected]>
Cc: Oliver Neukum <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/tty_port.c | 36 +++++++++++++++++++++++++++-
drivers/usb/serial/usb-serial.c | 49 ++++++++++++++++-----------------------
include/linux/tty.h | 10 +++++++-
3 files changed, 64 insertions(+), 31 deletions(-)

diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
index c63f3d3..b22a61a 100644
--- a/drivers/char/tty_port.c
+++ b/drivers/char/tty_port.c
@@ -99,10 +99,11 @@ EXPORT_SYMBOL(tty_port_tty_set);

static void tty_port_shutdown(struct tty_port *port)
{
+ mutex_lock(&port->mutex);
if (port->ops->shutdown &&
test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags))
port->ops->shutdown(port);
-
+ mutex_unlock(&port->mutex);
}

/**
@@ -381,3 +382,36 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty,
tty_port_tty_set(port, NULL);
}
EXPORT_SYMBOL(tty_port_close);
+
+int tty_port_open(struct tty_port *port, struct tty_struct *tty,
+ struct file *filp)
+{
+ spin_lock_irq(&port->lock);
+ if (!tty_hung_up_p(filp))
+ ++port->count;
+ spin_unlock_irq(&port->lock);
+ tty_port_tty_set(port, tty);
+
+ /*
+ * Do the device-specific open only if the hardware isn't
+ * already initialized. Serialize open and shutdown using the
+ * port mutex.
+ */
+
+ mutex_lock(&port->mutex);
+
+ if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) {
+ if (port->ops->activate) {
+ int retval = port->ops->activate(port, tty);
+ if (retval) {
+ mutex_unlock(&port->mutex);
+ return retval;
+ }
+ }
+ set_bit(ASYNCB_INITIALIZED, &port->flags);
+ }
+ mutex_unlock(&port->mutex);
+ return tty_port_block_til_ready(port, tty, filp);
+}
+
+EXPORT_SYMBOL(tty_port_open);
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index bd3fa7f..b0649d9 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -247,41 +247,31 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
return retval;
}

-static int serial_open(struct tty_struct *tty, struct file *filp)
+static int serial_activate(struct tty_port *tport, struct tty_struct *tty)
{
- struct usb_serial_port *port = tty->driver_data;
+ struct usb_serial_port *port =
+ container_of(tport, struct usb_serial_port, port);
struct usb_serial *serial = port->serial;
int retval;

- dbg("%s - port %d", __func__, port->number);
-
- spin_lock_irq(&port->port.lock);
- if (!tty_hung_up_p(filp))
- ++port->port.count;
- spin_unlock_irq(&port->port.lock);
- tty_port_tty_set(&port->port, tty);
+ if (mutex_lock_interruptible(&port->mutex))
+ return -ERESTARTSYS;
+ mutex_lock(&serial->disc_mutex);
+ if (serial->disconnected)
+ retval = -ENODEV;
+ else
+ retval = port->serial->type->open(tty, port);
+ mutex_unlock(&serial->disc_mutex);
+ mutex_unlock(&port->mutex);
+ return retval;
+}

- /* Do the device-specific open only if the hardware isn't
- * already initialized.
- */
- if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) {
- if (mutex_lock_interruptible(&port->mutex))
- return -ERESTARTSYS;
- mutex_lock(&serial->disc_mutex);
- if (serial->disconnected)
- retval = -ENODEV;
- else
- retval = port->serial->type->open(tty, port);
- mutex_unlock(&serial->disc_mutex);
- mutex_unlock(&port->mutex);
- if (retval)
- return retval;
- set_bit(ASYNCB_INITIALIZED, &port->port.flags);
- }
+static int serial_open(struct tty_struct *tty, struct file *filp)
+{
+ struct usb_serial_port *port = tty->driver_data;

- /* Now do the correct tty layer semantics */
- retval = tty_port_block_til_ready(&port->port, tty, filp);
- return retval;
+ dbg("%s - port %d", __func__, port->number);
+ return tty_port_open(&port->port, tty, filp);
}

/**
@@ -725,6 +715,7 @@ static void serial_dtr_rts(struct tty_port *port, int on)
static const struct tty_port_operations serial_port_ops = {
.carrier_raised = serial_carrier_raised,
.dtr_rts = serial_dtr_rts,
+ .activate = serial_activate,
};

int usb_serial_probe(struct usb_interface *interface,
diff --git a/include/linux/tty.h b/include/linux/tty.h
index f0f43d0..6352ac2 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -190,9 +190,15 @@ struct tty_port_operations {
/* Control the DTR line */
void (*dtr_rts)(struct tty_port *port, int raise);
/* Called when the last close completes or a hangup finishes
- IFF the port was initialized. Do not use to free resources */
+ IFF the port was initialized. Do not use to free resources. Called
+ under the port mutex to serialize against activate/shutdowns */
void (*shutdown)(struct tty_port *port);
void (*drop)(struct tty_port *port);
+ /* Called under the port mutex from tty_port_open, serialized using
+ the port mutex */
+ /* FIXME: long term getting the tty argument *out* of this would be
+ good for consoles */
+ int (*activate)(struct tty_port *port, struct tty_struct *tty);
};

struct tty_port {
@@ -467,6 +473,8 @@ extern int tty_port_close_start(struct tty_port *port,
extern void tty_port_close_end(struct tty_port *port, struct tty_struct *tty);
extern void tty_port_close(struct tty_port *port,
struct tty_struct *tty, struct file *filp);
+extern int tty_port_open(struct tty_port *port,
+ struct tty_struct *tty, struct file *filp);
extern inline int tty_port_users(struct tty_port *port)
{
return port->count + port->blocked_open;
--
1.6.5.5

2009-12-11 23:40:34

by Greg KH

[permalink] [raw]
Subject: [PATCH 14/58] tty_port: coding style cleaning pass

From: Alan Cox <[email protected]>

Mind the hoover wire...

Signed-off-by: Alan Cox <[email protected]>
Cc: Alan Stern <[email protected]>
Cc: Oliver Neukum <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/tty_port.c | 25 +++++++++++++------------
1 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
index b22a61a..41b314c 100644
--- a/drivers/char/tty_port.c
+++ b/drivers/char/tty_port.c
@@ -199,7 +199,7 @@ EXPORT_SYMBOL(tty_port_lower_dtr_rts);
* management of these lines. Note that the dtr/rts raise is done each
* iteration as a hangup may have previously dropped them while we wait.
*/
-
+
int tty_port_block_til_ready(struct tty_port *port,
struct tty_struct *tty, struct file *filp)
{
@@ -254,7 +254,8 @@ int tty_port_block_til_ready(struct tty_port *port,
tty_port_raise_dtr_rts(port);

prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE);
- /* Check for a hangup or uninitialised port. Return accordingly */
+ /* Check for a hangup or uninitialised port.
+ Return accordingly */
if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
if (port->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
@@ -286,11 +287,11 @@ int tty_port_block_til_ready(struct tty_port *port,
port->flags |= ASYNC_NORMAL_ACTIVE;
spin_unlock_irqrestore(&port->lock, flags);
return retval;
-
}
EXPORT_SYMBOL(tty_port_block_til_ready);

-int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp)
+int tty_port_close_start(struct tty_port *port,
+ struct tty_struct *tty, struct file *filp)
{
unsigned long flags;

@@ -300,7 +301,7 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f
return 0;
}

- if( tty->count == 1 && port->count != 1) {
+ if (tty->count == 1 && port->count != 1) {
printk(KERN_WARNING
"tty_port_close_start: tty->count = 1 port count = %d.\n",
port->count);
@@ -332,8 +333,8 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f
long timeout;

if (bps > 1200)
- timeout = max_t(long, (HZ * 10 * port->drain_delay) / bps,
- HZ / 10);
+ timeout = max_t(long,
+ (HZ * 10 * port->drain_delay) / bps, HZ / 10);
else
timeout = 2 * HZ;
schedule_timeout_interruptible(timeout);
@@ -384,7 +385,7 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty,
EXPORT_SYMBOL(tty_port_close);

int tty_port_open(struct tty_port *port, struct tty_struct *tty,
- struct file *filp)
+ struct file *filp)
{
spin_lock_irq(&port->lock);
if (!tty_hung_up_p(filp))
@@ -404,10 +405,10 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty,
if (port->ops->activate) {
int retval = port->ops->activate(port, tty);
if (retval) {
- mutex_unlock(&port->mutex);
- return retval;
- }
- }
+ mutex_unlock(&port->mutex);
+ return retval;
+ }
+ }
set_bit(ASYNCB_INITIALIZED, &port->flags);
}
mutex_unlock(&port->mutex);
--
1.6.5.5

2009-12-11 23:41:01

by Greg KH

[permalink] [raw]
Subject: [PATCH 15/58] usb_serial: Use the shutdown() operation

From: Alan Cox <[email protected]>

As Alan Stern pointed out - now we have tty_port_open the shutdown method
and locking allow us to whack the other bits into the full helper methods
and provide a shutdown op which the tty port code will synchronize with
setup for us.

Signed-off-by: Alan Cox <[email protected]>
Cc: Alan Stern <[email protected]>
Cc: Oliver Neukum <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/usb/serial/usb-serial.c | 39 +++++++++++----------------------------
1 files changed, 11 insertions(+), 28 deletions(-)

diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index b0649d9..829a466 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -254,15 +254,12 @@ static int serial_activate(struct tty_port *tport, struct tty_struct *tty)
struct usb_serial *serial = port->serial;
int retval;

- if (mutex_lock_interruptible(&port->mutex))
- return -ERESTARTSYS;
mutex_lock(&serial->disc_mutex);
if (serial->disconnected)
retval = -ENODEV;
else
retval = port->serial->type->open(tty, port);
mutex_unlock(&serial->disc_mutex);
- mutex_unlock(&port->mutex);
return retval;
}

@@ -276,57 +273,40 @@ static int serial_open(struct tty_struct *tty, struct file *filp)

/**
* serial_down - shut down hardware
- * @port: port to shut down
+ * @tport: tty port to shut down
*
* Shut down a USB serial port unless it is the console. We never
- * shut down the console hardware as it will always be in use.
+ * shut down the console hardware as it will always be in use. Serialized
+ * against activate by the tport mutex and kept to matching open/close pairs
+ * of calls by the ASYNCB_INITIALIZED flag.
*/
-static void serial_down(struct usb_serial_port *port)
+static void serial_down(struct tty_port *tport)
{
+ struct usb_serial_port *port =
+ container_of(tport, struct usb_serial_port, port);
struct usb_serial_driver *drv = port->serial->type;
-
/*
* The console is magical. Do not hang up the console hardware
* or there will be tears.
*/
if (port->console)
return;
-
- /* Don't call the close method if the hardware hasn't been
- * initialized.
- */
- if (!test_and_clear_bit(ASYNCB_INITIALIZED, &port->port.flags))
- return;
-
- mutex_lock(&port->mutex);
if (drv->close)
drv->close(port);
- mutex_unlock(&port->mutex);
}

static void serial_hangup(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
-
dbg("%s - port %d", __func__, port->number);
-
- serial_down(port);
tty_port_hangup(&port->port);
}

static void serial_close(struct tty_struct *tty, struct file *filp)
{
struct usb_serial_port *port = tty->driver_data;
-
dbg("%s - port %d", __func__, port->number);
-
- if (tty_hung_up_p(filp))
- return;
- if (tty_port_close_start(&port->port, tty, filp) == 0)
- return;
- serial_down(port);
- tty_port_close_end(&port->port, tty);
- tty_port_tty_set(&port->port, NULL);
+ tty_port_close(&port->port, tty, filp);
}

/**
@@ -716,6 +696,7 @@ static const struct tty_port_operations serial_port_ops = {
.carrier_raised = serial_carrier_raised,
.dtr_rts = serial_dtr_rts,
.activate = serial_activate,
+ .shutdown = serial_down,
};

int usb_serial_probe(struct usb_interface *interface,
@@ -914,6 +895,8 @@ int usb_serial_probe(struct usb_interface *interface,
port->port.ops = &serial_port_ops;
port->serial = serial;
spin_lock_init(&port->lock);
+ /* Keep this for private driver use for the moment but
+ should probably go away */
mutex_init(&port->mutex);
INIT_WORK(&port->work, usb_serial_port_work);
serial->port[i] = port;
--
1.6.5.5

2009-12-11 23:31:35

by Greg KH

[permalink] [raw]
Subject: [PATCH 16/58] usb_serial: Kill port mutex

From: Alan Cox <[email protected]>

The tty port has a port mutex used for all the port related locking so we
don't need the one in the USB serial layer any more.

Signed-off-by: Alan Cox <[email protected]>
Cc: Alan Stern <[email protected]>
Cc: Oliver Neukum <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/usb/serial/opticon.c | 4 ++--
drivers/usb/serial/usb-serial.c | 1 -
include/linux/usb/serial.h | 3 ---
3 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index 80f59b6..c03fdc0 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -501,12 +501,12 @@ static int opticon_resume(struct usb_interface *intf)
struct usb_serial_port *port = serial->port[0];
int result;

- mutex_lock(&port->mutex);
+ mutex_lock(&port->port.mutex);
if (port->port.count)
result = usb_submit_urb(priv->bulk_read_urb, GFP_NOIO);
else
result = 0;
- mutex_unlock(&port->mutex);
+ mutex_unlock(&port->port.mutex);
return result;
}

diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 829a466..4543f35 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -897,7 +897,6 @@ int usb_serial_probe(struct usb_interface *interface,
spin_lock_init(&port->lock);
/* Keep this for private driver use for the moment but
should probably go away */
- mutex_init(&port->mutex);
INIT_WORK(&port->work, usb_serial_port_work);
serial->port[i] = port;
port->dev.parent = &interface->dev;
diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h
index ce911eb..acf6e45 100644
--- a/include/linux/usb/serial.h
+++ b/include/linux/usb/serial.h
@@ -39,8 +39,6 @@ enum port_dev_state {
* @serial: pointer back to the struct usb_serial owner of this port.
* @port: pointer to the corresponding tty_port for this port.
* @lock: spinlock to grab when updating portions of this structure.
- * @mutex: mutex used to synchronize serial_open() and serial_close()
- * access for this port.
* @number: the number of the port (the minor number).
* @interrupt_in_buffer: pointer to the interrupt in buffer for this port.
* @interrupt_in_urb: pointer to the interrupt in struct urb for this port.
@@ -77,7 +75,6 @@ struct usb_serial_port {
struct usb_serial *serial;
struct tty_port port;
spinlock_t lock;
- struct mutex mutex;
unsigned char number;

unsigned char *interrupt_in_buffer;
--
1.6.5.5

2009-12-11 23:29:51

by Greg KH

[permalink] [raw]
Subject: [PATCH 17/58] opticon: Fix resume logic

From: Alan Cox <[email protected]>

Opticon now takes the right mutex to check the port status but the status
check is done wrongly for the modern serial code, so fix it.

Signed-off-by: Alan Cox <[email protected]>
Cc: Alan Stern <[email protected]>
Cc: Oliver Neukum <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/usb/serial/opticon.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index c03fdc0..4cdb975 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -502,7 +502,8 @@ static int opticon_resume(struct usb_interface *intf)
int result;

mutex_lock(&port->port.mutex);
- if (port->port.count)
+ /* This is protected by the port mutex against close/open */
+ if (test_bit(ASYNCB_INITIALIZED, &port->port.flags))
result = usb_submit_urb(priv->bulk_read_urb, GFP_NOIO);
else
result = 0;
--
1.6.5.5

2009-12-11 23:29:36

by Greg KH

[permalink] [raw]
Subject: [PATCH 18/58] serial: fix NULL pointer dereference

From: André Goddard Rosa <[email protected]>

If kzalloc() or alloc_tty_driver() fails, we call:
put_tty_driver(normal = NULL).

Then:
put_tty_driver -> tty_driver_kref_put -> kref_put(&NULL->kref, ...)

Signed-off-by: André Goddard Rosa <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/serial_core.c | 21 +++++++++++----------
1 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index dcc7244..885eabe 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -2344,7 +2344,7 @@ static const struct tty_operations uart_ops = {
*/
int uart_register_driver(struct uart_driver *drv)
{
- struct tty_driver *normal = NULL;
+ struct tty_driver *normal;
int i, retval;

BUG_ON(drv->state);
@@ -2354,13 +2354,12 @@ int uart_register_driver(struct uart_driver *drv)
* we have a large number of ports to handle.
*/
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
- retval = -ENOMEM;
if (!drv->state)
goto out;

- normal = alloc_tty_driver(drv->nr);
+ normal = alloc_tty_driver(drv->nr);
if (!normal)
- goto out;
+ goto out_kfree;

drv->tty_driver = normal;

@@ -2393,12 +2392,14 @@ int uart_register_driver(struct uart_driver *drv)
}

retval = tty_register_driver(normal);
- out:
- if (retval < 0) {
- put_tty_driver(normal);
- kfree(drv->state);
- }
- return retval;
+ if (retval >= 0)
+ return retval;
+
+ put_tty_driver(normal);
+out_kfree:
+ kfree(drv->state);
+out:
+ return -ENOMEM;
}

/**
--
1.6.5.5

2009-12-11 23:35:36

by Greg KH

[permalink] [raw]
Subject: [PATCH 19/58] serial: cascade needless conditionals

From: André Goddard Rosa <[email protected]>

Signed-off-by: André Goddard Rosa <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/serial_core.c | 12 +++++-------
1 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index 885eabe..047530b 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -342,11 +342,11 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,

if (flags == UPF_SPD_HI)
altbaud = 57600;
- if (flags == UPF_SPD_VHI)
+ else if (flags == UPF_SPD_VHI)
altbaud = 115200;
- if (flags == UPF_SPD_SHI)
+ else if (flags == UPF_SPD_SHI)
altbaud = 230400;
- if (flags == UPF_SPD_WARP)
+ else if (flags == UPF_SPD_WARP)
altbaud = 460800;

for (try = 0; try < 2; try++) {
@@ -1217,9 +1217,8 @@ static void uart_set_termios(struct tty_struct *tty,
/* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR);
-
/* Handle transition away from B0 status */
- if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
+ else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
unsigned int mask = TIOCM_DTR;
if (!(cflag & CRTSCTS) ||
!test_bit(TTY_THROTTLED, &tty->flags))
@@ -1234,9 +1233,8 @@ static void uart_set_termios(struct tty_struct *tty,
__uart_start(tty);
spin_unlock_irqrestore(&state->uart_port->lock, flags);
}
-
/* Handle turning on CRTSCTS */
- if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
+ else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
spin_lock_irqsave(&state->uart_port->lock, flags);
if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) {
tty->hw_stopped = 1;
--
1.6.5.5

2009-12-11 23:29:20

by Greg KH

[permalink] [raw]
Subject: [PATCH 20/58] serial, 8250: calculate irqflags bitmask before loop

From: André Goddard Rosa <[email protected]>

Signed-off-by: André Goddard Rosa <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/8250.c | 16 ++++++++++------
1 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 737b4c9..b75e63e 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -2646,7 +2646,7 @@ static void __init serial8250_isa_init_ports(void)
{
struct uart_8250_port *up;
static int first = 1;
- int i;
+ int i, irqflag = 0;

if (!first)
return;
@@ -2670,6 +2670,9 @@ static void __init serial8250_isa_init_ports(void)
up->port.ops = &serial8250_pops;
}

+ if (share_irqs)
+ irqflag = IRQF_SHARED;
+
for (i = 0, up = serial8250_ports;
i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
i++, up++) {
@@ -2683,8 +2686,7 @@ static void __init serial8250_isa_init_ports(void)
up->port.iotype = old_serial_port[i].io_type;
up->port.regshift = old_serial_port[i].iomem_reg_shift;
set_io_from_upio(&up->port);
- if (share_irqs)
- up->port.irqflags |= IRQF_SHARED;
+ up->port.irqflags |= irqflag;
}
}

@@ -2940,10 +2942,13 @@ static int __devinit serial8250_probe(struct platform_device *dev)
{
struct plat_serial8250_port *p = dev->dev.platform_data;
struct uart_port port;
- int ret, i;
+ int ret, i, irqflag = 0;

memset(&port, 0, sizeof(struct uart_port));

+ if (share_irqs)
+ irqflag = IRQF_SHARED;
+
for (i = 0; p && p->flags != 0; p++, i++) {
port.iobase = p->iobase;
port.membase = p->membase;
@@ -2960,8 +2965,7 @@ static int __devinit serial8250_probe(struct platform_device *dev)
port.serial_in = p->serial_in;
port.serial_out = p->serial_out;
port.dev = &dev->dev;
- if (share_irqs)
- port.irqflags |= IRQF_SHARED;
+ port.irqflags |= irqflag;
ret = serial8250_register_port(&port);
if (ret < 0) {
dev_err(&dev->dev, "unable to register port at index %d "
--
1.6.5.5

2009-12-11 23:32:58

by Greg KH

[permalink] [raw]
Subject: [PATCH 21/58] Serial: pxa: work around Errata #75

From: Uwe Kleine-König <[email protected]>

Intel(R) PXA27x Processor Family Specification Update (Nov 2005)
says:

E75. UART: Baud rate may not be programmed correctly on
back-to-back writes.

Problem:
When programming the Divisor Latch registers, Low and High (DLL and
DLH), with back-to-back writes, the second register write may not
take effect. The result is an incorrect baud rate.

Workaround:
After programming the first Divisor Latch register, read and verify
it before programming the second Divisor Latch register.

This was hit when changing the baud rate from 115200 to 9600 while
receiving characters at 9600 Bd.

And fixed indention of some comments nearby.

Signed-off-by: Uwe Kleine-König <[email protected]>
Acked-by: Wolfram Sang <[email protected]>
Acked-by: Marc Kleine-Budde <[email protected]>
Cc: Eric Miao <[email protected]>
Cc: Alan Cox <[email protected]>
Cc: Mike Rapoport <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/pxa.c | 13 +++++++++++--
1 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c
index b8629d7..4a82104 100644
--- a/drivers/serial/pxa.c
+++ b/drivers/serial/pxa.c
@@ -438,6 +438,7 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios,
unsigned char cval, fcr = 0;
unsigned long flags;
unsigned int baud, quot;
+ unsigned int dll;

switch (termios->c_cflag & CSIZE) {
case CS5:
@@ -534,10 +535,18 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios,
else
up->mcr &= ~UART_MCR_AFE;

- serial_out(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+ serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */
+
+ /*
+ * work around Errata #75 according to Intel(R) PXA27x Processor Family
+ * Specification Update (Nov 2005)
+ */
+ dll = serial_in(up, UART_DLL);
+ WARN_ON(dll != (quot & 0xff));
+
serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */
- serial_out(up, UART_LCR, cval); /* reset DLAB */
+ serial_out(up, UART_LCR, cval); /* reset DLAB */
up->lcr = cval; /* Save LCR */
serial_pxa_set_mctrl(&up->port, up->port.mctrl);
serial_out(up, UART_FCR, fcr);
--
1.6.5.5

2009-12-11 23:29:28

by Greg KH

[permalink] [raw]
Subject: [PATCH 22/58] tty: docs: serial/tty, add to ldisc methods

From: Tilman Schmidt <[email protected]>

A small addition to the ldisc method descriptions.

Signed-off-by: Tilman Schmidt <[email protected]>
Signed-off-by: Randy Dunlap <[email protected]>
Acked-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
Documentation/serial/tty.txt | 9 +++++++--
1 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/Documentation/serial/tty.txt b/Documentation/serial/tty.txt
index 8e65c44..5e5349a 100644
--- a/Documentation/serial/tty.txt
+++ b/Documentation/serial/tty.txt
@@ -42,7 +42,8 @@ TTY side interfaces:
open() - Called when the line discipline is attached to
the terminal. No other call into the line
discipline for this tty will occur until it
- completes successfully. Can sleep.
+ completes successfully. Returning an error will
+ prevent the ldisc from being attached. Can sleep.

close() - This is called on a terminal when the line
discipline is being unplugged. At the point of
@@ -52,7 +53,7 @@ close() - This is called on a terminal when the line
hangup() - Called when the tty line is hung up.
The line discipline should cease I/O to the tty.
No further calls into the ldisc code will occur.
- Can sleep.
+ The return value is ignored. Can sleep.

write() - A process is writing data through the line
discipline. Multiple write calls are serialized
@@ -83,6 +84,10 @@ ioctl() - Called when an ioctl is handed to the tty layer
that might be for the ldisc. Multiple ioctl calls
may occur in parallel. May sleep.

+compat_ioctl() - Called when a 32 bit ioctl is handed to the tty layer
+ that might be for the ldisc. Multiple ioctl calls
+ may occur in parallel. May sleep.
+
Driver Side Interfaces:

receive_buf() - Hand buffers of bytes from the driver to the ldisc
--
1.6.5.5

2009-12-11 23:29:42

by Greg KH

[permalink] [raw]
Subject: [PATCH 23/58] Serial: Do not read IIR in serial8250_start_tx when UART_BUG_TXEN

From: Ian Jackson <[email protected]>

Do not read IIR in serial8250_start_tx when UART_BUG_TXEN

Reading the IIR clears some oustanding interrupts so it is not safe.
Instead, simply transmit immediately if the buffer is empty without
regard to IIR.

Signed-off-by: Ian Jackson <[email protected]>
Reviewed-by: Markus Armbruster <[email protected]>
Reviewed-by: Jiri Kosina <[email protected]>
Cc: Alan Cox <[email protected]>
Cc: stable <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/serial/8250.c | 8 +++-----
1 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index b75e63e..c3e37c8 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -1339,14 +1339,12 @@ static void serial8250_start_tx(struct uart_port *port)
serial_out(up, UART_IER, up->ier);

if (up->bugs & UART_BUG_TXEN) {
- unsigned char lsr, iir;
+ unsigned char lsr;
lsr = serial_in(up, UART_LSR);
up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
- iir = serial_in(up, UART_IIR) & 0x0f;
if ((up->port.type == PORT_RM9000) ?
- (lsr & UART_LSR_THRE &&
- (iir == UART_IIR_NO_INT || iir == UART_IIR_THRI)) :
- (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT))
+ (lsr & UART_LSR_THRE) :
+ (lsr & UART_LSR_TEMT))
transmit_chars(up);
}
}
--
1.6.5.5

2009-12-11 23:38:01

by Greg KH

[permalink] [raw]
Subject: [PATCH 24/58] devpts_get_tty() should validate inode

From: Sukadev Bhattiprolu <[email protected]>

devpts_get_tty() assumes that the inode passed in is associated with a valid
pty. But if the only reference to the pty is via a bind-mount, the inode
passed to devpts_get_tty() while valid, would refer to a pty that no longer
exists.

With a lot of debug effort, Grzegorz Nosek developed a small program (see
below) to reproduce a crash on recent kernels. This crash is a regression
introduced by the commit:

commit 527b3e4773628b30d03323a2cb5fb0d84441990f
Author: Sukadev Bhattiprolu <[email protected]>
Date: Mon Oct 13 10:43:08 2008 +0100

To fix, ensure that the dentry associated with the inode has not yet been
deleted/unhashed by devpts_pty_kill().

See also:
https://lists.linux-foundation.org/pipermail/containers/2009-July/019273.html

tty-bug.c:

#define _GNU_SOURCE
#include <fcntl.h>
#include <sched.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/signal.h>
#include <unistd.h>
#include <stdio.h>

#include <linux/fs.h>

void dummy(int sig)
{
}

static int child(void *unused)
{
int fd;

signal(SIGINT, dummy); signal(SIGHUP, dummy);
pause(); /* cheesy synchronisation to wait for /dev/pts/0 to appear */

mount("/dev/pts/0", "/dev/console", NULL, MS_BIND, NULL);
sleep(2);

fd = open("/dev/console", O_RDWR);
dup(0); dup(0);
write(1, "Hello world!\n", sizeof("Hello world!\n")-1);
return 0;
}

int main(void)
{
pid_t pid;
char *stack;

stack = malloc(16384);
pid = clone(child, stack+16384, CLONE_NEWNS|SIGCHLD, NULL);

open("/dev/ptmx", O_RDWR|O_NOCTTY|O_NONBLOCK);

unlockpt(fd); grantpt(fd);

sleep(2);
kill(pid, SIGHUP);
sleep(1);
return 0; /* exit before child opens /dev/console */
}

Reported-by: Grzegorz Nosek <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>
Tested-by: Serge Hallyn <[email protected]>
Cc: stable <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
fs/devpts/inode.c | 16 ++++++++++++++--
1 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index d5f8c96..8882ecc 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -517,11 +517,23 @@ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty)

struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number)
{
+ struct dentry *dentry;
+ struct tty_struct *tty;
+
BUG_ON(pts_inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR));

+ /* Ensure dentry has not been deleted by devpts_pty_kill() */
+ dentry = d_find_alias(pts_inode);
+ if (!dentry)
+ return NULL;
+
+ tty = NULL;
if (pts_inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)
- return (struct tty_struct *)pts_inode->i_private;
- return NULL;
+ tty = (struct tty_struct *)pts_inode->i_private;
+
+ dput(dentry);
+
+ return tty;
}

void devpts_pty_kill(struct tty_struct *tty)
--
1.6.5.5

2009-12-11 23:37:53

by Greg KH

[permalink] [raw]
Subject: [PATCH 25/58] tty_port: Move hupcl handling

From: Alan Cox <[email protected]>

Move the HUCPL handling from the end of close_port_start to the beginning
of close_port_end. What this actually does is change the ordering from

port shutdown
port->dtr_rts

to

port->dtr_rts
port shutdown

Some hardware drops the physical connection on shutdown so we must perform
the port operations before the shutdown.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/tty_port.c | 13 ++++++++-----
1 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
index 41b314c..dd471d6 100644
--- a/drivers/char/tty_port.c
+++ b/drivers/char/tty_port.c
@@ -339,6 +339,14 @@ int tty_port_close_start(struct tty_port *port,
timeout = 2 * HZ;
schedule_timeout_interruptible(timeout);
}
+ /* Flush the ldisc buffering */
+ tty_ldisc_flush(tty);
+
+ /* Drop DTR/RTS if HUPCL is set. This causes any attached modem to
+ hang up the line */
+ if (tty->termios->c_cflag & HUPCL)
+ tty_port_lower_dtr_rts(port);
+
/* Don't call port->drop for the last reference. Callers will want
to drop the last active reference in ->shutdown() or the tty
shutdown path */
@@ -350,11 +358,6 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
{
unsigned long flags;

- tty_ldisc_flush(tty);
-
- if (tty->termios->c_cflag & HUPCL)
- tty_port_lower_dtr_rts(port);
-
spin_lock_irqsave(&port->lock, flags);
tty->closing = 0;

--
1.6.5.5

2009-12-11 23:37:46

by Greg KH

[permalink] [raw]
Subject: [PATCH 26/58] sdio_uart: use tty_port

From: Alan Cox <[email protected]>

Add a tty_port object to the sdio uart. For the moment just begin using the
tty field of the port, as this is the critical one to clean up.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/mmc/card/sdio_uart.c | 41 +++++++++++++++++++++++------------------
1 files changed, 23 insertions(+), 18 deletions(-)

diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index b8e7c5a..c2759db 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -73,6 +73,7 @@ struct uart_icount {
};

struct sdio_uart_port {
+ struct tty_port port;
struct kref kref;
struct tty_struct *tty;
unsigned int index;
@@ -172,7 +173,7 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port)
port->func = NULL;
mutex_unlock(&port->func_lock);
if (port->opened)
- tty_hangup(port->tty);
+ tty_hangup(port->port.tty);
mutex_unlock(&port->open_lock);
sdio_release_irq(func);
sdio_disable_func(func);
@@ -391,7 +392,7 @@ static void sdio_uart_stop_rx(struct sdio_uart_port *port)
static void sdio_uart_receive_chars(struct sdio_uart_port *port,
unsigned int *status)
{
- struct tty_struct *tty = port->tty;
+ struct tty_struct *tty = port->port.tty;
unsigned int ch, flag;
int max_count = 256;

@@ -446,6 +447,7 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
{
struct circ_buf *xmit = &port->xmit;
int count;
+ struct tty_struct *tty = port->port.tty;

if (port->x_char) {
sdio_out(port, UART_TX, port->x_char);
@@ -453,7 +455,7 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
port->x_char = 0;
return;
}
- if (circ_empty(xmit) || port->tty->stopped || port->tty->hw_stopped) {
+ if (circ_empty(xmit) || tty->stopped || tty->hw_stopped) {
sdio_uart_stop_tx(port);
return;
}
@@ -468,7 +470,7 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
} while (--count > 0);

if (circ_chars_pending(xmit) < WAKEUP_CHARS)
- tty_wakeup(port->tty);
+ tty_wakeup(tty);

if (circ_empty(xmit))
sdio_uart_stop_tx(port);
@@ -477,6 +479,7 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
{
int status;
+ struct tty_struct *tty = port->port.tty;

status = sdio_in(port, UART_MSR);

@@ -491,17 +494,17 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
port->icount.dcd++;
if (status & UART_MSR_DCTS) {
port->icount.cts++;
- if (port->tty->termios->c_cflag & CRTSCTS) {
+ if (tty->termios->c_cflag & CRTSCTS) {
int cts = (status & UART_MSR_CTS);
- if (port->tty->hw_stopped) {
+ if (tty->hw_stopped) {
if (cts) {
- port->tty->hw_stopped = 0;
+ tty->hw_stopped = 0;
sdio_uart_start_tx(port);
- tty_wakeup(port->tty);
+ tty_wakeup(tty);
}
} else {
if (!cts) {
- port->tty->hw_stopped = 1;
+ tty->hw_stopped = 1;
sdio_uart_stop_tx(port);
}
}
@@ -546,12 +549,13 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
{
unsigned long page;
int ret;
+ struct tty_struct *tty = port->port.tty;

/*
* Set the TTY IO error marker - we will only clear this
* once we have successfully opened the port.
*/
- set_bit(TTY_IO_ERROR, &port->tty->flags);
+ set_bit(TTY_IO_ERROR, &tty->flags);

/* Initialise and allocate the transmit buffer. */
page = __get_free_page(GFP_KERNEL);
@@ -595,14 +599,14 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
port->mctrl = TIOCM_OUT2;

- sdio_uart_change_speed(port, port->tty->termios, NULL);
+ sdio_uart_change_speed(port, tty->termios, NULL);

- if (port->tty->termios->c_cflag & CBAUD)
+ if (tty->termios->c_cflag & CBAUD)
sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);

- if (port->tty->termios->c_cflag & CRTSCTS)
+ if (tty->termios->c_cflag & CRTSCTS)
if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS))
- port->tty->hw_stopped = 1;
+ tty->hw_stopped = 1;

clear_bit(TTY_IO_ERROR, &port->tty->flags);

@@ -634,7 +638,7 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port)
/* TODO: wait here for TX FIFO to drain */

/* Turn off DTR and RTS early. */
- if (port->tty->termios->c_cflag & HUPCL)
+ if (port->port.tty->termios->c_cflag & HUPCL)
sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);

/* Disable interrupts from this port */
@@ -684,11 +688,11 @@ static int sdio_uart_open(struct tty_struct *tty, struct file *filp)

if (!port->opened) {
tty->driver_data = port;
- port->tty = tty;
+ port->port.tty = tty;
ret = sdio_uart_startup(port);
if (ret) {
tty->driver_data = NULL;
- port->tty = NULL;
+ port->port.tty = NULL;
mutex_unlock(&port->open_lock);
sdio_uart_port_put(port);
return ret;
@@ -723,7 +727,7 @@ static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
tty->closing = 1;
sdio_uart_shutdown(port);
tty_ldisc_flush(tty);
- port->tty = NULL;
+ port->port.tty = NULL;
tty->driver_data = NULL;
tty->closing = 0;
}
@@ -1068,6 +1072,7 @@ static int sdio_uart_probe(struct sdio_func *func,

port->func = func;
sdio_set_drvdata(func, port);
+ tty_port_init(&port->port);

ret = sdio_uart_add_port(port);
if (ret) {
--
1.6.5.5

2009-12-11 23:37:41

by Greg KH

[permalink] [raw]
Subject: [PATCH 27/58] sdio_uart: Fix oops caused by the previous changeset

From: Nicolas Pitre <[email protected]>

Now... testing reveals that the very first patch "sdio_uart: use
tty_port" causes a segmentation fault in sdio_uart_open():

Unable to handle kernel NULL pointer dereference at virtual address 00000084
pgd = dfb44000 [00000084] *pgd=1fb99031, *pte=00000000, *ppte=00000000
Internal error: Oops: 17 [#1] PREEMPT
last sysfs file:
/sys/devices/platform/mvsdio/mmc_host/mmc0/mmc0:f111/uevent
Modules linked in:
CPU: 0 Not tainted (2.6.32-rc5-next-20091102-00001-gb36eae9 #10)
PC is at sdio_uart_open+0x204/0x2cc
[...]

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/mmc/card/sdio_uart.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index c2759db..671fe5e 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -608,7 +608,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS))
tty->hw_stopped = 1;

- clear_bit(TTY_IO_ERROR, &port->tty->flags);
+ clear_bit(TTY_IO_ERROR, &tty->flags);

/* Kick the IRQ handler once while we're still holding the host lock */
sdio_uart_irq(port->func);
--
1.6.5.5

2009-12-11 23:37:27

by Greg KH

[permalink] [raw]
Subject: [PATCH 28/58] sdio_uart: refcount the tty objects

From: Alan Cox <[email protected]>

The tty can go away underneath us, so we must refcount it. Do the naïve
implementation initially. We will worry about startup shortly.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/mmc/card/sdio_uart.c | 59 +++++++++++++++++++++++++++++------------
1 files changed, 42 insertions(+), 17 deletions(-)

diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 671fe5e..86ad543 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -172,8 +172,13 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port)
sdio_claim_host(func);
port->func = NULL;
mutex_unlock(&port->func_lock);
- if (port->opened)
- tty_hangup(port->port.tty);
+ if (port->opened) {
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
+ /* tty_hangup is async so is this safe as is ?? */
+ if (tty)
+ tty_hangup(tty);
+ tty_kref_put(tty);
+ }
mutex_unlock(&port->open_lock);
sdio_release_irq(func);
sdio_disable_func(func);
@@ -392,7 +397,7 @@ static void sdio_uart_stop_rx(struct sdio_uart_port *port)
static void sdio_uart_receive_chars(struct sdio_uart_port *port,
unsigned int *status)
{
- struct tty_struct *tty = port->port.tty;
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
unsigned int ch, flag;
int max_count = 256;

@@ -429,25 +434,30 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port,
}

if ((*status & port->ignore_status_mask & ~UART_LSR_OE) == 0)
- tty_insert_flip_char(tty, ch, flag);
+ if (tty)
+ tty_insert_flip_char(tty, ch, flag);

/*
* Overrun is special. Since it's reported immediately,
* it doesn't affect the current character.
*/
if (*status & ~port->ignore_status_mask & UART_LSR_OE)
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ if (tty)
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);

*status = sdio_in(port, UART_LSR);
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
- tty_flip_buffer_push(tty);
+ if (tty) {
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+ }
}

static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
{
struct circ_buf *xmit = &port->xmit;
int count;
- struct tty_struct *tty = port->port.tty;
+ struct tty_struct *tty;

if (port->x_char) {
sdio_out(port, UART_TX, port->x_char);
@@ -455,8 +465,12 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
port->x_char = 0;
return;
}
- if (circ_empty(xmit) || tty->stopped || tty->hw_stopped) {
+
+ tty = tty_port_tty_get(&port->port);
+
+ if (tty == NULL || circ_empty(xmit) || tty->stopped || tty->hw_stopped) {
sdio_uart_stop_tx(port);
+ tty_kref_put(tty);
return;
}

@@ -474,12 +488,13 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port)

if (circ_empty(xmit))
sdio_uart_stop_tx(port);
+ tty_kref_put(tty);
}

static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
{
int status;
- struct tty_struct *tty = port->port.tty;
+ struct tty_struct *tty;

status = sdio_in(port, UART_MSR);

@@ -494,7 +509,8 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
port->icount.dcd++;
if (status & UART_MSR_DCTS) {
port->icount.cts++;
- if (tty->termios->c_cflag & CRTSCTS) {
+ tty = tty_port_tty_get(&port->port);
+ if (tty && (tty->termios->c_cflag & CRTSCTS)) {
int cts = (status & UART_MSR_CTS);
if (tty->hw_stopped) {
if (cts) {
@@ -509,6 +525,7 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
}
}
}
+ tty_kref_put(tty);
}
}

@@ -548,8 +565,10 @@ static void sdio_uart_irq(struct sdio_func *func)
static int sdio_uart_startup(struct sdio_uart_port *port)
{
unsigned long page;
- int ret;
- struct tty_struct *tty = port->port.tty;
+ int ret = -ENOMEM;
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
+
+ /* FIXME: What if it is NULL ?? */

/*
* Set the TTY IO error marker - we will only clear this
@@ -560,7 +579,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
/* Initialise and allocate the transmit buffer. */
page = __get_free_page(GFP_KERNEL);
if (!page)
- return -ENOMEM;
+ goto err0;
port->xmit.buf = (unsigned char *)page;
circ_clear(&port->xmit);

@@ -614,6 +633,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
sdio_uart_irq(port->func);

sdio_uart_release_func(port);
+ tty_kref_put(tty);
return 0;

err3:
@@ -622,12 +642,15 @@ err2:
sdio_uart_release_func(port);
err1:
free_page((unsigned long)port->xmit.buf);
+err0:
+ tty_kref_put(tty);
return ret;
}

static void sdio_uart_shutdown(struct sdio_uart_port *port)
{
int ret;
+ struct tty_struct *tty;

ret = sdio_uart_claim_func(port);
if (ret)
@@ -637,9 +660,11 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port)

/* TODO: wait here for TX FIFO to drain */

+ tty = tty_port_tty_get(&port->port);
/* Turn off DTR and RTS early. */
- if (port->port.tty->termios->c_cflag & HUPCL)
+ if (tty && (tty->termios->c_cflag & HUPCL))
sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+ tty_kref_put(tty);

/* Disable interrupts from this port */
sdio_release_irq(port->func);
@@ -688,11 +713,11 @@ static int sdio_uart_open(struct tty_struct *tty, struct file *filp)

if (!port->opened) {
tty->driver_data = port;
- port->port.tty = tty;
+ tty_port_tty_set(&port->port, tty);
ret = sdio_uart_startup(port);
if (ret) {
tty->driver_data = NULL;
- port->port.tty = NULL;
+ tty_port_tty_set(&port->port, NULL);
mutex_unlock(&port->open_lock);
sdio_uart_port_put(port);
return ret;
@@ -727,7 +752,7 @@ static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
tty->closing = 1;
sdio_uart_shutdown(port);
tty_ldisc_flush(tty);
- port->port.tty = NULL;
+ tty_port_tty_set(&port->port, NULL);
tty->driver_data = NULL;
tty->closing = 0;
}
--
1.6.5.5

2009-12-11 23:37:21

by Greg KH

[permalink] [raw]
Subject: [PATCH 29/58] sdio_uart: Move the open lock

From: Alan Cox <[email protected]>

When we move to the tty_port logic the port mutex will protect open v close
v hangup. Move to this first in the existing open code so we have a bisection
point.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/mmc/card/sdio_uart.c | 20 +++++++++-----------
1 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 86ad543..31f7023 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -78,7 +78,6 @@ struct sdio_uart_port {
struct tty_struct *tty;
unsigned int index;
unsigned int opened;
- struct mutex open_lock;
struct sdio_func *func;
struct mutex func_lock;
struct task_struct *in_sdio_uart_irq;
@@ -103,7 +102,6 @@ static int sdio_uart_add_port(struct sdio_uart_port *port)
int index, ret = -EBUSY;

kref_init(&port->kref);
- mutex_init(&port->open_lock);
mutex_init(&port->func_lock);
spin_lock_init(&port->write_lock);

@@ -166,7 +164,7 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port)
* give up on that port ASAP.
* Beware: the lock ordering is critical.
*/
- mutex_lock(&port->open_lock);
+ mutex_lock(&port->port.mutex);
mutex_lock(&port->func_lock);
func = port->func;
sdio_claim_host(func);
@@ -179,7 +177,7 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port)
tty_hangup(tty);
tty_kref_put(tty);
}
- mutex_unlock(&port->open_lock);
+ mutex_unlock(&port->port.mutex);
sdio_release_irq(func);
sdio_disable_func(func);
sdio_release_host(func);
@@ -699,14 +697,14 @@ static int sdio_uart_open(struct tty_struct *tty, struct file *filp)
if (!port)
return -ENODEV;

- mutex_lock(&port->open_lock);
+ mutex_lock(&port->port.mutex);

/*
* Make sure not to mess up with a dead port
* which has not been closed yet.
*/
if (tty->driver_data && tty->driver_data != port) {
- mutex_unlock(&port->open_lock);
+ mutex_unlock(&port->port.mutex);
sdio_uart_port_put(port);
return -EBUSY;
}
@@ -718,13 +716,13 @@ static int sdio_uart_open(struct tty_struct *tty, struct file *filp)
if (ret) {
tty->driver_data = NULL;
tty_port_tty_set(&port->port, NULL);
- mutex_unlock(&port->open_lock);
+ mutex_unlock(&port->port.mutex);
sdio_uart_port_put(port);
return ret;
}
}
port->opened++;
- mutex_unlock(&port->open_lock);
+ mutex_unlock(&port->port.mutex);
return 0;
}

@@ -735,7 +733,7 @@ static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
if (!port)
return;

- mutex_lock(&port->open_lock);
+ mutex_lock(&port->port.mutex);
BUG_ON(!port->opened);

/*
@@ -744,7 +742,7 @@ static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
* is larger than port->count.
*/
if (tty->count > port->opened) {
- mutex_unlock(&port->open_lock);
+ mutex_unlock(&port->port.mutex);
return;
}

@@ -756,7 +754,7 @@ static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
tty->driver_data = NULL;
tty->closing = 0;
}
- mutex_unlock(&port->open_lock);
+ mutex_unlock(&port->port.mutex);
sdio_uart_port_put(port);
}

--
1.6.5.5

2009-12-11 23:37:01

by Greg KH

[permalink] [raw]
Subject: [PATCH 30/58] tty: sdio_uart: Switch to the open/close helpers

From: Alan Cox <[email protected]>

Gets us proper tty semantics, removes some code and fixes up a few corner
case races (hangup during open etc)

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/mmc/card/sdio_uart.c | 201 +++++++++++++++++++++++++-----------------
1 files changed, 119 insertions(+), 82 deletions(-)

diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 31f7023..95027b0 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -77,7 +77,6 @@ struct sdio_uart_port {
struct kref kref;
struct tty_struct *tty;
unsigned int index;
- unsigned int opened;
struct sdio_func *func;
struct mutex func_lock;
struct task_struct *in_sdio_uart_irq;
@@ -150,6 +149,7 @@ static void sdio_uart_port_put(struct sdio_uart_port *port)
static void sdio_uart_port_remove(struct sdio_uart_port *port)
{
struct sdio_func *func;
+ struct tty_struct *tty;

BUG_ON(sdio_uart_table[port->index] != port);

@@ -170,11 +170,10 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port)
sdio_claim_host(func);
port->func = NULL;
mutex_unlock(&port->func_lock);
- if (port->opened) {
- struct tty_struct *tty = tty_port_tty_get(&port->port);
- /* tty_hangup is async so is this safe as is ?? */
- if (tty)
- tty_hangup(tty);
+ tty = tty_port_tty_get(&port->port);
+ /* tty_hangup is async so is this safe as is ?? */
+ if (tty) {
+ tty_hangup(tty);
tty_kref_put(tty);
}
mutex_unlock(&port->port.mutex);
@@ -560,13 +559,46 @@ static void sdio_uart_irq(struct sdio_func *func)
port->in_sdio_uart_irq = NULL;
}

-static int sdio_uart_startup(struct sdio_uart_port *port)
+/**
+ * uart_dtr_rts - port helper to set uart signals
+ * @tport: tty port to be updated
+ * @onoff: set to turn on DTR/RTS
+ *
+ * Called by the tty port helpers when the modem signals need to be
+ * adjusted during an open, close and hangup.
+ */
+
+static void uart_dtr_rts(struct tty_port *tport, int onoff)
{
- unsigned long page;
- int ret = -ENOMEM;
- struct tty_struct *tty = tty_port_tty_get(&port->port);
+ struct sdio_uart_port *port =
+ container_of(tport, struct sdio_uart_port, port);
+ if (onoff == 0)
+ sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+ else
+ sdio_uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+}

- /* FIXME: What if it is NULL ?? */
+/**
+ * sdio_uart_activate - start up hardware
+ * @tport: tty port to activate
+ * @tty: tty bound to this port
+ *
+ * Activate a tty port. The port locking guarantees us this will be
+ * run exactly once per set of opens, and if successful will see the
+ * shutdown method run exactly once to match. Start up and shutdown are
+ * protected from each other by the internal locking and will not run
+ * at the same time even during a hangup event.
+ *
+ * If we successfully start up the port we take an extra kref as we
+ * will keep it around until shutdown when the kref is dropped.
+ */
+
+static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty)
+{
+ struct sdio_uart_port *port =
+ container_of(tport, struct sdio_uart_port, port);
+ unsigned long page;
+ int ret;

/*
* Set the TTY IO error marker - we will only clear this
@@ -577,7 +609,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
/* Initialise and allocate the transmit buffer. */
page = __get_free_page(GFP_KERNEL);
if (!page)
- goto err0;
+ return -ENOMEM;
port->xmit.buf = (unsigned char *)page;
circ_clear(&port->xmit);

@@ -631,7 +663,6 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
sdio_uart_irq(port->func);

sdio_uart_release_func(port);
- tty_kref_put(tty);
return 0;

err3:
@@ -640,15 +671,25 @@ err2:
sdio_uart_release_func(port);
err1:
free_page((unsigned long)port->xmit.buf);
-err0:
- tty_kref_put(tty);
return ret;
}

-static void sdio_uart_shutdown(struct sdio_uart_port *port)
+
+/**
+ * sdio_uart_shutdown - stop hardware
+ * @tport: tty port to shut down
+ *
+ * Deactivate a tty port. The port locking guarantees us this will be
+ * run only if a successful matching activate already ran. The two are
+ * protected from each other by the internal locking and will not run
+ * at the same time even during a hangup event.
+ */
+
+static void sdio_uart_shutdown(struct tty_port *tport)
{
+ struct sdio_uart_port *port =
+ container_of(tport, struct sdio_uart_port, port);
int ret;
- struct tty_struct *tty;

ret = sdio_uart_claim_func(port);
if (ret)
@@ -656,14 +697,6 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port)

sdio_uart_stop_rx(port);

- /* TODO: wait here for TX FIFO to drain */
-
- tty = tty_port_tty_get(&port->port);
- /* Turn off DTR and RTS early. */
- if (tty && (tty->termios->c_cflag & HUPCL))
- sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
- tty_kref_put(tty);
-
/* Disable interrupts from this port */
sdio_release_irq(port->func);
port->ier = 0;
@@ -688,74 +721,68 @@ skip:
free_page((unsigned long)port->xmit.buf);
}

-static int sdio_uart_open(struct tty_struct *tty, struct file *filp)
-{
- struct sdio_uart_port *port;
- int ret;
-
- port = sdio_uart_port_get(tty->index);
- if (!port)
- return -ENODEV;
-
- mutex_lock(&port->port.mutex);
+/**
+ * sdio_uart_install - install method
+ * @driver: the driver in use (sdio_uart in our case)
+ * @tty: the tty being bound
+ *
+ * Look up and bind the tty and the driver together. Initialize
+ * any needed private data (in our case the termios)
+ */

- /*
- * Make sure not to mess up with a dead port
- * which has not been closed yet.
- */
- if (tty->driver_data && tty->driver_data != port) {
- mutex_unlock(&port->port.mutex);
+static int sdio_uart_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ int idx = tty->index;
+ struct sdio_uart_port *port = sdio_uart_port_get(idx);
+ int ret = tty_init_termios(tty);
+
+ if (ret == 0) {
+ tty_driver_kref_get(driver);
+ tty->count++;
+ /* This is the ref sdio_uart_port get provided */
+ tty->driver_data = port;
+ driver->ttys[idx] = tty;
+ } else
sdio_uart_port_put(port);
- return -EBUSY;
- }
+ return ret;

- if (!port->opened) {
- tty->driver_data = port;
- tty_port_tty_set(&port->port, tty);
- ret = sdio_uart_startup(port);
- if (ret) {
- tty->driver_data = NULL;
- tty_port_tty_set(&port->port, NULL);
- mutex_unlock(&port->port.mutex);
- sdio_uart_port_put(port);
- return ret;
- }
- }
- port->opened++;
- mutex_unlock(&port->port.mutex);
- return 0;
}

-static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
+/**
+ * sdio_uart_cleanup - called on the last tty kref drop
+ * @tty: the tty being destroyed
+ *
+ * Called asynchronously when the last reference to the tty is dropped.
+ * We cannot destroy the tty->driver_data port kref until this point
+ */
+
+static void sdio_uart_cleanup(struct tty_struct *tty)
{
struct sdio_uart_port *port = tty->driver_data;
+ tty->driver_data = NULL; /* Bug trap */
+ sdio_uart_port_put(port);
+}

- if (!port)
- return;
+/*
+ * Open/close/hangup is now entirely boilerplate
+ */

- mutex_lock(&port->port.mutex);
- BUG_ON(!port->opened);
+static int sdio_uart_open(struct tty_struct *tty, struct file *filp)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+ return tty_port_open(&port->port, tty, filp);
+}

- /*
- * This is messy. The tty layer calls us even when open()
- * returned an error. Ignore this close request if tty->count
- * is larger than port->count.
- */
- if (tty->count > port->opened) {
- mutex_unlock(&port->port.mutex);
- return;
- }
+static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+ tty_port_close(&port->port, tty, filp);
+}

- if (--port->opened == 0) {
- tty->closing = 1;
- sdio_uart_shutdown(port);
- tty_ldisc_flush(tty);
- tty_port_tty_set(&port->port, NULL);
- tty->driver_data = NULL;
- tty->closing = 0;
- }
- mutex_unlock(&port->port.mutex);
- sdio_uart_port_put(port);
+static void sdio_uart_hangup(struct tty_struct *tty)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+ tty_port_hangup(&port->port);
}

static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,
@@ -1021,6 +1048,12 @@ static const struct file_operations sdio_uart_proc_fops = {
.release = single_release,
};

+static const struct tty_port_operations sdio_uart_port_ops = {
+ .dtr_rts = uart_dtr_rts,
+ .shutdown = sdio_uart_shutdown,
+ .activate = sdio_uart_activate,
+};
+
static const struct tty_operations sdio_uart_ops = {
.open = sdio_uart_open,
.close = sdio_uart_close,
@@ -1031,9 +1064,12 @@ static const struct tty_operations sdio_uart_ops = {
.throttle = sdio_uart_throttle,
.unthrottle = sdio_uart_unthrottle,
.set_termios = sdio_uart_set_termios,
+ .hangup = sdio_uart_hangup,
.break_ctl = sdio_uart_break_ctl,
.tiocmget = sdio_uart_tiocmget,
.tiocmset = sdio_uart_tiocmset,
+ .install = sdio_uart_install,
+ .cleanup = sdio_uart_cleanup,
.proc_fops = &sdio_uart_proc_fops,
};

@@ -1096,6 +1132,7 @@ static int sdio_uart_probe(struct sdio_func *func,
port->func = func;
sdio_set_drvdata(func, port);
tty_port_init(&port->port);
+ port->port.ops = &sdio_uart_port_ops;

ret = sdio_uart_add_port(port);
if (ret) {
--
1.6.5.5

2009-12-11 23:36:51

by Greg KH

[permalink] [raw]
Subject: [PATCH 31/58] tty: sdio_uart: Fix termios handling

From: Alan Cox <[email protected]>

Switching between two non standard baud rates fails because of the cflag
test. Do as we did elsewhere and just kill the "optimisation".

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/mmc/card/sdio_uart.c | 6 ------
1 files changed, 0 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 95027b0..2104397 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -902,12 +902,6 @@ static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_t
struct sdio_uart_port *port = tty->driver_data;
unsigned int cflag = tty->termios->c_cflag;

-#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
-
- if ((cflag ^ old_termios->c_cflag) == 0 &&
- RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
- return;
-
if (sdio_uart_claim_func(port) != 0)
return;

--
1.6.5.5

2009-12-11 23:36:32

by Greg KH

[permalink] [raw]
Subject: [PATCH 32/58] tty: sdio_uart: Style fixes

From: Alan Cox <[email protected]>

Running the current code through checkpatch shows a few bits of noise
mostly but not entirely from before the changes.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/mmc/card/sdio_uart.c | 23 ++++++++++++-----------
1 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 2104397..b999972 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -465,7 +465,8 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port)

tty = tty_port_tty_get(&port->port);

- if (tty == NULL || circ_empty(xmit) || tty->stopped || tty->hw_stopped) {
+ if (tty == NULL || circ_empty(xmit) ||
+ tty->stopped || tty->hw_stopped) {
sdio_uart_stop_tx(port);
tty_kref_put(tty);
return;
@@ -645,7 +646,7 @@ static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty)
*/
sdio_out(port, UART_LCR, UART_LCR_WLEN8);

- port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
+ port->ier = UART_IER_RLSI|UART_IER_RDI|UART_IER_RTOIE|UART_IER_UUE;
port->mctrl = TIOCM_OUT2;

sdio_uart_change_speed(port, tty->termios, NULL);
@@ -674,7 +675,6 @@ err1:
return ret;
}

-
/**
* sdio_uart_shutdown - stop hardware
* @tport: tty port to shut down
@@ -745,7 +745,6 @@ static int sdio_uart_install(struct tty_driver *driver, struct tty_struct *tty)
} else
sdio_uart_port_put(port);
return ret;
-
}

/**
@@ -785,7 +784,7 @@ static void sdio_uart_hangup(struct tty_struct *tty)
tty_port_hangup(&port->port);
}

-static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,
+static int sdio_uart_write(struct tty_struct *tty, const unsigned char *buf,
int count)
{
struct sdio_uart_port *port = tty->driver_data;
@@ -810,7 +809,7 @@ static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,
}
spin_unlock(&port->write_lock);

- if ( !(port->ier & UART_IER_THRI)) {
+ if (!(port->ier & UART_IER_THRI)) {
int err = sdio_uart_claim_func(port);
if (!err) {
sdio_uart_start_tx(port);
@@ -897,7 +896,8 @@ static void sdio_uart_unthrottle(struct tty_struct *tty)
sdio_uart_release_func(port);
}

-static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+static void sdio_uart_set_termios(struct tty_struct *tty,
+ struct ktermios *old_termios)
{
struct sdio_uart_port *port = tty->driver_data;
unsigned int cflag = tty->termios->c_cflag;
@@ -976,7 +976,7 @@ static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file,
int result;

result = sdio_uart_claim_func(port);
- if(!result) {
+ if (!result) {
sdio_uart_update_mctrl(port, set, clear);
sdio_uart_release_func(port);
}
@@ -994,7 +994,7 @@ static int sdio_uart_proc_show(struct seq_file *m, void *v)
struct sdio_uart_port *port = sdio_uart_port_get(i);
if (port) {
seq_printf(m, "%d: uart:SDIO", i);
- if(capable(CAP_SYS_ADMIN)) {
+ if (capable(CAP_SYS_ADMIN)) {
seq_printf(m, " tx:%d rx:%d",
port->icount.tx, port->icount.rx);
if (port->icount.frame)
@@ -1100,7 +1100,7 @@ static int sdio_uart_probe(struct sdio_func *func,
}
if (!tpl) {
printk(KERN_WARNING
- "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
+ "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
sdio_func_id(func));
kfree(port);
return -EINVAL;
@@ -1133,7 +1133,8 @@ static int sdio_uart_probe(struct sdio_func *func,
kfree(port);
} else {
struct device *dev;
- dev = tty_register_device(sdio_uart_tty_driver, port->index, &func->dev);
+ dev = tty_register_device(sdio_uart_tty_driver,
+ port->index, &func->dev);
if (IS_ERR(dev)) {
sdio_uart_port_remove(port);
ret = PTR_ERR(dev);
--
1.6.5.5

2009-12-11 23:37:07

by Greg KH

[permalink] [raw]
Subject: [PATCH 33/58] tty: sdio_uart: add modem functionality

From: Alan Cox <[email protected]>

Add the POSIX block for carrier

Linux TIOCMIWAIT functionality is still lacking from the driver.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/mmc/card/sdio_uart.c | 33 ++++++++++++++++++++++++++++++++-
1 files changed, 32 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index b999972..2a13db5 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/serial_reg.h>
@@ -86,6 +87,7 @@ struct sdio_uart_port {
struct uart_icount icount;
unsigned int uartclk;
unsigned int mctrl;
+ unsigned int rx_mctrl;
unsigned int read_status_mask;
unsigned int ignore_status_mask;
unsigned char x_char;
@@ -220,6 +222,8 @@ static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port)
unsigned char status;
unsigned int ret;

+ /* FIXME: What stops this losing the delta bits and breaking
+ sdio_uart_check_modem_status ? */
status = sdio_in(port, UART_MSR);

ret = 0;
@@ -503,8 +507,20 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
port->icount.rng++;
if (status & UART_MSR_DDSR)
port->icount.dsr++;
- if (status & UART_MSR_DDCD)
+ if (status & UART_MSR_DDCD) {
port->icount.dcd++;
+ /* DCD raise - wake for open */
+ if (status & UART_MSR_DCD)
+ wake_up_interruptible(&port->port.open_wait);
+ else {
+ /* DCD drop - hang up if tty attached */
+ tty = tty_port_tty_get(&port->port);
+ if (tty) {
+ tty_hangup(tty);
+ tty_kref_put(tty);
+ }
+ }
+ }
if (status & UART_MSR_DCTS) {
port->icount.cts++;
tty = tty_port_tty_get(&port->port);
@@ -560,6 +576,20 @@ static void sdio_uart_irq(struct sdio_func *func)
port->in_sdio_uart_irq = NULL;
}

+static int uart_carrier_raised(struct tty_port *tport)
+{
+ struct sdio_uart_port *port =
+ container_of(tport, struct sdio_uart_port, port);
+ unsigned int ret = sdio_uart_claim_func(port);
+ if (ret) /* Missing hardware shoudn't block for carrier */
+ return 1;
+ ret = sdio_uart_get_mctrl(port);
+ sdio_uart_release_func(port);
+ if (ret & TIOCM_CAR)
+ return 1;
+ return 0;
+}
+
/**
* uart_dtr_rts - port helper to set uart signals
* @tport: tty port to be updated
@@ -1044,6 +1074,7 @@ static const struct file_operations sdio_uart_proc_fops = {

static const struct tty_port_operations sdio_uart_port_ops = {
.dtr_rts = uart_dtr_rts,
+ .carrier_raised = uart_carrier_raised,
.shutdown = sdio_uart_shutdown,
.activate = sdio_uart_activate,
};
--
1.6.5.5

2009-12-11 23:36:13

by Greg KH

[permalink] [raw]
Subject: [PATCH 34/58] tty: sdio_uart: Fix the locking on "func" for new code

From: Alan Cox <[email protected]>

The new dtr_rts function didn't take the port->func lock as it should
so add use of the lock there.


Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/mmc/card/sdio_uart.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 2a13db5..f537555 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -603,10 +603,14 @@ static void uart_dtr_rts(struct tty_port *tport, int onoff)
{
struct sdio_uart_port *port =
container_of(tport, struct sdio_uart_port, port);
+ int ret = sdio_uart_claim_func(port);
+ if (ret)
+ return;
if (onoff == 0)
sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
else
sdio_uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+ sdio_uart_release_func(port);
}

/**
--
1.6.5.5

2009-12-11 23:35:47

by Greg KH

[permalink] [raw]
Subject: [PATCH 35/58] tty: tty_port: Change the buffer allocator locking

From: Alan Cox <[email protected]>

We want to be able to do this without regard for the activate/own open
method being used which causes a problem using port->mutex. Add another
mutex for now. Once everything uses port_open to do buffer allocs we can
kill it back off

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/tty_port.c | 9 +++++----
include/linux/tty.h | 1 +
2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
index dd471d6..3ef644a 100644
--- a/drivers/char/tty_port.c
+++ b/drivers/char/tty_port.c
@@ -25,6 +25,7 @@ void tty_port_init(struct tty_port *port)
init_waitqueue_head(&port->close_wait);
init_waitqueue_head(&port->delta_msr_wait);
mutex_init(&port->mutex);
+ mutex_init(&port->buf_mutex);
spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100;
@@ -34,10 +35,10 @@ EXPORT_SYMBOL(tty_port_init);
int tty_port_alloc_xmit_buf(struct tty_port *port)
{
/* We may sleep in get_zeroed_page() */
- mutex_lock(&port->mutex);
+ mutex_lock(&port->buf_mutex);
if (port->xmit_buf == NULL)
port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
- mutex_unlock(&port->mutex);
+ mutex_unlock(&port->buf_mutex);
if (port->xmit_buf == NULL)
return -ENOMEM;
return 0;
@@ -46,12 +47,12 @@ EXPORT_SYMBOL(tty_port_alloc_xmit_buf);

void tty_port_free_xmit_buf(struct tty_port *port)
{
- mutex_lock(&port->mutex);
+ mutex_lock(&port->buf_mutex);
if (port->xmit_buf != NULL) {
free_page((unsigned long)port->xmit_buf);
port->xmit_buf = NULL;
}
- mutex_unlock(&port->mutex);
+ mutex_unlock(&port->buf_mutex);
}
EXPORT_SYMBOL(tty_port_free_xmit_buf);

diff --git a/include/linux/tty.h b/include/linux/tty.h
index 6352ac2..e9269ca 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -212,6 +212,7 @@ struct tty_port {
wait_queue_head_t delta_msr_wait; /* Modem status change */
unsigned long flags; /* TTY flags ASY_*/
struct mutex mutex; /* Locking */
+ struct mutex buf_mutex; /* Buffer alloc lock */
unsigned char *xmit_buf; /* Optional buffer */
unsigned int close_delay; /* Close port delay */
unsigned int closing_wait; /* Delay for output */
--
1.6.5.5

2009-12-11 23:35:42

by Greg KH

[permalink] [raw]
Subject: [PATCH 36/58] tty: riscom8: switch to the tty_port_open API

From: Alan Cox <[email protected]>

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/riscom8.c | 89 ++++++++++++++++++++++--------------------------
1 files changed, 41 insertions(+), 48 deletions(-)

diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c
index 3cfa22d..0a8d1e5 100644
--- a/drivers/char/riscom8.c
+++ b/drivers/char/riscom8.c
@@ -793,26 +793,21 @@ static void rc_change_speed(struct tty_struct *tty, struct riscom_board *bp,
}

/* Must be called with interrupts enabled */
-static int rc_setup_port(struct tty_struct *tty, struct riscom_board *bp,
- struct riscom_port *port)
+static int rc_activate_port(struct tty_port *port, struct tty_struct *tty)
{
+ struct riscom_port *rp = container_of(port, struct riscom_port, port);
+ struct riscom_board *bp = port_Board(rp);
unsigned long flags;

- if (port->port.flags & ASYNC_INITIALIZED)
- return 0;
-
- if (tty_port_alloc_xmit_buf(&port->port) < 0)
+ if (tty_port_alloc_xmit_buf(port) < 0)
return -ENOMEM;

spin_lock_irqsave(&riscom_lock, flags);

clear_bit(TTY_IO_ERROR, &tty->flags);
- if (port->port.count == 1)
- bp->count++;
- port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
- rc_change_speed(tty, bp, port);
- port->port.flags |= ASYNC_INITIALIZED;
-
+ bp->count++;
+ rp->xmit_cnt = rp->xmit_head = rp->xmit_tail = 0;
+ rc_change_speed(tty, bp, rp);
spin_unlock_irqrestore(&riscom_lock, flags);
return 0;
}
@@ -821,9 +816,6 @@ static int rc_setup_port(struct tty_struct *tty, struct riscom_board *bp,
static void rc_shutdown_port(struct tty_struct *tty,
struct riscom_board *bp, struct riscom_port *port)
{
- if (!(port->port.flags & ASYNC_INITIALIZED))
- return;
-
#ifdef RC_REPORT_OVERRUN
printk(KERN_INFO "rc%d: port %d: Total %ld overruns were detected.\n",
board_No(bp), port_No(port), port->overrun);
@@ -840,11 +832,6 @@ static void rc_shutdown_port(struct tty_struct *tty,
}
#endif
tty_port_free_xmit_buf(&port->port);
- if (C_HUPCL(tty)) {
- /* Drop DTR */
- bp->DTR |= (1u << port_No(port));
- rc_out(bp, RC_DTR, bp->DTR);
- }

/* Select port */
rc_out(bp, CD180_CAR, port_No(port));
@@ -856,7 +843,6 @@ static void rc_shutdown_port(struct tty_struct *tty,
rc_out(bp, CD180_IER, port->IER);

set_bit(TTY_IO_ERROR, &tty->flags);
- port->port.flags &= ~ASYNC_INITIALIZED;

if (--bp->count < 0) {
printk(KERN_INFO "rc%d: rc_shutdown_port: "
@@ -889,6 +875,20 @@ static int carrier_raised(struct tty_port *port)
return CD;
}

+static void dtr_rts(struct tty_port *port, int onoff)
+{
+ struct riscom_port *p = container_of(port, struct riscom_port, port);
+ struct riscom_board *bp = port_Board(p);
+ unsigned long flags;
+
+ spin_lock_irqsave(&riscom_lock, flags);
+ bp->DTR &= ~(1u << port_No(p));
+ if (onoff == 0)
+ bp->DTR |= (1u << port_No(p));
+ rc_out(bp, RC_DTR, bp->DTR);
+ spin_unlock_irqrestore(&riscom_lock, flags);
+}
+
static int rc_open(struct tty_struct *tty, struct file *filp)
{
int board;
@@ -909,14 +909,7 @@ static int rc_open(struct tty_struct *tty, struct file *filp)
if (error)
return error;

- port->port.count++;
- tty->driver_data = port;
- tty_port_tty_set(&port->port, tty);
-
- error = rc_setup_port(tty, bp, port);
- if (error == 0)
- error = tty_port_block_til_ready(&port->port, tty, filp);
- return error;
+ return tty_port_open(&port->port, tty, filp);
}

static void rc_flush_buffer(struct tty_struct *tty)
@@ -950,24 +943,23 @@ static void rc_close_port(struct tty_port *port)

spin_lock_irqsave(&riscom_lock, flags);
rp->IER &= ~IER_RXD;
- if (port->flags & ASYNC_INITIALIZED) {
- rp->IER &= ~IER_TXRDY;
- rp->IER |= IER_TXEMPTY;
- rc_out(bp, CD180_CAR, port_No(rp));
- rc_out(bp, CD180_IER, rp->IER);
- /*
- * Before we drop DTR, make sure the UART transmitter
- * has completely drained; this is especially
- * important if there is a transmit FIFO!
- */
- timeout = jiffies + HZ;
- while (rp->IER & IER_TXEMPTY) {
- spin_unlock_irqrestore(&riscom_lock, flags);
- msleep_interruptible(jiffies_to_msecs(rp->timeout));
- spin_lock_irqsave(&riscom_lock, flags);
- if (time_after(jiffies, timeout))
- break;
- }
+
+ rp->IER &= ~IER_TXRDY;
+ rp->IER |= IER_TXEMPTY;
+ rc_out(bp, CD180_CAR, port_No(rp));
+ rc_out(bp, CD180_IER, rp->IER);
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ timeout = jiffies + HZ;
+ while (rp->IER & IER_TXEMPTY) {
+ spin_unlock_irqrestore(&riscom_lock, flags);
+ msleep_interruptible(jiffies_to_msecs(rp->timeout));
+ spin_lock_irqsave(&riscom_lock, flags);
+ if (time_after(jiffies, timeout))
+ break;
}
rc_shutdown_port(port->tty, bp, rp);
spin_unlock_irqrestore(&riscom_lock, flags);
@@ -1354,7 +1346,6 @@ static void rc_hangup(struct tty_struct *tty)
if (rc_paranoia_check(port, tty->name, "rc_hangup"))
return;

- rc_shutdown_port(tty, port_Board(port), port);
tty_port_hangup(&port->port);
}

@@ -1401,7 +1392,9 @@ static const struct tty_operations riscom_ops = {

static const struct tty_port_operations riscom_port_ops = {
.carrier_raised = carrier_raised,
+ .dtr_rts = dtr_rts,
.shutdown = rc_close_port,
+ .activate = rc_activate_port,
};


--
1.6.5.5

2009-12-11 23:34:27

by Greg KH

[permalink] [raw]
Subject: [PATCH 37/58] tty: tty_port: Add IO_ERROR bit handling

From: Alan Cox <[email protected]>

To propogate tty_port_open/close to a few other devices we need to start
handling the IO_ERROR flag on the tty. We can do this pretty trivially.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/tty_port.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
index 3ef644a..43a1907 100644
--- a/drivers/char/tty_port.c
+++ b/drivers/char/tty_port.c
@@ -122,8 +122,10 @@ void tty_port_hangup(struct tty_port *port)
spin_lock_irqsave(&port->lock, flags);
port->count = 0;
port->flags &= ~ASYNC_NORMAL_ACTIVE;
- if (port->tty)
+ if (port->tty) {
+ set_bit(TTY_IO_ERROR, &port->tty->flags);
tty_kref_put(port->tty);
+ }
port->tty = NULL;
spin_unlock_irqrestore(&port->lock, flags);
wake_up_interruptible(&port->open_wait);
@@ -383,6 +385,7 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty,
if (tty_port_close_start(port, tty, filp) == 0)
return;
tty_port_shutdown(port);
+ set_bit(TTY_IO_ERROR, &tty->flags);
tty_port_close_end(port, tty);
tty_port_tty_set(port, NULL);
}
@@ -414,6 +417,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty,
}
}
set_bit(ASYNCB_INITIALIZED, &port->flags);
+ clear_bit(TTY_IO_ERROR, &tty->flags);
}
mutex_unlock(&port->mutex);
return tty_port_block_til_ready(port, tty, filp);
--
1.6.5.5

2009-12-11 23:34:21

by Greg KH

[permalink] [raw]
Subject: [PATCH 38/58] tty: tty_port: Move the IO_ERROR clear

From: Alan Cox <[email protected]>

Some devices want to set IO_ERROR in their activate methods so that you can
be handed a 'dead' port for operations like setserial. Thus we need to
clear the flag before activate so that activate can choose to set the flag
and still return 0.

This is fine as the file handle/tty are not accessible to the user yet.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/tty_port.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
index 43a1907..84006de 100644
--- a/drivers/char/tty_port.c
+++ b/drivers/char/tty_port.c
@@ -409,6 +409,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty,
mutex_lock(&port->mutex);

if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) {
+ clear_bit(TTY_IO_ERROR, &tty->flags);
if (port->ops->activate) {
int retval = port->ops->activate(port, tty);
if (retval) {
@@ -417,7 +418,6 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty,
}
}
set_bit(ASYNCB_INITIALIZED, &port->flags);
- clear_bit(TTY_IO_ERROR, &tty->flags);
}
mutex_unlock(&port->mutex);
return tty_port_block_til_ready(port, tty, filp);
--
1.6.5.5

2009-12-11 23:34:57

by Greg KH

[permalink] [raw]
Subject: [PATCH 39/58] tty: stallion: Convert to the tty_port_open/close methods

From: Alan Cox <[email protected]>

The driver is already structured this way so just slice and dice

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/stallion.c | 116 ++++++++++++++---------------------------------
1 files changed, 35 insertions(+), 81 deletions(-)

diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index b4ba5ed..0e511d6 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -702,6 +702,24 @@ static struct stlbrd *stl_allocbrd(void)

/*****************************************************************************/

+static int stl_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct stlport *portp = container_of(port, struct stlport, port);
+ if (!portp->tx.buf) {
+ portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL);
+ if (!portp->tx.buf)
+ return -ENOMEM;
+ portp->tx.head = portp->tx.buf;
+ portp->tx.tail = portp->tx.buf;
+ }
+ stl_setport(portp, tty->termios);
+ portp->sigs = stl_getsignals(portp);
+ stl_setsignals(portp, 1, 1);
+ stl_enablerxtx(portp, 1, 1);
+ stl_startrxtx(portp, 1, 0);
+ return 0;
+}
+
static int stl_open(struct tty_struct *tty, struct file *filp)
{
struct stlport *portp;
@@ -737,32 +755,8 @@ static int stl_open(struct tty_struct *tty, struct file *filp)
if (portp == NULL)
return -ENODEV;
port = &portp->port;
+ return tty_port_open(&portp->port, tty, filp);

-/*
- * On the first open of the device setup the port hardware, and
- * initialize the per port data structure.
- */
- tty_port_tty_set(port, tty);
- tty->driver_data = portp;
- port->count++;
-
- if ((port->flags & ASYNC_INITIALIZED) == 0) {
- if (!portp->tx.buf) {
- portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL);
- if (!portp->tx.buf)
- return -ENOMEM;
- portp->tx.head = portp->tx.buf;
- portp->tx.tail = portp->tx.buf;
- }
- stl_setport(portp, tty->termios);
- portp->sigs = stl_getsignals(portp);
- stl_setsignals(portp, 1, 1);
- stl_enablerxtx(portp, 1, 1);
- stl_startrxtx(portp, 1, 0);
- clear_bit(TTY_IO_ERROR, &tty->flags);
- port->flags |= ASYNC_INITIALIZED;
- }
- return tty_port_block_til_ready(port, tty, filp);
}

/*****************************************************************************/
@@ -826,38 +820,12 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout)

/*****************************************************************************/

-static void stl_close(struct tty_struct *tty, struct file *filp)
+static void stl_shutdown(struct tty_port *port)
{
- struct stlport *portp;
- struct tty_port *port;
- unsigned long flags;
-
- pr_debug("stl_close(tty=%p,filp=%p)\n", tty, filp);
-
- portp = tty->driver_data;
- BUG_ON(portp == NULL);
-
- port = &portp->port;
-
- if (tty_port_close_start(port, tty, filp) == 0)
- return;
-/*
- * May want to wait for any data to drain before closing. The BUSY
- * flag keeps track of whether we are still sending or not - it is
- * very accurate for the cd1400, not quite so for the sc26198.
- * (The sc26198 has no "end-of-data" interrupt only empty FIFO)
- */
- stl_waituntilsent(tty, (HZ / 2));
-
- spin_lock_irqsave(&port->lock, flags);
- portp->port.flags &= ~ASYNC_INITIALIZED;
- spin_unlock_irqrestore(&port->lock, flags);
-
+ struct stlport *portp = container_of(port, struct stlport, port);
stl_disableintrs(portp);
- if (tty->termios->c_cflag & HUPCL)
- stl_setsignals(portp, 0, 0);
stl_enablerxtx(portp, 0, 0);
- stl_flushbuffer(tty);
+ stl_flush(portp);
portp->istate = 0;
if (portp->tx.buf != NULL) {
kfree(portp->tx.buf);
@@ -865,9 +833,16 @@ static void stl_close(struct tty_struct *tty, struct file *filp)
portp->tx.head = NULL;
portp->tx.tail = NULL;
}
+}
+
+static void stl_close(struct tty_struct *tty, struct file *filp)
+{
+ struct stlport*portp;
+ pr_debug("stl_close(tty=%p,filp=%p)\n", tty, filp);

- tty_port_close_end(port, tty);
- tty_port_tty_set(port, NULL);
+ portp = tty->driver_data;
+ BUG_ON(portp == NULL);
+ tty_port_close(&portp->port, tty, filp);
}

/*****************************************************************************/
@@ -1314,35 +1289,12 @@ static void stl_stop(struct tty_struct *tty)

static void stl_hangup(struct tty_struct *tty)
{
- struct stlport *portp;
- struct tty_port *port;
- unsigned long flags;
-
+ struct stlport *portp = tty->driver_data;
pr_debug("stl_hangup(tty=%p)\n", tty);

- portp = tty->driver_data;
if (portp == NULL)
return;
- port = &portp->port;
-
- spin_lock_irqsave(&port->lock, flags);
- port->flags &= ~ASYNC_INITIALIZED;
- spin_unlock_irqrestore(&port->lock, flags);
-
- stl_disableintrs(portp);
- if (tty->termios->c_cflag & HUPCL)
- stl_setsignals(portp, 0, 0);
- stl_enablerxtx(portp, 0, 0);
- stl_flushbuffer(tty);
- portp->istate = 0;
- set_bit(TTY_IO_ERROR, &tty->flags);
- if (portp->tx.buf != NULL) {
- kfree(portp->tx.buf);
- portp->tx.buf = NULL;
- portp->tx.head = NULL;
- portp->tx.tail = NULL;
- }
- tty_port_hangup(port);
+ tty_port_hangup(&portp->port);
}

/*****************************************************************************/
@@ -2550,6 +2502,8 @@ static const struct tty_operations stl_ops = {
static const struct tty_port_operations stl_port_ops = {
.carrier_raised = stl_carrier_raised,
.dtr_rts = stl_dtr_rts,
+ .activate = stl_activate,
+ .shutdown = stl_shutdown,
};

/*****************************************************************************/
--
1.6.5.5

2009-12-11 23:34:15

by Greg KH

[permalink] [raw]
Subject: [PATCH 40/58] tty: istallion: tty port open/close methods

From: Alan Cox <[email protected]>

Slice/dice/repeat as with the stallion driver this is just code shuffling
and removal

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/istallion.c | 177 ++++++++++++++++------------------------------
1 files changed, 60 insertions(+), 117 deletions(-)

diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index babfd44..4cd6c52 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -213,7 +213,6 @@ static int stli_shared;
* with the slave. Most of them need to be updated atomically, so always
* use the bit setting operations (unless protected by cli/sti).
*/
-#define ST_INITIALIZING 1
#define ST_OPENING 2
#define ST_CLOSING 3
#define ST_CMDING 4
@@ -783,13 +782,32 @@ static int stli_parsebrd(struct stlconf *confp, char **argp)

/*****************************************************************************/

+/*
+ * On the first open of the device setup the port hardware, and
+ * initialize the per port data structure. Since initializing the port
+ * requires several commands to the board we will need to wait for any
+ * other open that is already initializing the port.
+ *
+ * Locking: protected by the port mutex.
+ */
+
+static int stli_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct stliport *portp = container_of(port, struct stliport, port);
+ struct stlibrd *brdp = stli_brds[portp->brdnr];
+ int rc;
+
+ if ((rc = stli_initopen(tty, brdp, portp)) >= 0)
+ clear_bit(TTY_IO_ERROR, &tty->flags);
+ wake_up_interruptible(&portp->raw_wait);
+ return rc;
+}
+
static int stli_open(struct tty_struct *tty, struct file *filp)
{
struct stlibrd *brdp;
struct stliport *portp;
- struct tty_port *port;
unsigned int minordev, brdnr, portnr;
- int rc;

minordev = tty->index;
brdnr = MINOR2BRD(minordev);
@@ -809,95 +827,56 @@ static int stli_open(struct tty_struct *tty, struct file *filp)
return -ENODEV;
if (portp->devnr < 1)
return -ENODEV;
- port = &portp->port;
-
-/*
- * On the first open of the device setup the port hardware, and
- * initialize the per port data structure. Since initializing the port
- * requires several commands to the board we will need to wait for any
- * other open that is already initializing the port.
- *
- * Review - locking
- */
- tty_port_tty_set(port, tty);
- tty->driver_data = portp;
- port->count++;
-
- wait_event_interruptible(portp->raw_wait,
- !test_bit(ST_INITIALIZING, &portp->state));
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- if ((portp->port.flags & ASYNC_INITIALIZED) == 0) {
- set_bit(ST_INITIALIZING, &portp->state);
- if ((rc = stli_initopen(tty, brdp, portp)) >= 0) {
- /* Locking */
- port->flags |= ASYNC_INITIALIZED;
- clear_bit(TTY_IO_ERROR, &tty->flags);
- }
- clear_bit(ST_INITIALIZING, &portp->state);
- wake_up_interruptible(&portp->raw_wait);
- if (rc < 0)
- return rc;
- }
- return tty_port_block_til_ready(&portp->port, tty, filp);
+ return tty_port_open(&portp->port, tty, filp);
}

+
/*****************************************************************************/

-static void stli_close(struct tty_struct *tty, struct file *filp)
+static void stli_shutdown(struct tty_port *port)
{
struct stlibrd *brdp;
- struct stliport *portp;
- struct tty_port *port;
+ unsigned long ftype;
unsigned long flags;
+ struct stliport *portp = container_of(port, struct stliport, port);

- portp = tty->driver_data;
- if (portp == NULL)
+ if (portp->brdnr >= stli_nrbrds)
return;
- port = &portp->port;
-
- if (tty_port_close_start(port, tty, filp) == 0)
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == NULL)
return;

-/*
- * May want to wait for data to drain before closing. The BUSY flag
- * keeps track of whether we are still transmitting or not. It is
- * updated by messages from the slave - indicating when all chars
- * really have drained.
- */
- spin_lock_irqsave(&stli_lock, flags);
- if (tty == stli_txcooktty)
- stli_flushchars(tty);
- spin_unlock_irqrestore(&stli_lock, flags);
-
- /* We end up doing this twice for the moment. This needs looking at
- eventually. Note we still use portp->closing_wait as a result */
- if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, portp->closing_wait);
+ /*
+ * May want to wait for data to drain before closing. The BUSY
+ * flag keeps track of whether we are still transmitting or not.
+ * It is updated by messages from the slave - indicating when all
+ * chars really have drained.
+ */

- /* FIXME: port locking here needs attending to */
- port->flags &= ~ASYNC_INITIALIZED;
+ if (!test_bit(ST_CLOSING, &portp->state))
+ stli_rawclose(brdp, portp, 0, 0);

- brdp = stli_brds[portp->brdnr];
- stli_rawclose(brdp, portp, 0, 0);
- if (tty->termios->c_cflag & HUPCL) {
- stli_mkasysigs(&portp->asig, 0, 0);
- if (test_bit(ST_CMDING, &portp->state))
- set_bit(ST_DOSIGS, &portp->state);
- else
- stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig,
- sizeof(asysigs_t), 0);
- }
+ spin_lock_irqsave(&stli_lock, flags);
clear_bit(ST_TXBUSY, &portp->state);
clear_bit(ST_RXSTOP, &portp->state);
- set_bit(TTY_IO_ERROR, &tty->flags);
- tty_ldisc_flush(tty);
- set_bit(ST_DOFLUSHRX, &portp->state);
- stli_flushbuffer(tty);
+ spin_unlock_irqrestore(&stli_lock, flags);

- tty_port_close_end(port, tty);
- tty_port_tty_set(port, NULL);
+ ftype = FLUSHTX | FLUSHRX;
+ stli_cmdwait(brdp, portp, A_FLUSH, &ftype, sizeof(u32), 0);
+}
+
+static void stli_close(struct tty_struct *tty, struct file *filp)
+{
+ struct stliport *portp = tty->driver_data;
+ unsigned long flags;
+ if (portp == NULL)
+ return;
+ spin_lock_irqsave(&stli_lock, flags);
+ /* Flush any internal buffering out first */
+ if (tty == stli_txcooktty)
+ stli_flushchars(tty);
+ spin_unlock_irqrestore(&stli_lock, flags);
+ tty_port_close(&portp->port, tty, filp);
}

/*****************************************************************************/
@@ -1724,6 +1703,7 @@ static void stli_start(struct tty_struct *tty)

/*****************************************************************************/

+
/*
* Hangup this port. This is pretty much like closing the port, only
* a little more brutal. No waiting for data to drain. Shutdown the
@@ -1733,47 +1713,8 @@ static void stli_start(struct tty_struct *tty)

static void stli_hangup(struct tty_struct *tty)
{
- struct stliport *portp;
- struct stlibrd *brdp;
- struct tty_port *port;
- unsigned long flags;
-
- portp = tty->driver_data;
- if (portp == NULL)
- return;
- if (portp->brdnr >= stli_nrbrds)
- return;
- brdp = stli_brds[portp->brdnr];
- if (brdp == NULL)
- return;
- port = &portp->port;
-
- spin_lock_irqsave(&port->lock, flags);
- port->flags &= ~ASYNC_INITIALIZED;
- spin_unlock_irqrestore(&port->lock, flags);
-
- if (!test_bit(ST_CLOSING, &portp->state))
- stli_rawclose(brdp, portp, 0, 0);
-
- spin_lock_irqsave(&stli_lock, flags);
- if (tty->termios->c_cflag & HUPCL) {
- stli_mkasysigs(&portp->asig, 0, 0);
- if (test_bit(ST_CMDING, &portp->state)) {
- set_bit(ST_DOSIGS, &portp->state);
- set_bit(ST_DOFLUSHTX, &portp->state);
- set_bit(ST_DOFLUSHRX, &portp->state);
- } else {
- stli_sendcmd(brdp, portp, A_SETSIGNALSF,
- &portp->asig, sizeof(asysigs_t), 0);
- }
- }
-
- clear_bit(ST_TXBUSY, &portp->state);
- clear_bit(ST_RXSTOP, &portp->state);
- set_bit(TTY_IO_ERROR, &tty->flags);
- spin_unlock_irqrestore(&stli_lock, flags);
-
- tty_port_hangup(port);
+ struct stliport *portp = tty->driver_data;
+ tty_port_hangup(&portp->port);
}

/*****************************************************************************/
@@ -4420,6 +4361,8 @@ static const struct tty_operations stli_ops = {
static const struct tty_port_operations stli_port_ops = {
.carrier_raised = stli_carrier_raised,
.dtr_rts = stli_dtr_rts,
+ .activate = stli_activate,
+ .shutdown = stli_shutdown,
};

/*****************************************************************************/
--
1.6.5.5

2009-12-11 23:34:11

by Greg KH

[permalink] [raw]
Subject: [PATCH 41/58] tty: tty_port: Add a kref object to the tty port

From: Alan Cox <[email protected]>

Users of tty port need a way to refcount ports when hotplugging is
involved.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/tty_port.c | 18 ++++++++++++++++++
include/linux/tty.h | 12 ++++++++++++
2 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
index 84006de..be492dd 100644
--- a/drivers/char/tty_port.c
+++ b/drivers/char/tty_port.c
@@ -29,6 +29,7 @@ void tty_port_init(struct tty_port *port)
spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100;
+ kref_init(&port->kref);
}
EXPORT_SYMBOL(tty_port_init);

@@ -56,6 +57,23 @@ void tty_port_free_xmit_buf(struct tty_port *port)
}
EXPORT_SYMBOL(tty_port_free_xmit_buf);

+static void tty_port_destructor(struct kref *kref)
+{
+ struct tty_port *port = container_of(kref, struct tty_port, kref);
+ if (port->xmit_buf)
+ free_page((unsigned long)port->xmit_buf);
+ if (port->ops->destruct)
+ port->ops->destruct(port);
+ else
+ kfree(port);
+}
+
+void tty_port_put(struct tty_port *port)
+{
+ if (port)
+ kref_put(&port->kref, tty_port_destructor);
+}
+EXPORT_SYMBOL(tty_port_put);

/**
* tty_port_tty_get - get a tty reference
diff --git a/include/linux/tty.h b/include/linux/tty.h
index e9269ca..e6da667 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -199,6 +199,8 @@ struct tty_port_operations {
/* FIXME: long term getting the tty argument *out* of this would be
good for consoles */
int (*activate)(struct tty_port *port, struct tty_struct *tty);
+ /* Called on the final put of a port */
+ void (*destruct)(struct tty_port *port);
};

struct tty_port {
@@ -219,6 +221,7 @@ struct tty_port {
int drain_delay; /* Set to zero if no pure time
based drain is needed else
set to size of fifo */
+ struct kref kref; /* Ref counter */
};

/*
@@ -461,6 +464,15 @@ extern int tty_write_lock(struct tty_struct *tty, int ndelay);
extern void tty_port_init(struct tty_port *port);
extern int tty_port_alloc_xmit_buf(struct tty_port *port);
extern void tty_port_free_xmit_buf(struct tty_port *port);
+extern void tty_port_put(struct tty_port *port);
+
+extern inline struct tty_port *tty_port_get(struct tty_port *port)
+{
+ if (port)
+ kref_get(&port->kref);
+ return port;
+}
+
extern struct tty_struct *tty_port_tty_get(struct tty_port *port);
extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty);
extern int tty_port_carrier_raised(struct tty_port *port);
--
1.6.5.5

2009-12-11 23:34:05

by Greg KH

[permalink] [raw]
Subject: [PATCH 42/58] tty: isicom: switch to the new tty_port_open helper

From: Alan Cox <[email protected]>

Trivial conversion in this case so might as well do it while testing the
port_open design is right

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/isicom.c | 88 ++++++++++++-------------------------------------
1 files changed, 21 insertions(+), 67 deletions(-)

diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
index 426bfdd..e7be3ec 100644
--- a/drivers/char/isicom.c
+++ b/drivers/char/isicom.c
@@ -804,24 +804,21 @@ static inline void isicom_setup_board(struct isi_board *bp)
bp->status |= BOARD_ACTIVE;
for (channel = 0; channel < bp->port_count; channel++, port++)
drop_dtr_rts(port);
+ bp->count++;
spin_unlock_irqrestore(&bp->card_lock, flags);
}

-static int isicom_setup_port(struct tty_struct *tty)
+static int isicom_activate(struct tty_port *tport, struct tty_struct *tty)
{
- struct isi_port *port = tty->driver_data;
+ struct isi_port *port = container_of(tport, struct isi_port, port);
struct isi_board *card = port->card;
unsigned long flags;

- if (port->port.flags & ASYNC_INITIALIZED)
- return 0;
- if (tty_port_alloc_xmit_buf(&port->port) < 0)
+ if (tty_port_alloc_xmit_buf(tport) < 0)
return -ENOMEM;

spin_lock_irqsave(&card->card_lock, flags);
- clear_bit(TTY_IO_ERROR, &tty->flags);
- if (port->port.count == 1)
- card->count++;
+ isicom_setup_board(card);

port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;

@@ -832,9 +829,7 @@ static int isicom_setup_port(struct tty_struct *tty)
outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base);
InterruptTheCard(card->base);
}
-
isicom_config_port(tty);
- port->port.flags |= ASYNC_INITIALIZED;
spin_unlock_irqrestore(&card->card_lock, flags);

return 0;
@@ -871,31 +866,20 @@ static struct tty_port *isicom_find_port(struct tty_struct *tty)

return &port->port;
}
-
+
static int isicom_open(struct tty_struct *tty, struct file *filp)
{
struct isi_port *port;
struct isi_board *card;
struct tty_port *tport;
- int error = 0;

tport = isicom_find_port(tty);
if (tport == NULL)
return -ENODEV;
port = container_of(tport, struct isi_port, port);
card = &isi_card[BOARD(tty->index)];
- isicom_setup_board(card);

- /* FIXME: locking on port.count etc */
- port->port.count++;
- tty->driver_data = port;
- tty_port_tty_set(&port->port, tty);
- /* FIXME: Locking on Initialized flag */
- if (!test_bit(ASYNCB_INITIALIZED, &tport->flags))
- error = isicom_setup_port(tty);
- if (error == 0)
- error = tty_port_block_til_ready(&port->port, tty, filp);
- return error;
+ return tty_port_open(tport, tty, filp);
}

/* close et all */
@@ -914,40 +898,21 @@ static void isicom_shutdown_port(struct isi_port *port)

tty = tty_port_tty_get(&port->port);

- if (!(port->port.flags & ASYNC_INITIALIZED)) {
- tty_kref_put(tty);
- return;
- }
-
tty_port_free_xmit_buf(&port->port);
- port->port.flags &= ~ASYNC_INITIALIZED;
- /* 3rd October 2000 : Vinayak P Risbud */
- tty_port_tty_set(&port->port, NULL);
-
- /*Fix done by Anil .S on 30-04-2001
- remote login through isi port has dtr toggle problem
- due to which the carrier drops before the password prompt
- appears on the remote end. Now we drop the dtr only if the
- HUPCL(Hangup on close) flag is set for the tty*/
-
- if (C_HUPCL(tty))
- /* drop dtr on this port */
- drop_dtr(port);
-
- /* any other port uninits */
- if (tty)
- set_bit(TTY_IO_ERROR, &tty->flags);
-
if (--card->count < 0) {
pr_dbg("isicom_shutdown_port: bad board(0x%lx) count %d.\n",
card->base, card->count);
card->count = 0;
}

- /* last port was closed, shutdown that boad too */
- if (C_HUPCL(tty)) {
- if (!card->count)
- isicom_shutdown_board(card);
+ /* last port was closed, shutdown that board too */
+ if (tty && C_HUPCL(tty)) {
+ /* FIXME: this logic is bogus - it's the old logic that was
+ bogus before but it still wants fixing */
+ if (!card->count) {
+ if (card->status & BOARD_ACTIVE)
+ card->status &= ~BOARD_ACTIVE;
+ }
}
tty_kref_put(tty);
}
@@ -968,7 +933,7 @@ static void isicom_flush_buffer(struct tty_struct *tty)
tty_wakeup(tty);
}

-static void isicom_close_port(struct tty_port *port)
+static void isicom_shutdown(struct tty_port *port)
{
struct isi_port *ip = container_of(port, struct isi_port, port);
struct isi_board *card = ip->card;
@@ -977,10 +942,8 @@ static void isicom_close_port(struct tty_port *port)
/* indicate to the card that no more data can be received
on this port */
spin_lock_irqsave(&card->card_lock, flags);
- if (port->flags & ASYNC_INITIALIZED) {
- card->port_status &= ~(1 << ip->channel);
- outw(card->port_status, card->base + 0x02);
- }
+ card->port_status &= ~(1 << ip->channel);
+ outw(card->port_status, card->base + 0x02);
isicom_shutdown_port(ip);
spin_unlock_irqrestore(&card->card_lock, flags);
}
@@ -991,12 +954,7 @@ static void isicom_close(struct tty_struct *tty, struct file *filp)
struct tty_port *port = &ip->port;
if (isicom_paranoia_check(ip, tty->name, "isicom_close"))
return;
-
- if (tty_port_close_start(port, tty, filp) == 0)
- return;
- isicom_close_port(port);
- isicom_flush_buffer(tty);
- tty_port_close_end(port, tty);
+ tty_port_close(port, tty, filp);
}

/* write et all */
@@ -1326,15 +1284,9 @@ static void isicom_start(struct tty_struct *tty)
static void isicom_hangup(struct tty_struct *tty)
{
struct isi_port *port = tty->driver_data;
- unsigned long flags;

if (isicom_paranoia_check(port, tty->name, "isicom_hangup"))
return;
-
- spin_lock_irqsave(&port->card->card_lock, flags);
- isicom_shutdown_port(port);
- spin_unlock_irqrestore(&port->card->card_lock, flags);
-
tty_port_hangup(&port->port);
}

@@ -1367,6 +1319,8 @@ static const struct tty_operations isicom_ops = {
static const struct tty_port_operations isicom_port_ops = {
.carrier_raised = isicom_carrier_raised,
.dtr_rts = isicom_dtr_rts,
+ .activate = isicom_activate,
+ .shutdown = isicom_shutdown,
};

static int __devinit reset_card(struct pci_dev *pdev,
--
1.6.5.5

2009-12-11 23:33:41

by Greg KH

[permalink] [raw]
Subject: [PATCH 43/58] tty: isicom: sort out the board init logic

From: Alan Cox <[email protected]>

Split this into two flags - INIT meaning the board is set up and ACTIVE
meaning the board has ports open. Remove the broken HUPCL casing and push
the counts somewhere sensible.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/isicom.c | 41 +++++++++++------------------------------
include/linux/isicom.h | 1 +
2 files changed, 12 insertions(+), 30 deletions(-)

diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
index e7be3ec..1e91c30 100644
--- a/drivers/char/isicom.c
+++ b/drivers/char/isicom.c
@@ -793,21 +793,19 @@ static inline void isicom_setup_board(struct isi_board *bp)
{
int channel;
struct isi_port *port;
- unsigned long flags;

- spin_lock_irqsave(&bp->card_lock, flags);
- if (bp->status & BOARD_ACTIVE) {
- spin_unlock_irqrestore(&bp->card_lock, flags);
- return;
- }
- port = bp->ports;
- bp->status |= BOARD_ACTIVE;
- for (channel = 0; channel < bp->port_count; channel++, port++)
- drop_dtr_rts(port);
bp->count++;
- spin_unlock_irqrestore(&bp->card_lock, flags);
+ if (!(bp->status & BOARD_INIT)) {
+ port = bp->ports;
+ for (channel = 0; channel < bp->port_count; channel++, port++)
+ drop_dtr_rts(port);
+ }
+ bp->status |= BOARD_ACTIVE | BOARD_INIT;
}

+/* Activate and thus setup board are protected from races against shutdown
+ by the tty_port mutex */
+
static int isicom_activate(struct tty_port *tport, struct tty_struct *tty)
{
struct isi_port *port = container_of(tport, struct isi_port, port);
@@ -884,19 +882,10 @@ static int isicom_open(struct tty_struct *tty, struct file *filp)

/* close et all */

-static inline void isicom_shutdown_board(struct isi_board *bp)
-{
- if (bp->status & BOARD_ACTIVE)
- bp->status &= ~BOARD_ACTIVE;
-}
-
/* card->lock HAS to be held */
static void isicom_shutdown_port(struct isi_port *port)
{
struct isi_board *card = port->card;
- struct tty_struct *tty;
-
- tty = tty_port_tty_get(&port->port);

tty_port_free_xmit_buf(&port->port);
if (--card->count < 0) {
@@ -904,17 +893,9 @@ static void isicom_shutdown_port(struct isi_port *port)
card->base, card->count);
card->count = 0;
}
-
/* last port was closed, shutdown that board too */
- if (tty && C_HUPCL(tty)) {
- /* FIXME: this logic is bogus - it's the old logic that was
- bogus before but it still wants fixing */
- if (!card->count) {
- if (card->status & BOARD_ACTIVE)
- card->status &= ~BOARD_ACTIVE;
- }
- }
- tty_kref_put(tty);
+ if (!card->count)
+ card->status &= BOARD_ACTIVE;
}

static void isicom_flush_buffer(struct tty_struct *tty)
diff --git a/include/linux/isicom.h b/include/linux/isicom.h
index bbd4219..b92e056 100644
--- a/include/linux/isicom.h
+++ b/include/linux/isicom.h
@@ -67,6 +67,7 @@

#define FIRMWARE_LOADED 0x0001
#define BOARD_ACTIVE 0x0002
+#define BOARD_INIT 0x0004

/* isi_port status bitmap */

--
1.6.5.5

2009-12-11 23:33:22

by Greg KH

[permalink] [raw]
Subject: [PATCH 44/58] tty: mxser: use the tty_port_open method

From: Alan Cox <[email protected]>

At first this looks a fairly trivial conversion but we can't quite push
everything into the right format yet. The open side is easy but care is needed
over the setserial methods. Fix up the locking now that we've adopted the
port->mutex locking rule for the initialization.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/mxser.c | 111 ++++++++++++++++----------------------------------
1 files changed, 35 insertions(+), 76 deletions(-)

diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index 5e28d39..9dac516 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -856,9 +856,9 @@ static void mxser_check_modem_status(struct tty_struct *tty,
}
}

-static int mxser_startup(struct tty_struct *tty)
+static int mxser_activate(struct tty_port *port, struct tty_struct *tty)
{
- struct mxser_port *info = tty->driver_data;
+ struct mxser_port *info = container_of(port, struct mxser_port, port);
unsigned long page;
unsigned long flags;

@@ -868,22 +868,13 @@ static int mxser_startup(struct tty_struct *tty)

spin_lock_irqsave(&info->slock, flags);

- if (info->port.flags & ASYNC_INITIALIZED) {
- free_page(page);
- spin_unlock_irqrestore(&info->slock, flags);
- return 0;
- }
-
if (!info->ioaddr || !info->type) {
set_bit(TTY_IO_ERROR, &tty->flags);
free_page(page);
spin_unlock_irqrestore(&info->slock, flags);
return 0;
}
- if (info->port.xmit_buf)
- free_page(page);
- else
- info->port.xmit_buf = (unsigned char *) page;
+ info->port.xmit_buf = (unsigned char *) page;

/*
* Clear the FIFO buffers and disable them
@@ -951,24 +942,19 @@ static int mxser_startup(struct tty_struct *tty)
* and set the speed of the serial port
*/
mxser_change_speed(tty, NULL);
- info->port.flags |= ASYNC_INITIALIZED;
spin_unlock_irqrestore(&info->slock, flags);

return 0;
}

/*
- * This routine will shutdown a serial port; interrupts maybe disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
+ * This routine will shutdown a serial port
*/
-static void mxser_shutdown(struct tty_struct *tty)
+static void mxser_shutdown_port(struct tty_port *port)
{
- struct mxser_port *info = tty->driver_data;
+ struct mxser_port *info = container_of(port, struct mxser_port, port);
unsigned long flags;

- if (!(info->port.flags & ASYNC_INITIALIZED))
- return;
-
spin_lock_irqsave(&info->slock, flags);

/*
@@ -978,7 +964,7 @@ static void mxser_shutdown(struct tty_struct *tty)
wake_up_interruptible(&info->port.delta_msr_wait);

/*
- * Free the IRQ, if necessary
+ * Free the xmit buffer, if necessary
*/
if (info->port.xmit_buf) {
free_page((unsigned long) info->port.xmit_buf);
@@ -988,10 +974,6 @@ static void mxser_shutdown(struct tty_struct *tty)
info->IER = 0;
outb(0x00, info->ioaddr + UART_IER);

- if (tty->termios->c_cflag & HUPCL)
- info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS);
- outb(info->MCR, info->ioaddr + UART_MCR);
-
/* clear Rx/Tx FIFO's */
if (info->board->chip_flag)
outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |
@@ -1004,9 +986,6 @@ static void mxser_shutdown(struct tty_struct *tty)
/* read data port to reset things */
(void) inb(info->ioaddr + UART_RX);

- set_bit(TTY_IO_ERROR, &tty->flags);
-
- info->port.flags &= ~ASYNC_INITIALIZED;

if (info->board->chip_flag)
SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->ioaddr);
@@ -1023,8 +1002,7 @@ static void mxser_shutdown(struct tty_struct *tty)
static int mxser_open(struct tty_struct *tty, struct file *filp)
{
struct mxser_port *info;
- unsigned long flags;
- int retval, line;
+ int line;

line = tty->index;
if (line == MXSER_PORTS)
@@ -1035,23 +1013,7 @@ static int mxser_open(struct tty_struct *tty, struct file *filp)
if (!info->ioaddr)
return -ENODEV;

- tty->driver_data = info;
- tty_port_tty_set(&info->port, tty);
- /*
- * Start up serial port
- */
- spin_lock_irqsave(&info->port.lock, flags);
- info->port.count++;
- spin_unlock_irqrestore(&info->port.lock, flags);
- retval = mxser_startup(tty);
- if (retval)
- return retval;
-
- retval = tty_port_block_til_ready(&info->port, tty, filp);
- if (retval)
- return retval;
-
- return 0;
+ return tty_port_open(&info->port, tty, filp);
}

static void mxser_flush_buffer(struct tty_struct *tty)
@@ -1075,19 +1037,11 @@ static void mxser_flush_buffer(struct tty_struct *tty)
}


-static void mxser_close_port(struct tty_struct *tty, struct tty_port *port)
+static void mxser_close_port(struct tty_port *port)
{
struct mxser_port *info = container_of(port, struct mxser_port, port);
unsigned long timeout;
/*
- * Save the termios structure, since this port may have
- * separate termios for callout and dialin.
- *
- * FIXME: Can this go ?
- */
- if (port->flags & ASYNC_NORMAL_ACTIVE)
- info->normal_termios = *tty->termios;
- /*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
* interrupt driver to stop checking the data ready bit in the
@@ -1097,22 +1051,18 @@ static void mxser_close_port(struct tty_struct *tty, struct tty_port *port)
if (info->board->chip_flag)
info->IER &= ~MOXA_MUST_RECV_ISR;

- if (port->flags & ASYNC_INITIALIZED) {
- outb(info->IER, info->ioaddr + UART_IER);
- /*
- * Before we drop DTR, make sure the UART transmitter
- * has completely drained; this is especially
- * important if there is a transmit FIFO!
- */
- timeout = jiffies + HZ;
- while (!(inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT)) {
- schedule_timeout_interruptible(5);
- if (time_after(jiffies, timeout))
- break;
- }
+ outb(info->IER, info->ioaddr + UART_IER);
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ timeout = jiffies + HZ;
+ while (!(inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT)) {
+ schedule_timeout_interruptible(5);
+ if (time_after(jiffies, timeout))
+ break;
}
- mxser_shutdown(tty);
-
}

/*
@@ -1130,8 +1080,12 @@ static void mxser_close(struct tty_struct *tty, struct file *filp)
return;
if (tty_port_close_start(port, tty, filp) == 0)
return;
- mxser_close_port(tty, port);
+ mutex_lock(&port->mutex);
+ mxser_close_port(port);
mxser_flush_buffer(tty);
+ mxser_shutdown_port(port);
+ clear_bit(ASYNCB_INITIALIZED, &port->flags);
+ mutex_unlock(&port->mutex);
/* Right now the tty_port set is done outside of the close_end helper
as we don't yet have everyone using refcounts */
tty_port_close_end(port, tty);
@@ -1329,9 +1283,13 @@ static int mxser_set_serial_info(struct tty_struct *tty,
mxser_change_speed(tty, NULL);
spin_unlock_irqrestore(&info->slock, sl_flags);
}
- } else
- retval = mxser_startup(tty);
-
+ } else {
+ mutex_lock(&info->port.mutex);
+ retval = mxser_activate(&info->port, tty);
+ if (retval == 0)
+ set_bit(ASYNCB_INITIALIZED, &info->port.flags);
+ mutex_unlock(&info->port.mutex);
+ }
return retval;
}

@@ -2059,7 +2017,6 @@ static void mxser_hangup(struct tty_struct *tty)
struct mxser_port *info = tty->driver_data;

mxser_flush_buffer(tty);
- mxser_shutdown(tty);
tty_port_hangup(&info->port);
}

@@ -2363,6 +2320,8 @@ static const struct tty_operations mxser_ops = {
struct tty_port_operations mxser_port_ops = {
.carrier_raised = mxser_carrier_raised,
.dtr_rts = mxser_dtr_rts,
+ .activate = mxser_activate,
+ .shutdown = mxser_shutdown_port,
};

/*
--
1.6.5.5

2009-12-11 23:33:12

by Greg KH

[permalink] [raw]
Subject: [PATCH 45/58] tty: mxser: Use the new locking rules to fix setserial properly

From: Alan Cox <[email protected]>

Propogate the init/shutdown mutex through the setserial logic. Use the proper
locks for the various bits still using the BKL. Kill the BKL in this driver.

Updated to fix the bug noted by Dan Carpenter

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/mxser.c | 145 +++++++++++++++++++++++++++-----------------------
1 files changed, 78 insertions(+), 67 deletions(-)

diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index 9dac516..3d92306 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -23,7 +23,6 @@
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
-#include <linux/smp_lock.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
@@ -1229,6 +1228,7 @@ static int mxser_set_serial_info(struct tty_struct *tty,
struct serial_struct __user *new_info)
{
struct mxser_port *info = tty->driver_data;
+ struct tty_port *port = &info->port;
struct serial_struct new_serial;
speed_t baud;
unsigned long sl_flags;
@@ -1244,7 +1244,7 @@ static int mxser_set_serial_info(struct tty_struct *tty,
new_serial.port != info->ioaddr)
return -EINVAL;

- flags = info->port.flags & ASYNC_SPD_MASK;
+ flags = port->flags & ASYNC_SPD_MASK;

if (!capable(CAP_SYS_ADMIN)) {
if ((new_serial.baud_base != info->baud_base) ||
@@ -1258,16 +1258,17 @@ static int mxser_set_serial_info(struct tty_struct *tty,
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
- info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) |
+ port->flags = ((port->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
- info->port.close_delay = new_serial.close_delay * HZ / 100;
- info->port.closing_wait = new_serial.closing_wait * HZ / 100;
- tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY)
- ? 1 : 0;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
+ port->close_delay = new_serial.close_delay * HZ / 100;
+ port->closing_wait = new_serial.closing_wait * HZ / 100;
+ tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
(new_serial.baud_base != info->baud_base ||
new_serial.custom_divisor !=
info->custom_divisor)) {
+ if (new_serial.custom_divisor == 0)
+ return -EINVAL;
baud = new_serial.baud_base / new_serial.custom_divisor;
tty_encode_baud_rate(tty, baud, baud);
}
@@ -1277,18 +1278,16 @@ static int mxser_set_serial_info(struct tty_struct *tty,

process_txrx_fifo(info);

- if (info->port.flags & ASYNC_INITIALIZED) {
- if (flags != (info->port.flags & ASYNC_SPD_MASK)) {
+ if (test_bit(ASYNCB_INITIALIZED, &port->flags)) {
+ if (flags != (port->flags & ASYNC_SPD_MASK)) {
spin_lock_irqsave(&info->slock, sl_flags);
mxser_change_speed(tty, NULL);
spin_unlock_irqrestore(&info->slock, sl_flags);
}
} else {
- mutex_lock(&info->port.mutex);
- retval = mxser_activate(&info->port, tty);
+ retval = mxser_activate(port, tty);
if (retval == 0)
- set_bit(ASYNCB_INITIALIZED, &info->port.flags);
- mutex_unlock(&info->port.mutex);
+ set_bit(ASYNCB_INITIALIZED, &port->flags);
}
return retval;
}
@@ -1478,7 +1477,8 @@ static int __init mxser_read_register(int port, unsigned short *regs)

static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
{
- struct mxser_port *port;
+ struct mxser_port *ip;
+ struct tty_port *port;
struct tty_struct *tty;
int result, status;
unsigned int i, j;
@@ -1494,38 +1494,39 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)

case MOXA_CHKPORTENABLE:
result = 0;
- lock_kernel();
for (i = 0; i < MXSER_BOARDS; i++)
for (j = 0; j < MXSER_PORTS_PER_BOARD; j++)
if (mxser_boards[i].ports[j].ioaddr)
result |= (1 << i);
- unlock_kernel();
return put_user(result, (unsigned long __user *)argp);
case MOXA_GETDATACOUNT:
- lock_kernel();
+ /* The receive side is locked by port->slock but it isn't
+ clear that an exact snapshot is worth copying here */
if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log)))
ret = -EFAULT;
- unlock_kernel();
return ret;
case MOXA_GETMSTATUS: {
struct mxser_mstatus ms, __user *msu = argp;
- lock_kernel();
for (i = 0; i < MXSER_BOARDS; i++)
for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) {
- port = &mxser_boards[i].ports[j];
+ ip = &mxser_boards[i].ports[j];
+ port = &ip->port;
memset(&ms, 0, sizeof(ms));

- if (!port->ioaddr)
+ mutex_lock(&port->mutex);
+ if (!ip->ioaddr)
goto copy;

- tty = tty_port_tty_get(&port->port);
+ tty = tty_port_tty_get(port);

if (!tty || !tty->termios)
- ms.cflag = port->normal_termios.c_cflag;
+ ms.cflag = ip->normal_termios.c_cflag;
else
ms.cflag = tty->termios->c_cflag;
tty_kref_put(tty);
- status = inb(port->ioaddr + UART_MSR);
+ spin_lock_irq(&ip->slock);
+ status = inb(ip->ioaddr + UART_MSR);
+ spin_unlock_irq(&ip->slock);
if (status & UART_MSR_DCD)
ms.dcd = 1;
if (status & UART_MSR_DSR)
@@ -1533,13 +1534,11 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
if (status & UART_MSR_CTS)
ms.cts = 1;
copy:
- if (copy_to_user(msu, &ms, sizeof(ms))) {
- unlock_kernel();
+ mutex_unlock(&port->mutex);
+ if (copy_to_user(msu, &ms, sizeof(ms)))
return -EFAULT;
- }
msu++;
}
- unlock_kernel();
return 0;
}
case MOXA_ASPP_MON_EXT: {
@@ -1551,41 +1550,48 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
if (!me)
return -ENOMEM;

- lock_kernel();
for (i = 0, p = 0; i < MXSER_BOARDS; i++) {
for (j = 0; j < MXSER_PORTS_PER_BOARD; j++, p++) {
if (p >= ARRAY_SIZE(me->rx_cnt)) {
i = MXSER_BOARDS;
break;
}
- port = &mxser_boards[i].ports[j];
- if (!port->ioaddr)
+ ip = &mxser_boards[i].ports[j];
+ port = &ip->port;
+
+ mutex_lock(&port->mutex);
+ if (!ip->ioaddr) {
+ mutex_unlock(&port->mutex);
continue;
+ }

- status = mxser_get_msr(port->ioaddr, 0, p);
+ spin_lock_irq(&ip->slock);
+ status = mxser_get_msr(ip->ioaddr, 0, p);

if (status & UART_MSR_TERI)
- port->icount.rng++;
+ ip->icount.rng++;
if (status & UART_MSR_DDSR)
- port->icount.dsr++;
+ ip->icount.dsr++;
if (status & UART_MSR_DDCD)
- port->icount.dcd++;
+ ip->icount.dcd++;
if (status & UART_MSR_DCTS)
- port->icount.cts++;
+ ip->icount.cts++;

- port->mon_data.modem_status = status;
- me->rx_cnt[p] = port->mon_data.rxcnt;
- me->tx_cnt[p] = port->mon_data.txcnt;
- me->up_rxcnt[p] = port->mon_data.up_rxcnt;
- me->up_txcnt[p] = port->mon_data.up_txcnt;
+ ip->mon_data.modem_status = status;
+ me->rx_cnt[p] = ip->mon_data.rxcnt;
+ me->tx_cnt[p] = ip->mon_data.txcnt;
+ me->up_rxcnt[p] = ip->mon_data.up_rxcnt;
+ me->up_txcnt[p] = ip->mon_data.up_txcnt;
me->modem_status[p] =
- port->mon_data.modem_status;
- tty = tty_port_tty_get(&port->port);
+ ip->mon_data.modem_status;
+ spin_unlock_irq(&ip->slock);
+
+ tty = tty_port_tty_get(&ip->port);

if (!tty || !tty->termios) {
- cflag = port->normal_termios.c_cflag;
- iflag = port->normal_termios.c_iflag;
- me->baudrate[p] = tty_termios_baud_rate(&port->normal_termios);
+ cflag = ip->normal_termios.c_cflag;
+ iflag = ip->normal_termios.c_iflag;
+ me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios);
} else {
cflag = tty->termios->c_cflag;
iflag = tty->termios->c_iflag;
@@ -1604,16 +1610,15 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
if (iflag & (IXON | IXOFF))
me->flowctrl[p] |= 0x0C;

- if (port->type == PORT_16550A)
+ if (ip->type == PORT_16550A)
me->fifo[p] = 1;

- opmode = inb(port->opmode_ioaddr) >>
- ((p % 4) * 2);
+ opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2);
opmode &= OP_MODE_MASK;
me->iftype[p] = opmode;
+ mutex_unlock(&port->mutex);
}
}
- unlock_kernel();
if (copy_to_user(argp, me, sizeof(*me)))
ret = -EFAULT;
kfree(me);
@@ -1650,6 +1655,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct mxser_port *info = tty->driver_data;
+ struct tty_port *port = &info->port;
struct async_icount cnow;
unsigned long flags;
void __user *argp = (void __user *)arg;
@@ -1674,20 +1680,20 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
opmode != RS422_MODE &&
opmode != RS485_4WIRE_MODE)
return -EFAULT;
- lock_kernel();
mask = ModeMask[p];
shiftbit = p * 2;
+ spin_lock_irq(&info->slock);
val = inb(info->opmode_ioaddr);
val &= mask;
val |= (opmode << shiftbit);
outb(val, info->opmode_ioaddr);
- unlock_kernel();
+ spin_unlock_irq(&info->slock);
} else {
- lock_kernel();
shiftbit = p * 2;
+ spin_lock_irq(&info->slock);
opmode = inb(info->opmode_ioaddr) >> shiftbit;
+ spin_unlock_irq(&info->slock);
opmode &= OP_MODE_MASK;
- unlock_kernel();
if (put_user(opmode, (int __user *)argp))
return -EFAULT;
}
@@ -1700,14 +1706,14 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,

switch (cmd) {
case TIOCGSERIAL:
- lock_kernel();
+ mutex_lock(&port->mutex);
retval = mxser_get_serial_info(tty, argp);
- unlock_kernel();
+ mutex_unlock(&port->mutex);
return retval;
case TIOCSSERIAL:
- lock_kernel();
+ mutex_lock(&port->mutex);
retval = mxser_set_serial_info(tty, argp);
- unlock_kernel();
+ mutex_unlock(&port->mutex);
return retval;
case TIOCSERGETLSR: /* Get line status register */
return mxser_get_lsr_info(info, argp);
@@ -1753,31 +1759,33 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
case MOXA_HighSpeedOn:
return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp);
case MOXA_SDS_RSTICOUNTER:
- lock_kernel();
+ spin_lock_irq(&info->slock);
info->mon_data.rxcnt = 0;
info->mon_data.txcnt = 0;
- unlock_kernel();
+ spin_unlock_irq(&info->slock);
return 0;

case MOXA_ASPP_OQUEUE:{
int len, lsr;

- lock_kernel();
len = mxser_chars_in_buffer(tty);
+ spin_lock(&info->slock);
lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_THRE;
+ spin_unlock_irq(&info->slock);
len += (lsr ? 0 : 1);
- unlock_kernel();

return put_user(len, (int __user *)argp);
}
case MOXA_ASPP_MON: {
int mcr, status;

- lock_kernel();
+ spin_lock(&info->slock);
status = mxser_get_msr(info->ioaddr, 1, tty->index);
mxser_check_modem_status(tty, info, status);

mcr = inb(info->ioaddr + UART_MCR);
+ spin_unlock(&info->slock);
+
if (mcr & MOXA_MUST_MCR_XON_FLAG)
info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD;
else
@@ -1792,7 +1800,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD;
else
info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD;
- unlock_kernel();
+
if (copy_to_user(argp, &info->mon_data,
sizeof(struct mxser_mon)))
return -EFAULT;
@@ -1951,6 +1959,7 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct mxser_port *info = tty->driver_data;
unsigned long orig_jiffies, char_time;
+ unsigned long flags;
int lsr;

if (info->type == PORT_UNKNOWN)
@@ -1990,19 +1999,21 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
timeout, char_time);
printk("jiff=%lu...", jiffies);
#endif
- lock_kernel();
+ spin_lock_irqsave(&info->slock, flags);
while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) {
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
#endif
+ spin_unlock_irqrestore(&info->slock, flags);
schedule_timeout_interruptible(char_time);
+ spin_lock_irqsave(&info->slock, flags);
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
break;
}
+ spin_unlock_irqrestore(&info->slock, flags);
set_current_state(TASK_RUNNING);
- unlock_kernel();

#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
--
1.6.5.5

2009-12-11 23:31:29

by Greg KH

[permalink] [raw]
Subject: [PATCH 46/58] tty: isicom: fix deadlock on shutdown

From: Alan Cox <[email protected]>

Alexander Strakh <[email protected]> reported

KERNEL_VERSION: 2.6.31
DESCRIBE:
Driver drivers/char/isicom.c might sleep in atomic context, because it
calls
tty_port_xmit_buf under spin_lock.

./drivers/char/isicom.c:
1307 static void isicom_hangup(struct tty_struct *tty)
1308 {
...
1315 spin_lock_irqsave(&port->card->card_lock, flags);
1316 isicom_shutdown_port(port);
...

Path to might_sleep macro from isicom_hangup:
1. isicom_hangup calls spin_lock_irqsave (drivers/char/isicom.c:1315) and
then
calls isicom_shutdown_port.
2. isiscom_shutdown_port calls tty_port_free_xmit_buf at
drivers/char/isicom.c:906
3. tty_port_free_xmit_buf calls mutex_lock at drivers/char/tty_port:48

Found by Linux Driver Verification Project.

Reported-by: Alexander Strakh <[email protected]>
Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/isicom.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
index 1e91c30..300d5bd 100644
--- a/drivers/char/isicom.c
+++ b/drivers/char/isicom.c
@@ -887,7 +887,6 @@ static void isicom_shutdown_port(struct isi_port *port)
{
struct isi_board *card = port->card;

- tty_port_free_xmit_buf(&port->port);
if (--card->count < 0) {
pr_dbg("isicom_shutdown_port: bad board(0x%lx) count %d.\n",
card->base, card->count);
@@ -927,6 +926,7 @@ static void isicom_shutdown(struct tty_port *port)
outw(card->port_status, card->base + 0x02);
isicom_shutdown_port(ip);
spin_unlock_irqrestore(&card->card_lock, flags);
+ tty_port_free_xmit_buf(port);
}

static void isicom_close(struct tty_struct *tty, struct file *filp)
--
1.6.5.5

2009-12-11 23:33:30

by Greg KH

[permalink] [raw]
Subject: [PATCH 47/58] tty: moxa: Use more tty_port ops

From: Alan Cox <[email protected]>

Rework a few bits of this into tty_port format

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/moxa.c | 137 ++++++++-------------------------------------------
1 files changed, 21 insertions(+), 116 deletions(-)

diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index dd0083b..d8bcbed 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -206,8 +206,9 @@ static int moxa_tiocmset(struct tty_struct *tty, struct file *file,
static void moxa_poll(unsigned long);
static void moxa_set_tty_param(struct tty_struct *, struct ktermios *);
static void moxa_setup_empty_event(struct tty_struct *);
-static void moxa_shut_down(struct tty_struct *);
+static void moxa_shutdown(struct tty_port *);
static int moxa_carrier_raised(struct tty_port *);
+static void moxa_dtr_rts(struct tty_port *, int);
/*
* moxa board interface functions:
*/
@@ -409,6 +410,8 @@ static const struct tty_operations moxa_ops = {

static const struct tty_port_operations moxa_port_ops = {
.carrier_raised = moxa_carrier_raised,
+ .dtr_rts = moxa_dtr_rts,
+ .shutdown = moxa_shutdown,
};

static struct tty_driver *moxaDriver;
@@ -1112,14 +1115,12 @@ static void __exit moxa_exit(void)
module_init(moxa_init);
module_exit(moxa_exit);

-static void moxa_close_port(struct tty_struct *tty)
+static void moxa_shutdown(struct tty_port *port)
{
- struct moxa_port *ch = tty->driver_data;
- moxa_shut_down(tty);
+ struct moxa_port *ch = container_of(port, struct moxa_port, port);
+ MoxaPortDisable(ch);
MoxaPortFlushData(ch, 2);
- ch->port.flags &= ~ASYNC_NORMAL_ACTIVE;
- tty->driver_data = NULL;
- tty_port_tty_set(&ch->port, NULL);
+ clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
}

static int moxa_carrier_raised(struct tty_port *port)
@@ -1133,39 +1134,13 @@ static int moxa_carrier_raised(struct tty_port *port)
return dcd;
}

-static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp,
- struct moxa_port *ch)
+static void moxa_dtr_rts(struct tty_port *port, int onoff)
{
- struct tty_port *port = &ch->port;
- DEFINE_WAIT(wait);
- int retval = 0;
- u8 dcd;
-
- while (1) {
- prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE);
- if (tty_hung_up_p(filp)) {
-#ifdef SERIAL_DO_RESTART
- retval = -ERESTARTSYS;
-#else
- retval = -EAGAIN;
-#endif
- break;
- }
- dcd = tty_port_carrier_raised(port);
- if (dcd)
- break;
-
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
- schedule();
- }
- finish_wait(&port->open_wait, &wait);
-
- return retval;
+ struct moxa_port *ch = container_of(port, struct moxa_port, port);
+ MoxaPortLineCtrl(ch, onoff, onoff);
}

+
static int moxa_open(struct tty_struct *tty, struct file *filp)
{
struct moxa_board_conf *brd;
@@ -1194,6 +1169,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
ch->port.count++;
tty->driver_data = ch;
tty_port_tty_set(&ch->port, tty);
+ mutex_lock(&ch->port.mutex);
if (!(ch->port.flags & ASYNC_INITIALIZED)) {
ch->statusflags = 0;
moxa_set_tty_param(tty, tty->termios);
@@ -1202,57 +1178,21 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
MoxaSetFifo(ch, ch->type == PORT_16550A);
ch->port.flags |= ASYNC_INITIALIZED;
}
+ mutex_unlock(&ch->port.mutex);
mutex_unlock(&moxa_openlock);

- retval = 0;
- if (!(filp->f_flags & O_NONBLOCK) && !C_CLOCAL(tty))
- retval = moxa_block_till_ready(tty, filp, ch);
- mutex_lock(&moxa_openlock);
- if (retval) {
- if (ch->port.count) /* 0 means already hung up... */
- if (--ch->port.count == 0)
- moxa_close_port(tty);
- } else
- ch->port.flags |= ASYNC_NORMAL_ACTIVE;
- mutex_unlock(&moxa_openlock);
-
+ retval = tty_port_block_til_ready(&ch->port, tty, filp);
+ if (retval == 0)
+ set_bit(ASYNCB_NORMAL_ACTIVE, &ch->port.flags);
return retval;
}

static void moxa_close(struct tty_struct *tty, struct file *filp)
{
- struct moxa_port *ch;
- int port;
-
- port = tty->index;
- if (port == MAX_PORTS || tty_hung_up_p(filp))
- return;
-
- mutex_lock(&moxa_openlock);
- ch = tty->driver_data;
- if (ch == NULL)
- goto unlock;
- if (tty->count == 1 && ch->port.count != 1) {
- printk(KERN_WARNING "moxa_close: bad serial port count; "
- "tty->count is 1, ch->port.count is %d\n", ch->port.count);
- ch->port.count = 1;
- }
- if (--ch->port.count < 0) {
- printk(KERN_WARNING "moxa_close: bad serial port count, "
- "device=%s\n", tty->name);
- ch->port.count = 0;
- }
- if (ch->port.count)
- goto unlock;
-
+ struct moxa_port *ch = tty->driver_data;
ch->cflag = tty->termios->c_cflag;
- if (ch->port.flags & ASYNC_INITIALIZED) {
- moxa_setup_empty_event(tty);
- tty_wait_until_sent(tty, 30 * HZ); /* 30 seconds timeout */
- }
-
- moxa_close_port(tty);
-unlock:
+ mutex_lock(&moxa_openlock);
+ tty_port_close(&ch->port, tty, filp);
mutex_unlock(&moxa_openlock);
}

@@ -1300,14 +1240,6 @@ static int moxa_chars_in_buffer(struct tty_struct *tty)
struct moxa_port *ch = tty->driver_data;
int chars;

- /*
- * Sigh...I have to check if driver_data is NULL here, because
- * if an open() fails, the TTY subsystem eventually calls
- * tty_wait_until_sent(), which calls the driver's chars_in_buffer()
- * routine. And since the open() failed, we return 0 here. TDJ
- */
- if (ch == NULL)
- return 0;
lock_kernel();
chars = MoxaPortTxQueue(ch);
if (chars) {
@@ -1436,15 +1368,8 @@ static void moxa_hangup(struct tty_struct *tty)

mutex_lock(&moxa_openlock);
ch = tty->driver_data;
- if (ch == NULL) {
- mutex_unlock(&moxa_openlock);
- return;
- }
- ch->port.count = 0;
- moxa_close_port(tty);
+ tty_port_hangup(&ch->port);
mutex_unlock(&moxa_openlock);
-
- wake_up_interruptible(&ch->port.open_wait);
}

static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd)
@@ -1597,26 +1522,6 @@ static void moxa_setup_empty_event(struct tty_struct *tty)
spin_unlock_bh(&moxa_lock);
}

-static void moxa_shut_down(struct tty_struct *tty)
-{
- struct moxa_port *ch = tty->driver_data;
-
- if (!(ch->port.flags & ASYNC_INITIALIZED))
- return;
-
- MoxaPortDisable(ch);
-
- /*
- * If we're a modem control device and HUPCL is on, drop RTS & DTR.
- */
- if (C_HUPCL(tty))
- MoxaPortLineCtrl(ch, 0, 0);
-
- spin_lock_bh(&moxa_lock);
- ch->port.flags &= ~ASYNC_INITIALIZED;
- spin_unlock_bh(&moxa_lock);
-}
-
/*****************************************************************************
* Driver level functions: *
*****************************************************************************/
--
1.6.5.5

2009-12-11 23:32:42

by Greg KH

[permalink] [raw]
Subject: [PATCH 48/58] tty: moxa: rework the locking a bit

From: Alan Cox <[email protected]>

Introduce a lock for moxafunc() to protect the cases where were get collisions
between two function requests at the same time.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/moxa.c | 27 ++++++++++++++++++++++-----
1 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index d8bcbed..474d936 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -248,9 +248,25 @@ static void moxa_wait_finish(void __iomem *ofsAddr)

static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg)
{
+ unsigned long flags;
+ spin_lock_irqsave(&moxafunc_lock, flags);
writew(arg, ofsAddr + FuncArg);
writew(cmd, ofsAddr + FuncCode);
moxa_wait_finish(ofsAddr);
+ spin_unlock_irqrestore(&moxafunc_lock, flags);
+}
+
+static int moxafuncret(void __iomem *ofsAddr, u16 cmd, u16 arg)
+{
+ unsigned long flags;
+ u16 ret;
+ spin_lock_irqsave(&moxafunc_lock, flags);
+ writew(arg, ofsAddr + FuncArg);
+ writew(cmd, ofsAddr + FuncCode);
+ moxa_wait_finish(ofsAddr);
+ ret = readw(ofsAddr + FuncArg);
+ spin_unlock_irqrestore(&moxafunc_lock, flags);
+ return ret;
}

static void moxa_low_water_check(void __iomem *ofsAddr)
@@ -417,6 +433,7 @@ static const struct tty_port_operations moxa_port_ops = {
static struct tty_driver *moxaDriver;
static DEFINE_TIMER(moxaTimer, moxa_poll, 0, 0);
static DEFINE_SPINLOCK(moxa_lock);
+static DEFINE_SPINLOCK(moxafunc_lock);

/*
* HW init
@@ -1823,10 +1840,12 @@ static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio,
baud = MoxaPortSetBaud(port, baud);

if (termio->c_iflag & (IXON | IXOFF | IXANY)) {
+ spin_lock_irq(&moxafunc_lock);
writeb(termio->c_cc[VSTART], ofsAddr + FuncArg);
writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1);
writeb(FC_SetXonXoff, ofsAddr + FuncCode);
moxa_wait_finish(ofsAddr);
+ spin_unlock_irqrestore(&moxafunc_lock);

}
return baud;
@@ -1879,12 +1898,10 @@ static int MoxaPortLineStatus(struct moxa_port *port)
int val;

ofsAddr = port->tableAddr;
- if (MOXA_IS_320(port->board)) {
- moxafunc(ofsAddr, FC_LineStatus, 0);
- val = readw(ofsAddr + FuncArg);
- } else {
+ if (MOXA_IS_320(port->board))
+ val = moxafuncret(ofsAddr, FC_LineStatus, 0);
+ else
val = readw(ofsAddr + FlagStat) >> 4;
- }
val &= 0x0B;
if (val & 8)
val |= 4;
--
1.6.5.5

2009-12-11 23:32:34

by Greg KH

[permalink] [raw]
Subject: [PATCH 49/58] tty: moxa: Locking clean up

From: Alan Cox <[email protected]>

- The open lock is needed to fix up the case of a board reset occuring during
tty open but too early for a sane hangup response.
- The lock can however got for other cases
- Use the port mutex for get/setserial
- Fix up the confused lack of locking on the THROTTLE and other bits in the
private flags. Just use set/test/clear bit and it covers the cases we need

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/moxa.c | 58 ++++++++++++++++++++++-----------------------------
1 files changed, 25 insertions(+), 33 deletions(-)

diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index 474d936..7ab720f 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -151,10 +151,10 @@ struct mon_str {
};

/* statusflags */
-#define TXSTOPPED 0x1
-#define LOWWAIT 0x2
-#define EMPTYWAIT 0x4
-#define THROTTLE 0x8
+#define TXSTOPPED 1
+#define LOWWAIT 2
+#define EMPTYWAIT 3
+#define THROTTLE 4

#define SERIAL_DO_RESTART

@@ -235,6 +235,8 @@ static void MoxaSetFifo(struct moxa_port *port, int enable);
* I/O functions
*/

+static DEFINE_SPINLOCK(moxafunc_lock);
+
static void moxa_wait_finish(void __iomem *ofsAddr)
{
unsigned long end = jiffies + moxaFuncTout;
@@ -381,14 +383,14 @@ copy:
break;
}
case TIOCGSERIAL:
- mutex_lock(&moxa_openlock);
+ mutex_lock(&ch->port.mutex);
ret = moxa_get_serial_info(ch, argp);
- mutex_unlock(&moxa_openlock);
+ mutex_unlock(&ch->port.mutex);
break;
case TIOCSSERIAL:
- mutex_lock(&moxa_openlock);
+ mutex_lock(&ch->port.mutex);
ret = moxa_set_serial_info(ch, argp);
- mutex_unlock(&moxa_openlock);
+ mutex_unlock(&ch->port.mutex);
break;
default:
ret = -ENOIOCTLCMD;
@@ -433,7 +435,6 @@ static const struct tty_port_operations moxa_port_ops = {
static struct tty_driver *moxaDriver;
static DEFINE_TIMER(moxaTimer, moxa_poll, 0, 0);
static DEFINE_SPINLOCK(moxa_lock);
-static DEFINE_SPINLOCK(moxafunc_lock);

/*
* HW init
@@ -1208,9 +1209,7 @@ static void moxa_close(struct tty_struct *tty, struct file *filp)
{
struct moxa_port *ch = tty->driver_data;
ch->cflag = tty->termios->c_cflag;
- mutex_lock(&moxa_openlock);
tty_port_close(&ch->port, tty, filp);
- mutex_unlock(&moxa_openlock);
}

static int moxa_write(struct tty_struct *tty,
@@ -1226,7 +1225,7 @@ static int moxa_write(struct tty_struct *tty,
len = MoxaPortWriteData(tty, buf, count);
spin_unlock_bh(&moxa_lock);

- ch->statusflags |= LOWWAIT;
+ set_bit(LOWWAIT, &ch->statusflags);
return len;
}

@@ -1264,7 +1263,7 @@ static int moxa_chars_in_buffer(struct tty_struct *tty)
* Make it possible to wakeup anything waiting for output
* in tty_ioctl.c, etc.
*/
- if (!(ch->statusflags & EMPTYWAIT))
+ if (!test_bit(EMPTYWAIT, &ch->statusflags))
moxa_setup_empty_event(tty);
}
unlock_kernel();
@@ -1332,14 +1331,14 @@ static void moxa_throttle(struct tty_struct *tty)
{
struct moxa_port *ch = tty->driver_data;

- ch->statusflags |= THROTTLE;
+ set_bit(THROTTLE, &ch->statusflags);
}

static void moxa_unthrottle(struct tty_struct *tty)
{
struct moxa_port *ch = tty->driver_data;

- ch->statusflags &= ~THROTTLE;
+ clear_bit(THROTTLE, &ch->statusflags);
}

static void moxa_set_termios(struct tty_struct *tty,
@@ -1361,7 +1360,7 @@ static void moxa_stop(struct tty_struct *tty)
if (ch == NULL)
return;
MoxaPortTxDisable(ch);
- ch->statusflags |= TXSTOPPED;
+ set_bit(TXSTOPPED, &ch->statusflags);
}


@@ -1376,17 +1375,13 @@ static void moxa_start(struct tty_struct *tty)
return;

MoxaPortTxEnable(ch);
- ch->statusflags &= ~TXSTOPPED;
+ clear_bit(TXSTOPPED, &ch->statusflags);
}

static void moxa_hangup(struct tty_struct *tty)
{
- struct moxa_port *ch;
-
- mutex_lock(&moxa_openlock);
- ch = tty->driver_data;
+ struct moxa_port *ch = tty->driver_data;
tty_port_hangup(&ch->port);
- mutex_unlock(&moxa_openlock);
}

static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd)
@@ -1412,24 +1407,24 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
u16 intr;

if (tty) {
- if ((p->statusflags & EMPTYWAIT) &&
+ if (test_bit(EMPTYWAIT, &p->statusflags) &&
MoxaPortTxQueue(p) == 0) {
- p->statusflags &= ~EMPTYWAIT;
+ clear_bit(EMPTYWAIT, &p->statusflags);
tty_wakeup(tty);
}
- if ((p->statusflags & LOWWAIT) && !tty->stopped &&
+ if (test_bit(LOWWAIT, &p->statusflags) && !tty->stopped &&
MoxaPortTxQueue(p) <= WAKEUP_CHARS) {
- p->statusflags &= ~LOWWAIT;
+ clear_bit(LOWWAIT, &p->statusflags);
tty_wakeup(tty);
}

- if (inited && !(p->statusflags & THROTTLE) &&
+ if (inited && !test_bit(THROTTLE, &p->statusflags) &&
MoxaPortRxQueue(p) > 0) { /* RX */
MoxaPortReadData(p);
tty_schedule_flip(tty);
}
} else {
- p->statusflags &= ~EMPTYWAIT;
+ clear_bit(EMPTYWAIT, &p->statusflags);
MoxaPortFlushData(p, 0); /* flush RX */
}

@@ -1533,10 +1528,7 @@ static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_term
static void moxa_setup_empty_event(struct tty_struct *tty)
{
struct moxa_port *ch = tty->driver_data;
-
- spin_lock_bh(&moxa_lock);
- ch->statusflags |= EMPTYWAIT;
- spin_unlock_bh(&moxa_lock);
+ set_bit(EMPTYWAIT, &ch->statusflags);
}

/*****************************************************************************
@@ -1845,7 +1837,7 @@ static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio,
writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1);
writeb(FC_SetXonXoff, ofsAddr + FuncCode);
moxa_wait_finish(ofsAddr);
- spin_unlock_irqrestore(&moxafunc_lock);
+ spin_unlock_irq(&moxafunc_lock);

}
return baud;
--
1.6.5.5

2009-12-11 23:32:25

by Greg KH

[permalink] [raw]
Subject: [PATCH 50/58] tty: moxa: Kill off the throttle method

From: Alan Cox <[email protected]>

The tty flag can be tested so the shadow flag isn't needed

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/moxa.c | 21 +--------------------
1 files changed, 1 insertions(+), 20 deletions(-)

diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index 7ab720f..d180aa9 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -154,7 +154,6 @@ struct mon_str {
#define TXSTOPPED 1
#define LOWWAIT 2
#define EMPTYWAIT 3
-#define THROTTLE 4

#define SERIAL_DO_RESTART

@@ -194,8 +193,6 @@ static int moxa_write(struct tty_struct *, const unsigned char *, int);
static int moxa_write_room(struct tty_struct *);
static void moxa_flush_buffer(struct tty_struct *);
static int moxa_chars_in_buffer(struct tty_struct *);
-static void moxa_throttle(struct tty_struct *);
-static void moxa_unthrottle(struct tty_struct *);
static void moxa_set_termios(struct tty_struct *, struct ktermios *);
static void moxa_stop(struct tty_struct *);
static void moxa_start(struct tty_struct *);
@@ -415,8 +412,6 @@ static const struct tty_operations moxa_ops = {
.flush_buffer = moxa_flush_buffer,
.chars_in_buffer = moxa_chars_in_buffer,
.ioctl = moxa_ioctl,
- .throttle = moxa_throttle,
- .unthrottle = moxa_unthrottle,
.set_termios = moxa_set_termios,
.stop = moxa_stop,
.start = moxa_start,
@@ -1327,20 +1322,6 @@ static int moxa_tiocmset(struct tty_struct *tty, struct file *file,
return 0;
}

-static void moxa_throttle(struct tty_struct *tty)
-{
- struct moxa_port *ch = tty->driver_data;
-
- set_bit(THROTTLE, &ch->statusflags);
-}
-
-static void moxa_unthrottle(struct tty_struct *tty)
-{
- struct moxa_port *ch = tty->driver_data;
-
- clear_bit(THROTTLE, &ch->statusflags);
-}
-
static void moxa_set_termios(struct tty_struct *tty,
struct ktermios *old_termios)
{
@@ -1418,7 +1399,7 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
tty_wakeup(tty);
}

- if (inited && !test_bit(THROTTLE, &p->statusflags) &&
+ if (inited && !test_bit(TTY_THROTTLED, &tty->flags) &&
MoxaPortRxQueue(p) > 0) { /* RX */
MoxaPortReadData(p);
tty_schedule_flip(tty);
--
1.6.5.5

2009-12-11 23:29:56

by Greg KH

[permalink] [raw]
Subject: [PATCH 51/58] tty: moxa: Fix modem op locking

From: Alan Cox <[email protected]>

This is overkill and mostly not needed

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/moxa.c | 25 ++++++++++---------------
1 files changed, 10 insertions(+), 15 deletions(-)

diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index d180aa9..ac06d01 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -139,7 +139,7 @@ struct moxa_port {
int cflag;
unsigned long statusflags;

- u8 DCDState;
+ u8 DCDState; /* Protected by the port lock */
u8 lineCtrl;
u8 lowChkFlag;
};
@@ -1141,9 +1141,9 @@ static int moxa_carrier_raised(struct tty_port *port)
struct moxa_port *ch = container_of(port, struct moxa_port, port);
int dcd;

- spin_lock_bh(&moxa_lock);
+ spin_lock_irq(&port->lock);
dcd = ch->DCDState;
- spin_unlock_bh(&moxa_lock);
+ spin_unlock_irq(&port->lock);
return dcd;
}

@@ -1267,16 +1267,9 @@ static int moxa_chars_in_buffer(struct tty_struct *tty)

static int moxa_tiocmget(struct tty_struct *tty, struct file *file)
{
- struct moxa_port *ch;
+ struct moxa_port *ch = tty->driver_data;
int flag = 0, dtr, rts;

- mutex_lock(&moxa_openlock);
- ch = tty->driver_data;
- if (!ch) {
- mutex_unlock(&moxa_openlock);
- return -EINVAL;
- }
-
MoxaPortGetLineOut(ch, &dtr, &rts);
if (dtr)
flag |= TIOCM_DTR;
@@ -1289,7 +1282,6 @@ static int moxa_tiocmget(struct tty_struct *tty, struct file *file)
flag |= TIOCM_DSR;
if (dtr & 4)
flag |= TIOCM_CD;
- mutex_unlock(&moxa_openlock);
return flag;
}

@@ -1368,15 +1360,20 @@ static void moxa_hangup(struct tty_struct *tty)
static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd)
{
struct tty_struct *tty;
+ unsigned long flags;
dcd = !!dcd;

+ spin_lock_irqsave(&p->port.lock, flags);
if (dcd != p->DCDState) {
+ p->DCDState = dcd;
+ spin_unlock_irqrestore(&p->port.lock, flags);
tty = tty_port_tty_get(&p->port);
if (tty && C_CLOCAL(tty) && !dcd)
tty_hangup(tty);
tty_kref_put(tty);
}
- p->DCDState = dcd;
+ else
+ spin_unlock_irqrestore(&p->port.lock, flags);
}

static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
@@ -1878,9 +1875,7 @@ static int MoxaPortLineStatus(struct moxa_port *port)
val &= 0x0B;
if (val & 8)
val |= 4;
- spin_lock_bh(&moxa_lock);
moxa_new_dcdstate(port, val & 8);
- spin_unlock_bh(&moxa_lock);
val &= 7;
return val;
}
--
1.6.5.5

2009-12-11 23:31:18

by Greg KH

[permalink] [raw]
Subject: [PATCH 52/58] tty: moxa: Kill the use of lock_kernel

From: Alan Cox <[email protected]>

It isn't needed here any more

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/moxa.c | 16 ++--------------
1 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index ac06d01..d53fac5 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -34,7 +34,6 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
-#include <linux/smp_lock.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
@@ -202,7 +201,6 @@ static int moxa_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
static void moxa_poll(unsigned long);
static void moxa_set_tty_param(struct tty_struct *, struct ktermios *);
-static void moxa_setup_empty_event(struct tty_struct *);
static void moxa_shutdown(struct tty_port *);
static int moxa_carrier_raised(struct tty_port *);
static void moxa_dtr_rts(struct tty_port *, int);
@@ -1251,17 +1249,13 @@ static int moxa_chars_in_buffer(struct tty_struct *tty)
struct moxa_port *ch = tty->driver_data;
int chars;

- lock_kernel();
chars = MoxaPortTxQueue(ch);
- if (chars) {
+ if (chars)
/*
* Make it possible to wakeup anything waiting for output
* in tty_ioctl.c, etc.
*/
- if (!test_bit(EMPTYWAIT, &ch->statusflags))
- moxa_setup_empty_event(tty);
- }
- unlock_kernel();
+ set_bit(EMPTYWAIT, &ch->statusflags);
return chars;
}

@@ -1503,12 +1497,6 @@ static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_term
tty_encode_baud_rate(tty, baud, baud);
}

-static void moxa_setup_empty_event(struct tty_struct *tty)
-{
- struct moxa_port *ch = tty->driver_data;
- set_bit(EMPTYWAIT, &ch->statusflags);
-}
-
/*****************************************************************************
* Driver level functions: *
*****************************************************************************/
--
1.6.5.5

2009-12-11 23:31:50

by Greg KH

[permalink] [raw]
Subject: [PATCH 53/58] tty: moxa: split open lock

From: Alan Cox <[email protected]>

moxa_openlock is used for several situations where we want to handle the
case of an ioctl that crosses many ports (not just the open tty), and also
cases where an open races a deinit (eg a pci unplug) and we hangup a port
before we can cope with that.

The non open race cases can use the moxa_lock spinlock. This simplifies sorting
out the remaining mess.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/moxa.c | 23 +++++++++++------------
1 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index d53fac5..63ee3bb 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -163,6 +163,7 @@ static struct mon_str moxaLog;
static unsigned int moxaFuncTout = HZ / 2;
static unsigned int moxaLowWaterChk;
static DEFINE_MUTEX(moxa_openlock);
+static DEFINE_SPINLOCK(moxa_lock);
/* Variables for insmod */
#ifdef MODULE
static unsigned long baseaddr[MAX_BOARDS];
@@ -313,22 +314,20 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file,
struct moxa_port *p;
unsigned int i, j;

- mutex_lock(&moxa_openlock);
for (i = 0; i < MAX_BOARDS; i++) {
p = moxa_boards[i].ports;
for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
memset(&tmp, 0, sizeof(tmp));
+ spin_lock_bh(&moxa_lock);
if (moxa_boards[i].ready) {
tmp.inq = MoxaPortRxQueue(p);
tmp.outq = MoxaPortTxQueue(p);
}
- if (copy_to_user(argm, &tmp, sizeof(tmp))) {
- mutex_unlock(&moxa_openlock);
+ spin_unlock_bh(&moxa_lock);
+ if (copy_to_user(argm, &tmp, sizeof(tmp)))
return -EFAULT;
- }
}
}
- mutex_unlock(&moxa_openlock);
break;
} case MOXA_GET_OQUEUE:
status = MoxaPortTxQueue(ch);
@@ -344,16 +343,20 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file,
struct moxa_port *p;
unsigned int i, j;

- mutex_lock(&moxa_openlock);
for (i = 0; i < MAX_BOARDS; i++) {
p = moxa_boards[i].ports;
for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
struct tty_struct *ttyp;
memset(&tmp, 0, sizeof(tmp));
- if (!moxa_boards[i].ready)
+ spin_lock_bh(&moxa_lock);
+ if (!moxa_boards[i].ready) {
+ spin_unlock_bh(&moxa_lock);
goto copy;
+ }

status = MoxaPortLineStatus(p);
+ spin_unlock_bh(&moxa_lock);
+
if (status & 1)
tmp.cts = 1;
if (status & 2)
@@ -368,13 +371,10 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file,
tmp.cflag = ttyp->termios->c_cflag;
tty_kref_put(tty);
copy:
- if (copy_to_user(argm, &tmp, sizeof(tmp))) {
- mutex_unlock(&moxa_openlock);
+ if (copy_to_user(argm, &tmp, sizeof(tmp)))
return -EFAULT;
- }
}
}
- mutex_unlock(&moxa_openlock);
break;
}
case TIOCGSERIAL:
@@ -427,7 +427,6 @@ static const struct tty_port_operations moxa_port_ops = {

static struct tty_driver *moxaDriver;
static DEFINE_TIMER(moxaTimer, moxa_poll, 0, 0);
-static DEFINE_SPINLOCK(moxa_lock);

/*
* HW init
--
1.6.5.5

2009-12-11 23:32:09

by Greg KH

[permalink] [raw]
Subject: [PATCH 54/58] tty: push the BKL down into the handlers a bit

From: Alan Cox <[email protected]>

Start trying to untangle the remaining BKL mess

Updated to fix missing unlock_kernel noted by Dan Carpenter

Signed-off-by: Alan "I must be out of my tree" Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/pty.c | 2 +-
drivers/char/tty_io.c | 141 ++++++++++++++++++++++++++--------------------
drivers/char/tty_ldisc.c | 13 ++++
include/linux/tty.h | 2 +-
4 files changed, 95 insertions(+), 63 deletions(-)

diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index d86c0bc..385c44b 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -659,7 +659,7 @@ static int __ptmx_open(struct inode *inode, struct file *filp)
if (!retval)
return 0;
out1:
- tty_release_dev(filp);
+ tty_release(inode, filp);
return retval;
out:
devpts_kill_index(inode, index);
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 59499ee..1e24130 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -142,7 +142,6 @@ ssize_t redirected_tty_write(struct file *, const char __user *,
size_t, loff_t *);
static unsigned int tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *);
-static int tty_release(struct inode *, struct file *);
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_COMPAT
static long tty_compat_ioctl(struct file *file, unsigned int cmd,
@@ -1017,14 +1016,16 @@ out:

void tty_write_message(struct tty_struct *tty, char *msg)
{
- lock_kernel();
if (tty) {
mutex_lock(&tty->atomic_write_lock);
- if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags))
+ lock_kernel();
+ if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
+ unlock_kernel();
tty->ops->write(tty, msg, strlen(msg));
+ } else
+ unlock_kernel();
tty_write_unlock(tty);
}
- unlock_kernel();
return;
}

@@ -1202,14 +1203,21 @@ static int tty_driver_install_tty(struct tty_driver *driver,
struct tty_struct *tty)
{
int idx = tty->index;
+ int ret;

- if (driver->ops->install)
- return driver->ops->install(driver, tty);
+ if (driver->ops->install) {
+ lock_kernel();
+ ret = driver->ops->install(driver, tty);
+ unlock_kernel();
+ return ret;
+ }

if (tty_init_termios(tty) == 0) {
+ lock_kernel();
tty_driver_kref_get(driver);
tty->count++;
driver->ttys[idx] = tty;
+ unlock_kernel();
return 0;
}
return -ENOMEM;
@@ -1302,10 +1310,14 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
struct tty_struct *tty;
int retval;

+ lock_kernel();
/* Check if pty master is being opened multiple times */
if (driver->subtype == PTY_TYPE_MASTER &&
- (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)
+ (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
+ unlock_kernel();
return ERR_PTR(-EIO);
+ }
+ unlock_kernel();

/*
* First time open is complex, especially for PTY devices.
@@ -1335,8 +1347,9 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
* If we fail here just call release_tty to clean up. No need
* to decrement the use counts, as release_tty doesn't care.
*/
-
+ lock_kernel();
retval = tty_ldisc_setup(tty, tty->link);
+ unlock_kernel();
if (retval)
goto release_mem_out;
return tty;
@@ -1350,7 +1363,9 @@ release_mem_out:
if (printk_ratelimit())
printk(KERN_INFO "tty_init_dev: ldisc open failed, "
"clearing slot %d\n", idx);
+ lock_kernel();
release_tty(tty, idx);
+ unlock_kernel();
return ERR_PTR(retval);
}

@@ -1464,7 +1479,17 @@ static void release_tty(struct tty_struct *tty, int idx)
tty_kref_put(tty);
}

-/*
+/**
+ * tty_release - vfs callback for close
+ * @inode: inode of tty
+ * @filp: file pointer for handle to tty
+ *
+ * Called the last time each file handle is closed that references
+ * this tty. There may however be several such references.
+ *
+ * Locking:
+ * Takes bkl. See tty_release_dev
+ *
* Even releasing the tty structures is a tricky business.. We have
* to be very careful that the structures are all released at the
* same time, as interrupts might otherwise get the wrong pointers.
@@ -1472,20 +1497,20 @@ static void release_tty(struct tty_struct *tty, int idx)
* WSH 09/09/97: rewritten to avoid some nasty race conditions that could
* lead to double frees or releasing memory still in use.
*/
-void tty_release_dev(struct file *filp)
+
+int tty_release(struct inode *inode, struct file *filp)
{
struct tty_struct *tty, *o_tty;
int pty_master, tty_closing, o_tty_closing, do_sleep;
int devpts;
int idx;
char buf[64];
- struct inode *inode;

- inode = filp->f_path.dentry->d_inode;
tty = (struct tty_struct *)filp->private_data;
if (tty_paranoia_check(tty, inode, "tty_release_dev"))
- return;
+ return 0;

+ lock_kernel();
check_tty_count(tty, "tty_release_dev");

tty_fasync(-1, filp, 0);
@@ -1500,19 +1525,22 @@ void tty_release_dev(struct file *filp)
if (idx < 0 || idx >= tty->driver->num) {
printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
"free (%s)\n", tty->name);
- return;
+ unlock_kernel();
+ return 0;
}
if (!devpts) {
if (tty != tty->driver->ttys[idx]) {
+ unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
"for (%s)\n", idx, tty->name);
- return;
+ return 0;
}
if (tty->termios != tty->driver->termios[idx]) {
+ unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
"for (%s)\n",
idx, tty->name);
- return;
+ return 0;
}
}
#endif
@@ -1526,26 +1554,30 @@ void tty_release_dev(struct file *filp)
if (tty->driver->other &&
!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
if (o_tty != tty->driver->other->ttys[idx]) {
+ unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
"not o_tty for (%s)\n",
idx, tty->name);
- return;
+ return 0 ;
}
if (o_tty->termios != tty->driver->other->termios[idx]) {
+ unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
"not o_termios for (%s)\n",
idx, tty->name);
- return;
+ return 0;
}
if (o_tty->link != tty) {
+ unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
- return;
+ return 0;
}
}
#endif
if (tty->ops->close)
tty->ops->close(tty, filp);

+ unlock_kernel();
/*
* 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
@@ -1568,6 +1600,7 @@ void tty_release_dev(struct file *filp)
opens on /dev/tty */

mutex_lock(&tty_mutex);
+ lock_kernel();
tty_closing = tty->count <= 1;
o_tty_closing = o_tty &&
(o_tty->count <= (pty_master ? 1 : 0));
@@ -1598,6 +1631,7 @@ void tty_release_dev(struct file *filp)

printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
"active!\n", tty_name(tty, buf));
+ unlock_kernel();
mutex_unlock(&tty_mutex);
schedule();
}
@@ -1661,8 +1695,10 @@ void tty_release_dev(struct file *filp)
mutex_unlock(&tty_mutex);

/* check whether both sides are closing ... */
- if (!tty_closing || (o_tty && !o_tty_closing))
- return;
+ if (!tty_closing || (o_tty && !o_tty_closing)) {
+ unlock_kernel();
+ return 0;
+ }

#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "freeing tty structure...");
@@ -1680,10 +1716,12 @@ void tty_release_dev(struct file *filp)
/* Make this pty number available for reallocation */
if (devpts)
devpts_kill_index(inode, idx);
+ unlock_kernel();
+ return 0;
}

/**
- * __tty_open - open a tty device
+ * tty_open - open a tty device
* @inode: inode of device file
* @filp: file pointer to tty
*
@@ -1703,7 +1741,7 @@ void tty_release_dev(struct file *filp)
* ->siglock protects ->signal/->sighand
*/

-static int __tty_open(struct inode *inode, struct file *filp)
+static int tty_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty = NULL;
int noctty, retval;
@@ -1720,10 +1758,12 @@ retry_open:
retval = 0;

mutex_lock(&tty_mutex);
+ lock_kernel();

if (device == MKDEV(TTYAUX_MAJOR, 0)) {
tty = get_current_tty();
if (!tty) {
+ unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENXIO;
}
@@ -1755,12 +1795,14 @@ retry_open:
goto got_driver;
}
}
+ unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENODEV;
}

driver = get_tty_driver(device, &index);
if (!driver) {
+ unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
@@ -1770,6 +1812,7 @@ got_driver:
tty = tty_driver_lookup_tty(driver, inode, index);

if (IS_ERR(tty)) {
+ unlock_kernel();
mutex_unlock(&tty_mutex);
return PTR_ERR(tty);
}
@@ -1784,8 +1827,10 @@ got_driver:

mutex_unlock(&tty_mutex);
tty_driver_kref_put(driver);
- if (IS_ERR(tty))
+ if (IS_ERR(tty)) {
+ unlock_kernel();
return PTR_ERR(tty);
+ }

filp->private_data = tty;
file_move(filp, &tty->tty_files);
@@ -1813,11 +1858,15 @@ got_driver:
printk(KERN_DEBUG "error %d in opening %s...", retval,
tty->name);
#endif
- tty_release_dev(filp);
- if (retval != -ERESTARTSYS)
+ tty_release(inode, filp);
+ if (retval != -ERESTARTSYS) {
+ unlock_kernel();
return retval;
- if (signal_pending(current))
+ }
+ if (signal_pending(current)) {
+ unlock_kernel();
return retval;
+ }
schedule();
/*
* Need to reset f_op in case a hangup happened.
@@ -1826,8 +1875,11 @@ got_driver:
filp->f_op = &tty_fops;
goto retry_open;
}
+ unlock_kernel();
+

mutex_lock(&tty_mutex);
+ lock_kernel();
spin_lock_irq(&current->sighand->siglock);
if (!noctty &&
current->signal->leader &&
@@ -1835,44 +1887,13 @@ got_driver:
tty->session == NULL)
__proc_set_tty(current, tty);
spin_unlock_irq(&current->sighand->siglock);
+ unlock_kernel();
mutex_unlock(&tty_mutex);
return 0;
}

-/* BKL pushdown: scary code avoidance wrapper */
-static int tty_open(struct inode *inode, struct file *filp)
-{
- int ret;
-
- lock_kernel();
- ret = __tty_open(inode, filp);
- unlock_kernel();
- return ret;
-}


-
-
-/**
- * tty_release - vfs callback for close
- * @inode: inode of tty
- * @filp: file pointer for handle to tty
- *
- * Called the last time each file handle is closed that references
- * this tty. There may however be several such references.
- *
- * Locking:
- * Takes bkl. See tty_release_dev
- */
-
-static int tty_release(struct inode *inode, struct file *filp)
-{
- lock_kernel();
- tty_release_dev(filp);
- unlock_kernel();
- return 0;
-}
-
/**
* tty_poll - check tty status
* @filp: file being polled
@@ -2317,9 +2338,7 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
if (get_user(ldisc, p))
return -EFAULT;

- lock_kernel();
ret = tty_set_ldisc(tty, ldisc);
- unlock_kernel();

return ret;
}
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index feb5507..d914e77 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -34,6 +34,8 @@
#include <linux/vt_kern.h>
#include <linux/selection.h>

+#include <linux/smp_lock.h> /* For the moment */
+
#include <linux/kmod.h>
#include <linux/nsproxy.h>

@@ -545,6 +547,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
if (IS_ERR(new_ldisc))
return PTR_ERR(new_ldisc);

+ lock_kernel();
/*
* We need to look at the tty locking here for pty/tty pairs
* when both sides try to change in parallel.
@@ -558,6 +561,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
*/

if (tty->ldisc->ops->num == ldisc) {
+ unlock_kernel();
tty_ldisc_put(new_ldisc);
return 0;
}
@@ -569,6 +573,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)

tty_wait_until_sent(tty, 0);

+ unlock_kernel();
mutex_lock(&tty->ldisc_mutex);

/*
@@ -582,6 +587,9 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
mutex_lock(&tty->ldisc_mutex);
}
+
+ lock_kernel();
+
set_bit(TTY_LDISC_CHANGING, &tty->flags);

/*
@@ -592,6 +600,8 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
tty->receive_room = 0;

o_ldisc = tty->ldisc;
+
+ unlock_kernel();
/*
* Make sure we don't change while someone holds a
* reference to the line discipline. The TTY_LDISC bit
@@ -617,12 +627,14 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
flush_scheduled_work();

mutex_lock(&tty->ldisc_mutex);
+ lock_kernel();
if (test_bit(TTY_HUPPED, &tty->flags)) {
/* We were raced by the hangup method. It will have stomped
the ldisc data and closed the ldisc down */
clear_bit(TTY_LDISC_CHANGING, &tty->flags);
mutex_unlock(&tty->ldisc_mutex);
tty_ldisc_put(new_ldisc);
+ unlock_kernel();
return -EIO;
}

@@ -664,6 +676,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
if (o_work)
schedule_delayed_work(&o_tty->buf.work, 1);
mutex_unlock(&tty->ldisc_mutex);
+ unlock_kernel();
return retval;
}

diff --git a/include/linux/tty.h b/include/linux/tty.h
index e6da667..405a903 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -449,7 +449,7 @@ extern void initialize_tty_struct(struct tty_struct *tty,
struct tty_driver *driver, int idx);
extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
int first_ok);
-extern void tty_release_dev(struct file *filp);
+extern int tty_release(struct inode *inode, struct file *filp);
extern int tty_init_termios(struct tty_struct *tty);

extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty);
--
1.6.5.5

2009-12-11 23:30:59

by Greg KH

[permalink] [raw]
Subject: [PATCH 55/58] tty: Push the lock down further into the ldisc code

From: Alan Cox <[email protected]>

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/tty_io.c | 2 --
drivers/char/tty_ldisc.c | 12 +++++++++---
2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 1e24130..c408c81 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -1347,9 +1347,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
* If we fail here just call release_tty to clean up. No need
* to decrement the use counts, as release_tty doesn't care.
*/
- lock_kernel();
retval = tty_ldisc_setup(tty, tty->link);
- unlock_kernel();
if (retval)
goto release_mem_out;
return tty;
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index d914e77..3f653f7 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -445,8 +445,14 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
{
WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
- if (ld->ops->open)
- return ld->ops->open(tty);
+ if (ld->ops->open) {
+ int ret;
+ /* BKL here locks verus a hangup event */
+ lock_kernel();
+ ret = ld->ops->open(tty);
+ unlock_kernel();
+ return ret;
+ }
return 0;
}

@@ -566,6 +572,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
return 0;
}

+ unlock_kernel();
/*
* Problem: What do we do if this blocks ?
* We could deadlock here
@@ -573,7 +580,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)

tty_wait_until_sent(tty, 0);

- unlock_kernel();
mutex_lock(&tty->ldisc_mutex);

/*
--
1.6.5.5

2009-12-11 23:30:46

by Greg KH

[permalink] [raw]
Subject: [PATCH 56/58] tty: Push the bkl down a bit in the hangup code

From: Alan Cox <[email protected]>

We know that the redirect field is handled via its own locking in all
places

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/tty_io.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index c408c81..cc941a3 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -505,8 +505,6 @@ static void do_tty_hangup(struct work_struct *work)
if (!tty)
return;

- /* inuse_filps is protected by the single kernel lock */
- lock_kernel();

spin_lock(&redirect_lock);
if (redirect && redirect->private_data == tty) {
@@ -515,6 +513,8 @@ static void do_tty_hangup(struct work_struct *work)
}
spin_unlock(&redirect_lock);

+ /* inuse_filps is protected by the single kernel lock */
+ lock_kernel();
check_tty_count(tty, "do_tty_hangup");
file_list_lock();
/* This breaks for file handles being sent over AF_UNIX sockets ? */
--
1.6.5.5

2009-12-11 23:30:16

by Greg KH

[permalink] [raw]
Subject: [PATCH 57/58] tty: Move the leader test in disassociate

From: Alan Cox <[email protected]>

There are two call points, both want to check that tty->signal->leader is
set. Move the test into disassociate_ctty() as that will make locking
changes easier in a bit

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/tty_io.c | 5 +++--
kernel/exit.c | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index cc941a3..a19fef2 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -707,6 +707,8 @@ void disassociate_ctty(int on_exit)
struct tty_struct *tty;
struct pid *tty_pgrp = NULL;

+ if (!current->signal->leader)
+ return;

tty = get_current_tty();
if (tty) {
@@ -772,8 +774,7 @@ void no_tty(void)
{
struct task_struct *tsk = current;
lock_kernel();
- if (tsk->signal->leader)
- disassociate_ctty(0);
+ disassociate_ctty(0);
unlock_kernel();
proc_clear_tty(tsk);
}
diff --git a/kernel/exit.c b/kernel/exit.c
index 1143012..6f50ef5 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -971,7 +971,7 @@ NORET_TYPE void do_exit(long code)
exit_thread();
cgroup_exit(tsk, 1);

- if (group_dead && tsk->signal->leader)
+ if (group_dead)
disassociate_ctty(1);

module_put(task_thread_info(tsk)->exec_domain->module);
--
1.6.5.5

2009-12-11 23:30:39

by Greg KH

[permalink] [raw]
Subject: [PATCH 58/58] tty: split the lock up a bit further

From: Alan Cox <[email protected]>

The tty count sanity check may need the BKL, that isn't clear. However it
is clear that the count use of the lock is internal and independant of the
bigger use of the lock.

Furthermore the file list locking is also separately locked already

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/char/tty_io.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index a19fef2..684f0e0 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -516,6 +516,8 @@ static void do_tty_hangup(struct work_struct *work)
/* inuse_filps is protected by the single kernel lock */
lock_kernel();
check_tty_count(tty, "do_tty_hangup");
+ unlock_kernel();
+
file_list_lock();
/* This breaks for file handles being sent over AF_UNIX sockets ? */
list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) {
@@ -529,6 +531,7 @@ static void do_tty_hangup(struct work_struct *work)
}
file_list_unlock();

+ lock_kernel();
tty_ldisc_hangup(tty);

read_lock(&tasklist_lock);
--
1.6.5.5

2009-12-12 08:46:31

by Ingo Molnar

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git


* Greg KH <[email protected]> wrote:

> Here's the big TTY patchset for your .33-git tree.

FYI, one of the changes in this tree is causing lockups on x86.

Config attached.

Possible suspects would one of these:

36ba782: tty: split the lock up a bit further
5ec93d1: tty: Move the leader test in disassociate
38c70b2: tty: Push the bkl down a bit in the hangup code
f18f949: tty: Push the lock down further into the ldisc code
eeb89d9: tty: push the BKL down into the handlers a bit

as they deal with locking details and are fresher than two weeks.

Ingo


Attachments:
(No filename) (562.00 B)
config (63.87 kB)
Download all attachments

2009-12-12 09:40:14

by Andrew Morton

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sat, 12 Dec 2009 09:46:11 +0100 Ingo Molnar <[email protected]> wrote:

> * Greg KH <[email protected]> wrote:
>
> > Here's the big TTY patchset for your .33-git tree.
>
> FYI, one of the changes in this tree is causing lockups on x86.
>
> Config attached.
>
> Possible suspects would one of these:
>
> 36ba782: tty: split the lock up a bit further
> 5ec93d1: tty: Move the leader test in disassociate
> 38c70b2: tty: Push the bkl down a bit in the hangup code
> f18f949: tty: Push the lock down further into the ldisc code
> eeb89d9: tty: push the BKL down into the handlers a bit
>
> as they deal with locking details and are fresher than two weeks.

yes, I started getting lockups yesterday when all this hit linux-next.
Seems to be quite .config-dependent.

I get all-cpu backtraces which show all eight CPUs stuck on either
lock_kernel() or files_lock(). It appears that both locks are held.

The do_tty_hangup()->tty_fasync() path takes the locks in the
file_list_lock()->lock_kernel() direction whereas most other code takes
them in the other direction, which cannot be good. But I'm not sure
that this recent merge significantly changed anything in that area.
Enabling lockdep makes the hang go away.

Have a trace. I'm actually wondering if perhaps there's a missing
unlock_kernel() somewhere else, and the tty code is just the victim of
that.

(hm, this trace only showed 6 CPUs. It's a bit of a mess)

[ 72.525902] INFO: RCU detected CPU 0 stall (t=2500 jiffies)
[ 72.525969] NMI backtrace for cpu 4
[ 72.526024] CPU 4
[ 72.526154] Process irqbalance (pid: 3152, threadinfo ffff88025d86e000, task ffff880256fac040)
[ 72.526209] Stack:
[ 72.526255] 0000000000000000 ffff88025d86fd08 ffffffff811a12f5 ffff88025d86fd38
[ 72.526434] <0> ffffffff811a572f ffff88025f0a2910 ffff88024a85c4c0 0000000000000000
[ 72.526698] <0> ffff88024a63f698 ffff88025d86fd48 ffffffff81383af9 ffff88025d86fd68
[ 72.527005] Call Trace:
[ 72.527057] [<ffffffff811a12f5>] __delay+0xa/0xc
[ 72.527112] [<ffffffff811a572f>] _raw_spin_lock+0xbc/0x125
[ 72.527165] [<ffffffff81383af9>] _spin_lock+0x9/0xb
[ 72.527220] [<ffffffff810ce929>] file_move+0x1e/0x4d
[ 72.527247] [<ffffffff810cd033>] __dentry_open+0x17e/0x2ef
[ 72.527247] [<ffffffff810cd26e>] nameidata_to_filp+0x3e/0x4f
[ 72.527247] [<ffffffff810d8bd5>] do_filp_open+0x529/0x972
[ 72.527247] [<ffffffff8105935b>] ? hrtimer_cancel+0x11/0x1d
[ 72.527247] [<ffffffff811a1fa3>] ? __strncpy_from_user+0x2b/0x55
[ 72.527247] [<ffffffff81383b04>] ? _spin_unlock+0x9/0xb
[ 72.527247] [<ffffffff810e2520>] ? alloc_fd+0x111/0x121
[ 72.527247] [<ffffffff810cc77d>] do_sys_open+0x5c/0x123
[ 72.527247] [<ffffffff810cc86d>] sys_open+0x1b/0x1d
[ 72.527247] [<ffffffff81002aab>] system_call_fastpath+0x16/0x1b
[ 72.527247] Code: 02 98 00 00 00 3e 48 89 c8 f7 e2 48 8d 7a 01 e8 b8 ff ff ff c9 c3 55 48 89 e5 50 65 8b 34 25 b0 cd 00 00 66 66 90 0f ae e8 0f 31 <41> 89 c0 66 66 90 0f ae e8 0f 31 89 c0 4c 29 c0 48 39 f8 73 20
[ 72.527247] Call Trace:
[ 72.527247] <#DB[1]> <<EOE>> Pid: 3152, comm: irqbalance Not tainted 2.6.32-mm1 #8
[ 72.527247] Call Trace:
[ 72.527247] <NMI> [<ffffffff81001098>] ? show_regs+0x23/0x27
[ 72.527247] [<ffffffff81385175>] nmi_watchdog_tick+0xc9/0x1ad
[ 72.527247] [<ffffffff813846b0>] do_nmi+0xa7/0x256
[ 72.527247] [<ffffffff8138433a>] nmi+0x1a/0x20
[ 72.527247] [<ffffffff811a134a>] ? delay_tsc+0x15/0x4c
[ 72.527247] <<EOE>> [<ffffffff811a12f5>] __delay+0xa/0xc
[ 72.527247] [<ffffffff811a572f>] _raw_spin_lock+0xbc/0x125
[ 72.527247] [<ffffffff81383af9>] _spin_lock+0x9/0xb
[ 72.527247] [<ffffffff810ce929>] file_move+0x1e/0x4d
[ 72.527247] [<ffffffff810cd033>] __dentry_open+0x17e/0x2ef
[ 72.527247] [<ffffffff810cd26e>] nameidata_to_filp+0x3e/0x4f
[ 72.527247] [<ffffffff810d8bd5>] do_filp_open+0x529/0x972
[ 72.527247] [<ffffffff8105935b>] ? hrtimer_cancel+0x11/0x1d
[ 72.527247] [<ffffffff811a1fa3>] ? __strncpy_from_user+0x2b/0x55
[ 72.527247] [<ffffffff81383b04>] ? _spin_unlock+0x9/0xb
[ 72.527247] [<ffffffff810e2520>] ? alloc_fd+0x111/0x121
[ 72.527247] [<ffffffff810cc77d>] do_sys_open+0x5c/0x123
[ 72.527247] [<ffffffff810cc86d>] sys_open+0x1b/0x1d
[ 72.527247] [<ffffffff81002aab>] system_call_fastpath+0x16/0x1b
[ 72.527230] NMI backtrace for cpu 6
[ 72.527230] CPU 6
[ 72.527230] Process mingetty (pid: 4105, threadinfo ffff88024aac4000, task ffff880256e2f810)
[ 72.527230] Stack:
[ 72.527230] ffffffff811a12f5 ffff88024aac5dc8 ffffffff811a572f 00007ffffbf94690
[ 72.527230] <0> 0000000000000000 000000000000033a ffffffff814ef5d0 ffff88024aac5e08
[ 72.527230] <0> ffffffff81383e0a ffff88025d5a3bc0 00007ffffbf94690 ffff88025d5a3bc0
[ 72.527230] Call Trace:
[ 72.527230] [<ffffffff811a12f5>] ? __delay+0xa/0xc
[ 72.527230] [<ffffffff811a572f>] _raw_spin_lock+0xbc/0x125
[ 72.527230] [<ffffffff81383e0a>] _lock_kernel+0x63/0x7c
[ 72.527230] [<ffffffff81101396>] __posix_lock_file+0x79/0x40e
[ 72.527230] [<ffffffff811018bf>] posix_lock_file+0x11/0x13
[ 72.527230] [<ffffffff811018ec>] vfs_lock_file+0x2b/0x2d
[ 72.527230] [<ffffffff81101ad4>] fcntl_setlk+0x139/0x278
[ 72.527230] [<ffffffff810da34c>] sys_fcntl+0x2ef/0x4a7
[ 72.527230] [<ffffffff81002aab>] system_call_fastpath+0x16/0x1b
[ 72.527230] Code: 48 8b 04 c5 60 85 86 81 48 c7 c2 c0 31 01 00 48 89 e5 48 6b 94 02 98 00 00 00 3e 48 89 c8 f7 e2 48 8d 7a 01 e8 b8 ff ff ff c9 c3 <55> 48 89 e5 50 65 8b 34 25 b0 cd 00 00 66 66 90 0f ae e8 0f 31
[ 72.527230] Call Trace:
[ 72.527230] <#DB[1]> <<EOE>> Pid: 4105, comm: mingetty Not tainted 2.6.32-mm1 #8
[ 72.527230] Call Trace:
[ 72.527230] <NMI> [<ffffffff81001098>] ? show_regs+0x23/0x27
[ 72.527230] [<ffffffff81385175>] nmi_watchdog_tick+0xc9/0x1ad
[ 72.527230] [<ffffffff813846b0>] do_nmi+0xa7/0x256
[ 72.527230] [<ffffffff8138433a>] nmi+0x1a/0x20
[ 72.527230] [<ffffffff811a1335>] ? delay_tsc+0x0/0x4c
[ 72.527230] <<EOE>> [<ffffffff811a12f5>] ? __delay+0xa/0xc
[ 72.527230] [<ffffffff811a572f>] _raw_spin_lock+0xbc/0x125
[ 72.527230] [<ffffffff81383e0a>] _lock_kernel+0x63/0x7c
[ 72.527230] [<ffffffff81101396>] __posix_lock_file+0x79/0x40e
[ 72.527230] [<ffffffff811018bf>] posix_lock_file+0x11/0x13
[ 72.527230] [<ffffffff811018ec>] vfs_lock_file+0x2b/0x2d
[ 72.527230] [<ffffffff81101ad4>] fcntl_setlk+0x139/0x278
[ 72.527230] [<ffffffff810da34c>] sys_fcntl+0x2ef/0x4a7
[ 72.527230] [<ffffffff81002aab>] system_call_fastpath+0x16/0x1b
[ 72.527211] NMI backtrace for cpu 1
[ 72.527230] INFO: RCU detected CPU 6 stall (t=2500 jiffies)
[ 72.527211] CPU 1
[ 72.527211] Process hald-addon-stor (pid: 3999, threadinfo ffff88025235c000, task ffff880256e2a080)
[ 72.527211] Stack:
[ 72.527211] 0000000000000000 ffff88025235dd08 ffffffff811a12f5 ffff88025235dd38
[ 72.527211] <0> ffffffff811a572f ffff88025d47ad10 ffff88025d4fd7c0 0000000000000000
[ 72.527211] <0> ffff8802583c78d0 ffff88025235dd48 ffffffff81383af9 ffff88025235dd68
[ 72.527211] Call Trace:
[ 72.527211] [<ffffffff811a12f5>] __delay+0xa/0xc
[ 72.527211] [<ffffffff811a572f>] _raw_spin_lock+0xbc/0x125
[ 72.527211] [<ffffffff81383af9>] _spin_lock+0x9/0xb
[ 72.527211] [<ffffffff810ce929>] file_move+0x1e/0x4d
[ 72.527211] [<ffffffff810cd033>] __dentry_open+0x17e/0x2ef
[ 72.527211] [<ffffffff810cd26e>] nameidata_to_filp+0x3e/0x4f
[ 72.527211] [<ffffffff810d8bd5>] do_filp_open+0x529/0x972
[ 72.527211] [<ffffffff81383b04>] ? _spin_unlock+0x9/0xb
[ 72.527211] [<ffffffff811a1fa3>] ? __strncpy_from_user+0x2b/0x55
[ 72.527211] [<ffffffff81383b04>] ? _spin_unlock+0x9/0xb
[ 72.527211] [<ffffffff810e2520>] ? alloc_fd+0x111/0x121
[ 72.527211] [<ffffffff810cc77d>] do_sys_open+0x5c/0x123
[ 72.527211] [<ffffffff810cc86d>] sys_open+0x1b/0x1d
[ 72.527211] [<ffffffff81002aab>] system_call_fastpath+0x16/0x1b
[ 72.527211] Code: 7a 01 e8 b8 ff ff ff c9 c3 55 48 89 e5 50 65 8b 34 25 b0 cd 00 00 66 66 90 0f ae e8 0f 31 41 89 c0 66 66 90 0f ae e8 0f 31 89 c0 <4c> 29 c0 48 39 f8 73 20 f3 90 65 8b 0c 25 b0 cd 00 00 39 ce 74
[ 72.527211] Call Trace:
[ 72.527211] <#DB[1]> <<EOE>> Pid: 3999, comm: hald-addon-stor Not tainted 2.6.32-mm1 #8
[ 72.527211] Call Trace:
[ 72.527211] <NMI> [<ffffffff81001098>] ? show_regs+0x23/0x27
[ 72.527211] [<ffffffff81385175>] nmi_watchdog_tick+0xc9/0x1ad
[ 72.527211] [<ffffffff813846b0>] do_nmi+0xa7/0x256
[ 72.527211] [<ffffffff8138433a>] nmi+0x1a/0x20
[ 72.527211] [<ffffffff811a1357>] ? delay_tsc+0x22/0x4c
[ 72.527211] <<EOE>> [<ffffffff811a12f5>] __delay+0xa/0xc
[ 72.527211] [<ffffffff811a572f>] _raw_spin_lock+0xbc/0x125
[ 72.527211] [<ffffffff81383af9>] _spin_lock+0x9/0xb
[ 72.527211] [<ffffffff810ce929>] file_move+0x1e/0x4d
[ 72.527211] [<ffffffff810cd033>] __dentry_open+0x17e/0x2ef
[ 72.527211] [<ffffffff810cd26e>] nameidata_to_filp+0x3e/0x4f
[ 72.527211] [<ffffffff810d8bd5>] do_filp_open+0x529/0x972
[ 72.527211] [<ffffffff81383b04>] ? _spin_unlock+0x9/0xb
[ 72.527211] [<ffffffff811a1fa3>] ? __strncpy_from_user+0x2b/0x55
[ 72.527211] [<ffffffff81383b04>] ? _spin_unlock+0x9/0xb
[ 72.527211] [<ffffffff810e2520>] ? alloc_fd+0x111/0x121
[ 72.527211] [<ffffffff810cc77d>] do_sys_open+0x5c/0x123
[ 72.527211] [<ffffffff810cc86d>] sys_open+0x1b/0x1d
[ 72.527211] [<ffffffff81002aab>] system_call_fastpath+0x16/0x1b

2009-12-12 10:07:06

by Ingo Molnar

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git


* Andrew Morton <[email protected]> wrote:

> Have a trace. I'm actually wondering if perhaps there's a missing
> unlock_kernel() somewhere else, and the tty code is just the victim of
> that.

Unlikely i'd say. I have 1000+ successful overnight tests on the latest
tree Linus pushed out:

3ef884b: Merge branch 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied/d

So that's a guaranteed 'good' kernel.

The moment i merged 053fe57 into -tip the lockups started, so that's a
guaranteed 'bad' kernel. There's only the TTY changes between those two
points that look remotely related.

It's spurious though so quite hard to bisect. I tried one bisection
today already and it got on the wrong track. I'll do a brute-force
revert of the commits i quoted, lets see what happens.

Ingo

2009-12-12 10:10:45

by Ingo Molnar

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git


* Andrew Morton <[email protected]> wrote:

> Seems to be quite .config-dependent.

My theory is that it's a race and that it's thus timing dependent. TTY
SMP details get stressed most during a particular point during bootup,
when all the mingetty's are starting up all at once and race with each
other.

If you are lucky to not hit the bug then, then the likelyhood is much
lower later on.

It would be nice if Alan posted his TTY stress-testing code. It could
potentially make this bug bisectable.

Ingo

2009-12-12 10:18:02

by Ingo Molnar

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git


* Ingo Molnar <[email protected]> wrote:

>
> * Andrew Morton <[email protected]> wrote:
>
> > Have a trace. I'm actually wondering if perhaps there's a missing
> > unlock_kernel() somewhere else, and the tty code is just the victim of
> > that.
>
> Unlikely i'd say. I have 1000+ successful overnight tests on the latest
> tree Linus pushed out:
>
> 3ef884b: Merge branch 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied/d
>
> So that's a guaranteed 'good' kernel.
>
> The moment i merged 053fe57 into -tip the lockups started, so that's a
> guaranteed 'bad' kernel. There's only the TTY changes between those two
> points that look remotely related.
>
> It's spurious though so quite hard to bisect. I tried one bisection
> today already and it got on the wrong track. I'll do a brute-force
> revert of the commits i quoted, lets see what happens.

i'm testing the series of 5 reverts below. It's looking good so far. You
might want to try them - how quickly can you reproduce the hangs?

Ingo

--------------->
>From 95b532d4e2f9a82c1dedf7ea4a6c2702402237e6 Mon Sep 17 00:00:00 2001
From: Ingo Molnar <[email protected]>
Date: Sat, 12 Dec 2009 11:08:04 +0100
Subject: [PATCH] Revert "tty: push the BKL down into the handlers a bit"

This reverts commit eeb89d918c2fa2b809e464136bbafdaec2aacb30.

diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 385c44b..d86c0bc 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -659,7 +659,7 @@ static int __ptmx_open(struct inode *inode, struct file *filp)
if (!retval)
return 0;
out1:
- tty_release(inode, filp);
+ tty_release_dev(filp);
return retval;
out:
devpts_kill_index(inode, index);
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 1e24130..59499ee 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -142,6 +142,7 @@ ssize_t redirected_tty_write(struct file *, const char __user *,
size_t, loff_t *);
static unsigned int tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *);
+static int tty_release(struct inode *, struct file *);
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_COMPAT
static long tty_compat_ioctl(struct file *file, unsigned int cmd,
@@ -1016,16 +1017,14 @@ out:

void tty_write_message(struct tty_struct *tty, char *msg)
{
+ lock_kernel();
if (tty) {
mutex_lock(&tty->atomic_write_lock);
- lock_kernel();
- if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
- unlock_kernel();
+ if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags))
tty->ops->write(tty, msg, strlen(msg));
- } else
- unlock_kernel();
tty_write_unlock(tty);
}
+ unlock_kernel();
return;
}

@@ -1203,21 +1202,14 @@ static int tty_driver_install_tty(struct tty_driver *driver,
struct tty_struct *tty)
{
int idx = tty->index;
- int ret;

- if (driver->ops->install) {
- lock_kernel();
- ret = driver->ops->install(driver, tty);
- unlock_kernel();
- return ret;
- }
+ if (driver->ops->install)
+ return driver->ops->install(driver, tty);

if (tty_init_termios(tty) == 0) {
- lock_kernel();
tty_driver_kref_get(driver);
tty->count++;
driver->ttys[idx] = tty;
- unlock_kernel();
return 0;
}
return -ENOMEM;
@@ -1310,14 +1302,10 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
struct tty_struct *tty;
int retval;

- lock_kernel();
/* Check if pty master is being opened multiple times */
if (driver->subtype == PTY_TYPE_MASTER &&
- (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
- unlock_kernel();
+ (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)
return ERR_PTR(-EIO);
- }
- unlock_kernel();

/*
* First time open is complex, especially for PTY devices.
@@ -1347,9 +1335,8 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
* If we fail here just call release_tty to clean up. No need
* to decrement the use counts, as release_tty doesn't care.
*/
- lock_kernel();
+
retval = tty_ldisc_setup(tty, tty->link);
- unlock_kernel();
if (retval)
goto release_mem_out;
return tty;
@@ -1363,9 +1350,7 @@ release_mem_out:
if (printk_ratelimit())
printk(KERN_INFO "tty_init_dev: ldisc open failed, "
"clearing slot %d\n", idx);
- lock_kernel();
release_tty(tty, idx);
- unlock_kernel();
return ERR_PTR(retval);
}

@@ -1479,17 +1464,7 @@ static void release_tty(struct tty_struct *tty, int idx)
tty_kref_put(tty);
}

-/**
- * tty_release - vfs callback for close
- * @inode: inode of tty
- * @filp: file pointer for handle to tty
- *
- * Called the last time each file handle is closed that references
- * this tty. There may however be several such references.
- *
- * Locking:
- * Takes bkl. See tty_release_dev
- *
+/*
* Even releasing the tty structures is a tricky business.. We have
* to be very careful that the structures are all released at the
* same time, as interrupts might otherwise get the wrong pointers.
@@ -1497,20 +1472,20 @@ static void release_tty(struct tty_struct *tty, int idx)
* WSH 09/09/97: rewritten to avoid some nasty race conditions that could
* lead to double frees or releasing memory still in use.
*/
-
-int tty_release(struct inode *inode, struct file *filp)
+void tty_release_dev(struct file *filp)
{
struct tty_struct *tty, *o_tty;
int pty_master, tty_closing, o_tty_closing, do_sleep;
int devpts;
int idx;
char buf[64];
+ struct inode *inode;

+ inode = filp->f_path.dentry->d_inode;
tty = (struct tty_struct *)filp->private_data;
if (tty_paranoia_check(tty, inode, "tty_release_dev"))
- return 0;
+ return;

- lock_kernel();
check_tty_count(tty, "tty_release_dev");

tty_fasync(-1, filp, 0);
@@ -1525,22 +1500,19 @@ int tty_release(struct inode *inode, struct file *filp)
if (idx < 0 || idx >= tty->driver->num) {
printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
"free (%s)\n", tty->name);
- unlock_kernel();
- return 0;
+ return;
}
if (!devpts) {
if (tty != tty->driver->ttys[idx]) {
- unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
"for (%s)\n", idx, tty->name);
- return 0;
+ return;
}
if (tty->termios != tty->driver->termios[idx]) {
- unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
"for (%s)\n",
idx, tty->name);
- return 0;
+ return;
}
}
#endif
@@ -1554,30 +1526,26 @@ int tty_release(struct inode *inode, struct file *filp)
if (tty->driver->other &&
!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
if (o_tty != tty->driver->other->ttys[idx]) {
- unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
"not o_tty for (%s)\n",
idx, tty->name);
- return 0 ;
+ return;
}
if (o_tty->termios != tty->driver->other->termios[idx]) {
- unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
"not o_termios for (%s)\n",
idx, tty->name);
- return 0;
+ return;
}
if (o_tty->link != tty) {
- unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
- return 0;
+ return;
}
}
#endif
if (tty->ops->close)
tty->ops->close(tty, filp);

- unlock_kernel();
/*
* 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
@@ -1600,7 +1568,6 @@ int tty_release(struct inode *inode, struct file *filp)
opens on /dev/tty */

mutex_lock(&tty_mutex);
- lock_kernel();
tty_closing = tty->count <= 1;
o_tty_closing = o_tty &&
(o_tty->count <= (pty_master ? 1 : 0));
@@ -1631,7 +1598,6 @@ int tty_release(struct inode *inode, struct file *filp)

printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
"active!\n", tty_name(tty, buf));
- unlock_kernel();
mutex_unlock(&tty_mutex);
schedule();
}
@@ -1695,10 +1661,8 @@ int tty_release(struct inode *inode, struct file *filp)
mutex_unlock(&tty_mutex);

/* check whether both sides are closing ... */
- if (!tty_closing || (o_tty && !o_tty_closing)) {
- unlock_kernel();
- return 0;
- }
+ if (!tty_closing || (o_tty && !o_tty_closing))
+ return;

#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "freeing tty structure...");
@@ -1716,12 +1680,10 @@ int tty_release(struct inode *inode, struct file *filp)
/* Make this pty number available for reallocation */
if (devpts)
devpts_kill_index(inode, idx);
- unlock_kernel();
- return 0;
}

/**
- * tty_open - open a tty device
+ * __tty_open - open a tty device
* @inode: inode of device file
* @filp: file pointer to tty
*
@@ -1741,7 +1703,7 @@ int tty_release(struct inode *inode, struct file *filp)
* ->siglock protects ->signal/->sighand
*/

-static int tty_open(struct inode *inode, struct file *filp)
+static int __tty_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty = NULL;
int noctty, retval;
@@ -1758,12 +1720,10 @@ retry_open:
retval = 0;

mutex_lock(&tty_mutex);
- lock_kernel();

if (device == MKDEV(TTYAUX_MAJOR, 0)) {
tty = get_current_tty();
if (!tty) {
- unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENXIO;
}
@@ -1795,14 +1755,12 @@ retry_open:
goto got_driver;
}
}
- unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENODEV;
}

driver = get_tty_driver(device, &index);
if (!driver) {
- unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
@@ -1812,7 +1770,6 @@ got_driver:
tty = tty_driver_lookup_tty(driver, inode, index);

if (IS_ERR(tty)) {
- unlock_kernel();
mutex_unlock(&tty_mutex);
return PTR_ERR(tty);
}
@@ -1827,10 +1784,8 @@ got_driver:

mutex_unlock(&tty_mutex);
tty_driver_kref_put(driver);
- if (IS_ERR(tty)) {
- unlock_kernel();
+ if (IS_ERR(tty))
return PTR_ERR(tty);
- }

filp->private_data = tty;
file_move(filp, &tty->tty_files);
@@ -1858,15 +1813,11 @@ got_driver:
printk(KERN_DEBUG "error %d in opening %s...", retval,
tty->name);
#endif
- tty_release(inode, filp);
- if (retval != -ERESTARTSYS) {
- unlock_kernel();
+ tty_release_dev(filp);
+ if (retval != -ERESTARTSYS)
return retval;
- }
- if (signal_pending(current)) {
- unlock_kernel();
+ if (signal_pending(current))
return retval;
- }
schedule();
/*
* Need to reset f_op in case a hangup happened.
@@ -1875,11 +1826,8 @@ got_driver:
filp->f_op = &tty_fops;
goto retry_open;
}
- unlock_kernel();
-

mutex_lock(&tty_mutex);
- lock_kernel();
spin_lock_irq(&current->sighand->siglock);
if (!noctty &&
current->signal->leader &&
@@ -1887,13 +1835,44 @@ got_driver:
tty->session == NULL)
__proc_set_tty(current, tty);
spin_unlock_irq(&current->sighand->siglock);
- unlock_kernel();
mutex_unlock(&tty_mutex);
return 0;
}

+/* BKL pushdown: scary code avoidance wrapper */
+static int tty_open(struct inode *inode, struct file *filp)
+{
+ int ret;
+
+ lock_kernel();
+ ret = __tty_open(inode, filp);
+ unlock_kernel();
+ return ret;
+}


+
+
+/**
+ * tty_release - vfs callback for close
+ * @inode: inode of tty
+ * @filp: file pointer for handle to tty
+ *
+ * Called the last time each file handle is closed that references
+ * this tty. There may however be several such references.
+ *
+ * Locking:
+ * Takes bkl. See tty_release_dev
+ */
+
+static int tty_release(struct inode *inode, struct file *filp)
+{
+ lock_kernel();
+ tty_release_dev(filp);
+ unlock_kernel();
+ return 0;
+}
+
/**
* tty_poll - check tty status
* @filp: file being polled
@@ -2338,7 +2317,9 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
if (get_user(ldisc, p))
return -EFAULT;

+ lock_kernel();
ret = tty_set_ldisc(tty, ldisc);
+ unlock_kernel();

return ret;
}
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index d914e77..feb5507 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -34,8 +34,6 @@
#include <linux/vt_kern.h>
#include <linux/selection.h>

-#include <linux/smp_lock.h> /* For the moment */
-
#include <linux/kmod.h>
#include <linux/nsproxy.h>

@@ -547,7 +545,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
if (IS_ERR(new_ldisc))
return PTR_ERR(new_ldisc);

- lock_kernel();
/*
* We need to look at the tty locking here for pty/tty pairs
* when both sides try to change in parallel.
@@ -561,7 +558,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
*/

if (tty->ldisc->ops->num == ldisc) {
- unlock_kernel();
tty_ldisc_put(new_ldisc);
return 0;
}
@@ -573,7 +569,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)

tty_wait_until_sent(tty, 0);

- unlock_kernel();
mutex_lock(&tty->ldisc_mutex);

/*
@@ -587,9 +582,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
mutex_lock(&tty->ldisc_mutex);
}
-
- lock_kernel();
-
set_bit(TTY_LDISC_CHANGING, &tty->flags);

/*
@@ -600,8 +592,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
tty->receive_room = 0;

o_ldisc = tty->ldisc;
-
- unlock_kernel();
/*
* Make sure we don't change while someone holds a
* reference to the line discipline. The TTY_LDISC bit
@@ -627,14 +617,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
flush_scheduled_work();

mutex_lock(&tty->ldisc_mutex);
- lock_kernel();
if (test_bit(TTY_HUPPED, &tty->flags)) {
/* We were raced by the hangup method. It will have stomped
the ldisc data and closed the ldisc down */
clear_bit(TTY_LDISC_CHANGING, &tty->flags);
mutex_unlock(&tty->ldisc_mutex);
tty_ldisc_put(new_ldisc);
- unlock_kernel();
return -EIO;
}

@@ -676,7 +664,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
if (o_work)
schedule_delayed_work(&o_tty->buf.work, 1);
mutex_unlock(&tty->ldisc_mutex);
- unlock_kernel();
return retval;
}

diff --git a/include/linux/tty.h b/include/linux/tty.h
index 405a903..e6da667 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -449,7 +449,7 @@ extern void initialize_tty_struct(struct tty_struct *tty,
struct tty_driver *driver, int idx);
extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
int first_ok);
-extern int tty_release(struct inode *inode, struct file *filp);
+extern void tty_release_dev(struct file *filp);
extern int tty_init_termios(struct tty_struct *tty);

extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty);

>From bd9a619494c123470a081e5e30602c307de2bba3 Mon Sep 17 00:00:00 2001
From: Ingo Molnar <[email protected]>
Date: Sat, 12 Dec 2009 11:07:55 +0100
Subject: [PATCH] Revert "tty: Push the lock down further into the ldisc code"

This reverts commit f18f9498e90327b9b0e245e191029e6e1996d203.

diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index c408c81..1e24130 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -1347,7 +1347,9 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
* If we fail here just call release_tty to clean up. No need
* to decrement the use counts, as release_tty doesn't care.
*/
+ lock_kernel();
retval = tty_ldisc_setup(tty, tty->link);
+ unlock_kernel();
if (retval)
goto release_mem_out;
return tty;
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index 3f653f7..d914e77 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -445,14 +445,8 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
{
WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
- if (ld->ops->open) {
- int ret;
- /* BKL here locks verus a hangup event */
- lock_kernel();
- ret = ld->ops->open(tty);
- unlock_kernel();
- return ret;
- }
+ if (ld->ops->open)
+ return ld->ops->open(tty);
return 0;
}

@@ -572,7 +566,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
return 0;
}

- unlock_kernel();
/*
* Problem: What do we do if this blocks ?
* We could deadlock here
@@ -580,6 +573,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)

tty_wait_until_sent(tty, 0);

+ unlock_kernel();
mutex_lock(&tty->ldisc_mutex);

/*

>From 8e991a5112643a63820a1088860f0c7c869d6f25 Mon Sep 17 00:00:00 2001
From: Ingo Molnar <[email protected]>
Date: Sat, 12 Dec 2009 11:07:46 +0100
Subject: [PATCH] Revert "tty: Push the bkl down a bit in the hangup code"

This reverts commit 38c70b27f9502c31c1d0c29676275f7362cdb0d9.

diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index cc941a3..c408c81 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -505,6 +505,8 @@ static void do_tty_hangup(struct work_struct *work)
if (!tty)
return;

+ /* inuse_filps is protected by the single kernel lock */
+ lock_kernel();

spin_lock(&redirect_lock);
if (redirect && redirect->private_data == tty) {
@@ -513,8 +515,6 @@ static void do_tty_hangup(struct work_struct *work)
}
spin_unlock(&redirect_lock);

- /* inuse_filps is protected by the single kernel lock */
- lock_kernel();
check_tty_count(tty, "do_tty_hangup");
file_list_lock();
/* This breaks for file handles being sent over AF_UNIX sockets ? */

>From 9d6d77b3c56e6c169e50b6bb9033e4a2e7d4ec71 Mon Sep 17 00:00:00 2001
From: Ingo Molnar <[email protected]>
Date: Sat, 12 Dec 2009 11:07:38 +0100
Subject: [PATCH] Revert "tty: Move the leader test in disassociate"

This reverts commit 5ec93d1154fd1e269162398f8e70efc7e004485d.

diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index a19fef2..cc941a3 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -707,8 +707,6 @@ void disassociate_ctty(int on_exit)
struct tty_struct *tty;
struct pid *tty_pgrp = NULL;

- if (!current->signal->leader)
- return;

tty = get_current_tty();
if (tty) {
@@ -774,7 +772,8 @@ void no_tty(void)
{
struct task_struct *tsk = current;
lock_kernel();
- disassociate_ctty(0);
+ if (tsk->signal->leader)
+ disassociate_ctty(0);
unlock_kernel();
proc_clear_tty(tsk);
}
diff --git a/kernel/exit.c b/kernel/exit.c
index 6f50ef5..1143012 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -971,7 +971,7 @@ NORET_TYPE void do_exit(long code)
exit_thread();
cgroup_exit(tsk, 1);

- if (group_dead)
+ if (group_dead && tsk->signal->leader)
disassociate_ctty(1);

module_put(task_thread_info(tsk)->exec_domain->module);

>From 153d2afe47cc994d35adc7e61be13bf840fd0b47 Mon Sep 17 00:00:00 2001
From: Ingo Molnar <[email protected]>
Date: Sat, 12 Dec 2009 11:07:28 +0100
Subject: [PATCH] Revert "tty: split the lock up a bit further"

This reverts commit 36ba782e9674cdc29ec7003757df0b375e99fa96.

diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 684f0e0..a19fef2 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -516,8 +516,6 @@ static void do_tty_hangup(struct work_struct *work)
/* inuse_filps is protected by the single kernel lock */
lock_kernel();
check_tty_count(tty, "do_tty_hangup");
- unlock_kernel();
-
file_list_lock();
/* This breaks for file handles being sent over AF_UNIX sockets ? */
list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) {
@@ -531,7 +529,6 @@ static void do_tty_hangup(struct work_struct *work)
}
file_list_unlock();

- lock_kernel();
tty_ldisc_hangup(tty);

read_lock(&tasklist_lock);

2009-12-12 10:36:46

by Andrew Morton

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sat, 12 Dec 2009 11:10:32 +0100 Ingo Molnar <[email protected]> wrote:

>
> * Andrew Morton <[email protected]> wrote:
>
> > Seems to be quite .config-dependent.
>
> My theory is that it's a race and that it's thus timing dependent. TTY
> SMP details get stressed most during a particular point during bootup,
> when all the mingetty's are starting up all at once and race with each
> other.
>
> If you are lucky to not hit the bug then, then the likelyhood is much
> lower later on.
>
> It would be nice if Alan posted his TTY stress-testing code. It could
> potentially make this bug bisectable.
>

I'm surprised that lockdep didn't notice that ab/ba I thought I saw.
Maybe the do_tty_hangup()->tty_fasync() never happens.

The machine I can reproduce this on is at work and I'm not, until
Monday. I'd try removing the files_lock() calls from tty_io.c, see if
that helps.

I had

[ 71.553228] Warning: dev (tty1) tty->count(7) != #fd's(6) in tty_release_dev

come out once, then it went away.

2009-12-12 10:42:16

by Andrew Morton

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sat, 12 Dec 2009 11:15:51 +0100 Ingo Molnar <[email protected]> wrote:

> i'm testing the series of 5 reverts below. It's looking good so far. You
> might want to try them - how quickly can you reproduce the hangs?

Immediately, with http://userweb.kernel.org/~akpm/config-akpm2.txt

2009-12-12 10:40:58

by Alan

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sat, 12 Dec 2009 09:46:11 +0100
Ingo Molnar <[email protected]> wrote:

>
> * Greg KH <[email protected]> wrote:
>
> > Here's the big TTY patchset for your .33-git tree.
>
> FYI, one of the changes in this tree is causing lockups on x86.
>
> Config attached.
>
> Possible suspects would one of these:
>
> 36ba782: tty: split the lock up a bit further
> 5ec93d1: tty: Move the leader test in disassociate
> 38c70b2: tty: Push the bkl down a bit in the hangup code
> f18f949: tty: Push the lock down further into the ldisc code
> eeb89d9: tty: push the BKL down into the handlers a bit
>
> as they deal with locking details and are fresher than two weeks.

Any diagnostics with the lockup or just a system hang ?

You can pop back those five and if the lockup then vanishes those are
ones trying to work on hangup and BKL rather than security fixes so can
just get punted to next release

2009-12-12 10:42:34

by Ingo Molnar

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git


* Alan Cox <[email protected]> wrote:

> On Sat, 12 Dec 2009 09:46:11 +0100
> Ingo Molnar <[email protected]> wrote:
>
> >
> > * Greg KH <[email protected]> wrote:
> >
> > > Here's the big TTY patchset for your .33-git tree.
> >
> > FYI, one of the changes in this tree is causing lockups on x86.
> >
> > Config attached.
> >
> > Possible suspects would one of these:
> >
> > 36ba782: tty: split the lock up a bit further
> > 5ec93d1: tty: Move the leader test in disassociate
> > 38c70b2: tty: Push the bkl down a bit in the hangup code
> > f18f949: tty: Push the lock down further into the ldisc code
> > eeb89d9: tty: push the BKL down into the handlers a bit
> >
> > as they deal with locking details and are fresher than two weeks.
>
> Any diagnostics with the lockup or just a system hang ?

None that i've captured (the hang is silent) but Andrew posted some.

Ingo

2009-12-12 10:52:55

by Ingo Molnar

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git


* Andrew Morton <[email protected]> wrote:

> On Sat, 12 Dec 2009 11:15:51 +0100 Ingo Molnar <[email protected]> wrote:
>
> > i'm testing the series of 5 reverts below. It's looking good so far. You
> > might want to try them - how quickly can you reproduce the hangs?
>
> Immediately, with http://userweb.kernel.org/~akpm/config-akpm2.txt

Ok. The lockups i saw went away with the 5 reverts i posted. I'll keep
testing that over the weekend - that should narrow down the range very
precisely.

Ingo

2009-12-12 11:03:14

by Alan

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

> The do_tty_hangup()->tty_fasync() path takes the locks in the
> file_list_lock()->lock_kernel() direction whereas most other code takes
> them in the other direction, which cannot be good. But I'm not sure

Thats a bug - the BKL does want to be taken first (and will
sleep/yield/drop)

> Have a trace. I'm actually wondering if perhaps there's a missing
> unlock_kernel() somewhere else, and the tty code is just the victim of
> that.

It's introduced by the BKL shuffle. Try the following


commit 1f61f07a985c7e8cfc20ad8fcced2f3d226bd0dc
Author: Alan Cox <[email protected]>
Date: Sat Dec 12 10:32:36 2009 +0000

tty: Fix the AB-BA locking bug introduced in the BKL split

The fasync path takes the BKL (it probably doesn't need to in fact) but
this causes lock inversions and deadlocks so we can't do that. Leave the
BKL over that bit for the moment.

Identified by AKPM.

Signed-off-by: Alan Cox <[email protected]>

diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 684f0e0..f15df40 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -516,7 +516,6 @@ static void do_tty_hangup(struct work_struct *work)
/* inuse_filps is protected by the single kernel lock */
lock_kernel();
check_tty_count(tty, "do_tty_hangup");
- unlock_kernel();

file_list_lock();
/* This breaks for file handles being sent over AF_UNIX sockets ? */
@@ -531,7 +530,6 @@ static void do_tty_hangup(struct work_struct *work)
}
file_list_unlock();

- lock_kernel();
tty_ldisc_hangup(tty);

read_lock(&tasklist_lock);

2009-12-13 01:15:27

by Linus Torvalds

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git



On Sat, 12 Dec 2009, Andrew Morton wrote:

> On Sat, 12 Dec 2009 11:10:32 +0100 Ingo Molnar <[email protected]> wrote:
>
> >
> > * Andrew Morton <[email protected]> wrote:
> >
> > > Seems to be quite .config-dependent.
> >
> > My theory is that it's a race and that it's thus timing dependent. TTY
> > SMP details get stressed most during a particular point during bootup,
> > when all the mingetty's are starting up all at once and race with each
> > other.
> >
> > If you are lucky to not hit the bug then, then the likelyhood is much
> > lower later on.
> >
> > It would be nice if Alan posted his TTY stress-testing code. It could
> > potentially make this bug bisectable.
> >
>
> I'm surprised that lockdep didn't notice that ab/ba I thought I saw.
> Maybe the do_tty_hangup()->tty_fasync() never happens.

The kernel lock cannot have any ABBA deadlocks.

If somebody blocks on another lock (after getting the kernel lock), the
kernel lock will be dropped. So no ABBA.

Linus

2009-12-13 01:15:25

by Linus Torvalds

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git



On Sat, 12 Dec 2009, Linus Torvalds wrote:
> >
> > I'm surprised that lockdep didn't notice that ab/ba I thought I saw.
> > Maybe the do_tty_hangup()->tty_fasync() never happens.
>
> The kernel lock cannot have any ABBA deadlocks.
>
> If somebody blocks on another lock (after getting the kernel lock), the
> kernel lock will be dropped. So no ABBA.

Oh, but it turns out that while there cannot be any ABBA deadlocks with
sleeping locks, what the tty code does is invalid for _another_ reason:
file_list_lock() is a spinlock. And that's a no-no.

You cannot take the kernel lock inside a spinlock. Ordering doesn't
matter, there's no ABBA issues - it's simply invalid _regardless_ of any
other use of that lock.

I think we could possibly add a "__might_sleep()" to _lock_kernel(). It
doesn't really sleep, but it's invalid to take the kernel lock in an
atomic region, so __might_sleep() might be the right thing anyway.

Linus

2009-12-13 01:55:21

by Alan

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

> I think we could possibly add a "__might_sleep()" to _lock_kernel(). It
> doesn't really sleep, but it's invalid to take the kernel lock in an
> atomic region, so __might_sleep() might be the right thing anyway.

It's only invalid if you don't already hold the lock. The old tty code
worked because every path into tty_fasync already held the lock ! That
specific case - taking it the first time should definitely
__might_sleep().

Mind you it's probably still rather dumb and would be a good debugging
aid for -next to be able to warn on all offences if only to catch this
stuff for the future BKL removal work.

Alan

2009-12-13 01:48:34

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sat, 12 Dec 2009, Alan Cox wrote:
> > I think we could possibly add a "__might_sleep()" to _lock_kernel(). It
> > doesn't really sleep, but it's invalid to take the kernel lock in an
> > atomic region, so __might_sleep() might be the right thing anyway.
>
> It's only invalid if you don't already hold the lock. The old tty code
> worked because every path into tty_fasync already held the lock ! That
> specific case - taking it the first time should definitely
> __might_sleep().
>
> Mind you it's probably still rather dumb and would be a good debugging
> aid for -next to be able to warn on all offences if only to catch this
> stuff for the future BKL removal work.

Just patched the following in and it catched your problem nicely. With
your AB/BA fix patch applied everything is fine.

Thanks,

tglx
---
Subject: BKL: Add might sleep to __lock_kernel
From: Thomas Gleixner <[email protected]>
Date: Sat, 12 Dec 2009 20:29:00 +0100

Catches all offenders which take the BKL first time in an atomic
region. Recursive lock_kernel calls are not affected.

Signed-off-by: Thomas Gleixner <[email protected]>
---
lib/kernel_lock.c | 2 ++
1 file changed, 2 insertions(+)

Index: linux-2.6/lib/kernel_lock.c
===================================================================
--- linux-2.6.orig/lib/kernel_lock.c
+++ linux-2.6/lib/kernel_lock.c
@@ -64,6 +64,8 @@ void __lockfunc __release_kernel_lock(vo
#ifdef CONFIG_PREEMPT
static inline void __lock_kernel(void)
{
+ might_sleep();
+
preempt_disable();
if (unlikely(!_raw_spin_trylock(&kernel_flag))) {
/*

2009-12-13 01:15:15

by Linus Torvalds

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git



On Sat, 12 Dec 2009, Alan Cox wrote:

> > I think we could possibly add a "__might_sleep()" to _lock_kernel(). It
> > doesn't really sleep, but it's invalid to take the kernel lock in an
> > atomic region, so __might_sleep() might be the right thing anyway.
>
> It's only invalid if you don't already hold the lock.

True.

> The old tty code worked because every path into tty_fasync already held
> the lock ! That specific case - taking it the first time should
> definitely __might_sleep().

That would give us at least somewhat better debugging. And it's a very
natural thing to do. IOW, just something like the appended.

But maybe it complains about valid (but unusual) things. For example, it's
not strictly speaking _wrong_ to take the kernel lock while preemption is
disabled, even though it's a really bad idea.

Anybody willing to be the guinea-pig?

Linus

---
lib/kernel_lock.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/lib/kernel_lock.c b/lib/kernel_lock.c
index 4ebfa5a..5526b46 100644
--- a/lib/kernel_lock.c
+++ b/lib/kernel_lock.c
@@ -122,8 +122,10 @@ void __lockfunc _lock_kernel(const char *func, const char *file, int line)

trace_lock_kernel(func, file, line);

- if (likely(!depth))
+ if (likely(!depth)) {
+ might_sleep();
__lock_kernel();
+ }
current->lock_depth = depth;
}

2009-12-13 01:15:17

by Linus Torvalds

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git



On Sat, 12 Dec 2009, Thomas Gleixner wrote:
>
> Just patched the following in and it catched your problem nicely. With
> your AB/BA fix patch applied everything is fine.

Actually, the patch I just suggested might be better. Admittedly the
CONFIG_PREEMPT case is the more important one (and the one that will catch
more cases), but even without preemptyion the might_sleep() in
_lock_kernel() (one underscore) would trigger on the case of somebody
doing lock_kernel with local interrupts disabled.

Linus

2009-12-13 01:48:30

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sat, 12 Dec 2009, Linus Torvalds wrote:
> On Sat, 12 Dec 2009, Thomas Gleixner wrote:
> >
> > Just patched the following in and it catched your problem nicely. With
> > your AB/BA fix patch applied everything is fine.
>
> Actually, the patch I just suggested might be better. Admittedly the
> CONFIG_PREEMPT case is the more important one (and the one that will catch
> more cases), but even without preemptyion the might_sleep() in
> _lock_kernel() (one underscore) would trigger on the case of somebody
> doing lock_kernel with local interrupts disabled.

Fair enough.

tglx

2009-12-13 01:48:54

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sat, 12 Dec 2009, Linus Torvalds wrote:

>
> On Sat, 12 Dec 2009, Alan Cox wrote:
>
> > > I think we could possibly add a "__might_sleep()" to _lock_kernel(). It
> > > doesn't really sleep, but it's invalid to take the kernel lock in an
> > > atomic region, so __might_sleep() might be the right thing anyway.
> >
> > It's only invalid if you don't already hold the lock.
>
> True.
>
> > The old tty code worked because every path into tty_fasync already held
> > the lock ! That specific case - taking it the first time should
> > definitely __might_sleep().
>
> That would give us at least somewhat better debugging. And it's a very
> natural thing to do. IOW, just something like the appended.
>
> But maybe it complains about valid (but unusual) things. For example, it's
> not strictly speaking _wrong_ to take the kernel lock while preemption is
> disabled, even though it's a really bad idea.
>
> Anybody willing to be the guinea-pig?

Replaced my patch with yours and it works the same way (except for the
PREEMPT=n case)

Acked-and-Tested-by: Thomas Gleixner <[email protected]>

> Linus
>
> ---
> lib/kernel_lock.c | 4 +++-
> 1 files changed, 3 insertions(+), 1 deletions(-)
>
> diff --git a/lib/kernel_lock.c b/lib/kernel_lock.c
> index 4ebfa5a..5526b46 100644
> --- a/lib/kernel_lock.c
> +++ b/lib/kernel_lock.c
> @@ -122,8 +122,10 @@ void __lockfunc _lock_kernel(const char *func, const char *file, int line)
>
> trace_lock_kernel(func, file, line);
>
> - if (likely(!depth))
> + if (likely(!depth)) {
> + might_sleep();
> __lock_kernel();
> + }
> current->lock_depth = depth;
> }
>
>

2009-12-13 01:48:38

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git



On Sat, 12 Dec 2009, Alan Cox wrote:

> > The do_tty_hangup()->tty_fasync() path takes the locks in the
> > file_list_lock()->lock_kernel() direction whereas most other code takes
> > them in the other direction, which cannot be good. But I'm not sure
>
> Thats a bug - the BKL does want to be taken first (and will
> sleep/yield/drop)
>
> > Have a trace. I'm actually wondering if perhaps there's a missing
> > unlock_kernel() somewhere else, and the tty code is just the victim of
> > that.
>
> It's introduced by the BKL shuffle. Try the following
>
>
> commit 1f61f07a985c7e8cfc20ad8fcced2f3d226bd0dc
> Author: Alan Cox <[email protected]>
> Date: Sat Dec 12 10:32:36 2009 +0000
>
> tty: Fix the AB-BA locking bug introduced in the BKL split
>
> The fasync path takes the BKL (it probably doesn't need to in fact) but
> this causes lock inversions and deadlocks so we can't do that. Leave the
> BKL over that bit for the moment.
>
> Identified by AKPM.
>
> Signed-off-by: Alan Cox <[email protected]>

Acked-and-Tested-by: Thomas Gleixner <[email protected]>


> diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
> index 684f0e0..f15df40 100644
> --- a/drivers/char/tty_io.c
> +++ b/drivers/char/tty_io.c
> @@ -516,7 +516,6 @@ static void do_tty_hangup(struct work_struct *work)
> /* inuse_filps is protected by the single kernel lock */
> lock_kernel();
> check_tty_count(tty, "do_tty_hangup");
> - unlock_kernel();
>
> file_list_lock();
> /* This breaks for file handles being sent over AF_UNIX sockets ? */
> @@ -531,7 +530,6 @@ static void do_tty_hangup(struct work_struct *work)
> }
> file_list_unlock();
>
> - lock_kernel();
> tty_ldisc_hangup(tty);
>
> read_lock(&tasklist_lock);
>

2009-12-13 01:15:03

by Linus Torvalds

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git



On Sat, 12 Dec 2009, Thomas Gleixner wrote:
> >
> > Anybody willing to be the guinea-pig?
>
> Replaced my patch with yours and it works the same way (except for the
> PREEMPT=n case)
>
> Acked-and-Tested-by: Thomas Gleixner <[email protected]>

Ok, I also decided to just test it myself too (after applying the tty
layer fix) and it doesn't seem to cause any problems, so I've committed
it.

If there is dubious BKL usage that triggers the new might_sleep() warning
(and it turns out that it's necessary and not really fixable), we can
always just remove it again. But on the other hand, maybe it shows some
other potential problems that should just be fixed.

We've had quite a bit of BKL work this merge-window. Maybe we'll even get
rid of it one of these days. There are "only" about 600 instances of
"lock_kernel()" in the tree right now ;)

Linus

2009-12-13 06:59:14

by Ingo Molnar

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git


* Linus Torvalds <[email protected]> wrote:

> We've had quite a bit of BKL work this merge-window. Maybe we'll even get
> rid of it one of these days. There are "only" about 600 instances of
> "lock_kernel()" in the tree right now ;)

I tend to use unlock_kernel() as the metric. (as it's more precisely greppable
and it is also more indicative of the underlying complexity of locking, as it
gets used more in more complex scenarios)

In the last ~4.5 years:

earth4:~/tip> git checkout v2.6.12
Date: Fri Jun 17 12:48:29 2005 -0700
earth4:~/tip> git grep -w unlock_kernel | wc -l
713

earth4:~/tip> git checkout linus
Date: Fri Dec 11 20:58:20 2009 -0800
earth4:~/tip> git grep -w unlock_kernel | wc -l
841

we grew the (absolute) number of BKL sites by ~15%. Certainly the kernel grew
at a much faster rate, so the relative proportion of the BKL shrunk.

Also, a lot of BKL use was hidden before, and due to the BKL removal
activities (by Thomas, Frederic, Jon, Alan and others) the remaining BKL using
sites are a lot more well defined, a lot more isolated and thus a lot more
removable than ever before.

Ingo

2009-12-13 11:35:07

by Alan

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

> earth4:~/tip> git checkout linus
> Date: Fri Dec 11 20:58:20 2009 -0800
> earth4:~/tip> git grep -w unlock_kernel | wc -l
> 841
>
> we grew the (absolute) number of BKL sites by ~15%. Certainly the kernel grew
> at a much faster rate, so the relative proportion of the BKL shrunk.

Thats actually very misleading. The reason is we have created more
lock/unlock points as we remove and drive down the lock.

By your metric the original SMP kernel was best - it had one of each 8)

> Also, a lot of BKL use was hidden before, and due to the BKL removal
> activities (by Thomas, Frederic, Jon, Alan and others) the remaining BKL using
> sites are a lot more well defined, a lot more isolated and thus a lot more
> removable than ever before.

ioctl is almost done and I've gont some other random ones in my tree.
lseek is close. At that point most of the nasties are squashed except tty.

We do have some remaining locking horrors some partly introduced by the
finer locking work in the past including the rather nasty device
unload/load v open file handle races.

2009-12-13 12:13:39

by Ingo Molnar

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git


* Alan Cox <[email protected]> wrote:

> > earth4:~/tip> git checkout linus
> > Date: Fri Dec 11 20:58:20 2009 -0800
> > earth4:~/tip> git grep -w unlock_kernel | wc -l
> > 841
> >
> > we grew the (absolute) number of BKL sites by ~15%. Certainly the kernel grew
> > at a much faster rate, so the relative proportion of the BKL shrunk.
>
> Thats actually very misleading. [...]

Somewhat, so i qualified it with the next paragraph:

> > Also, a lot of BKL use was hidden before, and due to the BKL removal
> > activities (by Thomas, Frederic, Jon, Alan and others) the remaining BKL
> > using sites are a lot more well defined, a lot more isolated and thus a
> > lot more removable than ever before.

Thanks,

Ingo

2009-12-13 17:47:06

by Linus Torvalds

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git



On Sun, 13 Dec 2009, Ingo Molnar wrote:
>
> In the last ~4.5 years:
>
> earth4:~/tip> git checkout v2.6.12
> Date: Fri Jun 17 12:48:29 2005 -0700
> earth4:~/tip> git grep -w unlock_kernel | wc -l
> 713
>
> earth4:~/tip> git checkout linus
> Date: Fri Dec 11 20:58:20 2009 -0800
> earth4:~/tip> git grep -w unlock_kernel | wc -l
> 841

Git hint of the day: you don't need to check out a kernel to do "git
grep".

Do this:

git grep -w unlock_kernel v2.6.12 | wc

and it will JustWork(tm).

> Also, a lot of BKL use was hidden before, and due to the BKL removal
> activities (by Thomas, Frederic, Jon, Alan and others) the remaining BKL using
> sites are a lot more well defined, a lot more isolated and thus a lot more
> removable than ever before.

That's the main thing. We've been pushing them down a lot.

We still have a few annoying core ones, though. I hate the execve() and
file locking ones.

Linus

2009-12-13 17:53:33

by Arjan van de Ven

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sun, 13 Dec 2009 07:58:44 +0100
Ingo Molnar <[email protected]> wrote:

>
> * Linus Torvalds <[email protected]> wrote:
>
> > We've had quite a bit of BKL work this merge-window. Maybe we'll
> > even get rid of it one of these days. There are "only" about 600
> > instances of "lock_kernel()" in the tree right now ;)
>
> I tend to use unlock_kernel() as the metric. (as it's more precisely
> greppable and it is also more indicative of the underlying complexity
> of locking, as it gets used more in more complex scenarios)

another metric is... how many times do we take the BKL for some
workload. (For example booting or compiling a kernel).
A counter like "BKLs-per-second" would be nice to expose
(and then we can track that number going up as a regression etc)

For me, a secondary metric would be "how many times do we depend on the
magic auto-drop/reget behavior".. also easy to build a counter for.

--
Arjan van de Ven Intel Open Source Technology Centre
For development, discussion and tips for power savings,
visit http://www.lesswatts.org

2009-12-13 18:17:47

by Ingo Molnar

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git


* Linus Torvalds <[email protected]> wrote:

>
>
> On Sun, 13 Dec 2009, Ingo Molnar wrote:
> >
> > In the last ~4.5 years:
> >
> > earth4:~/tip> git checkout v2.6.12
> > Date: Fri Jun 17 12:48:29 2005 -0700
> > earth4:~/tip> git grep -w unlock_kernel | wc -l
> > 713
> >
> > earth4:~/tip> git checkout linus
> > Date: Fri Dec 11 20:58:20 2009 -0800
> > earth4:~/tip> git grep -w unlock_kernel | wc -l
> > 841
>
> Git hint of the day: you don't need to check out a kernel to do "git
> grep".
>
> Do this:
>
> git grep -w unlock_kernel v2.6.12 | wc
>
> and it will JustWork(tm).

/me adds it to the metal toolbox

> > Also, a lot of BKL use was hidden before, and due to the BKL removal
> > activities (by Thomas, Frederic, Jon, Alan and others) the remaining BKL using
> > sites are a lot more well defined, a lot more isolated and thus a lot more
> > removable than ever before.
>
> That's the main thing. We've been pushing them down a lot.
>
> We still have a few annoying core ones, though. I hate the execve() and file
> locking ones.

When we did the BKL-as-a-mutex trick and let lockdep loose on it, three areas
were particularly tricky: tty, reiser3 and NFS. tty and reiserfs should be ok
now, but i havent seen much activity on the NFS front.

Ingo

2009-12-13 18:33:47

by Trond Myklebust

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sun, 2009-12-13 at 19:17 +0100, Ingo Molnar wrote:
> When we did the BKL-as-a-mutex trick and let lockdep loose on it, three areas
> were particularly tricky: tty, reiser3 and NFS. tty and reiserfs should be ok
> now, but i havent seen much activity on the NFS front.

I've got a couple of NFS bkl removal patches queued up that I'll send on
to Linus today, but they will not suffice to fully remove BKL from the
NFS code.

The main remaining problem area is that of file locking (i.e. anything
that references inode->i_flock). I've started work on that, but a couple
of higher interrupts have prevented me from pulling it all together in
time for this merge window...

Trond

2009-12-13 19:04:19

by Frederic Weisbecker

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sun, Dec 13, 2009 at 07:17:26PM +0100, Ingo Molnar wrote:
>
> * Linus Torvalds <[email protected]> wrote:
>
> >
> >
> > On Sun, 13 Dec 2009, Ingo Molnar wrote:
> > >
> > > In the last ~4.5 years:
> > >
> > > earth4:~/tip> git checkout v2.6.12
> > > Date: Fri Jun 17 12:48:29 2005 -0700
> > > earth4:~/tip> git grep -w unlock_kernel | wc -l
> > > 713
> > >
> > > earth4:~/tip> git checkout linus
> > > Date: Fri Dec 11 20:58:20 2009 -0800
> > > earth4:~/tip> git grep -w unlock_kernel | wc -l
> > > 841
> >
> > Git hint of the day: you don't need to check out a kernel to do "git
> > grep".
> >
> > Do this:
> >
> > git grep -w unlock_kernel v2.6.12 | wc
> >
> > and it will JustWork(tm).
>
> /me adds it to the metal toolbox
>
> > > Also, a lot of BKL use was hidden before, and due to the BKL removal
> > > activities (by Thomas, Frederic, Jon, Alan and others) the remaining BKL using
> > > sites are a lot more well defined, a lot more isolated and thus a lot more
> > > removable than ever before.
> >
> > That's the main thing. We've been pushing them down a lot.
> >
> > We still have a few annoying core ones, though. I hate the execve() and file
> > locking ones.
>
> When we did the BKL-as-a-mutex trick and let lockdep loose on it, three areas
> were particularly tricky: tty, reiser3 and NFS. tty and reiserfs should be ok
> now, but i havent seen much activity on the NFS front.
>
> Ingo


Nfs was a problem in the BKL-as-a-mutex tree because it is acquired
recursively and then locked up. And I suspect this recursion happens
only on mount time because vfs acquires it too there.

But looking at it more closely, it doesn't look that dramatic at
a first glance.

git-grep unlock_kernel fs/nfs

fs/nfs/callback.c: unlock_kernel();
fs/nfs/callback.c: unlock_kernel();

In nfs4_callback_svc() it embraces the socket
listening/processing callback thread.

svc_recv() might sleep so the bkl can be
lost in the middle.

And then svc_process().

This doesn't seem to protect anything there.

In nfs4_callback_svc() it's about the same thing, plus
a list manipulation but protected by a spinlock.


fs/nfs/delegation.c: unlock_kernel();
fs/nfs/delegation.c: unlock_kernel();
fs/nfs/nfs4state.c: unlock_kernel();
fs/nfs/nfs4state.c: unlock_kernel();



In the above cases we have the following comment:

/* Protect inode->i_flock using the BKL */

And really it doesn't seem to protect anything else,
fortunately it is acquired in a short path.


fs/nfs/super.c: unlock_kernel();
fs/nfs/super.c: unlock_kernel();

Protect the mount/unmount path, a bit trickier there.

But really that looks way much easier to fix than it was
with reiserfs.

2009-12-13 19:08:35

by Linus Torvalds

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git



On Sun, 13 Dec 2009, Trond Myklebust wrote:
>
> The main remaining problem area is that of file locking (i.e. anything
> that references inode->i_flock). I've started work on that, but a couple
> of higher interrupts have prevented me from pulling it all together in
> time for this merge window...

I'm pretty sure we've had at least two trees with the file locking code
fixed, but NFS in a status of "unknown".

If I recall correctly, the file locking code itself is not that hard:
we've done it without the kernel lock in the past (long long ago), and the
lock usage doesn't nest (or at least it didn't at some point back then ;).
In fact, I think we even do the actual lock data structure allocations
outside of the kernel lock exactly because we at one time had a patch that
used a spinlock for protection of the lists.

(Again, not only my memory, but the code itself may have bitrotted in the
meantime, of course).

Linus

2009-12-13 19:09:34

by Trond Myklebust

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sun, 2009-12-13 at 20:04 +0100, Frederic Weisbecker wrote:
> In the above cases we have the following comment:
>
> /* Protect inode->i_flock using the BKL */
>
> And really it doesn't seem to protect anything else,
> fortunately it is acquired in a short path.

As I said in my reply, this is the tough one, because the BKL protection
is imposed by the VFS locking scheme used in fs/locks.c.

There is a similar dependency imposed upon fs/lockd/

I'm working on this, but I don't have anything ready for 2.6.33.

> fs/nfs/super.c: unlock_kernel();
> fs/nfs/super.c: unlock_kernel();
>
> Protect the mount/unmount path, a bit trickier there.
>
> But really that looks way much easier to fix than it was
> with reiserfs.

All the other cases you list should be fixed in the GIT PULL request
that I just put out.

Trond

2009-12-13 19:16:19

by Frederic Weisbecker

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sun, Dec 13, 2009 at 09:55:34AM -0800, Arjan van de Ven wrote:
> On Sun, 13 Dec 2009 07:58:44 +0100
> Ingo Molnar <[email protected]> wrote:
>
> >
> > * Linus Torvalds <[email protected]> wrote:
> >
> > > We've had quite a bit of BKL work this merge-window. Maybe we'll
> > > even get rid of it one of these days. There are "only" about 600
> > > instances of "lock_kernel()" in the tree right now ;)
> >
> > I tend to use unlock_kernel() as the metric. (as it's more precisely
> > greppable and it is also more indicative of the underlying complexity
> > of locking, as it gets used more in more complex scenarios)
>
> another metric is... how many times do we take the BKL for some
> workload. (For example booting or compiling a kernel).
> A counter like "BKLs-per-second" would be nice to expose
> (and then we can track that number going up as a regression etc)



We have the bkl tracepoints for that, attaching an example below,
blkdev_get/bkldev_put is among the highest consumer for me.

Then after we have (not sorted in order) tty, __blkdev_driver_ioctl(),
etc...


# tracer: nop
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
hald-addon-stor-4285 [000] 413.952233: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 413.969331: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 413.969343: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 413.989339: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 413.989349: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 413.992067: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [001] 415.953350: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 415.970537: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 415.970552: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 415.990404: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 415.990414: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 415.993139: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
less-5147 [001] 416.155702: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1847 func=tty_open()
less-5147 [001] 416.155712: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1849 func=tty_open()
lesspipe-5149 [001] 416.159402: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1847 func=tty_open()
lesspipe-5149 [001] 416.159412: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1849 func=tty_open()
lesspipe-5149 [001] 416.159418: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1870 func=tty_release()
lesspipe-5149 [001] 416.159419: lock_kernel: depth=1 file:line=drivers/char/tty_io.c:1911 func=tty_fasync()
lesspipe-5149 [001] 416.159421: unlock_kernel: depth=0 file:line=drivers/char/tty_io.c:1943 func=tty_fasync()
lesspipe-5149 [001] 416.159423: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1872 func=tty_release()
less-5147 [001] 420.459944: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1870 func=tty_release()
less-5147 [001] 420.459949: lock_kernel: depth=1 file:line=drivers/char/tty_io.c:1911 func=tty_fasync()
less-5147 [001] 420.459954: unlock_kernel: depth=0 file:line=drivers/char/tty_io.c:1943 func=tty_fasync()
less-5147 [001] 420.459960: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1872 func=tty_release()
hald-addon-stor-4285 [001] 421.954357: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 421.971499: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 421.971510: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 421.991585: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 421.991604: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 421.994321: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22215.805856: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22215.823023: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22215.823035: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22215.842989: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22215.843001: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22215.845718: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22217.805987: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22217.823165: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22217.823178: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22217.843182: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22217.843194: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22217.845932: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
less-10802 [000] 22218.162060: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1847 func=tty_open()
less-10802 [000] 22218.162078: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1849 func=tty_open()
lesspipe-10804 [001] 22218.169025: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1847 func=tty_open()
lesspipe-10804 [001] 22218.169043: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1849 func=tty_open()
lesspipe-10804 [001] 22218.169052: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1870 func=tty_release()
lesspipe-10804 [001] 22218.169054: lock_kernel: depth=1 file:line=drivers/char/tty_io.c:1911 func=tty_fasync()
lesspipe-10804 [001] 22218.169059: unlock_kernel: depth=0 file:line=drivers/char/tty_io.c:1943 func=tty_fasync()
lesspipe-10804 [001] 22218.169062: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1872 func=tty_release()
less-10802 [001] 22255.644555: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1870 func=tty_release()
less-10802 [001] 22255.644560: lock_kernel: depth=1 file:line=drivers/char/tty_io.c:1911 func=tty_fasync()
less-10802 [001] 22255.644565: unlock_kernel: depth=0 file:line=drivers/char/tty_io.c:1943 func=tty_fasync()
less-10802 [001] 22255.644571: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1872 func=tty_release()
hald-addon-stor-4285 [001] 22255.808942: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22255.826113: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22255.826126: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22255.846022: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22255.846034: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22255.848822: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
less-10810 [001] 22256.438465: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1847 func=tty_open()
less-10810 [001] 22256.438476: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1849 func=tty_open()
lesspipe-10812 [001] 22256.442197: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1847 func=tty_open()
lesspipe-10812 [001] 22256.442209: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1849 func=tty_open()
lesspipe-10812 [001] 22256.442215: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1870 func=tty_release()
lesspipe-10812 [001] 22256.442217: lock_kernel: depth=1 file:line=drivers/char/tty_io.c:1911 func=tty_fasync()
lesspipe-10812 [001] 22256.442220: unlock_kernel: depth=0 file:line=drivers/char/tty_io.c:1943 func=tty_fasync()
lesspipe-10812 [001] 22256.442223: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1872 func=tty_release()
less-10810 [001] 22288.556512: lock_kernel: depth=0 file:line=drivers/char/tty_io.c:1870 func=tty_release()
less-10810 [001] 22288.556518: lock_kernel: depth=1 file:line=drivers/char/tty_io.c:1911 func=tty_fasync()
less-10810 [001] 22288.556523: unlock_kernel: depth=0 file:line=drivers/char/tty_io.c:1943 func=tty_fasync()
less-10810 [001] 22288.556528: unlock_kernel: depth=-1 file:line=drivers/char/tty_io.c:1872 func=tty_release()
hald-addon-stor-4285 [001] 22289.811558: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22289.828644: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22289.828655: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22289.848642: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22289.848654: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22289.851377: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [001] 22291.811651: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22291.828861: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22291.828873: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22291.848808: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22291.848820: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22291.851539: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [001] 22293.811865: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22293.829005: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22293.829018: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22293.848924: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22293.848939: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22293.851648: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22295.812598: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22295.829710: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22295.829729: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22295.849774: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22295.849793: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22295.852533: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22297.812827: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22297.830066: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22297.830080: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22297.850050: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22297.850063: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22297.852787: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [001] 22299.813048: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22299.830191: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22299.830202: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22299.850124: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22299.850139: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22299.852861: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [001] 22301.813131: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22301.830331: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22301.830344: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22301.850322: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22301.850336: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22301.853056: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22303.813335: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22303.830509: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22303.830524: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22303.850524: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22303.850536: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22303.853252: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22305.812998: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22305.830173: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22305.830187: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22305.850228: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22305.850240: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22305.852977: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [001] 22307.813260: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22307.830358: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22307.830370: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22307.850336: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22307.850348: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22307.853092: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22309.812671: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22309.829824: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22309.829838: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22309.849862: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22309.849876: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22309.852593: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [001] 22311.813880: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22311.830955: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22311.830969: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22311.850934: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22311.850947: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22311.853683: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22313.813953: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22313.831033: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22313.831044: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22313.850994: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22313.851008: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22313.853724: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22315.814036: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22315.831141: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22315.831152: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22315.851149: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22315.851164: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22315.853885: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22317.814201: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22317.831298: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22317.831311: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22317.851279: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22317.851293: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22317.854013: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22319.814300: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22319.831452: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22319.831464: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22319.851428: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22319.851442: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22319.854164: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22321.814478: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22321.831562: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22321.831574: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22321.851544: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22321.851558: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22321.854283: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22323.814548: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22323.831653: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22323.831664: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22323.851570: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22323.851619: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22323.854349: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22325.814617: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22325.831827: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22325.831840: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22325.851802: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22325.851815: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22325.854538: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22327.814807: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22327.831958: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22327.831971: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22327.851955: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22327.851966: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22327.854674: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22329.814947: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22329.832137: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22329.832148: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22329.852105: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22329.852117: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22329.854842: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22331.815111: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22331.832302: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22331.832317: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22331.852379: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22331.852396: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22331.855122: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22333.814527: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22333.831013: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22333.831025: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22333.850780: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22333.850815: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22333.853545: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [001] 22335.814932: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22335.831815: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22335.831833: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22335.851497: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22335.851506: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22335.854229: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22337.816545: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22337.833716: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22337.833727: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22337.853705: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22337.853718: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22337.856494: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22339.815783: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22339.832582: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22339.832594: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22339.852607: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22339.852619: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22339.855342: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [001] 22341.815543: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [001] 22341.832662: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [001] 22341.832674: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22341.852565: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [001] 22341.852578: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [001] 22341.855297: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [001] 22343.816613: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22343.833956: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22343.833975: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22343.853966: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22343.853981: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22343.856700: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22345.816971: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22345.834130: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22345.834143: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22345.854081: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22345.854095: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22345.856815: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22347.817085: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22347.834270: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22347.834284: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22347.854281: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22347.854295: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22347.857011: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22349.817281: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22349.834499: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22349.834512: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22349.854373: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22349.854387: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22349.857103: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22351.817387: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22351.834567: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22351.834580: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22351.854612: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22351.854627: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22351.857350: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22353.817639: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22353.834644: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22353.834658: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22353.854721: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22353.854735: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22353.857476: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()
hald-addon-stor-4285 [000] 22355.817753: lock_kernel: depth=0 file:line=fs/block_dev.c:1192 func=__blkdev_get()
hald-addon-stor-4285 [000] 22355.834840: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1278 func=__blkdev_get()
hald-addon-stor-4285 [000] 22355.834854: lock_kernel: depth=0 file:line=block/ioctl.c:171 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22355.854832: unlock_kernel: depth=-1 file:line=block/ioctl.c:173 func=__blkdev_driver_ioctl()
hald-addon-stor-4285 [000] 22355.854852: lock_kernel: depth=0 file:line=fs/block_dev.c:1358 func=__blkdev_put()
hald-addon-stor-4285 [000] 22355.857591: unlock_kernel: depth=-1 file:line=fs/block_dev.c:1383 func=__blkdev_put()

2009-12-13 19:17:54

by Trond Myklebust

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sun, 2009-12-13 at 11:07 -0800, Linus Torvalds wrote:
>
> On Sun, 13 Dec 2009, Trond Myklebust wrote:
> >
> > The main remaining problem area is that of file locking (i.e. anything
> > that references inode->i_flock). I've started work on that, but a couple
> > of higher interrupts have prevented me from pulling it all together in
> > time for this merge window...
>
> I'm pretty sure we've had at least two trees with the file locking code
> fixed, but NFS in a status of "unknown".
>
> If I recall correctly, the file locking code itself is not that hard:
> we've done it without the kernel lock in the past (long long ago), and the
> lock usage doesn't nest (or at least it didn't at some point back then ;).
> In fact, I think we even do the actual lock data structure allocations
> outside of the kernel lock exactly because we at one time had a patch that
> used a spinlock for protection of the lists.

After the current set of patches, have been merged by you, the only
stuff that will continue to rely on nested BKL will be lockd. I can fix
that up in the next cycle.

> (Again, not only my memory, but the code itself may have bitrotted in the
> meantime, of course).

Agreed. I'm not saying that it's hard. I'm just saying that I ran out of
time due to other commitments.

Cheers
Trond

2009-12-13 19:20:44

by Linus Torvalds

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git



On Sun, 13 Dec 2009, Trond Myklebust wrote:

> On Sun, 2009-12-13 at 20:04 +0100, Frederic Weisbecker wrote:
> > In the above cases we have the following comment:
> >
> > /* Protect inode->i_flock using the BKL */
> >
> > And really it doesn't seem to protect anything else,
> > fortunately it is acquired in a short path.
>
> As I said in my reply, this is the tough one, because the BKL protection
> is imposed by the VFS locking scheme used in fs/locks.c.
>
> There is a similar dependency imposed upon fs/lockd/

Note that since NFS seems to be the only one who really cares, I think the
appropriate course of action is to just replace the BKL - preferably with
a spinlock that you just drop before you do anything that blocks.

Not only does the BKL already do that (so the locking doesn't change), but
I think most _users_ of the BKL actually already do the explicit dropping
of the lock (rather than the implicit one done by schedule()) because it's
already been a scalability issue and we've had some history of trying
alternative approaches that didn't do that whole auto-dropping anyway
(whether those alternate approaches be semaphores or spinlocks).

So don't worry about the "imposed by the VFS" thing. I think you can
fairly easily change the VFS side.

Linus

2009-12-13 19:20:13

by Frederic Weisbecker

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sun, Dec 13, 2009 at 02:09:14PM -0500, Trond Myklebust wrote:
> On Sun, 2009-12-13 at 20:04 +0100, Frederic Weisbecker wrote:
> > In the above cases we have the following comment:
> >
> > /* Protect inode->i_flock using the BKL */
> >
> > And really it doesn't seem to protect anything else,
> > fortunately it is acquired in a short path.
>
> As I said in my reply, this is the tough one, because the BKL protection
> is imposed by the VFS locking scheme used in fs/locks.c.
>
> There is a similar dependency imposed upon fs/lockd/



Ok.


> > fs/nfs/super.c: unlock_kernel();
> > fs/nfs/super.c: unlock_kernel();
> >
> > Protect the mount/unmount path, a bit trickier there.
> >
> > But really that looks way much easier to fix than it was
> > with reiserfs.
>
> All the other cases you list should be fixed in the GIT PULL request
> that I just put out.
>


Cool, thanks a lot!

2009-12-13 20:04:37

by Trond Myklebust

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sun, 2009-12-13 at 11:19 -0800, Linus Torvalds wrote:
>
> On Sun, 13 Dec 2009, Trond Myklebust wrote:
>
> > On Sun, 2009-12-13 at 20:04 +0100, Frederic Weisbecker wrote:
> > > In the above cases we have the following comment:
> > >
> > > /* Protect inode->i_flock using the BKL */
> > >
> > > And really it doesn't seem to protect anything else,
> > > fortunately it is acquired in a short path.
> >
> > As I said in my reply, this is the tough one, because the BKL protection
> > is imposed by the VFS locking scheme used in fs/locks.c.
> >
> > There is a similar dependency imposed upon fs/lockd/
>
> Note that since NFS seems to be the only one who really cares, I think the
> appropriate course of action is to just replace the BKL - preferably with
> a spinlock that you just drop before you do anything that blocks.
>
> Not only does the BKL already do that (so the locking doesn't change), but
> I think most _users_ of the BKL actually already do the explicit dropping
> of the lock (rather than the implicit one done by schedule()) because it's
> already been a scalability issue and we've had some history of trying
> alternative approaches that didn't do that whole auto-dropping anyway
> (whether those alternate approaches be semaphores or spinlocks).
>
> So don't worry about the "imposed by the VFS" thing. I think you can
> fairly easily change the VFS side.

That comment applies to the NFSv4 code, which applies the BKL only
because we need to traverse the inode->i_flock list. There is no
recursive use of the BKL there, and we can trivially replace it with
whichever new lock that that is chosen to replace the BKL in fs/locks.c.

To fix the lockd module, we have to backport some of the lessons we
learned when writing the NFSv4 file locking code. It won't be a huge
job, but I'd feel uncomfortable if I were to do it in a hurry just in
order to meet the merge window deadline.

Trond

2009-12-14 05:28:12

by Arjan van de Ven

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Sun, 13 Dec 2009 20:16:08 +0100
Frederic Weisbecker <[email protected]> wrote:

> On Sun, Dec 13, 2009 at 09:55:34AM -0800, Arjan van de Ven wrote:
> > On Sun, 13 Dec 2009 07:58:44 +0100
> > Ingo Molnar <[email protected]> wrote:
> >
> > >
> > > * Linus Torvalds <[email protected]> wrote:
> > >
> > > > We've had quite a bit of BKL work this merge-window. Maybe we'll
> > > > even get rid of it one of these days. There are "only" about 600
> > > > instances of "lock_kernel()" in the tree right now ;)
> > >
> > > I tend to use unlock_kernel() as the metric. (as it's more
> > > precisely greppable and it is also more indicative of the
> > > underlying complexity of locking, as it gets used more in more
> > > complex scenarios)
> >
> > another metric is... how many times do we take the BKL for some
> > workload. (For example booting or compiling a kernel).
> > A counter like "BKLs-per-second" would be nice to expose
> > (and then we can track that number going up as a regression etc)
>
>
>
> We have the bkl tracepoints for that, attaching an example below,
> blkdev_get/bkldev_put is among the highest consumer for me.

we have a trace, but not a number that anyone can just pull out without
having to go through great lengths to set stuff up... (esp to capture a
boot)...
Adding a counter always to the lock_kernel function should be fine
instead...


--
Arjan van de Ven Intel Open Source Technology Centre
For development, discussion and tips for power savings,
visit http://www.lesswatts.org

2009-12-14 10:39:39

by Oliver Neukum

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

Am Montag, 14. Dezember 2009 06:30:15 schrieb Arjan van de Ven:
> > We have the bkl tracepoints for that, attaching an example below,
> > blkdev_get/bkldev_put is among the highest consumer for me.
>
> we have a trace, but not a number that anyone can just pull out without
> having to go through great lengths to set stuff up... (esp to capture a
> boot)...
> Adding a counter always to the lock_kernel function should be fine
> instead...

But don't we need to know how long it is held? If you just count
you'll make code that drops it while it can look bad.

Regards
Oliver

2009-12-14 16:00:16

by Arjan van de Ven

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Mon, 14 Dec 2009 11:39:48 +0100
Oliver Neukum <[email protected]> wrote:

> Am Montag, 14. Dezember 2009 06:30:15 schrieb Arjan van de Ven:
> > > We have the bkl tracepoints for that, attaching an example below,
> > > blkdev_get/bkldev_put is among the highest consumer for me.
> >
> > we have a trace, but not a number that anyone can just pull out
> > without having to go through great lengths to set stuff up... (esp
> > to capture a boot)...
> > Adding a counter always to the lock_kernel function should be fine
> > instead...
>
> But don't we need to know how long it is held? If you just count
> you'll make code that drops it while it can look bad.

I think/hope we're well past that. At this point, just the act of taking
it at all is bad.


--
Arjan van de Ven Intel Open Source Technology Centre
For development, discussion and tips for power savings,
visit http://www.lesswatts.org

2009-12-16 09:16:47

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [GIT PATCH] TTY patches for 2.6.33-git

On Monday 14 December 2009 05:30:15 Arjan van de Ven wrote:
> On Sun, 13 Dec 2009 20:16:08 +0100 Frederic Weisbecker <[email protected]> wrote:
>
> > We have the bkl tracepoints for that, attaching an example below,
> > blkdev_get/bkldev_put is among the highest consumer for me.
>
> we have a trace, but not a number that anyone can just pull out without
> having to go through great lengths to set stuff up... (esp to capture a
> boot)...
> Adding a counter always to the lock_kernel function should be fine
> instead...

FWIW, I've hacked up some debugging logic in lib/kernel_lock.c to count
and measure the BKL. If anyone is interested in this, I can bring the
patch into a form that is usable. The output for a boot followed by
building a kernel on a four-way box with today's git looks like:

fs/ioctl.c:51 vfs_ioctl,5672809,0,9,108968293432,40528
drivers/char/vt_ioctl.c:512 vt_ioctl,15615,0,45,7967944,16400928
fs/locks.c:826 __posix_lock_file,1272,0,57,2815752,2013920
sound/core/sound.c:175 snd_open,1201,0,0,3863432,0
fs/block_dev.c:1192 __blkdev_get,479,26,14,39860472,686636416
drivers/usb/core/devio.c:656 usbdev_open,240,0,0,1640792,0
drivers/usb/core/devio.c:1868 usbdev_ioctl,240,0,0,369368,0
fs/read_write.c:110 default_llseek,185,0,5,88048,669224
drivers/char/tty_io.c:1761 tty_open,136,0,11,2878344,249472048
fs/block_dev.c:1358 __blkdev_put,117,1,1,10654208,3840
drivers/char/tty_io.c:1882 tty_open,106,0,5,44344,32056
drivers/char/tty_io.c:1603 tty_release,99,0,3,363496,8192
drivers/char/tty_io.c:1513 tty_release,99,0,3,639200,104312
kernel/ptrace.c:610 sys_ptrace,98,0,0,588432,0
block/ioctl.c:171 __blkdev_driver_ioctl,40,0,4,2960544,69288
fs/locks.c:1188 __break_lease,25,0,0,14960,0
fs/namespace.c:1650 do_new_mount,14,1,0,403088,0
fs/locks.c:733 flock_lock_file,14,0,0,33072,0
fs/locks.c:2022 locks_remove_flock,9,0,0,17160,0
drivers/char/pty.c:673 ptmx_open,8,0,0,406200,0
drivers/input/input.c:1767 input_open_file,7,0,0,83776,0
drivers/char/tty_io.c:717 disassociate_ctty,5,0,1,2376,322352
lib/kernel_lock.c:236 stat_read,3,0,0,590328,0
kernel/trace/trace.c:721 register_tracer,3,0,1,2420999112,17296
fs/locks.c:647 posix_test_lock,3,0,0,6920,0
drivers/gpu/drm/drm_fops.c:176 drm_stub_open,2,0,0,16712,0
init/main.c:849 kernel_init,1,0,0,334951112,0
init/main.c:546 start_kernel,1,0,0,873891904,0
fs/ext3/super.c:2548 ext3_remount,1,0,0,86592,0
fs/ext3/super.c:2036 ext3_fill_super,1,1,0,2656,0
drivers/gpu/drm/drm_fops.c:473 drm_release,1,0,0,6360,0
block/ioctl.c:316 blkdev_ioctl,1,0,1,848,32352

The comma-separated fields are
1. caller location
2. number of lock_kernel() calls
3. number of times it was called with BKL held already
4. number of times it blocked
5. total time it was held in get_cycles() units, not counting
time spent during release-on-sleep
6. total time that this lock_kernel() was blocked by another
thread holding the BKL.

It would be easy to add some recording of who is nesting
or blocking whom, or which ones call a function that might_sleep().

Arnd