* [PATCH] trivial: remove duplicated MIN macro from tehuti.
From: Thiago Farina @ 2009-11-02 20:45 UTC (permalink / raw)
To: trivial
Cc: baum, andy, davem, shemminger, apkm, ben, yanghy, netdev,
linux-kernel, Thiago Farina
Since the kernel api already has the macro "min",
just use it instead of declaring another one.
Signed-off-by: Thiago Farina <tfransosi@gmail.com>
---
drivers/net/tehuti.c | 4 ++--
drivers/net/tehuti.h | 2 --
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/net/tehuti.c b/drivers/net/tehuti.c
index ec9dfb2..8d116a9 100644
--- a/drivers/net/tehuti.c
+++ b/drivers/net/tehuti.c
@@ -1878,7 +1878,7 @@ static void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size)
udelay(50); /* give hw a chance to clean fifo */
continue;
}
- avail = MIN(avail, size);
+ avail = min(avail, size);
DBG("about to push %d bytes starting %p size %d\n", avail,
data, size);
bdx_tx_push_desc(priv, data, avail);
@@ -1889,7 +1889,7 @@ static void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size)
}
static const struct net_device_ops bdx_netdev_ops = {
- .ndo_open = bdx_open,
+ .ndo_open = bdx_open,
.ndo_stop = bdx_close,
.ndo_start_xmit = bdx_tx_transmit,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/tehuti.h b/drivers/net/tehuti.h
index 4fc875e..1241419 100644
--- a/drivers/net/tehuti.h
+++ b/drivers/net/tehuti.h
@@ -76,8 +76,6 @@
#define FIFO_SIZE 4096
#define FIFO_EXTRA_SPACE 1024
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
-
#if BITS_PER_LONG == 64
# define H32_64(x) (u32) ((u64)(x) >> 32)
# define L32_64(x) (u32) ((u64)(x) & 0xffffffff)
--
1.6.5.1.61.ge79999
^ permalink raw reply related
* Re: HTB accuracy on 10GbE
From: Stephen Hemminger @ 2009-11-02 20:53 UTC (permalink / raw)
To: Patrick McHardy; +Cc: Ryousei Takano, Linux Netdev List, takano-ryousei
In-Reply-To: <4AEEFE2E.7090706@trash.net>
On Mon, 02 Nov 2009 16:43:42 +0100
Patrick McHardy <kaber@trash.net> wrote:
> Ryousei Takano wrote:
> > Hi Stephen and all,
> >
> > I have observed a HTB accuracy problem on the Linux kernel 2.6.30 and
> > the Myri-10G 10 GbE NIC.
> > HTB can control the transmission rate at Gigabit speed, however it can
> > not work well at 10 Gigabit speed.
> >
> > I asked Stephen this problem at Japan Linux Symposium. He mentioned a
> > HTB bug related to the timer granularity.
> > I want to know what is happen, and what should be do for fixing it.
> >
> > Any comments and suggestions will be welcome.
> >
> > For more detail, please see the following page:
> > http://code.google.com/p/pspacer/wiki/HTBon10GbE
>
> This is not an easy problem to fix. Userspace, the kernel and the
> netlink API use 32 bit for timing related values, which is too small
> to use more than microsecond resolution. All of them need to be
> converted to use bigger types, additionally some kind of compatibility
> handling to deal with old iproute versions still using microsecond
> resolution is required.
The existing API is a legacy mish-mash. The field is limited to 32 bits,
but it might be possible to use a finer scale.
Maybe if kernel advertised finer resolution through /proc/net/psched
then table could be finer grained. This would maintain compatibility
between kernel and user space. You would need to have new kernel and
new iproute to get nanosecond resolution but older combinations would
still work.
The downside is that by using nanosecond resolution the rates are upper
bounded at 4.2seconds / packet.
^ permalink raw reply
* Re: wanPMC-CxT1E1
From: Greg KH @ 2009-11-02 20:59 UTC (permalink / raw)
To: Bob Beers; +Cc: netdev, Krzysztof Halasa
In-Reply-To: <4f6ba3b0911021241gb6d9684ga76228013e0f5f14@mail.gmail.com>
On Mon, Nov 02, 2009 at 03:41:44PM -0500, Bob Beers wrote:
> On Thu, Oct 29, 2009 at 8:52 PM, Krzysztof Halasa <khc@pm.waw.pl> wrote:
> > Bob Beers <bob.beers@gmail.com> writes:
> >
> >> ok, so where do I start, I have a system ready to start
> >> git cloning, and creating patches. I googled for a while
> >> but didn't find a nice recipe for participating in the -staging
> >> process.
> >
> > I gave it a try. At least compiles with few warnings. Not sure about the
> > WORK_INIT() change.
> >
> > Created drivers/net/wan/cxt1e1, moved all relevant SBE's .c and .h
> > there, added a simple Makefile/Kconfig. Quick and dirty. There is a
> > _lot_ of work to be done before it meets the usual kernel standards.
> >
> > It's not in staging/ so the paths need to be corrected but I can't work
> > further on it at this time. Hand-edited but I tried to be careful.
> >
> <snip Krzysztof's diff file>
>
> Ok, I took a shot at it too, It compiles for me and generates the
> cxt1e1.ko file.
> I have made a git clone of kernel, added the directory cxt1e1 under staging,
> added a Kconfig, Makefile, and all the *.[ch] from the tarball's driver/ and
> include/ directories, called git add on cxt1e1 directory and the files
> mentioned above. In the cxt1e1 directory I modified the *.[ch] files as I saw
> fit, and using many hints from Krzysztof
> (I'm also not sure about the changes to INIT_WORK).
> I modified Makefile in staging/ and ran 'git -a commit'.
>
> Here is top part of output from 'git log --stat':
>
> commit d54e08030785153c8c0f4eb4f1cf320d60fff286
> Author: Bob Beers <bob.beers@gmail.com>
> Date: Mon Nov 2 15:06:19 2009 -0500
>
> Add CXT1E1 [1,2,4] channel wan card driver.
>
> drivers/net/wan/Makefile | 1 +
This file shouldn't need to be modified. But I can fix that up by hand.
> drivers/staging/Kconfig | 2 +
> drivers/staging/Makefile | 1 +
> drivers/staging/cxt1e1/Kconfig | 22 +
> drivers/staging/cxt1e1/Makefile | 20 +
> drivers/staging/cxt1e1/comet.c | 566 +++++++
> drivers/staging/cxt1e1/comet.h | 366 +++++
> drivers/staging/cxt1e1/comet_tables.c | 561 +++++++
> drivers/staging/cxt1e1/comet_tables.h | 85 +
> drivers/staging/cxt1e1/functions.c | 366 +++++
> drivers/staging/cxt1e1/hwprobe.c | 400 +++++
> drivers/staging/cxt1e1/libsbew.h | 581 +++++++
> drivers/staging/cxt1e1/linux.c | 1354 ++++++++++++++++
> drivers/staging/cxt1e1/musycc.c | 2180 ++++++++++++++++++++++++++
> drivers/staging/cxt1e1/musycc.h | 460 ++++++
> drivers/staging/cxt1e1/ossiRelease.c | 39 +
> drivers/staging/cxt1e1/pmc93x6_eeprom.c | 559 +++++++
> drivers/staging/cxt1e1/pmc93x6_eeprom.h | 60 +
> drivers/staging/cxt1e1/pmcc4.h | 155 ++
> drivers/staging/cxt1e1/pmcc4_cpld.h | 124 ++
> drivers/staging/cxt1e1/pmcc4_defs.h | 82 +
> drivers/staging/cxt1e1/pmcc4_drv.c | 1855 ++++++++++++++++++++++
> drivers/staging/cxt1e1/pmcc4_ioctls.h | 81 +
> drivers/staging/cxt1e1/pmcc4_private.h | 295 ++++
> drivers/staging/cxt1e1/pmcc4_sysdep.h | 62 +
> drivers/staging/cxt1e1/sbe_bid.h | 61 +
> drivers/staging/cxt1e1/sbe_promformat.h | 157 ++
> drivers/staging/cxt1e1/sbecom_inline_linux.h | 310 ++++
> drivers/staging/cxt1e1/sbecrc.c | 137 ++
> drivers/staging/cxt1e1/sbeid.c | 217 +++
> drivers/staging/cxt1e1/sbeproc.c | 358 +++++
> drivers/staging/cxt1e1/sbeproc.h | 52 +
> drivers/staging/cxt1e1/sbew_ioc.h | 136 ++
> 33 files changed, 11705 insertions(+), 0 deletions(-)
>
> How do I generate the patch file?
git show --pretty=email d54e08030785153c8c0f4eb4f1cf320d60fff286 > my_patch.patch
will do it.
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH net-next-2.6] ifb: RCU locking avoids touching dev refcount
From: Jarek Poplawski @ 2009-11-02 21:14 UTC (permalink / raw)
To: Eric Dumazet; +Cc: David S. Miller, Linux Netdev List
In-Reply-To: <4AEE71EC.7040208@gmail.com>
Eric Dumazet wrote, On 11/02/2009 06:45 AM:
> Avoids touching dev refcount in hotpath
>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> ---
> drivers/net/ifb.c | 6 ++++--
> 1 files changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
> index 030913f..69c2566 100644
> --- a/drivers/net/ifb.c
> +++ b/drivers/net/ifb.c
> @@ -98,13 +98,15 @@ static void ri_tasklet(unsigned long dev)
> stats->tx_packets++;
> stats->tx_bytes +=skb->len;
>
> - skb->dev = dev_get_by_index(&init_net, skb->iif);
> + rcu_read_lock();
> + skb->dev = dev_get_by_index_rcu(&init_net, skb->iif);
> if (!skb->dev) {
> + rcu_read_unlock();
> dev_kfree_skb(skb);
> stats->tx_dropped++;
> break;
> }
> - dev_put(skb->dev);
> + rcu_read_unlock();
I wonder if this rcu_read_unlock() isn't too early here. I know, it
functionally fully replaces the old method, but as a whole it looks
strange:
> rcu_read_lock();
> skb->dev = dev_get_by_index_rcu(&init_net, skb->iif);
> if (!skb->dev) {
> rcu_read_unlock();
> dev_kfree_skb(skb);
> stats->tx_dropped++;
> break;
> }
> rcu_read_unlock();
> skb->iif = _dev->ifindex;
>
> if (from & AT_EGRESS) {
> dp->st_rx_frm_egr++;
> dev_queue_xmit(skb);
> } else if (from & AT_INGRESS) {
> dp->st_rx_frm_ing++;
> skb_pull(skb, skb->dev->hard_header_len);
So, how is skb->dev protected here, above and below? It seems these
rcu read blocks need extending, don't they?
Jarek P.
> netif_rx(skb);
> } else
> BUG();
> }
^ permalink raw reply
* Re: [PATCH net-next-2.6] ifb: RCU locking avoids touching dev refcount
From: Eric Dumazet @ 2009-11-02 21:34 UTC (permalink / raw)
To: Jarek Poplawski; +Cc: David S. Miller, Linux Netdev List
In-Reply-To: <4AEF4B9B.7000205@gmail.com>
Jarek Poplawski a écrit :
> Eric Dumazet wrote, On 11/02/2009 06:45 AM:
>
>> Avoids touching dev refcount in hotpath
>>
>> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
>> ---
>> drivers/net/ifb.c | 6 ++++--
>> 1 files changed, 4 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
>> index 030913f..69c2566 100644
>> --- a/drivers/net/ifb.c
>> +++ b/drivers/net/ifb.c
>> @@ -98,13 +98,15 @@ static void ri_tasklet(unsigned long dev)
>> stats->tx_packets++;
>> stats->tx_bytes +=skb->len;
>>
>> - skb->dev = dev_get_by_index(&init_net, skb->iif);
>> + rcu_read_lock();
>> + skb->dev = dev_get_by_index_rcu(&init_net, skb->iif);
>> if (!skb->dev) {
>> + rcu_read_unlock();
>> dev_kfree_skb(skb);
>> stats->tx_dropped++;
>> break;
>> }
>> - dev_put(skb->dev);
>> + rcu_read_unlock();
>
> I wonder if this rcu_read_unlock() isn't too early here. I know, it
> functionally fully replaces the old method, but as a whole it looks
> strange:
>
>> rcu_read_lock();
>> skb->dev = dev_get_by_index_rcu(&init_net, skb->iif);
>> if (!skb->dev) {
>> rcu_read_unlock();
>> dev_kfree_skb(skb);
>> stats->tx_dropped++;
>> break;
>> }
>> rcu_read_unlock();
>> skb->iif = _dev->ifindex;
>>
>> if (from & AT_EGRESS) {
>> dp->st_rx_frm_egr++;
>> dev_queue_xmit(skb);
>> } else if (from & AT_INGRESS) {
>> dp->st_rx_frm_ing++;
>> skb_pull(skb, skb->dev->hard_header_len);
>
>
> So, how is skb->dev protected here, above and below? It seems these
> rcu read blocks need extending, don't they?
>
Well, this might be true, but we run under tasklet (softirq) with preemption disabled.
We might move rcu_read_unlock() some lines down to not rely on this.
^ permalink raw reply
* Re: wanPMC-CxT1E1
From: Bob Beers @ 2009-11-02 21:35 UTC (permalink / raw)
To: Greg KH; +Cc: netdev, Krzysztof Halasa
In-Reply-To: <4f6ba3b0911021316v16ff7431s4dfa939743541df6@mail.gmail.com>
I just remembered that I have another mistake in the patch that I'd
like to confess: I put a 'CONFIG_CXT1E1 = m" at the top
of the staging/cxt1e1/Makefile.
I should be able to fix those things an generate another patch file,
but my git skills are very weak, so I guess you have the options:
fix it up by hand, or teach me a little more git, so I can submit
a better patch.
thanks,
--
-Bob Beers
^ permalink raw reply
* Re: wanPMC-CxT1E1
From: Bob Beers @ 2009-11-02 21:39 UTC (permalink / raw)
To: Greg KH; +Cc: netdev, Krzysztof Halasa
In-Reply-To: <4f6ba3b0911021335k3054da38x56fe72ac7ea8781c@mail.gmail.com>
On Mon, Nov 2, 2009 at 4:35 PM, Bob Beers <bob.beers@gmail.com> wrote:
> I should be able to fix those things an generate another patch file,
> but my git skills are very weak, so I guess you have the options:
> fix it up by hand, or teach me a little more git, so I can submit
> a better patch.
Here is patch on top of previous patch to fix the two mistakes:
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index ebc96c7..19d14bc 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -29,7 +29,6 @@ obj-$(CONFIG_DSCC4) += dscc4.o
obj-$(CONFIG_X25_ASY) += x25_asy.o
obj-$(CONFIG_LANMEDIA) += lmc/
-obj-$(CONFIG_CXT1E1) += cxt1e1/
obj-$(CONFIG_DLCI) += dlci.o
obj-$(CONFIG_SDLA) += sdla.o
diff --git a/drivers/staging/cxt1e1/Makefile b/drivers/staging/cxt1e1/Makefile
index edfad71..10020d7 100644
--- a/drivers/staging/cxt1e1/Makefile
+++ b/drivers/staging/cxt1e1/Makefile
@@ -1,4 +1,3 @@
-CONFIG_CXT1E1 = m
obj-$(CONFIG_CXT1E1) += cxt1e1.o
EXTRA_CFLAGS += -DSBE_PMCC4_ENABLE
thanks,
--
-Bob Beers
^ permalink raw reply related
* pull request: wireless-2.6 2009-11-02
From: John W. Linville @ 2009-11-02 21:40 UTC (permalink / raw)
To: davem; +Cc: linux-wireless, netdev, linux-kernel
Dave,
Another collection of fixes intended for 2.6.32...several
(almost-)one-liners, a b43 bounce-buffer fix, and a couple of USB IDs.
There are several from Johannes -- I'm sure there was sake involved... :-)
Please let me know if there are problems!
John
---
Individual patches are available here:
http://www.kernel.org/pub/linux/kernel/people/linville/wireless-2.6/
---
The following changes since commit 63ca2d74ea4f9c7a7ac082c915609a7b224908e7:
Ken Kawasaki (1):
pcnet_cs: add cis of PreMax PE-200 ethernet pcmcia card
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git master
David Woodhouse (1):
libertas if_usb: Fix crash on 64-bit machines
Johannes Berg (5):
mac80211: fix BSS leak
mac80211: fix addba timer
mac80211: fix reason code output endianness
cfg80211: fix NULL ptr deref
mac80211: check interface is down before type change
Luis R. Rodriguez (1):
ath9k: fix misplaced semicolon on rate control
Michael Buesch (1):
b43: Fix DMA TX bounce buffer copying
Xose Vazquez Perez (1):
rt73usb.c : more ids
Zhu Yi (1):
ipw2200: fix oops on missing firmware
drivers/net/wireless/ath/ath9k/rc.c | 2 +-
drivers/net/wireless/b43/dma.c | 15 +++++++++++++--
drivers/net/wireless/ipw2x00/ipw2100.c | 5 ++++-
drivers/net/wireless/ipw2x00/ipw2200.c | 2 ++
drivers/net/wireless/ipw2x00/libipw.h | 1 +
drivers/net/wireless/ipw2x00/libipw_module.c | 14 +++++++++-----
drivers/net/wireless/libertas/if_usb.c | 2 +-
drivers/net/wireless/rt2x00/rt73usb.c | 5 +++++
net/mac80211/agg-tx.c | 19 ++++++++++++-------
net/mac80211/cfg.c | 6 +++---
net/mac80211/ht.c | 2 +-
net/mac80211/ibss.c | 6 ++++--
net/wireless/sme.c | 7 +++++--
13 files changed, 61 insertions(+), 25 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index 16a2717..1895d63 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -679,7 +679,7 @@ static u8 ath_rc_get_highest_rix(struct ath_softc *sc,
return rate;
if (rate_table->info[rate].valid_single_stream &&
- !(ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG));
+ !(ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG))
return rate;
/* This should not happen */
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c
index 8701034..de4e804 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/b43/dma.c
@@ -1157,8 +1157,9 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot)
}
static int dma_tx_fragment(struct b43_dmaring *ring,
- struct sk_buff *skb)
+ struct sk_buff **in_skb)
{
+ struct sk_buff *skb = *in_skb;
const struct b43_dma_ops *ops = ring->ops;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
u8 *header;
@@ -1224,8 +1225,14 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
}
memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len);
+ memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb));
+ bounce_skb->dev = skb->dev;
+ skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb));
+ info = IEEE80211_SKB_CB(bounce_skb);
+
dev_kfree_skb_any(skb);
skb = bounce_skb;
+ *in_skb = bounce_skb;
meta->skb = skb;
meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) {
@@ -1355,7 +1362,11 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
* static, so we don't need to store it per frame. */
ring->queue_prio = skb_get_queue_mapping(skb);
- err = dma_tx_fragment(ring, skb);
+ /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing
+ * into the skb data or cb now. */
+ hdr = NULL;
+ info = NULL;
+ err = dma_tx_fragment(ring, &skb);
if (unlikely(err == -ENOKEY)) {
/* Drop this packet, as we don't have the encryption key
* anymore and must not transmit it unencrypted. */
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index 240cff1..a741d37 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -6325,8 +6325,10 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
fail:
if (dev) {
- if (registered)
+ if (registered) {
+ unregister_ieee80211(priv->ieee);
unregister_netdev(dev);
+ }
ipw2100_hw_stop_adapter(priv);
@@ -6383,6 +6385,7 @@ static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev)
/* Unregister the device first - this results in close()
* being called if the device is open. If we free storage
* first, then close() will crash. */
+ unregister_ieee80211(priv->ieee);
unregister_netdev(dev);
/* ipw2100_down will ensure that there is no more pending work
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c
index 8d58e6e..04341a2 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/ipw2x00/ipw2200.c
@@ -11821,6 +11821,7 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev,
if (err) {
IPW_ERROR("Failed to register promiscuous network "
"device (error %d).\n", err);
+ unregister_ieee80211(priv->ieee);
unregister_netdev(priv->net_dev);
goto out_remove_sysfs;
}
@@ -11871,6 +11872,7 @@ static void __devexit ipw_pci_remove(struct pci_dev *pdev)
mutex_unlock(&priv->mutex);
+ unregister_ieee80211(priv->ieee);
unregister_netdev(priv->net_dev);
if (priv->rxq) {
diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/ipw2x00/libipw.h
index bf45391..f42ade6 100644
--- a/drivers/net/wireless/ipw2x00/libipw.h
+++ b/drivers/net/wireless/ipw2x00/libipw.h
@@ -1020,6 +1020,7 @@ static inline int libipw_is_cck_rate(u8 rate)
/* ieee80211.c */
extern void free_ieee80211(struct net_device *dev, int monitor);
extern struct net_device *alloc_ieee80211(int sizeof_priv, int monitor);
+extern void unregister_ieee80211(struct libipw_device *ieee);
extern int libipw_change_mtu(struct net_device *dev, int new_mtu);
extern void libipw_networks_age(struct libipw_device *ieee,
diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c
index a0e9f6a..be5b809 100644
--- a/drivers/net/wireless/ipw2x00/libipw_module.c
+++ b/drivers/net/wireless/ipw2x00/libipw_module.c
@@ -235,16 +235,19 @@ void free_ieee80211(struct net_device *dev, int monitor)
libipw_networks_free(ieee);
/* free cfg80211 resources */
- if (!monitor) {
- wiphy_unregister(ieee->wdev.wiphy);
- kfree(ieee->a_band.channels);
- kfree(ieee->bg_band.channels);
+ if (!monitor)
wiphy_free(ieee->wdev.wiphy);
- }
free_netdev(dev);
}
+void unregister_ieee80211(struct libipw_device *ieee)
+{
+ wiphy_unregister(ieee->wdev.wiphy);
+ kfree(ieee->a_band.channels);
+ kfree(ieee->bg_band.channels);
+}
+
#ifdef CONFIG_LIBIPW_DEBUG
static int debug = 0;
@@ -330,3 +333,4 @@ module_init(libipw_init);
EXPORT_SYMBOL(alloc_ieee80211);
EXPORT_SYMBOL(free_ieee80211);
+EXPORT_SYMBOL(unregister_ieee80211);
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index 92bc8c5..3fac4ef 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -508,7 +508,7 @@ static int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
/* Fill the receive configuration URB and initialise the Rx call back */
usb_fill_bulk_urb(cardp->rx_urb, cardp->udev,
usb_rcvbulkpipe(cardp->udev, cardp->ep_in),
- (void *) (skb->tail + (size_t) IPFIELD_ALIGN_OFFSET),
+ skb->data + IPFIELD_ALIGN_OFFSET,
MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn,
cardp);
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index b8f5ee3..14e7bb2 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -2389,10 +2389,13 @@ static struct usb_device_id rt73usb_device_table[] = {
{ USB_DEVICE(0x13b1, 0x0023), USB_DEVICE_DATA(&rt73usb_ops) },
{ USB_DEVICE(0x13b1, 0x0028), USB_DEVICE_DATA(&rt73usb_ops) },
/* MSI */
+ { USB_DEVICE(0x0db0, 0x4600), USB_DEVICE_DATA(&rt73usb_ops) },
{ USB_DEVICE(0x0db0, 0x6877), USB_DEVICE_DATA(&rt73usb_ops) },
{ USB_DEVICE(0x0db0, 0x6874), USB_DEVICE_DATA(&rt73usb_ops) },
{ USB_DEVICE(0x0db0, 0xa861), USB_DEVICE_DATA(&rt73usb_ops) },
{ USB_DEVICE(0x0db0, 0xa874), USB_DEVICE_DATA(&rt73usb_ops) },
+ /* Ovislink */
+ { USB_DEVICE(0x1b75, 0x7318), USB_DEVICE_DATA(&rt73usb_ops) },
/* Ralink */
{ USB_DEVICE(0x04bb, 0x093d), USB_DEVICE_DATA(&rt73usb_ops) },
{ USB_DEVICE(0x148f, 0x2573), USB_DEVICE_DATA(&rt73usb_ops) },
@@ -2420,6 +2423,8 @@ static struct usb_device_id rt73usb_device_table[] = {
/* Planex */
{ USB_DEVICE(0x2019, 0xab01), USB_DEVICE_DATA(&rt73usb_ops) },
{ USB_DEVICE(0x2019, 0xab50), USB_DEVICE_DATA(&rt73usb_ops) },
+ /* WideTell */
+ { USB_DEVICE(0x7167, 0x3840), USB_DEVICE_DATA(&rt73usb_ops) },
/* Zcom */
{ USB_DEVICE(0x0cde, 0x001c), USB_DEVICE_DATA(&rt73usb_ops) },
/* ZyXEL */
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index bd765f3..b09948c 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -666,26 +666,25 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
state = &sta->ampdu_mlme.tid_state_tx[tid];
+ del_timer_sync(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
+
spin_lock_bh(&sta->lock);
- if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
- spin_unlock_bh(&sta->lock);
- return;
- }
+ if (!(*state & HT_ADDBA_REQUESTED_MSK))
+ goto timer_still_needed;
if (mgmt->u.action.u.addba_resp.dialog_token !=
sta->ampdu_mlme.tid_tx[tid]->dialog_token) {
- spin_unlock_bh(&sta->lock);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- return;
+ goto timer_still_needed;
}
- del_timer_sync(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "switched off addBA timer for tid %d \n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
+
if (le16_to_cpu(mgmt->u.action.u.addba_resp.status)
== WLAN_STATUS_SUCCESS) {
u8 curstate = *state;
@@ -699,5 +698,11 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
} else {
___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR);
}
+
+ goto out;
+
+ timer_still_needed:
+ add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
+ out:
spin_unlock_bh(&sta->lock);
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 5608f6c..7b5131b 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -72,6 +72,9 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
struct ieee80211_sub_if_data *sdata;
int ret;
+ if (netif_running(dev))
+ return -EBUSY;
+
if (!nl80211_type_check(type))
return -EINVAL;
@@ -81,9 +84,6 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
if (ret)
return ret;
- if (netif_running(sdata->dev))
- return -EBUSY;
-
if (ieee80211_vif_is_mesh(&sdata->vif) && params->mesh_id_len)
ieee80211_sdata_set_mesh_id(sdata,
params->mesh_id_len,
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 0891bfb..48ef1a2 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -153,7 +153,7 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
if (net_ratelimit())
printk(KERN_DEBUG "delba from %pM (%s) tid %d reason code %d\n",
mgmt->sa, initiator ? "initiator" : "recipient", tid,
- mgmt->u.action.u.delba.reason_code);
+ le16_to_cpu(mgmt->u.action.u.delba.reason_code));
#endif /* CONFIG_MAC80211_HT_DEBUG */
if (initiator == WLAN_BACK_INITIATOR)
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index ca8ecce..f1362f3 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -73,6 +73,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt;
u8 *pos;
struct ieee80211_supported_band *sband;
+ struct cfg80211_bss *bss;
u32 bss_change;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
@@ -177,8 +178,9 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
mod_timer(&ifibss->timer,
round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
- cfg80211_inform_bss_frame(local->hw.wiphy, local->hw.conf.channel,
- mgmt, skb->len, 0, GFP_KERNEL);
+ bss = cfg80211_inform_bss_frame(local->hw.wiphy, local->hw.conf.channel,
+ mgmt, skb->len, 0, GFP_KERNEL);
+ cfg80211_put_bss(bss);
cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
}
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index ece378d..9f0b280 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -165,7 +165,7 @@ void cfg80211_conn_work(struct work_struct *work)
struct cfg80211_registered_device *rdev =
container_of(work, struct cfg80211_registered_device, conn_work);
struct wireless_dev *wdev;
- u8 bssid[ETH_ALEN];
+ u8 bssid_buf[ETH_ALEN], *bssid = NULL;
rtnl_lock();
cfg80211_lock_rdev(rdev);
@@ -181,7 +181,10 @@ void cfg80211_conn_work(struct work_struct *work)
wdev_unlock(wdev);
continue;
}
- memcpy(bssid, wdev->conn->params.bssid, ETH_ALEN);
+ if (wdev->conn->params.bssid) {
+ memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
+ bssid = bssid_buf;
+ }
if (cfg80211_conn_do_work(wdev))
__cfg80211_connect_result(
wdev->netdev, bssid,
--
John W. Linville Someday the world will need a hero, and you
linville@tuxdriver.com might be all we have. Be ready.
^ permalink raw reply related
* Re: [PATCH net-next-2.6] ifb: RCU locking avoids touching dev refcount
From: Jarek Poplawski @ 2009-11-02 21:40 UTC (permalink / raw)
To: Eric Dumazet; +Cc: David S. Miller, Linux Netdev List
In-Reply-To: <4AEF504C.7000401@gmail.com>
On Mon, Nov 02, 2009 at 10:34:04PM +0100, Eric Dumazet wrote:
> Jarek Poplawski a écrit :
...
> > So, how is skb->dev protected here, above and below? It seems these
> > rcu read blocks need extending, don't they?
> >
>
> Well, this might be true, but we run under tasklet (softirq) with preemption disabled.
>
> We might move rcu_read_unlock() some lines down to not rely on this.
I think it's needed now at least for readability.
Jarek P.
^ permalink raw reply
* Re: [RFC][PATCH 3/3] cgroups: net_cls subsys as module
From: Randy Dunlap @ 2009-11-02 21:46 UTC (permalink / raw)
To: Ben Blum, netdev; +Cc: linux-kernel, containers, lizf, akpm, menage
In-Reply-To: <20091102213103.GD13692@andrew.cmu.edu>
On Mon, 2 Nov 2009 16:31:03 -0500 Ben Blum wrote:
> Allows the net_cls cgroup subsystem to be compiled as a module
Please cc: netdev on networking patches. [done]
> From: Ben Blum <bblum@andrew.cmu.edu>
>
> This patch modifies net/sched/cls_cgroup.c to allow the net_cls subsystem to
> be optionally compiled as a module instead of builtin. The cgroup_subsys
> struct is moved around a bit to allow the subsys_id to be either declared as a
> compile-time constant by the cgroup_subsys.h include in cgroup.h, or, if it's
> a module, initialized within the struct by cgroup_load_subsys.
>
> Signed-off-by: Ben Blum <bblum@andrew.cmu.edu>
> ---
>
> net/sched/Kconfig | 2 +-
> net/sched/cls_cgroup.c | 37 ++++++++++++++++++++++++++++---------
> 2 files changed, 29 insertions(+), 10 deletions(-)
>
>
> diff --git a/net/sched/Kconfig b/net/sched/Kconfig
> index 929218a..8489951 100644
> --- a/net/sched/Kconfig
> +++ b/net/sched/Kconfig
> @@ -328,7 +328,7 @@ config NET_CLS_FLOW
> module will be called cls_flow.
>
> config NET_CLS_CGROUP
> - bool "Control Group Classifier"
> + tristate "Control Group Classifier"
> select NET_CLS
> depends on CGROUPS
> ---help---
> diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c
> index e4877ca..df9723b 100644
> --- a/net/sched/cls_cgroup.c
> +++ b/net/sched/cls_cgroup.c
> @@ -24,6 +24,25 @@ struct cgroup_cls_state
> u32 classid;
> };
>
> +static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss,
> + struct cgroup *cgrp);
> +static void cgrp_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp);
> +static int cgrp_populate(struct cgroup_subsys *ss, struct cgroup *cgrp);
> +
> +struct cgroup_subsys net_cls_subsys = {
> + .name = "net_cls",
> + .create = cgrp_create,
> + .destroy = cgrp_destroy,
> + .populate = cgrp_populate,
> +#ifdef CONFIG_NET_CLS_CGROUP
> + .subsys_id = net_cls_subsys_id,
> +#else
> +#define net_cls_subsys_id net_cls_subsys.subsys_id
> +#endif
> + .module = THIS_MODULE,
> +};
> +
> +
> static inline struct cgroup_cls_state *cgrp_cls_state(struct cgroup *cgrp)
> {
> return container_of(cgroup_subsys_state(cgrp, net_cls_subsys_id),
> @@ -79,14 +98,6 @@ static int cgrp_populate(struct cgroup_subsys *ss, struct cgroup *cgrp)
> return cgroup_add_files(cgrp, ss, ss_files, ARRAY_SIZE(ss_files));
> }
>
> -struct cgroup_subsys net_cls_subsys = {
> - .name = "net_cls",
> - .create = cgrp_create,
> - .destroy = cgrp_destroy,
> - .populate = cgrp_populate,
> - .subsys_id = net_cls_subsys_id,
> -};
> -
> struct cls_cgroup_head
> {
> u32 handle;
> @@ -277,12 +288,20 @@ static struct tcf_proto_ops cls_cgroup_ops __read_mostly = {
>
> static int __init init_cgroup_cls(void)
> {
> - return register_tcf_proto_ops(&cls_cgroup_ops);
> + int ret = register_tcf_proto_ops(&cls_cgroup_ops);
> + if (ret)
> + return ret;
> + ret = cgroup_load_subsys(&net_cls_subsys);
> + if (ret)
> + unregister_tcf_proto_ops(&cls_cgroup_ops);
> + return ret;
> }
>
> static void __exit exit_cgroup_cls(void)
> {
> unregister_tcf_proto_ops(&cls_cgroup_ops);
> + /* TODO: unload subsystem. for now, the try_module_get in load_subsys
> + * prevents us from getting here. */
> }
>
> module_init(init_cgroup_cls);
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
---
~Randy
^ permalink raw reply
* Re: pull request: wireless-2.6 2009-11-02
From: Johannes Berg @ 2009-11-02 21:59 UTC (permalink / raw)
To: John W. Linville; +Cc: davem, linux-wireless, netdev, linux-kernel
In-Reply-To: <20091102214022.GM14046@tuxdriver.com>
[-- Attachment #1: Type: text/plain, Size: 258 bytes --]
On Mon, 2009-11-02 at 16:40 -0500, John W. Linville wrote:
> There are several from Johannes -- I'm sure there was sake involved... :-)
More likely there was sake involved when I did that patch adding that
NULL-ptr deref!
Thanks :)
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* [PATCHv6 0/3] vhost: a kernel-level virtio server
From: Michael S. Tsirkin @ 2009-11-02 22:25 UTC (permalink / raw)
To: netdev, virtualization, kvm, linux-kernel, mingo, linux-mm, akpm
Rusty, ok, I think I've addressed your comments so far here. In
particular I have added write logging for live migration, indirect
buffers and virtio net header (enables gso). I'd like this to go
into linux-next, through your tree, and hopefully 2.6.33.
What do you think?
---
This implements vhost: a kernel-level backend for virtio,
The main motivation for this work is to reduce virtualization
overhead for virtio by removing system calls on data path,
without guest changes. For virtio-net, this removes up to
4 system calls per packet: vm exit for kick, reentry for kick,
iothread wakeup for packet, interrupt injection for packet.
This driver is pretty minimal, but it's fully functional (including
migration support interfaces), and already shows performance (especially
latency) improvement over userspace.
Some more detailed description attached to the patch itself.
The patches apply to both 2.6.32-rc5 and kvm.git. I'd like them to go
into linux-next if possible. Please comment.
Changelog from v5:
- tun support
- backends with virtio net header support (enables GSO, checksum etc)
- 32 bit compat fixed
- support indirect buffers, tx exit mitigation,
tx interrupt mitigation
- support write logging (allows migration without virtio ring code in userspace)
Changelog from v4:
- disable rx notification when have rx buffers
- addressed all comments from Rusty's review
- copy bugfixes from lguest commits:
ebf9a5a99c1a464afe0b4dfa64416fc8b273bc5c
e606490c440900e50ccf73a54f6fc6150ff40815
Changelog from v3:
- checkpatch fixes
Changelog from v2:
- Comments on RCU usage
- Compat ioctl support
- Make variable static
- Copied more idiomatic english from Rusty
Changes from v1:
- Move use_mm/unuse_mm from fs/aio.c to mm instead of copying.
- Reorder code to avoid need for forward declarations
- Kill a couple of debugging printks
Michael S. Tsirkin (3):
tun: export underlying socket
mm: export use_mm/unuse_mm to modules
vhost_net: a kernel-level virtio server
MAINTAINERS | 10 +
arch/x86/kvm/Kconfig | 1 +
drivers/Makefile | 1 +
drivers/net/tun.c | 101 ++++-
drivers/vhost/Kconfig | 11 +
drivers/vhost/Makefile | 2 +
drivers/vhost/net.c | 634 +++++++++++++++++++++++++++++
drivers/vhost/vhost.c | 968 ++++++++++++++++++++++++++++++++++++++++++++
drivers/vhost/vhost.h | 158 +++++++
include/linux/Kbuild | 1 +
include/linux/if_tun.h | 14 +
include/linux/miscdevice.h | 1 +
include/linux/vhost.h | 126 ++++++
mm/mmu_context.c | 3 +
14 files changed, 2012 insertions(+), 19 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
--
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
* [PATCHv6 1/3] tun: export underlying socket
From: Michael S. Tsirkin @ 2009-11-02 22:26 UTC (permalink / raw)
To: netdev, virtualization, kvm, linux-kernel, mingo, linux-mm, akpm
In-Reply-To: <cover.1257193660.git.mst@redhat.com>
Tun device looks similar to a packet socket
in that both pass complete frames from/to userspace.
This patch fills in enough fields in the socket underlying tun driver
to support sendmsg/recvmsg operations, and message flags
MSG_TRUNC and MSG_DONTWAIT, and exports access to this socket
to modules. Regular read/write behaviour is unchanged.
This way, code using raw sockets to inject packets
into a physical device, can support injecting
packets into host network stack almost without modification.
First user of this interface will be vhost virtualization
accelerator.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
This was posted to netdev separately as well.
Assuming it gets acked, and assuming it's okay with davem,
I think it makes sense to merge this patch through Rusty's
tree because vhost is the first user of the new interface.
Posted here for completeness.
drivers/net/tun.c | 101 +++++++++++++++++++++++++++++++++++++++---------
include/linux/if_tun.h | 14 +++++++
2 files changed, 96 insertions(+), 19 deletions(-)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 4fdfa2a..18f8876 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -144,6 +144,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file)
err = 0;
tfile->tun = tun;
tun->tfile = tfile;
+ tun->socket.file = file;
dev_hold(tun->dev);
sock_hold(tun->socket.sk);
atomic_inc(&tfile->count);
@@ -158,6 +159,7 @@ static void __tun_detach(struct tun_struct *tun)
/* Detach from net device */
netif_tx_lock_bh(tun->dev);
tun->tfile = NULL;
+ tun->socket.file = NULL;
netif_tx_unlock_bh(tun->dev);
/* Drop read queue */
@@ -387,7 +389,8 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
/* Notify and wake up reader process */
if (tun->flags & TUN_FASYNC)
kill_fasync(&tun->fasync, SIGIO, POLL_IN);
- wake_up_interruptible(&tun->socket.wait);
+ wake_up_interruptible_poll(&tun->socket.wait, POLLIN |
+ POLLRDNORM | POLLRDBAND);
return NETDEV_TX_OK;
drop:
@@ -743,7 +746,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
len = min_t(int, skb->len, len);
skb_copy_datagram_const_iovec(skb, 0, iv, total, len);
- total += len;
+ total += skb->len;
tun->dev->stats.tx_packets++;
tun->dev->stats.tx_bytes += len;
@@ -751,34 +754,23 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
return total;
}
-static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
- unsigned long count, loff_t pos)
+static ssize_t tun_do_read(struct tun_struct *tun,
+ struct kiocb *iocb, const struct iovec *iv,
+ ssize_t len, int noblock)
{
- struct file *file = iocb->ki_filp;
- struct tun_file *tfile = file->private_data;
- struct tun_struct *tun = __tun_get(tfile);
DECLARE_WAITQUEUE(wait, current);
struct sk_buff *skb;
- ssize_t len, ret = 0;
-
- if (!tun)
- return -EBADFD;
+ ssize_t ret = 0;
DBG(KERN_INFO "%s: tun_chr_read\n", tun->dev->name);
- len = iov_length(iv, count);
- if (len < 0) {
- ret = -EINVAL;
- goto out;
- }
-
add_wait_queue(&tun->socket.wait, &wait);
while (len) {
current->state = TASK_INTERRUPTIBLE;
/* Read frames from the queue */
if (!(skb=skb_dequeue(&tun->socket.sk->sk_receive_queue))) {
- if (file->f_flags & O_NONBLOCK) {
+ if (noblock) {
ret = -EAGAIN;
break;
}
@@ -805,6 +797,27 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
current->state = TASK_RUNNING;
remove_wait_queue(&tun->socket.wait, &wait);
+ return ret;
+}
+
+static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
+ unsigned long count, loff_t pos)
+{
+ struct file *file = iocb->ki_filp;
+ struct tun_file *tfile = file->private_data;
+ struct tun_struct *tun = __tun_get(tfile);
+ ssize_t len, ret;
+
+ if (!tun)
+ return -EBADFD;
+ len = iov_length(iv, count);
+ if (len < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = tun_do_read(tun, iocb, iv, len, file->f_flags & O_NONBLOCK);
+ ret = min_t(ssize_t, ret, len);
out:
tun_put(tun);
return ret;
@@ -847,7 +860,8 @@ static void tun_sock_write_space(struct sock *sk)
return;
if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
- wake_up_interruptible_sync(sk->sk_sleep);
+ wake_up_interruptible_sync_poll(sk->sk_sleep, POLLOUT |
+ POLLWRNORM | POLLWRBAND);
tun = container_of(sk, struct tun_sock, sk)->tun;
kill_fasync(&tun->fasync, SIGIO, POLL_OUT);
@@ -858,6 +872,37 @@ static void tun_sock_destruct(struct sock *sk)
free_netdev(container_of(sk, struct tun_sock, sk)->tun->dev);
}
+static int tun_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len)
+{
+ struct tun_struct *tun = container_of(sock, struct tun_struct, socket);
+ return tun_get_user(tun, m->msg_iov, total_len,
+ m->msg_flags & MSG_DONTWAIT);
+}
+
+static int tun_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len,
+ int flags)
+{
+ struct tun_struct *tun = container_of(sock, struct tun_struct, socket);
+ int ret;
+ if (flags & ~(MSG_DONTWAIT|MSG_TRUNC))
+ return -EINVAL;
+ ret = tun_do_read(tun, iocb, m->msg_iov, total_len,
+ flags & MSG_DONTWAIT);
+ if (ret > total_len) {
+ m->msg_flags |= MSG_TRUNC;
+ ret = flags & MSG_TRUNC ? ret : total_len;
+ }
+ return ret;
+}
+
+/* Ops structure to mimic raw sockets with tun */
+static const struct proto_ops tun_socket_ops = {
+ .sendmsg = tun_sendmsg,
+ .recvmsg = tun_recvmsg,
+};
+
static struct proto tun_proto = {
.name = "tun",
.owner = THIS_MODULE,
@@ -986,6 +1031,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
goto err_free_dev;
init_waitqueue_head(&tun->socket.wait);
+ tun->socket.ops = &tun_socket_ops;
sock_init_data(&tun->socket, sk);
sk->sk_write_space = tun_sock_write_space;
sk->sk_sndbuf = INT_MAX;
@@ -1489,6 +1535,23 @@ static void tun_cleanup(void)
rtnl_link_unregister(&tun_link_ops);
}
+/* Get an underlying socket object from tun file. Returns error unless file is
+ * attached to a device. The returned object works like a packet socket, it
+ * can be used for sock_sendmsg/sock_recvmsg. The caller is responsible for
+ * holding a reference to the file for as long as the socket is in use. */
+struct socket *tun_get_socket(struct file *file)
+{
+ struct tun_struct *tun;
+ if (file->f_op != &tun_fops)
+ return ERR_PTR(-EINVAL);
+ tun = tun_get(file);
+ if (!tun)
+ return ERR_PTR(-EBADFD);
+ tun_put(tun);
+ return &tun->socket;
+}
+EXPORT_SYMBOL_GPL(tun_get_socket);
+
module_init(tun_init);
module_exit(tun_cleanup);
MODULE_DESCRIPTION(DRV_DESCRIPTION);
diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h
index 3f5fd52..404abe0 100644
--- a/include/linux/if_tun.h
+++ b/include/linux/if_tun.h
@@ -86,4 +86,18 @@ struct tun_filter {
__u8 addr[0][ETH_ALEN];
};
+#ifdef __KERNEL__
+#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
+struct socket *tun_get_socket(struct file *);
+#else
+#include <linux/err.h>
+#include <linux/errno.h>
+struct file;
+struct socket;
+static inline struct socket *tun_get_socket(struct file *f)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif /* CONFIG_TUN */
+#endif /* __KERNEL__ */
#endif /* __IF_TUN_H */
--
1.6.5.rc2
--
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
* [PATCHv6 2/3] mm: export use_mm/unuse_mm to modules
From: Michael S. Tsirkin @ 2009-11-02 22:26 UTC (permalink / raw)
To: netdev, virtualization, kvm, linux-kernel, mingo, linux-mm, akpm
In-Reply-To: <cover.1257193660.git.mst@redhat.com>
vhost net module wants to do copy to/from user from a kernel thread,
which needs use_mm. Export it to modules.
Acked-by: Andrea Arcangeli <aarcange@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
mm/mmu_context.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/mm/mmu_context.c b/mm/mmu_context.c
index ded9081..0777654 100644
--- a/mm/mmu_context.c
+++ b/mm/mmu_context.c
@@ -5,6 +5,7 @@
#include <linux/mm.h>
#include <linux/mmu_context.h>
+#include <linux/module.h>
#include <linux/sched.h>
#include <asm/mmu_context.h>
@@ -37,6 +38,7 @@ void use_mm(struct mm_struct *mm)
if (active_mm != mm)
mmdrop(active_mm);
}
+EXPORT_SYMBOL_GPL(use_mm);
/*
* unuse_mm
@@ -56,3 +58,4 @@ void unuse_mm(struct mm_struct *mm)
enter_lazy_tlb(mm, tsk);
task_unlock(tsk);
}
+EXPORT_SYMBOL_GPL(unuse_mm);
--
1.6.5.rc2
--
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
* [PATCHv6 3/3] vhost_net: a kernel-level virtio server
From: Michael S. Tsirkin @ 2009-11-02 22:29 UTC (permalink / raw)
To: netdev, virtualization, kvm, linux-kernel, mingo, linux-mm, akpm
In-Reply-To: <cover.1257193660.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
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
MAINTAINERS | 10 +
arch/x86/kvm/Kconfig | 1 +
drivers/Makefile | 1 +
drivers/vhost/Kconfig | 11 +
drivers/vhost/Makefile | 2 +
drivers/vhost/net.c | 634 +++++++++++++++++++++++++++++
drivers/vhost/vhost.c | 968 ++++++++++++++++++++++++++++++++++++++++++++
drivers/vhost/vhost.h | 158 +++++++
include/linux/Kbuild | 1 +
include/linux/miscdevice.h | 1 +
include/linux/vhost.h | 126 ++++++
11 files changed, 1913 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 69e31aa..58b7a21 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5605,6 +5605,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..d955406
--- /dev/null
+++ b/drivers/vhost/Kconfig
@@ -0,0 +1,11 @@
+config VHOST_NET
+ tristate "Host kernel accelerator for virtio net"
+ depends on NET && EVENTFD
+ ---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..ecf86ec
--- /dev/null
+++ b/drivers/vhost/net.c
@@ -0,0 +1,634 @@
+/* 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_no_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, &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 (vhost_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: "
+ "%ld 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_trigger(&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_no_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, &out, &in,
+ vq_log, &log);
+ /* OK, now we need to know about added descriptors. */
+ if (head == vq->num && vhost_notify(vq))
+ /* They could have slipped one in as we were doing that:
+ * check again. */
+ continue;
+ /* Nothing new? Wait for eventfd to tell us they refilled. */
+ if (head == vq->num)
+ break;
+ /* We don't need to be notified again. */
+ vhost_no_notify(vq);
+ 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_trigger(&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 = kzalloc(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, int index)
+{
+ if (!n->vqs[index].private_data)
+ return;
+ if (index == 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, int index)
+{
+ struct socket *sock = n->vqs[index].private_data;
+ if (!sock)
+ return;
+ if (index == 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, int index)
+{
+ struct socket *sock;
+
+ mutex_lock(&n->vqs[index].mutex);
+ sock = n->vqs[index].private_data;
+ vhost_net_disable_vq(n, index);
+ rcu_assign_pointer(n->vqs[index].private_data, NULL);
+ mutex_unlock(&n->vqs[index].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, VHOST_NET_VQ_TX);
+ *rx_sock = vhost_net_stop_vq(n, 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 = NULL;
+ struct vhost_virtqueue *vq;
+ int r;
+
+ mutex_lock(&n->dev.mutex);
+ r = vhost_dev_check_owner(&n->dev);
+ if (r)
+ goto done;
+
+ if (index >= VHOST_NET_VQ_MAX) {
+ r = -ENOBUFS;
+ goto done;
+ }
+ vq = n->vqs + index;
+ mutex_lock(&vq->mutex);
+ sock = get_socket(fd);
+ if (IS_ERR(sock)) {
+ r = PTR_ERR(sock);
+ goto done;
+ }
+
+ /* start polling new socket */
+ oldsock = vq->private_data;
+ if (sock == oldsock)
+ goto done;
+
+ vhost_net_disable_vq(n, index);
+ rcu_assign_pointer(vq->private_data, sock);
+ vhost_net_enable_vq(n, index);
+ mutex_unlock(&vq->mutex);
+done:
+ mutex_unlock(&n->dev.mutex);
+ if (oldsock) {
+ vhost_net_flush_vq(n, index);
+ fput(oldsock->file);
+ }
+ return r;
+}
+
+static long vhost_net_reset_owner(struct vhost_net *n)
+{
+ struct socket *tx_sock = NULL;
+ struct socket *rx_sock = NULL;
+ long r;
+ mutex_lock(&n->dev.mutex);
+ r = vhost_dev_check_owner(&n->dev);
+ if (r)
+ goto done;
+ vhost_net_stop(n, &tx_sock, &rx_sock);
+ vhost_net_flush(n);
+ r = 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 r;
+}
+
+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_unlock(&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_ACK_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..6db2e63
--- /dev/null
+++ b/drivers/vhost/vhost.c
@@ -0,0 +1,968 @@
+/* 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);
+}
+
+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);
+
+ for (i = 0; i < dev->nvqs; ++i) {
+ dev->vqs[i].dev = dev;
+ mutex_init(&dev->vqs[i].mutex);
+ 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 = kzalloc(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);
+ dev->vqs[i].error_ctx = NULL;
+ dev->vqs[i].error = NULL;
+ dev->vqs[i].kick = NULL;
+ dev->vqs[i].call_ctx = NULL;
+ dev->vqs[i].call = NULL;
+ }
+ 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)
+{
+ int r = put_user(vq->used_flags, &vq->used->flags);
+ if (r)
+ return r;
+ return get_user(vq->last_used_idx, &vq->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 > 0xffff) {
+ 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->avail_idx = vq->last_avail_idx = s.num;
+ 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_DESC:
+ r = copy_from_user(&a, argp, sizeof a);
+ if (r < 0)
+ break;
+ if (a.padding) {
+ r = -EOPNOTSUPP;
+ break;
+ }
+ if ((u64)(unsigned long)a.user_addr != a.user_addr) {
+ r = -EFAULT;
+ break;
+ }
+ vq->desc = (void __user *)(unsigned long)a.user_addr;
+ break;
+ case VHOST_SET_VRING_AVAIL:
+ r = copy_from_user(&a, argp, sizeof a);
+ if (r < 0)
+ break;
+ if (a.padding) {
+ r = -EOPNOTSUPP;
+ break;
+ }
+ if ((u64)(unsigned long)a.user_addr != a.user_addr) {
+ r = -EFAULT;
+ break;
+ }
+ if (a.user_addr & (sizeof *vq->avail->ring - 1)) {
+ r = -EINVAL;
+ break;
+ }
+ vq->avail = (void __user *)(unsigned long)a.user_addr;
+ /* Forget the cached index value. */
+ vq->avail_idx = vq->last_avail_idx;
+ break;
+ case VHOST_SET_VRING_USED:
+ r = copy_from_user(&a, argp, sizeof a);
+ if (r < 0)
+ break;
+ if (a.padding) {
+ r = -EOPNOTSUPP;
+ break;
+ }
+ if ((u64)(unsigned long)a.user_addr != a.user_addr) {
+ r = -EFAULT;
+ break;
+ }
+ if (a.user_addr & (sizeof *vq->used->ring - 1)) {
+ r = -EINVAL;
+ break;
+ }
+ vq->used = (void __user *)(unsigned long)a.user_addr;
+ r = init_used(vq);
+ if (r)
+ break;
+ break;
+ case VHOST_SET_VRING_LOG:
+ r = copy_from_user(&a, argp, sizeof a);
+ if (r < 0)
+ break;
+ if (a.padding) {
+ r = -EOPNOTSUPP;
+ break;
+ }
+ if (a.user_addr == VHOST_VRING_LOG_DISABLE) {
+ vq->log_used = false;
+ break;
+ }
+ if (a.user_addr & (sizeof *vq->used->ring - 1)) {
+ r = -EINVAL;
+ break;
+ }
+ vq->log_used = true;
+ vq->log_addr = a.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 = get_user(p, (u64 __user *)argp);
+ 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_USERO);
+ 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)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 *)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 *)
+ (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 -1 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 -1;
+
+ /* 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 *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 > 0x10000) {
+ 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,
+ VHOST_NET_MAX_SG - 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 *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, 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,
+ VHOST_NET_MAX_SG - 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 send them an interrupt, using vq->call. */
+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 sends the interrupt for this virtqueue */
+void vhost_trigger_irq(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 send one, 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;
+
+ /* Send the Guest an interrupt 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_trigger(struct vhost_dev *dev,
+ struct vhost_virtqueue *vq,
+ unsigned int head, int len)
+{
+ vhost_add_used(vq, head, len);
+ vhost_trigger_irq(dev, vq);
+}
+
+/* OK, now we need to know about added descriptors. */
+bool vhost_notify(struct vhost_virtqueue *vq)
+{
+ 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 disable notification: %d\n", r);
+ /* They could have slipped one in as we were doing that: make
+ * sure it's written, tell caller it needs to check again. */
+ mb();
+ return true;
+}
+
+/* We don't need to be notified again. */
+void vhost_no_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: %d\n", 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..ae66b5e
--- /dev/null
+++ b/drivers/vhost/vhost.h
@@ -0,0 +1,158 @@
+#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 {
+ 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 *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_trigger_irq(struct vhost_dev *, struct vhost_virtqueue *);
+void vhost_add_used_and_trigger(struct vhost_dev *, struct vhost_virtqueue *,
+ unsigned int head, int len);
+void vhost_no_notify(struct vhost_virtqueue *);
+bool vhost_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 3f384d4..b6335c6 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..f66142f
--- /dev/null
+++ b/include/linux/vhost.h
@@ -0,0 +1,126 @@
+#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;
+ unsigned int padding;
+ __u64 user_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_ACK_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. These parameters can not be modified while ring is running
+ * (bound to a device). */
+/* Set number of descriptors in ring */
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+/* Start of array of descriptors (virtually contiguous) */
+#define VHOST_SET_VRING_DESC _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+/* Used structure address. Must be 32 bit aligned */
+#define VHOST_SET_VRING_USED _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_addr)
+/* Available structure address. Must be 16 bit aligned */
+#define VHOST_SET_VRING_AVAIL _IOW(VHOST_VIRTIO, 0x13, struct vhost_vring_addr)
+/* Base value where queue looks for available descriptors */
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x14, struct vhost_vring_state)
+/* Get accessor: reads index, writes value in num */
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x14, struct vhost_vring_state)
+
+/* Logging support. Can be modified while ring is running. */
+/* Log writes to used structure, at offset calculated from specified address.
+ * Address must be 32 bit aligned. Pass 0x1 to disable logging. */
+#define VHOST_SET_VRING_LOG _IOW(VHOST_VIRTIO, 0x18, struct vhost_vring_addr)
+#define VHOST_VRING_LOG_DISABLE (0x1)
+
+/* 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.rc2
--
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
* Re: [PATCH net-next-2.6] ipv6: no more dev_put() in datagram_send_ctl()
From: Paul E. McKenney @ 2009-11-02 22:35 UTC (permalink / raw)
To: Eric Dumazet; +Cc: David S. Miller, Linux Netdev List
In-Reply-To: <4AEEC0A2.1070502@gmail.com>
On Mon, Nov 02, 2009 at 12:21:06PM +0100, Eric Dumazet wrote:
> Avoids touching device refcount in datagram_send_ctl(), thanks to RCU
Quite a series of patches!!! I am impressed!!!
Thanx, Paul
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> ---
> net/ipv6/datagram.c | 14 +++++++++-----
> 1 files changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
> index 9f70452..e6f9cdf 100644
> --- a/net/ipv6/datagram.c
> +++ b/net/ipv6/datagram.c
> @@ -537,12 +537,17 @@ int datagram_send_ctl(struct net *net,
>
> addr_type = __ipv6_addr_type(&src_info->ipi6_addr);
>
> + rcu_read_lock();
> if (fl->oif) {
> - dev = dev_get_by_index(net, fl->oif);
> - if (!dev)
> + dev = dev_get_by_index_rcu(net, fl->oif);
> + if (!dev) {
> + rcu_read_unlock();
> return -ENODEV;
> - } else if (addr_type & IPV6_ADDR_LINKLOCAL)
> + }
> + } else if (addr_type & IPV6_ADDR_LINKLOCAL) {
> + rcu_read_unlock();
> return -EINVAL;
> + }
>
> if (addr_type != IPV6_ADDR_ANY) {
> int strict = __ipv6_addr_src_scope(addr_type) <= IPV6_ADDR_SCOPE_LINKLOCAL;
> @@ -553,8 +558,7 @@ int datagram_send_ctl(struct net *net,
> ipv6_addr_copy(&fl->fl6_src, &src_info->ipi6_addr);
> }
>
> - if (dev)
> - dev_put(dev);
> + rcu_read_unlock();
>
> if (err)
> goto exit_f;
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH net-next] iwmc3200bt: Add iwmc3200 bluetooth driver
From: Tomas Winkler @ 2009-11-02 23:36 UTC (permalink / raw)
To: davem, marcel, netdev, linux-bluetooth, linux-mmc
Cc: guy.cohen, ron.rindjunsky, Tomas Winkler
Add Intel Wireless MultiCom 3200 SDIO BT driver
IWMC3200 is 4Wireless Com CHIP (GPS/BT/WiFi/WiMAX).
wmc3200bt is derived from btsdio driver
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
drivers/bluetooth/Kconfig | 16 ++
drivers/bluetooth/Makefile | 2 +
drivers/bluetooth/iwmc3200bt.c | 553 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 571 insertions(+), 0 deletions(-)
create mode 100644 drivers/bluetooth/iwmc3200bt.c
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 652367a..e520889 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -195,5 +195,21 @@ config BT_MRVL_SDIO
Say Y here to compile support for Marvell BT-over-SDIO driver
into the kernel or say M to compile it as module.
+config BT_IWMC3200
+ tristate "Intel Wireless MultiCom 3200 SDIO BT driver"
+ depends on IWMC3200TOP && EXPERIMENTAL
+ help
+ Intel Wireless MultiCom 3200 SDIO Bluetooth driver
+
+ Say Y here to compile support for IWMC3200 SDIO BT driver
+ into the kernel or say M to compile it as module.
+
+config BT_IWMC3200_DEBUG
+ bool "Enable verbose debugging for iwmc3200bt"
+ depends on BT_IWMC3200
+ help
+ Say Y here to enable IWMC3200 SDIO BT driver
+ verbose debugging
+
endmenu
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index b3f57d2..975036e 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -21,6 +21,8 @@ obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
+obj-$(CONFIG_BT_IWMC3200) += iwmc3200bt.o
+
hci_uart-y := hci_ldisc.o
hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o
hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o
diff --git a/drivers/bluetooth/iwmc3200bt.c b/drivers/bluetooth/iwmc3200bt.c
new file mode 100644
index 0000000..5aed7a3
--- /dev/null
+++ b/drivers/bluetooth/iwmc3200bt.c
@@ -0,0 +1,553 @@
+/*
+ * ibtsdio - Intel Wireless MultiCom 3200 Bluetooth SDIO Driver
+ * drivers/bluetooth/ibtsdio.c
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * Based on drivers/bluetooth/btsdio.c
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ * Copyright (C) 2007 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+#ifdef CONFIG_BT_IWMC3200_DEBUG
+#define IBT_DBG(func, fmt, arg...) \
+ dev_dbg(&((func)->dev), "%s: " fmt "\n" , __func__ , ## arg)
+#define IBT_ERR(func, fmt, arg...) \
+ dev_err(&((func)->dev), "%s: " fmt "\n" , __func__ , ## arg)
+#define IBT_DUMP(func, buf, size) do { \
+ char prefix_str[30 + 1]; \
+ scnprintf(prefix_str, 30, "%s %s", \
+ dev_driver_string(&((func)->dev)), \
+ dev_name(&((func)->dev))); \
+ print_hex_dump(KERN_DEBUG, prefix_str, DUMP_PREFIX_OFFSET, \
+ 16, 1, buf, size, false); \
+} while (0)
+
+#define VD "-d"
+#else
+#define IBT_DBG(func, fmt, arg...)
+#define IBT_ERR(func, fmt, arg...)
+#define IBT_DUMP(func, buf, size)
+#define VD
+#endif /* CONFIG_BT_IWMC3200_DEBUG */
+
+#ifdef REPOSITORY_LABEL
+#define RL REPOSITORY_LABEL
+#else
+#define RL local
+#endif
+
+
+#define IWMCBT_VERSION "0.1.6"
+
+#define DRIVER_VERSION IWMCBT_VERSION "-" __stringify(RL) VD
+
+
+#define IWMC_BT_SDIO_DEVID (0x1406)
+
+static const struct sdio_device_id btsdio_table[] = {
+ /* IWMC3200BT (0x1406) */
+ { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1406)},
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(sdio, btsdio_table);
+
+struct btsdio_data {
+ struct hci_dev *hdev;
+ struct sdio_func *func;
+
+ struct work_struct work;
+
+ struct sk_buff_head txq;
+ unsigned char tx_buf[2048];
+ unsigned char rx_buf[2048];
+};
+
+#define REG_RDAT 0x00 /* Receiver Data */
+#define REG_TDAT 0x00 /* Transmitter Data */
+#define REG_PC_RRT 0x10 /* Read Packet Control */
+#define REG_PC_WRT 0x11 /* Write Packet Control */
+#define REG_RTC_STAT 0x12 /* Retry Control Status */
+#define REG_RTC_SET 0x12 /* Retry Control Set */
+#define REG_INTRD 0x13 /* Interrupt Indication */
+#define REG_CL_INTRD 0x13 /* Interrupt Clear */
+#define REG_EN_INTRD 0x14 /* Interrupt Enable */
+#define REG_MD_STAT 0x20 /* Bluetooth Mode Status */
+#define REG_H2D_COUNT_L 0x2C
+#define REG_H2D_COUNT_H 0x2D
+
+#ifdef CONFIG_BT_IWMC3200_DEBUG
+static char *type_names[] = {
+ "Illegal",
+ "HCI_COMMAND_PKT",
+ "HCI_ACLDATA_PKT",
+ "HCI_SCODATA_PKT",
+ "HCI_EVENT_PKT"
+};
+#endif /* CONFIG_BT_IWMC3200_DEBUG */
+
+
+static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb)
+{
+ int err;
+ int hdr_sz = 7, pkt_sz = 0;
+
+ IBT_DBG(data->func, "%s", data->hdev->name);
+ memset(data->tx_buf, 0, 2048);
+
+ /* Prepend Type-A header */
+ skb_push(skb, 4);
+ skb->data[0] = (skb->len & 0x0000ff);
+ skb->data[1] = (skb->len & 0x00ff00) >> 8;
+ skb->data[2] = (skb->len & 0xff0000) >> 16;
+ skb->data[3] = bt_cb(skb)->pkt_type;
+
+ hdr_sz = 4 + 3; /* (4) btsdio header + (3) hci cmd hdr */
+ memcpy(data->tx_buf, skb->data, hdr_sz);
+
+ IBT_DBG(data->func, "requested to send packet of type %s\n",
+ type_names[data->tx_buf[3]]);
+ if (bt_cb(skb)->pkt_type == HCI_ACLDATA_PKT) {
+ data->tx_buf[7] = skb->data[7];
+ hdr_sz++;
+ }
+ memcpy(data->tx_buf + 8, skb->data + hdr_sz, skb->len - hdr_sz);
+ pkt_sz = ALIGN(skb->len, 256);
+ IBT_DBG(data->func, "padded size to send %d bytes\n", pkt_sz);
+ IBT_DUMP(data->func, data->tx_buf, pkt_sz);
+
+ err = sdio_writesb(data->func, REG_TDAT, data->tx_buf, pkt_sz);
+ if (err < 0) {
+ IBT_ERR(data->func, "sdio_writesb exited with error %d\n", err);
+ skb_pull(skb, 4);
+ sdio_writeb(data->func, 0x01, REG_PC_WRT, NULL);
+ return err;
+ }
+ IBT_DBG(data->func, "sdio_writesb exited with success %d\n", err);
+ data->hdev->stat.byte_tx += skb->len;
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void btsdio_work(struct work_struct *work)
+{
+ struct btsdio_data *data = container_of(work, struct btsdio_data, work);
+ struct sk_buff *skb;
+ int err;
+
+ IBT_DBG(data->func, "%s", data->hdev->name);
+
+ sdio_claim_host(data->func);
+
+ while ((skb = skb_dequeue(&data->txq))) {
+ err = btsdio_tx_packet(data, skb);
+ if (err < 0) {
+ data->hdev->stat.err_tx++;
+ skb_queue_head(&data->txq, skb);
+ break;
+ }
+ }
+
+ sdio_release_host(data->func);
+}
+
+static int btsdio_rx_packet(struct btsdio_data *data, int padded_len)
+{
+ struct sk_buff *skb;
+ int err, len, hdr_sz;
+
+ IBT_DBG(data->func, "%s", data->hdev->name);
+
+ memset(data->rx_buf, 0, 2048);
+ err = sdio_readsb(data->func, data->rx_buf,
+ REG_RDAT, padded_len);
+ if (err < 0) {
+ IBT_ERR(data->func, "%s with error %d\n", __func__, err);
+ return err;
+ }
+ IBT_DUMP(data->func, data->rx_buf, padded_len);
+
+ len = data->rx_buf[0] | (data->rx_buf[1] << 8) | (data->rx_buf[2] << 16);
+ len -= 4;
+ skb = bt_skb_alloc(len, GFP_KERNEL);
+ /* Out of memory. Prepare a read retry and just
+ * return with the expectation that the next time
+ * we're called we'll have more memory. */
+ if (!skb)
+ return -ENOMEM;
+
+ if (data->rx_buf[3] == HCI_EVENT_PKT)
+ hdr_sz = HCI_EVENT_HDR_SIZE;
+ else if (data->rx_buf[3] == HCI_SCODATA_PKT)
+ hdr_sz = HCI_SCO_HDR_SIZE;
+ else if (data->rx_buf[3] == HCI_ACLDATA_PKT)
+ hdr_sz = HCI_ACL_HDR_SIZE;
+ else {
+ IBT_ERR(data->func, "Wrong packet type %d \n", data->rx_buf[3]);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ IBT_DBG(data->func, "copying hci header of %d bytes\n", hdr_sz);
+ memcpy(skb_put(skb, hdr_sz), data->rx_buf + 4, hdr_sz);
+ IBT_DUMP(data->func, skb->data, skb->len);
+ len -= hdr_sz;
+ IBT_DBG(data->func, "copying payload of %d bytes\n", len);
+ memcpy(skb_put(skb, len), data->rx_buf + 4 + 4, len);
+ data->hdev->stat.byte_rx += len + hdr_sz;
+ IBT_DBG(data->func, "Data before hci_recv_frame\n");
+ IBT_DUMP(data->func, skb->data, skb->len);
+ skb->dev = (void *)data->hdev;
+ bt_cb(skb)->pkt_type = data->rx_buf[3];
+ err = hci_recv_frame(skb);
+ if (err < 0) {
+ IBT_ERR(data->func,
+ "hci_recv_frame exited with error %d\n", err);
+ return err;
+ }
+ IBT_DBG(data->func, "hci_recv_frame exited with success %d\n", err);
+ sdio_writeb(data->func, 0x00, REG_PC_RRT, NULL);
+ return 0;
+}
+
+static void btsdio_interrupt(struct sdio_func *func)
+{
+ struct btsdio_data *data = sdio_get_drvdata(func);
+ int intrd, err;
+ int padded_len = 0;
+ unsigned int val1, val2;
+
+ IBT_DBG(data->func, "%s", data->hdev->name);
+
+ intrd = sdio_readb(func, REG_INTRD, NULL);
+ if (intrd & 0x01) {
+ /* in IWMC_BT we need to read the transfer length
+ * before interrupt clearing */
+ val1 = sdio_readb(data->func, REG_H2D_COUNT_L, &err);
+ IBT_DBG(data->func, "%s to read len1,"
+ " reg_addr=%x ret=%d val=%x\n",
+ err ? "Failed" : "Succeeded",
+ REG_H2D_COUNT_L, err, err ? 0xff : val1);
+ if (err == 0) {
+ val2 = sdio_readb(data->func, REG_H2D_COUNT_H, &err);
+ IBT_DBG(data->func, "%s to read len2,"
+ " reg_addr=%x ret=%d val=%x\n",
+ err ? "Failed" : "Succeeded",
+ REG_H2D_COUNT_H, err, err ? 0xff : val2);
+ if (err == 0)
+ padded_len = val1 + (val2 << 8);
+ }
+
+ sdio_writeb(func, 0x01, REG_CL_INTRD, NULL);
+
+ if (err || btsdio_rx_packet(data, padded_len) < 0) {
+ data->hdev->stat.err_rx++;
+ sdio_writeb(data->func, 0x01, REG_PC_RRT, NULL);
+ }
+ }
+}
+
+static int btsdio_open(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+ int err;
+
+ IBT_DBG(data->func, "%s", hdev->name);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ sdio_claim_host(data->func);
+
+#if 0
+ err = sdio_enable_func(data->func);
+ if (err < 0) {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ goto release;
+ }
+#endif
+
+ err = sdio_claim_irq(data->func, btsdio_interrupt);
+ if (err < 0) {
+ sdio_disable_func(data->func);
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ goto release;
+ }
+
+ if (data->func->class == SDIO_CLASS_BT_B)
+ sdio_writeb(data->func, 0x00, REG_MD_STAT, NULL);
+
+ sdio_writeb(data->func, 0x01, REG_EN_INTRD, NULL);
+
+release:
+ sdio_release_host(data->func);
+
+ return err;
+}
+
+static int btsdio_close(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+
+ IBT_DBG(data->func, "%s", hdev->name);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ sdio_claim_host(data->func);
+
+ sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL);
+
+ sdio_release_irq(data->func);
+
+ sdio_release_host(data->func);
+
+ return 0;
+}
+
+static int btsdio_flush(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+
+ IBT_DBG(data->func, "%s", hdev->name);
+
+ skb_queue_purge(&data->txq);
+
+ return 0;
+}
+
+static int btsdio_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct btsdio_data *data = hdev->driver_data;
+
+ IBT_DBG(data->func, "%s", hdev->name);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+
+ default:
+ return -EILSEQ;
+ }
+
+ skb_queue_tail(&data->txq, skb);
+
+ schedule_work(&data->work);
+
+ return 0;
+}
+
+static void btsdio_destruct(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+
+ IBT_DBG(data->func, "%s", hdev->name);
+
+ kfree(data);
+}
+
+static int btsdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct btsdio_data *data;
+ struct hci_dev *hdev;
+ struct sdio_func_tuple *tuple = func->tuples;
+ int err;
+
+ IBT_DBG(func, "id %p class 0x%04x", id, func->class);
+
+ while (tuple) {
+ IBT_DBG(func, "code 0x%x size %d",
+ tuple->code, tuple->size);
+ tuple = tuple->next;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->func = func;
+
+
+ sdio_claim_host(data->func);
+ /* FIXME: Remove after it is fixed in the Boot ROM upgrade */
+ func->enable_timeout = 10;
+ err = sdio_enable_func(data->func);
+ sdio_release_host(data->func);
+ if (err < 0) {
+ kfree(data);
+ return -ENODEV;
+ }
+
+
+ INIT_WORK(&data->work, btsdio_work);
+
+ skb_queue_head_init(&data->txq);
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ hdev->type = HCI_SDIO;
+ hdev->driver_data = data;
+
+ data->hdev = hdev;
+
+ SET_HCIDEV_DEV(hdev, &func->dev);
+
+ hdev->open = btsdio_open;
+ hdev->close = btsdio_close;
+ hdev->flush = btsdio_flush;
+ hdev->send = btsdio_send_frame;
+ hdev->destruct = btsdio_destruct;
+
+ hdev->owner = THIS_MODULE;
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ hci_free_dev(hdev);
+ kfree(data);
+ return err;
+ }
+
+ sdio_set_drvdata(func, data);
+
+ return 0;
+}
+
+static void btsdio_remove(struct sdio_func *func)
+{
+ struct btsdio_data *data = sdio_get_drvdata(func);
+ struct hci_dev *hdev;
+
+ IBT_DBG(data->func, "remove");
+
+ if (!data)
+ return;
+
+ hdev = data->hdev;
+
+ sdio_set_drvdata(func, NULL);
+
+ sdio_claim_host(func);
+
+ sdio_disable_func(func);
+
+ sdio_release_host(func);
+
+ hci_unregister_dev(hdev);
+
+ hci_free_dev(hdev);
+}
+
+/* Note that the macro CONFIG_BTSDIO_PM is deliberately not defined in the
+ * config file. In order to use it, the macro should be defined or changed to
+ * CONFIG_PM.
+ */
+#ifdef CONFIG_BTSDIO_PM
+static int btsdio_suspend(struct sdio_func *func, pm_message_t msg)
+{
+ struct btsdio_data *data = sdio_get_drvdata(func);
+
+ IBT_DBG(data->func, "suspend");
+
+ if (!data)
+ return -EINVAL;
+
+ btsdio_close(data->hdev);
+ return 0;
+}
+
+static int btsdio_resume(struct sdio_func *func)
+{
+ struct btsdio_data *data = sdio_get_drvdata(func);
+
+ IBT_DBG(data->func, "resume");
+
+ if (!data)
+ return -EINVAL;
+
+ btsdio_open(data->hdev);
+ return 0;
+}
+#endif /* CONFIG_BTSDIO_PM */
+
+static struct sdio_driver btsdio_driver = {
+ .name = "ibtsdio",
+ .probe = btsdio_probe,
+ .remove = btsdio_remove,
+ .id_table = btsdio_table,
+#ifdef CONFIG_BTSDIO_PM
+ .suspend = btsdio_suspend,
+ .resume = btsdio_resume,
+#endif /* CONFIG_BTSDIO_PM */
+};
+
+static int __init btsdio_init(void)
+{
+ printk(KERN_INFO "IWMC Bluetooth SDIO driver ver %s", DRIVER_VERSION);
+
+ return sdio_register_driver(&btsdio_driver);
+}
+
+static void __exit btsdio_exit(void)
+{
+ sdio_unregister_driver(&btsdio_driver);
+}
+
+module_init(btsdio_init);
+module_exit(btsdio_exit);
+
+MODULE_AUTHOR("Gregory Paskar <gregory.paskar@intel.com>");
+MODULE_DESCRIPTION("IWMC Bluetooth SDIO driver ver " DRIVER_VERSION);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ibtsdio");
--
1.6.0.6
---------------------------------------------------------------------
Intel Israel (74) Limited
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply related
* Re: [RFC-PATCH] dhcp provisioning support in cxgb3i
From: Mike Christie @ 2009-11-02 23:48 UTC (permalink / raw)
To: Rakesh Ranjan
Cc: davem, James Bottomley, Karen Xie, open-iscsi@googlegroups.com,
LKML, linux-scsi@vger.kernel.org, netdev
In-Reply-To: <4AEA7870.4010804@chelsio.com>
Rakesh Ranjan wrote:
> Mike Christie wrote:
>> Rakesh Ranjan wrote:
>>> Hi Mike,
>>>
>>> Herein attached patches for having dhcp provisioning support in
>>> cxgb3i. I have added one new iscsi netlink message
>>> ISCSI_UEVENT_REQ_IPCONF.
>>
>> Is the idea to have iscsid/uip send down this msg?
>>
>> Was it not possible to hook in more like how bnx2i does dhcp?
>
> yep, idea is to have iscsid send down ISCSI_UEVENT_REQ_IPCONF msg. bnx2i
> approach in our case for at least T3 is not feasible.
>
I think adding DHCP in the kernel is getting a little crazy :) If we go
down this path, I agree with the other person that stated it should at
least be generic.
^ permalink raw reply
* Re: [PATCHv6 3/3] vhost_net: a kernel-level virtio server
From: Daniel Walker @ 2009-11-03 0:05 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: netdev, virtualization, kvm, linux-kernel, mingo, linux-mm, akpm,
hpa, gregory.haskins, Rusty Russell, s.hetze
In-Reply-To: <20091102222904.GD15184@redhat.com>
Random style issues below .. Part of this is just stuff checkpatch
found.
On Tue, 2009-11-03 at 00:29 +0200, Michael S. Tsirkin wrote:
> +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;
> +}
The code above isn't properly indented .. You need to use tabs.
> +/* 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_no_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, &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 (vhost_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: "
> + "%ld 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_trigger(&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_no_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, &out, &in,
> + vq_log, &log);
> + /* OK, now we need to know about added descriptors. */
> + if (head == vq->num && vhost_notify(vq))
> + /* They could have slipped one in as we were doing that:
> + * check again. */
> + continue;
> + /* Nothing new? Wait for eventfd to tell us they refilled. */
> + if (head == vq->num)
> + break;
> + /* We don't need to be notified again. */
> + vhost_no_notify(vq);
> + 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_trigger(&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 = kzalloc(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, int index)
> +{
> + if (!n->vqs[index].private_data)
> + return;
> + if (index == 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, int index)
> +{
> + struct socket *sock = n->vqs[index].private_data;
> + if (!sock)
> + return;
> + if (index == 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, int index)
> +{
> + struct socket *sock;
> +
> + mutex_lock(&n->vqs[index].mutex);
> + sock = n->vqs[index].private_data;
> + vhost_net_disable_vq(n, index);
> + rcu_assign_pointer(n->vqs[index].private_data, NULL);
> + mutex_unlock(&n->vqs[index].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, VHOST_NET_VQ_TX);
> + *rx_sock = vhost_net_stop_vq(n, 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 = NULL;
> + struct vhost_virtqueue *vq;
> + int r;
> +
> + mutex_lock(&n->dev.mutex);
> + r = vhost_dev_check_owner(&n->dev);
> + if (r)
> + goto done;
> +
> + if (index >= VHOST_NET_VQ_MAX) {
> + r = -ENOBUFS;
> + goto done;
> + }
> + vq = n->vqs + index;
> + mutex_lock(&vq->mutex);
> + sock = get_socket(fd);
> + if (IS_ERR(sock)) {
> + r = PTR_ERR(sock);
> + goto done;
> + }
> +
> + /* start polling new socket */
> + oldsock = vq->private_data;
> + if (sock == oldsock)
> + goto done;
> +
> + vhost_net_disable_vq(n, index);
> + rcu_assign_pointer(vq->private_data, sock);
> + vhost_net_enable_vq(n, index);
> + mutex_unlock(&vq->mutex);
> +done:
> + mutex_unlock(&n->dev.mutex);
> + if (oldsock) {
> + vhost_net_flush_vq(n, index);
> + fput(oldsock->file);
> + }
> + return r;
> +}
> +
> +static long vhost_net_reset_owner(struct vhost_net *n)
> +{
> + struct socket *tx_sock = NULL;
> + struct socket *rx_sock = NULL;
> + long r;
> + mutex_lock(&n->dev.mutex);
> + r = vhost_dev_check_owner(&n->dev);
> + if (r)
> + goto done;
> + vhost_net_stop(n, &tx_sock, &rx_sock);
> + vhost_net_flush(n);
> + r = 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 r;
> +}
> +
> +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_unlock(&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_ACK_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..6db2e63
> --- /dev/null
> +++ b/drivers/vhost/vhost.c
> @@ -0,0 +1,968 @@
> +/* 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);
> +}
> +
> +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);
> +
> + for (i = 0; i < dev->nvqs; ++i) {
> + dev->vqs[i].dev = dev;
> + mutex_init(&dev->vqs[i].mutex);
> + 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 = kzalloc(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);
> + dev->vqs[i].error_ctx = NULL;
> + dev->vqs[i].error = NULL;
> + dev->vqs[i].kick = NULL;
> + dev->vqs[i].call_ctx = NULL;
> + dev->vqs[i].call = NULL;
> + }
> + 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)
> +{
> + int r = put_user(vq->used_flags, &vq->used->flags);
> + if (r)
> + return r;
> + return get_user(vq->last_used_idx, &vq->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 > 0xffff) {
> + 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->avail_idx = vq->last_avail_idx = s.num;
> + 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_DESC:
> + r = copy_from_user(&a, argp, sizeof a);
> + if (r < 0)
> + break;
> + if (a.padding) {
> + r = -EOPNOTSUPP;
> + break;
> + }
> + if ((u64)(unsigned long)a.user_addr != a.user_addr) {
> + r = -EFAULT;
> + break;
> + }
> + vq->desc = (void __user *)(unsigned long)a.user_addr;
> + break;
> + case VHOST_SET_VRING_AVAIL:
> + r = copy_from_user(&a, argp, sizeof a);
> + if (r < 0)
> + break;
> + if (a.padding) {
> + r = -EOPNOTSUPP;
> + break;
> + }
> + if ((u64)(unsigned long)a.user_addr != a.user_addr) {
> + r = -EFAULT;
> + break;
> + }
> + if (a.user_addr & (sizeof *vq->avail->ring - 1)) {
> + r = -EINVAL;
> + break;
> + }
> + vq->avail = (void __user *)(unsigned long)a.user_addr;
> + /* Forget the cached index value. */
> + vq->avail_idx = vq->last_avail_idx;
> + break;
> + case VHOST_SET_VRING_USED:
> + r = copy_from_user(&a, argp, sizeof a);
> + if (r < 0)
> + break;
> + if (a.padding) {
> + r = -EOPNOTSUPP;
> + break;
> + }
> + if ((u64)(unsigned long)a.user_addr != a.user_addr) {
> + r = -EFAULT;
> + break;
> + }
> + if (a.user_addr & (sizeof *vq->used->ring - 1)) {
> + r = -EINVAL;
> + break;
> + }
> + vq->used = (void __user *)(unsigned long)a.user_addr;
> + r = init_used(vq);
> + if (r)
> + break;
> + break;
> + case VHOST_SET_VRING_LOG:
> + r = copy_from_user(&a, argp, sizeof a);
> + if (r < 0)
> + break;
> + if (a.padding) {
> + r = -EOPNOTSUPP;
> + break;
> + }
> + if (a.user_addr == VHOST_VRING_LOG_DISABLE) {
> + vq->log_used = false;
> + break;
> + }
> + if (a.user_addr & (sizeof *vq->used->ring - 1)) {
> + r = -EINVAL;
> + break;
> + }
> + vq->log_used = true;
> + vq->log_addr = a.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 = get_user(p, (u64 __user *)argp);
> + 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_USERO);
> + 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)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 *)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 *)
> + (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 -1 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 -1;
> +
> + /* 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 *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: "
Need tabs for the line above.
> + "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 > 0x10000) {
Could you wrap that constant in a macro?
> + 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,
> + VHOST_NET_MAX_SG - 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 *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, 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,
> + VHOST_NET_MAX_SG - 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 send them an interrupt, using vq->call. */
> +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 sends the interrupt for this virtqueue */
> +void vhost_trigger_irq(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 send one, 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;
> +
> + /* Send the Guest an interrupt 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_trigger(struct vhost_dev *dev,
> + struct vhost_virtqueue *vq,
> + unsigned int head, int len)
> +{
> + vhost_add_used(vq, head, len);
> + vhost_trigger_irq(dev, vq);
> +}
> +
> +/* OK, now we need to know about added descriptors. */
> +bool vhost_notify(struct vhost_virtqueue *vq)
> +{
> + 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 disable notification: %d\n", r);
> + /* They could have slipped one in as we were doing that: make
> + * sure it's written, tell caller it needs to check again. */
> + mb();
> + return true;
> +}
> +
> +/* We don't need to be notified again. */
> +void vhost_no_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: %d\n", 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..ae66b5e
> --- /dev/null
> +++ b/drivers/vhost/vhost.h
> @@ -0,0 +1,158 @@
> +#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 {
> + 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 *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_trigger_irq(struct vhost_dev *, struct vhost_virtqueue *);
> +void vhost_add_used_and_trigger(struct vhost_dev *, struct vhost_virtqueue *,
> + unsigned int head, int len);
> +void vhost_no_notify(struct vhost_virtqueue *);
> +bool vhost_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) |
Line above needs more tabs.
Daniel
^ permalink raw reply
* Bugfix for iproute2
From: Sven Anders @ 2009-11-02 23:45 UTC (permalink / raw)
To: netdev; +Cc: shemminger
[-- Attachment #1.1: Type: text/plain, Size: 1892 bytes --]
Hello!
I already send this mail to Stephen Hemminger, but I'm not sure if it reached him.
Moreover I tried to join the LARTC mailing list (http://mailman.ds9a.nl/mailman/listinfo/lartc)
but it did not work. Is this mailing list dead?
Therefore I sent this mail again over this mailing list...
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 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
[-- Attachment #1.2: iproute-2.6.29-flush.patch --]
[-- Type: text/x-patch, Size: 627 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: signature.asc --]
[-- Type: application/pgp-signature, Size: 261 bytes --]
[-- Attachment #1.4: anders.vcf --]
[-- Type: text/x-vcard, Size: 352 bytes --]
begin:vcard
fn:Sven Anders
n:Anders;Sven
org:ANDURAS AG;Research and Development
adr;quoted-printable:;;Innstra=C3=9Fe 71;Passau;Bavaria;94036;Germany
email;internet:anders@anduras.de
title:Dipl. Inf.
tel;work:++49 (0)851 / 490 50 -0
tel;fax:++49 (0)851 / 590 50 - 55
x-mozilla-html:FALSE
url:http://www.anduras.de
version:2.1
end:vcard
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/x-pkcs7-signature, Size: 4888 bytes --]
^ permalink raw reply
* Re: [PATCH] e1000: the power down when running ifdown command
From: Jeff Kirsher @ 2009-11-03 0:26 UTC (permalink / raw)
To: Naohiro Ooiwa
Cc: jesse.brandeburg, peter.p.waskiewicz.jr, john.ronciak, davem,
Andrew Morton, netdev, svaidy, e1000-devel
In-Reply-To: <4AEC05E8.40600@miraclelinux.com>
2009/10/31 Naohiro Ooiwa <nooiwa@miraclelinux.com>:
> Hi All
>
> I resend my patch.
> Sorry, my previous mail lacked an explanation.
>
>
> The e1000 driver doesn't let the power down when running ifdown command.
> So, I set to the D3hot state of a PCI device at the end of e1000_close().
>
> With this modification, e1000 driver reduces power by ifdown.
> It's about 6 watts when I measured a total power of one server machine
> in my environment.
>
> I tested this patch. The result is good enough to me.
>
> Could you please check my patch ?
> If I should have other considerations, please tell me.
>
>
> Thanks you.
> Naohiro Ooiwa
>
>
>
> Signed-off-by: Naohiro Ooiwa <nooiwa@miraclelinux.com>
> ---
> drivers/net/e1000/e1000_main.c | 32 ++++++++++++++++++++++++++++++++
> 1 files changed, 32 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
> index bcd192c..12e1a42 100644
> --- a/drivers/net/e1000/e1000_main.c
> +++ b/drivers/net/e1000/e1000_main.c
> @@ -26,6 +26,11 @@
>
> *******************************************************************************/
>
> +/*
> + * define this if you want pci save power while ifdown.
> + */
> +#define E1000_PCI_POWER_SAVE
> +
> #include "e1000.h"
> #include <net/ip6_checksum.h>
>
> @@ -1248,6 +1253,23 @@ static int e1000_open(struct net_device *netdev)
> struct e1000_hw *hw = &adapter->hw;
> int err;
>
> +#ifdef E1000_PCI_POWER_SAVE
> + struct pci_dev *pdev = adapter->pdev;
> +
> + pci_set_power_state(pdev, PCI_D0);
> + pci_restore_state(pdev);
> +
> + if (adapter->need_ioport)
> + err = pci_enable_device(pdev);
> + else
> + err = pci_enable_device_mem(pdev);
> + if (err) {
> + printk(KERN_ERR "e1000: Cannot enable PCI device from power-save\n");
> + return err;
> + }
> + pci_set_master(pdev);
> +#endif
> +
> /* disallow open during test */
> if (test_bit(__E1000_TESTING, &adapter->flags))
> return -EBUSY;
> @@ -1265,6 +1287,7 @@ static int e1000_open(struct net_device *netdev)
> goto err_setup_rx;
>
> e1000_power_up_phy(adapter);
> + e1000_reset(adapter);
>
> adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;
> if ((hw->mng_cookie.status &
> @@ -1341,6 +1364,15 @@ static int e1000_close(struct net_device *netdev)
> e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id);
> }
>
> +#ifdef E1000_PCI_POWER_SAVE
> +#ifdef CONFIG_PM
> + pci_save_state(adapter->pdev);
> +#endif
> + pci_disable_device(adapter->pdev);
> + pci_wake_from_d3(adapter->pdev, true);
> + pci_set_power_state(adapter->pdev, PCI_D3hot);
> +#endif
> +
> return 0;
> }
>
I have added this patch to my tree for testing. This patch requires a
fair amount of regression testing, so once its passed testing I will
push the patch to David/netdev.
--
Cheers,
Jeff
^ permalink raw reply
* Re: [PATCH] e1000: the power down when running ifdown command
From: Dan Williams @ 2009-11-03 0:56 UTC (permalink / raw)
To: Jeff Kirsher
Cc: Naohiro Ooiwa, e1000-devel, netdev, jesse.brandeburg,
john.ronciak, Andrew Morton, svaidy, davem
In-Reply-To: <9929d2390911021626j4a2e4a96neaba49ffd8775dc9@mail.gmail.com>
On Mon, 2009-11-02 at 16:26 -0800, Jeff Kirsher wrote:
> 2009/10/31 Naohiro Ooiwa <nooiwa@miraclelinux.com>:
> > Hi All
> >
> > I resend my patch.
> > Sorry, my previous mail lacked an explanation.
> >
> >
> > The e1000 driver doesn't let the power down when running ifdown command.
> > So, I set to the D3hot state of a PCI device at the end of e1000_close().
> >
> > With this modification, e1000 driver reduces power by ifdown.
> > It's about 6 watts when I measured a total power of one server machine
> > in my environment.
> >
> > I tested this patch. The result is good enough to me.
> >
> > Could you please check my patch ?
> > If I should have other considerations, please tell me.
> >
> >
> > Thanks you.
> > Naohiro Ooiwa
> >
> >
> >
> > Signed-off-by: Naohiro Ooiwa <nooiwa@miraclelinux.com>
> > ---
> > drivers/net/e1000/e1000_main.c | 32 ++++++++++++++++++++++++++++++++
> > 1 files changed, 32 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
> > index bcd192c..12e1a42 100644
> > --- a/drivers/net/e1000/e1000_main.c
> > +++ b/drivers/net/e1000/e1000_main.c
> > @@ -26,6 +26,11 @@
> >
> > *******************************************************************************/
> >
> > +/*
> > + * define this if you want pci save power while ifdown.
> > + */
> > +#define E1000_PCI_POWER_SAVE
> > +
> > #include "e1000.h"
> > #include <net/ip6_checksum.h>
> >
> > @@ -1248,6 +1253,23 @@ static int e1000_open(struct net_device *netdev)
> > struct e1000_hw *hw = &adapter->hw;
> > int err;
> >
> > +#ifdef E1000_PCI_POWER_SAVE
> > + struct pci_dev *pdev = adapter->pdev;
> > +
> > + pci_set_power_state(pdev, PCI_D0);
> > + pci_restore_state(pdev);
> > +
> > + if (adapter->need_ioport)
> > + err = pci_enable_device(pdev);
> > + else
> > + err = pci_enable_device_mem(pdev);
> > + if (err) {
> > + printk(KERN_ERR "e1000: Cannot enable PCI device from power-save\n");
> > + return err;
> > + }
> > + pci_set_master(pdev);
> > +#endif
> > +
> > /* disallow open during test */
> > if (test_bit(__E1000_TESTING, &adapter->flags))
> > return -EBUSY;
> > @@ -1265,6 +1287,7 @@ static int e1000_open(struct net_device *netdev)
> > goto err_setup_rx;
> >
> > e1000_power_up_phy(adapter);
> > + e1000_reset(adapter);
> >
> > adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;
> > if ((hw->mng_cookie.status &
> > @@ -1341,6 +1364,15 @@ static int e1000_close(struct net_device *netdev)
> > e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id);
> > }
> >
> > +#ifdef E1000_PCI_POWER_SAVE
> > +#ifdef CONFIG_PM
> > + pci_save_state(adapter->pdev);
> > +#endif
> > + pci_disable_device(adapter->pdev);
> > + pci_wake_from_d3(adapter->pdev, true);
> > + pci_set_power_state(adapter->pdev, PCI_D3hot);
> > +#endif
> > +
> > return 0;
> > }
> >
>
> I have added this patch to my tree for testing. This patch requires a
> fair amount of regression testing, so once its passed testing I will
> push the patch to David/netdev.
Can we get rid of E1000_PCI_POWER_SAVE and just enable this
unconditionally once it's baked? Having the define there is pretty
superfluous right now. Either that, or make it
CONFIG_E1000_PCI_POWER_SAVE and make a Kconfig option for it that
depends on experimental or something. Or a module parameter. Or
something other than a dangling #define :)
Dan
------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
^ permalink raw reply
* [PATCH 03/20] tg3: Fix disappearing 57780 devices
From: Matt Carlson @ 2009-11-03 0:24 UTC (permalink / raw)
To: davem; +Cc: netdev, andy
Under certain power saving conditions, 57780 asic rev devices might
disappear from the system. The fix is to disallow the PCIe PLL from
powering down.
Signed-off-by: Matt Carlson <mcarlson@broadcom.com>
Reviewed-by: Michael Chan <mchan@broadcom.com>
---
drivers/net/tg3.c | 24 ++----------------------
drivers/net/tg3.h | 1 -
2 files changed, 2 insertions(+), 23 deletions(-)
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index a7a582e..b3c5d74 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -3243,15 +3243,6 @@ relink:
pci_write_config_word(tp->pdev,
tp->pcie_cap + PCI_EXP_LNKCTL,
newlnkctl);
- } else if (tp->tg3_flags3 & TG3_FLG3_TOGGLE_10_100_L1PLLPD) {
- u32 newreg, oldreg = tr32(TG3_PCIE_LNKCTL);
- if (tp->link_config.active_speed == SPEED_100 ||
- tp->link_config.active_speed == SPEED_10)
- newreg = oldreg & ~TG3_PCIE_LNKCTL_L1_PLL_PD_EN;
- else
- newreg = oldreg | TG3_PCIE_LNKCTL_L1_PLL_PD_EN;
- if (newreg != oldreg)
- tw32(TG3_PCIE_LNKCTL, newreg);
}
if (current_link_up != netif_carrier_ok(tp->dev)) {
@@ -7180,15 +7171,9 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
tw32(TG3_PCIE_EIDLE_DELAY, val | TG3_PCIE_EIDLE_DELAY_13_CLKS);
tw32(TG3_CORR_ERR_STAT, TG3_CORR_ERR_STAT_CLEAR);
- }
- if (tp->tg3_flags3 & TG3_FLG3_TOGGLE_10_100_L1PLLPD) {
- val = tr32(TG3_PCIE_LNKCTL);
- if (tp->tg3_flags3 & TG3_FLG3_CLKREQ_BUG)
- val |= TG3_PCIE_LNKCTL_L1_PLL_PD_DIS;
- else
- val &= ~TG3_PCIE_LNKCTL_L1_PLL_PD_DIS;
- tw32(TG3_PCIE_LNKCTL, val);
+ val = tr32(TG3_PCIE_LNKCTL) & ~TG3_PCIE_LNKCTL_L1_PLL_PD_EN;
+ tw32(TG3_PCIE_LNKCTL, val | TG3_PCIE_LNKCTL_L1_PLL_PD_DIS);
}
/* This works around an issue with Athlon chipsets on
@@ -12951,11 +12936,6 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
tp->tg3_flags3 |= TG3_FLG3_USE_PHYLIB;
- if ((tp->pci_chip_rev_id == CHIPREV_ID_57780_A1 &&
- tr32(RCVLPC_STATS_ENABLE) & RCVLPC_STATSENAB_ASF_FIX) ||
- tp->pci_chip_rev_id == CHIPREV_ID_57780_A0)
- tp->tg3_flags3 |= TG3_FLG3_TOGGLE_10_100_L1PLLPD;
-
err = tg3_mdio_init(tp);
if (err)
return err;
diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h
index a5568a1..eb4ed82 100644
--- a/drivers/net/tg3.h
+++ b/drivers/net/tg3.h
@@ -2756,7 +2756,6 @@ struct tg3 {
#define TG3_FLG3_PHY_ENABLE_APD 0x00001000
#define TG3_FLG3_5755_PLUS 0x00002000
#define TG3_FLG3_NO_NVRAM 0x00004000
-#define TG3_FLG3_TOGGLE_10_100_L1PLLPD 0x00008000
#define TG3_FLG3_PHY_IS_FET 0x00010000
#define TG3_FLG3_ENABLE_RSS 0x00020000
#define TG3_FLG3_4G_DMA_BNDRY_BUG 0x00080000
--
1.6.4.4
^ permalink raw reply related
* [PATCH 00/20] tg3 bugfixes and 5785 updates
From: Matt Carlson @ 2009-11-03 0:33 UTC (permalink / raw)
To: davem; +Cc: netdev, andy
This patchset implements some needed bugfixes and rounds out
support for the 5785.
^ permalink raw reply
* [PATCH 01/20] tg3: Assign flags to fixes in start_xmit_dma_bug
From: Matt Carlson @ 2009-11-02 22:33 UTC (permalink / raw)
To: davem; +Cc: netdev, andy
This patch adds a flag for each bug workaround in
tg3_start_xmit_dma_bug(). This is prep work for the following patch.
Signed-off-by: Matt Carlson <mcarlson@broadcom.com>
Reviewed-by: Michael Chan <mchan@broadcom.com>
---
drivers/net/tg3.c | 26 +++++++++++++++++++-------
drivers/net/tg3.h | 2 ++
2 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index ba5d3fe..da80825 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -5124,7 +5124,8 @@ static int tigon3_dma_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb,
/* Make sure new skb does not cross any 4G boundaries.
* Drop the packet if it does.
*/
- if (ret || tg3_4g_overflow_test(new_addr, new_skb->len)) {
+ if (ret || ((tp->tg3_flags3 & TG3_FLG3_4G_DMA_BNDRY_BUG) &&
+ tg3_4g_overflow_test(new_addr, new_skb->len))) {
if (!ret)
skb_dma_unmap(&tp->pdev->dev, new_skb,
DMA_TO_DEVICE);
@@ -5459,9 +5460,15 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb,
would_hit_hwbug = 0;
- if (tp->tg3_flags3 & TG3_FLG3_5701_DMA_BUG)
+ if ((tp->tg3_flags3 & TG3_FLG3_4G_DMA_BNDRY_BUG) &&
+ tg3_4g_overflow_test(mapping, len))
+ would_hit_hwbug = 1;
+
+ if ((tp->tg3_flags3 & TG3_FLG3_40BIT_DMA_LIMIT_BUG) &&
+ tg3_40bit_overflow_test(tp, mapping, len))
would_hit_hwbug = 1;
- else if (tg3_4g_overflow_test(mapping, len))
+
+ if (tp->tg3_flags3 & TG3_FLG3_5701_DMA_BUG)
would_hit_hwbug = 1;
tg3_set_txd(tnapi, entry, mapping, len, base_flags,
@@ -5482,10 +5489,12 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb,
tnapi->tx_buffers[entry].skb = NULL;
- if (tg3_4g_overflow_test(mapping, len))
+ if ((tp->tg3_flags3 & TG3_FLG3_4G_DMA_BNDRY_BUG) &&
+ tg3_4g_overflow_test(mapping, len))
would_hit_hwbug = 1;
- if (tg3_40bit_overflow_test(tp, mapping, len))
+ if ((tp->tg3_flags3 & TG3_FLG3_40BIT_DMA_LIMIT_BUG) &&
+ tg3_40bit_overflow_test(tp, mapping, len))
would_hit_hwbug = 1;
if (tp->tg3_flags2 & TG3_FLG2_HW_TSO)
@@ -12610,12 +12619,15 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tp->irq_max = 1;
-#ifdef TG3_NAPI
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) {
tp->tg3_flags |= TG3_FLAG_SUPPORT_MSIX;
tp->irq_max = TG3_IRQ_MAX_VECS;
}
-#endif
+
+ if (!(tp->tg3_flags3 & TG3_FLG3_5755_PLUS)) {
+ tp->tg3_flags3 |= TG3_FLG3_4G_DMA_BNDRY_BUG;
+ tp->tg3_flags3 |= TG3_FLG3_40BIT_DMA_LIMIT_BUG;
+ }
if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS) ||
(tp->tg3_flags2 & TG3_FLG2_5780_CLASS) ||
diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h
index bab7940..b603810 100644
--- a/drivers/net/tg3.h
+++ b/drivers/net/tg3.h
@@ -2759,6 +2759,8 @@ struct tg3 {
#define TG3_FLG3_TOGGLE_10_100_L1PLLPD 0x00008000
#define TG3_FLG3_PHY_IS_FET 0x00010000
#define TG3_FLG3_ENABLE_RSS 0x00020000
+#define TG3_FLG3_4G_DMA_BNDRY_BUG 0x00080000
+#define TG3_FLG3_40BIT_DMA_LIMIT_BUG 0x00100000
struct timer_list timer;
u16 timer_counter;
--
1.6.4.4
^ 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