Netdev List
 help / color / mirror / Atom feed
* [PATCH 05/10] l2tp: pppol2tp_connect() handles ipv6 sockaddr variants
From: James Chapman @ 2012-04-20  9:02 UTC (permalink / raw)
  To: netdev; +Cc: bcrl
In-Reply-To: <1334912573-28804-1-git-send-email-jchapman@katalix.com>

Userspace uses connect() to associate a pppol2tp socket with a tunnel
socket. This needs to allow the caller to supply the new IPv6
sockaddr_pppol2tp structures if IPv6 is used.

Signed-off-by: James Chapman <jchapman@katalix.com>
---
 net/l2tp/l2tp_ppp.c |   36 ++++++++++++++++++++++++++++--------
 1 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 27b9dec..9f2c421 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -628,7 +628,6 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 {
 	struct sock *sk = sock->sk;
 	struct sockaddr_pppol2tp *sp = (struct sockaddr_pppol2tp *) uservaddr;
-	struct sockaddr_pppol2tpv3 *sp3 = (struct sockaddr_pppol2tpv3 *) uservaddr;
 	struct pppox_sock *po = pppox_sk(sk);
 	struct l2tp_session *session = NULL;
 	struct l2tp_tunnel *tunnel;
@@ -657,7 +656,13 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 	if (sk->sk_user_data)
 		goto end; /* socket is already attached */
 
-	/* Get params from socket address. Handle L2TPv2 and L2TPv3 */
+	/* Get params from socket address. Handle L2TPv2 and L2TPv3.
+	 * This is nasty because there are different sockaddr_pppol2tp
+	 * structs for L2TPv2, L2TPv3, over IPv4 and IPv6. We use
+	 * the sockaddr size to determine which structure the caller
+	 * is using.
+	 */
+	peer_tunnel_id = 0;
 	if (sockaddr_len == sizeof(struct sockaddr_pppol2tp)) {
 		fd = sp->pppol2tp.fd;
 		tunnel_id = sp->pppol2tp.s_tunnel;
@@ -665,12 +670,31 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 		session_id = sp->pppol2tp.s_session;
 		peer_session_id = sp->pppol2tp.d_session;
 	} else if (sockaddr_len == sizeof(struct sockaddr_pppol2tpv3)) {
+		struct sockaddr_pppol2tpv3 *sp3 =
+			(struct sockaddr_pppol2tpv3 *) sp;
 		ver = 3;
 		fd = sp3->pppol2tp.fd;
 		tunnel_id = sp3->pppol2tp.s_tunnel;
 		peer_tunnel_id = sp3->pppol2tp.d_tunnel;
 		session_id = sp3->pppol2tp.s_session;
 		peer_session_id = sp3->pppol2tp.d_session;
+	} else if (sockaddr_len == sizeof(struct sockaddr_pppol2tpin6)) {
+		struct sockaddr_pppol2tpin6 *sp6 =
+			(struct sockaddr_pppol2tpin6 *) sp;
+		fd = sp6->pppol2tp.fd;
+		tunnel_id = sp6->pppol2tp.s_tunnel;
+		peer_tunnel_id = sp6->pppol2tp.d_tunnel;
+		session_id = sp6->pppol2tp.s_session;
+		peer_session_id = sp6->pppol2tp.d_session;
+	} else if (sockaddr_len == sizeof(struct sockaddr_pppol2tpv3in6)) {
+		struct sockaddr_pppol2tpv3in6 *sp6 =
+			(struct sockaddr_pppol2tpv3in6 *) sp;
+		ver = 3;
+		fd = sp6->pppol2tp.fd;
+		tunnel_id = sp6->pppol2tp.s_tunnel;
+		peer_tunnel_id = sp6->pppol2tp.d_tunnel;
+		session_id = sp6->pppol2tp.s_session;
+		peer_session_id = sp6->pppol2tp.d_session;
 	} else {
 		error = -EINVAL;
 		goto end; /* bad socket address */
@@ -711,12 +735,8 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 	if (tunnel->recv_payload_hook == NULL)
 		tunnel->recv_payload_hook = pppol2tp_recv_payload_hook;
 
-	if (tunnel->peer_tunnel_id == 0) {
-		if (ver == 2)
-			tunnel->peer_tunnel_id = sp->pppol2tp.d_tunnel;
-		else
-			tunnel->peer_tunnel_id = sp3->pppol2tp.d_tunnel;
-	}
+	if (tunnel->peer_tunnel_id == 0)
+		tunnel->peer_tunnel_id = peer_tunnel_id;
 
 	/* Create session if it doesn't already exist. We handle the
 	 * case where a session was previously created by the netlink
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 04/10] pppox: Replace __attribute__((packed)) in if_pppox.h
From: James Chapman @ 2012-04-20  9:02 UTC (permalink / raw)
  To: netdev; +Cc: bcrl
In-Reply-To: <1334912573-28804-1-git-send-email-jchapman@katalix.com>

Checkpatch warns about the use of __attribute__((packed)). So use the
recommended __packed syntax instead.

Signed-off-by: James Chapman <jchapman@katalix.com>
---
 include/linux/if_pppox.h |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h
index 6720d57..09c474c 100644
--- a/include/linux/if_pppox.h
+++ b/include/linux/if_pppox.h
@@ -70,7 +70,7 @@ struct sockaddr_pppox {
 		struct pppoe_addr  pppoe;
 		struct pptp_addr   pptp;
 	} sa_addr;
-} __attribute__((packed));
+} __packed;
 
 /* The use of the above union isn't viable because the size of this
  * struct must stay fixed over time -- applications use sizeof(struct
@@ -81,13 +81,13 @@ struct sockaddr_pppol2tp {
 	__kernel_sa_family_t sa_family; /* address family, AF_PPPOX */
 	unsigned int    sa_protocol;    /* protocol identifier */
 	struct pppol2tp_addr pppol2tp;
-} __attribute__((packed));
+} __packed;
 
 struct sockaddr_pppol2tpin6 {
 	__kernel_sa_family_t sa_family; /* address family, AF_PPPOX */
 	unsigned int    sa_protocol;    /* protocol identifier */
 	struct pppol2tpin6_addr pppol2tp;
-} __attribute__((packed));
+} __packed;
 
 /* The L2TPv3 protocol changes tunnel and session ids from 16 to 32
  * bits. So we need a different sockaddr structure.
@@ -96,13 +96,13 @@ struct sockaddr_pppol2tpv3 {
 	__kernel_sa_family_t sa_family; /* address family, AF_PPPOX */
 	unsigned int    sa_protocol;    /* protocol identifier */
 	struct pppol2tpv3_addr pppol2tp;
-} __attribute__((packed));
+} __packed;
 
 struct sockaddr_pppol2tpv3in6 {
 	__kernel_sa_family_t sa_family; /* address family, AF_PPPOX */
 	unsigned int    sa_protocol;    /* protocol identifier */
 	struct pppol2tpv3in6_addr pppol2tp;
-} __attribute__((packed));
+} __packed;
 
 /*********************************************************************
  *
@@ -152,7 +152,7 @@ struct pppoe_hdr {
 	__be16 sid;
 	__be16 length;
 	struct pppoe_tag tag[0];
-} __attribute__((packed));
+} __packed;
 
 /* Length of entire PPPoE + PPP header */
 #define PPPOE_SES_HLEN	8
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 07/10] l2tp: netlink api for l2tpv3 ipv6 unmanaged tunnels
From: James Chapman @ 2012-04-20  9:02 UTC (permalink / raw)
  To: netdev; +Cc: bcrl, Chris Elston
In-Reply-To: <1334912573-28804-1-git-send-email-jchapman@katalix.com>

From: Chris Elston <celston@katalix.com>

This patch adds support for unmanaged L2TPv3 tunnels over IPv6 using
the netlink API. We already support unmanaged L2TPv3 tunnels over
IPv4. A patch to iproute2 to make use of this feature will be
submitted separately.

Signed-off-by: Chris Elston <celston@katalix.com>
Signed-off-by: James Chapman <jchapman@katalix.com>
---
 include/linux/l2tp.h    |    2 +
 net/l2tp/l2tp_core.c    |   78 ++++++++++++++++++++++++++++++++++++----------
 net/l2tp/l2tp_core.h    |    4 ++
 net/l2tp/l2tp_netlink.c |   48 ++++++++++++++++++++++++++--
 4 files changed, 111 insertions(+), 21 deletions(-)

diff --git a/include/linux/l2tp.h b/include/linux/l2tp.h
index e77d7f9..16b8347 100644
--- a/include/linux/l2tp.h
+++ b/include/linux/l2tp.h
@@ -108,6 +108,8 @@ enum {
 	L2TP_ATTR_MTU,			/* u16 */
 	L2TP_ATTR_MRU,			/* u16 */
 	L2TP_ATTR_STATS,		/* nested */
+	L2TP_ATTR_IP6_SADDR,		/* struct in6_addr */
+	L2TP_ATTR_IP6_DADDR,		/* struct in6_addr */
 	__L2TP_ATTR_MAX,
 };
 
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 5e27c05..5a7d908 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1365,31 +1365,68 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t
 {
 	int err = -EINVAL;
 	struct sockaddr_in udp_addr;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct sockaddr_in6 udp6_addr;
+#endif
 	struct sockaddr_l2tpip ip_addr;
 	struct socket *sock = NULL;
 
 	switch (cfg->encap) {
 	case L2TP_ENCAPTYPE_UDP:
-		err = sock_create(AF_INET, SOCK_DGRAM, 0, sockp);
-		if (err < 0)
-			goto out;
+#if IS_ENABLED(CONFIG_IPV6)
+		if (cfg->local_ip6 && cfg->peer_ip6) {
+			err = sock_create(AF_INET6, SOCK_DGRAM, 0, sockp);
+			if (err < 0)
+				goto out;
 
-		sock = *sockp;
+			sock = *sockp;
 
-		memset(&udp_addr, 0, sizeof(udp_addr));
-		udp_addr.sin_family = AF_INET;
-		udp_addr.sin_addr = cfg->local_ip;
-		udp_addr.sin_port = htons(cfg->local_udp_port);
-		err = kernel_bind(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr));
-		if (err < 0)
-			goto out;
+			memset(&udp6_addr, 0, sizeof(udp6_addr));
+			udp6_addr.sin6_family = AF_INET6;
+			memcpy(&udp6_addr.sin6_addr, cfg->local_ip6,
+			       sizeof(udp6_addr.sin6_addr));
+			udp6_addr.sin6_port = htons(cfg->local_udp_port);
+			err = kernel_bind(sock, (struct sockaddr *) &udp6_addr,
+					  sizeof(udp6_addr));
+			if (err < 0)
+				goto out;
 
-		udp_addr.sin_family = AF_INET;
-		udp_addr.sin_addr = cfg->peer_ip;
-		udp_addr.sin_port = htons(cfg->peer_udp_port);
-		err = kernel_connect(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr), 0);
-		if (err < 0)
-			goto out;
+			udp6_addr.sin6_family = AF_INET6;
+			memcpy(&udp6_addr.sin6_addr, cfg->peer_ip6,
+			       sizeof(udp6_addr.sin6_addr));
+			udp6_addr.sin6_port = htons(cfg->peer_udp_port);
+			err = kernel_connect(sock,
+					     (struct sockaddr *) &udp6_addr,
+					     sizeof(udp6_addr), 0);
+			if (err < 0)
+				goto out;
+		} else
+#endif
+		{
+			err = sock_create(AF_INET, SOCK_DGRAM, 0, sockp);
+			if (err < 0)
+				goto out;
+
+			sock = *sockp;
+
+			memset(&udp_addr, 0, sizeof(udp_addr));
+			udp_addr.sin_family = AF_INET;
+			udp_addr.sin_addr = cfg->local_ip;
+			udp_addr.sin_port = htons(cfg->local_udp_port);
+			err = kernel_bind(sock, (struct sockaddr *) &udp_addr,
+					  sizeof(udp_addr));
+			if (err < 0)
+				goto out;
+
+			udp_addr.sin_family = AF_INET;
+			udp_addr.sin_addr = cfg->peer_ip;
+			udp_addr.sin_port = htons(cfg->peer_udp_port);
+			err = kernel_connect(sock,
+					     (struct sockaddr *) &udp_addr,
+					     sizeof(udp_addr), 0);
+			if (err < 0)
+				goto out;
+		}
 
 		if (!cfg->use_udp_checksums)
 			sock->sk->sk_no_check = UDP_CSUM_NOXMIT;
@@ -1397,6 +1434,13 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t
 		break;
 
 	case L2TP_ENCAPTYPE_IP:
+#if IS_ENABLED(CONFIG_IPV6)
+		if (cfg->local_ip6 && cfg->peer_ip6) {
+			/* IP encap over IPv6 not yet supported */
+			err = -EPROTONOSUPPORT;
+			goto out;
+		}
+#endif
 		err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_L2TP, sockp);
 		if (err < 0)
 			goto out;
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index a8c943b..0bf60fc 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -151,6 +151,10 @@ struct l2tp_tunnel_cfg {
 	/* Used only for kernel-created sockets */
 	struct in_addr		local_ip;
 	struct in_addr		peer_ip;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct in6_addr		*local_ip6;
+	struct in6_addr		*peer_ip6;
+#endif
 	u16			local_udp_port;
 	u16			peer_udp_port;
 	unsigned int		use_udp_checksums:1;
diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c
index 1dbb977..24edad0 100644
--- a/net/l2tp/l2tp_netlink.c
+++ b/net/l2tp/l2tp_netlink.c
@@ -133,10 +133,25 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info
 	if (info->attrs[L2TP_ATTR_FD]) {
 		fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]);
 	} else {
-		if (info->attrs[L2TP_ATTR_IP_SADDR])
-			cfg.local_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_SADDR]);
-		if (info->attrs[L2TP_ATTR_IP_DADDR])
-			cfg.peer_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_DADDR]);
+#if IS_ENABLED(CONFIG_IPV6)
+		if (info->attrs[L2TP_ATTR_IP6_SADDR] &&
+		    info->attrs[L2TP_ATTR_IP6_DADDR]) {
+			cfg.local_ip6 = nla_data(
+				info->attrs[L2TP_ATTR_IP6_SADDR]);
+			cfg.peer_ip6 = nla_data(
+				info->attrs[L2TP_ATTR_IP6_DADDR]);
+		} else
+#endif
+		if (info->attrs[L2TP_ATTR_IP_SADDR] &&
+		    info->attrs[L2TP_ATTR_IP_DADDR]) {
+			cfg.local_ip.s_addr = nla_get_be32(
+				info->attrs[L2TP_ATTR_IP_SADDR]);
+			cfg.peer_ip.s_addr = nla_get_be32(
+				info->attrs[L2TP_ATTR_IP_DADDR]);
+		} else {
+			ret = -EINVAL;
+			goto out;
+		}
 		if (info->attrs[L2TP_ATTR_UDP_SPORT])
 			cfg.local_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_SPORT]);
 		if (info->attrs[L2TP_ATTR_UDP_DPORT])
@@ -225,6 +240,9 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
 	struct nlattr *nest;
 	struct sock *sk = NULL;
 	struct inet_sock *inet;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct ipv6_pinfo *np = NULL;
+#endif
 	struct l2tp_stats stats;
 	unsigned int start;
 
@@ -273,6 +291,11 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
 	if (!sk)
 		goto out;
 
