aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChao Bi <chao.bi@intel.com>2013-10-17 03:08:27 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-11-25 11:52:53 -0500
commitc284ee2cf12b55fa8496b2d098bf0938688f1c1c (patch)
tree5f553255c2b1dd33f6dcab7434ea2d7673bbb71b
parentf3014127ada32f51ce0baf0bee4c6324a601ef59 (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. 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 not completed.. This patch is try to avoid it by: 1) in n_gsm driver, use a global gsm spin 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(), which is a tty framework api, and 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>
-rw-r--r--drivers/tty/n_gsm.c37
1 files changed, 27 insertions, 10 deletions
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index c0f76da55304..069bfd654485 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -2054,9 +2054,11 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
2054 dlci->state == DLCI_CLOSED); 2054 dlci->state == DLCI_CLOSED);
2055 } 2055 }
2056 /* Free up any link layer users */ 2056 /* Free up any link layer users */
2057 spin_lock(&gsm->lock);
2057 for (i = 0; i < NUM_DLCI; i++) 2058 for (i = 0; i < NUM_DLCI; i++)
2058 if (gsm->dlci[i]) 2059 if (gsm->dlci[i])
2059 gsm_dlci_release(gsm->dlci[i]); 2060 gsm_dlci_release(gsm->dlci[i]);
2061 spin_unlock(&gsm->lock);
2060 /* Now wipe the queues */ 2062 /* Now wipe the queues */
2061 list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list) 2063 list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
2062 kfree(txq); 2064 kfree(txq);
@@ -2909,23 +2911,33 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty)
2909 This is ok from a locking 2911 This is ok from a locking
2910 perspective as we don't have to worry about this 2912 perspective as we don't have to worry about this
2911 if DLCI0 is lost */ 2913 if DLCI0 is lost */
2912 if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) 2914 spin_lock(&gsm->lock);
2915 if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) {
2916 spin_unlock(&gsm->lock);
2913 return -EL2NSYNC; 2917 return -EL2NSYNC;
2918 }
2914 dlci = gsm->dlci[line]; 2919 dlci = gsm->dlci[line];
2915 if (dlci == NULL) { 2920 if (dlci == NULL) {
2916 alloc = true; 2921 alloc = true;
2917 dlci = gsm_dlci_alloc(gsm, line); 2922 dlci = gsm_dlci_alloc(gsm, line);
2918 } 2923 }
2919 if (dlci == NULL) 2924 if (dlci == NULL) {
2925 spin_unlock(&gsm->lock);
2920 return -ENOMEM; 2926 return -ENOMEM;
2927 }
2921 ret = tty_port_install(&dlci->port, driver, tty); 2928 ret = tty_port_install(&dlci->port, driver, tty);
2922 if (ret) { 2929 if (ret) {
2923 if (alloc) 2930 if (alloc)
2924 dlci_put(dlci); 2931 dlci_put(dlci);
2932 spin_unlock(&gsm->lock);
2925 return ret; 2933 return ret;
2926 } 2934 }
2927 2935
2936 dlci_get(dlci);
2937 dlci_get(gsm->dlci[0]);
2938 mux_get(gsm);
2928 tty->driver_data = dlci; 2939 tty->driver_data = dlci;
2940 spin_unlock(&gsm->lock);
2929 2941
2930 return 0; 2942 return 0;
2931} 2943}
@@ -2936,9 +2948,6 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp)
2936 struct tty_port *port = &dlci->port; 2948 struct tty_port *port = &dlci->port;
2937 2949
2938 port->count++; 2950 port->count++;
2939 dlci_get(dlci);
2940 dlci_get(dlci->gsm->dlci[0]);
2941 mux_get(dlci->gsm);
2942 tty_port_tty_set(port, tty); 2951 tty_port_tty_set(port, tty);
2943 2952
2944 dlci->modem_rx = 0; 2953 dlci->modem_rx = 0;
@@ -2965,7 +2974,7 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp)
2965 mutex_unlock(&dlci->mutex); 2974 mutex_unlock(&dlci->mutex);
2966 gsm = dlci->gsm; 2975 gsm = dlci->gsm;
2967 if (tty_port_close_start(&dlci->port, tty, filp) == 0) 2976 if (tty_port_close_start(&dlci->port, tty, filp) == 0)
2968 goto out; 2977 return;
2969 gsm_dlci_begin_close(dlci); 2978 gsm_dlci_begin_close(dlci);
2970 if (test_bit(ASYNCB_INITIALIZED, &dlci->port.flags)) { 2979 if (test_bit(ASYNCB_INITIALIZED, &dlci->port.flags)) {
2971 if (C_HUPCL(tty)) 2980 if (C_HUPCL(tty))
@@ -2973,10 +2982,7 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp)
2973 } 2982 }
2974 tty_port_close_end(&dlci->port, tty); 2983 tty_port_close_end(&dlci->port, tty);
2975 tty_port_tty_set(&dlci->port, NULL); 2984 tty_port_tty_set(&dlci->port, NULL);
2976out: 2985 return;
2977 dlci_put(dlci);
2978 dlci_put(gsm->dlci[0]);
2979 mux_put(gsm);
2980} 2986}
2981 2987
2982static void gsmtty_hangup(struct tty_struct *tty) 2988static void gsmtty_hangup(struct tty_struct *tty)
@@ -3153,6 +3159,16 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state)
3153 return gsmtty_modem_update(dlci, encode); 3159 return gsmtty_modem_update(dlci, encode);
3154} 3160}
3155 3161
3162static void gsmtty_remove(struct tty_driver *driver, struct tty_struct *tty)
3163{
3164 struct gsm_dlci *dlci = tty->driver_data;
3165 struct gsm_mux *gsm = dlci->gsm;
3166
3167 dlci_put(dlci);
3168 dlci_put(gsm->dlci[0]);
3169 mux_put(gsm);
3170 driver->ttys[tty->index] = NULL;
3171}
3156 3172
3157/* Virtual ttys for the demux */ 3173/* Virtual ttys for the demux */
3158static const struct tty_operations gsmtty_ops = { 3174static const struct tty_operations gsmtty_ops = {
@@ -3172,6 +3188,7 @@ static const struct tty_operations gsmtty_ops = {
3172 .tiocmget = gsmtty_tiocmget, 3188 .tiocmget = gsmtty_tiocmget,
3173 .tiocmset = gsmtty_tiocmset, 3189 .tiocmset = gsmtty_tiocmset,
3174 .break_ctl = gsmtty_break_ctl, 3190 .break_ctl = gsmtty_break_ctl,
3191 .remove = gsmtty_remove,
3175}; 3192};
3176 3193
3177 3194