Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH] bnx2: turn off the network statck during initialization
From: Michael Chan @ 2012-08-16 18:57 UTC (permalink / raw)
  To: Jiang Wang; +Cc: netdev, linux-kernel, clala, Francis.St.Amant, Jiang Wang
In-Reply-To: <1345141295-7589-1-git-send-email-Jiang.Wang@riverbed.com>

On Thu, 2012-08-16 at 11:21 -0700, Jiang Wang wrote: 
> The initialization state of bnx2 driver is wrong. It does not turn
> of the Linux network stack using netif_carrier_off. This may lead to
> inconsistent report from ethtool as the link is up but speed is
> unknown when the cable is not plugged in.
> 
> E.g.
>         Speed: Unknown! (0)<--------------------------------------
>         Duplex: Half       <--------------------------------------
>         MDI: Unknown! (0)
>         Port: Twisted Pair
>         PHYAD: 1
>         Transceiver: internal
>         Auto-negotiation: on
>         Supports Wake-on: g
>         Wake-on: d
>         Link detected: yes <---------------------------------------
> 
> This patches fixed the problem by turning off the network stack
> during initialization.
> 
> Signed-off-by: Jiang Wang <jwang@riverbed.com>
> ---
>  drivers/net/ethernet/broadcom/bnx2.c |    4 ++++
>  1 files changed, 4 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c
> index ac7b744..ce4548d 100644
> --- a/drivers/net/ethernet/broadcom/bnx2.c
> +++ b/drivers/net/ethernet/broadcom/bnx2.c
> @@ -8463,6 +8463,10 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>  	dev->features |= dev->hw_features;
>  	dev->priv_flags |= IFF_UNICAST_FLT;
>  
> +	/* tell the stack to leave us alone until bnx2_open() is called */
> +	netif_carrier_off(dev);

We have tried this before and this didn't work.  netif_carrier_off()
calls linkwatch_fire_event() to schedule the event.  If
register_netdev() fails for whatever reason, the netdev will be freed
but the link_watch event may still be scheduled.

Calling netif_carrier_off() after register_netdev() returns successfully
also may not work as it will race with bnx2_open() which enables IRQ.
An linkup IRQ can happen at time and we may end up with the wrong
carrier state because of the race condition.

