Netdev List
 help / color / mirror / Atom feed
* [ANNOUNCE] bridge-utils 1.5
From: Stephen Hemminger @ 2011-03-29  1:13 UTC (permalink / raw)
  To: netdev, bridge; +Cc: linux-kernel

The Bridge Utilities haven't been re-released in a very long time.
The only new feature is the introduction of hairpin mode.

Note: this is probably the last release of these utilities because
I am actively working on intergrating bridge utility into iproute2
using netlink.

Package download:
   http://sourceforge.net/projects/bridge/files/bridge/bridge-utils-1.5.tar.gz/download

Repository:
   git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/bridge-utils.git

Information:
   http://www.linuxfoundation.org/collaborate/workgroups/networking/bridge

Report problems (or enhancements) to the netdev@vger.kernel.org mailing list.

Changelog:

Anton Danilov (1):
      show selected bridge

Fischer, Anna (1):
      bridge-utils: Add 'hairpin' port forwarding mode

Stephen Hemminger (8):
      don't install libbridge.a
      use proper version of install in doc/Makefile
      Check for fopen() failing
      Skip . and .. in foreach_bridge test
      Handle unknown attributes more gracefully
      bridge-utils 1.5
      update mailing address
      Update URL of git repository

^ permalink raw reply

* [PATCH] phylib: phy_attach_direct: phy_init_hw can fail, add cleanup
From: Marc Kleine-Budde @ 2011-03-29  0:54 UTC (permalink / raw)
  To: Netdev; +Cc: kernel, David S. Miller, Marc Kleine-Budde

The function phy_attach_direct attaches the phy and calls phy_init_hw.
phy_init_hw can fail, but the phy is still marked as attached. Successive
calls to phy_attach_direct will fail because the phy is busy.

[    1.020000] eth0: Freescale FEC PHY driver [Generic PHY] (mii_bus:phy_addr=1:00, irq=-1)
[    1.030000] eth1: Freescale FEC PHY driver [Generic PHY] (mii_bus:phy_addr=1:01, irq=-1)
[    2.050000] Sending DHCP requests .
[    3.020000] PHY: 1:00 - Link is Up - 100/Full
[    5.110000] ..... timed out!
[   87.660000] IP-Config: Reopening network devices...
[   88.190000] FEC: MDIO read timeout
[   88.190000] eth0: could not attach to PHY
[   88.190000] IP-Config: Failed to open eth0
[   88.210000] FEC: MDIO read timeout
[   88.210000] eth1: could not attach to PHY
[   88.210000] IP-Config: Failed to open eth1
[   88.220000] IP-Config: No network devices available.
[   88.220000] Freeing init memory: 6968K

[...]

starting network interfaces...
ip: RTNETLINK answers: File exists
[   94.000000] net eth0: PHY already attached
[   94.010000] eth0: could not attach to PHY
ip: SIOCSIFFLAGS: Device or resource busy

This patch adds phy_detach to clean up if phy_init_hw fails.

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/phy/phy_device.c |    8 ++++++--
 1 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 993c52c..e870c06 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -442,11 +442,11 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 			     u32 flags, phy_interface_t interface)
 {
 	struct device *d = &phydev->dev;
+	int err;
 
 	/* Assume that if there is no driver, that it doesn't
 	 * exist, and we should use the genphy driver. */
 	if (NULL == d->driver) {
-		int err;
 		d->driver = &genphy_driver.driver;
 
 		err = d->driver->probe(d);
@@ -474,7 +474,11 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 	/* Do initial configuration here, now that
 	 * we have certain key parameters
 	 * (dev_flags and interface) */
-	return phy_init_hw(phydev);
+	err = phy_init_hw(phydev);
+	if (err)
+		phy_detach(phydev);
+
+	return err;
 }
 
 /**
-- 
1.7.2.3


^ permalink raw reply related

* Re: BUG: ping without route segfaults in dst_release
From: David Miller @ 2011-03-29  0:18 UTC (permalink / raw)
  To: mkl; +Cc: Netdev, kernel
In-Reply-To: <4D912538.8080001@pengutronix.de>

From: Marc Kleine-Budde <mkl@pengutronix.de>
Date: Tue, 29 Mar 2011 02:18:00 +0200

> On 03/29/2011 01:51 AM, David Miller wrote:
>> From: Marc Kleine-Budde <mkl@pengutronix.de>
>> Date: Tue, 29 Mar 2011 01:18:48 +0200
>> 
>>> root@ptx:~ ping 130.75.1.32
>>> PING 130.75.1.32 (130.75[   10.160000] Unable to handle kernel paging request at virtual address ffffffdb
>> 
>> Well, that was easy enough :-)
> 
> \o/, ping works again.
> 
>> --------------------
>> ipv4: Don't ip_rt_put() an error pointer in RAW sockets.
>> 
>> Reported-by: Marc Kleine-Budde <mkl@pengutronix.de>
>> Signed-off-by: David S. Miller <davem@davemloft.net>
> Tested-by: Marc Kleine-Budde <mkl@pengutronix.de>

Thanks for testing.

^ permalink raw reply

* Re: BUG: ping without route segfaults in dst_release
From: Marc Kleine-Budde @ 2011-03-29  0:18 UTC (permalink / raw)
  To: David Miller; +Cc: Netdev, kernel
In-Reply-To: <20110328.165149.193711805.davem@davemloft.net>

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

On 03/29/2011 01:51 AM, David Miller wrote:
> From: Marc Kleine-Budde <mkl@pengutronix.de>
> Date: Tue, 29 Mar 2011 01:18:48 +0200
> 
>> root@ptx:~ ping 130.75.1.32
>> PING 130.75.1.32 (130.75[   10.160000] Unable to handle kernel paging request at virtual address ffffffdb
> 
> Well, that was easy enough :-)

\o/, ping works again.

> --------------------
> ipv4: Don't ip_rt_put() an error pointer in RAW sockets.
> 
> Reported-by: Marc Kleine-Budde <mkl@pengutronix.de>
> Signed-off-by: David S. Miller <davem@davemloft.net>
Tested-by: Marc Kleine-Budde <mkl@pengutronix.de>

g'night
Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply

* Re: BUG: ping without route segfaults in dst_release
From: Marc Kleine-Budde @ 2011-03-29  0:00 UTC (permalink / raw)
  To: David Miller; +Cc: Netdev, kernel
In-Reply-To: <20110328.165149.193711805.davem@davemloft.net>

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

On 03/29/2011 01:51 AM, David Miller wrote:
> From: Marc Kleine-Budde <mkl@pengutronix.de>
> Date: Tue, 29 Mar 2011 01:18:48 +0200
> 
>> root@ptx:~ ping 130.75.1.32
>> PING 130.75.1.32 (130.75[   10.160000] Unable to handle kernel paging request at virtual address ffffffdb
> 
> Well, that was easy enough :-)

..just finished bisecting :)

FWIW:

b23dd4fe42b455af5c6e20966b7d6959fa8352ea is the first bad commit
commit b23dd4fe42b455af5c6e20966b7d6959fa8352ea
Author: David S. Miller <davem@davemloft.net>
Date:   Wed Mar 2 14:31:35 2011 -0800

    ipv4: Make output route lookup return rtable directly.

    Instead of on the stack.

    Signed-off-by: David S. Miller <davem@davemloft.net>

:040000 040000 a30b2e32bbbbed71fe97ba44b8e0ac4d8691232b 589f843359ccb53c7800e594ea5efa074413f22e M      drivers
:040000 040000 001c629e354ff875e8f2d3d7e4df2506f3396448 a55b417b1c10c3f23fafdf9fa5c6ab3c132d5223 M      include
:040000 040000 1a0100ddadb50d9e61b66d1f18f47f25a4866856 4e89411fcefcfb87f8973910a97b4776d7a3f7f1 M      net

> 
> --------------------
> ipv4: Don't ip_rt_put() an error pointer in RAW sockets.
> 
> Reported-by: Marc Kleine-Budde <mkl@pengutronix.de>
> Signed-off-by: David S. Miller <davem@davemloft.net>
> ---
>  net/ipv4/raw.c |    1 +
>  1 files changed, 1 insertions(+), 0 deletions(-)
> 
> diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
> index e837ffd..2d3c72e 100644
> --- a/net/ipv4/raw.c
> +++ b/net/ipv4/raw.c
> @@ -569,6 +569,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
>  		rt = ip_route_output_flow(sock_net(sk), &fl4, sk);
>  		if (IS_ERR(rt)) {
>  			err = PTR_ERR(rt);
> +			rt = NULL;

IIRC I saw another commit which looks the same, in a different file
though. Any chance there are more rt = NULL missing?
>  			goto done;
>  		}
>  	}

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply

* Re: [PATCH 2/2 v2] tg3: Don't use IRQF_SAMPLE_RANDOM
From: David Miller @ 2011-03-28 23:53 UTC (permalink / raw)
  To: bhutchings
  Cc: martinez.javier, eric.dumazet, error27, netdev, kernel-janitors
In-Reply-To: <1301332822.2716.12.camel@bwh-desktop>

From: Ben Hutchings <bhutchings@solarflare.com>
Date: Mon, 28 Mar 2011 18:20:22 +0100

> On Mon, 2011-03-28 at 17:46 +0200, Javier Martinez Canillas wrote:
>> Yes this definitely is not janitor material :)
>> 
>> I just sent the patch because I saw IRQF_SAMPLE_RANDOM in
>> Documentation/feature-removal-schedule.txt. I can resend a patch
>> removing the macro in the remaining network cards if the decision is
>> to remove IRQF_SAMPLE_RANDOM.
> 
> It's not my call, but I would support it.

FWIW, I support it too.

^ permalink raw reply

* Re: BUG: ping without route segfaults in dst_release
From: David Miller @ 2011-03-28 23:51 UTC (permalink / raw)
  To: mkl; +Cc: Netdev, kernel
In-Reply-To: <4D911758.3010608@pengutronix.de>

From: Marc Kleine-Budde <mkl@pengutronix.de>
Date: Tue, 29 Mar 2011 01:18:48 +0200

> root@ptx:~ ping 130.75.1.32
> PING 130.75.1.32 (130.75[   10.160000] Unable to handle kernel paging request at virtual address ffffffdb

Well, that was easy enough :-)

--------------------
ipv4: Don't ip_rt_put() an error pointer in RAW sockets.

Reported-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
---
 net/ipv4/raw.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index e837ffd..2d3c72e 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -569,6 +569,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 		rt = ip_route_output_flow(sock_net(sk), &fl4, sk);
 		if (IS_ERR(rt)) {
 			err = PTR_ERR(rt);
+			rt = NULL;
 			goto done;
 		}
 	}
-- 
1.7.4.1


^ permalink raw reply related

* Re: [PATCH net-next] net-ethtool: Allow ethtool to set interface in loopback mode.
From: Ben Hutchings @ 2011-03-28 23:48 UTC (permalink / raw)
  To: Mahesh Bandewar
  Cc: David Miller, netdev, Tom Herbert, Michał Mirosław
In-Reply-To: <1301351066-24916-1-git-send-email-maheshb@google.com>

I'm sorry that things keep changing under your feet.  Unfortunately I'm
going to have to ask for more changes; see below.

I'm cc'ing Michał Mirosław so he can comment on how he thinks the
generic feature handling should be extended.

On Mon, 2011-03-28 at 15:24 -0700, Mahesh Bandewar wrote:
> Add second word for feature (currently it's single word). Also use the first
> bit of the second word to set the loopback mode. By configuring the interface
> in loopback mode in conjunction with a policy route / rule, a user-land
> application can stress the egress / ingress path exposing the flows of the
> change in progress and potentially help developer(s) understand the impact of
> those changes without even sending a packet out on the network.
> 
> Following set of commands illustrates one such example -
>     a) ip -4 addr add 192.168.1.1/24 dev eth1
>     b) ip -4 rule add from all iif eth1 lookup 250
>     c) ip -4 route add local 0/0 dev lo proto kernel scope host table 250
>     d) arp -Ds 192.168.1.100 eth1
>     e) arp -Ds 192.168.1.200 eth1
>     f) sysctl -w net.ipv4.ip_nonlocal_bind=1
>     g) sysctl -w net.ipv4.conf.all.accept_local=1
>     # Assuming that the machine has 8 cores
>     h) taskset 000f netserver -L 192.168.1.200
>     i) taskset 00f0 netperf -t TCP_CRR -L 192.168.1.100 -H 192.168.1.200 -l 30
> 
> Signed-off-by: Mahesh Bandewar <maheshb@google.com>
> ---
>  include/linux/ethtool.h |   14 ++++++++++++
>  net/core/ethtool.c      |   54 +++++++++++++++++++++++++++++++++++++++++++---
>  2 files changed, 64 insertions(+), 4 deletions(-)
> 
> diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
> index aac3e2e..a42f60c 100644
> --- a/include/linux/ethtool.h
> +++ b/include/linux/ethtool.h
> @@ -526,6 +526,8 @@ struct ethtool_flash {
>  
>  /* for returning and changing feature sets */
>  
> +#define ETHTOOL_DEV_FEATURE_WORDS	2

If this needs to be exposed in ethtool.h, it should be #ifdef
__KERNEL__.  User-space should find out the number of features at
run-time.

>  /**
>   * struct ethtool_get_features_block - block with state of 32 features
>   * @available: mask of changeable features
> @@ -594,6 +596,8 @@ struct ethtool_sfeatures {
>   *   %ETHTOOL_F_COMPAT - some or all changes requested were made by calling
>   *      compatibility functions. Requested offload state cannot be properly
>   *      managed by kernel.
> + *   %ETHTOOL_F_2NDWORD - some or all changes requested in the second word of
> + *      the features failed.

Why do you think it is necessary to distinguish between the words?

>   *
>   * Meaning of bits in the masks are obtained by %ETHTOOL_GSSET_INFO (number of
>   * bits in the arrays - always multiple of 32) and %ETHTOOL_GSTRINGS commands
> @@ -604,11 +608,13 @@ enum ethtool_sfeatures_retval_bits {
>  	ETHTOOL_F_UNSUPPORTED__BIT,
>  	ETHTOOL_F_WISH__BIT,
>  	ETHTOOL_F_COMPAT__BIT,
> +	ETHTOOL_F_2NDWORD__BIT,
>  };
>  
>  #define ETHTOOL_F_UNSUPPORTED   (1 << ETHTOOL_F_UNSUPPORTED__BIT)
>  #define ETHTOOL_F_WISH          (1 << ETHTOOL_F_WISH__BIT)
>  #define ETHTOOL_F_COMPAT        (1 << ETHTOOL_F_COMPAT__BIT)
> +#define ETHTOOL_F_2NDWORD       (1 << ETHTOOL_F_2NDWORD__BIT)
>  
>  #ifdef __KERNEL__
>  
> @@ -764,6 +770,8 @@ struct ethtool_ops {
>  				  struct ethtool_rxfh_indir *);
>  	int	(*set_rxfh_indir)(struct net_device *,
>  				  const struct ethtool_rxfh_indir *);
> +	int (*get_loopback)(struct net_device *);
> +	int (*set_loopback)(struct net_device *, u32);

Part of the idea of the 'features' interface is to treat feature flags
uniformly in ethtool.  I think there should be just two driver
operations to get/set all features outside the first word.

[...]
> +static int ethtool_set_secondword_features(struct net_device *dev,
> +		struct ethtool_set_features_block *features)
> +{
> +	int ret = 0;
> +
> +	if (features[1].valid & ETHTOOL_LOOPBACK) {
> +		features[1].valid &= ~ETHTOOL_LOOPBACK;
> +		if (dev->ethtool_ops->set_loopback) {	
> +			ret |= dev->ethtool_ops->set_loopback(dev,
> +				!!(features[1].requested & ETHTOOL_LOOPBACK));
> +		}
> +	}
> +
> +	return(ret);
> +}

This fails silently if the device doesn't implement set_loopback.

Minor style point: don't use parentheses around the return value.

> +
>  static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
>  {
>  	struct ethtool_gfeatures cmd = {
> @@ -251,12 +281,19 @@ static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
>  			.active = dev->features,
>  			.never_changed = NETIF_F_NEVER_CHANGE,
>  		},
> +		{
> +			.available = ETHTOOL_VALID_2NDWORD_FEATURES,

Should be 0 by default.

> +			.active = 0,
> +		},
>  	};
>  	u32 __user *sizeaddr;
>  	u32 copy_size;
>  
>  	ethtool_get_features_compat(dev, features);
>  
> +	/* Get 2nd-word features */
> +	ethtool_get_secondword_features(dev, features);
> +

If you define a get_ext_features operation, this could be:

	if (dev->ethtool_ops && dev->ethtool_ops->get_ext_features)
		dev->ethtool_ops->get_ext_features(dev, features);

>  	sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size);
>  	if (get_user(copy_size, sizeaddr))
>  		return -EFAULT;
> @@ -289,14 +326,20 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
>  	if (copy_from_user(features, useraddr, sizeof(features)))
>  		return -EFAULT;
>  
> -	if (features[0].valid & ~NETIF_F_ETHTOOL_BITS)
> +	if (features[0].valid & ~NETIF_F_ETHTOOL_BITS ||
> +	    features[1].valid & ~ETHTOOL_VALID_2NDWORD_FEATURES)
>  		return -EINVAL;
>  
>  	if (ethtool_set_features_compat(dev, features))
>  		ret |= ETHTOOL_F_COMPAT;
>  
> -	if (features[0].valid & ~dev->hw_features) {
> +	if (ethtool_set_secondword_features(dev, features))
> +		ret |= ETHTOOL_F_2NDWORD;
> +
> +	if ((features[0].valid & ~dev->hw_features) ||
> +	    (features[1].valid & ~ETHTOOL_VALID_2NDWORD_FEATURES)) {

We've already checked features[1].valid against this mask, so it is
pointless to do it again.  What we want to do here is to check against
what the hardware and driver support.  And beyond the first word of
features, we don't know the answer to that.  Maybe dev->hw_features
should be turned into an array?

If you define a set_ext_features operation,
ethtool_set_secondword_features() could be replaced with:

	if (features[1].valid) {
		if (!dev->ethtool_ops || !dev->ethtool_ops->set_ext_features) {
			ret |= ETHTOOL_F_UNSUPPORTED;
		} else {
			int ext_ret =
				dev->ethtool_ops->set_ext_features(dev, features);
			if (ext_ret < 0)
				return ext_ret;
			ret |= ext_ret;
		}
	}

Ben.

>  		features[0].valid &= dev->hw_features;
> +		features[1].valid &= ETHTOOL_VALID_2NDWORD_FEATURES;
>  		ret |= ETHTOOL_F_UNSUPPORTED;
>  	}
>  
> @@ -346,6 +389,9 @@ static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GS
>  	/* NETIF_F_RXCSUM */          "rx-checksum",
>  	"",
>  	"",
> +
> +	/* 2nd word of features */
> +	/* ETHTOOL_LOOPBACK */        "loopback",
>  };
>  
>  static int __ethtool_get_sset_count(struct net_device *dev, int sset)

-- 
Ben Hutchings, Senior Software Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


^ permalink raw reply

* Re: [PATCH net-next-2.6] sctp: Add Auto-ASCONF support
From: David Miller @ 2011-03-28 23:18 UTC (permalink / raw)
  To: micchie; +Cc: yjwei, netdev, lksctp-developers
In-Reply-To: <6DE94EB5-FD33-4829-AE34-C14CFDD5E527@sfc.wide.ad.jp>

From: Michio Honda <micchie@sfc.wide.ad.jp>
Date: Tue, 29 Mar 2011 03:41:34 +0900

> Thanks for the comment, I implemented the setsockopt-based control,
> and ran the checkpatch.pl.  I attach the patch.

When posting a new version of a patch, post it as a completely
new posting so that the commit message and other elements are
understood by automated tools properly.

^ permalink raw reply

* BUG: ping without route segfaults in dst_release
From: Marc Kleine-Budde @ 2011-03-28 23:18 UTC (permalink / raw)
  To: Netdev; +Cc: David Miller, kernel@pengutronix.de

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

Hello,

I just found this breakage in current mainline (v2.6.38-8876-g036a982)

root@ptx:~ ifconfig
eth0      Link encap:Ethernet  HWaddr 00:04:9F:01:7E:E9
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

root@ptx:~ ping 130.75.1.32
PING 130.75.1.32 (130.75[   10.160000] Unable to handle kernel paging request at virtual address ffffffdb
[   10.160000] pgd = c7998000
[   10.160000] [ffffffdb] *pgd=47ffe831, *pte=00000000, *ppte=00000000
[   10.160000] Internal error: Oops: 1 [#1]
[   10.160000] last sysfs file: /sys/kernel/uevent_seqnum
[   10.160000] Modules linked in:
[   10.160000] CPU: 0    Not tainted  (2.6.38+ #10)
[   10.160000] PC is at dst_release+0x1c/0x80
[   10.160000] LR is at raw_sendmsg+0x68c/0x714
[   10.160000] pc : [<c017a3fc>]    lr : [<c01ae0d0>]    psr: a0000093
[   10.160000] sp : c7965d70  ip : 8220014b  fp : 00000000
[   10.160000] r10: 00000000  r9 : 00000000  r8 : 20014b82
[   10.160000] r7 : 00000001  r6 : c78e1b40  r5 : ffffff9b  r4 : ffffff9b
[   10.160000] r3 : a0000093  r2 : a0000013  r1 : 6b006b83  r0 : ffffff9b
[   10.160000] Flags: NzCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment user
[   10.160000] Control: 0005317f  Table: 47998000  DAC: 00000015
[   10.160000] Process ping (pid: 102, stack limit = 0xc7964270)
[   10.160000] Stack: (0xc7965d70 to 0xc7966000)
[   10.160000] 5d60:                                     00000000 ffffff9b c7965f5c c01ae0d0
[   10.160000] 5d80: 60000013 c7994fc0 00000000 80000093 00000040 00000000 00000000 00000000
[   10.160000] 5da0: 00000000 04010000 00000000 20014b82 00000000 00000008 20014b82 00000000
[   10.160000] 5dc0: 00000000 c791f500 c7965dec ffffff9b 00000062 c78e1b40 00000040 c7965e10
[   10.160000] 5de0: c7965f5c 00085cc8 c7964000 0000001c be8b4e54 c01b7858 00000000 00000000
[   10.160000] 5e00: 00000000 00000000 c7965edc c0163810 c03d8e60 00000000 00000000 00000001
[   10.160000] 5e20: ffffffff 00000000 00000000 00000000 00000000 00000000 c7994fc0 00000200
[   10.160000] 5e40: 00000000 00000000 0006a000 c03d8e60 c7965e88 00000000 c7998000 c791f580
[   10.160000] 5e60: c79761a8 0006a000 c7998000 00000000 c7a16000 c007a8c8 00000062 00000000
[   10.160000] 5e80: 00000000 c03cf7e0 00000000 c006b76c c03cf7e0 00000040 c74752c0 00000000
[   10.160000] 5ea0: 00000000 c7965f5c c7998000 c74752c0 00000000 c7965edc 00085cc8 c7965edc
[   10.160000] 5ec0: 0000001c 00000000 00000040 c74752c0 00000000 c0164144 80000005 00000002
[   10.160000] 5ee0: 20014b82 00000000 00000000 00000000 00000000 00000000 00000005 c7965fb0
[   10.160000] 5f00: 0006a2bc be8b4e54 00085c70 c0021220 c02a4408 c01f13b4 00000000 c008e684
[   10.160000] 5f20: c7965f58 00000000 00000001 c78e1b40 00001c70 c74752c0 00000000 c0168020
[   10.160000] 5f40: c7965f4c c016456c c0296310 be8b4968 00000002 000000ae c0022088 c7965edc
[   10.160000] 5f60: 0000001c c7965f78 00000001 00000000 00000000 00000000 00ad80f8 00000040
[   10.160000] 5f80: 00000000 00000000 00000000 00085cc8 0000001c 00085c70 00000122 c0022088
[   10.160000] 5fa0: 00000000 c0021ee0 00085cc8 0000001c 00000003 00ad80f8 00000040 00000000
[   10.160000] 5fc0: 00085cc8 0000001c 00085c70 00000122 00000000 be8b4e54 00085c70 be8b4e54
[   10.160000] 5fe0: 000179d4 be8b4b70 0000da90 401a7f28 60000010 00000003 00000001 0000000a
[   10.160000] [<c017a3fc>] (dst_release+0x1c/0x80) from [<c01ae0d0>] (raw_sendmsg+0x68c/0x714)
[   10.160000] [<c01ae0d0>] (raw_sendmsg+0x68c/0x714) from [<c01b7858>] (inet_sendmsg+0x68/0x74)
[   10.160000] [<c01b7858>] (inet_sendmsg+0x68/0x74) from [<c0163810>] (sock_sendmsg+0x88/0xa8)
[   10.160000] [<c0163810>] (sock_sendmsg+0x88/0xa8) from [<c0164144>] (sys_sendto+0xb4/0xd8)
[   10.160000] [<c0164144>] (sys_sendto+0xb4/0xd8) from [<c0021ee0>] (ret_fast_syscall+0x0/0x2c)
[   10.160000] Code: 0a000017 e10f2000 e3823080 e121f003 (e5953040)
.1.32): 56 data bytes
[   10.470000] ---[ end trace 6d4174e1407fa33f ]---
Segmentation fault

2.6.38 works:

root@ptx:~ ping 130.75.1.32
PING 130.75.1.32 (130.75.1.32): 56 data bytes
ping: sendto: Network is unreachable

cheers, Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply

* [PATCH v2] net/usb: Ethernet quirks for the LG-VL600 4G modem
From: Andrzej Zaborowski @ 2011-03-28 22:56 UTC (permalink / raw)
  To: netdev; +Cc: Oliver Neukum, David Brownell, linux-kernel, balrogg
In-Reply-To: <1301263473-27849-1-git-send-email-andrew.zaborowski@intel.com>

This adds a driver for the CDC Ethernet part of this modem.  The
device's ID is blacklisted in cdc_ether.c and is white-listed in
this new driver because of the quirks needed to make it useful.
The modem's firmware exposes a CDC ACM port for modem control and a
CDC Ethernet port for network data.  The descriptors look fine but
both ports actually are some sort of multiplexers requiring non-
standard headers added/removed from every packet or they get
ignored.  All information is based on a usb traffic log from a
Windows machine.

On the Verizon 4G network I've seen speeds up to 1.1MB/s so far with
this driver, a speed-o-meter site reports 16.2Mbps/10.5Mbps.
Userspace scripts are required to talk to the CDC ACM port.

Signed-off-by: Andrzej Zaborowski <balrogg@gmail.com>
---
Changes in this version:
I just found out about IFF_NOARP which made a big ugly hack in the
code unnecessary.
Also the driver now handles potential fragmented packets spanning
multiple incoming URBs.  Note I used the .driver_priv field in usbnet
which none of the other drivers uses, possibly would be better to
extend cdc_state with a "void *priv_data" field.
---
 drivers/net/usb/Kconfig     |   15 ++
 drivers/net/usb/Makefile    |    1 +
 drivers/net/usb/cdc_ether.c |   21 ++-
 drivers/net/usb/lg-vl600.c  |  346 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/usbnet.h  |    3 +-
 5 files changed, 356 insertions(+), 7 deletions(-)
 create mode 100644 drivers/net/usb/lg-vl600.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 6f600cc..3ec22c3 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -433,4 +433,19 @@ config USB_SIERRA_NET
 	  To compile this driver as a module, choose M here: the
 	  module will be called sierra_net.
 
+config USB_VL600
+	tristate "LG VL600 modem dongle"
+	depends on USB_NET_CDCETHER
+	select USB_ACM
+	help
+	  Select this if you want to use an LG Electronics 4G/LTE usb modem
+	  called VL600.  This driver only handles the ethernet
+	  interface exposed by the modem firmware.  To establish a connection
+	  you will first need a userspace program that sends the right
+	  command to the modem through its CDC ACM port, and most
+	  likely also a DHCP client.  See this thread about using the
+	  4G modem from Verizon:
+
+	  http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
+
 endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index cac1703..c7ec8a5 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -27,4 +27,5 @@ obj-$(CONFIG_USB_IPHETH)	+= ipheth.o
 obj-$(CONFIG_USB_SIERRA_NET)	+= sierra_net.o
 obj-$(CONFIG_USB_NET_CX82310_ETH)	+= cx82310_eth.o
 obj-$(CONFIG_USB_NET_CDC_NCM)	+= cdc_ncm.o
+obj-$(CONFIG_USB_VL600)		+= lg-vl600.o
 
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 9a60e41..51c259b 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -378,7 +378,7 @@ static void dumpspeed(struct usbnet *dev, __le32 *speeds)
 		   __le32_to_cpu(speeds[1]) / 1000);
 }
 
-static void cdc_status(struct usbnet *dev, struct urb *urb)
+void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
 {
 	struct usb_cdc_notification	*event;
 
@@ -418,8 +418,9 @@ static void cdc_status(struct usbnet *dev, struct urb *urb)
 		break;
 	}
 }
+EXPORT_SYMBOL_GPL(usbnet_cdc_status);
 
-static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)
+int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
 {
 	int				status;
 	struct cdc_state		*info = (void *) &dev->data;
@@ -441,6 +442,7 @@ static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)
 	 */
 	return 0;
 }
+EXPORT_SYMBOL_GPL(usbnet_cdc_bind);
 
 static int cdc_manage_power(struct usbnet *dev, int on)
 {
@@ -452,18 +454,18 @@ static const struct driver_info	cdc_info = {
 	.description =	"CDC Ethernet Device",
 	.flags =	FLAG_ETHER,
 	// .check_connect = cdc_check_connect,
-	.bind =		cdc_bind,
+	.bind =		usbnet_cdc_bind,
 	.unbind =	usbnet_cdc_unbind,
-	.status =	cdc_status,
+	.status =	usbnet_cdc_status,
 	.manage_power =	cdc_manage_power,
 };
 
 static const struct driver_info mbm_info = {
 	.description =	"Mobile Broadband Network Device",
 	.flags =	FLAG_WWAN,
-	.bind = 	cdc_bind,
+	.bind =		usbnet_cdc_bind,
 	.unbind =	usbnet_cdc_unbind,
-	.status =	cdc_status,
+	.status =	usbnet_cdc_status,
 	.manage_power =	cdc_manage_power,
 };
 
@@ -560,6 +562,13 @@ static const struct usb_device_id	products [] = {
 	.driver_info		= 0,
 },
 
+/* LG Electronics VL600 wants additional headers on every frame */
+{
+	USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM,
+			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+	.driver_info		= 0,
+},
+
 /*
  * WHITELIST!!!
  *
diff --git a/drivers/net/usb/lg-vl600.c b/drivers/net/usb/lg-vl600.c
new file mode 100644
index 0000000..f9a7ced
--- /dev/null
+++ b/drivers/net/usb/lg-vl600.c
@@ -0,0 +1,346 @@
+/*
+ * Ethernet interface part of the LG VL600 LTE modem (4G dongle)
+ *
+ * Copyright (C) 2011 Intel Corporation
+ * Author: Andrzej Zaborowski <balrogg@gmail.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/usbnet.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+
+/*
+ * The device has a CDC ACM port for modem control (it claims to be
+ * CDC ACM anyway) and a CDC Ethernet port for actual network data.
+ * It will however ignore data on both ports that is not encapsulated
+ * in a specific way, any data returned is also encapsulated the same
+ * way.  The headers don't seem to follow any popular standard.
+ *
+ * This driver adds and strips these headers from the ethernet frames
+ * sent/received from the CDC Ethernet port.  The proprietary header
+ * replaces the standard ethernet header in a packet so only actual
+ * ethernet frames are allowed.  The headers allow some form of
+ * multiplexing by using non standard values of the .h_proto field.
+ * Windows/Mac drivers do send a couple of such frames to the device
+ * during initialisation, with protocol set to 0x0906 or 0x0b06 and (what
+ * seems to be) a flag in the .dummy_flags.  This doesn't seem necessary
+ * for modem operation but can possibly be used for GPS or other funcitons.
+ */
+
+struct vl600_frame_hdr {
+	__le32 len;
+	__le32 serial;
+	__le32 pkt_cnt;
+	__le32 dummy_flags;
+	__le32 dummy;
+	__le32 magic;
+} __attribute__((packed));
+
+struct vl600_pkt_hdr {
+	__le32 dummy[2];
+	__le32 len;
+	__be16 h_proto;
+} __attribute__((packed));
+
+struct vl600_state {
+	struct sk_buff *current_rx_buf;
+};
+
+static int vl600_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret;
+	struct vl600_state *s = kzalloc(sizeof(struct vl600_state), GFP_KERNEL);
+
+	if (!s)
+		return -ENOMEM;
+
+	ret = usbnet_cdc_bind(dev, intf);
+	if (ret) {
+		kfree(s);
+		return ret;
+	}
+
+	dev->driver_priv = s;
+
+	/* ARP packets don't go through, but they're also of no use.  The
+	 * subnet has only two hosts anyway: us and the gateway / DHCP
+	 * server (probably simulated by modem firmware or network operator)
+	 * whose address changes everytime we connect to the intarwebz and
+	 * who doesn't bother answering ARP requests either.  So hardware
+	 * addresses have no meaning, the destination and the source of every
+	 * packet depend only on whether it is on the IN or OUT endpoint.  */
+	dev->net->flags |= IFF_NOARP;
+
+	return ret;
+}
+
+static void vl600_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct vl600_state *s = dev->driver_priv;
+
+	if (s->current_rx_buf)
+		dev_kfree_skb(s->current_rx_buf);
+
+	kfree(s);
+
+	return usbnet_cdc_unbind(dev, intf);
+}
+
+static int vl600_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct vl600_frame_hdr *frame;
+	struct vl600_pkt_hdr *packet;
+	struct ethhdr *ethhdr;
+	int packet_len, count;
+	struct sk_buff *buf = skb;
+	struct sk_buff *clone;
+	struct vl600_state *s = dev->driver_priv;
+
+	/* Frame lengths are generally 4B multiplies but every couple of
+	 * hours there's an odd number of bytes sized yet correct frame,
+	 * so don't require this.  */
+
+	/* Allow a packet (or multiple packets batched together) to be
+	 * split across many frames.  We don't allow a new batch to
+	 * begin in the same frame another one is ending however, and no
+	 * leading or trailing pad bytes.  */
+	if (s->current_rx_buf) {
+		frame = (struct vl600_frame_hdr *) s->current_rx_buf->data;
+		if (skb->len + s->current_rx_buf->len >
+				le32_to_cpup(&frame->len)) {
+			netif_err(dev, ifup, dev->net, "Fragment too long\n");
+			dev->net->stats.rx_length_errors++;
+			goto error;
+		}
+
+		buf = s->current_rx_buf;
+		memcpy(skb_put(buf, skb->len), skb->data, skb->len);
+	} else if (skb->len < 4) {
+		netif_err(dev, ifup, dev->net, "Frame too short\n");
+		dev->net->stats.rx_length_errors++;
+		goto error;
+	}
+
+	frame = (struct vl600_frame_hdr *) buf->data;
+	/* NOTE: Should check that frame->magic == 0x53544448?
+	 * Otherwise if we receive garbage at the beginning of the frame
+	 * we may end up allocating a huge buffer and saving all the
+	 * future incoming data into it.  */
+
+	if (buf->len < sizeof(*frame) ||
+			buf->len != le32_to_cpup(&frame->len)) {
+		/* Save this fragment for later assembly */
+		if (s->current_rx_buf)
+			return 0;
+
+		s->current_rx_buf = skb_copy_expand(skb, 0,
+				le32_to_cpup(&frame->len), GFP_ATOMIC);
+		if (!s->current_rx_buf) {
+			netif_err(dev, ifup, dev->net, "Reserving %i bytes "
+					"for packet assembly failed.\n",
+					le32_to_cpup(&frame->len));
+			dev->net->stats.rx_errors++;
+		}
+
+		return 0;
+	}
+
+	count = le32_to_cpup(&frame->pkt_cnt);
+
+	skb_pull(buf, sizeof(*frame));
+
+	while (count--) {
+		if (buf->len < sizeof(*packet)) {
+			netif_err(dev, ifup, dev->net, "Packet too short\n");
+			goto error;
+		}
+
+		packet = (struct vl600_pkt_hdr *) buf->data;
+		packet_len = sizeof(*packet) + le32_to_cpup(&packet->len);
+		if (packet_len > buf->len) {
+			netif_err(dev, ifup, dev->net,
+					"Bad packet length stored in header\n");
+			goto error;
+		}
+
+		/* Packet header is same size as the ethernet header
+		 * (sizeof(*packet) == sizeof(*ethhdr)), additionally
+		 * the h_proto field is in the same place so we just leave it
+		 * alone and fill in the remaining fields.
+		 */
+		ethhdr = (struct ethhdr *) skb->data;
+		if (be16_to_cpup(&ethhdr->h_proto) == ETH_P_ARP &&
+				buf->len > 0x26) {
+			/* Copy the addresses from packet contents */
+			memcpy(ethhdr->h_source,
+					&buf->data[sizeof(*ethhdr) + 0x8],
+					ETH_ALEN);
+			memcpy(ethhdr->h_dest,
+					&buf->data[sizeof(*ethhdr) + 0x12],
+					ETH_ALEN);
+		} else {
+			memset(ethhdr->h_source, 0, ETH_ALEN);
+			memcpy(ethhdr->h_dest, dev->net->dev_addr, ETH_ALEN);
+		}
+
+		if (count) {
+			/* Not the last packet in this batch */
+			clone = skb_clone(buf, GFP_ATOMIC);
+			if (!clone)
+				goto error;
+
+			skb_trim(clone, packet_len);
+			usbnet_skb_return(dev, clone);
+
+			skb_pull(buf, (packet_len + 3) & ~3);
+		} else {
+			skb_trim(buf, packet_len);
+
+			if (s->current_rx_buf) {
+				usbnet_skb_return(dev, buf);
+				s->current_rx_buf = NULL;
+				return 0;
+			}
+
+			return 1;
+		}
+	}
+
+error:
+	if (s->current_rx_buf) {
+		dev_kfree_skb_any(s->current_rx_buf);
+		s->current_rx_buf = NULL;
+	}
+	dev->net->stats.rx_errors++;
+	return 0;
+}
+
+static struct sk_buff *vl600_tx_fixup(struct usbnet *dev,
+		struct sk_buff *skb, gfp_t flags)
+{
+	struct sk_buff *ret;
+	struct vl600_frame_hdr *frame;
+	struct vl600_pkt_hdr *packet;
+	static uint32_t serial = 1;
+	int orig_len = skb->len - sizeof(struct ethhdr);
+	int full_len = (skb->len + sizeof(struct vl600_frame_hdr) + 3) & ~3;
+
+	frame = (struct vl600_frame_hdr *) skb->data;
+	if (skb->len > sizeof(*frame) && skb->len == le32_to_cpup(&frame->len))
+		return skb; /* Already encapsulated? */
+
+	if (skb->len < sizeof(struct ethhdr))
+		/* Drop, device can only deal with ethernet packets */
+		return NULL;
+
+	if (!skb_cloned(skb)) {
+		int headroom = skb_headroom(skb);
+		int tailroom = skb_tailroom(skb);
+
+		if (tailroom >= full_len - skb->len - sizeof(*frame) &&
+				headroom >= sizeof(*frame))
+			/* There's enough head and tail room */
+			goto encapsulate;
+
+		if (headroom + tailroom + skb->len >= full_len) {
+			/* There's enough total room, just readjust */
+			skb->data = memmove(skb->head + sizeof(*frame),
+					skb->data, skb->len);
+			skb_set_tail_pointer(skb, skb->len);
+			goto encapsulate;
+		}
+	}
+
+	/* Alloc a new skb with the required size */
+	ret = skb_copy_expand(skb, sizeof(struct vl600_frame_hdr), full_len -
+			skb->len - sizeof(struct vl600_frame_hdr), flags);
+	dev_kfree_skb_any(skb);
+	if (!ret)
+		return ret;
+	skb = ret;
+
+encapsulate:
+	/* Packet header is same size as ethernet packet header
+	 * (sizeof(*packet) == sizeof(struct ethhdr)), additionally the
+	 * h_proto field is in the same place so we just leave it alone and
+	 * overwrite the remaining fields.
+	 */
+	packet = (struct vl600_pkt_hdr *) skb->data;
+	memset(&packet->dummy, 0, sizeof(packet->dummy));
+	packet->len = cpu_to_le32(orig_len);
+
+	frame = (struct vl600_frame_hdr *) skb_push(skb, sizeof(*frame));
+	memset(frame, 0, sizeof(*frame));
+	frame->len = cpu_to_le32(full_len);
+	frame->serial = cpu_to_le32(serial++);
+	frame->pkt_cnt = cpu_to_le32(1);
+
+	if (skb->len < full_len) /* Pad */
+		skb_put(skb, full_len - skb->len);
+
+	return skb;
+}
+
+static const struct driver_info	vl600_info = {
+	.description	= "LG VL600 modem",
+	.flags		= FLAG_ETHER | FLAG_RX_ASSEMBLE,
+	.bind		= vl600_bind,
+	.unbind		= vl600_unbind,
+	.status		= usbnet_cdc_status,
+	.rx_fixup	= vl600_rx_fixup,
+	.tx_fixup	= vl600_tx_fixup,
+};
+
+static const struct usb_device_id products[] = {
+	{
+		USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM,
+				USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+		.driver_info	= (unsigned long) &vl600_info,
+	},
+	{},	/* End */
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver lg_vl600_driver = {
+	.name		= "lg-vl600",
+	.id_table	= products,
+	.probe		= usbnet_probe,
+	.disconnect	= usbnet_disconnect,
+	.suspend	= usbnet_suspend,
+	.resume		= usbnet_resume,
+};
+
+static int __init vl600_init(void)
+{
+	return usb_register(&lg_vl600_driver);
+}
+module_init(vl600_init);
+
+static void __exit vl600_exit(void)
+{
+	usb_deregister(&lg_vl600_driver);
+}
+module_exit(vl600_exit);
+
+MODULE_AUTHOR("Anrzej Zaborowski");
+MODULE_DESCRIPTION("LG-VL600 modem's ethernet link");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 95c41d5..cf58b76 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -387,8 +387,12 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
 static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
 {
 	if (dev->driver_info->rx_fixup &&
-	    !dev->driver_info->rx_fixup (dev, skb))
-		goto error;
+	    !dev->driver_info->rx_fixup (dev, skb)) {
+		/* With RX_ASSEMBLE, rx_fixup() must update counters */
+		if (!(dev->driver_info->flags & FLAG_RX_ASSEMBLE))
+			dev->net->stats.rx_errors++;
+		goto done;
+	}
 	// else network stack removes extra byte if we forced a short packet
 
 	if (skb->len) {
@@ -401,8 +405,8 @@ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
 	}
 
 	netif_dbg(dev, rx_err, dev->net, "drop\n");
-error:
 	dev->net->stats.rx_errors++;
+done:
 	skb_queue_tail(&dev->done, skb);
 }
 
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 44842c8..201f222 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -102,6 +102,7 @@ struct driver_info {
  * Affects statistic (counters) and short packet handling.
  */
 #define FLAG_MULTI_PACKET	0x1000
+#define FLAG_RX_ASSEMBLE	0x2000	/* rx packets may span >1 frames */
 
 	/* init device ... can sleep, or cause probe() failure */
 	int	(*bind)(struct usbnet *, struct usb_interface *);
@@ -172,7 +173,9 @@ struct cdc_state {
 };
 
 extern int usbnet_generic_cdc_bind(struct usbnet *, struct usb_interface *);
+extern int usbnet_cdc_bind(struct usbnet *, struct usb_interface *);
 extern void usbnet_cdc_unbind(struct usbnet *, struct usb_interface *);
+extern void usbnet_cdc_status(struct usbnet *, struct urb *);
 
 /* CDC and RNDIS support the same host-chosen packet filters for IN transfers */
 #define	DEFAULT_FILTER	(USB_CDC_PACKET_TYPE_BROADCAST \
-- 
1.7.1.86.g0e460.dirty

^ permalink raw reply related

* [PATCH net-next] net-ethtool: Allow ethtool to set interface in loopback mode.
From: Mahesh Bandewar @ 2011-03-28 22:24 UTC (permalink / raw)
  To: David Miller; +Cc: Ben Hutchings, netdev, Tom Herbert, Mahesh Bandewar

Add second word for feature (currently it's single word). Also use the first
bit of the second word to set the loopback mode. By configuring the interface
in loopback mode in conjunction with a policy route / rule, a user-land
application can stress the egress / ingress path exposing the flows of the
change in progress and potentially help developer(s) understand the impact of
those changes without even sending a packet out on the network.

Following set of commands illustrates one such example -
    a) ip -4 addr add 192.168.1.1/24 dev eth1
    b) ip -4 rule add from all iif eth1 lookup 250
    c) ip -4 route add local 0/0 dev lo proto kernel scope host table 250
    d) arp -Ds 192.168.1.100 eth1
    e) arp -Ds 192.168.1.200 eth1
    f) sysctl -w net.ipv4.ip_nonlocal_bind=1
    g) sysctl -w net.ipv4.conf.all.accept_local=1
    # Assuming that the machine has 8 cores
    h) taskset 000f netserver -L 192.168.1.200
    i) taskset 00f0 netperf -t TCP_CRR -L 192.168.1.100 -H 192.168.1.200 -l 30

Signed-off-by: Mahesh Bandewar <maheshb@google.com>
---
 include/linux/ethtool.h |   14 ++++++++++++
 net/core/ethtool.c      |   54 +++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 64 insertions(+), 4 deletions(-)

diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index aac3e2e..a42f60c 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -526,6 +526,8 @@ struct ethtool_flash {
 
 /* for returning and changing feature sets */
 
+#define ETHTOOL_DEV_FEATURE_WORDS	2
+
 /**
  * struct ethtool_get_features_block - block with state of 32 features
  * @available: mask of changeable features
@@ -594,6 +596,8 @@ struct ethtool_sfeatures {
  *   %ETHTOOL_F_COMPAT - some or all changes requested were made by calling
  *      compatibility functions. Requested offload state cannot be properly
  *      managed by kernel.
+ *   %ETHTOOL_F_2NDWORD - some or all changes requested in the second word of
+ *      the features failed.
  *
  * Meaning of bits in the masks are obtained by %ETHTOOL_GSSET_INFO (number of
  * bits in the arrays - always multiple of 32) and %ETHTOOL_GSTRINGS commands
@@ -604,11 +608,13 @@ enum ethtool_sfeatures_retval_bits {
 	ETHTOOL_F_UNSUPPORTED__BIT,
 	ETHTOOL_F_WISH__BIT,
 	ETHTOOL_F_COMPAT__BIT,
+	ETHTOOL_F_2NDWORD__BIT,
 };
 
 #define ETHTOOL_F_UNSUPPORTED   (1 << ETHTOOL_F_UNSUPPORTED__BIT)
 #define ETHTOOL_F_WISH          (1 << ETHTOOL_F_WISH__BIT)
 #define ETHTOOL_F_COMPAT        (1 << ETHTOOL_F_COMPAT__BIT)
+#define ETHTOOL_F_2NDWORD       (1 << ETHTOOL_F_2NDWORD__BIT)
 
 #ifdef __KERNEL__
 
@@ -764,6 +770,8 @@ struct ethtool_ops {
 				  struct ethtool_rxfh_indir *);
 	int	(*set_rxfh_indir)(struct net_device *,
 				  const struct ethtool_rxfh_indir *);
+	int (*get_loopback)(struct net_device *);
+	int (*set_loopback)(struct net_device *, u32);
 };
 #endif /* __KERNEL__ */
 
@@ -837,6 +845,12 @@ struct ethtool_ops {
 #define SPARC_ETH_GSET		ETHTOOL_GSET
 #define SPARC_ETH_SSET		ETHTOOL_SSET
 
+/* Additional features (2 word of the feature-set) */
+#define ETHTOOL_LOOPBACK	(1 << 0)  /* Enable/Disable loop-back feature */
+
+/* Mask of all valid 2nd word features */
+#define ETHTOOL_VALID_2NDWORD_FEATURES	ETHTOOL_LOOPBACK
+
 /* Indicates what features are supported by the interface. */
 #define SUPPORTED_10baseT_Half		(1 << 0)
 #define SUPPORTED_10baseT_Full		(1 << 1)
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index c1a71bb..4f2c2d5 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -166,8 +166,6 @@ EXPORT_SYMBOL(ethtool_ntuple_flush);
 
 /* Handlers for each ethtool command */
 
-#define ETHTOOL_DEV_FEATURE_WORDS	1
-
 static void ethtool_get_features_compat(struct net_device *dev,
 	struct ethtool_get_features_block *features)
 {
@@ -238,6 +236,38 @@ static int ethtool_set_features_compat(struct net_device *dev,
 	return compat;
 }
 
+static void ethtool_get_secondword_features(struct net_device *dev,
+		struct ethtool_get_features_block *features)
+{
+	if (!dev->ethtool_ops)
+		return;
+
+	if (dev->ethtool_ops->get_loopback) {
+		if (dev->ethtool_ops->get_loopback(dev))
+		    features[1].active |= ETHTOOL_LOOPBACK;
+	} else
+		features[1].available &= ~ETHTOOL_LOOPBACK;
+
+
+	return;
+}
+
+static int ethtool_set_secondword_features(struct net_device *dev,
+		struct ethtool_set_features_block *features)
+{
+	int ret = 0;
+
+	if (features[1].valid & ETHTOOL_LOOPBACK) {
+		features[1].valid &= ~ETHTOOL_LOOPBACK;
+		if (dev->ethtool_ops->set_loopback) {	
+			ret |= dev->ethtool_ops->set_loopback(dev,
+				!!(features[1].requested & ETHTOOL_LOOPBACK));
+		}
+	}
+
+	return(ret);
+}
+
 static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
 {
 	struct ethtool_gfeatures cmd = {
@@ -251,12 +281,19 @@ static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
 			.active = dev->features,
 			.never_changed = NETIF_F_NEVER_CHANGE,
 		},
+		{
+			.available = ETHTOOL_VALID_2NDWORD_FEATURES,
+			.active = 0,
+		},
 	};
 	u32 __user *sizeaddr;
 	u32 copy_size;
 
 	ethtool_get_features_compat(dev, features);
 
+	/* Get 2nd-word features */
+	ethtool_get_secondword_features(dev, features);
+
 	sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size);
 	if (get_user(copy_size, sizeaddr))
 		return -EFAULT;
@@ -289,14 +326,20 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
 	if (copy_from_user(features, useraddr, sizeof(features)))
 		return -EFAULT;
 
-	if (features[0].valid & ~NETIF_F_ETHTOOL_BITS)
+	if (features[0].valid & ~NETIF_F_ETHTOOL_BITS ||
+	    features[1].valid & ~ETHTOOL_VALID_2NDWORD_FEATURES)
 		return -EINVAL;
 
 	if (ethtool_set_features_compat(dev, features))
 		ret |= ETHTOOL_F_COMPAT;
 
-	if (features[0].valid & ~dev->hw_features) {
+	if (ethtool_set_secondword_features(dev, features))
+		ret |= ETHTOOL_F_2NDWORD;
+
+	if ((features[0].valid & ~dev->hw_features) ||
+	    (features[1].valid & ~ETHTOOL_VALID_2NDWORD_FEATURES)) {
 		features[0].valid &= dev->hw_features;
+		features[1].valid &= ETHTOOL_VALID_2NDWORD_FEATURES;
 		ret |= ETHTOOL_F_UNSUPPORTED;
 	}
 
@@ -346,6 +389,9 @@ static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GS
 	/* NETIF_F_RXCSUM */          "rx-checksum",
 	"",
 	"",
+
+	/* 2nd word of features */
+	/* ETHTOOL_LOOPBACK */        "loopback",
 };
 
 static int __ethtool_get_sset_count(struct net_device *dev, int sset)
-- 
1.7.3.1


^ permalink raw reply related

* Re: [PATCH 2/2 v2] tg3: Don't use IRQF_SAMPLE_RANDOM
From: Ben Hutchings @ 2011-03-28 20:32 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Javier Martinez Canillas, Eric Dumazet, David Miller,
	Dan Carpenter, netdev, kernel-janitors
In-Reply-To: <201103282208.05830.ffainelli@freebox.fr>

On Mon, 2011-03-28 at 22:08 +0200, Florian Fainelli wrote:
> Hello,
> 
> On Monday 28 March 2011 19:20:22 Ben Hutchings wrote:
> > On Mon, 2011-03-28 at 17:46 +0200, Javier Martinez Canillas wrote:
> > > On Mon, Mar 28, 2011 at 2:25 PM, Eric Dumazet <eric.dumazet@gmail.com> 
> wrote:
> > > > 1) We dont believe its Janitor material ;)
> > > > 
> > > > http://thread.gmane.org/gmane.linux.kernel/680723
> > > > 
> > > > http://kerneltrap.org/mailarchive/linux-netdev/2009/4/6/5417754
> > > 
> > > I wasn't aware of this discussion. In one hand network drivers are not
> > > a good source of entropy because they can be controlled externally,
> > > but in embedded systems with only network cards (no video, audio,
> > > keyboard, etc) the only source of entropy they have is their network
> > > cards (at the kernel level i.e: not using EGD to feed /dev/random).
> > 
> > It may be the only source of entropy, but given how poor a source it is
> > these drivers are basically telling sweet little lies to the kernel and
> > the applications that demand real random numbers.
> > 
> > This also applies to many servers just as much as embedded systems.
> 
> Indeed, this is the reason why bcm63xx_enet.c for instance contributes to the 
> general entropy of the system, because it is used on MIPS based ADSL routers 
> where we have little or no other source of entropy.
> 
> Still even poor entropy is better than none on such devices imho.
[...]

Well, only if it's treated as such.

add_timer_randomness() does try to measure how much entropy it's getting
from the time, which should address the problem of interrupt moderation
causing predictable interrupts.  However I strongly suspect that its
tests cannot protect against deliberately timed packets, particularly if
get_cycles() has low resolution.  (On MIPS, get_cycles() always returns
0, although the comment before suggests that this is possible to do
better.)

I notice that there is an internal option 'dont_count_entropy' which
would allow interrupts to add entropy without it increasing the
estimated total entropy in the pool.  If that was set for network
interrupts, they could be used to improve the quality of /dev/urandom,
get_random_bits(), etc. without compromising applications that demand
real randomness.

Ben.

-- 
Ben Hutchings, Senior Software Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


^ permalink raw reply

* Re: [PATCH 2/2 v2] tg3: Don't use IRQF_SAMPLE_RANDOM
From: Florian Fainelli @ 2011-03-28 20:08 UTC (permalink / raw)
  To: Ben Hutchings
  Cc: Javier Martinez Canillas, Eric Dumazet, David Miller,
	Dan Carpenter, netdev, kernel-janitors
In-Reply-To: <1301332822.2716.12.camel@bwh-desktop>

Hello,

On Monday 28 March 2011 19:20:22 Ben Hutchings wrote:
> On Mon, 2011-03-28 at 17:46 +0200, Javier Martinez Canillas wrote:
> > On Mon, Mar 28, 2011 at 2:25 PM, Eric Dumazet <eric.dumazet@gmail.com> 
wrote:
> > > 1) We dont believe its Janitor material ;)
> > > 
> > > http://thread.gmane.org/gmane.linux.kernel/680723
> > > 
> > > http://kerneltrap.org/mailarchive/linux-netdev/2009/4/6/5417754
> > 
> > I wasn't aware of this discussion. In one hand network drivers are not
> > a good source of entropy because they can be controlled externally,
> > but in embedded systems with only network cards (no video, audio,
> > keyboard, etc) the only source of entropy they have is their network
> > cards (at the kernel level i.e: not using EGD to feed /dev/random).
> 
> It may be the only source of entropy, but given how poor a source it is
> these drivers are basically telling sweet little lies to the kernel and
> the applications that demand real random numbers.
> 
> This also applies to many servers just as much as embedded systems.

Indeed, this is the reason why bcm63xx_enet.c for instance contributes to the 
general entropy of the system, because it is used on MIPS based ADSL routers 
where we have little or no other source of entropy.

Still even poor entropy is better than none on such devices imho.

> 
> As you acknowledge, these systems can still get entropy using the EGD
> protocol over a secure channel.  (Or an entropy sampling device such as
> the Entropy Key.)



> 
> > Yes this definitely is not janitor material :)
> > 
> > I just sent the patch because I saw IRQF_SAMPLE_RANDOM in
> > Documentation/feature-removal-schedule.txt. I can resend a patch
> > removing the macro in the remaining network cards if the decision is
> > to remove IRQF_SAMPLE_RANDOM.
> 
> It's not my call, but I would support it.
> 
> Ben.

^ permalink raw reply

* Re: [PATCH] usbnet: fix memory leak in usbnet_disconnect()
From: Nicolas de Pesloüan @ 2011-03-28 20:01 UTC (permalink / raw)
  To: David Miller; +Cc: netdev
In-Reply-To: <20110327.181107.115927388.davem@davemloft.net>

Le 28/03/2011 03:11, David Miller a écrit :
> From: DMITRIY GRUZMAN<xmb836@motorola.com>
> Date: Mon, 21 Mar 2011 16:49:50 -0500
>
>> usb_probe() allocates memory for transfer buffer and urb and this
>> memory was not properly released in usbnet_disconnect().   The issue
>> was found and the fix was recommended by Fu-Lei Tian and Jun-Xiao
>> Chang.
>>
>> Signed-off-by: Dmitriy Gruzman<xmb836@motorola.com>
>
> Your email client corrupted your patch, among other things:
>
>> @@ -210,6 +210,7 @@ static int init_status (struct usbnet *dev, struct
>> usb_interface *intf)
>
> it chopped up long lines.
>
> Please fix this up, test your setup by emailing the patch to yourself
> and trying to apply what you receive, then try submitting it here
> freshly again.
>
> Thanks.

This nice advice should be inserted into Documentation/SubmittingPatch.

	Nicolas.

^ permalink raw reply

* Re: [PATCH] net: Handle gso packets in dev_forward_skb
From: Eric W. Biederman @ 2011-03-28 19:36 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, netdev, greearb, arnd, kaber, Daniel Lezcano
In-Reply-To: <1301340048.2506.13.camel@edumazet-laptop>

Eric Dumazet <eric.dumazet@gmail.com> writes:

> Le dimanche 27 mars 2011 à 18:09 -0700, David Miller a écrit :
>> From: ebiederm@xmission.com (Eric W. Biederman)
>> Date: Mon, 21 Mar 2011 15:00:56 -0700
>> 
>> > Eric Dumazet <eric.dumazet@gmail.com> writes:
>> > 
>> >> Hmm, did you follow http://patchwork.ozlabs.org/patch/86815/
>> >> discussion ?
>> > 
>> > No.  It seems I missed it, but I do have the same problem, and
>> > essentially the same fix. 
>> 
>> Can someone work on getting this straightened out and resubmit the
>> final patch so I can apply something?
>> 
>
> Apparently Daniel is busy.
>
> I guess we can use Eric B. patch, then ?

That sounds good to me.

After reviewing the other thread to the best of my knowledge there are
no issues with the patch I submitted.  At worst I am 4 bytes extra
permissive about the mtu because of the way that path handles vlan
headers and that isn't something my patch changed.

Eric



^ permalink raw reply

* Re: poll broken (for can)
From: Marc Kleine-Budde @ 2011-03-28 19:32 UTC (permalink / raw)
  To: Oliver Hartkopp
  Cc: Netdev-u79uwXL29TY76Z2rM5mHXA,
	socketcan-users-0fE9KPoRgkgATYTw5x5z8w, Wolfgang Grandegger
In-Reply-To: <4D90CB17.4030205-fJ+pQTUTwRTk1uMJSBkQmQ@public.gmane.org>


[-- Attachment #1.1: Type: text/plain, Size: 2619 bytes --]

On 03/28/2011 07:53 PM, Oliver Hartkopp wrote:
> On 28.03.2011 18:13, Marc Kleine-Budde wrote:
>> On 03/28/2011 05:55 PM, Wolfgang Grandegger wrote:
>>>> BTW: I figured out why poll() wakes you up but the next write will fail
>>>> with -ENOBUFS again.
>>>
>>> Ah, I'm curious? I also did realize that poll does burn CPU cycles
>>> (instead of waiting).
>>
>> The poll callback checks if the used memory is less than the half of per
>> socket snd buffer (IIRC ~60K). See:
>>
>> datagram_poll (http://lxr.linux.no/linux+v2.6.38/net/core/datagram.c#L737)
>> sock_writeable (http://lxr.linux.no/linux+v2.6.38/include/net/sock.h#L1618)
>>
>> Because the size of a can frame (+the skb overhead) is much less then
>> the ethernet frame (+overhead) the default value for the snd buffer is
>> too big for can.
>>
>> We get the -ENOBUF from write() if the tx_queue_len (default 10) is
>> exceeded.
>>
>> http://lxr.linux.no/linux+v2.6.38/drivers/net/can/dev.c#L435
>> http://lxr.linux.no/linux+v2.6.38/net/can/af_can.c#L268
>>
> 
> What would be your suggestion? Decreasing the socket send buffer for CAN by
> default?

I haven't done any testing.....As far as I understand the code, we can
a) increase the default tx_queue_len and/or
b) decrease the default snd buffer size.

Note: a) is a per device setting whereas b) is a per socket setting.

With the current settings the -ENOBUF is triggered if we have X unsend
can frames (per device) where X equals the tx_queue_len. This means
using 5 applications, it about 2 queued (i.e. unsent) frames per app and
device.

If we increase the tx_queue_len to a high value (via ifconfig), so that
the snd buffer is fully used, before the tx_queue_len is exceeded the
write system call will block, (or return -EAGAIN of opened non
blocking). At least the last time I've done this.

I think solution b) would lead to a similar behavioural change.

What do we really want to specify?

Something like: queue up to X frames per socket and queue only Y frames
per device. Where Y = X * n and n is "I don't know yet"?

Y is simple, it's the tx_queue_len. But X is more complicated. The can
frames have non constant length (i.e. dlc) and I'm not sure that the
netdev people say if we misuse the sock_alloc_send_pskb() for our
tx-flow-control :)

Cheers, Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

[-- Attachment #2: Type: text/plain, Size: 191 bytes --]

_______________________________________________
Socketcan-users mailing list
Socketcan-users-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org
https://lists.berlios.de/mailman/listinfo/socketcan-users

^ permalink raw reply

* Re: [PATCH] net: Handle gso packets in dev_forward_skb
From: Eric Dumazet @ 2011-03-28 19:20 UTC (permalink / raw)
  To: David Miller; +Cc: ebiederm, netdev, greearb, arnd, kaber, Daniel Lezcano
In-Reply-To: <20110327.180922.59673799.davem@davemloft.net>

Le dimanche 27 mars 2011 à 18:09 -0700, David Miller a écrit :
> From: ebiederm@xmission.com (Eric W. Biederman)
> Date: Mon, 21 Mar 2011 15:00:56 -0700
> 
> > Eric Dumazet <eric.dumazet@gmail.com> writes:
> > 
> >> Hmm, did you follow http://patchwork.ozlabs.org/patch/86815/
> >> discussion ?
> > 
> > No.  It seems I missed it, but I do have the same problem, and
> > essentially the same fix. 
> 
> Can someone work on getting this straightened out and resubmit the
> final patch so I can apply something?
> 

Apparently Daniel is busy.

I guess we can use Eric B. patch, then ?




^ permalink raw reply

* Re: [PATCH net-next-2.6] sctp: Add Auto-ASCONF support
From: Michio Honda @ 2011-03-28 18:41 UTC (permalink / raw)
  To: Wei Yongjun; +Cc: netdev, lksctp-developers
In-Reply-To: <4D901CE3.60501@cn.fujitsu.com>

Hi, 

Thanks for the comment, I implemented the setsockopt-based control, and ran the checkpatch.pl.  I attach the patch.  

Best regards,
- Michio

On Mar 28, 2011, at 14:30 , Wei Yongjun wrote:

> 
>> SCTP can reconfigure the IP addresses in the association by using ASCONF chunks as mentioned in RFC5061.  
>> For example, we can start to use the newly configured IP address in the existing association.  
>> This operation is invoked in two ways: 
>> First is operation at the user land to call sctp_bindx() system call.  
>> Second is automatic operation in the SCTP stack with address events in the host computer (called auto_asconf) .  
>> The former is already implemented, but the latter is not yet. This patch enables it with one sysctl parameter.  
> 
> Socket API defined SCTP_AUTO_ASCONF options to enable/disable this
> feature, http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-27#page-76
> 
> 8.1.23.  Set or Get the Auto ASCONF Flag (SCTP_AUTO_ASCONF)
> 
>   This option will enable or disable the use of the automatic
>   generation of ASCONF chunks to add and delete addresses to an
>   existing association.  Note that this option has two caveats namely:
>   a) it only affects sockets that are bound to all addresses available
>   to the SCTP stack, and b) the system administrator may have an
>   overriding control that turns the ASCONF feature off no matter what
>   setting the socket option may have.
> 
>   This option expects an integer boolean flag, where a non-zero value
>   turns on the option, and a zero value turns off the option.
> 
> Can you implement it with this option?
> 
> Also, your patch should be check first by ./scripts/checkpatch.pl under
> kernel sorce.

Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
---

diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 11684d9..11c3060 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -767,6 +767,7 @@ enum {
 	NET_SCTP_SNDBUF_POLICY		 = 15,
 	NET_SCTP_SACK_TIMEOUT		 = 16,
 	NET_SCTP_RCVBUF_POLICY		 = 17,
+	NET_SCTP_AUTO_ASCONF_ENABLE	 = 18,
 };
 
 /* /proc/sys/net/bridge */
diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h
index c70d8cc..d7a4ee3 100644
--- a/include/net/sctp/constants.h
+++ b/include/net/sctp/constants.h
@@ -441,4 +441,8 @@ enum {
  */
 #define SCTP_AUTH_RANDOM_LENGTH 32
 
+/* ASCONF PARAMETERS */
+#define SCTP_ASCONF_V4_PARAM_LEN 16
+#define SCTP_ASCONF_V6_PARAM_LEN 28
+
 #endif /* __sctp_constants_h__ */
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index 505845d..7161932 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -121,6 +121,7 @@ extern int sctp_copy_local_addr_list(struct sctp_bind_addr *,
 				     int flags);
 extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family);
 extern int sctp_register_pf(struct sctp_pf *, sa_family_t);
+void sctp_addr_wq_mgmt(union sctp_addr *, int);
 
 /*
  * sctp/socket.c
@@ -135,6 +136,8 @@ void sctp_sock_rfree(struct sk_buff *skb);
 void sctp_copy_sock(struct sock *newsk, struct sock *sk,
 		    struct sctp_association *asoc);
 extern struct percpu_counter sctp_sockets_allocated;
+int sctp_asconf_mgmt(struct sctp_endpoint *, struct sock *sk);
+void sctp_add_addr_to_laddr(struct sockaddr *, struct sctp_association *);
 
 /*
  * sctp/primitive.c
diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
index 9352d12..498a3cf 100644
--- a/include/net/sctp/sm.h
+++ b/include/net/sctp/sm.h
@@ -259,6 +259,7 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
 				       struct sctp_chunk *asconf);
 int sctp_process_asconf_ack(struct sctp_association *asoc,
 			    struct sctp_chunk *asconf_ack);
+void sctp_path_check_and_react(struct sctp_association *, struct sockaddr *);
 struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
 				    __u32 new_cum_tsn, size_t nstreams,
 				    struct sctp_fwdtsn_skip *skiplist);
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index cc9185c..0c6a6f4 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -205,6 +205,11 @@ extern struct sctp_globals {
 	 * It is a list of sctp_sockaddr_entry.
 	 */
 	struct list_head local_addr_list;
+	int auto_asconf_enable;
+	struct list_head addr_waitq;
+	struct timer_list addr_wq_timer;
+	struct list_head auto_asconf_eplist;
+	spinlock_t addr_wq_lock;
 
 	/* Lock that protects the local_addr_list writers */
 	spinlock_t addr_list_lock;
@@ -264,6 +269,11 @@ extern struct sctp_globals {
 #define sctp_port_hashtable		(sctp_globals.port_hashtable)
 #define sctp_local_addr_list		(sctp_globals.local_addr_list)
 #define sctp_local_addr_lock		(sctp_globals.addr_list_lock)
+#define sctp_auto_asconf_eplist		(sctp_globals.auto_asconf_eplist)
+#define sctp_addr_waitq			(sctp_globals.addr_waitq)
+#define sctp_addr_wq_timer		(sctp_globals.addr_wq_timer)
+#define sctp_addr_wq_lock		(sctp_globals.addr_wq_lock)
+#define sctp_auto_asconf_enable		(sctp_globals.auto_asconf_enable)
 #define sctp_scope_policy		(sctp_globals.ipv4_scope_policy)
 #define sctp_addip_enable		(sctp_globals.addip_enable)
 #define sctp_addip_noauth		(sctp_globals.addip_noauth_enable)
@@ -796,6 +806,16 @@ struct sctp_sockaddr_entry {
 	__u8 valid;
 };
 
+#define SCTP_NEWADDR	1
+#define SCTP_DELADDR	2
+#define SCTP_ADDRESS_TICK_DELAY	500
+struct sctp_addr_wait {
+	struct list_head list;
+	struct rcu_head rcu;
+	union sctp_addr a;
+	int	cmd;
+};
+
 typedef struct sctp_chunk *(sctp_packet_phandler_t)(struct sctp_association *);
 
 /* This structure holds lists of chunks as we are assembling for
@@ -1239,6 +1259,7 @@ sctp_scope_t sctp_scope(const union sctp_addr *);
 int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope);
 int sctp_is_any(struct sock *sk, const union sctp_addr *addr);
 int sctp_addr_is_valid(const union sctp_addr *addr);
+int sctp_is_ep_boundall(struct sock *sk);
 
 
 /* What type of endpoint?  */
@@ -1267,6 +1288,7 @@ struct sctp_ep_common {
 	/* Fields to help us manage our entries in the hash tables. */
 	struct hlist_node node;
 	int hashent;
+	struct list_head auto_asconf_list;
 
 	/* Runtime type information.  What kind of endpoint is this? */
 	sctp_endpoint_type_t type;
@@ -1901,6 +1923,17 @@ struct sctp_association {
 	 * after reaching 4294967295.
 	 */
 	__u32 addip_serial;
+	/* list of valid addresses in association local
+	 * This list is needed to ensure base.bind_addr being a valid address
+	 * list of the endpoint-wide.  When one of associations receives
+	 * ASCONF-ACK, that address is added to this list.  When all
+	 * associations belonging to the same endpoint receive ASCONF-ACKs,
+	 * that address is added to base.bind_addr
+	 */
+	struct list_head asoc_laddr_list;
+	union sctp_addr *asconf_addr_del_pending;
+	__u32 asconf_del_pending_cid;
+	int src_out_of_asoc_ok;
 
 	/* SCTP AUTH: list of the endpoint shared keys.  These
 	 * keys are provided out of band by the user applicaton
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h
index e73ebda..75c96b1 100644
--- a/include/net/sctp/user.h
+++ b/include/net/sctp/user.h
@@ -91,6 +91,7 @@ typedef __s32 sctp_assoc_t;
 #define SCTP_PEER_AUTH_CHUNKS	26	/* Read only */
 #define SCTP_LOCAL_AUTH_CHUNKS	27	/* Read only */
 #define SCTP_GET_ASSOC_NUMBER	28	/* Read only */
+#define SCTP_AUTO_ASCONF	29
 
 /* Internal Socket Options. Some of the sctp library functions are
  * implemented using these socket options.
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 6b04287..082f1f0 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -280,6 +280,10 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
 	if (sctp_addip_noauth)
 		asoc->peer.asconf_capable = 1;
 
+	asoc->asconf_addr_del_pending = NULL;
+	asoc->asconf_del_pending_cid = 0;
+	asoc->src_out_of_asoc_ok = 0;
+	INIT_LIST_HEAD(&asoc->asoc_laddr_list);
 	/* Create an input queue.  */
 	sctp_inq_init(&asoc->base.inqueue);
 	sctp_inq_set_th_handler(&asoc->base.inqueue, sctp_assoc_bh_rcv);
@@ -446,6 +450,18 @@ void sctp_association_free(struct sctp_association *asoc)
 	/* Free any cached ASCONF_ACK chunk. */
 	sctp_assoc_free_asconf_acks(asoc);
 
+	/* Free pending address space being deleted */
+	if (asoc->asconf_addr_del_pending != NULL)
+		kfree(asoc->asconf_addr_del_pending);
+	if (!list_empty(&asoc->asoc_laddr_list)) {
+		struct sctp_sockaddr_entry *laddr, *tmp;
+		list_for_each_entry_safe(laddr, tmp, &asoc->asoc_laddr_list, \
+		    list) {
+			list_del(&laddr->list);
+			kfree(laddr);
+		}
+	}
+
 	/* Free any cached ASCONF chunk. */
 	if (asoc->addip_last_asconf)
 		sctp_chunk_free(asoc->addip_last_asconf);
@@ -620,6 +636,7 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
 			if (!mod_timer(&active->T3_rtx_timer,
 					jiffies + active->rto))
 				sctp_transport_hold(active);
+		active->flight_size += peer->flight_size;
 	}
 
 	asoc->peer.transport_count--;
@@ -1277,7 +1294,7 @@ void sctp_assoc_update(struct sctp_association *asoc,
  */
 void sctp_assoc_update_retran_path(struct sctp_association *asoc)
 {
-	struct sctp_transport *t, *next;
+	struct sctp_transport *t, *next, *unconfirmed;
 	struct list_head *head = &asoc->peer.transport_addr_list;
 	struct list_head *pos;
 
@@ -1287,7 +1304,7 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
 	/* Find the next transport in a round-robin fashion. */
 	t = asoc->peer.retran_path;
 	pos = &t->transports;
-	next = NULL;
+	next = unconfirmed = NULL;
 
 	while (1) {
 		/* Skip the head. */
@@ -1318,11 +1335,15 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
 			 */
 			if (t->state != SCTP_UNCONFIRMED && !next)
 				next = t;
+			else if (t->state == SCTP_UNCONFIRMED)
+				unconfirmed = t;
 		}
 	}
 
 	if (t)
 		asoc->peer.retran_path = t;
+	else if (unconfirmed)
+		asoc->peer.retran_path = t = unconfirmed;
 
 	SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association"
 				 " %p addr: ",
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c
index faf71d1..426715f 100644
--- a/net/sctp/bind_addr.c
+++ b/net/sctp/bind_addr.c
@@ -536,6 +536,23 @@ int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope)
 	return 0;
 }
 
+int sctp_is_ep_boundall(struct sock *sk)
+{
+	struct sctp_bind_addr *bp;
+	struct sctp_sockaddr_entry *addr;
+
+	bp = &sctp_sk(sk)->ep->base.bind_addr;
+	if (sctp_list_single_entry(&bp->address_list)) {
+		addr = list_entry(bp->address_list.next,
+				  struct sctp_sockaddr_entry, list);
+		if (sctp_is_any(sk, &addr->a))
+			return 1;
+		else
+			return 0;
+	}
+	return 1;
+}
+
 /********************************************************************
  * 3rd Level Abstractions
  ********************************************************************/
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 865ce7b..471facd 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -105,6 +105,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
 			addr->valid = 1;
 			spin_lock_bh(&sctp_local_addr_lock);
 			list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
+			sctp_addr_wq_mgmt(&addr->a, SCTP_NEWADDR);
 			spin_unlock_bh(&sctp_local_addr_lock);
 		}
 		break;
@@ -115,6 +116,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
 			if (addr->a.sa.sa_family == AF_INET6 &&
 					ipv6_addr_equal(&addr->a.v6.sin6_addr,
 						&ifa->addr)) {
+				sctp_addr_wq_mgmt(&addr->a, SCTP_DELADDR);
 				found = 1;
 				addr->valid = 0;
 				list_del_rcu(&addr->list);
@@ -332,8 +334,26 @@ static void sctp_v6_get_saddr(struct sctp_sock *sk,
 				matchlen = bmatchlen;
 			}
 		}
+		if (laddr->state == SCTP_ADDR_NEW && asoc->src_out_of_asoc_ok) {
+			bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
+			if (!baddr || (matchlen < bmatchlen)) {
+				baddr = &laddr->a;
+				matchlen = bmatchlen;
+			}
+		}
+	}
+	if (baddr == NULL) {
+		/* We don't have a valid src addr in "endpoint-wide".
+		 * Looking up in assoc-locally valid address list.
+		 */
+		list_for_each_entry(laddr, &asoc->asoc_laddr_list, list) {
+			bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
+			if (!baddr || (matchlen < bmatchlen)) {
+				baddr = &laddr->a;
+				matchlen = bmatchlen;
+			}
+		}
 	}
-
 	if (baddr) {
 		memcpy(saddr, baddr, sizeof(union sctp_addr));
 		SCTP_DEBUG_PRINTK("saddr: %pI6\n", &saddr->v6.sin6_addr);
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 26dc005..033ea20 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -344,7 +344,14 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk)
 			break;
 		}
 	} else {
-		list_add_tail(&chunk->list, &q->control_chunk_list);
+		/* We add the ASCONF for the only one newly added address at
+		 * the front of the queue
+		 */
+		if (q->asoc->src_out_of_asoc_ok && \
+		    chunk->chunk_hdr->type == SCTP_CID_ASCONF)
+			list_add(&chunk->list, &q->control_chunk_list);
+		else
+			list_add_tail(&chunk->list, &q->control_chunk_list);
 		SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
 	}
 
@@ -850,6 +857,24 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
 		case SCTP_CID_SHUTDOWN:
 		case SCTP_CID_ECN_ECNE:
 		case SCTP_CID_ASCONF:
+			/* RFC 5061, 5.3
+			 * F1) This means that until such time as the ASCONF
+			 * containing the add is acknowledged, the sender MUST
+			 * NOT use the new IP address as a source for ANY SCTP
+			 * packet except on carrying an ASCONF Chunk.
+			 */
+			if (asoc->src_out_of_asoc_ok) {
+				SCTP_DEBUG_PRINTK("outq_flush: out_of_asoc_ok, transmit chunk type %d\n",
+				    chunk->chunk_hdr->type);
+				packet = &transport->packet;
+				sctp_packet_config(packet, vtag,
+						asoc->peer.ecn_capable);
+				sctp_packet_append_chunk(packet, chunk);
+				error = sctp_packet_transmit(packet);
+				if (error < 0)
+					return error;
+				goto sctp_flush_out;
+			}
 		case SCTP_CID_FWD_TSN:
 			status = sctp_packet_transmit_chunk(packet, chunk,
 							    one_packet);
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 152976e..2859c16 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -510,12 +510,20 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
 		sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port));
 		rcu_read_lock();
 		list_for_each_entry_rcu(laddr, &bp->address_list, list) {
-			if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
+			if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC &&
+			    asoc->src_out_of_asoc_ok == 0))
 				continue;
 			if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
 				goto out_unlock;
 		}
 		rcu_read_unlock();
+		/* We don't have a valid src addr in "endpoint-wide".
+		 * Looking up in assoc-locally valid address list.
+		 */
+		list_for_each_entry(laddr, &asoc->asoc_laddr_list, list) {
+			if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
+				goto out_unlock;
+		}
 
 		/* None of the bound addresses match the source address of the
 		 * dst. So release it.
@@ -636,6 +644,182 @@ static void sctp_v4_ecn_capable(struct sock *sk)
 	INET_ECN_xmit(sk);
 }
 
+void sctp_addr_wq_timeout_handler(unsigned long arg)
+{
+	struct sctp_addr_wait *addrw = NULL;
+	union sctp_addr *addr = NULL;
+	struct sctp_ep_common *epb = NULL;
+	struct sctp_endpoint *ep = NULL;
+	int cnt = 0;
+
+	spin_lock_bh(&sctp_addr_wq_lock);
+retry_wq:
+	if (list_empty(&sctp_addr_waitq)) {
+		SCTP_DEBUG_PRINTK("sctp_addrwq_timo_handler: nothing in addr waitq\n");
+		spin_unlock_bh(&sctp_addr_wq_lock);
+		return;
+	}
+	addrw = list_first_entry(&sctp_addr_waitq, struct sctp_addr_wait, list);
+	if (addrw->cmd != SCTP_NEWADDR && addrw->cmd != SCTP_DELADDR) {
+		SCTP_DEBUG_PRINTK("sctp_addrwq_timo_handler: cmd is neither NEWADDR nor DELADDR\n");
+		list_del(&addrw->list);
+		kfree(addrw);
+		goto retry_wq;
+	}
+
+	addr = &addrw->a;
+	SCTP_DEBUG_PRINTK_IPADDR("sctp_addrwq_timo_handler: the first ent in wq %p is ",
+	    " for cmd %d at entry %p\n", &sctp_addr_waitq, addr, addrw->cmd,
+	    addrw);
+
+	/* Now we send an ASCONF for each association */
+	/* Note. we currently don't handle link local IPv6 addressees */
+	if (addr->sa.sa_family == AF_INET6) {
+		struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr;
+
+		if (ipv6_addr_type(&addr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) {
+			SCTP_DEBUG_PRINTK("sctp_timo_handler: link local, hence don't tell eps\n");
+			list_del(&addrw->list);
+			kfree(addrw);
+			goto retry_wq;
+		}
+		if (ipv6_chk_addr(&init_net, in6, NULL, 0) == 0 &&
+		    addrw->cmd == SCTP_NEWADDR) {
+			unsigned long timeo_val;
+
+			SCTP_DEBUG_PRINTK("sctp_timo_handler: this is on DAD, trying %d sec later\n",
+			    SCTP_ADDRESS_TICK_DELAY);
+			timeo_val = jiffies;
+			timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY);
+			(void)mod_timer(&sctp_addr_wq_timer, timeo_val);
+			spin_unlock_bh(&sctp_addr_wq_lock);
+			return;
+		}
+	}
+	list_for_each_entry(epb, &sctp_auto_asconf_eplist, auto_asconf_list) {
+		if (epb == NULL) {
+			SCTP_DEBUG_PRINTK("addrwq_timo_handler: no epb\n");
+			continue;
+		}
+		if (!sctp_is_ep_boundall(epb->sk))
+			/* ignore bound-specific endpoints */
+			continue;
+		ep = sctp_ep(epb);
+		sctp_bh_lock_sock(epb->sk);
+		if (sctp_asconf_mgmt(ep, epb->sk) < 0) {
+			SCTP_DEBUG_PRINTK("sctp_addrwq_timo_handler: sctp_asconf_mgmt failed\n");
+			sctp_bh_unlock_sock(epb->sk);
+			continue;
+		}
+		sctp_bh_unlock_sock(epb->sk);
+		++cnt;
+	}
+
+	list_del(&addrw->list);
+	kfree(addrw);
+
+	if (list_empty(&sctp_addr_waitq)) {
+		spin_unlock_bh(&sctp_addr_wq_lock);
+		return;
+	} else
+		goto retry_wq;
+
+	spin_unlock_bh(&sctp_addr_wq_lock);
+}
+
+void sctp_addr_wq_mgmt(union sctp_addr *reqaddr, int cmd)
+{
+	struct sctp_addr_wait *addrw = NULL;
+	struct sctp_addr_wait *addrw_new = NULL;
+	unsigned long timeo_val;
+	union sctp_addr *tmpaddr;
+
+	/* first, we check if an opposite message already exist in the queue.
+	 * If we found such message, it is removed.
+	 * This operation is a bit stupid, but the DHCP client attaches the
+	 * new address after a couple of addition and deletion of that address
+	 */
+
+	if (reqaddr == NULL) {
+		SCTP_DEBUG_PRINTK("sctp_addr_wq_mgmt: no address message?\n");
+		return;
+	}
+
+	spin_lock_bh(&sctp_addr_wq_lock);
+	/* Offsets existing events in addr_wq */
+	list_for_each_entry(addrw, &sctp_addr_waitq, list) {
+		if (addrw->a.sa.sa_family != reqaddr->sa.sa_family)
+			continue;
+		if (reqaddr->sa.sa_family == AF_INET) {
+			if (reqaddr->v4.sin_addr.s_addr ==
+			    addrw->a.v4.sin_addr.s_addr) {
+				if (cmd != addrw->cmd) {
+					tmpaddr = &addrw->a;
+					SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt offsets existing entry for %d ",
+					    " in waitq %p\n", addrw->cmd,
+					    tmpaddr, &sctp_addr_waitq);
+					list_del(&addrw->list);
+					kfree(addrw);
+					/* nothing to do anymore */
+					spin_unlock_bh(&sctp_addr_wq_lock);
+					return;
+				}
+			}
+		} else if (reqaddr->sa.sa_family == AF_INET6) {
+			if (ipv6_addr_equal(&reqaddr->v6.sin6_addr,
+			    &addrw->a.v6.sin6_addr)) {
+				if (cmd != addrw->cmd) {
+					tmpaddr = &addrw->a;
+					SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt: offsets existing entry for %d ",
+					    " in waitq %p\n", addrw->cmd,
+					    tmpaddr, &sctp_addr_waitq);
+					list_del(&addrw->list);
+					kfree(addrw);
+					spin_unlock_bh(&sctp_addr_wq_lock);
+					return;
+				}
+			}
+		}
+	}
+
+	/* OK, we have to add the new address to the wait queue */
+	addrw_new = kzalloc(sizeof(struct sctp_addr_wait), GFP_ATOMIC);
+	if (addrw_new == NULL) {
+		SCTP_DEBUG_PRINTK("sctp_addr_weitq_mgmt no memory? return\n");
+		spin_unlock_bh(&sctp_addr_wq_lock);
+		return;
+	}
+	if (reqaddr->sa.sa_family == AF_INET) {
+		addrw_new->a.v4.sin_family = AF_INET;
+		addrw_new->a.v4.sin_addr.s_addr = reqaddr->v4.sin_addr.s_addr;
+	} else if (reqaddr->sa.sa_family == AF_INET6) {
+		addrw_new->a.v6.sin6_family = AF_INET6;
+		ipv6_addr_copy(&addrw_new->a.v6.sin6_addr,
+		    &reqaddr->v6.sin6_addr);
+	} else {
+		SCTP_DEBUG_PRINTK("sctp_addr_waitq_mgmt: Unknown family of request addr, return\n");
+		kfree(addrw_new);
+		spin_unlock_bh(&sctp_addr_wq_lock);
+		return;
+	}
+	addrw_new->cmd = cmd;
+	list_add_tail(&addrw_new->list, &sctp_addr_waitq);
+	tmpaddr = &addrw_new->a;
+	SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt add new entry for cmd:%d ",
+	    " in waitq %p, start a timer\n",
+	    addrw_new->cmd, tmpaddr, &sctp_addr_waitq);
+
+	if (timer_pending(&sctp_addr_wq_timer)) {
+		SCTP_DEBUG_PRINTK("sctp_addr_wq_mgmt: addr_wq timer is already running\n");
+		spin_unlock_bh(&sctp_addr_wq_lock);
+		return;
+	}
+	timeo_val = jiffies;
+	timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY);
+	(void)mod_timer(&sctp_addr_wq_timer, timeo_val);
+	spin_unlock_bh(&sctp_addr_wq_lock);
+}
+
 /* Event handler for inet address addition/deletion events.
  * The sctp_local_addr_list needs to be protocted by a spin lock since
  * multiple notifiers (say IPv4 and IPv6) may be running at the same
@@ -663,6 +847,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
 			addr->valid = 1;
 			spin_lock_bh(&sctp_local_addr_lock);
 			list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
+			sctp_addr_wq_mgmt(&addr->a, SCTP_NEWADDR);
 			spin_unlock_bh(&sctp_local_addr_lock);
 		}
 		break;
@@ -673,6 +858,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
 			if (addr->a.sa.sa_family == AF_INET &&
 					addr->a.v4.sin_addr.s_addr ==
 					ifa->ifa_local) {
+				sctp_addr_wq_mgmt(&addr->a, SCTP_DELADDR);
 				found = 1;
 				addr->valid = 0;
 				list_del_rcu(&addr->list);
@@ -1277,6 +1463,12 @@ SCTP_STATIC __init int sctp_init(void)
 
 	/* Initialize the local address list. */
 	INIT_LIST_HEAD(&sctp_local_addr_list);
+	INIT_LIST_HEAD(&sctp_addr_waitq);
+	INIT_LIST_HEAD(&sctp_auto_asconf_eplist);
+	spin_lock_init(&sctp_addr_wq_lock);
+	sctp_addr_wq_timer.expires = 0;
+	setup_timer(&sctp_addr_wq_timer, sctp_addr_wq_timeout_handler,
+	    (unsigned long)NULL);
 	spin_lock_init(&sctp_local_addr_lock);
 	sctp_get_local_addr_list();
 
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index de98665..b5ca24a 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -2651,6 +2651,61 @@ __u32 sctp_generate_tsn(const struct sctp_endpoint *ep)
 	return retval;
 }
 
+void
+sctp_path_check_and_react(struct sctp_association *asoc, struct sockaddr *sa)
+{
+	struct sctp_transport *trans;
+	int addrnum, family;
+	struct sctp_sockaddr_entry *saddr;
+	struct sctp_bind_addr *bp;
+	union sctp_addr *tmpaddr;
+
+	family = sa->sa_family;
+	bp = &asoc->base.bind_addr;
+	addrnum = 0;
+	/* count up the number of local addresses in the same family */
+	list_for_each_entry(saddr, &bp->address_list, list) {
+		if (saddr->a.sa.sa_family == family) {
+			tmpaddr = &saddr->a;
+			if (family == AF_INET6 &&
+			    ipv6_addr_type(&tmpaddr->v6.sin6_addr) &
+			    IPV6_ADDR_LINKLOCAL) {
+				continue;
+			}
+			addrnum++;
+		}
+	}
+	if (addrnum == 1) {
+		union sctp_addr *tmpaddr;
+		tmpaddr = (union sctp_addr *)sa;
+		SCTP_DEBUG_PRINTK_IPADDR("pcheck_react: only 1 local addr in asoc %p ",
+		    " family %d\n", asoc, tmpaddr, family);
+		list_for_each_entry(trans, &asoc->peer.transport_addr_list,
+		    transports) {
+			/* reset path information and release refcount to the
+			 * dst_entry  based on the src change */
+			sctp_transport_hold(trans);
+			trans->cwnd = min(4*asoc->pathmtu,
+			    max_t(__u32, 2*asoc->pathmtu, 4380));
+			trans->ssthresh = asoc->peer.i.a_rwnd;
+			trans->rtt = 0;
+			trans->srtt = 0;
+			trans->rttvar = 0;
+			trans->rto = asoc->rto_initial;
+			dst_release(trans->dst);
+			trans->dst = NULL;
+			memset(&trans->saddr, 0, sizeof(union sctp_addr));
+			sctp_transport_route(trans, NULL,
+			    sctp_sk(asoc->base.sk));
+			SCTP_DEBUG_PRINTK_IPADDR("we freed dst_entry (asoc: %p dst: ",
+			    " trans: %p)\n", asoc, (&trans->ipaddr), trans);
+			trans->rto_pending = 1;
+			sctp_transport_put(trans);
+		}
+	}
+	return;
+}
+
 /*
  * ADDIP 3.1.1 Address Configuration Change Chunk (ASCONF)
  *      0                   1                   2                   3
@@ -2744,11 +2799,29 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
 	int			addr_param_len = 0;
 	int 			totallen = 0;
 	int 			i;
+	sctp_addip_param_t del_param; /* 8 Bytes (Type 0xC002, Len and CrrID) */
+	sctp_addip_param_t spr_param;
+	struct sctp_af *del_af;
+	struct sctp_af *spr_af;
+	int del_addr_param_len = 0;
+	int spr_addr_param_len = 0;
+	int del_paramlen = sizeof(sctp_addip_param_t);
+	int spr_paramlen = sizeof(sctp_addip_param_t);
+	union sctp_addr_param del_addr_param; /* (v4) 8 Bytes, (v6) 20 Bytes */
+	union sctp_addr_param spr_addr_param;
+	int			v4 = 0;
+	int			v6 = 0;
 
 	/* Get total length of all the address parameters. */
 	addr_buf = addrs;
 	for (i = 0; i < addrcnt; i++) {
 		addr = (union sctp_addr *)addr_buf;
+		if (addr != NULL) {
+			if (addr->sa.sa_family == AF_INET)
+				v4 = 1;
+			else if (addr->sa.sa_family == AF_INET6)
+				v6 = 1;
+		}
 		af = sctp_get_af_specific(addr->v4.sin_family);
 		addr_param_len = af->to_addr_param(addr, &addr_param);
 
@@ -2757,6 +2830,40 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
 
 		addr_buf += af->sockaddr_len;
 	}
+	/* Add the length of a pending address being deleted */
+	if (flags == SCTP_PARAM_ADD_IP && asoc->asconf_addr_del_pending) {
+		if ((asoc->asconf_addr_del_pending->sa.sa_family == AF_INET
+		    && v4) ||
+		    (asoc->asconf_addr_del_pending->sa.sa_family == AF_INET6
+		    && v6)) {
+			del_af = sctp_get_af_specific(
+			    asoc->asconf_addr_del_pending->sa.sa_family);
+			del_addr_param_len = del_af->to_addr_param(
+			    asoc->asconf_addr_del_pending, &del_addr_param);
+			totallen += del_paramlen;
+			totallen += del_addr_param_len;
+			SCTP_DEBUG_PRINTK("mkasconf_update_ip: now we picked del_pending addr, totallen for all addresses is %d\n",
+			    totallen);
+			/* for Set Primary (equal size as del parameters */
+			totallen += del_paramlen;
+			totallen += del_addr_param_len;
+		}
+		if (v4) {
+			if (totallen != SCTP_ASCONF_V4_PARAM_LEN * 2 &&
+			    totallen != SCTP_ASCONF_V4_PARAM_LEN * 3) {
+				SCTP_DEBUG_PRINTK("mkasconf_update_ip: incorrect total length of ASCONF parameters, del + add MUST be 32 bytes, but %d bytes\n", totallen);
+			return NULL;
+			}
+		} else if (v6) {
+			if (totallen != SCTP_ASCONF_V6_PARAM_LEN * 2 &&
+			    totallen != SCTP_ASCONF_V6_PARAM_LEN * 3) {
+				SCTP_DEBUG_PRINTK("mkasconf_update_ip: incorrect total length of ASCONF parameters, del + add MUST be 56 bytes, but %d bytes\n", totallen);
+			return NULL;
+			}
+		}
+	}
+	SCTP_DEBUG_PRINTK("mkasconf_update_ip: call mkasconf() for %d bytes\n",
+	    totallen);
 
 	/* Create an asconf chunk with the required length. */
 	retval = sctp_make_asconf(asoc, laddr, totallen);
@@ -2778,6 +2885,32 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
 
 		addr_buf += af->sockaddr_len;
 	}
+	if (flags == SCTP_PARAM_ADD_IP && asoc->asconf_addr_del_pending) {
+		addr = asoc->asconf_addr_del_pending;
+		del_af = sctp_get_af_specific(addr->v4.sin_family);
+		del_addr_param_len = del_af->to_addr_param(addr,
+		    &del_addr_param);
+		del_param.param_hdr.type = SCTP_PARAM_DEL_IP;
+		del_param.param_hdr.length = htons(del_paramlen +
+		    del_addr_param_len);
+		del_param.crr_id = i;
+		asoc->asconf_del_pending_cid = i;
+
+		sctp_addto_chunk(retval, del_paramlen, &del_param);
+		sctp_addto_chunk(retval, del_addr_param_len, &del_addr_param);
+		/* For SET_PRIMARY */
+		addr_buf = addrs;
+		addr = (union sctp_addr *)addr_buf;
+		spr_af = sctp_get_af_specific(addr->v4.sin_family);
+		spr_addr_param_len = spr_af->to_addr_param(addr,
+		    &spr_addr_param);
+		spr_param.param_hdr.type = SCTP_PARAM_SET_PRIMARY;
+		spr_param.param_hdr.length = htons(spr_paramlen +
+		    spr_addr_param_len);
+		spr_param.crr_id = (i+1);
+		sctp_addto_chunk(retval, spr_paramlen, &spr_param);
+		sctp_addto_chunk(retval, spr_addr_param_len, &spr_addr_param);
+	}
 	return retval;
 }
 
@@ -2990,7 +3123,7 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
 		 * an Error Cause TLV set to the new error code 'Request to
 		 * Delete Source IP Address'
 		 */
-		if (sctp_cmp_addr_exact(sctp_source(asconf), &addr))
+		if (sctp_cmp_addr_exact(&asconf->source, &addr))
 			return SCTP_ERROR_DEL_SRC_IP;
 
 		/* Section 4.2.2
@@ -3171,7 +3304,6 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
 	struct sctp_bind_addr *bp = &asoc->base.bind_addr;
 	union sctp_addr_param *addr_param;
 	struct sctp_transport *transport;
-	struct sctp_sockaddr_entry *saddr;
 
 	addr_param = (union sctp_addr_param *)
 			((void *)asconf_param + sizeof(sctp_addip_param_t));
@@ -3186,9 +3318,16 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
 		 * held, so the list can not change.
 		 */
 		local_bh_disable();
-		list_for_each_entry(saddr, &bp->address_list, list) {
-			if (sctp_cmp_addr_exact(&saddr->a, &addr))
-				saddr->state = SCTP_ADDR_SRC;
+		/* Until this ASCONF is acked on all associations, we cannot
+		 * consider this address as ADDR_SRC
+		 */
+		asoc->src_out_of_asoc_ok = 0;
+		sctp_add_addr_to_laddr(&addr.sa, asoc);
+		list_for_each_entry(transport, &asoc->peer.transport_addr_list,
+				transports) {
+			dst_release(transport->dst);
+			sctp_transport_route(transport, NULL,
+					     sctp_sk(asoc->base.sk));
 		}
 		local_bh_enable();
 		list_for_each_entry(transport, &asoc->peer.transport_addr_list,
@@ -3203,6 +3342,25 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
 	case SCTP_PARAM_DEL_IP:
 		local_bh_disable();
 		sctp_del_bind_addr(bp, &addr);
+		if (asoc->asconf_addr_del_pending != NULL) {
+			if ((addr.sa.sa_family == AF_INET) &&
+			    (asoc->asconf_addr_del_pending->sa.sa_family ==
+			     AF_INET)) {
+				if (asoc->asconf_addr_del_pending->v4.sin_addr.s_addr == addr.v4.sin_addr.s_addr) {
+					kfree(asoc->asconf_addr_del_pending);
+					asoc->asconf_del_pending_cid = 0;
+					asoc->asconf_addr_del_pending = NULL;
+				}
+			} else if ((addr.sa.sa_family == AF_INET6) &&
+				(asoc->asconf_addr_del_pending->sa.sa_family ==
+				 AF_INET6)) {
+				if (ipv6_addr_equal(&asoc->asconf_addr_del_pending->v6.sin6_addr, &addr.v6.sin6_addr)) {
+					kfree(asoc->asconf_addr_del_pending);
+					asoc->asconf_del_pending_cid = 0;
+					asoc->asconf_addr_del_pending = NULL;
+				}
+			}
+		}
 		local_bh_enable();
 		list_for_each_entry(transport, &asoc->peer.transport_addr_list,
 				transports) {
@@ -3293,6 +3451,8 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
 	int	no_err = 1;
 	int	retval = 0;
 	__be16	err_code = SCTP_ERROR_NO_ERROR;
+	sctp_addip_param_t *first_asconf_param = NULL;
+	int first_asconf_paramlen;
 
 	/* Skip the chunkhdr and addiphdr from the last asconf sent and store
 	 * a pointer to address parameter.
@@ -3307,6 +3467,8 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
 	length = ntohs(addr_param->v4.param_hdr.length);
 	asconf_param = (sctp_addip_param_t *)((void *)addr_param + length);
 	asconf_len -= length;
+	first_asconf_param = asconf_param;
+	first_asconf_paramlen = ntohs(first_asconf_param->param_hdr.length);
 
 	/* ADDIP 4.1
 	 * A8) If there is no response(s) to specific TLV parameter(s), and no
@@ -3361,6 +3523,35 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
 		asconf_len -= length;
 	}
 
+	/* When the source address obviously changes to newly added one, we
+	   reset the cwnd to re-probe the path condition
+	*/
+	if (no_err && first_asconf_param->param_hdr.type == SCTP_PARAM_ADD_IP) {
+		if (first_asconf_paramlen == SCTP_ASCONF_V4_PARAM_LEN) {
+			struct sockaddr_in sin;
+
+			memset(&sin, 0, sizeof(struct sockaddr_in));
+			sin.sin_family = AF_INET;
+			memcpy(&sin.sin_addr.s_addr, first_asconf_param + 1,
+					sizeof(struct in_addr));
+			sctp_path_check_and_react(asoc,
+					(struct sockaddr *)&sin);
+
+		} else if (first_asconf_paramlen == SCTP_ASCONF_V6_PARAM_LEN) {
+			struct sockaddr_in6 sin6;
+
+			memset(&sin6, 0, sizeof(struct sockaddr_in6));
+			sin6.sin6_family = AF_INET6;
+			memcpy(&sin6.sin6_addr, first_asconf_param + 1,
+					sizeof(struct in6_addr));
+			sctp_path_check_and_react(asoc,
+					(struct sockaddr *)&sin6);
+		} else {
+			SCTP_DEBUG_PRINTK("funny asconf_paramlen? (%d)\n",
+			    first_asconf_paramlen);
+		}
+	}
+
 	/* Free the cached last sent asconf chunk. */
 	list_del_init(&asconf->transmitted_list);
 	sctp_chunk_free(asconf);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 3951a10..9bc5e98 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -527,6 +527,7 @@ static int sctp_send_asconf_add_ip(struct sock		*sk,
 	struct list_head		*p;
 	int 				i;
 	int 				retval = 0;
+	struct sctp_transport		*trans = NULL;
 
 	if (!sctp_addip_enable)
 		return retval;
@@ -583,13 +584,10 @@ static int sctp_send_asconf_add_ip(struct sock		*sk,
 			goto out;
 		}
 
-		retval = sctp_send_asconf(asoc, chunk);
-		if (retval)
-			goto out;
-
 		/* Add the new addresses to the bind address list with
 		 * use_as_src set to 0.
 		 */
+		SCTP_DEBUG_PRINTK("snd_asconf_addip: next, add_bind_addr with ADDR_NEW flag\n");
 		addr_buf = addrs;
 		for (i = 0; i < addrcnt; i++) {
 			addr = (union sctp_addr *)addr_buf;
@@ -599,6 +597,28 @@ static int sctp_send_asconf_add_ip(struct sock		*sk,
 						    SCTP_ADDR_NEW, GFP_ATOMIC);
 			addr_buf += af->sockaddr_len;
 		}
+		list_for_each_entry(trans, &asoc->peer.transport_addr_list,
+		    transports) {
+			if (asoc->asconf_addr_del_pending != NULL)
+				/* This ADDIP ASCONF piggybacks DELIP for the
+				 * last address, so need to select src addr
+				 * from the out_of_asoc addrs
+				 */
+				asoc->src_out_of_asoc_ok = 1;
+			/* Clear the source and route cache in the path */
+			memset(&trans->saddr, 0, sizeof(union sctp_addr));
+			dst_release(trans->dst);
+			trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
+			    2*asoc->pathmtu, 4380));
+			trans->ssthresh = asoc->peer.i.a_rwnd;
+			trans->rto = asoc->rto_initial;
+			trans->rtt = 0;
+			trans->srtt = 0;
+			trans->rttvar = 0;
+			sctp_transport_route(trans, NULL,
+			    sctp_sk(asoc->base.sk));
+		}
+		retval = sctp_send_asconf(asoc, chunk);
 	}
 
 out:
@@ -711,7 +731,9 @@ static int sctp_send_asconf_del_ip(struct sock		*sk,
 	struct sctp_sockaddr_entry *saddr;
 	int 			i;
 	int 			retval = 0;
+	int			stored = 0;
 
+	chunk = NULL;
 	if (!sctp_addip_enable)
 		return retval;
 
@@ -762,8 +784,42 @@ static int sctp_send_asconf_del_ip(struct sock		*sk,
 		bp = &asoc->base.bind_addr;
 		laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
 					       addrcnt, sp);
-		if (!laddr)
-			continue;
+		if ((laddr == NULL) && (addrcnt == 1)) {
+			union sctp_addr *sa_addr = NULL;
+
+			if (asoc->asconf_addr_del_pending == NULL) {
+				asoc->asconf_addr_del_pending =
+				    kmalloc(sizeof(union sctp_addr),
+				    GFP_ATOMIC);
+				memset(asoc->asconf_addr_del_pending, 0,
+						sizeof(union sctp_addr));
+				if (addrs->sa_family == AF_INET) {
+					struct sockaddr_in *sin;
+
+					sin = (struct sockaddr_in *)addrs;
+					asoc->asconf_addr_del_pending->v4.sin_family = AF_INET;
+					memcpy(&asoc->asconf_addr_del_pending->v4.sin_addr, &sin->sin_addr, sizeof(struct in_addr));
+				} else if (addrs->sa_family == AF_INET6) {
+					struct sockaddr_in6 *sin6;
+
+					sin6 = (struct sockaddr_in6 *)addrs;
+					asoc->asconf_addr_del_pending->v6.sin6_family = AF_INET6;
+					memcpy(&asoc->asconf_addr_del_pending->v6.sin6_addr, &sin6->sin6_addr, sizeof(struct in6_addr));
+				}
+				sa_addr = (union sctp_addr *)addrs;
+				SCTP_DEBUG_PRINTK_IPADDR("send_asconf_del_ip: keep the last address asoc: %p ",
+				    " at %p\n", asoc, sa_addr,
+				    asoc->asconf_addr_del_pending);
+				stored = 1;
+				goto skip_mkasconf;
+			} else {
+				SCTP_DEBUG_PRINTK_IPADDR("send_asconf_del_ip: asoc %p, deleting last address ",
+				    " is already stored at %p\n", asoc,
+				    asoc->asconf_addr_del_pending,
+				    asoc->asconf_addr_del_pending);
+				continue;
+			}
+		}
 
 		/* We do not need RCU protection throughout this loop
 		 * because this is done under a socket lock from the
@@ -776,6 +832,7 @@ static int sctp_send_asconf_del_ip(struct sock		*sk,
 			goto out;
 		}
 
+skip_mkasconf:
 		/* Reset use_as_src flag for the addresses in the bind address
 		 * list that are to be deleted.
 		 */
@@ -797,16 +854,205 @@ static int sctp_send_asconf_del_ip(struct sock		*sk,
 		list_for_each_entry(transport, &asoc->peer.transport_addr_list,
 					transports) {
 			dst_release(transport->dst);
+			/* Clear source address cache */
+			memset(&transport->saddr, 0, sizeof(union sctp_addr));
 			sctp_transport_route(transport, NULL,
 					     sctp_sk(asoc->base.sk));
 		}
 
+		if (stored) {
+			/* We don't need to transmit ASCONF */
+			continue;
+		}
 		retval = sctp_send_asconf(asoc, chunk);
 	}
 out:
 	return retval;
 }
 
+/* Add a new address to the list contains available addresses only in the
+ * association.  If the new address is also available on the other associations
+ * on the endpoint, it is marked as SCTP_ADDR_SRC in the bind address list on
+ * the endpoint.  This situation is possible when some of associations receive
+ * ASCONF-ACK for ADD_IP at the endpoint
+ */
+void
+sctp_add_addr_to_laddr(struct sockaddr *sa, struct sctp_association *asoc)
+{
+	struct sctp_endpoint *ep = asoc->ep;
+	struct sctp_association *tmp = NULL;
+	struct sctp_bind_addr *bp;
+	struct sctp_sockaddr_entry *addr;
+	struct sockaddr_in *sin = NULL;
+	struct sockaddr_in6 *sin6 = NULL;
+	int local;
+	int found;
+
+	union sctp_addr *tmpaddr = NULL;
+	tmpaddr = (union sctp_addr *)sa;
+	SCTP_DEBUG_PRINTK_IPADDR("add_addr_to_laddr: asoc: %p ", " ep: %p",
+	    asoc, tmpaddr, ep);
+	if (sa->sa_family == AF_INET)
+		sin = (struct sockaddr_in *)sa;
+	else if (sa->sa_family == AF_INET6)
+		sin6 = (struct sockaddr_in6 *)sa;
+
+	/* Check if this address is locally available in the other asocs */
+	local = 0;
+	list_for_each_entry(tmp, &ep->asocs, asocs) {
+		if (tmp == asoc)
+			continue;
+		found = 0;
+		list_for_each_entry(addr, &tmp->asoc_laddr_list, list) {
+			tmpaddr = &addr->a;
+			if (sa->sa_family != addr->a.sa.sa_family)
+				continue;
+			if (sa->sa_family == AF_INET) {
+				if (sin->sin_addr.s_addr ==
+				    addr->a.v4.sin_addr.s_addr)
+					found = 1;
+			} else if (sa->sa_family == AF_INET6) {
+				if (ipv6_addr_equal(&sin6->sin6_addr,
+				    &addr->a.v6.sin6_addr))
+					found = 1;
+
+			}
+		}
+		if (!found) {
+			SCTP_DEBUG_PRINTK("add_addr_to_laddr: not found in asoc %p\n", tmp);
+			local = 1;
+			break;
+		}
+	}
+	addr = NULL;
+
+	if (local) {
+		/* this address is not available in some of the other
+		 * associations.  So add as locally-available in this
+		 * asocciation
+		 */
+		addr = kmalloc(sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC);
+		if  (addr == NULL) {
+			SCTP_DEBUG_PRINTK("add_addr_to_laddr: failed to allocate memory for this address\n");
+			return;
+		}
+		memset(addr, 0, sizeof(struct sctp_sockaddr_entry));
+		if (sa->sa_family == AF_INET) {
+			addr->a.sa.sa_family = AF_INET;
+			addr->a.v4.sin_port = sin->sin_port;
+			addr->a.v4.sin_addr.s_addr = sin->sin_addr.s_addr;
+		} else if (sa->sa_family == AF_INET6) {
+			addr->a.sa.sa_family = AF_INET6;
+			addr->a.v6.sin6_port = sin6->sin6_port;
+			memcpy(&addr->a.v6.sin6_addr, &sin6->sin6_addr,
+			    sizeof(struct in6_addr));
+		}
+		list_add_tail(&addr->list, &asoc->asoc_laddr_list);
+		SCTP_DEBUG_PRINTK("add_addr_to_laddr: now we added this address to the local list on asoc %p\n", asoc);
+	} else {
+		/* this address is also available in all other asocs.  So set
+		 * it as ADDR_SRC in the bind-addr list in the endpoint, then
+		 * remove from the asoc_laddr_list on the associations.
+		 */
+		SCTP_DEBUG_PRINTK("add_addr_to_laddr: this address is available in all other asocs\n");
+		bp = &asoc->base.bind_addr;
+
+		/* change state of the new address in the bind list */
+		list_for_each_entry(addr, &bp->address_list, list) {
+			if (addr->state != SCTP_ADDR_NEW)
+				continue;
+			if (addr->a.sa.sa_family != sa->sa_family)
+				continue;
+			if (addr->a.sa.sa_family == AF_INET) {
+				if (sin->sin_port != addr->a.v4.sin_port)
+					continue;
+				if (sin->sin_addr.s_addr !=
+				    addr->a.v4.sin_addr.s_addr)
+					continue;
+			} else if (addr->a.sa.sa_family == AF_INET6) {
+				if (sin6->sin6_port != addr->a.v6.sin6_port)
+					continue;
+				if (!ipv6_addr_equal(&sin6->sin6_addr,
+				    &addr->a.v6.sin6_addr))
+					continue;
+			}
+			SCTP_DEBUG_PRINTK("add_addr_to_laddr: found the entry for this address with ADDR_NEW flag, set to ADDR_SRC\n");
+			addr->state = SCTP_ADDR_SRC;
+		}
+
+		/* remove the entry of this address from the asoc-local list */
+		list_for_each_entry(tmp, &ep->asocs, asocs) {
+			if (tmp == asoc)
+				continue;
+			addr = NULL;
+			list_for_each_entry(addr, &tmp->asoc_laddr_list, list) {
+				if (sa->sa_family != addr->a.sa.sa_family)
+					continue;
+				if (sa->sa_family == AF_INET) {
+					if (sin->sin_addr.s_addr !=
+					    addr->a.v4.sin_addr.s_addr)
+						continue;
+				} else if (sa->sa_family == AF_INET6) {
+					if (!ipv6_addr_equal(&sin6->sin6_addr,
+					    &addr->a.v6.sin6_addr))
+						continue;
+				}
+				break;
+			}
+			if (addr == NULL) {
+				SCTP_DEBUG_PRINTK("add_addr_to_laddr: Huh, asoc %p doesn't have the entry for this address?\n", asoc);
+				continue;
+			}
+			list_del(&addr->list);
+			kfree(addr);
+		}
+	}
+}
+
+/* set addr events to assocs in the endpoint.  ep and addr_wq must be locked */
+int
+sctp_asconf_mgmt(struct sctp_endpoint *ep, struct sock *sk)
+{
+	struct sctp_addr_wait *addrw = NULL;
+	union sctp_addr *addr = NULL;
+	int cmd;
+	int error = 0;
+
+	if (ep == NULL || sk == NULL)
+		return -EINVAL;
+	if (list_empty(&sctp_addr_waitq)) {
+		SCTP_DEBUG_PRINTK("asconf_mgmt: nothing in the wq\n");
+		return -EINVAL;
+	}
+	addrw = list_first_entry(&sctp_addr_waitq, struct sctp_addr_wait, list);
+	if (addrw->cmd != SCTP_NEWADDR && addrw->cmd != SCTP_DELADDR)
+		return -EINVAL;
+	addr = &addrw->a;
+	cmd = addrw->cmd;
+
+	if (addr->sa.sa_family == AF_INET)
+		addr->v4.sin_port = htons(ep->base.bind_addr.port);
+	else if (addr->sa.sa_family == AF_INET6)
+		addr->v6.sin6_port = htons(ep->base.bind_addr.port);
+
+	SCTP_DEBUG_PRINTK("sctp_asconf_mgmt sk:%p ep:%p\n", sk, ep);
+	if (cmd == SCTP_NEWADDR) {
+		error = sctp_send_asconf_add_ip(sk, (struct sockaddr *)addr, 1);
+		if (error) {
+			SCTP_DEBUG_PRINTK("asconf_mgmt: send_asconf_add_ip returns %d\n", error);
+			return error;
+		}
+	} else if (cmd == SCTP_DELADDR) {
+		error = sctp_send_asconf_del_ip(sk, (struct sockaddr *)addr, 1);
+		if (error) {
+			SCTP_DEBUG_PRINTK("asconf_mgmt: send_asconf_del_ip returns %d\n", error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
 /* Helper for tunneling sctp_bindx() requests through sctp_setsockopt()
  *
  * API 8.1
@@ -3341,6 +3587,44 @@ static int sctp_setsockopt_del_key(struct sock *sk,
 
 }
 
+/*
+ * 8.1.23 SCTP_AUTO_ASCONF
+ *
+ * This option will enable or disable the use of the automatic generation of
+ * ASCONF chunks to add and delete addresses to an existing association.  Note
+ * that this option has two caveats namely: a) it only affects sockets that
+ * are bound to all addresses available to the SCTP stack, and b) the system
+ * administrator may have an overriding control that turns the ASCONF feature
+ * off no matter what setting the socket option may have.
+ * This option expects an integer boolean flag, where a non-zero value turns on
+ * the option, and a zero value turns off the option.
+ * Note. In this implementation, socket operation overrides default parameter
+ * being set by sysctl as well as FreeBSD implementation
+ */
+static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval,
+					unsigned int optlen)
+{
+	int val;
+	struct sctp_ep_common *epb;
+
+	if (optlen < sizeof(int))
+		return -EINVAL;
+	if (get_user(val, (int __user *)optval))
+		return -EFAULT;
+	if (!sctp_is_ep_boundall(sk) && val)
+		return -EINVAL;
+	list_for_each_entry(epb, &sctp_auto_asconf_eplist, auto_asconf_list) {
+		if (epb->sk == sk) {
+			if (val == 0)
+				list_del(&epb->auto_asconf_list);
+			return 0;
+		}
+	}
+	if (val)
+		list_add_tail(&sctp_sk(sk)->ep->base.auto_asconf_list,
+		    &sctp_auto_asconf_eplist);
+	return 0;
+}
 
 /* API 6.2 setsockopt(), getsockopt()
  *
@@ -3488,6 +3772,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
 	case SCTP_AUTH_DELETE_KEY:
 		retval = sctp_setsockopt_del_key(sk, optval, optlen);
 		break;
+	case SCTP_AUTO_ASCONF:
+		retval = sctp_setsockopt_auto_asconf(sk, optval, optlen);
+		break;
 	default:
 		retval = -ENOPROTOOPT;
 		break;
@@ -3770,6 +4057,10 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
 	local_bh_disable();
 	percpu_counter_inc(&sctp_sockets_allocated);
 	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+	if (sctp_auto_asconf_enable)
+		list_add_tail(&ep->base.auto_asconf_list,
+		    &sctp_auto_asconf_eplist);
+	SCTP_DEBUG_PRINTK("sctp_init_sk sk:%p ep:%p\n", sk, ep);
 	local_bh_enable();
 
 	return 0;
@@ -3779,11 +4070,18 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
 SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
 {
 	struct sctp_endpoint *ep;
+	struct sctp_ep_common *epb;
 
 	SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk);
 
 	/* Release our hold on the endpoint. */
 	ep = sctp_sk(sk)->ep;
+	list_for_each_entry(epb, &sctp_auto_asconf_eplist, auto_asconf_list) {
+		if (epb->sk == sk) {
+			list_del(&epb->auto_asconf_list);
+			break;
+		}
+	}
 	sctp_endpoint_free(ep);
 	local_bh_disable();
 	percpu_counter_dec(&sctp_sockets_allocated);
@@ -5283,6 +5581,31 @@ static int sctp_getsockopt_assoc_number(struct sock *sk, int len,
 	return 0;
 }
 
+/*
+ * 8.1.23 SCTP_AUTO_ASCONF
+ * See the corresponding setsockopt entry as description
+ */
+static int sctp_getsockopt_auto_asconf(struct sock *sk, int len,
+				   char __user *optval, int __user *optlen)
+{
+	int val = 0;
+	struct sctp_ep_common *epb;
+
+	if (len < sizeof(int))
+		return -EINVAL;
+
+	len = sizeof(int);
+	list_for_each_entry(epb, &sctp_auto_asconf_eplist, auto_asconf_list) {
+		if (epb->sk == sk)
+			val = 1;
+	}
+	if (put_user(len, optlen))
+		return -EFAULT;
+	if (copy_to_user(optval, &val, len))
+		return -EFAULT;
+	return 0;
+}
+
 SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
 				char __user *optval, int __user *optlen)
 {
@@ -5415,6 +5738,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
 	case SCTP_GET_ASSOC_NUMBER:
 		retval = sctp_getsockopt_assoc_number(sk, len, optval, optlen);
 		break;
+	case SCTP_AUTO_ASCONF:
+		retval = sctp_getsockopt_auto_asconf(sk, len, optval, optlen);
+		break;
 	default:
 		retval = -ENOPROTOOPT;
 		break;
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 50cb57f..df39789 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -183,6 +183,13 @@ static ctl_table sctp_table[] = {
 		.proc_handler	= proc_dointvec,
 	},
 	{
+		.procname	= "auto_asconf_enable",
+		.data		= &sctp_auto_asconf_enable,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec,
+	},
+	{
 		.procname	= "prsctp_enable",
 		.data		= &sctp_prsctp_enable,
 		.maxlen		= sizeof(int),


^ permalink raw reply related

* connector: convert to synchronous netlink message processing
From: Patrick McHardy @ 2011-03-28 18:39 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: Linux Netdev List

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

This patch fixes up connector to work properly with the recent
netlink changes that require synchronous netlink message processing.

The patch is so far only compile tested since I'm still looking
for a good way to test it. If you could point me to some software
using the proc events that would be appreciated.



[-- Attachment #2: cn.diff --]
[-- Type: text/x-patch, Size: 7766 bytes --]

commit 21c40e4675954b42c939334e3ed15e1bd0a4da29
Author: Patrick McHardy <kaber@trash.net>
Date:   Mon Mar 28 20:20:26 2011 +0200

    connector: convert to synchronous netlink message processing
    
    Commits 01a16b21 (netlink: kill eff_cap from struct netlink_skb_parms)
    and c53fa1ed (netlink: kill loginuid/sessionid/sid members from struct
    netlink_skb_parms) removed some members from struct netlink_skb_parms
    that depend on the current context, all netlink users are now required
    to do synchronous message processing.
    
    connector however queues received messages and processes them in a work
    queue, which is not valid anymore. This patch converts connector to do
    synchronous message processing by invoking the registered callback handler
    directly from the netlink receive function.
    
    In order to avoid invoking the callback with connector locks held, a
    reference count is added to struct cn_callback_entry, the reference
    is taken when finding a matching callback entry on the device's queue_list
    and released after the callback handler has been invoked.
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/drivers/connector/cn_queue.c b/drivers/connector/cn_queue.c
index 55653ab..c42c9d5 100644
--- a/drivers/connector/cn_queue.c
+++ b/drivers/connector/cn_queue.c
@@ -31,24 +31,9 @@
 #include <linux/connector.h>
 #include <linux/delay.h>
 
-void cn_queue_wrapper(struct work_struct *work)
-{
-	struct cn_callback_entry *cbq =
-		container_of(work, struct cn_callback_entry, work);
-	struct cn_callback_data *d = &cbq->data;
-	struct cn_msg *msg = NLMSG_DATA(nlmsg_hdr(d->skb));
-	struct netlink_skb_parms *nsp = &NETLINK_CB(d->skb);
-
-	d->callback(msg, nsp);
-
-	kfree_skb(d->skb);
-	d->skb = NULL;
-
-	kfree(d->free);
-}
-
 static struct cn_callback_entry *
-cn_queue_alloc_callback_entry(const char *name, struct cb_id *id,
+cn_queue_alloc_callback_entry(struct cn_queue_dev *dev, const char *name,
+			      struct cb_id *id,
 			      void (*callback)(struct cn_msg *, struct netlink_skb_parms *))
 {
 	struct cn_callback_entry *cbq;
@@ -59,17 +44,23 @@ cn_queue_alloc_callback_entry(const char *name, struct cb_id *id,
 		return NULL;
 	}
 
+	atomic_set(&cbq->refcnt, 1);
+
+	atomic_inc(&dev->refcnt);
+	cbq->pdev = dev;
+
 	snprintf(cbq->id.name, sizeof(cbq->id.name), "%s", name);
 	memcpy(&cbq->id.id, id, sizeof(struct cb_id));
-	cbq->data.callback = callback;
-
-	INIT_WORK(&cbq->work, &cn_queue_wrapper);
+	cbq->callback = callback;
 	return cbq;
 }
 
-static void cn_queue_free_callback(struct cn_callback_entry *cbq)
+void cn_queue_release_callback(struct cn_callback_entry *cbq)
 {
-	flush_workqueue(cbq->pdev->cn_queue);
+	if (!atomic_dec_and_test(&cbq->refcnt))
+		return;
+
+	atomic_dec(&cbq->pdev->refcnt);
 	kfree(cbq);
 }
 
@@ -85,13 +76,10 @@ int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
 	struct cn_callback_entry *cbq, *__cbq;
 	int found = 0;
 
-	cbq = cn_queue_alloc_callback_entry(name, id, callback);
+	cbq = cn_queue_alloc_callback_entry(dev, name, id, callback);
 	if (!cbq)
 		return -ENOMEM;
 
-	atomic_inc(&dev->refcnt);
-	cbq->pdev = dev;
-
 	spin_lock_bh(&dev->queue_lock);
 	list_for_each_entry(__cbq, &dev->queue_list, callback_entry) {
 		if (cn_cb_equal(&__cbq->id.id, id)) {
@@ -104,8 +92,7 @@ int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
 	spin_unlock_bh(&dev->queue_lock);
 
 	if (found) {
-		cn_queue_free_callback(cbq);
-		atomic_dec(&dev->refcnt);
+		cn_queue_release_callback(cbq);
 		return -EINVAL;
 	}
 
@@ -130,10 +117,8 @@ void cn_queue_del_callback(struct cn_queue_dev *dev, struct cb_id *id)
 	}
 	spin_unlock_bh(&dev->queue_lock);
 
-	if (found) {
-		cn_queue_free_callback(cbq);
-		atomic_dec(&dev->refcnt);
-	}
+	if (found)
+		cn_queue_release_callback(cbq);
 }
 
 struct cn_queue_dev *cn_queue_alloc_dev(const char *name, struct sock *nls)
@@ -151,12 +136,6 @@ struct cn_queue_dev *cn_queue_alloc_dev(const char *name, struct sock *nls)
 
 	dev->nls = nls;
 
-	dev->cn_queue = alloc_ordered_workqueue(dev->name, 0);
-	if (!dev->cn_queue) {
-		kfree(dev);
-		return NULL;
-	}
-
 	return dev;
 }
 
@@ -164,9 +143,6 @@ void cn_queue_free_dev(struct cn_queue_dev *dev)
 {
 	struct cn_callback_entry *cbq, *n;
 
-	flush_workqueue(dev->cn_queue);
-	destroy_workqueue(dev->cn_queue);
-
 	spin_lock_bh(&dev->queue_lock);
 	list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry)
 		list_del(&cbq->callback_entry);
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index f7554de..d770058 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -122,51 +122,28 @@ EXPORT_SYMBOL_GPL(cn_netlink_send);
  */
 static int cn_call_callback(struct sk_buff *skb)
 {
-	struct cn_callback_entry *__cbq, *__new_cbq;
+	struct cn_callback_entry *i, *cbq = NULL;
 	struct cn_dev *dev = &cdev;
 	struct cn_msg *msg = NLMSG_DATA(nlmsg_hdr(skb));
+	struct netlink_skb_parms *nsp = &NETLINK_CB(skb);
 	int err = -ENODEV;
 
 	spin_lock_bh(&dev->cbdev->queue_lock);
-	list_for_each_entry(__cbq, &dev->cbdev->queue_list, callback_entry) {
-		if (cn_cb_equal(&__cbq->id.id, &msg->id)) {
-			if (likely(!work_pending(&__cbq->work) &&
-					__cbq->data.skb == NULL)) {
-				__cbq->data.skb = skb;
-
-				if (queue_work(dev->cbdev->cn_queue,
-					       &__cbq->work))
-					err = 0;
-				else
-					err = -EINVAL;
-			} else {
-				struct cn_callback_data *d;
-
-				err = -ENOMEM;
-				__new_cbq = kzalloc(sizeof(struct cn_callback_entry), GFP_ATOMIC);
-				if (__new_cbq) {
-					d = &__new_cbq->data;
-					d->skb = skb;
-					d->callback = __cbq->data.callback;
-					d->free = __new_cbq;
-
-					INIT_WORK(&__new_cbq->work,
-							&cn_queue_wrapper);
-
-					if (queue_work(dev->cbdev->cn_queue,
-						       &__new_cbq->work))
-						err = 0;
-					else {
-						kfree(__new_cbq);
-						err = -EINVAL;
-					}
-				}
-			}
+	list_for_each_entry(i, &dev->cbdev->queue_list, callback_entry) {
+		if (cn_cb_equal(&i->id.id, &msg->id)) {
+			atomic_inc(&i->refcnt);
+			cbq = i;
 			break;
 		}
 	}
 	spin_unlock_bh(&dev->cbdev->queue_lock);
 
+	if (cbq != NULL) {
+		cbq->callback(msg, nsp);
+		kfree_skb(skb);
+		cn_queue_release_callback(cbq);
+	}
+
 	return err;
 }
 
diff --git a/include/linux/connector.h b/include/linux/connector.h
index bcafc94..7c60d09 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -88,8 +88,6 @@ struct cn_queue_dev {
 	atomic_t refcnt;
 	unsigned char name[CN_CBQ_NAMELEN];
 
-	struct workqueue_struct *cn_queue;
-
 	struct list_head queue_list;
 	spinlock_t queue_lock;
 
@@ -101,20 +99,13 @@ struct cn_callback_id {
 	struct cb_id id;
 };
 
-struct cn_callback_data {
-	struct sk_buff *skb;
-	void (*callback) (struct cn_msg *, struct netlink_skb_parms *);
-
-	void *free;
-};
-
 struct cn_callback_entry {
 	struct list_head callback_entry;
-	struct work_struct work;
+	atomic_t refcnt;
 	struct cn_queue_dev *pdev;
 
 	struct cn_callback_id id;
-	struct cn_callback_data data;
+	void (*callback) (struct cn_msg *, struct netlink_skb_parms *);
 
 	u32 seq, group;
 };
@@ -138,13 +129,12 @@ int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
 			  struct cb_id *id,
 			  void (*callback)(struct cn_msg *, struct netlink_skb_parms *));
 void cn_queue_del_callback(struct cn_queue_dev *dev, struct cb_id *id);
+void cn_queue_release_callback(struct cn_callback_entry *);
 
 struct cn_queue_dev *cn_queue_alloc_dev(const char *name, struct sock *);
 void cn_queue_free_dev(struct cn_queue_dev *dev);
 
 int cn_cb_equal(struct cb_id *, struct cb_id *);
 
-void cn_queue_wrapper(struct work_struct *work);
-
 #endif				/* __KERNEL__ */
 #endif				/* __CONNECTOR_H */


^ permalink raw reply related

* RE: zero copy for relay server
From: Eric Dumazet @ 2011-03-28 18:34 UTC (permalink / raw)
  To: Viral Mehta; +Cc: netdev@vger.kernel.org
In-Reply-To: <D69C90565D53114396BF743585AF5A09122E61E9E9@VSHINMSMBX01.vshodc.lntinfotech.com>

Le lundi 28 mars 2011 à 23:48 +0530, Viral Mehta a écrit :

> Still, these are two system calls.

Yes. Is it a problem ? What kind ?

> In addition to this, many things to handle,
> 1. if the incoming_fd is blocking, then it will block till 64K data read. Why so ?

I dont think so. Try it, like read() of recv().

> 2. I believe underlying PIPE that we are using will also have some size limit
>     (like in user space 4K or 64K, not sure)

What kind of socket is able to deliver more than 64K frames ?

> 
> So, all in all
> Why cant we have just one system call which really transfers "length"
> bytes of data form one socket to another ? Recv "length" bytes of data
> from socket A and send to socket B.
> 
> I wanted to understand if there are any limitations or concerns that we still do
> not have any such system call .... ?
> 

The answer is : Once you try to implement this, you'll discover it'll be
splice() based, using pipe as a buffer between the sockets.

sendfile() is based on top of splice(), but it's faster to use splice().




^ permalink raw reply

* RE: zero copy for relay server
From: Viral Mehta @ 2011-03-28 18:18 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: netdev@vger.kernel.org
In-Reply-To: <1301331138.3182.43.camel@edumazet-laptop>


>From: Eric Dumazet [eric.dumazet@gmail.com]

>Le lundi 28 mars 2011 à 21:57 +0530, Viral Mehta a écrit :
>> Hi,
>> I am implementing a particular application where
>> my application acts nothing but like Relay Server.
>>
>> Relay server accepts connection from machine A.
>> It also accepts connection from Machine B.
>>
>> Machine A and B are on different LAN/subnnets.
>> Now, there are two connections.
>> What server is supposed to do is RECV packets from machine A and SEND same
>> to machine B.
>>
>> Pseudo Code is something like,
>> while(1)
>> {
>> recvagain:
>>    n =3D recv(incoming_fd, &buf, 8192, ...)
>>    if(n < 0)
>>         goto recvagain;
>>    send(outgoing_fd, &buf, n, ...);
>> }
>>
>> Now the question is,
>> I want to avoid kernel-user copy for such application.
>> I found that a syscall like "sendfile"; I wanted to know if there is any
>> similar thing exists in-kernel which can take 2 socket descriptors....
>>
>> If not, is it possible ? I would like to implement the same if someone
>> can suggest some pointers.

>linux way (if you want to avoid netfilter stuff and use userland code)
>is to use splice() system call, and a pipe between two sockets.

Yes, I want to avoid netfilter stuff.
I know it but I think it is more complicated than an application programmer should know.

>/* skeleton : must add error checking to exit the loop properly */
>int fds[2];
>pipe(fds);
>
>while (1) {
>        splice(incoming_fd, NULL, fds[1], NULL, 65536, 0);
>        splice(fds[0], NULL, outgoing_fd, NULL, 65536, 0);
>}

Still, these are two system calls.
In addition to this, many things to handle,
1. if the incoming_fd is blocking, then it will block till 64K data read. Why so ?
2. I believe underlying PIPE that we are using will also have some size limit
    (like in user space 4K or 64K, not sure)

So, all in all
Why cant we have just one system call which really transfers "length"
bytes of data form one socket to another ? Recv "length" bytes of data
from socket A and send to socket B.

I wanted to understand if there are any limitations or concerns that we still do
not have any such system call .... ?

>This way, messages dont cross kernel<>user boundary.

>The pipe is acting as a buffer between the two sockets.




______________________________________________________________________

The contents of this e-mail and any attachment(s) may contain confidential or privileged information for the intended recipient(s). Unintended recipients are prohibited from taking action on the basis of information in this e-mail and  using or disseminating the information,  and must notify the sender and delete it from their system. L&T Infotech will not accept responsibility or liability for the accuracy or completeness of, or the presence of any virus or disabling code in this e-mail"

______________________________________________________________________

^ permalink raw reply

* Re: [PATCH V13 4/4] ptp: Added a clock driver for the National Semiconductor PHYTER.
From: John Stultz @ 2011-03-28 18:08 UTC (permalink / raw)
  To: Richard Cochran
  Cc: linux-kernel, linux-api, netdev, devicetree-discuss,
	linux-arm-kernel, linuxppc-dev, Alan Cox, Arnd Bergmann,
	Christoph Lameter, David Miller, Krzysztof Halasa, Peter Zijlstra,
	Rodolfo Giometti, Thomas Gleixner, Benjamin Herrenschmidt,
	Mike Frysinger, Paul Mackerras, Russell King
In-Reply-To: <3ecd2d805a96324a04d3b231b16f21e2b5cda0e6.1301206090.git.richard.cochran@omicron.at>

On Sun, 2011-03-27 at 08:39 +0200, Richard Cochran wrote:
> This patch adds support for the PTP clock found on the DP83640.
> The basic clock operations and one external time stamp have
> been implemented.
> 
> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>

Acked-by: John Stultz <john.stultz@linaro.org>

^ permalink raw reply

* Re: [PATCH V13 3/4] ptp: Added a clock driver for the IXP46x.
From: John Stultz @ 2011-03-28 18:07 UTC (permalink / raw)
  To: Richard Cochran
  Cc: linux-kernel, linux-api, netdev, devicetree-discuss,
	linux-arm-kernel, linuxppc-dev, Alan Cox, Arnd Bergmann,
	Christoph Lameter, David Miller, Krzysztof Halasa, Peter Zijlstra,
	Rodolfo Giometti, Thomas Gleixner, Benjamin Herrenschmidt,
	Mike Frysinger, Paul Mackerras, Russell King
In-Reply-To: <706adc379f5f64dfad665cb8f831f44050b59eae.1301206090.git.richard.cochran@omicron.at>

On Sun, 2011-03-27 at 08:39 +0200, Richard Cochran wrote:
> This patch adds a driver for the hardware time stamping unit found on the
> IXP465. The basic clock operations and an external trigger are implemented.
> 
> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>

Acked-by: John Stultz <john.stultz@linaro.org>

^ permalink raw reply

* Re: [PATCH V13 2/4] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
From: John Stultz @ 2011-03-28 18:07 UTC (permalink / raw)
  To: Richard Cochran
  Cc: linux-kernel, linux-api, netdev, devicetree-discuss,
	linux-arm-kernel, linuxppc-dev, Alan Cox, Arnd Bergmann,
	Christoph Lameter, David Miller, Krzysztof Halasa, Peter Zijlstra,
	Rodolfo Giometti, Thomas Gleixner, Benjamin Herrenschmidt,
	Mike Frysinger, Paul Mackerras, Russell King
In-Reply-To: <120e586f678230c5f7d6dcfe70aa4483c2d50bf5.1301206090.git.richard.cochran@omicron.at>

On Sun, 2011-03-27 at 08:38 +0200, Richard Cochran wrote:
> The eTSEC includes a PTP clock with quite a few features. This patch adds
> support for the basic clock adjustment functions, plus two external time
> stamps, one alarm, and the PPS callback.
> 
> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>

Acked-by: John Stultz <john.stultz@linaro.org>


^ permalink raw reply


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