* Re: [PATCH net-next] neigh: increase queue_len_bytes to match wmem_default
From: David Miller @ 2017-08-29 23:17 UTC (permalink / raw)
To: eric.dumazet; +Cc: netdev
In-Reply-To: <1504048528.11498.109.camel@edumazet-glaptop3.roam.corp.google.com>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Tue, 29 Aug 2017 16:15:28 -0700
> On Tue, 2017-08-29 at 15:16 -0700, Eric Dumazet wrote:
>> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
>> index
>> 568ccfd6dd371d88136ffabe5cfcc36f099786b6..7616cd76f6f6a62f395da897baef2c66c0098193 100644
>> --- a/net/ipv4/tcp_input.c
>> +++ b/net/ipv4/tcp_input.c
>> @@ -6086,9 +6086,9 @@ int tcp_conn_request(struct request_sock_ops
>> *rsk_ops,
>> struct tcp_sock *tp = tcp_sk(sk);
>> struct net *net = sock_net(sk);
>> struct sock *fastopen_sk = NULL;
>> - struct dst_entry *dst = NULL;
>> struct request_sock *req;
>> bool want_cookie = false;
>> + struct dst_entry *dst;
>> struct flowi fl;
>>
>> /* TW buckets are converted to open req
>
> This part was meant to belong to a separate patch :/
>
> No big deal, this was also one of your suggestion David.
Yeah, no big deal. But thanks for pointing it out.
^ permalink raw reply
* Re: [PATCH net-next] neigh: increase queue_len_bytes to match wmem_default
From: Eric Dumazet @ 2017-08-29 23:15 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <1504044961.11498.100.camel@edumazet-glaptop3.roam.corp.google.com>
On Tue, 2017-08-29 at 15:16 -0700, Eric Dumazet wrote:
> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
> index
> 568ccfd6dd371d88136ffabe5cfcc36f099786b6..7616cd76f6f6a62f395da897baef2c66c0098193 100644
> --- a/net/ipv4/tcp_input.c
> +++ b/net/ipv4/tcp_input.c
> @@ -6086,9 +6086,9 @@ int tcp_conn_request(struct request_sock_ops
> *rsk_ops,
> struct tcp_sock *tp = tcp_sk(sk);
> struct net *net = sock_net(sk);
> struct sock *fastopen_sk = NULL;
> - struct dst_entry *dst = NULL;
> struct request_sock *req;
> bool want_cookie = false;
> + struct dst_entry *dst;
> struct flowi fl;
>
> /* TW buckets are converted to open req
This part was meant to belong to a separate patch :/
No big deal, this was also one of your suggestion David.
^ permalink raw reply
* [PATCH v2 net-next 0/6] flow_dissector: Protocol specific flow dissector offload
From: Tom Herbert @ 2017-08-29 23:27 UTC (permalink / raw)
To: davem; +Cc: netdev, Tom Herbert
This patch set adds a new offload type to perform flow dissection for
specific protocols (either by EtherType or by IP protocol). This is
primary useful to crack open UDP encapsulations (like VXLAN, GUE) for
the purposes of parsing the encapsulated packet.
Items in this patch set:
- Constify skb argument to UDP lookup functions
- Create new protocol case in __skb_dissect for ETH_P_TEB. This is based
on the code in the GRE dissect function and the special handling in
GRE can now be removed (it sets protocol to ETH_P_TEB and returns so
goto proto_again is done)
- Add infrastructure for protocol specific flow dissection offload
- Add infrastructure to perform UDP flow dissection. Uses same model of
GRO where a flow_dissect callback can be associated with a UDP
socket
- Use the infrastructure to support flow dissection of VXLAN and GUE
Tested:
Forced RPS to call flow dissection for VXLAN, FOU, and GUE. Observed
that inner packet was being properly dissected.
v2: Add signed off
Tom Herbert (6):
flow_dissector: Move ETH_P_TEB processing to main switch
udp: Constify skb argument in lookup functions
flow_dissector: Add protocol specific flow dissection offload
udp: flow dissector offload
fou: Support flow dissection
vxlan: support flow dissect
drivers/net/vxlan.c | 50 ++++++++++++
include/linux/netdevice.h | 7 ++
include/linux/udp.h | 8 ++
include/net/flow_dissector.h | 9 +++
include/net/ip.h | 2 +-
include/net/sock_reuseport.h | 2 +-
include/net/udp.h | 19 +++--
include/net/udp_tunnel.h | 8 ++
net/core/dev.c | 14 ++++
net/core/flow_dissector.c | 176 +++++++++++++++++++++++++++++--------------
net/core/sock_reuseport.c | 5 +-
net/ipv4/fou.c | 63 ++++++++++++++++
net/ipv4/route.c | 4 +-
net/ipv4/udp.c | 11 +--
net/ipv4/udp_offload.c | 45 +++++++++++
net/ipv4/udp_tunnel.c | 1 +
net/ipv6/udp.c | 10 +--
net/ipv6/udp_offload.c | 13 ++++
18 files changed, 369 insertions(+), 78 deletions(-)
--
2.11.0
^ permalink raw reply
* [PATCH v2 net-next 1/6] flow_dissector: Move ETH_P_TEB processing to main switch
From: Tom Herbert @ 2017-08-29 23:27 UTC (permalink / raw)
To: davem; +Cc: netdev, Tom Herbert
In-Reply-To: <20170829232711.1465-1-tom@quantonium.net>
Support for processing TEB is currently in GRE flow dissection as a
special case. This can be moved to be a case the main proto switch in
__skb_flow_dissect.
Signed-off-by: Tom Herbert <tom@quantonium.net>
---
net/core/flow_dissector.c | 44 +++++++++++++++++++++++---------------------
1 file changed, 23 insertions(+), 21 deletions(-)
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index e2eaa1ff948d..12302acdb073 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -288,27 +288,8 @@ __skb_flow_dissect_gre(const struct sk_buff *skb,
if (hdr->flags & GRE_SEQ)
offset += sizeof(((struct pptp_gre_header *) 0)->seq);
- if (gre_ver == 0) {
- if (*p_proto == htons(ETH_P_TEB)) {
- const struct ethhdr *eth;
- struct ethhdr _eth;
-
- eth = __skb_header_pointer(skb, *p_nhoff + offset,
- sizeof(_eth),
- data, *p_hlen, &_eth);
- if (!eth)
- return FLOW_DISSECT_RET_OUT_BAD;
- *p_proto = eth->h_proto;
- offset += sizeof(*eth);
-
- /* Cap headers that we access via pointers at the
- * end of the Ethernet header as our maximum alignment
- * at that point is only 2 bytes.
- */
- if (NET_IP_ALIGN)
- *p_hlen = *p_nhoff + offset;
- }
- } else { /* version 1, must be PPTP */
+ /* version 1, must be PPTP */
+ if (gre_ver == 1) {
u8 _ppp_hdr[PPP_HDRLEN];
u8 *ppp_hdr;
@@ -573,6 +554,27 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
break;
}
+ case htons(ETH_P_TEB): {
+ const struct ethhdr *eth;
+ struct ethhdr _eth;
+
+ eth = __skb_header_pointer(skb, nhoff, sizeof(_eth),
+ data, hlen, &_eth);
+ if (!eth)
+ goto out_bad;
+
+ proto = eth->h_proto;
+ nhoff += sizeof(*eth);
+
+ /* Cap headers that we access via pointers at the
+ * end of the Ethernet header as our maximum alignment
+ * at that point is only 2 bytes.
+ */
+ if (NET_IP_ALIGN)
+ hlen = nhoff;
+
+ goto proto_again;
+ }
case htons(ETH_P_8021AD):
case htons(ETH_P_8021Q): {
const struct vlan_hdr *vlan;
--
2.11.0
^ permalink raw reply related
* [PATCH v2 net-next 2/6] udp: Constify skb argument in lookup functions
From: Tom Herbert @ 2017-08-29 23:27 UTC (permalink / raw)
To: davem; +Cc: netdev, Tom Herbert
In-Reply-To: <20170829232711.1465-1-tom@quantonium.net>
For UDP socket lookup functions, and associateed functions that take an
skbuf as argument, declare the skb argument as constant.
One caveat is that reuseport_select_sock can be called from the UDP
lookup functions with an skb argument. This function temporarily
modifies the skbuff data pointer (in bpf_run via a pull/push sequence).
To resolve compiler warning I added a local skbuf declaration that is
not const and assigned to the skb argument with an explicit cast.
Signed-off-by: Tom Herbert <tom@quantonium.net>
---
include/net/ip.h | 2 +-
include/net/sock_reuseport.h | 2 +-
include/net/udp.h | 11 ++++++-----
net/core/sock_reuseport.c | 5 +++--
net/ipv4/udp.c | 11 ++++++-----
net/ipv6/udp.c | 10 +++++-----
6 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/include/net/ip.h b/include/net/ip.h
index 9896f46cbbf1..8c0d84ffc659 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -79,7 +79,7 @@ struct ipcm_cookie {
#define PKTINFO_SKB_CB(skb) ((struct in_pktinfo *)((skb)->cb))
/* return enslaved device index if relevant */
-static inline int inet_sdif(struct sk_buff *skb)
+static inline int inet_sdif(const struct sk_buff *skb)
{
#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
if (skb && ipv4_l3mdev_skb(IPCB(skb)->flags))
diff --git a/include/net/sock_reuseport.h b/include/net/sock_reuseport.h
index aecd30308d50..d25352a848d9 100644
--- a/include/net/sock_reuseport.h
+++ b/include/net/sock_reuseport.h
@@ -20,7 +20,7 @@ extern int reuseport_add_sock(struct sock *sk, struct sock *sk2);
extern void reuseport_detach_sock(struct sock *sk);
extern struct sock *reuseport_select_sock(struct sock *sk,
u32 hash,
- struct sk_buff *skb,
+ const struct sk_buff *skb,
int hdr_len);
extern struct bpf_prog *reuseport_attach_prog(struct sock *sk,
struct bpf_prog *prog);
diff --git a/include/net/udp.h b/include/net/udp.h
index 4e5f23fec35e..f3d1de6f0983 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -167,7 +167,7 @@ static inline void udp_csum_pull_header(struct sk_buff *skb)
UDP_SKB_CB(skb)->cscov -= sizeof(struct udphdr);
}
-typedef struct sock *(*udp_lookup_t)(struct sk_buff *skb, __be16 sport,
+typedef struct sock *(*udp_lookup_t)(const struct sk_buff *skb, __be16 sport,
__be16 dport);
struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
@@ -288,8 +288,9 @@ struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
__be32 daddr, __be16 dport, int dif);
struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
__be32 daddr, __be16 dport, int dif, int sdif,
- struct udp_table *tbl, struct sk_buff *skb);
-struct sock *udp4_lib_lookup_skb(struct sk_buff *skb,
+ struct udp_table *tbl,
+ const struct sk_buff *skb);
+struct sock *udp4_lib_lookup_skb(const struct sk_buff *skb,
__be16 sport, __be16 dport);
struct sock *udp6_lib_lookup(struct net *net,
const struct in6_addr *saddr, __be16 sport,
@@ -299,8 +300,8 @@ struct sock *__udp6_lib_lookup(struct net *net,
const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport,
int dif, int sdif, struct udp_table *tbl,
- struct sk_buff *skb);
-struct sock *udp6_lib_lookup_skb(struct sk_buff *skb,
+ const struct sk_buff *skb);
+struct sock *udp6_lib_lookup_skb(const struct sk_buff *skb,
__be16 sport, __be16 dport);
/* UDP uses skb->dev_scratch to cache as much information as possible and avoid
diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c
index eed1ebf7f29d..a17f13b33189 100644
--- a/net/core/sock_reuseport.c
+++ b/net/core/sock_reuseport.c
@@ -164,9 +164,10 @@ void reuseport_detach_sock(struct sock *sk)
EXPORT_SYMBOL(reuseport_detach_sock);
static struct sock *run_bpf(struct sock_reuseport *reuse, u16 socks,
- struct bpf_prog *prog, struct sk_buff *skb,
+ struct bpf_prog *prog, const struct sk_buff *_skb,
int hdr_len)
{
+ struct sk_buff *skb = (struct sk_buff *)_skb; /* Override const */
struct sk_buff *nskb = NULL;
u32 index;
@@ -205,7 +206,7 @@ static struct sock *run_bpf(struct sock_reuseport *reuse, u16 socks,
*/
struct sock *reuseport_select_sock(struct sock *sk,
u32 hash,
- struct sk_buff *skb,
+ const struct sk_buff *skb,
int hdr_len)
{
struct sock_reuseport *reuse;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index bf6c406bf5e7..a851026ef28b 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -135,7 +135,8 @@ EXPORT_SYMBOL(udp_memory_allocated);
#define PORTS_PER_CHAIN (MAX_UDP_PORTS / UDP_HTABLE_SIZE_MIN)
/* IPCB reference means this can not be used from early demux */
-static bool udp_lib_exact_dif_match(struct net *net, struct sk_buff *skb)
+static bool udp_lib_exact_dif_match(struct net *net,
+ const struct sk_buff *skb)
{
#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
if (!net->ipv4.sysctl_udp_l3mdev_accept &&
@@ -445,7 +446,7 @@ static struct sock *udp4_lib_lookup2(struct net *net,
__be32 daddr, unsigned int hnum,
int dif, int sdif, bool exact_dif,
struct udp_hslot *hslot2,
- struct sk_buff *skb)
+ const struct sk_buff *skb)
{
struct sock *sk, *result;
int score, badness, matches = 0, reuseport = 0;
@@ -484,7 +485,7 @@ static struct sock *udp4_lib_lookup2(struct net *net,
*/
struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
__be16 sport, __be32 daddr, __be16 dport, int dif,
- int sdif, struct udp_table *udptable, struct sk_buff *skb)
+ int sdif, struct udp_table *udptable, const struct sk_buff *skb)
{
struct sock *sk, *result;
unsigned short hnum = ntohs(dport);
@@ -552,7 +553,7 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
}
EXPORT_SYMBOL_GPL(__udp4_lib_lookup);
-static inline struct sock *__udp4_lib_lookup_skb(struct sk_buff *skb,
+static inline struct sock *__udp4_lib_lookup_skb(const struct sk_buff *skb,
__be16 sport, __be16 dport,
struct udp_table *udptable)
{
@@ -563,7 +564,7 @@ static inline struct sock *__udp4_lib_lookup_skb(struct sk_buff *skb,
inet_sdif(skb), udptable, skb);
}
-struct sock *udp4_lib_lookup_skb(struct sk_buff *skb,
+struct sock *udp4_lib_lookup_skb(const struct sk_buff *skb,
__be16 sport, __be16 dport)
{
return __udp4_lib_lookup_skb(skb, sport, dport, &udp_table);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 976f30391356..e9aa4db3ba53 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -56,7 +56,7 @@
#include <trace/events/skb.h>
#include "udp_impl.h"
-static bool udp6_lib_exact_dif_match(struct net *net, struct sk_buff *skb)
+static bool udp6_lib_exact_dif_match(struct net *net, const struct sk_buff *skb)
{
#if defined(CONFIG_NET_L3_MASTER_DEV)
if (!net->ipv4.sysctl_udp_l3mdev_accept &&
@@ -181,7 +181,7 @@ static struct sock *udp6_lib_lookup2(struct net *net,
const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, unsigned int hnum,
int dif, int sdif, bool exact_dif,
- struct udp_hslot *hslot2, struct sk_buff *skb)
+ struct udp_hslot *hslot2, const struct sk_buff *skb)
{
struct sock *sk, *result;
int score, badness, matches = 0, reuseport = 0;
@@ -221,7 +221,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport,
int dif, int sdif, struct udp_table *udptable,
- struct sk_buff *skb)
+ const struct sk_buff *skb)
{
struct sock *sk, *result;
unsigned short hnum = ntohs(dport);
@@ -290,7 +290,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
}
EXPORT_SYMBOL_GPL(__udp6_lib_lookup);
-static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb,
+static struct sock *__udp6_lib_lookup_skb(const struct sk_buff *skb,
__be16 sport, __be16 dport,
struct udp_table *udptable)
{
@@ -301,7 +301,7 @@ static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb,
inet6_sdif(skb), udptable, skb);
}
-struct sock *udp6_lib_lookup_skb(struct sk_buff *skb,
+struct sock *udp6_lib_lookup_skb(const struct sk_buff *skb,
__be16 sport, __be16 dport)
{
const struct ipv6hdr *iph = ipv6_hdr(skb);
--
2.11.0
^ permalink raw reply related
* [PATCH v2 net-next 3/6] flow_dissector: Add protocol specific flow dissection offload
From: Tom Herbert @ 2017-08-29 23:27 UTC (permalink / raw)
To: davem; +Cc: netdev, Tom Herbert
In-Reply-To: <20170829232711.1465-1-tom@quantonium.net>
Add offload capability for performing protocol specific flow dissection
(either by EtherType or IP protocol).
Specifically:
- Add flow_dissect to offload callbacks
- Move flow_dissect_ret enum to flow_dissector.h, cleanup names and add a
couple of values
- Create GOTO_BY_RESULT macro to use in the main flow dissector switch to
simplify handling of functions that return flow_dissect_ret enum
- In __skb_flow_dissect, add default case for switch(proto) as well as
switch(ip_proto) that looks up and calls protocol specific flow
dissection
Signed-off-by: Tom Herbert <tom@quantonium.net>
---
include/linux/netdevice.h | 7 +++
include/net/flow_dissector.h | 9 +++
net/core/dev.c | 14 +++++
net/core/flow_dissector.c | 132 +++++++++++++++++++++++++++++++------------
net/ipv4/route.c | 4 +-
5 files changed, 128 insertions(+), 38 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index c5475b37a631..90ccb434e127 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2208,6 +2208,12 @@ struct offload_callbacks {
struct sk_buff **(*gro_receive)(struct sk_buff **head,
struct sk_buff *skb);
int (*gro_complete)(struct sk_buff *skb, int nhoff);
+ enum flow_dissect_ret (*flow_dissect)(const struct sk_buff *skb,
+ struct flow_dissector_key_control *key_control,
+ struct flow_dissector *flow_dissector,
+ void *target_container, void *data,
+ __be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
+ int *p_hlen, unsigned int flags);
};
struct packet_offload {
@@ -3253,6 +3259,7 @@ struct sk_buff *napi_get_frags(struct napi_struct *napi);
gro_result_t napi_gro_frags(struct napi_struct *napi);
struct packet_offload *gro_find_receive_by_type(__be16 type);
struct packet_offload *gro_find_complete_by_type(__be16 type);
+struct packet_offload *flow_dissect_find_by_type(__be16 type);
static inline void napi_free_frags(struct napi_struct *napi)
{
diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h
index e2663e900b0a..ad75bbfd1c9c 100644
--- a/include/net/flow_dissector.h
+++ b/include/net/flow_dissector.h
@@ -19,6 +19,14 @@ struct flow_dissector_key_control {
#define FLOW_DIS_FIRST_FRAG BIT(1)
#define FLOW_DIS_ENCAPSULATION BIT(2)
+enum flow_dissect_ret {
+ FLOW_DISSECT_RET_OUT_GOOD,
+ FLOW_DISSECT_RET_OUT_BAD,
+ FLOW_DISSECT_RET_PROTO_AGAIN,
+ FLOW_DISSECT_RET_IPPROTO_AGAIN,
+ FLOW_DISSECT_RET_CONTINUE,
+};
+
/**
* struct flow_dissector_key_basic:
* @thoff: Transport header offset
@@ -205,6 +213,7 @@ enum flow_dissector_key_id {
#define FLOW_DISSECTOR_F_STOP_AT_L3 BIT(1)
#define FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL BIT(2)
#define FLOW_DISSECTOR_F_STOP_AT_ENCAP BIT(3)
+#define FLOW_DISSECTOR_F_STOP_AT_L4 BIT(4)
struct flow_dissector_key {
enum flow_dissector_key_id key_id;
diff --git a/net/core/dev.c b/net/core/dev.c
index 270b54754821..22ea8daa930c 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4860,6 +4860,20 @@ struct packet_offload *gro_find_receive_by_type(__be16 type)
}
EXPORT_SYMBOL(gro_find_receive_by_type);
+struct packet_offload *flow_dissect_find_by_type(__be16 type)
+{
+ struct list_head *offload_head = &offload_base;
+ struct packet_offload *ptype;
+
+ list_for_each_entry_rcu(ptype, offload_head, list) {
+ if (ptype->type != type || !ptype->callbacks.flow_dissect)
+ continue;
+ return ptype;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(flow_dissect_find_by_type);
+
struct packet_offload *gro_find_complete_by_type(__be16 type)
{
struct list_head *offload_head = &offload_base;
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 12302acdb073..6a2cf240069a 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -9,6 +9,7 @@
#include <net/ipv6.h>
#include <net/gre.h>
#include <net/pptp.h>
+#include <net/protocol.h>
#include <linux/igmp.h>
#include <linux/icmp.h>
#include <linux/sctp.h>
@@ -115,12 +116,6 @@ __be32 __skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto,
}
EXPORT_SYMBOL(__skb_flow_get_ports);
-enum flow_dissect_ret {
- FLOW_DISSECT_RET_OUT_GOOD,
- FLOW_DISSECT_RET_OUT_BAD,
- FLOW_DISSECT_RET_OUT_PROTO_AGAIN,
-};
-
static enum flow_dissect_ret
__skb_flow_dissect_mpls(const struct sk_buff *skb,
struct flow_dissector *flow_dissector,
@@ -322,7 +317,7 @@ __skb_flow_dissect_gre(const struct sk_buff *skb,
if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP)
return FLOW_DISSECT_RET_OUT_GOOD;
- return FLOW_DISSECT_RET_OUT_PROTO_AGAIN;
+ return FLOW_DISSECT_RET_PROTO_AGAIN;
}
static void
@@ -383,6 +378,27 @@ __skb_flow_dissect_ipv6(const struct sk_buff *skb,
key_ip->ttl = iph->hop_limit;
}
+#define GOTO_BY_RESULT(ret) do { \
+ switch (ret) { \
+ case FLOW_DISSECT_RET_OUT_GOOD: \
+ goto out_good; \
+ case FLOW_DISSECT_RET_PROTO_AGAIN: \
+ goto proto_again; \
+ case FLOW_DISSECT_RET_IPPROTO_AGAIN: \
+ goto ip_proto_again; \
+ case FLOW_DISSECT_RET_OUT_BAD: \
+ default: \
+ goto out_bad; \
+ } \
+} while (0)
+
+#define GOTO_OR_CONT_BY_RESULT(ret) do { \
+ enum flow_dissect_ret __ret = (ret); \
+ \
+ if (__ret != FLOW_DISSECT_RET_CONTINUE) \
+ GOTO_BY_RESULT(__ret); \
+} while (0)
+
/**
* __skb_flow_dissect - extract the flow_keys struct and return it
* @skb: sk_buff to extract the flow from, can be NULL if the rest are specified
@@ -659,15 +675,10 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
case htons(ETH_P_MPLS_UC):
case htons(ETH_P_MPLS_MC):
mpls:
- switch (__skb_flow_dissect_mpls(skb, flow_dissector,
- target_container, data,
- nhoff, hlen)) {
- case FLOW_DISSECT_RET_OUT_GOOD:
- goto out_good;
- case FLOW_DISSECT_RET_OUT_BAD:
- default:
- goto out_bad;
- }
+ GOTO_BY_RESULT(__skb_flow_dissect_mpls(skb, flow_dissector,
+ target_container, data,
+ nhoff, hlen));
+
case htons(ETH_P_FCOE):
if ((hlen - nhoff) < FCOE_HEADER_LEN)
goto out_bad;
@@ -677,32 +688,44 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
case htons(ETH_P_ARP):
case htons(ETH_P_RARP):
- switch (__skb_flow_dissect_arp(skb, flow_dissector,
- target_container, data,
- nhoff, hlen)) {
- case FLOW_DISSECT_RET_OUT_GOOD:
- goto out_good;
- case FLOW_DISSECT_RET_OUT_BAD:
- default:
- goto out_bad;
+ GOTO_BY_RESULT(__skb_flow_dissect_arp(skb, flow_dissector,
+ target_container, data,
+ nhoff, hlen));
+
+ default: {
+ struct packet_offload *ptype;
+ enum flow_dissect_ret ret;
+
+ rcu_read_lock();
+
+ ptype = flow_dissect_find_by_type(proto);
+
+ if (ptype) {
+ ret = ptype->callbacks.flow_dissect(skb, key_control,
+ flow_dissector,
+ target_container,
+ data, &proto, &ip_proto, &nhoff,
+ &hlen, flags);
+ rcu_read_unlock();
+
+ GOTO_BY_RESULT(ret);
+ } else {
+ rcu_read_unlock();
}
- default:
+
goto out_bad;
}
+ }
ip_proto_again:
switch (ip_proto) {
case IPPROTO_GRE:
- switch (__skb_flow_dissect_gre(skb, key_control, flow_dissector,
- target_container, data,
- &proto, &nhoff, &hlen, flags)) {
- case FLOW_DISSECT_RET_OUT_GOOD:
- goto out_good;
- case FLOW_DISSECT_RET_OUT_BAD:
- goto out_bad;
- case FLOW_DISSECT_RET_OUT_PROTO_AGAIN:
- goto proto_again;
- }
+ GOTO_BY_RESULT(__skb_flow_dissect_gre(skb, key_control,
+ flow_dissector,
+ target_container, data,
+ &proto, &nhoff, &hlen,
+ flags));
+
case NEXTHDR_HOP:
case NEXTHDR_ROUTING:
case NEXTHDR_DEST: {
@@ -768,9 +791,43 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
__skb_flow_dissect_tcp(skb, flow_dissector, target_container,
data, nhoff, hlen);
break;
- default:
+ default: {
+ const struct net_offload *ops = NULL;
+
+ if (flags & FLOW_DISSECTOR_F_STOP_AT_L4)
+ break;
+
+ rcu_read_lock();
+
+ switch (proto) {
+ case htons(ETH_P_IP):
+ ops = rcu_dereference(inet_offloads[ip_proto]);
+ break;
+ case htons(ETH_P_IPV6):
+ ops = rcu_dereference(inet6_offloads[ip_proto]);
+ break;
+ default:
+ break;
+ }
+
+ if (ops && ops->callbacks.flow_dissect) {
+ enum flow_dissect_ret ret;
+
+ ret = ops->callbacks.flow_dissect(skb, key_control,
+ flow_dissector,
+ target_container,
+ data, &proto, &ip_proto, &nhoff,
+ &hlen, flags);
+ rcu_read_unlock();
+
+ GOTO_OR_CONT_BY_RESULT(ret);
+ } else {
+ rcu_read_unlock();
+ }
+
break;
}
+ }
if (dissector_uses_key(flow_dissector,
FLOW_DISSECTOR_KEY_PORTS)) {
@@ -935,7 +992,8 @@ static inline u32 ___skb_get_hash(const struct sk_buff *skb,
struct flow_keys *keys, u32 keyval)
{
skb_flow_dissect_flow_keys(skb, keys,
- FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
+ FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL |
+ FLOW_DISSECTOR_F_STOP_AT_L4);
return __flow_hash_from_keys(keys, keyval);
}
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 94d4cd2d5ea4..85f12b8e0b7f 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1811,7 +1811,9 @@ int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
case 1:
/* skb is currently provided only when forwarding */
if (skb) {
- unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
+ unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP |
+ FLOW_DISSECTOR_F_STOP_AT_L4;
+;
struct flow_keys keys;
/* short-circuit if we already have L4 hash present */
--
2.11.0
^ permalink raw reply related
* [PATCH v2 net-next 4/6] udp: flow dissector offload
From: Tom Herbert @ 2017-08-29 23:27 UTC (permalink / raw)
To: davem; +Cc: netdev, Tom Herbert
In-Reply-To: <20170829232711.1465-1-tom@quantonium.net>
Add support to perform UDP specific flow dissection. This is
primarily intended for dissecting encapsulated packets in UDP
encapsulation.
This patch adds a flow_dissect offload for UDP4 and UDP6. The backend
function performs a socket lookup and calls the flow_dissect function
if a socket is found.
Signed-off-by: Tom Herbert <tom@quantonium.net>
---
include/linux/udp.h | 8 ++++++++
include/net/udp.h | 8 ++++++++
include/net/udp_tunnel.h | 8 ++++++++
net/ipv4/udp_offload.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
net/ipv4/udp_tunnel.c | 1 +
net/ipv6/udp_offload.c | 13 +++++++++++++
6 files changed, 83 insertions(+)
diff --git a/include/linux/udp.h b/include/linux/udp.h
index eaea63bc79bb..2e90b189ef6a 100644
--- a/include/linux/udp.h
+++ b/include/linux/udp.h
@@ -79,6 +79,14 @@ struct udp_sock {
int (*gro_complete)(struct sock *sk,
struct sk_buff *skb,
int nhoff);
+ /* Flow dissector function for a UDP socket */
+ enum flow_dissect_ret (*flow_dissect)(struct sock *sk,
+ const struct sk_buff *skb,
+ struct flow_dissector_key_control *key_control,
+ struct flow_dissector *flow_dissector,
+ void *target_container, void *data,
+ __be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
+ int *p_hlen, unsigned int flags);
/* udp_recvmsg try to use this before splicing sk_receive_queue */
struct sk_buff_head reader_queue ____cacheline_aligned_in_smp;
diff --git a/include/net/udp.h b/include/net/udp.h
index f3d1de6f0983..499e4faf8b14 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -174,6 +174,14 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
struct udphdr *uh, udp_lookup_t lookup);
int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup);
+enum flow_dissect_ret udp_flow_dissect(const struct sk_buff *skb,
+ udp_lookup_t lookup,
+ struct flow_dissector_key_control *key_control,
+ struct flow_dissector *flow_dissector,
+ void *target_container, void *data,
+ __be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
+ int *p_hlen, unsigned int flags);
+
static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb)
{
struct udphdr *uh;
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index 10cce0dd4450..b7102e0f41a9 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -69,6 +69,13 @@ typedef struct sk_buff **(*udp_tunnel_gro_receive_t)(struct sock *sk,
struct sk_buff *skb);
typedef int (*udp_tunnel_gro_complete_t)(struct sock *sk, struct sk_buff *skb,
int nhoff);
+typedef enum flow_dissect_ret (*udp_tunnel_flow_dissect_t)(struct sock *sk,
+ const struct sk_buff *skb,
+ struct flow_dissector_key_control *key_control,
+ struct flow_dissector *flow_dissector,
+ void *target_container, void *data,
+ __be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
+ int *p_hlen, unsigned int flags);
struct udp_tunnel_sock_cfg {
void *sk_user_data; /* user data used by encap_rcv call back */
@@ -78,6 +85,7 @@ struct udp_tunnel_sock_cfg {
udp_tunnel_encap_destroy_t encap_destroy;
udp_tunnel_gro_receive_t gro_receive;
udp_tunnel_gro_complete_t gro_complete;
+ udp_tunnel_flow_dissect_t flow_dissect;
};
/* Setup the given (UDP) sock to receive UDP encapsulated packets */
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 97658bfc1b58..7f0a7ed4a6f7 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -328,11 +328,56 @@ static int udp4_gro_complete(struct sk_buff *skb, int nhoff)
return udp_gro_complete(skb, nhoff, udp4_lib_lookup_skb);
}
+enum flow_dissect_ret udp_flow_dissect(const struct sk_buff *skb,
+ udp_lookup_t lookup,
+ struct flow_dissector_key_control *key_control,
+ struct flow_dissector *flow_dissector,
+ void *target_container, void *data,
+ __be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
+ int *p_hlen, unsigned int flags)
+{
+ enum flow_dissect_ret ret = FLOW_DISSECT_RET_CONTINUE;
+ struct udphdr *uh, _uh;
+ struct sock *sk;
+
+ uh = __skb_header_pointer(skb, *p_nhoff, sizeof(_uh), data,
+ *p_hlen, &_uh);
+ if (!uh)
+ return FLOW_DISSECT_RET_OUT_BAD;
+
+ rcu_read_lock();
+
+ sk = (*lookup)(skb, uh->source, uh->dest);
+
+ if (sk && udp_sk(sk)->flow_dissect)
+ ret = udp_sk(sk)->flow_dissect(sk, skb, key_control,
+ flow_dissector, target_container,
+ data, p_proto, p_ip_proto,
+ p_nhoff, p_hlen, flags);
+ rcu_read_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL(udp_flow_dissect);
+
+static enum flow_dissect_ret udp4_flow_dissect(const struct sk_buff *skb,
+ struct flow_dissector_key_control *key_control,
+ struct flow_dissector *flow_dissector,
+ void *target_container, void *data,
+ __be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
+ int *p_hlen, unsigned int flags)
+{
+ return udp_flow_dissect(skb, udp4_lib_lookup_skb, key_control,
+ flow_dissector, target_container, data,
+ p_proto, p_ip_proto, p_nhoff, p_hlen, flags);
+}
+
static const struct net_offload udpv4_offload = {
.callbacks = {
.gso_segment = udp4_tunnel_segment,
.gro_receive = udp4_gro_receive,
.gro_complete = udp4_gro_complete,
+ .flow_dissect = udp4_flow_dissect,
},
};
diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c
index 6539ff15e9a3..a4eec2a044d2 100644
--- a/net/ipv4/udp_tunnel.c
+++ b/net/ipv4/udp_tunnel.c
@@ -71,6 +71,7 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
udp_sk(sk)->encap_destroy = cfg->encap_destroy;
udp_sk(sk)->gro_receive = cfg->gro_receive;
udp_sk(sk)->gro_complete = cfg->gro_complete;
+ udp_sk(sk)->flow_dissect = cfg->flow_dissect;
udp_tunnel_encap_enable(sock);
}
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index 455fd4e39333..99ade504eaf7 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -73,11 +73,24 @@ static int udp6_gro_complete(struct sk_buff *skb, int nhoff)
return udp_gro_complete(skb, nhoff, udp6_lib_lookup_skb);
}
+static enum flow_dissect_ret udp6_flow_dissect(const struct sk_buff *skb,
+ struct flow_dissector_key_control *key_control,
+ struct flow_dissector *flow_dissector,
+ void *target_container, void *data,
+ __be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
+ int *p_hlen, unsigned int flags)
+{
+ return udp_flow_dissect(skb, udp6_lib_lookup_skb, key_control,
+ flow_dissector, target_container, data,
+ p_proto, p_ip_proto, p_nhoff, p_hlen, flags);
+}
+
static const struct net_offload udpv6_offload = {
.callbacks = {
.gso_segment = udp6_tunnel_segment,
.gro_receive = udp6_gro_receive,
.gro_complete = udp6_gro_complete,
+ .flow_dissect = udp6_flow_dissect,
},
};
--
2.11.0
^ permalink raw reply related
* [PATCH v2 net-next 5/6] fou: Support flow dissection
From: Tom Herbert @ 2017-08-29 23:27 UTC (permalink / raw)
To: davem; +Cc: netdev, Tom Herbert
In-Reply-To: <20170829232711.1465-1-tom@quantonium.net>
Populate offload flow_dissect callabck appropriately for fou and gue.
Signed-off-by: Tom Herbert <tom@quantonium.net>
---
net/ipv4/fou.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index 1540db65241a..a831dd49fb28 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -282,6 +282,20 @@ static int fou_gro_complete(struct sock *sk, struct sk_buff *skb,
return err;
}
+static enum flow_dissect_ret fou_flow_dissect(struct sock *sk,
+ const struct sk_buff *skb,
+ struct flow_dissector_key_control *key_control,
+ struct flow_dissector *flow_dissector,
+ void *target_container, void *data,
+ __be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
+ int *p_hlen, unsigned int flags)
+{
+ *p_ip_proto = fou_from_sock(sk)->protocol;
+ *p_nhoff += sizeof(struct udphdr);
+
+ return FLOW_DISSECT_RET_IPPROTO_AGAIN;
+}
+
static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
struct guehdr *guehdr, void *data,
size_t hdrlen, struct gro_remcsum *grc,
@@ -500,6 +514,53 @@ static int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
return err;
}
+static enum flow_dissect_ret gue_flow_dissect(struct sock *sk,
+ const struct sk_buff *skb,
+ struct flow_dissector_key_control *key_control,
+ struct flow_dissector *flow_dissector,
+ void *target_container, void *data,
+ __be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
+ int *p_hlen, unsigned int flags)
+{
+ struct guehdr *guehdr, _guehdr;
+
+ guehdr = __skb_header_pointer(skb, *p_nhoff + sizeof(struct udphdr),
+ sizeof(_guehdr), data, *p_hlen, &_guehdr);
+ if (!guehdr)
+ return FLOW_DISSECT_RET_OUT_BAD;
+
+ switch (guehdr->version) {
+ case 0:
+ if (unlikely(guehdr->control))
+ return FLOW_DISSECT_RET_CONTINUE;
+
+ *p_ip_proto = guehdr->proto_ctype;
+ *p_nhoff += sizeof(struct udphdr) +
+ sizeof(*guehdr) + (guehdr->hlen << 2);
+
+ break;
+ case 1:
+ switch (((struct iphdr *)guehdr)->version) {
+ case 4:
+ *p_ip_proto = IPPROTO_IPIP;
+ break;
+ case 6:
+ *p_ip_proto = IPPROTO_IPV6;
+ break;
+ default:
+ return FLOW_DISSECT_RET_CONTINUE;
+ }
+
+ *p_nhoff += sizeof(struct udphdr);
+
+ break;
+ default:
+ return FLOW_DISSECT_RET_CONTINUE;
+ }
+
+ return FLOW_DISSECT_RET_IPPROTO_AGAIN;
+}
+
static int fou_add_to_port_list(struct net *net, struct fou *fou)
{
struct fou_net *fn = net_generic(net, fou_net_id);
@@ -570,12 +631,14 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
tunnel_cfg.encap_rcv = fou_udp_recv;
tunnel_cfg.gro_receive = fou_gro_receive;
tunnel_cfg.gro_complete = fou_gro_complete;
+ tunnel_cfg.flow_dissect = fou_flow_dissect;
fou->protocol = cfg->protocol;
break;
case FOU_ENCAP_GUE:
tunnel_cfg.encap_rcv = gue_udp_recv;
tunnel_cfg.gro_receive = gue_gro_receive;
tunnel_cfg.gro_complete = gue_gro_complete;
+ tunnel_cfg.flow_dissect = gue_flow_dissect;
break;
default:
err = -EINVAL;
--
2.11.0
^ permalink raw reply related
* [PATCH v2 net-next 6/6] vxlan: support flow dissect
From: Tom Herbert @ 2017-08-29 23:27 UTC (permalink / raw)
To: davem; +Cc: netdev, Tom Herbert
In-Reply-To: <20170829232711.1465-1-tom@quantonium.net>
Populate offload flow_dissect callback appropriately for VXLAN and
VXLAN-GPE.
Signed-off-by: Tom Herbert <tom@quantonium.net>
---
drivers/net/vxlan.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index ae3a1da703c2..41e50de40af4 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -1336,6 +1336,55 @@ static bool vxlan_ecn_decapsulate(struct vxlan_sock *vs, void *oiph,
return err <= 1;
}
+static enum flow_dissect_ret vxlan_flow_dissect(struct sock *sk,
+ const struct sk_buff *skb,
+ struct flow_dissector_key_control *key_control,
+ struct flow_dissector *flow_dissector,
+ void *target_container, void *data,
+ __be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,
+ int *p_hlen, unsigned int flags)
+{
+ __be16 protocol = htons(ETH_P_TEB);
+ struct vxlanhdr *vhdr, _vhdr;
+ struct vxlan_sock *vs;
+
+ vhdr = __skb_header_pointer(skb, *p_nhoff + sizeof(struct udphdr),
+ sizeof(_vhdr), data, *p_hlen, &_vhdr);
+ if (!vhdr)
+ return FLOW_DISSECT_RET_OUT_BAD;
+
+ vs = rcu_dereference_sk_user_data(sk);
+ if (!vs)
+ return FLOW_DISSECT_RET_OUT_BAD;
+
+ if (vs->flags & VXLAN_F_GPE) {
+ struct vxlanhdr_gpe *gpe = (struct vxlanhdr_gpe *)vhdr;
+
+ /* Need to have Next Protocol set for interfaces in GPE mode. */
+ if (gpe->version != 0 || !gpe->np_applied || gpe->oam_flag)
+ return FLOW_DISSECT_RET_CONTINUE;
+
+ switch (gpe->next_protocol) {
+ case VXLAN_GPE_NP_IPV4:
+ protocol = htons(ETH_P_IP);
+ break;
+ case VXLAN_GPE_NP_IPV6:
+ protocol = htons(ETH_P_IPV6);
+ break;
+ case VXLAN_GPE_NP_ETHERNET:
+ protocol = htons(ETH_P_TEB);
+ break;
+ default:
+ return FLOW_DISSECT_RET_CONTINUE;
+ }
+ }
+
+ *p_nhoff += sizeof(struct udphdr) + sizeof(_vhdr);
+ *p_proto = protocol;
+
+ return FLOW_DISSECT_RET_PROTO_AGAIN;
+}
+
/* Callback from net/ipv4/udp.c to receive packets */
static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
{
@@ -2864,6 +2913,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
tunnel_cfg.encap_destroy = NULL;
tunnel_cfg.gro_receive = vxlan_gro_receive;
tunnel_cfg.gro_complete = vxlan_gro_complete;
+ tunnel_cfg.flow_dissect = vxlan_flow_dissect;
setup_udp_tunnel_sock(net, sock, &tunnel_cfg);
--
2.11.0
^ permalink raw reply related
* Re: [RESEND PATCH] Allow passing tid or pid in SCM_CREDENTIALS without CAP_SYS_ADMIN
From: prakash.sangappa @ 2017-08-29 23:59 UTC (permalink / raw)
To: David Miller; +Cc: linux-kernel, netdev, ebiederm, drepper
In-Reply-To: <20170829.160232.1901318933754673000.davem@davemloft.net>
On 08/29/2017 04:02 PM, David Miller wrote:
> From: Prakash Sangappa <prakash.sangappa@oracle.com>
> Date: Mon, 28 Aug 2017 17:12:20 -0700
>
>> Currently passing tid(gettid(2)) of a thread in struct ucred in
>> SCM_CREDENTIALS message requires CAP_SYS_ADMIN capability otherwise
>> it fails with EPERM error. Some applications deal with thread id
>> of a thread(tid) and so it would help to allow tid in SCM_CREDENTIALS
>> message. Basically, either tgid(pid of the process) or the tid of
>> the thread should be allowed without the need for CAP_SYS_ADMIN capability.
>>
>> SCM_CREDENTIALS will be used to determine the global id of a process or
>> a thread running inside a pid namespace.
>>
>> This patch adds necessary check to accept tid in SCM_CREDENTIALS
>> struct ucred.
>>
>> Signed-off-by: Prakash Sangappa <prakash.sangappa@oracle.com>
> I'm pretty sure that by the descriptions in previous changes to this
> function, what you are proposing is basically a minor form of PID
> spoofing which we only want someone with CAP_SYS_ADMIN over the
> PID namespace to be able to do.
The fix is to allow passing tid of the calling thread itself not of any
other thread or process. Curious why would this be considered
as pid spoofing?
This change would enable a thread in a multi threaded process, running
inside a pid namespace to be identified by the recipient of the
message easily.
>
> Sorry, I'm not applying this.
^ permalink raw reply
* Re: [RESEND PATCH] Allow passing tid or pid in SCM_CREDENTIALS without CAP_SYS_ADMIN
From: Eric W. Biederman @ 2017-08-30 0:10 UTC (permalink / raw)
To: prakash.sangappa; +Cc: David Miller, linux-kernel, netdev, drepper
In-Reply-To: <c9ea9bec-6b0e-70d6-3f74-9b483358edd2@oracle.com>
"prakash.sangappa" <prakash.sangappa@oracle.com> writes:
> On 08/29/2017 04:02 PM, David Miller wrote:
>> From: Prakash Sangappa <prakash.sangappa@oracle.com>
>> Date: Mon, 28 Aug 2017 17:12:20 -0700
>>
>>> Currently passing tid(gettid(2)) of a thread in struct ucred in
>>> SCM_CREDENTIALS message requires CAP_SYS_ADMIN capability otherwise
>>> it fails with EPERM error. Some applications deal with thread id
>>> of a thread(tid) and so it would help to allow tid in SCM_CREDENTIALS
>>> message. Basically, either tgid(pid of the process) or the tid of
>>> the thread should be allowed without the need for CAP_SYS_ADMIN capability.
>>>
>>> SCM_CREDENTIALS will be used to determine the global id of a process or
>>> a thread running inside a pid namespace.
>>>
>>> This patch adds necessary check to accept tid in SCM_CREDENTIALS
>>> struct ucred.
>>>
>>> Signed-off-by: Prakash Sangappa <prakash.sangappa@oracle.com>
>> I'm pretty sure that by the descriptions in previous changes to this
>> function, what you are proposing is basically a minor form of PID
>> spoofing which we only want someone with CAP_SYS_ADMIN over the
>> PID namespace to be able to do.
>
> The fix is to allow passing tid of the calling thread itself not of any
> other thread or process. Curious why would this be considered
> as pid spoofing?
>
> This change would enable a thread in a multi threaded process, running
> inside a pid namespace to be identified by the recipient of the
> message easily.
I think a more practical problem is that change, changes what is being
passed in the SCM_CREDENTIALS from a pid of a process to a tid of a
thread. That could be confusing and that confusion could be exploited.
It is definitely confusing because in some instances a value can be both
a tgid and a tid.
I definitely think this needs to be talked about in terms of changing
what is passed in that field and what the consequences could be.
I suspect you are ok. As nothing allows passing a tid today. But I
don't see any analysis on why passing a tid instead of a tgid will not
confuse the receiving application, and in such confusion introduce a
security hole.
Eric
^ permalink raw reply
* Re: Fwd: DA850-evm MAC Address is random
From: Adam Ford @ 2017-08-30 0:49 UTC (permalink / raw)
To: Sekhar Nori; +Cc: Tony Lindgren, Grygorii Strashko, linux-omap, netdev
In-Reply-To: <CAHCN7x+paAdW7cXrALhtLZZiJbkE6v_8_qRXBec7hxLJPvSMbw@mail.gmail.com>
On Tue, Aug 29, 2017 at 10:20 AM, Adam Ford <aford173@gmail.com> wrote:
> On Tue, Aug 29, 2017 at 10:16 AM, Sekhar Nori <nsekhar@ti.com> wrote:
>> On Tuesday 29 August 2017 05:32 PM, Adam Ford wrote:
>>> On Tue, Aug 29, 2017 at 6:42 AM, Sekhar Nori <nsekhar@ti.com> wrote:
>>>> On Tuesday 29 August 2017 03:53 PM, Adam Ford wrote:
>>>>> On Tue, Aug 29, 2017 at 3:23 AM, Sekhar Nori <nsekhar@ti.com> wrote:
>>>>>> On Tuesday 29 August 2017 02:42 AM, Tony Lindgren wrote:
>>>>>>> * Adam Ford <aford173@gmail.com> [170828 13:33]:
>>>>>>>> On Mon, Aug 28, 2017 at 1:54 PM, Grygorii Strashko
>>>>>>>> <grygorii.strashko@ti.com> wrote:
>>>>>>>>> Cc: Sekhar
>>>>>>>>>
>>>>>>>>> On 08/28/2017 10:32 AM, Adam Ford wrote:
>>>>>>>>>>
>>>>>>>>>> The davinvi_emac MAC address seems to attempt a call to
>>>>>>>>>> ti_cm_get_macid in cpsw-common.c but it returns the message
>>>>>>>>>> 'davinci_emac davinci_emac.1: incompatible machine/device type for
>>>>>>>>>> reading mac address ' and then generates a random MAC address.
>>>>>>>>>>
>>>>>>>>>> The function appears to lookup varions boards using
>>>>>>>>>> 'of_machine_is_compaible' and supports dm8148, am33xx, am3517, dm816,
>>>>>>>>>> am4372 and dra7. I don't see the ti,davinci-dm6467-emac which is
>>>>>>>>>> what's shown in the da850 device tree.
>>>>>>>>>>
>>>>>>>>>> Is there a patch somewhere for supporting the da850-evm?
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Not sure if MAC address can be read from Control module.
>>>>>>>>> May be Sekhar can say more?
>>>>>>>>
>>>>>>>> My understanding is that the MAC address is programmed by Logic PD
>>>>>>>> into the SPI flash. The Bootloader reads this from either SPI or its
>>>>>>>> env variables. Looking at the partition info listed in the
>>>>>>>> da850-evm.dts file, it appears as if they've reserved space for it.
>>>>>>>> Unfortunately, I don't see any code that reads it out. I was hoping
>>>>>>
>>>>>> This code is present in U-Boot sources at
>>>>>> board/davinci/da8xxevm/da850evm.c. See the function get_mac_addr() and
>>>>>> its usage in misc_init_r().
>>>>>>
>>>>>>>> there might be a way to just pass cmdline parameter from the
>>>>>>>> bootloader to the kernel to accept the MAC address.
>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> If not, is there a way to pass the MAC address from U-Boot to the
>>>>>>>>>> driver so it doesn't generate a random MAC?
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> "local-mac-address" dt porp
>>>>>>>>
>>>>>>>> The downside here, is that we'd have to have the Bootloader modify the
>>>>>>>> device tree.
>>>>>>>
>>>>>>> That piece of code exists somewhere in u-boot already. Note how
>>>>>>
>>>>>> Yes, it is fdt_fixup_ethernet() and its usage is in common/image-fdt.c.
>>>>>>
>>>>>>> we are populating the mac address for USB Ethernet drivers in
>>>>>>> u-boot and then the Ethernet driver code parses it. See commit
>>>>>>> 055d31de7158 ("ARM: omap3: beagleboard-xm: dt: Add ethernet to
>>>>>>> the device tree") for some more information.
>>>>>>>
>>>>>>> I think u-boot needs the ethernet alias for finding the interface.
>>>>>>
>>>>>> That's exactly what was missing. I have sent a patch for fixing that and
>>>>>> copied you there.
>>>>>
>>>>> Thanks for doing that.
>>>>>
>>>>>>
>>>>>> Adam, if I can get your Tested-by, I will make an attempt to send it for
>>>>>> v4.13 itself.
>>>>>
>>>>> I will test it. Do need to run some instruction or do something
>>>>> special in U-Boot to pass this in the proper place for the kernel to
>>>>> pull it? Tony's patch reference showed
>>>>> command for fdt set, but I am not sure I fully understand the
>>>>> parameters that went along with that.
>>>>
>>>> Nope, just applying the patch and booting the with the new dtb should
>>>> result in the random mac address going away.
>>>
>>> Unfortunately, I am not seeing any change with the patch (at least
>>> with Kernel 4.12.9 from stable).
>>>
>>> netconsole: network logging started
>>> davinci_emac davinci_emac.1: incompatible machine/device type for
>>> reading mac address
>>> davinci_emac davinci_emac.1: using random MAC addr: ee:74:c3:3a:15:be
>>>
>>> Looking at the source for cpsw-common.c function ti_cm_get_macid()
>>> doesn't have a case for the ti,davinci-dm6467-emac so I wonder if
>>> there might be more to it.
>>
>> Hmm, it did solve the issue for me when I tried latest -next. And
>> reverting the patch brought back the random mac address usage. Could you
>> try latest mainline or -next?
>>
>> Meanwhile let me see whats going on with the observations you have.
>
> I will try again with -next this afternoon and see what I can find.
> Can you tell me which U-Boot version you're using? I want to match
> your setup. I want to see if something is missing during the hand-off
> between the Bootloader and Linux.
>
I wonder if U-Boot isn't pushing something to Linux because it doesn't
appear to be running some of the da850 specific code even when I run
linux-next. Can you tell me what verision of U-Boot you're using?
Other than using davinci_all_defconfig, did you change the
configuration at all?
[ 1.411107] netconsole: network logging started
[ 1.416237] davinci_emac davinci_emac.1: incompatible machine/device type for
reading mac address
[ 1.424496] davinci_emac davinci_emac.1: using random MAC addr: be:e2:84:ed:3
c:87
I also confirmed the SPI-flash has the MAC programmed:
# hexdump /dev/mtd5ro
0000000 0800 04ee 8e32 ffff ffff ffff ffff ffff
0000010 ffff ffff ffff ffff ffff ffff ffff ffff
Here is my full log:
[ 0.000000] Booting Linux on physical CPU 0x0
[ 0.000000] Linux version 4.13.0-rc7-next-20170829 (aford@ubuntu16) (gcc vers
ion 4.8.3 20140320 (prerelease) (Sourcery CodeBench Lite 2014.05-29)) #1 PREEMPT
Tue Aug 29 19:11:06 CDT 2017
[ 0.000000] CPU: ARM926EJ-S [41069265] revision 5 (ARMv5TEJ), cr=0005317f
[ 0.000000] CPU: VIVT data cache, VIVT instruction cache
[ 0.000000] OF: fdt: Machine model: DA850/AM1808/OMAP-L138 EVM
[ 0.000000] Memory policy: Data cache writethrough
[ 0.000000] DaVinci da850/omap-l138 variant 0x0
[ 0.000000] On node 0 totalpages: 8192
[ 0.000000] free_area_init_node: node 0, pgdat c05dfec8, node_mem_map c1fb900
0
[ 0.000000] DMA zone: 64 pages used for memmap
[ 0.000000] DMA zone: 0 pages reserved
[ 0.000000] DMA zone: 8192 pages, LIFO batch:0
[ 0.000000] random: fast init done
[ 0.000000] pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
[ 0.000000] pcpu-alloc: [0] 0
[ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 8128
[ 0.000000] Kernel command line: mem=32M console=ttyS2,115200n8 root=/dev/mmc
blk0p2 rw wait noinitrd
[ 0.000000] PID hash table entries: 128 (order: -3, 512 bytes)
[ 0.000000] Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
[ 0.000000] Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
[ 0.000000] Memory: 26196K/32768K available (4412K kernel code, 330K rwdata,
1012K rodata, 224K init, 146K bss, 6572K reserved, 0K cma-reserved)
[ 0.000000] Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB)
fixmap : 0xffc00000 - 0xfff00000 (3072 kB)
vmalloc : 0xc2800000 - 0xff800000 ( 976 MB)
lowmem : 0xc0000000 - 0xc2000000 ( 32 MB)
modules : 0xbf000000 - 0xc0000000 ( 16 MB)
.text : 0xc0008000 - 0xc04574e8 (4414 kB)
.init : 0xc0556000 - 0xc058e000 ( 224 kB)
.data : 0xc058e000 - 0xc05e09e0 ( 331 kB)
.bss : 0xc05e4ebc - 0xc06097b4 ( 147 kB)\x00c - 0xc06097
b4 ( 147 kB)
[ 0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[ 0.000000] Preemptible hierarchical RCU implementation.
[ 0.000000] Tasks RCU enabled.\x00d.
[ 0.000000] NR_IRQS: 245
[ 0.000000] clocksource: timer0_1: mask: 0xffffffff max_cycles: 0xffffffff, m
ax_idle_ns: 79635851949 ns
[ 0.000000] sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 89478
484971ns
[ 0.000480] Console: colour dummy device 80x30
[ 0.000623] Calibrating delay loop... 148.88 BogoMIPS (lpj=744448)
[ 0.070151] pid_max: default: 32768 minimum: 301
[ 0.070730] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[ 0.070802] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[ 0.073212] CPU: Testing write buffer coherency: ok
[ 0.076384] Setting up static identity map for 0xc0008400 - 0xc0008458
[ 0.077078] Hierarchical SRCU implementation.
[ 0.081656] devtmpfs: initialized
[ 0.102577] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, ma
x_idle_ns: 19112604462750000 ns
[ 0.102678] futex hash table entries: 256 (order: -1, 3072 bytes)
[ 0.103368] pinctrl core: initialized pinctrl subsystem
[ 0.107090] NET: Registered protocol family 16
[ 0.108769] DMA: preallocated 256 KiB pool for atomic coherent allocations
[ 0.112006] cpuidle: using governor menu
[ 0.142220] mux: initialized RTC_ALARM
[ 0.142267] mux: Setting register RTC_ALARM
[ 0.142313] mux: PINMUX0 (0x00000000) = 0x44080000 -> 0x24080000
[ 0.173799] edma 1c00000.edma: memcpy is disabled
[ 0.187509] edma 1c00000.edma: TI EDMA DMA engine driver
[ 0.188706] edma 1e30000.edma: memcpy is disabled
[ 0.203238] edma 1e30000.edma: TI EDMA DMA engine driver
[ 0.208604] i2c_davinci i2c_davinci.1: could not find pctldev for node /soc@1
c00000/pinmux@14120/pinmux_i2c0_pins, deferring probe
[ 0.213319] clocksource: Switched to clocksource timer0_1
[ 0.272942] NET: Registered protocol family 2
[ 0.276271] TCP established hash table entries: 1024 (order: 0, 4096 bytes)
[ 0.276399] TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
[ 0.276478] TCP: Hash tables configured (established 1024 bind 1024)
[ 0.276920] UDP hash table entries: 256 (order: 0, 4096 bytes)
[ 0.277028] UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
[ 0.277860] NET: Registered protocol family 1
[ 0.279709] RPC: Registered named UNIX socket transport module.
[ 0.279757] RPC: Registered udp transport module.
[ 0.279779] RPC: Registered tcp transport module.
[ 0.279800] RPC: Registered tcp NFSv4.1 backchannel transport module.
[ 0.286296] workingset: timestamp_bits=30 max_order=13 bucket_order=0
[ 0.334423] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 2
51)
[ 0.334488] io scheduler noop registered (default)
[ 0.334519] io scheduler mq-deadline registered
[ 0.334544] io scheduler kyber registered
[ 0.337441] pinctrl-single 1c14120.pinmux: 160 pins at pa fec14120 size 80
[ 0.724136] Serial: 8250/16550 driver, 3 ports, IRQ sharing disabled
[ 0.731781] serial8250.0: ttyS0 at MMIO 0x1c42000 (irq = 25, base_baud = 9375
000) is a TI DA8xx/66AK2x
[ 0.736191] serial8250.1: ttyS1 at MMIO 0x1d0c000 (irq = 53, base_baud = 8250
000) is a TI DA8xx/66AK2x
[ 0.740320] serial8250.2: ttyS2 at MMIO 0x1d0d000 (irq = 61, base_baud = 8250
000) is a TI DA8xx/66AK2x
[ 1.104485] console [ttyS2] enabled
[ 1.112179] brd: module loaded
[ 1.115635] libphy: Fixed MDIO Bus: probed
[ 1.173556] davinci_mdio davinci_mdio.0: davinci mdio revision 1.5, bus freq
2200000
[ 1.180102] davinci_mdio davinci_mdio.0: detected phy mask fffffffe
[ 1.188281] libphy: davinci_mdio.0: probed
[ 1.191175] davinci_mdio davinci_mdio.0: phy[0]: device davinci_mdio.0:00, dr
iver SMSC LAN8710/LAN8720
[ 1.200474] i2c /dev entries driver
[ 1.203043] IR NEC protocol handler initialized
[ 1.206722] IR RC5(x/sz) protocol handler initialized
[ 1.210530] IR RC6 protocol handler initialized
[ 1.214207] IR JVC protocol handler initialized
[ 1.217493] IR Sony protocol handler initialized
[ 1.220843] IR SANYO protocol handler initialized
[ 1.224603] IR Sharp protocol handler initialized
[ 1.228062] IR MCE Keyboard/mouse protocol handler initialized
[ 1.232635] IR XMP protocol handler initialized
[ 1.293829] davinci_mmc da830-mmc.0: Using DMA, 4-bit mode
[ 1.307464] NET: Registered protocol family 10
[ 1.317055] Segment Routing with IPv6
[ 1.319797] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[ 1.330958] NET: Registered protocol family 17
[ 1.362382] mmc0: host does not support reading read-only switch, assuming wr
ite-enable
[ 1.374564] pca953x 0-0020: 0-0020 supply vcc not found, using dummy regulato
r
[ 1.381327] mmc0: new high speed SDHC card at address b368
[ 1.387395] mmcblk0: mmc0:b368 00000 3.75 GiB
[ 1.392025] pca953x 0-0020: failed reading register
[ 1.398285] mmcblk0: p1 p2
[ 1.403791] pca953x: probe of 0-0020 failed with error -121
[ 1.408691] console [netcon0] enabled
[ 1.411107] netconsole: network logging started
[ 1.416237] davinci_emac davinci_emac.1: incompatible machine/device type for
reading mac address
[ 1.424496] davinci_emac davinci_emac.1: using random MAC addr: be:e2:84:ed:3
c:87
[ 1.435043] hctosys: unable to open rtc device (rtc0)
[ 1.439533] vbat: disabling
[ 1.455953] EXT4-fs (mmcblk0p2): couldn't mount as ext3 due to feature incomp
atibilities
[ 1.495292] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. O
pts: (null)
[ 1.502362] VFS: Mounted root (ext4 filesystem) on device 179:2.
[ 1.509400] devtmpfs: mounted
[ 1.512507] Freeing unused kernel memory: 224K
[ 1.515909] This architecture does not have kernel memory protection.
[ 1.942277] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)
[ 2.381303] udevd[54]: starting version 3.2.2
[ 2.611166] udevd[55]: starting eudev-3.2.2
[ 4.393796] omap_rtc 1c23000.rtc: rtc core: registered 1c23000.rtc as rtc0
[ 4.760433] davinci-wdt davinci-wdt: heartbeat 60 sec
[ 5.631862] spi_davinci spi_davinci.1: Controller at 0xfef0e000
[ 5.715839] vpif vpif: vpif probe success
[ 5.805009] Linux video capture interface: v2.00
[ 5.876891] asoc-simple-card sound: tlv320aic3x-hifi <-> davinci-mcasp.0 mapp
ing ok
[ 6.281740] adv7343 0-002a: chip found @ 0x54 (DaVinci I2C adapter)
[ 6.304545] adv7343 0-002a: Error initializing
[ 6.307892] adv7343: probe of 0-002a failed with error -121
[ 6.312763] vpif_display vpif_display: Error registering v4l2 subdevice
[ 6.503036] tvp514x 0-005d: Write: retry ... 0
[ 6.543977] tvp514x 0-005d: Write: retry ... 1
[ 6.574113] tvp514x 0-005d: Write: retry ... 2
[ 6.603935] tvp514x 0-005d: Write: retry ... 3
[ 6.633983] tvp514x 0-005d: Write: retry ... 4
[ 6.663965] tvp514x 0-005d: Write: retry ... 5
[ 6.693978] tvp514x 0-005d: tvp514x 0-005d decoder driver registered !!
[ 6.704907] vpif_capture vpif_capture: registered sub device tvp514x-0
[ 6.813679] tvp514x 0-005c: Write: retry ... 0
[ 6.844005] tvp514x 0-005c: Write: retry ... 1
[ 6.874221] tvp514x 0-005c: Write: retry ... 2
[ 6.903956] tvp514x 0-005c: Write: retry ... 3
[ 6.934023] tvp514x 0-005c: Write: retry ... 4
[ 6.963950] tvp514x 0-005c: Write: retry ... 5
[ 6.993967] tvp514x 0-005c: tvp514x 0-005c decoder driver registered !!
[ 7.013865] vpif_capture vpif_capture: registered sub device tvp514x-1
[ 7.052179] tvp514x 0-005d: Write: retry ... 0
[ 7.093976] tvp514x 0-005d: Write: retry ... 1
[ 7.123965] tvp514x 0-005d: Write: retry ... 2
[ 7.153916] tvp514x 0-005d: Write: retry ... 3
[ 7.184160] tvp514x 0-005d: Write: retry ... 4
[ 7.219318] tvp514x 0-005d: Write: retry ... 5
[ 7.243986] vpif_capture vpif_capture: Failed to set input
[ 12.944674] m25p80 spi0.0: m25p64 (8192 Kbytes)
[ 12.973994] nand: No NAND device found
[ 13.062551] 6 ofpart partitions found on MTD device spi0.0
[ 13.067365] Creating 6 MTD partitions on "spi0.0":
[ 13.070964] 0x000000000000-0x000000010000 : "U-Boot-SPL"
[ 13.084491] 0x000000010000-0x000000090000 : "U-Boot"
[ 13.095174] 0x000000090000-0x0000000a0000 : "U-Boot-Env"
[ 13.106238] 0x0000000a0000-0x000000320000 : "Kernel"
[ 13.116910] 0x000000320000-0x000000720000 : "Filesystem"
[ 13.127963] 0x0000007f0000-0x000000800000 : "MAC-Address"
>
>>
>> Thanks,
>> Sekhar
^ permalink raw reply
* [PATCH net-next 0/3 v10] Add support for rmnet driver
From: Subash Abhinov Kasiviswanathan @ 2017-08-30 0:47 UTC (permalink / raw)
To: netdev, davem, fengguang.wu, dcbw, jiri, stephen, David.Laight,
marcel, andrew
Cc: Subash Abhinov Kasiviswanathan
Hi David
I have updated the locking scheme as follows -
The shared resource which needs to be protected is realdev->rx_handler_data.
For the writer path, this is using rtnl_lock(). The writer paths are
rmnet_newlink(), rmnet_dellink() and rmnet_force_unassociate_device(). These
paths are already called with rtnl_lock() acquired in. There is also an
ASSERT_RTNL() to ensure that we are calling with rtnl acquired. For
dereference here, we will need to use rtnl_dereference(). Dev list writing
needs to happen with rtnl_lock() acquired for netdev_master_upper_dev_link().
For the reader path, the real_dev->rx_handler_data is called in the TX / RX
path. We only need rcu_read_lock() for these scenarios. In these cases,
the rcu_read_lock() is held in __dev_queue_xmit() and
netif_receive_skb_internal(), so readers need to use rcu_dereference_rtnl()
to get the relevant information. For dev list reading, we again acquire
rcu_read_lock() in rmnet_dellink() for netdev_master_upper_dev_get_rcu().
We also use unregister_netdevice_many() to free all rmnet devices in
rmnet_force_unassociate_device() so we dont lose the rtnl_lock() and free in
same context.
I have also added this as a comment in rmnet_config.c.
--
v1: Same as the RFC patch with some minor fixes for issues reported by
kbuild test robot.
v1->v2: Change datatypes and remove config IOCTL as mentioned by David.
Also fix checkpatch issues and remove some unused code.
v2->v3: Move location to drivers/net and rename to rmnet. Change the
userspace - netlink communication from custom netlink to rtnl_link_ops.
Refactor some code. Use a fixed config for ingress and egress.
v3->v4: Move location to drivers/net/ethernet/qualcomm/.
Fix comments from Stephen and Jiri -
Split the ether and arp type changes into seperate patches.
Remove debug and custom logging and switch to standard netdevice log.
Remove module parameters. Refactor and change some code style issues.
v4->v5: Rename some structs and variables. Move the initializer
before the for loop start. Put the arp type in correct sequence.
v5->v6: Fix comments from Dan -
Use the upper link API. As a result, remove all the refcounting logic.
Device refcount is explicitly held on real_dev on rx_handler
registration only. Modifiy the flow control struct. Remove the unused
ethernet mode handling.
v6->v7: Fix comments from David - Add newline to end of Makefile. Remove
inline from .c files. Move the module init/exit to rmnet config. Fix an
error reported by kbuild test robot for an unused file.
v7->v8: Use a smaller value for ETH_P_MAP as mentioned by David. Change
netdev_info to netdev_dbg as mentioned by Andew. Fix comments from
Stephen regarding netdev_priv and sparse related errors of using 0 as NULL
v8->v9: Fix comments from David - Remove the CFLAG rule. Change the way
rmnet devices are freed. Instead of using a workqueue to unregister devices
individually, go through the list and free all devices within the rtnl_lock().
v9->v10: Fix the locking scheme as mentioned by David. Change comment near
MAP type definition as mentioned by Dan. Refactor some code.
Subash Abhinov Kasiviswanathan (3):
net: ether: Add support for multiplexing and aggregation type
net: arp: Add support for raw IP device
drivers: net: ethernet: qualcomm: rmnet: Initial implementation
Documentation/networking/rmnet.txt | 82 ++++
drivers/net/ethernet/qualcomm/Kconfig | 2 +
drivers/net/ethernet/qualcomm/Makefile | 2 +
drivers/net/ethernet/qualcomm/rmnet/Kconfig | 12 +
drivers/net/ethernet/qualcomm/rmnet/Makefile | 10 +
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 419 +++++++++++++++++++++
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | 56 +++
.../net/ethernet/qualcomm/rmnet/rmnet_handlers.c | 271 +++++++++++++
.../net/ethernet/qualcomm/rmnet/rmnet_handlers.h | 26 ++
drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h | 88 +++++
.../ethernet/qualcomm/rmnet/rmnet_map_command.c | 107 ++++++
.../net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 105 ++++++
.../net/ethernet/qualcomm/rmnet/rmnet_private.h | 45 +++
drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 170 +++++++++
drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h | 29 ++
include/uapi/linux/if_arp.h | 1 +
include/uapi/linux/if_ether.h | 3 +
17 files changed, 1428 insertions(+)
create mode 100644 Documentation/networking/rmnet.txt
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/Kconfig
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/Makefile
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h
--
1.9.1
^ permalink raw reply
* [PATCH net-next 1/3 v10] net: ether: Add support for multiplexing and aggregation type
From: Subash Abhinov Kasiviswanathan @ 2017-08-30 0:47 UTC (permalink / raw)
To: netdev, davem, fengguang.wu, dcbw, jiri, stephen, David.Laight,
marcel, andrew
Cc: Subash Abhinov Kasiviswanathan
In-Reply-To: <1504054078-10173-1-git-send-email-subashab@codeaurora.org>
Define the Qualcomm multiplexing and aggregation (MAP) ether type 0x00F9.
This is needed for receiving data in the MAP protocol like RMNET. This is
not an officially registered ID.
Signed-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
---
include/uapi/linux/if_ether.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index 5bc9bfd..30526db 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -137,6 +137,9 @@
#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */
#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */
#define ETH_P_XDSA 0x00F8 /* Multiplexed DSA protocol */
+#define ETH_P_MAP 0x00F9 /* Qualcomm multiplexing and
+ * aggregation protocol
+ */
/*
* This is an Ethernet frame header.
--
1.9.1
^ permalink raw reply related
* [PATCH net-next 2/3 v10] net: arp: Add support for raw IP device
From: Subash Abhinov Kasiviswanathan @ 2017-08-30 0:47 UTC (permalink / raw)
To: netdev, davem, fengguang.wu, dcbw, jiri, stephen, David.Laight,
marcel, andrew
Cc: Subash Abhinov Kasiviswanathan
In-Reply-To: <1504054078-10173-1-git-send-email-subashab@codeaurora.org>
Define the raw IP type. This is needed for raw IP net devices
like rmnet.
Signed-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
---
include/uapi/linux/if_arp.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h
index cf73510..a2a6356 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/uapi/linux/if_arp.h
@@ -59,6 +59,7 @@
#define ARPHRD_LAPB 516 /* LAPB */
#define ARPHRD_DDCMP 517 /* Digital's DDCMP protocol */
#define ARPHRD_RAWHDLC 518 /* Raw HDLC */
+#define ARPHRD_RAWIP 519 /* Raw IP */
#define ARPHRD_TUNNEL 768 /* IPIP tunnel */
#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */
--
1.9.1
^ permalink raw reply related
* [PATCH net-next 3/3 v10] drivers: net: ethernet: qualcomm: rmnet: Initial implementation
From: Subash Abhinov Kasiviswanathan @ 2017-08-30 0:47 UTC (permalink / raw)
To: netdev, davem, fengguang.wu, dcbw, jiri, stephen, David.Laight,
marcel, andrew
Cc: Subash Abhinov Kasiviswanathan
In-Reply-To: <1504054078-10173-1-git-send-email-subashab@codeaurora.org>
RmNet driver provides a transport agnostic MAP (multiplexing and
aggregation protocol) support in embedded module. Module provides
virtual network devices which can be attached to any IP-mode
physical device. This will be used to provide all MAP functionality
on future hardware in a single consistent location.
Signed-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
---
Documentation/networking/rmnet.txt | 82 ++++
drivers/net/ethernet/qualcomm/Kconfig | 2 +
drivers/net/ethernet/qualcomm/Makefile | 2 +
drivers/net/ethernet/qualcomm/rmnet/Kconfig | 12 +
drivers/net/ethernet/qualcomm/rmnet/Makefile | 10 +
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 419 +++++++++++++++++++++
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | 56 +++
.../net/ethernet/qualcomm/rmnet/rmnet_handlers.c | 271 +++++++++++++
.../net/ethernet/qualcomm/rmnet/rmnet_handlers.h | 26 ++
drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h | 88 +++++
.../ethernet/qualcomm/rmnet/rmnet_map_command.c | 107 ++++++
.../net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 105 ++++++
.../net/ethernet/qualcomm/rmnet/rmnet_private.h | 45 +++
drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 170 +++++++++
drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h | 29 ++
15 files changed, 1424 insertions(+)
create mode 100644 Documentation/networking/rmnet.txt
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/Kconfig
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/Makefile
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h
diff --git a/Documentation/networking/rmnet.txt b/Documentation/networking/rmnet.txt
new file mode 100644
index 0000000..6b341ea
--- /dev/null
+++ b/Documentation/networking/rmnet.txt
@@ -0,0 +1,82 @@
+1. Introduction
+
+rmnet driver is used for supporting the Multiplexing and aggregation
+Protocol (MAP). This protocol is used by all recent chipsets using Qualcomm
+Technologies, Inc. modems.
+
+This driver can be used to register onto any physical network device in
+IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator.
+
+Multiplexing allows for creation of logical netdevices (rmnet devices) to
+handle multiple private data networks (PDN) like a default internet, tethering,
+multimedia messaging service (MMS) or IP media subsystem (IMS). Hardware sends
+packets with MAP headers to rmnet. Based on the multiplexer id, rmnet
+routes to the appropriate PDN after removing the MAP header.
+
+Aggregation is required to achieve high data rates. This involves hardware
+sending aggregated bunch of MAP frames. rmnet driver will de-aggregate
+these MAP frames and send them to appropriate PDN's.
+
+2. Packet format
+
+a. MAP packet (data / control)
+
+MAP header has the same endianness of the IP packet.
+
+Packet format -
+
+Bit 0 1 2-7 8 - 15 16 - 31
+Function Command / Data Reserved Pad Multiplexer ID Payload length
+Bit 32 - x
+Function Raw Bytes
+
+Command (1)/ Data (0) bit value is to indicate if the packet is a MAP command
+or data packet. Control packet is used for transport level flow control. Data
+packets are standard IP packets.
+
+Reserved bits are usually zeroed out and to be ignored by receiver.
+
+Padding is number of bytes to be added for 4 byte alignment if required by
+hardware.
+
+Multiplexer ID is to indicate the PDN on which data has to be sent.
+
+Payload length includes the padding length but does not include MAP header
+length.
+
+b. MAP packet (command specific)
+
+Bit 0 1 2-7 8 - 15 16 - 31
+Function Command Reserved Pad Multiplexer ID Payload length
+Bit 32 - 39 40 - 45 46 - 47 48 - 63
+Function Command name Reserved Command Type Reserved
+Bit 64 - 95
+Function Transaction ID
+Bit 96 - 127
+Function Command data
+
+Command 1 indicates disabling flow while 2 is enabling flow
+
+Command types -
+0 for MAP command request
+1 is to acknowledge the receipt of a command
+2 is for unsupported commands
+3 is for error during processing of commands
+
+c. Aggregation
+
+Aggregation is multiple MAP packets (can be data or command) delivered to
+rmnet in a single linear skb. rmnet will process the individual
+packets and either ACK the MAP command or deliver the IP packet to the
+network stack as needed
+
+MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional padding....
+MAP header|IP Packet|Optional padding|MAP header|Command Packet|Optional pad...
+
+3. Userspace configuration
+
+rmnet userspace configuration is done through netlink library librmnetctl
+and command line utility rmnetcli. Utility is hosted in codeaurora forum git.
+The driver uses rtnl_link_ops for communication.
+
+https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/dataservices/tree/rmnetctl
diff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig
index 877675a..f520071 100644
--- a/drivers/net/ethernet/qualcomm/Kconfig
+++ b/drivers/net/ethernet/qualcomm/Kconfig
@@ -59,4 +59,6 @@ config QCOM_EMAC
low power, Receive-Side Scaling (RSS), and IEEE 1588-2008
Precision Clock Synchronization Protocol.
+source "drivers/net/ethernet/qualcomm/rmnet/Kconfig"
+
endif # NET_VENDOR_QUALCOMM
diff --git a/drivers/net/ethernet/qualcomm/Makefile b/drivers/net/ethernet/qualcomm/Makefile
index 92fa7c4..1847350 100644
--- a/drivers/net/ethernet/qualcomm/Makefile
+++ b/drivers/net/ethernet/qualcomm/Makefile
@@ -9,3 +9,5 @@ obj-$(CONFIG_QCA7000_UART) += qcauart.o
qcauart-objs := qca_uart.o
obj-y += emac/
+
+obj-$(CONFIG_RMNET) += rmnet/
diff --git a/drivers/net/ethernet/qualcomm/rmnet/Kconfig b/drivers/net/ethernet/qualcomm/rmnet/Kconfig
new file mode 100644
index 0000000..4948f14
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/Kconfig
@@ -0,0 +1,12 @@
+#
+# RMNET MAP driver
+#
+
+menuconfig RMNET
+ depends on NETDEVICES
+ bool "RmNet MAP driver"
+ default n
+ ---help---
+ If you say Y here, then the rmnet module will be statically
+ compiled into the kernel. The rmnet module provides MAP
+ functionality for embedded and bridged traffic.
diff --git a/drivers/net/ethernet/qualcomm/rmnet/Makefile b/drivers/net/ethernet/qualcomm/rmnet/Makefile
new file mode 100644
index 0000000..01bddf2
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the RMNET module
+#
+
+rmnet-y := rmnet_config.o
+rmnet-y += rmnet_vnd.o
+rmnet-y += rmnet_handlers.o
+rmnet-y += rmnet_map_data.o
+rmnet-y += rmnet_map_command.o
+obj-$(CONFIG_RMNET) += rmnet.o
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
new file mode 100644
index 0000000..e836d26
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
@@ -0,0 +1,419 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * RMNET configuration engine
+ *
+ */
+
+#include <net/sock.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netdevice.h>
+#include "rmnet_config.h"
+#include "rmnet_handlers.h"
+#include "rmnet_vnd.h"
+#include "rmnet_private.h"
+
+/* Locking scheme -
+ * The shared resource which needs to be protected is realdev->rx_handler_data.
+ * For the writer path, this is using rtnl_lock(). The writer paths are
+ * rmnet_newlink(), rmnet_dellink() and rmnet_force_unassociate_device(). These
+ * paths are already called with rtnl_lock() acquired in. There is also an
+ * ASSERT_RTNL() to ensure that we are calling with rtnl acquired. For
+ * dereference here, we will need to use rtnl_dereference(). Dev list writing
+ * needs to happen with rtnl_lock() acquired for netdev_master_upper_dev_link().
+ * For the reader path, the real_dev->rx_handler_data is called in the TX / RX
+ * path. We only need rcu_read_lock() for these scenarios. In these cases,
+ * the rcu_read_lock() is held in __dev_queue_xmit() and
+ * netif_receive_skb_internal(), so readers need to use rcu_dereference_rtnl()
+ * to get the relevant information. For dev list reading, we again acquire
+ * rcu_read_lock() in rmnet_dellink() for netdev_master_upper_dev_get_rcu().
+ * We also use unregister_netdevice_many() to free all rmnet devices in
+ * rmnet_force_unassociate_device() so we dont lose the rtnl_lock() and free in
+ * same context.
+ */
+
+/* Local Definitions and Declarations */
+#define RMNET_LOCAL_LOGICAL_ENDPOINT -1
+
+struct rmnet_walk_data {
+ struct net_device *real_dev;
+ struct list_head *head;
+ struct rmnet_real_dev_info *real_dev_info;
+};
+
+static int rmnet_is_real_dev_registered(const struct net_device *real_dev)
+{
+ rx_handler_func_t *rx_handler;
+
+ rx_handler = rcu_dereference(real_dev->rx_handler);
+ return (rx_handler == rmnet_rx_handler);
+}
+
+/* Needs either rcu_read_lock() or rtnl lock */
+static struct rmnet_real_dev_info*
+__rmnet_get_real_dev_info(const struct net_device *real_dev)
+{
+ if (rmnet_is_real_dev_registered(real_dev))
+ return rcu_dereference_rtnl(real_dev->rx_handler_data);
+ else
+ return NULL;
+}
+
+/* Needs rtnl lock */
+static struct rmnet_real_dev_info*
+rmnet_get_real_dev_info_rtnl(const struct net_device *real_dev)
+{
+ return rtnl_dereference(real_dev->rx_handler_data);
+}
+
+static struct rmnet_endpoint*
+rmnet_get_endpoint(struct net_device *dev, int config_id)
+{
+ struct rmnet_real_dev_info *r;
+ struct rmnet_endpoint *ep;
+
+ if (!rmnet_is_real_dev_registered(dev)) {
+ ep = rmnet_vnd_get_endpoint(dev);
+ } else {
+ r = __rmnet_get_real_dev_info(dev);
+
+ if (!r)
+ return NULL;
+
+ if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT)
+ ep = &r->local_ep;
+ else
+ ep = &r->muxed_ep[config_id];
+ }
+
+ return ep;
+}
+
+static int rmnet_unregister_real_device(struct net_device *real_dev,
+ struct rmnet_real_dev_info *r)
+{
+ if (r->nr_rmnet_devs)
+ return -EINVAL;
+
+ kfree(r);
+
+ netdev_rx_handler_unregister(real_dev);
+
+ /* release reference on real_dev */
+ dev_put(real_dev);
+
+ netdev_dbg(real_dev, "Removed from rmnet\n");
+ return 0;
+}
+
+static int rmnet_register_real_device(struct net_device *real_dev)
+{
+ struct rmnet_real_dev_info *r;
+ int rc;
+
+ ASSERT_RTNL();
+
+ if (rmnet_is_real_dev_registered(real_dev))
+ return 0;
+
+ r = kzalloc(sizeof(*r), GFP_ATOMIC);
+ if (!r)
+ return -ENOMEM;
+
+ r->dev = real_dev;
+ rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, r);
+ if (rc) {
+ kfree(r);
+ return -EBUSY;
+ }
+
+ /* hold on to real dev for MAP data */
+ dev_hold(real_dev);
+
+ netdev_dbg(real_dev, "registered with rmnet\n");
+ return 0;
+}
+
+static int rmnet_set_ingress_data_format(struct net_device *dev, u32 idf)
+{
+ struct rmnet_real_dev_info *r;
+
+ netdev_dbg(dev, "Ingress format 0x%08X\n", idf);
+
+ r = __rmnet_get_real_dev_info(dev);
+
+ r->ingress_data_format = idf;
+
+ return 0;
+}
+
+static int rmnet_set_egress_data_format(struct net_device *dev, u32 edf,
+ u16 agg_size, u16 agg_count)
+{
+ struct rmnet_real_dev_info *r;
+
+ netdev_dbg(dev, "Egress format 0x%08X agg size %d cnt %d\n",
+ edf, agg_size, agg_count);
+
+ r = __rmnet_get_real_dev_info(dev);
+
+ r->egress_data_format = edf;
+
+ return 0;
+}
+
+static int __rmnet_set_endpoint_config(struct net_device *dev, int config_id,
+ struct rmnet_endpoint *ep)
+{
+ struct rmnet_endpoint *dev_ep;
+
+ dev_ep = rmnet_get_endpoint(dev, config_id);
+
+ if (!dev_ep)
+ return -EINVAL;
+
+ memcpy(dev_ep, ep, sizeof(struct rmnet_endpoint));
+ if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT)
+ dev_ep->mux_id = 0;
+ else
+ dev_ep->mux_id = config_id;
+
+ return 0;
+}
+
+static int rmnet_set_endpoint_config(struct net_device *dev,
+ int config_id, u8 rmnet_mode,
+ struct net_device *egress_dev)
+{
+ struct rmnet_endpoint ep;
+
+ netdev_dbg(dev, "id %d mode %d dev %s\n",
+ config_id, rmnet_mode, egress_dev->name);
+
+ if (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT ||
+ config_id >= RMNET_MAX_LOGICAL_EP)
+ return -EINVAL;
+
+ /* This config is cleared on every set, so its ok to not
+ * clear it on a device delete.
+ */
+ memset(&ep, 0, sizeof(struct rmnet_endpoint));
+ ep.rmnet_mode = rmnet_mode;
+ ep.egress_dev = egress_dev;
+
+ return __rmnet_set_endpoint_config(dev, config_id, &ep);
+}
+
+static int rmnet_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[],
+ struct netlink_ext_ack *extack)
+{
+ int ingress_format = RMNET_INGRESS_FORMAT_DEMUXING |
+ RMNET_INGRESS_FORMAT_DEAGGREGATION |
+ RMNET_INGRESS_FORMAT_MAP;
+ int egress_format = RMNET_EGRESS_FORMAT_MUXING |
+ RMNET_EGRESS_FORMAT_MAP;
+ struct rmnet_real_dev_info *r;
+ struct net_device *real_dev;
+ int mode = RMNET_EPMODE_VND;
+ int err = 0;
+ u16 mux_id;
+
+ real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
+ if (!real_dev || !dev)
+ return -ENODEV;
+
+ if (!data[IFLA_VLAN_ID])
+ return -EINVAL;
+
+ mux_id = nla_get_u16(data[IFLA_VLAN_ID]);
+
+ err = rmnet_register_real_device(real_dev);
+ if (err)
+ goto err0;
+
+ r = rmnet_get_real_dev_info_rtnl(real_dev);
+ err = rmnet_vnd_newlink(mux_id, dev, r);
+ if (err)
+ goto err1;
+
+ err = netdev_master_upper_dev_link(dev, real_dev, NULL, NULL);
+ if (err)
+ goto err2;
+
+ rmnet_vnd_set_mux(dev, mux_id);
+ rmnet_set_egress_data_format(real_dev, egress_format, 0, 0);
+ rmnet_set_ingress_data_format(real_dev, ingress_format);
+ rmnet_set_endpoint_config(real_dev, mux_id, mode, dev);
+ rmnet_set_endpoint_config(dev, mux_id, mode, real_dev);
+ return 0;
+
+err2:
+ rmnet_vnd_dellink(mux_id, r);
+err1:
+ rmnet_unregister_real_device(real_dev, r);
+err0:
+ return err;
+}
+
+static void rmnet_dellink(struct net_device *dev, struct list_head *head)
+{
+ struct rmnet_real_dev_info *r;
+ struct net_device *real_dev;
+ u8 mux_id;
+
+ rcu_read_lock();
+ real_dev = netdev_master_upper_dev_get_rcu(dev);
+ rcu_read_unlock();
+
+ if (!real_dev || !rmnet_is_real_dev_registered(real_dev))
+ return;
+
+ r = rmnet_get_real_dev_info_rtnl(real_dev);
+
+ mux_id = rmnet_vnd_get_mux(dev);
+ rmnet_vnd_dellink(mux_id, r);
+ netdev_upper_dev_unlink(dev, real_dev);
+ rmnet_unregister_real_device(real_dev, r);
+
+ unregister_netdevice_queue(dev, head);
+}
+
+static int rmnet_dev_walk_unreg(struct net_device *rmnet_dev, void *data)
+{
+ struct rmnet_walk_data *d = data;
+ u8 mux_id;
+
+ mux_id = rmnet_vnd_get_mux(rmnet_dev);
+
+ rmnet_vnd_dellink(mux_id, d->real_dev_info);
+ netdev_upper_dev_unlink(rmnet_dev, d->real_dev);
+ unregister_netdevice_queue(rmnet_dev, d->head);
+
+ return 0;
+}
+
+static void rmnet_force_unassociate_device(struct net_device *dev)
+{
+ struct net_device *real_dev = dev;
+ struct rmnet_real_dev_info *r;
+ struct rmnet_walk_data d;
+ LIST_HEAD(list);
+
+ if (!rmnet_is_real_dev_registered(real_dev))
+ return;
+
+ ASSERT_RTNL();
+
+ d.real_dev = real_dev;
+ d.head = &list;
+
+ r = rmnet_get_real_dev_info_rtnl(dev);
+ d.real_dev_info = r;
+
+ rcu_read_lock();
+ netdev_walk_all_lower_dev_rcu(real_dev, rmnet_dev_walk_unreg, &d);
+ rcu_read_unlock();
+ unregister_netdevice_many(&list);
+
+ rmnet_unregister_real_device(real_dev, r);
+}
+
+static int rmnet_config_notify_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(data);
+
+ if (!dev)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_UNREGISTER:
+ netdev_dbg(dev, "Kernel unregister\n");
+ rmnet_force_unassociate_device(dev);
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block rmnet_dev_notifier __read_mostly = {
+ .notifier_call = rmnet_config_notify_cb,
+};
+
+static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],
+ struct netlink_ext_ack *extack)
+{
+ u16 mux_id;
+
+ if (!data || !data[IFLA_VLAN_ID])
+ return -EINVAL;
+
+ mux_id = nla_get_u16(data[IFLA_VLAN_ID]);
+ if (mux_id > (RMNET_MAX_LOGICAL_EP - 1))
+ return -ERANGE;
+
+ return 0;
+}
+
+static size_t rmnet_get_size(const struct net_device *dev)
+{
+ return nla_total_size(2); /* IFLA_VLAN_ID */
+}
+
+struct rtnl_link_ops rmnet_link_ops __read_mostly = {
+ .kind = "rmnet",
+ .maxtype = __IFLA_VLAN_MAX,
+ .priv_size = sizeof(struct rmnet_priv),
+ .setup = rmnet_vnd_setup,
+ .validate = rmnet_rtnl_validate,
+ .newlink = rmnet_newlink,
+ .dellink = rmnet_dellink,
+ .get_size = rmnet_get_size,
+};
+
+struct rmnet_real_dev_info*
+rmnet_get_real_dev_info(struct net_device *real_dev)
+{
+ return __rmnet_get_real_dev_info(real_dev);
+}
+
+/* Startup/Shutdown */
+
+static int __init rmnet_init(void)
+{
+ int rc;
+
+ rc = register_netdevice_notifier(&rmnet_dev_notifier);
+ if (rc != 0)
+ return rc;
+
+ rc = rtnl_link_register(&rmnet_link_ops);
+ if (rc != 0) {
+ unregister_netdevice_notifier(&rmnet_dev_notifier);
+ return rc;
+ }
+ return rc;
+}
+
+static void __exit rmnet_exit(void)
+{
+ unregister_netdevice_notifier(&rmnet_dev_notifier);
+ rtnl_link_unregister(&rmnet_link_ops);
+}
+
+module_init(rmnet_init)
+module_exit(rmnet_exit)
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
new file mode 100644
index 0000000..985d372
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
@@ -0,0 +1,56 @@
+/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * RMNET Data configuration engine
+ *
+ */
+
+#include <linux/skbuff.h>
+
+#ifndef _RMNET_CONFIG_H_
+#define _RMNET_CONFIG_H_
+
+#define RMNET_MAX_LOGICAL_EP 255
+#define RMNET_MAX_VND 32
+
+/* Information about the next device to deliver the packet to.
+ * Exact usage of this parameter depends on the rmnet_mode.
+ */
+struct rmnet_endpoint {
+ u8 rmnet_mode;
+ u8 mux_id;
+ struct net_device *egress_dev;
+};
+
+/* One instance of this structure is instantiated for each real_dev associated
+ * with rmnet.
+ */
+struct rmnet_real_dev_info {
+ struct net_device *dev;
+ struct rmnet_endpoint local_ep;
+ struct rmnet_endpoint muxed_ep[RMNET_MAX_LOGICAL_EP];
+ u32 ingress_data_format;
+ u32 egress_data_format;
+ struct net_device *rmnet_devices[RMNET_MAX_VND];
+ u8 nr_rmnet_devs;
+};
+
+extern struct rtnl_link_ops rmnet_link_ops;
+
+struct rmnet_priv {
+ struct rmnet_endpoint local_ep;
+ u8 mux_id;
+};
+
+struct rmnet_real_dev_info*
+rmnet_get_real_dev_info(struct net_device *real_dev);
+
+#endif /* _RMNET_CONFIG_H_ */
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
new file mode 100644
index 0000000..7dab3bb
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
@@ -0,0 +1,271 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * RMNET Data ingress/egress handler
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/netdev_features.h>
+#include "rmnet_private.h"
+#include "rmnet_config.h"
+#include "rmnet_vnd.h"
+#include "rmnet_map.h"
+#include "rmnet_handlers.h"
+
+#define RMNET_IP_VERSION_4 0x40
+#define RMNET_IP_VERSION_6 0x60
+
+/* Helper Functions */
+
+static void rmnet_set_skb_proto(struct sk_buff *skb)
+{
+ switch (skb->data[0] & 0xF0) {
+ case RMNET_IP_VERSION_4:
+ skb->protocol = htons(ETH_P_IP);
+ break;
+ case RMNET_IP_VERSION_6:
+ skb->protocol = htons(ETH_P_IPV6);
+ break;
+ default:
+ skb->protocol = htons(ETH_P_MAP);
+ break;
+ }
+}
+
+/* Generic handler */
+
+static rx_handler_result_t
+rmnet_bridge_handler(struct sk_buff *skb, struct rmnet_endpoint *ep)
+{
+ if (!ep->egress_dev)
+ kfree_skb(skb);
+ else
+ rmnet_egress_handler(skb, ep);
+
+ return RX_HANDLER_CONSUMED;
+}
+
+static rx_handler_result_t
+rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_endpoint *ep)
+{
+ switch (ep->rmnet_mode) {
+ case RMNET_EPMODE_NONE:
+ return RX_HANDLER_PASS;
+
+ case RMNET_EPMODE_BRIDGE:
+ return rmnet_bridge_handler(skb, ep);
+
+ case RMNET_EPMODE_VND:
+ skb_reset_transport_header(skb);
+ skb_reset_network_header(skb);
+ rmnet_vnd_rx_fixup(skb, skb->dev);
+
+ skb->pkt_type = PACKET_HOST;
+ skb_set_mac_header(skb, 0);
+ netif_receive_skb(skb);
+ return RX_HANDLER_CONSUMED;
+
+ default:
+ kfree_skb(skb);
+ return RX_HANDLER_CONSUMED;
+ }
+}
+
+static rx_handler_result_t
+rmnet_ingress_deliver_packet(struct sk_buff *skb,
+ struct rmnet_real_dev_info *r)
+{
+ if (!r) {
+ kfree_skb(skb);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ skb->dev = r->local_ep.egress_dev;
+
+ return rmnet_deliver_skb(skb, &r->local_ep);
+}
+
+/* MAP handler */
+
+static rx_handler_result_t
+__rmnet_map_ingress_handler(struct sk_buff *skb,
+ struct rmnet_real_dev_info *r)
+{
+ struct rmnet_endpoint *ep;
+ u8 mux_id;
+ u16 len;
+
+ if (RMNET_MAP_GET_CD_BIT(skb)) {
+ if (r->ingress_data_format
+ & RMNET_INGRESS_FORMAT_MAP_COMMANDS)
+ return rmnet_map_command(skb, r);
+
+ kfree_skb(skb);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ mux_id = RMNET_MAP_GET_MUX_ID(skb);
+ len = RMNET_MAP_GET_LENGTH(skb) - RMNET_MAP_GET_PAD(skb);
+
+ if (mux_id >= RMNET_MAX_LOGICAL_EP) {
+ kfree_skb(skb);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ ep = &r->muxed_ep[mux_id];
+
+ if (r->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING)
+ skb->dev = ep->egress_dev;
+
+ /* Subtract MAP header */
+ skb_pull(skb, sizeof(struct rmnet_map_header));
+ skb_trim(skb, len);
+ rmnet_set_skb_proto(skb);
+ return rmnet_deliver_skb(skb, ep);
+}
+
+static rx_handler_result_t
+rmnet_map_ingress_handler(struct sk_buff *skb,
+ struct rmnet_real_dev_info *r)
+{
+ struct sk_buff *skbn;
+ int rc;
+
+ if (r->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) {
+ while ((skbn = rmnet_map_deaggregate(skb, r)) != NULL)
+ __rmnet_map_ingress_handler(skbn, r);
+
+ consume_skb(skb);
+ rc = RX_HANDLER_CONSUMED;
+ } else {
+ rc = __rmnet_map_ingress_handler(skb, r);
+ }
+
+ return rc;
+}
+
+static int rmnet_map_egress_handler(struct sk_buff *skb,
+ struct rmnet_real_dev_info *r,
+ struct rmnet_endpoint *ep,
+ struct net_device *orig_dev)
+{
+ int required_headroom, additional_header_len;
+ struct rmnet_map_header *map_header;
+
+ additional_header_len = 0;
+ required_headroom = sizeof(struct rmnet_map_header);
+
+ if (skb_headroom(skb) < required_headroom) {
+ if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL))
+ return RMNET_MAP_CONSUMED;
+ }
+
+ map_header = rmnet_map_add_map_header(skb, additional_header_len, 0);
+ if (!map_header)
+ return RMNET_MAP_CONSUMED;
+
+ if (r->egress_data_format & RMNET_EGRESS_FORMAT_MUXING) {
+ if (ep->mux_id == 0xff)
+ map_header->mux_id = 0;
+ else
+ map_header->mux_id = ep->mux_id;
+ }
+
+ skb->protocol = htons(ETH_P_MAP);
+
+ return RMNET_MAP_SUCCESS;
+}
+
+/* Ingress / Egress Entry Points */
+
+/* Processes packet as per ingress data format for receiving device. Logical
+ * endpoint is determined from packet inspection. Packet is then sent to the
+ * egress device listed in the logical endpoint configuration.
+ */
+rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb)
+{
+ struct rmnet_real_dev_info *r;
+ struct sk_buff *skb = *pskb;
+ struct net_device *dev;
+ int rc;
+
+ if (!skb)
+ return RX_HANDLER_CONSUMED;
+
+ dev = skb->dev;
+ r = rmnet_get_real_dev_info(dev);
+
+ if (r->ingress_data_format & RMNET_INGRESS_FORMAT_MAP) {
+ rc = rmnet_map_ingress_handler(skb, r);
+ } else {
+ switch (ntohs(skb->protocol)) {
+ case ETH_P_MAP:
+ if (r->local_ep.rmnet_mode ==
+ RMNET_EPMODE_BRIDGE) {
+ rc = rmnet_ingress_deliver_packet(skb, r);
+ } else {
+ kfree_skb(skb);
+ rc = RX_HANDLER_CONSUMED;
+ }
+ break;
+
+ case ETH_P_IP:
+ case ETH_P_IPV6:
+ rc = rmnet_ingress_deliver_packet(skb, r);
+ break;
+
+ default:
+ rc = RX_HANDLER_PASS;
+ }
+ }
+
+ return rc;
+}
+
+/* Modifies packet as per logical endpoint configuration and egress data format
+ * for egress device configured in logical endpoint. Packet is then transmitted
+ * on the egress device.
+ */
+void rmnet_egress_handler(struct sk_buff *skb,
+ struct rmnet_endpoint *ep)
+{
+ struct rmnet_real_dev_info *r;
+ struct net_device *orig_dev;
+
+ orig_dev = skb->dev;
+ skb->dev = ep->egress_dev;
+
+ r = rmnet_get_real_dev_info(skb->dev);
+ if (!r) {
+ kfree_skb(skb);
+ return;
+ }
+
+ if (r->egress_data_format & RMNET_EGRESS_FORMAT_MAP) {
+ switch (rmnet_map_egress_handler(skb, r, ep, orig_dev)) {
+ case RMNET_MAP_CONSUMED:
+ return;
+
+ case RMNET_MAP_SUCCESS:
+ break;
+
+ default:
+ kfree_skb(skb);
+ return;
+ }
+ }
+
+ if (ep->rmnet_mode == RMNET_EPMODE_VND)
+ rmnet_vnd_tx_fixup(skb, orig_dev);
+
+ dev_queue_xmit(skb);
+}
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h
new file mode 100644
index 0000000..f2638cf
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * RMNET Data ingress/egress handler
+ *
+ */
+
+#ifndef _RMNET_HANDLERS_H_
+#define _RMNET_HANDLERS_H_
+
+#include "rmnet_config.h"
+
+void rmnet_egress_handler(struct sk_buff *skb,
+ struct rmnet_endpoint *ep);
+
+rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb);
+
+#endif /* _RMNET_HANDLERS_H_ */
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
new file mode 100644
index 0000000..2aabad2
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
@@ -0,0 +1,88 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _RMNET_MAP_H_
+#define _RMNET_MAP_H_
+
+struct rmnet_map_control_command {
+ u8 command_name;
+ u8 cmd_type:2;
+ u8 reserved:6;
+ u16 reserved2;
+ u32 transaction_id;
+ union {
+ struct {
+ u16 ip_family:2;
+ u16 reserved:14;
+ u16 flow_control_seq_num;
+ u32 qos_id;
+ } flow_control;
+ u8 data[0];
+ };
+} __aligned(1);
+
+enum rmnet_map_results {
+ RMNET_MAP_SUCCESS,
+ RMNET_MAP_CONSUMED,
+ RMNET_MAP_GENERAL_FAILURE,
+ RMNET_MAP_NOT_ENABLED,
+ RMNET_MAP_FAILED_AGGREGATION,
+ RMNET_MAP_FAILED_MUX
+};
+
+enum rmnet_map_commands {
+ RMNET_MAP_COMMAND_NONE,
+ RMNET_MAP_COMMAND_FLOW_DISABLE,
+ RMNET_MAP_COMMAND_FLOW_ENABLE,
+ /* These should always be the last 2 elements */
+ RMNET_MAP_COMMAND_UNKNOWN,
+ RMNET_MAP_COMMAND_ENUM_LENGTH
+};
+
+struct rmnet_map_header {
+ u8 pad_len:6;
+ u8 reserved_bit:1;
+ u8 cd_bit:1;
+ u8 mux_id;
+ u16 pkt_len;
+} __aligned(1);
+
+#define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \
+ (Y)->data)->mux_id)
+#define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \
+ (Y)->data)->cd_bit)
+#define RMNET_MAP_GET_PAD(Y) (((struct rmnet_map_header *) \
+ (Y)->data)->pad_len)
+#define RMNET_MAP_GET_CMD_START(Y) ((struct rmnet_map_control_command *) \
+ ((Y)->data + \
+ sizeof(struct rmnet_map_header)))
+#define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header *) \
+ (Y)->data)->pkt_len))
+
+#define RMNET_MAP_COMMAND_REQUEST 0
+#define RMNET_MAP_COMMAND_ACK 1
+#define RMNET_MAP_COMMAND_UNSUPPORTED 2
+#define RMNET_MAP_COMMAND_INVALID 3
+
+#define RMNET_MAP_NO_PAD_BYTES 0
+#define RMNET_MAP_ADD_PAD_BYTES 1
+
+u8 rmnet_map_demultiplex(struct sk_buff *skb);
+struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
+ struct rmnet_real_dev_info *rdinfo);
+
+struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,
+ int hdrlen, int pad);
+rx_handler_result_t rmnet_map_command(struct sk_buff *skb,
+ struct rmnet_real_dev_info *rdinfo);
+
+#endif /* _RMNET_MAP_H_ */
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c
new file mode 100644
index 0000000..ccded40
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c
@@ -0,0 +1,107 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/netdevice.h>
+#include "rmnet_config.h"
+#include "rmnet_map.h"
+#include "rmnet_private.h"
+#include "rmnet_vnd.h"
+
+static u8 rmnet_map_do_flow_control(struct sk_buff *skb,
+ struct rmnet_real_dev_info *rdinfo,
+ int enable)
+{
+ struct rmnet_map_control_command *cmd;
+ struct rmnet_endpoint *ep;
+ struct net_device *vnd;
+ u16 ip_family;
+ u16 fc_seq;
+ u32 qos_id;
+ u8 mux_id;
+ int r;
+
+ mux_id = RMNET_MAP_GET_MUX_ID(skb);
+ cmd = RMNET_MAP_GET_CMD_START(skb);
+
+ if (mux_id >= RMNET_MAX_LOGICAL_EP) {
+ kfree_skb(skb);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ ep = &rdinfo->muxed_ep[mux_id];
+ vnd = ep->egress_dev;
+
+ ip_family = cmd->flow_control.ip_family;
+ fc_seq = ntohs(cmd->flow_control.flow_control_seq_num);
+ qos_id = ntohl(cmd->flow_control.qos_id);
+
+ /* Ignore the ip family and pass the sequence number for both v4 and v6
+ * sequence. User space does not support creating dedicated flows for
+ * the 2 protocols
+ */
+ r = rmnet_vnd_do_flow_control(vnd, enable);
+ if (r) {
+ kfree_skb(skb);
+ return RMNET_MAP_COMMAND_UNSUPPORTED;
+ } else {
+ return RMNET_MAP_COMMAND_ACK;
+ }
+}
+
+static void rmnet_map_send_ack(struct sk_buff *skb,
+ unsigned char type,
+ struct rmnet_real_dev_info *rdinfo)
+{
+ struct rmnet_map_control_command *cmd;
+ int xmit_status;
+
+ skb->protocol = htons(ETH_P_MAP);
+
+ cmd = RMNET_MAP_GET_CMD_START(skb);
+ cmd->cmd_type = type & 0x03;
+
+ netif_tx_lock(skb->dev);
+ xmit_status = skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev);
+ netif_tx_unlock(skb->dev);
+}
+
+/* Process MAP command frame and send N/ACK message as appropriate. Message cmd
+ * name is decoded here and appropriate handler is called.
+ */
+rx_handler_result_t rmnet_map_command(struct sk_buff *skb,
+ struct rmnet_real_dev_info *rdinfo)
+{
+ struct rmnet_map_control_command *cmd;
+ unsigned char command_name;
+ unsigned char rc = 0;
+
+ cmd = RMNET_MAP_GET_CMD_START(skb);
+ command_name = cmd->command_name;
+
+ switch (command_name) {
+ case RMNET_MAP_COMMAND_FLOW_ENABLE:
+ rc = rmnet_map_do_flow_control(skb, rdinfo, 1);
+ break;
+
+ case RMNET_MAP_COMMAND_FLOW_DISABLE:
+ rc = rmnet_map_do_flow_control(skb, rdinfo, 0);
+ break;
+
+ default:
+ rc = RMNET_MAP_COMMAND_UNSUPPORTED;
+ kfree_skb(skb);
+ break;
+ }
+ if (rc == RMNET_MAP_COMMAND_ACK)
+ rmnet_map_send_ack(skb, rc, rdinfo);
+ return RX_HANDLER_CONSUMED;
+}
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
new file mode 100644
index 0000000..a29c476
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -0,0 +1,105 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * RMNET Data MAP protocol
+ *
+ */
+
+#include <linux/netdevice.h>
+#include "rmnet_config.h"
+#include "rmnet_map.h"
+#include "rmnet_private.h"
+
+#define RMNET_MAP_DEAGGR_SPACING 64
+#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2)
+
+/* Adds MAP header to front of skb->data
+ * Padding is calculated and set appropriately in MAP header. Mux ID is
+ * initialized to 0.
+ */
+struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,
+ int hdrlen, int pad)
+{
+ struct rmnet_map_header *map_header;
+ u32 padding, map_datalen;
+ u8 *padbytes;
+
+ if (skb_headroom(skb) < sizeof(struct rmnet_map_header))
+ return NULL;
+
+ map_datalen = skb->len - hdrlen;
+ map_header = (struct rmnet_map_header *)
+ skb_push(skb, sizeof(struct rmnet_map_header));
+ memset(map_header, 0, sizeof(struct rmnet_map_header));
+
+ if (pad == RMNET_MAP_NO_PAD_BYTES) {
+ map_header->pkt_len = htons(map_datalen);
+ return map_header;
+ }
+
+ padding = ALIGN(map_datalen, 4) - map_datalen;
+
+ if (padding == 0)
+ goto done;
+
+ if (skb_tailroom(skb) < padding)
+ return NULL;
+
+ padbytes = (u8 *)skb_put(skb, padding);
+ memset(padbytes, 0, padding);
+
+done:
+ map_header->pkt_len = htons(map_datalen + padding);
+ map_header->pad_len = padding & 0x3F;
+
+ return map_header;
+}
+
+/* Deaggregates a single packet
+ * A whole new buffer is allocated for each portion of an aggregated frame.
+ * Caller should keep calling deaggregate() on the source skb until 0 is
+ * returned, indicating that there are no more packets to deaggregate. Caller
+ * is responsible for freeing the original skb.
+ */
+struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
+ struct rmnet_real_dev_info *rdinfo)
+{
+ struct rmnet_map_header *maph;
+ struct sk_buff *skbn;
+ u32 packet_len;
+
+ if (skb->len == 0)
+ return NULL;
+
+ maph = (struct rmnet_map_header *)skb->data;
+ packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header);
+
+ if (((int)skb->len - (int)packet_len) < 0)
+ return NULL;
+
+ skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC);
+ if (!skbn)
+ return NULL;
+
+ skbn->dev = skb->dev;
+ skb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM);
+ skb_put(skbn, packet_len);
+ memcpy(skbn->data, skb->data, packet_len);
+ skb_pull(skb, packet_len);
+
+ /* Some hardware can send us empty frames. Catch them */
+ if (ntohs(maph->pkt_len) == 0) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ return skbn;
+}
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h
new file mode 100644
index 0000000..ed820b5
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _RMNET_PRIVATE_H_
+#define _RMNET_PRIVATE_H_
+
+#define RMNET_MAX_VND 32
+#define RMNET_MAX_PACKET_SIZE 16384
+#define RMNET_DFLT_PACKET_SIZE 1500
+#define RMNET_NEEDED_HEADROOM 16
+#define RMNET_TX_QUEUE_LEN 1000
+
+/* Constants */
+#define RMNET_EGRESS_FORMAT__RESERVED__ BIT(0)
+#define RMNET_EGRESS_FORMAT_MAP BIT(1)
+#define RMNET_EGRESS_FORMAT_AGGREGATION BIT(2)
+#define RMNET_EGRESS_FORMAT_MUXING BIT(3)
+#define RMNET_EGRESS_FORMAT_MAP_CKSUMV3 BIT(4)
+#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 BIT(5)
+
+#define RMNET_INGRESS_FIX_ETHERNET BIT(0)
+#define RMNET_INGRESS_FORMAT_MAP BIT(1)
+#define RMNET_INGRESS_FORMAT_DEAGGREGATION BIT(2)
+#define RMNET_INGRESS_FORMAT_DEMUXING BIT(3)
+#define RMNET_INGRESS_FORMAT_MAP_COMMANDS BIT(4)
+#define RMNET_INGRESS_FORMAT_MAP_CKSUMV3 BIT(5)
+#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 BIT(6)
+
+/* Pass the frame up the stack with no modifications to skb->dev */
+#define RMNET_EPMODE_NONE (0)
+/* Replace skb->dev to a virtual rmnet device and pass up the stack */
+#define RMNET_EPMODE_VND (1)
+/* Pass the frame directly to another device with dev_queue_xmit() */
+#define RMNET_EPMODE_BRIDGE (2)
+
+#endif /* _RMNET_PRIVATE_H_ */
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
new file mode 100644
index 0000000..c8b573d
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
@@ -0,0 +1,170 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * RMNET Data virtual network driver
+ *
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <net/pkt_sched.h>
+#include "rmnet_config.h"
+#include "rmnet_handlers.h"
+#include "rmnet_private.h"
+#include "rmnet_map.h"
+#include "rmnet_vnd.h"
+
+/* RX/TX Fixup */
+
+void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev)
+{
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+}
+
+void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev)
+{
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+}
+
+/* Network Device Operations */
+
+static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct rmnet_priv *priv;
+
+ priv = netdev_priv(dev);
+ if (priv->local_ep.egress_dev) {
+ rmnet_egress_handler(skb, &priv->local_ep);
+ } else {
+ dev->stats.tx_dropped++;
+ kfree_skb(skb);
+ }
+ return NETDEV_TX_OK;
+}
+
+static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu)
+{
+ if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE)
+ return -EINVAL;
+
+ rmnet_dev->mtu = new_mtu;
+ return 0;
+}
+
+static const struct net_device_ops rmnet_vnd_ops = {
+ .ndo_start_xmit = rmnet_vnd_start_xmit,
+ .ndo_change_mtu = rmnet_vnd_change_mtu,
+};
+
+/* Called by kernel whenever a new rmnet<n> device is created. Sets MTU,
+ * flags, ARP type, needed headroom, etc...
+ */
+void rmnet_vnd_setup(struct net_device *rmnet_dev)
+{
+ struct rmnet_priv *priv;
+
+ priv = netdev_priv(rmnet_dev);
+ netdev_dbg(rmnet_dev, "Setting up device %s\n", rmnet_dev->name);
+
+ rmnet_dev->netdev_ops = &rmnet_vnd_ops;
+ rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE;
+ rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM;
+ random_ether_addr(rmnet_dev->dev_addr);
+ rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN;
+
+ /* Raw IP mode */
+ rmnet_dev->header_ops = NULL; /* No header */
+ rmnet_dev->type = ARPHRD_RAWIP;
+ rmnet_dev->hard_header_len = 0;
+ rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+
+ rmnet_dev->needs_free_netdev = true;
+}
+
+/* Exposed API */
+
+int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
+ struct rmnet_real_dev_info *r)
+{
+ int rc;
+
+ if (r->rmnet_devices[id])
+ return -EINVAL;
+
+ rc = register_netdevice(rmnet_dev);
+ if (!rc) {
+ r->rmnet_devices[id] = rmnet_dev;
+ r->nr_rmnet_devs++;
+ rmnet_dev->rtnl_link_ops = &rmnet_link_ops;
+ }
+
+ return rc;
+}
+
+int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r)
+{
+ if (id >= RMNET_MAX_VND || !r->rmnet_devices[id])
+ return -EINVAL;
+
+ r->rmnet_devices[id] = NULL;
+ r->nr_rmnet_devs--;
+ return 0;
+}
+
+u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev)
+{
+ struct rmnet_priv *priv;
+
+ priv = netdev_priv(rmnet_dev);
+ return priv->mux_id;
+}
+
+void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id)
+{
+ struct rmnet_priv *priv;
+
+ priv = netdev_priv(rmnet_dev);
+ priv->mux_id = mux_id;
+}
+
+/* Gets the logical endpoint configuration for a RmNet virtual network device
+ * node. Caller should confirm that devices is a RmNet VND before calling.
+ */
+struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *rmnet_dev)
+{
+ struct rmnet_priv *priv;
+
+ if (!rmnet_dev)
+ return NULL;
+
+ priv = netdev_priv(rmnet_dev);
+
+ return &priv->local_ep;
+}
+
+int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable)
+{
+ netdev_dbg(rmnet_dev, "Setting VND TX queue state to %d\n", enable);
+ /* Although we expect similar number of enable/disable
+ * commands, optimize for the disable. That is more
+ * latency sensitive than enable
+ */
+ if (unlikely(enable))
+ netif_wake_queue(rmnet_dev);
+ else
+ netif_stop_queue(rmnet_dev);
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h
new file mode 100644
index 0000000..b102b42
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * RMNET Data Virtual Network Device APIs
+ *
+ */
+
+#ifndef _RMNET_VND_H_
+#define _RMNET_VND_H_
+
+int rmnet_vnd_do_flow_control(struct net_device *dev, int enable);
+struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *dev);
+int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
+ struct rmnet_real_dev_info *r);
+int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r);
+void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev);
+void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev);
+u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev);
+void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id);
+void rmnet_vnd_setup(struct net_device *dev);
+#endif /* _RMNET_VND_H_ */
--
1.9.1
^ permalink raw reply related
* Re: [PATCH v2 net-next 2/6] udp: Constify skb argument in lookup functions
From: David Miller @ 2017-08-30 0:58 UTC (permalink / raw)
To: tom; +Cc: netdev
In-Reply-To: <20170829232711.1465-3-tom@quantonium.net>
From: Tom Herbert <tom@quantonium.net>
Date: Tue, 29 Aug 2017 16:27:07 -0700
> For UDP socket lookup functions, and associateed functions that take an
> skbuf as argument, declare the skb argument as constant.
>
> One caveat is that reuseport_select_sock can be called from the UDP
> lookup functions with an skb argument. This function temporarily
> modifies the skbuff data pointer (in bpf_run via a pull/push sequence).
> To resolve compiler warning I added a local skbuf declaration that is
> not const and assigned to the skb argument with an explicit cast.
>
> Signed-off-by: Tom Herbert <tom@quantonium.net>
Please don't do this.
If reuseport_select_sock() modifies anything in the SKB, especially
skb->data, it infects the entire call chain. So you can't mark it
const in this family of calls.
^ permalink raw reply
* Re: [PATCH v2 net-next 3/6] flow_dissector: Add protocol specific flow dissection offload
From: David Miller @ 2017-08-30 1:00 UTC (permalink / raw)
To: tom; +Cc: netdev
In-Reply-To: <20170829232711.1465-4-tom@quantonium.net>
From: Tom Herbert <tom@quantonium.net>
Date: Tue, 29 Aug 2017 16:27:08 -0700
> +#define GOTO_BY_RESULT(ret) do { \
> + switch (ret) { \
> + case FLOW_DISSECT_RET_OUT_GOOD: \
> + goto out_good; \
> + case FLOW_DISSECT_RET_PROTO_AGAIN: \
> + goto proto_again; \
> + case FLOW_DISSECT_RET_IPPROTO_AGAIN: \
> + goto ip_proto_again; \
> + case FLOW_DISSECT_RET_OUT_BAD: \
> + default: \
> + goto out_bad; \
> + } \
> +} while (0)
> +
> +#define GOTO_OR_CONT_BY_RESULT(ret) do { \
> + enum flow_dissect_ret __ret = (ret); \
> + \
> + if (__ret != FLOW_DISSECT_RET_CONTINUE) \
> + GOTO_BY_RESULT(__ret); \
> +} while (0)
Please don't hide major control flow changes inside of a macro. This
means returns and gotos.
It makes code impossible to audit.
Yes, this applies even if the macro has the word "GOTO" in it :-)
^ permalink raw reply
* Re: [PATCH v2 net-next 1/8] bpf: Add support for recursively running cgroup sock filters
From: David Ahern @ 2017-08-30 1:03 UTC (permalink / raw)
To: Alexei Starovoitov; +Cc: netdev, daniel, ast, tj, davem, luto
In-Reply-To: <20170829041118.m6bsjvif2bxwtk6g@ast-mbp>
On 8/28/17 10:11 PM, Alexei Starovoitov wrote:
>
> Agree on the above, but you're mixing semantics of the new recurse
> flag and implementation of it. Ex: we don't have to copy this flag
> from prog->attr into cgroup. So this reset or non-reset discussion
> only makes sense in the context of your current implementation.
> We can implement the logic differently. Like don't copy that flag
> at all and at attach time walk parent->parent->parent and see
> what programs are attached. All of them should have prog->attr & recurse_bit set
> In such implementation detach from 'b' is a nop from reset/non-reset
> point of view. When socket creation in 'c' is invoked the program
> 'c' is called first then the code keeps walking parents until root
> invoking 'a' along the way.
So you are suggesting there is no recursive flag per cgroup? How do you
know you need to walk cgroups? How do you know when to stop running
programs?
> I'm not saying it will be an efficient implementation. The point
> is to discuss UAPI independent of implementation.
>
>> ###
>>
>> Also, let's agree on this intention. Based on the new ground rule, I
>> want to point out this example:
>>
>> If 'a' gets a program installed with no recurse flag set, ONLY processes
>> in 'a' have the 'a' program run. Processes in groups 'b', 'c' and 'd'
>> all stop at cgroup 'b' program.
>
> I'm proposing that such situation should not be allowed to happen.
> In a->b->c->d cgroup scenario if override+recurse prog attached to 'b'
> then only the same override+recurse can be attached to c, d, a.
> So at detach time there can be gaps (like only 'b' and 'd' have
> override+recurse progs), but walking up until root from any point
> will guarantee that only override+recurse programs are seen.
>
That seems very limiting to me. Seems like you are suggesting the entire
cgroup tree is recursive or non-recursive, but never a mix.
^ permalink raw reply
* Re: [PATCH net-next 0/3 v10] Add support for rmnet driver
From: David Miller @ 2017-08-30 1:05 UTC (permalink / raw)
To: subashab
Cc: netdev, fengguang.wu, dcbw, jiri, stephen, David.Laight, marcel,
andrew
In-Reply-To: <1504054078-10173-1-git-send-email-subashab@codeaurora.org>
From: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
Date: Tue, 29 Aug 2017 18:47:55 -0600
> I have updated the locking scheme as follows -
Series applied, but this is not how you write a header posting for a
patch set.
This posting is where you say at a high level what the patch series is
doing, how it is doing it, and why it is doing it that way.
You can explain what changes happened, and why, but that belongs
in the changelog at the end of this posting. Here you've made
an explaination for one change the entire content of the text.
You not even saying what rmnet is, why we would want to add it to the
kernel, and what it's all about. So now when someone tries to read
the merge commit that contains this text, they will have no context
about you and me talking about locking and they will thus ask
themselves "what is this person talking about here? it's not
explaining the patch series at all"
^ permalink raw reply
* Re: [PATCH net-next 0/3 v10] Add support for rmnet driver
From: David Miller @ 2017-08-30 1:12 UTC (permalink / raw)
To: subashab
Cc: netdev, fengguang.wu, dcbw, jiri, stephen, David.Laight, marcel,
andrew
In-Reply-To: <20170829.180557.1826943085634265902.davem@davemloft.net>
Sigh, I had to revert.
You only allow RMNET to take on the values 'y' and 'n'.
You must allow for it to be 'm' and modular as well.
^ permalink raw reply
* Re: [PATCH net-next 0/3 v10] Add support for rmnet driver
From: Subash Abhinov Kasiviswanathan @ 2017-08-30 1:18 UTC (permalink / raw)
To: David Miller
Cc: netdev, fengguang.wu, dcbw, jiri, stephen, David.Laight, marcel,
andrew
In-Reply-To: <20170829.181244.658164638453646342.davem@davemloft.net>
On 2017-08-29 19:12, David Miller wrote:
> Sigh, I had to revert.
>
> You only allow RMNET to take on the values 'y' and 'n'.
>
> You must allow for it to be 'm' and modular as well.
Hi David
I'll fix this now.
Sorry about the cover letter. I'll explain it better in subsequent
submission.
^ permalink raw reply
* Re: [PATCH net] sch_hhf: fix null pointer dereference on init failure
From: Cong Wang @ 2017-08-30 1:24 UTC (permalink / raw)
To: Nikolay Aleksandrov
Cc: Linux Kernel Network Developers, Eric Dumazet, Jamal Hadi Salim,
Jiri Pirko, Roopa Prabhu
In-Reply-To: <1504033335-19098-1-git-send-email-nikolay@cumulusnetworks.com>
On Tue, Aug 29, 2017 at 12:02 PM, Nikolay Aleksandrov
<nikolay@cumulusnetworks.com> wrote:
> First I did it with the check in the for () conditional, but this is more
> visible and explicit. Let me know if you'd like the shorter version. :-)
Or, if you want to make the patch size smaller, just check NULL
before for():
if (!q->hh_flows)
return;
for (...)
Up to you, I have no strong opinion here, slightly prefer a smaller
one for backport.
^ permalink raw reply
* Re: [PATCH net-next] virtio-net: invoke zerocopy callback on xmit path if no tx napi
From: Jason Wang @ 2017-08-30 1:45 UTC (permalink / raw)
To: Willem de Bruijn, Michael S. Tsirkin
Cc: Koichiro Den, virtualization, Network Development
In-Reply-To: <CAF=yD-+Wk9sc9dXMUq1+x_hh=3ThTXa6BnZkygP3tgVpjbp93g@mail.gmail.com>
On 2017年08月30日 03:35, Willem de Bruijn wrote:
> On Fri, Aug 25, 2017 at 9:03 PM, Willem de Bruijn
> <willemdebruijn.kernel@gmail.com> wrote:
>> On Fri, Aug 25, 2017 at 7:32 PM, Michael S. Tsirkin <mst@redhat.com> wrote:
>>> On Fri, Aug 25, 2017 at 06:44:36PM -0400, Willem de Bruijn wrote:
>>>>>>>>> We don't enable network watchdog on virtio but we could and maybe
>>>>>>>>> should.
>>>>>>>> Can you elaborate?
>>>>>>> The issue is that holding onto buffers for very long times makes guests
>>>>>>> think they are stuck. This is funamentally because from guest point of
>>>>>>> view this is a NIC, so it is supposed to transmit things out in
>>>>>>> a timely manner. If host backs the virtual NIC by something that is not
>>>>>>> a NIC, with traffic shaping etc introducing unbounded latencies,
>>>>>>> guest will be confused.
>>>>>> That assumes that guests are fragile in this regard. A linux guest
>>>>>> does not make such assumptions.
>>>>> Yes it does. Examples above:
>>>>> > > - a single slow flow can occupy the whole ring, you will not
>>>>> > > be able to make any new buffers available for the fast flow
>>>> Oh, right. Though those are due to vring_desc pool exhaustion
>>>> rather than an upper bound on latency of any single packet.
>>>>
>>>> Limiting the number of zerocopy packets in flight to some fraction
>>>> of the ring ensures that fast flows can always grab a slot.
>>>> Running
>>>> out of ubuf_info slots reverts to copy, so indirectly does this. But
>>>> I read it correclty the zerocopy pool may be equal to or larger than
>>>> the descriptor pool. Should we refine the zcopy_used test
>>>>
>>>> (nvq->upend_idx + 1) % UIO_MAXIOV != nvq->done_idx
>>>>
>>>> to also return false if the number of outstanding ubuf_info is greater
>>>> than, say, vq->num >> 1?
>>>
>>> We'll need to think about where to put the threshold, but I think it's
>>> a good idea.
>>>
>>> Maybe even a fixed number, e.g. max(vq->num >> 1, X) to limit host
>>> resources.
>>>
>>> In a sense it still means once you run out of slots zcopt gets disabled possibly permanently.
>>>
>>> Need to experiment with some numbers.
>> I can take a stab with two flows, one delayed in a deep host qdisc
>> queue. See how this change affects the other flow and also how
>> sensitive that is to the chosen threshold value.
> Incomplete results at this stage, but I do see this correlation between
> flows. It occurs even while not running out of zerocopy descriptors,
> which I cannot yet explain.
>
> Running two threads in a guest, each with a udp socket, each
> sending up to 100 datagrams, or until EAGAIN, every msec.
>
> Sender A sends 1B datagrams.
> Sender B sends VHOST_GOODCOPY_LEN, which is enough
> to trigger zcopy_used in vhost net.
>
> A local receive process on the host receives both flows. To avoid
> a deep copy when looping the packet onto the receive path,
> changed skb_orphan_frags_rx to always return false (gross hack).
>
> The flow with the larger packets is redirected through netem on ifb0:
>
> modprobe ifb
> ip link set dev ifb0 up
> tc qdisc add dev ifb0 root netem limit $LIMIT rate 1MBit
>
> tc qdisc add dev tap0 ingress
> tc filter add dev tap0 parent ffff: protocol ip \
> u32 match ip dport 8000 0xffff \
> action mirred egress redirect dev ifb0
>
> For 10 second run, packet count with various ifb0 queue lengths $LIMIT:
>
> no filter
> rx.A: ~840,000
> rx.B: ~840,000
Just to make sure I understand the case here. What did rx.B mean here? I
thought all traffic sent by Sender B has been redirected to ifb0?
>
> limit 1
> rx.A: ~500,000
> rx.B: ~3100
> ifb0: 3273 sent, 371141 dropped
>
> limit 100
> rx.A: ~9000
> rx.B: ~4200
> ifb0: 4630 sent, 1491 dropped
>
> limit 1000
> rx.A: ~6800
> rx.B: ~4200
> ifb0: 4651 sent, 0 dropped
>
> Sender B is always correctly rate limited to 1 MBps or less. With a
> short queue, it ends up dropping a lot and sending even less.
>
> When a queue builds up for sender B, sender A throughput is strongly
> correlated with queue length. With queue length 1, it can send almost
> at unthrottled speed. But even at limit 100 its throughput is on the
> same order as sender B.
>
> What is surprising to me is that this happens even though the number
> of ubuf_info in use at limit 100 is around 100 at all times. In other words,
> it does not exhaust the pool.
>
> When forcing zcopy_used to be false for all packets, this effect of
> sender A throughput being correlated with sender B does not happen.
>
> no filter
> rx.A: ~850,000
> rx.B: ~850,000
>
> limit 100
> rx.A: ~850,000
> rx.B: ~4200
> ifb0: 4518 sent, 876182 dropped
>
> Also relevant is that with zerocopy, the sender processes back off
> and report the same count as the receiver. Without zerocopy,
> both senders send at full speed, even if only 4200 packets from flow
> B arrive at the receiver.
>
> This is with the default virtio_net driver, so without napi-tx.
What kind of qdisc do you use in guest? I suspect we should use
something which could do fair queueing (e.g sfq).
>
> It appears that the zerocopy notifications are pausing the guest.
> Will look at that now.
Another factor is the tx interrupt coalescing parameters of ifb0, maybe
we should disable it during the test.
Thanks
>
> By the way, I have had an unrelated patch outstanding for a while
> to have virtio-net support the VIRTIO_CONFIG_S_NEEDS_RESET
> command. Will send that as RFC.
^ permalink raw reply
* RE: Question about ip_defrag
From: liujian (CE) @ 2017-08-30 1:52 UTC (permalink / raw)
To: Florian Westphal
Cc: Jesper Dangaard Brouer, netdev@vger.kernel.org,
Wangkefeng (Kevin), weiyongjun (A)
In-Reply-To: <20170829134635.GB9993@breakpoint.cc>
Best Regards,
liujian
> -----Original Message-----
> From: netdev-owner@vger.kernel.org [mailto:netdev-owner@vger.kernel.org]
> On Behalf Of Florian Westphal
> Sent: Tuesday, August 29, 2017 9:47 PM
> To: liujian (CE)
> Cc: Florian Westphal; Jesper Dangaard Brouer; netdev@vger.kernel.org;
> Wangkefeng (Kevin); weiyongjun (A)
> Subject: Re: Question about ip_defrag
>
> liujian (CE) <liujian56@huawei.com> wrote:
>
> [ trimming cc list ]
>
> > Now, I have not the real environment.
> > I use iperf generate fragment packets; and I always change NIC rx
> > irq's affinity cpu, to make sure frag_mem_limit reach to thresh.
> > my test machine, CPU num is 384.
>
> Oh well, that explains it.
>
> > > > + if (frag_mem_limit(nf) > nf->low_thresh) {
> > > > inet_frag_schedule_worker(f);
> > > > + update_frag_mem_limit(nf, SKB_TRUESIZE(1500) * 16);
> > > > + }
>
> You need to reduce this to a lower value.
> Your cpu count * batch_value needs to be less than low_thresh to avoid
> problems.
>
> Wtih 384 cpus its close to 12 mbyte...
>
> Perhaps do this:
>
> update_frag_mem_limit(nf, 2 * 1024*1024 / NR_CPUS);
>
>
> However, I think its better to revert the percpu counter change and move back
> to a single atomic_t count.
Ok.
Florian and Jesper, many thanks for this issue.
^ 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