* [PATCH 1/2] SH7619: fix typo in Ether platform data
From: Sergei Shtylyov @ 2013-08-21 22:17 UTC (permalink / raw)
To: netdev, lethal, linux-sh, davem
In-Reply-To: <201308220215.16463.sergei.shtylyov@cogentembedded.com>
Commit 06a64f91da72cb5827e2bedef2ead60a123fd66e (SH7619: fix Ether support) has
a typo in the 'phy_interface' field name of the platform data which causes build
error -- fix it.
Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
---
arch/sh/kernel/cpu/sh2/setup-sh7619.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Index: net-next/arch/sh/kernel/cpu/sh2/setup-sh7619.c
===================================================================
--- net-next.orig/arch/sh/kernel/cpu/sh2/setup-sh7619.c
+++ net-next/arch/sh/kernel/cpu/sh2/setup-sh7619.c
@@ -114,7 +114,7 @@ static struct platform_device scif2_devi
static struct sh_eth_plat_data eth_platform_data = {
.phy = 1,
.edmac_endian = EDMAC_LITTLE_ENDIAN,
- .phy_interace = PHY_INTERFACE_MODE_MII,
+ .phy_interface = PHY_INTERFACE_MODE_MII,
};
static struct resource eth_resources[] = {
^ permalink raw reply
* [PATCH 0/2] SH: fix typos in Ether platform data
From: Sergei Shtylyov @ 2013-08-21 22:15 UTC (permalink / raw)
To: netdev, lethal, linux-sh, davem
Hello.
Shamefully, I have fat fingered in both patches fixing Ether support on SH
platforms and I didn't bother to compile test them at least with the ARM
compiler I have at hand (although one of the files turned out to not really be
compilable by it). Here's 2 patches fixing the typos, they are against David
Miller's 'net-next.git'.
[1/2] SH7619: fix typo in Ether platform data
[2/2] SolutionEngine7724: fix typo in Ether platform data
WBR, Sergei
^ permalink raw reply
* [PATCH net-next v4] r8169: fix invalid register dump
From: Peter Wu @ 2013-08-21 21:17 UTC (permalink / raw)
To: netdev, David Miller; +Cc: nic_swsd, bhutchings, Francois Romieu, lekensteyn
In-Reply-To: <20130821205325.GA24054@electric-eye.fr.zoreil.com>
For some reason, my PCIe RTL8111E onboard NIC on a GA-Z68X-UD3H-B3
motherboard reads as FFs when reading from MMIO with a block size
larger than 7. Therefore change to reading blocks of four bytes.
Ben Hutchings noted that the buffer is large enough to hold all
registers, so now all registers are read.
Signed-off-by: Peter Wu <lekensteyn@gmail.com>
Acked-by: Francois Romieu <romieu@fr.zoreil.com>
---
drivers/net/ethernet/realtek/r8169.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 93ee49d..c0c9e14 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -1897,9 +1897,13 @@ static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs,
void *p)
{
struct rtl8169_private *tp = netdev_priv(dev);
+ u32 __iomem *data = tp->mmio_addr;
+ u32 *dw = p;
+ int i;
rtl_lock_work(tp);
- memcpy_fromio(p, tp->mmio_addr, regs->len);
+ for (i = 0; i < R8169_REGS_SIZE; i += 4)
+ memcpy_fromio(dw++, data++, 4);
rtl_unlock_work(tp);
}
--
1.8.3.4
^ permalink raw reply related
* Re: [PATCH v3] r8169: fix invalid register dump
From: Peter Wu @ 2013-08-21 21:13 UTC (permalink / raw)
To: Francois Romieu; +Cc: David Miller, bhutchings, netdev, nic_swsd
In-Reply-To: <20130821205325.GA24054@electric-eye.fr.zoreil.com>
On Wednesday 21 August 2013 22:53:25 Francois Romieu wrote:
> Peter Wu <lekensteyn@gmail.com> :
> [...]
>
> > Please find the updated patch below. There may be differences in lines
> > because net-next has the r8169,sis190 patch and net the WOL fix.
> >
> > Can I consider Francois reply[1] as Acked-by?
>
> Yes, provided you insert an empty line between declarations and code.
Done, new patch will arrive in a minute.
> You may / should send patches as normal submissions. It will probably
> be easier for davem to use and you can always raise questions or add
> minor remarks in the "--- ... diff" section.
I noticed that patchwork also recognized submissions that are appended to a
mail, so I tried that out ;-) Well, apparantly it does not work well with git,
so I will just send a new patch with a useful In-Reply-To header.
Peter
^ permalink raw reply
* Re: [PATCH 3/4] ps3_gelic: lower NAPI weight
From: Geoff Levand @ 2013-08-21 21:03 UTC (permalink / raw)
To: Michal Schmidt; +Cc: David Miller, netdev, Eric Dumazet
In-Reply-To: <1377103771-1590-4-git-send-email-mschmidt@redhat.com>
On Wed, 2013-08-21 at 18:49 +0200, Michal Schmidt wrote:
> Since commit 82dc3c63 netif_napi_add() produces an error message if
> a NAPI poll weight greater than 64 is requested.
>
> GELIC_NET_NAPI_WEIGHT is defined to GELIC_NET_RX_DESCRIPTORS,
> which is 128.
>
> Use the standard NAPI weight.
>
> Signed-off-by: Michal Schmidt <mschmidt@redhat.com>
> CC: Geoff Levand <geoff@infradead.org>
> ---
> drivers/net/ethernet/toshiba/ps3_gelic_net.c | 3 +--
> drivers/net/ethernet/toshiba/ps3_gelic_net.h | 1 -
> 2 files changed, 1 insertion(+), 3 deletions(-)
Change looks fine. Please correct comment as Sergei
suggested.
Acked-by: Geoff Levand <geoff@infradead.org>
^ permalink raw reply
* Re: [PATCH v3] r8169: fix invalid register dump
From: Francois Romieu @ 2013-08-21 20:53 UTC (permalink / raw)
To: Peter Wu; +Cc: David Miller, bhutchings, netdev, nic_swsd
In-Reply-To: <18365346.kxAWDne41Q@al>
Peter Wu <lekensteyn@gmail.com> :
[...]
> Please find the updated patch below. There may be differences in lines
> because net-next has the r8169,sis190 patch and net the WOL fix.
>
> Can I consider Francois reply[1] as Acked-by?
Yes, provided you insert an empty line between declarations and code.
You may / should send patches as normal submissions. It will probably
be easier for davem to use and you can always raise questions or add
minor remarks in the "--- ... diff" section.
--
Ueimor
^ permalink raw reply
* Re: [PATCH v3] r8169: fix invalid register dump
From: Peter Wu @ 2013-08-21 20:40 UTC (permalink / raw)
To: David Miller; +Cc: romieu, bhutchings, netdev, nic_swsd
In-Reply-To: <20130820.150213.1489180923839317674.davem@davemloft.net>
On Tuesday 20 August 2013 15:02:13 David Miller wrote:
> > This partly obsoletes "r8169,sis190: remove unnecessary length
> > check"[1]. I do not have sis190 hardware, but since that is based on
> > this r8169 driver, would it make sense to apply this patch to sis190
> > too?
>
> You're going to have to respin this since I applied the length
> check removal patch already.
Please find the updated patch below. There may be differences in lines
because net-next has the r8169,sis190 patch and net the WOL fix.
Can I consider Francois reply[1] as Acked-by?
Thanks,
Peter
[1]: http://lkml.kernel.org/r/20130817211407.GA30676@electric-eye.fr.zoreil.com
--
>From 56b7598a712e63841b6d8efb2245e5bb87990e01 Mon Sep 17 00:00:00 2001
From: Peter Wu <lekensteyn@gmail.com>
Date: Tue, 23 Jul 2013 12:27:27 +0200
Subject: [PATCH] r8169: fix invalid register dump
For some reason, my PCIe RTL8111E onboard NIC on a GA-Z68X-UD3H-B3
motherboard reads as FFs when reading from MMIO with a block size
larger than 7. Therefore change to reading blocks of four bytes.
Ben Hutchings noted that the buffer is large enough to hold all
registers, so now all registers are read.
Signed-off-by: Peter Wu <lekensteyn@gmail.com>
---
drivers/net/ethernet/realtek/r8169.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 93ee49d..f724d9c 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -1897,9 +1897,12 @@ static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs,
void *p)
{
struct rtl8169_private *tp = netdev_priv(dev);
-
+ u32 __iomem *data = tp->mmio_addr;
+ u32 *dw = p;
+ int i;
rtl_lock_work(tp);
- memcpy_fromio(p, tp->mmio_addr, regs->len);
+ for (i = 0; i < R8169_REGS_SIZE; i += 4)
+ memcpy_fromio(dw++, data++, 4);
rtl_unlock_work(tp);
}
--
1.8.3.4
^ permalink raw reply related
* Re: Re: [PATCH-SR9700] Merge USB 1.1 Ethernet Adapter SR9700 DeviceDriver into the Linux Kernel
From: Francois Romieu @ 2013-08-21 20:33 UTC (permalink / raw)
To: liujunliang_ljl; +Cc: gregkh, sunhecheng, linux-usb, netdev, linux-kernel
In-Reply-To: <201308211805574844059@163.com>
liujunliang_ljl <liujunliang_ljl@163.com> :
[...]
> diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c
> new file mode 100644
> index 0000000..9c8f167
> --- /dev/null
> +++ b/drivers/net/usb/sr9700.c
[...]
> +static int sr_read(struct usbnet *dev, u8 reg, u16 length, void *data)
> +{
> + int err;
> +
> + err = usbnet_read_cmd(dev, SR_RD_REGS, SR_REQ_RD_REG,
> + 0, reg, data, length);
err = usbnet_read_cmd(dev, SR_RD_REGS, SR_REQ_RD_REG, 0, reg, data,
length);
(I already outlined it in my first review)
> + if ((err != length) && (err >= 0))
> + err = -EINVAL;
> + return err;
> +}
> +
> +static int sr_write(struct usbnet *dev, u8 reg, u16 length, void *data)
> +{
> + int err;
> +
> + err = usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG,
> + 0, reg, data, length);
See my first review as well.
> + if ((err >= 0) && (err < length))
> + err = -EINVAL;
> + return err;
> +}
> +
> +static int sr_read_reg(struct usbnet *dev, u8 reg, u8 *value)
> +{
> + return sr_read(dev, reg, 1, value);
> +}
> +
> +static int sr_write_reg(struct usbnet *dev, u8 reg, u8 value)
> +{
> + return usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG,
> + value, reg, NULL, 0);
See my first review as well.
> +}
> +
> +static void sr_write_async(struct usbnet *dev, u8 reg, u16 length, void *data)
> +{
> + usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG,
> + 0, reg, data, length);
See my first review as well.
> +}
> +
> +static void sr_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
> +{
> + usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG,
> + value, reg, NULL, 0);
See my first review as well.
> +}
> +
> +static int sr_share_read_word(struct usbnet *dev, int phy, u8 reg, __le16 *value)
> +{
> + int ret, i;
> +
> + mutex_lock(&dev->phy_mutex);
> +
> + sr_write_reg(dev, EPAR, phy ? (reg | 0x40) : reg);
> + sr_write_reg(dev, EPCR, phy ? 0xc : 0x4);
> +
> + for (i = 0; i < SR_SHARE_TIMEOUT; i++) {
> + u8 tmp = 0;
> +
> + udelay(1);
> + ret = sr_read_reg(dev, EPCR, &tmp);
> + if (ret < 0)
> + goto out_unlock;
> +
> + /* ready */
> + if ((tmp & EPCR_ERRE) == 0)
> + break;
> + }
> +
> + if (i >= SR_SHARE_TIMEOUT) {
> + netdev_err(dev->net, "%s read timed out!\n", phy ? "phy" : "eeprom");
> + ret = -EIO;
> + goto out_unlock;
> + }
> +
> + sr_write_reg(dev, EPCR, 0x0);
> + ret = sr_read(dev, EPDR, 2, value);
> +
> + netdev_dbg(dev->net, "read shared %d 0x%02x returned 0x%04x, %d\n",
> + phy, reg, *value, ret);
> +
> + out_unlock:
^ please remove space.
(you renamed it but the "no space before label" part has been ignored)
> + mutex_unlock(&dev->phy_mutex);
> + return ret;
> +}
> +
> +static int sr_share_write_word(struct usbnet *dev, int phy, u8 reg, __le16 value)
> +{
> + int ret, i;
> +
> + mutex_lock(&dev->phy_mutex);
> +
> + ret = sr_write(dev, EPDR, 2, &value);
> + if (ret < 0)
> + goto out_unlock;
> +
> + sr_write_reg(dev, EPAR, phy ? (reg | 0x40) : reg);
> + sr_write_reg(dev, EPCR, phy ? 0x1a : 0x12);
> +
> + for (i = 0; i < SR_SHARE_TIMEOUT; i++) {
> + u8 tmp = 0;
> +
> + udelay(1);
> + ret = sr_read_reg(dev, EPCR, &tmp);
> + if (ret < 0)
> + goto out_unlock;
> +
> + /* ready */
> + if ((tmp & EPCR_ERRE) == 0)
> + break;
> + }
I made a remark about the 11 lines above in my first review. May I politely
ask you to read it again ?
[...]
> +static const struct ethtool_ops sr9700_ethtool_ops = {
> + .get_drvinfo = usbnet_get_drvinfo,
> + .get_link = sr9700_get_link,
> + .get_msglevel = usbnet_get_msglevel,
> + .set_msglevel = usbnet_set_msglevel,
> + .get_eeprom_len = sr9700_get_eeprom_len,
> + .get_eeprom = sr9700_get_eeprom,
> + .get_settings = usbnet_get_settings,
> + .set_settings = usbnet_set_settings,
> + .nway_reset = usbnet_nway_reset,
The fields above line up nicely with a 4 spaces tabulation but the
standard kernel one is 8 spaces wide.
[...]
> +static const struct net_device_ops sr9700_netdev_ops = {
> + .ndo_open = usbnet_open,
> + .ndo_stop = usbnet_stop,
> + .ndo_start_xmit = usbnet_start_xmit,
> + .ndo_tx_timeout = usbnet_tx_timeout,
> + .ndo_change_mtu = usbnet_change_mtu,
> + .ndo_validate_addr = eth_validate_addr,
> + .ndo_do_ioctl = sr9700_ioctl,
> + .ndo_set_rx_mode = sr9700_set_multicast,
> + .ndo_set_mac_address = sr9700_set_mac_address,
Same 4 vs 8 spaces tabs problem as above.
[...]
> +static int sr9700_bind(struct usbnet *dev, struct usb_interface *intf)
> +{
> + int ret;
> +
> + ret = usbnet_get_endpoints(dev, intf);
> + if (ret)
> + goto out;
> +
> + dev->net->netdev_ops = &sr9700_netdev_ops;
> + dev->net->ethtool_ops = &sr9700_ethtool_ops;
> + dev->net->hard_header_len += SR_TX_OVERHEAD;
> + dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
> + dev->rx_urb_size = 3072; /* bulkin buffer is preferably not less than 3K */
> +
> + dev->mii.dev = dev->net;
> + dev->mii.mdio_read = sr_mdio_read;
> + dev->mii.mdio_write = sr_mdio_write;
> + dev->mii.phy_id_mask = 0x1f;
> + dev->mii.reg_num_mask = 0x1f;
Please add local dev->net and dev->mii variables.
[...]
> + /* power up and reset phy */
> + sr_write_reg(dev, PRR, 1);
Please replace '1' with appropriate #define.
> + mdelay(20); /* at least 10ms, here 20ms for safe */
> + sr_write_reg(dev, PRR, 0);
Please replace '0' with appropriate #define.
[...]
> +static int sr9700_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
> +{
> + int len;
> + struct sk_buff *sr_skb;
struct sk_buff *sr_skb;
int len;
(long lines first)
[...]
> + /* Each packet contains multiple skbs */
> + while (skb->len > SR_RX_OVERHEAD) {
> + if (skb->data[0] != 0x40)
> + return 0;
> +
> + /* ignore the CRC length */
> + len = (skb->data[1] | (skb->data[2] << 8)) - 4;
> +
> + if (len > ETH_FRAME_LEN)
> + return 0;
> +
> + /* the last skb of current packet */
> + if (skb->len == (len + SR_RX_OVERHEAD)) {
> + skb_pull(skb, 3);
> + skb->len = len;
> + skb->tail = skb->data + len;
> + skb->truesize = len + sizeof(struct sk_buff);
> + return 2;
> + }
> +
> + /* skb_clone is used for address align */
> + sr_skb = skb_clone(skb, GFP_ATOMIC);
> + if (sr_skb) {
if (!sr_skb)
return 0;
> + sr_skb->len = len;
> + sr_skb->data = skb->data + 3;
> + sr_skb->tail = skb->data + len;
> + sr_skb->truesize = len + sizeof(struct sk_buff);
> + usbnet_skb_return(dev, sr_skb);
> + } else {
> + return 0;
> + }
> +
> + skb_pull(skb, len + SR_RX_OVERHEAD);
> + };
> +
> + return 0;
> +}
[...]
> +static struct usb_driver sr9700_usb_driver = {
> + .name = "sr9700",
> + .id_table = products,
> + .probe = usbnet_probe,
> + .disconnect = usbnet_disconnect,
> + .suspend = usbnet_suspend,
> + .resume = usbnet_resume,
> + .disable_hub_initiated_lpm = 1,
> +};
Please line things up (see my first review).
[...]
> ???????? Francois Romieu
> ?????????? 2013-08-21 04:46:12
> ???????? liujunliang_ljl
> ?????? gregkh; sunhecheng; linux-usb; netdev; linux-kernel
> ?????? Re: [PATCH-SR9700] Merge USB 1.1 Ethernet Adapter SR9700 DeviceDriver into the Linux Kernel
Please avoid full quote and top posting.
--
Ueimor
^ permalink raw reply
* Re: [PATCH RESEND net-next 0/3] huawei_cdc_ncm introduction
From: Bjørn Mork @ 2013-08-21 20:26 UTC (permalink / raw)
To: Enrico Mioso, netdev
In-Reply-To: <1377010256-19063-1-git-send-email-mrkiko.rs@gmail.com>
Enrico Mioso <mrkiko.rs@gmail.com> wrote:
>
>These patches are all related to the new huawei_cdc_ncm driver,
>supporting
>devices that use the NCM protocol as a transport layer for other
>protocols.
>this is the case of the Huawei E3131 3G modem.
>Thanks should go to bjorn and others who helped me with patience.
>
>Enrico Mioso (3):
> net: cdc_ncm: Export cdc_ncm_{tx,rx}_fixup functions for re-use
> net: huawei_cdc_ncm: Introduce the
> net: cdc_ncm: remove non-standard NCM device IDs
>
> drivers/net/usb/Kconfig | 11 +++++++++++
> drivers/net/usb/Makefile | 1 +
> drivers/net/usb/cdc_ncm.c | 17 ++++-------------
> include/linux/usb/cdc_ncm.h | 3 +++
> 4 files changed, 19 insertions(+), 13 deletions(-)
Just for the record: I strongly support this.
The Huawei devices were added to the cdc_ncm driver without any understanding of the embedded management protocol. We initially believed that the devices could be managed through an associated serial port. Now that Enrico has found that this isn't necessarily true, and that the embedded protocol is simply AT commands, it is clear that we should expose the embedded protocol to userspace. The absolutely best way to do that is by creating a new driver for these devices, reusing the parts of cdc-wdm and cdc-ncm which are common. Just like Enrico has done here.
Thanks for doing this!
Bjørn
^ permalink raw reply
* Re: [RFC] phy: micrel: Convert micrel PHY driver to use OF
From: Dinh Nguyen @ 2013-08-21 20:00 UTC (permalink / raw)
To: Linus Walleij
Cc: devicetree@vger.kernel.org, Dinh Nguyen, netdev@vger.kernel.org,
Richard Cochran, Felipe Balbi, David S. Miller,
Giuseppe Cavallaro, Olof Johansson, Rob Herring
In-Reply-To: <CACRpkdZkyeA5ExvHQPExNBq5qB1hDHON32TO63BmtVrRxDxhXw@mail.gmail.com>
On Thu, 2013-08-15 at 21:55 +0200, Linus Walleij wrote:
> On Tue, Aug 13, 2013 at 6:42 PM, <dinguyen@altera.com> wrote:
>
> > From: Dinh Nguyen <dinguyen@altera.com>
> >
> > Convert the Micrel PHY driver to use OF. This initial patch is only
> > the beginning of an idea to convert the PHY driver to device tree.
> >
> > Signed-of-by: Dinh Nguyen <dinguyen@altera.com>
>
> This is full of device tree stuff so please copy it to
> devicetree@vger.kernel.org.
>
> It seems to add quite a few compatible strings so these should
> be added to the bindings doc, or is it already?
No, there is not a bindings doc yet. I was just trying to see if this is
the right approach..
Thanks,
Dinh
>
> Yours,
> Linus Walleij
>
^ permalink raw reply
* Re: [PATCH v3] net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)
From: Joe Perches @ 2013-08-21 19:38 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Arvid Brodin, netdev@vger.kernel.org, Stephen Hemminger,
Javier Boticario, balferreira, David Miller
In-Reply-To: <52150F95.7060004@cogentembedded.com>
On Wed, 2013-08-21 at 23:05 +0400, Sergei Shtylyov wrote:
> On 08/21/2013 11:01 PM, Joe Perches wrote:
> >> High-availability Seamless Redundancy ("HSR") provides instant failover
> >> redundancy for Ethernet networks. It requires a special network topology where
> >> all nodes are connected in a ring (each node having two physical network
> >> interfaces). It is suited for applications that demand high availability and
> >> very short reaction time.
Hi back.
> >> + if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN ||
> >> + res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) {
>
> > This might be faster using | instead of ||
>
> Doesn't || use shortcut evaluation in C? I.e. if the 1st == evaluates to
> true, other comparisons are not even done.
Yes it does.
But if the 1st 3 comparisons are false and the
last comparison is true then those 3 tests and
jumps are also done which takes additional
branch predictions paths. Oring is fast.
^ permalink raw reply
* Re: [PATCH 0/8] net: remove unnecessary dev_set_drvdata()
From: David Miller @ 2013-08-21 19:27 UTC (permalink / raw)
To: libo.chen
Cc: netdev, mugunthanvnm, sergei.shtylyov, jg1.han, leoli, gregkh,
linuxppc-dev, lizefan
In-Reply-To: <1377068737-48616-1-git-send-email-libo.chen@huawei.com>
From: Libo Chen <libo.chen@huawei.com>
Date: Wed, 21 Aug 2013 15:05:37 +0800
> Unnecessary dev_set_drvdata() is removed, because the driver core
> clears the driver data to NULL after device_release or on probe failure.
Series applied, thanks.
^ permalink raw reply
* Re: [PATCH net-next 0/4] tun: Some bits required for tun's checkpoint-restore (v2)
From: David Miller @ 2013-08-21 19:22 UTC (permalink / raw)
To: xemul; +Cc: netdev
In-Reply-To: <521496EF.8090909@parallels.com>
From: Pavel Emelyanov <xemul@parallels.com>
Date: Wed, 21 Aug 2013 14:31:11 +0400
> After taking a closer look on tun checkpoint-restore I've found several
> issues with the tun's API that make it impossible to dump and restore
> the state of tun device and attached tun-files.
>
> The proposed API changes are all about extending the existing ioctl-based
> stuff. Patches fit today's net-next.
>
> This v2 has David's comments about patch #1 fixed. All the rest is the same.
>
> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Series applied, thanks.
^ permalink raw reply
* Re: [PATCH net-next V2 0/5] Mellanox driver updates 2013-08-19
From: David Miller @ 2013-08-21 19:20 UTC (permalink / raw)
To: amirv; +Cc: netdev
In-Reply-To: <1377068939-17561-1-git-send-email-amirv@mellanox.com>
From: Amir Vadai <amirv@mellanox.com>
Date: Wed, 21 Aug 2013 10:08:54 +0300
> This small patchset contains 2 fixes to bad handling of error flows in TX
> datapath, and a fix to a bug in mlx4_en_dcbnl_ieee_setpfc() where global pause
> frames were enabled whenever PFC was configured.
> Also added a patch to reduce the scope of some local variables in the moved
> block of code and it's surrounding.
Series applied, thanks.
^ permalink raw reply
* Re: [PATCH net-next 0/5] qlcnic: Implement Multi Tx queues and ndo_get_phys_port support for 82xx.
From: David Miller @ 2013-08-21 19:17 UTC (permalink / raw)
To: himanshu.madhani; +Cc: netdev, Dept_NX_Linux_NIC_Driver
In-Reply-To: <cover.1377122994.git.himanshu.madhani@qlogic.com>
From: Himanshu Madhani <himanshu.madhani@qlogic.com>
Date: Wed, 21 Aug 2013 11:24:09 -0400
> From: Himanshu Madhani <himanshu.madhani@qlogic.com>
>
> This patch series contains following updates
>
> o Patches for enabling multiple Tx queues for 82xx series adapter
>
> o Patch to implement ndo_get_phys_port support for 82xx series adapter.
>
> Please apply to net-next.
Series applied, thanks.
^ permalink raw reply
* Re: [net-next 11/15] igb: Expose RSS indirection table for ethtool
From: Ben Hutchings @ 2013-08-21 19:15 UTC (permalink / raw)
To: Jeff Kirsher; +Cc: davem, Laura Mihaela Vasilescu, netdev, gospo, sassmann
In-Reply-To: <1377073416-7872-12-git-send-email-jeffrey.t.kirsher@intel.com>
On Wed, 2013-08-21 at 01:23 -0700, Jeff Kirsher wrote:
[...]
> --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
> +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
[...]
> +static int igb_set_rxfh_indir(struct net_device *netdev, const u32 *indir)
> +{
[...]
> + /* Verify user input. */
> + for (i = 0; i < IGB_RETA_SIZE; i++)
> + if (indir[i] > num_queues)
[...]
Should be:
if (indir[i] >= num_queues)
Ben.
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* Re: [PATCH 0/2] fix generic netlink locking issue(s)
From: Oliver Hartkopp @ 2013-08-21 19:05 UTC (permalink / raw)
To: Johannes Berg; +Cc: netdev, Thomas Graf
In-Reply-To: <1377094083-8122-1-git-send-email-johannes@sipsolutions.net>
On 21.08.2013 16:08, Johannes Berg wrote:
> Here's another attempt at fixing the genetlink locking issue,
> hopefully better tested this time. Sorry about the mess the
> previous version caused.
>
> I really didn't find a way to "simply" add locking, no matter
> which way I turn genetlink is special because it would then use
> the same locks "inside" and "outside" the generic netlink family;
> "inside" because I'm trying to protect the otherwise unlocked
> dump call, and "outside" because it itself is a generic netlink
> family so needs to protect things there.
>
> As a result, I've turned to RCU and (hopefully) made it safe.
> The unregistration can get fairly expensive with all the calls
> to synchronize_rcu(), but I don't see any better way, and it's
> hopefully really rare.
>
> The only place that now uses RCU is ctrl_dumpfamily(), but it'd
> be possible to use it in other places. I didn't want to do it
> in this patch, but I also don't see any candidates where that
> would really make sense.
>
> The first patch should obviously go into 3.11, I'll let you
> decide about the second. This one might actually be easier to
> backport than the original one, but it'd still have to be done
> carefully.
I applied both patches.
The lockdep issue vanished and i still can write emails :-)
Thanks Johannes.
Tested-by: Oliver Hartkopp <socketcan@hartkopp.net>
>
> johannes
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCH v3] net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)
From: Sergei Shtylyov @ 2013-08-21 19:05 UTC (permalink / raw)
To: Joe Perches
Cc: Arvid Brodin, netdev@vger.kernel.org, Stephen Hemminger,
Javier Boticario, balferreira, David Miller
In-Reply-To: <1377111680.1936.45.camel@joe-AO722>
Hello.
On 08/21/2013 11:01 PM, Joe Perches wrote:
>> High-availability Seamless Redundancy ("HSR") provides instant failover
>> redundancy for Ethernet networks. It requires a special network topology where
>> all nodes are connected in a ring (each node having two physical network
>> interfaces). It is suited for applications that demand high availability and
>> very short reaction time.
> Hi Arvid.
> just trivia:
>> diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
> []
>> +static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
> []
>> + if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN ||
>> + res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) {
> This might be faster using | instead of ||
Doesn't || use shortcut evaluation in C? I.e. if the 1st == evaluates to
true, other comparisons are not even done.
WBR, Sergei
^ permalink raw reply
* Re: [PATCH v3] net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)
From: Joe Perches @ 2013-08-21 19:01 UTC (permalink / raw)
To: Arvid Brodin
Cc: netdev@vger.kernel.org, Stephen Hemminger, Javier Boticario,
balferreira, David Miller
In-Reply-To: <521504E1.5060309@xdin.com>
On Wed, 2013-08-21 at 20:20 +0200, Arvid Brodin wrote:
> High-availability Seamless Redundancy ("HSR") provides instant failover
> redundancy for Ethernet networks. It requires a special network topology where
> all nodes are connected in a ring (each node having two physical network
> interfaces). It is suited for applications that demand high availability and
> very short reaction time.
Hi Arvid.
just trivia:
> diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
[]
> +static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
[]
> + if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN ||
> + res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) {
This might be faster using | instead of ||
[]
> diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
[]
> +
> +/* above(a, b) - return 1 if a > b, 0 otherwise.
> + */
> +static bool above(u16 a, u16 b)
> +{
> + /* Remove inconsistency where above(a, b) == below(a, b) */
> + if ((int) b - a == 32768)
> + return 0;
> +
> + return (((s16) (b - a)) < 0);
> +}
> +#define below(a, b) above((b), (a))
> +#define above_or_eq(a, b) (!below((a), (b)))
> +#define below_or_eq(a, b) (!above((a), (b)))
These may be overly generic macro names given
these only take u16.
^ permalink raw reply
* Re: [PATCH 2/2] SolutionEngine7724: fix Ether support
From: Sergei Shtylyov @ 2013-08-21 18:58 UTC (permalink / raw)
To: netdev, lethal, linux-sh, davem
In-Reply-To: <201308180219.09528.sergei.shtylyov@cogentembedded.com>
Hello.
On 08/18/2013 02:19 AM, Sergei Shtylyov wrote:
> The Ether platform data is behind the declaration of 'struct sh_eth_plat_data'
> as it's lacking the initializers for the 'register_type' and 'phy_interface'
> fields -- it means they'll be implicitly and wrongly set to SH_ETH_REG_GIGABIT
> and PHY_INTERFACE_MODE_NA. Initialize the fields explicitly and fix off-by-one
> error in the Ether memory resource end, while at it...
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> Cc: stable@vger.kernel.org
> ---
> arch/sh/boards/mach-se/7724/setup.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
> Index: net/arch/sh/boards/mach-se/7724/setup.c
> ===================================================================
> --- net.orig/arch/sh/boards/mach-se/7724/setup.c
> +++ net/arch/sh/boards/mach-se/7724/setup.c
[...]
> @@ -377,6 +377,8 @@ static struct resource sh_eth_resources[
> static struct sh_eth_plat_data sh_eth_plat = {
> .phy = 0x1f, /* SMSC LAN8187 */
> .edmac_endian = EDMAC_LITTLE_ENDIAN,
> + .register_type = SH_ETH_REG_FAST_SH4,
> + .phy_interace = PHY_INTERFACE_MODE_MII,
Darn, "interace" here too, this time spotted by myself. Obviously, I did
copy&paste the same mistake.
WBR, Sergei
^ permalink raw reply
* net: netem: regression/bug: using a pfifo no longer avoids reordering caused by jitter
From: Johannes Naab @ 2013-08-21 18:40 UTC (permalink / raw)
To: netdev; +Cc: mailings, eric.dumazet, stephen
Hi,
This is in relation to the
"[PATCH 2/2] net: netem: always adjust now/delay when not reordering"
discussion.
Since the issue is not related to the proposed patch itself (but the workaround),
I have taken the freedom of starting a new thread.
I tried the pfifo for delay+jitter with reordering avoidance
(as given on the netem website http://www.linuxfoundation.org/collaborate/workgroups/networking/netem#How_to_reorder_packets_based_on_jitter.3F)
in 3.11-rc4, 3.8 and 3.2 (Debian experimental, an old experimental
and current stable kernels).
The jitter reorder avoidance seems to work in 3.2, but no longer in 3.8 or 3.11-rc4.
First the working 3.2:
$ tc qdisc add dev node-net handle 1: root netem delay 1000ms 1000ms
$ ping -D -n -c 25 -i 0.1 10.0.2.1
PING 10.0.2.1 (10.0.2.1) 56(84) bytes of data.
[1377107794.371992] 64 bytes from 10.0.2.1: icmp_req=3 ttl=63 time=1846 ms
[1377107794.372231] 64 bytes from 10.0.2.1: icmp_req=5 ttl=63 time=1630 ms
[1377107794.372341] 64 bytes from 10.0.2.1: icmp_req=2 ttl=63 time=1954 ms
[1377107794.372432] 64 bytes from 10.0.2.1: icmp_req=7 ttl=63 time=1414 ms
[1377107794.372524] 64 bytes from 10.0.2.1: icmp_req=10 ttl=63 time=1090 ms
[1377107794.372629] 64 bytes from 10.0.2.1: icmp_req=1 ttl=63 time=2061 ms
[1377107794.372719] 64 bytes from 10.0.2.1: icmp_req=4 ttl=63 time=1738 ms
[1377107794.372810] 64 bytes from 10.0.2.1: icmp_req=14 ttl=63 time=658 ms
[1377107794.372901] 64 bytes from 10.0.2.1: icmp_req=11 ttl=63 time=982 ms
[1377107794.372971] 64 bytes from 10.0.2.1: icmp_req=8 ttl=63 time=1306 ms
[1377107794.397883] 64 bytes from 10.0.2.1: icmp_req=6 ttl=63 time=1548 ms
[1377107794.603519] 64 bytes from 10.0.2.1: icmp_req=16 ttl=63 time=674 ms
[1377107794.797632] 64 bytes from 10.0.2.1: icmp_req=9 ttl=63 time=1624 ms
[1377107794.952395] 64 bytes from 10.0.2.1: icmp_req=12 ttl=63 time=1454 ms
[1377107795.062865] 64 bytes from 10.0.2.1: icmp_req=15 ttl=63 time=1241 ms
[1377107795.168301] 64 bytes from 10.0.2.1: icmp_req=24 ttl=63 time=390 ms
[1377107795.263823] 64 bytes from 10.0.2.1: icmp_req=18 ttl=63 time=1118 ms
[1377107795.272856] 64 bytes from 10.0.2.1: icmp_req=20 ttl=63 time=911 ms
[1377107795.323775] 64 bytes from 10.0.2.1: icmp_req=19 ttl=63 time=1070 ms
[1377107795.419494] 64 bytes from 10.0.2.1: icmp_req=17 ttl=63 time=1382 ms
[1377107795.478443] 64 bytes from 10.0.2.1: icmp_req=13 ttl=63 time=1872 ms
[1377107795.703842] 64 bytes from 10.0.2.1: icmp_req=21 ttl=63 time=1241 ms
[1377107796.002957] 64 bytes from 10.0.2.1: icmp_req=25 ttl=63 time=1125 ms
[1377107796.434347] 64 bytes from 10.0.2.1: icmp_req=22 ttl=63 time=1864 ms
[1377107796.606394] 64 bytes from 10.0.2.1: icmp_req=23 ttl=63 time=1936 ms
--- 10.0.2.1 ping statistics ---
25 packets transmitted, 25 received, 0% packet loss, time 2567ms
rtt min/avg/max/mdev = 390.813/1365.603/2061.036/438.727 ms, pipe 20
by adding the pfifo, the reordering does not occur:
$ sudo tc qdisc add dev node-net parent 1:1 pfifo limit 1000
$ sudo ping -D -n -c 25 -i 0.1 10.0.2.1
PING 10.0.2.1 (10.0.2.1) 56(84) bytes of data.
[1377107865.731835] 64 bytes from 10.0.2.1: icmp_req=1 ttl=63 time=1855 ms
[1377107865.732082] 64 bytes from 10.0.2.1: icmp_req=2 ttl=63 time=1746 ms
[1377107865.732185] 64 bytes from 10.0.2.1: icmp_req=3 ttl=63 time=1638 ms
[1377107865.732290] 64 bytes from 10.0.2.1: icmp_req=4 ttl=63 time=1530 ms
[1377107865.786417] 64 bytes from 10.0.2.1: icmp_req=5 ttl=63 time=1476 ms
[1377107865.786552] 64 bytes from 10.0.2.1: icmp_req=6 ttl=63 time=1368 ms
[1377107865.978256] 64 bytes from 10.0.2.1: icmp_req=7 ttl=63 time=1452 ms
[1377107865.978349] 64 bytes from 10.0.2.1: icmp_req=8 ttl=63 time=1344 ms
[1377107865.978407] 64 bytes from 10.0.2.1: icmp_req=9 ttl=63 time=1236 ms
[1377107866.840396] 64 bytes from 10.0.2.1: icmp_req=10 ttl=63 time=1990 ms
[1377107866.840597] 64 bytes from 10.0.2.1: icmp_req=11 ttl=63 time=1882 ms
[1377107866.840740] 64 bytes from 10.0.2.1: icmp_req=12 ttl=63 time=1774 ms
[1377107867.020757] 64 bytes from 10.0.2.1: icmp_req=13 ttl=63 time=1847 ms
[1377107867.020893] 64 bytes from 10.0.2.1: icmp_req=14 ttl=63 time=1739 ms
[1377107867.020987] 64 bytes from 10.0.2.1: icmp_req=15 ttl=63 time=1631 ms
[1377107867.021078] 64 bytes from 10.0.2.1: icmp_req=16 ttl=63 time=1523 ms
[1377107867.021168] 64 bytes from 10.0.2.1: icmp_req=17 ttl=63 time=1415 ms
[1377107867.021258] 64 bytes from 10.0.2.1: icmp_req=18 ttl=63 time=1307 ms
[1377107867.021348] 64 bytes from 10.0.2.1: icmp_req=19 ttl=63 time=1207 ms
[1377107867.022735] 64 bytes from 10.0.2.1: icmp_req=20 ttl=63 time=1101 ms
[1377107867.757821] 64 bytes from 10.0.2.1: icmp_req=21 ttl=63 time=1736 ms
[1377107867.758009] 64 bytes from 10.0.2.1: icmp_req=22 ttl=63 time=1628 ms
[1377107867.832652] 64 bytes from 10.0.2.1: icmp_req=23 ttl=63 time=1595 ms
[1377107867.832776] 64 bytes from 10.0.2.1: icmp_req=24 ttl=63 time=1487 ms
[1377107867.832881] 64 bytes from 10.0.2.1: icmp_req=25 ttl=63 time=1379 ms
--- 10.0.2.1 ping statistics ---
25 packets transmitted, 25 received, 0% packet loss, time 2577ms
rtt min/avg/max/mdev = 1101.255/1555.884/1990.917/226.825 ms, pipe 18
In 3.8 and 3.11-rc4 without pfifo (with causes reordering as currently expected):
$ sudo ping -D -n -c 25 -i 0.1 10.0.2.1
PING 10.0.2.1 (10.0.2.1) 56(84) bytes of data.
[1377108036.574818] 64 bytes from 10.0.2.1: icmp_req=1 ttl=63 time=252 ms
[1377108037.846016] 64 bytes from 10.0.2.1: icmp_req=4 ttl=63 time=1207 ms
[1377108037.846200] 64 bytes from 10.0.2.1: icmp_req=5 ttl=63 time=1100 ms
[1377108037.846287] 64 bytes from 10.0.2.1: icmp_req=8 ttl=63 time=776 ms
[1377108037.846399] 64 bytes from 10.0.2.1: icmp_req=6 ttl=63 time=992 ms
[1377108037.846483] 64 bytes from 10.0.2.1: icmp_req=3 ttl=63 time=1308 ms
[1377108038.025777] 64 bytes from 10.0.2.1: icmp_req=10 ttl=63 time=740 ms
[1377108038.072852] 64 bytes from 10.0.2.1: icmp_req=2 ttl=63 time=1643 ms
[1377108038.288748] 64 bytes from 10.0.2.1: icmp_req=9 ttl=63 time=1111 ms
[1377108038.317722] 64 bytes from 10.0.2.1: icmp_req=11 ttl=63 time=924 ms
[1377108038.369579] 64 bytes from 10.0.2.1: icmp_req=13 ttl=63 time=760 ms
[1377108038.384918] 64 bytes from 10.0.2.1: icmp_req=7 ttl=63 time=1423 ms
[1377108038.704846] 64 bytes from 10.0.2.1: icmp_req=14 ttl=63 time=987 ms
[1377108038.852763] 64 bytes from 10.0.2.1: icmp_req=16 ttl=63 time=926 ms
[1377108039.158401] 64 bytes from 10.0.2.1: icmp_req=21 ttl=63 time=724 ms
[1377108039.213823] 64 bytes from 10.0.2.1: icmp_req=12 ttl=63 time=1712 ms
[1377108039.310065] 64 bytes from 10.0.2.1: icmp_req=18 ttl=63 time=1183 ms
[1377108039.457295] 64 bytes from 10.0.2.1: icmp_req=25 ttl=63 time=604 ms
[1377108039.560758] 64 bytes from 10.0.2.1: icmp_req=15 ttl=63 time=1735 ms
[1377108039.847344] 64 bytes from 10.0.2.1: icmp_req=17 ttl=63 time=1821 ms
[1377108039.922450] 64 bytes from 10.0.2.1: icmp_req=23 ttl=63 time=1273 ms
[1377108040.189117] 64 bytes from 10.0.2.1: icmp_req=19 ttl=63 time=1955 ms
[1377108040.233637] 64 bytes from 10.0.2.1: icmp_req=20 ttl=63 time=1899 ms
[1377108040.430303] 64 bytes from 10.0.2.1: icmp_req=22 ttl=63 time=1889 ms
[1377108040.437195] 64 bytes from 10.0.2.1: icmp_req=24 ttl=63 time=1686 ms
--- 10.0.2.1 ping statistics ---
25 packets transmitted, 25 received, 0% packet loss, time 2531ms
rtt min/avg/max/mdev = 252.930/1225.728/1955.892/459.288 ms, pipe 16
Adding the pfifo does not avoid the reordering (which is a regression against 3.2).
$ sudo tc qdisc add dev node-net parent 1:1 pfifo limit 1000
$ sudo ping -D -n -c 25 -i 0.1 10.0.2.1
PING 10.0.2.1 (10.0.2.1) 56(84) bytes of data.
[1377108141.816852] 64 bytes from 10.0.2.1: icmp_req=1 ttl=63 time=1260 ms
[1377108141.817114] 64 bytes from 10.0.2.1: icmp_req=7 ttl=63 time=611 ms
[1377108141.817230] 64 bytes from 10.0.2.1: icmp_req=3 ttl=63 time=1043 ms
[1377108141.817336] 64 bytes from 10.0.2.1: icmp_req=4 ttl=63 time=935 ms
[1377108141.842461] 64 bytes from 10.0.2.1: icmp_req=10 ttl=63 time=313 ms
[1377108142.029281] 64 bytes from 10.0.2.1: icmp_req=2 ttl=63 time=1363 ms
[1377108142.085116] 64 bytes from 10.0.2.1: icmp_req=6 ttl=63 time=987 ms
[1377108142.136125] 64 bytes from 10.0.2.1: icmp_req=8 ttl=63 time=822 ms
[1377108142.359697] 64 bytes from 10.0.2.1: icmp_req=9 ttl=63 time=938 ms
[1377108142.456998] 64 bytes from 10.0.2.1: icmp_req=11 ttl=63 time=819 ms
[1377108142.779780] 64 bytes from 10.0.2.1: icmp_req=20 ttl=63 time=202 ms
[1377108142.779960] 64 bytes from 10.0.2.1: icmp_req=15 ttl=63 time=719 ms
[1377108142.815326] 64 bytes from 10.0.2.1: icmp_req=13 ttl=63 time=962 ms
[1377108142.830840] 64 bytes from 10.0.2.1: icmp_req=5 ttl=63 time=1841 ms
[1377108143.099230] 64 bytes from 10.0.2.1: icmp_req=23 ttl=63 time=207 ms
[1377108143.299404] 64 bytes from 10.0.2.1: icmp_req=18 ttl=63 time=929 ms
[1377108143.333906] 64 bytes from 10.0.2.1: icmp_req=21 ttl=63 time=648 ms
[1377108143.391823] 64 bytes from 10.0.2.1: icmp_req=19 ttl=63 time=921 ms
[1377108143.402795] 64 bytes from 10.0.2.1: icmp_req=12 ttl=63 time=1657 ms
[1377108143.496919] 64 bytes from 10.0.2.1: icmp_req=16 ttl=63 time=1335 ms
[1377108143.680456] 64 bytes from 10.0.2.1: icmp_req=17 ttl=63 time=1411 ms
[1377108143.781552] 64 bytes from 10.0.2.1: icmp_req=14 ttl=63 time=1820 ms
[1377108144.020711] 64 bytes from 10.0.2.1: icmp_req=24 ttl=63 time=1029 ms
[1377108144.374671] 64 bytes from 10.0.2.1: icmp_req=25 ttl=63 time=1283 ms
[1377108144.447944] 64 bytes from 10.0.2.1: icmp_req=22 ttl=63 time=1657 ms
--- 10.0.2.1 ping statistics ---
25 packets transmitted, 25 received, 0% packet loss, time 2534ms
rtt min/avg/max/mdev = 202.462/1029.004/1841.404/445.533 ms, pipe 18
Can anyone confirm this behavior? Is the documentation out of date,
or is that a regression?
Best regards,
Johannes
--
^ permalink raw reply
* [PATCH v3] net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)
From: Arvid Brodin @ 2013-08-21 18:20 UTC (permalink / raw)
To: netdev@vger.kernel.org
Cc: Arvid Brodin, Stephen Hemminger, Joe Perches, Javier Boticario,
balferreira, David Miller
High-availability Seamless Redundancy ("HSR") provides instant failover
redundancy for Ethernet networks. It requires a special network topology where
all nodes are connected in a ring (each node having two physical network
interfaces). It is suited for applications that demand high availability and
very short reaction time.
HSR acts on the Ethernet layer, using a registered Ethernet protocol type to
send special HSR frames in both directions over the ring. The driver creates
virtual network interfaces that can be used just like any ordinary Linux
network interface, for IP/TCP/UDP traffic etc. All nodes in the network ring
must be HSR capable.
This code is a "best effort" to comply with the HSR standard as described in
IEC 62439-3:2010 (HSRv0).
Signed-off-by: Arvid Brodin <arvid.brodin@xdin.com>
---
This is a patch against net-next (2013-08-21).
The code has been statically analysed using sparse (no problems detected).
kmemleak has been used to detect memory leaks (no known leaks exist).
The code passes checkpatch.pl with the options --strict --max-line-length=83
except for CHECKs, which are all intentional; e.g. CamelCase is used in the
IEC standard to describe field names, and these names have been used in the
code.
The code has been tested extensively on rings of up to 32 avr32-based nodes.
Less intense testing has included an x86_64 (SMP) system, and running HSR
over a USB-to-Ethernet device.
Changes in v3:
* hsr_dev_open() no longer calls dev_open() on the HSR slave interfaces.
This is to let user space decide how to treat any errors on slave open.
* is_operstate_up() is now called is_slave_up() and checks both admin
state and operational state. Slaves going DOWN could otherwise be
detected as still UP when HSR decided its own state.
* Files under net/ now have header comments that follow the network code
standard.
Changes in v2:
* Aligned newlines with open parenthesis (Joe Perches)
* Removed unneccessary WARN_ON_ONCE from is_hsr_master() (Joe Perches)
* Removed deprecated comments about "actually signed" for HSR_A_IFn_AGE (Joe Perches)
* Broke out userspace hsr_netlink.h into include/uapi/linux/ (Joe Perches)
* Use netif_oper_up() to check for operational state UP (Stephen Hemminger)
Possibly unresolved:
* dev_base_lock vs rtnl_mutex when setting operstate? (Stephen Hemminger)
* Move more (local) header files into include/net/? (checkpatch still warns
about CamelCase) (Joe Perches / with help from David Miller?)
Earlier RFC:s:
RFC v1: http://www.spinics.net/lists/netdev/msg192817.html
RFC v2: http://www.spinics.net/lists/netdev/msg203397.html
RFC v3: http://www.spinics.net/lists/netdev/msg207816.html
RFC v4: http://www.spinics.net/lists/netdev/msg213309.html
Thanks to Stephen Hemminger, Joe Perches, and others for their comments on
these RFC:s.
include/uapi/linux/hsr_netlink.h | 50 ++++
include/uapi/linux/if_ether.h | 1 +
include/uapi/linux/if_link.h | 13 +
net/Kconfig | 1 +
net/Makefile | 1 +
net/hsr/Kconfig | 27 ++
net/hsr/Makefile | 7 +
net/hsr/hsr_device.c | 592 +++++++++++++++++++++++++++++++++++++++
net/hsr/hsr_device.h | 29 ++
net/hsr/hsr_framereg.c | 501 +++++++++++++++++++++++++++++++++
net/hsr/hsr_framereg.h | 53 ++++
net/hsr/hsr_main.c | 458 ++++++++++++++++++++++++++++++
net/hsr/hsr_main.h | 166 +++++++++++
net/hsr/hsr_netlink.c | 457 ++++++++++++++++++++++++++++++
net/hsr/hsr_netlink.h | 30 ++
15 files changed, 2386 insertions(+)
create mode 100644 include/uapi/linux/hsr_netlink.h
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_main.h
create mode 100644 net/hsr/hsr_netlink.c
create mode 100644 net/hsr/hsr_netlink.h
diff --git a/include/uapi/linux/hsr_netlink.h b/include/uapi/linux/hsr_netlink.h
new file mode 100644
index 0000000..2475cb8
--- /dev/null
+++ b/include/uapi/linux/hsr_netlink.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011-2013 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-2013 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef __UAPI_HSR_NETLINK_H
+#define __UAPI_HSR_NETLINK_H
+
+/* Generic Netlink HSR family definition
+ */
+
+/* attributes */
+enum {
+ HSR_A_UNSPEC,
+ HSR_A_NODE_ADDR,
+ HSR_A_IFINDEX,
+ HSR_A_IF1_AGE,
+ HSR_A_IF2_AGE,
+ HSR_A_NODE_ADDR_B,
+ HSR_A_IF1_SEQ,
+ HSR_A_IF2_SEQ,
+ HSR_A_IF1_IFINDEX,
+ HSR_A_IF2_IFINDEX,
+ HSR_A_ADDR_B_IFINDEX,
+ __HSR_A_MAX,
+};
+#define HSR_A_MAX (__HSR_A_MAX - 1)
+
+
+/* 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_GET_NODE_LIST,
+ HSR_C_SET_NODE_LIST,
+ __HSR_C_MAX,
+};
+#define HSR_C_MAX (__HSR_C_MAX - 1)
+
+#endif /* __UAPI_HSR_NETLINK_H */
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index ade07f1..2ce0f6a 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -85,6 +85,7 @@
#define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */
#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */
#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */
+#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */
#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/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 04c0e7a..3ff5b20 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -468,4 +468,17 @@ enum {
#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1)
+
+/* HSR section */
+
+enum {
+ IFLA_HSR_UNSPEC,
+ IFLA_HSR_SLAVE1,
+ IFLA_HSR_SLAVE2,
+ IFLA_HSR_MULTICAST_SPEC,
+ __IFLA_HSR_MAX,
+};
+
+#define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1)
+
#endif /* _UAPI_LINUX_IF_LINK_H */
diff --git a/net/Kconfig b/net/Kconfig
index ee02136..718935c 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -220,6 +220,7 @@ source "net/openvswitch/Kconfig"
source "net/vmw_vsock/Kconfig"
source "net/netlink/Kconfig"
source "net/mpls/Kconfig"
+source "net/hsr/Kconfig"
config RPS
boolean
diff --git a/net/Makefile b/net/Makefile
index 9492e8c..8fa2f91 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -71,3 +71,4 @@ obj-$(CONFIG_NFC) += nfc/
obj-$(CONFIG_OPENVSWITCH) += openvswitch/
obj-$(CONFIG_VSOCKETS) += vmw_vsock/
obj-$(CONFIG_NET_MPLS_GSO) += mpls/
+obj-$(CONFIG_HSR) += hsr/
diff --git a/net/hsr/Kconfig b/net/hsr/Kconfig
new file mode 100644
index 0000000..0d3d709
--- /dev/null
+++ b/net/hsr/Kconfig
@@ -0,0 +1,27 @@
+#
+# 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 it 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:2010 (HSRv0), 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!
+
+ If unsure, say N.
diff --git a/net/hsr/Makefile b/net/hsr/Makefile
new file mode 100644
index 0000000..b68359f
--- /dev/null
+++ b/net/hsr/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for HSR
+#
+
+obj-$(CONFIG_HSR) += hsr.o
+
+hsr-y := hsr_main.o hsr_framereg.o hsr_device.o hsr_netlink.o
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
new file mode 100644
index 0000000..2a2bb8f
--- /dev/null
+++ b/net/hsr/hsr_device.c
@@ -0,0 +1,592 @@
+/* Copyright 2011-2013 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-2013 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/pkt_sched.h>
+#include "hsr_device.h"
+#include "hsr_framereg.h"
+#include "hsr_main.h"
+
+
+static bool is_admin_up(struct net_device *dev)
+{
+ return dev && (dev->flags & IFF_UP);
+}
+
+static bool is_slave_up(struct net_device *dev)
+{
+ return dev && is_admin_up(dev) && netif_oper_up(dev);
+}
+
+static void __hsr_set_operstate(struct net_device *dev, int transition)
+{
+ if (dev->operstate != transition) {
+ write_lock_bh(&dev_base_lock);
+ dev->operstate = transition;
+ write_unlock_bh(&dev_base_lock);
+ netdev_state_change(dev);
+ }
+}
+
+void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1,
+ struct net_device *slave2)
+{
+ if (!is_admin_up(hsr_dev)) {
+ __hsr_set_operstate(hsr_dev, IF_OPER_DOWN);
+ return;
+ }
+
+ if (is_slave_up(slave1) || is_slave_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_slave_up(slave1) || is_slave_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);
+}
+
+
+int hsr_get_max_mtu(struct hsr_priv *hsr_priv)
+{
+ int mtu_max;
+
+ if (hsr_priv->slave[0] && hsr_priv->slave[1])
+ mtu_max = min(hsr_priv->slave[0]->mtu, hsr_priv->slave[1]->mtu);
+ else if (hsr_priv->slave[0])
+ mtu_max = hsr_priv->slave[0]->mtu;
+ else if (hsr_priv->slave[1])
+ mtu_max = hsr_priv->slave[1]->mtu;
+ else
+ mtu_max = HSR_TAGLEN;
+
+ return mtu_max - HSR_TAGLEN;
+}
+
+static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct hsr_priv *hsr_priv;
+
+ hsr_priv = netdev_priv(dev);
+
+ if (new_mtu > hsr_get_max_mtu(hsr_priv)) {
+ netdev_info(hsr_priv->dev, "A HSR master's MTU cannot be greater than the smallest MTU of its slaves minus the HSR Tag length (%d octets).\n",
+ HSR_TAGLEN);
+ return -EINVAL;
+ }
+
+ dev->mtu = new_mtu;
+
+ return 0;
+}
+
+static int hsr_dev_open(struct net_device *dev)
+{
+ struct hsr_priv *hsr_priv;
+ int i;
+ char *slave_name;
+
+ hsr_priv = netdev_priv(dev);
+
+ for (i = 0; i < HSR_MAX_SLAVE; i++) {
+ if (hsr_priv->slave[i])
+ slave_name = hsr_priv->slave[i]->name;
+ else
+ slave_name = "null";
+
+ if (!is_slave_up(hsr_priv->slave[i]))
+ netdev_warn(dev, "Slave %c (%s) is not up; please bring it up to get a working HSR network\n",
+ 'A' + i, slave_name);
+ }
+
+ return 0;
+}
+
+static int hsr_dev_close(struct net_device *dev)
+{
+ /* Nothing to do here. We could try to restore the state of the slaves
+ * to what they were before being changed by the hsr master dev's state,
+ * but they might have been changed manually in the mean time too, so
+ * taking them up or down here might be confusing and is probably not a
+ * good idea.
+ */
+ return 0;
+}
+
+
+static void hsr_fill_tag(struct hsr_ethhdr *hsr_ethhdr, struct hsr_priv *hsr_priv)
+{
+ unsigned long irqflags;
+
+ /* IEC 62439-1:2010, p 48, says the 4-bit "path" field can take values
+ * between 0001-1001 ("ring identifier", for regular HSR frames),
+ * or 1111 ("HSR management", supervision frames). Unfortunately, the
+ * spec writers forgot to explain what a "ring identifier" is, or
+ * how it is used. So we just set this to 0001 for regular frames,
+ * and 1111 for supervision frames.
+ */
+ set_hsr_tag_path(&hsr_ethhdr->hsr_tag, 0x1);
+
+ /* IEC 62439-1:2010, p 12: "The link service data unit in an Ethernet
+ * frame is the content of the frame located between the Length/Type
+ * field and the Frame Check Sequence."
+ *
+ * IEC 62439-3, p 48, specifies the "original LPDU" to include the
+ * original "LT" field (what "LT" means is not explained anywhere as
+ * far as I can see - perhaps "Length/Type"?). So LSDU_size might
+ * equal original length + 2.
+ * Also, the fact that this field is not used anywhere (might be used
+ * by a RedBox connecting HSR and PRP nets?) means I cannot test its
+ * correctness. Instead of guessing, I set this to 0 here, to make any
+ * problems immediately apparent. Anyone using this driver with PRP/HSR
+ * RedBoxes might need to fix this...
+ */
+ set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, 0);
+
+ spin_lock_irqsave(&hsr_priv->seqnr_lock, irqflags);
+ hsr_ethhdr->hsr_tag.sequence_nr = htons(hsr_priv->sequence_nr);
+ hsr_priv->sequence_nr++;
+ spin_unlock_irqrestore(&hsr_priv->seqnr_lock, irqflags);
+
+ hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto;
+
+ hsr_ethhdr->ethhdr.h_proto = htons(ETH_P_PRP);
+}
+
+static int slave_xmit(struct sk_buff *skb, struct hsr_priv *hsr_priv,
+ enum hsr_dev_idx dev_idx)
+{
+ struct hsr_ethhdr *hsr_ethhdr;
+
+ hsr_ethhdr = (struct hsr_ethhdr *) skb->data;
+
+ skb->dev = hsr_priv->slave[dev_idx];
+
+ hsr_addr_subst_dest(hsr_priv, &hsr_ethhdr->ethhdr, dev_idx);
+
+ /* 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, skb->dev->dev_addr, ETH_ALEN);
+
+ 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 ((skb->protocol != htons(ETH_P_PRP)) ||
+ (hsr_ethhdr->ethhdr.h_proto != htons(ETH_P_PRP))) {
+ hsr_fill_tag(hsr_ethhdr, hsr_priv);
+ skb->protocol = htons(ETH_P_PRP);
+ }
+
+ skb2 = pskb_copy(skb, GFP_ATOMIC);
+
+ res1 = NET_XMIT_DROP;
+ if (likely(hsr_priv->slave[HSR_DEV_SLAVE_A]))
+ res1 = slave_xmit(skb, hsr_priv, HSR_DEV_SLAVE_A);
+
+ res2 = NET_XMIT_DROP;
+ if (likely(skb2 && hsr_priv->slave[HSR_DEV_SLAVE_B]))
+ res2 = slave_xmit(skb2, hsr_priv, HSR_DEV_SLAVE_B);
+
+ if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN ||
+ res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) {
+ hsr_priv->dev->stats.tx_packets++;
+ hsr_priv->dev->stats.tx_bytes += skb->len;
+ } else {
+ hsr_priv->dev->stats.tx_dropped++;
+ }
+
+ return NETDEV_TX_OK;
+}
+
+
+static int hsr_header_create(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned int len)
+{
+ int res;
+
+ /* Make room for the HSR tag now. We will fill it in later (in
+ * hsr_dev_xmit)
+ */
+ if (skb_headroom(skb) < HSR_TAGLEN + ETH_HLEN)
+ return -ENOBUFS;
+ skb_push(skb, HSR_TAGLEN);
+
+ /* To allow VLAN/HSR combos we should probably use
+ * res = dev_hard_header(skb, dev, type, daddr, saddr, len + HSR_TAGLEN);
+ * here instead. It would require other changes too, though - e.g.
+ * separate headers for each slave etc...
+ */
+ 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,
+};
+
+
+/* HSR:2010 supervision frames should be padded so that the whole frame,
+ * including headers and FCS, is 64 bytes (without VLAN).
+ */
+static int hsr_pad(int size)
+{
+ const int min_size = ETH_ZLEN - HSR_TAGLEN - ETH_HLEN;
+
+ if (size >= min_size)
+ return size;
+ return min_size;
+}
+
+static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type)
+{
+ struct hsr_priv *hsr_priv;
+ struct sk_buff *skb;
+ int hlen, tlen;
+ struct hsr_sup_tag *hsr_stag;
+ struct hsr_sup_payload *hsr_sp;
+ unsigned long irqflags;
+
+ hlen = LL_RESERVED_SPACE(hsr_dev);
+ tlen = hsr_dev->needed_tailroom;
+ skb = alloc_skb(hsr_pad(sizeof(struct hsr_sup_payload)) + hlen + tlen,
+ GFP_ATOMIC);
+
+ if (skb == NULL)
+ return;
+
+ hsr_priv = netdev_priv(hsr_dev);
+
+ skb_reserve(skb, hlen);
+
+ skb->dev = hsr_dev;
+ skb->protocol = htons(ETH_P_PRP);
+ skb->priority = TC_PRIO_CONTROL;
+
+ if (dev_hard_header(skb, skb->dev, ETH_P_PRP,
+ hsr_priv->sup_multicast_addr,
+ skb->dev->dev_addr, skb->len) < 0)
+ goto out;
+
+ skb_pull(skb, sizeof(struct ethhdr));
+ hsr_stag = (typeof(hsr_stag)) skb->data;
+
+ set_hsr_stag_path(hsr_stag, 0xf);
+ set_hsr_stag_HSR_Ver(hsr_stag, 0);
+
+ spin_lock_irqsave(&hsr_priv->seqnr_lock, irqflags);
+ hsr_stag->sequence_nr = htons(hsr_priv->sequence_nr);
+ hsr_priv->sequence_nr++;
+ spin_unlock_irqrestore(&hsr_priv->seqnr_lock, irqflags);
+
+ hsr_stag->HSR_TLV_Type = type;
+ hsr_stag->HSR_TLV_Length = 12;
+
+ skb_push(skb, sizeof(struct ethhdr));
+
+ /* Payload: MacAddressA */
+ hsr_sp = (typeof(hsr_sp)) skb_put(skb, sizeof(*hsr_sp));
+ memcpy(hsr_sp->MacAddressA, hsr_dev->dev_addr, ETH_ALEN);
+
+ dev_queue_xmit(skb);
+ return;
+
+out:
+ kfree_skb(skb);
+}
+
+
+/* Announce (supervision frame) timer function
+ */
+static void hsr_announce(unsigned long data)
+{
+ struct hsr_priv *hsr_priv;
+
+ hsr_priv = (struct hsr_priv *) data;
+
+ if (hsr_priv->announce_count < 3) {
+ send_hsr_supervision_frame(hsr_priv->dev, HSR_TLV_ANNOUNCE);
+ hsr_priv->announce_count++;
+ } else {
+ send_hsr_supervision_frame(hsr_priv->dev, HSR_TLV_LIFE_CHECK);
+ }
+
+ if (hsr_priv->announce_count < 3)
+ hsr_priv->announce_timer.expires = jiffies +
+ msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL);
+ else
+ hsr_priv->announce_timer.expires = jiffies +
+ msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL);
+
+ if (is_admin_up(hsr_priv->dev))
+ add_timer(&hsr_priv->announce_timer);
+}
+
+
+static void restore_slaves(struct net_device *hsr_dev)
+{
+ struct hsr_priv *hsr_priv;
+ int i;
+ int res;
+
+ hsr_priv = netdev_priv(hsr_dev);
+
+ rtnl_lock();
+
+ /* Restore promiscuity */
+ for (i = 0; i < 2; i++) {
+ if (!hsr_priv->slave[i])
+ continue;
+ res = dev_set_promiscuity(hsr_priv->slave[i], -1);
+ if (res)
+ netdev_info(hsr_dev, "Cannot restore slave promiscuity (%s, %d)\n",
+ hsr_priv->slave[i]->name, res);
+ }
+
+ rtnl_unlock();
+}
+
+static void reclaim_hsr_dev(struct rcu_head *rh)
+{
+ struct hsr_priv *hsr_priv;
+
+ hsr_priv = container_of(rh, struct hsr_priv, rcu_head);
+ free_netdev(hsr_priv->dev);
+}
+
+
+/* According to comments in the declaration of struct net_device, this function
+ * is "Called from unregister, can be used to call free_netdev". Ok then...
+ */
+static void hsr_dev_destroy(struct net_device *hsr_dev)
+{
+ struct hsr_priv *hsr_priv;
+
+ hsr_priv = netdev_priv(hsr_dev);
+
+ del_timer(&hsr_priv->announce_timer);
+ unregister_hsr_master(hsr_priv); /* calls list_del_rcu on hsr_priv */
+ restore_slaves(hsr_dev);
+ call_rcu(&hsr_priv->rcu_head, reclaim_hsr_dev); /* reclaim hsr_priv */
+}
+
+static const struct net_device_ops hsr_device_ops = {
+ .ndo_change_mtu = hsr_dev_change_mtu,
+ .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->tx_queue_len = 0;
+
+ dev->destructor = hsr_dev_destroy;
+}
+
+
+/* If dev is a HSR master, return 1; otherwise, return 0.
+ */
+bool is_hsr_master(struct net_device *dev)
+{
+ return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit);
+}
+
+static int check_slave_ok(struct net_device *dev)
+{
+ /* Don't allow HSR on non-ethernet like devices */
+ if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) ||
+ (dev->addr_len != ETH_ALEN)) {
+ netdev_info(dev, "Cannot use loopback or non-ethernet device as HSR slave.\n");
+ return -EINVAL;
+ }
+
+ /* Don't allow enslaving hsr devices */
+ if (is_hsr_master(dev)) {
+ netdev_info(dev, "Cannot create trees of HSR devices.\n");
+ return -EINVAL;
+ }
+
+ if (is_hsr_slave(dev)) {
+ netdev_info(dev, "This device is already a HSR slave.\n");
+ return -EINVAL;
+ }
+
+ if (dev->priv_flags & IFF_802_1Q_VLAN) {
+ netdev_info(dev, "HSR on top of VLAN is not yet supported in this driver.\n");
+ return -EINVAL;
+ }
+
+ /* HSR over bonded devices has not been tested, but I'm not sure it
+ * won't work...
+ */
+
+ return 0;
+}
+
+
+/* Default multicast address for HSR Supervision frames */
+static const unsigned char def_multicast_addr[ETH_ALEN] = {
+ 0x01, 0x15, 0x4e, 0x00, 0x01, 0x00
+};
+
+int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
+ unsigned char multicast_spec)
+{
+ 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[i] = slave[i];
+
+ spin_lock_init(&hsr_priv->seqnr_lock);
+ /* Overflow soon to find bugs easier: */
+ hsr_priv->sequence_nr = USHRT_MAX - 1024;
+
+ init_timer(&hsr_priv->announce_timer);
+ hsr_priv->announce_timer.function = hsr_announce;
+ hsr_priv->announce_timer.data = (unsigned long) hsr_priv;
+
+ memcpy(hsr_priv->sup_multicast_addr, def_multicast_addr, ETH_ALEN);
+ hsr_priv->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec;
+
+/* FIXME: should I modify the value of these?
+ *
+ * - hsr_dev->flags - i.e.
+ * IFF_MASTER/SLAVE?
+ * - hsr_dev->priv_flags - i.e.
+ * IFF_EBRIDGE?
+ * IFF_TX_SKB_SHARING?
+ * IFF_HSR_MASTER/SLAVE?
+ */
+
+ 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;
+ /* Prevent recursive tx locking */
+ hsr_dev->features |= NETIF_F_LLTX;
+ /* VLAN on top of HSR needs testing and probably some work on
+ * hsr_header_create() etc.
+ */
+ hsr_dev->features |= NETIF_F_VLAN_CHALLENGED;
+
+ /* Set hsr_dev's MAC address to that of mac_slave1 */
+ memcpy(hsr_dev->dev_addr, hsr_priv->slave[0]->dev_addr, ETH_ALEN);
+
+ /* Set required header length */
+ for (i = 0; i < 2; i++)
+ if (slave[i]->hard_header_len + HSR_TAGLEN >
+ hsr_dev->hard_header_len)
+ hsr_dev->hard_header_len =
+ slave[i]->hard_header_len + HSR_TAGLEN;
+
+ /* MTU */
+ for (i = 0; i < 2; i++)
+ if (slave[i]->mtu - HSR_TAGLEN < hsr_dev->mtu)
+ hsr_dev->mtu = slave[i]->mtu - HSR_TAGLEN;
+
+ /* Make sure the 1st call to netif_carrier_on() gets through */
+ netif_carrier_off(hsr_dev);
+
+ /* Promiscuity */
+ for (i = 0; i < 2; i++) {
+ res = dev_set_promiscuity(slave[i], 1);
+ if (res) {
+ netdev_info(hsr_dev, "Cannot set slave promiscuity (%s, %d)\n",
+ slave[i]->name, res);
+ goto fail;
+ }
+ }
+
+ /* Make sure we recognize frames from ourselves in hsr_rcv() */
+ res = hsr_create_self_node(&hsr_priv->self_node_db,
+ hsr_dev->dev_addr,
+ hsr_priv->slave[1]->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..2c7148e
--- /dev/null
+++ b/net/hsr/hsr_device.h
@@ -0,0 +1,29 @@
+/* Copyright 2011-2013 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-2013 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef __HSR_DEVICE_H
+#define __HSR_DEVICE_H
+
+#include <linux/netdevice.h>
+#include "hsr_main.h"
+
+void hsr_dev_setup(struct net_device *dev);
+int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
+ unsigned char multicast_spec);
+void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1,
+ struct net_device *slave2);
+void hsr_set_carrier(struct net_device *hsr_dev, struct net_device *slave1,
+ struct net_device *slave2);
+void hsr_check_announce(struct net_device *hsr_dev, int old_operstate);
+bool is_hsr_master(struct net_device *dev);
+int hsr_get_max_mtu(struct hsr_priv *hsr_priv);
+
+#endif /* __HSR_DEVICE_H */
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
new file mode 100644
index 0000000..fbcd7fc
--- /dev/null
+++ b/net/hsr/hsr_framereg.c
@@ -0,0 +1,501 @@
+/* Copyright 2011-2013 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-2013 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, and to detect HSR ring errors.
+ */
+
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/rculist.h>
+#include "hsr_main.h"
+#include "hsr_framereg.h"
+#include "hsr_netlink.h"
+
+
+struct node_entry {
+ struct list_head mac_list;
+ unsigned char MacAddressA[ETH_ALEN];
+ unsigned char MacAddressB[ETH_ALEN];
+ enum hsr_dev_idx AddrB_if; /* The local slave through which AddrB
+ * frames are received from this node
+ */
+ unsigned long time_in[HSR_MAX_SLAVE];
+ bool time_in_stale[HSR_MAX_SLAVE];
+ u16 seq_out[HSR_MAX_DEV];
+ struct rcu_head rcu_head;
+};
+
+/* TODO: use hash lists for mac addresses (linux/jhash.h)? */
+
+
+
+/* Search for mac entry. Caller must hold rcu read lock.
+ */
+static struct node_entry *find_node_by_AddrA(struct list_head *node_db,
+ const 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,
+ const unsigned char addr[ETH_ALEN])
+{
+ struct node_entry *node;
+
+ list_for_each_entry_rcu(node, node_db, mac_list) {
+ if (!compare_ether_addr(node->MacAddressB, addr))
+ return node;
+ }
+
+ return NULL;
+}
+
+
+/* Search for mac entry. Caller must hold rcu read lock.
+ */
+struct node_entry *hsr_find_node(struct list_head *node_db, struct sk_buff *skb)
+{
+ struct node_entry *node;
+ struct ethhdr *ethhdr;
+
+ if (!skb_mac_header_was_set(skb))
+ return NULL;
+
+ ethhdr = (struct ethhdr *) skb_mac_header(skb);
+
+ list_for_each_entry_rcu(node, node_db, mac_list) {
+ if (!compare_ether_addr(node->MacAddressA, ethhdr->h_source))
+ return node;
+ if (!compare_ether_addr(node->MacAddressB, ethhdr->h_source))
+ return node;
+ }
+
+ return NULL;
+}
+
+
+/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize
+ * frames from self that's been looped over the HSR ring.
+ */
+int hsr_create_self_node(struct list_head *self_node_db,
+ unsigned char addr_a[ETH_ALEN],
+ unsigned char addr_b[ETH_ALEN])
+{
+ struct node_entry *node, *oldnode;
+
+ node = kmalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return -ENOMEM;
+
+ memcpy(node->MacAddressA, addr_a, ETH_ALEN);
+ memcpy(node->MacAddressB, addr_b, ETH_ALEN);
+
+ rcu_read_lock();
+ oldnode = list_first_or_null_rcu(self_node_db,
+ struct node_entry, mac_list);
+ if (oldnode) {
+ list_replace_rcu(&oldnode->mac_list, &node->mac_list);
+ rcu_read_unlock();
+ synchronize_rcu();
+ kfree(oldnode);
+ } else {
+ rcu_read_unlock();
+ list_add_tail_rcu(&node->mac_list, self_node_db);
+ }
+
+ return 0;
+}
+
+static void node_entry_reclaim(struct rcu_head *rh)
+{
+ kfree(container_of(rh, struct node_entry, rcu_head));
+}
+
+
+/* Add/merge node to the database of nodes. 'skb' must contain an HSR
+ * supervision frame.
+ * - If the supervision header's MacAddressA field is not yet in the database,
+ * this frame is from an hitherto unknown node - add it to the database.
+ * - If the sender's MAC address is not the same as its MacAddressA address,
+ * the node is using PICS_SUBS (address substitution). Record the sender's
+ * address as the node's MacAddressB.
+ *
+ * This function needs to work even if the sender node has changed one of its
+ * slaves' MAC addresses. In this case, there are four different cases described
+ * by (Addr-changed, received-from) pairs as follows. Note that changing the
+ * SlaveA address is equal to changing the node's own address:
+ *
+ * - (AddrB, SlaveB): The new AddrB will be recorded by PICS_SUBS code since
+ * node == NULL.
+ * - (AddrB, SlaveA): Will work as usual (the AddrB change won't be detected
+ * from this frame).
+ *
+ * - (AddrA, SlaveB): The old node will be found. We need to detect this and
+ * remove the node.
+ * - (AddrA, SlaveA): A new node will be registered (non-PICS_SUBS at first).
+ * The old one will be pruned after HSR_NODE_FORGET_TIME.
+ *
+ * We also need to detect if the sender's SlaveA and SlaveB cables have been
+ * swapped.
+ */
+struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv,
+ struct node_entry *node,
+ struct sk_buff *skb,
+ enum hsr_dev_idx dev_idx)
+{
+ struct hsr_sup_payload *hsr_sp;
+ struct hsr_ethhdr_sp *hsr_ethsup;
+ int i;
+ unsigned long now;
+
+ hsr_ethsup = (struct hsr_ethhdr_sp *) skb_mac_header(skb);
+ hsr_sp = (struct hsr_sup_payload *) skb->data;
+
+ if (node && compare_ether_addr(node->MacAddressA, hsr_sp->MacAddressA)) {
+ /* Node has changed its AddrA, frame was received from SlaveB */
+ list_del_rcu(&node->mac_list);
+ call_rcu(&node->rcu_head, node_entry_reclaim);
+ node = NULL;
+ }
+
+ if (node && (dev_idx == node->AddrB_if) &&
+ compare_ether_addr(node->MacAddressB, hsr_ethsup->ethhdr.h_source)) {
+ /* Cables have been swapped */
+ list_del_rcu(&node->mac_list);
+ call_rcu(&node->rcu_head, node_entry_reclaim);
+ node = NULL;
+ }
+
+ if (node && (dev_idx != node->AddrB_if) &&
+ (node->AddrB_if != HSR_DEV_NONE) &&
+ compare_ether_addr(node->MacAddressA, hsr_ethsup->ethhdr.h_source)) {
+ /* Cables have been swapped */
+ list_del_rcu(&node->mac_list);
+ call_rcu(&node->rcu_head, node_entry_reclaim);
+ node = NULL;
+ }
+
+ if (node)
+ return node;
+
+ node = find_node_by_AddrA(&hsr_priv->node_db, hsr_sp->MacAddressA);
+ if (node) {
+ /* Node is known, but frame was received from an unknown
+ * address. Node is PICS_SUBS capable; merge its AddrB.
+ */
+ memcpy(node->MacAddressB, hsr_ethsup->ethhdr.h_source, ETH_ALEN);
+ node->AddrB_if = dev_idx;
+ return node;
+ }
+
+ node = kzalloc(sizeof(*node), GFP_ATOMIC);
+ if (!node)
+ return NULL;
+
+ memcpy(node->MacAddressA, hsr_sp->MacAddressA, ETH_ALEN);
+ memcpy(node->MacAddressB, hsr_ethsup->ethhdr.h_source, ETH_ALEN);
+ if (compare_ether_addr(hsr_sp->MacAddressA, hsr_ethsup->ethhdr.h_source))
+ node->AddrB_if = dev_idx;
+ else
+ node->AddrB_if = HSR_DEV_NONE;
+
+ /* We are only interested in time diffs here, so use current jiffies
+ * as initialization. (0 could trigger an spurious ring error warning).
+ */
+ now = jiffies;
+ for (i = 0; i < HSR_MAX_SLAVE; i++)
+ node->time_in[i] = now;
+ for (i = 0; i < HSR_MAX_DEV; i++)
+ node->seq_out[i] = ntohs(hsr_ethsup->hsr_sup.sequence_nr) - 1;
+
+ list_add_tail_rcu(&node->mac_list, &hsr_priv->node_db);
+
+ return node;
+}
+
+
+/* 'skb' is a frame meant for this host, that is to be passed to upper layers.
+ *
+ * If the frame was sent by a node's B interface, replace the sender
+ * address with that node's "official" address (MacAddressA) so that upper
+ * layers recognize where it came from.
+ */
+void hsr_addr_subst_source(struct hsr_priv *hsr_priv, struct sk_buff *skb)
+{
+ struct ethhdr *ethhdr;
+ struct node_entry *node;
+
+ if (!skb_mac_header_was_set(skb)) {
+ WARN_ONCE(1, "%s: Mac header not set\n", __func__);
+ return;
+ }
+ ethhdr = (struct ethhdr *) skb_mac_header(skb);
+
+ rcu_read_lock();
+ node = find_node_by_AddrB(&hsr_priv->node_db, ethhdr->h_source);
+ if (node)
+ memcpy(ethhdr->h_source, node->MacAddressA, ETH_ALEN);
+ rcu_read_unlock();
+}
+
+
+/* 'skb' is a frame meant for another host.
+ * 'hsr_dev_idx' is the HSR index of the outgoing device
+ *
+ * Substitute the target (dest) MAC address if necessary, so the it matches the
+ * recipient interface MAC address, regardless of whether that is the
+ * recipient's A or B interface.
+ * This is needed to keep the packets flowing through switches that learn on
+ * which "side" the different interfaces are.
+ */
+void hsr_addr_subst_dest(struct hsr_priv *hsr_priv, struct ethhdr *ethhdr,
+ enum hsr_dev_idx dev_idx)
+{
+ struct node_entry *node;
+
+ rcu_read_lock();
+ node = find_node_by_AddrA(&hsr_priv->node_db, ethhdr->h_dest);
+ if (node && (node->AddrB_if == dev_idx))
+ memcpy(ethhdr->h_dest, node->MacAddressB, ETH_ALEN);
+ rcu_read_unlock();
+}
+
+
+/* above(a, b) - return 1 if a > b, 0 otherwise.
+ */
+static bool above(u16 a, u16 b)
+{
+ /* Remove inconsistency where above(a, b) == below(a, b) */
+ if ((int) b - a == 32768)
+ return 0;
+
+ return (((s16) (b - a)) < 0);
+}
+#define below(a, b) above((b), (a))
+#define above_or_eq(a, b) (!below((a), (b)))
+#define below_or_eq(a, b) (!above((a), (b)))
+
+
+void hsr_register_frame_in(struct node_entry *node, enum hsr_dev_idx dev_idx)
+{
+ if ((dev_idx < 0) || (dev_idx >= HSR_MAX_DEV)) {
+ WARN_ONCE(1, "%s: Invalid dev_idx (%d)\n", __func__, dev_idx);
+ return;
+ }
+ node->time_in[dev_idx] = jiffies;
+ node->time_in_stale[dev_idx] = 0;
+}
+
+
+/* 'skb' is a HSR Ethernet frame (with a HSR tag inserted), with a valid
+ * ethhdr->h_source address and skb->mac_header set.
+ *
+ * Return:
+ * 1 if frame can be shown to have been sent recently on this interface,
+ * 0 otherwise, or
+ * negative error code on error
+ */
+int hsr_register_frame_out(struct node_entry *node, enum hsr_dev_idx dev_idx,
+ struct sk_buff *skb)
+{
+ struct hsr_ethhdr *hsr_ethhdr;
+ u16 sequence_nr;
+
+ if ((dev_idx < 0) || (dev_idx >= HSR_MAX_DEV)) {
+ WARN_ONCE(1, "%s: Invalid dev_idx (%d)\n", __func__, dev_idx);
+ return -EINVAL;
+ }
+ if (!skb_mac_header_was_set(skb)) {
+ WARN_ONCE(1, "%s: Mac header not set\n", __func__);
+ return -EINVAL;
+ }
+ hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb);
+
+ sequence_nr = ntohs(hsr_ethhdr->hsr_tag.sequence_nr);
+ if (below_or_eq(sequence_nr, node->seq_out[dev_idx]))
+ return 1;
+
+ node->seq_out[dev_idx] = sequence_nr;
+ return 0;
+}
+
+
+
+static bool is_late(struct node_entry *node, enum hsr_dev_idx dev_idx)
+{
+ enum hsr_dev_idx other;
+
+ if (node->time_in_stale[dev_idx])
+ return 1;
+
+ if (dev_idx == HSR_DEV_SLAVE_A)
+ other = HSR_DEV_SLAVE_B;
+ else
+ other = HSR_DEV_SLAVE_A;
+
+ if (node->time_in_stale[other])
+ return 0;
+
+ if (time_after(node->time_in[other], node->time_in[dev_idx] +
+ msecs_to_jiffies(MAX_SLAVE_DIFF)))
+ return 1;
+
+ return 0;
+}
+
+
+/* Remove stale sequence_nr records. Called by timer every
+ * HSR_LIFE_CHECK_INTERVAL (two seconds or so).
+ */
+void hsr_prune_nodes(struct hsr_priv *hsr_priv)
+{
+ struct node_entry *node;
+ unsigned long timestamp;
+ unsigned long time_a, time_b;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(node, &hsr_priv->node_db, mac_list) {
+ /* Shorthand */
+ time_a = node->time_in[HSR_DEV_SLAVE_A];
+ time_b = node->time_in[HSR_DEV_SLAVE_B];
+
+ /* Check for timestamps old enough to risk wrap-around */
+ if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET/2))
+ node->time_in_stale[HSR_DEV_SLAVE_A] = 1;
+ if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET/2))
+ node->time_in_stale[HSR_DEV_SLAVE_B] = 1;
+
+ /* Get age of newest frame from node.
+ * At least one time_in is OK here; nodes get pruned long
+ * before both time_ins can get stale
+ */
+ timestamp = time_a;
+ if (node->time_in_stale[HSR_DEV_SLAVE_A] ||
+ (!node->time_in_stale[HSR_DEV_SLAVE_B] &&
+ time_after(time_b, time_a)))
+ timestamp = time_b;
+
+ /* Warn of ring error only as long as we get frames at all */
+ if (time_is_after_jiffies(timestamp +
+ msecs_to_jiffies(1.5*MAX_SLAVE_DIFF))) {
+
+ if (is_late(node, HSR_DEV_SLAVE_A))
+ hsr_nl_ringerror(hsr_priv, node->MacAddressA,
+ HSR_DEV_SLAVE_A);
+ else if (is_late(node, HSR_DEV_SLAVE_B))
+ hsr_nl_ringerror(hsr_priv, node->MacAddressA,
+ HSR_DEV_SLAVE_B);
+ }
+
+ /* Prune old entries */
+ if (time_is_before_jiffies(timestamp +
+ msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
+ hsr_nl_nodedown(hsr_priv, node->MacAddressA);
+ list_del_rcu(&node->mac_list);
+ /* Note that we need to free this entry later: */
+ call_rcu(&node->rcu_head, node_entry_reclaim);
+ }
+ }
+ rcu_read_unlock();
+}
+
+
+void *hsr_get_next_node(struct hsr_priv *hsr_priv, void *_pos,
+ unsigned char addr[ETH_ALEN])
+{
+ struct node_entry *node;
+
+ if (!_pos) {
+ node = list_first_or_null_rcu(&hsr_priv->node_db,
+ struct node_entry, mac_list);
+ if (node)
+ memcpy(addr, node->MacAddressA, ETH_ALEN);
+ return node;
+ }
+
+ node = _pos;
+ list_for_each_entry_continue_rcu(node, &hsr_priv->node_db, mac_list) {
+ memcpy(addr, node->MacAddressA, ETH_ALEN);
+ return node;
+ }
+
+ return NULL;
+}
+
+
+int hsr_get_node_data(struct hsr_priv *hsr_priv,
+ const unsigned char *addr,
+ unsigned char addr_b[ETH_ALEN],
+ unsigned int *addr_b_ifindex,
+ int *if1_age,
+ u16 *if1_seq,
+ int *if2_age,
+ u16 *if2_seq)
+{
+ struct node_entry *node;
+ unsigned long tdiff;
+
+
+ rcu_read_lock();
+ node = find_node_by_AddrA(&hsr_priv->node_db, addr);
+ if (!node) {
+ rcu_read_unlock();
+ return -ENOENT; /* No such entry */
+ }
+
+ memcpy(addr_b, node->MacAddressB, ETH_ALEN);
+
+ tdiff = jiffies - node->time_in[HSR_DEV_SLAVE_A];
+ if (node->time_in_stale[HSR_DEV_SLAVE_A])
+ *if1_age = INT_MAX;
+#if HZ <= MSEC_PER_SEC
+ else if (tdiff > msecs_to_jiffies(INT_MAX))
+ *if1_age = INT_MAX;
+#endif
+ else
+ *if1_age = jiffies_to_msecs(tdiff);
+
+ tdiff = jiffies - node->time_in[HSR_DEV_SLAVE_B];
+ if (node->time_in_stale[HSR_DEV_SLAVE_B])
+ *if2_age = INT_MAX;
+#if HZ <= MSEC_PER_SEC
+ else if (tdiff > msecs_to_jiffies(INT_MAX))
+ *if2_age = INT_MAX;
+#endif
+ else
+ *if2_age = jiffies_to_msecs(tdiff);
+
+ /* Present sequence numbers as if they were incoming on interface */
+ *if1_seq = node->seq_out[HSR_DEV_SLAVE_B];
+ *if2_seq = node->seq_out[HSR_DEV_SLAVE_A];
+
+ if ((node->AddrB_if != HSR_DEV_NONE) && hsr_priv->slave[node->AddrB_if])
+ *addr_b_ifindex = hsr_priv->slave[node->AddrB_if]->ifindex;
+ else
+ *addr_b_ifindex = -1;
+
+ rcu_read_unlock();
+
+ return 0;
+}
diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h
new file mode 100644
index 0000000..e6c4022
--- /dev/null
+++ b/net/hsr/hsr_framereg.h
@@ -0,0 +1,53 @@
+/* Copyright 2011-2013 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-2013 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef _HSR_FRAMEREG_H
+#define _HSR_FRAMEREG_H
+
+#include "hsr_main.h"
+
+struct node_entry;
+
+struct node_entry *hsr_find_node(struct list_head *node_db, struct sk_buff *skb);
+
+struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv,
+ struct node_entry *node,
+ struct sk_buff *skb,
+ enum hsr_dev_idx dev_idx);
+
+void hsr_addr_subst_source(struct hsr_priv *hsr_priv, struct sk_buff *skb);
+void hsr_addr_subst_dest(struct hsr_priv *hsr_priv, struct ethhdr *ethhdr,
+ enum hsr_dev_idx dev_idx);
+
+void hsr_register_frame_in(struct node_entry *node, enum hsr_dev_idx dev_idx);
+
+int hsr_register_frame_out(struct node_entry *node, enum hsr_dev_idx dev_idx,
+ struct sk_buff *skb);
+
+void hsr_prune_nodes(struct hsr_priv *hsr_priv);
+
+int hsr_create_self_node(struct list_head *self_node_db,
+ unsigned char addr_a[ETH_ALEN],
+ unsigned char addr_b[ETH_ALEN]);
+
+void *hsr_get_next_node(struct hsr_priv *hsr_priv, void *_pos,
+ unsigned char addr[ETH_ALEN]);
+
+int hsr_get_node_data(struct hsr_priv *hsr_priv,
+ const unsigned char *addr,
+ unsigned char addr_b[ETH_ALEN],
+ unsigned int *addr_b_ifindex,
+ int *if1_age,
+ u16 *if1_seq,
+ int *if2_age,
+ u16 *if2_seq);
+
+#endif /* _HSR_FRAMEREG_H */
diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c
new file mode 100644
index 0000000..f95fcbb
--- /dev/null
+++ b/net/hsr/hsr_main.c
@@ -0,0 +1,458 @@
+/* Copyright 2011-2013 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-2013 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_PRP (HSRv0), and network device event handling.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/rculist.h>
+#include <linux/timer.h>
+#include <linux/etherdevice.h>
+#include "hsr_main.h"
+#include "hsr_device.h"
+#include "hsr_netlink.h"
+#include "hsr_framereg.h"
+
+
+/* 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;
+ }
+}
+
+bool is_hsr_slave(struct net_device *dev)
+{
+ struct hsr_priv *hsr_priv_it;
+
+ list_for_each_entry_rcu(hsr_priv_it, &hsr_list, hsr_list) {
+ if (dev == hsr_priv_it->slave[0])
+ return true;
+ if (dev == hsr_priv_it->slave[1])
+ return true;
+ }
+
+ return false;
+}
+
+
+/* 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[0]) ||
+ (dev == hsr_priv->slave[1])) {
+ 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 net_device *get_other_slave(struct hsr_priv *hsr_priv,
+ struct net_device *dev)
+{
+ if (dev == hsr_priv->slave[0])
+ return hsr_priv->slave[1];
+ if (dev == hsr_priv->slave[1])
+ return hsr_priv->slave[0];
+
+ return NULL;
+}
+
+
+static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct net_device *slave, *other_slave;
+ struct hsr_priv *hsr_priv;
+ int old_operstate;
+ int mtu_max;
+ int res;
+ struct net_device *dev;
+
+ dev = netdev_notifier_info_to_dev(ptr);
+
+ hsr_priv = get_hsr_master(dev);
+ if (hsr_priv) {
+ /* dev is a slave device */
+ slave = dev;
+ other_slave = get_other_slave(hsr_priv, slave);
+ } else {
+ if (!is_hsr_master(dev))
+ return NOTIFY_DONE;
+ hsr_priv = netdev_priv(dev);
+ slave = hsr_priv->slave[0];
+ other_slave = hsr_priv->slave[1];
+ }
+
+ switch (event) {
+ case NETDEV_UP: /* Administrative state DOWN */
+ case NETDEV_DOWN: /* Administrative state UP */
+ case NETDEV_CHANGE: /* Link (carrier) state changes */
+ old_operstate = hsr_priv->dev->operstate;
+ hsr_set_carrier(hsr_priv->dev, slave, other_slave);
+ /* netif_stacked_transfer_operstate() cannot be used here since
+ * it doesn't set IF_OPER_LOWERLAYERDOWN (?)
+ */
+ hsr_set_operstate(hsr_priv->dev, slave, other_slave);
+ hsr_check_announce(hsr_priv->dev, old_operstate);
+ break;
+ case NETDEV_CHANGEADDR:
+
+ /* This should not happen since there's no ndo_set_mac_address()
+ * for HSR devices - i.e. not supported.
+ */
+ if (dev == hsr_priv->dev)
+ break;
+
+ if (dev == hsr_priv->slave[0])
+ memcpy(hsr_priv->dev->dev_addr,
+ hsr_priv->slave[0]->dev_addr, ETH_ALEN);
+
+ /* Make sure we recognize frames from ourselves in hsr_rcv() */
+ res = hsr_create_self_node(&hsr_priv->self_node_db,
+ hsr_priv->dev->dev_addr,
+ hsr_priv->slave[1] ?
+ hsr_priv->slave[1]->dev_addr :
+ hsr_priv->dev->dev_addr);
+ if (res)
+ netdev_warn(hsr_priv->dev,
+ "Could not update HSR node address.\n");
+
+ if (dev == hsr_priv->slave[0])
+ call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr_priv->dev);
+ break;
+ case NETDEV_CHANGEMTU:
+ if (dev == hsr_priv->dev)
+ break; /* Handled in ndo_change_mtu() */
+ mtu_max = hsr_get_max_mtu(hsr_priv);
+ if (hsr_priv->dev->mtu > mtu_max)
+ dev_set_mtu(hsr_priv->dev, mtu_max);
+ break;
+ case NETDEV_UNREGISTER:
+ if (dev == hsr_priv->slave[0])
+ hsr_priv->slave[0] = NULL;
+ if (dev == hsr_priv->slave[1])
+ hsr_priv->slave[1] = NULL;
+
+ /* There should really be a way to set a new slave device... */
+
+ break;
+ case NETDEV_PRE_TYPE_CHANGE:
+ /* HSR works only on Ethernet devices. Refuse slave to change
+ * its type.
+ */
+ return NOTIFY_BAD;
+ }
+
+ return NOTIFY_DONE;
+}
+
+
+static struct timer_list prune_timer;
+
+static void prune_nodes_all(unsigned long data)
+{
+ struct hsr_priv *hsr_priv;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list)
+ hsr_prune_nodes(hsr_priv);
+ 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_pull(skb, HSR_TAGLEN);
+
+ return skb;
+
+err_free:
+ kfree_skb(skb);
+ return NULL;
+}
+
+
+/* The uses I can see for these HSR supervision frames are:
+ * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type =
+ * 22") to reset any sequence_nr counters belonging to that node. Useful if
+ * the other node's counter has been reset for some reason.
+ * --
+ * Or not - resetting the counter and bridging the frame would create a
+ * loop, unfortunately.
+ *
+ * 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck
+ * frame is received from a particular node, we know something is wrong.
+ * We just register these (as with normal frames) and throw them away.
+ *
+ * 3) Allow different MAC addresses for the two slave interfaces, using the
+ * MacAddressA field.
+ */
+static bool is_supervision_frame(struct hsr_priv *hsr_priv, struct sk_buff *skb)
+{
+ struct hsr_sup_tag *hsr_stag;
+
+ if (compare_ether_addr(eth_hdr(skb)->h_dest,
+ hsr_priv->sup_multicast_addr))
+ return 0;
+
+ hsr_stag = (struct hsr_sup_tag *) skb->data;
+ if (get_hsr_stag_path(hsr_stag) != 0x0f)
+ return 0;
+ if ((hsr_stag->HSR_TLV_Type != HSR_TLV_ANNOUNCE) &&
+ (hsr_stag->HSR_TLV_Type != HSR_TLV_LIFE_CHECK))
+ return 0;
+ if (hsr_stag->HSR_TLV_Length != 12)
+ return 0;
+
+ 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 net_device *other_slave;
+ struct node_entry *node;
+ int deliver_to_self;
+ struct sk_buff *skb_deliver;
+ enum hsr_dev_idx dev_in_idx, dev_other_idx;
+ bool dup_out;
+ int ret;
+
+ hsr_priv = get_hsr_master(dev);
+
+ if (!hsr_priv) {
+ /* Non-HSR-slave device 'dev' is connected to a HSR network */
+ kfree_skb(skb);
+ dev->stats.rx_errors++;
+ return NET_RX_SUCCESS;
+ }
+
+ if (dev == hsr_priv->slave[0]) {
+ dev_in_idx = HSR_DEV_SLAVE_A;
+ dev_other_idx = HSR_DEV_SLAVE_B;
+ } else {
+ dev_in_idx = HSR_DEV_SLAVE_B;
+ dev_other_idx = HSR_DEV_SLAVE_A;
+ }
+
+ node = hsr_find_node(&hsr_priv->self_node_db, skb);
+ if (node) {
+ /* Always kill frames sent by ourselves */
+ kfree_skb(skb);
+ return NET_RX_SUCCESS;
+ }
+
+ /* Is this frame a candidate for local reception? */
+ 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;
+ }
+
+
+ rcu_read_lock(); /* node_db */
+ node = hsr_find_node(&hsr_priv->node_db, skb);
+
+ if (is_supervision_frame(hsr_priv, skb)) {
+ skb_pull(skb, sizeof(struct hsr_sup_tag));
+ node = hsr_merge_node(hsr_priv, node, skb, dev_in_idx);
+ if (!node) {
+ rcu_read_unlock(); /* node_db */
+ kfree_skb(skb);
+ hsr_priv->dev->stats.rx_dropped++;
+ return NET_RX_DROP;
+ }
+ skb_push(skb, sizeof(struct hsr_sup_tag));
+ deliver_to_self = 0;
+ }
+
+ if (!node) {
+ /* Source node unknown; this might be a HSR frame from
+ * another net (different multicast address). Ignore it.
+ */
+ rcu_read_unlock(); /* node_db */
+ kfree_skb(skb);
+ return NET_RX_SUCCESS;
+ }
+
+ /* Register ALL incoming frames as outgoing through the other interface.
+ * This allows us to register frames as incoming only if they are valid
+ * for the receiving interface, without using a specific counter for
+ * incoming frames.
+ */
+ dup_out = hsr_register_frame_out(node, dev_other_idx, skb);
+ if (!dup_out)
+ hsr_register_frame_in(node, dev_in_idx);
+
+ /* Forward this frame? */
+ if (!dup_out && (skb->pkt_type != PACKET_HOST))
+ other_slave = get_other_slave(hsr_priv, dev);
+ else
+ other_slave = NULL;
+
+ if (hsr_register_frame_out(node, HSR_DEV_MASTER, skb))
+ deliver_to_self = 0;
+
+ rcu_read_unlock(); /* node_db */
+
+ if (!deliver_to_self && !other_slave) {
+ kfree_skb(skb);
+ /* Circulated frame; silently remove it. */
+ return NET_RX_SUCCESS;
+ }
+
+ skb_deliver = skb;
+ if (deliver_to_self && other_slave) {
+ /* skb_clone() is not enough since we will strip the hsr tag
+ * and do address substitution below
+ */
+ skb_deliver = pskb_copy(skb, GFP_ATOMIC);
+ if (!skb_deliver) {
+ deliver_to_self = 0;
+ hsr_priv->dev->stats.rx_dropped++;
+ }
+ }
+
+ if (deliver_to_self) {
+ bool multicast_frame;
+
+ 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)
+ /* We need to memmove the whole header to work around
+ * alignment problems caused by the 6-byte HSR tag.
+ */
+ memmove(skb_deliver->data - HSR_TAGLEN, skb_deliver->data,
+ skb_headlen(skb_deliver));
+ skb_deliver->data -= HSR_TAGLEN;
+ skb_deliver->tail -= HSR_TAGLEN;
+#endif
+ skb_deliver->dev = hsr_priv->dev;
+ hsr_addr_subst_source(hsr_priv, skb_deliver);
+ multicast_frame = (skb_deliver->pkt_type == PACKET_MULTICAST);
+ 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;
+ if (multicast_frame)
+ hsr_priv->dev->stats.multicast++;
+ }
+ }
+
+forward:
+ if (other_slave) {
+ skb_push(skb, ETH_HLEN);
+ skb->dev = other_slave;
+ dev_queue_xmit(skb);
+ }
+
+ return NET_RX_SUCCESS;
+}
+
+
+static struct packet_type hsr_pt __read_mostly = {
+ .type = htons(ETH_P_PRP),
+ .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;
+
+ BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_TAGLEN);
+
+ dev_add_pack(&hsr_pt);
+
+ init_timer(&prune_timer);
+ prune_timer.function = prune_nodes_all;
+ prune_timer.data = 0;
+ prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD);
+ add_timer(&prune_timer);
+
+ register_netdevice_notifier(&hsr_nb);
+
+ res = hsr_netlink_init();
+
+ return res;
+}
+
+static void __exit hsr_exit(void)
+{
+ unregister_netdevice_notifier(&hsr_nb);
+ del_timer(&prune_timer);
+ hsr_netlink_exit();
+ dev_remove_pack(&hsr_pt);
+}
+
+module_init(hsr_init);
+module_exit(hsr_exit);
+MODULE_LICENSE("GPL");
diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h
new file mode 100644
index 0000000..56fe060
--- /dev/null
+++ b/net/hsr/hsr_main.h
@@ -0,0 +1,166 @@
+/* Copyright 2011-2013 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-2013 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef _HSR_PRIVATE_H
+#define _HSR_PRIVATE_H
+
+#include <linux/netdevice.h>
+#include <linux/list.h>
+
+
+/* Time constants as specified in the HSR specification (IEC-62439-3 2010)
+ * Table 8.
+ * All values in milliseconds.
+ */
+#define HSR_LIFE_CHECK_INTERVAL 2000 /* ms */
+#define HSR_NODE_FORGET_TIME 60000 /* ms */
+#define HSR_ANNOUNCE_INTERVAL 100 /* ms */
+
+
+/* By how much may slave1 and slave2 timestamps of latest received frame from
+ * each node differ before we notify of communication problem?
+ */
+#define MAX_SLAVE_DIFF 3000 /* ms */
+
+
+/* How often shall we check for broken ring and remove node entries older than
+ * HSR_NODE_FORGET_TIME?
+ */
+#define PRUNE_PERIOD 3000 /* ms */
+
+
+#define HSR_TLV_ANNOUNCE 22
+#define HSR_TLV_LIFE_CHECK 23
+
+
+/* HSR Tag.
+ * As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB,
+ * path, LSDU_size, sequence Nr }. But we let eth_header() create { h_dest,
+ * h_source, h_proto = 0x88FB }, and add { path, LSDU_size, sequence Nr,
+ * encapsulated protocol } instead.
+ */
+#define HSR_TAGLEN 6
+
+/* Field names below as defined in the IEC:2010 standard for HSR. */
+struct hsr_tag {
+ __be16 path_and_LSDU_size;
+ __be16 sequence_nr;
+ __be16 encap_proto;
+} __packed;
+
+
+/* The helper functions below assumes that 'path' occupies the 4 most
+ * significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or
+ * equivalently, the 4 most significant bits of HSR tag byte 14).
+ *
+ * This is unclear in the IEC specification; its definition of MAC addresses
+ * indicates the spec is written with the least significant bit first (to the
+ * left). This, however, would mean that the LSDU field would be split in two
+ * with the path field in-between, which seems strange. I'm guessing the MAC
+ * address definition is in error.
+ */
+static inline u16 get_hsr_tag_path(struct hsr_tag *ht)
+{
+ return ntohs(ht->path_and_LSDU_size) >> 12;
+}
+
+static inline u16 get_hsr_tag_LSDU_size(struct hsr_tag *ht)
+{
+ return ntohs(ht->path_and_LSDU_size) & 0x0FFF;
+}
+
+static inline void set_hsr_tag_path(struct hsr_tag *ht, u16 path)
+{
+ ht->path_and_LSDU_size = htons(
+ (ntohs(ht->path_and_LSDU_size) & 0x0FFF) | (path << 12));
+}
+
+static inline void set_hsr_tag_LSDU_size(struct hsr_tag *ht, u16 LSDU_size)
+{
+ ht->path_and_LSDU_size = htons(
+ (ntohs(ht->path_and_LSDU_size) & 0xF000) |
+ (LSDU_size & 0x0FFF));
+}
+
+struct hsr_ethhdr {
+ struct ethhdr ethhdr;
+ struct hsr_tag hsr_tag;
+} __packed;
+
+
+/* HSR Supervision Frame data types.
+ * Field names as defined in the IEC:2010 standard for HSR.
+ */
+struct hsr_sup_tag {
+ __be16 path_and_HSR_Ver;
+ __be16 sequence_nr;
+ __u8 HSR_TLV_Type;
+ __u8 HSR_TLV_Length;
+} __packed;
+
+struct hsr_sup_payload {
+ unsigned char MacAddressA[ETH_ALEN];
+} __packed;
+
+static inline u16 get_hsr_stag_path(struct hsr_sup_tag *hst)
+{
+ return get_hsr_tag_path((struct hsr_tag *) hst);
+}
+
+static inline u16 get_hsr_stag_HSR_ver(struct hsr_sup_tag *hst)
+{
+ return get_hsr_tag_LSDU_size((struct hsr_tag *) hst);
+}
+
+static inline void set_hsr_stag_path(struct hsr_sup_tag *hst, u16 path)
+{
+ set_hsr_tag_path((struct hsr_tag *) hst, path);
+}
+
+static inline void set_hsr_stag_HSR_Ver(struct hsr_sup_tag *hst, u16 HSR_Ver)
+{
+ set_hsr_tag_LSDU_size((struct hsr_tag *) hst, HSR_Ver);
+}
+
+struct hsr_ethhdr_sp {
+ struct ethhdr ethhdr;
+ struct hsr_sup_tag hsr_sup;
+} __packed;
+
+
+enum hsr_dev_idx {
+ HSR_DEV_NONE = -1,
+ HSR_DEV_SLAVE_A = 0,
+ HSR_DEV_SLAVE_B,
+ HSR_DEV_MASTER,
+};
+#define HSR_MAX_SLAVE (HSR_DEV_SLAVE_B + 1)
+#define HSR_MAX_DEV (HSR_DEV_MASTER + 1)
+
+struct hsr_priv {
+ struct list_head hsr_list; /* List of hsr devices */
+ struct rcu_head rcu_head;
+ struct net_device *dev;
+ struct net_device *slave[HSR_MAX_SLAVE];
+ struct list_head node_db; /* Other HSR nodes */
+ struct list_head self_node_db; /* MACs of slaves */
+ struct timer_list announce_timer; /* Supervision frame dispatch */
+ int announce_count;
+ u16 sequence_nr;
+ spinlock_t seqnr_lock; /* locking for sequence_nr */
+ unsigned char sup_multicast_addr[ETH_ALEN];
+};
+
+void register_hsr_master(struct hsr_priv *hsr_priv);
+void unregister_hsr_master(struct hsr_priv *hsr_priv);
+bool is_hsr_slave(struct net_device *dev);
+
+#endif /* _HSR_PRIVATE_H */
diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c
new file mode 100644
index 0000000..4e66bf6
--- /dev/null
+++ b/net/hsr/hsr_netlink.c
@@ -0,0 +1,457 @@
+/* Copyright 2011-2013 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-2013 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_main.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 },
+ [IFLA_HSR_MULTICAST_SPEC] = { .type = NLA_U8 },
+};
+
+
+/* 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];
+ unsigned char multicast_spec;
+
+ if (!data[IFLA_HSR_SLAVE1]) {
+ netdev_info(dev, "IFLA_HSR_SLAVE1 missing!\n");
+ return -EINVAL;
+ }
+ link[0] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE1]));
+ if (!data[IFLA_HSR_SLAVE2]) {
+ netdev_info(dev, "IFLA_HSR_SLAVE2 missing!\n");
+ return -EINVAL;
+ }
+ link[1] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE2]));
+
+ if (!link[0] || !link[1])
+ return -ENODEV;
+ if (link[0] == link[1])
+ return -EINVAL;
+
+ if (!data[IFLA_HSR_MULTICAST_SPEC])
+ multicast_spec = 0;
+ else
+ multicast_spec = nla_get_u8(data[IFLA_HSR_MULTICAST_SPEC]);
+
+ return hsr_dev_finalize(dev, link, multicast_spec);
+}
+
+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,
+ .newlink = hsr_newlink,
+};
+
+
+
+/* attribute policy */
+/* NLA_BINARY missing in libnl; use NLA_UNSPEC in userspace instead. */
+static const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = {
+ [HSR_A_NODE_ADDR] = { .type = NLA_BINARY, .len = ETH_ALEN },
+ [HSR_A_NODE_ADDR_B] = { .type = NLA_BINARY, .len = ETH_ALEN },
+ [HSR_A_IFINDEX] = { .type = NLA_U32 },
+ [HSR_A_IF1_AGE] = { .type = NLA_U32 },
+ [HSR_A_IF2_AGE] = { .type = NLA_U32 },
+ [HSR_A_IF1_SEQ] = { .type = NLA_U16 },
+ [HSR_A_IF2_SEQ] = { .type = NLA_U16 },
+};
+
+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",
+};
+
+
+
+/* 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(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
+ enum hsr_dev_idx dev_idx)
+{
+ struct sk_buff *skb;
+ void *msg_head;
+ int res;
+ int ifindex;
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!skb)
+ goto fail;
+
+ msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_RING_ERROR);
+ if (!msg_head)
+ goto nla_put_failure;
+
+ res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
+ if (res < 0)
+ goto nla_put_failure;
+
+ if (hsr_priv->slave[dev_idx])
+ ifindex = hsr_priv->slave[dev_idx]->ifindex;
+ else
+ ifindex = -1;
+ res = nla_put_u32(skb, HSR_A_IFINDEX, ifindex);
+ if (res < 0)
+ goto nla_put_failure;
+
+ genlmsg_end(skb, msg_head);
+ genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC);
+
+ return;
+
+nla_put_failure:
+ kfree_skb(skb);
+
+fail:
+ netdev_warn(hsr_priv->dev, "Could not send HSR ring error message\n");
+}
+
+/* This is called when we haven't heard from the node with MAC address addr for
+ * some time (just before the node is removed from the node table/list).
+ */
+void hsr_nl_nodedown(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN])
+{
+ struct sk_buff *skb;
+ void *msg_head;
+ int res;
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!skb)
+ goto fail;
+
+ msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_NODE_DOWN);
+ if (!msg_head)
+ goto nla_put_failure;
+
+
+ res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
+ if (res < 0)
+ goto nla_put_failure;
+
+ genlmsg_end(skb, msg_head);
+ genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC);
+
+ return;
+
+nla_put_failure:
+ kfree_skb(skb);
+
+fail:
+ netdev_warn(hsr_priv->dev, "Could not send HSR node down\n");
+}
+
+
+/* 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;
+ struct net_device *hsr_dev;
+
+ /* For sending */
+ struct sk_buff *skb_out;
+ void *msg_head;
+ struct hsr_priv *hsr_priv;
+ unsigned char hsr_node_addr_b[ETH_ALEN];
+ int hsr_node_if1_age;
+ u16 hsr_node_if1_seq;
+ int hsr_node_if2_age;
+ u16 hsr_node_if2_seq;
+ int addr_b_ifindex;
+ int res;
+
+ 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 = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!skb_out) {
+ res = -ENOMEM;
+ goto fail;
+ }
+
+ msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
+ info->snd_seq, &hsr_genl_family, 0,
+ HSR_C_SET_NODE_STATUS);
+ if (!msg_head) {
+ res = -ENOMEM;
+ goto nla_put_failure;
+ }
+
+ res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex);
+ if (res < 0)
+ goto nla_put_failure;
+
+ hsr_priv = netdev_priv(hsr_dev);
+ res = hsr_get_node_data(hsr_priv,
+ (unsigned char *) nla_data(info->attrs[HSR_A_NODE_ADDR]),
+ hsr_node_addr_b,
+ &addr_b_ifindex,
+ &hsr_node_if1_age,
+ &hsr_node_if1_seq,
+ &hsr_node_if2_age,
+ &hsr_node_if2_seq);
+ if (res < 0)
+ goto fail;
+
+ res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN,
+ nla_data(info->attrs[HSR_A_NODE_ADDR]));
+ if (res < 0)
+ goto nla_put_failure;
+
+ if (addr_b_ifindex > -1) {
+ res = nla_put(skb_out, HSR_A_NODE_ADDR_B, ETH_ALEN,
+ hsr_node_addr_b);
+ if (res < 0)
+ goto nla_put_failure;
+
+ res = nla_put_u32(skb_out, HSR_A_ADDR_B_IFINDEX, addr_b_ifindex);
+ if (res < 0)
+ goto nla_put_failure;
+ }
+
+ res = nla_put_u32(skb_out, HSR_A_IF1_AGE, hsr_node_if1_age);
+ if (res < 0)
+ goto nla_put_failure;
+ res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq);
+ if (res < 0)
+ goto nla_put_failure;
+ if (hsr_priv->slave[0])
+ res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX,
+ hsr_priv->slave[0]->ifindex);
+ if (res < 0)
+ goto nla_put_failure;
+
+ res = nla_put_u32(skb_out, HSR_A_IF2_AGE, hsr_node_if2_age);
+ if (res < 0)
+ goto nla_put_failure;
+ res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq);
+ if (res < 0)
+ goto nla_put_failure;
+ if (hsr_priv->slave[1])
+ res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX,
+ hsr_priv->slave[1]->ifindex);
+
+ genlmsg_end(skb_out, msg_head);
+ genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
+
+ return 0;
+
+invalid:
+ netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL);
+ return 0;
+
+nla_put_failure:
+ kfree_skb(skb_out);
+ /* Fall through */
+
+fail:
+ return res;
+}
+
+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,
+};
+
+
+/* Get a list of MacAddressA of all nodes known to this node (other than self).
+ */
+static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
+{
+ /* For receiving */
+ struct nlattr *na;
+ struct net_device *hsr_dev;
+
+ /* For sending */
+ struct sk_buff *skb_out;
+ void *msg_head;
+ struct hsr_priv *hsr_priv;
+ void *pos;
+ unsigned char addr[ETH_ALEN];
+ int res;
+
+ if (!info)
+ goto invalid;
+
+ na = info->attrs[HSR_A_IFINDEX];
+ 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 = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!skb_out) {
+ res = -ENOMEM;
+ goto fail;
+ }
+
+ msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
+ info->snd_seq, &hsr_genl_family, 0,
+ HSR_C_SET_NODE_LIST);
+ if (!msg_head) {
+ res = -ENOMEM;
+ goto nla_put_failure;
+ }
+
+ res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex);
+ if (res < 0)
+ goto nla_put_failure;
+
+ hsr_priv = netdev_priv(hsr_dev);
+
+ rcu_read_lock();
+ pos = hsr_get_next_node(hsr_priv, NULL, addr);
+ while (pos) {
+ res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, addr);
+ if (res < 0) {
+ rcu_read_unlock();
+ goto nla_put_failure;
+ }
+ pos = hsr_get_next_node(hsr_priv, pos, addr);
+ }
+ rcu_read_unlock();
+
+ genlmsg_end(skb_out, msg_head);
+ genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
+
+ return 0;
+
+invalid:
+ netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL);
+ return 0;
+
+nla_put_failure:
+ kfree_skb(skb_out);
+ /* Fall through */
+
+fail:
+ return res;
+}
+
+
+static struct genl_ops hsr_ops_get_node_list = {
+ .cmd = HSR_C_GET_NODE_LIST,
+ .flags = 0,
+ .policy = hsr_genl_policy,
+ .doit = hsr_get_node_list,
+ .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_ops(&hsr_genl_family, &hsr_ops_get_node_list);
+ if (rc)
+ goto fail_genl_register_ops_node_list;
+
+ 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_list);
+fail_genl_register_ops_node_list:
+ 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..d4579dc
--- /dev/null
+++ b/net/hsr/hsr_netlink.h
@@ -0,0 +1,30 @@
+/* Copyright 2011-2013 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-2013 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef __HSR_NETLINK_H
+#define __HSR_NETLINK_H
+
+#include <linux/if_ether.h>
+#include <linux/module.h>
+#include <uapi/linux/hsr_netlink.h>
+
+struct hsr_priv;
+
+int __init hsr_netlink_init(void);
+void __exit hsr_netlink_exit(void);
+
+void hsr_nl_ringerror(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
+ int dev_idx);
+void hsr_nl_nodedown(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN]);
+void hsr_nl_framedrop(int dropcount, int dev_idx);
+void hsr_nl_linkdown(int dev_idx);
+
+#endif /* __HSR_NETLINK_H */
--
1.8.1.5
--
Arvid Brodin | Consultant (Linux)
XDIN AB | Knarrarnäsgatan 7 | SE-164 40 Kista | Sweden | xdin.com
^ permalink raw reply related
* Re: ADSL/ATM linklayer tc shaping regression fix commits for stable
From: David Miller @ 2013-08-21 18:20 UTC (permalink / raw)
To: brouer-H+wXaHxf7aLQT0dZR+AlfA
Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
stable-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
bloat-JXvr2/1DY2fm6VMwtOF2vx4hnT+Y9+D1
In-Reply-To: <20130821140350.2b953007-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
From: Jesper Dangaard Brouer <brouer-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Date: Wed, 21 Aug 2013 14:03:50 +0200
>
> On Tue, 20 Aug 2013 08:16:50 -0700
> Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org> wrote:
>> On Tue, Aug 20, 2013 at 01:26:29PM +0200, Jesper Dangaard Brouer wrote:
> [...]
>
>> If there are issues with 3.10, that's a different story.
>>
>> > Refactor improvements (v3.11-rc1):
>> > commit 130d3d68b52 (net_sched: psched_ratecfg_precompute() improvements)
>
> Needed because it fixes the accuracy of the rate calc.
>
>> > The linklayer ATM/ADSL fix, reached 3.11-rc6:
>> > commit 8a8e3d84b17 (net_sched: restore "linklayer atm" handling)
>
> Needed because we broke userspace interface.
>
>> David sends me the networking patches for the stable tree, and if he
>> thinks these are applicable, then I'll take them.
>
> DaveM, please? What do you want me to do, submit this req/patches somehow?
>
> (Can see that is not currently on your stable queue via:
> http://patchwork.ozlabs.org/bundle/davem/stable/?state=*)
I'll put it there, you just have to formally ask me to do it that's
all.
Asking Greg to do it directly is a complete waste of time. :-)
^ permalink raw reply
* Re: [PATCH 4/4] qlcnic: use standard NAPI weights
From: Sergei Shtylyov @ 2013-08-21 18:19 UTC (permalink / raw)
To: Michal Schmidt
Cc: David Miller, netdev, Eric Dumazet, Himanshu Madhani,
Rajesh Borundia, Shahed Shaikh, Jitendra Kalsaria, Sony Chacko,
Sucheta Chakraborty, linux-driver
In-Reply-To: <1377103771-1590-5-git-send-email-mschmidt@redhat.com>
On 08/21/2013 08:49 PM, Michal Schmidt wrote:
> Since commit 82dc3c63 netif_napi_add() produces an error message if
Likewise, summary line is needed.
> a NAPI poll weight greater than 64 is requested.
> qlcnic requests the weight as large as 256 for some of its rings, and
> smaller values for other rings. For instance in qlcnic_82xx_napi_add()
> I think the intention was to give the tx+rx ring a bigger weight than
> to rx-only rings, but it's actually doing the opposite. So I'm assuming
> the weights do not really matter much.
> Just use the standard NAPI weights for all rings.
> Signed-off-by: Michal Schmidt <mschmidt@redhat.com>
> CC: Himanshu Madhani <himanshu.madhani@qlogic.com>
> CC: Rajesh Borundia <rajesh.borundia@qlogic.com>
> CC: Shahed Shaikh <shahed.shaikh@qlogic.com>
> CC: Jitendra Kalsaria <jitendra.kalsaria@qlogic.com>
> CC: Sony Chacko <sony.chacko@qlogic.com>
> CC: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
> CC: linux-driver@qlogic.com
WBR, Sergei
^ permalink raw reply
* Re: [PATCH 3/4] ps3_gelic: lower NAPI weight
From: Sergei Shtylyov @ 2013-08-21 18:17 UTC (permalink / raw)
To: Michal Schmidt; +Cc: David Miller, netdev, Eric Dumazet, Geoff Levand
In-Reply-To: <1377103771-1590-4-git-send-email-mschmidt@redhat.com>
On 08/21/2013 08:49 PM, Michal Schmidt wrote:
> Since commit 82dc3c63 netif_napi_add() produces an error message if
Likewise, need a summary line.
> a NAPI poll weight greater than 64 is requested.
> GELIC_NET_NAPI_WEIGHT is defined to GELIC_NET_RX_DESCRIPTORS,
> which is 128.
> Use the standard NAPI weight.
> Signed-off-by: Michal Schmidt <mschmidt@redhat.com>
> CC: Geoff Levand <geoff@infradead.org>
WBR, Sergei
^ permalink raw reply
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