> +	netif_stop_queue(dev);
> +
>  	if ((rc = register_netdev(dev))) {
>  		dev_err(&pdev->dev, "Cannot register net device\n");
>  		goto error;

^ permalink raw reply

* Re: [PATCH net-next] sctp: fix compile issue with disabled CONFIG_NET_NS
From: Eric W. Biederman @ 2012-08-16 18:58 UTC (permalink / raw)
  To: Ulrich Weber; +Cc: davem, netdev
In-Reply-To: <20120816112449.GA17354@uweber-WS>

Ulrich Weber <ulrich.weber@sophos.com> writes:

> struct seq_net_private has no struct net
> if CONFIG_NET_NS is not enabled

My mistake.

Reviewed-by: "Eric W. Biederman" <ebiederm@xmission.com>

> Signed-off-by: Ulrich Weber <ulrich.weber@sophos.com>

Ulrich do you get any performance advantage by disabling the network
namespace?

I am wondering if there is any benefit to keeping it possible to disable
the network namespace?

The original reason for the option was so that distributions and
other who wanted to avoid new code could protect their users, and
that reasons seems to have long since passed.

> ---
>  net/sctp/proc.c |    9 +++------
>  1 file changed, 3 insertions(+), 6 deletions(-)
>
> diff --git a/net/sctp/proc.c b/net/sctp/proc.c
> index 3e62ee5..d9cb2ab 100644
> --- a/net/sctp/proc.c
> +++ b/net/sctp/proc.c
> @@ -199,7 +199,6 @@ static void * sctp_eps_seq_next(struct seq_file *seq, void *v, loff_t *pos)
>  /* Display sctp endpoints (/proc/net/sctp/eps). */
>  static int sctp_eps_seq_show(struct seq_file *seq, void *v)
>  {
> -	struct seq_net_private *priv = seq->private;
>  	struct sctp_hashbucket *head;
>  	struct sctp_ep_common *epb;
>  	struct sctp_endpoint *ep;
> @@ -216,7 +215,7 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v)
>  	sctp_for_each_hentry(epb, node, &head->chain) {
>  		ep = sctp_ep(epb);
>  		sk = epb->sk;
> -		if (!net_eq(sock_net(sk), priv->net))
> +		if (!net_eq(sock_net(sk), seq_file_net(seq)))
>  			continue;
>  		seq_printf(seq, "%8pK %8pK %-3d %-3d %-4d %-5d %5d %5lu ", ep, sk,
>  			   sctp_sk(sk)->type, sk->sk_state, hash,
> @@ -307,7 +306,6 @@ static void * sctp_assocs_seq_next(struct seq_file *seq, void *v, loff_t *pos)
>  /* Display sctp associations (/proc/net/sctp/assocs). */
>  static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
>  {
> -	struct seq_net_private *priv = seq->private;
>  	struct sctp_hashbucket *head;
>  	struct sctp_ep_common *epb;
>  	struct sctp_association *assoc;
> @@ -324,7 +322,7 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
>  	sctp_for_each_hentry(epb, node, &head->chain) {
>  		assoc = sctp_assoc(epb);
>  		sk = epb->sk;
> -		if (!net_eq(sock_net(sk), priv->net))
> +		if (!net_eq(sock_net(sk), seq_file_net(seq)))
>  			continue;
>  		seq_printf(seq,
>  			   "%8pK %8pK %-3d %-3d %-2d %-4d "
> @@ -423,7 +421,6 @@ static void sctp_remaddr_seq_stop(struct seq_file *seq, void *v)
>  
>  static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
>  {
> -	struct seq_net_private *priv = seq->private;
>  	struct sctp_hashbucket *head;
>  	struct sctp_ep_common *epb;
>  	struct sctp_association *assoc;
> @@ -438,7 +435,7 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
>  	sctp_local_bh_disable();
>  	read_lock(&head->lock);
>  	sctp_for_each_hentry(epb, node, &head->chain) {
> -		if (!net_eq(sock_net(epb->sk), priv->net))
> +		if (!net_eq(sock_net(epb->sk), seq_file_net(seq)))
>  			continue;
>  		assoc = sctp_assoc(epb);
>  		list_for_each_entry(tsp, &assoc->peer.transport_addr_list,

^ permalink raw reply

* Re: [PATCH v0 0/5] cgroup: Assign subsystem IDs during compile time
From: Neil Horman @ 2012-08-16 19:05 UTC (permalink / raw)
  To: Daniel Wagner
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, cgroups-u79uwXL29TY76Z2rM5mHXA,
	Daniel Wagner, David S. Miller, Andrew Morton, Eric Dumazet,
	Gao feng, Glauber Costa, Jamal Hadi Salim, John Fastabend,
	Kamezawa Hiroyuki, Li Zefan, Tejun Heo
In-Reply-To: <1345126336-20755-1-git-send-email-wagi-kQCPcA+X3s7YtjvyW6yDsg@public.gmane.org>

On Thu, Aug 16, 2012 at 04:12:11PM +0200, Daniel Wagner wrote:
> From: Daniel Wagner <daniel.wagner-98C5kh4wR6ohFhg+JK9F0w@public.gmane.org>
> 
> Hi,
> 
> The patch #1 and #2 are there to be able to introduce (#3, #4) the 
> jump labels in task_cls_classid() and task_netprioidx(). The jump
> labels are needed to know when it is safe to access the controller. 
> For example not safe means the module is not yet loaded.
> 
> All those patches are just preparation for the center piece (#5) 
> of these series. This one will remove the dynamic subsystem ID
> generation and falls back to compile time generated IDs. 
> 
> This is the first result from the discussion around on the
> "cgroup cls & netprio 'cleanups'" patches.
> 
> This patches are against net-next
> 
> cheers,
> daniel
> 
> Cc: "David S. Miller" <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
> Cc: Andrew Morton <akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b@public.gmane.org>
> Cc: Eric Dumazet <edumazet-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
> Cc: Gao feng <gaofeng-BthXqXjhjHXQFUHtdCDX3A@public.gmane.org>
> Cc: Glauber Costa <glommer-bzQdu9zFT3WakBO8gow8eQ@public.gmane.org>
> Cc: Jamal Hadi Salim <jhs-jkUAjuhPggJWk0Htik3J/w@public.gmane.org>
> Cc: John Fastabend <john.r.fastabend-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> Cc: Kamezawa Hiroyuki <kamezawa.hiroyu-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
> Cc: Li Zefan <lizefan-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> Cc: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>
> Cc: Tejun Heo <tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: cgroups-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> 
> Daniel Wagner (5):
>   cgroup: Use empty task_cls_classid() when !CONFIG_NET_CLS(_MODULE)
>   cgroup: Move sock_update_classid() decleration to cls_cgroup.h
>   cgroup: Protect access to task_cls_classid() when built as module
>   cgroup: Protect access to task_netprioidx() when built as module
>   cgroup: Assign subsystem IDs during compile time
> 
>  include/linux/cgroup.h        | 20 +++++++++++++-------
>  include/linux/cgroup_subsys.h | 24 ++++++++++++------------
>  include/net/cls_cgroup.h      | 42 +++++++++++++++++++++++++++++-------------
>  include/net/netprio_cgroup.h  | 23 +++++++++++------------
>  include/net/sock.h            |  8 --------
>  kernel/cgroup.c               | 31 +++++++++----------------------
>  net/core/netprio_cgroup.c     | 17 ++++++-----------
>  net/core/sock.c               | 12 ++++++------
>  net/sched/cls_cgroup.c        | 18 +++++-------------
>  9 files changed, 91 insertions(+), 104 deletions(-)
> 
> -- 
> 1.7.12.rc1.16.g05a20c8
> 
> 


The series seems reasonable.  I presume you've testing building and running both
net_prio and net_cls as modules and monolithically?
Neil

^ permalink raw reply

* Re: smsc75xx maintainer?
From: Joe Perches @ 2012-08-16 19:12 UTC (permalink / raw)
  To: Grant Grundler, Steve Glendinning; +Cc: netdev
In-Reply-To: <CANEJEGuh++1-7HajcK65a=NPyKY_5JWkjGQFOKddv4JrVn0gFA@mail.gmail.com>

On Wed, 2012-08-15 at 14:48 -0700, Grant Grundler wrote:
> scripts/get_maintainer.pl can't identify an owner for
> drivers/net/usb/smsc75xx.c (USB GigE dongle).

Maybe add --file-emails or --interactive to try harder.

> Anyone (at SMSC?) interested in fixing suspend/resume for smsc75xx driver?
> 
> I don't have time to chase this but it would be nice if this part
> worked. Only suspend/resume appears broken.
> 
> Some details in http://crosbug.com/31871.

MODULE_AUTHOR("Steve Glendinning <steve.glendinning@shawell.net>");

Maybe Steve?  (cc'd)

^ permalink raw reply

* Re: [RFC v3 0/1] net/hsr: Add support for IEC 62439-3 High-availability Seamless Redundancy
From: Arvid Brodin @ 2012-08-16 19:12 UTC (permalink / raw)
  To: netdev@vger.kernel.org
  Cc: Joe Perches, Stephen Hemminger, Alexey Kuznetsov,
	Javier Boticario, Bruno Ferreira
In-Reply-To: <1341361824.1993.16.camel@joe2Laptop>

Hello! This is version 3 of my RFC for HSR support. The complete diffstat of this version
of the kernel patch:

 Documentation/networking/hsr/hsr_genl.c |  221 ++++++++++++++
 include/linux/if_ether.h                |    1 +
 include/linux/if_link.h                 |   11 +
 net/Kconfig                             |    1 +
 net/Makefile                            |    1 +
 net/hsr/Kconfig                         |   33 ++
 net/hsr/Makefile                        |    7 +
 net/hsr/hsr_device.c                    |  506 +++++++++++++++++++++++++++++++
 net/hsr/hsr_device.h                    |   27 ++
 net/hsr/hsr_framereg.c                  |  303 ++++++++++++++++++
 net/hsr/hsr_framereg.h                  |   52 ++++
 net/hsr/hsr_main.c                      |  402 ++++++++++++++++++++++++
 net/hsr/hsr_netlink.c                   |  293 ++++++++++++++++++
 net/hsr/hsr_netlink.h                   |   64 ++++
 net/hsr/hsr_private.h                   |  162 ++++++++++
 15 files changed, 2084 insertions(+), 0 deletions(-)


Changes since v2:


On 2012-07-04 02:30, Joe Perches wrote:
> On Wed, 2012-07-04 at 00:12 +0000, Arvid Brodin wrote:
>> +static int is_operstate_up(struct net_device *dev)
>> +{
>> +	return (dev->operstate == IF_OPER_UP);
>> +}
> 
> bool?

Check. (Changed to bool here and in other places where a boolean was returned.)


>> +static void __hsr_set_operstate(struct net_device *dev, int transition)
>> +{
>> +	if (dev->operstate != transition) {
>> +/*
>> +		switch (transition) {
>> +		case IF_OPER_UP:
>> +			printk(KERN_INFO "%s: new operstate is IF_OPER_UP\n", dev->name);
> 
> 	netdev_info(dev, "new operstate is IF_OPER_UP\n");

Check. Changed all printouts to use netdev_info() etc instead of printk()/pr_info().


>> +/*
>> +	printk(KERN_INFO "Slave1/2 operstate: %d/%d\n",
>> +					slave1->operstate, slave2->operstate);
>> +*/
> 
> Please remove commented out code.

Check. (Removed debug code; still need to keep the FIXMEs so I don't forget
them.)


>> +void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1,
>> +						struct net_device *slave2);
> 
> please align arguments immediately after the open parenthesis.

Check, fixed.


>> +int framereg_merge_node(struct hsr_priv *hsr_priv, enum hsr_dev_idx dev_idx,
>> +							struct sk_buff *skb)
>> +{
> []
>> +	node = find_node_by_AddrA(&hsr_priv->node_db, hsr_stag->MacAddressA);
>> +	if (!node) {
>> +		rcu_read_unlock();
>> +		found = 0;
>> +		node = kmalloc(sizeof(*node), GFP_ATOMIC);
> 
> why GFP_ATOMIC?

This is in the frame receive path; we're not allowed to sleep here (?).


On 2012-07-04 06:20, Stephen Hemminger wrote:
> On Wed, 4 Jul 2012 00:12:13 +0000, Arvid Brodin wrote:
>> diff --git a/Documentation/networking/hsr/hsr_genl.c b/Documentation/networking/hsr/hsr_genl.c
> 1. I like documentation, I like examples, but examples in the Documentation
> directory get stale and end up not working.

I see your point, but I'd really like to supply this example code, because
frankly, the documentation on Generic Netlink with libnl isn't very good if you
aren't already "in the know". It took some time to figure this out.

I added "Current as of <date>" in the header comment so that people at least see
when it is out of date. Is this an OK compromise?


> 2. I am not a fan of the non-standard unaligned access optimization.
> Stinks too much like the old BSD IFF_TRAILERS thing that still persists
> to this day.

This has now been removed. Architectures without HAVE_EFFICIENT_UNALIGNED_ACCESS
will always memmove() the payload.


> 3. The code seems to go to a lot of locking effort to get atomic state
>    update, if it used the bits in dev_state (ie netif_carrier etc) instead of
>    operstate and if_flags I think it would be simpler and safer.  I.e
> 
> +static int is_admin_up(struct net_device *dev)
> +{
> +	return (dev->flags & IFF_UP);
> +}
>     is redundant with netif_running()

Not sure what you mean by neither "a lot of locking effort" nor "atomic state
update" here? (Please elaborate.)

netif_running() checks __LINK_STATE_START. This has got to do with carrier state
rather than administrative state, right? So it's not the same as checking
IFF_UP? Actually I'm pretty confused by how the kernel implements state tracking
of network devices...

It doesn't seem to be normal practice to change operstate like I do, so I'm
probably not doing it right. Linkwatch sets IF_OPER_LOWERLAYERDOWN in
default_operstate() (net/core/link_watch.c) if !netif_carrier_ok(dev) and
(dev->ifindex != dev->iflink). Can I use this for my virtual hsr device driver
somehow? What is dev->iflink?

The HSR interface should be IF_OPER_LOWERLAYERDOWN if it's admin UP but both
slaves are inoperable.

(The linkwatch reference to IF_OPER_LOWERLAYERDOWN is the only one that I can
find in the kernel code.)


> 4. Don't use mixed case as in:
>      +	HSR_Ver = 0;

The HSR_Ver (and others like MacAddressA, HSR_TLV_Length etc) are the names as
written in the HSR IEC standard. Is it still desirable that I change them to
lower case?

I've changed the code a bit so the HSR_Ver local variable and some others are
no longer present, but the struct hsr_tag fields is still as written in the
IEC spec. I also added a comment about this. Ok?


> 5. The header create code has to handle error cases where:
>      1. skb must be copied to add header
>      2. no space left for header in skb

These checks does not seem to be done anywhere else where eth_header() is used,
but I added

	if (skb_headroom(skb) < HSR_TAGLEN + ETH_HLEN)
		return -ENOBUFS;

It is not possible to copy the skb to make room for the header, since there is
no way to return the new pointer from hsr_header_create().


> 6. Don't comment out code and then submit it. I don't like reading and
>    reviewing leftover debug stuff.

Sorry about that. All debug leftovers should now have been removed.


> 7. Any operations type structure should be declared 'const'. This is safer
>    from a security point of view and keeps dirty and immutable variables in separate
>    cache areas.

In general, this results in "warning: passing argument <n> of '<some-func>' discards
qualifiers from pointer target type", where <some-func> is genl_register_family,
genl_register_mc_group, dev_add_pack etc. It does work (and is now fixed) for struct
net_device_ops and struct nla_policy though.


> 8. If possible use standard netdev macros for printing info messages:
>      see netdev_info() etc.

Done.


> 9. Be careful about variable names: "seqlock" implies you are using 
>     seq_lock() but you aren't doing that. It is just a spinlock.

Check (changed variable name to seqnr_lock - it's the lock for the sequence_nr variable
in the same struct).


> 10. above() seems like it could be done by signed math in one operation
>     without conditional ?: operation.
>    See timer_before/timer_after for example.

Thanks. I've now changed the function to something more akin to the time_after()
function. However, there's an inconsistency in that code where time_after(0,
MAX_UTYPE/2) == time_before(0, MAX_UTYPE/2). I don't like that, so I kept an
if statement that fixes that. I hope that's OK.


> 11. All global variables need to have one prefix.  You are using both hsr_
>      and framreg_. Can you just use hsr_?

Done (using hsr_ only now).



I have run the patch through checkpatch and the remaining errors and warnings are
intentional, except for the "C99 // comments" which will be fixed in due time. I
believe fixing the other problems would reduce the readability of the code. I am
however open to suggestions about this, of course.


-- 
Arvid Brodin | Consultant (Linux)
XDIN AB | Jan Stenbecks Torg 17 | SE-164 40 Kista | Sweden | xdin.com

^ permalink raw reply

* RE: [PATCH] bnx2: turn off the network statck during initialization
From: Jiang Wang @ 2012-08-16 19:15 UTC (permalink / raw)
  To: Michael Chan
  Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	Chaitanya Lala, Francis St. Amant
In-Reply-To: <1345143431.6916.36.camel@LTIRV-MCHAN1.corp.ad.broadcom.com>

Hi Michael,

I just checked the code for netif_carrier_off(), and it will return if the dev is not initialized (registered) as follows:

void netif_carrier_off(struct net_device *dev)
{
        if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
                if (dev->reg_state == NETREG_UNINITIALIZED)
                        return;
                linkwatch_fire_event(dev);
        }
}

So linkwatch_fire_event should not fire in this case, right? Thanks for the quick response.

Regards,

Jiang

-------------------------------------
Jiang Wang
Member of Technical Staff
Riverbed Technology
Tel: (408) 522-5109
Email: Jiang.Wang@riverbed.com
www.riverbed.com


-----Original Message-----
From: Michael Chan [mailto:mchan@broadcom.com] 
Sent: Thursday, August 16, 2012 11:57 AM
To: Jiang Wang
Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; Chaitanya Lala; Francis St. Amant; Jiang Wang
Subject: Re: [PATCH] bnx2: turn off the network statck during initialization

On Thu, 2012-08-16 at 11:21 -0700, Jiang Wang wrote: 
> The initialization state of bnx2 driver is wrong. It does not turn of 
> the Linux network stack using netif_carrier_off. This may lead to 
> inconsistent report from ethtool as the link is up but speed is 
> unknown when the cable is not plugged in.
> 
> E.g.
>         Speed: Unknown! (0)<--------------------------------------
>         Duplex: Half       <--------------------------------------
>         MDI: Unknown! (0)
>         Port: Twisted Pair
>         PHYAD: 1
>         Transceiver: internal
>         Auto-negotiation: on
>         Supports Wake-on: g
>         Wake-on: d
>         Link detected: yes <---------------------------------------
> 
> This patches fixed the problem by turning off the network stack during 
> initialization.
> 
> Signed-off-by: Jiang Wang <jwang@riverbed.com>
> ---
>  drivers/net/ethernet/broadcom/bnx2.c |    4 ++++
>  1 files changed, 4 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/net/ethernet/broadcom/bnx2.c 
> b/drivers/net/ethernet/broadcom/bnx2.c
> index ac7b744..ce4548d 100644
> --- a/drivers/net/ethernet/broadcom/bnx2.c
> +++ b/drivers/net/ethernet/broadcom/bnx2.c
> @@ -8463,6 +8463,10 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>  	dev->features |= dev->hw_features;
>  	dev->priv_flags |= IFF_UNICAST_FLT;
>  
> +	/* tell the stack to leave us alone until bnx2_open() is called */
> +	netif_carrier_off(dev);

We have tried this before and this didn't work.  netif_carrier_off() calls linkwatch_fire_event() to schedule the event.  If
register_netdev() fails for whatever reason, the netdev will be freed but the link_watch event may still be scheduled.

Calling netif_carrier_off() after register_netdev() returns successfully also may not work as it will race with bnx2_open() which enables IRQ.
An linkup IRQ can happen at time and we may end up with the wrong carrier state because of the race condition.

> +	netif_stop_queue(dev);
> +
>  	if ((rc = register_netdev(dev))) {
>  		dev_err(&pdev->dev, "Cannot register net device\n");
>  		goto error;

^ permalink raw reply

* Re: [RFC v3 1/1] net/hsr: Add support for IEC 62439-3 High-availability Seamless Redundancy
From: Arvid Brodin @ 2012-08-16 19:17 UTC (permalink / raw)
  To: netdev@vger.kernel.org
  Cc: Joe Perches, Stephen Hemminger, Alexey Kuznetsov,
	Javier Boticario, Bruno Ferreira
In-Reply-To: <1341361824.1993.16.camel@joe2Laptop>

diff --git a/Documentation/networking/hsr/hsr_genl.c b/Documentation/networking/hsr/hsr_genl.c
new file mode 100644
index 0000000..cb384d5
--- /dev/null
+++ b/Documentation/networking/hsr/hsr_genl.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2011-2012 Autronica Fire and Security AS
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Author(s):
+ *	2011-2012 Arvid Brodin, arvid.brodin@xdin.com
+ *
+ * Userspace example of using Generic Netlink to get HSR
+ * ("High-availability Seamless Redundancy") link/network status.
+ *
+ * Needs userspace libnl-3. Current as of 2012-08-07.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netlink/netlink.h>
+#include <netlink/socket.h>
+#include <netlink/attr.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+/**** insert path to hsr_netlink from your kernel below: ****/
+#include "<path>/net/hsr/hsr_netlink.h"
+
+
+static struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = {
+	[HSR_A_NODE_ADDR] = { .type = NLA_UNSPEC },
+	[HSR_A_IFINDEX] = { .type = NLA_U32 },
+	[HSR_A_IF1AGE] = { .type = NLA_U32 },
+	[HSR_A_IF2AGE] = { .type = NLA_U32 },
+};
+
+#define ETH_ALEN	6
+
+void print_mac(const unsigned char *addr)
+{
+	int i;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		printf("%02x ", addr[i]);
+}
+
+
+int parse_genlmsg(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *attrs[HSR_A_MAX + 1];
+	int rc;
+	struct genlmsghdr *hdr;
+	int i;
+
+	rc = genlmsg_parse(nlmsg_hdr(msg), 0, attrs, HSR_A_MAX, hsr_genl_policy);
+	if (rc < 0) {
+		printf("Error parsing genlmsg: %d\n", rc);
+		return rc;
+	}
+
+
+	/*
+	 * Extract command ID from "message" -> "netlink header" ->
+	 * "generic netlink header".
+	 *
+	 * These are the command enums used when creating a genl msg header
+	 * in the kernel with genlmsg_put().
+	 */
+	hdr = genlmsg_hdr(nlmsg_hdr(msg));
+
+	switch (hdr->cmd) {
+	case HSR_C_RING_ERROR:
+		printf("Ring error: \n");
+		break;
+	case HSR_C_NODE_DOWN:
+		printf("Node down: \n");
+		break;
+	case HSR_C_SET_NODE_STATUS:
+		printf("Node status: \n");
+		break;
+	default:
+		printf("Unknown genl message (%d)\n", hdr->cmd);
+	}
+
+
+	/*
+	 * Extract the attached data (the "attributes").
+	 */
+	for (i = 0; i < HSR_A_MAX + 1; i++)
+		if (attrs[i]) {
+			switch (attrs[i]->nla_type) {
+			case HSR_A_NODE_ADDR:
+				printf("    node address ");
+				print_mac(nla_data(attrs[i]));
+				printf("\n");
+				break;
+			case HSR_A_IFINDEX:
+				printf("    interface index %d\n",
+						nla_get_u32(attrs[i]));
+				break;
+			case HSR_A_IF1AGE:
+				printf("    last frame over slave #1 %d ms ago\n",
+						(int) nla_get_u32(attrs[i]));
+				break;
+			case HSR_A_IF2AGE:
+				printf("    last frame over slave #2 %d ms ago\n",
+						(int) nla_get_u32(attrs[i]));
+				break;
+			default:
+				printf("    unknown attribute type: %d\n",
+						attrs[i]->nla_type);
+			}
+		}
+
+	return 0;
+}
+
+/*
+ * Send a "simple" (header only) Generic Netlink message
+int query_link_status(int family)
+{
+	return (genl_send_simple(nlsk, family, HSR_C_GET_STATUS, 1, 0));
+}
+ */
+
+int query_node_status(struct nl_sock *nlsk, int family, int ifindex,
+		      const unsigned char node_addr[ETH_ALEN])
+{
+	struct nl_msg *msg;
+	void *user_hdr;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -1;
+
+	user_hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family,
+						0, 0, HSR_C_GET_NODE_STATUS, 1);
+	if (!user_hdr)
+		goto nla_put_failure;
+
+/*
+ * Query by interface name could be implemented in the kernel if needed:
+ *	NLA_PUT_STRING(msg, HSR_A_IFNAME, ifname);
+ */
+	NLA_PUT_U32(msg, HSR_A_IFINDEX, ifindex);
+	NLA_PUT(msg, HSR_A_NODE_ADDR, ETH_ALEN, node_addr);
+
+	printf("Querying if %d for status of node ", ifindex);
+	print_mac(node_addr);
+	printf("\n");
+
+	return nl_send_auto(nlsk, msg);
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -1;
+}
+
+
+int main()
+{
+	struct nl_sock *nlsk;
+	int hsr_mgroup;
+	int rc;
+
+	nlsk = nl_socket_alloc();
+	if (!nlsk) {
+		printf("nl_socket_alloc() failed\n");
+		return EXIT_FAILURE;
+	}
+	nl_socket_disable_seq_check(nlsk);
+	nl_socket_modify_cb(nlsk, NL_CB_VALID, NL_CB_CUSTOM, parse_genlmsg, NULL);
+	genl_connect(nlsk);
+
+	/*
+	 * Sign up for HSR messages
+	 */
+	hsr_mgroup = genl_ctrl_resolve_grp(nlsk, "HSR", "hsr-network");
+	if (hsr_mgroup < 0) {
+		printf("genl_ctrl_resolve_grp() failed: %d\n", hsr_mgroup);
+		rc = EXIT_FAILURE;
+		goto out;
+	}
+
+	printf("Registering for multicast group %d\n", hsr_mgroup);
+	rc = nl_socket_add_memberships(nlsk, hsr_mgroup, 0);
+	if (rc < 0) {
+		printf("nl_socket_add_memberships() failed: %d\n", rc);
+		goto out;
+	}
+
+	/*
+	 * Send a query about the status of another node on the HSR network:
+	 */
+	int hsr_family;
+	/* The hsr if we send the enquiry to (get it with e.g.
+	 * 'cat /sys/class/net/hsr0/ifindex'): */
+	const int hsr_ifindex = 4;
+	/* The node to enquire about: */
+	const unsigned char node[ETH_ALEN] = {0x00, 0x24, 0x74, 0x00, 0x17, 0xAD};
+
+	hsr_family = genl_ctrl_resolve(nlsk, "HSR");
+	if (hsr_family < 0) {
+		printf("genl_ctrl_resolve() failed: %d\n", hsr_family);
+		goto receive;
+	}
+	rc = query_node_status(nlsk, hsr_family, hsr_ifindex, node);
+	printf("query_node_status() returned %d\n", rc);
+
+	/*
+	 * Receive messages
+	 */
+receive:
+	while (1)
+		nl_recvmsgs_default(nlsk);
+
+	rc = EXIT_SUCCESS;
+out:
+	nl_close(nlsk);
+	nl_socket_free(nlsk);
+	return rc;
+}
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index 56d907a..0d0e2f9 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -83,6 +83,7 @@
 #define ETH_P_TIPC	0x88CA		/* TIPC 			*/
 #define ETH_P_8021AH	0x88E7          /* 802.1ah Backbone Service Tag */
 #define ETH_P_1588	0x88F7		/* IEEE 1588 Timesync */
+#define ETH_P_HSR	0x88FB		/* IEC 62439-3 HSR/PRP		*/
 #define ETH_P_FCOE	0x8906		/* Fibre Channel over Ethernet  */
 #define ETH_P_TDLS	0x890D          /* TDLS */
 #define ETH_P_FIP	0x8914		/* FCoE Initialization Protocol */
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 4b24ff4..3e3efb4 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -391,4 +391,15 @@ struct ifla_port_vsi {
 	__u8 pad[3];
 };

+/* HSR section */
+
+enum {
+	IFLA_HSR_UNSPEC,
+	IFLA_HSR_SLAVE1,
+	IFLA_HSR_SLAVE2,
+	__IFLA_HSR_MAX,
+};
+
+#define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1)
+
 #endif /* _LINUX_IF_LINK_H */
diff --git a/net/Kconfig b/net/Kconfig
index e07272d..22446d3 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -216,6 +216,7 @@ source "net/dcb/Kconfig"
 source "net/dns_resolver/Kconfig"
 source "net/batman-adv/Kconfig"
 source "net/openvswitch/Kconfig"
+source "net/hsr/Kconfig"

 config RPS
 	boolean
diff --git a/net/Makefile b/net/Makefile
index ad432fa..7d8787f 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -70,3 +70,4 @@ obj-$(CONFIG_CEPH_LIB)		+= ceph/
 obj-$(CONFIG_BATMAN_ADV)	+= batman-adv/
 obj-$(CONFIG_NFC)		+= nfc/
 obj-$(CONFIG_OPENVSWITCH)	+= openvswitch/
+obj-$(CONFIG_HSR)		+= hsr/
diff --git a/net/hsr/Kconfig b/net/hsr/Kconfig
new file mode 100644
index 0000000..a9ed304
--- /dev/null
+++ b/net/hsr/Kconfig
@@ -0,0 +1,33 @@
+#
+# IEC 62439-3 High-availability Seamless Redundancy
+#
+
+config HSR
+	tristate "High-availability Seamless Redundancy (HSR)"
+	---help---
+	  If you say Y here, then your Linux box will be able to act as a
+	  DANH ("Doubly attached node implementing HSR"). For this to work,
+	  your Linux box needs (at least) two physical Ethernet interfaces,
+	  and you need to enslave these to a virtual hsr interface using the
+	  appropriate user space tool, i.e.:
+
+	  # ip link add name hsr0 type hsr dev1 dev2
+
+	  Your Linux box must be connected as a node in a ring network
+	  together with other HSR capable nodes.
+
+	  All Ethernet frames sent over the hsr device will be sent in both
+	  directions on the ring (over both slave ports), giving a redundant,
+	  instant fail-over network.
+
+	  Each HSR node in the ring acts like a bridge for HSR frames, but
+	  filters frames that have been forwarded earlier.
+
+	  This code is a "best effort" to comply with the HSR standard as
+	  described in IEC 62439-3, but no compliancy tests have been made.
+	  You need to perform any and all necessary tests yourself before
+	  relying on this code in a safety critical system. In particular, the
+	  standard is very diffuse on how to use the Ring ID field in the HSR
+	  tag, and it's probable that this code does not do the right thing.
+
+	  If unsure, say N.
diff --git a/net/hsr/Makefile b/net/hsr/Makefile
new file mode 100644
index 0000000..b68359f
--- /dev/null
+++ b/net/hsr/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for HSR
+#
+
+obj-$(CONFIG_HSR)	+= hsr.o
+
+hsr-y			:= hsr_main.o hsr_framereg.o hsr_device.o hsr_netlink.o
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
new file mode 100644
index 0000000..cd42ff3
--- /dev/null
+++ b/net/hsr/hsr_device.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright 2011-2012 Autronica Fire and Security AS
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Author(s):
+ *	2011-2012 Arvid Brodin, arvid.brodin@xdin.com
+ *
+ * This file contains device methods for creating, using and destroying
+ * virtual HSR devices.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/netfilter.h>
+#include <linux/netpoll.h>
+#include "hsr_framereg.h"
+#include "hsr_private.h"
+
+
+static bool is_admin_up(struct net_device *dev)
+{
+	return (dev->flags & IFF_UP);
+}
+
+static bool is_operstate_up(struct net_device *dev)
+{
+	return (dev->operstate == IF_OPER_UP);
+}
+
+static void __hsr_set_operstate(struct net_device *dev, int transition)
+{
+	if (dev->operstate != transition) {
+		write_lock_bh(&dev_base_lock);
+		dev->operstate = transition;
+		write_unlock_bh(&dev_base_lock);
+		netdev_state_change(dev);
+	}
+}
+
+void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1,
+		       struct net_device *slave2)
+{
+	if (!is_admin_up(hsr_dev)) {
+		__hsr_set_operstate(hsr_dev, IF_OPER_DOWN);
+		return;
+	}
+
+	if (is_operstate_up(slave1) || is_operstate_up(slave2))
+		__hsr_set_operstate(hsr_dev, IF_OPER_UP);
+	else
+		__hsr_set_operstate(hsr_dev, IF_OPER_LOWERLAYERDOWN);
+}
+
+void hsr_set_carrier(struct net_device *hsr_dev, struct net_device *slave1,
+		     struct net_device *slave2)
+{
+	if (is_operstate_up(slave1) || is_operstate_up(slave2))
+		netif_carrier_on(hsr_dev);
+	else
+		netif_carrier_off(hsr_dev);
+}
+
+
+void hsr_check_announce(struct net_device *hsr_dev, int old_operstate)
+{
+	struct hsr_priv *hsr_priv;
+
+	hsr_priv = netdev_priv(hsr_dev);
+
+	if ((hsr_dev->operstate == IF_OPER_UP) && (old_operstate != IF_OPER_UP)) {
+		/* Went up */
+		hsr_priv->announce_count = 0;
+		hsr_priv->announce_timer.expires = jiffies +
+				msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL);
+		add_timer(&hsr_priv->announce_timer);
+	}
+
+	if ((hsr_dev->operstate != IF_OPER_UP) && (old_operstate == IF_OPER_UP))
+		/* Went down */
+		del_timer(&hsr_priv->announce_timer);
+}
+
+
+
+static int hsr_dev_open(struct net_device *dev)
+{
+	struct hsr_priv *hsr_priv;
+
+	hsr_priv = netdev_priv(dev);
+
+	dev_open(hsr_priv->slave_data[0].dev);
+	dev_open(hsr_priv->slave_data[1].dev);
+
+	return 0;
+}
+
+static int hsr_dev_close(struct net_device *dev)
+{
+	// FIXME: restore status of slaves?
+	return 0;
+}
+
+
+static void hsr_fill_tag(struct hsr_ethhdr *hsr_ethhdr, struct hsr_priv *hsr_priv)
+{
+	unsigned long irqflags;
+
+	/*
+	 * IEC 62439-1, p 48, says the 4-bit "path" field can take values
+	 * between 0001-1001 ("ring identifier", for regular HSR frames),
+	 * or 1111 ("HSR management", supervision frames). Unfortunately, the
+	 * spec writers forgot to explain what a "ring identifier" is, or
+	 * how it is used. So we just set this to 0001 for regular frames,
+	 * and 1111 for supervision frames.
+	 */
+	set_hsr_tag_path(&hsr_ethhdr->hsr_tag, 0x1);
+
+	/*
+	 * IEC 62439-1, p 12: "The link service data unit in an Ethernet frame
+	 * is the content of the frame located between the Length/Type field
+	 * and the Frame Check Sequence."
+	 *
+	 * IEC 62439-3, p 48, specifies the "original LPDU" to include the
+	 * original "LT" field (what "LT" means is not explained anywhere as
+	 * far as I can see - perhaps "Length/Type"?). So LSDU_size might
+	 * equal original length + 2.
+	 *   Also, the fact that this field is not used anywhere (might be used
+	 * by a RedBox connecting HSR and PRP nets?) means I cannot test its
+	 * correctness. Instead of guessing, I set this to 0 here, to make any
+	 * problems immediately apparent. Anyone using this driver with PRP/HSR
+	 * RedBoxes might need to fix this...
+	 */
+	set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, 0);
+
+	spin_lock_irqsave(&hsr_priv->seqnr_lock, irqflags);
+	hsr_ethhdr->hsr_tag.sequence_nr = htons(hsr_priv->sequence_nr);
+	hsr_priv->sequence_nr++;
+	spin_unlock_irqrestore(&hsr_priv->seqnr_lock, irqflags);
+
+	hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto;
+
+	hsr_ethhdr->ethhdr.h_proto = htons(ETH_P_HSR);
+}
+
+static int slave_xmit(struct sk_buff *skb, struct net_device *dev,
+		      struct net_device *hsr_dev)
+{
+	skb_set_dev(skb, dev);
+	skb->priority = 1; // FIXME: what does this mean?
+
+	// FIXME: what's netpoll_tx_running?
+	if (netpoll_tx_running(hsr_dev))
+		return skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev);
+
+	return dev_queue_xmit(skb);
+}
+
+
+static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct hsr_priv *hsr_priv;
+	struct hsr_ethhdr *hsr_ethhdr;
+	struct sk_buff *skb2;
+	int res1, res2;
+
+	hsr_priv = netdev_priv(dev);
+	hsr_ethhdr = (struct hsr_ethhdr *) skb->data;
+
+	if ((ntohs(skb->protocol) != ETH_P_HSR) ||
+			(ntohs(hsr_ethhdr->ethhdr.h_proto) != ETH_P_HSR)) {
+
+		hsr_fill_tag(hsr_ethhdr, hsr_priv);
+		skb->protocol = htons(ETH_P_HSR);
+	}
+
+	skb2 = skb_clone(skb, GFP_ATOMIC);
+
+	res1 = slave_xmit(skb, hsr_priv->slave_data[0].dev, dev);
+	res2 = NET_XMIT_DROP;
+	if (skb2) {
+		/* Address substitution (IEC62439-3 pp 26, 50): replace mac
+		 * address of outgoing frame with that of the outgoing slave's.
+		 */
+		memcpy(hsr_ethhdr->ethhdr.h_source,
+					hsr_priv->slave_data[1].dev->dev_addr,
+					ETH_ALEN);
+		res2 = slave_xmit(skb2, hsr_priv->slave_data[1].dev, dev);
+	}
+
+	if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN ||
+			res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) {
+		hsr_priv->dev->stats.tx_packets++;
+		hsr_priv->dev->stats.tx_bytes += skb->len;
+	} else
+		hsr_priv->dev->stats.tx_dropped++;
+
+	return NETDEV_TX_OK;
+}
+
+
+static int hsr_header_create(struct sk_buff *skb, struct net_device *dev,
+			     unsigned short type, const void *daddr,
+			     const void *saddr, unsigned int len)
+{
+	int res;
+
+	/* Make room for the HSR tag now. We will fill it in later (in
+	   hsr_dev_xmit) */
+	if (skb_headroom(skb) < HSR_TAGLEN + ETH_HLEN)
+		return -ENOBUFS;
+	skb_push(skb, HSR_TAGLEN);
+	res = eth_header(skb, dev, type, daddr, saddr, len + HSR_TAGLEN);
+	if (res <= 0)
+		return res;
+	skb_reset_mac_header(skb);
+
+	return res + HSR_TAGLEN;
+}
+
+
+static const struct header_ops hsr_header_ops = {
+	.create	 = hsr_header_create,
+	.parse	 = eth_header_parse,
+};
+
+
+static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type)
+{
+	struct hsr_priv *hsr_priv;
+	struct sk_buff *skb;
+	struct hsr_sup_tag *hsr_stag;
+	struct hsr_sup_payload *hsr_sp;
+	unsigned long irqflags;
+
+	skb = alloc_skb(LL_ALLOCATED_SPACE(hsr_dev) +
+				sizeof(struct hsr_sup_payload),
+				GFP_ATOMIC);
+	if (skb == NULL)
+		return;
+
+	hsr_priv = netdev_priv(hsr_dev);
+
+	skb_reserve(skb, LL_RESERVED_SPACE(hsr_dev));
+
+	skb->dev = hsr_dev;
+	skb->protocol = htons(ETH_P_HSR);
+
+	if (dev_hard_header(skb, skb->dev, ETH_P_HSR, hsr_multicast_addr,
+					skb->dev->dev_addr, skb->len) < 0)
+		goto out;
+
+	skb_pull(skb, sizeof(struct ethhdr));
+	hsr_stag = (struct hsr_sup_tag *) skb->data;
+
+	set_hsr_stag_path(hsr_stag, 0xf);
+	set_hsr_stag_HSR_Ver(hsr_stag, 0);
+
+	spin_lock_irqsave(&hsr_priv->seqnr_lock, irqflags);
+	hsr_stag->sequence_nr = htons(hsr_priv->sequence_nr);
+	hsr_priv->sequence_nr++;
+	spin_unlock_irqrestore(&hsr_priv->seqnr_lock, irqflags);
+
+	hsr_stag->HSR_TLV_Type = type;
+	hsr_stag->HSR_TLV_Length = 12;
+
+	skb_push(skb, sizeof(struct ethhdr));
+
+	/* Payload: MacAddressA */
+	hsr_sp = (struct hsr_sup_payload *) skb_put(skb,
+				sizeof(struct hsr_sup_payload));
+	memcpy(hsr_sp->MacAddressA, hsr_dev->dev_addr, ETH_ALEN);
+
+	dev_queue_xmit(skb);
+	return;
+
+out:
+	kfree_skb(skb);
+}
+
+
+/*
+ * Announce (supervision frame) timer function
+ */
+static void hsr_announce(unsigned long data)
+{
+	struct hsr_priv *hsr_priv;
+
+	hsr_priv = (struct hsr_priv *) data;
+
+	if (hsr_priv->announce_count < 3) {
+		send_hsr_supervision_frame(hsr_priv->dev, HSR_TLV_ANNOUNCE);
+		hsr_priv->announce_count++;
+	} else
+		send_hsr_supervision_frame(hsr_priv->dev, HSR_TLV_LIFE_CHECK);
+
+	if (hsr_priv->announce_count < 3)
+		hsr_priv->announce_timer.expires = jiffies +
+				msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL);
+	else
+		hsr_priv->announce_timer.expires = jiffies +
+				msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL);
+
+	if (is_admin_up(hsr_priv->dev))
+		add_timer(&hsr_priv->announce_timer);
+}
+
+
+
+
+static void restore_slaves(struct net_device *hsr_dev)
+{
+	struct hsr_priv *hsr_priv;
+	struct net_device *slave[2];
+	int i;
+	int res;
+
+	hsr_priv = netdev_priv(hsr_dev);
+	for (i = 0; i < 2; i++)
+		slave[i] = hsr_priv->slave_data[i].dev;
+
+	rtnl_lock();
+
+	/* Restore promiscuity */
+	for (i = 0; i < 2; i++) {
+		if (!hsr_priv->slave_data[i].promisc)
+			continue;
+		res = dev_set_promiscuity(slave[i],
+					-hsr_priv->slave_data[i].promisc);
+		if (res)
+			netdev_info(hsr_dev, "HSR: Cannot restore slave "
+						"promiscuity (%s, %d)\n",
+						slave[i]->name, res);
+	}
+
+	rtnl_unlock();
+}
+
+static void reclaim_hsr_dev(struct rcu_head *rh)
+{
+	struct hsr_priv *hsr_priv;
+
+	hsr_priv = container_of(rh, struct hsr_priv, rcu_head);
+	free_netdev(hsr_priv->dev);
+}
+
+/*
+ * According to comments in the declaration of struct net_device, this function
+ * is "Called from unregister, can be used to call free_netdev". Ok then...
+ */
+static void hsr_dev_destroy(struct net_device *hsr_dev)
+{
+	struct hsr_priv *hsr_priv;
+
+	hsr_priv = netdev_priv(hsr_dev);
+
+	del_timer(&hsr_priv->announce_timer);
+	unregister_hsr_master(hsr_priv);    /* calls list_del_rcu on hsr_priv */
+	restore_slaves(hsr_dev);
+	call_rcu(&hsr_priv->rcu_head, reclaim_hsr_dev);   /* reclaim hsr_priv */
+}
+
+static const struct net_device_ops hsr_device_ops = {
+	.ndo_open = hsr_dev_open,
+	.ndo_stop = hsr_dev_close,
+	.ndo_start_xmit = hsr_dev_xmit,
+};
+
+
+void hsr_dev_setup(struct net_device *dev)
+{
+	random_ether_addr(dev->dev_addr);
+
+	ether_setup(dev);
+	dev->header_ops		 = &hsr_header_ops;
+	dev->netdev_ops		 = &hsr_device_ops;
+	dev->hard_header_len	+= HSR_TAGLEN;
+	dev->mtu		-= HSR_TAGLEN;
+	dev->tx_queue_len	 = 0;
+
+	dev->destructor = hsr_dev_destroy;
+}
+
+
+/*
+ * If dev is a HSR master, return 1; otherwise, return 0.
+ */
+bool is_hsr_master(struct net_device *dev)
+{
+	return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit);
+}
+
+static int check_slave_ok(struct net_device *dev)
+{
+	/* Don't allow HSR on non-ethernet like devices */
+	if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) ||
+						(dev->addr_len != ETH_ALEN)) {
+		netdev_info(dev, "Cannot enslave loopback or non-ethernet "
+								"device\n");
+		return -EINVAL;
+	}
+
+	/* Don't allow enslaving hsr devices */
+	if (is_hsr_master(dev)) {
+		netdev_info(dev, "Cannot create trees of hsr devices.\n");
+		return -ELOOP;
+	}
+
+	/* FIXME: What about VLAN devices, bonded devices, etc? */
+
+	return 0;
+}
+
+int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2])
+{
+	struct hsr_priv *hsr_priv;
+	int i;
+	int res;
+
+	hsr_priv = netdev_priv(hsr_dev);
+	hsr_priv->dev = hsr_dev;
+	INIT_LIST_HEAD(&hsr_priv->node_db);
+	INIT_LIST_HEAD(&hsr_priv->self_node_db);
+	for (i = 0; i < 2; i++)
+		hsr_priv->slave_data[i].dev = slave[i];
+
+	spin_lock_init(&hsr_priv->seqnr_lock);
+	hsr_priv->sequence_nr = 0;
+
+	init_timer(&hsr_priv->announce_timer);
+	hsr_priv->announce_timer.function = hsr_announce;
+	hsr_priv->announce_timer.data = (unsigned long) hsr_priv;
+
+
+/*
+ * FIXME: do I need to set the value of these?
+ *
+ * - hsr_dev->flags
+ * - hsr_dev->priv_flags
+ */
+
+	for (i = 0; i < 2; i++) {
+		res = check_slave_ok(slave[i]);
+		if (res)
+			return res;
+	}
+
+	hsr_dev->features = slave[0]->features & slave[1]->features;
+	hsr_dev->features |= NETIF_F_LLTX; /* Prevent recursive tx locking */
+
+	/* Save/init data needed for restore */
+	for (i = 0; i < 2; i++) {
+		hsr_priv->slave_data[i].was_up = slave[i]->flags & IFF_UP;
+		hsr_priv->slave_data[i].promisc = 0;
+	}
+
+	/* Set hsr_dev's MAC address to that of mac_slave1 */
+	memcpy(hsr_dev->dev_addr, hsr_priv->slave_data[0].dev->dev_addr,
+								ETH_ALEN);
+
+	/* MTU */
+	for (i = 0; i < 2; i++)
+		if (slave[i]->mtu < hsr_dev->mtu)
+			hsr_dev->mtu = slave[i]->mtu;
+
+	/* Make sure the 1st call to netif_carrier_on() gets through */
+	netif_carrier_off(hsr_dev);
+
+	/* Promiscuity */
+	for (i = 0; i < 2; i++) {
+		res = dev_set_promiscuity(slave[i], 1);
+		if (res) {
+			netdev_info(hsr_dev, "HSR: Cannot set slave "
+						"promiscuity (%s, %d)\n",
+						slave[i]->name, res);
+			goto fail;
+		}
+		/* Remember what we have done so we can restore it later */
+		hsr_priv->slave_data[i].promisc = 1;
+	}
+
+	/* Make sure we recognize frames from ourselves in hsr_rcv() */
+	res = hsr_create_self_node(&hsr_priv->self_node_db,
+					hsr_dev->dev_addr,
+					hsr_priv->slave_data[1].dev->dev_addr);
+	if (res < 0)
+		goto fail;
+
+	res = register_netdevice(hsr_dev);
+	if (res)
+		goto fail;
+
+	register_hsr_master(hsr_priv);
+
+	return 0;
+
+fail:
+	restore_slaves(hsr_dev);
+	return res;
+}
diff --git a/net/hsr/hsr_device.h b/net/hsr/hsr_device.h
new file mode 100644
index 0000000..9a1471b
--- /dev/null
+++ b/net/hsr/hsr_device.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011-2012 Autronica Fire and Security AS
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Author(s):
+ *	2011-2012 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef __HSR_DEVICE_H
+#define __HSR_DEVICE_H
+
+#include <linux/netdevice.h>
+
+void hsr_dev_setup(struct net_device *dev);
+int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2]);
+void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1,
+		       struct net_device *slave2);
+void hsr_set_carrier(struct net_device *hsr_dev, struct net_device *slave1,
+		     struct net_device *slave2);
+void hsr_check_announce(struct net_device *hsr_dev, int old_operstate);
+bool is_hsr_master(struct net_device *dev);
+
+#endif /* __HSR_DEVICE_H */
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
new file mode 100644
index 0000000..de406f6
--- /dev/null
+++ b/net/hsr/hsr_framereg.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2011-2012 Autronica Fire and Security AS
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Author(s):
+ *	2011-2012 Arvid Brodin, arvid.brodin@xdin.com
+ *
+ * The HSR spec says never to forward the same frame twice on the same
+ * interface. A frame is identified by its source MAC address and its HSR
+ * sequence number. This code keeps track of senders and their sequence numbers
+ * to allow filtering of duplicate frames.
+ */
+
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/rculist.h>
+#include "hsr_private.h"
+#include "hsr_framereg.h"
+#include "hsr_netlink.h"
+
+
+/*
+	TODO: use hash lists for mac addresses (linux/jhash.h)?
+*/
+
+struct node_entry {
+	struct list_head mac_list;
+	unsigned char	MacAddressA[ETH_ALEN];
+	unsigned char	MacAddressB[ETH_ALEN];
+	unsigned long	time_in[HSR_MAX_SLAVE];
+	u16		seq_out[HSR_MAX_DEV];
+	struct rcu_head rcu_head;
+};
+
+
+unsigned char *hsr_get_node_addr(struct node_entry *node)
+{
+	return node->MacAddressA;
+}
+
+
+/*
+ * Search for mac entry. Caller must hold rcu read lock.
+ */
+static struct node_entry *find_node_by_AddrA(struct list_head *node_db,
+					     unsigned char addr[ETH_ALEN])
+{
+	struct node_entry *node;
+
+	list_for_each_entry_rcu(node, node_db, mac_list)
+		if (!compare_ether_addr(node->MacAddressA, addr))
+			return node;
+
+	return NULL;
+}
+
+/*
+ * Search for mac entry. Caller must hold rcu read lock.
+ */
+static struct node_entry *find_node_by_AddrB(struct list_head *node_db,
+					     unsigned char addr[ETH_ALEN])
+{
+	struct node_entry *node;
+
+	list_for_each_entry_rcu(node, node_db, mac_list)
+		if (!compare_ether_addr(node->MacAddressB, addr))
+			return node;
+
+	return NULL;
+}
+
+/*
+ * Search for mac entry. Caller must hold rcu read lock.
+ */
+struct node_entry *hsr_find_node(struct list_head *node_db, struct sk_buff *skb)
+{
+	struct node_entry *node;
+	struct ethhdr *ethhdr;
+
+	if (!skb_mac_header_was_set(skb))
+		return NULL;
+
+	ethhdr = (struct ethhdr *) skb_mac_header(skb);
+
+	list_for_each_entry_rcu(node, node_db, mac_list) {
+		if (!compare_ether_addr(node->MacAddressA, ethhdr->h_source))
+			return node;
+		if (!compare_ether_addr(node->MacAddressB, ethhdr->h_source))
+			return node;
+	}
+
+	return NULL;
+}
+
+/*
+ * Helper for device init; the self_node_db is used in hsr_rcv() to recognize
+ * frames from self that's been looped over the HSR ring.
+ */
+int hsr_create_self_node(struct list_head *self_node_db,
+			 unsigned char addr_a[ETH_ALEN],
+			 unsigned char addr_b[ETH_ALEN])
+{
+	struct node_entry *node;
+
+	node = kmalloc(sizeof(*node), GFP_KERNEL);
+	if (!node)
+		return -ENOMEM;
+
+	memcpy(node->MacAddressA, addr_a, ETH_ALEN);
+	memcpy(node->MacAddressB, addr_b, ETH_ALEN);
+
+	list_add_tail_rcu(&node->mac_list, self_node_db);
+	return 0;
+}
+
+int hsr_merge_node(struct hsr_priv *hsr_priv, enum hsr_dev_idx dev_idx,
+		   struct sk_buff *skb)
+{
+	struct ethhdr *ethhdr;
+	struct hsr_sup_payload *hsr_sp;
+	struct node_entry *node;
+	int i;
+	int found;
+
+	ethhdr = (struct ethhdr *) skb_mac_header(skb);
+	hsr_sp = (struct hsr_sup_payload *) skb->data;
+
+	found = 1;
+	rcu_read_lock();
+	node = find_node_by_AddrA(&hsr_priv->node_db, hsr_sp->MacAddressA);
+	if (!node) {
+		rcu_read_unlock();
+		found = 0;
+		node = kmalloc(sizeof(*node), GFP_ATOMIC);
+		if (!node)
+			return -ENOMEM;
+
+		memcpy(node->MacAddressA, hsr_sp->MacAddressA, ETH_ALEN);
+		memcpy(node->MacAddressB, ethhdr->h_source, ETH_ALEN);
+
+		for (i = 0; i < HSR_MAX_SLAVE; i++)
+			node->time_in[i] = 0;
+		for (i = 0; i < HSR_MAX_DEV; i++)
+			node->seq_out[i] = 0;
+	}
+
+	/* Merge node if it's PICS_SUBS capable */
+	if (compare_ether_addr(hsr_sp->MacAddressA, ethhdr->h_source))
+		memcpy(node->MacAddressB, ethhdr->h_source, ETH_ALEN);
+
+	node->time_in[dev_idx] = jiffies;
+
+	if (found)
+		rcu_read_unlock();
+	else
+		list_add_tail_rcu(&node->mac_list, &hsr_priv->node_db);
+
+	return 0;
+}
+
+
+void hsr_addr_subst(struct hsr_priv *hsr_priv, struct sk_buff *skb)
+{
+	struct ethhdr *ethhdr;
+	struct node_entry *node;
+
+	ethhdr = (struct ethhdr *) skb_mac_header(skb);
+
+	rcu_read_lock();
+	node = find_node_by_AddrB(&hsr_priv->node_db, ethhdr->h_source);
+	if (node)
+		memcpy(ethhdr->h_source, node->MacAddressA, ETH_ALEN);
+	rcu_read_unlock();
+}
+
+
+
+/*
+ * above(a, b) - return 1 if a > b, 0 otherwise.
+ */
+static bool above(u16 a, u16 b)
+{
+	/* Remove inconsistency where above(a, b) == below(a, b) */
+	if ((int) b - a == 32768)
+		return 0;
+
+	return (((s16) (b - a)) < 0);
+}
+#define below(a, b)		above((b), (a))
+#define above_or_eq(a, b)	(!below((a), (b)))
+#define below_or_eq(a, b)	(!above((a), (b)))
+
+
+void hsr_register_frame_in(struct node_entry *node, enum hsr_dev_idx dev_idx)
+{
+	if ((dev_idx < 0) || (dev_idx >= HSR_MAX_DEV)) {
+		WARN_ON(1);
+		return;
+	}
+	node->time_in[dev_idx] = jiffies;
+}
+
+/*
+ * Parameters:
+ *	'skb' is a HSR Ethernet frame (with a HSR tag inserted), with a valid
+ *	ethhdr->h_source address and skb->mac_header set.
+ *
+ * Return:
+ *	 1 if frame can be shown to have been sent recently on this interface,
+ *	 0 otherwise, or
+ *	 negative error code on error
+ */
+int hsr_register_frame_out(struct node_entry *node, enum hsr_dev_idx dev_idx,
+			   struct sk_buff *skb)
+{
+	struct hsr_ethhdr *hsr_ethhdr;
+
+	if ((dev_idx < 0) || (dev_idx >= HSR_MAX_DEV)) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+	if (!skb_mac_header_was_set(skb)) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+	hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb);
+
+	if (below_or_eq(hsr_ethhdr->hsr_tag.sequence_nr,
+							node->seq_out[dev_idx]))
+		return 1;
+
+	node->seq_out[dev_idx] = hsr_ethhdr->hsr_tag.sequence_nr;
+	return 0;
+}
+
+
+static void node_entry_reclaim(struct rcu_head *rh)
+{
+	kfree(container_of(rh, struct node_entry, rcu_head));
+}
+
+/*
+ * Remove stale sequence_nr records. Called by timer every
+ * HSR_LIFE_CHECK_INTERVAL (two seconds or so). This is also the only function
+ * that removes mac_entries; it shouldn't need to be rcu_read_lock():ed.
+ */
+void hsr_prune_nodes(struct list_head *node_db)
+{
+	struct node_entry *node_entry, *node_entry_next;
+	unsigned long timestamp;
+
+	list_for_each_entry_safe(node_entry, node_entry_next, node_db, mac_list) {
+
+		timestamp = max(node_entry->time_in[HSR_DEV_SLAVE1],
+				node_entry->time_in[HSR_DEV_SLAVE2]);
+
+		/* Warn only as long as we get frames at all */
+		if (time_is_after_jiffies(timestamp +
+					msecs_to_jiffies(1.5*MAX_SLAVE_DIFF))) {
+
+			/* Check for open ring */
+			if (time_after(node_entry->time_in[HSR_DEV_SLAVE2],
+					node_entry->time_in[HSR_DEV_SLAVE1] +
+					msecs_to_jiffies(MAX_SLAVE_DIFF)))
+				hsr_nl_ringerror(node_entry->MacAddressA, HSR_DEV_SLAVE1);
+			else if (time_after(node_entry->time_in[HSR_DEV_SLAVE1],
+					node_entry->time_in[HSR_DEV_SLAVE2] +
+					msecs_to_jiffies(MAX_SLAVE_DIFF)))
+				hsr_nl_ringerror(node_entry->MacAddressA, HSR_DEV_SLAVE2);
+		}
+
+		/* Prune old entries */
+		if (time_is_before_jiffies(timestamp +
+					msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
+			hsr_nl_nodedown(node_entry->MacAddressA);
+			list_del_rcu(&node_entry->mac_list);
+			call_rcu(&node_entry->rcu_head, node_entry_reclaim);
+		}
+	}
+}
+
+
+void hsr_get_node_times(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
+			unsigned long *time1, unsigned long *time2)
+{
+	struct node_entry *node;
+
+	rcu_read_lock();
+	node = find_node_by_AddrA(&hsr_priv->node_db, addr);
+	if (!node) {
+		*time1 = 0;
+		*time2 = 0;
+	} else {
+		*time1 = node->time_in[HSR_DEV_SLAVE1];
+		*time2 = node->time_in[HSR_DEV_SLAVE2];
+	}
+	rcu_read_unlock();
+}
diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h
new file mode 100644
index 0000000..6e28490
--- /dev/null
+++ b/net/hsr/hsr_framereg.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011-2012 Autronica Fire and Security AS
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Author(s):
+ *	2011-2012 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef _HSR_FRAMEREG_H
+#define _HSR_FRAMEREG_H
+
+#include "hsr_private.h"
+
+enum hsr_dev_idx {
+	HSR_DEV_SLAVE1 = 0,
+	HSR_DEV_SLAVE2,
+	HSR_DEV_MASTER,
+};
+
+struct node_entry;
+
+#define HSR_MAX_SLAVE	(HSR_DEV_SLAVE2 + 1)
+#define HSR_MAX_DEV	(HSR_DEV_MASTER + 1)
+
+struct node_entry *hsr_find_node(struct list_head *node_db, struct sk_buff *skb);
+
+int hsr_merge_node(struct hsr_priv *hsr_priv, enum hsr_dev_idx dev_idx,
+		   struct sk_buff *skb);
+
+void hsr_addr_subst(struct hsr_priv *hsr_priv, struct sk_buff *skb);
+
+void hsr_register_frame_in(struct node_entry *node, enum hsr_dev_idx);
+
+int hsr_register_frame_out(struct node_entry *node, enum hsr_dev_idx,
+			   struct sk_buff *skb);
+
+void hsr_prune_nodes(struct list_head *node_db);
+
+void hsr_get_node_times(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
+			unsigned long *time1, unsigned long *time2);
+
+int hsr_create_self_node(struct list_head *self_node_db,
+			 unsigned char addr_a[ETH_ALEN],
+			 unsigned char addr_b[ETH_ALEN]);
+
+unsigned char *hsr_get_node_addr(struct node_entry *node);
+
+#endif /* _HSR_FRAMEREG_H */
diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c
new file mode 100644
index 0000000..38974f2
--- /dev/null
+++ b/net/hsr/hsr_main.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2011-2012 Autronica Fire and Security AS
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Author(s):
+ *	2011-2012 Arvid Brodin, arvid.brodin@xdin.com
+ *
+ * In addition to routines for registering and unregistering HSR support, this
+ * file also contains the receive routine that handles all incoming frames with
+ * Ethertype (protocol) ETH_P_HSR.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/rculist.h>
+#include <linux/timer.h>
+#include <linux/etherdevice.h>
+#include "hsr_private.h"
+#include "hsr_device.h"
+#include "hsr_netlink.h"
+#include "hsr_framereg.h"
+
+
+/* Multicast address for HSR Supervision frames */
+const u8 hsr_multicast_addr[ETH_ALEN] = {0x01, 0x15, 0x4e, 0x00, 0x01, 0x00};
+
+
+/* List of all registered virtual HSR devices */
+static LIST_HEAD(hsr_list);
+
+void register_hsr_master(struct hsr_priv *hsr_priv)
+{
+	list_add_tail_rcu(&hsr_priv->hsr_list, &hsr_list);
+}
+
+void unregister_hsr_master(struct hsr_priv *hsr_priv)
+{
+	struct hsr_priv *hsr_priv_it;
+
+	list_for_each_entry(hsr_priv_it, &hsr_list, hsr_list)
+		if (hsr_priv_it == hsr_priv) {
+			list_del_rcu(&hsr_priv_it->hsr_list);
+			return;
+		}
+}
+
+
+/*
+ * If dev is a HSR slave device, return the virtual master device. Return NULL
+ * otherwise.
+ */
+static struct hsr_priv *get_hsr_master(struct net_device *dev)
+{
+	struct hsr_priv *hsr_priv;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list)
+		if ((dev == hsr_priv->slave_data[0].dev) ||
+				(dev == hsr_priv->slave_data[1].dev)) {
+			rcu_read_unlock();
+			return hsr_priv;
+		}
+
+	rcu_read_unlock();
+	return NULL;
+}
+
+/*
+ * If dev is a HSR slave device, return the other slave device. Return NULL
+ * otherwise.
+ */
+static struct hsr_slave_data *get_other_slave(struct hsr_priv *hsr_priv,
+					      struct net_device *dev)
+{
+	if (dev == hsr_priv->slave_data[0].dev)
+		return &hsr_priv->slave_data[1];
+	if (dev == hsr_priv->slave_data[1].dev)
+		return &hsr_priv->slave_data[0];
+
+	return NULL;
+}
+
+
+static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
+			     void *ptr)
+{
+
+/*
+ * Should do:
+ *
+ * - error monitoring (broken link)
+ * - slave monitoring (disallow down, reconfiguring ?)
+
+	register_netdevice_notifier(...);
+	NETDEV_GOING_DOWN
+	NETDEV_CHANGEADDR
+	NETDEV_CHANGE (dev->flags)
+	NETDEV_UNREGISTER
+ */
+
+	struct net_device *slave, *other_slave;
+	struct hsr_priv *hsr_priv;
+	struct hsr_slave_data *other_data;
+	int old_operstate;
+
+	hsr_priv = get_hsr_master(ptr);
+	if (hsr_priv) { /* Is ptr a slave device? */
+		slave = ptr;
+		other_data = get_other_slave(hsr_priv, slave);
+		other_slave = other_data->dev;
+	} else {
+		if (!is_hsr_master(ptr))
+			return NOTIFY_DONE;
+		hsr_priv = netdev_priv(ptr);
+		slave = hsr_priv->slave_data[0].dev;
+		other_slave = hsr_priv->slave_data[1].dev;
+	}
+
+	switch (event) {
+	case NETDEV_UP:		/* Administrative state DOWN */
+	case NETDEV_DOWN:	/* Administrative state UP */
+	case NETDEV_CHANGE:	/* Link (carrier) state changes */
+		old_operstate = hsr_priv->dev->operstate;
+		hsr_set_carrier(hsr_priv->dev, slave, other_slave);
+		hsr_set_operstate(hsr_priv->dev, slave, other_slave);
+		hsr_check_announce(hsr_priv->dev, old_operstate);
+	}
+
+	return NOTIFY_DONE;
+}
+
+
+static struct timer_list prune_timer;
+
+static void prune_nodes_all(unsigned long data)
+{
+	struct hsr_priv *hsr_priv;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list)
+		hsr_prune_nodes(&hsr_priv->node_db);
+	rcu_read_unlock();
+
+	prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD);
+	add_timer(&prune_timer);
+}
+
+
+static struct sk_buff *strip_hsr_tag(struct sk_buff *skb)
+{
+	struct hsr_tag *hsr_tag;
+	struct sk_buff *skb2;
+
+	skb2 = skb_share_check(skb, GFP_ATOMIC);
+	if (unlikely(!skb2))
+		goto err_free;
+	skb = skb2;
+
+	if (unlikely(!pskb_may_pull(skb, HSR_TAGLEN)))
+		goto err_free;
+
+	hsr_tag = (struct hsr_tag *) skb->data;
+	skb->protocol = hsr_tag->encap_proto;
+	skb_reset_network_header(skb);  // FIXME is this needed & correct?
+	skb_pull_rcsum(skb, HSR_TAGLEN);
+
+	return skb;
+
+err_free:
+	kfree_skb(skb);
+	return NULL;
+}
+
+
+/*
+ * The uses I can see for these HSR supervision frames are:
+ * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type =
+ *    22") to reset any sequence_nr counters belonging to that node. Useful if
+ *    the other node's counter has been reset for some reason.
+ *    --
+ *    Or not - resetting the counter and bridging the frame would create a
+ *    loop, unfortunately.
+ *
+ * 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck
+ *    frame is received from a particular node, we know something is wrong.
+ *    We just register these (as with normal frames) and throw them away.
+ *
+ * 3) Allow different MAC addresses for the two slave interfaces, using the
+ *    MacAddressA field.
+ */
+static bool handle_supervision_frame(struct hsr_priv *hsr_priv,
+				     enum hsr_dev_idx dev_idx,
+				     struct sk_buff *skb)
+{
+	struct hsr_sup_tag *hsr_stag;
+
+	if (compare_ether_addr(eth_hdr(skb)->h_dest, hsr_multicast_addr))
+		return 0;
+
+	hsr_stag = (struct hsr_sup_tag *) skb->data;
+	if (get_hsr_stag_path(hsr_stag) != 0x0f)
+		return 0;
+	if ((hsr_stag->HSR_TLV_Type != HSR_TLV_ANNOUNCE) &&
+				(hsr_stag->HSR_TLV_Type != HSR_TLV_LIFE_CHECK))
+		return 0;
+	if (hsr_stag->HSR_TLV_Length != 12)
+		return 0;
+
+	skb_pull(skb, sizeof(struct hsr_sup_tag));
+	hsr_merge_node(hsr_priv, dev_idx, skb);
+
+	/* Prepare for forwarding */
+	skb_push(skb, sizeof(struct hsr_sup_tag));
+
+	return 1;
+}
+
+
+/*
+ * Implementation somewhat according to IEC-62439-3, p. 43
+ */
+static int hsr_rcv(struct sk_buff *skb, struct net_device *dev,
+		   struct packet_type *pt, struct net_device *orig_dev)
+{
+	struct hsr_priv *hsr_priv;
+	struct hsr_slave_data *other_slave_data;
+	struct node_entry *node;
+	int deliver_to_self;
+	struct sk_buff *skb_deliver;
+	enum hsr_dev_idx dev_in_idx, dev_other_idx;
+	int ret;
+
+	hsr_priv = get_hsr_master(dev);
+
+	if (!hsr_priv) {
+		netdev_info(dev, "HSR: Got HSR frame on non-HSR device; "
+							"dropping it.\n");
+		kfree_skb(skb);
+		return NET_RX_DROP;
+	}
+
+	if (dev == hsr_priv->slave_data[0].dev) {
+		dev_in_idx = HSR_DEV_SLAVE1;
+		dev_other_idx = HSR_DEV_SLAVE2;
+	} else {
+		dev_in_idx = HSR_DEV_SLAVE2;
+		dev_other_idx = HSR_DEV_SLAVE1;
+	}
+
+	node = hsr_find_node(&hsr_priv->self_node_db, skb);
+	if (node) {
+		/* Always kill frames sent by ourselves */
+		kfree_skb(skb);
+		return NET_RX_SUCCESS;
+	}
+
+	/* Receive this frame? */
+	deliver_to_self = 0;
+	if ((skb->pkt_type == PACKET_HOST) ||
+				(skb->pkt_type == PACKET_MULTICAST) ||
+				(skb->pkt_type == PACKET_BROADCAST))
+		deliver_to_self = 1;
+	else if (!compare_ether_addr(eth_hdr(skb)->h_dest,
+						hsr_priv->dev->dev_addr)) {
+		skb->pkt_type = PACKET_HOST;
+		deliver_to_self = 1;
+	}
+
+	if (handle_supervision_frame(hsr_priv, dev_in_idx, skb) == 1)
+		deliver_to_self = 0;
+
+	rcu_read_lock(); /* node_db */
+	node = hsr_find_node(&hsr_priv->node_db, skb);
+	if (!node) {
+		/* Source node unknown; don't create a network loop */
+		rcu_read_unlock();
+		netdev_info(dev, "HSR: Got HSR frame from unknown node %pM: "
+				 "dropping it.\n",
+				 eth_hdr(skb)->h_source);
+		kfree_skb(skb);
+		return NET_RX_DROP;
+	}
+
+	if (hsr_register_frame_out(node, HSR_DEV_MASTER, skb) == 1)
+		deliver_to_self = 0;
+
+	hsr_register_frame_in(node, dev_in_idx);
+
+	/* Forward this frame? */
+	other_slave_data = NULL;
+	if (skb->pkt_type != PACKET_HOST) {
+		other_slave_data = get_other_slave(hsr_priv, dev);
+		if (hsr_register_frame_out(node, dev_other_idx, skb) == 1)
+			other_slave_data = NULL;
+	}
+
+	rcu_read_unlock(); /* node_db */
+
+	if (!deliver_to_self && !other_slave_data) {
+		kfree_skb(skb);
+		return NET_RX_SUCCESS;
+	}
+
+	skb_deliver = skb;
+	if (deliver_to_self && other_slave_data) {
+#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+		/* We have to memmove the whole payload below */
+		skb_deliver = skb_copy(skb, GFP_ATOMIC);
+#else
+		skb_deliver = skb_clone(skb, GFP_ATOMIC);
+#endif
+		if (!skb_deliver) {
+			deliver_to_self = 0;
+			hsr_priv->dev->stats.rx_dropped++;
+		}
+	}
+
+	if (deliver_to_self) {
+		skb_deliver = strip_hsr_tag(skb_deliver);
+		if (!skb_deliver) {
+			hsr_priv->dev->stats.rx_dropped++;
+			goto forward;
+		}
+#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+		/*
+		 * skb_deliver should be linear here, after the call to
+		 * skb_copy() above. We need to memmove the whole payload to
+		 * work around alignment problems caused by the 6-byte HSR tag.
+		 */
+		memmove(skb_deliver->data - HSR_TAGLEN, skb_deliver->data,
+							skb_deliver->len);
+		skb_deliver->data -= HSR_TAGLEN;
+		skb_deliver->tail -= HSR_TAGLEN;
+		skb_reset_network_header(skb_deliver); // FIXME is this needed?
+#endif
+		skb_deliver->dev = hsr_priv->dev;
+		hsr_addr_subst(hsr_priv, skb_deliver);
+		ret = netif_rx(skb_deliver);
+		if (ret == NET_RX_DROP)
+			hsr_priv->dev->stats.rx_dropped++;
+		else {
+			hsr_priv->dev->stats.rx_packets++;
+			hsr_priv->dev->stats.rx_bytes += skb->len;
+		}
+	}
+
+forward:
+	if (other_slave_data) {
+		skb_push(skb, ETH_HLEN);
+		skb->dev = other_slave_data->dev;
+		dev_queue_xmit(skb);
+	}
+
+	return NET_RX_SUCCESS;
+}
+
+
+static struct packet_type hsr_pt __read_mostly = {
+	.type = htons(ETH_P_HSR),
+	.func = hsr_rcv,
+};
+
+static struct notifier_block hsr_nb = {
+	.notifier_call = hsr_netdev_notify,	/* Slave event notifications */
+};
+
+
+static int __init hsr_init(void)
+{
+	int res;
+
+	BUG_ON(sizeof(struct hsr_tag) != HSR_TAGLEN);
+
+	dev_add_pack(&hsr_pt);
+
+	init_timer(&prune_timer);
+	prune_timer.function = prune_nodes_all;
+	prune_timer.data = 0;
+	prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD);
+	add_timer(&prune_timer);
+
+	register_netdevice_notifier(&hsr_nb);
+
+	res = hsr_netlink_init();
+
+	return res;
+}
+
+static void __exit hsr_exit(void)
+{
+	unregister_netdevice_notifier(&hsr_nb);
+	del_timer(&prune_timer);
+	hsr_netlink_exit();
+	dev_remove_pack(&hsr_pt);
+}
+
+module_init(hsr_init);
+module_exit(hsr_exit);
+MODULE_LICENSE("GPL");
diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c
new file mode 100644
index 0000000..40e31f4
--- /dev/null
+++ b/net/hsr/hsr_netlink.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2011-2012 Autronica Fire and Security AS
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Author(s):
+ *	2011-2012 Arvid Brodin, arvid.brodin@xdin.com
+ *
+ * Routines for handling Netlink messages for HSR.
+ */
+
+#include "hsr_netlink.h"
+#include <linux/kernel.h>
+#include <net/rtnetlink.h>
+#include <net/genetlink.h>
+#include "hsr_private.h"
+#include "hsr_device.h"
+#include "hsr_framereg.h"
+
+static const struct nla_policy hsr_policy[IFLA_HSR_MAX + 1] = {
+	[IFLA_HSR_SLAVE1]	= { .type = NLA_U32 },
+	[IFLA_HSR_SLAVE2]	= { .type = NLA_U32 },
+};
+
+
+/*
+ * Here, it seems a netdevice has already been allocated for us, and the
+ * hsr_dev_setup routine has been executed. Nice!
+ */
+static int hsr_newlink(struct net *src_net, struct net_device *dev,
+		       struct nlattr *tb[], struct nlattr *data[])
+{
+	struct net_device *link[2];
+
+	if (!data[IFLA_HSR_SLAVE1]) {
+		netdev_info(dev, "IFLA_HSR_SLAVE1 missing!\n");
+		return -EINVAL;
+	}
+	link[0] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE1]));
+	if (!data[IFLA_HSR_SLAVE2]) {
+		netdev_info(dev, "IFLA_HSR_SLAVE2 missing!\n");
+		return -EINVAL;
+	}
+	link[1] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE2]));
+
+	if (!link[0] || !link[1])
+		return -ENODEV;
+	if (link[0] == link[1])
+		return -EINVAL;
+
+	return hsr_dev_finalize(dev, link);
+}
+
+static struct rtnl_link_ops hsr_link_ops __read_mostly = {
+	.kind		= "hsr",
+	.maxtype	= IFLA_HSR_MAX,
+	.policy		= hsr_policy,
+	.priv_size	= sizeof(struct hsr_priv),
+	.setup		= hsr_dev_setup,
+//	.validate	= vlan_validate,
+	.newlink	= hsr_newlink,
+//	.changelink	= vlan_changelink,
+//	.dellink	= hsr_dellink,  dev->destructor() called automatically?
+//	.get_size	= vlan_get_size,
+//	.fill_info	= vlan_fill_info,
+};
+
+
+
+/* attribute policy */
+/* NLA_BINARY missing in libnl; use unspec in userspace instead. */
+static const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = {
+	[HSR_A_NODE_ADDR] = { .type = NLA_BINARY, .len = ETH_ALEN },
+	[HSR_A_IFINDEX] = { .type = NLA_U32 },
+	[HSR_A_IF1AGE] = { .type = NLA_U32 }, /* 32-bit int */
+	[HSR_A_IF2AGE] = { .type = NLA_U32 }, /* 32-bit int */
+};
+
+static struct genl_family hsr_genl_family = {
+	.id = GENL_ID_GENERATE,
+	.hdrsize = 0,
+	.name = "HSR",
+	.version = 1,
+	.maxattr = HSR_A_MAX,
+};
+
+static struct genl_multicast_group hsr_network_genl_mcgrp = {
+	.name = "hsr-network",
+};
+
+static int hsr_genl_seq = 0;
+
+
+
+static struct sk_buff *hsr_create_genl_msg(void **pmsg_head, unsigned gfp,
+					   int cmd)
+{
+	struct sk_buff *skb;
+
+	skb = genlmsg_new(NLMSG_GOODSIZE, gfp);
+	if (!skb)
+		return NULL;
+
+	*pmsg_head = genlmsg_put(skb, 0, hsr_genl_seq++, &hsr_genl_family, 0,
+									cmd);
+	if (!pmsg_head) {
+		kfree_skb(skb);
+		return NULL;
+	}
+
+	return skb;
+}
+
+
+/*
+ * This is called if for some node with MAC address addr, we only get frames
+ * over one of the slave interfaces. This would indicate an open network ring
+ * (i.e. a link has failed somewhere).
+ */
+void hsr_nl_ringerror(unsigned char addr[ETH_ALEN], int dev_idx)
+{
+	struct sk_buff *skb;
+	void *msg_head;
+
+	skb = hsr_create_genl_msg(&msg_head, GFP_ATOMIC, HSR_C_RING_ERROR);
+	if (!skb)
+		return;
+
+	NLA_PUT(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
+	NLA_PUT_U32(skb, HSR_A_IFINDEX, dev_idx);
+
+	genlmsg_end(skb, msg_head);
+	genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC);
+
+	return;
+
+nla_put_failure:
+	kfree_skb(skb);
+}
+
+/*
+ * This is called when we haven't heard from the node with MAC address addr for
+ * some time (just before the node is removed from the node table/list).
+ */
+void hsr_nl_nodedown(unsigned char addr[ETH_ALEN])
+{
+	struct sk_buff *skb;
+	void *msg_head;
+
+	skb = hsr_create_genl_msg(&msg_head, GFP_ATOMIC, HSR_C_NODE_DOWN);
+	if (!skb)
+		return;
+
+	NLA_PUT(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
+
+	genlmsg_end(skb, msg_head);
+	genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC);
+
+	return;
+
+nla_put_failure:
+	kfree_skb(skb);
+}
+
+/*
+ * HSR_C_GET_NODE_STATUS lets userspace query the internal HSR node table
+ * about the status of a specific node in the network, defined by its MAC
+ * address.
+ *
+ * Input: hsr ifindex, node mac address
+ * Output: hsr ifindex, node mac address (copied from request),
+ *	   age of latest frame from node over slave 1, slave 2 [ms]
+ */
+static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
+{
+	/* For receiving */
+	struct nlattr *na;
+	char *node_addr;
+	struct net_device *hsr_dev;
+
+	/* For sending */
+	struct sk_buff *skb_out;
+	void *msg_head;
+	struct hsr_priv *hsr_priv;
+	unsigned long time1, time2;
+
+	if (!info)
+		goto invalid;
+
+	na = info->attrs[HSR_A_IFINDEX];
+	if (!na)
+		goto invalid;
+	na = info->attrs[HSR_A_NODE_ADDR];
+	if (!na)
+		goto invalid;
+
+	hsr_dev = __dev_get_by_index(genl_info_net(info),
+					nla_get_u32(info->attrs[HSR_A_IFINDEX]));
+	if (!hsr_dev)
+		goto invalid;
+	if (!is_hsr_master(hsr_dev))
+		goto invalid;
+
+
+	/* Send reply */
+
+	skb_out = hsr_create_genl_msg(&msg_head, GFP_ATOMIC,
+							HSR_C_SET_NODE_STATUS);
+	if (!skb_out)
+		return -ENOMEM;
+
+	NLA_PUT_U32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex);
+
+	node_addr = nla_data(info->attrs[HSR_A_NODE_ADDR]);
+	NLA_PUT(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, node_addr);
+
+	hsr_priv = netdev_priv(hsr_dev);
+	hsr_get_node_times(hsr_priv, node_addr, &time1, &time2);
+
+	NLA_PUT_U32(skb_out, HSR_A_IF1AGE, time1 ?
+					jiffies_to_msecs(jiffies - time1) : -1);
+	NLA_PUT_U32(skb_out, HSR_A_IF2AGE, time2 ?
+					jiffies_to_msecs(jiffies - time2) : -1);
+
+	genlmsg_end(skb_out, msg_head);
+	genlmsg_unicast(genl_info_net(info), skb_out, info->snd_pid);
+
+	return 0;
+
+nla_put_failure:
+	kfree_skb(skb_out);
+
+	return -ENOMEM;
+
+invalid:
+	return -EINVAL;
+}
+
+static struct genl_ops hsr_ops_get_node_status = {
+	.cmd = HSR_C_GET_NODE_STATUS,
+	.flags = 0,
+	.policy = hsr_genl_policy,
+	.doit = hsr_get_node_status,
+	.dumpit = NULL,
+};
+
+
+int __init hsr_netlink_init(void)
+{
+	int rc;
+
+	rc = rtnl_link_register(&hsr_link_ops);
+	if (rc)
+		goto fail_rtnl_link_register;
+
+	rc = genl_register_family(&hsr_genl_family);
+	if (rc)
+		goto fail_genl_register_family;
+
+	rc = genl_register_ops(&hsr_genl_family, &hsr_ops_get_node_status);
+	if (rc)
+		goto fail_genl_register_ops;
+
+	rc = genl_register_mc_group(&hsr_genl_family, &hsr_network_genl_mcgrp);
+	if (rc)
+		goto fail_genl_register_mc_group;
+
+	return 0;
+
+fail_genl_register_mc_group:
+	genl_unregister_ops(&hsr_genl_family, &hsr_ops_get_node_status);
+fail_genl_register_ops:
+	genl_unregister_family(&hsr_genl_family);
+fail_genl_register_family:
+	rtnl_link_unregister(&hsr_link_ops);
+fail_rtnl_link_register:
+
+	return rc;
+}
+
+void __exit hsr_netlink_exit(void)
+{
+	genl_unregister_mc_group(&hsr_genl_family, &hsr_network_genl_mcgrp);
+	genl_unregister_ops(&hsr_genl_family, &hsr_ops_get_node_status);
+	genl_unregister_family(&hsr_genl_family);
+
+	rtnl_link_unregister(&hsr_link_ops);
+}
+
+MODULE_ALIAS_RTNL_LINK("hsr");
diff --git a/net/hsr/hsr_netlink.h b/net/hsr/hsr_netlink.h
new file mode 100644
index 0000000..4282d9f
--- /dev/null
+++ b/net/hsr/hsr_netlink.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011-2012 Autronica Fire and Security AS
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Author(s):
+ *	2011-2012 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef __HSR_NETLINK_H
+#define __HSR_NETLINK_H
+
+/* attributes */
+enum {
+	HSR_A_UNSPEC,
+	HSR_A_NODE_ADDR,
+	HSR_A_IFINDEX,
+	HSR_A_IF1AGE,
+	HSR_A_IF2AGE,
+	__HSR_A_MAX,
+};
+#define HSR_A_MAX (__HSR_A_MAX - 1)
+
+
+#ifdef __KERNEL__
+
+#include <linux/if_ether.h>
+#include <linux/module.h>
+
+int __init hsr_netlink_init(void);
+void __exit hsr_netlink_exit(void);
+
+void hsr_nl_ringerror(unsigned char addr[ETH_ALEN], int dev_idx);
+void hsr_nl_nodedown(unsigned char addr[ETH_ALEN]);
+void hsr_nl_framedrop(int dropcount, int dev_idx);
+void hsr_nl_linkdown(int dev_idx);
+
+
+/*
+ * Generic Netlink HSR family definition
+ */
+
+
+#endif /* __KERNEL__ */
+
+
+
+/* commands */
+enum {
+	HSR_C_UNSPEC,
+	HSR_C_RING_ERROR,
+	HSR_C_NODE_DOWN,
+	HSR_C_GET_NODE_STATUS,
+	HSR_C_SET_NODE_STATUS,
+	__HSR_C_MAX,
+};
+#define HSR_C_MAX (__HSR_C_MAX - 1)
+
+
+
+#endif /* __HSR_NETLINK_H */
diff --git a/net/hsr/hsr_private.h b/net/hsr/hsr_private.h
new file mode 100644
index 0000000..50c8784
--- /dev/null
+++ b/net/hsr/hsr_private.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2011-2012 Autronica Fire and Security AS
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Author(s):
+ *	2011-2012 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef _HSR_PRIVATE_H
+#define _HSR_PRIVATE_H
+
+#include <linux/netdevice.h>
+#include <linux/list.h>
+
+
+/*
+ * Time constants as specified in the HSR specification (IEC-62439-3 2010)
+ * Table 8.
+ * All values in milliseconds.
+ */
+#define HSR_LIFE_CHECK_INTERVAL		 2000 /* ms */
+#define HSR_NODE_FORGET_TIME		60000 /* ms */
+#define HSR_ANNOUNCE_INTERVAL		  100 /* ms */
+
+/*
+ * By how much may slave1 and slave2 timestamps of latest received frame from
+ * each node differ before we notify of communication problem?
+ */
+#define MAX_SLAVE_DIFF			 3000 /* ms */
+
+/*
+ * How often shall we check for broken ring and remove node entries older than
+ * HSR_NODE_FORGET_TIME?
+ */
+#define PRUNE_PERIOD			 3000 /* ms */
+
+
+#define HSR_TLV_ANNOUNCE		   22
+#define HSR_TLV_LIFE_CHECK		   23
+
+
+/*
+ * HSR Tag.
+ * As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB,
+ * path, LSDU_size, sequence Nr }. But we let eth_header() create { h_dest,
+ * h_source, h_proto = 0x88FB }, and add { path, LSDU_size, sequence Nr,
+ * encapsulated protocol } instead.
+ */
+#define HSR_TAGLEN	6
+
+/* Field names below as defined in the IEC:2010 standard for HSR. */
+struct hsr_tag {
+	__be16		path_and_LSDU_size;
+	__be16		sequence_nr;
+	__be16		encap_proto;
+} __packed;
+
+/*
+ * The helper functions below assumes that 'path' occupies the 4 most
+ * significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or
+ * equivalently, the 4 most significant bits of HSR tag byte 14).
+ *
+ * This is unclear in the IEC specification; its definition of MAC addresses
+ * indicates the spec is written with the least significant bit first (to the
+ * left). This, however, would mean that the LSDU field would be split in two
+ * with the path field in-between, which seems strange. I'm guessing the MAC
+ * address definition is in error.
+ */
+static inline u16 get_hsr_tag_path(struct hsr_tag *ht)
+{
+	return ntohs(ht->path_and_LSDU_size) >> 12;
+}
+
+static inline u16 get_hsr_tag_LSDU_size(struct hsr_tag *ht)
+{
+	return ntohs(ht->path_and_LSDU_size) & 0x0FFF;
+}
+
+static inline void set_hsr_tag_path(struct hsr_tag *ht, u16 path)
+{
+	ht->path_and_LSDU_size = htons(
+			(ntohs(ht->path_and_LSDU_size) & 0x0FFF) | (path << 12));
+}
+
+static inline void set_hsr_tag_LSDU_size(struct hsr_tag *ht, u16 LSDU_size)
+{
+	ht->path_and_LSDU_size = htons(
+			(ntohs(ht->path_and_LSDU_size) & 0xF000) |
+			(LSDU_size & 0x0FFF));
+}
+
+struct hsr_ethhdr {
+	struct ethhdr	ethhdr;
+	struct hsr_tag	hsr_tag;
+} __packed;
+
+
+/*
+ * HSR Supervision Frame data types.
+ * Field names as defined in the IEC:2010 standard for HSR.
+ */
+struct hsr_sup_tag {
+	__be16		path_and_HSR_Ver;
+	__be16		sequence_nr;
+	__u8		HSR_TLV_Type;
+	__u8		HSR_TLV_Length;
+} __packed;
+
+struct hsr_sup_payload {
+	unsigned char	MacAddressA[ETH_ALEN];
+} __packed;
+
+static inline u16 get_hsr_stag_path(struct hsr_sup_tag *hst)
+{
+	return get_hsr_tag_path((struct hsr_tag *) hst);
+}
+
+static inline u16 get_hsr_stag_HSR_ver(struct hsr_sup_tag *hst)
+{
+	return get_hsr_tag_LSDU_size((struct hsr_tag *) hst);
+}
+
+static inline void set_hsr_stag_path(struct hsr_sup_tag *hst, u16 path)
+{
+	set_hsr_tag_path((struct hsr_tag *) hst, path);
+}
+
+static inline void set_hsr_stag_HSR_Ver(struct hsr_sup_tag *hst, u16 HSR_Ver)
+{
+	set_hsr_tag_LSDU_size((struct hsr_tag *) hst, HSR_Ver);
+}
+
+
+struct hsr_slave_data {
+	struct net_device	*dev;
+	int promisc;
+	int was_up;
+};
+
+struct hsr_priv {
+	struct list_head	hsr_list;	/* List of hsr devices */
+	struct rcu_head		rcu_head;
+	struct net_device	*dev;
+	struct hsr_slave_data	slave_data[2];
+	struct list_head	node_db;	/* Other HSR nodes */
+	struct list_head	self_node_db;	/* MACs of slaves */
+	struct timer_list	announce_timer;	/* Supervision frame dispatch */
+	int announce_count;
+	u16 sequence_nr;
+	spinlock_t seqnr_lock;			/* locking for sequence_nr */
+};
+
+extern const u8 hsr_multicast_addr[ETH_ALEN];
+
+void register_hsr_master(struct hsr_priv *hsr_priv);
+void unregister_hsr_master(struct hsr_priv *hsr_priv);
+
+#endif /*  _HSR_PRIVATE_H */


-- 
Arvid Brodin | Consultant (Linux)
XDIN AB | Jan Stenbecks Torg 17 | SE-164 40 Kista | Sweden | xdin.com

^ permalink raw reply related

* RE: [PATCH] bnx2: turn off the network statck during initialization
From: Michael Chan @ 2012-08-16 19:28 UTC (permalink / raw)
  To: Jiang Wang
  Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	Chaitanya Lala, Francis St. Amant
In-Reply-To: <CEE05A0D7856E14880AD5560634EBEA834DB2ABC@SFO1EXC-MBXP06.nbttech.com>

On Thu, 2012-08-16 at 19:15 +0000, Jiang Wang wrote: 
> Hi Michael,
> 
> I just checked the code for netif_carrier_off(), and it will return if
> the dev is not initialized (registered) as follows:
> 
> void netif_carrier_off(struct net_device *dev)
> {
>         if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
>                 if (dev->reg_state == NETREG_UNINITIALIZED)
>                         return;
>                 linkwatch_fire_event(dev);
>         }
> }
> 
> So linkwatch_fire_event should not fire in this case, right? Thanks
> for the quick response.
> 
Yes, you're right.  Your patch should not cause any problem then.

But I still don't understand what problem you're trying to solve.  The
link status from ethtool is determined by bnx2_get_link() and it should
always return link down before bnx2_open() is called.  Can you please
elborate?  Thanks.

> Regards,
> 
> Jiang
> 
> -------------------------------------
> Jiang Wang
> Member of Technical Staff
> Riverbed Technology
> Tel: (408) 522-5109
> Email: Jiang.Wang@riverbed.com
> www.riverbed.com
> 
> 
> -----Original Message-----
> From: Michael Chan [mailto:mchan@broadcom.com] 
> Sent: Thursday, August 16, 2012 11:57 AM
> To: Jiang Wang
> Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; Chaitanya Lala; Francis St. Amant; Jiang Wang
> Subject: Re: [PATCH] bnx2: turn off the network statck during initialization
> 
> On Thu, 2012-08-16 at 11:21 -0700, Jiang Wang wrote: 
> > The initialization state of bnx2 driver is wrong. It does not turn of 
> > the Linux network stack using netif_carrier_off. This may lead to 
> > inconsistent report from ethtool as the link is up but speed is 
> > unknown when the cable is not plugged in.
> > 
> > E.g.
> >         Speed: Unknown! (0)<--------------------------------------
> >         Duplex: Half       <--------------------------------------
> >         MDI: Unknown! (0)
> >         Port: Twisted Pair
> >         PHYAD: 1
> >         Transceiver: internal
> >         Auto-negotiation: on
> >         Supports Wake-on: g
> >         Wake-on: d
> >         Link detected: yes <---------------------------------------
> > 
> > This patches fixed the problem by turning off the network stack during 
> > initialization.
> > 
> > Signed-off-by: Jiang Wang <jwang@riverbed.com>
> > ---
> >  drivers/net/ethernet/broadcom/bnx2.c |    4 ++++
> >  1 files changed, 4 insertions(+), 0 deletions(-)
> > 
> > diff --git a/drivers/net/ethernet/broadcom/bnx2.c 
> > b/drivers/net/ethernet/broadcom/bnx2.c
> > index ac7b744..ce4548d 100644
> > --- a/drivers/net/ethernet/broadcom/bnx2.c
> > +++ b/drivers/net/ethernet/broadcom/bnx2.c
> > @@ -8463,6 +8463,10 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> >  	dev->features |= dev->hw_features;
> >  	dev->priv_flags |= IFF_UNICAST_FLT;
> >  
> > +	/* tell the stack to leave us alone until bnx2_open() is called */
> > +	netif_carrier_off(dev);
> 
> We have tried this before and this didn't work.  netif_carrier_off() calls linkwatch_fire_event() to schedule the event.  If
> register_netdev() fails for whatever reason, the netdev will be freed but the link_watch event may still be scheduled.
> 
> Calling netif_carrier_off() after register_netdev() returns successfully also may not work as it will race with bnx2_open() which enables IRQ.
> An linkup IRQ can happen at time and we may end up with the wrong carrier state because of the race condition.
> 
> > +	netif_stop_queue(dev);
> > +
> >  	if ((rc = register_netdev(dev))) {
> >  		dev_err(&pdev->dev, "Cannot register net device\n");
> >  		goto error;
> 
> 
> 
> 

^ permalink raw reply

* Re: [PATCH net-next] sctp: fix compile issue with disabled CONFIG_NET_NS
From: Ben Hutchings @ 2012-08-16 19:36 UTC (permalink / raw)
  To: Eric W. Biederman; +Cc: Ulrich Weber, davem, netdev
In-Reply-To: <87fw7m64v9.fsf@xmission.com>

On Thu, 2012-08-16 at 11:58 -0700, Eric W. Biederman wrote:
> Ulrich Weber <ulrich.weber@sophos.com> writes:
> 
> > struct seq_net_private has no struct net
> > if CONFIG_NET_NS is not enabled
> 
> My mistake.
> 
> Reviewed-by: "Eric W. Biederman" <ebiederm@xmission.com>
> 
> > Signed-off-by: Ulrich Weber <ulrich.weber@sophos.com>
> 
> Ulrich do you get any performance advantage by disabling the network
> namespace?
> 
> I am wondering if there is any benefit to keeping it possible to disable
> the network namespace?
> 
> The original reason for the option was so that distributions and
> other who wanted to avoid new code could protect their users, and
> that reasons seems to have long since passed.
[...]

Network namespaces are now used by common applications such as Chrome/
Chromium and vsftpd (which flushed out some of the early implementation
problems).  They undoubtedly have some overhead that some users would
like to avoid, but I wouldn't expect distributions to disable the
option.

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply

* Re: [PATCH net-next] sctp: fix compile issue with disabled CONFIG_NET_NS
From: Eric W. Biederman @ 2012-08-16 20:00 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: Ulrich Weber, davem, netdev
In-Reply-To: <1345145802.2832.6.camel@bwh-desktop.uk.solarflarecom.com>

Ben Hutchings <bhutchings@solarflare.com> writes:

> On Thu, 2012-08-16 at 11:58 -0700, Eric W. Biederman wrote:
>> Ulrich Weber <ulrich.weber@sophos.com> writes:
>> 
>> > struct seq_net_private has no struct net
>> > if CONFIG_NET_NS is not enabled
>> 
>> My mistake.
>> 
>> Reviewed-by: "Eric W. Biederman" <ebiederm@xmission.com>
>> 
>> > Signed-off-by: Ulrich Weber <ulrich.weber@sophos.com>
>> 
>> Ulrich do you get any performance advantage by disabling the network
>> namespace?
>> 
>> I am wondering if there is any benefit to keeping it possible to disable
>> the network namespace?
>> 
>> The original reason for the option was so that distributions and
>> other who wanted to avoid new code could protect their users, and
>> that reasons seems to have long since passed.
> [...]
>
> Network namespaces are now used by common applications such as Chrome/
> Chromium and vsftpd (which flushed out some of the early implementation
> problems).  They undoubtedly have some overhead that some users would
> like to avoid, but I wouldn't expect distributions to disable the
> option.

When the network namespace code was merged we could not mearsure the
overhead, at least not on gigabit ethernet.  And there is very little
code change when network namespaces go away.

So I am starting to look for people who care.  Especially for people who
can measure overhead associated with network namespaces.

Eric

^ permalink raw reply

* Re: [PATCH v0 0/5] cgroup: Assign subsystem IDs during compile time
From: Daniel Wagner @ 2012-08-16 20:25 UTC (permalink / raw)
  To: Neil Horman
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, cgroups-u79uwXL29TY76Z2rM5mHXA,
	Daniel Wagner, David S. Miller, Andrew Morton, Eric Dumazet,
	Gao feng, Glauber Costa, Jamal Hadi Salim, John Fastabend,
	Kamezawa Hiroyuki, Li Zefan, Tejun Heo
In-Reply-To: <20120816190542.GA8203-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org>

Hi Neil,

On 08/16/2012 09:05 PM, Neil Horman wrote:
>> Daniel Wagner (5):
>>    cgroup: Use empty task_cls_classid() when !CONFIG_NET_CLS(_MODULE)
>>    cgroup: Move sock_update_classid() decleration to cls_cgroup.h
>>    cgroup: Protect access to task_cls_classid() when built as module
>>    cgroup: Protect access to task_netprioidx() when built as module
>>    cgroup: Assign subsystem IDs during compile time
> 
> The series seems reasonable.  I presume you've testing building and running both
> net_prio and net_cls as modules and monolithically?
> Neil

Yep, I spend a good bit of time rebuilding and testing all patches in
the different configuration. I hope I really got the small semantic
differences between net_cls and net_prio regarding the loading and
unloading correct. So please have a close look at the jump label
patches (#3 and #4). 

BTW, I have a few mores on top of these patches, e.g. merging the
builtin and module version of task_cls_classid()/task_netprioidx()
implementation together and getting rid of the many ifdefs in the
header. But let's first get this part reviewed.

thanks,
daniel

^ permalink raw reply

* RE: [PATCH] bnx2: turn off the network statck during initialization
From: Jiang Wang @ 2012-08-16 20:28 UTC (permalink / raw)
  To: Michael Chan
  Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	Chaitanya Lala, Francis St. Amant
In-Reply-To: <1345145318.6916.42.camel@LTIRV-MCHAN1.corp.ad.broadcom.com>

OK. I see what you mean. In fact, I am working on an old RHEL based kernel and it still reports the link via ethtool_op_get_link. That is why I got the inconsistent report from the ethtool as I described in the patch. (the ethtool showed the link is up, but speed is unknown, duplex is half).

I guess I can just port in the patch for bnx2_get_link() to solve this problem.

Also, I have another comment related to link state.

Right now, the bnx2 driver powers up the device in bnx2_init_board(), regardless the netif_carrier is on or off. This may introduce following inconsistent behaviors:
1) suppose the cable is plugged in to the NIC and the other end is connected to a switch
2) user powers up the box
3) the Linux does not bring up the interface; i.e, ifconfig ethx shows it is down
4) ethtool ethx will show no link
5) if the user goes to check the light on the physical NIC, he will see the green link light is ON. That means the link is up, right? 

I think it is better to power down the device until bnx2_open is called. In this way, ethtool report and the physical link light will be consistent. 

Any suggestions? Thanks.

Regards,

Jiang

-----Original Message-----
From: Michael Chan [mailto:mchan@broadcom.com] 
Sent: Thursday, August 16, 2012 12:29 PM
To: Jiang Wang
Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; Chaitanya Lala; Francis St. Amant
Subject: RE: [PATCH] bnx2: turn off the network statck during initialization

On Thu, 2012-08-16 at 19:15 +0000, Jiang Wang wrote: 
> Hi Michael,
> 
> I just checked the code for netif_carrier_off(), and it will return if 
> the dev is not initialized (registered) as follows:
> 
> void netif_carrier_off(struct net_device *dev) {
>         if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
>                 if (dev->reg_state == NETREG_UNINITIALIZED)
>                         return;
>                 linkwatch_fire_event(dev);
>         }
> }
> 
> So linkwatch_fire_event should not fire in this case, right? Thanks 
> for the quick response.
> 
Yes, you're right.  Your patch should not cause any problem then.

But I still don't understand what problem you're trying to solve.  The link status from ethtool is determined by bnx2_get_link() and it should always return link down before bnx2_open() is called.  Can you please elborate?  Thanks.

> Regards,
> 
> Jiang
> 
> -------------------------------------
> Jiang Wang
> Member of Technical Staff
> Riverbed Technology
> Tel: (408) 522-5109
> Email: Jiang.Wang@riverbed.com
> www.riverbed.com
> 
> 
> -----Original Message-----
> From: Michael Chan [mailto:mchan@broadcom.com]
> Sent: Thursday, August 16, 2012 11:57 AM
> To: Jiang Wang
> Cc: netdev@vger.kernel.org; linux-kernel@vger.kernel.org; Chaitanya 
> Lala; Francis St. Amant; Jiang Wang
> Subject: Re: [PATCH] bnx2: turn off the network statck during 
> initialization
> 
> On Thu, 2012-08-16 at 11:21 -0700, Jiang Wang wrote: 
> > The initialization state of bnx2 driver is wrong. It does not turn 
> > of the Linux network stack using netif_carrier_off. This may lead to 
> > inconsistent report from ethtool as the link is up but speed is 
> > unknown when the cable is not plugged in.
> > 
> > E.g.
> >         Speed: Unknown! (0)<--------------------------------------
> >         Duplex: Half       <--------------------------------------
> >         MDI: Unknown! (0)
> >         Port: Twisted Pair
> >         PHYAD: 1
> >         Transceiver: internal
> >         Auto-negotiation: on
> >         Supports Wake-on: g
> >         Wake-on: d
> >         Link detected: yes <---------------------------------------
> > 
> > This patches fixed the problem by turning off the network stack 
> > during initialization.
> > 
> > Signed-off-by: Jiang Wang <jwang@riverbed.com>
> > ---
> >  drivers/net/ethernet/broadcom/bnx2.c |    4 ++++
> >  1 files changed, 4 insertions(+), 0 deletions(-)
> > 
> > diff --git a/drivers/net/ethernet/broadcom/bnx2.c
> > b/drivers/net/ethernet/broadcom/bnx2.c
> > index ac7b744..ce4548d 100644
> > --- a/drivers/net/ethernet/broadcom/bnx2.c
> > +++ b/drivers/net/ethernet/broadcom/bnx2.c
> > @@ -8463,6 +8463,10 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> >  	dev->features |= dev->hw_features;
> >  	dev->priv_flags |= IFF_UNICAST_FLT;
> >  
> > +	/* tell the stack to leave us alone until bnx2_open() is called */
> > +	netif_carrier_off(dev);
> 
> We have tried this before and this didn't work.  netif_carrier_off() 
> calls linkwatch_fire_event() to schedule the event.  If
> register_netdev() fails for whatever reason, the netdev will be freed but the link_watch event may still be scheduled.
> 
> Calling netif_carrier_off() after register_netdev() returns successfully also may not work as it will race with bnx2_open() which enables IRQ.
> An linkup IRQ can happen at time and we may end up with the wrong carrier state because of the race condition.
> 
> > +	netif_stop_queue(dev);
> > +
> >  	if ((rc = register_netdev(dev))) {
> >  		dev_err(&pdev->dev, "Cannot register net device\n");
> >  		goto error;
> 
> 
> 
> 

^ permalink raw reply

* Re: [RFC v3 1/1] net/hsr: Add support for IEC 62439-3 High-availability Seamless Redundancy
From: David Miller @ 2012-08-16 20:30 UTC (permalink / raw)
  To: Arvid.Brodin; +Cc: netdev, joe, shemminger, kuznet, jboticario, balferreira
In-Reply-To: <502D475C.9090208@xdin.com>


This is not the correct way to post a patch.

You should provide a proper full Subject: and commit message text in
the body of the email before the patch itself.

^ permalink raw reply

* Re: [PATCH net-next] sctp: fix compile issue with disabled CONFIG_NET_NS
From: David Miller @ 2012-08-16 20:36 UTC (permalink / raw)
  To: ebiederm; +Cc: ulrich.weber, netdev
In-Reply-To: <87fw7m64v9.fsf@xmission.com>

From: ebiederm@xmission.com (Eric W. Biederman)
Date: Thu, 16 Aug 2012 11:58:34 -0700

> Ulrich Weber <ulrich.weber@sophos.com> writes:
> 
>> struct seq_net_private has no struct net
>> if CONFIG_NET_NS is not enabled
> 
> My mistake.
> 
> Reviewed-by: "Eric W. Biederman" <ebiederm@xmission.com>
> 
>> Signed-off-by: Ulrich Weber <ulrich.weber@sophos.com>

Applied.

^ permalink raw reply

* Re: [patch] sctp: fix bogus if statement in sctp_auth_recv_cid()
From: David Miller @ 2012-08-16 20:37 UTC (permalink / raw)
  To: ebiederm; +Cc: dan.carpenter, vyasevich, sri, linux-sctp, netdev,
	kernel-janitors
In-Reply-To: <87txw266hk.fsf@xmission.com>

From: ebiederm@xmission.com (Eric W. Biederman)
Date: Thu, 16 Aug 2012 11:23:35 -0700

> Dan Carpenter <dan.carpenter@oracle.com> writes:
> 
>> There is an extra semi-colon here, so we always return 0 instead of
>> calling __sctp_auth_cid().
> 
> Doh.
> 
> Reviewed-by: "Eric W. Biederman" <ebiederm@xmission.com>

Applied.

^ permalink raw reply

* $650,500.00 USD.
From: United Nations Trust Funds @ 2012-08-16 21:08 UTC (permalink / raw)
  To: Recipients

Reply with this details to claim.
Full Name:.....................
Address:.......................
Phone Number:..................
Country:......................

^ permalink raw reply

* RE: [PATCH] bnx2: turn off the network statck during initialization
From: Michael Chan @ 2012-08-16 21:13 UTC (permalink / raw)
  To: Jiang Wang
  Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	Chaitanya Lala, Francis St. Amant
In-Reply-To: <CEE05A0D7856E14880AD5560634EBEA834DB2B40@SFO1EXC-MBXP06.nbttech.com>

On Thu, 2012-08-16 at 20:28 +0000, Jiang Wang wrote:
> Also, I have another comment related to link state.
> 
> Right now, the bnx2 driver powers up the device in bnx2_init_board(),
> regardless the netif_carrier is on or off.

We actually don't power up the device.  bnx2_init_board() just probes
the device.  If link is already up, it will stay up.  If it is down, it
will stay down.

> This may introduce following inconsistent behaviors:
> 1) suppose the cable is plugged in to the NIC and the other end is
> connected to a switch
> 2) user powers up the box

