Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v7 0/8] Request for inclusion: tcp memory buffers
From: David Miller @ 2011-10-13 20:18 UTC (permalink / raw)
  To: glommer
  Cc: linux-kernel, akpm, lizf, kamezawa.hiroyu, ebiederm, paul,
	gthelen, netdev, linux-mm, kirill, avagin, devel
In-Reply-To: <4E9746B0.7030603@parallels.com>

From: Glauber Costa <glommer@parallels.com>
Date: Fri, 14 Oct 2011 00:14:40 +0400

> Are you happy, or at least willing to accept, an approach that keep
> things as they were with cgroups *compiled out*, or were you referring
> to not in use == compiled in, but with no users?

To me these are the same exact thing, because %99 of users will be running
a kernel with every feature turned on in the Kconfig.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH v7 0/8] Request for inclusion: tcp memory buffers
From: Glauber Costa @ 2011-10-13 20:23 UTC (permalink / raw)
  To: David Miller
  Cc: linux-kernel, akpm, lizf, kamezawa.hiroyu, ebiederm, paul,
	gthelen, netdev, linux-mm, kirill, avagin, devel
In-Reply-To: <20111013.161608.1413756673453885746.davem@davemloft.net>

On 10/14/2011 12:16 AM, David Miller wrote:
> From: Glauber Costa<glommer@parallels.com>
> Date: Fri, 14 Oct 2011 00:05:58 +0400
>
>> Thank you for letting me now about your view of this that early.
>
> I depend upon my colleagues to assist me in the large task that is reviewing
> the enormous number of networking patches that get submitted.
>
> Unfortunately, none of them got a chance to review this patch set
> seriously, since I know most of them (especially Eric Dumazet) would
> balk at the overhead you're proposing to add to our stack, just as I
> did.
>
> This is the reality of the situation, and I'm sorry to tell you that
> snippy retorts when someone does take the time out to review your work
> won't help at all.
I understand that and appreciate your time.
I'll try to come up with something that addresses this problem in the 
next submission.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH net-next] net: more accurate skb truesize
From: Andi Kleen @ 2011-10-13 20:33 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, netdev
In-Reply-To: <1318519581.2393.18.camel@edumazet-HP-Compaq-6005-Pro-SFF-PC>

On Thu, Oct 13, 2011 at 05:26:21PM +0200, Eric Dumazet wrote:
> skb truesize currently accounts for sk_buff struct and part of skb head.
> 
> Considering that skb_shared_info is larger than sk_buff, its time to
> take it into account for better memory accounting.
> 
> This patch introduces SKB_TRUESIZE(X) macro to centralize various
> assumptions into a single place.

It's still quite inaccurate, especially for the kmalloced data area if it's not
paged. It would be better to ask slab how much memory was really 
allocated. But at least this could be done more easily now with the new 
macro, so it's definitely a step in the right direction.

-Andi

^ permalink raw reply

* Re: [net-next 1/5] stmmac: add CHAINED descriptor mode support (V2)
From: David Miller @ 2011-10-13 20:39 UTC (permalink / raw)
  To: peppe.cavallaro; +Cc: netdev, rayagond
In-Reply-To: <1318426688-9419-2-git-send-email-peppe.cavallaro@st.com>

From: Giuseppe CAVALLARO <peppe.cavallaro@st.com>
Date: Wed, 12 Oct 2011 15:38:04 +0200

> +#if defined(CONFIG_STMMAC_RING)
> +
> +static unsigned int stmmac_jumbo_frm(struct stmmac_priv *priv,
> +				     struct sk_buff *skb, int csum_insertion)
> +{

This is not exactly what I meant.

In your original patch, two or three line snippets of code were conditionalized.

That's what I wanted you to do here.  Keep as much common code around as possible
in the driver *.c file, but the small 2 or 3 line conditional parts are implemented
in very small well contained inline functions implemented in a header file.

These small, 2 or 3 line, inline functions are where the ifdefs go.

I didn't mean to replicate all of the functions, in their entirety, into some
header file.

You might was well put the entire driver into a header file, then you can add
all the ifdefs you want :-)

^ permalink raw reply

* Re: [PATCH net-next] net: more accurate skb truesize
From: Eric Dumazet @ 2011-10-13 20:51 UTC (permalink / raw)
  To: Andi Kleen; +Cc: David Miller, netdev
In-Reply-To: <20111013203352.GA5707@tassilo.jf.intel.com>

Le jeudi 13 octobre 2011 à 13:33 -0700, Andi Kleen a écrit :
> On Thu, Oct 13, 2011 at 05:26:21PM +0200, Eric Dumazet wrote:
> > skb truesize currently accounts for sk_buff struct and part of skb head.
> > 
> > Considering that skb_shared_info is larger than sk_buff, its time to
> > take it into account for better memory accounting.
> > 
> > This patch introduces SKB_TRUESIZE(X) macro to centralize various
> > assumptions into a single place.
> 
> It's still quite inaccurate, especially for the kmalloced data area if it's not
> paged. It would be better to ask slab how much memory was really 
> allocated. But at least this could be done more easily now with the new 
> macro, so it's definitely a step in the right direction.

Note : in skb_alloc() function, SKB_TRUESIZE(size) delivers the exact
value : I do the ksize(data) call to ask how many byte kmalloc()
provided me.

So skb->truesize is quite accurate (unless KMEMCHECK or other debug
stuff is used of course)

For the SKB_TRUESIZE() macro, we dont want to do a dummy call to
kmalloc()/kfree(), since its basically used to roughly set a queue
limit.

Thanks !

^ permalink raw reply

* [PATCH net-next] sky2: fix skb truesize underestimation
From: Eric Dumazet @ 2011-10-13 21:11 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, Stephen Hemminger

sky2 allocates a page per skb fragment. We must account
PAGE_SIZE increments on skb->truesize, not the actual frag length.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Stephen Hemminger <shemminger@vyatta.com>
---
 drivers/net/ethernet/marvell/sky2.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 6895e3b..9263490 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -2486,7 +2486,7 @@ static void skb_put_frags(struct sk_buff *skb, unsigned int hdr_space,
 
 			frag->size = size;
 			skb->data_len += size;
-			skb->truesize += size;
+			skb->truesize += PAGE_SIZE;
 			skb->len += size;
 			length -= size;
 		}

^ permalink raw reply related

* Re: [PATCH net-next] sky2: fix skb truesize underestimation
From: David Miller @ 2011-10-13 21:13 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev, shemminger
In-Reply-To: <1318540290.2533.22.camel@edumazet-laptop>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Thu, 13 Oct 2011 23:11:30 +0200

> sky2 allocates a page per skb fragment. We must account
> PAGE_SIZE increments on skb->truesize, not the actual frag length.
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied, thanks Eric.

^ permalink raw reply

* [PATCH net-next] ftmac100: fix skb truesize underestimation
From: Eric Dumazet @ 2011-10-13 21:20 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, Po-Yu Chuang

ftmac100 allocates a page per skb fragment. We must account
PAGE_SIZE increments on skb->truesize, not the actual frag length.

If frame is under 64 bytes, page is freed, so increase truesize only for
bigger frames.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Po-Yu Chuang <ratbert@faraday-tech.com>
---
 drivers/net/ethernet/faraday/ftmac100.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c
index 9bd7746..a127cb2 100644
--- a/drivers/net/ethernet/faraday/ftmac100.c
+++ b/drivers/net/ethernet/faraday/ftmac100.c
@@ -439,7 +439,10 @@ static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
 	skb_fill_page_desc(skb, 0, page, 0, length);
 	skb->len += length;
 	skb->data_len += length;
-	skb->truesize += length;
+
+	/* page might be freed in __pskb_pull_tail() */
+	if (length > 64)
+		skb->truesize += PAGE_SIZE;
 	__pskb_pull_tail(skb, min(length, 64));
 
 	ftmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC);

^ permalink raw reply related

* [PATCH net-next] ftgmac100: fix skb truesize underestimation
From: Eric Dumazet @ 2011-10-13 21:30 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, Po-Yu Chuang

ftgmac100 allocates a page per skb fragment. We must account
PAGE_SIZE increments on skb->truesize, not the actual frag length.

If frame is under 64 bytes, page is freed, and truesize adjusted.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Po-Yu Chuang <ratbert@faraday-tech.com>
---
 drivers/net/ethernet/faraday/ftgmac100.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 54709af..fb5579a 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -467,7 +467,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
 
 		skb->len += size;
 		skb->data_len += size;
-		skb->truesize += size;
+		skb->truesize += PAGE_SIZE;
 
 		if (ftgmac100_rxdes_last_segment(rxdes))
 			done = true;
@@ -478,6 +478,8 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
 		rxdes = ftgmac100_current_rxdes(priv);
 	} while (!done);
 
+	if (skb->len <= 64)
+		skb->truesize -= PAGE_SIZE;
 	__pskb_pull_tail(skb, min(skb->len, 64U));
 	skb->protocol = eth_type_trans(skb, netdev);
 

^ permalink raw reply related

* [PATCH net-next] vmxnet3: fix skb truesize underestimation
From: Eric Dumazet @ 2011-10-13 21:38 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, Shreyas Bhatewara

vmxnet3 allocates a page per skb fragment. We must account
PAGE_SIZE increments on skb->truesize, not the actual frag length.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Shreyas Bhatewara <sbhatewara@vmware.com>
---
 drivers/net/vmxnet3/vmxnet3_drv.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 1694038..902f284 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -658,6 +658,7 @@ vmxnet3_append_frag(struct sk_buff *skb, struct Vmxnet3_RxCompDesc *rcd,
 	frag->page_offset = 0;
 	frag->size = rcd->len;
 	skb->data_len += frag->size;
+	skb->truesize += PAGE_SIZE;
 	skb_shinfo(skb)->nr_frags++;
 }
 
@@ -1277,7 +1278,6 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
 		skb = ctx->skb;
 		if (rcd->eop) {
 			skb->len += skb->data_len;
-			skb->truesize += skb->data_len;
 
 			vmxnet3_rx_csum(adapter, skb,
 					(union Vmxnet3_GenericDesc *)rcd);

^ permalink raw reply related

* [PATCH 0/8] caif-hsi: Bug-fixes for CAIF HSI.
From: Sjur Brændeland @ 2011-10-13 21:29 UTC (permalink / raw)
  To: David Miller, netdev
  Cc: dmitry.tarnyagin, daniel.martensson, Sjur Brændeland

This patch-set contains our latest pile of HSI bug fixes and
performance improvements for CAIF HSI driver.

Patches should apply cleanly on both net and net-next.

Regards,
Sjur

Daniel Martensson (4):
  caif-hsi: Making read and writes asynchronous.
  caif-hsi: HSI-Platform device register and unregisters itself
  caif-hsi: Added sanity check for length of CAIF frames
  caif-hsi: Added recovery check of CA wake status.

Dmitry Tarnyagin (3):
  caif-hsi: Fixing a race condition in the caif_hsi code
  caif-hsi: Fix for wakeup condition problem
  caif-hsi: Make inactivity timeout configurable.

Sjur Brændeland (1):
  caif-hsi: HSI Fix uninitialized data in HSI header

 drivers/net/caif/caif_hsi.c |  427 +++++++++++++++++++++++++------------------
 include/net/caif/caif_hsi.h |   37 +++-
 2 files changed, 276 insertions(+), 188 deletions(-)

^ permalink raw reply

* [PATCH 1/8] caif-hsi: HSI Fix uninitialized data in HSI header
From: Sjur Brændeland @ 2011-10-13 21:29 UTC (permalink / raw)
  To: David Miller, netdev
  Cc: dmitry.tarnyagin, daniel.martensson, Sjur Brændeland
In-Reply-To: <1318541369-8141-1-git-send-email-sjur.brandeland@stericsson.com>

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 <sjur.brandeland@stericsson.com>
---
 drivers/net/caif/caif_hsi.c |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 2fcabba..1937813 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) {
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 2/8] caif-hsi: Fixing a race condition in the caif_hsi code
From: Sjur Brændeland @ 2011-10-13 21:29 UTC (permalink / raw)
  To: David Miller, netdev
  Cc: dmitry.tarnyagin, daniel.martensson, Sjur Brændeland
