TTY: ircomm, use tty from tty_port
authorJiri Slaby <jslaby@suse.cz>
Mon, 4 Jun 2012 11:35:21 +0000 (13:35 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Jun 2012 22:50:24 +0000 (15:50 -0700)
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>
include/net/irda/ircomm_tty.h
net/irda/ircomm/ircomm_param.c
net/irda/ircomm/ircomm_tty.c
net/irda/ircomm/ircomm_tty_attach.c
net/irda/ircomm/ircomm_tty_ioctl.c

index a9027d8f670df6884e222f02791fc62b7d90fb19..80ffde3bb164e2a3a856838f0c8c4c2906bfeaf6 100644 (file)
@@ -62,7 +62,6 @@ struct ircomm_tty_cb {
 
        int state;                /* Connect state */
 
-       struct tty_struct *tty;
        struct ircomm_cb *ircomm; /* IrCOMM layer instance */
 
        struct sk_buff *tx_skb;   /* Transmit buffer */
index 8b915f3ac3b91a6b2471149799117d8bdd5b8b00..30893912835926a2bbc83ce716a1fdbf242c81a4 100644 (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)
 {
-       struct tty_struct *tty;
        unsigned long flags;
        struct sk_buff *skb;
        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->magic == IRCOMM_TTY_MAGIC, return -1;);
 
-       tty = self->tty;
-       if (!tty)
-               return 0;
-
        /* Make sure we don't send parameters for raw mode */
        if (self->service_type == IRCOMM_3_WIRE_RAW)
                return 0;
index 7b2152cfd42ab13cc9d1b9ead527243669d939b7..03acc073b8c33b15009b2b8a17d699b012241a9c 100644 (file)
@@ -242,18 +242,15 @@ err:
  *
  */
 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);
        int             retval;
        int             do_clocal = 0, extra_count = 0;
        unsigned long   flags;
-       struct tty_struct *tty;
 
        IRDA_DEBUG(2, "%s()\n", __func__ );
 
-       tty = self->tty;
-
        /*
         * If non-blocking mode is set, or the port is not enabled,
         * 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++;
 
        tty->driver_data = self;
-       self->tty = tty;
        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,
                   self->line, self->port.count);
@@ -467,7 +464,7 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
        if (ret)
                return ret;
 
-       ret = ircomm_tty_block_til_ready(self, filp);
+       ret = ircomm_tty_block_til_ready(self, tty, filp);
        if (ret) {
                IRDA_DEBUG(2,
                      "%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);
        tty->closing = 0;
-       self->tty = NULL;
 
        if (self->port.blocked_open) {
                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);
        spin_unlock_irqrestore(&self->port.lock, flags);
        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)
                return;
 
-       tty = self->tty;
+       tty = tty_port_tty_get(&self->port);
        if (!tty)
                return;
 
@@ -625,7 +622,7 @@ static void ircomm_tty_do_softint(struct work_struct *work)
        }
 
        if (tty->hw_stopped)
-               return;
+               goto put;
 
        /* Unlink transmit buffer */
        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 */
        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);
        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;
        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->magic == IRCOMM_TTY_MAGIC, return;);
 
-       tty = self->tty;
+       tty = tty_port_tty_get(&self->port);
 
        status = self->settings.dce;
 
@@ -1089,10 +1092,10 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
                                tty_hangup(tty);
 
                        /* 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 (status & IRCOMM_CTS) {
                                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);
 
                                schedule_work(&self->tqueue);
-                               return;
+                               goto put;
                        }
                } else {
                        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 ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+       struct tty_struct *tty;
 
        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(skb != NULL, return -1;);
 
-       if (!self->tty) {
+       tty = tty_port_tty_get(&self->port);
+       if (!tty) {
                IRDA_DEBUG(0, "%s(), no tty!\n", __func__ );
                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
         * 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__ );
                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
         * context
         */
-       tty_insert_flip_string(self->tty, skb->data, skb->len);
-       tty_flip_buffer_push(self->tty);
+       tty_insert_flip_string(tty, skb->data, skb->len);
+       tty_flip_buffer_push(tty);
+       tty_kref_put(tty);
 
        /* 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->magic == IRCOMM_TTY_MAGIC, return;);
 
-       tty = self->tty;
+       tty = tty_port_tty_get(&self->port);
 
        switch (cmd) {
        case FLOW_START:
                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 */
                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 */
        case FLOW_STOP:
                IRDA_DEBUG(2, "%s(), hw stopped!\n", __func__ );
-               tty->hw_stopped = 1;
+               if (tty)
+                       tty->hw_stopped = 1;
                break;
        }
+
+       tty_kref_put(tty);
        self->flow = cmd;
 }
 
 #ifdef CONFIG_PROC_FS
 static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m)
 {
+       struct tty_struct *tty;
        char sep;
 
        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 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",
-                              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)
index bed311af7311bf63c7e84f8d3968570fbef0cf1e..3ab70e7a07157821b3112578528ce63b99c4389c 100644 (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)
 {
+       struct tty_struct *tty;
+
        IRDA_DEBUG(0, "%s()\n", __func__ );
 
        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 */
-       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);
 
@@ -398,23 +404,26 @@ void ircomm_tty_disconnect_indication(void *instance, void *sap,
                                      struct sk_buff *skb)
 {
        struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+       struct tty_struct *tty;
 
        IRDA_DEBUG(2, "%s()\n", __func__ );
 
        IRDA_ASSERT(self != NULL, return;);
        IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
-       if (!self->tty)
+       tty = tty_port_tty_get(&self->port);
+       if (!tty)
                return;
 
        /* This will stop control data transfers */
        self->flow = FLOW_STOP;
 
        /* Stop data transfers */
-       self->tty->hw_stopped = 1;
+       tty->hw_stopped = 1;
 
        ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, 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)
 {
+       struct tty_struct *tty;
+
        IRDA_DEBUG(2, "%s()\n", __func__ );
 
        IRDA_ASSERT(self != NULL, return;);
        IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
-       if (!self->tty)
+       tty = tty_port_tty_get(&self->port);
+       if (!tty)
                return;
 
        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) &&
                        ((self->settings.dce & IRCOMM_CTS) == 0)) {
                IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __func__ );
-               return;
+               goto put;
        } else {
                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_interruptible(&self->port.open_wait);
        }
 
        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;
                        ircomm_tty_check_modem_status(self);
                } else {
+                       struct tty_struct *tty = tty_port_tty_get(&self->port);
                        IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ );
-                       if (self->tty)
-                               tty_hangup(self->tty);
+                       if (tty) {
+                               tty_hangup(tty);
+                               tty_kref_put(tty);
+                       }
                }
                break;
        default:
index 31b917e9c8d8e7ce560649f58f9a9bcec5d3c715..0eab6500e99f7425a0c1f370bc3bbd785b6a1c3d 100644 (file)
  *    Change speed of the driver. If the remote device is a DCE, then this
  *    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;
        int baud;
 
        IRDA_DEBUG(2, "%s()\n", __func__ );
 
-       if (!self->tty || !self->tty->termios || !self->ircomm)
+       if (!self->ircomm)
                return;
 
-       cflag = self->tty->termios->c_cflag;
+       cflag = tty->termios->c_cflag;
 
        /*  byte size and parity */
        switch (cflag & CSIZE) {
@@ -81,7 +82,7 @@ static void ircomm_tty_change_speed(struct ircomm_tty_cb *self)
                cval |= IRCOMM_PARITY_EVEN;
 
        /* Determine divisor based on baud rate */
-       baud = tty_get_baud_rate(self->tty);
+       baud = tty_get_baud_rate(tty);
        if (!baud)
                baud = 9600;    /* B0 transition handled in rs_set_termios */
 
@@ -159,7 +160,7 @@ void ircomm_tty_set_termios(struct tty_struct *tty,
                return;
        }
 
-       ircomm_tty_change_speed(self);
+       ircomm_tty_change_speed(self, tty);
 
        /* Handle transition to B0 status */
        if ((old_termios->c_cflag & CBAUD) &&