tty/n_gsm: avoid fifo overflow in gsm_dlci_data_output
authorMikhail Kshevetskiy <mikhail.kshevetskiy@gmail.com>
Fri, 23 Sep 2011 15:22:56 +0000 (19:22 +0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 26 Sep 2011 23:39:23 +0000 (16:39 -0700)
n_gsm use a simple approach: every writing to fifo correspond exactly one
reading from fifo. There are no problem in this approach until we read
less bytes then we write. As result fifo may owerflow. This leads to packet
loss and very slow responce.

For example, this happens with ping packets (about 96 byte each) and default
gsm->mtu = 64. As result we get 50 sec ping timeout and 20% packet loss.

Fix the problem by reading and sending all data from the fifo

Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/tty/n_gsm.c

index 14c26cdd8aca245c6d8bdea42206eb38261e1850..4cb0d0a3e57b774c22d6a8909cc20a7615c6ed60 100644 (file)
@@ -809,37 +809,41 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
 {
        struct gsm_msg *msg;
        u8 *dp;
-       int len, size;
+       int len, total_size, size;
        int h = dlci->adaption - 1;
 
-       len = kfifo_len(dlci->fifo);
-       if (len == 0)
-               return 0;
-
-       /* MTU/MRU count only the data bits */
-       if (len > gsm->mtu)
-               len = gsm->mtu;
-
-       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 == NULL)
-               return -ENOMEM;
-       dp = msg->data;
-       switch (dlci->adaption) {
-       case 1: /* Unstructured */
-               break;
-       case 2: /* Unstructed with modem bits. Always one byte as we never
-                  send inline break data */
-               *dp++ = gsm_encode_modem(dlci);
-               break;
+       total_size = 0;
+       while(1) {
+               len = kfifo_len(dlci->fifo);
+               if (len == 0)
+                       return total_size;
+
+               /* MTU/MRU count only the data bits */
+               if (len > gsm->mtu)
+                       len = gsm->mtu;
+
+               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 == NULL)
+                       return -ENOMEM;
+               dp = msg->data;
+               switch (dlci->adaption) {
+               case 1: /* Unstructured */
+                       break;
+               case 2: /* Unstructed with modem bits. Always one byte as we never
+                          send inline break data */
+                       *dp++ = gsm_encode_modem(dlci);
+                       break;
+               }
+               WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len);
+               __gsm_data_queue(dlci, msg);
+               total_size += size;
        }
-       WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len);
-       __gsm_data_queue(dlci, msg);
        /* Bytes of data we used up */
-       return size;
+       return total_size;
 }
 
 /**
This page took 0.026856 seconds and 5 git commands to generate.