virtio: add api for delayed callbacks
Add an API that tells the other side that callbacks should be delayed until a lot of work has been done. Implement using the new event_idx feature. Note: it might seem advantageous to let the drivers ask for a callback after a specific capacity has been reached. However, as a single head can free many entries in the descriptor table, we don't really have a clue about capacity until get_buf is called. The API is the simplest to implement at the moment, we'll see what kind of hints drivers can pass when there's more than one user of the feature. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Родитель
4423fe40b0
Коммит
7ab358c23c
|
@ -376,6 +376,33 @@ bool virtqueue_enable_cb(struct virtqueue *_vq)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(virtqueue_enable_cb);
|
||||
|
||||
bool virtqueue_enable_cb_delayed(struct virtqueue *_vq)
|
||||
{
|
||||
struct vring_virtqueue *vq = to_vvq(_vq);
|
||||
u16 bufs;
|
||||
|
||||
START_USE(vq);
|
||||
|
||||
/* We optimistically turn back on interrupts, then check if there was
|
||||
* more to do. */
|
||||
/* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
|
||||
* either clear the flags bit or point the event index at the next
|
||||
* entry. Always do both to keep code simple. */
|
||||
vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
|
||||
/* TODO: tune this threshold */
|
||||
bufs = (u16)(vq->vring.avail->idx - vq->last_used_idx) * 3 / 4;
|
||||
vring_used_event(&vq->vring) = vq->last_used_idx + bufs;
|
||||
virtio_mb();
|
||||
if (unlikely((u16)(vq->vring.used->idx - vq->last_used_idx) > bufs)) {
|
||||
END_USE(vq);
|
||||
return false;
|
||||
}
|
||||
|
||||
END_USE(vq);
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed);
|
||||
|
||||
void *virtqueue_detach_unused_buf(struct virtqueue *_vq)
|
||||
{
|
||||
struct vring_virtqueue *vq = to_vvq(_vq);
|
||||
|
|
|
@ -51,6 +51,13 @@ struct virtqueue {
|
|||
* 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
|
||||
|
@ -86,6 +93,8 @@ void virtqueue_disable_cb(struct virtqueue *vq);
|
|||
|
||||
bool virtqueue_enable_cb(struct virtqueue *vq);
|
||||
|
||||
bool virtqueue_enable_cb_delayed(struct virtqueue *vq);
|
||||
|
||||
void *virtqueue_detach_unused_buf(struct virtqueue *vq);
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче