From 7ac2ed0ceeafa130f85aa947b271b571c68b9e75 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 25 Aug 2011 13:22:24 +0000 Subject: caif: Remove OOM messages, use kzalloc Remove per site OOM messages because they duplicate the generic mm subsystem OOM message. Use kzalloc instead of kmalloc/memset when next to the OOM message removals. Reduces object size (allyesconfig ~2%) $ size -t drivers/net/caif/built-in.o.old net/caif/built-in.o.old text data bss dec hex filename 32297 700 8224 41221 a105 drivers/net/caif/built-in.o.old 72159 1317 20552 94028 16f4c net/caif/built-in.o.old 104456 2017 28776 135249 21051 (TOTALS) $ size -t drivers/net/caif/built-in.o.new net/caif/built-in.o.new text data bss dec hex filename 31975 700 8184 40859 9f9b drivers/net/caif/built-in.o.new 70748 1317 20152 92217 16839 net/caif/built-in.o.new 102723 2017 28336 133076 207d4 (TOTALS) Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers/net/caif/caif_hsi.c') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index b41c2fced0a7..2fcabba56087 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -937,11 +937,8 @@ int cfhsi_probe(struct platform_device *pdev) int res; ndev = alloc_netdev(sizeof(struct cfhsi), "cfhsi%d", cfhsi_setup); - if (!ndev) { - dev_err(&pdev->dev, "%s: alloc_netdev failed.\n", - __func__); + if (!ndev) return -ENODEV; - } cfhsi = netdev_priv(ndev); cfhsi->ndev = ndev; @@ -969,8 +966,6 @@ int cfhsi_probe(struct platform_device *pdev) */ cfhsi->tx_buf = kzalloc(CFHSI_BUF_SZ_TX, GFP_KERNEL); if (!cfhsi->tx_buf) { - dev_err(&ndev->dev, "%s: Failed to allocate TX buffer.\n", - __func__); res = -ENODEV; goto err_alloc_tx; } @@ -981,8 +976,6 @@ int cfhsi_probe(struct platform_device *pdev) */ cfhsi->rx_buf = kzalloc(CFHSI_BUF_SZ_RX, GFP_KERNEL); if (!cfhsi->rx_buf) { - dev_err(&ndev->dev, "%s: Failed to allocate RX buffer.\n", - __func__); res = -ENODEV; goto err_alloc_rx; } -- cgit v1.2.3 From 94230febe47f82331f9493c4fd61085e2a6bf756 Mon Sep 17 00:00:00 2001 From: "sjur.brandeland@stericsson.com" Date: Thu, 13 Oct 2011 11:29:22 +0000 Subject: caif-hsi: HSI Fix uninitialized data in HSI header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CAIF HSI header may be uninitialized and cause last message to be repeated if transmit size is ~86 bytes long. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net/caif/caif_hsi.c') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 2fcabba56087..193781389f73 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -178,6 +178,9 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi) if (!skb) return 0; + /* Clear offset. */ + desc->offset = 0; + /* Check if we can embed a CAIF frame. */ if (skb->len < CFHSI_MAX_EMB_FRM_SZ) { struct caif_payload_info *info; @@ -206,9 +209,7 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi) consume_skb(skb); skb = NULL; } - } else - /* Clear offset. */ - desc->offset = 0; + } /* Create payload CAIF frames. */ pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ; @@ -990,6 +991,8 @@ int cfhsi_probe(struct platform_device *pdev) /* Set up the driver. */ cfhsi->drv.tx_done_cb = cfhsi_tx_done_cb; cfhsi->drv.rx_done_cb = cfhsi_rx_done_cb; + cfhsi->drv.wake_up_cb = cfhsi_wake_up_cb; + cfhsi->drv.wake_down_cb = cfhsi_wake_down_cb; /* Initialize the work queues. */ INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up); @@ -1045,9 +1048,6 @@ int cfhsi_probe(struct platform_device *pdev) goto err_net_reg; } - cfhsi->drv.wake_up_cb = cfhsi_wake_up_cb; - cfhsi->drv.wake_down_cb = cfhsi_wake_down_cb; - /* Register network device. */ res = register_netdev(ndev); if (res) { -- cgit v1.2.3 From fe47f1250805438fa06580c9ce6d37bc4bc595d2 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 13 Oct 2011 11:29:23 +0000 Subject: caif-hsi: Fixing a race condition in the caif_hsi code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cfhsi->tx_state was not protected by a spin lock. TX soft-irq could interrupt cfhsi_tx_done_work work leading to inconsistent state of the driver. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'drivers/net/caif/caif_hsi.c') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 193781389f73..36da27b50114 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -304,14 +304,22 @@ static void cfhsi_tx_done_work(struct work_struct *work) spin_unlock_bh(&cfhsi->lock); /* Create HSI frame. */ - len = cfhsi_tx_frm(desc, cfhsi); - if (!len) { - cfhsi->tx_state = CFHSI_TX_STATE_IDLE; - /* Start inactivity timer. */ - mod_timer(&cfhsi->timer, + do { + len = cfhsi_tx_frm(desc, cfhsi); + if (!len) { + spin_lock_bh(&cfhsi->lock); + if (unlikely(skb_peek(&cfhsi->qhead))) { + spin_unlock_bh(&cfhsi->lock); + continue; + } + cfhsi->tx_state = CFHSI_TX_STATE_IDLE; + /* Start inactivity timer. */ + mod_timer(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT); - break; - } + spin_unlock_bh(&cfhsi->lock); + goto done; + } + } while (!len); /* Set up new transfer. */ res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); @@ -320,6 +328,9 @@ static void cfhsi_tx_done_work(struct work_struct *work) __func__, res); } } while (res < 0); + +done: + return; } static void cfhsi_tx_done_cb(struct cfhsi_drv *drv) -- cgit v1.2.3 From 73033c987a8bd0b080509063bb7c130b8941ad73 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 13 Oct 2011 11:29:24 +0000 Subject: caif-hsi: Fix for wakeup condition problem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under stressed conditions a race could happen when del_timer_sync() was called from softirq context at the same time when mod_timer_pending() for the same timer was called from the workqueue. This leaded to a state mismatch in the CAIF HSI driver and following unexpected link wakeup procedure. The fix puts del_timer_sync() and mod_timer_pending() calls under a spin lock to protect against the race condition. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/net/caif/caif_hsi.c') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 36da27b50114..82c4d6ca2d3f 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -551,7 +551,9 @@ static void cfhsi_rx_done_work(struct work_struct *work) return; /* Update inactivity timer if pending. */ + spin_lock_bh(&cfhsi->lock); mod_timer_pending(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT); + spin_unlock_bh(&cfhsi->lock); if (cfhsi->rx_state == CFHSI_RX_STATE_DESC) { desc_pld_len = cfhsi_rx_desc(desc, cfhsi); @@ -866,10 +868,10 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) start_xfer = 1; } - spin_unlock_bh(&cfhsi->lock); - - if (!start_xfer) + if (!start_xfer) { + spin_unlock_bh(&cfhsi->lock); return 0; + } /* Delete inactivity timer if started. */ #ifdef CONFIG_SMP @@ -878,6 +880,8 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) timer_active = del_timer(&cfhsi->timer); #endif /* CONFIG_SMP */ + spin_unlock_bh(&cfhsi->lock); + if (timer_active) { struct cfhsi_desc *desc = (struct cfhsi_desc *)cfhsi->tx_buf; int len; -- cgit v1.2.3 From 687b13e98addc99644002703944ec89e94287cb6 Mon Sep 17 00:00:00 2001 From: Daniel Martensson Date: Thu, 13 Oct 2011 11:29:25 +0000 Subject: caif-hsi: Making read and writes asynchronous. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some platforms do not allow to put HSI block into low-power mode when FIFO is not empty. The patch flushes (by reading) FIFO at wake down sequence. Asynchronous read and write is implemented for that. As a side effect this will also greatly improve performance. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 263 +++++++++++++++++++++----------------------- include/net/caif/caif_hsi.h | 29 +++-- 2 files changed, 147 insertions(+), 145 deletions(-) (limited to 'drivers/net/caif/caif_hsi.c') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 82c4d6ca2d3f..478b025c9f8b 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -145,7 +145,7 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi) } ret = 5 * HZ; - wait_event_interruptible_timeout(cfhsi->flush_fifo_wait, + ret = wait_event_interruptible_timeout(cfhsi->flush_fifo_wait, !test_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits), ret); if (ret < 0) { @@ -272,16 +272,13 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi) return CFHSI_DESC_SZ + pld_len; } -static void cfhsi_tx_done_work(struct work_struct *work) +static void cfhsi_tx_done(struct cfhsi *cfhsi) { - struct cfhsi *cfhsi = NULL; struct cfhsi_desc *desc = NULL; int len = 0; int res; - cfhsi = container_of(work, struct cfhsi, tx_done_work); - dev_dbg(&cfhsi->ndev->dev, "%s.\n", - __func__); + dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__); if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) return; @@ -343,11 +340,11 @@ static void cfhsi_tx_done_cb(struct cfhsi_drv *drv) if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) return; - - queue_work(cfhsi->wq, &cfhsi->tx_done_work); + cfhsi_tx_done(cfhsi); } -static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) +static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, + bool *dump) { int xfer_sz = 0; int nfrms = 0; @@ -358,6 +355,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", __func__); + *dump = true; return 0; } @@ -365,7 +363,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) if (desc->offset) { struct sk_buff *skb; u8 *dst = NULL; - int len = 0, retries = 0; + int len = 0; pfrm = ((u8 *)desc) + desc->offset; /* Remove offset padding. */ @@ -378,24 +376,11 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) /* Allocate SKB (OK even in IRQ context). */ - skb = alloc_skb(len + 1, GFP_KERNEL); - while (!skb) { - retries++; - schedule_timeout(1); - skb = alloc_skb(len + 1, GFP_KERNEL); - if (skb) { - printk(KERN_WARNING "%s: slept for %u " - "before getting memory\n", - __func__, retries); - break; - } - if (retries > HZ) { - printk(KERN_ERR "%s: slept for 1HZ and " - "did not get memory\n", - __func__); - cfhsi->ndev->stats.rx_dropped++; - goto drop_frame; - } + skb = alloc_skb(len + 1, GFP_ATOMIC); + if (!skb) { + dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n", + __func__); + return -ENOMEM; } caif_assert(skb != NULL); @@ -421,7 +406,6 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) cfhsi->ndev->stats.rx_bytes += len; } -drop_frame: /* Calculate transfer length. */ plen = desc->cffrm_len; while (nfrms < CFHSI_MAX_PKTS && *plen) { @@ -439,12 +423,13 @@ drop_frame: "%s: Invalid payload len: %d, ignored.\n", __func__, xfer_sz); xfer_sz = 0; + *dump = true; } - return xfer_sz; } -static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) +static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, + bool *dump) { int rx_sz = 0; int nfrms = 0; @@ -456,21 +441,33 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) (desc->offset > CFHSI_MAX_EMB_FRM_SZ))) { dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", __func__); + *dump = true; return -EINVAL; } /* Set frame pointer to start of payload. */ pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ; plen = desc->cffrm_len; + + /* Skip already processed frames. */ + while (nfrms < cfhsi->rx_state.nfrms) { + pfrm += *plen; + rx_sz += *plen; + plen++; + nfrms++; + } + + /* Parse payload. */ while (nfrms < CFHSI_MAX_PKTS && *plen) { struct sk_buff *skb; u8 *dst = NULL; u8 *pcffrm = NULL; - int len = 0, retries = 0; + int len = 0; if (WARN_ON(desc->cffrm_len[nfrms] > CFHSI_MAX_PAYLOAD_SZ)) { dev_err(&cfhsi->ndev->dev, "%s: Invalid payload.\n", __func__); + *dump = true; return -EINVAL; } @@ -483,24 +480,12 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) len += 2; /* Add FCS fields. */ /* Allocate SKB (OK even in IRQ context). */ - skb = alloc_skb(len + 1, GFP_KERNEL); - while (!skb) { - retries++; - schedule_timeout(1); - skb = alloc_skb(len + 1, GFP_KERNEL); - if (skb) { - printk(KERN_WARNING "%s: slept for %u " - "before getting memory\n", - __func__, retries); - break; - } - if (retries > HZ) { - printk(KERN_ERR "%s: slept for 1HZ " - "and did not get memory\n", - __func__); - cfhsi->ndev->stats.rx_dropped++; - goto drop_frame; - } + skb = alloc_skb(len + 1, GFP_ATOMIC); + if (!skb) { + dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n", + __func__); + cfhsi->rx_state.nfrms = nfrms; + return -ENOMEM; } caif_assert(skb != NULL); @@ -524,7 +509,6 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) cfhsi->ndev->stats.rx_packets++; cfhsi->ndev->stats.rx_bytes += len; -drop_frame: pfrm += *plen; rx_sz += *plen; plen++; @@ -534,18 +518,16 @@ drop_frame: return rx_sz; } -static void cfhsi_rx_done_work(struct work_struct *work) +static void cfhsi_rx_done(struct cfhsi *cfhsi) { int res; int desc_pld_len = 0; - struct cfhsi *cfhsi = NULL; struct cfhsi_desc *desc = NULL; + bool dump = false; - cfhsi = container_of(work, struct cfhsi, rx_done_work); desc = (struct cfhsi_desc *)cfhsi->rx_buf; - dev_dbg(&cfhsi->ndev->dev, "%s: Kick timer if pending.\n", - __func__); + dev_dbg(&cfhsi->ndev->dev, "%s\n", __func__); if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) return; @@ -555,21 +537,33 @@ static void cfhsi_rx_done_work(struct work_struct *work) mod_timer_pending(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT); spin_unlock_bh(&cfhsi->lock); - if (cfhsi->rx_state == CFHSI_RX_STATE_DESC) { - desc_pld_len = cfhsi_rx_desc(desc, cfhsi); + if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { + desc_pld_len = cfhsi_rx_desc(desc, cfhsi, &dump); + if (desc_pld_len == -ENOMEM) + goto restart; } else { int pld_len; - pld_len = cfhsi_rx_pld(desc, cfhsi); + if (!cfhsi->rx_state.piggy_desc) { + pld_len = cfhsi_rx_pld(desc, cfhsi, &dump); + if (pld_len == -ENOMEM) + goto restart; + cfhsi->rx_state.pld_len = pld_len; + } else { + pld_len = cfhsi->rx_state.pld_len; + } if ((pld_len > 0) && (desc->header & CFHSI_PIGGY_DESC)) { struct cfhsi_desc *piggy_desc; piggy_desc = (struct cfhsi_desc *) (desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ + pld_len); + cfhsi->rx_state.piggy_desc = true; /* Extract piggy-backed descriptor. */ - desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi); + desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi, &dump); + if (desc_pld_len == -ENOMEM) + goto restart; /* * Copy needed information from the piggy-backed @@ -580,16 +574,24 @@ static void cfhsi_rx_done_work(struct work_struct *work) } } + if (unlikely(dump)) { + size_t rx_offset = cfhsi->rx_ptr - cfhsi->rx_buf; + dev_err(&cfhsi->ndev->dev, "%s: RX offset: %u.\n", + __func__, (unsigned) rx_offset); + print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, + cfhsi->rx_buf, cfhsi->rx_len + rx_offset); + } + + memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state)); if (desc_pld_len) { - cfhsi->rx_state = CFHSI_RX_STATE_PAYLOAD; + cfhsi->rx_state.state = CFHSI_RX_STATE_PAYLOAD; cfhsi->rx_ptr = cfhsi->rx_buf + CFHSI_DESC_SZ; cfhsi->rx_len = desc_pld_len; } else { - cfhsi->rx_state = CFHSI_RX_STATE_DESC; + cfhsi->rx_state.state = CFHSI_RX_STATE_DESC; cfhsi->rx_ptr = cfhsi->rx_buf; cfhsi->rx_len = CFHSI_DESC_SZ; } - clear_bit(CFHSI_PENDING_RX, &cfhsi->bits); if (test_bit(CFHSI_AWAKE, &cfhsi->bits)) { /* Set up new transfer. */ @@ -604,6 +606,26 @@ static void cfhsi_rx_done_work(struct work_struct *work) cfhsi->ndev->stats.rx_dropped++; } } + return; + +restart: + if (++cfhsi->rx_state.retries > CFHSI_MAX_RX_RETRIES) { + dev_err(&cfhsi->ndev->dev, "%s: No memory available " + "in %d iterations.\n", + __func__, CFHSI_MAX_RX_RETRIES); + BUG(); + } + mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1); +} + +static void cfhsi_rx_slowpath(unsigned long arg) +{ + struct cfhsi *cfhsi = (struct cfhsi *)arg; + + dev_dbg(&cfhsi->ndev->dev, "%s.\n", + __func__); + + cfhsi_rx_done(cfhsi); } static void cfhsi_rx_done_cb(struct cfhsi_drv *drv) @@ -617,12 +639,10 @@ static void cfhsi_rx_done_cb(struct cfhsi_drv *drv) if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) return; - set_bit(CFHSI_PENDING_RX, &cfhsi->bits); - if (test_and_clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits)) wake_up_interruptible(&cfhsi->flush_fifo_wait); else - queue_work(cfhsi->wq, &cfhsi->rx_done_work); + cfhsi_rx_done(cfhsi); } static void cfhsi_wake_up(struct work_struct *work) @@ -651,9 +671,9 @@ static void cfhsi_wake_up(struct work_struct *work) __func__); /* Wait for acknowledge. */ - ret = CFHSI_WAKEUP_TOUT; - wait_event_interruptible_timeout(cfhsi->wake_up_wait, - test_bit(CFHSI_WAKE_UP_ACK, + ret = CFHSI_WAKE_TOUT; + ret = wait_event_interruptible_timeout(cfhsi->wake_up_wait, + test_and_clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits), ret); if (unlikely(ret < 0)) { /* Interrupted by signal. */ @@ -678,16 +698,11 @@ static void cfhsi_wake_up(struct work_struct *work) clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); /* Resume read operation. */ - if (!test_bit(CFHSI_PENDING_RX, &cfhsi->bits)) { - dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n", - __func__); - res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, - cfhsi->rx_len, cfhsi->dev); - if (WARN_ON(res < 0)) { - dev_err(&cfhsi->ndev->dev, "%s: RX error %d.\n", - __func__, res); - } - } + dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n", __func__); + res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len, cfhsi->dev); + + if (WARN_ON(res < 0)) + dev_err(&cfhsi->ndev->dev, "%s: RX err %d.\n", __func__, res); /* Clear power up acknowledment. */ clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); @@ -726,50 +741,29 @@ static void cfhsi_wake_up(struct work_struct *work) "%s: Failed to create HSI frame: %d.\n", __func__, len); } - } static void cfhsi_wake_down(struct work_struct *work) { long ret; struct cfhsi *cfhsi = NULL; - size_t fifo_occupancy; + size_t fifo_occupancy = 0; + int retry = CFHSI_WAKE_TOUT; cfhsi = container_of(work, struct cfhsi, wake_down_work); - dev_dbg(&cfhsi->ndev->dev, "%s.\n", - __func__); + dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__); if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) return; - /* Check if there is something in FIFO. */ - if (WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, - &fifo_occupancy))) - fifo_occupancy = 0; - - if (fifo_occupancy) { - dev_dbg(&cfhsi->ndev->dev, - "%s: %u words in RX FIFO, restart timer.\n", - __func__, (unsigned) fifo_occupancy); - spin_lock_bh(&cfhsi->lock); - mod_timer(&cfhsi->timer, - jiffies + CFHSI_INACTIVITY_TOUT); - spin_unlock_bh(&cfhsi->lock); - return; - } - - /* Cancel pending RX requests */ - cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev); - /* Deactivate wake line. */ cfhsi->dev->cfhsi_wake_down(cfhsi->dev); /* Wait for acknowledge. */ - ret = CFHSI_WAKEUP_TOUT; + ret = CFHSI_WAKE_TOUT; ret = wait_event_interruptible_timeout(cfhsi->wake_down_wait, - test_bit(CFHSI_WAKE_DOWN_ACK, - &cfhsi->bits), - ret); + test_and_clear_bit(CFHSI_WAKE_DOWN_ACK, + &cfhsi->bits), ret); if (ret < 0) { /* Interrupted by signal. */ dev_info(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n", @@ -777,28 +771,31 @@ static void cfhsi_wake_down(struct work_struct *work) return; } else if (!ret) { /* Timeout */ - dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", - __func__); + dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", __func__); } - /* Clear power down acknowledment. */ - clear_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits); + /* Check FIFO occupancy. */ + while (retry) { + WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, + &fifo_occupancy)); + + if (!fifo_occupancy) + break; + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + retry--; + } + + if (!retry) + dev_err(&cfhsi->ndev->dev, "%s: FIFO Timeout.\n", __func__); + + /* Clear AWAKE condition. */ clear_bit(CFHSI_AWAKE, &cfhsi->bits); - /* Check if there is something in FIFO. */ - if (WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, - &fifo_occupancy))) - fifo_occupancy = 0; + /* Cancel pending RX requests. */ + cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev); - if (fifo_occupancy) { - dev_dbg(&cfhsi->ndev->dev, - "%s: %u words in RX FIFO, wakeup forced.\n", - __func__, (unsigned) fifo_occupancy); - if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits)) - queue_work(cfhsi->wq, &cfhsi->wake_up_work); - } else - dev_dbg(&cfhsi->ndev->dev, "%s: Done.\n", - __func__); } static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) @@ -874,11 +871,7 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) } /* Delete inactivity timer if started. */ -#ifdef CONFIG_SMP timer_active = del_timer_sync(&cfhsi->timer); -#else - timer_active = del_timer(&cfhsi->timer); -#endif /* CONFIG_SMP */ spin_unlock_bh(&cfhsi->lock); @@ -962,7 +955,7 @@ int cfhsi_probe(struct platform_device *pdev) /* Initialize state vaiables. */ cfhsi->tx_state = CFHSI_TX_STATE_IDLE; - cfhsi->rx_state = CFHSI_RX_STATE_DESC; + cfhsi->rx_state.state = CFHSI_RX_STATE_DESC; /* Set flow info */ cfhsi->flow_off_sent = 0; @@ -1012,15 +1005,12 @@ int cfhsi_probe(struct platform_device *pdev) /* Initialize the work queues. */ INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up); INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down); - INIT_WORK(&cfhsi->rx_done_work, cfhsi_rx_done_work); - INIT_WORK(&cfhsi->tx_done_work, cfhsi_tx_done_work); /* Clear all bit fields. */ clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); clear_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits); clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); clear_bit(CFHSI_AWAKE, &cfhsi->bits); - clear_bit(CFHSI_PENDING_RX, &cfhsi->bits); /* Create work thread. */ cfhsi->wq = create_singlethread_workqueue(pdev->name); @@ -1040,6 +1030,10 @@ int cfhsi_probe(struct platform_device *pdev) init_timer(&cfhsi->timer); cfhsi->timer.data = (unsigned long)cfhsi; cfhsi->timer.function = cfhsi_inactivity_tout; + /* Setup the slowpath RX timer. */ + init_timer(&cfhsi->rx_slowpath_timer); + cfhsi->rx_slowpath_timer.data = (unsigned long)cfhsi; + cfhsi->rx_slowpath_timer.function = cfhsi_rx_slowpath; /* Add CAIF HSI device to list. */ spin_lock(&cfhsi_list_lock); @@ -1110,12 +1104,9 @@ static void cfhsi_shutdown(struct cfhsi *cfhsi, bool remove_platform_dev) /* Flush workqueue */ flush_workqueue(cfhsi->wq); - /* Delete timer if pending */ -#ifdef CONFIG_SMP + /* Delete timers if pending */ del_timer_sync(&cfhsi->timer); -#else - del_timer(&cfhsi->timer); -#endif /* CONFIG_SMP */ + del_timer_sync(&cfhsi->rx_slowpath_timer); /* Cancel pending RX request (if any) */ cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev); diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h index c5dedd87b4cb..17dff451efe3 100644 --- a/include/net/caif/caif_hsi.h +++ b/include/net/caif/caif_hsi.h @@ -75,18 +75,21 @@ struct cfhsi_desc { #define CFHSI_WAKE_UP_ACK 1 #define CFHSI_WAKE_DOWN_ACK 2 #define CFHSI_AWAKE 3 -#define CFHSI_PENDING_RX 4 -#define CFHSI_SHUTDOWN 6 -#define CFHSI_FLUSH_FIFO 7 +#define CFHSI_WAKELOCK_HELD 4 +#define CFHSI_SHUTDOWN 5 +#define CFHSI_FLUSH_FIFO 6 #ifndef CFHSI_INACTIVITY_TOUT #define CFHSI_INACTIVITY_TOUT (1 * HZ) #endif /* CFHSI_INACTIVITY_TOUT */ -#ifndef CFHSI_WAKEUP_TOUT -#define CFHSI_WAKEUP_TOUT (3 * HZ) -#endif /* CFHSI_WAKEUP_TOUT */ +#ifndef CFHSI_WAKE_TOUT +#define CFHSI_WAKE_TOUT (3 * HZ) +#endif /* CFHSI_WAKE_TOUT */ +#ifndef CFHSI_MAX_RX_RETRIES +#define CFHSI_MAX_RX_RETRIES (10 * HZ) +#endif /* Structure implemented by the CAIF HSI driver. */ struct cfhsi_drv { @@ -109,6 +112,15 @@ struct cfhsi_dev { struct cfhsi_drv *drv; }; +/* Structure holds status of received CAIF frames processing */ +struct cfhsi_rx_state { + int state; + int nfrms; + int pld_len; + int retries; + bool piggy_desc; +}; + /* Structure implemented by CAIF HSI drivers. */ struct cfhsi { struct caif_dev_common cfdev; @@ -118,7 +130,7 @@ struct cfhsi { struct cfhsi_drv drv; struct cfhsi_dev *dev; int tx_state; - int rx_state; + struct cfhsi_rx_state rx_state; int rx_len; u8 *rx_ptr; u8 *tx_buf; @@ -130,13 +142,12 @@ struct cfhsi { struct list_head list; struct work_struct wake_up_work; struct work_struct wake_down_work; - struct work_struct rx_done_work; - struct work_struct tx_done_work; struct workqueue_struct *wq; wait_queue_head_t wake_up_wait; wait_queue_head_t wake_down_wait; wait_queue_head_t flush_fifo_wait; struct timer_list timer; + struct timer_list rx_slowpath_timer; unsigned long bits; }; -- cgit v1.2.3 From ca63f8c7512acbd1171bbabefc7a7765ce117939 Mon Sep 17 00:00:00 2001 From: Daniel Martensson Date: Thu, 13 Oct 2011 11:29:26 +0000 Subject: caif-hsi: HSI-Platform device register and unregisters itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Platform device is no longer removed from caif_hsi at shutdown. The HSI-platform device must do it's own registration and unregistration. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'drivers/net/caif/caif_hsi.c') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 478b025c9f8b..f46ab4dfd7a0 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -1083,7 +1083,7 @@ int cfhsi_probe(struct platform_device *pdev) return res; } -static void cfhsi_shutdown(struct cfhsi *cfhsi, bool remove_platform_dev) +static void cfhsi_shutdown(struct cfhsi *cfhsi) { u8 *tx_buf, *rx_buf; @@ -1093,14 +1093,6 @@ static void cfhsi_shutdown(struct cfhsi *cfhsi, bool remove_platform_dev) /* going to shutdown driver */ set_bit(CFHSI_SHUTDOWN, &cfhsi->bits); - if (remove_platform_dev) { - /* Flush workqueue */ - flush_workqueue(cfhsi->wq); - - /* Notify device. */ - platform_device_unregister(cfhsi->pdev); - } - /* Flush workqueue */ flush_workqueue(cfhsi->wq); @@ -1111,7 +1103,7 @@ static void cfhsi_shutdown(struct cfhsi *cfhsi, bool remove_platform_dev) /* Cancel pending RX request (if any) */ cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev); - /* Flush again and destroy workqueue */ + /* Destroy workqueue */ destroy_workqueue(cfhsi->wq); /* Store bufferes: will be freed later. */ @@ -1150,7 +1142,7 @@ int cfhsi_remove(struct platform_device *pdev) spin_unlock(&cfhsi_list_lock); /* Shutdown driver. */ - cfhsi_shutdown(cfhsi, false); + cfhsi_shutdown(cfhsi); return 0; } @@ -1183,7 +1175,7 @@ static void __exit cfhsi_exit_module(void) spin_unlock(&cfhsi_list_lock); /* Shutdown driver. */ - cfhsi_shutdown(cfhsi, true); + cfhsi_shutdown(cfhsi); spin_lock(&cfhsi_list_lock); } -- cgit v1.2.3 From 28bd2049428202cb3bc982536ed3de3c69ae120a Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 13 Oct 2011 11:29:27 +0000 Subject: caif-hsi: Make inactivity timeout configurable. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CAIF HSI uses a timer for inactivity. Upon timeout HSI-wake signaling is initiated to allow power-down of the HSI block. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 28 +++++++++++++++++++++++----- include/net/caif/caif_hsi.h | 1 + 2 files changed, 24 insertions(+), 5 deletions(-) (limited to 'drivers/net/caif/caif_hsi.c') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index f46ab4dfd7a0..1e1f0a372ffa 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -29,6 +29,10 @@ MODULE_DESCRIPTION("CAIF HSI driver"); #define PAD_POW2(x, pow) ((((x)&((pow)-1)) == 0) ? 0 :\ (((pow)-((x)&((pow)-1))))) +static int inactivity_timeout = 1000; +module_param(inactivity_timeout, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(inactivity_timeout, "Inactivity timeout on HSI, ms."); + /* * HSI padding options. * Warning: must be a base of 2 (& operation used) and can not be zero ! @@ -98,7 +102,8 @@ static void cfhsi_abort_tx(struct cfhsi *cfhsi) } cfhsi->tx_state = CFHSI_TX_STATE_IDLE; if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) - mod_timer(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT); + mod_timer(&cfhsi->timer, + jiffies + cfhsi->inactivity_timeout); spin_unlock_bh(&cfhsi->lock); } @@ -312,7 +317,7 @@ static void cfhsi_tx_done(struct cfhsi *cfhsi) cfhsi->tx_state = CFHSI_TX_STATE_IDLE; /* Start inactivity timer. */ mod_timer(&cfhsi->timer, - jiffies + CFHSI_INACTIVITY_TOUT); + jiffies + cfhsi->inactivity_timeout); spin_unlock_bh(&cfhsi->lock); goto done; } @@ -534,7 +539,8 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) /* Update inactivity timer if pending. */ spin_lock_bh(&cfhsi->lock); - mod_timer_pending(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT); + mod_timer_pending(&cfhsi->timer, + jiffies + cfhsi->inactivity_timeout); spin_unlock_bh(&cfhsi->lock); if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { @@ -715,7 +721,7 @@ static void cfhsi_wake_up(struct work_struct *work) __func__); /* Start inactivity timer. */ mod_timer(&cfhsi->timer, - jiffies + CFHSI_INACTIVITY_TOUT); + jiffies + cfhsi->inactivity_timeout); spin_unlock_bh(&cfhsi->lock); return; } @@ -989,7 +995,19 @@ int cfhsi_probe(struct platform_device *pdev) goto err_alloc_rx; } - /* Initialize receive variables. */ + /* Pre-calculate inactivity timeout. */ + if (inactivity_timeout != -1) { + cfhsi->inactivity_timeout = + inactivity_timeout * HZ / 1000; + if (!cfhsi->inactivity_timeout) + cfhsi->inactivity_timeout = 1; + else if (cfhsi->inactivity_timeout > NEXT_TIMER_MAX_DELTA) + cfhsi->inactivity_timeout = NEXT_TIMER_MAX_DELTA; + } else { + cfhsi->inactivity_timeout = NEXT_TIMER_MAX_DELTA; + } + + /* Initialize recieve vaiables. */ cfhsi->rx_ptr = cfhsi->rx_buf; cfhsi->rx_len = CFHSI_DESC_SZ; diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h index 17dff451efe3..9b69d153c94a 100644 --- a/include/net/caif/caif_hsi.h +++ b/include/net/caif/caif_hsi.h @@ -131,6 +131,7 @@ struct cfhsi { struct cfhsi_dev *dev; int tx_state; struct cfhsi_rx_state rx_state; + unsigned long inactivity_timeout; int rx_len; u8 *rx_ptr; u8 *tx_buf; -- cgit v1.2.3 From 5bbed92d3d8dab1f28945eec9fb15b6f50bf8669 Mon Sep 17 00:00:00 2001 From: Daniel Martensson Date: Thu, 13 Oct 2011 11:29:28 +0000 Subject: caif-hsi: Added sanity check for length of CAIF frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added sanity check for length of CAIF frames, and tear down of CAIF link-layer device upon protocol error. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 79 ++++++++++++++++++++++++++++----------------- include/net/caif/caif_hsi.h | 6 ++-- 2 files changed, 53 insertions(+), 32 deletions(-) (limited to 'drivers/net/caif/caif_hsi.c') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 1e1f0a372ffa..e9e7cbf01a5f 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -348,8 +349,7 @@ static void cfhsi_tx_done_cb(struct cfhsi_drv *drv) cfhsi_tx_done(cfhsi); } -static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, - bool *dump) +static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) { int xfer_sz = 0; int nfrms = 0; @@ -360,8 +360,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", __func__); - *dump = true; - return 0; + return -EPROTO; } /* Check for embedded CAIF frame. */ @@ -379,6 +378,12 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, len |= ((*(pfrm+1)) << 8) & 0xFF00; len += 2; /* Add FCS fields. */ + /* Sanity check length of CAIF frame. */ + if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { + dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n", + __func__); + return -EPROTO; + } /* Allocate SKB (OK even in IRQ context). */ skb = alloc_skb(len + 1, GFP_ATOMIC); @@ -423,18 +428,16 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, if (desc->header & CFHSI_PIGGY_DESC) xfer_sz += CFHSI_DESC_SZ; - if (xfer_sz % 4) { + if ((xfer_sz % 4) || (xfer_sz > (CFHSI_BUF_SZ_RX - CFHSI_DESC_SZ))) { dev_err(&cfhsi->ndev->dev, "%s: Invalid payload len: %d, ignored.\n", __func__, xfer_sz); - xfer_sz = 0; - *dump = true; + return -EPROTO; } return xfer_sz; } -static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, - bool *dump) +static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) { int rx_sz = 0; int nfrms = 0; @@ -446,8 +449,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, (desc->offset > CFHSI_MAX_EMB_FRM_SZ))) { dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", __func__); - *dump = true; - return -EINVAL; + return -EPROTO; } /* Set frame pointer to start of payload. */ @@ -469,13 +471,6 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, u8 *pcffrm = NULL; int len = 0; - if (WARN_ON(desc->cffrm_len[nfrms] > CFHSI_MAX_PAYLOAD_SZ)) { - dev_err(&cfhsi->ndev->dev, "%s: Invalid payload.\n", - __func__); - *dump = true; - return -EINVAL; - } - /* CAIF frame starts after head padding. */ pcffrm = pfrm + *pfrm + 1; @@ -484,6 +479,13 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, len |= ((*(pcffrm + 1)) << 8) & 0xFF00; len += 2; /* Add FCS fields. */ + /* Sanity check length of CAIF frames. */ + if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { + dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n", + __func__); + return -EPROTO; + } + /* Allocate SKB (OK even in IRQ context). */ skb = alloc_skb(len + 1, GFP_ATOMIC); if (!skb) { @@ -528,7 +530,6 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) int res; int desc_pld_len = 0; struct cfhsi_desc *desc = NULL; - bool dump = false; desc = (struct cfhsi_desc *)cfhsi->rx_buf; @@ -544,16 +545,20 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) spin_unlock_bh(&cfhsi->lock); if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { - desc_pld_len = cfhsi_rx_desc(desc, cfhsi, &dump); + desc_pld_len = cfhsi_rx_desc(desc, cfhsi); if (desc_pld_len == -ENOMEM) goto restart; + if (desc_pld_len == -EPROTO) + goto out_of_sync; } else { int pld_len; if (!cfhsi->rx_state.piggy_desc) { - pld_len = cfhsi_rx_pld(desc, cfhsi, &dump); + pld_len = cfhsi_rx_pld(desc, cfhsi); if (pld_len == -ENOMEM) goto restart; + if (pld_len == -EPROTO) + goto out_of_sync; cfhsi->rx_state.pld_len = pld_len; } else { pld_len = cfhsi->rx_state.pld_len; @@ -567,7 +572,7 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) cfhsi->rx_state.piggy_desc = true; /* Extract piggy-backed descriptor. */ - desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi, &dump); + desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi); if (desc_pld_len == -ENOMEM) goto restart; @@ -577,15 +582,10 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) */ memcpy((u8 *)desc, (u8 *)piggy_desc, CFHSI_DESC_SHORT_SZ); - } - } - if (unlikely(dump)) { - size_t rx_offset = cfhsi->rx_ptr - cfhsi->rx_buf; - dev_err(&cfhsi->ndev->dev, "%s: RX offset: %u.\n", - __func__, (unsigned) rx_offset); - print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, - cfhsi->rx_buf, cfhsi->rx_len + rx_offset); + if (desc_pld_len == -EPROTO) + goto out_of_sync; + } } memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state)); @@ -622,6 +622,13 @@ restart: BUG(); } mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1); + return; + +out_of_sync: + dev_err(&cfhsi->ndev->dev, "%s: Out of sync.\n", __func__); + print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, + cfhsi->rx_buf, CFHSI_DESC_SZ); + schedule_work(&cfhsi->out_of_sync_work); } static void cfhsi_rx_slowpath(unsigned long arg) @@ -804,6 +811,17 @@ static void cfhsi_wake_down(struct work_struct *work) } +static void cfhsi_out_of_sync(struct work_struct *work) +{ + struct cfhsi *cfhsi = NULL; + + cfhsi = container_of(work, struct cfhsi, out_of_sync_work); + + rtnl_lock(); + dev_close(cfhsi->ndev); + rtnl_unlock(); +} + static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) { struct cfhsi *cfhsi = NULL; @@ -1023,6 +1041,7 @@ int cfhsi_probe(struct platform_device *pdev) /* Initialize the work queues. */ INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up); INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down); + INIT_WORK(&cfhsi->out_of_sync_work, cfhsi_out_of_sync); /* Clear all bit fields. */ clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h index 9b69d153c94a..3356769afaee 100644 --- a/include/net/caif/caif_hsi.h +++ b/include/net/caif/caif_hsi.h @@ -52,8 +52,9 @@ struct cfhsi_desc { /* * Maximum bytes transferred in one transfer. */ -/* TODO: 4096 is temporary... */ -#define CFHSI_MAX_PAYLOAD_SZ (CFHSI_MAX_PKTS * 4096) +#define CFHSI_MAX_CAIF_FRAME_SZ 4096 + +#define CFHSI_MAX_PAYLOAD_SZ (CFHSI_MAX_PKTS * CFHSI_MAX_CAIF_FRAME_SZ) /* Size of the complete HSI TX buffer. */ #define CFHSI_BUF_SZ_TX (CFHSI_DESC_SZ + CFHSI_MAX_PAYLOAD_SZ) @@ -143,6 +144,7 @@ struct cfhsi { struct list_head list; struct work_struct wake_up_work; struct work_struct wake_down_work; + struct work_struct out_of_sync_work; struct workqueue_struct *wq; wait_queue_head_t wake_up_wait; wait_queue_head_t wake_down_wait; -- cgit v1.2.3 From 5ea2ef5f8b006ff9e970327e7fea78f1f5841c44 Mon Sep 17 00:00:00 2001 From: Daniel Martensson Date: Thu, 13 Oct 2011 11:29:29 +0000 Subject: caif-hsi: Added recovery check of CA wake status. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added recovery check of CA wake status in case of wake up timeout. Added check of CA wake status in case of wake down timeout. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 42 ++++++++++++++++++++++++++++++++++++++++-- include/net/caif/caif_hsi.h | 1 + 2 files changed, 41 insertions(+), 2 deletions(-) (limited to 'drivers/net/caif/caif_hsi.c') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index e9e7cbf01a5f..073352517adc 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -674,6 +674,7 @@ static void cfhsi_wake_up(struct work_struct *work) /* It happenes when wakeup is requested by * both ends at the same time. */ clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); + clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); return; } @@ -690,19 +691,47 @@ static void cfhsi_wake_up(struct work_struct *work) &cfhsi->bits), ret); if (unlikely(ret < 0)) { /* Interrupted by signal. */ - dev_info(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n", + dev_err(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n", __func__, ret); + clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); cfhsi->dev->cfhsi_wake_down(cfhsi->dev); return; } else if (!ret) { + bool ca_wake = false; + size_t fifo_occupancy = 0; + /* Wakeup timeout */ dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", __func__); + + /* Check FIFO to check if modem has sent something. */ + WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, + &fifo_occupancy)); + + dev_err(&cfhsi->ndev->dev, "%s: Bytes in FIFO: %u.\n", + __func__, (unsigned) fifo_occupancy); + + /* Check if we misssed the interrupt. */ + WARN_ON(cfhsi->dev->cfhsi_get_peer_wake(cfhsi->dev, + &ca_wake)); + + if (ca_wake) { + dev_err(&cfhsi->ndev->dev, "%s: CA Wake missed !.\n", + __func__); + + /* Clear the CFHSI_WAKE_UP_ACK bit to prevent race. */ + clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); + + /* Continue execution. */ + goto wake_ack; + } + clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); cfhsi->dev->cfhsi_wake_down(cfhsi->dev); return; } +wake_ack: dev_dbg(&cfhsi->ndev->dev, "%s: Woken.\n", __func__); @@ -779,12 +808,21 @@ static void cfhsi_wake_down(struct work_struct *work) &cfhsi->bits), ret); if (ret < 0) { /* Interrupted by signal. */ - dev_info(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n", + dev_err(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n", __func__, ret); return; } else if (!ret) { + bool ca_wake = true; + /* Timeout */ dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", __func__); + + /* Check if we misssed the interrupt. */ + WARN_ON(cfhsi->dev->cfhsi_get_peer_wake(cfhsi->dev, + &ca_wake)); + if (!ca_wake) + dev_err(&cfhsi->ndev->dev, "%s: CA Wake missed !.\n", + __func__); } /* Check FIFO occupancy. */ diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h index 3356769afaee..8d552519ff67 100644 --- a/include/net/caif/caif_hsi.h +++ b/include/net/caif/caif_hsi.h @@ -108,6 +108,7 @@ struct cfhsi_dev { int (*cfhsi_rx) (u8 *ptr, int len, struct cfhsi_dev *dev); int (*cfhsi_wake_up) (struct cfhsi_dev *dev); int (*cfhsi_wake_down) (struct cfhsi_dev *dev); + int (*cfhsi_get_peer_wake) (struct cfhsi_dev *dev, bool *status); int (*cfhsi_fifo_occupancy)(struct cfhsi_dev *dev, size_t *occupancy); int (*cfhsi_rx_cancel)(struct cfhsi_dev *dev); struct cfhsi_drv *drv; -- cgit v1.2.3