The link may already be up before or when you power up the box because
of management firmware (iLO, etc) or WoL.

> 3) the Linux does not bring up the interface; i.e, ifconfig ethx shows
> it is down
> 4) ethtool ethx will show no link
> 5) if the user goes to check the light on the physical NIC, he will
> see the green link light is ON. That means the link is up, right? 
> 
> I think it is better to power down the device until bnx2_open is
> called. In this way, ethtool report and the physical link light will
> be consistent. 

We cannot power it down.  If link is up, it is up for a reason (e.g. it
is an iLO port, etc).

Thanks.

^ permalink raw reply

* Re: [RFC v3 1/1] net/hsr: Add support for IEC 62439-3 High-availability Seamless Redundancy
From: Arvid Brodin @ 2012-08-16 21:16 UTC (permalink / raw)
  To: David Miller
  Cc: netdev@vger.kernel.org, joe@perches.com, shemminger@vyatta.com,
	kuznet@ms2.inr.ac.ru, jboticario@gmail.com,
	balferreira@googlemail.com
In-Reply-To: <20120816.133036.1422214989121265534.davem@davemloft.net>

On 2012-08-16 22:30, David Miller wrote:
> 
> This is not the correct way to post a patch.
> 
> You should provide a proper full Subject: and commit message text in
> the body of the email before the patch itself.

I fail to see what's missing from the Subject: line; please elaborate.

(Please note that I posted this as an RFC since there's still FIXMEs in the code, and
there may still be issues pointed out by Stephen Hemminger to be solved. I'm not sure
it's ready to go into the kernel just yet.)

-- 
Arvid Brodin | Consultant (Linux)
XDIN AB | Jan Stenbecks Torg 17 | SE-164 40 Kista | Sweden | xdin.com

^ permalink raw reply

* RE: [PATCH] bnx2: turn off the network statck during initialization
From: Ben Hutchings @ 2012-08-16 21:25 UTC (permalink / raw)
  To: Jiang Wang
  Cc: Michael Chan, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, Chaitanya Lala, Francis St. Amant
In-Reply-To: <CEE05A0D7856E14880AD5560634EBEA834DB2B40@SFO1EXC-MBXP06.nbttech.com>

On Thu, 2012-08-16 at 20:28 +0000, Jiang Wang wrote:
[...]
> Also, I have another comment related to link state.
> 
> Right now, the bnx2 driver powers up the device in bnx2_init_board(),
> regardless the netif_carrier is on or off. This may introduce
> following inconsistent behaviors:
> 1) suppose the cable is plugged in to the NIC and the other end is
> connected to a switch
> 2) user powers up the box
> 3) the Linux does not bring up the interface; i.e, ifconfig ethx shows
> it is down

