virtio: document functions better
Subsystems (13)
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/1 virtio: document functions better
The old documentation is left over from when we used a structure with strategy pointers.
--- include/linux/virtio.h | 130 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 87 insertions(+), 43 deletions(-)
include/linux/virtio.h +87 -43
@@ -26,52 +26,22 @@ struct virtqueue { };/** -* operations for virtqueue -* virtqueue_add_buf: expose buffer to other end -* vq: the struct virtqueue we're talking about. -* sg: the description of the buffer(s). -* out_num: the number of sg readable by other side -* in_num: the number of sg which are writable (after readable ones) -* data: the token identifying the buffer. -* gfp: how to do memory allocations (if necessary). -* Returns remaining capacity of queue (sg segments) or a negative error. -* virtqueue_kick: update after add_buf -* vq: the struct virtqueue -* After one or more add_buf calls, invoke this to kick the other side. -* virtqueue_get_buf: get the next used buffer -* vq: the struct virtqueue we're talking about. -* len: the length written into the buffer -* Returns NULL or the "data" token handed to add_buf. -* virtqueue_disable_cb: disable callbacks -* vq: the struct virtqueue we're talking about. -* Note that this is not necessarily synchronous, hence unreliable and only -* useful as an optimization. -* virtqueue_enable_cb: restart callbacks after disable_cb. -* vq: the struct virtqueue we're talking about. -* This re-enables callbacks; it returns "false" if there are pending -* buffers in the queue, to detect a possible race between the driver -* checking for more work, and enabling callbacks. -* virtqueue_enable_cb_delayed: restart callbacks after disable_cb. -* vq: the struct virtqueue we're talking about. -* This re-enables callbacks but hints to the other side to delay -* interrupts until most of the available buffers have been processed; -* it returns "false" if there are many pending buffers in the queue, -* to detect a possible race between the driver checking for more work, -* and enabling callbacks. -* virtqueue_detach_unused_buf: detach first unused buffer -* vq: the struct virtqueue we're talking about. -* Returns NULL or the "data" token handed to add_buf -* virtqueue_get_vring_size: return the size of the virtqueue's vring -* vq: the struct virtqueue containing the vring of interest. -* Returns the size of the vring. +* virtqueue_add_buf_gfp - expose buffer to other end +* @vq: the struct virtqueue we're talking about. +* @sg: the description of the buffer(s). +* @out_num: the number of sg readable by other side +* @in_num: the number of sg which are writable (after readable ones) +* @data: the token identifying the buffer. +* @gfp: how to do memory allocations (if necessary). * -* Locking rules are straightforward: the driver is responsible for -* locking. No two operations may be invoked simultaneously, with the exception -* of virtqueue_disable_cb. +* Caller must ensure we don't call this with other virtqueue operations +* at the same time (except where noted). * -* All operations can be called in any context. +* Returns remaining capacity of queue or a negative error +* (ie. ENOSPC). Note that it only really makes sense to treat all +* positive return values as "available": indirect buffers mean that +* we can put an entire sg[] array inside a single queue entry. */ - int virtqueue_add_buf_gfp(struct virtqueue *vq, struct scatterlist sg[], unsigned int out_num,
@@ -88,18 +58,92 @@ static inline int virtqueue_add_buf(strureturn virtqueue_add_buf_gfp (vq, sg, out_num, in_num, data,GFP_ATOMIC ); } +/** +* virtqueue_kick - update after add_buf +* @vq: the struct virtqueue +* +* After one or more virtqueue_add_buf_gfp calls, invoke this to kick +* the other side. +* +* Caller must ensure we don't call this with other virtqueue +* operations at the same time (except where noted). +*/ void virtqueue_kick (struct virtqueue *vq); +/** +* virtqueue_get_buf - get the next used buffer +* @vq: the struct virtqueue we're talking about. +* @len: the length written into the buffer +* +* If the driver wrote data into the buffer, @len will be set to the +* amount written. This means you don't need to clear the buffer +* beforehand to ensure there's no data leakage in the case of short +* writes. +* +* Caller must ensure we don't call this with other virtqueue +* operations at the same time (except where noted). +* +* Returns NULL if there are no used buffers, or the "data" token +* handed to virtqueue_add_buf_gfp(). +*/ void *virtqueue_get_buf (struct virtqueue *vq,unsigned int *len); +/** +* virtqueue_disable_cb - disable callbacks +* @vq: the struct virtqueue we're talking about. +* +* Note that this is not necessarily synchronous, hence unreliable and only +* useful as an optimization. +* +* Unlike other operations, this need not be serialized. +*/ void virtqueue_disable_cb (struct virtqueue *vq); +/** +* virtqueue_enable_cb - restart callbacks after disable_cb. +* @vq: the struct virtqueue we're talking about. +* +* This re-enables callbacks; it returns "false" if there are pending +* buffers in the queue, to detect a possible race between the driver +* checking for more work, and enabling callbacks. +* +* Caller must ensure we don't call this with other virtqueue +* operations at the same time (except where noted). +*/ bool virtqueue_enable_cb (struct virtqueue *vq); +/** +* virtqueue_enable_cb_delayed - restart callbacks after disable_cb. +* @vq: the struct virtqueue we're talking about. +* +* This re-enables callbacks but hints to the other side to delay +* interrupts until most of the available buffers have been processed; +* it returns "false" if there are many pending buffers in the queue, +* to detect a possible race between the driver checking for more work, +* and enabling callbacks. +* +* Caller must ensure we don't call this with other virtqueue +* operations at the same time (except where noted). +*/ bool virtqueue_enable_cb_delayed (struct virtqueue *vq); +/** +* virtqueue_detach_unused_buf - detach first unused buffer +* @vq: the struct virtqueue we're talking about. +* +* Returns NULL or the "data" token handed to virtqueue_add_buf_gfp(). +* This is not valid on an active queue; it is useful only for device +* shutdown. +*/ void *virtqueue_detach_unused_buf (struct virtqueue *vq); +/** +* virtqueue_get_vring_size - return the size of the virtqueue's vring +* @vq: the struct virtqueue containing the vring of interest. +* +* Returns the size of the vring. This is mainly used for boasting to +* userspace. Unlike other operations, this need not be serialized. +*/ unsigned int virtqueue_get_vring_size (struct virtqueue *vq);/**
Quoted 2 lines · Rusty Russell
The old documentation is left over from when we used a structure with strategy pointers.
Looks good,
PATCH 1/1 virtio: rename virtqueue_add_buf_gfp to virtqueue_add_buf
In reply to a message not in the archiveRemove wrapper functions. This makes the allocation type explicit in all callers; I used GPF_KERNEL where it seemed obvious, left it at GFP_ATOMIC otherwise.
drivers/block/virtio_blk.c +1 -1
@@ -171,7 +171,7 @@ static bool do_req(struct request_queue } } -if (virtqueue_add_buf (vblk->vq, vblk->sg, out, in, vbr) <0 ) { +if (virtqueue_add_buf (vblk->vq, vblk->sg, out, in, vbr,GFP_ATOMIC )<0 ) {mempool_free (vbr, vblk->pool);return false; }
drivers/char/hw_random/virtio-rng.c +1 -1
@@ -46,7 +46,7 @@ static void register_buffer(u8 *buf, sizsg_init_one (&sg, buf, size);/* There should always be room for one buffer. */ -if (virtqueue_add_buf (vq, &sg,0 ,1 , buf) <0 ) +if (virtqueue_add_buf (vq, &sg,0 ,1 , buf,GFP_KERNEL ) <0 )BUG ();virtqueue_kick (vq);
drivers/char/virtio_console.c +3 -3
@@ -391,7 +391,7 @@ static int add_inbuf(struct virtqueue *vsg_init_one (sg, buf->buf, buf->size); - ret =virtqueue_add_buf (vq, sg,0 ,1 , buf); + ret =virtqueue_add_buf (vq, sg,0 ,1 , buf,GFP_ATOMIC );virtqueue_kick (vq);return ret; }
@@ -456,7 +456,7 @@ static ssize_t __send_control_msg(struct vq = portdev->c_ovq;sg_init_one (sg, &cpkt,sizeof (cpkt)); -if (virtqueue_add_buf (vq, sg,1 ,0 , &cpkt) >=0 ) { +if (virtqueue_add_buf (vq, sg,1 ,0 , &cpkt,GFP_ATOMIC ) >=0 ) {virtqueue_kick (vq);while (!virtqueue_get_buf (vq, &len))cpu_relax ();
@@ -505,7 +505,7 @@ static ssize_t send_buf(struct port *porreclaim_consumed_buffers (port);sg_init_one (sg, in_buf, in_count); - ret =virtqueue_add_buf (out_vq, sg,1 ,0 , in_buf); + ret =virtqueue_add_buf (out_vq, sg,1 ,0 , in_buf,GFP_ATOMIC );/* Tell Host to go! */ virtqueue_kick (out_vq);
drivers/net/virtio_net.c +6 -6
@@ -368,7 +368,7 @@ static int add_recvbuf_small(struct virtskb_to_sgvec (skb, vi->rx_sg +1 ,0 , skb->len); - err =virtqueue_add_buf_gfp (vi->rvq, vi->rx_sg,0 ,2 , skb, gfp); + err =virtqueue_add_buf (vi->rvq, vi->rx_sg,0 ,2 , skb, gfp);if (err <0 )dev_kfree_skb (skb);
@@ -413,8 +413,8 @@ static int add_recvbuf_big(struct virtne/* chain first in list head */ first->private = (unsigned long )list; - err =virtqueue_add_buf_gfp (vi->rvq, vi->rx_sg,0 ,MAX_SKB_FRAGS +2 , - first, gfp); + err =virtqueue_add_buf (vi->rvq, vi->rx_sg,0 ,MAX_SKB_FRAGS +2 , + first, gfp);if (err <0 )give_pages (vi, first);
@@ -432,7 +432,7 @@ static int add_recvbuf_mergeable(structsg_init_one (vi->rx_sg,page_address (page),PAGE_SIZE ); - err =virtqueue_add_buf_gfp (vi->rvq, vi->rx_sg,0 ,1 , page, gfp); + err =virtqueue_add_buf (vi->rvq, vi->rx_sg,0 ,1 , page, gfp);if (err <0 )give_pages (vi, page);
@@ -601,7 +601,7 @@ static int xmit_skb(struct virtnet_info hdr->num_sg =skb_to_sgvec (skb, vi->tx_sg +1 ,0 , skb->len) +1 ;return virtqueue_add_buf (vi->svq, vi->tx_sg, hdr->num_sg, -0 , skb); +0 , skb,GFP_ATOMIC ); }static netdev_tx_t start_xmit (struct sk_buff *skb,struct net_device *dev)
@@ -754,7 +754,7 @@ static bool virtnet_send_command(structsg_set_buf (&sg[i +1 ],sg_virt (s), s->length);sg_set_buf (&sg[out + in -1 ], &status,sizeof (status)); -BUG_ON (virtqueue_add_buf (vi->cvq, sg, out, in, vi) <0 ); +BUG_ON (virtqueue_add_buf (vi->cvq, sg, out, in, vi,GFP_ATOMIC ) <0 );virtqueue_kick (vi->cvq);
drivers/virtio/virtio_balloon.c +4 -3
@@ -86,7 +86,7 @@ static void tell_host(struct virtio_ballinit_completion (&vb->acked);/* We should always be able to add one buffer to an empty queue. */ -if (virtqueue_add_buf (vq, &sg,1 ,0 , vb) <0 ) +if (virtqueue_add_buf (vq, &sg,1 ,0 , vb,GFP_KERNEL ) <0 )BUG ();virtqueue_kick (vq);
@@ -219,7 +219,7 @@ static void stats_handle_request(struct vq = vb->stats_vq;sg_init_one (&sg, vb->stats,sizeof (vb->stats)); -if (virtqueue_add_buf (vq, &sg,1 ,0 , vb) <0 ) +if (virtqueue_add_buf (vq, &sg,1 ,0 , vb,GFP_KERNEL ) <0 )BUG ();virtqueue_kick (vq); }
@@ -312,7 +312,8 @@ static int virtballoon_probe(struct virt* use it to signal us later. */ sg_init_one (&sg, vb->stats,sizeof vb->stats); -if (virtqueue_add_buf (vb->stats_vq, &sg,1 ,0 , vb) <0 ) +if (virtqueue_add_buf (vb->stats_vq, &sg,1 ,0 , vb,GFP_KERNEL ) + <0 )BUG ();virtqueue_kick (vb->stats_vq); }
drivers/virtio/virtio_ring.c +7 -7
@@ -159,12 +159,12 @@ static int vring_add_indirect(struct vrireturn head; } -int virtqueue_add_buf_gfp (struct virtqueue *_vq, -struct scatterlist sg[], -unsigned int out, -unsigned int in, -void *data, -gfp_t gfp) +int virtqueue_add_buf (struct virtqueue *_vq, +struct scatterlist sg[], +unsigned int out, +unsigned int in, +void *data, +gfp_t gfp) {struct vring_virtqueue *vq =to_vvq (_vq);unsigned int i, avail,uninitialized_var (prev );
@@ -235,7 +235,7 @@ add_head:return vq->num_free; } -EXPORT_SYMBOL_GPL (virtqueue_add_buf_gfp); +EXPORT_SYMBOL_GPL (virtqueue_add_buf);void virtqueue_kick (struct virtqueue *_vq) {
include/linux/virtio.h +10 -19
@@ -26,7 +26,7 @@ struct virtqueue { };/** -* virtqueue_add_buf_gfp - expose buffer to other end +* virtqueue_add_buf - expose buffer to other end * @vq: the struct virtqueue we're talking about. * @sg: the description of the buffer(s). * @out_num: the number of sg readable by other side
@@ -42,27 +42,18 @@ struct virtqueue {* positive return values as "available": indirect buffers mean that * we can put an entire sg[] array inside a single queue entry. */ -int virtqueue_add_buf_gfp (struct virtqueue *vq, -struct scatterlist sg[], -unsigned int out_num, -unsigned int in_num, -void *data, -gfp_t gfp); - -static inline int virtqueue_add_buf (struct virtqueue *vq, -struct scatterlist sg[], -unsigned int out_num, -unsigned int in_num, -void *data) -{ -return virtqueue_add_buf_gfp (vq, sg, out_num, in_num, data,GFP_ATOMIC ); -} +int virtqueue_add_buf (struct virtqueue *vq, +struct scatterlist sg[], +unsigned int out_num, +unsigned int in_num, +void *data, +gfp_t gfp);/** * virtqueue_kick - update after add_buf * @vq: the struct virtqueue * -* After one or more virtqueue_add_buf_gfp calls, invoke this to kick +* After one or more virtqueue_add_buf calls, invoke this to kick * the other side. * * Caller must ensure we don't call this with other virtqueue
@@ -84,7 +75,7 @@ void virtqueue_kick(struct virtqueue *vq* operations at the same time (except where noted). * * Returns NULL if there are no used buffers, or the "data" token -* handed to virtqueue_add_buf_gfp(). +* handed to virtqueue_add_buf(). */ void *virtqueue_get_buf (struct virtqueue *vq,unsigned int *len);
@@ -131,7 +122,7 @@ bool virtqueue_enable_cb_delayed(struct* virtqueue_detach_unused_buf - detach first unused buffer * @vq: the struct virtqueue we're talking about. * -* Returns NULL or the "data" token handed to virtqueue_add_buf_gfp(). +* Returns NULL or the "data" token handed to virtqueue_add_buf(). * This is not valid on an active queue; it is useful only for device * shutdown. */
net/9p/trans_virtio.c +4 -2
@@ -270,7 +270,8 @@ req_retry: in =pack_sg_list (chan->sg, out,VIRTQUEUE_NUM , req->rc->sdata, req->rc->capacity); - err =virtqueue_add_buf (chan->vq, chan->sg, out, in, req->tc); + err =virtqueue_add_buf (chan->vq, chan->sg, out, in, req->tc, +GFP_ATOMIC );if (err <0 ) {if (err == -ENOSPC ) { chan->ring_bufs_avail =0 ;
@@ -413,7 +414,8 @@ req_retry_pinned: in +=pack_sg_list_p (chan->sg, out + in,VIRTQUEUE_NUM , in_pages, in_nr_pages, uidata, inlen); - err =virtqueue_add_buf (chan->vq, chan->sg, out, in, req->tc); + err =virtqueue_add_buf (chan->vq, chan->sg, out, in, req->tc, +GFP_ATOMIC );if (err <0 ) {if (err == -ENOSPC ) { chan->ring_bufs_avail =0 ;
tools/virtio/linux/virtio.h +6 -15
@@ -186,21 +186,12 @@ struct virtqueue {#endif /* Interfaces exported by virtio_ring. */ -int virtqueue_add_buf_gfp (struct virtqueue *vq, -struct scatterlist sg[], -unsigned int out_num, -unsigned int in_num, -void *data, -gfp_t gfp); - -static inline int virtqueue_add_buf (struct virtqueue *vq, -struct scatterlist sg[], -unsigned int out_num, -unsigned int in_num, -void *data) -{ -return virtqueue_add_buf_gfp (vq, sg, out_num, in_num, data,GFP_ATOMIC ); -} +int virtqueue_add_buf (struct virtqueue *vq, +struct scatterlist sg[], +unsigned int out_num, +unsigned int in_num, +void *data, +gfp_t gfp);void virtqueue_kick (struct virtqueue *vq);
tools/virtio/virtio_test.c +2 -1
@@ -160,7 +160,8 @@ static void run_test(struct vdev_info *dif (started < bufs) {sg_init_one (&sl, dev->buf, dev->buf_size); r =virtqueue_add_buf (vq->vq, &sl,1 ,0 , - dev->buf + started); + dev->buf + started, +GFP_ATOMIC );if (likely (r >=0 )) { ++started;virtqueue_kick (vq->vq);
Re: [PATCH 2 of 5] virtio: rename virtqueue_add_buf_gfp to virtqueue_add_buf
Quoted 3 lines · Rusty Russell
Remove wrapper functions. This makes the allocation type explicit in all callers; I used
GPF_KERNELwhere it seemed obvious, left it atGFP_ATOMICotherwise.
Looks good,
PATCH 1/1 virtio: support unlocked queue kick
In reply to a message not in the archiveBased on patch by Christoph for virtio_blk speedup:
Split virtqueue_kick to be able to do the actual notification outside the lock protecting the virtqueue. This patch was originally done by Stefan Hajnoczi, but I can't find the original one anymore and had to recreated it from memory. Pointers to the original or corrections for the commit message are welcome.
drivers/virtio/virtio_ring.c +26 -7
@@ -237,10 +237,12 @@ add_head: }EXPORT_SYMBOL_GPL (virtqueue_add_buf); -void virtqueue_kick (struct virtqueue *_vq) +bool virtqueue_kick_prepare (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq);u16 new, old; +bool needs_kick; +START_USE (vq);/* Descriptors and available array need to be set before we expose the * new available array entries. */
@@ -253,13 +255,30 @@ void virtqueue_kick(struct virtqueue *_v/* Need to update avail index before checking if we should notify */ virtio_mb (); -if (vq->event ? -vring_need_event (vring_avail_event (&vq->vring), new, old) : - !(vq->vring.used->flags &VRING_USED_F_NO_NOTIFY )) -/* Prod other side to tell it about changes. */ - vq->notify (&vq->vq); +if (vq->event) { + needs_kick =vring_need_event (vring_avail_event (&vq->vring), + new, old); + }else { + needs_kick = (!(vq->vring.used->flags &VRING_USED_F_NO_NOTIFY )); + } +END_USE (vq); +return needs_kick; +} +EXPORT_SYMBOL_GPL (virtqueue_kick_prepare); -END_USE (vq); +void virtqueue_notify (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +/* Prod other side to tell it about changes. */ + vq->notify (_vq); +} +EXPORT_SYMBOL_GPL (virtqueue_notify); + +void virtqueue_kick (struct virtqueue *vq) +{ +if (virtqueue_kick_prepare (vq)) +virtqueue_notify (vq); }EXPORT_SYMBOL_GPL (virtqueue_kick);
include/linux/virtio.h +21 -0
@@ -62,6 +62,27 @@ int virtqueue_add_buf(struct virtqueue *void virtqueue_kick (struct virtqueue *vq);/** +* virtqueue_kick_prepare - first half of split virtqueue_kick call. +* @vq: the struct virtqueue +* +* Instead of virtqueue_kick(), you can do: +* if (virtqueue_kick_prepare(vq)) +* virtqueue_notify(vq); +* +* This is sometimes useful because the virtqueue_kick_prepare() needs +* to be serialized, but the actual virtqueue_notify() call does not. +*/ +bool virtqueue_kick_prepare (struct virtqueue *vq); + +/** +* virtqueue_notify - second half of split virtqueue_kick call. +* @vq: the struct virtqueue +* +* This does not need to be serialized. +*/ +void virtqueue_notify (struct virtqueue *vq); + +/** * virtqueue_get_buf - get the next used buffer * @vq: the struct virtqueue we're talking about. * @len: the length written into the buffer
Re: [PATCH 3 of 5] virtio: support unlocked queue kick
Quoted 1 line · Rusty Russell
Based on patch by Christoph for
virtio_blkspeedup:
Please credit it to Stefan - he also sent a pointer to his original version in reply to the previous thread.
Also shouldn't virtqueue_kick have kerneldoc comments?
I also notices that you documented the functions bother here and in the first patch in the headers. At least historically the kerneldoc tools didn't parse comments at declarations, but only at the function defintions. Did you check these actually get picked up?
Re: [PATCH 3 of 5] virtio: support unlocked queue kick
In reply to Rusty RussellQuoted 1 line · Rusty Russell
Based on patch by Christoph for
virtio_blkspeedup:
Please credit it to Stefan - he also sent a pointer to his original version in reply to the previous thread.
Also shouldn't virtqueue_kick have kerneldoc comments?
I also notices that you documented the functions bother here and in the first patch in the headers. At least historically the kerneldoc tools didn't parse comments at declarations, but only at the function defintions. Did you check these actually get picked up?
Re: [PATCH 3 of 5] virtio: support unlocked queue kick
Quoted 5 lines · Christoph Hellwig
Quoted 1 line · Rusty Russell
Based on patch by Christoph for
virtio_blkspeedup:Please credit it to Stefan - he also sent a pointer to his original version in reply to the previous thread.
Thank you Christoph. Here the pointer to the original mailing list thread:
"Here is the patch:
https://github.com/stefanha/linux/commit/a6d06644e3a58e57a774e77d7dc34c4a5a2e7496
Or as an email if you want to track it down in your inbox:
http://www.spinics.net/lists/linux-virtualization/msg14616.html"
Stefan
Re: [PATCH 3 of 5] virtio: support unlocked queue kick
In reply to Christoph HellwigQuoted 7 lines · Christoph Hellwig
Quoted 1 line · Rusty Russell
Based on patch by Christoph for
virtio_blkspeedup:Please credit it to Stefan - he also sent a pointer to his original version in reply to the previous thread.
Also shouldn't
virtqueue_kickhave kerneldoc comments?
Yep, and I've credited it properly.
Quoted 4 lines
I also notices that you documented the functions bother here and in the first patch in the headers. At least historically the kerneldoc tools didn't parse comments at declarations, but only at the function defintions. Did you check these actually get picked up?
Gah, I'd forgotten that the kernel tendency is to put the interface documentation next to the implementation.
Personally, I think extracting it is insane, but I've moved them.
Thanks,
Rusty.
PATCH 1/1 virtio: avoid modulus operation
In reply to a message not in the archiveSince we know vq->vring.num is a power of 2, modulus is lazy (it's asserted in vring_new_virtqueue()).
--- drivers/virtio/virtio_ring.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
arch/x86/lguest/boot.c +1 -1
@@ -1420,7 +1420,7 @@ __init void lguest_init(void)add_preferred_console ("hvc" ,0 ,NULL );/* Register our very early console. */ -virtio_cons_early_init (early_put_chars); +// virtio_cons_early_init(early_put_chars); /* * Last of all, we set the power management poweroff hook to point to
drivers/virtio/virtio_ring.c +6 -4
@@ -226,8 +226,8 @@ add_head: vq->data[head] = data;/* Put entry in available array (but don't update avail->idx until they -* do sync). FIXME: avoid modulus here? */ - avail = (vq->vring.avail->idx + vq->num_added++) % vq->vring.num; +* do sync). */ + avail = ((vq->vring.avail->idx + vq->num_added++) & (vq->vring.num-1 )); vq->vring.avail->ring[avail] = head;pr_debug ("Added buffer head %i to %p\n" , head, vq);
@@ -317,6 +317,7 @@ void *virtqueue_get_buf(struct virtqueuestruct vring_virtqueue *vq =to_vvq (_vq);void *ret;unsigned int i; +u16 last_used;START_USE (vq);
@@ -334,8 +335,9 @@ void *virtqueue_get_buf(struct virtqueue/* Only get used array entries after they have been exposed by host. */ virtio_rmb (); - i = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].id; - *len = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].len; + last_used = (vq->last_used_idx & (vq->vring.num -1 )); + i = vq->vring.used->ring[last_used].id; + *len = vq->vring.used->ring[last_used].len;if (unlikely (i >= vq->vring.num)) {BAD_RING (vq,"id %u out of range\n" , i);
Re: [PATCH 4 of 5] virtio: avoid modulus operation
Quoted 12 lines · Rusty Russell
Since we know
vq->vring.numis a power of 2, modulus is lazy (it's asserted invring_new_virtqueue()).Signed-off-by Rusty Russell <[email protected]>---
drivers/virtio/virtio_ring.c | 10 ++++++----1 file changed, 6 insertions(+), 4 deletions(-)
arch/x86/lguest/boot.c __init void lguest_init(void) · line 1423
Quoted 5 lines
arch/x86/lguest/boot.c +1
-1@@ -1420,7 +1420,7 @@ __init void lguest_init(void) add_preferred_console("hvc", 0, NULL); /* Register our very early console. */ - virtio_cons_early_init(early_put_chars); +// virtio_cons_early_init(early_put_chars);
What's this hunk here?
Re: [PATCH 4 of 5] virtio: avoid modulus operation
Quoted 15 lines · Pekka Enberg
Quoted 14 lines · Rusty Russell
Since we know
vq->vring.numis a power of 2, modulus is lazy (it's asserted invring_new_virtqueue()).Signed-off-by Rusty Russell <[email protected]>---
drivers/virtio/virtio_ring.c | 10 ++++++----1 file changed, 6 insertions(+), 4 deletions(-)arch/x86/lguest/boot.c +0
-0@@ -1420,7 +1420,7 @@ __init void lguest_init(void) add_preferred_console("hvc", 0, NULL);
Quoted 5 lines
arch/x86/lguest/boot.c __init void lguest_init(void) · line 1423
Quoted 3 lines
arch/x86/lguest/boot.c +1
-1/* Register our very early console. */ - virtio_cons_early_init(early_put_chars); +// virtio_cons_early_init(early_put_chars); What's this hunk here?
Ugly workaround for console until hvc_console breakage gets reverted.
Left in by mistake :)
Thanks,
Rusty.
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Re: [PATCH 4 of 5] virtio: avoid modulus operation
In reply to Pekka EnbergQuoted 15 lines · Pekka Enberg
Quoted 14 lines · Rusty Russell
Since we know
vq->vring.numis a power of 2, modulus is lazy (it's asserted invring_new_virtqueue()).Signed-off-by Rusty Russell <[email protected]>---
drivers/virtio/virtio_ring.c | 10 ++++++----1 file changed, 6 insertions(+), 4 deletions(-)arch/x86/lguest/boot.c +0
-0@@ -1420,7 +1420,7 @@ __init void lguest_init(void) add_preferred_console("hvc", 0, NULL);
Quoted 5 lines
arch/x86/lguest/boot.c __init void lguest_init(void) · line 1423
Quoted 3 lines
arch/x86/lguest/boot.c +1
-1/* Register our very early console. */ - virtio_cons_early_init(early_put_chars); +// virtio_cons_early_init(early_put_chars); What's this hunk here?
Ugly workaround for console until hvc_console breakage gets reverted.
Left in by mistake :)
Thanks,
Rusty.
Re: [PATCH 4 of 5] virtio: avoid modulus operation
In reply to Rusty RussellQuoted 12 lines · Rusty Russell
Since we know
vq->vring.numis a power of 2, modulus is lazy (it's asserted invring_new_virtqueue()).Signed-off-by Rusty Russell <[email protected]>---
drivers/virtio/virtio_ring.c | 10 ++++++----1 file changed, 6 insertions(+), 4 deletions(-)
arch/x86/lguest/boot.c __init void lguest_init(void) · line 1423
Quoted 5 lines
arch/x86/lguest/boot.c +1
-1@@ -1420,7 +1420,7 @@ __init void lguest_init(void) add_preferred_console("hvc", 0, NULL); /* Register our very early console. */ - virtio_cons_early_init(early_put_chars); +// virtio_cons_early_init(early_put_chars);
What's this hunk here?
PATCH 1/1 virtio: expose added descriptors immediately
In reply to a message not in the archiveA virtio driver does virtqueue_add_buf() multiple times before finally calling virtqueue_kick(); previously we only exposed the added buffers in the virtqueue_kick() call. This means we don't need a memory barrier in virtqueue_add_buf(), but it reduces concurrency as the device (ie. host) can't see the buffers until the kick.
--- drivers/virtio/virtio_ring.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-)
drivers/virtio/virtio_ring.c +9 -6
@@ -227,9 +227,15 @@ add_head:/* Put entry in available array (but don't update avail->idx until they * do sync). */ - avail = ((vq->vring.avail->idx + vq->num_added++) & (vq->vring.num-1 )); + avail = (vq->vring.avail->idx & (vq->vring.num-1 )); vq->vring.avail->ring[avail] = head; +/* Descriptors and available array need to be set before we expose the +* new available array entries. */ +virtio_wmb (); + vq->vring.avail->idx++; + vq->num_added++; +pr_debug ("Added buffer head %i to %p\n" , head, vq);END_USE (vq);
@@ -248,13 +254,10 @@ bool virtqueue_kick_prepare(struct virtq* new available array entries. */ virtio_wmb (); - old = vq->vring.avail->idx; - new = vq->vring.avail->idx = old + vq->num_added; + old = vq->vring.avail->idx - vq->num_added; + new = vq->vring.avail->idx; vq->num_added =0 ; -/* Need to update avail index before checking if we should notify */ -virtio_mb (); -if (vq->event) { needs_kick =vring_need_event (vring_avail_event (&vq->vring), new, old);
Michael S. Tsirkin
maintainerRe: [PATCH 5 of 5] virtio: expose added descriptors immediately
Quoted 7 lines · Rusty Russell
A virtio driver does
virtqueue_add_buf()multiple times before finally callingvirtqueue_kick(); previously we only exposed the added buffers in thevirtqueue_kick()call. This means we don't need a memory barrier invirtqueue_add_buf(), but it reduces concurrency as the device (ie. host) can't see the buffers until the kick.Signed-off-by Rusty Russell <[email protected]>
In the past I played with a patch like this, but I didn't see a performance gain either way. Do you see any gain?
I'm a bit concerned that with this patch, a buggy driver that adds more than 2^16 descriptors without a kick
would seem to work sometimes. Let's add WARN_ON(vq->num_added > (1 << 16))?
Quoted 46 lines
--- drivers/virtio/virtio_ring.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-)drivers/virtio/virtio_ring.c +9
-6@@ -227,9 +227,15 @@ add_head:/* Put entry in available array (but don't update avail->idx until they * do sync). */ - avail = ((vq->vring.avail->idx + vq->num_added++) & (vq->vring.num-1 )); + avail = (vq->vring.avail->idx & (vq->vring.num-1 )); vq->vring.avail->ring[avail] = head; +/* Descriptors and available array need to be set before we expose the +* new available array entries. */ +virtio_wmb (); + vq->vring.avail->idx++; + vq->num_added++; +pr_debug ("Added buffer head %i to %p\n" , head, vq);END_USE (vq);@@ -248,13 +254,10 @@ bool virtqueue_kick_prepare(struct virtq* new available array entries. */ virtio_wmb (); - old = vq->vring.avail->idx; - new = vq->vring.avail->idx = old + vq->num_added; + old = vq->vring.avail->idx - vq->num_added; + new = vq->vring.avail->idx; vq->num_added =0 ; -/* Need to update avail index before checking if we should notify */ -virtio_mb (); -if (vq->event) { needs_kick =vring_need_event (vring_avail_event (&vq->vring), new, old);_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Re: [PATCH 5 of 5] virtio: expose added descriptors immediately
Quoted 11 lines · Michael S. Tsirkin
Quoted 7 lines · Rusty Russell
A virtio driver does
virtqueue_add_buf()multiple times before finally callingvirtqueue_kick(); previously we only exposed the added buffers in thevirtqueue_kick()call. This means we don't need a memory barrier invirtqueue_add_buf(), but it reduces concurrency as the device (ie. host) can't see the buffers until the kick.Signed-off-by Rusty Russell <[email protected]>In the past I played with a patch like this, but I didn't see a performance gain either way. Do you see any gain?
No, but I haven't run it on real hardware. lguest may see a win with this in theory, since the virtqueue processing is fully async, so I'll run some tests.
Quoted 3 lines
I'm a bit concerned that with this patch, a buggy driver that adds more than 2^16 descriptors without a kick
would seem to work sometimes. Let's addWARN_ON(vq->num_added > (1 << 16))?
Hmm, I guess it could wait for the add to fail before doing a kick, but noone does that at the moment, so I've added a slight variant:
WARN_ON(vq->num_added > vq->vring.num);
Thanks,
Rusty.
Michael S. Tsirkin
maintainerRe: [PATCH 5 of 5] virtio: expose added descriptors immediately
In reply to Michael S. TsirkinQuoted 7 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell
A virtio driver does
virtqueue_add_buf()multiple times before finally callingvirtqueue_kick(); previously we only exposed the added buffers in thevirtqueue_kick()call. This means we don't need a memory barrier invirtqueue_add_buf(), but it reduces concurrency as the device (ie. host) can't see the buffers until the kick.
Quoted 8 lines
Quoted 1 line
Signed-off-by Rusty Russell <[email protected]>In the past I played with a patch like this, but I didn't see a performance gain either way. Do you see any gain?
I'm a bit concerned that with this patch, a buggy driver that adds more than 2^16 descriptors without a kick
would seem to work sometimes. Let's addWARN_ON(vq->num_added > (1 << 16))?
Thinking about it more - it might be tricky for drivers to ensure this. add used to fail when vq is full, but now driver might do get between add and notify:
lock
add_buf * N
prep
unlock
lock
get_buf * N
unlock
lock
add_buf
prep
unlock
notify
and since add was followed by get, this doesn't fail.
So the right thing to do I think is to either ignore indexes and assume a kick is needed, something like:
if vq->num_added >= (1 << 15))
needs_kick = true
(note: maybe it's 1<<16, and maybe >, but 1<<15 is plenty anyway)
Or alternatively, fail add when num_added is too large.
Quoted 46 lines
Quoted 46 lines
--- drivers/virtio/virtio_ring.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-)drivers/virtio/virtio_ring.c +9
-6@@ -227,9 +227,15 @@ add_head:/* Put entry in available array (but don't update avail->idx until they * do sync). */ - avail = ((vq->vring.avail->idx + vq->num_added++) & (vq->vring.num-1 )); + avail = (vq->vring.avail->idx & (vq->vring.num-1 )); vq->vring.avail->ring[avail] = head; +/* Descriptors and available array need to be set before we expose the +* new available array entries. */ +virtio_wmb (); + vq->vring.avail->idx++; + vq->num_added++; +pr_debug ("Added buffer head %i to %p\n" , head, vq);END_USE (vq);@@ -248,13 +254,10 @@ bool virtqueue_kick_prepare(struct virtq* new available array entries. */ virtio_wmb (); - old = vq->vring.avail->idx; - new = vq->vring.avail->idx = old + vq->num_added; + old = vq->vring.avail->idx - vq->num_added; + new = vq->vring.avail->idx; vq->num_added =0 ; -/* Need to update avail index before checking if we should notify */ -virtio_mb (); -if (vq->event) { needs_kick =vring_need_event (vring_avail_event (&vq->vring), new, old);_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Re: [PATCH 5 of 5] virtio: expose added descriptors immediately
Quoted 33 lines · Michael S. Tsirkin
Quoted 7 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell
A virtio driver does
virtqueue_add_buf()multiple times before finally callingvirtqueue_kick(); previously we only exposed the added buffers in thevirtqueue_kick()call. This means we don't need a memory barrier invirtqueue_add_buf(), but it reduces concurrency as the device (ie. host) can't see the buffers until the kick.Quoted 8 lines
Quoted 1 line
Signed-off-by Rusty Russell <[email protected]>In the past I played with a patch like this, but I didn't see a performance gain either way. Do you see any gain?
I'm a bit concerned that with this patch, a buggy driver that adds more than 2^16 descriptors without a kick
would seem to work sometimes. Let's addWARN_ON(vq->num_added > (1 << 16))?Thinking about it more - it might be tricky for drivers to ensure this. add used to fail when vq is full, but now driver might do get between add and notify:
lock
add_buf* N
prep
unlock
lock
get_buf* N
unlock
lock
add_buf
prep
unlock
notify
Quoted 1 line
and since add was followed by get, this doesn't fail.
Right, the driver could, in theory, do:
add_buf()
if (!get_buf())
notify()But we don't allow that at the moment in our API: we insist on a notify occasionally. Noone does this at the moment, so a WARN_ON is correct.
If you're just add_buf() without the get_buf() then add_buf() will fail already.
Here's my current variant:
drivers/virtio/virtio_ring.c +11 -1
@@ -245,9 +245,19 @@ add_head:/* Put entry in available array (but don't update avail->idx until they * do sync). */ - avail = ((vq->vring.avail->idx + vq->num_added++) & (vq->vring.num-1 )); + avail = (vq->vring.avail->idx & (vq->vring.num-1 )); vq->vring.avail->ring[avail] = head; +/* Descriptors and available array need to be set before we expose the +* new available array entries. */ +virtio_wmb (); + vq->vring.avail->idx++; + vq->num_added++; + +/* If you haven't kicked in this long, you're probably doing something +* wrong. */ +WARN_ON (vq->num_added > vq->vring.num); +pr_debug ("Added buffer head %i to %p\n" , head, vq);END_USE (vq);
It's hard to write a useful WARN_ON() for the "you should kick more regularly" case (we could take timestamps if DEBUG is defined, I guess), so let's leave this until someone actually trips it.
Thanks,
Rusty.
Michael S. Tsirkin
maintainerRe: [PATCH 5 of 5] virtio: expose added descriptors immediately
Quoted 77 lines · Rusty Russell
Quoted 33 lines · Michael S. Tsirkin
Quoted 7 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell
A virtio driver does
virtqueue_add_buf()multiple times before finally callingvirtqueue_kick(); previously we only exposed the added buffers in thevirtqueue_kick()call. This means we don't need a memory barrier invirtqueue_add_buf(), but it reduces concurrency as the device (ie. host) can't see the buffers until the kick.Quoted 8 lines
Quoted 1 line
Signed-off-by Rusty Russell <[email protected]>In the past I played with a patch like this, but I didn't see a performance gain either way. Do you see any gain?
I'm a bit concerned that with this patch, a buggy driver that adds more than 2^16 descriptors without a kick
would seem to work sometimes. Let's addWARN_ON(vq->num_added > (1 << 16))?Thinking about it more - it might be tricky for drivers to ensure this. add used to fail when vq is full, but now driver might do get between add and notify:
lock
add_buf* N
prep
unlock
lock
get_buf* N
unlock
lock
add_buf
prep
unlock
notifyQuoted 1 line
and since add was followed by get, this doesn't fail.
Right, the driver could, in theory, do:
add_buf() if (!get_buf()) notify()But we don't allow that at the moment in our API: we insist on a notify occasionally. Noone does this at the moment, so a
WARN_ONis correct.If you're just
add_buf()without theget_buf()thenadd_buf()will fail already.Here's my current variant:
drivers/virtio/virtio_ring.c +11
-1@@ -245,9 +245,19 @@ add_head:/* Put entry in available array (but don't update avail->idx until they * do sync). */ - avail = ((vq->vring.avail->idx + vq->num_added++) & (vq->vring.num-1 )); + avail = (vq->vring.avail->idx & (vq->vring.num-1 )); vq->vring.avail->ring[avail] = head; +/* Descriptors and available array need to be set before we expose the +* new available array entries. */ +virtio_wmb (); + vq->vring.avail->idx++; + vq->num_added++; + +/* If you haven't kicked in this long, you're probably doing something +* wrong. */ +WARN_ON (vq->num_added > vq->vring.num); +pr_debug ("Added buffer head %i to %p\n" , head, vq);END_USE (vq);It's hard to write a useful
WARN_ON()for the "you should kick more regularly" case (we could take timestamps if DEBUG is defined, I guess), so let's leave this until someone actually trips it.
Quoted 2 lines
Thanks,
Rusty.
My unlocked kick patches will trip this warning: they make virtio-net do add + get without kick.
I think block with unlocked kick can trip it too:
add, lock is dropped and then an interrupt can get.
We also don't need a kick each num - each 2^15 is enough. Why don't we do this at start of add_buf:
if (vq->num_added >= 0x7fff)
return -ENOSPC;
Signature · 1 line
MST
Re: [PATCH 5 of 5] virtio: expose added descriptors immediately
Quoted 2 lines · Michael S. Tsirkin
My unlocked kick patches will trip this warning: they make virtio-net do add + get without kick.
Heh, it's a good sign if they do, since that means you're running really well :)
Quoted 7 lines
I think block with unlocked kick can trip it too:
add, lock is dropped and then an interrupt can get.We also don't need a kick each num - each 2^15 is enough. Why don't we do this at start of
add_buf:
if (vq->num_added>=0x7fff)return -ENOSPC;
The warning was there in case a driver is never doing a kick, and getting away with it (mostly) because the device is polling. Let's not penalize good drivers to catch bad ones.
How about we do this properly, like so:
From: Rusty Russell <[email protected]>
Subject: virtio: add debugging if driver doesn't kick.
Under the existing #ifdef DEBUG, check that they don't have more than 1/10 of a second between an add_buf() and a virtqueue_notify()/virtqueue_kick_prepare() call.
We could get false positives on a really busy system, but good for development.
--- drivers/virtio/virtio_ring.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
drivers/virtio/virtio_ring.c +31 -0
@@ -22,6 +22,7 @@#include <linux/device.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/hrtimer.h> /* virtio guest is communicating with a virtual "device" that actually runs on * a host processor. Memory barriers are used to control SMP effects. */
@@ -102,6 +103,10 @@ struct vring_virtqueue#ifdef DEBUG /* They're supposed to lock for us. */ unsigned int in_use; + +/* Figure out if their kicks are too delayed. */ +bool last_add_time_valid; +ktime_t last_add_time;#endif /* Tokens for callbacks. */
@@ -192,6 +197,19 @@ int virtqueue_add_buf(struct virtqueue *BUG_ON (data ==NULL ); +#ifdef DEBUG + { +ktime_t now =ktime_get (); + +/* No kick or get, with .1 second between? Warn. */ +if (vq->last_add_time_valid) +WARN_ON (ktime_to_ms (ktime_sub (now, vq->last_add_time)) + >100 ); + vq->last_add_time = now; + vq->last_add_time_valid = true; + } +#endif +/* If the host supports indirect descriptor tables, and we have multiple * buffers, then go indirect. FIXME: tune this threshold */ if (vq->indirect && (out + in) >1 && vq->num_free) {
@@ -291,6 +309,14 @@ bool virtqueue_kick_prepare(struct virtq new = vq->vring.avail->idx; vq->num_added =0 ; +#ifdef DEBUG +if (vq->last_add_time_valid) { +WARN_ON (ktime_to_ms (ktime_sub (ktime_get (), + vq->last_add_time)) >100 ); + } + vq->last_add_time_valid = false; +#endif +if (vq->event) { needs_kick =vring_need_event (vring_avail_event (&vq->vring), new, old);
@@ -428,6 +454,10 @@ void *virtqueue_get_buf(struct virtqueuevirtio_mb (); } +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif +END_USE (vq);return ret; }
@@ -611,6 +641,7 @@ struct virtqueue *vring_new_virtqueue(unlist_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG vq->in_use = false; + vq->last_add_time_valid = false;#endif vq->indirect =virtio_has_feature (vdev,VIRTIO_RING_F_INDIRECT_DESC );
Michael S. Tsirkin
maintainerRe: [PATCH 5 of 5] virtio: expose added descriptors immediately
Quoted 6 lines · Rusty Russell
Quoted 2 lines · Michael S. Tsirkin
My unlocked kick patches will trip this warning: they make virtio-net do add + get without kick.
Heh, it's a good sign if they do, since that means you're running really well :)
They don't in fact, in my testing :(. But I think they can with luck.
Quoted 8 lines
Quoted 7 lines
I think block with unlocked kick can trip it too:
add, lock is dropped and then an interrupt can get.We also don't need a kick each num - each 2^15 is enough. Why don't we do this at start of
add_buf:
if (vq->num_added>=0x7fff)return -ENOSPC;
Quoted 5 lines
The warning was there in case a driver is never doing a kick, and getting away with it (mostly) because the device is polling. Let's not penalize good drivers to catch bad ones.
How about we do this properly, like so:
Absolutely. But I think we also need to handle num_added overflow of a 15 bit counter, no? Otherwise the vring_need_event logic might give us false negatives .... I'm guessing we can just assume we need a kick in that case.
Quoted 91 lines
From: Rusty Russell <[email protected]>
Subject: virtio: add debugging if driver doesn't kick.Under the existing #ifdef DEBUG, check that they don't have more than 1/10 of a second between an
add_buf()and a virtqueue_notify()/virtqueue_kick_prepare() call.We could get false positives on a really busy system, but good for development.
Signed-off-by Rusty Russell <[email protected]>--- drivers/virtio/virtio_ring.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)drivers/virtio/virtio_ring.c +31
-0@@ -22,6 +22,7 @@#include <linux/device.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/hrtimer.h> /* virtio guest is communicating with a virtual "device" that actually runs on * a host processor. Memory barriers are used to control SMP effects. */ @@ -102,6 +103,10 @@ struct vring_virtqueue#ifdef DEBUG /* They're supposed to lock for us. */ unsigned int in_use; + +/* Figure out if their kicks are too delayed. */ +bool last_add_time_valid; +ktime_t last_add_time;#endif /* Tokens for callbacks. */ @@ -192,6 +197,19 @@ int virtqueue_add_buf(struct virtqueue *BUG_ON (data ==NULL ); +#ifdef DEBUG + { +ktime_t now =ktime_get (); + +/* No kick or get, with .1 second between? Warn. */ +if (vq->last_add_time_valid) +WARN_ON (ktime_to_ms (ktime_sub (now, vq->last_add_time)) + >100 ); + vq->last_add_time = now; + vq->last_add_time_valid = true; + } +#endif +/* If the host supports indirect descriptor tables, and we have multiple * buffers, then go indirect. FIXME: tune this threshold */ if (vq->indirect && (out + in) >1 && vq->num_free) {@@ -291,6 +309,14 @@ bool virtqueue_kick_prepare(struct virtq new = vq->vring.avail->idx; vq->num_added =0 ; +#ifdef DEBUG +if (vq->last_add_time_valid) { +WARN_ON (ktime_to_ms (ktime_sub (ktime_get (), + vq->last_add_time)) >100 ); + } + vq->last_add_time_valid = false; +#endif +if (vq->event) { needs_kick =vring_need_event (vring_avail_event (&vq->vring), new, old);@@ -428,6 +454,10 @@ void *virtqueue_get_buf(struct virtqueuevirtio_mb (); } +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif +END_USE (vq);return ret; }@@ -611,6 +641,7 @@ struct virtqueue *vring_new_virtqueue(unlist_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG vq->in_use = false; + vq->last_add_time_valid = false;#endif vq->indirect =virtio_has_feature (vdev,VIRTIO_RING_F_INDIRECT_DESC );
Re: [PATCH 5 of 5] virtio: expose added descriptors immediately
Quoted 22 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell
Quoted 2 lines · Michael S. Tsirkin
My unlocked kick patches will trip this warning: they make virtio-net do add + get without kick.
Heh, it's a good sign if they do, since that means you're running really well :)
They don't in fact, in my testing :(. But I think they can with luck.
Quoted 12 lines
Quoted 7 lines
I think block with unlocked kick can trip it too:
add, lock is dropped and then an interrupt can get.We also don't need a kick each num - each 2^15 is enough. Why don't we do this at start of
add_buf:
if (vq->num_added>=0x7fff)return -ENOSPC;The warning was there in case a driver is never doing a kick, and getting away with it (mostly) because the device is polling. Let's not penalize good drivers to catch bad ones.
Quoted 6 lines
Quoted 1 line
How about we do this properly, like so:
Absolutely. But I think we also need to handle
num_addedoverflow of a 15 bit counter, no? Otherwise thevring_need_eventlogic might give us false negatives .... I'm guessing we can just assume we need a kick in that case.
You're right. Thankyou. My immediate reaction of "make it an unsigned long" doesn't work.
Here's the diff to what I posted before:
drivers/virtio/virtio_ring.c +4 -3
@@ -254,9 +254,10 @@ add_head: vq->vring.avail->idx++; vq->num_added++; -/* If you haven't kicked in this long, you're probably doing something -* wrong. */ -WARN_ON (vq->num_added > vq->vring.num); +/* This is very unlikely, but theoretically possible. Kick +* just in case. */ +if (unlikely (vq->num_added ==65535 )) +virtqueue_kick (_vq);pr_debug ("Added buffer head %i to %p\n" , head, vq);END_USE (vq);
Michael S. Tsirkin
maintainerRe: [PATCH 5 of 5] virtio: expose added descriptors immediately
Quoted 39 lines · Rusty Russell
Quoted 22 lines · Michael S. Tsirkin
Quoted 6 lines · Rusty Russell
Quoted 2 lines · Michael S. Tsirkin
My unlocked kick patches will trip this warning: they make virtio-net do add + get without kick.
Heh, it's a good sign if they do, since that means you're running really well :)
They don't in fact, in my testing :(. But I think they can with luck.
Quoted 12 lines
Quoted 7 lines
I think block with unlocked kick can trip it too:
add, lock is dropped and then an interrupt can get.We also don't need a kick each num - each 2^15 is enough. Why don't we do this at start of
add_buf:
if (vq->num_added>=0x7fff)return -ENOSPC;The warning was there in case a driver is never doing a kick, and getting away with it (mostly) because the device is polling. Let's not penalize good drivers to catch bad ones.
Quoted 6 lines
Quoted 1 line
How about we do this properly, like so:
Absolutely. But I think we also need to handle
num_addedoverflow of a 15 bit counter, no? Otherwise thevring_need_eventlogic might give us false negatives .... I'm guessing we can just assume we need a kick in that case.You're right. Thankyou. My immediate reaction of "make it an unsigned long" doesn't work.
Here's the diff to what I posted before:
drivers/virtio/virtio_ring.c add_head: · line 259
Quoted 9 lines
drivers/virtio/virtio_ring.c +3
-3@@ -254,9 +254,10 @@ add_head: vq->vring.avail->idx++; vq->num_added++; -/* If you haven't kicked in this long, you're probably doing something -* wrong. */ -WARN_ON (vq->num_added > vq->vring.num); +/* This is very unlikely, but theoretically possible. Kick +* just in case. */ +if (unlikely (vq->num_added ==65535 ))
This is 0xffff but why use the decimal notation?
drivers/virtio/virtio_ring.c add_head: · line 263
Quoted 4 lines
drivers/virtio/virtio_ring.c +1
-0+virtqueue_kick (_vq);pr_debug ("Added buffer head %i to %p\n" , head, vq);END_USE (vq);
We also still need to reset vq->num_added, right?
Signature · 1 line
MST
Re: [PATCH 5 of 5] virtio: expose added descriptors immediately
Quoted 9 lines · Michael S. Tsirkin
drivers/virtio/virtio_ring.c add_head: · line 259
Quoted 6 lines · Rusty Russell
drivers/virtio/virtio_ring.c +3
-3-/* If you haven't kicked in this long, you're probably doing something -* wrong. */ -WARN_ON (vq->num_added > vq->vring.num); +/* This is very unlikely, but theoretically possible. Kick +* just in case. */ +if (unlikely (vq->num_added ==65535 ))This is
0xffffbut why use the decimal notation?
Interesting. Why use hex? Feels more like binary?
But I've changed it to "(1 << 16) - 1" to be clear.
Quoted 6 lines
drivers/virtio/virtio_ring.c add_head: · line 263
Quoted 4 lines
drivers/virtio/virtio_ring.c +1
-0+virtqueue_kick (_vq);pr_debug ("Added buffer head %i to %p\n" , head, vq);END_USE (vq);We also still need to reset
vq->num_added, right?
virtqueue_kick does that for us.
Cheers,
Rusty.
Michael S. Tsirkin
maintainerRe: [PATCH 5 of 5] virtio: expose added descriptors immediately
Quoted 11 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
drivers/virtio/virtio_ring.c add_head: · line 259
Quoted 6 lines · Rusty Russell
drivers/virtio/virtio_ring.c +3
-3-/* If you haven't kicked in this long, you're probably doing something -* wrong. */ -WARN_ON (vq->num_added > vq->vring.num); +/* This is very unlikely, but theoretically possible. Kick +* just in case. */ +if (unlikely (vq->num_added ==65535 ))This is
0xffffbut why use the decimal notation?
Quoted 1 line
Interesting. Why use hex? Feels more like binary?
Just easier to see it's the largest 16 bit number.
Quoted 1 line
But I've changed it to "(1 << 16) - 1" to be clear.
That's even better.
Quoted 11 lines
Quoted 6 lines
drivers/virtio/virtio_ring.c add_head: · line 263
Quoted 4 lines
drivers/virtio/virtio_ring.c +1
-0+virtqueue_kick (_vq);pr_debug ("Added buffer head %i to %p\n" , head, vq);END_USE (vq);We also still need to reset
vq->num_added, right?
virtqueue_kickdoes that for us.Cheers,
Rusty.
Right.
Michael S. Tsirkin
maintainerRFD: virtio balloon API use (was Re: [PATCH 5 of 5] virtio: expose added descriptors immediately)
In reply to Rusty RussellQuoted 7 lines · Rusty Russell
A virtio driver does
virtqueue_add_buf()multiple times before finally callingvirtqueue_kick(); previously we only exposed the added buffers in thevirtqueue_kick()call. This means we don't need a memory barrier invirtqueue_add_buf(), but it reduces concurrency as the device (ie. host) can't see the buffers until the kick.Signed-off-by Rusty Russell <[email protected]>
Looking at recent mm compaction patches made me look at locking in balloon closely. And I noticed the referenced patch (commit ee7cd8981e15bcb365fc762afe3fc47b8242f630 upstream) interacts strangely with virtio balloon; balloon currently does:
static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq)
{
struct scatterlist sg;
sg_init_one(&sg, vb->pfns, sizeof(vb->pfns[0]) * vb->num_pfns);
init_completion(&vb->acked);
/* We should always be able to add one buffer to an empty queue. */
if (virtqueue_add_buf(vq, &sg, 1, 0, vb, GFP_KERNEL) < 0)
BUG();
virtqueue_kick(vq);
/* When host has read buffer, this completes via balloon_ack */
wait_for_completion(&vb->acked);
}While vq callback does:
static void balloon_ack(struct virtqueue *vq)
{
struct virtio_balloon *vb;
unsigned int len;
vb = virtqueue_get_buf(vq, &len);
if (vb)
complete(&vb->acked);
}So virtqueue_get_buf might now run concurrently with virtqueue_kick. I audited both and this seems safe in practice but I think we need to either declare this legal at the API level
or add locking in driver.
Further, is there a guarantee that we never get
spurious callbacks? We currently check ring not empty
but esp for non shared MSI this might not be needed.
If a spurious callback triggers, virtqueue_get_buf can run concurrently with virtqueue_add_buf which is known to be racy. Again I think this is currently safe as no spurious callbacks in practice but should we guarantee no spurious callbacks at the API level or add locking in driver?
Signature · 1 line
MST
Re: RFD: virtio balloon API use (was Re: [PATCH 5 of 5] virtio: expose added descriptors immediately)
Quoted 45 lines · Michael S. Tsirkin
Quoted 7 lines · Rusty Russell
A virtio driver does
virtqueue_add_buf()multiple times before finally callingvirtqueue_kick(); previously we only exposed the added buffers in thevirtqueue_kick()call. This means we don't need a memory barrier invirtqueue_add_buf(), but it reduces concurrency as the device (ie. host) can't see the buffers until the kick.Signed-off-by Rusty Russell <[email protected]>Looking at recent mm compaction patches made me look at locking in balloon closely. And I noticed the referenced patch (commit
ee7cd8981e15bcb365fc762afe3fc47b8242f630upstream) interacts strangely with virtio balloon; balloon currently does:static void
tell_host(struct virtio_balloon *vb, struct virtqueue *vq){ struct scatterlist sg; sg_init_one(&sg, vb->pfns, sizeof(vb->pfns[0]) * vb->num_pfns); init_completion(&vb->acked); /* We should always be able to add one buffer to an empty queue. */ if (virtqueue_add_buf(vq, &sg, 1, 0, vb, GFP_KERNEL) < 0) BUG(); virtqueue_kick(vq); /* When host has read buffer, this completes via balloon_ack */ wait_for_completion(&vb->acked); }While vq callback does:
static void
balloon_ack(struct virtqueue *vq){ struct virtio_balloon *vb; unsigned int len; vb = virtqueue_get_buf(vq, &len); if (vb) complete(&vb->acked); }
Quoted 2 lines
So
virtqueue_get_bufmight now run concurrently withvirtqueue_kick. I audited both and this seems safe in practice but I think
Good spotting!
Agreed. Because there's only add_buf, we get away with it: the add_buf must be almost finished by the time get_buf runs because the device has seen the buffer.
Quoted 2 lines
we need to either declare this legal at the API level or add locking in driver.
I wonder if we should just lock in the balloon driver, rather than document this corner case and set a bad example. Are there other drivers which take the same shortcut?
Quoted 3 lines
Further, is there a guarantee that we never get spurious callbacks? We currently check ring not empty but esp for non shared MSI this might not be needed.
Yes, I think this saves us. A spurious interrupt won't trigger a spurious callback.
Quoted 5 lines
If a spurious callback triggers,
virtqueue_get_bufcan run concurrently withvirtqueue_add_bufwhich is known to be racy. Again I think this is currently safe as no spurious callbacks in practice but should we guarantee no spurious callbacks at the API level or add locking in driver?
I think we should guarantee it, but is there a hole in the current implementation?
Thanks,
Rusty.
Michael S. Tsirkin
maintainerRe: RFD: virtio balloon API use (was Re: [PATCH 5 of 5] virtio: expose added descriptors immediately)
Quoted 55 lines · Rusty Russell
Quoted 45 lines · Michael S. Tsirkin
Quoted 7 lines · Rusty Russell
A virtio driver does
virtqueue_add_buf()multiple times before finally callingvirtqueue_kick(); previously we only exposed the added buffers in thevirtqueue_kick()call. This means we don't need a memory barrier invirtqueue_add_buf(), but it reduces concurrency as the device (ie. host) can't see the buffers until the kick.Signed-off-by Rusty Russell <[email protected]>Looking at recent mm compaction patches made me look at locking in balloon closely. And I noticed the referenced patch (commit
ee7cd8981e15bcb365fc762afe3fc47b8242f630upstream) interacts strangely with virtio balloon; balloon currently does:static void
tell_host(struct virtio_balloon *vb, struct virtqueue *vq){ struct scatterlist sg; sg_init_one(&sg, vb->pfns, sizeof(vb->pfns[0]) * vb->num_pfns); init_completion(&vb->acked); /* We should always be able to add one buffer to an empty queue. */ if (virtqueue_add_buf(vq, &sg, 1, 0, vb, GFP_KERNEL) < 0) BUG(); virtqueue_kick(vq); /* When host has read buffer, this completes via balloon_ack */ wait_for_completion(&vb->acked); }While vq callback does:
static void
balloon_ack(struct virtqueue *vq){ struct virtio_balloon *vb; unsigned int len; vb = virtqueue_get_buf(vq, &len); if (vb) complete(&vb->acked); }Quoted 2 lines
So
virtqueue_get_bufmight now run concurrently withvirtqueue_kick. I audited both and this seems safe in practice but I thinkGood spotting!
Agreed. Because there's only
add_buf, we get away with it: theadd_bufmust be almost finished by the timeget_bufruns because the device has seen the buffer.
Quoted 5 lines
Quoted 2 lines
we need to either declare this legal at the API level or add locking in driver.
I wonder if we should just lock in the balloon driver, rather than document this corner case and set a bad example.
We'll need to replace &vb->acked with a waitqueue and do get_buf from the same thread.
But I note that stats_request hash the same issue. Let's see if we can fix it.
Quoted 2 lines
Are there other
drivers which take the same shortcut?
Not that I know.
Quoted 13 lines
Quoted 3 lines
Further, is there a guarantee that we never get spurious callbacks? We currently check ring not empty but esp for non shared MSI this might not be needed.
Yes, I think this saves us. A spurious interrupt won't trigger a spurious callback.
Quoted 5 lines
If a spurious callback triggers,
virtqueue_get_bufcan run concurrently withvirtqueue_add_bufwhich is known to be racy. Again I think this is currently safe as no spurious callbacks in practice but should we guarantee no spurious callbacks at the API level or add locking in driver?
Quoted 5 lines
I think we should guarantee it, but is there a hole in the current implementation?
Thanks,
Rusty.
Could be. The check for ring empty looks somewhat suspicious. It might be expensive to make it 100% robust - that check was intended as an optimization for shared interrupts. Whith per vq interrupts we IMO do not need the check. If we add locking in balloon I think there's no need to guarantee no spurious interrupts.
Signature · 1 line
MST
Re: RFD: virtio balloon API use (was Re: [PATCH 5 of 5] virtio: expose added descriptors immediately)
Quoted 91 lines · Michael S. Tsirkin
Quoted 55 lines · Rusty Russell
Quoted 45 lines · Michael S. Tsirkin
Quoted 7 lines · Rusty Russell
A virtio driver does
virtqueue_add_buf()multiple times before finally callingvirtqueue_kick(); previously we only exposed the added buffers in thevirtqueue_kick()call. This means we don't need a memory barrier invirtqueue_add_buf(), but it reduces concurrency as the device (ie. host) can't see the buffers until the kick.Signed-off-by Rusty Russell <[email protected]>Looking at recent mm compaction patches made me look at locking in balloon closely. And I noticed the referenced patch (commit
ee7cd8981e15bcb365fc762afe3fc47b8242f630upstream) interacts strangely with virtio balloon; balloon currently does:static void
tell_host(struct virtio_balloon *vb, struct virtqueue *vq){ struct scatterlist sg; sg_init_one(&sg, vb->pfns, sizeof(vb->pfns[0]) * vb->num_pfns); init_completion(&vb->acked); /* We should always be able to add one buffer to an empty queue. */ if (virtqueue_add_buf(vq, &sg, 1, 0, vb, GFP_KERNEL) < 0) BUG(); virtqueue_kick(vq); /* When host has read buffer, this completes via balloon_ack */ wait_for_completion(&vb->acked); }While vq callback does:
static void
balloon_ack(struct virtqueue *vq){ struct virtio_balloon *vb; unsigned int len; vb = virtqueue_get_buf(vq, &len); if (vb) complete(&vb->acked); }Quoted 2 lines
So
virtqueue_get_bufmight now run concurrently withvirtqueue_kick. I audited both and this seems safe in practice but I thinkGood spotting!
Agreed. Because there's only
add_buf, we get away with it: theadd_bufmust be almost finished by the timeget_bufruns because the device has seen the buffer.Quoted 5 lines
Quoted 2 lines
we need to either declare this legal at the API level or add locking in driver.
I wonder if we should just lock in the balloon driver, rather than document this corner case and set a bad example.
We'll need to replace
&vb->ackedwith a waitqueue and doget_buffrom the same thread.
But I note thatstats_requesthash the same issue. Let's see if we can fix it.Quoted 2 lines
Are there other
drivers which take the same shortcut?Not that I know.
Quoted 13 lines
Quoted 3 lines
Further, is there a guarantee that we never get spurious callbacks? We currently check ring not empty but esp for non shared MSI this might not be needed.
Yes, I think this saves us. A spurious interrupt won't trigger a spurious callback.
Quoted 5 lines
If a spurious callback triggers,
virtqueue_get_bufcan run concurrently withvirtqueue_add_bufwhich is known to be racy. Again I think this is currently safe as no spurious callbacks in practice but should we guarantee no spurious callbacks at the API level or add locking in driver?Quoted 5 lines
I think we should guarantee it, but is there a hole in the current implementation?
Thanks,
Rusty.
Quoted 7 lines
Could be. The check for ring empty looks somewhat suspicious. It might be expensive to make it 100% robust - that check was intended as an optimization for shared interrupts. Whith per vq interrupts we IMO do not need the check. If we add locking in balloon I think there's no need to guarantee no spurious interrupts.
As 'locking in balloon', may I assume the approach I took for the compaction case is OK and aligned to address these concerns of yours? If not, do not hesitate in giving me your thoughts, please. I'm respinning a V3 series to address a couple of extra nitpicks from the compaction standpoint, and I'd love to be able to address any extra concern you might have on the balloon side of that work.
Thanks!
Rafael.
Re: RFD: virtio balloon API use (was Re: [PATCH 5 of 5] virtio: expose added descriptors immediately)
Quoted 5 lines · Rafael Aquini
As 'locking in balloon', may I assume the approach I took for the compaction case is OK and aligned to address these concerns of yours? If not, do not hesitate in giving me your thoughts, please. I'm respinning a V3 series to address a couple of extra nitpicks from the compaction standpoint, and I'd love to be able to address any extra concern you might have on the balloon side of that work.
It's orthogonal, though looks like they clash textually :(
I'll re-spin MST's patch on top of yours, and include both in my tree, otherwise linux-next will have to do the merge. But I'll await your push before pushing to Linus next merge window.
Thanks,
Rusty.
Re: RFD: virtio balloon API use (was Re: [PATCH 5 of 5] virtio: expose added descriptors immediately)
Quoted 7 lines · Rusty Russell
Quoted 5 lines · Rafael Aquini
As 'locking in balloon', may I assume the approach I took for the compaction case is OK and aligned to address these concerns of yours? If not, do not hesitate in giving me your thoughts, please. I'm respinning a V3 series to address a couple of extra nitpicks from the compaction standpoint, and I'd love to be able to address any extra concern you might have on the balloon side of that work.
Quoted 6 lines
It's orthogonal, though looks like they clash textually :(
I'll re-spin MST's patch on top of yours, and include both in my tree, otherwise linux-next will have to do the merge. But I'll await your push before pushing to Linus next merge window.
Thanks, Rusty.
I'll post V3 series quite soon.
Cheers!
Rafael
Quoted 2 lines
Thanks,
Rusty.
Re: RFD: virtio balloon API use (was Re: [PATCH 5 of 5] virtio: expose added descriptors immediately)
In reply to Rusty RussellQuoted 7 lines · Rusty Russell
Quoted 5 lines · Rafael Aquini
As 'locking in balloon', may I assume the approach I took for the compaction case is OK and aligned to address these concerns of yours? If not, do not hesitate in giving me your thoughts, please. I'm respinning a V3 series to address a couple of extra nitpicks from the compaction standpoint, and I'd love to be able to address any extra concern you might have on the balloon side of that work.
Quoted 6 lines
It's orthogonal, though looks like they clash textually :(
I'll re-spin MST's patch on top of yours, and include both in my tree, otherwise linux-next will have to do the merge. But I'll await your push before pushing to Linus next merge window.
Thanks, Rusty.
I'll post V3 series quite soon.
Cheers!
Rafael
Quoted 2 lines
Thanks,
Rusty.
Michael S. Tsirkin
maintainerRe: RFD: virtio balloon API use (was Re: [PATCH 5 of 5] virtio: expose added descriptors immediately)
In reply to Rusty RussellQuoted 7 lines · Rusty Russell
Quoted 5 lines · Rafael Aquini
As 'locking in balloon', may I assume the approach I took for the compaction case is OK and aligned to address these concerns of yours? If not, do not hesitate in giving me your thoughts, please. I'm respinning a V3 series to address a couple of extra nitpicks from the compaction standpoint, and I'd love to be able to address any extra concern you might have on the balloon side of that work.
Quoted 8 lines
It's orthogonal, though looks like they clash textually :(
I'll re-spin MST's patch on top of yours, and include both in my tree, otherwise linux-next will have to do the merge. But I'll await your push before pushing to Linus next merge window.
Thanks,
Rusty.
While theoretical mine is a bugfix so could be 3.5 material, no?
Signature · 1 line
MST
Re: RFD: virtio balloon API use (was Re: [PATCH 5 of 5] virtio: expose added descriptors immediately)
Quoted 10 lines · Michael S. Tsirkin
Quoted 9 lines · Rusty Russell
Quoted 5 lines · Rafael Aquini
As 'locking in balloon', may I assume the approach I took for the compaction case is OK and aligned to address these concerns of yours? If not, do not hesitate in giving me your thoughts, please. I'm respinning a V3 series to address a couple of extra nitpicks from the compaction standpoint, and I'd love to be able to address any extra concern you might have on the balloon side of that work.
It's orthogonal, though looks like they clash textually :(
Quoted 8 lines
Quoted 6 lines
I'll re-spin MST's patch on top of yours, and include both in my tree, otherwise linux-next will have to do the merge. But I'll await your push before pushing to Linus next merge window.
Thanks,
Rusty.While theoretical mine is a bugfix so could be 3.5 material, no?
It's a little thin, but it's a good idea. I'll try.
Cheers,
Rusty.
Michael S. Tsirkin
maintainerRe: RFD: virtio balloon API use (was Re: [PATCH 5 of 5] virtio: expose added descriptors immediately)
In reply to Rafael AquiniQuoted 100 lines · Rafael Aquini
Quoted 91 lines · Michael S. Tsirkin
Quoted 55 lines · Rusty Russell
Quoted 45 lines · Michael S. Tsirkin
Quoted 7 lines · Rusty Russell
A virtio driver does
virtqueue_add_buf()multiple times before finally callingvirtqueue_kick(); previously we only exposed the added buffers in thevirtqueue_kick()call. This means we don't need a memory barrier invirtqueue_add_buf(), but it reduces concurrency as the device (ie. host) can't see the buffers until the kick.Signed-off-by Rusty Russell <[email protected]>Looking at recent mm compaction patches made me look at locking in balloon closely. And I noticed the referenced patch (commit
ee7cd8981e15bcb365fc762afe3fc47b8242f630upstream) interacts strangely with virtio balloon; balloon currently does:static void
tell_host(struct virtio_balloon *vb, struct virtqueue *vq){ struct scatterlist sg; sg_init_one(&sg, vb->pfns, sizeof(vb->pfns[0]) * vb->num_pfns); init_completion(&vb->acked); /* We should always be able to add one buffer to an empty queue. */ if (virtqueue_add_buf(vq, &sg, 1, 0, vb, GFP_KERNEL) < 0) BUG(); virtqueue_kick(vq); /* When host has read buffer, this completes via balloon_ack */ wait_for_completion(&vb->acked); }While vq callback does:
static void
balloon_ack(struct virtqueue *vq){ struct virtio_balloon *vb; unsigned int len; vb = virtqueue_get_buf(vq, &len); if (vb) complete(&vb->acked); }Quoted 2 lines
So
virtqueue_get_bufmight now run concurrently withvirtqueue_kick. I audited both and this seems safe in practice but I thinkGood spotting!
Agreed. Because there's only
add_buf, we get away with it: theadd_bufmust be almost finished by the timeget_bufruns because the device has seen the buffer.Quoted 5 lines
Quoted 2 lines
we need to either declare this legal at the API level or add locking in driver.
I wonder if we should just lock in the balloon driver, rather than document this corner case and set a bad example.
We'll need to replace
&vb->ackedwith a waitqueue and doget_buffrom the same thread.
But I note thatstats_requesthash the same issue. Let's see if we can fix it.Quoted 2 lines
Are there other
drivers which take the same shortcut?Not that I know.
Quoted 13 lines
Quoted 3 lines
Further, is there a guarantee that we never get spurious callbacks? We currently check ring not empty but esp for non shared MSI this might not be needed.
Yes, I think this saves us. A spurious interrupt won't trigger a spurious callback.
Quoted 5 lines
If a spurious callback triggers,
virtqueue_get_bufcan run concurrently withvirtqueue_add_bufwhich is known to be racy. Again I think this is currently safe as no spurious callbacks in practice but should we guarantee no spurious callbacks at the API level or add locking in driver?Quoted 5 lines
I think we should guarantee it, but is there a hole in the current implementation?
Thanks,
Rusty.Quoted 7 lines
Could be. The check for ring empty looks somewhat suspicious. It might be expensive to make it 100% robust - that check was intended as an optimization for shared interrupts. Whith per vq interrupts we IMO do not need the check. If we add locking in balloon I think there's no need to guarantee no spurious interrupts.
Quoted 2 lines
As 'locking in balloon', may I assume the approach I took for the compaction case is OK and aligned to address these concerns of yours?
No, I mean the patch I posted. Not so much locking as moving get_buf to thread itself.
Quoted 8 lines
If not, do not hesitate in
giving me your thoughts, please. I'm respinning a V3 series to address a couple of extra nitpicks from the compaction standpoint, and I'd love to be able to address any extra concern you might have on the balloon side of that work.Thanks!
Rafael.
Signature · 1 line
MST
Michael S. Tsirkin
maintainerRFC PATCH 1/1 virtio-balloon: fix add/get API use
In reply to Rusty RussellIn virtio balloon virtqueue_get_buf might now run concurrently with virtqueue_kick. I audited both and this seems safe in practice but this is not guaranteed by the API.
Additionally, a spurious interrupt might in theory make virtqueue_get_buf run in parallel with virtqueue_add_buf, which is racy.
While we might try to protect against spurious callbacks it's easier to fix the driver: balloon seems to be the only one (mis)using the API like this, so let's just fix balloon.
---
Warning: completely untested.
drivers/virtio/virtio_balloon.c +11 -14
@@ -47,7 +47,7 @@ struct virtio_balloonstruct task_struct *thread;/* Waiting for host to ack the pages we released. */ -struct completion acked; +wait_queue_head_t acked;/* Number of balloon pages we've told the Host we're not using. */ unsigned int num_pages;
@@ -89,29 +89,26 @@ static struct page *balloon_pfn_to_page(u32 pfn)static void balloon_ack (struct virtqueue *vq) { -struct virtio_balloon *vb; -unsigned int len; +struct virtio_balloon *vb = vq->vdev->priv; - vb =virtqueue_get_buf (vq, &len); -if (vb) -complete (&vb->acked); +wake_up (&vb->acked); }static void tell_host (struct virtio_balloon *vb,struct virtqueue *vq) {struct scatterlist sg; +unsigned int len; +void *buf;sg_init_one (&sg, vb->pfns,sizeof (vb->pfns[0 ]) * vb->num_pfns); -init_completion (&vb->acked); -/* We should always be able to add one buffer to an empty queue. */ if (virtqueue_add_buf (vq, &sg,1 ,0 , vb,GFP_KERNEL ) <0 )BUG ();virtqueue_kick (vq);/* When host has read buffer, this completes via balloon_ack */ -wait_for_completion (&vb->acked); +wait_event (vb->acked,virtqueue_get_buf (vq, &len)); }static void set_page_pfns (u32 pfns[],struct page *page)
@@ -231,12 +228,8 @@ static void update_balloon_stats(struct virtio_balloon *vb)*/ static void stats_request (struct virtqueue *vq) { -struct virtio_balloon *vb; -unsigned int len; +struct virtio_balloon *vb = vq->vdev->priv; - vb =virtqueue_get_buf (vq, &len); -if (!vb) -return ; vb->need_stats_update =1 ;wake_up (&vb->config_change); }
@@ -245,11 +238,14 @@ static void stats_handle_request(struct virtio_balloon *vb) {struct virtqueue *vq;struct scatterlist sg; +unsigned int len; vb->need_stats_update =0 ;update_balloon_stats (vb); vq = vb->stats_vq; +if (!virtqueue_get_buf (vq, &len)) +return ;sg_init_one (&sg, vb->stats,sizeof (vb->stats));if (virtqueue_add_buf (vq, &sg,1 ,0 , vb,GFP_KERNEL ) <0 )BUG ();
@@ -358,6 +354,7 @@ static int virtballoon_probe(struct virtio_device *vdev)INIT_LIST_HEAD (&vb->pages); vb->num_pages =0 ;init_waitqueue_head (&vb->config_change); +init_waitqueue_head (&vb->acked); vb->vdev = vdev; vb->need_stats_update =0 ;
Re: [PATCH RFC] virtio-balloon: fix add/get API use
Quoted 11 lines · Michael S. Tsirkin
In virtio balloon
virtqueue_get_bufmight now run concurrently withvirtqueue_kick. I audited both and this seems safe in practice but this is not guaranteed by the API.
Additionally, a spurious interrupt might in theory makevirtqueue_get_bufrun in parallel withvirtqueue_add_buf, which is racy.While we might try to protect against spurious callbacks it's easier to fix the driver: balloon seems to be the only one (mis)using the API like this, so let's just fix balloon.
Signed-off-by Michael S. Tsirkin <[email protected]>
I was thinking of a spinlock, but this is far more elegant.
And I added an explicit reference to the 'virtio: expose added descriptors immediately.' commit in your commit msg.
Kudos!
Rusty.