+#if IS_ENABLED(CONFIG_IPV6)
+	if (sk->sk_family == AF_INET6)
+		np = inet6_sk(sk);
+#endif
+
 	inet = inet_sk(sk);
 
 	switch (tunnel->encap) {
@@ -284,6 +307,15 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
 			goto nla_put_failure;
 		/* NOBREAK */
 	case L2TP_ENCAPTYPE_IP:
+#if IS_ENABLED(CONFIG_IPV6)
+		if (np) {
+			if (nla_put(skb, L2TP_ATTR_IP6_SADDR, sizeof(np->saddr),
+				    &np->saddr) ||
+			    nla_put(skb, L2TP_ATTR_IP6_DADDR, sizeof(np->daddr),
+				    &np->daddr))
+				goto nla_put_failure;
+		} else
+#endif
 		if (nla_put_be32(skb, L2TP_ATTR_IP_SADDR, inet->inet_saddr) ||
 		    nla_put_be32(skb, L2TP_ATTR_IP_DADDR, inet->inet_daddr))
 			goto nla_put_failure;
@@ -752,6 +784,14 @@ static struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = {
 	[L2TP_ATTR_MTU]			= { .type = NLA_U16, },
 	[L2TP_ATTR_MRU]			= { .type = NLA_U16, },
 	[L2TP_ATTR_STATS]		= { .type = NLA_NESTED, },
+	[L2TP_ATTR_IP6_SADDR] = {
+		.type = NLA_BINARY,
+		.len = sizeof(struct in6_addr),
+	},
+	[L2TP_ATTR_IP6_DADDR] = {
+		.type = NLA_BINARY,
+		.len = sizeof(struct in6_addr),
+	},
 	[L2TP_ATTR_IFNAME] = {
 		.type = NLA_NUL_STRING,
 		.len = IFNAMSIZ - 1,
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 06/10] l2tp: show IPv6 addresses in l2tp debugfs file
From: James Chapman @ 2012-04-20  9:02 UTC (permalink / raw)
  To: netdev; +Cc: bcrl, Chris Elston
In-Reply-To: <1334912573-28804-1-git-send-email-jchapman@katalix.com>

From: Chris Elston <celston@katalix.com>

If an L2TP tunnel uses IPv6, make sure the l2tp debugfs file shows the
IPv6 address correctly.

Signed-off-by: Chris Elston <celston@katalix.com>
Signed-off-by: James Chapman <jchapman@katalix.com>
---
 net/l2tp/l2tp_debugfs.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/net/l2tp/l2tp_debugfs.c b/net/l2tp/l2tp_debugfs.c
index 7613013..c0d57ba 100644
--- a/net/l2tp/l2tp_debugfs.c
+++ b/net/l2tp/l2tp_debugfs.c
@@ -122,6 +122,14 @@ static void l2tp_dfs_seq_tunnel_show(struct seq_file *m, void *v)
 	seq_printf(m, "\nTUNNEL %u peer %u", tunnel->tunnel_id, tunnel->peer_tunnel_id);
 	if (tunnel->sock) {
 		struct inet_sock *inet = inet_sk(tunnel->sock);
+
+#if IS_ENABLED(CONFIG_IPV6)
+		if (tunnel->sock->sk_family == AF_INET6) {
+			struct ipv6_pinfo *np = inet6_sk(tunnel->sock);
+			seq_printf(m, " from %pI6c to %pI6c\n",
+				&np->saddr, &np->daddr);
+		} else
+#endif
 		seq_printf(m, " from %pI4 to %pI4\n",
 			   &inet->inet_saddr, &inet->inet_daddr);
 		if (tunnel->encap == L2TP_ENCAPTYPE_UDP)
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 08/10] ipv6: Export ipv6 functions for use by other protocols
From: James Chapman @ 2012-04-20  9:02 UTC (permalink / raw)
  To: netdev; +Cc: bcrl, Chris Elston
In-Reply-To: <1334912573-28804-1-git-send-email-jchapman@katalix.com>

From: Chris Elston <celston@katalix.com>

For implementing other protocols on top of IPv6, such as L2TPv3's IP
encapsulation over ipv6, we'd like to call some IPv6 functions which
are not currently exported. This patch exports them.

Signed-off-by: Chris Elston <celston@katalix.com>
Signed-off-by: James Chapman <jchapman@katalix.com>
---
 net/ipv6/datagram.c      |    4 ++++
 net/ipv6/exthdrs.c       |    1 +
 net/ipv6/ip6_flowlabel.c |    1 +
 net/ipv6/ip6_output.c    |    3 +++
 4 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 7fba35a..b8b61ac 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -22,6 +22,7 @@
 #include <linux/ipv6.h>
 #include <linux/route.h>
 #include <linux/slab.h>
+#include <linux/export.h>
 
 #include <net/ipv6.h>
 #include <net/ndisc.h>
@@ -202,6 +203,7 @@ out:
 	fl6_sock_release(flowlabel);
 	return err;
 }
+EXPORT_SYMBOL_GPL(ip6_datagram_connect);
 
 void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
 		     __be16 port, u32 info, u8 *payload)
@@ -414,6 +416,7 @@ out_free_skb:
 out:
 	return err;
 }
+EXPORT_SYMBOL_GPL(ipv6_recv_error);
 
 /*
  *	Handle IPV6_RECVPATHMTU
@@ -868,3 +871,4 @@ int datagram_send_ctl(struct net *net, struct sock *sk,
 exit_f:
 	return err;
 }
+EXPORT_SYMBOL_GPL(datagram_send_ctl);
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index aa0a51e..a93bd23 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -883,6 +883,7 @@ struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
 
 	return opt;
 }
+EXPORT_SYMBOL_GPL(ipv6_fixup_options);
 
 /**
  * fl6_update_dst - update flowi destination address with info given
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index 1dd6329..cb43df6 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -294,6 +294,7 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
 	opt_space->opt_flen = fopt->opt_flen;
 	return opt_space;
 }
+EXPORT_SYMBOL_GPL(fl6_merge_options);
 
 static unsigned long check_linger(unsigned long ttl)
 {
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index b7ca461..2a16aa9 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1535,6 +1535,7 @@ error:
 	IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
 	return err;
 }
+EXPORT_SYMBOL_GPL(ip6_append_data);
 
 static void ip6_cork_release(struct inet_sock *inet, struct ipv6_pinfo *np)
 {
@@ -1638,6 +1639,7 @@ error:
 	IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
 	goto out;
 }
+EXPORT_SYMBOL_GPL(ip6_push_pending_frames);
 
 void ip6_flush_pending_frames(struct sock *sk)
 {
@@ -1652,3 +1654,4 @@ void ip6_flush_pending_frames(struct sock *sk)
 
 	ip6_cork_release(inet_sk(sk), inet6_sk(sk));
 }
+EXPORT_SYMBOL_GPL(ip6_flush_pending_frames);
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 09/10] l2tp: introduce L2TPv3 IP encapsulation support for IPv6
From: James Chapman @ 2012-04-20  9:02 UTC (permalink / raw)
  To: netdev; +Cc: bcrl, Chris Elston
In-Reply-To: <1334912573-28804-1-git-send-email-jchapman@katalix.com>

From: Chris Elston <celston@katalix.com>

L2TPv3 defines an IP encapsulation packet format where data is carried
directly over IP (no UDP). The kernel already has support for L2TP IP
encapsulation over IPv4 (l2tp_ip). This patch introduces support for
L2TP IP encapsulation over IPv6.

The implementation is derived from ipv6/raw and ipv4/l2tp_ip.

Signed-off-by: Chris Elston <celston@katalix.com>
Signed-off-by: James Chapman <jchapman@katalix.com>
---
 include/linux/l2tp.h |   17 +
 net/l2tp/Makefile    |    3 +
 net/l2tp/l2tp_ip6.c  |  792 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 812 insertions(+), 0 deletions(-)
 create mode 100644 net/l2tp/l2tp_ip6.c

diff --git a/include/linux/l2tp.h b/include/linux/l2tp.h
index 16b8347..7eab668 100644
--- a/include/linux/l2tp.h
+++ b/include/linux/l2tp.h
@@ -11,6 +11,7 @@
 #include <linux/socket.h>
 #ifdef __KERNEL__
 #include <linux/in.h>
+#include <linux/in6.h>
 #else
 #include <netinet/in.h>
 #endif
@@ -39,6 +40,22 @@ struct sockaddr_l2tpip {
 			      sizeof(__u32)];
 };
 
+/**
+ * struct sockaddr_l2tpip6 - the sockaddr structure for L2TP-over-IPv6 sockets
+ * @l2tp_family:  address family number AF_L2TPIP.
+ * @l2tp_addr:    protocol specific address information
+ * @l2tp_conn_id: connection id of tunnel
+ */
+struct sockaddr_l2tpip6 {
+	/* The first fields must match struct sockaddr_in6 */
+	__kernel_sa_family_t l2tp_family; /* AF_INET6 */
+	__be16		l2tp_unused;	/* INET port number (unused) */
+	__be32		l2tp_flowinfo;	/* IPv6 flow information */
+	struct in6_addr	l2tp_addr;	/* IPv6 address */
+	__u32		l2tp_scope_id;	/* scope id (new in RFC2553) */
+	__u32		l2tp_conn_id;	/* Connection ID of tunnel */
+};
+
 /*****************************************************************************
  *  NETLINK_GENERIC netlink family.
  *****************************************************************************/
diff --git a/net/l2tp/Makefile b/net/l2tp/Makefile
index 110e7bc..2870f41 100644
--- a/net/l2tp/Makefile
+++ b/net/l2tp/Makefile
@@ -10,3 +10,6 @@ obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP)) += l2tp_ip.o
 obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_V3)) += l2tp_netlink.o
 obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_ETH)) += l2tp_eth.o
 obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_DEBUGFS)) += l2tp_debugfs.o
+ifneq ($(CONFIG_IPV6),)
+obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP)) += l2tp_ip6.o
+endif
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
new file mode 100644
index 0000000..88f0abe
--- /dev/null
+++ b/net/l2tp/l2tp_ip6.c
@@ -0,0 +1,792 @@
+/*
+ * L2TPv3 IP encapsulation support for IPv6
+ *
+ * Copyright (c) 2012 Katalix Systems Ltd
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/icmp.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/random.h>
+#include <linux/socket.h>
+#include <linux/l2tp.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/inet_common.h>
+#include <net/inet_hashtables.h>
+#include <net/tcp_states.h>
+#include <net/protocol.h>
+#include <net/xfrm.h>
+
+#include <net/transp_v6.h>
+#include <net/addrconf.h>
+#include <net/ip6_route.h>
+
+#include "l2tp_core.h"
+
+struct l2tp_ip6_sock {
+	/* inet_sock has to be the first member of l2tp_ip6_sock */
+	struct inet_sock	inet;
+
+	u32			conn_id;
+	u32			peer_conn_id;
+
+	/* ipv6_pinfo has to be the last member of l2tp_ip6_sock, see
+	   inet6_sk_generic */
+	struct ipv6_pinfo	inet6;
+};
+
+static DEFINE_RWLOCK(l2tp_ip6_lock);
+static struct hlist_head l2tp_ip6_table;
+static struct hlist_head l2tp_ip6_bind_table;
+
+static inline struct l2tp_ip6_sock *l2tp_ip6_sk(const struct sock *sk)
+{
+	return (struct l2tp_ip6_sock *)sk;
+}
+
+static struct sock *__l2tp_ip6_bind_lookup(struct net *net,
+					   struct in6_addr *laddr,
+					   int dif, u32 tunnel_id)
+{
+	struct hlist_node *node;
+	struct sock *sk;
+
+	sk_for_each_bound(sk, node, &l2tp_ip6_bind_table) {
+		struct in6_addr *addr = inet6_rcv_saddr(sk);
+		struct l2tp_ip6_sock *l2tp = l2tp_ip6_sk(sk);
+
+		if (l2tp == NULL)
+			continue;
+
+		if ((l2tp->conn_id == tunnel_id) &&
+		    net_eq(sock_net(sk), net) &&
+		    !(addr && ipv6_addr_equal(addr, laddr)) &&
+		    !(sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif))
+			goto found;
+	}
+
+	sk = NULL;
+found:
+	return sk;
+}
+
+static inline struct sock *l2tp_ip6_bind_lookup(struct net *net,
+						struct in6_addr *laddr,
+						int dif, u32 tunnel_id)
+{
+	struct sock *sk = __l2tp_ip6_bind_lookup(net, laddr, dif, tunnel_id);
+	if (sk)
+		sock_hold(sk);
+
+	return sk;
+}
+
+/* When processing receive frames, there are two cases to
+ * consider. Data frames consist of a non-zero session-id and an
+ * optional cookie. Control frames consist of a regular L2TP header
+ * preceded by 32-bits of zeros.
+ *
+ * L2TPv3 Session Header Over IP
+ *
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                           Session ID                          |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |               Cookie (optional, maximum 64 bits)...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *                                                                 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * L2TPv3 Control Message Header Over IP
+ *
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                      (32 bits of zeros)                       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |T|L|x|x|S|x|x|x|x|x|x|x|  Ver  |             Length            |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                     Control Connection ID                     |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |               Ns              |               Nr              |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * All control frames are passed to userspace.
+ */
+static int l2tp_ip6_recv(struct sk_buff *skb)
+{
+	struct sock *sk;
+	u32 session_id;
+	u32 tunnel_id;
+	unsigned char *ptr, *optr;
+	struct l2tp_session *session;
+	struct l2tp_tunnel *tunnel = NULL;
+	int length;
+	int offset;
+
+	/* Point to L2TP header */
+	optr = ptr = skb->data;
+
+	if (!pskb_may_pull(skb, 4))
+		goto discard;
+
+	session_id = ntohl(*((__be32 *) ptr));
+	ptr += 4;
+
+	/* RFC3931: L2TP/IP packets have the first 4 bytes containing
+	 * the session_id. If it is 0, the packet is a L2TP control
+	 * frame and the session_id value can be discarded.
+	 */
+	if (session_id == 0) {
+		__skb_pull(skb, 4);
+		goto pass_up;
+	}
+
+	/* Ok, this is a data packet. Lookup the session. */
+	session = l2tp_session_find(&init_net, NULL, session_id);
+	if (session == NULL)
+		goto discard;
+
+	tunnel = session->tunnel;
+	if (tunnel == NULL)
+		goto discard;
+
+	/* Trace packet contents, if enabled */
+	if (tunnel->debug & L2TP_MSG_DATA) {
+		length = min(32u, skb->len);
+		if (!pskb_may_pull(skb, length))
+			goto discard;
+
+		printk(KERN_DEBUG "%s: ip recv: ", tunnel->name);
+
+		offset = 0;
+		do {
+			printk(" %02X", ptr[offset]);
+		} while (++offset < length);
+
+		printk("\n");
+	}
+
+	l2tp_recv_common(session, skb, ptr, optr, 0, skb->len,
+			 tunnel->recv_payload_hook);
+	return 0;
+
+pass_up:
+	/* Get the tunnel_id from the L2TP header */
+	if (!pskb_may_pull(skb, 12))
+		goto discard;
+
+	if ((skb->data[0] & 0xc0) != 0xc0)
+		goto discard;
+
+	tunnel_id = ntohl(*(__be32 *) &skb->data[4]);
+	tunnel = l2tp_tunnel_find(&init_net, tunnel_id);
+	if (tunnel != NULL)
+		sk = tunnel->sock;
+	else {
+		struct ipv6hdr *iph = ipv6_hdr(skb);
+
+		read_lock_bh(&l2tp_ip6_lock);
+		sk = __l2tp_ip6_bind_lookup(&init_net, &iph->daddr,
+					    0, tunnel_id);
+		read_unlock_bh(&l2tp_ip6_lock);
+	}
+
+	if (sk == NULL)
+		goto discard;
+
+	sock_hold(sk);
+
+	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
+		goto discard_put;
+
+	nf_reset(skb);
+
+	return sk_receive_skb(sk, skb, 1);
+
+discard_put:
+	sock_put(sk);
+
+discard:
+	kfree_skb(skb);
+	return 0;
+}
+
+static int l2tp_ip6_open(struct sock *sk)
+{
+	/* Prevent autobind. We don't have ports. */
+	inet_sk(sk)->inet_num = IPPROTO_L2TP;
+
+	write_lock_bh(&l2tp_ip6_lock);
+	sk_add_node(sk, &l2tp_ip6_table);
+	write_unlock_bh(&l2tp_ip6_lock);
+
+	return 0;
+}
+
+static void l2tp_ip6_close(struct sock *sk, long timeout)
+{
+	write_lock_bh(&l2tp_ip6_lock);
+	hlist_del_init(&sk->sk_bind_node);
+	sk_del_node_init(sk);
+	write_unlock_bh(&l2tp_ip6_lock);
+
+	sk_common_release(sk);
+}
+
+static void l2tp_ip6_destroy_sock(struct sock *sk)
+{
+	lock_sock(sk);
+	ip6_flush_pending_frames(sk);
+	release_sock(sk);
+
+	inet6_destroy_sock(sk);
+}
+
+static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+	struct inet_sock *inet = inet_sk(sk);
+	struct ipv6_pinfo *np = inet6_sk(sk);
+	struct sockaddr_l2tpip6 *addr = (struct sockaddr_l2tpip6 *) uaddr;
+	__be32 v4addr = 0;
+	int addr_type;
+	int err;
+
+	if (addr_len < sizeof(*addr))
+		return -EINVAL;
+
+	addr_type = ipv6_addr_type(&addr->l2tp_addr);
+
+	/* l2tp_ip6 sockets are IPv6 only */
+	if (addr_type == IPV6_ADDR_MAPPED)
+		return -EADDRNOTAVAIL;
+
+	/* L2TP is point-point, not multicast */
+	if (addr_type & IPV6_ADDR_MULTICAST)
+		return -EADDRNOTAVAIL;
+
+	err = -EADDRINUSE;
+	read_lock_bh(&l2tp_ip6_lock);
+	if (__l2tp_ip6_bind_lookup(&init_net, &addr->l2tp_addr,
+				   sk->sk_bound_dev_if, addr->l2tp_conn_id))
+		goto out_in_use;
+	read_unlock_bh(&l2tp_ip6_lock);
+
+	lock_sock(sk);
+
+	err = -EINVAL;
+	if (sk->sk_state != TCP_CLOSE)
+		goto out_unlock;
+
+	/* Check if the address belongs to the host. */
+	rcu_read_lock();
+	if (addr_type != IPV6_ADDR_ANY) {
+		struct net_device *dev = NULL;
+
+		if (addr_type & IPV6_ADDR_LINKLOCAL) {
+			if (addr_len >= sizeof(struct sockaddr_in6) &&
+			    addr->l2tp_scope_id) {
+				/* Override any existing binding, if another
+				 * one is supplied by user.
+				 */
+				sk->sk_bound_dev_if = addr->l2tp_scope_id;
+			}
+
+			/* Binding to link-local address requires an
+			   interface */
+			if (!sk->sk_bound_dev_if)
+				goto out_unlock_rcu;
+
+			err = -ENODEV;
+			dev = dev_get_by_index_rcu(sock_net(sk),
+						   sk->sk_bound_dev_if);
+			if (!dev)
+				goto out_unlock_rcu;
+		}
+
+		/* ipv4 addr of the socket is invalid.  Only the
+		 * unspecified and mapped address have a v4 equivalent.
+		 */
+		v4addr = LOOPBACK4_IPV6;
+		err = -EADDRNOTAVAIL;
+		if (!ipv6_chk_addr(sock_net(sk), &addr->l2tp_addr, dev, 0))
+			goto out_unlock_rcu;
+	}
+	rcu_read_unlock();
+
+	inet->inet_rcv_saddr = inet->inet_saddr = v4addr;
+	np->rcv_saddr = addr->l2tp_addr;
+	np->saddr = addr->l2tp_addr;
+
+	l2tp_ip6_sk(sk)->conn_id = addr->l2tp_conn_id;
+
+	write_lock_bh(&l2tp_ip6_lock);
+	sk_add_bind_node(sk, &l2tp_ip6_bind_table);
+	sk_del_node_init(sk);
+	write_unlock_bh(&l2tp_ip6_lock);
+
+	release_sock(sk);
+	return 0;
+
+out_unlock_rcu:
+	rcu_read_unlock();
+out_unlock:
+	release_sock(sk);
+	return err;
+
+out_in_use:
+	read_unlock_bh(&l2tp_ip6_lock);
+	return err;
+}
+
+static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr,
+			    int addr_len)
+{
+	struct sockaddr_l2tpip6 *lsa = (struct sockaddr_l2tpip6 *) uaddr;
+	struct sockaddr_in6	*usin = (struct sockaddr_in6 *) uaddr;
+	struct in6_addr	*daddr;
+	int	addr_type;
+	int rc;
+
+	if (addr_len < sizeof(*lsa))
+		return -EINVAL;
+
+	addr_type = ipv6_addr_type(&usin->sin6_addr);
+	if (addr_type & IPV6_ADDR_MULTICAST)
+		return -EINVAL;
+
+	if (addr_type & IPV6_ADDR_MAPPED) {
+		daddr = &usin->sin6_addr;
+		if (ipv4_is_multicast(daddr->s6_addr32[3]))
+			return -EINVAL;
+	}
+
+	rc = ip6_datagram_connect(sk, uaddr, addr_len);
+
+	lock_sock(sk);
+
+	l2tp_ip6_sk(sk)->peer_conn_id = lsa->l2tp_conn_id;
+
+	write_lock_bh(&l2tp_ip6_lock);
+	hlist_del_init(&sk->sk_bind_node);
+	sk_add_bind_node(sk, &l2tp_ip6_bind_table);
+	write_unlock_bh(&l2tp_ip6_lock);
+
+	release_sock(sk);
+
+	return rc;
+}
+
+static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr,
+			    int *uaddr_len, int peer)
+{
+	struct sockaddr_l2tpip6 *lsa = (struct sockaddr_l2tpip6 *)uaddr;
+	struct sock *sk = sock->sk;
+	struct ipv6_pinfo *np = inet6_sk(sk);
+	struct l2tp_ip6_sock *lsk = l2tp_ip6_sk(sk);
+
+	lsa->l2tp_family = AF_INET6;
+	lsa->l2tp_flowinfo = 0;
+	lsa->l2tp_scope_id = 0;
+	if (peer) {
+		if (!lsk->peer_conn_id)
+			return -ENOTCONN;
+		lsa->l2tp_conn_id = lsk->peer_conn_id;
+		lsa->l2tp_addr = np->daddr;
+		if (np->sndflow)
+			lsa->l2tp_flowinfo = np->flow_label;
+	} else {
+		if (ipv6_addr_any(&np->rcv_saddr))
+			lsa->l2tp_addr = np->saddr;
+		else
+			lsa->l2tp_addr = np->rcv_saddr;
+
+		lsa->l2tp_conn_id = lsk->conn_id;
+	}
+	if (ipv6_addr_type(&lsa->l2tp_addr) & IPV6_ADDR_LINKLOCAL)
+		lsa->l2tp_scope_id = sk->sk_bound_dev_if;
+	*uaddr_len = sizeof(*lsa);
+	return 0;
+}
+
+static int l2tp_ip6_backlog_recv(struct sock *sk, struct sk_buff *skb)
+{
+	int rc;
+
+	/* Charge it to the socket, dropping if the queue is full. */
+	rc = sock_queue_rcv_skb(sk, skb);
+	if (rc < 0)
+		goto drop;
+
+	return 0;
+
+drop:
+	IP_INC_STATS(&init_net, IPSTATS_MIB_INDISCARDS);
+	kfree_skb(skb);
+	return -1;
+}
+
+static int l2tp_ip6_push_pending_frames(struct sock *sk)
+{
+	struct sk_buff *skb;
+	__be32 *transhdr = NULL;
+	int err = 0;
+
+	skb = skb_peek(&sk->sk_write_queue);
+	if (skb == NULL)
+		goto out;
+
+	transhdr = (__be32 *)skb_transport_header(skb);
+	*transhdr = 0;
+
+	err = ip6_push_pending_frames(sk);
+
+out:
+	return err;
+}
+
+/* Userspace will call sendmsg() on the tunnel socket to send L2TP
+ * control frames.
+ */
+static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
+			    struct msghdr *msg, size_t len)
+{
+	struct ipv6_txoptions opt_space;
+	struct sockaddr_l2tpip6 *lsa =
+		(struct sockaddr_l2tpip6 *) msg->msg_name;
+	struct in6_addr *daddr, *final_p, final;
+	struct ipv6_pinfo *np = inet6_sk(sk);
+	struct ipv6_txoptions *opt = NULL;
+	struct ip6_flowlabel *flowlabel = NULL;
+	struct dst_entry *dst = NULL;
+	struct flowi6 fl6;
+	int addr_len = msg->msg_namelen;
+	int hlimit = -1;
+	int tclass = -1;
+	int dontfrag = -1;
+	int transhdrlen = 4; /* zero session-id */
+	int ulen = len + transhdrlen;
+	int err;
+
+	/* Rough check on arithmetic overflow,
+	   better check is made in ip6_append_data().
+	 */
+	if (len > INT_MAX)
+		return -EMSGSIZE;
+
+	/* Mirror BSD error message compatibility */
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	/*
+	 *	Get and verify the address.
+	 */
+	memset(&fl6, 0, sizeof(fl6));
+
+	fl6.flowi6_mark = sk->sk_mark;
+
+	if (lsa) {
+		if (addr_len < SIN6_LEN_RFC2133)
+			return -EINVAL;
+
+		if (lsa->l2tp_family && lsa->l2tp_family != AF_INET6)
+			return -EAFNOSUPPORT;
+
+		daddr = &lsa->l2tp_addr;
+		if (np->sndflow) {
+			fl6.flowlabel = lsa->l2tp_flowinfo & IPV6_FLOWINFO_MASK;
+			if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) {
+				flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
+				if (flowlabel == NULL)
+					return -EINVAL;
+				daddr = &flowlabel->dst;
+			}
+		}
+
+		/*
+		 * Otherwise it will be difficult to maintain
+		 * sk->sk_dst_cache.
+		 */
+		if (sk->sk_state == TCP_ESTABLISHED &&
+		    ipv6_addr_equal(daddr, &np->daddr))
+			daddr = &np->daddr;
+
+		if (addr_len >= sizeof(struct sockaddr_in6) &&
+		    lsa->l2tp_scope_id &&
+		    ipv6_addr_type(daddr) & IPV6_ADDR_LINKLOCAL)
+			fl6.flowi6_oif = lsa->l2tp_scope_id;
+	} else {
+		if (sk->sk_state != TCP_ESTABLISHED)
+			return -EDESTADDRREQ;
+
+		daddr = &np->daddr;
+		fl6.flowlabel = np->flow_label;
+	}
+
+	if (fl6.flowi6_oif == 0)
+		fl6.flowi6_oif = sk->sk_bound_dev_if;
+
+	if (msg->msg_controllen) {
+		opt = &opt_space;
+		memset(opt, 0, sizeof(struct ipv6_txoptions));
+		opt->tot_len = sizeof(struct ipv6_txoptions);
+
+		err = datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt,
+					&hlimit, &tclass, &dontfrag);
+		if (err < 0) {
+			fl6_sock_release(flowlabel);
+			return err;
+		}
+		if ((fl6.flowlabel & IPV6_FLOWLABEL_MASK) && !flowlabel) {
+			flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
+			if (flowlabel == NULL)
+				return -EINVAL;
+		}
+		if (!(opt->opt_nflen|opt->opt_flen))
+			opt = NULL;
+	}
+
+	if (opt == NULL)
+		opt = np->opt;
+	if (flowlabel)
+		opt = fl6_merge_options(&opt_space, flowlabel, opt);
+	opt = ipv6_fixup_options(&opt_space, opt);
+
+	fl6.flowi6_proto = sk->sk_protocol;
+	if (!ipv6_addr_any(daddr))
+		fl6.daddr = *daddr;
+	else
+		fl6.daddr.s6_addr[15] = 0x1; /* :: means loopback (BSD'ism) */
+	if (ipv6_addr_any(&fl6.saddr) && !ipv6_addr_any(&np->saddr))
+		fl6.saddr = np->saddr;
+
+	final_p = fl6_update_dst(&fl6, opt, &final);
+
+	if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
+		fl6.flowi6_oif = np->mcast_oif;
+	else if (!fl6.flowi6_oif)
+		fl6.flowi6_oif = np->ucast_oif;
+
+	security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
+
+	dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true);
+	if (IS_ERR(dst)) {
+		err = PTR_ERR(dst);
+		goto out;
+	}
+
+	if (hlimit < 0) {
+		if (ipv6_addr_is_multicast(&fl6.daddr))
+			hlimit = np->mcast_hops;
+		else
+			hlimit = np->hop_limit;
+		if (hlimit < 0)
+			hlimit = ip6_dst_hoplimit(dst);
+	}
+
+	if (tclass < 0)
+		tclass = np->tclass;
+
+	if (dontfrag < 0)
+		dontfrag = np->dontfrag;
+
+	if (msg->msg_flags & MSG_CONFIRM)
+		goto do_confirm;
+
+back_from_confirm:
+	lock_sock(sk);
+	err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov,
+			      ulen, transhdrlen, hlimit, tclass, opt,
+			      &fl6, (struct rt6_info *)dst,
+			      msg->msg_flags, dontfrag);
+	if (err)
+		ip6_flush_pending_frames(sk);
+	else if (!(msg->msg_flags & MSG_MORE))
+		err = l2tp_ip6_push_pending_frames(sk);
+	release_sock(sk);
+done:
+	dst_release(dst);
+out:
+	fl6_sock_release(flowlabel);
+
+	return err < 0 ? err : len;
+
+do_confirm:
+	dst_confirm(dst);
+	if (!(msg->msg_flags & MSG_PROBE) || len)
+		goto back_from_confirm;
+	err = 0;
+	goto done;
+}
+
+static int l2tp_ip6_recvmsg(struct kiocb *iocb, struct sock *sk,
+			    struct msghdr *msg, size_t len, int noblock,
+			    int flags, int *addr_len)
+{
+	struct inet_sock *inet = inet_sk(sk);
+	struct sockaddr_l2tpip6 *lsa = (struct sockaddr_l2tpip6 *)msg->msg_name;
+	size_t copied = 0;
+	int err = -EOPNOTSUPP;
+	struct sk_buff *skb;
+
+	if (flags & MSG_OOB)
+		goto out;
+
+	if (addr_len)
+		*addr_len = sizeof(*lsa);
+
+	if (flags & MSG_ERRQUEUE)
+		return ipv6_recv_error(sk, msg, len);
+
+	skb = skb_recv_datagram(sk, flags, noblock, &err);
+	if (!skb)
+		goto out;
+
+	copied = skb->len;
+	if (len < copied) {
+		msg->msg_flags |= MSG_TRUNC;
+		copied = len;
+	}
+
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+	if (err)
+		goto done;
+
+	sock_recv_timestamp(msg, sk, skb);
+
+	/* Copy the address. */
+	if (lsa) {
+		lsa->l2tp_family = AF_INET6;
+		lsa->l2tp_unused = 0;
+		lsa->l2tp_addr = ipv6_hdr(skb)->saddr;
+		lsa->l2tp_flowinfo = 0;
+		lsa->l2tp_scope_id = 0;
+		if (ipv6_addr_type(&lsa->l2tp_addr) & IPV6_ADDR_LINKLOCAL)
+			lsa->l2tp_scope_id = IP6CB(skb)->iif;
+	}
+
+	if (inet->cmsg_flags)
+		ip_cmsg_recv(msg, skb);
+
+	if (flags & MSG_TRUNC)
+		copied = skb->len;
+done:
+	skb_free_datagram(sk, skb);
+out:
+	return err ? err : copied;
+}
+
+static struct proto l2tp_ip6_prot = {
+	.name		   = "L2TP/IPv6",
+	.owner		   = THIS_MODULE,
+	.init		   = l2tp_ip6_open,
+	.close		   = l2tp_ip6_close,
+	.bind		   = l2tp_ip6_bind,
+	.connect	   = l2tp_ip6_connect,
+	.disconnect	   = udp_disconnect,
+	.ioctl		   = udp_ioctl,
+	.destroy	   = l2tp_ip6_destroy_sock,
+	.setsockopt	   = ipv6_setsockopt,
+	.getsockopt	   = ipv6_getsockopt,
+	.sendmsg	   = l2tp_ip6_sendmsg,
+	.recvmsg	   = l2tp_ip6_recvmsg,
+	.backlog_rcv	   = l2tp_ip6_backlog_recv,
+	.hash		   = inet_hash,
+	.unhash		   = inet_unhash,
+	.obj_size	   = sizeof(struct l2tp_ip6_sock),
+#ifdef CONFIG_COMPAT
+	.compat_setsockopt = compat_ipv6_setsockopt,
+	.compat_getsockopt = compat_ipv6_getsockopt,
+#endif
+};
+
+static const struct proto_ops l2tp_ip6_ops = {
+	.family		   = PF_INET6,
+	.owner		   = THIS_MODULE,
+	.release	   = inet6_release,
+	.bind		   = inet6_bind,
+	.connect	   = inet_dgram_connect,
+	.socketpair	   = sock_no_socketpair,
+	.accept		   = sock_no_accept,
+	.getname	   = l2tp_ip6_getname,
+	.poll		   = datagram_poll,
+	.ioctl		   = inet6_ioctl,
+	.listen		   = sock_no_listen,
+	.shutdown	   = inet_shutdown,
+	.setsockopt	   = sock_common_setsockopt,
+	.getsockopt	   = sock_common_getsockopt,
+	.sendmsg	   = inet_sendmsg,
+	.recvmsg	   = sock_common_recvmsg,
+	.mmap		   = sock_no_mmap,
+	.sendpage	   = sock_no_sendpage,
+#ifdef CONFIG_COMPAT
+	.compat_setsockopt = compat_sock_common_setsockopt,
+	.compat_getsockopt = compat_sock_common_getsockopt,
+#endif
+};
+
+static struct inet_protosw l2tp_ip6_protosw = {
+	.type		= SOCK_DGRAM,
+	.protocol	= IPPROTO_L2TP,
+	.prot		= &l2tp_ip6_prot,
+	.ops		= &l2tp_ip6_ops,
+	.no_check	= 0,
+};
+
+static struct inet6_protocol l2tp_ip6_protocol __read_mostly = {
+	.handler	= l2tp_ip6_recv,
+};
+
+static int __init l2tp_ip6_init(void)
+{
+	int err;
+
+	printk(KERN_INFO "L2TP IP encapsulation support for IPv6 (L2TPv3)\n");
+
+	err = proto_register(&l2tp_ip6_prot, 1);
+	if (err != 0)
+		goto out;
+
+	err = inet6_add_protocol(&l2tp_ip6_protocol, IPPROTO_L2TP);
+	if (err)
+		goto out1;
+
+	inet6_register_protosw(&l2tp_ip6_protosw);
+	return 0;
+
+out1:
+	proto_unregister(&l2tp_ip6_prot);
+out:
+	return err;
+}
+
+static void __exit l2tp_ip6_exit(void)
+{
+	inet6_unregister_protosw(&l2tp_ip6_protosw);
+	inet6_del_protocol(&l2tp_ip6_protocol, IPPROTO_L2TP);
+	proto_unregister(&l2tp_ip6_prot);
+}
+
+module_init(l2tp_ip6_init);
+module_exit(l2tp_ip6_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Chris Elston <celston@katalix.com>");
+MODULE_DESCRIPTION("L2TP IP encapsulation for IPv6");
+MODULE_VERSION("1.0");
+
+/* Use the value of SOCK_DGRAM (2) directory, because __stringify doesn't like
+ * enums
+ */
+MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 2, IPPROTO_L2TP);
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 10/10] l2tp: let iproute2 create L2TPv3 IP tunnels using IPv6
From: James Chapman @ 2012-04-20  9:02 UTC (permalink / raw)
  To: netdev; +Cc: bcrl
In-Reply-To: <1334912573-28804-1-git-send-email-jchapman@katalix.com>

The netlink API lets users create unmanaged L2TPv3 tunnels using
iproute2. Until now, a request to create an unmanaged L2TPv3 IP
encapsulation tunnel over IPv6 would be rejected with
EPROTONOSUPPORT. Now that l2tp_ip6 implements sockets for L2TP IP
encapsulation over IPv6, we can add support for that tunnel type.

Signed-off-by: James Chapman <jchapman@katalix.com>
---
 net/l2tp/l2tp_core.c |   72 ++++++++++++++++++++++++++++++++++---------------
 1 files changed, 50 insertions(+), 22 deletions(-)

diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 5a7d908..7415620 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1367,6 +1367,7 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t
 	struct sockaddr_in udp_addr;
 #if IS_ENABLED(CONFIG_IPV6)
 	struct sockaddr_in6 udp6_addr;
+	struct sockaddr_l2tpip6 ip6_addr;
 #endif
 	struct sockaddr_l2tpip ip_addr;
 	struct socket *sock = NULL;
@@ -1436,32 +1437,59 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t
 	case L2TP_ENCAPTYPE_IP:
 #if IS_ENABLED(CONFIG_IPV6)
 		if (cfg->local_ip6 && cfg->peer_ip6) {
-			/* IP encap over IPv6 not yet supported */
-			err = -EPROTONOSUPPORT;
-			goto out;
-		}
-#endif
-		err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_L2TP, sockp);
-		if (err < 0)
-			goto out;
+			err = sock_create(AF_INET6, SOCK_DGRAM, IPPROTO_L2TP,
+					  sockp);
+			if (err < 0)
+				goto out;
 
-		sock = *sockp;
+			sock = *sockp;
 
-		memset(&ip_addr, 0, sizeof(ip_addr));
-		ip_addr.l2tp_family = AF_INET;
-		ip_addr.l2tp_addr = cfg->local_ip;
-		ip_addr.l2tp_conn_id = tunnel_id;
-		err = kernel_bind(sock, (struct sockaddr *) &ip_addr, sizeof(ip_addr));
-		if (err < 0)
-			goto out;
+			memset(&ip6_addr, 0, sizeof(ip6_addr));
+			ip6_addr.l2tp_family = AF_INET6;
+			memcpy(&ip6_addr.l2tp_addr, cfg->local_ip6,
+			       sizeof(ip6_addr.l2tp_addr));
+			ip6_addr.l2tp_conn_id = tunnel_id;
+			err = kernel_bind(sock, (struct sockaddr *) &ip6_addr,
+					  sizeof(ip6_addr));
+			if (err < 0)
+				goto out;
 
-		ip_addr.l2tp_family = AF_INET;
-		ip_addr.l2tp_addr = cfg->peer_ip;
-		ip_addr.l2tp_conn_id = peer_tunnel_id;
-		err = kernel_connect(sock, (struct sockaddr *) &ip_addr, sizeof(ip_addr), 0);
-		if (err < 0)
-			goto out;
+			ip6_addr.l2tp_family = AF_INET6;
+			memcpy(&ip6_addr.l2tp_addr, cfg->peer_ip6,
+			       sizeof(ip6_addr.l2tp_addr));
+			ip6_addr.l2tp_conn_id = peer_tunnel_id;
+			err = kernel_connect(sock,
+					     (struct sockaddr *) &ip6_addr,
+					     sizeof(ip6_addr), 0);
+			if (err < 0)
+				goto out;
+		} else
+#endif
+		{
+			err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_L2TP,
+					  sockp);
+			if (err < 0)
+				goto out;
 
+			sock = *sockp;
+
+			memset(&ip_addr, 0, sizeof(ip_addr));
+			ip_addr.l2tp_family = AF_INET;
+			ip_addr.l2tp_addr = cfg->local_ip;
+			ip_addr.l2tp_conn_id = tunnel_id;
+			err = kernel_bind(sock, (struct sockaddr *) &ip_addr,
+					  sizeof(ip_addr));
+			if (err < 0)
+				goto out;
+
+			ip_addr.l2tp_family = AF_INET;
+			ip_addr.l2tp_addr = cfg->peer_ip;
+			ip_addr.l2tp_conn_id = peer_tunnel_id;
+			err = kernel_connect(sock, (struct sockaddr *) &ip_addr,
+					     sizeof(ip_addr), 0);
+			if (err < 0)
+				goto out;
+		}
 		break;
 
 	default:
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 03/10] l2tp: remove unused stats from l2tp_ip socket
From: James Chapman @ 2012-04-20  9:02 UTC (permalink / raw)
  To: netdev; +Cc: bcrl
In-Reply-To: <1334912573-28804-1-git-send-email-jchapman@katalix.com>

The l2tp_ip socket currently maintains packet/byte stats in its
private socket structure. But these counters aren't exposed to
userspace and so serve no purpose. The counters were also
smp-unsafe. So this patch just gets rid of the stats.

While here, change a couple of internal __u32 variables to u32.

Signed-off-by: James Chapman <jchapman@katalix.com>
---
 net/l2tp/l2tp_ip.c |   32 ++++----------------------------
 1 files changed, 4 insertions(+), 28 deletions(-)

diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index 5162592..adefe52 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -32,15 +32,8 @@ struct l2tp_ip_sock {
 	/* inet_sock has to be the first member of l2tp_ip_sock */
 	struct inet_sock	inet;
 
-	__u32			conn_id;
-	__u32			peer_conn_id;
-
-	__u64			tx_packets;
-	__u64			tx_bytes;
-	__u64			tx_errors;
-	__u64			rx_packets;
-	__u64			rx_bytes;
-	__u64			rx_errors;
+	u32			conn_id;
+	u32			peer_conn_id;
 };
 
 static DEFINE_RWLOCK(l2tp_ip_lock);
@@ -298,7 +291,6 @@ out_in_use:
 static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
 	struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *) uaddr;
-	struct inet_sock *inet = inet_sk(sk);
 	int rc;
 
 	if (addr_len < sizeof(*lsa))
@@ -374,7 +366,6 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
 {
 	struct sk_buff *skb;
 	int rc;
-	struct l2tp_ip_sock *lsa = l2tp_ip_sk(sk);
 	struct inet_sock *inet = inet_sk(sk);
 	struct rtable *rt = NULL;
 	struct flowi4 *fl4;
@@ -473,14 +464,8 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
 	rcu_read_unlock();
 
 error:
-	/* Update stats */
-	if (rc >= 0) {
-		lsa->tx_packets++;
-		lsa->tx_bytes += len;
+	if (rc >= 0)
 		rc = len;
-	} else {
-		lsa->tx_errors++;
-	}
 
 out:
 	release_sock(sk);
@@ -498,7 +483,6 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
 			   size_t len, int noblock, int flags, int *addr_len)
 {
 	struct inet_sock *inet = inet_sk(sk);
-	struct l2tp_ip_sock *lsk = l2tp_ip_sk(sk);
 	size_t copied = 0;
 	int err = -EOPNOTSUPP;
 	struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
@@ -540,15 +524,7 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
 done:
 	skb_free_datagram(sk, skb);
 out:
-	if (err) {
-		lsk->rx_errors++;
-		return err;
-	}
-
-	lsk->rx_packets++;
-	lsk->rx_bytes += copied;
-
-	return copied;
+	return err ? err : copied;
 }
 
 static struct proto l2tp_ip_prot = {
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 01/10] l2tp: fix locking of 64-bit counters for smp
From: James Chapman @ 2012-04-20  9:02 UTC (permalink / raw)
  To: netdev; +Cc: bcrl
In-Reply-To: <1334912573-28804-1-git-send-email-jchapman@katalix.com>

L2TP uses 64-bit counters but since these are not updated atomically,
we need to make them safe for smp. This patch addresses that.

Signed-off-by: James Chapman <jchapman@katalix.com>
---
 net/l2tp/l2tp_core.c    |   75 +++++++++++++++++++++++++++++++++++------------
 net/l2tp/l2tp_core.h    |    1 +
 net/l2tp/l2tp_netlink.c |   62 ++++++++++++++++++++++++++++----------
 3 files changed, 103 insertions(+), 35 deletions(-)

diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 8cd5f4b..5e27c05 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -329,8 +329,10 @@ static void l2tp_recv_queue_skb(struct l2tp_session *session, struct sk_buff *sk
 	struct sk_buff *skbp;
 	struct sk_buff *tmp;
 	u32 ns = L2TP_SKB_CB(skb)->ns;
+	struct l2tp_stats *sstats;
 
 	spin_lock_bh(&session->reorder_q.lock);
+	sstats = &session->stats;
 	skb_queue_walk_safe(&session->reorder_q, skbp, tmp) {
 		if (L2TP_SKB_CB(skbp)->ns > ns) {
 			__skb_queue_before(&session->reorder_q, skbp, skb);
@@ -338,7 +340,9 @@ static void l2tp_recv_queue_skb(struct l2tp_session *session, struct sk_buff *sk
 			       "%s: pkt %hu, inserted before %hu, reorder_q len=%d\n",
 			       session->name, ns, L2TP_SKB_CB(skbp)->ns,
 			       skb_queue_len(&session->reorder_q));
-			session->stats.rx_oos_packets++;
+			u64_stats_update_begin(&sstats->syncp);
+			sstats->rx_oos_packets++;
+			u64_stats_update_end(&sstats->syncp);
 			goto out;
 		}
 	}
@@ -355,16 +359,23 @@ static void l2tp_recv_dequeue_skb(struct l2tp_session *session, struct sk_buff *
 {
 	struct l2tp_tunnel *tunnel = session->tunnel;
 	int length = L2TP_SKB_CB(skb)->length;
+	struct l2tp_stats *tstats, *sstats;
 
 	/* We're about to requeue the skb, so return resources
 	 * to its current owner (a socket receive buffer).
 	 */
 	skb_orphan(skb);
 
-	tunnel->stats.rx_packets++;
-	tunnel->stats.rx_bytes += length;
-	session->stats.rx_packets++;
-	session->stats.rx_bytes += length;
+	tstats = &tunnel->stats;
+	u64_stats_update_begin(&tstats->syncp);
+	sstats = &session->stats;
+	u64_stats_update_begin(&sstats->syncp);
+	tstats->rx_packets++;
+	tstats->rx_bytes += length;
+	sstats->rx_packets++;
+	sstats->rx_bytes += length;
+	u64_stats_update_end(&tstats->syncp);
+	u64_stats_update_end(&sstats->syncp);
 
 	if (L2TP_SKB_CB(skb)->has_seq) {
 		/* Bump our Nr */
@@ -395,6 +406,7 @@ static void l2tp_recv_dequeue(struct l2tp_session *session)
 {
 	struct sk_buff *skb;
 	struct sk_buff *tmp;
+	struct l2tp_stats *sstats;
 
 	/* If the pkt at the head of the queue has the nr that we
 	 * expect to send up next, dequeue it and any other
@@ -402,10 +414,13 @@ static void l2tp_recv_dequeue(struct l2tp_session *session)
 	 */
 start:
 	spin_lock_bh(&session->reorder_q.lock);
+	sstats = &session->stats;
 	skb_queue_walk_safe(&session->reorder_q, skb, tmp) {
 		if (time_after(jiffies, L2TP_SKB_CB(skb)->expires)) {
-			session->stats.rx_seq_discards++;
-			session->stats.rx_errors++;
+			u64_stats_update_begin(&sstats->syncp);
+			sstats->rx_seq_discards++;
+			sstats->rx_errors++;
+			u64_stats_update_end(&sstats->syncp);
 			PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG,
 			       "%s: oos pkt %u len %d discarded (too old), "
 			       "waiting for %u, reorder_q_len=%d\n",
@@ -557,6 +572,7 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
 	struct l2tp_tunnel *tunnel = session->tunnel;
 	int offset;
 	u32 ns, nr;
+	struct l2tp_stats *sstats = &session->stats;
 
 	/* The ref count is increased since we now hold a pointer to
 	 * the session. Take care to decrement the refcnt when exiting
@@ -572,7 +588,9 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
 			PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_INFO,
 			       "%s: cookie mismatch (%u/%u). Discarding.\n",
 			       tunnel->name, tunnel->tunnel_id, session->session_id);
-			session->stats.rx_cookie_discards++;
+			u64_stats_update_begin(&sstats->syncp);
+			sstats->rx_cookie_discards++;
+			u64_stats_update_end(&sstats->syncp);
 			goto discard;
 		}
 		ptr += session->peer_cookie_len;
@@ -641,7 +659,9 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
 			PRINTK(session->debug, L2TP_MSG_SEQ, KERN_WARNING,
 			       "%s: recv data has no seq numbers when required. "
 			       "Discarding\n", session->name);
-			session->stats.rx_seq_discards++;
+			u64_stats_update_begin(&sstats->syncp);
+			sstats->rx_seq_discards++;
+			u64_stats_update_end(&sstats->syncp);
 			goto discard;
 		}
 
@@ -660,7 +680,9 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
 			PRINTK(session->debug, L2TP_MSG_SEQ, KERN_WARNING,
 			       "%s: recv data has no seq numbers when required. "
 			       "Discarding\n", session->name);
-			session->stats.rx_seq_discards++;
+			u64_stats_update_begin(&sstats->syncp);
+			sstats->rx_seq_discards++;
+			u64_stats_update_end(&sstats->syncp);
 			goto discard;
 		}
 	}
@@ -714,7 +736,9 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
 			 * packets
 			 */
 			if (L2TP_SKB_CB(skb)->ns != session->nr) {
-				session->stats.rx_seq_discards++;
+				u64_stats_update_begin(&sstats->syncp);
+				sstats->rx_seq_discards++;
+				u64_stats_update_end(&sstats->syncp);
 				PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG,
 				       "%s: oos pkt %u len %d discarded, "
 				       "waiting for %u, reorder_q_len=%d\n",
@@ -741,7 +765,9 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
 	return;
 
 discard:
-	session->stats.rx_errors++;
+	u64_stats_update_begin(&sstats->syncp);
+	sstats->rx_errors++;
+	u64_stats_update_end(&sstats->syncp);
 	kfree_skb(skb);
 
 	if (session->deref)
@@ -767,6 +793,7 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
 	int offset;
 	u16 version;
 	int length;
+	struct l2tp_stats *tstats;
 
 	if (tunnel->sock && l2tp_verify_udp_checksum(tunnel->sock, skb))
 		goto discard_bad_csum;
@@ -859,7 +886,10 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
 discard_bad_csum:
 	LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name);
 	UDP_INC_STATS_USER(tunnel->l2tp_net, UDP_MIB_INERRORS, 0);
-	tunnel->stats.rx_errors++;
+	tstats = &tunnel->stats;
+	u64_stats_update_begin(&tstats->syncp);
+	tstats->rx_errors++;
+	u64_stats_update_end(&tstats->syncp);
 	kfree_skb(skb);
 
 	return 0;
@@ -985,6 +1015,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
 	struct l2tp_tunnel *tunnel = session->tunnel;
 	unsigned int len = skb->len;
 	int error;
+	struct l2tp_stats *tstats, *sstats;
 
 	/* Debug */
 	if (session->send_seq)
@@ -1021,15 +1052,21 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
 		error = ip_queue_xmit(skb, fl);
 
 	/* Update stats */
+	tstats = &tunnel->stats;
+	u64_stats_update_begin(&tstats->syncp);
+	sstats = &session->stats;
+	u64_stats_update_begin(&sstats->syncp);
 	if (error >= 0) {
-		tunnel->stats.tx_packets++;
-		tunnel->stats.tx_bytes += len;
-		session->stats.tx_packets++;
-		session->stats.tx_bytes += len;
+		tstats->tx_packets++;
+		tstats->tx_bytes += len;
+		sstats->tx_packets++;
+		sstats->tx_bytes += len;
 	} else {
-		tunnel->stats.tx_errors++;
-		session->stats.tx_errors++;
+		tstats->tx_errors++;
+		sstats->tx_errors++;
 	}
+	u64_stats_update_end(&tstats->syncp);
+	u64_stats_update_end(&sstats->syncp);
 
 	return 0;
 }
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index 09e4a38..a8c943b 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -45,6 +45,7 @@ struct l2tp_stats {
 	u64			rx_oos_packets;
 	u64			rx_errors;
 	u64			rx_cookie_discards;
+	struct u64_stats_sync	syncp;
 };
 
 struct l2tp_tunnel;
diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c
index bc8c334..1dbb977 100644
--- a/net/l2tp/l2tp_netlink.c
+++ b/net/l2tp/l2tp_netlink.c
@@ -225,6 +225,8 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
 	struct nlattr *nest;
 	struct sock *sk = NULL;
 	struct inet_sock *inet;
+	struct l2tp_stats stats;
+	unsigned int start;
 
 	hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags,
 			  L2TP_CMD_TUNNEL_GET);
@@ -242,16 +244,28 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
 	if (nest == NULL)
 		goto nla_put_failure;
 
-	if (nla_put_u64(skb, L2TP_ATTR_TX_PACKETS, tunnel->stats.tx_packets) ||
-	    nla_put_u64(skb, L2TP_ATTR_TX_BYTES, tunnel->stats.tx_bytes) ||
-	    nla_put_u64(skb, L2TP_ATTR_TX_ERRORS, tunnel->stats.tx_errors) ||
-	    nla_put_u64(skb, L2TP_ATTR_RX_PACKETS, tunnel->stats.rx_packets) ||
-	    nla_put_u64(skb, L2TP_ATTR_RX_BYTES, tunnel->stats.rx_bytes) ||
+	do {
+		start = u64_stats_fetch_begin(&tunnel->stats.syncp);
+		stats.tx_packets = tunnel->stats.tx_packets;
+		stats.tx_bytes = tunnel->stats.tx_bytes;
+		stats.tx_errors = tunnel->stats.tx_errors;
+		stats.rx_packets = tunnel->stats.rx_packets;
+		stats.rx_bytes = tunnel->stats.rx_bytes;
+		stats.rx_errors = tunnel->stats.rx_errors;
+		stats.rx_seq_discards = tunnel->stats.rx_seq_discards;
+		stats.rx_oos_packets = tunnel->stats.rx_oos_packets;
+	} while (u64_stats_fetch_retry(&tunnel->stats.syncp, start));
+
+	if (nla_put_u64(skb, L2TP_ATTR_TX_PACKETS, stats.tx_packets) ||
+	    nla_put_u64(skb, L2TP_ATTR_TX_BYTES, stats.tx_bytes) ||
+	    nla_put_u64(skb, L2TP_ATTR_TX_ERRORS, stats.tx_errors) ||
+	    nla_put_u64(skb, L2TP_ATTR_RX_PACKETS, stats.rx_packets) ||
+	    nla_put_u64(skb, L2TP_ATTR_RX_BYTES, stats.rx_bytes) ||
 	    nla_put_u64(skb, L2TP_ATTR_RX_SEQ_DISCARDS,
-			tunnel->stats.rx_seq_discards) ||
+			stats.rx_seq_discards) ||
 	    nla_put_u64(skb, L2TP_ATTR_RX_OOS_PACKETS,
-			tunnel->stats.rx_oos_packets) ||
-	    nla_put_u64(skb, L2TP_ATTR_RX_ERRORS, tunnel->stats.rx_errors))
+			stats.rx_oos_packets) ||
+	    nla_put_u64(skb, L2TP_ATTR_RX_ERRORS, stats.rx_errors))
 		goto nla_put_failure;
 	nla_nest_end(skb, nest);
 
@@ -563,6 +577,8 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 pid, u32 seq, int flags
 	struct nlattr *nest;
 	struct l2tp_tunnel *tunnel = session->tunnel;
 	struct sock *sk = NULL;
+	struct l2tp_stats stats;
+	unsigned int start;
 
 	sk = tunnel->sock;
 
@@ -600,19 +616,33 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 pid, u32 seq, int flags
 	    (session->reorder_timeout &&
 	     nla_put_msecs(skb, L2TP_ATTR_RECV_TIMEOUT, session->reorder_timeout)))
 		goto nla_put_failure;
+
 	nest = nla_nest_start(skb, L2TP_ATTR_STATS);
 	if (nest == NULL)
 		goto nla_put_failure;
-	if (nla_put_u64(skb, L2TP_ATTR_TX_PACKETS, session->stats.tx_packets) ||
-	    nla_put_u64(skb, L2TP_ATTR_TX_BYTES, session->stats.tx_bytes) ||
-	    nla_put_u64(skb, L2TP_ATTR_TX_ERRORS, session->stats.tx_errors) ||
-	    nla_put_u64(skb, L2TP_ATTR_RX_PACKETS, session->stats.rx_packets) ||
-	    nla_put_u64(skb, L2TP_ATTR_RX_BYTES, session->stats.rx_bytes) ||
+
+	do {
+		start = u64_stats_fetch_begin(&session->stats.syncp);
+		stats.tx_packets = session->stats.tx_packets;
+		stats.tx_bytes = session->stats.tx_bytes;
+		stats.tx_errors = session->stats.tx_errors;
+		stats.rx_packets = session->stats.rx_packets;
+		stats.rx_bytes = session->stats.rx_bytes;
+		stats.rx_errors = session->stats.rx_errors;
+		stats.rx_seq_discards = session->stats.rx_seq_discards;
+		stats.rx_oos_packets = session->stats.rx_oos_packets;
+	} while (u64_stats_fetch_retry(&session->stats.syncp, start));
+
+	if (nla_put_u64(skb, L2TP_ATTR_TX_PACKETS, stats.tx_packets) ||
+	    nla_put_u64(skb, L2TP_ATTR_TX_BYTES, stats.tx_bytes) ||
+	    nla_put_u64(skb, L2TP_ATTR_TX_ERRORS, stats.tx_errors) ||
+	    nla_put_u64(skb, L2TP_ATTR_RX_PACKETS, stats.rx_packets) ||
+	    nla_put_u64(skb, L2TP_ATTR_RX_BYTES, stats.rx_bytes) ||
 	    nla_put_u64(skb, L2TP_ATTR_RX_SEQ_DISCARDS,
-			session->stats.rx_seq_discards) ||
+			stats.rx_seq_discards) ||
 	    nla_put_u64(skb, L2TP_ATTR_RX_OOS_PACKETS,
-			session->stats.rx_oos_packets) ||
-	    nla_put_u64(skb, L2TP_ATTR_RX_ERRORS, session->stats.rx_errors))
+			stats.rx_oos_packets) ||
+	    nla_put_u64(skb, L2TP_ATTR_RX_ERRORS, stats.rx_errors))
 		goto nla_put_failure;
 	nla_nest_end(skb, nest);
 
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 02/10] l2tp: Use ip4_datagram_connect() in l2tp_ip_connect()
From: James Chapman @ 2012-04-20  9:02 UTC (permalink / raw)
  To: netdev; +Cc: bcrl
In-Reply-To: <1334912573-28804-1-git-send-email-jchapman@katalix.com>

Cleanup the l2tp_ip code to make use of an existing ipv4 support function.

Signed-off-by: James Chapman <jchapman@katalix.com>
---
 net/l2tp/l2tp_ip.c |   54 ++++++---------------------------------------------
 1 files changed, 7 insertions(+), 47 deletions(-)

diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index 585d93e..5162592 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -299,67 +299,27 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
 {
 	struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *) uaddr;
 	struct inet_sock *inet = inet_sk(sk);
-	struct flowi4 *fl4;
-	struct rtable *rt;
-	__be32 saddr;
-	int oif, rc;
+	int rc;
 
-	rc = -EINVAL;
 	if (addr_len < sizeof(*lsa))
-		goto out;
+		return -EINVAL;
 
-	rc = -EAFNOSUPPORT;
-	if (lsa->l2tp_family != AF_INET)
-		goto out;
-
-	lock_sock(sk);
-
-	sk_dst_reset(sk);
-
-	oif = sk->sk_bound_dev_if;
-	saddr = inet->inet_saddr;
-
-	rc = -EINVAL;
 	if (ipv4_is_multicast(lsa->l2tp_addr.s_addr))
-		goto out;
+		return -EINVAL;
 
-	fl4 = &inet->cork.fl.u.ip4;
-	rt = ip_route_connect(fl4, lsa->l2tp_addr.s_addr, saddr,
-			      RT_CONN_FLAGS(sk), oif,
-			      IPPROTO_L2TP,
-			      0, 0, sk, true);
-	if (IS_ERR(rt)) {
-		rc = PTR_ERR(rt);
-		if (rc == -ENETUNREACH)
-			IP_INC_STATS_BH(&init_net, IPSTATS_MIB_OUTNOROUTES);
-		goto out;
-	}
+	rc = ip4_datagram_connect(sk, uaddr, addr_len);
+	if (rc < 0)
+		return rc;
 
-	rc = -ENETUNREACH;
-	if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
-		ip_rt_put(rt);
-		goto out;
-	}
+	lock_sock(sk);
 
 	l2tp_ip_sk(sk)->peer_conn_id = lsa->l2tp_conn_id;
 
-	if (!inet->inet_saddr)
-		inet->inet_saddr = fl4->saddr;
-	if (!inet->inet_rcv_saddr)
-		inet->inet_rcv_saddr = fl4->saddr;
-	inet->inet_daddr = fl4->daddr;
-	sk->sk_state = TCP_ESTABLISHED;
-	inet->inet_id = jiffies;
-
-	sk_dst_set(sk, &rt->dst);
-
 	write_lock_bh(&l2tp_ip_lock);
 	hlist_del_init(&sk->sk_bind_node);
 	sk_add_bind_node(sk, &l2tp_ip_bind_table);
 	write_unlock_bh(&l2tp_ip_lock);
 
-	rc = 0;
-out:
 	release_sock(sk);
 	return rc;
 }
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 00/10] l2tp: misc fixes and add L2TPv3 IP encap over IPv6
From: James Chapman @ 2012-04-20  9:02 UTC (permalink / raw)
  To: netdev; +Cc: bcrl

This patch series includes several L2TP fixes / cleanups and adds
L2TPv3 IP encapsulation support for IPv6.

Patches 4-10 depend on Benjamin LaHaise's L2TP UDP IPv6 patches which
have already been submitted for review. Patches 1-3 are not IPv6
related so can be reviewed / applied now.

  1. l2tp: fix locking of 64-bit counters for smp
  2. l2tp: Use ip4_datagram_connect() in l2tp_ip_connect()
  3. l2tp: remove unused stats from l2tp_ip socket
  4. pppox: Replace __attribute__((packed)) in if_pppox.h
  5. l2tp: pppol2tp_connect() handles ipv6 sockaddr variants
  6. l2tp: show IPv6 addresses in l2tp debugfs file
  7. l2tp: netlink api for l2tpv3 ipv6 unmanaged tunnels
  8. ipv6: export ipv6 functions for use by other protocols
  9. l2tp: introduce L2TPv3 IP encapsulation support for IPv6
 10. l2tp: let iproute2 create L2TPv3 IP tunnels using IPv6

A patch for iproute2 to add support for unmanaged L2TPv3 ethernet
tunnels over IPv6 will be submitted separately.

Chris Elston (4):
  l2tp: show IPv6 addresses in l2tp debugfs file
  l2tp: netlink api for l2tpv3 ipv6 unmanaged tunnels
  ipv6: Export ipv6 functions for use by other protocols
  l2tp: introduce L2TPv3 IP encapsulation support for IPv6

James Chapman (6):
  l2tp: fix locking of 64-bit counters for smp
  l2tp: Use ip4_datagram_connect() in l2tp_ip_connect()
  l2tp: remove unused stats from l2tp_ip socket
  pppox: Replace __attribute__((packed)) in if_pppox.h
  l2tp: pppol2tp_connect() handles ipv6 sockaddr variants
  l2tp: let iproute2 create L2TPv3 IP tunnels using IPv6

 include/linux/if_pppox.h |   12 +-
 include/linux/l2tp.h     |   19 ++
 net/ipv6/datagram.c      |    4 +
 net/ipv6/exthdrs.c       |    1 +
 net/ipv6/ip6_flowlabel.c |    1 +
 net/ipv6/ip6_output.c    |    3 +
 net/l2tp/Makefile        |    3 +
 net/l2tp/l2tp_core.c     |  215 ++++++++++---
 net/l2tp/l2tp_core.h     |    5 +
 net/l2tp/l2tp_debugfs.c  |    8 +
 net/l2tp/l2tp_ip.c       |   86 +-----
 net/l2tp/l2tp_ip6.c      |  792 ++++++++++++++++++++++++++++++++++++++++++++++
 net/l2tp/l2tp_netlink.c  |  110 ++++++--
 net/l2tp/l2tp_ppp.c      |   36 ++-
 14 files changed, 1133 insertions(+), 162 deletions(-)
 create mode 100644 net/l2tp/l2tp_ip6.c

^ permalink raw reply

* RE: [PATCH 2/9] atl1c: refine phy-register read/write function
From: Huang, Xiong @ 2012-04-20  8:54 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: davem@davemloft.net, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, qca-linux-team, nic-devel
In-Reply-To: <4F9112F1.1030403@openwrt.org>



> -----Original Message-----
> From: netdev-owner@vger.kernel.org [mailto:netdev-owner@vger.kernel.org]
> On Behalf Of Florian Fainelli
> Sent: Friday, April 20, 2012 15:41
> To: Huang, Xiong
> Cc: davem@davemloft.net; netdev@vger.kernel.org; linux-
> kernel@vger.kernel.org; qca-linux-team; nic-devel
> Subject: Re: [PATCH 2/9] atl1c: refine phy-register read/write function
> 
> Hi Xiong,
> 
> Le 04/20/12 08:16, xiong a écrit :
> > phy register is read/write via MDIO control module --- that module
> > will be affected by the hibernate status, to access phy regs in hib
> > stutus, slow frequency clk must be selected.
> > To access phy extension register, the MDIO related registers are
> > refined/updated, a _core function is re-wroted for both regular PHY
> > regs and extension regs.
> > existing PHY r/w function is revised based on the _core.
> > PHY extension registers will be used for the comming patches.
> >
> > Signed-off-by: xiong<xiong@qca.qualcomm.com>
> > Tested-by: Liu David<dwliu@qca.qualcomm.com>
> > ---
> >   drivers/net/ethernet/atheros/atl1c/atl1c_hw.c   |  178
> +++++++++++++++++++----
> >   drivers/net/ethernet/atheros/atl1c/atl1c_hw.h   |   64 +++++---
> >   drivers/net/ethernet/atheros/atl1c/atl1c_main.c |    4 +-
> >   3 files changed, 189 insertions(+), 57 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
> > b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
> > index bd1667c..6cbe78a 100644
> > --- a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
> > +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
> > @@ -277,65 +277,181 @@ void atl1c_hash_set(struct atl1c_hw *hw, u32
> hash_value)
> >   	AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta);
> >   }
> >
> > +
> > +void atl1c_stop_phy_polling(struct atl1c_hw *hw) {
> > +	u32 val;
> > +	int i;
> > +
> > +	if (hw->ctrl_flags&  ATL1C_FPGA_VERSION) {
> > +		AT_WRITE_REG(hw, REG_MDIO_CTRL, 0);
> > +		for (i = 0; i<  MDIO_MAX_AC_TO; i++) {
> > +			AT_READ_REG(hw, REG_MDIO_CTRL,&val);
> > +			if (0 == (val&  MDIO_CTRL_BUSY))
> > +				break;
> > +			udelay(10);
> > +		}
> > +	}
> > +}
> 
> Please reduce the identation by doing this:
> 
> if (!(hw->ctrl_flags & ALT1C_FPAG_VERSION))
> 	return;
> 
> that makes it much more readable.


No problem


> > +
> > +void atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel) {
> > +	u32 val;
> > +	int i;
> > +
> > +	if (hw->ctrl_flags&  ATL1C_FPGA_VERSION) {
> > +		val = MDIO_CTRL_SPRES_PRMBL |
> > +			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
> > +			FIELDX(MDIO_CTRL_REG, 1) |
> > +			MDIO_CTRL_START |
> > +			MDIO_CTRL_OP_READ;
> > +		AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
> > +		for (i = 0; i<  MDIO_MAX_AC_TO; i++) {
> > +			AT_READ_REG(hw, REG_MDIO_CTRL,&val);
> > +			if (0 == (val&  MDIO_CTRL_BUSY))
> > +				break;
> > +			udelay(10);
> > +		}
> > +		val |= MDIO_CTRL_AP_EN;
> > +		val&= ~MDIO_CTRL_START;
> > +		AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
> > +		udelay(30);
> > +	}
> > +}
> 
> Seems like the for() busy-checking of the register could use their own function
> since it is used both in : atl1c_stop_phy_polling() and atl1c_start_phy_polling().
> 

Good suggestion. I will update.


^ permalink raw reply

* Re: [PATCH net-next 00/19] net: Sysctl simplifications and enhancements
From: Pavel Emelyanov @ 2012-04-20  8:45 UTC (permalink / raw)
  To: Eric W. Biederman, David Miller
  Cc: netdev@vger.kernel.org, Serge E. Hallyn, Gao feng,
	pablo@netfilter.org, Stephen Hemminger
In-Reply-To: <m1aa27ia6h.fsf@fess.ebiederm.org>

On 04/20/2012 03:17 AM, Eric W. Biederman wrote:
> 
> Summary:
> - Kill approximately 400 lines of code
> - Allow all networking sysctls with just CAP_NET_ADMIN
> - Hide all networking sysctls that don't apply to your current network namespace.
> - Uniformly register flat sysctl tables not sysctl tables with .child entries
> - Readable string paths for registering sysctls
> 
> Eric W. Biederman (19):
>       net: Implement register_net_sysctl.
>       net sysctl:  Register an empty /proc/sys/net
>       net sysctl: Initialize the network sysctls sooner to avoid problems.
>       net: Kill register_sysctl_rotable
>       net: Move all of the network sysctls without a namespace into init_net.
>       net core: Remove unneded creation of an empty  net/core sysctl directory
>       net ipv6: Remove unneded registration of an empty net/ipv6/neigh
>       net ipv4: Remove the unneeded registration of an empty net/ipv4/neigh
>       net ax25: Simplify and cleanup the ax25 sysctl handling.
>       net llc: Don't use sysctl tables with .child entries.
>       net ipv6: Don't use sysctl tables with .child entries.
>       net neighbour:  Convert to use register_net_sysctl
>       net decnet:  Convert to use register_net_sysctl
>       net ipv6:  Convert addrconf to use register_net_sysctl
>       net ipv4:  Convert devinet to use register_net_sysctl
>       net: Convert nf_conntrack_proto to use register_net_sysctl
>       net: Convert all sysctl registrations to register_net_sysctl
>       net: Delete all remaining instances of ctl_path
>       net: Remove register_net_sysctl_table

After resolving issues with Eric

Acked-by: Pavel Emelyanov <xemul@parallels.com>

Thanks,
Pavel

>  drivers/infiniband/core/ucma.c                 |   10 +--
>  include/linux/netfilter.h                      |    6 --
>  include/net/ax25.h                             |   10 ++--
>  include/net/ip.h                               |    3 -
>  include/net/ip_vs.h                            |    2 -
>  include/net/ipv6.h                             |    3 -
>  include/net/net_namespace.h                    |   12 ++--
>  include/net/netfilter/nf_conntrack_l3proto.h   |    2 +-
>  include/net/netns/ipv6.h                       |    4 +-
>  net/802/tr.c                                   |    8 +--
>  net/appletalk/sysctl_net_atalk.c               |   10 +--
>  net/ax25/af_ax25.c                             |    2 -
>  net/ax25/ax25_dev.c                            |   10 +--
>  net/ax25/sysctl_net_ax25.c                     |   82 ++++++++---------------
>  net/bridge/br_netfilter.c                      |   10 +--
>  net/core/neighbour.c                           |   35 ++--------
>  net/core/sysctl_net_core.c                     |   14 +----
>  net/dccp/sysctl.c                              |   11 +---
>  net/decnet/dn_dev.c                            |   21 ++-----
>  net/decnet/sysctl_net_decnet.c                 |   10 +--
>  net/ipv4/devinet.c                             |   39 ++----------
>  net/ipv4/ip_fragment.c                         |    4 +-
>  net/ipv4/netfilter.c                           |   10 ---
>  net/ipv4/netfilter/ip_queue.c                  |    6 +-
>  net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c |    2 +-
>  net/ipv4/route.c                               |   29 +--------
>  net/ipv4/sysctl_net_ipv4.c                     |   14 +---
>  net/ipv4/xfrm4_policy.c                        |    4 +-
>  net/ipv6/addrconf.c                            |   32 +--------
>  net/ipv6/af_inet6.c                            |   15 ----
>  net/ipv6/netfilter/ip6_queue.c                 |    6 +-
>  net/ipv6/netfilter/nf_conntrack_reasm.c        |    6 +-
>  net/ipv6/reassembly.c                          |    4 +-
>  net/ipv6/sysctl_net_ipv6.c                     |   83 +++++++-----------------
>  net/ipv6/xfrm6_policy.c                        |    4 +-
>  net/ipx/sysctl_net_ipx.c                       |   11 +--
>  net/irda/irsysctl.c                            |   10 +--
>  net/llc/sysctl_net_llc.c                       |   52 +++++----------
>  net/netfilter/core.c                           |    9 ---
>  net/netfilter/ipvs/ip_vs_ctl.c                 |   10 +---
>  net/netfilter/ipvs/ip_vs_lblc.c                |    3 +-
>  net/netfilter/ipvs/ip_vs_lblcr.c               |    3 +-
>  net/netfilter/nf_conntrack_acct.c              |    4 +-
>  net/netfilter/nf_conntrack_ecache.c            |    3 +-
>  net/netfilter/nf_conntrack_proto.c             |   10 ++--
>  net/netfilter/nf_conntrack_proto_dccp.c        |    4 +-
>  net/netfilter/nf_conntrack_standalone.c        |   14 +---
>  net/netfilter/nf_conntrack_timestamp.c         |    4 +-
>  net/netfilter/nf_log.c                         |    9 +--
>  net/netrom/sysctl_net_netrom.c                 |   10 +--
>  net/phonet/sysctl.c                            |   10 +--
>  net/rds/ib_sysctl.c                            |   11 +---
>  net/rds/iw_sysctl.c                            |   11 +---
>  net/rds/sysctl.c                               |   11 +---
>  net/rose/sysctl_net_rose.c                     |   10 +--
>  net/sctp/sysctl.c                              |   10 +--
>  net/socket.c                                   |    6 ++
>  net/sysctl_net.c                               |   45 ++++---------
>  net/unix/sysctl_net_unix.c                     |   10 +--
>  net/x25/sysctl_net_x25.c                       |   10 +--
>  net/xfrm/xfrm_sysctl.c                         |    2 +-
>  61 files changed, 209 insertions(+), 606 deletions(-)
> .
> 

^ permalink raw reply

* Re: [PATCH net-next 01/19] net: Implement register_net_sysctl.
From: Pavel Emelyanov @ 2012-04-20  8:45 UTC (permalink / raw)
  To: Eric W. Biederman
  Cc: David Miller, netdev@vger.kernel.org, Serge E. Hallyn, Gao feng,
	pablo@netfilter.org, Stephen Hemminger
In-Reply-To: <m1obqmakl4.fsf@fess.ebiederm.org>

On 04/20/2012 12:11 PM, Eric W. Biederman wrote:
> Pavel Emelyanov <xemul@parallels.com> writes:
> 
>>> @@ -117,6 +117,13 @@ struct ctl_table_header *register_net_sysctl_rotable(const
>>>  }
>>>  EXPORT_SYMBOL_GPL(register_net_sysctl_rotable);
>>>  
>>> +struct ctl_table_header *register_net_sysctl(struct net *net,
>>> +	const char *path, struct ctl_table *table)
>>> +{
>>> +	return __register_sysctl_table(&net->sysctls, path, table);
>>
>> Eric, am I right, that after this all sysctl-s registered in init_net will
>> not be even visible in the non-init net namespaces?
> 
> Yes.
> 
>> If I'm not mistaken, before this all non-virtualized, i.e. "global" sysctls
>> were read-only in sub net namespaces and that solved lots of problems for us.
> 
> Nope.  There are only 4 sysctls that were both global and read only, and
> coincidentally I shoved them all into the initial network namespace in
> patch 4.

OK, thanks.

> So this part of the discussion really belongs about patch 4 but whatever.
> 
> In principle I don't mind the technique of sysctls that are writable
> in the initial network namespace and readable everywhere else.  I hate
> the name register_net_sysctl_rotable because it suggests that every
> sysctl in the table will all be read-only or something like that.
> 
> In practice I think where we are at with converting and looking at
> sysctls is disaster.
> 
> - People complain and want bad hacks so they can avoid writing to
>   sysctls in containers but don't seem to work on the clean solutions.
> 
> - It is not discoverable which sysctls are per network namespace.
> 
> - We have only made a grand total 4 sysctls (in 3 tables) writable
>   in the initial network namespace readable everywhere else.
> 
> So I think the best path forward is to just shove all sysctls that
> aren't per network namespace into the initial network namespace so that
> it is abundantly clear that they are not per network namespace, and
> the fix the sysctls that people care about to be per network namespace.

Agree.

> I do admit their is actual interest in fixing some of the non-converted
> netfliter sysctls.  So my perception of the situation may be wrong, but
> right now I honestly think we have been too clever and no one knows what
> is going on or cares enough to pay detailed attention.

We constantly see two types of problems with proc files and sysctls.
Various apps fail to work if they cannot do either of two

1. find some sysctl without trying to do anything with it
2. write to some sysctl without checking for the actual result

I don't see the ways of fixing any of the above in the generic way. However
step #1 can be ... work-around-ed by making all non-virtualized sysctls RO
in containers, but this is also not a perfect solution.

> Eric
> 
> .
> 

^ permalink raw reply

* RE: rx_dropped packets stop with tcpdump running
From: Marco Berizzi @ 2012-04-20  8:34 UTC (permalink / raw)
  To: tushar.n.dave; +Cc: netdev, eric.dumazet
In-Reply-To: <061C8A8601E8EE4CA8D8FD6990CEA8910FF2D23F@ORSMSX102.amr.corp.intel.com>


tushar.n.dave@intel.com wrote:
> Marco,

Hi Dave,

as pointed by Eric, the counter is
increasing because a windows box is
sending out packets with ethertype
0x886d

> Do you see any packet drop at HW level?

no, at hw level (ethtool -S eth2)
all is in good shape

> How are you measuring drop packet?

cat /sys/class/net/eth2/statistics/rx_dropped

> ethtool -S ethx shows you any packet drop?

no, all fine:

root@Teti:~# ethtool -S eth2
NIC statistics:
     rx_packets: 5939685
     tx_packets: 6108953
     rx_bytes: 1416989994
     tx_bytes: 5089228182
     rx_broadcast: 470407
     tx_broadcast: 56489
     rx_multicast: 135
     tx_multicast: 0
     rx_errors: 4
     tx_errors: 0
     tx_dropped: 0
     multicast: 135
     collisions: 0
     rx_length_errors: 4
     rx_over_errors: 0
     rx_crc_errors: 0
     rx_frame_errors: 0
     rx_no_buffer_count: 0
     rx_missed_errors: 0
     tx_aborted_errors: 0
     tx_carrier_errors: 0
     tx_fifo_errors: 0
     tx_heartbeat_errors: 0
     tx_window_errors: 0
     tx_abort_late_coll: 0
     tx_deferred_ok: 0
     tx_single_coll_ok: 0
     tx_multi_coll_ok: 0
     tx_timeout_count: 0
     tx_restart_queue: 0
     rx_long_length_errors: 0
     rx_short_length_errors: 4
     rx_align_errors: 0
     tx_tcp_seg_good: 0
     tx_tcp_seg_failed: 0
     rx_flow_control_xon: 0
     rx_flow_control_xoff: 0
     tx_flow_control_xon: 0
     tx_flow_control_xoff: 0
     rx_long_byte_count: 1416989994
     rx_csum_offload_good: 5252692
     rx_csum_offload_errors: 3
     rx_header_split: 0
     alloc_rx_buff_failed: 0
     tx_smbus: 0
     rx_smbus: 0
     dropped_smbus: 0
     rx_dma_failed: 0
     tx_dma_failed: 0

> Also can you please send me the eeprom dump
> of you card/LOM. ethtool -e ethx

here is the eeprom dump:

root@Teti:~# ethtool -e eth2
Offset          Values
------          ------
0x0000          00 19 99 ab 2e f6 20 0c 46 f7 01 10 ff ff ff ff 
0x0010          31 31 30 32 6b 02 92 11 34 17 d3 10 ff ff 58 a8 
0x0020          00 00 01 20 74 7e ff ff 00 10 c8 00 00 00 04 27 
0x0030          c9 6c 50 31 3e 07 0b 46 84 21 40 01 00 f0 06 07 
0x0040          00 60 80 00 04 0f ff 7f 01 4d 00 c6 00 00 ff 20 
0x0050          28 00 93 00 50 00 01 01 01 00 b3 05 68 00 ff ff 
0x0060          00 01 00 40 28 13 13 40 ff ff ff ff ff ff ff ff 
0x0070          ff ff ff ff ff ff ff ff ff ff 20 01 ff ff 6b 08 
0x0080          ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
0x0090          ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
0x00a0          00 00 1f 38 0a 00 04 00 00 00 1f 38 07 00 04 00 
0x00b0          00 00 1f 38 08 00 04 00 00 00 1f 38 09 00 04 00 
0x00c0          02 01 01 00 ff ff ff ff ff ff ff ff ff ff ff ff 
0x00d0          00 00 1f 38 0a 00 04 00 80 0a 00 20 ef ef ef ef 
0x00e0          00 00 1f 38 0b 00 04 00 00 00 1f 38 03 00 04 00 
0x00f0          00 00 1f 38 05 00 04 00 00 00 1f 38 02 00 04 00 
0x0100          80 07 00 20 ef ef ef ef 00 04 00 00 04 04 00 00 
0x0110          08 08 00 08 04 00 08 00 04 00 04 00 ff 00 00 00 
0x0120          ff 00 00 04 00 00 00 00 00 00 00 00 01 01 01 00 
0x0130          00 00 df 09 98 11 00 00 00 00 1f 38 01 00 04 00 
0x0140          00 fd e4 67 00 01 00 00 02 1d 00 20 00 fd e5 7f 
0x0150          81 50 00 00 02 15 00 20 20 7a e2 7f 02 06 00 20 
0x0160          10 84 04 08 00 7c 3f 60 00 00 04 00 00 82 28 6a 
0x0170          01 7a e0 67 01 82 28 72 00 22 0a 10 0a fe ff 61 
0x0180          13 fc 44 10 00 00 00 00 00 00 1f 38 9e 02 04 00 
0x0190          21 7a e2 7f 82 02 00 20 12 fe 08 88 01 7e 00 60 
0x01a0          14 80 44 10 0b fe ff 61 80 fa ff 27 06 7a e2 7f 
0x01b0          82 09 00 20 08 fe 04 40 00 fe 3f 60 02 02 80 11 
0x01c0          02 02 80 11 02 02 80 11 02 7c 80 11 57 01 00 00 
0x01d0          02 7c 40 11 06 00 00 00 78 fe 6a 50 04 80 21 08 
0x01e0          02 fe 5f 60 40 02 1f 38 e0 04 04 00 00 80 21 08 
0x01f0          04 fe 5f 60 40 02 1f 38 e0 04 04 00 0f fe ff 61 
0x0200          17 7a e6 7f 02 02 00 20 00 17 e5 47 02 01 00 20 
0x0210          24 fc 84 10 00 00 00 00 00 00 df 09 98 11 00 00 
0x0220          00 17 e5 47 01 fc c6 79 00 00 00 00 00 1c 1f 10 
0x0230          98 11 00 00 00 00 1f 38 0c 00 04 00 ff ff ff ff 
0x0240          69 53 84 03 01 00 00 00 00 00 00 00 00 00 00 00 
0x0250          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0260          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0270          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0280          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0290          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x02a0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x02b0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x02c0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x02d0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x02e0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x02f0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0300          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0310          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0320          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0330          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0340          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0350          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0360          00 00 00 00 00 00 00 00 03 3d 00 00 00 00 00 00 
0x0370          00 00 00 00 00 00 00 00 00 00 00 00 bc 0c 00 00 
0x0380          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0390          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x03a0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x03b0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x03c0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x03d0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x03e0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x03f0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0400          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0410          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0420          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0430          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0440          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0450          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0460          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0470          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0480          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0490          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x04a0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x04b0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x04c0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x04d0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x04e0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x04f0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0500          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0510          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0520          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0530          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0540          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0550          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0560          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0570          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0580          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x0590          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x05a0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x05b0          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0x05c0          00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff 
[...]
0x7ff0          ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 

 		 	   		  

^ permalink raw reply

* Re: [PATCH] vmsplice: relax alignement requirements for SPLICE_F_GIFT
From: Eric Dumazet @ 2012-04-20  8:35 UTC (permalink / raw)
  To: Jens Axboe
  Cc: linux-kernel, David Miller, netdev, Tom Herbert, Al Viro,
	Hugh Dickins, Changli Gao, Andrew Morton, Miklos Szeredi
In-Reply-To: <20120420081953.GJ7505@kernel.dk>

On Fri, 2012-04-20 at 10:19 +0200, Jens Axboe wrote:
> On Wed, Apr 04 2012, Eric Dumazet wrote:
> > It seems there is no fundamental reason to limit vmsplice()
> > SPLICE_F_GIFT to page aligned chunks.
> > 
> > All helpers are prepared to cope with offsets in page.
> > 
> > This limitation makes vmsplice() API very impractical in the zero-copy
> > land.
> 
> Sorry for the slow reply on this one. It's been a long time since I
> wrote that code... If I recall correctly, the reason it currently
> requires alignment, is that a gifted page is no longer yours, by
> definition. So the alignment check was to ensure that we gift full
> pages. But if the caller screws that up, I guess it's the callers
> problem and it's a shame to limit the API in this way.
> 
> I will apply it for 3.5.
> 

Thanks Jens 

^ permalink raw reply

* Re: [PATCH 1/2] workqueue: Catch more locking problems with flush_work()
From: Yong Zhang @ 2012-04-20  8:32 UTC (permalink / raw)
  To: Stephen Boyd; +Cc: linux-kernel, Tejun Heo, netdev, Ben Dooks
In-Reply-To: <4F911BCB.7010809@codeaurora.org>

On Fri, Apr 20, 2012 at 01:18:19AM -0700, Stephen Boyd wrote:
> On 4/20/2012 12:18 AM, Yong Zhang wrote:
> > On Thu, Apr 19, 2012 at 11:26:47PM -0700, Stephen Boyd wrote:
> >> complain in the case where the work is not queued. That case is not a
> >> false positive. We will get a lockdep warning if the work is running
> > IIRC, flush_work() is just a nop when a work is not queued nor running.
> 
> Agreed, but it's better to always print a lockdep warning instead of
> only when the deadlock is going to occur.

It will IMHO.

> 
> >
> >> (when start_flush_work() returns true) solely with the
> >> lock_map_acquire() on cwq->wq->lockdep_map.
> > Yeah, that is the point we use lockdep to detect deadlock for workqueue.
> >
> > But when looking at start_flush_work(), for some case
> > !(cwq->wq->saved_max_active == 1 || cwq->wq->flags & WQ_RESCUER),
> > lock_map_acquire_read() is called, but recursive read is not added to
> > the chain list. So when lock_map_acquire_read(&cwq->wq->lockdep_map)
> > is called, deadlock will not be detected. I hope you don't hit that
> > special case.
> 
> Hmm. Originally I had what you suggested in my patch but I left it out
> because I wasn't sure if it would cause false positives.
> Do you see any
> possibility for false positives?

No, I don't. My test indeed show nothing (just build and boot).

>I'll add it back in if not.

It's great if you can try it :)

Thanks,
Yong

^ permalink raw reply

* Re: [PATCH] vmsplice: relax alignement requirements for SPLICE_F_GIFT
From: Jens Axboe @ 2012-04-20  8:19 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: linux-kernel, David Miller, netdev, Tom Herbert, Al Viro,
	Hugh Dickins, Changli Gao, Andrew Morton, Miklos Szeredi
In-Reply-To: <1333530531.18626.550.camel@edumazet-glaptop>

On Wed, Apr 04 2012, Eric Dumazet wrote:
> It seems there is no fundamental reason to limit vmsplice()
> SPLICE_F_GIFT to page aligned chunks.
> 
> All helpers are prepared to cope with offsets in page.
> 
> This limitation makes vmsplice() API very impractical in the zero-copy
> land.

Sorry for the slow reply on this one. It's been a long time since I
wrote that code... If I recall correctly, the reason it currently
requires alignment, is that a gifted page is no longer yours, by
definition. So the alignment check was to ensure that we gift full
pages. But if the caller screws that up, I guess it's the callers
problem and it's a shame to limit the API in this way.

I will apply it for 3.5.

-- 
Jens Axboe

^ permalink raw reply

* Re: [PATCH 1/2] workqueue: Catch more locking problems with flush_work()
From: Stephen Boyd @ 2012-04-20  8:18 UTC (permalink / raw)
  To: Yong Zhang; +Cc: linux-kernel, Tejun Heo, netdev, Ben Dooks
In-Reply-To: <20120420071759.GA17846@zhy>

On 4/20/2012 12:18 AM, Yong Zhang wrote:
> On Thu, Apr 19, 2012 at 11:26:47PM -0700, Stephen Boyd wrote:
>> complain in the case where the work is not queued. That case is not a
>> false positive. We will get a lockdep warning if the work is running
> IIRC, flush_work() is just a nop when a work is not queued nor running.

Agreed, but it's better to always print a lockdep warning instead of
only when the deadlock is going to occur.

>
>> (when start_flush_work() returns true) solely with the
>> lock_map_acquire() on cwq->wq->lockdep_map.
> Yeah, that is the point we use lockdep to detect deadlock for workqueue.
>
> But when looking at start_flush_work(), for some case
> !(cwq->wq->saved_max_active == 1 || cwq->wq->flags & WQ_RESCUER),
> lock_map_acquire_read() is called, but recursive read is not added to
> the chain list. So when lock_map_acquire_read(&cwq->wq->lockdep_map)
> is called, deadlock will not be detected. I hope you don't hit that
> special case.

Hmm. Originally I had what you suggested in my patch but I left it out
because I wasn't sure if it would cause false positives. Do you see any
possibility for false positives? I'll add it back in if not.

-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply

* Re: [PATCH net-next 01/19] net: Implement register_net_sysctl.
From: Eric W. Biederman @ 2012-04-20  8:11 UTC (permalink / raw)
  To: Pavel Emelyanov
  Cc: David Miller, netdev@vger.kernel.org, Serge E. Hallyn, Gao feng,
	pablo@netfilter.org, Stephen Hemminger
In-Reply-To: <4F90F3AB.7050600@parallels.com>

Pavel Emelyanov <xemul@parallels.com> writes:

>> @@ -117,6 +117,13 @@ struct ctl_table_header *register_net_sysctl_rotable(const
>>  }
>>  EXPORT_SYMBOL_GPL(register_net_sysctl_rotable);
>>  
>> +struct ctl_table_header *register_net_sysctl(struct net *net,
>> +	const char *path, struct ctl_table *table)
>> +{
>> +	return __register_sysctl_table(&net->sysctls, path, table);
>
> Eric, am I right, that after this all sysctl-s registered in init_net will
> not be even visible in the non-init net namespaces?

Yes.

> If I'm not mistaken, before this all non-virtualized, i.e. "global" sysctls
> were read-only in sub net namespaces and that solved lots of problems for us.

Nope.  There are only 4 sysctls that were both global and read only, and
coincidentally I shoved them all into the initial network namespace in
patch 4.

So this part of the discussion really belongs about patch 4 but whatever.

In principle I don't mind the technique of sysctls that are writable
in the initial network namespace and readable everywhere else.  I hate
the name register_net_sysctl_rotable because it suggests that every
sysctl in the table will all be read-only or something like that.

In practice I think where we are at with converting and looking at
sysctls is disaster.

- People complain and want bad hacks so they can avoid writing to
  sysctls in containers but don't seem to work on the clean solutions.

- It is not discoverable which sysctls are per network namespace.

- We have only made a grand total 4 sysctls (in 3 tables) writable
  in the initial network namespace readable everywhere else.

So I think the best path forward is to just shove all sysctls that
aren't per network namespace into the initial network namespace so that
it is abundantly clear that they are not per network namespace, and
the fix the sysctls that people care about to be per network namespace.

I do admit their is actual interest in fixing some of the non-converted
netfliter sysctls.  So my perception of the situation may be wrong, but
right now I honestly think we have been too clever and no one knows what
is going on or cares enough to pay detailed attention.

Eric

^ permalink raw reply

* [PATCH net-next] af_packet: packet_getsockopt() cleanup
From: Eric Dumazet @ 2012-04-20  7:56 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

From: Eric Dumazet <edumazet@google.com>

Factorize code, since most fetched values are int type.

Signed-off-by: Eric Dumazet <edumazet@google.com>
---
 net/packet/af_packet.c |   46 ++++++---------------------------------
 1 file changed, 8 insertions(+), 38 deletions(-)

diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 40053a0..0f66174 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -3224,10 +3224,10 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
 			     char __user *optval, int __user *optlen)
 {
 	int len;
-	int val;
+	int val, lv = sizeof(val);
 	struct sock *sk = sock->sk;
 	struct packet_sock *po = pkt_sk(sk);
-	void *data;
+	void *data = &val;
 	struct tpacket_stats st;
 	union tpacket_stats_u st_u;
 
@@ -3242,21 +3242,17 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
 
 	switch (optname) {
 	case PACKET_STATISTICS:
-		if (po->tp_version == TPACKET_V3) {
-			len = sizeof(struct tpacket_stats_v3);
-		} else {
-			if (len > sizeof(struct tpacket_stats))
-				len = sizeof(struct tpacket_stats);
-		}
 		spin_lock_bh(&sk->sk_receive_queue.lock);
 		if (po->tp_version == TPACKET_V3) {
+			lv = sizeof(struct tpacket_stats_v3);
 			memcpy(&st_u.stats3, &po->stats,
-			sizeof(struct tpacket_stats));
+			       sizeof(struct tpacket_stats));
 			st_u.stats3.tp_freeze_q_cnt =
-			po->stats_u.stats3.tp_freeze_q_cnt;
+					po->stats_u.stats3.tp_freeze_q_cnt;
 			st_u.stats3.tp_packets += po->stats.tp_drops;
 			data = &st_u.stats3;
 		} else {
+			lv = sizeof(struct tpacket_stats);
 			st = po->stats;
 			st.tp_packets += st.tp_drops;
 			data = &st;
@@ -3265,31 +3261,16 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
 		spin_unlock_bh(&sk->sk_receive_queue.lock);
 		break;
 	case PACKET_AUXDATA:
-		if (len > sizeof(int))
-			len = sizeof(int);
 		val = po->auxdata;
-
-		data = &val;
 		break;
 	case PACKET_ORIGDEV:
-		if (len > sizeof(int))
-			len = sizeof(int);
 		val = po->origdev;
-
-		data = &val;
 		break;
 	case PACKET_VNET_HDR:
-		if (len > sizeof(int))
-			len = sizeof(int);
 		val = po->has_vnet_hdr;
-
-		data = &val;
 		break;
 	case PACKET_VERSION:
-		if (len > sizeof(int))
-			len = sizeof(int);
 		val = po->tp_version;
-		data = &val;
 		break;
 	case PACKET_HDRLEN:
 		if (len > sizeof(int))
@@ -3309,39 +3290,28 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
 		default:
 			return -EINVAL;
 		}
-		data = &val;
 		break;
 	case PACKET_RESERVE:
-		if (len > sizeof(unsigned int))
-			len = sizeof(unsigned int);
 		val = po->tp_reserve;
-		data = &val;
 		break;
 	case PACKET_LOSS:
-		if (len > sizeof(unsigned int))
-			len = sizeof(unsigned int);
 		val = po->tp_loss;
-		data = &val;
 		break;
 	case PACKET_TIMESTAMP:
-		if (len > sizeof(int))
-			len = sizeof(int);
 		val = po->tp_tstamp;
-		data = &val;
 		break;
 	case PACKET_FANOUT:
-		if (len > sizeof(int))
-			len = sizeof(int);
 		val = (po->fanout ?
 		       ((u32)po->fanout->id |
 			((u32)po->fanout->type << 16)) :
 		       0);
-		data = &val;
 		break;
 	default:
 		return -ENOPROTOOPT;
 	}
 
+	if (len > lv)
+		len = lv;
 	if (put_user(len, optlen))
 		return -EFAULT;
 	if (copy_to_user(optval, data, len))

^ permalink raw reply related

* Re: [PATCH 2/9] atl1c: refine phy-register read/write function
From: Florian Fainelli @ 2012-04-20  7:40 UTC (permalink / raw)
  To: xiong; +Cc: davem, netdev, linux-kernel, qca-linux-team, nic-devel
In-Reply-To: <1334902584-15584-3-git-send-email-xiong@qca.qualcomm.com>

Hi Xiong,

Le 04/20/12 08:16, xiong a écrit :
> phy register is read/write via MDIO control module ---
> that module will be affected by the hibernate status,
> to access phy regs in hib stutus, slow frequency clk must
> be selected.
> To access phy extension register, the MDIO related
> registers are refined/updated, a _core function is
> re-wroted for both regular PHY regs and extension regs.
> existing PHY r/w function is revised based on the _core.
> PHY extension registers will be used for the comming
> patches.
>
> Signed-off-by: xiong<xiong@qca.qualcomm.com>
> Tested-by: Liu David<dwliu@qca.qualcomm.com>
> ---
>   drivers/net/ethernet/atheros/atl1c/atl1c_hw.c   |  178 +++++++++++++++++++----
>   drivers/net/ethernet/atheros/atl1c/atl1c_hw.h   |   64 +++++---
>   drivers/net/ethernet/atheros/atl1c/atl1c_main.c |    4 +-
>   3 files changed, 189 insertions(+), 57 deletions(-)
>
> diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
> index bd1667c..6cbe78a 100644
> --- a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
> +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
> @@ -277,65 +277,181 @@ void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value)
>   	AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta);
>   }
>
> +
> +void atl1c_stop_phy_polling(struct atl1c_hw *hw)
> +{
> +	u32 val;
> +	int i;
> +
> +	if (hw->ctrl_flags&  ATL1C_FPGA_VERSION) {
> +		AT_WRITE_REG(hw, REG_MDIO_CTRL, 0);
> +		for (i = 0; i<  MDIO_MAX_AC_TO; i++) {
> +			AT_READ_REG(hw, REG_MDIO_CTRL,&val);
> +			if (0 == (val&  MDIO_CTRL_BUSY))
> +				break;
> +			udelay(10);
> +		}
> +	}
> +}

Please reduce the identation by doing this:

if (!(hw->ctrl_flags & ALT1C_FPAG_VERSION))
	return;

that makes it much more readable.

> +
> +void atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel)
> +{
> +	u32 val;
> +	int i;
> +
> +	if (hw->ctrl_flags&  ATL1C_FPGA_VERSION) {
> +		val = MDIO_CTRL_SPRES_PRMBL |
> +			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
> +			FIELDX(MDIO_CTRL_REG, 1) |
> +			MDIO_CTRL_START |
> +			MDIO_CTRL_OP_READ;
> +		AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
> +		for (i = 0; i<  MDIO_MAX_AC_TO; i++) {
> +			AT_READ_REG(hw, REG_MDIO_CTRL,&val);
> +			if (0 == (val&  MDIO_CTRL_BUSY))
> +				break;
> +			udelay(10);
> +		}
> +		val |= MDIO_CTRL_AP_EN;
> +		val&= ~MDIO_CTRL_START;
> +		AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
> +		udelay(30);
> +	}
> +}

Seems like the for() busy-checking of the register could use their own 
function since it is used both in : atl1c_stop_phy_polling() and 
atl1c_start_phy_polling().

> +
> +
>   /*
> - * Reads the value from a PHY register
> - * hw - Struct containing variables accessed by shared code
> - * reg_addr - address of the PHY register to read
> + * atl1c_read_phy_core
> + * core funtion to read register in PHY via MDIO control regsiter.
> + * ext: extension register (see IEEE 802.3)
> + * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
> + * reg: reg to read
>    */
> -int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data)
> +int atl1c_read_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
> +			u16 reg, u16 *phy_data)
>   {
>   	u32 val;
> +	u16 clk_sel;
>   	int i;
>
> -	val = ((u32)(reg_addr&  MDIO_REG_ADDR_MASK))<<  MDIO_REG_ADDR_SHIFT |
> -		MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW |
> -		MDIO_CLK_25_4<<  MDIO_CLK_SEL_SHIFT;
> +	atl1c_stop_phy_polling(hw);
>
> +	*phy_data = 0;
> +	clk_sel = hw->hibernate ? MDIO_CTRL_CLK_25_128 : MDIO_CTRL_CLK_25_4;
> +	if (ext) {
> +		val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg);
> +		AT_WRITE_REG(hw, REG_MDIO_EXTN, val);
> +		val = MDIO_CTRL_SPRES_PRMBL |
> +			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
> +			MDIO_CTRL_START |
> +			MDIO_CTRL_MODE_EXT |
> +			MDIO_CTRL_OP_READ;
> +	} else {
> +		val = MDIO_CTRL_SPRES_PRMBL |
> +			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
> +			FIELDX(MDIO_CTRL_REG, reg) |
> +			MDIO_CTRL_START |
> +			MDIO_CTRL_OP_READ;
> +	}
>   	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
>
> -	for (i = 0; i<  MDIO_WAIT_TIMES; i++) {
> -		udelay(2);
> +	for (i = 0; i<  MDIO_MAX_AC_TO; i++) {
>   		AT_READ_REG(hw, REG_MDIO_CTRL,&val);
> -		if (!(val&  (MDIO_START | MDIO_BUSY)))
> +		if (0 == (val&  MDIO_CTRL_BUSY)) {
> +			*phy_data = (u16)FIELD_GETX(val, MDIO_CTRL_DATA);
>   			break;
> +		}
> +		udelay(10);

same here, you could use the helper for busy-checking MDIO_CTRL_BUSY.

>   	}
> -	if (!(val&  (MDIO_START | MDIO_BUSY))) {
> -		*phy_data = (u16)val;
> -		return 0;
> -	}
> +	if (MDIO_MAX_AC_TO == i)
> +		return -1;
>
> -	return -1;
> +	atl1c_start_phy_polling(hw, clk_sel);
> +
> +	return 0;
>   }
>
>   /*
> - * Writes a value to a PHY register
> - * hw - Struct containing variables accessed by shared code
> - * reg_addr - address of the PHY register to write
> - * data - data to write to the PHY
> + * atl1c_write_phy_core
> + * core funtion to write to register in PHY via MDIO control regsiter.
> + * ext: extension register (see IEEE 802.3)
> + * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
> + * reg: reg to write
>    */
> -int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data)
> +int atl1c_write_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
> +			u16 reg, u16 phy_data)
>   {
> -	int i;
>   	u32 val;
> +	u16 clk_sel;
> +	int i;
>
> -	val = ((u32)(phy_data&  MDIO_DATA_MASK))<<  MDIO_DATA_SHIFT   |
> -	       (reg_addr&  MDIO_REG_ADDR_MASK)<<  MDIO_REG_ADDR_SHIFT |
> -	       MDIO_SUP_PREAMBLE | MDIO_START |
> -	       MDIO_CLK_25_4<<  MDIO_CLK_SEL_SHIFT;
> +	atl1c_stop_phy_polling(hw);
>
> +	clk_sel = hw->hibernate ? MDIO_CTRL_CLK_25_128 : MDIO_CTRL_CLK_25_4;
> +	if (ext) {
> +		val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg);
> +		AT_WRITE_REG(hw, REG_MDIO_EXTN, val);
> +		val = MDIO_CTRL_SPRES_PRMBL |
> +			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
> +			FIELDX(MDIO_CTRL_DATA, phy_data) |
> +			MDIO_CTRL_START |
> +			MDIO_CTRL_MODE_EXT;
> +	} else {
> +		val = MDIO_CTRL_SPRES_PRMBL |
> +			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
> +			FIELDX(MDIO_CTRL_DATA, phy_data) |
> +			FIELDX(MDIO_CTRL_REG, reg) |
> +			MDIO_CTRL_START;
> +	}
>   	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
>
> -	for (i = 0; i<  MDIO_WAIT_TIMES; i++) {
> -		udelay(2);
> +	for (i = 0; i<  MDIO_MAX_AC_TO; i++) {
>   		AT_READ_REG(hw, REG_MDIO_CTRL,&val);
> -		if (!(val&  (MDIO_START | MDIO_BUSY)))
> +		if (0 == (val&  MDIO_CTRL_BUSY))
>   			break;
> +		udelay(10);
>   	}
> +	if (MDIO_MAX_AC_TO == i)
> +		return -1;
>
> -	if (!(val&  (MDIO_START | MDIO_BUSY)))
> -		return 0;
> +	atl1c_start_phy_polling(hw, clk_sel);
>
> -	return -1;
> +	return 0;
> +}
> +
> +/*
> + * Reads the value from a PHY register
> + * hw - Struct containing variables accessed by shared code
> + * reg_addr - address of the PHY register to read
> + */
> +int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data)
> +{
> +	return atl1c_read_phy_core(hw, false, 0, reg_addr, phy_data);
> +}
> +
> +/*
> + * Writes a value to a PHY register
> + * hw - Struct containing variables accessed by shared code
> + * reg_addr - address of the PHY register to write
> + * data - data to write to the PHY
> + */
> +int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data)
> +{
> +	return atl1c_write_phy_core(hw, false, 0, reg_addr, phy_data);
> +}
> +
> +/* read from PHY extension register */
> +int atl1c_read_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
> +			u16 reg_addr, u16 *phy_data)
> +{
> +	return atl1c_read_phy_core(hw, true, dev_addr, reg_addr, phy_data);
> +}
> +
> +/* write to PHY extension register */
> +int atl1c_write_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
> +			u16 reg_addr, u16 phy_data)
> +{
> +	return atl1c_write_phy_core(hw, true, dev_addr, reg_addr, phy_data);
>   }
>
>   /*
> diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h
> index 459e141..eb7f3bb 100644
> --- a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h
> +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h
> @@ -49,6 +49,16 @@ int atl1c_phy_init(struct atl1c_hw *hw);
>   int atl1c_check_eeprom_exist(struct atl1c_hw *hw);
>   int atl1c_restart_autoneg(struct atl1c_hw *hw);
>   int atl1c_phy_power_saving(struct atl1c_hw *hw);
> +void atl1c_stop_phy_polling(struct atl1c_hw *hw);
> +void atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel);
> +int atl1c_read_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
> +			u16 reg, u16 *phy_data);
> +int atl1c_write_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
> +			u16 reg, u16 phy_data);
> +int atl1c_read_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
> +			u16 reg_addr, u16 *phy_data);
> +int atl1c_write_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
> +			u16 reg_addr, u16 phy_data);
>   /* register definition */
>   #define REG_DEVICE_CAP              	0x5C
>   #define DEVICE_CAP_MAX_PAYLOAD_MASK     0x7
> @@ -268,30 +278,36 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw);
>
>   /* MDIO Control Register */
>   #define REG_MDIO_CTRL           	0x1414
> -#define MDIO_DATA_MASK          	0xffff  /* On MDIO write, the 16-bit
> -						 * control data to write to PHY
> -						 * MII management register */
> -#define MDIO_DATA_SHIFT         	0       /* On MDIO read, the 16-bit
> -						 * status data that was read
> -						 * from the PHY MII management register */
> -#define MDIO_REG_ADDR_MASK      	0x1f    /* MDIO register address */
> -#define MDIO_REG_ADDR_SHIFT     	16
> -#define MDIO_RW                 	0x200000  /* 1: read, 0: write */
> -#define MDIO_SUP_PREAMBLE       	0x400000  /* Suppress preamble */
> -#define MDIO_START              	0x800000  /* Write 1 to initiate the MDIO
> -						   * master. And this bit is self
> -						   * cleared after one cycle */
> -#define MDIO_CLK_SEL_SHIFT      	24
> -#define MDIO_CLK_25_4           	0
> -#define MDIO_CLK_25_6           	2
> -#define MDIO_CLK_25_8           	3
> -#define MDIO_CLK_25_10          	4
> -#define MDIO_CLK_25_14          	5
> -#define MDIO_CLK_25_20          	6
> -#define MDIO_CLK_25_28          	7
> -#define MDIO_BUSY               	0x8000000
> -#define MDIO_AP_EN              	0x10000000
> -#define MDIO_WAIT_TIMES         	10
> +#define MDIO_CTRL_MODE_EXT		BIT(30)
> +#define MDIO_CTRL_POST_READ		BIT(29)
> +#define MDIO_CTRL_AP_EN			BIT(28)
> +#define MDIO_CTRL_BUSY			BIT(27)
> +#define MDIO_CTRL_CLK_SEL_MASK		0x7UL
> +#define MDIO_CTRL_CLK_SEL_SHIFT		24
> +#define MDIO_CTRL_CLK_25_4		0	/* 25MHz divide 4 */
> +#define MDIO_CTRL_CLK_25_6		2
> +#define MDIO_CTRL_CLK_25_8		3
> +#define MDIO_CTRL_CLK_25_10		4
> +#define MDIO_CTRL_CLK_25_32		5
> +#define MDIO_CTRL_CLK_25_64		6
> +#define MDIO_CTRL_CLK_25_128		7
> +#define MDIO_CTRL_START			BIT(23)
> +#define MDIO_CTRL_SPRES_PRMBL		BIT(22)
> +#define MDIO_CTRL_OP_READ		BIT(21)	/* 1:read, 0:write */
> +#define MDIO_CTRL_REG_MASK		0x1FUL
> +#define MDIO_CTRL_REG_SHIFT		16
> +#define MDIO_CTRL_DATA_MASK		0xFFFFUL
> +#define MDIO_CTRL_DATA_SHIFT		0
> +#define MDIO_MAX_AC_TO			120	/* 1.2ms timeout for slow clk */
> +
> +/* for extension reg access */
> +#define REG_MDIO_EXTN			0x1448
> +#define MDIO_EXTN_PORTAD_MASK		0x1FUL
> +#define MDIO_EXTN_PORTAD_SHIFT		21
> +#define MDIO_EXTN_DEVAD_MASK		0x1FUL
> +#define MDIO_EXTN_DEVAD_SHIFT		16
> +#define MDIO_EXTN_REG_MASK		0xFFFFUL
> +#define MDIO_EXTN_REG_SHIFT		0
>
>   /* BIST Control and Status Register0 (for the Packet Memory) */
>   #define REG_BIST0_CTRL              	0x141c
> diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
> index a6c3f05..1f82880 100644
> --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
> +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
> @@ -2270,7 +2270,7 @@ static int atl1c_open(struct net_device *netdev)
>   		u32 phy_data;
>
>   		AT_READ_REG(&adapter->hw, REG_MDIO_CTRL,&phy_data);
> -		phy_data |= MDIO_AP_EN;
> +		phy_data |= MDIO_CTRL_AP_EN;
>   		AT_WRITE_REG(&adapter->hw, REG_MDIO_CTRL, phy_data);
>   	}
>   	return 0;
> @@ -2558,7 +2558,7 @@ static int __devinit atl1c_probe(struct pci_dev *pdev,
>   	adapter->mii.mdio_read  = atl1c_mdio_read;
>   	adapter->mii.mdio_write = atl1c_mdio_write;
>   	adapter->mii.phy_id_mask = 0x1f;
> -	adapter->mii.reg_num_mask = MDIO_REG_ADDR_MASK;
> +	adapter->mii.reg_num_mask = MDIO_CTRL_REG_MASK;
>   	netif_napi_add(netdev,&adapter->napi, atl1c_clean, 64);
>   	setup_timer(&adapter->phy_config_timer, atl1c_phy_config,
>   			(unsigned long)adapter);

^ permalink raw reply

* Re: [PATCH 3/3] decrement static keys on real destroy time
From: KAMEZAWA Hiroyuki @ 2012-04-20  7:38 UTC (permalink / raw)
  To: Glauber Costa
  Cc: Tejun Heo, netdev-u79uwXL29TY76Z2rM5mHXA,
	cgroups-u79uwXL29TY76Z2rM5mHXA, Li Zefan, David Miller,
	devel-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <1334875758-20939-4-git-send-email-glommer-bzQdu9zFT3WakBO8gow8eQ@public.gmane.org>

(2012/04/20 7:49), Glauber Costa wrote:

> We call the destroy function when a cgroup starts to be removed,
> such as by a rmdir event.
> 
> However, because of our reference counters, some objects are still
> inflight. Right now, we are decrementing the static_keys at destroy()
> time, meaning that if we get rid of the last static_key reference,
> some objects will still have charges, but the code to properly
> uncharge them won't be run.
> 
> This becomes a problem specially if it is ever enabled again, because
> now new charges will be added to the staled charges making keeping
> it pretty much impossible.
> 
> We just need to be careful with the static branch activation:
> since there is no particular preferred order of their activation,
> we need to make sure that we only start using it after all
> call sites are active. This is achieved by having a per-memcg
> flag that is only updated after static_key_slow_inc() returns.
> At this time, we are sure all sites are active.
> 
> This is made per-memcg, not global, for a reason:
> it also has the effect of making socket accounting more
> consistent. The first memcg to be limited will trigger static_key()
> activation, therefore, accounting. But all the others will then be
> accounted no matter what. After this patch, only limited memcgs
> will have its sockets accounted.
> 
> [v2: changed a tcp limited flag for a generic proto limited flag ]
> 
> Signed-off-by: Glauber Costa <glommer-bzQdu9zFT3WakBO8gow8eQ@public.gmane.org>

> ---
>  include/net/sock.h        |    9 +++++++
>  mm/memcontrol.c           |   20 +++++++++++++++-
>  net/ipv4/tcp_memcontrol.c |   52 ++++++++++++++++++++++++++++++++++++++------
>  3 files changed, 72 insertions(+), 9 deletions(-)
> 
> diff --git a/include/net/sock.h b/include/net/sock.h
> index b3ebe6b..c5a2010 100644
> --- a/include/net/sock.h
> +++ b/include/net/sock.h
> @@ -914,6 +914,15 @@ struct cg_proto {
>  	int			*memory_pressure;
>  	long			*sysctl_mem;
>  	/*
> +	 * active means it is currently active, and new sockets should
> +	 * be assigned to cgroups.
> +	 *
> +	 * activated means it was ever activated, and we need to
> +	 * disarm the static keys on destruction
> +	 */
> +	bool			activated;
> +	bool			active; 
> +	/*
>  	 * memcg field is used to find which memcg we belong directly
>  	 * Each memcg struct can hold more than one cg_proto, so container_of
>  	 * won't really cut.
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 7832b4d..01d25a0 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -404,6 +404,7 @@ void sock_update_memcg(struct sock *sk)
>  {
>  	if (mem_cgroup_sockets_enabled) {
>  		struct mem_cgroup *memcg;
> +		struct cg_proto *cg_proto;
>  
>  		BUG_ON(!sk->sk_prot->proto_cgroup);
>  
> @@ -423,9 +424,10 @@ void sock_update_memcg(struct sock *sk)
>  
>  		rcu_read_lock();
>  		memcg = mem_cgroup_from_task(current);
> -		if (!mem_cgroup_is_root(memcg)) {
> +		cg_proto = sk->sk_prot->proto_cgroup(memcg);
> +		if (!mem_cgroup_is_root(memcg) && cg_proto->active) {

>

>  			mem_cgroup_get(memcg);
> -			sk->sk_cgrp = sk->sk_prot->proto_cgroup(memcg);
> +			sk->sk_cgrp = cg_proto;
>  		}



Is this correct ? cg_proto->active can be true before all jump_labels are
patched, then we can loose accounting. That will cause underflow of
res_countner.

cg_proto->active should be set after jump_label modification.
Then, things will work, I guess.

Thanks,
-Kame




>  		rcu_read_unlock();
>  	}
> @@ -442,6 +444,14 @@ void sock_release_memcg(struct sock *sk)
>  	}
>  }
>  
> +static void disarm_static_keys(struct mem_cgroup *memcg)
> +{
> +#ifdef CONFIG_INET
> +	if (memcg->tcp_mem.cg_proto.activated)
> +		static_key_slow_dec(&memcg_socket_limit_enabled);
> +#endif
> +}
> +
>  #ifdef CONFIG_INET
>  struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg)
>  {
> @@ -452,6 +462,11 @@ struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg)
>  }
>  EXPORT_SYMBOL(tcp_proto_cgroup);
>  #endif /* CONFIG_INET */
> +#else
> +static inline void disarm_static_keys(struct mem_cgroup *memcg)
> +{
> +}
> +
>  #endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */
>  
>  static void drain_all_stock_async(struct mem_cgroup *memcg);
> @@ -4883,6 +4898,7 @@ static void __mem_cgroup_put(struct mem_cgroup *memcg, int count)
>  {
>  	if (atomic_sub_and_test(count, &memcg->refcnt)) {
>  		struct mem_cgroup *parent = parent_mem_cgroup(memcg);
> +		disarm_static_keys(memcg);
>  		__mem_cgroup_free(memcg);
>  		if (parent)
>  			mem_cgroup_put(parent);
> diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c
> index 1517037..d02573a 100644
> --- a/net/ipv4/tcp_memcontrol.c
> +++ b/net/ipv4/tcp_memcontrol.c
> @@ -54,6 +54,8 @@ int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
>  	cg_proto->sysctl_mem = tcp->tcp_prot_mem;
>  	cg_proto->memory_allocated = &tcp->tcp_memory_allocated;
>  	cg_proto->sockets_allocated = &tcp->tcp_sockets_allocated;
> +	cg_proto->active = false;
> +	cg_proto->activated = false;
>  	cg_proto->memcg = memcg;
>  
>  	return 0;
> @@ -74,12 +76,23 @@ void tcp_destroy_cgroup(struct mem_cgroup *memcg)
>  	percpu_counter_destroy(&tcp->tcp_sockets_allocated);
>  
>  	val = res_counter_read_u64(&tcp->tcp_memory_allocated, RES_LIMIT);
> -
> -	if (val != RESOURCE_MAX)
> -		static_key_slow_dec(&memcg_socket_limit_enabled);
>  }
>  EXPORT_SYMBOL(tcp_destroy_cgroup);
>  
> +/*
> + * This is to prevent two writes arriving at the same time
> + * at kmem.tcp.limit_in_bytes.
> + *
> + * There is a race at the first time we write to this file:
> + *
> + * - cg_proto->activated == false for all writers.
> + * - They all do a static_key_slow_inc().
> + * - When we are finally read to decrement the static_keys,
> + *   we'll do it only once per activated cgroup. So we won't
> + *   be able to disable it.
> + */
> +static DEFINE_MUTEX(tcp_set_limit_mutex);
> +
>  static int tcp_update_limit(struct mem_cgroup *memcg, u64 val)
>  {
>  	struct net *net = current->nsproxy->net_ns;
> @@ -107,10 +120,35 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val)
>  		tcp->tcp_prot_mem[i] = min_t(long, val >> PAGE_SHIFT,
>  					     net->ipv4.sysctl_tcp_mem[i]);
>  
> -	if (val == RESOURCE_MAX && old_lim != RESOURCE_MAX)
> -		static_key_slow_dec(&memcg_socket_limit_enabled);
> -	else if (old_lim == RESOURCE_MAX && val != RESOURCE_MAX)
> -		static_key_slow_inc(&memcg_socket_limit_enabled);
> +	if (val == RESOURCE_MAX)
> +		cg_proto->active = false;
> +	else if (val != RESOURCE_MAX) {
> +		cg_proto->active = true;
> +
> +
> +		/*
> +		 * ->activated needs to be written after the static_key update.
> +		 *  This is what guarantees that the socket activation function
> +		 *  is the last one to run. See sock_update_memcg() for details,
> +		 *  and note that we don't mark any socket as belonging to this
> +		 *  memcg until that flag is up.
> +		 *
> +		 *  We need to do this, because static_keys will span multiple
> +		 *  sites, but we can't control their order. If we mark a socket
> +		 *  as accounted, but the accounting functions are not patched in
> +		 *  yet, we'll lose accounting.
> +		 *
> +		 *  We never race with the readers in sock_update_memcg(), because
> +		 *  when this value change, the code to process it is not patched in
> +		 *  yet.
> +		 */
> +		mutex_lock(&tcp_set_limit_mutex);
> +		if (!cg_proto->activated) {
> +			static_key_slow_inc(&memcg_socket_limit_enabled);
> +			cg_proto->activated = true;
> +		}
> +		mutex_unlock(&tcp_set_limit_mutex);
> +	}
>  
>  	return 0;
>  }

^ permalink raw reply

* Re: [PATCH net-next 12/19] net neighbour:  Convert to use register_net_sysctl
From: Eric W. Biederman @ 2012-04-20  7:25 UTC (permalink / raw)
  To: Pavel Emelyanov
  Cc: David Miller, netdev@vger.kernel.org, Serge E. Hallyn, Gao feng,
	pablo@netfilter.org, Stephen Hemminger
In-Reply-To: <4F90F26B.3090906@parallels.com>

Pavel Emelyanov <xemul@parallels.com> writes:

>> @@ -2925,19 +2924,7 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
>>  {
>>  	struct neigh_sysctl_table *t;
>>  	const char *dev_name_source = NULL;
>> -
>> -#define NEIGH_CTL_PATH_ROOT	0
>> -#define NEIGH_CTL_PATH_PROTO	1
>> -#define NEIGH_CTL_PATH_NEIGH	2
>> -#define NEIGH_CTL_PATH_DEV	3
>> -
>> -	struct ctl_path neigh_path[] = {
>> -		{ .procname = "net",	 },
>> -		{ .procname = "proto",	 },
>> -		{ .procname = "neigh",	 },
>> -		{ .procname = "default", },
>> -		{ },
>> -	};
>> +	char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ];
>
> Why two IFNAMSIZ-es? One is for the dev->name, but the other one is not.
> Is it just for not having any other better constant at hands?

Yep.  We don't seem to have any proto name size constants, and all
of decnet ipv4 and ipv6 are all shorter than the 16 bytes of IFNAMSIZ.

Even if I am wrong the snprintf below truncates it's output to the
buffer size and null terminates it so in the worst case we won't cause
a buffer overflow, we will just get a truncated path name to pass
to sysctl.

Shrug I stopped at good enough but I am happy for a better number.

Eric

>>  	t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL);
>>  	if (!t)

^ permalink raw reply

* Re: [PATCH 1/2] workqueue: Catch more locking problems with flush_work()
From: Yong Zhang @ 2012-04-20  7:18 UTC (permalink / raw)
  To: Stephen Boyd; +Cc: linux-kernel, Tejun Heo, netdev, Ben Dooks
In-Reply-To: <4F9101A7.5010100@codeaurora.org>

On Thu, Apr 19, 2012 at 11:26:47PM -0700, Stephen Boyd wrote:
> complain in the case where the work is not queued. That case is not a
> false positive. We will get a lockdep warning if the work is running

IIRC, flush_work() is just a nop when a work is not queued nor running.

> (when start_flush_work() returns true) solely with the
> lock_map_acquire() on cwq->wq->lockdep_map.

Yeah, that is the point we use lockdep to detect deadlock for workqueue.

But when looking at start_flush_work(), for some case
!(cwq->wq->saved_max_active == 1 || cwq->wq->flags & WQ_RESCUER),
lock_map_acquire_read() is called, but recursive read is not added to
the chain list. So when lock_map_acquire_read(&cwq->wq->lockdep_map)
is called, deadlock will not be detected. I hope you don't hit that
special case.

Thanks,
Yong

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox