dect
/
linux-2.6
Archived
13
0
Fork 0

TTY: ircomm, use tty from tty_port

This also includes a switch to tty refcounting. It makes sure, the
code no longer can access a freed TTY struct.

Sometimes the only thing needed is to pass tty down to the callies.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Samuel Ortiz <samuel@sortiz.org>
Cc: netdev@vger.kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Jiri Slaby 2012-06-04 13:35:21 +02:00 committed by Greg Kroah-Hartman
parent e673927d8a
commit 62f228acb8
5 changed files with 70 additions and 42 deletions

View File

@ -62,7 +62,6 @@ struct ircomm_tty_cb {
int state; /* Connect state */ int state; /* Connect state */
struct tty_struct *tty;
struct ircomm_cb *ircomm; /* IrCOMM layer instance */ struct ircomm_cb *ircomm; /* IrCOMM layer instance */
struct sk_buff *tx_skb; /* Transmit buffer */ struct sk_buff *tx_skb; /* Transmit buffer */

View File

@ -99,7 +99,6 @@ pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 };
*/ */
int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
{ {
struct tty_struct *tty;
unsigned long flags; unsigned long flags;
struct sk_buff *skb; struct sk_buff *skb;
int count; int count;
@ -109,10 +108,6 @@ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
tty = self->tty;
if (!tty)
return 0;
/* Make sure we don't send parameters for raw mode */ /* Make sure we don't send parameters for raw mode */
if (self->service_type == IRCOMM_3_WIRE_RAW) if (self->service_type == IRCOMM_3_WIRE_RAW)
return 0; return 0;

View File

@ -242,18 +242,15 @@ err:
* *
*/ */
static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
struct file *filp) struct tty_struct *tty, struct file *filp)
{ {
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
int retval; int retval;
int do_clocal = 0, extra_count = 0; int do_clocal = 0, extra_count = 0;
unsigned long flags; unsigned long flags;
struct tty_struct *tty;
IRDA_DEBUG(2, "%s()\n", __func__ ); IRDA_DEBUG(2, "%s()\n", __func__ );
tty = self->tty;
/* /*
* If non-blocking mode is set, or the port is not enabled, * If non-blocking mode is set, or the port is not enabled,
* then make the check up front and then exit. * then make the check up front and then exit.
@ -412,8 +409,8 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
self->port.count++; self->port.count++;
tty->driver_data = self; tty->driver_data = self;
self->tty = tty;
spin_unlock_irqrestore(&self->port.lock, flags); spin_unlock_irqrestore(&self->port.lock, flags);
tty_port_tty_set(&self->port, tty);
IRDA_DEBUG(1, "%s(), %s%d, count = %d\n", __func__ , tty->driver->name, IRDA_DEBUG(1, "%s(), %s%d, count = %d\n", __func__ , tty->driver->name,
self->line, self->port.count); self->line, self->port.count);
@ -467,7 +464,7 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
if (ret) if (ret)
return ret; return ret;
ret = ircomm_tty_block_til_ready(self, filp); ret = ircomm_tty_block_til_ready(self, tty, filp);
if (ret) { if (ret) {
IRDA_DEBUG(2, IRDA_DEBUG(2,
"%s(), returning after block_til_ready with %d\n", __func__ , "%s(), returning after block_til_ready with %d\n", __func__ ,
@ -548,7 +545,6 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp)
spin_lock_irqsave(&self->port.lock, flags); spin_lock_irqsave(&self->port.lock, flags);
tty->closing = 0; tty->closing = 0;
self->tty = NULL;
if (self->port.blocked_open) { if (self->port.blocked_open) {
if (self->port.close_delay) { if (self->port.close_delay) {
@ -562,6 +558,7 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp)
self->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); self->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
spin_unlock_irqrestore(&self->port.lock, flags); spin_unlock_irqrestore(&self->port.lock, flags);
wake_up_interruptible(&self->port.close_wait); wake_up_interruptible(&self->port.close_wait);
tty_port_tty_set(&self->port, NULL);
} }
/* /*
@ -604,7 +601,7 @@ static void ircomm_tty_do_softint(struct work_struct *work)
if (!self || self->magic != IRCOMM_TTY_MAGIC) if (!self || self->magic != IRCOMM_TTY_MAGIC)
return; return;
tty = self->tty; tty = tty_port_tty_get(&self->port);
if (!tty) if (!tty)
return; return;
@ -625,7 +622,7 @@ static void ircomm_tty_do_softint(struct work_struct *work)
} }
if (tty->hw_stopped) if (tty->hw_stopped)
return; goto put;
/* Unlink transmit buffer */ /* Unlink transmit buffer */
spin_lock_irqsave(&self->spinlock, flags); spin_lock_irqsave(&self->spinlock, flags);
@ -644,6 +641,8 @@ static void ircomm_tty_do_softint(struct work_struct *work)
/* Check if user (still) wants to be waken up */ /* Check if user (still) wants to be waken up */
tty_wakeup(tty); tty_wakeup(tty);
put:
tty_kref_put(tty);
} }
/* /*
@ -1004,7 +1003,11 @@ static void ircomm_tty_hangup(struct tty_struct *tty)
spin_lock_irqsave(&self->port.lock, flags); spin_lock_irqsave(&self->port.lock, flags);
self->port.flags &= ~ASYNC_NORMAL_ACTIVE; self->port.flags &= ~ASYNC_NORMAL_ACTIVE;
self->tty = NULL; if (self->port.tty) {
set_bit(TTY_IO_ERROR, &self->port.tty->flags);
tty_kref_put(self->port.tty);
}
self->port.tty = NULL;
self->port.count = 0; self->port.count = 0;
spin_unlock_irqrestore(&self->port.lock, flags); spin_unlock_irqrestore(&self->port.lock, flags);
@ -1068,7 +1071,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
tty = self->tty; tty = tty_port_tty_get(&self->port);
status = self->settings.dce; status = self->settings.dce;
@ -1089,10 +1092,10 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
tty_hangup(tty); tty_hangup(tty);
/* Hangup will remote the tty, so better break out */ /* Hangup will remote the tty, so better break out */
return; goto put;
} }
} }
if (self->port.flags & ASYNC_CTS_FLOW) { if (tty && self->port.flags & ASYNC_CTS_FLOW) {
if (tty->hw_stopped) { if (tty->hw_stopped) {
if (status & IRCOMM_CTS) { if (status & IRCOMM_CTS) {
IRDA_DEBUG(2, IRDA_DEBUG(2,
@ -1103,7 +1106,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
wake_up_interruptible(&self->port.open_wait); wake_up_interruptible(&self->port.open_wait);
schedule_work(&self->tqueue); schedule_work(&self->tqueue);
return; goto put;
} }
} else { } else {
if (!(status & IRCOMM_CTS)) { if (!(status & IRCOMM_CTS)) {
@ -1113,6 +1116,8 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
} }
} }
} }
put:
tty_kref_put(tty);
} }
/* /*
@ -1125,6 +1130,7 @@ static int ircomm_tty_data_indication(void *instance, void *sap,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
struct tty_struct *tty;
IRDA_DEBUG(2, "%s()\n", __func__ ); IRDA_DEBUG(2, "%s()\n", __func__ );
@ -1132,7 +1138,8 @@ static int ircomm_tty_data_indication(void *instance, void *sap,
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;); IRDA_ASSERT(skb != NULL, return -1;);
if (!self->tty) { tty = tty_port_tty_get(&self->port);
if (!tty) {
IRDA_DEBUG(0, "%s(), no tty!\n", __func__ ); IRDA_DEBUG(0, "%s(), no tty!\n", __func__ );
return 0; return 0;
} }
@ -1143,7 +1150,7 @@ static int ircomm_tty_data_indication(void *instance, void *sap,
* Devices like WinCE can do this, and since they don't send any * Devices like WinCE can do this, and since they don't send any
* params, we can just as well declare the hardware for running. * params, we can just as well declare the hardware for running.
*/ */
if (self->tty->hw_stopped && (self->flow == FLOW_START)) { if (tty->hw_stopped && (self->flow == FLOW_START)) {
IRDA_DEBUG(0, "%s(), polling for line settings!\n", __func__ ); IRDA_DEBUG(0, "%s(), polling for line settings!\n", __func__ );
ircomm_param_request(self, IRCOMM_POLL, TRUE); ircomm_param_request(self, IRCOMM_POLL, TRUE);
@ -1156,8 +1163,9 @@ static int ircomm_tty_data_indication(void *instance, void *sap,
* Use flip buffer functions since the code may be called from interrupt * Use flip buffer functions since the code may be called from interrupt
* context * context
*/ */
tty_insert_flip_string(self->tty, skb->data, skb->len); tty_insert_flip_string(tty, skb->data, skb->len);
tty_flip_buffer_push(self->tty); tty_flip_buffer_push(tty);
tty_kref_put(tty);
/* No need to kfree_skb - see ircomm_ttp_data_indication() */ /* No need to kfree_skb - see ircomm_ttp_data_indication() */
@ -1208,12 +1216,13 @@ static void ircomm_tty_flow_indication(void *instance, void *sap,
IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
tty = self->tty; tty = tty_port_tty_get(&self->port);
switch (cmd) { switch (cmd) {
case FLOW_START: case FLOW_START:
IRDA_DEBUG(2, "%s(), hw start!\n", __func__ ); IRDA_DEBUG(2, "%s(), hw start!\n", __func__ );
tty->hw_stopped = 0; if (tty)
tty->hw_stopped = 0;
/* ircomm_tty_do_softint will take care of the rest */ /* ircomm_tty_do_softint will take care of the rest */
schedule_work(&self->tqueue); schedule_work(&self->tqueue);
@ -1221,15 +1230,19 @@ static void ircomm_tty_flow_indication(void *instance, void *sap,
default: /* If we get here, something is very wrong, better stop */ default: /* If we get here, something is very wrong, better stop */
case FLOW_STOP: case FLOW_STOP:
IRDA_DEBUG(2, "%s(), hw stopped!\n", __func__ ); IRDA_DEBUG(2, "%s(), hw stopped!\n", __func__ );
tty->hw_stopped = 1; if (tty)
tty->hw_stopped = 1;
break; break;
} }
tty_kref_put(tty);
self->flow = cmd; self->flow = cmd;
} }
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m) static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m)
{ {
struct tty_struct *tty;
char sep; char sep;
seq_printf(m, "State: %s\n", ircomm_tty_state[self->state]); seq_printf(m, "State: %s\n", ircomm_tty_state[self->state]);
@ -1356,9 +1369,12 @@ static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m)
seq_printf(m, "Max data size: %d\n", self->max_data_size); seq_printf(m, "Max data size: %d\n", self->max_data_size);
seq_printf(m, "Max header size: %d\n", self->max_header_size); seq_printf(m, "Max header size: %d\n", self->max_header_size);
if (self->tty) tty = tty_port_tty_get(&self->port);
if (tty) {
seq_printf(m, "Hardware: %s\n", seq_printf(m, "Hardware: %s\n",
self->tty->hw_stopped ? "Stopped" : "Running"); tty->hw_stopped ? "Stopped" : "Running");
tty_kref_put(tty);
}
} }
static int ircomm_tty_proc_show(struct seq_file *m, void *v) static int ircomm_tty_proc_show(struct seq_file *m, void *v)

