Skip to content

Commit

Permalink
Merge branch 'vhost-net-next' of git://git.kernel.org/pub/scm/linux/k…
Browse files Browse the repository at this point in the history
…ernel/git/mst/vhost
  • Loading branch information
David S. Miller committed Dec 14, 2010
2 parents 6389aa7 + 4e53f78 commit 9fe146a
Show file tree
Hide file tree
Showing 12 changed files with 842 additions and 30 deletions.
9 changes: 1 addition & 8 deletions drivers/vhost/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <linux/eventfd.h>
#include <linux/vhost.h>
#include <linux/virtio_net.h>
#include <linux/mmu_context.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
Expand Down Expand Up @@ -143,7 +142,6 @@ static void handle_tx(struct vhost_net *net)
return;
}

use_mm(net->dev.mm);
mutex_lock(&vq->mutex);
vhost_disable_notify(vq);

Expand Down Expand Up @@ -208,7 +206,6 @@ static void handle_tx(struct vhost_net *net)
}

mutex_unlock(&vq->mutex);
unuse_mm(net->dev.mm);
}

static int peek_head_len(struct sock *sk)
Expand Down Expand Up @@ -313,7 +310,6 @@ static void handle_rx_big(struct vhost_net *net)
if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
return;

use_mm(net->dev.mm);
mutex_lock(&vq->mutex);
vhost_disable_notify(vq);
hdr_size = vq->vhost_hlen;
Expand Down Expand Up @@ -392,7 +388,6 @@ static void handle_rx_big(struct vhost_net *net)
}

mutex_unlock(&vq->mutex);
unuse_mm(net->dev.mm);
}

/* Expects to be always run from workqueue - which acts as
Expand Down Expand Up @@ -424,7 +419,6 @@ static void handle_rx_mergeable(struct vhost_net *net)
if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
return;

use_mm(net->dev.mm);
mutex_lock(&vq->mutex);
vhost_disable_notify(vq);
vhost_hlen = vq->vhost_hlen;
Expand Down Expand Up @@ -459,7 +453,7 @@ static void handle_rx_mergeable(struct vhost_net *net)
move_iovec_hdr(vq->iov, vq->hdr, vhost_hlen, in);
else
/* Copy the header for use in VIRTIO_NET_F_MRG_RXBUF:
* needed because sendmsg can modify msg_iov. */
* needed because recvmsg can modify msg_iov. */
copy_iovec_hdr(vq->iov, vq->hdr, sock_hlen, in);
msg.msg_iovlen = in;
err = sock->ops->recvmsg(NULL, sock, &msg,
Expand Down Expand Up @@ -501,7 +495,6 @@ static void handle_rx_mergeable(struct vhost_net *net)
}

mutex_unlock(&vq->mutex);
unuse_mm(net->dev.mm);
}

static void handle_rx(struct vhost_net *net)
Expand Down
320 changes: 320 additions & 0 deletions drivers/vhost/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
/* 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.
*
* test virtio server in host kernel.
*/

#include <linux/compat.h>
#include <linux/eventfd.h>
#include <linux/vhost.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/slab.h>

#include "test.h"
#include "vhost.c"

/* Max number of bytes transferred before requeueing the job.
* Using this limit prevents one virtqueue from starving others. */
#define VHOST_TEST_WEIGHT 0x80000

enum {
VHOST_TEST_VQ = 0,
VHOST_TEST_VQ_MAX = 1,
};

struct vhost_test {
struct vhost_dev dev;
struct vhost_virtqueue vqs[VHOST_TEST_VQ_MAX];
};

/* Expects to be always run from workqueue - which acts as
* read-size critical section for our kind of RCU. */
static void handle_vq(struct vhost_test *n)
{
struct vhost_virtqueue *vq = &n->dev.vqs[VHOST_TEST_VQ];
unsigned out, in;
int head;
size_t len, total_len = 0;
void *private;

private = rcu_dereference_check(vq->private_data, 1);
if (!private)
return;

mutex_lock(&vq->mutex);
vhost_disable_notify(vq);

for (;;) {
head = vhost_get_vq_desc(&n->dev, vq, vq->iov,
ARRAY_SIZE(vq->iov),
&out, &in,
NULL, NULL);
/* On error, stop handling until the next kick. */
if (unlikely(head < 0))
break;
/* Nothing new? Wait for eventfd to tell us they refilled. */
if (head == vq->num) {
if (unlikely(vhost_enable_notify(vq))) {
vhost_disable_notify(vq);
continue;
}
break;
}
if (in) {
vq_err(vq, "Unexpected descriptor format for TX: "
"out %d, int %d\n", out, in);
break;
}
len = iov_length(vq->iov, out);
/* Sanity check */
if (!len) {
vq_err(vq, "Unexpected 0 len for TX\n");
break;
}
vhost_add_used_and_signal(&n->dev, vq, head, 0);
total_len += len;
if (unlikely(total_len >= VHOST_TEST_WEIGHT)) {
vhost_poll_queue(&vq->poll);
break;
}
}

mutex_unlock(&vq->mutex);
}

static void handle_vq_kick(struct vhost_work *work)
{
struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
poll.work);
struct vhost_test *n = container_of(vq->dev, struct vhost_test, dev);

handle_vq(n);
}

static int vhost_test_open(struct inode *inode, struct file *f)
{
struct vhost_test *n = kmalloc(sizeof *n, GFP_KERNEL);
struct vhost_dev *dev;
int r;

if (!n)
return -ENOMEM;

dev = &n->dev;
n->vqs[VHOST_TEST_VQ].handle_kick = handle_vq_kick;
r = vhost_dev_init(dev, n->vqs, VHOST_TEST_VQ_MAX);
if (r < 0) {
kfree(n);
return r;
}

f->private_data = n;

return 0;
}

static void *vhost_test_stop_vq(struct vhost_test *n,
struct vhost_virtqueue *vq)
{
void *private;

mutex_lock(&vq->mutex);
private = rcu_dereference_protected(vq->private_data,
lockdep_is_held(&vq->mutex));
rcu_assign_pointer(vq->private_data, NULL);
mutex_unlock(&vq->mutex);
return private;
}

static void vhost_test_stop(struct vhost_test *n, void **privatep)
{
*privatep = vhost_test_stop_vq(n, n->vqs + VHOST_TEST_VQ);
}

static void vhost_test_flush_vq(struct vhost_test *n, int index)
{
vhost_poll_flush(&n->dev.vqs[index].poll);
}

static void vhost_test_flush(struct vhost_test *n)
{
vhost_test_flush_vq(n, VHOST_TEST_VQ);
}

static int vhost_test_release(struct inode *inode, struct file *f)
{
struct vhost_test *n = f->private_data;
void *private;

vhost_test_stop(n, &private);
vhost_test_flush(n);
vhost_dev_cleanup(&n->dev);
/* We do an extra flush before freeing memory,
* since jobs can re-queue themselves. */
vhost_test_flush(n);
kfree(n);
return 0;
}

static long vhost_test_run(struct vhost_test *n, int test)
{
void *priv, *oldpriv;
struct vhost_virtqueue *vq;
int r, index;

if (test < 0 || test > 1)
return -EINVAL;

mutex_lock(&n->dev.mutex);
r = vhost_dev_check_owner(&n->dev);
if (r)
goto err;

for (index = 0; index < n->dev.nvqs; ++index) {
/* Verify that ring has been setup correctly. */
if (!vhost_vq_access_ok(&n->vqs[index])) {
r = -EFAULT;
goto err;
}
}

for (index = 0; index < n->dev.nvqs; ++index) {
vq = n->vqs + index;
mutex_lock(&vq->mutex);
priv = test ? n : NULL;

/* start polling new socket */
oldpriv = rcu_dereference_protected(vq->private_data,
lockdep_is_held(&vq->mutex));
rcu_assign_pointer(vq->private_data, priv);

mutex_unlock(&vq->mutex);

if (oldpriv) {
vhost_test_flush_vq(n, index);
}
}

mutex_unlock(&n->dev.mutex);
return 0;

err:
mutex_unlock(&n->dev.mutex);
return r;
}

static long vhost_test_reset_owner(struct vhost_test *n)
{
void *priv = NULL;
long err;
mutex_lock(&n->dev.mutex);
err = vhost_dev_check_owner(&n->dev);
if (err)
goto done;
vhost_test_stop(n, &priv);
vhost_test_flush(n);
err = vhost_dev_reset_owner(&n->dev);
done:
mutex_unlock(&n->dev.mutex);
return err;
}

static int vhost_test_set_features(struct vhost_test *n, u64 features)
{
mutex_lock(&n->dev.mutex);
if ((features & (1 << VHOST_F_LOG_ALL)) &&
!vhost_log_access_ok(&n->dev)) {
mutex_unlock(&n->dev.mutex);
return -EFAULT;
}
n->dev.acked_features = features;
smp_wmb();
vhost_test_flush(n);
mutex_unlock(&n->dev.mutex);
return 0;
}

static long vhost_test_ioctl(struct file *f, unsigned int ioctl,
unsigned long arg)
{
struct vhost_test *n = f->private_data;
void __user *argp = (void __user *)arg;
u64 __user *featurep = argp;
int test;
u64 features;
int r;
switch (ioctl) {
case VHOST_TEST_RUN:
if (copy_from_user(&test, argp, sizeof test))
return -EFAULT;
return vhost_test_run(n, test);
case VHOST_GET_FEATURES:
features = VHOST_FEATURES;
if (copy_to_user(featurep, &features, sizeof features))
return -EFAULT;
return 0;
case VHOST_SET_FEATURES:
if (copy_from_user(&features, featurep, sizeof features))
return -EFAULT;
if (features & ~VHOST_FEATURES)
return -EOPNOTSUPP;
return vhost_test_set_features(n, features);
case VHOST_RESET_OWNER:
return vhost_test_reset_owner(n);
default:
mutex_lock(&n->dev.mutex);
r = vhost_dev_ioctl(&n->dev, ioctl, arg);
vhost_test_flush(n);
mutex_unlock(&n->dev.mutex);
return r;
}
}

#ifdef CONFIG_COMPAT
static long vhost_test_compat_ioctl(struct file *f, unsigned int ioctl,
unsigned long arg)
{
return vhost_test_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
}
#endif

static const struct file_operations vhost_test_fops = {
.owner = THIS_MODULE,
.release = vhost_test_release,
.unlocked_ioctl = vhost_test_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = vhost_test_compat_ioctl,
#endif
.open = vhost_test_open,
.llseek = noop_llseek,
};

static struct miscdevice vhost_test_misc = {
MISC_DYNAMIC_MINOR,
"vhost-test",
&vhost_test_fops,
};

static int vhost_test_init(void)
{
return misc_register(&vhost_test_misc);
}
module_init(vhost_test_init);

static void vhost_test_exit(void)
{
misc_deregister(&vhost_test_misc);
}
module_exit(vhost_test_exit);

MODULE_VERSION("0.0.1");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Michael S. Tsirkin");
MODULE_DESCRIPTION("Host kernel side for virtio simulator");
7 changes: 7 additions & 0 deletions drivers/vhost/test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef LINUX_VHOST_TEST_H
#define LINUX_VHOST_TEST_H

/* Start a given test on the virtio null device. 0 stops all tests. */
#define VHOST_TEST_RUN _IOW(VHOST_VIRTIO, 0x31, int)

#endif
Loading

0 comments on commit 9fe146a

Please sign in to comment.