Netdev List
 help / color / mirror / Atom feed
* [IFGROUPv5 0/3 (+3)] Interface group patches
From: Laszlo Attila Toth @ 2007-10-26 12:30 UTC (permalink / raw)
  To: David Miller; +Cc: Patrick McHardy, netdev, Laszlo Attila Toth

Hi Dave,

This is the 5th version of our interface group patches.

The first patch is a fix in the rtnl socket interface.

An u_int32_t member was added to net devices indicating the interface
group number of the device which can be get/set via netlink.

The xt_ifgroup netfilter match is for checking this value with an
optional mask.

Other patches are for userpace programs:
 * iptables
 
 * iproute2. Because kernel 2.6.24-rc1 introduced a new enum value,
   IFLA_NET_NS_PID, and it wasn't in the iproute2 code, the first
   patch simply adds this value. The second patch adds support of
   interface group.

Usage:
 ip link set eth0 group 4    # set
 ip link set eth0 group 0    # unset
 iptables -A INPUT -m ifgroup --ifgroup-in 4/0xf -j ACCEPT
 iptables -A FORWARD -m ifgroup --ifgroup-in 4  ! --ifgroup-out 5 -j DROP

Patches:
 [1/3] rtnetlink: setlink changes atomic with single notification
 [2/3] Interface group: core (netlink) part
 [3/3] Netfilter Interface group match
 [iptables] Interface group match
 [iproute 1/2] Added IFLA_NET_NS_PID as in kernel v2.6.24-rc1
 [iproute 2/2] Interface group as new ip link option

--
Laszlo Attila Toth

^ permalink raw reply

* [IFGROUPv5 iproute 1/2] Added IFLA_NET_NS_PID as in kernel v2.6.24-rc1
From: Laszlo Attila Toth @ 2007-10-26 12:30 UTC (permalink / raw)
  To: David Miller; +Cc: Patrick McHardy, netdev, Laszlo Attila Toth
In-Reply-To: <11934018352773-git-send-email-panther@balabit.hu>

Signed-off-by: Laszlo Attila Toth <panther@balabit.hu>
---
 include/linux/if_link.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 23b3a8e..c948395 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -78,6 +78,7 @@ enum
 	IFLA_LINKMODE,
 	IFLA_LINKINFO,
 #define IFLA_LINKINFO IFLA_LINKINFO
+	IFLA_NET_NS_PID,
 	__IFLA_MAX
 };
 
-- 
1.5.2.5


^ permalink raw reply related

* [IFGROUPv5 1/3] rtnetlink: setlink changes are unprotected; with single notification
From: Laszlo Attila Toth @ 2007-10-26 12:30 UTC (permalink / raw)
  To: David Miller; +Cc: Patrick McHardy, netdev, Laszlo Attila Toth
In-Reply-To: <11934018353825-git-send-email-panther@balabit.hu>

In do_setlink the device changes don't need to be protected. Notification
is sent at the end of the function once if any modification occured
and once if an address has been changed.

Signed-off-by: Laszlo Attila Toth <panther@balabit.hu>
---
 net/core/rtnetlink.c |   32 ++++++++++++++++++++------------
 1 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 4a2640d..0e278ac 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -537,7 +537,7 @@ int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id,
 
 EXPORT_SYMBOL_GPL(rtnl_put_cacheinfo);
 
-static void set_operstate(struct net_device *dev, unsigned char transition)
+static int set_operstate(struct net_device *dev, unsigned char transition)
 {
 	unsigned char operstate = dev->operstate;
 
@@ -557,11 +557,10 @@ static void set_operstate(struct net_device *dev, unsigned char transition)
 	}
 
 	if (dev->operstate != operstate) {
-		write_lock_bh(&dev_base_lock);
 		dev->operstate = operstate;
-		write_unlock_bh(&dev_base_lock);
-		netdev_state_change(dev);
-	}
+		return 1;
+	} else
+		return 0;
 }
 
 static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
@@ -855,6 +854,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
 	if (tb[IFLA_BROADCAST]) {
 		nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len);
 		send_addr_notify = 1;
+		modified = 1;
 	}
 
 	if (ifm->ifi_flags || ifm->ifi_change) {
@@ -867,16 +867,22 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
 		dev_change_flags(dev, flags);
 	}
 
-	if (tb[IFLA_TXQLEN])
-		dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
+	if (tb[IFLA_TXQLEN]) {
+		if (dev->tx_queue_len != nla_get_u32(tb[IFLA_TXQLEN])) {
+			dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
+			modified = 1;
+		}
+	}
 
-	if (tb[IFLA_OPERSTATE])
-		set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
+	if (tb[IFLA_OPERSTATE]) {
+		modified |= set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
+	}
 
 	if (tb[IFLA_LINKMODE]) {
-		write_lock_bh(&dev_base_lock);
-		dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);
-		write_unlock_bh(&dev_base_lock);
+		if (dev->link_mode != nla_get_u8(tb[IFLA_LINKMODE])) {
+			dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);
+			modified = 1;
+		}
 	}
 
 	err = 0;
@@ -890,6 +896,8 @@ errout:
 
 	if (send_addr_notify)
 		call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+	if (modified)
+		netdev_state_change(dev);
 	return err;
 }
 
-- 
1.5.2.5


^ permalink raw reply related

* [IFGROUPv5 iptables] Interface group match
From: Laszlo Attila Toth @ 2007-10-26 12:30 UTC (permalink / raw)
  To: David Miller; +Cc: Patrick McHardy, netdev, Laszlo Attila Toth
In-Reply-To: <11934018354079-git-send-email-panther@balabit.hu>

Interface group values can be checked on both input and output interfaces
with optional mask.

Signed-off-by: Laszlo Attila Toth <panther@balabit.hu>
---
 Makefile                     |    2 
 libip6t_ifgroup.man          |   36 +++++++
 libipt_ifgroup.man           |   36 +++++++
 libxt_ifgroup.c              |  202 +++++++++++++++++++++++++++++++++++++++++++
 linux/netfilter/xt_ifgroup.h |   18 +++
 5 files changed, 293 insertions(+), 1 deletion(-)

Index: include/linux/netfilter/xt_ifgroup.h
===================================================================
--- include/linux/netfilter/xt_ifgroup.h	(revision 0)
+++ include/linux/netfilter/xt_ifgroup.h	(revision 0)
@@ -0,0 +1,18 @@
+#ifndef _XT_IFGROUP_H
+#define _XT_IFGROUP_H
+
+#define XT_IFGROUP_INVERT_IN	0x01
+#define XT_IFGROUP_INVERT_OUT	0x02
+#define XT_IFGROUP_MATCH_IN	0x04
+#define XT_IFGROUP_MATCH_OUT	0x08		
+
+struct xt_ifgroup_info {
+	u_int32_t in_group;
+	u_int32_t in_mask;
+	u_int32_t out_group;
+	u_int32_t out_mask;
+	u_int8_t flags;
+};
+
+#endif /*_XT_IFGROUP_H*/
+
Index: extensions/libxt_ifgroup.c
===================================================================
--- extensions/libxt_ifgroup.c	(revision 0)
+++ extensions/libxt_ifgroup.c	(revision 0)
@@ -0,0 +1,202 @@
+/* 
+ * Shared library add-on to iptables to match 
+ * packets by the incoming interface group.
+ *
+ * (c) 2006, 2007 Balazs Scheidler <bazsi@balabit.hu>,
+ * Laszlo Attila Toth <panther@balabit.hu>
+ */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_ifgroup.h>
+
+static void
+ifgroup_help(void)
+{
+	printf(
+"ifgroup v%s options:\n"
+"  --ifgroup-in  [!] group[/mask]  incoming interface group and its mask\n"
+"  --ifgroup-out [!] group[/mask]  outgoing interface group and its mask\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+	{"ifgroup-in", 1, NULL, '1'},
+	{"ifgroup-out", 1, NULL, '2'},
+	{ }
+};
+
+#define PARAM_MATCH_IN	0x01
+#define PARAM_MATCH_OUT	0x02
+
+
+#define IFGROUP_DEFAULT_MASK 0xffffffffU
+
+static int
+ifgroup_parse(int c, char **argv, int invert, unsigned int *flags,
+	      const void *entry, struct xt_entry_match **match)
+{
+	struct xt_ifgroup_info *info =
+			 (struct xt_ifgroup_info *) (*match)->data;
+	char *end;
+
+	switch (c) {
+	case '1':
+		if (*flags & PARAM_MATCH_IN)
+			exit_error(PARAMETER_PROBLEM,
+			    "ifgroup match: Can't specify --ifgroup-in twice");
+
+		check_inverse(optarg, &invert, &optind, 0);
+
+		info->in_group = strtoul(optarg, &end, 0);
+		info->in_mask = IFGROUP_DEFAULT_MASK;
+
+		if (*end == '/')
+			info->in_mask = strtoul(end+1, &end, 0);
+
+		if (*end != '\0' || end == optarg)
+			exit_error(PARAMETER_PROBLEM,
+				  "ifgroup match: Bad ifgroup value `%s'", optarg);
+
+		if (invert)
+			info->flags |= XT_IFGROUP_INVERT_IN;
+
+		*flags |= PARAM_MATCH_IN;
+		info->flags |= XT_IFGROUP_MATCH_IN;
+		break;
+
+	case '2':
+		if (*flags & PARAM_MATCH_OUT)
+			exit_error(PARAMETER_PROBLEM,
+			    "ifgroup match: Can't specify --ifgroup-out twice");
+
+		check_inverse(optarg, &invert, &optind, 0);
+
+		info->out_group = strtoul(optarg, &end, 0);
+		info->out_mask = IFGROUP_DEFAULT_MASK;
+
+		if (*end == '/')
+			info->out_mask = strtoul(end+1, &end, 0);
+
+		if (*end != '\0' || end == optarg)
+			exit_error(PARAMETER_PROBLEM,
+			    "ifgroup match: Bad ifgroup value `%s'", optarg);
+
+		if (invert)
+			info->flags |= XT_IFGROUP_INVERT_OUT;
+
+		*flags |= PARAM_MATCH_OUT;
+		info->flags |= XT_IFGROUP_MATCH_OUT;
+		break;
+
+	default: 
+		return 0;
+	}
+
+	return 1;
+}
+
+static void
+ifgroup_final_check(unsigned int flags)
+{
+	if (!flags)
+		exit_error(PARAMETER_PROBLEM,
+		    "You must specify either "
+		    "`--ifgroup-in' or `--ifgroup-out'");
+}
+
+static void
+ifgroup_print_value_in(struct xt_ifgroup_info *info)
+{
+	printf("0x%x", info->in_group);
+	if (info->in_mask != IFGROUP_DEFAULT_MASK)
+		printf("/0x%x", info->in_mask);
+	printf(" ");
+}
+
+static void
+ifgroup_print_value_out(struct xt_ifgroup_info *info)
+{
+	printf("0x%x", info->out_group);
+	if (info->out_mask != IFGROUP_DEFAULT_MASK)
+		printf("/0x%x", info->out_mask);
+	printf(" ");
+}
+
+static void
+ifgroup_print(const void *ip,
+	      const struct xt_entry_match *match,
+	      int numeric)
+{
+	struct xt_ifgroup_info *info =
+		(struct xt_ifgroup_info *) match->data;
+
+	printf("ifgroup ");
+
+	if (info->flags & XT_IFGROUP_MATCH_IN) {
+		printf("in %s",
+		       info->flags & XT_IFGROUP_INVERT_IN ? "! " : "");
+		ifgroup_print_value_in(info);
+	}
+	if (info->flags & XT_IFGROUP_MATCH_OUT) {
+		printf("out %s",
+		       info->flags & XT_IFGROUP_INVERT_OUT ? "! " : "");
+		ifgroup_print_value_out(info);
+	}
+}
+
+static void
+ifgroup_save(const void *ip, const struct xt_entry_match *match)
+{
+	struct xt_ifgroup_info *info =
+		(struct xt_ifgroup_info *) match->data;
+
+	if (info->flags & XT_IFGROUP_MATCH_IN) {
+		printf("%s--ifgroup-in ",
+		       info->flags & XT_IFGROUP_INVERT_IN ? "! " : "");
+		ifgroup_print_value_in(info);
+	}
+	if (info->flags & XT_IFGROUP_MATCH_OUT) {
+		printf("%s--ifgroup-out ",
+		       info->flags & XT_IFGROUP_INVERT_OUT ? "! " : "");
+		ifgroup_print_value_out(info);
+	}
+}
+
+static struct xtables_match ifgroup_match = {
+	.family		= AF_INET,
+	.name		= "ifgroup",
+	.version	= IPTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_ifgroup_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_ifgroup_info)),
+	.help		= ifgroup_help,
+	.parse		= ifgroup_parse,
+	.final_check	= ifgroup_final_check,
+	.print		= ifgroup_print,
+	.save		= ifgroup_save,
+	.extra_opts	= opts
+};
+
+static struct xtables_match ifgroup_match6 = {
+	.family		= AF_INET6,
+	.name		= "ifgroup",
+	.version	= IPTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_ifgroup_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_ifgroup_info)),
+	.help		= ifgroup_help,
+	.parse		= ifgroup_parse,
+	.final_check	= ifgroup_final_check,
+	.print		= ifgroup_print,
+	.save		= ifgroup_save,
+	.extra_opts	= opts
+};
+
+void _init(void)
+{
+	xtables_register_match(&ifgroup_match);
+	xtables_register_match(&ifgroup_match6);
+}
+
Index: extensions/Makefile
===================================================================
--- extensions/Makefile	(revision 7083)
+++ extensions/Makefile	(working copy)
@@ -7,7 +7,7 @@
 #
 PF_EXT_SLIB:=ah addrtype conntrack ecn icmp iprange owner policy realm recent tos ttl unclean CLUSTERIP DNAT ECN LOG MASQUERADE MIRROR NETMAP REDIRECT REJECT SAME SNAT TOS TTL ULOG
 PF6_EXT_SLIB:=ah dst eui64 frag hbh hl icmp6 ipv6header mh owner policy rt HL LOG REJECT
-PFX_EXT_SLIB:=connbytes connmark connlimit comment dccp dscp esp hashlimit helper length limit mac mark multiport physdev pkttype quota sctp state statistic standard string tcp tcpmss time u32 udp CLASSIFY CONNMARK DSCP MARK NFLOG NFQUEUE NOTRACK TCPMSS TRACE
+PFX_EXT_SLIB:=connbytes connmark connlimit comment dccp dscp esp hashlimit helper ifgroup length limit mac mark multiport physdev pkttype quota sctp state statistic standard string tcp tcpmss time u32 udp CLASSIFY CONNMARK DSCP MARK NFLOG NFQUEUE NOTRACK TCPMSS TRACE
 
 PF_EXT_SELINUX_SLIB:=
 PF6_EXT_SELINUX_SLIB:=
Index: extensions/libip6t_ifgroup.man
===================================================================
--- extensions/libip6t_ifgroup.man	(revision 0)
+++ extensions/libip6t_ifgroup.man	(revision 0)
@@ -0,0 +1,36 @@
+Maches packets on an interface if it is in the same interface group
+as specified by the
+.B "--ifgroup-in"
+or
+.B "--ifgroup-in"
+parameter. If a mask is also specified, the masked value of
+the inteface's group must be equal to the given value of the
+.B "--ifgroup-in"
+or
+.B "--ifgroup-out"
+parameter to match. This match is available in all tables.
+.TP
+.BR "[!] --ifgroup-in \fIgroup[/mask]\fR"
+This specifies the interface group of input interface and the optional mask.
+Valid only in the in the
+.B PREROUTING
+and
+.B INPUT
+and
+.B FORWARD
+chains, and user-defined chains which are only called from those
+chains. 
+.TP
+.BR "[!] --ifgroup-out \fIgroup[/mask]\fR"
+This specifies the interface group of out interface and the optional mask.
+Valid only in the in the
+.B FORWARD
+and
+.B OUTPUT
+and
+.B POSTROUTING
+chains, and user-defined chains which are only called from those
+chains. 
+.RS
+.PP
+
Index: extensions/libipt_ifgroup.man
===================================================================
--- extensions/libipt_ifgroup.man	(revision 0)
+++ extensions/libipt_ifgroup.man	(revision 0)
@@ -0,0 +1,36 @@
+Maches packets on an interface if it is in the same interface group
+as specified by the
+.B "--ifgroup-in"
+or
+.B "--ifgroup-in"
+parameter. If a mask is also specified, the masked value of
+the inteface's group must be equal to the given value of the
+.B "--ifgroup-in"
+or
+.B "--ifgroup-out"
+parameter to match. This match is available in all tables.
+.TP
+.BR "[!] --ifgroup-in \fIgroup[/mask]\fR"
+This specifies the interface group of input interface and the optional mask.
+Valid only in the in the
+.B PREROUTING
+and
+.B INPUT
+and
+.B FORWARD
+chains, and user-defined chains which are only called from those
+chains. 
+.TP
+.BR "[!] --ifgroup-out \fIgroup[/mask]\fR"
+This specifies the interface group of out interface and the optional mask.
+Valid only in the in the
+.B FORWARD
+and
+.B OUTPUT
+and
+.B POSTROUTING
+chains, and user-defined chains which are only called from those
+chains. 
+.RS
+.PP
+

^ permalink raw reply

* [IFGROUPv5 iproute 2/2] Interface group as new ip link option
From: Laszlo Attila Toth @ 2007-10-26 12:30 UTC (permalink / raw)
  To: David Miller; +Cc: Patrick McHardy, netdev, Laszlo Attila Toth
In-Reply-To: <11934018351170-git-send-email-panther@balabit.hu>

Interfaces can be grouped and each group has an unique positive integer ID.
It can be set via ip link. Symbolic names can be specified in
/etc/iproute2/rt_ifgroup.

Signed-off-by: Laszlo Attila Toth <panther@balabit.hu>
---
 include/linux/if_link.h |    2 +
 include/rt_names.h      |    2 +
 ip/ipaddress.c          |    4 +++
 ip/iplink.c             |   11 ++++++++
 lib/rt_names.c          |   62 +++++++++++++++++++++++++++++++++++++++++++++++
 man/man8/ip.8           |    5 ++++
 6 files changed, 86 insertions(+), 0 deletions(-)

diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index c948395..5a2d071 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -79,6 +79,8 @@ enum
 	IFLA_LINKINFO,
 #define IFLA_LINKINFO IFLA_LINKINFO
 	IFLA_NET_NS_PID,
+	IFLA_IFGROUP,
+#define	IFLA_IFGROUP IFLA_IFGROUP
 	__IFLA_MAX
 };
 
diff --git a/include/rt_names.h b/include/rt_names.h
index 07a10e0..72c5247 100644
--- a/include/rt_names.h
+++ b/include/rt_names.h
@@ -8,11 +8,13 @@ char* rtnl_rtscope_n2a(int id, char *buf, int len);
 char* rtnl_rttable_n2a(__u32 id, char *buf, int len);
 char* rtnl_rtrealm_n2a(int id, char *buf, int len);
 char* rtnl_dsfield_n2a(int id, char *buf, int len);
+char* rtnl_ifgroup_n2a(int id, char *buf, int len);
 int rtnl_rtprot_a2n(__u32 *id, char *arg);
 int rtnl_rtscope_a2n(__u32 *id, char *arg);
 int rtnl_rttable_a2n(__u32 *id, char *arg);
 int rtnl_rtrealm_a2n(__u32 *id, char *arg);
 int rtnl_dsfield_a2n(__u32 *id, char *arg);
