Netdev List
 help / color / mirror / Atom feed
* [PATCH net v2] packet: fix use after free race in send path when dev is released
From: Daniel Borkmann @ 2013-11-21  9:47 UTC (permalink / raw)
  To: davem; +Cc: netdev, Salam Noureddine, Ben Greear, Eric Dumazet

Salam reported a use after free bug in PF_PACKET that occurs when
we're sending out frames on a socket bound device and suddenly the
net device is being unregistered. It appears that commit 827d9780
introduced a possible race condition between {t,}packet_snd() and
packet_notifier(). In the case of a bound socket, packet_notifier()
can drop the last reference to the net_device and {t,}packet_snd()
might end up suddenly sending a packet over a freed net_device.

To avoid reverting 827d9780 and thus introducing a performance
regression compared to the current state of things, we decided to
hold a cached RCU protected pointer to the net device and maintain
it on write side via bind spin_lock protected register_prot_hook()
and __unregister_prot_hook() calls.

In {t,}packet_snd() path, we access this pointer under rcu_read_lock
through packet_cached_dev_get() that holds reference to the device
to prevent it from being freed through packet_notifier() while
we're in send path. This is okay to do as dev_put()/dev_hold() are
per-cpu counters, so this should not be a performance issue. Also,
the code simplifies a bit as we don't need need_rls_dev anymore. For
the notifier section, we're paranoid and defer putting the reference
on the net device, so that we really make sure that we've quite the
RCU read section from packet_cached_dev_get() and waited a grace
period. This fixes the issue reported.

Fixes: 827d978037d7 ("af-packet: Use existing netdev reference for bound sockets.")
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: Salam Noureddine <noureddine@aristanetworks.com>
Tested-by: Salam Noureddine <noureddine@aristanetworks.com>
Cc: Ben Greear <greearb@candelatech.com>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
---
 v1->v2:
  - Applied feedback from Dave and Eric, thanks a lot for this!
  - After back and forth and trying out multiple ideas, we think that this
    patch seems the best way to fix this issue.

 net/packet/af_packet.c | 85 +++++++++++++++++++++++++++++++++-----------------
 net/packet/internal.h  |  2 ++
 2 files changed, 59 insertions(+), 28 deletions(-)

diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 2e8286b..e6de9cc 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -244,11 +244,15 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po);
 static void register_prot_hook(struct sock *sk)
 {
 	struct packet_sock *po = pkt_sk(sk);
+
 	if (!po->running) {
-		if (po->fanout)
+		if (po->fanout) {
 			__fanout_link(sk, po);
-		else
+		} else {
 			dev_add_pack(&po->prot_hook);
+			rcu_assign_pointer(po->cached_dev, po->prot_hook.dev);
+		}
+
 		sock_hold(sk);
 		po->running = 1;
 	}
@@ -266,10 +270,13 @@ static void __unregister_prot_hook(struct sock *sk, bool sync)
 	struct packet_sock *po = pkt_sk(sk);
 
 	po->running = 0;
-	if (po->fanout)
+	if (po->fanout) {
 		__fanout_unlink(sk, po);
-	else
+	} else {
 		__dev_remove_pack(&po->prot_hook);
+		RCU_INIT_POINTER(po->cached_dev, NULL);
+	}
+
 	__sock_put(sk);
 
 	if (sync) {
@@ -2052,12 +2059,24 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
 	return tp_len;
 }
 
+static struct net_device *packet_cached_dev_get(struct packet_sock *po)
+{
+	struct net_device *dev;
+
+	rcu_read_lock();
+	dev = rcu_dereference(po->cached_dev);
+	if (dev)
+		dev_hold(dev);
+	rcu_read_unlock();
+
+	return dev;
+}
+
 static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
 {
 	struct sk_buff *skb;
 	struct net_device *dev;
 	__be16 proto;
-	bool need_rls_dev = false;
 	int err, reserve = 0;
 	void *ph;
 	struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name;
@@ -2070,7 +2089,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
 	mutex_lock(&po->pg_vec_lock);
 
 	if (saddr == NULL) {
-		dev = po->prot_hook.dev;
+		dev	= packet_cached_dev_get(po);
 		proto	= po->num;
 		addr	= NULL;
 	} else {
@@ -2084,19 +2103,17 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
 		proto	= saddr->sll_protocol;
 		addr	= saddr->sll_addr;
 		dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
-		need_rls_dev = true;
 	}
 
 	err = -ENXIO;
 	if (unlikely(dev == NULL))
 		goto out;
-
-	reserve = dev->hard_header_len;
-
 	err = -ENETDOWN;
 	if (unlikely(!(dev->flags & IFF_UP)))
 		goto out_put;
 
+	reserve = dev->hard_header_len;
+
 	size_max = po->tx_ring.frame_size
 		- (po->tp_hdrlen - sizeof(struct sockaddr_ll));
 
@@ -2173,8 +2190,7 @@ out_status:
 	__packet_set_status(po, ph, status);
 	kfree_skb(skb);
 out_put:
-	if (need_rls_dev)
-		dev_put(dev);
+	dev_put(dev);
 out:
 	mutex_unlock(&po->pg_vec_lock);
 	return err;
@@ -2212,7 +2228,6 @@ static int packet_snd(struct socket *sock,
 	struct sk_buff *skb;
 	struct net_device *dev;
 	__be16 proto;
-	bool need_rls_dev = false;
 	unsigned char *addr;
 	int err, reserve = 0;
 	struct virtio_net_hdr vnet_hdr = { 0 };
@@ -2228,7 +2243,7 @@ static int packet_snd(struct socket *sock,
 	 */
 
 	if (saddr == NULL) {
-		dev = po->prot_hook.dev;
+		dev	= packet_cached_dev_get(po);
 		proto	= po->num;
 		addr	= NULL;
 	} else {
@@ -2240,19 +2255,17 @@ static int packet_snd(struct socket *sock,
 		proto	= saddr->sll_protocol;
 		addr	= saddr->sll_addr;
 		dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex);
-		need_rls_dev = true;
 	}
 
 	err = -ENXIO;
-	if (dev == NULL)
+	if (unlikely(dev == NULL))
 		goto out_unlock;
-	if (sock->type == SOCK_RAW)
-		reserve = dev->hard_header_len;
-
 	err = -ENETDOWN;
-	if (!(dev->flags & IFF_UP))
+	if (unlikely(!(dev->flags & IFF_UP)))
 		goto out_unlock;
 
+	if (sock->type == SOCK_RAW)
+		reserve = dev->hard_header_len;
 	if (po->has_vnet_hdr) {
 		vnet_hdr_len = sizeof(vnet_hdr);
 
@@ -2386,15 +2399,14 @@ static int packet_snd(struct socket *sock,
 	if (err > 0 && (err = net_xmit_errno(err)) != 0)
 		goto out_unlock;
 
-	if (need_rls_dev)
-		dev_put(dev);
+	dev_put(dev);
 
 	return len;
 
 out_free:
 	kfree_skb(skb);
 out_unlock:
-	if (dev && need_rls_dev)
+	if (dev)
 		dev_put(dev);
 out:
 	return err;
@@ -2614,6 +2626,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol,
 	po = pkt_sk(sk);
 	sk->sk_family = PF_PACKET;
 	po->num = proto;
+	RCU_INIT_POINTER(po->cached_dev, NULL);
 
 	sk->sk_destruct = packet_sock_destruct;
 	sk_refcnt_debug_inc(sk);
@@ -3298,6 +3311,22 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
 	return 0;
 }
 
+static void packet_dev_put_deferred(struct rcu_head *head)
+{
+	struct packet_sock *po = container_of(head, struct packet_sock, rcu);
+	struct sock *sk = &po->sk;
+
+	spin_lock(&po->bind_lock);
+	po->ifindex = -1;
+
+	if (po->prot_hook.dev) {
+		dev_put(po->prot_hook.dev);
+		po->prot_hook.dev = NULL;
+	}
+
+	spin_unlock(&po->bind_lock);
+	sock_put(sk);
+}
 
 static int packet_notifier(struct notifier_block *this,
 			   unsigned long msg, void *ptr)
@@ -3325,13 +3354,13 @@ static int packet_notifier(struct notifier_block *this,
 					if (!sock_flag(sk, SOCK_DEAD))
 						sk->sk_error_report(sk);
 				}
+				spin_unlock(&po->bind_lock);
+
 				if (msg == NETDEV_UNREGISTER) {
-					po->ifindex = -1;
-					if (po->prot_hook.dev)
-						dev_put(po->prot_hook.dev);
-					po->prot_hook.dev = NULL;
+					sock_hold(sk);
+					call_rcu(&po->rcu,
+						 packet_dev_put_deferred);
 				}
-				spin_unlock(&po->bind_lock);
 			}
 			break;
 		case NETDEV_UP:
diff --git a/net/packet/internal.h b/net/packet/internal.h
index c4e4b45..c6e2d24 100644
--- a/net/packet/internal.h
+++ b/net/packet/internal.h
@@ -113,6 +113,8 @@ struct packet_sock {
 	unsigned int		tp_loss:1;
 	unsigned int		tp_tx_has_off:1;
 	unsigned int		tp_tstamp;
+	struct net_device __rcu	*cached_dev;
+	struct rcu_head		rcu;
 	struct packet_type	prot_hook ____cacheline_aligned_in_smp;
 };
 
-- 
1.8.3.1

^ permalink raw reply related

* Re: [PATCH net] net: sctp: fix copying sk_v6_rcv_saddr in sctp_v6_create_accept_sk
From: Daniel Borkmann @ 2013-11-21  9:08 UTC (permalink / raw)
  To: wangweidong; +Cc: Vlad Yasevich, davem, netdev, linux-sctp, Eric Dumazet
In-Reply-To: <528DAD15.20901@huawei.com>

On 11/21/2013 07:49 AM, wangweidong wrote:
> On 2013/11/19 23:38, Vlad Yasevich wrote:
>> On 11/19/2013 05:51 AM, Daniel Borkmann wrote:
>>> Wang reported an issue that lksctp's test_getname_v6 seems to fail.
>>>
>>> The issue is that we do not copy sk_v6_rcv_saddr over to the new
>>> socket, although the comment above says so regarding rcv_saddr.
>>>
>>> Commit 914e1c8b6980 ("sctp: Inherit all socket options from parent
>>> correctly.") originally moved that over to sctp_copy_sock(), but
>>> after commit efe4208f47f9 ("ipv6: make lookups simpler and faster")
>>> this no longer holds and the actual value of sk_v6_rcv_saddr was
>>> no longer being migrated.
>>>
>>> With this patch, the lksctp test suite passes again for IPv6.
>>>
>>> Fixes: efe4208f47f9 ("ipv6: make lookups simpler and faster")
>>> Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
>>> Signed-off-by: Wang Weidong <wangweidong1@huawei.com>
>>> Cc: Eric Dumazet <eric.dumazet@gmail.com>
>>> ---
>>>   net/sctp/ipv6.c | 1 +
>>>   1 file changed, 1 insertion(+)
>>>
>>> diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
>>> index 7567e6f..be08592 100644
>>> --- a/net/sctp/ipv6.c
>>> +++ b/net/sctp/ipv6.c
>>> @@ -661,6 +661,7 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
>>>   	 * and getpeername().
>>>   	 */
>>>   	sctp_v6_to_sk_daddr(&asoc->peer.primary_addr, newsk);
>>> +	newsk->sk_v6_rcv_saddr = sk->sk_v6_rcv_saddr;
>>>
>>>   	sk_refcnt_debug_inc(newsk);
>>>
>>>
>>
>> This fixes the issue for the accept() case, but the bug is still there
>> in the peeloff case.
>>
>> I think you should make sctp_copy_sock() use sock_copy() for now.  It
>> looks like it will catch all the cases.
>>
>
> I copied the part of the sock_copy() codes into sctp_v6_create_accept_sk instead of
> sctp_copy_sock for testing. Like this:
>
> --------------
>
> @@ -645,7 +645,11 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
>
>          sock_init_data(NULL, newsk);
>
> -       sctp_copy_sock(newsk, sk, asoc);
> +       memcpy(newsk, sk, offsetof(struct sock, sk_dontcopy_begin));
> +
> +       memcpy(&newsk->sk_dontcopy_end, &sk->sk_dontcopy_end,
> +              sk->sk_prot->obj_size - offsetof(struct sock, sk_dontcopy_end));
> +
>          sock_reset_flag(sk, SOCK_ZAPPED);
>
>          newsctp6sk = (struct sctp6_sock *)newsk;
>
> --------------
>
> when I did the test_getname_v6 of lksctp tools. It would hang. And I used the ps aux,
> I can see the process got a D+ STAT.
>
> BTW, I test it in the virtual machine.
>
> So I think: when we use the sock_copy or which calls it, maybe we will get the problem here.
> Is there I do something wrong?

Sorry, got distracted with other things in the mean time.

I will have a closer look at it very soon, so that we can find a proper fix.

> Thanks.
>
>> We can then look at possibly getting rid of sctp_copy_sock() for net-next.
>>
>> -vlad
>>
>>
>>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* [PATCH 4/7] netfilter: fix wrong byte order in nf_ct_seqadj_set internal information
From: Pablo Neira Ayuso @ 2013-11-21  9:05 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1385024728-4057-1-git-send-email-pablo@netfilter.org>

From: Phil Oester <kernel@linuxace.com>

In commit 41d73ec053d2, sequence number adjustments were moved to a
separate file. Unfortunately, the sequence numbers that are stored
in the nf_ct_seqadj structure are expressed in host byte order. The
necessary ntohl call was removed when the call to adjust_tcp_sequence
was collapsed into nf_ct_seqadj_set. This broke the FTP NAT helper.
Fix it by adding back the byte order conversions.

Reported-by: Dawid Stawiarski <dawid.stawiarski@netart.pl>
Signed-off-by: Phil Oester <kernel@linuxace.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_conntrack_seqadj.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c
index 5f9bfd0..17c1bcb 100644
--- a/net/netfilter/nf_conntrack_seqadj.c
+++ b/net/netfilter/nf_conntrack_seqadj.c
@@ -41,8 +41,8 @@ int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
 	spin_lock_bh(&ct->lock);
 	this_way = &seqadj->seq[dir];
 	if (this_way->offset_before == this_way->offset_after ||
-	    before(this_way->correction_pos, seq)) {
-		this_way->correction_pos = seq;
+	    before(this_way->correction_pos, ntohl(seq))) {
+		this_way->correction_pos = ntohl(seq);
 		this_way->offset_before	 = this_way->offset_after;
 		this_way->offset_after	+= off;
 	}
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 3/7] netfilter: synproxy: correct wscale option passing
From: Pablo Neira Ayuso @ 2013-11-21  9:05 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1385024728-4057-1-git-send-email-pablo@netfilter.org>

From: Martin Topholm <mph@one.com>

Timestamp are used to store additional syncookie parameters such as sack,
ecn, and wscale. The wscale value we need to encode is the client's
wscale, since we can't recover that later in the session. Next overwrite
the wscale option so the later synproxy_send_client_synack will send
the backend's wscale to the client.

Signed-off-by: Martin Topholm <mph@one.com>
Reviewed-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_synproxy_core.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c
index cdf4567..9858e3e 100644
--- a/net/netfilter/nf_synproxy_core.c
+++ b/net/netfilter/nf_synproxy_core.c
@@ -151,9 +151,10 @@ void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info,
 	opts->tsecr = opts->tsval;
 	opts->tsval = tcp_time_stamp & ~0x3f;
 
-	if (opts->options & XT_SYNPROXY_OPT_WSCALE)
-		opts->tsval |= info->wscale;
-	else
+	if (opts->options & XT_SYNPROXY_OPT_WSCALE) {
+		opts->tsval |= opts->wscale;
+		opts->wscale = info->wscale;
+	} else
 		opts->tsval |= 0xf;
 
 	if (opts->options & XT_SYNPROXY_OPT_SACK_PERM)
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 1/7] netfilter: fix connlimit Kconfig prompt string
From: Pablo Neira Ayuso @ 2013-11-21  9:05 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1385024728-4057-1-git-send-email-pablo@netfilter.org>

From: Randy Dunlap <rdunlap@infradead.org>

Under Core Netfilter Configuration, connlimit match support has
an extra double quote at the end of it.

Fixes a portion of kernel bugzilla #52671:
  https://bugzilla.kernel.org/show_bug.cgi?id=52671

Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
Reported-by: lailavrazda1979@gmail.com
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/Kconfig |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 48acec1..c3398cd 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -909,7 +909,7 @@ config NETFILTER_XT_MATCH_CONNLABEL
 	  connection simultaneously.
 
 config NETFILTER_XT_MATCH_CONNLIMIT
-	tristate '"connlimit" match support"'
+	tristate '"connlimit" match support'
 	depends on NF_CONNTRACK
 	depends on NETFILTER_ADVANCED
 	---help---
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 0/7] netfilter fixes for net
From: Pablo Neira Ayuso @ 2013-11-21  9:05 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

Hi David!

The following patchset contains fixes for your net tree, they are:

* Remove extra quote from connlimit configuration in Kconfig, from
  Randy Dunlap.

* Fix missing mss option in syn packets sent to the backend in our
  new synproxy target, from Martin Topholm.

* Use window scale announced by client when sending the forged
  syn to the backend, from Martin Topholm.

* Fix IPv6 address comparison in ebtables, from Luís Fernando
  Cornachioni Estrozi.

* Fix wrong endianess in sequence adjustment which breaks helpers
  in NAT configurations, from Phil Oester.

* Fix the error path handling of nft_compat, from me.

* Make sure the global conntrack counter is decremented after the
  object has been released, also from me.

You can pull these changes from:

  git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git master

Thanks!

----------------------------------------------------------------

The following changes since commit 42a2d923cc349583ebf6fdd52a7d35e1c2f7e6bd:

  Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next (2013-11-13 17:40:34 +0900)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git master

for you to fetch changes up to acab78b99633f12aa2b697474562e19c5718a1ca:

  netfilter: ebt_ip6: fix source and destination matching (2013-11-19 15:33:29 +0100)

----------------------------------------------------------------
Luís Fernando Cornachioni Estrozi (1):
      netfilter: ebt_ip6: fix source and destination matching

Martin Topholm (2):
      netfilter: synproxy: send mss option to backend
      netfilter: synproxy: correct wscale option passing

Pablo Neira Ayuso (2):
      netfilter: nft_compat: fix error path in nft_parse_compat()
      netfilter: nf_conntrack: decrement global counter after object release

Phil Oester (1):
      netfilter: fix wrong byte order in nf_ct_seqadj_set internal information

Randy Dunlap (1):
      netfilter: fix connlimit Kconfig prompt string

 net/bridge/netfilter/ebt_ip6.c      |    8 +++++---
 net/ipv4/netfilter/ipt_SYNPROXY.c   |    1 +
 net/ipv6/netfilter/ip6t_SYNPROXY.c  |    1 +
 net/netfilter/Kconfig               |    2 +-
 net/netfilter/nf_conntrack_core.c   |    3 ++-
 net/netfilter/nf_conntrack_seqadj.c |    4 ++--
 net/netfilter/nf_synproxy_core.c    |    7 ++++---
 net/netfilter/nft_compat.c          |   19 +++++++++++++------
 8 files changed, 29 insertions(+), 16 deletions(-)

^ permalink raw reply

* [PATCH 7/7] netfilter: ebt_ip6: fix source and destination matching
From: Pablo Neira Ayuso @ 2013-11-21  9:05 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1385024728-4057-1-git-send-email-pablo@netfilter.org>

From: Luís Fernando Cornachioni Estrozi <lestrozi@uolinc.com>

This bug was introduced on commit 0898f99a2. This just recovers two
checks that existed before as suggested by Bart De Schuymer.

Signed-off-by: Luís Fernando Cornachioni Estrozi <lestrozi@uolinc.com>
Signed-off-by: Bart De Schuymer <bdschuym@pandora.be>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/bridge/netfilter/ebt_ip6.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c
index 99c8566..17fd5f2 100644
--- a/net/bridge/netfilter/ebt_ip6.c
+++ b/net/bridge/netfilter/ebt_ip6.c
@@ -48,10 +48,12 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par)
 	if (info->bitmask & EBT_IP6_TCLASS &&
 	   FWINV(info->tclass != ipv6_get_dsfield(ih6), EBT_IP6_TCLASS))
 		return false;
-	if (FWINV(ipv6_masked_addr_cmp(&ih6->saddr, &info->smsk,
-				       &info->saddr), EBT_IP6_SOURCE) ||
+	if ((info->bitmask & EBT_IP6_SOURCE &&
+	    FWINV(ipv6_masked_addr_cmp(&ih6->saddr, &info->smsk,
+				       &info->saddr), EBT_IP6_SOURCE)) ||
+	    (info->bitmask & EBT_IP6_DEST &&
 	    FWINV(ipv6_masked_addr_cmp(&ih6->daddr, &info->dmsk,
-				       &info->daddr), EBT_IP6_DEST))
+				       &info->daddr), EBT_IP6_DEST)))
 		return false;
 	if (info->bitmask & EBT_IP6_PROTO) {
 		uint8_t nexthdr = ih6->nexthdr;
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 6/7] netfilter: nf_conntrack: decrement global counter after object release
From: Pablo Neira Ayuso @ 2013-11-21  9:05 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1385024728-4057-1-git-send-email-pablo@netfilter.org>

nf_conntrack_free() decrements our counter (net->ct.count)
before releasing the conntrack object. That counter is used in the
nf_conntrack_cleanup_net_list path to check if it's time to
kmem_cache_destroy our cache of conntrack objects. I think we have
a race there that should be easier to trigger (although still hard)
with CONFIG_DEBUG_OBJECTS_FREE as object releases become slowier
according to the following splat:

[ 1136.321305] WARNING: CPU: 2 PID: 2483 at lib/debugobjects.c:260
debug_print_object+0x83/0xa0()
[ 1136.321311] ODEBUG: free active (active state 0) object type:
timer_list hint: delayed_work_timer_fn+0x0/0x20
...
[ 1136.321390] Call Trace:
[ 1136.321398]  [<ffffffff8160d4a2>] dump_stack+0x45/0x56
[ 1136.321405]  [<ffffffff810514e8>] warn_slowpath_common+0x78/0xa0
[ 1136.321410]  [<ffffffff81051557>] warn_slowpath_fmt+0x47/0x50
[ 1136.321414]  [<ffffffff812f8883>] debug_print_object+0x83/0xa0
[ 1136.321420]  [<ffffffff8106aa90>] ? execute_in_process_context+0x90/0x90
[ 1136.321424]  [<ffffffff812f99fb>] debug_check_no_obj_freed+0x20b/0x250
[ 1136.321429]  [<ffffffff8112e7f2>] ? kmem_cache_destroy+0x92/0x100
[ 1136.321433]  [<ffffffff8115d945>] kmem_cache_free+0x125/0x210
[ 1136.321436]  [<ffffffff8112e7f2>] kmem_cache_destroy+0x92/0x100
[ 1136.321443]  [<ffffffffa046b806>] nf_conntrack_cleanup_net_list+0x126/0x160 [nf_conntrack]
[ 1136.321449]  [<ffffffffa046c43d>] nf_conntrack_pernet_exit+0x6d/0x80 [nf_conntrack]
[ 1136.321453]  [<ffffffff81511cc3>] ops_exit_list.isra.3+0x53/0x60
[ 1136.321457]  [<ffffffff815124f0>] cleanup_net+0x100/0x1b0
[ 1136.321460]  [<ffffffff8106b31e>] process_one_work+0x18e/0x430
[ 1136.321463]  [<ffffffff8106bf49>] worker_thread+0x119/0x390
[ 1136.321467]  [<ffffffff8106be30>] ? manage_workers.isra.23+0x2a0/0x2a0
[ 1136.321470]  [<ffffffff8107210b>] kthread+0xbb/0xc0
[ 1136.321472]  [<ffffffff81072050>] ? kthread_create_on_node+0x110/0x110
[ 1136.321477]  [<ffffffff8161b8fc>] ret_from_fork+0x7c/0xb0
[ 1136.321479]  [<ffffffff81072050>] ? kthread_create_on_node+0x110/0x110
[ 1136.321481] ---[ end trace 25f53c192da70825 ]---

Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_conntrack_core.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index e22d950..43549eb 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -764,9 +764,10 @@ void nf_conntrack_free(struct nf_conn *ct)
 	struct net *net = nf_ct_net(ct);
 
 	nf_ct_ext_destroy(ct);
-	atomic_dec(&net->ct.count);
 	nf_ct_ext_free(ct);
 	kmem_cache_free(net->ct.nf_conntrack_cachep, ct);
+	smp_mb__before_atomic_dec();
+	atomic_dec(&net->ct.count);
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_free);
 
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 5/7] netfilter: nft_compat: fix error path in nft_parse_compat()
From: Pablo Neira Ayuso @ 2013-11-21  9:05 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1385024728-4057-1-git-send-email-pablo@netfilter.org>

The patch 0ca743a55991: "netfilter: nf_tables: add compatibility
layer for x_tables", leads to the following Smatch

 warning: "net/netfilter/nft_compat.c:140 nft_parse_compat()
          warn: signedness bug returning '(-34)'"

This nft_parse_compat function returns error codes but the return
type is u8 so the error codes are transformed into small positive
values. The callers don't check the return.

Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nft_compat.c |   19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index a82667c..da0c1f4 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -128,7 +128,7 @@ static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1]
 	[NFTA_RULE_COMPAT_FLAGS]	= { .type = NLA_U32 },
 };
 
-static u8 nft_parse_compat(const struct nlattr *attr, bool *inv)
+static int nft_parse_compat(const struct nlattr *attr, u8 *proto, bool *inv)
 {
 	struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1];
 	u32 flags;
@@ -148,7 +148,8 @@ static u8 nft_parse_compat(const struct nlattr *attr, bool *inv)
 	if (flags & NFT_RULE_COMPAT_F_INV)
 		*inv = true;
 
-	return ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_PROTO]));
+	*proto = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_PROTO]));
+	return 0;
 }
 
 static int
@@ -166,8 +167,11 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
 
 	target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info);
 
-	if (ctx->nla[NFTA_RULE_COMPAT])
-		proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv);
+	if (ctx->nla[NFTA_RULE_COMPAT]) {
+		ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
+		if (ret < 0)
+			goto err;
+	}
 
 	nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
 
@@ -356,8 +360,11 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
 
 	match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info);
 
-	if (ctx->nla[NFTA_RULE_COMPAT])
-		proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv);
+	if (ctx->nla[NFTA_RULE_COMPAT]) {
+		ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
+		if (ret < 0)
+			goto err;
+	}
 
 	nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
 
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 2/7] netfilter: synproxy: send mss option to backend
From: Pablo Neira Ayuso @ 2013-11-21  9:05 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1385024728-4057-1-git-send-email-pablo@netfilter.org>

From: Martin Topholm <mph@one.com>

When the synproxy_parse_options is called on the client ack the mss
option will not be present. Consequently mss wont be included in the
backend syn packet, which falls back to 536 bytes mss.

Therefore XT_SYNPROXY_OPT_MSS is explicitly flagged when recovering mss
value from cookie.

Signed-off-by: Martin Topholm <mph@one.com>
Reviewed-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/ipv4/netfilter/ipt_SYNPROXY.c  |    1 +
 net/ipv6/netfilter/ip6t_SYNPROXY.c |    1 +
 2 files changed, 2 insertions(+)

diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c
index 01cffea..f13bd91 100644
--- a/net/ipv4/netfilter/ipt_SYNPROXY.c
+++ b/net/ipv4/netfilter/ipt_SYNPROXY.c
@@ -244,6 +244,7 @@ synproxy_recv_client_ack(const struct synproxy_net *snet,
 
 	this_cpu_inc(snet->stats->cookie_valid);
 	opts->mss = mss;
+	opts->options |= XT_SYNPROXY_OPT_MSS;
 
 	if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
 		synproxy_check_timestamp_cookie(opts);
diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c
index bf9f612..f78f41a 100644
--- a/net/ipv6/netfilter/ip6t_SYNPROXY.c
+++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c
@@ -259,6 +259,7 @@ synproxy_recv_client_ack(const struct synproxy_net *snet,
 
 	this_cpu_inc(snet->stats->cookie_valid);
 	opts->mss = mss;
+	opts->options |= XT_SYNPROXY_OPT_MSS;
 
 	if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
 		synproxy_check_timestamp_cookie(opts);
-- 
1.7.10.4


^ permalink raw reply related

* Re: [PATCH RFC v1 7/7] net: cpsw: resume/suspend PHY on port start/stop
From: Sebastian Hesselbarth @ 2013-11-21  8:41 UTC (permalink / raw)
  To: Mugunthan V N; +Cc: David S. Miller, netdev, linux-arm-kernel, linux-kernel
In-Reply-To: <528DC5DD.8040801@ti.com>

On 11/21/2013 09:35 AM, Mugunthan V N wrote:
> On Thursday 21 November 2013 01:51 AM, Sebastian Hesselbarth wrote:
>> Network PHYs consume a noticable amount of power. This adds phy_resume
>> on slave_open and phy_suspend on slave_stop to save this power if the
>> port is down anyway.
>>
>> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
>> ---
>> Cc: David S. Miller <davem@davemloft.net>
>> Cc: netdev@vger.kernel.org
>> Cc: linux-arm-kernel@lists.infradead.org
>> Cc: linux-kernel@vger.kernel.org
>> ---
>>   drivers/net/ethernet/ti/cpsw.c |    2 ++
>>   1 files changed, 2 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
>> index 90d41d2..f1dc54f 100644
>> --- a/drivers/net/ethernet/ti/cpsw.c
>> +++ b/drivers/net/ethernet/ti/cpsw.c
>> @@ -1013,6 +1013,7 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
>>   	} else {
>>   		dev_info(priv->dev, "phy found : id is : 0x%x\n",
>>   			 slave->phy->phy_id);
>> +		phy_resume(slave->phy);
>>   		phy_start(slave->phy);
>>
>>   		/* Configure GMII_SEL register */
>> @@ -1081,6 +1082,7 @@ static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv)
>>   	if (!slave->phy)
>>   		return;
>>   	phy_stop(slave->phy);
>> +	phy_suspend(slave->phy);
>>   	phy_disconnect(slave->phy);
>>   	slave->phy = NULL;
>>   }
>
> Can these be called from phy_start and phy_stop itself so that it
> reduces the effort of patching all drivers and also applies to all
> etherent drivers.

Mugunthan,

Florian already mentioned that and I was already guessing that it will
be better hidden within the PHY state machine, too.

My current idea is to have a new state, that Florian also mentioned,
with the following behavior: On phy_stop the PHY will drop down to
suspend if (a) the PHY driver provides a suspend callback and (b) if
there is nothing enabled that requires the PHY to remain powered, e.g.
WoL.

Anyway, I'll have to dig into PHY state machine internals for that
first.

Sebastian

^ permalink raw reply

* Re: [PATCH RFC v1 7/7] net: cpsw: resume/suspend PHY on port start/stop
From: Mugunthan V N @ 2013-11-21  8:35 UTC (permalink / raw)
  To: Sebastian Hesselbarth
  Cc: netdev, David S. Miller, linux-arm-kernel, linux-kernel
In-Reply-To: <1384978913-8052-8-git-send-email-sebastian.hesselbarth@gmail.com>

On Thursday 21 November 2013 01:51 AM, Sebastian Hesselbarth wrote:
> Network PHYs consume a noticable amount of power. This adds phy_resume
> on slave_open and phy_suspend on slave_stop to save this power if the
> port is down anyway.
>
> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> ---
> Cc: David S. Miller <davem@davemloft.net>
> Cc: netdev@vger.kernel.org
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-kernel@vger.kernel.org
> ---
>  drivers/net/ethernet/ti/cpsw.c |    2 ++
>  1 files changed, 2 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
> index 90d41d2..f1dc54f 100644
> --- a/drivers/net/ethernet/ti/cpsw.c
> +++ b/drivers/net/ethernet/ti/cpsw.c
> @@ -1013,6 +1013,7 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
>  	} else {
>  		dev_info(priv->dev, "phy found : id is : 0x%x\n",
>  			 slave->phy->phy_id);
> +		phy_resume(slave->phy);
>  		phy_start(slave->phy);
>  
>  		/* Configure GMII_SEL register */
> @@ -1081,6 +1082,7 @@ static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv)
>  	if (!slave->phy)
>  		return;
>  	phy_stop(slave->phy);
> +	phy_suspend(slave->phy);
>  	phy_disconnect(slave->phy);
>  	slave->phy = NULL;
>  }

Can these be called from phy_start and phy_stop itself so that it
reduces the effort of patching all drivers and also applies to all
etherent drivers.

Regards
Mugunthan V N

^ permalink raw reply

* Re: [PATCH] wimax: remove dead code
From: Johannes Berg @ 2013-11-21  7:27 UTC (permalink / raw)
  To: Joe Perches
  Cc: Michael Opdenacker, inaky.perez-gonzalez, linux-wimax, davem,
	wimax, netdev, linux-kernel
In-Reply-To: <1385007469.2164.39.camel@joe-AO722>

On Wed, 2013-11-20 at 20:17 -0800, Joe Perches wrote:
> On Thu, 2013-11-21 at 04:43 +0100, Michael Opdenacker wrote:
> > This removes a code line that is between a "return 0;" and an error label.
> > This code line can never be reached.
> 
> Adding Johannes Berg who added this oversight.

Obviously, sorry about that. I seem to remember deleting *something*
there (like another error label) but I guess I missed that line.

Acked-by: Johannes Berg <johannes@sipsolutions.net>

johannes

^ permalink raw reply

* Re: [PATCH net] net: sctp: fix copying sk_v6_rcv_saddr in sctp_v6_create_accept_sk
From: wangweidong @ 2013-11-21  6:49 UTC (permalink / raw)
  To: Vlad Yasevich, Daniel Borkmann, davem; +Cc: netdev, linux-sctp, Eric Dumazet
In-Reply-To: <528B85E2.5080403@gmail.com>

On 2013/11/19 23:38, Vlad Yasevich wrote:
> On 11/19/2013 05:51 AM, Daniel Borkmann wrote:
>> Wang reported an issue that lksctp's test_getname_v6 seems to fail.
>>
>> The issue is that we do not copy sk_v6_rcv_saddr over to the new
>> socket, although the comment above says so regarding rcv_saddr.
>>
>> Commit 914e1c8b6980 ("sctp: Inherit all socket options from parent
>> correctly.") originally moved that over to sctp_copy_sock(), but
>> after commit efe4208f47f9 ("ipv6: make lookups simpler and faster")
>> this no longer holds and the actual value of sk_v6_rcv_saddr was
>> no longer being migrated.
>>
>> With this patch, the lksctp test suite passes again for IPv6.
>>
>> Fixes: efe4208f47f9 ("ipv6: make lookups simpler and faster")
>> Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
>> Signed-off-by: Wang Weidong <wangweidong1@huawei.com>
>> Cc: Eric Dumazet <eric.dumazet@gmail.com>
>> ---
>>  net/sctp/ipv6.c | 1 +
>>  1 file changed, 1 insertion(+)
>>
>> diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
>> index 7567e6f..be08592 100644
>> --- a/net/sctp/ipv6.c
>> +++ b/net/sctp/ipv6.c
>> @@ -661,6 +661,7 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
>>  	 * and getpeername().
>>  	 */
>>  	sctp_v6_to_sk_daddr(&asoc->peer.primary_addr, newsk);
>> +	newsk->sk_v6_rcv_saddr = sk->sk_v6_rcv_saddr;
>>  
>>  	sk_refcnt_debug_inc(newsk);
>>  
>>
> 
> This fixes the issue for the accept() case, but the bug is still there
> in the peeloff case.
> 
> I think you should make sctp_copy_sock() use sock_copy() for now.  It
> looks like it will catch all the cases.
> 

I copied the part of the sock_copy() codes into sctp_v6_create_accept_sk instead of
sctp_copy_sock for testing. Like this:

--------------

@@ -645,7 +645,11 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
 
        sock_init_data(NULL, newsk);
 
-       sctp_copy_sock(newsk, sk, asoc);
+       memcpy(newsk, sk, offsetof(struct sock, sk_dontcopy_begin));
+
+       memcpy(&newsk->sk_dontcopy_end, &sk->sk_dontcopy_end,
+              sk->sk_prot->obj_size - offsetof(struct sock, sk_dontcopy_end));
+
        sock_reset_flag(sk, SOCK_ZAPPED);
 
        newsctp6sk = (struct sctp6_sock *)newsk;

--------------

when I did the test_getname_v6 of lksctp tools. It would hang. And I used the ps aux,
I can see the process got a D+ STAT.

BTW, I test it in the virtual machine.

So I think: when we use the sock_copy or which calls it, maybe we will get the problem here.
Is there I do something wrong?

Thanks. 

> We can then look at possibly getting rid of sctp_copy_sock() for net-next.
> 
> -vlad
> 
> 
> 

^ permalink raw reply

* Re: Issue with gratuitous arps when new addr is different from cached addr
From: Salam Noureddine @ 2013-11-21  6:33 UTC (permalink / raw)
  To: Salam Noureddine, David S. Miller, Daniel Borkmann,
	Willem de Bruijn, Phil Sutter, Eric Dumazet, netdev
In-Reply-To: <20131121062349.GC4347@order.stressinduktion.org>

On Wed, Nov 20, 2013 at 10:23 PM, Hannes Frederic Sowa
<hannes@stressinduktion.org> wrote:
> On Wed, Nov 20, 2013 at 10:06:34PM -0800, Salam Noureddine wrote:
>> Isn't locktime useful in that case for limiting the rate of garp
>> changes to the arp cache?
>
> Yes, as I said, it would be nice to have rate-limiting for those, too.
>
> GARP is used in cluster setups to make the switch-over as fast as possible and
> I don't think they accept those lock-down delays, so I guess it would be nice
> to forceful override the lladdr if (sip == tip) && ACCEPT_ARP(dev) is true in
> every case.

I was in fact testing a switch-over scenario and saw that the arp
update was taking
much longer than the locktime period which pointed to this problem.

>
> I guess you are not testing with ACCEPT_ARP?
>

Yes, I am not setting ACCEPT_ARP.

> In that case I am not sure what to do.
>
> override = (...timing...) || sip == tip; could work but does relax the
> protection of the neigh cache.
>
> In IPv6 we have a flag in the packet if we should overwrite the entry.
>
> I'll have to think about that a bit more. Could well be the case that we need
> your proposal, too. But then we would have to validate the change with IPv6,
> too and neighbour cache states are really complex.
>
> Greetings,
>
>   Hannes
>

^ permalink raw reply

* [net-next 05/13] bna: Enable Multi Buffer RX
From: Rasesh Mody @ 2013-11-21  5:54 UTC (permalink / raw)
  To: davem; +Cc: netdev, adapter_linux_open_src_team, Rasesh Mody
In-Reply-To: <1385013257-32745-1-git-send-email-rmody@brocade.com>

The CT2 HW supports multi-buffer Rx. This patch provides the necessary changes
for bnad to use multi-buffer Rx feature. For BNAD, multi-buffer Rx is by
default enabled when MTU is > 4096. Using module parameter bnad_multi_buffer_rx
this behavior can be reverted. For >4096 MTU, q0 data/large buffers are of 2048
size. As the resource requirements of multi-buffer Rx are different new Rx needs
to be created to use this feature. ASIC posts multiple completions if frame
exceeds buffer size. The last completion is marked with EOP flag.
 - Separate HQ and DQ enums for resource allocations and configurations.
 - rx_config and rxq structure changes to pass the correct info from bnad.
 - DQ depth need not be same as HQ depth. So CQ depth is adjusted accordingly.
 - Rx CFG frame size is taken from configured MTU.
 - Rx q0 buffer size is configured from bnad s rx_config when multi-buffer is
   enabled.
 - Poll for entire frame completion.
 - Once EOP completion is received gather the number of vectors used by the
   frame to submit it to the stack.
 - Changed MTU to frame size wherever necessary.

Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 drivers/net/ethernet/brocade/bna/bfi_enet.h    |   3 +-
 drivers/net/ethernet/brocade/bna/bna_hw_defs.h |   4 +
 drivers/net/ethernet/brocade/bna/bna_tx_rx.c   |  66 +++--
 drivers/net/ethernet/brocade/bna/bna_types.h   |  49 ++--
 drivers/net/ethernet/brocade/bna/bnad.c        | 320 ++++++++++++++++++-------
 drivers/net/ethernet/brocade/bna/bnad.h        |  19 +-
 6 files changed, 336 insertions(+), 125 deletions(-)

diff --git a/drivers/net/ethernet/brocade/bna/bfi_enet.h b/drivers/net/ethernet/brocade/bna/bfi_enet.h
index 7d10e33..ae072dc 100644
--- a/drivers/net/ethernet/brocade/bna/bfi_enet.h
+++ b/drivers/net/ethernet/brocade/bna/bfi_enet.h
@@ -472,7 +472,8 @@ enum bfi_enet_hds_type {
 
 struct bfi_enet_rx_cfg {
 	u8		rxq_type;
-	u8		rsvd[3];
+	u8		rsvd[1];
+	u16		frame_size;
 
 	struct {
 		u8			max_header_size;
diff --git a/drivers/net/ethernet/brocade/bna/bna_hw_defs.h b/drivers/net/ethernet/brocade/bna/bna_hw_defs.h
index af3f7bb..2702d02 100644
--- a/drivers/net/ethernet/brocade/bna/bna_hw_defs.h
+++ b/drivers/net/ethernet/brocade/bna/bna_hw_defs.h
@@ -322,6 +322,10 @@ do {									\
 #define	BNA_CQ_EF_REMOTE	(1 << 19)
 
 #define	BNA_CQ_EF_LOCAL		(1 << 20)
+/* CAT2 ASIC does not use bit 21 as per the SPEC.
+ * Bit 31 is set in every end of frame completion
+ */
+#define BNA_CQ_EF_EOP		(1 << 31)
 
 /* Data structures */
 
diff --git a/drivers/net/ethernet/brocade/bna/bna_tx_rx.c b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c
index d028f1d..489017e 100644
--- a/drivers/net/ethernet/brocade/bna/bna_tx_rx.c
+++ b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c
@@ -1811,6 +1811,7 @@ bna_bfi_rx_enet_start(struct bna_rx *rx)
 	cfg_req->mh.num_entries = htons(
 		bfi_msgq_num_cmd_entries(sizeof(struct bfi_enet_rx_cfg_req)));
 
+	cfg_req->rx_cfg.frame_size = bna_enet_mtu_get(&rx->bna->enet);
 	cfg_req->num_queue_sets = rx->num_paths;
 	for (i = 0, rxp_qe = bfa_q_first(&rx->rxp_q);
 		i < rx->num_paths;
@@ -1832,8 +1833,17 @@ bna_bfi_rx_enet_start(struct bna_rx *rx)
 			/* Large/Single RxQ */
 			bfi_enet_datapath_q_init(&cfg_req->q_cfg[i].ql.q,
 						&q0->qpt);
-			q0->buffer_size =
-				bna_enet_mtu_get(&rx->bna->enet);
+			if (q0->multi_buffer)
+				/* multi-buffer is enabled by allocating
+				 * a new rx with new set of resources.
+				 * q0->buffer_size should be initialized to
+				 * fragment size.
+				 */
+				cfg_req->rx_cfg.multi_buffer =
+					BNA_STATUS_T_ENABLED;
+			else
+				q0->buffer_size =
+					bna_enet_mtu_get(&rx->bna->enet);
 			cfg_req->q_cfg[i].ql.rx_buffer_size =
 				htons((u16)q0->buffer_size);
 			break;
@@ -2383,8 +2393,8 @@ bna_rx_res_req(struct bna_rx_config *q_cfg, struct bna_res_info *res_info)
 	u32 hq_depth;
 	u32 dq_depth;
 
-	dq_depth = q_cfg->q_depth;
-	hq_depth = ((q_cfg->rxp_type == BNA_RXP_SINGLE) ? 0 : q_cfg->q_depth);
+	dq_depth = q_cfg->q0_depth;
+	hq_depth = ((q_cfg->rxp_type == BNA_RXP_SINGLE) ? 0 : q_cfg->q1_depth);
 	cq_depth = dq_depth + hq_depth;
 
 	BNA_TO_POWER_OF_2_HIGH(cq_depth);
@@ -2501,10 +2511,10 @@ bna_rx_create(struct bna *bna, struct bnad *bnad,
 	struct bna_rxq *q0;
 	struct bna_rxq *q1;
 	struct bna_intr_info *intr_info;
-	u32 page_count;
+	struct bna_mem_descr *hqunmap_mem;
+	struct bna_mem_descr *dqunmap_mem;
 	struct bna_mem_descr *ccb_mem;
 	struct bna_mem_descr *rcb_mem;
-	struct bna_mem_descr *unmapq_mem;
 	struct bna_mem_descr *cqpt_mem;
 	struct bna_mem_descr *cswqpt_mem;
 	struct bna_mem_descr *cpage_mem;
@@ -2514,8 +2524,10 @@ bna_rx_create(struct bna *bna, struct bnad *bnad,
 	struct bna_mem_descr *dsqpt_mem;
 	struct bna_mem_descr *hpage_mem;
 	struct bna_mem_descr *dpage_mem;
-	int i;
-	int dpage_count, hpage_count, rcb_idx;
+	u32 dpage_count, hpage_count;
+	u32 hq_idx, dq_idx, rcb_idx;
+	u32 cq_depth, i;
+	u32 page_count;
 
 	if (!bna_rx_res_check(rx_mod, rx_cfg))
 		return NULL;
@@ -2523,7 +2535,8 @@ bna_rx_create(struct bna *bna, struct bnad *bnad,
 	intr_info = &res_info[BNA_RX_RES_T_INTR].res_u.intr_info;
 	ccb_mem = &res_info[BNA_RX_RES_MEM_T_CCB].res_u.mem_info.mdl[0];
 	rcb_mem = &res_info[BNA_RX_RES_MEM_T_RCB].res_u.mem_info.mdl[0];
-	unmapq_mem = &res_info[BNA_RX_RES_MEM_T_UNMAPQ].res_u.mem_info.mdl[0];
+	dqunmap_mem = &res_info[BNA_RX_RES_MEM_T_UNMAPDQ].res_u.mem_info.mdl[0];
+	hqunmap_mem = &res_info[BNA_RX_RES_MEM_T_UNMAPHQ].res_u.mem_info.mdl[0];
 	cqpt_mem = &res_info[BNA_RX_RES_MEM_T_CQPT].res_u.mem_info.mdl[0];
 	cswqpt_mem = &res_info[BNA_RX_RES_MEM_T_CSWQPT].res_u.mem_info.mdl[0];
 	cpage_mem = &res_info[BNA_RX_RES_MEM_T_CQPT_PAGE].res_u.mem_info.mdl[0];
@@ -2575,7 +2588,8 @@ bna_rx_create(struct bna *bna, struct bnad *bnad,
 	}
 
 	rx->num_paths = rx_cfg->num_paths;
-	for (i = 0, rcb_idx = 0; i < rx->num_paths; i++) {
+	for (i = 0, hq_idx = 0, dq_idx = 0, rcb_idx = 0;
+			i < rx->num_paths; i++) {
 		rxp = bna_rxp_get(rx_mod);
 		list_add_tail(&rxp->qe, &rx->rxp_q);
 		rxp->type = rx_cfg->rxp_type;
@@ -2618,9 +2632,13 @@ bna_rx_create(struct bna *bna, struct bnad *bnad,
 		q0->rxp = rxp;
 
 		q0->rcb = (struct bna_rcb *) rcb_mem[rcb_idx].kva;
-		q0->rcb->unmap_q = (void *)unmapq_mem[rcb_idx].kva;
-		rcb_idx++;
-		q0->rcb->q_depth = rx_cfg->q_depth;
+		q0->rcb->unmap_q = (void *)dqunmap_mem[dq_idx].kva;
+		rcb_idx++; dq_idx++;
+		q0->rcb->q_depth = rx_cfg->q0_depth;
+		q0->q_depth = rx_cfg->q0_depth;
+		q0->multi_buffer = rx_cfg->q0_multi_buf;
+		q0->buffer_size = rx_cfg->q0_buf_size;
+		q0->num_vecs = rx_cfg->q0_num_vecs;
 		q0->rcb->rxq = q0;
 		q0->rcb->bnad = bna->bnad;
 		q0->rcb->id = 0;
@@ -2640,15 +2658,18 @@ bna_rx_create(struct bna *bna, struct bnad *bnad,
 			q1->rxp = rxp;
 
 			q1->rcb = (struct bna_rcb *) rcb_mem[rcb_idx].kva;
-			q1->rcb->unmap_q = (void *)unmapq_mem[rcb_idx].kva;
-			rcb_idx++;
-			q1->rcb->q_depth = rx_cfg->q_depth;
+			q1->rcb->unmap_q = (void *)hqunmap_mem[hq_idx].kva;
+			rcb_idx++; hq_idx++;
+			q1->rcb->q_depth = rx_cfg->q1_depth;
+			q1->q_depth = rx_cfg->q1_depth;
+			q1->multi_buffer = BNA_STATUS_T_DISABLED;
+			q1->num_vecs = 1;
 			q1->rcb->rxq = q1;
 			q1->rcb->bnad = bna->bnad;
 			q1->rcb->id = 1;
 			q1->buffer_size = (rx_cfg->rxp_type == BNA_RXP_HDS) ?
 					rx_cfg->hds_config.forced_offset
-					: rx_cfg->small_buff_size;
+					: rx_cfg->q1_buf_size;
 			q1->rx_packets = q1->rx_bytes = 0;
 			q1->rx_packets_with_error = q1->rxbuf_alloc_failed = 0;
 
@@ -2663,9 +2684,14 @@ bna_rx_create(struct bna *bna, struct bnad *bnad,
 		/* Setup CQ */
 
 		rxp->cq.ccb = (struct bna_ccb *) ccb_mem[i].kva;
-		rxp->cq.ccb->q_depth =	rx_cfg->q_depth +
-					((rx_cfg->rxp_type == BNA_RXP_SINGLE) ?
-					0 : rx_cfg->q_depth);
+		cq_depth = rx_cfg->q0_depth +
+			((rx_cfg->rxp_type == BNA_RXP_SINGLE) ?
+			 0 : rx_cfg->q1_depth);
+		/* if multi-buffer is enabled sum of q0_depth
+		 * and q1_depth need not be a power of 2
+		 */
+		BNA_TO_POWER_OF_2_HIGH(cq_depth);
+		rxp->cq.ccb->q_depth = cq_depth;
 		rxp->cq.ccb->cq = &rxp->cq;
 		rxp->cq.ccb->rcb[0] = q0->rcb;
 		q0->rcb->ccb = rxp->cq.ccb;
diff --git a/drivers/net/ethernet/brocade/bna/bna_types.h b/drivers/net/ethernet/brocade/bna/bna_types.h
index acedac2..621547c 100644
--- a/drivers/net/ethernet/brocade/bna/bna_types.h
+++ b/drivers/net/ethernet/brocade/bna/bna_types.h
@@ -109,20 +109,21 @@ enum bna_tx_res_req_type {
 enum bna_rx_mem_type {
 	BNA_RX_RES_MEM_T_CCB		= 0,	/* CQ context */
 	BNA_RX_RES_MEM_T_RCB		= 1,	/* CQ context */
-	BNA_RX_RES_MEM_T_UNMAPQ		= 2,	/* UnmapQ for RxQs */
-	BNA_RX_RES_MEM_T_CQPT		= 3,	/* CQ QPT */
-	BNA_RX_RES_MEM_T_CSWQPT		= 4,	/* S/W QPT */
-	BNA_RX_RES_MEM_T_CQPT_PAGE	= 5,	/* CQPT page */
-	BNA_RX_RES_MEM_T_HQPT		= 6,	/* RX QPT */
-	BNA_RX_RES_MEM_T_DQPT		= 7,	/* RX QPT */
-	BNA_RX_RES_MEM_T_HSWQPT		= 8,	/* RX s/w QPT */
-	BNA_RX_RES_MEM_T_DSWQPT		= 9,	/* RX s/w QPT */
-	BNA_RX_RES_MEM_T_DPAGE		= 10,	/* RX s/w QPT */
-	BNA_RX_RES_MEM_T_HPAGE		= 11,	/* RX s/w QPT */
-	BNA_RX_RES_MEM_T_IBIDX		= 12,
-	BNA_RX_RES_MEM_T_RIT		= 13,
-	BNA_RX_RES_T_INTR		= 14,	/* Rx interrupts */
-	BNA_RX_RES_T_MAX		= 15
+	BNA_RX_RES_MEM_T_UNMAPHQ	= 2,
+	BNA_RX_RES_MEM_T_UNMAPDQ	= 3,
+	BNA_RX_RES_MEM_T_CQPT		= 4,
+	BNA_RX_RES_MEM_T_CSWQPT		= 5,
+	BNA_RX_RES_MEM_T_CQPT_PAGE	= 6,
+	BNA_RX_RES_MEM_T_HQPT		= 7,
+	BNA_RX_RES_MEM_T_DQPT		= 8,
+	BNA_RX_RES_MEM_T_HSWQPT		= 9,
+	BNA_RX_RES_MEM_T_DSWQPT		= 10,
+	BNA_RX_RES_MEM_T_DPAGE		= 11,
+	BNA_RX_RES_MEM_T_HPAGE		= 12,
+	BNA_RX_RES_MEM_T_IBIDX		= 13,
+	BNA_RX_RES_MEM_T_RIT		= 14,
+	BNA_RX_RES_T_INTR		= 15,
+	BNA_RX_RES_T_MAX		= 16
 };
 
 enum bna_tx_type {
@@ -583,6 +584,8 @@ struct bna_rxq {
 
 	int			buffer_size;
 	int			q_depth;
+	u32			num_vecs;
+	enum bna_status		multi_buffer;
 
 	struct bna_qpt qpt;
 	struct bna_rcb *rcb;
@@ -632,6 +635,8 @@ struct bna_ccb {
 	struct bna_rcb *rcb[2];
 	void			*ctrl; /* For bnad */
 	struct bna_pkt_rate pkt_rate;
+	u32			pkts_una;
+	u32			bytes_per_intr;
 
 	/* Control path */
 	struct bna_cq *cq;
@@ -671,14 +676,22 @@ struct bna_rx_config {
 	int			num_paths;
 	enum bna_rxp_type rxp_type;
 	int			paused;
-	int			q_depth;
 	int			coalescing_timeo;
 	/*
 	 * Small/Large (or Header/Data) buffer size to be configured
-	 * for SLR and HDS queue type. Large buffer size comes from
-	 * enet->mtu.
+	 * for SLR and HDS queue type.
 	 */
-	int			small_buff_size;
+	u32			frame_size;
+
+	/* header or small queue */
+	u32			q1_depth;
+	u32			q1_buf_size;
+
+	/* data or large queue */
+	u32			q0_depth;
+	u32			q0_buf_size;
+	u32			q0_num_vecs;
+	enum bna_status		q0_multi_buf;
 
 	enum bna_status rss_status;
 	struct bna_rss_config rss_config;
diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index 3b94270..8dfd783 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -49,6 +49,10 @@ module_param(bna_debugfs_enable, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(bna_debugfs_enable, "Enables debugfs feature, default=1,"
 		 " Range[false:0|true:1]");
 
+static uint bnad_multi_buffer_rx = 1;
+module_param(bnad_multi_buffer_rx, uint, 0444);
+MODULE_PARM_DESC(bnad_multi_buffer_rx, "Multi-buffer Rx; 1 - en(default), 0 - dis");
+
 /*
  * Global variables
  */
@@ -282,27 +286,32 @@ static int
 bnad_rxq_alloc_init(struct bnad *bnad, struct bna_rcb *rcb)
 {
 	struct bnad_rx_unmap_q *unmap_q = rcb->unmap_q;
-	int mtu, order;
+	int order;
 
 	bnad_rxq_alloc_uninit(bnad, rcb);
 
-	mtu = bna_enet_mtu_get(&bnad->bna.enet);
-	order = get_order(mtu);
+	order = get_order(rcb->rxq->buffer_size);
+
+	unmap_q->type = BNAD_RXBUF_PAGE;
 
 	if (bna_is_small_rxq(rcb->id)) {
 		unmap_q->alloc_order = 0;
 		unmap_q->map_size = rcb->rxq->buffer_size;
 	} else {
-		unmap_q->alloc_order = order;
-		unmap_q->map_size =
-			(rcb->rxq->buffer_size > 2048) ?
-			PAGE_SIZE << order : 2048;
+		if (rcb->rxq->multi_buffer) {
+			unmap_q->alloc_order = 0;
+			unmap_q->map_size = rcb->rxq->buffer_size;
+			unmap_q->type = BNAD_RXBUF_MULTI_BUFF;
+		} else {
+			unmap_q->alloc_order = order;
+			unmap_q->map_size =
+				(rcb->rxq->buffer_size > 2048) ?
+				PAGE_SIZE << order : 2048;
+		}
 	}
 
 	BUG_ON(((PAGE_SIZE << order) % unmap_q->map_size));
 
-	unmap_q->type = BNAD_RXBUF_PAGE;
-
 	return 0;
 }
 
@@ -345,10 +354,10 @@ bnad_rxq_cleanup(struct bnad *bnad, struct bna_rcb *rcb)
 	for (i = 0; i < rcb->q_depth; i++) {
 		struct bnad_rx_unmap *unmap = &unmap_q->unmap[i];
 
-		if (BNAD_RXBUF_IS_PAGE(unmap_q->type))
-			bnad_rxq_cleanup_page(bnad, unmap);
-		else
+		if (BNAD_RXBUF_IS_SK_BUFF(unmap_q->type))
 			bnad_rxq_cleanup_skb(bnad, unmap);
+		else
+			bnad_rxq_cleanup_page(bnad, unmap);
 	}
 	bnad_rxq_alloc_uninit(bnad, rcb);
 }
@@ -480,10 +489,10 @@ bnad_rxq_post(struct bnad *bnad, struct bna_rcb *rcb)
 	if (!(to_alloc >> BNAD_RXQ_REFILL_THRESHOLD_SHIFT))
 		return;
 
-	if (BNAD_RXBUF_IS_PAGE(unmap_q->type))
-		bnad_rxq_refill_page(bnad, rcb, to_alloc);
-	else
+	if (BNAD_RXBUF_IS_SK_BUFF(unmap_q->type))
 		bnad_rxq_refill_skb(bnad, rcb, to_alloc);
+	else
+		bnad_rxq_refill_page(bnad, rcb, to_alloc);
 }
 
 #define flags_cksum_prot_mask (BNA_CQ_EF_IPV4 | BNA_CQ_EF_L3_CKSUM_OK | \
@@ -500,62 +509,91 @@ bnad_rxq_post(struct bnad *bnad, struct bna_rcb *rcb)
 #define flags_udp6 (BNA_CQ_EF_IPV6 | \
 				BNA_CQ_EF_UDP | BNA_CQ_EF_L4_CKSUM_OK)
 
-static inline struct sk_buff *
-bnad_cq_prepare_skb(struct bnad_rx_ctrl *rx_ctrl,
-		struct bnad_rx_unmap_q *unmap_q,
-		struct bnad_rx_unmap *unmap,
-		u32 length, u32 flags)
+static void
+bnad_cq_drop_packet(struct bnad *bnad, struct bna_rcb *rcb,
+		u32 sop_ci, u32 nvecs)
 {
-	struct bnad *bnad = rx_ctrl->bnad;
-	struct sk_buff *skb;
+	struct bnad_rx_unmap_q *unmap_q;
+	struct bnad_rx_unmap *unmap;
+	u32 ci, vec;
 
-	if (BNAD_RXBUF_IS_PAGE(unmap_q->type)) {
-		skb = napi_get_frags(&rx_ctrl->napi);
-		if (unlikely(!skb))
-			return NULL;
+	unmap_q = rcb->unmap_q;
+	for (vec = 0, ci = sop_ci; vec < nvecs; vec++) {
+		unmap = &unmap_q->unmap[ci];
+		BNA_QE_INDX_INC(ci, rcb->q_depth);
+
+		if (BNAD_RXBUF_IS_SK_BUFF(unmap_q->type))
+			bnad_rxq_cleanup_skb(bnad, unmap);
+		else
+			bnad_rxq_cleanup_page(bnad, unmap);
+	}
+}
+
+static void
+bnad_cq_setup_skb_frags(struct bna_rcb *rcb, struct sk_buff *skb,
+		u32 sop_ci, u32 nvecs, u32 last_fraglen)
+{
+	struct bnad *bnad;
+	u32 ci, vec, len, totlen = 0;
+	struct bnad_rx_unmap_q *unmap_q;
+	struct bnad_rx_unmap *unmap;
+
+	unmap_q = rcb->unmap_q;
+	bnad = rcb->bnad;
+	for (vec = 1, ci = sop_ci; vec <= nvecs; vec++) {
+		unmap = &unmap_q->unmap[ci];
+		BNA_QE_INDX_INC(ci, rcb->q_depth);
 
 		dma_unmap_page(&bnad->pcidev->dev,
 				dma_unmap_addr(&unmap->vector, dma_addr),
 				unmap->vector.len, DMA_FROM_DEVICE);
+
+		len = (vec == nvecs) ?
+			last_fraglen : unmap->vector.len;
+		totlen += len;
+
 		skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags,
-				unmap->page, unmap->page_offset, length);
-		skb->len += length;
-		skb->data_len += length;
-		skb->truesize += length;
+				unmap->page, unmap->page_offset, len);
 
 		unmap->page = NULL;
 		unmap->vector.len = 0;
-
-		return skb;
 	}
 
-	skb = unmap->skb;
-	BUG_ON(!skb);
+	skb->len += totlen;
+	skb->data_len += totlen;
+	skb->truesize += totlen;
+}
+
+static inline void
+bnad_cq_setup_skb(struct bnad *bnad, struct sk_buff *skb,
+		struct bnad_rx_unmap *unmap, u32 len)
+{
+	prefetch(skb->data);
 
 	dma_unmap_single(&bnad->pcidev->dev,
 			dma_unmap_addr(&unmap->vector, dma_addr),
 			unmap->vector.len, DMA_FROM_DEVICE);
 
-	skb_put(skb, length);
-
+	skb_put(skb, len);
 	skb->protocol = eth_type_trans(skb, bnad->netdev);
 
 	unmap->skb = NULL;
 	unmap->vector.len = 0;
-	return skb;
 }
 
 static u32
 bnad_cq_process(struct bnad *bnad, struct bna_ccb *ccb, int budget)
 {
-	struct bna_cq_entry *cq, *cmpl;
+	struct bna_cq_entry *cq, *cmpl, *next_cmpl;
 	struct bna_rcb *rcb = NULL;
 	struct bnad_rx_unmap_q *unmap_q;
-	struct bnad_rx_unmap *unmap;
-	struct sk_buff *skb;
+	struct bnad_rx_unmap *unmap = NULL;
+	struct sk_buff *skb = NULL;
 	struct bna_pkt_rate *pkt_rt = &ccb->pkt_rate;
 	struct bnad_rx_ctrl *rx_ctrl = ccb->ctrl;
-	u32 packets = 0, length = 0, flags, masked_flags;
+	u32 packets = 0, len = 0, totlen = 0;
+	u32 pi, vec, sop_ci = 0, nvecs = 0;
+	u32 flags, masked_flags;
 
 	prefetch(bnad->netdev);
 
@@ -563,9 +601,6 @@ bnad_cq_process(struct bnad *bnad, struct bna_ccb *ccb, int budget)
 	cmpl = &cq[ccb->producer_index];
 
 	while (cmpl->valid && (packets < budget)) {
-		packets++;
-		flags = ntohl(cmpl->flags);
-		length = ntohs(cmpl->length);
 		BNA_UPDATE_PKT_CNT(pkt_rt, ntohs(cmpl->length));
 
 		if (bna_is_small_rxq(cmpl->rxq_id))
@@ -574,25 +609,68 @@ bnad_cq_process(struct bnad *bnad, struct bna_ccb *ccb, int budget)
 			rcb = ccb->rcb[0];
 
 		unmap_q = rcb->unmap_q;
-		unmap = &unmap_q->unmap[rcb->consumer_index];
 
-		if (unlikely(flags & (BNA_CQ_EF_MAC_ERROR |
-					BNA_CQ_EF_FCS_ERROR |
-					BNA_CQ_EF_TOO_LONG))) {
-			if (BNAD_RXBUF_IS_PAGE(unmap_q->type))
-				bnad_rxq_cleanup_page(bnad, unmap);
-			else
-				bnad_rxq_cleanup_skb(bnad, unmap);
+		/* start of packet ci */
+		sop_ci = rcb->consumer_index;
+
+		if (BNAD_RXBUF_IS_SK_BUFF(unmap_q->type)) {
+			unmap = &unmap_q->unmap[sop_ci];
+			skb = unmap->skb;
+		} else {
+			skb = napi_get_frags(&rx_ctrl->napi);
+			if (unlikely(!skb))
+				break;
+		}
+		prefetch(skb);
 
+		flags = ntohl(cmpl->flags);
+		totlen = len = ntohs(cmpl->length);
+		nvecs = 1;
+
+		/* Check all the completions for this frame.
+		 * busy-wait doesn't help much, break here.
+		 */
+		if (BNAD_RXBUF_IS_MULTI_BUFF(unmap_q->type) &&
+				(flags & BNA_CQ_EF_EOP) == 0) {
+
+			pi = ccb->producer_index;
+			do {
+				BNA_QE_INDX_INC(pi, ccb->q_depth);
+				next_cmpl = &cq[pi];
+
+				if (!next_cmpl->valid)
+					break;
+
+				len = ntohs(next_cmpl->length);
+				flags = ntohl(next_cmpl->flags);
+
+				nvecs++;
+				totlen += len;
+			} while ((flags & BNA_CQ_EF_EOP) == 0);
+
+			if (!next_cmpl->valid)
+				break;
+		}
+
+		/* TODO: BNA_CQ_EF_LOCAL ? */
+		if (unlikely(flags & (BNA_CQ_EF_MAC_ERROR |
+						BNA_CQ_EF_FCS_ERROR |
+						BNA_CQ_EF_TOO_LONG))) {
+			bnad_cq_drop_packet(bnad, rcb, sop_ci, nvecs);
 			rcb->rxq->rx_packets_with_error++;
+
 			goto next;
 		}
 
-		skb = bnad_cq_prepare_skb(ccb->ctrl, unmap_q, unmap,
-				length, flags);
+		if (BNAD_RXBUF_IS_SK_BUFF(unmap_q->type))
+			bnad_cq_setup_skb(bnad, skb, unmap, len);
+		else
+			bnad_cq_setup_skb_frags(rcb, skb, sop_ci, nvecs, len);
 
-		if (unlikely(!skb))
-			break;
+		packets++;
+		rcb->rxq->rx_packets++;
+		rcb->rxq->rx_bytes += totlen;
+		ccb->bytes_per_intr += totlen;
 
 		masked_flags = flags & flags_cksum_prot_mask;
 
@@ -606,21 +684,21 @@ bnad_cq_process(struct bnad *bnad, struct bna_ccb *ccb, int budget)
 		else
 			skb_checksum_none_assert(skb);
 
-		rcb->rxq->rx_packets++;
-		rcb->rxq->rx_bytes += length;
-
 		if (flags & BNA_CQ_EF_VLAN)
 			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(cmpl->vlan_tag));
 
-		if (BNAD_RXBUF_IS_PAGE(unmap_q->type))
-			napi_gro_frags(&rx_ctrl->napi);
-		else
+		if (BNAD_RXBUF_IS_SK_BUFF(unmap_q->type))
 			netif_receive_skb(skb);
+		else
+			napi_gro_frags(&rx_ctrl->napi);
 
 next:
-		cmpl->valid = 0;
-		BNA_QE_INDX_INC(rcb->consumer_index, rcb->q_depth);
-		BNA_QE_INDX_INC(ccb->producer_index, ccb->q_depth);
+		BNA_QE_INDX_ADD(rcb->consumer_index, nvecs, rcb->q_depth);
+		for (vec = 0; vec < nvecs; vec++) {
+			cmpl = &cq[ccb->producer_index];
+			cmpl->valid = 0;
+			BNA_QE_INDX_INC(ccb->producer_index, ccb->q_depth);
+		}
 		cmpl = &cq[ccb->producer_index];
 	}
 
@@ -1950,10 +2028,38 @@ bnad_init_rx_config(struct bnad *bnad, struct bna_rx_config *rx_config)
 		memset(&rx_config->rss_config, 0,
 		       sizeof(rx_config->rss_config));
 	}
+
+	rx_config->frame_size = BNAD_FRAME_SIZE(bnad->netdev->mtu);
+
+	/* BNA_RXP_SINGLE - one data-buffer queue
+	 * BNA_RXP_SLR - one small-buffer and one large-buffer queues
+	 * BNA_RXP_HDS - one header-buffer and one data-buffer queues
+	 */
+	/* TODO: configurable param for queue type */
 	rx_config->rxp_type = BNA_RXP_SLR;
-	rx_config->q_depth = bnad->rxq_depth;
 
-	rx_config->small_buff_size = BFI_SMALL_RXBUF_SIZE;
+	if (BNAD_PCI_DEV_IS_CAT2(bnad) &&
+			bnad_enable_multi_buffer(rx_config->frame_size)) {
+		/* though size_routing_enable is set in SLR,
+		 * small packets may get routed to same rxq.
+		 * set buf_size to 2048 instead of PAGE_SIZE.
+		 */
+		rx_config->q0_buf_size = 2048;
+		/* this should be in multiples of 2 */
+		rx_config->q0_num_vecs = 4;
+		rx_config->q0_depth = bnad->rxq_depth * rx_config->q0_num_vecs;
+		rx_config->q0_multi_buf = BNA_STATUS_T_ENABLED;
+	} else {
+		rx_config->q0_buf_size = rx_config->frame_size;
+		rx_config->q0_num_vecs = 1;
+		rx_config->q0_depth = bnad->rxq_depth;
+	}
+
+	/* initialize for q1 for BNA_RXP_SLR/BNA_RXP_HDS */
+	if (rx_config->rxp_type == BNA_RXP_SLR) {
+		rx_config->q1_depth = bnad->rxq_depth;
+		rx_config->q1_buf_size = BFI_SMALL_RXBUF_SIZE;
+	}
 
 	rx_config->vlan_strip_status = BNA_STATUS_T_ENABLED;
 }
@@ -1969,6 +2075,41 @@ bnad_rx_ctrl_init(struct bnad *bnad, u32 rx_id)
 }
 
 /* Called with mutex_lock(&bnad->conf_mutex) held */
+u32
+bnad_reinit_rx(struct bnad *bnad)
+{
+	struct net_device *netdev = bnad->netdev;
+	u32 err = 0, current_err = 0;
+	u32 rx_id = 0, count = 0;
+	unsigned long flags;
+
+	/* destroy and create new rx objects */
+	for (rx_id = 0; rx_id < bnad->num_rx; rx_id++) {
+		if (!bnad->rx_info[rx_id].rx)
+			continue;
+		count++;
+		bnad_destroy_rx(bnad, rx_id);
+		current_err = bnad_setup_rx(bnad, rx_id);
+		if (current_err && !err) {
+			err = current_err;
+			pr_err("RXQ:%u setup failed\n", rx_id);
+		}
+	}
+
+	/* restore rx configuration */
+	if (bnad->rx_info[0].rx && !err) {
+		bnad_restore_vlans(bnad, 0);
+		bnad_enable_default_bcast(bnad);
+		spin_lock_irqsave(&bnad->bna_lock, flags);
+		bnad_mac_addr_set_locked(bnad, netdev->dev_addr);
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+		bnad_set_rx_mode(netdev);
+	}
+
+	return count;
+}
+
+/* Called with bnad_conf_lock() held */
 void
 bnad_destroy_rx(struct bnad *bnad, u32 rx_id)
 {
@@ -2047,13 +2188,19 @@ bnad_setup_rx(struct bnad *bnad, u32 rx_id)
 	spin_unlock_irqrestore(&bnad->bna_lock, flags);
 
 	/* Fill Unmap Q memory requirements */
-	BNAD_FILL_UNMAPQ_MEM_REQ(&res_info[BNA_RX_RES_MEM_T_UNMAPQ],
-			rx_config->num_paths +
-			((rx_config->rxp_type == BNA_RXP_SINGLE) ?
-			 0 : rx_config->num_paths),
-			((bnad->rxq_depth * sizeof(struct bnad_rx_unmap)) +
-			 sizeof(struct bnad_rx_unmap_q)));
-
+	BNAD_FILL_UNMAPQ_MEM_REQ(&res_info[BNA_RX_RES_MEM_T_UNMAPDQ],
+			rx_config->num_paths,
+			(rx_config->q0_depth *
+			 sizeof(struct bnad_rx_unmap)) +
+			 sizeof(struct bnad_rx_unmap_q));
+
+	if (rx_config->rxp_type != BNA_RXP_SINGLE) {
+		BNAD_FILL_UNMAPQ_MEM_REQ(&res_info[BNA_RX_RES_MEM_T_UNMAPHQ],
+				rx_config->num_paths,
+				(rx_config->q1_depth *
+				 sizeof(struct bnad_rx_unmap) +
+				 sizeof(struct bnad_rx_unmap_q)));
+	}
 	/* Allocate resource */
 	err = bnad_rx_res_alloc(bnad, res_info, rx_id);
 	if (err)
@@ -2548,7 +2695,6 @@ bnad_open(struct net_device *netdev)
 	int err;
 	struct bnad *bnad = netdev_priv(netdev);
 	struct bna_pause_config pause_config;
-	int mtu;
 	unsigned long flags;
 
 	mutex_lock(&bnad->conf_mutex);
@@ -2567,10 +2713,9 @@ bnad_open(struct net_device *netdev)
 	pause_config.tx_pause = 0;
 	pause_config.rx_pause = 0;
 
-	mtu = ETH_HLEN + VLAN_HLEN + bnad->netdev->mtu + ETH_FCS_LEN;
-
 	spin_lock_irqsave(&bnad->bna_lock, flags);
-	bna_enet_mtu_set(&bnad->bna.enet, mtu, NULL);
+	bna_enet_mtu_set(&bnad->bna.enet,
+			BNAD_FRAME_SIZE(bnad->netdev->mtu), NULL);
 	bna_enet_pause_config(&bnad->bna.enet, &pause_config, NULL);
 	bna_enet_enable(&bnad->bna.enet);
 	spin_unlock_irqrestore(&bnad->bna_lock, flags);
@@ -3092,14 +3237,14 @@ bnad_set_mac_address(struct net_device *netdev, void *mac_addr)
 }
 
 static int
-bnad_mtu_set(struct bnad *bnad, int mtu)
+bnad_mtu_set(struct bnad *bnad, int frame_size)
 {
 	unsigned long flags;
 
 	init_completion(&bnad->bnad_completions.mtu_comp);
 
 	spin_lock_irqsave(&bnad->bna_lock, flags);
-	bna_enet_mtu_set(&bnad->bna.enet, mtu, bnad_cb_enet_mtu_set);
+	bna_enet_mtu_set(&bnad->bna.enet, frame_size, bnad_cb_enet_mtu_set);
 	spin_unlock_irqrestore(&bnad->bna_lock, flags);
 
 	wait_for_completion(&bnad->bnad_completions.mtu_comp);
@@ -3110,18 +3255,27 @@ bnad_mtu_set(struct bnad *bnad, int mtu)
 static int
 bnad_change_mtu(struct net_device *netdev, int new_mtu)
 {
-	int err, mtu = netdev->mtu;
+	int err, mtu;
 	struct bnad *bnad = netdev_priv(netdev);
+	u32 rx_count = 0;
 
 	if (new_mtu + ETH_HLEN < ETH_ZLEN || new_mtu > BNAD_JUMBO_MTU)
 		return -EINVAL;
 
 	mutex_lock(&bnad->conf_mutex);
 
+	mtu = netdev->mtu;
 	netdev->mtu = new_mtu;
 
-	mtu = ETH_HLEN + VLAN_HLEN + new_mtu + ETH_FCS_LEN;
-	err = bnad_mtu_set(bnad, mtu);
+	/* check if multi-buffer needs to be enabled */
+	if (BNAD_PCI_DEV_IS_CAT2(bnad) &&
+			bnad_reinit_rx_needed(BNAD_FRAME_SIZE(new_mtu)))
+		rx_count = bnad_reinit_rx(bnad);
+
+	/* rx_count > 0 - new rx created
+	 *	- Linux set err = 0 and return
+	 */
+	err = bnad_mtu_set(bnad, new_mtu);
 	if (err)
 		err = -EBUSY;
 
diff --git a/drivers/net/ethernet/brocade/bna/bnad.h b/drivers/net/ethernet/brocade/bna/bnad.h
index 7055432..03de9cc 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.h
+++ b/drivers/net/ethernet/brocade/bna/bnad.h
@@ -105,6 +105,9 @@ struct bnad_rx_ctrl {
 #define BNAD_NUM_TXQ			(bnad->num_tx * bnad->num_txq_per_tx)
 #define BNAD_NUM_RXP			(bnad->num_rx * bnad->num_rxp_per_rx)
 
+#define BNAD_FRAME_SIZE(_mtu) \
+	(ETH_HLEN + VLAN_HLEN + (_mtu) + ETH_FCS_LEN)
+
 /*
  * DATA STRUCTURES
  */
@@ -241,12 +244,13 @@ struct bnad_rx_unmap {
 
 enum bnad_rxbuf_type {
 	BNAD_RXBUF_NONE		= 0,
-	BNAD_RXBUF_SKB		= 1,
+	BNAD_RXBUF_SK_BUFF	= 1,
 	BNAD_RXBUF_PAGE		= 2,
-	BNAD_RXBUF_MULTI	= 3
+	BNAD_RXBUF_MULTI_BUFF	= 3
 };
 
-#define BNAD_RXBUF_IS_PAGE(_type)	((_type) == BNAD_RXBUF_PAGE)
+#define BNAD_RXBUF_IS_SK_BUFF(_type)	((_type) == BNAD_RXBUF_SK_BUFF)
+#define BNAD_RXBUF_IS_MULTI_BUFF(_type)	((_type) == BNAD_RXBUF_MULTI_BUFF)
 
 struct bnad_rx_unmap_q {
 	int			reuse_pi;
@@ -256,6 +260,15 @@ struct bnad_rx_unmap_q {
 	struct bnad_rx_unmap	unmap[0];
 };
 
+#define BNAD_PCI_DEV_IS_CAT2(_bnad) \
+	((_bnad)->pcidev->device == BFA_PCI_DEVICE_ID_CT2)
+
+#define bnad_enable_multi_buffer(_mtu) \
+		bnad_reinit_rx_needed((_mtu))
+
+#define bnad_reinit_rx_needed(_frame) \
+	(bnad_multi_buffer_rx && (_frame) > 4096)
+
 /* Bit mask values for bnad->cfg_flags */
 #define	BNAD_CF_DIM_ENABLED		0x01	/* DIM */
 #define	BNAD_CF_PROMISC			0x02
-- 
1.8.2.3

^ permalink raw reply related

* [net-next 08/13] bna: CQ Read Fix
From: Rasesh Mody @ 2013-11-21  5:54 UTC (permalink / raw)
  To: davem; +Cc: netdev, adapter_linux_open_src_team, Rasesh Mody
In-Reply-To: <1385013257-32745-1-git-send-email-rmody@brocade.com>

Valid bit check for completion needs read fence so that it does not get
reordered with other loads.

Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 drivers/net/ethernet/brocade/bna/bnad.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index 6ae1d9a..cfdcfb6 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -605,7 +605,11 @@ bnad_cq_process(struct bnad *bnad, struct bna_ccb *ccb, int budget)
 	cq = ccb->sw_q;
 	cmpl = &cq[ccb->producer_index];
 
-	while (cmpl->valid && (packets < budget)) {
+	while (packets < budget) {
+		if (!cmpl->valid)
+			break;
+		rmb();
+
 		BNA_UPDATE_PKT_CNT(pkt_rt, ntohs(cmpl->length));
 
 		if (bna_is_small_rxq(cmpl->rxq_id))
@@ -645,6 +649,7 @@ bnad_cq_process(struct bnad *bnad, struct bna_ccb *ccb, int budget)
 
 				if (!next_cmpl->valid)
 					break;
+				mb();
 
 				len = ntohs(next_cmpl->length);
 				flags = ntohl(next_cmpl->flags);
-- 
1.8.2.3

^ permalink raw reply related

* Re: Issue with gratuitous arps when new addr is different from cached addr
From: Hannes Frederic Sowa @ 2013-11-21  6:26 UTC (permalink / raw)
  To: Salam Noureddine, David S. Miller, Daniel Borkmann,
	Willem de Bruijn, Phil Sutter, Eric Dumazet, netdev
In-Reply-To: <20131121062349.GC4347@order.stressinduktion.org>

On Thu, Nov 21, 2013 at 07:23:49AM +0100, Hannes Frederic Sowa wrote:
> On Wed, Nov 20, 2013 at 10:06:34PM -0800, Salam Noureddine wrote:
> > Isn't locktime useful in that case for limiting the rate of garp
> > changes to the arp cache?
> 
> Yes, as I said, it would be nice to have rate-limiting for those, too.
> 
> GARP is used in cluster setups to make the switch-over as fast as possible and
> I don't think they accept those lock-down delays, so I guess it would be nice
> to forceful override the lladdr if (sip == tip) && ACCEPT_ARP(dev) is true in
> every case.
> 
> I guess you are not testing with ACCEPT_ARP?
> 
> In that case I am not sure what to do.
> 
> override = (...timing...) || sip == tip; could work but does relax the
> protection of the neigh cache.
> 
> In IPv6 we have a flag in the packet if we should overwrite the entry.
> 
> I'll have to think about that a bit more. Could well be the case that we need
> your proposal, too. But then we would have to validate the change with IPv6,
> too and neighbour cache states are really complex.

Hmm, could it help to bring the neighbor to suspect state (== WEAK_OVERRIDE)?

^ permalink raw reply

* Re: [net-next 12/13] bna: Firmware Patch Simplification
From: Joe Perches @ 2013-11-21  6:26 UTC (permalink / raw)
  To: Rasesh Mody; +Cc: davem, netdev, adapter_linux_open_src_team
In-Reply-To: <1385013257-32745-13-git-send-email-rmody@brocade.com>

On Wed, 2013-11-20 at 21:54 -0800, Rasesh Mody wrote:
> This patch includes change to enable firmware patch simplication feature.
> This feature is targeted to address the requirement to have independent patch
> release for firmware. Prior to the 3.2.3.0 firmware, releasing a patch fix for
> firmware would require changes to bna driver, to use new firmware images.
> However with these changes, if the new firmware is flashed on to the Adapter,
> the driver will use the new firmware after checking the patch release byte in
> the firmware version.

Please run your patches through checkpatch --strict

> +/* flash command register data structure */
> +union bfa_flash_cmd_reg {
> +	struct {
> +#ifdef __BIGENDIAN

__BIG_ENDIAN?

> +/* flash device status register data structure */
> +union bfa_flash_dev_status_reg {
> +	struct {
> +#ifdef __BIGENDIAN

__BIG_ENDIAN here too?

> +/* flash address register data structure */
> +union bfa_flash_addr_reg {
> +	struct {
> +#ifdef __BIGENDIAN

and here

^ permalink raw reply

* [net-next 01/13] bna: Add software PTP support
From: Rasesh Mody @ 2013-11-21  5:54 UTC (permalink / raw)
  To: davem; +Cc: netdev, adapter_linux_open_src_team, Rasesh Mody
In-Reply-To: <1385013257-32745-1-git-send-email-rmody@brocade.com>

 - Invoke skb_tx_timestamp() API just before invoking txq_doorbell()
 - Add ethtool (-T) support

Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 drivers/net/ethernet/brocade/bna/bnad.c         | 2 ++
 drivers/net/ethernet/brocade/bna/bnad_ethtool.c | 1 +
 2 files changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index f276433..9b1c550 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -2911,6 +2911,8 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
 	if (unlikely(!test_bit(BNAD_TXQ_TX_STARTED, &tcb->flags)))
 		return NETDEV_TX_OK;
 
+	skb_tx_timestamp(skb);
+
 	bna_txq_prod_indx_doorbell(tcb);
 	smp_mb();
 
diff --git a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c
index 455b5a2..f9e1508 100644
--- a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c
+++ b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c
@@ -1131,6 +1131,7 @@ static const struct ethtool_ops bnad_ethtool_ops = {
 	.get_eeprom = bnad_get_eeprom,
 	.set_eeprom = bnad_set_eeprom,
 	.flash_device = bnad_flash_device,
+	.get_ts_info = ethtool_op_get_ts_info,
 };
 
 void
-- 
1.8.2.3

^ permalink raw reply related

* Re: Issue with gratuitous arps when new addr is different from cached addr
From: Hannes Frederic Sowa @ 2013-11-21  6:23 UTC (permalink / raw)
  To: Salam Noureddine
  Cc: David S. Miller, Daniel Borkmann, Willem de Bruijn, Phil Sutter,
	Eric Dumazet, netdev
In-Reply-To: <CAO7SqHC4astdejH6wVAyjsVeuh96T8P3ao3bCDNs=opfv5bbUg@mail.gmail.com>

On Wed, Nov 20, 2013 at 10:06:34PM -0800, Salam Noureddine wrote:
> Isn't locktime useful in that case for limiting the rate of garp
> changes to the arp cache?

Yes, as I said, it would be nice to have rate-limiting for those, too.

GARP is used in cluster setups to make the switch-over as fast as possible and
I don't think they accept those lock-down delays, so I guess it would be nice
to forceful override the lladdr if (sip == tip) && ACCEPT_ARP(dev) is true in
every case.

I guess you are not testing with ACCEPT_ARP?

In that case I am not sure what to do.

override = (...timing...) || sip == tip; could work but does relax the
protection of the neigh cache.

In IPv6 we have a flag in the packet if we should overwrite the entry.

I'll have to think about that a bit more. Could well be the case that we need
your proposal, too. But then we would have to validate the change with IPv6,
too and neighbour cache states are really complex.

Greetings,

  Hannes

^ permalink raw reply

* [net-next 10/13] bna: Handle the TX Setup Failures
From: Rasesh Mody @ 2013-11-21  5:54 UTC (permalink / raw)
  To: davem; +Cc: netdev, adapter_linux_open_src_team, Rasesh Mody
In-Reply-To: <1385013257-32745-1-git-send-email-rmody@brocade.com>

Change details:
 - When bnad_setup_tx() returns NULL, the error is NOT returned to the caller.
   The caller will incorrectly assume success. So Return ENOMEM when bna_tx_create()
   fails.
 - If bnad_tx_msix_register() fails, call bna_tx_destroy() to free tx & to NULL
   the bnad reference to tcb.

Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 drivers/net/ethernet/brocade/bna/bnad.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index 41689ae..797d0f2 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -1987,8 +1987,10 @@ bnad_setup_tx(struct bnad *bnad, u32 tx_id)
 	tx = bna_tx_create(&bnad->bna, bnad, tx_config, &tx_cbfn, res_info,
 			tx_info);
 	spin_unlock_irqrestore(&bnad->bna_lock, flags);
-	if (!tx)
+	if (!tx) {
+		err = -ENOMEM;
 		goto err_return;
+	}
 	tx_info->tx = tx;
 
 	INIT_DELAYED_WORK(&tx_info->tx_cleanup_work,
@@ -1999,7 +2001,7 @@ bnad_setup_tx(struct bnad *bnad, u32 tx_id)
 		err = bnad_tx_msix_register(bnad, tx_info,
 			tx_id, bnad->num_txq_per_tx);
 		if (err)
-			goto err_return;
+			goto cleanup_tx;
 	}
 
 	spin_lock_irqsave(&bnad->bna_lock, flags);
@@ -2008,6 +2010,12 @@ bnad_setup_tx(struct bnad *bnad, u32 tx_id)
 
 	return 0;
 
+cleanup_tx:
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_tx_destroy(tx_info->tx);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	tx_info->tx = NULL;
+	tx_info->tx_id = 0;
 err_return:
 	bnad_tx_res_free(bnad, res_info);
 	return err;
-- 
1.8.2.3

^ permalink raw reply related

* [net-next 06/13] bna: UDP RX Processing Improvements
From: Rasesh Mody @ 2013-11-21  5:54 UTC (permalink / raw)
  To: davem; +Cc: netdev, adapter_linux_open_src_team, Rasesh Mody
In-Reply-To: <1385013257-32745-1-git-send-email-rmody@brocade.com>

Change Details:
 - Prefetch header in GRO path. This reduces napi_frags_skb time from 9% to 5%.
 - Changed the configurable limit of RxQ depth to 16384 (was 2048).
 - Rx unmap structure of 40 bytes is packed to 32 bytes reducing unmap_q size by 16K.
 - bnad_rx_unmap_q elements are cachealigned.

Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 drivers/net/ethernet/brocade/bna/bnad.c | 5 +++++
 drivers/net/ethernet/brocade/bna/bnad.h | 8 ++++----
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index 8dfd783..e8c1900 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -540,6 +540,11 @@ bnad_cq_setup_skb_frags(struct bna_rcb *rcb, struct sk_buff *skb,
 
 	unmap_q = rcb->unmap_q;
 	bnad = rcb->bnad;
+
+	/* prefetch header */
+	prefetch(page_address(unmap_q->unmap[sop_ci].page) +
+			unmap_q->unmap[sop_ci].page_offset);
+
 	for (vec = 1, ci = sop_ci; vec <= nvecs; vec++) {
 		unmap = &unmap_q->unmap[ci];
 		BNA_QE_INDX_INC(ci, rcb->q_depth);
diff --git a/drivers/net/ethernet/brocade/bna/bnad.h b/drivers/net/ethernet/brocade/bna/bnad.h
index 03de9cc..d1dc930 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.h
+++ b/drivers/net/ethernet/brocade/bna/bnad.h
@@ -84,7 +84,7 @@ struct bnad_rx_ctrl {
 #define BNAD_IOCETH_TIMEOUT	     10000
 
 #define BNAD_MIN_Q_DEPTH		512
-#define BNAD_MAX_RXQ_DEPTH		2048
+#define BNAD_MAX_RXQ_DEPTH		16384
 #define BNAD_MAX_TXQ_DEPTH		2048
 
 #define BNAD_JUMBO_MTU			9000
@@ -233,13 +233,13 @@ struct bnad_tx_unmap {
 struct bnad_rx_vector {
 	DEFINE_DMA_UNMAP_ADDR(dma_addr);
 	u32			len;
-};
+} __packed;
 
 struct bnad_rx_unmap {
 	struct page		*page;
-	u32			page_offset;
 	struct sk_buff		*skb;
 	struct bnad_rx_vector	vector;
+	u32			page_offset;
 };
 
 enum bnad_rxbuf_type {
@@ -257,7 +257,7 @@ struct bnad_rx_unmap_q {
 	int			alloc_order;
 	u32			map_size;
 	enum bnad_rxbuf_type	type;
-	struct bnad_rx_unmap	unmap[0];
+	struct bnad_rx_unmap	unmap[0] ____cacheline_aligned;
 };
 
 #define BNAD_PCI_DEV_IS_CAT2(_bnad) \
-- 
1.8.2.3

^ permalink raw reply related

* [net-next 09/13] bna: Add NULL Check Before Dereferencing TCB
From: Rasesh Mody @ 2013-11-21  5:54 UTC (permalink / raw)
  To: davem; +Cc: netdev, adapter_linux_open_src_team, Rasesh Mody
In-Reply-To: <1385013257-32745-1-git-send-email-rmody@brocade.com>

Currently we already check to see whether the BNAD_TXQ_TX_STARTED cleared.
But if the tcb structure which contains this flag is also already freed by that
time, we would dereference the NULL pointer. This patch is to check tcb for NULL
pointer, before dereferencing it.

Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 drivers/net/ethernet/brocade/bna/bnad.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index cfdcfb6..41689ae 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -2947,21 +2947,21 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
 	}
 
 	tcb = bnad->tx_info[0].tcb[txq_id];
-	q_depth = tcb->q_depth;
-	prod = tcb->producer_index;
-
-	unmap_q = tcb->unmap_q;
 
 	/*
 	 * Takes care of the Tx that is scheduled between clearing the flag
 	 * and the netif_tx_stop_all_queues() call.
 	 */
-	if (unlikely(!test_bit(BNAD_TXQ_TX_STARTED, &tcb->flags))) {
+	if (unlikely(!tcb || !test_bit(BNAD_TXQ_TX_STARTED, &tcb->flags))) {
 		dev_kfree_skb(skb);
 		BNAD_UPDATE_CTR(bnad, tx_skb_stopping);
 		return NETDEV_TX_OK;
 	}
 
+	q_depth = tcb->q_depth;
+	prod = tcb->producer_index;
+	unmap_q = tcb->unmap_q;
+
 	vectors = 1 + skb_shinfo(skb)->nr_frags;
 	wis = BNA_TXQ_WI_NEEDED(vectors);	/* 4 vectors per work item */
 
-- 
1.8.2.3

^ permalink raw reply related

* [net-next 00/13] bna: Update the Driver to v3.2.23.0
From: Rasesh Mody @ 2013-11-21  5:54 UTC (permalink / raw)
  To: davem; +Cc: netdev, adapter_linux_open_src_team, Rasesh Mody

Hi Dave,

This patch set consists of feature additions like s/w ptp support, multi-buffer
RX, firmware patch simplification, enhancements for RX filters, RX processing,
Bug fixes and updates the firmware version to v3.2.3.0.

This patch set updates the BNA driver to v3.2.23.0 and was tested against
net-next 3.12.0-rc6 kernel.

Thanks
Rasesh

Rasesh Mody (13):
  bna: Add software PTP support
  bna: Set Get IOC fw State
  bna: Fix Filter Add Del
  bna: RX Filter Enhancements
  bna: Enable Multi Buffer RX
  bna: UDP RX Processing Improvements
  bna: Fix Stale MTU used to Configure Multi-buffer RX
  bna: CQ Read Fix
  bna: Add NULL Check Before Dereferencing TCB
  bna: Handle the TX Setup Failures
  bna: Embed SKB Length in TX Vector
  bna: Firmware Patch Simplification
  bna: Update the Driver Version to 3.2.23.0

 drivers/net/ethernet/brocade/bna/bfa_ioc.c      | 653 ++++++++++++++++++++++--
 drivers/net/ethernet/brocade/bna/bfa_ioc.h      |   8 +
 drivers/net/ethernet/brocade/bna/bfa_ioc_ct.c   |  40 ++
 drivers/net/ethernet/brocade/bna/bfi.h          |  33 +-
 drivers/net/ethernet/brocade/bna/bfi_enet.h     |   3 +-
 drivers/net/ethernet/brocade/bna/bna.h          |  24 +-
 drivers/net/ethernet/brocade/bna/bna_enet.c     |  58 ++-
 drivers/net/ethernet/brocade/bna/bna_hw_defs.h  |   4 +
 drivers/net/ethernet/brocade/bna/bna_tx_rx.c    | 249 +++++++--
 drivers/net/ethernet/brocade/bna/bna_types.h    |  57 ++-
 drivers/net/ethernet/brocade/bna/bnad.c         | 543 ++++++++++++++------
 drivers/net/ethernet/brocade/bna/bnad.h         |  29 +-
 drivers/net/ethernet/brocade/bna/bnad_ethtool.c |   1 +
 drivers/net/ethernet/brocade/bna/cna.h          |   4 +-
 14 files changed, 1414 insertions(+), 292 deletions(-)

-- 
1.8.2.3

^ 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