virtqueue related functions has been abstract since commit ("virtio: abstract virtqueue related methods"), add compatible for abstract API. Signed-off-by: zhenwei pi <pizhenwei@xxxxxxxxxxxxx> --- tools/virtio/linux/virtio.h | 355 ++++++++++++++++++++++++++++++++---- 1 file changed, 324 insertions(+), 31 deletions(-) diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h index 5d3440f474dd..3531dba53584 100644 --- a/tools/virtio/linux/virtio.h +++ b/tools/virtio/linux/virtio.h @@ -4,6 +4,7 @@ #include <linux/scatterlist.h> #include <linux/kernel.h> #include <linux/spinlock.h> +#include <linux/virtio_ring.h> struct device { void *parent; @@ -27,46 +28,338 @@ struct virtqueue { unsigned int num_max; void *priv; bool reset; + bool abstract; + + /* abstract operations */ + int (*add_sgs)(struct virtqueue *vq, struct scatterlist *sgs[], + unsigned int total_sg, + unsigned int out_sgs, unsigned int in_sgs, + void *data, void *ctx, gfp_t gfp); + bool (*kick_prepare)(struct virtqueue *vq); + bool (*notify)(struct virtqueue *vq); + unsigned int (*enable_cb_prepare)(struct virtqueue *vq); + bool (*enable_cb_delayed)(struct virtqueue *vq); + void (*disable_cb)(struct virtqueue *vq); + bool (*poll)(struct virtqueue *vq, unsigned int idx); + void *(*get_buf_ctx)(struct virtqueue *vq, unsigned int *len, void **ctx); + void *(*detach_unused_buf)(struct virtqueue *vq); + unsigned int (*get_vring_size)(const struct virtqueue *vq); + int (*resize)(struct virtqueue *vq, u32 num, + void (*recycle)(struct virtqueue *vq, void *buf)); + void (*__break)(struct virtqueue *vq); + void (*__unbreak)(struct virtqueue *vq); + bool (*is_broken)(const struct virtqueue *vq); }; -/* Interfaces exported by virtio_ring. */ -int virtqueue_add_sgs(struct virtqueue *vq, - struct scatterlist *sgs[], - unsigned int out_sgs, - unsigned int in_sgs, - void *data, - gfp_t gfp); +/** + * virtqueue_add_sgs - expose buffers to other end + * @vq: the struct virtqueue we're talking about. + * @sgs: array of terminated scatterlists. + * @out_sgs: the number of scatterlists readable by other side + * @in_sgs: the number of scatterlists which are writable (after readable ones) + * @data: the token identifying the buffer. + * @gfp: how to do memory allocations (if necessary). + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO). + */ +static inline int virtqueue_add_sgs(struct virtqueue *vq, + struct scatterlist *sgs[], + unsigned int out_sgs, + unsigned int in_sgs, + void *data, gfp_t gfp) +{ + unsigned int i, total_sg = 0; -int virtqueue_add_outbuf(struct virtqueue *vq, - struct scatterlist sg[], unsigned int num, - void *data, - gfp_t gfp); + /* Count them first. */ + for (i = 0; i < out_sgs + in_sgs; i++) { + struct scatterlist *sg; -int virtqueue_add_inbuf(struct virtqueue *vq, - struct scatterlist sg[], unsigned int num, - void *data, - gfp_t gfp); + for (sg = sgs[i]; sg; sg = sg_next(sg)) + total_sg++; + } -bool virtqueue_kick(struct virtqueue *vq); + if (likely(!vq->abstract)) + return vring_virtqueue_add_sgs(vq, sgs, total_sg, out_sgs, in_sgs, data, NULL, gfp); -void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len); + return vq->add_sgs(vq, sgs, total_sg, out_sgs, in_sgs, data, NULL, gfp); +} -void virtqueue_disable_cb(struct virtqueue *vq); +/** + * virtqueue_add_outbuf - expose output buffers to other end + * @vq: the struct virtqueue we're talking about. + * @sg: scatterlist (must be well-formed and terminated!) + * @num: the number of entries in @sg readable by other side + * @data: the token identifying the buffer. + * @gfp: how to do memory allocations (if necessary). + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO). + */ +static inline int virtqueue_add_outbuf(struct virtqueue *vq, + struct scatterlist *sg, + unsigned int num, + void *data, + gfp_t gfp) +{ + if (likely(!vq->abstract)) + return vring_virtqueue_add_sgs(vq, &sg, num, 1, 0, data, NULL, gfp); -bool virtqueue_enable_cb(struct virtqueue *vq); -bool virtqueue_enable_cb_delayed(struct virtqueue *vq); + return vq->add_sgs(vq, &sg, num, 1, 0, data, NULL, gfp); +} -void *virtqueue_detach_unused_buf(struct virtqueue *vq); -struct virtqueue *vring_new_virtqueue(unsigned int index, +/** + * virtqueue_add_inbuf - expose input buffers to other end + * @vq: the struct virtqueue we're talking about. + * @sg: scatterlist (must be well-formed and terminated!) + * @num: the number of entries in @sg writable by other side + * @data: the token identifying the buffer. + * @gfp: how to do memory allocations (if necessary). + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO). + */ +static inline int virtqueue_add_inbuf(struct virtqueue *vq, + struct scatterlist *sg, unsigned int num, - unsigned int vring_align, - struct virtio_device *vdev, - bool weak_barriers, - bool ctx, - void *pages, - bool (*notify)(struct virtqueue *vq), - void (*callback)(struct virtqueue *vq), - const char *name); -void vring_del_virtqueue(struct virtqueue *vq); + void *data, + gfp_t gfp) +{ + if (likely(!vq->abstract)) + return vring_virtqueue_add_sgs(vq, &sg, num, 0, 1, data, NULL, gfp); + + return vq->add_sgs(vq, &sg, num, 0, 1, data, NULL, gfp); +} + +/** + * virtqueue_add_inbuf_ctx - expose input buffers to other end + * @vq: the struct virtqueue we're talking about. + * @sg: scatterlist (must be well-formed and terminated!) + * @num: the number of entries in @sg writable by other side + * @data: the token identifying the buffer. + * @ctx: extra context for the token + * @gfp: how to do memory allocations (if necessary). + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO). + */ +static inline int virtqueue_add_inbuf_ctx(struct virtqueue *vq, + struct scatterlist *sg, + unsigned int num, + void *data, + void *ctx, + gfp_t gfp) +{ + if (likely(!vq->abstract)) + return vring_virtqueue_add_sgs(vq, &sg, num, 0, 1, data, ctx, gfp); + + return vq->add_sgs(vq, &sg, num, 0, 1, data, ctx, gfp); +} + +/** + * 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. + */ +static inline bool virtqueue_kick_prepare(struct virtqueue *vq) +{ + if (likely(!vq->abstract)) + return vring_virtqueue_kick_prepare(vq); + + return vq->kick_prepare(vq); +} + +/** + * virtqueue_notify - second half of split virtqueue_kick call. + * @vq: the struct virtqueue + * + * This does not need to be serialized. + * + * Returns false if host notify failed or queue is broken, otherwise true. + */ +static inline bool virtqueue_notify(struct virtqueue *vq) +{ + if (likely(!vq->abstract)) + return vring_virtqueue_notify(vq); + + return vq->notify(vq); +} + +/** + * virtqueue_kick - update after add_buf + * @vq: the struct virtqueue + * + * After one or more virtqueue_add_* 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). + * + * Returns false if kick failed, otherwise true. + */ +static inline bool virtqueue_kick(struct virtqueue *vq) +{ + if (virtqueue_kick_prepare(vq)) + return virtqueue_notify(vq); + + return true; +} + +/** + * virtqueue_enable_cb_prepare - restart callbacks after disable_cb + * @vq: the struct virtqueue we're talking about. + * + * This re-enables callbacks; it returns current queue state + * in an opaque unsigned value. This value should be later tested by + * virtqueue_poll, 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). + */ +static inline unsigned int virtqueue_enable_cb_prepare(struct virtqueue *vq) +{ + if (likely(!vq->abstract)) + return vring_virtqueue_enable_cb_prepare(vq); + + return vq->enable_cb_prepare(vq); +} + +/** + * virtqueue_poll - query pending used buffers + * @vq: the struct virtqueue we're talking about. + * @last_used_idx: virtqueue state (from call to virtqueue_enable_cb_prepare). + * + * Returns "true" if there are pending used buffers in the queue. + * + * This does not need to be serialized. + */ +static inline bool virtqueue_poll(struct virtqueue *vq, unsigned int idx) +{ + if (likely(!vq->abstract)) + return vring_virtqueue_poll(vq, idx); + + return vq->poll(vq, idx); +} + +/** + * 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). + */ +static inline bool virtqueue_enable_cb(struct virtqueue *vq) +{ + unsigned int opaque = virtqueue_enable_cb_prepare(vq); + + return !virtqueue_poll(vq, opaque); +} + +/** + * 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). + */ +static inline bool virtqueue_enable_cb_delayed(struct virtqueue *vq) +{ + if (likely(!vq->abstract)) + return vring_virtqueue_enable_cb_delayed(vq); + + return vq->enable_cb_delayed(vq); +} + +/** + * 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. + */ +static inline void virtqueue_disable_cb(struct virtqueue *vq) +{ + if (likely(!vq->abstract)) { + vring_virtqueue_disable_cb(vq); + return; + } + + vq->disable_cb(vq); +} + +/** + * virtqueue_get_buf_ctx - get the next used buffer + * @vq: the struct virtqueue we're talking about. + * @len: the length written into the buffer + * @ctx: extra context for the token + * + * If the device 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_*(). + */ +static inline void *virtqueue_get_buf_ctx(struct virtqueue *vq, + unsigned int *len, + void **ctx) +{ + if (likely(!vq->abstract)) + return vring_virtqueue_get_buf_ctx(vq, len, ctx); + + return vq->get_buf_ctx(vq, len, ctx); +} + +static inline void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len) +{ + if (likely(!vq->abstract)) + return vring_virtqueue_get_buf_ctx(vq, len, NULL); + + return vq->get_buf_ctx(vq, len, NULL); +} + +/** + * 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_*(). + * This is not valid on an active queue; it is useful for device + * shutdown or the reset queue. + */ +static inline void *virtqueue_detach_unused_buf(struct virtqueue *vq) +{ + return vq->detach_unused_buf(vq); +} #endif -- 2.20.1 _______________________________________________ Virtualization mailing list Virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linuxfoundation.org/mailman/listinfo/virtualization