In-Reply-To: <1318541369-8141-1-git-send-email-sjur.brandeland@stericsson.com>

From: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>

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 <sjur.brandeland@stericsson.com>
---
 drivers/net/caif/caif_hsi.c |   25 ++++++++++++++++++-------
 1 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 1937813..36da27b 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)
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 3/8] caif-hsi: Fix for wakeup condition problem
From: Sjur Brændeland @ 2011-10-13 21:29 UTC (permalink / raw)
  To: David Miller, netdev
  Cc: dmitry.tarnyagin, daniel.martensson, Sjur Brændeland
In-Reply-To: <1318541369-8141-1-git-send-email-sjur.brandeland@stericsson.com>

From: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>

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 <sjur.brandeland@stericsson.com>
---
 drivers/net/caif/caif_hsi.c |   10 +++++++---
 1 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 36da27b..82c4d6c 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;
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 4/8] caif-hsi: Making read and writes asynchronous.
From: Sjur Brændeland @ 2011-10-13 21:29 UTC (permalink / raw)
  To: David Miller, netdev
  Cc: dmitry.tarnyagin, daniel.martensson, Sjur Brændeland
In-Reply-To: <1318541369-8141-1-git-send-email-sjur.brandeland@stericsson.com>

From: Daniel Martensson <daniel.martensson@stericsson.com>

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 <sjur.brandeland@stericsson.com>
---
 drivers/net/caif/caif_hsi.c |  263 +++++++++++++++++++++----------------------
 include/net/caif/caif_hsi.h |   29 ++++--
 2 files changed, 147 insertions(+), 145 deletions(-)

diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 82c4d6c..478b025 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 c5dedd8..17dff45 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;
 };
 
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 6/8] caif-hsi: Make inactivity timeout configurable.
From: Sjur Brændeland @ 2011-10-13 21:29 UTC (permalink / raw)
  To: David Miller, netdev
  Cc: dmitry.tarnyagin, daniel.martensson, Sjur Brændeland
In-Reply-To: <1318541369-8141-1-git-send-email-sjur.brandeland@stericsson.com>

From: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>

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 <sjur.brandeland@stericsson.com>
---
 drivers/net/caif/caif_hsi.c |   28 +++++++++++++++++++++++-----
 include/net/caif/caif_hsi.h |    1 +
 2 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index f46ab4d..1e1f0a3 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 17dff45..9b69d15 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;
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 5/8] caif-hsi: HSI-Platform device register and unregisters itself
From: Sjur Brændeland @ 2011-10-13 21:29 UTC (permalink / raw)
  To: David Miller, netdev
  Cc: dmitry.tarnyagin, daniel.martensson, Sjur Brændeland
In-Reply-To: <1318541369-8141-1-git-send-email-sjur.brandeland@stericsson.com>

From: Daniel Martensson <daniel.martensson@stericsson.com>

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 <sjur.brandeland@stericsson.com>
---
 drivers/net/caif/caif_hsi.c |   16 ++++------------
 1 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 478b025..f46ab4d 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);
 	}
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 7/8] caif-hsi: Added sanity check for length of CAIF frames
From: Sjur Brændeland @ 2011-10-13 21:29 UTC (permalink / raw)
  To: David Miller, netdev
  Cc: dmitry.tarnyagin, daniel.martensson, Sjur Brændeland
In-Reply-To: <1318541369-8141-1-git-send-email-sjur.brandeland@stericsson.com>

From: Daniel Martensson <daniel.martensson@stericsson.com>

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 <sjur.brandeland@stericsson.com>
---
 drivers/net/caif/caif_hsi.c |   79 ++++++++++++++++++++++++++----------------
 include/net/caif/caif_hsi.h |    6 ++-
 2 files changed, 53 insertions(+), 32 deletions(-)

diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 1e1f0a3..e9e7cbf 100644
--- a/drivers/net/caif/caif_hsi.c
+++ b/drivers/net/caif/caif_hsi.c
@@ -18,6 +18,7 @@
 #include <linux/sched.h>
 #include <linux/if_arp.h>
 #include <linux/timer.h>
+#include <linux/rtnetlink.h>
 #include <net/caif/caif_layer.h>
 #include <net/caif/caif_hsi.h>
 
@@ -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 9b69d15..3356769 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;
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 8/8] caif-hsi: Added recovery check of CA wake status.
From: Sjur Brændeland @ 2011-10-13 21:29 UTC (permalink / raw)
  To: David Miller, netdev
  Cc: dmitry.tarnyagin, daniel.martensson, Sjur Brændeland
In-Reply-To: <1318541369-8141-1-git-send-email-sjur.brandeland@stericsson.com>

From: Daniel Martensson <daniel.martensson@stericsson.com>

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 <sjur.brandeland@stericsson.com>
---
 drivers/net/caif/caif_hsi.c |   42 ++++++++++++++++++++++++++++++++++++++++--
 include/net/caif/caif_hsi.h |    1 +
 2 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index e9e7cbf..0733525 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 3356769..8d55251 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;
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH net-next] niu: fix skb truesize underestimation
From: Eric Dumazet @ 2011-10-13 22:39 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

Add a 'truesize' argument to niu_rx_skb_append(), filled with rcr_size
by the caller to properly account frag sizes in skb->truesize

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
Please David double check this one as I am not very familiar with NIU
code. Thanks !

 drivers/net/ethernet/sun/niu.c |   12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c
index d133888..23740e8 100644
--- a/drivers/net/ethernet/sun/niu.c
+++ b/drivers/net/ethernet/sun/niu.c
@@ -3287,17 +3287,13 @@ static u16 tcam_get_valid_entry_cnt(struct niu *np)
 }
 
 static void niu_rx_skb_append(struct sk_buff *skb, struct page *page,
-			      u32 offset, u32 size)
+			      u32 offset, u32 size, u32 truesize)
 {
-	int i = skb_shinfo(skb)->nr_frags;
-
-	__skb_fill_page_desc(skb, i, page, offset, size);
+	skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, offset, size);
 
 	skb->len += size;
 	skb->data_len += size;
-	skb->truesize += size;
-
-	skb_shinfo(skb)->nr_frags = i + 1;
+	skb->truesize += truesize;
 }
 
 static unsigned int niu_hash_rxaddr(struct rx_ring_info *rp, u64 a)
@@ -3480,7 +3476,7 @@ static int niu_process_rx_pkt(struct napi_struct *napi, struct niu *np,
 		} else if (!(val & RCR_ENTRY_MULTI))
 			append_size = len - skb->len;
 
-		niu_rx_skb_append(skb, page, off, append_size);
+		niu_rx_skb_append(skb, page, off, append_size, rcr_size);
 		if ((page->index + rp->rbr_block_size) - rcr_size == addr) {
 			*link = (struct page *) page->mapping;
 			np->ops->unmap_page(np->device, page->index,

^ permalink raw reply related

* Re: [PATCH] net: ipv6: Allow netlink to set IPv6 address scope
From: Lorenzo Colitti @ 2011-10-13 23:55 UTC (permalink / raw)
  To: Brian Haley; +Cc: maze, yoshfuji, netdev
In-Reply-To: <4E931A46.1040905@hp.com>

On Mon, Oct 10, 2011 at 09:16, Brian Haley <brian.haley@hp.com> wrote:
> > net: ipv6: Allow netlink to set IPv6 address scope
> >
> > Currently, userspace cannot specify the scope of IPv6
> > addresses when creating or modifying them. Instead, the
> > scope is automatically determined from the address itself.
> > In IPv4, userspace can set whatever scope it likes.
> >
> > Allow userspace to specify the scope of IPv6 addresses in
> > a backwards-compatible way: if the scope passed in is zero,
> > use the old behaviour of automatically determining the
> > scope based on the address.
> >
> > Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
>
> Hi Lorenzo,
>
> I remember someone proposing a similar patch before and it was not accepted, do you have a use case for doing this?  It just seems like it will cause problems.

Well, to begin with it's a question of feature parity. If we allow
users to set the scope of IPv4 addresses, then we should allow them to
set the scope of IPv6 addresses as well. Policy belongs in userspace,
not in the kernel.

One use case is as follows. I have a phone with an always-on IPv6
interface which is used for signaling, provisioning, SMS, etc. This
IPv6 address is a global unicast IPv6 address, but it belongs to a
closed carrier network, and cannot be used to communicate with the
IPv6 Internet in either direction.

The phone also supports wifi. When there is IPv6 on the wifi
interface, things get messy because the phone can decide to use the
carrier source address on the wifi interface. This will not work,
because the replies will be dropped in the carrier network.

You can stop this from happening in most cases by not putting a
default route on the walled garden interface, and only using host
routes as needed. Unfortunately, you can't stop it from happening in
at least the following two cases:

1. You receive an IPv6 RA on the wifi interface which provides a
default route and a 6to4 address, or a default route and no IPv6
address (for example, because the network uses DHCPv6 and not
RFC4862-style autoconf)

2. You receive an IPv6 RA on the wifi interface which provides a
default route and a "native" IPv6 address, but the phone attempts a
connection while the address is in the tentative state (i.e., the
phone is performing DAD on the address)

In both cases, ipv6_get_saddr_eval will return the carrier IPv6
address (because "avoid tentative and optimistic addresses" takes
precedence over "prefer outgoing interface" in RFC3484) and the kernel
will pick the default route on the wifi interface. The return packets
will get dropped and the connection will time out after several
minutes.
The only way I can think of to get this right is to set the carrier
IPv6 address to a scope less than global - which, in effect, it is,
because it can't reach the Internet.

> Also, there are other parts of the kernel (NFS, SCTP, IPv6 multicast) that are still calling ipv6_addr_scope() on a plain address - won't those be broken since they'll return the correct, RFC-implied scope?

Good point. I looked at these and don't think there is a serious problem though:

- SCTP doesn't look at the scope in IPv4 either, it just looks at the
address itself. So at worse this change will make IPv6 match IPv4.

- NFS only looks at the scope to check whether it's link-local, and if
so only declares an address to be unique if the scopes match. In this
case I think it's the right thing to do, because it's really the
network that decides whether an address can be duplicate on different
links, not the host.

- Unicast and multicast do it when dumping the addresses to userspace.
I need to fix these.

Does that make sense?

^ permalink raw reply

* Re: [net-next PATCH] net: allow vlan traffic to be received under bond
From: Jesse Gross @ 2011-10-14  0:22 UTC (permalink / raw)
  To: John Fastabend
  Cc: Hans Schillström, Jiri Pirko, Maxime Bizon,
	davem@davemloft.net, netdev@vger.kernel.org, fubar@us.ibm.com
In-Reply-To: <4E972301.4030004@intel.com>

2011/10/13 John Fastabend <john.r.fastabend@intel.com>:
> On 10/13/2011 8:59 AM, Hans Schillström wrote:
>> Thu, Oct 13, 2011 at 05:04:34PM CEST, mbizon@freebox.fr wrote:
>>>>
>>>> On Tue, 2011-10-11 at 00:37 +0200, Jiri Pirko wrote:
>>>>
>>>>> Hmm, I must look at this again tomorrow but I have strong feeling this
>>>>> will break some some scenario including vlan-bridge-macvlan.
>>>>
>>>> unless I'm mistaken, today's behaviour:
>>>>
>>>> # vconfig add eth0 100
>>>> # brctl addbr br0
>>>> # brctl addif br0 eth0
>>>>
>>>> => eth0.100 gets no more packets, br0.100 is to be used
>>>>
>>>> after the patch won't we get the opposite ?
>>>
>>> Looks like it. The question is what is the correct behaviour...
>>
>> I think this it become correct now, you should not destroy lover level if possible.
>> I.e. as John wrote "it's not an unexpected behaviour"
>>
>> Consider adding a bridge to a vlan like this
>>
>> vconfig add eth0 100
>> brctl addbr br1
>> brctl addif br1 eth0.100
>>
>> If you later add a bridge (or bond) should the previous added bridge still work ?
>> Yes I think so, for me it's the expected behaviour.
>>
>> brctl addbr br0
>> brctl addif br0 eth0
>>
>> Regards
>> Hans
>>
>>
>
> Sorry I'm not entirely sure I followed the above two posts. Note
> this patch restores behavior that has existed for most of the
> 2.6.x kernels. In the 3.x kernels VLAN100 is dropped in the
> schematic below (assuming eth0 is active), I think this is
> incorrect.

Actually, for most of 2.6.x the behavior was somewhat
non-deterministic since it depended on kernel version and the NIC.  As
a result, I think we can safely say that this wasn't a particularly
firm interface that we have to be wedded to.  Based on overwhelming
feedback, I think the interface in this patch is the preferred one and
what we should stabilize on.

^ permalink raw reply

* Re: [PATCH v7 0/8] Request for inclusion: tcp memory buffers
From: Andi Kleen @ 2011-10-14  2:12 UTC (permalink / raw)
  To: David Miller
  Cc: glommer, linux-kernel, akpm, lizf, kamezawa.hiroyu, ebiederm,
	paul, gthelen, netdev, linux-mm, kirill, avagin, devel
In-Reply-To: <20111013.160031.605700447623532119.davem@davemloft.net>

David Miller <davem@davemloft.net> writes:
>
> Make this evaluate into exactly the same exact code stream we have
> now when the memory cgroup feature is not in use, which will be the
> majority of users.

One possible way may be to guard it with static_branch() for 
no limit per cgroup set. That should be as near as practically
possible to the original code.

BTW the thing that usually worries me more is the cache line behaviour
when the feature is in use. In the past some of the namespace patches
have created some extremly hot global cache lines, that hurt on larger
systems (for example the Unix socket regression from uid namespaces that
is still not completly fixed). It would be good to double check that all
important state is distributed properly.

-Andi

-- 
ak@linux.intel.com -- Speaking for myself only

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH net-next] ftgmac100: fix skb truesize underestimation
From: David Miller @ 2011-10-14  2:25 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev, ratbert
In-Reply-To: <1318541452.2533.30.camel@edumazet-laptop>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Thu, 13 Oct 2011 23:30:52 +0200

> ftgmac100 allocates a page per skb fragment. We must account
> PAGE_SIZE increments on skb->truesize, not the actual frag length.
> 
> If frame is under 64 bytes, page is freed, and truesize adjusted.
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied.

^ permalink raw reply

* Re: [PATCH net-next] vmxnet3: fix skb truesize underestimation
From: David Miller @ 2011-10-14  2:26 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev, sbhatewara
In-Reply-To: <1318541897.2533.33.camel@edumazet-laptop>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Thu, 13 Oct 2011 23:38:17 +0200

> vmxnet3 allocates a page per skb fragment. We must account
> PAGE_SIZE increments on skb->truesize, not the actual frag length.
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox