aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/n_gsm.c
diff options
context:
space:
mode:
authorChao Bi <chao.bi@intel.com>2013-11-25 23:09:39 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-12-08 20:05:14 -0500
commitdfabf7ffa30585fe491341f069df926cc28670ac (patch)
tree392f88b2baf68ebb91e21f8a43e0de1583736dd9 /drivers/tty/n_gsm.c
parentf166b4981472baa9af2ae29984ea08e0eb62ca6c (diff)
n_gsm: race between ld close and gsmtty open
ttyA has ld associated to n_gsm, when ttyA is closing, it triggers to release gsmttyB's ld data dlci[B], then race would happen if gsmttyB is opening in parallel. (Note: This patch set differs from previous set in that it uses mutex instead of spin lock to avoid race, so that it avoids sleeping in automic context) Here are race cases we found recently in test: CASE #1 ==================================================================== releasing dlci[B] race with gsmtty_install(gsmttyB), then panic in gsmtty_open(gsmttyB), as below: tty_release(ttyA) tty_open(gsmttyB) | | ----- gsmtty_install(gsmttyB) | | ----- gsm_dlci_alloc(gsmttyB) => alloc dlci[B] tty_ldisc_release(ttyA) ----- | | gsm_dlci_release(dlci[B]) ----- | | gsm_dlci_free(dlci[B]) ----- | | ----- gsmtty_open(gsmttyB) gsmtty_open() { struct gsm_dlci *dlci = tty->driver_data; => here it uses dlci[B] ... } In gsmtty_open(gsmttyA), it uses dlci[B] which was release, so hit a panic. ===================================================================== CASE #2 ===================================================================== releasing dlci[0] race with gsmtty_install(gsmttyB), then panic in gsmtty_open(), as below: tty_release(ttyA) tty_open(gsmttyB) | | ----- gsmtty_install(gsmttyB) | | ----- gsm_dlci_alloc(gsmttyB) => alloc dlci[B] | | ----- gsmtty_open(gsmttyB) fail | | ----- tty_release(gsmttyB) | | ----- gsmtty_close(gsmttyB) | | ----- gsmtty_detach_dlci(dlci[B]) | | ----- dlci_put(dlci[B]) | | tty_ldisc_release(ttyA) ----- | | gsm_dlci_release(dlci[0]) ----- | | gsm_dlci_free(dlci[0]) ----- | | ----- dlci_put(dlci[0]) In gsmtty_detach_dlci(dlci[B]), it tries to use dlci[0] which was released, then hit panic. ===================================================================== IMHO, n_gsm tty operations would refer released ldisc, as long as gsm_dlci_release() has chance to release ldisc data when some gsmtty operations are ongoing.. This patch is try to avoid it by: 1) in n_gsm driver, use a global gsm mutex lock to avoid gsm_dlci_release() run in parallel with gsmtty_install(); 2) Increase dlci's ref count in gsmtty_install() instead of in gsmtty_open(), the purpose is to prevent gsm_dlci_release() releasing dlci after gsmtty_install() allocats dlci but before gsmtty_open increases dlci's ref count; 3) Decrease dlci's ref count in gsmtty_remove(), a tty framework API, this is the opposite process of step 2). Signed-off-by: Chao Bi <chao.bi@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/n_gsm.c')
-rw-r--r--drivers/tty/n_gsm.c39
1 files changed, 29 insertions, 10 deletions
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index c09db11b8831..679294b37653 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -194,6 +194,7 @@ struct gsm_control {
194struct gsm_mux { 194struct gsm_mux {
195 struct tty_struct *tty; /* The tty our ldisc is bound to */ 195 struct tty_struct *tty; /* The tty our ldisc is bound to */
196 spinlock_t lock; 196 spinlock_t lock;
197 struct mutex mutex;
197 unsigned int num; 198 unsigned int num;
198 struct kref ref; 199 struct kref ref;
199 200
@@ -2054,9 +2055,11 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
2054 dlci->state == DLCI_CLOSED); 2055 dlci->state == DLCI_CLOSED);
2055 } 2056 }
2056 /* Free up any link layer users */ 2057 /* Free up any link layer users */
2058 mutex_lock(&gsm->mutex);
2057 for (i = 0; i < NUM_DLCI; i++) 2059 for (i = 0; i < NUM_DLCI; i++)
2058 if (gsm->dlci[i]) 2060 if (gsm->dlci[i])
2059 gsm_dlci_release(gsm->dlci[i]); 2061 gsm_dlci_release(gsm->dlci[i]);
2062 mutex_unlock(&gsm->mutex);
2060 /* Now wipe the queues */ 2063 /* Now wipe the queues */
2061 list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list) 2064 list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
2062 kfree(txq); 2065 kfree(txq);
@@ -2170,6 +2173,7 @@ struct gsm_mux *gsm_alloc_mux(void)
2170 return NULL; 2173 return NULL;
2171 } 2174 }
2172 spin_lock_init(&gsm->lock); 2175 spin_lock_init(&gsm->lock);
2176 mutex_init(&gsm->mutex);
2173 kref_init(&gsm->ref); 2177 kref_init(&gsm->ref);
2174 INIT_LIST_HEAD(&gsm->tx_list); 2178 INIT_LIST_HEAD(&gsm->tx_list);
2175 2179
@@ -2910,23 +2914,33 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty)
2910 This is ok from a locking 2914 This is ok from a locking
2911 perspective as we don't have to worry about this 2915 perspective as we don't have to worry about this
2912 if DLCI0 is lost */ 2916 if DLCI0 is lost */
2913 if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) 2917 mutex_lock(&gsm->mutex);
2918 if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) {
2919 mutex_unlock(&gsm->mutex);
2914 return -EL2NSYNC; 2920 return -EL2NSYNC;
2921 }
2915 dlci = gsm->dlci[line]; 2922 dlci = gsm->dlci[line];
2916 if (dlci == NULL) { 2923 if (dlci == NULL) {
2917 alloc = true; 2924 alloc = true;
2918 dlci = gsm_dlci_alloc(gsm, line); 2925 dlci = gsm_dlci_alloc(gsm, line);
2919 } 2926 }
2920 if (dlci == NULL) 2927 if (dlci == NULL) {
2928 mutex_unlock(&gsm->mutex);
2921 return -ENOMEM; 2929 return -ENOMEM;
2930 }
2922 ret = tty_port_install(&dlci->port, driver, tty); 2931 ret = tty_port_install(&dlci->port, driver, tty);
2923 if (ret) { 2932 if (ret) {
2924 if (alloc) 2933 if (alloc)
2925 dlci_put(dlci); 2934 dlci_put(dlci);
2935 mutex_unlock(&gsm->mutex);
2926 return ret; 2936 return ret;
2927 } 2937 }
2928 2938
2939 dlci_get(dlci);
2940 dlci_get(gsm->dlci[0]);
2941 mux_get(gsm);
2929 tty->driver_data = dlci; 2942 tty->driver_data = dlci;
2943 mutex_unlock(&gsm->mutex);
2930 2944
2931 return 0; 2945 return 0;
2932} 2946}
@@ -2937,9 +2951,6 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp)
2937 struct tty_port *port = &dlci->port; 2951 struct tty_port *port = &dlci->port;
2938 2952
2939 port->count++; 2953 port->count++;
2940 dlci_get(dlci);
2941 dlci_get(dlci->gsm->dlci[0]);
2942 mux_get(dlci->gsm);
2943 tty_port_tty_set(port, tty); 2954 tty_port_tty_set(port, tty);
2944 2955
2945 dlci->modem_rx = 0; 2956 dlci->modem_rx = 0;
@@ -2966,7 +2977,7 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp)
2966 mutex_unlock(&dlci->mutex); 2977 mutex_unlock(&dlci->mutex);
2967 gsm = dlci->gsm; 2978 gsm = dlci->gsm;
2968 if (tty_port_close_start(&dlci->port, tty, filp) == 0) 2979 if (tty_port_close_start(&dlci->port, tty, filp) == 0)
2969 goto out; 2980 return;
2970 gsm_dlci_begin_close(dlci); 2981 gsm_dlci_begin_close(dlci);
2971 if (test_bit(ASYNCB_INITIALIZED, &dlci->port.flags)) { 2982 if (test_bit(ASYNCB_INITIALIZED, &dlci->port.flags)) {
2972 if (C_HUPCL(tty)) 2983 if (C_HUPCL(tty))
@@ -2974,10 +2985,7 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp)
2974 } 2985 }
2975 tty_port_close_end(&dlci->port, tty); 2986 tty_port_close_end(&dlci->port, tty);
2976 tty_port_tty_set(&dlci->port, NULL); 2987 tty_port_tty_set(&dlci->port, NULL);
2977out: 2988 return;
2978 dlci_put(dlci);
2979 dlci_put(gsm->dlci[0]);
2980 mux_put(gsm);
2981} 2989}
2982 2990
2983static void gsmtty_hangup(struct tty_struct *tty) 2991static void gsmtty_hangup(struct tty_struct *tty)
@@ -3154,6 +3162,16 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state)
3154 return gsmtty_modem_update(dlci, encode); 3162 return gsmtty_modem_update(dlci, encode);
3155} 3163}
3156 3164
3165static void gsmtty_remove(struct tty_driver *driver, struct tty_struct *tty)
3166{
3167 struct gsm_dlci *dlci = tty->driver_data;
3168 struct gsm_mux *gsm = dlci->gsm;
3169
3170 dlci_put(dlci);
3171 dlci_put(gsm->dlci[0]);
3172 mux_put(gsm);
3173 driver->ttys[tty->index] = NULL;
3174}
3157 3175
3158/* Virtual ttys for the demux */ 3176/* Virtual ttys for the demux */
3159static const struct tty_operations gsmtty_ops = { 3177static const struct tty_operations gsmtty_ops = {
@@ -3173,6 +3191,7 @@ static const struct tty_operations gsmtty_ops = {
3173 .tiocmget = gsmtty_tiocmget, 3191 .tiocmget = gsmtty_tiocmget,
3174 .tiocmset = gsmtty_tiocmset, 3192 .tiocmset = gsmtty_tiocmset,
3175 .break_ctl = gsmtty_break_ctl, 3193 .break_ctl = gsmtty_break_ctl,
3194 .remove = gsmtty_remove,
3176}; 3195};
3177 3196
3178 3197