* Re: [PATCH 1/4] Update firmware for rtl8192ce
From: Kyle McMartin @ 2014-09-13 2:37 UTC (permalink / raw)
To: Larry Finger
Cc: linux-firmware-DgEjT+Ai2ygdnm+yROfE0A,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1409673648-20328-1-git-send-email-Larry.Finger-tQ5ms3gMjBLk1uMJSBkQmQ@public.gmane.org>
On Tue, Sep 02, 2014 at 11:00:45AM -0500, Larry Finger wrote:
> The latest driver, labelled rtlwifi_linux_mac80211_0019.0320.2014V628,
> has updated firmware for the RTL8192CE.
>
> Signed-off-by: Larry Finger <Larry.Finger-tQ5ms3gMjBLk1uMJSBkQmQ@public.gmane.org>
> ---
> WHENCE | 1 +
> rtlwifi/rtl8192cfw.bin | Bin 13540 -> 16192 bytes
> rtlwifi/rtl8192cfwU_B.bin | Bin 14800 -> 16332 bytes
> 3 files changed, 1 insertion(+)
>
i've applied all 4. thanks larry.
--kyle
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [net-next PATCH v5 00/16] net/sched rcu filters
From: David Miller @ 2014-09-13 2:36 UTC (permalink / raw)
To: john.fastabend; +Cc: xiyou.wangcong, eric.dumazet, jhs, netdev, paulmck, brouer
In-Reply-To: <20140912162748.19588.39677.stgit@nitbit.x32>
John you're getting really close with this series.
Why don't you post everything up to the tc actions stuff,
which Eric has ACK'd already, and I can put that all into
the net-next tree right now.
Then you can concentrate on the bits that remain without
having to post the other stuff over and over again.
Thanks.
^ permalink raw reply
* [net-next v6 4/4] l2tp: Refactor l2tp core driver to make use of the common UDP tunnel functions
From: Andy Zhou @ 2014-09-13 2:22 UTC (permalink / raw)
To: davem; +Cc: netdev, Andy Zhou
In-Reply-To: <1410574920-16486-1-git-send-email-azhou@nicira.com>
Signed-off-by: Andy Zhou <azhou@nicira.com>
---
net/l2tp/l2tp_core.c | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 2aa2b6c..c31b3d3 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1392,8 +1392,6 @@ static int l2tp_tunnel_sock_create(struct net *net,
if (err < 0)
goto out;
- udp_set_convert_csum(sock->sk, true);
-
break;
case L2TP_ENCAPTYPE_IP:
@@ -1584,19 +1582,18 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
tunnel->encap = encap;
if (encap == L2TP_ENCAPTYPE_UDP) {
- /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
- udp_sk(sk)->encap_type = UDP_ENCAP_L2TPINUDP;
- udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv;
- udp_sk(sk)->encap_destroy = l2tp_udp_encap_destroy;
-#if IS_ENABLED(CONFIG_IPV6)
- if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
- udpv6_encap_enable();
- else
-#endif
- udp_encap_enable();
- }
+ struct udp_tunnel_sock_cfg udp_cfg;
+
+ udp_cfg.sock = sock;
+ udp_cfg.sk_user_data = tunnel;
+ udp_cfg.encap_type = UDP_ENCAP_L2TPINUDP;
+ udp_cfg.encap_rcv = l2tp_udp_encap_recv;
+ udp_cfg.encap_destroy = l2tp_udp_encap_destroy;
- sk->sk_user_data = tunnel;
+ setup_udp_tunnel_sock(net, &udp_cfg);
+ } else {
+ sk->sk_user_data = tunnel;
+ }
/* Hook on the tunnel socket destructor so that we can cleanup
* if the tunnel socket goes away.
--
1.7.9.5
^ permalink raw reply related
* [net-next v6 3/4] vxlan: Refactor vxlan driver to make use of the common UDP tunnel functions.
From: Andy Zhou @ 2014-09-13 2:21 UTC (permalink / raw)
To: davem; +Cc: netdev, Andy Zhou
In-Reply-To: <1410574920-16486-1-git-send-email-azhou@nicira.com>
Signed-off-by: Andy Zhou <azhou@nicira.com>
---
drivers/net/vxlan.c | 106 +++++++++++----------------------------------------
1 file changed, 23 insertions(+), 83 deletions(-)
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 53c3ec1..bb56103 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -42,6 +42,7 @@
#include <net/netns/generic.h>
#include <net/vxlan.h>
#include <net/protocol.h>
+#include <net/udp_tunnel.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/ipv6.h>
#include <net/addrconf.h>
@@ -1062,7 +1063,6 @@ void vxlan_sock_release(struct vxlan_sock *vs)
spin_lock(&vn->sock_lock);
hlist_del_rcu(&vs->hlist);
- rcu_assign_sk_user_data(vs->sock->sk, NULL);
vxlan_notify_del_rx_port(vs);
spin_unlock(&vn->sock_lock);
@@ -1336,7 +1336,6 @@ out:
}
#if IS_ENABLED(CONFIG_IPV6)
-
static struct sk_buff *vxlan_na_create(struct sk_buff *request,
struct neighbour *n, bool isrouter)
{
@@ -1570,13 +1569,6 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
return false;
}
-static inline struct sk_buff *vxlan_handle_offloads(struct sk_buff *skb,
- bool udp_csum)
-{
- int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
- return iptunnel_handle_offloads(skb, udp_csum, type);
-}
-
#if IS_ENABLED(CONFIG_IPV6)
static int vxlan6_xmit_skb(struct vxlan_sock *vs,
struct dst_entry *dst, struct sk_buff *skb,
@@ -1585,13 +1577,12 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
__be16 src_port, __be16 dst_port, __be32 vni,
bool xnet)
{
- struct ipv6hdr *ip6h;
struct vxlanhdr *vxh;
- struct udphdr *uh;
int min_headroom;
int err;
+ bool udp_sum = !udp_get_no_check6_tx(vs->sock->sk);
- skb = vxlan_handle_offloads(skb, !udp_get_no_check6_tx(vs->sock->sk));
+ skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb))
return -EINVAL;
@@ -1619,38 +1610,8 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
vxh->vx_flags = htonl(VXLAN_FLAGS);
vxh->vx_vni = vni;
- __skb_push(skb, sizeof(*uh));
- skb_reset_transport_header(skb);
- uh = udp_hdr(skb);
-
- uh->dest = dst_port;
- uh->source = src_port;
-
- uh->len = htons(skb->len);
-
- memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
- IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
- IPSKB_REROUTED);
- skb_dst_set(skb, dst);
-
- udp6_set_csum(udp_get_no_check6_tx(vs->sock->sk), skb,
- saddr, daddr, skb->len);
-
- __skb_push(skb, sizeof(*ip6h));
- skb_reset_network_header(skb);
- ip6h = ipv6_hdr(skb);
- ip6h->version = 6;
- ip6h->priority = prio;
- ip6h->flow_lbl[0] = 0;
- ip6h->flow_lbl[1] = 0;
- ip6h->flow_lbl[2] = 0;
- ip6h->payload_len = htons(skb->len);
- ip6h->nexthdr = IPPROTO_UDP;
- ip6h->hop_limit = ttl;
- ip6h->daddr = *daddr;
- ip6h->saddr = *saddr;
-
- ip6tunnel_xmit(skb, dev);
+ udp_tunnel6_xmit_skb(vs->sock, dst, skb, dev, saddr, daddr, prio,
+ ttl, src_port, dst_port);
return 0;
}
#endif
@@ -1661,11 +1622,11 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
__be16 src_port, __be16 dst_port, __be32 vni, bool xnet)
{
struct vxlanhdr *vxh;
- struct udphdr *uh;
int min_headroom;
int err;
+ bool udp_sum = !vs->sock->sk->sk_no_check_tx;
- skb = vxlan_handle_offloads(skb, !vs->sock->sk->sk_no_check_tx);
+ skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb))
return -EINVAL;
@@ -1691,20 +1652,8 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
vxh->vx_flags = htonl(VXLAN_FLAGS);
vxh->vx_vni = vni;
- __skb_push(skb, sizeof(*uh));
- skb_reset_transport_header(skb);
- uh = udp_hdr(skb);
-
- uh->dest = dst_port;
- uh->source = src_port;
-
- uh->len = htons(skb->len);
-
- udp_set_csum(vs->sock->sk->sk_no_check_tx, skb,
- src, dst, skb->len);
-
- return iptunnel_xmit(vs->sock->sk, rt, skb, src, dst, IPPROTO_UDP,
- tos, ttl, df, xnet);
+ return udp_tunnel_xmit_skb(vs->sock, rt, skb, src, dst, tos,
+ ttl, df, src_port, dst_port, xnet);
}
EXPORT_SYMBOL_GPL(vxlan_xmit_skb);
@@ -1829,11 +1778,11 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
- err = vxlan_xmit_skb(vxlan->vn_sock, rt, skb,
- fl4.saddr, dst->sin.sin_addr.s_addr,
- tos, ttl, df, src_port, dst_port,
- htonl(vni << 8),
- !net_eq(vxlan->net, dev_net(vxlan->dev)));
+ err = udp_tunnel_xmit_skb(vxlan->vn_sock->sock, rt, skb,
+ fl4.saddr, dst->sin.sin_addr.s_addr,
+ tos, ttl, df, src_port, dst_port,
+ !net_eq(vxlan->net,
+ dev_net(vxlan->dev)));
if (err < 0)
goto rt_tx_error;
@@ -2333,8 +2282,7 @@ static const struct ethtool_ops vxlan_ethtool_ops = {
static void vxlan_del_work(struct work_struct *work)
{
struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work);
-
- sk_release_kernel(vs->sock->sk);
+ udp_tunnel_sock_release(vs->sock);
kfree_rcu(vs, rcu);
}
@@ -2367,11 +2315,6 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
if (err < 0)
return ERR_PTR(err);
- /* Disable multicast loopback */
- inet_sk(sock->sk)->mc_loop = 0;
-
- udp_set_convert_csum(sock->sk, true);
-
return sock;
}
@@ -2383,9 +2326,9 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
struct vxlan_net *vn = net_generic(net, vxlan_net_id);
struct vxlan_sock *vs;
struct socket *sock;
- struct sock *sk;
unsigned int h;
bool ipv6 = !!(flags & VXLAN_F_IPV6);
+ struct udp_tunnel_sock_cfg tunnel_cfg;
vs = kzalloc(sizeof(*vs), GFP_KERNEL);
if (!vs)
@@ -2403,11 +2346,9 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
}
vs->sock = sock;
- sk = sock->sk;
atomic_set(&vs->refcnt, 1);
vs->rcv = rcv;
vs->data = data;
- rcu_assign_sk_user_data(vs->sock->sk, vs);
/* Initialize the vxlan udp offloads structure */
vs->udp_offloads.port = port;
@@ -2420,14 +2361,13 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
spin_unlock(&vn->sock_lock);
/* Mark socket as an encapsulation socket. */
- udp_sk(sk)->encap_type = 1;
- udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv;
-#if IS_ENABLED(CONFIG_IPV6)
- if (ipv6)
- ipv6_stub->udpv6_encap_enable();
- else
-#endif
- udp_encap_enable();
+ tunnel_cfg.sock = sock;
+ tunnel_cfg.sk_user_data = vs;
+ tunnel_cfg.encap_type = 1;
+ tunnel_cfg.encap_rcv = vxlan_udp_encap_recv;
+ tunnel_cfg.encap_destroy = NULL;
+
+ setup_udp_tunnel_sock(net, &tunnel_cfg);
return vs;
}
--
1.7.9.5
^ permalink raw reply related
* [net-next v6 2/4] udp-tunnel: Expand UDP tunnel APIs
From: Andy Zhou @ 2014-09-13 2:21 UTC (permalink / raw)
To: davem; +Cc: netdev, Andy Zhou
In-Reply-To: <1410574920-16486-1-git-send-email-azhou@nicira.com>
Added common udp tunnel socket creation, and packet transmission APIs
API that can be used by other UDP based tunneling protocol
implementation.
Signed-off-by: Andy Zhou <azhou@nicira.com>
---
include/net/udp_tunnel.h | 56 +++++++++++++++++++++++++++++++++++++++++++++
net/ipv4/udp_tunnel.c | 4 ++--
net/ipv6/ip6_udp_tunnel.c | 42 ++++++++++++++++++++++++++++++++++
3 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index 0b9e017..9c58c23 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -1,6 +1,14 @@
#ifndef __NET_UDP_TUNNEL_H
#define __NET_UDP_TUNNEL_H
+#include <net/ip_tunnels.h>
+#include <net/udp.h>
+
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/ipv6.h>
+#include <net/addrconf.h>
+#endif
+
struct udp_port_cfg {
u8 family;
@@ -53,4 +61,52 @@ static inline int udp_sock_create(struct net *net,
return -EPFNOSUPPORT;
}
+typedef int (*udp_tunnel_encap_rcv_t)(struct sock *sk, struct sk_buff *skb);
+typedef void (*udp_tunnel_encap_destroy_t)(struct sock *sk);
+
+struct udp_tunnel_sock_cfg {
+ struct socket *sock; /* The socket UDP tunnel will attach to */
+ void *sk_user_data; /* user data used by encap_rcv call back */
+ /* Used for setting up udp_sock fields, see udp.h for details */
+ __u8 encap_type;
+ udp_tunnel_encap_rcv_t encap_rcv;
+ udp_tunnel_encap_destroy_t encap_destroy;
+};
+
+void setup_udp_tunnel_sock(struct net *net,
+ struct udp_tunnel_sock_cfg *sock_cfg);
+
+int udp_tunnel_xmit_skb(struct socket *sock, struct rtable *rt,
+ struct sk_buff *skb, __be32 src, __be32 dst,
+ __u8 tos, __u8 ttl, __be16 df, __be16 src_port,
+ __be16 dst_port, bool xnet);
+
+#if IS_ENABLED(CONFIG_IPV6)
+int udp_tunnel6_xmit_skb(struct socket *sock, struct dst_entry *dst,
+ struct sk_buff *skb, struct net_device *dev,
+ struct in6_addr *saddr, struct in6_addr *daddr,
+ __u8 prio, __u8 ttl, __be16 src_port,
+ __be16 dst_port);
+#endif
+
+void udp_tunnel_sock_release(struct socket *sock);
+
+static inline struct sk_buff *udp_tunnel_handle_offloads(struct sk_buff *skb,
+ bool udp_csum)
+{
+ int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
+
+ return iptunnel_handle_offloads(skb, udp_csum, type);
+}
+
+static inline void udp_tunnel_encap_enable(struct socket *sock)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+ if (sock->sk->sk_family == PF_INET6)
+ ipv6_stub->udpv6_encap_enable();
+ else
+#endif
+ udp_encap_enable();
+}
+
#endif
diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c
index 93ef133..db73ce8 100644
--- a/net/ipv4/udp_tunnel.c
+++ b/net/ipv4/udp_tunnel.c
@@ -11,7 +11,7 @@
int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
struct socket **sockp)
{
- int err = -EINVAL;
+ int err;
struct socket *sock = NULL;
struct sockaddr_in udp_addr;
@@ -62,7 +62,7 @@ void setup_udp_tunnel_sock(struct net *net, struct udp_tunnel_sock_cfg *cfg)
/* Disable multicast loopback */
inet_sk(sk)->mc_loop = 0;
- /* Enable CHECKSUM_UNNECESSAY to CHECSUM_COMPLETE conversion */
+ /* Enable CHECKSUM_UNNECESSARY to CHECKSUM_COMPLETE conversion */
udp_set_convert_csum(sk, true);
rcu_assign_sk_user_data(sk, cfg->sk_user_data);
diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c
index bcfbb4b..cbc9907 100644
--- a/net/ipv6/ip6_udp_tunnel.c
+++ b/net/ipv6/ip6_udp_tunnel.c
@@ -61,3 +61,45 @@ error:
return err;
}
EXPORT_SYMBOL_GPL(udp_sock_create6);
+
+int udp_tunnel6_xmit_skb(struct socket *sock, struct dst_entry *dst,
+ struct sk_buff *skb, struct net_device *dev,
+ struct in6_addr *saddr, struct in6_addr *daddr,
+ __u8 prio, __u8 ttl, __be16 src_port, __be16 dst_port)
+{
+ struct udphdr *uh;
+ struct ipv6hdr *ip6h;
+ struct sock *sk = sock->sk;
+
+ __skb_push(skb, sizeof(*uh));
+ skb_reset_transport_header(skb);
+ uh = udp_hdr(skb);
+
+ uh->dest = dst_port;
+ uh->source = src_port;
+
+ uh->len = htons(skb->len);
+ uh->check = 0;
+
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+ IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED
+ | IPSKB_REROUTED);
+ skb_dst_set(skb, dst);
+
+ udp6_set_csum(udp_get_no_check6_tx(sk), skb, &inet6_sk(sk)->saddr,
+ &sk->sk_v6_daddr, skb->len);
+
+ __skb_push(skb, sizeof(*ip6h));
+ skb_reset_network_header(skb);
+ ip6h = ipv6_hdr(skb);
+ ip6_flow_hdr(ip6h, prio, htonl(0));
+ ip6h->payload_len = htons(skb->len);
+ ip6h->nexthdr = IPPROTO_UDP;
+ ip6h->hop_limit = ttl;
+ ip6h->daddr = *daddr;
+ ip6h->saddr = *saddr;
+
+ ip6tunnel_xmit(skb, dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb);
--
1.7.9.5
^ permalink raw reply related
* [net-next v6 1/4] udp_tunnel: Seperate ipv6 functions into its own file.
From: Andy Zhou @ 2014-09-13 2:21 UTC (permalink / raw)
To: davem; +Cc: netdev, Andy Zhou
In-Reply-To: <1410574920-16486-1-git-send-email-azhou@nicira.com>
Add ip6_udp_tunnel.c for ipv6 UDP tunnel functions to avoid ifdefs
in udp_tunnel.c
Signed-off-by: Andy Zhou <azhou@nicira.com>
---
include/net/udp_tunnel.h | 28 +++++++++-
net/ipv4/udp_tunnel.c | 136 ++++++++++++++++++++++++---------------------
net/ipv6/Makefile | 1 +
net/ipv6/ip6_udp_tunnel.c | 63 +++++++++++++++++++++
4 files changed, 162 insertions(+), 66 deletions(-)
create mode 100644 net/ipv6/ip6_udp_tunnel.c
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index ffd69cb..0b9e017 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -26,7 +26,31 @@ struct udp_port_cfg {
use_udp6_rx_checksums:1;
};
-int udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
- struct socket **sockp);
+int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
+ struct socket **sockp);
+
+#if IS_ENABLED(CONFIG_IPV6)
+int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
+ struct socket **sockp);
+#else
+static inline int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
+ struct socket **sockp)
+{
+ return 0;
+}
+#endif
+
+static inline int udp_sock_create(struct net *net,
+ struct udp_port_cfg *cfg,
+ struct socket **sockp)
+{
+ if (cfg->family == AF_INET)
+ return udp_sock_create4(net, cfg, sockp);
+
+ if (cfg->family == AF_INET6)
+ return udp_sock_create6(net, cfg, sockp);
+
+ return -EPFNOSUPPORT;
+}
#endif
diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c
index 61ec1a6..93ef133 100644
--- a/net/ipv4/udp_tunnel.c
+++ b/net/ipv4/udp_tunnel.c
@@ -8,83 +8,40 @@
#include <net/udp_tunnel.h>
#include <net/net_namespace.h>
-int udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
- struct socket **sockp)
+int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
+ struct socket **sockp)
{
int err = -EINVAL;
struct socket *sock = NULL;
+ struct sockaddr_in udp_addr;
-#if IS_ENABLED(CONFIG_IPV6)
- if (cfg->family == AF_INET6) {
- struct sockaddr_in6 udp6_addr;
+ err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock);
+ if (err < 0)
+ goto error;
- err = sock_create_kern(AF_INET6, SOCK_DGRAM, 0, &sock);
- if (err < 0)
- goto error;
-
- sk_change_net(sock->sk, net);
-
- udp6_addr.sin6_family = AF_INET6;
- memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
- sizeof(udp6_addr.sin6_addr));
- udp6_addr.sin6_port = cfg->local_udp_port;
- err = kernel_bind(sock, (struct sockaddr *)&udp6_addr,
- sizeof(udp6_addr));
- if (err < 0)
- goto error;
-
- if (cfg->peer_udp_port) {
- udp6_addr.sin6_family = AF_INET6;
- memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6,
- sizeof(udp6_addr.sin6_addr));
- udp6_addr.sin6_port = cfg->peer_udp_port;
- err = kernel_connect(sock,
- (struct sockaddr *)&udp6_addr,
- sizeof(udp6_addr), 0);
- }
- if (err < 0)
- goto error;
+ sk_change_net(sock->sk, net);
- udp_set_no_check6_tx(sock->sk, !cfg->use_udp6_tx_checksums);
- udp_set_no_check6_rx(sock->sk, !cfg->use_udp6_rx_checksums);
- } else
-#endif
- if (cfg->family == AF_INET) {
- struct sockaddr_in udp_addr;
-
- err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock);
- if (err < 0)
- goto error;
-
- sk_change_net(sock->sk, net);
+ udp_addr.sin_family = AF_INET;
+ udp_addr.sin_addr = cfg->local_ip;
+ udp_addr.sin_port = cfg->local_udp_port;
+ err = kernel_bind(sock, (struct sockaddr *)&udp_addr,
+ sizeof(udp_addr));
+ if (err < 0)
+ goto error;
+ if (cfg->peer_udp_port) {
udp_addr.sin_family = AF_INET;
- udp_addr.sin_addr = cfg->local_ip;
- udp_addr.sin_port = cfg->local_udp_port;
- err = kernel_bind(sock, (struct sockaddr *)&udp_addr,
- sizeof(udp_addr));
+ udp_addr.sin_addr = cfg->peer_ip;
+ udp_addr.sin_port = cfg->peer_udp_port;
+ err = kernel_connect(sock, (struct sockaddr *)&udp_addr,
+ sizeof(udp_addr), 0);
if (err < 0)
goto error;
-
- if (cfg->peer_udp_port) {
- udp_addr.sin_family = AF_INET;
- udp_addr.sin_addr = cfg->peer_ip;
- udp_addr.sin_port = cfg->peer_udp_port;
- err = kernel_connect(sock,
- (struct sockaddr *)&udp_addr,
- sizeof(udp_addr), 0);
- if (err < 0)
- goto error;
- }
-
- sock->sk->sk_no_check_tx = !cfg->use_udp_checksums;
- } else {
- return -EPFNOSUPPORT;
}
+ sock->sk->sk_no_check_tx = !cfg->use_udp_checksums;
*sockp = sock;
-
return 0;
error:
@@ -95,6 +52,57 @@ error:
*sockp = NULL;
return err;
}
-EXPORT_SYMBOL(udp_sock_create);
+EXPORT_SYMBOL(udp_sock_create4);
+
+void setup_udp_tunnel_sock(struct net *net, struct udp_tunnel_sock_cfg *cfg)
+{
+ struct socket *sock = cfg->sock;
+ struct sock *sk = sock->sk;
+
+ /* Disable multicast loopback */
+ inet_sk(sk)->mc_loop = 0;
+
+ /* Enable CHECKSUM_UNNECESSAY to CHECSUM_COMPLETE conversion */
+ udp_set_convert_csum(sk, true);
+
+ rcu_assign_sk_user_data(sk, cfg->sk_user_data);
+
+ udp_sk(sk)->encap_type = cfg->encap_type;
+ udp_sk(sk)->encap_rcv = cfg->encap_rcv;
+ udp_sk(sk)->encap_destroy = cfg->encap_destroy;
+
+ udp_tunnel_encap_enable(sock);
+}
+EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock);
+
+int udp_tunnel_xmit_skb(struct socket *sock, struct rtable *rt,
+ struct sk_buff *skb, __be32 src, __be32 dst,
+ __u8 tos, __u8 ttl, __be16 df, __be16 src_port,
+ __be16 dst_port, bool xnet)
+{
+ struct udphdr *uh;
+
+ __skb_push(skb, sizeof(*uh));
+ skb_reset_transport_header(skb);
+ uh = udp_hdr(skb);
+
+ uh->dest = dst_port;
+ uh->source = src_port;
+ uh->len = htons(skb->len);
+
+ udp_set_csum(sock->sk->sk_no_check_tx, skb, src, dst, skb->len);
+
+ return iptunnel_xmit(sock->sk, rt, skb, src, dst, IPPROTO_UDP,
+ tos, ttl, df, xnet);
+}
+EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb);
+
+void udp_tunnel_sock_release(struct socket *sock)
+{
+ rcu_assign_sk_user_data(sock->sk, NULL);
+ kernel_sock_shutdown(sock, SHUT_RDWR);
+ sk_release_kernel(sock->sk);
+}
+EXPORT_SYMBOL_GPL(udp_tunnel_sock_release);
MODULE_LICENSE("GPL");
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 2fe6836..45f830e 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION) += xfrm6_mode_ro.o
obj-$(CONFIG_INET6_XFRM_MODE_BEET) += xfrm6_mode_beet.o
obj-$(CONFIG_IPV6_MIP6) += mip6.o
obj-$(CONFIG_NETFILTER) += netfilter/
+obj-$(CONFIG_NET_UDP_TUNNEL) += ip6_udp_tunnel.o
obj-$(CONFIG_IPV6_VTI) += ip6_vti.o
obj-$(CONFIG_IPV6_SIT) += sit.o
diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c
new file mode 100644
index 0000000..bcfbb4b
--- /dev/null
+++ b/net/ipv6/ip6_udp_tunnel.c
@@ -0,0 +1,63 @@
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/udp.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/in6.h>
+#include <net/udp.h>
+#include <net/udp_tunnel.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/ip6_tunnel.h>
+#include <net/ip6_checksum.h>
+
+int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
+ struct socket **sockp)
+{
+ struct sockaddr_in6 udp6_addr;
+ int err;
+ struct socket *sock = NULL;
+
+ err = sock_create_kern(AF_INET6, SOCK_DGRAM, 0, &sock);
+ if (err < 0)
+ goto error;
+
+ sk_change_net(sock->sk, net);
+
+ udp6_addr.sin6_family = AF_INET6;
+ memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
+ sizeof(udp6_addr.sin6_addr));
+ udp6_addr.sin6_port = cfg->local_udp_port;
+ err = kernel_bind(sock, (struct sockaddr *)&udp6_addr,
+ sizeof(udp6_addr));
+ if (err < 0)
+ goto error;
+
+ if (cfg->peer_udp_port) {
+ udp6_addr.sin6_family = AF_INET6;
+ memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6,
+ sizeof(udp6_addr.sin6_addr));
+ udp6_addr.sin6_port = cfg->peer_udp_port;
+ err = kernel_connect(sock,
+ (struct sockaddr *)&udp6_addr,
+ sizeof(udp6_addr), 0);
+ }
+ if (err < 0)
+ goto error;
+
+ udp_set_no_check6_tx(sock->sk, !cfg->use_udp6_tx_checksums);
+ udp_set_no_check6_rx(sock->sk, !cfg->use_udp6_rx_checksums);
+
+ *sockp = sock;
+ return 0;
+
+error:
+ if (sock) {
+ kernel_sock_shutdown(sock, SHUT_RDWR);
+ sk_release_kernel(sock->sk);
+ }
+ *sockp = NULL;
+ return err;
+}
+EXPORT_SYMBOL_GPL(udp_sock_create6);
--
1.7.9.5
^ permalink raw reply related
* [net-next v6 0/4] Refactor vxlan and l2tp to use common UDP tunnel APIs
From: Andy Zhou @ 2014-09-13 2:21 UTC (permalink / raw)
To: davem; +Cc: netdev, Andy Zhou
Andy Zhou (4):
udp_tunnel: Seperate ipv6 functions into its own file.
udp-tunnel: Expand UDP tunnel APIs
vxlan: Refactor vxlan driver to make use of the common UDP tunnel
functions.
l2tp: Refactor l2tp core driver to make use of the common UDP tunnel
functions
drivers/net/vxlan.c | 106 ++++++++--------------------------
include/net/udp_tunnel.h | 84 ++++++++++++++++++++++++++-
net/ipv4/udp_tunnel.c | 138 ++++++++++++++++++++++++---------------------
net/ipv6/Makefile | 1 +
net/ipv6/ip6_udp_tunnel.c | 105 ++++++++++++++++++++++++++++++++++
net/l2tp/l2tp_core.c | 25 ++++----
6 files changed, 295 insertions(+), 164 deletions(-)
create mode 100644 net/ipv6/ip6_udp_tunnel.c
--
1.7.9.5
^ permalink raw reply
* [PATCH] netdevice: Support DSA tagging when DSA is built as a module
From: Alexander Duyck @ 2014-09-13 1:59 UTC (permalink / raw)
To: netdev; +Cc: f.fainelli, davem, kernel
This change corrects an error seen when DSA tagging is built as a module.
Without this change it is not possible to get XDSA tagged frames as the
test for tagging is stripped by the #ifdef check.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
---
include/linux/netdevice.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ba72f6b..c8f3806 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1789,7 +1789,7 @@ void dev_net_set(struct net_device *dev, struct net *net)
static inline bool netdev_uses_dsa(struct net_device *dev)
{
-#ifdef CONFIG_NET_DSA
+#if IS_ENABLED(CONFIG_NET_DSA)
if (dev->dsa_ptr != NULL)
return dsa_uses_tagged_protocol(dev->dsa_ptr);
#endif
^ permalink raw reply related
* Re: [net-next PATCH v5 16/16] net: sched: drop ingress qdisc lock
From: Eric Dumazet @ 2014-09-13 1:33 UTC (permalink / raw)
To: John Fastabend, Changli Gao
Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <1410566188.7106.120.camel@edumazet-glaptop2.roam.corp.google.com>
On Fri, 2014-09-12 at 16:56 -0700, Eric Dumazet wrote:
> On Fri, 2014-09-12 at 09:35 -0700, John Fastabend wrote:
> > After the previous patches to make the filters RCU safe and
> > support per cpu counters we can drop the qdisc lock around
> > the ingress qdisc hook.
> >
> > This is possible because the ingress qdisc is a very basic
> > qdisc and only updates stats and runs tc_classify. Its the
> > simplest qdiscs we have.
> >
> > In order for the per-cpu counters to get invoked the
> > ingress qdisc must set the LLQDISC flag. We could use per-cpu
> > counters on all counters but it is only necessary when the
> > qdisc lock is not held.
> >
> > Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> > ---
>
> Acked-by: Eric Dumazet <edumazet@google.com>
>
>
BTW, it might be time to revive the multiqueue patch for IFB,
Changli Gao worked on this 5 years ago, but work was not completed.
^ permalink raw reply
* Re: [PATCH] net: DSA: Marvell mv88e6171 switch driver
From: Claudio Leite @ 2014-09-13 0:42 UTC (permalink / raw)
To: Andrew Lunn; +Cc: davem, f.fainelli, netdev
In-Reply-To: <1410559124-10495-1-git-send-email-andrew@lunn.ch>
This is the Marvell driver with some cleanups by Claudio Leite
and myself.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Claudio Leite <leitec@staticky.com>
---
drivers/net/dsa/Kconfig | 9 +
drivers/net/dsa/Makefile | 3 +
drivers/net/dsa/mv88e6171.c | 407 ++++++++++++++++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx.c | 6 +
drivers/net/dsa/mv88e6xxx.h | 1 +
5 files changed, 426 insertions(+)
create mode 100644 drivers/net/dsa/mv88e6171.c
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index c6ee07c6a1b5..ea0697eaeff5 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -36,6 +36,15 @@ config NET_DSA_MV88E6123_61_65
This enables support for the Marvell 88E6123/6161/6165
ethernet switch chips.
+config NET_DSA_MV88E6171
+ tristate "Marvell 88E6171 ethernet switch chip support"
+ select NET_DSA
+ select NET_DSA_MV88E6XXX
+ select NET_DSA_TAG_EDSA
+ ---help---
+ This enables support for the Marvell 88E6171 ethernet switch
+ chip.
+
config NET_DSA_BCM_SF2
tristate "Broadcom Starfighter 2 Ethernet switch support"
select NET_DSA
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index dd3cd3b8157f..23a90de9830e 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -7,4 +7,7 @@ endif
ifdef CONFIG_NET_DSA_MV88E6131
mv88e6xxx_drv-y += mv88e6131.o
endif
+ifdef CONFIG_NET_DSA_MV88E6171
+mv88e6xxx_drv-y += mv88e6171.o
+endif
obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm_sf2.o
diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c
new file mode 100644
index 000000000000..8bae73bec1d7
--- /dev/null
+++ b/drivers/net/dsa/mv88e6171.c
@@ -0,0 +1,407 @@
+/* net/dsa/mv88e6171.c - Marvell 88e6171 switch chip support
+ * Copyright (c) 2008-2009 Marvell Semiconductor
+ * Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <net/dsa.h>
+#include "mv88e6xxx.h"
+
+static char *mv88e6171_probe(struct mii_bus *bus, int sw_addr)
+{
+ int ret;
+
+ ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
+ if (ret >= 0) {
+ if ((ret & 0xfff0) == 0x1710)
+ return "Marvell 88E6171";
+ }
+
+ return NULL;
+}
+
+static int mv88e6171_switch_reset(struct dsa_switch *ds)
+{
+ int i;
+ int ret;
+ unsigned long timeout;
+
+ /* Set all ports to the disabled state. */
+ for (i = 0; i < 8; i++) {
+ ret = REG_READ(REG_PORT(i), 0x04);
+ REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
+ }
+
+ /* Wait for transmit queues to drain. */
+ usleep_range(2000, 4000);
+
+ /* Reset the switch. */
+ REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
+
+ /* Wait up to one second for reset to complete. */
+ timeout = jiffies + 1 * HZ;
+ while (time_before(jiffies, timeout)) {
+ ret = REG_READ(REG_GLOBAL, 0x00);
+ if ((ret & 0xc800) == 0xc800)
+ break;
+
+ usleep_range(1000, 2000);
+ }
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+
+ /* Enable ports not under DSA, e.g. WAN port */
+ for (i = 0; i < 8; i++) {
+ if (dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i))
+ continue;
+
+ ret = REG_READ(REG_PORT(i), 0x04);
+ REG_WRITE(REG_PORT(i), 0x04, ret | 0x03);
+ }
+
+ return 0;
+}
+
+static int mv88e6171_setup_global(struct dsa_switch *ds)
+{
+ int ret;
+ int i;
+
+ /* Disable the PHY polling unit (since there won't be any
+ * external PHYs to poll), don't discard packets with
+ * excessive collisions, and mask all interrupt sources.
+ */
+ REG_WRITE(REG_GLOBAL, 0x04, 0x0000);
+
+ /* Set the default address aging time to 5 minutes, and
+ * enable address learn messages to be sent to all message
+ * ports.
+ */
+ REG_WRITE(REG_GLOBAL, 0x0a, 0x0148);
+
+ /* Configure the priority mapping registers. */
+ ret = mv88e6xxx_config_prio(ds);
+ if (ret < 0)
+ return ret;
+
+ /* Configure the upstream port, and configure the upstream
+ * port as the port to which ingress and egress monitor frames
+ * are to be sent.
+ */
+ if (REG_READ(REG_PORT(0), 0x03) == 0x1710)
+ REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1111));
+ else
+ REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1110));
+
+ /* Disable remote management for now, and set the switch's
+ * DSA device number.
+ */
+ REG_WRITE(REG_GLOBAL, 0x1c, ds->index & 0x1f);
+
+ /* Send all frames with destination addresses matching
+ * 01:80:c2:00:00:2x to the CPU port.
+ */
+ REG_WRITE(REG_GLOBAL2, 0x02, 0xffff);
+
+ /* Send all frames with destination addresses matching
+ * 01:80:c2:00:00:0x to the CPU port.
+ */
+ REG_WRITE(REG_GLOBAL2, 0x03, 0xffff);
+
+ /* Disable the loopback filter, disable flow control
+ * messages, disable flood broadcast override, disable
+ * removing of provider tags, disable ATU age violation
+ * interrupts, disable tag flow control, force flow
+ * control priority to the highest, and send all special
+ * multicast frames to the CPU at the highest priority.
+ */
+ REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff);
+
+ /* Program the DSA routing table. */
+ for (i = 0; i < 32; i++) {
+ int nexthop;
+
+ nexthop = 0x1f;
+ if (i != ds->index && i < ds->dst->pd->nr_chips)
+ nexthop = ds->pd->rtable[i] & 0x1f;
+
+ REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | nexthop);
+ }
+
+ /* Clear all trunk masks. */
+ for (i = 0; i < 8; i++)
+ REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff);
+
+ /* Clear all trunk mappings. */
+ for (i = 0; i < 16; i++)
+ REG_WRITE(REG_GLOBAL2, 0x08, 0x8000 | (i << 11));
+
+ /* Disable ingress rate limiting by resetting all ingress
+ * rate limit registers to their initial state.
+ */
+ for (i = 0; i < 6; i++)
+ REG_WRITE(REG_GLOBAL2, 0x09, 0x9000 | (i << 8));
+
+ /* Initialise cross-chip port VLAN table to reset defaults. */
+ REG_WRITE(REG_GLOBAL2, 0x0b, 0x9000);
+
+ /* Clear the priority override table. */
+ for (i = 0; i < 16; i++)
+ REG_WRITE(REG_GLOBAL2, 0x0f, 0x8000 | (i << 8));
+
+ /* @@@ initialise AVB (22/23) watchdog (27) sdet (29) registers */
+
+ return 0;
+}
+
+static int mv88e6171_setup_port(struct dsa_switch *ds, int p)
+{
+ int addr = REG_PORT(p);
+ u16 val;
+
+ /* MAC Forcing register: don't force link, speed, duplex
+ * or flow control state to any particular values on physical
+ * ports, but force the CPU port and all DSA ports to 1000 Mb/s
+ * full duplex.
+ */
+ val = REG_READ(addr, 0x01);
+ if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p))
+ REG_WRITE(addr, 0x01, val | 0x003e);
+ else
+ REG_WRITE(addr, 0x01, val | 0x0003);
+
+ /* Do not limit the period of time that this port can be
+ * paused for by the remote end or the period of time that
+ * this port can pause the remote end.
+ */
+ REG_WRITE(addr, 0x02, 0x0000);
+
+ /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
+ * disable Header mode, enable IGMP/MLD snooping, disable VLAN
+ * tunneling, determine priority by looking at 802.1p and IP
+ * priority fields (IP prio has precedence), and set STP state
+ * to Forwarding.
+ *
+ * If this is the CPU link, use DSA or EDSA tagging depending
+ * on which tagging mode was configured.
+ *
+ * If this is a link to another switch, use DSA tagging mode.
+ *
+ * If this is the upstream port for this switch, enable
+ * forwarding of unknown unicasts and multicasts.
+ */
+ val = 0x0433;
+ if (dsa_is_cpu_port(ds, p)) {
+ if (ds->dst->tag_protocol == htons(ETH_P_EDSA))
+ val |= 0x3300;
+ else
+ val |= 0x0100;
+ }
+ if (ds->dsa_port_mask & (1 << p))
+ val |= 0x0100;
+ if (p == dsa_upstream_port(ds))
+ val |= 0x000c;
+ REG_WRITE(addr, 0x04, val);
+
+ /* Port Control 1: disable trunking. Also, if this is the
+ * CPU port, enable learn messages to be sent to this port.
+ */
+ REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000);
+
+ /* Port based VLAN map: give each port its own address
+ * database, allow the CPU port to talk to each of the 'real'
+ * ports, and allow each of the 'real' ports to only talk to
+ * the upstream port.
+ */
+ val = (p & 0xf) << 12;
+ if (dsa_is_cpu_port(ds, p))
+ val |= ds->phys_port_mask;
+ else
+ val |= 1 << dsa_upstream_port(ds);
+ REG_WRITE(addr, 0x06, val);
+
+ /* Default VLAN ID and priority: don't set a default VLAN
+ * ID, and set the default packet priority to zero.
+ */
+ REG_WRITE(addr, 0x07, 0x0000);
+
+ /* Port Control 2: don't force a good FCS, set the maximum
+ * frame size to 10240 bytes, don't let the switch add or
+ * strip 802.1q tags, don't discard tagged or untagged frames
+ * on this port, do a destination address lookup on all
+ * received packets as usual, disable ARP mirroring and don't
+ * send a copy of all transmitted/received frames on this port
+ * to the CPU.
+ */
+ REG_WRITE(addr, 0x08, 0x2080);
+
+ /* Egress rate control: disable egress rate control. */
+ REG_WRITE(addr, 0x09, 0x0001);
+
+ /* Egress rate control 2: disable egress rate control. */
+ REG_WRITE(addr, 0x0a, 0x0000);
+
+ /* Port Association Vector: when learning source addresses
+ * of packets, add the address to the address database using
+ * a port bitmap that has only the bit for this port set and
+ * the other bits clear.
+ */
+ REG_WRITE(addr, 0x0b, 1 << p);
+
+ /* Port ATU control: disable limiting the number of address
+ * database entries that this port is allowed to use.
+ */
+ REG_WRITE(addr, 0x0c, 0x0000);
+
+ /* Priority Override: disable DA, SA and VTU priority override. */
+ REG_WRITE(addr, 0x0d, 0x0000);
+
+ /* Port Ethertype: use the Ethertype DSA Ethertype value. */
+ REG_WRITE(addr, 0x0f, ETH_P_EDSA);
+
+ /* Tag Remap: use an identity 802.1p prio -> switch prio
+ * mapping.
+ */
+ REG_WRITE(addr, 0x18, 0x3210);
+
+ /* Tag Remap 2: use an identity 802.1p prio -> switch prio
+ * mapping.
+ */
+ REG_WRITE(addr, 0x19, 0x7654);
+
+ return 0;
+}
+
+static int mv88e6171_setup(struct dsa_switch *ds)
+{
+ struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+ int i;
+ int ret;
+
+ mutex_init(&ps->smi_mutex);
+ mutex_init(&ps->stats_mutex);
+
+ ret = mv88e6171_switch_reset(ds);
+ if (ret < 0)
+ return ret;
+
+ /* @@@ initialise vtu and atu */
+
+ ret = mv88e6171_setup_global(ds);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < 8; i++) {
+ if (!(dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i)))
+ continue;
+
+ ret = mv88e6171_setup_port(ds, i);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mv88e6171_port_to_phy_addr(int port)
+{
+ if (port >= 0 && port <= 4)
+ return port;
+ return -1;
+}
+
+static int
+mv88e6171_phy_read(struct dsa_switch *ds, int port, int regnum)
+{
+ int addr = mv88e6171_port_to_phy_addr(port);
+
+ return mv88e6xxx_phy_read(ds, addr, regnum);
+}
+
+static int
+mv88e6171_phy_write(struct dsa_switch *ds,
+ int port, int regnum, u16 val)
+{
+ int addr = mv88e6171_port_to_phy_addr(port);
+
+ return mv88e6xxx_phy_write(ds, addr, regnum, val);
+}
+
+static struct mv88e6xxx_hw_stat mv88e6171_hw_stats[] = {
+ { "in_good_octets", 8, 0x00, },
+ { "in_bad_octets", 4, 0x02, },
+ { "in_unicast", 4, 0x04, },
+ { "in_broadcasts", 4, 0x06, },
+ { "in_multicasts", 4, 0x07, },
+ { "in_pause", 4, 0x16, },
+ { "in_undersize", 4, 0x18, },
+ { "in_fragments", 4, 0x19, },
+ { "in_oversize", 4, 0x1a, },
+ { "in_jabber", 4, 0x1b, },
+ { "in_rx_error", 4, 0x1c, },
+ { "in_fcs_error", 4, 0x1d, },
+ { "out_octets", 8, 0x0e, },
+ { "out_unicast", 4, 0x10, },
+ { "out_broadcasts", 4, 0x13, },
+ { "out_multicasts", 4, 0x12, },
+ { "out_pause", 4, 0x15, },
+ { "excessive", 4, 0x11, },
+ { "collisions", 4, 0x1e, },
+ { "deferred", 4, 0x05, },
+ { "single", 4, 0x14, },
+ { "multiple", 4, 0x17, },
+ { "out_fcs_error", 4, 0x03, },
+ { "late", 4, 0x1f, },
+ { "hist_64bytes", 4, 0x08, },
+ { "hist_65_127bytes", 4, 0x09, },
+ { "hist_128_255bytes", 4, 0x0a, },
+ { "hist_256_511bytes", 4, 0x0b, },
+ { "hist_512_1023bytes", 4, 0x0c, },
+ { "hist_1024_max_bytes", 4, 0x0d, },
+};
+
+static void
+mv88e6171_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
+{
+ mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6171_hw_stats),
+ mv88e6171_hw_stats, port, data);
+}
+
+static void
+mv88e6171_get_ethtool_stats(struct dsa_switch *ds,
+ int port, uint64_t *data)
+{
+ mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6171_hw_stats),
+ mv88e6171_hw_stats, port, data);
+}
+
+static int mv88e6171_get_sset_count(struct dsa_switch *ds)
+{
+ return ARRAY_SIZE(mv88e6171_hw_stats);
+}
+
+struct dsa_switch_driver mv88e6171_switch_driver = {
+ .tag_protocol = cpu_to_be16(ETH_P_EDSA),
+ .priv_size = sizeof(struct mv88e6xxx_priv_state),
+ .probe = mv88e6171_probe,
+ .setup = mv88e6171_setup,
+ .set_addr = mv88e6xxx_set_addr_indirect,
+ .phy_read = mv88e6171_phy_read,
+ .phy_write = mv88e6171_phy_write,
+ .poll_link = mv88e6xxx_poll_link,
+ .get_strings = mv88e6171_get_strings,
+ .get_ethtool_stats = mv88e6171_get_ethtool_stats,
+ .get_sset_count = mv88e6171_get_sset_count,
+};
+
+MODULE_ALIAS("platform:mv88e6171");
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index 9ce2146346b6..901d2a9704ef 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -501,12 +501,18 @@ static int __init mv88e6xxx_init(void)
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
register_switch_driver(&mv88e6123_61_65_switch_driver);
#endif
+#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
+ register_switch_driver(&mv88e6171_switch_driver);
+#endif
return 0;
}
module_init(mv88e6xxx_init);
static void __exit mv88e6xxx_cleanup(void)
{
+#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
+ unregister_switch_driver(&mv88e6171_switch_driver);
+#endif
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
unregister_switch_driver(&mv88e6123_61_65_switch_driver);
#endif
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index 911ede58dd12..5e5145ad9525 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -70,6 +70,7 @@ void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
extern struct dsa_switch_driver mv88e6131_switch_driver;
extern struct dsa_switch_driver mv88e6123_61_65_switch_driver;
+extern struct dsa_switch_driver mv88e6171_switch_driver;
#define REG_READ(addr, reg) \
({ \
--
2.1.0
^ permalink raw reply related
* Re: [PATCH] net: DSA: Marvell mv88e6171 switch driver
From: Claudio Leite @ 2014-09-13 0:50 UTC (permalink / raw)
To: netdev
In-Reply-To: <1410559124-10495-1-git-send-email-andrew@lunn.ch>
* Andrew Lunn (andrew@lunn.ch) wrote:
> This is the Marvell driver with some cleanups by Claudio Leite
> and myself.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> Cc: Claudio Leite <leitec@staticky.com>
> ---
Signed-off-by: Claudio Leite <leitec@staticky.com>
^ permalink raw reply
* Re: [PATCH] net: DSA: Marvell mv88e6171 switch driver
From: Andrew Lunn @ 2014-09-13 0:12 UTC (permalink / raw)
To: Florian Fainelli; +Cc: Andrew Lunn, davem, netdev, Claudio Leite
In-Reply-To: <541370DD.7090309@gmail.com>
On Fri, Sep 12, 2014 at 03:17:01PM -0700, Florian Fainelli wrote:
> Hi Andrew,
>
> On 09/12/2014 02:58 PM, Andrew Lunn wrote:
> > This is the Marvell driver with some cleanups by Claudio Leite
> > and myself.
>
> Looks good to me, this will slightly conflict with the patch for the
> tag_protocol field [1], other than that, good to see this driver
> submitted! Thanks!
Hi Florian
How do you want to handle this conflict?
Andrew
^ permalink raw reply
* Re: [Patch net-next] ipv6: exit early in addrconf_notify() if IPv6 is disabled
From: Cong Wang @ 2014-09-13 0:10 UTC (permalink / raw)
To: Hannes Frederic Sowa
Cc: Linux Kernel Network Developers, Hideaki YOSHIFUJI,
David S. Miller
In-Reply-To: <1410553099.2970.18.camel@localhost>
On Fri, Sep 12, 2014 at 1:18 PM, Hannes Frederic Sowa <hannes@redhat.com> wrote:
>
> disable_ipv6 is absolutely bad implemented. It should not be in the ipv6
> procfs namespace at all. An inet6_dev must be available to manage this
> knob! Thus we are automagically subscribed to all ipv6 LL multicast
> groups.
Exactly, this is chicken and egg problem and is why we have ipv6_idev_add()
in the NETDEV_REGISTER event.
^ permalink raw reply
* Re: [net-next PATCH v5 13/16] net: sched: make tc_action safe to walk under RCU
From: John Fastabend @ 2014-09-13 0:05 UTC (permalink / raw)
To: Eric Dumazet; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <1410555114.7106.101.camel@edumazet-glaptop2.roam.corp.google.com>
On 09/12/2014 01:51 PM, Eric Dumazet wrote:
> On Fri, 2014-09-12 at 09:34 -0700, John Fastabend wrote:
>
>> Second there is a suspect usage of list_splice_init_rcu() in the
>> tcf_exts_change() routine. Notice how it is used twice in succession
>> and the second init works on the src tcf_exts. There is probably a
>> better way to accomplish that.
Not only is it suspect its broke as best as I can tell.
>>
>
>
>> +/* It is not safe to use src->actions after this due to _init_rcu usage
>> + * INIT_LIST_HEAD_RCU() is called on src->actions
>> + */
>> void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
>> struct tcf_exts *src)
>> {
>> #ifdef CONFIG_NET_CLS_ACT
>> LIST_HEAD(tmp);
>> - tcf_tree_lock(tp);
>> - list_splice_init(&dst->actions, &tmp);
>> - list_splice(&src->actions, &dst->actions);
>> - tcf_tree_unlock(tp);
Here we have three lists,
tmp (which has just been initialized)
dst -> act1 -> act2 -> ... -> actn ->
src -> acta -> actb -> ... -> actn ->
The active list is dst from the tc_classify and we want to
switch to the src list
>> + list_splice_init_rcu(&dst->actions, &tmp, synchronize_rcu);
Now we spliced in tmp and did an INIT_LIST_HEAD_RCU() on dst->actions
(oops) so the lists look like this
tmp -> act1 -> act2 -> ... -> actn ->
dst
src -> acta -> actb -> ... -> actn ->
The active list is still dst from the tc_classify caller so now we are
missing both the old and new action lists. This is broke.
>> + list_splice_init_rcu(&src->actions,
>> + &dst->actions,
>> + synchronize_rcu);
But the final state is what we wanted,
tmp -> act1 -> act2 -> ... -> actn ->
dst -> acta -> actb -> ... -> actn ->
src
>> tcf_action_destroy(&tmp, TCA_ACT_UNBIND);
Finally clean it all up,
tmp
dst -> acta -> actb -> ... -> actn ->
src
>> #endif
>> }
>
> I am afraid I do not understand this part.
Because it doesn't work as far as I can tell. What I really want here
is to swap the head pointer with,
struct list_head *tmp = dst->actions;
rcu_assign_pointer(dst->actions, src->actions)
synchronize_rcu()
tcf_action_destroy(tmp);
This requires a bit more work. I'll work out a patch after a bit more
thought.
Thanks again!
.John
--
John Fastabend Intel Corporation
^ permalink raw reply
* [PATCHv2] net/macb: Add hardware revision information during probe
From: Alexandre Belloni @ 2014-09-12 23:57 UTC (permalink / raw)
To: David S. Miller
Cc: linux-arm-kernel, Nicolas Ferre, linux-kernel, netdev, Bo Shen
From: Bo Shen <voice.shen@atmel.com>
Print the IP revision when probing.
Signed-off-by: Bo Shen <voice.shen@atmel.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
Changes in v2:
- condense information on one line
drivers/net/ethernet/cadence/macb.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index ca5d7798b265..d9b8e94b805f 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -2241,9 +2241,9 @@ static int __init macb_probe(struct platform_device *pdev)
netif_carrier_off(dev);
- netdev_info(dev, "Cadence %s at 0x%08lx irq %d (%pM)\n",
- macb_is_gem(bp) ? "GEM" : "MACB", dev->base_addr,
- dev->irq, dev->dev_addr);
+ netdev_info(dev, "Cadence %s rev 0x%08x at 0x%08lx irq %d (%pM)\n",
+ macb_is_gem(bp) ? "GEM" : "MACB", macb_readl(bp, MID),
+ dev->base_addr, dev->irq, dev->dev_addr);
phydev = bp->phy_dev;
netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
--
1.9.1
^ permalink raw reply related
* Re: [net-next PATCH v5 16/16] net: sched: drop ingress qdisc lock
From: Eric Dumazet @ 2014-09-12 23:56 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140912163519.19588.54966.stgit@nitbit.x32>
On Fri, 2014-09-12 at 09:35 -0700, John Fastabend wrote:
> After the previous patches to make the filters RCU safe and
> support per cpu counters we can drop the qdisc lock around
> the ingress qdisc hook.
>
> This is possible because the ingress qdisc is a very basic
> qdisc and only updates stats and runs tc_classify. Its the
> simplest qdiscs we have.
>
> In order for the per-cpu counters to get invoked the
> ingress qdisc must set the LLQDISC flag. We could use per-cpu
> counters on all counters but it is only necessary when the
> qdisc lock is not held.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
Acked-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply
* Re: [net-next PATCH v5 15/16] net: sched: make qstats per cpu
From: Eric Dumazet @ 2014-09-12 23:55 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140912163453.19588.6775.stgit@nitbit.x32>
On Fri, 2014-09-12 at 09:34 -0700, John Fastabend wrote:
> Now that we run qdisc without locked the qstats need to handle the
> case where multiple cpus are receiving or transmiting a skb. For now
> the only qdisc that supports running without locks is the ingress
> qdisc which increments the 32-bit drop counter.
>
> When the stats are collected the summation of all CPUs is collected
> and returned in the dump tlv.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
> include/net/codel.h | 4 ++--
> include/net/gen_stats.h | 2 ++
> include/net/sch_generic.h | 19 +++++++++++--------
> net/core/gen_stats.c | 30 +++++++++++++++++++++++++++++-
> net/sched/sch_api.c | 31 ++++++++++++++++++++++++++-----
> net/sched/sch_atm.c | 2 +-
> net/sched/sch_cbq.c | 10 +++++-----
> net/sched/sch_choke.c | 15 ++++++++-------
> net/sched/sch_codel.c | 2 +-
> net/sched/sch_drr.c | 8 ++++----
> net/sched/sch_dsmark.c | 2 +-
> net/sched/sch_fifo.c | 6 ++++--
> net/sched/sch_fq.c | 4 ++--
> net/sched/sch_fq_codel.c | 8 ++++----
> net/sched/sch_generic.c | 8 +++++---
> net/sched/sch_gred.c | 10 +++++-----
> net/sched/sch_hfsc.c | 15 ++++++++-------
> net/sched/sch_hhf.c | 8 ++++----
> net/sched/sch_htb.c | 6 +++---
> net/sched/sch_ingress.c | 4 +++-
> net/sched/sch_mq.c | 23 ++++++++++++++---------
> net/sched/sch_mqprio.c | 35 ++++++++++++++++++++++-------------
> net/sched/sch_multiq.c | 8 ++++----
> net/sched/sch_netem.c | 17 +++++++++--------
> net/sched/sch_pie.c | 8 ++++----
> net/sched/sch_plug.c | 2 +-
> net/sched/sch_prio.c | 8 ++++----
> net/sched/sch_qfq.c | 8 ++++----
> net/sched/sch_red.c | 13 +++++++------
> net/sched/sch_sfb.c | 13 +++++++------
> net/sched/sch_sfq.c | 19 ++++++++++---------
> net/sched/sch_tbf.c | 11 ++++++-----
> 32 files changed, 220 insertions(+), 139 deletions(-)
>
> diff --git a/include/net/codel.h b/include/net/codel.h
> index aeee280..72d37a4 100644
> --- a/include/net/codel.h
> +++ b/include/net/codel.h
> @@ -228,13 +228,13 @@ static bool codel_should_drop(const struct sk_buff *skb,
> }
>
> vars->ldelay = now - codel_get_enqueue_time(skb);
> - sch->qstats.backlog -= qdisc_pkt_len(skb);
> + sch->qstats_qdisc.qstats.backlog -= qdisc_pkt_len(skb);
>
> if (unlikely(qdisc_pkt_len(skb) > stats->maxpacket))
> stats->maxpacket = qdisc_pkt_len(skb);
>
> if (codel_time_before(vars->ldelay, params->target) ||
> - sch->qstats.backlog <= stats->maxpacket) {
> + sch->qstats_qdisc.qstats.backlog <= stats->maxpacket) {
> /* went below - stay below for at least interval */
> vars->first_above_time = 0;
> return false;
> diff --git a/include/net/gen_stats.h b/include/net/gen_stats.h
> index 4b7ca2b..d548dc9 100644
> --- a/include/net/gen_stats.h
> +++ b/include/net/gen_stats.h
> @@ -44,6 +44,8 @@ int gnet_stats_copy_rate_est(struct gnet_dump *d,
> const struct gnet_stats_basic_cpu __percpu *cpu_b,
> struct gnet_stats_rate_est64 *r);
> int gnet_stats_copy_queue(struct gnet_dump *d, struct gnet_stats_queue *q);
> +int gnet_stats_copy_queue_cpu(struct gnet_dump *d,
> + struct gnet_stats_queue __percpu *q);
> int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len);
>
> int gnet_stats_finish_copy(struct gnet_dump *d);
> diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
> index 54e318f..518be3b 100644
> --- a/include/net/sch_generic.h
> +++ b/include/net/sch_generic.h
> @@ -92,7 +92,10 @@ struct Qdisc {
> struct gnet_stats_basic_cpu __percpu *cpu_bstats;
> } bstats_qdisc;
> unsigned int __state;
> - struct gnet_stats_queue qstats;
> + union {
> + struct gnet_stats_queue qstats;
> + struct gnet_stats_queue __percpu *cpu_qstats;
> + } qstats_qdisc;
> struct rcu_head rcu_head;
> int padded;
> atomic_t refcnt;
> @@ -538,7 +541,7 @@ static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch,
> struct sk_buff_head *list)
> {
> __skb_queue_tail(list, skb);
> - sch->qstats.backlog += qdisc_pkt_len(skb);
> + sch->qstats_qdisc.qstats.backlog += qdisc_pkt_len(skb);
I am a bit lost :(
Probably a helper could make this cleaner.
>
> return NET_XMIT_SUCCESS;
> }
> @@ -554,7 +557,7 @@ static inline struct sk_buff *__qdisc_dequeue_head(struct Qdisc *sch,
> struct sk_buff *skb = __skb_dequeue(list);
>
> if (likely(skb != NULL)) {
> - sch->qstats.backlog -= qdisc_pkt_len(skb);
> + sch->qstats_qdisc.qstats.backlog -= qdisc_pkt_len(skb);
because this kind of patch in the future could be easier : only change
the helper ;)
Please split this patch in 2 parts :
1) Add clean helpers/macros (no functional change)
2) Make the percpu changes
^ permalink raw reply
* Re: [net-next PATCH v5 14/16] net: sched: make bstats per cpu and estimator RCU safe
From: Eric Dumazet @ 2014-09-12 23:49 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140912163428.19588.52611.stgit@nitbit.x32>
On Fri, 2014-09-12 at 09:34 -0700, John Fastabend wrote:
> In order to run qdisc's without locking statistics and estimators
> need to be handled correctly.
>
> To resolve bstats make the statistics per cpu. And because this is
> only needed for qdiscs that are running without locks which is not
> the case for most qdiscs in the near future only create percpu
> stats when qdiscs set the TCQ_F_LLQDISC flag.
>
> Next because estimators use the bstats to calculate packets per
> second and bytes per second the estimator code paths are updated
> to use the per cpu statistics.
>
> After this if qdiscs use the _percpu routines to update stats
> and set the TCQ_F_LLQDISC they can be made safe to run without
> locking. Any qdisc that sets the flag needs to ensure that the
> data structures it owns are safe to run without locks and
> additionally currently all the skb list routines run without
> locking so those would need to be updated in a future patch.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
Hmm... this is a bit too complex for my taste.
> include/net/gen_stats.h | 16 ++++++++++++
> include/net/sch_generic.h | 40 +++++++++++++++++++++++++++--
> net/core/gen_estimator.c | 60 ++++++++++++++++++++++++++++++++++----------
> net/core/gen_stats.c | 47 ++++++++++++++++++++++++++++++++++
> net/netfilter/xt_RATEEST.c | 4 +--
> net/sched/act_api.c | 9 ++++---
> net/sched/act_police.c | 4 +--
> net/sched/sch_api.c | 51 ++++++++++++++++++++++++++++++++-----
> net/sched/sch_cbq.c | 9 ++++---
> net/sched/sch_drr.c | 9 ++++---
> net/sched/sch_generic.c | 11 +++++++-
> net/sched/sch_hfsc.c | 15 +++++++----
> net/sched/sch_htb.c | 14 +++++++---
> net/sched/sch_ingress.c | 2 +
> net/sched/sch_mq.c | 10 ++++---
> net/sched/sch_mqprio.c | 13 +++++-----
> net/sched/sch_multiq.c | 2 +
> net/sched/sch_prio.c | 2 +
> net/sched/sch_qfq.c | 12 +++++----
> 19 files changed, 258 insertions(+), 72 deletions(-)
>
> diff --git a/include/net/gen_stats.h b/include/net/gen_stats.h
> index ea4271d..4b7ca2b 100644
> --- a/include/net/gen_stats.h
> +++ b/include/net/gen_stats.h
> @@ -6,6 +6,11 @@
> #include <linux/rtnetlink.h>
> #include <linux/pkt_sched.h>
>
> +struct gnet_stats_basic_cpu {
> + struct gnet_stats_basic_packed bstats;
> + struct u64_stats_sync syncp;
> +};
> +
> struct gnet_dump {
> spinlock_t * lock;
> struct sk_buff * skb;
> @@ -26,10 +31,17 @@ int gnet_stats_start_copy_compat(struct sk_buff *skb, int type,
> int tc_stats_type, int xstats_type,
> spinlock_t *lock, struct gnet_dump *d);
>
> +int gnet_stats_copy_basic_cpu(struct gnet_dump *d,
> + struct gnet_stats_basic_cpu __percpu *b);
> +void __gnet_stats_copy_basic_cpu(struct gnet_stats_basic_packed *bstats,
> + struct gnet_stats_basic_cpu __percpu *b);
> int gnet_stats_copy_basic(struct gnet_dump *d,
> struct gnet_stats_basic_packed *b);
> +void __gnet_stats_copy_basic(struct gnet_stats_basic_packed *bstats,
> + struct gnet_stats_basic_packed *b);
> int gnet_stats_copy_rate_est(struct gnet_dump *d,
> const struct gnet_stats_basic_packed *b,
> + const struct gnet_stats_basic_cpu __percpu *cpu_b,
> struct gnet_stats_rate_est64 *r);
> int gnet_stats_copy_queue(struct gnet_dump *d, struct gnet_stats_queue *q);
> int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len);
> @@ -37,13 +49,17 @@ int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len);
> int gnet_stats_finish_copy(struct gnet_dump *d);
>
> int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
> + struct gnet_stats_basic_cpu __percpu *cpu_bstats,
> struct gnet_stats_rate_est64 *rate_est,
> spinlock_t *stats_lock, struct nlattr *opt);
> void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
> + struct gnet_stats_basic_cpu __percpu *cpu_bstats,
> struct gnet_stats_rate_est64 *rate_est);
> int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
> + struct gnet_stats_basic_cpu __percpu *cpu_bstats,
> struct gnet_stats_rate_est64 *rate_est,
> spinlock_t *stats_lock, struct nlattr *opt);
> bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
> + const struct gnet_stats_basic_cpu __percpu *cpu_bstat,
> const struct gnet_stats_rate_est64 *rate_est);
> #endif
> diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
> index 1e89b9a..54e318f 100644
> --- a/include/net/sch_generic.h
> +++ b/include/net/sch_generic.h
> @@ -6,6 +6,7 @@
> #include <linux/rcupdate.h>
> #include <linux/pkt_sched.h>
> #include <linux/pkt_cls.h>
> +#include <linux/percpu.h>
> #include <net/gen_stats.h>
> #include <net/rtnetlink.h>
>
> @@ -57,6 +58,9 @@ struct Qdisc {
> * Its true for MQ/MQPRIO slaves, or non
> * multiqueue device.
> */
> +#define TCQ_F_LLQDISC 0x20 /* lockless qdiscs can run without using
> + * the qdisc lock to serialize skbs.
> + */
> #define TCQ_F_WARN_NONWC (1 << 16)
> u32 limit;
> const struct Qdisc_ops *ops;
> @@ -83,7 +87,10 @@ struct Qdisc {
> */
> unsigned long state;
> struct sk_buff_head q;
> - struct gnet_stats_basic_packed bstats;
> + union {
> + struct gnet_stats_basic_packed bstats;
> + struct gnet_stats_basic_cpu __percpu *cpu_bstats;
> + } bstats_qdisc;
I believe this union will loose the _packed attribute, and __state will
no longer fill the 4 bytes hole we had after struct
gnet_stats_basic_packed bstats
So I think you should add a __packed attribute :
+ union {
+ struct gnet_stats_basic_packed bstats;
+ struct gnet_stats_basic_cpu __percpu *cpu_bstats;
+ } __packed bstats_qdisc;
btw, have you tried to use an anonymous union ?
> unsigned int __state;
> struct gnet_stats_queue qstats;
> struct rcu_head rcu_head;
> @@ -486,7 +493,6 @@ static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch)
> return qdisc_enqueue(skb, sch) & NET_XMIT_MASK;
> }
>
> -
> static inline void bstats_update(struct gnet_stats_basic_packed *bstats,
> const struct sk_buff *skb)
> {
> @@ -494,10 +500,38 @@ static inline void bstats_update(struct gnet_stats_basic_packed *bstats,
> bstats->packets += skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1;
> }
>
> +static inline void qdisc_bstats_update_cpu(struct Qdisc *sch,
> + const struct sk_buff *skb)
> +{
> + struct gnet_stats_basic_cpu *bstats =
> + this_cpu_ptr(sch->bstats_qdisc.cpu_bstats);
> +
> + u64_stats_update_begin(&bstats->syncp);
> + bstats_update(&bstats->bstats, skb);
> + u64_stats_update_end(&bstats->syncp);
> +}
> +
> static inline void qdisc_bstats_update(struct Qdisc *sch,
> const struct sk_buff *skb)
> {
> - bstats_update(&sch->bstats, skb);
> + bstats_update(&sch->bstats_qdisc.bstats, skb);
> +}
> +
> +static inline bool qdisc_is_lockless(struct Qdisc *q)
const ?
btw, not sure if the name is correct. You could still use percpu stats
even for a qdisc using a lock at some point...
> +{
> + return q->flags & TCQ_F_LLQDISC;
> +}
> +
> +static inline int
> +qdisc_bstats_copy_qdisc(struct gnet_dump *d, struct Qdisc *q)
const struct Qdisc *q ?
> +{
> + int err;
> +
> + if (qdisc_is_lockless(q))
> + err = gnet_stats_copy_basic_cpu(d, q->bstats_qdisc.cpu_bstats);
> + else
> + err = gnet_stats_copy_basic(d, &q->bstats_qdisc.bstats);
> + return err;
> }
I believe you should reuse a single function, gnet_stats_copy_basic()
and add an additional parameter (per_cpu pointer), that can be NULL.
>
> static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch,
> diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c
> index 9d33dff..0d13319 100644
> --- a/net/core/gen_estimator.c
> +++ b/net/core/gen_estimator.c
> @@ -91,6 +91,8 @@ struct gen_estimator
> u32 avpps;
> struct rcu_head e_rcu;
> struct rb_node node;
> + struct gnet_stats_basic_cpu __percpu *cpu_bstats;
> + struct rcu_head head;
> };
>
> struct gen_estimator_head
> @@ -122,11 +124,21 @@ static void est_timer(unsigned long arg)
>
> spin_lock(e->stats_lock);
> read_lock(&est_lock);
> - if (e->bstats == NULL)
> +
> + if (e->bstats == NULL && e->cpu_bstats == NULL)
> goto skip;
You should only test e->bstats here.
>
> - nbytes = e->bstats->bytes;
> - npackets = e->bstats->packets;
> + if (e->cpu_bstats) {
> + struct gnet_stats_basic_packed b = {0};
> +
> + __gnet_stats_copy_basic_cpu(&b, e->cpu_bstats);
> + nbytes = b.bytes;
> + npackets = b.packets;
> + } else {
> + nbytes = e->bstats->bytes;
> + npackets = e->bstats->packets;
> + }
> +
> brate = (nbytes - e->last_bytes)<<(7 - idx);
> e->last_bytes = nbytes;
> e->avbps += (brate >> e->ewma_log) - (e->avbps >> e->ewma_log);
> @@ -146,6 +158,11 @@ skip:
> rcu_read_unlock();
> }
>
> +static void *gen_get_bstats(struct gen_estimator *est)
> +{
> + return est->cpu_bstats ? (void *)est->cpu_bstats : (void *)est->bstats;
> +}
Note there is no guarantee that percpu addresses and kmalloc() addresses
do not collide on some arches.
Thats a bit ugly, you could change your convention to always sort on
est->bstats
(No need for this helper)
> +
> static void gen_add_node(struct gen_estimator *est)
> {
> struct rb_node **p = &est_root.rb_node, *parent = NULL;
> @@ -156,7 +173,7 @@ static void gen_add_node(struct gen_estimator *est)
> parent = *p;
> e = rb_entry(parent, struct gen_estimator, node);
>
> - if (est->bstats > e->bstats)
> + if (gen_get_bstats(est) > gen_get_bstats(e))
Change not needed.
> p = &parent->rb_right;
> else
> p = &parent->rb_left;
> @@ -167,18 +184,20 @@ static void gen_add_node(struct gen_estimator *est)
>
> static
> struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats,
> + const struct gnet_stats_basic_cpu __percpu *cpu_bstats,
> const struct gnet_stats_rate_est64 *rate_est)
> {
> struct rb_node *p = est_root.rb_node;
> + void *b = bstats ? (void *)bstats : (void *)cpu_bstats;
>
Same complexity not needed. You should not have to change this function
at all.
> while (p) {
> struct gen_estimator *e;
>
> e = rb_entry(p, struct gen_estimator, node);
>
> - if (bstats > e->bstats)
> + if (b > gen_get_bstats(e))
> p = p->rb_right;
> - else if (bstats < e->bstats || rate_est != e->rate_est)
> + else if (b < gen_get_bstats(e) || rate_est != e->rate_est)
> p = p->rb_left;
> else
> return e;
> @@ -203,6 +222,7 @@ struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats
> *
> */
> int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
> + struct gnet_stats_basic_cpu __percpu *cpu_bstats,
> struct gnet_stats_rate_est64 *rate_est,
> spinlock_t *stats_lock,
> struct nlattr *opt)
> @@ -221,14 +241,24 @@ int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
> if (est == NULL)
> return -ENOBUFS;
>
> + if (cpu_bstats) {
> + struct gnet_stats_basic_packed b = {0};
> +
> + est->cpu_bstats = cpu_bstats;
> + __gnet_stats_copy_basic_cpu(&b, cpu_bstats);
> + est->last_bytes = b.bytes;
> + est->last_packets = b.packets;
> + } else {
> + est->bstats = bstats;
est->bstats should be set in both cases, this is the key for searches.
> + est->last_bytes = bstats->bytes;
> + est->last_packets = bstats->packets;
> + }
> +
> idx = parm->interval + 2;
> - est->bstats = bstats;
> est->rate_est = rate_est;
> est->stats_lock = stats_lock;
> est->ewma_log = parm->ewma_log;
> - est->last_bytes = bstats->bytes;
> est->avbps = rate_est->bps<<5;
> - est->last_packets = bstats->packets;
> est->avpps = rate_est->pps<<10;
>
> spin_lock_bh(&est_tree_lock);
> @@ -258,14 +288,14 @@ EXPORT_SYMBOL(gen_new_estimator);
> * Note : Caller should respect an RCU grace period before freeing stats_lock
> */
> void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
> + struct gnet_stats_basic_cpu __percpu *cpu_bstats,
> struct gnet_stats_rate_est64 *rate_est)
> {
> struct gen_estimator *e;
>
> spin_lock_bh(&est_tree_lock);
> - while ((e = gen_find_node(bstats, rate_est))) {
> + while ((e = gen_find_node(bstats, cpu_bstats, rate_est))) {
> rb_erase(&e->node, &est_root);
> -
> write_lock(&est_lock);
> e->bstats = NULL;
> write_unlock(&est_lock);
> @@ -290,11 +320,12 @@ EXPORT_SYMBOL(gen_kill_estimator);
> * Returns 0 on success or a negative error code.
> */
> int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
> + struct gnet_stats_basic_cpu __percpu *cpu_bstats,
> struct gnet_stats_rate_est64 *rate_est,
> spinlock_t *stats_lock, struct nlattr *opt)
> {
> - gen_kill_estimator(bstats, rate_est);
> - return gen_new_estimator(bstats, rate_est, stats_lock, opt);
> + gen_kill_estimator(bstats, cpu_bstats, rate_est);
> + return gen_new_estimator(bstats, cpu_bstats, rate_est, stats_lock, opt);
> }
> EXPORT_SYMBOL(gen_replace_estimator);
>
> @@ -306,6 +337,7 @@ EXPORT_SYMBOL(gen_replace_estimator);
> * Returns true if estimator is active, and false if not.
> */
> bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
> + const struct gnet_stats_basic_cpu __percpu *cpu_bstats,
> const struct gnet_stats_rate_est64 *rate_est)
> {
> bool res;
> @@ -313,7 +345,7 @@ bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
> ASSERT_RTNL();
>
> spin_lock_bh(&est_tree_lock);
> - res = gen_find_node(bstats, rate_est) != NULL;
> + res = gen_find_node(bstats, cpu_bstats, rate_est) != NULL;
> spin_unlock_bh(&est_tree_lock);
>
> return res;
> diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c
> index 2ddbce4..e43b55f 100644
> --- a/net/core/gen_stats.c
> +++ b/net/core/gen_stats.c
> @@ -128,6 +128,50 @@ gnet_stats_copy_basic(struct gnet_dump *d, struct gnet_stats_basic_packed *b)
> }
> EXPORT_SYMBOL(gnet_stats_copy_basic);
>
> +void
> +__gnet_stats_copy_basic(struct gnet_stats_basic_packed *bstats,
> + struct gnet_stats_basic_packed *b)
> +{
> + bstats->bytes = b->bytes;
> + bstats->packets = b->packets;
> +}
> +EXPORT_SYMBOL(__gnet_stats_copy_basic);
> +
> +void
> +__gnet_stats_copy_basic_cpu(struct gnet_stats_basic_packed *bstats,
> + struct gnet_stats_basic_cpu __percpu *b)
> +{
> + int i;
> +
> + for_each_possible_cpu(i) {
> + struct gnet_stats_basic_cpu *bcpu = per_cpu_ptr(b, i);
> + unsigned int start;
> + __u64 bytes;
> + __u32 packets;
> +
> + do {
> + start = u64_stats_fetch_begin_irq(&bcpu->syncp);
> + bytes = bcpu->bstats.bytes;
> + packets = bcpu->bstats.packets;
> + } while (u64_stats_fetch_retry_irq(&bcpu->syncp, start));
> +
> + bstats->bytes += bcpu->bstats.bytes;
> + bstats->packets += bcpu->bstats.packets;
> + }
> +}
> +EXPORT_SYMBOL(__gnet_stats_copy_basic_cpu);
> +
> +int
> +gnet_stats_copy_basic_cpu(struct gnet_dump *d,
> + struct gnet_stats_basic_cpu __percpu *b)
> +{
> + struct gnet_stats_basic_packed bstats = {0};
> +
> + __gnet_stats_copy_basic_cpu(&bstats, b);
> + return gnet_stats_copy_basic(d, &bstats);
> +}
> +EXPORT_SYMBOL(gnet_stats_copy_basic_cpu);
> +
> /**
> * gnet_stats_copy_rate_est - copy rate estimator statistics into statistics TLV
> * @d: dumping handle
> @@ -143,12 +187,13 @@ EXPORT_SYMBOL(gnet_stats_copy_basic);
> int
> gnet_stats_copy_rate_est(struct gnet_dump *d,
> const struct gnet_stats_basic_packed *b,
> + const struct gnet_stats_basic_cpu __percpu *cpu_b,
> struct gnet_stats_rate_est64 *r)
> {
> struct gnet_stats_rate_est est;
> int res;
>
> - if (b && !gen_estimator_active(b, r))
> + if ((b || cpu_b) && !gen_estimator_active(b, cpu_b, r))
> return 0;
>
> est.bps = min_t(u64, UINT_MAX, r->bps);
> diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c
> index 370adf6..02e2532 100644
> --- a/net/netfilter/xt_RATEEST.c
> +++ b/net/netfilter/xt_RATEEST.c
> @@ -64,7 +64,7 @@ void xt_rateest_put(struct xt_rateest *est)
> mutex_lock(&xt_rateest_mutex);
> if (--est->refcnt == 0) {
> hlist_del(&est->list);
> - gen_kill_estimator(&est->bstats, &est->rstats);
> + gen_kill_estimator(&est->bstats, NULL, &est->rstats);
> /*
> * gen_estimator est_timer() might access est->lock or bstats,
> * wait a RCU grace period before freeing 'est'
> @@ -136,7 +136,7 @@ static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par)
> cfg.est.interval = info->interval;
> cfg.est.ewma_log = info->ewma_log;
>
> - ret = gen_new_estimator(&est->bstats, &est->rstats,
> + ret = gen_new_estimator(&est->bstats, NULL, &est->rstats,
> &est->lock, &cfg.opt);
> if (ret < 0)
> goto err2;
> diff --git a/net/sched/act_api.c b/net/sched/act_api.c
> index ae32b5b..89504ea 100644
> --- a/net/sched/act_api.c
> +++ b/net/sched/act_api.c
> @@ -35,7 +35,7 @@ void tcf_hash_destroy(struct tc_action *a)
> spin_lock_bh(&hinfo->lock);
> hlist_del(&p->tcfc_head);
> spin_unlock_bh(&hinfo->lock);
> - gen_kill_estimator(&p->tcfc_bstats,
> + gen_kill_estimator(&p->tcfc_bstats, NULL,
> &p->tcfc_rate_est);
> /*
> * gen_estimator est_timer() might access p->tcfc_lock
> @@ -228,7 +228,7 @@ void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est)
> {
> struct tcf_common *pc = a->priv;
> if (est)
> - gen_kill_estimator(&pc->tcfc_bstats,
> + gen_kill_estimator(&pc->tcfc_bstats, NULL,
> &pc->tcfc_rate_est);
> kfree_rcu(pc, tcfc_rcu);
> }
> @@ -252,7 +252,8 @@ int tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a,
> p->tcfc_tm.install = jiffies;
> p->tcfc_tm.lastuse = jiffies;
> if (est) {
> - int err = gen_new_estimator(&p->tcfc_bstats, &p->tcfc_rate_est,
> + int err = gen_new_estimator(&p->tcfc_bstats, NULL,
> + &p->tcfc_rate_est,
> &p->tcfc_lock, est);
> if (err) {
> kfree(p);
> @@ -620,7 +621,7 @@ int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a,
> goto errout;
>
> if (gnet_stats_copy_basic(&d, &p->tcfc_bstats) < 0 ||
> - gnet_stats_copy_rate_est(&d, &p->tcfc_bstats,
> + gnet_stats_copy_rate_est(&d, &p->tcfc_bstats, NULL,
> &p->tcfc_rate_est) < 0 ||
> gnet_stats_copy_queue(&d, &p->tcfc_qstats) < 0)
> goto errout;
> diff --git a/net/sched/act_police.c b/net/sched/act_police.c
> index f32bcb0..26f4bb3 100644
> --- a/net/sched/act_police.c
> +++ b/net/sched/act_police.c
> @@ -178,14 +178,14 @@ override:
>
> spin_lock_bh(&police->tcf_lock);
> if (est) {
> - err = gen_replace_estimator(&police->tcf_bstats,
> + err = gen_replace_estimator(&police->tcf_bstats, NULL,
> &police->tcf_rate_est,
> &police->tcf_lock, est);
> if (err)
> goto failure_unlock;
> } else if (tb[TCA_POLICE_AVRATE] &&
> (ret == ACT_P_CREATED ||
> - !gen_estimator_active(&police->tcf_bstats,
> + !gen_estimator_active(&police->tcf_bstats, NULL,
> &police->tcf_rate_est))) {
> err = -EINVAL;
> goto failure_unlock;
> diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
> index ca62483..beb2064 100644
> --- a/net/sched/sch_api.c
> +++ b/net/sched/sch_api.c
> @@ -942,6 +942,13 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
> sch->handle = handle;
>
> if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) {
> + if (qdisc_is_lockless(sch)) {
> + sch->bstats_qdisc.cpu_bstats =
> + alloc_percpu(struct gnet_stats_basic_cpu);
> + if (!sch->bstats_qdisc.cpu_bstats)
> + goto err_out4;
> + }
> +
> if (tca[TCA_STAB]) {
> stab = qdisc_get_stab(tca[TCA_STAB]);
> if (IS_ERR(stab)) {
> @@ -964,8 +971,18 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
> else
> root_lock = qdisc_lock(sch);
>
> - err = gen_new_estimator(&sch->bstats, &sch->rate_est,
> - root_lock, tca[TCA_RATE]);
> + if (qdisc_is_lockless(sch))
> + err = gen_new_estimator(NULL,
> + sch->bstats_qdisc.cpu_bstats,
> + &sch->rate_est,
> + root_lock,
> + tca[TCA_RATE]);
> + else
> + err = gen_new_estimator(&sch->bstats_qdisc.bstats,
> + NULL,
> + &sch->rate_est,
> + root_lock,
> + tca[TCA_RATE]);
Note that you could have a simpler convention : and use
err = gen_new_estimator(&sch->bstats_qdisc.bstats,
sch->bstats_qdisc.cpu_bstats,
root_lock,
tca[TCA_RATE]);
This way, this patch would be way smaller.
> if (err)
> goto err_out4;
> }
> @@ -1022,9 +1039,11 @@ static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
> because change can't be undone. */
> if (sch->flags & TCQ_F_MQROOT)
> goto out;
> - gen_replace_estimator(&sch->bstats, &sch->rate_est,
> - qdisc_root_sleeping_lock(sch),
> - tca[TCA_RATE]);
> + gen_replace_estimator(&sch->bstats_qdisc.bstats,
> + sch->bstats_qdisc.cpu_bstats,
> + &sch->rate_est,
> + qdisc_root_sleeping_lock(sch),
> + tca[TCA_RATE]);
> }
> out:
> return 0;
> @@ -1304,6 +1323,7 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
> unsigned char *b = skb_tail_pointer(skb);
> struct gnet_dump d;
> struct qdisc_size_table *stab;
> + int err;
>
> cond_resched();
> nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
> @@ -1334,10 +1354,25 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
> if (q->ops->dump_stats && q->ops->dump_stats(q, &d) < 0)
> goto nla_put_failure;
>
> - if (gnet_stats_copy_basic(&d, &q->bstats) < 0 ||
> - gnet_stats_copy_rate_est(&d, &q->bstats, &q->rate_est) < 0 ||
> + if (qdisc_is_lockless(q)) {
> + err = gnet_stats_copy_basic_cpu(&d, q->bstats_qdisc.cpu_bstats);
> + if (err < 0)
> + goto nla_put_failure;
> + err = gnet_stats_copy_rate_est(&d, NULL,
> + q->bstats_qdisc.cpu_bstats,
> + &q->rate_est);
> + } else {
> + err = gnet_stats_copy_basic(&d, &q->bstats_qdisc.bstats);
> + if (err < 0)
> + goto nla_put_failure;
> + err = gnet_stats_copy_rate_est(&d,
> + &q->bstats_qdisc.bstats, NULL,
> + &q->rate_est);
> + }
Same thing here....
You could simply extend existing functions with one cpu_bstats argument.
If it is null, then you copy the traditional bstats.
> +
> + if (err < 0 ||
> gnet_stats_copy_queue(&d, &q->qstats) < 0)
> - goto nla_put_failure;
> + goto nla_put_failure;
>
> if (gnet_stats_finish_copy(&d) < 0)
> goto nla_put_failure;
> diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
> index a3244a8..208074d 100644
> --- a/net/sched/sch_cbq.c
> +++ b/net/sched/sch_cbq.c
> @@ -1602,7 +1602,7 @@ cbq_dump_class_stats(struct Qdisc *sch, unsigned long arg,
> cl->xstats.undertime = cl->undertime - q->now;
>
> if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
> - gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
> + gnet_stats_copy_rate_est(d, &cl->bstats, NULL, &cl->rate_est) < 0 ||
> gnet_stats_copy_queue(d, &cl->qstats) < 0)
> return -1;
>
> @@ -1671,7 +1671,7 @@ static void cbq_destroy_class(struct Qdisc *sch, struct cbq_class *cl)
> tcf_destroy_chain(&cl->filter_list);
> qdisc_destroy(cl->q);
> qdisc_put_rtab(cl->R_tab);
> - gen_kill_estimator(&cl->bstats, &cl->rate_est);
> + gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
> if (cl != &q->link)
> kfree(cl);
> }
> @@ -1759,7 +1759,8 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
> }
>
> if (tca[TCA_RATE]) {
> - err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> + err = gen_replace_estimator(&cl->bstats, NULL,
> + &cl->rate_est,
> qdisc_root_sleeping_lock(sch),
> tca[TCA_RATE]);
> if (err) {
> @@ -1852,7 +1853,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
> goto failure;
>
> if (tca[TCA_RATE]) {
> - err = gen_new_estimator(&cl->bstats, &cl->rate_est,
> + err = gen_new_estimator(&cl->bstats, NULL, &cl->rate_est,
> qdisc_root_sleeping_lock(sch),
> tca[TCA_RATE]);
> if (err) {
> diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c
> index d8b5ccf..88a65f6 100644
> --- a/net/sched/sch_drr.c
> +++ b/net/sched/sch_drr.c
> @@ -88,7 +88,8 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
>
> if (cl != NULL) {
> if (tca[TCA_RATE]) {
> - err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> + err = gen_replace_estimator(&cl->bstats, NULL,
> + &cl->rate_est,
> qdisc_root_sleeping_lock(sch),
> tca[TCA_RATE]);
> if (err)
> @@ -116,7 +117,7 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
> cl->qdisc = &noop_qdisc;
>
> if (tca[TCA_RATE]) {
> - err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> + err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est,
> qdisc_root_sleeping_lock(sch),
> tca[TCA_RATE]);
> if (err) {
> @@ -138,7 +139,7 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
>
> static void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl)
> {
> - gen_kill_estimator(&cl->bstats, &cl->rate_est);
> + gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
> qdisc_destroy(cl->qdisc);
> kfree(cl);
> }
> @@ -283,7 +284,7 @@ static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg,
> }
>
> if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
> - gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
> + gnet_stats_copy_rate_est(d, &cl->bstats, NULL, &cl->rate_est) < 0 ||
> gnet_stats_copy_queue(d, &cl->qdisc->qstats) < 0)
> return -1;
>
> diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
> index 346ef85..e3d203e 100644
> --- a/net/sched/sch_generic.c
> +++ b/net/sched/sch_generic.c
> @@ -632,6 +632,9 @@ static void qdisc_rcu_free(struct rcu_head *head)
> {
> struct Qdisc *qdisc = container_of(head, struct Qdisc, rcu_head);
>
> + if (qdisc_is_lockless(qdisc))
> + free_percpu(qdisc->bstats_qdisc.cpu_bstats);
> +
> kfree((char *) qdisc - qdisc->padded);
> }
>
> @@ -648,7 +651,13 @@ void qdisc_destroy(struct Qdisc *qdisc)
>
> qdisc_put_stab(rtnl_dereference(qdisc->stab));
> #endif
> - gen_kill_estimator(&qdisc->bstats, &qdisc->rate_est);
> + if (qdisc_is_lockless(qdisc))
> + gen_kill_estimator(NULL, qdisc->bstats_qdisc.cpu_bstats,
> + &qdisc->rate_est);
> + else
> + gen_kill_estimator(&qdisc->bstats_qdisc.bstats, NULL,
> + &qdisc->rate_est);
> +
> if (ops->reset)
> ops->reset(qdisc);
> if (ops->destroy)
> diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
> index 04b0de4..3b112d2 100644
> --- a/net/sched/sch_hfsc.c
> +++ b/net/sched/sch_hfsc.c
> @@ -1014,9 +1014,12 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
> cur_time = psched_get_time();
>
> if (tca[TCA_RATE]) {
> - err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> - qdisc_root_sleeping_lock(sch),
> - tca[TCA_RATE]);
> + spinlock_t *lock = qdisc_root_sleeping_lock(sch);
> +
> + err = gen_replace_estimator(&cl->bstats, NULL,
> + &cl->rate_est,
> + lock,
> + tca[TCA_RATE]);
> if (err)
> return err;
> }
> @@ -1063,7 +1066,7 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
> return -ENOBUFS;
>
> if (tca[TCA_RATE]) {
> - err = gen_new_estimator(&cl->bstats, &cl->rate_est,
> + err = gen_new_estimator(&cl->bstats, NULL, &cl->rate_est,
> qdisc_root_sleeping_lock(sch),
> tca[TCA_RATE]);
> if (err) {
> @@ -1113,7 +1116,7 @@ hfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl)
>
> tcf_destroy_chain(&cl->filter_list);
> qdisc_destroy(cl->qdisc);
> - gen_kill_estimator(&cl->bstats, &cl->rate_est);
> + gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
> if (cl != &q->root)
> kfree(cl);
> }
> @@ -1375,7 +1378,7 @@ hfsc_dump_class_stats(struct Qdisc *sch, unsigned long arg,
> xstats.rtwork = cl->cl_cumul;
>
> if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
> - gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
> + gnet_stats_copy_rate_est(d, &cl->bstats, NULL, &cl->rate_est) < 0 ||
> gnet_stats_copy_queue(d, &cl->qstats) < 0)
> return -1;
>
> diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
> index 6d16b9b..8067a82 100644
> --- a/net/sched/sch_htb.c
> +++ b/net/sched/sch_htb.c
> @@ -1145,7 +1145,7 @@ htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d)
> cl->xstats.ctokens = PSCHED_NS2TICKS(cl->ctokens);
>
> if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
> - gnet_stats_copy_rate_est(d, NULL, &cl->rate_est) < 0 ||
> + gnet_stats_copy_rate_est(d, NULL, NULL, &cl->rate_est) < 0 ||
> gnet_stats_copy_queue(d, &cl->qstats) < 0)
> return -1;
>
> @@ -1235,7 +1235,7 @@ static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl)
> WARN_ON(!cl->un.leaf.q);
> qdisc_destroy(cl->un.leaf.q);
> }
> - gen_kill_estimator(&cl->bstats, &cl->rate_est);
> + gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
> tcf_destroy_chain(&cl->filter_list);
> kfree(cl);
> }
> @@ -1402,7 +1402,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
> goto failure;
>
> if (htb_rate_est || tca[TCA_RATE]) {
> - err = gen_new_estimator(&cl->bstats, &cl->rate_est,
> + err = gen_new_estimator(&cl->bstats, NULL,
> + &cl->rate_est,
> qdisc_root_sleeping_lock(sch),
> tca[TCA_RATE] ? : &est.nla);
> if (err) {
> @@ -1464,8 +1465,11 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
> parent->children++;
> } else {
> if (tca[TCA_RATE]) {
> - err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> - qdisc_root_sleeping_lock(sch),
> + spinlock_t *lock = qdisc_root_sleeping_lock(sch);
> +
> + err = gen_replace_estimator(&cl->bstats, NULL,
> + &cl->rate_est,
> + lock,
> tca[TCA_RATE]);
> if (err)
> return err;
> diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
> index b351125..25302be 100644
> --- a/net/sched/sch_ingress.c
> +++ b/net/sched/sch_ingress.c
> @@ -65,7 +65,7 @@ static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch)
>
> result = tc_classify(skb, fl, &res);
>
> - qdisc_bstats_update(sch, skb);
> + qdisc_bstats_update_cpu(sch, skb);
> switch (result) {
> case TC_ACT_SHOT:
> result = TC_ACT_SHOT;
> diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c
> index a8b2864..e96a41f 100644
> --- a/net/sched/sch_mq.c
> +++ b/net/sched/sch_mq.c
> @@ -98,20 +98,22 @@ static void mq_attach(struct Qdisc *sch)
>
> static int mq_dump(struct Qdisc *sch, struct sk_buff *skb)
> {
> + struct gnet_stats_basic_packed *bstats = &sch->bstats_qdisc.bstats;
> struct net_device *dev = qdisc_dev(sch);
> struct Qdisc *qdisc;
> unsigned int ntx;
>
> sch->q.qlen = 0;
> - memset(&sch->bstats, 0, sizeof(sch->bstats));
> + memset(bstats, 0, sizeof(sch->bstats_qdisc));
> memset(&sch->qstats, 0, sizeof(sch->qstats));
>
> for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
> qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping;
> spin_lock_bh(qdisc_lock(qdisc));
> sch->q.qlen += qdisc->q.qlen;
> - sch->bstats.bytes += qdisc->bstats.bytes;
> - sch->bstats.packets += qdisc->bstats.packets;
> +
> + bstats->bytes += qdisc->bstats_qdisc.bstats.bytes;
> + bstats->packets += qdisc->bstats_qdisc.bstats.packets;
> sch->qstats.qlen += qdisc->qstats.qlen;
> sch->qstats.backlog += qdisc->qstats.backlog;
> sch->qstats.drops += qdisc->qstats.drops;
> @@ -201,7 +203,7 @@ static int mq_dump_class_stats(struct Qdisc *sch, unsigned long cl,
>
> sch = dev_queue->qdisc_sleeping;
> sch->qstats.qlen = sch->q.qlen;
> - if (gnet_stats_copy_basic(d, &sch->bstats) < 0 ||
> + if (gnet_stats_copy_basic(d, &sch->bstats_qdisc.bstats) < 0 ||
> gnet_stats_copy_queue(d, &sch->qstats) < 0)
> return -1;
> return 0;
> diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c
> index 37e7d25..6e3e4e9 100644
> --- a/net/sched/sch_mqprio.c
> +++ b/net/sched/sch_mqprio.c
> @@ -219,6 +219,7 @@ static int mqprio_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
>
> static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb)
> {
> + struct gnet_stats_basic_packed *bstats = &sch->bstats_qdisc.bstats;
> struct net_device *dev = qdisc_dev(sch);
> struct mqprio_sched *priv = qdisc_priv(sch);
> unsigned char *b = skb_tail_pointer(skb);
> @@ -227,15 +228,15 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb)
> unsigned int i;
>
> sch->q.qlen = 0;
> - memset(&sch->bstats, 0, sizeof(sch->bstats));
> + memset(bstats, 0, sizeof(sch->bstats_qdisc.bstats));
> memset(&sch->qstats, 0, sizeof(sch->qstats));
>
> for (i = 0; i < dev->num_tx_queues; i++) {
> qdisc = rtnl_dereference(netdev_get_tx_queue(dev, i)->qdisc);
> spin_lock_bh(qdisc_lock(qdisc));
> sch->q.qlen += qdisc->q.qlen;
> - sch->bstats.bytes += qdisc->bstats.bytes;
> - sch->bstats.packets += qdisc->bstats.packets;
> + bstats->bytes += qdisc->bstats_qdisc.bstats.bytes;
> + bstats->packets += qdisc->bstats_qdisc.bstats.packets;
> sch->qstats.qlen += qdisc->qstats.qlen;
> sch->qstats.backlog += qdisc->qstats.backlog;
> sch->qstats.drops += qdisc->qstats.drops;
> @@ -344,8 +345,8 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
>
> qdisc = rtnl_dereference(q->qdisc);
> spin_lock_bh(qdisc_lock(qdisc));
> - bstats.bytes += qdisc->bstats.bytes;
> - bstats.packets += qdisc->bstats.packets;
> + bstats.bytes += qdisc->bstats_qdisc.bstats.bytes;
> + bstats.packets += qdisc->bstats_qdisc.bstats.packets;
> qstats.qlen += qdisc->qstats.qlen;
> qstats.backlog += qdisc->qstats.backlog;
> qstats.drops += qdisc->qstats.drops;
> @@ -363,7 +364,7 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
>
> sch = dev_queue->qdisc_sleeping;
> sch->qstats.qlen = sch->q.qlen;
> - if (gnet_stats_copy_basic(d, &sch->bstats) < 0 ||
> + if (gnet_stats_copy_basic(d, &sch->bstats_qdisc.bstats) < 0 ||
> gnet_stats_copy_queue(d, &sch->qstats) < 0)
> return -1;
> }
> diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c
> index c0466c1..d6430102 100644
> --- a/net/sched/sch_multiq.c
> +++ b/net/sched/sch_multiq.c
> @@ -361,7 +361,7 @@ static int multiq_dump_class_stats(struct Qdisc *sch, unsigned long cl,
>
> cl_q = q->queues[cl - 1];
> cl_q->qstats.qlen = cl_q->q.qlen;
> - if (gnet_stats_copy_basic(d, &cl_q->bstats) < 0 ||
> + if (gnet_stats_copy_basic(d, &cl_q->bstats_qdisc.bstats) < 0 ||
> gnet_stats_copy_queue(d, &cl_q->qstats) < 0)
> return -1;
>
> diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
> index 03ef99e..9069aba 100644
> --- a/net/sched/sch_prio.c
> +++ b/net/sched/sch_prio.c
> @@ -325,7 +325,7 @@ static int prio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
>
> cl_q = q->queues[cl - 1];
> cl_q->qstats.qlen = cl_q->q.qlen;
> - if (gnet_stats_copy_basic(d, &cl_q->bstats) < 0 ||
> + if (gnet_stats_copy_basic(d, &cl_q->bstats_qdisc.bstats) < 0 ||
> gnet_stats_copy_queue(d, &cl_q->qstats) < 0)
> return -1;
>
> diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
> index 602ea01..52a602d 100644
> --- a/net/sched/sch_qfq.c
> +++ b/net/sched/sch_qfq.c
> @@ -459,7 +459,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
>
> if (cl != NULL) { /* modify existing class */
> if (tca[TCA_RATE]) {
> - err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> + err = gen_replace_estimator(&cl->bstats, NULL,
> + &cl->rate_est,
> qdisc_root_sleeping_lock(sch),
> tca[TCA_RATE]);
> if (err)
> @@ -484,7 +485,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
> cl->qdisc = &noop_qdisc;
>
> if (tca[TCA_RATE]) {
> - err = gen_new_estimator(&cl->bstats, &cl->rate_est,
> + err = gen_new_estimator(&cl->bstats, NULL,
> + &cl->rate_est,
> qdisc_root_sleeping_lock(sch),
> tca[TCA_RATE]);
> if (err)
> @@ -505,7 +507,7 @@ set_change_agg:
> new_agg = kzalloc(sizeof(*new_agg), GFP_KERNEL);
> if (new_agg == NULL) {
> err = -ENOBUFS;
> - gen_kill_estimator(&cl->bstats, &cl->rate_est);
> + gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
> goto destroy_class;
> }
> sch_tree_lock(sch);
> @@ -530,7 +532,7 @@ static void qfq_destroy_class(struct Qdisc *sch, struct qfq_class *cl)
> struct qfq_sched *q = qdisc_priv(sch);
>
> qfq_rm_from_agg(q, cl);
> - gen_kill_estimator(&cl->bstats, &cl->rate_est);
> + gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
> qdisc_destroy(cl->qdisc);
> kfree(cl);
> }
> @@ -668,7 +670,7 @@ static int qfq_dump_class_stats(struct Qdisc *sch, unsigned long arg,
> xstats.lmax = cl->agg->lmax;
>
> if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
> - gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
> + gnet_stats_copy_rate_est(d, &cl->bstats, NULL, &cl->rate_est) < 0 ||
> gnet_stats_copy_queue(d, &cl->qdisc->qstats) < 0)
> return -1;
>
>
^ permalink raw reply
* Re: [PATCH] crypto: talitos: Avoid excessive loops in softirq context
From: Kim Phillips @ 2014-09-12 23:21 UTC (permalink / raw)
To: Helmut Schaa
Cc: linux-crypto, Herbert Xu, David Miller, Sandeep Malik,
Horia Geanta, netdev
In-Reply-To: <CAGXE3d-EiYmJQfEwS9VeR3-5vePNzc2+N20hQTw0vY8FKhJYRA@mail.gmail.com>
[adding Sandeep, Horia and netdev]
On Fri, 12 Sep 2014 09:39:12 +0200
Helmut Schaa <helmut.schaa@googlemail.com> wrote:
> On Fri, Sep 12, 2014 at 2:49 AM, Kim Phillips
> <kim.phillips@freescale.com> wrote:
> > On Wed, 10 Sep 2014 10:34:47 +0200
> > Helmut Schaa <helmut.schaa@googlemail.com> wrote:
> >
> >> The talitos driver can cause starvation of other softirqs and as such
> >> it can also cause rcu stalls like:
> > ...
> >> Work around this by processing a maximum amount of 16 finished requests
> >> and rescheduling the done-tasklet if any work is left.
> >> This allows other softirqs to run.
> >
> > 16 sounds rather arbitrary, and application-dependent - talitos'
> > FIFO size is 24.
>
> Yep, 16 is arbitrary, I can also do "fifo_len" if you prefer?
>
> > IIRC, netdev's NAPI can be refactored out of just being able to work
> > on network devices, and be made to apply to crypto devices, too. In
> > fact, some old Freescale hacks of this nature have improved
> > performance. Can we do something like refactor NAPI instead?
>
> That would indeed be nice but sounds like quite some more work and
> I won't have time to do so. Especially since my system was taken down
> completely by the talitos tasklet under some circumstances. If there is
> any work going on in that regard I'd be fine with just dropping that patch
> (and carrying it myself until the refactoring is done).
I'm not aware of any, but to prove whether NAPI actually fixes the
issue, can you try applying this patch:
http://patchwork.ozlabs.org/patch/146094/
For it to be upstream-acceptable, I believe it would have to port
NAPI in such a way that all crypto drivers could use it. The
difference between net NAPI and crypto NAPI would be that the crypto
version would be reduced to only using the weight code, since that's
the only source of the performance benefit.
Thanks,
Kim
^ permalink raw reply
* [PATCHv2 net-next 3/3] sunvnet: generate ICMP PTMUD messages for smaller port MTUs
From: David L Stevens @ 2014-09-12 23:19 UTC (permalink / raw)
To: David Miller; +Cc: netdev
This patch sends ICMP and ICMPv6 messages for Path MTU Discovery when a remote
port MTU is smaller than the device MTU. This allows mixing newer VIO protocol
devices that support MTU negotiation with older devices that do not on the
same vswitch. It also allows Linux-Linux LDOMs to use 64K-1 data packets even
though Solaris vswitch is limited to <16K MTU.
Signed-off-by: David L Stevens <david.stevens@oracle.com>
---
drivers/net/ethernet/sun/sunvnet.c | 34 ++++++++++++++++++++++++++++++++++
1 files changed, 34 insertions(+), 0 deletions(-)
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index b557ec4..b2381f0 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -17,6 +17,13 @@
#include <linux/mutex.h>
#include <linux/if_vlan.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <linux/icmpv6.h>
+#endif
+
+#include <net/icmp.h>
+#include <net/route.h>
+
#include <asm/vio.h>
#include <asm/ldc.h>
@@ -752,6 +759,33 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
goto out_dropped;
if (skb->len > port->vio.rmtu) {
+ unsigned long localmtu = port->vio.rmtu - ETH_HLEN;
+
+ if (port->vio.ver.major == 1 && port->vio.ver.minor >= 3)
+ localmtu -= VLAN_HLEN;
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ struct flowi4 fl4;
+ struct rtable *rt = NULL;
+
+ memset(&fl4, 0, sizeof(fl4));
+ fl4.flowi4_oif = dev->ifindex;
+ fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
+ fl4.daddr = ip_hdr(skb)->daddr;
+ fl4.saddr = ip_hdr(skb)->saddr;
+
+ rt = ip_route_output_key(dev_net(dev), &fl4);
+ if (!IS_ERR(rt)) {
+ skb_dst_set(skb, &rt->dst);
+ icmp_send(skb, ICMP_DEST_UNREACH,
+ ICMP_FRAG_NEEDED,
+ htonl(localmtu));
+ }
+ }
+#if IS_ENABLED(CONFIG_IPV6)
+ else if (skb->protocol == htons(ETH_P_IPV6))
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, localmtu);
+#endif
goto out_dropped;
}
--
1.7.1
^ permalink raw reply related
* [PATCHv2 net-next 2/3] sunvnet: allow admin to set sunvnet MTU
From: David L Stevens @ 2014-09-12 23:18 UTC (permalink / raw)
To: David Miller; +Cc: netdev
This patch allows an admin to set the MTU on a sunvnet device to arbitrary
values between the minimum (68) and maximum (65535) IPv4 packet sizes.
Signed-off-by: David L Stevens <david.stevens@oracle.com>
---
drivers/net/ethernet/sun/sunvnet.c | 6 +++++-
drivers/net/ethernet/sun/sunvnet.h | 2 +-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index a6418bb..b557ec4 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -751,6 +751,10 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(!port))
goto out_dropped;
+ if (skb->len > port->vio.rmtu) {
+ goto out_dropped;
+ }
+
spin_lock_irqsave(&port->vio.lock, flags);
dr = &port->vio.drings[VIO_DRIVER_TX_RING];
@@ -972,7 +976,7 @@ static void vnet_set_rx_mode(struct net_device *dev)
static int vnet_change_mtu(struct net_device *dev, int new_mtu)
{
- if (new_mtu != ETH_DATA_LEN)
+ if (new_mtu < 68 || new_mtu > 65535)
return -EINVAL;
dev->mtu = new_mtu;
diff --git a/drivers/net/ethernet/sun/sunvnet.h b/drivers/net/ethernet/sun/sunvnet.h
index 243ae69..fcf0129 100644
--- a/drivers/net/ethernet/sun/sunvnet.h
+++ b/drivers/net/ethernet/sun/sunvnet.h
@@ -11,7 +11,7 @@
*/
#define VNET_TX_TIMEOUT (5 * HZ)
-#define VNET_MAXPACKET 1518ULL /* ETH_FRAMELEN + VLAN_HDR */
+#define VNET_MAXPACKET 65553ULL /* 64K-1 +ETH HDR +VLAN HDR*/
#define VNET_TX_RING_SIZE 512
#define VNET_TX_WAKEUP_THRESH(dr) ((dr)->pending / 4)
--
1.7.1
^ permalink raw reply related
* [PATCHv2 net-next 1/3] sunvnet: upgrade to VIO protocol version 1.6
From: David L Stevens @ 2014-09-12 23:18 UTC (permalink / raw)
To: David Miller; +Cc: netdev
This patch upgrades the sunvnet driver to support VIO protocol version 1.6.
In particular, it adds per-port MTU negotiation, allowing MTUs other than
ETH_FRAMELEN with ports using newer VIO protocol versions.
Signed-off-by: David L Stevens <david.stevens@oracle.com>
---
arch/sparc/include/asm/vio.h | 19 ++++++-
arch/sparc/kernel/viohs.c | 14 +++++-
drivers/net/ethernet/sun/sunvnet.c | 96 ++++++++++++++++++++++++++++++------
drivers/net/ethernet/sun/sunvnet.h | 1 +
4 files changed, 110 insertions(+), 20 deletions(-)
diff --git a/arch/sparc/include/asm/vio.h b/arch/sparc/include/asm/vio.h
index 5c0ebe7..da13e3b 100644
--- a/arch/sparc/include/asm/vio.h
+++ b/arch/sparc/include/asm/vio.h
@@ -65,6 +65,7 @@ struct vio_dring_register {
u16 options;
#define VIO_TX_DRING 0x0001
#define VIO_RX_DRING 0x0002
+#define VIO_RX_DRING_DATA 0x0004
u16 resv;
u32 num_cookies;
struct ldc_trans_cookie cookies[0];
@@ -80,6 +81,8 @@ struct vio_dring_unregister {
#define VIO_PKT_MODE 0x01 /* Packet based transfer */
#define VIO_DESC_MODE 0x02 /* In-band descriptors */
#define VIO_DRING_MODE 0x03 /* Descriptor rings */
+/* in vers >= 1.2, VIO_DRING_MODE is 0x04 and transfer mode is a bitmask */
+#define VIO_NEW_DRING_MODE 0x04
struct vio_dring_data {
struct vio_msg_tag tag;
@@ -209,10 +212,20 @@ struct vio_net_attr_info {
u8 addr_type;
#define VNET_ADDR_ETHERMAC 0x01
u16 ack_freq;
- u32 resv1;
+ u8 plnk_updt;
+#define PHYSLINK_UPDATE_NONE 0x00
+#define PHYSLINK_UPDATE_STATE 0x01
+#define PHYSLINK_UPDATE_STATE_ACK 0x02
+#define PHYSLINK_UPDATE_STATE_NACK 0x03
+ u8 options;
+ u16 resv1;
u64 addr;
u64 mtu;
- u64 resv2[3];
+ u16 cflags;
+#define VNET_LSO_IPV4_CAPAB 0x0001
+ u16 ipv4_lso_maxlen;
+ u32 resv2;
+ u64 resv3[2];
};
#define VNET_NUM_MCAST 7
@@ -368,6 +381,8 @@ struct vio_driver_state {
char *name;
struct vio_driver_ops *ops;
+
+ u64 rmtu; /* remote MTU */
};
#define viodbg(TYPE, f, a...) \
diff --git a/arch/sparc/kernel/viohs.c b/arch/sparc/kernel/viohs.c
index f8e7dd5..446438b 100644
--- a/arch/sparc/kernel/viohs.c
+++ b/arch/sparc/kernel/viohs.c
@@ -426,6 +426,13 @@ static int process_dreg_info(struct vio_driver_state *vio,
if (vio->dr_state & VIO_DR_STATE_RXREG)
goto send_nack;
+ /* v1.6 and higher, ACK with desired, supported mode, or NACK */
+ if (vio->ver.major <= 1 && vio->ver.minor >= 6) {
+ if (!(pkt->options & VIO_TX_DRING))
+ goto send_nack;
+ pkt->options = VIO_TX_DRING;
+ }
+
BUG_ON(vio->desc_buf);
vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC);
@@ -453,8 +460,11 @@ static int process_dreg_info(struct vio_driver_state *vio,
pkt->tag.stype = VIO_SUBTYPE_ACK;
pkt->dring_ident = ++dr->ident;
- viodbg(HS, "SEND DRING_REG ACK ident[%llx]\n",
- (unsigned long long) pkt->dring_ident);
+ viodbg(HS, "SEND DRING_REG ACK ident[%llx] "
+ "ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n",
+ (unsigned long long) pkt->dring_ident,
+ pkt->num_descr, pkt->descr_size, pkt->options,
+ pkt->num_cookies);
len = (sizeof(*pkt) +
(dr->ncookies * sizeof(struct ldc_trans_cookie)));
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index a4657a4..a6418bb 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -15,6 +15,7 @@
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/mutex.h>
+#include <linux/if_vlan.h>
#include <asm/vio.h>
#include <asm/ldc.h>
@@ -39,6 +40,7 @@ MODULE_VERSION(DRV_MODULE_VERSION);
/* Ordered from largest major to lowest */
static struct vio_version vnet_versions[] = {
+ { .major = 1, .minor = 6 },
{ .major = 1, .minor = 0 },
};
@@ -65,6 +67,7 @@ static int vnet_send_attr(struct vio_driver_state *vio)
struct vnet_port *port = to_vnet_port(vio);
struct net_device *dev = port->vp->dev;
struct vio_net_attr_info pkt;
+ int framelen = ETH_FRAME_LEN;
int i;
memset(&pkt, 0, sizeof(pkt));
@@ -72,19 +75,37 @@ static int vnet_send_attr(struct vio_driver_state *vio)
pkt.tag.stype = VIO_SUBTYPE_INFO;
pkt.tag.stype_env = VIO_ATTR_INFO;
pkt.tag.sid = vio_send_sid(vio);
- pkt.xfer_mode = VIO_DRING_MODE;
+ if (vio->ver.major <= 1 && vio->ver.minor < 2)
+ pkt.xfer_mode = VIO_DRING_MODE;
+ else
+ pkt.xfer_mode = VIO_NEW_DRING_MODE;
pkt.addr_type = VNET_ADDR_ETHERMAC;
pkt.ack_freq = 0;
for (i = 0; i < 6; i++)
pkt.addr |= (u64)dev->dev_addr[i] << ((5 - i) * 8);
- pkt.mtu = ETH_FRAME_LEN;
+ if (vio->ver.major == 1) {
+ if (vio->ver.minor > 3) {
+ if (vio->rmtu)
+ vio->rmtu = pkt.mtu = min(VNET_MAXPACKET,
+ vio->rmtu);
+ else
+ vio->rmtu = pkt.mtu = VNET_MAXPACKET;
+ } else if (vio->ver.minor == 3)
+ pkt.mtu = framelen + VLAN_HLEN;
+ else
+ pkt.mtu = framelen;
+ if (vio->ver.minor >= 6)
+ pkt.options = VIO_TX_DRING;
+ }
viodbg(HS, "SEND NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] "
- "ackfreq[%u] mtu[%llu]\n",
+ "ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] mtu[%llu] "
+ "cflags[0x%04x] lso_max[%u]\n",
pkt.xfer_mode, pkt.addr_type,
(unsigned long long) pkt.addr,
- pkt.ack_freq,
- (unsigned long long) pkt.mtu);
+ pkt.ack_freq, pkt.plnk_updt, pkt.options,
+ (unsigned long long) pkt.mtu, pkt.cflags, pkt.ipv4_lso_maxlen);
+
return vio_ldc_send(vio, &pkt, sizeof(pkt));
}
@@ -92,18 +113,52 @@ static int vnet_send_attr(struct vio_driver_state *vio)
static int handle_attr_info(struct vio_driver_state *vio,
struct vio_net_attr_info *pkt)
{
- viodbg(HS, "GOT NET ATTR INFO xmode[0x%x] atype[0x%x] addr[%llx] "
- "ackfreq[%u] mtu[%llu]\n",
+ u64 localmtu;
+ u8 xfer_mode;
+
+ viodbg(HS, "GOT NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] "
+ "ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] mtu[%llu] "
+ " (rmtu[%llu]) cflags[0x%04x] lso_max[%u]\n",
pkt->xfer_mode, pkt->addr_type,
(unsigned long long) pkt->addr,
- pkt->ack_freq,
- (unsigned long long) pkt->mtu);
+ pkt->ack_freq, pkt->plnk_updt, pkt->options,
+ (unsigned long long) pkt->mtu, vio->rmtu, pkt->cflags,
+ pkt->ipv4_lso_maxlen);
pkt->tag.sid = vio_send_sid(vio);
- if (pkt->xfer_mode != VIO_DRING_MODE ||
+ xfer_mode = pkt->xfer_mode;
+ /* for version < 1.2, VIO_DRING_MODE = 0x3 and no bitmask */
+ if ((vio->ver.major <= 1 && vio->ver.minor < 2) &&
+ xfer_mode == VIO_DRING_MODE)
+ xfer_mode = VIO_NEW_DRING_MODE;
+
+ /* MTU negotiation:
+ * < v1.3 - ETH_FRAME_LEN exactly
+ * = v1.3 - ETH_FRAME_LEN + VLAN_HLEN exactly
+ * > v1.4 - MIN(pkt.mtu, VNET_MAX_PACKET, vio->rmtu) and change
+ * pkt->mtu for ACK
+ */
+ if (vio->ver.major == 1 && vio->ver.minor < 3) {
+ localmtu = ETH_FRAME_LEN;
+ } else if (vio->ver.major == 1 && vio->ver.minor == 3) {
+ localmtu = ETH_FRAME_LEN + VLAN_HLEN;
+ } else {
+ localmtu = vio->rmtu ? vio->rmtu : VNET_MAXPACKET;
+ localmtu = pkt->mtu = min(pkt->mtu, localmtu);
+ }
+ vio->rmtu = localmtu;
+
+
+ /* for version >= 1.6, ACK packet mode we support */
+ if (vio->ver.major <= 1 && vio->ver.minor >= 6) {
+ pkt->xfer_mode = VIO_NEW_DRING_MODE;
+ pkt->options = VIO_TX_DRING;
+ }
+
+ if (!(xfer_mode | VIO_NEW_DRING_MODE) ||
pkt->addr_type != VNET_ADDR_ETHERMAC ||
- pkt->mtu != ETH_FRAME_LEN) {
+ pkt->mtu != localmtu) {
viodbg(HS, "SEND NET ATTR NACK\n");
pkt->tag.stype = VIO_SUBTYPE_NACK;
@@ -112,7 +167,14 @@ static int handle_attr_info(struct vio_driver_state *vio,
return -ECONNRESET;
} else {
- viodbg(HS, "SEND NET ATTR ACK\n");
+ viodbg(HS, "SEND NET ATTR ACK xmode[0x%x] atype[0x%x] "
+ "addr[%llx] ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] "
+ "mtu[%llu] (rmtu[%llu]) cflags[0x%04x] lso_max[%u]\n",
+ pkt->xfer_mode, pkt->addr_type,
+ (unsigned long long) pkt->addr,
+ pkt->ack_freq, pkt->plnk_updt, pkt->options,
+ (unsigned long long) pkt->mtu, vio->rmtu, pkt->cflags,
+ pkt->ipv4_lso_maxlen);
pkt->tag.stype = VIO_SUBTYPE_ACK;
@@ -208,7 +270,7 @@ static int vnet_rx_one(struct vnet_port *port, unsigned int len,
int err;
err = -EMSGSIZE;
- if (unlikely(len < ETH_ZLEN || len > ETH_FRAME_LEN)) {
+ if (unlikely(len < ETH_ZLEN || len > port->vio.rmtu)) {
dev->stats.rx_length_errors++;
goto out_dropped;
}
@@ -528,8 +590,10 @@ static void vnet_event(void *arg, int event)
vio_link_state_change(vio, event);
spin_unlock_irqrestore(&vio->lock, flags);
- if (event == LDC_EVENT_RESET)
+ if (event == LDC_EVENT_RESET) {
+ vio->rmtu = 0;
vio_port_up(vio);
+ }
return;
}
@@ -986,8 +1050,8 @@ static int vnet_port_alloc_tx_bufs(struct vnet_port *port)
void *dring;
for (i = 0; i < VNET_TX_RING_SIZE; i++) {
- void *buf = kzalloc(ETH_FRAME_LEN + 8, GFP_KERNEL);
- int map_len = (ETH_FRAME_LEN + 7) & ~7;
+ void *buf = kzalloc(VNET_MAXPACKET + 8, GFP_KERNEL);
+ int map_len = (VNET_MAXPACKET + 7) & ~7;
err = -ENOMEM;
if (!buf)
diff --git a/drivers/net/ethernet/sun/sunvnet.h b/drivers/net/ethernet/sun/sunvnet.h
index de5c2c6..243ae69 100644
--- a/drivers/net/ethernet/sun/sunvnet.h
+++ b/drivers/net/ethernet/sun/sunvnet.h
@@ -11,6 +11,7 @@
*/
#define VNET_TX_TIMEOUT (5 * HZ)
+#define VNET_MAXPACKET 1518ULL /* ETH_FRAMELEN + VLAN_HDR */
#define VNET_TX_RING_SIZE 512
#define VNET_TX_WAKEUP_THRESH(dr) ((dr)->pending / 4)
--
1.7.1
^ permalink raw reply related
* [PATCHv2 net-next 0/3] sunvnet: add jumbo frames support
From: David L Stevens @ 2014-09-12 23:13 UTC (permalink / raw)
To: David Miller; +Cc: netdev
This patch set updates the sunvnet driver to version 1.6 of the VIO protocol
to support per-port exchange of MTU information and allow non-standard MTU
sizes, including jumbo frames.
Using large MTUs shows a > 4X throughput improvement Linux-Solaris
and > 8X throughput improvement Linux-Linux.
Changes from v1:
- fix brace formatting per Dave Miller <davem@davemloft.net>
David L Stevens (3):
sunvnet: upgrade to VIO protocol version 1.6
sunvnet: allow admin to set sunvnet MTU
sunvnet: generate ICMP PTMUD messages for smaller port MTUs
arch/sparc/include/asm/vio.h | 19 +++++-
arch/sparc/kernel/viohs.c | 14 +++-
drivers/net/ethernet/sun/sunvnet.c | 136 +++++++++++++++++++++++++++++++-----
drivers/net/ethernet/sun/sunvnet.h | 1 +
4 files changed, 149 insertions(+), 21 deletions(-)
^ permalink raw reply
* Re: [PATCH v11 net-next 00/12] eBPF syscall, verifier, testsuite
From: Alexei Starovoitov @ 2014-09-12 22:40 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Alexei Starovoitov, Daniel Borkmann, David S. Miller, Ingo Molnar,
Linus Torvalds, Steven Rostedt, Hannes Frederic Sowa,
Chema Gonzalez, Eric Dumazet, Peter Zijlstra, Pablo Neira Ayuso,
H. Peter Anvin, Andrew Morton, Kees Cook, Linux API,
Network Development, LKML
In-Reply-To: <CALCETrXqwd=dp31fckMPruQMwVw+UAjaf=SSWp8wr_Cdz_tQdw@mail.gmail.com>
Hi All,
the list of things I fixed so far from V11:
- dropped patch 11 and copied few macros to libbpf.h (suggested by Daniel)
- replaced 'enum bpf_prog_type' with u32 to be safe in compat (.. Andy)
- implemented and tested compat support (.. Daniel)
- changed 'void *log_buf' to 'char *' (.. Daniel)
- combined struct bpf_work_struct and bpf_prog_info (.. Daniel)
- added better return value explanation to manpage (.. Andy)
- added log_buf/log_size explanation to manpage (.. Andy & Daniel)
- added a lot more info about prog_type and map_type and
they relation to verifier (.. Andy)
anything else I missed from the discussion we had?
Here is updated manpage. Please take a look.
BPF(2) Linux Programmer's Manual BPF(2)
NAME
bpf - perform a command on eBPF map or program
SYNOPSIS
#include <linux/bpf.h>
int bpf(int cmd, union bpf_attr *attr, unsigned int size);
DESCRIPTION
bpf() syscall is a multiplexor for a range of different operations on
eBPF which can be characterized as "universal in-kernel virtual
machine". eBPF is similar to original Berkeley Packet Filter (or
"classic BPF") used to filter network packets. Both statically analyze
the programs before loading them into the kernel to ensure that
programs cannot harm the running system.
eBPF extends classic BPF in multiple ways including ability to call in-
kernel helper functions and access shared data structures like eBPF
maps. The programs can be written in a restricted C that is compiled
into eBPF bytecode and executed on the eBPF virtual machine or JITed
into native instruction set.
eBPF Design/Architecture
eBPF maps is a generic storage of different types. User process can
create multiple maps (with key/value being opaque bytes of data) and
access them via file descriptor. In parallel eBPF programs can access
maps from inside the kernel. It's up to user process and eBPF program
to decide what they store inside maps.
eBPF programs are similar to kernel modules. They are loaded by the
user process and automatically unloaded when process exits. Each eBPF
program is a safe run-to-completion set of instructions. eBPF verifier
statically determines that the program terminates and is safe to
execute. During verification the program takes a hold of maps that it
intends to use, so selected maps cannot be removed until the program is
unloaded. The program can be attached to different events. These events
can be packets, tracepoint events and other types in the future. A new
event triggers execution of the program which may store information
about the event in the maps. Beyond storing data the programs may call
into in-kernel helper functions which may, for example, dump stack, do
trace_printk or other forms of live kernel debugging. The same program
can be attached to multiple events. Different programs can access the
same map:
tracepoint tracepoint tracepoint sk_buff sk_buff
event A event B event C on eth0 on eth1
| | | | |
| | | | |
--> tracing <-- tracing socket socket
prog_1 prog_2 prog_3 prog_4
| | | |
|--- -----| |-------| map_3
map_1 map_2
Syscall Arguments
bpf() syscall operation is determined by cmd which can be one of the
following:
BPF_MAP_CREATE
Create a map with given type and attributes and return map FD
BPF_MAP_LOOKUP_ELEM
Lookup element by key in a given map and return its value
BPF_MAP_UPDATE_ELEM
Create or update element (key/value pair) in a given map
BPF_MAP_DELETE_ELEM
Lookup and delete element by key in a given map
BPF_MAP_GET_NEXT_KEY
Lookup element by key in a given map and return key of next
element
BPF_PROG_LOAD
Verify and load eBPF program
attr is a pointer to a union of type bpf_attr as defined below.
size is the size of the union.
union bpf_attr {
struct { /* anonymous struct used by BPF_MAP_CREATE command */
__u32 map_type;
__u32 key_size; /* size of key in bytes */
__u32 value_size; /* size of value in bytes */
__u32 max_entries; /* max number of entries in a map */
};
struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
int map_fd;
void *key;
union {
void *value;
void *next_key;
};
};
struct { /* anonymous struct used by BPF_PROG_LOAD command */
__u32 prog_type;
__u32 insn_cnt;
const struct bpf_insn *insns;
const char *license;
__u32 log_level; /* verbosity level of eBPF verifier */
__u32 log_size; /* size of user buffer */
char *log_buf; /* user supplied buffer */
};
};
eBPF maps
maps is a generic storage of different types for sharing data between
kernel and userspace.
Any map type has the following attributes:
. type
. max number of elements
. key size in bytes
. value size in bytes
The following wrapper functions demonstrate how this syscall can be
used to access the maps. The functions use the cmd argument to invoke
different operations.
BPF_MAP_CREATE
int bpf_create_map(enum bpf_map_type map_type, int key_size,
int value_size, int max_entries)
{
union bpf_attr attr = {
.map_type = map_type,
.key_size = key_size,
.value_size = value_size,
.max_entries = max_entries
};
return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
}
bpf() syscall creates a map of map_type type and given
attributes key_size, value_size, max_entries. On success it
returns process-local file descriptor. On error, -1 is returned
and errno is set to EINVAL or EPERM or ENOMEM.
The attributes key_size and value_size will be used by verifier
during program loading to check that program is calling
bpf_map_*_elem() helper functions with correctly initialized key
and that program doesn't access map element value beyond
specified value_size. For example, when map is created with
key_size = 8 and program does:
bpf_map_lookup_elem(map_fd, fp - 4)
such program will be rejected, since in-kernel helper function
bpf_map_lookup_elem(map_fd, void *key) expects to read 8 bytes
from 'key' pointer, but 'fp - 4' starting address will cause out
of bounds stack access.
Similarly, when map is created with value_size = 1 and program
does:
value = bpf_map_lookup_elem(...);
*(u32 *)value = 1;
such program will be rejected, since it accesses value pointer
beyond specified 1 byte value_size limit.
Currently only hash table map_type is supported:
enum bpf_map_type {
BPF_MAP_TYPE_UNSPEC,
BPF_MAP_TYPE_HASH,
};
map_type selects one of the available map implementations in
kernel. Regardless of type eBPF program accesses maps with the
same bpf_map_lookup_elem()/bpf_map_update_elem() helper
functions.
BPF_MAP_LOOKUP_ELEM
int bpf_lookup_elem(int fd, void *key, void *value)
{
union bpf_attr attr = {
.map_fd = fd,
.key = key,
.value = value,
};
return bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}
bpf() syscall looks up an element with given key in a map fd.
If element is found it returns zero and stores element's value
into value. If element is not found it returns -1 and sets
errno to ENOENT.
BPF_MAP_UPDATE_ELEM
int bpf_update_elem(int fd, void *key, void *value)
{
union bpf_attr attr = {
.map_fd = fd,
.key = key,
.value = value,
};
return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}
The call creates or updates element with given key/value in a
map fd. On success it returns zero. On error, -1 is returned
and errno is set to EINVAL or EPERM or ENOMEM or E2BIG. E2BIG
indicates that number of elements in the map reached max_entries
limit specified at map creation time.
BPF_MAP_DELETE_ELEM
int bpf_delete_elem(int fd, void *key)
{
union bpf_attr attr = {
.map_fd = fd,
.key = key,
};
return bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
}
The call deletes an element in a map fd with given key. Returns
zero on success. If element is not found it returns -1 and sets
errno to ENOENT.
BPF_MAP_GET_NEXT_KEY
int bpf_get_next_key(int fd, void *key, void *next_key)
{
union bpf_attr attr = {
.map_fd = fd,
.key = key,
.next_key = next_key,
};
return bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
}
The call looks up an element by key in a given map fd and
returns key of the next element into next_key pointer. If key is
not found, it return zero and returns key of the first element
into next_key. If key is the last element, it returns -1 and
sets errno to ENOENT. Other possible errno values are ENOMEM,
EFAULT, EPERM, EINVAL. This method can be used to iterate over
all elements of the map.
close(map_fd)
will delete the map map_fd. Exiting process will delete all
maps automatically.
eBPF programs
BPF_PROG_LOAD
This cmd is used to load eBPF program into the kernel.
char bpf_log_buf[LOG_BUF_SIZE];
int bpf_prog_load(enum bpf_prog_type prog_type,
const struct bpf_insn *insns, int insn_cnt,
const char *license)
{
union bpf_attr attr = {
.prog_type = prog_type,
.insns = insns,
.insn_cnt = insn_cnt,
.license = license,
.log_buf = bpf_log_buf,
.log_size = LOG_BUF_SIZE,
.log_level = 1,
};
return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
}
prog_type is one of the available program types:
enum bpf_prog_type {
BPF_PROG_TYPE_UNSPEC,
BPF_PROG_TYPE_SOCKET,
BPF_PROG_TYPE_TRACING,
};
By picking prog_type program author selects a set of helper
functions callable from eBPF program and corresponding format of
struct bpf_context (which is the data blob passed into the
program as the first argument). For example, the programs
loaded with prog_type = TYPE_TRACING may call bpf_printk()
helper, whereas TYPE_SOCKET programs may not. The set of
functions available to the programs under given type may
increase in the future.
Currently the set of functions for TYPE_TRACING is:
bpf_map_lookup_elem(map_fd, void *key) // lookup key in a map_fd
bpf_map_update_elem(map_fd, void *key, void *value) // update key/value
bpf_map_delete_elem(map_fd, void *key) // delete key in a map_fd
bpf_ktime_get_ns(void) // returns current ktime
bpf_printk(char *fmt, int fmt_size, ...) // prints into trace buffer
bpf_get_current(void) // return current task pointer
bpf_memcmp(void *ptr1, void *ptr2, int size) // non-faulting memcmp
bpf_fetch_ptr(void *ptr) // non-faulting load pointer from any address
bpf_fetch_u8(void *ptr) // non-faulting 1 byte load
bpf_fetch_u16(void *ptr) // other non-faulting loads
bpf_fetch_u32(void *ptr)
bpf_fetch_u64(void *ptr)
and bpf_context is defined as:
struct bpf_context {
/* argN fields match one to one to arguments passed to trace events */
u64 arg1, arg2, arg3, arg4, arg5, arg6;
/* return value from kretprobe event or from syscall_exit event */
u64 ret;
};
The set of helper functions for TYPE_SOCKET is TBD.
More program types may be added in the future. Like
BPF_PROG_TYPE_USER_TRACING for unprivileged programs.
insns array of "struct bpf_insn" instructions
insn_cnt number of instructions in the program
license license string, which must be GPL compatible to call
helper functions marked gpl_only
log_buf user supplied buffer that in-kernel verifier is using to
store verification log. Log is a multi-line string that should
be used by program author to understand how verifier came to
conclusion that program is unsafe. The format of the output can
change at any time as verifier evolves.
log_size size of user buffer. If size of the buffer is not large
enough to store all verifier messages, -1 is returned and errno
is set to ENOSPC.
log_level verbosity level of eBPF verifier, where zero means no
logs provided
close(prog_fd)
will unload eBPF program
The maps are accesible from programs and generally tie the two
together. Programs process various events (like tracepoint, kprobe,
packets) and store the data into maps. User space fetches data from
maps. Either the same or a different map may be used by user space as
configuration space to alter program behavior on the fly.
Events
Once an eBPF program is loaded, it can be attached to an event. Various
kernel subsystems have different ways to do so. For example:
setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd));
will attach the program prog_fd to socket sock which was received by
prior call to socket().
ioctl(event_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
will attach the program prog_fd to perf event event_fd which was
received by prior call to perf_event_open().
Another way to attach the program to a tracing event is:
event_fd = open("/sys/kernel/debug/tracing/events/skb/kfree_skb/filter");
write(event_fd, "bpf-123"); /* where 123 is eBPF program FD */
/* here program is attached and will be triggered by events */
close(event_fd); /* to detach from event */
EXAMPLES
/* eBPF+sockets example:
* 1. create map with maximum of 2 elements
* 2. set map[6] = 0 and map[17] = 0
* 3. load eBPF program that counts number of TCP and UDP packets received
* via map[skb->ip->proto]++
* 4. attach prog_fd to raw socket via setsockopt()
* 5. print number of received TCP/UDP packets every second
*/
int main(int ac, char **av)
{
int sock, map_fd, prog_fd, key;
long long value = 0, tcp_cnt, udp_cnt;
map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), 2);
if (map_fd < 0) {
printf("failed to create map '%s'\n", strerror(errno));
/* likely not run as root */
return 1;
}
key = 6; /* ip->proto == tcp */
assert(bpf_update_elem(map_fd, &key, &value) == 0);
key = 17; /* ip->proto == udp */
assert(bpf_update_elem(map_fd, &key, &value) == 0);
struct bpf_insn prog[] = {
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), /* r6 = r1 */
BPF_LD_ABS(BPF_B, 14 + 9), /* r0 = ip->proto */
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4),/* *(u32 *)(fp - 4) = r0 */
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), /* r2 = fp */
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = r2 - 4 */
BPF_LD_MAP_FD(BPF_REG_1, map_fd), /* r1 = map_fd */
BPF_CALL_FUNC(BPF_FUNC_map_lookup_elem), /* r0 = map_lookup(r1, r2) */
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), /* if (r0 == 0) goto pc+2 */
BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */
BPF_XADD(BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* lock *(u64 *)r0 += r1 */
BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 */
BPF_EXIT_INSN(), /* return r0 */
};
prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET, prog, sizeof(prog), "GPL");
assert(prog_fd >= 0);
sock = open_raw_sock("lo");
assert(setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd,
sizeof(prog_fd)) == 0);
for (;;) {
key = 6;
assert(bpf_lookup_elem(map_fd, &key, &tcp_cnt) == 0);
key = 17;
assert(bpf_lookup_elem(map_fd, &key, &udp_cnt) == 0);
printf("TCP %lld UDP %lld packets0, tcp_cnt, udp_cnt);
sleep(1);
}
return 0;
}
RETURN VALUE
For a successful call, the return value depends on the operation:
BPF_MAP_CREATE
The new file descriptor associated with eBPF map.
BPF_PROG_LOAD
The new file descriptor associated with eBPF program.
All other commands
Zero.
On error, -1 is returned, and errno is set appropriately.
ERRORS
EPERM bpf() syscall was made without sufficient privilege (without the
CAP_SYS_ADMIN capability).
ENOMEM Cannot allocate sufficient memory.
EBADF fd is not an open file descriptor
EFAULT One of the pointers ( key or value or log_buf or insns ) is
outside accessible address space.
EINVAL The value specified in cmd is not recognized by this kernel.
EINVAL For BPF_MAP_CREATE, either map_type or attributes are invalid.
EINVAL For BPF_MAP_*_ELEM commands, some of the fields of "union
bpf_attr" unused by this command are not set to zero.
EINVAL For BPF_PROG_LOAD, attempt to load invalid program (unrecognized
instruction or uses reserved fields or jumps out of range or
loop detected or calls unknown function).
EACCES For BPF_PROG_LOAD, though program has valid instructions, it was
rejected, since it was deemed unsafe (may access disallowed
memory region or uninitialized stack/register or function
constraints don't match actual types or misaligned access). In
such case it is recommended to call bpf() again with log_level =
1 and examine log_buf for specific reason provided by verifier.
ENOENT For BPF_MAP_LOOKUP_ELEM or BPF_MAP_DELETE_ELEM, indicates that
element with given key was not found.
E2BIG program is too large or a map reached max_entries limit (max
number of elements).
NOTES
These commands may be used only by a privileged process (one having the
CAP_SYS_ADMIN capability).
SEE ALSO
eBPF architecture and instruction set is explained in
Documentation/networking/filter.txt
Linux 2014-09-12 BPF(2)
^ permalink raw reply
* Re: [PATCH net-next 3/3] sunvnet: generate ICMP PTMUD messages for smaller port MTUs
From: David Miller @ 2014-09-12 22:26 UTC (permalink / raw)
To: david.stevens; +Cc: netdev
In-Reply-To: <5413723C.7040607@oracle.com>
From: David L Stevens <david.stevens@oracle.com>
Date: Fri, 12 Sep 2014 18:22:52 -0400
> + if (skb->protocol == htons(ETH_P_IP))
> + {
Openning braces should be on the same line as the if() statement.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox