* [PATCH 1/2] alchemy: add au1000-eth platform device
From: Florian Fainelli @ 2009-11-10 0:13 UTC (permalink / raw)
To: Ralf Baechle, netdev, David Miller; +Cc: linux-mips
(resending per Ralf's request as the patch had some checkpatch errors)
This patch makes the board code register the au1000-eth
platform device. The au1000-eth platform data can be
overriden with the au1xxx_override_eth_cfg function
like it has to be done for the Bosporus board which uses
a different MAC/PHY setup.
Changes from v3:
- declare a static au1000_eth_platform_data structure for bosporus and
initialize it
- remove parenthis and bit shifting on SYS_PF_NI2
Changes from v2:
- declared the au1000-eth second driver instance platform_data
- made the override function generic and pass it the port number too
Changes from v1:
- remove per-board platform.c file
- add an override function to pass custom eth0 platform_data PHY settings
Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
diff --git a/arch/mips/alchemy/common/platform.c b/arch/mips/alchemy/common/platform.c
index 3be14b0..3fbe30c 100644
--- a/arch/mips/alchemy/common/platform.c
+++ b/arch/mips/alchemy/common/platform.c
@@ -19,6 +19,7 @@
#include <asm/mach-au1x00/au1xxx.h>
#include <asm/mach-au1x00/au1xxx_dbdma.h>
#include <asm/mach-au1x00/au1100_mmc.h>
+#include <asm/mach-au1x00/au1xxx_eth.h>
#define PORT(_base, _irq) \
{ \
@@ -326,6 +327,88 @@ static struct platform_device pbdb_smbus_device = {
};
#endif
+/* Macro to help defining the Ethernet MAC resources */
+#define MAC_RES(_base, _enable, _irq) \
+ { \
+ .start = CPHYSADDR(_base), \
+ .end = CPHYSADDR(_base + 0xffff), \
+ .flags = IORESOURCE_MEM, \
+ }, \
+ { \
+ .start = CPHYSADDR(_enable), \
+ .end = CPHYSADDR(_enable + 0x3), \
+ .flags = IORESOURCE_MEM, \
+ }, \
+ { \
+ .start = _irq, \
+ .end = _irq, \
+ .flags = IORESOURCE_IRQ \
+ }
+
+static struct resource au1xxx_eth0_resources[] = {
+#if defined(CONFIG_SOC_AU1000)
+ MAC_RES(AU1000_ETH0_BASE, AU1000_MAC0_ENABLE, AU1000_MAC0_DMA_INT),
+#elif defined(CONFIG_SOC_AU1100)
+ MAC_RES(AU1100_ETH0_BASE, AU1100_MAC0_ENABLE, AU1100_MAC0_DMA_INT),
+#elif defined(CONFIG_SOC_AU1550)
+ MAC_RES(AU1550_ETH0_BASE, AU1550_MAC0_ENABLE, AU1550_MAC0_DMA_INT),
+#elif defined(CONFIG_SOC_AU1500)
+ MAC_RES(AU1500_ETH0_BASE, AU1500_MAC0_ENABLE, AU1500_MAC0_DMA_INT),
+#endif
+};
+
+static struct resource au1xxx_eth1_resources[] = {
+#if defined(CONFIG_SOC_AU1000)
+ MAC_RES(AU1000_ETH1_BASE, AU1000_MAC1_ENABLE, AU1000_MAC1_DMA_INT),
+#elif defined(CONFIG_SOC_AU1550)
+ MAC_RES(AU1550_ETH1_BASE, AU1550_MAC1_ENABLE, AU1550_MAC1_DMA_INT),
+#elif defined(CONFIG_SOC_AU1500)
+ MAC_RES(AU1500_ETH1_BASE, AU1500_MAC1_ENABLE, AU1500_MAC1_DMA_INT),
+#endif
+};
+
+static struct au1000_eth_platform_data au1xxx_eth0_platform_data = {
+ .phy1_search_mac0 = 1,
+};
+
+static struct platform_device au1xxx_eth0_device = {
+ .name = "au1000-eth",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(au1xxx_eth0_resources),
+ .resource = au1xxx_eth0_resources,
+ .dev.platform_data = &au1xxx_eth0_platform_data,
+};
+
+#ifndef CONFIG_SOC_AU1100
+static struct au1000_eth_platform_data au1xxx_eth1_platform_data = {
+ .phy1_search_mac0 = 1,
+};
+
+static struct platform_device au1xxx_eth1_device = {
+ .name = "au1000-eth",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(au1xxx_eth1_resources),
+ .resource = au1xxx_eth1_resources,
+ .dev.platform_data = &au1xxx_eth1_platform_data,
+};
+#endif
+
+void __init au1xxx_override_eth_cfg(unsigned int port,
+ struct au1000_eth_platform_data *eth_data)
+{
+ if (!eth_data || port > 1)
+ return;
+
+ if (port == 0)
+ memcpy(&au1xxx_eth0_platform_data, eth_data,
+ sizeof(struct au1000_eth_platform_data));
+#ifndef CONFIG_SOC_AU1100
+ else
+ memcpy(&au1xxx_eth1_platform_data, eth_data,
+ sizeof(struct au1000_eth_platform_data));
+#endif
+}
+
static struct platform_device *au1xxx_platform_devices[] __initdata = {
&au1xx0_uart_device,
&au1xxx_usb_ohci_device,
@@ -345,6 +428,7 @@ static struct platform_device *au1xxx_platform_devices[] __initdata = {
#ifdef SMBUS_PSC_BASE
&pbdb_smbus_device,
#endif
+ &au1xxx_eth0_device,
};
static int __init au1xxx_platform_init(void)
@@ -356,6 +440,12 @@ static int __init au1xxx_platform_init(void)
for (i = 0; au1x00_uart_data[i].flags; i++)
au1x00_uart_data[i].uartclk = uartclk;
+#ifndef CONFIG_SOC_AU1100
+ /* Register second MAC if enabled in pinfunc */
+ if (!(au_readl(SYS_PINFUNC) & (u32)SYS_PF_NI2))
+ platform_device_register(&au1xxx_eth1_device);
+#endif
+
return platform_add_devices(au1xxx_platform_devices,
ARRAY_SIZE(au1xxx_platform_devices));
}
diff --git a/arch/mips/alchemy/devboards/db1x00/board_setup.c b/arch/mips/alchemy/devboards/db1x00/board_setup.c
index 7aee14d..ad26db2 100644
--- a/arch/mips/alchemy/devboards/db1x00/board_setup.c
+++ b/arch/mips/alchemy/devboards/db1x00/board_setup.c
@@ -32,6 +32,7 @@
#include <linux/interrupt.h>
#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_eth.h>
#include <asm/mach-db1x00/db1x00.h>
#include <asm/mach-db1x00/bcsr.h>
@@ -43,6 +44,18 @@ char irq_tab_alchemy[][5] __initdata = {
[13] = { -1, AU1500_PCI_INTA, AU1500_PCI_INTB, AU1500_PCI_INTC, AU1500_PCI_INTD }, /* IDSEL 13 - PCI slot */
};
#endif
+
+/*
+ * Micrel/Kendin 5 port switch attached to MAC0,
+ * MAC0 is associated with PHY address 5 (== WAN port)
+ * MAC1 is not associated with any PHY, since it's connected directly
+ * to the switch.
+ * no interrupts are used
+ */
+static struct au1000_eth_platform_data eth0_pdata = {
+ .phy_static_config = 1,
+ .phy_addr = 5,
+};
#ifdef CONFIG_MIPS_BOSPORUS
char irq_tab_alchemy[][5] __initdata = {
@@ -50,6 +63,8 @@ char irq_tab_alchemy[][5] __initdata = {
[12] = { -1, AU1500_PCI_INTA, 0xff, 0xff, 0xff }, /* IDSEL 12 - SN1741 */
[13] = { -1, AU1500_PCI_INTA, AU1500_PCI_INTB, AU1500_PCI_INTC, AU1500_PCI_INTD }, /* IDSEL 13 - PCI slot */
};
+
+
#endif
#ifdef CONFIG_MIPS_MIRAGE
@@ -103,6 +118,8 @@ void __init board_setup(void)
printk(KERN_INFO "AMD Alchemy Au1100/Db1100 Board\n");
#endif
#ifdef CONFIG_MIPS_BOSPORUS
+ au1xxx_override_eth_cfg(0, ð0_pdata);
+
printk(KERN_INFO "AMD Alchemy Bosporus Board\n");
#endif
#ifdef CONFIG_MIPS_MIRAGE
diff --git a/arch/mips/include/asm/mach-au1x00/au1xxx_eth.h b/arch/mips/include/asm/mach-au1x00/au1xxx_eth.h
new file mode 100644
index 0000000..f30529e
--- /dev/null
+++ b/arch/mips/include/asm/mach-au1x00/au1xxx_eth.h
@@ -0,0 +1,18 @@
+#ifndef __AU1X00_ETH_DATA_H
+#define __AU1X00_ETH_DATA_H
+
+/* Platform specific PHY configuration passed to the MAC driver */
+struct au1000_eth_platform_data {
+ int phy_static_config;
+ int phy_search_highest_addr;
+ int phy1_search_mac0;
+ int phy_addr;
+ int phy_busid;
+ int phy_irq;
+};
+
+void __init au1xxx_override_eth_cfg(unsigned port,
+ struct au1000_eth_platform_data *eth_data);
+
+#endif /* __AU1X00_ETH_DATA_H */
+
^ permalink raw reply related
* Re: [RFC,PATCH] mutex: mutex_is_owner() helper
From: Eric Dumazet @ 2009-11-09 23:21 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, Linus Torvalds, David S. Miller, Linux Netdev List,
linux kernel, Thomas Gleixner
In-Reply-To: <1257792987.4108.364.camel@laptop>
Peter Zijlstra a écrit :
> On Wed, 2009-11-04 at 18:19 +0100, Eric Dumazet wrote:
>> BTW, I was thinking of a mutex_yield() implementation, but could not
>> cook it without hard thinking, maybe you already have some nice
>> implementation ?
>
> Why? Yield sets off alarm bells, since 99.9%, and possibly more, of its
> uses are wrong.
If I remember well, I had problems doing "modprobe dummy numdummies=30000",
because it creates 30000 netdevices, and thanks to hotplug starts 30000 udev
that all wait that my modprobe is finished... Nice to see load average going
so big by the way :)
I tried following patch without success, because rtnl_unlock()/rtnl_lock()
is too fast (awaken process(es) ha(s/ve) no chance to get the lock, as we
take it immediately after releasing it)
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index 37dcfdc..3de1466 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -138,8 +138,11 @@ static int __init dummy_init_module(void)
rtnl_lock();
err = __rtnl_link_register(&dummy_link_ops);
- for (i = 0; i < numdummies && !err; i++)
+ for (i = 0; i < numdummies && !err; i++) {
err = dummy_init_one();
+ rtnl_unlock();
+ rtnl_lock();
+ }
if (err < 0)
__rtnl_link_unregister(&dummy_link_ops);
rtnl_unlock();
Then I added a msleep(1) between the unlock/lock and got beter results.
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index 37dcfdc..108c4fa 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -138,8 +138,12 @@ static int __init dummy_init_module(void)
rtnl_lock();
err = __rtnl_link_register(&dummy_link_ops);
- for (i = 0; i < numdummies && !err; i++)
+ for (i = 0; i < numdummies && !err; i++) {
err = dummy_init_one();
+ rtnl_unlock();
+ msleep(1);
+ rtnl_lock();
+ }
if (err < 0)
__rtnl_link_unregister(&dummy_link_ops);
rtnl_unlock();
But if hotplug is disabled, this force a useless msleep(1) * 30000 -> this is bit slow
Yes, this code is stupid, but I use it to stress network stack
with insane number of devices, to spot scalability problems.
mutex_yield() could help in this situation.
mutex is said to be FIFO, but its not exactly true : A new comer can take the mutex
even if 10000 threads are waiting on mutex...
I wont mention other problems, because mutex_{try}lock() has no timedwait variant, and
funny code doing :
if (!rtnl_trylock())
return restart_syscall();
Making 30000 processes running/fighting to get the mutex :(
^ permalink raw reply related
* Re: [PATCH] IPv6: use ipv6_addr_v4mapped()
From: Eric Dumazet @ 2009-11-09 22:59 UTC (permalink / raw)
To: Brian Haley; +Cc: David Miller, netdev@vger.kernel.org
In-Reply-To: <4AF89241.7060203@hp.com>
Brian Haley a écrit :
> Change udp6_portaddr_hash() to use ipv6_addr_v4mapped()
> inline instead of ipv6_addr_type().
>
> Signed-off-by: Brian Haley <brian.haley@hp.com>
> ---
>
> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
> index 2915e1d..0ed1637 100644
> --- a/net/ipv6/udp.c
> +++ b/net/ipv6/udp.c
> @@ -89,7 +89,7 @@ static unsigned int udp6_portaddr_hash(struct net *net,
>
> if (ipv6_addr_any(addr6))
> hash = jhash_1word(0, mix);
> - else if (ipv6_addr_type(addr6) == IPV6_ADDR_MAPPED)
> + else if (ipv6_addr_v4mapped(addr6))
> hash = jhash_1word(addr6->s6_addr32[3], mix);
> else
> hash = jhash2(addr6->s6_addr32, 4, mix);
Indeed, its a bit faster ;)
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Thanks Brian
^ permalink raw reply
* [PATCH] IPv6: use ipv6_addr_v4mapped()
From: Brian Haley @ 2009-11-09 22:05 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org
Change udp6_portaddr_hash() to use ipv6_addr_v4mapped()
inline instead of ipv6_addr_type().
Signed-off-by: Brian Haley <brian.haley@hp.com>
---
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 2915e1d..0ed1637 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -89,7 +89,7 @@ static unsigned int udp6_portaddr_hash(struct net *net,
if (ipv6_addr_any(addr6))
hash = jhash_1word(0, mix);
- else if (ipv6_addr_type(addr6) == IPV6_ADDR_MAPPED)
+ else if (ipv6_addr_v4mapped(addr6))
hash = jhash_1word(addr6->s6_addr32[3], mix);
else
hash = jhash2(addr6->s6_addr32, 4, mix);
^ permalink raw reply related
* [PATCH net-next-2.6] ipv6: speedup inet6_dump_ifinfo()
From: Eric Dumazet @ 2009-11-09 22:11 UTC (permalink / raw)
To: David S. Miller; +Cc: Linux Netdev List
When handling large number of netdevice, inet6_dump_ifinfo()
is very slow because it has O(N^2) complexity.
Instead of scanning one single list, we can use the 256 sub lists
of the dev_index hash table, and RCU lookups.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
net/ipv6/addrconf.c | 45 ++++++++++++++++++++++++++----------------
1 files changed, 28 insertions(+), 17 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 024bba3..f9f7fd6 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -3823,28 +3823,39 @@ nla_put_failure:
static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
- int idx, err;
- int s_idx = cb->args[0];
+ int h, s_h;
+ int idx = 0, err, s_idx;
struct net_device *dev;
struct inet6_dev *idev;
+ struct hlist_head *head;
+ struct hlist_node *node;
- read_lock(&dev_base_lock);
- idx = 0;
- for_each_netdev(net, dev) {
- if (idx < s_idx)
- goto cont;
- if ((idev = in6_dev_get(dev)) == NULL)
- goto cont;
- err = inet6_fill_ifinfo(skb, idev, NETLINK_CB(cb->skb).pid,
- cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI);
- in6_dev_put(idev);
- if (err <= 0)
- break;
+ s_h = cb->args[0];
+ s_idx = cb->args[1];
+
+ rcu_read_lock();
+ for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
+ idx = 0;
+ head = &net->dev_index_head[h];
+ hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
+ if (idx < s_idx)
+ goto cont;
+ idev = __in6_dev_get(dev);
+ if (!idev)
+ goto cont;
+ if (inet6_fill_ifinfo(skb, idev,
+ NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq,
+ RTM_NEWLINK, NLM_F_MULTI) <= 0)
+ goto out;
cont:
- idx++;
+ idx++;
+ }
}
- read_unlock(&dev_base_lock);
- cb->args[0] = idx;
+out:
+ rcu_read_unlock();
+ cb->args[1] = idx;
+ cb->args[0] = h;
return skb->len;
}
^ permalink raw reply related
* [PATCH] e1000e: Fix usage under kexec
From: Sven Anders @ 2009-11-09 20:25 UTC (permalink / raw)
To: netdev
[-- Attachment #1.1: Type: text/plain, Size: 1516 bytes --]
Hello!
We're experiencing the following problem:
We start a minimal linux system, prepare the system and start the
final system via kexec.
On two different systems (one with a 82573 and one with a 82574),
the driver cannot initialize the network hardware, because the PHY
is not recognized.
We get the following error:
e1000e: probe of 0000:02:00.0 failed with error -2
We cannot unload the driver of the first kernel, because it's
compiled in statically.
It's caused by the PHY, because it's already initialized by the
first driver and therefore does not respond correctly. The driver
assumes the PHY to be in 'reset' state, which is done by the BIOS.
We fixed it by adding a
+ /* Reset PHY before initializing it. Allows re-init after kexec. */
+ if (!e1000_check_reset_block(hw))
+ e1000_phy_hw_reset(hw);
to the initialization routine. I'm not sure, if we need the additional
"block" check.
A patch is attached.
Please send a note, if the patch is correct or wrong...
Regards
Sven Anders
--
Sven Anders <anders@anduras.de> () Ascii Ribbon Campaign
/\ Support plain text e-mail
ANDURAS service solutions AG
Innstrasse 71 - 94036 Passau - Germany
Web: www.anduras.de - Tel: +49 (0)851-4 90 50-0 - Fax: +49 (0)851-4 90 50-55
Those who would give up essential Liberty, to purchase a little
temporary Safety, deserve neither Liberty nor Safety.
- Benjamin Franklin
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: e1000e-kexec.patch --]
[-- Type: text/x-patch; name="e1000e-kexec.patch", Size: 507 bytes --]
--- linux-2.6.29.6/drivers/net/e1000e/82571.c.orig 2009-11-04 00:44:27.000000000 +0100
+++ linux-2.6.29.6/drivers/net/e1000e/82571.c 2009-11-04 00:44:19.000000000 +0100
@@ -107,6 +107,10 @@ static s32 e1000_init_phy_params_82571(s
break;
}
+ /* Reset PHY before initializing it. Allows re-init after kexec. */
+ if (!e1000_check_reset_block(hw))
+ e1000_phy_hw_reset(hw);
+
/* This can only be done after all function pointers are setup. */
ret_val = e1000_get_phy_id_82571(hw);
[-- Attachment #1.3: sven.vcf --]
[-- Type: text/x-vcard, Size: 307 bytes --]
begin:vcard
fn:Sven Anders
n:Anders;Sven
adr;quoted-printable:;;Weidestra=C3=9Fe 19;Hannover;Niedersachsen;30453;Deutschland
email;internet:sven@anduras.de
tel;home:+49 (0)511 / 2123090
tel;cell:+49 (0)170 / 8091180
x-mozilla-html:FALSE
url:http://staff.anduras.de/anders
version:2.1
end:vcard
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 260 bytes --]
^ permalink raw reply
* [PATCH] e1000e: Add new ID
From: Sven Anders @ 2009-11-09 20:25 UTC (permalink / raw)
To: netdev
[-- Attachment #1.1: Type: text/plain, Size: 652 bytes --]
Hello!
We use the the 82571EB (QUAD COPPER BP) with the e1000e driver.
We haven't experienced any problems yet.
The patch is trivial, just adding a new PCI ID...
Regards
Sven Anders
--
Sven Anders <anders@anduras.de> () Ascii Ribbon Campaign
/\ Support plain text e-mail
ANDURAS service solutions AG
Innstrasse 71 - 94036 Passau - Germany
Web: www.anduras.de - Tel: +49 (0)851-4 90 50-0 - Fax: +49 (0)851-4 90 50-55
Those who would give up essential Liberty, to purchase a little
temporary Safety, deserve neither Liberty nor Safety.
- Benjamin Franklin
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: e1000e-Add_ID_01A0.patch --]
[-- Type: text/x-patch; name="e1000e-Add_ID_01A0.patch", Size: 1724 bytes --]
--- linux-2.6.26.2/drivers/net/e1000e/hw.h.orig 2008-09-29 14:20:55.000000000 +0200
+++ linux-2.6.26.2/drivers/net/e1000e/hw.h 2008-09-29 14:26:38.000000000 +0200
@@ -323,6 +323,7 @@
#define E1000_DEV_ID_82571PT_QUAD_COPPER 0x10D5
#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5
#define E1000_DEV_ID_82571EB_QUAD_COPPER_LP 0x10BC
+#define E1000_DEV_ID_82571EB_QUAD_COPPER_BP 0x10A0
#define E1000_DEV_ID_82571EB_SERDES_DUAL 0x10D9
#define E1000_DEV_ID_82571EB_SERDES_QUAD 0x10DA
#define E1000_DEV_ID_82572EI_COPPER 0x107D
--- linux-2.6.26.2/drivers/net/e1000e/netdev.c.orig 2008-09-29 14:27:01.000000000 +0200
+++ linux-2.6.26.2/drivers/net/e1000e/netdev.c 2008-09-29 14:27:29.000000000 +0200
@@ -4560,6 +4560,7 @@
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_FIBER), board_82571 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER), board_82571 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER_LP), board_82571 },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER_BP), board_82571 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_FIBER), board_82571 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_SERDES), board_82571 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_SERDES_DUAL), board_82571 },
--- linux-2.6.26.2/drivers/net/e1000e/82571.c.orig 2008-09-29 14:27:42.000000000 +0200
+++ linux-2.6.26.2/drivers/net/e1000e/82571.c 2008-09-29 14:28:04.000000000 +0200
@@ -274,6 +274,7 @@
case E1000_DEV_ID_82571EB_QUAD_COPPER:
case E1000_DEV_ID_82571EB_QUAD_FIBER:
case E1000_DEV_ID_82571EB_QUAD_COPPER_LP:
+ case E1000_DEV_ID_82571EB_QUAD_COPPER_BP:
case E1000_DEV_ID_82571PT_QUAD_COPPER:
adapter->flags |= FLAG_IS_QUAD_PORT;
/* mark the first port */
[-- Attachment #1.3: sven.vcf --]
[-- Type: text/x-vcard, Size: 307 bytes --]
begin:vcard
fn:Sven Anders
n:Anders;Sven
adr;quoted-printable:;;Weidestra=C3=9Fe 19;Hannover;Niedersachsen;30453;Deutschland
email;internet:sven@anduras.de
tel;home:+49 (0)511 / 2123090
tel;cell:+49 (0)170 / 8091180
x-mozilla-html:FALSE
url:http://staff.anduras.de/anders
version:2.1
end:vcard
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 260 bytes --]
^ permalink raw reply
* Re: [PATCH] iproute2: ip route flush bugfix
From: Eric Dumazet @ 2009-11-09 20:24 UTC (permalink / raw)
To: Sven Anders; +Cc: netdev, Stephen Hemminger
In-Reply-To: <4AF86FE3.4090005@anduras.de>
Sven Anders a écrit :
> Hello!
>
> I experienced an error, if I try to perform a
>
> ip route flush proto 4
>
> with many routes in a complex environment, it
> gave me the following error:
>
> Failed to send flush request: Success
> Flush terminated
>
> I'm using version 2.6.29.
> I check GIT, but there was only the "MSG_PEEK" fix.
>
> I tracked it down to the rtnl_send_check() function
> in lib/libnetlink.c.
>
> In this function there is the following for loop:
>
> for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, status);
> h = NLMSG_NEXT(h, status)) {
> if (h->nlmsg_type == NLMSG_ERROR) {
> struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
> if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
> fprintf(stderr, "ERROR truncated\n");
> else
> errno = -err->error;
> }
> return -1;
> }
>
> I think the "return -1;" should be inside the if statement.
>
> I attached a patch for this. The first part of the patch is taken from
> one of the late git commits.
>
> Please note me, if the fix is wrong...
>
>
> Regards
> Sven
>
> PS: This is a repost, because I neither received a confirmation nor a remark, that
> the fix is wrong. Moreover the fix didn't make it in the GIT repository yet...
>
Yes, I posted a similar patch yesterday :)
http://article.gmane.org/gmane.linux.network/142908
Still waiting an ACK from Stephen
^ permalink raw reply
* [PATCH] iproute2: ip route flush bugfix
From: Sven Anders @ 2009-11-09 19:39 UTC (permalink / raw)
To: netdev
[-- Attachment #1.1: Type: text/plain, Size: 1673 bytes --]
Hello!
I experienced an error, if I try to perform a
ip route flush proto 4
with many routes in a complex environment, it
gave me the following error:
Failed to send flush request: Success
Flush terminated
I'm using version 2.6.29.
I check GIT, but there was only the "MSG_PEEK" fix.
I tracked it down to the rtnl_send_check() function
in lib/libnetlink.c.
In this function there is the following for loop:
for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, status);
h = NLMSG_NEXT(h, status)) {
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
fprintf(stderr, "ERROR truncated\n");
else
errno = -err->error;
}
return -1;
}
I think the "return -1;" should be inside the if statement.
I attached a patch for this. The first part of the patch is taken from
one of the late git commits.
Please note me, if the fix is wrong...
Regards
Sven
PS: This is a repost, because I neither received a confirmation nor a remark, that
the fix is wrong. Moreover the fix didn't make it in the GIT repository yet...
--
Sven Anders <anders@anduras.de> () Ascii Ribbon Campaign
/\ Support plain text e-mail
Weidestraße 19 - 30453 Hannover || Höllgasse 28 - 94032 Passau @ Germany
Web: http://staff2.anduras.de/anders/kim/ - Tel: +49 (0)170 / 80 911 80
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: iproute-2.6.29-flush.patch --]
[-- Type: text/x-patch; name="iproute-2.6.29-flush.patch", Size: 649 bytes --]
--- iproute2-2.6.29/lib/libnetlink.c.orig 2009-09-23 14:47:03.000000000 +0200
+++ iproute2-2.6.29/lib/libnetlink.c 2009-09-23 14:48:09.000000000 +0200
@@ -122,7 +122,7 @@ int rtnl_send_check(struct rtnl_handle *
return status;
/* Check for errors */
- status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT);
+ status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK);
if (status < 0) {
if (errno == EAGAIN)
return 0;
@@ -137,8 +137,8 @@ int rtnl_send_check(struct rtnl_handle *
fprintf(stderr, "ERROR truncated\n");
else
errno = -err->error;
- }
return -1;
+ }
}
return 0;
[-- Attachment #1.3: sven.vcf --]
[-- Type: text/x-vcard, Size: 307 bytes --]
begin:vcard
fn:Sven Anders
n:Anders;Sven
adr;quoted-printable:;;Weidestra=C3=9Fe 19;Hannover;Niedersachsen;30453;Deutschland
email;internet:sven@anduras.de
tel;home:+49 (0)511 / 2123090
tel;cell:+49 (0)170 / 8091180
x-mozilla-html:FALSE
url:http://staff.anduras.de/anders
version:2.1
end:vcard
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 260 bytes --]
^ permalink raw reply
* Re: TC-HTB issue : low throughput
From: Jean Tourrilhes @ 2009-11-09 20:04 UTC (permalink / raw)
To: Jarek Poplawski; +Cc: netdev
In-Reply-To: <4AF60349.8000502@gmail.com>
On Sun, Nov 08, 2009 at 12:31:21AM +0100, Jarek Poplawski wrote:
> Jean Tourrilhes wrote, On 11/07/2009 03:43 AM:
>
> > Behaviour :
> > ---------
> >
> > If I add classes 1:2 and 1:3 :
> > Each host independantly : ~170 Mb/s.
> > Both host together, for 10.10.10.38 : ~106 Mb/s ;
> > Both host together, for 10.10.10.33 : ~135 Mb/s
> >
> > So, not only performance did drop significantely, but
> > prioritisation did not happen as expected.
>
> If these eths are vlans (or other virtuals) something like this
> often happens if you forget to set dev's txqueuelen before
> adding classes (or a subqdisc with some 'limit').
Thanks a lot ! I've just added :
ifconfig eth6.34 txqueuelen 5
And now I get 450 Mb/s + 133 Mb/s. Not perfect, but much much
better, and probably good enough for now ;-)
> > I've noticed that /sbin/tc calculates a very low burst
> > value. This is due to the content of /proc/net/psched. I'm wondering
> > if the burst calculation is what causes the issue here.
> > However, I tried with "burst 50kb" and saw no difference...
>
> There is (probably still) unfixed overflow in tc.
>
> BTW, v2.6.31 should be more exact (but so much...) for above 100mbit
> scheduling, especially with this patch to iproute2:
> http://marc.info/?l=linux-netdev&m=124453482324409&w=2
>
> Jarek P.
Thanks. I'll look into upgrading.
Regards,
Jean
^ permalink raw reply
* Re: RFC: net: allow to propagate errors through ->ndo_hard_start_xmit()
From: Herbert Xu @ 2009-11-09 19:50 UTC (permalink / raw)
To: Patrick McHardy
Cc: Linux Netdev List, Jarek Poplawski, David S. Miller,
Stephen Hemminger
In-Reply-To: <4AF87070.6020007@trash.net>
On Mon, Nov 09, 2009 at 08:41:36PM +0100, Patrick McHardy wrote:
>
> - I'm not sure the error handling in dev_hard_start_xmit() for GSO
> skbs is optimal. When the driver returns an error, it is assumed
> the current segment has been freed. The patch then frees the
> entire GSO skb, including all remaining segments. Alternatively
> it could try to transmit the remaining segments later.
Well driver errors (not queueing errors) should never happen.
And if they do then they're likely to persist. So freeing the
rest should be sufficient, unless of course if doing it some
other way is simpler :)
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* RFC: net: allow to propagate errors through ->ndo_hard_start_xmit()
From: Patrick McHardy @ 2009-11-09 19:41 UTC (permalink / raw)
To: Linux Netdev List
Cc: Herbert Xu, Jarek Poplawski, David S. Miller, Stephen Hemminger
[-- Attachment #1: Type: text/plain, Size: 1232 bytes --]
I've updated my patch to propagate error values (errno and NET_XMIT
codes) through ndo_hard_start_xmit() and incorporated the suggestions
made last time, namely:
- move slightly complicated return value check to inline function and
add a few comments
- fix error handling while in the middle of transmitting GSO skbs
I've also audited the tree once again for invalid return values and
found a single remaining instance in a Wimax driver, I'll take care
of that later.
Two questions remain:
- I'm not sure the error handling in dev_hard_start_xmit() for GSO
skbs is optimal. When the driver returns an error, it is assumed
the current segment has been freed. The patch then frees the
entire GSO skb, including all remaining segments. Alternatively
it could try to transmit the remaining segments later.
- Stephen recently introduced an enum for the netdev_tx codes, with
this patch drivers are allowed to return different values.
The mainly useful part about Stephen's patch IMO is the netdev_tx
typedef to make it easier to locate ndo_start_xmit() functions
which are defined in different files than the netdev_ops.
So we could remove the enum again and simply typedef an int.
Any opinions are welcome :)
[-- Attachment #2: 01.diff --]
[-- Type: text/x-patch, Size: 6261 bytes --]
commit 08a98f11bc1c1452df74c171409218d2243f0818
Author: Patrick McHardy <kaber@trash.net>
Date: Mon Nov 9 20:33:14 2009 +0100
net: allow to propagate errors through ->ndo_hard_start_xmit()
Currently the ->ndo_hard_start_xmit() callbacks are only permitted to return
one of the NETDEV_TX codes. This prevents any kind of error propagation for
virtual devices, like queue congestion of the underlying device in case of
layered devices, or unreachability in case of tunnels.
This patches changes the NET_XMIT codes to avoid clashes with the NETDEV_TX
codes and changes the two callers of dev_hard_start_xmit() to expect either
errno codes, NET_XMIT codes or NETDEV_TX codes as return value.
In case of qdisc_restart(), all non NETDEV_TX codes are mapped to NETDEV_TX_OK
since no error propagation is possible when using qdiscs. In case of
dev_queue_xmit(), the error is propagated upwards.
Signed-off-by: Patrick McHardy <kaber@trash.net>
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 465add6..ab2812c 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -63,27 +63,48 @@ struct wireless_dev;
#define HAVE_FREE_NETDEV /* free_netdev() */
#define HAVE_NETDEV_PRIV /* netdev_priv() */
-#define NET_XMIT_SUCCESS 0
-#define NET_XMIT_DROP 1 /* skb dropped */
-#define NET_XMIT_CN 2 /* congestion notification */
-#define NET_XMIT_POLICED 3 /* skb is shot by police */
-#define NET_XMIT_MASK 0xFFFF /* qdisc flags in net/sch_generic.h */
+/*
+ * Transmit return codes: transmit return codes originate from three different
+ * namespaces:
+ *
+ * - qdisc return codes
+ * - driver transmit return codes
+ * - errno values
+ *
+ * Drivers are allowed to return any one of those in their hard_start_xmit()
+ * function. Real network devices commonly used with qdiscs should only return
+ * the driver transmit return codes though - when qdiscs are used, the actual
+ * transmission happens asynchronously, so the value is not propagated to
+ * higher layers. Virtual network devices transmit synchronously, in this case
+ * the driver transmit return codes are consumed by dev_queue_xmit(), all
+ * others are propagated to higher layers.
+ */
+
+/* qdisc ->enqueue() return codes. */
+#define NET_XMIT_SUCCESS 0x00
+#define NET_XMIT_DROP 0x10 /* skb dropped */
+#define NET_XMIT_CN 0x20 /* congestion notification */
+#define NET_XMIT_POLICED 0x30 /* skb is shot by police */
+#define NET_XMIT_MASK 0xf0 /* qdisc flags in net/sch_generic.h */
/* Backlog congestion levels */
-#define NET_RX_SUCCESS 0 /* keep 'em coming, baby */
-#define NET_RX_DROP 1 /* packet dropped */
+#define NET_RX_SUCCESS 0 /* keep 'em coming, baby */
+#define NET_RX_DROP 1 /* packet dropped */
/* NET_XMIT_CN is special. It does not guarantee that this packet is lost. It
* indicates that the device will soon be dropping packets, or already drops
* some packets of the same priority; prompting us to send less aggressively. */
-#define net_xmit_eval(e) ((e) == NET_XMIT_CN? 0 : (e))
+#define net_xmit_eval(e) ((e) == NET_XMIT_CN ? 0 : (e))
#define net_xmit_errno(e) ((e) != NET_XMIT_CN ? -ENOBUFS : 0)
/* Driver transmit return codes */
+#define NETDEV_TX_MASK 0xf
+
enum netdev_tx {
- NETDEV_TX_OK = 0, /* driver took care of packet */
- NETDEV_TX_BUSY, /* driver tx path was busy*/
- NETDEV_TX_LOCKED = -1, /* driver tx lock was already taken */
+ __NETDEV_TX_MIN = INT_MIN, /* make sure enum is signed */
+ NETDEV_TX_OK = 0, /* driver took care of packet */
+ NETDEV_TX_BUSY = 1, /* driver tx path was busy*/
+ NETDEV_TX_LOCKED = 2, /* driver tx lock was already taken */
};
typedef enum netdev_tx netdev_tx_t;
diff --git a/net/core/dev.c b/net/core/dev.c
index bf629ac..1f5752d 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1756,7 +1756,7 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq)
{
const struct net_device_ops *ops = dev->netdev_ops;
- int rc;
+ int rc = NETDEV_TX_OK;
if (likely(!skb->next)) {
if (!list_empty(&ptype_all))
@@ -1804,6 +1804,8 @@ gso:
nskb->next = NULL;
rc = ops->ndo_start_xmit(nskb, dev);
if (unlikely(rc != NETDEV_TX_OK)) {
+ if (rc & ~NETDEV_TX_MASK)
+ goto out_kfree_gso_skb;
nskb->next = skb->next;
skb->next = nskb;
return rc;
@@ -1813,11 +1815,14 @@ gso:
return NETDEV_TX_BUSY;
} while (skb->next);
- skb->destructor = DEV_GSO_CB(skb)->destructor;
+ rc = NETDEV_TX_OK;
+out_kfree_gso_skb:
+ if (likely(skb->next == NULL))
+ skb->destructor = DEV_GSO_CB(skb)->destructor;
out_kfree_skb:
kfree_skb(skb);
- return NETDEV_TX_OK;
+ return rc;
}
static u32 skb_tx_hashrnd;
@@ -1905,6 +1910,23 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
return rc;
}
+static inline bool dev_xmit_complete(int rc)
+{
+ /* successful transmission */
+ if (rc == NETDEV_TX_OK)
+ return true;
+
+ /* error while transmitting, driver consumed skb */
+ if (rc < 0)
+ return true;
+
+ /* error while queueing to a different device, driver consumed skb */
+ if (rc & NET_XMIT_MASK)
+ return true;
+
+ return false;
+}
+
/**
* dev_queue_xmit - transmit a buffer
* @skb: buffer to transmit
@@ -2002,8 +2024,8 @@ gso:
HARD_TX_LOCK(dev, txq, cpu);
if (!netif_tx_queue_stopped(txq)) {
- rc = NET_XMIT_SUCCESS;
- if (!dev_hard_start_xmit(skb, dev, txq)) {
+ rc = dev_hard_start_xmit(skb, dev, txq);
+ if (dev_xmit_complete(rc)) {
HARD_TX_UNLOCK(dev, txq);
goto out;
}
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 4ae6aa5..b13821a 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -120,8 +120,15 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
HARD_TX_LOCK(dev, txq, smp_processor_id());
if (!netif_tx_queue_stopped(txq) &&
- !netif_tx_queue_frozen(txq))
+ !netif_tx_queue_frozen(txq)) {
ret = dev_hard_start_xmit(skb, dev, txq);
+
+ /* an error implies that the skb was consumed */
+ if (ret < 0)
+ ret = NETDEV_TX_OK;
+ /* all NET_XMIT codes map to NETDEV_TX_OK */
+ ret &= ~NET_XMIT_MASK;
+ }
HARD_TX_UNLOCK(dev, txq);
spin_lock(root_lock);
^ permalink raw reply related
* Re: pull request: wireless-next-2.6 2009-11-09
From: David Miller @ 2009-11-09 19:28 UTC (permalink / raw)
To: linville-2XuSBdqkA4R54TAoqtyWWQ
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20091109183010.GF2805-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>
From: "John W. Linville" <linville-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>
Date: Mon, 9 Nov 2009 13:30:10 -0500
> Another big batch of patches intended for 2.6.33... Among the usual
> suspects are big updates for ath9k, iwlwifi, wl1271, mwl8k, and (especially)
> rt2x00. Also included are some patches to move some old pre-802.11
> drivers over to staging, and a number of mac80211 updates.
>
> Please let me know if there are problems!
Pulled, thanks a lot!
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] xfrm: SAD entries do not expire correctly after suspend-resume
From: Herbert Xu @ 2009-11-09 19:23 UTC (permalink / raw)
To: Yury Polyanskiy; +Cc: netdev, davem, peterz, yoshfuji, tglx, mingo
In-Reply-To: <20091109133153.668bb296@penta.localdomain>
On Mon, Nov 09, 2009 at 01:31:53PM -0500, Yury Polyanskiy wrote:
>
> But why would it be inoperable for hours?
As I said the other end is buggy and won't allow you to reconnect
when you've just connected successfully. Eventially it'll timeout
and let you back in but it could take hours.
> In any case, running ntpdate before racoon fixes the problem.
Not if the only trusted time source is over IPsec.
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Fix gpio-mdio driver to work for gpio's that return something other than 0 and 1.
From: Lennart Sorensen @ 2009-11-09 19:02 UTC (permalink / raw)
To: linux-kernel; +Cc: Len Sorensen, netdev, Paulius Zaleckas, Laurent Pinchart
The gpio-mdio driver seems to assume GPIOs return 0 and 1, although that
doesn't seem to be the case. I see return values of 0 and 0x1000000
for the GPIO I happen to be using. A simple application of !! solves
that though.
Tested on a RuggedCom RX5000 (mpc8360e based).
Signed-off-by: Lennart Sorensen <lsorense@csclub.uwaterloo.ca>
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index 8659d34..7e76f0c 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -55,7 +55,7 @@ static int mdio_get(struct mdiobb_ctrl *ctrl)
struct mdio_gpio_info *bitbang =
container_of(ctrl, struct mdio_gpio_info, ctrl);
- return gpio_get_value(bitbang->mdio);
+ return !!gpio_get_value(bitbang->mdio);
}
static void mdio_set(struct mdiobb_ctrl *ctrl, int what)
^ permalink raw reply related
* Fix syntax of mdio.txt to match actual syntax.
From: Lennart Sorensen @ 2009-11-09 18:56 UTC (permalink / raw)
To: linux-kernel; +Cc: Kumar Gala, Len Sorensen, netdev
The syntax in Documentation/powerpc/dts-bindings/gpio/mdio.txt is not
up to date and no longer works. Fix this as well as actually show how
to represent a PHY on the gpio-bitbang bus.
Tested with 2.6.31 on a RuggedCom RX5000 (mpc8360e based).
Signed-off-by: Lennart Sorensen <lsorense@csclub.uwaterloo.ca>
diff --git a/Documentation/powerpc/dts-bindings/gpio/mdio.txt b/Documentation/powerpc/dts-bindings/gpio/mdio.txt
index bc95495..7a84f25 100644
--- a/Documentation/powerpc/dts-bindings/gpio/mdio.txt
+++ b/Documentation/powerpc/dts-bindings/gpio/mdio.txt
@@ -14,6 +14,15 @@ mdio {
compatible = "virtual,mdio-gpio";
#address-cells = <1>;
#size-cells = <0>;
- gpios = <&qe_pio_a 11
- &qe_pio_c 6>;
+ gpios = <&qe_pio_a 11 0
+ &qe_pio_c 6 0>;
+
+ phy0: ethernet-phy@00 {
+ reg = <0x18>;
+ device_type = "ethernet-phy";
+ };
+ phy1: ethernet-phy@01 {
+ reg = <0x0f>;
+ device_type = "ethernet-phy";
+ };
};
^ permalink raw reply related
* Re: [RFC,PATCH] mutex: mutex_is_owner() helper
From: Peter Zijlstra @ 2009-11-09 18:56 UTC (permalink / raw)
To: Eric Dumazet
Cc: Ingo Molnar, Linus Torvalds, David S. Miller, Linux Netdev List,
linux kernel, Thomas Gleixner
In-Reply-To: <4AF1B7A7.6030902@gmail.com>
On Wed, 2009-11-04 at 18:19 +0100, Eric Dumazet wrote:
>
> BTW, I was thinking of a mutex_yield() implementation, but could not
> cook it without hard thinking, maybe you already have some nice
> implementation ?
Why? Yield sets off alarm bells, since 99.9%, and possibly more, of its
uses are wrong.
> int mutex_yield(struct mutex *lock)
> {
> int ret = 0;
>
> if (mutex_needbreak(lock) || should_resched()) {
> mutex_unlock(lock);
> __cond_resched();
> mutex_lock(lock);
> ret = 1;
> }
> return ret;
> }
That reads like it should be called cond_resched_mutex(), except that
the should_resched() thing seems daft (but maybe it makes sense for
silly preemption modes like voluntary).
iirc we actually have something similar in -rt in order to implement the
lock-break for the rt-mutex based spinlocks, we set ->needbreak when a
higher priority task contends -- a policy for regular mutexes might be
'interesting' though.
As to your 'debug' helper that started this thread, doesn't
lockdep_assert_held() work for you?
^ permalink raw reply
* sit: Clean up DF code by copying from IPIP
From: Herbert Xu @ 2009-11-09 18:42 UTC (permalink / raw)
To: David S. Miller, netdev
In-Reply-To: <20091106203741.GA19736@gondor.apana.org.au>
Hi Dave:
This can go into net-next since it doesn't really alter the
behaviour in any significant way.
sit: Clean up DF code by copying from IPIP
This patch rearranges the SIT DF bit handling using the new IPIP
DF code. The only externally visible effect should be the case where
PMTU is enabled and the MTU is exactly 1280 bytes. In this case
the previous code would send packets out with DF off while the new
code would set the DF bit. This is inline with RFC 4213.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index dbd19a7..97739ee 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -559,6 +559,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
struct iphdr *tiph = &tunnel->parms.iph;
struct ipv6hdr *iph6 = ipv6_hdr(skb);
u8 tos = tunnel->parms.iph.tos;
+ __be16 df = tiph->frag_off;
struct rtable *rt; /* Route to the other host */
struct net_device *tdev; /* Device to other host */
struct iphdr *iph; /* Our new IP header */
@@ -648,25 +649,28 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
goto tx_error;
}
- if (tiph->frag_off)
+ if (df) {
mtu = dst_mtu(&rt->u.dst) - sizeof(struct iphdr);
- else
- mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
- if (mtu < 68) {
- stats->collisions++;
- ip_rt_put(rt);
- goto tx_error;
- }
- if (mtu < IPV6_MIN_MTU)
- mtu = IPV6_MIN_MTU;
- if (tunnel->parms.iph.daddr && skb_dst(skb))
- skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu);
+ if (mtu < 68) {
+ stats->collisions++;
+ ip_rt_put(rt);
+ goto tx_error;
+ }
- if (skb->len > mtu) {
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
- ip_rt_put(rt);
- goto tx_error;
+ if (mtu < IPV6_MIN_MTU) {
+ mtu = IPV6_MIN_MTU;
+ df = 0;
+ }
+
+ if (tunnel->parms.iph.daddr && skb_dst(skb))
+ skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu);
+
+ if (skb->len > mtu) {
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
+ ip_rt_put(rt);
+ goto tx_error;
+ }
}
if (tunnel->err_count > 0) {
@@ -714,11 +718,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
iph = ip_hdr(skb);
iph->version = 4;
iph->ihl = sizeof(struct iphdr)>>2;
- if (mtu > IPV6_MIN_MTU)
- iph->frag_off = tiph->frag_off;
- else
- iph->frag_off = 0;
-
+ iph->frag_off = df;
iph->protocol = IPPROTO_IPV6;
iph->tos = INET_ECN_encapsulate(tos, ipv6_get_dsfield(iph6));
iph->daddr = rt->rt_dst;
Thanks,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply related
* Re: [PATCH] xfrm: SAD entries do not expire correctly after suspend-resume
From: Yury Polyanskiy @ 2009-11-09 18:31 UTC (permalink / raw)
To: Herbert Xu; +Cc: netdev, davem, peterz, yoshfuji, tglx, mingo
In-Reply-To: <20091109153910.GA8039@gondor.apana.org.au>
[-- Attachment #1: Type: text/plain, Size: 1924 bytes --]
On Mon, 9 Nov 2009 10:39:10 -0500
Herbert Xu <herbert@gondor.apana.org.au> wrote:
> However, I have some reservations as to whether this is the ideal
> situation. Unless I'm mistaken, this patch may cause IPsec SAs
> to expire if the system clock was out of sync prior to IPsec startup
> and is subsequently resynced by ntpdate or similar.
>
> For example, it's quite common for clocks to be out-of-sync by
> 10 hours in Australia due to time zone issues with BIOS clocks.
> So potentially ntpdate could move the clock forward by 10 hours
> or more on bootup thus causing IPsec SAs to expire prematurely
> with this patch.
>
> This shouldn't really be a problem in itself except that there
> are some dodgy IPsec gateways out there that refuse to reestablish
> IPsec SAs if the interval between two successive connections is
> too small. This could render the SA inoperable for hours.
But why would it be inoperable for hours?
I think that the following will happen:
* racoon will recreate SAD entry in the larval state, wait 30s and drop
it (since dodgy-gw filtered out all keyexchange packets)
* The next time there is a connect() with a match in the SPD, racoon
will again try to recreate the SAD entry. If there dodgy-gw still
filters out, the larval SAD entry dies after 30s.
So the inoperability will only last as long as dodgy-gw filters
keyexchanges.
In any case, running ntpdate before racoon fixes the problem.
>
> So the upshot of all this is that we definitely want the effect
> of this patch for suspend/resume, but it would be great if we can
> avoid it for settimeofday(2).
I think the natural solution is to have CLOCK_BOOTBASED hrtimers. I.e.
something in the spirit of monotonic_to_bootbased() and getboottime().
I understand that doing +=total_sleep_time is against the core idea of
hires timers, but perhaps there is a nicer way.
Best,
Y
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* pull request: wireless-next-2.6 2009-11-09
From: John W. Linville @ 2009-11-09 18:30 UTC (permalink / raw)
To: davme-fT/PcQaiUtIeIZ0/mPfg9Q
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
Dave,
Another big batch of patches intended for 2.6.33... Among the usual
suspects are big updates for ath9k, iwlwifi, wl1271, mwl8k, and (especially)
rt2x00. Also included are some patches to move some old pre-802.11
drivers over to staging, and a number of mac80211 updates.
Please let me know if there are problems!
Thanks,
John
---
Individual patches are available here:
http://www.kernel.org/pub/linux/kernel/people/linville/wireless-next-2.6/
---
The following changes since commit 29906f6a427d2004a515ebbcdc7b28bae8f6c19c:
Patrick McHardy (1):
vlan: cleanup multiple unregistrations
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6.git master
Andrey Yurovsky (1):
libertas: remove internal buffers from GSPI driver
Bartlomiej Zolnierkiewicz (41):
rt2800usb: make Kconfig help entry more helpful
rt2800pci: make Kconfig help entry more helpful
rt2800usb: fix rt2800usb_rfcsr_read()
rt2800pci: fix crypto in TX frame
rt2800pci: fix comment about register access
rt2800pci: fix comment about IV/EIV fields
rt2x00: fix rt2x00usb_register_read() comment
rt2800usb: use rt2x00usb_register_multiwrite() to set key entries
rt2800usb: fix comments in rt2800usb.h
rt2x00: remove needless ifdefs from rt2x00leds.h
rt2800usb: add rt2800_register_[read,write]() wrappers
rt2800pci: add rt2800_register_[read,write]() wrappers
rt2800usb: add rt2800_register_multi[read,write]() wrappers
rt2800pci: add rt2800_register_multi[read,write]() wrappers
rt2800usb: add rt2800_regbusy_read() wrapper
rt2800pci: add rt2800_regbusy_read() wrapper
rt2800usb: add rt2800_bbp_[read,write]() wrappers
rt2800pci: add rt2800_bbp_[read,write]() wrappers
rt2800usb: add rt2800_rfcsr_[read,write]() wrappers
rt2800pci: add rt2800_rfcsr_[read,write]() wrappers
rt2800usb: add rt2800_rf_[read,write]() wrappers
rt2800pci: add rt2800_rf_[read,write]() wrappers
rt2800usb: add rt2800_mcu_request() wrapper
rt2800pci: add rt2800_mcu_request() wrapper
rt2x00: add driver private field to struct rt2x00_dev
rt2800usb: convert to use struct rt2800_ops methods
rt2800pci: convert to use struct rt2800_ops methods
rt2x00: fix rt2x00usb_register_multiwrite() arguments
rt2x00: fix rt2x00usb_regbusy_read() arguments
rt2x00: fix rt2x00pci_register_multi[read,write]() arguments
rt2800: add rt2800lib.h
rt2800usb: add RXINFO_DESC_SIZE definition
rt2800: fix duplication in header files
rt2800: fix comments in rt2800.h
rt2x00: add support for different chipset interfaces
rt2800: prepare for rt2800lib addition
rt2800: add rt2800lib (part one)
rt2800: add rt2800lib (part two)
rt2x00: move REGISTER_BUSY_* definitions to rt2x00.h
rt2800: add rt2800lib (part three)
rt2800: add rt2800lib (part four)
Ben M Cahill (5):
iwlwifi: remove unneeded locks from apm_stop() and stop_master()
iwlwifi: remove power-wasting calls to apm_ops.init()
iwlagn: invoke L0S workaround for 6000/1000 series
iwlagn: Clarify FH_TX interrupt
iwlagn: update write pointers for all tx queues after wakeup
Christian Lamparter (1):
p54: disable channels with incomplete calibration data sets
Greg Kroah-Hartman (4):
Staging: strip: add TODO file
Staging: arlan: add TODO file
Staging: wavelan: add TODO file
Staging: netwave: add TODO file
Gábor Stefanik (1):
b43: LP-PHY: Begin implementing calibration & software RFKILL support
Johannes Berg (19):
cfg80211/mac80211: use debugfs_remove_recursive
mac80211_hwsim: don't register CCK rates on 5ghz
mac80211: remove outdated comment
mac80211: split hardware scan by band
mac80211: fix radiotap header generation
mac80211: remove RX_FLAG_RADIOTAP
mac80211: introduce ieee80211_beacon_get_tim()
mac80211: deprecate qual value
mac80211: unconditionally set IEEE80211_TX_CTL_SEND_AFTER_DTIM
mac80211: also drop qos-nullfunc frames silently
mac80211: remove sent_ps_buffered
mac80211: remove bogus code
mac80211: make CALL_TXH a statement
mac80211: fix scan abort sanity checks
cfg80211: validate scan channels
cfg80211: remove dead variable
mac80211: make ieee80211_find_sta per virtual interface
mac80211: fix internal scan request
mac80211: async station powersave handling
John W. Linville (7):
strip: move driver to staging
arlan: move driver to staging
wavelan: move driver to staging
netwave: move driver to staging
wireless: remove WLAN_80211 and WLAN_PRE80211 from Kconfig
wl1271: depend on INET
mwl8k: use integral index instead of pointer for driver_data
Jouni Malinen (3):
cfg80211: Fix WEXT compat siwauth wpa and group cipher
mac80211_hwsim: Check idle state on TX
mac80211_hwsim: Send ACK frames on the hwsim0 interface
Juuso Oikarinen (6):
wl1271: Remove excess null-data template settings
wl1271: Increase TX power value
wl1271: Check result code of commands
wl1271: Add retry implementation for PSM entries
wl1271: Correct endianness-handling of command status
wl1271: Generalize command response reading
Kalle Valo (3):
mac80211: refactor dynamic power save check
mac80211: fix dynamic power save for devices with nullfunc support in hw
wl1251: enable power save
Keng-Yu Lin (1):
ath5k: add LED support for Acer Aspire One AO751h/AO531h
Larry Finger (4):
b43legacy: Fix DMA TX bounce buffer copying
b43: Remove deprecated 'qual' from returned RX status
b43legacy: Remove deprecated 'qual' from returned RX status
rtl8187: Remove deprecated 'qual' from returned RX status
Lennert Buytenhek (28):
mwl8k: fix GET_STAT firmware command packet layout
mwl8k: coding style cleanups
mwl8k: minor transmit quiescing rework
mwl8k: fix multicast address filter programming
mwl8k: use the mac80211-provided workqueue instead of creating our own
mwl8k: implement FIF_ALLMULTI
mwl8k: enforce FIF_BCN_PRBRESP_PROMISC when no STA interfaces are active
mwl8k: clear hardware MAC address if no STA interface configured
mwl8k: use cond_resched() when loading firmware blocks
mwl8k: clarify WME transmit queue 0/1 swizzling
mwl8k: report rate and other information for received frames
mwl8k: add support for enabling hardware sniffer mode
mwl8k: shorten receive/transmit state variable names
mwl8k: pci BAR mapping changes
mwl8k: change pci id table driver data to a structure pointer
mwl8k: spell out the names of firmware images in the pci driver data
mwl8k: handle loading AP firmware images
mwl8k: use pci_unmap_addr{,set}() to keep track of unmap addresses on rx
mwl8k: allow for different receive descriptor formats
mwl8k: set ->interface_modes from the driver data
mwl8k: rename mwl8k_cmd_get_hw_spec() to mwl8k_cmd_get_hw_spec_sta()
mwl8k: add the commands used for AP firmware initialisation
mwl8k: implement AP firmware antenna configuration
mwl8k: add AP firmware handling to ->configure_filter()
mwl8k: add AP firmware handling to ->start()
mwl8k: add AP firmware (mbss) handling to mwl8k_set_mac_addr()
mwl8k: implement AP firmware EDCA parameter configuration
mwl8k: add support for the 88w8366
Luciano Coelho (1):
wl1271: fix init loop timeout
Luis R. Rodriguez (21):
ath9k_hw: move mac name and rf name helpers to hw code
ath9k_hw: distinguish single-chip solutions on initial probe print
ath9k_hw: add AR9271 single chip name mapping
ath9k_hw: correct AR_PHY_SPECTRAL_SCAN register offset
ath9k_hw: remove bogus register write on ath9k_hw_9271_pa_cal()
ath9k_hw: modify the rf control register for ar9271 revision 1.0
ath9k_hw: update register initialization/reset values for ar9271
ath9k_hw: change the way we initialize the pll for ar9271
ath9k_hw: start documenting 802.11n RF anlong front ends
ath9k_hw: bail out early on ath9k_hw_init_rf()
ath9k_hw: simplify rf attach and rename to ath9k_hw_rf_alloc_ext_banks()
ath9k_hw: simplify ath9k_hw_rf_alloc_ext_banks()
ath9k_hw: rename ath9k_hw_rf_free() to ath9k_hw_rf_free_ext_banks()
ath9k_hw: make both analog channel change routines return int
ath9k_hw: use a callback for frequency change
ath9k_hw: order phy.c code and integrate spur mitigation
ath9k_hw: make spur mitigation a callback
ath9k_hw: remove unused modesIndex param from ath9k_hw_write_regs()
ath9k_hw: Fix and complete force bias for AR5416
ath9k_hw: make ath9k_phy_modify_rx_buffer() static
wl1271: use __dev_alloc_skb() on RX
Randy Dunlap (1):
wireless: airo_cs needs WEXT_SPY
Reinette Chatre (7):
iwlwifi: provide firmware version
iwlwifi: unmap memory before use
iwlwifi: move iwl_[un]init_drv to iwlagn
iwlwifi: split adding broadcast station from others
iwl3945: store station rate scale information in mac80211 station structure
iwlagn: move rate scale initialization to init function
iwlwifi: print warning when sending host command fails
Sujith (1):
mac80211: Fix IBSS merge
Wey-Yi Guy (3):
iwlwifi: coex API data structure
iwlwifi: add SM PS support for 6x50 series
iwlwifi: add wimax/wifi coexist support for 6x50 series
Zhu Yi (1):
mac80211: make align adjustment code support paged SKB
drivers/net/wireless/Kconfig | 140 +--
drivers/net/wireless/Makefile | 10 -
drivers/net/wireless/ath/Kconfig | 1 -
drivers/net/wireless/ath/ar9170/Kconfig | 2 +-
drivers/net/wireless/ath/ath5k/Kconfig | 2 +-
drivers/net/wireless/ath/ath5k/led.c | 2 +
drivers/net/wireless/ath/ath9k/Kconfig | 2 +-
drivers/net/wireless/ath/ath9k/ahb.c | 10 +-
drivers/net/wireless/ath/ath9k/calib.c | 2 +-
drivers/net/wireless/ath/ath9k/eeprom_4k.c | 4 +
drivers/net/wireless/ath/ath9k/hw.c | 669 ++-----
drivers/net/wireless/ath/ath9k/hw.h | 22 +-
drivers/net/wireless/ath/ath9k/initvals.h | 29 +-
drivers/net/wireless/ath/ath9k/main.c | 58 -
drivers/net/wireless/ath/ath9k/pci.c | 10 +-
drivers/net/wireless/ath/ath9k/phy.c | 1100 +++++++++---
drivers/net/wireless/ath/ath9k/phy.h | 40 +-
drivers/net/wireless/ath/ath9k/recv.c | 3 +-
drivers/net/wireless/ath/ath9k/reg.h | 3 +
drivers/net/wireless/ath/ath9k/xmit.c | 3 +-
drivers/net/wireless/b43/Kconfig | 2 +-
drivers/net/wireless/b43/b43.h | 2 -
drivers/net/wireless/b43/main.c | 2 +-
drivers/net/wireless/b43/phy_lp.c | 783 +++++++--
drivers/net/wireless/b43/phy_lp.h | 11 +-
drivers/net/wireless/b43/xmit.c | 1 -
drivers/net/wireless/b43legacy/Kconfig | 2 +-
drivers/net/wireless/b43legacy/b43legacy.h | 2 -
drivers/net/wireless/b43legacy/dma.c | 17 +-
drivers/net/wireless/b43legacy/main.c | 2 +-
drivers/net/wireless/b43legacy/xmit.c | 1 -
drivers/net/wireless/hostap/Kconfig | 1 -
drivers/net/wireless/ipw2x00/Kconfig | 6 +-
drivers/net/wireless/iwlwifi/Kconfig | 2 +-
drivers/net/wireless/iwlwifi/iwl-1000.c | 4 +-
drivers/net/wireless/iwlwifi/iwl-3945-rs.c | 102 +-
drivers/net/wireless/iwlwifi/iwl-3945.h | 35 +-
drivers/net/wireless/iwlwifi/iwl-5000.c | 12 +-
drivers/net/wireless/iwlwifi/iwl-6000.c | 25 +-
drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 13 +-
drivers/net/wireless/iwlwifi/iwl-agn.c | 143 ++-
drivers/net/wireless/iwlwifi/iwl-calib.c | 1 +
drivers/net/wireless/iwlwifi/iwl-commands.h | 161 ++-
drivers/net/wireless/iwlwifi/iwl-core.c | 236 ++--
drivers/net/wireless/iwlwifi/iwl-core.h | 7 +-
drivers/net/wireless/iwlwifi/iwl-dev.h | 1 +
drivers/net/wireless/iwlwifi/iwl-eeprom.c | 11 +
drivers/net/wireless/iwlwifi/iwl-hcmd.c | 2 +
drivers/net/wireless/iwlwifi/iwl-scan.c | 1 +
drivers/net/wireless/iwlwifi/iwl-sta.c | 69 +-
drivers/net/wireless/iwlwifi/iwl-sta.h | 1 +
drivers/net/wireless/iwlwifi/iwl-tx.c | 13 +-
drivers/net/wireless/iwlwifi/iwl3945-base.c | 15 +-
drivers/net/wireless/iwmc3200wifi/Kconfig | 2 +-
drivers/net/wireless/libertas/Kconfig | 2 +-
drivers/net/wireless/libertas/if_spi.c | 136 +--
drivers/net/wireless/mac80211_hwsim.c | 67 +-
drivers/net/wireless/mwl8k.c | 1258 +++++++++----
drivers/net/wireless/orinoco/Kconfig | 2 +-
drivers/net/wireless/p54/Kconfig | 2 +-
drivers/net/wireless/p54/eeprom.c | 31 +-
drivers/net/wireless/rt2x00/Kconfig | 18 +-
drivers/net/wireless/rt2x00/Makefile | 1 +
drivers/net/wireless/rt2x00/rt2800.h | 1816 +++++++++++++++++++
drivers/net/wireless/rt2x00/rt2800lib.c | 1817 +++++++++++++++++++
drivers/net/wireless/rt2x00/rt2800lib.h | 134 ++
drivers/net/wireless/rt2x00/rt2800pci.c | 1908 ++------------------
drivers/net/wireless/rt2x00/rt2800pci.h | 1780 ------------------
drivers/net/wireless/rt2x00/rt2800usb.c | 1828 +------------------
drivers/net/wireless/rt2x00/rt2800usb.h | 1818 +-------------------
drivers/net/wireless/rt2x00/rt2x00.h | 43 +
drivers/net/wireless/rt2x00/rt2x00leds.h | 4 -
drivers/net/wireless/rt2x00/rt2x00pci.h | 24 +-
drivers/net/wireless/rt2x00/rt2x00usb.c | 2 +-
drivers/net/wireless/rt2x00/rt2x00usb.h | 17 +-
drivers/net/wireless/rtl818x/rtl8187.h | 1 -
drivers/net/wireless/rtl818x/rtl8187_dev.c | 13 +-
drivers/net/wireless/wl12xx/Kconfig | 3 +-
drivers/net/wireless/wl12xx/wl1251_main.c | 3 +-
drivers/net/wireless/wl12xx/wl1271.h | 3 +
drivers/net/wireless/wl12xx/wl1271_acx.c | 2 +-
drivers/net/wireless/wl12xx/wl1271_boot.c | 5 +-
drivers/net/wireless/wl12xx/wl1271_cmd.c | 99 +-
drivers/net/wireless/wl12xx/wl1271_cmd.h | 3 +-
drivers/net/wireless/wl12xx/wl1271_conf.h | 8 +
drivers/net/wireless/wl12xx/wl1271_event.c | 53 +
drivers/net/wireless/wl12xx/wl1271_event.h | 7 +
drivers/net/wireless/wl12xx/wl1271_init.c | 7 +-
drivers/net/wireless/wl12xx/wl1271_main.c | 19 +-
drivers/net/wireless/wl12xx/wl1271_rx.c | 2 +-
drivers/net/wireless/zd1211rw/Kconfig | 2 +-
drivers/staging/Kconfig | 8 +
drivers/staging/Makefile | 5 +
drivers/staging/arlan/Kconfig | 15 +
drivers/staging/arlan/Makefile | 3 +
drivers/staging/arlan/TODO | 7 +
.../{net/wireless => staging/arlan}/arlan-main.c | 0
.../{net/wireless => staging/arlan}/arlan-proc.c | 0
drivers/{net/wireless => staging/arlan}/arlan.h | 0
drivers/staging/netwave/Kconfig | 11 +
drivers/staging/netwave/Makefile | 1 +
drivers/staging/netwave/TODO | 7 +
.../{net/wireless => staging/netwave}/netwave_cs.c | 0
drivers/staging/strip/Kconfig | 22 +
drivers/staging/strip/Makefile | 1 +
drivers/staging/strip/TODO | 7 +
drivers/{net/wireless => staging/strip}/strip.c | 0
drivers/staging/wavelan/Kconfig | 38 +
drivers/staging/wavelan/Makefile | 2 +
drivers/staging/wavelan/TODO | 7 +
drivers/{net/wireless => staging/wavelan}/i82586.h | 0
drivers/{net/wireless => staging/wavelan}/i82593.h | 0
.../{net/wireless => staging/wavelan}/wavelan.c | 0
.../{net/wireless => staging/wavelan}/wavelan.h | 0
.../{net/wireless => staging/wavelan}/wavelan.p.h | 0
.../{net/wireless => staging/wavelan}/wavelan_cs.c | 0
.../{net/wireless => staging/wavelan}/wavelan_cs.h | 0
.../wireless => staging/wavelan}/wavelan_cs.p.h | 0
include/linux/ieee80211.h | 12 +-
include/net/ieee80211_radiotap.h | 2 +-
include/net/mac80211.h | 116 +-
net/mac80211/cfg.c | 7 -
net/mac80211/debugfs.c | 73 +-
net/mac80211/debugfs.h | 2 -
net/mac80211/debugfs_key.c | 44 +-
net/mac80211/debugfs_netdev.c | 174 +--
net/mac80211/debugfs_sta.c | 38 +-
net/mac80211/ibss.c | 4 +
net/mac80211/ieee80211_i.h | 123 +--
net/mac80211/iface.c | 8 +-
net/mac80211/key.h | 12 -
net/mac80211/main.c | 11 +-
net/mac80211/rate.c | 7 +-
net/mac80211/rx.c | 173 +--
net/mac80211/scan.c | 119 +-
net/mac80211/sta_info.c | 136 ++-
net/mac80211/sta_info.h | 47 +-
net/mac80211/tx.c | 99 +-
net/mac80211/util.c | 8 +-
net/wireless/core.c | 3 +-
net/wireless/core.h | 11 -
net/wireless/debugfs.c | 15 +-
net/wireless/debugfs.h | 3 -
net/wireless/mlme.c | 2 -
net/wireless/nl80211.c | 34 +-
net/wireless/scan.c | 6 +
net/wireless/wext-compat.c | 6 +-
147 files changed, 8305 insertions(+), 9862 deletions(-)
create mode 100644 drivers/net/wireless/rt2x00/rt2800.h
create mode 100644 drivers/net/wireless/rt2x00/rt2800lib.c
create mode 100644 drivers/net/wireless/rt2x00/rt2800lib.h
create mode 100644 drivers/staging/arlan/Kconfig
create mode 100644 drivers/staging/arlan/Makefile
create mode 100644 drivers/staging/arlan/TODO
rename drivers/{net/wireless => staging/arlan}/arlan-main.c (100%)
rename drivers/{net/wireless => staging/arlan}/arlan-proc.c (100%)
rename drivers/{net/wireless => staging/arlan}/arlan.h (100%)
create mode 100644 drivers/staging/netwave/Kconfig
create mode 100644 drivers/staging/netwave/Makefile
create mode 100644 drivers/staging/netwave/TODO
rename drivers/{net/wireless => staging/netwave}/netwave_cs.c (100%)
create mode 100644 drivers/staging/strip/Kconfig
create mode 100644 drivers/staging/strip/Makefile
create mode 100644 drivers/staging/strip/TODO
rename drivers/{net/wireless => staging/strip}/strip.c (100%)
create mode 100644 drivers/staging/wavelan/Kconfig
create mode 100644 drivers/staging/wavelan/Makefile
create mode 100644 drivers/staging/wavelan/TODO
rename drivers/{net/wireless => staging/wavelan}/i82586.h (100%)
rename drivers/{net/wireless => staging/wavelan}/i82593.h (100%)
rename drivers/{net/wireless => staging/wavelan}/wavelan.c (100%)
rename drivers/{net/wireless => staging/wavelan}/wavelan.h (100%)
rename drivers/{net/wireless => staging/wavelan}/wavelan.p.h (100%)
rename drivers/{net/wireless => staging/wavelan}/wavelan_cs.c (100%)
rename drivers/{net/wireless => staging/wavelan}/wavelan_cs.h (100%)
rename drivers/{net/wireless => staging/wavelan}/wavelan_cs.p.h (100%)
Omnibus patch available here:
http://www.kernel.org/pub/linux/kernel/people/linville/wireless-next-2.6-2009-11-09.patch.bz2
--
John W. Linville Someday the world will need a hero, and you
linville-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org might be all we have. Be ready.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RFC] netlink: add socket destruction notification
From: Johannes Berg @ 2009-11-09 17:46 UTC (permalink / raw)
To: Patrick McHardy; +Cc: netdev, Jouni Malinen, Thomas Graf
In-Reply-To: <4AF849F6.4050308@trash.net>
[-- Attachment #1: Type: text/plain, Size: 687 bytes --]
On Mon, 2009-11-09 at 17:57 +0100, Patrick McHardy wrote:
> > Oh? So on which sockets can I rely on it being used? After sending at
> > least one unicast message into the kernel? This seems to depend on pid
> > being assigned -- when is that?
>
> All unicast sockets that have either manually or automatically
> bound. Automatic binding happens when sending the first message
> or when calling connect().
>
> Before that, your code can't know of the sockets existance, so
> I guess this should be fine.
Ah, yes, of course... that's part of the boilerplate code to use generic
netlink so I always forget about what exactly is going on there :)
Thanks!
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* [PATCH] ipv6: Allow inet6_dump_addr() to handle more than 64 addresses
From: Eric Dumazet @ 2009-11-09 17:40 UTC (permalink / raw)
To: David S. Miller; +Cc: Linux Netdev List
Apparently, inet6_dump_addr() is not able to handle more than
64 ipv6 addresses per device. We must break from inner loops
in case skb is full, or else cursor is put at the end of list.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
net/ipv6/addrconf.c | 6 ++++++
1 files changed, 6 insertions(+)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 024bba3..1b072fe 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -3519,6 +3519,8 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
cb->nlh->nlmsg_seq,
RTM_NEWADDR,
NLM_F_MULTI);
+ if (err <= 0)
+ break;
}
break;
case MULTICAST_ADDR:
@@ -3532,6 +3534,8 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
cb->nlh->nlmsg_seq,
RTM_GETMULTICAST,
NLM_F_MULTI);
+ if (err <= 0)
+ break;
}
break;
case ANYCAST_ADDR:
@@ -3545,6 +3549,8 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
cb->nlh->nlmsg_seq,
RTM_GETANYCAST,
NLM_F_MULTI);
+ if (err <= 0)
+ break;
}
break;
default:
^ permalink raw reply related
* Re: RFC: ethtool support for n-tuple filter programming
From: Patrick McHardy @ 2009-11-09 17:36 UTC (permalink / raw)
To: Caitlin Bestler
Cc: Rick Jones, Bill Fink, Peter P Waskiewicz Jr,
netdev@vger.kernel.org
In-Reply-To: <469958e00911090923s7688bb78i18c7102195be6f9b@mail.gmail.com>
Caitlin Bestler wrote:
> On Sat, Nov 7, 2009 at 3:28 PM, Rick Jones <rick.jones2@hp.com> wrote:
>> At the risk of typing words into someone's keyboard, I interpreted it as
>> suggesting using the filtering language of netfilter or something similar,
>> not necessarily netfilter itself?
>>
>
> Correct, a netfilter-friendly interface to the driver could be invoked by
> lower-overhead entities that netfilter and the driver would not care.
>
> However the real goal would be to still use netfilter, which would become
> a low-overhead entity if it could delegate 90% of the rules it enforced to
> smart hardware.
That's not possible in a compatible fashion with ip_tables because of
counters, logging, rules affecting traffic from multiple interfaces,
targets not supported in hardware (which I presume will simply be
"DROP") etc.
Counters are actually the worst feature standing in the way of this,
but even without them, you could usually only offload the first n
dropping rules that don't use any features not supported in hardware
and only affect the specific interface. Any "ACCEPT" rule is most
likely followed by further drop rules, so packets actually need to
hit those rules in software to exit table processing.
It gets even worse if you consider ingress TC actions directing the
packets to different interfaces or mangling them.
> The fundamental suggestion is to start with a filter specification that is
> clearly too rich for any Ethernet device, and let the Ethernet devices
> decide how quickly they want to catch up. As opposed to standardizing
> how smart a smart Ethernet device is and potentially leaving some hardware
> capabilities made inaccessible.
>
> I'll point out that once you assume an Ethernet Device is capable of doing
> TCP/UDP checksum offload and LSO/LRO then clearly you have recognized
> that it is an L4 aware device. Designing its filtering rules as though it were
> an L2-only device does not allow it to take advantage of the L4 parsing that
> many/most Ethernet NICs already do.
^ permalink raw reply
* Re: RFC: ethtool support for n-tuple filter programming
From: Caitlin Bestler @ 2009-11-09 17:23 UTC (permalink / raw)
To: Rick Jones; +Cc: Bill Fink, Peter P Waskiewicz Jr, netdev@vger.kernel.org
In-Reply-To: <4AF6029F.6020403@hp.com>
On Sat, Nov 7, 2009 at 3:28 PM, Rick Jones <rick.jones2@hp.com> wrote:
>
> At the risk of typing words into someone's keyboard, I interpreted it as
> suggesting using the filtering language of netfilter or something similar,
> not necessarily netfilter itself?
>
Correct, a netfilter-friendly interface to the driver could be invoked by
lower-overhead entities that netfilter and the driver would not care.
However the real goal would be to still use netfilter, which would become
a low-overhead entity if it could delegate 90% of the rules it enforced to
smart hardware.
The fundamental suggestion is to start with a filter specification that is
clearly too rich for any Ethernet device, and let the Ethernet devices
decide how quickly they want to catch up. As opposed to standardizing
how smart a smart Ethernet device is and potentially leaving some hardware
capabilities made inaccessible.
I'll point out that once you assume an Ethernet Device is capable of doing
TCP/UDP checksum offload and LSO/LRO then clearly you have recognized
that it is an L4 aware device. Designing its filtering rules as though it were
an L2-only device does not allow it to take advantage of the L4 parsing that
many/most Ethernet NICs already do.
^ permalink raw reply
* [PATCHv9 3/3] vhost_net: a kernel-level virtio server
From: Michael S. Tsirkin @ 2009-11-09 17:22 UTC (permalink / raw)
To: netdev, virtualization, kvm, linux-kernel, mingo, linux-mm, akpm
In-Reply-To: <cover.1257786516.git.mst@redhat.com>
What it is: vhost net is a character device that can be used to reduce
the number of system calls involved in virtio networking.
Existing virtio net code is used in the guest without modification.
There's similarity with vringfd, with some differences and reduced scope
- uses eventfd for signalling
- structures can be moved around in memory at any time (good for
migration, bug work-arounds in userspace)
- write logging is supported (good for migration)
- support memory table and not just an offset (needed for kvm)
common virtio related code has been put in a separate file vhost.c and
can be made into a separate module if/when more backends appear. I used
Rusty's lguest.c as the source for developing this part : this supplied
me with witty comments I wouldn't be able to write myself.
What it is not: vhost net is not a bus, and not a generic new system
call. No assumptions are made on how guest performs hypercalls.
Userspace hypervisors are supported as well as kvm.
How it works: Basically, we connect virtio frontend (configured by
userspace) to a backend. The backend could be a network device, or a tap
device. Backend is also configured by userspace, including vlan/mac
etc.
Status: This works for me, and I haven't see any crashes.
Compared to userspace, people reported improved latency (as I save up to
4 system calls per packet), as well as better bandwidth and CPU
utilization.
Features that I plan to look at in the future:
- mergeable buffers
- zero copy
- scalability tuning: figure out the best threading model to use
Note on RCU usage (this is also documented in vhost.h, near
private_pointer which is the value protected by this variant of RCU):
what is happening is that the rcu_dereference() is being used in a
workqueue item. The role of rcu_read_lock() is taken on by the start of
execution of the workqueue item, of rcu_read_unlock() by the end of
execution of the workqueue item, and of synchronize_rcu() by
flush_workqueue()/flush_work(). In the future we might need to apply
some gcc attribute or sparse annotation to the function passed to
INIT_WORK(). Paul's ack below is for this RCU usage.
Acked-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
MAINTAINERS | 9 +
arch/x86/kvm/Kconfig | 1 +
drivers/Makefile | 1 +
drivers/vhost/Kconfig | 11 +
drivers/vhost/Makefile | 2 +
drivers/vhost/net.c | 648 +++++++++++++++++++++++++++++
drivers/vhost/vhost.c | 965 ++++++++++++++++++++++++++++++++++++++++++++
drivers/vhost/vhost.h | 159 ++++++++
include/linux/Kbuild | 1 +
include/linux/miscdevice.h | 1 +
include/linux/vhost.h | 130 ++++++
11 files changed, 1928 insertions(+), 0 deletions(-)
create mode 100644 drivers/vhost/Kconfig
create mode 100644 drivers/vhost/Makefile
create mode 100644 drivers/vhost/net.c
create mode 100644 drivers/vhost/vhost.c
create mode 100644 drivers/vhost/vhost.h
create mode 100644 include/linux/vhost.h
diff --git a/MAINTAINERS b/MAINTAINERS
index a1a2ace..7d4bfa2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5636,6 +5636,15 @@ S: Maintained
F: Documentation/filesystems/vfat.txt
F: fs/fat/
+VIRTIO HOST (VHOST)
+M: "Michael S. Tsirkin" <mst@redhat.com>
+L: kvm@vger.kernel.org
+L: virtualization@lists.osdl.org
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/vhost/
+F: include/linux/vhost.h
+
VIA RHINE NETWORK DRIVER
M: Roger Luethi <rl@hellgate.ch>
S: Maintained
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index b84e571..94f44d9 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -64,6 +64,7 @@ config KVM_AMD
# OK, it's a little counter-intuitive to do this, but it puts it neatly under
# the virtualization menu.
+source drivers/vhost/Kconfig
source drivers/lguest/Kconfig
source drivers/virtio/Kconfig
diff --git a/drivers/Makefile b/drivers/Makefile
index 6ee53c7..81e3659 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -106,6 +106,7 @@ obj-$(CONFIG_HID) += hid/
obj-$(CONFIG_PPC_PS3) += ps3/
obj-$(CONFIG_OF) += of/
obj-$(CONFIG_SSB) += ssb/
+obj-$(CONFIG_VHOST_NET) += vhost/
obj-$(CONFIG_VIRTIO) += virtio/
obj-$(CONFIG_VLYNQ) += vlynq/
obj-$(CONFIG_STAGING) += staging/
diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig
new file mode 100644
index 0000000..9f409f4
--- /dev/null
+++ b/drivers/vhost/Kconfig
@@ -0,0 +1,11 @@
+config VHOST_NET
+ tristate "Host kernel accelerator for virtio net (EXPERIMENTAL)"
+ depends on NET && EVENTFD && EXPERIMENTAL
+ ---help---
+ This kernel module can be loaded in host kernel to accelerate
+ guest networking with virtio_net. Not to be confused with virtio_net
+ module itself which needs to be loaded in guest kernel.
+
+ To compile this driver as a module, choose M here: the module will
+ be called vhost_net.
+
diff --git a/drivers/vhost/Makefile b/drivers/vhost/Makefile
new file mode 100644
index 0000000..72dd020
--- /dev/null
+++ b/drivers/vhost/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_VHOST_NET) += vhost_net.o
+vhost_net-y := vhost.o net.o
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
new file mode 100644
index 0000000..22d5fef
--- /dev/null
+++ b/drivers/vhost/net.c
@@ -0,0 +1,648 @@
+/* Copyright (C) 2009 Red Hat, Inc.
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ *
+ * virtio-net server in host kernel.
+ */
+
+#include <linux/compat.h>
+#include <linux/eventfd.h>
+#include <linux/vhost.h>
+#include <linux/virtio_net.h>
+#include <linux/mmu_context.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/rcupdate.h>
+#include <linux/file.h>
+
+#include <linux/net.h>
+#include <linux/if_packet.h>
+#include <linux/if_arp.h>
+#include <linux/if_tun.h>
+
+#include <net/sock.h>
+
+#include "vhost.h"
+
+/* Max number of bytes transferred before requeueing the job.
+ * Using this limit prevents one virtqueue from starving others. */
+#define VHOST_NET_WEIGHT 0x80000
+
+enum {
+ VHOST_NET_VQ_RX = 0,
+ VHOST_NET_VQ_TX = 1,
+ VHOST_NET_VQ_MAX = 2,
+};
+
+enum vhost_net_poll_state {
+ VHOST_NET_POLL_DISABLED = 0,
+ VHOST_NET_POLL_STARTED = 1,
+ VHOST_NET_POLL_STOPPED = 2,
+};
+
+struct vhost_net {
+ struct vhost_dev dev;
+ struct vhost_virtqueue vqs[VHOST_NET_VQ_MAX];
+ struct vhost_poll poll[VHOST_NET_VQ_MAX];
+ /* Tells us whether we are polling a socket for TX.
+ * We only do this when socket buffer fills up.
+ * Protected by tx vq lock. */
+ enum vhost_net_poll_state tx_poll_state;
+};
+
+/* Pop first len bytes from iovec. Return number of segments used. */
+static int move_iovec_hdr(struct iovec *from, struct iovec *to,
+ size_t len, int iov_count)
+{
+ int seg = 0;
+ size_t size;
+ while (len && seg < iov_count) {
+ size = min(from->iov_len, len);
+ to->iov_base = from->iov_base;
+ to->iov_len = size;
+ from->iov_len -= size;
+ from->iov_base += size;
+ len -= size;
+ ++from;
+ ++to;
+ ++seg;
+ }
+ return seg;
+}
+
+/* Caller must have TX VQ lock */
+static void tx_poll_stop(struct vhost_net *net)
+{
+ if (likely(net->tx_poll_state != VHOST_NET_POLL_STARTED))
+ return;
+ vhost_poll_stop(net->poll + VHOST_NET_VQ_TX);
+ net->tx_poll_state = VHOST_NET_POLL_STOPPED;
+}
+
+/* Caller must have TX VQ lock */
+static void tx_poll_start(struct vhost_net *net, struct socket *sock)
+{
+ if (unlikely(net->tx_poll_state != VHOST_NET_POLL_STOPPED))
+ return;
+ vhost_poll_start(net->poll + VHOST_NET_VQ_TX, sock->file);
+ net->tx_poll_state = VHOST_NET_POLL_STARTED;
+}
+
+/* Expects to be always run from workqueue - which acts as
+ * read-size critical section for our kind of RCU. */
+static void handle_tx(struct vhost_net *net)
+{
+ struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX];
+ unsigned head, out, in, s;
+ struct msghdr msg = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_iov = vq->iov,
+ .msg_flags = MSG_DONTWAIT,
+ };
+ size_t len, total_len = 0;
+ int err, wmem;
+ size_t hdr_size;
+ struct socket *sock = rcu_dereference(vq->private_data);
+ if (!sock)
+ return;
+
+ wmem = atomic_read(&sock->sk->sk_wmem_alloc);
+ if (wmem >= sock->sk->sk_sndbuf)
+ return;
+
+ use_mm(net->dev.mm);
+ mutex_lock(&vq->mutex);
+ vhost_disable_notify(vq);
+
+ if (wmem < sock->sk->sk_sndbuf * 2)
+ tx_poll_stop(net);
+ hdr_size = vq->hdr_size;
+
+ for (;;) {
+ head = vhost_get_vq_desc(&net->dev, vq, vq->iov,
+ ARRAY_SIZE(vq->iov),
+ &out, &in,
+ NULL, NULL);
+ /* Nothing new? Wait for eventfd to tell us they refilled. */
+ if (head == vq->num) {
+ wmem = atomic_read(&sock->sk->sk_wmem_alloc);
+ if (wmem >= sock->sk->sk_sndbuf * 3 / 4) {
+ tx_poll_start(net, sock);
+ set_bit(SOCK_ASYNC_NOSPACE, &sock->flags);
+ break;
+ }
+ if (unlikely(vhost_enable_notify(vq))) {
+ vhost_disable_notify(vq);
+ continue;
+ }
+ break;
+ }
+ if (in) {
+ vq_err(vq, "Unexpected descriptor format for TX: "
+ "out %d, int %d\n", out, in);
+ break;
+ }
+ /* Skip header. TODO: support TSO. */
+ s = move_iovec_hdr(vq->iov, vq->hdr, hdr_size, out);
+ msg.msg_iovlen = out;
+ len = iov_length(vq->iov, out);
+ /* Sanity check */
+ if (!len) {
+ vq_err(vq, "Unexpected header len for TX: "
+ "%zd expected %zd\n",
+ iov_length(vq->hdr, s), hdr_size);
+ break;
+ }
+ /* TODO: Check specific error and bomb out unless ENOBUFS? */
+ err = sock->ops->sendmsg(NULL, sock, &msg, len);
+ if (unlikely(err < 0)) {
+ vhost_discard_vq_desc(vq);
+ tx_poll_start(net, sock);
+ break;
+ }
+ if (err != len)
+ pr_err("Truncated TX packet: "
+ " len %d != %zd\n", err, len);
+ vhost_add_used_and_signal(&net->dev, vq, head, 0);
+ total_len += len;
+ if (unlikely(total_len >= VHOST_NET_WEIGHT)) {
+ vhost_poll_queue(&vq->poll);
+ break;
+ }
+ }
+
+ mutex_unlock(&vq->mutex);
+ unuse_mm(net->dev.mm);
+}
+
+/* Expects to be always run from workqueue - which acts as
+ * read-size critical section for our kind of RCU. */
+static void handle_rx(struct vhost_net *net)
+{
+ struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
+ unsigned head, out, in, log, s;
+ struct vhost_log *vq_log;
+ struct msghdr msg = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_control = NULL, /* FIXME: get and handle RX aux data. */
+ .msg_controllen = 0,
+ .msg_iov = vq->iov,
+ .msg_flags = MSG_DONTWAIT,
+ };
+
+ struct virtio_net_hdr hdr = {
+ .flags = 0,
+ .gso_type = VIRTIO_NET_HDR_GSO_NONE
+ };
+
+ size_t len, total_len = 0;
+ int err;
+ size_t hdr_size;
+ struct socket *sock = rcu_dereference(vq->private_data);
+ if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
+ return;
+
+ use_mm(net->dev.mm);
+ mutex_lock(&vq->mutex);
+ vhost_disable_notify(vq);
+ hdr_size = vq->hdr_size;
+
+ vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
+ vq->log : NULL;
+
+ for (;;) {
+ head = vhost_get_vq_desc(&net->dev, vq, vq->iov,
+ ARRAY_SIZE(vq->iov),
+ &out, &in,
+ vq_log, &log);
+ /* OK, now we need to know about added descriptors. */
+ if (head == vq->num) {
+ if (unlikely(vhost_enable_notify(vq))) {
+ /* They have slipped one in as we were
+ * doing that: check again. */
+ vhost_disable_notify(vq);
+ continue;
+ }
+ /* Nothing new? Wait for eventfd to tell us
+ * they refilled. */
+ break;
+ }
+ /* We don't need to be notified again. */
+ if (out) {
+ vq_err(vq, "Unexpected descriptor format for RX: "
+ "out %d, int %d\n",
+ out, in);
+ break;
+ }
+ /* Skip header. TODO: support TSO/mergeable rx buffers. */
+ s = move_iovec_hdr(vq->iov, vq->hdr, hdr_size, in);
+ msg.msg_iovlen = in;
+ len = iov_length(vq->iov, in);
+ /* Sanity check */
+ if (!len) {
+ vq_err(vq, "Unexpected header len for RX: "
+ "%zd expected %zd\n",
+ iov_length(vq->hdr, s), hdr_size);
+ break;
+ }
+ err = sock->ops->recvmsg(NULL, sock, &msg,
+ len, MSG_DONTWAIT | MSG_TRUNC);
+ /* TODO: Check specific error and bomb out unless EAGAIN? */
+ if (err < 0) {
+ vhost_discard_vq_desc(vq);
+ break;
+ }
+ /* TODO: Should check and handle checksum. */
+ if (err > len) {
+ pr_err("Discarded truncated rx packet: "
+ " len %d > %zd\n", err, len);
+ vhost_discard_vq_desc(vq);
+ continue;
+ }
+ len = err;
+ err = memcpy_toiovec(vq->hdr, (unsigned char *)&hdr, hdr_size);
+ if (err) {
+ vq_err(vq, "Unable to write vnet_hdr at addr %p: %d\n",
+ vq->iov->iov_base, err);
+ break;
+ }
+ len += hdr_size;
+ vhost_add_used_and_signal(&net->dev, vq, head, len);
+ if (unlikely(vq_log))
+ vhost_log_write(vq, vq_log, log, len);
+ total_len += len;
+ if (unlikely(total_len >= VHOST_NET_WEIGHT)) {
+ vhost_poll_queue(&vq->poll);
+ break;
+ }
+ }
+
+ mutex_unlock(&vq->mutex);
+ unuse_mm(net->dev.mm);
+}
+
+static void handle_tx_kick(struct work_struct *work)
+{
+ struct vhost_virtqueue *vq;
+ struct vhost_net *net;
+ vq = container_of(work, struct vhost_virtqueue, poll.work);
+ net = container_of(vq->dev, struct vhost_net, dev);
+ handle_tx(net);
+}
+
+static void handle_rx_kick(struct work_struct *work)
+{
+ struct vhost_virtqueue *vq;
+ struct vhost_net *net;
+ vq = container_of(work, struct vhost_virtqueue, poll.work);
+ net = container_of(vq->dev, struct vhost_net, dev);
+ handle_rx(net);
+}
+
+static void handle_tx_net(struct work_struct *work)
+{
+ struct vhost_net *net;
+ net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_TX].work);
+ handle_tx(net);
+}
+
+static void handle_rx_net(struct work_struct *work)
+{
+ struct vhost_net *net;
+ net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_RX].work);
+ handle_rx(net);
+}
+
+static int vhost_net_open(struct inode *inode, struct file *f)
+{
+ struct vhost_net *n = kmalloc(sizeof *n, GFP_KERNEL);
+ int r;
+ if (!n)
+ return -ENOMEM;
+ f->private_data = n;
+ n->vqs[VHOST_NET_VQ_TX].handle_kick = handle_tx_kick;
+ n->vqs[VHOST_NET_VQ_RX].handle_kick = handle_rx_kick;
+ r = vhost_dev_init(&n->dev, n->vqs, VHOST_NET_VQ_MAX);
+ if (r < 0) {
+ kfree(n);
+ return r;
+ }
+
+ vhost_poll_init(n->poll + VHOST_NET_VQ_TX, handle_tx_net, POLLOUT);
+ vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, POLLIN);
+ n->tx_poll_state = VHOST_NET_POLL_DISABLED;
+ return 0;
+}
+
+static void vhost_net_disable_vq(struct vhost_net *n,
+ struct vhost_virtqueue *vq)
+{
+ if (!vq->private_data)
+ return;
+ if (vq == n->vqs + VHOST_NET_VQ_TX) {
+ tx_poll_stop(n);
+ n->tx_poll_state = VHOST_NET_POLL_DISABLED;
+ } else
+ vhost_poll_stop(n->poll + VHOST_NET_VQ_RX);
+}
+
+static void vhost_net_enable_vq(struct vhost_net *n,
+ struct vhost_virtqueue *vq)
+{
+ struct socket *sock = vq->private_data;
+ if (!sock)
+ return;
+ if (vq == n->vqs + VHOST_NET_VQ_TX) {
+ n->tx_poll_state = VHOST_NET_POLL_STOPPED;
+ tx_poll_start(n, sock);
+ } else
+ vhost_poll_start(n->poll + VHOST_NET_VQ_RX, sock->file);
+}
+
+static struct socket *vhost_net_stop_vq(struct vhost_net *n,
+ struct vhost_virtqueue *vq)
+{
+ struct socket *sock;
+
+ mutex_lock(&vq->mutex);
+ sock = vq->private_data;
+ vhost_net_disable_vq(n, vq);
+ rcu_assign_pointer(vq->private_data, NULL);
+ mutex_unlock(&vq->mutex);
+ return sock;
+}
+
+static void vhost_net_stop(struct vhost_net *n, struct socket **tx_sock,
+ struct socket **rx_sock)
+{
+ *tx_sock = vhost_net_stop_vq(n, n->vqs + VHOST_NET_VQ_TX);
+ *rx_sock = vhost_net_stop_vq(n, n->vqs + VHOST_NET_VQ_RX);
+}
+
+static void vhost_net_flush_vq(struct vhost_net *n, int index)
+{
+ vhost_poll_flush(n->poll + index);
+ vhost_poll_flush(&n->dev.vqs[index].poll);
+}
+
+static void vhost_net_flush(struct vhost_net *n)
+{
+ vhost_net_flush_vq(n, VHOST_NET_VQ_TX);
+ vhost_net_flush_vq(n, VHOST_NET_VQ_RX);
+}
+
+static int vhost_net_release(struct inode *inode, struct file *f)
+{
+ struct vhost_net *n = f->private_data;
+ struct socket *tx_sock;
+ struct socket *rx_sock;
+
+ vhost_net_stop(n, &tx_sock, &rx_sock);
+ vhost_net_flush(n);
+ vhost_dev_cleanup(&n->dev);
+ if (tx_sock)
+ fput(tx_sock->file);
+ if (rx_sock)
+ fput(rx_sock->file);
+ /* We do an extra flush before freeing memory,
+ * since jobs can re-queue themselves. */
+ vhost_net_flush(n);
+ kfree(n);
+ return 0;
+}
+
+static struct socket *get_raw_socket(int fd)
+{
+ struct {
+ struct sockaddr_ll sa;
+ char buf[MAX_ADDR_LEN];
+ } uaddr;
+ int uaddr_len = sizeof uaddr, r;
+ struct socket *sock = sockfd_lookup(fd, &r);
+ if (!sock)
+ return ERR_PTR(-ENOTSOCK);
+
+ /* Parameter checking */
+ if (sock->sk->sk_type != SOCK_RAW) {
+ r = -ESOCKTNOSUPPORT;
+ goto err;
+ }
+
+ r = sock->ops->getname(sock, (struct sockaddr *)&uaddr.sa,
+ &uaddr_len, 0);
+ if (r)
+ goto err;
+
+ if (uaddr.sa.sll_family != AF_PACKET) {
+ r = -EPFNOSUPPORT;
+ goto err;
+ }
+ return sock;
+err:
+ fput(sock->file);
+ return ERR_PTR(r);
+}
+
+static struct socket *get_tun_socket(int fd)
+{
+ struct file *file = fget(fd);
+ struct socket *sock;
+ if (!file)
+ return ERR_PTR(-EBADF);
+ sock = tun_get_socket(file);
+ if (IS_ERR(sock))
+ fput(file);
+ return sock;
+}
+
+static struct socket *get_socket(int fd)
+{
+ struct socket *sock;
+ if (fd == -1)
+ return NULL;
+ sock = get_raw_socket(fd);
+ if (!IS_ERR(sock))
+ return sock;
+ sock = get_tun_socket(fd);
+ if (!IS_ERR(sock))
+ return sock;
+ return ERR_PTR(-ENOTSOCK);
+}
+
+static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
+{
+ struct socket *sock, *oldsock;
+ struct vhost_virtqueue *vq;
+ int r;
+
+ mutex_lock(&n->dev.mutex);
+ r = vhost_dev_check_owner(&n->dev);
+ if (r)
+ goto err;
+
+ if (index >= VHOST_NET_VQ_MAX) {
+ r = -ENOBUFS;
+ goto err;
+ }
+ vq = n->vqs + index;
+ mutex_lock(&vq->mutex);
+ sock = get_socket(fd);
+ if (IS_ERR(sock)) {
+ r = PTR_ERR(sock);
+ goto err;
+ }
+
+ /* start polling new socket */
+ oldsock = vq->private_data;
+ if (sock == oldsock)
+ goto done;
+
+ vhost_net_disable_vq(n, vq);
+ rcu_assign_pointer(vq->private_data, sock);
+ vhost_net_enable_vq(n, vq);
+ mutex_unlock(&vq->mutex);
+done:
+ mutex_unlock(&n->dev.mutex);
+ if (oldsock) {
+ vhost_net_flush_vq(n, index);
+ fput(oldsock->file);
+ }
+ return r;
+err:
+ mutex_unlock(&n->dev.mutex);
+ return r;
+}
+
+static long vhost_net_reset_owner(struct vhost_net *n)
+{
+ struct socket *tx_sock = NULL;
+ struct socket *rx_sock = NULL;
+ long err;
+ mutex_lock(&n->dev.mutex);
+ err = vhost_dev_check_owner(&n->dev);
+ if (err)
+ goto done;
+ vhost_net_stop(n, &tx_sock, &rx_sock);
+ vhost_net_flush(n);
+ err = vhost_dev_reset_owner(&n->dev);
+done:
+ mutex_unlock(&n->dev.mutex);
+ if (tx_sock)
+ fput(tx_sock->file);
+ if (rx_sock)
+ fput(rx_sock->file);
+ return err;
+}
+
+static void vhost_net_set_features(struct vhost_net *n, u64 features)
+{
+ size_t hdr_size = features & (1 << VHOST_NET_F_VIRTIO_NET_HDR) ?
+ sizeof(struct virtio_net_hdr) : 0;
+ int i;
+ mutex_lock(&n->dev.mutex);
+ n->dev.acked_features = features;
+ smp_wmb();
+ for (i = 0; i < VHOST_NET_VQ_MAX; ++i) {
+ mutex_lock(&n->vqs[i].mutex);
+ n->vqs[i].hdr_size = hdr_size;
+ mutex_unlock(&n->vqs[i].mutex);
+ }
+ mutex_unlock(&n->dev.mutex);
+ vhost_net_flush(n);
+}
+
+static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ struct vhost_net *n = f->private_data;
+ void __user *argp = (void __user *)arg;
+ u32 __user *featurep = argp;
+ struct vhost_vring_file backend;
+ u64 features;
+ int r;
+ switch (ioctl) {
+ case VHOST_NET_SET_BACKEND:
+ r = copy_from_user(&backend, argp, sizeof backend);
+ if (r < 0)
+ return r;
+ return vhost_net_set_backend(n, backend.index, backend.fd);
+ case VHOST_GET_FEATURES:
+ features = VHOST_FEATURES;
+ return put_user(features, featurep);
+ case VHOST_SET_FEATURES:
+ r = get_user(features, featurep);
+ /* No features for now */
+ if (r < 0)
+ return r;
+ if (features & ~VHOST_FEATURES)
+ return -EOPNOTSUPP;
+ vhost_net_set_features(n, features);
+ return 0;
+ case VHOST_RESET_OWNER:
+ return vhost_net_reset_owner(n);
+ default:
+ r = vhost_dev_ioctl(&n->dev, ioctl, arg);
+ vhost_net_flush(n);
+ return r;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static long vhost_net_compat_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ return vhost_net_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+const static struct file_operations vhost_net_fops = {
+ .owner = THIS_MODULE,
+ .release = vhost_net_release,
+ .unlocked_ioctl = vhost_net_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vhost_net_compat_ioctl,
+#endif
+ .open = vhost_net_open,
+};
+
+static struct miscdevice vhost_net_misc = {
+ VHOST_NET_MINOR,
+ "vhost-net",
+ &vhost_net_fops,
+};
+
+int vhost_net_init(void)
+{
+ int r = vhost_init();
+ if (r)
+ goto err_init;
+ r = misc_register(&vhost_net_misc);
+ if (r)
+ goto err_reg;
+ return 0;
+err_reg:
+ vhost_cleanup();
+err_init:
+ return r;
+
+}
+module_init(vhost_net_init);
+
+void vhost_net_exit(void)
+{
+ misc_deregister(&vhost_net_misc);
+ vhost_cleanup();
+}
+module_exit(vhost_net_exit);
+
+MODULE_VERSION("0.0.1");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Michael S. Tsirkin");
+MODULE_DESCRIPTION("Host kernel accelerator for virtio net");
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
new file mode 100644
index 0000000..97233d5
--- /dev/null
+++ b/drivers/vhost/vhost.c
@@ -0,0 +1,965 @@
+/* Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2006 Rusty Russell IBM Corporation
+ *
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * Inspiration, some code, and most witty comments come from
+ * Documentation/lguest/lguest.c, by Rusty Russell
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ *
+ * Generic code for virtio server in host kernel.
+ */
+
+#include <linux/eventfd.h>
+#include <linux/vhost.h>
+#include <linux/virtio_net.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/rcupdate.h>
+#include <linux/poll.h>
+#include <linux/file.h>
+#include <linux/highmem.h>
+
+#include <linux/net.h>
+#include <linux/if_packet.h>
+#include <linux/if_arp.h>
+
+#include <net/sock.h>
+
+#include "vhost.h"
+
+enum {
+ VHOST_MEMORY_MAX_NREGIONS = 64,
+ VHOST_MEMORY_F_LOG = 0x1,
+};
+
+static struct workqueue_struct *vhost_workqueue;
+
+static void vhost_poll_func(struct file *file, wait_queue_head_t *wqh,
+ poll_table *pt)
+{
+ struct vhost_poll *poll;
+ poll = container_of(pt, struct vhost_poll, table);
+
+ poll->wqh = wqh;
+ add_wait_queue(wqh, &poll->wait);
+}
+
+static int vhost_poll_wakeup(wait_queue_t *wait, unsigned mode, int sync,
+ void *key)
+{
+ struct vhost_poll *poll;
+ poll = container_of(wait, struct vhost_poll, wait);
+ if (!((unsigned long)key & poll->mask))
+ return 0;
+
+ queue_work(vhost_workqueue, &poll->work);
+ return 0;
+}
+
+/* Init poll structure */
+void vhost_poll_init(struct vhost_poll *poll, work_func_t func,
+ unsigned long mask)
+{
+ INIT_WORK(&poll->work, func);
+ init_waitqueue_func_entry(&poll->wait, vhost_poll_wakeup);
+ init_poll_funcptr(&poll->table, vhost_poll_func);
+ poll->mask = mask;
+}
+
+/* Start polling a file. We add ourselves to file's wait queue. The caller must
+ * keep a reference to a file until after vhost_poll_stop is called. */
+void vhost_poll_start(struct vhost_poll *poll, struct file *file)
+{
+ unsigned long mask;
+ mask = file->f_op->poll(file, &poll->table);
+ if (mask)
+ vhost_poll_wakeup(&poll->wait, 0, 0, (void *)mask);
+}
+
+/* Stop polling a file. After this function returns, it becomes safe to drop the
+ * file reference. You must also flush afterwards. */
+void vhost_poll_stop(struct vhost_poll *poll)
+{
+ remove_wait_queue(poll->wqh, &poll->wait);
+}
+
+/* Flush any work that has been scheduled. When calling this, don't hold any
+ * locks that are also used by the callback. */
+void vhost_poll_flush(struct vhost_poll *poll)
+{
+ flush_work(&poll->work);
+}
+
+void vhost_poll_queue(struct vhost_poll *poll)
+{
+ queue_work(vhost_workqueue, &poll->work);
+}
+
+static void vhost_vq_reset(struct vhost_dev *dev,
+ struct vhost_virtqueue *vq)
+{
+ vq->num = 1;
+ vq->desc = NULL;
+ vq->avail = NULL;
+ vq->used = NULL;
+ vq->last_avail_idx = 0;
+ vq->avail_idx = 0;
+ vq->last_used_idx = 0;
+ vq->used_flags = 0;
+ vq->used_flags = 0;
+ vq->log_used = false;
+ vq->log_addr = -1ull;
+ vq->hdr_size = 0;
+ vq->private_data = NULL;
+ vq->log_base = NULL;
+ vq->error_ctx = NULL;
+ vq->error = NULL;
+ vq->kick = NULL;
+ vq->call_ctx = NULL;
+ vq->call = NULL;
+}
+
+long vhost_dev_init(struct vhost_dev *dev,
+ struct vhost_virtqueue *vqs, int nvqs)
+{
+ int i;
+ dev->vqs = vqs;
+ dev->nvqs = nvqs;
+ mutex_init(&dev->mutex);
+ dev->log_ctx = NULL;
+ dev->log_file = NULL;
+ dev->memory = NULL;
+ dev->mm = NULL;
+
+ for (i = 0; i < dev->nvqs; ++i) {
+ dev->vqs[i].dev = dev;
+ mutex_init(&dev->vqs[i].mutex);
+ vhost_vq_reset(dev, dev->vqs + i);
+ if (dev->vqs[i].handle_kick)
+ vhost_poll_init(&dev->vqs[i].poll,
+ dev->vqs[i].handle_kick,
+ POLLIN);
+ }
+ return 0;
+}
+
+/* Caller should have device mutex */
+long vhost_dev_check_owner(struct vhost_dev *dev)
+{
+ /* Are you the owner? If not, I don't think you mean to do that */
+ return dev->mm == current->mm ? 0 : -EPERM;
+}
+
+/* Caller should have device mutex */
+static long vhost_dev_set_owner(struct vhost_dev *dev)
+{
+ /* Is there an owner already? */
+ if (dev->mm)
+ return -EBUSY;
+ /* No owner, become one */
+ dev->mm = get_task_mm(current);
+ return 0;
+}
+
+/* Caller should have device mutex */
+long vhost_dev_reset_owner(struct vhost_dev *dev)
+{
+ struct vhost_memory *memory;
+
+ /* Restore memory to default 1:1 mapping. */
+ memory = kmalloc(offsetof(struct vhost_memory, regions) +
+ 2 * sizeof *memory->regions, GFP_KERNEL);
+ if (!memory)
+ return -ENOMEM;
+
+ vhost_dev_cleanup(dev);
+
+ memory->nregions = 2;
+ memory->regions[0].guest_phys_addr = 1;
+ memory->regions[0].userspace_addr = 1;
+ memory->regions[0].memory_size = ~0ULL;
+ memory->regions[1].guest_phys_addr = 0;
+ memory->regions[1].userspace_addr = 0;
+ memory->regions[1].memory_size = 1;
+ dev->memory = memory;
+ return 0;
+}
+
+/* Caller should have device mutex */
+void vhost_dev_cleanup(struct vhost_dev *dev)
+{
+ int i;
+ for (i = 0; i < dev->nvqs; ++i) {
+ if (dev->vqs[i].kick && dev->vqs[i].handle_kick) {
+ vhost_poll_stop(&dev->vqs[i].poll);
+ vhost_poll_flush(&dev->vqs[i].poll);
+ }
+ if (dev->vqs[i].error_ctx)
+ eventfd_ctx_put(dev->vqs[i].error_ctx);
+ if (dev->vqs[i].error)
+ fput(dev->vqs[i].error);
+ if (dev->vqs[i].kick)
+ fput(dev->vqs[i].kick);
+ if (dev->vqs[i].call_ctx)
+ eventfd_ctx_put(dev->vqs[i].call_ctx);
+ if (dev->vqs[i].call)
+ fput(dev->vqs[i].call);
+ vhost_vq_reset(dev, dev->vqs + i);
+ }
+ if (dev->log_ctx)
+ eventfd_ctx_put(dev->log_ctx);
+ dev->log_ctx = NULL;
+ if (dev->log_file)
+ fput(dev->log_file);
+ dev->log_file = NULL;
+ /* No one will access memory at this point */
+ kfree(dev->memory);
+ dev->memory = NULL;
+ if (dev->mm)
+ mmput(dev->mm);
+ dev->mm = NULL;
+}
+
+static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
+{
+ struct vhost_memory mem, *newmem, *oldmem;
+ unsigned long size = offsetof(struct vhost_memory, regions);
+ long r;
+ r = copy_from_user(&mem, m, size);
+ if (r)
+ return r;
+ if (mem.padding)
+ return -EOPNOTSUPP;
+ if (mem.nregions > VHOST_MEMORY_MAX_NREGIONS)
+ return -E2BIG;
+ newmem = kmalloc(size + mem.nregions * sizeof *m->regions, GFP_KERNEL);
+ if (!newmem)
+ return -ENOMEM;
+
+ memcpy(newmem, &mem, size);
+ r = copy_from_user(newmem->regions, m->regions,
+ mem.nregions * sizeof *m->regions);
+ if (r) {
+ kfree(newmem);
+ return r;
+ }
+ oldmem = d->memory;
+ rcu_assign_pointer(d->memory, newmem);
+ synchronize_rcu();
+ kfree(oldmem);
+ return 0;
+}
+
+static int init_used(struct vhost_virtqueue *vq,
+ struct vring_used __user *used)
+{
+ int r = put_user(vq->used_flags, &used->flags);
+ if (r)
+ return r;
+ return get_user(vq->last_used_idx, &used->idx);
+}
+
+static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
+{
+ struct file *eventfp, *filep = NULL,
+ *pollstart = NULL, *pollstop = NULL;
+ struct eventfd_ctx *ctx = NULL;
+ u32 __user *idxp = argp;
+ struct vhost_virtqueue *vq;
+ struct vhost_vring_state s;
+ struct vhost_vring_file f;
+ struct vhost_vring_addr a;
+ u32 idx;
+ long r;
+
+ r = get_user(idx, idxp);
+ if (r < 0)
+ return r;
+ if (idx > d->nvqs)
+ return -ENOBUFS;
+
+ vq = d->vqs + idx;
+
+ mutex_lock(&vq->mutex);
+
+ switch (ioctl) {
+ case VHOST_SET_VRING_NUM:
+ r = copy_from_user(&s, argp, sizeof s);
+ if (r < 0)
+ break;
+ if (!s.num || s.num > 0xffff || (s.num & (s.num - 1))) {
+ r = -EINVAL;
+ break;
+ }
+ vq->num = s.num;
+ break;
+ case VHOST_SET_VRING_BASE:
+ r = copy_from_user(&s, argp, sizeof s);
+ if (r < 0)
+ break;
+ if (s.num > 0xffff) {
+ r = -EINVAL;
+ break;
+ }
+ vq->last_avail_idx = s.num;
+ /* Forget the cached index value. */
+ vq->avail_idx = vq->last_avail_idx;
+ break;
+ case VHOST_GET_VRING_BASE:
+ s.index = idx;
+ s.num = vq->last_avail_idx;
+ r = copy_to_user(argp, &s, sizeof s);
+ break;
+ case VHOST_SET_VRING_ADDR:
+ r = copy_from_user(&a, argp, sizeof a);
+ if (r < 0)
+ break;
+ if (a.flags & ~(0x1 << VHOST_VRING_F_LOG)) {
+ r = -EOPNOTSUPP;
+ break;
+ }
+ if ((u64)(unsigned long)a.desc_user_addr != a.desc_user_addr ||
+ (u64)(unsigned long)a.used_user_addr != a.used_user_addr ||
+ (u64)(unsigned long)a.avail_user_addr != a.avail_user_addr) {
+ r = -EFAULT;
+ break;
+ }
+ if ((a.avail_user_addr & (sizeof *vq->avail->ring - 1)) ||
+ (a.used_user_addr & (sizeof *vq->used->ring - 1)) ||
+ (a.log_guest_addr & (sizeof *vq->used->ring - 1))) {
+ r = -EINVAL;
+ break;
+ }
+ r = init_used(vq, (struct vring_used __user *)a.used_user_addr);
+ if (r)
+ break;
+ vq->log_used = !!(a.flags & (0x1 << VHOST_VRING_F_LOG));
+ vq->desc = (void __user *)(unsigned long)a.desc_user_addr;
+ vq->avail = (void __user *)(unsigned long)a.avail_user_addr;
+ vq->log_addr = a.log_guest_addr;
+ vq->used = (void __user *)(unsigned long)a.used_user_addr;
+ break;
+ case VHOST_SET_VRING_KICK:
+ r = copy_from_user(&f, argp, sizeof f);
+ if (r < 0)
+ break;
+ eventfp = f.fd == -1 ? NULL : eventfd_fget(f.fd);
+ if (IS_ERR(eventfp))
+ return PTR_ERR(eventfp);
+ if (eventfp != vq->kick) {
+ pollstop = filep = vq->kick;
+ pollstart = vq->kick = eventfp;
+ } else
+ filep = eventfp;
+ break;
+ case VHOST_SET_VRING_CALL:
+ r = copy_from_user(&f, argp, sizeof f);
+ if (r < 0)
+ break;
+ eventfp = f.fd == -1 ? NULL : eventfd_fget(f.fd);
+ if (IS_ERR(eventfp))
+ return PTR_ERR(eventfp);
+ if (eventfp != vq->call) {
+ filep = vq->call;
+ ctx = vq->call_ctx;
+ vq->call = eventfp;
+ vq->call_ctx = eventfp ?
+ eventfd_ctx_fileget(eventfp) : NULL;
+ } else
+ filep = eventfp;
+ break;
+ case VHOST_SET_VRING_ERR:
+ r = copy_from_user(&f, argp, sizeof f);
+ if (r < 0)
+ break;
+ eventfp = f.fd == -1 ? NULL : eventfd_fget(f.fd);
+ if (IS_ERR(eventfp))
+ return PTR_ERR(eventfp);
+ if (eventfp != vq->error) {
+ filep = vq->error;
+ vq->error = eventfp;
+ ctx = vq->error_ctx;
+ vq->error_ctx = eventfp ?
+ eventfd_ctx_fileget(eventfp) : NULL;
+ } else
+ filep = eventfp;
+ break;
+ default:
+ r = -ENOIOCTLCMD;
+ }
+
+ if (pollstop && vq->handle_kick)
+ vhost_poll_stop(&vq->poll);
+
+ if (ctx)
+ eventfd_ctx_put(ctx);
+ if (filep)
+ fput(filep);
+
+ if (pollstart && vq->handle_kick)
+ vhost_poll_start(&vq->poll, vq->kick);
+
+ mutex_unlock(&vq->mutex);
+
+ if (pollstop && vq->handle_kick)
+ vhost_poll_flush(&vq->poll);
+ return r;
+}
+
+long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct file *eventfp, *filep = NULL;
+ struct eventfd_ctx *ctx = NULL;
+ u64 p;
+ long r;
+ int i, fd;
+
+ mutex_lock(&d->mutex);
+ /* If you are not the owner, you can become one */
+ if (ioctl == VHOST_SET_OWNER) {
+ r = vhost_dev_set_owner(d);
+ goto done;
+ }
+
+ /* You must be the owner to do anything else */
+ r = vhost_dev_check_owner(d);
+ if (r)
+ goto done;
+
+ switch (ioctl) {
+ case VHOST_SET_MEM_TABLE:
+ r = vhost_set_memory(d, argp);
+ break;
+ case VHOST_SET_LOG_BASE:
+ r = copy_from_user(&p, argp, sizeof p);
+ if (r < 0)
+ break;
+ if ((u64)(unsigned long)p != p) {
+ r = -EFAULT;
+ break;
+ }
+ for (i = 0; i < d->nvqs; ++i) {
+ mutex_lock(&d->vqs[i].mutex);
+ d->vqs[i].log_base = (void __user *)(unsigned long)p;
+ mutex_unlock(&d->vqs[i].mutex);
+ }
+ break;
+ case VHOST_SET_LOG_FD:
+ r = get_user(fd, (int __user *)argp);
+ if (r < 0)
+ break;
+ eventfp = fd == -1 ? NULL : eventfd_fget(fd);
+ if (IS_ERR(eventfp)) {
+ r = PTR_ERR(eventfp);
+ break;
+ }
+ if (eventfp != d->log_file) {
+ filep = d->log_file;
+ ctx = d->log_ctx;
+ d->log_ctx = eventfp ?
+ eventfd_ctx_fileget(eventfp) : NULL;
+ } else
+ filep = eventfp;
+ for (i = 0; i < d->nvqs; ++i) {
+ mutex_lock(&d->vqs[i].mutex);
+ d->vqs[i].log_ctx = d->log_ctx;
+ mutex_unlock(&d->vqs[i].mutex);
+ }
+ if (ctx)
+ eventfd_ctx_put(ctx);
+ if (filep)
+ fput(filep);
+ break;
+ default:
+ r = vhost_set_vring(d, ioctl, argp);
+ break;
+ }
+done:
+ mutex_unlock(&d->mutex);
+ return r;
+}
+
+static const struct vhost_memory_region *find_region(struct vhost_memory *mem,
+ __u64 addr, __u32 len)
+{
+ struct vhost_memory_region *reg;
+ int i;
+ /* linear search is not brilliant, but we really have on the order of 6
+ * regions in practice */
+ for (i = 0; i < mem->nregions; ++i) {
+ reg = mem->regions + i;
+ if (reg->guest_phys_addr <= addr &&
+ reg->guest_phys_addr + reg->memory_size - 1 >= addr)
+ return reg;
+ }
+ return NULL;
+}
+
+/* TODO: This is really inefficient. We need something like get_user()
+ * (instruction directly accesses the data, with an exception table entry
+ * returning -EFAULT). See Documentation/x86/exception-tables.txt.
+ */
+static int set_bit_to_user(int nr, void __user *addr)
+{
+ unsigned long log = (unsigned long)addr;
+ struct page *page;
+ void *base;
+ int bit = nr + (log % PAGE_SIZE) * 8;
+ int r;
+ r = get_user_pages_fast(log, 1, 1, &page);
+ if (r)
+ return r;
+ base = kmap_atomic(page, KM_USER0);
+ set_bit(bit, base);
+ kunmap_atomic(base, KM_USER0);
+ set_page_dirty_lock(page);
+ put_page(page);
+ return 0;
+}
+
+static int log_write(void __user *log_base,
+ u64 write_address, u64 write_length)
+{
+ int r;
+ if (!write_length)
+ return 0;
+ write_address /= VHOST_PAGE_SIZE;
+ for (;;) {
+ u64 base = (u64)(unsigned long)log_base;
+ u64 log = base + write_address / 8;
+ int bit = write_address % 8;
+ if ((u64)(unsigned long)log != log)
+ return -EFAULT;
+ r = set_bit_to_user(bit, (void __user *)(unsigned long)log);
+ if (r < 0)
+ return r;
+ if (write_length <= VHOST_PAGE_SIZE)
+ break;
+ write_length -= VHOST_PAGE_SIZE;
+ write_address += VHOST_PAGE_SIZE;
+ }
+ return r;
+}
+
+int vhost_log_write(struct vhost_virtqueue *vq, struct vhost_log *log,
+ unsigned int log_num, u64 len)
+{
+ int i, r;
+
+ /* Make sure data written is seen before log. */
+ wmb();
+ for (i = 0; i < log_num; ++i) {
+ u64 l = min(log[i].len, len);
+ r = log_write(vq->log_base, log[i].addr, l);
+ if (r < 0)
+ return r;
+ len -= l;
+ if (!len)
+ return 0;
+ }
+ if (vq->log_ctx)
+ eventfd_signal(vq->log_ctx, 1);
+ /* Length written exceeds what we have stored. This is a bug. */
+ BUG();
+ return 0;
+}
+
+int translate_desc(struct vhost_dev *dev, u64 addr, u32 len,
+ struct iovec iov[], int iov_size)
+{
+ const struct vhost_memory_region *reg;
+ struct vhost_memory *mem;
+ struct iovec *_iov;
+ u64 s = 0;
+ int ret = 0;
+
+ rcu_read_lock();
+
+ mem = rcu_dereference(dev->memory);
+ while ((u64)len > s) {
+ u64 size;
+ if (ret >= iov_size) {
+ ret = -ENOBUFS;
+ break;
+ }
+ reg = find_region(mem, addr, len);
+ if (!reg) {
+ ret = -EFAULT;
+ break;
+ }
+ _iov = iov + ret;
+ size = reg->memory_size - addr + reg->guest_phys_addr;
+ _iov->iov_len = min((u64)len, size);
+ _iov->iov_base = (void *)(unsigned long)
+ (reg->userspace_addr + addr - reg->guest_phys_addr);
+ s += size;
+ addr += size;
+ ++ret;
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+/* Each buffer in the virtqueues is actually a chain of descriptors. This
+ * function returns the next descriptor in the chain,
+ * or -1U if we're at the end. */
+static unsigned next_desc(struct vring_desc *desc)
+{
+ unsigned int next;
+
+ /* If this descriptor says it doesn't chain, we're done. */
+ if (!(desc->flags & VRING_DESC_F_NEXT))
+ return -1U;
+
+ /* Check they're not leading us off end of descriptors. */
+ next = desc->next;
+ /* Make sure compiler knows to grab that: we don't want it changing! */
+ /* We will use the result as an index in an array, so most
+ * architectures only need a compiler barrier here. */
+ read_barrier_depends();
+
+ return next;
+}
+
+static unsigned get_indirect(struct vhost_dev *dev, struct vhost_virtqueue *vq,
+ struct iovec iov[], unsigned int iov_size,
+ unsigned int *out_num, unsigned int *in_num,
+ struct vhost_log *log, unsigned int *log_num,
+ struct vring_desc *indirect)
+{
+ struct vring_desc desc;
+ unsigned int i = 0, count, found = 0;
+ int ret;
+
+ /* Sanity check */
+ if (indirect->len % sizeof desc) {
+ vq_err(vq, "Invalid length in indirect descriptor: "
+ "len 0x%llx not multiple of 0x%zx\n",
+ (unsigned long long)indirect->len,
+ sizeof desc);
+ return -EINVAL;
+ }
+
+ ret = translate_desc(dev, indirect->addr, indirect->len, vq->indirect,
+ ARRAY_SIZE(vq->indirect));
+ if (ret < 0) {
+ vq_err(vq, "Translation failure %d in indirect.\n", ret);
+ return ret;
+ }
+
+ /* We will use the result as an address to read from, so most
+ * architectures only need a compiler barrier here. */
+ read_barrier_depends();
+
+ count = indirect->len / sizeof desc;
+ /* Buffers are chained via a 16 bit next field, so
+ * we can have at most 2^16 of these. */
+ if (count > USHORT_MAX + 1) {
+ vq_err(vq, "Indirect buffer length too big: %d\n",
+ indirect->len);
+ return -E2BIG;
+ }
+
+ do {
+ unsigned iov_count = *in_num + *out_num;
+ if (++found > count) {
+ vq_err(vq, "Loop detected: last one at %u "
+ "indirect size %u\n",
+ i, count);
+ return -EINVAL;
+ }
+ if (memcpy_fromiovec((unsigned char *)&desc, vq->indirect,
+ sizeof desc)) {
+ vq_err(vq, "Failed indirect descriptor: idx %d, %zx\n",
+ i, (size_t)indirect->addr + i * sizeof desc);
+ return -EINVAL;
+ }
+ if (desc.flags & VRING_DESC_F_INDIRECT) {
+ vq_err(vq, "Nested indirect descriptor: idx %d, %zx\n",
+ i, (size_t)indirect->addr + i * sizeof desc);
+ return -EINVAL;
+ }
+
+ ret = translate_desc(dev, desc.addr, desc.len, iov + iov_count,
+ iov_size - iov_count);
+ if (ret < 0) {
+ vq_err(vq, "Translation failure %d indirect idx %d\n",
+ ret, i);
+ return ret;
+ }
+ /* If this is an input descriptor, increment that count. */
+ if (desc.flags & VRING_DESC_F_WRITE) {
+ *in_num += ret;
+ if (unlikely(log)) {
+ log[*log_num].addr = desc.addr;
+ log[*log_num].len = desc.len;
+ ++*log_num;
+ }
+ } else {
+ /* If it's an output descriptor, they're all supposed
+ * to come before any input descriptors. */
+ if (*in_num) {
+ vq_err(vq, "Indirect descriptor "
+ "has out after in: idx %d\n", i);
+ return -EINVAL;
+ }
+ *out_num += ret;
+ }
+ } while ((i = next_desc(&desc)) != -1);
+ return 0;
+}
+
+/* This looks in the virtqueue and for the first available buffer, and converts
+ * it to an iovec for convenient access. Since descriptors consist of some
+ * number of output then some number of input descriptors, it's actually two
+ * iovecs, but we pack them into one and note how many of each there were.
+ *
+ * This function returns the descriptor number found, or vq->num (which
+ * is never a valid descriptor number) if none was found. */
+unsigned vhost_get_vq_desc(struct vhost_dev *dev, struct vhost_virtqueue *vq,
+ struct iovec iov[], unsigned int iov_size,
+ unsigned int *out_num, unsigned int *in_num,
+ struct vhost_log *log, unsigned int *log_num)
+{
+ struct vring_desc desc;
+ unsigned int i, head, found = 0;
+ u16 last_avail_idx;
+ int ret;
+
+ /* Check it isn't doing very strange things with descriptor numbers. */
+ last_avail_idx = vq->last_avail_idx;
+ if (get_user(vq->avail_idx, &vq->avail->idx)) {
+ vq_err(vq, "Failed to access avail idx at %p\n",
+ &vq->avail->idx);
+ return vq->num;
+ }
+
+ if ((u16)(vq->avail_idx - last_avail_idx) > vq->num) {
+ vq_err(vq, "Guest moved used index from %u to %u",
+ last_avail_idx, vq->avail_idx);
+ return vq->num;
+ }
+
+ /* If there's nothing new since last we looked, return invalid. */
+ if (vq->avail_idx == last_avail_idx)
+ return vq->num;
+
+ /* Only get avail ring entries after they have been exposed by guest. */
+ rmb();
+
+ /* Grab the next descriptor number they're advertising, and increment
+ * the index we've seen. */
+ if (get_user(head, &vq->avail->ring[last_avail_idx % vq->num])) {
+ vq_err(vq, "Failed to read head: idx %d address %p\n",
+ last_avail_idx,
+ &vq->avail->ring[last_avail_idx % vq->num]);
+ return vq->num;
+ }
+
+ /* If their number is silly, that's an error. */
+ if (head >= vq->num) {
+ vq_err(vq, "Guest says index %u > %u is available",
+ head, vq->num);
+ return vq->num;
+ }
+
+ /* When we start there are none of either input nor output. */
+ *out_num = *in_num = 0;
+ if (unlikely(log))
+ *log_num = 0;
+
+ i = head;
+ do {
+ unsigned iov_count = *in_num + *out_num;
+ if (i >= vq->num) {
+ vq_err(vq, "Desc index is %u > %u, head = %u",
+ i, vq->num, head);
+ return vq->num;
+ }
+ if (++found > vq->num) {
+ vq_err(vq, "Loop detected: last one at %u "
+ "vq size %u head %u\n",
+ i, vq->num, head);
+ return vq->num;
+ }
+ ret = copy_from_user(&desc, vq->desc + i, sizeof desc);
+ if (ret) {
+ vq_err(vq, "Failed to get descriptor: idx %d addr %p\n",
+ i, vq->desc + i);
+ return vq->num;
+ }
+ if (desc.flags & VRING_DESC_F_INDIRECT) {
+ ret = get_indirect(dev, vq, iov, iov_size,
+ out_num, in_num,
+ log, log_num, &desc);
+ if (ret < 0) {
+ vq_err(vq, "Failure detected "
+ "in indirect descriptor at idx %d\n", i);
+ return vq->num;
+ }
+ continue;
+ }
+
+ ret = translate_desc(dev, desc.addr, desc.len, iov + iov_count,
+ iov_size - iov_count);
+ if (ret < 0) {
+ vq_err(vq, "Translation failure %d descriptor idx %d\n",
+ ret, i);
+ return vq->num;
+ }
+ if (desc.flags & VRING_DESC_F_WRITE) {
+ /* If this is an input descriptor,
+ * increment that count. */
+ *in_num += ret;
+ if (unlikely(log)) {
+ log[*log_num].addr = desc.addr;
+ log[*log_num].len = desc.len;
+ ++*log_num;
+ }
+ } else {
+ /* If it's an output descriptor, they're all supposed
+ * to come before any input descriptors. */
+ if (*in_num) {
+ vq_err(vq, "Descriptor has out after in: "
+ "idx %d\n", i);
+ return vq->num;
+ }
+ *out_num += ret;
+ }
+ } while ((i = next_desc(&desc)) != -1);
+
+ /* On success, increment avail index. */
+ vq->last_avail_idx++;
+ return head;
+}
+
+/* Reverse the effect of vhost_get_vq_desc. Useful for error handling. */
+void vhost_discard_vq_desc(struct vhost_virtqueue *vq)
+{
+ vq->last_avail_idx--;
+}
+
+/* After we've used one of their buffers, we tell them about it. We'll then
+ * want to notify the guest, using eventfd. */
+int vhost_add_used(struct vhost_virtqueue *vq, unsigned int head, int len)
+{
+ struct vring_used_elem *used;
+
+ /* The virtqueue contains a ring of used buffers. Get a pointer to the
+ * next entry in that used ring. */
+ used = &vq->used->ring[vq->last_used_idx % vq->num];
+ if (put_user(head, &used->id)) {
+ vq_err(vq, "Failed to write used id");
+ return -EFAULT;
+ }
+ if (put_user(len, &used->len)) {
+ vq_err(vq, "Failed to write used len");
+ return -EFAULT;
+ }
+ /* Make sure buffer is written before we update index. */
+ wmb();
+ if (put_user(vq->last_used_idx + 1, &vq->used->idx)) {
+ vq_err(vq, "Failed to increment used idx");
+ return -EFAULT;
+ }
+ if (unlikely(vq->log_used)) {
+ /* Make sure data is seen before log. */
+ wmb();
+ log_write(vq->log_base, vq->log_addr + sizeof *vq->used->ring *
+ (vq->last_used_idx % vq->num),
+ sizeof *vq->used->ring);
+ log_write(vq->log_base, vq->log_addr, sizeof *vq->used->ring);
+ if (vq->log_ctx)
+ eventfd_signal(vq->log_ctx, 1);
+ }
+ vq->last_used_idx++;
+ return 0;
+}
+
+/* This actually signals the guest, using eventfd. */
+void vhost_signal(struct vhost_dev *dev, struct vhost_virtqueue *vq)
+{
+ __u16 flags = 0;
+ if (get_user(flags, &vq->avail->flags)) {
+ vq_err(vq, "Failed to get flags");
+ return;
+ }
+
+ /* If they don't want an interrupt, don't signal, unless empty. */
+ if ((flags & VRING_AVAIL_F_NO_INTERRUPT) &&
+ (vq->avail_idx != vq->last_avail_idx ||
+ !vhost_has_feature(dev, VIRTIO_F_NOTIFY_ON_EMPTY)))
+ return;
+
+ /* Signal the Guest tell them we used something up. */
+ if (vq->call_ctx)
+ eventfd_signal(vq->call_ctx, 1);
+}
+
+/* And here's the combo meal deal. Supersize me! */
+void vhost_add_used_and_signal(struct vhost_dev *dev,
+ struct vhost_virtqueue *vq,
+ unsigned int head, int len)
+{
+ vhost_add_used(vq, head, len);
+ vhost_signal(dev, vq);
+}
+
+/* OK, now we need to know about added descriptors. */
+bool vhost_enable_notify(struct vhost_virtqueue *vq)
+{
+ u16 avail_idx;
+ int r;
+ if (!(vq->used_flags & VRING_USED_F_NO_NOTIFY))
+ return false;
+ vq->used_flags &= ~VRING_USED_F_NO_NOTIFY;
+ r = put_user(vq->used_flags, &vq->used->flags);
+ if (r) {
+ vq_err(vq, "Failed to enable notification at %p: %d\n",
+ &vq->used->flags, r);
+ return false;
+ }
+ /* They could have slipped one in as we were doing that: make
+ * sure it's written, then check again. */
+ mb();
+ r = get_user(avail_idx, &vq->avail->idx);
+ if (r) {
+ vq_err(vq, "Failed to check avail idx at %p: %d\n",
+ &vq->avail->idx, r);
+ return false;
+ }
+
+ return avail_idx != vq->last_avail_idx;
+}
+
+/* We don't need to be notified again. */
+void vhost_disable_notify(struct vhost_virtqueue *vq)
+{
+ int r;
+ if (vq->used_flags & VRING_USED_F_NO_NOTIFY)
+ return;
+ vq->used_flags |= VRING_USED_F_NO_NOTIFY;
+ r = put_user(vq->used_flags, &vq->used->flags);
+ if (r)
+ vq_err(vq, "Failed to enable notification at %p: %d\n",
+ &vq->used->flags, r);
+}
+
+int vhost_init(void)
+{
+ vhost_workqueue = create_singlethread_workqueue("vhost");
+ if (!vhost_workqueue)
+ return -ENOMEM;
+ return 0;
+}
+
+void vhost_cleanup(void)
+{
+ destroy_workqueue(vhost_workqueue);
+}
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
new file mode 100644
index 0000000..d1f0453
--- /dev/null
+++ b/drivers/vhost/vhost.h
@@ -0,0 +1,159 @@
+#ifndef _VHOST_H
+#define _VHOST_H
+
+#include <linux/eventfd.h>
+#include <linux/vhost.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/poll.h>
+#include <linux/file.h>
+#include <linux/skbuff.h>
+#include <linux/uio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
+
+struct vhost_device;
+
+enum {
+ /* Enough place for all fragments, head, and virtio net header. */
+ VHOST_NET_MAX_SG = MAX_SKB_FRAGS + 2,
+};
+
+/* Poll a file (eventfd or socket) */
+/* Note: there's nothing vhost specific about this structure. */
+struct vhost_poll {
+ poll_table table;
+ wait_queue_head_t *wqh;
+ wait_queue_t wait;
+ /* struct which will handle all actual work. */
+ struct work_struct work;
+ unsigned long mask;
+};
+
+void vhost_poll_init(struct vhost_poll *poll, work_func_t func,
+ unsigned long mask);
+void vhost_poll_start(struct vhost_poll *poll, struct file *file);
+void vhost_poll_stop(struct vhost_poll *poll);
+void vhost_poll_flush(struct vhost_poll *poll);
+void vhost_poll_queue(struct vhost_poll *poll);
+
+struct vhost_log {
+ u64 addr;
+ u64 len;
+};
+
+/* The virtqueue structure describes a queue attached to a device. */
+struct vhost_virtqueue {
+ struct vhost_dev *dev;
+
+ /* The actual ring of buffers. */
+ struct mutex mutex;
+ unsigned int num;
+ struct vring_desc __user *desc;
+ struct vring_avail __user *avail;
+ struct vring_used __user *used;
+ struct file *kick;
+ struct file *call;
+ struct file *error;
+ struct eventfd_ctx *call_ctx;
+ struct eventfd_ctx *error_ctx;
+ struct eventfd_ctx *log_ctx;
+
+ struct vhost_poll poll;
+
+ /* The routine to call when the Guest pings us, or timeout. */
+ work_func_t handle_kick;
+
+ /* Last available index we saw. */
+ u16 last_avail_idx;
+
+ /* Caches available index value from user. */
+ u16 avail_idx;
+
+ /* Last index we used. */
+ u16 last_used_idx;
+
+ /* Used flags */
+ u16 used_flags;
+
+ /* Log writes to used structure. */
+ bool log_used;
+ u64 log_addr;
+
+ struct iovec indirect[VHOST_NET_MAX_SG];
+ struct iovec iov[VHOST_NET_MAX_SG];
+ struct iovec hdr[VHOST_NET_MAX_SG];
+ size_t hdr_size;
+ /* We use a kind of RCU to access private pointer.
+ * All readers access it from workqueue, which makes it possible to
+ * flush the workqueue instead of synchronize_rcu. Therefore readers do
+ * not need to call rcu_read_lock/rcu_read_unlock: the beginning of
+ * work item execution acts instead of rcu_read_lock() and the end of
+ * work item execution acts instead of rcu_read_lock().
+ * Writers use virtqueue mutex. */
+ void *private_data;
+ /* Log write descriptors */
+ void __user *log_base;
+ struct vhost_log log[VHOST_NET_MAX_SG];
+};
+
+struct vhost_dev {
+ /* Readers use RCU to access memory table pointer
+ * log base pointer and features.
+ * Writers use mutex below.*/
+ struct vhost_memory *memory;
+ struct mm_struct *mm;
+ struct mutex mutex;
+ unsigned acked_features;
+ struct vhost_virtqueue *vqs;
+ int nvqs;
+ struct file *log_file;
+ struct eventfd_ctx *log_ctx;
+};
+
+long vhost_dev_init(struct vhost_dev *, struct vhost_virtqueue *vqs, int nvqs);
+long vhost_dev_check_owner(struct vhost_dev *);
+long vhost_dev_reset_owner(struct vhost_dev *);
+void vhost_dev_cleanup(struct vhost_dev *);
+long vhost_dev_ioctl(struct vhost_dev *, unsigned int ioctl, unsigned long arg);
+
+unsigned vhost_get_vq_desc(struct vhost_dev *, struct vhost_virtqueue *,
+ struct iovec iov[], unsigned int iov_count,
+ unsigned int *out_num, unsigned int *in_num,
+ struct vhost_log *log, unsigned int *log_num);
+void vhost_discard_vq_desc(struct vhost_virtqueue *);
+
+int vhost_add_used(struct vhost_virtqueue *, unsigned int head, int len);
+void vhost_signal(struct vhost_dev *, struct vhost_virtqueue *);
+void vhost_add_used_and_signal(struct vhost_dev *, struct vhost_virtqueue *,
+ unsigned int head, int len);
+void vhost_disable_notify(struct vhost_virtqueue *);
+bool vhost_enable_notify(struct vhost_virtqueue *);
+
+int vhost_log_write(struct vhost_virtqueue *vq, struct vhost_log *log,
+ unsigned int log_num, u64 len);
+
+int vhost_init(void);
+void vhost_cleanup(void);
+
+#define vq_err(vq, fmt, ...) do { \
+ pr_debug(pr_fmt(fmt), ##__VA_ARGS__); \
+ if ((vq)->error_ctx) \
+ eventfd_signal((vq)->error_ctx, 1);\
+ } while (0)
+
+enum {
+ VHOST_FEATURES = (1 << VIRTIO_F_NOTIFY_ON_EMPTY) |
+ (1 << VIRTIO_RING_F_INDIRECT_DESC) |
+ (1 << VHOST_F_LOG_ALL) |
+ (1 << VHOST_NET_F_VIRTIO_NET_HDR),
+};
+
+static inline int vhost_has_feature(struct vhost_dev *dev, int bit)
+{
+ unsigned acked_features = rcu_dereference(dev->acked_features);
+ return acked_features & (1 << bit);
+}
+
+#endif
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 1feed71..e210194 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -361,6 +361,7 @@ unifdef-y += uio.h
unifdef-y += unistd.h
unifdef-y += usbdevice_fs.h
unifdef-y += utsname.h
+unifdef-y += vhost.h
unifdef-y += videodev2.h
unifdef-y += videodev.h
unifdef-y += virtio_config.h
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
index adaf3c1..8b5f7cc 100644
--- a/include/linux/miscdevice.h
+++ b/include/linux/miscdevice.h
@@ -30,6 +30,7 @@
#define HPET_MINOR 228
#define FUSE_MINOR 229
#define KVM_MINOR 232
+#define VHOST_NET_MINOR 233
#define MISC_DYNAMIC_MINOR 255
struct device;
diff --git a/include/linux/vhost.h b/include/linux/vhost.h
new file mode 100644
index 0000000..e847f1e
--- /dev/null
+++ b/include/linux/vhost.h
@@ -0,0 +1,130 @@
+#ifndef _LINUX_VHOST_H
+#define _LINUX_VHOST_H
+/* Userspace interface for in-kernel virtio accelerators. */
+
+/* vhost is used to reduce the number of system calls involved in virtio.
+ *
+ * Existing virtio net code is used in the guest without modification.
+ *
+ * This header includes interface used by userspace hypervisor for
+ * device configuration.
+ */
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/ioctl.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
+
+struct vhost_vring_state {
+ unsigned int index;
+ unsigned int num;
+};
+
+struct vhost_vring_file {
+ unsigned int index;
+ int fd; /* Pass -1 to unbind from file. */
+
+};
+
+struct vhost_vring_addr {
+ unsigned int index;
+ /* Option flags. */
+ unsigned int flags;
+ /* Flag values: */
+ /* Whether log address is valid. If set enables logging. */
+#define VHOST_VRING_F_LOG 0
+
+ /* Start of array of descriptors (virtually contiguous) */
+ __u64 desc_user_addr;
+ /* Used structure address. Must be 32 bit aligned */
+ __u64 used_user_addr;
+ /* Available structure address. Must be 16 bit aligned */
+ __u64 avail_user_addr;
+ /* Logging support. */
+ /* Log writes to used structure, at offset calculated from specified
+ * address. Address must be 32 bit aligned. */
+ __u64 log_guest_addr;
+};
+
+struct vhost_memory_region {
+ __u64 guest_phys_addr;
+ __u64 memory_size; /* bytes */
+ __u64 userspace_addr;
+ __u64 flags_padding; /* No flags are currently specified. */
+};
+
+/* All region addresses and sizes must be 4K aligned. */
+#define VHOST_PAGE_SIZE 0x1000
+
+struct vhost_memory {
+ __u32 nregions;
+ __u32 padding;
+ struct vhost_memory_region regions[0];
+};
+
+/* ioctls */
+
+#define VHOST_VIRTIO 0xAF
+
+/* Features bitmask for forward compatibility. Transport bits are used for
+ * vhost specific features. */
+#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
+
+/* Set current process as the (exclusive) owner of this file descriptor. This
+ * must be called before any other vhost command. Further calls to
+ * VHOST_OWNER_SET fail until VHOST_OWNER_RESET is called. */
+#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
+/* Give up ownership, and reset the device to default values.
+ * Allows subsequent call to VHOST_OWNER_SET to succeed. */
+#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
+
+/* Set up/modify memory layout */
+#define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory)
+
+/* Write logging setup. */
+/* Memory writes can optionally be logged by setting bit at an offset
+ * (calculated from the physical address) from specified log base.
+ * The bit is set using an atomic 32 bit operation. */
+/* Set base address for logging. */
+#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
+/* Specify an eventfd file descriptor to signal on log write. */
+#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
+
+/* Ring setup. */
+/* Set number of descriptors in ring. This parameter can not
+ * be modified while ring is running (bound to a device). */
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+/* Set addresses for the ring. */
+#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+/* Base value where queue looks for available descriptors */
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+/* Get accessor: reads index, writes value in num */
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+
+/* The following ioctls use eventfd file descriptors to signal and poll
+ * for events. */
+
+/* Set eventfd to poll for added buffers */
+#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
+/* Set eventfd to signal when buffers have beed used */
+#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
+/* Set eventfd to signal an error */
+#define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file)
+
+/* VHOST_NET specific defines */
+
+/* Attach virtio net ring to a raw socket, or tap device.
+ * The socket must be already bound to an ethernet device, this device will be
+ * used for transmit. Pass fd -1 to unbind from the socket and the transmit
+ * device. This can be used to stop the ring (e.g. for migration). */
+#define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file)
+
+/* Feature bits */
+/* Log all write descriptors. Can be changed while device is active. */
+#define VHOST_F_LOG_ALL 26
+/* vhost-net should add virtio_net_hdr for RX, and strip for TX packets. */
+#define VHOST_NET_F_VIRTIO_NET_HDR 27
+
+#endif
--
1.6.5.2.143.g8cc62
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox