diff options
author | Jason Wang <jasowang@redhat.com> | 2011-06-21 06:04:38 -0400 |
---|---|---|
committer | Michael S. Tsirkin <mst@redhat.com> | 2011-07-19 06:28:34 -0400 |
commit | 2723feaa8ec64f677f644c9189ed87d83f5559c1 (patch) | |
tree | 23ecb435e42dac4614c5470935e813cf91f74c5d /drivers/vhost | |
parent | f59281dafb832b161133743fcf3dc29051e6fdb8 (diff) |
vhost: set log when updating used flags or avail event
We need to log writes when updating used flags and avail event
fields. Otherwise the guest may see a stale value after migration and
miss notifying the host.
Signed-off-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Diffstat (limited to 'drivers/vhost')
-rw-r--r-- | drivers/vhost/vhost.c | 84 |
1 files changed, 54 insertions, 30 deletions
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 9a108038fe52..46822c0d9f7f 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c | |||
@@ -629,19 +629,6 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m) | |||
629 | return 0; | 629 | return 0; |
630 | } | 630 | } |
631 | 631 | ||
632 | int vhost_init_used(struct vhost_virtqueue *vq) | ||
633 | { | ||
634 | int r; | ||
635 | if (!vq->private_data) | ||
636 | return 0; | ||
637 | |||
638 | r = put_user(vq->used_flags, &vq->used->flags); | ||
639 | if (r) | ||
640 | return r; | ||
641 | vq->signalled_used_valid = false; | ||
642 | return get_user(vq->last_used_idx, &vq->used->idx); | ||
643 | } | ||
644 | |||
645 | static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp) | 632 | static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp) |
646 | { | 633 | { |
647 | struct file *eventfp, *filep = NULL, | 634 | struct file *eventfp, *filep = NULL, |
@@ -1008,6 +995,57 @@ int vhost_log_write(struct vhost_virtqueue *vq, struct vhost_log *log, | |||
1008 | return 0; | 995 | return 0; |
1009 | } | 996 | } |
1010 | 997 | ||
998 | static int vhost_update_used_flags(struct vhost_virtqueue *vq) | ||
999 | { | ||
1000 | void __user *used; | ||
1001 | if (put_user(vq->used_flags, &vq->used->flags) < 0) | ||
1002 | return -EFAULT; | ||
1003 | if (unlikely(vq->log_used)) { | ||
1004 | /* Make sure the flag is seen before log. */ | ||
1005 | smp_wmb(); | ||
1006 | /* Log used flag write. */ | ||
1007 | used = &vq->used->flags; | ||
1008 | log_write(vq->log_base, vq->log_addr + | ||
1009 | (used - (void __user *)vq->used), | ||
1010 | sizeof vq->used->flags); | ||
1011 | if (vq->log_ctx) | ||
1012 | eventfd_signal(vq->log_ctx, 1); | ||
1013 | } | ||
1014 | return 0; | ||
1015 | } | ||
1016 | |||
1017 | static int vhost_update_avail_event(struct vhost_virtqueue *vq, u16 avail_event) | ||
1018 | { | ||
1019 | if (put_user(vq->avail_idx, vhost_avail_event(vq))) | ||
1020 | return -EFAULT; | ||
1021 | if (unlikely(vq->log_used)) { | ||
1022 | void __user *used; | ||
1023 | /* Make sure the event is seen before log. */ | ||
1024 | smp_wmb(); | ||
1025 | /* Log avail event write */ | ||
1026 | used = vhost_avail_event(vq); | ||
1027 | log_write(vq->log_base, vq->log_addr + | ||
1028 | (used - (void __user *)vq->used), | ||
1029 | sizeof *vhost_avail_event(vq)); | ||
1030 | if (vq->log_ctx) | ||
1031 | eventfd_signal(vq->log_ctx, 1); | ||
1032 | } | ||
1033 | return 0; | ||
1034 | } | ||
1035 | |||
1036 | int vhost_init_used(struct vhost_virtqueue *vq) | ||
1037 | { | ||
1038 | int r; | ||
1039 | if (!vq->private_data) | ||
1040 | return 0; | ||
1041 | |||
1042 | r = vhost_update_used_flags(vq); | ||
1043 | if (r) | ||
1044 | return r; | ||
1045 | vq->signalled_used_valid = false; | ||
1046 | return get_user(vq->last_used_idx, &vq->used->idx); | ||
1047 | } | ||
1048 | |||
1011 | static int translate_desc(struct vhost_dev *dev, u64 addr, u32 len, | 1049 | static int translate_desc(struct vhost_dev *dev, u64 addr, u32 len, |
1012 | struct iovec iov[], int iov_size) | 1050 | struct iovec iov[], int iov_size) |
1013 | { | 1051 | { |
@@ -1479,34 +1517,20 @@ bool vhost_enable_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) | |||
1479 | return false; | 1517 | return false; |
1480 | vq->used_flags &= ~VRING_USED_F_NO_NOTIFY; | 1518 | vq->used_flags &= ~VRING_USED_F_NO_NOTIFY; |
1481 | if (!vhost_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) { | 1519 | if (!vhost_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) { |
1482 | r = put_user(vq->used_flags, &vq->used->flags); | 1520 | r = vhost_update_used_flags(vq); |
1483 | if (r) { | 1521 | if (r) { |
1484 | vq_err(vq, "Failed to enable notification at %p: %d\n", | 1522 | vq_err(vq, "Failed to enable notification at %p: %d\n", |
1485 | &vq->used->flags, r); | 1523 | &vq->used->flags, r); |
1486 | return false; | 1524 | return false; |
1487 | } | 1525 | } |
1488 | } else { | 1526 | } else { |
1489 | r = put_user(vq->avail_idx, vhost_avail_event(vq)); | 1527 | r = vhost_update_avail_event(vq, vq->avail_idx); |
1490 | if (r) { | 1528 | if (r) { |
1491 | vq_err(vq, "Failed to update avail event index at %p: %d\n", | 1529 | vq_err(vq, "Failed to update avail event index at %p: %d\n", |
1492 | vhost_avail_event(vq), r); | 1530 | vhost_avail_event(vq), r); |
1493 | return false; | 1531 | return false; |
1494 | } | 1532 | } |
1495 | } | 1533 | } |
1496 | if (unlikely(vq->log_used)) { | ||
1497 | void __user *used; | ||
1498 | /* Make sure data is seen before log. */ | ||
1499 | smp_wmb(); | ||
1500 | used = vhost_has_feature(dev, VIRTIO_RING_F_EVENT_IDX) ? | ||
1501 | &vq->used->flags : vhost_avail_event(vq); | ||
1502 | /* Log used flags or event index entry write. Both are 16 bit | ||
1503 | * fields. */ | ||
1504 | log_write(vq->log_base, vq->log_addr + | ||
1505 | (used - (void __user *)vq->used), | ||
1506 | sizeof(u16)); | ||
1507 | if (vq->log_ctx) | ||
1508 | eventfd_signal(vq->log_ctx, 1); | ||
1509 | } | ||
1510 | /* They could have slipped one in as we were doing that: make | 1534 | /* They could have slipped one in as we were doing that: make |
1511 | * sure it's written, then check again. */ | 1535 | * sure it's written, then check again. */ |
1512 | smp_mb(); | 1536 | smp_mb(); |
@@ -1529,7 +1553,7 @@ void vhost_disable_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) | |||
1529 | return; | 1553 | return; |
1530 | vq->used_flags |= VRING_USED_F_NO_NOTIFY; | 1554 | vq->used_flags |= VRING_USED_F_NO_NOTIFY; |
1531 | if (!vhost_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) { | 1555 | if (!vhost_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) { |
1532 | r = put_user(vq->used_flags, &vq->used->flags); | 1556 | r = vhost_update_used_flags(vq); |
1533 | if (r) | 1557 | if (r) |
1534 | vq_err(vq, "Failed to enable notification at %p: %d\n", | 1558 | vq_err(vq, "Failed to enable notification at %p: %d\n", |
1535 | &vq->used->flags, r); | 1559 | &vq->used->flags, r); |