Netdev List
 help / color / mirror / Atom feed
* Re: [Bugme-new] [Bug 9224] New: Settings to /proc/sys/net/ipv[46]/conf/all are not propagated
From: Andrew Morton @ 2007-10-25 16:16 UTC (permalink / raw)
  To: netdev; +Cc: bugme-daemon, svdb+linux-kernel-bugs
In-Reply-To: <bug-9224-10286@http.bugzilla.kernel.org/>

On Thu, 25 Oct 2007 04:44:06 -0700 (PDT) bugme-daemon@bugzilla.kernel.org wrote:

> http://bugzilla.kernel.org/show_bug.cgi?id=9224
> 
>            Summary: Settings to /proc/sys/net/ipv[46]/conf/all are not
>                     propagated
>            Product: Networking
>            Version: 2.5
>      KernelVersion: 2.6.24-rc1
>           Platform: All
>         OS/Version: Linux
>               Tree: Mainline
>             Status: NEW
>           Severity: normal
>           Priority: P1
>          Component: Other
>         AssignedTo: acme@ghostprotocols.net
>         ReportedBy: svdb+linux-kernel-bugs@stack.nl
> 
> 
> Most recent kernel where this bug did not occur: 2.6.22 (not sure)
> Distribution: custom
> Hardware Environment: See attached files
> Software Environment: See attached files.
> Problem Description: Changing a setting in /proc/sys/net/ipv[46]/conf/all does
> not propagate the setting to the individual network interfaces, for most
> settings.
> 
> Steps to reproduce:
> # cat /proc/sys/net/ipv6/conf/{all,default,lo,eth0}/autoconf
> 1
> 1
> 1
> 1
> # echo 0 > /proc/sys/net/ipv6/conf/all/autoconf
> # echo 0 > /proc/sys/net/ipv6/conf/default/autoconf
> # cat /proc/sys/net/ipv6/conf/{all,default,lo,eth0}/autoconf
> 0
> 0
> 1
> 1
> 
> The problem occurs for other settings than "autoconf", but not for all.
> "forwarding" is one which does work.
> 
> Changing settings for individual interfaces still works.
> 
> The problem is not specific to ipv6.
> 

Apparently a regression.

^ permalink raw reply

* Re: on the topic of alternate MAC addresses
From: Eric W. Biederman @ 2007-10-25 16:14 UTC (permalink / raw)
  To: David Miller; +Cc: jeff, netdev, Patrick McHardy
In-Reply-To: <20071023.202210.74747025.davem@davemloft.net>

David Miller <davem@davemloft.net> writes:

> From: Jeff Garzik <jeff@garzik.org>
> Date: Tue, 23 Oct 2007 22:25:05 -0400
>
>> hmmmm.  Using ethtool isn't a big deal, but IMO you probably want more 
>> than just an exported list for the usage you described...  it sounds 
>> like some sort of reservation system should be used, to note which MAC 
>> addresses are [not] in use?
>> 
>> Then a virt client -- or anyone who wants multiple unicast addresses for 
>> whatever reason -- can let other clients to avoid MAC addresses 1, 7, or 
>> 13 (random examples).
>
> I see your point.
>
> However, it's not the virt clients that do this, it's the control
> node (aka: domain 0) which has to manage these things.
>
> It has to manage all of the global hardware resources and allocate
> them out to itself and the clients anyways.
>
> And this is why I think it's sufficient to just publish the list of
> MAC addresses from the driver, and leave the allocation and policy
> to the userland virtualizatin daemon running on the control node.
>
> Let me know if you still disagree.

On the per interface level we know the set of used mac addresses in
the uc_list.

At least at a per interface level we should already have this
information, and this is where we care because duplicates at
that could cause problems.

Duplicate mac addresses across interfaces on the same machine
should generally be a don't care.  Although there may some
cases we don't mind.

Currently drivers like macvlan that care today call
random_ether_addr().  I think it would make sense to convenience
kernel function that picked the next available unicast address
for an interface that was not on the uc_list, (called
random_ether_adder if there was not such address) and called
dev_unicast_add to let the driver receive it and to keep other people
from using it.

Exporting that information with ethtool to handle the strange cases
makes sense.  But it looks like there are easy cases that don't
need that help.

Eric

^ permalink raw reply

* Re: [IFGROUPv4 2/3] Interface group: core (netlink) part
From: Patrick McHardy @ 2007-10-25 15:40 UTC (permalink / raw)
  To: panther; +Cc: David Miller, netdev
In-Reply-To: <4720B6D4.8080404@balabit.hu>

Laszlo Attila Toth wrote:
> Patrick McHardy írta:
>> Laszlo Attila Toth wrote:
>>> Interface groups let handle different interfaces together
>>> especially in netfilter modules.
>>> Modified net device structure and netlink interface.
>>>
>>> @@ -891,6 +895,13 @@ static int do_setlink(struct net_device *dev, 
>>> struct ifinfomsg *ifm,
>>>          }
>>>      }
>>>  
>>> +    if (tb[IFLA_IFGROUP]) {
>>> +        write_lock_bh(&dev_base_lock);
>>> +        dev->ifgroup = nla_get_u32(tb[IFLA_IFGROUP]);
>>> +        write_unlock_bh(&dev_base_lock);
>>> +        modified = 1;
>>> +    }
>>
>>
>> The locking looks unnecessary, the rtnl should be enough.
>> I'm not even sure why its used for operstate and linkmode,
>> AFAICS they are also protected by the rtnl.
>>
> 
> Hm, ok. In this case operstate and linkmode can be unprotected as code 
> cleanup, am I right? Or leave them unchanged?


There seems to be a single case where operstate is used without
the rtnl (under dev_base_lock), in dev_get_flags() invoked by
dev_ifsioc_locked(). But that looks like a bug, there are many
callers of dev_change_flags() that only take the rtnl. So this
would be more a fix than a cleanup.

> And notification is only needed if something was changed.

Yes.

^ permalink raw reply

