]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 2 May 2022 23:13:54 +0000 (01:13 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 2 May 2022 23:13:54 +0000 (01:13 +0200)
added patches:
tty-n_gsm-fix-decoupled-mux-resource.patch
tty-n_gsm-fix-restart-handling-via-cld-command.patch

queue-5.10/series
queue-5.10/tty-n_gsm-fix-decoupled-mux-resource.patch [new file with mode: 0644]
queue-5.10/tty-n_gsm-fix-restart-handling-via-cld-command.patch [new file with mode: 0644]

index cbdcb4d88ced79ef42a124f3186e45cfb5fad1d4..155c0c1bde2af2e55a2477ebe4e3c2c02dda64e3 100644 (file)
@@ -112,3 +112,5 @@ thermal-int340x-fix-attr.show-callback-prototype.patch
 x86-cpu-load-microcode-during-restore_processor_state.patch
 perf-symbol-pass-is_kallsyms-to-symbols__fixup_end.patch
 perf-symbol-update-symbols__fixup_end.patch
+tty-n_gsm-fix-restart-handling-via-cld-command.patch
+tty-n_gsm-fix-decoupled-mux-resource.patch
diff --git a/queue-5.10/tty-n_gsm-fix-decoupled-mux-resource.patch b/queue-5.10/tty-n_gsm-fix-decoupled-mux-resource.patch
new file mode 100644 (file)
index 0000000..dab79b6
--- /dev/null
@@ -0,0 +1,148 @@
+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
+@@ -2070,18 +2070,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])
+@@ -2105,7 +2093,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);
+@@ -2117,18 +2104,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;
+@@ -2144,6 +2119,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);
+@@ -2163,12 +2147,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)
+@@ -2189,6 +2181,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;
+@@ -2218,6 +2211,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;
+ }
diff --git a/queue-5.10/tty-n_gsm-fix-restart-handling-via-cld-command.patch b/queue-5.10/tty-n_gsm-fix-restart-handling-via-cld-command.patch
new file mode 100644 (file)
index 0000000..f2c40fb
--- /dev/null
@@ -0,0 +1,165 @@
+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
+@@ -2040,49 +2040,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++) {
+@@ -2096,13 +2082,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]);
+@@ -2304,19 +2284,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;
+@@ -2425,7 +2397,7 @@ static void gsmld_detach_gsm(struct tty_
+       WARN_ON(tty != gsm->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;
+ }
+@@ -2531,7 +2503,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;