Skip to content

Commit 67a873d

Browse files
jasowangmstsirkin
authored andcommitted
vhost: basic in order support
This patch adds basic in order support for vhost. Two optimizations are implemented in this patch: 1) Since driver uses descriptor in order, vhost can deduce the next avail ring head by counting the number of descriptors that has been used in next_avail_head. This eliminate the need to access the available ring in vhost. 2) vhost_add_used_and_singal_n() is extended to accept the number of batched buffers per used elem. While this increases the times of userspace memory access but it helps to reduce the chance of used ring access of both the driver and vhost. Vhost-net will be the first user for this. Acked-by: Jonah Palmer <[email protected]> Signed-off-by: Jason Wang <[email protected]> Message-Id: <[email protected]> Signed-off-by: Michael S. Tsirkin <[email protected]> Tested-by: Lei Yang <[email protected]>
1 parent b4ba120 commit 67a873d

File tree

3 files changed

+109
-25
lines changed

3 files changed

+109
-25
lines changed

drivers/vhost/net.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,8 @@ static void vhost_zerocopy_signal_used(struct vhost_net *net,
374374
while (j) {
375375
add = min(UIO_MAXIOV - nvq->done_idx, j);
376376
vhost_add_used_and_signal_n(vq->dev, vq,
377-
&vq->heads[nvq->done_idx], add);
377+
&vq->heads[nvq->done_idx],
378+
NULL, add);
378379
nvq->done_idx = (nvq->done_idx + add) % UIO_MAXIOV;
379380
j -= add;
380381
}
@@ -457,7 +458,8 @@ static void vhost_net_signal_used(struct vhost_net_virtqueue *nvq)
457458
if (!nvq->done_idx)
458459
return;
459460

460-
vhost_add_used_and_signal_n(dev, vq, vq->heads, nvq->done_idx);
461+
vhost_add_used_and_signal_n(dev, vq, vq->heads, NULL,
462+
nvq->done_idx);
461463
nvq->done_idx = 0;
462464
}
463465

drivers/vhost/vhost.c

Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ static void vhost_vq_reset(struct vhost_dev *dev,
372372
vq->avail = NULL;
373373
vq->used = NULL;
374374
vq->last_avail_idx = 0;
375+
vq->next_avail_head = 0;
375376
vq->avail_idx = 0;
376377
vq->last_used_idx = 0;
377378
vq->signalled_used = 0;
@@ -501,6 +502,8 @@ static void vhost_vq_free_iovecs(struct vhost_virtqueue *vq)
501502
vq->log = NULL;
502503
kfree(vq->heads);
503504
vq->heads = NULL;
505+
kfree(vq->nheads);
506+
vq->nheads = NULL;
504507
}
505508

506509
/* Helper to allocate iovec buffers for all vqs. */
@@ -518,7 +521,9 @@ static long vhost_dev_alloc_iovecs(struct vhost_dev *dev)
518521
GFP_KERNEL);
519522
vq->heads = kmalloc_array(dev->iov_limit, sizeof(*vq->heads),
520523
GFP_KERNEL);
521-
if (!vq->indirect || !vq->log || !vq->heads)
524+
vq->nheads = kmalloc_array(dev->iov_limit, sizeof(*vq->nheads),
525+
GFP_KERNEL);
526+
if (!vq->indirect || !vq->log || !vq->heads || !vq->nheads)
522527
goto err_nomem;
523528
}
524529
return 0;
@@ -2159,14 +2164,15 @@ long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *arg
21592164
break;
21602165
}
21612166
if (vhost_has_feature(vq, VIRTIO_F_RING_PACKED)) {
2162-
vq->last_avail_idx = s.num & 0xffff;
2167+
vq->next_avail_head = vq->last_avail_idx =
2168+
s.num & 0xffff;
21632169
vq->last_used_idx = (s.num >> 16) & 0xffff;
21642170
} else {
21652171
if (s.num > 0xffff) {
21662172
r = -EINVAL;
21672173
break;
21682174
}
2169-
vq->last_avail_idx = s.num;
2175+
vq->next_avail_head = vq->last_avail_idx = s.num;
21702176
}
21712177
/* Forget the cached index value. */
21722178
vq->avail_idx = vq->last_avail_idx;
@@ -2798,11 +2804,12 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
27982804
unsigned int *out_num, unsigned int *in_num,
27992805
struct vhost_log *log, unsigned int *log_num)
28002806
{
2807+
bool in_order = vhost_has_feature(vq, VIRTIO_F_IN_ORDER);
28012808
struct vring_desc desc;
28022809
unsigned int i, head, found = 0;
28032810
u16 last_avail_idx = vq->last_avail_idx;
28042811
__virtio16 ring_head;
2805-
int ret, access;
2812+
int ret, access, c = 0;
28062813

28072814
if (vq->avail_idx == vq->last_avail_idx) {
28082815
ret = vhost_get_avail_idx(vq);
@@ -2813,17 +2820,21 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
28132820
return vq->num;
28142821
}
28152822

2816-
/* Grab the next descriptor number they're advertising, and increment
2817-
* the index we've seen. */
2818-
if (unlikely(vhost_get_avail_head(vq, &ring_head, last_avail_idx))) {
2819-
vq_err(vq, "Failed to read head: idx %d address %p\n",
2820-
last_avail_idx,
2821-
&vq->avail->ring[last_avail_idx % vq->num]);
2822-
return -EFAULT;
2823+
if (in_order)
2824+
head = vq->next_avail_head & (vq->num - 1);
2825+
else {
2826+
/* Grab the next descriptor number they're
2827+
* advertising, and increment the index we've seen. */
2828+
if (unlikely(vhost_get_avail_head(vq, &ring_head,
2829+
last_avail_idx))) {
2830+
vq_err(vq, "Failed to read head: idx %d address %p\n",
2831+
last_avail_idx,
2832+
&vq->avail->ring[last_avail_idx % vq->num]);
2833+
return -EFAULT;
2834+
}
2835+
head = vhost16_to_cpu(vq, ring_head);
28232836
}
28242837

2825-
head = vhost16_to_cpu(vq, ring_head);
2826-
28272838
/* If their number is silly, that's an error. */
28282839
if (unlikely(head >= vq->num)) {
28292840
vq_err(vq, "Guest says index %u > %u is available",
@@ -2866,6 +2877,7 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
28662877
"in indirect descriptor at idx %d\n", i);
28672878
return ret;
28682879
}
2880+
++c;
28692881
continue;
28702882
}
28712883

@@ -2901,10 +2913,12 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
29012913
}
29022914
*out_num += ret;
29032915
}
2916+
++c;
29042917
} while ((i = next_desc(vq, &desc)) != -1);
29052918

29062919
/* On success, increment avail index. */
29072920
vq->last_avail_idx++;
2921+
vq->next_avail_head += c;
29082922

29092923
/* Assume notifications from guest are disabled at this point,
29102924
* if they aren't we would need to update avail_event index. */
@@ -2928,8 +2942,9 @@ int vhost_add_used(struct vhost_virtqueue *vq, unsigned int head, int len)
29282942
cpu_to_vhost32(vq, head),
29292943
cpu_to_vhost32(vq, len)
29302944
};
2945+
u16 nheads = 1;
29312946

2932-
return vhost_add_used_n(vq, &heads, 1);
2947+
return vhost_add_used_n(vq, &heads, &nheads, 1);
29332948
}
29342949
EXPORT_SYMBOL_GPL(vhost_add_used);
29352950

@@ -2965,10 +2980,9 @@ static int __vhost_add_used_n(struct vhost_virtqueue *vq,
29652980
return 0;
29662981
}
29672982

2968-
/* After we've used one of their buffers, we tell them about it. We'll then
2969-
* want to notify the guest, using eventfd. */
2970-
int vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads,
2971-
unsigned count)
2983+
static int vhost_add_used_n_ooo(struct vhost_virtqueue *vq,
2984+
struct vring_used_elem *heads,
2985+
unsigned count)
29722986
{
29732987
int start, n, r;
29742988

@@ -2981,7 +2995,69 @@ int vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads,
29812995
heads += n;
29822996
count -= n;
29832997
}
2984-
r = __vhost_add_used_n(vq, heads, count);
2998+
return __vhost_add_used_n(vq, heads, count);
2999+
}
3000+
3001+
static int vhost_add_used_n_in_order(struct vhost_virtqueue *vq,
3002+
struct vring_used_elem *heads,
3003+
const u16 *nheads,
3004+
unsigned count)
3005+
{
3006+
vring_used_elem_t __user *used;
3007+
u16 old, new = vq->last_used_idx;
3008+
int start, i;
3009+
3010+
if (!nheads)
3011+
return -EINVAL;
3012+
3013+
start = vq->last_used_idx & (vq->num - 1);
3014+
used = vq->used->ring + start;
3015+
3016+
for (i = 0; i < count; i++) {
3017+
if (vhost_put_used(vq, &heads[i], start, 1)) {
3018+
vq_err(vq, "Failed to write used");
3019+
return -EFAULT;
3020+
}
3021+
start += nheads[i];
3022+
new += nheads[i];
3023+
if (start >= vq->num)
3024+
start -= vq->num;
3025+
}
3026+
3027+
if (unlikely(vq->log_used)) {
3028+
/* Make sure data is seen before log. */
3029+
smp_wmb();
3030+
/* Log used ring entry write. */
3031+
log_used(vq, ((void __user *)used - (void __user *)vq->used),
3032+
(vq->num - start) * sizeof *used);
3033+
if (start + count > vq->num)
3034+
log_used(vq, 0,
3035+
(start + count - vq->num) * sizeof *used);
3036+
}
3037+
3038+
old = vq->last_used_idx;
3039+
vq->last_used_idx = new;
3040+
/* If the driver never bothers to signal in a very long while,
3041+
* used index might wrap around. If that happens, invalidate
3042+
* signalled_used index we stored. TODO: make sure driver
3043+
* signals at least once in 2^16 and remove this. */
3044+
if (unlikely((u16)(new - vq->signalled_used) < (u16)(new - old)))
3045+
vq->signalled_used_valid = false;
3046+
return 0;
3047+
}
3048+
3049+
/* After we've used one of their buffers, we tell them about it. We'll then
3050+
* want to notify the guest, using eventfd. */
3051+
int vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads,
3052+
u16 *nheads, unsigned count)
3053+
{
3054+
bool in_order = vhost_has_feature(vq, VIRTIO_F_IN_ORDER);
3055+
int r;
3056+
3057+
if (!in_order || !nheads)
3058+
r = vhost_add_used_n_ooo(vq, heads, count);
3059+
else
3060+
r = vhost_add_used_n_in_order(vq, heads, nheads, count);
29853061

29863062
if (r < 0)
29873063
return r;
@@ -3064,9 +3140,11 @@ EXPORT_SYMBOL_GPL(vhost_add_used_and_signal);
30643140
/* multi-buffer version of vhost_add_used_and_signal */
30653141
void vhost_add_used_and_signal_n(struct vhost_dev *dev,
30663142
struct vhost_virtqueue *vq,
3067-
struct vring_used_elem *heads, unsigned count)
3143+
struct vring_used_elem *heads,
3144+
u16 *nheads,
3145+
unsigned count)
30683146
{
3069-
vhost_add_used_n(vq, heads, count);
3147+
vhost_add_used_n(vq, heads, nheads, count);
30703148
vhost_signal(dev, vq);
30713149
}
30723150
EXPORT_SYMBOL_GPL(vhost_add_used_and_signal_n);

drivers/vhost/vhost.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ struct vhost_virtqueue {
115115
* Values are limited to 0x7fff, and the high bit is used as
116116
* a wrap counter when using VIRTIO_F_RING_PACKED. */
117117
u16 last_avail_idx;
118+
/* Next avail ring head when VIRTIO_F_IN_ORDER is negoitated */
119+
u16 next_avail_head;
118120

119121
/* Caches available index value from user. */
120122
u16 avail_idx;
@@ -141,6 +143,7 @@ struct vhost_virtqueue {
141143
struct iovec iotlb_iov[64];
142144
struct iovec *indirect;
143145
struct vring_used_elem *heads;
146+
u16 *nheads;
144147
/* Protected by virtqueue mutex. */
145148
struct vhost_iotlb *umem;
146149
struct vhost_iotlb *iotlb;
@@ -235,11 +238,12 @@ bool vhost_vq_is_setup(struct vhost_virtqueue *vq);
235238
int vhost_vq_init_access(struct vhost_virtqueue *);
236239
int vhost_add_used(struct vhost_virtqueue *, unsigned int head, int len);
237240
int vhost_add_used_n(struct vhost_virtqueue *, struct vring_used_elem *heads,
238-
unsigned count);
241+
u16 *nheads, unsigned count);
239242
void vhost_add_used_and_signal(struct vhost_dev *, struct vhost_virtqueue *,
240243
unsigned int id, int len);
241244
void vhost_add_used_and_signal_n(struct vhost_dev *, struct vhost_virtqueue *,
242-
struct vring_used_elem *heads, unsigned count);
245+
struct vring_used_elem *heads, u16 *nheads,
246+
unsigned count);
243247
void vhost_signal(struct vhost_dev *, struct vhost_virtqueue *);
244248
void vhost_disable_notify(struct vhost_dev *, struct vhost_virtqueue *);
245249
bool vhost_vq_avail_empty(struct vhost_dev *, struct vhost_virtqueue *);

0 commit comments

Comments
 (0)