* Re: [PATCH net] vhost_net: remove VHOST_NET_F_VIRTIO_NET_HDR support
From: Michael S. Tsirkin @ 2018-06-11 2:12 UTC (permalink / raw)
To: Jason Wang; +Cc: netdev, linux-kernel, kvm, virtualization
In-Reply-To: <23efe110-f61a-5aee-c0b4-bd3dc5426438@redhat.com>
On Fri, Jun 08, 2018 at 01:07:09PM +0800, Jason Wang wrote:
>
>
> On 2018年06月08日 12:46, Michael S. Tsirkin wrote:
> > On Fri, Jun 08, 2018 at 11:50:42AM +0800, Jason Wang wrote:
> > > This feature bit is duplicated with VIRTIO_F_ANY_LAYOUT, this means if
> > > a userpsace want to enable VRITIO_F_ANY_LAYOUT,
> > > VHOST_NET_F_VIRTIO_NET_HDR will be implied too. This is wrong and will
> > > break networking.
> > What breaks networking exactly? VHOST_NET supported ANY_LAYOUT
> > from day one. For this reason it does not need to know about
> > VRITIO_F_ANY_LAYOUT and we reused the bit for other purposes.
>
> It's the knowledge of vhost_net code it self but not userspace. For
> userspace, it should depends on the value of returned by VHOST_GET_FEATURES.
> So when userspace can set_features with ANY_LAYOUT, vhost may think it wants
> VHOST_NET_F_VIRTIO_NET_HDR.
Yes but that's the admittedly ugly API that we have now.
userspace is supposed to know VRITIO_F_ANY_LAYOUT does
not make sense for vhost.
> >
> >
> >
> > > Fixing this by safely removing
> > > VHOST_NET_F_VIRTIO_NET_HDR support. There should be very few or even
> > > no userspace can use this.
> > Quite possibly, but it is hard to be sure. It seems safer to
> > maintain it unless there's an actual reason something's broken.
>
> I think not since the feature is negotiated not mandatory?
That doesn't mean much.
> >
> > > Further cleanups could be done for
> > > -net-next for safety.
> > >
> > > In the future, we need a vhost dedicated feature set/get ioctl()
> > > instead of reusing virtio ones.
> > Not just in the future, we might want to switch iommu
> > to a sane structure without the 64 bit padding bug
> > right now.
>
> Yes, I hit this bug when introducing V2 of msg IOTLB message.
Sounds good, so if you like, reserve a bit for
VHOST_NET_F_VIRTIO_NET_HDR in the new ioctl mask and
do not enable it there.
> >
> > > Fixes: 4e9fa50c6ccbe ("vhost: move features to core")
> > This tag makes no sense here IMHO. Looks like people are using some tool
> > that just looks at the earliest version where patch won't apply. The
> > commit in question just moved some code around.
>
> Looks not, before this commit, vhost_net won't return ANY_LAYOUT.
>
> Thanks
Well ANY_LAYOUT just happens to be same as VHOST_NET_F_VIRTIO_NET_HDR
and that has been set since forever.
> >
> > > Signed-off-by: Jason Wang <jasowang@redhat.com>
> > > ---
> > > drivers/vhost/net.c | 15 +++++----------
> > > 1 file changed, 5 insertions(+), 10 deletions(-)
> > >
> > > diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> > > index 986058a..83eef52 100644
> > > --- a/drivers/vhost/net.c
> > > +++ b/drivers/vhost/net.c
> > > @@ -69,7 +69,6 @@ MODULE_PARM_DESC(experimental_zcopytx, "Enable Zero Copy TX;"
> > > enum {
> > > VHOST_NET_FEATURES = VHOST_FEATURES |
> > > - (1ULL << VHOST_NET_F_VIRTIO_NET_HDR) |
> > > (1ULL << VIRTIO_NET_F_MRG_RXBUF) |
> > > (1ULL << VIRTIO_F_IOMMU_PLATFORM)
> > > };
> > > @@ -1255,15 +1254,11 @@ static int vhost_net_set_features(struct vhost_net *n, u64 features)
> > > (1ULL << VIRTIO_F_VERSION_1))) ?
> > > sizeof(struct virtio_net_hdr_mrg_rxbuf) :
> > > sizeof(struct virtio_net_hdr);
> > > - if (features & (1 << VHOST_NET_F_VIRTIO_NET_HDR)) {
> > > - /* vhost provides vnet_hdr */
> > > - vhost_hlen = hdr_len;
> > > - sock_hlen = 0;
> > > - } else {
> > > - /* socket provides vnet_hdr */
> > > - vhost_hlen = 0;
> > > - sock_hlen = hdr_len;
> > > - }
> > > +
> > > + /* socket provides vnet_hdr */
> > > + vhost_hlen = 0;
> > > + sock_hlen = hdr_len;
> > > +
> > > mutex_lock(&n->dev.mutex);
> > > if ((features & (1 << VHOST_F_LOG_ALL)) &&
> > > !vhost_log_access_ok(&n->dev))
> > > --
> > > 2.7.4
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: Grant-
From: M. M. Fridman @ 2018-06-09 14:04 UTC (permalink / raw)
To: Recipients
I Mikhail Fridman. has selected you specially as one of my beneficiaries
for my Charitable Donation, Just as I have declared on May 23, 2016 to give
my fortune as charity.
Check the link below for confirmation:
http://www.ibtimes.co.uk/russias-second-wealthiest-man-mikhail-fridman-plans-leaving-14-2bn-fortune-charity-1561604
Reply as soon as possible with further directives.
Best Regards,
Mikhail Fridman.
^ permalink raw reply
* Re: [PATCH v6 net] stmmac: strip all VLAN tag types when kernel 802.1Q support is selected
From: Toshiaki Makita @ 2018-06-11 0:50 UTC (permalink / raw)
To: David Miller, eladv6
Cc: Jose.Abreu, f.fainelli, netdev, peppe.cavallaro, alexandre.torgue
In-Reply-To: <20180610.122918.824988605265684146.davem@davemloft.net>
On 2018/06/11 4:29, David Miller wrote:
> From: Elad Nachman <eladv6@gmail.com>
> Date: Fri, 8 Jun 2018 12:19:29 +0300
>
>> stmmac reception handler calls stmmac_rx_vlan() to strip the vlan before
>> calling napi_gro_receive().
>>
>> The function assumes VLAN tagged frames are always tagged with
>> 802.1Q protocol, and assigns ETH_P_8021Q to the skb by hard-coding
>> the parameter on call to __vlan_hwaccel_put_tag() .
>>
>> This causes packets not to be passed to the VLAN slave if it was created
>> with 802.1AD protocol
>> (ip link add link eth0 eth0.100 type vlan proto 802.1ad id 100).
>>
>> This fix passes the protocol from the VLAN header into
>> __vlan_hwaccel_put_tag() instead of using the hard-coded value of
>> ETH_P_8021Q.
>>
>> NETIF_F_HW_VLAN_CTAG_RX check was removed and instead the strip action
>> is dependent upon a preprocessor define which is defined when 802.1Q
>> support is selected in the kernel config.
>>
>> NETIF_F_HW_VLAN_STAG_RX feature was added to be in line with the driver
>> actual abilities.
>>
>> Signed-off-by: Elad Nachman <eladn@gilat.com>
>
> You can't remove the NETIF_F_* checks.
>
> If the user doesn't have VLAN offloading enabled, the VLAN tags should
> be left in the packet as-is.
>
> It can't be controlled by a CPP check.
David, NETIF_F_HW_VALN_*_RX is not user-controllable in this driver
because hw_features does not include them.
But this can break when those features are added to hw_features so if
David does not like this situation I think we need the condition anyway.
--
Toshiaki Makita
^ permalink raw reply
* [GIT] Networking
From: David Miller @ 2018-06-11 0:42 UTC (permalink / raw)
To: torvalds; +Cc: akpm, netdev, linux-kernel
1) For several bpfilter/UMH bugs, in particular make the UMH build not
depend upon X86 specific Kconfig symbols. From Alexei Starovoitov.
2) Fix handling of modified context pointer in bpf verifier, from
Daniel Borkmann.
3) Kill regression in ifdown/ifup sequences for hv_netvsc driver, from
Dexuan Cui.
4) When the bonding primary member name changes, we have to
re-evaluate the bond->force_primary setting, from Xiangning Yu.
5) Eliminate possible padding beyone end of SKB in cdc_ncm driver,
from Bjørn Mork.
6) RX queue length reported for UDP sockets in procfs and socket diag
are inaccurate, from Paolo Abeni.
7) Fix br_fdb_find_port() locking, from Petr Machata.
8) Limit sk_rcvlowat values properly in TCP, from Soheil Hassas
Yeganeh.
Please pull, thanks a lot!
The following changes since commit 3036bc45364f98515a2c446d7fac2c34dcfbeff4:
Merge tag 'media/v4.18-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media (2018-06-07 12:34:37 -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 867f816badc01e6da655028810d468c9f935b37c:
tcp: limit sk_rcvlowat by the maximum receive buffer (2018-06-10 14:12:50 -0700)
----------------------------------------------------------------
Alexei Starovoitov (3):
umh: fix race condition
bpfilter: fix OUTPUT_FORMAT
bpfilter: fix race in pipe access
Alvaro Gamez Machado (1):
net: phy: dp83822: use BMCR_ANENABLE instead of BMSR_ANEGCAPABLE for DP83620
Bjørn Mork (1):
cdc_ncm: avoid padding beyond end of skb
Colin Ian King (1):
net: aquantia: fix unsigned numvecs comparison with less than zero
Cong Wang (1):
socket: close race condition between sock_close() and sockfs_setattr()
Corentin Labbe (1):
net: stmmac: fix build failure due to missing COMMON_CLK dependency
Daniel Borkmann (2):
bpf: reject passing modified ctx to helper functions
bpf, xdp: fix crash in xdp_umem_unaccount_pages
David S. Miller (1):
Merge git://git.kernel.org/.../bpf/bpf
Davide Caratti (1):
net/sched: act_simple: fix parsing of TCA_DEF_DATA
Dexuan Cui (1):
hv_netvsc: Fix a network regression after ifdown/ifup
Geert Uytterhoeven (2):
net: mscc: ocelot: Fix uninitialized error in ocelot_netdevice_event()
xsk: Fix umem fill/completion queue mmap on 32-bit
Pablo Neira Ayuso (1):
netfilter: nf_tables: add NFT_LOGLEVEL_* enumeration and use it
Paolo Abeni (1):
udp: fix rx queue len reported by diag and proc interface
Petr Machata (1):
net: bridge: Fix locking in br_fdb_find_port()
Soheil Hassas Yeganeh (1):
tcp: limit sk_rcvlowat by the maximum receive buffer
Sultan Alsawaf (1):
ip_tunnel: Fix name string concatenate in __ip_tunnel_create()
Willem de Bruijn (1):
net: in virtio_net_hdr only add VLAN_HLEN to csum_start if payload holds vlan
Xiangning Yu (1):
bonding: re-evaluate force_primary when the primary slave name changes
Yonghong Song (1):
tools/bpf: fix selftest get_cgroup_id_user
YueHaibing (1):
net: fddi: fix a possible null-ptr-deref
arch/um/drivers/vector_transports.c | 3 ++-
drivers/net/bonding/bond_options.c | 1 +
drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c | 11 +++++------
drivers/net/ethernet/mscc/ocelot.c | 2 +-
drivers/net/ethernet/stmicro/stmmac/Kconfig | 10 +++++-----
drivers/net/fddi/skfp/skfddi.c | 55 ++++++++++++++++++++++++++++---------------------------
drivers/net/hyperv/netvsc_drv.c | 4 +++-
drivers/net/phy/dp83848.c | 35 +++++++++++++++++++++++++++++------
drivers/net/tap.c | 5 ++++-
drivers/net/tun.c | 3 ++-
drivers/net/usb/cdc_ncm.c | 4 ++--
drivers/net/virtio_net.c | 3 ++-
include/linux/virtio_net.h | 11 ++++-------
include/net/transp_v6.h | 11 +++++++++--
include/net/udp.h | 5 +++++
include/uapi/linux/if_xdp.h | 4 ++--
include/uapi/linux/netfilter/nf_tables.h | 28 +++++++++++++++++++++++++---
kernel/bpf/verifier.c | 48 +++++++++++++++++++++++++++++++-----------------
kernel/umh.c | 3 +--
net/bpfilter/Makefile | 2 +-
net/bpfilter/bpfilter_kern.c | 10 +++++++---
net/bridge/br_fdb.c | 4 +++-
net/ipv4/ip_tunnel.c | 4 ++--
net/ipv4/tcp.c | 12 +++++++-----
net/ipv4/udp.c | 2 +-
net/ipv4/udp_diag.c | 2 +-
net/ipv6/datagram.c | 6 +++---
net/ipv6/udp.c | 3 ++-
net/netfilter/nft_log.c | 10 +++++-----
net/packet/af_packet.c | 4 ++--
net/sched/act_simple.c | 15 ++++++---------
net/socket.c | 18 +++++++++++++++---
net/xdp/xdp_umem.c | 6 ++++--
net/xdp/xsk.c | 2 +-
tools/testing/selftests/bpf/get_cgroup_id_kern.c | 14 +++++++++++++-
tools/testing/selftests/bpf/get_cgroup_id_user.c | 12 ++++++++++--
tools/testing/selftests/bpf/test_verifier.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
37 files changed, 301 insertions(+), 129 deletions(-)
^ permalink raw reply
* Re: [PATCH net-next] vlan: implement vlan id and protocol changes
From: kbuild test robot @ 2018-06-11 0:15 UTC (permalink / raw)
To: Chas Williams
Cc: kbuild-all, davem, netdev, Charles (Chas) Williams, Chas Williams
In-Reply-To: <20180610231912.1543-1-3chas3@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2940 bytes --]
Hi Charles,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on net-next/master]
url: https://github.com/0day-ci/linux/commits/Chas-Williams/vlan-implement-vlan-id-and-protocol-changes/20180611-072123
config: parisc-c3000_defconfig (attached as .config)
compiler: hppa-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=parisc
All warnings (new ones prefixed by >>):
net/core/dev.c: In function 'netdev_cmd_to_name':
>> net/core/dev.c:1580:2: warning: enumeration value 'NETDEV_CHANGEVLAN' not handled in switch [-Wswitch]
switch (cmd) {
^~~~~~
vim +/NETDEV_CHANGEVLAN +1580 net/core/dev.c
56f5aa77 Michael Chan 2017-12-16 1574
ede2762d Kirill Tkhai 2018-03-23 1575 const char *netdev_cmd_to_name(enum netdev_cmd cmd)
ede2762d Kirill Tkhai 2018-03-23 1576 {
ede2762d Kirill Tkhai 2018-03-23 1577 #define N(val) \
ede2762d Kirill Tkhai 2018-03-23 1578 case NETDEV_##val: \
ede2762d Kirill Tkhai 2018-03-23 1579 return "NETDEV_" __stringify(val);
ede2762d Kirill Tkhai 2018-03-23 @1580 switch (cmd) {
ede2762d Kirill Tkhai 2018-03-23 1581 N(UP) N(DOWN) N(REBOOT) N(CHANGE) N(REGISTER) N(UNREGISTER)
ede2762d Kirill Tkhai 2018-03-23 1582 N(CHANGEMTU) N(CHANGEADDR) N(GOING_DOWN) N(CHANGENAME) N(FEAT_CHANGE)
ede2762d Kirill Tkhai 2018-03-23 1583 N(BONDING_FAILOVER) N(PRE_UP) N(PRE_TYPE_CHANGE) N(POST_TYPE_CHANGE)
ede2762d Kirill Tkhai 2018-03-23 1584 N(POST_INIT) N(RELEASE) N(NOTIFY_PEERS) N(JOIN) N(CHANGEUPPER)
ede2762d Kirill Tkhai 2018-03-23 1585 N(RESEND_IGMP) N(PRECHANGEMTU) N(CHANGEINFODATA) N(BONDING_INFO)
ede2762d Kirill Tkhai 2018-03-23 1586 N(PRECHANGEUPPER) N(CHANGELOWERSTATE) N(UDP_TUNNEL_PUSH_INFO)
ede2762d Kirill Tkhai 2018-03-23 1587 N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN)
9daae9bd Gal Pressman 2018-03-28 1588 N(CVLAN_FILTER_PUSH_INFO) N(CVLAN_FILTER_DROP_INFO)
9daae9bd Gal Pressman 2018-03-28 1589 N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO)
3f5ecd8a Kirill Tkhai 2018-04-26 1590 }
ede2762d Kirill Tkhai 2018-03-23 1591 #undef N
ede2762d Kirill Tkhai 2018-03-23 1592 return "UNKNOWN_NETDEV_EVENT";
ede2762d Kirill Tkhai 2018-03-23 1593 }
ede2762d Kirill Tkhai 2018-03-23 1594 EXPORT_SYMBOL_GPL(netdev_cmd_to_name);
ede2762d Kirill Tkhai 2018-03-23 1595
:::::: The code at line 1580 was first introduced by commit
:::::: ede2762d93ff16e0974f7446516b46b1022db213 net: Make NETDEV_XXX commands enum { }
:::::: TO: Kirill Tkhai <ktkhai@virtuozzo.com>
:::::: CC: David S. Miller <davem@davemloft.net>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 14440 bytes --]
^ permalink raw reply
* Re: [mmotm:master 192/212] include/uapi/asm-generic/int-ll64.h:20:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'typedef'
From: Randy Dunlap @ 2018-06-11 0:01 UTC (permalink / raw)
To: kbuild test robot, Andrew Morton
Cc: kbuild-all, Linux Memory Management List, Souptick Joarder,
Johannes Weiner, LKML, netdev@vger.kernel.org
In-Reply-To: <201806081045.eZrs5GGH%fengguang.wu@intel.com>
On 06/07/2018 07:06 PM, kbuild test robot wrote:
> tree: git://git.cmpxchg.org/linux-mmotm.git master
> head: 7393732bae530daa27567988b91d16ecfeef6c62
> commit: b1a8bfbadbcb79644ccdd5f9cd370caa63cb1fa7 [192/212] linux-next-git-rejects
> config: i386-randconfig-s0-201822-CONFIG_DEBUG_INFO_REDUCED (attached as .config)
> compiler: gcc-6 (Debian 6.4.0-9) 6.4.0 20171026
> reproduce:
> git checkout b1a8bfbadbcb79644ccdd5f9cd370caa63cb1fa7
> # save the attached .config to linux build tree
> make ARCH=i386
>
> All errors (new ones prefixed by >>):
>
> In file included from include/asm-generic/int-ll64.h:11:0,
> from include/uapi/asm-generic/types.h:7,
> from arch/x86/include/uapi/asm/types.h:5,
> from include/uapi/linux/types.h:5,
> from include/linux/types.h:6,
> from net/ipv4/ipconfig.c:36:
>>> include/uapi/asm-generic/int-ll64.h:20:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'typedef'
> typedef __signed__ char __s8;
> ^~~~~~~
The problem here was that net/ipv4/ipconfig.c begins like this:
q// SPDX-License-Identifier: GPL-2.0
Did anyone fix that?
--
~Randy
^ permalink raw reply
* [PATCH net-next] vlan: implement vlan id and protocol changes
From: Chas Williams @ 2018-06-10 23:19 UTC (permalink / raw)
To: davem; +Cc: netdev, Charles (Chas) Williams, Chas Williams
From: "Charles (Chas) Williams" <ciwillia@mail.eng.vyatta.net>
vlan_changelink silently ignores attempts to change the vlan id
or protocol id of an existing vlan interface. Implement by adding
the new vlan id and protocol to the interface's vlan group and then
removing the old vlan id and protocol from the vlan group.
Signed-off-by: Chas Williams <3chas3@gmail.com>
---
include/linux/netdevice.h | 1 +
net/8021q/vlan.c | 4 ++--
net/8021q/vlan.h | 2 ++
net/8021q/vlan_netlink.c | 38 ++++++++++++++++++++++++++++++++++++++
4 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 3ec9850c7936..a95ae238addf 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2409,6 +2409,7 @@ enum netdev_cmd {
NETDEV_CVLAN_FILTER_DROP_INFO,
NETDEV_SVLAN_FILTER_PUSH_INFO,
NETDEV_SVLAN_FILTER_DROP_INFO,
+ NETDEV_CHANGEVLAN,
};
const char *netdev_cmd_to_name(enum netdev_cmd cmd);
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 73a65789271b..b5e0ad1a581a 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -51,8 +51,8 @@ const char vlan_version[] = DRV_VERSION;
/* End of global variables definitions. */
-static int vlan_group_prealloc_vid(struct vlan_group *vg,
- __be16 vlan_proto, u16 vlan_id)
+int vlan_group_prealloc_vid(struct vlan_group *vg,
+ __be16 vlan_proto, u16 vlan_id)
{
struct net_device **array;
unsigned int pidx, vidx;
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h
index 44df1c3df02d..c734dd21d70d 100644
--- a/net/8021q/vlan.h
+++ b/net/8021q/vlan.h
@@ -116,6 +116,8 @@ int register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack);
void unregister_vlan_dev(struct net_device *dev, struct list_head *head);
bool vlan_dev_inherit_address(struct net_device *dev,
struct net_device *real_dev);
+int vlan_group_prealloc_vid(struct vlan_group *vg,
+ __be16 vlan_proto, u16 vlan_id);
static inline u32 vlan_get_ingress_priority(struct net_device *dev,
u16 vlan_tci)
diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c
index 9b60c1e399e2..0e59babe6651 100644
--- a/net/8021q/vlan_netlink.c
+++ b/net/8021q/vlan_netlink.c
@@ -107,10 +107,48 @@ static int vlan_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[],
struct netlink_ext_ack *extack)
{
+ struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
struct ifla_vlan_flags *flags;
struct ifla_vlan_qos_mapping *m;
struct nlattr *attr;
int rem;
+ int err;
+ __be16 vlan_proto = vlan->vlan_proto;
+ u16 vlan_id = vlan->vlan_id;
+
+ if (data[IFLA_VLAN_ID])
+ vlan_id = nla_get_u16(data[IFLA_VLAN_ID]);
+
+ if (data[IFLA_VLAN_PROTOCOL])
+ vlan_proto = nla_get_be16(data[IFLA_VLAN_PROTOCOL]);
+
+ if (vlan->vlan_id != vlan_id || vlan->vlan_proto != vlan_proto) {
+ struct net_device *real_dev = vlan->real_dev;
+ struct vlan_info *vlan_info;
+ struct vlan_group *grp;
+ __be16 old_vlan_proto = vlan->vlan_proto;
+ u16 old_vlan_id = vlan->vlan_id;
+
+ err = vlan_vid_add(real_dev, vlan_proto, vlan_id);
+ if (err)
+ return err;
+ vlan_info = rtnl_dereference(real_dev->vlan_info);
+ grp = &vlan_info->grp;
+ err = vlan_group_prealloc_vid(grp, vlan_proto, vlan_id);
+ if (err < 0) {
+ vlan_vid_del(real_dev, vlan_proto, vlan_id);
+ return err;
+ }
+ vlan_group_set_device(grp, vlan_proto, vlan_id, dev);
+ vlan->vlan_proto = vlan_proto;
+ vlan->vlan_id = vlan_id;
+
+ vlan_group_set_device(grp, old_vlan_proto, old_vlan_id, NULL);
+ vlan_vid_del(real_dev, old_vlan_proto, old_vlan_id);
+
+ err = call_netdevice_notifiers(NETDEV_CHANGEVLAN, dev);
+ notifier_to_errno(err);
+ }
if (data[IFLA_VLAN_FLAGS]) {
flags = nla_data(data[IFLA_VLAN_FLAGS]);
--
2.14.3
^ permalink raw reply related
* [PATCH net] ipv6: allow PMTU exceptions to local routes
From: Julian Anastasov @ 2018-06-10 23:02 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Martin KaFai Lau, kernel-team, lvs-devel
IPVS setups with local client and remote tunnel server need
to create exception for the local virtual IP. What we do is to
change PMTU from 64KB (on "lo") to 1460 in the common case.
Suggested-by: Martin KaFai Lau <kafai@fb.com>
Fixes: 45e4fd26683c ("ipv6: Only create RTF_CACHE routes after encountering pmtu exception")
Fixes: 7343ff31ebf0 ("ipv6: Don't create clones of host routes.")
Signed-off-by: Julian Anastasov <ja@ssi.bg>
---
net/ipv6/route.c | 3 ---
1 file changed, 3 deletions(-)
Note: I failed to build 2.6.38 kernel for the test but I think
commit 7343ff31ebf0 looks as the one that added the restriction
to change PMTU for local IPs.
So, I'm not sure from how long time the issue with local IPVS
clients and tunnelling method exists. May be it worked between
2.6.28 and 2.6.37.
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index fb95698..86a0e43 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2307,9 +2307,6 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
const struct in6_addr *daddr, *saddr;
struct rt6_info *rt6 = (struct rt6_info *)dst;
- if (rt6->rt6i_flags & RTF_LOCAL)
- return;
-
if (dst_metric_locked(dst, RTAX_MTU))
return;
--
2.9.5
^ permalink raw reply related
* Re: [Patch net] socket: close race condition between sock_close() and sockfs_setattr()
From: Cong Wang @ 2018-06-10 19:43 UTC (permalink / raw)
To: David Miller
Cc: Linux Kernel Network Developers, shankarapailoor, Tetsuo Handa,
Lorenzo Colitti, Al Viro
In-Reply-To: <20180610.122704.2150654250277031669.davem@davemloft.net>
On Sun, Jun 10, 2018 at 12:27 PM, David Miller <davem@davemloft.net> wrote:
> I'm applying this for now, it is at least a step towards fixing
> all of these issues.
>
> If it is really offensive, I can revert, just tell me.
Thanks, David!
Unless there is something fundamentally broken, there is no
reason to revert it. The only risk here is some possible deadlock,
but I am ready to fix any deadlock report caused by this
patch. :)
^ permalink raw reply
* Re: [PATCH] net: phy: dp83822: use BMCR_ANENABLE instead of BMSR_ANEGCAPABLE for DP83620
From: David Miller @ 2018-06-10 19:39 UTC (permalink / raw)
To: alvaro.gamez; +Cc: andrew, f.fainelli, netdev
In-Reply-To: <20180608102339.2572-1-alvaro.gamez@hazent.com>
From: Alvaro Gamez Machado <alvaro.gamez@hazent.com>
Date: Fri, 8 Jun 2018 12:23:39 +0200
> DP83620 register set is compatible with the DP83848, but it also supports
> 100base-FX. When the hardware is configured such as that fiber mode is
> enabled, autonegotiation is not possible.
>
> The chip, however, doesn't expose this information via BMSR_ANEGCAPABLE.
> Instead, this bit is always set high, even if the particular hardware
> configuration makes it so that auto negotiation is not possible [1]. Under
> these circumstances, the phy subsystem keeps trying for autonegotiation to
> happen, without success.
>
> Hereby, we inspect BMCR_ANENABLE bit after genphy_config_init, which on
> reset is set to 0 when auto negotiation is disabled, and so we use this
> value instead of BMSR_ANEGCAPABLE.
>
> [1] https://e2e.ti.com/support/interface/ethernet/f/903/p/697165/2571170
>
> Signed-off-by: Alvaro Gamez Machado <alvaro.gamez@hazent.com>
Applied and queued up for -stable, thanks.
^ permalink raw reply
* Re: [PATCH] net: thunderx: prevent concurrent data re-writing by nicvf_set_rx_mode
From: David Miller @ 2018-06-10 19:35 UTC (permalink / raw)
To: Vadim.Lomovtsev
Cc: rric, sgoutham, linux-arm-kernel, netdev, linux-kernel, dnelson,
Vadim.Lomovtsev
In-Reply-To: <20180608092759.28059-1-Vadim.Lomovtsev@caviumnetworks.com>
From: Vadim Lomovtsev <Vadim.Lomovtsev@caviumnetworks.com>
Date: Fri, 8 Jun 2018 02:27:59 -0700
> + /* Save message data locally to prevent them from
> + * being overwritten by next ndo_set_rx_mode call().
> + */
> + spin_lock(&nic->rx_mode_wq_lock);
> + mode = vf_work->mode;
> + mc = vf_work->mc;
> + vf_work->mc = NULL;
> + spin_unlock(&nic->rx_mode_wq_lock);
At the moment you drop this lock, the memory behind 'mc' can be
freed up by:
> + spin_lock(&nic->rx_mode_wq_lock);
> + kfree(nic->rx_mode_work.mc);
And you'll crash when you dereference it above via
__nicvf_set_rx_mode_task().
^ permalink raw reply
* Re: [PATCH v6 net] stmmac: strip all VLAN tag types when kernel 802.1Q support is selected
From: David Miller @ 2018-06-10 19:29 UTC (permalink / raw)
To: eladv6
Cc: makita.toshiaki, Jose.Abreu, f.fainelli, netdev, peppe.cavallaro,
alexandre.torgue
In-Reply-To: <cb89c947-21cf-b5da-9cff-940e904105c3@gmail.com>
From: Elad Nachman <eladv6@gmail.com>
Date: Fri, 8 Jun 2018 12:19:29 +0300
> stmmac reception handler calls stmmac_rx_vlan() to strip the vlan before
> calling napi_gro_receive().
>
> The function assumes VLAN tagged frames are always tagged with
> 802.1Q protocol, and assigns ETH_P_8021Q to the skb by hard-coding
> the parameter on call to __vlan_hwaccel_put_tag() .
>
> This causes packets not to be passed to the VLAN slave if it was created
> with 802.1AD protocol
> (ip link add link eth0 eth0.100 type vlan proto 802.1ad id 100).
>
> This fix passes the protocol from the VLAN header into
> __vlan_hwaccel_put_tag() instead of using the hard-coded value of
> ETH_P_8021Q.
>
> NETIF_F_HW_VLAN_CTAG_RX check was removed and instead the strip action
> is dependent upon a preprocessor define which is defined when 802.1Q
> support is selected in the kernel config.
>
> NETIF_F_HW_VLAN_STAG_RX feature was added to be in line with the driver
> actual abilities.
>
> Signed-off-by: Elad Nachman <eladn@gilat.com>
You can't remove the NETIF_F_* checks.
If the user doesn't have VLAN offloading enabled, the VLAN tags should
be left in the packet as-is.
It can't be controlled by a CPP check.
^ permalink raw reply
* Re: [PATCH net] vhost_net: remove VHOST_NET_F_VIRTIO_NET_HDR support
From: David Miller @ 2018-06-10 19:27 UTC (permalink / raw)
To: jasowang; +Cc: netdev, virtualization, linux-kernel, kvm, mst
In-Reply-To: <1528429842-22835-1-git-send-email-jasowang@redhat.com>
From: Jason Wang <jasowang@redhat.com>
Date: Fri, 8 Jun 2018 11:50:42 +0800
> This feature bit is duplicated with VIRTIO_F_ANY_LAYOUT, this means if
> a userpsace want to enable VRITIO_F_ANY_LAYOUT,
> VHOST_NET_F_VIRTIO_NET_HDR will be implied too. This is wrong and will
> break networking. Fixing this by safely removing
> VHOST_NET_F_VIRTIO_NET_HDR support. There should be very few or even
> no userspace can use this. Further cleanups could be done for
> -net-next for safety.
>
> In the future, we need a vhost dedicated feature set/get ioctl()
> instead of reusing virtio ones.
>
> Fixes: 4e9fa50c6ccbe ("vhost: move features to core")
> Signed-off-by: Jason Wang <jasowang@redhat.com>
I don't see this discussion as resolved yet so I'll mark this
as Deferred in patchwork.
Thanks.
^ permalink raw reply
* Re: [Patch net] socket: close race condition between sock_close() and sockfs_setattr()
From: David Miller @ 2018-06-10 19:27 UTC (permalink / raw)
To: xiyou.wangcong; +Cc: netdev, shankarapailoor, penguin-kernel, lorenzo, viro
In-Reply-To: <20180607203949.16945-1-xiyou.wangcong@gmail.com>
From: Cong Wang <xiyou.wangcong@gmail.com>
Date: Thu, 7 Jun 2018 13:39:49 -0700
> fchownat() doesn't even hold refcnt of fd until it figures out
> fd is really needed (otherwise is ignored) and releases it after
> it resolves the path. This means sock_close() could race with
> sockfs_setattr(), which leads to a NULL pointer dereference
> since typically we set sock->sk to NULL in ->release().
>
> As pointed out by Al, this is unique to sockfs. So we can fix this
> in socket layer by acquiring inode_lock in sock_close() and
> checking against NULL in sockfs_setattr().
>
> sock_release() is called in many places, only the sock_close()
> path matters here. And fortunately, this should not affect normal
> sock_close() as it is only called when the last fd refcnt is gone.
> It only affects sock_close() with a parallel sockfs_setattr() in
> progress, which is not common.
>
> Fixes: 86741ec25462 ("net: core: Add a UID field to struct sock.")
> Reported-by: shankarapailoor <shankarapailoor@gmail.com>
> Cc: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
> Cc: Lorenzo Colitti <lorenzo@google.com>
> Cc: Al Viro <viro@zeniv.linux.org.uk>
> Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
I'm applying this for now, it is at least a step towards fixing
all of these issues.
If it is really offensive, I can revert, just tell me.
^ permalink raw reply
* [ 54.304576] eth0: hw csum failure
From: Mathieu Malaterre @ 2018-06-10 18:57 UTC (permalink / raw)
To: netdev
Hi there,
I am getting the following in dmesg upon startup from
[ 54.304576] eth0: hw csum failure
[ 54.304610] CPU: 0 PID: 0 Comm: swapper Not tainted 4.17.0+ #3
[ 54.304616] Call Trace:
[ 54.304633] [dffedbd0] [c069d178]
__skb_checksum_complete+0xf0/0x108 (unreliable)
[ 54.304653] [dffedbf0] [c077711c] tcp_v4_rcv+0x604/0xe00
[ 54.304668] [dffedc70] [c0730c98] ip_local_deliver_finish+0xa8/0x3c4
[ 54.304677] [dffedcb0] [c0731aa4] ip_local_deliver+0xf0/0x154
[ 54.304687] [dffedcf0] [c0731f50] ip_rcv+0x448/0x774
[ 54.304701] [dffedd50] [c06adeac] __netif_receive_skb_core+0x5e8/0x1184
[ 54.304712] [dffedde0] [c06badec] napi_gro_receive+0x160/0x22c
[ 54.304729] [dffede10] [e1560590] gem_poll+0x7fc/0x1ac0 [sungem]
[ 54.304738] [dffedee0] [c06ba0bc] net_rx_action+0x34c/0x618
[ 54.304750] [dffedf60] [c07fc904] __do_softirq+0x16c/0x5f0
[ 54.304763] [dffedfd0] [c0064c9c] irq_exit+0x110/0x1a8
[ 54.304777] [dffedff0] [c0016170] call_do_irq+0x24/0x3c
[ 54.304791] [c0cf5e80] [c0009a84] do_IRQ+0x98/0x1a0
[ 54.304801] [c0cf5eb0] [c001b474] ret_from_except+0x0/0x14
[ 54.304812] --- interrupt: 501 at arch_cpu_idle+0x30/0x78
LR = arch_cpu_idle+0x30/0x78
[ 54.304821] [c0cf5f70] [c0cf4000] 0xc0cf4000 (unreliable)
[ 54.304836] [c0cf5f80] [c00a3868] do_idle+0xc4/0x158
[ 54.304845] [c0cf5fb0] [c00a3ab4] cpu_startup_entry+0x24/0x28
[ 54.304856] [c0cf5fc0] [c0997820] start_kernel+0x47c/0x490
[ 54.304864] [c0cf5ff0] [00003444] 0x3444
[ 54.309112] eth0: hw csum failure
[ 54.309133] CPU: 0 PID: 7 Comm: ksoftirqd/0 Not tainted 4.17.0+ #3
[ 54.309138] Call Trace:
[ 54.309154] [df503ab0] [c069d178]
__skb_checksum_complete+0xf0/0x108 (unreliable)
[ 54.309172] [df503ad0] [c077711c] tcp_v4_rcv+0x604/0xe00
[ 54.309184] [df503b50] [c0730c98] ip_local_deliver_finish+0xa8/0x3c4
[ 54.309191] [df503b90] [c0731aa4] ip_local_deliver+0xf0/0x154
[ 54.309199] [df503bd0] [c0731f50] ip_rcv+0x448/0x774
[ 54.309211] [df503c30] [c06adeac] __netif_receive_skb_core+0x5e8/0x1184
[ 54.309220] [df503cc0] [c06badec] napi_gro_receive+0x160/0x22c
[ 54.309235] [df503cf0] [e1560590] gem_poll+0x7fc/0x1ac0 [sungem]
[ 54.309242] [df503dc0] [c06ba0bc] net_rx_action+0x34c/0x618
[ 54.309252] [df503e40] [c07fc904] __do_softirq+0x16c/0x5f0
[ 54.309263] [df503eb0] [c00649c8] run_ksoftirqd+0x68/0x9c
[ 54.309276] [df503ec0] [c009477c] smpboot_thread_fn+0x1bc/0x324
[ 54.309289] [df503f10] [c008e01c] kthread+0x138/0x1f0
[ 54.309296] [df503f40] [c001b1c4] ret_from_kernel_thread+0x14/0x1c
[ 55.829670] eth0: hw csum failure
[ 55.829703] CPU: 0 PID: 0 Comm: swapper Not tainted 4.17.0+ #3
[ 55.829709] Call Trace:
[ 55.829726] [dffedbd0] [c069d178]
__skb_checksum_complete+0xf0/0x108 (unreliable)
[ 55.829746] [dffedbf0] [c077711c] tcp_v4_rcv+0x604/0xe00
[ 55.829760] [dffedc70] [c0730c98] ip_local_deliver_finish+0xa8/0x3c4
[ 55.829770] [dffedcb0] [c0731aa4] ip_local_deliver+0xf0/0x154
[ 55.829779] [dffedcf0] [c0731f50] ip_rcv+0x448/0x774
[ 55.829793] [dffedd50] [c06adeac] __netif_receive_skb_core+0x5e8/0x1184
[ 55.829804] [dffedde0] [c06badec] napi_gro_receive+0x160/0x22c
[ 55.829820] [dffede10] [e1560590] gem_poll+0x7fc/0x1ac0 [sungem]
[ 55.829830] [dffedee0] [c06ba0bc] net_rx_action+0x34c/0x618
[ 55.829842] [dffedf60] [c07fc904] __do_softirq+0x16c/0x5f0
[ 55.829855] [dffedfd0] [c0064c9c] irq_exit+0x110/0x1a8
[ 55.829869] [dffedff0] [c0016170] call_do_irq+0x24/0x3c
[ 55.829883] [c0cf5e80] [c0009a84] do_IRQ+0x98/0x1a0
[ 55.829892] [c0cf5eb0] [c001b474] ret_from_except+0x0/0x14
[ 55.829904] --- interrupt: 501 at arch_cpu_idle+0x30/0x78
LR = arch_cpu_idle+0x30/0x78
[ 55.829912] [c0cf5f70] [c0cf4000] 0xc0cf4000 (unreliable)
[ 55.829927] [c0cf5f80] [c00a3868] do_idle+0xc4/0x158
[ 55.829936] [c0cf5fb0] [c00a3ab4] cpu_startup_entry+0x24/0x28
[ 55.829947] [c0cf5fc0] [c0997820] start_kernel+0x47c/0x490
[ 55.829955] [c0cf5ff0] [00003444] 0x3444
[ 55.831823] eth0: hw csum failure
...
Do you want me to run a bit-bisect ?
^ permalink raw reply
* Re: [PATCH v2] net-fq: Add WARN_ON check for null flow.
From: Michał Kazior @ 2018-06-10 17:10 UTC (permalink / raw)
To: Arend van Spriel
Cc: Ben Greear, Cong Wang, Linux Kernel Network Developers,
linux-wireless@vger.kernel.org
In-Reply-To: <5B1AF7D4.9080700@broadcom.com>
Ben,
The patch is symptomatic. fq_tin_dequeue() already checks if the list
is empty before it tries to access first entry. I see no point in
using the _or_null() + WARN_ON.
The 0x3c deref is likely an offset off of NULL base pointer. Did you
check gdb/addr2line of the ieee80211_tx_dequeue+0xfb? Where did it
point to?
I suspect there's not enough synchronization between quescing the
device/ath10k after fw crashes and performing mac80211's reconfig
procedure.
Michał
On 8 June 2018 at 23:40, Arend van Spriel <arend.vanspriel@broadcom.com> wrote:
> On 6/8/2018 5:17 PM, Ben Greear wrote:
>
> I recalled an email from Michał leaving tieto so adding his alternate email
> he provided back then.
>
> Gr. AvS
>
>
>> On 06/07/2018 04:59 PM, Cong Wang wrote:
>>>
>>> On Thu, Jun 7, 2018 at 4:48 PM, <greearb@candelatech.com> wrote:
>>>>
>>>> diff --git a/include/net/fq_impl.h b/include/net/fq_impl.h
>>>> index be7c0fa..cb911f0 100644
>>>> --- a/include/net/fq_impl.h
>>>> +++ b/include/net/fq_impl.h
>>>> @@ -78,7 +78,10 @@ static struct sk_buff *fq_tin_dequeue(struct fq *fq,
>>>> return NULL;
>>>> }
>>>>
>>>> - flow = list_first_entry(head, struct fq_flow, flowchain);
>>>> + flow = list_first_entry_or_null(head, struct fq_flow,
>>>> flowchain);
>>>> +
>>>> + if (WARN_ON_ONCE(!flow))
>>>> + return NULL;
>>>
>>>
>>> This does not make sense either. list_first_entry_or_null()
>>> returns NULL only when the list is empty, but we already check
>>> list_empty() right before this code, and it is protected by fq->lock.
>>>
>>
>> Hello Michal,
>>
>> git blame shows you as the author of the fq_impl.h code.
>>
>> I saw a crash when debugging funky ath10k firmware in a 4.16 + hacks
>> kernel. There was an apparent
>> mostly-null deref in the fq_tin_dequeue method. According to gdb, it
>> was within
>> 1 line of the dereference of 'flow'.
>>
>> My hack above is probably not that useful. Cong thinks maybe the
>> locking is bad.
>>
>> If you get a chance, please review this thread and see if you have any
>> ideas for
>> a better fix (or better debugging code).
>>
>> As always, if you would like me to generate you a buggy firmware that
>> will crash
>> in the tx path and cause all sorts of mayhem in the ath10k driver and
>> wifi stack,
>> I will be happy to do so.
>>
>> https://www.mail-archive.com/netdev@vger.kernel.org/msg239738.html
>>
>> Thanks,
>> Ben
>>
>
^ permalink raw reply
* Dear Talented
From: Lisa Clement @ 2018-06-10 16:57 UTC (permalink / raw)
To: Recipients
Dear Talented,
I am Talent Scout For BLUE SKY FILM STUDIO, Present Blue sky Studio a
Film Corporation Located in the United State, is Soliciting for the
Right to use Your Photo/Face and Personality as One of the Semi -Major
Role/ Character in our Upcoming ANIMATED Stereoscope 3D Movie-The Story
of Spies in Disguise (Spies in Disguise 2019) The Movie is Currently Filming (In
Production) Please Note That There Will Be No Auditions, Traveling or
Any Special / Professional Acting Skills, Since the Production of This
Movie Will Be Done with our State of Art Computer -Generating Imagery
Equipment. We Are Prepared to Pay the Total Sum of $620,000.00 USD. For
More Information/Understanding, Please Write us on the E-Mail Below.
CONTACT EMAIL: bluesky.filmstudio@usa.com
All Reply to: bluesky.filmstudio@usa.com
Note: Only the Response send to this mail will be Given a Prior
Consideration.
Talent Scout
Lisa Clement
^ permalink raw reply
* [PATCH RFC v2 9/9] veth: Bulk skb xmit for XDP path
From: Toshiaki Makita @ 2018-06-10 16:02 UTC (permalink / raw)
To: netdev
Cc: Toshiaki Makita, Jesper Dangaard Brouer, Alexei Starovoitov,
Daniel Borkmann
In-Reply-To: <20180610160217.3146-1-toshiaki.makita1@gmail.com>
From: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Aquire txq lock instead of rxq ptr_ring lock so we avoid per-packet
lock when skb->xmit_more is true. We ensure that rxqs are always not
less than txqs and txq to rxq is one to one mapping, so we can
completely remove rxq side lock.
Since we removed rxq side lock, this change does not increase the number
of locking even when bulk sending is not possible, e.g. non-GSO packets.
Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
---
drivers/net/veth.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 67debd3eafe6..376d70f983e5 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -138,7 +138,7 @@ static void __veth_xdp_flush(struct veth_rq *rq)
static int veth_xdp_rx(struct veth_rq *rq, struct sk_buff *skb)
{
- if (unlikely(ptr_ring_produce(&rq->xdp_ring, skb))) {
+ if (unlikely(__ptr_ring_produce(&rq->xdp_ring, skb))) {
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@@ -188,7 +188,7 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
atomic64_inc(&priv->dropped);
}
- if (rcv_xdp)
+ if (rcv_xdp && !skb->xmit_more)
__veth_xdp_flush(rq);
rcu_read_unlock();
@@ -829,15 +829,21 @@ static netdev_features_t veth_fix_features(struct net_device *dev,
{
struct veth_priv *priv = netdev_priv(dev);
struct net_device *peer;
+ bool xdp = false;
peer = rtnl_dereference(priv->peer);
if (peer) {
struct veth_priv *peer_priv = netdev_priv(peer);
if (rtnl_dereference(peer_priv->rq[0].xdp_prog))
- features &= ~NETIF_F_GSO_SOFTWARE;
+ xdp = true;
}
+ if (xdp)
+ features &= ~(NETIF_F_GSO_SOFTWARE | NETIF_F_LLTX);
+ else
+ features |= NETIF_F_LLTX;
+
return features;
}
--
2.14.3
^ permalink raw reply related
* [PATCH RFC v2 8/9] veth: Support per queue XDP ring
From: Toshiaki Makita @ 2018-06-10 16:02 UTC (permalink / raw)
To: netdev
Cc: Toshiaki Makita, Jesper Dangaard Brouer, Alexei Starovoitov,
Daniel Borkmann
In-Reply-To: <20180610160217.3146-1-toshiaki.makita1@gmail.com>
From: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Move XDP and napi related fields in veth_priv to newly created veth_rq
structure.
When xdp_frames are enqueued from ndo_xdp_xmit and XDP_TX, rxq is
selected by current cpu.
When skbs are enqueued from the peer device, rxq is determined by its
peer's txq. In this way we can implement bulk packet send using
skb->xmit_more later.
Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
---
drivers/net/veth.c | 290 +++++++++++++++++++++++++++++++++++------------------
1 file changed, 191 insertions(+), 99 deletions(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index a47e1ba7d7e6..67debd3eafe6 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -38,20 +38,24 @@ struct pcpu_vstats {
struct u64_stats_sync syncp;
};
-struct veth_priv {
+struct veth_rq {
struct napi_struct xdp_napi;
struct net_device *dev;
struct bpf_prog __rcu *xdp_prog;
- struct net_device __rcu *peer;
- atomic64_t dropped;
struct xdp_mem_info xdp_mem;
- unsigned requested_headroom;
bool rx_notify_masked;
struct ptr_ring xdp_ring;
struct ptr_ring xdp_tx_ring;
struct xdp_rxq_info xdp_rxq;
};
+struct veth_priv {
+ struct net_device __rcu *peer;
+ atomic64_t dropped;
+ struct veth_rq *rq;
+ unsigned int requested_headroom;
+};
+
/*
* ethtool interface
*/
@@ -122,19 +126,19 @@ static void veth_xdp_free(void *frame)
xdp_return_frame(frame);
}
-static void __veth_xdp_flush(struct veth_priv *priv)
+static void __veth_xdp_flush(struct veth_rq *rq)
{
/* Write ptr_ring before reading rx_notify_masked */
smp_mb();
- if (!priv->rx_notify_masked) {
- priv->rx_notify_masked = true;
- napi_schedule(&priv->xdp_napi);
+ if (!rq->rx_notify_masked) {
+ rq->rx_notify_masked = true;
+ napi_schedule(&rq->xdp_napi);
}
}
-static int veth_xdp_rx(struct veth_priv *priv, struct sk_buff *skb)
+static int veth_xdp_rx(struct veth_rq *rq, struct sk_buff *skb)
{
- if (unlikely(ptr_ring_produce(&priv->xdp_ring, skb))) {
+ if (unlikely(ptr_ring_produce(&rq->xdp_ring, skb))) {
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@@ -142,12 +146,11 @@ static int veth_xdp_rx(struct veth_priv *priv, struct sk_buff *skb)
return NET_RX_SUCCESS;
}
-static int veth_forward_skb(struct net_device *dev, struct sk_buff *skb, bool xdp)
+static int veth_forward_skb(struct net_device *dev, struct sk_buff *skb,
+ struct veth_rq *rq, bool xdp)
{
- struct veth_priv *priv = netdev_priv(dev);
-
return __dev_forward_skb(dev, skb) ?: xdp ?
- veth_xdp_rx(priv, skb) :
+ veth_xdp_rx(rq, skb) :
netif_rx(skb);
}
@@ -157,6 +160,8 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
struct net_device *rcv;
int length = skb->len;
bool rcv_xdp = false;
+ struct veth_rq *rq;
+ int rxq;
rcu_read_lock();
rcv = rcu_dereference(priv->peer);
@@ -166,9 +171,12 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
}
rcv_priv = netdev_priv(rcv);
- rcv_xdp = rcu_access_pointer(rcv_priv->xdp_prog);
+ rxq = skb_get_queue_mapping(skb);
+ skb_record_rx_queue(skb, rxq);
+ rq = &rcv_priv->rq[rxq];
+ rcv_xdp = rcu_access_pointer(rq->xdp_prog);
- if (likely(veth_forward_skb(rcv, skb, rcv_xdp) == NET_RX_SUCCESS)) {
+ if (likely(veth_forward_skb(rcv, skb, rq, rcv_xdp) == NET_RX_SUCCESS)) {
struct pcpu_vstats *stats = this_cpu_ptr(dev->vstats);
u64_stats_update_begin(&stats->syncp);
@@ -181,7 +189,7 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
}
if (rcv_xdp)
- __veth_xdp_flush(rcv_priv);
+ __veth_xdp_flush(rq);
rcu_read_unlock();
@@ -256,11 +264,17 @@ static struct sk_buff *veth_build_skb(void *head, int headroom, int len,
return skb;
}
+static int veth_select_rxq(struct net_device *dev)
+{
+ return smp_processor_id() % dev->real_num_rx_queues;
+}
+
static int veth_xdp_xmit(struct net_device *dev, int n,
struct xdp_frame **frames, u32 flags)
{
struct veth_priv *rcv_priv, *priv = netdev_priv(dev);
struct net_device *rcv;
+ struct veth_rq *rq;
int i, drops = 0;
if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
@@ -271,24 +285,25 @@ static int veth_xdp_xmit(struct net_device *dev, int n,
return -ENXIO;
rcv_priv = netdev_priv(rcv);
+ rq = &rcv_priv->rq[veth_select_rxq(rcv)];
/* xdp_ring is initialized on receive side? */
- if (!rcu_access_pointer(rcv_priv->xdp_prog))
+ if (!rcu_access_pointer(rq->xdp_prog))
return -ENXIO;
- spin_lock(&rcv_priv->xdp_tx_ring.producer_lock);
+ spin_lock(&rq->xdp_tx_ring.producer_lock);
for (i = 0; i < n; i++) {
struct xdp_frame *frame = frames[i];
if (unlikely(xdp_ok_fwd_dev(rcv, frame->len) ||
- __ptr_ring_produce(&rcv_priv->xdp_tx_ring, frame))) {
+ __ptr_ring_produce(&rq->xdp_tx_ring, frame))) {
xdp_return_frame_rx_napi(frame);
drops++;
}
}
- spin_unlock(&rcv_priv->xdp_tx_ring.producer_lock);
+ spin_unlock(&rq->xdp_tx_ring.producer_lock);
if (flags & XDP_XMIT_FLUSH)
- __veth_xdp_flush(rcv_priv);
+ __veth_xdp_flush(rq);
return n - drops;
}
@@ -297,6 +312,7 @@ static void veth_xdp_flush(struct net_device *dev)
{
struct veth_priv *rcv_priv, *priv = netdev_priv(dev);
struct net_device *rcv;
+ struct veth_rq *rq;
rcu_read_lock();
rcv = rcu_dereference(priv->peer);
@@ -304,11 +320,12 @@ static void veth_xdp_flush(struct net_device *dev)
goto out;
rcv_priv = netdev_priv(rcv);
+ rq = &rcv_priv->rq[veth_select_rxq(rcv)];
/* xdp_ring is initialized on receive side? */
- if (unlikely(!rcu_access_pointer(rcv_priv->xdp_prog)))
+ if (unlikely(!rcu_access_pointer(rq->xdp_prog)))
goto out;
- __veth_xdp_flush(rcv_priv);
+ __veth_xdp_flush(rq);
out:
rcu_read_unlock();
}
@@ -323,7 +340,7 @@ static int veth_xdp_tx(struct net_device *dev, struct xdp_buff *xdp)
return veth_xdp_xmit(dev, 1, &frame, 0);
}
-static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
+static struct sk_buff *veth_xdp_rcv_one(struct veth_rq *rq,
struct xdp_frame *frame, bool *xdp_xmit,
bool *xdp_redir)
{
@@ -334,7 +351,7 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
struct sk_buff *skb;
rcu_read_lock();
- xdp_prog = rcu_dereference(priv->xdp_prog);
+ xdp_prog = rcu_dereference(rq->xdp_prog);
if (xdp_prog) {
struct xdp_buff xdp;
u32 act;
@@ -343,7 +360,7 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
xdp.data = frame->data;
xdp.data_end = frame->data + frame->len;
xdp.data_meta = frame->data - frame->metasize;
- xdp.rxq = &priv->xdp_rxq;
+ xdp.rxq = &rq->xdp_rxq;
act = bpf_prog_run_xdp(xdp_prog, &xdp);
@@ -357,8 +374,8 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
xdp.data_hard_start = frame;
xdp.rxq->mem = frame->mem;
xdp.rxq->mem.flags |= XDP_MEM_RF_NO_DIRECT;
- if (unlikely(veth_xdp_tx(priv->dev, &xdp))) {
- trace_xdp_exception(priv->dev, xdp_prog, act);
+ if (unlikely(veth_xdp_tx(rq->dev, &xdp))) {
+ trace_xdp_exception(rq->dev, xdp_prog, act);
frame = &orig_frame;
goto err_xdp;
}
@@ -370,7 +387,7 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
xdp.data_hard_start = frame;
xdp.rxq->mem = frame->mem;
xdp.rxq->mem.flags |= XDP_MEM_RF_NO_DIRECT;
- if (xdp_do_redirect(priv->dev, &xdp, xdp_prog)) {
+ if (xdp_do_redirect(rq->dev, &xdp, xdp_prog)) {
frame = &orig_frame;
goto err_xdp;
}
@@ -380,7 +397,7 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
default:
bpf_warn_invalid_xdp_action(act);
case XDP_ABORTED:
- trace_xdp_exception(priv->dev, xdp_prog, act);
+ trace_xdp_exception(rq->dev, xdp_prog, act);
case XDP_DROP:
goto err_xdp;
}
@@ -395,7 +412,7 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
}
memset(frame, 0, sizeof(*frame));
- skb->protocol = eth_type_trans(skb, priv->dev);
+ skb->protocol = eth_type_trans(skb, rq->dev);
err:
return skb;
err_xdp:
@@ -405,9 +422,8 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
return NULL;
}
-static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
- struct sk_buff *skb, bool *xdp_xmit,
- bool *xdp_redir)
+static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, struct sk_buff *skb,
+ bool *xdp_xmit, bool *xdp_redir)
{
u32 pktlen, headroom, act, metalen;
void *orig_data, *orig_data_end;
@@ -416,7 +432,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
struct xdp_buff xdp;
rcu_read_lock();
- xdp_prog = rcu_dereference(priv->xdp_prog);
+ xdp_prog = rcu_dereference(rq->xdp_prog);
if (!xdp_prog) {
rcu_read_unlock();
goto out;
@@ -467,7 +483,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
xdp.data = skb_mac_header(skb);
xdp.data_end = xdp.data + pktlen;
xdp.data_meta = xdp.data;
- xdp.rxq = &priv->xdp_rxq;
+ xdp.rxq = &rq->xdp_rxq;
orig_data = xdp.data;
orig_data_end = xdp.data_end;
@@ -479,9 +495,9 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
case XDP_TX:
get_page(virt_to_page(xdp.data));
dev_consume_skb_any(skb);
- xdp.rxq->mem = priv->xdp_mem;
- if (unlikely(veth_xdp_tx(priv->dev, &xdp))) {
- trace_xdp_exception(priv->dev, xdp_prog, act);
+ xdp.rxq->mem = rq->xdp_mem;
+ if (unlikely(veth_xdp_tx(rq->dev, &xdp))) {
+ trace_xdp_exception(rq->dev, xdp_prog, act);
goto err_xdp;
}
*xdp_xmit = true;
@@ -490,8 +506,8 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
case XDP_REDIRECT:
get_page(virt_to_page(xdp.data));
dev_consume_skb_any(skb);
- xdp.rxq->mem = priv->xdp_mem;
- if (xdp_do_redirect(priv->dev, &xdp, xdp_prog))
+ xdp.rxq->mem = rq->xdp_mem;
+ if (xdp_do_redirect(rq->dev, &xdp, xdp_prog))
goto err_xdp;
*xdp_redir = true;
rcu_read_unlock();
@@ -499,7 +515,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
default:
bpf_warn_invalid_xdp_action(act);
case XDP_ABORTED:
- trace_xdp_exception(priv->dev, xdp_prog, act);
+ trace_xdp_exception(rq->dev, xdp_prog, act);
case XDP_DROP:
goto drop;
}
@@ -515,7 +531,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
off = xdp.data_end - orig_data_end;
if (off != 0)
__skb_put(skb, off);
- skb->protocol = eth_type_trans(skb, priv->dev);
+ skb->protocol = eth_type_trans(skb, rq->dev);
metalen = xdp.data - xdp.data_meta;
if (metalen)
@@ -533,7 +549,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
return NULL;
}
-static int veth_xdp_rcv(struct veth_priv *priv, int budget, bool *xdp_xmit,
+static int veth_xdp_rcv(struct veth_rq *rq, int budget, bool *xdp_xmit,
bool *xdp_redir)
{
int done = 0;
@@ -551,15 +567,15 @@ static int veth_xdp_rcv(struct veth_priv *priv, int budget, bool *xdp_xmit,
struct xdp_frame *frame;
struct sk_buff *skb;
- frame = __ptr_ring_consume(&priv->xdp_tx_ring);
+ frame = __ptr_ring_consume(&rq->xdp_tx_ring);
if (!frame) {
curr_more = false;
break;
}
- skb = veth_xdp_rcv_one(priv, frame, xdp_xmit, xdp_redir);
+ skb = veth_xdp_rcv_one(rq, frame, xdp_xmit, xdp_redir);
if (skb)
- napi_gro_receive(&priv->xdp_napi, skb);
+ napi_gro_receive(&rq->xdp_napi, skb);
done++;
}
@@ -568,16 +584,16 @@ static int veth_xdp_rcv(struct veth_priv *priv, int budget, bool *xdp_xmit,
curr_more = true;
curr_budget = min(budget - done, budget >> 1);
for (i = 0; i < curr_budget; i++) {
- struct sk_buff *skb = __ptr_ring_consume(&priv->xdp_ring);
+ struct sk_buff *skb = __ptr_ring_consume(&rq->xdp_ring);
if (!skb) {
curr_more = false;
break;
}
- skb = veth_xdp_rcv_skb(priv, skb, xdp_xmit, xdp_redir);
+ skb = veth_xdp_rcv_skb(rq, skb, xdp_xmit, xdp_redir);
if (skb)
- napi_gro_receive(&priv->xdp_napi, skb);
+ napi_gro_receive(&rq->xdp_napi, skb);
done++;
}
@@ -589,26 +605,26 @@ static int veth_xdp_rcv(struct veth_priv *priv, int budget, bool *xdp_xmit,
static int veth_poll(struct napi_struct *napi, int budget)
{
- struct veth_priv *priv =
- container_of(napi, struct veth_priv, xdp_napi);
+ struct veth_rq *rq =
+ container_of(napi, struct veth_rq, xdp_napi);
bool xdp_xmit = false;
bool xdp_redir = false;
int done;
- done = veth_xdp_rcv(priv, budget, &xdp_xmit, &xdp_redir);
+ done = veth_xdp_rcv(rq, budget, &xdp_xmit, &xdp_redir);
if (done < budget && napi_complete_done(napi, done)) {
/* Write rx_notify_masked before reading ptr_ring */
- smp_store_mb(priv->rx_notify_masked, false);
- if (unlikely(!__ptr_ring_empty(&priv->xdp_tx_ring) ||
- !__ptr_ring_empty(&priv->xdp_ring))) {
- priv->rx_notify_masked = true;
- napi_schedule(&priv->xdp_napi);
+ smp_store_mb(rq->rx_notify_masked, false);
+ if (unlikely(!__ptr_ring_empty(&rq->xdp_tx_ring) ||
+ !__ptr_ring_empty(&rq->xdp_ring))) {
+ rq->rx_notify_masked = true;
+ napi_schedule(&rq->xdp_napi);
}
}
if (xdp_xmit)
- veth_xdp_flush(priv->dev);
+ veth_xdp_flush(rq->dev);
if (xdp_redir)
xdp_do_flush_map();
@@ -618,22 +634,36 @@ static int veth_poll(struct napi_struct *napi, int budget)
static int veth_napi_add(struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
- int err;
+ int err, i;
- err = ptr_ring_init(&priv->xdp_ring, VETH_RING_SIZE, GFP_KERNEL);
- if (err)
- return err;
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
- err = ptr_ring_init(&priv->xdp_tx_ring, VETH_RING_SIZE, GFP_KERNEL);
- if (err)
- goto err_xdp_tx_ring;
+ err = ptr_ring_init(&rq->xdp_ring, VETH_RING_SIZE, GFP_KERNEL);
+ if (err)
+ goto err_xdp_ring;
+
+ err = ptr_ring_init(&rq->xdp_tx_ring, VETH_RING_SIZE,
+ GFP_KERNEL);
+ if (err)
+ goto err_xdp_tx_ring;
+ }
+
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
- netif_napi_add(dev, &priv->xdp_napi, veth_poll, NAPI_POLL_WEIGHT);
- napi_enable(&priv->xdp_napi);
+ netif_napi_add(dev, &rq->xdp_napi, veth_poll, NAPI_POLL_WEIGHT);
+ napi_enable(&rq->xdp_napi);
+ }
return 0;
err_xdp_tx_ring:
- ptr_ring_cleanup(&priv->xdp_ring, __skb_array_destroy_skb);
+ ptr_ring_cleanup(&priv->rq[i].xdp_ring, __skb_array_destroy_skb);
+err_xdp_ring:
+ for (i--; i >= 0; i--) {
+ ptr_ring_cleanup(&priv->rq[i].xdp_ring, __skb_array_destroy_skb);
+ ptr_ring_cleanup(&priv->rq[i].xdp_tx_ring, veth_xdp_free);
+ }
return err;
}
@@ -641,37 +671,56 @@ static int veth_napi_add(struct net_device *dev)
static void veth_napi_del(struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
+ int i;
- napi_disable(&priv->xdp_napi);
- netif_napi_del(&priv->xdp_napi);
- ptr_ring_cleanup(&priv->xdp_ring, __skb_array_destroy_skb);
- ptr_ring_cleanup(&priv->xdp_tx_ring, veth_xdp_free);
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
+
+ napi_disable(&rq->xdp_napi);
+ napi_hash_del(&rq->xdp_napi);
+ }
+ synchronize_net();
+
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
+
+ netif_napi_del(&rq->xdp_napi);
+ ptr_ring_cleanup(&rq->xdp_ring, __skb_array_destroy_skb);
+ ptr_ring_cleanup(&rq->xdp_tx_ring, veth_xdp_free);
+ }
}
static int veth_enable_xdp(struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
- int err;
+ int err, i;
- err = xdp_rxq_info_reg(&priv->xdp_rxq, dev, 0);
- if (err < 0)
- return err;
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
- err = xdp_rxq_info_reg_mem_model(&priv->xdp_rxq,
- MEM_TYPE_PAGE_SHARED, NULL);
- if (err < 0)
- goto err;
+ err = xdp_rxq_info_reg(&rq->xdp_rxq, dev, i);
+ if (err < 0)
+ goto err_rxq_reg;
+
+ err = xdp_rxq_info_reg_mem_model(&rq->xdp_rxq,
+ MEM_TYPE_PAGE_SHARED, NULL);
+ if (err < 0)
+ goto err_reg_mem;
- /* Save original mem info as it can be overwritten */
- priv->xdp_mem = priv->xdp_rxq.mem;
+ /* Save original mem info as it can be overwritten */
+ rq->xdp_mem = rq->xdp_rxq.mem;
+ }
err = veth_napi_add(dev);
if (err)
- goto err;
+ goto err_rxq_reg;
return 0;
-err:
- xdp_rxq_info_unreg(&priv->xdp_rxq);
+err_reg_mem:
+ xdp_rxq_info_unreg(&priv->rq[i].xdp_rxq);
+err_rxq_reg:
+ for (i--; i >= 0; i--)
+ xdp_rxq_info_unreg(&priv->rq[i].xdp_rxq);
return err;
}
@@ -679,10 +728,15 @@ static int veth_enable_xdp(struct net_device *dev)
static void veth_disable_xdp(struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
+ int i;
veth_napi_del(dev);
- priv->xdp_rxq.mem = priv->xdp_mem;
- xdp_rxq_info_unreg(&priv->xdp_rxq);
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
+
+ rq->xdp_rxq.mem = rq->xdp_mem;
+ xdp_rxq_info_unreg(&rq->xdp_rxq);
+ }
}
static int veth_open(struct net_device *dev)
@@ -694,7 +748,7 @@ static int veth_open(struct net_device *dev)
if (!peer)
return -ENOTCONN;
- if (rtnl_dereference(priv->xdp_prog)) {
+ if (rtnl_dereference(priv->rq[0].xdp_prog)) {
err = veth_enable_xdp(dev);
if (err)
return err;
@@ -717,7 +771,7 @@ static int veth_close(struct net_device *dev)
if (peer)
netif_carrier_off(peer);
- if (rtnl_dereference(priv->xdp_prog))
+ if (rtnl_dereference(priv->rq[0].xdp_prog))
veth_disable_xdp(dev);
return 0;
@@ -780,7 +834,7 @@ static netdev_features_t veth_fix_features(struct net_device *dev,
if (peer) {
struct veth_priv *peer_priv = netdev_priv(peer);
- if (rtnl_dereference(peer_priv->xdp_prog))
+ if (rtnl_dereference(peer_priv->rq[0].xdp_prog))
features &= ~NETIF_F_GSO_SOFTWARE;
}
@@ -816,9 +870,9 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog,
struct veth_priv *priv = netdev_priv(dev);
struct bpf_prog *old_prog;
struct net_device *peer;
- int err;
+ int err, i;
- old_prog = rtnl_dereference(priv->xdp_prog);
+ old_prog = rtnl_dereference(priv->rq[0].xdp_prog);
peer = rtnl_dereference(priv->peer);
if (prog) {
@@ -826,6 +880,9 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog,
return -ENOTCONN;
if (!old_prog) {
+ if (dev->real_num_rx_queues < peer->real_num_tx_queues)
+ return -ENOSPC;
+
if (dev->flags & IFF_UP) {
err = veth_enable_xdp(dev);
if (err)
@@ -841,7 +898,8 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog,
}
}
- rcu_assign_pointer(priv->xdp_prog, prog);
+ for (i = 0; i < dev->real_num_rx_queues; i++)
+ rcu_assign_pointer(priv->rq[i].xdp_prog, prog);
if (old_prog) {
bpf_prog_put(old_prog);
@@ -867,7 +925,7 @@ static u32 veth_xdp_query(struct net_device *dev)
struct veth_priv *priv = netdev_priv(dev);
const struct bpf_prog *xdp_prog;
- xdp_prog = rtnl_dereference(priv->xdp_prog);
+ xdp_prog = rtnl_dereference(priv->rq[0].xdp_prog);
if (xdp_prog)
return xdp_prog->aux->id;
@@ -960,13 +1018,31 @@ static int veth_validate(struct nlattr *tb[], struct nlattr *data[],
return 0;
}
+static int veth_alloc_queues(struct net_device *dev)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+
+ priv->rq = kcalloc(dev->num_rx_queues, sizeof(*priv->rq), GFP_KERNEL);
+ if (!priv->rq)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void veth_free_queues(struct net_device *dev)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+
+ kfree(priv->rq);
+}
+
static struct rtnl_link_ops veth_link_ops;
static int veth_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
- int err;
+ int err, i;
struct net_device *peer;
struct veth_priv *priv;
char ifname[IFNAMSIZ];
@@ -1019,6 +1095,12 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
return PTR_ERR(peer);
}
+ err = veth_alloc_queues(peer);
+ if (err) {
+ put_net(net);
+ goto err_peer_alloc_queues;
+ }
+
if (!ifmp || !tbp[IFLA_ADDRESS])
eth_hw_addr_random(peer);
@@ -1047,6 +1129,10 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
* should be re-allocated
*/
+ err = veth_alloc_queues(dev);
+ if (err)
+ goto err_alloc_queues;
+
if (tb[IFLA_ADDRESS] == NULL)
eth_hw_addr_random(dev);
@@ -1066,22 +1152,28 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
*/
priv = netdev_priv(dev);
- priv->dev = dev;
+ for (i = 0; i < dev->real_num_rx_queues; i++)
+ priv->rq[i].dev = dev;
rcu_assign_pointer(priv->peer, peer);
priv = netdev_priv(peer);
- priv->dev = peer;
+ for (i = 0; i < peer->real_num_rx_queues; i++)
+ priv->rq[i].dev = peer;
rcu_assign_pointer(priv->peer, dev);
return 0;
err_register_dev:
+ veth_free_queues(dev);
+err_alloc_queues:
/* nothing to do */
err_configure_peer:
unregister_netdevice(peer);
return err;
err_register_peer:
+ veth_free_queues(peer);
+err_peer_alloc_queues:
free_netdev(peer);
return err;
}
--
2.14.3
^ permalink raw reply related
* [PATCH RFC v2 7/9] veth: Add XDP TX and REDIRECT
From: Toshiaki Makita @ 2018-06-10 16:02 UTC (permalink / raw)
To: netdev
Cc: Toshiaki Makita, Jesper Dangaard Brouer, Alexei Starovoitov,
Daniel Borkmann
In-Reply-To: <20180610160217.3146-1-toshiaki.makita1@gmail.com>
From: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
This allows further redirection of xdp_frames like
NIC -> veth--veth -> veth--veth
(XDP) (XDP) (XDP)
The intermediate XDP, redirecting packets from NIC to the other veth,
reuses xdp_mem_info from NIC so that page recycling of the NIC works on
the destination veth's XDP.
In this way return_frame is not fully guarded by NAPI, since another
NAPI handler on another cpu may use the same xdp_mem_info concurrently.
Thus disable napi_direct by XDP_MEM_RF_NO_DIRECT flag.
Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
---
drivers/net/veth.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 103 insertions(+), 7 deletions(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index b809d609a642..a47e1ba7d7e6 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -44,6 +44,7 @@ struct veth_priv {
struct bpf_prog __rcu *xdp_prog;
struct net_device __rcu *peer;
atomic64_t dropped;
+ struct xdp_mem_info xdp_mem;
unsigned requested_headroom;
bool rx_notify_masked;
struct ptr_ring xdp_ring;
@@ -292,10 +293,42 @@ static int veth_xdp_xmit(struct net_device *dev, int n,
return n - drops;
}
+static void veth_xdp_flush(struct net_device *dev)
+{
+ struct veth_priv *rcv_priv, *priv = netdev_priv(dev);
+ struct net_device *rcv;
+
+ rcu_read_lock();
+ rcv = rcu_dereference(priv->peer);
+ if (unlikely(!rcv))
+ goto out;
+
+ rcv_priv = netdev_priv(rcv);
+ /* xdp_ring is initialized on receive side? */
+ if (unlikely(!rcu_access_pointer(rcv_priv->xdp_prog)))
+ goto out;
+
+ __veth_xdp_flush(rcv_priv);
+out:
+ rcu_read_unlock();
+}
+
+static int veth_xdp_tx(struct net_device *dev, struct xdp_buff *xdp)
+{
+ struct xdp_frame *frame = convert_to_xdp_frame(xdp);
+
+ if (unlikely(!frame))
+ return -EOVERFLOW;
+
+ return veth_xdp_xmit(dev, 1, &frame, 0);
+}
+
static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
- struct xdp_frame *frame)
+ struct xdp_frame *frame, bool *xdp_xmit,
+ bool *xdp_redir)
{
int len = frame->len, delta = 0;
+ struct xdp_frame orig_frame;
struct bpf_prog *xdp_prog;
unsigned int headroom;
struct sk_buff *skb;
@@ -319,6 +352,31 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
delta = frame->data - xdp.data;
len = xdp.data_end - xdp.data;
break;
+ case XDP_TX:
+ orig_frame = *frame;
+ xdp.data_hard_start = frame;
+ xdp.rxq->mem = frame->mem;
+ xdp.rxq->mem.flags |= XDP_MEM_RF_NO_DIRECT;
+ if (unlikely(veth_xdp_tx(priv->dev, &xdp))) {
+ trace_xdp_exception(priv->dev, xdp_prog, act);
+ frame = &orig_frame;
+ goto err_xdp;
+ }
+ *xdp_xmit = true;
+ rcu_read_unlock();
+ goto xdp_xmit;
+ case XDP_REDIRECT:
+ orig_frame = *frame;
+ xdp.data_hard_start = frame;
+ xdp.rxq->mem = frame->mem;
+ xdp.rxq->mem.flags |= XDP_MEM_RF_NO_DIRECT;
+ if (xdp_do_redirect(priv->dev, &xdp, xdp_prog)) {
+ frame = &orig_frame;
+ goto err_xdp;
+ }
+ *xdp_redir = true;
+ rcu_read_unlock();
+ goto xdp_xmit;
default:
bpf_warn_invalid_xdp_action(act);
case XDP_ABORTED:
@@ -343,12 +401,13 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
err_xdp:
rcu_read_unlock();
xdp_return_frame(frame);
-
+xdp_xmit:
return NULL;
}
static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
- struct sk_buff *skb)
+ struct sk_buff *skb, bool *xdp_xmit,
+ bool *xdp_redir)
{
u32 pktlen, headroom, act, metalen;
void *orig_data, *orig_data_end;
@@ -417,6 +476,26 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
switch (act) {
case XDP_PASS:
break;
+ case XDP_TX:
+ get_page(virt_to_page(xdp.data));
+ dev_consume_skb_any(skb);
+ xdp.rxq->mem = priv->xdp_mem;
+ if (unlikely(veth_xdp_tx(priv->dev, &xdp))) {
+ trace_xdp_exception(priv->dev, xdp_prog, act);
+ goto err_xdp;
+ }
+ *xdp_xmit = true;
+ rcu_read_unlock();
+ goto xdp_xmit;
+ case XDP_REDIRECT:
+ get_page(virt_to_page(xdp.data));
+ dev_consume_skb_any(skb);
+ xdp.rxq->mem = priv->xdp_mem;
+ if (xdp_do_redirect(priv->dev, &xdp, xdp_prog))
+ goto err_xdp;
+ *xdp_redir = true;
+ rcu_read_unlock();
+ goto xdp_xmit;
default:
bpf_warn_invalid_xdp_action(act);
case XDP_ABORTED:
@@ -447,9 +526,15 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
rcu_read_unlock();
dev_kfree_skb_any(skb);
return NULL;
+err_xdp:
+ rcu_read_unlock();
+ page_frag_free(xdp.data);
+xdp_xmit:
+ return NULL;
}
-static int veth_xdp_rcv(struct veth_priv *priv, int budget)
+static int veth_xdp_rcv(struct veth_priv *priv, int budget, bool *xdp_xmit,
+ bool *xdp_redir)
{
int done = 0;
bool more;
@@ -472,7 +557,7 @@ static int veth_xdp_rcv(struct veth_priv *priv, int budget)
break;
}
- skb = veth_xdp_rcv_one(priv, frame);
+ skb = veth_xdp_rcv_one(priv, frame, xdp_xmit, xdp_redir);
if (skb)
napi_gro_receive(&priv->xdp_napi, skb);
@@ -490,7 +575,7 @@ static int veth_xdp_rcv(struct veth_priv *priv, int budget)
break;
}
- skb = veth_xdp_rcv_skb(priv, skb);
+ skb = veth_xdp_rcv_skb(priv, skb, xdp_xmit, xdp_redir);
if (skb)
napi_gro_receive(&priv->xdp_napi, skb);
@@ -506,9 +591,11 @@ static int veth_poll(struct napi_struct *napi, int budget)
{
struct veth_priv *priv =
container_of(napi, struct veth_priv, xdp_napi);
+ bool xdp_xmit = false;
+ bool xdp_redir = false;
int done;
- done = veth_xdp_rcv(priv, budget);
+ done = veth_xdp_rcv(priv, budget, &xdp_xmit, &xdp_redir);
if (done < budget && napi_complete_done(napi, done)) {
/* Write rx_notify_masked before reading ptr_ring */
@@ -520,6 +607,11 @@ static int veth_poll(struct napi_struct *napi, int budget)
}
}
+ if (xdp_xmit)
+ veth_xdp_flush(priv->dev);
+ if (xdp_redir)
+ xdp_do_flush_map();
+
return done;
}
@@ -570,6 +662,9 @@ static int veth_enable_xdp(struct net_device *dev)
if (err < 0)
goto err;
+ /* Save original mem info as it can be overwritten */
+ priv->xdp_mem = priv->xdp_rxq.mem;
+
err = veth_napi_add(dev);
if (err)
goto err;
@@ -586,6 +681,7 @@ static void veth_disable_xdp(struct net_device *dev)
struct veth_priv *priv = netdev_priv(dev);
veth_napi_del(dev);
+ priv->xdp_rxq.mem = priv->xdp_mem;
xdp_rxq_info_unreg(&priv->xdp_rxq);
}
--
2.14.3
^ permalink raw reply related
* [PATCH RFC v2 6/9] xdp: Add a flag for disabling napi_direct of xdp_return_frame in xdp_mem_info
From: Toshiaki Makita @ 2018-06-10 16:02 UTC (permalink / raw)
To: netdev
Cc: Toshiaki Makita, Jesper Dangaard Brouer, Alexei Starovoitov,
Daniel Borkmann
In-Reply-To: <20180610160217.3146-1-toshiaki.makita1@gmail.com>
From: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
We need some mechanism to disable napi_direct on calling
xdp_return_frame_rx_napi() from some context.
When veth gets support of XDP_REDIRECT, it will redirects packets which
are redirected from other devices. On redirection veth will reuse
xdp_mem_info of the redirection source device to make return_frame work.
But in this case .ndo_xdp_xmit() called from veth redirection uses
xdp_mem_info which is not guarded by NAPI, because the .ndo_xdp_xmit is
not called directly from the rxq which owns the xdp_mem_info.
This approach introduces a flag in xdp_mem_info to indicate that
napi_direct should be disabled even when _rx_napi variant is used.
Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
---
include/net/xdp.h | 4 ++++
net/core/xdp.c | 6 ++++--
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/include/net/xdp.h b/include/net/xdp.h
index 2deea7166a34..ea0c80f6c8ee 100644
--- a/include/net/xdp.h
+++ b/include/net/xdp.h
@@ -41,6 +41,9 @@ enum xdp_mem_type {
MEM_TYPE_MAX,
};
+/* XDP flags for xdp_mem_info */
+#define XDP_MEM_RF_NO_DIRECT BIT(0) /* don't use napi_direct */
+
/* XDP flags for ndo_xdp_xmit */
#define XDP_XMIT_FLUSH (1U << 0) /* doorbell signal consumer */
#define XDP_XMIT_FLAGS_MASK XDP_XMIT_FLUSH
@@ -48,6 +51,7 @@ enum xdp_mem_type {
struct xdp_mem_info {
u32 type; /* enum xdp_mem_type, but known size type */
u32 id;
+ u32 flags;
};
struct page_pool;
diff --git a/net/core/xdp.c b/net/core/xdp.c
index 9d1f22072d5d..e94f146360b2 100644
--- a/net/core/xdp.c
+++ b/net/core/xdp.c
@@ -327,10 +327,12 @@ static void __xdp_return(void *data, struct xdp_mem_info *mem, bool napi_direct,
/* mem->id is valid, checked in xdp_rxq_info_reg_mem_model() */
xa = rhashtable_lookup(mem_id_ht, &mem->id, mem_id_rht_params);
page = virt_to_head_page(data);
- if (xa)
+ if (xa) {
+ napi_direct &= !(mem->flags & XDP_MEM_RF_NO_DIRECT);
page_pool_put_page(xa->page_pool, page, napi_direct);
- else
+ } else {
put_page(page);
+ }
rcu_read_unlock();
break;
case MEM_TYPE_PAGE_SHARED:
--
2.14.3
^ permalink raw reply related
* [PATCH RFC v2 5/9] veth: Add ndo_xdp_xmit
From: Toshiaki Makita @ 2018-06-10 16:02 UTC (permalink / raw)
To: netdev
Cc: Toshiaki Makita, Jesper Dangaard Brouer, Alexei Starovoitov,
Daniel Borkmann
In-Reply-To: <20180610160217.3146-1-toshiaki.makita1@gmail.com>
From: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
This allows NIC's XDP to redirect packets to veth. The destination veth
device enqueues redirected packets to the napi ring of its peer, then
they are processed by XDP on its peer veth device.
This can be thought as calling another XDP program by XDP program using
REDIRECT, when the peer enables driver XDP.
Note that when the peer veth device does not set driver xdp, redirected
packets will be dropped because the peer is not ready for NAPI.
v2:
- Drop the part converting xdp_frame into skb when XDP is not enabled.
- Implement bulk interface of ndo_xdp_xmit.
- Implement XDP_XMIT_FLUSH bit and drop ndo_xdp_flush.
Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
---
drivers/net/veth.c | 39 +++++++++++++++++++++++++++++++++++++++
include/linux/filter.h | 16 ++++++++++++++++
net/core/filter.c | 11 +----------
3 files changed, 56 insertions(+), 10 deletions(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index cb3fa558fbe0..b809d609a642 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -17,6 +17,7 @@
#include <net/rtnetlink.h>
#include <net/dst.h>
#include <net/xfrm.h>
+#include <net/xdp.h>
#include <linux/veth.h>
#include <linux/module.h>
#include <linux/bpf.h>
@@ -254,6 +255,43 @@ static struct sk_buff *veth_build_skb(void *head, int headroom, int len,
return skb;
}
+static int veth_xdp_xmit(struct net_device *dev, int n,
+ struct xdp_frame **frames, u32 flags)
+{
+ struct veth_priv *rcv_priv, *priv = netdev_priv(dev);
+ struct net_device *rcv;
+ int i, drops = 0;
+
+ if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
+ return -EINVAL;
+
+ rcv = rcu_dereference(priv->peer);
+ if (unlikely(!rcv))
+ return -ENXIO;
+
+ rcv_priv = netdev_priv(rcv);
+ /* xdp_ring is initialized on receive side? */
+ if (!rcu_access_pointer(rcv_priv->xdp_prog))
+ return -ENXIO;
+
+ spin_lock(&rcv_priv->xdp_tx_ring.producer_lock);
+ for (i = 0; i < n; i++) {
+ struct xdp_frame *frame = frames[i];
+
+ if (unlikely(xdp_ok_fwd_dev(rcv, frame->len) ||
+ __ptr_ring_produce(&rcv_priv->xdp_tx_ring, frame))) {
+ xdp_return_frame_rx_napi(frame);
+ drops++;
+ }
+ }
+ spin_unlock(&rcv_priv->xdp_tx_ring.producer_lock);
+
+ if (flags & XDP_XMIT_FLUSH)
+ __veth_xdp_flush(rcv_priv);
+
+ return n - drops;
+}
+
static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
struct xdp_frame *frame)
{
@@ -770,6 +808,7 @@ static const struct net_device_ops veth_netdev_ops = {
.ndo_features_check = passthru_features_check,
.ndo_set_rx_headroom = veth_set_rx_headroom,
.ndo_bpf = veth_xdp,
+ .ndo_xdp_xmit = veth_xdp_xmit,
};
#define VETH_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HW_CSUM | \
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 45fc0f5000d8..12777eb70b40 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -19,6 +19,7 @@
#include <linux/cryptohash.h>
#include <linux/set_memory.h>
#include <linux/kallsyms.h>
+#include <linux/if_vlan.h>
#include <net/sch_generic.h>
@@ -786,6 +787,21 @@ static inline bool bpf_dump_raw_ok(void)
struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
const struct bpf_insn *patch, u32 len);
+static __always_inline int
+xdp_ok_fwd_dev(const struct net_device *fwd, unsigned int pktlen)
+{
+ unsigned int len;
+
+ if (unlikely(!(fwd->flags & IFF_UP)))
+ return -ENETDOWN;
+
+ len = fwd->mtu + fwd->hard_header_len + VLAN_HLEN;
+ if (pktlen > len)
+ return -EMSGSIZE;
+
+ return 0;
+}
+
/* The pair of xdp_do_redirect and xdp_do_flush_map MUST be called in the
* same cpu context. Further for best results no more than a single map
* for the do_redirect/do_flush pair should be used. This limitation is
diff --git a/net/core/filter.c b/net/core/filter.c
index 3d9ba7e5965a..05d9e84566a4 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -3216,16 +3216,7 @@ EXPORT_SYMBOL_GPL(xdp_do_redirect);
static int __xdp_generic_ok_fwd_dev(struct sk_buff *skb, struct net_device *fwd)
{
- unsigned int len;
-
- if (unlikely(!(fwd->flags & IFF_UP)))
- return -ENETDOWN;
-
- len = fwd->mtu + fwd->hard_header_len + VLAN_HLEN;
- if (skb->len > len)
- return -EMSGSIZE;
-
- return 0;
+ return xdp_ok_fwd_dev(fwd, skb->len);
}
static int xdp_do_generic_redirect_map(struct net_device *dev,
--
2.14.3
^ permalink raw reply related
* [PATCH RFC v2 4/9] veth: Add another napi ring for ndo_xdp_xmit and handle xdp_frames
From: Toshiaki Makita @ 2018-06-10 16:02 UTC (permalink / raw)
To: netdev
Cc: Toshiaki Makita, Jesper Dangaard Brouer, Alexei Starovoitov,
Daniel Borkmann
In-Reply-To: <20180610160217.3146-1-toshiaki.makita1@gmail.com>
From: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
This is preparation for XDP TX and ndo_xdp_xmit.
Add another napi ring and handle redirected xdp_frames through it.
v2:
- Use another ring instead of using flag to differentiate skb and
xdp_frame. This approach makes bulk skb transmit possible in
veth_xmit later.
- Clear xdp_frame feilds in skb->head.
- Implement adjust_tail.
Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
---
drivers/net/veth.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 114 insertions(+), 11 deletions(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 88d349da72cc..cb3fa558fbe0 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -46,6 +46,7 @@ struct veth_priv {
unsigned requested_headroom;
bool rx_notify_masked;
struct ptr_ring xdp_ring;
+ struct ptr_ring xdp_tx_ring;
struct xdp_rxq_info xdp_rxq;
};
@@ -114,6 +115,11 @@ static const struct ethtool_ops veth_ethtool_ops = {
/* general routines */
+static void veth_xdp_free(void *frame)
+{
+ xdp_return_frame(frame);
+}
+
static void __veth_xdp_flush(struct veth_priv *priv)
{
/* Write ptr_ring before reading rx_notify_masked */
@@ -248,6 +254,61 @@ static struct sk_buff *veth_build_skb(void *head, int headroom, int len,
return skb;
}
+static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
+ struct xdp_frame *frame)
+{
+ int len = frame->len, delta = 0;
+ struct bpf_prog *xdp_prog;
+ unsigned int headroom;
+ struct sk_buff *skb;
+
+ rcu_read_lock();
+ xdp_prog = rcu_dereference(priv->xdp_prog);
+ if (xdp_prog) {
+ struct xdp_buff xdp;
+ u32 act;
+
+ xdp.data_hard_start = frame->data - frame->headroom;
+ xdp.data = frame->data;
+ xdp.data_end = frame->data + frame->len;
+ xdp.data_meta = frame->data - frame->metasize;
+ xdp.rxq = &priv->xdp_rxq;
+
+ act = bpf_prog_run_xdp(xdp_prog, &xdp);
+
+ switch (act) {
+ case XDP_PASS:
+ delta = frame->data - xdp.data;
+ len = xdp.data_end - xdp.data;
+ break;
+ default:
+ bpf_warn_invalid_xdp_action(act);
+ case XDP_ABORTED:
+ trace_xdp_exception(priv->dev, xdp_prog, act);
+ case XDP_DROP:
+ goto err_xdp;
+ }
+ }
+ rcu_read_unlock();
+
+ headroom = frame->data - delta - (void *)frame;
+ skb = veth_build_skb(frame, headroom, len, 0);
+ if (!skb) {
+ xdp_return_frame(frame);
+ goto err;
+ }
+
+ memset(frame, 0, sizeof(*frame));
+ skb->protocol = eth_type_trans(skb, priv->dev);
+err:
+ return skb;
+err_xdp:
+ rcu_read_unlock();
+ xdp_return_frame(frame);
+
+ return NULL;
+}
+
static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
struct sk_buff *skb)
{
@@ -352,21 +413,53 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
static int veth_xdp_rcv(struct veth_priv *priv, int budget)
{
- int i, done = 0;
+ int done = 0;
+ bool more;
- for (i = 0; i < budget; i++) {
- struct sk_buff *skb = __ptr_ring_consume(&priv->xdp_ring);
+ do {
+ int curr_budget, i;
+ bool curr_more;
- if (!skb)
- break;
+ more = false;
- skb = veth_xdp_rcv_skb(priv, skb);
+ curr_more = true;
+ curr_budget = min(budget - done, budget >> 1);
+ for (i = 0; i < curr_budget; i++) {
+ struct xdp_frame *frame;
+ struct sk_buff *skb;
- if (skb)
- napi_gro_receive(&priv->xdp_napi, skb);
+ frame = __ptr_ring_consume(&priv->xdp_tx_ring);
+ if (!frame) {
+ curr_more = false;
+ break;
+ }
- done++;
- }
+ skb = veth_xdp_rcv_one(priv, frame);
+ if (skb)
+ napi_gro_receive(&priv->xdp_napi, skb);
+
+ done++;
+ }
+ more |= curr_more;
+
+ curr_more = true;
+ curr_budget = min(budget - done, budget >> 1);
+ for (i = 0; i < curr_budget; i++) {
+ struct sk_buff *skb = __ptr_ring_consume(&priv->xdp_ring);
+
+ if (!skb) {
+ curr_more = false;
+ break;
+ }
+
+ skb = veth_xdp_rcv_skb(priv, skb);
+ if (skb)
+ napi_gro_receive(&priv->xdp_napi, skb);
+
+ done++;
+ }
+ more |= curr_more;
+ } while (more && done < budget);
return done;
}
@@ -382,7 +475,8 @@ static int veth_poll(struct napi_struct *napi, int budget)
if (done < budget && napi_complete_done(napi, done)) {
/* Write rx_notify_masked before reading ptr_ring */
smp_store_mb(priv->rx_notify_masked, false);
- if (unlikely(!__ptr_ring_empty(&priv->xdp_ring))) {
+ if (unlikely(!__ptr_ring_empty(&priv->xdp_tx_ring) ||
+ !__ptr_ring_empty(&priv->xdp_ring))) {
priv->rx_notify_masked = true;
napi_schedule(&priv->xdp_napi);
}
@@ -400,10 +494,18 @@ static int veth_napi_add(struct net_device *dev)
if (err)
return err;
+ err = ptr_ring_init(&priv->xdp_tx_ring, VETH_RING_SIZE, GFP_KERNEL);
+ if (err)
+ goto err_xdp_tx_ring;
+
netif_napi_add(dev, &priv->xdp_napi, veth_poll, NAPI_POLL_WEIGHT);
napi_enable(&priv->xdp_napi);
return 0;
+err_xdp_tx_ring:
+ ptr_ring_cleanup(&priv->xdp_ring, __skb_array_destroy_skb);
+
+ return err;
}
static void veth_napi_del(struct net_device *dev)
@@ -413,6 +515,7 @@ static void veth_napi_del(struct net_device *dev)
napi_disable(&priv->xdp_napi);
netif_napi_del(&priv->xdp_napi);
ptr_ring_cleanup(&priv->xdp_ring, __skb_array_destroy_skb);
+ ptr_ring_cleanup(&priv->xdp_tx_ring, veth_xdp_free);
}
static int veth_enable_xdp(struct net_device *dev)
--
2.14.3
^ permalink raw reply related
* [PATCH RFC v2 3/9] veth: Avoid drops by oversized packets when XDP is enabled
From: Toshiaki Makita @ 2018-06-10 16:02 UTC (permalink / raw)
To: netdev
Cc: Toshiaki Makita, Jesper Dangaard Brouer, Alexei Starovoitov,
Daniel Borkmann
In-Reply-To: <20180610160217.3146-1-toshiaki.makita1@gmail.com>
From: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
All oversized packets including GSO packets are dropped if XDP is
enabled on receiver side, so don't send such packets from peer.
Drop TSO and SCTP fragmentation features so that veth devices themselves
segment packets with XDP enabled. Also cap MTU accordingly.
Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
---
drivers/net/veth.c | 49 +++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 43 insertions(+), 6 deletions(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 317ec92cf816..88d349da72cc 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -533,6 +533,23 @@ static int veth_get_iflink(const struct net_device *dev)
return iflink;
}
+static netdev_features_t veth_fix_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+ struct net_device *peer;
+
+ peer = rtnl_dereference(priv->peer);
+ if (peer) {
+ struct veth_priv *peer_priv = netdev_priv(peer);
+
+ if (rtnl_dereference(peer_priv->xdp_prog))
+ features &= ~NETIF_F_GSO_SOFTWARE;
+ }
+
+ return features;
+}
+
static void veth_set_rx_headroom(struct net_device *dev, int new_hr)
{
struct veth_priv *peer_priv, *priv = netdev_priv(dev);
@@ -571,10 +588,19 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog,
if (!peer)
return -ENOTCONN;
- if (!old_prog && dev->flags & IFF_UP) {
- err = veth_enable_xdp(dev);
- if (err)
- return err;
+ if (!old_prog) {
+ if (dev->flags & IFF_UP) {
+ err = veth_enable_xdp(dev);
+ if (err)
+ return err;
+ }
+
+ peer->hw_features &= ~NETIF_F_GSO_SOFTWARE;
+ peer->max_mtu = PAGE_SIZE - VETH_XDP_HEADROOM -
+ peer->hard_header_len -
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+ if (peer->mtu > peer->max_mtu)
+ dev_set_mtu(peer, peer->max_mtu);
}
}
@@ -582,10 +608,20 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog,
if (old_prog) {
bpf_prog_put(old_prog);
- if (!prog && dev->flags & IFF_UP)
- veth_disable_xdp(dev);
+ if (!prog) {
+ if (dev->flags & IFF_UP)
+ veth_disable_xdp(dev);
+
+ if (peer) {
+ peer->hw_features |= NETIF_F_GSO_SOFTWARE;
+ peer->max_mtu = ETH_MAX_MTU;
+ }
+ }
}
+ if ((!!old_prog ^ !!prog) && peer)
+ netdev_update_features(peer);
+
return 0;
}
@@ -627,6 +663,7 @@ static const struct net_device_ops veth_netdev_ops = {
.ndo_poll_controller = veth_poll_controller,
#endif
.ndo_get_iflink = veth_get_iflink,
+ .ndo_fix_features = veth_fix_features,
.ndo_features_check = passthru_features_check,
.ndo_set_rx_headroom = veth_set_rx_headroom,
.ndo_bpf = veth_xdp,
--
2.14.3
^ permalink raw reply related
* [PATCH RFC v2 2/9] veth: Add driver XDP
From: Toshiaki Makita @ 2018-06-10 16:02 UTC (permalink / raw)
To: netdev
Cc: Toshiaki Makita, Jesper Dangaard Brouer, Alexei Starovoitov,
Daniel Borkmann
In-Reply-To: <20180610160217.3146-1-toshiaki.makita1@gmail.com>
From: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
This is basic implementation of veth driver XDP.
Incoming packets are sent from the peer veth device in the form of skb,
so this is generally doing the same thing as generic XDP.
This itself is not so useful, but a starting point to implement other
useful veth XDP features like TX and REDIRECT.
This introduces NAPI when XDP is enabled, because XDP is now heavily
relies on NAPI context. Use ptr_ring to emulate NIC ring. Tx function
enqueues packets to the ring and peer NAPI handler drains the ring.
Currently only one ring is allocated for each veth device, so it does
not scale on multiqueue env. This can be resolved by allocating rings
on the per-queue basis later.
Note that NAPI is not used but netif_rx is used when XDP is not loaded,
so this does not change the default behaviour.
v2:
- Squashed with the patch adding NAPI.
- Implement adjust_tail.
- Don't acquire consumer lock because it is guarded by NAPI.
- Make poll_controller noop since it is unnecessary.
- Register rxq_info on enabling XDP rather than on opening the device.
Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
---
drivers/net/veth.c | 357 +++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 350 insertions(+), 7 deletions(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index a69ad39ee57e..317ec92cf816 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -19,10 +19,18 @@
#include <net/xfrm.h>
#include <linux/veth.h>
#include <linux/module.h>
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <linux/ptr_ring.h>
+#include <linux/skb_array.h>
+#include <linux/bpf_trace.h>
#define DRV_NAME "veth"
#define DRV_VERSION "1.0"
+#define VETH_RING_SIZE 256
+#define VETH_XDP_HEADROOM (XDP_PACKET_HEADROOM + NET_IP_ALIGN)
+
struct pcpu_vstats {
u64 packets;
u64 bytes;
@@ -30,9 +38,15 @@ struct pcpu_vstats {
};
struct veth_priv {
+ struct napi_struct xdp_napi;
+ struct net_device *dev;
+ struct bpf_prog __rcu *xdp_prog;
struct net_device __rcu *peer;
atomic64_t dropped;
unsigned requested_headroom;
+ bool rx_notify_masked;
+ struct ptr_ring xdp_ring;
+ struct xdp_rxq_info xdp_rxq;
};
/*
@@ -98,11 +112,43 @@ static const struct ethtool_ops veth_ethtool_ops = {
.get_link_ksettings = veth_get_link_ksettings,
};
-static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
+/* general routines */
+
+static void __veth_xdp_flush(struct veth_priv *priv)
+{
+ /* Write ptr_ring before reading rx_notify_masked */
+ smp_mb();
+ if (!priv->rx_notify_masked) {
+ priv->rx_notify_masked = true;
+ napi_schedule(&priv->xdp_napi);
+ }
+}
+
+static int veth_xdp_rx(struct veth_priv *priv, struct sk_buff *skb)
+{
+ if (unlikely(ptr_ring_produce(&priv->xdp_ring, skb))) {
+ dev_kfree_skb_any(skb);
+ return NET_RX_DROP;
+ }
+
+ return NET_RX_SUCCESS;
+}
+
+static int veth_forward_skb(struct net_device *dev, struct sk_buff *skb, bool xdp)
{
struct veth_priv *priv = netdev_priv(dev);
+
+ return __dev_forward_skb(dev, skb) ?: xdp ?
+ veth_xdp_rx(priv, skb) :
+ netif_rx(skb);
+}
+
+static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct veth_priv *rcv_priv, *priv = netdev_priv(dev);
struct net_device *rcv;
int length = skb->len;
+ bool rcv_xdp = false;
rcu_read_lock();
rcv = rcu_dereference(priv->peer);
@@ -111,7 +157,10 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
goto drop;
}
- if (likely(dev_forward_skb(rcv, skb) == NET_RX_SUCCESS)) {
+ rcv_priv = netdev_priv(rcv);
+ rcv_xdp = rcu_access_pointer(rcv_priv->xdp_prog);
+
+ if (likely(veth_forward_skb(rcv, skb, rcv_xdp) == NET_RX_SUCCESS)) {
struct pcpu_vstats *stats = this_cpu_ptr(dev->vstats);
u64_stats_update_begin(&stats->syncp);
@@ -122,14 +171,15 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
drop:
atomic64_inc(&priv->dropped);
}
+
+ if (rcv_xdp)
+ __veth_xdp_flush(rcv_priv);
+
rcu_read_unlock();
+
return NETDEV_TX_OK;
}
-/*
- * general routines
- */
-
static u64 veth_stats_one(struct pcpu_vstats *result, struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
@@ -179,18 +229,245 @@ static void veth_set_multicast_list(struct net_device *dev)
{
}
+static struct sk_buff *veth_build_skb(void *head, int headroom, int len,
+ int buflen)
+{
+ struct sk_buff *skb;
+
+ if (!buflen) {
+ buflen = SKB_DATA_ALIGN(headroom + len) +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+ }
+ skb = build_skb(head, buflen);
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, headroom);
+ skb_put(skb, len);
+
+ return skb;
+}
+
+static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
+ struct sk_buff *skb)
+{
+ u32 pktlen, headroom, act, metalen;
+ void *orig_data, *orig_data_end;
+ int size, mac_len, delta, off;
+ struct bpf_prog *xdp_prog;
+ struct xdp_buff xdp;
+
+ rcu_read_lock();
+ xdp_prog = rcu_dereference(priv->xdp_prog);
+ if (!xdp_prog) {
+ rcu_read_unlock();
+ goto out;
+ }
+
+ mac_len = skb->data - skb_mac_header(skb);
+ pktlen = skb->len + mac_len;
+ size = SKB_DATA_ALIGN(VETH_XDP_HEADROOM + pktlen) +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+ if (size > PAGE_SIZE)
+ goto drop;
+
+ headroom = skb_headroom(skb) - mac_len;
+ if (skb_shared(skb) || skb_head_is_locked(skb) ||
+ skb_is_nonlinear(skb) || headroom < XDP_PACKET_HEADROOM) {
+ struct sk_buff *nskb;
+ void *head, *start;
+ struct page *page;
+ int head_off;
+
+ page = alloc_page(GFP_ATOMIC);
+ if (!page)
+ goto drop;
+
+ head = page_address(page);
+ start = head + VETH_XDP_HEADROOM;
+ if (skb_copy_bits(skb, -mac_len, start, pktlen)) {
+ page_frag_free(head);
+ goto drop;
+ }
+
+ nskb = veth_build_skb(head,
+ VETH_XDP_HEADROOM + mac_len, skb->len,
+ PAGE_SIZE);
+ if (!nskb) {
+ page_frag_free(head);
+ goto drop;
+ }
+
+ skb_copy_header(nskb, skb);
+ head_off = skb_headroom(nskb) - skb_headroom(skb);
+ skb_headers_offset_update(nskb, head_off);
+ dev_consume_skb_any(skb);
+ skb = nskb;
+ }
+
+ xdp.data_hard_start = skb->head;
+ xdp.data = skb_mac_header(skb);
+ xdp.data_end = xdp.data + pktlen;
+ xdp.data_meta = xdp.data;
+ xdp.rxq = &priv->xdp_rxq;
+ orig_data = xdp.data;
+ orig_data_end = xdp.data_end;
+
+ act = bpf_prog_run_xdp(xdp_prog, &xdp);
+
+ switch (act) {
+ case XDP_PASS:
+ break;
+ default:
+ bpf_warn_invalid_xdp_action(act);
+ case XDP_ABORTED:
+ trace_xdp_exception(priv->dev, xdp_prog, act);
+ case XDP_DROP:
+ goto drop;
+ }
+ rcu_read_unlock();
+
+ delta = orig_data - xdp.data;
+ off = mac_len + delta;
+ if (off > 0)
+ __skb_push(skb, off);
+ else if (off < 0)
+ __skb_pull(skb, -off);
+ skb->mac_header -= delta;
+ off = xdp.data_end - orig_data_end;
+ if (off != 0)
+ __skb_put(skb, off);
+ skb->protocol = eth_type_trans(skb, priv->dev);
+
+ metalen = xdp.data - xdp.data_meta;
+ if (metalen)
+ skb_metadata_set(skb, metalen);
+out:
+ return skb;
+drop:
+ rcu_read_unlock();
+ dev_kfree_skb_any(skb);
+ return NULL;
+}
+
+static int veth_xdp_rcv(struct veth_priv *priv, int budget)
+{
+ int i, done = 0;
+
+ for (i = 0; i < budget; i++) {
+ struct sk_buff *skb = __ptr_ring_consume(&priv->xdp_ring);
+
+ if (!skb)
+ break;
+
+ skb = veth_xdp_rcv_skb(priv, skb);
+
+ if (skb)
+ napi_gro_receive(&priv->xdp_napi, skb);
+
+ done++;
+ }
+
+ return done;
+}
+
+static int veth_poll(struct napi_struct *napi, int budget)
+{
+ struct veth_priv *priv =
+ container_of(napi, struct veth_priv, xdp_napi);
+ int done;
+
+ done = veth_xdp_rcv(priv, budget);
+
+ if (done < budget && napi_complete_done(napi, done)) {
+ /* Write rx_notify_masked before reading ptr_ring */
+ smp_store_mb(priv->rx_notify_masked, false);
+ if (unlikely(!__ptr_ring_empty(&priv->xdp_ring))) {
+ priv->rx_notify_masked = true;
+ napi_schedule(&priv->xdp_napi);
+ }
+ }
+
+ return done;
+}
+
+static int veth_napi_add(struct net_device *dev)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+ int err;
+
+ err = ptr_ring_init(&priv->xdp_ring, VETH_RING_SIZE, GFP_KERNEL);
+ if (err)
+ return err;
+
+ netif_napi_add(dev, &priv->xdp_napi, veth_poll, NAPI_POLL_WEIGHT);
+ napi_enable(&priv->xdp_napi);
+
+ return 0;
+}
+
+static void veth_napi_del(struct net_device *dev)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+
+ napi_disable(&priv->xdp_napi);
+ netif_napi_del(&priv->xdp_napi);
+ ptr_ring_cleanup(&priv->xdp_ring, __skb_array_destroy_skb);
+}
+
+static int veth_enable_xdp(struct net_device *dev)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+ int err;
+
+ err = xdp_rxq_info_reg(&priv->xdp_rxq, dev, 0);
+ if (err < 0)
+ return err;
+
+ err = xdp_rxq_info_reg_mem_model(&priv->xdp_rxq,
+ MEM_TYPE_PAGE_SHARED, NULL);
+ if (err < 0)
+ goto err;
+
+ err = veth_napi_add(dev);
+ if (err)
+ goto err;
+
+ return 0;
+err:
+ xdp_rxq_info_unreg(&priv->xdp_rxq);
+
+ return err;
+}
+
+static void veth_disable_xdp(struct net_device *dev)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+
+ veth_napi_del(dev);
+ xdp_rxq_info_unreg(&priv->xdp_rxq);
+}
+
static int veth_open(struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
struct net_device *peer = rtnl_dereference(priv->peer);
+ int err;
if (!peer)
return -ENOTCONN;
+ if (rtnl_dereference(priv->xdp_prog)) {
+ err = veth_enable_xdp(dev);
+ if (err)
+ return err;
+ }
+
if (peer->flags & IFF_UP) {
netif_carrier_on(dev);
netif_carrier_on(peer);
}
+
return 0;
}
@@ -203,6 +480,9 @@ static int veth_close(struct net_device *dev)
if (peer)
netif_carrier_off(peer);
+ if (rtnl_dereference(priv->xdp_prog))
+ veth_disable_xdp(dev);
+
return 0;
}
@@ -228,7 +508,7 @@ static void veth_dev_free(struct net_device *dev)
static void veth_poll_controller(struct net_device *dev)
{
/* veth only receives frames when its peer sends one
- * Since it's a synchronous operation, we are guaranteed
+ * Since it has nothing to do with disabling irqs, we are guaranteed
* never to have pending data when we poll for it so
* there is nothing to do here.
*
@@ -276,6 +556,65 @@ static void veth_set_rx_headroom(struct net_device *dev, int new_hr)
rcu_read_unlock();
}
+static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog,
+ struct netlink_ext_ack *extack)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+ struct bpf_prog *old_prog;
+ struct net_device *peer;
+ int err;
+
+ old_prog = rtnl_dereference(priv->xdp_prog);
+ peer = rtnl_dereference(priv->peer);
+
+ if (prog) {
+ if (!peer)
+ return -ENOTCONN;
+
+ if (!old_prog && dev->flags & IFF_UP) {
+ err = veth_enable_xdp(dev);
+ if (err)
+ return err;
+ }
+ }
+
+ rcu_assign_pointer(priv->xdp_prog, prog);
+
+ if (old_prog) {
+ bpf_prog_put(old_prog);
+ if (!prog && dev->flags & IFF_UP)
+ veth_disable_xdp(dev);
+ }
+
+ return 0;
+}
+
+static u32 veth_xdp_query(struct net_device *dev)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+ const struct bpf_prog *xdp_prog;
+
+ xdp_prog = rtnl_dereference(priv->xdp_prog);
+ if (xdp_prog)
+ return xdp_prog->aux->id;
+
+ return 0;
+}
+
+static int veth_xdp(struct net_device *dev, struct netdev_bpf *xdp)
+{
+ switch (xdp->command) {
+ case XDP_SETUP_PROG:
+ return veth_xdp_set(dev, xdp->prog, xdp->extack);
+ case XDP_QUERY_PROG:
+ xdp->prog_id = veth_xdp_query(dev);
+ xdp->prog_attached = !!xdp->prog_id;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
static const struct net_device_ops veth_netdev_ops = {
.ndo_init = veth_dev_init,
.ndo_open = veth_open,
@@ -290,6 +629,7 @@ static const struct net_device_ops veth_netdev_ops = {
.ndo_get_iflink = veth_get_iflink,
.ndo_features_check = passthru_features_check,
.ndo_set_rx_headroom = veth_set_rx_headroom,
+ .ndo_bpf = veth_xdp,
};
#define VETH_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HW_CSUM | \
@@ -451,10 +791,13 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
*/
priv = netdev_priv(dev);
+ priv->dev = dev;
rcu_assign_pointer(priv->peer, peer);
priv = netdev_priv(peer);
+ priv->dev = peer;
rcu_assign_pointer(priv->peer, dev);
+
return 0;
err_register_dev:
--
2.14.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox