Skip to content

Commit cc5e710

Browse files
jasowangdavem330
authored andcommitted
vhost: log dirty page correctly
Vhost dirty page logging API is designed to sync through GPA. But we try to log GIOVA when device IOTLB is enabled. This is wrong and may lead to missing data after migration. To solve this issue, when logging with device IOTLB enabled, we will: 1) reuse the device IOTLB translation result of GIOVA->HVA mapping to get HVA, for writable descriptor, get HVA through iovec. For used ring update, translate its GIOVA to HVA 2) traverse the GPA->HVA mapping to get the possible GPA and log through GPA. Pay attention this reverse mapping is not guaranteed to be unique, so we should log each possible GPA in this case. This fix the failure of scp to guest during migration. In -next, we will probably support passing GIOVA->GPA instead of GIOVA->HVA. Fixes: 6b1e6cc ("vhost: new device IOTLB API") Reported-by: Jintack Lim <jintack@cs.columbia.edu> Cc: Jintack Lim <jintack@cs.columbia.edu> Signed-off-by: Jason Wang <jasowang@redhat.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent f655f8b commit cc5e710

File tree

3 files changed

+87
-16
lines changed

3 files changed

+87
-16
lines changed

drivers/vhost/net.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1236,7 +1236,8 @@ static void handle_rx(struct vhost_net *net)
12361236
if (nvq->done_idx > VHOST_NET_BATCH)
12371237
vhost_net_signal_used(nvq);
12381238
if (unlikely(vq_log))
1239-
vhost_log_write(vq, vq_log, log, vhost_len);
1239+
vhost_log_write(vq, vq_log, log, vhost_len,
1240+
vq->iov, in);
12401241
total_len += vhost_len;
12411242
if (unlikely(vhost_exceeds_weight(++recv_pkts, total_len))) {
12421243
vhost_poll_queue(&vq->poll);

drivers/vhost/vhost.c

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,13 +1733,87 @@ static int log_write(void __user *log_base,
17331733
return r;
17341734
}
17351735

1736+
static int log_write_hva(struct vhost_virtqueue *vq, u64 hva, u64 len)
1737+
{
1738+
struct vhost_umem *umem = vq->umem;
1739+
struct vhost_umem_node *u;
1740+
u64 start, end, l, min;
1741+
int r;
1742+
bool hit = false;
1743+
1744+
while (len) {
1745+
min = len;
1746+
/* More than one GPAs can be mapped into a single HVA. So
1747+
* iterate all possible umems here to be safe.
1748+
*/
1749+
list_for_each_entry(u, &umem->umem_list, link) {
1750+
if (u->userspace_addr > hva - 1 + len ||
1751+
u->userspace_addr - 1 + u->size < hva)
1752+
continue;
1753+
start = max(u->userspace_addr, hva);
1754+
end = min(u->userspace_addr - 1 + u->size,
1755+
hva - 1 + len);
1756+
l = end - start + 1;
1757+
r = log_write(vq->log_base,
1758+
u->start + start - u->userspace_addr,
1759+
l);
1760+
if (r < 0)
1761+
return r;
1762+
hit = true;
1763+
min = min(l, min);
1764+
}
1765+
1766+
if (!hit)
1767+
return -EFAULT;
1768+
1769+
len -= min;
1770+
hva += min;
1771+
}
1772+
1773+
return 0;
1774+
}
1775+
1776+
static int log_used(struct vhost_virtqueue *vq, u64 used_offset, u64 len)
1777+
{
1778+
struct iovec iov[64];
1779+
int i, ret;
1780+
1781+
if (!vq->iotlb)
1782+
return log_write(vq->log_base, vq->log_addr + used_offset, len);
1783+
1784+
ret = translate_desc(vq, (uintptr_t)vq->used + used_offset,
1785+
len, iov, 64, VHOST_ACCESS_WO);
1786+
if (ret)
1787+
return ret;
1788+
1789+
for (i = 0; i < ret; i++) {
1790+
ret = log_write_hva(vq, (uintptr_t)iov[i].iov_base,
1791+
iov[i].iov_len);
1792+
if (ret)
1793+
return ret;
1794+
}
1795+
1796+
return 0;
1797+
}
1798+
17361799
int vhost_log_write(struct vhost_virtqueue *vq, struct vhost_log *log,
1737-
unsigned int log_num, u64 len)
1800+
unsigned int log_num, u64 len, struct iovec *iov, int count)
17381801
{
17391802
int i, r;
17401803

17411804
/* Make sure data written is seen before log. */
17421805
smp_wmb();
1806+
1807+
if (vq->iotlb) {
1808+
for (i = 0; i < count; i++) {
1809+
r = log_write_hva(vq, (uintptr_t)iov[i].iov_base,
1810+
iov[i].iov_len);
1811+
if (r < 0)
1812+
return r;
1813+
}
1814+
return 0;
1815+
}
1816+
17431817
for (i = 0; i < log_num; ++i) {
17441818
u64 l = min(log[i].len, len);
17451819
r = log_write(vq->log_base, log[i].addr, l);
@@ -1769,9 +1843,8 @@ static int vhost_update_used_flags(struct vhost_virtqueue *vq)
17691843
smp_wmb();
17701844
/* Log used flag write. */
17711845
used = &vq->used->flags;
1772-
log_write(vq->log_base, vq->log_addr +
1773-
(used - (void __user *)vq->used),
1774-
sizeof vq->used->flags);
1846+
log_used(vq, (used - (void __user *)vq->used),
1847+
sizeof vq->used->flags);
17751848
if (vq->log_ctx)
17761849
eventfd_signal(vq->log_ctx, 1);
17771850
}
@@ -1789,9 +1862,8 @@ static int vhost_update_avail_event(struct vhost_virtqueue *vq, u16 avail_event)
17891862
smp_wmb();
17901863
/* Log avail event write */
17911864
used = vhost_avail_event(vq);
1792-
log_write(vq->log_base, vq->log_addr +
1793-
(used - (void __user *)vq->used),
1794-
sizeof *vhost_avail_event(vq));
1865+
log_used(vq, (used - (void __user *)vq->used),
1866+
sizeof *vhost_avail_event(vq));
17951867
if (vq->log_ctx)
17961868
eventfd_signal(vq->log_ctx, 1);
17971869
}
@@ -2191,10 +2263,8 @@ static int __vhost_add_used_n(struct vhost_virtqueue *vq,
21912263
/* Make sure data is seen before log. */
21922264
smp_wmb();
21932265
/* Log used ring entry write. */
2194-
log_write(vq->log_base,
2195-
vq->log_addr +
2196-
((void __user *)used - (void __user *)vq->used),
2197-
count * sizeof *used);
2266+
log_used(vq, ((void __user *)used - (void __user *)vq->used),
2267+
count * sizeof *used);
21982268
}
21992269
old = vq->last_used_idx;
22002270
new = (vq->last_used_idx += count);
@@ -2236,9 +2306,8 @@ int vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads,
22362306
/* Make sure used idx is seen before log. */
22372307
smp_wmb();
22382308
/* Log used index update. */
2239-
log_write(vq->log_base,
2240-
vq->log_addr + offsetof(struct vring_used, idx),
2241-
sizeof vq->used->idx);
2309+
log_used(vq, offsetof(struct vring_used, idx),
2310+
sizeof vq->used->idx);
22422311
if (vq->log_ctx)
22432312
eventfd_signal(vq->log_ctx, 1);
22442313
}

drivers/vhost/vhost.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ bool vhost_vq_avail_empty(struct vhost_dev *, struct vhost_virtqueue *);
205205
bool vhost_enable_notify(struct vhost_dev *, struct vhost_virtqueue *);
206206

207207
int vhost_log_write(struct vhost_virtqueue *vq, struct vhost_log *log,
208-
unsigned int log_num, u64 len);
208+
unsigned int log_num, u64 len,
209+
struct iovec *iov, int count);
209210
int vq_iotlb_prefetch(struct vhost_virtqueue *vq);
210211

211212
struct vhost_msg_node *vhost_new_msg(struct vhost_virtqueue *vq, int type);

0 commit comments

Comments
 (0)