View File

@ -130,6 +130,8 @@ static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
*/ */
int ircomm_tty_attach_cable(struct ircomm_tty_cb *self) int ircomm_tty_attach_cable(struct ircomm_tty_cb *self)
{ {
struct tty_struct *tty;
IRDA_DEBUG(0, "%s()\n", __func__ ); IRDA_DEBUG(0, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self != NULL, return -1;);
@ -142,7 +144,11 @@ int ircomm_tty_attach_cable(struct ircomm_tty_cb *self)
} }
/* Make sure nobody tries to write before the link is up */ /* Make sure nobody tries to write before the link is up */
self->tty->hw_stopped = 1; tty = tty_port_tty_get(&self->port);
if (tty) {
tty->hw_stopped = 1;
tty_kref_put(tty);
}
ircomm_tty_ias_register(self); ircomm_tty_ias_register(self);
@ -398,23 +404,26 @@ void ircomm_tty_disconnect_indication(void *instance, void *sap,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
struct tty_struct *tty;
IRDA_DEBUG(2, "%s()\n", __func__ ); IRDA_DEBUG(2, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
if (!self->tty) tty = tty_port_tty_get(&self->port);
if (!tty)
return; return;
/* This will stop control data transfers */ /* This will stop control data transfers */
self->flow = FLOW_STOP; self->flow = FLOW_STOP;
/* Stop data transfers */ /* Stop data transfers */
self->tty->hw_stopped = 1; tty->hw_stopped = 1;
ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL, ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL,
NULL); NULL);
tty_kref_put(tty);
} }
/* /*
@ -550,12 +559,15 @@ void ircomm_tty_connect_indication(void *instance, void *sap,
*/ */
void ircomm_tty_link_established(struct ircomm_tty_cb *self) void ircomm_tty_link_established(struct ircomm_tty_cb *self)
{ {
struct tty_struct *tty;
IRDA_DEBUG(2, "%s()\n", __func__ ); IRDA_DEBUG(2, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
if (!self->tty) tty = tty_port_tty_get(&self->port);
if (!tty)
return; return;
del_timer(&self->watchdog_timer); del_timer(&self->watchdog_timer);
@ -569,17 +581,19 @@ void ircomm_tty_link_established(struct ircomm_tty_cb *self)
if ((self->port.flags & ASYNC_CTS_FLOW) && if ((self->port.flags & ASYNC_CTS_FLOW) &&
((self->settings.dce & IRCOMM_CTS) == 0)) { ((self->settings.dce & IRCOMM_CTS) == 0)) {
IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __func__ ); IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __func__ );
return; goto put;
} else { } else {
IRDA_DEBUG(1, "%s(), starting hardware!\n", __func__ ); IRDA_DEBUG(1, "%s(), starting hardware!\n", __func__ );
self->tty->hw_stopped = 0; tty->hw_stopped = 0;
/* Wake up processes blocked on open */ /* Wake up processes blocked on open */
wake_up_interruptible(&self->port.open_wait); wake_up_interruptible(&self->port.open_wait);
} }
schedule_work(&self->tqueue); schedule_work(&self->tqueue);
put:
tty_kref_put(tty);
} }
/* /*
@ -983,9 +997,12 @@ static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
self->settings.dce = IRCOMM_DELTA_CD; self->settings.dce = IRCOMM_DELTA_CD;
ircomm_tty_check_modem_status(self); ircomm_tty_check_modem_status(self);
} else { } else {
struct tty_struct *tty = tty_port_tty_get(&self->port);
IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ ); IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ );
if (self->tty) if (tty) {
tty_hangup(self->tty); tty_hangup(tty);
tty_kref_put(tty);
}
} }
break; break;
default: default:

View File

@ -52,17 +52,18 @@
* Change speed of the driver. If the remote device is a DCE, then this * Change speed of the driver. If the remote device is a DCE, then this
* should make it change the speed of its serial port * should make it change the speed of its serial port
*/ */
static void ircomm_tty_change_speed(struct ircomm_tty_cb *self) static void ircomm_tty_change_speed(struct ircomm_tty_cb *self,
struct tty_struct *tty)
{ {
unsigned int cflag, cval; unsigned int cflag, cval;
int baud; int baud;
IRDA_DEBUG(2, "%s()\n", __func__ ); IRDA_DEBUG(2, "%s()\n", __func__ );
if (!self->tty || !self->tty->termios || !self->ircomm) if (!self->ircomm)
return; return;
cflag = self->tty->termios->c_cflag; cflag = tty->termios->c_cflag;
/* byte size and parity */ /* byte size and parity */
switch (cflag & CSIZE) { switch (cflag & CSIZE) {
@ -81,7 +82,7 @@ static void ircomm_tty_change_speed(struct ircomm_tty_cb *self)
cval |= IRCOMM_PARITY_EVEN; cval |= IRCOMM_PARITY_EVEN;
/* Determine divisor based on baud rate */ /* Determine divisor based on baud rate */
baud = tty_get_baud_rate(self->tty); baud = tty_get_baud_rate(tty);
if (!baud) if (!baud)
baud = 9600; /* B0 transition handled in rs_set_termios */ baud = 9600; /* B0 transition handled in rs_set_termios */
@ -159,7 +160,7 @@ void ircomm_tty_set_termios(struct tty_struct *tty,
return; return;
} }
ircomm_tty_change_speed(self); ircomm_tty_change_speed(self, tty);
/* Handle transition to B0 status */ /* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) && if ((old_termios->c_cflag & CBAUD) &&