Merge branch 'vhost-net-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost
This commit is contained in:
Коммит
0e24d34a5b
|
@ -60,6 +60,7 @@ static int move_iovec_hdr(struct iovec *from, struct iovec *to,
|
||||||
{
|
{
|
||||||
int seg = 0;
|
int seg = 0;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
||||||
while (len && seg < iov_count) {
|
while (len && seg < iov_count) {
|
||||||
size = min(from->iov_len, len);
|
size = min(from->iov_len, len);
|
||||||
to->iov_base = from->iov_base;
|
to->iov_base = from->iov_base;
|
||||||
|
@ -79,6 +80,7 @@ static void copy_iovec_hdr(const struct iovec *from, struct iovec *to,
|
||||||
{
|
{
|
||||||
int seg = 0;
|
int seg = 0;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
||||||
while (len && seg < iovcount) {
|
while (len && seg < iovcount) {
|
||||||
size = min(from->iov_len, len);
|
size = min(from->iov_len, len);
|
||||||
to->iov_base = from->iov_base;
|
to->iov_base = from->iov_base;
|
||||||
|
@ -211,12 +213,13 @@ static int peek_head_len(struct sock *sk)
|
||||||
{
|
{
|
||||||
struct sk_buff *head;
|
struct sk_buff *head;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
lock_sock(sk);
|
spin_lock_irqsave(&sk->sk_receive_queue.lock, flags);
|
||||||
head = skb_peek(&sk->sk_receive_queue);
|
head = skb_peek(&sk->sk_receive_queue);
|
||||||
if (head)
|
if (likely(head))
|
||||||
len = head->len;
|
len = head->len;
|
||||||
release_sock(sk);
|
spin_unlock_irqrestore(&sk->sk_receive_queue.lock, flags);
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +230,7 @@ static int peek_head_len(struct sock *sk)
|
||||||
* @iovcount - returned count of io vectors we fill
|
* @iovcount - returned count of io vectors we fill
|
||||||
* @log - vhost log
|
* @log - vhost log
|
||||||
* @log_num - log offset
|
* @log_num - log offset
|
||||||
|
* @quota - headcount quota, 1 for big buffer
|
||||||
* returns number of buffer heads allocated, negative on error
|
* returns number of buffer heads allocated, negative on error
|
||||||
*/
|
*/
|
||||||
static int get_rx_bufs(struct vhost_virtqueue *vq,
|
static int get_rx_bufs(struct vhost_virtqueue *vq,
|
||||||
|
@ -234,7 +238,8 @@ static int get_rx_bufs(struct vhost_virtqueue *vq,
|
||||||
int datalen,
|
int datalen,
|
||||||
unsigned *iovcount,
|
unsigned *iovcount,
|
||||||
struct vhost_log *log,
|
struct vhost_log *log,
|
||||||
unsigned *log_num)
|
unsigned *log_num,
|
||||||
|
unsigned int quota)
|
||||||
{
|
{
|
||||||
unsigned int out, in;
|
unsigned int out, in;
|
||||||
int seg = 0;
|
int seg = 0;
|
||||||
|
@ -242,7 +247,7 @@ static int get_rx_bufs(struct vhost_virtqueue *vq,
|
||||||
unsigned d;
|
unsigned d;
|
||||||
int r, nlogs = 0;
|
int r, nlogs = 0;
|
||||||
|
|
||||||
while (datalen > 0) {
|
while (datalen > 0 && headcount < quota) {
|
||||||
if (unlikely(seg >= UIO_MAXIOV)) {
|
if (unlikely(seg >= UIO_MAXIOV)) {
|
||||||
r = -ENOBUFS;
|
r = -ENOBUFS;
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -282,117 +287,7 @@ err:
|
||||||
|
|
||||||
/* Expects to be always run from workqueue - which acts as
|
/* Expects to be always run from workqueue - which acts as
|
||||||
* read-size critical section for our kind of RCU. */
|
* read-size critical section for our kind of RCU. */
|
||||||
static void handle_rx_big(struct vhost_net *net)
|
static void handle_rx(struct vhost_net *net)
|
||||||
{
|
|
||||||
struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
|
|
||||||
unsigned out, in, log, s;
|
|
||||||
int head;
|
|
||||||
struct vhost_log *vq_log;
|
|
||||||
struct msghdr msg = {
|
|
||||||
.msg_name = NULL,
|
|
||||||
.msg_namelen = 0,
|
|
||||||
.msg_control = NULL, /* FIXME: get and handle RX aux data. */
|
|
||||||
.msg_controllen = 0,
|
|
||||||
.msg_iov = vq->iov,
|
|
||||||
.msg_flags = MSG_DONTWAIT,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct virtio_net_hdr hdr = {
|
|
||||||
.flags = 0,
|
|
||||||
.gso_type = VIRTIO_NET_HDR_GSO_NONE
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t len, total_len = 0;
|
|
||||||
int err;
|
|
||||||
size_t hdr_size;
|
|
||||||
/* TODO: check that we are running from vhost_worker? */
|
|
||||||
struct socket *sock = rcu_dereference_check(vq->private_data, 1);
|
|
||||||
if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
|
|
||||||
return;
|
|
||||||
|
|
||||||
mutex_lock(&vq->mutex);
|
|
||||||
vhost_disable_notify(vq);
|
|
||||||
hdr_size = vq->vhost_hlen;
|
|
||||||
|
|
||||||
vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
|
|
||||||
vq->log : NULL;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
head = vhost_get_vq_desc(&net->dev, vq, vq->iov,
|
|
||||||
ARRAY_SIZE(vq->iov),
|
|
||||||
&out, &in,
|
|
||||||
vq_log, &log);
|
|
||||||
/* On error, stop handling until the next kick. */
|
|
||||||
if (unlikely(head < 0))
|
|
||||||
break;
|
|
||||||
/* OK, now we need to know about added descriptors. */
|
|
||||||
if (head == vq->num) {
|
|
||||||
if (unlikely(vhost_enable_notify(vq))) {
|
|
||||||
/* They have slipped one in as we were
|
|
||||||
* doing that: check again. */
|
|
||||||
vhost_disable_notify(vq);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* Nothing new? Wait for eventfd to tell us
|
|
||||||
* they refilled. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* We don't need to be notified again. */
|
|
||||||
if (out) {
|
|
||||||
vq_err(vq, "Unexpected descriptor format for RX: "
|
|
||||||
"out %d, int %d\n",
|
|
||||||
out, in);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* Skip header. TODO: support TSO/mergeable rx buffers. */
|
|
||||||
s = move_iovec_hdr(vq->iov, vq->hdr, hdr_size, in);
|
|
||||||
msg.msg_iovlen = in;
|
|
||||||
len = iov_length(vq->iov, in);
|
|
||||||
/* Sanity check */
|
|
||||||
if (!len) {
|
|
||||||
vq_err(vq, "Unexpected header len for RX: "
|
|
||||||
"%zd expected %zd\n",
|
|
||||||
iov_length(vq->hdr, s), hdr_size);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
err = sock->ops->recvmsg(NULL, sock, &msg,
|
|
||||||
len, MSG_DONTWAIT | MSG_TRUNC);
|
|
||||||
/* TODO: Check specific error and bomb out unless EAGAIN? */
|
|
||||||
if (err < 0) {
|
|
||||||
vhost_discard_vq_desc(vq, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* TODO: Should check and handle checksum. */
|
|
||||||
if (err > len) {
|
|
||||||
pr_debug("Discarded truncated rx packet: "
|
|
||||||
" len %d > %zd\n", err, len);
|
|
||||||
vhost_discard_vq_desc(vq, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
len = err;
|
|
||||||
err = memcpy_toiovec(vq->hdr, (unsigned char *)&hdr, hdr_size);
|
|
||||||
if (err) {
|
|
||||||
vq_err(vq, "Unable to write vnet_hdr at addr %p: %d\n",
|
|
||||||
vq->iov->iov_base, err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
len += hdr_size;
|
|
||||||
vhost_add_used_and_signal(&net->dev, vq, head, len);
|
|
||||||
if (unlikely(vq_log))
|
|
||||||
vhost_log_write(vq, vq_log, log, len);
|
|
||||||
total_len += len;
|
|
||||||
if (unlikely(total_len >= VHOST_NET_WEIGHT)) {
|
|
||||||
vhost_poll_queue(&vq->poll);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_unlock(&vq->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Expects to be always run from workqueue - which acts as
|
|
||||||
* read-size critical section for our kind of RCU. */
|
|
||||||
static void handle_rx_mergeable(struct vhost_net *net)
|
|
||||||
{
|
{
|
||||||
struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
|
struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
|
||||||
unsigned uninitialized_var(in), log;
|
unsigned uninitialized_var(in), log;
|
||||||
|
@ -405,19 +300,18 @@ static void handle_rx_mergeable(struct vhost_net *net)
|
||||||
.msg_iov = vq->iov,
|
.msg_iov = vq->iov,
|
||||||
.msg_flags = MSG_DONTWAIT,
|
.msg_flags = MSG_DONTWAIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct virtio_net_hdr_mrg_rxbuf hdr = {
|
struct virtio_net_hdr_mrg_rxbuf hdr = {
|
||||||
.hdr.flags = 0,
|
.hdr.flags = 0,
|
||||||
.hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE
|
.hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t total_len = 0;
|
size_t total_len = 0;
|
||||||
int err, headcount;
|
int err, headcount, mergeable;
|
||||||
size_t vhost_hlen, sock_hlen;
|
size_t vhost_hlen, sock_hlen;
|
||||||
size_t vhost_len, sock_len;
|
size_t vhost_len, sock_len;
|
||||||
/* TODO: check that we are running from vhost_worker? */
|
/* TODO: check that we are running from vhost_worker? */
|
||||||
struct socket *sock = rcu_dereference_check(vq->private_data, 1);
|
struct socket *sock = rcu_dereference_check(vq->private_data, 1);
|
||||||
if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
|
|
||||||
|
if (!sock)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mutex_lock(&vq->mutex);
|
mutex_lock(&vq->mutex);
|
||||||
|
@ -427,12 +321,14 @@ static void handle_rx_mergeable(struct vhost_net *net)
|
||||||
|
|
||||||
vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
|
vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
|
||||||
vq->log : NULL;
|
vq->log : NULL;
|
||||||
|
mergeable = vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF);
|
||||||
|
|
||||||
while ((sock_len = peek_head_len(sock->sk))) {
|
while ((sock_len = peek_head_len(sock->sk))) {
|
||||||
sock_len += sock_hlen;
|
sock_len += sock_hlen;
|
||||||
vhost_len = sock_len + vhost_hlen;
|
vhost_len = sock_len + vhost_hlen;
|
||||||
headcount = get_rx_bufs(vq, vq->heads, vhost_len,
|
headcount = get_rx_bufs(vq, vq->heads, vhost_len,
|
||||||
&in, vq_log, &log);
|
&in, vq_log, &log,
|
||||||
|
likely(mergeable) ? UIO_MAXIOV : 1);
|
||||||
/* On error, stop handling until the next kick. */
|
/* On error, stop handling until the next kick. */
|
||||||
if (unlikely(headcount < 0))
|
if (unlikely(headcount < 0))
|
||||||
break;
|
break;
|
||||||
|
@ -476,7 +372,7 @@ static void handle_rx_mergeable(struct vhost_net *net)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* TODO: Should check and handle checksum. */
|
/* TODO: Should check and handle checksum. */
|
||||||
if (vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF) &&
|
if (likely(mergeable) &&
|
||||||
memcpy_toiovecend(vq->hdr, (unsigned char *)&headcount,
|
memcpy_toiovecend(vq->hdr, (unsigned char *)&headcount,
|
||||||
offsetof(typeof(hdr), num_buffers),
|
offsetof(typeof(hdr), num_buffers),
|
||||||
sizeof hdr.num_buffers)) {
|
sizeof hdr.num_buffers)) {
|
||||||
|
@ -498,14 +394,6 @@ static void handle_rx_mergeable(struct vhost_net *net)
|
||||||
mutex_unlock(&vq->mutex);
|
mutex_unlock(&vq->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_rx(struct vhost_net *net)
|
|
||||||
{
|
|
||||||
if (vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF))
|
|
||||||
handle_rx_mergeable(net);
|
|
||||||
else
|
|
||||||
handle_rx_big(net);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tx_kick(struct vhost_work *work)
|
static void handle_tx_kick(struct vhost_work *work)
|
||||||
{
|
{
|
||||||
struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
|
struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
|
||||||
|
@ -654,6 +542,7 @@ static struct socket *get_raw_socket(int fd)
|
||||||
} uaddr;
|
} uaddr;
|
||||||
int uaddr_len = sizeof uaddr, r;
|
int uaddr_len = sizeof uaddr, r;
|
||||||
struct socket *sock = sockfd_lookup(fd, &r);
|
struct socket *sock = sockfd_lookup(fd, &r);
|
||||||
|
|
||||||
if (!sock)
|
if (!sock)
|
||||||
return ERR_PTR(-ENOTSOCK);
|
return ERR_PTR(-ENOTSOCK);
|
||||||
|
|
||||||
|
@ -682,6 +571,7 @@ static struct socket *get_tap_socket(int fd)
|
||||||
{
|
{
|
||||||
struct file *file = fget(fd);
|
struct file *file = fget(fd);
|
||||||
struct socket *sock;
|
struct socket *sock;
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
return ERR_PTR(-EBADF);
|
return ERR_PTR(-EBADF);
|
||||||
sock = tun_get_socket(file);
|
sock = tun_get_socket(file);
|
||||||
|
@ -696,6 +586,7 @@ static struct socket *get_tap_socket(int fd)
|
||||||
static struct socket *get_socket(int fd)
|
static struct socket *get_socket(int fd)
|
||||||
{
|
{
|
||||||
struct socket *sock;
|
struct socket *sock;
|
||||||
|
|
||||||
/* special case to disable backend */
|
/* special case to disable backend */
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -741,9 +632,9 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
|
||||||
oldsock = rcu_dereference_protected(vq->private_data,
|
oldsock = rcu_dereference_protected(vq->private_data,
|
||||||
lockdep_is_held(&vq->mutex));
|
lockdep_is_held(&vq->mutex));
|
||||||
if (sock != oldsock) {
|
if (sock != oldsock) {
|
||||||
vhost_net_disable_vq(n, vq);
|
vhost_net_disable_vq(n, vq);
|
||||||
rcu_assign_pointer(vq->private_data, sock);
|
rcu_assign_pointer(vq->private_data, sock);
|
||||||
vhost_net_enable_vq(n, vq);
|
vhost_net_enable_vq(n, vq);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&vq->mutex);
|
mutex_unlock(&vq->mutex);
|
||||||
|
@ -768,6 +659,7 @@ static long vhost_net_reset_owner(struct vhost_net *n)
|
||||||
struct socket *tx_sock = NULL;
|
struct socket *tx_sock = NULL;
|
||||||
struct socket *rx_sock = NULL;
|
struct socket *rx_sock = NULL;
|
||||||
long err;
|
long err;
|
||||||
|
|
||||||
mutex_lock(&n->dev.mutex);
|
mutex_lock(&n->dev.mutex);
|
||||||
err = vhost_dev_check_owner(&n->dev);
|
err = vhost_dev_check_owner(&n->dev);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -829,6 +721,7 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
|
||||||
struct vhost_vring_file backend;
|
struct vhost_vring_file backend;
|
||||||
u64 features;
|
u64 features;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
switch (ioctl) {
|
switch (ioctl) {
|
||||||
case VHOST_NET_SET_BACKEND:
|
case VHOST_NET_SET_BACKEND:
|
||||||
if (copy_from_user(&backend, argp, sizeof backend))
|
if (copy_from_user(&backend, argp, sizeof backend))
|
||||||
|
|
|
@ -41,8 +41,8 @@ static void vhost_poll_func(struct file *file, wait_queue_head_t *wqh,
|
||||||
poll_table *pt)
|
poll_table *pt)
|
||||||
{
|
{
|
||||||
struct vhost_poll *poll;
|
struct vhost_poll *poll;
|
||||||
poll = container_of(pt, struct vhost_poll, table);
|
|
||||||
|
|
||||||
|
poll = container_of(pt, struct vhost_poll, table);
|
||||||
poll->wqh = wqh;
|
poll->wqh = wqh;
|
||||||
add_wait_queue(wqh, &poll->wait);
|
add_wait_queue(wqh, &poll->wait);
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,7 @@ void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn,
|
||||||
void vhost_poll_start(struct vhost_poll *poll, struct file *file)
|
void vhost_poll_start(struct vhost_poll *poll, struct file *file)
|
||||||
{
|
{
|
||||||
unsigned long mask;
|
unsigned long mask;
|
||||||
|
|
||||||
mask = file->f_op->poll(file, &poll->table);
|
mask = file->f_op->poll(file, &poll->table);
|
||||||
if (mask)
|
if (mask)
|
||||||
vhost_poll_wakeup(&poll->wait, 0, 0, (void *)mask);
|
vhost_poll_wakeup(&poll->wait, 0, 0, (void *)mask);
|
||||||
|
@ -101,6 +102,7 @@ static bool vhost_work_seq_done(struct vhost_dev *dev, struct vhost_work *work,
|
||||||
unsigned seq)
|
unsigned seq)
|
||||||
{
|
{
|
||||||
int left;
|
int left;
|
||||||
|
|
||||||
spin_lock_irq(&dev->work_lock);
|
spin_lock_irq(&dev->work_lock);
|
||||||
left = seq - work->done_seq;
|
left = seq - work->done_seq;
|
||||||
spin_unlock_irq(&dev->work_lock);
|
spin_unlock_irq(&dev->work_lock);
|
||||||
|
@ -222,6 +224,7 @@ static int vhost_worker(void *data)
|
||||||
static long vhost_dev_alloc_iovecs(struct vhost_dev *dev)
|
static long vhost_dev_alloc_iovecs(struct vhost_dev *dev)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < dev->nvqs; ++i) {
|
for (i = 0; i < dev->nvqs; ++i) {
|
||||||
dev->vqs[i].indirect = kmalloc(sizeof *dev->vqs[i].indirect *
|
dev->vqs[i].indirect = kmalloc(sizeof *dev->vqs[i].indirect *
|
||||||
UIO_MAXIOV, GFP_KERNEL);
|
UIO_MAXIOV, GFP_KERNEL);
|
||||||
|
@ -235,6 +238,7 @@ static long vhost_dev_alloc_iovecs(struct vhost_dev *dev)
|
||||||
goto err_nomem;
|
goto err_nomem;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_nomem:
|
err_nomem:
|
||||||
for (; i >= 0; --i) {
|
for (; i >= 0; --i) {
|
||||||
kfree(dev->vqs[i].indirect);
|
kfree(dev->vqs[i].indirect);
|
||||||
|
@ -247,6 +251,7 @@ err_nomem:
|
||||||
static void vhost_dev_free_iovecs(struct vhost_dev *dev)
|
static void vhost_dev_free_iovecs(struct vhost_dev *dev)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < dev->nvqs; ++i) {
|
for (i = 0; i < dev->nvqs; ++i) {
|
||||||
kfree(dev->vqs[i].indirect);
|
kfree(dev->vqs[i].indirect);
|
||||||
dev->vqs[i].indirect = NULL;
|
dev->vqs[i].indirect = NULL;
|
||||||
|
@ -296,26 +301,28 @@ long vhost_dev_check_owner(struct vhost_dev *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct vhost_attach_cgroups_struct {
|
struct vhost_attach_cgroups_struct {
|
||||||
struct vhost_work work;
|
struct vhost_work work;
|
||||||
struct task_struct *owner;
|
struct task_struct *owner;
|
||||||
int ret;
|
int ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void vhost_attach_cgroups_work(struct vhost_work *work)
|
static void vhost_attach_cgroups_work(struct vhost_work *work)
|
||||||
{
|
{
|
||||||
struct vhost_attach_cgroups_struct *s;
|
struct vhost_attach_cgroups_struct *s;
|
||||||
s = container_of(work, struct vhost_attach_cgroups_struct, work);
|
|
||||||
s->ret = cgroup_attach_task_all(s->owner, current);
|
s = container_of(work, struct vhost_attach_cgroups_struct, work);
|
||||||
|
s->ret = cgroup_attach_task_all(s->owner, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vhost_attach_cgroups(struct vhost_dev *dev)
|
static int vhost_attach_cgroups(struct vhost_dev *dev)
|
||||||
{
|
{
|
||||||
struct vhost_attach_cgroups_struct attach;
|
struct vhost_attach_cgroups_struct attach;
|
||||||
attach.owner = current;
|
|
||||||
vhost_work_init(&attach.work, vhost_attach_cgroups_work);
|
attach.owner = current;
|
||||||
vhost_work_queue(dev, &attach.work);
|
vhost_work_init(&attach.work, vhost_attach_cgroups_work);
|
||||||
vhost_work_flush(dev, &attach.work);
|
vhost_work_queue(dev, &attach.work);
|
||||||
return attach.ret;
|
vhost_work_flush(dev, &attach.work);
|
||||||
|
return attach.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Caller should have device mutex */
|
/* Caller should have device mutex */
|
||||||
|
@ -323,11 +330,13 @@ static long vhost_dev_set_owner(struct vhost_dev *dev)
|
||||||
{
|
{
|
||||||
struct task_struct *worker;
|
struct task_struct *worker;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* Is there an owner already? */
|
/* Is there an owner already? */
|
||||||
if (dev->mm) {
|
if (dev->mm) {
|
||||||
err = -EBUSY;
|
err = -EBUSY;
|
||||||
goto err_mm;
|
goto err_mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No owner, become one */
|
/* No owner, become one */
|
||||||
dev->mm = get_task_mm(current);
|
dev->mm = get_task_mm(current);
|
||||||
worker = kthread_create(vhost_worker, dev, "vhost-%d", current->pid);
|
worker = kthread_create(vhost_worker, dev, "vhost-%d", current->pid);
|
||||||
|
@ -380,6 +389,7 @@ long vhost_dev_reset_owner(struct vhost_dev *dev)
|
||||||
void vhost_dev_cleanup(struct vhost_dev *dev)
|
void vhost_dev_cleanup(struct vhost_dev *dev)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < dev->nvqs; ++i) {
|
for (i = 0; i < dev->nvqs; ++i) {
|
||||||
if (dev->vqs[i].kick && dev->vqs[i].handle_kick) {
|
if (dev->vqs[i].kick && dev->vqs[i].handle_kick) {
|
||||||
vhost_poll_stop(&dev->vqs[i].poll);
|
vhost_poll_stop(&dev->vqs[i].poll);
|
||||||
|
@ -421,6 +431,7 @@ void vhost_dev_cleanup(struct vhost_dev *dev)
|
||||||
static int log_access_ok(void __user *log_base, u64 addr, unsigned long sz)
|
static int log_access_ok(void __user *log_base, u64 addr, unsigned long sz)
|
||||||
{
|
{
|
||||||
u64 a = addr / VHOST_PAGE_SIZE / 8;
|
u64 a = addr / VHOST_PAGE_SIZE / 8;
|
||||||
|
|
||||||
/* Make sure 64 bit math will not overflow. */
|
/* Make sure 64 bit math will not overflow. */
|
||||||
if (a > ULONG_MAX - (unsigned long)log_base ||
|
if (a > ULONG_MAX - (unsigned long)log_base ||
|
||||||
a + (unsigned long)log_base > ULONG_MAX)
|
a + (unsigned long)log_base > ULONG_MAX)
|
||||||
|
@ -461,6 +472,7 @@ static int memory_access_ok(struct vhost_dev *d, struct vhost_memory *mem,
|
||||||
int log_all)
|
int log_all)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < d->nvqs; ++i) {
|
for (i = 0; i < d->nvqs; ++i) {
|
||||||
int ok;
|
int ok;
|
||||||
mutex_lock(&d->vqs[i].mutex);
|
mutex_lock(&d->vqs[i].mutex);
|
||||||
|
@ -527,6 +539,7 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
|
||||||
{
|
{
|
||||||
struct vhost_memory mem, *newmem, *oldmem;
|
struct vhost_memory mem, *newmem, *oldmem;
|
||||||
unsigned long size = offsetof(struct vhost_memory, regions);
|
unsigned long size = offsetof(struct vhost_memory, regions);
|
||||||
|
|
||||||
if (copy_from_user(&mem, m, size))
|
if (copy_from_user(&mem, m, size))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
if (mem.padding)
|
if (mem.padding)
|
||||||
|
@ -544,7 +557,8 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!memory_access_ok(d, newmem, vhost_has_feature(d, VHOST_F_LOG_ALL))) {
|
if (!memory_access_ok(d, newmem,
|
||||||
|
vhost_has_feature(d, VHOST_F_LOG_ALL))) {
|
||||||
kfree(newmem);
|
kfree(newmem);
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
@ -560,6 +574,7 @@ static int init_used(struct vhost_virtqueue *vq,
|
||||||
struct vring_used __user *used)
|
struct vring_used __user *used)
|
||||||
{
|
{
|
||||||
int r = put_user(vq->used_flags, &used->flags);
|
int r = put_user(vq->used_flags, &used->flags);
|
||||||
|
|
||||||
if (r)
|
if (r)
|
||||||
return r;
|
return r;
|
||||||
return get_user(vq->last_used_idx, &used->idx);
|
return get_user(vq->last_used_idx, &used->idx);
|
||||||
|
@ -849,6 +864,7 @@ static const struct vhost_memory_region *find_region(struct vhost_memory *mem,
|
||||||
{
|
{
|
||||||
struct vhost_memory_region *reg;
|
struct vhost_memory_region *reg;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* linear search is not brilliant, but we really have on the order of 6
|
/* linear search is not brilliant, but we really have on the order of 6
|
||||||
* regions in practice */
|
* regions in practice */
|
||||||
for (i = 0; i < mem->nregions; ++i) {
|
for (i = 0; i < mem->nregions; ++i) {
|
||||||
|
@ -871,6 +887,7 @@ static int set_bit_to_user(int nr, void __user *addr)
|
||||||
void *base;
|
void *base;
|
||||||
int bit = nr + (log % PAGE_SIZE) * 8;
|
int bit = nr + (log % PAGE_SIZE) * 8;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = get_user_pages_fast(log, 1, 1, &page);
|
r = get_user_pages_fast(log, 1, 1, &page);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -888,6 +905,7 @@ static int log_write(void __user *log_base,
|
||||||
{
|
{
|
||||||
u64 write_page = write_address / VHOST_PAGE_SIZE;
|
u64 write_page = write_address / VHOST_PAGE_SIZE;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (!write_length)
|
if (!write_length)
|
||||||
return 0;
|
return 0;
|
||||||
write_length += write_address % VHOST_PAGE_SIZE;
|
write_length += write_address % VHOST_PAGE_SIZE;
|
||||||
|
@ -1037,8 +1055,8 @@ static int get_indirect(struct vhost_dev *dev, struct vhost_virtqueue *vq,
|
||||||
i, count);
|
i, count);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (unlikely(memcpy_fromiovec((unsigned char *)&desc, vq->indirect,
|
if (unlikely(memcpy_fromiovec((unsigned char *)&desc,
|
||||||
sizeof desc))) {
|
vq->indirect, sizeof desc))) {
|
||||||
vq_err(vq, "Failed indirect descriptor: idx %d, %zx\n",
|
vq_err(vq, "Failed indirect descriptor: idx %d, %zx\n",
|
||||||
i, (size_t)indirect->addr + i * sizeof desc);
|
i, (size_t)indirect->addr + i * sizeof desc);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -1153,7 +1171,7 @@ int vhost_get_vq_desc(struct vhost_dev *dev, struct vhost_virtqueue *vq,
|
||||||
i, vq->num, head);
|
i, vq->num, head);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = copy_from_user(&desc, vq->desc + i, sizeof desc);
|
ret = __copy_from_user(&desc, vq->desc + i, sizeof desc);
|
||||||
if (unlikely(ret)) {
|
if (unlikely(ret)) {
|
||||||
vq_err(vq, "Failed to get descriptor: idx %d addr %p\n",
|
vq_err(vq, "Failed to get descriptor: idx %d addr %p\n",
|
||||||
i, vq->desc + i);
|
i, vq->desc + i);
|
||||||
|
@ -1317,6 +1335,7 @@ int vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads,
|
||||||
void vhost_signal(struct vhost_dev *dev, struct vhost_virtqueue *vq)
|
void vhost_signal(struct vhost_dev *dev, struct vhost_virtqueue *vq)
|
||||||
{
|
{
|
||||||
__u16 flags;
|
__u16 flags;
|
||||||
|
|
||||||
/* Flush out used index updates. This is paired
|
/* Flush out used index updates. This is paired
|
||||||
* with the barrier that the Guest executes when enabling
|
* with the barrier that the Guest executes when enabling
|
||||||
* interrupts. */
|
* interrupts. */
|
||||||
|
@ -1361,6 +1380,7 @@ bool vhost_enable_notify(struct vhost_virtqueue *vq)
|
||||||
{
|
{
|
||||||
u16 avail_idx;
|
u16 avail_idx;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (!(vq->used_flags & VRING_USED_F_NO_NOTIFY))
|
if (!(vq->used_flags & VRING_USED_F_NO_NOTIFY))
|
||||||
return false;
|
return false;
|
||||||
vq->used_flags &= ~VRING_USED_F_NO_NOTIFY;
|
vq->used_flags &= ~VRING_USED_F_NO_NOTIFY;
|
||||||
|
@ -1387,6 +1407,7 @@ bool vhost_enable_notify(struct vhost_virtqueue *vq)
|
||||||
void vhost_disable_notify(struct vhost_virtqueue *vq)
|
void vhost_disable_notify(struct vhost_virtqueue *vq)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (vq->used_flags & VRING_USED_F_NO_NOTIFY)
|
if (vq->used_flags & VRING_USED_F_NO_NOTIFY)
|
||||||
return;
|
return;
|
||||||
vq->used_flags |= VRING_USED_F_NO_NOTIFY;
|
vq->used_flags |= VRING_USED_F_NO_NOTIFY;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче