perf-symbol-pass-is_kallsyms-to-symbols__fixup_end.patch
perf-symbol-update-symbols__fixup_end.patch
perf-symbol-remove-arch__symbols__fixup_end.patch
+tty-n_gsm-fix-missing-mux-reset-on-config-change-at-responder.patch
+tty-n_gsm-fix-restart-handling-via-cld-command.patch
+tty-n_gsm-fix-decoupled-mux-resource.patch
--- /dev/null
+From 1ec92e9742774bf42614fceea3bf6b50c9409225 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:08 -0700
+Subject: tty: n_gsm: fix decoupled mux resource
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit 1ec92e9742774bf42614fceea3bf6b50c9409225 upstream.
+
+The active mux instances are managed in the gsm_mux array and via mux_get()
+and mux_put() functions separately. This gives a very loose coupling
+between the actual instance and the gsm_mux array which manages it. It also
+results in unnecessary lockings which makes it prone to failures. And it
+creates a race condition if more than the maximum number of mux instances
+are requested while the user changes the parameters of an active instance.
+The user may loose ownership of the current mux instance in this case.
+Fix this by moving the gsm_mux array handling to the mux allocation and
+deallocation functions.
+
+Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-3-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 63 +++++++++++++++++++++++++++++++---------------------
+ 1 file changed, 38 insertions(+), 25 deletions(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -2136,18 +2136,6 @@ static void gsm_cleanup_mux(struct gsm_m
+ /* Finish outstanding timers, making sure they are done */
+ del_timer_sync(&gsm->t2_timer);
+
+- spin_lock(&gsm_mux_lock);
+- for (i = 0; i < MAX_MUX; i++) {
+- if (gsm_mux[i] == gsm) {
+- gsm_mux[i] = NULL;
+- break;
+- }
+- }
+- spin_unlock(&gsm_mux_lock);
+- /* open failed before registering => nothing to do */
+- if (i == MAX_MUX)
+- return;
+-
+ /* Free up any link layer users */
+ for (i = 0; i < NUM_DLCI; i++)
+ if (gsm->dlci[i])
+@@ -2171,7 +2159,6 @@ static void gsm_cleanup_mux(struct gsm_m
+ static int gsm_activate_mux(struct gsm_mux *gsm)
+ {
+ struct gsm_dlci *dlci;
+- int i = 0;
+
+ timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
+ init_waitqueue_head(&gsm->event);
+@@ -2183,18 +2170,6 @@ static int gsm_activate_mux(struct gsm_m
+ else
+ gsm->receive = gsm1_receive;
+
+- spin_lock(&gsm_mux_lock);
+- for (i = 0; i < MAX_MUX; i++) {
+- if (gsm_mux[i] == NULL) {
+- gsm->num = i;
+- gsm_mux[i] = gsm;
+- break;
+- }
+- }
+- spin_unlock(&gsm_mux_lock);
+- if (i == MAX_MUX)
+- return -EBUSY;
+-
+ dlci = gsm_dlci_alloc(gsm, 0);
+ if (dlci == NULL)
+ return -ENOMEM;
+@@ -2210,6 +2185,15 @@ static int gsm_activate_mux(struct gsm_m
+ */
+ static void gsm_free_mux(struct gsm_mux *gsm)
+ {
++ int i;
++
++ for (i = 0; i < MAX_MUX; i++) {
++ if (gsm == gsm_mux[i]) {
++ gsm_mux[i] = NULL;
++ break;
++ }
++ }
++ mutex_destroy(&gsm->mutex);
+ kfree(gsm->txframe);
+ kfree(gsm->buf);
+ kfree(gsm);
+@@ -2229,12 +2213,20 @@ static void gsm_free_muxr(struct kref *r
+
+ static inline void mux_get(struct gsm_mux *gsm)
+ {
++ unsigned long flags;
++
++ spin_lock_irqsave(&gsm_mux_lock, flags);
+ kref_get(&gsm->ref);
++ spin_unlock_irqrestore(&gsm_mux_lock, flags);
+ }
+
+ static inline void mux_put(struct gsm_mux *gsm)
+ {
++ unsigned long flags;
++
++ spin_lock_irqsave(&gsm_mux_lock, flags);
+ kref_put(&gsm->ref, gsm_free_muxr);
++ spin_unlock_irqrestore(&gsm_mux_lock, flags);
+ }
+
+ static inline unsigned int mux_num_to_base(struct gsm_mux *gsm)
+@@ -2255,6 +2247,7 @@ static inline unsigned int mux_line_to_n
+
+ static struct gsm_mux *gsm_alloc_mux(void)
+ {
++ int i;
+ struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL);
+ if (gsm == NULL)
+ return NULL;
+@@ -2284,6 +2277,26 @@ static struct gsm_mux *gsm_alloc_mux(voi
+ gsm->mtu = 64;
+ gsm->dead = true; /* Avoid early tty opens */
+
++ /* Store the instance to the mux array or abort if no space is
++ * available.
++ */
++ spin_lock(&gsm_mux_lock);
++ for (i = 0; i < MAX_MUX; i++) {
++ if (!gsm_mux[i]) {
++ gsm_mux[i] = gsm;
++ gsm->num = i;
++ break;
++ }
++ }
++ spin_unlock(&gsm_mux_lock);
++ if (i == MAX_MUX) {
++ mutex_destroy(&gsm->mutex);
++ kfree(gsm->txframe);
++ kfree(gsm->buf);
++ kfree(gsm);
++ return NULL;
++ }
++
+ return gsm;
+ }
+
--- /dev/null
+From 11451693e4081d32ef65147c6ca08cd0094ae252 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:06 -0700
+Subject: tty: n_gsm: fix missing mux reset on config change at responder
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit 11451693e4081d32ef65147c6ca08cd0094ae252 upstream.
+
+Currently, only the initiator resets the mux protocol if the user requests
+new parameters that are incompatible to those of the current connection.
+The responder also needs to reset the multiplexer if the new parameter set
+requires this. Otherwise, we end up with an inconsistent parameter set
+between initiator and responder.
+Revert the old behavior to inform the peer upon an incompatible parameter
+set change from the user on the responder side by re-establishing the mux
+protocol in such case.
+
+Fixes: 509067bbd264 ("tty: n_gsm: Delete gsm_disconnect when config requester")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-1-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -2373,7 +2373,7 @@ static int gsm_config(struct gsm_mux *gs
+ * configuration
+ */
+
+- if (gsm->initiator && (need_close || need_restart)) {
++ if (need_close || need_restart) {
+ int ret;
+
+ ret = gsm_disconnect(gsm);
--- /dev/null
+From aa371e96f05dcb36a88298f5cb70aa7234d5e8b8 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Thu, 14 Apr 2022 02:42:07 -0700
+Subject: tty: n_gsm: fix restart handling via CLD command
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit aa371e96f05dcb36a88298f5cb70aa7234d5e8b8 upstream.
+
+n_gsm is based on the 3GPP 07.010 and its newer version is the 3GPP 27.010.
+See https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1516
+The changes from 07.010 to 27.010 are non-functional. Therefore, I refer to
+the newer 27.010 here. Chapter 5.8.2 states that both sides will revert to
+the non-multiplexed mode via a close-down message (CLD). The usual program
+flow is as following:
+- start multiplex mode by sending AT+CMUX to the mobile
+- establish the control channel (DLCI 0)
+- establish user channels (DLCI >0)
+- terminate user channels
+- send close-down message (CLD)
+- revert to AT protocol (i.e. leave multiplexed mode)
+
+The AT protocol is out of scope of the n_gsm driver. However,
+gsm_disconnect() sends CLD if gsm_config() detects that the requested
+parameters require the mux protocol to restart. The next immediate action
+is to start the mux protocol by opening DLCI 0 again. Any responder side
+which handles CLD commands correctly forces us to fail at this point
+because AT+CMUX needs to be sent to the mobile to start the mux again.
+Therefore, remove the CLD command in this phase and keep both sides in
+multiplexed mode.
+Remove the gsm_disconnect() function as it become unnecessary and merge the
+remaining parts into gsm_cleanup_mux() to handle the termination order and
+locking correctly.
+
+Fixes: 71e077915396 ("tty: n_gsm: do not send/receive in ldisc close path")
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
+Link: https://lore.kernel.org/r/20220414094225.4527-2-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c | 68 +++++++++++++++-------------------------------------
+ 1 file changed, 20 insertions(+), 48 deletions(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -2106,49 +2106,35 @@ static void gsm_error(struct gsm_mux *gs
+ gsm->io_error++;
+ }
+
+-static int gsm_disconnect(struct gsm_mux *gsm)
+-{
+- struct gsm_dlci *dlci = gsm->dlci[0];
+- struct gsm_control *gc;
+-
+- if (!dlci)
+- return 0;
+-
+- /* In theory disconnecting DLCI 0 is sufficient but for some
+- modems this is apparently not the case. */
+- gc = gsm_control_send(gsm, CMD_CLD, NULL, 0);
+- if (gc)
+- gsm_control_wait(gsm, gc);
+-
+- del_timer_sync(&gsm->t2_timer);
+- /* Now we are sure T2 has stopped */
+-
+- gsm_dlci_begin_close(dlci);
+- wait_event_interruptible(gsm->event,
+- dlci->state == DLCI_CLOSED);
+-
+- if (signal_pending(current))
+- return -EINTR;
+-
+- return 0;
+-}
+-
+ /**
+ * gsm_cleanup_mux - generic GSM protocol cleanup
+ * @gsm: our mux
++ * @disc: disconnect link?
+ *
+ * Clean up the bits of the mux which are the same for all framing
+ * protocols. Remove the mux from the mux table, stop all the timers
+ * and then shut down each device hanging up the channels as we go.
+ */
+
+-static void gsm_cleanup_mux(struct gsm_mux *gsm)
++static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
+ {
+ int i;
+ struct gsm_dlci *dlci = gsm->dlci[0];
+ struct gsm_msg *txq, *ntxq;
+
+ gsm->dead = true;
++ mutex_lock(&gsm->mutex);
++
++ if (dlci) {
++ if (disc && dlci->state != DLCI_CLOSED) {
++ gsm_dlci_begin_close(dlci);
++ wait_event(gsm->event, dlci->state == DLCI_CLOSED);
++ }
++ dlci->dead = true;
++ }
++
++ /* Finish outstanding timers, making sure they are done */
++ del_timer_sync(&gsm->t2_timer);
+
+ spin_lock(&gsm_mux_lock);
+ for (i = 0; i < MAX_MUX; i++) {
+@@ -2162,13 +2148,7 @@ static void gsm_cleanup_mux(struct gsm_m
+ if (i == MAX_MUX)
+ return;
+
+- del_timer_sync(&gsm->t2_timer);
+- /* Now we are sure T2 has stopped */
+- if (dlci)
+- dlci->dead = true;
+-
+ /* Free up any link layer users */
+- mutex_lock(&gsm->mutex);
+ for (i = 0; i < NUM_DLCI; i++)
+ if (gsm->dlci[i])
+ gsm_dlci_release(gsm->dlci[i]);
+@@ -2370,19 +2350,11 @@ static int gsm_config(struct gsm_mux *gs
+
+ /*
+ * Close down what is needed, restart and initiate the new
+- * configuration
++ * configuration. On the first time there is no DLCI[0]
++ * and closing or cleaning up is not necessary.
+ */
+-
+- if (need_close || need_restart) {
+- int ret;
+-
+- ret = gsm_disconnect(gsm);
+-
+- if (ret)
+- return ret;
+- }
+- if (need_restart)
+- gsm_cleanup_mux(gsm);
++ if (need_close || need_restart)
++ gsm_cleanup_mux(gsm, true);
+
+ gsm->initiator = c->initiator;
+ gsm->mru = c->mru;
+@@ -2494,7 +2466,7 @@ static void gsmld_detach_gsm(struct tty_
+ for (i = 1; i < NUM_DLCI; i++)
+ tty_unregister_device(gsm_tty_driver, base + i);
+ }
+- gsm_cleanup_mux(gsm);
++ gsm_cleanup_mux(gsm, false);
+ tty_kref_put(gsm->tty);
+ gsm->tty = NULL;
+ }
+@@ -2597,7 +2569,7 @@ static int gsmld_open(struct tty_struct
+
+ ret = gsmld_attach_gsm(tty, gsm);
+ if (ret != 0) {
+- gsm_cleanup_mux(gsm);
++ gsm_cleanup_mux(gsm, false);
+ mux_put(gsm);
+ }
+ return ret;