aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/n_gsm.c
diff options
context:
space:
mode:
authorDirkjan Bussink <d.bussink@gmail.com>2013-01-30 05:44:50 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-01-30 06:10:09 -0500
commit4d9b109060f690f5c835130ff54165ae157b3087 (patch)
treef372d400b2b6cdb3f6c19b4a57b151fb3bc58001 /drivers/tty/n_gsm.c
parentc420811f117a59a4a7d4e34b362437b91c7fafa1 (diff)
tty: Prevent deadlock in n_gsm driver
This change fixes a deadlock when the multiplexer is closed while there are still client side ports open. When the multiplexer is closed and there are active tty's it tries to close them with tty_vhangup. This has a problem though, because tty_vhangup needs the tty_lock. This patch changes it to unlock the tty_lock before attempting the hangup and relocks afterwards. The additional call to tty_port_tty_set is needed because otherwise the port stays active because of the reference counter. This change also exposed another problem that other code paths don't expect that the multiplexer could have been closed. This patch also adds checks for these cases in the gsmtty_ class of function that could be called. The documentation explicitly states that "first close all virtual ports before closing the physical port" but we've found this to not always reality in our field situations. The GPRS / UTMS modem sometimes crashes and needs a power cycle in that case which means cleanly shutting down everything is not always possible. This change makes it much more robust for our situation where at least the system is recoverable with this patch and doesn't hang in a deadlock situation inside the kernel. The patch is against the long term support kernel (3.4.27) and should apply cleanly to more recent branches. Tested with a Telit GE864-QUADV2 and Telit HE910 modem. Signed-off-by: Dirkjan Bussink <dirkjan.bussink@nedap.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/n_gsm.c')
-rw-r--r--drivers/tty/n_gsm.c42
1 files changed, 41 insertions, 1 deletions
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index e0f80ce0cf8f..4a43ef5d7962 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -1689,6 +1689,8 @@ static inline void dlci_put(struct gsm_dlci *dlci)
1689 tty_port_put(&dlci->port); 1689 tty_port_put(&dlci->port);
1690} 1690}
1691 1691
1692static void gsm_destroy_network(struct gsm_dlci *dlci);
1693
1692/** 1694/**
1693 * gsm_dlci_release - release DLCI 1695 * gsm_dlci_release - release DLCI
1694 * @dlci: DLCI to destroy 1696 * @dlci: DLCI to destroy
@@ -1702,9 +1704,19 @@ static void gsm_dlci_release(struct gsm_dlci *dlci)
1702{ 1704{
1703 struct tty_struct *tty = tty_port_tty_get(&dlci->port); 1705 struct tty_struct *tty = tty_port_tty_get(&dlci->port);
1704 if (tty) { 1706 if (tty) {
1707 mutex_lock(&dlci->mutex);
1708 gsm_destroy_network(dlci);
1709 mutex_unlock(&dlci->mutex);
1710
1711 /* tty_vhangup needs the tty_lock, so unlock and
1712 relock after doing the hangup. */
1713 tty_unlock(tty);
1705 tty_vhangup(tty); 1714 tty_vhangup(tty);
1715 tty_lock(tty);
1716 tty_port_tty_set(&dlci->port, NULL);
1706 tty_kref_put(tty); 1717 tty_kref_put(tty);
1707 } 1718 }
1719 dlci->state = DLCI_CLOSED;
1708 dlci_put(dlci); 1720 dlci_put(dlci);
1709} 1721}
1710 1722
@@ -2947,6 +2959,8 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp)
2947 2959
2948 if (dlci == NULL) 2960 if (dlci == NULL)
2949 return; 2961 return;
2962 if (dlci->state == DLCI_CLOSED)
2963 return;
2950 mutex_lock(&dlci->mutex); 2964 mutex_lock(&dlci->mutex);
2951 gsm_destroy_network(dlci); 2965 gsm_destroy_network(dlci);
2952 mutex_unlock(&dlci->mutex); 2966 mutex_unlock(&dlci->mutex);
@@ -2965,6 +2979,8 @@ out:
2965static void gsmtty_hangup(struct tty_struct *tty) 2979static void gsmtty_hangup(struct tty_struct *tty)
2966{ 2980{
2967 struct gsm_dlci *dlci = tty->driver_data; 2981 struct gsm_dlci *dlci = tty->driver_data;
2982 if (dlci->state == DLCI_CLOSED)
2983 return;
2968 tty_port_hangup(&dlci->port); 2984 tty_port_hangup(&dlci->port);
2969 gsm_dlci_begin_close(dlci); 2985 gsm_dlci_begin_close(dlci);
2970} 2986}
@@ -2972,9 +2988,12 @@ static void gsmtty_hangup(struct tty_struct *tty)
2972static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf, 2988static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf,
2973 int len) 2989 int len)
2974{ 2990{
2991 int sent;
2975 struct gsm_dlci *dlci = tty->driver_data; 2992 struct gsm_dlci *dlci = tty->driver_data;
2993 if (dlci->state == DLCI_CLOSED)
2994 return -EINVAL;
2976 /* Stuff the bytes into the fifo queue */ 2995 /* Stuff the bytes into the fifo queue */
2977 int sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock); 2996 sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock);
2978 /* Need to kick the channel */ 2997 /* Need to kick the channel */
2979 gsm_dlci_data_kick(dlci); 2998 gsm_dlci_data_kick(dlci);
2980 return sent; 2999 return sent;
@@ -2983,18 +3002,24 @@ static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf,
2983static int gsmtty_write_room(struct tty_struct *tty) 3002static int gsmtty_write_room(struct tty_struct *tty)
2984{ 3003{
2985 struct gsm_dlci *dlci = tty->driver_data; 3004 struct gsm_dlci *dlci = tty->driver_data;
3005 if (dlci->state == DLCI_CLOSED)
3006 return -EINVAL;
2986 return TX_SIZE - kfifo_len(dlci->fifo); 3007 return TX_SIZE - kfifo_len(dlci->fifo);
2987} 3008}
2988 3009
2989static int gsmtty_chars_in_buffer(struct tty_struct *tty) 3010static int gsmtty_chars_in_buffer(struct tty_struct *tty)
2990{ 3011{
2991 struct gsm_dlci *dlci = tty->driver_data; 3012 struct gsm_dlci *dlci = tty->driver_data;
3013 if (dlci->state == DLCI_CLOSED)
3014 return -EINVAL;
2992 return kfifo_len(dlci->fifo); 3015 return kfifo_len(dlci->fifo);
2993} 3016}
2994 3017
2995static void gsmtty_flush_buffer(struct tty_struct *tty) 3018static void gsmtty_flush_buffer(struct tty_struct *tty)
2996{ 3019{
2997 struct gsm_dlci *dlci = tty->driver_data; 3020 struct gsm_dlci *dlci = tty->driver_data;
3021 if (dlci->state == DLCI_CLOSED)
3022 return;
2998 /* Caution needed: If we implement reliable transport classes 3023 /* Caution needed: If we implement reliable transport classes
2999 then the data being transmitted can't simply be junked once 3024 then the data being transmitted can't simply be junked once
3000 it has first hit the stack. Until then we can just blow it 3025 it has first hit the stack. Until then we can just blow it
@@ -3013,6 +3038,8 @@ static void gsmtty_wait_until_sent(struct tty_struct *tty, int timeout)
3013static int gsmtty_tiocmget(struct tty_struct *tty) 3038static int gsmtty_tiocmget(struct tty_struct *tty)
3014{ 3039{
3015 struct gsm_dlci *dlci = tty->driver_data; 3040 struct gsm_dlci *dlci = tty->driver_data;
3041 if (dlci->state == DLCI_CLOSED)
3042 return -EINVAL;
3016 return dlci->modem_rx; 3043 return dlci->modem_rx;
3017} 3044}
3018 3045
@@ -3022,6 +3049,8 @@ static int gsmtty_tiocmset(struct tty_struct *tty,
3022 struct gsm_dlci *dlci = tty->driver_data; 3049 struct gsm_dlci *dlci = tty->driver_data;
3023 unsigned int modem_tx = dlci->modem_tx; 3050 unsigned int modem_tx = dlci->modem_tx;
3024 3051
3052 if (dlci->state == DLCI_CLOSED)
3053 return -EINVAL;
3025 modem_tx &= ~clear; 3054 modem_tx &= ~clear;
3026 modem_tx |= set; 3055 modem_tx |= set;
3027 3056
@@ -3040,6 +3069,8 @@ static int gsmtty_ioctl(struct tty_struct *tty,
3040 struct gsm_netconfig nc; 3069 struct gsm_netconfig nc;
3041 int index; 3070 int index;
3042 3071
3072 if (dlci->state == DLCI_CLOSED)
3073 return -EINVAL;
3043 switch (cmd) { 3074 switch (cmd) {
3044 case GSMIOC_ENABLE_NET: 3075 case GSMIOC_ENABLE_NET:
3045 if (copy_from_user(&nc, (void __user *)arg, sizeof(nc))) 3076 if (copy_from_user(&nc, (void __user *)arg, sizeof(nc)))
@@ -3066,6 +3097,9 @@ static int gsmtty_ioctl(struct tty_struct *tty,
3066 3097
3067static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) 3098static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old)
3068{ 3099{
3100 struct gsm_dlci *dlci = tty->driver_data;
3101 if (dlci->state == DLCI_CLOSED)
3102 return;
3069 /* For the moment its fixed. In actual fact the speed information 3103 /* For the moment its fixed. In actual fact the speed information
3070 for the virtual channel can be propogated in both directions by 3104 for the virtual channel can be propogated in both directions by
3071 the RPN control message. This however rapidly gets nasty as we 3105 the RPN control message. This however rapidly gets nasty as we
@@ -3077,6 +3111,8 @@ static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old)
3077static void gsmtty_throttle(struct tty_struct *tty) 3111static void gsmtty_throttle(struct tty_struct *tty)
3078{ 3112{
3079 struct gsm_dlci *dlci = tty->driver_data; 3113 struct gsm_dlci *dlci = tty->driver_data;
3114 if (dlci->state == DLCI_CLOSED)
3115 return;
3080 if (tty->termios.c_cflag & CRTSCTS) 3116 if (tty->termios.c_cflag & CRTSCTS)
3081 dlci->modem_tx &= ~TIOCM_DTR; 3117 dlci->modem_tx &= ~TIOCM_DTR;
3082 dlci->throttled = 1; 3118 dlci->throttled = 1;
@@ -3087,6 +3123,8 @@ static void gsmtty_throttle(struct tty_struct *tty)
3087static void gsmtty_unthrottle(struct tty_struct *tty) 3123static void gsmtty_unthrottle(struct tty_struct *tty)
3088{ 3124{
3089 struct gsm_dlci *dlci = tty->driver_data; 3125 struct gsm_dlci *dlci = tty->driver_data;
3126 if (dlci->state == DLCI_CLOSED)
3127 return;
3090 if (tty->termios.c_cflag & CRTSCTS) 3128 if (tty->termios.c_cflag & CRTSCTS)
3091 dlci->modem_tx |= TIOCM_DTR; 3129 dlci->modem_tx |= TIOCM_DTR;
3092 dlci->throttled = 0; 3130 dlci->throttled = 0;
@@ -3098,6 +3136,8 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state)
3098{ 3136{
3099 struct gsm_dlci *dlci = tty->driver_data; 3137 struct gsm_dlci *dlci = tty->driver_data;
3100 int encode = 0; /* Off */ 3138 int encode = 0; /* Off */
3139 if (dlci->state == DLCI_CLOSED)
3140 return -EINVAL;
3101 3141
3102 if (state == -1) /* "On indefinitely" - we can't encode this 3142 if (state == -1) /* "On indefinitely" - we can't encode this
3103 properly */ 3143 properly */