+int rtnl_ifgroup_a2n(__u32 *id, char *arg);
 
 const char *inet_proto_n2a(int proto, char *buf, int len);
 int inet_proto_a2n(char *buf);
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index d1c6620..1ecbe03 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -227,6 +227,10 @@ int print_linkinfo(const struct sockaddr_nl *who,
 		fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
 	if (tb[IFLA_QDISC])
 		fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
+	if (tb[IFLA_IFGROUP]) {
+		SPRINT_BUF(b1);
+		fprintf(fp, "group %s ", rtnl_ifgroup_n2a(*(int*)RTA_DATA(tb[IFLA_IFGROUP]), b1, sizeof(b1)));
+	}
 #ifdef IFLA_MASTER
 	if (tb[IFLA_MASTER]) {
 		SPRINT_BUF(b1);
diff --git a/ip/iplink.c b/ip/iplink.c
index 8e0ed2a..71bd240 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <sys/ioctl.h>
 #include <linux/sockios.h>
+#include <linux/rtnetlink.h>
 
 #include "rt_names.h"
 #include "utils.h"
@@ -46,6 +47,7 @@ void iplink_usage(void)
 	fprintf(stderr, "	                     promisc { on | off } |\n");
 	fprintf(stderr, "	                     trailers { on | off } |\n");
 	fprintf(stderr, "	                     txqueuelen PACKETS |\n");
+	fprintf(stderr, "	                     group GROUP |\n");
 	fprintf(stderr, "	                     name NEWNAME |\n");
 	fprintf(stderr, "	                     address LLADDR | broadcast LLADDR |\n");
 	fprintf(stderr, "	                     mtu MTU }\n");
@@ -145,6 +147,7 @@ static int iplink_have_newlink(void)
 static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
 {
 	int qlen = -1;
+	__u32 group = 0;
 	int mtu = -1;
 	int len;
 	char abuf[32];
@@ -197,6 +200,14 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
 			if (get_integer(&qlen,  *argv, 0))
 				invarg("Invalid \"txqueuelen\" value\n", *argv);
 			addattr_l(&req.n, sizeof(req), IFLA_TXQLEN, &qlen, 4);
+		} else if (matches(*argv, "group") == 0) {
+			NEXT_ARG();
+			if (group != 0)
+				duparg("group", *argv);
+
+			if (rtnl_ifgroup_a2n(&group, *argv))
+				invarg("\"group\" value is invalid\n", *argv);
+			addattr_l(&req.n, sizeof(req), IFLA_IFGROUP, &group, sizeof(group));
 		} else if (strcmp(*argv, "mtu") == 0) {
 			NEXT_ARG();
 			if (mtu != -1)
diff --git a/lib/rt_names.c b/lib/rt_names.c
index 8d019a0..a067e74 100644
--- a/lib/rt_names.c
+++ b/lib/rt_names.c
@@ -446,3 +446,65 @@ int rtnl_dsfield_a2n(__u32 *id, char *arg)
 	return 0;
 }
 
+static char * rtnl_rtifgroup_tab[256] = {
+	"0",
+};
+
+static int rtnl_rtifgroup_init;
+
+static void rtnl_rtifgroup_initialize(void)
+{
+	rtnl_rtifgroup_init = 1;
+	rtnl_tab_initialize("/etc/iproute2/rt_ifgroup",
+			    rtnl_rtifgroup_tab, 256);
+}
+
+char * rtnl_ifgroup_n2a(int id, char *buf, int len)
+{
+	if (id<0 || id>=256) {
+		snprintf(buf, len, "%d", id);
+		return buf;
+	}
+	if (!rtnl_rtifgroup_tab[id]) {
+		if (!rtnl_rtifgroup_init)
+			rtnl_rtifgroup_initialize();
+	}
+	if (rtnl_rtifgroup_tab[id])
+		return rtnl_rtifgroup_tab[id];
+	snprintf(buf, len, "0x%02x", id);
+	return buf;
+}
+
+
+int rtnl_ifgroup_a2n(__u32 *id, char *arg)
+{
+	static char *cache = NULL;
+	static unsigned long res;
+	char *end;
+	int i;
+
+	if (cache && strcmp(cache, arg) == 0) {
+		*id = res;
+		return 0;
+	}
+
+	if (!rtnl_rtifgroup_init)
+		rtnl_rtifgroup_initialize();
+
+	for (i=0; i<256; i++) {
+		if (rtnl_rtifgroup_tab[i] &&
+		    strcmp(rtnl_rtifgroup_tab[i], arg) == 0) {
+			cache = rtnl_rtifgroup_tab[i];
+			res = i;
+			*id = res;
+			return 0;
+		}
+	}
+
+	res = strtoul(arg, &end, 16);
+	if (!end || end == arg || *end || res > 255)
+		return -1;
+	*id = res;
+	return 0;
+}
+
diff --git a/man/man8/ip.8 b/man/man8/ip.8
index 8fd6d52..0338dab 100644
--- a/man/man8/ip.8
+++ b/man/man8/ip.8
@@ -511,6 +511,11 @@ already configured.
 change the transmit queue length of the device.
 
 .TP
+.BI group " GROUP"
+.TP 
+change the interface group identifier of the device.
+
+.TP
 .BI mtu " NUMBER"
 change the 
 .I MTU
-- 
1.5.2.5


^ permalink raw reply related

* [IFGROUPv5 2/3] Interface group: core (netlink) part
From: Laszlo Attila Toth @ 2007-10-26 12:30 UTC (permalink / raw)
  To: David Miller; +Cc: Patrick McHardy, netdev, Laszlo Attila Toth
In-Reply-To: <11934018354072-git-send-email-panther@balabit.hu>

Interface groups let handle different interfaces together
especially in netfilter modules.
Modified net device structure and netlink interface.

Signed-off-by: Laszlo Attila Toth <panther@balabit.hu>
---
 include/linux/if_link.h   |    2 ++
 include/linux/netdevice.h |    2 ++
 net/core/rtnetlink.c      |   11 +++++++++++
 3 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 84c3492..722b25c 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -79,6 +79,8 @@ enum
 	IFLA_LINKINFO,
 #define IFLA_LINKINFO IFLA_LINKINFO
 	IFLA_NET_NS_PID,
+	IFLA_IFGROUP,
+#define IFLA_IFGROUP IFLA_IFGROUP
 	__IFLA_MAX
 };
 
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 811024e..e685ae0 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -519,6 +519,8 @@ struct net_device
 	/* Interface index. Unique device identifier	*/
 	int			ifindex;
 	int			iflink;
+	/* interface group this interface belongs to */
+	u_int32_t		ifgroup;
 
 
 	struct net_device_stats* (*get_stats)(struct net_device *dev);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 0e278ac..4a9f738 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -609,6 +609,7 @@ static inline size_t if_nlmsg_size(const struct net_device *dev)
 	       + nla_total_size(4) /* IFLA_MTU */
 	       + nla_total_size(4) /* IFLA_LINK */
 	       + nla_total_size(4) /* IFLA_MASTER */
+	       + nla_total_size(4) /* IFLA_IFGROUP */
 	       + nla_total_size(1) /* IFLA_OPERSTATE */
 	       + nla_total_size(1) /* IFLA_LINKMODE */
 	       + rtnl_link_get_size(dev); /* IFLA_LINKINFO */
@@ -646,6 +647,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
 	if (dev->master)
 		NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex);
 
+	if (dev->ifgroup)
+		NLA_PUT_U32(skb, IFLA_IFGROUP, dev->ifgroup);
+
 	if (dev->qdisc_sleeping)
 		NLA_PUT_STRING(skb, IFLA_QDISC, dev->qdisc_sleeping->ops->id);
 
@@ -885,6 +889,13 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
 		}
 	}
 
+	if (tb[IFLA_IFGROUP]) {
+		if (dev->ifgroup != nla_get_u32(tb[IFLA_IFGROUP])) {
+			dev->ifgroup = nla_get_u32(tb[IFLA_IFGROUP]);
+			modified = 1;
+		}
+	}
+
 	err = 0;
 
 errout:
-- 
1.5.2.5


^ permalink raw reply related

* [IFGROUPv5 3/3] Netfilter Interface group match
From: Laszlo Attila Toth @ 2007-10-26 12:30 UTC (permalink / raw)
  To: David Miller; +Cc: Patrick McHardy, netdev, Laszlo Attila Toth
In-Reply-To: <11934018353320-git-send-email-panther@balabit.hu>

Interface group values can be checked on both input and output interfaces.

Signed-off-by: Laszlo Attila Toth <panther@balabit.hu>
---
 include/linux/netfilter/xt_ifgroup.h |   18 +++++
 net/netfilter/Kconfig                |   16 +++++
 net/netfilter/Makefile               |    1 +
 net/netfilter/xt_ifgroup.c           |  120 ++++++++++++++++++++++++++++++++++
 4 files changed, 155 insertions(+), 0 deletions(-)

diff --git a/include/linux/netfilter/xt_ifgroup.h b/include/linux/netfilter/xt_ifgroup.h
new file mode 100644
index 0000000..9ac75de
--- /dev/null
+++ b/include/linux/netfilter/xt_ifgroup.h
@@ -0,0 +1,18 @@
+#ifndef _XT_IFGROUP_H
+#define _XT_IFGROUP_H
+
+#define XT_IFGROUP_INVERT_IN	0x01
+#define XT_IFGROUP_INVERT_OUT	0x02
+#define XT_IFGROUP_MATCH_IN	0x04
+#define XT_IFGROUP_MATCH_OUT	0x08		
+
+struct xt_ifgroup_info {
+	u_int32_t in_group;
+	u_int32_t in_mask;
+	u_int32_t out_group;
+	u_int32_t out_mask;
+	u_int8_t flags;
+};
+
+#endif /*_XT_IFGROUP_H*/
+
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index d7a600a..0e0cd4f 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -597,6 +597,22 @@ config NETFILTER_XT_MATCH_QUOTA
 	  If you want to compile it as a module, say M here and read
 	  <file:Documentation/kbuild/modules.txt>.  If unsure, say `N'.
 
+config NETFILTER_XT_MATCH_IFGROUP
+	tristate '"ifgroup" interface group match support'
+	depends on NETFILTER_XTABLES
+	help
+	  Interface group matching allows you to match a packet by
+	  its incoming interface "group", settable using ip link set
+	  group
+
+	  Typical usage is to assign dynamic interfaces to a group
+	  when they come up using "ip link set group" and then match
+	  incoming packets with a rule like this:
+
+	    iptables -A INPUT -m ifgroup --if-group openvpn-rw1 -j LOG
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 config NETFILTER_XT_MATCH_REALM
 	tristate  '"realm" match support'
 	depends on NETFILTER_XTABLES
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 93c58f9..29718c1 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_IFGROUP) += xt_ifgroup.o
diff --git a/net/netfilter/xt_ifgroup.c b/net/netfilter/xt_ifgroup.c
new file mode 100644
index 0000000..e0e1fbf
--- /dev/null
+++ b/net/netfilter/xt_ifgroup.c
@@ -0,0 +1,120 @@
+/*
+ * An x_tables match module to match interface groups
+ *
+ * (C) 2006,2007 Balazs Scheidler <bazsi@balabit.hu>,
+ *   Laszlo Attila Toth <panther@balabit.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter/xt_ifgroup.h>
+#include <linux/netfilter/x_tables.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Laszlo Attila Toth <panther@balabit.hu>");
+MODULE_DESCRIPTION("Xtables interface group matching module");
+MODULE_ALIAS("ipt_ifgroup");
+MODULE_ALIAS("ip6t_ifgroup");
+
+
+static inline bool
+ifgroup_match_in(const struct net_device *in,
+		 const struct xt_ifgroup_info *info)
+{
+	return ((in->ifgroup & info->in_mask) == info->in_group) ^ 
+		((info->flags & XT_IFGROUP_INVERT_IN) == XT_IFGROUP_INVERT_IN);
+}
+
+static inline bool
+ifgroup_match_out(const struct net_device *out,
+		 const struct xt_ifgroup_info *info)
+{
+	return ((out->ifgroup & info->out_mask) == info->out_group) ^ 
+		((info->flags & XT_IFGROUP_INVERT_OUT) == XT_IFGROUP_INVERT_OUT);
+}
+
+static bool
+ifgroup_match(const struct sk_buff *skb,
+	     const struct net_device *in,
+	     const struct net_device *out,
+	     const struct xt_match *match,
+	     const void *matchinfo,
+	     int offset,
+	     unsigned int protoff,
+	     bool *hotdrop)
+{
+	const struct xt_ifgroup_info *info = matchinfo;
+	
+	if (info->flags & XT_IFGROUP_MATCH_IN && !ifgroup_match_in(in, info))
+		return false;
+	if (info->flags & XT_IFGROUP_MATCH_OUT && !ifgroup_match_out(out, info))
+		return false;
+	
+	return true;
+}
+
+static bool ifgroup_checkentry(const char *tablename, const void *ip_void,
+			       const struct xt_match *match,
+			       void *matchinfo, unsigned int hook_mask)
+{
+	struct xt_ifgroup_info *info = matchinfo;
+
+	if (!(info->flags & (XT_IFGROUP_MATCH_IN|XT_IFGROUP_MATCH_OUT))) {
+		printk(KERN_ERR "xt_ifgroup: neither incoming nor "
+				"outgoing device selected\n");
+		return false;
+	}
+	if (hook_mask & (1 << NF_IP_PRE_ROUTING | 1 << NF_IP_LOCAL_IN)
+	    && info->flags & XT_IFGROUP_MATCH_OUT) {
+		printk(KERN_ERR "xt_ifgroup: output device not valid in "
+				"PRE_ROUTING and INPUT\n");
+		return false;
+	}
+	if (hook_mask & (1 << NF_IP_POST_ROUTING | 1 << NF_IP_LOCAL_OUT)
+	    && info->flags & XT_IFGROUP_MATCH_IN) {
+		printk(KERN_ERR "xt_ifgroup: input device not valid in "
+				"POST_ROUTING and OUTPUT\n");
+		return false;
+	}
+	return true;
+}
+
+static struct xt_match xt_ifgroup_match[] __read_mostly  = {
+        {
+		.name		= "ifgroup",
+		.match		= ifgroup_match,
+		.checkentry	= ifgroup_checkentry,
+		.matchsize	= sizeof(struct xt_ifgroup_info),
+		.family		= AF_INET,
+		.me		= THIS_MODULE,
+	
+	},
+	{
+		.name		= "ifgroup",
+		.match		= ifgroup_match,
+		.checkentry	= ifgroup_checkentry,
+		.matchsize	= sizeof(struct xt_ifgroup_info),
+		.family		= AF_INET6,
+		.me		= THIS_MODULE,
+	},
+};
+
+static int __init xt_ifgroup_init(void)
+{
+	return xt_register_matches(xt_ifgroup_match,
+		                   ARRAY_SIZE(xt_ifgroup_match));
+}
+
+static void __exit xt_ifgroup_fini(void)
+{
+	xt_unregister_matches(xt_ifgroup_match,
+			      ARRAY_SIZE(xt_ifgroup_match));
+}
+
+module_init(xt_ifgroup_init);
+module_exit(xt_ifgroup_fini);
-- 
1.5.2.5


^ permalink raw reply related

* [PATCH] ehea: add kexec support
From: Jan-Bernd Themann @ 2007-10-26 12:37 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: netdev, Christoph Raisch, Jan-Bernd Themann, linux-kernel,
	linux-ppc, Marcus Eder, Thomas Klein, Stefan Roscher

eHEA resources that are allocated via H_CALLs have a unique identifier each.
These identifiers are necessary to free the resources. A reboot notifier
is used to free all eHEA resources before the indentifiers get lost, i.e
before kexec starts a new kernel.

Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>

---
 drivers/net/ehea/ehea.h      |    2 +-
 drivers/net/ehea/ehea_main.c |   21 +++++++++++++++++++++
 2 files changed, 22 insertions(+), 1 deletions(-)

diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h
index 4b4b74e..f78e5bf 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ehea/ehea.h
@@ -40,7 +40,7 @@
 #include <asm/io.h>
 
 #define DRV_NAME	"ehea"
-#define DRV_VERSION	"EHEA_0079"
+#define DRV_VERSION	"EHEA_0080"
 
 /* eHEA capability flags */
 #define DLPAR_PORT_ADD_REM 1
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index 0a7e789..f0319f1 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -33,6 +33,9 @@
 #include <linux/if.h>
 #include <linux/list.h>
 #include <linux/if_ether.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+
 #include <net/ip.h>
 
 #include "ehea.h"
@@ -3295,6 +3298,20 @@ static int __devexit ehea_remove(struct of_device *dev)
 	return 0;
 }
 
+static int ehea_reboot_notifier(struct notifier_block *nb,
+				unsigned long action, void *unused)
+{
+	if (action == SYS_RESTART) {
+		ehea_info("Reboot: freeing all eHEA resources");
+		ibmebus_unregister_driver(&ehea_driver);
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block ehea_reboot_nb = {
+        .notifier_call = ehea_reboot_notifier,
+};
+
 static int check_module_parm(void)
 {
 	int ret = 0;
@@ -3351,6 +3368,8 @@ int __init ehea_module_init(void)
 	if (ret)
 		goto out;
 
+	register_reboot_notifier(&ehea_reboot_nb);
+
 	ret = ibmebus_register_driver(&ehea_driver);
 	if (ret) {
 		ehea_error("failed registering eHEA device driver on ebus");
@@ -3362,6 +3381,7 @@ int __init ehea_module_init(void)
 	if (ret) {
 		ehea_error("failed to register capabilities attribute, ret=%d",
 			   ret);
+		unregister_reboot_notifier(&ehea_reboot_nb);
 		ibmebus_unregister_driver(&ehea_driver);
 		goto out;
 	}
@@ -3375,6 +3395,7 @@ static void __exit ehea_module_exit(void)
 	flush_scheduled_work();
 	driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities);
 	ibmebus_unregister_driver(&ehea_driver);
+	unregister_reboot_notifier(&ehea_reboot_nb);
 	ehea_destroy_busmap();
 }
 
-- 
1.5.2


^ permalink raw reply related

* Re: [PATCH] Remove pointless casts from void pointers,
From: Dmitry Torokhov @ 2007-10-26 13:38 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: LKML, akpm, rmk, kernel, tony.luck, jwboyer, benh, paulus, netdev,
	linux-scsi, linux-serial, linux-wireless, bryan.wu, adaplas
In-Reply-To: <9799624f63e093aa915947aea8fb1b8a5df959a1.1193390973.git.jeff@garzik.org>

On 10/26/07, Jeff Garzik <jeff@garzik.org> wrote:
>  drivers/input/touchscreen/h3600_ts_input.c |    4 ++--

Acked-by: Dmitry Torokhov <dtor@mail.ru>

-- 
Dmitry

^ permalink raw reply

* Re: [2.6.25 patch] the planned eepro100 removal
From: David Acker @ 2007-10-26 13:42 UTC (permalink / raw)
  To: Kok, Auke
  Cc: Jeff Garzik, Bill Davidsen, Adrian Bunk, netdev, linux-kernel,
	saw
In-Reply-To: <47211C08.2040601@intel.com>

Kok, Auke wrote:
> Jeff Garzik wrote:
>> Bill Davidsen wrote:
>>> Adrian Bunk wrote:
>>>> This patch contains the planned removal of the eepro100 driver.
>>>>
>>> Are the e100 people satisfied that e100 now handles all known cases? I 
>> Nope.  There are still e100 work outstanding that means we cannot kill
>> eepro100.
> 
> Agreed, there is still a receive unit hang in the last version that I got from
> David Acker.
> 
> Auke
Yes, I have not had much time to work on it.  :-(

In testing, I was also able to get crashes which I believe are from receive skb corruption.

It doesn't ever appear in our normal use testing, just when using pktgen against it while it uses pktgen out.  Sadly 
this has pushed it down the slippery priority slope.

I am sorry if I am holding things up.  I will try to put some more time in on this issue as soon as I can.

-Ack

^ permalink raw reply

* Re: [PATCH v4.2] FEC - fast ethernet controller for mpc52xx
From: Dale Farnsworth @ 2007-10-26 14:18 UTC (permalink / raw)
  To: Domen Puncer; +Cc: Jeff Garzik, netdev, linuxppc-dev
In-Reply-To: <20071026115909.GI3369@nd47.coderock.org>

On Fri, Oct 26, 2007 at 01:59:09PM +0200, Domen Puncer wrote:
> +static irqreturn_t mpc52xx_fec_tx_interrupt(int irq, void *dev_id)
> +{
> +	struct net_device *dev = dev_id;
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +
> +	spin_lock(&priv->lock);
> +
> +	while (bcom_buffer_done(priv->tx_dmatsk)) {
> +		struct sk_buff *skb;
> +		struct bcom_fec_bd *bd;
> +		skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL,
> +				(struct bcom_bd **)&bd);
> +		/* Here (and in rx routines) would be a good place for
> +		 * dma_unmap_single(), but bcom doesn't return bcom_bd of the
> +		 * finished transfer, and _unmap is empty on this platfrom.
> +		 */

Oops, you forgot to remove the above comment.  :)

Otherwise,
Acked-by: Dale Farnsworth <dale@farnsworth.org>

Domen, thanks for all your work on this.  It's good to see it finally go in.

-Dale

^ permalink raw reply

* Re: [PATCH] Remove pointless casts from void pointers,
From: Josh Boyer @ 2007-10-26 14:46 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: LKML, akpm, rmk, kernel, tony.luck, benh, paulus, dmitry.torokhov,
	netdev, linux-scsi, linux-serial, linux-wireless, bryan.wu,
	adaplas
In-Reply-To: <9799624f63e093aa915947aea8fb1b8a5df959a1.1193390973.git.jeff@garzik.org>

On Fri, 26 Oct 2007 05:40:22 -0400 (EDT)
Jeff Garzik <jeff@garzik.org> wrote:

> mostly in and around irq handlers.
> 
> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
> ---

>  drivers/serial/uartlite.c                  |    2 +-

Acked-by: Josh Boyer <jwboyer@linux.vnet.ibm.com

^ permalink raw reply

* Re: [PATCH] Remove pointless casts from void pointers,
From: Holger Schurig @ 2007-10-26 14:55 UTC (permalink / raw)
  To: linux-wireless-u79uwXL29TY76Z2rM5mHXA
  Cc: Jeff Garzik, LKML, akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
	rmk-lFZ/pmaqli7XmaaqVzeoHQ, kernel-OLH4Qvv75CYX/NnBR394Jw,
	tony.luck-ral2JQCrhuEAvxtiuMwx3w,
	jwboyer-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
	benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r,
	paulus-eUNUBHrolfbYtjvyW6yDsg,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	netdev-u79uwXL29TY76Z2rM5mHXA, linux-scsi-u79uwXL29TY76Z2rM5mHXA,
	linux-serial-u79uwXL29TY76Z2rM5mHXA,
	bryan.wu-OyLXuOCK7orQT0dZR+AlfA, adaplas-Re5JQEeQqe8AvxtiuMwx3w
In-Reply-To: <9799624f63e093aa915947aea8fb1b8a5df959a1.1193390973.git.jeff-o2qLIJkoznsdnm+yROfE0A@public.gmane.org>

>  drivers/net/wireless/libertas/if_cs.c      |    2 +-

ACK

^ permalink raw reply

* [0/2] [CRYPTO] users: Fix up remaining sg issues
From: Herbert Xu @ 2007-10-26 14:59 UTC (permalink / raw)
  To: David S. Miller, Linux Kernel Mailing List,
	Linux Crypto Mailing List, netdev

Hi Dave:

Here's a couple of patches to fix up the crypto users with
respect to the scatterlist change.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* [PATCH 1/2] [CRYPTO] tcrypt: Move sg_init_table out of timing loops
From: Herbert Xu @ 2007-10-26 15:00 UTC (permalink / raw)
  To: David S. Miller, Linux Kernel Mailing List,
	Linux Crypto Mailing List, netdev
In-Reply-To: <20071026145905.GA13850@gondor.apana.org.au>

[CRYPTO] tcrypt: Move sg_init_table out of timing loops

This patch moves the sg_init_table out of the timing loops for hash
algorithms so that it doesn't impact on the speed test results.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---

 crypto/tcrypt.c |   20 ++++++++++++++------
 1 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index c457bdb..24141fb 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -572,9 +572,11 @@ static int test_hash_jiffies_digest(struct hash_desc *desc, char *p, int blen,
 	int bcount;
 	int ret;
 
+	sg_init_table(sg, 1);
+
 	for (start = jiffies, end = start + sec * HZ, bcount = 0;
 	     time_before(jiffies, end); bcount++) {
-		sg_init_one(sg, p, blen);
+		sg_set_buf(sg, p, blen);
 		ret = crypto_hash_digest(desc, sg, blen, out);
 		if (ret)
 			return ret;
@@ -597,13 +599,15 @@ static int test_hash_jiffies(struct hash_desc *desc, char *p, int blen,
 	if (plen == blen)
 		return test_hash_jiffies_digest(desc, p, blen, out, sec);
 
+	sg_init_table(sg, 1);
+
 	for (start = jiffies, end = start + sec * HZ, bcount = 0;
 	     time_before(jiffies, end); bcount++) {
 		ret = crypto_hash_init(desc);
 		if (ret)
 			return ret;
 		for (pcount = 0; pcount < blen; pcount += plen) {
-			sg_init_one(sg, p + pcount, plen);
+			sg_set_buf(sg, p + pcount, plen);
 			ret = crypto_hash_update(desc, sg, plen);
 			if (ret)
 				return ret;
@@ -628,12 +632,14 @@ static int test_hash_cycles_digest(struct hash_desc *desc, char *p, int blen,
 	int i;
 	int ret;
 
+	sg_init_table(sg, 1);
+
 	local_bh_disable();
 	local_irq_disable();
 
 	/* Warm-up run. */
 	for (i = 0; i < 4; i++) {
-		sg_init_one(sg, p, blen);
+		sg_set_buf(sg, p, blen);
 		ret = crypto_hash_digest(desc, sg, blen, out);
 		if (ret)
 			goto out;
@@ -645,7 +651,7 @@ static int test_hash_cycles_digest(struct hash_desc *desc, char *p, int blen,
 
 		start = get_cycles();
 
-		sg_init_one(sg, p, blen);
+		sg_set_buf(sg, p, blen);
 		ret = crypto_hash_digest(desc, sg, blen, out);
 		if (ret)
 			goto out;
@@ -679,6 +685,8 @@ static int test_hash_cycles(struct hash_desc *desc, char *p, int blen,
 	if (plen == blen)
 		return test_hash_cycles_digest(desc, p, blen, out);
 
+	sg_init_table(sg, 1);
+
 	local_bh_disable();
 	local_irq_disable();
 
@@ -688,7 +696,7 @@ static int test_hash_cycles(struct hash_desc *desc, char *p, int blen,
 		if (ret)
 			goto out;
 		for (pcount = 0; pcount < blen; pcount += plen) {
-			sg_init_one(sg, p + pcount, plen);
+			sg_set_buf(sg, p + pcount, plen);
 			ret = crypto_hash_update(desc, sg, plen);
 			if (ret)
 				goto out;
@@ -708,7 +716,7 @@ static int test_hash_cycles(struct hash_desc *desc, char *p, int blen,
 		if (ret)
 			goto out;
 		for (pcount = 0; pcount < blen; pcount += plen) {
-			sg_init_one(sg, p + pcount, plen);
+			sg_set_buf(sg, p + pcount, plen);
 			ret = crypto_hash_update(desc, sg, plen);
 			if (ret)
 				goto out;

^ permalink raw reply related

* [PATCH 2/2] [CRYPTO] users: Fix up scatterlist conversion errors
From: Herbert Xu @ 2007-10-26 15:00 UTC (permalink / raw)
  To: David S. Miller, Linux Kernel Mailing List,
	Linux Crypto Mailing List, netdev
In-Reply-To: <20071026145905.GA13850@gondor.apana.org.au>

[CRYPTO] users: Fix up scatterlist conversion errors

This patch fixes the errors made in the users of the crypto layer during
the sg_init_table conversion.  It also adds a few conversions that were
missing altogether.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---

 drivers/crypto/padlock-sha.c          |    4 +-
 drivers/md/dm-crypt.c                 |    2 -
 drivers/net/ppp_mppe.c                |   10 ++++-
 drivers/scsi/iscsi_tcp.c              |    5 +-
 fs/ecryptfs/crypto.c                  |    2 +
 net/ipv4/esp4.c                       |    7 ++-
 net/ipv6/esp6.c                       |    8 +++-
 net/rxrpc/rxkad.c                     |   66 +++++++++++++++++-----------------
 net/sctp/auth.c                       |    3 -
 net/sctp/sm_make_chunk.c              |    6 +--
 net/sunrpc/auth_gss/gss_krb5_crypto.c |   34 +++++++++++++----
 net/sunrpc/auth_gss/gss_spkm3_seal.c  |    2 -
 net/sunrpc/xdr.c                      |    2 +
 13 files changed, 93 insertions(+), 58 deletions(-)

diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c
index 4e8de16..c666b4e 100644
--- a/drivers/crypto/padlock-sha.c
+++ b/drivers/crypto/padlock-sha.c
@@ -55,7 +55,7 @@ static void padlock_sha_bypass(struct crypto_tfm *tfm)
 	if (ctx(tfm)->data && ctx(tfm)->used) {
 		struct scatterlist sg;
 
-		sg_set_buf(&sg, ctx(tfm)->data, ctx(tfm)->used);
+		sg_init_one(&sg, ctx(tfm)->data, ctx(tfm)->used);
 		crypto_hash_update(&ctx(tfm)->fallback, &sg, sg.length);
 	}
 
@@ -79,7 +79,7 @@ static void padlock_sha_update(struct crypto_tfm *tfm,
 
 	if (unlikely(ctx(tfm)->bypass)) {
 		struct scatterlist sg;
-		sg_set_buf(&sg, (uint8_t *)data, length);
+		sg_init_one(&sg, (uint8_t *)data, length);
 		crypto_hash_update(&ctx(tfm)->fallback, &sg, length);
 		return;
 	}
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 1c159ac..28c6ae0 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -168,7 +168,7 @@ static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
 		return -ENOMEM;
 	}
 
-	sg_set_buf(&sg, cc->key, cc->key_size);
+	sg_init_one(&sg, cc->key, cc->key_size);
 	desc.tfm = hash_tfm;
 	desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
 	err = crypto_hash_digest(&desc, &sg, cc->key_size, salt);
diff --git a/drivers/net/ppp_mppe.c b/drivers/net/ppp_mppe.c
index bcb0885..b35d794 100644
--- a/drivers/net/ppp_mppe.c
+++ b/drivers/net/ppp_mppe.c
@@ -68,7 +68,7 @@ MODULE_VERSION("1.0.2");
 static unsigned int
 setup_sg(struct scatterlist *sg, const void *address, unsigned int length)
 {
-	sg_init_one(sg, address, length);
+	sg_set_buf(sg, address, length);
 	return length;
 }
 
@@ -140,6 +140,8 @@ static void get_new_key_from_sha(struct ppp_mppe_state * state)
 	struct scatterlist sg[4];
 	unsigned int nbytes;
 
+	sg_init_table(sg, 4);
+
 	nbytes = setup_sg(&sg[0], state->master_key, state->keylen);
 	nbytes += setup_sg(&sg[1], sha_pad->sha_pad1,
 			   sizeof(sha_pad->sha_pad1));
@@ -166,6 +168,8 @@ static void mppe_rekey(struct ppp_mppe_state * state, int initial_key)
 	if (!initial_key) {
 		crypto_blkcipher_setkey(state->arc4, state->sha1_digest,
 					state->keylen);
+		sg_init_table(sg_in, 1);
+		sg_init_table(sg_out, 1);
 		setup_sg(sg_in, state->sha1_digest, state->keylen);
 		setup_sg(sg_out, state->session_key, state->keylen);
 		if (crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
@@ -421,6 +425,8 @@ mppe_compress(void *arg, unsigned char *ibuf, unsigned char *obuf,
 	isize -= 2;
 
 	/* Encrypt packet */
+	sg_init_table(sg_in, 1);
+	sg_init_table(sg_out, 1);
 	setup_sg(sg_in, ibuf, isize);
 	setup_sg(sg_out, obuf, osize);
 	if (crypto_blkcipher_encrypt(&desc, sg_out, sg_in, isize) != 0) {
@@ -608,6 +614,8 @@ mppe_decompress(void *arg, unsigned char *ibuf, int isize, unsigned char *obuf,
 	 * Decrypt the first byte in order to check if it is
 	 * a compressed or uncompressed protocol field.
 	 */
+	sg_init_table(sg_in, 1);
+	sg_init_table(sg_out, 1);
 	setup_sg(sg_in, ibuf, 1);
 	setup_sg(sg_out, obuf, 1);
 	if (crypto_blkcipher_decrypt(&desc, sg_out, sg_in, 1) != 0) {
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 097a136..4bcf916 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -674,9 +674,8 @@ partial_sg_digest_update(struct hash_desc *desc, struct scatterlist *sg,
 {
 	struct scatterlist temp;
 
-	memcpy(&temp, sg, sizeof(struct scatterlist));
-	temp.offset = offset;
-	temp.length = length;
+	sg_init_table(&temp, 1);
+	sg_set_page(&temp, sg_page(sg), length, offset);
 	crypto_hash_update(desc, &temp, length);
 }
 
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index 7a472b1..9d70289 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -279,6 +279,8 @@ int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
 	int offset;
 	int remainder_of_page;
 
+	sg_init_table(sg, sg_size);
+
 	while (size > 0 && i < sg_size) {
 		pg = virt_to_page(addr);
 		offset = offset_in_page(addr);
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index ba98401..194b1e7 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -111,7 +111,9 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
 				goto unlock;
 		}
 		sg_init_table(sg, nfrags);
-		skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, clen);
+		sg_mark_end(sg, skb_to_sgvec(skb, sg, esph->enc_data +
+						      esp->conf.ivlen -
+						      skb->data, clen));
 		err = crypto_blkcipher_encrypt(&desc, sg, sg, clen);
 		if (unlikely(sg != &esp->sgbuf[0]))
 			kfree(sg);
@@ -203,7 +205,8 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
 			goto out;
 	}
 	sg_init_table(sg, nfrags);
-	skb_to_sgvec(skb, sg, sizeof(*esph) + esp->conf.ivlen, elen);
+	sg_mark_end(sg, skb_to_sgvec(skb, sg, sizeof(*esph) + esp->conf.ivlen,
+				     elen));
 	err = crypto_blkcipher_decrypt(&desc, sg, sg, elen);
 	if (unlikely(sg != &esp->sgbuf[0]))
 		kfree(sg);
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index f67d51a..f992405 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -110,7 +110,9 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
 				goto unlock;
 		}
 		sg_init_table(sg, nfrags);
-		skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, clen);
+		sg_mark_end(sg, skb_to_sgvec(skb, sg, esph->enc_data +
+						      esp->conf.ivlen -
+						      skb->data, clen));
 		err = crypto_blkcipher_encrypt(&desc, sg, sg, clen);
 		if (unlikely(sg != &esp->sgbuf[0]))
 			kfree(sg);
@@ -207,7 +209,9 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
 			}
 		}
 		sg_init_table(sg, nfrags);
-		skb_to_sgvec(skb, sg, sizeof(*esph) + esp->conf.ivlen, elen);
+		sg_mark_end(sg, skb_to_sgvec(skb, sg,
+					     sizeof(*esph) + esp->conf.ivlen,
+					     elen));
 		ret = crypto_blkcipher_decrypt(&desc, sg, sg, elen);
 		if (unlikely(sg != &esp->sgbuf[0]))
 			kfree(sg);
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index ac3cabd..eebefb6 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -135,9 +135,8 @@ static void rxkad_prime_packet_security(struct rxrpc_connection *conn)
 	tmpbuf.x[2] = 0;
 	tmpbuf.x[3] = htonl(conn->security_ix);
 
-	memset(sg, 0, sizeof(sg));
-	sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf));
-	sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf));
+	sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf));
+	sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf));
 	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf));
 
 	memcpy(&conn->csum_iv, &tmpbuf.x[2], sizeof(conn->csum_iv));
@@ -180,9 +179,8 @@ static int rxkad_secure_packet_auth(const struct rxrpc_call *call,
 	desc.info = iv.x;
 	desc.flags = 0;
 
-	memset(sg, 0, sizeof(sg));
-	sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf));
-	sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf));
+	sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf));
+	sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf));
 	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf));
 
 	memcpy(sechdr, &tmpbuf, sizeof(tmpbuf));
@@ -227,9 +225,8 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call,
 	desc.info = iv.x;
 	desc.flags = 0;
 
-	memset(sg, 0, sizeof(sg[0]) * 2);
-	sg_set_buf(&sg[0], sechdr, sizeof(rxkhdr));
-	sg_set_buf(&sg[1], &rxkhdr, sizeof(rxkhdr));
+	sg_init_one(&sg[0], sechdr, sizeof(rxkhdr));
+	sg_init_one(&sg[1], &rxkhdr, sizeof(rxkhdr));
 	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(rxkhdr));
 
 	/* we want to encrypt the skbuff in-place */
@@ -240,7 +237,7 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call,
 	len = data_size + call->conn->size_align - 1;
 	len &= ~(call->conn->size_align - 1);
 
-	skb_to_sgvec(skb, sg, 0, len);
+	sg_init_table(sg, skb_to_sgvec(skb, sg, 0, len));
 	crypto_blkcipher_encrypt_iv(&desc, sg, sg, len);
 
 	_leave(" = 0");
@@ -290,9 +287,8 @@ static int rxkad_secure_packet(const struct rxrpc_call *call,
 	tmpbuf.x[0] = sp->hdr.callNumber;
 	tmpbuf.x[1] = x;
 
-	memset(&sg, 0, sizeof(sg));
-	sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf));
-	sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf));
+	sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf));
+	sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf));
 	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf));
 
 	x = ntohl(tmpbuf.x[1]);
@@ -332,20 +328,23 @@ static int rxkad_verify_packet_auth(const struct rxrpc_call *call,
 	struct rxrpc_skb_priv *sp;
 	struct blkcipher_desc desc;
 	struct rxrpc_crypt iv;
-	struct scatterlist sg[2];
+	struct scatterlist sg[16];
 	struct sk_buff *trailer;
 	u32 data_size, buf;
 	u16 check;
+	int nsg;
 
 	_enter("");
 
 	sp = rxrpc_skb(skb);
 
 	/* we want to decrypt the skbuff in-place */
-	if (skb_cow_data(skb, 0, &trailer) < 0)
+	nsg = skb_cow_data(skb, 0, &trailer);
+	if (nsg < 0 || nsg > 16)
 		goto nomem;
 
-	skb_to_sgvec(skb, sg, 0, 8);
+	sg_init_table(sg, nsg);
+	sg_mark_end(sg, skb_to_sgvec(skb, sg, 0, 8));
 
 	/* start the decryption afresh */
 	memset(&iv, 0, sizeof(iv));
@@ -426,7 +425,8 @@ static int rxkad_verify_packet_encrypt(const struct rxrpc_call *call,
 			goto nomem;
 	}
 
-	skb_to_sgvec(skb, sg, 0, skb->len);
+	sg_init_table(sg, nsg);
+	sg_mark_end(sg, skb_to_sgvec(skb, sg, 0, skb->len));
 
 	/* decrypt from the session key */
 	payload = call->conn->key->payload.data;
@@ -521,9 +521,8 @@ static int rxkad_verify_packet(const struct rxrpc_call *call,
 	tmpbuf.x[0] = call->call_id;
 	tmpbuf.x[1] = x;
 
-	memset(&sg, 0, sizeof(sg));
-	sg_set_buf(&sg[0], &tmpbuf, sizeof(tmpbuf));
-	sg_set_buf(&sg[1], &tmpbuf, sizeof(tmpbuf));
+	sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf));
+	sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf));
 	crypto_blkcipher_encrypt_iv(&desc, &sg[0], &sg[1], sizeof(tmpbuf));
 
 	x = ntohl(tmpbuf.x[1]);
@@ -690,16 +689,20 @@ static void rxkad_calc_response_checksum(struct rxkad_response *response)
 static void rxkad_sg_set_buf2(struct scatterlist sg[2],
 			      void *buf, size_t buflen)
 {
+	int nsg = 1;
 
-	memset(sg, 0, sizeof(sg));
+	sg_init_table(sg, 2);
 
 	sg_set_buf(&sg[0], buf, buflen);
 	if (sg[0].offset + buflen > PAGE_SIZE) {
 		/* the buffer was split over two pages */
 		sg[0].length = PAGE_SIZE - sg[0].offset;
 		sg_set_buf(&sg[1], buf + sg[0].length, buflen - sg[0].length);
+		nsg++;
 	}
 
+	sg_mark_end(sg, nsg);
+
 	ASSERTCMP(sg[0].length + sg[1].length, ==, buflen);
 }
 
@@ -712,7 +715,7 @@ static void rxkad_encrypt_response(struct rxrpc_connection *conn,
 {
 	struct blkcipher_desc desc;
 	struct rxrpc_crypt iv;
-	struct scatterlist ssg[2], dsg[2];
+	struct scatterlist sg[2];
 
 	/* continue encrypting from where we left off */
 	memcpy(&iv, s2->session_key, sizeof(iv));
@@ -720,9 +723,8 @@ static void rxkad_encrypt_response(struct rxrpc_connection *conn,
 	desc.info = iv.x;
 	desc.flags = 0;
 
-	rxkad_sg_set_buf2(ssg, &resp->encrypted, sizeof(resp->encrypted));
-	memcpy(dsg, ssg, sizeof(dsg));
-	crypto_blkcipher_encrypt_iv(&desc, dsg, ssg, sizeof(resp->encrypted));
+	rxkad_sg_set_buf2(sg, &resp->encrypted, sizeof(resp->encrypted));
+	crypto_blkcipher_encrypt_iv(&desc, sg, sg, sizeof(resp->encrypted));
 }
 
 /*
@@ -817,7 +819,7 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
 {
 	struct blkcipher_desc desc;
 	struct rxrpc_crypt iv, key;
-	struct scatterlist ssg[1], dsg[1];
+	struct scatterlist sg[1];
 	struct in_addr addr;
 	unsigned life;
 	time_t issue, now;
@@ -850,9 +852,8 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
 	desc.info = iv.x;
 	desc.flags = 0;
 
-	sg_init_one(&ssg[0], ticket, ticket_len);
-	memcpy(dsg, ssg, sizeof(dsg));
-	crypto_blkcipher_decrypt_iv(&desc, dsg, ssg, ticket_len);
+	sg_init_one(&sg[0], ticket, ticket_len);
+	crypto_blkcipher_decrypt_iv(&desc, sg, sg, ticket_len);
 
 	p = ticket;
 	end = p + ticket_len;
@@ -961,7 +962,7 @@ static void rxkad_decrypt_response(struct rxrpc_connection *conn,
 				   const struct rxrpc_crypt *session_key)
 {
 	struct blkcipher_desc desc;
-	struct scatterlist ssg[2], dsg[2];
+	struct scatterlist sg[2];
 	struct rxrpc_crypt iv;
 
 	_enter(",,%08x%08x",
@@ -979,9 +980,8 @@ static void rxkad_decrypt_response(struct rxrpc_connection *conn,
 	desc.info = iv.x;
 	desc.flags = 0;
 
-	rxkad_sg_set_buf2(ssg, &resp->encrypted, sizeof(resp->encrypted));
-	memcpy(dsg, ssg, sizeof(dsg));
-	crypto_blkcipher_decrypt_iv(&desc, dsg, ssg, sizeof(resp->encrypted));
+	rxkad_sg_set_buf2(sg, &resp->encrypted, sizeof(resp->encrypted));
+	crypto_blkcipher_decrypt_iv(&desc, sg, sg, sizeof(resp->encrypted));
 	mutex_unlock(&rxkad_ci_mutex);
 
 	_leave("");
diff --git a/net/sctp/auth.c b/net/sctp/auth.c
index 621113a..7cf9666 100644
--- a/net/sctp/auth.c
+++ b/net/sctp/auth.c
@@ -726,8 +726,7 @@ void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
 
 	/* set up scatter list */
 	end = skb_tail_pointer(skb);
-	sg_init_table(&sg, 1);
-	sg_set_buf(&sg, auth, end - (unsigned char *)auth);
+	sg_init_one(&sg, auth, end - (unsigned char *)auth);
 
 	desc.tfm = asoc->ep->auth_hmacs[hmac_id];
 	desc.flags = 0;
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index c055212..c377e4e 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1513,8 +1513,7 @@ static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
 		struct hash_desc desc;
 
 		/* Sign the message.  */
-		sg_init_table(&sg, 1);
-		sg_set_buf(&sg, &cookie->c, bodysize);
+		sg_init_one(&sg, &cookie->c, bodysize);
 		keylen = SCTP_SECRET_SIZE;
 		key = (char *)ep->secret_key[ep->current_key];
 		desc.tfm = sctp_sk(ep->base.sk)->hmac;
@@ -1584,8 +1583,7 @@ struct sctp_association *sctp_unpack_cookie(
 
 	/* Check the signature.  */
 	keylen = SCTP_SECRET_SIZE;
-	sg_init_table(&sg, 1);
-	sg_set_buf(&sg, bear_cookie, bodysize);
+	sg_init_one(&sg, bear_cookie, bodysize);
 	key = (char *)ep->secret_key[ep->current_key];
 	desc.tfm = sctp_sk(ep->base.sk)->hmac;
 	desc.flags = 0;
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c
index 24711be..91cd8f0 100644
--- a/net/sunrpc/auth_gss/gss_krb5_crypto.c
+++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c
@@ -75,7 +75,7 @@ krb5_encrypt(
 		memcpy(local_iv, iv, crypto_blkcipher_ivsize(tfm));
 
 	memcpy(out, in, length);
-	sg_set_buf(sg, out, length);
+	sg_init_one(sg, out, length);
 
 	ret = crypto_blkcipher_encrypt_iv(&desc, sg, sg, length);
 out:
@@ -110,7 +110,7 @@ krb5_decrypt(
 		memcpy(local_iv,iv, crypto_blkcipher_ivsize(tfm));
 
 	memcpy(out, in, length);
-	sg_set_buf(sg, out, length);
+	sg_init_one(sg, out, length);
 
 	ret = crypto_blkcipher_decrypt_iv(&desc, sg, sg, length);
 out:
@@ -146,7 +146,7 @@ make_checksum(char *cksumname, char *header, int hdrlen, struct xdr_buf *body,
 	err = crypto_hash_init(&desc);
 	if (err)
 		goto out;
-	sg_set_buf(sg, header, hdrlen);
+	sg_init_one(sg, header, hdrlen);
 	err = crypto_hash_update(&desc, sg, hdrlen);
 	if (err)
 		goto out;
@@ -188,8 +188,6 @@ encryptor(struct scatterlist *sg, void *data)
 	/* Worst case is 4 fragments: head, end of page 1, start
 	 * of page 2, tail.  Anything more is a bug. */
 	BUG_ON(desc->fragno > 3);
-	desc->infrags[desc->fragno] = *sg;
-	desc->outfrags[desc->fragno] = *sg;
 
 	page_pos = desc->pos - outbuf->head[0].iov_len;
 	if (page_pos >= 0 && page_pos < outbuf->page_len) {
@@ -199,7 +197,10 @@ encryptor(struct scatterlist *sg, void *data)
 	} else {
 		in_page = sg_page(sg);
 	}
-	sg_assign_page(&desc->infrags[desc->fragno], in_page);
+	sg_set_page(&desc->infrags[desc->fragno], in_page, sg->length,
+		    sg->offset);
+	sg_set_page(&desc->outfrags[desc->fragno], sg_page(sg), sg->length,
+		    sg->offset);
 	desc->fragno++;
 	desc->fraglen += sg->length;
 	desc->pos += sg->length;
@@ -210,10 +211,17 @@ encryptor(struct scatterlist *sg, void *data)
 	if (thislen == 0)
 		return 0;
 
+	sg_mark_end(desc->infrags, desc->fragno);
+	sg_mark_end(desc->outfrags, desc->fragno);
+
 	ret = crypto_blkcipher_encrypt_iv(&desc->desc, desc->outfrags,
 					  desc->infrags, thislen);
 	if (ret)
 		return ret;
+
+	sg_init_table(desc->infrags, 4);
+	sg_init_table(desc->outfrags, 4);
+
 	if (fraglen) {
 		sg_set_page(&desc->outfrags[0], sg_page(sg), fraglen,
 				sg->offset + sg->length - fraglen);
@@ -247,6 +255,9 @@ gss_encrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
 	desc.fragno = 0;
 	desc.fraglen = 0;
 
+	sg_init_table(desc.infrags, 4);
+	sg_init_table(desc.outfrags, 4);
+
 	ret = xdr_process_buf(buf, offset, buf->len - offset, encryptor, &desc);
 	return ret;
 }
@@ -271,7 +282,8 @@ decryptor(struct scatterlist *sg, void *data)
 	/* Worst case is 4 fragments: head, end of page 1, start
 	 * of page 2, tail.  Anything more is a bug. */
 	BUG_ON(desc->fragno > 3);
-	desc->frags[desc->fragno] = *sg;
+	sg_set_page(&desc->frags[desc->fragno], sg_page(sg), sg->length,
+		    sg->offset);
 	desc->fragno++;
 	desc->fraglen += sg->length;
 
@@ -281,10 +293,15 @@ decryptor(struct scatterlist *sg, void *data)
 	if (thislen == 0)
 		return 0;
 
+	sg_mark_end(desc->frags, desc->fragno);
+
 	ret = crypto_blkcipher_decrypt_iv(&desc->desc, desc->frags,
 					  desc->frags, thislen);
 	if (ret)
 		return ret;
+
+	sg_init_table(desc->frags, 4);
+
 	if (fraglen) {
 		sg_set_page(&desc->frags[0], sg_page(sg), fraglen,
 				sg->offset + sg->length - fraglen);
@@ -312,6 +329,9 @@ gss_decrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
 	desc.desc.flags = 0;
 	desc.fragno = 0;
 	desc.fraglen = 0;
+
+	sg_init_table(desc.frags, 4);
+
 	return xdr_process_buf(buf, offset, buf->len - offset, decryptor, &desc);
 }
 
diff --git a/net/sunrpc/auth_gss/gss_spkm3_seal.c b/net/sunrpc/auth_gss/gss_spkm3_seal.c
index d158635..abf17ce 100644
--- a/net/sunrpc/auth_gss/gss_spkm3_seal.c
+++ b/net/sunrpc/auth_gss/gss_spkm3_seal.c
@@ -173,7 +173,7 @@ make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
 	if (err)
 		goto out;
 
-	sg_set_buf(sg, header, hdrlen);
+	sg_init_one(sg, header, hdrlen);
 	crypto_hash_update(&desc, sg, sg->length);
 
 	xdr_process_buf(body, body_offset, body->len - body_offset,
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index f38dac3..fdc5e6d 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -1030,6 +1030,8 @@ xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len,
 	unsigned page_len, thislen, page_offset;
 	struct scatterlist      sg[1];
 
+	sg_init_table(sg, 1);
+
 	if (offset >= buf->head[0].iov_len) {
 		offset -= buf->head[0].iov_len;
 	} else {

^ permalink raw reply related

* [PATCH] net: Saner thash_entries default with much memory
From: Jean Delvare @ 2007-10-26 15:21 UTC (permalink / raw)
  To: netdev; +Cc: Andi Kleen

On systems with a very large amount of memory, the heuristics in
alloc_large_system_hash() result in a very large TCP established hash
table: 16 millions of entries for a 128 GB ia64 system. This makes
reading from /proc/net/tcp pretty slow (well over a second) and as a
result netstat is slow on these machines. I know that /proc/net/tcp is
deprecated in favor of tcp_diag, however at the moment netstat only
knows of the former.

I am skeptical that such a large TCP established hash is often needed.
Just because a system has a lot of memory doesn't imply that it will
have several millions of concurrent TCP connections. Thus I believe
that we should put an arbitrary high limit to the size of the TCP
established hash by default. Users who really need a bigger hash can
always use the thash_entries boot parameter to get more.

I propose 2 millions of entries as the arbitrary high limit. This
makes /proc/net/tcp reasonably fast on the system in question (0.2 s)
while being still large enough for me to be confident that network
performance won't suffer.

This is just one way to limit the hash size, there are others; I am not
familiar enough with the TCP code to decide which is best. Thus, I
would welcome the proposals of alternatives.

Signed-off-by: Jean Delvare <jdelvare@suse.de>
---
 net/ipv4/tcp.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

--- linux-2.6.24-rc1.orig/net/ipv4/tcp.c	2007-10-24 09:59:58.000000000 +0200
+++ linux-2.6.24-rc1/net/ipv4/tcp.c	2007-10-26 16:26:41.000000000 +0200
@@ -2453,7 +2453,7 @@ void __init tcp_init(void)
 					0,
 					&tcp_hashinfo.ehash_size,
 					NULL,
-					0);
+					thash_entries ? 0 : 2 * 1024 * 1024);
 	tcp_hashinfo.ehash_size = 1 << tcp_hashinfo.ehash_size;
 	for (i = 0; i < tcp_hashinfo.ehash_size; i++) {
 		rwlock_init(&tcp_hashinfo.ehash[i].lock);

-- 
Jean Delvare
Suse L3

^ permalink raw reply

* Re: [PATCH] net: Saner thash_entries default with much memory
From: Andi Kleen @ 2007-10-26 15:34 UTC (permalink / raw)
  To: Jean Delvare; +Cc: netdev, Andi Kleen
In-Reply-To: <200710261721.32019.jdelvare@suse.de>

On Fri, Oct 26, 2007 at 05:21:31PM +0200, Jean Delvare wrote:
> I know that /proc/net/tcp is
> deprecated in favor of tcp_diag, however at the moment netstat only
> knows of the former.

Even tcp_diag will be slow when all slots are dumped. It's a fundamental
problem of the data structure. /proc has slightly higher constant
factor overhead, that's all.

Also there are some tricks to make it a little faster (e.g.
the old patches to not take the lock for empty buckets) but again
it's only just patching constant factors, not the fundamental
scaling issue.

> I propose 2 millions of entries as the arbitrary high limit. This

It's probably still far too large.

-Andi


^ permalink raw reply

* RE: [2.6 patch] unexport softnet_data
From: Nelson, Shannon @ 2007-10-26 15:38 UTC (permalink / raw)
  To: David Miller, bunk
  Cc: shemminger, netdev, Leech, Christopher, Williams, Dan J
In-Reply-To: <20071026.041906.223457165.davem@davemloft.net>

>-----Original Message-----
>From: David Miller [mailto:davem@davemloft.net] 
>
>From: Adrian Bunk <bunk@kernel.org>
>Date: Wed, 24 Oct 2007 18:24:25 +0200
>
>> The EXPORT_PER_CPU_SYMBOL(softnet_data) is no longer used.
>> 
>> Signed-off-by: Adrian Bunk <bunk@kernel.org>
>
>I wanted to apply this, but in validing the patch I noticed
>what appears to be an omission in TCP ipv6.
>
>It seems that NET_DMA support there is only half-cooked and
>the following patch is needed (and thus there is a modular
>use of softnet_data again).
>
>Looking at Christopher Leech's original TCP I/O AT commit:
>
>1a2449a87bb7606113b1aa1a9d3c3e78ef189a1c
>
>this appears to just be an oversight.
>
>If one of the current I/O AT folks can look this over and
>confirm I'd appreciate it.
>
>Thanks!
>
>[TCP]: Add missing I/O AT code to ipv6 side.
>
>Signed-off-by: David S. Miller <davem@davemloft.net>
>
>diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
>index 32dc329..06fa4ba 100644
>--- a/net/ipv6/tcp_ipv6.c
>+++ b/net/ipv6/tcp_ipv6.c
>@@ -1732,6 +1732,8 @@ process:
> 	if (!sock_owned_by_user(sk)) {
> #ifdef CONFIG_NET_DMA
> 		struct tcp_sock *tp = tcp_sk(sk);
>+		if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
>+			tp->ucopy.dma_chan = get_softnet_dma();
> 		if (tp->ucopy.dma_chan)
> 			ret = tcp_v6_do_rcv(sk, skb);
> 		else
>

There is no creation of a pinned_list yet in this path, so I don't think
this would do us much good.  Anyway, I believe the next bit is simply
checking to see if we can process it right away (if we have a dma
channel or a reader task is waiting) otherwise stick it into backlog.
Once we go to process it, if it is an IP packet we'll drop into the ipv4
processing code and end up in tcp_recv() which can create the
pinned_list and process from there.

I'm sure someone will correct me if I'm wrong, but I believe this bit it
not needed.  I suggest a NAK for this patch.

sln

^ permalink raw reply

* Re: [PATCH 2/2] [CRYPTO] users: Fix up scatterlist conversion errors
From: Vlad Yasevich @ 2007-10-26 15:44 UTC (permalink / raw)
  To: Herbert Xu
  Cc: David S. Miller, Linux Kernel Mailing List,
	Linux Crypto Mailing List, netdev
In-Reply-To: <E1IlQfN-0003cW-00@gondolin.me.apana.org.au>

Herbert Xu wrote:
> [CRYPTO] users: Fix up scatterlist conversion errors
> 
> This patch fixes the errors made in the users of the crypto layer during
> the sg_init_table conversion.  It also adds a few conversions that were
> missing altogether.
> 
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
> ---
> 
>  drivers/crypto/padlock-sha.c          |    4 +-
>  drivers/md/dm-crypt.c                 |    2 -
>  drivers/net/ppp_mppe.c                |   10 ++++-
>  drivers/scsi/iscsi_tcp.c              |    5 +-
>  fs/ecryptfs/crypto.c                  |    2 +
>  net/ipv4/esp4.c                       |    7 ++-
>  net/ipv6/esp6.c                       |    8 +++-
>  net/rxrpc/rxkad.c                     |   66 +++++++++++++++++-----------------
>  net/sctp/auth.c                       |    3 -
>  net/sctp/sm_make_chunk.c              |    6 +--
>  net/sunrpc/auth_gss/gss_krb5_crypto.c |   34 +++++++++++++----
>  net/sunrpc/auth_gss/gss_spkm3_seal.c  |    2 -
>  net/sunrpc/xdr.c                      |    2 +
>  13 files changed, 93 insertions(+), 58 deletions(-)
> 

Ack SCTP pieces.  I had those queued up, but you got to it before me ;)

Thanks
-vlad

^ permalink raw reply

* Re: [PATCH] net: Saner thash_entries default with much memory
From: akepner @ 2007-10-26 15:55 UTC (permalink / raw)
  To: Jean Delvare; +Cc: netdev, Andi Kleen, Robert.Olsson
In-Reply-To: <200710261721.32019.jdelvare@suse.de>

On Fri, Oct 26, 2007 at 05:21:31PM +0200, Jean Delvare wrote:
> ....
> This is just one way to limit the hash size, there are others; I am not
> familiar enough with the TCP code to decide which is best. Thus, I
> would welcome the proposals of alternatives.
> 

Yeah, the existing way of sizing them is very bad on large systems 
and hardcoding a size sucks (though that's what we do on large 
Altix systems now).

IIRC there was some talk about using a different data structure 
here - something that wouldn't require a fixed size and that 
could maintain reasonably fast lookup even when it got large. 

Robert, has work been done to use TRASH to address this problem? 
Other ideas?

-- 
Arthur


^ permalink raw reply

* Re: Bad TCP checksum error
From: Gaurav Aggarwal @ 2007-10-26 16:03 UTC (permalink / raw)
  To: linux-net-owner
  Cc: netfilter-devel, netdev, linux-net, linux-kernel, davidsen,
	Gaurav Aggarwal
In-Reply-To: <1a41e0840710260041u4ebeb1e3h521e740a78f7e0bf@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 11607 bytes --]

Hi All,

I have dome some changes in the code. Now instead of calculating the
TCP checksum from scratch, I was just recalculating it by incremental
update as mentioned in RFC 1624. Good thing is that now I was able to
get the correct IP header checksum but still TCP checksum value is
corrupted. Interesting thing is, this time the difference in checksum
is exactly the same as that of difference in original and modified
packet header. What I mean to say is that I was changing the
destination IP from "10.102.35.22" to "10.102.35.24" and the
difference in tcp checksum (expected and computed) is coming as 0x02.
Attached is the modified source code and tcpdump. Any help will be
really appreciated.

On 10/26/07, Gaurav Aggarwal <grv.aggarwal@gmail.com> wrote:
> Hi,
>
> I wrote a program where I am using the nfnetlink and netfilter_queue
> model to capture the packet. After that I just change the destination
> address of the packet and insert it back into the ip stack. But after
> inserting the packet I am getting a bad TCP checksum error. Even I am
> getting the same error for IP header checksum. Attached is the source
> code and tcpdump on host machine.
>
> /* Source Code */
> /* Compile with gcc -lnfnetlink -lnetfilter_queue  */
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <unistd.h>
> #include <netinet/in.h>
> #include <netinet/ip.h>
> #include <netinet/tcp.h>
> #include <linux/netfilter.h>            /* for NF_ACCEPT */
> #include <arpa/inet.h>
>
> #include <libnetfilter_queue/libnetfilter_queue.h>
>
> #define  BUFSIZE  2048
>
> struct in_addr foreign;
> struct in_addr local;
>
> struct queued_pckt {
>         char *payload;
>         int payload_len;
> };
>
> struct pseudohdr
> {
>         unsigned long ip_src ;
>         unsigned long ip_dst ;
>         unsigned char reserve ;
>         unsigned char type ;
>         unsigned short length;
> } ;
>
> unsigned short checksum(unsigned short *addr, unsigned int count) {
>         /* Compute Internet Checksum for "count" bytes beginning at location "addr".
>          * Algorithm is simple, using a 32-bit accumulator (sum),
>          * we add sequential 16-bit words to it, and at the end, fold back
>          * all the carry bits from the top 16 bits into the lower 16 bits.
>         */
>        register long sum = 0;
>        unsigned short result;
>
>         while (count > 1)  {
>            /*  This is the inner loop */
>                sum += * addr++;
>                count -= 2;
>        }
>            /*  Add left-over byte, if any */
>        if (count == 1)
>        {
>                 result = 0;     //make sure top half is zero
>                 * (unsigned char *) (&result) = *(unsigned char *)addr;
>                 sum += result;
>         }
>
>         /*
>          * Add back carry outs from top 16 bits to low 16 bits.
>          * Fold 32-bit sum to 16 bits
>         */
>
>        sum = (sum >> 16) + (sum & 0xffff);      /* add high-16 to low-16 */
>        sum += (sum >> 16);                      /* add carry */
>
>        result = ~sum;                   /* ones-complement, then truncate to 16 bits */
>        return (result);
> }
>
>
> unsigned short get_tcp_chksum (struct tcphdr *orig_tcphdr, struct
> iphdr *orig_iphdr )
> {
>         struct pseudohdr pseudoh ;
>
>         unsigned int total_len = ntohs(orig_iphdr->tot_len);
>         int tcpopt_len = (orig_tcphdr->doff * 4) - 20;
>         int tcpdata_len = total_len - (orig_tcphdr->doff * 4) - (orig_iphdr->ihl * 4);
>
>         pseudoh.ip_src = orig_iphdr->saddr ;
>         pseudoh.ip_dst = orig_iphdr->daddr ;
>         pseudoh.reserve = 0 ;
>         pseudoh.type = orig_iphdr->protocol ;
>         pseudoh.length = htons (sizeof (struct tcphdr) + tcpopt_len + tcpdata_len) ;
>
>         int totaltcp_len = sizeof(struct pseudohdr) + sizeof(struct tcphdr) +
> tcpopt_len + tcpdata_len;
>
>         unsigned short *tcp = (unsigned short *)malloc (totaltcp_len);
>
>         memcpy ((unsigned char *)tcp, &pseudoh, sizeof(struct pseudohdr));
>         memcpy ((unsigned char *)tcp + sizeof(struct pseudohdr), (unsigned
> char *)orig_tcphdr, sizeof(struct tcphdr));
>         if (tcpopt_len > 0)
>                 memcpy ((unsigned char *)tcp + sizeof(struct pseudohdr) +
> sizeof(struct tcphdr), (unsigned char *)orig_iphdr + (orig_iphdr->ihl
> * 4) + sizeof(struct tcphdr), tcpopt_len);
>
>         if (tcpdata_len > 0)
>                 memcpy ((unsigned char *)tcp + sizeof(struct pseudohdr) +
> sizeof(struct tcphdr) + tcpopt_len, (unsigned char *)orig_tcphdr +
> (orig_tcphdr->doff * 4), tcpdata_len);
>
> #if 0
>         printf("pseudo length: %d\n",pseudoh.length);
>         printf("tcp hdr length: %d\n",orig_tcphdr->doff*4);
>         printf("tcp hdr struct length: %d\n",sizeof(struct tcphdr));
>         printf("tcphdr->doff = %d, tcp opt length:
> %d\n",orig_tcphdr->doff,tcpopt_len);
>         printf("tcp total+psuedo length: %d\n",totaltcp_len);
>
>         fflush(stdout);
>
>         printf("tcp data len: %d, data start %u\n",
> tcpdata_len,orig_tcphdr + (orig_tcphdr->doff*4));
> #endif
>
>         return (checksum (tcp, totaltcp_len)) ;
> }
>
> static void filter(
>         unsigned char *packet, unsigned int payload_len)
> {
>         struct iphdr *iphdr;
>         struct tcphdr *tcphdr;
>
>         printf ("in filter function\n");
>
>         iphdr = (struct iphdr *)packet;
>         /* check need some datas */
>         if (payload_len < sizeof(struct iphdr) + sizeof(struct tcphdr)) {
>                 return;
>         }
>         /* check IP version */
>         if (iphdr->protocol == IPPROTO_TCP)
>         {
>                 tcphdr = (struct tcphdr *)(((u_int32_t *)packet) + 4 * iphdr->ihl);
>
>                 if (iphdr->daddr == foreign.s_addr)
>                 {
>                         printf ("packet DEST addr = %s\n",inet_ntoa(foreign));
>                         fprintf (stderr, "changing pkt's DEST addr from FOREIGN to LOCAL\n");
>                         iphdr->daddr = local.s_addr;
>                         tcphdr->check = 0 ; // checksum will be calculated later
>                         iphdr->check = checksum(
>                                 (unsigned short *)iphdr,
>                                 sizeof(struct iphdr));
>                         tcphdr->check = get_tcp_chksum(
>                                 tcphdr,
>                                 iphdr);
>                 }
>         }
>
> }
>
> static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
>               struct nfq_data *nfa, void *data)
> {
>         int id = 0;
>         struct nfqnl_msg_packet_hdr *ph;
>         struct queued_pckt q_pckt;
>         u_int32_t mark,ifi;
>         int ret;
>         char *payload;
>
>         printf("entering callback\n");
>         ph = nfq_get_msg_packet_hdr(nfa);
>         if (ph){
>                 id = ntohl(ph->packet_id);
>                 printf("hw_protocol=0x%04x hook=%u id=%u ",
>                         ntohs(ph->hw_protocol), ph->hook, id);
>         }
>
>         mark = nfq_get_nfmark(nfa);
>         if (mark)
>                 printf("mark=%u ", mark);
>
>         ifi = nfq_get_indev(nfa);
>         if (ifi)
>                 printf("indev=%u ", ifi);
>
>         ifi = nfq_get_outdev(nfa);
>         if (ifi)
>                 printf("outdev=%u ", ifi);
>
>         q_pckt.payload_len = nfq_get_payload(nfa, &(q_pckt.payload));
>         if (q_pckt.payload_len >= 0)
>         {
>                 printf("payload_len=%d ", q_pckt.payload_len);
>                 fputc('\n', stdout);
>                 filter((unsigned char *)q_pckt.payload, q_pckt.payload_len);
>         }
>
>         printf("setting verdict of packet id %d\n",id);
>         return nfq_set_verdict(qh, id, NF_ACCEPT, q_pckt.payload_len, q_pckt.payload);
> }
>
> int main(int argc, char **argv)
> {
>         struct nfq_handle *h;
>         struct nfq_q_handle *qh;
>         struct nfnl_handle *nh;
>         int fd;
>         int rv;
>         unsigned char buf[BUFSIZE];
>
>         if (argc == 1)
>         {
>                 inet_aton("10.102.130.222", &(foreign));
>                 inet_aton("10.102.130.105", &(local));
>         } else if (argc == 3)
>         {
>                 inet_aton(argv[1], &(foreign));
>                 inet_aton(argv[2], &(local));
>         }
>         else
>         {
>                 printf("Usage: argv[0] [foreign_addr local_addr]\n");
>                 return 0;
>         }
>
>         printf("opening library handle\n");
>         h = nfq_open();
>         if (!h) {
>                 fprintf(stderr, "error during nfq_open()\n");
>                 return 0;
>         }
>
>         printf("unbinding existing nf_queue handler for AF_INET (if any)\n");
>         if (nfq_unbind_pf(h, AF_INET) < 0) {
>                 fprintf(stderr, "error during nfq_unbind_pf()\n");
>                 exit(1);
>         }
>
>         printf("binding nfnetlink_queue as nf_queue handler for AF_INET\n");
>         if (nfq_bind_pf(h, AF_INET) < 0) {
>                 fprintf(stderr, "error during nfq_bind_pf()\n");
>                 exit(1);
>         }
>
>         printf("binding this socket to queue '0'\n");
>         qh = nfq_create_queue(h,  0, &cb, NULL);
>         if (!qh) {
>                 fprintf(stderr, "error during nfq_create_queue()\n");
>                 exit(1);
>         }
>
>         printf("setting copy_packet mode\n");
>         if (nfq_set_mode(qh, NFQNL_COPY_PACKET, BUFSIZE) < 0) {
>                 fprintf(stderr, "can't set packet_copy mode\n");
>                 exit(1);
>         }
>
>         nh = nfq_nfnlh(h);
>         fd = nfnl_fd(nh);
>
>         while ((rv = recv(fd, buf, BUFSIZE, 0)) && rv >= 0) {
>                 printf("pkt received\n");
>                 nfq_handle_packet(h, buf, rv);
>                 printf("pkt handled\n");
>         }
>
>         printf("unbinding from queue 0\n");
>         nfq_destroy_queue(qh);
>
>         printf("closing library handle\n");
>         nfq_close(h);
>
>         exit(0);
> }
> /* end - Source Code */
>
> /* TCP dump */
> tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
> 12:59:36.706161 IP (tos 0x0, ttl  64, id 61974, offset 0, flags [DF],
> proto: TCP (6), length: 60, bad cksum 75 (->8e24)!) 10.102.35.76.36898
> > 10.102.130.105.colubris: S, cksum 0xc6d5 (incorrect (-> 0xc74a),
> 366446207:366446207(0) win 5840 <mss 1460,sackOK,timestamp 14775401
> 0,nop,wscale 6>
>         0x0000:  0012 010a 5f4c 000b cd3a 5bfb 0800 4500
>         0x0010:  003c f216 4000 4006 0075 0a66 234c 0a66
>         0x0020:  8269 9022 0da2 15d7 867f 0000 0000 a002
>         0x0030:  16d0 c6d5 0000 0204 05b4 0402 080a 00e1
>         0x0040:  7469 0000 0000 0103 0306
> 12:59:39.708619 IP (tos 0x0, ttl  64, id 61975, offset 0, flags [DF],
> proto: TCP (6), length: 60, bad cksum 75 (->8e23)!) 10.102.35.76.36898
> > 10.102.130.105.colubris: S, cksum 0xc3e7 (incorrect (-> 0xc45c),
> 366446207:366446207(0) win 5840 <mss 1460,sackOK,timestamp 14776151
> 0,nop,wscale 6>
>         0x0000:  0012 010a 5f4c 000b cd3a 5bfb 0800 4500
>         0x0010:  003c f217 4000 4006 0075 0a66 234c 0a66
>         0x0020:  8269 9022 0da2 15d7 867f 0000 0000 a002
>         0x0030:  16d0 c3e7 0000 0204 05b4 0402 080a 00e1
>         0x0040:  7757 0000 0000 0103 0306
>
> 2 packets captured
> 4 packets received by filter
> 0 packets dropped by kernel
> /* End - TCP dump */
>
> Attached is the dump for ethreal also.
>
> --
> Regards,
> Gaurav Aggarwal
>
>


-- 
Regards,
Gaurav Aggarwal

[-- Attachment #2: nfq_test_modified.c --]
[-- Type: application/octet-stream, Size: 4157 bytes --]

/* Compile with gcc -lnfnetlink -lnetfilter_queue  */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <linux/netfilter.h>		/* for NF_ACCEPT */
#include <arpa/inet.h>

#include <libnetfilter_queue/libnetfilter_queue.h>

#define  BUFSIZE  2048

struct in_addr foreign;
struct in_addr local;

struct queued_pckt {
	char *payload;
	int payload_len;
};

inline u_int16_t checksum_update_32(
	u_int16_t old_check,
	u_int32_t old,
	u_int32_t new)
{
	u_int32_t l;

	old_check = ~old_check;
	old = ~old;

	l = (u_int32_t)old_check + ((old >> 16) + (old & 0xffff)) + ((new >> 16) + (new & 0xffff));
	return ~((u_int16_t)((l >> 16) + (l & 0xffff)));
}

static void filter(
	unsigned char *packet, unsigned int payload_len)
{
	struct iphdr *iphdr;
	struct tcphdr *tcphdr;

	printf ("in filter function\n");

	iphdr = (struct iphdr *)packet;
	/* check need some datas */
	if (payload_len < sizeof(struct iphdr) + sizeof(struct tcphdr)) {
		return;
	}
	/* check IP version */
	if (iphdr->protocol == IPPROTO_TCP)
	{
		tcphdr = (struct tcphdr *)(((u_int32_t *)packet) + 4 * iphdr->ihl);

		if (iphdr->daddr == foreign.s_addr)
		{
			printf ("packet DEST addr = %s\n",inet_ntoa(foreign));
			printf ("local addr = %s\n",inet_ntoa(local));
			fprintf (stderr, "changing pkt's DEST addr from FOREIGN to LOCAL\n");
			iphdr->check = checksum_update_32(
				iphdr->check,
				iphdr->daddr,
				local.s_addr);
			tcphdr->check = checksum_update_32(
				tcphdr->check,
				iphdr->daddr,
				local.s_addr);
			iphdr->daddr = local.s_addr;
		}
	}

}

static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
	      struct nfq_data *nfa, void *data)
{
	int id = 0;
	struct nfqnl_msg_packet_hdr *ph;
	struct queued_pckt q_pckt;
	u_int32_t mark,ifi; 
	int ret;
	char *payload;
	
	printf("entering callback\n");
	ph = nfq_get_msg_packet_hdr(nfa);
	if (ph){
		id = ntohl(ph->packet_id);
		printf("hw_protocol=0x%04x hook=%u id=%u ",
			ntohs(ph->hw_protocol), ph->hook, id);
	}
	
	mark = nfq_get_nfmark(nfa);
	if (mark)
		printf("mark=%u ", mark);

	ifi = nfq_get_indev(nfa);
	if (ifi)
		printf("indev=%u ", ifi);

	ifi = nfq_get_outdev(nfa);
	if (ifi)
		printf("outdev=%u ", ifi);

	q_pckt.payload_len = nfq_get_payload(nfa, &(q_pckt.payload));
	if (q_pckt.payload_len >= 0)
	{
		printf("payload_len=%d ", q_pckt.payload_len);
		fputc('\n', stdout);
		filter((unsigned char *)q_pckt.payload, q_pckt.payload_len);
	}
	
	printf("setting verdict of packet id %d\n",id);
	return nfq_set_verdict(qh, id, NF_ACCEPT, q_pckt.payload_len, q_pckt.payload);
}

int main(int argc, char **argv)
{
	struct nfq_handle *h;
	struct nfq_q_handle *qh;
	struct nfnl_handle *nh;
	int fd;
	int rv;
	unsigned char buf[BUFSIZE];

	inet_aton("10.102.35.22", &(foreign));
	inet_aton("10.102.35.24", &(local));

	printf("opening library handle\n");
	h = nfq_open();
	if (!h) {
		fprintf(stderr, "error during nfq_open()\n");
		exit(1);
	}

	printf("unbinding existing nf_queue handler for AF_INET (if any)\n");
	if (nfq_unbind_pf(h, AF_INET) < 0) {
		fprintf(stderr, "error during nfq_unbind_pf()\n");
		exit(1);
	}

	printf("binding nfnetlink_queue as nf_queue handler for AF_INET\n");
	if (nfq_bind_pf(h, AF_INET) < 0) {
		fprintf(stderr, "error during nfq_bind_pf()\n");
		exit(1);
	}

	printf("binding this socket to queue '0'\n");
	qh = nfq_create_queue(h,  0, &cb, NULL);
	if (!qh) {
		fprintf(stderr, "error during nfq_create_queue()\n");
		exit(1);
	}

	printf("setting copy_packet mode\n");
	if (nfq_set_mode(qh, NFQNL_COPY_PACKET, BUFSIZE) < 0) {
		fprintf(stderr, "can't set packet_copy mode\n");
		exit(1);
	}

	nh = nfq_nfnlh(h);
	fd = nfnl_fd(nh);

	while ((rv = recv(fd, buf, BUFSIZE, 0)) && rv >= 0) {
		printf("pkt received\n");
		nfq_handle_packet(h, buf, rv);
		printf("pkt handled\n");
	}

	printf("unbinding from queue 0\n");
	nfq_destroy_queue(qh);

	printf("closing library handle\n");
	nfq_close(h);

	exit(0);
}

[-- Attachment #3: tcpdump.txt --]
[-- Type: text/plain, Size: 1136 bytes --]

tcpdump: listening on eth0
21:00:01.490920 10.102.35.76.38067 > 10.102.35.24.3490: S [bad tcp cksum fdff!] 1064689745:1064689745(0) win 5840 <mss 1460,sackOK,timestamp 4491010 0,nop,wscale 7> (DF) (ttl 64, id 26451, len 60)
			 4500 003c 6753 4000 4006 7839 0a66 234c
			 0a66 2318 94b3 0da2 3f75 e051 0000 0000
			 a002 16d0 8c9f 0000 0204 05b4 0402 080a
			 0044 8702 0000 0000 0103 0307
21:00:04.489859 10.102.35.76.38067 > 10.102.35.24.3490: S [bad tcp cksum fdff!] 1064689745:1064689745(0) win 5840 <mss 1460,sackOK,timestamp 4491760 0,nop,wscale 7> (DF) (ttl 64, id 26452, len 60)
			 4500 003c 6754 4000 4006 7838 0a66 234c
			 0a66 2318 94b3 0da2 3f75 e051 0000 0000
			 a002 16d0 89b1 0000 0204 05b4 0402 080a
			 0044 89f0 0000 0000 0103 0307
21:00:06.489554 arp who-has 10.102.35.24 tell 10.102.35.76
			 0001 0800 0604 0001 000b cd3a 5bfb 0a66
			 234c 0000 0000 0000 0a66 2318 0000 0000
			 0000 0000 0000 0000 0000 0000 0000
21:00:06.489596 arp reply 10.102.35.24 is-at 0:80:c8:1:56:13
			 0001 0800 0604 0002 0080 c801 5613 0a66
			 2318 000b cd3a 5bfb 0a66 234c

4 packets received by filter
0 packets dropped by kernel

[-- Attachment #4: ethreal_dump.pcap --]
[-- Type: application/octet-stream, Size: 338 bytes --]

^ permalink raw reply

* [PATCH v4.3] FEC - fast ethernet controller for mpc52xx
From: Domen Puncer @ 2007-10-26 16:07 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Dale Farnsworth, netdev, linuxppc-dev
In-Reply-To: <20071026141828.GA14900@xyzzy.farnsworth.org>

On 26/10/07 07:18 -0700, Dale Farnsworth wrote:
> On Fri, Oct 26, 2007 at 01:59:09PM +0200, Domen Puncer wrote:
> > +static irqreturn_t mpc52xx_fec_tx_interrupt(int irq, void *dev_id)
> > +{
> > +	struct net_device *dev = dev_id;
> > +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> > +
> > +	spin_lock(&priv->lock);
> > +
> > +	while (bcom_buffer_done(priv->tx_dmatsk)) {
> > +		struct sk_buff *skb;
> > +		struct bcom_fec_bd *bd;
> > +		skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL,
> > +				(struct bcom_bd **)&bd);
> > +		/* Here (and in rx routines) would be a good place for
> > +		 * dma_unmap_single(), but bcom doesn't return bcom_bd of the
> > +		 * finished transfer, and _unmap is empty on this platfrom.
> > +		 */
> 
> Oops, you forgot to remove the above comment.  :)

Argh!

Repost w/o the comment.
Sorry for receiving all this almost-spam.


> 
> Otherwise,
> Acked-by: Dale Farnsworth <dale@farnsworth.org>
> 
> Domen, thanks for all your work on this.  It's good to see it finally go in.
> 
> -Dale

--- again, use your scisors here ;-) ---


Driver for ethernet on mpc5200/mpc5200b SoCs (FEC).


Signed-off-by: Domen Puncer <domen.puncer@telargo.com>
Acked-by: Dale Farnsworth <dale@farnsworth.org>

---
 drivers/net/Kconfig           |   24 
 drivers/net/Makefile          |    4 
 drivers/net/fec_mpc52xx.c     | 1112 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/fec_mpc52xx.h     |  313 +++++++++++
 drivers/net/fec_mpc52xx_phy.c |  198 +++++++
 5 files changed, 1651 insertions(+)

Index: linux.git/drivers/net/Kconfig
===================================================================
--- linux.git.orig/drivers/net/Kconfig
+++ linux.git/drivers/net/Kconfig
@@ -1880,6 +1880,30 @@ config FEC2
 	  Say Y here if you want to use the second built-in 10/100 Fast
 	  ethernet controller on some Motorola ColdFire processors.
 
+config FEC_MPC52xx
+	tristate "MPC52xx FEC driver"
+	depends on PPC_MPC52xx
+	select PPC_BESTCOMM
+	select PPC_BESTCOMM_FEC
+	select CRC32
+	select PHYLIB
+	---help---
+	  This option enables support for the MPC5200's on-chip
+	  Fast Ethernet Controller
+	  If compiled as module, it will be called 'fec_mpc52xx.ko'.
+
+config FEC_MPC52xx_MDIO
+	bool "MPC52xx FEC MDIO bus driver"
+	depends on FEC_MPC52xx
+	default y
+	---help---
+	  The MPC5200's FEC can connect to the Ethernet either with
+	  an external MII PHY chip or 10 Mbps 7-wire interface
+	  (Motorola? industry standard).
+	  If your board uses an external PHY connected to FEC, enable this.
+	  If not sure, enable.
+	  If compiled as module, it will be called 'fec_mpc52xx_phy.ko'.
+
 config NE_H8300
 	tristate "NE2000 compatible support for H8/300"
 	depends on H8300
Index: linux.git/drivers/net/Makefile
===================================================================
--- linux.git.orig/drivers/net/Makefile
+++ linux.git/drivers/net/Makefile
@@ -96,6 +96,10 @@ obj-$(CONFIG_SHAPER) += shaper.o
 obj-$(CONFIG_HP100) += hp100.o
 obj-$(CONFIG_SMC9194) += smc9194.o
 obj-$(CONFIG_FEC) += fec.o
+obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
+ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
+	obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
+endif
 obj-$(CONFIG_68360_ENET) += 68360enet.o
 obj-$(CONFIG_WD80x3) += wd.o 8390.o
 obj-$(CONFIG_EL2) += 3c503.o 8390.o
Index: linux.git/drivers/net/fec_mpc52xx.c
===================================================================
--- /dev/null
+++ linux.git/drivers/net/fec_mpc52xx.c
@@ -0,0 +1,1112 @@
+/*
+ * Driver for the MPC5200 Fast Ethernet Controller
+ *
+ * Originally written by Dale Farnsworth <dfarnsworth@mvista.com> and
+ * now maintained by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2007  Domen Puncer, Telargo, Inc.
+ * Copyright (C) 2007  Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003-2004  MontaVista, Software, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <linux/hardirq.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/mpc52xx.h>
+
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/fec.h>
+
+#include "fec_mpc52xx.h"
+
+#define DRIVER_NAME "mpc52xx-fec"
+
+static irqreturn_t mpc52xx_fec_interrupt(int, void *);
+static irqreturn_t mpc52xx_fec_rx_interrupt(int, void *);
+static irqreturn_t mpc52xx_fec_tx_interrupt(int, void *);
+static void mpc52xx_fec_stop(struct net_device *dev);
+static void mpc52xx_fec_start(struct net_device *dev);
+static void mpc52xx_fec_reset(struct net_device *dev);
+
+static u8 mpc52xx_fec_mac_addr[6];
+module_param_array_named(mac, mpc52xx_fec_mac_addr, byte, NULL, 0);
+MODULE_PARM_DESC(mac, "six hex digits, ie. 0x1,0x2,0xc0,0x01,0xba,0xbe");
+
+#define MPC52xx_MESSAGES_DEFAULT ( NETIF_MSG_DRV | NETIF_MSG_PROBE | \
+		NETIF_MSG_LINK | NETIF_MSG_IFDOWN | NETIF_MSG_IFDOWN )
+static int debug = -1;	/* the above default */
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debugging messages level");
+
+static void mpc52xx_fec_tx_timeout(struct net_device *dev)
+{
+	dev_warn(&dev->dev, "transmit timed out\n");
+
+	mpc52xx_fec_reset(dev);
+
+	dev->stats.tx_errors++;
+
+	netif_wake_queue(dev);
+}
+
+static void mpc52xx_fec_set_paddr(struct net_device *dev, u8 *mac)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	out_be32(&fec->paddr1, *(u32 *)(&mac[0]));
+	out_be32(&fec->paddr2, (*(u16 *)(&mac[4]) << 16) | FEC_PADDR2_TYPE);
+}
+
+static void mpc52xx_fec_get_paddr(struct net_device *dev, u8 *mac)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	*(u32 *)(&mac[0]) = in_be32(&fec->paddr1);
+	*(u16 *)(&mac[4]) = in_be32(&fec->paddr2) >> 16;
+}
+
+static int mpc52xx_fec_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *sock = addr;
+
+	memcpy(dev->dev_addr, sock->sa_data, dev->addr_len);
+
+	mpc52xx_fec_set_paddr(dev, sock->sa_data);
+	return 0;
+}
+
+static void mpc52xx_fec_free_rx_buffers(struct net_device *dev, struct bcom_task *s)
+{
+	while (!bcom_queue_empty(s)) {
+		struct bcom_fec_bd *bd;
+		struct sk_buff *skb;
+
+		skb = bcom_retrieve_buffer(s, NULL, (struct bcom_bd **)&bd);
+		dma_unmap_single(&dev->dev, bd->skb_pa, skb->len, DMA_FROM_DEVICE);
+		kfree_skb(skb);
+	}
+}
+
+static int mpc52xx_fec_alloc_rx_buffers(struct net_device *dev, struct bcom_task *rxtsk)
+{
+	while (!bcom_queue_full(rxtsk)) {
+		struct sk_buff *skb;
+		struct bcom_fec_bd *bd;
+
+		skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
+		if (skb == NULL)
+			return -EAGAIN;
+
+		/* zero out the initial receive buffers to aid debugging */
+		memset(skb->data, 0, FEC_RX_BUFFER_SIZE);
+
+		bd = (struct bcom_fec_bd *)bcom_prepare_next_buffer(rxtsk);
+
+		bd->status = FEC_RX_BUFFER_SIZE;
+		bd->skb_pa = dma_map_single(&dev->dev, skb->data,
+				FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+		bcom_submit_next_buffer(rxtsk, skb);
+	}
+
+	return 0;
+}
+
+/* based on generic_adjust_link from fs_enet-main.c */
+static void mpc52xx_fec_adjust_link(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct phy_device *phydev = priv->phydev;
+	int new_state = 0;
+
+	if (phydev->link != PHY_DOWN) {
+		if (phydev->duplex != priv->duplex) {
+			struct mpc52xx_fec __iomem *fec = priv->fec;
+			u32 rcntrl;
+			u32 tcntrl;
+
+			new_state = 1;
+			priv->duplex = phydev->duplex;
+
+			rcntrl = in_be32(&fec->r_cntrl);
+			tcntrl = in_be32(&fec->x_cntrl);
+
+			rcntrl &= ~FEC_RCNTRL_DRT;
+			tcntrl &= ~FEC_TCNTRL_FDEN;
+			if (phydev->duplex == DUPLEX_FULL)
+				tcntrl |= FEC_TCNTRL_FDEN;	/* FD enable */
+			else
+				rcntrl |= FEC_RCNTRL_DRT;	/* disable Rx on Tx (HD) */
+
+			out_be32(&fec->r_cntrl, rcntrl);
+			out_be32(&fec->x_cntrl, tcntrl);
+		}
+
+		if (phydev->speed != priv->speed) {
+			new_state = 1;
+			priv->speed = phydev->speed;
+		}
+
+		if (priv->link == PHY_DOWN) {
+			new_state = 1;
+			priv->link = phydev->link;
+			netif_schedule(dev);
+			netif_carrier_on(dev);
+			netif_start_queue(dev);
+		}
+
+	} else if (priv->link) {
+		new_state = 1;
+		priv->link = PHY_DOWN;
+		priv->speed = 0;
+		priv->duplex = -1;
+		netif_stop_queue(dev);
+		netif_carrier_off(dev);
+	}
+
+	if (new_state && netif_msg_link(priv))
+		phy_print_status(phydev);
+}
+
+static int mpc52xx_fec_init_phy(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct phy_device *phydev;
+	char phy_id[BUS_ID_SIZE];
+
+	snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT,
+			(unsigned int)dev->base_addr, priv->phy_addr);
+
+	priv->link = PHY_DOWN;
+	priv->speed = 0;
+	priv->duplex = -1;
+
+	phydev = phy_connect(dev, phy_id, &mpc52xx_fec_adjust_link, 0, PHY_INTERFACE_MODE_MII);
+	if (IS_ERR(phydev)) {
+		dev_err(&dev->dev, "phy_connect failed\n");
+		return PTR_ERR(phydev);
+	}
+	dev_info(&dev->dev, "attached phy %i to driver %s\n",
+			phydev->addr, phydev->drv->name);
+
+	priv->phydev = phydev;
+
+	return 0;
+}
+
+static int mpc52xx_fec_phy_start(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	int err;
+
+	if (!priv->has_phy)
+		return 0;
+
+	err = mpc52xx_fec_init_phy(dev);
+	if (err) {
+		dev_err(&dev->dev, "mpc52xx_fec_init_phy failed\n");
+		return err;
+	}
+
+	/* reset phy - this also wakes it from PDOWN */
+	phy_write(priv->phydev, MII_BMCR, BMCR_RESET);
+	phy_start(priv->phydev);
+
+	return 0;
+}
+
+static void mpc52xx_fec_phy_stop(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+	if (!priv->has_phy)
+		return;
+
+	phy_disconnect(priv->phydev);
+	/* power down phy */
+	phy_stop(priv->phydev);
+	phy_write(priv->phydev, MII_BMCR, BMCR_PDOWN);
+}
+
+static int mpc52xx_fec_phy_mii_ioctl(struct mpc52xx_fec_priv *priv,
+		struct mii_ioctl_data *mii_data, int cmd)
+{
+	if (!priv->has_phy)
+		return -ENOTSUPP;
+
+	return phy_mii_ioctl(priv->phydev, mii_data, cmd);
+}
+
+static void mpc52xx_fec_phy_hw_init(struct mpc52xx_fec_priv *priv)
+{
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	if (!priv->has_phy)
+		return;
+
+	out_be32(&fec->mii_speed, priv->phy_speed);
+}
+
+static int mpc52xx_fec_open(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	int err = -EBUSY;
+
+	if (request_irq(dev->irq, &mpc52xx_fec_interrupt, IRQF_SHARED,
+	                DRIVER_NAME "_ctrl", dev)) {
+		dev_err(&dev->dev, "ctrl interrupt request failed\n");
+		goto out;
+	}
+	if (request_irq(priv->r_irq, &mpc52xx_fec_rx_interrupt, 0,
+	                DRIVER_NAME "_rx", dev)) {
+		dev_err(&dev->dev, "rx interrupt request failed\n");
+		goto free_ctrl_irq;
+	}
+	if (request_irq(priv->t_irq, &mpc52xx_fec_tx_interrupt, 0,
+	                DRIVER_NAME "_tx", dev)) {
+		dev_err(&dev->dev, "tx interrupt request failed\n");
+		goto free_2irqs;
+	}
+
+	bcom_fec_rx_reset(priv->rx_dmatsk);
+	bcom_fec_tx_reset(priv->tx_dmatsk);
+
+	err = mpc52xx_fec_alloc_rx_buffers(dev, priv->rx_dmatsk);
+	if (err) {
+		dev_err(&dev->dev, "mpc52xx_fec_alloc_rx_buffers failed\n");
+		goto free_irqs;
+	}
+
+	err = mpc52xx_fec_phy_start(dev);
+	if (err)
+		goto free_skbs;
+
+	bcom_enable(priv->rx_dmatsk);
+	bcom_enable(priv->tx_dmatsk);
+
+	mpc52xx_fec_start(dev);
+
+	netif_start_queue(dev);
+
+	return 0;
+
+ free_skbs:
+	mpc52xx_fec_free_rx_buffers(dev, priv->rx_dmatsk);
+
+ free_irqs:
+	free_irq(priv->t_irq, dev);
+ free_2irqs:
+	free_irq(priv->r_irq, dev);
+ free_ctrl_irq:
+	free_irq(dev->irq, dev);
+ out:
+
+	return err;
+}
+
+static int mpc52xx_fec_close(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+	netif_stop_queue(dev);
+
+	mpc52xx_fec_stop(dev);
+
+	mpc52xx_fec_free_rx_buffers(dev, priv->rx_dmatsk);
+
+	free_irq(dev->irq, dev);
+	free_irq(priv->r_irq, dev);
+	free_irq(priv->t_irq, dev);
+
+	mpc52xx_fec_phy_stop(dev);
+
+	return 0;
+}
+
+/* This will only be invoked if your driver is _not_ in XOFF state.
+ * What this means is that you need not check it, and that this
+ * invariant will hold if you make sure that the netif_*_queue()
+ * calls are done at the proper times.
+ */
+static int mpc52xx_fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct bcom_fec_bd *bd;
+
+	if (bcom_queue_full(priv->tx_dmatsk)) {
+		if (net_ratelimit())
+			dev_err(&dev->dev, "transmit queue overrun\n");
+		return 1;
+	}
+
+	spin_lock_irq(&priv->lock);
+	dev->trans_start = jiffies;
+
+	bd = (struct bcom_fec_bd *)
+		bcom_prepare_next_buffer(priv->tx_dmatsk);
+
+	bd->status = skb->len | BCOM_FEC_TX_BD_TFD | BCOM_FEC_TX_BD_TC;
+	bd->skb_pa = dma_map_single(&dev->dev, skb->data, skb->len, DMA_TO_DEVICE);
+
+	bcom_submit_next_buffer(priv->tx_dmatsk, skb);
+
+	if (bcom_queue_full(priv->tx_dmatsk)) {
+		netif_stop_queue(dev);
+	}
+
+	spin_unlock_irq(&priv->lock);
+
+	return 0;
+}
+
+/* This handles BestComm transmit task interrupts
+ */
+static irqreturn_t mpc52xx_fec_tx_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+	spin_lock(&priv->lock);
+
+	while (bcom_buffer_done(priv->tx_dmatsk)) {
+		struct sk_buff *skb;
+		struct bcom_fec_bd *bd;
+		skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL,
+				(struct bcom_bd **)&bd);
+		dma_unmap_single(&dev->dev, bd->skb_pa, skb->len, DMA_TO_DEVICE);
+
+		dev_kfree_skb_irq(skb);
+	}
+
+	netif_wake_queue(dev);
+
+	spin_unlock(&priv->lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mpc52xx_fec_rx_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+	while (bcom_buffer_done(priv->rx_dmatsk)) {
+		struct sk_buff *skb;
+		struct sk_buff *rskb;
+		struct bcom_fec_bd *bd;
+		u32 status;
+
+		rskb = bcom_retrieve_buffer(priv->rx_dmatsk, &status,
+				(struct bcom_bd **)&bd);
+		dma_unmap_single(&dev->dev, bd->skb_pa, skb->len, DMA_FROM_DEVICE);
+
+		/* Test for errors in received frame */
+		if (status & BCOM_FEC_RX_BD_ERRORS) {
+			/* Drop packet and reuse the buffer */
+			bd = (struct bcom_fec_bd *)
+				bcom_prepare_next_buffer(priv->rx_dmatsk);
+
+			bd->status = FEC_RX_BUFFER_SIZE;
+			bd->skb_pa = dma_map_single(&dev->dev, rskb->data,
+					FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+			bcom_submit_next_buffer(priv->rx_dmatsk, rskb);
+
+			dev->stats.rx_dropped++;
+
+			continue;
+		}
+
+		/* skbs are allocated on open, so now we allocate a new one,
+		 * and remove the old (with the packet) */
+		skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
+		if (skb) {
+			/* Process the received skb */
+			int length = status & BCOM_FEC_RX_BD_LEN_MASK;
+
+			skb_put(rskb, length - 4);	/* length without CRC32 */
+
+			rskb->dev = dev;
+			rskb->protocol = eth_type_trans(rskb, dev);
+
+			netif_rx(rskb);
+			dev->last_rx = jiffies;
+		} else {
+			/* Can't get a new one : reuse the same & drop pkt */
+			dev_notice(&dev->dev, "Memory squeeze, dropping packet.\n");
+			dev->stats.rx_dropped++;
+
+			skb = rskb;
+		}
+
+		bd = (struct bcom_fec_bd *)
+			bcom_prepare_next_buffer(priv->rx_dmatsk);
+
+		bd->status = FEC_RX_BUFFER_SIZE;
+		bd->skb_pa = dma_map_single(&dev->dev, rskb->data,
+				FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+		bcom_submit_next_buffer(priv->rx_dmatsk, skb);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mpc52xx_fec_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+	u32 ievent;
+
+	ievent = in_be32(&fec->ievent);
+
+	ievent &= ~FEC_IEVENT_MII;	/* mii is handled separately */
+	if (!ievent)
+		return IRQ_NONE;
+
+	out_be32(&fec->ievent, ievent);		/* clear pending events */
+
+	if (ievent & ~(FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) {
+		if (ievent & ~FEC_IEVENT_TFINT)
+			dev_dbg(&dev->dev, "ievent: %08x\n", ievent);
+		return IRQ_HANDLED;
+	}
+
+	if (net_ratelimit() && (ievent & FEC_IEVENT_RFIFO_ERROR))
+		dev_warn(&dev->dev, "FEC_IEVENT_RFIFO_ERROR\n");
+	if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR))
+		dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n");
+
+	mpc52xx_fec_reset(dev);
+
+	netif_wake_queue(dev);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *mpc52xx_fec_get_stats(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct net_device_stats *stats = &dev->stats;
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	stats->rx_bytes = in_be32(&fec->rmon_r_octets);
+	stats->rx_packets = in_be32(&fec->rmon_r_packets);
+	stats->rx_errors = in_be32(&fec->rmon_r_crc_align) +
+		in_be32(&fec->rmon_r_undersize) +
+		in_be32(&fec->rmon_r_oversize) +
+		in_be32(&fec->rmon_r_frag) +
+		in_be32(&fec->rmon_r_jab);
+
+	stats->tx_bytes = in_be32(&fec->rmon_t_octets);
+	stats->tx_packets = in_be32(&fec->rmon_t_packets);
+	stats->tx_errors = in_be32(&fec->rmon_t_crc_align) +
+		in_be32(&fec->rmon_t_undersize) +
+		in_be32(&fec->rmon_t_oversize) +
+		in_be32(&fec->rmon_t_frag) +
+		in_be32(&fec->rmon_t_jab);
+
+	stats->multicast = in_be32(&fec->rmon_r_mc_pkt);
+	stats->collisions = in_be32(&fec->rmon_t_col);
+
+	/* detailed rx_errors: */
+	stats->rx_length_errors = in_be32(&fec->rmon_r_undersize)
+					+ in_be32(&fec->rmon_r_oversize)
+					+ in_be32(&fec->rmon_r_frag)
+					+ in_be32(&fec->rmon_r_jab);
+	stats->rx_over_errors = in_be32(&fec->r_macerr);
+	stats->rx_crc_errors = in_be32(&fec->ieee_r_crc);
+	stats->rx_frame_errors = in_be32(&fec->ieee_r_align);
+	stats->rx_fifo_errors = in_be32(&fec->rmon_r_drop);
+	stats->rx_missed_errors = in_be32(&fec->rmon_r_drop);
+
+	/* detailed tx_errors: */
+	stats->tx_aborted_errors = 0;
+	stats->tx_carrier_errors = in_be32(&fec->ieee_t_cserr);
+	stats->tx_fifo_errors = in_be32(&fec->rmon_t_drop);
+	stats->tx_heartbeat_errors = in_be32(&fec->ieee_t_sqe);
+	stats->tx_window_errors = in_be32(&fec->ieee_t_lcol);
+
+	return stats;
+}
+
+/*
+ * Read MIB counters in order to reset them,
+ * then zero all the stats fields in memory
+ */
+static void mpc52xx_fec_reset_stats(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	out_be32(&fec->mib_control, FEC_MIB_DISABLE);
+	memset_io(&fec->rmon_t_drop, 0,	(__force u32)&fec->reserved10 -
+			(__force u32)&fec->rmon_t_drop);
+	out_be32(&fec->mib_control, 0);
+
+	memset(&dev->stats, 0, sizeof(dev->stats));
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+static void mpc52xx_fec_set_multicast_list(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+	u32 rx_control;
+
+	rx_control = in_be32(&fec->r_cntrl);
+
+	if (dev->flags & IFF_PROMISC) {
+		rx_control |= FEC_RCNTRL_PROM;
+		out_be32(&fec->r_cntrl, rx_control);
+	} else {
+		rx_control &= ~FEC_RCNTRL_PROM;
+		out_be32(&fec->r_cntrl, rx_control);
+
+		if (dev->flags & IFF_ALLMULTI) {
+			out_be32(&fec->gaddr1, 0xffffffff);
+			out_be32(&fec->gaddr2, 0xffffffff);
+		} else {
+			u32 crc;
+			int i;
+			struct dev_mc_list *dmi;
+			u32 gaddr1 = 0x00000000;
+			u32 gaddr2 = 0x00000000;
+
+			dmi = dev->mc_list;
+			for (i=0; i<dev->mc_count; i++) {
+				crc = ether_crc_le(6, dmi->dmi_addr) >> 26;
+				if (crc >= 32)
+					gaddr1 |= 1 << (crc-32);
+				else
+					gaddr2 |= 1 << crc;
+				dmi = dmi->next;
+			}
+			out_be32(&fec->gaddr1, gaddr1);
+			out_be32(&fec->gaddr2, gaddr2);
+		}
+	}
+}
+
+/**
+ * mpc52xx_fec_hw_init
+ * @dev: network device
+ *
+ * Setup various hardware setting, only needed once on start
+ */
+static void mpc52xx_fec_hw_init(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+	int i;
+
+	/* Whack a reset.  We should wait for this. */
+	out_be32(&fec->ecntrl, FEC_ECNTRL_RESET);
+	for (i = 0; i < FEC_RESET_DELAY; ++i) {
+		if ((in_be32(&fec->ecntrl) & FEC_ECNTRL_RESET) == 0)
+			break;
+		udelay(1);
+	}
+	if (i == FEC_RESET_DELAY)
+		dev_err(&dev->dev, "FEC Reset timeout!\n");
+
+	/* set pause to 0x20 frames */
+	out_be32(&fec->op_pause, FEC_OP_PAUSE_OPCODE | 0x20);
+
+	/* high service request will be deasserted when there's < 7 bytes in fifo
+	 * low service request will be deasserted when there's < 4*7 bytes in fifo
+	 */
+	out_be32(&fec->rfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7);
+	out_be32(&fec->tfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7);
+
+	/* alarm when <= x bytes in FIFO */
+	out_be32(&fec->rfifo_alarm, 0x0000030c);
+	out_be32(&fec->tfifo_alarm, 0x00000100);
+
+	/* begin transmittion when 256 bytes are in FIFO (or EOF or FIFO full) */
+	out_be32(&fec->x_wmrk, FEC_FIFO_WMRK_256B);
+
+	/* enable crc generation */
+	out_be32(&fec->xmit_fsm, FEC_XMIT_FSM_APPEND_CRC | FEC_XMIT_FSM_ENABLE_CRC);
+	out_be32(&fec->iaddr1, 0x00000000);	/* No individual filter */
+	out_be32(&fec->iaddr2, 0x00000000);	/* No individual filter */
+
+	/* set phy speed.
+	 * this can't be done in phy driver, since it needs to be called
+	 * before fec stuff (even on resume) */
+	mpc52xx_fec_phy_hw_init(priv);
+}
+
+/**
+ * mpc52xx_fec_start
+ * @dev: network device
+ *
+ * This function is called to start or restart the FEC during a link
+ * change.  This happens on fifo errors or when switching between half
+ * and full duplex.
+ */
+static void mpc52xx_fec_start(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+	u32 rcntrl;
+	u32 tcntrl;
+	u32 tmp;
+
+	/* clear sticky error bits */
+	tmp = FEC_FIFO_STATUS_ERR | FEC_FIFO_STATUS_UF | FEC_FIFO_STATUS_OF;
+	out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & tmp);
+	out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & tmp);
+
+	/* FIFOs will reset on mpc52xx_fec_enable */
+	out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_ENABLE_IS_RESET);
+
+	/* Set station address. */
+	mpc52xx_fec_set_paddr(dev, dev->dev_addr);
+
+	mpc52xx_fec_set_multicast_list(dev);
+
+	/* set max frame len, enable flow control, select mii mode */
+	rcntrl = FEC_RX_BUFFER_SIZE << 16;	/* max frame length */
+	rcntrl |= FEC_RCNTRL_FCE;
+
+	if (priv->has_phy)
+		rcntrl |= FEC_RCNTRL_MII_MODE;
+
+	if (priv->duplex == DUPLEX_FULL)
+		tcntrl = FEC_TCNTRL_FDEN;	/* FD enable */
+	else {
+		rcntrl |= FEC_RCNTRL_DRT;	/* disable Rx on Tx (HD) */
+		tcntrl = 0;
+	}
+	out_be32(&fec->r_cntrl, rcntrl);
+	out_be32(&fec->x_cntrl, tcntrl);
+
+	/* Clear any outstanding interrupt. */
+	out_be32(&fec->ievent, 0xffffffff);
+
+	/* Enable interrupts we wish to service. */
+	out_be32(&fec->imask, FEC_IMASK_ENABLE);
+
+	/* And last, enable the transmit and receive processing. */
+	out_be32(&fec->ecntrl, FEC_ECNTRL_ETHER_EN);
+	out_be32(&fec->r_des_active, 0x01000000);
+}
+
+/**
+ * mpc52xx_fec_stop
+ * @dev: network device
+ *
+ * stop all activity on fec and empty dma buffers
+ */
+static void mpc52xx_fec_stop(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+	unsigned long timeout;
+
+	/* disable all interrupts */
+	out_be32(&fec->imask, 0);
+
+	/* Disable the rx task. */
+	bcom_disable(priv->rx_dmatsk);
+
+	/* Wait for tx queue to drain, but only if we're in process context */
+	if (!in_interrupt()) {
+		timeout = jiffies + msecs_to_jiffies(2000);
+		while (time_before(jiffies, timeout) &&
+				!bcom_queue_empty(priv->tx_dmatsk))
+			msleep(100);
+
+		if (time_after_eq(jiffies, timeout))
+			dev_err(&dev->dev, "queues didn't drain\n");
+#if 1
+		if (time_after_eq(jiffies, timeout)) {
+			dev_err(&dev->dev, "  tx: index: %i, outdex: %i\n",
+					priv->tx_dmatsk->index,
+					priv->tx_dmatsk->outdex);
+			dev_err(&dev->dev, "  rx: index: %i, outdex: %i\n",
+					priv->rx_dmatsk->index,
+					priv->rx_dmatsk->outdex);
+		}
+#endif
+	}
+
+	bcom_disable(priv->tx_dmatsk);
+
+	/* Stop FEC */
+	out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~FEC_ECNTRL_ETHER_EN);
+
+	return;
+}
+
+/* reset fec and bestcomm tasks */
+static void mpc52xx_fec_reset(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	struct mpc52xx_fec __iomem *fec = priv->fec;
+
+	mpc52xx_fec_stop(dev);
+
+	out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status));
+	out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_RESET_FIFO);
+
+	mpc52xx_fec_free_rx_buffers(dev, priv->rx_dmatsk);
+
+	mpc52xx_fec_hw_init(dev);
+
+	phy_stop(priv->phydev);
+	phy_write(priv->phydev, MII_BMCR, BMCR_RESET);
+	phy_start(priv->phydev);
+
+	bcom_fec_rx_reset(priv->rx_dmatsk);
+	bcom_fec_tx_reset(priv->tx_dmatsk);
+
+	mpc52xx_fec_alloc_rx_buffers(dev, priv->rx_dmatsk);
+
+	bcom_enable(priv->rx_dmatsk);
+	bcom_enable(priv->tx_dmatsk);
+
+	mpc52xx_fec_start(dev);
+}
+
+
+/* ethtool interface */
+static void mpc52xx_fec_get_drvinfo(struct net_device *dev,
+		struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, DRIVER_NAME);
+}
+
+static int mpc52xx_fec_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int mpc52xx_fec_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static u32 mpc52xx_fec_get_msglevel(struct net_device *dev)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	return priv->msg_enable;
+}
+
+static void mpc52xx_fec_set_msglevel(struct net_device *dev, u32 level)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+	priv->msg_enable = level;
+}
+
+static const struct ethtool_ops mpc52xx_fec_ethtool_ops = {
+	.get_drvinfo = mpc52xx_fec_get_drvinfo,
+	.get_settings = mpc52xx_fec_get_settings,
+	.set_settings = mpc52xx_fec_set_settings,
+	.get_link = ethtool_op_get_link,
+	.get_msglevel = mpc52xx_fec_get_msglevel,
+	.set_msglevel = mpc52xx_fec_set_msglevel,
+};
+
+
+static int mpc52xx_fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+	return mpc52xx_fec_phy_mii_ioctl(priv, if_mii(rq), cmd);
+}
+
+/* ======================================================================== */
+/* OF Driver                                                                */
+/* ======================================================================== */
+
+static int __devinit
+mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
+{
+	int rv;
+	struct net_device *ndev;
+	struct mpc52xx_fec_priv *priv = NULL;
+	struct resource mem;
+	const phandle *ph;
+
+	phys_addr_t rx_fifo;
+	phys_addr_t tx_fifo;
+
+	/* Get the ether ndev & it's private zone */
+	ndev = alloc_etherdev(sizeof(struct mpc52xx_fec_priv));
+	if (!ndev)
+		return -ENOMEM;
+
+	priv = netdev_priv(ndev);
+
+	/* Reserve FEC control zone */
+	rv = of_address_to_resource(op->node, 0, &mem);
+	if (rv) {
+		printk(KERN_ERR DRIVER_NAME ": "
+				"Error while parsing device node resource\n" );
+		return rv;
+	}
+	if ((mem.end - mem.start + 1) != sizeof(struct mpc52xx_fec)) {
+		printk(KERN_ERR DRIVER_NAME
+			" - invalid resource size (%lx != %x), check mpc52xx_devices.c\n",
+			(unsigned long)(mem.end - mem.start + 1), sizeof(struct mpc52xx_fec));
+		return -EINVAL;
+	}
+
+	if (!request_mem_region(mem.start, sizeof(struct mpc52xx_fec), DRIVER_NAME))
+		return -EBUSY;
+
+	/* Init ether ndev with what we have */
+	ndev->open		= mpc52xx_fec_open;
+	ndev->stop		= mpc52xx_fec_close;
+	ndev->hard_start_xmit	= mpc52xx_fec_hard_start_xmit;
+	ndev->do_ioctl		= mpc52xx_fec_ioctl;
+	ndev->ethtool_ops	= &mpc52xx_fec_ethtool_ops;
+	ndev->get_stats		= mpc52xx_fec_get_stats;
+	ndev->set_mac_address	= mpc52xx_fec_set_mac_address;
+	ndev->set_multicast_list = mpc52xx_fec_set_multicast_list;
+	ndev->tx_timeout	= mpc52xx_fec_tx_timeout;
+	ndev->watchdog_timeo	= FEC_WATCHDOG_TIMEOUT;
+	ndev->base_addr		= mem.start;
+
+	priv->t_irq = priv->r_irq = ndev->irq = NO_IRQ; /* IRQ are free for now */
+
+	spin_lock_init(&priv->lock);
+
+	/* ioremap the zones */
+	priv->fec = ioremap(mem.start, sizeof(struct mpc52xx_fec));
+
+	if (!priv->fec) {
+		rv = -ENOMEM;
+		goto probe_error;
+	}
+
+	/* Bestcomm init */
+	rx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, rfifo_data);
+	tx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, tfifo_data);
+
+	priv->rx_dmatsk = bcom_fec_rx_init(FEC_RX_NUM_BD, rx_fifo, FEC_RX_BUFFER_SIZE);
+	priv->tx_dmatsk = bcom_fec_tx_init(FEC_TX_NUM_BD, tx_fifo);
+
+	if (!priv->rx_dmatsk || !priv->tx_dmatsk) {
+		printk(KERN_ERR DRIVER_NAME ": Can not init SDMA tasks\n" );
+		rv = -ENOMEM;
+		goto probe_error;
+	}
+
+	/* Get the IRQ we need one by one */
+		/* Control */
+	ndev->irq = irq_of_parse_and_map(op->node, 0);
+
+		/* RX */
+	priv->r_irq = bcom_get_task_irq(priv->rx_dmatsk);
+
+		/* TX */
+	priv->t_irq = bcom_get_task_irq(priv->tx_dmatsk);
+
+	/* MAC address init */
+	if (!is_zero_ether_addr(mpc52xx_fec_mac_addr))
+		memcpy(ndev->dev_addr, mpc52xx_fec_mac_addr, 6);
+	else
+		mpc52xx_fec_get_paddr(ndev, ndev->dev_addr);
+
+	priv->msg_enable = netif_msg_init(debug, MPC52xx_MESSAGES_DEFAULT);
+	priv->duplex = DUPLEX_FULL;
+
+	/* is the phy present in device tree? */
+	ph = of_get_property(op->node, "phy-handle", NULL);
+	if (ph) {
+		const unsigned int *prop;
+		struct device_node *phy_dn;
+		priv->has_phy = 1;
+
+		phy_dn = of_find_node_by_phandle(*ph);
+		prop = of_get_property(phy_dn, "reg", NULL);
+		priv->phy_addr = *prop;
+
+		of_node_put(phy_dn);
+
+		/* Phy speed */
+		priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1;
+	} else {
+		dev_info(&ndev->dev, "can't find \"phy-handle\" in device"
+				" tree, using 7-wire mode\n");
+	}
+
+	/* Hardware init */
+	mpc52xx_fec_hw_init(ndev);
+
+	mpc52xx_fec_reset_stats(ndev);
+
+	/* Register the new network device */
+	rv = register_netdev(ndev);
+	if (rv < 0)
+		goto probe_error;
+
+	/* We're done ! */
+	dev_set_drvdata(&op->dev, ndev);
+
+	return 0;
+
+
+	/* Error handling - free everything that might be allocated */
+probe_error:
+
+	irq_dispose_mapping(ndev->irq);
+
+	if (priv->rx_dmatsk)
+		bcom_fec_rx_release(priv->rx_dmatsk);
+	if (priv->tx_dmatsk)
+		bcom_fec_tx_release(priv->tx_dmatsk);
+
+	if (priv->fec)
+		iounmap(priv->fec);
+
+	release_mem_region(mem.start, sizeof(struct mpc52xx_fec));
+
+	free_netdev(ndev);
+
+	return rv;
+}
+
+static int
+mpc52xx_fec_remove(struct of_device *op)
+{
+	struct net_device *ndev;
+	struct mpc52xx_fec_priv *priv;
+
+	ndev = dev_get_drvdata(&op->dev);
+	priv = netdev_priv(ndev);
+
+	unregister_netdev(ndev);
+
+	irq_dispose_mapping(ndev->irq);
+
+	bcom_fec_rx_release(priv->rx_dmatsk);
+	bcom_fec_tx_release(priv->tx_dmatsk);
+
+	iounmap(priv->fec);
+
+	release_mem_region(ndev->base_addr, sizeof(struct mpc52xx_fec));
+
+	free_netdev(ndev);
+
+	dev_set_drvdata(&op->dev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state)
+{
+	struct net_device *dev = dev_get_drvdata(&op->dev);
+
+	if (netif_running(dev))
+		mpc52xx_fec_close(dev);
+
+	return 0;
+}
+
+static int mpc52xx_fec_of_resume(struct of_device *op)
+{
+	struct net_device *dev = dev_get_drvdata(&op->dev);
+
+	mpc52xx_fec_hw_init(dev);
+	mpc52xx_fec_reset_stats(dev);
+
+	if (netif_running(dev))
+		mpc52xx_fec_open(dev);
+
+	return 0;
+}
+#endif
+
+static struct of_device_id mpc52xx_fec_match[] = {
+	{
+		.type		= "network",
+		.compatible	= "mpc5200-fec",
+	},
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, mpc52xx_fec_match);
+
+static struct of_platform_driver mpc52xx_fec_driver = {
+	.owner		= THIS_MODULE,
+	.name		= DRIVER_NAME,
+	.match_table	= mpc52xx_fec_match,
+	.probe		= mpc52xx_fec_probe,
+	.remove		= mpc52xx_fec_remove,
+#ifdef CONFIG_PM
+	.suspend	= mpc52xx_fec_of_suspend,
+	.resume		= mpc52xx_fec_of_resume,
+#endif
+};
+
+
+/* ======================================================================== */
+/* Module                                                                   */
+/* ======================================================================== */
+
+static int __init
+mpc52xx_fec_init(void)
+{
+#ifdef CONFIG_FEC_MPC52xx_MDIO
+	int ret;
+	ret = of_register_platform_driver(&mpc52xx_fec_mdio_driver);
+	if (ret) {
+		printk(KERN_ERR DRIVER_NAME ": failed to register mdio driver\n");
+		return ret;
+	}
+#endif
+	return of_register_platform_driver(&mpc52xx_fec_driver);
+}
+
+static void __exit
+mpc52xx_fec_exit(void)
+{
+	of_unregister_platform_driver(&mpc52xx_fec_driver);
+#ifdef CONFIG_FEC_MPC52xx_MDIO
+	of_unregister_platform_driver(&mpc52xx_fec_mdio_driver);
+#endif
+}
+
+
+module_init(mpc52xx_fec_init);
+module_exit(mpc52xx_fec_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dale Farnsworth");
+MODULE_DESCRIPTION("Ethernet driver for the Freescale MPC52xx FEC");
Index: linux.git/drivers/net/fec_mpc52xx.h
===================================================================
--- /dev/null
+++ linux.git/drivers/net/fec_mpc52xx.h
@@ -0,0 +1,313 @@
+/*
+ * drivers/drivers/net/fec_mpc52xx/fec.h
+ *
+ * Driver for the MPC5200 Fast Ethernet Controller
+ *
+ * Author: Dale Farnsworth <dfarnsworth@mvista.com>
+ *
+ * 2003-2004 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#ifndef __DRIVERS_NET_MPC52XX_FEC_H__
+#define __DRIVERS_NET_MPC52XX_FEC_H__
+
+#include <linux/phy.h>
+
+/* Tunable constant */
+/* FEC_RX_BUFFER_SIZE includes 4 bytes for CRC32 */
+#define FEC_RX_BUFFER_SIZE	1522	/* max receive packet size */
+#define FEC_RX_NUM_BD		256
+#define FEC_TX_NUM_BD		64
+
+#define FEC_RESET_DELAY		50 	/* uS */
+
+#define FEC_WATCHDOG_TIMEOUT	((400*HZ)/1000)
+
+struct mpc52xx_fec_priv {
+	int duplex;
+	int r_irq;
+	int t_irq;
+	struct mpc52xx_fec __iomem *fec;
+	struct bcom_task *rx_dmatsk;
+	struct bcom_task *tx_dmatsk;
+	spinlock_t lock;
+	int msg_enable;
+
+	int has_phy;
+	unsigned int phy_speed;
+	unsigned int phy_addr;
+	struct phy_device *phydev;
+	enum phy_state link;
+	int speed;
+};
+
+
+/* ======================================================================== */
+/* Hardware register sets & bits                                            */
+/* ======================================================================== */
+
+struct mpc52xx_fec {
+	u32 fec_id;			/* FEC + 0x000 */
+	u32 ievent;			/* FEC + 0x004 */
+	u32 imask;			/* FEC + 0x008 */
+
+	u32 reserved0[1];		/* FEC + 0x00C */
+	u32 r_des_active;		/* FEC + 0x010 */
+	u32 x_des_active;		/* FEC + 0x014 */
+	u32 r_des_active_cl;		/* FEC + 0x018 */
+	u32 x_des_active_cl;		/* FEC + 0x01C */
+	u32 ivent_set;			/* FEC + 0x020 */
+	u32 ecntrl;			/* FEC + 0x024 */
+
+	u32 reserved1[6];		/* FEC + 0x028-03C */
+	u32 mii_data;			/* FEC + 0x040 */
+	u32 mii_speed;			/* FEC + 0x044 */
+	u32 mii_status;			/* FEC + 0x048 */
+
+	u32 reserved2[5];		/* FEC + 0x04C-05C */
+	u32 mib_data;			/* FEC + 0x060 */
+	u32 mib_control;		/* FEC + 0x064 */
+
+	u32 reserved3[6];		/* FEC + 0x068-7C */
+	u32 r_activate;			/* FEC + 0x080 */
+	u32 r_cntrl;			/* FEC + 0x084 */
+	u32 r_hash;			/* FEC + 0x088 */
+	u32 r_data;			/* FEC + 0x08C */
+	u32 ar_done;			/* FEC + 0x090 */
+	u32 r_test;			/* FEC + 0x094 */
+	u32 r_mib;			/* FEC + 0x098 */
+	u32 r_da_low;			/* FEC + 0x09C */
+	u32 r_da_high;			/* FEC + 0x0A0 */
+
+	u32 reserved4[7];		/* FEC + 0x0A4-0BC */
+	u32 x_activate;			/* FEC + 0x0C0 */
+	u32 x_cntrl;			/* FEC + 0x0C4 */
+	u32 backoff;			/* FEC + 0x0C8 */
+	u32 x_data;			/* FEC + 0x0CC */
+	u32 x_status;			/* FEC + 0x0D0 */
+	u32 x_mib;			/* FEC + 0x0D4 */
+	u32 x_test;			/* FEC + 0x0D8 */
+	u32 fdxfc_da1;			/* FEC + 0x0DC */
+	u32 fdxfc_da2;			/* FEC + 0x0E0 */
+	u32 paddr1;			/* FEC + 0x0E4 */
+	u32 paddr2;			/* FEC + 0x0E8 */
+	u32 op_pause;			/* FEC + 0x0EC */
+
+	u32 reserved5[4];		/* FEC + 0x0F0-0FC */
+	u32 instr_reg;			/* FEC + 0x100 */
+	u32 context_reg;		/* FEC + 0x104 */
+	u32 test_cntrl;			/* FEC + 0x108 */
+	u32 acc_reg;			/* FEC + 0x10C */
+	u32 ones;			/* FEC + 0x110 */
+	u32 zeros;			/* FEC + 0x114 */
+	u32 iaddr1;			/* FEC + 0x118 */
+	u32 iaddr2;			/* FEC + 0x11C */
+	u32 gaddr1;			/* FEC + 0x120 */
+	u32 gaddr2;			/* FEC + 0x124 */
+	u32 random;			/* FEC + 0x128 */
+	u32 rand1;			/* FEC + 0x12C */
+	u32 tmp;			/* FEC + 0x130 */
+
+	u32 reserved6[3];		/* FEC + 0x134-13C */
+	u32 fifo_id;			/* FEC + 0x140 */
+	u32 x_wmrk;			/* FEC + 0x144 */
+	u32 fcntrl;			/* FEC + 0x148 */
+	u32 r_bound;			/* FEC + 0x14C */
+	u32 r_fstart;			/* FEC + 0x150 */
+	u32 r_count;			/* FEC + 0x154 */
+	u32 r_lag;			/* FEC + 0x158 */
+	u32 r_read;			/* FEC + 0x15C */
+	u32 r_write;			/* FEC + 0x160 */
+	u32 x_count;			/* FEC + 0x164 */
+	u32 x_lag;			/* FEC + 0x168 */
+	u32 x_retry;			/* FEC + 0x16C */
+	u32 x_write;			/* FEC + 0x170 */
+	u32 x_read;			/* FEC + 0x174 */
+
+	u32 reserved7[2];		/* FEC + 0x178-17C */
+	u32 fm_cntrl;			/* FEC + 0x180 */
+	u32 rfifo_data;			/* FEC + 0x184 */
+	u32 rfifo_status;		/* FEC + 0x188 */
+	u32 rfifo_cntrl;		/* FEC + 0x18C */
+	u32 rfifo_lrf_ptr;		/* FEC + 0x190 */
+	u32 rfifo_lwf_ptr;		/* FEC + 0x194 */
+	u32 rfifo_alarm;		/* FEC + 0x198 */
+	u32 rfifo_rdptr;		/* FEC + 0x19C */
+	u32 rfifo_wrptr;		/* FEC + 0x1A0 */
+	u32 tfifo_data;			/* FEC + 0x1A4 */
+	u32 tfifo_status;		/* FEC + 0x1A8 */
+	u32 tfifo_cntrl;		/* FEC + 0x1AC */
+	u32 tfifo_lrf_ptr;		/* FEC + 0x1B0 */
+	u32 tfifo_lwf_ptr;		/* FEC + 0x1B4 */
+	u32 tfifo_alarm;		/* FEC + 0x1B8 */
+	u32 tfifo_rdptr;		/* FEC + 0x1BC */
+	u32 tfifo_wrptr;		/* FEC + 0x1C0 */
+
+	u32 reset_cntrl;		/* FEC + 0x1C4 */
+	u32 xmit_fsm;			/* FEC + 0x1C8 */
+
+	u32 reserved8[3];		/* FEC + 0x1CC-1D4 */
+	u32 rdes_data0;			/* FEC + 0x1D8 */
+	u32 rdes_data1;			/* FEC + 0x1DC */
+	u32 r_length;			/* FEC + 0x1E0 */
+	u32 x_length;			/* FEC + 0x1E4 */
+	u32 x_addr;			/* FEC + 0x1E8 */
+	u32 cdes_data;			/* FEC + 0x1EC */
+	u32 status;			/* FEC + 0x1F0 */
+	u32 dma_control;		/* FEC + 0x1F4 */
+	u32 des_cmnd;			/* FEC + 0x1F8 */
+	u32 data;			/* FEC + 0x1FC */
+
+	u32 rmon_t_drop;		/* FEC + 0x200 */
+	u32 rmon_t_packets;		/* FEC + 0x204 */
+	u32 rmon_t_bc_pkt;		/* FEC + 0x208 */
+	u32 rmon_t_mc_pkt;		/* FEC + 0x20C */
+	u32 rmon_t_crc_align;		/* FEC + 0x210 */
+	u32 rmon_t_undersize;		/* FEC + 0x214 */
+	u32 rmon_t_oversize;		/* FEC + 0x218 */
+	u32 rmon_t_frag;		/* FEC + 0x21C */
+	u32 rmon_t_jab;			/* FEC + 0x220 */
+	u32 rmon_t_col;			/* FEC + 0x224 */
+	u32 rmon_t_p64;			/* FEC + 0x228 */
+	u32 rmon_t_p65to127;		/* FEC + 0x22C */
+	u32 rmon_t_p128to255;		/* FEC + 0x230 */
+	u32 rmon_t_p256to511;		/* FEC + 0x234 */
+	u32 rmon_t_p512to1023;		/* FEC + 0x238 */
+	u32 rmon_t_p1024to2047;		/* FEC + 0x23C */
+	u32 rmon_t_p_gte2048;		/* FEC + 0x240 */
+	u32 rmon_t_octets;		/* FEC + 0x244 */
+	u32 ieee_t_drop;		/* FEC + 0x248 */
+	u32 ieee_t_frame_ok;		/* FEC + 0x24C */
+	u32 ieee_t_1col;		/* FEC + 0x250 */
+	u32 ieee_t_mcol;		/* FEC + 0x254 */
+	u32 ieee_t_def;			/* FEC + 0x258 */
+	u32 ieee_t_lcol;		/* FEC + 0x25C */
+	u32 ieee_t_excol;		/* FEC + 0x260 */
+	u32 ieee_t_macerr;		/* FEC + 0x264 */
+	u32 ieee_t_cserr;		/* FEC + 0x268 */
+	u32 ieee_t_sqe;			/* FEC + 0x26C */
+	u32 t_fdxfc;			/* FEC + 0x270 */
+	u32 ieee_t_octets_ok;		/* FEC + 0x274 */
+
+	u32 reserved9[2];		/* FEC + 0x278-27C */
+	u32 rmon_r_drop;		/* FEC + 0x280 */
+	u32 rmon_r_packets;		/* FEC + 0x284 */
+	u32 rmon_r_bc_pkt;		/* FEC + 0x288 */
+	u32 rmon_r_mc_pkt;		/* FEC + 0x28C */
+	u32 rmon_r_crc_align;		/* FEC + 0x290 */
+	u32 rmon_r_undersize;		/* FEC + 0x294 */
+	u32 rmon_r_oversize;		/* FEC + 0x298 */
+	u32 rmon_r_frag;		/* FEC + 0x29C */
+	u32 rmon_r_jab;			/* FEC + 0x2A0 */
+
+	u32 rmon_r_resvd_0;		/* FEC + 0x2A4 */
+
+	u32 rmon_r_p64;			/* FEC + 0x2A8 */
+	u32 rmon_r_p65to127;		/* FEC + 0x2AC */
+	u32 rmon_r_p128to255;		/* FEC + 0x2B0 */
+	u32 rmon_r_p256to511;		/* FEC + 0x2B4 */
+	u32 rmon_r_p512to1023;		/* FEC + 0x2B8 */
+	u32 rmon_r_p1024to2047;		/* FEC + 0x2BC */
+	u32 rmon_r_p_gte2048;		/* FEC + 0x2C0 */
+	u32 rmon_r_octets;		/* FEC + 0x2C4 */
+	u32 ieee_r_drop;		/* FEC + 0x2C8 */
+	u32 ieee_r_frame_ok;		/* FEC + 0x2CC */
+	u32 ieee_r_crc;			/* FEC + 0x2D0 */
+	u32 ieee_r_align;		/* FEC + 0x2D4 */
+	u32 r_macerr;			/* FEC + 0x2D8 */
+	u32 r_fdxfc;			/* FEC + 0x2DC */
+	u32 ieee_r_octets_ok;		/* FEC + 0x2E0 */
+
+	u32 reserved10[7];		/* FEC + 0x2E4-2FC */
+
+	u32 reserved11[64];		/* FEC + 0x300-3FF */
+};
+
+#define	FEC_MIB_DISABLE			0x80000000
+
+#define	FEC_IEVENT_HBERR		0x80000000
+#define	FEC_IEVENT_BABR			0x40000000
+#define	FEC_IEVENT_BABT			0x20000000
+#define	FEC_IEVENT_GRA			0x10000000
+#define	FEC_IEVENT_TFINT		0x08000000
+#define	FEC_IEVENT_MII			0x00800000
+#define	FEC_IEVENT_LATE_COL		0x00200000
+#define	FEC_IEVENT_COL_RETRY_LIM	0x00100000
+#define	FEC_IEVENT_XFIFO_UN		0x00080000
+#define	FEC_IEVENT_XFIFO_ERROR		0x00040000
+#define	FEC_IEVENT_RFIFO_ERROR		0x00020000
+
+#define	FEC_IMASK_HBERR			0x80000000
+#define	FEC_IMASK_BABR			0x40000000
+#define	FEC_IMASK_BABT			0x20000000
+#define	FEC_IMASK_GRA			0x10000000
+#define	FEC_IMASK_MII			0x00800000
+#define	FEC_IMASK_LATE_COL		0x00200000
+#define	FEC_IMASK_COL_RETRY_LIM		0x00100000
+#define	FEC_IMASK_XFIFO_UN		0x00080000
+#define	FEC_IMASK_XFIFO_ERROR		0x00040000
+#define	FEC_IMASK_RFIFO_ERROR		0x00020000
+
+/* all but MII, which is enabled separately */
+#define FEC_IMASK_ENABLE	(FEC_IMASK_HBERR | FEC_IMASK_BABR | \
+		FEC_IMASK_BABT | FEC_IMASK_GRA | FEC_IMASK_LATE_COL | \
+		FEC_IMASK_COL_RETRY_LIM | FEC_IMASK_XFIFO_UN | \
+		FEC_IMASK_XFIFO_ERROR | FEC_IMASK_RFIFO_ERROR)
+
+#define	FEC_RCNTRL_MAX_FL_SHIFT		16
+#define	FEC_RCNTRL_LOOP			0x01
+#define	FEC_RCNTRL_DRT			0x02
+#define	FEC_RCNTRL_MII_MODE		0x04
+#define	FEC_RCNTRL_PROM			0x08
+#define	FEC_RCNTRL_BC_REJ		0x10
+#define	FEC_RCNTRL_FCE			0x20
+
+#define	FEC_TCNTRL_GTS			0x00000001
+#define	FEC_TCNTRL_HBC			0x00000002
+#define	FEC_TCNTRL_FDEN			0x00000004
+#define	FEC_TCNTRL_TFC_PAUSE		0x00000008
+#define	FEC_TCNTRL_RFC_PAUSE		0x00000010
+
+#define	FEC_ECNTRL_RESET		0x00000001
+#define	FEC_ECNTRL_ETHER_EN		0x00000002
+
+#define FEC_MII_DATA_ST			0x40000000	/* Start frame */
+#define FEC_MII_DATA_OP_RD		0x20000000	/* Perform read */
+#define FEC_MII_DATA_OP_WR		0x10000000	/* Perform write */
+#define FEC_MII_DATA_PA_MSK		0x0f800000	/* PHY Address mask */
+#define FEC_MII_DATA_RA_MSK		0x007c0000	/* PHY Register mask */
+#define FEC_MII_DATA_TA			0x00020000	/* Turnaround */
+#define FEC_MII_DATA_DATAMSK		0x0000ffff	/* PHY data mask */
+
+#define FEC_MII_READ_FRAME	(FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA)
+#define FEC_MII_WRITE_FRAME	(FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | FEC_MII_DATA_TA)
+
+#define FEC_MII_DATA_RA_SHIFT		0x12		/* MII reg addr bits */
+#define FEC_MII_DATA_PA_SHIFT		0x17		/* MII PHY addr bits */
+
+#define FEC_PADDR2_TYPE			0x8808
+
+#define FEC_OP_PAUSE_OPCODE		0x00010000
+
+#define FEC_FIFO_WMRK_256B		0x3
+
+#define FEC_FIFO_STATUS_ERR		0x00400000
+#define FEC_FIFO_STATUS_UF		0x00200000
+#define FEC_FIFO_STATUS_OF		0x00100000
+
+#define FEC_FIFO_CNTRL_FRAME		0x08000000
+#define FEC_FIFO_CNTRL_LTG_7		0x07000000
+
+#define FEC_RESET_CNTRL_RESET_FIFO	0x02000000
+#define FEC_RESET_CNTRL_ENABLE_IS_RESET	0x01000000
+
+#define FEC_XMIT_FSM_APPEND_CRC		0x02000000
+#define FEC_XMIT_FSM_ENABLE_CRC		0x01000000
+
+
+extern struct of_platform_driver mpc52xx_fec_mdio_driver;
+
+#endif	/* __DRIVERS_NET_MPC52XX_FEC_H__ */
Index: linux.git/drivers/net/fec_mpc52xx_phy.c
===================================================================
--- /dev/null
+++ linux.git/drivers/net/fec_mpc52xx_phy.c
@@ -0,0 +1,198 @@
+/*
+ * Driver for the MPC5200 Fast Ethernet Controller - MDIO bus driver
+ *
+ * Copyright (C) 2007  Domen Puncer, Telargo, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/of_platform.h>
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+#include "fec_mpc52xx.h"
+
+struct mpc52xx_fec_mdio_priv {
+	struct mpc52xx_fec __iomem *regs;
+};
+
+static int mpc52xx_fec_mdio_read(struct mii_bus *bus, int phy_id, int reg)
+{
+	struct mpc52xx_fec_mdio_priv *priv = bus->priv;
+	struct mpc52xx_fec __iomem *fec;
+	int tries = 100;
+	u32 request = FEC_MII_READ_FRAME;
+
+	fec = priv->regs;
+	out_be32(&fec->ievent, FEC_IEVENT_MII);
+
+	request |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK;
+	request |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK;
+
+	out_be32(&priv->regs->mii_data, request);
+
+	/* wait for it to finish, this takes about 23 us on lite5200b */
+	while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries)
+		udelay(5);
+
+	if (tries == 0)
+		return -ETIMEDOUT;
+
+	return in_be32(&priv->regs->mii_data) & FEC_MII_DATA_DATAMSK;
+}
+
+static int mpc52xx_fec_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data)
+{
+	struct mpc52xx_fec_mdio_priv *priv = bus->priv;
+	struct mpc52xx_fec __iomem *fec;
+	u32 value = data;
+	int tries = 100;
+
+	fec = priv->regs;
+	out_be32(&fec->ievent, FEC_IEVENT_MII);
+
+	value |= FEC_MII_WRITE_FRAME;
+	value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK;
+	value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK;
+
+	out_be32(&priv->regs->mii_data, value);
+
+	/* wait for request to finish */
+	while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries)
+		udelay(5);
+
+	if (tries == 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int mpc52xx_fec_mdio_probe(struct of_device *of, const struct of_device_id *match)
+{
+	struct device *dev = &of->dev;
+	struct device_node *np = of->node;
+	struct device_node *child = NULL;
+	struct mii_bus *bus;
+	struct mpc52xx_fec_mdio_priv *priv;
+	struct resource res = {};
+	int err;
+	int i;
+
+	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+	if (bus == NULL)
+		return -ENOMEM;
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL) {
+		err = -ENOMEM;
+		goto out_free;
+	}
+
+	bus->name = "mpc52xx MII bus";
+	bus->read = mpc52xx_fec_mdio_read;
+	bus->write = mpc52xx_fec_mdio_write;
+
+	/* setup irqs */
+	bus->irq = kmalloc(sizeof(bus->irq[0]) * PHY_MAX_ADDR, GFP_KERNEL);
+	if (bus->irq == NULL) {
+		err = -ENOMEM;
+		goto out_free;
+	}
+	for (i=0; i<PHY_MAX_ADDR; i++)
+		bus->irq[i] = PHY_POLL;
+
+	while ((child = of_get_next_child(np, child)) != NULL) {
+		int irq = irq_of_parse_and_map(child, 0);
+		if (irq != NO_IRQ) {
+			const u32 *id = of_get_property(child, "reg", NULL);
+			bus->irq[*id] = irq;
+		}
+	}
+
+	/* setup registers */
+	err = of_address_to_resource(np, 0, &res);
+	if (err)
+		goto out_free;
+	priv->regs = ioremap(res.start, res.end - res.start + 1);
+	if (priv->regs == NULL) {
+		err = -ENOMEM;
+		goto out_free;
+	}
+
+	bus->id = res.start;
+	bus->priv = priv;
+
+	bus->dev = dev;
+	dev_set_drvdata(dev, bus);
+
+	/* set MII speed */
+	out_be32(&priv->regs->mii_speed, ((mpc52xx_find_ipb_freq(of->node) >> 20) / 5) << 1);
+
+	/* enable MII interrupt */
+	out_be32(&priv->regs->imask, in_be32(&priv->regs->imask) | FEC_IMASK_MII);
+
+	err = mdiobus_register(bus);
+	if (err)
+		goto out_unmap;
+
+	return 0;
+
+ out_unmap:
+	iounmap(priv->regs);
+ out_free:
+	for (i=0; i<PHY_MAX_ADDR; i++)
+		if (bus->irq[i] != PHY_POLL)
+			irq_dispose_mapping(bus->irq[i]);
+	kfree(bus->irq);
+	kfree(priv);
+	kfree(bus);
+
+	return err;
+}
+
+static int mpc52xx_fec_mdio_remove(struct of_device *of)
+{
+	struct device *dev = &of->dev;
+	struct mii_bus *bus = dev_get_drvdata(dev);
+	struct mpc52xx_fec_mdio_priv *priv = bus->priv;
+	int i;
+
+	mdiobus_unregister(bus);
+	dev_set_drvdata(dev, NULL);
+
+	iounmap(priv->regs);
+	for (i=0; i<PHY_MAX_ADDR; i++)
+		if (bus->irq[i])
+			irq_dispose_mapping(bus->irq[i]);
+	kfree(priv);
+	kfree(bus->irq);
+	kfree(bus);
+
+	return 0;
+}
+
+
+static struct of_device_id mpc52xx_fec_mdio_match[] = {
+	{
+		.type = "mdio",
+		.compatible = "mpc5200b-fec-phy",
+	},
+	{},
+};
+
+struct of_platform_driver mpc52xx_fec_mdio_driver = {
+	.name = "mpc5200b-fec-phy",
+	.probe = mpc52xx_fec_mdio_probe,
+	.remove = mpc52xx_fec_mdio_remove,
+	.match_table = mpc52xx_fec_mdio_match,
+};
+
+/* let fec driver call it, since this has to be registered before it */
+EXPORT_SYMBOL_GPL(mpc52xx_fec_mdio_driver);
+
+
+MODULE_LICENSE("Dual BSD/GPL");
-- 
Domen Puncer | Research & Development
.............................................................................................
Telargo d.o.o. | Zagrebška cesta 20 | 2000 Maribor | Slovenia
.............................................................................................
www.telargo.com

^ permalink raw reply

* Re: 2.6.24-rc1 fails with lockup - /sbin/ifconfig / inet_ioctl() / dev_close() / rtl8169_down()
From: Stephen Hemminger @ 2007-10-26 16:48 UTC (permalink / raw)
  To: Ingo Molnar, Francois Romieu
  Cc: Romano Giannetti, Peter Zijlstra, Linux Kernel Mailing List,
	David S. Miller, netdev, Edward Hsu, Jeff Garzik, Andrew Morton
In-Reply-To: <20071026063733.GA12426@elte.hu>

On Fri, 26 Oct 2007 08:37:33 +0200
Ingo Molnar <mingo@elte.hu> wrote:

> 
> * Romano Giannetti <romanol@upcomillas.es> wrote:
> 
> > > Does this help?
> > 
> > I tried this, but although I have the D-state processes, I cannot see 
> > any debug trace now. Results are at:
> > 
> > http://www.dea.icai.upcomillas.es/romano/linux/info/2624rc1_3/
> > 
> > Can I try anything more? This is quite a show-stopper for me... and 
> > before trying to bisect 11Mbyte of patches...
> 
> hm, from your log it appears that lockdep did not find anything, still 
> the hang does trigger.
> 
> it's /sbin/ifconfig and inet_ioctl() / dev_close() / rtl8169_down() that 
> seems to be hanging. I've extracted the relevant backtrace below. I've 
> Cc:-ed people who might have a better idea about what's going on.
> 
> 	Ingo

Are you building with NAPI enabled or not. Looks like the following
might help the non-napi case.

--- a/drivers/net/r8169.c	2007-10-24 21:38:43.000000000 -0700
+++ b/drivers/net/r8169.c	2007-10-26 09:46:07.000000000 -0700
@@ -2989,13 +2989,16 @@ static void rtl8169_down(struct net_devi
 {
 	struct rtl8169_private *tp = netdev_priv(dev);
 	void __iomem *ioaddr = tp->mmio_addr;
-	unsigned int poll_locked = 0;
 	unsigned int intrmask;
 
 	rtl8169_delete_timer(dev);
 
 	netif_stop_queue(dev);
 
+#ifdef CONFIG_R8169_NAPI
+	napi_disable(&tp->napi);
+#endif
+
 core_down:
 	spin_lock_irq(&tp->lock);
 
@@ -3009,11 +3012,6 @@ core_down:
 
 	synchronize_irq(dev->irq);
 
-	if (!poll_locked) {
-		napi_disable(&tp->napi);
-		poll_locked++;
-	}
-
 	/* Give a racing hard_start_xmit a few cycles to complete. */
 	synchronize_sched();  /* FIXME: should this be synchronize_irq()? */
 




-- 
Stephen Hemminger <shemminger@linux-foundation.org>

^ permalink raw reply

* Re: Bad TCP checksum error
From: Rick Jones @ 2007-10-26 16:59 UTC (permalink / raw)
  To: Gaurav Aggarwal
  Cc: netfilter-devel, netdev, linux-net, linux-kernel, davidsen
In-Reply-To: <1a41e0840710260903w33516f62kc53e5eb56e424d69@mail.gmail.com>

Checksum Offload on the NIC(s) can complicate things.  First, if you are tracing 
on the sender, the tracepoint is before the NIC has computed the full checksum. 
  IIRC only a partial checksum is passed-down to the NIC when CKO is in use.

So, making certain your trace is from the "wire" or the receiver rather than the 
sender would be a good thing, and trying again with CKO disabled on the 
interface(s) (via ethtool) might be something worth looking at.  Ultimately, 
doing the partial checksum modificiations in a CKO-friendly manner might be a 
good thing.

rick jones

^ 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