Most distributions will bring up all interfaces at boot, by default.

> 4) ethtool ethx will show no link
> 5) if the user goes to check the light on the physical NIC, he will
> see the green link light is ON. That means the link is up, right? 
> 
> I think it is better to power down the device until bnx2_open is
> called. In this way, ethtool report and the physical link light will
> be consistent. 
[...]

In general, a physical network interface may need to be enabled to allow
management traffic to a BMC, even when the interface is in the 'down'
state on the host.

The link state should be considered unknown whenever the interface is
down, and /sys/class/net/$IFACE/carrier is not readable then.  However
ETHTOOL_GLINK is not expected to fail in this way, and must always
return 0 (down) or 1 (up).  The convention (which is now enforced in
ethtool_get_link()) is that when the interface is down it should always
return 0.

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply

* Re: [RFC v3 1/1] net/hsr: Add support for IEC 62439-3 High-availability Seamless Redundancy
From: David Miller @ 2012-08-16 21:46 UTC (permalink / raw)
  To: Arvid.Brodin; +Cc: netdev, joe, shemminger, kuznet, jboticario, balferreira
In-Reply-To: <502D6332.1030303@xdin.com>

From: Arvid Brodin <Arvid.Brodin@xdin.com>
Date: Thu, 16 Aug 2012 21:16:34 +0000

> On 2012-08-16 22:30, David Miller wrote:
>> 
>> This is not the correct way to post a patch.
>> 
>> You should provide a proper full Subject: and commit message text in
>> the body of the email before the patch itself.
> 
> I fail to see what's missing from the Subject: line; please elaborate.

You left in the "Re: " etc. prefix.  You should never post new versions
of a patch as a reply, you should make new, fresh, mailing list postings.

And you failed to include the commit message body.  Everything you seem
to thing belongs in the "0/1" posting, at a minimum actually belongs
here in 1/1 too.

^ permalink raw reply

* RE: [PATCH] bnx2: turn off the network statck during initialization
From: Jiang Wang @ 2012-08-16 21:48 UTC (permalink / raw)
  To: Michael Chan
  Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	Chaitanya Lala, Francis St. Amant
In-Reply-To: <1345151593.6916.62.camel@LTIRV-MCHAN1.corp.ad.broadcom.com>


On Thu, 2012-08-16 at 20:28 +0000, Jiang Wang wrote:
> Also, I have another comment related to link state.
> 
> Right now, the bnx2 driver powers up the device in bnx2_init_board(), 
> regardless the netif_carrier is on or off.

We actually don't power up the device.  bnx2_init_board() just probes the device.  If link is already up, it will stay up.  If it is down, it will stay down.

---- I see. I was confused by the name of bnx2_set_power_state()  and I think the PHY is actually powered up by bnx2_reset_nic, right?

> This may introduce following inconsistent behaviors:
> 1) suppose the cable is plugged in to the NIC and the other end is 
> connected to a switch
> 2) user powers up the box

The link may already be up before or when you power up the box because of management firmware (iLO, etc) or WoL.

---- OK.

> 3) the Linux does not bring up the interface; i.e, ifconfig ethx shows 
> it is down
> 4) ethtool ethx will show no link
> 5) if the user goes to check the light on the physical NIC, he will 
> see the green link light is ON. That means the link is up, right?
> 
> I think it is better to power down the device until bnx2_open is 
> called. In this way, ethtool report and the physical link light will 
> be consistent.

We cannot power it down.  If link is up, it is up for a reason (e.g. it is an iLO port, etc).

---- OK. 


Thanks.

^ permalink raw reply

* Re: [RFC PATCH v1.0] af_packet: don't emit packet on orig fanout group
From: David Miller @ 2012-08-16 21:52 UTC (permalink / raw)
  To: eric; +Cc: netdev
In-Reply-To: <1345131890-15991-1-git-send-email-eric@regit.org>

From: Eric Leblond <eric@regit.org>
Date: Thu, 16 Aug 2012 17:44:50 +0200

> If a packet is emitted on one socket in one group of fanout sockets,
> it is transmitted again. It is thus read again on one of the sockets
> of the fanout group. This result in a loop for software which
> generate packets when receiving one.
> This retransmission is not the intended behavior: a fanout group
> must behave like a single socket. The packet should not be
> transmitted on a socket if it originates from a socket belonging
> to the same fanout group.
> 
> This patch fixes the issue by changing the transmission check to
> take fanout group info account.

This looks mostly fine, thanks for fixing this.

I wonder if it wouldn't be better to simply have a callback?  That
would eliminate all of the ifdefs:

	if (ptype->id_match) {
		if (ptype->id_match(ptype, skb->sk))
			return true;
	} else if (ptype->af_packet_priv == skb->sk)
		return true;

It's a shame that we have a user of af_packet_priv outside of
AF_PACKET, in TIPC.  If we could get rid of that we could simplify
things even futher.

^ permalink raw reply

* Re: [RFC PATCH v1.0] af_packet: don't emit packet on orig fanout group
From: David Miller @ 2012-08-16 21:52 UTC (permalink / raw)
  To: eric; +Cc: netdev
In-Reply-To: <20120816.145200.694410419765575459.davem@davemloft.net>


BTW, please also give "Aleksandr Kotov <a1k@mail.ru>" credit for
reporting this problem recently with a Reported-by tag, thanks.

^ permalink raw reply

* Re: AF_PACKET + PACKET_FANOUT: tx packet delivered back in rx
From: David Miller @ 2012-08-16 21:53 UTC (permalink / raw)
  To: a1k; +Cc: netdev, miquels
In-Reply-To: <1342081817.394175102@f168.mail.ru>

From: Aleksandr Kotov <a1k@mail.ru>
Date: Thu, 12 Jul 2012 12:30:17 +0400

> I have a problem using AF_PACKET socket with PACKET_RX_RING, PACKET_TX_RING and PACKET_FANOUT options enabled.
> I am seeing my TX packets get back to the same socket in RX ring. Socket is created and gets bind with ETH_P_ALL protocol type. The problem is in the fanout_add function 
> which sets af_packet_priv to "match" variable, but not sk one describing socket and in dev_queue_xmit_nit skb get bounced back to the socket despite the comment left in the function.

Eric Leblong is posting fixes for this problem recently:

http://patchwork.ozlabs.org/patch/178044/

^ permalink raw reply

* Re: [net V2] act_mirred: do not drop packets when fails to mirror it
From: David Miller @ 2012-08-16 21:55 UTC (permalink / raw)
  To: jhs; +Cc: jasowang, netdev, linux-kernel
In-Reply-To: <1345119107.5398.0.camel@mojatatu>

From: Jamal Hadi Salim <jhs@mojatatu.com>
Date: Thu, 16 Aug 2012 08:11:47 -0400

> On Thu, 2012-08-16 at 14:44 +0800, Jason Wang wrote:
>> We drop packet unconditionally when we fail to mirror it. This is not intended
>> in some cases. Consdier for kvm guest, we may mirror the traffic of the bridge
>> to a tap device used by a VM. When kernel fails to mirror the packet in
>> conditions such as when qemu crashes or stop polling the tap, it's hard for the
>> management software to detect such condition and clean the the mirroring
>> before. This would lead all packets to the bridge to be dropped and break the
>> netowrk of other virtual machines.
>> 
>> To solve the issue, the patch does not drop packets when kernel fails to mirror
>> it, and only drop the redirected packets.
>> 
>> Signed-off-by: Jason Wang <jasowang@redhat.com>
> 
> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>

Applied, thanks.

^ 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