* Fried r8169.
From: Tom Prince @ 2007-10-25 15:44 UTC (permalink / raw)
  To: Francois Romieu, netdev

On Wed, Oct 24, 2007 at 10:11:23PM -0600, Tom Prince wrote:
While testing Linux v2.6.24-rc1, I noticed that my onboard r8169 had
disappeared, and the it remained gone when I rebooted to v2.6.23 and v2.6.22,
where it had previously worked.
 
I have no idea where to begin debugging (or even reproducing) this, but am
quite willing to provide whatever additional details are requested.

I am running x86_64 ASUS Z92T laptop (an OEM version of the A6T).

I have posted dmesg/lspci/config from v2.6.23 (and soon v2.6.24) at 
 www.math.uwo.ca/~rprince5/r8169/

Also these appear to be the relevant log messages from when the device was
working. Unfortunately, I didn't have the foresight to save dmesg/lspci from
before the device disappeared.

kernel: r8169 Gigabit Ethernet driver 2.2LK-NAPI loaded
kernel: ACPI: PCI Interrupt Link [LNEA] enabled at IRQ 16
kernel: ACPI: PCI Interrupt 0000:01:00.0[A] -> Link [LNEA] -> GSI 16 (level, low) -> IRQ 16
kernel: PCI: Setting latency timer of device 0000:01:00.0 to 64
kernel: eth1: RTL8168b/8111b at 0xffffc200020e2000, 00:18:f3:87:0b:bb, XID 38000000 IRQ 16

  Tom

[PS Please CC me.]


^ permalink raw reply

* Re: [IFGROUPv4 2/3] Interface group: core (netlink) part
From: Laszlo Attila Toth @ 2007-10-25 15:31 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: David Miller, netdev
In-Reply-To: <4720B2E6.1060505@trash.net>

Patrick McHardy írta:
> Laszlo Attila Toth wrote:
>> Interface groups let handle different interfaces together
>> especially in netfilter modules.
>> Modified net device structure and netlink interface.
>>
>> @@ -891,6 +895,13 @@ static int do_setlink(struct net_device *dev, 
>> struct ifinfomsg *ifm,
>>          }
>>      }
>>  
>> +    if (tb[IFLA_IFGROUP]) {
>> +        write_lock_bh(&dev_base_lock);
>> +        dev->ifgroup = nla_get_u32(tb[IFLA_IFGROUP]);
>> +        write_unlock_bh(&dev_base_lock);
>> +        modified = 1;
>> +    }
> 
> 
> The locking looks unnecessary, the rtnl should be enough.
> I'm not even sure why its used for operstate and linkmode,
> AFAICS they are also protected by the rtnl.
> 

Hm, ok. In this case operstate and linkmode can be unprotected as code 
cleanup, am I right? Or leave them unchanged?
And notification is only needed if something was changed.

--
Attila

^ permalink raw reply

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

Laszlo Attila Toth wrote:
> +++ extensions/libxt_ifgroup.c	(revision 0)
> @@ -0,0 +1,196 @@
> +/* 
> + * 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, 0, '1'},
> +	{"ifgroup-out", 1, 0, '2'},

The third member is a pointer, please use NULL.

> +	{ }
> +};
> +
> +#define PARAM_MATCH_IN	0x01
> +#define PARAM_MATCH_OUT	0x02
> +
> +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)
> +	{

This goes on the same line as the switch statement please.

> +		case '1':

And please no extra indentation for the case labels.

> +			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 = 0xffffffffUL;

in_mask is not an unsigned long but an unsigned int.

> +			
> +			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 = 0xffffffffUL;
> +			
> +			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/0x%x ", info->in_group, info->in_mask);
> +}
> +
> +static void
> +ifgroup_print_value_out(struct xt_ifgroup_info *info)
> +{
> +	printf("0x%x/0x%x ", info->out_group, info->out_mask);
> +}

Just a suggestion: not printing the mask when its ~0 would
improve readability.

> +
> +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("--ifgroup-in %s",
> +		       info->flags & XT_IFGROUP_INVERT_IN ? "! " : "");
> +		ifgroup_print_value_in(info);
> +	}
> +	if (info->flags & XT_IFGROUP_MATCH_OUT) {
> +		printf("--ifgroup-out %s",
> +		       info->flags & XT_IFGROUP_INVERT_OUT ? "! " : "");
> +		ifgroup_print_value_out(info);
> +	}
> +}

^ permalink raw reply

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

Laszlo Attila Toth wrote:
> +++ b/net/netfilter/xt_ifgroup.c
> +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))


If you'd remove the extra space it would fit on a 80 character line.

^ permalink raw reply

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

Laszlo Attila Toth wrote:
> Interface groups let handle different interfaces together
> especially in netfilter modules.
> Modified net device structure and netlink interface.
> 
> @@ -891,6 +895,13 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
>  		}
>  	}
>  
> +	if (tb[IFLA_IFGROUP]) {
> +		write_lock_bh(&dev_base_lock);
> +		dev->ifgroup = nla_get_u32(tb[IFLA_IFGROUP]);
> +		write_unlock_bh(&dev_base_lock);
> +		modified = 1;
> +	}


The locking looks unnecessary, the rtnl should be enough.
I'm not even sure why its used for operstate and linkmode,
AFAICS they are also protected by the rtnl.

^ permalink raw reply

* Re: D-Link DFE-580TX and snmpd problems, who's maintainer of sundance.c ?
From: Volker Sauer @ 2007-10-25 15:13 UTC (permalink / raw)
  To: Francois Romieu; +Cc: linux-kernel, adi, Jesse Huang, netdev
In-Reply-To: <20071024214650.GA6253@electric-eye.fr.zoreil.com>

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

On Mi, 24 Okt 2007, Francois Romieu <romieu@fr.zoreil.com> wrote:
> > So the questions is: why? What is this good for?
> > And: can I remove it? I can not abandon snmpd and the messages spam my
> > syslog.
> > Or: do I have to patch snmpd?
> 
> Please try the attached patch.
> 

Hi Francois,

the patch solves the problem. Thank you.

Regards
Volker

-- 
  Volker Sauer  *  Poststrasse 1/601   *   64293 Darmstadt  *   Germany
  E-Mail/Jabber: volker(at)volker-sauer.de * http://www.volker-sauer.de
  PGPKey-Fingerprint: DB26 11C7 B12E 0B27 3999 2E4F 7E35 4E4D 5DD5 D0E0
  http://wwwkeys.de.pgp.net/pks/lookup?op=get&search=0x7E354E4D5DD5D0E0 

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* Re: [NETNS] Oops in register_pernet_operations() with CONFIG_NET_NS=n
From: Benjamin Thery @ 2007-10-25 15:10 UTC (permalink / raw)
  To: Eric W. Biederman
  Cc: Denis V. Lunev, Pavel Emelianov, David Miller, Linux Netdev List,
	Cedric Le Goater, Linux Containers, Daniel Lezcano
In-Reply-To: <m17ilb6xs9.fsf@ebiederm.dsl.xmission.com>

Eric W. Biederman wrote:
> Benjamin Thery <benjamin.thery@bull.net> writes:
> 
>> Denis V. Lunev wrote:
>>> The patch attached should help. The idea is simple. The "init" should be
>>> called only once without NETNS. Period. No need for any lists.
>> This is the kind of idea I had but I didn't think it could be 
>> that simple. :) 
>> Thanks Denis.
> 
> It isn't.
> 
>>> I'll resend it to Dave after the ACK.
>> Tested on x86_64 with CONFIG_NET_NS=n and y. 
>> It fixes the issue we observed.
>>
>> Acked-by: Benjamin Thery <benjamin.thery@bull.net>
> 
> Try rmmod.

rmmod was part of my tests and it does work.
I did:

$ iptables --list

  modules x_tables, ip_tables & iptable_filter are loaded
  each calling register_pernet_subsys.

$ rmmod iptable_filter ip_tables x_tables

  No problem here

$ iptables --list

  To be sure I can load the modules again.


> 
> Eric
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


-- 
B e n j a m i n   T h e r y  - BULL/DT/Open Software R&D

   http://www.bull.com

^ permalink raw reply

* Re: [NETNS] Oops in register_pernet_operations() with CONFIG_NET_NS=n
From: Eric W. Biederman @ 2007-10-25 15:04 UTC (permalink / raw)
  To: Benjamin Thery
  Cc: Denis V. Lunev, Pavel Emelianov, Eric W. Biederman, David Miller,
	Linux Netdev List, Cedric Le Goater, Linux Containers,
	Daniel Lezcano
In-Reply-To: <4720AD20.5030902@bull.net>

Benjamin Thery <benjamin.thery@bull.net> writes:

> Denis V. Lunev wrote:
>> The patch attached should help. The idea is simple. The "init" should be
>> called only once without NETNS. Period. No need for any lists.
>
> This is the kind of idea I had but I didn't think it could be 
> that simple. :) 
> Thanks Denis.

It isn't.

>> I'll resend it to Dave after the ACK.
>
> Tested on x86_64 with CONFIG_NET_NS=n and y. 
> It fixes the issue we observed.
>
> Acked-by: Benjamin Thery <benjamin.thery@bull.net>

Try rmmod.

Eric

^ permalink raw reply

* Re: [NETNS] Oops in register_pernet_operations() with CONFIG_NET_NS=n
From: Eric W. Biederman @ 2007-10-25 15:03 UTC (permalink / raw)
  To: Denis V. Lunev
  Cc: Benjamin Thery, Pavel Emelianov, David Miller, Linux Netdev List,
	Cedric Le Goater, Linux Containers, Daniel Lezcano
In-Reply-To: <4720A181.3060003@sw.ru>

"Denis V. Lunev" <den@sw.ru> writes:

> The patch attached should help. The idea is simple. The "init" should be
> called only once without NETNS. Period. No need for any lists.
>
> I'll resend it to Dave after the ACK.

First in the case of the code that is currently merged none of 
the __net_init __net_exit or __net_initdata can be modular, so for
2.6.24 there is no fix needed. Yeah.

Second the whole concept of concept pernet_operations being __init
doesn't work when you have modular code that calls unregister_pernet_subsys().
Because unregister calls the exit method from the pernet_operations
structure.  So the patch doesn't even begin to address the real
issue.

Third from my perspective CONFIG_NET_NS is a temporary measure
designed to last only until we have enough implementation experience 
so that we can feel comfortable removing the experimental status of
the network namespace work.  It was not my intention for it to be a
space saving measure.  So I think it is silly to go marking up the
patches in development with __net_initdata etc.

At least so far I think __net_initdata is a totally bogus concept
and I'm not certain about the other two. 

Eric

^ permalink raw reply

* [IFGROUPv4 iptables] Interface group match
From: Laszlo Attila Toth @ 2007-10-25 15:03 UTC (permalink / raw)
  To: David Miller, netdev; +Cc: Laszlo Attila Toth
In-Reply-To: <11933245931626-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              |  196 +++++++++++++++++++++++++++++++++++++++++++
 linux/netfilter/xt_ifgroup.h |   18 +++
 5 files changed, 287 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,196 @@
+/* 
+ * 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, 0, '1'},
+	{"ifgroup-out", 1, 0, '2'},
+	{ }
+};
+
+#define PARAM_MATCH_IN	0x01
+#define PARAM_MATCH_OUT	0x02
+
+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 = 0xffffffffUL;
+			
+			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 = 0xffffffffUL;
+			
+			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/0x%x ", info->in_group, info->in_mask);
+}
+
+static void
+ifgroup_print_value_out(struct xt_ifgroup_info *info)
+{
+	printf("0x%x/0x%x ", info->out_group, info->out_mask);
+}
+
+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("--ifgroup-in %s",
+		       info->flags & XT_IFGROUP_INVERT_IN ? "! " : "");
+		ifgroup_print_value_in(info);
+	}
+	if (info->flags & XT_IFGROUP_MATCH_OUT) {
+		printf("--ifgroup-out %s",
+		       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

* [IFGROUPv4 iproute 1/2] Added IFLA_NET_NS_PID as in kernel v2.6.24-rc1
From: Laszlo Attila Toth @ 2007-10-25 15:03 UTC (permalink / raw)
  To: David Miller, netdev; +Cc: Laszlo Attila Toth
In-Reply-To: <11933245933190-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

* [IFGROUPv4 3/3] Netfilter Interface group match
From: Laszlo Attila Toth @ 2007-10-25 15:03 UTC (permalink / raw)
  To: David Miller, netdev; +Cc: Laszlo Attila Toth
In-Reply-To: <11933245921874-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           |  121 ++++++++++++++++++++++++++++++++++
 4 files changed, 156 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..2baf772
--- /dev/null
+++ b/net/netfilter/xt_ifgroup.c
@@ -0,0 +1,121 @@
+/*
+ * 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

* [IFGROUPv4 2/3] Interface group: core (netlink) part
From: Laszlo Attila Toth @ 2007-10-25 15:03 UTC (permalink / raw)
  To: David Miller, netdev; +Cc: Laszlo Attila Toth
In-Reply-To: <11933245922165-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 c4de536..87aa550 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 ecf4dc5..f13883d 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -611,6 +611,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 */
@@ -648,6 +649,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);
 
@@ -891,6 +895,13 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
 		}
 	}
 
+	if (tb[IFLA_IFGROUP]) {
+		write_lock_bh(&dev_base_lock);
+		dev->ifgroup = nla_get_u32(tb[IFLA_IFGROUP]);
+		write_unlock_bh(&dev_base_lock);
+		modified = 1;
+	}
+
 	err = 0;
 
 errout:
-- 
1.5.2.5


^ permalink raw reply related

* [IFGROUPv4 iproute 2/2] Interface group as new ip link option
From: Laszlo Attila Toth @ 2007-10-25 15:03 UTC (permalink / raw)
  To: David Miller, netdev; +Cc: Laszlo Attila Toth
In-Reply-To: <11933245933597-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

* [IFGROUPv4 1/3] rtnetlink: setlink changes atomic with single notification
From: Laszlo Attila Toth @ 2007-10-25 15:03 UTC (permalink / raw)
  To: David Miller, netdev; +Cc: Laszlo Attila Toth
In-Reply-To: <11933245923082-git-send-email-panther@balabit.hu>

In do_setlink the device changes are atomic and notifications will be sent
at the end of the function once if any modification occured and once if
address has been changed.

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

diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 4a2640d..ecf4dc5 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;
 
@@ -560,8 +560,9 @@ static void set_operstate(struct net_device *dev, unsigned char transition)
 		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 +856,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 +869,26 @@ 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])) {
+			write_lock_bh(&dev_base_lock);
+			dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
+			write_unlock_bh(&dev_base_lock);
+			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])) {
+			write_lock_bh(&dev_base_lock);
+			dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);
+			write_unlock_bh(&dev_base_lock);
+			modified = 1;
+		}
 	}
 
 	err = 0;
@@ -890,6 +902,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

* [IFGROUPv4 0/3 (+3)] Interface group patches
From: Laszlo Attila Toth @ 2007-10-25 15:03 UTC (permalink / raw)
  To: David Miller, netdev; +Cc: Laszlo Attila Toth

Hi Dave,

This is the 4th version of our interface group patches. The only difference
from the previous one is in the first patch (single notification and atomic
changes in rtnetlink) plus in iptables code: parameter in-ifgroup changed to
ifgroup-in (and ifgroup-out).

I had to add IFLA_NET_NS_PID in the iproute2 patches as in kernel v2.6.24-rc1.

Usage as before:
 ip link set eth0 group 4
 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

Please apply,
Attila

^ permalink raw reply

* Re: [NETNS] Oops in register_pernet_operations() with CONFIG_NET_NS=n
From: Benjamin Thery @ 2007-10-25 14:50 UTC (permalink / raw)
  To: Denis V. Lunev
  Cc: Pavel Emelianov, Eric W. Biederman, David Miller,
	Linux Netdev List, Cedric Le Goater, Linux Containers,
	Daniel Lezcano
In-Reply-To: <4720A181.3060003@sw.ru>

Denis V. Lunev wrote:
> The patch attached should help. The idea is simple. The "init" should be
> called only once without NETNS. Period. No need for any lists.

This is the kind of idea I had but I didn't think it could be 
that simple. :) 
Thanks Denis.

> I'll resend it to Dave after the ACK.

Tested on x86_64 with CONFIG_NET_NS=n and y. 
It fixes the issue we observed.

Acked-by: Benjamin Thery <benjamin.thery@bull.net>


> Regards,
> 	Den
> 
> Benjamin Thery wrote:
>> Hello Pavel,
>>
>> I've found a problem with one of your patch related to netns:
>>
>> * [NETNS] Move some code into __init section when CONFIG_NET_NS=n (v2)
>>    http://www.spinics.net/lists/netdev/msg43310.html
>>
>> This patch introduces the __net_init/__net_exit/__net_initdata
>> defines to save some memory when CONFIG_NET_NS is not set.
>>
>> Cedric Le Goater reported he had a *non-fatal* oops when booting
>> a 2.6.23-mm1-lxc1 kernel with CONFIG_NET_NS=n. (2.6.23-mm1-lxc1 
>> contains the NETNS49 patchset). The oops occured when modules 
>> related to iptables were loaded after the boot completes.
>>
>> The problem is the following:
>>
>> - Your patch adds the __net_initdata attribute to pernet_operations 
>>   structures.
>>
>> - pernet_operations are registered via register_pernet_subsys() and 
>>   linked in the pernet_list during boot.
>>
>> - At the end of boot, pernet_operations are freed (because of the
>>   __net_initdata attribute), and the pernet_list (or first_device list) 
>>   points to freed memory.
>>
>> - After boot, network modules which are netns-aware try to register
>>   themselves with register_pernet_subsys() and ...KABOOM... page
>>   fault when accessing pernet_list (or first_device list).
>>   (I reproduce Cedric's oops with the command: iptables --list)
>>
>> This is not a problem right now in 2.6.23-mm1 or net-2.6 because 
>> there are very few netns-aware network subsystems merged and they
>> are all initialized during boot. But it will be problematic when 
>> we will merge netns code for subsystems which can be built as 
>> modules (eg. iptables, ...). I'm not sure we can use 
>> __net_init_data for pernet_operations then. 
>> Maybe we can add some checks in register_pernet_operations
>> when CONFIG_NET_NS=n.
>>
>> I haven't found a fix yet.
>>
>> Regards,
>> Benjamin
>>
> 


-- 
B e n j a m i n   T h e r y  - BULL/DT/Open Software R&D

   http://www.bull.com

^ permalink raw reply

* BUG in sys_socketpair
From: Rich Paul @ 2007-10-25 14:11 UTC (permalink / raw)
  To: netdev, linux-kernel

In 2.6.23, there seems to be a minor bug in sys_socketpair.  When the
calls to sock_alloc_fd fail, it aborts the routine, but it returns the
variable err, which is not set in this case.

The result is a silent failure if you have too many files open and call
socketpair.

Here is a simple UNTESTED patch (not even compiled) which should resolve the
issue.

Regards,
Rich

--- net/socket.c.orig   2007-10-25 10:03:56.000000000 -0400
+++ net/socket.c        2007-10-25 10:04:00.000000000 -0400
@@ -1245,11 +1245,14 @@
                goto out_release_both;

        fd1 = sock_alloc_fd(&newfile1);
-       if (unlikely(fd1 < 0))
+       if (unlikely(fd1 < 0)) {
+               err=fd1;
                goto out_release_both;
+       }

        fd2 = sock_alloc_fd(&newfile2);
        if (unlikely(fd2 < 0)) {
+               err=fd2;
                put_filp(newfile1);
                put_unused_fd(fd1);
                goto out_release_both;


^ permalink raw reply

* Re: [Devel] Re: [NETNS] Oops in register_pernet_operations() with CONFIG_NET_NS=n (resend, was wrong patch)
From: Denis V. Lunev @ 2007-10-25 14:14 UTC (permalink / raw)
  To: Benjamin Thery
  Cc: Linux Netdev List, Cedric Le Goater, Linux Containers,
	David Miller
In-Reply-To: <4720A181.3060003@sw.ru>

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

Denis V. Lunev wrote:
> The patch attached should help. The idea is simple. The "init" should be
> called only once without NETNS. Period. No need for any lists.
> 
> I'll resend it to Dave after the ACK.
> 
> Regards,
> 	Den
> 
> Benjamin Thery wrote:
>> Hello Pavel,
>>
>> I've found a problem with one of your patch related to netns:
>>
>> * [NETNS] Move some code into __init section when CONFIG_NET_NS=n (v2)
>>    http://www.spinics.net/lists/netdev/msg43310.html
>>
>> This patch introduces the __net_init/__net_exit/__net_initdata
>> defines to save some memory when CONFIG_NET_NS is not set.
>>
>> Cedric Le Goater reported he had a *non-fatal* oops when booting
>> a 2.6.23-mm1-lxc1 kernel with CONFIG_NET_NS=n. (2.6.23-mm1-lxc1 
>> contains the NETNS49 patchset). The oops occured when modules 
>> related to iptables were loaded after the boot completes.
>>
>> The problem is the following:
>>
>> - Your patch adds the __net_initdata attribute to pernet_operations 
>>   structures.
>>
>> - pernet_operations are registered via register_pernet_subsys() and 
>>   linked in the pernet_list during boot.
>>
>> - At the end of boot, pernet_operations are freed (because of the
>>   __net_initdata attribute), and the pernet_list (or first_device list) 
>>   points to freed memory.
>>
>> - After boot, network modules which are netns-aware try to register
>>   themselves with register_pernet_subsys() and ...KABOOM... page
>>   fault when accessing pernet_list (or first_device list).
>>   (I reproduce Cedric's oops with the command: iptables --list)
>>
>> This is not a problem right now in 2.6.23-mm1 or net-2.6 because 
>> there are very few netns-aware network subsystems merged and they
>> are all initialized during boot. But it will be problematic when 
>> we will merge netns code for subsystems which can be built as 
>> modules (eg. iptables, ...). I'm not sure we can use 
>> __net_init_data for pernet_operations then. 
>> Maybe we can add some checks in register_pernet_operations
>> when CONFIG_NET_NS=n.
>>
>> I haven't found a fix yet.
>>
>> Regards,
>> Benjamin


[-- Attachment #2: diff-no-netns --]
[-- Type: text/plain, Size: 440 bytes --]

diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 6f71db8..cc306dc 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -186,7 +186,11 @@ static int register_pernet_operations(struct list_head *list,
 	int error;
 
 	error = 0;
+#ifdef CONFIG_NET_NS
 	list_add_tail(&ops->list, list);
+#else
+	INIT_LIST_HEAD(&ops->list);
+#endif
 	for_each_net(net) {
 		if (ops->init) {
 			error = ops->init(net);

^ permalink raw reply related

* Re: [PATCH v4] FEC - fast ethernet controller for mpc52xx
From: Domen Puncer @ 2007-10-25 14:10 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Grant Likely, galak, linuxppc-dev, tnt, netdev
In-Reply-To: <47206209.8080109@pobox.com>

On 25/10/07 05:29 -0400, Jeff Garzik wrote:
> Domen Puncer wrote:
> >+static int mpc52xx_fec_alloc_rx_buffers(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 = virt_to_phys(skb->data);
> >+
> >+		bcom_submit_next_buffer(rxtsk, skb);
> 
> use your platform's dma mapping functions, rather than virt_to_phys()
> 
> it might be the exact same implementation, inside the platform 
> internals, but drivers should not be using this directly.

I've replaced this with dma_map_single(), unmatched with
dma_unmap_single(), since bestcomm doesn't have a way to do that
and it's blank on ppc32 anyway.

Is this OK? PPC guys?

> 
> >+{
> >+	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> >+	int err = -EBUSY;
> >+
> >+	if (request_irq(dev->irq, &mpc52xx_fec_interrupt, IRQF_DISABLED | 
> >IRQF_SHARED,
> 
> why IRQF_DISABLED?  that should not be needed.

Removed all three occurances.

> >+	bd->status = skb->len | BCOM_FEC_TX_BD_TFD | BCOM_FEC_TX_BD_TC;
> >+	bd->skb_pa = virt_to_phys(skb->data);
> 
> use dma_xxx

dma_map_single

> 
> >+	bcom_submit_next_buffer(priv->tx_dmatsk, skb);
> >+
> >+	if (bcom_queue_full(priv->tx_dmatsk)) {
> >+		priv->tx_full = 1;
> 
> no need for your own tx_full variable

I'm not sure what I should do here.
It does it internally, so just removing tx_full is OK?


> >+		dev_kfree_skb_irq(skb);
> >+	}
> >+
> >+	if (netif_queue_stopped(dev) && !priv->tx_full)
> 
> no need to test netif_queue_stopped(), netif_wake_queue() does that anyway
> 
> no need for tx_full

OK.

> >+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);
> 
> generally wise to check for 0xffffffff, which often indicates hardware 
> fault / device not there / scrogged

It's on the CPU, so it has to be there :-)
dev_warn and fec_reset would catch that anyway.

> 
> 
> >+	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;
> >+}
> >+


> >+
> >+	memset(&priv->stats, 0, sizeof(priv->stats));
> 
> don't use your own copy of net_device_stats, it's in net_device now

Nice. OK.

> 
> 
> >+/*
> >+ * 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);
> 
> fall back to ALLMULTI behavior if dev->mc_count is too large (for your 
> chip's version of "too large")

Uh, as far as I understand, this gaddr is some mask of which packages to
receive? IOW. if dmi_addrs are just right, it'll become IFF_ALLMULTI
anyway.

> 
> >+/* ethtool interface */
> >+static void mpc52xx_fec_get_drvinfo(struct net_device *dev,
> >+		struct ethtool_drvinfo *info)
> >+{
> >+	strcpy(info->driver, DRIVER_NAME);
> 
> version?  anything else?

Version is Linux version. What else?

I think the only reason I implemented this was because some tool
didn't want to play without it.

> 
> 
> >+	ndev->watchdog_timeo	= FEC_WATCHDOG_TIMEOUT;
> >+	ndev->flags &= ~IFF_RUNNING;
> 
> delete this, no reason to ever touch IFF_RUNNING yourself

OK.

> 
> >+	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_IFUP << 1) - 1;
> 
> netif_msg_init

OK. Also added "debug" module parameter now.

> 
> 
> >+mpc52xx_fec_remove(struct of_device *op)
> >+{
> >+	struct net_device *ndev;
> >+	struct mpc52xx_fec_priv *priv;
> >+
> >+	ndev = dev_get_drvdata(&op->dev);
> >+	if (!ndev)
> >+		return 0;
> 
> testing for impossible condition

Removed now.


Version incorporating fixes:

--- cut here ;-) ---

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


Signed-off-by: Domen Puncer <domen.puncer@telargo.com>

---
 drivers/net/Kconfig           |   24 
 drivers/net/Makefile          |    4 
 drivers/net/fec_mpc52xx.c     | 1109 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/fec_mpc52xx.h     |  313 +++++++++++
 drivers/net/fec_mpc52xx_phy.c |  198 +++++++
 5 files changed, 1648 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,1109 @@
+/*
+ * 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 bcom_task *s)
+{
+	struct sk_buff *skb;
+
+	while (!bcom_queue_empty(s)) {
+		skb = bcom_retrieve_buffer(s, NULL, NULL);
+		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(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(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;
+		skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL, NULL);
+		/* 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.
+		 */
+
+		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, NULL);
+
+		/* 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(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: [NETNS] Oops in register_pernet_operations() with CONFIG_NET_NS=n
From: Denis V. Lunev @ 2007-10-25 14:00 UTC (permalink / raw)
  To: Benjamin Thery
  Cc: Pavel Emelianov, Eric W. Biederman, David Miller,
	Linux Netdev List, Cedric Le Goater, Linux Containers,
	Daniel Lezcano
In-Reply-To: <47209317.8000802@bull.net>

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

The patch attached should help. The idea is simple. The "init" should be
called only once without NETNS. Period. No need for any lists.

I'll resend it to Dave after the ACK.

Regards,
	Den

Benjamin Thery wrote:
> Hello Pavel,
> 
> I've found a problem with one of your patch related to netns:
> 
> * [NETNS] Move some code into __init section when CONFIG_NET_NS=n (v2)
>    http://www.spinics.net/lists/netdev/msg43310.html
> 
> This patch introduces the __net_init/__net_exit/__net_initdata
> defines to save some memory when CONFIG_NET_NS is not set.
> 
> Cedric Le Goater reported he had a *non-fatal* oops when booting
> a 2.6.23-mm1-lxc1 kernel with CONFIG_NET_NS=n. (2.6.23-mm1-lxc1 
> contains the NETNS49 patchset). The oops occured when modules 
> related to iptables were loaded after the boot completes.
> 
> The problem is the following:
> 
> - Your patch adds the __net_initdata attribute to pernet_operations 
>   structures.
> 
> - pernet_operations are registered via register_pernet_subsys() and 
>   linked in the pernet_list during boot.
> 
> - At the end of boot, pernet_operations are freed (because of the
>   __net_initdata attribute), and the pernet_list (or first_device list) 
>   points to freed memory.
> 
> - After boot, network modules which are netns-aware try to register
>   themselves with register_pernet_subsys() and ...KABOOM... page
>   fault when accessing pernet_list (or first_device list).
>   (I reproduce Cedric's oops with the command: iptables --list)
> 
> This is not a problem right now in 2.6.23-mm1 or net-2.6 because 
> there are very few netns-aware network subsystems merged and they
> are all initialized during boot. But it will be problematic when 
> we will merge netns code for subsystems which can be built as 
> modules (eg. iptables, ...). I'm not sure we can use 
> __net_init_data for pernet_operations then. 
> Maybe we can add some checks in register_pernet_operations
> when CONFIG_NET_NS=n.
> 
> I haven't found a fix yet.
> 
> Regards,
> Benjamin
> 


[-- Attachment #2: diff-no-netns --]
[-- Type: text/plain, Size: 441 bytes --]

diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 6f71db8..9ba4809 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -186,7 +186,11 @@ static int register_pernet_operations(struct list_head *list,
 	int error;
 
 	error = 0;
+#ifdef CONFIG_NET_NS
 	list_add_tail(&ops->list, list);
+#endif
+	INIT_LIST_HEAD(&ops->list);
+#endif
 	for_each_net(net) {
 		if (ops->init) {
 			error = ops->init(net);

^ permalink raw reply related

* [PATCH] NetLabel: correct usage of RCU locking
From: Paul Moore @ 2007-10-25 13:52 UTC (permalink / raw)
  To: netdev
In-Reply-To: <20071025134727.6382.79273.stgit@flek.americas.hpqcorp.net>

This fixes some awkward, and perhaps even problematic, RCU lock usage in the
NetLabel code as well as some other related trivial cleanups found when
looking through the RCU locking.  Most of the changes involve removing the
redundant RCU read locks wrapping spinlocks in the case of a RCU writer.

Signed-off-by: Paul Moore <paul.moore@hp.com>
---

 net/ipv4/cipso_ipv4.c              |   39 +++++++++---------------------------
 net/netlabel/netlabel_domainhash.c |   37 ++++++++++------------------------
 net/netlabel/netlabel_mgmt.c       |    4 ----
 net/netlabel/netlabel_unlabeled.c  |    4 +---
 4 files changed, 22 insertions(+), 62 deletions(-)

diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index 805a78e..f18e88b 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -504,22 +504,16 @@ int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
 	INIT_RCU_HEAD(&doi_def->rcu);
 	INIT_LIST_HEAD(&doi_def->dom_list);
 
-	rcu_read_lock();
-	if (cipso_v4_doi_search(doi_def->doi) != NULL)
-		goto doi_add_failure_rlock;
 	spin_lock(&cipso_v4_doi_list_lock);
 	if (cipso_v4_doi_search(doi_def->doi) != NULL)
-		goto doi_add_failure_slock;
+		goto doi_add_failure;
 	list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list);
 	spin_unlock(&cipso_v4_doi_list_lock);
-	rcu_read_unlock();
 
 	return 0;
 
-doi_add_failure_slock:
+doi_add_failure:
 	spin_unlock(&cipso_v4_doi_list_lock);
-doi_add_failure_rlock:
-	rcu_read_unlock();
 	return -EEXIST;
 }
 
@@ -543,29 +537,23 @@ int cipso_v4_doi_remove(u32 doi,
 	struct cipso_v4_doi *doi_def;
 	struct cipso_v4_domhsh_entry *dom_iter;
 
-	rcu_read_lock();
-	if (cipso_v4_doi_search(doi) != NULL) {
-		spin_lock(&cipso_v4_doi_list_lock);
-		doi_def = cipso_v4_doi_search(doi);
-		if (doi_def == NULL) {
-			spin_unlock(&cipso_v4_doi_list_lock);
-			rcu_read_unlock();
-			return -ENOENT;
-		}
+	spin_lock(&cipso_v4_doi_list_lock);
+	doi_def = cipso_v4_doi_search(doi);
+	if (doi_def != NULL) {
 		doi_def->valid = 0;
 		list_del_rcu(&doi_def->list);
 		spin_unlock(&cipso_v4_doi_list_lock);
+		rcu_read_lock();
 		list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list)
 			if (dom_iter->valid)
 				netlbl_domhsh_remove(dom_iter->domain,
 						     audit_info);
-		cipso_v4_cache_invalidate();
 		rcu_read_unlock();
-
+		cipso_v4_cache_invalidate();
 		call_rcu(&doi_def->rcu, callback);
 		return 0;
 	}
-	rcu_read_unlock();
+	spin_unlock(&cipso_v4_doi_list_lock);
 
 	return -ENOENT;
 }
@@ -653,22 +641,19 @@ int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain)
 	new_dom->valid = 1;
 	INIT_RCU_HEAD(&new_dom->rcu);
 
-	rcu_read_lock();
 	spin_lock(&cipso_v4_doi_list_lock);
-	list_for_each_entry_rcu(iter, &doi_def->dom_list, list)
+	list_for_each_entry(iter, &doi_def->dom_list, list)
 		if (iter->valid &&
 		    ((domain != NULL && iter->domain != NULL &&
 		      strcmp(iter->domain, domain) == 0) ||
 		     (domain == NULL && iter->domain == NULL))) {
 			spin_unlock(&cipso_v4_doi_list_lock);
-			rcu_read_unlock();
 			kfree(new_dom->domain);
 			kfree(new_dom);
 			return -EEXIST;
 		}
 	list_add_tail_rcu(&new_dom->list, &doi_def->dom_list);
 	spin_unlock(&cipso_v4_doi_list_lock);
-	rcu_read_unlock();
 
 	return 0;
 }
@@ -689,9 +674,8 @@ int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
 {
 	struct cipso_v4_domhsh_entry *iter;
 
-	rcu_read_lock();
 	spin_lock(&cipso_v4_doi_list_lock);
-	list_for_each_entry_rcu(iter, &doi_def->dom_list, list)
+	list_for_each_entry(iter, &doi_def->dom_list, list)
 		if (iter->valid &&
 		    ((domain != NULL && iter->domain != NULL &&
 		      strcmp(iter->domain, domain) == 0) ||
@@ -699,13 +683,10 @@ int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
 			iter->valid = 0;
 			list_del_rcu(&iter->list);
 			spin_unlock(&cipso_v4_doi_list_lock);
-			rcu_read_unlock();
 			call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free);
-
 			return 0;
 		}
 	spin_unlock(&cipso_v4_doi_list_lock);
-	rcu_read_unlock();
 
 	return -ENOENT;
 }
diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c
index b6c844b..b3675bd 100644
--- a/net/netlabel/netlabel_domainhash.c
+++ b/net/netlabel/netlabel_domainhash.c
@@ -178,11 +178,9 @@ int netlbl_domhsh_init(u32 size)
 	for (iter = 0; iter < hsh_tbl->size; iter++)
 		INIT_LIST_HEAD(&hsh_tbl->tbl[iter]);
 
-	rcu_read_lock();
 	spin_lock(&netlbl_domhsh_lock);
 	rcu_assign_pointer(netlbl_domhsh, hsh_tbl);
 	spin_unlock(&netlbl_domhsh_lock);
-	rcu_read_unlock();
 
 	return 0;
 }
@@ -222,7 +220,6 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
 	entry->valid = 1;
 	INIT_RCU_HEAD(&entry->rcu);
 
-	ret_val = 0;
 	rcu_read_lock();
 	if (entry->domain != NULL) {
 		bkt = netlbl_domhsh_hash(entry->domain);
@@ -233,7 +230,7 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
 		else
 			ret_val = -EEXIST;
 		spin_unlock(&netlbl_domhsh_lock);
-	} else if (entry->domain == NULL) {
+	} else {
 		INIT_LIST_HEAD(&entry->list);
 		spin_lock(&netlbl_domhsh_def_lock);
 		if (rcu_dereference(netlbl_domhsh_def) == NULL)
@@ -241,9 +238,7 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
 		else
 			ret_val = -EEXIST;
 		spin_unlock(&netlbl_domhsh_def_lock);
-	} else
-		ret_val = -EINVAL;
-
+	}
 	audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
 	if (audit_buf != NULL) {
 		audit_log_format(audit_buf,
@@ -262,7 +257,6 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
 		audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
 		audit_log_end(audit_buf);
 	}
-
 	rcu_read_unlock();
 
 	if (ret_val != 0) {
@@ -313,38 +307,30 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
 	struct audit_buffer *audit_buf;
 
 	rcu_read_lock();
-	if (domain != NULL)
-		entry = netlbl_domhsh_search(domain, 0);
-	else
-		entry = netlbl_domhsh_search(domain, 1);
+	entry = netlbl_domhsh_search(domain, (domain != NULL ? 0 : 1));
 	if (entry == NULL)
 		goto remove_return;
 	switch (entry->type) {
-	case NETLBL_NLTYPE_UNLABELED:
-		break;
 	case NETLBL_NLTYPE_CIPSOV4:
-		ret_val = cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4,
-						     entry->domain);
-		if (ret_val != 0)
-			goto remove_return;
+		cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4,
+					   entry->domain);
 		break;
 	}
-	ret_val = 0;
 	if (entry != rcu_dereference(netlbl_domhsh_def)) {
 		spin_lock(&netlbl_domhsh_lock);
 		if (entry->valid) {
 			entry->valid = 0;
 			list_del_rcu(&entry->list);
-		} else
-			ret_val = -ENOENT;
+			ret_val = 0;
+		}
 		spin_unlock(&netlbl_domhsh_lock);
 	} else {
 		spin_lock(&netlbl_domhsh_def_lock);
 		if (entry->valid) {
 			entry->valid = 0;
 			rcu_assign_pointer(netlbl_domhsh_def, NULL);
-		} else
-			ret_val = -ENOENT;
+			ret_val = 0;
+		}
 		spin_unlock(&netlbl_domhsh_def_lock);
 	}
 
@@ -357,11 +343,10 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
 		audit_log_end(audit_buf);
 	}
 
-	if (ret_val == 0)
-		call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
-
 remove_return:
 	rcu_read_unlock();
+	if (ret_val == 0)
+		call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
 	return ret_val;
 }
 
diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c
index 5315dac..5648337 100644
--- a/net/netlabel/netlabel_mgmt.c
+++ b/net/netlabel/netlabel_mgmt.c
@@ -85,11 +85,9 @@ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
  */
 void netlbl_mgmt_protocount_inc(void)
 {
-	rcu_read_lock();
 	spin_lock(&netlabel_mgmt_protocount_lock);
 	netlabel_mgmt_protocount++;
 	spin_unlock(&netlabel_mgmt_protocount_lock);
-	rcu_read_unlock();
 }
 
 /**
@@ -103,12 +101,10 @@ void netlbl_mgmt_protocount_inc(void)
  */
 void netlbl_mgmt_protocount_dec(void)
 {
-	rcu_read_lock();
 	spin_lock(&netlabel_mgmt_protocount_lock);
 	if (netlabel_mgmt_protocount > 0)
 		netlabel_mgmt_protocount--;
 	spin_unlock(&netlabel_mgmt_protocount_lock);
-	rcu_read_unlock();
 }
 
 /**
diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c
index 5c303c6..3482924 100644
--- a/net/netlabel/netlabel_unlabeled.c
+++ b/net/netlabel/netlabel_unlabeled.c
@@ -84,12 +84,10 @@ static void netlbl_unlabel_acceptflg_set(u8 value,
 	struct audit_buffer *audit_buf;
 	u8 old_val;
 
-	rcu_read_lock();
-	old_val = netlabel_unlabel_acceptflg;
 	spin_lock(&netlabel_unlabel_acceptflg_lock);
+	old_val = netlabel_unlabel_acceptflg;
 	netlabel_unlabel_acceptflg = value;
 	spin_unlock(&netlabel_unlabel_acceptflg_lock);
-	rcu_read_unlock();
 
 	audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_ALLOW,
 					      audit_info);


^ permalink raw reply related


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