aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/n_gsm.c
diff options
context:
space:
mode:
authorMikhail Kshevetskiy <mikhail.kshevetskiy@gmail.com>2011-09-23 11:22:56 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-09-26 19:39:23 -0400
commit268e526b935e794386d75025577b745e6bd57f13 (patch)
treeb7438beb8271cf94b311677c13743252d5a23bc9 /drivers/tty/n_gsm.c
parentf37ac5a144027cddfcf1dfab30eb7c2ba765f5ca (diff)
tty/n_gsm: avoid fifo overflow in gsm_dlci_data_output
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>
Diffstat (limited to 'drivers/tty/n_gsm.c')
-rw-r--r--drivers/tty/n_gsm.c58
1 files changed, 31 insertions, 27 deletions
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 14c26cdd8aca..4cb0d0a3e57b 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -809,37 +809,41 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
809{ 809{
810 struct gsm_msg *msg; 810 struct gsm_msg *msg;
811 u8 *dp; 811 u8 *dp;
812 int len, size; 812 int len, total_size, size;
813 int h = dlci->adaption - 1; 813 int h = dlci->adaption - 1;
814 814
815 len = kfifo_len(dlci->fifo); 815 total_size = 0;
816 if (len == 0) 816 while(1) {
817 return 0; 817 len = kfifo_len(dlci->fifo);
818 818 if (len == 0)
819 /* MTU/MRU count only the data bits */ 819 return total_size;
820 if (len > gsm->mtu) 820
821 len = gsm->mtu; 821 /* MTU/MRU count only the data bits */
822 822 if (len > gsm->mtu)
823 size = len + h; 823 len = gsm->mtu;
824 824
825 msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); 825 size = len + h;
826 /* FIXME: need a timer or something to kick this so it can't 826
827 get stuck with no work outstanding and no buffer free */ 827 msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
828 if (msg == NULL) 828 /* FIXME: need a timer or something to kick this so it can't
829 return -ENOMEM; 829 get stuck with no work outstanding and no buffer free */
830 dp = msg->data; 830 if (msg == NULL)
831 switch (dlci->adaption) { 831 return -ENOMEM;
832 case 1: /* Unstructured */ 832 dp = msg->data;
833 break; 833 switch (dlci->adaption) {
834 case 2: /* Unstructed with modem bits. Always one byte as we never 834 case 1: /* Unstructured */
835 send inline break data */ 835 break;
836 *dp++ = gsm_encode_modem(dlci); 836 case 2: /* Unstructed with modem bits. Always one byte as we never
837 break; 837 send inline break data */
838 *dp++ = gsm_encode_modem(dlci);
839 break;
840 }
841 WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len);
842 __gsm_data_queue(dlci, msg);
843 total_size += size;
838 } 844 }
839 WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len);
840 __gsm_data_queue(dlci, msg);
841 /* Bytes of data we used up */ 845 /* Bytes of data we used up */
842 return size; 846 return total_size;
843} 847}
844 848
845/** 849/**