* Re: AlacrityVM numbers updated for 31-rc4
From: Anthony Liguori @ 2009-08-12 23:05 UTC (permalink / raw)
To: Gregory Haskins
Cc: alacrityvm-devel, alacrityvm-users, linux-kernel@vger.kernel.org,
kvm@vger.kernel.org, Michael S. Tsirkin, netdev
In-Reply-To: <4A83296B.3080606@gmail.com>
Gregory Haskins wrote:
> I re-ran the numbers on 10GE against the actual alacrityvm v0.1 release
> available in git on kernel.org.
>
> I tried to include the newly announced "vhost" driver (Michael Tsirkin)
> for virtio acceleration, but ran into issues getting the patches to apply.
>
> For now, this includes native, virtio-u (virtio-userspace), and venet
> all running on 31-rc4. If I can resolve the issue with Michaels
> patches, I will add "virtio-k" (virtio-kernel) to the mix as well. For
> now, here are the results for 1500mtu:
>
> native: 7388Mb/s, 29.8us rtt (33505 tps udp-rr)
> venet: 3654Mb/s, 56.8us rtt (17600 tps udp-rr)
> virtio-u: 1955Mb/s, 4016.0us rtt ( 249 tps udp-rr)
>
Just FYI, the numbers quoted are wrong for virtio-u. Greg's machine
didn't have high res timers enabled in the kernel. He'll post newer
numbers later but they're much better than these (venet is still ahead
though).
Regards,
Anthony Liguori
^ permalink raw reply
* Re: [RFC PATCH v2 2/2] selinux: Support for the new TUN LSM hooks
From: Paul Moore @ 2009-08-12 22:55 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: linux-security-module, netdev, selinux
In-Reply-To: <20090812221440.GA8524@us.ibm.com>
On Wednesday 12 August 2009 06:14:40 pm Serge E. Hallyn wrote:
> Quoting Paul Moore (paul.moore@hp.com):
> > +static int selinux_tun_dev_attach(struct sock *sk)
> > +{
> > + struct sk_security_struct *sksec = sk->sk_security;
> > + u32 sid = current_sid();
> > + int err;
> > +
> > + err = avc_has_perm(sid, sksec->sid, SECCLASS_TUN_SOCKET,
> > + TUN_SOCKET__RELABELFROM, NULL);
> > + if (err)
> > + return err;
> > + err = avc_has_perm(sid, sid, SECCLASS_RAWIP_SOCKET,
>
> Was RAWIP on purpose here?
Nope, a mistake on my part that I hadn't caught yet. Thanks.
> > + TUN_SOCKET__RELABELTO, NULL);
> > + if (err)
> > + return err;
> > +
> > + sksec->sid = sid;
> > +
> > + return 0;
> > +}
>
> IIUC it is possible for multiple processes to attach to the same
> tun device. Will it get confusing/incorrect to have each attach
> potentially (if tasks have different sids) relabel?
I may be reading the code wrong, but in drivers/net/tun.c:tun_attach() the
code checks to see if the TUN device is already in use and if it is then the
attach fails with -EBUSY (check where the tun_device->tfile is examined). I
believe this should ensure that only one process at a time has access to the
TUN device so we shouldn't have to worry about a TUN socket getting relabeled
while it is currently in use. As far as persistent TUN devices getting
relabeled when a new process attaches to them, that is what we are trying to
accomplish here so that the network traffic being sent via the TUN device is
labeled according to the currently attached process; this is consistent with
how SELinux currently labels locally generated outbound traffic - outbound
packets inherit their security label from the sending process via the
originating socket/sock.
--
paul moore
linux @ hp
^ permalink raw reply
* [PATCH 2/2] korina: add error-handling to korina_alloc_ring
From: Phil Sutter @ 2009-08-12 22:52 UTC (permalink / raw)
To: netdev; +Cc: Andrew Morton, David S. Miller, florian, Roel Kluin
In-Reply-To: <20090812222249.39EAB4CEAA@orbit.nwl.cc>
This also avoids a potential buffer overflow in case the very first
receive descriptor fails to allocate, as an index of -1 would be used
afterwards. Kudos to Roel Kluin for pointing this out and providing an
initial patch.
Signed-off-by: Phil Sutter <n0-1@freewrt.org>
---
drivers/net/korina.c | 12 +++++++++---
1 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/net/korina.c b/drivers/net/korina.c
index 6df9d25..51ca54c 100644
--- a/drivers/net/korina.c
+++ b/drivers/net/korina.c
@@ -750,7 +750,7 @@ static struct ethtool_ops netdev_ethtool_ops = {
.get_link = netdev_get_link,
};
-static void korina_alloc_ring(struct net_device *dev)
+static int korina_alloc_ring(struct net_device *dev)
{
struct korina_private *lp = netdev_priv(dev);
struct sk_buff *skb;
@@ -771,7 +771,7 @@ static void korina_alloc_ring(struct net_device *dev)
for (i = 0; i < KORINA_NUM_RDS; i++) {
skb = dev_alloc_skb(KORINA_RBSIZE + 2);
if (!skb)
- break;
+ return -ENOMEM;
skb_reserve(skb, 2);
lp->rx_skb[i] = skb;
lp->rd_ring[i].control = DMA_DESC_IOD |
@@ -790,6 +790,8 @@ static void korina_alloc_ring(struct net_device *dev)
lp->rx_chain_head = 0;
lp->rx_chain_tail = 0;
lp->rx_chain_status = desc_empty;
+
+ return 0;
}
static void korina_free_ring(struct net_device *dev)
@@ -832,7 +834,11 @@ static int korina_init(struct net_device *dev)
writel(ETH_INT_FC_EN, &lp->eth_regs->ethintfc);
/* Allocate rings */
- korina_alloc_ring(dev);
+ if (korina_alloc_ring(dev)) {
+ printk(KERN_ERR "%s: descriptor allocation failed\n", dev->name);
+ korina_free_ring(dev);
+ return -ENOMEM;
+ }
writel(0, &lp->rx_dma_regs->dmas);
/* Start Rx DMA */
--
1.6.0.6
^ permalink raw reply related
* [PATCH 1/2] korina: fix printk formatting, add final info line
From: Phil Sutter @ 2009-08-12 22:22 UTC (permalink / raw)
To: netdev; +Cc: Andrew Morton, David S. Miller, florian, Roel Kluin
In-Reply-To: <20090812221509.725F74CEAA@orbit.nwl.cc>
The macro DRV_NAME contains "korina", the field dev->name points to the
actual interface name. So messages were formerly prefixed with
'korinaeth2:' (on my system).
Signed-off-by: Phil Sutter <n0-1@freewrt.org>
---
drivers/net/korina.c | 32 +++++++++++++++++---------------
1 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/drivers/net/korina.c b/drivers/net/korina.c
index b4cf602..6df9d25 100644
--- a/drivers/net/korina.c
+++ b/drivers/net/korina.c
@@ -338,7 +338,7 @@ static irqreturn_t korina_rx_dma_interrupt(int irq, void *dev_id)
napi_schedule(&lp->napi);
if (dmas & DMA_STAT_ERR)
- printk(KERN_ERR DRV_NAME "%s: DMA error\n", dev->name);
+ printk(KERN_ERR "%s: DMA error\n", dev->name);
retval = IRQ_HANDLED;
} else
@@ -555,7 +555,7 @@ static void korina_tx(struct net_device *dev)
dev->stats.tx_dropped++;
/* Should never happen */
- printk(KERN_ERR DRV_NAME "%s: split tx ignored\n",
+ printk(KERN_ERR "%s: split tx ignored\n",
dev->name);
} else if (devcs & ETH_TX_TOK) {
dev->stats.tx_packets++;
@@ -641,7 +641,7 @@ korina_tx_dma_interrupt(int irq, void *dev_id)
dev->trans_start = jiffies;
}
if (dmas & DMA_STAT_ERR)
- printk(KERN_ERR DRV_NAME "%s: DMA error\n", dev->name);
+ printk(KERN_ERR "%s: DMA error\n", dev->name);
retval = IRQ_HANDLED;
} else
@@ -917,8 +917,7 @@ static int korina_restart(struct net_device *dev)
ret = korina_init(dev);
if (ret < 0) {
- printk(KERN_ERR DRV_NAME "%s: cannot restart device\n",
- dev->name);
+ printk(KERN_ERR "%s: cannot restart device\n", dev->name);
return ret;
}
korina_multicast_list(dev);
@@ -1005,7 +1004,7 @@ static int korina_open(struct net_device *dev)
/* Initialize */
ret = korina_init(dev);
if (ret < 0) {
- printk(KERN_ERR DRV_NAME "%s: cannot open device\n", dev->name);
+ printk(KERN_ERR "%s: cannot open device\n", dev->name);
goto out;
}
@@ -1015,14 +1014,14 @@ static int korina_open(struct net_device *dev)
ret = request_irq(lp->rx_irq, &korina_rx_dma_interrupt,
IRQF_DISABLED, "Korina ethernet Rx", dev);
if (ret < 0) {
- printk(KERN_ERR DRV_NAME "%s: unable to get Rx DMA IRQ %d\n",
+ printk(KERN_ERR "%s: unable to get Rx DMA IRQ %d\n",
dev->name, lp->rx_irq);
goto err_release;
}
ret = request_irq(lp->tx_irq, &korina_tx_dma_interrupt,
IRQF_DISABLED, "Korina ethernet Tx", dev);
if (ret < 0) {
- printk(KERN_ERR DRV_NAME "%s: unable to get Tx DMA IRQ %d\n",
+ printk(KERN_ERR "%s: unable to get Tx DMA IRQ %d\n",
dev->name, lp->tx_irq);
goto err_free_rx_irq;
}
@@ -1031,7 +1030,7 @@ static int korina_open(struct net_device *dev)
ret = request_irq(lp->ovr_irq, &korina_ovr_interrupt,
IRQF_DISABLED, "Ethernet Overflow", dev);
if (ret < 0) {
- printk(KERN_ERR DRV_NAME"%s: unable to get OVR IRQ %d\n",
+ printk(KERN_ERR "%s: unable to get OVR IRQ %d\n",
dev->name, lp->ovr_irq);
goto err_free_tx_irq;
}
@@ -1040,7 +1039,7 @@ static int korina_open(struct net_device *dev)
ret = request_irq(lp->und_irq, &korina_und_interrupt,
IRQF_DISABLED, "Ethernet Underflow", dev);
if (ret < 0) {
- printk(KERN_ERR DRV_NAME "%s: unable to get UND IRQ %d\n",
+ printk(KERN_ERR "%s: unable to get UND IRQ %d\n",
dev->name, lp->und_irq);
goto err_free_ovr_irq;
}
@@ -1137,7 +1136,7 @@ static int korina_probe(struct platform_device *pdev)
dev->base_addr = r->start;
lp->eth_regs = ioremap_nocache(r->start, r->end - r->start);
if (!lp->eth_regs) {
- printk(KERN_ERR DRV_NAME "cannot remap registers\n");
+ printk(KERN_ERR DRV_NAME ": cannot remap registers\n");
rc = -ENXIO;
goto probe_err_out;
}
@@ -1145,7 +1144,7 @@ static int korina_probe(struct platform_device *pdev)
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_dma_rx");
lp->rx_dma_regs = ioremap_nocache(r->start, r->end - r->start);
if (!lp->rx_dma_regs) {
- printk(KERN_ERR DRV_NAME "cannot remap Rx DMA registers\n");
+ printk(KERN_ERR DRV_NAME ": cannot remap Rx DMA registers\n");
rc = -ENXIO;
goto probe_err_dma_rx;
}
@@ -1153,14 +1152,14 @@ static int korina_probe(struct platform_device *pdev)
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_dma_tx");
lp->tx_dma_regs = ioremap_nocache(r->start, r->end - r->start);
if (!lp->tx_dma_regs) {
- printk(KERN_ERR DRV_NAME "cannot remap Tx DMA registers\n");
+ printk(KERN_ERR DRV_NAME ": cannot remap Tx DMA registers\n");
rc = -ENXIO;
goto probe_err_dma_tx;
}
lp->td_ring = kmalloc(TD_RING_SIZE + RD_RING_SIZE, GFP_KERNEL);
if (!lp->td_ring) {
- printk(KERN_ERR DRV_NAME "cannot allocate descriptors\n");
+ printk(KERN_ERR DRV_NAME ": cannot allocate descriptors\n");
rc = -ENXIO;
goto probe_err_td_ring;
}
@@ -1193,10 +1192,13 @@ static int korina_probe(struct platform_device *pdev)
rc = register_netdev(dev);
if (rc < 0) {
printk(KERN_ERR DRV_NAME
- ": cannot register net device %d\n", rc);
+ ": cannot register net device: %d\n", rc);
goto probe_err_register;
}
setup_timer(&lp->media_check_timer, korina_poll_media, (unsigned long) dev);
+
+ printk(KERN_INFO "%s: " DRV_NAME "-" DRV_VERSION " " DRV_RELDATE "\n",
+ dev->name);
out:
return rc;
--
1.6.0.6
^ permalink raw reply related
* Re: [PATCH] korina: Read buffer overflow
From: Phil Sutter @ 2009-08-12 22:15 UTC (permalink / raw)
To: netdev; +Cc: Andrew Morton, David S. Miller, florian, Roel Kluin
In-Reply-To: <20090809000640.216D44CEAA@orbit.nwl.cc>
Hi,
On Sun, Aug 09, 2009 at 02:06:25AM +0200, Phil Sutter wrote:
> The following series fixes the incorrect printk formatting we already
> discussed, implements the solution from above and makes the driver use
> netdev_ops.
Obviously, I took the chance to mess things up again. These three
patches were accidentially written on top of the linux-mips tree, right
before Ralf pulled from Linus. So they do not apply cleanly to the
netdev tree, and even worse the last one is completely useless since
it's changes have already been implemented.
I will follow up to this email with an updated series of the two
remaining, valid patches. Sorry for the inconvenience.
Greetings, Phil
^ permalink raw reply
* Re: [RFC PATCH v2 2/2] selinux: Support for the new TUN LSM hooks
From: Serge E. Hallyn @ 2009-08-12 22:14 UTC (permalink / raw)
To: Paul Moore; +Cc: linux-security-module, netdev, selinux
In-Reply-To: <20090810172850.7946.25175.stgit@flek.lan>
Quoting Paul Moore (paul.moore@hp.com):
> Add support for the new TUN LSM hooks: security_tun_dev_create(),
> security_tun_dev_post_create() and security_tun_dev_attach(). This includes
> the addition of a new object class, tun_socket, which represents the socks
> associated with TUN devices. The _tun_dev_create() and _tun_dev_post_create()
> hooks are fairly similar to the standard socket functions but _tun_dev_attach()
> is a bit special. The _tun_dev_attach() is unique because it involves a
> domain attaching to an existing TUN device and its associated tun_socket
> object, an operation which does not exist with standard sockets and most
> closely resembles a relabel operation.
>
> --
>
> NOTE: This relies on some changes to the policy to add the new object class
> and its associated permissions, I will ensure that the policy is sorted
> and merged before pushing this patch upstream. Also, you will notice
> that the new tun_socket object class simply inherits the base socket
> object class, thoughts?
> ---
>
> security/selinux/hooks.c | 60 +++++++++++++++++++++++++++-
> security/selinux/include/av_inherit.h | 1
> security/selinux/include/av_permissions.h | 22 ++++++++++
> security/selinux/include/class_to_string.h | 1
> security/selinux/include/flask.h | 1
> 5 files changed, 83 insertions(+), 2 deletions(-)
>
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 15c2a08..fc7caa0 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -13,8 +13,8 @@
> * Eric Paris <eparis@redhat.com>
> * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
> * <dgoeddel@trustedcs.com>
> - * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
> - * Paul Moore <paul.moore@hp.com>
> + * Copyright (C) 2006, 2007, 2009 Hewlett-Packard Development Company, L.P.
> + * Paul Moore <paul.moore@hp.com>
> * Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
> * Yuichi Nakamura <ynakam@hitachisoft.jp>
> *
> @@ -4296,6 +4296,59 @@ static void selinux_req_classify_flow(const struct request_sock *req,
> fl->secid = req->secid;
> }
>
> +static int selinux_tun_dev_create(void)
> +{
> + u32 sid = current_sid();
> +
> + /* we aren't taking into account the "sockcreate" SID since the socket
> + * that is being created here is not a socket in the traditional sense,
> + * instead it is a private sock, accessible only to the kernel, and
> + * representing a wide range of network traffic spanning multiple
> + * connections unlike traditional sockets - check the TUN driver to
> + * get a better understanding of why this socket is special */
> +
> + return avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE,
> + NULL);
> +}
> +
> +static void selinux_tun_dev_post_create(struct sock *sk)
> +{
> + struct sk_security_struct *sksec = sk->sk_security;
> +
> + /* we don't currently perform any NetLabel based labeling here and it
> + * isn't clear that we would want to do so anyway; while we could apply
> + * labeling without the support of the TUN user the resulting labeled
> + * traffic from the other end of the connection would almost certainly
> + * cause confusion to the TUN user that had no idea network labeling
> + * protocols were being used */
> +
> + /* see the comments in selinux_tun_dev_create() about why we don't use
> + * the sockcreate SID here */
> +
> + sksec->sid = current_sid();
> + sksec->sclass = SECCLASS_TUN_SOCKET;
> +}
> +
> +static int selinux_tun_dev_attach(struct sock *sk)
> +{
> + struct sk_security_struct *sksec = sk->sk_security;
> + u32 sid = current_sid();
> + int err;
> +
> + err = avc_has_perm(sid, sksec->sid, SECCLASS_TUN_SOCKET,
> + TUN_SOCKET__RELABELFROM, NULL);
> + if (err)
> + return err;
> + err = avc_has_perm(sid, sid, SECCLASS_RAWIP_SOCKET,
Was RAWIP on purpose here?
> + TUN_SOCKET__RELABELTO, NULL);
> + if (err)
> + return err;
> +
> + sksec->sid = sid;
> +
> + return 0;
> +}
IIUC it is possible for multiple processes to attach to the same
tun device. Will it get confusing/incorrect to have each attach
potentially (if tasks have different sids) relabel?
> static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
> {
> int err = 0;
> @@ -5464,6 +5517,9 @@ static struct security_operations selinux_ops = {
> .inet_csk_clone = selinux_inet_csk_clone,
> .inet_conn_established = selinux_inet_conn_established,
> .req_classify_flow = selinux_req_classify_flow,
> + .tun_dev_create = selinux_tun_dev_create,
> + .tun_dev_post_create = selinux_tun_dev_post_create,
> + .tun_dev_attach = selinux_tun_dev_attach,
>
> #ifdef CONFIG_SECURITY_NETWORK_XFRM
> .xfrm_policy_alloc_security = selinux_xfrm_policy_alloc,
> diff --git a/security/selinux/include/av_inherit.h b/security/selinux/include/av_inherit.h
> index 8377a4b..abedcd7 100644
> --- a/security/selinux/include/av_inherit.h
> +++ b/security/selinux/include/av_inherit.h
> @@ -15,6 +15,7 @@
> S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL)
> S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL)
> S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL)
> + S_(SECCLASS_TUN_SOCKET, socket, 0x00400000UL)
> S_(SECCLASS_IPC, ipc, 0x00000200UL)
> S_(SECCLASS_SEM, ipc, 0x00000200UL)
> S_(SECCLASS_MSGQ, ipc, 0x00000200UL)
> diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
> index d645192..0b41ad5 100644
> --- a/security/selinux/include/av_permissions.h
> +++ b/security/selinux/include/av_permissions.h
> @@ -423,6 +423,28 @@
> #define UNIX_DGRAM_SOCKET__RECV_MSG 0x00080000UL
> #define UNIX_DGRAM_SOCKET__SEND_MSG 0x00100000UL
> #define UNIX_DGRAM_SOCKET__NAME_BIND 0x00200000UL
> +#define TUN_SOCKET__IOCTL 0x00000001UL
> +#define TUN_SOCKET__READ 0x00000002UL
> +#define TUN_SOCKET__WRITE 0x00000004UL
> +#define TUN_SOCKET__CREATE 0x00000008UL
> +#define TUN_SOCKET__GETATTR 0x00000010UL
> +#define TUN_SOCKET__SETATTR 0x00000020UL
> +#define TUN_SOCKET__LOCK 0x00000040UL
> +#define TUN_SOCKET__RELABELFROM 0x00000080UL
> +#define TUN_SOCKET__RELABELTO 0x00000100UL
> +#define TUN_SOCKET__APPEND 0x00000200UL
> +#define TUN_SOCKET__BIND 0x00000400UL
> +#define TUN_SOCKET__CONNECT 0x00000800UL
> +#define TUN_SOCKET__LISTEN 0x00001000UL
> +#define TUN_SOCKET__ACCEPT 0x00002000UL
> +#define TUN_SOCKET__GETOPT 0x00004000UL
> +#define TUN_SOCKET__SETOPT 0x00008000UL
> +#define TUN_SOCKET__SHUTDOWN 0x00010000UL
> +#define TUN_SOCKET__RECVFROM 0x00020000UL
> +#define TUN_SOCKET__SENDTO 0x00040000UL
> +#define TUN_SOCKET__RECV_MSG 0x00080000UL
> +#define TUN_SOCKET__SEND_MSG 0x00100000UL
> +#define TUN_SOCKET__NAME_BIND 0x00200000UL
> #define PROCESS__FORK 0x00000001UL
> #define PROCESS__TRANSITION 0x00000002UL
> #define PROCESS__SIGCHLD 0x00000004UL
> diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h
> index 21ec786..7ab9299 100644
> --- a/security/selinux/include/class_to_string.h
> +++ b/security/selinux/include/class_to_string.h
> @@ -77,3 +77,4 @@
> S_(NULL)
> S_(NULL)
> S_("kernel_service")
> + S_("tun_socket")
> diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h
> index 882f27d..f248500 100644
> --- a/security/selinux/include/flask.h
> +++ b/security/selinux/include/flask.h
> @@ -53,6 +53,7 @@
> #define SECCLASS_PEER 68
> #define SECCLASS_CAPABILITY2 69
> #define SECCLASS_KERNEL_SERVICE 74
> +#define SECCLASS_TUN_SOCKET 75
>
> /*
> * Security identifier indices for initial entities
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" 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
* Re: AlacrityVM numbers updated for 31-rc4
From: Michael S. Zick @ 2009-08-12 22:05 UTC (permalink / raw)
To: Javier Guerra
Cc: Gregory Haskins, alacrityvm-devel, alacrityvm-users,
linux-kernel@vger.kernel.org, kvm@vger.kernel.org,
Michael S. Tsirkin, netdev
In-Reply-To: <90eb1dc70908121433u3d5916efg5f0ef5994d6aa42c@mail.gmail.com>
On Wed August 12 2009, Javier Guerra wrote:
> On Wed, Aug 12, 2009 at 3:43 PM, Gregory
> Haskins<gregory.haskins@gmail.com> wrote:
> > In any case, I have updated the graphs on the AlacrityVM wiki to reflect
> > these latest numbers:
>
> pseudo-3D charts are just wrong
> (http://www.chuckchakrapani.com/articles/PDF/94070347.pdf)
>
Nice quote.
Even 15 years after it was published, those first two graphs
are prime "bad examples".
The second two could stand some improvement also - like put
the legend and numbers *on* each solid bar.
@J.G. - I think you are fighting an uphill battle here against
human nature. People tend to go with their idea of "pretty".
Mike
^ permalink raw reply
* Re: [PATCH] net/ipv4, linux-2.6.30.4
From: David Miller @ 2009-08-12 21:55 UTC (permalink / raw)
To: slot.daniel; +Cc: netdev
In-Reply-To: <bb6e06c00908121147h2dab7d0kf5841a40956c5c56@mail.gmail.com>
From: Daniel Slot <slot.daniel@gmail.com>
Date: Wed, 12 Aug 2009 20:47:44 +0200
> RFC 4653 specifies Non-Congestion Robustness (NCR) for TCP.
> In the absence of explicit congestion notification from the network, TCP
> uses loss as an indication of congestion.
> One of the ways TCP detects loss is using the arrival of three duplicate
> acknowledgments.
> However, this heuristic is not always correct,
> notably in the case when network paths reorder segments (for whatever
> reason), resulting in degraded performance.
Linux's TCP stack already has sophisticated reordering detection.
> TCP-NCR is designed to mitigate this degraded performance by increasing the
> number of duplicate acknowledgments required to trigger loss recovery,
> based on the current state of the connection, in an effort to better
> disambiguate true segment loss from segment reordering.
We already have code in the stack which tries to detect packet
reordering with a high level of sophistication.
^ permalink raw reply
* Re: pull request: wireless-2.6 2009-08-11
From: David Miller @ 2009-08-12 21:52 UTC (permalink / raw)
To: linville-2XuSBdqkA4R54TAoqtyWWQ
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20090812182403.GF2657-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>
From: "John W. Linville" <linville-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>
Date: Wed, 12 Aug 2009 14:24:03 -0400
> When you pull this, could you also revert 57921c31 ("libertas: Read
> buffer overflow"). It has been shown to create a new problem. There
> is work towards a solution to that one, but it isn't a simple clean-up.
Ok, I'll take care of that.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [net-next 00/36] bnx2x patch series
From: David Miller @ 2009-08-12 21:50 UTC (permalink / raw)
To: eilong; +Cc: netdev, yanivr, vladz, gertner, benli
In-Reply-To: <1250101162.27379.151.camel@lb-tlvb-eilong>
Come on... 36 patches? :-/
Please trickle changes in, don't send patch bombs. I think
I've told you this not once, but several times. But I keep
seeing these sizable patch sets.
I frankly don't care that it might not mesh well with how you code up
and validate changes internally, because it absolutely does NOT work
well for how bugs really get found and fixed upstream.
If you trickle changes in, the guilty change is obvious to spot and it
gets found before you do more development that depends upon that buggy
change.
Whereas if you patch bomb, someone has to do a lot of work and
bisecting to nail down the bad change. And the bug might be so
fundamental in a patch that it also invalidates all the followon
work you did.
I really don't want to apply any of this stuff, to be honest with you.
I simply don't. I keep giving guidelines and they keep getting
ignored.
^ permalink raw reply
* Re: AlacrityVM numbers updated for 31-rc4
From: Javier Guerra @ 2009-08-12 21:33 UTC (permalink / raw)
To: Gregory Haskins
Cc: alacrityvm-devel, alacrityvm-users, linux-kernel@vger.kernel.org,
kvm@vger.kernel.org, Michael S. Tsirkin, netdev
In-Reply-To: <4A83296B.3080606@gmail.com>
On Wed, Aug 12, 2009 at 3:43 PM, Gregory
Haskins<gregory.haskins@gmail.com> wrote:
> In any case, I have updated the graphs on the AlacrityVM wiki to reflect
> these latest numbers:
pseudo-3D charts are just wrong
(http://www.chuckchakrapani.com/articles/PDF/94070347.pdf)
--
Javier
^ permalink raw reply
* Re: [PATCH 1/2] mac802154: add a software MAC 802.15.4 implementation
From: Dmitry Eremin-Solenikov @ 2009-08-12 20:46 UTC (permalink / raw)
To: Johannes Berg; +Cc: netdev, linux-wireless, Sergey Lapin
In-Reply-To: <1250082837.8784.21.camel@johannes.local>
On Wed, Aug 12, 2009 at 03:13:57PM +0200, Johannes Berg wrote:
> On Wed, 2009-08-12 at 17:06 +0400, Dmitry Eremin-Solenikov wrote:
>
> > > In addition to that, you can't put anything into skb->cb, then push the
> > > frame to the master netdev, and expect things in skb->cb to still be
> > > there when the frame arrives at the master netdev. Not sure you do that
> > > (I hope not because that would be very buggy), but eventually you'll
> > > probably find that you do want that, etc.
> >
> > Hmm. It works for us. Could you please tell me more about the problems
> > with skb->cb ?
>
> Uh, well, you don't own the skb->cb between dev_queue_xmit() and
> ndo_start_xmit(). In fact, your phy_cb data will be overwritten by the
> qdisc.
Hmmm. Really weird. Then, if we want to pass data from socket layer to
MAC layer, we should place data in skb->data and not in skb->cb (like
radiotap header)?
> Also, looking into this a bit more, I see no reason to allocate a work
> struct every time you get a frame -- better just queue them up and stick
> everything else into skb->cb. In fact, you don't need a master netdev
> here anyway, just move all code from ieee802154_master_hard_start_xmit()
> into ieee802154_net_xmit().
>
Nice idea. Thanks a lot!
--
With best wishes
Dmitry
^ permalink raw reply
* AlacrityVM numbers updated for 31-rc4
From: Gregory Haskins @ 2009-08-12 20:43 UTC (permalink / raw)
To: alacrityvm-devel, alacrityvm-users
Cc: linux-kernel@vger.kernel.org, kvm@vger.kernel.org,
Michael S. Tsirkin, netdev
[-- Attachment #1: Type: text/plain, Size: 1437 bytes --]
I re-ran the numbers on 10GE against the actual alacrityvm v0.1 release
available in git on kernel.org.
I tried to include the newly announced "vhost" driver (Michael Tsirkin)
for virtio acceleration, but ran into issues getting the patches to apply.
For now, this includes native, virtio-u (virtio-userspace), and venet
all running on 31-rc4. If I can resolve the issue with Michaels
patches, I will add "virtio-k" (virtio-kernel) to the mix as well. For
now, here are the results for 1500mtu:
native: 7388Mb/s, 29.8us rtt (33505 tps udp-rr)
venet: 3654Mb/s, 56.8us rtt (17600 tps udp-rr)
virtio-u: 1955Mb/s, 4016.0us rtt ( 249 tps udp-rr)
Note that on one particular boot-session, I actually saw venet run
consistently 4600Mb/s, and virtio-u run consistently 2800Mb/s. I am not
sure why other runs resulted in deterministically lower numbers, but I
included the average of the low runs to be conservative, ~3600, ~1900
respectively).
Of interest here is that native made a huge jump (2.6.29 was more like
4Gb/s), and also that KVM regressed a bit, especially compared to native
numbers. The cause for either of these changes is not known at this
time, but I will investigate the cause over the next few days.
In any case, I have updated the graphs on the AlacrityVM wiki to reflect
these latest numbers:
http://developer.novell.com/wiki/index.php/AlacrityVM
Kind Regards,
-Greg
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 267 bytes --]
^ permalink raw reply
* Re: WARNING: at net/ipv4/af_inet.c:155 inet_sock_destruct+0x122/0x13a()
From: Vlad Yasevich @ 2009-08-12 20:00 UTC (permalink / raw)
To: Eric Dumazet; +Cc: David Miller, john.dykstra1, mangoo, netdev
In-Reply-To: <4A77D2BA.3040304@gmail.com>
Eric Dumazet wrote:
> David Miller a écrit :
>> From: John Dykstra <john.dykstra1@gmail.com>
>> Date: Mon, 03 Aug 2009 19:38:01 -0500
>>
>>> There's a good chance e51a67a9c8a2ea5c563f8c2ba6613fe2100ffe67 from the
>>> current mainline will fix this problem.
>>>
>>> Dave, Eric's fix might be a candidate for -stable. The symptom is
>>> usually a WARN, but the impact is significant.
>> Hmmm, I'll double-check. I thought I had submitted this one.
>>
>> Thanks for the heads up.
>
> Hmm, I dont see how this patch could solve Tomasz case...
> Since commit 2b85a34e911bf483c27cfdd124aeb1605145dc80
> (net: No more expensive sock_hold()/sock_put() on each tx)
> was not part of 2.6.30.4 AFAIK
>
> This is the WARN_ON(sk->sk_forward_alloc) that triggers...
>
> Sounds like a truesize mismatch rather than a sk_refcount one ?
> --
> 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
>
BTW, I've seen the same issue in 2.6.28 and 2.6.29 while doing a bunch
of NFS-over-UDP testing. I've seen the issue reported in 2.6.27 as well,
but it went by ignored. It's not easy to reproduce as it seems like it
requires quite a bit traffic over over multiple interfaces.
I've been looking at this for a while and haven't caught the bugger.
Here is the stack trace from 2.6.28:
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086015] ------------[ cut here
]-------
-----
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086017] WARNING: at
net/ipv4/af_inet.c:
155 inet_sock_destruct+0x15d/0x182()
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086019] Modules linked in: sctp
libcrc32c sg edd nfsd auth_rpcgss exportfs nfs lockd nfs_acl sunrpc deflate
zlib_deflate ctr twofish twofish_common camellia serpent blowfish des_generic
cbc aes_x86_64 aes_generic xcbc rmd160 sha256_generic sha1_generic crypto_null
af_key loop serio_raw psmouse hpilo shpchp pci_hotplug container button evdev
ext3 jbd mbcache ses enclosure sd_mod crc_t10dif usbhid hid ehci_hcd uhci_hcd
mptsas mptscsih mptbase scsi_transport_sas bnx2 zlib_inflate cciss scsi_mod
thermal processor fan thermal_sys [last unloaded: ipmi_msghandler]
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086053] Pid: 4570, comm: nfsd Not
tainted 2.6.28-clim-9-amd64 #1
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086055] Call Trace:
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086062] [<ffffffff8024307f>]
warn_on_slowpath+0x58/0x7d
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086066] [<ffffffff804b5ada>] ?
_spin_unlock_irq+0x1c/0x35
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086069] [<ffffffff8024813f>] ?
local_bh_disable+0xe/0x10
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086072] [<ffffffff804b58af>] ?
_spin_lock_bh+0x23/0x29
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086074] [<ffffffff8024826a>] ?
local_bh_enable+0x88/0xa1
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086076] [<ffffffff8024813f>] ?
local_bh_disable+0xe/0x10
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086078] [<ffffffff80454e77>]
inet_sock_destruct+0x15d/0x182
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086082] [<ffffffff80400719>]
sk_free+0x1e/0xda
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086084] [<ffffffff80400899>]
sk_common_release+0xc4/0xc9
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086087] [<ffffffff8044c399>]
udp_lib_close+0x9/0xb
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086089] [<ffffffff8045490a>]
inet_release+0x50/0x57
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086091] [<ffffffff803fda24>]
sock_release+0x20/0xb1
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086093] [<ffffffff803fdad7>]
sock_close+0x22/0x26
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086097] [<ffffffff802bb867>]
__fput+0xd4/0x198
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086099] [<ffffffff802bb940>]
fput+0x15/0x17
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086116] [<ffffffffa025a67e>]
svc_sock_free+0x3b/0x51 [sunrpc]
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086131] [<ffffffffa0264834>]
svc_xprt_free+0x3b/0x4c [sunrpc]
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086144] [<ffffffffa02647f9>] ?
svc_xprt_free+0x0/0x4c [sunrpc]
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086147] [<ffffffff8034f509>]
kref_put+0x43/0x4f
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086161] [<ffffffffa0263c1a>]
svc_close_xprt+0x50/0x59 [sunrpc]
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086174] [<ffffffffa0263c6e>]
svc_close_all+0x4b/0x64 [sunrpc]
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086187] [<ffffffffa0259b6f>]
svc_destroy+0x99/0x13d [sunrpc]
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086201] [<ffffffffa0259cc7>]
svc_exit_thread+0xb4/0xbd [sunrpc]
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086210] [<ffffffffa02ed8f5>]
nfsd+0x277/0x291 [nfsd]
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086218] [<ffffffffa02ed67e>] ?
nfsd+0x0/0x291 [nfsd]
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086226] [<ffffffffa02ed67e>] ?
nfsd+0x0/0x291 [nfsd]
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086229] [<ffffffff80256464>]
kthread+0x49/0x76
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086232] [<ffffffff802134f9>]
child_rip+0xa/0x11
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086235] [<ffffffff8025641b>] ?
kthread+0x0/0x76
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086238] [<ffffffff802134ef>] ?
child_rip+0x0/0x11
May 13 16:17:38 dl380g6-2 kernel: [ 4473.086240] ---[ end trace
7a78cc0dbbc1385d ]---
And here is one from 2.6.29 (nearly identical):
15764.278127] ------------[ cut here]------------
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278130] WARNING: at
net/ipv4/af_inet.c:156 inet_sock_destruct+0x16f/0x194()
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278133] Hardware name: ProLiant DL380 G6
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278134] Modules linked in: sctp crc32c
libcrc32c edd nfsd exportfs nfs lockd nfs_acl auth_rpcgss sunrpc deflate
zlib_deflate ctr twofish twofish_common camellia serpent blowfish des_generic
cbc aes_x86_64 aes_generic xcbc rmd160 sha256_generic sha1_generic crypto_null
af_key loop psmouse hpilo serio_raw container shpchp pci_hotplug button evdev
ext3 jbd mbcache ata_generic usbhid hid ata_piix libata mptsas ide_pci_generic
mptscsih ide_core mptbase ehci_hcd uhci_hcd scsi_transport_sas cciss bnx2
zlib_inflate e1000e scsi_mod thermal processor fan thermal_sys [last unloaded:
ipmi_msghandler]
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278184] Pid: 5146, comm: nfsd Not
tainted 2.6.29-clim-2-amd64 #1
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278186] Call Trace:
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278194] [<ffffffff80243317>]
warn_slowpath+0xd3/0x10f
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278200] [<ffffffff80240107>] ?
finish_task_switch+0x2b/0xc8
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278207] [<ffffffff804c5e20>] ?
_spin_lock+0x9/0xc
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278210] [<ffffffff804c5f3d>] ?
_spin_lock_bh+0x19/0x1e
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278214] [<ffffffff8046429f>]
inet_sock_destruct+0x16f/0x194
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278220] [<ffffffff8040d612>]
sk_free+0x1e/0xf9
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278223] [<ffffffff8040d7b3>]
sk_common_release+0xc6/0xcb
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278227] [<ffffffff8045b14c>]
udp_lib_close+0x9/0xb
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278231] [<ffffffff80463d83>]
inet_release+0x50/0x57
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278234] [<ffffffff8040a93d>]
sock_release+0x1a/0x76
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278237] [<ffffffff8040a9bb>]
sock_close+0x22/0x26
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278242] [<ffffffff802c34e0>]
__fput+0xd4/0x199
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278246] [<ffffffff802c35bd>]
fput+0x18/0x1a
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278274] [<ffffffffa028c2cf>]
svc_sock_free+0x3b/0x51 [sunrpc]
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278296] [<ffffffffa02960d6>]
svc_xprt_free+0x3b/0x4b [sunrpc]
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278317] [<ffffffffa029609b>]
? svc_xprt_free+0x0/0x4b [sunrpc]
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278321] [<ffffffff80358d15>]
kref_put+0x4b/0x57
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278342] [<ffffffffa02954db>]
svc_close_xprt+0x50/0x59 [sunrpc]
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278362] [<ffffffffa029552f>]
svc_close_all+0x4b/0x64 [sunrpc]
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278383] [<ffffffffa028b827>]
svc_destroy+0x99/0x13d [sunrpc]
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278404] [<ffffffffa028b97f>]
svc_exit_thread+0xb4/0xbd [sunrpc]
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278419] [<ffffffffa03178cc>]
nfsd+0x244/0x25e [nfsd]
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278431] [<ffffffffa0317688>] ?
nfsd+0x0/0x25e [nfsd]
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278436] [<ffffffff802561c1>]
kthread+0x49/0x76
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278440] [<ffffffff8021241a>]
child_rip+0xa/0x20
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278443] [<ffffffff80256178>] ?
kthread+0x0/0x76
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278446] [<ffffffff80212410>] ?
child_rip+0x0/0x20
Jun 29 19:48:50 dl380g6-3 kernel: [15764.278448] ---[ end trace
fdb0852e39bf7319 ]---
It smells like a race to me but I can't find/prove it.
-vlad
^ permalink raw reply
* [PATCH] c/r: Add AF_UNIX support (v10)
From: Dan Smith @ 2009-08-12 20:02 UTC (permalink / raw)
To: orenl; +Cc: containers, Oren Laaden, Alexey Dobriyan, netdev
This patch adds basic checkpoint/restart support for AF_UNIX sockets. It
has been tested with a single and multiple processes, and with data inflight
at the time of checkpoint. It supports socketpair()s, path-based, and
abstract sockets.
Changes in v10:
- Moved header structure definitions back to checkpoint_hdr.h
- Moved AF_UNIX checkpoint/restart code to net/unix/checkpoint.c
- Make sock_unix_*() functions only compile if CONFIG_UNIX=y
- Add TODO for CONFIG_UNIX=m case
Changes in v9:
- Fix double-free of skb's in the list and target holding queue in the
error path of sock_copy_buffers()
- Adjust use of ckpt_read_string() to match new signature
Changes in v8:
- Fix stale dev_alloc_skb() from before the conversion to skb_clone()
- Fix a couple of broken error paths
- Fix memory leak of kvec.iov_base on successful return from sendmsg()
- Fix condition for deciding when to run sock_cptrst_verify()
- Fix buffer queue copy algorithm to hold the lock during walk(s)
- Log the errno when either getname() or getpeer() fails
- Add comments about ancillary messages in the UNIX queue
- Add TODO comments for credential restore and flags via setsockopt()
- Add TODO comment about strangely-connected dgram sockets and the use
of sendmsg(peer)
Changes in v7:
- Fix failure to free iov_base in error path of sock_read_buffer()
- Change sock_read_buffer() to use _ckpt_read_obj_type() to get the
header length and then use ckpt_kread() directly to read the payload
- Change sock_read_buffers() to sock_unix_read_buffers() and break out
some common functionality to better accommodate the subsequent INET
patch
- Generalize sock_unix_getnames() into sock_getnames() so INET can use it
- Change skb_morph() to skb_clone() which uses the more common path and
still avoids the copy
- Add check to validate the socket type before creating socket
on restore
- Comment the CAP_NET_ADMIN override in sock_read_buffer_hdr
- Strengthen the comment about priming the buffer limits
- Change the objhash functions to deny direct checkpoint of sockets and
remove the reference counting function
- Change SOCKET_BUFFERS to SOCKET_QUEUE
- Change this,peer objrefs to signed integers
- Remove names from internal socket structures
- Fix handling of sock_copy_buffers() result
- Use ckpt_fill_fname() instead of d_path() for writing CWD
- Use sock_getname() and sock_getpeer() for proper security hookage
- Return -ENOSYS for unsupported socket families in checkpoint and restart
- Use sock_setsockopt() and sock_getsockopt() where possible to save and
restore socket option values
- Check for SOCK_DESTROY flag in the global verify function because none
of our supported socket types use it
- Check for SOCK_USE_WRITE_QUEUE in AF_UNIX restore function because
that flag should not be used on such a socket
- Check socket state in UNIX restart path to validate the subset of valid
values
Changes in v6:
- Moved the socket addresses to the per-type header
- Eliminated the HASCWD flag
- Remove use of ckpt_write_err() in restart paths
- Change the order in which buffers are read so that we can set the
socket's limit equal to the size of the image's buffers (if appropriate)
and then restore the original values afterwards.
- Use the ckpt_validate_errno() helper
- Add a check to make sure that we didn't restore a (UNIX) socket with
any skb's in the send buffer
- Fix up sock_unix_join() to not leave addr uninitialized for socketpair
- Remove inclusion of checkpoint_hdr.h in the socket files
- Make sock_unix_write_cwd() use ckpt_write_string() and use the new
ckpt_read_string() for reading the cwd
- Use the restored realcred credentials in sock_unix_join()
- Fix error path of the chdir_and_bind
- Change the algorithm for reloading the socket buffers to use sendmsg()
on the socket's peer for better accounting
- For DGRAM sockets, check the backlog value against the system max
to avoid letting a restart bypass the overloaded queue length
- Use sock_bind() instead of sock->ops->bind() to gain the security hook
- Change "restart" to "restore" in some of the function names
Changes in v5:
- Change laddr and raddr buffers in socket header to be long enough
for INET6 addresses
- Place socket.c and sock.h function definitions inside #ifdef
CONFIG_CHECKPOINT
- Add explicit check in sock_unix_makeaddr() to refuse if the
checkpoint image specifies an addr length of 0
- Split sock_unix_restart() into a few pieces to facilitate:
- Changed behavior of the unix restore code so that unlinked LISTEN
sockets don't do a bind()...unlink()
- Save the base path of a bound socket's path so that we can chdir()
to the base before bind() if it is a relative path
- Call bind() for any socket that is not established but has a
non-zero-length local address
- Enforce the current sysctl limit on socket buffer size during restart
unless the user holds CAP_NET_ADMIN
- Unlink a path-based socket before calling bind()
Changes in v4:
- Changed the signdness of rcvlowat, rcvtimeo, sndtimeo, and backlog
to match their struct sock definitions. This should avoid issues
with sign extension.
- Add a sock_cptrst_verify() function to be run at restore time to
validate several of the values in the checkpoint image against
limits, flag masks, etc.
- Write an error string with ctk_write_err() in the obscure cases
- Don't write socket buffers for listen sockets
- Sanity check address lengths before we agree to allocate memory
- Check the result of inserting the peer object in the objhash on
restart
- Check return value of sock_cptrst() on restart
- Change logic in remote getname() phase of checkpoint to not fail for
closed (et al) sockets
- Eliminate the memory copy while reading socket buffers on restart
Changes in v3:
- Move sock_file_checkpoint() above sock_file_restore()
- Change __sock_file_*() functions to do_sock_file_*()
- Adjust some of the struct cr_hdr_socket alignment
- Improve the sock_copy_buffers() algorithm to avoid locking the source
queue for the entire operation
- Fix alignment in the socket header struct(s)
- Move the per-protocol structure (ckpt_hdr_socket_un) out of the
common socket header and read/write it separately
- Fix missing call to sock_cptrst() in restore path
- Break out the socket joining into another function
- Fix failure to restore the socket address thus fixing getname()
- Check the state values on restart
- Fix case of state being TCP_CLOSE, which allows dgram sockets to be
properly connected (if appropriate) to their peer and maintain the
sockaddr for getname() operation
- Fix restoring a listening socket that has been unlink()'d
- Fix checkpointing sockets with an in-flight FD-passing SKB. Fail
with EBUSY.
- Fix checkpointing listening sockets with an unaccepted connection.
Fail with EBUSY.
- Changed 'un' to 'unix' in function and structure names
Changes in v2:
- Change GFP_KERNEL to GFP_ATOMIC in sock_copy_buffers() (this seems
to be rather common in other uses of skb_copy())
- Move the ckpt_hdr_socket structure definition to linux/socket.h
- Fix whitespace issue
- Move sock_file_checkpoint() to net/socket.c for symmetry
Cc: Oren Laaden <orenl@cs.columbia.edu>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: netdev@vger.kernel.org
Signed-off-by: Dan Smith <danms@us.ibm.com>
---
checkpoint/files.c | 7 +
checkpoint/objhash.c | 19 ++
include/linux/checkpoint.h | 10 +
include/linux/checkpoint_hdr.h | 72 ++++++
include/net/af_unix.h | 32 +++
include/net/sock.h | 11 +
net/Makefile | 2 +
net/checkpoint.c | 438 +++++++++++++++++++++++++++++++++
net/socket.c | 85 +++++++
net/unix/Makefile | 1 +
net/unix/checkpoint.c | 524 ++++++++++++++++++++++++++++++++++++++++
11 files changed, 1201 insertions(+), 0 deletions(-)
create mode 100644 net/checkpoint.c
create mode 100644 net/unix/checkpoint.c
diff --git a/checkpoint/files.c b/checkpoint/files.c
index 7aecac9..8729e00 100644
--- a/checkpoint/files.c
+++ b/checkpoint/files.c
@@ -21,6 +21,7 @@
#include <linux/syscalls.h>
#include <linux/checkpoint.h>
#include <linux/checkpoint_hdr.h>
+#include <net/sock.h>
/**************************************************************************
@@ -565,6 +566,12 @@ static struct restore_file_ops restore_file_ops[] = {
.file_type = CKPT_FILE_FIFO,
.restore = fifo_file_restore,
},
+ /* socket */
+ {
+ .file_name = "SOCKET",
+ .file_type = CKPT_FILE_SOCKET,
+ .restore = sock_file_restore,
+ },
};
static struct file *do_restore_file(struct ckpt_ctx *ctx)
diff --git a/checkpoint/objhash.c b/checkpoint/objhash.c
index a2fb278..4bfa1d8 100644
--- a/checkpoint/objhash.c
+++ b/checkpoint/objhash.c
@@ -20,6 +20,7 @@
#include <linux/user_namespace.h>
#include <linux/checkpoint.h>
#include <linux/checkpoint_hdr.h>
+#include <net/sock.h>
struct ckpt_obj;
struct ckpt_obj_ops;
@@ -234,6 +235,17 @@ static void obj_groupinfo_drop(void *ptr)
put_group_info((struct group_info *) ptr);
}
+static int obj_sock_grab(void *ptr)
+{
+ sock_hold((struct sock *) ptr);
+ return 0;
+}
+
+static void obj_sock_drop(void *ptr)
+{
+ sock_put((struct sock *) ptr);
+}
+
static struct ckpt_obj_ops ckpt_obj_ops[] = {
/* ignored object */
{
@@ -356,6 +368,13 @@ static struct ckpt_obj_ops ckpt_obj_ops[] = {
.checkpoint = checkpoint_groupinfo,
.restore = restore_groupinfo,
},
+ /* sock object */
+ {
+ .obj_name = "SOCKET",
+ .obj_type = CKPT_OBJ_SOCK,
+ .ref_drop = obj_sock_drop,
+ .ref_grab = obj_sock_grab,
+ },
};
diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h
index 60b230e..8a492e4 100644
--- a/include/linux/checkpoint.h
+++ b/include/linux/checkpoint.h
@@ -29,6 +29,7 @@
#include <linux/checkpoint_types.h>
#include <linux/checkpoint_hdr.h>
#include <linux/err.h>
+#include <net/sock.h>
/* ckpt_ctx: kflags */
#define CKPT_CTX_CHECKPOINT_BIT 0
@@ -78,6 +79,15 @@ extern int ckpt_read_consume(struct ckpt_ctx *ctx, int len, int type);
extern char *ckpt_fill_fname(struct path *path, struct path *root,
char *buf, int *len);
+/* socket functions */
+extern int ckpt_sock_getnames(struct ckpt_ctx *ctx,
+ struct socket *socket,
+ struct sockaddr *loc, unsigned *loc_len,
+ struct sockaddr *rem, unsigned *rem_len);
+extern struct ckpt_hdr_socket_queue *
+ckpt_sock_read_buffer_hdr(struct ckpt_ctx *ctx,
+ uint32_t *bufsize);
+
/* ckpt kflags */
#define ckpt_set_ctx_kflag(__ctx, __kflag) \
set_bit(__kflag##_BIT, &(__ctx)->kflags)
diff --git a/include/linux/checkpoint_hdr.h b/include/linux/checkpoint_hdr.h
index 4b85956..829ff2d 100644
--- a/include/linux/checkpoint_hdr.h
+++ b/include/linux/checkpoint_hdr.h
@@ -12,6 +12,8 @@
#include <linux/types.h>
#include <linux/utsname.h>
+#include <linux/socket.h>
+#include <linux/un.h>
/*
* To maintain compatibility between 32-bit and 64-bit architecture flavors,
@@ -88,6 +90,12 @@ enum {
CKPT_HDR_SIGHAND = 601,
+ CKPT_HDR_FD_SOCKET = 701,
+ CKPT_HDR_SOCKET,
+ CKPT_HDR_SOCKET_QUEUE,
+ CKPT_HDR_SOCKET_BUFFER,
+ CKPT_HDR_SOCKET_UNIX,
+
CKPT_HDR_TAIL = 9001,
CKPT_HDR_ERROR = 9999,
@@ -122,6 +130,7 @@ enum obj_type {
CKPT_OBJ_CRED,
CKPT_OBJ_USER,
CKPT_OBJ_GROUPINFO,
+ CKPT_OBJ_SOCK,
CKPT_OBJ_MAX
};
@@ -327,6 +336,7 @@ enum file_type {
CKPT_FILE_GENERIC,
CKPT_FILE_PIPE,
CKPT_FILE_FIFO,
+ CKPT_FILE_SOCKET,
CKPT_FILE_MAX
};
@@ -350,6 +360,10 @@ struct ckpt_hdr_file_pipe {
__s32 pipe_objref;
} __attribute__((aligned(8)));
+struct ckpt_hdr_file_socket {
+ struct ckpt_hdr_file common;
+} __attribute__((aligned(8)));
+
struct ckpt_hdr_utsns {
struct ckpt_hdr h;
char sysname[__NEW_UTS_LEN + 1];
@@ -504,6 +518,64 @@ struct ckpt_hdr_ipc_sem {
__u32 sem_nsems;
} __attribute__((aligned(8)));
+#define CKPT_UNIX_LINKED 1
+struct ckpt_hdr_socket_unix {
+ struct ckpt_hdr h;
+ __s32 this;
+ __s32 peer;
+ __u32 flags;
+ __u32 laddr_len;
+ __u32 raddr_len;
+ struct sockaddr_un laddr;
+ struct sockaddr_un raddr;
+} __attribute__ ((aligned(8)));
+
+struct ckpt_hdr_socket {
+ struct ckpt_hdr h;
+
+ struct { /* struct socket */
+ __u64 flags;
+ __u8 state;
+ } socket __attribute__ ((aligned(8)));
+
+ struct { /* struct sock_common */
+ __u32 bound_dev_if;
+ __u32 reuse;
+ __u16 family;
+ __u8 state;
+ } sock_common __attribute__ ((aligned(8)));
+
+ struct { /* struct sock */
+ __s64 rcvlowat;
+ __u64 flags;
+
+ __u32 err;
+ __u32 err_soft;
+ __u32 priority;
+ __s32 rcvbuf;
+ __s32 sndbuf;
+ __u16 type;
+ __s16 backlog;
+
+ __u8 protocol;
+ __u8 state;
+ __u8 shutdown;
+ __u8 userlocks;
+ __u8 no_check;
+
+ struct linger linger;
+ struct timeval rcvtimeo;
+ struct timeval sndtimeo;
+
+ } sock __attribute__ ((aligned(8)));
+
+} __attribute__ ((aligned(8)));
+
+struct ckpt_hdr_socket_queue {
+ struct ckpt_hdr h;
+ __u32 skb_count;
+ __u32 total_bytes;
+} __attribute__ ((aligned(8)));
#define CKPT_TST_OVERFLOW_16(a, b) \
((sizeof(a) > sizeof(b)) && ((a) > SHORT_MAX))
diff --git a/include/net/af_unix.h b/include/net/af_unix.h
index 1614d78..7aef51b 100644
--- a/include/net/af_unix.h
+++ b/include/net/af_unix.h
@@ -68,4 +68,36 @@ static inline int unix_sysctl_register(struct net *net) { return 0; }
static inline void unix_sysctl_unregister(struct net *net) {}
#endif
#endif
+
+#ifdef CONFIG_CHECKPOINT
+
+#ifdef CONFIG_UNIX_MODULE
+/* FIXME: Our current scheme won't work with CONFIG_UNIX=m */
+#error "CONFIG_UNIX=m not currently supported by CONFIG_CHECKPOINT"
+#endif
+
+#ifdef CONFIG_UNIX
+extern int sock_unix_checkpoint(struct ckpt_ctx *ctx,
+ struct socket *socket,
+ struct ckpt_hdr_socket *h);
+extern int sock_unix_restore(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_socket *h,
+ struct socket *socket);
+#else
+static inline int sock_unix_checkpoint(struct ckpt_ctx *ctx,
+ struct socket *socket,
+ struct ckpt_hdr_socket *h)
+{
+ return -ENOSYS;
+}
+
+static inline int sock_unix_restore(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_socket *h,
+ struct socket *socket)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_UNIX */
+#endif /* CONFIG_CHECKPOINT */
+
#endif
diff --git a/include/net/sock.h b/include/net/sock.h
index 43b9599..da75f2f 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1640,4 +1640,15 @@ extern int sysctl_optmem_max;
extern __u32 sysctl_wmem_default;
extern __u32 sysctl_rmem_default;
+#ifdef CONFIG_CHECKPOINT
+/* Checkpoint/Restart Functions */
+struct ckpt_ctx;
+struct ckpt_hdr_socket;
+extern int sock_file_checkpoint(struct ckpt_ctx *, void *);
+extern void *sock_file_restore(struct ckpt_ctx *);
+extern struct socket *do_sock_file_restore(struct ckpt_ctx *,
+ struct ckpt_hdr_socket *);
+extern int do_sock_file_checkpoint(struct ckpt_ctx *ctx, struct file *file);
+#endif
+
#endif /* _SOCK_H */
diff --git a/net/Makefile b/net/Makefile
index ba324ae..91d12fe 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -66,3 +66,5 @@ ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_SYSCTL) += sysctl_net.o
endif
obj-$(CONFIG_WIMAX) += wimax/
+
+obj-$(CONFIG_CHECKPOINT) += checkpoint.o
diff --git a/net/checkpoint.c b/net/checkpoint.c
new file mode 100644
index 0000000..ebbd68a
--- /dev/null
+++ b/net/checkpoint.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright 2009 IBM Corporation
+ *
+ * Author: Dan Smith <danms@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#include <linux/socket.h>
+#include <linux/mount.h>
+#include <linux/file.h>
+#include <linux/namei.h>
+#include <linux/syscalls.h>
+#include <linux/sched.h>
+#include <linux/fs_struct.h>
+
+#include <net/af_unix.h>
+#include <net/tcp_states.h>
+
+#include <linux/checkpoint.h>
+#include <linux/checkpoint_hdr.h>
+
+static int sock_copy_buffers(struct sk_buff_head *from,
+ struct sk_buff_head *to,
+ uint32_t *total_bytes)
+{
+ int count1 = 0;
+ int count2 = 0;
+ int i;
+ struct sk_buff *skb;
+ struct sk_buff **skbs;
+
+ *total_bytes = 0;
+
+ spin_lock(&from->lock);
+ skb_queue_walk(from, skb)
+ count1++;
+ spin_unlock(&from->lock);
+
+ skbs = kzalloc(sizeof(*skbs) * count1, GFP_KERNEL);
+ if (!skbs)
+ return -ENOMEM;
+
+ for (i = 0; i < count1; i++) {
+ skbs[i] = dev_alloc_skb(0);
+ if (!skbs[i])
+ goto err;
+ }
+
+ i = 0;
+ spin_lock(&from->lock);
+ skb_queue_walk(from, skb) {
+ if (++count2 > count1)
+ break; /* The queue changed as we read it */
+
+ skb_morph(skbs[i], skb);
+ skb_queue_tail(to, skbs[i]);
+
+ *total_bytes += skb->len;
+ i++;
+ }
+ spin_unlock(&from->lock);
+
+ if (count1 != count2)
+ goto err;
+
+ kfree(skbs);
+
+ return count1;
+ err:
+ while (skb_dequeue(to))
+ ; /* Pull all the buffers out of the queue */
+ for (i = 0; i < count1; i++)
+ kfree_skb(skbs[i]);
+ kfree(skbs);
+
+ return -EAGAIN;
+}
+
+static int __sock_write_buffers(struct ckpt_ctx *ctx,
+ struct sk_buff_head *queue)
+{
+ struct sk_buff *skb;
+ int ret = 0;
+
+ skb_queue_walk(queue, skb) {
+ /* FIXME: This could be a false positive for non-unix
+ * buffers, so add a type check here in the
+ * future
+ */
+ if (UNIXCB(skb).fp) {
+ ckpt_write_err(ctx, "fd-passing is not supported");
+ return -EBUSY;
+ }
+
+ /* The other ancillary messages are always present
+ * unlike descriptors. Even though we can't detect
+ * them and fail the checkpoint, we're not at risk
+ * because we don't save out (or restore) the control
+ * information contained in the skb.
+ */
+
+ ret = ckpt_write_obj_type(ctx, skb->data, skb->len,
+ CKPT_HDR_SOCKET_BUFFER);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sock_write_buffers(struct ckpt_ctx *ctx, struct sk_buff_head *queue)
+{
+ struct ckpt_hdr_socket_queue *h;
+ struct sk_buff_head tmpq;
+ int ret = -ENOMEM;
+
+ h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_SOCKET_QUEUE);
+ if (!h)
+ return -ENOMEM;
+
+ skb_queue_head_init(&tmpq);
+
+ ret = sock_copy_buffers(queue, &tmpq, &h->total_bytes);
+ if (ret < 0)
+ goto out;
+
+ h->skb_count = ret;
+ ret = ckpt_write_obj(ctx, (struct ckpt_hdr *) h);
+ if (!ret)
+ ret = __sock_write_buffers(ctx, &tmpq);
+
+ out:
+ ckpt_hdr_put(ctx, h);
+ __skb_queue_purge(&tmpq);
+
+ return ret;
+}
+
+int ckpt_sock_getnames(struct ckpt_ctx *ctx,
+ struct socket *socket,
+ struct sockaddr *loc, unsigned *loc_len,
+ struct sockaddr *rem, unsigned *rem_len)
+{
+ int ret;
+
+ ret = sock_getname(socket, loc, loc_len);
+ if (ret) {
+ ckpt_write_err(ctx, "Unable to getname of local: %i", ret);
+ return -EINVAL;
+ }
+
+ ret = sock_getpeer(socket, rem, rem_len);
+ if (ret) {
+ if ((socket->sk->sk_type != SOCK_DGRAM) &&
+ (socket->sk->sk_state == TCP_ESTABLISHED)) {
+ ckpt_write_err(ctx, "Unable to getname of remote: %i",
+ ret);
+ return -EINVAL;
+ }
+ *rem_len = 0;
+ }
+
+ return 0;
+}
+
+static int sock_cptrst_verify(struct ckpt_hdr_socket *h)
+{
+ uint8_t userlocks_mask = SOCK_SNDBUF_LOCK | SOCK_RCVBUF_LOCK |
+ SOCK_BINDADDR_LOCK | SOCK_BINDPORT_LOCK;
+
+ if (h->sock.shutdown & ~SHUTDOWN_MASK)
+ return -EINVAL;
+ if (h->sock.userlocks & ~userlocks_mask)
+ return -EINVAL;
+ if (!ckpt_validate_errno(h->sock.err))
+ return -EINVAL;
+
+ /* None of our supported types use this flag */
+ if (h->sock.flags & SOCK_DESTROY)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sock_cptrst_opt(int op, struct socket *socket,
+ int optname, char *opt, int len)
+{
+ mm_segment_t fs;
+ int ret;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ if (op == CKPT_CPT)
+ ret = sock_getsockopt(socket, SOL_SOCKET, optname, opt, &len);
+ else
+ ret = sock_setsockopt(socket, SOL_SOCKET, optname, opt, len);
+
+ set_fs(fs);
+
+ return ret;
+}
+
+#define CKPT_COPY_SOPT(op, sock, name, opt) \
+ sock_cptrst_opt(op, sock->sk_socket, name, (char *)opt, sizeof(*opt))
+
+static int sock_cptrst_bufopts(int op, struct sock *sock,
+ struct ckpt_hdr_socket *h)
+
+{
+ if (CKPT_COPY_SOPT(op, sock, SO_RCVBUF, &h->sock.rcvbuf))
+ if ((op == CKPT_RST) &&
+ CKPT_COPY_SOPT(op, sock, SO_RCVBUFFORCE, &h->sock.rcvbuf)) {
+ ckpt_debug("Failed to set SO_RCVBUF");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_SNDBUF, &h->sock.sndbuf))
+ if ((op == CKPT_RST) &&
+ CKPT_COPY_SOPT(op, sock, SO_SNDBUFFORCE, &h->sock.sndbuf)) {
+ ckpt_debug("Failed to set SO_SNDBUF");
+ return -EINVAL;
+ }
+
+ /* It's silly that we have to fight ourselves here, but
+ * sock_setsockopt() doubles the initial value, so divide here
+ * to store the user's value and avoid doubling on restart
+ */
+ if ((op == CKPT_CPT) && (h->sock.rcvbuf != SOCK_MIN_RCVBUF))
+ h->sock.rcvbuf >>= 1;
+
+ if ((op == CKPT_CPT) && (h->sock.sndbuf != SOCK_MIN_SNDBUF))
+ h->sock.sndbuf >>= 1;
+
+ return 0;
+}
+
+static int sock_cptrst(struct ckpt_ctx *ctx,
+ struct sock *sock,
+ struct ckpt_hdr_socket *h,
+ int op)
+{
+ if (sock->sk_socket) {
+ CKPT_COPY(op, h->socket.flags, sock->sk_socket->flags);
+ CKPT_COPY(op, h->socket.state, sock->sk_socket->state);
+ }
+
+ CKPT_COPY(op, h->sock_common.bound_dev_if, sock->sk_bound_dev_if);
+ CKPT_COPY(op, h->sock_common.family, sock->sk_family);
+
+ CKPT_COPY(op, h->sock.shutdown, sock->sk_shutdown);
+ CKPT_COPY(op, h->sock.userlocks, sock->sk_userlocks);
+ CKPT_COPY(op, h->sock.no_check, sock->sk_no_check);
+ CKPT_COPY(op, h->sock.protocol, sock->sk_protocol);
+ CKPT_COPY(op, h->sock.err, sock->sk_err);
+ CKPT_COPY(op, h->sock.err_soft, sock->sk_err_soft);
+ CKPT_COPY(op, h->sock.type, sock->sk_type);
+ CKPT_COPY(op, h->sock.state, sock->sk_state);
+ CKPT_COPY(op, h->sock.backlog, sock->sk_max_ack_backlog);
+
+ /* TODO:
+ * Break out setting each of the flags to use setsockopt() or
+ * perform proper security check
+ */
+ CKPT_COPY(op, h->sock.flags, sock->sk_flags);
+
+ if (sock_cptrst_bufopts(op, sock, h))
+ return -EINVAL;
+
+ if (CKPT_COPY_SOPT(op, sock, SO_REUSEADDR, &h->sock_common.reuse)) {
+ ckpt_debug("Failed to set SO_REUSEADDR");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_PRIORITY, &h->sock.priority)) {
+ ckpt_debug("Failed to set SO_PRIORITY");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_RCVLOWAT, &h->sock.rcvlowat)) {
+ ckpt_debug("Failed to set SO_RCVLOWAT");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_LINGER, &h->sock.linger)) {
+ ckpt_debug("Failed to set SO_LINGER");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_SNDTIMEO, &h->sock.sndtimeo)) {
+ ckpt_debug("Failed to set SO_SNDTIMEO");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_RCVTIMEO, &h->sock.rcvtimeo)) {
+ ckpt_debug("Failed to set SO_RCVTIMEO");
+ return -EINVAL;
+ }
+
+ if ((h->socket.state == SS_CONNECTED) &&
+ (h->sock.state != TCP_ESTABLISHED)) {
+ ckpt_debug("socket/sock in inconsistent state: %i/%i",
+ h->socket.state, h->sock.state);
+ return -EINVAL;
+ } else if ((h->sock.state < TCP_ESTABLISHED) ||
+ (h->sock.state >= TCP_MAX_STATES)) {
+ ckpt_debug("sock in invalid state: %i", h->sock.state);
+ return -EINVAL;
+ } else if ((h->socket.state < SS_FREE) ||
+ (h->socket.state > SS_DISCONNECTING)) {
+ ckpt_debug("socket in invalid state: %i",
+ h->socket.state);
+ return -EINVAL;
+ }
+
+ if (op == CKPT_RST)
+ return sock_cptrst_verify(h);
+ else
+ return 0;
+}
+
+int do_sock_file_checkpoint(struct ckpt_ctx *ctx, struct file *file)
+{
+ struct socket *socket = file->private_data;
+ struct sock *sock = socket->sk;
+ struct ckpt_hdr_socket *h;
+ int ret = 0;
+
+ h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_SOCKET);
+ if (!h)
+ return -ENOMEM;
+
+ ret = sock_cptrst(ctx, sock, h, CKPT_CPT);
+ if (ret)
+ goto out;
+
+ if (sock->sk_family == AF_UNIX) {
+ ret = sock_unix_checkpoint(ctx, socket, h);
+ if (ret)
+ goto out;
+ } else {
+ ckpt_write_err(ctx, "unsupported socket family %i",
+ sock->sk_family);
+ ret = -ENOSYS;
+ goto out;
+ }
+
+ if (sock->sk_state != TCP_LISTEN) {
+ ret = sock_write_buffers(ctx, &sock->sk_receive_queue);
+ if (ret)
+ goto out;
+
+ ret = sock_write_buffers(ctx, &sock->sk_write_queue);
+ if (ret)
+ goto out;
+ }
+ out:
+ ckpt_hdr_put(ctx, h);
+
+ return ret;
+}
+
+struct ckpt_hdr_socket_queue *ckpt_sock_read_buffer_hdr(struct ckpt_ctx *ctx,
+ uint32_t *bufsize)
+{
+ struct ckpt_hdr_socket_queue *h;
+ int err = 0;
+
+ h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SOCKET_QUEUE);
+ if (IS_ERR(h))
+ return h;
+
+ if (!bufsize) {
+ if (h->total_bytes != 0) {
+ ckpt_debug("Expected empty buffer, got %u\n",
+ h->total_bytes);
+ err = -EINVAL;
+ }
+ } else if (h->total_bytes > *bufsize) {
+ /* NB: We let CAP_NET_ADMIN override the system buffer limit
+ * as setsockopt() does
+ */
+ if (capable(CAP_NET_ADMIN))
+ *bufsize = h->total_bytes;
+ else {
+ ckpt_debug("Buffer total %u exceeds limit %u\n",
+ h->total_bytes, *bufsize);
+ err = -EINVAL;
+ }
+ }
+
+ if (err) {
+ ckpt_hdr_put(ctx, h);
+ return ERR_PTR(err);
+ } else
+ return h;
+}
+
+struct socket *do_sock_file_restore(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_socket *h)
+{
+ struct socket *socket;
+ int ret;
+
+ if ((h->sock.type != SOCK_DGRAM) && (h->sock.type != SOCK_STREAM)) {
+ ckpt_debug("Socket type %i not supported", h->sock.type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = sock_create(h->sock_common.family, h->sock.type, 0, &socket);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ if (h->sock_common.family == AF_UNIX) {
+ ret = sock_unix_restore(ctx, h, socket);
+ ckpt_debug("sock_unix_restore: %i\n", ret);
+ } else {
+ ckpt_debug("unsupported family %i\n", h->sock_common.family);
+ ret = -ENOSYS;
+ }
+
+ if (ret)
+ goto out;
+
+ ret = sock_cptrst(ctx, socket->sk, h, CKPT_RST);
+ out:
+ if (ret) {
+ sock_release(socket);
+ socket = ERR_PTR(ret);
+ }
+
+ return socket;
+}
+
diff --git a/net/socket.c b/net/socket.c
index 65e7698..8732fe2 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -96,6 +96,8 @@
#include <net/sock.h>
#include <linux/netfilter.h>
+#include <linux/checkpoint.h>
+
static int sock_no_open(struct inode *irrelevant, struct file *dontcare);
static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
@@ -140,6 +142,9 @@ static const struct file_operations socket_file_ops = {
.sendpage = sock_sendpage,
.splice_write = generic_splice_sendpage,
.splice_read = sock_splice_read,
+#ifdef CONFIG_CHECKPOINT
+ .checkpoint = sock_file_checkpoint,
+#endif
};
/*
@@ -415,6 +420,86 @@ int sock_map_fd(struct socket *sock, int flags)
return fd;
}
+#ifdef CONFIG_CHECKPOINT
+int sock_file_checkpoint(struct ckpt_ctx *ctx, void *ptr)
+{
+ struct ckpt_hdr_file_socket *h;
+ int ret;
+ struct file *file = ptr;
+
+ h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_FILE);
+ if (!h)
+ return -ENOMEM;
+
+ h->common.f_type = CKPT_FILE_SOCKET;
+
+ ret = checkpoint_file_common(ctx, file, &h->common);
+ if (ret < 0)
+ goto out;
+ ret = ckpt_write_obj(ctx, (struct ckpt_hdr *) h);
+ if (ret < 0)
+ goto out;
+
+ ret = do_sock_file_checkpoint(ctx, file);
+ out:
+ ckpt_hdr_put(ctx, h);
+ return ret;
+}
+
+static struct file *sock_alloc_attach_fd(struct socket *socket)
+{
+ struct file *file;
+ int err;
+
+ file = get_empty_filp();
+ if (!file)
+ return ERR_PTR(ENOMEM);
+
+ err = sock_attach_fd(socket, file, 0);
+ if (err < 0) {
+ put_filp(file);
+ file = ERR_PTR(err);
+ }
+
+ return file;
+}
+
+void *sock_file_restore(struct ckpt_ctx *ctx)
+{
+ struct ckpt_hdr_socket *h = NULL;
+ struct socket *socket = NULL;
+ struct file *file = NULL;
+ int err;
+
+ h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SOCKET);
+ if (IS_ERR(h))
+ return h;
+
+ socket = do_sock_file_restore(ctx, h);
+ if (IS_ERR(socket)) {
+ err = PTR_ERR(socket);
+ goto err_put;
+ }
+
+ file = sock_alloc_attach_fd(socket);
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ goto err_release;
+ }
+
+ ckpt_hdr_put(ctx, h);
+
+ return file;
+
+ err_release:
+ sock_release(socket);
+ err_put:
+ ckpt_hdr_put(ctx, h);
+
+ return ERR_PTR(err);
+}
+#endif /* CONFIG_CHECKPOINT */
+
static struct socket *sock_from_file(struct file *file, int *err)
{
if (file->f_op == &socket_file_ops)
diff --git a/net/unix/Makefile b/net/unix/Makefile
index b852a2b..fbff1e6 100644
--- a/net/unix/Makefile
+++ b/net/unix/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_UNIX) += unix.o
unix-y := af_unix.o garbage.o
unix-$(CONFIG_SYSCTL) += sysctl_net_unix.o
+unix-$(CONFIG_CHECKPOINT) += checkpoint.o
diff --git a/net/unix/checkpoint.c b/net/unix/checkpoint.c
new file mode 100644
index 0000000..007899b
--- /dev/null
+++ b/net/unix/checkpoint.c
@@ -0,0 +1,524 @@
+#include <linux/namei.h>
+#include <linux/file.h>
+#include <linux/fs_struct.h>
+#include <linux/checkpoint.h>
+#include <linux/checkpoint_hdr.h>
+#include <net/af_unix.h>
+#include <net/tcp_states.h>
+
+#define UNIX_ADDR_EMPTY(a) (a <= sizeof(short))
+
+static inline int sock_unix_need_cwd(struct sockaddr_un *addr,
+ unsigned long len)
+{
+ return (!UNIX_ADDR_EMPTY(len)) &&
+ addr->sun_path[0] &&
+ (addr->sun_path[0] != '/');
+}
+
+static int sock_unix_write_cwd(struct ckpt_ctx *ctx,
+ struct sock *sock,
+ const char *sockpath)
+{
+ struct path path;
+ char *buf;
+ char *fqpath;
+ int offset;
+ int len = PATH_MAX;
+ int ret = -ENOENT;
+
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ path.dentry = unix_sk(sock)->dentry;
+ path.mnt = unix_sk(sock)->mnt;
+
+ fqpath = ckpt_fill_fname(&path, &ctx->fs_mnt, buf, &len);
+ if (IS_ERR(fqpath)) {
+ ret = PTR_ERR(fqpath);
+ goto out;
+ }
+
+ offset = strlen(fqpath) - strlen(sockpath);
+ if (offset <= 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fqpath[offset] = '\0';
+
+ ckpt_debug("writing socket directory: %s\n", fqpath);
+ ret = ckpt_write_string(ctx, fqpath, strlen(fqpath));
+ out:
+ kfree(buf);
+ return ret;
+}
+
+int sock_unix_checkpoint(struct ckpt_ctx *ctx,
+ struct socket *socket,
+ struct ckpt_hdr_socket *h)
+{
+ struct unix_sock *sk = unix_sk(socket->sk);
+ struct unix_sock *pr = unix_sk(sk->peer);
+ struct ckpt_hdr_socket_unix *un;
+ int new;
+ int ret = -ENOMEM;
+
+ if ((socket->sk->sk_state == TCP_LISTEN) &&
+ !skb_queue_empty(&socket->sk->sk_receive_queue)) {
+ ckpt_write_err(ctx, "listening socket has unaccepted peers");
+ return -EBUSY;
+ }
+
+ un = ckpt_hdr_get_type(ctx, sizeof(*un), CKPT_HDR_SOCKET_UNIX);
+ if (!un)
+ return -EINVAL;
+
+ ret = ckpt_sock_getnames(ctx, socket,
+ (struct sockaddr *)&un->laddr, &un->laddr_len,
+ (struct sockaddr *)&un->raddr, &un->raddr_len);
+ if (ret)
+ goto out;
+
+ if (sk->dentry && (sk->dentry->d_inode->i_nlink > 0))
+ un->flags |= CKPT_UNIX_LINKED;
+
+ un->this = ckpt_obj_lookup_add(ctx, sk, CKPT_OBJ_SOCK, &new);
+ if (un->this < 0)
+ goto out;
+
+ if (sk->peer)
+ un->peer = ckpt_obj_lookup_add(ctx, pr, CKPT_OBJ_SOCK, &new);
+ else
+ un->peer = 0;
+
+ if (un->peer < 0) {
+ ret = un->peer;
+ goto out;
+ }
+
+ ret = ckpt_write_obj(ctx, (struct ckpt_hdr *) h);
+ if (ret < 0)
+ goto out;
+
+ ret = ckpt_write_obj(ctx, (struct ckpt_hdr *) un);
+ if (ret < 0)
+ goto out;
+
+ if (sock_unix_need_cwd(&un->laddr, un->laddr_len))
+ ret = sock_unix_write_cwd(ctx, socket->sk, un->laddr.sun_path);
+ out:
+ ckpt_hdr_put(ctx, un);
+
+ return ret;
+}
+
+static int sock_read_buffer_sendmsg(struct ckpt_ctx *ctx, struct sock *sock)
+{
+ struct msghdr msg;
+ struct kvec kvec;
+ int ret = 0;
+ int len;
+
+ memset(&msg, 0, sizeof(msg));
+
+ len = _ckpt_read_obj_type(ctx, NULL, 0, CKPT_HDR_SOCKET_BUFFER);
+ if (len < 0)
+ return len;
+
+ if (len > SKB_MAX_ALLOC) {
+ ckpt_debug("Socket buffer too big (%i > %lu)",
+ len, SKB_MAX_ALLOC);
+ return -ENOSPC;
+ }
+
+ kvec.iov_len = len;
+ kvec.iov_base = kmalloc(len, GFP_KERNEL);
+ if (!kvec.iov_base)
+ return -ENOMEM;
+
+ ret = ckpt_kread(ctx, kvec.iov_base, len);
+ if (ret < 0)
+ goto out;
+
+ ret = kernel_sendmsg(sock->sk_socket, &msg, &kvec, 1, len);
+ ckpt_debug("kernel_sendmsg(%i): %i\n", len, ret);
+ if ((ret > 0) && (ret != len))
+ ret = -ENOMEM;
+ out:
+ kfree(kvec.iov_base);
+
+ return ret;
+}
+
+static int sock_unix_read_buffers(struct ckpt_ctx *ctx,
+ struct sock *sock,
+ uint32_t *bufsize)
+{
+ uint8_t sock_shutdown;
+ struct ckpt_hdr_socket_queue *h;
+ int ret = 0;
+ int i;
+
+ h = ckpt_sock_read_buffer_hdr(ctx, bufsize);
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ /* If peer is shutdown, unshutdown it for this process */
+ sock_shutdown = sock->sk_shutdown;
+ sock->sk_shutdown &= ~SHUTDOWN_MASK;
+
+ for (i = 0; i < h->skb_count; i++) {
+ ret = sock_read_buffer_sendmsg(ctx, sock);
+ ckpt_debug("read_buffer_sendmsg(%i): %i\n", i, ret);
+ if (ret < 0)
+ break;
+
+ if (ret > h->total_bytes) {
+ ckpt_debug("Buffers exceeded claim");
+ ret = -EINVAL;
+ break;
+ }
+
+ h->total_bytes -= ret;
+ ret = 0;
+ }
+
+ sock->sk_shutdown = sock_shutdown;
+ ckpt_hdr_put(ctx, h);
+
+ return ret;
+}
+
+static struct unix_address *sock_unix_makeaddr(struct sockaddr_un *sun_addr,
+ unsigned len)
+{
+ struct unix_address *addr;
+
+ if (len > sizeof(struct sockaddr_un))
+ return ERR_PTR(-EINVAL);
+
+ addr = kmalloc(sizeof(*addr) + len, GFP_KERNEL);
+ if (!addr)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(addr->name, sun_addr, len);
+ addr->len = len;
+ atomic_set(&addr->refcnt, 1);
+
+ return addr;
+}
+
+static int sock_unix_join(struct ckpt_ctx *ctx,
+ struct sock *a,
+ struct sock *b,
+ struct ckpt_hdr_socket_unix *un)
+{
+ struct unix_address *addr = NULL;
+
+ /* FIXME: Do we need to call some security hooks here? */
+
+ sock_hold(a);
+ sock_hold(b);
+
+ unix_sk(a)->peer = b;
+ unix_sk(b)->peer = a;
+
+ /* TODO:
+ * Checkpoint the credentials, restore them here if the values match
+ * the restored creds or we may_setuid()
+ */
+
+ a->sk_peercred.pid = task_tgid_vnr(current);
+ a->sk_peercred.uid = ctx->realcred->uid;
+ a->sk_peercred.gid = ctx->realcred->gid;
+
+ b->sk_peercred.pid = a->sk_peercred.pid;
+ b->sk_peercred.uid = a->sk_peercred.uid;
+ b->sk_peercred.gid = a->sk_peercred.gid;
+
+ if (!UNIX_ADDR_EMPTY(un->raddr_len))
+ addr = sock_unix_makeaddr(&un->raddr, un->raddr_len);
+ else if (!UNIX_ADDR_EMPTY(un->laddr_len))
+ addr = sock_unix_makeaddr(&un->laddr, un->laddr_len);
+
+ if (IS_ERR(addr))
+ return PTR_ERR(addr);
+ else if (addr) {
+ atomic_inc(&addr->refcnt); /* Held by both ends */
+ unix_sk(a)->addr = unix_sk(b)->addr = addr;
+ }
+
+ return 0;
+}
+
+static int sock_unix_restore_connected(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_socket *h,
+ struct ckpt_hdr_socket_unix *un,
+ struct socket *socket)
+{
+ struct sock *this = ckpt_obj_fetch(ctx, un->this, CKPT_OBJ_SOCK);
+ struct sock *peer = ckpt_obj_fetch(ctx, un->peer, CKPT_OBJ_SOCK);
+ struct socket *tmp = NULL;
+ int ret;
+
+ if (!IS_ERR(this) && !IS_ERR(peer)) {
+ /* We're last */
+ struct socket *old = this->sk_socket;
+
+ old->sk = NULL;
+ sock_release(old);
+ sock_graft(this, socket);
+
+ } else if ((PTR_ERR(this) == -EINVAL) && (PTR_ERR(peer) == -EINVAL)) {
+ /* We're first */
+ int family = socket->sk->sk_family;
+ int type = socket->sk->sk_type;
+
+ ret = sock_create(family, type, 0, &tmp);
+ ckpt_debug("sock_create: %i\n", ret);
+ if (ret)
+ goto out;
+
+ this = socket->sk;
+ peer = tmp->sk;
+
+ ret = ckpt_obj_insert(ctx, this, un->this, CKPT_OBJ_SOCK);
+ if (ret < 0)
+ goto out;
+
+ ret = ckpt_obj_insert(ctx, peer, un->peer, CKPT_OBJ_SOCK);
+ if (ret < 0)
+ goto out;
+
+ ret = sock_unix_join(ctx, this, peer, un);
+ ckpt_debug("sock_unix_join: %i\n", ret);
+ if (ret)
+ goto out;
+
+ } else {
+ ckpt_debug("Order Error\n");
+ ret = PTR_ERR(this);
+ goto out;
+ }
+
+ /* Prime the socket's buffer limit with the maximum. These will be
+ * overwritten with the values in the checkpoint stream in a later
+ * phase.
+ */
+ peer->sk_userlocks |= SOCK_SNDBUF_LOCK;
+ peer->sk_sndbuf = sysctl_wmem_max;
+
+ /* Read my buffers and sendmsg() them back to me via my peer */
+
+ /* TODO: handle the unconnected case, as well, as the case
+ * where sendto() has been used on some of the buffers
+ */
+
+ ret = sock_unix_read_buffers(ctx, peer, &peer->sk_sndbuf);
+ ckpt_debug("sock_unix_read_buffers: %i\n", ret);
+ if (ret)
+ goto out;
+
+ /* Read peer's buffers and expect 0 */
+ ret = sock_unix_read_buffers(ctx, peer, NULL);
+ out:
+ if (tmp && ret)
+ sock_release(tmp);
+
+ return ret;
+}
+
+static int sock_unix_unlink(const char *name)
+{
+ struct path spath;
+ struct path ppath;
+ int ret;
+
+ ret = kern_path(name, 0, &spath);
+ if (ret)
+ return ret;
+
+ ret = kern_path(name, LOOKUP_PARENT, &ppath);
+ if (ret)
+ goto out_s;
+
+ if (!spath.dentry) {
+ ckpt_debug("No dentry found for %s\n", name);
+ ret = -ENOENT;
+ goto out_p;
+ }
+
+ if (!ppath.dentry || !ppath.dentry->d_inode) {
+ ckpt_debug("No inode for parent of %s\n", name);
+ ret = -ENOENT;
+ goto out_p;
+ }
+
+ ret = vfs_unlink(ppath.dentry->d_inode, spath.dentry);
+ out_p:
+ path_put(&ppath);
+ out_s:
+ path_put(&spath);
+
+ return ret;
+}
+
+/* Call bind() for socket, optionally changing (temporarily) to @path first
+ * if non-NULL
+ */
+static int sock_unix_chdir_and_bind(struct socket *socket,
+ const char *path,
+ struct sockaddr *addr,
+ unsigned long addrlen)
+{
+ struct sockaddr_un *un = (struct sockaddr_un *)addr;
+ int ret;
+ struct path cur;
+ struct path dir;
+
+ if (path) {
+ ckpt_debug("switching to cwd %s for unix bind", path);
+
+ ret = kern_path(path, 0, &dir);
+ if (ret)
+ return ret;
+
+ ret = inode_permission(dir.dentry->d_inode,
+ MAY_EXEC | MAY_ACCESS);
+ if (ret)
+ goto out;
+
+ write_lock(¤t->fs->lock);
+ cur = current->fs->pwd;
+ current->fs->pwd = dir;
+ write_unlock(¤t->fs->lock);
+ }
+
+ ret = sock_unix_unlink(un->sun_path);
+ ckpt_debug("unlink(%s): %i\n", un->sun_path, ret);
+ if ((ret == 0) || (ret == -ENOENT))
+ ret = sock_bind(socket, addr, addrlen);
+
+ if (path) {
+ write_lock(¤t->fs->lock);
+ current->fs->pwd = cur;
+ write_unlock(¤t->fs->lock);
+ }
+ out:
+ if (path)
+ path_put(&dir);
+
+ return ret;
+}
+
+static int sock_unix_fakebind(struct socket *socket,
+ struct sockaddr_un *addr,
+ unsigned long len)
+{
+ struct unix_address *uaddr;
+
+ uaddr = sock_unix_makeaddr(addr, len);
+ if (IS_ERR(uaddr))
+ return PTR_ERR(uaddr);
+
+ unix_sk(socket->sk)->addr = uaddr;
+
+ return 0;
+}
+
+static int sock_unix_bind(struct ckpt_hdr_socket *h,
+ struct ckpt_hdr_socket_unix *un,
+ struct socket *socket,
+ const char *path)
+{
+ struct sockaddr *addr = (struct sockaddr *)&un->laddr;
+ unsigned long len = un->laddr_len;
+
+ if (!un->laddr.sun_path[0])
+ return sock_bind(socket, addr, len);
+ else if (!(un->flags & CKPT_UNIX_LINKED))
+ return sock_unix_fakebind(socket, &un->laddr, len);
+ else
+ return sock_unix_chdir_and_bind(socket, path, addr, len);
+}
+
+/* Some easy pre-flight checks before we get underway */
+static int sock_unix_precheck(struct socket *socket,
+ struct ckpt_hdr_socket *h)
+{
+ struct net *net = sock_net(socket->sk);
+
+ if ((h->socket.state == SS_CONNECTING) ||
+ (h->socket.state == SS_DISCONNECTING) ||
+ (h->socket.state == SS_FREE)) {
+ ckpt_debug("AF_UNIX socket can't be SS_(DIS)CONNECTING");
+ return -EINVAL;
+ }
+
+ /* AF_UNIX overloads the backlog setting to define the maximum
+ * queue length for DGRAM sockets. Make sure we don't let the
+ * caller exceed that value on restart.
+ */
+ if ((h->sock.type == SOCK_DGRAM) &&
+ (h->sock.backlog > net->unx.sysctl_max_dgram_qlen)) {
+ ckpt_debug("DGRAM backlog of %i exceeds system max of %i\n",
+ h->sock.backlog, net->unx.sysctl_max_dgram_qlen);
+ return -EINVAL;
+ }
+
+ if (h->sock.flags & SOCK_USE_WRITE_QUEUE) {
+ ckpt_debug("AF_UNIX socket has SOCK_USE_WRITE_QUEUE set");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int sock_unix_restore(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_socket *h,
+ struct socket *socket)
+{
+ struct ckpt_hdr_socket_unix *un;
+ int ret = -EINVAL;
+ char *cwd = NULL;
+
+ ret = sock_unix_precheck(socket, h);
+ if (ret)
+ return ret;
+
+ un = ckpt_read_obj_type(ctx, sizeof(*un), CKPT_HDR_SOCKET_UNIX);
+ if (IS_ERR(un))
+ return PTR_ERR(un);
+
+ if (un->peer < 0)
+ goto out;
+
+ if (sock_unix_need_cwd(&un->laddr, un->laddr_len)) {
+ cwd = ckpt_read_string(ctx, PATH_MAX);
+ if (IS_ERR(cwd)) {
+ ret = PTR_ERR(cwd);
+ goto out;
+ }
+ }
+
+ if ((h->sock.state != TCP_ESTABLISHED) &&
+ !UNIX_ADDR_EMPTY(un->laddr_len)) {
+ ret = sock_unix_bind(h, un, socket, cwd);
+ if (ret)
+ goto out;
+ }
+
+ if ((h->sock.state == TCP_ESTABLISHED) || (h->sock.state == TCP_CLOSE))
+ ret = sock_unix_restore_connected(ctx, h, un, socket);
+ else if (h->sock.state == TCP_LISTEN)
+ ret = socket->ops->listen(socket, h->sock.backlog);
+ else
+ ckpt_debug("unsupported UNIX socket state %i\n", h->sock.state);
+ out:
+ ckpt_hdr_put(ctx, un);
+ kfree(cwd);
+ return ret;
+}
+
--
1.6.2.5
^ permalink raw reply related
* Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
From: Paul E. McKenney @ 2009-08-12 19:58 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: netdev, virtualization, kvm, linux-kernel
In-Reply-To: <20090810185340.GC13924@redhat.com>
On Mon, Aug 10, 2009 at 09:53:40PM +0300, Michael S. Tsirkin wrote:
> 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)
> - 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 backend 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
> tun-like device. In this version I only support raw socket as a backend,
> which can be bound to e.g. SR IOV, or to macvlan device. Backend is
> also configured by userspace, including vlan/mac etc.
>
> Status:
> This works for me, and I haven't see any crashes.
> I have not run any benchmarks yet, compared to userspace, I expect to
> see improved latency (as I save up to 4 system calls per packet) but not
> yet bandwidth/CPU (as TSO and interrupt mitigation are not yet supported).
>
> Features that I plan to look at in the future:
> - TSO
> - interrupt mitigation
> - zero copy
Much better -- a couple of documentation nits below.
Thanx, Paul
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
>
> ---
> MAINTAINERS | 10 +
> arch/x86/kvm/Kconfig | 1 +
> drivers/Makefile | 1 +
> drivers/block/virtio_blk.c | 3 +
> drivers/vhost/Kconfig | 11 +
> drivers/vhost/Makefile | 2 +
> drivers/vhost/net.c | 462 ++++++++++++++++++++++++++++++
> drivers/vhost/vhost.c | 663 ++++++++++++++++++++++++++++++++++++++++++++
> drivers/vhost/vhost.h | 108 +++++++
> include/linux/Kbuild | 1 +
> include/linux/miscdevice.h | 1 +
> include/linux/vhost.h | 100 +++++++
> 12 files changed, 1363 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 ebc2691..eb0c1da 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6312,6 +6312,16 @@ S: Maintained
> F: Documentation/filesystems/vfat.txt
> F: fs/fat/
>
> +VIRTIO HOST (VHOST)
> +P: Michael S. Tsirkin
> +M: 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
> P: Roger Luethi
> M: rl@hellgate.ch
> 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 bc4205d..1551ae1 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -105,6 +105,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/block/virtio_blk.c b/drivers/block/virtio_blk.c
> index aa1a3d5..42e61b0 100644
> --- a/drivers/block/virtio_blk.c
> +++ b/drivers/block/virtio_blk.c
> @@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
> err = PTR_ERR(vblk->vq);
> goto out_free_vblk;
> }
> + printk(KERN_ERR "vblk->vq = %p\n", vblk->vq);
>
> vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req));
> if (!vblk->pool) {
> @@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
> if (!err)
> blk_queue_logical_block_size(vblk->disk->queue, blk_size);
>
> + printk(KERN_ERR "virtio_config_val returned %d\n", err);
> +
> add_disk(vblk->disk);
> return 0;
>
> 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..fc3359b
> --- /dev/null
> +++ b/drivers/vhost/net.c
> @@ -0,0 +1,462 @@
> +/* 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/eventfd.h>
> +#include <linux/vhost.h>
> +#include <linux/virtio_net.h>
> +#include <linux/mm.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 <net/sock.h>
> +
> +#include <asm/mmu_context.h>
> +
> +#include "vhost.h"
> +
> +enum {
> + VHOST_NET_VQ_RX = 0,
> + VHOST_NET_VQ_TX = 1,
> + VHOST_NET_VQ_MAX = 2,
> +};
> +
> +struct vhost_net {
> + struct vhost_dev dev;
> + struct vhost_virtqueue vqs[VHOST_NET_VQ_MAX];
> + /* We use a kind of RCU to access sock pointer.
> + * All readers access it from workqueue,
> + * which makes it possible to flush the workqueue
> + * instead of synchronize_rcu. Therefore readers
> + * do not need rcu_read_lock/rcu_read_unlock.
How about something like "Therefore the beginning of workqueue
execution acts as rcu_read_lock() and the end of workqueue execution
acts as rcu_read_lock()"?
It would also be good to add comments to the workqueue functions
themselves saying that they act as read-side critical sections for
your kind of RCU.
Thanx, Paul
> + * Writers use device mutex. */
> + struct socket *sock;
> + struct vhost_poll poll[VHOST_NET_VQ_MAX];
> +};
> +
> +static void handle_tx_kick(struct work_struct *work);
> +static void handle_rx_kick(struct work_struct *work);
> +static void handle_tx_net(struct work_struct *work);
> +static void handle_rx_net(struct work_struct *work);
> +
> +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);
> + return 0;
> +}
> +
> +static struct socket *vhost_net_stop(struct vhost_net *n)
> +{
> + struct socket *sock = n->sock;
> + rcu_assign_pointer(n->sock, NULL);
> + if (sock) {
> + vhost_poll_flush(n->poll + VHOST_NET_VQ_TX);
> + vhost_poll_flush(n->poll + VHOST_NET_VQ_RX);
> + }
> + return sock;
> +}
> +
> +static int vhost_net_release(struct inode *inode, struct file *f)
> +{
> + struct vhost_net *n = f->private_data;
> + struct socket *sock;
> +
> + sock = vhost_net_stop(n);
> + vhost_dev_cleanup(&n->dev);
> + if (sock)
> + fput(sock->file);
> + kfree(n);
> + return 0;
> +}
> +
> +static long vhost_net_set_socket(struct vhost_net *n, int fd)
> +{
> + struct {
> + struct sockaddr_ll sa;
> + char buf[MAX_ADDR_LEN];
> + } uaddr;
> + struct socket *sock, *oldsock = NULL;
> + int uaddr_len = sizeof uaddr, r;
> +
> + mutex_lock(&n->dev.mutex);
> + r = vhost_dev_check_owner(&n->dev);
> + if (r)
> + goto done;
> +
> + if (fd == -1) {
> + /* Disconnect from socket and device. */
> + oldsock = vhost_net_stop(n);
> + goto done;
> + }
> +
> + sock = sockfd_lookup(fd, &r);
> + if (!sock) {
> + r = -ENOTSOCK;
> + goto done;
> + }
> +
> + /* Parameter checking */
> + if (sock->sk->sk_type != SOCK_RAW) {
> + r = -ESOCKTNOSUPPORT;
> + goto done;
> + }
> +
> + r = sock->ops->getname(sock, (struct sockaddr *)&uaddr.sa,
> + &uaddr_len, 0);
> + if (r)
> + goto done;
> +
> + if (uaddr.sa.sll_family != AF_PACKET) {
> + r = -EPFNOSUPPORT;
> + goto done;
> + }
> +
> + /* start polling new socket */
> + if (sock == oldsock)
> + goto done;
> +
> + if (oldsock) {
> + vhost_poll_stop(n->poll + VHOST_NET_VQ_TX);
> + vhost_poll_stop(n->poll + VHOST_NET_VQ_RX);
> + }
> + oldsock = n->sock;
> + rcu_assign_pointer(n->sock, sock);
> + vhost_poll_start(n->poll + VHOST_NET_VQ_TX, sock->file);
> + vhost_poll_start(n->poll + VHOST_NET_VQ_RX, sock->file);
> +done:
> + mutex_unlock(&n->dev.mutex);
> + if (oldsock) {
> + vhost_poll_flush(n->poll + VHOST_NET_VQ_TX);
> + vhost_poll_flush(n->poll + VHOST_NET_VQ_RX);
> + vhost_poll_flush(&n->dev.vqs[VHOST_NET_VQ_TX].poll);
> + vhost_poll_flush(&n->dev.vqs[VHOST_NET_VQ_RX].poll);
> + fput(oldsock->file);
> + }
> + return r;
> +}
> +
> +static long vhost_net_reset_owner(struct vhost_net *n)
> +{
> + struct socket *sock = NULL;
> + long r;
> + mutex_lock(&n->dev.mutex);
> + r = vhost_dev_check_owner(&n->dev);
> + if (r)
> + goto done;
> + sock = vhost_net_stop(n);
> + r = vhost_dev_reset_owner(&n->dev);
> +done:
> + mutex_unlock(&n->dev.mutex);
> + if (sock)
> + fput(sock->file);
> + return r;
> +}
> +
> +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;
> + int __user *fdp = argp;
> + u32 features;
> + int fd, r;
> + switch (ioctl) {
> + case VHOST_NET_SET_SOCKET:
> + r = get_user(fd, fdp);
> + if (r < 0)
> + return r;
> + return vhost_net_set_socket(n, fd);
> + case VHOST_GET_FEATURES:
> + /* No features for now */
> + features = 0;
> + 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)
> + return -EOPNOTSUPP;
> + return 0;
> + case VHOST_RESET_OWNER:
> + return vhost_net_reset_owner(n);
> + default:
> + return vhost_dev_ioctl(&n->dev, ioctl, arg);
> + }
> +}
> +
> +static struct file_operations vhost_net_fops = {
> + .owner = THIS_MODULE,
> + .release = vhost_net_release,
> + .unlocked_ioctl = vhost_net_ioctl,
> + .open = vhost_net_open,
> +};
> +
> +static struct miscdevice vhost_net_misc = {
> + VHOST_NET_MINOR,
> + "vhost-net",
> + &vhost_net_fops,
> +};
> +
> +/* Bits from fs/aio.c. TODO: export and use from there? */
> +/*
> + * use_mm
> + * Makes the calling kernel thread take on the specified
> + * mm context.
> + * Called by the retry thread execute retries within the
> + * iocb issuer's mm context, so that copy_from/to_user
> + * operations work seamlessly for aio.
> + * (Note: this routine is intended to be called only
> + * from a kernel thread context)
> + */
> +static void use_mm(struct mm_struct *mm)
> +{
> + struct mm_struct *active_mm;
> + struct task_struct *tsk = current;
> +
> + task_lock(tsk);
> + active_mm = tsk->active_mm;
> + atomic_inc(&mm->mm_count);
> + tsk->mm = mm;
> + tsk->active_mm = mm;
> + switch_mm(active_mm, mm, tsk);
> + task_unlock(tsk);
> +
> + mmdrop(active_mm);
> +}
> +
> +/*
> + * unuse_mm
> + * Reverses the effect of use_mm, i.e. releases the
> + * specified mm context which was earlier taken on
> + * by the calling kernel thread
> + * (Note: this routine is intended to be called only
> + * from a kernel thread context)
> + */
> +static void unuse_mm(struct mm_struct *mm)
> +{
> + struct task_struct *tsk = current;
> +
> + task_lock(tsk);
> + tsk->mm = NULL;
> + /* active_mm is still 'mm' */
> + enter_lazy_tlb(mm, tsk);
> + task_unlock(tsk);
> +}
> +
> +static void handle_tx(struct vhost_net *net)
> +{
> + struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX];
> + unsigned head, out, in;
> + struct msghdr msg = {
> + .msg_name = NULL,
> + .msg_namelen = 0,
> + .msg_control = NULL,
> + .msg_controllen = 0,
> + .msg_iov = (struct iovec *)vq->iov + 1,
> + .msg_flags = MSG_DONTWAIT,
> + };
> + size_t len;
> + int err;
> + struct socket *sock = rcu_dereference(net->sock);
> + if (!sock || !sock_writeable(sock->sk))
> + return;
> +
> + use_mm(net->dev.mm);
> + mutex_lock(&vq->mutex);
> + for (;;) {
> + head = vhost_get_vq_desc(&net->dev, vq, vq->iov, &out, &in);
> + if (head == vq->num)
> + break;
> + if (out <= 1 || in) {
> + vq_err(vq, "Unexpected descriptor format for TX: "
> + "out %d, int %d\n", out, in);
> + break;
> + }
> + /* Sanity check */
> + if (vq->iov->iov_len != sizeof(struct virtio_net_hdr)) {
> + vq_err(vq, "Unexpected header len for TX: "
> + "%ld expected %zd\n", vq->iov->iov_len,
> + sizeof(struct virtio_net_hdr));
> + break;
> + }
> + /* Skip header. TODO: support TSO. */
> + msg.msg_iovlen = out - 1;
> + len = iov_length(vq->iov + 1, out - 1);
> + /* TODO: Check specific error and bomb out unless ENOBUFS? */
> + err = sock->ops->sendmsg(NULL, sock, &msg, len);
> + if (err < 0) {
> + vhost_discard_vq_desc(vq);
> + break;
> + }
> + if (err != len)
> + pr_err("Truncated TX packet: "
> + " len %d != %zd\n", err, len);
> + vhost_add_used_and_trigger(vq, head,
> + len + sizeof(struct virtio_net_hdr));
> + }
> +
> + mutex_unlock(&vq->mutex);
> + unuse_mm(net->dev.mm);
> +}
> +
> +static void handle_rx(struct vhost_net *net)
> +{
> + struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
> + unsigned head, out, in;
> + 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 + 1,
> + .msg_flags = MSG_DONTWAIT,
> + };
> +
> + struct virtio_net_hdr hdr = {
> + .flags = 0,
> + .gso_type = VIRTIO_NET_HDR_GSO_NONE
> + };
> +
> + size_t len;
> + int err;
> + struct socket *sock = rcu_dereference(net->sock);
> + if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
> + return;
> +
> + use_mm(net->dev.mm);
> + mutex_lock(&vq->mutex);
> +
> + for (;;) {
> + head = vhost_get_vq_desc(&net->dev, vq, vq->iov, &out, &in);
> + if (head == vq->num)
> + break;
> + if (in <= 1 || out) {
> + vq_err(vq, "Unexpected descriptor format for RX: out %d, int %d\n",
> + out, in);
> + break;
> + }
> + /* Sanity check */
> + if (vq->iov->iov_len != sizeof(struct virtio_net_hdr)) {
> + vq_err(vq, "Unexpected header len for RX: %ld expected %zd\n",
> + vq->iov->iov_len, sizeof(struct virtio_net_hdr));
> + break;
> + }
> + /* Skip header. TODO: support TSO/mergeable rx buffers. */
> + msg.msg_iovlen = in - 1;
> + len = iov_length(vq->iov + 1, in - 1);
> + 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 = copy_to_user(vq->iov->iov_base, &hdr, sizeof hdr);
> + if (err) {
> + vq_err(vq, "Unable to write vnet_hdr at addr %p: %d\n",
> + vq->iov->iov_base, err);
> + break;
> + }
> + vhost_add_used_and_trigger(vq, head, len + sizeof hdr);
> + }
> +
> + 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);
> +}
> +
> +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..6178ec1
> --- /dev/null
> +++ b/drivers/vhost/vhost.c
> @@ -0,0 +1,663 @@
> +/* 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/net.h>
> +#include <linux/if_packet.h>
> +#include <linux/if_arp.h>
> +
> +#include <net/sock.h>
> +
> +#include <asm/mmu_context.h>
> +
> +#include "vhost.h"
> +
> +enum {
> + VHOST_MEMORY_MAX_NREGIONS = 64,
> +};
> +
> +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 user 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);
> +}
> +
> +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;
> +}
> +
> +/* User should have device mutex */
> +long vhost_dev_check_owner(struct vhost_dev *dev)
> +{
> + return dev->mm == current->mm ? 0 : -EPERM;
> +}
> +
> +/* User should have device mutex */
> +static long vhost_dev_set_owner(struct vhost_dev *dev)
> +{
> + if (dev->mm)
> + return -EBUSY;
> + dev->mm = get_task_mm(current);
> + return 0;
> +}
> +
> +/* User should have device mutex */
> +long vhost_dev_reset_owner(struct vhost_dev *dev)
> +{
> + struct vhost_memory *memory;
> +
> + /* Restore memory to default 1:1 mapping. */
> + memory = kmalloc(offsetof(struct vhost_memory, regions) +
> + 2 * sizeof *memory->regions, GFP_KERNEL);
> + if (!memory)
> + return -ENOMEM;
> +
> + vhost_dev_cleanup(dev);
> +
> + memory->nregions = 2;
> + memory->regions[0].guest_phys_addr = 1;
> + memory->regions[0].userspace_addr = 1;
> + memory->regions[0].memory_size = ~0ULL;
> + memory->regions[1].guest_phys_addr = 0;
> + memory->regions[1].userspace_addr = 0;
> + memory->regions[1].memory_size = 1;
> + dev->memory = memory;
> + return 0;
> +}
> +
> +/* User 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;
> + }
> + /* 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)
> +{
> + u16 flags = 0;
> + int r = put_user(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->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)(long)a.user_addr != a.user_addr) {
> + r = -EFAULT;
> + break;
> + }
> + vq->desc = (void __user *)(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)(long)a.user_addr != a.user_addr) {
> + r = -EFAULT;
> + break;
> + }
> + vq->avail = (void __user *)(long)a.user_addr;
> + 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)(long)a.user_addr != a.user_addr) {
> + r = -EFAULT;
> + break;
> + }
> + vq->used = (void __user *)(long)a.user_addr;
> + r = init_used(vq);
> + if (r)
> + break;
> + 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 = -ENOTTY;
> + }
> +
> + 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 0;
> +}
> +
> +long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, unsigned long arg)
> +{
> + void __user *argp = (void __user *)arg;
> + long r;
> +
> + mutex_lock(&d->mutex);
> + if (ioctl == VHOST_SET_OWNER) {
> + r = vhost_dev_set_owner(d);
> + goto done;
> + }
> +
> + r = vhost_dev_check_owner(d);
> + if (r)
> + goto done;
> +
> + switch (ioctl) {
> + case VHOST_SET_MEM_TABLE:
> + r = vhost_set_memory(d, argp);
> + 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;
> +}
> +
> +/* FIXME: this does not handle a region that spans multiple
> + * address/len pairs */
> +int translate_desc(struct vhost_dev *dev, u64 addr, u32 len,
> + struct iovec iov[], int iov_count, int iov_size,
> + unsigned *num)
> +{
> + 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 (*num + iov_count >= iov_size) {
> + ret = -ENOBUFS;
> + break;
> + }
> + reg = find_region(mem, addr, len);
> + if (!reg) {
> + ret = -EFAULT;
> + break;
> + }
> + _iov = iov + iov_count + *num;
> + 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;
> + ++*num;
> + }
> +
> + 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 vq->vring.num if we're
> + * at the end. */
> +static unsigned next_desc(struct vhost_virtqueue *vq, 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 vq->num;
> +
> + /* 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();
> +
> + if (next >= vq->num) {
> + vq_err(vq, "Desc next is %u > %u", next, vq->num);
> + return vq->num;
> + }
> +
> + return next;
> +}
> +
> +/* 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 vring_desc desc;
> + unsigned int i, head;
> + u16 last_avail_idx, idx;
> +
> + /* Check it isn't doing very strange things with descriptor numbers. */
> + last_avail_idx = vq->last_avail_idx;
> + if (get_user(idx, &vq->avail->idx)) {
> + vq_err(vq, "Failed to access avail idx at %p\n",
> + &vq->avail->idx);
> + return vq->num;
> + }
> +
> + if ((u16)(idx - last_avail_idx) > vq->num) {
> + vq_err(vq, "Guest moved used index from %u to %u",
> + last_avail_idx, idx);
> + return vq->num;
> + }
> +
> + /* If there's nothing new since last we looked, return invalid. */
> + if (idx == last_avail_idx)
> + return vq->num;
> +
> + /* 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",
> + idx, &vq->avail->ring[last_avail_idx % vq->num]);
> + return vq->num;
> + }
> +
> + /* If their number is silly, that's a fatal mistake. */
> + if (head >= vq->num) {
> + vq_err(vq, "Guest says index %u > %u is available",
> + head, vq->num);
> + return vq->num;
> + }
> +
> + vq->last_avail_idx++;
> +
> + /* When we start there are none of either input nor output. */
> + *out_num = *in_num = 0;
> +
> + i = head;
> + do {
> + unsigned *num;
> + unsigned iov_count;
> + if (copy_from_user(&desc, vq->desc + i, sizeof desc)) {
> + vq_err(vq, "Failed to get descriptor: idx %d addr %p\n",
> + i, vq->desc + i);
> + return vq->num;
> + }
> + /* If this is an input descriptor, increment that count. */
> + if (desc.flags & VRING_DESC_F_WRITE) {
> + num = in_num;
> + iov_count = *out_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;
> + }
> + num = out_num;
> + iov_count = *in_num;
> + }
> + if (translate_desc(dev, desc.addr, desc.len, iov, iov_count,
> + VHOST_NET_MAX_SG, num)) {
> + vq_err(vq, "Failed to translate descriptor: idx %d\n",
> + i);
> + return vq->num;
> + }
> +
> + /* If we've got too many, that implies a descriptor loop. */
> + if (*out_num + *in_num > vq->num) {
> + vq_err(vq, "Looped descriptor: idx %d\n", i);
> + return vq->num;
> + }
> + } while ((i = next_desc(vq, &desc)) != vq->num);
> +
> + vq->inflight++;
> + 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--;
> + vq->inflight--;
> +}
> +
> +/* 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;
> + }
> + vq->last_used_idx++;
> + vq->inflight--;
> + return 0;
> +}
> +
> +/* This actually sends the interrupt for this virtqueue */
> +void vhost_trigger_irq(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->inflight)
> + 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_virtqueue *vq,
> + unsigned int head, int len)
> +{
> + vhost_add_used(vq, head, len);
> + vhost_trigger_irq(vq);
> +}
> +
> +int vhost_init(void)
> +{
> + vhost_workqueue = create_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..7f7ffcd
> --- /dev/null
> +++ b/drivers/vhost/vhost.h
> @@ -0,0 +1,108 @@
> +#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>
> +
> +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);
> +
> +/* 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 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;
> +
> + /* Last index we used. */
> + u16 last_used_idx;
> +
> + /* Outstanding buffers */
> + unsigned int inflight;
> +
> + /* Is this blocked? */
> + bool blocked;
> +
> + struct iovec iov[VHOST_NET_MAX_SG];
> +
> +} ____cacheline_aligned;
> +
> +struct vhost_dev {
> + /* Readers use RCU to access memory table pointer.
> + * Writers use mutex below.*/
> + struct vhost_memory *memory;
> + struct mm_struct *mm;
> + struct vhost_virtqueue *vqs;
> + int nvqs;
> + struct mutex mutex;
> +};
> +
> +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);
> +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_virtqueue *);
> +void vhost_add_used_and_trigger(struct vhost_virtqueue *,
> + unsigned int head, int len);
> +
> +int vhost_init(void);
> +void vhost_cleanup(void);
> +
> +#define vq_err(vq, fmt, ...) do { \
> + printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__); \
> + if ((vq)->error_ctx) \
> + eventfd_signal((vq)->error_ctx, 1);\
> + } while (0)
> +
> +#endif
> diff --git a/include/linux/Kbuild b/include/linux/Kbuild
> index dec2f18..975df9a 100644
> --- a/include/linux/Kbuild
> +++ b/include/linux/Kbuild
> @@ -360,6 +360,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 0521177..781a8bb 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..9ec6d5f
> --- /dev/null
> +++ b/include/linux/vhost.h
> @@ -0,0 +1,100 @@
> +#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;
> +};
> +
> +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 padding; /* read/write protection? */
> +};
> +
> +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 must be zero. */
> +#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u32)
> +#define VHOST_ACK_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u32)
> +
> +/* 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)
> +
> +/* 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 */
> +#define VHOST_SET_VRING_USED _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_addr)
> +/* Available structure address */
> +#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)
> +
> +/* 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 device to a raw socket. The socket must be already
> + * bound to an ethernet device, this device will be used for transmit.
> + * Pass -1 to unbind from the socket and the transmit device.
> + * This can be used to stop the device (e.g. for migration). */
> +#define VHOST_NET_SET_SOCKET _IOW(VHOST_VIRTIO, 0x30, int)
> +
> +#endif
> --
> 1.6.2.5
> --
> 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/
^ permalink raw reply
* Re: [RFC PATCH v2 1/2] lsm: Add hooks to the TUN driver
From: Paul Moore @ 2009-08-12 19:43 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: linux-security-module, netdev, selinux
In-Reply-To: <20090812192840.GA13135@us.ibm.com>
On Wednesday 12 August 2009 03:28:40 pm Serge E. Hallyn wrote:
> Quoting Paul Moore (paul.moore@hp.com):
> > The TUN driver lacks any LSM hooks which makes it difficult for LSM
> > modules, such as SELinux, to enforce access controls on network traffic
> > generated by TUN users; this is particularly problematic for
> > virtualization apps such as QEMU and KVM. This patch adds three new LSM
> > hooks designed to control the creation and attachment of TUN devices, the
> > hooks are:
> >
> > * security_tun_dev_create()
> > Provides access control for the creation of new TUN devices
> >
> > * security_tun_dev_post_create()
> > Provides the ability to create the necessary socket LSM state for
> > newly created TUN devices
> >
> > * security_tun_dev_attach()
> > Provides access control for attaching to existing, persistent TUN
> > devices and the ability to update the TUN device's socket LSM state as
> > necessary ---
>
> Acked-by: Serge Hallyn <serue@us.ibm.com>
Thanks.
--
paul moore
linux @ hp
^ permalink raw reply
* Re: [RFC PATCH v2 1/2] lsm: Add hooks to the TUN driver
From: Serge E. Hallyn @ 2009-08-12 19:28 UTC (permalink / raw)
To: Paul Moore; +Cc: linux-security-module, netdev, selinux
In-Reply-To: <20090810172844.7946.43287.stgit@flek.lan>
Quoting Paul Moore (paul.moore@hp.com):
> The TUN driver lacks any LSM hooks which makes it difficult for LSM modules,
> such as SELinux, to enforce access controls on network traffic generated by
> TUN users; this is particularly problematic for virtualization apps such as
> QEMU and KVM. This patch adds three new LSM hooks designed to control the
> creation and attachment of TUN devices, the hooks are:
>
> * security_tun_dev_create()
> Provides access control for the creation of new TUN devices
>
> * security_tun_dev_post_create()
> Provides the ability to create the necessary socket LSM state for newly
> created TUN devices
>
> * security_tun_dev_attach()
> Provides access control for attaching to existing, persistent TUN devices
> and the ability to update the TUN device's socket LSM state as necessary
> ---
Acked-by: Serge Hallyn <serue@us.ibm.com>
>
> drivers/net/tun.c | 22 +++++++++++++++-------
> include/linux/security.h | 31 +++++++++++++++++++++++++++++++
> security/capability.c | 19 +++++++++++++++++++
> security/security.c | 18 ++++++++++++++++++
> 4 files changed, 83 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> index 027f7ab..e6667ce 100644
> --- a/drivers/net/tun.c
> +++ b/drivers/net/tun.c
> @@ -130,17 +130,10 @@ static inline struct tun_sock *tun_sk(struct sock *sk)
> static int tun_attach(struct tun_struct *tun, struct file *file)
> {
> struct tun_file *tfile = file->private_data;
> - const struct cred *cred = current_cred();
> int err;
>
> ASSERT_RTNL();
>
> - /* Check permissions */
> - if (((tun->owner != -1 && cred->euid != tun->owner) ||
> - (tun->group != -1 && !in_egroup_p(tun->group))) &&
> - !capable(CAP_NET_ADMIN))
> - return -EPERM;
> -
> netif_tx_lock_bh(tun->dev);
>
> err = -EINVAL;
> @@ -926,6 +919,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
>
> dev = __dev_get_by_name(net, ifr->ifr_name);
> if (dev) {
> + const struct cred *cred = current_cred();
> +
> if (ifr->ifr_flags & IFF_TUN_EXCL)
> return -EBUSY;
> if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops)
> @@ -935,6 +930,14 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
> else
> return -EINVAL;
>
> + if (((tun->owner != -1 && cred->euid != tun->owner) ||
> + (tun->group != -1 && !in_egroup_p(tun->group))) &&
> + !capable(CAP_NET_ADMIN))
> + return -EPERM;
> + err = security_tun_dev_attach(tun->sk);
> + if (err < 0)
> + return err;
> +
> err = tun_attach(tun, file);
> if (err < 0)
> return err;
> @@ -947,6 +950,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
>
> if (!capable(CAP_NET_ADMIN))
> return -EPERM;
> + err = security_tun_dev_create();
> + if (err < 0)
> + return err;
>
> /* Set dev type */
> if (ifr->ifr_flags & IFF_TUN) {
> @@ -989,6 +995,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
> tun->sk = sk;
> container_of(sk, struct tun_sock, sk)->tun = tun;
>
> + security_tun_dev_post_create(sk);
> +
> tun_net_init(dev);
>
> if (strchr(dev->name, '%')) {
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 5eff459..d8efc35 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -974,6 +974,17 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
> * Sets the connection's peersid to the secmark on skb.
> * @req_classify_flow:
> * Sets the flow's sid to the openreq sid.
> + * @tun_dev_create:
> + * Check permissions prior to creating a new TUN device.
> + * @tun_dev_post_create:
> + * This hook allows a module to update or allocate a per-socket security
> + * structure.
> + * @sk contains the newly created sock structure.
> + * @tun_dev_attach:
> + * Check permissions prior to attaching to a persistent TUN device. This
> + * hook can also be used by the module to update any security state
> + * associated with the TUN device's sock structure.
> + * @sk contains the existing sock structure.
> *
> * Security hooks for XFRM operations.
> *
> @@ -1572,6 +1583,9 @@ struct security_operations {
> void (*inet_csk_clone) (struct sock *newsk, const struct request_sock *req);
> void (*inet_conn_established) (struct sock *sk, struct sk_buff *skb);
> void (*req_classify_flow) (const struct request_sock *req, struct flowi *fl);
> + int (*tun_dev_create)(void);
> + void (*tun_dev_post_create)(struct sock *sk);
> + int (*tun_dev_attach)(struct sock *sk);
> #endif /* CONFIG_SECURITY_NETWORK */
>
> #ifdef CONFIG_SECURITY_NETWORK_XFRM
> @@ -2557,6 +2571,9 @@ void security_inet_csk_clone(struct sock *newsk,
> const struct request_sock *req);
> void security_inet_conn_established(struct sock *sk,
> struct sk_buff *skb);
> +int security_tun_dev_create(void);
> +void security_tun_dev_post_create(struct sock *sk);
> +int security_tun_dev_attach(struct sock *sk);
>
> #else /* CONFIG_SECURITY_NETWORK */
> static inline int security_unix_stream_connect(struct socket *sock,
> @@ -2707,6 +2724,20 @@ static inline void security_inet_conn_established(struct sock *sk,
> struct sk_buff *skb)
> {
> }
> +
> +static inline int security_tun_dev_create(void)
> +{
> + return 0;
> +}
> +
> +static inline void security_tun_dev_post_create(struct sock *sk)
> +{
> +}
> +
> +static inline int security_tun_dev_attach(struct sock *sk)
> +{
> + return 0;
> +}
> #endif /* CONFIG_SECURITY_NETWORK */
>
> #ifdef CONFIG_SECURITY_NETWORK_XFRM
> diff --git a/security/capability.c b/security/capability.c
> index 21b6cea..a10a44a 100644
> --- a/security/capability.c
> +++ b/security/capability.c
> @@ -710,10 +710,26 @@ static void cap_inet_conn_established(struct sock *sk, struct sk_buff *skb)
> {
> }
>
> +
> +
> static void cap_req_classify_flow(const struct request_sock *req,
> struct flowi *fl)
> {
> }
> +
> +static int cap_tun_dev_create(void)
> +{
> + return 0;
> +}
> +
> +static void cap_tun_dev_post_create(struct sock *sk)
> +{
> +}
> +
> +static int cap_tun_dev_attach(struct sock *sk)
> +{
> + return 0;
> +}
> #endif /* CONFIG_SECURITY_NETWORK */
>
> #ifdef CONFIG_SECURITY_NETWORK_XFRM
> @@ -1029,6 +1045,9 @@ void security_fixup_ops(struct security_operations *ops)
> set_to_cap_if_null(ops, inet_csk_clone);
> set_to_cap_if_null(ops, inet_conn_established);
> set_to_cap_if_null(ops, req_classify_flow);
> + set_to_cap_if_null(ops, tun_dev_create);
> + set_to_cap_if_null(ops, tun_dev_post_create);
> + set_to_cap_if_null(ops, tun_dev_attach);
> #endif /* CONFIG_SECURITY_NETWORK */
> #ifdef CONFIG_SECURITY_NETWORK_XFRM
> set_to_cap_if_null(ops, xfrm_policy_alloc_security);
> diff --git a/security/security.c b/security/security.c
> index dc7674f..dc6953c 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1112,6 +1112,24 @@ void security_inet_conn_established(struct sock *sk,
> security_ops->inet_conn_established(sk, skb);
> }
>
> +int security_tun_dev_create(void)
> +{
> + return security_ops->tun_dev_create();
> +}
> +EXPORT_SYMBOL(security_tun_dev_create);
> +
> +void security_tun_dev_post_create(struct sock *sk)
> +{
> + return security_ops->tun_dev_post_create(sk);
> +}
> +EXPORT_SYMBOL(security_tun_dev_post_create);
> +
> +int security_tun_dev_attach(struct sock *sk)
> +{
> + return security_ops->tun_dev_attach(sk);
> +}
> +EXPORT_SYMBOL(security_tun_dev_attach);
> +
> #endif /* CONFIG_SECURITY_NETWORK */
>
> #ifdef CONFIG_SECURITY_NETWORK_XFRM
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" 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
* Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
From: Anthony Liguori @ 2009-08-12 19:27 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Michael S. Tsirkin, virtualization, Ira W. Snyder, netdev, kvm,
linux-kernel
In-Reply-To: <200908121959.47222.arnd@arndb.de>
Arnd Bergmann wrote:
>> As I pointed out earlier, most code in virtio net is asymmetrical: guest
>> provides buffers, host consumes them. Possibly, one could use virtio
>> rings in a symmetrical way, but support of existing guest virtio net
>> means there's almost no shared code.
>>
>
> The trick is to swap the virtqueues instead. virtio-net is actually
> mostly symmetric in just the same way that the physical wires on a
> twisted pair ethernet are symmetric (I like how that analogy fits).
>
It's already been done between two guests. See
http://article.gmane.org/gmane.linux.kernel.virtualization/5423
Regards,
Anthony Liguori
^ permalink raw reply
* Re: [PATCH] net/ipv4, linux-2.6.30.4
From: Daniel Slot @ 2009-08-12 19:27 UTC (permalink / raw)
Cc: netdev, davem
In-Reply-To: <20090812120204.2e13163e@nehalam>
I already tried to adapt the style to existing code.
Would be nice if you could give me hint about what is akward.
I'm quite new to the kernelhacking area and sorry for all obvious errors.
The usage is imho interesting in research domains.
As it is an implementation of an ietf RFC, there might be some
researchers who can use it.
It is part of my master thesis and I use it for measurements and comparisons.
I tried to avoid a sysctl value for this.
I don't wanted this algorithm to be used by all TCP connections.
2009/8/12 Stephen Hemminger <shemminger@vyatta.com>:
> On Wed, 12 Aug 2009 20:50:59 +0200
> Daniel Slot <slot.daniel@gmail.com> wrote:
>
>> RFC 4653 specifies Non-Congestion Robustness (NCR) for TCP.
>> In the absence of explicit congestion notification from the network,
>> TCP uses loss as an indication of congestion.
>> One of the ways TCP detects loss is using the arrival of three
>> duplicate acknowledgments.
>> However, this heuristic is not always correct, notably in the case
>> when network paths reorder segments (for whatever reason), resulting
>> in degraded performance.
>> TCP-NCR is designed to mitigate this degraded performance by
>> increasing the number of duplicate acknowledgments required to trigger
>> loss recovery,
>> based on the current state of the connection, in an effort to better
>> disambiguate true segment loss from segment reordering.
>> This document specifies the changes to TCP, as well as the costs and
>> benefits of these modifications.
>>
>> This patch adds TCP-NCR as socket option to the Linux kernel (version 2.6.30.4).
>> Written by Daniel Slot, Email: slot.daniel(at)gmail.com
>
> Patch has funny indentation and awkward naming for socket elements.
> Your style needs to match existing code.
>
> What is the usage model for this? I expect that some user who wants
> to enable this would be stuck somewhere with a lossy network and
> would want to enable it. Or is it something only researchers will
> want to play with?
>
> It would be easier to use a sysctl value for this because otherwise
> each application has to be changed to select the socket option.
>
> --
>
^ permalink raw reply
* Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
From: Anthony Liguori @ 2009-08-12 19:22 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Arnd Bergmann, virtualization, Ira W. Snyder, netdev, kvm,
linux-kernel
In-Reply-To: <20090812172141.GA29966@redhat.com>
Michael S. Tsirkin wrote:
>>
>> We discussed this before, and I still think this could be directly derived
>> from struct virtqueue, in the same way that vring_virtqueue is derived from
>> struct virtqueue.
>>
>
> I prefer keeping it simple. Much of abstraction in virtio is due to the
> fact that it needs to work on top of different hardware emulations:
> lguest,kvm, possibly others in the future. vhost is always working on
> real hardware, using eventfd as the interface, so it does not need that.
>
Actually, vhost may not always be limited to real hardware.
We may on day use vhost as the basis of a driver domain. There's quite
a lot of interest in this for networking.
At any rate, I'd like to see performance results before we consider
trying to reuse virtio code.
Regards,
Anthony Liguori
^ permalink raw reply
* Re: [PATCH 5/5] c/r: Add AF_UNIX support (v8)
From: Oren Laadan @ 2009-08-12 19:19 UTC (permalink / raw)
To: Dan Smith; +Cc: netdev
In-Reply-To: <873a7x59ji.fsf@caffeine.danplanet.com>
Dan Smith wrote:
> OL> Before pulling this one, I took a quick look at this patch, and I
> OL> saw that it still uses skb_morph despite the changelog and my
> OL> memory...
>
> That's correct. We've been through several ways of allocating the
> skb's, so it's definitely confusing. We're back to skb_morph()
> because I'm pre-allocating them for lock safety when traversing the
> queues. The only thing that is allocated is the actual skb structure
> itself; the buffers are still shared like with skb_clone().
>
> OL> 1) Move 'struct ckpt_hdr_socket' et-al to checkpoint_hdr.h
>
> Okay, yeah, I guess we never really resolved the question of where
> they really belong, but I'll put them back there for now.
>
> OL> 2) Move everything that is af_unix specific from net/checkpoint.c
> OL> to (a new) net/unix/checkpoint.c (prototypes probably in af_unix.h
> OL> ?)
>
> Um, I guess I can. That will end up with a bunch of other
> externally-defined interfaces between the generic and the specific
> code, but I suppose I don't really have a solid argument against it.
>
> OL> 3) Make sure that af_unix code does not compile (and is not called
> OL> from net/checkpoint.c) unless CONFIG_UNIX is defined.
>
> Okay.
>
> OL> I pulled all the other patches already, will add this one once
> OL> you resend.
>
> Alright, well, ignore v9 I guess and hopefully we can settle on a nice
> even number 10 :)
>
Great !
^ permalink raw reply
* Re: [PATCH] net/ipv4, linux-2.6.30.4
From: Stephen Hemminger @ 2009-08-12 19:02 UTC (permalink / raw)
To: Daniel Slot; +Cc: netdev, davem
In-Reply-To: <bb6e06c00908121150q6d08cdfdp818c26cd8ae4f064@mail.gmail.com>
On Wed, 12 Aug 2009 20:50:59 +0200
Daniel Slot <slot.daniel@gmail.com> wrote:
> RFC 4653 specifies Non-Congestion Robustness (NCR) for TCP.
> In the absence of explicit congestion notification from the network,
> TCP uses loss as an indication of congestion.
> One of the ways TCP detects loss is using the arrival of three
> duplicate acknowledgments.
> However, this heuristic is not always correct, notably in the case
> when network paths reorder segments (for whatever reason), resulting
> in degraded performance.
> TCP-NCR is designed to mitigate this degraded performance by
> increasing the number of duplicate acknowledgments required to trigger
> loss recovery,
> based on the current state of the connection, in an effort to better
> disambiguate true segment loss from segment reordering.
> This document specifies the changes to TCP, as well as the costs and
> benefits of these modifications.
>
> This patch adds TCP-NCR as socket option to the Linux kernel (version 2.6.30.4).
> Written by Daniel Slot, Email: slot.daniel(at)gmail.com
Patch has funny indentation and awkward naming for socket elements.
Your style needs to match existing code.
What is the usage model for this? I expect that some user who wants
to enable this would be stuck somewhere with a lossy network and
would want to enable it. Or is it something only researchers will
want to play with?
It would be easier to use a sysctl value for this because otherwise
each application has to be changed to select the socket option.
--
^ permalink raw reply
* [PATCH] net/ipv4, linux-2.6.30.4
From: Daniel Slot @ 2009-08-12 18:59 UTC (permalink / raw)
To: netdev; +Cc: davem
RFC 4653 specifies Non-Congestion Robustness (NCR) for TCP.
In the absence of explicit congestion notification from the network,
TCP uses loss as an indication of congestion.
One of the ways TCP detects loss is using the arrival of three
duplicate acknowledgments.
However, this heuristic is not always correct, notably in the case
when network paths reorder segments (for whatever reason), resulting
in degraded performance.
TCP-NCR is designed to mitigate this degraded performance by
increasing the number of duplicate acknowledgments required to trigger
loss recovery,
based on the current state of the connection, in an effort to better
disambiguate true segment loss from segment reordering.
This document specifies the changes to TCP, as well as the costs and
benefits of these modifications.
This patch adds TCP-NCR as socket option to the Linux kernel (version 2.6.30.4).
To use TCP-NCR in careful mode (resp. aggressive mode),
an application has to set the TCP-NCR socket option (23) to the value
1 (resp. 2) \
when it starts a TCP connection.
Written by Daniel Slot, Email: slot.daniel(at)gmail.com
---------------------------
diff -uprN linux-2.6.30.4/include/linux/tcp.h
linux-2.6.30.4-NCR/include/linux/tcp.h
--- /include/linux/tcp.h 2009-07-31 00:34:47.000000000 +0200
+++ /include/linux/tcp.h 2009-08-12 20:15:18.000000000 +0200
@@ -96,6 +96,7 @@ enum {
#define TCP_QUICKACK 12 /* Block/reenable quick acks */
#define TCP_CONGESTION 13 /* Congestion control algorithm */
#define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */
+#define TCP_NCR 23 /* TCP NCR (RFC4653) */
#define TCPI_OPT_TIMESTAMPS 1
#define TCPI_OPT_SACK 2
@@ -408,6 +409,13 @@ struct tcp_sock {
#endif
int linger2;
+
+/* TCP NCR extension information */
+ u8 tcp_ncr_flag;
+ u8 elt_flag;
+ u8 dupthresh;
+ u8 LT_F;
+ u32 priorFlightSize;
};
static inline struct tcp_sock *tcp_sk(const struct sock *sk)
diff -uprN linux-2.6.30.4/net/ipv4/tcp.c linux-2.6.30.4-NCR/net/ipv4/tcp.c
--- /net/ipv4/tcp.c 2009-07-31 00:34:47.000000000 +0200
+++ /net/ipv4/tcp.c 2009-08-12 20:15:18.000000000 +0200
@@ -2208,6 +2208,17 @@ static int do_tcp_setsockopt(struct sock
break;
#endif
+ case TCP_NCR:
+ /* TCP-NCR : val equal 1 for careful mode, val equal 2 for
aggressive mode */
+ if (val){
+ tp->tcp_ncr_flag = 1;
+ if (val==1) tp->LT_F = 3;
+ if (val==2) tp->LT_F = 4;
+ } else {
+ tp->tcp_ncr_flag = 0;
+ }
+ break;
+
default:
err = -ENOPROTOOPT;
break;
diff -uprN linux-2.6.30.4/net/ipv4/tcp_input.c
linux-2.6.30.4-NCR/net/ipv4/tcp_input.c
--- /net/ipv4/tcp_input.c 2009-07-31 00:34:47.000000000 +0200
+++ /net/ipv4/tcp_input.c 2009-08-12 20:15:18.000000000 +0200
@@ -1003,6 +1003,45 @@ static void tcp_skb_mark_lost_uncond_ver
}
}
+/* TCP-NCR: Test if TCP-NCR may be used
+ * (Following RFC 4653 recommendations)
+ */
+static int tcp_ncr_test(struct tcp_sock *tp)
+{
+ return (tp->tcp_ncr_flag && tcp_is_sack(tp) && !(tp->nonagle &
TCP_NAGLE_OFF));
+}
+
+/* TCP-NCR: Initiate Extended Limited Transmit
+ * (RFC 4653 Initialization)
+ * */
+static void tcp_ncr_elt_init(struct tcp_sock *tp, int how)
+{
+ if (!how) tp->priorFlightSize = tp->packets_out;
+ tp->elt_flag = 1;
+ tp->dupthresh = max_t(u32, ((2 * tp->packets_out)/tp->LT_F), 3);
+}
+
+/* TCP-NCR Extended Limited Transmit
+ * (RFC 4653 Termination)
+ */
+static void tcp_ncr_elt_end(struct tcp_sock *tp, int flag , int how)
+{
+ if (how){
+ /* New cumulative ACK during ELT, it is reordering. */
+ tp->snd_ssthresh = tp->priorFlightSize;
+ tp->snd_cwnd = min(tp->packets_out+1, tp->priorFlightSize);
+ tp->snd_cwnd_stamp = tcp_time_stamp;
+ if (flag & FLAG_DATA_SACKED) tcp_ncr_elt_init(tp, 1);
+ else tp->elt_flag = 0;
+ } else {
+ /* Dupthresh is reached, start recovery */
+ tp->snd_ssthresh = (tp->priorFlightSize/2);
+ tp->snd_cwnd = tp->snd_ssthresh;
+ tp->snd_cwnd_stamp = tcp_time_stamp;
+ tp->elt_flag = 0;
+ }
+}
+
/* This procedure tags the retransmission queue when SACKs arrive.
*
* We have three tag bits: SACKED(S), RETRANS(R) and LOST(L).
@@ -1346,6 +1385,9 @@ static u8 tcp_sacktag_one(struct sk_buff
}
}
+ /* TCP-NCR: Initialization */
+ if (tcp_ncr_test(tp) && (!tp->elt_flag) && (tp->sacked_out ==
0)) tcp_ncr_elt_init(tp, 0);
+
sacked |= TCPCB_SACKED_ACKED;
state->flag |= FLAG_DATA_SACKED;
tp->sacked_out += pcount;
@@ -2425,9 +2467,13 @@ static int tcp_time_to_recover(struct so
if (tp->lost_out)
return 1;
- /* Not-A-Trick#2 : Classic rule... */
- if (tcp_dupack_heurestics(tp) > tp->reordering)
- return 1;
+ /* Not-A-Trick#2 : Classic rule...
+ * (Option to use TCP-NCR dupthresh instead)
+ */
+ if (tp->elt_flag && (tcp_dupack_heurestics(tp) > tp->dupthresh))
+ return 1;
+ if (!tp->elt_flag && (tcp_dupack_heurestics(tp) > tp->reordering))
+ return 1;
/* Trick#3 : when we use RFC2988 timer restart, fast
* retransmit can be triggered by timeout of queue head.
@@ -2603,6 +2649,17 @@ static void tcp_cwnd_down(struct sock *s
}
}
+/* TCP-NCR: Extended Limited Transmit
+ * (RFC 4653 Main Part)
+ */
+static void tcp_ncr_elt(struct sock *sk, int flag)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ if (tp->LT_F == 3) tcp_cwnd_down(sk, flag);
+ tp->dupthresh = max_t(u32, ((2 * tp->packets_out)/tp->LT_F), 3);
+}
+
/* Nothing was retransmitted or returned timestamp is less
* than timestamp of the first retransmission.
*/
@@ -2812,7 +2869,7 @@ static void tcp_try_to_open(struct sock
if (inet_csk(sk)->icsk_ca_state != TCP_CA_CWR) {
tcp_try_keep_open(sk);
- tcp_moderate_cwnd(tp);
+ if (!tcp_ncr_test(tp)) tcp_moderate_cwnd(tp);
} else {
tcp_cwnd_down(sk, flag);
}
@@ -2920,6 +2977,9 @@ static void tcp_fastretrans_alert(struct
if (WARN_ON(!tp->sacked_out && tp->fackets_out))
tp->fackets_out = 0;
+ /* TCP-NCR: Extended Limited Transmit */
+ if (tp->elt_flag && (flag & FLAG_DATA_SACKED)) tcp_ncr_elt(sk, flag);
+
/* Now state machine starts.
* A. ECE, hence prohibit cwnd undoing, the reduction is required. */
if (flag & FLAG_ECE)
@@ -3050,7 +3110,8 @@ static void tcp_fastretrans_alert(struct
if (icsk->icsk_ca_state < TCP_CA_CWR) {
if (!(flag & FLAG_ECE))
tp->prior_ssthresh = tcp_current_ssthresh(sk);
- tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
+ if (tp->elt_flag) tcp_ncr_elt_end(tp, flag, 0);
+ else tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
TCP_ECN_queue_cwr(tp);
}
@@ -3062,8 +3123,8 @@ static void tcp_fastretrans_alert(struct
if (do_lost || (tcp_is_fack(tp) && tcp_head_timedout(sk)))
tcp_update_scoreboard(sk, fast_rexmit);
- tcp_cwnd_down(sk, flag);
- tcp_xmit_retransmit_queue(sk);
+ if (!tcp_ncr_test(tp))tcp_cwnd_down(sk, flag);
+ tcp_xmit_retransmit_queue(sk);
}
static void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt)
@@ -3285,8 +3346,10 @@ static int tcp_clean_rtx_queue(struct so
int delta;
/* Non-retransmitted hole got filled? That's reordering */
- if (reord < prior_fackets)
+ if (reord < prior_fackets){
tcp_update_reordering(sk, tp->fackets_out - reord, 0);
+ if (tp->elt_flag) tcp_ncr_elt_end(tp, flag, 1);
+ }
delta = tcp_is_fack(tp) ? pkts_acked :
prior_sacked - tp->sacked_out;
diff -uprN linux-2.6.30.4/net/ipv4/tcp_ipv4.c
linux-2.6.30.4-NCR/net/ipv4/tcp_ipv4.c
--- /net/ipv4/tcp_ipv4.c 2009-07-31 00:34:47.000000000 +0200
+++ /net/ipv4/tcp_ipv4.c 2009-08-12 20:15:18.000000000 +0200
@@ -1774,6 +1774,11 @@ static int tcp_v4_init_sock(struct sock
tp->mss_cache = 536;
tp->reordering = sysctl_tcp_reordering;
+
+ /* TCP-NCR: Initiate some variables */
+ tp->dupthresh = TCP_FASTRETRANS_THRESH;
+ tp->elt_flag = 0;
+
icsk->icsk_ca_ops = &tcp_init_congestion_ops;
sk->sk_state = TCP_CLOSE;
---------------------------
^ permalink raw reply
* [PATCH] net/ipv4, linux-2.6.30.4
From: Daniel Slot @ 2009-08-12 18:50 UTC (permalink / raw)
To: netdev; +Cc: davem
[-- Attachment #1: Type: text/plain, Size: 938 bytes --]
RFC 4653 specifies Non-Congestion Robustness (NCR) for TCP.
In the absence of explicit congestion notification from the network,
TCP uses loss as an indication of congestion.
One of the ways TCP detects loss is using the arrival of three
duplicate acknowledgments.
However, this heuristic is not always correct, notably in the case
when network paths reorder segments (for whatever reason), resulting
in degraded performance.
TCP-NCR is designed to mitigate this degraded performance by
increasing the number of duplicate acknowledgments required to trigger
loss recovery,
based on the current state of the connection, in an effort to better
disambiguate true segment loss from segment reordering.
This document specifies the changes to TCP, as well as the costs and
benefits of these modifications.
This patch adds TCP-NCR as socket option to the Linux kernel (version 2.6.30.4).
Written by Daniel Slot, Email: slot.daniel(at)gmail.com
[-- Attachment #2: README.txt --]
[-- Type: text/plain, Size: 234 bytes --]
This patch adds TCP-NCR as socket option to the Linux kernel.
To use TCP-NCR in careful mode (resp. aggressive mode),
an application has to set the TCP-NCR socket option (23) to the value 1 (resp. 2) when it starts a TCP connection.
[-- Attachment #3: patch-linuxkernel-2.6.30-tcp_ncr --]
[-- Type: application/octet-stream, Size: 6904 bytes --]
diff -uprN linux-2.6.30.4/include/linux/tcp.h linux-2.6.30.4-NCR/include/linux/tcp.h
--- /include/linux/tcp.h 2009-07-31 00:34:47.000000000 +0200
+++ /include/linux/tcp.h 2009-08-12 20:15:18.000000000 +0200
@@ -96,6 +96,7 @@ enum {
#define TCP_QUICKACK 12 /* Block/reenable quick acks */
#define TCP_CONGESTION 13 /* Congestion control algorithm */
#define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */
+#define TCP_NCR 23 /* TCP NCR (RFC4653) */
#define TCPI_OPT_TIMESTAMPS 1
#define TCPI_OPT_SACK 2
@@ -408,6 +409,13 @@ struct tcp_sock {
#endif
int linger2;
+
+/* TCP NCR extension information */
+ u8 tcp_ncr_flag;
+ u8 elt_flag;
+ u8 dupthresh;
+ u8 LT_F;
+ u32 priorFlightSize;
};
static inline struct tcp_sock *tcp_sk(const struct sock *sk)
diff -uprN linux-2.6.30.4/net/ipv4/tcp.c linux-2.6.30.4-NCR/net/ipv4/tcp.c
--- /net/ipv4/tcp.c 2009-07-31 00:34:47.000000000 +0200
+++ /net/ipv4/tcp.c 2009-08-12 20:15:18.000000000 +0200
@@ -2208,6 +2208,17 @@ static int do_tcp_setsockopt(struct sock
break;
#endif
+ case TCP_NCR:
+ /* TCP-NCR : val equal 1 for careful mode, val equal 2 for aggressive mode */
+ if (val){
+ tp->tcp_ncr_flag = 1;
+ if (val==1) tp->LT_F = 3;
+ if (val==2) tp->LT_F = 4;
+ } else {
+ tp->tcp_ncr_flag = 0;
+ }
+ break;
+
default:
err = -ENOPROTOOPT;
break;
diff -uprN linux-2.6.30.4/net/ipv4/tcp_input.c linux-2.6.30.4-NCR/net/ipv4/tcp_input.c
--- /net/ipv4/tcp_input.c 2009-07-31 00:34:47.000000000 +0200
+++ /net/ipv4/tcp_input.c 2009-08-12 20:15:18.000000000 +0200
@@ -1003,6 +1003,45 @@ static void tcp_skb_mark_lost_uncond_ver
}
}
+/* TCP-NCR: Test if TCP-NCR may be used
+ * (Following RFC 4653 recommendations)
+ */
+static int tcp_ncr_test(struct tcp_sock *tp)
+{
+ return (tp->tcp_ncr_flag && tcp_is_sack(tp) && !(tp->nonagle & TCP_NAGLE_OFF));
+}
+
+/* TCP-NCR: Initiate Extended Limited Transmit
+ * (RFC 4653 Initialization)
+ * */
+static void tcp_ncr_elt_init(struct tcp_sock *tp, int how)
+{
+ if (!how) tp->priorFlightSize = tp->packets_out;
+ tp->elt_flag = 1;
+ tp->dupthresh = max_t(u32, ((2 * tp->packets_out)/tp->LT_F), 3);
+}
+
+/* TCP-NCR Extended Limited Transmit
+ * (RFC 4653 Termination)
+ */
+static void tcp_ncr_elt_end(struct tcp_sock *tp, int flag , int how)
+{
+ if (how){
+ /* New cumulative ACK during ELT, it is reordering. */
+ tp->snd_ssthresh = tp->priorFlightSize;
+ tp->snd_cwnd = min(tp->packets_out+1, tp->priorFlightSize);
+ tp->snd_cwnd_stamp = tcp_time_stamp;
+ if (flag & FLAG_DATA_SACKED) tcp_ncr_elt_init(tp, 1);
+ else tp->elt_flag = 0;
+ } else {
+ /* Dupthresh is reached, start recovery */
+ tp->snd_ssthresh = (tp->priorFlightSize/2);
+ tp->snd_cwnd = tp->snd_ssthresh;
+ tp->snd_cwnd_stamp = tcp_time_stamp;
+ tp->elt_flag = 0;
+ }
+}
+
/* This procedure tags the retransmission queue when SACKs arrive.
*
* We have three tag bits: SACKED(S), RETRANS(R) and LOST(L).
@@ -1346,6 +1385,9 @@ static u8 tcp_sacktag_one(struct sk_buff
}
}
+ /* TCP-NCR: Initialization */
+ if (tcp_ncr_test(tp) && (!tp->elt_flag) && (tp->sacked_out == 0)) tcp_ncr_elt_init(tp, 0);
+
sacked |= TCPCB_SACKED_ACKED;
state->flag |= FLAG_DATA_SACKED;
tp->sacked_out += pcount;
@@ -2425,9 +2467,13 @@ static int tcp_time_to_recover(struct so
if (tp->lost_out)
return 1;
- /* Not-A-Trick#2 : Classic rule... */
- if (tcp_dupack_heurestics(tp) > tp->reordering)
- return 1;
+ /* Not-A-Trick#2 : Classic rule...
+ * (Option to use TCP-NCR dupthresh instead)
+ */
+ if (tp->elt_flag && (tcp_dupack_heurestics(tp) > tp->dupthresh))
+ return 1;
+ if (!tp->elt_flag && (tcp_dupack_heurestics(tp) > tp->reordering))
+ return 1;
/* Trick#3 : when we use RFC2988 timer restart, fast
* retransmit can be triggered by timeout of queue head.
@@ -2603,6 +2649,17 @@ static void tcp_cwnd_down(struct sock *s
}
}
+/* TCP-NCR: Extended Limited Transmit
+ * (RFC 4653 Main Part)
+ */
+static void tcp_ncr_elt(struct sock *sk, int flag)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ if (tp->LT_F == 3) tcp_cwnd_down(sk, flag);
+ tp->dupthresh = max_t(u32, ((2 * tp->packets_out)/tp->LT_F), 3);
+}
+
/* Nothing was retransmitted or returned timestamp is less
* than timestamp of the first retransmission.
*/
@@ -2812,7 +2869,7 @@ static void tcp_try_to_open(struct sock
if (inet_csk(sk)->icsk_ca_state != TCP_CA_CWR) {
tcp_try_keep_open(sk);
- tcp_moderate_cwnd(tp);
+ if (!tcp_ncr_test(tp)) tcp_moderate_cwnd(tp);
} else {
tcp_cwnd_down(sk, flag);
}
@@ -2920,6 +2977,9 @@ static void tcp_fastretrans_alert(struct
if (WARN_ON(!tp->sacked_out && tp->fackets_out))
tp->fackets_out = 0;
+ /* TCP-NCR: Extended Limited Transmit */
+ if (tp->elt_flag && (flag & FLAG_DATA_SACKED)) tcp_ncr_elt(sk, flag);
+
/* Now state machine starts.
* A. ECE, hence prohibit cwnd undoing, the reduction is required. */
if (flag & FLAG_ECE)
@@ -3050,7 +3110,8 @@ static void tcp_fastretrans_alert(struct
if (icsk->icsk_ca_state < TCP_CA_CWR) {
if (!(flag & FLAG_ECE))
tp->prior_ssthresh = tcp_current_ssthresh(sk);
- tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
+ if (tp->elt_flag) tcp_ncr_elt_end(tp, flag, 0);
+ else tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
TCP_ECN_queue_cwr(tp);
}
@@ -3062,8 +3123,8 @@ static void tcp_fastretrans_alert(struct
if (do_lost || (tcp_is_fack(tp) && tcp_head_timedout(sk)))
tcp_update_scoreboard(sk, fast_rexmit);
- tcp_cwnd_down(sk, flag);
- tcp_xmit_retransmit_queue(sk);
+ if (!tcp_ncr_test(tp))tcp_cwnd_down(sk, flag);
+ tcp_xmit_retransmit_queue(sk);
}
static void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt)
@@ -3285,8 +3346,10 @@ static int tcp_clean_rtx_queue(struct so
int delta;
/* Non-retransmitted hole got filled? That's reordering */
- if (reord < prior_fackets)
+ if (reord < prior_fackets){
tcp_update_reordering(sk, tp->fackets_out - reord, 0);
+ if (tp->elt_flag) tcp_ncr_elt_end(tp, flag, 1);
+ }
delta = tcp_is_fack(tp) ? pkts_acked :
prior_sacked - tp->sacked_out;
diff -uprN linux-2.6.30.4/net/ipv4/tcp_ipv4.c linux-2.6.30.4-NCR/net/ipv4/tcp_ipv4.c
--- /net/ipv4/tcp_ipv4.c 2009-07-31 00:34:47.000000000 +0200
+++ /net/ipv4/tcp_ipv4.c 2009-08-12 20:15:18.000000000 +0200
@@ -1774,6 +1774,11 @@ static int tcp_v4_init_sock(struct sock
tp->mss_cache = 536;
tp->reordering = sysctl_tcp_reordering;
+
+ /* TCP-NCR: Initiate some variables */
+ tp->dupthresh = TCP_FASTRETRANS_THRESH;
+ tp->elt_flag = 0;
+
icsk->icsk_ca_ops = &tcp_init_congestion_ops;
sk->sk_state = TCP_CLOSE;
^ permalink raw reply
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