Michael S. Tsirkin
2017-Jul-13 20:19 UTC
[PATCH v12 5/8] virtio-balloon: VIRTIO_BALLOON_F_SG
On Thu, Jul 13, 2017 at 03:42:35PM +0800, Wei Wang wrote:> On 07/12/2017 09:56 PM, Michael S. Tsirkin wrote: > > > > So the way I see it, there are several issues: > > > > - internal wait - forces multiple APIs like kick/kick_sync > > note how kick_sync can fail but your code never checks return code > > - need to re-write the last descriptor - might not work > > for alternative layouts which always expose descriptors > > immediately > > Probably it wasn't clear. Please let me explain the two functions here: > > 1) virtqueue_add_chain_desc(vq, head_id, prev_id,..): > grabs a desc from the vq and inserts it to the chain tail (which is indexed > by > prev_id, probably better to call it tail_id). Then, the new added desc > becomes > the tail (i.e. the last desc). The _F_NEXT flag is cleared for each desc > when it's > added to the chain, and set when another desc comes to follow later.And this only works if there are multiple rings like avail + descriptor ring. It won't work e.g. with the proposed new layout where writing out a descriptor exposes it immediately.> 2) virtqueue_add_chain(vq, head_id,..): expose the chain to the other end. > > So, if people want to add a desc and immediately expose it to the other end, > i.e. build a single desc chain, they can just add and expose: > > virtqueue_add_chain_desc(..); > virtqueue_add_chain(..,head_id); > > Would you see any issues here?The way the new APIs poll used ring internally.> > > - some kind of iterator type would be nicer instead of > > maintaining head/prev explicitly > > Why would we need to iterate the chain?In your patches prev/tail are iterators - they keep track of where you are in the chain.> I think it would be simpler to use > a wrapper struct: > > struct virtqueue_desc_chain { > unsigned int head; // head desc id of the chain > unsigned int tail; // tail desc id of the chain > } > > The new desc will be put to desc[tail].next, and we don't need to walk > from the head desc[head].next when inserting a new desc to the chain, right? > > > > > > As for the use, it would be better to do > > > > if (!add_next(vq, ...)) { > > add_last(vq, ...) > > kick > > wait > > } > > "!add_next(vq, ...)" means that the vq is full?No - it means there's only 1 entry left for the last descriptor.> If so, what would add_last() > do then? > > > Using VIRTQUEUE_DESC_ID_INIT seems to avoid a branch in the driver, but > > in fact it merely puts the branch in the virtio code. > > > > Actually it wasn't intended to improve performance. It is used to indicate > the "init" state > of the chain. So, when virtqueue_add_chain_desc(, head_id,..) finds head > id=INIT, it will > assign the grabbed desc id to &head_id. In some sense, it is equivalent to > add_first(). > > Do you have a different opinion here? > > Best, > Wei >It is but let's make it explicit here - an API function is better than a special value. -- MST
On 07/14/2017 04:19 AM, Michael S. Tsirkin wrote:> On Thu, Jul 13, 2017 at 03:42:35PM +0800, Wei Wang wrote: >> On 07/12/2017 09:56 PM, Michael S. Tsirkin wrote: >>> So the way I see it, there are several issues: >>> >>> - internal wait - forces multiple APIs like kick/kick_sync >>> note how kick_sync can fail but your code never checks return code >>> - need to re-write the last descriptor - might not work >>> for alternative layouts which always expose descriptors >>> immediately >> Probably it wasn't clear. Please let me explain the two functions here: >> >> 1) virtqueue_add_chain_desc(vq, head_id, prev_id,..): >> grabs a desc from the vq and inserts it to the chain tail (which is indexed >> by >> prev_id, probably better to call it tail_id). Then, the new added desc >> becomes >> the tail (i.e. the last desc). The _F_NEXT flag is cleared for each desc >> when it's >> added to the chain, and set when another desc comes to follow later. > And this only works if there are multiple rings like > avail + descriptor ring. > It won't work e.g. with the proposed new layout where > writing out a descriptor exposes it immediately.I think it can support the 1.1 proposal, too. But before getting into that, I think we first need to deep dive into the implementation and usage of _first/next/last. The usage would need to lock the vq from the first to the end (otherwise, the returned info about the number of available desc in the vq, i.e. num_free, would be invalid): lock(vq); add_first(); add_next(); add_last(); unlock(vq); However, I think the case isn't this simple, since we need to check more things after each add_xx() step. For example, if only one entry is available at the time we start to use the vq, that is, num_free is 0 after add_first(), we wouldn't be able to add_next and add_last. So, it would work like this: start: ...get free page block.. lock(vq) retry: ret = add_first(..,&num_free,); if(ret == -ENOSPC) { goto retry; } else if (!num_free) { add_chain_head(); unlock(vq); kick & wait; goto start; } next_one: ...get free page block.. add_next(..,&num_free,); if (!num_free) { add_chain_head(); unlock(vq); kick & wait; goto start; } if (num_free == 1) { ...get free page block.. add_last(..); unlock(vq); kick & wait; goto start; } else { goto next_one; } The above seems unnecessary to me to have three different APIs. That's the reason to combine them into one virtqueue_add_chain_desc(). -- or, do you have a different thought about using the three APIs? Implementation Reference: struct desc_iterator { unsigned int head; unsigned int tail; }; add_first(*vq, *desc_iterator, *num_free, ..) { if (vq->vq.num_free < 1) return -ENOSPC; get_desc(&desc_id); desc[desc_id].flag &= ~_F_NEXT; desc_iterator->head = desc_id desc_iterator->tail = desc_iterator->head; *num_free = vq->vq.num_free; } add_next(vq, desc_iterator, *num_free,..) { get_desc(&desc_id); desc[desc_id].flag &= ~_F_NEXT; desc[desc_iterator.tail].next = desc_id; desc[desc_iterator->tail].flag |= _F_NEXT; desc_iterator->tail = desc_id; *num_free = vq->vq.num_free; } add_last(vq, desc_iterator,..) { get_desc(&desc_id); desc[desc_id].flag &= ~_F_NEXT; desc[desc_iterator.tail].next = desc_id; desc_iterator->tail = desc_id; add_chain_head(); // put the desc_iterator.head to the ring } Best, Wei
Michael S. Tsirkin
2017-Jul-23 01:45 UTC
[PATCH v12 5/8] virtio-balloon: VIRTIO_BALLOON_F_SG
On Fri, Jul 14, 2017 at 03:12:43PM +0800, Wei Wang wrote:> On 07/14/2017 04:19 AM, Michael S. Tsirkin wrote: > > On Thu, Jul 13, 2017 at 03:42:35PM +0800, Wei Wang wrote: > > > On 07/12/2017 09:56 PM, Michael S. Tsirkin wrote: > > > > So the way I see it, there are several issues: > > > > > > > > - internal wait - forces multiple APIs like kick/kick_sync > > > > note how kick_sync can fail but your code never checks return code > > > > - need to re-write the last descriptor - might not work > > > > for alternative layouts which always expose descriptors > > > > immediately > > > Probably it wasn't clear. Please let me explain the two functions here: > > > > > > 1) virtqueue_add_chain_desc(vq, head_id, prev_id,..): > > > grabs a desc from the vq and inserts it to the chain tail (which is indexed > > > by > > > prev_id, probably better to call it tail_id). Then, the new added desc > > > becomes > > > the tail (i.e. the last desc). The _F_NEXT flag is cleared for each desc > > > when it's > > > added to the chain, and set when another desc comes to follow later. > > And this only works if there are multiple rings like > > avail + descriptor ring. > > It won't work e.g. with the proposed new layout where > > writing out a descriptor exposes it immediately. > > I think it can support the 1.1 proposal, too. But before getting > into that, I think we first need to deep dive into the implementation > and usage of _first/next/last. The usage would need to lock the vq > from the first to the end (otherwise, the returned info about the number > of available desc in the vq, i.e. num_free, would be invalid): > > lock(vq); > add_first(); > add_next(); > add_last(); > unlock(vq); > > However, I think the case isn't this simple, since we need to check more > things > after each add_xx() step. For example, if only one entry is available at the > time > we start to use the vq, that is, num_free is 0 after add_first(), we > wouldn't be > able to add_next and add_last. So, it would work like this: > > start: > ...get free page block.. > lock(vq) > retry: > ret = add_first(..,&num_free,); > if(ret == -ENOSPC) { > goto retry; > } else if (!num_free) { > add_chain_head(); > unlock(vq); > kick & wait; > goto start; > } > next_one: > ...get free page block.. > add_next(..,&num_free,); > if (!num_free) { > add_chain_head(); > unlock(vq); > kick & wait; > goto start; > } if (num_free == 1) { > ...get free page block.. > add_last(..); > unlock(vq); > kick & wait; > goto start; > } else { > goto next_one; > } > > The above seems unnecessary to me to have three different APIs. > That's the reason to combine them into one virtqueue_add_chain_desc(). > > -- or, do you have a different thought about using the three APIs? > > > Implementation Reference: > > struct desc_iterator { > unsigned int head; > unsigned int tail; > }; > > add_first(*vq, *desc_iterator, *num_free, ..) > { > if (vq->vq.num_free < 1) > return -ENOSPC; > get_desc(&desc_id); > desc[desc_id].flag &= ~_F_NEXT; > desc_iterator->head = desc_id > desc_iterator->tail = desc_iterator->head; > *num_free = vq->vq.num_free; > } > > add_next(vq, desc_iterator, *num_free,..) > { > get_desc(&desc_id); > desc[desc_id].flag &= ~_F_NEXT; > desc[desc_iterator.tail].next = desc_id; > desc[desc_iterator->tail].flag |= _F_NEXT; > desc_iterator->tail = desc_id; > *num_free = vq->vq.num_free; > } > > add_last(vq, desc_iterator,..) > { > get_desc(&desc_id); > desc[desc_id].flag &= ~_F_NEXT; > desc[desc_iterator.tail].next = desc_id; > desc_iterator->tail = desc_id; > > add_chain_head(); // put the desc_iterator.head to the ring > } > > > Best, > WeiOK I thought this over. While we might need these new APIs in the future, I think that at the moment, there's a way to implement this feature that is significantly simpler. Just add each s/g as a separate input buffer. This needs zero new APIs. I know that follow-up patches need to add a header in front so you might be thinking: how am I going to add this header? The answer is quite simple - add it as a separate out header. Host will be able to distinguish between header and pages by looking at the direction, and - should we want to add IN data to header - additionally size (<4K => header). We will be able to look at extended APIs separately down the road. -- MST
Possibly Parallel Threads
- [PATCH v12 5/8] virtio-balloon: VIRTIO_BALLOON_F_SG
- [PATCH v12 5/8] virtio-balloon: VIRTIO_BALLOON_F_SG
- [PATCH v12 5/8] virtio-balloon: VIRTIO_BALLOON_F_SG
- [PATCH v12 5/8] virtio-balloon: VIRTIO_BALLOON_F_SG
- [PATCH v12 5/8] virtio-balloon: VIRTIO_BALLOON_F_SG