Skip to content

Commit

Permalink
tty: usb-serial krefs
Browse files Browse the repository at this point in the history
Use kref in the USB serial drivers so that we don't free tty structures
from under the URB receive handlers as has historically been the case if
you were unlucky. This also gives us a framework for general tty drivers to
use tty_port objects and refcount.

Contains two err->dev_err changes merged together to fix clashes in the
-next tree.

Signed-off-by: Alan Cox <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Alan Cox authored and torvalds committed Oct 13, 2008
1 parent 95f9bfc commit 4a90f09
Show file tree
Hide file tree
Showing 37 changed files with 313 additions and 208 deletions.
41 changes: 41 additions & 0 deletions drivers/char/tty_port.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ void tty_port_init(struct tty_port *port)
init_waitqueue_head(&port->open_wait);
init_waitqueue_head(&port->close_wait);
mutex_init(&port->mutex);
spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100;
}
Expand Down Expand Up @@ -53,3 +54,43 @@ void tty_port_free_xmit_buf(struct tty_port *port)
EXPORT_SYMBOL(tty_port_free_xmit_buf);


/**
* tty_port_tty_get - get a tty reference
* @port: tty port
*
* Return a refcount protected tty instance or NULL if the port is not
* associated with a tty (eg due to close or hangup)
*/

struct tty_struct *tty_port_tty_get(struct tty_port *port)
{
unsigned long flags;
struct tty_struct *tty;

spin_lock_irqsave(&port->lock, flags);
tty = tty_kref_get(port->tty);
spin_unlock_irqrestore(&port->lock, flags);
return tty;
}
EXPORT_SYMBOL(tty_port_tty_get);

/**
* tty_port_tty_set - set the tty of a port
* @port: tty port
* @tty: the tty
*
* Associate the port and tty pair. Manages any internal refcounts.
* Pass NULL to deassociate a port
*/

void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty)
{
unsigned long flags;

spin_lock_irqsave(&port->lock, flags);
if (port->tty)
tty_kref_put(port->tty);
port->tty = tty;
spin_unlock_irqrestore(&port->lock, flags);
}
EXPORT_SYMBOL(tty_port_tty_set);
15 changes: 9 additions & 6 deletions drivers/usb/serial/aircable.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ static void aircable_read(struct work_struct *work)
* 64 bytes, to ensure I do not get throttled.
* Ask USB mailing list for better aproach.
*/
tty = port->port.tty;
tty = tty_port_tty_get(&port->port);

if (!tty) {
schedule_work(&priv->rx_work);
Expand All @@ -283,12 +283,13 @@ static void aircable_read(struct work_struct *work)
count = min(64, serial_buf_data_avail(priv->rx_buf));

if (count <= 0)
return; /* We have finished sending everything. */
goto out; /* We have finished sending everything. */

tty_prepare_flip_string(tty, &data, count);
if (!data) {
err("%s- kzalloc(%d) failed.", __func__, count);
return;
dev_err(&port->dev, "%s- kzalloc(%d) failed.",
__func__, count);
goto out;
}

serial_buf_get(priv->rx_buf, data, count);
Expand All @@ -297,7 +298,8 @@ static void aircable_read(struct work_struct *work)

if (serial_buf_data_avail(priv->rx_buf))
schedule_work(&priv->rx_work);

out:
tty_kref_put(tty);
return;
}
/* End of private methods */
Expand Down Expand Up @@ -495,7 +497,7 @@ static void aircable_read_bulk_callback(struct urb *urb)
usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, urb->transfer_buffer);

tty = port->port.tty;
tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
if (urb->actual_length <= 2) {
/* This is an incomplete package */
Expand Down Expand Up @@ -527,6 +529,7 @@ static void aircable_read_bulk_callback(struct urb *urb)
}
aircable_read(&priv->rx_work);
}
tty_kref_put(tty);

/* Schedule the next read _if_ we are still open */
if (port->port.count) {
Expand Down
3 changes: 2 additions & 1 deletion drivers/usb/serial/belkin_sa.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ static void belkin_sa_read_int_callback(struct urb *urb)
* to look in to this before committing any code.
*/
if (priv->last_lsr & BELKIN_SA_LSR_ERR) {
tty = port->port.tty;
tty = tty_port_tty_get(&port->port);
/* Overrun Error */
if (priv->last_lsr & BELKIN_SA_LSR_OE) {
}
Expand All @@ -335,6 +335,7 @@ static void belkin_sa_read_int_callback(struct urb *urb)
/* Break Indicator */
if (priv->last_lsr & BELKIN_SA_LSR_BI) {
}
tty_kref_put(tty);
}
#endif
spin_unlock_irqrestore(&priv->lock, flags);
Expand Down
8 changes: 4 additions & 4 deletions drivers/usb/serial/console.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ static int usb_console_setup(struct console *co, char *options)
}

