Commit ce89294c056805019d8369b3b74bb52ef51b4708

Authored by Paul Fulghum
Committed by Linus Torvalds
1 parent 2a13373cf8

synclink_gt: fix transmit race and timeout

Fix race condition when adding transmit data to active DMA buffer ring
that can cause transmit stall.

Update transmit timeout when adding data to active DMA buffer ring.
Base transmit timeout on amount of buffered data instead of using fixed
value.

Signed-off-by: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 1 changed file with 30 additions and 42 deletions Side-by-side Diff

drivers/char/synclink_gt.c
... ... @@ -467,7 +467,6 @@
467 467 static unsigned int tbuf_bytes(struct slgt_info *info);
468 468 static void reset_tbufs(struct slgt_info *info);
469 469 static void tdma_reset(struct slgt_info *info);
470   -static void tdma_start(struct slgt_info *info);
471 470 static void tx_load(struct slgt_info *info, const char *buf, unsigned int count);
472 471  
473 472 static void get_signals(struct slgt_info *info);
... ... @@ -795,6 +794,18 @@
795 794 }
796 795 }
797 796  
  797 +static void update_tx_timer(struct slgt_info *info)
  798 +{
  799 + /*
  800 + * use worst case speed of 1200bps to calculate transmit timeout
  801 + * based on data in buffers (tbuf_bytes) and FIFO (128 bytes)
  802 + */
  803 + if (info->params.mode == MGSL_MODE_HDLC) {
  804 + int timeout = (tbuf_bytes(info) * 7) + 1000;
  805 + mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout));
  806 + }
  807 +}
  808 +
798 809 static int write(struct tty_struct *tty,
799 810 const unsigned char *buf, int count)
800 811 {
... ... @@ -838,8 +849,18 @@
838 849 spin_lock_irqsave(&info->lock,flags);
839 850 if (!info->tx_active)
840 851 tx_start(info);
841   - else
842   - tdma_start(info);
  852 + else if (!(rd_reg32(info, TDCSR) & BIT0)) {
  853 + /* transmit still active but transmit DMA stopped */
  854 + unsigned int i = info->tbuf_current;
  855 + if (!i)
  856 + i = info->tbuf_count;
  857 + i--;
  858 + /* if DMA buf unsent must try later after tx idle */
  859 + if (desc_count(info->tbufs[i]))
  860 + ret = 0;
  861 + }
  862 + if (ret > 0)
  863 + update_tx_timer(info);
843 864 spin_unlock_irqrestore(&info->lock,flags);
844 865 }
845 866  
846 867  
... ... @@ -1502,10 +1523,9 @@
1502 1523 /* save start time for transmit timeout detection */
1503 1524 dev->trans_start = jiffies;
1504 1525  
1505   - /* start hardware transmitter if necessary */
1506 1526 spin_lock_irqsave(&info->lock,flags);
1507   - if (!info->tx_active)
1508   - tx_start(info);
  1527 + tx_start(info);
  1528 + update_tx_timer(info);
1509 1529 spin_unlock_irqrestore(&info->lock,flags);
1510 1530  
1511 1531 return 0;
1512 1532  
1513 1533  
... ... @@ -3946,50 +3966,19 @@
3946 3966 slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE);
3947 3967 /* clear tx idle and underrun status bits */
3948 3968 wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
3949   - if (info->params.mode == MGSL_MODE_HDLC)
3950   - mod_timer(&info->tx_timer, jiffies +
3951   - msecs_to_jiffies(5000));
3952 3969 } else {
3953 3970 slgt_irq_off(info, IRQ_TXDATA);
3954 3971 slgt_irq_on(info, IRQ_TXIDLE);
3955 3972 /* clear tx idle status bit */
3956 3973 wr_reg16(info, SSR, IRQ_TXIDLE);
3957 3974 }
3958   - tdma_start(info);
  3975 + /* set 1st descriptor address and start DMA */
  3976 + wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
  3977 + wr_reg32(info, TDCSR, BIT2 + BIT0);
3959 3978 info->tx_active = true;
3960 3979 }
3961 3980 }
3962 3981  
3963   -/*
3964   - * start transmit DMA if inactive and there are unsent buffers
3965   - */
3966   -static void tdma_start(struct slgt_info *info)
3967   -{
3968   - unsigned int i;
3969   -
3970   - if (rd_reg32(info, TDCSR) & BIT0)
3971   - return;
3972   -
3973   - /* transmit DMA inactive, check for unsent buffers */
3974   - i = info->tbuf_start;
3975   - while (!desc_count(info->tbufs[i])) {
3976   - if (++i == info->tbuf_count)
3977   - i = 0;
3978   - if (i == info->tbuf_current)
3979   - return;
3980   - }
3981   - info->tbuf_start = i;
3982   -
3983   - /* there are unsent buffers, start transmit DMA */
3984   -
3985   - /* reset needed if previous error condition */
3986   - tdma_reset(info);
3987   -
3988   - /* set 1st descriptor address */
3989   - wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
3990   - wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
3991   -}
3992   -
3993 3982 static void tx_stop(struct slgt_info *info)
3994 3983 {
3995 3984 unsigned short val;
... ... @@ -5004,8 +4993,7 @@
5004 4993 info->icount.txtimeout++;
5005 4994 }
5006 4995 spin_lock_irqsave(&info->lock,flags);
5007   - info->tx_active = false;
5008   - info->tx_count = 0;
  4996 + tx_stop(info);
5009 4997 spin_unlock_irqrestore(&info->lock,flags);
5010 4998  
5011 4999 #if SYNCLINK_GENERIC_HDLC