All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCH] uncompress.h cleanup and DT support
From: Zoltan Devai @ 2011-10-23  8:18 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Originally, I just wanted to get rid of arch_decomp_* in all the uncompress.h
files, that were mostly empty. But then, I couldn't stop. Which may also mean
I have gone too far.

Plase consider all this as RFC, it's mostly not even compile-tested.
If you think it's worth to follow-up, I'm happy to do it.

What this series does:
- Remove all instances of arch_decomp_wdog

- Restructure the decompression code just a bit,
  to separate the UART-printing stuff

- Introduce a low-level serial driver of the style
  "check a reg if there's space for a TX char",
  "output char", "wait for flush reg"

- Add init functions for the two common UARTs:
  8250 and the amba.
  This finally gets rid of that dozen re-implementations
  that all use different register access methods and
  register defines, but do the same.

- Some basic style cleanup

- Allow the uncompress UART definitions to be overridden
  by a device tree description.
  This would allow the removal of all the
  "if machine_is_xxx, serial base is 123" lookup tables.

Remarks:
- Just to see how it works out, I converted most of the
  arches/machines to use the ucuart driver.
  I didn't convert any Samsung machines, because the
  code is just too convoluted for me to dive into now.
- The overall patch is above 100K, so I didn't want
  to post it here.
  It's available here:
  git://github.com/zdevai/linux.git uncompress-h

- Overall diffstat below.

Comments welcome.

Cheers,
Zoltan

 arch/arm/Kconfig                                   |    9 +
 arch/arm/boot/compressed/Makefile                  |    9 +-
 arch/arm/boot/compressed/decompress.c              |   78 ++++--
 arch/arm/boot/compressed/head.S                    |    8 +
 arch/arm/boot/compressed/misc.c                    |  154 -----------
 arch/arm/boot/compressed/print.c                   |  290 ++++++++++++++++++++
 arch/arm/mach-at91/include/mach/uncompress.h       |   62 +----
 arch/arm/mach-bcmring/include/mach/uncompress.h    |   47 +---
 arch/arm/mach-clps711x/include/mach/uncompress.h   |   49 +---
 arch/arm/mach-cns3xxx/include/mach/uncompress.h    |   49 +---
 arch/arm/mach-davinci/include/mach/uncompress.h    |   24 +--
 arch/arm/mach-dove/include/mach/uncompress.h       |   30 +--
 arch/arm/mach-ebsa110/include/mach/uncompress.h    |   40 +---
 arch/arm/mach-ep93xx/include/mach/uncompress.h     |   49 +---
 arch/arm/mach-exynos4/include/mach/uncompress.h    |   15 +-
 arch/arm/mach-footbridge/include/mach/uncompress.h |   34 +--
 arch/arm/mach-gemini/include/mach/uncompress.h     |   41 +---
 arch/arm/mach-h720x/include/mach/uncompress.h      |   39 +--
 arch/arm/mach-integrator/include/mach/uncompress.h |   49 +---
 arch/arm/mach-iop13xx/include/mach/uncompress.h    |   26 +--
 arch/arm/mach-iop32x/include/mach/uncompress.h     |   37 +--
 arch/arm/mach-iop33x/include/mach/uncompress.h     |   34 +--
 arch/arm/mach-ixp2000/include/mach/uncompress.h    |   48 +---
 arch/arm/mach-ixp23xx/include/mach/uncompress.h    |   33 +--
 arch/arm/mach-ixp4xx/include/mach/uncompress.h     |   44 +---
 arch/arm/mach-kirkwood/include/mach/uncompress.h   |   40 +---
 arch/arm/mach-ks8695/include/mach/uncompress.h     |   32 +--
 arch/arm/mach-lpc32xx/include/mach/uncompress.h    |   57 +----
 arch/arm/mach-mmp/include/mach/uncompress.h        |   38 +---
 arch/arm/mach-msm/include/mach/uncompress.h        |   45 +---
 arch/arm/mach-mv78xx0/include/mach/uncompress.h    |   40 +---
 arch/arm/mach-mxs/include/mach/uncompress.h        |   63 +----
 arch/arm/mach-netx/include/mach/uncompress.h       |   73 +-----
 arch/arm/mach-nomadik/include/mach/uncompress.h    |   58 +----
 arch/arm/mach-omap1/include/mach/uncompress.h      |    4 -
 arch/arm/mach-omap2/include/mach/uncompress.h      |    4 -
 arch/arm/mach-orion5x/include/mach/uncompress.h    |   48 +---
 arch/arm/mach-picoxcell/include/mach/uncompress.h  |   18 +-
 arch/arm/mach-pnx4008/include/mach/uncompress.h    |   43 +---
 arch/arm/mach-prima2/include/mach/uncompress.h     |   41 +---
 arch/arm/mach-pxa/include/mach/uncompress.h        |   77 +-----
 arch/arm/mach-realview/include/mach/uncompress.h   |   70 +----
 arch/arm/mach-rpc/include/mach/uncompress.h        |    9 +-
 arch/arm/mach-s3c2410/include/mach/uncompress.h    |    2 +
 arch/arm/mach-s3c64xx/include/mach/uncompress.h    |   18 +-
 arch/arm/mach-s5p64x0/include/mach/uncompress.h    |   39 +---
 arch/arm/mach-s5pc100/include/mach/uncompress.h    |    6 +-
 arch/arm/mach-s5pv210/include/mach/uncompress.h    |   17 +-
 arch/arm/mach-sa1100/include/mach/uncompress.h     |   62 ++---
 arch/arm/mach-shark/include/mach/uncompress.h      |   52 +----
 arch/arm/mach-shmobile/include/mach/uncompress.h   |   17 +-
 arch/arm/mach-spear3xx/include/mach/uncompress.h   |   18 --
 arch/arm/mach-spear6xx/include/mach/uncompress.h   |   18 --
 arch/arm/mach-tegra/include/mach/uncompress.h      |   62 +----
 arch/arm/mach-u300/include/mach/uncompress.h       |   45 +---
 arch/arm/mach-ux500/include/mach/uncompress.h      |   54 +----
 arch/arm/mach-versatile/include/mach/uncompress.h  |   45 +---
 arch/arm/mach-vexpress/include/mach/uncompress.h   |   51 +----
 arch/arm/mach-vt8500/include/mach/uncompress.h     |   40 +---
 arch/arm/mach-w90x900/include/mach/uncompress.h    |   47 +---
 arch/arm/mach-zynq/include/mach/uncompress.h       |   53 +---
 arch/arm/plat-mxc/include/mach/uncompress.h        |   37 +--
 arch/arm/plat-omap/include/plat/uncompress.h       |   45 +---
 arch/arm/plat-samsung/Kconfig                      |    8 -
 arch/arm/plat-samsung/include/plat/uncompress.h    |   35 +---
 arch/arm/plat-spear/include/plat/uncompress.h      |   43 +---
 arch/arm/plat-tcc/include/mach/uncompress.h        |   33 +--
 67 files changed, 671 insertions(+), 2234 deletions(-)

^ permalink raw reply

* Re: [PATCH] Fix to qt1010 tuner frequency selection (media/dvb)
From: Carlos Corbacho @ 2011-10-23  8:18 UTC (permalink / raw)
  To: Jyrki Kuoppala; +Cc: linux-media, linux-kernel, Mauro Carvalho Chehab
In-Reply-To: <4E9FA779.5040406@iki.fi>

On Thursday 20 Oct 2011 07:45:45 Jyrki Kuoppala wrote:
> I think my problem frequency has also been at the later spot. It is possible
> there is something more complicated going on at 474 MHz - so based on your
> testing, it's best to apply just the latter change, at least for now.
> 
> Jyrki
> 
> 
> 
> +    else if (freq<   546000000) rd[15].val = 0xd6; /* 546 MHz */
> 
> +    else if (freq<   546000000) rd[15].val = 0xd6; /* 546 MHz */

Are you going to resubmit the patch with just these changes (you can add my 
Tested-by to that), or if you want, I can take care of resubmitting it?

-Carlos

^ permalink raw reply

* Re: [PATCH 01/24] Staging: comedi: fix printk issue in das6402.c
From: Greg KH @ 2011-10-23  8:18 UTC (permalink / raw)
  To: Ravishankar Karkala Mallikarjunayya; +Cc: gregkh, wfp5p, devel, linux-kernel
In-Reply-To: <1319091550-2558-1-git-send-email-ravishankar.km@greenturtles.in>

On Thu, Oct 20, 2011 at 11:48:47AM +0530, Ravishankar Karkala Mallikarjunayya wrote:
> This is a patch to the das6402.c file that fixes up a printk warning
> found by the checkpatch.pl tool.
> Added #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

Why is this needed?  dev_printk() doesn't use it, right?  So it
shouldn't be needed here, or am I missing something?

greg k-h

^ permalink raw reply

* Re: [PATCH 03/24] Staging: comedi: fix warning issue in pcl818.c
From: Greg KH @ 2011-10-23  8:20 UTC (permalink / raw)
  To: Ravishankar Karkala Mallikarjunayya; +Cc: gregkh, wfp5p, devel, linux-kernel
In-Reply-To: <1319091550-2558-3-git-send-email-ravishankar.km@greenturtles.in>

On Thu, Oct 20, 2011 at 11:48:49AM +0530, Ravishankar Karkala Mallikarjunayya wrote:
> This is a patch to the pcl818.c file that fixes up a warning
> found by the checkpatch.pl tool.
> Fix brace coding style and NULL initilization issue.
> Converted printks to dev_printk.
> Removed unnecessary printk statements.

That's a lot of different things all at once here.  Please only do one
"change" per patch.  So this should be split up into multiple patches.

Same goes for your other 24 patches, care to break them up as well, as
it's easier to review and verify that you got it correct.

thanks,

greg k-h

^ permalink raw reply

* Re: [PATCH 02/24] Staging: comedi: fix printk coding style issue in
From: Greg KH @ 2011-10-23  8:19 UTC (permalink / raw)
  To: Ravishankar Karkala Mallikarjunayya; +Cc: gregkh, wfp5p, devel, linux-kernel
In-Reply-To: <1319091550-2558-2-git-send-email-ravishankar.km@greenturtles.in>

On Thu, Oct 20, 2011 at 11:48:48AM +0530, Ravishankar Karkala Mallikarjunayya wrote:
> This is a patch to the serial2002.c file that fixes up a printk warning
> found by the checkpatch.pl tool.
> Converted printks to pr_<level>.
> 
> Signed-off-by: Ravishankar Karkala Mallikarjunayya <ravishankar.km@greenturtles.in>
> ---
>  drivers/staging/comedi/drivers/serial2002.c |    9 +++++----
>  1 files changed, 5 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/staging/comedi/drivers/serial2002.c b/drivers/staging/comedi/drivers/serial2002.c
> index ade2202..6bd07ec 100644
> --- a/drivers/staging/comedi/drivers/serial2002.c
> +++ b/drivers/staging/comedi/drivers/serial2002.c
> @@ -431,7 +431,8 @@ static int serial_2002_open(struct comedi_device *dev)
>  		}
>  
>  		tty_setspeed(devpriv->tty, devpriv->speed);
> -		poll_channel(devpriv->tty, 31);	/*  Start reading configuration */
> +		/* Start reading configuration */
> +		poll_channel(devpriv->tty, 31);
>  		while (1) {
>  			struct serial_data data;
>  
> @@ -824,7 +825,7 @@ static int serial2002_attach(struct comedi_device *dev,
>  {
>  	struct comedi_subdevice *s;
>  
> -	printk("comedi%d: serial2002: ", dev->minor);
> +	pr_debug("comedi%d: serial2002: attached\n", dev->minor);

You have a device, so use dev_dbg() instead of pr_debug()

>  	dev->board_name = thisboard->name;
>  	if (alloc_private(dev, sizeof(struct serial2002_private)) < 0)
>  		return -ENOMEM;
> @@ -832,7 +833,7 @@ static int serial2002_attach(struct comedi_device *dev,
>  	dev->close = serial_2002_close;
>  	devpriv->port = it->options[0];
>  	devpriv->speed = it->options[1];
> -	printk("/dev/ttyS%d @ %d\n", devpriv->port, devpriv->speed);
> +	pr_debug("/dev/ttyS%d @ %d\n", devpriv->port, devpriv->speed);
>  
>  	if (alloc_subdevices(dev, 5) < 0)
>  		return -ENOMEM;
> @@ -891,7 +892,7 @@ static int serial2002_detach(struct comedi_device *dev)
>  	struct comedi_subdevice *s;
>  	int i;
>  
> -	printk("comedi%d: serial2002: remove\n", dev->minor);
> +	pr_debug("comedi%d: serial2002: remove\n", dev->minor);

Same for the above two as well.

greg k-h

^ permalink raw reply

* [U-Boot] [PATCH v4 2/2] NS16550: buffer reads
From: Wolfgang Denk @ 2011-10-23  8:20 UTC (permalink / raw)
  To: u-boot
In-Reply-To: <4EA34086.4030101@gmail.com>

Dear Graeme Russ,

In message <4EA34086.4030101@gmail.com> you wrote:
> 
> One problem I see with XON/XOFF is that if we don't send XOFF at the right
> time, we run the risk of entering a busy loop (any reasonable timeout delay
> for example) and loosing input. So in theory, we would need to send XOFF
> after every getc() ...

That's not true.  I am not aware of any significant delays that take
place while receiving characters that belong to a single line.  If we
had any of these, we would lose characters all the time - but we
don't.

It should be sufficient to send XOFF after receiving a newline
character.

> Maybe we need disable/enable flow control functions for when we know we
> will be entering a busy loop the consumes serial input (ymodem and kermit
> transfers and readline for example)

This should not be necessary. Actually the implementation should not
need to know about such special cases.

Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
Compassion -- that's the one things no machine ever had.  Maybe it's
the one thing that keeps men ahead of them.
	-- McCoy, "The Ultimate Computer", stardate 4731.3

^ permalink raw reply

* Re: [PATCH 02/15] ATA : vortex86 : fix vortex86dx/sx hardware CRC bug.
From: Rolf Eike Beer @ 2011-10-23  8:20 UTC (permalink / raw)
  To: Paul Schilling
  Cc: Jeff Garzik, David S. Miller, Jesse Barnes, linux-ide,
	linux-kernel, linux-pci, Paul Schilling
In-Reply-To: <1319346300-18151-1-git-send-email-paul.s.schilling@gmail.com>

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

Paul Schilling wrote:
> This fixes a DMA issue related to a CRC bug on
> the RDC pata peripherial found on the vortex86sx and vortex86dx.
> 
> Signed-off-by: Paul Schilling <paul.s.schilling@gmail.com>
> ---
>  drivers/ata/pata_it821x.c |   22 ++++++++++++++++++----
>  drivers/ata/pata_rdc.c    |   29 ++++++++++++++++++++++++-----
>  drivers/ide/it821x.c      |    9 +++++++--
>  include/linux/pci_ids.h   |    2 ++
>  4 files changed, 51 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
> index c5532b9..5f8a54a 100644
> --- a/drivers/ata/pata_it821x.c
> +++ b/drivers/ata/pata_it821x.c
> @@ -897,7 +897,16 @@ static int it821x_init_one(struct pci_dev *pdev, const
> struct pci_device_id *id) static const struct ata_port_info info_rdc_11 = {
>  		.flags = ATA_FLAG_SLAVE_POSS,
>  		.pio_mask = ATA_PIO4,
> -		.mwdma_mask = ATA_MWDMA2,
> +		.mwdma_mask = 0,
> +		.udma_mask = 0,
> +		/* No UDMA */
> +		.port_ops = &it821x_rdc_port_ops
> +	};
> +	static const struct ata_port_info info_rdc_01 = {
> +		.flags = ATA_FLAG_SLAVE_POSS,
> +		.pio_mask = ATA_PIO4,
> +		.mwdma_mask = 0,
> +		.udma_mask = 0,
>  		/* No UDMA */
>  		.port_ops = &it821x_rdc_port_ops
>  	};

Why do you define this if it is never used?

Eike

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Re: lsusd - The Linux SUSpend Daemon
From: NeilBrown @ 2011-10-23  8:21 UTC (permalink / raw)
  To: Alan Stern
  Cc: John Stultz, Rafael J. Wysocki, mark gross, Linux PM list, LKML
In-Reply-To: <Pine.LNX.4.44L0.1110212125020.8657-100000@netrider.rowland.org>

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

On Fri, 21 Oct 2011 22:00:13 -0400 (EDT) Alan Stern
<stern@rowland.harvard.edu> wrote:

> On Sat, 22 Oct 2011, NeilBrown wrote:
> 
> > > >     It uses files in /var/run/suspend for all communication.
> > > 
> > > I'm not so keen on using files for communication.  At best, they are
> > > rather awkward for two-way messaging.  If you really want to use them,
> > > then at least put them on a non-backed filesystem, like something under
> > > /dev.
> > 
> > Isn't /var/run a tmpfs filesystem?  It should be.
> > Surely /run is, so in the new world order the files should probably go
> > there.   But that is just a detail.
> 
> On my Fedora-14 systems there is no /run, and /var/run is a regular 
> directory in a regular filesystem.
> 
> > I like files...  I particularly like 'flock' to block suspend.   The
> > rest.... whatever..
> > With files, you only need a context switch when there is real communication.
> > With sockets, every message sent must be read so there will be a context
> > switch.
> > 
> > Maybe we could do something with futexes...
> 
> Not easily -- as far as I can tell, futexes enjoy relatively little 
> support.  In any case, they provide the same service as a mutex, which 
> means you'd have to build a shared lock on top of them.
> 
> > > >     lsusd does not try to be event-loop based because:
> > > >       - /sys/power/wakeup_count is not pollable.  This could probably be
> > > >         'fixed' but I want code to work with today's kernel.  It will probably
> > > 
> > > Why does this matter?
> > 
> > In my mind an event based program should never block.  Every action should be
> > non-blocking and only taken when 'poll' says it can.
> > Reading /sys/power/wakeup_count can be read non-blocking, but you cannot find
> > out when it is sensible to try to read it again.  So it doesn't fit.
> 
> There shouldn't be any trouble about making wakeup_count pollable.  It
> also would need to respect nonblocking reads, which it currently does 
> not do.

Hmm.. you are correct.  I wonder why I thought it did support non-blocking
reads...
I guess it was the code for handling an interrupted system call.

I feel a bit uncomfortable with the idea of sysfs files that block but I
don't think I can convincingly argue against it.
A non-blocking flag could be passed in, but it would be a very messy change -
lots of function call signatures changing needlessly:  we would need a flag
to the 'show' method ... or add a 'show_nonblock' method which would also be
ugly.


But I think there is a need to block - if there is an in-progress event then
it must be possible to wait for it to complete as it may not be visible to
userspace until then.
We could easily enable 'poll' for wakeup_count and then make it always
non-blocking, but I'm not really sure I want to require programs to use poll,
only to allow them.  And without using poll there is no way to wait.

As wakeup_count really has to be single-access we could possibly fudge
something by remembering the last value read (like we remember the last value
written).

- if the current count is different from the last value read, then return
  it even if there are in-progress events.
- if the current count is the same as the last value read, then block until
  there are no in-progress events and return the new value.
- enable sysfs_poll on wakeup_count by calling sysfs_notify_dirent at the
  end of wakeup_source_deactivated .... or calling something in
  kernel/power/main.c which calls that.  However we would need to make
  sysfs_notify_dirent a lot lighter weight first.  Maybe I should do that.

Then a process that uses 'poll' could avoid reading wakeup_count except when
it has changed, and then it won't block.  And a process that doesn't use poll
can block by simply reading twice - either explicitly or by going around a 
   read then write and it fails
loop a second time.

I'm not sure I'm completely comfortable with that, but it is the best I could
come up with.


> 
> At the worst, you could always have a separate thread to read 
> wakeup_count.

Maybe.  I'm tempted simply not to worry about the short delay after all, but
the moment I do that, someone will use a wakeup_source to disable suspend
for a longer period of time (just like I suggested) and suddenly it won't be
a short delay after all.

> 
> > > >       - I cannot get an event notification when a lock is removed from a
> > > >         file. :-(  And I think locks are an excellent light-weight
> > > >         mechanism for blocking suspend.
> > > 
> > > Except for this one drawback.  Socket connections are superior in that 
> > > regard.
> > 
> > I'm very happy for someone else write an all-socket based daemon.
> 
> Hmmm...  Maybe I'll take you up on that.
> 
> 
> > > >       lsused will send a 'S' message to the client and await an 'R' reply
> > > >       (S == suspend, R == ready).  When all replies are in, lsused will
> > > >       allow the suspend to complete.  When it does (or aborts), it will send
> > > >       'A' (awake) to those clients to which it sent 'S'.
> > > 
> > > But not to the client which failed to send an 'R'?
> > 
> > Every client must send an R before suspend can continue.
> 
> I was referring to the case where you abort before receiving an 'R'.  
> The current suspend attempt will fail, but then what happens during the
> next attempt?

There is no situation in which I abort before receiving an 'R'.
I wait until all 'R's are in until I even check if an abort was requested.


NeilBrown



Sample untested patch to allow non-blocking reads and poll on wakeup_count.

diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 84f7c7d..54543ba 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -44,6 +44,8 @@ static void split_counters(unsigned int *cnt, unsigned int *inpr)
 
 /* A preserved old value of the events counter. */
 static unsigned int saved_count;
+/* Record last value that was read from events counter. */
+static unsigned int last_read_count;
 
 static DEFINE_SPINLOCK(events_lock);
 
@@ -410,6 +412,7 @@ static void wakeup_source_deactivate(struct wakeup_source *ws)
 {
 	ktime_t duration;
 	ktime_t now;
+	unsigned int comb;
 
 	ws->relax_count++;
 	/*
@@ -440,7 +443,10 @@ static void wakeup_source_deactivate(struct wakeup_source *ws)
 	 * Increment the counter of registered wakeup events and decrement the
 	 * couter of wakeup events in progress simultaneously.
 	 */
-	atomic_add(MAX_IN_PROGRESS, &combined_event_count);
+	comb = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count);
+
+	if ((comb >> IN_PROGRESS_BITS) == last_read_count + 1)
+		wakeup_count_changed();
 }
 
 /**
@@ -624,15 +630,24 @@ bool pm_get_wakeup_count(unsigned int *count)
 
 	for (;;) {
 		split_counters(&cnt, &inpr);
-		if (inpr == 0 || signal_pending(current))
+		if (inpr == 0 || cnt != last_read_count)
 			break;
+		if (signal_pending(current))
+			return false;
 		pm_wakeup_update_hit_counts();
 		schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT));
 	}
 
+	last_read_count = cnt;
+	/* If cnt has just changed, then last_read_count will be a bit
+	 * old, so we won't block on the next read, only the one after.
+	 * However this ensures wakeup_source_deactivate doesn't
+	 * miss out on calling wakeup_count_changed() on a change.
+	 */
+	smp_wmb();
 	split_counters(&cnt, &inpr);
 	*count = cnt;
-	return !inpr;
+	return true;
 }
 
 /**
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 1ad8c93..41361ba 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -444,18 +444,19 @@ static unsigned int sysfs_poll(struct file *filp, poll_table *wait)
 
 void sysfs_notify_dirent(struct sysfs_dirent *sd)
 {
-	struct sysfs_open_dirent *od;
-	unsigned long flags;
+	if (sd->s_attr.open) {
+		struct sysfs_open_dirent *od;
+		unsigned long flags;
+		spin_lock_irqsave(&sysfs_open_dirent_lock, flags);
 
-	spin_lock_irqsave(&sysfs_open_dirent_lock, flags);
+		od = sd->s_attr.open;
+		if (od) {
+			atomic_inc(&od->event);
+			wake_up_interruptible(&od->poll);
+		}
 
-	od = sd->s_attr.open;
-	if (od) {
-		atomic_inc(&od->event);
-		wake_up_interruptible(&od->poll);
+		spin_unlock_irqrestore(&sysfs_open_dirent_lock, flags);
 	}
-
-	spin_unlock_irqrestore(&sysfs_open_dirent_lock, flags);
 }
 EXPORT_SYMBOL_GPL(sysfs_notify_dirent);
 
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 6bbcef2..e42b7f9 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -296,6 +296,7 @@ extern bool events_check_enabled;
 extern bool pm_wakeup_pending(void);
 extern bool pm_get_wakeup_count(unsigned int *count);
 extern bool pm_save_wakeup_count(unsigned int count);
+extern void wakeup_count_changed(void);
 #else /* !CONFIG_PM_SLEEP */
 
 static inline int register_pm_notifier(struct notifier_block *nb)
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 6c601f8..6b3cd80 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -231,6 +231,8 @@ power_attr(state);
  * are any wakeup events detected after 'wakeup_count' was written to.
  */
 
+struct sysfs_dirent *wakeup_count_dirent;
+
 static ssize_t wakeup_count_show(struct kobject *kobj,
 				struct kobj_attribute *attr,
 				char *buf)
@@ -253,6 +255,12 @@ static ssize_t wakeup_count_store(struct kobject *kobj,
 	return -EINVAL;
 }
 
+void wakeup_count_change(void)
+{
+	if (wakeup_count_dirent)
+		sysfs_notify_dirent(wakeup_count_dirent);
+}
+
 power_attr(wakeup_count);
 #endif /* CONFIG_PM_SLEEP */
 
@@ -342,7 +350,11 @@ static int __init pm_init(void)
 	power_kobj = kobject_create_and_add("power", NULL);
 	if (!power_kobj)
 		return -ENOMEM;
-	return sysfs_create_group(power_kobj, &attr_group);
+	error = sysfs_create_group(power_kobj, &attr_group);
+#ifdef CONFIG_PM_SLEEP
+	wakeup_count_dirent = sysfs_get_dirent(power_kobj->sd, NULL, "wakeup_count");
+#endif
+	return error;
 }
 
 core_initcall(pm_init);




[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply related

* Re: a bug when execute "git status" in git version 1.7.7.431.g89633
From: Matthieu Moy @ 2011-10-23  8:25 UTC (permalink / raw)
  To: John Hsing; +Cc: git
In-Reply-To: <4EA20C5B.3090808@gmail.com>

John Hsing <tsyj2007@gmail.com> writes:

> the error:

When does this error occur? is it reproducible on any repository? Which
OS?

> git: malloc.c:3096: sYSMALLOc: Assertion `(old_top == (((mbinptr)
> (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct
> malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >=
> (unsigned long)((((__builtin_offsetof (struct malloc_chunk,
> fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & ~((2 * (sizeof(size_t))) -
> 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask)
> == 0)' failed.

The assertion is violated outside the source code of Git itself, so
either Git calls malloc incorrectly, or this is a bug in your libc.

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

^ permalink raw reply

* Re: [patch net-next V2] net: introduce ethernet teaming device
From: Jiri Pirko @ 2011-10-23  8:25 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: netdev, davem, bhutchings, shemminger, fubar, andy, tgraf,
	ebiederm, mirqus, kaber, greearb, jesse, fbl, benjamin.poirier,
	jzupka
In-Reply-To: <1319302282.6180.60.camel@edumazet-laptop>

Sat, Oct 22, 2011 at 06:51:22PM CEST, eric.dumazet@gmail.com wrote:
>Le samedi 22 octobre 2011 à 17:13 +0200, Jiri Pirko a écrit :
>> >> +
>> >> +/************************
>> >> + * Rx path frame handler
>> >> + ************************/
>> >> +
>> >> +/* note: already called with rcu_read_lock */
>> >> +static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
>> >> +{
>> >> +	struct sk_buff *skb = *pskb;
>> >> +	struct team_port *port;
>> >> +	struct team *team;
>> >> +	rx_handler_result_t res = RX_HANDLER_ANOTHER;
>> >> +
>> >> +	skb = skb_share_check(skb, GFP_ATOMIC);
>> >> +	if (!skb)
>> >> +		return RX_HANDLER_CONSUMED;
>> >> +
>> >> +	*pskb = skb;
>> >> +
>> >> +	port = team_port_get_rcu(skb->dev);
>> >> +	team = port->team;
>> >> +
>> >> +	if (team->mode_ops.receive)
>> >
>> >Hmm, you need ACCESS_ONCE() here or rcu_dereference()
>> >
>> >See commit 4d97480b1806e883eb (bonding: use local function pointer of
>> >bond->recv_probe in bond_handle_frame) for reference
>> 
>> I do not think so. Because mode_ops.receive changes only from
>> __team_change_mode() and this can be called only in case no ports are in
>> team. And team_port_del() calls synchronize_rcu().
>> 
>
>
>
>We are used to code following this template :
>
>if (ops->handler)
>	ops->handler(arguments);
>
>But this is valid only because ops points to constant memory.
>
>
>In your case, we really see its not true, dont try to pretend its safe.

Please forgive me, it's possible I'm missing something. But I see no way how
team->mode_ops.receive can change during team_handle_frame (holding
rcu_read_lock) for the reason I stated earlier.

team_port_del() calls netdev_rx_handler_unregister() and after that it
calls synchronize_rcu(). That ensures that by the finish of team_port_del()
run, team_handle_frame() is not called for this port anymore.

And this combined with "if (!list_empty(&team->port_list))" check in
team_change_mode() ensures safety.

Of course team_port_del() and team_change_mode() are both protected by
team->lock so they are mutually excluded.

Jirka.

>
>
>

^ permalink raw reply

* Linux 3.0.7 now on kernel.org
From: Greg KH @ 2011-10-23  8:31 UTC (permalink / raw)
  To: linux-kernel; +Cc: H. Peter Anvin, John Hawley, Linus Torvalds

I wanted to let people know that the 3.0.7 release is now downloadable
in patch and full-tarball form from kernel.org:
	http://www.kernel.org/pub/linux/kernel/v3.0/
as well as the incremental patches in the incr/ subdir:
	http://www.kernel.org/pub/linux/kernel/v3.0/incr/

ftp.kernel.org also works for those of you using that protocol.

You will note that the files are signed with my new kernel release
signing key, and that the .tar file is signed, and then compressed, so
there is not signatures for the individual compressed files.

For those of you caring and wanting to verify things, the fingerprint of
my signing key is:
	4096R/6092693E 2011-09-23
	Key fingerprint = 647F 2865 4894 E3BD 4571  99BE 38DB BDC8 6092 693E
	Greg Kroah-Hartman (Linux kernel stable release signing key) <greg@kroah.com>

The 3.0.5 and 3.0.6 releases are there as well if people want them.

I'll work on the older releases as we get time, that shouldn't be that
pressing of an issue at the moment.

Thanks so much to John and hpa for working hard on the tools and backend
configuration to make this possible.

thanks,

greg k-h

^ permalink raw reply

* Re: [RFC] subdevice PM: .s_power() deprecation?
From: Sylwester Nawrocki @ 2011-10-23  8:35 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Guennadi Liakhovetski, Laurent Pinchart, Linux Media Mailing List,
	Tomasz Stanislawski
In-Reply-To: <4EA3CB48.5000203@iki.fi>

Hi Sakari,

On 10/23/2011 10:07 AM, Sakari Ailus wrote:
> Sylwester Nawrocki wrote:
> ...
>>> I understand what you're saying, but can you give us a specific example,
>>> when a subdev driver (your SoC internal subdev, that is) doesn't have a
>>> way to react to an event itself and only the bridge driver gets called
>>> into at that time? Something like an interrupt or an internal timer or
>>> some other internal event?
>>
>> 1. The S5P SoC video output subsystem (http://lwn.net/Articles/449661) comprises
>>   of multiple logical blocks, like Video Processor, Mixer, HDMI, HDMI PHY, SD TV Out.
>>   For instance the master video clock is during normal operation derived from
>>   (synchronized to, with PLL,) the HDMI-PHY output clock. The host driver can
>>   switch to this clock only when the HDMI-PHY (subdev) power and clocks are enabled.
>>   And it should be done before .s_stream(), to do some H/W configuration earlier
>>   in the pipeline, before streaming is enabled. Perhaps Tomasz could give some
>>   further explanation of what the s_power() op does and why in the driver.
>>
>> 2. In some of our camera pipeline setups - "Sensor - MIPI-CSI receiver - host/DMA",
>>   the sensor won't boot properly if all MIPI-CSI regulators aren't enabled. So the
>>   MIPI-CSI receiver must always be powered on before the sensor. With the subdevs
>>   doing their own magic wrt to power control the situation is getting slightly
>>   out of control.
> 
> How about this: CSI-2 receiver implements a few new regulators which the
> sensor driver then requests to be enabled. Would that work for you?

No, I don't like that... :)

We would have to standardize the regulator supply names, etc. Such approach
would be more difficult to align with runtime/system suspend/resume.
Also the sensor drivers should be independent on other drivers. The MIPI-CSI
receiver is more specific to the host, rather than a sensor.

Not all sensors need MIPI-CSI, some just use parallel video bus.

> 
>>>> I guess we all agree the power requirements of external subdevs are generally
>>>> unknown to the hosts.
>>>>
>>>> For these it might make lot of sense to let the subdev driver handle the device
>>>> power supplies on basis of requests like, s_ctrl, s_stream, etc.
>>>
>>> Yes, right, so, most "external" (sensor, decoder,...) subdev drivers
>>> should never need to implement .s_power(), regardless of whether we decide
>>> to keep it or not. Well, ok, no, put it differently - in those drivers
>>> .s_power() should only really be called during system-wide suspend /
>>> resume.
>>
>> Yes, I agree with that. But before we attempt to remove .s_power() or deprecate
>> it on "external" subdevs, I'd like to get solved the issue with sensor master clock
>> provided by the bridge. As these two are closely related - the sensor controller
>> won't boot if the clock is disabled. And there are always advantages of not keeping
>> the clock always enabled.
> 
> I guess we'll need to wait awhile before the clock framework would
> support this. I don't know what's the status of this; probably worth
> checking.

Last time I checked, a reference platform migration to common clk struct was
being prepared for OMAP.
So hopefully we are close to the agreement. I think there is a speech about
this during ELCE.

--
Regards,
Sylwester

^ permalink raw reply

* Re: a bug when execute "git status" in git version 1.7.7.431.g89633
From: John Hsing @ 2011-10-23  8:35 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: git
In-Reply-To: <vpqfwiknmh3.fsf@bauges.imag.fr>

ok,when i finish compiling git 1.7.7.431.g89633,I use it to check 
Cyanogenod(an Android mod source) by “git status”,it happend this 
error!but when i reuse git v1.7.7,it is OK!so i think it is a bug in 
git 1.7.7.431.g89633!My OS is Ubuntu Linux 10.10,sorry for my bad 
english! If you want to reproduce this error,please excute "git status" 
in https://github.com/CyanogenMod/android_packages_apps_DSPManager.git 
repo!

于2011年10月23日 星期日 16时25分28秒,Matthieu Moy写到:
> John Hsing <tsyj2007@gmail.com> writes:
>
>> the error:
>
> When does this error occur? is it reproducible on any repository? Which
> OS?
>
>> git: malloc.c:3096: sYSMALLOc: Assertion `(old_top == (((mbinptr)
>> (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct
>> malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >=
>> (unsigned long)((((__builtin_offsetof (struct malloc_chunk,
>> fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & ~((2 * (sizeof(size_t))) -
>> 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask)
>> == 0)' failed.
>
> The assertion is violated outside the source code of Git itself, so
> either Git calls malloc incorrectly, or this is a bug in your libc.
>

^ permalink raw reply

* [U-Boot] [RESEND PATCH v3] Add assert() for debug assertions
From: Wolfgang Denk @ 2011-10-23  8:35 UTC (permalink / raw)
  To: u-boot
In-Reply-To: <CAPnjgZ1OYiqTT0Y+mWnkdh7ZZWruXy5n0atB6e4CSjuQkQcvKw@mail.gmail.com>

Dear Simon Glass,

In message <CAPnjgZ1OYiqTT0Y+mWnkdh7ZZWruXy5n0atB6e4CSjuQkQcvKw@mail.gmail.com> you wrote:
>
> > Can you please #ifdef it so it doesn't get added for the non-debug
> > case, too?
> 
> Well I could, but then if someone defines DEBUG in a file they will
> get a link error I think. Or are you thinking of adding a new CONFIG
> which *permits* people to use DEBUG?

I don't understand what you mean.

__assert_fail() only ever gets used (and thus needs to be compiled in)
if DEBUG is defined, right?  SO why cannot you wrap the code in a
"#ifdef DEBUG" ?

Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
"There's only one kind of woman ..." "Or man, for  that  matter.  You
either believe in yourself or you don't."
	-- Kirk and Harry Mudd, "Mudd's Women", stardate 1330.1

^ permalink raw reply

* Re: [22/27] VFS: Fix automount for negative autofs dentries
From: Greg KH @ 2011-10-23  8:36 UTC (permalink / raw)
  To: Linus Torvalds, David Howells, Miklos Szeredi
  Cc: linux-kernel, stable, stable-review, akpm, alan, Ian Kent,
	Al Viro, Chuck Ebbert, Trond Myklebust
In-Reply-To: <CA+55aFxkHr75LZU0udv=A+Bg52fMMOEfp47TTxHGbgf51UouRg@mail.gmail.com>

On Sun, Oct 23, 2011 at 10:35:14AM +0300, Linus Torvalds wrote:
> This one is debatable.
> 
> I think it also wants
> 
>  - commit 0ec26fd0698285b31248e34bf1abb022c00f23d6 (with
> LOOKUP_PARENT->LOOKUP_CONTINUE too)

Really?  I thought I dropped this one from the last stable release due
to problems with it, or does this patch here, and the one you mention
below, resolve those issues?

>  - commit d94c177beeb4469cd4f6e83354ab0223353e98ed (to get the few
> other cases right too)
> 
> anything else I missed?

Ok, that makes a bit more sense, I'll add these if David and Miklos
agree that they are ok to add here.  David and Miklos?

greg k-h

^ permalink raw reply

* Re: [PATCH 1/3] mfd: Add S5M core driver
From: Mark Brown @ 2011-10-23  8:37 UTC (permalink / raw)
  To: Sangbeom Kim; +Cc: 'Samuel Ortiz', linux-kernel
In-Reply-To: <009201cc915a$cae8f290$60bad7b0$@com>

On Sun, Oct 23, 2011 at 05:07:22PM +0900, Sangbeom Kim wrote:

>  drivers/mfd/s5m-core.c               |  235 +++++++++++++++++++++++++
>  include/linux/mfd/s5m87xx/s5m-core.h |  313
> ++++++++++++++++++++++++++++++++++

Naughty Outlook!

> +int s5m_reg_read(struct s5m87xx_dev *s5m87xx, u8 reg, u8 *dest)
> +{
> +	int ret;
> +	unsigned int val;
> +
> +	ret = regmap_read(s5m87xx->regmap, reg, &val);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	ret &= 0xff;
> +	*dest = ret;

This should be manipulating val rather than ret.  You shouldn't need the
&= 0xff bit but it's understandable for paranoia.

Actually, looking at this what I'm thinking is that we should put some
inline functions in the regmap header which let you write this as
something like

	int s5m_reg_read(struct s5m87xx_dev *s5m87xx, u8 reg, u8 *dest)
	{
		return regmap_read_u8(s5m87xx->dev, reg, dest);
	}

(which might be inline itself) as most of what you're doing here is the
type conversion for the arguments.

> +EXPORT_SYMBOL(s5m_bulk_read);

All this stuff should be _GPL as the regmap core is _GPL - you shouldn't
wrap a _GPL function with a non-GPL one.

> +static struct mfd_cell s5m87xx_devs[] = {
> +	{
> +		.name = "s5m8763-pmic",
> +	}, {
> +		.name = "s5m8767-pmic",
> +	}, {

It looks a bit odd to have both simultaneously but I guess this will
become more obvious later on?  I guess what I'd expect is one of these
arrays per device variant.

> +	s5m87xx->dev = &i2c->dev;
> +	s5m87xx->i2c = i2c;
> +	s5m87xx->irq = i2c->irq;

Is SPI supported?

> +	dev_info(s5m87xx->dev ,"SAMSUNG S5M MFD\n");

Probably best to remove this for mainline, it's not really adding
anything.

> +static const struct i2c_device_id s5m87xx_i2c_id[] = {
> +	{ "s5m87xx", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id);

It'd be better to have one entry per chip explicitly naming the device,
that way boards are describing which they have and the kernel can apply
any device specific configuration.

> +static int s5m_suspend(struct i2c_client *i2c, pm_message_t state)
> +{
> +	struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
> +
> +	if (s5m87xx->wakeup)
> +		enable_irq_wake(s5m87xx->irq);
> +
> +	disable_irq(s5m87xx->irq);

Enabling wake and disabling the IRQ looks odd...  perhaps an else?

^ permalink raw reply

* Re: git mergetool ignores configured command line
From: Andreas Schwab @ 2011-10-23  8:37 UTC (permalink / raw)
  To: Alexander Vladimirov; +Cc: git
In-Reply-To: <CAARCrw6D7CKy2Jn43zUZ3EefyqdY6Tk4A39ZQ74H6hySA5eCBQ@mail.gmail.com>

Alexander Vladimirov <alexander.idkfa.vladimirov@gmail.com> writes:

> To configure diffuse as merge tool I set mergetool configuration using
> following commands:
>
> git config --global merge.tool diffuse
> git config --global mergetool.diffuse.cmd '/usr/bin/diffuse "$LOCAL"
> "$MERGED" "$REMOTE"'
>
> Then, when I invoke mergetool during merge, I get diffuse with four
> panes open as by default, instead of three specified in command line
> setting.
> Is this an expected behavior, and how can I force mergetool to use
> provided command line?

diffuse is already a predefined merge tool, so mergetool.diffuse.cmd is
ignored.  Try using a different name.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."

^ permalink raw reply

* Re: [linux-lvm] Changing dev_t-devname mapping in lvmlib seems to be problematic
From: Zdenek Kabelac @ 2011-10-23  8:39 UTC (permalink / raw)
  To: Alexander Lyakas; +Cc: LVM general discussion and development
In-Reply-To: <CAGRgLy6mHYGGS-ydGCx_mWGuKE31TLu5xnAzkAb6ZoEHjzPW+A@mail.gmail.com>

2011/10/18 Alexander Lyakas <alex.bolshoy@gmail.com>:
> Hello Zdenek,
> I am testing a following scenario:
>
> I have 5 dm-linear devices, which I have setup manually using dmsetup.
> Their table points at some local disks, like this:
> /dev/mapper/alex0 => /dev/sda
> /dev/mapper/alex1 => /dev/sdb
>
> I create a VG on top of these 5 dm devices, using pvcreate and then
> lvmlib APIs. The VG has no LVs at this point.

Not really sure about lvmlib API capabilities - IMHO I'd not use it
for anything else then lvs-like operations ATM.  (since there are
quite a few rules to avoid deadlocks) If it's not a problem for you
app I'd suggest to use lvm2cmd library preferable in a separate forked
small process so there could be full control over memory and file
descriptors....

> Later I teardown all the dm devices (using dmsetup remove).
> Then I recreate the 5 dm devices, I give them the same names and setup
> the same linear tables.
>
> The difference is that I create them in different order.
> So, for example, previously I had /dev/mapper/alex0 pointing at
> /dev/sda, and it's real devnode was /dev/dm-0 (251:0), now
> /dev/mapper/alex0 still points at /dev/sda, but its devnode is
> /dev/dm-1 (251:1).
> The names that I feed to LVM are always �/dev/mapper/alex0,
> /dev/mapper/alex1 ...

dm-xxx devices are created dynamicaly - there is currently no way to
have a fixed dm device node and it would be quite ugly to provide such
feature.

So at dm level - you could use   /dev/mapper/devicename however at lvm
level the only supported way  is /dev/vg/lv
(even though they are visible through /dev/mapper  - only /dev/vg  are
meant to be 'public')

>
> The issue that I see is in _cache.devices handling: it maps devt to
> 'struct device*' objects. So when searching for 251:1, a stale entry
> for 251:1 is found (former /dev/mapper/alex1). So it contains an old
> list of aliases in dev->aliases, and now a new name is added there, so
> it contains now both /dev/mapper/alex0 and /dev/mapper/alex1 (and also
> other names)...
>
> In addition, this entry has dev->pvid of the /dev/mapper/alex1 PV.

Could be there is some bug in there - could you post a some simple
source file to expose this bug?

>
> Further I see a call to lvmcache_add with the following parameters:
> pvid = correct pvid of /dev/mapper/alex0 (pointing at /dev/sda)
> dev->pvid = the pvid of /dev/mapper/alex1
>
> As a result, the _pvid_hash gets messed up...basically it ends up
> having 4 PVs instead of 5. I am attaching a text file, which traces
> the contents of the _pvid_hash during lvmcache_add call (I added some
> prints there).
>
> So it looks like having an open lib_t handle cannot survive such a
> change in dev_t.

Maybe you could try to open bugzilla - where you would attach source file,
lvm.conf  and version you are trying to use.

>
> I have a couple of questions:
> - Is my analysis (at least more or less) correct?
> - Is this generally a bad idea to have dm-linear devices for PVs? (I
> guarantee that dm-linear table is always setup correctly).

It's perfecly normal usage - but it depends on your  lvm.conf filter settings
(i.e. if you will allow only   /dev/sdX devices,  then  /dev/mapper/
devices will be invisible)


> - Will using a fresh lib_t handle for each LVM operation solve this
> issue? Because the command-line tools, when invoked, seem to work fine
> (and they build a fresh cache each time). I will guarantee that nobody
> else touches relevant dm devices during the LVM operation.

Yes, there is locking, so as long as you are using only lvm tools,
there should be no collisions.

> - I also see that LVM scans devices like /dev/disk/by-id... while in
> lvm.conf I set the filter to accept only /dev/mapper/alex (all others
> I set to reject). What am I missing?

Recent versions of lvm should be getting list of block devices for
scanning from udev, and then more filters are applied.

Zdenek

^ permalink raw reply

* Re: [PATCH 2/2] pinctrl: add a generic control interface
From: Shawn Guo @ 2011-10-23  8:51 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Barry Song, Linus Walleij, linux-kernel, Stephen Warren,
	Linaro Dev, Sascha Hauer, David Brown, Grant Likely
In-Reply-To: <CACRpkdYah+MSPCcHKhD+Wh11u2V3XR_3T8aCFxKpL0Znh+gh9g@mail.gmail.com>

On Thu, Oct 20, 2011 at 04:18:49PM +0200, Linus Walleij wrote:
> On Thu, Oct 20, 2011 at 3:10 PM, Shawn Guo <shawn.guo@freescale.com> wrote:
> 
> >> without the common definition from  PIN_CONFIG_PULL to
> >> PIN_CONFIG_USER, many platforms will need to definite them repeatedly.
> >> that is what we hate.
> >>
> > I prefer to completely leave the encoding of this 'u32 param' to pinctrl
> > drivers, so that they can map the bit definition of 'param' to the
> > controller register as much as they can. On imx, we have one register
> > accommodating all pin configuration options for one pin, so we can
> > write 'param' directly into register with exactly same bit definition
> > as register.
> 
> I understand this argument, because it makes it more convenient to
> write drivers and you can cut a few corners.
> 
> However I would prefer to keep things more abstract, and the reason
> is that we strive to have drivers reused across SoCs. For example
> drivers/tty/serial/amba-pl011.c is used on a multitude of SoCs,
> U300, Nomadik, Ux500, ARM Versatile, ARM Vexpress, LPC32XX
> etc etc.
> 
> What if this common driver suddenly need to bias a pin? Is it:
> 
> #include <linus/pinctrl/pinctrl.h>
> 
> ret = pin_config(pctldev, pin, PIN_CONFIG_BIAS_HIGH, 0);
> 
> Or (OK silly example but you get the point):
> 
> #include <linus/pinctrl/pinctrl.h>
> 
> if (machine_is_u300())
>     ret = pin_config(pctldev, pin, PIN_CONFIG_U300_BIAS_VDD, 0);
> else if (machine_is_nomadik())
>     ret = pin_config(pctldev, pin, PIN_CONFIG_NOMADIK_BIAS_UP, 0);
> else if (machine_is_ux500())
>     ret = pin_config(pctldev, pin, PIN_CONFIG_UX500_PULLHIGH, 0);
> else if (machine_is_versatile())
>     ret = pin_config(pctldev, pin, PIN_CONFIG_VERSATILE_POW, 0);
> else if (machine_is_vexpress())
>     ret = pin_config(pctldev, pin, PIN_CONFIG_VEXPRESS_XTRA, 0);
> else if (machine_is_lpc32xx())
>     ret = pin_config(pctldev, pin, PIN_CONFIG_LPC_SUPPLY, 0);
> ...
> 
> IMO we have enough strange and custom things in the kernel
> already and we need to make things more abstract, not the least
> so that people can understand what the code is doing.
> 
To me, pin_config() and its parameters should be invisible to client
drivers.  For amba-pl011 example, do you think any of those SoCs will
need multiple sets of uart pin configurations to switch for different
working modes?  If that's case, the individual SoC pinctrl driver
should be responsible for mapping particular pin configuration to
specific pl011 working mode.  Otherwise, the amba-pl011 can simply
call pinmux_enable(...) to have both mux and cfg set up.

-- 
Regards,
Shawn


^ permalink raw reply

* [patch net-next V3] net: introduce ethernet teaming device
From: Jiri Pirko @ 2011-10-23  8:40 UTC (permalink / raw)
  To: netdev
  Cc: davem, eric.dumazet, bhutchings, shemminger, fubar, andy, tgraf,
	ebiederm, mirqus, kaber, greearb, jesse, fbl, benjamin.poirier,
	jzupka

This patch introduces new network device called team. It supposes to be
very fast, simple, userspace-driven alternative to existing bonding
driver.

Userspace library called libteam with couple of demo apps is available
here:
https://github.com/jpirko/libteam
Note it's still in its dipers atm.

team<->libteam use generic netlink for communication. That and rtnl
suppose to be the only way to configure team device, no sysfs etc.

Python binding basis for libteam was recently introduced (some need
still need to be done on it though). Daemon providing arpmon/miimon
active-backup functionality will be introduced shortly.
All what's necessary is already implemented in kernel team driver.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>

v2->v3:
	- team_change_mtu() user rcu version of list traversal to unwind
	- set and clear of mode_ops happens per pointer, not per byte
	- port hashlist changed to be embedded into team structure
	- error branch in team_port_enter() does cleanup now
	- fixed rtln->rtnl

v1->v2:
	- modes are made as modules. Makes team more modular and
	  extendable.
	- several commenters' nitpicks found on v1 were fixed
	- several other bugs were fixed.
	- note I ignored Eric's comment about roundrobin port selector
	  as Eric's way may be easily implemented as another mode (mode
	  "random") in future.
---
 Documentation/networking/team.txt         |    2 +
 MAINTAINERS                               |    7 +
 drivers/net/Kconfig                       |    2 +
 drivers/net/Makefile                      |    1 +
 drivers/net/team/Kconfig                  |   38 +
 drivers/net/team/Makefile                 |    7 +
 drivers/net/team/team.c                   | 1574 +++++++++++++++++++++++++++++
 drivers/net/team/team_mode_activebackup.c |  152 +++
 drivers/net/team/team_mode_roundrobin.c   |  107 ++
 include/linux/Kbuild                      |    1 +
 include/linux/if.h                        |    1 +
 include/linux/if_team.h                   |  254 +++++
 include/linux/rculist.h                   |   14 +
 13 files changed, 2160 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/networking/team.txt
 create mode 100644 drivers/net/team/Kconfig
 create mode 100644 drivers/net/team/Makefile
 create mode 100644 drivers/net/team/team.c
 create mode 100644 drivers/net/team/team_mode_activebackup.c
 create mode 100644 drivers/net/team/team_mode_roundrobin.c
 create mode 100644 include/linux/if_team.h

diff --git a/Documentation/networking/team.txt b/Documentation/networking/team.txt
new file mode 100644
index 0000000..5a01368
--- /dev/null
+++ b/Documentation/networking/team.txt
@@ -0,0 +1,2 @@
+Team devices are driven from userspace via libteam library which is here:
+	https://github.com/jpirko/libteam
diff --git a/MAINTAINERS b/MAINTAINERS
index 5008b08..c33400d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6372,6 +6372,13 @@ W:	http://tcp-lp-mod.sourceforge.net/
 S:	Maintained
 F:	net/ipv4/tcp_lp.c
 
+TEAM DRIVER
+M:	Jiri Pirko <jpirko@redhat.com>
+L:	netdev@vger.kernel.org
+S:	Supported
+F:	drivers/net/team/
+F:	include/linux/if_team.h
+
 TEGRA SUPPORT
 M:	Colin Cross <ccross@android.com>
 M:	Erik Gilling <konkers@android.com>
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 583f66c..b3020be 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -125,6 +125,8 @@ config IFB
 	  'ifb1' etc.
 	  Look at the iproute2 documentation directory for usage etc
 
+source "drivers/net/team/Kconfig"
+
 config MACVLAN
 	tristate "MAC-VLAN support (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index fa877cd..4e4ebfe 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_NET) += Space.o loopback.o
 obj-$(CONFIG_NETCONSOLE) += netconsole.o
 obj-$(CONFIG_PHYLIB) += phy/
 obj-$(CONFIG_RIONET) += rionet.o
+obj-$(CONFIG_NET_TEAM) += team/
 obj-$(CONFIG_TUN) += tun.o
 obj-$(CONFIG_VETH) += veth.o
 obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
diff --git a/drivers/net/team/Kconfig b/drivers/net/team/Kconfig
new file mode 100644
index 0000000..70a43a6
--- /dev/null
+++ b/drivers/net/team/Kconfig
@@ -0,0 +1,38 @@
+menuconfig NET_TEAM
+	tristate "Ethernet team driver support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	---help---
+	  This allows one to create virtual interfaces that teams together
+	  multiple ethernet devices.
+
+	  Team devices can be added using the "ip" command from the
+	  iproute2 package:
+
+	  "ip link add link [ address MAC ] [ NAME ] type team"
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called team.
+
+if NET_TEAM
+
+config NET_TEAM_MODE_ROUNDROBIN
+	tristate "Round-robin mode support"
+	depends on NET_TEAM
+	---help---
+	  Basic mode where port used for transmitting packets is selected in
+	  round-robin fashion using packet counter.
+
+	  To compile this team mode as a module, choose M here: the module
+	  will be called team_mode_roundrobin.
+
+config NET_TEAM_MODE_ACTIVEBACKUP
+	tristate "Active-backup mode support"
+	depends on NET_TEAM
+	---help---
+	  Only one port is active at a time and the rest of ports are used
+	  for backup.
+
+	  To compile this team mode as a module, choose M here: the module
+	  will be called team_mode_activebackup.
+
+endif # NET_TEAM
diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile
new file mode 100644
index 0000000..85f2028
--- /dev/null
+++ b/drivers/net/team/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the network team driver
+#
+
+obj-$(CONFIG_NET_TEAM) += team.o
+obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o
+obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
new file mode 100644
index 0000000..8004916
--- /dev/null
+++ b/drivers/net/team/team.c
@@ -0,0 +1,1574 @@
+/*
+ * net/drivers/team/team.c - Network team device driver
+ * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/rcupdate.h>
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/socket.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/rtnetlink.h>
+#include <net/genetlink.h>
+#include <net/netlink.h>
+#include <linux/if_team.h>
+
+#define DRV_NAME "team"
+
+
+/**********
+ * Helpers
+ **********/
+
+#define team_port_exists(dev) (dev->priv_flags & IFF_TEAM_PORT)
+
+static struct team_port *team_port_get_rcu(const struct net_device *dev)
+{
+	struct team_port *port = rcu_dereference(dev->rx_handler_data);
+
+	return team_port_exists(dev) ? port : NULL;
+}
+
+static struct team_port *team_port_get_rtnl(const struct net_device *dev)
+{
+	struct team_port *port = rtnl_dereference(dev->rx_handler_data);
+
+	return team_port_exists(dev) ? port : NULL;
+}
+
+/*
+ * Since the ability to change mac address for open port device is tested in
+ * team_port_add, this function can be called without control of return value
+ */
+static int __set_port_mac(struct net_device *port_dev,
+			  const unsigned char *dev_addr)
+{
+	struct sockaddr addr;
+
+	memcpy(addr.sa_data, dev_addr, ETH_ALEN);
+	addr.sa_family = ARPHRD_ETHER;
+	return dev_set_mac_address(port_dev, &addr);
+}
+
+int team_port_set_orig_mac(struct team_port *port)
+{
+	return __set_port_mac(port->dev, port->orig.dev_addr);
+}
+EXPORT_SYMBOL(team_port_set_orig_mac);
+
+int team_port_set_team_mac(struct team_port *port)
+{
+	return __set_port_mac(port->dev, port->team->dev->dev_addr);
+}
+EXPORT_SYMBOL(team_port_set_team_mac);
+
+
+/*******************
+ * Options handling
+ *******************/
+
+void team_options_register(struct team *team, struct team_option *option,
+			   size_t option_count)
+{
+	int i;
+
+	for (i = 0; i < option_count; i++, option++)
+		list_add_tail(&option->list, &team->option_list);
+}
+EXPORT_SYMBOL(team_options_register);
+
+static void __team_options_change_check(struct team *team,
+					struct team_option *changed_option);
+
+static void __team_options_unregister(struct team *team,
+				      struct team_option *option,
+				      size_t option_count)
+{
+	int i;
+
+	for (i = 0; i < option_count; i++, option++)
+		list_del(&option->list);
+}
+
+void team_options_unregister(struct team *team, struct team_option *option,
+			     size_t option_count)
+{
+	__team_options_unregister(team, option, option_count);
+	__team_options_change_check(team, NULL);
+}
+EXPORT_SYMBOL(team_options_unregister);
+
+static int team_option_get(struct team *team, struct team_option *option,
+			   void *arg)
+{
+	return option->getter(team, arg);
+}
+
+static int team_option_set(struct team *team, struct team_option *option,
+			   void *arg)
+{
+	int err;
+
+	err = option->setter(team, arg);
+	if (err)
+		return err;
+
+	__team_options_change_check(team, option);
+	return err;
+}
+
+/****************
+ * Mode handling
+ ****************/
+
+static LIST_HEAD(mode_list);
+static DEFINE_SPINLOCK(mode_list_lock);
+
+static struct team_mode *__find_mode(const char *kind)
+{
+	struct team_mode *mode;
+
+	list_for_each_entry(mode, &mode_list, list) {
+		if (strcmp(mode->kind, kind) == 0)
+			return mode;
+	}
+	return NULL;
+}
+
+static bool is_good_mode_name(const char *name)
+{
+	while (*name != '\0') {
+		if (!isalpha(*name) && !isdigit(*name) && *name != '_')
+			return false;
+		name++;
+	}
+	return true;
+}
+
+int team_mode_register(struct team_mode *mode)
+{
+	int err = 0;
+
+	if (!is_good_mode_name(mode->kind) ||
+	    mode->priv_size > TEAM_MODE_PRIV_SIZE)
+		return -EINVAL;
+	spin_lock(&mode_list_lock);
+	if (__find_mode(mode->kind)) {
+		err = -EEXIST;
+		goto unlock;
+	}
+	list_add_tail(&mode->list, &mode_list);
+unlock:
+	spin_unlock(&mode_list_lock);
+	return err;
+}
+EXPORT_SYMBOL(team_mode_register);
+
+int team_mode_unregister(struct team_mode *mode)
+{
+	spin_lock(&mode_list_lock);
+	list_del_init(&mode->list);
+	spin_unlock(&mode_list_lock);
+	return 0;
+}
+EXPORT_SYMBOL(team_mode_unregister);
+
+static struct team_mode *team_mode_get(const char *kind)
+{
+	struct team_mode *mode;
+
+	spin_lock(&mode_list_lock);
+	mode = __find_mode(kind);
+	if (!mode) {
+		spin_unlock(&mode_list_lock);
+		request_module("team-mode-%s", kind);
+		spin_lock(&mode_list_lock);
+		mode = __find_mode(kind);
+	}
+	if (mode)
+		if (!try_module_get(mode->owner))
+			mode = NULL;
+
+	spin_unlock(&mode_list_lock);
+	return mode;
+}
+
+static void team_mode_put(const char *kind)
+{
+	struct team_mode *mode;
+
+	spin_lock(&mode_list_lock);
+	mode = __find_mode(kind);
+	BUG_ON(!mode);
+	module_put(mode->owner);
+	spin_unlock(&mode_list_lock);
+}
+
+/*
+ * We can benefit from the fact that it's ensured no port is present
+ * at the time of mode change.
+ */
+static int __team_change_mode(struct team *team,
+			      const struct team_mode *new_mode)
+{
+	/* Check if mode was previously set and do cleanup if so */
+	if (team->mode_kind) {
+		void (*exit_op)(struct team *team) = team->mode_ops.exit;
+
+		/* Clear ops area so no callback is called any longer */
+		team_mode_ops_clear(&team->mode_ops);
+
+		synchronize_rcu();
+
+		if (exit_op)
+			exit_op(team);
+		team_mode_put(team->mode_kind);
+		team->mode_kind = NULL;
+		/* zero private data area */
+		memset(&team->mode_priv, 0,
+		       sizeof(struct team) - offsetof(struct team, mode_priv));
+	}
+
+	if (!new_mode)
+		return 0;
+
+	if (new_mode->ops->init) {
+		int err;
+
+		err = new_mode->ops->init(team);
+		if (err)
+			return err;
+	}
+
+	team->mode_kind = new_mode->kind;
+	team_mode_ops_copy(&team->mode_ops, new_mode->ops);
+
+	return 0;
+}
+
+static int team_change_mode(struct team *team, const char *kind)
+{
+	struct team_mode *new_mode;
+	struct net_device *dev = team->dev;
+	int err;
+
+	if (!list_empty(&team->port_list)) {
+		netdev_err(dev, "No ports can be present during mode change\n");
+		return -EBUSY;
+	}
+
+	if (team->mode_kind && strcmp(team->mode_kind, kind) == 0) {
+		netdev_err(dev, "Unable to change to the same mode the team is in\n");
+		return -EINVAL;
+	}
+
+	new_mode = team_mode_get(kind);
+	if (!new_mode) {
+		netdev_err(dev, "Mode \"%s\" not found\n", kind);
+		return -EINVAL;
+	}
+
+	err = __team_change_mode(team, new_mode);
+	if (err) {
+		netdev_err(dev, "Failed to change to mode \"%s\"\n", kind);
+		team_mode_put(kind);
+		return err;
+	}
+
+	netdev_info(dev, "Mode changed to \"%s\"\n", kind);
+	return 0;
+}
+
+
+/************************
+ * Rx path frame handler
+ ************************/
+
+/* note: already called with rcu_read_lock */
+static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
+{
+	struct sk_buff *skb = *pskb;
+	struct team_port *port;
+	struct team *team;
+	rx_handler_result_t res = RX_HANDLER_ANOTHER;
+
+	skb = skb_share_check(skb, GFP_ATOMIC);
+	if (!skb)
+		return RX_HANDLER_CONSUMED;
+
+	*pskb = skb;
+
+	port = team_port_get_rcu(skb->dev);
+	team = port->team;
+
+	if (team->mode_ops.receive)
+		res = team->mode_ops.receive(team, port, skb);
+
+	if (res == RX_HANDLER_ANOTHER) {
+		struct team_pcpu_stats *pcpu_stats;
+
+		pcpu_stats = this_cpu_ptr(team->pcpu_stats);
+		u64_stats_update_begin(&pcpu_stats->syncp);
+		pcpu_stats->rx_packets++;
+		pcpu_stats->rx_bytes += skb->len;
+		if (skb->pkt_type == PACKET_MULTICAST)
+			pcpu_stats->rx_multicast++;
+		u64_stats_update_end(&pcpu_stats->syncp);
+
+		skb->dev = team->dev;
+	} else {
+		this_cpu_inc(team->pcpu_stats->rx_dropped);
+	}
+
+	return res;
+}
+
+
+/****************
+ * Port handling
+ ****************/
+
+static bool team_port_find(const struct team *team,
+			   const struct team_port *port)
+{
+	struct team_port *cur;
+
+	list_for_each_entry(cur, &team->port_list, list)
+		if (cur == port)
+			return true;
+	return false;
+}
+
+/*
+ * Add/delete port to the team port list. Write guarded by rtnl_lock.
+ * Takes care of correct port->index setup (might be racy).
+ */
+static void team_port_list_add_port(struct team *team,
+				    struct team_port *port)
+{
+	port->index = team->port_count++;
+	hlist_add_head_rcu(&port->hlist,
+			   team_port_index_hash(team, port->index));
+	list_add_tail_rcu(&port->list, &team->port_list);
+}
+
+static void __reconstruct_port_hlist(struct team *team, int rm_index)
+{
+	int i;
+	struct team_port *port;
+
+	for (i = rm_index + 1; i < team->port_count; i++) {
+		port = team_get_port_by_index_rcu(team, i);
+		hlist_del_rcu(&port->hlist);
+		port->index--;
+		hlist_add_head_rcu(&port->hlist,
+				   team_port_index_hash(team, port->index));
+	}
+}
+
+static void team_port_list_del_port(struct team *team,
+				   struct team_port *port)
+{
+	int rm_index = port->index;
+
+	hlist_del_rcu(&port->hlist);
+	list_del_rcu(&port->list);
+	__reconstruct_port_hlist(team, rm_index);
+	team->port_count--;
+}
+
+#define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \
+			    NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
+			    NETIF_F_HIGHDMA | NETIF_F_LRO)
+
+static void __team_compute_features(struct team *team)
+{
+	struct team_port *port;
+	u32 vlan_features = TEAM_VLAN_FEATURES;
+	unsigned short max_hard_header_len = ETH_HLEN;
+
+	list_for_each_entry(port, &team->port_list, list) {
+		vlan_features = netdev_increment_features(vlan_features,
+					port->dev->vlan_features,
+					TEAM_VLAN_FEATURES);
+
+		if (port->dev->hard_header_len > max_hard_header_len)
+			max_hard_header_len = port->dev->hard_header_len;
+	}
+
+	team->dev->vlan_features = vlan_features;
+	team->dev->hard_header_len = max_hard_header_len;
+
+	netdev_change_features(team->dev);
+}
+
+static void team_compute_features(struct team *team)
+{
+	spin_lock(&team->lock);
+	__team_compute_features(team);
+	spin_unlock(&team->lock);
+}
+
+static int team_port_enter(struct team *team, struct team_port *port)
+{
+	int err = 0;
+
+	dev_hold(team->dev);
+	port->dev->priv_flags |= IFF_TEAM_PORT;
+	if (team->mode_ops.port_enter) {
+		err = team->mode_ops.port_enter(team, port);
+		if (err) {
+			netdev_err(team->dev, "Device %s failed to enter team mode\n",
+				   port->dev->name);
+			goto err_port_enter;
+		}
+	}
+
+	return 0;
+
+err_port_enter:
+	port->dev->priv_flags &= ~IFF_TEAM_PORT;
+	dev_put(team->dev);
+
+	return err;
+}
+
+static void team_port_leave(struct team *team, struct team_port *port)
+{
+	if (team->mode_ops.port_leave)
+		team->mode_ops.port_leave(team, port);
+	port->dev->priv_flags &= ~IFF_TEAM_PORT;
+	dev_put(team->dev);
+}
+
+static void __team_port_change_check(struct team_port *port, bool linkup);
+
+static int team_port_add(struct team *team, struct net_device *port_dev)
+{
+	struct net_device *dev = team->dev;
+	struct team_port *port;
+	char *portname = port_dev->name;
+	char tmp_addr[ETH_ALEN];
+	int err;
+
+	if (port_dev->flags & IFF_LOOPBACK ||
+	    port_dev->type != ARPHRD_ETHER) {
+		netdev_err(dev, "Device %s is of an unsupported type\n",
+			   portname);
+		return -EINVAL;
+	}
+
+	if (team_port_exists(port_dev)) {
+		netdev_err(dev, "Device %s is already a port "
+				"of a team device\n", portname);
+		return -EBUSY;
+	}
+
+	if (port_dev->flags & IFF_UP) {
+		netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n",
+			   portname);
+		return -EBUSY;
+	}
+
+	port = kzalloc(sizeof(struct team_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->dev = port_dev;
+	port->team = team;
+
+	port->orig.mtu = port_dev->mtu;
+	err = dev_set_mtu(port_dev, dev->mtu);
+	if (err) {
+		netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err);
+		goto err_set_mtu;
+	}
+
+	memcpy(port->orig.dev_addr, port_dev->dev_addr, ETH_ALEN);
+	random_ether_addr(tmp_addr);
+	err = __set_port_mac(port_dev, tmp_addr);
+	if (err) {
+		netdev_dbg(dev, "Device %s mac addr set failed\n",
+			   portname);
+		goto err_set_mac_rand;
+	}
+
+	err = dev_open(port_dev);
+	if (err) {
+		netdev_dbg(dev, "Device %s opening failed\n",
+			   portname);
+		goto err_dev_open;
+	}
+
+	err = team_port_set_orig_mac(port);
+	if (err) {
+		netdev_dbg(dev, "Device %s mac addr set failed - Device does not support addr change when it's opened\n",
+			   portname);
+		goto err_set_mac_opened;
+	}
+
+	err = team_port_enter(team, port);
+	if (err) {
+		netdev_err(dev, "Device %s failed to enter team mode\n",
+			   portname);
+		goto err_port_enter;
+	}
+
+	err = netdev_set_master(port_dev, dev);
+	if (err) {
+		netdev_err(dev, "Device %s failed to set master\n", portname);
+		goto err_set_master;
+	}
+
+	err = netdev_rx_handler_register(port_dev, team_handle_frame,
+					 port);
+	if (err) {
+		netdev_err(dev, "Device %s failed to register rx_handler\n",
+			   portname);
+		goto err_handler_register;
+	}
+
+	team_port_list_add_port(team, port);
+	__team_compute_features(team);
+	__team_port_change_check(port, !!netif_carrier_ok(port_dev));
+
+	netdev_info(dev, "Port device %s added\n", portname);
+
+	return 0;
+
+err_handler_register:
+	netdev_set_master(port_dev, NULL);
+
+err_set_master:
+	team_port_leave(team, port);
+
+err_port_enter:
+err_set_mac_opened:
+	dev_close(port_dev);
+
+err_dev_open:
+	team_port_set_orig_mac(port);
+
+err_set_mac_rand:
+	dev_set_mtu(port_dev, port->orig.mtu);
+
+err_set_mtu:
+	kfree(port);
+
+	return err;
+}
+
+static int team_port_del(struct team *team, struct net_device *port_dev)
+{
+	struct net_device *dev = team->dev;
+	struct team_port *port;
+	char *portname = port_dev->name;
+
+	port = team_port_get_rtnl(port_dev);
+	if (!port || !team_port_find(team, port)) {
+		netdev_err(dev, "Device %s does not act as a port of this team\n",
+			   portname);
+		return -ENOENT;
+	}
+
+	__team_port_change_check(port, false);
+	team_port_list_del_port(team, port);
+	netdev_rx_handler_unregister(port_dev);
+	netdev_set_master(port_dev, NULL);
+	team_port_leave(team, port);
+	dev_close(port_dev);
+	team_port_set_orig_mac(port);
+	dev_set_mtu(port_dev, port->orig.mtu);
+	synchronize_rcu();
+	kfree(port);
+	netdev_info(dev, "Port device %s removed\n", portname);
+	__team_compute_features(team);
+
+	return 0;
+}
+
+
+/*****************
+ * Net device ops
+ *****************/
+
+static const char team_no_mode_kind[] = "*NOMODE*";
+
+static int team_mode_option_get(struct team *team, void *arg)
+{
+	const char **str = arg;
+
+	*str = team->mode_kind ? team->mode_kind : team_no_mode_kind;
+	return 0;
+}
+
+static int team_mode_option_set(struct team *team, void *arg)
+{
+	const char **str = arg;
+
+	return team_change_mode(team, *str);
+}
+
+static struct team_option team_options[] = {
+	{
+		.name = "mode",
+		.type = TEAM_OPTION_TYPE_STRING,
+		.getter = team_mode_option_get,
+		.setter = team_mode_option_set,
+	},
+};
+
+static int team_init(struct net_device *dev)
+{
+	struct team *team = netdev_priv(dev);
+	int i;
+
+	team->dev = dev;
+	spin_lock_init(&team->lock);
+
+	team->pcpu_stats = alloc_percpu(struct team_pcpu_stats);
+	if (!team->pcpu_stats)
+		return -ENOMEM;
+
+	for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
+		INIT_HLIST_HEAD(&team->port_hlist[i]);
+	INIT_LIST_HEAD(&team->port_list);
+
+	INIT_LIST_HEAD(&team->option_list);
+	team_options_register(team, team_options, ARRAY_SIZE(team_options));
+	netif_carrier_off(dev);
+
+	return 0;
+}
+
+static void team_uninit(struct net_device *dev)
+{
+	struct team *team = netdev_priv(dev);
+	struct team_port *port;
+	struct team_port *tmp;
+
+	spin_lock(&team->lock);
+	list_for_each_entry_safe(port, tmp, &team->port_list, list)
+		team_port_del(team, port->dev);
+
+	__team_change_mode(team, NULL); /* cleanup */
+	__team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
+	spin_unlock(&team->lock);
+}
+
+static void team_destructor(struct net_device *dev)
+{
+	struct team *team = netdev_priv(dev);
+
+	free_percpu(team->pcpu_stats);
+	free_netdev(dev);
+}
+
+static int team_open(struct net_device *dev)
+{
+	netif_carrier_on(dev);
+	return 0;
+}
+
+static int team_close(struct net_device *dev)
+{
+	netif_carrier_off(dev);
+	return 0;
+}
+
+/*
+ * note: already called with rcu_read_lock
+ */
+static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct team *team = netdev_priv(dev);
+	bool tx_success = false;
+	unsigned int len = skb->len;
+
+	/*
+	 * Ensure transmit function is called only in case there is at least
+	 * one port present.
+	 */
+	if (likely(!list_empty(&team->port_list) && team->mode_ops.transmit))
+		tx_success = team->mode_ops.transmit(team, skb);
+	if (tx_success) {
+		struct team_pcpu_stats *pcpu_stats;
+
+		pcpu_stats = this_cpu_ptr(team->pcpu_stats);
+		u64_stats_update_begin(&pcpu_stats->syncp);
+		pcpu_stats->tx_packets++;
+		pcpu_stats->tx_bytes += len;
+		u64_stats_update_end(&pcpu_stats->syncp);
+	} else {
+		this_cpu_inc(team->pcpu_stats->tx_dropped);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+static void team_change_rx_flags(struct net_device *dev, int change)
+{
+	struct team *team = netdev_priv(dev);
+	struct team_port *port;
+	int inc;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(port, &team->port_list, list) {
+		if (change & IFF_PROMISC) {
+			inc = dev->flags & IFF_PROMISC ? 1 : -1;
+			dev_set_promiscuity(port->dev, inc);
+		}
+		if (change & IFF_ALLMULTI) {
+			inc = dev->flags & IFF_ALLMULTI ? 1 : -1;
+			dev_set_allmulti(port->dev, inc);
+		}
+	}
+	rcu_read_unlock();
+}
+
+static void team_set_rx_mode(struct net_device *dev)
+{
+	struct team *team = netdev_priv(dev);
+	struct team_port *port;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(port, &team->port_list, list) {
+		dev_uc_sync(port->dev, dev);
+		dev_mc_sync(port->dev, dev);
+	}
+	rcu_read_unlock();
+}
+
+static int team_set_mac_address(struct net_device *dev, void *p)
+{
+	struct team *team = netdev_priv(dev);
+	struct team_port *port;
+	struct sockaddr *addr = p;
+
+	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+	rcu_read_lock();
+	list_for_each_entry_rcu(port, &team->port_list, list)
+		if (team->mode_ops.port_change_mac)
+			team->mode_ops.port_change_mac(team, port);
+	rcu_read_unlock();
+	return 0;
+}
+
+static int team_change_mtu(struct net_device *dev, int new_mtu)
+{
+	struct team *team = netdev_priv(dev);
+	struct team_port *port;
+	int err;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(port, &team->port_list, list) {
+		err = dev_set_mtu(port->dev, new_mtu);
+		if (err) {
+			netdev_err(dev, "Device %s failed to change mtu",
+				   port->dev->name);
+			goto unwind;
+		}
+	}
+	rcu_read_unlock();
+
+	dev->mtu = new_mtu;
+
+	return 0;
+
+unwind:
+	list_for_each_entry_continue_reverse_rcu(port, &team->port_list, list)
+		dev_set_mtu(port->dev, dev->mtu);
+
+	rcu_read_unlock();
+	return err;
+}
+
+static struct rtnl_link_stats64 *
+team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+	struct team *team = netdev_priv(dev);
+	struct team_pcpu_stats *p;
+	u64 rx_packets, rx_bytes, rx_multicast, tx_packets, tx_bytes;
+	u32 rx_dropped = 0, tx_dropped = 0;
+	unsigned int start;
+	int i;
+
+	for_each_possible_cpu(i) {
+		p = per_cpu_ptr(team->pcpu_stats, i);
+		do {
+			start = u64_stats_fetch_begin_bh(&p->syncp);
+			rx_packets	= p->rx_packets;
+			rx_bytes	= p->rx_bytes;
+			rx_multicast	= p->rx_multicast;
+			tx_packets	= p->tx_packets;
+			tx_bytes	= p->tx_bytes;
+		} while (u64_stats_fetch_retry_bh(&p->syncp, start));
+
+		stats->rx_packets	+= rx_packets;
+		stats->rx_bytes		+= rx_bytes;
+		stats->multicast	+= rx_multicast;
+		stats->tx_packets	+= tx_packets;
+		stats->tx_bytes		+= tx_bytes;
+		/*
+		 * rx_dropped & tx_dropped are u32, updated
+		 * without syncp protection.
+		 */
+		rx_dropped	+= p->rx_dropped;
+		tx_dropped	+= p->tx_dropped;
+	}
+	stats->rx_dropped	= rx_dropped;
+	stats->tx_dropped	= tx_dropped;
+	return stats;
+}
+
+static void team_vlan_rx_add_vid(struct net_device *dev, uint16_t vid)
+{
+	struct team *team = netdev_priv(dev);
+	struct team_port *port;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(port, &team->port_list, list) {
+		const struct net_device_ops *ops = port->dev->netdev_ops;
+
+		ops->ndo_vlan_rx_add_vid(port->dev, vid);
+	}
+	rcu_read_unlock();
+}
+
+static void team_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid)
+{
+	struct team *team = netdev_priv(dev);
+	struct team_port *port;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(port, &team->port_list, list) {
+		const struct net_device_ops *ops = port->dev->netdev_ops;
+
+		ops->ndo_vlan_rx_kill_vid(port->dev, vid);
+	}
+	rcu_read_unlock();
+}
+
+static int team_add_slave(struct net_device *dev, struct net_device *port_dev)
+{
+	struct team *team = netdev_priv(dev);
+	int err;
+
+	spin_lock(&team->lock);
+	err = team_port_add(team, port_dev);
+	spin_unlock(&team->lock);
+	return err;
+}
+
+static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
+{
+	struct team *team = netdev_priv(dev);
+	int err;
+
+	spin_lock(&team->lock);
+	err = team_port_del(team, port_dev);
+	spin_unlock(&team->lock);
+	return err;
+}
+
+static const struct net_device_ops team_netdev_ops = {
+	.ndo_init		= team_init,
+	.ndo_uninit		= team_uninit,
+	.ndo_open		= team_open,
+	.ndo_stop		= team_close,
+	.ndo_start_xmit		= team_xmit,
+	.ndo_change_rx_flags	= team_change_rx_flags,
+	.ndo_set_rx_mode	= team_set_rx_mode,
+	.ndo_set_mac_address	= team_set_mac_address,
+	.ndo_change_mtu		= team_change_mtu,
+	.ndo_get_stats64	= team_get_stats64,
+	.ndo_vlan_rx_add_vid	= team_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	= team_vlan_rx_kill_vid,
+	.ndo_add_slave		= team_add_slave,
+	.ndo_del_slave		= team_del_slave,
+};
+
+
+/***********************
+ * rt netlink interface
+ ***********************/
+
+static void team_setup(struct net_device *dev)
+{
+	ether_setup(dev);
+
+	dev->netdev_ops = &team_netdev_ops;
+	dev->destructor	= team_destructor;
+	dev->tx_queue_len = 0;
+	dev->flags |= IFF_MULTICAST;
+	dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
+
+	/*
+	 * Indicate we support unicast address filtering. That way core won't
+	 * bring us to promisc mode in case a unicast addr is added.
+	 * Let this up to underlay drivers.
+	 */
+	dev->priv_flags |= IFF_UNICAST_FLT;
+
+	dev->features |= NETIF_F_LLTX;
+	dev->features |= NETIF_F_GRO;
+	dev->hw_features = NETIF_F_HW_VLAN_TX |
+			   NETIF_F_HW_VLAN_RX |
+			   NETIF_F_HW_VLAN_FILTER;
+
+	dev->features |= dev->hw_features;
+}
+
+static int team_newlink(struct net *src_net, struct net_device *dev,
+			struct nlattr *tb[], struct nlattr *data[])
+{
+	int err;
+
+	if (tb[IFLA_ADDRESS] == NULL)
+		random_ether_addr(dev->dev_addr);
+
+	err = register_netdevice(dev);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int team_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+	if (tb[IFLA_ADDRESS]) {
+		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
+			return -EINVAL;
+		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
+			return -EADDRNOTAVAIL;
+	}
+	return 0;
+}
+
+static struct rtnl_link_ops team_link_ops __read_mostly = {
+	.kind		= DRV_NAME,
+	.priv_size	= sizeof(struct team),
+	.setup		= team_setup,
+	.newlink	= team_newlink,
+	.validate	= team_validate,
+};
+
+
+/***********************************
+ * Generic netlink custom interface
+ ***********************************/
+
+static struct genl_family team_nl_family = {
+	.id		= GENL_ID_GENERATE,
+	.name		= TEAM_GENL_NAME,
+	.version	= TEAM_GENL_VERSION,
+	.maxattr	= TEAM_ATTR_MAX,
+	.netnsok	= true,
+};
+
+static const struct nla_policy team_nl_policy[TEAM_ATTR_MAX + 1] = {
+	[TEAM_ATTR_UNSPEC]			= { .type = NLA_UNSPEC, },
+	[TEAM_ATTR_TEAM_IFINDEX]		= { .type = NLA_U32 },
+	[TEAM_ATTR_LIST_OPTION]			= { .type = NLA_NESTED },
+	[TEAM_ATTR_LIST_PORT]			= { .type = NLA_NESTED },
+};
+
+static const struct nla_policy
+team_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = {
+	[TEAM_ATTR_OPTION_UNSPEC]		= { .type = NLA_UNSPEC, },
+	[TEAM_ATTR_OPTION_NAME] = {
+		.type = NLA_STRING,
+		.len = TEAM_STRING_MAX_LEN,
+	},
+	[TEAM_ATTR_OPTION_CHANGED]		= { .type = NLA_FLAG },
+	[TEAM_ATTR_OPTION_TYPE]			= { .type = NLA_U8 },
+	[TEAM_ATTR_OPTION_DATA] = {
+		.type = NLA_BINARY,
+		.len = TEAM_STRING_MAX_LEN,
+	},
+};
+
+static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
+{
+	struct sk_buff *msg;
+	void *hdr;
+	int err;
+
+	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
+			  &team_nl_family, 0, TEAM_CMD_NOOP);
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto err_msg_put;
+	}
+
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid);
+
+err_msg_put:
+	nlmsg_free(msg);
+
+	return err;
+}
+
+/*
+ * Netlink cmd functions should be locked by following two functions.
+ * To ensure team_uninit would not be called in between, hold rcu_read_lock
+ * all the time.
+ */
+static struct team *team_nl_team_get(struct genl_info *info)
+{
+	struct net *net = genl_info_net(info);
+	int ifindex;
+	struct net_device *dev;
+	struct team *team;
+
+	if (!info->attrs[TEAM_ATTR_TEAM_IFINDEX])
+		return NULL;
+
+	ifindex = nla_get_u32(info->attrs[TEAM_ATTR_TEAM_IFINDEX]);
+	rcu_read_lock();
+	dev = dev_get_by_index_rcu(net, ifindex);
+	if (!dev || dev->netdev_ops != &team_netdev_ops) {
+		rcu_read_unlock();
+		return NULL;
+	}
+
+	team = netdev_priv(dev);
+	spin_lock(&team->lock);
+	return team;
+}
+
+static void team_nl_team_put(struct team *team)
+{
+	spin_unlock(&team->lock);
+	rcu_read_unlock();
+}
+
+static int team_nl_send_generic(struct genl_info *info, struct team *team,
+				int (*fill_func)(struct sk_buff *skb,
+						 struct genl_info *info,
+						 int flags, struct team *team))
+{
+	struct sk_buff *skb;
+	int err;
+
+	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	err = fill_func(skb, info, NLM_F_ACK, team);
+	if (err < 0)
+		goto err_fill;
+
+	err = genlmsg_unicast(genl_info_net(info), skb, info->snd_pid);
+	return err;
+
+err_fill:
+	nlmsg_free(skb);
+	return err;
+}
+
+static int team_nl_fill_options_get_changed(struct sk_buff *skb,
+					    u32 pid, u32 seq, int flags,
+					    struct team *team,
+					    struct team_option *changed_option)
+{
+	struct nlattr *option_list;
+	void *hdr;
+	struct team_option *option;
+
+	hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
+			  TEAM_CMD_OPTIONS_GET);
+	if (IS_ERR(hdr))
+		return PTR_ERR(hdr);
+
+	NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
+	option_list = nla_nest_start(skb, TEAM_ATTR_LIST_OPTION);
+	if (!option_list)
+		return -EMSGSIZE;
+
+	list_for_each_entry(option, &team->option_list, list) {
+		struct nlattr *option_item;
+		long arg;
+
+		option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION);
+		if (!option_item)
+			goto nla_put_failure;
+		NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name);
+		if (option == changed_option)
+			NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED);
+		switch (option->type) {
+		case TEAM_OPTION_TYPE_U32:
+			NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32);
+			team_option_get(team, option, &arg);
+			NLA_PUT_U32(skb, TEAM_ATTR_OPTION_DATA, arg);
+			break;
+		case TEAM_OPTION_TYPE_STRING:
+			NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING);
+			team_option_get(team, option, &arg);
+			NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_DATA,
+				       (char *) arg);
+			break;
+		default:
+			BUG();
+		}
+		nla_nest_end(skb, option_item);
+	}
+
+	nla_nest_end(skb, option_list);
+	return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+	genlmsg_cancel(skb, hdr);
+	return -EMSGSIZE;
+}
+
+static int team_nl_fill_options_get(struct sk_buff *skb,
+				    struct genl_info *info, int flags,
+				    struct team *team)
+{
+	return team_nl_fill_options_get_changed(skb, info->snd_pid,
+						info->snd_seq, NLM_F_ACK,
+						team, NULL);
+}
+
+static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
+{
+	struct team *team;
+	int err;
+
+	team = team_nl_team_get(info);
+	if (!team)
+		return -EINVAL;
+
+	err = team_nl_send_generic(info, team, team_nl_fill_options_get);
+
+	team_nl_team_put(team);
+
+	return err;
+}
+
+static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+{
+	struct team *team;
+	int err = 0;
+	int i;
+	struct nlattr *nl_option;
+
+	team = team_nl_team_get(info);
+	if (!team)
+		return -EINVAL;
+
+	err = -EINVAL;
+	if (!info->attrs[TEAM_ATTR_LIST_OPTION]) {
+		err = -EINVAL;
+		goto team_put;
+	}
+
+	nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
+		struct nlattr *mode_attrs[TEAM_ATTR_OPTION_MAX + 1];
+		enum team_option_type opt_type;
+		struct team_option *option;
+		char *opt_name;
+		bool opt_found = false;
+
+		if (nla_type(nl_option) != TEAM_ATTR_ITEM_OPTION) {
+			err = -EINVAL;
+			goto team_put;
+		}
+		err = nla_parse_nested(mode_attrs, TEAM_ATTR_OPTION_MAX,
+				       nl_option, team_nl_option_policy);
+		if (err)
+			goto team_put;
+		if (!mode_attrs[TEAM_ATTR_OPTION_NAME] ||
+		    !mode_attrs[TEAM_ATTR_OPTION_TYPE] ||
+		    !mode_attrs[TEAM_ATTR_OPTION_DATA]) {
+			err = -EINVAL;
+			goto team_put;
+		}
+		switch (nla_get_u8(mode_attrs[TEAM_ATTR_OPTION_TYPE])) {
+		case NLA_U32:
+			opt_type = TEAM_OPTION_TYPE_U32;
+			break;
+		case NLA_STRING:
+			opt_type = TEAM_OPTION_TYPE_STRING;
+			break;
+		default:
+			goto team_put;
+		}
+
+		opt_name = nla_data(mode_attrs[TEAM_ATTR_OPTION_NAME]);
+		list_for_each_entry(option, &team->option_list, list) {
+			long arg;
+			struct nlattr *opt_data_attr;
+
+			if (option->type != opt_type ||
+			    strcmp(option->name, opt_name))
+				continue;
+			opt_found = true;
+			opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA];
+			switch (opt_type) {
+			case TEAM_OPTION_TYPE_U32:
+				arg = nla_get_u32(opt_data_attr);
+				break;
+			case TEAM_OPTION_TYPE_STRING:
+				arg = (long) nla_data(opt_data_attr);
+				break;
+			default:
+				BUG();
+			}
+			err = team_option_set(team, option, &arg);
+			if (err)
+				goto team_put;
+		}
+		if (!opt_found) {
+			err = -ENOENT;
+			goto team_put;
+		}
+	}
+
+team_put:
+	team_nl_team_put(team);
+
+	return err;
+}
+
+static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
+					      u32 pid, u32 seq, int flags,
+					      struct team *team,
+					      struct team_port *changed_port)
+{
+	struct nlattr *port_list;
+	void *hdr;
+	struct team_port *port;
+
+	hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
+			  TEAM_CMD_PORT_LIST_GET);
+	if (IS_ERR(hdr))
+		return PTR_ERR(hdr);
+
+	NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
+	port_list = nla_nest_start(skb, TEAM_ATTR_LIST_PORT);
+	if (!port_list)
+		return -EMSGSIZE;
+
+	list_for_each_entry_rcu(port, &team->port_list, list) {
+		struct nlattr *port_item;
+
+		port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT);
+		if (!port_item)
+			goto nla_put_failure;
+		NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex);
+		if (port == changed_port)
+			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED);
+		if (port->linkup)
+			NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP);
+		NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed);
+		NLA_PUT_U8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex);
+		nla_nest_end(skb, port_item);
+	}
+
+	nla_nest_end(skb, port_list);
+	return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+	genlmsg_cancel(skb, hdr);
+	return -EMSGSIZE;
+}
+
+static int team_nl_fill_port_list_get(struct sk_buff *skb,
+				      struct genl_info *info, int flags,
+				      struct team *team)
+{
+	return team_nl_fill_port_list_get_changed(skb, info->snd_pid,
+						  info->snd_seq, NLM_F_ACK,
+						  team, NULL);
+}
+
+static int team_nl_cmd_port_list_get(struct sk_buff *skb,
+				     struct genl_info *info)
+{
+	struct team *team;
+	int err;
+
+	team = team_nl_team_get(info);
+	if (!team)
+		return -EINVAL;
+
+	err = team_nl_send_generic(info, team, team_nl_fill_port_list_get);
+
+	team_nl_team_put(team);
+
+	return err;
+}
+
+static struct genl_ops team_nl_ops[] = {
+	{
+		.cmd = TEAM_CMD_NOOP,
+		.doit = team_nl_cmd_noop,
+		.policy = team_nl_policy,
+	},
+	{
+		.cmd = TEAM_CMD_OPTIONS_SET,
+		.doit = team_nl_cmd_options_set,
+		.policy = team_nl_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = TEAM_CMD_OPTIONS_GET,
+		.doit = team_nl_cmd_options_get,
+		.policy = team_nl_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = TEAM_CMD_PORT_LIST_GET,
+		.doit = team_nl_cmd_port_list_get,
+		.policy = team_nl_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+};
+
+static struct genl_multicast_group team_change_event_mcgrp = {
+	.name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME,
+};
+
+static int team_nl_send_event_options_get(struct team *team,
+					  struct team_option *changed_option)
+{
+	struct sk_buff *skb;
+	int err;
+	struct net *net = dev_net(team->dev);
+
+	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	err = team_nl_fill_options_get_changed(skb, 0, 0, 0, team,
+					       changed_option);
+	if (err < 0)
+		goto err_fill;
+
+	err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id,
+				      GFP_KERNEL);
+	return err;
+
+err_fill:
+	nlmsg_free(skb);
+	return err;
+}
+
+static int team_nl_send_event_port_list_get(struct team_port *port)
+{
+	struct sk_buff *skb;
+	int err;
+	struct net *net = dev_net(port->team->dev);
+
+	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	err = team_nl_fill_port_list_get_changed(skb, 0, 0, 0,
+						 port->team, port);
+	if (err < 0)
+		goto err_fill;
+
+	err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id,
+				      GFP_KERNEL);
+	return err;
+
+err_fill:
+	nlmsg_free(skb);
+	return err;
+}
+
+static int team_nl_init(void)
+{
+	int err;
+
+	err = genl_register_family_with_ops(&team_nl_family, team_nl_ops,
+					    ARRAY_SIZE(team_nl_ops));
+	if (err)
+		return err;
+
+	err = genl_register_mc_group(&team_nl_family, &team_change_event_mcgrp);
+	if (err)
+		goto err_change_event_grp_reg;
+
+	return 0;
+
+err_change_event_grp_reg:
+	genl_unregister_family(&team_nl_family);
+
+	return err;
+}
+
+static void team_nl_fini(void)
+{
+	genl_unregister_family(&team_nl_family);
+}
+
+
+/******************
+ * Change checkers
+ ******************/
+
+static void __team_options_change_check(struct team *team,
+					struct team_option *changed_option)
+{
+	int err;
+
+	err = team_nl_send_event_options_get(team, changed_option);
+	if (err)
+		netdev_warn(team->dev, "Failed to send options change via netlink\n");
+}
+
+/* rtnl lock is held */
+static void __team_port_change_check(struct team_port *port, bool linkup)
+{
+	int err;
+
+	if (port->linkup == linkup)
+		return;
+
+	port->linkup = linkup;
+	if (linkup) {
+		struct ethtool_cmd ecmd;
+
+		err = __ethtool_get_settings(port->dev, &ecmd);
+		if (!err) {
+			port->speed = ethtool_cmd_speed(&ecmd);
+			port->duplex = ecmd.duplex;
+			goto send_event;
+		}
+	}
+	port->speed = 0;
+	port->duplex = 0;
+
+send_event:
+	err = team_nl_send_event_port_list_get(port);
+	if (err)
+		netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n",
+			    port->dev->name);
+
+}
+
+static void team_port_change_check(struct team_port *port, bool linkup)
+{
+	struct team *team = port->team;
+
+	spin_lock(&team->lock);
+	__team_port_change_check(port, linkup);
+	spin_unlock(&team->lock);
+}
+
+/************************************
+ * Net device notifier event handler
+ ************************************/
+
+static int team_device_event(struct notifier_block *unused,
+			     unsigned long event, void *ptr)
+{
+	struct net_device *dev = (struct net_device *) ptr;
+	struct team_port *port;
+
+	port = team_port_get_rtnl(dev);
+	if (!port)
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case NETDEV_UP:
+		if (netif_carrier_ok(dev))
+			team_port_change_check(port, true);
+	case NETDEV_DOWN:
+		team_port_change_check(port, false);
+	case NETDEV_CHANGE:
+		if (netif_running(port->dev))
+			team_port_change_check(port,
+					       !!netif_carrier_ok(port->dev));
+		break;
+	case NETDEV_UNREGISTER:
+		team_del_slave(port->team->dev, dev);
+		break;
+	case NETDEV_FEAT_CHANGE:
+		team_compute_features(port->team);
+		break;
+	case NETDEV_CHANGEMTU:
+		/* Forbid to change mtu of underlaying device */
+		return NOTIFY_BAD;
+	case NETDEV_CHANGEADDR:
+		/* Forbid to change addr of underlaying device */
+		return NOTIFY_BAD;
+	case NETDEV_PRE_TYPE_CHANGE:
+		/* Forbid to change type of underlaying device */
+		return NOTIFY_BAD;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block team_notifier_block __read_mostly = {
+	.notifier_call = team_device_event,
+};
+
+
+/***********************
+ * Module init and exit
+ ***********************/
+
+static int __init team_module_init(void)
+{
+	int err;
+
+	register_netdevice_notifier(&team_notifier_block);
+
+	err = rtnl_link_register(&team_link_ops);
+	if (err)
+		goto err_rtnl_reg;
+
+	err = team_nl_init();
+	if (err)
+		goto err_nl_init;
+
+	return 0;
+
+err_nl_init:
+	rtnl_link_unregister(&team_link_ops);
+
+err_rtnl_reg:
+	unregister_netdevice_notifier(&team_notifier_block);
+
+	return err;
+}
+
+static void __exit team_module_exit(void)
+{
+	team_nl_fini();
+	rtnl_link_unregister(&team_link_ops);
+	unregister_netdevice_notifier(&team_notifier_block);
+}
+
+module_init(team_module_init);
+module_exit(team_module_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
+MODULE_DESCRIPTION("Ethernet team device driver");
+MODULE_ALIAS_RTNL_LINK(DRV_NAME);
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
new file mode 100644
index 0000000..1aa2bfb
--- /dev/null
+++ b/drivers/net/team/team_mode_activebackup.c
@@ -0,0 +1,152 @@
+/*
+ * net/drivers/team/team_mode_activebackup.c - Active-backup mode for team
+ * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <net/rtnetlink.h>
+#include <linux/if_team.h>
+
+struct ab_priv {
+	struct team_port __rcu *active_port;
+};
+
+static struct ab_priv *ab_priv(struct team *team)
+{
+	return (struct ab_priv *) &team->mode_priv;
+}
+
+static rx_handler_result_t ab_receive(struct team *team, struct team_port *port,
+				      struct sk_buff *skb) {
+	struct team_port *active_port;
+
+	active_port = rcu_dereference(ab_priv(team)->active_port);
+	if (active_port != port)
+		return RX_HANDLER_EXACT;
+	return RX_HANDLER_ANOTHER;
+}
+
+static bool ab_transmit(struct team *team, struct sk_buff *skb)
+{
+	struct team_port *active_port;
+
+	active_port = rcu_dereference(ab_priv(team)->active_port);
+	if (unlikely(!active_port))
+		goto drop;
+	skb->dev = active_port->dev;
+	if (dev_queue_xmit(skb))
+		return false;
+	return true;
+
+drop:
+	dev_kfree_skb(skb);
+	return false;
+}
+
+static void ab_port_leave(struct team *team, struct team_port *port)
+{
+	if (ab_priv(team)->active_port == port)
+		rcu_assign_pointer(ab_priv(team)->active_port, NULL);
+}
+
+static void ab_port_change_mac(struct team *team, struct team_port *port)
+{
+	if (ab_priv(team)->active_port == port)
+		team_port_set_team_mac(port);
+}
+
+static int ab_active_port_get(struct team *team, void *arg)
+{
+	u32 *ifindex = arg;
+
+	*ifindex = 0;
+	if (ab_priv(team)->active_port)
+		*ifindex = ab_priv(team)->active_port->dev->ifindex;
+	return 0;
+}
+
+static int ab_active_port_set(struct team *team, void *arg)
+{
+	u32 *ifindex = arg;
+	struct team_port *port;
+
+	list_for_each_entry_rcu(port, &team->port_list, list) {
+		if (port->dev->ifindex == *ifindex) {
+			struct team_port *ac_port = ab_priv(team)->active_port;
+
+			/* rtnl_lock needs to be held when setting macs */
+			rtnl_lock();
+			if (ac_port)
+				team_port_set_orig_mac(ac_port);
+			rcu_assign_pointer(ab_priv(team)->active_port, port);
+			team_port_set_team_mac(port);
+			rtnl_unlock();
+			return 0;
+		}
+	}
+	return -ENOENT;
+}
+
+static struct team_option ab_options[] = {
+	{
+		.name = "activeport",
+		.type = TEAM_OPTION_TYPE_U32,
+		.getter = ab_active_port_get,
+		.setter = ab_active_port_set,
+	},
+};
+
+int ab_init(struct team *team)
+{
+	team_options_register(team, ab_options, ARRAY_SIZE(ab_options));
+	return 0;
+}
+
+void ab_exit(struct team *team)
+{
+	team_options_unregister(team, ab_options, ARRAY_SIZE(ab_options));
+}
+
+static const struct team_mode_ops ab_mode_ops = {
+	.init			= ab_init,
+	.exit			= ab_exit,
+	.receive		= ab_receive,
+	.transmit		= ab_transmit,
+	.port_leave		= ab_port_leave,
+	.port_change_mac	= ab_port_change_mac,
+};
+
+static struct team_mode ab_mode = {
+	.kind		= "activebackup",
+	.owner		= THIS_MODULE,
+	.priv_size	= sizeof(struct ab_priv),
+	.ops		= &ab_mode_ops,
+};
+
+static int __init ab_init_module(void)
+{
+	return team_mode_register(&ab_mode);
+}
+
+static void __exit ab_cleanup_module(void)
+{
+	team_mode_unregister(&ab_mode);
+}
+
+module_init(ab_init_module);
+module_exit(ab_cleanup_module);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
+MODULE_DESCRIPTION("Active-backup mode for team");
+MODULE_ALIAS("team-mode-activebackup");
diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c
new file mode 100644
index 0000000..0374052
--- /dev/null
+++ b/drivers/net/team/team_mode_roundrobin.c
@@ -0,0 +1,107 @@
+/*
+ * net/drivers/team/team_mode_roundrobin.c - Round-robin mode for team
+ * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_team.h>
+
+struct rr_priv {
+	unsigned int sent_packets;
+};
+
+static struct rr_priv *rr_priv(struct team *team)
+{
+	return (struct rr_priv *) &team->mode_priv;
+}
+
+static struct team_port *__get_first_port_up(struct team *team,
+					     struct team_port *port)
+{
+	struct team_port *cur;
+
+	if (port->linkup)
+		return port;
+	cur = port;
+	list_for_each_entry_continue_rcu(cur, &team->port_list, list)
+		if (cur->linkup)
+			return cur;
+	list_for_each_entry_rcu(cur, &team->port_list, list) {
+		if (cur == port)
+			break;
+		if (cur->linkup)
+			return cur;
+	}
+	return NULL;
+}
+
+static bool rr_transmit(struct team *team, struct sk_buff *skb)
+{
+	struct team_port *port;
+	int port_index;
+
+	port_index = rr_priv(team)->sent_packets++ % team->port_count;
+	port = team_get_port_by_index_rcu(team, port_index);
+	port = __get_first_port_up(team, port);
+	if (unlikely(!port))
+		goto drop;
+	skb->dev = port->dev;
+	if (dev_queue_xmit(skb))
+		return false;
+	return true;
+
+drop:
+	dev_kfree_skb(skb);
+	return false;
+}
+
+static int rr_port_enter(struct team *team, struct team_port *port)
+{
+	return team_port_set_team_mac(port);
+}
+
+static void rr_port_change_mac(struct team *team, struct team_port *port)
+{
+	team_port_set_team_mac(port);
+}
+
+static const struct team_mode_ops rr_mode_ops = {
+	.transmit		= rr_transmit,
+	.port_enter		= rr_port_enter,
+	.port_change_mac	= rr_port_change_mac,
+};
+
+static struct team_mode rr_mode = {
+	.kind		= "roundrobin",
+	.owner		= THIS_MODULE,
+	.priv_size	= sizeof(struct rr_priv),
+	.ops		= &rr_mode_ops,
+};
+
+static int __init rr_init_module(void)
+{
+	return team_mode_register(&rr_mode);
+}
+
+static void __exit rr_cleanup_module(void)
+{
+	team_mode_unregister(&rr_mode);
+}
+
+module_init(rr_init_module);
+module_exit(rr_cleanup_module);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
+MODULE_DESCRIPTION("Round-robin mode for team");
+MODULE_ALIAS("team-mode-roundrobin");
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 619b565..0b091b3 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -185,6 +185,7 @@ header-y += if_pppol2tp.h
 header-y += if_pppox.h
 header-y += if_slip.h
 header-y += if_strip.h
+header-y += if_team.h
 header-y += if_tr.h
 header-y += if_tun.h
 header-y += if_tunnel.h
diff --git a/include/linux/if.h b/include/linux/if.h
index db20bd4..06b6ef6 100644
--- a/include/linux/if.h
+++ b/include/linux/if.h
@@ -79,6 +79,7 @@
 #define IFF_TX_SKB_SHARING	0x10000	/* The interface supports sharing
 					 * skbs on transmit */
 #define IFF_UNICAST_FLT	0x20000		/* Supports unicast filtering	*/
+#define IFF_TEAM_PORT	0x40000		/* device used as team port */
 
 #define IF_GET_IFACE	0x0001		/* for querying only */
 #define IF_GET_PROTO	0x0002
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
new file mode 100644
index 0000000..de395fc
--- /dev/null
+++ b/include/linux/if_team.h
@@ -0,0 +1,254 @@
+/*
+ * include/linux/if_team.h - Network team device driver header
+ * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _LINUX_IF_TEAM_H_
+#define _LINUX_IF_TEAM_H_
+
+#ifdef __KERNEL__
+
+struct team_pcpu_stats {
+	u64			rx_packets;
+	u64			rx_bytes;
+	u64			rx_multicast;
+	u64			tx_packets;
+	u64			tx_bytes;
+	struct u64_stats_sync	syncp;
+	u32			rx_dropped;
+	u32			tx_dropped;
+};
+
+struct team;
+
+struct team_port {
+	struct net_device *dev;
+	struct hlist_node hlist; /* node in hash list */
+	struct list_head list; /* node in ordinary list */
+	struct team *team;
+	int index;
+
+	/*
+	 * A place for storing original values of the device before it
+	 * become a port.
+	 */
+	struct {
+		unsigned char dev_addr[MAX_ADDR_LEN];
+		unsigned int mtu;
+	} orig;
+
+	bool linkup;
+	u32 speed;
+	u8 duplex;
+
+	struct rcu_head rcu;
+};
+
+struct team_mode_ops {
+	int (*init)(struct team *team);
+	void (*exit)(struct team *team);
+	rx_handler_result_t (*receive)(struct team *team,
+				       struct team_port *port,
+				       struct sk_buff *skb);
+	bool (*transmit)(struct team *team, struct sk_buff *skb);
+	int (*port_enter)(struct team *team, struct team_port *port);
+	void (*port_leave)(struct team *team, struct team_port *port);
+	void (*port_change_mac)(struct team *team, struct team_port *port);
+};
+
+static inline void team_mode_ops_copy(struct team_mode_ops *dst,
+				      const struct team_mode_ops *src)
+{
+	dst->init		= src->init;
+	dst->exit		= src->exit;
+	dst->receive		= src->receive;
+	dst->transmit		= src->transmit;
+	dst->port_enter		= src->port_enter;
+	dst->port_leave		= src->port_leave;
+	dst->port_change_mac	= src->port_change_mac;
+}
+
+static inline void team_mode_ops_clear(struct team_mode_ops *dst)
+{
+	dst->init		= NULL;
+	dst->exit		= NULL;
+	dst->receive		= NULL;
+	dst->transmit		= NULL;
+	dst->port_enter		= NULL;
+	dst->port_leave		= NULL;
+	dst->port_change_mac	= NULL;
+}
+
+enum team_option_type {
+	TEAM_OPTION_TYPE_U32,
+	TEAM_OPTION_TYPE_STRING,
+};
+
+struct team_option {
+	struct list_head list;
+	const char *name;
+	enum team_option_type type;
+	int (*getter)(struct team *team, void *arg);
+	int (*setter)(struct team *team, void *arg);
+};
+
+struct team_mode {
+	struct list_head list;
+	const char *kind;
+	struct module *owner;
+	size_t priv_size;
+	const struct team_mode_ops *ops;
+};
+
+#define TEAM_PORT_HASHBITS 4
+#define TEAM_PORT_HASHENTRIES (1 << TEAM_PORT_HASHBITS)
+
+#define TEAM_MODE_PRIV_LONGS 4
+#define TEAM_MODE_PRIV_SIZE (sizeof(long) * TEAM_MODE_PRIV_LONGS)
+
+struct team {
+	struct net_device *dev; /* associated netdevice */
+	struct team_pcpu_stats __percpu *pcpu_stats;
+
+	spinlock_t lock; /* used for overall locking, e.g. port lists write */
+
+	/*
+	 * port lists with port count
+	 */
+	int port_count;
+	struct hlist_head port_hlist[TEAM_PORT_HASHENTRIES];
+	struct list_head port_list;
+
+	struct list_head option_list;
+
+	const char *mode_kind;
+	struct team_mode_ops mode_ops;
+	long mode_priv[TEAM_MODE_PRIV_LONGS];
+};
+
+static inline struct hlist_head *team_port_index_hash(struct team *team,
+						      int port_index)
+{
+	return &team->port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)];
+}
+
+static inline struct team_port *team_get_port_by_index_rcu(struct team *team,
+							   int port_index)
+{
+	struct hlist_node *p;
+	struct team_port *port;
+	struct hlist_head *head = team_port_index_hash(team, port_index);
+
+	hlist_for_each_entry_rcu(port, p, head, hlist)
+		if (port->index == port_index)
+			return port;
+	return NULL;
+}
+
+extern int team_port_set_orig_mac(struct team_port *port);
+extern int team_port_set_team_mac(struct team_port *port);
+extern void team_options_register(struct team *team,
+				  struct team_option *option,
+				  size_t option_count);
+extern void team_options_unregister(struct team *team,
+				    struct team_option *option,
+				    size_t option_count);
+extern int team_mode_register(struct team_mode *mode);
+extern int team_mode_unregister(struct team_mode *mode);
+
+#endif /* __KERNEL__ */
+
+#define TEAM_STRING_MAX_LEN 32
+
+/**********************************
+ * NETLINK_GENERIC netlink family.
+ **********************************/
+
+enum {
+	TEAM_CMD_NOOP,
+	TEAM_CMD_OPTIONS_SET,
+	TEAM_CMD_OPTIONS_GET,
+	TEAM_CMD_PORT_LIST_GET,
+
+	__TEAM_CMD_MAX,
+	TEAM_CMD_MAX = (__TEAM_CMD_MAX - 1),
+};
+
+enum {
+	TEAM_ATTR_UNSPEC,
+	TEAM_ATTR_TEAM_IFINDEX,		/* u32 */
+	TEAM_ATTR_LIST_OPTION,		/* nest */
+	TEAM_ATTR_LIST_PORT,		/* nest */
+
+	__TEAM_ATTR_MAX,
+	TEAM_ATTR_MAX = __TEAM_ATTR_MAX - 1,
+};
+
+/* Nested layout of get/set msg:
+ *
+ *	[TEAM_ATTR_LIST_OPTION]
+ *		[TEAM_ATTR_ITEM_OPTION]
+ *			[TEAM_ATTR_OPTION_*], ...
+ *		[TEAM_ATTR_ITEM_OPTION]
+ *			[TEAM_ATTR_OPTION_*], ...
+ *		...
+ *	[TEAM_ATTR_LIST_PORT]
+ *		[TEAM_ATTR_ITEM_PORT]
+ *			[TEAM_ATTR_PORT_*], ...
+ *		[TEAM_ATTR_ITEM_PORT]
+ *			[TEAM_ATTR_PORT_*], ...
+ *		...
+ */
+
+enum {
+	TEAM_ATTR_ITEM_OPTION_UNSPEC,
+	TEAM_ATTR_ITEM_OPTION,		/* nest */
+
+	__TEAM_ATTR_ITEM_OPTION_MAX,
+	TEAM_ATTR_ITEM_OPTION_MAX = __TEAM_ATTR_ITEM_OPTION_MAX - 1,
+};
+
+enum {
+	TEAM_ATTR_OPTION_UNSPEC,
+	TEAM_ATTR_OPTION_NAME,		/* string */
+	TEAM_ATTR_OPTION_CHANGED,	/* flag */
+	TEAM_ATTR_OPTION_TYPE,		/* u8 */
+	TEAM_ATTR_OPTION_DATA,		/* dynamic */
+
+	__TEAM_ATTR_OPTION_MAX,
+	TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,
+};
+
+enum {
+	TEAM_ATTR_ITEM_PORT_UNSPEC,
+	TEAM_ATTR_ITEM_PORT,		/* nest */
+
+	__TEAM_ATTR_ITEM_PORT_MAX,
+	TEAM_ATTR_ITEM_PORT_MAX = __TEAM_ATTR_ITEM_PORT_MAX - 1,
+};
+
+enum {
+	TEAM_ATTR_PORT_UNSPEC,
+	TEAM_ATTR_PORT_IFINDEX,		/* u32 */
+	TEAM_ATTR_PORT_CHANGED,		/* flag */
+	TEAM_ATTR_PORT_LINKUP,		/* flag */
+	TEAM_ATTR_PORT_SPEED,		/* u32 */
+	TEAM_ATTR_PORT_DUPLEX,		/* u8 */
+
+	__TEAM_ATTR_PORT_MAX,
+	TEAM_ATTR_PORT_MAX = __TEAM_ATTR_PORT_MAX - 1,
+};
+
+/*
+ * NETLINK_GENERIC related info
+ */
+#define TEAM_GENL_NAME "team"
+#define TEAM_GENL_VERSION 0x1
+#define TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME "change_event"
+
+#endif /* _LINUX_IF_TEAM_H_ */
diff --git a/include/linux/rculist.h b/include/linux/rculist.h
index d079290..7586b2c 100644
--- a/include/linux/rculist.h
+++ b/include/linux/rculist.h
@@ -288,6 +288,20 @@ static inline void list_splice_init_rcu(struct list_head *list,
 	     pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
 
 /**
+ * list_for_each_entry_continue_reverse_rcu - iterate backwards from the given point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse_rcu(pos, head, member)	\
+	for (pos = list_entry_rcu(pos->member.prev, typeof(*pos), member); \
+	     &pos->member != (head);	\
+	     pos = list_entry_rcu(pos->member.prev, typeof(*pos), member))
+
+/**
  * hlist_del_rcu - deletes entry from hash list without re-initialization
  * @n: the element to delete from the hash list.
  *
-- 
1.7.6

^ permalink raw reply related

* [RFC][PATCH] uncompress.h cleanup and DT support
From: Russell King - ARM Linux @ 2011-10-23  8:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAGVye5MSqdxh0DzCMt79sKk+Z0Trfro0RNDr-4zccMh7O7vGiA@mail.gmail.com>

On Sun, Oct 23, 2011 at 10:18:29AM +0200, Zoltan Devai wrote:
> Plase consider all this as RFC, it's mostly not even compile-tested.
> If you think it's worth to follow-up, I'm happy to do it.

Is the patch available?

Has this been tested with ZBOOT_ROM?

Thanks.

^ permalink raw reply

* Re: [PATCH 2/3] mfd: Add s5m series irq support
From: Mark Brown @ 2011-10-23  8:42 UTC (permalink / raw)
  To: Sangbeom Kim; +Cc: 'Samuel Ortiz', linux-kernel
In-Reply-To: <009301cc915a$cd72d1c0$68587540$@com>

On Sun, Oct 23, 2011 at 05:07:26PM +0900, Sangbeom Kim wrote:
> This patch support irq for s5m series.
> Basically, S5M8767 and S5M8763 irq can be handled by this patch.

This all looks pretty good, though it looks somewhat familiar :)

Actually, one of the things I want to do in the 3.3 timeframe is to
factor out this code from my drivers into a regmap based irq_chip since
it seems a lot of people are "drawing inspiration" from it.  This
shouldn't be any form of blocker for merging this driver though.

^ permalink raw reply

* Re: [patch net-next V2] net: introduce ethernet teaming device
From: Eric Dumazet @ 2011-10-23  8:43 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: netdev, davem, bhutchings, shemminger, fubar, andy, tgraf,
	ebiederm, mirqus, kaber, greearb, jesse, fbl, benjamin.poirier,
	jzupka
In-Reply-To: <20111023082545.GA15908@minipsycho.orion>

Le dimanche 23 octobre 2011 à 10:25 +0200, Jiri Pirko a écrit :
> Sat, Oct 22, 2011 at 06:51:22PM CEST, eric.dumazet@gmail.com wrote:
> >Le samedi 22 octobre 2011 à 17:13 +0200, Jiri Pirko a écrit :
> >> >> +
> >> >> +/************************
> >> >> + * Rx path frame handler
> >> >> + ************************/
> >> >> +
> >> >> +/* note: already called with rcu_read_lock */
> >> >> +static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
> >> >> +{
> >> >> +	struct sk_buff *skb = *pskb;
> >> >> +	struct team_port *port;
> >> >> +	struct team *team;
> >> >> +	rx_handler_result_t res = RX_HANDLER_ANOTHER;
> >> >> +
> >> >> +	skb = skb_share_check(skb, GFP_ATOMIC);
> >> >> +	if (!skb)
> >> >> +		return RX_HANDLER_CONSUMED;
> >> >> +
> >> >> +	*pskb = skb;
> >> >> +
> >> >> +	port = team_port_get_rcu(skb->dev);
> >> >> +	team = port->team;
> >> >> +
> >> >> +	if (team->mode_ops.receive)
> >> >
> >> >Hmm, you need ACCESS_ONCE() here or rcu_dereference()
> >> >
> >> >See commit 4d97480b1806e883eb (bonding: use local function pointer of
> >> >bond->recv_probe in bond_handle_frame) for reference
> >> 
> >> I do not think so. Because mode_ops.receive changes only from
> >> __team_change_mode() and this can be called only in case no ports are in
> >> team. And team_port_del() calls synchronize_rcu().
> >> 
> >
> >
> >
> >We are used to code following this template :
> >
> >if (ops->handler)
> >	ops->handler(arguments);
> >
> >But this is valid only because ops points to constant memory.
> >
> >
> >In your case, we really see its not true, dont try to pretend its safe.
> 
> Please forgive me, it's possible I'm missing something. But I see no way how
> team->mode_ops.receive can change during team_handle_frame (holding
> rcu_read_lock) for the reason I stated earlier.
> 
> team_port_del() calls netdev_rx_handler_unregister() and after that it
> calls synchronize_rcu(). That ensures that by the finish of team_port_del()
> run, team_handle_frame() is not called for this port anymore.
> 
> And this combined with "if (!list_empty(&team->port_list))" check in
> team_change_mode() ensures safety.
> 
> Of course team_port_del() and team_change_mode() are both protected by
> team->lock so they are mutually excluded.

Then, why even testing (team->mode_ops.receive) being NULL at the first
place, if you are sure no packets can flight meeting this NULL pointer ?

Something is flawed in the logic.

^ permalink raw reply

* Re: [RFC] subdevice PM: .s_power() deprecation?
From: Sakari Ailus @ 2011-10-23  8:44 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: Guennadi Liakhovetski, Laurent Pinchart, Linux Media Mailing List,
	Tomasz Stanislawski
In-Reply-To: <4EA3D1C4.8050302@gmail.com>

Sylwester Nawrocki wrote:
> Hi Sakari,
> 
> On 10/23/2011 10:07 AM, Sakari Ailus wrote:
>> Sylwester Nawrocki wrote:
>> ...
>>>> I understand what you're saying, but can you give us a specific example,
>>>> when a subdev driver (your SoC internal subdev, that is) doesn't have a
>>>> way to react to an event itself and only the bridge driver gets called
>>>> into at that time? Something like an interrupt or an internal timer or
>>>> some other internal event?
>>>
>>> 1. The S5P SoC video output subsystem (http://lwn.net/Articles/449661) comprises
>>>   of multiple logical blocks, like Video Processor, Mixer, HDMI, HDMI PHY, SD TV Out.
>>>   For instance the master video clock is during normal operation derived from
>>>   (synchronized to, with PLL,) the HDMI-PHY output clock. The host driver can
>>>   switch to this clock only when the HDMI-PHY (subdev) power and clocks are enabled.
>>>   And it should be done before .s_stream(), to do some H/W configuration earlier
>>>   in the pipeline, before streaming is enabled. Perhaps Tomasz could give some
>>>   further explanation of what the s_power() op does and why in the driver.
>>>
>>> 2. In some of our camera pipeline setups - "Sensor - MIPI-CSI receiver - host/DMA",
>>>   the sensor won't boot properly if all MIPI-CSI regulators aren't enabled. So the
>>>   MIPI-CSI receiver must always be powered on before the sensor. With the subdevs
>>>   doing their own magic wrt to power control the situation is getting slightly
>>>   out of control.
>>
>> How about this: CSI-2 receiver implements a few new regulators which the
>> sensor driver then requests to be enabled. Would that work for you?
> 
> No, I don't like that... :)
> 
> We would have to standardize the regulator supply names, etc. Such approach
> would be more difficult to align with runtime/system suspend/resume.
> Also the sensor drivers should be independent on other drivers. The MIPI-CSI
> receiver is more specific to the host, rather than a sensor.
> 
> Not all sensors need MIPI-CSI, some just use parallel video bus.

The sensor drivers are responsible for the regulators they want to use,
right? If they need no CSI-2 related regulators then they just ignore
them as any other regulators the sensor doesn't need.

The names of the regulators could come from the platform data, they're
board specific anyway. I can't see another way to do this without having
platform code to do this which is not quite compatible with the idea of
the device tree.

-- 
Sakari Ailus
sakari.ailus@iki.fi

^ permalink raw reply

* [U-Boot] [PATCH 1/2] fdt: Add new fdt_set_node_status & fdt_set_status_by_alias helpers
From: Wolfgang Denk @ 2011-10-23  8:49 UTC (permalink / raw)
  To: u-boot
In-Reply-To: <1318619444-2059-1-git-send-email-galak@kernel.crashing.org>

Dear Kumar Gala,

In message <1318619444-2059-1-git-send-email-galak@kernel.crashing.org> you wrote:
> From: Shengzhou Liu <Shengzhou.Liu@freescale.com>
> 
> Add common function fdt_set_node_status() to assist in various locations
> that we set a nodes status.  This function utilizes the status values
> that are part of the EPAPR spec (on power.org).
> 
> fdt_set_status_by_alias() is based on fdt_set_node_status() but uses an
> alias string to identify the node to update.
> 
> We also add some shortcut functions to help the common cases of setting
> "okay" and "disabled":
> 
> 	fdt_status_okay()
> 	fdt_status_disabled()
> 	fdt_status_okay_by_alias()
> 	fdt_status_disabled_by_alias()
> 
> Finally, we fixup the corenet_ds ethernet code which previously had
> a function by the same name that can be replaced with the new helpers.
> 
> Signed-off-by: Shengzhou Liu <Shengzhou.Liu@freescale.com>
> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>

This patch breaks a number of boards by growing the code size even for
boards which never make use of this new stuff.

Affected boards: TQM8555 TQM8541

[Building with ELDK 4.2]:

+ ./MAKEALL TQM8555
Configuring for TQM8555 - Board: TQM85xx, Options: MPC8555,TQM8555=y,HOSTNAME=tqm8555,BOARDNAME="TQM8555"
ppc_6xx-ld: warning: dot moved backwards before `.bss'
ppc_6xx-ld: warning: dot moved backwards before `.bss'
ppc_6xx-ld: u-boot: section .text lma 0xfffc0000 overlaps previous sections
ppc_6xx-ld: u-boot: section .rodata lma 0xfffef388 overlaps previous sections
ppc_6xx-ld: u-boot: section .reloc lma 0xffffa400 overlaps previous sections
ppc_6xx-ld: u-boot: section .data lma 0xffffcd3c overlaps previous sections
ppc_6xx-ld: u-boot: section .u_boot_cmd lma 0xffffea68 overlaps previous sections
ppc_6xx-ld: u-boot: section .bootpg lma 0xfffff0dc overlaps previous sections


Please fix.

Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
Each honest calling, each walk of life, has its own  elite,  its  own
aristocracy based on excellence of performance. - James Bryant Conant

^ permalink raw reply


This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.