port = serial->port[0];
port->port.tty = NULL;
tty_port_tty_set(&port->port, NULL);

info->port = port;

Expand All @@ -143,7 +143,7 @@ static int usb_console_setup(struct console *co, char *options)
}
memset(&dummy, 0, sizeof(struct ktermios));
tty->termios = termios;
port->port.tty = tty;
tty_port_tty_set(&port->port, tty);
}

/* only call the device specific open if this
Expand All @@ -163,7 +163,7 @@ static int usb_console_setup(struct console *co, char *options)
tty_termios_encode_baud_rate(termios, baud, baud);
serial->type->set_termios(tty, port, &dummy);

port->port.tty = NULL;
tty_port_tty_set(&port->port, NULL);
kfree(termios);
kfree(tty);
}
Expand All @@ -176,7 +176,7 @@ static int usb_console_setup(struct console *co, char *options)
return retval;
free_termios:
kfree(termios);
port->port.tty = NULL;
tty_port_tty_set(&port->port, NULL);
free_tty:
kfree(tty);
reset_open_count:
Expand Down
3 changes: 2 additions & 1 deletion drivers/usb/serial/cyberjack.c
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb)
return;
}

tty = port->port.tty;
tty = tty_port_tty_get(&port->port);
if (!tty) {
dbg("%s - ignoring since device not open\n", __func__);
return;
Expand All @@ -394,6 +394,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb)
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
tty_kref_put(tty);

spin_lock(&priv->lock);

Expand Down
5 changes: 3 additions & 2 deletions drivers/usb/serial/cypress_m8.c
Original file line number Diff line number Diff line change
Expand Up @@ -1286,7 +1286,7 @@ static void cypress_read_int_callback(struct urb *urb)
}
spin_unlock_irqrestore(&priv->lock, flags);

tty = port->port.tty;
tty = tty_port_tty_get(&port->port);
if (!tty) {
dbg("%s - bad tty pointer - exiting", __func__);
return;
Expand Down Expand Up @@ -1362,7 +1362,7 @@ static void cypress_read_int_callback(struct urb *urb)
data[i]);
tty_insert_flip_char(tty, data[i], tty_flag);
}
tty_flip_buffer_push(port->port.tty);
tty_flip_buffer_push(tty);
}

spin_lock_irqsave(&priv->lock, flags);
Expand All @@ -1371,6 +1371,7 @@ static void cypress_read_int_callback(struct urb *urb)
spin_unlock_irqrestore(&priv->lock, flags);

continue_read:
tty_kref_put(tty);

/* Continue trying to always read... unless the port has closed. */

Expand Down
19 changes: 13 additions & 6 deletions drivers/usb/serial/digi_acceleport.c
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,9 @@ static void digi_wakeup_write_lock(struct work_struct *work)

static void digi_wakeup_write(struct usb_serial_port *port)
{
tty_wakeup(port->port.tty);
struct tty_struct *tty = tty_port_tty_get(&port->port);
tty_wakeup(tty);
tty_kref_put(tty);
}


Expand Down Expand Up @@ -1668,7 +1670,7 @@ static int digi_read_inb_callback(struct urb *urb)
{

struct usb_serial_port *port = urb->context;
struct tty_struct *tty = port->port.tty;
struct tty_struct *tty;
struct digi_port *priv = usb_get_serial_port_data(port);
int opcode = ((unsigned char *)urb->transfer_buffer)[0];
int len = ((unsigned char *)urb->transfer_buffer)[1];
Expand All @@ -1692,6 +1694,7 @@ static int digi_read_inb_callback(struct urb *urb)
return -1;
}

tty = tty_port_tty_get(&port->port);
spin_lock(&priv->dp_port_lock);

/* check for throttle; if set, do not resubmit read urb */
Expand Down Expand Up @@ -1735,6 +1738,7 @@ static int digi_read_inb_callback(struct urb *urb)
}
}
spin_unlock(&priv->dp_port_lock);
tty_kref_put(tty);

if (opcode == DIGI_CMD_RECEIVE_DISABLE)
dbg("%s: got RECEIVE_DISABLE", __func__);
Expand All @@ -1760,6 +1764,7 @@ static int digi_read_oob_callback(struct urb *urb)

struct usb_serial_port *port = urb->context;
struct usb_serial *serial = port->serial;
struct tty_struct *tty;
struct digi_port *priv = usb_get_serial_port_data(port);
int opcode, line, status, val;
int i;
Expand Down Expand Up @@ -1787,25 +1792,26 @@ static int digi_read_oob_callback(struct urb *urb)
if (priv == NULL)
return -1;

tty = tty_port_tty_get(&port->port);
rts = 0;
if (port->port.count)
rts = port->port.tty->termios->c_cflag & CRTSCTS;

rts = tty->termios->c_cflag & CRTSCTS;
if (opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
spin_lock(&priv->dp_port_lock);
/* convert from digi flags to termiox flags */
if (val & DIGI_READ_INPUT_SIGNALS_CTS) {
priv->dp_modem_signals |= TIOCM_CTS;
/* port must be open to use tty struct */
if (rts) {
port->port.tty->hw_stopped = 0;
tty->hw_stopped = 0;
digi_wakeup_write(port);
}
} else {
priv->dp_modem_signals &= ~TIOCM_CTS;
/* port must be open to use tty struct */
if (rts)
port->port.tty->hw_stopped = 1;
tty->hw_stopped = 1;
}
if (val & DIGI_READ_INPUT_SIGNALS_DSR)
priv->dp_modem_signals |= TIOCM_DSR;
Expand All @@ -1830,6 +1836,7 @@ static int digi_read_oob_callback(struct urb *urb)
} else if (opcode == DIGI_CMD_IFLUSH_FIFO) {
wake_up_interruptible(&priv->dp_flush_wait);
}
tty_kref_put(tty);
}
return 0;

Expand Down
8 changes: 4 additions & 4 deletions drivers/usb/serial/empeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@
* Moved MOD_DEC_USE_COUNT to end of empeg_close().
*
* (12/03/2000) gb
* Added port->port.tty->ldisc.set_termios(port->port.tty, NULL) to
* empeg_open(). This notifies the tty driver that the termios have
* changed.
* Added tty->ldisc.set_termios(port, tty, NULL) to empeg_open().
* This notifies the tty driver that the termios have changed.
*
* (11/13/2000) gb
* Moved tty->low_latency = 1 from empeg_read_bulk_callback() to
Expand Down Expand Up @@ -354,14 +353,15 @@ static void empeg_read_bulk_callback(struct urb *urb)

usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, data);
tty = port->port.tty;
tty = tty_port_tty_get(&port->port);

if (urb->actual_length) {
tty_buffer_request_room(tty, urb->actual_length);
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
bytes_in += urb->actual_length;
}
tty_kref_put(tty);

/* Continue trying to always read */
usb_fill_bulk_urb(
Expand Down
19 changes: 11 additions & 8 deletions drivers/usb/serial/ftdi_sio.c
Original file line number Diff line number Diff line change
Expand Up @@ -1808,7 +1808,7 @@ static void ftdi_read_bulk_callback(struct urb *urb)
if (port->port.count <= 0)
return;

tty = port->port.tty;
tty = tty_port_tty_get(&port->port);
if (!tty) {
dbg("%s - bad tty pointer - exiting", __func__);
return;
Expand All @@ -1817,7 +1817,7 @@ static void ftdi_read_bulk_callback(struct urb *urb)
priv = usb_get_serial_port_data(port);
if (!priv) {
dbg("%s - bad port private data pointer - exiting", __func__);
return;
goto out;
}

if (urb != port->read_urb)
Expand All @@ -1827,7 +1827,7 @@ static void ftdi_read_bulk_callback(struct urb *urb)
/* This will happen at close every time so it is a dbg not an
err */
dbg("(this is ok on close) nonzero read bulk status received: %d", status);
return;
goto out;
}

/* count data bytes, but not status bytes */
Expand All @@ -1838,7 +1838,8 @@ static void ftdi_read_bulk_callback(struct urb *urb)
spin_unlock_irqrestore(&priv->rx_lock, flags);

ftdi_process_read(&priv->rx_work.work);

out:
tty_kref_put(tty);
} /* ftdi_read_bulk_callback */


Expand All @@ -1863,7 +1864,7 @@ static void ftdi_process_read(struct work_struct *work)
if (port->port.count <= 0)
return;

tty = port->port.tty;
tty = tty_port_tty_get(&port->port);
if (!tty) {
dbg("%s - bad tty pointer - exiting", __func__);
return;
Expand All @@ -1872,13 +1873,13 @@ static void ftdi_process_read(struct work_struct *work)
priv = usb_get_serial_port_data(port);
if (!priv) {
dbg("%s - bad port private data pointer - exiting", __func__);
return;
goto out;
}

urb = port->read_urb;
if (!urb) {
dbg("%s - bad read_urb pointer - exiting", __func__);
return;
goto out;
}

data = urb->transfer_buffer;
Expand Down Expand Up @@ -2020,7 +2021,7 @@ static void ftdi_process_read(struct work_struct *work)
schedule_delayed_work(&priv->rx_work, 1);
else
dbg("%s - port is closed", __func__);
return;
goto out;
}

/* urb is completely processed */
Expand All @@ -2041,6 +2042,8 @@ static void ftdi_process_read(struct work_struct *work)
err("%s - failed resubmitting read urb, error %d",
__func__, result);
}
out:
tty_kref_put(tty);
} /* ftdi_process_read */


Expand Down
Loading

0 comments on commit 4a90f09

Please sign in to comment.