* Re: [GIT] Networking
From: pr-tracker-bot @ 2019-06-22 5:30 UTC (permalink / raw)
To: David Miller; +Cc: torvalds, akpm, netdev, linux-kernel
In-Reply-To: <20190621.212137.1249209897243384684.davem@davemloft.net>
The pull request you sent on Fri, 21 Jun 2019 21:21:37 -0400 (EDT):
> (unable to parse the git remote)
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/c356dc4b540edd6c02b409dd8cf3208ba2804c38
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.wiki.kernel.org/userdoc/prtracker
^ permalink raw reply
* Re: [PATCH 15/15] kbuild: compile test UAPI headers to ensure they are self-contained
From: Masahiro Yamada @ 2019-06-22 5:12 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Linux Kbuild mailing list, Randy Dunlap, David Howells,
Sam Ravnborg, Jani Nikula, Song Liu, Alexei Starovoitov,
Networking, Yonghong Song, Linux Kernel Mailing List, linux-riscv,
Michal Marek, Martin KaFai Lau, Palmer Dabbelt, bpf,
Daniel Borkmann, Albert Ou
In-Reply-To: <CAK8P3a08f25WYP5r57JHPcZWieS2+07=_qTphLosS4M2w8F0Zw@mail.gmail.com>
On Sat, Jun 22, 2019 at 4:05 AM Arnd Bergmann <arnd@arndb.de> wrote:
>
> On Tue, Jun 4, 2019 at 12:16 PM Masahiro Yamada
> <yamada.masahiro@socionext.com> wrote:
>
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -1363,7 +1363,7 @@ CLEAN_DIRS += $(MODVERDIR) include/ksym
> > CLEAN_FILES += modules.builtin.modinfo
> >
> > # Directories & files removed with 'make mrproper'
> > -MRPROPER_DIRS += include/config usr/include include/generated \
> > +MRPROPER_DIRS += include/config include/generated \
> > arch/$(SRCARCH)/include/generated .tmp_objdiff
> > MRPROPER_FILES += .config .config.old .version \
> > Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \
>
> This change seems to have caused a minor regression:
>
> $ make clean ; make clean
> find: ‘*’: No such file or directory
Hmm, I cannot reproduce this.
I checked the latest linux-next.
masahiro@grover:~/ref/linux-next$ git describe
next-20190621
masahiro@grover:~/ref/linux-next$ make clean; make clean
masahiro@grover:~/ref/linux-next$
> Any idea?
>
> Arnd
--
Best Regards
Masahiro Yamada
^ permalink raw reply
* RE: [PATCH v3 0/5] net: macb: cover letter
From: Parshuram Raju Thombare @ 2019-06-22 3:18 UTC (permalink / raw)
To: Andrew Lunn
Cc: nicolas.ferre@microchip.com, davem@davemloft.net,
f.fainelli@gmail.com, linux@armlinux.org.uk,
netdev@vger.kernel.org, hkallweit1@gmail.com,
linux-kernel@vger.kernel.org, Rafal Ciepiela, Anil Joy Varughese,
Piotr Sroka
In-Reply-To: <20190621131611.GB21188@lunn.ch>
Hi Andrew,
>On Fri, Jun 21, 2019 at 09:33:57AM +0100, Parshuram Thombare wrote:
>> Hello !
>>
>> 2. 0002-net-macb-add-support-for-sgmii-MAC-PHY-interface.patch
>> This patch add support for SGMII mode.
>
>Hi Parshuram
>
>What PHYs are using to test this? You mention TI PHY DP83867, but that seems to
>be a plain old 10/100/1000 RGMII PHY.
It is DP83867ISRGZ on VCU118 board. This PHY supports SGMII but driver dp83867
doesn't seems to support it, that was the reason previous patch set has patch
trying to configure PHY in SGMII mode from PCI wrapper driver.
Regards,
Parshuram Thombare
^ permalink raw reply
* Re: [PATCH net-next 0/2] PCI: let pci_disable_link_state propagate errors
From: David Miller @ 2019-06-22 2:06 UTC (permalink / raw)
To: hkallweit1; +Cc: bhelgaas, nic_swsd, linux-pci, netdev
In-Reply-To: <5ea56278-05e2-794f-5f66-23343e72164c@gmail.com>
From: Heiner Kallweit <hkallweit1@gmail.com>
Date: Tue, 18 Jun 2019 23:12:56 +0200
> Drivers like r8169 rely on pci_disable_link_state() having disabled
> certain ASPM link states. If OS can't control ASPM then
> pci_disable_link_state() turns into a no-op w/o informing the caller.
> The driver therefore may falsely assume the respective ASPM link
> states are disabled. Let pci_disable_link_state() propagate errors
> to the caller, enabling the caller to react accordingly.
>
> I'd propose to let this series go through the netdev tree if the PCI
> core extension is acked by the PCI people.
Series applied, thanks Heiner.
^ permalink raw reply
* [GIT] Networking
From: David Miller @ 2019-06-22 1:21 UTC (permalink / raw)
To: torvalds; +Cc: akpm, netdev, linux-kernel
1) Fix leak of unqueued fragments in ipv6 nf_defrag, from Guillaume
Nault.
2) Don't access the DDM interface unless the transceiver implements
it in bnx2x, from Mauro S. M. Rodrigues.
3) Don't double fetch 'len' from userspace in sock_getsockopt(), from
JingYi Hou.
4) Sign extension overflow in lio_core, from Colin Ian King.
5) Various netem bug fixes wrt. corrupted packets from Jakub
Kicinski.
6) Fix epollout hang in hvsock, from Sunil Muthuswamy.
7) Fix regression in default fib6_type, from David Ahern.
8) Handle memory limits in tcp_fragment more appropriately,
from Eric Dumazet.
Please pull, thanks a lot!
The following changes since commit 29f785ff76b65696800b75c3d8e0b58e603bb1d0:
Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs (2019-06-17 16:28:28 -0700)
are available in the Git repository at:
git://git.kernel.org:/pub/scm/linux/kernel/git/davem/net.git
for you to fetch changes up to b6653b3629e5b88202be3c9abc44713973f5c4b4:
tcp: refine memory limit test in tcp_fragment() (2019-06-21 20:58:42 -0400)
----------------------------------------------------------------
Colin Ian King (1):
net: lio_core: fix potential sign-extension overflow on large shift
David Ahern (1):
ipv6: Default fib6_type to RTN_UNICAST when not set
David S. Miller (4):
Merge branch 'net-fix-quite-a-few-dst_cache-crashes-reported-by-syzbot'
Merge branch 'net-netem-fix-issues-with-corrupting-GSO-frames'
Merge git://git.kernel.org/.../pablo/nf
Merge branch 'af_iucv-fixes'
Eric Dumazet (2):
inet: clear num_timeout reqsk_alloc()
tcp: refine memory limit test in tcp_fragment()
Fei Li (1):
tun: wake up waitqueues after IFF_UP is set
Fred Klassen (1):
net/udp_gso: Allow TX timestamp with UDP GSO
Guillaume Nault (2):
netfilter: ipv6: nf_defrag: fix leakage of unqueued fragments
netfilter: ipv6: nf_defrag: accept duplicate fragments again
Jakub Kicinski (2):
net: netem: fix backlog accounting for corrupted GSO frames
net: netem: fix use after free and double free with packet corruption
JingYi Hou (1):
net: remove duplicate fetch in sock_getsockopt
Julian Wiedmann (3):
net/af_iucv: remove GFP_DMA restriction for HiperTransport
net/af_iucv: build proper skbs for HiperTransport
net/af_iucv: always register net_device notifier
Krzysztof Kozlowski (1):
net: hns3: Fix inconsistent indenting
Mauro S. M. Rodrigues (1):
bnx2x: Check if transceiver implements DDM before access
Nathan Huckleberry (1):
net: mvpp2: debugfs: Add pmap to fs dump
Pablo Neira Ayuso (1):
netfilter: nf_tables: fix module autoload with inet family
Rasmus Villemoes (1):
net: dsa: mv88e6xxx: fix shift of FID bits in mv88e6185_g1_vtu_loadpurge()
Sunil Muthuswamy (1):
hvsock: fix epollout hang from race condition
Tuong Lien (1):
tipc: fix issues with early FAILOVER_MSG from peer
Xin Long (3):
ip_tunnel: allow not to count pkts on tstats by setting skb's dev to NULL
ip6_tunnel: allow not to count pkts on tstats by passing dev as NULL
tipc: pass tunnel dev as NULL to udp_tunnel(6)_xmit_skb
drivers/net/dsa/mv88e6xxx/global1_vtu.c | 2 +-
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 3 ++-
drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h | 1 +
drivers/net/ethernet/cavium/liquidio/lio_core.c | 2 +-
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 2 +-
drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c | 3 +++
drivers/net/tun.c | 19 +++++++++----------
include/net/ip6_tunnel.h | 9 ++++++---
include/net/request_sock.h | 3 +++
net/core/sock.c | 3 ---
net/ipv4/inet_connection_sock.c | 4 ----
net/ipv4/ip_tunnel_core.c | 9 ++++++---
net/ipv4/tcp_fastopen.c | 4 ----
net/ipv4/tcp_output.c | 3 ++-
net/ipv4/udp_offload.c | 5 +++++
net/ipv6/netfilter/nf_conntrack_reasm.c | 22 ++++++++++++----------
net/ipv6/route.c | 2 +-
net/iucv/af_iucv.c | 49 ++++++++++++++++++++++++++++++++++++-------------
net/netfilter/nft_masq.c | 3 +--
net/netfilter/nft_redir.c | 3 +--
net/sched/sch_netem.c | 26 ++++++++++++++------------
net/tipc/link.c | 1 -
net/tipc/node.c | 10 +++++++---
net/tipc/udp_media.c | 8 +++-----
net/vmw_vsock/hyperv_transport.c | 39 ++++++++-------------------------------
25 files changed, 123 insertions(+), 112 deletions(-)
^ permalink raw reply
* Re: [PATCH net] tcp: refine memory limit test in tcp_fragment()
From: David Miller @ 2019-06-22 1:00 UTC (permalink / raw)
To: edumazet; +Cc: netdev, eric.dumazet, cpaasch
In-Reply-To: <20190621130955.147974-1-edumazet@google.com>
From: Eric Dumazet <edumazet@google.com>
Date: Fri, 21 Jun 2019 06:09:55 -0700
> tcp_fragment() might be called for skbs in the write queue.
>
> Memory limits might have been exceeded because tcp_sendmsg() only
> checks limits at full skb (64KB) boundaries.
>
> Therefore, we need to make sure tcp_fragment() wont punish applications
> that might have setup very low SO_SNDBUF values.
>
> Fixes: f070ef2ac667 ("tcp: tcp_fragment() should apply sane memory limits")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> Reported-by: Christoph Paasch <cpaasch@apple.com>
Applied and queued up for -stable.
^ permalink raw reply
* [PATCH next 2/3] blackhole_netdev: use blackhole_netdev to invalidate dst entries
From: Mahesh Bandewar @ 2019-06-22 0:45 UTC (permalink / raw)
To: Netdev
Cc: Eric Dumazet, David Miller, Michael Chan, Daniel Axtens,
Mahesh Bandewar, Mahesh Bandewar
Use blackhole_netdev instead of 'lo' device with lower MTU when marking
dst "dead".
Signed-off-by: Mahesh Bandewar <maheshb@google.com>
---
net/core/dst.c | 2 +-
net/ipv4/route.c | 3 +--
net/ipv6/route.c | 2 +-
3 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/net/core/dst.c b/net/core/dst.c
index e46366228eaf..1325316d9eab 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -160,7 +160,7 @@ void dst_dev_put(struct dst_entry *dst)
dst->ops->ifdown(dst, dev, true);
dst->input = dst_discard;
dst->output = dst_discard_out;
- dst->dev = dev_net(dst->dev)->loopback_dev;
+ dst->dev = blackhole_netdev;
dev_hold(dst->dev);
dev_put(dev);
}
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 66cbe8a7a168..049af0f88307 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1532,7 +1532,6 @@ static void ipv4_dst_destroy(struct dst_entry *dst)
void rt_flush_dev(struct net_device *dev)
{
- struct net *net = dev_net(dev);
struct rtable *rt;
int cpu;
@@ -1543,7 +1542,7 @@ void rt_flush_dev(struct net_device *dev)
list_for_each_entry(rt, &ul->head, rt_uncached) {
if (rt->dst.dev != dev)
continue;
- rt->dst.dev = net->loopback_dev;
+ rt->dst.dev = blackhole_netdev;
dev_hold(rt->dst.dev);
dev_put(dev);
}
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index c4d285fe0adc..335bcf812b25 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -176,7 +176,7 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
}
if (rt_dev == dev) {
- rt->dst.dev = loopback_dev;
+ rt->dst.dev = blackhole_netdev;
dev_hold(rt->dst.dev);
dev_put(rt_dev);
}
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH next 3/3] blackhole_dev: add a selftest
From: Mahesh Bandewar @ 2019-06-22 0:45 UTC (permalink / raw)
To: Netdev
Cc: Eric Dumazet, David Miller, Michael Chan, Daniel Axtens,
Mahesh Bandewar, Mahesh Bandewar
Since this is not really a device with all capabilities, this test
ensures that it has *enough* to make it through the data path
without causing unwanted side-effects (read crash!).
Signed-off-by: Mahesh Bandewar <maheshb@google.com>
---
lib/Kconfig.debug | 9 ++
lib/Makefile | 1 +
lib/test_blackhole_dev.c | 100 ++++++++++++++++++
tools/testing/selftests/net/Makefile | 3 +-
tools/testing/selftests/net/config | 1 +
.../selftests/net/test_blackhole_dev.sh | 11 ++
6 files changed, 124 insertions(+), 1 deletion(-)
create mode 100644 lib/test_blackhole_dev.c
create mode 100755 tools/testing/selftests/net/test_blackhole_dev.sh
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index cbdfae379896..79d96e1614f5 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1909,6 +1909,15 @@ config TEST_BPF
If unsure, say N.
+config TEST_BLACKHOLE_DEV
+ tristate "Test BPF filter functionality"
+ depends on m && NET
+ help
+ This builds the "test_blackhole_dev" module that validates the
+ data path through this blackhole netdev.
+
+ If unsure, say N.
+
config FIND_BIT_BENCHMARK
tristate "Test find_bit functions"
help
diff --git a/lib/Makefile b/lib/Makefile
index fb7697031a79..c293624d3664 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o
obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o
obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o
obj-$(CONFIG_TEST_STACKINIT) += test_stackinit.o
+obj-$(CONFIG_TEST_BLACKHOLE_DEV) += test_blackhole_dev.o
obj-$(CONFIG_TEST_LIVEPATCH) += livepatch/
diff --git a/lib/test_blackhole_dev.c b/lib/test_blackhole_dev.c
new file mode 100644
index 000000000000..4c40580a99a3
--- /dev/null
+++ b/lib/test_blackhole_dev.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This module tests the blackhole_dev that is created during the
+ * net subsystem initialization. The test this module performs is
+ * by injecting an skb into the stack with skb->dev as the
+ * blackhole_dev and expects kernel to behave in a sane manner
+ * (in other words, *not crash*)!
+ *
+ * Copyright (c) 2018, Mahesh Bandewar <maheshb@google.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/udp.h>
+#include <linux/ipv6.h>
+
+#include <net/dst.h>
+
+#define SKB_SIZE 256
+#define HEAD_SIZE (14+40+8) /* Ether + IPv6 + UDP */
+#define TAIL_SIZE 32 /* random tail-room */
+
+#define UDP_PORT 1234
+
+static int __init test_blackholedev_init(void)
+{
+ struct ipv6hdr *ip6h;
+ struct sk_buff *skb;
+ struct ethhdr *ethh;
+ struct udphdr *uh;
+ int data_len;
+ int ret;
+
+ skb = alloc_skb(SKB_SIZE, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ /* Reserve head-room for the headers */
+ skb_reserve(skb, HEAD_SIZE);
+
+ /* Add data to the skb */
+ data_len = SKB_SIZE - (HEAD_SIZE + TAIL_SIZE);
+ memset(__skb_put(skb, data_len), 0xf, data_len);
+
+ /* Add protocol data */
+ /* (Transport) UDP */
+ uh = (struct udphdr *)skb_push(skb, sizeof(struct udphdr));
+ skb_set_transport_header(skb, 0);
+ uh->source = uh->dest = htons(UDP_PORT);
+ uh->len = htons(data_len);
+ uh->check = 0;
+ /* (Network) IPv6 */
+ ip6h = (struct ipv6hdr *)skb_push(skb, sizeof(struct ipv6hdr));
+ skb_set_network_header(skb, 0);
+ ip6h->hop_limit = 32;
+ ip6h->payload_len = data_len + sizeof(struct udphdr);
+ ip6h->nexthdr = IPPROTO_UDP;
+ ip6h->saddr = in6addr_loopback;
+ ip6h->daddr = in6addr_loopback;
+ /* Ether */
+ ethh = (struct ethhdr *)skb_push(skb, sizeof(struct ethhdr));
+ skb_set_mac_header(skb, 0);
+
+ skb->protocol = htons(ETH_P_IPV6);
+ skb->pkt_type = PACKET_HOST;
+ skb->dev = blackhole_netdev;
+
+ /* Now attempt to send the packet */
+ ret = dev_queue_xmit(skb);
+
+ switch (ret) {
+ case NET_XMIT_SUCCESS:
+ pr_warn("dev_queue_xmit() returned NET_XMIT_SUCCESS\n");
+ break;
+ case NET_XMIT_DROP:
+ pr_warn("dev_queue_xmit() returned NET_XMIT_DROP\n");
+ break;
+ case NET_XMIT_CN:
+ pr_warn("dev_queue_xmit() returned NET_XMIT_CN\n");
+ break;
+ default:
+ pr_err("dev_queue_xmit() returned UNKNOWN(%d)\n", ret);
+ }
+
+ return 0;
+}
+
+static void __exit test_blackholedev_exit(void)
+{
+ pr_warn("test_blackholedev module terminating.\n");
+}
+
+module_init(test_blackholedev_init);
+module_exit(test_blackholedev_exit);
+
+MODULE_AUTHOR("Mahesh Bandewar <maheshb@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 9a275d932fd5..fac7e9eca034 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -4,8 +4,9 @@
CFLAGS = -Wall -Wl,--no-as-needed -O2 -g
CFLAGS += -I../../../../usr/include/
+<<<<<<< HEAD
TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh \
- rtnetlink.sh xfrm_policy.sh
+ rtnetlink.sh xfrm_policy.sh test_blackhole_dev.sh
TEST_PROGS += fib_tests.sh fib-onlink-tests.sh pmtu.sh udpgso.sh ip_defrag.sh
TEST_PROGS += udpgso_bench.sh fib_rule_tests.sh msg_zerocopy.sh psock_snd.sh
TEST_PROGS += udpgro_bench.sh udpgro.sh test_vxlan_under_vrf.sh reuseport_addr_any.sh
diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config
index 89f84b5118bf..e4b878d95ba0 100644
--- a/tools/testing/selftests/net/config
+++ b/tools/testing/selftests/net/config
@@ -27,3 +27,4 @@ CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
CONFIG_NET_SCH_FQ=m
CONFIG_NET_SCH_ETF=m
+CONFIG_TEST_BLACKHOLE_DEV=m
diff --git a/tools/testing/selftests/net/test_blackhole_dev.sh b/tools/testing/selftests/net/test_blackhole_dev.sh
new file mode 100755
index 000000000000..3119b80e711f
--- /dev/null
+++ b/tools/testing/selftests/net/test_blackhole_dev.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs blackhole-dev test using blackhole-dev kernel module
+
+if /sbin/modprobe -q test_blackhole_dev ; then
+ /sbin/modprobe -q -r test_blackhole_dev;
+ echo "test_blackhole_dev: ok";
+else
+ echo "test_blackhole_dev: [FAIL]";
+ exit 1;
+fi
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH next 1/3] loopback: create blackhole net device similar to loopack.
From: Mahesh Bandewar @ 2019-06-22 0:45 UTC (permalink / raw)
To: Netdev
Cc: Eric Dumazet, David Miller, Michael Chan, Daniel Axtens,
Mahesh Bandewar, Mahesh Bandewar
Create a blackhole net device that can be used for "dead"
dst entries instead of loopback device. This blackhole device differs
from loopback in few aspects: (a) It's not per-ns. (b) MTU on this
device is ETH_MIN_MTU (c) The xmit function is essentially kfree_skb().
and (d) since it's not registered it won't have ifindex.
Lower MTU effectively make the device not pass the MTU check during
the route check when a dst associated with the skb is dead.
Signed-off-by: Mahesh Bandewar <maheshb@google.com>
---
drivers/net/loopback.c | 76 ++++++++++++++++++++++++++++++++++-----
include/linux/netdevice.h | 2 ++
2 files changed, 69 insertions(+), 9 deletions(-)
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index 87d361666cdd..3b39def5471e 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -55,6 +55,13 @@
#include <net/net_namespace.h>
#include <linux/u64_stats_sync.h>
+/* blackhole_netdev - a device used for dsts that are marked expired!
+ * This is global device (instead of per-net-ns) since it's not needed
+ * to be per-ns and gets initialized at boot time.
+ */
+struct net_device *blackhole_netdev;
+EXPORT_SYMBOL(blackhole_netdev);
+
/* The higher levels take care of making this non-reentrant (it's
* called with bh's disabled).
*/
@@ -150,12 +157,14 @@ static const struct net_device_ops loopback_ops = {
.ndo_set_mac_address = eth_mac_addr,
};
-/* The loopback device is special. There is only one instance
- * per network namespace.
- */
-static void loopback_setup(struct net_device *dev)
+static void gen_lo_setup(struct net_device *dev,
+ unsigned int mtu,
+ const struct ethtool_ops *eth_ops,
+ const struct header_ops *hdr_ops,
+ const struct net_device_ops *dev_ops,
+ void (*dev_destructor)(struct net_device *dev))
{
- dev->mtu = 64 * 1024;
+ dev->mtu = mtu;
dev->hard_header_len = ETH_HLEN; /* 14 */
dev->min_header_len = ETH_HLEN; /* 14 */
dev->addr_len = ETH_ALEN; /* 6 */
@@ -174,11 +183,20 @@ static void loopback_setup(struct net_device *dev)
| NETIF_F_NETNS_LOCAL
| NETIF_F_VLAN_CHALLENGED
| NETIF_F_LOOPBACK;
- dev->ethtool_ops = &loopback_ethtool_ops;
- dev->header_ops = ð_header_ops;
- dev->netdev_ops = &loopback_ops;
+ dev->ethtool_ops = eth_ops;
+ dev->header_ops = hdr_ops;
+ dev->netdev_ops = dev_ops;
dev->needs_free_netdev = true;
- dev->priv_destructor = loopback_dev_free;
+ dev->priv_destructor = dev_destructor;
+}
+
+/* The loopback device is special. There is only one instance
+ * per network namespace.
+ */
+static void loopback_setup(struct net_device *dev)
+{
+ gen_lo_setup(dev, (64 * 1024), &loopback_ethtool_ops, ð_header_ops,
+ &loopback_ops, loopback_dev_free);
}
/* Setup and register the loopback device. */
@@ -213,3 +231,43 @@ static __net_init int loopback_net_init(struct net *net)
struct pernet_operations __net_initdata loopback_net_ops = {
.init = loopback_net_init,
};
+
+/* blackhole netdevice */
+static netdev_tx_t blackhole_netdev_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ kfree_skb(skb);
+ net_warn_ratelimited("%s(): Dropping skb.\n", __func__);
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops blackhole_netdev_ops = {
+ .ndo_start_xmit = blackhole_netdev_xmit,
+};
+
+/* This is a dst-dummy device used specifically for invalidated
+ * DSTs and unlike loopback, this is not per-ns.
+ */
+static void blackhole_netdev_setup(struct net_device *dev)
+{
+ gen_lo_setup(dev, ETH_MIN_MTU, NULL, NULL, &blackhole_netdev_ops, NULL);
+}
+
+/* Setup and register the blackhole_netdev. */
+static int __init blackhole_netdev_init(void)
+{
+ blackhole_netdev = alloc_netdev(0, "blackhole_dev", NET_NAME_UNKNOWN,
+ blackhole_netdev_setup);
+ if (!blackhole_netdev)
+ return -ENOMEM;
+
+ dev_init_scheduler(blackhole_netdev);
+ dev_activate(blackhole_netdev);
+
+ blackhole_netdev->flags |= IFF_UP | IFF_RUNNING;
+ dev_net_set(blackhole_netdev, &init_net);
+
+ return 0;
+}
+
+device_initcall(blackhole_netdev_init);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index eeacebd7debb..88292953aa6f 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4870,4 +4870,6 @@ do { \
#define PTYPE_HASH_SIZE (16)
#define PTYPE_HASH_MASK (PTYPE_HASH_SIZE - 1)
+extern struct net_device *blackhole_netdev;
+
#endif /* _LINUX_NETDEVICE_H */
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH next 0/3] blackhole device to invalidate dst
From: Mahesh Bandewar @ 2019-06-22 0:45 UTC (permalink / raw)
To: Netdev
Cc: Eric Dumazet, David Miller, Michael Chan, Daniel Axtens,
Mahesh Bandewar, Mahesh Bandewar
When we invalidate dst or mark it "dead", we assign 'lo' to
dst->dev. First of all this assignment is racy and more over,
it has MTU implications.
The standard dev MTU is 1500 while the Loopback MTU is 64k. TCP
code when dereferencing the dst don't check if the dst is valid
or not. TCP when dereferencing a dead-dst while negotiating a
new connection, may use dst device which is 'lo' instead of
using the correct device. Consider the following scenario:
A SYN arrives on an interface and tcp-layer while processing
SYNACK finds a dst and associates it with SYNACK skb. Now before
skb gets passed to L3 for processing, if that dst gets "dead"
(because of the virtual device getting disappeared & then reappeared),
the 'lo' gets assigned to that dst (lo MTU = 64k). Let's assume
the SYN has ADV_MSS set as 9k while the output device through
which this SYNACK is going to go out has standard MTU of 1500.
The MTU check during the route check passes since MIN(9K, 64K)
is 9k and TCP successfully negotiates 9k MSS. The subsequent
data packet; bigger in size gets passed to the device and it
won't be marked as GSO since the assumed MTU of the device is
9k.
This either crashes the NIC and we have seen fixes that went
into drivers to handle this scenario. 8914a595110a ('bnx2x:
disable GSO where gso_size is too big for hardware') and
2b16f048729b ('net: create skb_gso_validate_mac_len()') and
with those fixes TCP eventually recovers but not before
few dropped segments.
Well, I'm not a TCP expert and though we have experienced
these corner cases in our environment, I could not reproduce
this case reliably in my test setup to try this fix myself.
However, Michael Chan <michael.chan@broadcom.com> had a setup
where these fixes helped him mitigate the issue and not cause
the crash.
The idea here is to not alter the data-path with additional
locks or smb()/rmb() barriers to avoid racy assignments but
to create a new device that has really low MTU that has
.ndo_start_xmit essentially a kfree_skb(). Make use of this
device instead of 'lo' when marking the dst dead.
First patch implements the blackhole device and second
patch uses it in IPv4 and IPv6 stack while the third patch
is the self test that ensures the sanity of this device.
Mahesh Bandewar (3):
loopback: create blackhole net device similar to loopack.
blackhole_netdev: use blackhole_netdev to invalidate dst entries
blackhole_dev: add a selftest
drivers/net/loopback.c | 76 +++++++++++--
include/linux/netdevice.h | 2 +
lib/Kconfig.debug | 9 ++
lib/Makefile | 1 +
lib/test_blackhole_dev.c | 100 ++++++++++++++++++
net/core/dst.c | 2 +-
net/ipv4/route.c | 3 +-
net/ipv6/route.c | 2 +-
tools/testing/selftests/net/Makefile | 3 +-
tools/testing/selftests/net/config | 1 +
.../selftests/net/test_blackhole_dev.sh | 11 ++
11 files changed, 196 insertions(+), 14 deletions(-)
create mode 100644 lib/test_blackhole_dev.c
create mode 100755 tools/testing/selftests/net/test_blackhole_dev.sh
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply
* Re: Removing skb_orphan() from ip_rcv_core()
From: Eric Dumazet @ 2019-06-22 0:36 UTC (permalink / raw)
To: Joe Stringer, Florian Westphal
Cc: netdev, john fastabend, Daniel Borkmann, Lorenz Bauer,
Jakub Sitnicki, Paolo Abeni
In-Reply-To: <CAOftzPisP-3jN8drC6RXcTigXJjdwEnvTRvTHR-Kv4LKn4rhQQ@mail.gmail.com>
On 6/21/19 10:58 AM, Joe Stringer wrote:
> Hi folks, picking this up again..
>
> As discussed during LSFMM, I've been looking at adding something like
> an `skb_sk_assign()` helper to BPF so that logic similar to TPROXY can
> be implemented with integration into other BPF logic, however
> currently any attempts to do so are blocked by the skb_orphan() call
> in ip_rcv_core() (which will effectively ignore any socket assign
> decision made by the TC BPF program).
>
> Recently I was attempting to remove the skb_orphan() call, and I've
> been trying different things but there seems to be some context I'm
> missing. Here's the core of the patch:
>
> diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
> index ed97724c5e33..16aea980318a 100644
> --- a/net/ipv4/ip_input.c
> +++ b/net/ipv4/ip_input.c
> @@ -500,8 +500,6 @@ static struct sk_buff *ip_rcv_core(struct sk_buff
> *skb, struct net *net)
> memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
> IPCB(skb)->iif = skb->skb_iif;
>
> - /* Must drop socket now because of tproxy. */
> - skb_orphan(skb);
>
> return skb;
>
> The statement that the socket must be dropped because of tproxy
> doesn't make sense to me, because the PRE_ROUTING hook is hit after
> this, which will call into the tproxy logic and eventually
> nf_tproxy_assign_sock() which already does the skb_orphan() itself.
>
> However, if I drop these lines then I end up causing sockets to
> release references too many times. Seems like if we don't orphan the
> skb here, then later logic assumes that we have one more reference
> than we actually have, and decrements the count when it shouldn't
> (perhaps the skb_steal_sock() call in __inet_lookup_skb() which seems
> to assume we always have a reference to the socket?)
>
> Splat:
>
> refcount_t hit zero at sk_stop_timer+0x2c/0x30 in cilium-agent[16359],
> uid/euid: 0/0
> WARNING: CPU: 0 PID: 16359 at kernel/panic.c:686 refcount_error_report+0x9c/0xa1
> ...
> ? inet_put_port+0xa6/0xd0
> inet_csk_clear_xmit_timers+0x2e/0x50
> tcp_done+0x8b/0xf0
> tcp_reset+0x49/0xc0
> tcp_validate_incoming+0x2f7/0x410
> tcp_rcv_state_process+0x250/0xdb6
> ? tcp_v4_connect+0x46f/0x4e0
> tcp_v4_do_rcv+0xbd/0x1f0
> __release_sock+0x84/0xd0
> release_sock+0x30/0xa0
> inet_stream_connect+0x47/0x60
>
> (Full version: https://gist.github.com/joestringer/d5313e4bf4231e2c46405bd7a3053936
> )
>
> This seems potentially related to some of the socket referencing
> discussion in the peer thread "[RFC bpf-next 0/7] Programming socket
> lookup with BPF".
>
> During LSFMM, it seemed like no-one knew quite why the skb_orphan() is
> necessary in that path in the current version of the code, and that we
> may be able to remove it. Florian, I know you weren't in the room for
> that discussion, so raising it again now with a stack trace, Do you
> have some sense what's going on here and whether there's a path
> towards removing it from this path or allowing the skb->sk to be
> retained during ip_rcv() in some conditions?
>
I guess you might missed part of the LSFMM discussion :
We need to make sure skb_orphan() is called from any virtual
driver ndo_start_xmit() that re-injects packet back to the stack.
loopback driver has it, but probably some driver lacks it.
^ permalink raw reply
* [PATCH V34 23/29] bpf: Restrict bpf when kernel lockdown is in confidentiality mode
From: Matthew Garrett @ 2019-06-22 0:03 UTC (permalink / raw)
To: jmorris
Cc: linux-security-module, linux-kernel, linux-api, David Howells,
Alexei Starovoitov, Matthew Garrett, netdev, Chun-Yi Lee,
Daniel Borkmann
In-Reply-To: <20190622000358.19895-1-matthewgarrett@google.com>
From: David Howells <dhowells@redhat.com>
There are some bpf functions can be used to read kernel memory:
bpf_probe_read, bpf_probe_write_user and bpf_trace_printk. These allow
private keys in kernel memory (e.g. the hibernation image signing key) to
be read by an eBPF program and kernel memory to be altered without
restriction. Disable them if the kernel has been locked down in
confidentiality mode.
Suggested-by: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Matthew Garrett <mjg59@google.com>
cc: netdev@vger.kernel.org
cc: Chun-Yi Lee <jlee@suse.com>
cc: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
include/linux/security.h | 1 +
kernel/trace/bpf_trace.c | 20 +++++++++++++++++++-
security/lockdown/lockdown.c | 1 +
3 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/include/linux/security.h b/include/linux/security.h
index e6e3e2403474..de0d37b1fe79 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -97,6 +97,7 @@ enum lockdown_reason {
LOCKDOWN_INTEGRITY_MAX,
LOCKDOWN_KCORE,
LOCKDOWN_KPROBES,
+ LOCKDOWN_BPF_READ,
LOCKDOWN_CONFIDENTIALITY_MAX,
};
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index d64c00afceb5..638f9b00a8df 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -137,6 +137,10 @@ BPF_CALL_3(bpf_probe_read, void *, dst, u32, size, const void *, unsafe_ptr)
{
int ret;
+ ret = security_locked_down(LOCKDOWN_BPF_READ);
+ if (ret)
+ return ret;
+
ret = probe_kernel_read(dst, unsafe_ptr, size);
if (unlikely(ret < 0))
memset(dst, 0, size);
@@ -156,6 +160,12 @@ static const struct bpf_func_proto bpf_probe_read_proto = {
BPF_CALL_3(bpf_probe_write_user, void *, unsafe_ptr, const void *, src,
u32, size)
{
+ int ret;
+
+ ret = security_locked_down(LOCKDOWN_BPF_READ);
+ if (ret)
+ return ret;
+
/*
* Ensure we're in user context which is safe for the helper to
* run. This helper has no business in a kthread.
@@ -205,7 +215,11 @@ BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
int fmt_cnt = 0;
u64 unsafe_addr;
char buf[64];
- int i;
+ int i, ret;
+
+ ret = security_locked_down(LOCKDOWN_BPF_READ);
+ if (ret)
+ return ret;
/*
* bpf_check()->check_func_arg()->check_stack_boundary()
@@ -534,6 +548,10 @@ BPF_CALL_3(bpf_probe_read_str, void *, dst, u32, size,
{
int ret;
+ ret = security_locked_down(LOCKDOWN_BPF_READ);
+ if (ret)
+ return ret;
+
/*
* The strncpy_from_unsafe() call will likely not fill the entire
* buffer, but that's okay in this circumstance as we're probing
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
index 5a08c17f224d..2eea2cc13117 100644
--- a/security/lockdown/lockdown.c
+++ b/security/lockdown/lockdown.c
@@ -33,6 +33,7 @@ static char *lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
[LOCKDOWN_INTEGRITY_MAX] = "integrity",
[LOCKDOWN_KCORE] = "/proc/kcore access",
[LOCKDOWN_KPROBES] = "use of kprobes",
+ [LOCKDOWN_BPF_READ] = "use of bpf to read kernel RAM",
[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
};
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH net-next] rtnetlink: skip metrics loop for dst_default_metrics
From: David Ahern @ 2019-06-21 23:27 UTC (permalink / raw)
To: davem; +Cc: netdev, David Ahern
From: David Ahern <dsahern@gmail.com>
dst_default_metrics has all of the metrics initialized to 0, so nothing
will be added to the skb in rtnetlink_put_metrics. Avoid the loop if
metrics is from dst_default_metrics.
Signed-off-by: David Ahern <dsahern@gmail.com>
---
net/core/rtnetlink.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 8ac81630ab5c..1ee6460f8275 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -751,6 +751,10 @@ int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics)
struct nlattr *mx;
int i, valid = 0;
+ /* nothing is dumped for dst_default_metrics, so just skip the loop */
+ if (metrics == dst_default_metrics.metrics)
+ return 0;
+
mx = nla_nest_start_noflag(skb, RTA_METRICS);
if (mx == NULL)
return -ENOBUFS;
--
2.11.0
^ permalink raw reply related
* [RFC PATCH 3/6] bpf: keep bpf.h in sync with tools/
From: Brian Vazquez @ 2019-06-21 23:16 UTC (permalink / raw)
To: Brian Vazquez, Alexei Starovoitov, Daniel Borkmann,
David S . Miller
Cc: Stanislav Fomichev, Willem de Bruijn, Petar Penkov, linux-kernel,
netdev, bpf, Brian Vazquez
In-Reply-To: <20190621231650.32073-1-brianvv@google.com>
Adds bpf_attr.dump structure to libbpf.
Suggested-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Brian Vazquez <brianvv@google.com>
---
tools/include/uapi/linux/bpf.h | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index b077507efa3f3..1d753958874df 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -106,6 +106,7 @@ enum bpf_cmd {
BPF_TASK_FD_QUERY,
BPF_MAP_LOOKUP_AND_DELETE_ELEM,
BPF_MAP_FREEZE,
+ BPF_MAP_DUMP,
};
enum bpf_map_type {
@@ -385,6 +386,14 @@ union bpf_attr {
__u64 flags;
};
+ struct { /* struct used by BPF_MAP_DUMP command */
+ __u32 map_fd;
+ __aligned_u64 prev_key;
+ __aligned_u64 buf;
+ __aligned_u64 buf_len; /* input/output: len of buf */
+ __u64 flags;
+ } dump;
+
struct { /* anonymous struct used by BPF_PROG_LOAD command */
__u32 prog_type; /* one of enum bpf_prog_type */
__u32 insn_cnt;
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [RFC PATCH 2/6] bpf: add BPF_MAP_DUMP command to access more than one entry per call
From: Brian Vazquez @ 2019-06-21 23:16 UTC (permalink / raw)
To: Brian Vazquez, Alexei Starovoitov, Daniel Borkmann,
David S . Miller
Cc: Stanislav Fomichev, Willem de Bruijn, Petar Penkov, linux-kernel,
netdev, bpf, Brian Vazquez
In-Reply-To: <20190621231650.32073-1-brianvv@google.com>
This introduces a new command to retrieve a variable number of entries
from a bpf map wrapping the existing bpf methods:
map_get_next_key and map_lookup_elem
Note that map_dump doesn't guarantee that reading the entire table is
consistent since this function is always racing with kernel and user code
but the same behaviour is found when the entire table is walked using
the current interfaces: map_get_next_key + map_lookup_elem.
It is also important to note that when a locked map is provided it is
consistent only for 1 entry at the time, meaning that the buf returned
might or might not be consistent.
Suggested-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Brian Vazquez <brianvv@google.com>
---
include/uapi/linux/bpf.h | 9 ++++
kernel/bpf/syscall.c | 108 +++++++++++++++++++++++++++++++++++++++
2 files changed, 117 insertions(+)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index b077507efa3f3..1d753958874df 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -106,6 +106,7 @@ enum bpf_cmd {
BPF_TASK_FD_QUERY,
BPF_MAP_LOOKUP_AND_DELETE_ELEM,
BPF_MAP_FREEZE,
+ BPF_MAP_DUMP,
};
enum bpf_map_type {
@@ -385,6 +386,14 @@ union bpf_attr {
__u64 flags;
};
+ struct { /* struct used by BPF_MAP_DUMP command */
+ __u32 map_fd;
+ __aligned_u64 prev_key;
+ __aligned_u64 buf;
+ __aligned_u64 buf_len; /* input/output: len of buf */
+ __u64 flags;
+ } dump;
+
struct { /* anonymous struct used by BPF_PROG_LOAD command */
__u32 prog_type; /* one of enum bpf_prog_type */
__u32 insn_cnt;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index a1823a50f9be0..7653346b5cfd1 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -1097,6 +1097,111 @@ static int map_get_next_key(union bpf_attr *attr)
return err;
}
+/* last field in 'union bpf_attr' used by this command */
+#define BPF_MAP_DUMP_LAST_FIELD dump.buf_len
+
+static int map_dump(union bpf_attr *attr)
+{
+ void __user *ukey = u64_to_user_ptr(attr->dump.prev_key);
+ void __user *ubuf = u64_to_user_ptr(attr->dump.buf);
+ u32 __user *ubuf_len = u64_to_user_ptr(attr->dump.buf_len);
+ int ufd = attr->dump.map_fd;
+ struct bpf_map *map;
+ void *buf, *prev_key, *key, *value;
+ u32 value_size, elem_size, buf_len, cp_len;
+ struct fd f;
+ int err;
+
+ if (CHECK_ATTR(BPF_MAP_DUMP))
+ return -EINVAL;
+
+ attr->flags = 0;
+ if (attr->dump.flags & ~BPF_F_LOCK)
+ return -EINVAL;
+
+ f = fdget(ufd);
+ map = __bpf_map_get(f);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+ if (!(map_get_sys_perms(map, f) & FMODE_CAN_READ)) {
+ err = -EPERM;
+ goto err_put;
+ }
+
+ if ((attr->dump.flags & BPF_F_LOCK) &&
+ !map_value_has_spin_lock(map)) {
+ err = -EINVAL;
+ goto err_put;
+ }
+
+ if (map->map_type == BPF_MAP_TYPE_QUEUE ||
+ map->map_type == BPF_MAP_TYPE_STACK) {
+ err = -ENOTSUPP;
+ goto err_put;
+ }
+
+ value_size = bpf_map_value_size(map);
+
+ err = get_user(buf_len, ubuf_len);
+ if (err)
+ goto err_put;
+
+ elem_size = map->key_size + value_size;
+ if (buf_len < elem_size) {
+ err = -EINVAL;
+ goto err_put;
+ }
+
+ if (ukey) {
+ prev_key = __bpf_copy_key(ukey, map->key_size);
+ if (IS_ERR(prev_key)) {
+ err = PTR_ERR(prev_key);
+ goto err_put;
+ }
+ } else {
+ prev_key = NULL;
+ }
+
+ err = -ENOMEM;
+ buf = kmalloc(elem_size, GFP_USER | __GFP_NOWARN);
+ if (!buf)
+ goto err_put;
+
+ key = buf;
+ value = key + map->key_size;
+ for (cp_len = 0; cp_len + elem_size <= buf_len ; cp_len += elem_size) {
+next:
+ if (signal_pending(current)) {
+ err = -EINTR;
+ break;
+ }
+
+ rcu_read_lock();
+ err = map->ops->map_get_next_key(map, prev_key, key);
+ rcu_read_unlock();
+
+ if (err)
+ break;
+
+ if (bpf_map_copy_value(map, key, value, attr->dump.flags))
+ goto next;
+
+ if (copy_to_user(ubuf + cp_len, buf, elem_size))
+ break;
+
+ prev_key = key;
+ }
+
+ if (cp_len)
+ err = 0;
+ if (copy_to_user(ubuf_len, &cp_len, sizeof(cp_len)))
+ err = -EFAULT;
+ kfree(buf);
+err_put:
+ fdput(f);
+ return err;
+}
+
#define BPF_MAP_LOOKUP_AND_DELETE_ELEM_LAST_FIELD value
static int map_lookup_and_delete_elem(union bpf_attr *attr)
@@ -2891,6 +2996,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
case BPF_MAP_LOOKUP_AND_DELETE_ELEM:
err = map_lookup_and_delete_elem(&attr);
break;
+ case BPF_MAP_DUMP:
+ err = map_dump(&attr);
+ break;
default:
err = -EINVAL;
break;
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [RFC PATCH 4/6] libbpf: support BPF_MAP_DUMP command
From: Brian Vazquez @ 2019-06-21 23:16 UTC (permalink / raw)
To: Brian Vazquez, Alexei Starovoitov, Daniel Borkmann,
David S . Miller
Cc: Stanislav Fomichev, Willem de Bruijn, Petar Penkov, linux-kernel,
netdev, bpf, Brian Vazquez
In-Reply-To: <20190621231650.32073-1-brianvv@google.com>
Make libbpf aware of new BPF_MAP_DUMP command and add bpf_map_dump and
bpf_map_dump_flags to use them from the library.
Suggested-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Brian Vazquez <brianvv@google.com>
---
tools/lib/bpf/bpf.c | 28 ++++++++++++++++++++++++++++
tools/lib/bpf/bpf.h | 4 ++++
tools/lib/bpf/libbpf.map | 2 ++
3 files changed, 34 insertions(+)
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index c7d7993c44bb0..c1139b7db756a 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -368,6 +368,34 @@ int bpf_map_update_elem(int fd, const void *key, const void *value,
return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}
+int bpf_map_dump(int fd, const void *prev_key, void *buf, void *buf_len)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.dump.map_fd = fd;
+ attr.dump.prev_key = ptr_to_u64(prev_key);
+ attr.dump.buf = ptr_to_u64(buf);
+ attr.dump.buf_len = ptr_to_u64(buf_len);
+
+ return sys_bpf(BPF_MAP_DUMP, &attr, sizeof(attr));
+}
+
+int bpf_map_dump_flags(int fd, const void *prev_key, void *buf, void *buf_len,
+ __u64 flags)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.dump.map_fd = fd;
+ attr.dump.prev_key = ptr_to_u64(prev_key);
+ attr.dump.buf = ptr_to_u64(buf);
+ attr.dump.buf_len = ptr_to_u64(buf_len);
+ attr.dump.flags = flags;
+
+ return sys_bpf(BPF_MAP_DUMP, &attr, sizeof(attr));
+}
+
int bpf_map_lookup_elem(int fd, const void *key, void *value)
{
union bpf_attr attr;
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index ff42ca043dc8f..86496443440e9 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -112,6 +112,10 @@ LIBBPF_API int bpf_verify_program(enum bpf_prog_type type,
LIBBPF_API int bpf_map_update_elem(int fd, const void *key, const void *value,
__u64 flags);
+LIBBPF_API int bpf_map_dump(int fd, const void *prev_key, void *buf,
+ void *buf_len);
+LIBBPF_API int bpf_map_dump_flags(int fd, const void *prev_key, void *buf,
+ void *buf_len, __u64 flags);
LIBBPF_API int bpf_map_lookup_elem(int fd, const void *key, void *value);
LIBBPF_API int bpf_map_lookup_elem_flags(int fd, const void *key, void *value,
__u64 flags);
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 2c6d835620d25..e7641773cfb0f 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -173,4 +173,6 @@ LIBBPF_0.0.4 {
btf__parse_elf;
bpf_object__load_xattr;
libbpf_num_possible_cpus;
+ bpf_map_dump;
+ bpf_map_dump_flags;
} LIBBPF_0.0.3;
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [RFC PATCH 5/6] selftests/bpf: test BPF_MAP_DUMP command on a bpf hashmap
From: Brian Vazquez @ 2019-06-21 23:16 UTC (permalink / raw)
To: Brian Vazquez, Alexei Starovoitov, Daniel Borkmann,
David S . Miller
Cc: Stanislav Fomichev, Willem de Bruijn, Petar Penkov, linux-kernel,
netdev, bpf, Brian Vazquez
In-Reply-To: <20190621231650.32073-1-brianvv@google.com>
This tests exercise the new command on a bpf hashmap and make sure it
works as expected.
Signed-off-by: Brian Vazquez <brianvv@google.com>
---
tools/testing/selftests/bpf/test_maps.c | 70 ++++++++++++++++++++++++-
1 file changed, 68 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
index a3fbc571280a9..3df72b46fd1d9 100644
--- a/tools/testing/selftests/bpf/test_maps.c
+++ b/tools/testing/selftests/bpf/test_maps.c
@@ -309,6 +309,73 @@ static void test_hashmap_walk(unsigned int task, void *data)
close(fd);
}
+static void test_hashmap_dump(void)
+{
+ int fd, i, max_entries = 3;
+ uint64_t keys[max_entries], values[max_entries];
+ uint64_t key, value, next_key;
+ bool next_key_valid = true;
+ void *buf, *elem, *prev_key;
+ u32 buf_len;
+ const int elem_size = sizeof(key) + sizeof(value);
+
+ fd = helper_fill_hashmap(max_entries);
+
+ // Get the elements in the hashmap, and store them in that order
+ assert(bpf_map_get_next_key(fd, NULL, &key) == 0);
+ i = 0;
+ keys[i] = key;
+ for (i = 1; next_key_valid; i++) {
+ next_key_valid = bpf_map_get_next_key(fd, &key, &next_key) == 0;
+ assert(bpf_map_lookup_elem(fd, &key, &values[i - 1]) == 0);
+ keys[i-1] = key;
+ key = next_key;
+ }
+
+ // Alloc memory for the whole table
+ buf = malloc(elem_size * max_entries);
+ assert(buf != NULL);
+
+ // Check that buf_len < elem_size returns EINVAL
+ buf_len = elem_size-1;
+ errno = 0;
+ assert(bpf_map_dump(fd, NULL, buf, &buf_len) == -1 && errno == EINVAL);
+
+ // Check that it returns the first two elements
+ errno = 0;
+ buf_len = elem_size * 2;
+ prev_key = NULL;
+ i = 0;
+ assert(bpf_map_dump(fd, prev_key, buf, &buf_len) == 0 &&
+ buf_len == 2*elem_size);
+ elem = buf;
+ assert((*(uint64_t *)elem) == keys[i] &&
+ (*(uint64_t *)(elem + sizeof(key))) == values[i]);
+ elem = buf + elem_size;
+ i++;
+ assert((*(uint64_t *)elem) == keys[i] &&
+ (*(uint64_t *)(elem + sizeof(key))) == values[i]);
+ i++;
+
+ /* Continue reading from map and verify buf_len only contains 1 element
+ * even though buf_len is 2 elem_size.
+ */
+ prev_key = elem;
+ assert(bpf_map_dump(fd, prev_key, buf, &buf_len) == 0 &&
+ buf_len == elem_size);
+ elem = buf;
+ assert((*(uint64_t *)elem) == keys[i] &&
+ (*(uint64_t *)(elem + sizeof(key))) == values[i]);
+
+ // Check that there are no more entries after last_key
+ prev_key = &keys[i];
+ assert(bpf_map_dump(fd, prev_key, buf, &buf_len) == -1 &&
+ errno == ENOENT);
+
+ free(buf);
+ close(fd);
+}
+
static void test_hashmap_zero_seed(void)
{
int i, first, second, old_flags;
@@ -1668,6 +1735,7 @@ static void run_all_tests(void)
test_hashmap_percpu(0, NULL);
test_hashmap_walk(0, NULL);
test_hashmap_zero_seed();
+ test_hashmap_dump();
test_arraymap(0, NULL);
test_arraymap_percpu(0, NULL);
@@ -1705,11 +1773,9 @@ int main(void)
map_flags = BPF_F_NO_PREALLOC;
run_all_tests();
-
#define CALL
#include <map_tests/tests.h>
#undef CALL
-
printf("test_maps: OK, %d SKIPPED\n", skips);
return 0;
}
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [RFC PATCH 6/6] selftests/bpf: add test to measure performance of BPF_MAP_DUMP
From: Brian Vazquez @ 2019-06-21 23:16 UTC (permalink / raw)
To: Brian Vazquez, Alexei Starovoitov, Daniel Borkmann,
David S . Miller
Cc: Stanislav Fomichev, Willem de Bruijn, Petar Penkov, linux-kernel,
netdev, bpf, Brian Vazquez
In-Reply-To: <20190621231650.32073-1-brianvv@google.com>
This tests compares the amount of time that takes to read an entire
table of 100K elements on a bpf hashmap using both BPF_MAP_DUMP and
BPF_MAP_GET_NEXT_KEY + BPF_MAP_LOOKUP_ELEM.
Signed-off-by: Brian Vazquez <brianvv@google.com>
---
tools/testing/selftests/bpf/test_maps.c | 71 +++++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
index 3df72b46fd1d9..61050272c20ee 100644
--- a/tools/testing/selftests/bpf/test_maps.c
+++ b/tools/testing/selftests/bpf/test_maps.c
@@ -18,6 +18,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/bpf.h>
+#include <linux/time64.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
@@ -376,6 +377,75 @@ static void test_hashmap_dump(void)
close(fd);
}
+static void test_hashmap_dump_perf(void)
+{
+ int fd, i, max_entries = 100000;
+ uint64_t key, value, next_key;
+ bool next_key_valid = true;
+ void *buf;
+ u32 buf_len, entries;
+ int j, k = 0;
+ int num_ent, off;
+ int clk_id = CLOCK_MONOTONIC;
+ struct timespec begin, end;
+ long long time_spent, dump_time_spent;
+ double res;
+ int tests[] = {1, 2, 230, 5000, 73000, 100000, 234567};
+ int test_len = ARRAY_SIZE(tests);
+ const int elem_size = sizeof(key) + sizeof(value);
+
+ fd = helper_fill_hashmap(max_entries);
+ // Alloc memory considering the largest buffer
+ buf = malloc(elem_size * tests[test_len-1]);
+ assert(buf != NULL);
+
+test:
+ entries = tests[k];
+ buf_len = elem_size*tests[k];
+ k++;
+ clock_gettime(clk_id, &begin);
+ errno = 0;
+ i = 0;
+ while (errno == 0) {
+ bpf_map_dump(fd, !i ? NULL : &key,
+ buf, &buf_len);
+ if (errno)
+ break;
+ num_ent = buf_len / elem_size;
+ for (j = 0, off = 0; j < num_ent; j++) {
+ key = *((uint64_t *)(buf + off));
+ off += sizeof(key);
+ value = *((uint64_t *)(buf + off));
+ off += sizeof(value);
+ }
+ i += num_ent;
+ }
+ clock_gettime(clk_id, &end);
+ assert(i == max_entries);
+ dump_time_spent = NSEC_PER_SEC * (end.tv_sec - begin.tv_sec) +
+ end.tv_nsec - begin.tv_nsec;
+ next_key_valid = true;
+ clock_gettime(clk_id, &begin);
+ assert(bpf_map_get_next_key(fd, NULL, &key) == 0);
+ for (i = 0; next_key_valid; i++) {
+ next_key_valid = bpf_map_get_next_key(fd, &key, &next_key) == 0;
+ assert(bpf_map_lookup_elem(fd, &key, &value) == 0);
+ key = next_key;
+ }
+ clock_gettime(clk_id, &end);
+ time_spent = NSEC_PER_SEC * (end.tv_sec - begin.tv_sec) +
+ end.tv_nsec - begin.tv_nsec;
+ res = (1-((double)dump_time_spent/time_spent))*100;
+ printf("buf_len_%u:\t %llu entry-by-entry: %llu improvement %lf\n",
+ entries, dump_time_spent, time_spent, res);
+ assert(i == max_entries);
+
+ if (k < test_len)
+ goto test;
+ free(buf);
+ close(fd);
+}
+
static void test_hashmap_zero_seed(void)
{
int i, first, second, old_flags;
@@ -1736,6 +1806,7 @@ static void run_all_tests(void)
test_hashmap_walk(0, NULL);
test_hashmap_zero_seed();
test_hashmap_dump();
+ test_hashmap_dump_perf();
test_arraymap(0, NULL);
test_arraymap_percpu(0, NULL);
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [RFC PATCH 1/6] bpf: add bpf_map_value_size and bp_map_copy_value helper functions
From: Brian Vazquez @ 2019-06-21 23:16 UTC (permalink / raw)
To: Brian Vazquez, Alexei Starovoitov, Daniel Borkmann,
David S . Miller
Cc: Stanislav Fomichev, Willem de Bruijn, Petar Penkov, linux-kernel,
netdev, bpf, Brian Vazquez
In-Reply-To: <20190621231650.32073-1-brianvv@google.com>
Move reusable code from map_lookup_elem to helper functions to avoid code
duplication in kernel/bpf/syscall.c
Suggested-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Brian Vazquez <brianvv@google.com>
---
kernel/bpf/syscall.c | 134 +++++++++++++++++++++++--------------------
1 file changed, 73 insertions(+), 61 deletions(-)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 7713cf39795a4..a1823a50f9be0 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -126,6 +126,76 @@ static struct bpf_map *find_and_alloc_map(union bpf_attr *attr)
return map;
}
+static u32 bpf_map_value_size(struct bpf_map *map)
+{
+ if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||
+ map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH ||
+ map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY ||
+ map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE)
+ return round_up(map->value_size, 8) * num_possible_cpus();
+ else if (IS_FD_MAP(map))
+ return sizeof(u32);
+ else
+ return map->value_size;
+}
+
+static int bpf_map_copy_value(struct bpf_map *map, void *key, void *value,
+ __u64 flags)
+{
+ void *ptr;
+ int err;
+
+ if (bpf_map_is_dev_bound(map))
+ return bpf_map_offload_lookup_elem(map, key, value);
+
+ preempt_disable();
+ this_cpu_inc(bpf_prog_active);
+ if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||
+ map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) {
+ err = bpf_percpu_hash_copy(map, key, value);
+ } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) {
+ err = bpf_percpu_array_copy(map, key, value);
+ } else if (map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) {
+ err = bpf_percpu_cgroup_storage_copy(map, key, value);
+ } else if (map->map_type == BPF_MAP_TYPE_STACK_TRACE) {
+ err = bpf_stackmap_copy(map, key, value);
+ } else if (IS_FD_ARRAY(map)) {
+ err = bpf_fd_array_map_lookup_elem(map, key, value);
+ } else if (IS_FD_HASH(map)) {
+ err = bpf_fd_htab_map_lookup_elem(map, key, value);
+ } else if (map->map_type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) {
+ err = bpf_fd_reuseport_array_lookup_elem(map, key, value);
+ } else if (map->map_type == BPF_MAP_TYPE_QUEUE ||
+ map->map_type == BPF_MAP_TYPE_STACK) {
+ err = map->ops->map_peek_elem(map, value);
+ } else {
+ rcu_read_lock();
+ if (map->ops->map_lookup_elem_sys_only)
+ ptr = map->ops->map_lookup_elem_sys_only(map, key);
+ else
+ ptr = map->ops->map_lookup_elem(map, key);
+ if (IS_ERR(ptr)) {
+ err = PTR_ERR(ptr);
+ } else if (!ptr) {
+ err = -ENOENT;
+ } else {
+ err = 0;
+ if (flags & BPF_F_LOCK)
+ /* lock 'ptr' and copy everything but lock */
+ copy_map_value_locked(map, value, ptr, true);
+ else
+ copy_map_value(map, value, ptr);
+ /* mask lock, since value wasn't zero inited */
+ check_and_init_map_lock(map, value);
+ }
+ rcu_read_unlock();
+ }
+ this_cpu_dec(bpf_prog_active);
+ preempt_enable();
+
+ return err;
+}
+
void *bpf_map_area_alloc(size_t size, int numa_node)
{
/* We really just want to fail instead of triggering OOM killer
@@ -729,7 +799,7 @@ static int map_lookup_elem(union bpf_attr *attr)
void __user *uvalue = u64_to_user_ptr(attr->value);
int ufd = attr->map_fd;
struct bpf_map *map;
- void *key, *value, *ptr;
+ void *key, *value;
u32 value_size;
struct fd f;
int err;
@@ -761,72 +831,14 @@ static int map_lookup_elem(union bpf_attr *attr)
goto err_put;
}
- if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||
- map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH ||
- map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY ||
- map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE)
- value_size = round_up(map->value_size, 8) * num_possible_cpus();
- else if (IS_FD_MAP(map))
- value_size = sizeof(u32);
- else
- value_size = map->value_size;
+ value_size = bpf_map_value_size(map);
err = -ENOMEM;
value = kmalloc(value_size, GFP_USER | __GFP_NOWARN);
if (!value)
goto free_key;
- if (bpf_map_is_dev_bound(map)) {
- err = bpf_map_offload_lookup_elem(map, key, value);
- goto done;
- }
-
- preempt_disable();
- this_cpu_inc(bpf_prog_active);
- if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||
- map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) {
- err = bpf_percpu_hash_copy(map, key, value);
- } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) {
- err = bpf_percpu_array_copy(map, key, value);
- } else if (map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) {
- err = bpf_percpu_cgroup_storage_copy(map, key, value);
- } else if (map->map_type == BPF_MAP_TYPE_STACK_TRACE) {
- err = bpf_stackmap_copy(map, key, value);
- } else if (IS_FD_ARRAY(map)) {
- err = bpf_fd_array_map_lookup_elem(map, key, value);
- } else if (IS_FD_HASH(map)) {
- err = bpf_fd_htab_map_lookup_elem(map, key, value);
- } else if (map->map_type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) {
- err = bpf_fd_reuseport_array_lookup_elem(map, key, value);
- } else if (map->map_type == BPF_MAP_TYPE_QUEUE ||
- map->map_type == BPF_MAP_TYPE_STACK) {
- err = map->ops->map_peek_elem(map, value);
- } else {
- rcu_read_lock();
- if (map->ops->map_lookup_elem_sys_only)
- ptr = map->ops->map_lookup_elem_sys_only(map, key);
- else
- ptr = map->ops->map_lookup_elem(map, key);
- if (IS_ERR(ptr)) {
- err = PTR_ERR(ptr);
- } else if (!ptr) {
- err = -ENOENT;
- } else {
- err = 0;
- if (attr->flags & BPF_F_LOCK)
- /* lock 'ptr' and copy everything but lock */
- copy_map_value_locked(map, value, ptr, true);
- else
- copy_map_value(map, value, ptr);
- /* mask lock, since value wasn't zero inited */
- check_and_init_map_lock(map, value);
- }
- rcu_read_unlock();
- }
- this_cpu_dec(bpf_prog_active);
- preempt_enable();
-
-done:
+ err = bpf_map_copy_value(map, key, value, attr->flags);
if (err)
goto free_value;
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [RFC PATCH 0/6] bpf: add BPF_MAP_DUMP command to access more
From: Brian Vazquez @ 2019-06-21 23:16 UTC (permalink / raw)
To: Brian Vazquez, Alexei Starovoitov, Daniel Borkmann,
David S . Miller
Cc: Stanislav Fomichev, Willem de Bruijn, Petar Penkov, linux-kernel,
netdev, bpf, Brian Vazquez
This introduces a new command to retrieve a variable number of entries
from a bpf map.
This new command can be executed from the existing BPF syscall as follows:
err = bpf(BPF_MAP_DUMP, union bpf_attr *attr, u32 size)
using attr->dump.map_fd, attr->dump.prev_key, attr->dump.buf,
attr->dump.buf_len
returns zero or negative error, and populates buf and buf_len on
succees
This implementation is wrapping the existing bpf methods:
map_get_next_key and map_lookup_elem
the results show that even with a 1-elem_size buffer, it runs ~40 faster
than the current implementation, improvements of ~85% are reported when
the buffer size is increased, although, after the buffer size is around
5% of the total number of entries there's no huge difference in increasing
it.
Tested:
Tried different size buffers to handle case where the bulk is bigger, or
the elements to retrieve are less than the existing ones, all runs read
a map of 100K entries. Below are the results(in ns) from the different
runs:
buf_len_1: 55528939 entry-by-entry: 97244981 improvement 42.897887%
buf_len_2: 34425779 entry-by-entry: 88863122 improvement 61.259769%
buf_len_230: 11700316 entry-by-entry: 88753301 improvement 86.817036%
buf_len_5000: 11615290 entry-by-entry: 88362637 improvement 86.854976%
buf_len_73000: 12083976 entry-by-entry: 89956483 improvement 86.566865%
buf_len_100000: 12638913 entry-by-entry: 89642303 improvement 85.900727%
buf_len_234567: 11873964 entry-by-entry: 89080077 improvement 86.670461%
Suggested-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Brian Vazquez <brianvv@google.com>
Brian Vazquez (6):
bpf: add bpf_map_value_size and bp_map_copy_value helper functions
bpf: add BPF_MAP_DUMP command to access more than one entry per call
bpf: keep bpf.h in sync with tools/
libbpf: support BPF_MAP_DUMP command
selftests/bpf: test BPF_MAP_DUMP command on a bpf hashmap
selftests/bpf: add test to measure performance of BPF_MAP_DUMP
include/uapi/linux/bpf.h | 9 +
kernel/bpf/syscall.c | 242 ++++++++++++++++++------
tools/include/uapi/linux/bpf.h | 9 +
tools/lib/bpf/bpf.c | 28 +++
tools/lib/bpf/bpf.h | 4 +
tools/lib/bpf/libbpf.map | 2 +
tools/testing/selftests/bpf/test_maps.c | 141 +++++++++++++-
7 files changed, 372 insertions(+), 63 deletions(-)
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply
* [RFC PATCH bpf-next] RV32G eBPF JIT
From: Luke Nelson @ 2019-06-21 22:59 UTC (permalink / raw)
Cc: Luke Nelson, Xi Wang, Palmer Dabbelt, Albert Ou,
Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
Yonghong Song, Björn Töpel, linux-riscv, linux-kernel,
netdev, bpf
From: Luke Nelson <luke.r.nels@gmail.com>
This is an eBPF JIT for RV32G, adapted from the JIT for RV64G.
Any feedback would be greatly appreciated.
It passes 359 out of 378 tests in test_bpf.ko. The failing tests are
features that are not supported right now:
- ALU64 DIV/MOD:
These require loops to emulate on 32-bit hardware,
and are not supported on other 32-bit JITs like
ARM32.
- BPF_XADD | BPF_DW:
RV32G does not have atomic instructions for operating
on double words. This is similar to ARM32.
- Tail calls:
I'm working on adding support for these now, but couldn't
find any test cases that use them. What's the best way
of testing tail call code?
- Far branches
These are not supported in RV64G either.
There are two main changes required for this to work compared to the
RV64 JIT.
First, eBPF registers are 64-bit, while RV32G registers are 32-bit.
I take an approach similar to ARM32: most BPF registers map directly to
2 RISC-V registers, while some reside in stack scratch space and must
be saved / restored when used.
Second, many 64-bit ALU operations do not trivially map to 32-bit
operations. Operations that move bits between high and low words, such
as ADD, LSH, MUL, and others must emulate the 64-bit behavior in terms
of 32-bit instructions.
Signed-off-by: Luke Nelson <luke.r.nels@gmail.com>
Cc: Xi Wang <xi.wang@gmail.com>
---
arch/riscv/Kconfig | 2 +-
arch/riscv/net/Makefile | 7 +-
arch/riscv/net/bpf_jit_comp32.c | 1460 +++++++++++++++++++++++++++++++
3 files changed, 1467 insertions(+), 2 deletions(-)
create mode 100644 arch/riscv/net/bpf_jit_comp32.c
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 0c4b12205632..153ff9ee6e6c 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -49,7 +49,7 @@ config RISCV
select GENERIC_IRQ_MULTI_HANDLER
select ARCH_HAS_PTE_SPECIAL
select ARCH_HAS_MMIOWB
- select HAVE_EBPF_JIT if 64BIT
+ select HAVE_EBPF_JIT
config MMU
def_bool y
diff --git a/arch/riscv/net/Makefile b/arch/riscv/net/Makefile
index ec5b14763316..5511fb1dd40e 100644
--- a/arch/riscv/net/Makefile
+++ b/arch/riscv/net/Makefile
@@ -1,2 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o
+
+ifeq ($(CONFIG_ARCH_RV64I),y)
+ obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o
+else
+ obj-$(CONFIG_BPF_JIT) += bpf_jit_comp32.o
+endif
diff --git a/arch/riscv/net/bpf_jit_comp32.c b/arch/riscv/net/bpf_jit_comp32.c
new file mode 100644
index 000000000000..8790fd931aed
--- /dev/null
+++ b/arch/riscv/net/bpf_jit_comp32.c
@@ -0,0 +1,1460 @@
+// SPDX-License-Identifier: GPL-2.0
+/* BPF JIT compiler for RV32G
+ *
+ * Copyright(c) 2019 Luke Nelson <luke.r.nels@gmail.com>
+ * This code is based on the code and ideas from
+ * Björn Töpel <bjorn.topel@gmail.com>,
+ * who wrote the rv64g BPF JIT, and
+ * Shubham Bansal <illusionist.neo@gmail.com> and
+ * Mircea Gherzan <mgherzan@gmail.com>,
+ * who wrote the 32-bit ARM BPF JIT.
+ */
+
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <asm/cacheflush.h>
+
+enum {
+ RV_REG_ZERO = 0, /* The constant value 0 */
+ RV_REG_RA = 1, /* Return address */
+ RV_REG_SP = 2, /* Stack pointer */
+ RV_REG_GP = 3, /* Global pointer */
+ RV_REG_TP = 4, /* Thread pointer */
+ RV_REG_T0 = 5, /* Temporaries */
+ RV_REG_T1 = 6,
+ RV_REG_T2 = 7,
+ RV_REG_FP = 8,
+ RV_REG_S1 = 9, /* Saved registers */
+ RV_REG_A0 = 10, /* Function argument/return values */
+ RV_REG_A1 = 11, /* Function arguments */
+ RV_REG_A2 = 12,
+ RV_REG_A3 = 13,
+ RV_REG_A4 = 14,
+ RV_REG_A5 = 15,
+ RV_REG_A6 = 16,
+ RV_REG_A7 = 17,
+ RV_REG_S2 = 18, /* Saved registers */
+ RV_REG_S3 = 19,
+ RV_REG_S4 = 20,
+ RV_REG_S5 = 21,
+ RV_REG_S6 = 22,
+ RV_REG_S7 = 23,
+ RV_REG_S8 = 24,
+ RV_REG_S9 = 25,
+ RV_REG_S10 = 26,
+ RV_REG_S11 = 27,
+ RV_REG_T3 = 28, /* Temporaries */
+ RV_REG_T4 = 29,
+ RV_REG_T5 = 30,
+ RV_REG_T6 = 31,
+};
+
+enum {
+ /* Stack layout - these are offsets from (top of stack - 4) */
+ BPF_R6_HI,
+ BPF_R6_LO,
+ BPF_R7_HI,
+ BPF_R7_LO,
+ BPF_R8_HI,
+ BPF_R8_LO,
+ BPF_R9_HI,
+ BPF_R9_LO,
+ BPF_TC_HI,
+ BPF_TC_LO,
+ BPF_AX_HI,
+ BPF_AX_LO,
+ /* Stack space for BPF_REG_6, BPF_REG_7, BPF_REG_8, BPF_REG_9,
+ * BPF_REG_AX and tail call counts.
+ */
+ BPF_JIT_SCRATCH_REGS,
+};
+
+#define STACK_OFFSET(k) (-4 - ((k) * 4))
+
+#define TMP_REG_1 (MAX_BPF_JIT_REG + 0) /* TEMP Register 1 */
+#define TMP_REG_2 (MAX_BPF_JIT_REG + 1) /* TEMP Register 2 */
+#define TCALL_CNT (MAX_BPF_JIT_REG + 2) /* Tail Call Count */
+
+static const s8 bpf2rv32[][2] = {
+ /* Return value from in-kernel function, and exit value from eBPF */
+ [BPF_REG_0] = {RV_REG_S2, RV_REG_S1},
+ /* Arguments from eBPF program to in-kernel function */
+ [BPF_REG_1] = {RV_REG_A1, RV_REG_A0},
+ [BPF_REG_2] = {RV_REG_A3, RV_REG_A2},
+ [BPF_REG_3] = {RV_REG_A5, RV_REG_A4},
+ [BPF_REG_4] = {RV_REG_A7, RV_REG_A6},
+ [BPF_REG_5] = {RV_REG_S4, RV_REG_S3},
+ /* Callee saved registers that in-kernel function will preserve */
+ /* Stored on stack scratch space */
+ [BPF_REG_6] = {STACK_OFFSET(BPF_R6_HI), STACK_OFFSET(BPF_R6_LO)},
+ [BPF_REG_7] = {STACK_OFFSET(BPF_R7_HI), STACK_OFFSET(BPF_R7_LO)},
+ [BPF_REG_8] = {STACK_OFFSET(BPF_R8_HI), STACK_OFFSET(BPF_R8_LO)},
+ [BPF_REG_9] = {STACK_OFFSET(BPF_R9_HI), STACK_OFFSET(BPF_R9_LO)},
+ /* Read only Frame Pointer to access Stack */
+ [BPF_REG_FP] = {RV_REG_S6, RV_REG_S5},
+ /* Temporary Register for internal BPF JIT, can be used
+ * for constant blindings and others. Save T0 and T1
+ * for use internal to one instruction.
+ */
+ [TMP_REG_1] = {RV_REG_T3, RV_REG_T2},
+ [TMP_REG_2] = {RV_REG_T5, RV_REG_T4},
+ /* Tail call count. Stored on stack scratch space. */
+ [TCALL_CNT] = {STACK_OFFSET(BPF_TC_HI), STACK_OFFSET(BPF_TC_LO)},
+ /* Temporary register for blinding constants.
+ * Stored on stack scratch space.
+ */
+ [BPF_REG_AX] = {STACK_OFFSET(BPF_AX_HI), STACK_OFFSET(BPF_AX_LO)},
+};
+
+struct rv_jit_context {
+ struct bpf_prog *prog;
+ u32 *insns; /* RV insns */
+ int ninsns;
+ int epilogue_offset;
+ int *offset; /* BPF to RV */
+ unsigned long flags;
+ int stack_size;
+};
+
+struct rv_jit_data {
+ struct bpf_binary_header *header;
+ u8 *image;
+ struct rv_jit_context ctx;
+};
+
+static void emit(const u32 insn, struct rv_jit_context *ctx)
+{
+ if (ctx->insns)
+ ctx->insns[ctx->ninsns] = insn;
+
+ ctx->ninsns++;
+}
+
+static u32 rv_r_insn(u8 funct7, u8 rs2, u8 rs1, u8 funct3, u8 rd, u8 opcode)
+{
+ return (funct7 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) |
+ (rd << 7) | opcode;
+}
+
+static u32 rv_i_insn(u16 imm11_0, u8 rs1, u8 funct3, u8 rd, u8 opcode)
+{
+ return (imm11_0 << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) |
+ opcode;
+}
+
+static u32 rv_s_insn(u16 imm11_0, u8 rs2, u8 rs1, u8 funct3, u8 opcode)
+{
+ u8 imm11_5 = imm11_0 >> 5, imm4_0 = imm11_0 & 0x1f;
+
+ return (imm11_5 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) |
+ (imm4_0 << 7) | opcode;
+}
+
+static u32 rv_sb_insn(u16 imm12_1, u8 rs2, u8 rs1, u8 funct3, u8 opcode)
+{
+ u8 imm12 = ((imm12_1 & 0x800) >> 5) | ((imm12_1 & 0x3f0) >> 4);
+ u8 imm4_1 = ((imm12_1 & 0xf) << 1) | ((imm12_1 & 0x400) >> 10);
+
+ return (imm12 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) |
+ (imm4_1 << 7) | opcode;
+}
+
+static u32 rv_u_insn(u32 imm31_12, u8 rd, u8 opcode)
+{
+ return (imm31_12 << 12) | (rd << 7) | opcode;
+}
+
+static u32 rv_uj_insn(u32 imm20_1, u8 rd, u8 opcode)
+{
+ u32 imm;
+
+ imm = (imm20_1 & 0x80000) | ((imm20_1 & 0x3ff) << 9) |
+ ((imm20_1 & 0x400) >> 2) | ((imm20_1 & 0x7f800) >> 11);
+
+ return (imm << 12) | (rd << 7) | opcode;
+}
+
+static u32 rv_amo_insn(u8 funct5, u8 aq, u8 rl, u8 rs2, u8 rs1,
+ u8 funct3, u8 rd, u8 opcode)
+{
+ u8 funct7 = (funct5 << 2) | (aq << 1) | rl;
+
+ return rv_r_insn(funct7, rs2, rs1, funct3, rd, opcode);
+}
+
+static u32 rv_addi(u8 rd, u8 rs1, u16 imm11_0)
+{
+ return rv_i_insn(imm11_0, rs1, 0, rd, 0x13);
+}
+
+static u32 rv_add(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(0, rs2, rs1, 0, rd, 0x33);
+}
+
+static u32 rv_sub(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(0x20, rs2, rs1, 0, rd, 0x33);
+}
+
+static u32 rv_and(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(0, rs2, rs1, 7, rd, 0x33);
+}
+
+static u32 rv_or(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(0, rs2, rs1, 6, rd, 0x33);
+}
+
+static u32 rv_xor(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(0, rs2, rs1, 4, rd, 0x33);
+}
+
+static u32 rv_mul(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(1, rs2, rs1, 0, rd, 0x33);
+}
+
+static u32 rv_mulhu(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(1, rs2, rs1, 3, rd, 0x33);
+}
+
+static u32 rv_divu(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(1, rs2, rs1, 5, rd, 0x33);
+}
+
+static u32 rv_remu(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(1, rs2, rs1, 7, rd, 0x33);
+}
+
+static u32 rv_sll(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(0, rs2, rs1, 1, rd, 0x33);
+}
+
+static u32 rv_srl(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(0, rs2, rs1, 5, rd, 0x33);
+}
+
+static u32 rv_sra(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(0x20, rs2, rs1, 5, rd, 0x33);
+}
+
+static u32 rv_sltu(u8 rd, u8 rs1, u8 rs2)
+{
+ return rv_r_insn(0, rs2, rs1, 3, rd, 0x33);
+}
+
+static u32 rv_lui(u8 rd, u32 imm31_12)
+{
+ return rv_u_insn(imm31_12, rd, 0x37);
+}
+
+static u32 rv_slli(u8 rd, u8 rs1, u16 imm11_0)
+{
+ return rv_i_insn(imm11_0, rs1, 1, rd, 0x13);
+}
+
+static u32 rv_srli(u8 rd, u8 rs1, u16 imm11_0)
+{
+ return rv_i_insn(imm11_0, rs1, 5, rd, 0x13);
+}
+
+static u32 rv_srai(u8 rd, u8 rs1, u16 imm11_0)
+{
+ return rv_i_insn(0x400 | imm11_0, rs1, 5, rd, 0x13);
+}
+
+static u32 rv_andi(u8 rd, u8 rs1, u16 imm11_0)
+{
+ return rv_i_insn(imm11_0, rs1, 7, rd, 0x13);
+}
+
+static u32 rv_jal(u8 rd, u32 imm20_1)
+{
+ return rv_uj_insn(imm20_1, rd, 0x6f);
+}
+
+static u32 rv_jalr(u8 rd, u8 rs1, u16 imm11_0)
+{
+ return rv_i_insn(imm11_0, rs1, 0, rd, 0x67);
+}
+
+static u32 rv_beq(u8 rs1, u8 rs2, u16 imm12_1)
+{
+ return rv_sb_insn(imm12_1, rs2, rs1, 0, 0x63);
+}
+
+static u32 rv_bltu(u8 rs1, u8 rs2, u16 imm12_1)
+{
+ return rv_sb_insn(imm12_1, rs2, rs1, 6, 0x63);
+}
+
+static u32 rv_bgeu(u8 rs1, u8 rs2, u16 imm12_1)
+{
+ return rv_sb_insn(imm12_1, rs2, rs1, 7, 0x63);
+}
+
+static u32 rv_bne(u8 rs1, u8 rs2, u16 imm12_1)
+{
+ return rv_sb_insn(imm12_1, rs2, rs1, 1, 0x63);
+}
+
+static u32 rv_blt(u8 rs1, u8 rs2, u16 imm12_1)
+{
+ return rv_sb_insn(imm12_1, rs2, rs1, 4, 0x63);
+}
+
+static u32 rv_bge(u8 rs1, u8 rs2, u16 imm12_1)
+{
+ return rv_sb_insn(imm12_1, rs2, rs1, 5, 0x63);
+}
+
+static u32 rv_sb(u8 rs1, u16 imm11_0, u8 rs2)
+{
+ return rv_s_insn(imm11_0, rs2, rs1, 0, 0x23);
+}
+
+static u32 rv_sh(u8 rs1, u16 imm11_0, u8 rs2)
+{
+ return rv_s_insn(imm11_0, rs2, rs1, 1, 0x23);
+}
+
+static u32 rv_sw(u8 rs1, u16 imm11_0, u8 rs2)
+{
+ return rv_s_insn(imm11_0, rs2, rs1, 2, 0x23);
+}
+
+static u32 rv_lbu(u8 rd, u16 imm11_0, u8 rs1)
+{
+ return rv_i_insn(imm11_0, rs1, 4, rd, 0x03);
+}
+
+static u32 rv_lhu(u8 rd, u16 imm11_0, u8 rs1)
+{
+ return rv_i_insn(imm11_0, rs1, 5, rd, 0x03);
+}
+
+static u32 rv_lw(u8 rd, u16 imm11_0, u8 rs1)
+{
+ return rv_i_insn(imm11_0, rs1, 2, rd, 0x03);
+}
+
+static u32 rv_amoadd_w(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
+{
+ return rv_amo_insn(0, aq, rl, rs2, rs1, 2, rd, 0x2f);
+}
+
+static bool is_13b_int(s32 val)
+{
+ return -(1 << 12) <= val && val < (1 << 12);
+}
+
+static bool is_21b_int(s32 val)
+{
+ return -(1L << 20) <= val && val < (1L << 20);
+}
+
+static int is_13b_check(int off, int insn)
+{
+ if (!is_13b_int(off)) {
+ pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n",
+ insn, off);
+ return -1;
+ }
+ return 0;
+}
+
+static int is_21b_check(int off, int insn)
+{
+ if (!is_21b_int(off)) {
+ pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n",
+ insn, off);
+ return -1;
+ }
+ return 0;
+}
+
+static void emit_imm(const s8 rd, s32 imm, struct rv_jit_context *ctx)
+{
+ u32 upper = (imm + (1 << 11)) >> 12;
+ u32 lower = imm & 0xfff;
+
+ if (upper) {
+ emit(rv_lui(rd, upper), ctx);
+ emit(rv_addi(rd, rd, lower), ctx);
+ } else {
+ emit(rv_addi(rd, RV_REG_ZERO, lower), ctx);
+ }
+}
+
+static void emit_imm32(const s8 *rd, s32 imm, struct rv_jit_context *ctx)
+{
+ /* Emit immediate into lower bits */
+ emit_imm(rd[1], imm, ctx);
+
+ /* Sign-extend into upper bits */
+ emit(rv_addi(rd[0], rd[1], 0), ctx);
+ emit(rv_srai(rd[0], rd[0], 31), ctx);
+}
+
+static void emit_imm64(const s8 *rd, s32 imm_hi, s32 imm_lo,
+ struct rv_jit_context *ctx)
+{
+ emit_imm(rd[1], imm_lo, ctx);
+ emit_imm(rd[0], imm_hi, ctx);
+}
+
+static int rv_offset(int bpf_to, int bpf_from, struct rv_jit_context *ctx)
+{
+ int from = ctx->offset[bpf_from] - 1, to = ctx->offset[bpf_to];
+
+ return (to - from) << 2;
+}
+
+static int epilogue_offset(struct rv_jit_context *ctx)
+{
+ int to = ctx->epilogue_offset, from = ctx->ninsns;
+
+ return (to - from) << 2;
+}
+
+static void build_epilogue(struct rv_jit_context *ctx)
+{
+ int stack_adjust = ctx->stack_size, store_offset = stack_adjust - 8;
+ const s8 *r0 = bpf2rv32[BPF_REG_0];
+
+ store_offset -= 4 * BPF_JIT_SCRATCH_REGS;
+
+ /* Set return value. */
+ emit(rv_addi(RV_REG_A0, r0[1], 0), ctx);
+ emit(rv_addi(RV_REG_A1, r0[0], 0), ctx);
+
+ /* Restore callee-saved registers. */
+ emit(rv_lw(RV_REG_RA, store_offset - 0, RV_REG_SP), ctx);
+ emit(rv_lw(RV_REG_FP, store_offset - 4, RV_REG_SP), ctx);
+ emit(rv_lw(RV_REG_S1, store_offset - 8, RV_REG_SP), ctx);
+ emit(rv_lw(RV_REG_S2, store_offset - 12, RV_REG_SP), ctx);
+ emit(rv_lw(RV_REG_S3, store_offset - 16, RV_REG_SP), ctx);
+ emit(rv_lw(RV_REG_S4, store_offset - 20, RV_REG_SP), ctx);
+ emit(rv_lw(RV_REG_S5, store_offset - 24, RV_REG_SP), ctx);
+ emit(rv_lw(RV_REG_S6, store_offset - 28, RV_REG_SP), ctx);
+
+ emit(rv_addi(RV_REG_SP, RV_REG_SP, stack_adjust), ctx);
+ emit(rv_jalr(RV_REG_ZERO, RV_REG_RA, 0), ctx);
+}
+
+static int rv_offset_check(int *rvoff, s16 off, int insn,
+ struct rv_jit_context *ctx)
+{
+ *rvoff = rv_offset(insn + off, insn, ctx);
+ return is_13b_check(*rvoff, insn);
+}
+
+static bool is_stacked(s8 reg)
+{
+ return reg < 0;
+}
+
+static const s8 *rv32_bpf_get_reg64(const s8 *reg, const s8 *tmp,
+ struct rv_jit_context *ctx)
+{
+ if (is_stacked(reg[0])) {
+ emit(rv_lw(tmp[0], reg[0], RV_REG_FP), ctx);
+ emit(rv_lw(tmp[1], reg[1], RV_REG_FP), ctx);
+ reg = tmp;
+ }
+ return reg;
+}
+
+static void rv32_bpf_put_reg64(const s8 *reg, const s8 *src,
+ struct rv_jit_context *ctx)
+{
+ if (is_stacked(reg[0])) {
+ emit(rv_sw(RV_REG_FP, reg[0], src[0]), ctx);
+ emit(rv_sw(RV_REG_FP, reg[1], src[1]), ctx);
+ }
+}
+
+static const s8 *rv32_bpf_get_reg32(const s8 *reg, const s8 *tmp,
+ struct rv_jit_context *ctx)
+{
+ if (is_stacked(reg[1])) {
+ emit(rv_lw(tmp[1], reg[1], RV_REG_FP), ctx);
+ reg = tmp;
+ }
+ return reg;
+}
+
+static void rv32_bpf_put_reg32(const s8 *reg, const s8 *src,
+ struct rv_jit_context *ctx)
+{
+ if (is_stacked(reg[1])) {
+ emit(rv_sw(RV_REG_FP, reg[1], src[1]), ctx);
+ emit(rv_sw(RV_REG_FP, reg[0], RV_REG_ZERO), ctx);
+ } else {
+ emit(rv_addi(reg[0], RV_REG_ZERO, 0), ctx);
+ }
+}
+
+static void emit_rv32_alu_i64(const s8 dst[], s32 imm,
+ struct rv_jit_context *ctx,
+ const u8 op)
+{
+ const s8 *tmp1 = bpf2rv32[TMP_REG_1];
+ const s8 *rd = rv32_bpf_get_reg64(dst, tmp1, ctx);
+
+ switch (op) {
+ case BPF_MOV:
+ emit_imm32(rd, imm, ctx);
+ break;
+ }
+
+ rv32_bpf_put_reg64(dst, rd, ctx);
+}
+
+static void emit_rv32_alu_i32(const s8 dst[], s32 imm,
+ struct rv_jit_context *ctx,
+ const u8 op)
+{
+ const s8 *tmp1 = bpf2rv32[TMP_REG_1];
+ const s8 *rd = rv32_bpf_get_reg32(dst, tmp1, ctx);
+
+ switch (op) {
+ case BPF_MOV:
+ emit_imm32(rd, imm, ctx);
+ break;
+ }
+
+ rv32_bpf_put_reg32(dst, rd, ctx);
+}
+
+static void emit_rv32_alu_r64(const s8 dst[], const s8 src[],
+ struct rv_jit_context *ctx,
+ const u8 op)
+{
+ const s8 *tmp1 = bpf2rv32[TMP_REG_1];
+ const s8 *tmp2 = bpf2rv32[TMP_REG_2];
+ const s8 *rd = rv32_bpf_get_reg64(dst, tmp1, ctx);
+ const s8 *rs = rv32_bpf_get_reg64(src, tmp2, ctx);
+
+ switch (op) {
+ case BPF_MOV:
+ emit(rv_addi(rd[0], rs[0], 0), ctx);
+ emit(rv_addi(rd[1], rs[1], 0), ctx);
+ break;
+ case BPF_ADD:
+ emit(rv_addi(RV_REG_T0, rd[1], 0), ctx);
+ emit(rv_add(rd[1], rd[1], rs[1]), ctx);
+ emit(rv_sltu(RV_REG_T0, rd[1], RV_REG_T0), ctx);
+ emit(rv_add(rd[0], rd[0], rs[0]), ctx);
+ emit(rv_add(rd[0], rd[0], RV_REG_T0), ctx);
+ break;
+ case BPF_SUB:
+ emit(rv_addi(RV_REG_T0, rd[1], 0), ctx);
+ emit(rv_sub(rd[1], rd[1], rs[1]), ctx);
+ emit(rv_sltu(RV_REG_T0, RV_REG_T0, rd[1]), ctx);
+ emit(rv_sub(rd[0], rd[0], rs[0]), ctx);
+ emit(rv_sub(rd[0], rd[0], RV_REG_T0), ctx);
+ break;
+ case BPF_AND:
+ emit(rv_and(rd[0], rd[0], rs[0]), ctx);
+ emit(rv_and(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_OR:
+ emit(rv_or(rd[0], rd[0], rs[0]), ctx);
+ emit(rv_or(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_XOR:
+ emit(rv_xor(rd[0], rd[0], rs[0]), ctx);
+ emit(rv_xor(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_MUL:
+ emit(rv_mul(RV_REG_T0, rs[0], rd[1]), ctx);
+ emit(rv_mul(rd[0], rd[0], rs[1]), ctx);
+ emit(rv_mulhu(RV_REG_T1, rd[1], rs[1]), ctx);
+ emit(rv_add(rd[0], rd[0], RV_REG_T0), ctx);
+ emit(rv_mul(rd[1], rd[1], rs[1]), ctx);
+ emit(rv_add(rd[0], rd[0], RV_REG_T1), ctx);
+ break;
+ case BPF_LSH:
+ emit(rv_addi(RV_REG_T0, rs[1], -32), ctx);
+ emit(rv_blt(RV_REG_T0, RV_REG_ZERO, 16 >> 1), ctx);
+
+ emit(rv_sll(rd[0], rd[1], RV_REG_T0), ctx);
+ emit(rv_addi(rd[1], RV_REG_ZERO, 0), ctx);
+ emit(rv_jal(RV_REG_ZERO, 32 >> 1), ctx);
+
+ emit(rv_addi(RV_REG_T1, RV_REG_ZERO, 31), ctx);
+ emit(rv_srli(RV_REG_T0, rd[1], 1), ctx);
+ emit(rv_sub(RV_REG_T1, RV_REG_T1, rs[1]), ctx);
+ emit(rv_srl(RV_REG_T0, RV_REG_T0, RV_REG_T1), ctx);
+ emit(rv_sll(rd[0], rd[0], rs[1]), ctx);
+ emit(rv_or(rd[0], RV_REG_T0, rd[0]), ctx);
+ emit(rv_sll(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_RSH:
+ emit(rv_addi(RV_REG_T0, rs[1], -32), ctx);
+ emit(rv_blt(RV_REG_T0, RV_REG_ZERO, 16 >> 1), ctx);
+
+ emit(rv_srl(rd[1], rd[0], RV_REG_T0), ctx);
+ emit(rv_addi(rd[0], RV_REG_ZERO, 0), ctx);
+ emit(rv_jal(RV_REG_ZERO, 32 >> 1), ctx);
+
+ emit(rv_addi(RV_REG_T1, RV_REG_ZERO, 31), ctx);
+ emit(rv_slli(RV_REG_T0, rd[0], 1), ctx);
+ emit(rv_sub(RV_REG_T1, RV_REG_T1, rs[1]), ctx);
+ emit(rv_sll(RV_REG_T0, RV_REG_T0, RV_REG_T1), ctx);
+ emit(rv_srl(rd[1], rd[1], rs[1]), ctx);
+ emit(rv_or(rd[1], RV_REG_T0, rd[1]), ctx);
+ emit(rv_srl(rd[0], rd[0], rs[1]), ctx);
+ break;
+ case BPF_ARSH:
+ emit(rv_addi(RV_REG_T0, rs[1], -32), ctx);
+ emit(rv_blt(RV_REG_T0, RV_REG_ZERO, 16 >> 1), ctx);
+
+ emit(rv_sra(rd[1], rd[0], RV_REG_T0), ctx);
+ emit(rv_srai(rd[0], rd[0], 0x1f), ctx);
+ emit(rv_jal(RV_REG_ZERO, 32 >> 1), ctx);
+
+ emit(rv_addi(RV_REG_T1, RV_REG_ZERO, 31), ctx);
+ emit(rv_slli(RV_REG_T0, rd[0], 1), ctx);
+ emit(rv_sub(RV_REG_T1, RV_REG_T1, rs[1]), ctx);
+ emit(rv_sll(RV_REG_T0, RV_REG_T0, RV_REG_T1), ctx);
+ emit(rv_srl(rd[1], rd[1], rs[1]), ctx);
+ emit(rv_or(rd[1], RV_REG_T0, rd[1]), ctx);
+ emit(rv_sra(rd[0], rd[0], rs[1]), ctx);
+ break;
+ case BPF_NEG:
+ emit(rv_sub(rd[1], RV_REG_ZERO, rd[1]), ctx);
+ emit(rv_sltu(RV_REG_T0, RV_REG_ZERO, rd[1]), ctx);
+ emit(rv_sub(rd[0], RV_REG_ZERO, rd[0]), ctx);
+ emit(rv_sub(rd[0], rd[0], RV_REG_T0), ctx);
+ break;
+ }
+
+ rv32_bpf_put_reg64(dst, rd, ctx);
+}
+
+static void emit_rv32_alu_r32(const s8 dst[], const s8 src[],
+ struct rv_jit_context *ctx,
+ const u8 op)
+{
+ const s8 *tmp1 = bpf2rv32[TMP_REG_1];
+ const s8 *tmp2 = bpf2rv32[TMP_REG_2];
+ const s8 *rd = rv32_bpf_get_reg32(dst, tmp1, ctx);
+ const s8 *rs = rv32_bpf_get_reg32(src, tmp2, ctx);
+
+ switch (op) {
+ case BPF_MOV:
+ emit(rv_addi(rd[1], rs[1], 0), ctx);
+ break;
+ case BPF_ADD:
+ emit(rv_add(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_SUB:
+ emit(rv_sub(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_AND:
+ emit(rv_and(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_OR:
+ emit(rv_or(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_XOR:
+ emit(rv_xor(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_MUL:
+ emit(rv_mul(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_DIV:
+ emit(rv_divu(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_MOD:
+ emit(rv_remu(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_LSH:
+ emit(rv_sll(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_RSH:
+ emit(rv_srl(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_ARSH:
+ emit(rv_sra(rd[1], rd[1], rs[1]), ctx);
+ break;
+ case BPF_NEG:
+ emit(rv_sub(rd[1], RV_REG_ZERO, rd[1]), ctx);
+ break;
+ }
+
+ rv32_bpf_put_reg32(dst, rd, ctx);
+}
+
+static int emit_rv32_jump_r64(const s8 src1[], const s8 src2[],
+ s16 off, int insn,
+ struct rv_jit_context *ctx,
+ const u8 op)
+{
+ int rvoff;
+ const s8 *tmp1 = bpf2rv32[TMP_REG_1];
+ const s8 *tmp2 = bpf2rv32[TMP_REG_2];
+ const s8 *rs1 = rv32_bpf_get_reg64(src1, tmp1, ctx);
+ const s8 *rs2 = rv32_bpf_get_reg64(src2, tmp2, ctx);
+
+ if (rv_offset_check(&rvoff, off, insn, ctx))
+ return -1;
+
+ switch (op) {
+ case BPF_JEQ:
+ emit(rv_bne(rs1[0], rs2[0], 8 >> 1), ctx);
+ emit(rv_beq(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JNE:
+ emit(rv_bne(rs1[0], rs2[0], (rvoff + 4) >> 1), ctx);
+ emit(rv_bne(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JLE:
+ emit(rv_bltu(rs1[0], rs2[0], (rvoff + 8) >> 1), ctx);
+ emit(rv_bltu(rs2[0], rs1[0], 8 >> 1), ctx);
+ emit(rv_bgeu(rs2[1], rs1[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JLT:
+ emit(rv_bltu(rs1[0], rs2[0], (rvoff + 8) >> 1), ctx);
+ emit(rv_bltu(rs2[0], rs1[0], 8 >> 1), ctx);
+ emit(rv_bltu(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JGE:
+ emit(rv_bltu(rs2[0], rs1[0], (rvoff + 8) >> 1), ctx);
+ emit(rv_bltu(rs1[0], rs2[0], 8 >> 1), ctx);
+ emit(rv_bgeu(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JGT:
+ emit(rv_bltu(rs2[0], rs1[0], (rvoff + 8) >> 1), ctx);
+ emit(rv_bltu(rs1[0], rs2[0], 8 >> 1), ctx);
+ emit(rv_bltu(rs2[1], rs1[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JSLE:
+ emit(rv_blt(rs1[0], rs2[0], (rvoff + 8) >> 1), ctx);
+ emit(rv_bne(rs2[0], rs1[0], 8 >> 1), ctx);
+ emit(rv_bgeu(rs2[1], rs1[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JSLT:
+ emit(rv_blt(rs1[0], rs2[0], (rvoff + 8) >> 1), ctx);
+ emit(rv_bne(rs2[0], rs1[0], 8 >> 1), ctx);
+ emit(rv_bltu(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JSGE:
+ emit(rv_blt(rs2[0], rs1[0], (rvoff + 8) >> 1), ctx);
+ emit(rv_bne(rs1[0], rs2[0], 8 >> 1), ctx);
+ emit(rv_bgeu(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JSGT:
+ emit(rv_blt(rs2[0], rs1[0], (rvoff + 8) >> 1), ctx);
+ emit(rv_bne(rs1[0], rs2[0], 8 >> 1), ctx);
+ emit(rv_bltu(rs2[1], rs1[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JSET:
+ emit(rv_and(RV_REG_T0, rs1[0], rs2[0]), ctx);
+ emit(rv_bne(RV_REG_T0, RV_REG_ZERO, (rvoff + 8) >> 1), ctx);
+ emit(rv_and(RV_REG_T0, rs1[1], rs2[1]), ctx);
+ emit(rv_bne(RV_REG_T0, RV_REG_ZERO, rvoff >> 1), ctx);
+ break;
+ }
+
+ return 0;
+}
+
+static int emit_rv32_jump_r32(const s8 src1[], const s8 src2[],
+ s16 off, int insn,
+ struct rv_jit_context *ctx,
+ const u8 op)
+{
+ int rvoff;
+ const s8 *tmp1 = bpf2rv32[TMP_REG_1];
+ const s8 *tmp2 = bpf2rv32[TMP_REG_2];
+ const s8 *rs1 = rv32_bpf_get_reg32(src1, tmp1, ctx);
+ const s8 *rs2 = rv32_bpf_get_reg32(src2, tmp2, ctx);
+
+ if (rv_offset_check(&rvoff, off, insn, ctx))
+ return -1;
+
+ switch (op) {
+ case BPF_JEQ:
+ emit(rv_beq(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JNE:
+ emit(rv_bne(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JLE:
+ emit(rv_bgeu(rs2[1], rs1[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JLT:
+ emit(rv_bltu(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JGE:
+ emit(rv_bgeu(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JGT:
+ emit(rv_bltu(rs2[1], rs1[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JSLE:
+ emit(rv_bge(rs2[1], rs1[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JSLT:
+ emit(rv_blt(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JSGE:
+ emit(rv_bge(rs1[1], rs2[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JSGT:
+ emit(rv_blt(rs2[1], rs1[1], rvoff >> 1), ctx);
+ break;
+ case BPF_JSET:
+ emit(rv_and(RV_REG_T0, rs1[1], rs2[1]), ctx);
+ emit(rv_bne(RV_REG_T0, RV_REG_ZERO, rvoff >> 1), ctx);
+ break;
+ }
+
+ return 0;
+}
+
+static int emit_rv32_load_r64(const s8 dst[], const s8 src[],
+ s16 off,
+ struct rv_jit_context *ctx,
+ const u8 size)
+{
+ const s8 *tmp1 = bpf2rv32[TMP_REG_1];
+ const s8 *tmp2 = bpf2rv32[TMP_REG_2];
+ const s8 *rd = rv32_bpf_get_reg64(dst, tmp1, ctx);
+ const s8 *rs = rv32_bpf_get_reg64(src, tmp2, ctx);
+
+ emit_imm(RV_REG_T0, off, ctx);
+ emit(rv_add(RV_REG_T0, RV_REG_T0, rs[1]), ctx);
+
+ switch (size) {
+ case BPF_B:
+ emit(rv_lbu(rd[1], 0, RV_REG_T0), ctx);
+ emit(rv_addi(rd[0], RV_REG_ZERO, 0), ctx);
+ break;
+ case BPF_H:
+ emit(rv_lhu(rd[1], 0, RV_REG_T0), ctx);
+ emit(rv_addi(rd[0], RV_REG_ZERO, 0), ctx);
+ break;
+ case BPF_W:
+ emit(rv_lw(rd[1], 0, RV_REG_T0), ctx);
+ emit(rv_addi(rd[0], RV_REG_ZERO, 0), ctx);
+ break;
+ case BPF_DW:
+ emit(rv_lw(rd[1], 0, RV_REG_T0), ctx);
+ emit(rv_lw(rd[0], 4, RV_REG_T0), ctx);
+ break;
+ }
+
+ rv32_bpf_put_reg64(dst, rd, ctx);
+ return 0;
+}
+
+static int emit_rv32_store_r64(const s8 dst[], const s8 src[],
+ s16 off,
+ struct rv_jit_context *ctx,
+ const u8 size, const u8 mode)
+{
+ const s8 *tmp1 = bpf2rv32[TMP_REG_1];
+ const s8 *tmp2 = bpf2rv32[TMP_REG_2];
+ const s8 *rd = rv32_bpf_get_reg64(dst, tmp1, ctx);
+ const s8 *rs = rv32_bpf_get_reg64(src, tmp2, ctx);
+
+ if (mode == BPF_XADD && size != BPF_W)
+ return -1;
+
+ emit_imm(RV_REG_T0, off, ctx);
+ emit(rv_add(RV_REG_T0, RV_REG_T0, rd[1]), ctx);
+
+ switch (size) {
+ case BPF_B:
+ emit(rv_sb(RV_REG_T0, 0, rs[1]), ctx);
+ break;
+ case BPF_H:
+ emit(rv_sh(RV_REG_T0, 0, rs[1]), ctx);
+ break;
+ case BPF_W:
+ switch (mode) {
+ case BPF_MEM:
+ emit(rv_sw(RV_REG_T0, 0, rs[1]), ctx);
+ break;
+ case BPF_XADD:
+ emit(rv_amoadd_w(RV_REG_ZERO, rs[1], RV_REG_T0, 0, 0), ctx);
+ break;
+ }
+ break;
+ case BPF_DW:
+ emit(rv_sw(RV_REG_T0, 0, rs[1]), ctx);
+ emit(rv_sw(RV_REG_T0, 4, rs[0]), ctx);
+ break;
+ }
+
+ return 0;
+}
+
+static void emit_rv32_rev16(const s8 rd, struct rv_jit_context *ctx)
+{
+ emit(rv_addi(RV_REG_T1, RV_REG_ZERO, 0), ctx);
+
+ emit(rv_andi(RV_REG_T0, rd, 0xff), ctx);
+ emit(rv_add(RV_REG_T1, RV_REG_T1, RV_REG_T0), ctx);
+ emit(rv_slli(RV_REG_T1, RV_REG_T1, 8), ctx);
+ emit(rv_srli(rd, rd, 8), ctx);
+
+ emit(rv_andi(RV_REG_T0, rd, 0xff), ctx);
+ emit(rv_add(RV_REG_T1, RV_REG_T1, RV_REG_T0), ctx);
+
+ emit(rv_addi(rd, RV_REG_T1, 0), ctx);
+}
+
+static void emit_rv32_rev32(const s8 rd, struct rv_jit_context *ctx)
+{
+ emit(rv_addi(RV_REG_T1, RV_REG_ZERO, 0), ctx);
+
+ emit(rv_andi(RV_REG_T0, rd, 0xff), ctx);
+ emit(rv_add(RV_REG_T1, RV_REG_T1, RV_REG_T0), ctx);
+ emit(rv_slli(RV_REG_T1, RV_REG_T1, 8), ctx);
+ emit(rv_srli(rd, rd, 8), ctx);
+
+ emit(rv_andi(RV_REG_T0, rd, 0xff), ctx);
+ emit(rv_add(RV_REG_T1, RV_REG_T1, RV_REG_T0), ctx);
+ emit(rv_slli(RV_REG_T1, RV_REG_T1, 8), ctx);
+ emit(rv_srli(rd, rd, 8), ctx);
+
+ emit(rv_andi(RV_REG_T0, rd, 0xff), ctx);
+ emit(rv_add(RV_REG_T1, RV_REG_T1, RV_REG_T0), ctx);
+ emit(rv_slli(RV_REG_T1, RV_REG_T1, 8), ctx);
+ emit(rv_srli(rd, rd, 8), ctx);
+ emit(rv_andi(RV_REG_T0, rd, 0xff), ctx);
+ emit(rv_add(RV_REG_T1, RV_REG_T1, RV_REG_T0), ctx);
+
+ emit(rv_addi(rd, RV_REG_T1, 0), ctx);
+}
+
+static int emit_insn(const struct bpf_insn *insn,
+ struct rv_jit_context *ctx,
+ bool extra_pass)
+{
+ int rvoff, i = insn - ctx->prog->insnsi;
+ u8 code = insn->code;
+ s16 off = insn->off;
+ s32 imm = insn->imm;
+
+ const s8 *dst = bpf2rv32[insn->dst_reg];
+ const s8 *src = bpf2rv32[insn->src_reg];
+ const s8 *tmp1 = bpf2rv32[TMP_REG_1];
+ const s8 *tmp2 = bpf2rv32[TMP_REG_2];
+
+ switch (code) {
+ case BPF_ALU64 | BPF_MOV | BPF_X:
+
+ case BPF_ALU64 | BPF_ADD | BPF_X:
+ case BPF_ALU64 | BPF_ADD | BPF_K:
+
+ case BPF_ALU64 | BPF_SUB | BPF_X:
+ case BPF_ALU64 | BPF_SUB | BPF_K:
+
+ case BPF_ALU64 | BPF_AND | BPF_X:
+ case BPF_ALU64 | BPF_AND | BPF_K:
+
+ case BPF_ALU64 | BPF_OR | BPF_X:
+ case BPF_ALU64 | BPF_OR | BPF_K:
+
+ case BPF_ALU64 | BPF_XOR | BPF_X:
+ case BPF_ALU64 | BPF_XOR | BPF_K:
+
+ case BPF_ALU64 | BPF_MUL | BPF_X:
+ case BPF_ALU64 | BPF_MUL | BPF_K:
+
+ case BPF_ALU64 | BPF_LSH | BPF_X:
+ case BPF_ALU64 | BPF_LSH | BPF_K:
+
+ case BPF_ALU64 | BPF_RSH | BPF_X:
+ case BPF_ALU64 | BPF_RSH | BPF_K:
+
+ case BPF_ALU64 | BPF_ARSH | BPF_X:
+ case BPF_ALU64 | BPF_ARSH | BPF_K:
+ if (BPF_SRC(code) == BPF_K) {
+ emit_imm32(tmp2, imm, ctx);
+ src = tmp2;
+ }
+ emit_rv32_alu_r64(dst, src, ctx, BPF_OP(code));
+ break;
+
+ case BPF_ALU64 | BPF_NEG:
+ emit_rv32_alu_r64(dst, tmp2, ctx, BPF_OP(code));
+ break;
+
+ case BPF_ALU64 | BPF_DIV | BPF_X:
+ case BPF_ALU64 | BPF_DIV | BPF_K:
+ case BPF_ALU64 | BPF_MOD | BPF_X:
+ case BPF_ALU64 | BPF_MOD | BPF_K:
+ goto notsupported;
+
+ case BPF_ALU64 | BPF_MOV | BPF_K:
+ emit_rv32_alu_i64(dst, imm, ctx, BPF_OP(code));
+ break;
+
+ case BPF_ALU | BPF_MOV | BPF_X:
+
+ case BPF_ALU | BPF_ADD | BPF_X:
+ case BPF_ALU | BPF_ADD | BPF_K:
+
+ case BPF_ALU | BPF_SUB | BPF_X:
+ case BPF_ALU | BPF_SUB | BPF_K:
+
+ case BPF_ALU | BPF_AND | BPF_X:
+ case BPF_ALU | BPF_AND | BPF_K:
+
+ case BPF_ALU | BPF_OR | BPF_X:
+ case BPF_ALU | BPF_OR | BPF_K:
+
+ case BPF_ALU | BPF_XOR | BPF_X:
+ case BPF_ALU | BPF_XOR | BPF_K:
+
+ case BPF_ALU | BPF_MUL | BPF_X:
+ case BPF_ALU | BPF_MUL | BPF_K:
+
+ case BPF_ALU | BPF_DIV | BPF_X:
+ case BPF_ALU | BPF_DIV | BPF_K:
+
+ case BPF_ALU | BPF_MOD | BPF_X:
+ case BPF_ALU | BPF_MOD | BPF_K:
+
+ case BPF_ALU | BPF_LSH | BPF_X:
+ case BPF_ALU | BPF_LSH | BPF_K:
+
+ case BPF_ALU | BPF_RSH | BPF_X:
+ case BPF_ALU | BPF_RSH | BPF_K:
+
+ case BPF_ALU | BPF_ARSH | BPF_X:
+ case BPF_ALU | BPF_ARSH | BPF_K:
+ if (BPF_SRC(code) == BPF_K) {
+ emit_imm32(tmp2, imm, ctx);
+ src = tmp2;
+ }
+ emit_rv32_alu_r32(dst, src, ctx, BPF_OP(code));
+ break;
+
+ case BPF_ALU | BPF_MOV | BPF_K:
+ emit_rv32_alu_i32(dst, imm, ctx, BPF_OP(code));
+ break;
+
+ case BPF_ALU | BPF_NEG:
+ /* src is ignored---choose a register known not to be stacked */
+ emit_rv32_alu_r32(dst, tmp2, ctx, BPF_OP(code));
+ break;
+
+ case BPF_ALU | BPF_END | BPF_FROM_LE:
+ {
+ const s8 *rd = rv32_bpf_get_reg64(dst, tmp1, ctx);
+
+ switch (imm) {
+ case 16:
+ emit(rv_slli(rd[1], rd[1], 16), ctx);
+ emit(rv_srli(rd[1], rd[1], 16), ctx);
+ /* Fallthrough to clear high bits. */
+ case 32:
+ emit(rv_addi(rd[0], RV_REG_ZERO, 0), ctx);
+ break;
+ case 64:
+ /* Do nothing. */
+ break;
+ default:
+ pr_err("bpf-jit: BPF_END imm %d invalid\n", imm);
+ return -1;
+ }
+
+ rv32_bpf_put_reg64(dst, rd, ctx);
+ break;
+ }
+
+ case BPF_ALU | BPF_END | BPF_FROM_BE:
+ {
+ const s8 *rd = rv32_bpf_get_reg64(dst, tmp1, ctx);
+
+ switch (imm) {
+ case 16:
+ emit_rv32_rev16(rd[1], ctx);
+ emit(rv_addi(rd[0], RV_REG_ZERO, 0), ctx);
+ break;
+ case 32:
+ emit_rv32_rev32(rd[1], ctx);
+ emit(rv_addi(rd[0], RV_REG_ZERO, 0), ctx);
+ break;
+ case 64:
+ /* Swap upper and lower halves. */
+ emit(rv_addi(RV_REG_T0, rd[1], 0), ctx);
+ emit(rv_addi(rd[1], rd[0], 0), ctx);
+ emit(rv_addi(rd[0], RV_REG_T0, 0), ctx);
+
+ /* Swap each half. */
+ emit_rv32_rev32(rd[1], ctx);
+ emit_rv32_rev32(rd[0], ctx);
+ break;
+ default:
+ pr_err("bpf-jit: BPF_END imm %d invalid\n", imm);
+ return -1;
+ }
+
+ rv32_bpf_put_reg64(dst, rd, ctx);
+ break;
+ }
+
+ case BPF_JMP | BPF_JA:
+ rvoff = rv_offset(i + off, i, ctx);
+ if (!is_21b_int(rvoff)) {
+ pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n",
+ i, rvoff);
+ return -1;
+ }
+ emit(rv_jal(RV_REG_ZERO, rvoff >> 1), ctx);
+ break;
+
+ case BPF_JMP | BPF_CALL:
+ {
+ bool fixed;
+ int ret;
+ u64 addr;
+ const s8 *r0 = bpf2rv32[BPF_REG_0];
+ const s8 *r5 = bpf2rv32[BPF_REG_5];
+
+ ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, &addr,
+ &fixed);
+ if (ret < 0)
+ return ret;
+ if (fixed) {
+ emit_imm(RV_REG_T0, (u32) addr, ctx);
+ } else {
+ pr_err("bpf-jit: pseudocall call not supported yet!\n");
+ return -1;
+ }
+
+ /* R1-R4 already in correct reigsters---need to push R5 to stack */
+ emit(rv_addi(RV_REG_SP, RV_REG_SP, -8), ctx);
+ emit(rv_sw(RV_REG_SP, 0, r5[1]), ctx);
+ emit(rv_sw(RV_REG_SP, 4, r5[0]), ctx);
+
+ emit(rv_jalr(RV_REG_RA, RV_REG_T0, 0), ctx);
+
+ /* Set return value */
+ emit(rv_addi(r0[1], RV_REG_A0, 0), ctx);
+ emit(rv_addi(r0[0], RV_REG_A1, 0), ctx);
+ emit(rv_addi(RV_REG_SP, RV_REG_SP, 8), ctx);
+ break;
+ }
+
+ case BPF_JMP | BPF_JEQ | BPF_X:
+ case BPF_JMP | BPF_JEQ | BPF_K:
+ case BPF_JMP32 | BPF_JEQ | BPF_X:
+ case BPF_JMP32 | BPF_JEQ | BPF_K:
+
+ case BPF_JMP | BPF_JNE | BPF_X:
+ case BPF_JMP | BPF_JNE | BPF_K:
+ case BPF_JMP32 | BPF_JNE | BPF_X:
+ case BPF_JMP32 | BPF_JNE | BPF_K:
+
+ case BPF_JMP | BPF_JLE | BPF_X:
+ case BPF_JMP | BPF_JLE | BPF_K:
+ case BPF_JMP32 | BPF_JLE | BPF_X:
+ case BPF_JMP32 | BPF_JLE | BPF_K:
+
+ case BPF_JMP | BPF_JLT | BPF_X:
+ case BPF_JMP | BPF_JLT | BPF_K:
+ case BPF_JMP32 | BPF_JLT | BPF_X:
+ case BPF_JMP32 | BPF_JLT | BPF_K:
+
+ case BPF_JMP | BPF_JGE | BPF_X:
+ case BPF_JMP | BPF_JGE | BPF_K:
+ case BPF_JMP32 | BPF_JGE | BPF_X:
+ case BPF_JMP32 | BPF_JGE | BPF_K:
+
+ case BPF_JMP | BPF_JGT | BPF_X:
+ case BPF_JMP | BPF_JGT | BPF_K:
+ case BPF_JMP32 | BPF_JGT | BPF_X:
+ case BPF_JMP32 | BPF_JGT | BPF_K:
+
+ case BPF_JMP | BPF_JSLE | BPF_X:
+ case BPF_JMP | BPF_JSLE | BPF_K:
+ case BPF_JMP32 | BPF_JSLE | BPF_X:
+ case BPF_JMP32 | BPF_JSLE | BPF_K:
+
+ case BPF_JMP | BPF_JSLT | BPF_X:
+ case BPF_JMP | BPF_JSLT | BPF_K:
+ case BPF_JMP32 | BPF_JSLT | BPF_X:
+ case BPF_JMP32 | BPF_JSLT | BPF_K:
+
+ case BPF_JMP | BPF_JSGE | BPF_X:
+ case BPF_JMP | BPF_JSGE | BPF_K:
+ case BPF_JMP32 | BPF_JSGE | BPF_X:
+ case BPF_JMP32 | BPF_JSGE | BPF_K:
+
+ case BPF_JMP | BPF_JSGT | BPF_X:
+ case BPF_JMP | BPF_JSGT | BPF_K:
+ case BPF_JMP32 | BPF_JSGT | BPF_X:
+ case BPF_JMP32 | BPF_JSGT | BPF_K:
+
+ case BPF_JMP | BPF_JSET | BPF_X:
+ case BPF_JMP | BPF_JSET | BPF_K:
+ case BPF_JMP32 | BPF_JSET | BPF_X:
+ case BPF_JMP32 | BPF_JSET | BPF_K:
+ if (BPF_SRC(code) == BPF_K) {
+ emit_imm32(tmp2, imm, ctx);
+ src = tmp2;
+ }
+ switch (BPF_CLASS(code)) {
+ case BPF_JMP:
+ if (emit_rv32_jump_r64(dst, src, off, i, ctx, BPF_OP(code)))
+ return -1;
+ break;
+ case BPF_JMP32:
+ if (emit_rv32_jump_r32(dst, src, off, i, ctx, BPF_OP(code)))
+ return -1;
+ break;
+ }
+ break;
+
+ case BPF_JMP | BPF_EXIT:
+ if (i == ctx->prog->len - 1)
+ break;
+
+ rvoff = epilogue_offset(ctx);
+ if (is_21b_check(rvoff, i))
+ return -1;
+ emit(rv_jal(RV_REG_ZERO, rvoff >> 1), ctx);
+ break;
+
+ case BPF_LD | BPF_IMM | BPF_DW:
+ {
+ struct bpf_insn insn1 = insn[1];
+ s32 imm_lo = imm;
+ s32 imm_hi = insn1.imm;
+ const s8 *rd = rv32_bpf_get_reg64(dst, tmp1, ctx);
+
+ emit_imm64(rd, imm_hi, imm_lo, ctx);
+ rv32_bpf_put_reg64(dst, rd, ctx);
+ return 1;
+ }
+
+ case BPF_LDX | BPF_MEM | BPF_B:
+ case BPF_LDX | BPF_MEM | BPF_H:
+ case BPF_LDX | BPF_MEM | BPF_W:
+ case BPF_LDX | BPF_MEM | BPF_DW:
+ if (emit_rv32_load_r64(dst, src, off, ctx, BPF_SIZE(code)))
+ return -1;
+ break;
+
+
+ case BPF_ST | BPF_MEM | BPF_B:
+ case BPF_STX | BPF_MEM | BPF_B:
+ case BPF_ST | BPF_MEM | BPF_H:
+ case BPF_STX | BPF_MEM | BPF_H:
+ case BPF_ST | BPF_MEM | BPF_W:
+ case BPF_STX | BPF_MEM | BPF_W:
+ case BPF_ST | BPF_MEM | BPF_DW:
+ case BPF_STX | BPF_MEM | BPF_DW:
+
+ case BPF_STX | BPF_XADD | BPF_W:
+ if (BPF_CLASS(code) == BPF_ST) {
+ emit_imm32(tmp2, imm, ctx);
+ src = tmp2;
+ }
+
+ if (emit_rv32_store_r64(dst, src, off, ctx, BPF_SIZE(code), BPF_MODE(code)))
+ return -1;
+ break;
+
+ case BPF_STX | BPF_XADD | BPF_DW:
+ goto notsupported;
+
+notsupported:
+ pr_info_once("*** NOT SUPPORTED: opcode %02x ***\n", code);
+ return -EFAULT;
+
+ default:
+ pr_err("bpf-jit: unknown opcode %02x\n", code);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void build_prologue(struct rv_jit_context *ctx)
+{
+ int stack_adjust = 32, store_offset, bpf_stack_adjust;
+
+ stack_adjust = round_up(stack_adjust, 16);
+ bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16);
+ stack_adjust += bpf_stack_adjust;
+
+ store_offset = stack_adjust - 8;
+
+ stack_adjust += 4 * BPF_JIT_SCRATCH_REGS;
+
+ emit(rv_addi(RV_REG_SP, RV_REG_SP, -stack_adjust), ctx);
+
+ /* Save callee-save registers */
+ emit(rv_sw(RV_REG_SP, store_offset - 0, RV_REG_RA), ctx);
+ emit(rv_sw(RV_REG_SP, store_offset - 4, RV_REG_FP), ctx);
+ emit(rv_sw(RV_REG_SP, store_offset - 8, RV_REG_S1), ctx);
+ emit(rv_sw(RV_REG_SP, store_offset - 12, RV_REG_S2), ctx);
+ emit(rv_sw(RV_REG_SP, store_offset - 16, RV_REG_S3), ctx);
+ emit(rv_sw(RV_REG_SP, store_offset - 20, RV_REG_S4), ctx);
+ emit(rv_sw(RV_REG_SP, store_offset - 24, RV_REG_S5), ctx);
+ emit(rv_sw(RV_REG_SP, store_offset - 28, RV_REG_S6), ctx);
+
+ emit(rv_addi(RV_REG_FP, RV_REG_SP, stack_adjust), ctx);
+
+ /* Set up BPF stack pointer */
+ emit(rv_addi(bpf2rv32[BPF_REG_FP][1], RV_REG_SP, bpf_stack_adjust), ctx);
+ emit(rv_addi(bpf2rv32[BPF_REG_FP][0], RV_REG_ZERO, 0), ctx);
+
+ /* Set up context pointer */
+ emit(rv_addi(bpf2rv32[BPF_REG_1][1], RV_REG_A0, 0), ctx);
+ emit(rv_addi(bpf2rv32[BPF_REG_1][0], RV_REG_ZERO, 0), ctx);
+
+ ctx->stack_size = stack_adjust;
+}
+
+static int build_body(struct rv_jit_context *ctx, bool extra_pass)
+{
+ 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;
+
+ ret = emit_insn(insn, ctx, extra_pass);
+ if (ret > 0) {
+ i++;
+ if (ctx->insns == NULL)
+ ctx->offset[i] = ctx->ninsns;
+ continue;
+ }
+ if (ctx->insns == NULL)
+ ctx->offset[i] = ctx->ninsns;
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static void bpf_fill_ill_insns(void *area, unsigned int size)
+{
+ memset(area, 0, size);
+}
+
+static void bpf_flush_icache(void *start, void *end)
+{
+ flush_icache_range((unsigned long)start, (unsigned long)end);
+}
+
+struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+{
+ bool tmp_blinded = false, extra_pass = false;
+ struct bpf_prog *tmp, *orig_prog = prog;
+ struct rv_jit_data *jit_data;
+ struct rv_jit_context *ctx;
+ unsigned int image_size;
+
+ if (!prog->jit_requested)
+ return orig_prog;
+
+ tmp = bpf_jit_blind_constants(prog);
+ if (IS_ERR(tmp))
+ return orig_prog;
+ if (tmp != prog) {
+ tmp_blinded = true;
+ prog = tmp;
+ }
+
+ jit_data = prog->aux->jit_data;
+ if (!jit_data) {
+ jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL);
+ if (!jit_data) {
+ prog = orig_prog;
+ goto out;
+ }
+ prog->aux->jit_data = jit_data;
+ }
+
+ ctx = &jit_data->ctx;
+
+ if (ctx->offset) {
+ extra_pass = true;
+ image_size = sizeof(u32) * ctx->ninsns;
+ goto skip_init_ctx;
+ }
+
+ ctx->prog = prog;
+ ctx->offset = kcalloc(prog->len, sizeof(int), GFP_KERNEL);
+ if (!ctx->offset) {
+ prog = orig_prog;
+ goto out_offset;
+ }
+
+ /* First pass generates the ctx->offset, but does not emit an image. */
+ if (build_body(ctx, extra_pass)) {
+ prog = orig_prog;
+ goto out_offset;
+ }
+ build_prologue(ctx);
+ ctx->epilogue_offset = ctx->ninsns;
+ build_epilogue(ctx);
+
+ /* Allocate image, now that we know the size. */
+ image_size = sizeof(u32) * ctx->ninsns;
+ jit_data->header = bpf_jit_binary_alloc(image_size, &jit_data->image,
+ sizeof(u32),
+ bpf_fill_ill_insns);
+ if (!jit_data->header) {
+ prog = orig_prog;
+ goto out_offset;
+ }
+
+ /* Second, real pass, that acutally emits the image. */
+ ctx->insns = (u32 *)jit_data->image;
+skip_init_ctx:
+ ctx->ninsns = 0;
+
+ build_prologue(ctx);
+ if (build_body(ctx, extra_pass)) {
+ bpf_jit_binary_free(jit_data->header);
+ prog = orig_prog;
+ goto out_offset;
+ }
+ build_epilogue(ctx);
+
+ if (bpf_jit_enable > 1)
+ bpf_jit_dump(prog->len, image_size, 2, ctx->insns);
+
+ prog->bpf_func = (void *)ctx->insns;
+ prog->jited = 1;
+ prog->jited_len = image_size;
+
+ bpf_flush_icache(jit_data->header, ctx->insns + ctx->ninsns);
+
+ if (!prog->is_func || extra_pass) {
+out_offset:
+ kfree(ctx->offset);
+ kfree(jit_data);
+ prog->aux->jit_data = NULL;
+ }
+out:
+ if (tmp_blinded)
+ bpf_jit_prog_release_other(prog, prog == orig_prog ?
+ tmp : orig_prog);
+ return prog;
+}
--
2.20.1
^ permalink raw reply related
* Re: [PATCH iproute2-next v2 1/2] ipaddress: correctly print a VF hw address in the IPoIB case
From: David Ahern @ 2019-06-21 22:56 UTC (permalink / raw)
To: Denis Kirjanov, stephen; +Cc: netdev, linux-rdma, dledford, mkubecek
In-Reply-To: <20190620090249.106704-1-dkirjanov@suse.com>
On 6/20/19 3:02 AM, Denis Kirjanov wrote:
> @@ -349,9 +350,10 @@ static void print_af_spec(FILE *fp, struct rtattr *af_spec_attr)
>
> static void print_vf_stats64(FILE *fp, struct rtattr *vfstats);
>
> -static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
> +static void print_vfinfo(struct ifinfomsg *ifi, FILE *fp, struct rtattr *vfinfo)
> {
> struct ifla_vf_mac *vf_mac;
> + struct ifla_vf_broadcast *vf_broadcast;
> struct ifla_vf_tx_rate *vf_tx_rate;
> struct rtattr *vf[IFLA_VF_MAX + 1] = {};
>
> @@ -365,13 +367,43 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
> parse_rtattr_nested(vf, IFLA_VF_MAX, vfinfo);
>
> vf_mac = RTA_DATA(vf[IFLA_VF_MAC]);
> + vf_broadcast = RTA_DATA(vf[IFLA_VF_BROADCAST]);
> vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]);
>
> print_string(PRINT_FP, NULL, "%s ", _SL_);
> print_int(PRINT_ANY, "vf", "vf %d ", vf_mac->vf);
> - print_string(PRINT_ANY, "mac", "MAC %s",
> - ll_addr_n2a((unsigned char *) &vf_mac->mac,
> - ETH_ALEN, 0, b1, sizeof(b1)));
> +
> + print_string(PRINT_ANY,
> + "link_type",
> + " link/%s ",
> + ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
> +
> + print_color_string(PRINT_ANY,
> + COLOR_MAC,
> + "address",
> + "%s",
> + ll_addr_n2a((unsigned char *) &vf_mac->mac,
> + ifi->ifi_type == ARPHRD_ETHER ? ETH_ALEN : INFINIBAND_ALEN,
> + ifi->ifi_type,
> + b1, sizeof(b1)));
> +
> + if (vf[IFLA_VF_BROADCAST]) {
> + if (ifi->ifi_flags&IFF_POINTOPOINT) {
> + print_string(PRINT_FP, NULL, " peer ", NULL);
> + print_bool(PRINT_JSON,
> + "link_pointtopoint", NULL, true);
> + } else {
> + print_string(PRINT_FP, NULL, " brd ", NULL);
> + }
> + print_color_string(PRINT_ANY,
> + COLOR_MAC,
> + "broadcast",
> + "%s",
> + ll_addr_n2a((unsigned char *) &vf_broadcast->broadcast,
> + ifi->ifi_type == ARPHRD_ETHER ? ETH_ALEN : INFINIBAND_ALEN,
> + ifi->ifi_type,
> + b1, sizeof(b1)));
> + }
>
you have a number of alignment problems with the above changes. you can
run checkpatch from the kernel repo on it to verify the coding standards.
^ permalink raw reply
* Re: [PATCH iproute2-next] tc: q_netem: JSON-ify the output
From: David Ahern @ 2019-06-21 22:52 UTC (permalink / raw)
To: Jakub Kicinski, stephen, dsahern; +Cc: netdev, oss-drivers, Quentin Monnet
In-Reply-To: <20190614172817.14817-1-jakub.kicinski@netronome.com>
On 6/14/19 11:28 AM, Jakub Kicinski wrote:
> Add JSON output support to q_netem.
>
> The normal output is untouched.
>
> In JSON output always use seconds as the base of time units,
> and non-percentage numbers (0.01 instead of 1%). Try to always
> report the fields, even if they are zero.
> All this should make the output more machine-friendly.
>
> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
> ---
> tc/q_netem.c | 174 +++++++++++++++++++++++++++++++--------------------
> 1 file changed, 107 insertions(+), 67 deletions(-)
>
applied to iproute2-next. Thanks
^ permalink raw reply
* Re: [iproute2-next v5] tipc: support interface name when activating UDP bearer
From: David Ahern @ 2019-06-21 22:50 UTC (permalink / raw)
To: Hoang Le, dsahern, jon.maloy, maloy, ying.xue, netdev,
tipc-discussion
In-Reply-To: <20190613080719.22081-1-hoang.h.le@dektech.com.au>
On 6/13/19 2:07 AM, Hoang Le wrote:
> @@ -119,6 +121,74 @@ static int generate_multicast(short af, char *buf, int bufsize)
> return 0;
> }
>
> +static struct ifreq ifr = {};
you don't need to initialize globals, but you could pass a a struct as
the arg to the filter here which is both the addr buffer and the ifindex
of interest.
> +static int nl_dump_addr_filter(struct nlmsghdr *nlh, void *arg)
> +{
> + struct ifaddrmsg *ifa = NLMSG_DATA(nlh);
> + char *r_addr = (char *)arg;
> + int len = nlh->nlmsg_len;
> + struct rtattr *addr_attr;
> +
> + if (ifr.ifr_ifindex != ifa->ifa_index)
> + return 0;
> +
> + if (strlen(r_addr) > 0)
> + return 1;
> +
> + addr_attr = parse_rtattr_one(IFA_ADDRESS, IFA_RTA(ifa),
> + len - NLMSG_LENGTH(sizeof(*ifa)));
> + if (!addr_attr)
> + return 0;
> +
> + if (ifa->ifa_family == AF_INET) {
> + struct sockaddr_in ip4addr;
> + memcpy(&ip4addr.sin_addr, RTA_DATA(addr_attr),
> + sizeof(struct in_addr));
> + if (inet_ntop(AF_INET, &ip4addr.sin_addr, r_addr,
> + INET_ADDRSTRLEN) == NULL)
> + return 0;
> + } else if (ifa->ifa_family == AF_INET6) {
> + struct sockaddr_in6 ip6addr;
> + memcpy(&ip6addr.sin6_addr, RTA_DATA(addr_attr),
> + sizeof(struct in6_addr));
> + if (inet_ntop(AF_INET6, &ip6addr.sin6_addr, r_addr,
> + INET6_ADDRSTRLEN) == NULL)
> + return 0;
> + }
> + return 1;
> +}
> +
> +static int cmd_bearer_validate_and_get_addr(const char *name, char *r_addr)
> +{
> + struct rtnl_handle rth ={ .fd = -1 };
space between '={'
> +
> + memset(&ifr, 0, sizeof(ifr));
> + if (!name || !r_addr || get_ifname(ifr.ifr_name, name))
> + return 0;
> +
> + ifr.ifr_ifindex = ll_name_to_index(ifr.ifr_name);
> + if (!ifr.ifr_ifindex)
> + return 0;
> +
> + /* remove from cache */
> + ll_drop_by_index(ifr.ifr_ifindex);
why the call to ll_drop_by_index? doing so means that ifindex is looked
up again.
> +
> + if (rtnl_open(&rth, 0) < 0)
> + return 0;
> +
> + if (rtnl_addrdump_req(&rth, AF_UNSPEC, 0) < 0) {
If you pass a filter here to set ifa_index, this command on newer
kernels will be much more efficient. See ipaddr_dump_filter.
> + rtnl_close(&rth);
> + return 0;
> + }
> +
> + if (rtnl_dump_filter(&rth, nl_dump_addr_filter, r_addr) < 0) {
> + rtnl_close(&rth);
> + return 0;
> + }
> + rtnl_close(&rth);
> + return 1;
> +}
it would better to have 1 exit with the rtnl_close and return rc based
on above.
^ permalink raw reply
* Re: [PATCH 0/3] net: ethernet: atheros: atlx: Use PCI generic definitions instead of private duplicates
From: Chris Snook @ 2019-06-21 22:45 UTC (permalink / raw)
To: Joe Perches
Cc: Bjorn Helgaas, Puranjay Mohan, Jay Cliburn, Shuah Khan,
Bjorn Helgaas, netdev, Linux Kernel Mailing List,
linux-kernel-mentees, Linux PCI
In-Reply-To: <838b8e84523151418ab8cda4abdbb114ce24a497.camel@perches.com>
On Fri, Jun 21, 2019 at 11:33 AM Joe Perches <joe@perches.com> wrote:
>
> On Fri, 2019-06-21 at 13:12 -0500, Bjorn Helgaas wrote:
> > On Fri, Jun 21, 2019 at 12:27 PM Joe Perches <joe@perches.com> wrote:
> []
> > > Subsystem specific local PCI #defines without generic
> > > naming is poor style and makes treewide grep and
> > > refactoring much more difficult.
> >
> > Don't worry, we have the same objectives. I totally agree that local
> > #defines are a bad thing, which is why I proposed this project in the
> > first place.
>
> Hi again Bjorn.
>
> I didn't know that was your idea. Good idea.
>
> > I'm just saying that this is a "first-patch" sort of learning project
> > and I think it'll avoid some list spamming and discouragement if we
> > can figure out the scope and shake out some of the teething problems
> > ahead of time. I don't want to end up with multiple versions of
> > dozens of little 2-3 patch series posted every week or two.
>
> Great, that's sensible.
>
> > I'd rather be able to deal with a whole block of them at one time.
>
> Also very sensible.
>
> > > 2: Show that you compiled the object files and verified
> > > where possible that there are no object file changes.
> >
> > Do you have any pointers for the best way to do this? Is it as simple
> > as comparing output of "objdump -d"?
>
> Generically, yes.
>
> I have a little script that does the equivalent of:
>
> <git reset>
> make <foo.o>
> mv <foo.o> <foo.o>.old
> patch -P1 < <foo_patch>
> make <foo.o>
> mv <foo.o> <foo.o>.new
> diff -urN <(objdump -d <foo.o>.old) <(objdump -d <foo.o>.new)
>
> But it's not foolproof as gcc does not guarantee
> compilation repeatability.
>
> And some subsystems Makefiles do not allow per-file
> compilation.
>
This should work, but be aware that the older atlx drivers did some
regrettable things with file structure, so not all .c files are
expected to generate a corresponding .o file.
- Chris
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox