tty: n_gsm: fix missing timer to handle stalled links
authorDaniel Starke <daniel.starke@siemens.com>
Fri, 1 Jul 2022 06:16:47 +0000 (08:16 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 1 Jul 2022 08:07:26 +0000 (10:07 +0200)
The current implementation does not handle the situation that no data is in
the internal queue and needs to be sent out while the user tty fifo is
full.
Add a timer that moves more data from user tty down to the internal queue
which is then serialized on the ldisc. This timer is triggered if no data
was moved from a user tty to the internal queue within 10 * T1.

Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
Link: https://lore.kernel.org/r/20220701061652.39604-4-daniel.starke@siemens.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/n_gsm.c

index d056b15..a012258 100644 (file)
@@ -244,6 +244,7 @@ struct gsm_mux {
        struct list_head tx_list;       /* Pending data packets */
 
        /* Control messages */
+       struct timer_list kick_timer;   /* Kick TX queuing on timeout */
        struct timer_list t2_timer;     /* Retransmit timer for commands */
        int cretries;                   /* Command retry counter */
        struct gsm_control *pending_cmd;/* Our current pending command */
@@ -850,6 +851,7 @@ static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
        list_add_tail(&msg->list, &gsm->tx_list);
        gsm->tx_bytes += msg->len;
        gsm_data_kick(gsm, dlci);
+       mod_timer(&gsm->kick_timer, jiffies + 10 * gsm->t1 * HZ / 100);
 }
 
 /**
@@ -902,9 +904,6 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
        size = len + h;
 
        msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
-       /* FIXME: need a timer or something to kick this so it can't
-        * get stuck with no work outstanding and no buffer free
-        */
        if (!msg)
                return -ENOMEM;
        dp = msg->data;
@@ -981,9 +980,6 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
 
        size = len + overhead;
        msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
-
-       /* FIXME: need a timer or something to kick this so it can't
-          get stuck with no work outstanding and no buffer free */
        if (msg == NULL) {
                skb_queue_tail(&dlci->skb_list, dlci->skb);
                dlci->skb = NULL;
@@ -1079,9 +1075,9 @@ static int gsm_dlci_modem_output(struct gsm_mux *gsm, struct gsm_dlci *dlci,
  *     renegotiate DLCI priorities with optional stuff. Needs optimising.
  */
 
-static void gsm_dlci_data_sweep(struct gsm_mux *gsm)
+static int gsm_dlci_data_sweep(struct gsm_mux *gsm)
 {
-       int len;
+       int len, ret = 0;
        /* Priority ordering: We should do priority with RR of the groups */
        int i = 1;
 
@@ -1104,7 +1100,11 @@ static void gsm_dlci_data_sweep(struct gsm_mux *gsm)
                /* DLCI empty - try the next */
                if (len == 0)
                        i++;
+               else
+                       ret++;
        }
+
+       return ret;
 }
 
 /**
@@ -1823,6 +1823,30 @@ static void gsm_dlci_command(struct gsm_dlci *dlci, const u8 *data, int len)
        }
 }
 
+/**
+ *     gsm_kick_timer  -       transmit if possible
+ *     @t: timer contained in our gsm object
+ *
+ *     Transmit data from DLCIs if the queue is empty. We can't rely on
+ *     a tty wakeup except when we filled the pipe so we need to fire off
+ *     new data ourselves in other cases.
+ */
+static void gsm_kick_timer(struct timer_list *t)
+{
+       struct gsm_mux *gsm = from_timer(gsm, t, kick_timer);
+       unsigned long flags;
+       int sent = 0;
+
+       spin_lock_irqsave(&gsm->tx_lock, flags);
+       /* If we have nothing running then we need to fire up */
+       if (gsm->tx_bytes < TX_THRESH_LO)
+               sent = gsm_dlci_data_sweep(gsm);
+       spin_unlock_irqrestore(&gsm->tx_lock, flags);
+
+       if (sent && debug & 4)
+               pr_info("%s TX queue stalled\n", __func__);
+}
+
 /*
  *     Allocate/Free DLCI channels
  */
@@ -2274,6 +2298,7 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
        }
 
        /* Finish outstanding timers, making sure they are done */
+       del_timer_sync(&gsm->kick_timer);
        del_timer_sync(&gsm->t2_timer);
 
        /* Free up any link layer users and finally the control channel */
@@ -2306,6 +2331,7 @@ static int gsm_activate_mux(struct gsm_mux *gsm)
        struct gsm_dlci *dlci;
        int ret;
 
+       timer_setup(&gsm->kick_timer, gsm_kick_timer, 0);
        timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
        init_waitqueue_head(&gsm->event);
        spin_lock_init(&gsm->control_lock);
@@ -2710,6 +2736,7 @@ static int gsmld_open(struct tty_struct *tty)
 
        gsmld_attach_gsm(tty, gsm);
 
+       timer_setup(&gsm->kick_timer, gsm_kick_timer, 0);
        timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
 
        return 0;