* [RFC PATCH] tcp: limit data skbs in qdisc layer
From: Eric Dumazet @ 2012-07-04 10:11 UTC (permalink / raw)
To: David Miller
Cc: Nandita Dukkipati, netdev, Yuchung Cheng, codel, Matt Mathis,
Neal Cardwell
In-Reply-To: <1340945457.29822.7.camel@edumazet-glaptop>
On Fri, 2012-06-29 at 06:51 +0200, Eric Dumazet wrote:
> My long term plan is to reduce number of skbs queued in Qdisc for TCP
> stack, to reduce RTT (removing the artificial RTT bias because of local
> queues)
preliminar patch to give the rough idea :
sk->sk_wmem_alloc not allowed to grow above a given limit,
allowing no more than ~4 segments [1] per tcp socket in qdisc layer at a
given time. (if TSO is enabled, then a single TSO packet hits the limit)
This means we divert sock_wfree() to a tcp_wfree() handler, to
queue/send following frames when skb_orphan() is called for the already
queued skbs.
Note : While this lowers artificial cwnd and rtt, this means more work
has to be done by tx completion handler (softirq instead of preemptable
process context)
To reduce this possible source of latency, we can skb_try_orphan(skb) in
NIC drivers ndo_start_xmit() instead of waiting TX completion, but do
this orphaning only on BQL enabled drivers, or in drivers known to
possibly delay TX completion (NIU is an example, as David had to revert
BQL because of this delaying)
results on my dev machine (tg3 nic) are really impressive, using
standard pfifo_fast, and with or without TSO/GSO
I no longer have 3MBytes backlogged in qdisc by a single netperf
session, and both side socket autotuning no longer use 4 Mbytes.
tcp_write_xmit() call is probably very naive and lacks proper tests.
Note :
[1] because sk_wmem_alloc accounts skb truesize, mss*4 test allow less
then 4 frames, but it seems ok.
drivers/net/ethernet/broadcom/tg3.c | 1
include/linux/skbuff.h | 6 +++
include/net/sock.h | 1
net/ipv4/tcp_output.c | 44 +++++++++++++++++++++++++-
4 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index e47ff8b..ce0ca96 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -7020,6 +7020,7 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
skb_tx_timestamp(skb);
netdev_tx_sent_queue(txq, skb->len);
+ skb_try_orphan(skb);
/* Sync BD data before updating mailbox */
wmb();
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 885c9bd..c4d4d15 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -1667,6 +1667,12 @@ static inline void skb_orphan(struct sk_buff *skb)
skb->sk = NULL;
}
+static inline void skb_try_orphan(struct sk_buff *skb)
+{
+ if (!skb_shinfo(skb)->tx_flags)
+ skb_orphan(skb);
+}
+
/**
* __skb_queue_purge - empty a list
* @list: list to empty
diff --git a/include/net/sock.h b/include/net/sock.h
index 640432a..2ce17b1 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1389,6 +1389,7 @@ extern void release_sock(struct sock *sk);
/* BH context may only use the following locking interface. */
#define bh_lock_sock(__sk) spin_lock(&((__sk)->sk_lock.slock))
+#define bh_trylock_sock(__sk) spin_trylock(&((__sk)->sk_lock.slock))
#define bh_lock_sock_nested(__sk) \
spin_lock_nested(&((__sk)->sk_lock.slock), \
SINGLE_DEPTH_NESTING)
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index c465d3e..4e6ef82 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -65,6 +65,8 @@ int sysctl_tcp_slow_start_after_idle __read_mostly = 1;
int sysctl_tcp_cookie_size __read_mostly = 0; /* TCP_COOKIE_MAX */
EXPORT_SYMBOL_GPL(sysctl_tcp_cookie_size);
+static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
+ int push_one, gfp_t gfp);
/* Account for new data that has been sent to the network. */
static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb)
@@ -783,6 +785,34 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
return size;
}
+/*
+ * Write buffer destructor automatically called from kfree_skb.
+ */
+void tcp_wfree(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+ unsigned int len = skb->truesize;
+
+ atomic_sub(len - 1, &sk->sk_wmem_alloc);
+ if (bh_trylock_sock(sk)) {
+ if (!sock_owned_by_user(sk)) {
+ if ((1 << sk->sk_state) &
+ (TCPF_CLOSE_WAIT | TCPF_ESTABLISHED))
+ tcp_write_xmit(sk,
+ tcp_current_mss(sk),
+ 0, 0,
+ GFP_ATOMIC);
+ } else {
+ /* TODO : might signal something here
+ * so that user thread can call tcp_write_xmit()
+ */
+ }
+ bh_unlock_sock(sk);
+ }
+
+ sk_free(sk);
+}
+
/* This routine actually transmits TCP packets queued in by
* tcp_do_sendmsg(). This is used by both the initial
* transmission and possible later retransmissions.
@@ -844,7 +874,11 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
skb_push(skb, tcp_header_size);
skb_reset_transport_header(skb);
- skb_set_owner_w(skb, sk);
+
+ skb_orphan(skb);
+ skb->sk = sk;
+ skb->destructor = tcp_wfree;
+ atomic_add(skb->truesize, &sk->sk_wmem_alloc);
/* Build TCP header and checksum it. */
th = tcp_hdr(skb);
@@ -1780,6 +1814,14 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
while ((skb = tcp_send_head(sk))) {
unsigned int limit;
+ /* yes, sk_wmem_alloc accounts skb truesize, so mss_now * 4
+ * is a lazy approximation of our needs, but it seems ok
+ */
+ if (atomic_read(&sk->sk_wmem_alloc) >= mss_now * 4) {
+ pr_debug("here we stop sending frame because sk_wmem_alloc %d\n",
+ atomic_read(&sk->sk_wmem_alloc));
+ break;
+ }
tso_segs = tcp_init_tso_segs(sk, skb, mss_now);
BUG_ON(!tso_segs);
^ permalink raw reply related
* [PATCH 2/2] [sky2] Fix for interrupt handler
From: Mirko Lindner @ 2012-07-04 9:38 UTC (permalink / raw)
To: davem@davemloft.net; +Cc: shemminger@vyatta.com, netdev@vger.kernel.org
Re-enable interrupts if it is not our interrupt
Signed-off-by: Mirko Lindner <mlindner@marvell.com>
---
drivers/net/ethernet/marvell/sky2.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index f1163b2..2b0748d 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -3080,8 +3080,10 @@ static irqreturn_t sky2_intr(int irq, void *dev_id)
/* Reading this mask interrupts as side effect */
status = sky2_read32(hw, B0_Y2_SP_ISRC2);
- if (status == 0 || status == ~0)
+ if (status == 0 || status == ~0) {
+ sky2_write32(hw, B0_Y2_SP_ICR, 2);
return IRQ_NONE;
+ }
prefetch(&hw->st_le[hw->st_idx]);
--
1.7.10.4
^ permalink raw reply related
* [PATCH 1/2] [sky2] Added support for Optima EEE
From: Mirko Lindner @ 2012-07-04 9:38 UTC (permalink / raw)
To: davem@davemloft.net; +Cc: netdev@vger.kernel.org, shemminger@vyatta.com
This patch adds support for the Optima EEE chipset.
Signed-off-by: Mirko Lindner <mlindner@marvell.com>
---
drivers/net/ethernet/marvell/sky2.c | 14 +++++++++++++-
drivers/net/ethernet/marvell/sky2.h | 5 +++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 28a5445..f1163b2 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -141,6 +141,7 @@ static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = {
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4370) }, /* 88E8075 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4380) }, /* 88E8057 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4381) }, /* 88E8059 */
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4382) }, /* 88E8079 */
{ 0 }
};
@@ -3349,6 +3350,17 @@ static void sky2_reset(struct sky2_hw *hw)
sky2_pci_write16(hw, pdev->pcie_cap + PCI_EXP_LNKCTL,
reg);
+ if (hw->chip_id == CHIP_ID_YUKON_PRM &&
+ hw->chip_rev == CHIP_REV_YU_PRM_A0) {
+ /* change PHY Interrupt polarity to low active */
+ reg = sky2_read16(hw, GPHY_CTRL);
+ sky2_write16(hw, GPHY_CTRL, reg | GPC_INTPOL);
+
+ /* adapt HW for low active PHY Interrupt */
+ reg = sky2_read16(hw, Y2_CFG_SPC + PCI_LDO_CTRL);
+ sky2_write16(hw, Y2_CFG_SPC + PCI_LDO_CTRL, reg | PHY_M_UNDOC1);
+ }
+
sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
/* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */
@@ -4871,7 +4883,7 @@ static const char *sky2_name(u8 chipid, char *buf, int sz)
"UL 2", /* 0xba */
"Unknown", /* 0xbb */
"Optima", /* 0xbc */
- "Optima Prime", /* 0xbd */
+ "OptimaEEE", /* 0xbd */
"Optima 2", /* 0xbe */
};
diff --git a/drivers/net/ethernet/marvell/sky2.h b/drivers/net/ethernet/marvell/sky2.h
index 3c896ce..615ac63 100644
--- a/drivers/net/ethernet/marvell/sky2.h
+++ b/drivers/net/ethernet/marvell/sky2.h
@@ -23,6 +23,7 @@ enum {
PSM_CONFIG_REG3 = 0x164,
PSM_CONFIG_REG4 = 0x168,
+ PCI_LDO_CTRL = 0xbc,
};
/* Yukon-2 */
@@ -586,6 +587,10 @@ enum yukon_supr_rev {
CHIP_REV_YU_SU_B1 = 3,
};
+enum yukon_prm_rev {
+ CHIP_REV_YU_PRM_Z1 = 1,
+ CHIP_REV_YU_PRM_A0 = 2,
+};
/* B2_Y2_CLK_GATE 8 bit Clock Gating (Yukon-2 only) */
enum {
--
1.7.10.4
^ permalink raw reply related
* [PATCH 0/2] [net-next] Marvell sky2 updates
From: Mirko Lindner @ 2012-07-04 9:38 UTC (permalink / raw)
To: davem@davemloft.net; +Cc: shemminger@vyatta.com, netdev@vger.kernel.org
Hi David,
here are a couple of sky2 driver updates. This patch series contains
support for the Optima EEE chipset and a relatively small correction to
the interrupt handler.
Mirko Lindner (2):
This patch adds support for the Optima EEE chipset.
Re-enable interrupts if it is not our interrupt
drivers/net/ethernet/marvell/sky2.c | 18 ++++++++++++++++--
drivers/net/ethernet/marvell/sky2.h | 5 +++++
2 files changed, 21 insertions(+), 2 deletions(-)
^ permalink raw reply
* [PATCH] net/macb: manage carrier state with call to netif_carrier_{on|off}()
From: Nicolas Ferre @ 2012-07-04 9:14 UTC (permalink / raw)
To: netdev, bhutchings, Arvid.Brodin
Cc: kuznet, shemminger, linux-arm-kernel, davem, Nicolas Ferre
In-Reply-To: <1341361835.2839.21.camel@bwh-desktop.uk.solarflarecom.com>
OFF carrier state is setup in probe() open() and suspend() functions.
The carrier ON state is managed in macb_handle_link_change().
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
Hi,
This patch follows the discussion about IPv6 and macb interface
("ADDRCONF(NETDEV_UP): eth0: link is not ready" with IPv6)
It may not address all aspects of the discussion, so please do comments...
I am still wondering if netif_carrier_off() is needed in macb_probe() *after*
the call to register_netdev() in addition to its inclusion in the open()
function. As I saw it in sevral drivers, I kept it.
I have tested several suspend/resume loops, and I think that just calling
netif_carrier_off() in suspend() function is sufficient: am I right?
Thanks for your help,
Best regards,
drivers/net/ethernet/cadence/macb.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 1466bc4..033064b 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -179,13 +179,16 @@ static void macb_handle_link_change(struct net_device *dev)
spin_unlock_irqrestore(&bp->lock, flags);
if (status_change) {
- if (phydev->link)
+ if (phydev->link) {
+ netif_carrier_on(dev);
netdev_info(dev, "link up (%d/%s)\n",
phydev->speed,
phydev->duplex == DUPLEX_FULL ?
"Full" : "Half");
- else
+ } else {
+ netif_carrier_off(dev);
netdev_info(dev, "link down\n");
+ }
}
}
@@ -1033,6 +1036,9 @@ static int macb_open(struct net_device *dev)
netdev_dbg(bp->dev, "open\n");
+ /* carrier starts down */
+ netif_carrier_off(dev);
+
/* if the phy is not yet register, retry later*/
if (!bp->phy_dev)
return -EAGAIN;
@@ -1406,6 +1412,8 @@ static int __init macb_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dev);
+ netif_carrier_off(dev);
+
netdev_info(dev, "Cadence %s at 0x%08lx irq %d (%pM)\n",
macb_is_gem(bp) ? "GEM" : "MACB", dev->base_addr,
dev->irq, dev->dev_addr);
@@ -1469,6 +1477,7 @@ static int macb_suspend(struct platform_device *pdev, pm_message_t state)
struct net_device *netdev = platform_get_drvdata(pdev);
struct macb *bp = netdev_priv(netdev);
+ netif_carrier_off(netdev);
netif_device_detach(netdev);
clk_disable(bp->hclk);
--
1.7.10
^ permalink raw reply related
* RE: [PATCH v2 net-next 1/2] r8169: support RTL8106E
From: hayeswang @ 2012-07-04 9:02 UTC (permalink / raw)
To: 'Francois Romieu'; +Cc: netdev, linux-kernel
In-Reply-To: <20120703110101.GA4779@electric-eye.fr.zoreil.com>
Francois Romieu [mailto:romieu@fr.zoreil.com]
[...]
> > +#define FIRMWARE_8106E_1 "rtl_nic/rtl8106e-1.fw"
>
> I did not notice it. Was it submitted ?
>
I don't submit it yet.
> It was obviously not required for testing :o)
>
> > @@ -1933,6 +1941,8 @@ static void
> rtl8169_get_mac_version(struct rtl8169_private *tp,
> > { 0x7c800000, 0x30000000, RTL_GIGA_MAC_VER_11 },
> >
> > /* 8101 family. */
> > + { 0x7cf00000, 0x44900000, RTL_GIGA_MAC_VER_39 },
> > + { 0x7c800000, 0x44800000, RTL_GIGA_MAC_VER_39 },
> > { 0x7c800000, 0x44000000, RTL_GIGA_MAC_VER_37 },
> > { 0x7cf00000, 0x40b00000, RTL_GIGA_MAC_VER_30 },
> > { 0x7cf00000, 0x40a00000, RTL_GIGA_MAC_VER_30 },
>
> Realtek's 1.022.00 8101 driver only maps { 0x7c800000; 0x44800000 } to
> a generic device - if at all - and it maps { 0x7cf00000; 0x44800000 }
> to a different chipset (namely CFG_METHOD_15 where RTL_GIGA_MAC_VER_39
> is CFG_METHOD_16).
>
> Why should both drivers diverge ?
>
Our PMs say only 0x44900000 would have mass production. Besides, you could use
the same setting for both of them.
Best Regards,
Hayes
^ permalink raw reply
* Re: [PATCH 2/2] net: add support for NS8390 based eth controllers on some ColdFire CPU boards
From: Greg Ungerer @ 2012-07-04 8:06 UTC (permalink / raw)
To: Joe Perches; +Cc: Geert Uytterhoeven, netdev, linux-m68k, Greg Ungerer
In-Reply-To: <CAMuHMdVYiQyurOQOoeLbd6zXnaVK8Y9bUC=wXkHxhQO+cQwecQ@mail.gmail.com>
On 07/04/2012 05:52 PM, Geert Uytterhoeven wrote:
> On Wed, Jul 4, 2012 at 8:39 AM, Joe Perches <joe@perches.com> wrote:
>>>>> +static int mcf8390_init(struct net_device *dev)
>>>>> +{
>>>>> + static u32 offsets[] = {
>>>>> + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
>>>>> + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
>>>>> + };
>>>>
>>>> const? u8?
>>>
>>> That is assigned to the "reg_offset" field of "struct ei_device"
>>> (defined in the existing 8390.h) and that is:
>>>
>>> u32 *reg_offset; /* Register mapping table */
>>>
>>> So I can't change this.
>>
>> Sure you can, you can assign a u8 to a u32 just fine.
>
> It's not the value that's assigned, but a pointer to the table, so
> for now it must be non-const u32 *.
>
>> The value fits in a u8. Might as well make it take
>> less data space. static const makes it read-only and
>> makes the compiler assign it just once instead of at
>> function invocation.
>
> That needs changes to drivers/net/ethernet/8390/8390.h, and
> also probably to some drivers that assign values to the array.
Yes, that is right, some drivers as well.
Just making that local offsets array const generates compiler
warnings (as you would expect):
CC drivers/net/ethernet/8390/mcf8390.o
drivers/net/ethernet/8390/mcf8390.c: In function ‘mcf8390_init’:
drivers/net/ethernet/8390/mcf8390.c:393:23: warning: assignment discards
qualifiers from pointer target type
Regards
Greg
------------------------------------------------------------------------
Greg Ungerer -- Principal Engineer EMAIL: gerg@snapgear.com
SnapGear Group, McAfee PHONE: +61 7 3435 2888
8 Gardner Close, FAX: +61 7 3891 3630
Milton, QLD, 4064, Australia WEB: http://www.SnapGear.com
^ permalink raw reply
* Re: [PATCH 2/2] net: add support for NS8390 based eth controllers on some ColdFire CPU boards
From: Geert Uytterhoeven @ 2012-07-04 7:52 UTC (permalink / raw)
To: Joe Perches; +Cc: Greg Ungerer, netdev, linux-m68k, Greg Ungerer
In-Reply-To: <1341383999.3627.20.camel@joe2Laptop>
On Wed, Jul 4, 2012 at 8:39 AM, Joe Perches <joe@perches.com> wrote:
>> >> +static int mcf8390_init(struct net_device *dev)
>> >> +{
>> >> + static u32 offsets[] = {
>> >> + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
>> >> + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
>> >> + };
>> >
>> > const? u8?
>>
>> That is assigned to the "reg_offset" field of "struct ei_device"
>> (defined in the existing 8390.h) and that is:
>>
>> u32 *reg_offset; /* Register mapping table */
>>
>> So I can't change this.
>
> Sure you can, you can assign a u8 to a u32 just fine.
It's not the value that's assigned, but a pointer to the table, so
for now it must be non-const u32 *.
> The value fits in a u8. Might as well make it take
> less data space. static const makes it read-only and
> makes the compiler assign it just once instead of at
> function invocation.
That needs changes to drivers/net/ethernet/8390/8390.h, and
also probably to some drivers that assign values to the array.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCH] netem: add limitation to reordered packets
From: Eric Dumazet @ 2012-07-04 6:55 UTC (permalink / raw)
To: David Miller
Cc: netdev, Hagen Paul Pfeifer, Mark Gordon, Andreas Terzis,
Yuchung Cheng
From: Eric Dumazet <edumazet@google.com>
Fix two netem bugs :
1) When a frame was dropped by tfifo_enqueue(), drop counter
was incremented twice.
2) When reordering is triggered, we enqueue a packet without
checking queue limit. This can OOM pretty fast when this
is repeated enough, since skbs are orphaned, no socket limit
can help in this situation.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Mark Gordon <msg@google.com>
Cc: Andreas Terzis <aterzis@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Cc: Hagen Paul Pfeifer <hagen@jauu.net>
---
net/sched/sch_netem.c | 42 ++++++++++++++--------------------------
1 file changed, 15 insertions(+), 27 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index a2a95aa..c412ad0 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -331,29 +331,22 @@ static psched_time_t packet_len_2_sched_time(unsigned int len, struct netem_sche
return PSCHED_NS2TICKS(ticks);
}
-static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
+static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
{
struct sk_buff_head *list = &sch->q;
psched_time_t tnext = netem_skb_cb(nskb)->time_to_send;
- struct sk_buff *skb;
-
- if (likely(skb_queue_len(list) < sch->limit)) {
- skb = skb_peek_tail(list);
- /* Optimize for add at tail */
- if (likely(!skb || tnext >= netem_skb_cb(skb)->time_to_send))
- return qdisc_enqueue_tail(nskb, sch);
+ struct sk_buff *skb = skb_peek_tail(list);
- skb_queue_reverse_walk(list, skb) {
- if (tnext >= netem_skb_cb(skb)->time_to_send)
- break;
- }
+ /* Optimize for add at tail */
+ if (likely(!skb || tnext >= netem_skb_cb(skb)->time_to_send))
+ return __skb_queue_tail(list, nskb);
- __skb_queue_after(list, skb, nskb);
- sch->qstats.backlog += qdisc_pkt_len(nskb);
- return NET_XMIT_SUCCESS;
+ skb_queue_reverse_walk(list, skb) {
+ if (tnext >= netem_skb_cb(skb)->time_to_send)
+ break;
}
- return qdisc_reshape_fail(nskb, sch);
+ __skb_queue_after(list, skb, nskb);
}
/*
@@ -368,7 +361,6 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
/* We don't fill cb now as skb_unshare() may invalidate it */
struct netem_skb_cb *cb;
struct sk_buff *skb2;
- int ret;
int count = 1;
/* Random duplication */
@@ -419,6 +411,11 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
skb->data[net_random() % skb_headlen(skb)] ^= 1<<(net_random() % 8);
}
+ if (unlikely(skb_queue_len(&sch->q) >= sch->limit))
+ return qdisc_reshape_fail(skb, sch);
+
+ sch->qstats.backlog += qdisc_pkt_len(skb);
+
cb = netem_skb_cb(skb);
if (q->gap == 0 || /* not doing reordering */
q->counter < q->gap - 1 || /* inside last reordering gap */
@@ -450,7 +447,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
cb->time_to_send = now + delay;
++q->counter;
- ret = tfifo_enqueue(skb, sch);
+ tfifo_enqueue(skb, sch);
} else {
/*
* Do re-ordering by putting one out of N packets at the front
@@ -460,16 +457,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
q->counter = 0;
__skb_queue_head(&sch->q, skb);
- sch->qstats.backlog += qdisc_pkt_len(skb);
sch->qstats.requeues++;
- ret = NET_XMIT_SUCCESS;
- }
-
- if (ret != NET_XMIT_SUCCESS) {
- if (net_xmit_drop_count(ret)) {
- sch->qstats.drops++;
- return ret;
- }
}
return NET_XMIT_SUCCESS;
^ permalink raw reply related
* Re: [PATCH 2/2] net: add support for NS8390 based eth controllers on some ColdFire CPU boards
From: Joe Perches @ 2012-07-04 6:39 UTC (permalink / raw)
To: Greg Ungerer; +Cc: netdev, linux-m68k, Greg Ungerer
In-Reply-To: <4FF3E031.5040703@snapgear.com>
On Wed, 2012-07-04 at 16:18 +1000, Greg Ungerer wrote:
> Hi Joe,
Hi Greg.
> On 04/07/12 15:18, Joe Perches wrote:
> > On Wed, 2012-07-04 at 14:56 +1000, gerg@snapgear.com wrote:
> >> From: Greg Ungerer <gerg@uclinux.org>
> >>
> >> A number of older ColdFire CPU based boards use NS8390 based network
> >> controllers. Most use the Davicom 9008F or the UMC 9008F. This driver
> >> provides the support code to get these devices working on these platforms.
[]
> >> diff --git a/drivers/net/ethernet/8390/mcf8390.c b/drivers/net/ethernet/8390/mcf8390.c
[]
> >> +static int mcf8390_init(struct net_device *dev)
> >> +{
> >> + static u32 offsets[] = {
> >> + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
> >> + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
> >> + };
> >
> > const? u8?
>
> That is assigned to the "reg_offset" field of "struct ei_device"
> (defined in the existing 8390.h) and that is:
>
> u32 *reg_offset; /* Register mapping table */
>
> So I can't change this.
Sure you can, you can assign a u8 to a u32 just fine.
The value fits in a u8. Might as well make it take
less data space. static const makes it read-only and
makes the compiler assign it just once instead of at
function invocation.
cheers, Joe
^ permalink raw reply
* Re: [PATCH 2/2] net: add support for NS8390 based eth controllers on some ColdFire CPU boards
From: Greg Ungerer @ 2012-07-04 6:18 UTC (permalink / raw)
To: Joe Perches; +Cc: netdev, linux-m68k, Greg Ungerer
In-Reply-To: <1341379091.3627.15.camel@joe2Laptop>
Hi Joe,
On 04/07/12 15:18, Joe Perches wrote:
> On Wed, 2012-07-04 at 14:56 +1000, gerg@snapgear.com wrote:
>> From: Greg Ungerer <gerg@uclinux.org>
>>
>> A number of older ColdFire CPU based boards use NS8390 based network
>> controllers. Most use the Davicom 9008F or the UMC 9008F. This driver
>> provides the support code to get these devices working on these platforms.
>
> Hi Greg, just some trivia:
Thanks for the quick feedback!
> []
>
>> diff --git a/drivers/net/ethernet/8390/mcf8390.c b/drivers/net/ethernet/8390/mcf8390.c
>
> []
>
>> +#ifdef NE2000_ODDOFFSET
>> +/*
>> + * A lot of the ColdFire boards use a separate address region for odd offset
>> + * register addresses. The following macros and functions convert and map
>> + * as required. Note that the data port accesses are treated a little
>> + * differently, and always accessed via the insX/outsX functions.
>> + */
>> +#define NE_PTR(a) (((a) & 0x1) ? (NE2000_ODDOFFSET + (a) - 1) : (a))
>
> Maybe use static inlines instead of macros?
>
> static inline void *NE_PTR(void *ptr)
> {
> if ((unsigned long)ptr & 1)
> return ptr - 1 + NE2000_ODDOFFSET;
> return ptr;
> }
Ok, that looks better. Though I might make the arg u32, which matches
the way that the address is passed to the IO functions currently.
> []
>
>> +static void mcf8390_get_8390_hdr(struct net_device *dev,
>> + struct e8390_pkt_hdr *hdr, int ring_page)
>> +{
> []
>> + /*
>> + * This *shouldn't* happen.
>> + * If it does, it's the last thing you'll see
>> + */
>> + if (ei_status.dmaing) {
>> + netdev_err(dev,
>> + "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
>> + __func__, ei_local->dmaing, ei_local->irqlock);
>
> This message seems to get repeated a few times.
> Maybe another function/macro or maybe a BUG?
>
> some_dma_err(dev, __func__, ei_local);
Yep, sure thing. I copied that part "as is", and a few of the other
drivers seem to have it done this way too. No excuses though :-)
>> +static void mcf8390_block_input(struct net_device *dev, int count,
>> + struct sk_buff *skb, int ring_offset)
>> +{
> []
>> + /*
>> + * This *shouldn't* happen.
>> + * If it does, it's the last thing you'll see
>> + */
>> + if (ei_local->dmaing) {
>> + netdev_err(dev,
>> + "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
>> + __func__, ei_local->dmaing, ei_local->irqlock);
>> + return;
>> + }
>
>> +static void mcf8390_block_output(struct net_device *dev, int count,
>> + const unsigned char *buf,
>> + const int start_page)
>> +{
> []
>> + /*
>> + *This *shouldn't* happen.
>> + * If it does, it's the last thing you'll see
>> + */
>> + if (ei_local->dmaing) {
>> + netdev_err(dev,
>> + "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
>> + __func__, ei_local->dmaing, ei_local->irqlock);
>> + return;
>> + }
>
>> +static int mcf8390_init(struct net_device *dev)
>> +{
>> + static u32 offsets[] = {
>> + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
>> + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
>> + };
>
> const? u8?
That is assigned to the "reg_offset" field of "struct ei_device"
(defined in the existing 8390.h) and that is:
u32 *reg_offset; /* Register mapping table */
So I can't change this.
>> + pr_debug("Found ethernet address: %pM\n", dev->dev_addr);
>
> netdev_dbg ?
Sure, will change it.
Thanks for the quick feedback, much appreciated.
I'll send a revised patch in 24 hours or so.
Regards
Greg
------------------------------------------------------------------------
Greg Ungerer -- Principal Engineer EMAIL: gerg@snapgear.com
SnapGear Group, McAfee PHONE: +61 7 3435 2888
8 Gardner Close FAX: +61 7 3217 5323
Milton, QLD, 4064, Australia WEB: http://www.SnapGear.com
^ permalink raw reply
* Re: [PATCH] netem: fix rate extension and drop accounting
From: Eric Dumazet @ 2012-07-04 5:58 UTC (permalink / raw)
To: Hagen Paul Pfeifer; +Cc: netdev, Yuchung Cheng, Andreas Terzis, Mark Gordon
In-Reply-To: <20120703220442.GC5064@nuttenaction>
On Wed, 2012-07-04 at 00:04 +0200, Hagen Paul Pfeifer wrote:
> Strange, we test the patch in detail. I will take a look ...
I tried to fix the thing but lacked time yesterday.
I had to use the good old way for my tests.
DEV=eth0
tc qdisc del dev $DEV root
tc qdisc add dev $DEV root handle 30: est 1sec 4sec netem \
delay 100ms 10ms reorder 3
tc qdisc add dev $DEV handle 40:0 parent 30:0 tbf \
burst 5000 limit 10000 mtu 1514 rate 100kbit
tc qdisc add dev $DEV handle 50:00 parent 40:0 pfifo limit 200
fundamentally, mixing the TBF is going to be hard with "delay ..."
especially with jitter.
Same problem for reorder : since packets are put at head of queue,
they have no effect on the 'time_to_send' of packets already in queue
and netem use more bandwidth than allowed.
I'll send the patch on the double drop accounting problem because the
fix is easy enough, but fir the rate limiting, I prefer letting you work
on it if you dont mind ?
Thanks
^ permalink raw reply
* Re: [PATCH 2/2] net: add support for NS8390 based eth controllers on some ColdFire CPU boards
From: Joe Perches @ 2012-07-04 5:18 UTC (permalink / raw)
To: gerg; +Cc: netdev, linux-m68k, Greg Ungerer
In-Reply-To: <1341377789-12970-3-git-send-email-gerg@snapgear.com>
On Wed, 2012-07-04 at 14:56 +1000, gerg@snapgear.com wrote:
> From: Greg Ungerer <gerg@uclinux.org>
>
> A number of older ColdFire CPU based boards use NS8390 based network
> controllers. Most use the Davicom 9008F or the UMC 9008F. This driver
> provides the support code to get these devices working on these platforms.
Hi Greg, just some trivia:
[]
> diff --git a/drivers/net/ethernet/8390/mcf8390.c b/drivers/net/ethernet/8390/mcf8390.c
[]
> +#ifdef NE2000_ODDOFFSET
> +/*
> + * A lot of the ColdFire boards use a separate address region for odd offset
> + * register addresses. The following macros and functions convert and map
> + * as required. Note that the data port accesses are treated a little
> + * differently, and always accessed via the insX/outsX functions.
> + */
> +#define NE_PTR(a) (((a) & 0x1) ? (NE2000_ODDOFFSET + (a) - 1) : (a))
Maybe use static inlines instead of macros?
static inline void *NE_PTR(void *ptr)
{
if ((unsigned long)ptr & 1)
return ptr - 1 + NE2000_ODDOFFSET;
return ptr;
}
[]
> +static void mcf8390_get_8390_hdr(struct net_device *dev,
> + struct e8390_pkt_hdr *hdr, int ring_page)
> +{
[]
> + /*
> + * This *shouldn't* happen.
> + * If it does, it's the last thing you'll see
> + */
> + if (ei_status.dmaing) {
> + netdev_err(dev,
> + "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
> + __func__, ei_local->dmaing, ei_local->irqlock);
This message seems to get repeated a few times.
Maybe another function/macro or maybe a BUG?
some_dma_err(dev, __func__, ei_local);
> +static void mcf8390_block_input(struct net_device *dev, int count,
> + struct sk_buff *skb, int ring_offset)
> +{
[]
> + /*
> + * This *shouldn't* happen.
> + * If it does, it's the last thing you'll see
> + */
> + if (ei_local->dmaing) {
> + netdev_err(dev,
> + "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
> + __func__, ei_local->dmaing, ei_local->irqlock);
> + return;
> + }
> +static void mcf8390_block_output(struct net_device *dev, int count,
> + const unsigned char *buf,
> + const int start_page)
> +{
[]
> + /*
> + *This *shouldn't* happen.
> + * If it does, it's the last thing you'll see
> + */
> + if (ei_local->dmaing) {
> + netdev_err(dev,
> + "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
> + __func__, ei_local->dmaing, ei_local->irqlock);
> + return;
> + }
> +static int mcf8390_init(struct net_device *dev)
> +{
> + static u32 offsets[] = {
> + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
> + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
> + };
const? u8?
> + pr_debug("Found ethernet address: %pM\n", dev->dev_addr);
netdev_dbg ?
^ permalink raw reply
* [PATCH 2/2] net: add support for NS8390 based eth controllers on some ColdFire CPU boards
From: gerg @ 2012-07-04 4:56 UTC (permalink / raw)
To: netdev, linux-m68k; +Cc: Greg Ungerer
In-Reply-To: <1341377789-12970-1-git-send-email-gerg@snapgear.com>
From: Greg Ungerer <gerg@uclinux.org>
A number of older ColdFire CPU based boards use NS8390 based network
controllers. Most use the Davicom 9008F or the UMC 9008F. This driver
provides the support code to get these devices working on these platforms.
Generally the NS8390 based eth device is direct connected via the general
purpose bus of the ColdFire CPU. So its addressing and interrupt setup is
fixed on each of the different platforms (classic platform setup).
This driver is based on the other drivers/net/ethernet/8390 drivers, and
includes the lib8390.c code. It uses the existing definitions of the
board NS8390 device addresses, interrupts and access types from the
arch/m68k/include/asm/mcf8390.h, but moves the IO access functions into
the driver code and out of that header.
Signed-off-by: Greg Ungerer <gerg@uclinux.org>
---
arch/m68k/include/asm/mcf8390.h | 129 +---------
drivers/net/ethernet/8390/Kconfig | 14 +
drivers/net/ethernet/8390/Makefile | 1 +
drivers/net/ethernet/8390/mcf8390.c | 478 +++++++++++++++++++++++++++++++++++
4 files changed, 502 insertions(+), 120 deletions(-)
create mode 100644 drivers/net/ethernet/8390/mcf8390.c
diff --git a/arch/m68k/include/asm/mcf8390.h b/arch/m68k/include/asm/mcf8390.h
index bf3d97b..a72a208 100644
--- a/arch/m68k/include/asm/mcf8390.h
+++ b/arch/m68k/include/asm/mcf8390.h
@@ -37,6 +37,7 @@
#if defined(CONFIG_ARN5206)
#define NE2000_ADDR 0x40000300
#define NE2000_ODDOFFSET 0x00010000
+#define NE2000_ADDRSIZE 0x00020000
#define NE2000_IRQ_VECTOR 0xf0
#define NE2000_IRQ_PRIORITY 2
#define NE2000_IRQ_LEVEL 4
@@ -46,6 +47,7 @@
#if defined(CONFIG_M5206eC3)
#define NE2000_ADDR 0x40000300
#define NE2000_ODDOFFSET 0x00010000
+#define NE2000_ADDRSIZE 0x00020000
#define NE2000_IRQ_VECTOR 0x1c
#define NE2000_IRQ_PRIORITY 2
#define NE2000_IRQ_LEVEL 4
@@ -54,6 +56,7 @@
#if defined(CONFIG_M5206e) && defined(CONFIG_NETtel)
#define NE2000_ADDR 0x30000300
+#define NE2000_ADDRSIZE 0x00001000
#define NE2000_IRQ_VECTOR 25
#define NE2000_IRQ_PRIORITY 1
#define NE2000_IRQ_LEVEL 3
@@ -63,6 +66,7 @@
#if defined(CONFIG_M5307C3)
#define NE2000_ADDR 0x40000300
#define NE2000_ODDOFFSET 0x00010000
+#define NE2000_ADDRSIZE 0x00020000
#define NE2000_IRQ_VECTOR 0x1b
#define NE2000_BYTE volatile unsigned short
#endif
@@ -70,6 +74,7 @@
#if defined(CONFIG_M5272) && defined(CONFIG_NETtel)
#define NE2000_ADDR 0x30600300
#define NE2000_ODDOFFSET 0x00008000
+#define NE2000_ADDRSIZE 0x00010000
#define NE2000_IRQ_VECTOR 67
#undef BSWAP
#define BSWAP(w) (w)
@@ -82,6 +87,7 @@
#define NE2000_ADDR0 0x30600300
#define NE2000_ADDR1 0x30800300
#define NE2000_ODDOFFSET 0x00008000
+#define NE2000_ADDRSIZE 0x00010000
#define NE2000_IRQ_VECTOR0 27
#define NE2000_IRQ_VECTOR1 29
#undef BSWAP
@@ -94,6 +100,7 @@
#if defined(CONFIG_M5307) && defined(CONFIG_SECUREEDGEMP3)
#define NE2000_ADDR 0x30600300
#define NE2000_ODDOFFSET 0x00008000
+#define NE2000_ADDRSIZE 0x00010000
#define NE2000_IRQ_VECTOR 27
#undef BSWAP
#define BSWAP(w) (w)
@@ -105,6 +112,7 @@
#if defined(CONFIG_ARN5307)
#define NE2000_ADDR 0xfe600300
#define NE2000_ODDOFFSET 0x00010000
+#define NE2000_ADDRSIZE 0x00020000
#define NE2000_IRQ_VECTOR 0x1b
#define NE2000_IRQ_PRIORITY 2
#define NE2000_IRQ_LEVEL 3
@@ -114,129 +122,10 @@
#if defined(CONFIG_M5407C3)
#define NE2000_ADDR 0x40000300
#define NE2000_ODDOFFSET 0x00010000
+#define NE2000_ADDRSIZE 0x00020000
#define NE2000_IRQ_VECTOR 0x1b
#define NE2000_BYTE volatile unsigned short
#endif
/****************************************************************************/
-
-/*
- * Side-band address space for odd address requires re-mapping
- * many of the standard ISA access functions.
- */
-#ifdef NE2000_ODDOFFSET
-
-#undef outb
-#undef outb_p
-#undef inb
-#undef inb_p
-#undef outsb
-#undef outsw
-#undef insb
-#undef insw
-
-#define outb ne2000_outb
-#define inb ne2000_inb
-#define outb_p ne2000_outb
-#define inb_p ne2000_inb
-#define outsb ne2000_outsb
-#define outsw ne2000_outsw
-#define insb ne2000_insb
-#define insw ne2000_insw
-
-
-#ifndef COLDFIRE_NE2000_FUNCS
-
-void ne2000_outb(unsigned int val, unsigned int addr);
-int ne2000_inb(unsigned int addr);
-void ne2000_insb(unsigned int addr, void *vbuf, int unsigned long len);
-void ne2000_insw(unsigned int addr, void *vbuf, unsigned long len);
-void ne2000_outsb(unsigned int addr, void *vbuf, unsigned long len);
-void ne2000_outsw(unsigned int addr, void *vbuf, unsigned long len);
-
-#else
-
-/*
- * This macro converts a conventional register address into the
- * real memory pointer of the mapped NE2000 device.
- * On most NE2000 implementations on ColdFire boards the chip is
- * mapped in kinda funny, due to its ISA heritage.
- */
-#define NE2000_PTR(addr) ((addr&0x1)?(NE2000_ODDOFFSET+addr-1):(addr))
-#define NE2000_DATA_PTR(addr) (addr)
-
-
-void ne2000_outb(unsigned int val, unsigned int addr)
-{
- NE2000_BYTE *rp;
-
- rp = (NE2000_BYTE *) NE2000_PTR(addr);
- *rp = RSWAP(val);
-}
-
-int ne2000_inb(unsigned int addr)
-{
- NE2000_BYTE *rp, val;
-
- rp = (NE2000_BYTE *) NE2000_PTR(addr);
- val = *rp;
- return((int) ((NE2000_BYTE) RSWAP(val)));
-}
-
-void ne2000_insb(unsigned int addr, void *vbuf, int unsigned long len)
-{
- NE2000_BYTE *rp, val;
- unsigned char *buf;
-
- buf = (unsigned char *) vbuf;
- rp = (NE2000_BYTE *) NE2000_DATA_PTR(addr);
- for (; (len > 0); len--) {
- val = *rp;
- *buf++ = RSWAP(val);
- }
-}
-
-void ne2000_insw(unsigned int addr, void *vbuf, unsigned long len)
-{
- volatile unsigned short *rp;
- unsigned short w, *buf;
-
- buf = (unsigned short *) vbuf;
- rp = (volatile unsigned short *) NE2000_DATA_PTR(addr);
- for (; (len > 0); len--) {
- w = *rp;
- *buf++ = BSWAP(w);
- }
-}
-
-void ne2000_outsb(unsigned int addr, const void *vbuf, unsigned long len)
-{
- NE2000_BYTE *rp, val;
- unsigned char *buf;
-
- buf = (unsigned char *) vbuf;
- rp = (NE2000_BYTE *) NE2000_DATA_PTR(addr);
- for (; (len > 0); len--) {
- val = *buf++;
- *rp = RSWAP(val);
- }
-}
-
-void ne2000_outsw(unsigned int addr, const void *vbuf, unsigned long len)
-{
- volatile unsigned short *rp;
- unsigned short w, *buf;
-
- buf = (unsigned short *) vbuf;
- rp = (volatile unsigned short *) NE2000_DATA_PTR(addr);
- for (; (len > 0); len--) {
- w = *buf++;
- *rp = BSWAP(w);
- }
-}
-
-#endif /* COLDFIRE_NE2000_FUNCS */
-#endif /* NE2000_OFFOFFSET */
-
-/****************************************************************************/
#endif /* mcf8390_h */
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
index 2e53867..e1219e0 100644
--- a/drivers/net/ethernet/8390/Kconfig
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -162,6 +162,20 @@ config MAC8390
and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
+config MCF8390
+ tristate "ColdFire NS8390 based Ethernet support"
+ depends on COLDFIRE
+ select CRC32
+ ---help---
+ This driver is for Ethernet devices using an NS8390-compatible
+ chipset on many common ColdFire CPU based boards. Many of the older
+ Freescale dev boards use this, and some other common boards like
+ some SnapGear routers do as well.
+
+ If you have one of these boards and want to use the network interface
+ on them then choose Y. To compile this driver as a module, choose M
+ here, the module will be called mcf8390.
+
config NE2000
tristate "NE2000/NE1000 support"
depends on (ISA || (Q40 && m) || M32R || MACH_TX49XX)
diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile
index d13790b..f43038b 100644
--- a/drivers/net/ethernet/8390/Makefile
+++ b/drivers/net/ethernet/8390/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_HPLAN_PLUS) += hp-plus.o 8390p.o
obj-$(CONFIG_HPLAN) += hp.o 8390p.o
obj-$(CONFIG_HYDRA) += hydra.o 8390.o
obj-$(CONFIG_LNE390) += lne390.o 8390.o
+obj-$(CONFIG_MCF8390) += mcf8390.o 8390.o
obj-$(CONFIG_NE2000) += ne.o 8390p.o
obj-$(CONFIG_NE2_MCA) += ne2.o 8390p.o
obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
diff --git a/drivers/net/ethernet/8390/mcf8390.c b/drivers/net/ethernet/8390/mcf8390.c
new file mode 100644
index 0000000..12b3d40
--- /dev/null
+++ b/drivers/net/ethernet/8390/mcf8390.c
@@ -0,0 +1,478 @@
+/*
+ * Support for ColdFire CPU based boards using a NS8390 Ethernet device.
+ *
+ * Derived from the many other 8390 drivers.
+ *
+ * (C) Copyright 2012, Greg Ungerer <gerg@uclinux.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <asm/mcf8390.h>
+
+static const char version[] =
+ "mcf8390.c: (15-06-2012) Greg Ungerer <gerg@uclinux.org>";
+
+#define NE_CMD 0x00
+#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset */
+#define NE_RESET 0x1f /* Issue a read to reset ,a write to clear */
+#define NE_EN0_ISR 0x07
+#define NE_EN0_DCFG 0x0e
+#define NE_EN0_RSARLO 0x08
+#define NE_EN0_RSARHI 0x09
+#define NE_EN0_RCNTLO 0x0a
+#define NE_EN0_RXCR 0x0c
+#define NE_EN0_TXCR 0x0d
+#define NE_EN0_RCNTHI 0x0b
+#define NE_EN0_IMR 0x0f
+
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+#ifdef NE2000_ODDOFFSET
+/*
+ * A lot of the ColdFire boards use a separate address region for odd offset
+ * register addresses. The following macros and functions convert and map
+ * as required. Note that the data port accesses are treated a little
+ * differently, and always accessed via the insX/outsX functions.
+ */
+#define NE_PTR(a) (((a) & 0x1) ? (NE2000_ODDOFFSET + (a) - 1) : (a))
+#define NE_DATA_PTR(a) (a)
+
+void ei_outb(u32 val, u32 addr)
+{
+ NE2000_BYTE *rp;
+
+ rp = (NE2000_BYTE *) NE_PTR(addr);
+ *rp = RSWAP(val);
+}
+
+#define ei_inb ei_inb
+u8 ei_inb(u32 addr)
+{
+ NE2000_BYTE *rp, val;
+
+ rp = (NE2000_BYTE *) NE_PTR(addr);
+ val = *rp;
+ return (u8) (RSWAP(val) & 0xff);
+}
+
+void ei_insb(u32 addr, void *vbuf, int len)
+{
+ NE2000_BYTE *rp, val;
+ u8 *buf;
+
+ buf = (u8 *) vbuf;
+ rp = (NE2000_BYTE *) NE_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ val = *rp;
+ *buf++ = RSWAP(val);
+ }
+}
+
+void ei_insw(u32 addr, void *vbuf, int len)
+{
+ volatile u16 *rp;
+ u16 w, *buf;
+
+ buf = (u16 *) vbuf;
+ rp = (volatile u16 *) NE_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ w = *rp;
+ *buf++ = BSWAP(w);
+ }
+}
+
+void ei_outsb(u32 addr, const void *vbuf, int len)
+{
+ NE2000_BYTE *rp, val;
+ u8 *buf;
+
+ buf = (u8 *) vbuf;
+ rp = (NE2000_BYTE *) NE_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ val = *buf++;
+ *rp = RSWAP(val);
+ }
+}
+
+void ei_outsw(u32 addr, const void *vbuf, int len)
+{
+ volatile u16 *rp;
+ u16 w, *buf;
+
+ buf = (u16 *) vbuf;
+ rp = (volatile u16 *) NE_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ w = *buf++;
+ *rp = BSWAP(w);
+ }
+}
+
+#else /* !NE2000_ODDOFFSET */
+
+#define ei_inb inb
+#define ei_outb outb
+#define ei_insb insb
+#define ei_insw insw
+#define ei_outsb outsb
+#define ei_outsw outsw
+
+#endif /* !NE2000_ODDOFFSET */
+
+#define ei_inb_p ei_inb
+#define ei_outb_p ei_outb
+
+#include "lib8390.c"
+
+/*
+ * Hard reset the card. This used to pause for the same period that a
+ * 8390 reset command required, but that shouldn't be necessary.
+ */
+static void mcf8390_reset_8390(struct net_device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+ u32 addr = dev->base_addr;
+
+ if (ei_debug > 1)
+ netdev_dbg(dev, "resetting the 8390 t=%ld...\n", jiffies);
+
+ ei_outb(ei_inb(addr + NE_RESET), addr + NE_RESET);
+
+ ei_status.txing = 0;
+ ei_status.dmaing = 0;
+
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((ei_inb(addr + NE_EN0_ISR) & ENISR_RESET) == 0) {
+ if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
+ netdev_warn(dev, "%s: did not complete\n", __func__);
+ break;
+ }
+ }
+
+ ei_outb(ENISR_RESET, addr + NE_EN0_ISR);
+}
+
+/*
+ * Grab the 8390 specific header. Similar to the block_input routine, but
+ * we don't need to be concerned with ring wrap as the header will be at
+ * the start of a page, so we optimize accordingly.
+ */
+static void mcf8390_get_8390_hdr(struct net_device *dev,
+ struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ u32 addr = dev->base_addr;
+
+ /*
+ * This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_status.dmaing) {
+ netdev_err(dev,
+ "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+ ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, addr + NE_CMD);
+ ei_outb(ENISR_RDC, addr + NE_EN0_ISR);
+ ei_outb(sizeof(struct e8390_pkt_hdr), addr + NE_EN0_RCNTLO);
+ ei_outb(0, addr + NE_EN0_RCNTHI);
+ ei_outb(0, addr + NE_EN0_RSARLO); /* On page boundary */
+ ei_outb(ring_page, addr + NE_EN0_RSARHI);
+ ei_outb(E8390_RREAD + E8390_START, addr + NE_CMD);
+
+ ei_insw(addr + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr) >> 1);
+
+ outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */
+ ei_local->dmaing &= ~0x01;
+
+ hdr->count = cpu_to_le16(hdr->count);
+}
+
+/*
+ * Block input and output, similar to the Crynwr packet driver.
+ * If you are porting to a new ethercard, look at the packet driver source
+ * for hints. The NEx000 doesn't share the on-board packet memory --
+ * you have to put the packet out through the "remote DMA" dataport
+ * using z_writeb.
+ */
+static void mcf8390_block_input(struct net_device *dev, int count,
+ struct sk_buff *skb, int ring_offset)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ u32 addr = dev->base_addr;
+ char *buf = skb->data;
+
+ /*
+ * This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_local->dmaing) {
+ netdev_err(dev,
+ "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+ ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, addr + NE_CMD);
+ ei_outb(ENISR_RDC, addr + NE_EN0_ISR);
+ ei_outb(count & 0xff, addr + NE_EN0_RCNTLO);
+ ei_outb(count >> 8, addr + NE_EN0_RCNTHI);
+ ei_outb(ring_offset & 0xff, addr + NE_EN0_RSARLO);
+ ei_outb(ring_offset >> 8, addr + NE_EN0_RSARHI);
+ ei_outb(E8390_RREAD + E8390_START, addr + NE_CMD);
+
+ ei_insw(addr + NE_DATAPORT, buf, count >> 1);
+ if (count & 1)
+ buf[count - 1] = ei_inb(addr + NE_DATAPORT);
+
+ ei_outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */
+ ei_local->dmaing &= ~0x01;
+}
+
+static void mcf8390_block_output(struct net_device *dev, int count,
+ const unsigned char *buf,
+ const int start_page)
+{
+ struct ei_device *ei_local = netdev_priv(dev);
+ u32 addr = dev->base_addr;
+ unsigned long dma_start;
+
+ /* Make sure we transfer all bytes if 16bit IO writes */
+ if (count & 0x1)
+ count++;
+
+ /*
+ *This *shouldn't* happen.
+ * If it does, it's the last thing you'll see
+ */
+ if (ei_local->dmaing) {
+ netdev_err(dev,
+ "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+ __func__, ei_local->dmaing, ei_local->irqlock);
+ return;
+ }
+
+ ei_local->dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ ei_outb(E8390_PAGE0 + E8390_START + E8390_NODMA, addr + NE_CMD);
+
+ ei_outb(ENISR_RDC, addr + NE_EN0_ISR);
+
+ /* Now the normal output. */
+ ei_outb(count & 0xff, addr + NE_EN0_RCNTLO);
+ ei_outb(count >> 8, addr + NE_EN0_RCNTHI);
+ ei_outb(0x00, addr + NE_EN0_RSARLO);
+ ei_outb(start_page, addr + NE_EN0_RSARHI);
+ ei_outb(E8390_RWRITE + E8390_START, addr + NE_CMD);
+
+ ei_outsw(addr + NE_DATAPORT, buf, count >> 1);
+
+ dma_start = jiffies;
+ while ((ei_inb(addr + NE_EN0_ISR) & ENISR_RDC) == 0) {
+ if (time_after(jiffies, dma_start + 2 * HZ / 100)) { /* 20ms */
+ netdev_err(dev, "timeout waiting for Tx RDC\n");
+ mcf8390_reset_8390(dev);
+ __NS8390_init(dev, 1);
+ break;
+ }
+ }
+
+ ei_outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */
+ ei_local->dmaing &= ~0x01;
+}
+
+static const struct net_device_ops mcf8390_netdev_ops = {
+ .ndo_open = __ei_open,
+ .ndo_stop = __ei_close,
+ .ndo_start_xmit = __ei_start_xmit,
+ .ndo_tx_timeout = __ei_tx_timeout,
+ .ndo_get_stats = __ei_get_stats,
+ .ndo_set_rx_mode = __ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = __ei_poll,
+#endif
+};
+
+static int mcf8390_init(struct net_device *dev)
+{
+ static u32 offsets[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ };
+ struct ei_device *ei_local = netdev_priv(dev);
+ unsigned char SA_prom[32];
+ u32 addr = dev->base_addr;
+ int start_page, stop_page;
+ int i, ret;
+
+ mcf8390_reset_8390(dev);
+
+ /*
+ * Read the 16 bytes of station address PROM.
+ * We must first initialize registers,
+ * similar to NS8390_init(eifdev, 0).
+ * We can't reliably read the SAPROM address without this.
+ * (I learned the hard way!).
+ */
+ {
+ static const struct {
+ u32 value;
+ u32 offset;
+ } program_seq[] = {
+ {E8390_NODMA + E8390_PAGE0 + E8390_STOP, NE_CMD},
+ /* Select page 0 */
+ {0x48, NE_EN0_DCFG}, /* 0x48: Set byte-wide access */
+ {0x00, NE_EN0_RCNTLO}, /* Clear the count regs */
+ {0x00, NE_EN0_RCNTHI},
+ {0x00, NE_EN0_IMR}, /* Mask completion irq */
+ {0xFF, NE_EN0_ISR},
+ {E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */
+ {E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode */
+ {32, NE_EN0_RCNTLO},
+ {0x00, NE_EN0_RCNTHI},
+ {0x00, NE_EN0_RSARLO}, /* DMA starting at 0x0000 */
+ {0x00, NE_EN0_RSARHI},
+ {E8390_RREAD + E8390_START, NE_CMD},
+ };
+ for (i = 0; i < ARRAY_SIZE(program_seq); i++) {
+ ei_outb(program_seq[i].value,
+ addr + program_seq[i].offset);
+ }
+ }
+
+ for (i = 0; i < 16; i++) {
+ SA_prom[i] = ei_inb(addr + NE_DATAPORT);
+ ei_inb(addr + NE_DATAPORT);
+ }
+
+ /* We must set the 8390 for word mode. */
+ ei_outb(0x49, addr + NE_EN0_DCFG);
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+
+ /* Install the Interrupt handler */
+ ret = request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] = SA_prom[i];
+
+ pr_debug("Found ethernet address: %pM\n", dev->dev_addr);
+
+ ei_local->name = "mcf8390";
+ ei_local->tx_start_page = start_page;
+ ei_local->stop_page = stop_page;
+ ei_local->word16 = 1;
+ ei_local->rx_start_page = start_page + TX_PAGES;
+ ei_local->reset_8390 = mcf8390_reset_8390;
+ ei_local->block_input = mcf8390_block_input;
+ ei_local->block_output = mcf8390_block_output;
+ ei_local->get_8390_hdr = mcf8390_get_8390_hdr;
+ ei_local->reg_offset = offsets;
+
+ dev->netdev_ops = &mcf8390_netdev_ops;
+ __NS8390_init(dev, 0);
+ ret = register_netdev(dev);
+ if (ret) {
+ free_irq(dev->irq, dev);
+ return ret;
+ }
+
+ netdev_info(dev, "addr=0x%08x irq=%d, Ethernet Address %pM\n",
+ addr, dev->irq, dev->dev_addr);
+ return 0;
+}
+
+static int mcf8390_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct ei_device *ei_local;
+ struct resource *mem, *irq;
+ resource_size_t msize;
+ int ret;
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (irq == NULL) {
+ dev_err(&pdev->dev, "no IRQ specified?\n");
+ return -ENXIO;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem == NULL) {
+ dev_err(&pdev->dev, "no memory address specified?\n");
+ return -ENXIO;
+ }
+ msize = resource_size(mem);
+ if (!request_mem_region(mem->start, msize, pdev->name))
+ return -EBUSY;
+
+ dev = ____alloc_ei_netdev(0);
+ if (dev == NULL) {
+ release_mem_region(mem->start, msize);
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ platform_set_drvdata(pdev, dev);
+ ei_local = netdev_priv(dev);
+
+ dev->irq = irq->start;
+ dev->base_addr = mem->start;
+
+ ret = mcf8390_init(dev);
+ if (ret) {
+ release_mem_region(mem->start, msize);
+ free_netdev(dev);
+ return ret;
+ }
+ return 0;
+}
+
+static int mcf8390_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ unregister_netdev(dev);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem)
+ release_mem_region(mem->start, resource_size(mem));
+ free_netdev(dev);
+ return 0;
+}
+
+static struct platform_driver mcf8390_drv = {
+ .driver = {
+ .name = "mcf8390",
+ .owner = THIS_MODULE,
+ },
+ .probe = mcf8390_probe,
+ .remove = mcf8390_remove,
+};
+
+module_platform_driver(mcf8390_drv);
+
+MODULE_DESCRIPTION("MCF8390 ColdFire NS8390 driver");
+MODULE_AUTHOR("Greg Ungerer <gerg@uclinux.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mcf8390");
--
1.7.0.4
^ permalink raw reply related
* [PATCH 0/2] net: support for NS8390 based ethernet on ColdFire CPU boards
From: gerg @ 2012-07-04 4:56 UTC (permalink / raw)
To: netdev, linux-m68k
The following 2 patches add platform support for using the NS8390 based
ethernet ports used on some ColdFire CPU boards. Patches to use these
have existed out-of-tree for years. Some of the base IO definitions
(those in arch/m68k/include/asm/mcfne.h) have been in mainline, but unused
for most of that time.
The first patch just neatens up mcfne.h (moving it to mcf8390.h). The
second patch is the platform driver. The first patch would normaly just
go through the m68knommu git tree, but I figured keeping these together made
more sense.
---
arch/m68k/include/asm/mcf8390.h | 371 ++++++++++++++++++---------
arch/m68k/include/asm/mcfne.h | 242 ------------------
drivers/net/ethernet/8390/Kconfig | 14 +
drivers/net/ethernet/8390/Makefile | 1
drivers/net/ethernet/8390/mcf8390.c | 478 ++++++++++++++++++++++++++++++++++++
5 files changed, 744 insertions(+), 362 deletions(-)
^ permalink raw reply
* [PATCH 1/2] m68knommu: move the badly named mcfne.h to a better mcf8390.h
From: gerg @ 2012-07-04 4:56 UTC (permalink / raw)
To: netdev, linux-m68k; +Cc: Greg Ungerer
In-Reply-To: <1341377789-12970-1-git-send-email-gerg@snapgear.com>
From: Greg Ungerer <gerg@uclinux.org>
The mcfne.h include contains definitions to support NS8390 eth based hardware
on ColdFire based CPU boards. So change its name to reflect that better.
Signed-off-by: Greg Ungerer <gerg@uclinux.org>
---
arch/m68k/include/asm/mcf8390.h | 242 +++++++++++++++++++++++++++++++++++++++
arch/m68k/include/asm/mcfne.h | 242 ---------------------------------------
2 files changed, 242 insertions(+), 242 deletions(-)
create mode 100644 arch/m68k/include/asm/mcf8390.h
delete mode 100644 arch/m68k/include/asm/mcfne.h
diff --git a/arch/m68k/include/asm/mcf8390.h b/arch/m68k/include/asm/mcf8390.h
new file mode 100644
index 0000000..bf3d97b
--- /dev/null
+++ b/arch/m68k/include/asm/mcf8390.h
@@ -0,0 +1,242 @@
+/****************************************************************************/
+
+/*
+ * mcf8390.h -- NS8390 support for ColdFire eval boards.
+ *
+ * (C) Copyright 1999-2000, Greg Ungerer (gerg@snapgear.com)
+ * (C) Copyright 2000, Lineo (www.lineo.com)
+ * (C) Copyright 2001, SnapGear (www.snapgear.com)
+ *
+ * 19990409 David W. Miller Converted from m5206ne.h for 5307 eval board
+ *
+ * Hacked support for m5206e Cadre III evaluation board
+ * Fred Stevens (fred.stevens@pemstar.com) 13 April 1999
+ */
+
+/****************************************************************************/
+#ifndef mcf8390_h
+#define mcf8390_h
+/****************************************************************************/
+
+
+/*
+ * Support for NE2000 clones devices in ColdFire based boards.
+ * Not all boards address these parts the same way, some use a
+ * direct addressing method, others use a side-band address space
+ * to access odd address registers, some require byte swapping
+ * others do not.
+ */
+#define BSWAP(w) (((w) << 8) | ((w) >> 8))
+#define RSWAP(w) (w)
+
+
+/*
+ * Define the basic hardware resources of NE2000 boards.
+ */
+
+#if defined(CONFIG_ARN5206)
+#define NE2000_ADDR 0x40000300
+#define NE2000_ODDOFFSET 0x00010000
+#define NE2000_IRQ_VECTOR 0xf0
+#define NE2000_IRQ_PRIORITY 2
+#define NE2000_IRQ_LEVEL 4
+#define NE2000_BYTE volatile unsigned short
+#endif
+
+#if defined(CONFIG_M5206eC3)
+#define NE2000_ADDR 0x40000300
+#define NE2000_ODDOFFSET 0x00010000
+#define NE2000_IRQ_VECTOR 0x1c
+#define NE2000_IRQ_PRIORITY 2
+#define NE2000_IRQ_LEVEL 4
+#define NE2000_BYTE volatile unsigned short
+#endif
+
+#if defined(CONFIG_M5206e) && defined(CONFIG_NETtel)
+#define NE2000_ADDR 0x30000300
+#define NE2000_IRQ_VECTOR 25
+#define NE2000_IRQ_PRIORITY 1
+#define NE2000_IRQ_LEVEL 3
+#define NE2000_BYTE volatile unsigned char
+#endif
+
+#if defined(CONFIG_M5307C3)
+#define NE2000_ADDR 0x40000300
+#define NE2000_ODDOFFSET 0x00010000
+#define NE2000_IRQ_VECTOR 0x1b
+#define NE2000_BYTE volatile unsigned short
+#endif
+
+#if defined(CONFIG_M5272) && defined(CONFIG_NETtel)
+#define NE2000_ADDR 0x30600300
+#define NE2000_ODDOFFSET 0x00008000
+#define NE2000_IRQ_VECTOR 67
+#undef BSWAP
+#define BSWAP(w) (w)
+#define NE2000_BYTE volatile unsigned short
+#undef RSWAP
+#define RSWAP(w) (((w) << 8) | ((w) >> 8))
+#endif
+
+#if defined(CONFIG_M5307) && defined(CONFIG_NETtel)
+#define NE2000_ADDR0 0x30600300
+#define NE2000_ADDR1 0x30800300
+#define NE2000_ODDOFFSET 0x00008000
+#define NE2000_IRQ_VECTOR0 27
+#define NE2000_IRQ_VECTOR1 29
+#undef BSWAP
+#define BSWAP(w) (w)
+#define NE2000_BYTE volatile unsigned short
+#undef RSWAP
+#define RSWAP(w) (((w) << 8) | ((w) >> 8))
+#endif
+
+#if defined(CONFIG_M5307) && defined(CONFIG_SECUREEDGEMP3)
+#define NE2000_ADDR 0x30600300
+#define NE2000_ODDOFFSET 0x00008000
+#define NE2000_IRQ_VECTOR 27
+#undef BSWAP
+#define BSWAP(w) (w)
+#define NE2000_BYTE volatile unsigned short
+#undef RSWAP
+#define RSWAP(w) (((w) << 8) | ((w) >> 8))
+#endif
+
+#if defined(CONFIG_ARN5307)
+#define NE2000_ADDR 0xfe600300
+#define NE2000_ODDOFFSET 0x00010000
+#define NE2000_IRQ_VECTOR 0x1b
+#define NE2000_IRQ_PRIORITY 2
+#define NE2000_IRQ_LEVEL 3
+#define NE2000_BYTE volatile unsigned short
+#endif
+
+#if defined(CONFIG_M5407C3)
+#define NE2000_ADDR 0x40000300
+#define NE2000_ODDOFFSET 0x00010000
+#define NE2000_IRQ_VECTOR 0x1b
+#define NE2000_BYTE volatile unsigned short
+#endif
+
+/****************************************************************************/
+
+/*
+ * Side-band address space for odd address requires re-mapping
+ * many of the standard ISA access functions.
+ */
+#ifdef NE2000_ODDOFFSET
+
+#undef outb
+#undef outb_p
+#undef inb
+#undef inb_p
+#undef outsb
+#undef outsw
+#undef insb
+#undef insw
+
+#define outb ne2000_outb
+#define inb ne2000_inb
+#define outb_p ne2000_outb
+#define inb_p ne2000_inb
+#define outsb ne2000_outsb
+#define outsw ne2000_outsw
+#define insb ne2000_insb
+#define insw ne2000_insw
+
+
+#ifndef COLDFIRE_NE2000_FUNCS
+
+void ne2000_outb(unsigned int val, unsigned int addr);
+int ne2000_inb(unsigned int addr);
+void ne2000_insb(unsigned int addr, void *vbuf, int unsigned long len);
+void ne2000_insw(unsigned int addr, void *vbuf, unsigned long len);
+void ne2000_outsb(unsigned int addr, void *vbuf, unsigned long len);
+void ne2000_outsw(unsigned int addr, void *vbuf, unsigned long len);
+
+#else
+
+/*
+ * This macro converts a conventional register address into the
+ * real memory pointer of the mapped NE2000 device.
+ * On most NE2000 implementations on ColdFire boards the chip is
+ * mapped in kinda funny, due to its ISA heritage.
+ */
+#define NE2000_PTR(addr) ((addr&0x1)?(NE2000_ODDOFFSET+addr-1):(addr))
+#define NE2000_DATA_PTR(addr) (addr)
+
+
+void ne2000_outb(unsigned int val, unsigned int addr)
+{
+ NE2000_BYTE *rp;
+
+ rp = (NE2000_BYTE *) NE2000_PTR(addr);
+ *rp = RSWAP(val);
+}
+
+int ne2000_inb(unsigned int addr)
+{
+ NE2000_BYTE *rp, val;
+
+ rp = (NE2000_BYTE *) NE2000_PTR(addr);
+ val = *rp;
+ return((int) ((NE2000_BYTE) RSWAP(val)));
+}
+
+void ne2000_insb(unsigned int addr, void *vbuf, int unsigned long len)
+{
+ NE2000_BYTE *rp, val;
+ unsigned char *buf;
+
+ buf = (unsigned char *) vbuf;
+ rp = (NE2000_BYTE *) NE2000_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ val = *rp;
+ *buf++ = RSWAP(val);
+ }
+}
+
+void ne2000_insw(unsigned int addr, void *vbuf, unsigned long len)
+{
+ volatile unsigned short *rp;
+ unsigned short w, *buf;
+
+ buf = (unsigned short *) vbuf;
+ rp = (volatile unsigned short *) NE2000_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ w = *rp;
+ *buf++ = BSWAP(w);
+ }
+}
+
+void ne2000_outsb(unsigned int addr, const void *vbuf, unsigned long len)
+{
+ NE2000_BYTE *rp, val;
+ unsigned char *buf;
+
+ buf = (unsigned char *) vbuf;
+ rp = (NE2000_BYTE *) NE2000_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ val = *buf++;
+ *rp = RSWAP(val);
+ }
+}
+
+void ne2000_outsw(unsigned int addr, const void *vbuf, unsigned long len)
+{
+ volatile unsigned short *rp;
+ unsigned short w, *buf;
+
+ buf = (unsigned short *) vbuf;
+ rp = (volatile unsigned short *) NE2000_DATA_PTR(addr);
+ for (; (len > 0); len--) {
+ w = *buf++;
+ *rp = BSWAP(w);
+ }
+}
+
+#endif /* COLDFIRE_NE2000_FUNCS */
+#endif /* NE2000_OFFOFFSET */
+
+/****************************************************************************/
+#endif /* mcf8390_h */
diff --git a/arch/m68k/include/asm/mcfne.h b/arch/m68k/include/asm/mcfne.h
deleted file mode 100644
index bf638be..0000000
--- a/arch/m68k/include/asm/mcfne.h
+++ /dev/null
@@ -1,242 +0,0 @@
-/****************************************************************************/
-
-/*
- * mcfne.h -- NE2000 in ColdFire eval boards.
- *
- * (C) Copyright 1999-2000, Greg Ungerer (gerg@snapgear.com)
- * (C) Copyright 2000, Lineo (www.lineo.com)
- * (C) Copyright 2001, SnapGear (www.snapgear.com)
- *
- * 19990409 David W. Miller Converted from m5206ne.h for 5307 eval board
- *
- * Hacked support for m5206e Cadre III evaluation board
- * Fred Stevens (fred.stevens@pemstar.com) 13 April 1999
- */
-
-/****************************************************************************/
-#ifndef mcfne_h
-#define mcfne_h
-/****************************************************************************/
-
-
-/*
- * Support for NE2000 clones devices in ColdFire based boards.
- * Not all boards address these parts the same way, some use a
- * direct addressing method, others use a side-band address space
- * to access odd address registers, some require byte swapping
- * others do not.
- */
-#define BSWAP(w) (((w) << 8) | ((w) >> 8))
-#define RSWAP(w) (w)
-
-
-/*
- * Define the basic hardware resources of NE2000 boards.
- */
-
-#if defined(CONFIG_ARN5206)
-#define NE2000_ADDR 0x40000300
-#define NE2000_ODDOFFSET 0x00010000
-#define NE2000_IRQ_VECTOR 0xf0
-#define NE2000_IRQ_PRIORITY 2
-#define NE2000_IRQ_LEVEL 4
-#define NE2000_BYTE volatile unsigned short
-#endif
-
-#if defined(CONFIG_M5206eC3)
-#define NE2000_ADDR 0x40000300
-#define NE2000_ODDOFFSET 0x00010000
-#define NE2000_IRQ_VECTOR 0x1c
-#define NE2000_IRQ_PRIORITY 2
-#define NE2000_IRQ_LEVEL 4
-#define NE2000_BYTE volatile unsigned short
-#endif
-
-#if defined(CONFIG_M5206e) && defined(CONFIG_NETtel)
-#define NE2000_ADDR 0x30000300
-#define NE2000_IRQ_VECTOR 25
-#define NE2000_IRQ_PRIORITY 1
-#define NE2000_IRQ_LEVEL 3
-#define NE2000_BYTE volatile unsigned char
-#endif
-
-#if defined(CONFIG_M5307C3)
-#define NE2000_ADDR 0x40000300
-#define NE2000_ODDOFFSET 0x00010000
-#define NE2000_IRQ_VECTOR 0x1b
-#define NE2000_BYTE volatile unsigned short
-#endif
-
-#if defined(CONFIG_M5272) && defined(CONFIG_NETtel)
-#define NE2000_ADDR 0x30600300
-#define NE2000_ODDOFFSET 0x00008000
-#define NE2000_IRQ_VECTOR 67
-#undef BSWAP
-#define BSWAP(w) (w)
-#define NE2000_BYTE volatile unsigned short
-#undef RSWAP
-#define RSWAP(w) (((w) << 8) | ((w) >> 8))
-#endif
-
-#if defined(CONFIG_M5307) && defined(CONFIG_NETtel)
-#define NE2000_ADDR0 0x30600300
-#define NE2000_ADDR1 0x30800300
-#define NE2000_ODDOFFSET 0x00008000
-#define NE2000_IRQ_VECTOR0 27
-#define NE2000_IRQ_VECTOR1 29
-#undef BSWAP
-#define BSWAP(w) (w)
-#define NE2000_BYTE volatile unsigned short
-#undef RSWAP
-#define RSWAP(w) (((w) << 8) | ((w) >> 8))
-#endif
-
-#if defined(CONFIG_M5307) && defined(CONFIG_SECUREEDGEMP3)
-#define NE2000_ADDR 0x30600300
-#define NE2000_ODDOFFSET 0x00008000
-#define NE2000_IRQ_VECTOR 27
-#undef BSWAP
-#define BSWAP(w) (w)
-#define NE2000_BYTE volatile unsigned short
-#undef RSWAP
-#define RSWAP(w) (((w) << 8) | ((w) >> 8))
-#endif
-
-#if defined(CONFIG_ARN5307)
-#define NE2000_ADDR 0xfe600300
-#define NE2000_ODDOFFSET 0x00010000
-#define NE2000_IRQ_VECTOR 0x1b
-#define NE2000_IRQ_PRIORITY 2
-#define NE2000_IRQ_LEVEL 3
-#define NE2000_BYTE volatile unsigned short
-#endif
-
-#if defined(CONFIG_M5407C3)
-#define NE2000_ADDR 0x40000300
-#define NE2000_ODDOFFSET 0x00010000
-#define NE2000_IRQ_VECTOR 0x1b
-#define NE2000_BYTE volatile unsigned short
-#endif
-
-/****************************************************************************/
-
-/*
- * Side-band address space for odd address requires re-mapping
- * many of the standard ISA access functions.
- */
-#ifdef NE2000_ODDOFFSET
-
-#undef outb
-#undef outb_p
-#undef inb
-#undef inb_p
-#undef outsb
-#undef outsw
-#undef insb
-#undef insw
-
-#define outb ne2000_outb
-#define inb ne2000_inb
-#define outb_p ne2000_outb
-#define inb_p ne2000_inb
-#define outsb ne2000_outsb
-#define outsw ne2000_outsw
-#define insb ne2000_insb
-#define insw ne2000_insw
-
-
-#ifndef COLDFIRE_NE2000_FUNCS
-
-void ne2000_outb(unsigned int val, unsigned int addr);
-int ne2000_inb(unsigned int addr);
-void ne2000_insb(unsigned int addr, void *vbuf, int unsigned long len);
-void ne2000_insw(unsigned int addr, void *vbuf, unsigned long len);
-void ne2000_outsb(unsigned int addr, void *vbuf, unsigned long len);
-void ne2000_outsw(unsigned int addr, void *vbuf, unsigned long len);
-
-#else
-
-/*
- * This macro converts a conventional register address into the
- * real memory pointer of the mapped NE2000 device.
- * On most NE2000 implementations on ColdFire boards the chip is
- * mapped in kinda funny, due to its ISA heritage.
- */
-#define NE2000_PTR(addr) ((addr&0x1)?(NE2000_ODDOFFSET+addr-1):(addr))
-#define NE2000_DATA_PTR(addr) (addr)
-
-
-void ne2000_outb(unsigned int val, unsigned int addr)
-{
- NE2000_BYTE *rp;
-
- rp = (NE2000_BYTE *) NE2000_PTR(addr);
- *rp = RSWAP(val);
-}
-
-int ne2000_inb(unsigned int addr)
-{
- NE2000_BYTE *rp, val;
-
- rp = (NE2000_BYTE *) NE2000_PTR(addr);
- val = *rp;
- return((int) ((NE2000_BYTE) RSWAP(val)));
-}
-
-void ne2000_insb(unsigned int addr, void *vbuf, int unsigned long len)
-{
- NE2000_BYTE *rp, val;
- unsigned char *buf;
-
- buf = (unsigned char *) vbuf;
- rp = (NE2000_BYTE *) NE2000_DATA_PTR(addr);
- for (; (len > 0); len--) {
- val = *rp;
- *buf++ = RSWAP(val);
- }
-}
-
-void ne2000_insw(unsigned int addr, void *vbuf, unsigned long len)
-{
- volatile unsigned short *rp;
- unsigned short w, *buf;
-
- buf = (unsigned short *) vbuf;
- rp = (volatile unsigned short *) NE2000_DATA_PTR(addr);
- for (; (len > 0); len--) {
- w = *rp;
- *buf++ = BSWAP(w);
- }
-}
-
-void ne2000_outsb(unsigned int addr, const void *vbuf, unsigned long len)
-{
- NE2000_BYTE *rp, val;
- unsigned char *buf;
-
- buf = (unsigned char *) vbuf;
- rp = (NE2000_BYTE *) NE2000_DATA_PTR(addr);
- for (; (len > 0); len--) {
- val = *buf++;
- *rp = RSWAP(val);
- }
-}
-
-void ne2000_outsw(unsigned int addr, const void *vbuf, unsigned long len)
-{
- volatile unsigned short *rp;
- unsigned short w, *buf;
-
- buf = (unsigned short *) vbuf;
- rp = (volatile unsigned short *) NE2000_DATA_PTR(addr);
- for (; (len > 0); len--) {
- w = *buf++;
- *rp = BSWAP(w);
- }
-}
-
-#endif /* COLDFIRE_NE2000_FUNCS */
-#endif /* NE2000_OFFOFFSET */
-
-/****************************************************************************/
-#endif /* mcfne_h */
--
1.7.0.4
^ permalink raw reply related
* Re: [RFC v2 1/2] net/hsr: Add support for IEC 62439-3 High-availability Seamless Redundancy
From: Stephen Hemminger @ 2012-07-04 4:20 UTC (permalink / raw)
To: Arvid Brodin
Cc: netdev@vger.kernel.org, Alexey Kuznetsov, Javier Boticario,
Bruno Ferreira
In-Reply-To: <4FF38A5C.9000802@xdin.com>
On Wed, 4 Jul 2012 00:12:13 +0000
Arvid Brodin <Arvid.Brodin@xdin.com> 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.
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.
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()
4. Don't use mixed case as in:
+ HSR_Ver = 0;
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
6. Don't comment out code and then submit it. I don't like reading and
reviewing leftover debug stuff.
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.
8. If possible use standard netdev macros for printing info messages:
see netdev_info() etc.
9. Be careful about variable names: "seqlock" implies you are using
seq_lock() but you aren't doing that. It is just a spinlock.
10. above() seems like it could be done by signed math in one operation
without conditional ?: operation.
See timer_before/timer_after for example.
11. All global variables need to have one prefix. You are using both hsr_
and framreg_. Can you just use hsr_?
^ permalink raw reply
* [PATCH can-next v4] em_canid: Ematch rule to match CAN frames according to their identifiers
From: Oliver Hartkopp @ 2012-07-04 4:12 UTC (permalink / raw)
To: Marc Kleine-Budde
Cc: linux-can@vger.kernel.org, lartc, Rostislav Lisovy,
Pavel Píša, Michal Sojka, Linux Netdev List
This ematch makes it possible to classify CAN frames (AF_CAN) according
to their identifiers. This functionality can not be easily achieved with
existing classifiers, such as u32, because CAN identifier is always stored
in native endianness, whereas u32 expects Network byte order.
Signed-off-by: Rostislav Lisovy <lisovy@gmail.com>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
Results of simple benchmark are available at address:
http://rtime.felk.cvut.cz/~lisovros/em_canid_benchmark.pdf
In short: there is no significant difference between em_canid and cls_can
(i.e. stand-alone AF_CAN classifier).
Changes in v4:
* fixed obsolete comment due to code changes
* removed obsolete rulescnt variable
* removed obsolete initialisation due to kzalloc()
* fixed the SFF frame detection for the rules_raw processing
* fixed the m->datalen value to the allocated memory size
Changes in v3:
* Added zero-length check for configuration data
* Small fixes in patch format (patch created against net-next)
Changes in v2:
* Integrated remarks received from the mailing list
* Array containing rules (rules_raw) is dynamically allocated
* When processing a rule during configuration, CAN_EFF_FLAG in mask
determines if we care about the value of CAN_EFF_FLAG in an identifier
-- i.e. when CAN_EFF_FLAG is set in the mask, the rule will be added
as SFF or EFF only depending on CAN_EFF_FLAG value in the identifier.
If CAN_EFF_FLAG is 0 in the mask, the rule will be added as both SFF
and EFF.
include/linux/can.h | 3 +
include/linux/pkt_cls.h | 5 +-
net/sched/Kconfig | 10 ++
net/sched/Makefile | 1 +
net/sched/em_canid.c | 243 +++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 260 insertions(+), 2 deletions(-)
create mode 100644 net/sched/em_canid.c
diff --git a/include/linux/can.h b/include/linux/can.h
index 1a66cf61..018055e 100644
--- a/include/linux/can.h
+++ b/include/linux/can.h
@@ -38,6 +38,9 @@
*/
typedef __u32 canid_t;
+#define CAN_SFF_ID_BITS 11
+#define CAN_EFF_ID_BITS 29
+
/*
* Controller Area Network Error Message Frame Mask structure
*
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
index defbde2..7fbe6c2 100644
--- a/include/linux/pkt_cls.h
+++ b/include/linux/pkt_cls.h
@@ -451,8 +451,9 @@ enum {
#define TCF_EM_U32 3
#define TCF_EM_META 4
#define TCF_EM_TEXT 5
-#define TCF_EM_VLAN 6
-#define TCF_EM_MAX 6
+#define TCF_EM_VLAN 6
+#define TCF_EM_CANID 7
+#define TCF_EM_MAX 7
enum {
TCF_EM_PROG_TC
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index e7a8976..8f759b6 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -507,6 +507,16 @@ config NET_EMATCH_TEXT
To compile this code as a module, choose M here: the
module will be called em_text.
+config NET_EMATCH_CANID
+ tristate "CAN Identifier"
+ depends on NET_EMATCH && CAN
+ ---help---
+ Say Y here if you want to be able to classify CAN frames based
+ on CAN Identifier.
+
+ To compile this code as a module, choose M here: the
+ module will be called em_canid.
+
config NET_CLS_ACT
bool "Actions"
---help---
diff --git a/net/sched/Makefile b/net/sched/Makefile
index 5940a19..bcada75 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_NET_EMATCH_NBYTE) += em_nbyte.o
obj-$(CONFIG_NET_EMATCH_U32) += em_u32.o
obj-$(CONFIG_NET_EMATCH_META) += em_meta.o
obj-$(CONFIG_NET_EMATCH_TEXT) += em_text.o
+obj-$(CONFIG_NET_EMATCH_CANID) += em_canid.o
diff --git a/net/sched/em_canid.c b/net/sched/em_canid.c
new file mode 100644
index 0000000..dc9f515
--- /dev/null
+++ b/net/sched/em_canid.c
@@ -0,0 +1,243 @@
+/*
+ * em_canid.c Ematch rule to match CAN frames according to their CAN IDs
+ *
+ * This program is free software; you can distribute 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.
+ *
+ * Idea: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright: (c) 2011 Czech Technical University in Prague
+ * (c) 2011 Volkswagen Group Research
+ * Authors: Michal Sojka <sojkam1@fel.cvut.cz>
+ * Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ * Rostislav Lisovy <lisovy@gmail.cz>
+ * Funded by: Volkswagen Group Research
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <net/pkt_cls.h>
+#include <linux/can.h>
+
+#define EM_CAN_RULES_MAX 500
+
+struct canid_match {
+ /* For each SFF CAN ID (11 bit) there is one record in this bitfield */
+ DECLARE_BITMAP(match_sff, (1 << CAN_SFF_ID_BITS));
+
+ int rules_count;
+ int sff_rules_count;
+ int eff_rules_count;
+
+ /*
+ * Raw rules copied from netlink message; Used for sending
+ * information to userspace (when 'tc filter show' is invoked)
+ * AND when matching EFF frames
+ */
+ struct can_filter rules_raw[];
+};
+
+/**
+ * em_canid_get_id() - Extracts Can ID out of the sk_buff structure.
+ */
+static canid_t em_canid_get_id(struct sk_buff *skb)
+{
+ /* CAN ID is stored within the data field */
+ struct can_frame *cf = (struct can_frame *)skb->data;
+
+ return cf->can_id;
+}
+
+static void em_canid_sff_match_add(struct canid_match *cm, u32 can_id,
+ u32 can_mask)
+{
+ int i;
+
+ /*
+ * Limit can_mask and can_id to SFF range to
+ * protect against write after end of array
+ */
+ can_mask &= CAN_SFF_MASK;
+ can_id &= can_mask;
+
+ /* Single frame */
+ if (can_mask == CAN_SFF_MASK) {
+ set_bit(can_id, cm->match_sff);
+ return;
+ }
+
+ /* All frames */
+ if (can_mask == 0) {
+ bitmap_fill(cm->match_sff, (1 << CAN_SFF_ID_BITS));
+ return;
+ }
+
+ /*
+ * Individual frame filter.
+ * Add record (set bit to 1) for each ID that
+ * conforms particular rule
+ */
+ for (i = 0; i < (1 << CAN_SFF_ID_BITS); i++) {
+ if ((i & can_mask) == can_id)
+ set_bit(i, cm->match_sff);
+ }
+}
+
+static inline struct canid_match *em_canid_priv(struct tcf_ematch *m)
+{
+ return (struct canid_match *)m->data;
+}
+
+static int em_canid_match(struct sk_buff *skb, struct tcf_ematch *m,
+ struct tcf_pkt_info *info)
+{
+ struct canid_match *cm = em_canid_priv(m);
+ canid_t can_id;
+ int match = 0;
+ int i;
+ const struct can_filter *lp;
+
+ can_id = em_canid_get_id(skb);
+
+ if (can_id & CAN_EFF_FLAG) {
+ for (i = 0, lp = cm->rules_raw;
+ i < cm->eff_rules_count; i++, lp++) {
+ if (!(((lp->can_id ^ can_id) & lp->can_mask))) {
+ match = 1;
+ break;
+ }
+ }
+ } else { /* SFF */
+ can_id &= CAN_SFF_MASK;
+ match = (test_bit(can_id, cm->match_sff) ? 1 : 0);
+ }
+
+ return match;
+}
+
+static int em_canid_change(struct tcf_proto *tp, void *data, int len,
+ struct tcf_ematch *m)
+{
+ struct can_filter *conf = data; /* Array with rules */
+ struct canid_match *cm;
+ struct canid_match *cm_old = (struct canid_match *)m->data;
+ int i;
+
+ if (!len)
+ return -EINVAL;
+
+ if (len % sizeof(struct can_filter))
+ return -EINVAL;
+
+ if (len > sizeof(struct can_filter) * EM_CAN_RULES_MAX)
+ return -EINVAL;
+
+ cm = kzalloc(sizeof(struct canid_match) + len, GFP_KERNEL);
+ if (!cm)
+ return -ENOMEM;
+
+ cm->rules_count = len / sizeof(struct can_filter);
+
+ /*
+ * We need two for() loops for copying rules into
+ * two contiguous areas in rules_raw
+ */
+
+ /* Process EFF and mixed EFF/SFF frame rules*/
+ for (i = 0; i < cm->rules_count; i++) {
+ if (!(conf[i].can_id & CAN_EFF_FLAG) &&
+ (conf[i].can_mask & CAN_EFF_FLAG)) {
+ /* SFF only filter */
+ continue;
+ } else {
+ memcpy(cm->rules_raw + cm->eff_rules_count,
+ &conf[i],
+ sizeof(struct can_filter));
+
+ cm->eff_rules_count++;
+ }
+ }
+
+ /* Process SFF frame rules */
+ for (i = 0; i < cm->rules_count; i++) {
+ if (!(conf[i].can_id & CAN_EFF_FLAG) &&
+ (conf[i].can_mask & CAN_EFF_FLAG)) {
+ /* SFF only filter */
+ memcpy(cm->rules_raw
+ + cm->eff_rules_count
+ + cm->sff_rules_count,
+ &conf[i], sizeof(struct can_filter));
+
+ cm->sff_rules_count++;
+
+ em_canid_sff_match_add(cm,
+ conf[i].can_id, conf[i].can_mask);
+ } else {
+ continue;
+ }
+ }
+
+ m->datalen = sizeof(struct canid_match) + len;
+ m->data = (unsigned long)cm;
+
+ if (cm_old != NULL) {
+ pr_err("canid: Configuring an existing ematch!\n");
+ kfree(cm_old);
+ }
+
+ return 0;
+}
+
+static void em_canid_destroy(struct tcf_proto *tp, struct tcf_ematch *m)
+{
+ struct canid_match *cm = em_canid_priv(m);
+
+ kfree(cm);
+}
+
+static int em_canid_dump(struct sk_buff *skb, struct tcf_ematch *m)
+{
+ struct canid_match *cm = em_canid_priv(m);
+
+ /*
+ * When configuring this ematch 'rules_count' is set not to exceed
+ * 'rules_raw' array size
+ */
+ if (nla_put_nohdr(skb, sizeof(struct can_filter) * cm->rules_count,
+ &cm->rules_raw) < 0)
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static struct tcf_ematch_ops em_canid_ops = {
+ .kind = TCF_EM_CANID,
+ .change = em_canid_change,
+ .match = em_canid_match,
+ .destroy = em_canid_destroy,
+ .dump = em_canid_dump,
+ .owner = THIS_MODULE,
+ .link = LIST_HEAD_INIT(em_canid_ops.link)
+};
+
+static int __init init_em_canid(void)
+{
+ return tcf_em_register(&em_canid_ops);
+}
+
+static void __exit exit_em_canid(void)
+{
+ tcf_em_unregister(&em_canid_ops);
+}
+
+MODULE_LICENSE("GPL");
+
+module_init(init_em_canid);
+module_exit(exit_em_canid);
+
+MODULE_ALIAS_TCF_EMATCH(TCF_EM_CANID);
--
1.7.10.4
^ permalink raw reply related
* [PATCH 1/1] atl1c: fix issue of transmit queue 0 timed out
From: cjren @ 2012-07-04 2:51 UTC (permalink / raw)
To: davem, netdev, linux-kernel; +Cc: qca-linux-team, nic-devel
some people report atl1c could cause system hang with following
kernel trace info:
---------------------------------------
WARNING: at.../net/sched/sch_generic.c:258 dev_watchdog+0x1db/0x1d0()
...
NETDEV WATCHDOG: eth0 (atl1c): transmit queue 0 timed out
...
---------------------------------------
This is caused by netif_stop_queue calling when cable Link is down.
So remove netif_stop_queue, because link_watch will take it over.
Signed-off-by: xiong <xiong@qca.qualcomm.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Cloud Ren <cjren@qca.qualcomm.com>
---
drivers/net/ethernet/atheros/atl1c/atl1c_main.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
index 85717cb..7901831 100644
--- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
@@ -261,7 +261,6 @@ static void atl1c_check_link_status(struct atl1c_adapter *adapter)
if ((phy_data & BMSR_LSTATUS) == 0) {
/* link down */
netif_carrier_off(netdev);
- netif_stop_queue(netdev);
hw->hibernate = true;
if (atl1c_reset_mac(hw) != 0)
if (netif_msg_hw(adapter))
--
1.7.7
^ permalink raw reply related
* RE: [PATCH 1/1] atl1c: fix issue of transmit queue 0 timed out
From: Ren, Cloud @ 2012-07-04 1:56 UTC (permalink / raw)
To: Huang, Xiong, davem@davemloft.net, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
Cc: qca-linux-team, nic-devel
In-Reply-To: <157393863283F442885425D2C454285623DB7BEC@NASANEXD02A.na.qualcomm.com>
Xiong, It looks my system time is wrong. After correct it, I will resend it.
Cloud ren
-----Original Message-----
From: Huang, Xiong
Sent: 2012年7月3日 21:04
To: Ren, Cloud; davem@davemloft.net; netdev@vger.kernel.org; linux-kernel@vger.kernel.org
Cc: qca-linux-team; nic-devel
Subject: RE: [PATCH 1/1] atl1c: fix issue of transmit queue 0 timed out
Cloud, why your patch contains : 'From: Cloud Ren <cjren@qca.qualcomm.com>'
I don’t find it in other people's patch.
Doesn't David Miller think your time is wrong ?
> -----Original Message-----
> From: Ren, Cloud
> Sent: Tuesday, July 03, 2012 21:28
> To: davem@davemloft.net; netdev@vger.kernel.org; linux-
> kernel@vger.kernel.org
> Cc: qca-linux-team; nic-devel; Ren, Cloud
> Subject: [PATCH 1/1] atl1c: fix issue of transmit queue 0 timed out
>
> From: Cloud Ren <cjren@qca.qualcomm.com>
>
> some people report atl1c could cause system hang with following kernel
> trace
> info:
> ---------------------------------------
> WARNING: at.../net/sched/sch_generic.c:258 dev_watchdog+0x1db/0x1d0() ...
> NETDEV WATCHDOG: eth0 (atl1c): transmit queue 0 timed out ...
> ---------------------------------------
> This is caused by netif_stop_queue calling when cable Link is down.
> So remove netif_stop_queue, because link_watch will take it over.
>
> Signed-off-by: xiong <xiong@qca.qualcomm.com>
> Cc: stable <stable@vger.kernel.org>
> Signed-off-by: Cloud Ren <cjren@qca.qualcomm.com>
> ---
> drivers/net/ethernet/atheros/atl1c/atl1c_main.c | 1 -
> 1 files changed, 0 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
> b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
> index 85717cb..7901831 100644
> --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
> +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
> @@ -261,7 +261,6 @@ static void atl1c_check_link_status(struct
> atl1c_adapter *adapter)
> if ((phy_data & BMSR_LSTATUS) == 0) {
> /* link down */
> netif_carrier_off(netdev);
> - netif_stop_queue(netdev);
> hw->hibernate = true;
> if (atl1c_reset_mac(hw) != 0)
> if (netif_msg_hw(adapter))
> --
> 1.7.7
^ permalink raw reply
* [PATCH next-next] ppp: change default for incoming protocol filter to NPMODE_DROP
From: Benjamin LaHaise @ 2012-07-04 1:32 UTC (permalink / raw)
To: David Miller; +Cc: netdev, linux-ppp
By default, the ppp_generic code initializes the npmode array that filters
incoming packet to accept packets for all protocols. This behaviour is
incorrect, as it results in packets for protocols that an older version
of a PPP implementation may not be aware of to be incorrectly accepted.
This behaviour is visible, for example, when sending IPv6 packets across a
ppp link where pppd has only been configured to use IPv4.
This change should be safe since pppd will correctly set the protocols it
negotiates to NPMODE_PASS as the appropriate protocols transition to an Up
state.
Signed-off-by: Benjamin LaHaise <bcrl@kvack.org>
---
drivers/net/ppp/ppp_generic.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 5c05572..404ac50 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -2619,7 +2619,7 @@ ppp_create_interface(struct net *net, int unit, int *retp)
init_ppp_file(&ppp->file, INTERFACE);
ppp->file.hdrlen = PPP_HDRLEN - 2; /* don't count proto bytes */
for (i = 0; i < NUM_NP; ++i)
- ppp->npmode[i] = NPMODE_PASS;
+ ppp->npmode[i] = NPMODE_DROP;
INIT_LIST_HEAD(&ppp->channels);
spin_lock_init(&ppp->rlock);
spin_lock_init(&ppp->wlock);
--
1.7.4.1
--
"Thought is the essence of where you are now."
^ permalink raw reply related
* Re: "ADDRCONF(NETDEV_UP): eth0: link is not ready" with IPv6
From: Ben Hutchings @ 2012-07-04 0:30 UTC (permalink / raw)
To: Nicolas Ferre
Cc: Arvid Brodin, netdev@vger.kernel.org, Alexey Kuznetsov,
Stephen Hemminger, linux-arm-kernel
In-Reply-To: <4FF31A99.9010806@atmel.com>
On Tue, 2012-07-03 at 18:15 +0200, Nicolas Ferre wrote:
> On 07/03/2012 05:47 PM, Arvid Brodin :
> > (Added MACB "patch" contact Nicolas Ferre to CC list.)
>
> Hi,
>
> (adding linux-arm-kernel)
>
> > On 2012-06-29 17:24, Ben Hutchings wrote:
> >> On Fri, 2012-06-29 at 02:36 +0000, Arvid Brodin wrote:
> >>> Hi,
> >>>
> >>> After 'ip link set eth0 up' on an avr32 board (network driver macb), the device ends up in
> >>> operational mode "UNKNOWN":
> >>>
> >>> # ip link
> >>> 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
> >>> link/ether 00:24:74:00:17:9d brd ff:ff:ff:ff:ff:ff
> >>>
> >>> Unplugging and plugging in the network cable gets the device to mode "UP".
> >>>
> >>> This is a problem for me because I'm trying to use this device as a "slave" device (for a
> >>> virtual HSR device*) and I need to be able to decide if the slave device is operational or
> >>> not.
> >>>
> >>> Following Stephen's advice here:
> >>> http://kerneltrap.org/mailarchive/linux-netdev/2008/9/24/3398834 I checked the macb.c code
> >>> and noticed they do not call netif_carrier_off() neither before register_netdev() nor in
> >>> dev_open().
> >
> >> It should be called after register_netdev() and before the driver's
> >> ndo_open implementation returns.
>
> After having read several drivers, it seems that some are calling
> netif_carrier_off() *before* register_netdev() and some *after*... What
> is the proper way?
[...]
The latter is correct; the former is a bug that is tolerated for now:
http://article.gmane.org/gmane.linux.network/111355/
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 v2 1/2] net/hsr: Add support for IEC 62439-3 High-availability Seamless Redundancy
From: Joe Perches @ 2012-07-04 0:30 UTC (permalink / raw)
To: Arvid Brodin
Cc: netdev@vger.kernel.org, Stephen Hemminger, Alexey Kuznetsov,
Javier Boticario, Bruno Ferreira
In-Reply-To: <4FF38A5C.9000802@xdin.com>
On Wed, 2012-07-04 at 00:12 +0000, Arvid Brodin wrote:
> The kernel patch.
[]
> diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
> @@ -0,0 +1,531 @@
> +static int is_admin_up(struct net_device *dev)
> +{
> + return (dev->flags & IFF_UP);
> +}
> +
> +static int is_operstate_up(struct net_device *dev)
> +{
> + return (dev->operstate == IF_OPER_UP);
> +}
bool?
> +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");
> + break;
> + default:
> + printk(KERN_INFO "%s: new operstate is !IF_OPER_UP (%d)\n", dev->name, transition);
etc.
> +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;
> + }
> +/*
> + printk(KERN_INFO "Slave1/2 operstate: %d/%d\n",
> + slave1->operstate, slave2->operstate);
> +*/
Please remove commented out code.
> +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)
> + pr_info("HSR: Cannot restore promiscuity (%s, %d)\n",
> + slave[i]->name,
> + res);
shouldn't this just be:
netdev_info(slave[i], "cannot restore promiscuity: %d\n",
res);
If you must use pr_<level> please add
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
before any include and let the printk subsystem
add MODNAME as a prefix.
> +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)) {
> + pr_info("%s: Cannot enslave loopback or non-ethernet device\n",
> + dev->name);
netdev_info(dev, "Cannot enslave...");
> + return -EINVAL;
> + }
> +
> + /* Don't allow enslaving hsr devices */
> + if (is_hsr_master(dev)) {
> + pr_info("%s: Don't try to create trees of hsr devices!\n",
> + dev->name);
netdev_err(dev, "Cannot create trees of hsr devices\n");
> +int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2])
> +{
> + /* Set hsr_dev's MAC address to that of mac_slave1 */
> + memcpy(hsr_dev->dev_addr, hsr_priv->slave_data[0].dev->dev_addr,
> + hsr_dev->addr_len);
ETH_ALEN?
> diff --git a/net/hsr/hsr_device.h b/net/hsr/hsr_device.h
[]
> @@ -0,0 +1,27 @@
> +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);
please align arguments immediately after the open parenthesis.
> diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
[]
> +static struct node_entry *find_node_by_AddrA(struct list_head *node_db,
> + unsigned char addr[ETH_ALEN])
static struct node_entry *find_node_by_AddrA(struct list_head *node_db,
unsigned char addr[ETH_ALEN])
[]
> +static struct node_entry *find_node_by_AddrB(struct list_head *node_db,
> + unsigned char addr[ETH_ALEN])
Alignment...
> +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?
> + if (!node)
> + return -ENOMEM;
> +
> + memcpy(node->MacAddressA, hsr_stag->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;
> +/*
> + printk(KERN_INFO "HSR: Added node %pM / %pM\n",
> + node->MacAddressA,
> + node->MacAddressB);
> +*/
Please remove commented out code here and everywhere else...
[too long, stopped reading]
^ permalink raw reply
* [RFC v2 2/2] net/hsr: Add support for IEC 62439-3 High-availability Seamless Redundancy
From: Arvid Brodin @ 2012-07-04 0:18 UTC (permalink / raw)
To: netdev@vger.kernel.org
Cc: Stephen Hemminger, Alexey Kuznetsov, Javier Boticario,
Bruno Ferreira
The iproute patch.
Syntax:
# ip link add name hsr0 type hsr eth0 eth1
# ip link del dev hsr0
diff -Nurp iproute2-2.6.35-orig/ip/Makefile iproute2-2.6.35-hsr/ip/Makefile
--- iproute2-2.6.35-orig/ip/Makefile 2010-08-04 19:45:59.000000000 +0200
+++ iproute2-2.6.35-hsr/ip/Makefile 2011-12-13 19:43:14.271913247 +0100
@@ -3,7 +3,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o ipr
ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o \
ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
- iplink_macvlan.o
+ iplink_macvlan.o iplink_hsr.o
RTMONOBJ=rtmon.o
diff -Nurp iproute2-2.6.35-orig/ip/iplink_hsr.c iproute2-2.6.35-hsr/ip/iplink_hsr.c
--- iproute2-2.6.35-orig/ip/iplink_hsr.c 1970-01-01 01:00:00.000000000 +0100
+++ iproute2-2.6.35-hsr/ip/iplink_hsr.c 2011-12-13 19:43:14.252913043 +0100
@@ -0,0 +1,101 @@
+/*
+ * iplink_hsr.c HSR device support
+ *
+ * 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.
+ *
+ * Authors: Arvid Brodin <arvid.brodin@enea.com>
+ *
+ * Based on iplink_vlan.c by Patrick McHardy <kaber@trash.net>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h> /* Needed by linux/if.h for some reason */
+#include <linux/if.h>
+#include <linux/if_vlan.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static void explain(void)
+{
+ fprintf(stderr,
+ "Usage: ip link add name NAME type hsr SLAVE1 SLAVE2\n"
+ "\n"
+ "NAME name of new hsr device (e.g. hsr0)\n"
+ "SLAVE1, SLAVE2 the two slave devices bound by hsr0\n"
+ );
+}
+
+static int hsr_parse_opt(struct link_util *lu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+ char *name, *link;
+ int ifindex, len;
+
+ if (argc != 2) {
+ explain();
+ return -1;
+ }
+
+ switch (n->nlmsg_type) {
+ case RTM_NEWLINK:
+ if (argc != 2) {
+ explain();
+ return -1;
+ }
+ while (argc > 0) {
+ link = *argv;
+ ifindex = ll_name_to_index(link);
+ if (argc == 2)
+ addattr_l(n, 1024, IFLA_HSR_SLAVE1, &ifindex, 4);
+ else
+ addattr_l(n, 1024, IFLA_HSR_SLAVE2, &ifindex, 4);
+ printf("Slave %d: %s; %d\n", 3-argc, link, ifindex);
+ argc--;
+ argv++;
+ }
+ break;
+
+ case RTM_DELLINK:
+ if (argc != 0) {
+ explain();
+ return -1;
+ }
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+
+/*
+ if (ifindex != 0) {
+ fprintf(stderr, "Link device \"%s\" already exist\n", name);
+ return -1;
+ }
+ if (ifindex == 0) {
+ fprintf(stderr, "Cannot find device \"%s\"\n",
+ link);
+ return -1;
+ }
+*/
+}
+
+static void hsr_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+ fprintf(f, "hsr_print_opt() called\n");
+}
+
+struct link_util hsr_link_util = {
+ .id = "hsr",
+ .maxattr = IFLA_VLAN_MAX,
+ .parse_opt = hsr_parse_opt,
+ .print_opt = hsr_print_opt,
+};
--- iproute2-2.6.35-orig/ip/iplink.c 2010-08-04 19:45:59.000000000 +0200
+++ iproute2-2.6.35-build/ip/iplink.c 2011-12-07 00:12:09.588604817 +0100
@@ -433,6 +433,6 @@ static int iplink_modify(int cmd, unsign
lu = get_link_kind(type);
- if (lu && argc) {
+ if (lu) {
struct rtattr * data = NLMSG_TAIL(&req.n);
addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
--- iproute2-2.6.35-orig/include/linux/if_link.h 2010-08-04 19:45:59.000000000 +0200
+++ iproute2-2.6.35-hsr/include/linux/if_link.h 2011-12-13 19:51:22.639129004 +0100
@@ -347,4 +347,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 */
--
Arvid Brodin | Consultant (Linux)
XDIN AB | Jan Stenbecks Torg 17 | SE-164 40 Kista | Sweden | xdin.com
^ permalink raw reply
* [RFC v2 1/2] net/hsr: Add support for IEC 62439-3 High-availability Seamless Redundancy
From: Arvid Brodin @ 2012-07-04 0:12 UTC (permalink / raw)
To: netdev@vger.kernel.org
Cc: Stephen Hemminger, Alexey Kuznetsov, Javier Boticario,
Bruno Ferreira
The kernel patch.
Documentation/networking/hsr/hsr_genl.c | 213 +++++++++++++
include/linux/if_ether.h | 1 +
include/linux/if_link.h | 11 +
net/Kconfig | 1 +
net/Makefile | 1 +
net/hsr/Kconfig | 84 +++++
net/hsr/Makefile | 7 +
net/hsr/hsr_device.c | 531 +++++++++++++++++++++++++++++++
net/hsr/hsr_device.h | 27 ++
net/hsr/hsr_framereg.c | 328 +++++++++++++++++++
net/hsr/hsr_framereg.h | 54 ++++
net/hsr/hsr_main.c | 411 ++++++++++++++++++++++++
net/hsr/hsr_netlink.c | 293 +++++++++++++++++
net/hsr/hsr_netlink.h | 64 ++++
net/hsr/hsr_private.h | 114 +++++++
15 files changed, 2140 insertions(+), 0 deletions(-)
create mode 100644 Documentation/networking/hsr/hsr_genl.c
create mode 100644 net/hsr/Kconfig
create mode 100644 net/hsr/Makefile
create mode 100644 net/hsr/hsr_device.c
create mode 100644 net/hsr/hsr_device.h
create mode 100644 net/hsr/hsr_framereg.c
create mode 100644 net/hsr/hsr_framereg.h
create mode 100644 net/hsr/hsr_main.c
create mode 100644 net/hsr/hsr_netlink.c
create mode 100644 net/hsr/hsr_netlink.h
create mode 100644 net/hsr/hsr_private.h
diff --git a/Documentation/networking/hsr/hsr_genl.c b/Documentation/networking/hsr/hsr_genl.c
new file mode 100644
index 0000000..1319258
--- /dev/null
+++ b/Documentation/networking/hsr/hsr_genl.c
@@ -0,0 +1,213 @@
+/*
+ * 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 (through libnl-3) to get HSR
+ * ("High-availability Seamless Redundancy") link/network status.
+ */
+
+#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>
+#include "../../linux-next/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..895afda
--- /dev/null
+++ b/net/hsr/Kconfig
@@ -0,0 +1,84 @@
+#
+# 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.
+
+if HAVE_EFFICIENT_UNALIGNED_ACCESS
+
+config NONSTANDARD_HSR
+ bool "HSR: Use efficient tag (breaks HSR standard, read help!)"
+ depends on HSR
+ ---help---
+ The HSR standard specifies a 6-byte HSR tag to be inserted into the
+ transmitted network frames. This breaks the 32-bit alignment that the
+ Linux network stack relies on, and would cause kernel panics on
+ certain architectures. To avoid this, the whole frame payload is
+ memmoved 2 bytes on reception on these architectures - which is very
+ inefficient!
+
+ If you select Y here, 2 bytes of padding is inserted into the HSR tag,
+ which makes it possible to skip the memmove. This however breaks
+ compatibility with compliant HSR devices. I.e., either all or none of
+ the devices in your HSR ring needs to have this option set.
+
+ Your architecture has HAVE_EFFICIENT_UNALIGNED_ACCESS, so you do not
+ need this unless you have other nodes in your ring which have this
+ option set.
+
+ If unsure, say N.
+
+endif # HAVE_EFFICIENT_UNALIGNED_ACCESS
+if !HAVE_EFFICIENT_UNALIGNED_ACCESS
+
+config NONSTANDARD_HSR
+ bool "HSR: Use efficient tag (breaks HSR standard, read help!)"
+ depends on HSR
+ ---help---
+ The HSR standard specifies a 6-byte HSR tag to be inserted into the
+ transmitted network frames. This breaks the 32-bit alignment that the
+ Linux network stack relies on, and would cause kernel panics on
+ certain architectures. To avoid this, the whole frame payload is
+ memmoved 2 bytes on reception on these architectures - which is very
+ inefficient!
+
+ If you select Y here, 2 bytes of padding is inserted into the HSR tag,
+ which makes it possible to skip the memmove. This however breaks
+ compatibility with compliant HSR devices. I.e., either all or none of
+ the devices in your HSR ring needs to have this option set.
+
+ Your architecture does not have HAVE_EFFICIENT_UNALIGNED_ACCESS, so
+ you should seriously consider saying Y here if performance is at all
+ important to you.
+
+ If unsure, say N.
+
+endif # !HAVE_EFFICIENT_UNALIGNED_ACCESS
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..e95c006
--- /dev/null
+++ b/net/hsr/hsr_device.c
@@ -0,0 +1,531 @@
+/*
+ * 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 int is_admin_up(struct net_device *dev)
+{
+ return (dev->flags & IFF_UP);
+}
+
+static int 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) {
+/*
+ switch (transition) {
+ case IF_OPER_UP:
+ printk(KERN_INFO "%s: new operstate is IF_OPER_UP\n", dev->name);
+ break;
+ default:
+ printk(KERN_INFO "%s: new operstate is !IF_OPER_UP (%d)\n", dev->name, 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;
+ }
+/*
+ printk(KERN_INFO "Slave1/2 operstate: %d/%d\n",
+ slave1->operstate, slave2->operstate);
+*/
+ 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: reset status of slaves
+ return 0;
+}
+
+
+static void hsr_fill_tag(struct hsr_ethhdr *hsr_ethhdr, struct hsr_priv *hsr_priv)
+{
+ u16 path;
+ u16 LSDU_size;
+ 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.
+ */
+ path = 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...
+ */
+ LSDU_size = 0;
+ hsr_ethhdr->hsr_tag.path_and_LSDU_size =
+ htons((u16) (path << 12) | LSDU_size);
+
+ spin_lock_irqsave(&hsr_priv->seqlock, irqflags);
+ hsr_ethhdr->hsr_tag.sequence_nr = htons(hsr_priv->sequence_nr);
+ hsr_priv->sequence_nr++;
+ spin_unlock_irqrestore(&hsr_priv->seqlock, 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 = NET_XMIT_DROP;
+ res2 = NET_XMIT_DROP;
+ res1 = slave_xmit(skb, hsr_priv->slave_data[0].dev, dev);
+ 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) */
+ 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_ethhdr *hsr_ethhdr;
+ unsigned char *mac;
+ u16 path, HSR_Ver, HSR_TLV_Length;
+ unsigned long irqflags;
+
+ skb = alloc_skb(sizeof(struct ethhdr) +
+ sizeof(struct hsr_supervision_tag) +
+ LL_RESERVED_SPACE(hsr_dev) +
+ hsr_dev->needed_tailroom, GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ hsr_priv = netdev_priv(hsr_dev);
+
+ skb_reserve(skb, LL_RESERVED_SPACE(hsr_dev));
+ skb_reset_network_header(skb);
+
+ /* Payload: MacAddressA */
+ mac = (unsigned char *) skb_put(skb, ETH_ALEN);
+ memcpy(mac, hsr_dev->dev_addr, ETH_ALEN);
+
+ 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;
+
+ hsr_ethhdr = (struct hsr_ethhdr *) skb->data;
+
+ path = 0x0f;
+ HSR_Ver = 0;
+ hsr_ethhdr->hsr_tag.path_and_LSDU_size = htons(path << 12 | HSR_Ver);
+
+ spin_lock_irqsave(&hsr_priv->seqlock, irqflags);
+ hsr_ethhdr->hsr_tag.sequence_nr = htons(hsr_priv->sequence_nr);
+ hsr_priv->sequence_nr++;
+ spin_unlock_irqrestore(&hsr_priv->seqlock, irqflags);
+
+ HSR_TLV_Length = 12;
+ hsr_ethhdr->hsr_tag.encap_proto = htons(type << 8 | HSR_TLV_Length);
+
+ 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)
+ pr_info("HSR: Cannot restore promiscuity (%s, %d)\n",
+ slave[i]->name,
+ res);
+ }
+
+ /* Restore up state */
+/*
+ for (i = 0; i < 2; i++)
+ if (hsr_priv->slave_data[i].was_up)
+ dev_open(slave[i]);
+ else
+ dev_close(slave[i]);
+*/
+ 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 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.
+ */
+int 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)) {
+ pr_info("%s: Cannot enslave loopback or non-ethernet device\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+ /* Don't allow enslaving hsr devices */
+ if (is_hsr_master(dev)) {
+ pr_info("%s: Don't try to create trees of hsr devices!\n",
+ dev->name);
+ 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->seqlock);
+ 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,
+ hsr_dev->addr_len);
+
+ /* 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) {
+ pr_info("HSR: Cannot set promiscuous mode (%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 = frameref_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..a7596a2
--- /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);
+int 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..f92bc9f
--- /dev/null
+++ b/net/hsr/hsr_framereg.c
@@ -0,0 +1,328 @@
+/*
+ * 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 *node_get_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 *framereg_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 frameref_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 framereg_merge_node(struct hsr_priv *hsr_priv, enum hsr_dev_idx dev_idx,
+ struct sk_buff *skb)
+{
+ struct ethhdr *ethhdr;
+ struct hsr_supervision_tag *hsr_stag;
+ struct node_entry *node;
+ int i;
+ int found;
+
+ ethhdr = (struct ethhdr *) skb_mac_header(skb);
+ hsr_stag = (struct hsr_supervision_tag *)
+ (&((struct hsr_ethhdr *) ethhdr)->hsr_tag);
+
+ found = 1;
+ rcu_read_lock();
+ 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);
+ if (!node)
+ return -ENOMEM;
+
+ memcpy(node->MacAddressA, hsr_stag->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;
+/*
+ printk(KERN_INFO "HSR: Added node %pM / %pM\n",
+ node->MacAddressA,
+ node->MacAddressB);
+*/
+ }
+
+ /* Merge node if it's PICS_SUBS capable */
+ if (compare_ether_addr(hsr_stag->MacAddressA, ethhdr->h_source)) {
+ memcpy(node->MacAddressB, ethhdr->h_source, ETH_ALEN);
+/*
+ printk(KERN_INFO "HSR: Merged node %pM / %pM\n",
+ node->MacAddressA,
+ node->MacAddressB);
+*/
+ }
+
+ 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) {
+/*
+ printk(KERN_INFO "HSR: Substituting %pM -> %pM\n",
+ ethhdr->h_source,
+ node->MacAddressA);
+*/
+ memcpy(ethhdr->h_source, node->MacAddressA, ETH_ALEN);
+ } /*else
+ printk(KERN_INFO "HSR: Not substituting addr %pM\n",
+ ethhdr->h_source);
+*/
+ rcu_read_unlock();
+}
+
+
+
+/*
+ * above(a, b) - return 1 if a > b, 0 otherwise.
+ * Uses C 16-bit unsigned arithmetic, with differences > (1 << 15) interpreted
+ * as negative.
+ */
+#define MAX_RANGE_DIFF (1 << 15)
+static int above(u16 a, u16 b)
+{
+ if ((u16) (a - b) == (u16) (b - a))
+ return (a > b);
+ return (((u16) (a - b) > (u16) 0) &&
+ ((u16) (a - b) <= (u16) MAX_RANGE_DIFF));
+}
+#define below(a, b) above((b), (a))
+#define above_or_equal(a, b) (!below((a), (b)))
+#define below_or_equal(a, b) (!above((a), (b)))
+
+
+void framereg_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;
+ }
+// printk(KERN_INFO "node %pM; dev_idx %d\n", node->MacAddressA, dev_idx);
+ 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 framereg_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)) {
+ printk(KERN_INFO "%s:%d: MAC header not set\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb);
+
+ if (below_or_equal(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 framereg_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 framereg_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..7617b83
--- /dev/null
+++ b/net/hsr/hsr_framereg.h
@@ -0,0 +1,54 @@
+/*
+ * 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)
+/*
+int framereg_add_node(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
+ enum hsr_dev_idx dev_idx, unsigned long time,
+ u16 sequence_nr);
+*/
+struct node_entry *framereg_find_node(struct list_head *node_db,
+ struct sk_buff *skb);
+int framereg_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 framereg_frame_in(struct node_entry *node, enum hsr_dev_idx);
+
+int framereg_frame_out(struct node_entry *node, enum hsr_dev_idx,
+ struct sk_buff *skb);
+void framereg_prune_nodes(struct list_head *node_db);
+
+void framereg_get_node_times(struct hsr_priv *hsr_priv,
+ unsigned char addr[ETH_ALEN],
+ unsigned long *time1, unsigned long *time2);
+int frameref_create_self_node(struct list_head *self_node_db,
+ unsigned char addr_a[ETH_ALEN],
+ unsigned char addr_b[ETH_ALEN]);
+
+unsigned char *node_get_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..f17f222
--- /dev/null
+++ b/net/hsr/hsr_main.c
@@ -0,0 +1,411 @@
+/*
+ * 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 */
+//printk(KERN_INFO "Got %s event NETDEV_UP\n", ((struct net_device *) ptr)->name);
+ goto netdev_change;
+ case NETDEV_DOWN: /* Administrative state UP */
+//printk(KERN_INFO "Got %s event NETDEV_DOWN\n", ((struct net_device *) ptr)->name);
+ goto netdev_change;
+ case NETDEV_CHANGE: /* Link (carrier) state changes */
+//printk(KERN_INFO "Got %s event NETDEV_CHANGE\n", ((struct net_device *) ptr)->name);
+netdev_change:
+ 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 hsr_prune_nodes(unsigned long data)
+{
+ struct hsr_priv *hsr_priv;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list)
+ framereg_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); // Huh???
+ 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) These could also be used to allow different MAC addresses for the two
+ * slave interfaces. This is mentioned in the standard but not explained.
+ */
+static int handle_supervision_frame(struct hsr_priv *hsr_priv,
+ enum hsr_dev_idx dev_idx, struct sk_buff *skb)
+{
+ struct hsr_supervision_tag *hsr_stag;
+
+ if (compare_ether_addr(eth_hdr(skb)->h_dest, hsr_multicast_addr))
+ return 0;
+
+ hsr_stag = (struct hsr_supervision_tag *) skb->data;
+ if (ntohs(hsr_stag->path_and_HSR_ver) >> 12 != 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;
+/*
+ if (hsr_stag->HSR_TLV_Type == HSR_TLV_ANNOUNCE)
+ printk(KERN_INFO "HSR: Got Announce frame from %02x\n",
+ eth_hdr(skb)->h_source[ETH_ALEN-1]);
+*/
+ framereg_merge_node(hsr_priv, dev_idx, skb);
+
+ 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) {
+ printk(KERN_INFO "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 = framereg_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 = framereg_find_node(&hsr_priv->node_db, skb);
+ if (!node) {
+ /* Source node unknown; don't create a network loop */
+ rcu_read_unlock();
+ printk(KERN_INFO "HSR: Got HSR frame from unknown node %pM "
+ "on dev %s: dropping it.\n",
+ eth_hdr(skb)->h_source, dev->name);
+ kfree_skb(skb);
+ return NET_RX_DROP;
+ }
+
+ if (framereg_frame_out(node, HSR_DEV_MASTER, skb) == 1)
+ deliver_to_self = 0;
+
+ framereg_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 (framereg_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) && \
+ !defined(CONFIG_NONSTANDARD_HSR)
+ /* 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) && \
+ !defined(CONFIG_NONSTANDARD_HSR)
+ /*
+ * skb_deliver should be linear here, after the call to
+ * skb_copy() in the block 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 - should prbl be mac_header()?
+#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);
+ BUG_ON(sizeof(struct hsr_ethhdr) != ETH_HLEN + HSR_TAGLEN);
+
+ dev_add_pack(&hsr_pt);
+
+ init_timer(&prune_timer);
+ prune_timer.function = hsr_prune_nodes;
+ 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..fee910a
--- /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]) {
+ printk(KERN_INFO "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]) {
+ printk(KERN_INFO "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 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;
+
+
+
+struct sk_buff *hsr_create_genl_msg(void **pmsg_head, unsigned gfp, int cmd)
+{
+ struct sk_buff *skb;
+
+// printk("Sending HSR_C_[%d]\n", cmd);
+
+ 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 (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);
+ framereg_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..1522907
--- /dev/null
+++ b/net/hsr/hsr_private.h
@@ -0,0 +1,114 @@
+/*
+ * 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) 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, 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.
+ */
+#ifdef CONFIG_NONSTANDARD_HSR
+#define HSR_TAGLEN 8
+#else
+#define HSR_TAGLEN 6
+#endif
+struct hsr_tag {
+/*
+ This is nice but I'm not sure it is "portably compatible" with
+ endianness swaps:
+ __be16 path:4;
+ __be16 LSDU_size:12;
+*/
+ __be16 path_and_LSDU_size;
+ __be16 sequence_nr;
+ __be16 encap_proto;
+#ifdef CONFIG_NONSTANDARD_HSR
+ __be16 padding;
+#endif
+} __packed;
+
+struct hsr_ethhdr {
+ struct ethhdr ethhdr;
+ struct hsr_tag hsr_tag;
+} __packed;
+
+
+struct hsr_supervision_tag {
+ __be16 path_and_HSR_ver;
+ __be16 sequence_nr;
+ __u8 HSR_TLV_Type;
+ __u8 HSR_TLV_Length;
+#ifdef CONFIG_NONSTANDARD_HSR
+ __be16 padding;
+#endif
+ unsigned char MacAddressA[ETH_ALEN];
+} __packed;
+
+
+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 seqlock;
+};
+
+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
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox