* Re: [PATCH 0/2] Get rid of ndo_xmit_flush
From: Jesper Dangaard Brouer @ 2014-08-27 7:48 UTC (permalink / raw)
To: Alexander Duyck
Cc: David Miller, netdev, therbert, jhs, hannes, edumazet,
jeffrey.t.kirsher, rusty, dborkman, brouer, Jeremy Eder
In-Reply-To: <53FCB944.9060904@intel.com>
On Tue, 26 Aug 2014 09:43:48 -0700
Alexander Duyck <alexander.h.duyck@intel.com> wrote:
> On 08/26/2014 05:52 AM, Jesper Dangaard Brouer wrote:
> >
[...]
> >
> > Setting: (inc TX ring size)
> > sudo ethtool -G eth5 tx 1024
> > sudo ethtool -C eth5 rx-usecs 1 #(default setting)
> > Result pktgen:
> > * instant rx:1 tx:5745632 pps n:118 average: rx:1 tx:5748818 pps
> > (instant variation TX -0.096 ns (min:-0.293 max:0.897) RX 0.000 ns)
> >
[...]
> > Setting:
> > sudo ethtool -G eth5 tx 512
> > sudo ethtool -C eth5 rx-usecs 30
> > Result pktgen:
> > * instant rx:1 tx:5920856 pps n:114 average: rx:1 tx:5918350 pps
> > (instant variation TX 0.071 ns (min:-0.177 max:0.135) RX 0.000 ns)
> >
>
> My advice would be to disable all C states and P states (including
> turbo) if possible, and try using idle=poll. Any processor frequency
> and/or C state transitions will totally wreak havoc with trying to get
> reliable results out of any performance test.
Thanks for the advice.
Reading Jeremy Eder's blog post:
http://www.breakage.org/2012/11/14/processor-max_cstate-intel_idle-max_cstate-and-devcpu_dma_latency/
It seems the best method for disabling these C and P states, and
keeping all CPUs in C0/C1 state is doing:
# tuned-adm profile latency-performance
Accuracy of: "ethtool -C eth5 rx-usecs 30" got slightly little better, (see
below) latency variations below 1ns min:-0.209 ns and max:0.114 ns. Which is
very good, and should be good enough for us, to measure the upcoming
code changes.
Just increasing TX ring queue still have variations.
I'm also going to disable Hyper-threading in BIOS, even-though I'm only
using one CPU in these tests (I worry that some process running on a
sibling could disturb the accuracy).
Setting:
* sudo ethtool -C eth5 rx-usecs 30
Result:
* instant rx:1 tx:5603644 pps n:120 average: rx:0 tx:5603317 pps
(instant variation TX 0.010 ns (min:-0.132 max:0.114) RX 0.000 ns)
* instant rx:1 tx:5599820 pps n:300 average: rx:1 tx:5602382 pps
(instant variation TX -0.082 ns (min:-0.209 max:0.114) RX 0.000 ns)
Setting:
* sudo ethtool -G eth5 tx 1024
Result:
* instant rx:1 tx:5398208 pps n:300 average: rx:1 tx:5404893 pps
(instant variation TX -0.229 ns (min:-0.257 max:1.666) RX 0.000 ns)
--
Best regards,
Jesper Dangaard Brouer
MSc.CS, Sr. Network Kernel Developer at Red Hat
Author of http://www.iptv-analyzer.org
LinkedIn: http://www.linkedin.com/in/brouer
^ permalink raw reply
* Re: [RFC] net: ipv4: drop unicast encapsulated in L2 multicast
From: Hannes Frederic Sowa @ 2014-08-27 7:38 UTC (permalink / raw)
To: Johannes Berg
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1408642331.4388.2.camel-8Nb76shvtaUJvtFkdXX2HixXY32XiHfO@public.gmane.org>
Hi Johannes,
On Do, 2014-08-21 at 19:32 +0200, Johannes Berg wrote:
> On Thu, 2014-08-21 at 19:22 +0200, Johannes Berg wrote:
> > From: Johannes Berg <johannes.berg-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> >
> > RFC 1122 says that unicast packets encapsulated in broadcast
> > link-layer packets should be dropped. Implement that, but also
> > extend it to link-layer multicast packets.
>
> I cannot find anything equivalent in the IPv6 RFCs - anyone more
> familiar with that who could help check for that?
>
> And if it's *not* in the IPv6 RFCs, how should we implement this?
I haven't found anything, too. Should I bring this up with IETF?
Bye,
Hannes
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH net-next v3 2/3] ethtool: Add generic options for tunables
From: Ben Hutchings @ 2014-08-27 6:59 UTC (permalink / raw)
To: Govindarajulu Varadarajan; +Cc: davem, netdev, stephen, ssujith, benve
In-Reply-To: <alpine.LNX.2.03.1408270247280.28873@ws.cisco>
[-- Attachment #1: Type: text/plain, Size: 1904 bytes --]
On Wed, 2014-08-27 at 03:03 +0530, Govindarajulu Varadarajan wrote:
> On Fri, 22 Aug 2014, Ben Hutchings wrote:
> > On Thu, 2014-08-14 at 14:59 +0530, Govindarajulu Varadarajan wrote:
> >> @@ -257,6 +262,7 @@ struct ethtool_ops {
> >> struct ethtool_eeprom *, u8 *);
> >> int (*get_eee)(struct net_device *, struct ethtool_eee *);
> >> int (*set_eee)(struct net_device *, struct ethtool_eee *);
> >> + struct ethtool_tunable_ops tunable_ops[ETHTOOL_TUNABLE_MAX];
> >
> > This is OK but if we add a lot of tunables then it bloats up each driver
> > with a (probably quite sparse) array of function pointers.
> >
>
> Will fix with David's suggestion.
>
> >> +struct ethtool_tunable {
> >> + u32 cmd;
> >> + u32 tcmd;
> >> + u32 len;
> >> + union {
> >> + u32 rx_copybreak;
> >> + u32 data[48];
> >> + } data;
> > [...]
> >
> > This is not at all generic. If tunables don't all have the same type,
> > then the type - not just the length - should be explicit.
> >
> > I also think that if the length of a value can vary then we should not
> > declare a data member at all. So we would have something like:
> >
> > struct ethtool_tunable {
> > __u32 cmd;
> > __u32 id;
> > __u32 type_id;
> > __u32 len;
> > __u8 data[0];
> > };
> >
> > Then the value buffer would be passed to the driver functions separately
> > (as for other variable-length command structures).
> >
>
> OK. id show be the tunable command type right? Like RX_COPYBREAK, TX_COPYBREAK
> etc. What is the type_id?
A number that identifies the type of the tunable (it might be u32, u64,
link-layer address, text, etc.).
Ben.
> > By the way, you must use double-underscore prefixes on fixed-width
> > integer type names in UAPI headers.
> >
>
> will fix it.
>
> Thanks
--
Ben Hutchings
No political challenge can be met by shopping. - George Monbiot
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 811 bytes --]
^ permalink raw reply
* [PATCH v2] ethernet: arc: Add support for Rockchip SoC layer device tree bindings
From: Romain Perier @ 2014-08-27 6:55 UTC (permalink / raw)
To: heiko; +Cc: linux-rockchip, linux-arm-kernel, netdev, arnd
This patch defines a platform glue layer for Rockchip SoCs which
support arc-emac driver. It ensures that regulator for the rmii is on
before trying to connect to the ethernet controller. It applies right
speed and mode changes to the grf when ethernet settings change.
Signed-off-by: Romain Perier <romain.perier@gmail.com>
---
drivers/net/ethernet/arc/Kconfig | 15 +++
drivers/net/ethernet/arc/Makefile | 1 +
drivers/net/ethernet/arc/emac.h | 2 +
drivers/net/ethernet/arc/emac_main.c | 2 +
drivers/net/ethernet/arc/emac_rockchip.c | 224 +++++++++++++++++++++++++++++++
5 files changed, 244 insertions(+)
create mode 100644 drivers/net/ethernet/arc/emac_rockchip.c
diff --git a/drivers/net/ethernet/arc/Kconfig b/drivers/net/ethernet/arc/Kconfig
index 89e04fd..6d96a82 100644
--- a/drivers/net/ethernet/arc/Kconfig
+++ b/drivers/net/ethernet/arc/Kconfig
@@ -32,4 +32,19 @@ config ARC_EMAC
non-standard on-chip ethernet device ARC EMAC 10/100 is used.
Say Y here if you have such a board. If unsure, say N.
+config EMAC_ROCKCHIP
+ tristate "Rockchip EMAC support"
+ select ARC_EMAC_CORE
+ depends on OF_IRQ
+ depends on OF_NET
+ depends on ARCH_ROCKCHIP
+ depends on REGULATOR_ACT8865
+ depends on SMSC_PHY
+ depends on MFD_SYSCON
+ ---help---
+ Support for Rockchip RK3066/RK3188 EMAC ethernet controllers.
+ This selects Rockchip SoC glue layer support for the
+ emac device driver. This driver is used for RK3066/RK3188
+ EMAC ethernet controller.
+
endif # NET_VENDOR_ARC
diff --git a/drivers/net/ethernet/arc/Makefile b/drivers/net/ethernet/arc/Makefile
index 241bb80..79108af 100644
--- a/drivers/net/ethernet/arc/Makefile
+++ b/drivers/net/ethernet/arc/Makefile
@@ -5,3 +5,4 @@
arc_emac-objs := emac_main.o emac_mdio.o
obj-$(CONFIG_ARC_EMAC_CORE) += arc_emac.o
obj-$(CONFIG_ARC_EMAC) += emac_arc.o
+obj-$(CONFIG_EMAC_ROCKCHIP) += emac_rockchip.o
diff --git a/drivers/net/ethernet/arc/emac.h b/drivers/net/ethernet/arc/emac.h
index 164b1c7..2bfe8c76 100644
--- a/drivers/net/ethernet/arc/emac.h
+++ b/drivers/net/ethernet/arc/emac.h
@@ -127,6 +127,8 @@ struct buffer_state {
struct arc_emac_priv {
const char *drv_name;
const char *drv_version;
+ void (*set_mac_speed)(void *priv, unsigned int speed);
+
/* Devices */
struct device *dev;
struct phy_device *phy_dev;
diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c
index b35c69e..a08f343 100644
--- a/drivers/net/ethernet/arc/emac_main.c
+++ b/drivers/net/ethernet/arc/emac_main.c
@@ -48,6 +48,8 @@ static void arc_emac_adjust_link(struct net_device *ndev)
if (priv->speed != phy_dev->speed) {
priv->speed = phy_dev->speed;
state_changed = 1;
+ if (priv->set_mac_speed)
+ priv->set_mac_speed(priv, priv->speed);
}
if (priv->duplex != phy_dev->duplex) {
diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c
new file mode 100644
index 0000000..ff1a657
--- /dev/null
+++ b/drivers/net/ethernet/arc/emac_rockchip.c
@@ -0,0 +1,224 @@
+/**
+ * emac-rockchip.c - Rockchip EMAC specific glue layer
+ *
+ * Copyright (C) 2014 Romain Perier
+ *
+ * Romain Perier <romain.perier@gmail.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_net.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include "emac.h"
+
+#define DRV_NAME "rockchip_emac"
+#define DRV_VERSION "1.0"
+
+#define GRF_MODE_MII BIT(0)
+#define GRF_MODE_RMII 0x0
+#define GRF_SPEED_10M 0x0
+#define GRF_SPEED_100M BIT(1)
+
+struct emac_rockchip_soc_data {
+ int grf_offset;
+};
+
+struct rockchip_priv_data {
+ struct arc_emac_priv emac;
+ struct regmap *grf;
+ struct emac_rockchip_soc_data *soc_data;
+ struct regulator *regulator;
+ struct clk *refclk;
+};
+
+static const struct of_device_id emac_rockchip_dt_ids[];
+
+static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed)
+{
+ struct rockchip_priv_data *emac = priv;
+ u32 data;
+ int err = 0;
+
+ /* write-enable bits */
+ data = BIT(17);
+
+ switch(speed) {
+ case 10:
+ data |= GRF_SPEED_10M;
+ break;
+ case 100:
+ data |= GRF_SPEED_100M;
+ break;
+ default:
+ pr_err("speed %u not supported\n", speed);
+ return;
+ }
+
+ err = regmap_write(emac->grf, emac->soc_data->grf_offset, data);
+ if (err)
+ pr_err("unable to apply speed %u to grf (%d)\n", speed, err);
+}
+
+static int emac_rockchip_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct net_device *ndev;
+ struct rockchip_priv_data *priv;
+ const struct of_device_id *match;
+ u32 data, rate;
+ int err, interface;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ ndev = alloc_etherdev(sizeof(struct rockchip_priv_data));
+ if (!ndev)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, ndev);
+ SET_NETDEV_DEV(ndev, dev);
+
+ priv = netdev_priv(ndev);
+ priv->emac.drv_name = DRV_NAME;
+ priv->emac.drv_version = DRV_VERSION;
+ priv->emac.set_mac_speed = emac_rockchip_set_mac_speed;
+
+ interface = of_get_phy_mode(dev->of_node);
+
+ /* RK3066 and RK3188 SoCs only support RMII */
+ if (interface != PHY_INTERFACE_MODE_RMII) {
+ dev_err(dev, "unsupported phy interface mode %d\n", interface);
+ err = -ENOTSUPP;
+ goto out_netdev;
+ }
+
+ priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+ if (IS_ERR(priv->grf)) {
+ dev_err(dev, "failed to retrieve global register file from the device tree (%ld)\n", PTR_ERR(priv->grf));
+ err = PTR_ERR(priv->grf);
+ goto out_netdev;
+ }
+
+ match = of_match_node(emac_rockchip_dt_ids, dev->of_node);
+ priv->soc_data = (struct emac_rockchip_soc_data *)match->data;
+
+ priv->emac.clk = devm_clk_get(dev, "hclk");
+ if (IS_ERR(priv->emac.clk)) {
+ dev_err(dev, "failed to retrieve host clock from device tree (%ld)\n", PTR_ERR(priv->emac.clk));
+ err = PTR_ERR(priv->emac.clk);
+ goto out_netdev;
+ }
+
+ priv->refclk = devm_clk_get(dev, "macref");
+ if (IS_ERR(priv->refclk)) {
+ dev_err(dev, "failed to retrieve reference clock from device tree (%ld)\n", PTR_ERR(priv->refclk));
+ err = PTR_ERR(priv->refclk);
+ goto out_netdev;
+ }
+
+ err = clk_prepare_enable(priv->refclk);
+ if (err) {
+ dev_err(dev, "failed to enable reference clock (%d)\n", err);
+ goto out_netdev;
+ }
+
+ /* Optional regulator for PHY */
+ priv->regulator = devm_regulator_get_optional(dev, "phy");
+ if (IS_ERR(priv->regulator)) {
+ if (PTR_ERR(priv->regulator) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_err(dev, "no regulator found\n");
+ priv->regulator = NULL;
+ }
+
+ if (priv->regulator) {
+ err = regulator_enable(priv->regulator);
+ if (err) {
+ dev_err(dev, "failed to enable phy-supply (%d)\n", err);
+ goto out_netdev;
+ }
+ }
+
+ err = arc_emac_probe(ndev, interface);
+ if (err)
+ goto out_netdev;
+
+ /* write-enable bits */
+ data = BIT(16) | BIT(17);
+
+ data |= GRF_SPEED_100M;
+ data |= GRF_MODE_RMII;
+ rate = 50000000;
+
+ err = regmap_write(priv->grf, priv->soc_data->grf_offset, data);
+ if (err) {
+ dev_err(dev, "unable to apply initial settings to grf (%d)\n", err);
+ goto out_netdev;
+ }
+
+ err = clk_set_rate(priv->refclk, rate);
+ if (err)
+ dev_err(dev, "failed to change reference clock rate (%d)\n", err);
+
+out_netdev:
+ if (err)
+ free_netdev(ndev);
+ return err;
+}
+
+static int emac_rockchip_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct rockchip_priv_data *priv = netdev_priv(ndev);
+ int err;
+
+ clk_disable_unprepare(priv->refclk);
+
+ if (priv->regulator)
+ regulator_disable(priv->regulator);
+ err = arc_emac_remove(ndev);
+ free_netdev(ndev);
+ return err;
+}
+
+static const struct emac_rockchip_soc_data emac_rockchip_dt_data[] = {
+ { .grf_offset = 0x154 }, /* rk3066 */
+ { .grf_offset = 0x0a4 }, /* rk3188 */
+};
+
+static const struct of_device_id emac_rockchip_dt_ids[] = {
+ { .compatible = "rockchip,rk3066-emac", .data = (void *)&emac_rockchip_dt_data[0]},
+ { .compatible = "rockchip,rk3188-emac", .data = (void *)&emac_rockchip_dt_data[1]},
+ { /* Sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, emac_rockchip_dt_ids);
+
+static struct platform_driver emac_rockchip_driver = {
+ .probe = emac_rockchip_probe,
+ .remove = emac_rockchip_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = emac_rockchip_dt_ids,
+ },
+};
+
+module_platform_driver(emac_rockchip_driver);
+
+MODULE_AUTHOR("Romain Perier <romain.perier@gmail.com>");
+MODULE_DESCRIPTION("Rockchip EMAC platform driver");
+MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related
* linux xfrm question
From: Chintu Aggarwal @ 2014-08-27 6:37 UTC (permalink / raw)
To: netdev
Hi
I wanted to add multiple SAs but only one SPD. SPD has remote address
as wildcard but proper local address. SAs have both proper local and
remote address. Local address in all SAs and SPD is same. As IPsec has
to be applied for all packets, I wanted to avoid SPD setup and lookup
time.
When I use it this way, it seems that xfrm code adds bundles to the
same SPD which is a linear search. Is there a hash implementation
available for the same, or some other way to improve search time here?
Thanks
Chintu
^ permalink raw reply
* [PATCH (net.git)] stmmac: fix tipo on mmc crc error
From: Giuseppe Cavallaro @ 2014-08-27 6:26 UTC (permalink / raw)
To: netdev; +Cc: Giuseppe CAVALLARO
From: Giuseppe CAVALLARO <peppe.cavallaro@st.com>
This patch is to fix a typo on mmc rx crc error when reported by ethtool.
Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
---
drivers/net/ethernet/stmicro/stmmac/mmc.h | 2 +-
drivers/net/ethernet/stmicro/stmmac/mmc_core.c | 2 +-
.../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc.h b/drivers/net/ethernet/stmicro/stmmac/mmc.h
index 8607488..192c249 100644
--- a/drivers/net/ethernet/stmicro/stmmac/mmc.h
+++ b/drivers/net/ethernet/stmicro/stmmac/mmc.h
@@ -68,7 +68,7 @@ struct stmmac_counters {
unsigned int mmc_rx_octetcount_g;
unsigned int mmc_rx_broadcastframe_g;
unsigned int mmc_rx_multicastframe_g;
- unsigned int mmc_rx_crc_errror;
+ unsigned int mmc_rx_crc_error;
unsigned int mmc_rx_align_error;
unsigned int mmc_rx_run_error;
unsigned int mmc_rx_jabber_error;
diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
index 50617c5..08c483b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
@@ -196,7 +196,7 @@ void dwmac_mmc_read(void __iomem *ioaddr, struct stmmac_counters *mmc)
mmc->mmc_rx_octetcount_g += readl(ioaddr + MMC_RX_OCTETCOUNT_G);
mmc->mmc_rx_broadcastframe_g += readl(ioaddr + MMC_RX_BROADCASTFRAME_G);
mmc->mmc_rx_multicastframe_g += readl(ioaddr + MMC_RX_MULTICASTFRAME_G);
- mmc->mmc_rx_crc_errror += readl(ioaddr + MMC_RX_CRC_ERRROR);
+ mmc->mmc_rx_crc_error += readl(ioaddr + MMC_RX_CRC_ERRROR);
mmc->mmc_rx_align_error += readl(ioaddr + MMC_RX_ALIGN_ERROR);
mmc->mmc_rx_run_error += readl(ioaddr + MMC_RX_RUN_ERROR);
mmc->mmc_rx_jabber_error += readl(ioaddr + MMC_RX_JABBER_ERROR);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index 9af50ba..cf4f38d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -175,7 +175,7 @@ static const struct stmmac_stats stmmac_mmc[] = {
STMMAC_MMC_STAT(mmc_rx_octetcount_g),
STMMAC_MMC_STAT(mmc_rx_broadcastframe_g),
STMMAC_MMC_STAT(mmc_rx_multicastframe_g),
- STMMAC_MMC_STAT(mmc_rx_crc_errror),
+ STMMAC_MMC_STAT(mmc_rx_crc_error),
STMMAC_MMC_STAT(mmc_rx_align_error),
STMMAC_MMC_STAT(mmc_rx_run_error),
STMMAC_MMC_STAT(mmc_rx_jabber_error),
--
1.7.4.4
^ permalink raw reply related
* iproute2: Optional 'name' keywork in ip link is broken
From: Mehdi Abaakouk @ 2014-08-27 6:16 UTC (permalink / raw)
To: netdev
Hi,
I got an issue since iproute2 3.16, this command is not working anymore:
# ip link add bar type veth peer name foo
Error: argument "bar" is wrong: Unknown device
My current workaround is to use the optional "name" keywork for the
first veth
# ip link add name bar type veth peer name foo
This issue was introduced by:
http://git.kernel.org/cgit/linux/kernel/git/shemminger/iproute2.git/commit/?id=9a02651a87d0fd56e2e7eedd63921a050a42b3ec
Regards,
--
Mehdi Abaakouk
mail: sileht@sileht.net
irc: sileht
^ permalink raw reply
* [PATCH nf-next] ipvs: reduce stack usage for sockopt data
From: Simon Horman @ 2014-08-27 6:20 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: lvs-devel, netdev, netfilter-devel, Wensong Zhang,
Julian Anastasov, Dan Carpenter, Andrey Utkin, David Binderman,
Simon Horman
In-Reply-To: <1409120443-978-1-git-send-email-horms@verge.net.au>
From: Julian Anastasov <ja@ssi.bg>
Use macros and union to reserve the required stack space for
sockopt data. Now the tables for commands should be more safe
to extend. The checks added for readability are optimized by
compiler, others warn at compile time if command uses too much
stack or exceeds the storage of set_arglen and get_arglen.
As Dan Carpenter points out, we can run for unprivileged user,
so we can silent some error messages.
Signed-off-by: Julian Anastasov <ja@ssi.bg>
CC: Dan Carpenter <dan.carpenter@oracle.com>
CC: Andrey Utkin <andrey.krieger.utkin@gmail.com>
CC: David Binderman <dcb314@hotmail.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
---
net/netfilter/ipvs/ip_vs_ctl.c | 102 +++++++++++++++++++++++------------------
1 file changed, 57 insertions(+), 45 deletions(-)
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index fd3f444..0140e09 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -2180,28 +2180,34 @@ static int ip_vs_set_timeout(struct net *net, struct ip_vs_timeout_user *u)
}
-#define SET_CMDID(cmd) (cmd - IP_VS_BASE_CTL)
-#define SERVICE_ARG_LEN (sizeof(struct ip_vs_service_user))
-#define SVCDEST_ARG_LEN (sizeof(struct ip_vs_service_user) + \
- sizeof(struct ip_vs_dest_user))
-#define TIMEOUT_ARG_LEN (sizeof(struct ip_vs_timeout_user))
-#define DAEMON_ARG_LEN (sizeof(struct ip_vs_daemon_user))
-#define MAX_ARG_LEN SVCDEST_ARG_LEN
+#define SET_CMDID(cmd) (cmd - IP_VS_BASE_CTL)
+#define IP_VS_SET_CMDID(c, t) [SET_CMDID(c)] = sizeof(t),
+#define IP_VS_SET_CMDID_LEN(c, t) t field_ ## c;
+
+struct ip_vs_svcdest_user {
+ struct ip_vs_service_user s;
+ struct ip_vs_dest_user d;
+};
+
+#define IP_VS_SET_CMDID_TABLE(e) \
+ e(IP_VS_SO_SET_ADD, struct ip_vs_service_user) \
+ e(IP_VS_SO_SET_EDIT, struct ip_vs_service_user) \
+ e(IP_VS_SO_SET_DEL, struct ip_vs_service_user) \
+ e(IP_VS_SO_SET_ADDDEST, struct ip_vs_svcdest_user) \
+ e(IP_VS_SO_SET_DELDEST, struct ip_vs_svcdest_user) \
+ e(IP_VS_SO_SET_EDITDEST, struct ip_vs_svcdest_user) \
+ e(IP_VS_SO_SET_TIMEOUT, struct ip_vs_timeout_user) \
+ e(IP_VS_SO_SET_STARTDAEMON, struct ip_vs_daemon_user) \
+ e(IP_VS_SO_SET_STOPDAEMON, struct ip_vs_daemon_user) \
+ e(IP_VS_SO_SET_ZERO, struct ip_vs_service_user)
static const unsigned char set_arglen[SET_CMDID(IP_VS_SO_SET_MAX)+1] = {
- [SET_CMDID(IP_VS_SO_SET_ADD)] = SERVICE_ARG_LEN,
- [SET_CMDID(IP_VS_SO_SET_EDIT)] = SERVICE_ARG_LEN,
- [SET_CMDID(IP_VS_SO_SET_DEL)] = SERVICE_ARG_LEN,
- [SET_CMDID(IP_VS_SO_SET_FLUSH)] = 0,
- [SET_CMDID(IP_VS_SO_SET_ADDDEST)] = SVCDEST_ARG_LEN,
- [SET_CMDID(IP_VS_SO_SET_DELDEST)] = SVCDEST_ARG_LEN,
- [SET_CMDID(IP_VS_SO_SET_EDITDEST)] = SVCDEST_ARG_LEN,
- [SET_CMDID(IP_VS_SO_SET_TIMEOUT)] = TIMEOUT_ARG_LEN,
- [SET_CMDID(IP_VS_SO_SET_STARTDAEMON)] = DAEMON_ARG_LEN,
- [SET_CMDID(IP_VS_SO_SET_STOPDAEMON)] = DAEMON_ARG_LEN,
- [SET_CMDID(IP_VS_SO_SET_ZERO)] = SERVICE_ARG_LEN,
+ IP_VS_SET_CMDID_TABLE(IP_VS_SET_CMDID)
};
+union ip_vs_set_arglen { IP_VS_SET_CMDID_TABLE(IP_VS_SET_CMDID_LEN) };
+#define MAX_SET_ARGLEN sizeof(union ip_vs_set_arglen)
+
static void ip_vs_copy_usvc_compat(struct ip_vs_service_user_kern *usvc,
struct ip_vs_service_user *usvc_compat)
{
@@ -2239,7 +2245,7 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
{
struct net *net = sock_net(sk);
int ret;
- unsigned char arg[MAX_ARG_LEN];
+ unsigned char arg[MAX_SET_ARGLEN];
struct ip_vs_service_user *usvc_compat;
struct ip_vs_service_user_kern usvc;
struct ip_vs_service *svc;
@@ -2247,16 +2253,15 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
struct ip_vs_dest_user_kern udest;
struct netns_ipvs *ipvs = net_ipvs(net);
+ BUILD_BUG_ON(sizeof(arg) > 255);
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_SET_MAX)
return -EINVAL;
- if (len < 0 || len > MAX_ARG_LEN)
- return -EINVAL;
if (len != set_arglen[SET_CMDID(cmd)]) {
- pr_err("set_ctl: len %u != %u\n",
- len, set_arglen[SET_CMDID(cmd)]);
+ IP_VS_DBG(1, "set_ctl: len %u != %u\n",
+ len, set_arglen[SET_CMDID(cmd)]);
return -EINVAL;
}
@@ -2513,49 +2518,56 @@ __ip_vs_get_timeouts(struct net *net, struct ip_vs_timeout_user *u)
}
-#define GET_CMDID(cmd) (cmd - IP_VS_BASE_CTL)
-#define GET_INFO_ARG_LEN (sizeof(struct ip_vs_getinfo))
-#define GET_SERVICES_ARG_LEN (sizeof(struct ip_vs_get_services))
-#define GET_SERVICE_ARG_LEN (sizeof(struct ip_vs_service_entry))
-#define GET_DESTS_ARG_LEN (sizeof(struct ip_vs_get_dests))
-#define GET_TIMEOUT_ARG_LEN (sizeof(struct ip_vs_timeout_user))
-#define GET_DAEMON_ARG_LEN (sizeof(struct ip_vs_daemon_user) * 2)
+#define GET_CMDID(cmd) (cmd - IP_VS_BASE_CTL)
+#define IP_VS_GET_CMDID(c, t) [GET_CMDID(c)] = sizeof(t),
+#define IP_VS_GET_CMDID_LEN(c, t) t field_ ## c;
+
+struct ip_vs_version_user {
+ char v[64];
+};
+
+struct ip_vs_daemon_user2 {
+ struct ip_vs_daemon_user d1, d2;
+};
+
+#define IP_VS_GET_CMDID_TABLE(e) \
+ e(IP_VS_SO_GET_VERSION, struct ip_vs_version_user) \
+ e(IP_VS_SO_GET_INFO, struct ip_vs_getinfo) \
+ e(IP_VS_SO_GET_SERVICES, struct ip_vs_get_services) \
+ e(IP_VS_SO_GET_SERVICE, struct ip_vs_service_entry) \
+ e(IP_VS_SO_GET_DESTS, struct ip_vs_get_dests) \
+ e(IP_VS_SO_GET_TIMEOUT, struct ip_vs_timeout_user) \
+ e(IP_VS_SO_GET_DAEMON, struct ip_vs_daemon_user2)
static const unsigned char get_arglen[GET_CMDID(IP_VS_SO_GET_MAX)+1] = {
- [GET_CMDID(IP_VS_SO_GET_VERSION)] = 64,
- [GET_CMDID(IP_VS_SO_GET_INFO)] = GET_INFO_ARG_LEN,
- [GET_CMDID(IP_VS_SO_GET_SERVICES)] = GET_SERVICES_ARG_LEN,
- [GET_CMDID(IP_VS_SO_GET_SERVICE)] = GET_SERVICE_ARG_LEN,
- [GET_CMDID(IP_VS_SO_GET_DESTS)] = GET_DESTS_ARG_LEN,
- [GET_CMDID(IP_VS_SO_GET_TIMEOUT)] = GET_TIMEOUT_ARG_LEN,
- [GET_CMDID(IP_VS_SO_GET_DAEMON)] = GET_DAEMON_ARG_LEN,
+ IP_VS_GET_CMDID_TABLE(IP_VS_GET_CMDID)
};
+union ip_vs_get_arglen { IP_VS_GET_CMDID_TABLE(IP_VS_GET_CMDID_LEN) };
+#define MAX_GET_ARGLEN sizeof(union ip_vs_get_arglen)
+
static int
do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
{
- unsigned char arg[128];
+ unsigned char arg[MAX_GET_ARGLEN];
int ret = 0;
unsigned int copylen;
struct net *net = sock_net(sk);
struct netns_ipvs *ipvs = net_ipvs(net);
BUG_ON(!net);
+ BUILD_BUG_ON(sizeof(arg) > 255);
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_GET_MAX)
return -EINVAL;
- if (*len < get_arglen[GET_CMDID(cmd)]) {
- pr_err("get_ctl: len %u < %u\n",
- *len, get_arglen[GET_CMDID(cmd)]);
- return -EINVAL;
- }
-
copylen = get_arglen[GET_CMDID(cmd)];
- if (copylen > 128)
+ if (*len < (int) copylen || *len < 0) {
+ IP_VS_DBG(1, "get_ctl: len %d < %u\n", *len, copylen);
return -EINVAL;
+ }
if (copy_from_user(arg, user, copylen) != 0)
return -EFAULT;
--
2.0.1
^ permalink raw reply related
* [GIT PULL nf-next] IPVS Updates for v3.18
From: Simon Horman @ 2014-08-27 6:20 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: lvs-devel, netdev, netfilter-devel, Wensong Zhang,
Julian Anastasov, Simon Horman
Hi Pablo,
please consider this IPVS update for v3.17.
The update reduces stack memory for sockopt data.
The following changes since commit f111f780ae1abf4cdc464f24293be90c010a04f6:
netfilter: nfnetlink_acct: add filter support to nfacct counter list/reset (2014-08-26 21:36:19 +0200)
are available in the git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/horms/ipvs-next.git tags/ipvs-for-v3.18
for you to fetch changes up to af912017e809155ddded01ddc1dc48883f5186e5:
ipvs: reduce stack usage for sockopt data (2014-08-27 14:32:34 +0900)
----------------------------------------------------------------
Julian Anastasov (1):
ipvs: reduce stack usage for sockopt data
net/netfilter/ipvs/ip_vs_ctl.c | 102 +++++++++++++++++++++++------------------
1 file changed, 57 insertions(+), 45 deletions(-)
^ permalink raw reply
* [PATCH (net.git)] stmmac: prevent false carrier sense detection
From: Giuseppe Cavallaro @ 2014-08-27 6:20 UTC (permalink / raw)
To: netdev; +Cc: Giuseppe Cavallaro
This patch is to w/a a problem that happens on some boxes when run at 10Mbps
Half duplex mode.
During the transmission the CSR signal is asserted for some time and the frames
aborted because of carrier sense error.
This is reported by MMC HW counter: txcarrier signal.
This actually is a false carrier so the frames are good and there is no reason
to ask for dropping them.
This patch so disables the Carrier Sense During Transmission
and this means that the MAC transmitter ignore the CRS signal
during frame transmission in Half-Duplex mode.
Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Acked-by: Vince Bridgers <vbridgers2013@gmail.com>
Acked-by: Ley Foon Tan <lftan@altera.com>
Acked-by: Chen-Yu Tsai <wens@csie.org>
---
drivers/net/ethernet/stmicro/stmmac/dwmac1000.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
index 71b5419..64d8f56 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
@@ -153,7 +153,7 @@ enum inter_frame_gap {
#define GMAC_CONTROL_RE 0x00000004 /* Receiver Enable */
#define GMAC_CORE_INIT (GMAC_CONTROL_JD | GMAC_CONTROL_PS | GMAC_CONTROL_ACS | \
- GMAC_CONTROL_BE)
+ GMAC_CONTROL_BE | GMAC_CONTROL_DCRS)
/* GMAC Frame Filter defines */
#define GMAC_FRAME_FILTER_PR 0x00000001 /* Promiscuous Mode */
--
1.7.4.4
^ permalink raw reply related
* [GIT PULL nf] IPVS fixes for v3.17
From: Simon Horman @ 2014-08-27 6:15 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: lvs-devel, netdev, netfilter-devel, Wensong Zhang,
Julian Anastasov, Simon Horman
Hi Pablo,
please consider this IPVS fix for v3.17.
It allows CHECKSUM_PARTIAL to work in conjunction with
TCP/UDP csum offlad and the tunnel forwarding mechanism
provided by IPVS.
I suspect that this problem has existed for as long
has IPVS has been present in the kernel: some time before
git epoch of the git history in Linus's tree.
I would like this change considered for -stable.
It appears to apply and build cleanly against v3.16.1 and v3.14.17.
I can work on patches for older versions if you think it is appropriate.
The following changes since commit d1c85c2ebe7ffe1f1b27846bd1ba0944c513d822:
netfilter: HAVE_JUMP_LABEL instead of CONFIG_JUMP_LABEL (2014-08-25 10:45:28 +0200)
are available in the git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/horms/ipvs.git tags/ipvs-fixes-for-v3.17
for you to fetch changes up to ea1d5d7755a3e556de78cc757d1895d5c7180548:
ipvs: properly declare tunnel encapsulation (2014-08-27 14:31:56 +0900)
----------------------------------------------------------------
Julian Anastasov (1):
ipvs: properly declare tunnel encapsulation
net/netfilter/ipvs/ip_vs_xmit.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
Julian Anastasov (1):
ipvs: properly declare tunnel encapsulation
net/netfilter/ipvs/ip_vs_xmit.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
--
2.0.1
^ permalink raw reply
* [PATCH nf] ipvs: properly declare tunnel encapsulation
From: Simon Horman @ 2014-08-27 6:15 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: lvs-devel, netdev, netfilter-devel, Wensong Zhang,
Julian Anastasov, Alex Gartrell, Simon Horman
In-Reply-To: <1409120115-32053-1-git-send-email-horms@verge.net.au>
From: Julian Anastasov <ja@ssi.bg>
The tunneling method should properly use tunnel encapsulation.
Fixes problem with CHECKSUM_PARTIAL packets when TCP/UDP csum
offload is supported.
Thanks to Alex Gartrell for reporting the problem, providing
solution and for all suggestions.
Reported-by: Alex Gartrell <agartrell@fb.com>
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Alex Gartrell <agartrell@fb.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
---
net/netfilter/ipvs/ip_vs_xmit.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index 6f70bdd..56896a4 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -38,6 +38,7 @@
#include <net/route.h> /* for ip_route_output */
#include <net/ipv6.h>
#include <net/ip6_route.h>
+#include <net/ip_tunnels.h>
#include <net/addrconf.h>
#include <linux/icmpv6.h>
#include <linux/netfilter.h>
@@ -862,11 +863,15 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
old_iph = ip_hdr(skb);
}
- skb->transport_header = skb->network_header;
-
/* fix old IP header checksum */
ip_send_check(old_iph);
+ skb = iptunnel_handle_offloads(skb, false, SKB_GSO_IPIP);
+ if (IS_ERR(skb))
+ goto tx_error;
+
+ skb->transport_header = skb->network_header;
+
skb_push(skb, sizeof(struct iphdr));
skb_reset_network_header(skb);
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
@@ -900,7 +905,8 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
return NF_STOLEN;
tx_error:
- kfree_skb(skb);
+ if (!IS_ERR(skb))
+ kfree_skb(skb);
rcu_read_unlock();
LeaveFunction(10);
return NF_STOLEN;
@@ -953,6 +959,11 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
old_iph = ipv6_hdr(skb);
}
+ /* GSO: we need to provide proper SKB_GSO_ value for IPv6 */
+ skb = iptunnel_handle_offloads(skb, false, 0); /* SKB_GSO_SIT/IPV6 */
+ if (IS_ERR(skb))
+ goto tx_error;
+
skb->transport_header = skb->network_header;
skb_push(skb, sizeof(struct ipv6hdr));
@@ -988,7 +999,8 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
return NF_STOLEN;
tx_error:
- kfree_skb(skb);
+ if (!IS_ERR(skb))
+ kfree_skb(skb);
rcu_read_unlock();
LeaveFunction(10);
return NF_STOLEN;
--
2.0.1
^ permalink raw reply related
* Re: [PATCH RFC v7 net-next 00/28] BPF syscall
From: David Miller @ 2014-08-27 6:11 UTC (permalink / raw)
To: ast-uqk4Ao+rVK5Wk0Htik3J/w
Cc: mingo-DgEjT+Ai2ygdnm+yROfE0A,
torvalds-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
luto-kltTT9wpgjJwATOyAt5JVQ, rostedt-nx8X9YLhiw1AfugRpC6u6w,
dborkman-H+wXaHxf7aLQT0dZR+AlfA, chema-hpIqsD4AKlfQT0dZR+AlfA,
edumazet-hpIqsD4AKlfQT0dZR+AlfA,
a.p.zijlstra-/NLkJaSkS4VmR6Xm/wNWPw,
brendan.d.gregg-Re5JQEeQqe8AvxtiuMwx3w,
namhyung-DgEjT+Ai2ygdnm+yROfE0A, hpa-YMNOUZJC4hwAvxtiuMwx3w,
akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
keescook-F7+t8E8rja9g9hUCZPvPmw, linux-api-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1409106582-10095-1-git-send-email-ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org>
From: Alexei Starovoitov <ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org>
Date: Tue, 26 Aug 2014 19:29:14 -0700
> posting whole thing again as RFC to get feedback on syscall only.
> If syscall bpf(int cmd, union bpf_attr *attr, unsigned int size) is ok,
I'm personally not reviewing such a large patch series, sorry.
You need to submit smaller sets if you want to get reasonable
review of your changes and ideas.
^ permalink raw reply
* Re: IPv6 Policy based routing not working.
From: Hannes Frederic Sowa @ 2014-08-27 6:05 UTC (permalink / raw)
To: Tushar Shinde; +Cc: netdev
In-Reply-To: <CABQpAL4zbeqX4X1Kuzw-+FOppmYx7VzTzJn9OMLyYVBdS0Bz9w@mail.gmail.com>
On Mi, 2014-08-27 at 11:32 +0530, Tushar Shinde wrote:
> So do we need to always use ipv6 address to bind unlike ipv4?
No, I think SO_BINDTODEVICE should work. But ping6 does not use it, so
one could add support for this to ping with a new option.
ping6 uses cmsg with undefined ipv6 source address but set ifindex and
that's not at all like SO_BINDTODEVICE.
Bye,
Hannes
^ permalink raw reply
* Re: IPv6 Policy based routing not working.
From: Tushar Shinde @ 2014-08-27 6:02 UTC (permalink / raw)
To: Hannes Frederic Sowa; +Cc: netdev
In-Reply-To: <1409118957.11976.9.camel@localhost>
So do we need to always use ipv6 address to bind unlike ipv4?
Tushar
On Wed, Aug 27, 2014 at 11:25 AM, Hannes Frederic Sowa
<hannes@stressinduktion.org> wrote:
> On Mi, 2014-08-27 at 10:24 +0530, Tushar Shinde wrote:
>> Hi Hannes,
>>
>> Thank you for reply.
>>
>> I tested it will 2 kernels 2.6.32-431.11.2.el6.x86_64, and ubuntu
>> 14.04 kernel 3.13.
>>
>> ping6 -I <IPv6 src ip> <dest> is working. I also verified ssh with -b working.
>>
>> So my question is, is it necessary to give bind address to make source
>> based routing work? why don't it select ip address of given bind
>> interface? Because of this SO_BINDTODEVICE may not work as expected.
>
> Hmm, not sure, but last time I looked ping6 did not do SO_BINDTODEVICE
> at all and I doubt it changed. It merely sets outgoing interface index
> in cmsg PKTINFO data.
>
> Semantically those are not the same. Interface index on global
> destination addresses is only advisory, no strict routing lookup will be
> done.
>
> Bye,
> Hannes
>
>
^ permalink raw reply
* Re: IPv6 Policy based routing not working.
From: Hannes Frederic Sowa @ 2014-08-27 5:55 UTC (permalink / raw)
To: Tushar Shinde; +Cc: netdev
In-Reply-To: <CABQpAL4J7D704iw8Qj9SNDxW8E1P7Mw+KONUapZY6_LdoHkEYw@mail.gmail.com>
On Mi, 2014-08-27 at 10:24 +0530, Tushar Shinde wrote:
> Hi Hannes,
>
> Thank you for reply.
>
> I tested it will 2 kernels 2.6.32-431.11.2.el6.x86_64, and ubuntu
> 14.04 kernel 3.13.
>
> ping6 -I <IPv6 src ip> <dest> is working. I also verified ssh with -b working.
>
> So my question is, is it necessary to give bind address to make source
> based routing work? why don't it select ip address of given bind
> interface? Because of this SO_BINDTODEVICE may not work as expected.
Hmm, not sure, but last time I looked ping6 did not do SO_BINDTODEVICE
at all and I doubt it changed. It merely sets outgoing interface index
in cmsg PKTINFO data.
Semantically those are not the same. Interface index on global
destination addresses is only advisory, no strict routing lookup will be
done.
Bye,
Hannes
^ permalink raw reply
* Re: [net-next v3 2/3] vxlan: Refactor vxlan driver to make use of the common UDP tunnel functions.
From: Andy Zhou @ 2014-08-27 5:19 UTC (permalink / raw)
To: Tom Herbert; +Cc: David Miller, Linux Netdev List
In-Reply-To: <CA+mtBx8R2YyAMbPuw=iYDbLpSwRo_robnxOYPUFV2368-RzdLg@mail.gmail.com>
On Tue, Aug 26, 2014 at 9:14 PM, Tom Herbert <therbert@google.com> wrote:
>
>
> On Tue, Aug 26, 2014 at 8:35 PM, Andy Zhou <azhou@nicira.com> wrote:
>> Signed-off-by: Andy Zhou <azhou@nicira.com>
>> ---
>> drivers/net/vxlan.c | 199
>> ++++++++++++-----------------------------
>> include/net/vxlan.h | 16 ++--
>> net/openvswitch/vport-vxlan.c | 6 +-
>> 3 files changed, 68 insertions(+), 153 deletions(-)
>>
>> diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
>> index beb377b..f1f1c48 100644
>> --- a/drivers/net/vxlan.c
>> +++ b/drivers/net/vxlan.c
>> @@ -42,6 +42,7 @@
>> #include <net/netns/generic.h>
>> #include <net/vxlan.h>
>> #include <net/protocol.h>
>> +#include <net/udp_tunnel.h>
>> #if IS_ENABLED(CONFIG_IPV6)
>> #include <net/ipv6.h>
>> #include <net/addrconf.h>
>> @@ -277,13 +278,7 @@ static inline struct vxlan_rdst
>> *first_remote_rtnl(struct vxlan_fdb *fdb)
>> /* Find VXLAN socket based on network namespace and UDP port */
>> static struct vxlan_sock *vxlan_find_sock(struct net *net, __be16 port)
>> {
>> - struct vxlan_sock *vs;
>> -
>> - hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {
>> - if (inet_sk(vs->sock->sk)->inet_sport == port)
>> - return vs;
>> - }
>> - return NULL;
>> + return (struct vxlan_sock *)udp_tunnel_find_sock(net, port);
>
> It seems incorrect to assume that the socket returned is vxlan, presumably
> this could be some other type of tunnel socket.
>
If I drop udp_tunnel_find_sock API from last patch, then this should
not be an issue any more right?
vxlan driver will just keep track of its own open sock.
>> }
>>
>> static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, u32 id)
>> @@ -636,7 +631,7 @@ static int vxlan_gro_complete(struct sk_buff *skb, int
>> nhoff)
>> static void vxlan_notify_add_rx_port(struct vxlan_sock *vs)
>> {
>> struct net_device *dev;
>> - struct sock *sk = vs->sock->sk;
>> + struct sock *sk = vs->uts.sock->sk;
>> struct net *net = sock_net(sk);
>> sa_family_t sa_family = sk->sk_family;
>> __be16 port = inet_sk(sk)->inet_sport;
>> @@ -661,7 +656,7 @@ static void vxlan_notify_add_rx_port(struct vxlan_sock
>> *vs)
>> static void vxlan_notify_del_rx_port(struct vxlan_sock *vs)
>> {
>> struct net_device *dev;
>> - struct sock *sk = vs->sock->sk;
>> + struct sock *sk = vs->uts.sock->sk;
>> struct net *net = sock_net(sk);
>> sa_family_t sa_family = sk->sk_family;
>> __be16 port = inet_sk(sk)->inet_sport;
>> @@ -1053,7 +1048,7 @@ static void vxlan_sock_hold(struct vxlan_sock *vs)
>>
>> void vxlan_sock_release(struct vxlan_sock *vs)
>> {
>> - struct sock *sk = vs->sock->sk;
>> + struct sock *sk = vs->uts.sock->sk;
>> struct net *net = sock_net(sk);
>> struct vxlan_net *vn = net_generic(net, vxlan_net_id);
>>
>> @@ -1062,7 +1057,6 @@ void vxlan_sock_release(struct vxlan_sock *vs)
>>
>> spin_lock(&vn->sock_lock);
>> hlist_del_rcu(&vs->hlist);
>> - rcu_assign_sk_user_data(vs->sock->sk, NULL);
>> vxlan_notify_del_rx_port(vs);
>> spin_unlock(&vn->sock_lock);
>>
>> @@ -1078,7 +1072,7 @@ static void vxlan_igmp_join(struct work_struct
>> *work)
>> {
>> struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev,
>> igmp_join);
>> struct vxlan_sock *vs = vxlan->vn_sock;
>> - struct sock *sk = vs->sock->sk;
>> + struct sock *sk = vs->uts.sock->sk;
>> union vxlan_addr *ip = &vxlan->default_dst.remote_ip;
>> int ifindex = vxlan->default_dst.remote_ifindex;
>>
>> @@ -1107,7 +1101,7 @@ static void vxlan_igmp_leave(struct work_struct
>> *work)
>> {
>> struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev,
>> igmp_leave);
>> struct vxlan_sock *vs = vxlan->vn_sock;
>> - struct sock *sk = vs->sock->sk;
>> + struct sock *sk = vs->uts.sock->sk;
>> union vxlan_addr *ip = &vxlan->default_dst.remote_ip;
>> int ifindex = vxlan->default_dst.remote_ifindex;
>>
>> @@ -1338,7 +1332,6 @@ out:
>> }
>>
>> #if IS_ENABLED(CONFIG_IPV6)
>> -
>> static struct sk_buff *vxlan_na_create(struct sk_buff *request,
>> struct neighbour *n, bool isrouter)
>> {
>> @@ -1572,13 +1565,6 @@ static bool route_shortcircuit(struct net_device
>> *dev, struct sk_buff *skb)
>> return false;
>> }
>>
>> -static inline struct sk_buff *vxlan_handle_offloads(struct sk_buff *skb,
>> - bool udp_csum)
>> -{
>> - int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM :
>> SKB_GSO_UDP_TUNNEL;
>> - return iptunnel_handle_offloads(skb, udp_csum, type);
>> -}
>> -
>> #if IS_ENABLED(CONFIG_IPV6)
>> static int vxlan6_xmit_skb(struct vxlan_sock *vs,
>> struct dst_entry *dst, struct sk_buff *skb,
>> @@ -1587,13 +1573,13 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
>> __be16 src_port, __be16 dst_port, __be32 vni,
>> bool xnet)
>> {
>> - struct ipv6hdr *ip6h;
>> struct vxlanhdr *vxh;
>> - struct udphdr *uh;
>> int min_headroom;
>> int err;
>>
>> - skb = vxlan_handle_offloads(skb,
>> !udp_get_no_check6_tx(vs->sock->sk));
>> + skb = udp_tunnel_handle_offloads(skb,
>> + !udp_get_no_check6_tx(
>> + vs->uts.sock->sk));
>> if (IS_ERR(skb))
>> return -EINVAL;
>>
>> @@ -1621,38 +1607,8 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
>> vxh->vx_flags = htonl(VXLAN_FLAGS);
>> vxh->vx_vni = vni;
>>
>> - __skb_push(skb, sizeof(*uh));
>> - skb_reset_transport_header(skb);
>> - uh = udp_hdr(skb);
>> -
>> - uh->dest = dst_port;
>> - uh->source = src_port;
>> -
>> - uh->len = htons(skb->len);
>> -
>> - memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
>> - IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE |
>> IPSKB_XFRM_TRANSFORMED |
>> - IPSKB_REROUTED);
>> - skb_dst_set(skb, dst);
>> -
>> - udp6_set_csum(udp_get_no_check6_tx(vs->sock->sk), skb,
>> - saddr, daddr, skb->len);
>> -
>> - __skb_push(skb, sizeof(*ip6h));
>> - skb_reset_network_header(skb);
>> - ip6h = ipv6_hdr(skb);
>> - ip6h->version = 6;
>> - ip6h->priority = prio;
>> - ip6h->flow_lbl[0] = 0;
>> - ip6h->flow_lbl[1] = 0;
>> - ip6h->flow_lbl[2] = 0;
>> - ip6h->payload_len = htons(skb->len);
>> - ip6h->nexthdr = IPPROTO_UDP;
>> - ip6h->hop_limit = ttl;
>> - ip6h->daddr = *daddr;
>> - ip6h->saddr = *saddr;
>> -
>> - ip6tunnel_xmit(skb, dev);
>> + udp_tunnel6_xmit_skb(vs->uts.sock, dst, skb, dev, saddr, daddr,
>> prio,
>> + ttl, src_port, dst_port);
>> return 0;
>> }
>> #endif
>> @@ -1663,11 +1619,11 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
>> __be16 src_port, __be16 dst_port, __be32 vni, bool
>> xnet)
>> {
>> struct vxlanhdr *vxh;
>> - struct udphdr *uh;
>> int min_headroom;
>> int err;
>>
>> - skb = vxlan_handle_offloads(skb, !vs->sock->sk->sk_no_check_tx);
>> + skb = udp_tunnel_handle_offloads(skb,
>> +
>> !vs->uts.sock->sk->sk_no_check_tx);
>> if (IS_ERR(skb))
>> return -EINVAL;
>>
>> @@ -1693,20 +1649,8 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
>> vxh->vx_flags = htonl(VXLAN_FLAGS);
>> vxh->vx_vni = vni;
>>
>> - __skb_push(skb, sizeof(*uh));
>> - skb_reset_transport_header(skb);
>> - uh = udp_hdr(skb);
>> -
>> - uh->dest = dst_port;
>> - uh->source = src_port;
>> -
>> - uh->len = htons(skb->len);
>> -
>> - udp_set_csum(vs->sock->sk->sk_no_check_tx, skb,
>> - src, dst, skb->len);
>> -
>> - return iptunnel_xmit(vs->sock->sk, rt, skb, src, dst, IPPROTO_UDP,
>> - tos, ttl, df, xnet);
>> + return udp_tunnel_xmit_skb(vs->uts.sock, rt, skb, src, dst, tos,
>> + ttl, df, src_port, dst_port, xnet);
>> }
>> EXPORT_SYMBOL_GPL(vxlan_xmit_skb);
>>
>> @@ -1831,18 +1775,18 @@ static void vxlan_xmit_one(struct sk_buff *skb,
>> struct net_device *dev,
>> tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
>> ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
>>
>> - err = vxlan_xmit_skb(vxlan->vn_sock, rt, skb,
>> - fl4.saddr, dst->sin.sin_addr.s_addr,
>> - tos, ttl, df, src_port, dst_port,
>> - htonl(vni << 8),
>> - !net_eq(vxlan->net,
>> dev_net(vxlan->dev)));
>> + err = udp_tunnel_xmit_skb(vxlan->vn_sock->uts.sock, rt,
>> skb,
>> + fl4.saddr,
>> dst->sin.sin_addr.s_addr,
>> + tos, ttl, df, src_port,
>> dst_port,
>> + !net_eq(vxlan->net,
>> + dev_net(vxlan->dev)));
>>
>> if (err < 0)
>> goto rt_tx_error;
>> iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
>> #if IS_ENABLED(CONFIG_IPV6)
>> } else {
>> - struct sock *sk = vxlan->vn_sock->sock->sk;
>> + struct sock *sk = vxlan->vn_sock->uts.sock->sk;
>> struct dst_entry *ndst;
>> struct flowi6 fl6;
>> u32 flags;
>> @@ -2204,8 +2148,8 @@ void vxlan_get_rx_port(struct net_device *dev)
>> spin_lock(&vn->sock_lock);
>> for (i = 0; i < PORT_HASH_SIZE; ++i) {
>> hlist_for_each_entry_rcu(vs, &vn->sock_list[i], hlist) {
>> - port = inet_sk(vs->sock->sk)->inet_sport;
>> - sa_family = vs->sock->sk->sk_family;
>> + port = inet_sk(vs->uts.sock->sk)->inet_sport;
>> + sa_family = vs->uts.sock->sk->sk_family;
>> dev->netdev_ops->ndo_add_vxlan_port(dev,
>> sa_family,
>> port);
>> }
>> @@ -2335,79 +2279,60 @@ static const struct ethtool_ops vxlan_ethtool_ops
>> = {
>> static void vxlan_del_work(struct work_struct *work)
>> {
>> struct vxlan_sock *vs = container_of(work, struct vxlan_sock,
>> del_work);
>> -
>> - sk_release_kernel(vs->sock->sk);
>> + udp_tunnel_sock_release(&vs->uts);
>> kfree_rcu(vs, rcu);
>> }
>>
>> -static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
>> - __be16 port, u32 flags)
>> +/* Create new listen socket if needed */
>> +static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16
>> port,
>> + vxlan_rcv_t rcv, void *data,
>> + u32 flags)
>> {
>> - struct socket *sock;
>> - struct udp_port_cfg udp_conf;
>> - int err;
>> + struct vxlan_net *vn = net_generic(net, vxlan_net_id);
>> + struct vxlan_sock *vs;
>> + struct udp_tunnel_socket_cfg vxlan_ts_cfg;
>> + bool ipv6 = !!(flags & VXLAN_F_IPV6);
>> + unsigned int h;
>>
>> - memset(&udp_conf, 0, sizeof(udp_conf));
>> + memset(&vxlan_ts_cfg, 0, sizeof(struct udp_tunnel_socket_cfg));
>>
>> if (ipv6) {
>> - udp_conf.family = AF_INET6;
>> - udp_conf.use_udp6_tx_checksums =
>> + vxlan_ts_cfg.port.family = AF_INET6;
>> + vxlan_ts_cfg.port.use_udp6_tx_checksums =
>> !!(flags & VXLAN_F_UDP_ZERO_CSUM6_TX);
>> - udp_conf.use_udp6_rx_checksums =
>> + vxlan_ts_cfg.port.use_udp6_rx_checksums =
>> !!(flags & VXLAN_F_UDP_ZERO_CSUM6_RX);
>> } else {
>> - udp_conf.family = AF_INET;
>> - udp_conf.local_ip.s_addr = INADDR_ANY;
>> - udp_conf.use_udp_checksums =
>> + vxlan_ts_cfg.port.family = AF_INET;
>> + vxlan_ts_cfg.port.local_ip.s_addr = INADDR_ANY;
>> + vxlan_ts_cfg.port.use_udp_checksums =
>> !!(flags & VXLAN_F_UDP_CSUM);
>> }
>>
>> - udp_conf.local_udp_port = port;
>> + vxlan_ts_cfg.port.local_udp_port = port;
>> + vxlan_ts_cfg.encap_type = 1;
>> + vxlan_ts_cfg.encap_rcv = vxlan_udp_encap_recv;
>> + vxlan_ts_cfg.encap_destroy = NULL;
>>
>> - /* Open UDP socket */
>> - err = udp_sock_create(net, &udp_conf, &sock);
>> - if (err < 0)
>> - return ERR_PTR(err);
>> -
>> - /* Disable multicast loopback */
>> - inet_sk(sock->sk)->mc_loop = 0;
>> -
>> - return sock;
>> -}
>> -
>> -/* Create new listen socket if needed */
>> -static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16
>> port,
>> - vxlan_rcv_t *rcv, void
>> *data,
>> - u32 flags)
>> -{
>> - struct vxlan_net *vn = net_generic(net, vxlan_net_id);
>> - struct vxlan_sock *vs;
>> - struct socket *sock;
>> - struct sock *sk;
>> - unsigned int h;
>> - bool ipv6 = !!(flags & VXLAN_F_IPV6);
>> -
>> - vs = kzalloc(sizeof(*vs), GFP_KERNEL);
>> + vs = (struct vxlan_sock *)create_udp_tunnel_sock(net, sizeof(*vs),
>> + NULL,
>> + &vxlan_ts_cfg);
>> if (!vs)
>> return ERR_PTR(-ENOMEM);
>>
>> for (h = 0; h < VNI_HASH_SIZE; ++h)
>> INIT_HLIST_HEAD(&vs->vni_list[h]);
>>
>> - INIT_WORK(&vs->del_work, vxlan_del_work);
>> + spin_lock(&vn->sock_lock);
>> + list_add(&vs->next, &vn->vxlan_list);
>> + spin_unlock(&vn->sock_lock);
>>
>> - sock = vxlan_create_sock(net, ipv6, port, flags);
>> - if (IS_ERR(sock)) {
>> - kfree(vs);
>> - return ERR_CAST(sock);
>> - }
>> + INIT_WORK(&vs->del_work, vxlan_del_work);
>>
>> - vs->sock = sock;
>> - sk = sock->sk;
>> atomic_set(&vs->refcnt, 1);
>> +
>> vs->rcv = rcv;
>> - vs->data = data;
>> - rcu_assign_sk_user_data(vs->sock->sk, vs);
>> + vs->rcv_data = data;
>>
>> /* Initialize the vxlan udp offloads structure */
>> vs->udp_offloads.port = port;
>> @@ -2419,24 +2344,13 @@ static struct vxlan_sock
>> *vxlan_socket_create(struct net *net, __be16 port,
>> vxlan_notify_add_rx_port(vs);
>> spin_unlock(&vn->sock_lock);
>>
>> - /* Mark socket as an encapsulation socket. */
>> - udp_sk(sk)->encap_type = 1;
>> - udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv;
>> -#if IS_ENABLED(CONFIG_IPV6)
>> - if (ipv6)
>> - ipv6_stub->udpv6_encap_enable();
>> - else
>> -#endif
>> - udp_encap_enable();
>> -
>> return vs;
>> }
>>
>> struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
>> - vxlan_rcv_t *rcv, void *data,
>> + vxlan_rcv_t rcv, void *data,
>> bool no_share, u32 flags)
>> {
>> - struct vxlan_net *vn = net_generic(net, vxlan_net_id);
>> struct vxlan_sock *vs;
>>
>> vs = vxlan_socket_create(net, port, rcv, data, flags);
>> @@ -2446,7 +2360,6 @@ struct vxlan_sock *vxlan_sock_add(struct net *net,
>> __be16 port,
>> if (no_share) /* Return error if sharing is not allowed. */
>> return vs;
>>
>> - spin_lock(&vn->sock_lock);
>> vs = vxlan_find_sock(net, port);
>> if (vs) {
>> if (vs->rcv == rcv)
>> @@ -2454,7 +2367,6 @@ struct vxlan_sock *vxlan_sock_add(struct net *net,
>> __be16 port,
>> else
>> vs = ERR_PTR(-EBUSY);
>> }
>> - spin_unlock(&vn->sock_lock);
>>
>> if (!vs)
>> vs = ERR_PTR(-EINVAL);
>> @@ -2634,7 +2546,6 @@ static int vxlan_newlink(struct net *net, struct
>> net_device *dev,
>> }
>>
>> list_add(&vxlan->next, &vn->vxlan_list);
>> -
>> return 0;
>> }
>>
>> diff --git a/include/net/vxlan.h b/include/net/vxlan.h
>> index d5f59f3..10bfc13 100644
>> --- a/include/net/vxlan.h
>> +++ b/include/net/vxlan.h
>> @@ -4,23 +4,27 @@
>> #include <linux/skbuff.h>
>> #include <linux/netdevice.h>
>> #include <linux/udp.h>
>> +#include <net/udp_tunnel.h>
>>
>> #define VNI_HASH_BITS 10
>> #define VNI_HASH_SIZE (1<<VNI_HASH_BITS)
>>
>> struct vxlan_sock;
>> -typedef void (vxlan_rcv_t)(struct vxlan_sock *vh, struct sk_buff *skb,
>> __be32 key);
>>
>> -/* per UDP socket information */
>> +typedef void (*vxlan_rcv_t)(struct vxlan_sock *vs, struct sk_buff *skb,
>> + __be32 key);
>> +
>> +/* per vxlan socket information */
>> struct vxlan_sock {
>> + struct udp_tunnel_sock uts; /* Must be the first member */
>> struct hlist_node hlist;
>> - vxlan_rcv_t *rcv;
>> - void *data;
>> + struct list_head next;
>> struct work_struct del_work;
>> - struct socket *sock;
>> struct rcu_head rcu;
>> struct hlist_head vni_list[VNI_HASH_SIZE];
>> atomic_t refcnt;
>> + vxlan_rcv_t rcv;
>> + void *rcv_data;
>> struct udp_offload udp_offloads;
>> };
>>
>> @@ -35,7 +39,7 @@ struct vxlan_sock {
>> #define VXLAN_F_UDP_ZERO_CSUM6_RX 0x100
>>
>> struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
>> - vxlan_rcv_t *rcv, void *data,
>> + vxlan_rcv_t rcv, void *data,
>> bool no_share, u32 flags);
>>
>> void vxlan_sock_release(struct vxlan_sock *vs);
>> diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c
>> index d8b7e24..7599efd 100644
>> --- a/net/openvswitch/vport-vxlan.c
>> +++ b/net/openvswitch/vport-vxlan.c
>> @@ -59,7 +59,7 @@ static inline struct vxlan_port *vxlan_vport(const
>> struct vport *vport)
>> static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, __be32
>> vx_vni)
>> {
>> struct ovs_key_ipv4_tunnel tun_key;
>> - struct vport *vport = vs->data;
>> + struct vport *vport = vs->rcv_data;
>> struct iphdr *iph;
>> __be64 key;
>>
>> @@ -74,7 +74,7 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct
>> sk_buff *skb, __be32 vx_vni)
>> static int vxlan_get_options(const struct vport *vport, struct sk_buff
>> *skb)
>> {
>> struct vxlan_port *vxlan_port = vxlan_vport(vport);
>> - __be16 dst_port = inet_sk(vxlan_port->vs->sock->sk)->inet_sport;
>> + __be16 dst_port =
>> inet_sk(vxlan_port->vs->uts.sock->sk)->inet_sport;
>>
>> if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(dst_port)))
>> return -EMSGSIZE;
>> @@ -139,7 +139,7 @@ static int vxlan_tnl_send(struct vport *vport, struct
>> sk_buff *skb)
>> {
>> struct net *net = ovs_dp_get_net(vport->dp);
>> struct vxlan_port *vxlan_port = vxlan_vport(vport);
>> - __be16 dst_port = inet_sk(vxlan_port->vs->sock->sk)->inet_sport;
>> + __be16 dst_port =
>> inet_sk(vxlan_port->vs->uts.sock->sk)->inet_sport;
>> struct rtable *rt;
>> struct flowi4 fl;
>> __be16 src_port;
>> --
>> 1.7.9.5
>>
>> --
>> 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: [net-next v3 1/3] udp: Expand UDP tunnel common APIs
From: Andy Zhou @ 2014-08-27 5:17 UTC (permalink / raw)
To: Tom Herbert; +Cc: David Miller, Linux Netdev List
In-Reply-To: <CA+mtBx-XKH7wYq4hsGLTn63i06JefQjaR3TfnhH2PyB2DM0R=w@mail.gmail.com>
On Tue, Aug 26, 2014 at 9:12 PM, Tom Herbert <therbert@google.com> wrote:
> On Tue, Aug 26, 2014 at 8:35 PM, Andy Zhou <azhou@nicira.com> wrote:
>> Added create_udp_tunnel_socket(), packet receive and transmit, and
>> other related common functions for UDP tunnels.
>>
>> Per net open UDP tunnel ports are tracked in this common layer to
>> prevent sharing of a single port with more than one UDP tunnel.
>>
> This is not needed! If a UDP port is already bound (whether by another
> tunnel or not), then bind during tunnel initialization will fail. All
> this logic to store tunnel sockets in a separate list seems like
> unnecessary complexity. If a driver needs to track multiple ports it
> opens, it can do that on its own like VXLAN is already doing.
I was hoping to retain the udp_tunnel_find_sock() function which seems
to be common.
But it is not doing much at the moment, so I can just drop it in the
next version.
^ permalink raw reply
* Re: [net-next v3 1/3] udp: Expand UDP tunnel common APIs
From: Andy Zhou @ 2014-08-27 5:01 UTC (permalink / raw)
To: Eric Dumazet; +Cc: David Miller, netdev@vger.kernel.org
In-Reply-To: <1409111647.3173.22.camel@edumazet-glaptop2.roam.corp.google.com>
On Tue, Aug 26, 2014 at 8:54 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> On Tue, 2014-08-26 at 20:35 -0700, Andy Zhou wrote:
>> Added create_udp_tunnel_socket(), packet receive and transmit, and
>> other related common functions for UDP tunnels.
>>
>> Per net open UDP tunnel ports are tracked in this common layer to
>> prevent sharing of a single port with more than one UDP tunnel.
>>
>> Signed-off-by: Andy Zhou <azhou@nicira.com>
>> ---
>
> I see some RCU code, but I do not see elementary rules of RCU being
> respected in this patch.
Which RCU rule are you referring to?
>
> Changelogs are a bit terse, and there is not a single comment in this
> code.
I can add more content and comments in the next patch.
> What is going on exactly ?
>
I am trying to refactor some UDP tunnel code so that we don't end up
duplicate a lot of code when adding new UDP based tunnel protocols.
^ permalink raw reply
* Re: [PATCH 1/1] add selftest for virtio-net
From: Jason Wang @ 2014-08-27 4:57 UTC (permalink / raw)
To: Hengjinxiao, virtualization, netdev, linux-kernel, linux-api; +Cc: famz, mst
In-Reply-To: <1409103921-2879-1-git-send-email-hjxiaohust@gmail.com>
On 08/27/2014 09:45 AM, Hengjinxiao wrote:
> Selftest is an important part of network driver, this patch adds selftest for
> virtio-net, including loopback test, negotiate test and reset test. Loopback
> test checks whether virtio-net can send and receive packets normally. Negotiate test
> executes feature negotiation between virtio-net driver in Guest OS and virtio-net
> device in Host OS. Reset test resets virtio-net.
Thanks for the patch. Feature negotiation part brings some complicity
and need more through. And this could be extended for CVE regression in
the future. And you probably also need to send a patch of virtio spec to
implement the loop back mode.
See comments inline.
>
> Signed-off-by: Hengjinxiao <hjxiaohust@gmail.com>
>
> ---
> drivers/net/virtio_net.c | 233 +++++++++++++++++++++++++++++++++++++++-
> include/uapi/linux/virtio_net.h | 9 ++
> 2 files changed, 241 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 59caa06..f83f6e4 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -28,6 +28,7 @@
> #include <linux/cpu.h>
> #include <linux/average.h>
> #include <net/busy_poll.h>
> +#include <linux/pci.h>
>
> static int napi_weight = NAPI_POLL_WEIGHT;
> module_param(napi_weight, int, 0444);
> @@ -51,6 +52,23 @@ module_param(gso, bool, 0444);
> #define MERGEABLE_BUFFER_ALIGN max(L1_CACHE_BYTES, 256)
>
> #define VIRTNET_DRIVER_VERSION "1.0.0"
> +#define __VIRTNET_TESTING 0
> +
Why need this marco?
> +enum {
> + VIRTNET_LOOPBACK_TEST,
> + VIRTNET_FEATURE_NEG_TEST,
> + VIRTNET_RESET_TEST,
> +};
> +
> +static const struct {
> + const char string[ETH_GSTRING_LEN];
> +} virtnet_gstrings_test[] = {
> + [VIRTNET_LOOPBACK_TEST] = { "loopback test (offline)" },
> + [VIRTNET_FEATURE_NEG_TEST] = { "negotiate test (offline)" },
> + [VIRTNET_RESET_TEST] = { "reset test (offline)" },
> +};
> +
> +#define VIRTNET_NUM_TEST ARRAY_SIZE(virtnet_gstrings_test)
>
> struct virtnet_stats {
> struct u64_stats_sync tx_syncp;
> @@ -104,6 +122,8 @@ struct virtnet_info {
> struct send_queue *sq;
> struct receive_queue *rq;
> unsigned int status;
> + unsigned long flags;
> + atomic_t lb_count;
>
> /* Max # of queue pairs supported by the device */
> u16 max_queue_pairs;
> @@ -436,6 +456,19 @@ err_buf:
> return NULL;
> }
>
> +void virtnet_check_lb_frame(struct virtnet_info *vi,
> + struct sk_buff *skb)
> +{
> + unsigned int frame_size = skb->len;
> +
> + if (*(skb->data + 3) == 0xFF) {
> + if ((*(skb->data + frame_size / 2 + 10) == 0xBE) &&
> + (*(skb->data + frame_size / 2 + 12) == 0xAF)) {
> + atomic_dec(&vi->lb_count);
> + }
> + }
> +}
> +
> static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
> {
> struct virtnet_info *vi = rq->vq->vdev->priv;
> @@ -485,7 +518,12 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
> } else if (hdr->hdr.flags & VIRTIO_NET_HDR_F_DATA_VALID) {
> skb->ip_summed = CHECKSUM_UNNECESSARY;
> }
> -
> + /* loopback self test for ethtool */
> + if (test_bit(__VIRTNET_TESTING, &vi->flags)) {
> + virtnet_check_lb_frame(vi, skb);
> + dev_kfree_skb_any(skb);
> + return;
> + }
Not sure it's a good choice for adding such in fast path. We may need a
test specific rx interrupt handler (and disable NAPI) for this.
> skb->protocol = eth_type_trans(skb, dev);
> pr_debug("Receiving skb proto 0x%04x len %i type %i\n",
> ntohs(skb->protocol), skb->len, skb->pkt_type);
> @@ -813,6 +851,9 @@ static int virtnet_open(struct net_device *dev)
> {
> struct virtnet_info *vi = netdev_priv(dev);
> int i;
> + /* disallow open during test */
> + if (test_bit(__VIRTNET_TESTING, &vi->flags))
> + return -EBUSY;
>
> for (i = 0; i < vi->max_queue_pairs; i++) {
> if (i < vi->curr_queue_pairs)
> @@ -1363,12 +1404,158 @@ static void virtnet_get_channels(struct net_device *dev,
> channels->other_count = 0;
> }
>
> +static int virtnet_reset(struct virtnet_info *vi);
> +
> +static void virtnet_create_lb_frame(struct sk_buff *skb,
> + unsigned int frame_size)
> +{
> + memset(skb->data, 0xFF, frame_size);
> + frame_size &= ~1;
> + memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1);
> + memset(&skb->data[frame_size / 2 + 10], 0xBE, 1);
> + memset(&skb->data[frame_size / 2 + 12], 0xAF, 1);
> +}
> +
> +static int virtnet_start_loopback(struct virtnet_info *vi)
> +{
> + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_LOOPBACK,
> + VIRTIO_NET_CTRL_LOOPBACK_SET, NULL, NULL)) {
> + dev_warn(&vi->dev->dev, "Failed to set loopback.\n");
> + return -EINVAL;
> + }
> + return 0;
You may need to test the feature bit of loop back first and report the
card does not support loop back in some way.
> +}
> +
> +static int virtnet_run_loopback_test(struct virtnet_info *vi)
> +{
> + int i;
> + netdev_tx_t rc;
> + struct sk_buff *skb;
> + unsigned int size = GOOD_COPY_LEN;
> +
> + for (i = 0; i < 100; i++) {
> + skb = netdev_alloc_skb(vi->dev, size);
> + if (!skb)
> + return -ENOMEM;
> +
> + skb->queue_mapping = 0;
> + skb_put(skb, size);
> + virtnet_create_lb_frame(skb, size);
> + rc = start_xmit(skb, vi->dev);
virtio_net does not use tx interrupt to free old xmit skbs. It poll tx
completion only during xmit_skb(). So at least the last skb is leaked
since it was not freed. A possible solution is using tx interrupt here.
> + if (rc != NETDEV_TX_OK)
> + return -EPIPE;
It looks to me that start_xmit() never return other value than NETDEV_TX_OK.
> + atomic_inc(&vi->lb_count);
> + }
> + /* Give queue time to settle before testing results. */
> + msleep(20);
Need to make sure this value is also ok for qemu. ixgbe use 200 to 64
packets.
> + return atomic_read(&vi->lb_count) ? -EIO : 0;
> +}
> +
> +static int virtnet_stop_loopback(struct virtnet_info *vi)
> +{
> + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_LOOPBACK,
> + VIRTIO_NET_CTRL_LOOPBACK_UNSET, NULL, NULL)) {
> + dev_warn(&vi->dev->dev, "Failed to unset loopback.\n");
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +static int virtnet_loopback_test(struct virtnet_info *vi, u64 *data)
> +{
> + *data = virtnet_start_loopback(vi);
> + if (*data)
> + goto out;
> + *data = virtnet_run_loopback_test(vi);
> + if (*data)
Do we need to stop loopback here?
> + goto out;
> + *data = virtnet_stop_loopback(vi);
> +out:
> + return *data;
> +}
> +
> +static void virtnet_feature_neg_test(struct virtnet_info *vi)
> +{
> + struct virtio_device *dev = vi->vdev;
> + struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
> + int i;
> + u32 device_features;
> +
> + /* Figure out what features the device supports. */
> + device_features = dev->config->get_features(dev);
> +
> + /* Features supported by both device and driver into dev->features. */
> + memset(dev->features, 0, sizeof(dev->features));
> + for (i = 0; i < drv->feature_table_size; i++) {
> + unsigned int f = drv->feature_table[i];
> +
> + BUG_ON(f >= 32);
> + if (device_features & (1 << f))
> + set_bit(f, dev->features);
> + }
> +
> + /* Transport features always preserved to pass to finalize_features. */
> + for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
> + if (device_features & (1 << i))
> + set_bit(i, dev->features);
> +
> + dev->config->finalize_features(dev);
> +}
A problem of the function is it may be called during DRIVER_OK, not sure
this is ok since spec suggest to do the feature negotiation after DRIVER
bit but before DRIVER_OK bit. And this function duplicates some of the
code from virtio core, may consider a method to share between them.
Another issue is the test never fail which needs more thought.
> +
> +static int virtnet_get_sset_count(struct net_device *netdev, int sset)
> +{
> + switch (sset) {
> + case ETH_SS_TEST:
> + return VIRTNET_NUM_TEST;
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static void virtnet_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
> +{
> + switch (stringset) {
> + case ETH_SS_TEST:
> + memcpy(buf, &virtnet_gstrings_test,
> + sizeof(virtnet_gstrings_test));
> + break;
> + default:
> + break;
> + }
> +}
> +
> +static void virtnet_self_test(struct net_device *netdev,
> + struct ethtool_test *eth_test, u64 *data)
> +{
> + struct virtnet_info *vi = netdev_priv(netdev);
> + bool if_running = netif_running(netdev);
> +
> + set_bit(__VIRTNET_TESTING, &vi->flags);
> + memset(data, 0, sizeof(u64) * VIRTNET_NUM_TEST);
> +
> + if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
> + if (!if_running) {
> + dev_warn(&vi->dev->dev, "Failed to execute self test.\n");
> + eth_test->flags |= ETH_TEST_FL_FAILED;
> + return;
> + }
> + if (virtnet_loopback_test(vi, &data[VIRTNET_LOOPBACK_TEST]))
> + eth_test->flags |= ETH_TEST_FL_FAILED;
> + virtnet_feature_neg_test(vi);
> + virtnet_reset(vi);
> + }
> + clear_bit(__VIRTNET_TESTING, &vi->flags);
> +}
> +
> static const struct ethtool_ops virtnet_ethtool_ops = {
> .get_drvinfo = virtnet_get_drvinfo,
> .get_link = ethtool_op_get_link,
> .get_ringparam = virtnet_get_ringparam,
> .set_channels = virtnet_set_channels,
> .get_channels = virtnet_get_channels,
> + .self_test = virtnet_self_test,
> + .get_strings = virtnet_get_strings,
> + .get_sset_count = virtnet_get_sset_count,
> };
>
> #define MIN_MTU 68
> @@ -1957,6 +2144,50 @@ static int virtnet_restore(struct virtio_device *vdev)
> }
> #endif
>
> +static int virtnet_reset(struct virtnet_info *vi)
If this is needed, better split this into anther patch.
> +{
> + struct virtio_device *vdev = vi->vdev;
> + int err, i;
> + u8 status;
> +
> + mutex_lock(&vi->config_lock);
> + vi->config_enable = false;
> + mutex_unlock(&vi->config_lock);
> +
> + cancel_delayed_work_sync(&vi->refill);
> +
> + if (netif_running(vi->dev))
> + for (i = 0; i < vi->max_queue_pairs; i++) {
> + napi_disable(&vi->rq[i].napi);
> + netif_napi_del(&vi->rq[i].napi);
> + }
> +
> + remove_vq_common(vi);
> + flush_work(&vi->config_work);
> +
> + virtnet_feature_neg_test(vi);
Is this used for feature negotiation? If yes, need a better name and can
we reuse virtio core function to do this?
> + err = init_vqs(vi);
> + if (err)
> + return err;
> + if (netif_running(vi->dev)) {
> + for (i = 0; i < vi->curr_queue_pairs; i++)
> + if (!try_fill_recv(&vi->rq[i], GFP_KERNEL))
> + schedule_delayed_work(&vi->refill, 0);
> +
> + for (i = 0; i < vi->max_queue_pairs; i++)
> + virtnet_napi_enable(&vi->rq[i]);
> + }
> +
> + mutex_lock(&vi->config_lock);
> + vi->config_enable = true;
> + mutex_unlock(&vi->config_lock);
> +
> + virtnet_set_queues(vi, vi->curr_queue_pairs);
> + status = vdev->config->get_status(vdev);
> + vdev->config->set_status(vdev, status | VIRTIO_CONFIG_S_DRIVER_OK);
> + return 0;
> +}
> +
> static struct virtio_device_id id_table[] = {
> { VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID },
> { 0 },
> diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h
> index 172a7f0..1f31f90 100644
> --- a/include/uapi/linux/virtio_net.h
> +++ b/include/uapi/linux/virtio_net.h
> @@ -201,4 +201,13 @@ struct virtio_net_ctrl_mq {
> #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1
> #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000
>
> + /*
> + * Control Loopback(5 is used by VIRTIO_NET_CTRL_GUEST_OFFLOADS in latest qemu)
> + *
> + * The command VIRTIO_NET_CTRL_LOOPBACK_SET is used to require the device come
> + * into loopback state.
> + */
> +#define VIRTIO_NET_CTRL_LOOPBACK 6
> + #define VIRTIO_NET_CTRL_LOOPBACK_SET 0
> + #define VIRTIO_NET_CTRL_LOOPBACK_UNSET 1
> #endif /* _LINUX_VIRTIO_NET_H */
^ permalink raw reply
* Re: [PATCH RFC v7 net-next 00/28] BPF syscall
From: Alexei Starovoitov @ 2014-08-27 4:57 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Daniel Borkmann, H. Peter Anvin, Andrew Morton, Chema Gonzalez,
Namhyung Kim, Eric Dumazet, David S. Miller,
linux-kernel@vger.kernel.org, Brendan Gregg, Linus Torvalds,
Steven Rostedt, Network Development, Peter Zijlstra, Kees Cook,
Linux API, Ingo Molnar
In-Reply-To: <CALCETrWs9GRufGKxAoV628bh1f3HObkxNQuW3tYRt2rFwNAbxA@mail.gmail.com>
On Tue, Aug 26, 2014 at 9:49 PM, Andy Lutomirski <luto@amacapital.net> wrote:
> On Tue, Aug 26, 2014 at 9:35 PM, Alexei Starovoitov <ast@plumgrid.com> wrote:
>> On Tue, Aug 26, 2014 at 8:56 PM, Andy Lutomirski <luto@amacapital.net> wrote:
>>> On Aug 26, 2014 7:29 PM, "Alexei Starovoitov" <ast@plumgrid.com> wrote:
>>>>
>>>> Hi Ingo, David,
>>>>
>>>> posting whole thing again as RFC to get feedback on syscall only.
>>>> If syscall bpf(int cmd, union bpf_attr *attr, unsigned int size) is ok,
>>>> I'll split them into small chunks as requested and will repost without RFC.
>>>
>>> IMO it's much easier to review a syscall if we just look at a
>>> specification of what it does. The code is, in some sense, secondary.
>>
>> 'specification of what it does'... hmm, you mean beyond what's
>> there in commit logs and in Documentation/networking/filter.txt ?
>> Aren't samples at the end give an idea on 'what it does'?
>> I'm happy to add 'specification', I just don't understand yet what
>> it suppose to talk about beyond what's already written.
>> I understand that the patches are missing explanation on 'why'
>> the syscall is being added, but I don't think it's what you're asking...
>
> I mean a hopefully short document that defines what the syscall does.
> It should be precise enough that one could, in principle, implement
> the syscall just by reading the document and that one could use the
> syscall just by reading the document.
>
> Given that there's a whole instruction set to go with it, it may end
> up being moderately complicated or saying things like "see this other
> thing for a description of the instruction set" and "there are some
> extensible sets of functions you can call with it".
I'm still lost.
Here is the quote from Documentation/networking/filter.txt
"
'maps' is a generic storage of different types for sharing data between kernel
and userspace.
The maps are accessed from user space via BPF syscall,
which has commands:
- create a map with given type and attributes
map_fd = bpf(BPF_MAP_CREATE, union bpf_attr *attr, u32 size)
using attr->map_type, attr->key_size, attr->value_size, attr->max_entries
returns process-local file descriptor or negative error
- lookup key in a given map
err = bpf(BPF_MAP_LOOKUP_ELEM, union bpf_attr *attr, u32 size)
using attr->map_fd, attr->key, attr->value
returns zero and stores found elem into value or negative error
- create or update key/value pair in a given map
err = bpf(BPF_MAP_UPDATE_ELEM, union bpf_attr *attr, u32 size)
using attr->map_fd, attr->key, attr->value
returns zero or negative error
- find and delete element by key in a given map
err = bpf(BPF_MAP_DELETE_ELEM, union bpf_attr *attr, u32 size)
using attr->map_fd, attr->key
- to delete map: close(fd)
Exiting process will delete maps automatically
userspace programs uses this API to create/populate/read
maps that eBPF programs are concurrently updating.
"
and more in commit log:
"
- load eBPF program
fd = bpf(BPF_PROG_LOAD, union bpf_attr *attr, u32 size)
where 'attr' is
struct {
enum bpf_prog_type prog_type;
__u32 insn_cnt;
struct bpf_insn __user *insns;
const char __user *license;
};
insns - array of eBPF instructions
license - must be GPL compatible to call helper functions marked gpl_only
- unload eBPF program
close(fd)
"
Isn't it short and describes what it does?
Do you want me to describe what eBPF program can do?
^ permalink raw reply
* Re: IPv6 Policy based routing not working.
From: Tushar Shinde @ 2014-08-27 4:54 UTC (permalink / raw)
To: Hannes Frederic Sowa; +Cc: netdev
In-Reply-To: <1409090482.1152.8.camel@localhost>
Hi Hannes,
Thank you for reply.
I tested it will 2 kernels 2.6.32-431.11.2.el6.x86_64, and ubuntu
14.04 kernel 3.13.
ping6 -I <IPv6 src ip> <dest> is working. I also verified ssh with -b working.
So my question is, is it necessary to give bind address to make source
based routing work? why don't it select ip address of given bind
interface? Because of this SO_BINDTODEVICE may not work as expected.
Tushar
On Wed, Aug 27, 2014 at 3:31 AM, Hannes Frederic Sowa
<hannes@stressinduktion.org> wrote:
> Hi,
>
> On Di, 2014-08-26 at 21:32 +0530, Tushar Shinde wrote:
>> Hello NetDev,
>>
>> I am facing problem where if ipv6 route (outside to current subnet) is
>> reachable only if route is present in main table. Policy based routes
>> are not working.
>>
>> Following is my setup,
>>
>> eth0 inet6 2001:1::10/120 scope global
>> eth1 inet6 2001:1::11/120 scope global
>> 2001:1::1 is gateway
>> 2.6.32-431.11.2.el6.x86_64 kernel (I tried on 3.x also)
>>
>> [root@ipv6node1 ~]# ip -6 route show tab 1
>> 2001:1::/120 dev eth0 metric 1024 mtu 1500 advmss 1440 hoplimit 4294967295
>> default via 2001:1::1 dev eth0 metric 1024 mtu 1500 advmss 1440
>> hoplimit 4294967295
>> [root@ipv6node1 ~]#
>> [root@ipv6node1 ~]# ip -6 route show tab 2
>> 2001:1::/120 dev eth1 metric 1024 mtu 1500 advmss 1440 hoplimit 4294967295
>> default via 2001:1::1 dev eth1 metric 1024 mtu 1500 advmss 1440
>> hoplimit 4294967295
>> [root@ipv6node1 ~]#
>> [root@ipv6node1 ~]# ip -6 rule show
>> 0: from all lookup local
>> 16383: from 2001:1::10 lookup 1
>> 16383: from 2001:1::11 lookup 2
>> 32766: from all lookup main
>> [root@ipv6node1 ~]#
>>
>> If I add default vai eth0 only ip of eth0 is reachable from outside or
>> eth1 dont work. In above setup default route is not present.
>>
>> But default entry in "table" is never getting used to resolve route
>>
>> [root@ipv6node1 ~]# ping6 -I eth0 2001:2::20
>> connect: Network is unreachable
>> [root@ipv6node1 ~]#
>> [root@ipv6node1 ~]# ping6 -I eth1 2001:2::20
>> connect: Network is unreachable
>> [root@ipv6node1 ~]#
>
> ping6 selects the ipv6 address based on netlink route lookup given the
> specified interface but without setting the source address. That said,
> it is probable that the rule lookup happens with a completely different
> ipv6 address. Please verify this. ping6 -I also accepts a source ipv6
> address, can you try this?
>
> Otherwise please specify with which 3.x kernel you tested this.
>
> Thanks,
> Hannes
>
>
^ permalink raw reply
* Re: [PATCH RFC v7 net-next 00/28] BPF syscall
From: Andy Lutomirski @ 2014-08-27 4:49 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Daniel Borkmann, H. Peter Anvin, Andrew Morton, Chema Gonzalez,
Namhyung Kim, Eric Dumazet, David S. Miller,
linux-kernel@vger.kernel.org, Brendan Gregg, Linus Torvalds,
Steven Rostedt, Network Development, Peter Zijlstra, Kees Cook,
Linux API, Ingo Molnar
In-Reply-To: <CAMEtUuwYqJVUuZxPBKy57NonWF+X_5vkNx4uSEx_d=-xBka1Kg@mail.gmail.com>
On Tue, Aug 26, 2014 at 9:35 PM, Alexei Starovoitov <ast@plumgrid.com> wrote:
> On Tue, Aug 26, 2014 at 8:56 PM, Andy Lutomirski <luto@amacapital.net> wrote:
>> On Aug 26, 2014 7:29 PM, "Alexei Starovoitov" <ast@plumgrid.com> wrote:
>>>
>>> Hi Ingo, David,
>>>
>>> posting whole thing again as RFC to get feedback on syscall only.
>>> If syscall bpf(int cmd, union bpf_attr *attr, unsigned int size) is ok,
>>> I'll split them into small chunks as requested and will repost without RFC.
>>
>> IMO it's much easier to review a syscall if we just look at a
>> specification of what it does. The code is, in some sense, secondary.
>
> 'specification of what it does'... hmm, you mean beyond what's
> there in commit logs and in Documentation/networking/filter.txt ?
> Aren't samples at the end give an idea on 'what it does'?
> I'm happy to add 'specification', I just don't understand yet what
> it suppose to talk about beyond what's already written.
> I understand that the patches are missing explanation on 'why'
> the syscall is being added, but I don't think it's what you're asking...
I mean a hopefully short document that defines what the syscall does.
It should be precise enough that one could, in principle, implement
the syscall just by reading the document and that one could use the
syscall just by reading the document.
Given that there's a whole instruction set to go with it, it may end
up being moderately complicated or saying things like "see this other
thing for a description of the instruction set" and "there are some
extensible sets of functions you can call with it".
--Andy
--
Andy Lutomirski
AMA Capital Management, LLC
^ permalink raw reply
* Re: [PATCH RFC v7 net-next 00/28] BPF syscall
From: Alexei Starovoitov @ 2014-08-27 4:35 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Daniel Borkmann, H. Peter Anvin, Andrew Morton, Chema Gonzalez,
Namhyung Kim, Eric Dumazet, David S. Miller,
linux-kernel@vger.kernel.org, Brendan Gregg, Linus Torvalds,
Steven Rostedt, Network Development, Peter Zijlstra, Kees Cook,
Linux API, Ingo Molnar
In-Reply-To: <CALCETrX2MzNxQ1ogt+Qx8iPjUe+z0gN6ZLwu9nAcZzTN+D+iYw@mail.gmail.com>
On Tue, Aug 26, 2014 at 8:56 PM, Andy Lutomirski <luto@amacapital.net> wrote:
> On Aug 26, 2014 7:29 PM, "Alexei Starovoitov" <ast@plumgrid.com> wrote:
>>
>> Hi Ingo, David,
>>
>> posting whole thing again as RFC to get feedback on syscall only.
>> If syscall bpf(int cmd, union bpf_attr *attr, unsigned int size) is ok,
>> I'll split them into small chunks as requested and will repost without RFC.
>
> IMO it's much easier to review a syscall if we just look at a
> specification of what it does. The code is, in some sense, secondary.
'specification of what it does'... hmm, you mean beyond what's
there in commit logs and in Documentation/networking/filter.txt ?
Aren't samples at the end give an idea on 'what it does'?
I'm happy to add 'specification', I just don't understand yet what
it suppose to talk about beyond what's already written.
I understand that the patches are missing explanation on 'why'
the syscall is being added, but I don't think it's what you're asking...
^ permalink raw reply
* [PATCHv2 14/14] arm64: eBPF JIT compiler
From: Zi Shen Lim @ 2014-08-27 4:15 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon
Cc: Zi Shen Lim, David S. Miller, Daniel Borkmann, Alexei Starovoitov,
Chema Gonzalez, linux-kernel, linux-arm-kernel, netdev
In-Reply-To: <1409112930-25677-1-git-send-email-zlim.lnx@gmail.com>
The JIT compiler emits A64 instructions. It supports eBPF only.
Legacy BPF is supported thanks to conversion by BPF core.
JIT is enabled in the same way as for other architectures:
echo 1 > /proc/sys/net/core/bpf_jit_enable
Or for additional compiler output:
echo 2 > /proc/sys/net/core/bpf_jit_enable
See Documentation/networking/filter.txt for more information.
The implementation passes all 57 tests in lib/test_bpf.c
on ARMv8 Foundation Model :) Also tested by Will on Juno platform.
Signed-off-by: Zi Shen Lim <zlim.lnx@gmail.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Acked-by: Will Deacon <will.deacon@arm.com>
---
v1->v2:
Rebased onto 3.17-rc2, and fixed up changes related to:
- sock_filter_int -> bpf_insn: 2695fb552cbe (net: filter: rename
'struct sock_filter_int' into 'struct bpf_insn')
- sk_filter -> bpf_prog: 7ae457c1e5b4 (net: filter: split
'struct sk_filter' into socket and bpf parts)
RFCv3->v1:
Addressed review comments from Will wrt codegen bits:
- define and use {SF,N}_BIT
- use masks for limit checks
Also:
- rebase onto net-next
RFCv2->RFCv3:
- clarify 16B stack alignment requirement - I missed one reference
- fixed a couple checks for immediate bits
- make bpf_jit.h checkpatch clean
- remove stale DW case in LD_IND and LD_ABS (good catch by Alexei)
- add Alexei's Acked-by
- rebase onto net-next
Also, per discussion with Will, consolidated bpf_jit.h into
arch/arm64/.../insn.{c,h}:
- instruction encoding stuff moved into arch/arm64/kernel/insn.c
- bpf_jit.h uses arch/arm64/include/asm/insn.h
RFCv1->RFCv2:
Addressed review comments from Alexei:
- use core-$(CONFIG_NET)
- use GENMASK
- lower-case function names in header file
- drop LD_ABS+DW and LD_IND+DW, which do not exist in eBPF yet
- use pr_xxx_once() to prevent spamming logs
- clarify 16B stack alignment requirement
- drop usage of EMIT macro which was saving just one argument,
turns out having additional argument wasn't too much of an eyesore
Also, per discussion with Alexei, and additional suggestion from
Daniel:
- moved load_pointer() from net/core/filter.c into filter.h
as bpf_load_pointer()
which is done as a separate preparatory patch. [1]
[1] http://patchwork.ozlabs.org/patch/366906/
NOTES:
* The preparatory patch [1] has been merged into net-next
9f12fbe603f7 ("net: filter: move load_pointer() into filter.h").
* bpf_jit_comp.c and bpf_jit.h is checkpatch clean.
* The following sparse warning is not applicable:
warning: symbol 'bpf_jit_enable' was not declared. Should it be static?
FUTURE WORK:
1. Implement remaining classes of eBPF instructions: ST|MEM, STX|XADD
which currently do not have corresponding test cases in test_bpf.
2. Further compiler optimization, such as optimization for small
immediates.
Documentation/networking/filter.txt | 6 +-
arch/arm64/Kconfig | 1 +
arch/arm64/Makefile | 1 +
arch/arm64/net/Makefile | 4 +
arch/arm64/net/bpf_jit.h | 169 +++++++++
arch/arm64/net/bpf_jit_comp.c | 677 ++++++++++++++++++++++++++++++++++++
6 files changed, 855 insertions(+), 3 deletions(-)
create mode 100644 arch/arm64/net/Makefile
create mode 100644 arch/arm64/net/bpf_jit.h
create mode 100644 arch/arm64/net/bpf_jit_comp.c
diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt
index c48a970..1842d4f 100644
--- a/Documentation/networking/filter.txt
+++ b/Documentation/networking/filter.txt
@@ -462,9 +462,9 @@ JIT compiler
------------
The Linux kernel has a built-in BPF JIT compiler for x86_64, SPARC, PowerPC,
-ARM and s390 and can be enabled through CONFIG_BPF_JIT. The JIT compiler is
-transparently invoked for each attached filter from user space or for internal
-kernel users if it has been previously enabled by root:
+ARM, ARM64 and s390 and can be enabled through CONFIG_BPF_JIT. The JIT compiler
+is transparently invoked for each attached filter from user space or for
+internal kernel users if it has been previously enabled by root:
echo 1 > /proc/sys/net/core/bpf_jit_enable
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index fd4e81a..cfea623 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -35,6 +35,7 @@ config ARM64
select HAVE_ARCH_JUMP_LABEL
select HAVE_ARCH_KGDB
select HAVE_ARCH_TRACEHOOK
+ select HAVE_BPF_JIT
select HAVE_C_RECORDMCOUNT
select HAVE_CC_STACKPROTECTOR
select HAVE_DEBUG_BUGVERBOSE
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 2df5e5d..59c86b6 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -47,6 +47,7 @@ endif
export TEXT_OFFSET GZFLAGS
core-y += arch/arm64/kernel/ arch/arm64/mm/
+core-$(CONFIG_NET) += arch/arm64/net/
core-$(CONFIG_KVM) += arch/arm64/kvm/
core-$(CONFIG_XEN) += arch/arm64/xen/
core-$(CONFIG_CRYPTO) += arch/arm64/crypto/
diff --git a/arch/arm64/net/Makefile b/arch/arm64/net/Makefile
new file mode 100644
index 0000000..da97633
--- /dev/null
+++ b/arch/arm64/net/Makefile
@@ -0,0 +1,4 @@
+#
+# ARM64 networking code
+#
+obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o
diff --git a/arch/arm64/net/bpf_jit.h b/arch/arm64/net/bpf_jit.h
new file mode 100644
index 0000000..2134f7e
--- /dev/null
+++ b/arch/arm64/net/bpf_jit.h
@@ -0,0 +1,169 @@
+/*
+ * BPF JIT compiler for ARM64
+ *
+ * Copyright (C) 2014 Zi Shen Lim <zlim.lnx@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _BPF_JIT_H
+#define _BPF_JIT_H
+
+#include <asm/insn.h>
+
+/* 5-bit Register Operand */
+#define A64_R(x) AARCH64_INSN_REG_##x
+#define A64_FP AARCH64_INSN_REG_FP
+#define A64_LR AARCH64_INSN_REG_LR
+#define A64_ZR AARCH64_INSN_REG_ZR
+#define A64_SP AARCH64_INSN_REG_SP
+
+#define A64_VARIANT(sf) \
+ ((sf) ? AARCH64_INSN_VARIANT_64BIT : AARCH64_INSN_VARIANT_32BIT)
+
+/* Compare & branch (immediate) */
+#define A64_COMP_BRANCH(sf, Rt, offset, type) \
+ aarch64_insn_gen_comp_branch_imm(0, offset, Rt, A64_VARIANT(sf), \
+ AARCH64_INSN_BRANCH_COMP_##type)
+#define A64_CBZ(sf, Rt, imm19) A64_COMP_BRANCH(sf, Rt, (imm19) << 2, ZERO)
+
+/* Conditional branch (immediate) */
+#define A64_COND_BRANCH(cond, offset) \
+ aarch64_insn_gen_cond_branch_imm(0, offset, cond)
+#define A64_COND_EQ AARCH64_INSN_COND_EQ /* == */
+#define A64_COND_NE AARCH64_INSN_COND_NE /* != */
+#define A64_COND_CS AARCH64_INSN_COND_CS /* unsigned >= */
+#define A64_COND_HI AARCH64_INSN_COND_HI /* unsigned > */
+#define A64_COND_GE AARCH64_INSN_COND_GE /* signed >= */
+#define A64_COND_GT AARCH64_INSN_COND_GT /* signed > */
+#define A64_B_(cond, imm19) A64_COND_BRANCH(cond, (imm19) << 2)
+
+/* Unconditional branch (immediate) */
+#define A64_BRANCH(offset, type) aarch64_insn_gen_branch_imm(0, offset, \
+ AARCH64_INSN_BRANCH_##type)
+#define A64_B(imm26) A64_BRANCH((imm26) << 2, NOLINK)
+#define A64_BL(imm26) A64_BRANCH((imm26) << 2, LINK)
+
+/* Unconditional branch (register) */
+#define A64_BLR(Rn) aarch64_insn_gen_branch_reg(Rn, AARCH64_INSN_BRANCH_LINK)
+#define A64_RET(Rn) aarch64_insn_gen_branch_reg(Rn, AARCH64_INSN_BRANCH_RETURN)
+
+/* Load/store register (register offset) */
+#define A64_LS_REG(Rt, Rn, Rm, size, type) \
+ aarch64_insn_gen_load_store_reg(Rt, Rn, Rm, \
+ AARCH64_INSN_SIZE_##size, \
+ AARCH64_INSN_LDST_##type##_REG_OFFSET)
+#define A64_STRB(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 8, STORE)
+#define A64_LDRB(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 8, LOAD)
+#define A64_STRH(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 16, STORE)
+#define A64_LDRH(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 16, LOAD)
+#define A64_STR32(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 32, STORE)
+#define A64_LDR32(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 32, LOAD)
+#define A64_STR64(Xt, Xn, Xm) A64_LS_REG(Xt, Xn, Xm, 64, STORE)
+#define A64_LDR64(Xt, Xn, Xm) A64_LS_REG(Xt, Xn, Xm, 64, LOAD)
+
+/* Load/store register pair */
+#define A64_LS_PAIR(Rt, Rt2, Rn, offset, ls, type) \
+ aarch64_insn_gen_load_store_pair(Rt, Rt2, Rn, offset, \
+ AARCH64_INSN_VARIANT_64BIT, \
+ AARCH64_INSN_LDST_##ls##_PAIR_##type)
+/* Rn -= 16; Rn[0] = Rt; Rn[8] = Rt2; */
+#define A64_PUSH(Rt, Rt2, Rn) A64_LS_PAIR(Rt, Rt2, Rn, -16, STORE, PRE_INDEX)
+/* Rt = Rn[0]; Rt2 = Rn[8]; Rn += 16; */
+#define A64_POP(Rt, Rt2, Rn) A64_LS_PAIR(Rt, Rt2, Rn, 16, LOAD, POST_INDEX)
+
+/* Add/subtract (immediate) */
+#define A64_ADDSUB_IMM(sf, Rd, Rn, imm12, type) \
+ aarch64_insn_gen_add_sub_imm(Rd, Rn, imm12, \
+ A64_VARIANT(sf), AARCH64_INSN_ADSB_##type)
+/* Rd = Rn OP imm12 */
+#define A64_ADD_I(sf, Rd, Rn, imm12) A64_ADDSUB_IMM(sf, Rd, Rn, imm12, ADD)
+#define A64_SUB_I(sf, Rd, Rn, imm12) A64_ADDSUB_IMM(sf, Rd, Rn, imm12, SUB)
+/* Rd = Rn */
+#define A64_MOV(sf, Rd, Rn) A64_ADD_I(sf, Rd, Rn, 0)
+
+/* Bitfield move */
+#define A64_BITFIELD(sf, Rd, Rn, immr, imms, type) \
+ aarch64_insn_gen_bitfield(Rd, Rn, immr, imms, \
+ A64_VARIANT(sf), AARCH64_INSN_BITFIELD_MOVE_##type)
+/* Signed, with sign replication to left and zeros to right */
+#define A64_SBFM(sf, Rd, Rn, ir, is) A64_BITFIELD(sf, Rd, Rn, ir, is, SIGNED)
+/* Unsigned, with zeros to left and right */
+#define A64_UBFM(sf, Rd, Rn, ir, is) A64_BITFIELD(sf, Rd, Rn, ir, is, UNSIGNED)
+
+/* Rd = Rn << shift */
+#define A64_LSL(sf, Rd, Rn, shift) ({ \
+ int sz = (sf) ? 64 : 32; \
+ A64_UBFM(sf, Rd, Rn, (unsigned)-(shift) % sz, sz - 1 - (shift)); \
+})
+/* Rd = Rn >> shift */
+#define A64_LSR(sf, Rd, Rn, shift) A64_UBFM(sf, Rd, Rn, shift, (sf) ? 63 : 31)
+/* Rd = Rn >> shift; signed */
+#define A64_ASR(sf, Rd, Rn, shift) A64_SBFM(sf, Rd, Rn, shift, (sf) ? 63 : 31)
+
+/* Move wide (immediate) */
+#define A64_MOVEW(sf, Rd, imm16, shift, type) \
+ aarch64_insn_gen_movewide(Rd, imm16, shift, \
+ A64_VARIANT(sf), AARCH64_INSN_MOVEWIDE_##type)
+/* Rd = Zeros (for MOVZ);
+ * Rd |= imm16 << shift (where shift is {0, 16, 32, 48});
+ * Rd = ~Rd; (for MOVN); */
+#define A64_MOVN(sf, Rd, imm16, shift) A64_MOVEW(sf, Rd, imm16, shift, INVERSE)
+#define A64_MOVZ(sf, Rd, imm16, shift) A64_MOVEW(sf, Rd, imm16, shift, ZERO)
+#define A64_MOVK(sf, Rd, imm16, shift) A64_MOVEW(sf, Rd, imm16, shift, KEEP)
+
+/* Add/subtract (shifted register) */
+#define A64_ADDSUB_SREG(sf, Rd, Rn, Rm, type) \
+ aarch64_insn_gen_add_sub_shifted_reg(Rd, Rn, Rm, 0, \
+ A64_VARIANT(sf), AARCH64_INSN_ADSB_##type)
+/* Rd = Rn OP Rm */
+#define A64_ADD(sf, Rd, Rn, Rm) A64_ADDSUB_SREG(sf, Rd, Rn, Rm, ADD)
+#define A64_SUB(sf, Rd, Rn, Rm) A64_ADDSUB_SREG(sf, Rd, Rn, Rm, SUB)
+#define A64_SUBS(sf, Rd, Rn, Rm) A64_ADDSUB_SREG(sf, Rd, Rn, Rm, SUB_SETFLAGS)
+/* Rd = -Rm */
+#define A64_NEG(sf, Rd, Rm) A64_SUB(sf, Rd, A64_ZR, Rm)
+/* Rn - Rm; set condition flags */
+#define A64_CMP(sf, Rn, Rm) A64_SUBS(sf, A64_ZR, Rn, Rm)
+
+/* Data-processing (1 source) */
+#define A64_DATA1(sf, Rd, Rn, type) aarch64_insn_gen_data1(Rd, Rn, \
+ A64_VARIANT(sf), AARCH64_INSN_DATA1_##type)
+/* Rd = BSWAPx(Rn) */
+#define A64_REV16(sf, Rd, Rn) A64_DATA1(sf, Rd, Rn, REVERSE_16)
+#define A64_REV32(sf, Rd, Rn) A64_DATA1(sf, Rd, Rn, REVERSE_32)
+#define A64_REV64(Rd, Rn) A64_DATA1(1, Rd, Rn, REVERSE_64)
+
+/* Data-processing (2 source) */
+/* Rd = Rn OP Rm */
+#define A64_UDIV(sf, Rd, Rn, Rm) aarch64_insn_gen_data2(Rd, Rn, Rm, \
+ A64_VARIANT(sf), AARCH64_INSN_DATA2_UDIV)
+
+/* Data-processing (3 source) */
+/* Rd = Ra + Rn * Rm */
+#define A64_MADD(sf, Rd, Ra, Rn, Rm) aarch64_insn_gen_data3(Rd, Ra, Rn, Rm, \
+ A64_VARIANT(sf), AARCH64_INSN_DATA3_MADD)
+/* Rd = Rn * Rm */
+#define A64_MUL(sf, Rd, Rn, Rm) A64_MADD(sf, Rd, A64_ZR, Rn, Rm)
+
+/* Logical (shifted register) */
+#define A64_LOGIC_SREG(sf, Rd, Rn, Rm, type) \
+ aarch64_insn_gen_logical_shifted_reg(Rd, Rn, Rm, 0, \
+ A64_VARIANT(sf), AARCH64_INSN_LOGIC_##type)
+/* Rd = Rn OP Rm */
+#define A64_AND(sf, Rd, Rn, Rm) A64_LOGIC_SREG(sf, Rd, Rn, Rm, AND)
+#define A64_ORR(sf, Rd, Rn, Rm) A64_LOGIC_SREG(sf, Rd, Rn, Rm, ORR)
+#define A64_EOR(sf, Rd, Rn, Rm) A64_LOGIC_SREG(sf, Rd, Rn, Rm, EOR)
+#define A64_ANDS(sf, Rd, Rn, Rm) A64_LOGIC_SREG(sf, Rd, Rn, Rm, AND_SETFLAGS)
+/* Rn & Rm; set condition flags */
+#define A64_TST(sf, Rn, Rm) A64_ANDS(sf, A64_ZR, Rn, Rm)
+
+#endif /* _BPF_JIT_H */
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
new file mode 100644
index 0000000..38c4296
--- /dev/null
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -0,0 +1,677 @@
+/*
+ * BPF JIT compiler for ARM64
+ *
+ * Copyright (C) 2014 Zi Shen Lim <zlim.lnx@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) "bpf_jit: " fmt
+
+#include <linux/filter.h>
+#include <linux/moduleloader.h>
+#include <linux/printk.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+#include <asm/cacheflush.h>
+
+#include "bpf_jit.h"
+
+int bpf_jit_enable __read_mostly;
+
+#define TMP_REG_1 (MAX_BPF_REG + 0)
+#define TMP_REG_2 (MAX_BPF_REG + 1)
+
+/* Map BPF registers to A64 registers */
+static const int bpf2a64[] = {
+ /* return value from in-kernel function, and exit value from eBPF */
+ [BPF_REG_0] = A64_R(7),
+ /* arguments from eBPF program to in-kernel function */
+ [BPF_REG_1] = A64_R(0),
+ [BPF_REG_2] = A64_R(1),
+ [BPF_REG_3] = A64_R(2),
+ [BPF_REG_4] = A64_R(3),
+ [BPF_REG_5] = A64_R(4),
+ /* callee saved registers that in-kernel function will preserve */
+ [BPF_REG_6] = A64_R(19),
+ [BPF_REG_7] = A64_R(20),
+ [BPF_REG_8] = A64_R(21),
+ [BPF_REG_9] = A64_R(22),
+ /* read-only frame pointer to access stack */
+ [BPF_REG_FP] = A64_FP,
+ /* temporary register for internal BPF JIT */
+ [TMP_REG_1] = A64_R(23),
+ [TMP_REG_2] = A64_R(24),
+};
+
+struct jit_ctx {
+ const struct bpf_prog *prog;
+ int idx;
+ int tmp_used;
+ int body_offset;
+ int *offset;
+ u32 *image;
+};
+
+static inline void emit(const u32 insn, struct jit_ctx *ctx)
+{
+ if (ctx->image != NULL)
+ ctx->image[ctx->idx] = cpu_to_le32(insn);
+
+ ctx->idx++;
+}
+
+static inline void emit_a64_mov_i64(const int reg, const u64 val,
+ struct jit_ctx *ctx)
+{
+ u64 tmp = val;
+ int shift = 0;
+
+ emit(A64_MOVZ(1, reg, tmp & 0xffff, shift), ctx);
+ tmp >>= 16;
+ shift += 16;
+ while (tmp) {
+ if (tmp & 0xffff)
+ emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx);
+ tmp >>= 16;
+ shift += 16;
+ }
+}
+
+static inline void emit_a64_mov_i(const int is64, const int reg,
+ const s32 val, struct jit_ctx *ctx)
+{
+ u16 hi = val >> 16;
+ u16 lo = val & 0xffff;
+
+ if (hi & 0x8000) {
+ if (hi == 0xffff) {
+ emit(A64_MOVN(is64, reg, (u16)~lo, 0), ctx);
+ } else {
+ emit(A64_MOVN(is64, reg, (u16)~hi, 16), ctx);
+ emit(A64_MOVK(is64, reg, lo, 0), ctx);
+ }
+ } else {
+ emit(A64_MOVZ(is64, reg, lo, 0), ctx);
+ if (hi)
+ emit(A64_MOVK(is64, reg, hi, 16), ctx);
+ }
+}
+
+static inline int bpf2a64_offset(int bpf_to, int bpf_from,
+ const struct jit_ctx *ctx)
+{
+ int to = ctx->offset[bpf_to + 1];
+ /* -1 to account for the Branch instruction */
+ int from = ctx->offset[bpf_from + 1] - 1;
+
+ return to - from;
+}
+
+static inline int epilogue_offset(const struct jit_ctx *ctx)
+{
+ int to = ctx->offset[ctx->prog->len - 1];
+ int from = ctx->idx - ctx->body_offset;
+
+ return to - from;
+}
+
+/* Stack must be multiples of 16B */
+#define STACK_ALIGN(sz) (((sz) + 15) & ~15)
+
+static void build_prologue(struct jit_ctx *ctx)
+{
+ const u8 r6 = bpf2a64[BPF_REG_6];
+ const u8 r7 = bpf2a64[BPF_REG_7];
+ const u8 r8 = bpf2a64[BPF_REG_8];
+ const u8 r9 = bpf2a64[BPF_REG_9];
+ const u8 fp = bpf2a64[BPF_REG_FP];
+ const u8 ra = bpf2a64[BPF_REG_A];
+ const u8 rx = bpf2a64[BPF_REG_X];
+ const u8 tmp1 = bpf2a64[TMP_REG_1];
+ const u8 tmp2 = bpf2a64[TMP_REG_2];
+ int stack_size = MAX_BPF_STACK;
+
+ stack_size += 4; /* extra for skb_copy_bits buffer */
+ stack_size = STACK_ALIGN(stack_size);
+
+ /* Save callee-saved register */
+ emit(A64_PUSH(r6, r7, A64_SP), ctx);
+ emit(A64_PUSH(r8, r9, A64_SP), ctx);
+ if (ctx->tmp_used)
+ emit(A64_PUSH(tmp1, tmp2, A64_SP), ctx);
+
+ /* Set up BPF stack */
+ emit(A64_SUB_I(1, A64_SP, A64_SP, stack_size), ctx);
+
+ /* Set up frame pointer */
+ emit(A64_MOV(1, fp, A64_SP), ctx);
+
+ /* Clear registers A and X */
+ emit_a64_mov_i64(ra, 0, ctx);
+ emit_a64_mov_i64(rx, 0, ctx);
+}
+
+static void build_epilogue(struct jit_ctx *ctx)
+{
+ const u8 r0 = bpf2a64[BPF_REG_0];
+ const u8 r6 = bpf2a64[BPF_REG_6];
+ const u8 r7 = bpf2a64[BPF_REG_7];
+ const u8 r8 = bpf2a64[BPF_REG_8];
+ const u8 r9 = bpf2a64[BPF_REG_9];
+ const u8 fp = bpf2a64[BPF_REG_FP];
+ const u8 tmp1 = bpf2a64[TMP_REG_1];
+ const u8 tmp2 = bpf2a64[TMP_REG_2];
+ int stack_size = MAX_BPF_STACK;
+
+ stack_size += 4; /* extra for skb_copy_bits buffer */
+ stack_size = STACK_ALIGN(stack_size);
+
+ /* We're done with BPF stack */
+ emit(A64_ADD_I(1, A64_SP, A64_SP, stack_size), ctx);
+
+ /* Restore callee-saved register */
+ if (ctx->tmp_used)
+ emit(A64_POP(tmp1, tmp2, A64_SP), ctx);
+ emit(A64_POP(r8, r9, A64_SP), ctx);
+ emit(A64_POP(r6, r7, A64_SP), ctx);
+
+ /* Restore frame pointer */
+ emit(A64_MOV(1, fp, A64_SP), ctx);
+
+ /* Set return value */
+ emit(A64_MOV(1, A64_R(0), r0), ctx);
+
+ emit(A64_RET(A64_LR), ctx);
+}
+
+static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
+{
+ const u8 code = insn->code;
+ const u8 dst = bpf2a64[insn->dst_reg];
+ const u8 src = bpf2a64[insn->src_reg];
+ const u8 tmp = bpf2a64[TMP_REG_1];
+ const u8 tmp2 = bpf2a64[TMP_REG_2];
+ const s16 off = insn->off;
+ const s32 imm = insn->imm;
+ const int i = insn - ctx->prog->insnsi;
+ const bool is64 = BPF_CLASS(code) == BPF_ALU64;
+ u8 jmp_cond;
+ s32 jmp_offset;
+
+ switch (code) {
+ /* dst = src */
+ case BPF_ALU | BPF_MOV | BPF_X:
+ case BPF_ALU64 | BPF_MOV | BPF_X:
+ emit(A64_MOV(is64, dst, src), ctx);
+ break;
+ /* dst = dst OP src */
+ case BPF_ALU | BPF_ADD | BPF_X:
+ case BPF_ALU64 | BPF_ADD | BPF_X:
+ emit(A64_ADD(is64, dst, dst, src), ctx);
+ break;
+ case BPF_ALU | BPF_SUB | BPF_X:
+ case BPF_ALU64 | BPF_SUB | BPF_X:
+ emit(A64_SUB(is64, dst, dst, src), ctx);
+ break;
+ case BPF_ALU | BPF_AND | BPF_X:
+ case BPF_ALU64 | BPF_AND | BPF_X:
+ emit(A64_AND(is64, dst, dst, src), ctx);
+ break;
+ case BPF_ALU | BPF_OR | BPF_X:
+ case BPF_ALU64 | BPF_OR | BPF_X:
+ emit(A64_ORR(is64, dst, dst, src), ctx);
+ break;
+ case BPF_ALU | BPF_XOR | BPF_X:
+ case BPF_ALU64 | BPF_XOR | BPF_X:
+ emit(A64_EOR(is64, dst, dst, src), ctx);
+ break;
+ case BPF_ALU | BPF_MUL | BPF_X:
+ case BPF_ALU64 | BPF_MUL | BPF_X:
+ emit(A64_MUL(is64, dst, dst, src), ctx);
+ break;
+ case BPF_ALU | BPF_DIV | BPF_X:
+ case BPF_ALU64 | BPF_DIV | BPF_X:
+ emit(A64_UDIV(is64, dst, dst, src), ctx);
+ break;
+ case BPF_ALU | BPF_MOD | BPF_X:
+ case BPF_ALU64 | BPF_MOD | BPF_X:
+ ctx->tmp_used = 1;
+ emit(A64_UDIV(is64, tmp, dst, src), ctx);
+ emit(A64_MUL(is64, tmp, tmp, src), ctx);
+ emit(A64_SUB(is64, dst, dst, tmp), ctx);
+ break;
+ /* dst = -dst */
+ case BPF_ALU | BPF_NEG:
+ case BPF_ALU64 | BPF_NEG:
+ emit(A64_NEG(is64, dst, dst), ctx);
+ break;
+ /* dst = BSWAP##imm(dst) */
+ case BPF_ALU | BPF_END | BPF_FROM_LE:
+ case BPF_ALU | BPF_END | BPF_FROM_BE:
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ if (BPF_SRC(code) == BPF_FROM_BE)
+ break;
+#else /* !CONFIG_CPU_BIG_ENDIAN */
+ if (BPF_SRC(code) == BPF_FROM_LE)
+ break;
+#endif
+ switch (imm) {
+ case 16:
+ emit(A64_REV16(is64, dst, dst), ctx);
+ break;
+ case 32:
+ emit(A64_REV32(is64, dst, dst), ctx);
+ break;
+ case 64:
+ emit(A64_REV64(dst, dst), ctx);
+ break;
+ }
+ break;
+ /* dst = imm */
+ case BPF_ALU | BPF_MOV | BPF_K:
+ case BPF_ALU64 | BPF_MOV | BPF_K:
+ emit_a64_mov_i(is64, dst, imm, ctx);
+ break;
+ /* dst = dst OP imm */
+ case BPF_ALU | BPF_ADD | BPF_K:
+ case BPF_ALU64 | BPF_ADD | BPF_K:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(is64, tmp, imm, ctx);
+ emit(A64_ADD(is64, dst, dst, tmp), ctx);
+ break;
+ case BPF_ALU | BPF_SUB | BPF_K:
+ case BPF_ALU64 | BPF_SUB | BPF_K:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(is64, tmp, imm, ctx);
+ emit(A64_SUB(is64, dst, dst, tmp), ctx);
+ break;
+ case BPF_ALU | BPF_AND | BPF_K:
+ case BPF_ALU64 | BPF_AND | BPF_K:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(is64, tmp, imm, ctx);
+ emit(A64_AND(is64, dst, dst, tmp), ctx);
+ break;
+ case BPF_ALU | BPF_OR | BPF_K:
+ case BPF_ALU64 | BPF_OR | BPF_K:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(is64, tmp, imm, ctx);
+ emit(A64_ORR(is64, dst, dst, tmp), ctx);
+ break;
+ case BPF_ALU | BPF_XOR | BPF_K:
+ case BPF_ALU64 | BPF_XOR | BPF_K:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(is64, tmp, imm, ctx);
+ emit(A64_EOR(is64, dst, dst, tmp), ctx);
+ break;
+ case BPF_ALU | BPF_MUL | BPF_K:
+ case BPF_ALU64 | BPF_MUL | BPF_K:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(is64, tmp, imm, ctx);
+ emit(A64_MUL(is64, dst, dst, tmp), ctx);
+ break;
+ case BPF_ALU | BPF_DIV | BPF_K:
+ case BPF_ALU64 | BPF_DIV | BPF_K:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(is64, tmp, imm, ctx);
+ emit(A64_UDIV(is64, dst, dst, tmp), ctx);
+ break;
+ case BPF_ALU | BPF_MOD | BPF_K:
+ case BPF_ALU64 | BPF_MOD | BPF_K:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(is64, tmp2, imm, ctx);
+ emit(A64_UDIV(is64, tmp, dst, tmp2), ctx);
+ emit(A64_MUL(is64, tmp, tmp, tmp2), ctx);
+ emit(A64_SUB(is64, dst, dst, tmp), ctx);
+ break;
+ case BPF_ALU | BPF_LSH | BPF_K:
+ case BPF_ALU64 | BPF_LSH | BPF_K:
+ emit(A64_LSL(is64, dst, dst, imm), ctx);
+ break;
+ case BPF_ALU | BPF_RSH | BPF_K:
+ case BPF_ALU64 | BPF_RSH | BPF_K:
+ emit(A64_LSR(is64, dst, dst, imm), ctx);
+ break;
+ case BPF_ALU | BPF_ARSH | BPF_K:
+ case BPF_ALU64 | BPF_ARSH | BPF_K:
+ emit(A64_ASR(is64, dst, dst, imm), ctx);
+ break;
+
+#define check_imm(bits, imm) do { \
+ if ((((imm) > 0) && ((imm) >> (bits))) || \
+ (((imm) < 0) && (~(imm) >> (bits)))) { \
+ pr_info("[%2d] imm=%d(0x%x) out of range\n", \
+ i, imm, imm); \
+ return -EINVAL; \
+ } \
+} while (0)
+#define check_imm19(imm) check_imm(19, imm)
+#define check_imm26(imm) check_imm(26, imm)
+
+ /* JUMP off */
+ case BPF_JMP | BPF_JA:
+ jmp_offset = bpf2a64_offset(i + off, i, ctx);
+ check_imm26(jmp_offset);
+ emit(A64_B(jmp_offset), ctx);
+ break;
+ /* IF (dst COND src) JUMP off */
+ case BPF_JMP | BPF_JEQ | BPF_X:
+ case BPF_JMP | BPF_JGT | BPF_X:
+ case BPF_JMP | BPF_JGE | BPF_X:
+ case BPF_JMP | BPF_JNE | BPF_X:
+ case BPF_JMP | BPF_JSGT | BPF_X:
+ case BPF_JMP | BPF_JSGE | BPF_X:
+ emit(A64_CMP(1, dst, src), ctx);
+emit_cond_jmp:
+ jmp_offset = bpf2a64_offset(i + off, i, ctx);
+ check_imm19(jmp_offset);
+ switch (BPF_OP(code)) {
+ case BPF_JEQ:
+ jmp_cond = A64_COND_EQ;
+ break;
+ case BPF_JGT:
+ jmp_cond = A64_COND_HI;
+ break;
+ case BPF_JGE:
+ jmp_cond = A64_COND_CS;
+ break;
+ case BPF_JNE:
+ jmp_cond = A64_COND_NE;
+ break;
+ case BPF_JSGT:
+ jmp_cond = A64_COND_GT;
+ break;
+ case BPF_JSGE:
+ jmp_cond = A64_COND_GE;
+ break;
+ default:
+ return -EFAULT;
+ }
+ emit(A64_B_(jmp_cond, jmp_offset), ctx);
+ break;
+ case BPF_JMP | BPF_JSET | BPF_X:
+ emit(A64_TST(1, dst, src), ctx);
+ goto emit_cond_jmp;
+ /* IF (dst COND imm) JUMP off */
+ case BPF_JMP | BPF_JEQ | BPF_K:
+ case BPF_JMP | BPF_JGT | BPF_K:
+ case BPF_JMP | BPF_JGE | BPF_K:
+ case BPF_JMP | BPF_JNE | BPF_K:
+ case BPF_JMP | BPF_JSGT | BPF_K:
+ case BPF_JMP | BPF_JSGE | BPF_K:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(1, tmp, imm, ctx);
+ emit(A64_CMP(1, dst, tmp), ctx);
+ goto emit_cond_jmp;
+ case BPF_JMP | BPF_JSET | BPF_K:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(1, tmp, imm, ctx);
+ emit(A64_TST(1, dst, tmp), ctx);
+ goto emit_cond_jmp;
+ /* function call */
+ case BPF_JMP | BPF_CALL:
+ {
+ const u8 r0 = bpf2a64[BPF_REG_0];
+ const u64 func = (u64)__bpf_call_base + imm;
+
+ ctx->tmp_used = 1;
+ emit_a64_mov_i64(tmp, func, ctx);
+ emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx);
+ emit(A64_MOV(1, A64_FP, A64_SP), ctx);
+ emit(A64_BLR(tmp), ctx);
+ emit(A64_MOV(1, r0, A64_R(0)), ctx);
+ emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx);
+ break;
+ }
+ /* function return */
+ case BPF_JMP | BPF_EXIT:
+ if (i == ctx->prog->len - 1)
+ break;
+ jmp_offset = epilogue_offset(ctx);
+ check_imm26(jmp_offset);
+ emit(A64_B(jmp_offset), ctx);
+ break;
+
+ /* LDX: dst = *(size *)(src + off) */
+ case BPF_LDX | BPF_MEM | BPF_W:
+ case BPF_LDX | BPF_MEM | BPF_H:
+ case BPF_LDX | BPF_MEM | BPF_B:
+ case BPF_LDX | BPF_MEM | BPF_DW:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(1, tmp, off, ctx);
+ switch (BPF_SIZE(code)) {
+ case BPF_W:
+ emit(A64_LDR32(dst, src, tmp), ctx);
+ break;
+ case BPF_H:
+ emit(A64_LDRH(dst, src, tmp), ctx);
+ break;
+ case BPF_B:
+ emit(A64_LDRB(dst, src, tmp), ctx);
+ break;
+ case BPF_DW:
+ emit(A64_LDR64(dst, src, tmp), ctx);
+ break;
+ }
+ break;
+
+ /* ST: *(size *)(dst + off) = imm */
+ case BPF_ST | BPF_MEM | BPF_W:
+ case BPF_ST | BPF_MEM | BPF_H:
+ case BPF_ST | BPF_MEM | BPF_B:
+ case BPF_ST | BPF_MEM | BPF_DW:
+ goto notyet;
+
+ /* STX: *(size *)(dst + off) = src */
+ case BPF_STX | BPF_MEM | BPF_W:
+ case BPF_STX | BPF_MEM | BPF_H:
+ case BPF_STX | BPF_MEM | BPF_B:
+ case BPF_STX | BPF_MEM | BPF_DW:
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(1, tmp, off, ctx);
+ switch (BPF_SIZE(code)) {
+ case BPF_W:
+ emit(A64_STR32(src, dst, tmp), ctx);
+ break;
+ case BPF_H:
+ emit(A64_STRH(src, dst, tmp), ctx);
+ break;
+ case BPF_B:
+ emit(A64_STRB(src, dst, tmp), ctx);
+ break;
+ case BPF_DW:
+ emit(A64_STR64(src, dst, tmp), ctx);
+ break;
+ }
+ break;
+ /* STX XADD: lock *(u32 *)(dst + off) += src */
+ case BPF_STX | BPF_XADD | BPF_W:
+ /* STX XADD: lock *(u64 *)(dst + off) += src */
+ case BPF_STX | BPF_XADD | BPF_DW:
+ goto notyet;
+
+ /* R0 = ntohx(*(size *)(((struct sk_buff *)R6)->data + imm)) */
+ case BPF_LD | BPF_ABS | BPF_W:
+ case BPF_LD | BPF_ABS | BPF_H:
+ case BPF_LD | BPF_ABS | BPF_B:
+ /* R0 = ntohx(*(size *)(((struct sk_buff *)R6)->data + src + imm)) */
+ case BPF_LD | BPF_IND | BPF_W:
+ case BPF_LD | BPF_IND | BPF_H:
+ case BPF_LD | BPF_IND | BPF_B:
+ {
+ const u8 r0 = bpf2a64[BPF_REG_0]; /* r0 = return value */
+ const u8 r6 = bpf2a64[BPF_REG_6]; /* r6 = pointer to sk_buff */
+ const u8 fp = bpf2a64[BPF_REG_FP];
+ const u8 r1 = bpf2a64[BPF_REG_1]; /* r1: struct sk_buff *skb */
+ const u8 r2 = bpf2a64[BPF_REG_2]; /* r2: int k */
+ const u8 r3 = bpf2a64[BPF_REG_3]; /* r3: unsigned int size */
+ const u8 r4 = bpf2a64[BPF_REG_4]; /* r4: void *buffer */
+ const u8 r5 = bpf2a64[BPF_REG_5]; /* r5: void *(*func)(...) */
+ int size;
+
+ emit(A64_MOV(1, r1, r6), ctx);
+ emit_a64_mov_i(0, r2, imm, ctx);
+ if (BPF_MODE(code) == BPF_IND)
+ emit(A64_ADD(0, r2, r2, src), ctx);
+ switch (BPF_SIZE(code)) {
+ case BPF_W:
+ size = 4;
+ break;
+ case BPF_H:
+ size = 2;
+ break;
+ case BPF_B:
+ size = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ emit_a64_mov_i64(r3, size, ctx);
+ emit(A64_ADD_I(1, r4, fp, MAX_BPF_STACK), ctx);
+ emit_a64_mov_i64(r5, (unsigned long)bpf_load_pointer, ctx);
+ emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx);
+ emit(A64_MOV(1, A64_FP, A64_SP), ctx);
+ emit(A64_BLR(r5), ctx);
+ emit(A64_MOV(1, r0, A64_R(0)), ctx);
+ emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx);
+
+ jmp_offset = epilogue_offset(ctx);
+ check_imm19(jmp_offset);
+ emit(A64_CBZ(1, r0, jmp_offset), ctx);
+ emit(A64_MOV(1, r5, r0), ctx);
+ switch (BPF_SIZE(code)) {
+ case BPF_W:
+ emit(A64_LDR32(r0, r5, A64_ZR), ctx);
+#ifndef CONFIG_CPU_BIG_ENDIAN
+ emit(A64_REV32(0, r0, r0), ctx);
+#endif
+ break;
+ case BPF_H:
+ emit(A64_LDRH(r0, r5, A64_ZR), ctx);
+#ifndef CONFIG_CPU_BIG_ENDIAN
+ emit(A64_REV16(0, r0, r0), ctx);
+#endif
+ break;
+ case BPF_B:
+ emit(A64_LDRB(r0, r5, A64_ZR), ctx);
+ break;
+ }
+ break;
+ }
+notyet:
+ pr_info_once("*** NOT YET: opcode %02x ***\n", code);
+ return -EFAULT;
+
+ default:
+ pr_err_once("unknown opcode %02x\n", code);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int build_body(struct jit_ctx *ctx)
+{
+ const struct bpf_prog *prog = ctx->prog;
+ int i;
+
+ for (i = 0; i < prog->len; i++) {
+ const struct bpf_insn *insn = &prog->insnsi[i];
+ int ret;
+
+ if (ctx->image == NULL)
+ ctx->offset[i] = ctx->idx;
+
+ ret = build_insn(insn, ctx);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline void bpf_flush_icache(void *start, void *end)
+{
+ flush_icache_range((unsigned long)start, (unsigned long)end);
+}
+
+void bpf_jit_compile(struct bpf_prog *prog)
+{
+ /* Nothing to do here. We support Internal BPF. */
+}
+
+void bpf_int_jit_compile(struct bpf_prog *prog)
+{
+ struct jit_ctx ctx;
+ int image_size;
+
+ if (!bpf_jit_enable)
+ return;
+
+ if (!prog || !prog->len)
+ return;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.prog = prog;
+
+ ctx.offset = kcalloc(prog->len, sizeof(int), GFP_KERNEL);
+ if (ctx.offset == NULL)
+ return;
+
+ /* 1. Initial fake pass to compute ctx->idx. */
+
+ /* Fake pass to fill in ctx->offset. */
+ if (build_body(&ctx))
+ goto out;
+
+ build_prologue(&ctx);
+
+ build_epilogue(&ctx);
+
+ /* Now we know the actual image size. */
+ image_size = sizeof(u32) * ctx.idx;
+ ctx.image = module_alloc(image_size);
+ if (unlikely(ctx.image == NULL))
+ goto out;
+
+ /* 2. Now, the actual pass. */
+
+ ctx.idx = 0;
+ build_prologue(&ctx);
+
+ ctx.body_offset = ctx.idx;
+ if (build_body(&ctx))
+ goto out;
+
+ build_epilogue(&ctx);
+
+ /* And we're done. */
+ if (bpf_jit_enable > 1)
+ bpf_jit_dump(prog->len, image_size, 2, ctx.image);
+
+ bpf_flush_icache(ctx.image, ctx.image + ctx.idx);
+ prog->bpf_func = (void *)ctx.image;
+ prog->jited = 1;
+
+out:
+ kfree(ctx.offset);
+}
+
+void bpf_jit_free(struct bpf_prog *prog)
+{
+ if (prog->jited)
+ module_free(NULL, prog->bpf_func);
+
+ kfree(prog);
+}
--
1.9.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox