virtio: support packed ring
Subsystems (6)
v2 cover virtio: support packed ring
Hello everyone,
This patch set implements packed ring support in virtio driver.
Some functional tests have been done with Jason's
packed ring implementation in vhost:
https://lkml.org/lkml/2018/7/3/33
Both of ping and netperf worked as expected.
v1 -> v2:
- Use READ_ONCE() to read event off_wrap and flags together (Jason);
- Add comments related to ccw (Jason);
RFC (v6) -> v1:
- Avoid extra virtio_wmb() in virtqueue_enable_cb_delayed_packed() when event idx is off (Jason);
- Fix bufs calculation in virtqueue_enable_cb_delayed_packed() (Jason);
- Test the state of the desc at used_idx instead of last_used_idx in virtqueue_enable_cb_delayed_packed() (Jason);
- Save wrap counter (as part of queue state) in the return value of virtqueue_enable_cb_prepare_packed();
- Refine the packed ring definitions in uapi;
- Rebase on the net-next tree;
RFC v5 -> RFC v6:
- Avoid tracking addr/len/flags when DMA API isn't used (MST/Jason);
- Define wrap counter as bool (Jason);
- Use ALIGN() in vring_init_packed() (Jason);
- Avoid using pointer to track next in detach_buf_packed() (Jason);
- Add comments for barriers (Jason);
- Don't enable RING_PACKED on ccw for now (noticed by Jason);
- Refine the memory barrier in virtqueue_poll();
- Add a missing memory barrier in virtqueue_enable_cb_delayed_packed();
- Remove the hacks in virtqueue_enable_cb_prepare_packed();
RFC v4 -> RFC v5:
- Save DMA addr, etc in desc state (Jason);
- Track used wrap counter;
RFC v3 -> RFC v4:
- Make ID allocation support out-of-order (Jason);
- Various fixes for EVENT_IDX support;
RFC v2 -> RFC v3:
- Split into small patches (Jason);
- Add helper virtqueue_use_indirect() (Jason);
- Just set id for the last descriptor of a list (Jason);
- Calculate the prev in virtqueue_add_packed() (Jason);
- Fix/improve desc suppression code (Jason/MST);
- Refine the code layout for XXX_split/packed and wrappers (MST);
- Fix the comments and API in uapi (MST);
- Remove the BUG_ON() for indirect (Jason);
- Some other refinements and bug fixes;
RFC v1 -> RFC v2:
- Add indirect descriptor support - compile test only;
- Add event suppression supprt - compile test only;
- Move vring_packed_init() out of uapi (Jason, MST);
- Merge two loops into one in virtqueue_add_packed() (Jason);
- Split vring_unmap_one() for packed ring and split ring (Jason);
- Avoid using '%' operator (Jason);
- Rename free_head -> next_avail_idx (Jason);
- Add comments for virtio_wmb() in virtqueue_add_packed() (Jason);
- Some other refinements and bug fixes;
Thanks!
Tiwei Bie (5):
virtio: add packed ring definitions
virtio_ring: support creating packed ring
virtio_ring: add packed ring support
virtio_ring: add event idx support in packed ring
virtio_ring: enable packed ring
drivers/s390/virtio/virtio_ccw.c | 14 + drivers/virtio/virtio_ring.c | 1365 ++++++++++++++++++++++------ include/linux/virtio_ring.h | 8 +- include/uapi/linux/virtio_config.h | 3 + include/uapi/linux/virtio_ring.h | 43 + 5 files changed, 1157 insertions(+), 276 deletions(-)
Signature · 1 line
2.18.0
PATCH v2 1/5 virtio: add packed ring definitions
--- include/uapi/linux/virtio_config.h | 3 +++ include/uapi/linux/virtio_ring.h | 43 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
include/uapi/linux/virtio_config.h +3 -0
@@ -75,6 +75,9 @@*/ #define VIRTIO_F_IOMMU_PLATFORM 33 +/* This feature indicates support for the packed virtqueue layout. */ +#define VIRTIO_F_RING_PACKED 34 +/* * Does the device support Single Root I/O Virtualization? */
include/uapi/linux/virtio_ring.h +43 -0
59 lines not on this page · raw message
Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 1/5] virtio: add packed ring definitions
Quoted 43 lines · Tiwei Bie
Signed-off-by Tiwei Bie <[email protected]>--- include/uapi/linux/virtio_config.h | 3 +++ include/uapi/linux/virtio_ring.h | 43 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)include/uapi/linux/virtio_config.h +3
-0@@ -75,6 +75,9 @@*/ #define VIRTIO_F_IOMMU_PLATFORM 33 +/* This feature indicates support for the packed virtqueue layout. */ +#define VIRTIO_F_RING_PACKED 34 +/* * Does the device support Single Root I/O Virtualization? */ include/uapi/linux/virtio_ring.h +8
-0@@ -44,6 +44,10 @@/* This means the buffer contains a list of buffer descriptors. */ #define VRING_DESC_F_INDIRECT 4 +/* Mark a descriptor as available or used. */ +#define VRING_DESC_F_AVAIL (1ul << 7) +#define VRING_DESC_F_USED (1ul << 15) +/* The Host uses this in used->flags to advise the Guest: don't kick me when * you add a buffer. It's unreliable, so it's simply an optimization. Guest * will still kick if it's out of buffers. */ @@ -53,6 +57,17 @@* optimization. */ #define VRING_AVAIL_F_NO_INTERRUPT 1 +/* Enable events. */ +#define VRING_EVENT_F_ENABLE 0x0 +/* Disable events. */ +#define VRING_EVENT_F_DISABLE 0x1
include/uapi/linux/virtio_ring.h line 73
Quoted 10 lines
include/uapi/linux/virtio_ring.h +7
-0+/* +* Enable events for a specific descriptor +* (as specified by Descriptor Ring Change Event Offset/Wrap Counter). +* Only valid if VIRTIO_RING_F_EVENT_IDX has been negotiated. +*/ +#define VRING_EVENT_F_DESC 0x2 +/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28
These are for the packed ring, right? Pls prefix accordingly. Also, you likely need macros for the wrap counters.
Quoted 11 lines
include/uapi/linux/virtio_ring.h +7
-0@@ -171,4 +186,32 @@ static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)return (__u16)(new_idx - event_idx -1 ) < (__u16)(new_idx - old); } +struct vring_packed_desc_event { +/* Descriptor Ring Change Event Offset/Wrap Counter. */ +__virtio16 off_wrap; +/* Descriptor Ring Change Event Flags. */ +__virtio16 flags; +}; +
include/uapi/linux/virtio_ring.h static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old) · line 205
Quoted 10 lines
include/uapi/linux/virtio_ring.h +10
-0+struct vring_packed_desc { +/* Buffer Address. */ +__virtio64 addr; +/* Buffer Length. */ +__virtio32 len; +/* Buffer ID. */ +__virtio16 id; +/* The flags depending on descriptor type. */ +__virtio16 flags; +};
Don't use __virtioXX types, just __leXX ones.
Quoted 14 lines
include/uapi/linux/virtio_ring.h +11
-0+ +struct vring_packed { +unsigned int num; + +struct vring_packed_desc *desc; + +struct vring_packed_desc_event *driver; + +struct vring_packed_desc_event *device; +}; +#endif /* _UAPI_LINUX_VIRTIO_RING_H */ Signature · 1 line
2.18.0
Re: [PATCH net-next v2 1/5] virtio: add packed ring definitions
Quoted 40 lines · Michael S. Tsirkin
Quoted 39 lines · Tiwei Bie
Signed-off-by Tiwei Bie <[email protected]>--- include/uapi/linux/virtio_config.h | 3 +++ include/uapi/linux/virtio_ring.h | 43 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)include/uapi/linux/virtio_config.h +3
-0@@ -75,6 +75,9 @@*/ #define VIRTIO_F_IOMMU_PLATFORM 33 +/* This feature indicates support for the packed virtqueue layout. */ +#define VIRTIO_F_RING_PACKED 34 +/* * Does the device support Single Root I/O Virtualization? */ include/uapi/linux/virtio_ring.h +4
-0@@ -44,6 +44,10 @@/* This means the buffer contains a list of buffer descriptors. */ #define VRING_DESC_F_INDIRECT 4 +/* Mark a descriptor as available or used. */ +#define VRING_DESC_F_AVAIL (1ul << 7) +#define VRING_DESC_F_USED (1ul << 15) +/* The Host uses this in used->flags to advise the Guest: don't kick me when * you add a buffer. It's unreliable, so it's simply an optimization. Guest * will still kick if it's out of buffers. */ @@ -53,6 +57,17 @@* optimization. */ #define VRING_AVAIL_F_NO_INTERRUPT 1
Quoted 16 lines
Quoted 4 lines
include/uapi/linux/virtio_ring.h +4
-0+/* Enable events. */ +#define VRING_EVENT_F_ENABLE 0x0 +/* Disable events. */ +#define VRING_EVENT_F_DISABLE 0x1include/uapi/linux/virtio_ring.h line 73
Quoted 10 lines
include/uapi/linux/virtio_ring.h +7
-0+/* +* Enable events for a specific descriptor +* (as specified by Descriptor Ring Change Event Offset/Wrap Counter). +* Only valid if VIRTIO_RING_F_EVENT_IDX has been negotiated. +*/ +#define VRING_EVENT_F_DESC 0x2 +/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28These are for the packed ring, right? Pls prefix accordingly.
How about something like this:
#define VRING_PACKED_DESC_F_AVAIL (1u << 7)
#define VRING_PACKED_DESC_F_USED (1u << 15)
#define VRING_PACKED_EVENT_F_ENABLE 0x0
#define VRING_PACKED_EVENT_F_DISABLE 0x1
#define VRING_PACKED_EVENT_F_DESC 0x2
Quoted 1 line
Also, you likely need macros for the wrap counters.
How about something like this:
#define VRING_PACKED_EVENT_WRAP_COUNTER_SHIFT 15
#define VRING_PACKED_EVENT_WRAP_COUNTER_MASK \
(1u << VRING_PACKED_WRAP_COUNTER_SHIFT)
#define VRING_PACKED_EVENT_OFFSET_MASK \ ((1u << VRING_PACKED_WRAP_COUNTER_SHIFT) - 1)
Quoted 16 lines
Quoted 16 lines
include/uapi/linux/virtio_ring.h +11
-0@@ -171,4 +186,32 @@ static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)return (__u16)(new_idx - event_idx -1 ) < (__u16)(new_idx - old); } +struct vring_packed_desc_event { +/* Descriptor Ring Change Event Offset/Wrap Counter. */ +__virtio16 off_wrap; +/* Descriptor Ring Change Event Flags. */ +__virtio16 flags; +}; + +struct vring_packed_desc { +/* Buffer Address. */ +__virtio64 addr; +/* Buffer Length. */
Quoted 8 lines
include/uapi/linux/virtio_ring.h static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old) · line 205
Quoted 6 lines
include/uapi/linux/virtio_ring.h +6
-0+__virtio32 len; +/* Buffer ID. */ +__virtio16 id; +/* The flags depending on descriptor type. */ +__virtio16 flags; +};Don't use
__virtioXXtypes, just__leXXones.
Got it, will do that.
Quoted 15 lines
Quoted 15 lines
include/uapi/linux/virtio_ring.h +11
-0+ +struct vring_packed { +unsigned int num; + +struct vring_packed_desc *desc; + +struct vring_packed_desc_event *driver; + +struct vring_packed_desc_event *device; +}; +#endif /* _UAPI_LINUX_VIRTIO_RING_H */ Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 1/5] virtio: add packed ring definitions
Quoted 58 lines · Tiwei Bie
Quoted 40 lines · Michael S. Tsirkin
Quoted 39 lines · Tiwei Bie
Signed-off-by Tiwei Bie <[email protected]>--- include/uapi/linux/virtio_config.h | 3 +++ include/uapi/linux/virtio_ring.h | 43 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)include/uapi/linux/virtio_config.h +3
-0@@ -75,6 +75,9 @@*/ #define VIRTIO_F_IOMMU_PLATFORM 33 +/* This feature indicates support for the packed virtqueue layout. */ +#define VIRTIO_F_RING_PACKED 34 +/* * Does the device support Single Root I/O Virtualization? */ include/uapi/linux/virtio_ring.h +4
-0@@ -44,6 +44,10 @@/* This means the buffer contains a list of buffer descriptors. */ #define VRING_DESC_F_INDIRECT 4 +/* Mark a descriptor as available or used. */ +#define VRING_DESC_F_AVAIL (1ul << 7) +#define VRING_DESC_F_USED (1ul << 15) +/* The Host uses this in used->flags to advise the Guest: don't kick me when * you add a buffer. It's unreliable, so it's simply an optimization. Guest * will still kick if it's out of buffers. */ @@ -53,6 +57,17 @@* optimization. */ #define VRING_AVAIL_F_NO_INTERRUPT 1Quoted 16 lines
Quoted 4 lines
include/uapi/linux/virtio_ring.h +4
-0+/* Enable events. */ +#define VRING_EVENT_F_ENABLE 0x0 +/* Disable events. */ +#define VRING_EVENT_F_DISABLE 0x1include/uapi/linux/virtio_ring.h line 73
Quoted 10 lines
include/uapi/linux/virtio_ring.h +7
-0+/* +* Enable events for a specific descriptor +* (as specified by Descriptor Ring Change Event Offset/Wrap Counter). +* Only valid if VIRTIO_RING_F_EVENT_IDX has been negotiated. +*/ +#define VRING_EVENT_F_DESC 0x2 +/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28These are for the packed ring, right? Pls prefix accordingly.
Quoted 4 lines
How about something like this:
#define
VRING_PACKED_DESC_F_AVAIL(1u << 7)
#defineVRING_PACKED_DESC_F_USED(1u << 15)
_F_ should be a bit number, not a shifted value. Yes I know current virtio_ring.h is inconsistent in that respect. changes welcome though we need to maintain the old names for the sake of old apps.
Quoted 4 lines
#define
VRING_PACKED_EVENT_F_ENABLE0x0
#defineVRING_PACKED_EVENT_F_DISABLE0x1
#defineVRING_PACKED_EVENT_F_DESC0x2
These are values, I think they should not have _F_. Yes I know current virtio_ring.h is inconsistent in that respect.
Quoted 6 lines
Quoted 2 lines
Also, you likely need macros for the wrap counters.
How about something like this:
#define
VRING_PACKED_EVENT_WRAP_COUNTER_SHIFT15
Given it's a single bit, maybe evenVRING_PACKED_EVENT_F_WRAP_CTR
Quoted 4 lines
#define
VRING_PACKED_EVENT_WRAP_COUNTER_MASK\
(1u <<VRING_PACKED_WRAP_COUNTER_SHIFT)#define VRING_PACKED_EVENT_OFFSET_MASK \ ((1u << VRING_PACKED_WRAP_COUNTER_SHIFT) - 1)
I'm not sure the mask is justified.
Quoted 42 lines
Quoted 16 lines
Quoted 16 lines
include/uapi/linux/virtio_ring.h +11
-0@@ -171,4 +186,32 @@ static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)return (__u16)(new_idx - event_idx -1 ) < (__u16)(new_idx - old); } +struct vring_packed_desc_event { +/* Descriptor Ring Change Event Offset/Wrap Counter. */ +__virtio16 off_wrap; +/* Descriptor Ring Change Event Flags. */ +__virtio16 flags; +}; + +struct vring_packed_desc { +/* Buffer Address. */ +__virtio64 addr; +/* Buffer Length. */ Quoted 8 lines
include/uapi/linux/virtio_ring.h static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old) · line 205
Quoted 6 lines
include/uapi/linux/virtio_ring.h +6
-0+__virtio32 len; +/* Buffer ID. */ +__virtio16 id; +/* The flags depending on descriptor type. */ +__virtio16 flags; +};Don't use
__virtioXXtypes, just__leXXones.Got it, will do that.
Quoted 15 lines
Quoted 15 lines
include/uapi/linux/virtio_ring.h +11
-0+ +struct vring_packed { +unsigned int num; + +struct vring_packed_desc *desc; + +struct vring_packed_desc_event *driver; + +struct vring_packed_desc_event *device; +}; +#endif /* _UAPI_LINUX_VIRTIO_RING_H */ Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 1/5] virtio: add packed ring definitions
In reply to Tiwei BieQuoted 58 lines · Tiwei Bie
Quoted 40 lines · Michael S. Tsirkin
Quoted 39 lines · Tiwei Bie
Signed-off-by Tiwei Bie <[email protected]>--- include/uapi/linux/virtio_config.h | 3 +++ include/uapi/linux/virtio_ring.h | 43 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)include/uapi/linux/virtio_config.h +3
-0@@ -75,6 +75,9 @@*/ #define VIRTIO_F_IOMMU_PLATFORM 33 +/* This feature indicates support for the packed virtqueue layout. */ +#define VIRTIO_F_RING_PACKED 34 +/* * Does the device support Single Root I/O Virtualization? */ include/uapi/linux/virtio_ring.h +4
-0@@ -44,6 +44,10 @@/* This means the buffer contains a list of buffer descriptors. */ #define VRING_DESC_F_INDIRECT 4 +/* Mark a descriptor as available or used. */ +#define VRING_DESC_F_AVAIL (1ul << 7) +#define VRING_DESC_F_USED (1ul << 15) +/* The Host uses this in used->flags to advise the Guest: don't kick me when * you add a buffer. It's unreliable, so it's simply an optimization. Guest * will still kick if it's out of buffers. */ @@ -53,6 +57,17 @@* optimization. */ #define VRING_AVAIL_F_NO_INTERRUPT 1Quoted 16 lines
Quoted 4 lines
include/uapi/linux/virtio_ring.h +4
-0+/* Enable events. */ +#define VRING_EVENT_F_ENABLE 0x0 +/* Disable events. */ +#define VRING_EVENT_F_DISABLE 0x1include/uapi/linux/virtio_ring.h line 73
Quoted 10 lines
include/uapi/linux/virtio_ring.h +7
-0+/* +* Enable events for a specific descriptor +* (as specified by Descriptor Ring Change Event Offset/Wrap Counter). +* Only valid if VIRTIO_RING_F_EVENT_IDX has been negotiated. +*/ +#define VRING_EVENT_F_DESC 0x2 +/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28These are for the packed ring, right? Pls prefix accordingly.
Quoted 4 lines
How about something like this:
#define
VRING_PACKED_DESC_F_AVAIL(1u << 7)
#defineVRING_PACKED_DESC_F_USED(1u << 15)
_F_ should be a bit number, not a shifted value. Yes I know current virtio_ring.h is inconsistent in that respect. changes welcome though we need to maintain the old names for the sake of old apps.
Quoted 4 lines
#define
VRING_PACKED_EVENT_F_ENABLE0x0
#defineVRING_PACKED_EVENT_F_DISABLE0x1
#defineVRING_PACKED_EVENT_F_DESC0x2
These are values, I think they should not have _F_. Yes I know current virtio_ring.h is inconsistent in that respect.
Quoted 6 lines
Quoted 2 lines
Also, you likely need macros for the wrap counters.
How about something like this:
#define
VRING_PACKED_EVENT_WRAP_COUNTER_SHIFT15
Given it's a single bit, maybe evenVRING_PACKED_EVENT_F_WRAP_CTR
Quoted 4 lines
#define
VRING_PACKED_EVENT_WRAP_COUNTER_MASK\
(1u <<VRING_PACKED_WRAP_COUNTER_SHIFT)#define VRING_PACKED_EVENT_OFFSET_MASK \ ((1u << VRING_PACKED_WRAP_COUNTER_SHIFT) - 1)
I'm not sure the mask is justified.
Quoted 42 lines
Quoted 16 lines
Quoted 16 lines
include/uapi/linux/virtio_ring.h +11
-0@@ -171,4 +186,32 @@ static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)return (__u16)(new_idx - event_idx -1 ) < (__u16)(new_idx - old); } +struct vring_packed_desc_event { +/* Descriptor Ring Change Event Offset/Wrap Counter. */ +__virtio16 off_wrap; +/* Descriptor Ring Change Event Flags. */ +__virtio16 flags; +}; + +struct vring_packed_desc { +/* Buffer Address. */ +__virtio64 addr; +/* Buffer Length. */ Quoted 8 lines
include/uapi/linux/virtio_ring.h static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old) · line 205
Quoted 6 lines
include/uapi/linux/virtio_ring.h +6
-0+__virtio32 len; +/* Buffer ID. */ +__virtio16 id; +/* The flags depending on descriptor type. */ +__virtio16 flags; +};Don't use
__virtioXXtypes, just__leXXones.Got it, will do that.
Quoted 15 lines
Quoted 15 lines
include/uapi/linux/virtio_ring.h +11
-0+ +struct vring_packed { +unsigned int num; + +struct vring_packed_desc *desc; + +struct vring_packed_desc_event *driver; + +struct vring_packed_desc_event *device; +}; +#endif /* _UAPI_LINUX_VIRTIO_RING_H */ Signature · 1 line
2.18.0
Re: [PATCH net-next v2 1/5] virtio: add packed ring definitions
In reply to Michael S. TsirkinQuoted 40 lines · Michael S. Tsirkin
Quoted 39 lines · Tiwei Bie
Signed-off-by Tiwei Bie <[email protected]>--- include/uapi/linux/virtio_config.h | 3 +++ include/uapi/linux/virtio_ring.h | 43 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)include/uapi/linux/virtio_config.h +3
-0@@ -75,6 +75,9 @@*/ #define VIRTIO_F_IOMMU_PLATFORM 33 +/* This feature indicates support for the packed virtqueue layout. */ +#define VIRTIO_F_RING_PACKED 34 +/* * Does the device support Single Root I/O Virtualization? */ include/uapi/linux/virtio_ring.h +4
-0@@ -44,6 +44,10 @@/* This means the buffer contains a list of buffer descriptors. */ #define VRING_DESC_F_INDIRECT 4 +/* Mark a descriptor as available or used. */ +#define VRING_DESC_F_AVAIL (1ul << 7) +#define VRING_DESC_F_USED (1ul << 15) +/* The Host uses this in used->flags to advise the Guest: don't kick me when * you add a buffer. It's unreliable, so it's simply an optimization. Guest * will still kick if it's out of buffers. */ @@ -53,6 +57,17 @@* optimization. */ #define VRING_AVAIL_F_NO_INTERRUPT 1
Quoted 16 lines
Quoted 4 lines
include/uapi/linux/virtio_ring.h +4
-0+/* Enable events. */ +#define VRING_EVENT_F_ENABLE 0x0 +/* Disable events. */ +#define VRING_EVENT_F_DISABLE 0x1include/uapi/linux/virtio_ring.h line 73
Quoted 10 lines
include/uapi/linux/virtio_ring.h +7
-0+/* +* Enable events for a specific descriptor +* (as specified by Descriptor Ring Change Event Offset/Wrap Counter). +* Only valid if VIRTIO_RING_F_EVENT_IDX has been negotiated. +*/ +#define VRING_EVENT_F_DESC 0x2 +/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28These are for the packed ring, right? Pls prefix accordingly.
How about something like this:
#define VRING_PACKED_DESC_F_AVAIL (1u << 7)
#define VRING_PACKED_DESC_F_USED (1u << 15)
#define VRING_PACKED_EVENT_F_ENABLE 0x0
#define VRING_PACKED_EVENT_F_DISABLE 0x1
#define VRING_PACKED_EVENT_F_DESC 0x2
Quoted 1 line
Also, you likely need macros for the wrap counters.
How about something like this:
#define VRING_PACKED_EVENT_WRAP_COUNTER_SHIFT 15
#define VRING_PACKED_EVENT_WRAP_COUNTER_MASK \
(1u << VRING_PACKED_WRAP_COUNTER_SHIFT)
#define VRING_PACKED_EVENT_OFFSET_MASK \ ((1u << VRING_PACKED_WRAP_COUNTER_SHIFT) - 1)
Quoted 16 lines
Quoted 16 lines
include/uapi/linux/virtio_ring.h +11
-0@@ -171,4 +186,32 @@ static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)return (__u16)(new_idx - event_idx -1 ) < (__u16)(new_idx - old); } +struct vring_packed_desc_event { +/* Descriptor Ring Change Event Offset/Wrap Counter. */ +__virtio16 off_wrap; +/* Descriptor Ring Change Event Flags. */ +__virtio16 flags; +}; + +struct vring_packed_desc { +/* Buffer Address. */ +__virtio64 addr; +/* Buffer Length. */
Quoted 8 lines
include/uapi/linux/virtio_ring.h static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old) · line 205
Quoted 6 lines
include/uapi/linux/virtio_ring.h +6
-0+__virtio32 len; +/* Buffer ID. */ +__virtio16 id; +/* The flags depending on descriptor type. */ +__virtio16 flags; +};Don't use
__virtioXXtypes, just__leXXones.
Got it, will do that.
Quoted 15 lines
Quoted 15 lines
include/uapi/linux/virtio_ring.h +11
-0+ +struct vring_packed { +unsigned int num; + +struct vring_packed_desc *desc; + +struct vring_packed_desc_event *driver; + +struct vring_packed_desc_event *device; +}; +#endif /* _UAPI_LINUX_VIRTIO_RING_H */ Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 1/5] virtio: add packed ring definitions
In reply to Tiwei BieQuoted 43 lines · Tiwei Bie
Signed-off-by Tiwei Bie <[email protected]>--- include/uapi/linux/virtio_config.h | 3 +++ include/uapi/linux/virtio_ring.h | 43 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)include/uapi/linux/virtio_config.h +3
-0@@ -75,6 +75,9 @@*/ #define VIRTIO_F_IOMMU_PLATFORM 33 +/* This feature indicates support for the packed virtqueue layout. */ +#define VIRTIO_F_RING_PACKED 34 +/* * Does the device support Single Root I/O Virtualization? */ include/uapi/linux/virtio_ring.h +8
-0@@ -44,6 +44,10 @@/* This means the buffer contains a list of buffer descriptors. */ #define VRING_DESC_F_INDIRECT 4 +/* Mark a descriptor as available or used. */ +#define VRING_DESC_F_AVAIL (1ul << 7) +#define VRING_DESC_F_USED (1ul << 15) +/* The Host uses this in used->flags to advise the Guest: don't kick me when * you add a buffer. It's unreliable, so it's simply an optimization. Guest * will still kick if it's out of buffers. */ @@ -53,6 +57,17 @@* optimization. */ #define VRING_AVAIL_F_NO_INTERRUPT 1 +/* Enable events. */ +#define VRING_EVENT_F_ENABLE 0x0 +/* Disable events. */ +#define VRING_EVENT_F_DISABLE 0x1
include/uapi/linux/virtio_ring.h line 73
Quoted 10 lines
include/uapi/linux/virtio_ring.h +7
-0+/* +* Enable events for a specific descriptor +* (as specified by Descriptor Ring Change Event Offset/Wrap Counter). +* Only valid if VIRTIO_RING_F_EVENT_IDX has been negotiated. +*/ +#define VRING_EVENT_F_DESC 0x2 +/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28
These are for the packed ring, right? Pls prefix accordingly. Also, you likely need macros for the wrap counters.
Quoted 11 lines
include/uapi/linux/virtio_ring.h +7
-0@@ -171,4 +186,32 @@ static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)return (__u16)(new_idx - event_idx -1 ) < (__u16)(new_idx - old); } +struct vring_packed_desc_event { +/* Descriptor Ring Change Event Offset/Wrap Counter. */ +__virtio16 off_wrap; +/* Descriptor Ring Change Event Flags. */ +__virtio16 flags; +}; +
include/uapi/linux/virtio_ring.h static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old) · line 205
Quoted 10 lines
include/uapi/linux/virtio_ring.h +10
-0+struct vring_packed_desc { +/* Buffer Address. */ +__virtio64 addr; +/* Buffer Length. */ +__virtio32 len; +/* Buffer ID. */ +__virtio16 id; +/* The flags depending on descriptor type. */ +__virtio16 flags; +};
Don't use __virtioXX types, just __leXX ones.
Quoted 14 lines
include/uapi/linux/virtio_ring.h +11
-0+ +struct vring_packed { +unsigned int num; + +struct vring_packed_desc *desc; + +struct vring_packed_desc_event *driver; + +struct vring_packed_desc_event *device; +}; +#endif /* _UAPI_LINUX_VIRTIO_RING_H */ Signature · 1 line
2.18.0
PATCH v2 1/5 virtio: add packed ring definitions
In reply to Tiwei Bie--- include/uapi/linux/virtio_config.h | 3 +++ include/uapi/linux/virtio_ring.h | 43 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
include/uapi/linux/virtio_config.h +3 -0
@@ -75,6 +75,9 @@*/ #define VIRTIO_F_IOMMU_PLATFORM 33 +/* This feature indicates support for the packed virtqueue layout. */ +#define VIRTIO_F_RING_PACKED 34 +/* * Does the device support Single Root I/O Virtualization? */
include/uapi/linux/virtio_ring.h +43 -0
59 lines not on this page · raw message
Signature · 1 line
2.18.0
PATCH v2 2/5 virtio_ring: support creating packed ring
In reply to Tiwei BieThis commit introduces the support for creating packed ring. All split ring specific functions are added _split suffix. Some necessary stubs for packed ring are also added.
--- drivers/virtio/virtio_ring.c | 801 +++++++++++++++++++++++------------ include/linux/virtio_ring.h | 8 +- 2 files changed, 546 insertions(+), 263 deletions(-)
drivers/virtio/virtio_ring.c +539 -262
@@ -60,11 +60,15 @@ struct vring_desc_state {struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ }; +struct vring_desc_state_packed { +int next;/* The next desc state. */ +}; +struct vring_virtqueue {struct virtqueue vq; -/* Actual memory layout for this queue */ -struct vring vring; +/* Is this a packed ring? */ +bool packed;/* Can we use weak barriers? */ bool weak_barriers;
@@ -86,11 +90,39 @@ struct vring_virtqueue {/* Last used index we've seen. */ u16 last_used_idx; -/* Last written value to avail->flags */ -u16 avail_flags_shadow; +union { +/* Available for split ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring vring; -/* Last written value to avail->idx in guest byte order */ -u16 avail_idx_shadow; +/* Last written value to avail->flags */ +u16 avail_flags_shadow; + +/* Last written value to avail->idx in +* guest byte order. */ +u16 avail_idx_shadow; + }; + +/* Available for packed ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring_packed vring_packed; + +/* Driver ring wrap counter. */ +bool avail_wrap_counter; + +/* Device ring wrap counter. */ +bool used_wrap_counter; + +/* Index of the next avail descriptor. */ +u16 next_avail_idx; + +/* Last written value to driver->flags in +* guest byte order. */ +u16 event_flags_shadow; + }; + };/* How to notify other side. FIXME: commonalize hcalls! */ bool (*notify)(struct virtqueue *vq);
@@ -110,11 +142,24 @@ struct vring_virtqueue {#endif /* Per-descriptor state. */ -struct vring_desc_state desc_state[]; +union { +struct vring_desc_state desc_state[1 ]; +struct vring_desc_state_packed desc_state_packed[1 ]; + }; };#define to_vvq (_vq) container_of(_vq, struct vring_virtqueue, vq) +static inline bool virtqueue_use_indirect (struct virtqueue *_vq, +unsigned int total_sg) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +/* If the host supports indirect descriptor tables, and we have multiple +* buffers, then go indirect. FIXME: tune this threshold */ +return (vq->indirect && total_sg >1 && vq->vq.num_free); +} +/* * Modern virtio devices have feature bits to specify whether they need a * quirk and bypass the IOMMU. If not there, just use the DMA API.
@@ -200,8 +245,17 @@ static dma_addr_t vring_map_single(const struct vring_virtqueue *vq, cpu_addr, size, direction); } -static void vring_unmap_one (const struct vring_virtqueue *vq, -struct vring_desc *desc) +static int vring_mapping_error (const struct vring_virtqueue *vq, +dma_addr_t addr) +{ +if (!vring_use_dma_api (vq->vq.vdev)) +return 0 ; + +return dma_mapping_error (vring_dma_dev (vq), addr); +} + +static void vring_unmap_one_split (const struct vring_virtqueue *vq, +struct vring_desc *desc) {u16 flags;
@@ -225,17 +279,9 @@ static void vring_unmap_one(const struct vring_virtqueue *vq, } } -static int vring_mapping_error (const struct vring_virtqueue *vq, -dma_addr_t addr) -{ -if (!vring_use_dma_api (vq->vq.vdev)) -return 0 ; - -return dma_mapping_error (vring_dma_dev (vq), addr); -} - -static struct vring_desc *alloc_indirect (struct virtqueue *_vq, -unsigned int total_sg,gfp_t gfp) +static struct vring_desc *alloc_indirect_split (struct virtqueue *_vq, +unsigned int total_sg, +gfp_t gfp) {struct vring_desc *desc;unsigned int i;
@@ -256,14 +302,14 @@ static struct vring_desc *alloc_indirect(struct virtqueue *_vq,return desc; } -static inline int virtqueue_add (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) +static inline int virtqueue_add_split (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) {struct vring_virtqueue *vq =to_vvq (_vq);struct scatterlist *sg;
@@ -299,10 +345,8 @@ static inline int virtqueue_add(struct virtqueue *_vq, head = vq->free_head; -/* If the host supports indirect descriptor tables, and we have multiple -* buffers, then go indirect. FIXME: tune this threshold */ -if (vq->indirect && total_sg >1 && vq->vq.num_free) - desc =alloc_indirect (_vq, total_sg, gfp); +if (virtqueue_use_indirect (_vq, total_sg)) + desc =alloc_indirect_split (_vq, total_sg, gfp);else { desc =NULL ;WARN_ON_ONCE (total_sg > vq->vring.num && !vq->indirect);
@@ -423,7 +467,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,for (n =0 ; n < total_sg; n++) {if (i == err_idx)break ; -vring_unmap_one (vq, &desc[i]); +vring_unmap_one_split (vq, &desc[i]); i =virtio16_to_cpu (_vq->vdev, vq->vring.desc[i].next); }
@@ -434,6 +478,355 @@ static inline int virtqueue_add(struct virtqueue *_vq,return -EIO ; } +static bool virtqueue_kick_prepare_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +u16 new, old; +bool needs_kick; + +START_USE (vq); +/* We need to expose available array entries before checking avail +* event. */ +virtio_mb (vq->weak_barriers); + + old = vq->avail_idx_shadow - vq->num_added; + new = vq->avail_idx_shadow; + 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 (virtio16_to_cpu (_vq->vdev,vring_avail_event (&vq->vring)), + new, old); + }else { + needs_kick = !(vq->vring.used->flags &cpu_to_virtio16 (_vq->vdev,VRING_USED_F_NO_NOTIFY )); + } +END_USE (vq); +return needs_kick; +} + +static void detach_buf_split (struct vring_virtqueue *vq,unsigned int head, +void **ctx) +{ +unsigned int i, j; +__virtio16 nextflag =cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_NEXT ); + +/* Clear data ptr. */ + vq->desc_state[head].data =NULL ; + +/* Put back on free list: unmap first-level descriptors and find end */ + i = head; + +while (vq->vring.desc[i].flags & nextflag) { +vring_unmap_one_split (vq, &vq->vring.desc[i]); + i =virtio16_to_cpu (vq->vq.vdev, vq->vring.desc[i].next); + vq->vq.num_free++; + } + +vring_unmap_one_split (vq, &vq->vring.desc[i]); + vq->vring.desc[i].next =cpu_to_virtio16 (vq->vq.vdev, vq->free_head); + vq->free_head = head; + +/* Plus final descriptor */ + vq->vq.num_free++; + +if (vq->indirect) { +struct vring_desc *indir_desc = vq->desc_state[head].indir_desc; +u32 len; + +/* Free the indirect table, if any, now that it's unmapped. */ +if (!indir_desc) +return ; + + len =virtio32_to_cpu (vq->vq.vdev, vq->vring.desc[head].len); + +BUG_ON (!(vq->vring.desc[head].flags & +cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_INDIRECT ))); +BUG_ON (len ==0 || len %sizeof (struct vring_desc )); + +for (j =0 ; j < len /sizeof (struct vring_desc ); j++) +vring_unmap_one_split (vq, &indir_desc[j]); + +kfree (indir_desc); + vq->desc_state[head].indir_desc =NULL ; + }else if (ctx) { + *ctx = vq->desc_state[head].indir_desc; + } +} + +static inline bool more_used_split (const struct vring_virtqueue *vq) +{ +return vq->last_used_idx !=virtio16_to_cpu (vq->vq.vdev, vq->vring.used->idx); +} + +static void *virtqueue_get_buf_ctx_split (struct virtqueue *_vq, +unsigned int *len, +void **ctx) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +void *ret; +unsigned int i; +u16 last_used; + +START_USE (vq); + +if (unlikely (vq->broken)) { +END_USE (vq); +return NULL ; + } + +if (!more_used_split (vq)) { +pr_debug ("No more buffers in queue\n" ); +END_USE (vq); +return NULL ; + } + +/* Only get used array entries after they have been exposed by host. */ +virtio_rmb (vq->weak_barriers); + + last_used = (vq->last_used_idx & (vq->vring.num -1 )); + i =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].id); + *len =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].len); + +if (unlikely (i >= vq->vring.num)) { +BAD_RING (vq,"id %u out of range\n" , i); +return NULL ; + } +if (unlikely (!vq->desc_state[i].data)) { +BAD_RING (vq,"id %u is not a head!\n" , i); +return NULL ; + } + +/* detach_buf_split clears data, so grab it now. */ + ret = vq->desc_state[i].data; +detach_buf_split (vq, i, ctx); + vq->last_used_idx++; +/* If we expect an interrupt for the next entry, tell host +* by writing event index and flush out the write before +* the read in the next get_buf call. */ +if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) +virtio_store_mb (vq->weak_barriers, + &vring_used_event (&vq->vring), +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx)); + +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif + +END_USE (vq); +return ret; +} + +static void virtqueue_disable_cb_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) { + vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +} + +static unsigned virtqueue_enable_cb_prepare_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +u16 last_used_idx; + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ +/* Depending on the VIRTIO_RING_F_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. */ +if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { + vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +vring_used_event (&vq->vring) =cpu_to_virtio16 (_vq->vdev, last_used_idx = vq->last_used_idx); +END_USE (vq); +return last_used_idx; +} + +static bool virtqueue_poll_split (struct virtqueue *_vq,unsigned last_used_idx) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +virtio_mb (vq->weak_barriers); +return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); +} + +static bool virtqueue_enable_cb_delayed_split (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 update the event index to keep code simple. */ +if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { + vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +/* TODO: tune this threshold */ + bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) *3 /4 ; + +virtio_store_mb (vq->weak_barriers, + &vring_used_event (&vq->vring), +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx + bufs)); + +if (unlikely ((u16)(virtio16_to_cpu (_vq->vdev, vq->vring.used->idx) - vq->last_used_idx) > bufs)) { +END_USE (vq); +return false; + } + +END_USE (vq); +return true; +} + +static void *virtqueue_detach_unused_buf_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +unsigned int i; +void *buf; + +START_USE (vq); + +for (i =0 ; i < vq->vring.num; i++) { +if (!vq->desc_state[i].data) +continue ; +/* detach_buf clears data, so grab it now. */ + buf = vq->desc_state[i].data; +detach_buf_split (vq, i,NULL ); + vq->avail_idx_shadow--; + vq->vring.avail->idx =cpu_to_virtio16 (_vq->vdev, vq->avail_idx_shadow); +END_USE (vq); +return buf; + } +/* That should have freed everything. */ +BUG_ON (vq->vq.num_free != vq->vring.num); + +END_USE (vq); +return NULL ; +} + +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +* +* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver; +* +* // Device Event Suppression +* struct vring_packed_desc_event device; +* }; +*/ +static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +} + +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +} + +static inline int virtqueue_add_packed (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) +{ +return -EIO ; +} + +static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) +{ +return false; +} + +static inline bool more_used_packed (const struct vring_virtqueue *vq) +{ +return false; +} + +static void *virtqueue_get_buf_ctx_packed (struct virtqueue *_vq, +unsigned int *len, +void **ctx) +{ +return NULL ; +} + +static void virtqueue_disable_cb_packed (struct virtqueue *_vq) +{ +} + +static unsigned virtqueue_enable_cb_prepare_packed (struct virtqueue *_vq) +{ +return 0 ; +} + +static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned last_used_idx) +{ +return false; +} + +static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) +{ +return false; +} + +static void *virtqueue_detach_unused_buf_packed (struct virtqueue *_vq) +{ +return NULL ; +} + +static inline int virtqueue_add (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) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +return vq->packed ?virtqueue_add_packed (_vq, sgs, total_sg, out_sgs, + in_sgs, data, ctx, gfp) : +virtqueue_add_split (_vq, sgs, total_sg, out_sgs, + in_sgs, data, ctx, gfp); +} +/** * virtqueue_add_sgs - expose buffers to other end * @vq: the struct virtqueue we're talking about.
@@ -550,34 +943,9 @@ EXPORT_SYMBOL_GPL(virtqueue_add_inbuf_ctx);bool virtqueue_kick_prepare (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 new, old; -bool needs_kick; -START_USE (vq); -/* We need to expose available array entries before checking avail -* event. */ -virtio_mb (vq->weak_barriers); - - old = vq->avail_idx_shadow - vq->num_added; - new = vq->avail_idx_shadow; - 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 (virtio16_to_cpu (_vq->vdev,vring_avail_event (&vq->vring)), - new, old); - }else { - needs_kick = !(vq->vring.used->flags &cpu_to_virtio16 (_vq->vdev,VRING_USED_F_NO_NOTIFY )); - } -END_USE (vq); -return needs_kick; +return vq->packed ?virtqueue_kick_prepare_packed (_vq) : +virtqueue_kick_prepare_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_kick_prepare);
@@ -625,58 +993,9 @@ bool virtqueue_kick(struct virtqueue *vq) }EXPORT_SYMBOL_GPL (virtqueue_kick); -static void detach_buf (struct vring_virtqueue *vq,unsigned int head, -void **ctx) -{ -unsigned int i, j; -__virtio16 nextflag =cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_NEXT ); - -/* Clear data ptr. */ - vq->desc_state[head].data =NULL ; - -/* Put back on free list: unmap first-level descriptors and find end */ - i = head; - -while (vq->vring.desc[i].flags & nextflag) { -vring_unmap_one (vq, &vq->vring.desc[i]); - i =virtio16_to_cpu (vq->vq.vdev, vq->vring.desc[i].next); - vq->vq.num_free++; - } - -vring_unmap_one (vq, &vq->vring.desc[i]); - vq->vring.desc[i].next =cpu_to_virtio16 (vq->vq.vdev, vq->free_head); - vq->free_head = head; - -/* Plus final descriptor */ - vq->vq.num_free++; - -if (vq->indirect) { -struct vring_desc *indir_desc = vq->desc_state[head].indir_desc; -u32 len; - -/* Free the indirect table, if any, now that it's unmapped. */ -if (!indir_desc) -return ; - - len =virtio32_to_cpu (vq->vq.vdev, vq->vring.desc[head].len); - -BUG_ON (!(vq->vring.desc[head].flags & -cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_INDIRECT ))); -BUG_ON (len ==0 || len %sizeof (struct vring_desc )); - -for (j =0 ; j < len /sizeof (struct vring_desc ); j++) -vring_unmap_one (vq, &indir_desc[j]); - -kfree (indir_desc); - vq->desc_state[head].indir_desc =NULL ; - }else if (ctx) { - *ctx = vq->desc_state[head].indir_desc; - } -} -static inline bool more_used (const struct vring_virtqueue *vq) { -return vq->last_used_idx !=virtio16_to_cpu (vq->vq.vdev, vq->vring.used->idx); +return vq->packed ?more_used_packed (vq) :more_used_split (vq); }/**
@@ -699,57 +1018,9 @@ void *virtqueue_get_buf_ctx(struct virtqueue *_vq, unsigned int *len, void **ctx) {struct vring_virtqueue *vq =to_vvq (_vq); -void *ret; -unsigned int i; -u16 last_used; -START_USE (vq); - -if (unlikely (vq->broken)) { -END_USE (vq); -return NULL ; - } - -if (!more_used (vq)) { -pr_debug ("No more buffers in queue\n" ); -END_USE (vq); -return NULL ; - } - -/* Only get used array entries after they have been exposed by host. */ -virtio_rmb (vq->weak_barriers); - - last_used = (vq->last_used_idx & (vq->vring.num -1 )); - i =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].id); - *len =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].len); - -if (unlikely (i >= vq->vring.num)) { -BAD_RING (vq,"id %u out of range\n" , i); -return NULL ; - } -if (unlikely (!vq->desc_state[i].data)) { -BAD_RING (vq,"id %u is not a head!\n" , i); -return NULL ; - } - -/* detach_buf clears data, so grab it now. */ - ret = vq->desc_state[i].data; -detach_buf (vq, i, ctx); - vq->last_used_idx++; -/* If we expect an interrupt for the next entry, tell host -* by writing event index and flush out the write before -* the read in the next get_buf call. */ -if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) -virtio_store_mb (vq->weak_barriers, - &vring_used_event (&vq->vring), -cpu_to_virtio16 (_vq->vdev, vq->last_used_idx)); - -#ifdef DEBUG - vq->last_add_time_valid = false; -#endif - -END_USE (vq); -return ret; +return vq->packed ?virtqueue_get_buf_ctx_packed (_vq, len, ctx) : +virtqueue_get_buf_ctx_split (_vq, len, ctx); }EXPORT_SYMBOL_GPL (virtqueue_get_buf_ctx);
@@ -771,12 +1042,10 @@ void virtqueue_disable_cb(struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) { - vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } - +if (vq->packed) +virtqueue_disable_cb_packed (_vq); +else +virtqueue_disable_cb_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_disable_cb);
@@ -795,23 +1064,9 @@ EXPORT_SYMBOL_GPL(virtqueue_disable_cb);unsigned virtqueue_enable_cb_prepare (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 last_used_idx; -START_USE (vq); - -/* We optimistically turn back on interrupts, then check if there was -* more to do. */ -/* Depending on the VIRTIO_RING_F_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. */ -if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { - vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } -vring_used_event (&vq->vring) =cpu_to_virtio16 (_vq->vdev, last_used_idx = vq->last_used_idx); -END_USE (vq); -return last_used_idx; +return vq->packed ?virtqueue_enable_cb_prepare_packed (_vq) : +virtqueue_enable_cb_prepare_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_enable_cb_prepare);
@@ -828,8 +1083,8 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); -virtio_mb (vq->weak_barriers); -return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); +return vq->packed ?virtqueue_poll_packed (_vq, last_used_idx) : +virtqueue_poll_split (_vq, last_used_idx); }EXPORT_SYMBOL_GPL (virtqueue_poll);
@@ -867,34 +1122,9 @@ 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 update the event index to keep code simple. */ -if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { - vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } -/* TODO: tune this threshold */ - bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) *3 /4 ; - -virtio_store_mb (vq->weak_barriers, - &vring_used_event (&vq->vring), -cpu_to_virtio16 (_vq->vdev, vq->last_used_idx + bufs)); - -if (unlikely ((u16)(virtio16_to_cpu (_vq->vdev, vq->vring.used->idx) - vq->last_used_idx) > bufs)) { -END_USE (vq); -return false; - } - -END_USE (vq); -return true; +return vq->packed ?virtqueue_enable_cb_delayed_packed (_vq) : +virtqueue_enable_cb_delayed_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_enable_cb_delayed);
@@ -909,27 +1139,9 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed);void *virtqueue_detach_unused_buf (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -unsigned int i; -void *buf; -START_USE (vq); - -for (i =0 ; i < vq->vring.num; i++) { -if (!vq->desc_state[i].data) -continue ; -/* detach_buf clears data, so grab it now. */ - buf = vq->desc_state[i].data; -detach_buf (vq, i,NULL ); - vq->avail_idx_shadow--; - vq->vring.avail->idx =cpu_to_virtio16 (_vq->vdev, vq->avail_idx_shadow); -END_USE (vq); -return buf; - } -/* That should have freed everything. */ -BUG_ON (vq->vq.num_free != vq->vring.num); - -END_USE (vq); -return NULL ; +return vq->packed ?virtqueue_detach_unused_buf_packed (_vq) : +virtqueue_detach_unused_buf_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_detach_unused_buf);
@@ -954,7 +1166,8 @@ irqreturn_t vring_interrupt(int irq, void *_vq)EXPORT_SYMBOL_GPL (vring_interrupt); struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool context,
@@ -962,19 +1175,22 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *), const char *name) { - unsigned int i; struct vring_virtqueue *vq; +unsigned int num, i; +size_t size; - vq =kmalloc (sizeof (*vq) + vring.num *sizeof (struct vring_desc_state ), -GFP_KERNEL ); + num = packed ? vring.vring_packed.num : vring.vring_split.num; + size = packed ? num *sizeof (struct vring_desc_state_packed ) : + num *sizeof (struct vring_desc_state ); + + vq =kmalloc (sizeof (*vq) + size,GFP_KERNEL );if (!vq)return NULL ; - vq->vring = vring; vq->vq.callback = callback; vq->vq.vdev = vdev; vq->vq.name = name; - vq->vq.num_free = vring.num; + vq->vq.num_free = num; vq->vq.index = index; vq->we_own_ring = false; vq->queue_dma_addr =0 ;
@@ -983,9 +1199,8 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, vq->weak_barriers = weak_barriers; vq->broken = false; vq->last_used_idx =0 ; - vq->avail_flags_shadow =0 ; - vq->avail_idx_shadow =0 ; vq->num_added =0 ; + vq->packed = packed;list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG vq->in_use = false;
@@ -996,19 +1211,48 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, !context; vq->event =virtio_has_feature (vdev,VIRTIO_RING_F_EVENT_IDX ); +if (vq->packed) { + vq->vring_packed = vring.vring_packed; + vq->next_avail_idx =0 ; + vq->avail_wrap_counter =1 ; + vq->used_wrap_counter =1 ; + vq->event_flags_shadow =0 ; + +memset (vq->desc_state_packed,0 , + num *sizeof (struct vring_desc_state_packed )); + +/* Put everything in free lists. */ + vq->free_head =0 ; +for (i =0 ; i < num-1 ; i++) + vq->desc_state_packed[i].next = i +1 ; + }else { + vq->vring = vring.vring_split; + vq->avail_flags_shadow =0 ; + vq->avail_idx_shadow =0 ; + +/* Put everything in free lists. */ + vq->free_head =0 ; +for (i =0 ; i < num-1 ; i++) + vq->vring.desc[i].next =cpu_to_virtio16 (vdev, i +1 ); + +memset (vq->desc_state,0 , + num *sizeof (struct vring_desc_state )); + } +/* No callback? Tell other side not to bother us. */ if (!callback) { - vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (vdev, vq->avail_flags_shadow); +if (packed) { + vq->event_flags_shadow =VRING_EVENT_F_DISABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (vdev, + vq->event_flags_shadow); + }else { + vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (vdev, + vq->avail_flags_shadow); + } } -/* Put everything in free lists. */ - vq->free_head =0 ; -for (i =0 ; i < vring.num-1 ; i++) - vq->vring.desc[i].next =cpu_to_virtio16 (vdev, i +1 ); -memset (vq->desc_state,0 , vring.num *sizeof (struct vring_desc_state )); -return &vq->vq; }EXPORT_SYMBOL_GPL (__vring_new_virtqueue);
@@ -1055,6 +1299,12 @@ static void vring_free_queue(struct virtio_device *vdev, size_t size, } } +static inline int +__vring_size (unsigned int num,unsigned long align,bool packed) +{ +return packed ?vring_size_packed (num, align) :vring_size (num, align); +} + struct virtqueue *vring_create_virtqueue( unsigned int index, unsigned int num,
@@ -1071,7 +1321,8 @@ struct virtqueue *vring_create_virtqueue(void *queue =NULL ;dma_addr_t dma_addr;size_t queue_size_in_bytes; -struct vring vring; +union vring_union vring; +bool packed;/* We assume num is a power of 2. */ if (num & (num -1 )) {
@@ -1079,9 +1330,13 @@ struct virtqueue *vring_create_virtqueue(return NULL ; } + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED ); +/* TODO: allocate each queue chunk individually */ -for (; num &&vring_size (num, vring_align) >PAGE_SIZE ; num /=2 ) { - queue =vring_alloc_queue (vdev,vring_size (num, vring_align), +for (; num &&__vring_size (num, vring_align, packed) >PAGE_SIZE ; + num /=2 ) { + queue =vring_alloc_queue (vdev,__vring_size (num, vring_align, + packed), &dma_addr,GFP_KERNEL |__GFP_NOWARN|__GFP_ZERO);if (queue)
@@ -1093,17 +1348,21 @@ struct virtqueue *vring_create_virtqueue(if (!queue) {/* Try to get a single page. You are my only hope! */ - queue =vring_alloc_queue (vdev,vring_size (num, vring_align), + queue =vring_alloc_queue (vdev,__vring_size (num, vring_align, + packed), &dma_addr,GFP_KERNEL |__GFP_ZERO); }if (!queue)return NULL ; - queue_size_in_bytes =vring_size (num, vring_align); -vring_init (&vring, num, queue, vring_align); + queue_size_in_bytes =__vring_size (num, vring_align, packed); +if (packed) +vring_init_packed (&vring.vring_packed, num, queue, vring_align); +else +vring_init (&vring.vring_split, num, queue, vring_align); - vq =__vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + vq =__vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name);if (!vq) {vring_free_queue (vdev, queue_size_in_bytes, queue, dma_addr);
@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align); -return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED ); +if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align); + +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);
@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);
@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);
@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }
@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;
include/linux/virtio_ring.h +7 -1
@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than
@@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,
Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 2/5] virtio_ring: support creating packed ring
Quoted 5 lines · Tiwei Bie
This commit introduces the support for creating packed ring. All split ring specific functions are added
_splitsuffix. Some necessary stubs for packed ring are also added.Signed-off-by Tiwei Bie <[email protected]>
I'd rather have a patch just renaming split functions, then add all packed stuff in as a separate patch on top.
Quoted 10 lines
--- drivers/virtio/virtio_ring.c | 801 +++++++++++++++++++++++------------ include/linux/virtio_ring.h | 8 +- 2 files changed, 546 insertions(+), 263 deletions(-)drivers/virtio/virtio_ring.c +0
-0
drivers/virtio/virtio_ring.c struct vring_desc_state { · line 64
Quoted 5 lines
drivers/virtio/virtio_ring.c +2
-0@@ -60,11 +60,15 @@ struct vring_desc_state {struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ }; +struct vring_desc_state_packed { +int next;/* The next desc state. */
So this can go away with IN_ORDER?
Quoted 23 lines
drivers/virtio/virtio_ring.c +9
-4+}; +struct vring_virtqueue {struct virtqueue vq; -/* Actual memory layout for this queue */ -struct vring vring; +/* Is this a packed ring? */ +bool packed;/* Can we use weak barriers? */ bool weak_barriers;@@ -86,11 +90,39 @@ struct vring_virtqueue {/* Last used index we've seen. */ u16 last_used_idx; -/* Last written value to avail->flags */ -u16 avail_flags_shadow; +union { +/* Available for split ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring vring;
drivers/virtio/virtio_ring.c struct vring_virtqueue { · line 105
Quoted 10 lines
drivers/virtio/virtio_ring.c +7
-2-/* Last written value to avail->idx in guest byte order */ -u16 avail_idx_shadow; +/* Last written value to avail->flags */ +u16 avail_flags_shadow; + +/* Last written value to avail->idx in +* guest byte order. */ +u16 avail_idx_shadow; + };
Name this field split so it's easier to detect misuse of e.g. packed fields in split code?
Quoted 392 lines
drivers/virtio/virtio_ring.c +312
-27+ +/* Available for packed ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring_packed vring_packed; + +/* Driver ring wrap counter. */ +bool avail_wrap_counter; + +/* Device ring wrap counter. */ +bool used_wrap_counter; + +/* Index of the next avail descriptor. */ +u16 next_avail_idx; + +/* Last written value to driver->flags in +* guest byte order. */ +u16 event_flags_shadow; + }; + };/* How to notify other side. FIXME: commonalize hcalls! */ bool (*notify)(struct virtqueue *vq);@@ -110,11 +142,24 @@ struct vring_virtqueue {#endif /* Per-descriptor state. */ -struct vring_desc_state desc_state[]; +union { +struct vring_desc_state desc_state[1 ]; +struct vring_desc_state_packed desc_state_packed[1 ]; + }; };#define to_vvq (_vq) container_of(_vq, struct vring_virtqueue, vq) +static inline bool virtqueue_use_indirect (struct virtqueue *_vq, +unsigned int total_sg) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +/* If the host supports indirect descriptor tables, and we have multiple +* buffers, then go indirect. FIXME: tune this threshold */ +return (vq->indirect && total_sg >1 && vq->vq.num_free); +} +/* * Modern virtio devices have feature bits to specify whether they need a * quirk and bypass the IOMMU. If not there, just use the DMA API. @@ -200,8 +245,17 @@ static dma_addr_t vring_map_single(const struct vring_virtqueue *vq, cpu_addr, size, direction); } -static void vring_unmap_one (const struct vring_virtqueue *vq, -struct vring_desc *desc) +static int vring_mapping_error (const struct vring_virtqueue *vq, +dma_addr_t addr) +{ +if (!vring_use_dma_api (vq->vq.vdev)) +return 0 ; + +return dma_mapping_error (vring_dma_dev (vq), addr); +} + +static void vring_unmap_one_split (const struct vring_virtqueue *vq, +struct vring_desc *desc) {u16 flags;@@ -225,17 +279,9 @@ static void vring_unmap_one(const struct vring_virtqueue *vq, } } -static int vring_mapping_error (const struct vring_virtqueue *vq, -dma_addr_t addr) -{ -if (!vring_use_dma_api (vq->vq.vdev)) -return 0 ; - -return dma_mapping_error (vring_dma_dev (vq), addr); -} - -static struct vring_desc *alloc_indirect (struct virtqueue *_vq, -unsigned int total_sg,gfp_t gfp) +static struct vring_desc *alloc_indirect_split (struct virtqueue *_vq, +unsigned int total_sg, +gfp_t gfp) {struct vring_desc *desc;unsigned int i;@@ -256,14 +302,14 @@ static struct vring_desc *alloc_indirect(struct virtqueue *_vq,return desc; } -static inline int virtqueue_add (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) +static inline int virtqueue_add_split (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) {struct vring_virtqueue *vq =to_vvq (_vq);struct scatterlist *sg;@@ -299,10 +345,8 @@ static inline int virtqueue_add(struct virtqueue *_vq, head = vq->free_head; -/* If the host supports indirect descriptor tables, and we have multiple -* buffers, then go indirect. FIXME: tune this threshold */ -if (vq->indirect && total_sg >1 && vq->vq.num_free) - desc =alloc_indirect (_vq, total_sg, gfp); +if (virtqueue_use_indirect (_vq, total_sg)) + desc =alloc_indirect_split (_vq, total_sg, gfp);else { desc =NULL ;WARN_ON_ONCE (total_sg > vq->vring.num && !vq->indirect);@@ -423,7 +467,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,for (n =0 ; n < total_sg; n++) {if (i == err_idx)break ; -vring_unmap_one (vq, &desc[i]); +vring_unmap_one_split (vq, &desc[i]); i =virtio16_to_cpu (_vq->vdev, vq->vring.desc[i].next); }@@ -434,6 +478,355 @@ static inline int virtqueue_add(struct virtqueue *_vq,return -EIO ; } +static bool virtqueue_kick_prepare_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +u16 new, old; +bool needs_kick; + +START_USE (vq); +/* We need to expose available array entries before checking avail +* event. */ +virtio_mb (vq->weak_barriers); + + old = vq->avail_idx_shadow - vq->num_added; + new = vq->avail_idx_shadow; + 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 (virtio16_to_cpu (_vq->vdev,vring_avail_event (&vq->vring)), + new, old); + }else { + needs_kick = !(vq->vring.used->flags &cpu_to_virtio16 (_vq->vdev,VRING_USED_F_NO_NOTIFY )); + } +END_USE (vq); +return needs_kick; +} + +static void detach_buf_split (struct vring_virtqueue *vq,unsigned int head, +void **ctx) +{ +unsigned int i, j; +__virtio16 nextflag =cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_NEXT ); + +/* Clear data ptr. */ + vq->desc_state[head].data =NULL ; + +/* Put back on free list: unmap first-level descriptors and find end */ + i = head; + +while (vq->vring.desc[i].flags & nextflag) { +vring_unmap_one_split (vq, &vq->vring.desc[i]); + i =virtio16_to_cpu (vq->vq.vdev, vq->vring.desc[i].next); + vq->vq.num_free++; + } + +vring_unmap_one_split (vq, &vq->vring.desc[i]); + vq->vring.desc[i].next =cpu_to_virtio16 (vq->vq.vdev, vq->free_head); + vq->free_head = head; + +/* Plus final descriptor */ + vq->vq.num_free++; + +if (vq->indirect) { +struct vring_desc *indir_desc = vq->desc_state[head].indir_desc; +u32 len; + +/* Free the indirect table, if any, now that it's unmapped. */ +if (!indir_desc) +return ; + + len =virtio32_to_cpu (vq->vq.vdev, vq->vring.desc[head].len); + +BUG_ON (!(vq->vring.desc[head].flags & +cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_INDIRECT ))); +BUG_ON (len ==0 || len %sizeof (struct vring_desc )); + +for (j =0 ; j < len /sizeof (struct vring_desc ); j++) +vring_unmap_one_split (vq, &indir_desc[j]); + +kfree (indir_desc); + vq->desc_state[head].indir_desc =NULL ; + }else if (ctx) { + *ctx = vq->desc_state[head].indir_desc; + } +} + +static inline bool more_used_split (const struct vring_virtqueue *vq) +{ +return vq->last_used_idx !=virtio16_to_cpu (vq->vq.vdev, vq->vring.used->idx); +} + +static void *virtqueue_get_buf_ctx_split (struct virtqueue *_vq, +unsigned int *len, +void **ctx) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +void *ret; +unsigned int i; +u16 last_used; + +START_USE (vq); + +if (unlikely (vq->broken)) { +END_USE (vq); +return NULL ; + } + +if (!more_used_split (vq)) { +pr_debug ("No more buffers in queue\n" ); +END_USE (vq); +return NULL ; + } + +/* Only get used array entries after they have been exposed by host. */ +virtio_rmb (vq->weak_barriers); + + last_used = (vq->last_used_idx & (vq->vring.num -1 )); + i =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].id); + *len =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].len); + +if (unlikely (i >= vq->vring.num)) { +BAD_RING (vq,"id %u out of range\n" , i); +return NULL ; + } +if (unlikely (!vq->desc_state[i].data)) { +BAD_RING (vq,"id %u is not a head!\n" , i); +return NULL ; + } + +/* detach_buf_split clears data, so grab it now. */ + ret = vq->desc_state[i].data; +detach_buf_split (vq, i, ctx); + vq->last_used_idx++; +/* If we expect an interrupt for the next entry, tell host +* by writing event index and flush out the write before +* the read in the next get_buf call. */ +if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) +virtio_store_mb (vq->weak_barriers, + &vring_used_event (&vq->vring), +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx)); + +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif + +END_USE (vq); +return ret; +} + +static void virtqueue_disable_cb_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) { + vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +} + +static unsigned virtqueue_enable_cb_prepare_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +u16 last_used_idx; + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ +/* Depending on the VIRTIO_RING_F_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. */ +if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { + vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +vring_used_event (&vq->vring) =cpu_to_virtio16 (_vq->vdev, last_used_idx = vq->last_used_idx); +END_USE (vq); +return last_used_idx; +} + +static bool virtqueue_poll_split (struct virtqueue *_vq,unsigned last_used_idx) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +virtio_mb (vq->weak_barriers); +return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); +} + +static bool virtqueue_enable_cb_delayed_split (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 update the event index to keep code simple. */ +if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { + vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +/* TODO: tune this threshold */ + bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) *3 /4 ; + +virtio_store_mb (vq->weak_barriers, + &vring_used_event (&vq->vring), +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx + bufs)); + +if (unlikely ((u16)(virtio16_to_cpu (_vq->vdev, vq->vring.used->idx) - vq->last_used_idx) > bufs)) { +END_USE (vq); +return false; + } + +END_USE (vq); +return true; +} + +static void *virtqueue_detach_unused_buf_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +unsigned int i; +void *buf; + +START_USE (vq); + +for (i =0 ; i < vq->vring.num; i++) { +if (!vq->desc_state[i].data) +continue ; +/* detach_buf clears data, so grab it now. */ + buf = vq->desc_state[i].data; +detach_buf_split (vq, i,NULL ); + vq->avail_idx_shadow--; + vq->vring.avail->idx =cpu_to_virtio16 (_vq->vdev, vq->avail_idx_shadow); +END_USE (vq); +return buf; + } +/* That should have freed everything. */ +BUG_ON (vq->vq.num_free != vq->vring.num); + +END_USE (vq); +return NULL ; +} + +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +*
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 743
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver; +* +* // Device Event Suppression +* struct vring_packed_desc_event device; +* }; +*/
Why not just allocate event structures separately? Is it a win to have them share a cache line for some reason?
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 752
Quoted 9 lines
drivers/virtio/virtio_ring.c +9
-0+static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +}
What's all this about alignment? Where does it come from?
Quoted 537 lines
drivers/virtio/virtio_ring.c +166
-225+ +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +} + +static inline int virtqueue_add_packed (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) +{ +return -EIO ; +} + +static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) +{ +return false; +} + +static inline bool more_used_packed (const struct vring_virtqueue *vq) +{ +return false; +} + +static void *virtqueue_get_buf_ctx_packed (struct virtqueue *_vq, +unsigned int *len, +void **ctx) +{ +return NULL ; +} + +static void virtqueue_disable_cb_packed (struct virtqueue *_vq) +{ +} + +static unsigned virtqueue_enable_cb_prepare_packed (struct virtqueue *_vq) +{ +return 0 ; +} + +static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned last_used_idx) +{ +return false; +} + +static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) +{ +return false; +} + +static void *virtqueue_detach_unused_buf_packed (struct virtqueue *_vq) +{ +return NULL ; +} + +static inline int virtqueue_add (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) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +return vq->packed ?virtqueue_add_packed (_vq, sgs, total_sg, out_sgs, + in_sgs, data, ctx, gfp) : +virtqueue_add_split (_vq, sgs, total_sg, out_sgs, + in_sgs, data, ctx, gfp); +} +/** * virtqueue_add_sgs - expose buffers to other end * @vq: the struct virtqueue we're talking about. @@ -550,34 +943,9 @@ EXPORT_SYMBOL_GPL(virtqueue_add_inbuf_ctx);bool virtqueue_kick_prepare (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 new, old; -bool needs_kick; -START_USE (vq); -/* We need to expose available array entries before checking avail -* event. */ -virtio_mb (vq->weak_barriers); - - old = vq->avail_idx_shadow - vq->num_added; - new = vq->avail_idx_shadow; - 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 (virtio16_to_cpu (_vq->vdev,vring_avail_event (&vq->vring)), - new, old); - }else { - needs_kick = !(vq->vring.used->flags &cpu_to_virtio16 (_vq->vdev,VRING_USED_F_NO_NOTIFY )); - } -END_USE (vq); -return needs_kick; +return vq->packed ?virtqueue_kick_prepare_packed (_vq) : +virtqueue_kick_prepare_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_kick_prepare);@@ -625,58 +993,9 @@ bool virtqueue_kick(struct virtqueue *vq) }EXPORT_SYMBOL_GPL (virtqueue_kick); -static void detach_buf (struct vring_virtqueue *vq,unsigned int head, -void **ctx) -{ -unsigned int i, j; -__virtio16 nextflag =cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_NEXT ); - -/* Clear data ptr. */ - vq->desc_state[head].data =NULL ; - -/* Put back on free list: unmap first-level descriptors and find end */ - i = head; - -while (vq->vring.desc[i].flags & nextflag) { -vring_unmap_one (vq, &vq->vring.desc[i]); - i =virtio16_to_cpu (vq->vq.vdev, vq->vring.desc[i].next); - vq->vq.num_free++; - } - -vring_unmap_one (vq, &vq->vring.desc[i]); - vq->vring.desc[i].next =cpu_to_virtio16 (vq->vq.vdev, vq->free_head); - vq->free_head = head; - -/* Plus final descriptor */ - vq->vq.num_free++; - -if (vq->indirect) { -struct vring_desc *indir_desc = vq->desc_state[head].indir_desc; -u32 len; - -/* Free the indirect table, if any, now that it's unmapped. */ -if (!indir_desc) -return ; - - len =virtio32_to_cpu (vq->vq.vdev, vq->vring.desc[head].len); - -BUG_ON (!(vq->vring.desc[head].flags & -cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_INDIRECT ))); -BUG_ON (len ==0 || len %sizeof (struct vring_desc )); - -for (j =0 ; j < len /sizeof (struct vring_desc ); j++) -vring_unmap_one (vq, &indir_desc[j]); - -kfree (indir_desc); - vq->desc_state[head].indir_desc =NULL ; - }else if (ctx) { - *ctx = vq->desc_state[head].indir_desc; - } -} -static inline bool more_used (const struct vring_virtqueue *vq) { -return vq->last_used_idx !=virtio16_to_cpu (vq->vq.vdev, vq->vring.used->idx); +return vq->packed ?more_used_packed (vq) :more_used_split (vq); }/** @@ -699,57 +1018,9 @@ void *virtqueue_get_buf_ctx(struct virtqueue *_vq, unsigned int *len, void **ctx) {struct vring_virtqueue *vq =to_vvq (_vq); -void *ret; -unsigned int i; -u16 last_used; -START_USE (vq); - -if (unlikely (vq->broken)) { -END_USE (vq); -return NULL ; - } - -if (!more_used (vq)) { -pr_debug ("No more buffers in queue\n" ); -END_USE (vq); -return NULL ; - } - -/* Only get used array entries after they have been exposed by host. */ -virtio_rmb (vq->weak_barriers); - - last_used = (vq->last_used_idx & (vq->vring.num -1 )); - i =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].id); - *len =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].len); - -if (unlikely (i >= vq->vring.num)) { -BAD_RING (vq,"id %u out of range\n" , i); -return NULL ; - } -if (unlikely (!vq->desc_state[i].data)) { -BAD_RING (vq,"id %u is not a head!\n" , i); -return NULL ; - } - -/* detach_buf clears data, so grab it now. */ - ret = vq->desc_state[i].data; -detach_buf (vq, i, ctx); - vq->last_used_idx++; -/* If we expect an interrupt for the next entry, tell host -* by writing event index and flush out the write before -* the read in the next get_buf call. */ -if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) -virtio_store_mb (vq->weak_barriers, - &vring_used_event (&vq->vring), -cpu_to_virtio16 (_vq->vdev, vq->last_used_idx)); - -#ifdef DEBUG - vq->last_add_time_valid = false; -#endif - -END_USE (vq); -return ret; +return vq->packed ?virtqueue_get_buf_ctx_packed (_vq, len, ctx) : +virtqueue_get_buf_ctx_split (_vq, len, ctx); }EXPORT_SYMBOL_GPL (virtqueue_get_buf_ctx);@@ -771,12 +1042,10 @@ void virtqueue_disable_cb(struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) { - vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } - +if (vq->packed) +virtqueue_disable_cb_packed (_vq); +else +virtqueue_disable_cb_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_disable_cb);@@ -795,23 +1064,9 @@ EXPORT_SYMBOL_GPL(virtqueue_disable_cb);unsigned virtqueue_enable_cb_prepare (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 last_used_idx; -START_USE (vq); - -/* We optimistically turn back on interrupts, then check if there was -* more to do. */ -/* Depending on the VIRTIO_RING_F_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. */ -if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { - vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } -vring_used_event (&vq->vring) =cpu_to_virtio16 (_vq->vdev, last_used_idx = vq->last_used_idx); -END_USE (vq); -return last_used_idx; +return vq->packed ?virtqueue_enable_cb_prepare_packed (_vq) : +virtqueue_enable_cb_prepare_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_enable_cb_prepare);@@ -828,8 +1083,8 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); -virtio_mb (vq->weak_barriers); -return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); +return vq->packed ?virtqueue_poll_packed (_vq, last_used_idx) : +virtqueue_poll_split (_vq, last_used_idx); }EXPORT_SYMBOL_GPL (virtqueue_poll);@@ -867,34 +1122,9 @@ 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 update the event index to keep code simple. */ -if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { - vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } -/* TODO: tune this threshold */ - bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) *3 /4 ; - -virtio_store_mb (vq->weak_barriers, - &vring_used_event (&vq->vring), -cpu_to_virtio16 (_vq->vdev, vq->last_used_idx + bufs)); - -if (unlikely ((u16)(virtio16_to_cpu (_vq->vdev, vq->vring.used->idx) - vq->last_used_idx) > bufs)) { -END_USE (vq); -return false; - } - -END_USE (vq); -return true; +return vq->packed ?virtqueue_enable_cb_delayed_packed (_vq) : +virtqueue_enable_cb_delayed_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_enable_cb_delayed);@@ -909,27 +1139,9 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed);void *virtqueue_detach_unused_buf (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -unsigned int i; -void *buf; -START_USE (vq); - -for (i =0 ; i < vq->vring.num; i++) { -if (!vq->desc_state[i].data) -continue ; -/* detach_buf clears data, so grab it now. */ - buf = vq->desc_state[i].data; -detach_buf (vq, i,NULL ); - vq->avail_idx_shadow--; - vq->vring.avail->idx =cpu_to_virtio16 (_vq->vdev, vq->avail_idx_shadow); -END_USE (vq); -return buf; - } -/* That should have freed everything. */ -BUG_ON (vq->vq.num_free != vq->vring.num); - -END_USE (vq); -return NULL ; +return vq->packed ?virtqueue_detach_unused_buf_packed (_vq) : +virtqueue_detach_unused_buf_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_detach_unused_buf);@@ -954,7 +1166,8 @@ irqreturn_t vring_interrupt(int irq, void *_vq)EXPORT_SYMBOL_GPL (vring_interrupt); struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool context,@@ -962,19 +1175,22 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *), const char *name) { - unsigned int i; struct vring_virtqueue *vq; +unsigned int num, i; +size_t size; - vq =kmalloc (sizeof (*vq) + vring.num *sizeof (struct vring_desc_state ), -GFP_KERNEL ); + num = packed ? vring.vring_packed.num : vring.vring_split.num; + size = packed ? num *sizeof (struct vring_desc_state_packed ) : + num *sizeof (struct vring_desc_state ); + + vq =kmalloc (sizeof (*vq) + size,GFP_KERNEL );if (!vq)return NULL ; - vq->vring = vring; vq->vq.callback = callback; vq->vq.vdev = vdev; vq->vq.name = name; - vq->vq.num_free = vring.num; + vq->vq.num_free = num; vq->vq.index = index; vq->we_own_ring = false; vq->queue_dma_addr =0 ;@@ -983,9 +1199,8 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, vq->weak_barriers = weak_barriers; vq->broken = false; vq->last_used_idx =0 ; - vq->avail_flags_shadow =0 ; - vq->avail_idx_shadow =0 ; vq->num_added =0 ; + vq->packed = packed;list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG vq->in_use = false;@@ -996,19 +1211,48 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, !context; vq->event =virtio_has_feature (vdev,VIRTIO_RING_F_EVENT_IDX ); +if (vq->packed) { + vq->vring_packed = vring.vring_packed; + vq->next_avail_idx =0 ; + vq->avail_wrap_counter =1 ; + vq->used_wrap_counter =1 ; + vq->event_flags_shadow =0 ; + +memset (vq->desc_state_packed,0 , + num *sizeof (struct vring_desc_state_packed )); + +/* Put everything in free lists. */ + vq->free_head =0 ; +for (i =0 ; i < num-1 ; i++) + vq->desc_state_packed[i].next = i +1 ; + }else { + vq->vring = vring.vring_split; + vq->avail_flags_shadow =0 ; + vq->avail_idx_shadow =0 ; + +/* Put everything in free lists. */ + vq->free_head =0 ; +for (i =0 ; i < num-1 ; i++) + vq->vring.desc[i].next =cpu_to_virtio16 (vdev, i +1 ); + +memset (vq->desc_state,0 , + num *sizeof (struct vring_desc_state )); + } +/* No callback? Tell other side not to bother us. */ if (!callback) { - vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (vdev, vq->avail_flags_shadow); +if (packed) { + vq->event_flags_shadow =VRING_EVENT_F_DISABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (vdev, + vq->event_flags_shadow); + }else { + vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (vdev, + vq->avail_flags_shadow); + } } -/* Put everything in free lists. */ - vq->free_head =0 ; -for (i =0 ; i < vring.num-1 ; i++) - vq->vring.desc[i].next =cpu_to_virtio16 (vdev, i +1 ); -memset (vq->desc_state,0 , vring.num *sizeof (struct vring_desc_state )); -return &vq->vq; }EXPORT_SYMBOL_GPL (__vring_new_virtqueue);@@ -1055,6 +1299,12 @@ static void vring_free_queue(struct virtio_device *vdev, size_t size, } } +static inline int +__vring_size (unsigned int num,unsigned long align,bool packed) +{ +return packed ?vring_size_packed (num, align) :vring_size (num, align); +} + struct virtqueue *vring_create_virtqueue( unsigned int index, unsigned int num,@@ -1071,7 +1321,8 @@ struct virtqueue *vring_create_virtqueue(void *queue =NULL ;dma_addr_t dma_addr;size_t queue_size_in_bytes; -struct vring vring; +union vring_union vring; +bool packed;/* We assume num is a power of 2. */ if (num & (num -1 )) {@@ -1079,9 +1330,13 @@ struct virtqueue *vring_create_virtqueue(return NULL ; } + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED ); +/* TODO: allocate each queue chunk individually */ -for (; num &&vring_size (num, vring_align) >PAGE_SIZE ; num /=2 ) { - queue =vring_alloc_queue (vdev,vring_size (num, vring_align), +for (; num &&__vring_size (num, vring_align, packed) >PAGE_SIZE ; + num /=2 ) { + queue =vring_alloc_queue (vdev,__vring_size (num, vring_align, + packed), &dma_addr,GFP_KERNEL |__GFP_NOWARN|__GFP_ZERO);if (queue)@@ -1093,17 +1348,21 @@ struct virtqueue *vring_create_virtqueue(if (!queue) {/* Try to get a single page. You are my only hope! */ - queue =vring_alloc_queue (vdev,vring_size (num, vring_align), + queue =vring_alloc_queue (vdev,__vring_size (num, vring_align, + packed), &dma_addr,GFP_KERNEL |__GFP_ZERO); }if (!queue)return NULL ; - queue_size_in_bytes =vring_size (num, vring_align); -vring_init (&vring, num, queue, vring_align); + queue_size_in_bytes =__vring_size (num, vring_align, packed); +if (packed) +vring_init_packed (&vring.vring_packed, num, queue, vring_align); +else +vring_init (&vring.vring_split, num, queue, vring_align); - vq =__vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + vq =__vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name);if (!vq) {vring_free_queue (vdev, queue_size_in_bytes, queue, dma_addr);@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align);
drivers/virtio/virtio_ring.c struct virtqueue *vring_new_virtqueue(unsigned int index, · line 1398
Quoted 10 lines
drivers/virtio/virtio_ring.c +8
-2-return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); +union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED ); +if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align);
vring_init in the UAPI header is more or less a bug. I'd just stop using it, keep it around for legacy userspace.
Quoted 82 lines
drivers/virtio/virtio_ring.c +16
-2+ +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;include/linux/virtio_ring.h +7
-1@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than @@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,Signature · 1 line
2.18.0
Re: [PATCH net-next v2 2/5] virtio_ring: support creating packed ring
Quoted 9 lines · Michael S. Tsirkin
Quoted 5 lines · Tiwei Bie
This commit introduces the support for creating packed ring. All split ring specific functions are added
_splitsuffix. Some necessary stubs for packed ring are also added.Signed-off-by Tiwei Bie <[email protected]>I'd rather have a patch just renaming split functions, then add all packed stuff in as a separate patch on top.
Sure, I will do that.
Quoted 15 lines
Quoted 15 lines
--- drivers/virtio/virtio_ring.c | 801 +++++++++++++++++++++++------------ include/linux/virtio_ring.h | 8 +- 2 files changed, 546 insertions(+), 263 deletions(-)drivers/virtio/virtio_ring.c +0
-0@@ -60,11 +60,15 @@ struct vring_desc_state {struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };
Quoted 4 lines
drivers/virtio/virtio_ring.c struct vring_desc_state { · line 64
Quoted 2 lines
drivers/virtio/virtio_ring.c +2
-0+struct vring_desc_state_packed { +int next;/* The next desc state. */ So this can go away with
IN_ORDER?
Yes. If IN_ORDER is negotiated, next won't be needed anymore. Currently, IN_ORDER isn't included in this patch set, because some changes for split ring are needed to make sure that it will use the descs in the expected order. After that, optimizations can be done for both of split ring and packed ring respectively.
Quoted 25 lines
Quoted 25 lines
drivers/virtio/virtio_ring.c +9
-4+}; +struct vring_virtqueue {struct virtqueue vq; -/* Actual memory layout for this queue */ -struct vring vring; +/* Is this a packed ring? */ +bool packed;/* Can we use weak barriers? */ bool weak_barriers;@@ -86,11 +90,39 @@ struct vring_virtqueue {/* Last used index we've seen. */ u16 last_used_idx; -/* Last written value to avail->flags */ -u16 avail_flags_shadow; +union { +/* Available for split ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring vring;
Quoted 12 lines
drivers/virtio/virtio_ring.c struct vring_virtqueue { · line 105
Quoted 9 lines
drivers/virtio/virtio_ring.c +7
-2-/* Last written value to avail->idx in guest byte order */ -u16 avail_idx_shadow; +/* Last written value to avail->flags */ +u16 avail_flags_shadow; + +/* Last written value to avail->idx in +* guest byte order. */ +u16 avail_idx_shadow; + };Name this field split so it's easier to detect misuse of e.g. packed fields in split code?
Good point, I'll do that.
Quoted 21 lines
Quoted 21 lines
drivers/virtio/virtio_ring.c +20
-0+ +/* Available for packed ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring_packed vring_packed; + +/* Driver ring wrap counter. */ +bool avail_wrap_counter; + +/* Device ring wrap counter. */ +bool used_wrap_counter; + +/* Index of the next avail descriptor. */ +u16 next_avail_idx; + +/* Last written value to driver->flags in +* guest byte order. */ +u16 event_flags_shadow; + }; + };
[...]
Quoted 14 lines
Quoted 14 lines
drivers/virtio/virtio_ring.c +14
-0+ +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +* +* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver;
Quoted 8 lines
Quoted 5 lines
+ *
+ * // Device Event Suppression
+ * structvring_packed_desc_eventdevice;
+ * };
+ */Why not just allocate event structures separately? Is it a win to have them share a cache line for some reason?
Will do that.
Quoted 12 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 752
Quoted 10 lines
drivers/virtio/virtio_ring.c +9
-0+static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +}What's all this about alignment? Where does it come from?
It comes from the vring_align parameter of vring_create_virtqueue() and vring_new_virtqueue():
https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1061
https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1123
Should I just ignore it in packed ring?
CCW defined this:
#define KVM_VIRTIO_CCW_RING_ALIGN 4096
I'm not familiar with CCW. Currently, in this patch set, packed ring isn't enabled on CCW dues to some legacy accessors are not implemented in packed ring yet.
Quoted 7 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +6
-0+ +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +}
[...]
Quoted 12 lines
Quoted 12 lines
drivers/virtio/virtio_ring.c +4
-4@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align); -return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED );
Quoted 8 lines
drivers/virtio/virtio_ring.c struct virtqueue *vring_new_virtqueue(unsigned int index, · line 1398
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align);
vring_initin the UAPI header is more or less a bug. I'd just stop using it, keep it around for legacy userspace.
Got it. I'd like to do that. Thanks.
Quoted 83 lines
Quoted 83 lines
drivers/virtio/virtio_ring.c +16
-2+ +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;include/linux/virtio_ring.h +7
-1@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than @@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,Signature · 1 line
2.18.0
Re: [PATCH net-next v2 2/5] virtio_ring: support creating packed ring
Quoted 8 lines · Tiwei Bie
Quoted 7 lines · Michael S. Tsirkin
Quoted 5 lines · Tiwei Bie
This commit introduces the support for creating packed ring. All split ring specific functions are added
_splitsuffix. Some necessary stubs for packed ring are also added.Signed-off-by Tiwei Bie <[email protected]>
[...]
Quoted 14 lines
Quoted 14 lines
Quoted 14 lines
drivers/virtio/virtio_ring.c +14
-0+ +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +* +* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver;
Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
+ *
+ * // Device Event Suppression
+ * structvring_packed_desc_eventdevice;
+ * };
+ */Why not just allocate event structures separately? Is it a win to have them share a cache line for some reason?
Users may call vring_new_virtqueue() with preallocated memory to setup the ring, e.g.:
https://github.com/torvalds/linux/blob/11da3a7f84f1/drivers/s390/virtio/virtio_ccw.c#L513-L522
https://github.com/torvalds/linux/blob/11da3a7f84f1/drivers/misc/mic/vop/vop_main.c#L306-L307
Below is the corresponding definition in split ring:
https://github.com/oasis-tcs/virtio-spec/blob/89dd55f5e606/split-ring.tex#L64-L78
If my understanding is correct, this is just for legacy interfaces, and we won't define layout in packed ring and don't need to support vring_new_virtqueue() in packed ring. Is it correct? Thanks!
Quoted 146 lines
Will do that.
Quoted 12 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 752
Quoted 10 lines
drivers/virtio/virtio_ring.c +9
-0+static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +}What's all this about alignment? Where does it come from?
It comes from the
vring_alignparameter ofvring_create_virtqueue()andvring_new_virtqueue():https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1061
https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1123Should I just ignore it in packed ring?
CCW defined this:
#define
KVM_VIRTIO_CCW_RING_ALIGN4096I'm not familiar with CCW. Currently, in this patch set, packed ring isn't enabled on CCW dues to some legacy accessors are not implemented in packed ring yet.
Quoted 7 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +6
-0+ +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +}[...]
Quoted 12 lines
Quoted 12 lines
drivers/virtio/virtio_ring.c +4
-4@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align); -return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED );Quoted 8 lines
drivers/virtio/virtio_ring.c struct virtqueue *vring_new_virtqueue(unsigned int index, · line 1398
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align);
vring_initin the UAPI header is more or less a bug. I'd just stop using it, keep it around for legacy userspace.Got it. I'd like to do that. Thanks.
Quoted 83 lines
Quoted 83 lines
drivers/virtio/virtio_ring.c +16
-2+ +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;include/linux/virtio_ring.h +7
-1@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than @@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 2/5] virtio_ring: support creating packed ring
Quoted 39 lines · Tiwei Bie
Quoted 8 lines · Tiwei Bie
Quoted 7 lines · Michael S. Tsirkin
Quoted 5 lines · Tiwei Bie
This commit introduces the support for creating packed ring. All split ring specific functions are added
_splitsuffix. Some necessary stubs for packed ring are also added.Signed-off-by Tiwei Bie <[email protected]>[...]
Quoted 14 lines
Quoted 14 lines
Quoted 14 lines
drivers/virtio/virtio_ring.c +14
-0+ +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +* +* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver; Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
+ *
+ * // Device Event Suppression
+ * structvring_packed_desc_eventdevice;
+ * };
+ */Why not just allocate event structures separately? Is it a win to have them share a cache line for some reason?
Users may call
vring_new_virtqueue()with preallocated memory to setup the ring, e.g.:https://github.com/torvalds/linux/blob/11da3a7f84f1/drivers/s390/virtio/virtio_ccw.c#L513-L522
https://github.com/torvalds/linux/blob/11da3a7f84f1/drivers/misc/mic/vop/vop_main.c#L306-L307
Quoted 8 lines
Below is the corresponding definition in split ring:
https://github.com/oasis-tcs/virtio-spec/blob/89dd55f5e606/split-ring.tex#L64-L78
If my understanding is correct, this is just for legacy interfaces, and we won't define layout in packed ring and don't need to support
vring_new_virtqueue()in packed ring. Is it correct? Thanks!
mic doesn't support 1.0 yet but ccw does.
It's probably best to look into converting ccw tovirtio_create_virtqueue, then you can just failvring_new_virtqueue for packed.
Cornelia, are there any gotchas to look out for?
Quoted 148 lines
Quoted 148 lines
Will do that.
Quoted 12 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 752
Quoted 10 lines
drivers/virtio/virtio_ring.c +9
-0+static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +}What's all this about alignment? Where does it come from?
It comes from the
vring_alignparameter ofvring_create_virtqueue()andvring_new_virtqueue():https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1061
https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1123Should I just ignore it in packed ring?
CCW defined this:
#define
KVM_VIRTIO_CCW_RING_ALIGN4096I'm not familiar with CCW. Currently, in this patch set, packed ring isn't enabled on CCW dues to some legacy accessors are not implemented in packed ring yet.
Quoted 7 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +6
-0+ +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +}[...]
Quoted 12 lines
Quoted 12 lines
drivers/virtio/virtio_ring.c +4
-4@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align); -return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED );Quoted 8 lines
drivers/virtio/virtio_ring.c struct virtqueue *vring_new_virtqueue(unsigned int index, · line 1398
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align);
vring_initin the UAPI header is more or less a bug. I'd just stop using it, keep it around for legacy userspace.Got it. I'd like to do that. Thanks.
Quoted 83 lines
Quoted 83 lines
drivers/virtio/virtio_ring.c +16
-2+ +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;include/linux/virtio_ring.h +7
-1@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than @@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 2/5] virtio_ring: support creating packed ring
In reply to Tiwei BieQuoted 39 lines · Tiwei Bie
Quoted 8 lines · Tiwei Bie
Quoted 7 lines · Michael S. Tsirkin
Quoted 5 lines · Tiwei Bie
This commit introduces the support for creating packed ring. All split ring specific functions are added
_splitsuffix. Some necessary stubs for packed ring are also added.Signed-off-by Tiwei Bie <[email protected]>[...]
Quoted 14 lines
Quoted 14 lines
Quoted 14 lines
drivers/virtio/virtio_ring.c +14
-0+ +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +* +* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver; Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
+ *
+ * // Device Event Suppression
+ * structvring_packed_desc_eventdevice;
+ * };
+ */Why not just allocate event structures separately? Is it a win to have them share a cache line for some reason?
Users may call
vring_new_virtqueue()with preallocated memory to setup the ring, e.g.:https://github.com/torvalds/linux/blob/11da3a7f84f1/drivers/s390/virtio/virtio_ccw.c#L513-L522
https://github.com/torvalds/linux/blob/11da3a7f84f1/drivers/misc/mic/vop/vop_main.c#L306-L307
Quoted 8 lines
Below is the corresponding definition in split ring:
https://github.com/oasis-tcs/virtio-spec/blob/89dd55f5e606/split-ring.tex#L64-L78
If my understanding is correct, this is just for legacy interfaces, and we won't define layout in packed ring and don't need to support
vring_new_virtqueue()in packed ring. Is it correct? Thanks!
mic doesn't support 1.0 yet but ccw does.
It's probably best to look into converting ccw tovirtio_create_virtqueue, then you can just failvring_new_virtqueue for packed.
Cornelia, are there any gotchas to look out for?
Quoted 148 lines
Quoted 148 lines
Will do that.
Quoted 12 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 752
Quoted 10 lines
drivers/virtio/virtio_ring.c +9
-0+static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +}What's all this about alignment? Where does it come from?
It comes from the
vring_alignparameter ofvring_create_virtqueue()andvring_new_virtqueue():https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1061
https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1123Should I just ignore it in packed ring?
CCW defined this:
#define
KVM_VIRTIO_CCW_RING_ALIGN4096I'm not familiar with CCW. Currently, in this patch set, packed ring isn't enabled on CCW dues to some legacy accessors are not implemented in packed ring yet.
Quoted 7 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +6
-0+ +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +}[...]
Quoted 12 lines
Quoted 12 lines
drivers/virtio/virtio_ring.c +4
-4@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align); -return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED );Quoted 8 lines
drivers/virtio/virtio_ring.c struct virtqueue *vring_new_virtqueue(unsigned int index, · line 1398
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align);
vring_initin the UAPI header is more or less a bug. I'd just stop using it, keep it around for legacy userspace.Got it. I'd like to do that. Thanks.
Quoted 83 lines
Quoted 83 lines
drivers/virtio/virtio_ring.c +16
-2+ +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;include/linux/virtio_ring.h +7
-1@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than @@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,Signature · 1 line
2.18.0
Re: [PATCH net-next v2 2/5] virtio_ring: support creating packed ring
In reply to Tiwei BieQuoted 8 lines · Tiwei Bie
Quoted 7 lines · Michael S. Tsirkin
Quoted 5 lines · Tiwei Bie
This commit introduces the support for creating packed ring. All split ring specific functions are added
_splitsuffix. Some necessary stubs for packed ring are also added.Signed-off-by Tiwei Bie <[email protected]>
[...]
Quoted 14 lines
Quoted 14 lines
Quoted 14 lines
drivers/virtio/virtio_ring.c +14
-0+ +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +* +* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver;
Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
+ *
+ * // Device Event Suppression
+ * structvring_packed_desc_eventdevice;
+ * };
+ */Why not just allocate event structures separately? Is it a win to have them share a cache line for some reason?
Users may call vring_new_virtqueue() with preallocated memory to setup the ring, e.g.:
https://github.com/torvalds/linux/blob/11da3a7f84f1/drivers/s390/virtio/virtio_ccw.c#L513-L522
https://github.com/torvalds/linux/blob/11da3a7f84f1/drivers/misc/mic/vop/vop_main.c#L306-L307
Below is the corresponding definition in split ring:
https://github.com/oasis-tcs/virtio-spec/blob/89dd55f5e606/split-ring.tex#L64-L78
If my understanding is correct, this is just for legacy interfaces, and we won't define layout in packed ring and don't need to support vring_new_virtqueue() in packed ring. Is it correct? Thanks!
Quoted 146 lines
Will do that.
Quoted 12 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 752
Quoted 10 lines
drivers/virtio/virtio_ring.c +9
-0+static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +}What's all this about alignment? Where does it come from?
It comes from the
vring_alignparameter ofvring_create_virtqueue()andvring_new_virtqueue():https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1061
https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1123Should I just ignore it in packed ring?
CCW defined this:
#define
KVM_VIRTIO_CCW_RING_ALIGN4096I'm not familiar with CCW. Currently, in this patch set, packed ring isn't enabled on CCW dues to some legacy accessors are not implemented in packed ring yet.
Quoted 7 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +6
-0+ +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +}[...]
Quoted 12 lines
Quoted 12 lines
drivers/virtio/virtio_ring.c +4
-4@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align); -return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED );Quoted 8 lines
drivers/virtio/virtio_ring.c struct virtqueue *vring_new_virtqueue(unsigned int index, · line 1398
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align);
vring_initin the UAPI header is more or less a bug. I'd just stop using it, keep it around for legacy userspace.Got it. I'd like to do that. Thanks.
Quoted 83 lines
Quoted 83 lines
drivers/virtio/virtio_ring.c +16
-2+ +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;include/linux/virtio_ring.h +7
-1@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than @@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 2/5] virtio_ring: support creating packed ring
In reply to Tiwei BieQuoted 140 lines · Tiwei Bie
Quoted 9 lines · Michael S. Tsirkin
Quoted 5 lines · Tiwei Bie
This commit introduces the support for creating packed ring. All split ring specific functions are added
_splitsuffix. Some necessary stubs for packed ring are also added.Signed-off-by Tiwei Bie <[email protected]>I'd rather have a patch just renaming split functions, then add all packed stuff in as a separate patch on top.
Sure, I will do that.
Quoted 15 lines
Quoted 15 lines
--- drivers/virtio/virtio_ring.c | 801 +++++++++++++++++++++++------------ include/linux/virtio_ring.h | 8 +- 2 files changed, 546 insertions(+), 263 deletions(-)drivers/virtio/virtio_ring.c +0
-0@@ -60,11 +60,15 @@ struct vring_desc_state {struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };Quoted 4 lines
drivers/virtio/virtio_ring.c struct vring_desc_state { · line 64
Quoted 2 lines
drivers/virtio/virtio_ring.c +2
-0+struct vring_desc_state_packed { +int next;/* The next desc state. */ So this can go away with
IN_ORDER?Yes. If
IN_ORDERis negotiated, next won't be needed anymore. Currently,IN_ORDERisn't included in this patch set, because some changes for split ring are needed to make sure that it will use the descs in the expected order. After that, optimizations can be done for both of split ring and packed ring respectively.Quoted 25 lines
Quoted 25 lines
drivers/virtio/virtio_ring.c +9
-4+}; +struct vring_virtqueue {struct virtqueue vq; -/* Actual memory layout for this queue */ -struct vring vring; +/* Is this a packed ring? */ +bool packed;/* Can we use weak barriers? */ bool weak_barriers;@@ -86,11 +90,39 @@ struct vring_virtqueue {/* Last used index we've seen. */ u16 last_used_idx; -/* Last written value to avail->flags */ -u16 avail_flags_shadow; +union { +/* Available for split ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring vring;Quoted 12 lines
drivers/virtio/virtio_ring.c struct vring_virtqueue { · line 105
Quoted 9 lines
drivers/virtio/virtio_ring.c +7
-2-/* Last written value to avail->idx in guest byte order */ -u16 avail_idx_shadow; +/* Last written value to avail->flags */ +u16 avail_flags_shadow; + +/* Last written value to avail->idx in +* guest byte order. */ +u16 avail_idx_shadow; + };Name this field split so it's easier to detect misuse of e.g. packed fields in split code?
Good point, I'll do that.
Quoted 21 lines
Quoted 21 lines
drivers/virtio/virtio_ring.c +20
-0+ +/* Available for packed ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring_packed vring_packed; + +/* Driver ring wrap counter. */ +bool avail_wrap_counter; + +/* Device ring wrap counter. */ +bool used_wrap_counter; + +/* Index of the next avail descriptor. */ +u16 next_avail_idx; + +/* Last written value to driver->flags in +* guest byte order. */ +u16 event_flags_shadow; + }; + };[...]
Quoted 14 lines
Quoted 14 lines
drivers/virtio/virtio_ring.c +14
-0+ +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +* +* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver; Quoted 8 lines
Quoted 5 lines
+ *
+ * // Device Event Suppression
+ * structvring_packed_desc_eventdevice;
+ * };
+ */Why not just allocate event structures separately? Is it a win to have them share a cache line for some reason?
Will do that.
Quoted 12 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 752
Quoted 10 lines
drivers/virtio/virtio_ring.c +9
-0+static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +}What's all this about alignment? Where does it come from?
Quoted 5 lines
It comes from the
vring_alignparameter ofvring_create_virtqueue()andvring_new_virtqueue():https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1061
https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1123
Note the TODO - we just never got to fixing it. It would be great to fix this for virtio 1 rings, but if not - at least let's not add this stuff for packed rings.
Quoted 9 lines
Should I just ignore it in packed ring?
CCW defined this:
#define
KVM_VIRTIO_CCW_RING_ALIGN4096I'm not familiar with CCW. Currently, in this patch set, packed ring isn't enabled on CCW dues to some legacy accessors are not implemented in packed ring yet.
Then you need to take steps not to negotiate this feature bit for ccw drivers.
Quoted 114 lines
Quoted 7 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +6
-0+ +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +}[...]
Quoted 12 lines
Quoted 12 lines
drivers/virtio/virtio_ring.c +4
-4@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align); -return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED );Quoted 8 lines
drivers/virtio/virtio_ring.c struct virtqueue *vring_new_virtqueue(unsigned int index, · line 1398
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align);
vring_initin the UAPI header is more or less a bug. I'd just stop using it, keep it around for legacy userspace.Got it. I'd like to do that. Thanks.
Quoted 83 lines
Quoted 83 lines
drivers/virtio/virtio_ring.c +16
-2+ +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;include/linux/virtio_ring.h +7
-1@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than @@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 2/5] virtio_ring: support creating packed ring
In reply to Tiwei BieQuoted 140 lines · Tiwei Bie
Quoted 9 lines · Michael S. Tsirkin
Quoted 5 lines · Tiwei Bie
This commit introduces the support for creating packed ring. All split ring specific functions are added
_splitsuffix. Some necessary stubs for packed ring are also added.Signed-off-by Tiwei Bie <[email protected]>I'd rather have a patch just renaming split functions, then add all packed stuff in as a separate patch on top.
Sure, I will do that.
Quoted 15 lines
Quoted 15 lines
--- drivers/virtio/virtio_ring.c | 801 +++++++++++++++++++++++------------ include/linux/virtio_ring.h | 8 +- 2 files changed, 546 insertions(+), 263 deletions(-)drivers/virtio/virtio_ring.c +0
-0@@ -60,11 +60,15 @@ struct vring_desc_state {struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };Quoted 4 lines
drivers/virtio/virtio_ring.c struct vring_desc_state { · line 64
Quoted 2 lines
drivers/virtio/virtio_ring.c +2
-0+struct vring_desc_state_packed { +int next;/* The next desc state. */ So this can go away with
IN_ORDER?Yes. If
IN_ORDERis negotiated, next won't be needed anymore. Currently,IN_ORDERisn't included in this patch set, because some changes for split ring are needed to make sure that it will use the descs in the expected order. After that, optimizations can be done for both of split ring and packed ring respectively.Quoted 25 lines
Quoted 25 lines
drivers/virtio/virtio_ring.c +9
-4+}; +struct vring_virtqueue {struct virtqueue vq; -/* Actual memory layout for this queue */ -struct vring vring; +/* Is this a packed ring? */ +bool packed;/* Can we use weak barriers? */ bool weak_barriers;@@ -86,11 +90,39 @@ struct vring_virtqueue {/* Last used index we've seen. */ u16 last_used_idx; -/* Last written value to avail->flags */ -u16 avail_flags_shadow; +union { +/* Available for split ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring vring;Quoted 12 lines
drivers/virtio/virtio_ring.c struct vring_virtqueue { · line 105
Quoted 9 lines
drivers/virtio/virtio_ring.c +7
-2-/* Last written value to avail->idx in guest byte order */ -u16 avail_idx_shadow; +/* Last written value to avail->flags */ +u16 avail_flags_shadow; + +/* Last written value to avail->idx in +* guest byte order. */ +u16 avail_idx_shadow; + };Name this field split so it's easier to detect misuse of e.g. packed fields in split code?
Good point, I'll do that.
Quoted 21 lines
Quoted 21 lines
drivers/virtio/virtio_ring.c +20
-0+ +/* Available for packed ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring_packed vring_packed; + +/* Driver ring wrap counter. */ +bool avail_wrap_counter; + +/* Device ring wrap counter. */ +bool used_wrap_counter; + +/* Index of the next avail descriptor. */ +u16 next_avail_idx; + +/* Last written value to driver->flags in +* guest byte order. */ +u16 event_flags_shadow; + }; + };[...]
Quoted 14 lines
Quoted 14 lines
drivers/virtio/virtio_ring.c +14
-0+ +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +* +* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver; Quoted 8 lines
Quoted 5 lines
+ *
+ * // Device Event Suppression
+ * structvring_packed_desc_eventdevice;
+ * };
+ */Why not just allocate event structures separately? Is it a win to have them share a cache line for some reason?
Will do that.
Quoted 12 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 752
Quoted 10 lines
drivers/virtio/virtio_ring.c +9
-0+static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +}What's all this about alignment? Where does it come from?
Quoted 5 lines
It comes from the
vring_alignparameter ofvring_create_virtqueue()andvring_new_virtqueue():https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1061
https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1123
Note the TODO - we just never got to fixing it. It would be great to fix this for virtio 1 rings, but if not - at least let's not add this stuff for packed rings.
Quoted 9 lines
Should I just ignore it in packed ring?
CCW defined this:
#define
KVM_VIRTIO_CCW_RING_ALIGN4096I'm not familiar with CCW. Currently, in this patch set, packed ring isn't enabled on CCW dues to some legacy accessors are not implemented in packed ring yet.
Then you need to take steps not to negotiate this feature bit for ccw drivers.
Quoted 114 lines
Quoted 7 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +6
-0+ +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +}[...]
Quoted 12 lines
Quoted 12 lines
drivers/virtio/virtio_ring.c +4
-4@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align); -return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED );Quoted 8 lines
drivers/virtio/virtio_ring.c struct virtqueue *vring_new_virtqueue(unsigned int index, · line 1398
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align);
vring_initin the UAPI header is more or less a bug. I'd just stop using it, keep it around for legacy userspace.Got it. I'd like to do that. Thanks.
Quoted 83 lines
Quoted 83 lines
drivers/virtio/virtio_ring.c +16
-2+ +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;include/linux/virtio_ring.h +7
-1@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than @@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,Signature · 1 line
2.18.0
Re: [PATCH net-next v2 2/5] virtio_ring: support creating packed ring
In reply to Michael S. TsirkinQuoted 9 lines · Michael S. Tsirkin
Quoted 5 lines · Tiwei Bie
This commit introduces the support for creating packed ring. All split ring specific functions are added
_splitsuffix. Some necessary stubs for packed ring are also added.Signed-off-by Tiwei Bie <[email protected]>I'd rather have a patch just renaming split functions, then add all packed stuff in as a separate patch on top.
Sure, I will do that.
Quoted 15 lines
Quoted 15 lines
--- drivers/virtio/virtio_ring.c | 801 +++++++++++++++++++++++------------ include/linux/virtio_ring.h | 8 +- 2 files changed, 546 insertions(+), 263 deletions(-)drivers/virtio/virtio_ring.c +0
-0@@ -60,11 +60,15 @@ struct vring_desc_state {struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };
Quoted 4 lines
drivers/virtio/virtio_ring.c struct vring_desc_state { · line 64
Quoted 2 lines
drivers/virtio/virtio_ring.c +2
-0+struct vring_desc_state_packed { +int next;/* The next desc state. */ So this can go away with
IN_ORDER?
Yes. If IN_ORDER is negotiated, next won't be needed anymore. Currently, IN_ORDER isn't included in this patch set, because some changes for split ring are needed to make sure that it will use the descs in the expected order. After that, optimizations can be done for both of split ring and packed ring respectively.
Quoted 25 lines
Quoted 25 lines
drivers/virtio/virtio_ring.c +9
-4+}; +struct vring_virtqueue {struct virtqueue vq; -/* Actual memory layout for this queue */ -struct vring vring; +/* Is this a packed ring? */ +bool packed;/* Can we use weak barriers? */ bool weak_barriers;@@ -86,11 +90,39 @@ struct vring_virtqueue {/* Last used index we've seen. */ u16 last_used_idx; -/* Last written value to avail->flags */ -u16 avail_flags_shadow; +union { +/* Available for split ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring vring;
Quoted 12 lines
drivers/virtio/virtio_ring.c struct vring_virtqueue { · line 105
Quoted 9 lines
drivers/virtio/virtio_ring.c +7
-2-/* Last written value to avail->idx in guest byte order */ -u16 avail_idx_shadow; +/* Last written value to avail->flags */ +u16 avail_flags_shadow; + +/* Last written value to avail->idx in +* guest byte order. */ +u16 avail_idx_shadow; + };Name this field split so it's easier to detect misuse of e.g. packed fields in split code?
Good point, I'll do that.
Quoted 21 lines
Quoted 21 lines
drivers/virtio/virtio_ring.c +20
-0+ +/* Available for packed ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring_packed vring_packed; + +/* Driver ring wrap counter. */ +bool avail_wrap_counter; + +/* Device ring wrap counter. */ +bool used_wrap_counter; + +/* Index of the next avail descriptor. */ +u16 next_avail_idx; + +/* Last written value to driver->flags in +* guest byte order. */ +u16 event_flags_shadow; + }; + };
[...]
Quoted 14 lines
Quoted 14 lines
drivers/virtio/virtio_ring.c +14
-0+ +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +* +* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver;
Quoted 8 lines
Quoted 5 lines
+ *
+ * // Device Event Suppression
+ * structvring_packed_desc_eventdevice;
+ * };
+ */Why not just allocate event structures separately? Is it a win to have them share a cache line for some reason?
Will do that.
Quoted 12 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 752
Quoted 10 lines
drivers/virtio/virtio_ring.c +9
-0+static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +}What's all this about alignment? Where does it come from?
It comes from the vring_align parameter of vring_create_virtqueue() and vring_new_virtqueue():
https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1061
https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L1123
Should I just ignore it in packed ring?
CCW defined this:
#define KVM_VIRTIO_CCW_RING_ALIGN 4096
I'm not familiar with CCW. Currently, in this patch set, packed ring isn't enabled on CCW dues to some legacy accessors are not implemented in packed ring yet.
Quoted 7 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +6
-0+ +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +}
[...]
Quoted 12 lines
Quoted 12 lines
drivers/virtio/virtio_ring.c +4
-4@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align); -return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED );
Quoted 8 lines
drivers/virtio/virtio_ring.c struct virtqueue *vring_new_virtqueue(unsigned int index, · line 1398
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align);
vring_initin the UAPI header is more or less a bug. I'd just stop using it, keep it around for legacy userspace.
Got it. I'd like to do that. Thanks.
Quoted 83 lines
Quoted 83 lines
drivers/virtio/virtio_ring.c +16
-2+ +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;include/linux/virtio_ring.h +7
-1@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than @@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 2/5] virtio_ring: support creating packed ring
In reply to Tiwei BieQuoted 5 lines · Tiwei Bie
This commit introduces the support for creating packed ring. All split ring specific functions are added
_splitsuffix. Some necessary stubs for packed ring are also added.Signed-off-by Tiwei Bie <[email protected]>
I'd rather have a patch just renaming split functions, then add all packed stuff in as a separate patch on top.
Quoted 10 lines
--- drivers/virtio/virtio_ring.c | 801 +++++++++++++++++++++++------------ include/linux/virtio_ring.h | 8 +- 2 files changed, 546 insertions(+), 263 deletions(-)drivers/virtio/virtio_ring.c +0
-0
drivers/virtio/virtio_ring.c struct vring_desc_state { · line 64
Quoted 5 lines
drivers/virtio/virtio_ring.c +2
-0@@ -60,11 +60,15 @@ struct vring_desc_state {struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ }; +struct vring_desc_state_packed { +int next;/* The next desc state. */
So this can go away with IN_ORDER?
Quoted 23 lines
drivers/virtio/virtio_ring.c +9
-4+}; +struct vring_virtqueue {struct virtqueue vq; -/* Actual memory layout for this queue */ -struct vring vring; +/* Is this a packed ring? */ +bool packed;/* Can we use weak barriers? */ bool weak_barriers;@@ -86,11 +90,39 @@ struct vring_virtqueue {/* Last used index we've seen. */ u16 last_used_idx; -/* Last written value to avail->flags */ -u16 avail_flags_shadow; +union { +/* Available for split ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring vring;
drivers/virtio/virtio_ring.c struct vring_virtqueue { · line 105
Quoted 10 lines
drivers/virtio/virtio_ring.c +7
-2-/* Last written value to avail->idx in guest byte order */ -u16 avail_idx_shadow; +/* Last written value to avail->flags */ +u16 avail_flags_shadow; + +/* Last written value to avail->idx in +* guest byte order. */ +u16 avail_idx_shadow; + };
Name this field split so it's easier to detect misuse of e.g. packed fields in split code?
Quoted 392 lines
drivers/virtio/virtio_ring.c +312
-27+ +/* Available for packed ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring_packed vring_packed; + +/* Driver ring wrap counter. */ +bool avail_wrap_counter; + +/* Device ring wrap counter. */ +bool used_wrap_counter; + +/* Index of the next avail descriptor. */ +u16 next_avail_idx; + +/* Last written value to driver->flags in +* guest byte order. */ +u16 event_flags_shadow; + }; + };/* How to notify other side. FIXME: commonalize hcalls! */ bool (*notify)(struct virtqueue *vq);@@ -110,11 +142,24 @@ struct vring_virtqueue {#endif /* Per-descriptor state. */ -struct vring_desc_state desc_state[]; +union { +struct vring_desc_state desc_state[1 ]; +struct vring_desc_state_packed desc_state_packed[1 ]; + }; };#define to_vvq (_vq) container_of(_vq, struct vring_virtqueue, vq) +static inline bool virtqueue_use_indirect (struct virtqueue *_vq, +unsigned int total_sg) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +/* If the host supports indirect descriptor tables, and we have multiple +* buffers, then go indirect. FIXME: tune this threshold */ +return (vq->indirect && total_sg >1 && vq->vq.num_free); +} +/* * Modern virtio devices have feature bits to specify whether they need a * quirk and bypass the IOMMU. If not there, just use the DMA API. @@ -200,8 +245,17 @@ static dma_addr_t vring_map_single(const struct vring_virtqueue *vq, cpu_addr, size, direction); } -static void vring_unmap_one (const struct vring_virtqueue *vq, -struct vring_desc *desc) +static int vring_mapping_error (const struct vring_virtqueue *vq, +dma_addr_t addr) +{ +if (!vring_use_dma_api (vq->vq.vdev)) +return 0 ; + +return dma_mapping_error (vring_dma_dev (vq), addr); +} + +static void vring_unmap_one_split (const struct vring_virtqueue *vq, +struct vring_desc *desc) {u16 flags;@@ -225,17 +279,9 @@ static void vring_unmap_one(const struct vring_virtqueue *vq, } } -static int vring_mapping_error (const struct vring_virtqueue *vq, -dma_addr_t addr) -{ -if (!vring_use_dma_api (vq->vq.vdev)) -return 0 ; - -return dma_mapping_error (vring_dma_dev (vq), addr); -} - -static struct vring_desc *alloc_indirect (struct virtqueue *_vq, -unsigned int total_sg,gfp_t gfp) +static struct vring_desc *alloc_indirect_split (struct virtqueue *_vq, +unsigned int total_sg, +gfp_t gfp) {struct vring_desc *desc;unsigned int i;@@ -256,14 +302,14 @@ static struct vring_desc *alloc_indirect(struct virtqueue *_vq,return desc; } -static inline int virtqueue_add (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) +static inline int virtqueue_add_split (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) {struct vring_virtqueue *vq =to_vvq (_vq);struct scatterlist *sg;@@ -299,10 +345,8 @@ static inline int virtqueue_add(struct virtqueue *_vq, head = vq->free_head; -/* If the host supports indirect descriptor tables, and we have multiple -* buffers, then go indirect. FIXME: tune this threshold */ -if (vq->indirect && total_sg >1 && vq->vq.num_free) - desc =alloc_indirect (_vq, total_sg, gfp); +if (virtqueue_use_indirect (_vq, total_sg)) + desc =alloc_indirect_split (_vq, total_sg, gfp);else { desc =NULL ;WARN_ON_ONCE (total_sg > vq->vring.num && !vq->indirect);@@ -423,7 +467,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,for (n =0 ; n < total_sg; n++) {if (i == err_idx)break ; -vring_unmap_one (vq, &desc[i]); +vring_unmap_one_split (vq, &desc[i]); i =virtio16_to_cpu (_vq->vdev, vq->vring.desc[i].next); }@@ -434,6 +478,355 @@ static inline int virtqueue_add(struct virtqueue *_vq,return -EIO ; } +static bool virtqueue_kick_prepare_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +u16 new, old; +bool needs_kick; + +START_USE (vq); +/* We need to expose available array entries before checking avail +* event. */ +virtio_mb (vq->weak_barriers); + + old = vq->avail_idx_shadow - vq->num_added; + new = vq->avail_idx_shadow; + 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 (virtio16_to_cpu (_vq->vdev,vring_avail_event (&vq->vring)), + new, old); + }else { + needs_kick = !(vq->vring.used->flags &cpu_to_virtio16 (_vq->vdev,VRING_USED_F_NO_NOTIFY )); + } +END_USE (vq); +return needs_kick; +} + +static void detach_buf_split (struct vring_virtqueue *vq,unsigned int head, +void **ctx) +{ +unsigned int i, j; +__virtio16 nextflag =cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_NEXT ); + +/* Clear data ptr. */ + vq->desc_state[head].data =NULL ; + +/* Put back on free list: unmap first-level descriptors and find end */ + i = head; + +while (vq->vring.desc[i].flags & nextflag) { +vring_unmap_one_split (vq, &vq->vring.desc[i]); + i =virtio16_to_cpu (vq->vq.vdev, vq->vring.desc[i].next); + vq->vq.num_free++; + } + +vring_unmap_one_split (vq, &vq->vring.desc[i]); + vq->vring.desc[i].next =cpu_to_virtio16 (vq->vq.vdev, vq->free_head); + vq->free_head = head; + +/* Plus final descriptor */ + vq->vq.num_free++; + +if (vq->indirect) { +struct vring_desc *indir_desc = vq->desc_state[head].indir_desc; +u32 len; + +/* Free the indirect table, if any, now that it's unmapped. */ +if (!indir_desc) +return ; + + len =virtio32_to_cpu (vq->vq.vdev, vq->vring.desc[head].len); + +BUG_ON (!(vq->vring.desc[head].flags & +cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_INDIRECT ))); +BUG_ON (len ==0 || len %sizeof (struct vring_desc )); + +for (j =0 ; j < len /sizeof (struct vring_desc ); j++) +vring_unmap_one_split (vq, &indir_desc[j]); + +kfree (indir_desc); + vq->desc_state[head].indir_desc =NULL ; + }else if (ctx) { + *ctx = vq->desc_state[head].indir_desc; + } +} + +static inline bool more_used_split (const struct vring_virtqueue *vq) +{ +return vq->last_used_idx !=virtio16_to_cpu (vq->vq.vdev, vq->vring.used->idx); +} + +static void *virtqueue_get_buf_ctx_split (struct virtqueue *_vq, +unsigned int *len, +void **ctx) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +void *ret; +unsigned int i; +u16 last_used; + +START_USE (vq); + +if (unlikely (vq->broken)) { +END_USE (vq); +return NULL ; + } + +if (!more_used_split (vq)) { +pr_debug ("No more buffers in queue\n" ); +END_USE (vq); +return NULL ; + } + +/* Only get used array entries after they have been exposed by host. */ +virtio_rmb (vq->weak_barriers); + + last_used = (vq->last_used_idx & (vq->vring.num -1 )); + i =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].id); + *len =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].len); + +if (unlikely (i >= vq->vring.num)) { +BAD_RING (vq,"id %u out of range\n" , i); +return NULL ; + } +if (unlikely (!vq->desc_state[i].data)) { +BAD_RING (vq,"id %u is not a head!\n" , i); +return NULL ; + } + +/* detach_buf_split clears data, so grab it now. */ + ret = vq->desc_state[i].data; +detach_buf_split (vq, i, ctx); + vq->last_used_idx++; +/* If we expect an interrupt for the next entry, tell host +* by writing event index and flush out the write before +* the read in the next get_buf call. */ +if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) +virtio_store_mb (vq->weak_barriers, + &vring_used_event (&vq->vring), +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx)); + +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif + +END_USE (vq); +return ret; +} + +static void virtqueue_disable_cb_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) { + vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +} + +static unsigned virtqueue_enable_cb_prepare_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +u16 last_used_idx; + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ +/* Depending on the VIRTIO_RING_F_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. */ +if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { + vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +vring_used_event (&vq->vring) =cpu_to_virtio16 (_vq->vdev, last_used_idx = vq->last_used_idx); +END_USE (vq); +return last_used_idx; +} + +static bool virtqueue_poll_split (struct virtqueue *_vq,unsigned last_used_idx) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +virtio_mb (vq->weak_barriers); +return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); +} + +static bool virtqueue_enable_cb_delayed_split (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 update the event index to keep code simple. */ +if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { + vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +/* TODO: tune this threshold */ + bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) *3 /4 ; + +virtio_store_mb (vq->weak_barriers, + &vring_used_event (&vq->vring), +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx + bufs)); + +if (unlikely ((u16)(virtio16_to_cpu (_vq->vdev, vq->vring.used->idx) - vq->last_used_idx) > bufs)) { +END_USE (vq); +return false; + } + +END_USE (vq); +return true; +} + +static void *virtqueue_detach_unused_buf_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +unsigned int i; +void *buf; + +START_USE (vq); + +for (i =0 ; i < vq->vring.num; i++) { +if (!vq->desc_state[i].data) +continue ; +/* detach_buf clears data, so grab it now. */ + buf = vq->desc_state[i].data; +detach_buf_split (vq, i,NULL ); + vq->avail_idx_shadow--; + vq->vring.avail->idx =cpu_to_virtio16 (_vq->vdev, vq->avail_idx_shadow); +END_USE (vq); +return buf; + } +/* That should have freed everything. */ +BUG_ON (vq->vq.num_free != vq->vring.num); + +END_USE (vq); +return NULL ; +} + +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +*
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 743
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver; +* +* // Device Event Suppression +* struct vring_packed_desc_event device; +* }; +*/
Why not just allocate event structures separately? Is it a win to have them share a cache line for some reason?
drivers/virtio/virtio_ring.c static inline int virtqueue_add(struct virtqueue *_vq, · line 752
Quoted 9 lines
drivers/virtio/virtio_ring.c +9
-0+static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +}
What's all this about alignment? Where does it come from?
Quoted 537 lines
drivers/virtio/virtio_ring.c +166
-225+ +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +} + +static inline int virtqueue_add_packed (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) +{ +return -EIO ; +} + +static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) +{ +return false; +} + +static inline bool more_used_packed (const struct vring_virtqueue *vq) +{ +return false; +} + +static void *virtqueue_get_buf_ctx_packed (struct virtqueue *_vq, +unsigned int *len, +void **ctx) +{ +return NULL ; +} + +static void virtqueue_disable_cb_packed (struct virtqueue *_vq) +{ +} + +static unsigned virtqueue_enable_cb_prepare_packed (struct virtqueue *_vq) +{ +return 0 ; +} + +static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned last_used_idx) +{ +return false; +} + +static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) +{ +return false; +} + +static void *virtqueue_detach_unused_buf_packed (struct virtqueue *_vq) +{ +return NULL ; +} + +static inline int virtqueue_add (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) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +return vq->packed ?virtqueue_add_packed (_vq, sgs, total_sg, out_sgs, + in_sgs, data, ctx, gfp) : +virtqueue_add_split (_vq, sgs, total_sg, out_sgs, + in_sgs, data, ctx, gfp); +} +/** * virtqueue_add_sgs - expose buffers to other end * @vq: the struct virtqueue we're talking about. @@ -550,34 +943,9 @@ EXPORT_SYMBOL_GPL(virtqueue_add_inbuf_ctx);bool virtqueue_kick_prepare (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 new, old; -bool needs_kick; -START_USE (vq); -/* We need to expose available array entries before checking avail -* event. */ -virtio_mb (vq->weak_barriers); - - old = vq->avail_idx_shadow - vq->num_added; - new = vq->avail_idx_shadow; - 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 (virtio16_to_cpu (_vq->vdev,vring_avail_event (&vq->vring)), - new, old); - }else { - needs_kick = !(vq->vring.used->flags &cpu_to_virtio16 (_vq->vdev,VRING_USED_F_NO_NOTIFY )); - } -END_USE (vq); -return needs_kick; +return vq->packed ?virtqueue_kick_prepare_packed (_vq) : +virtqueue_kick_prepare_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_kick_prepare);@@ -625,58 +993,9 @@ bool virtqueue_kick(struct virtqueue *vq) }EXPORT_SYMBOL_GPL (virtqueue_kick); -static void detach_buf (struct vring_virtqueue *vq,unsigned int head, -void **ctx) -{ -unsigned int i, j; -__virtio16 nextflag =cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_NEXT ); - -/* Clear data ptr. */ - vq->desc_state[head].data =NULL ; - -/* Put back on free list: unmap first-level descriptors and find end */ - i = head; - -while (vq->vring.desc[i].flags & nextflag) { -vring_unmap_one (vq, &vq->vring.desc[i]); - i =virtio16_to_cpu (vq->vq.vdev, vq->vring.desc[i].next); - vq->vq.num_free++; - } - -vring_unmap_one (vq, &vq->vring.desc[i]); - vq->vring.desc[i].next =cpu_to_virtio16 (vq->vq.vdev, vq->free_head); - vq->free_head = head; - -/* Plus final descriptor */ - vq->vq.num_free++; - -if (vq->indirect) { -struct vring_desc *indir_desc = vq->desc_state[head].indir_desc; -u32 len; - -/* Free the indirect table, if any, now that it's unmapped. */ -if (!indir_desc) -return ; - - len =virtio32_to_cpu (vq->vq.vdev, vq->vring.desc[head].len); - -BUG_ON (!(vq->vring.desc[head].flags & -cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_INDIRECT ))); -BUG_ON (len ==0 || len %sizeof (struct vring_desc )); - -for (j =0 ; j < len /sizeof (struct vring_desc ); j++) -vring_unmap_one (vq, &indir_desc[j]); - -kfree (indir_desc); - vq->desc_state[head].indir_desc =NULL ; - }else if (ctx) { - *ctx = vq->desc_state[head].indir_desc; - } -} -static inline bool more_used (const struct vring_virtqueue *vq) { -return vq->last_used_idx !=virtio16_to_cpu (vq->vq.vdev, vq->vring.used->idx); +return vq->packed ?more_used_packed (vq) :more_used_split (vq); }/** @@ -699,57 +1018,9 @@ void *virtqueue_get_buf_ctx(struct virtqueue *_vq, unsigned int *len, void **ctx) {struct vring_virtqueue *vq =to_vvq (_vq); -void *ret; -unsigned int i; -u16 last_used; -START_USE (vq); - -if (unlikely (vq->broken)) { -END_USE (vq); -return NULL ; - } - -if (!more_used (vq)) { -pr_debug ("No more buffers in queue\n" ); -END_USE (vq); -return NULL ; - } - -/* Only get used array entries after they have been exposed by host. */ -virtio_rmb (vq->weak_barriers); - - last_used = (vq->last_used_idx & (vq->vring.num -1 )); - i =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].id); - *len =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].len); - -if (unlikely (i >= vq->vring.num)) { -BAD_RING (vq,"id %u out of range\n" , i); -return NULL ; - } -if (unlikely (!vq->desc_state[i].data)) { -BAD_RING (vq,"id %u is not a head!\n" , i); -return NULL ; - } - -/* detach_buf clears data, so grab it now. */ - ret = vq->desc_state[i].data; -detach_buf (vq, i, ctx); - vq->last_used_idx++; -/* If we expect an interrupt for the next entry, tell host -* by writing event index and flush out the write before -* the read in the next get_buf call. */ -if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) -virtio_store_mb (vq->weak_barriers, - &vring_used_event (&vq->vring), -cpu_to_virtio16 (_vq->vdev, vq->last_used_idx)); - -#ifdef DEBUG - vq->last_add_time_valid = false; -#endif - -END_USE (vq); -return ret; +return vq->packed ?virtqueue_get_buf_ctx_packed (_vq, len, ctx) : +virtqueue_get_buf_ctx_split (_vq, len, ctx); }EXPORT_SYMBOL_GPL (virtqueue_get_buf_ctx);@@ -771,12 +1042,10 @@ void virtqueue_disable_cb(struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) { - vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } - +if (vq->packed) +virtqueue_disable_cb_packed (_vq); +else +virtqueue_disable_cb_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_disable_cb);@@ -795,23 +1064,9 @@ EXPORT_SYMBOL_GPL(virtqueue_disable_cb);unsigned virtqueue_enable_cb_prepare (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 last_used_idx; -START_USE (vq); - -/* We optimistically turn back on interrupts, then check if there was -* more to do. */ -/* Depending on the VIRTIO_RING_F_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. */ -if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { - vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } -vring_used_event (&vq->vring) =cpu_to_virtio16 (_vq->vdev, last_used_idx = vq->last_used_idx); -END_USE (vq); -return last_used_idx; +return vq->packed ?virtqueue_enable_cb_prepare_packed (_vq) : +virtqueue_enable_cb_prepare_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_enable_cb_prepare);@@ -828,8 +1083,8 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); -virtio_mb (vq->weak_barriers); -return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); +return vq->packed ?virtqueue_poll_packed (_vq, last_used_idx) : +virtqueue_poll_split (_vq, last_used_idx); }EXPORT_SYMBOL_GPL (virtqueue_poll);@@ -867,34 +1122,9 @@ 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 update the event index to keep code simple. */ -if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { - vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } -/* TODO: tune this threshold */ - bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) *3 /4 ; - -virtio_store_mb (vq->weak_barriers, - &vring_used_event (&vq->vring), -cpu_to_virtio16 (_vq->vdev, vq->last_used_idx + bufs)); - -if (unlikely ((u16)(virtio16_to_cpu (_vq->vdev, vq->vring.used->idx) - vq->last_used_idx) > bufs)) { -END_USE (vq); -return false; - } - -END_USE (vq); -return true; +return vq->packed ?virtqueue_enable_cb_delayed_packed (_vq) : +virtqueue_enable_cb_delayed_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_enable_cb_delayed);@@ -909,27 +1139,9 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed);void *virtqueue_detach_unused_buf (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -unsigned int i; -void *buf; -START_USE (vq); - -for (i =0 ; i < vq->vring.num; i++) { -if (!vq->desc_state[i].data) -continue ; -/* detach_buf clears data, so grab it now. */ - buf = vq->desc_state[i].data; -detach_buf (vq, i,NULL ); - vq->avail_idx_shadow--; - vq->vring.avail->idx =cpu_to_virtio16 (_vq->vdev, vq->avail_idx_shadow); -END_USE (vq); -return buf; - } -/* That should have freed everything. */ -BUG_ON (vq->vq.num_free != vq->vring.num); - -END_USE (vq); -return NULL ; +return vq->packed ?virtqueue_detach_unused_buf_packed (_vq) : +virtqueue_detach_unused_buf_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_detach_unused_buf);@@ -954,7 +1166,8 @@ irqreturn_t vring_interrupt(int irq, void *_vq)EXPORT_SYMBOL_GPL (vring_interrupt); struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool context,@@ -962,19 +1175,22 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *), const char *name) { - unsigned int i; struct vring_virtqueue *vq; +unsigned int num, i; +size_t size; - vq =kmalloc (sizeof (*vq) + vring.num *sizeof (struct vring_desc_state ), -GFP_KERNEL ); + num = packed ? vring.vring_packed.num : vring.vring_split.num; + size = packed ? num *sizeof (struct vring_desc_state_packed ) : + num *sizeof (struct vring_desc_state ); + + vq =kmalloc (sizeof (*vq) + size,GFP_KERNEL );if (!vq)return NULL ; - vq->vring = vring; vq->vq.callback = callback; vq->vq.vdev = vdev; vq->vq.name = name; - vq->vq.num_free = vring.num; + vq->vq.num_free = num; vq->vq.index = index; vq->we_own_ring = false; vq->queue_dma_addr =0 ;@@ -983,9 +1199,8 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, vq->weak_barriers = weak_barriers; vq->broken = false; vq->last_used_idx =0 ; - vq->avail_flags_shadow =0 ; - vq->avail_idx_shadow =0 ; vq->num_added =0 ; + vq->packed = packed;list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG vq->in_use = false;@@ -996,19 +1211,48 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, !context; vq->event =virtio_has_feature (vdev,VIRTIO_RING_F_EVENT_IDX ); +if (vq->packed) { + vq->vring_packed = vring.vring_packed; + vq->next_avail_idx =0 ; + vq->avail_wrap_counter =1 ; + vq->used_wrap_counter =1 ; + vq->event_flags_shadow =0 ; + +memset (vq->desc_state_packed,0 , + num *sizeof (struct vring_desc_state_packed )); + +/* Put everything in free lists. */ + vq->free_head =0 ; +for (i =0 ; i < num-1 ; i++) + vq->desc_state_packed[i].next = i +1 ; + }else { + vq->vring = vring.vring_split; + vq->avail_flags_shadow =0 ; + vq->avail_idx_shadow =0 ; + +/* Put everything in free lists. */ + vq->free_head =0 ; +for (i =0 ; i < num-1 ; i++) + vq->vring.desc[i].next =cpu_to_virtio16 (vdev, i +1 ); + +memset (vq->desc_state,0 , + num *sizeof (struct vring_desc_state )); + } +/* No callback? Tell other side not to bother us. */ if (!callback) { - vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (vdev, vq->avail_flags_shadow); +if (packed) { + vq->event_flags_shadow =VRING_EVENT_F_DISABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (vdev, + vq->event_flags_shadow); + }else { + vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (vdev, + vq->avail_flags_shadow); + } } -/* Put everything in free lists. */ - vq->free_head =0 ; -for (i =0 ; i < vring.num-1 ; i++) - vq->vring.desc[i].next =cpu_to_virtio16 (vdev, i +1 ); -memset (vq->desc_state,0 , vring.num *sizeof (struct vring_desc_state )); -return &vq->vq; }EXPORT_SYMBOL_GPL (__vring_new_virtqueue);@@ -1055,6 +1299,12 @@ static void vring_free_queue(struct virtio_device *vdev, size_t size, } } +static inline int +__vring_size (unsigned int num,unsigned long align,bool packed) +{ +return packed ?vring_size_packed (num, align) :vring_size (num, align); +} + struct virtqueue *vring_create_virtqueue( unsigned int index, unsigned int num,@@ -1071,7 +1321,8 @@ struct virtqueue *vring_create_virtqueue(void *queue =NULL ;dma_addr_t dma_addr;size_t queue_size_in_bytes; -struct vring vring; +union vring_union vring; +bool packed;/* We assume num is a power of 2. */ if (num & (num -1 )) {@@ -1079,9 +1330,13 @@ struct virtqueue *vring_create_virtqueue(return NULL ; } + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED ); +/* TODO: allocate each queue chunk individually */ -for (; num &&vring_size (num, vring_align) >PAGE_SIZE ; num /=2 ) { - queue =vring_alloc_queue (vdev,vring_size (num, vring_align), +for (; num &&__vring_size (num, vring_align, packed) >PAGE_SIZE ; + num /=2 ) { + queue =vring_alloc_queue (vdev,__vring_size (num, vring_align, + packed), &dma_addr,GFP_KERNEL |__GFP_NOWARN|__GFP_ZERO);if (queue)@@ -1093,17 +1348,21 @@ struct virtqueue *vring_create_virtqueue(if (!queue) {/* Try to get a single page. You are my only hope! */ - queue =vring_alloc_queue (vdev,vring_size (num, vring_align), + queue =vring_alloc_queue (vdev,__vring_size (num, vring_align, + packed), &dma_addr,GFP_KERNEL |__GFP_ZERO); }if (!queue)return NULL ; - queue_size_in_bytes =vring_size (num, vring_align); -vring_init (&vring, num, queue, vring_align); + queue_size_in_bytes =__vring_size (num, vring_align, packed); +if (packed) +vring_init_packed (&vring.vring_packed, num, queue, vring_align); +else +vring_init (&vring.vring_split, num, queue, vring_align); - vq =__vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + vq =__vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name);if (!vq) {vring_free_queue (vdev, queue_size_in_bytes, queue, dma_addr);@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align);
drivers/virtio/virtio_ring.c struct virtqueue *vring_new_virtqueue(unsigned int index, · line 1398
Quoted 10 lines
drivers/virtio/virtio_ring.c +8
-2-return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); +union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED ); +if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align);
vring_init in the UAPI header is more or less a bug. I'd just stop using it, keep it around for legacy userspace.
Quoted 82 lines
drivers/virtio/virtio_ring.c +16
-2+ +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;include/linux/virtio_ring.h +7
-1@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than @@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,Signature · 1 line
2.18.0
PATCH v2 2/5 virtio_ring: support creating packed ring
In reply to Tiwei BieThis commit introduces the support for creating packed ring. All split ring specific functions are added _split suffix. Some necessary stubs for packed ring are also added.
--- drivers/virtio/virtio_ring.c | 801 +++++++++++++++++++++++------------ include/linux/virtio_ring.h | 8 +- 2 files changed, 546 insertions(+), 263 deletions(-)
drivers/virtio/virtio_ring.c +539 -262
@@ -60,11 +60,15 @@ struct vring_desc_state {struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ }; +struct vring_desc_state_packed { +int next;/* The next desc state. */ +}; +struct vring_virtqueue {struct virtqueue vq; -/* Actual memory layout for this queue */ -struct vring vring; +/* Is this a packed ring? */ +bool packed;/* Can we use weak barriers? */ bool weak_barriers;
@@ -86,11 +90,39 @@ struct vring_virtqueue {/* Last used index we've seen. */ u16 last_used_idx; -/* Last written value to avail->flags */ -u16 avail_flags_shadow; +union { +/* Available for split ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring vring; -/* Last written value to avail->idx in guest byte order */ -u16 avail_idx_shadow; +/* Last written value to avail->flags */ +u16 avail_flags_shadow; + +/* Last written value to avail->idx in +* guest byte order. */ +u16 avail_idx_shadow; + }; + +/* Available for packed ring */ +struct { +/* Actual memory layout for this queue. */ +struct vring_packed vring_packed; + +/* Driver ring wrap counter. */ +bool avail_wrap_counter; + +/* Device ring wrap counter. */ +bool used_wrap_counter; + +/* Index of the next avail descriptor. */ +u16 next_avail_idx; + +/* Last written value to driver->flags in +* guest byte order. */ +u16 event_flags_shadow; + }; + };/* How to notify other side. FIXME: commonalize hcalls! */ bool (*notify)(struct virtqueue *vq);
@@ -110,11 +142,24 @@ struct vring_virtqueue {#endif /* Per-descriptor state. */ -struct vring_desc_state desc_state[]; +union { +struct vring_desc_state desc_state[1 ]; +struct vring_desc_state_packed desc_state_packed[1 ]; + }; };#define to_vvq (_vq) container_of(_vq, struct vring_virtqueue, vq) +static inline bool virtqueue_use_indirect (struct virtqueue *_vq, +unsigned int total_sg) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +/* If the host supports indirect descriptor tables, and we have multiple +* buffers, then go indirect. FIXME: tune this threshold */ +return (vq->indirect && total_sg >1 && vq->vq.num_free); +} +/* * Modern virtio devices have feature bits to specify whether they need a * quirk and bypass the IOMMU. If not there, just use the DMA API.
@@ -200,8 +245,17 @@ static dma_addr_t vring_map_single(const struct vring_virtqueue *vq, cpu_addr, size, direction); } -static void vring_unmap_one (const struct vring_virtqueue *vq, -struct vring_desc *desc) +static int vring_mapping_error (const struct vring_virtqueue *vq, +dma_addr_t addr) +{ +if (!vring_use_dma_api (vq->vq.vdev)) +return 0 ; + +return dma_mapping_error (vring_dma_dev (vq), addr); +} + +static void vring_unmap_one_split (const struct vring_virtqueue *vq, +struct vring_desc *desc) {u16 flags;
@@ -225,17 +279,9 @@ static void vring_unmap_one(const struct vring_virtqueue *vq, } } -static int vring_mapping_error (const struct vring_virtqueue *vq, -dma_addr_t addr) -{ -if (!vring_use_dma_api (vq->vq.vdev)) -return 0 ; - -return dma_mapping_error (vring_dma_dev (vq), addr); -} - -static struct vring_desc *alloc_indirect (struct virtqueue *_vq, -unsigned int total_sg,gfp_t gfp) +static struct vring_desc *alloc_indirect_split (struct virtqueue *_vq, +unsigned int total_sg, +gfp_t gfp) {struct vring_desc *desc;unsigned int i;
@@ -256,14 +302,14 @@ static struct vring_desc *alloc_indirect(struct virtqueue *_vq,return desc; } -static inline int virtqueue_add (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) +static inline int virtqueue_add_split (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) {struct vring_virtqueue *vq =to_vvq (_vq);struct scatterlist *sg;
@@ -299,10 +345,8 @@ static inline int virtqueue_add(struct virtqueue *_vq, head = vq->free_head; -/* If the host supports indirect descriptor tables, and we have multiple -* buffers, then go indirect. FIXME: tune this threshold */ -if (vq->indirect && total_sg >1 && vq->vq.num_free) - desc =alloc_indirect (_vq, total_sg, gfp); +if (virtqueue_use_indirect (_vq, total_sg)) + desc =alloc_indirect_split (_vq, total_sg, gfp);else { desc =NULL ;WARN_ON_ONCE (total_sg > vq->vring.num && !vq->indirect);
@@ -423,7 +467,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,for (n =0 ; n < total_sg; n++) {if (i == err_idx)break ; -vring_unmap_one (vq, &desc[i]); +vring_unmap_one_split (vq, &desc[i]); i =virtio16_to_cpu (_vq->vdev, vq->vring.desc[i].next); }
@@ -434,6 +478,355 @@ static inline int virtqueue_add(struct virtqueue *_vq,return -EIO ; } +static bool virtqueue_kick_prepare_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +u16 new, old; +bool needs_kick; + +START_USE (vq); +/* We need to expose available array entries before checking avail +* event. */ +virtio_mb (vq->weak_barriers); + + old = vq->avail_idx_shadow - vq->num_added; + new = vq->avail_idx_shadow; + 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 (virtio16_to_cpu (_vq->vdev,vring_avail_event (&vq->vring)), + new, old); + }else { + needs_kick = !(vq->vring.used->flags &cpu_to_virtio16 (_vq->vdev,VRING_USED_F_NO_NOTIFY )); + } +END_USE (vq); +return needs_kick; +} + +static void detach_buf_split (struct vring_virtqueue *vq,unsigned int head, +void **ctx) +{ +unsigned int i, j; +__virtio16 nextflag =cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_NEXT ); + +/* Clear data ptr. */ + vq->desc_state[head].data =NULL ; + +/* Put back on free list: unmap first-level descriptors and find end */ + i = head; + +while (vq->vring.desc[i].flags & nextflag) { +vring_unmap_one_split (vq, &vq->vring.desc[i]); + i =virtio16_to_cpu (vq->vq.vdev, vq->vring.desc[i].next); + vq->vq.num_free++; + } + +vring_unmap_one_split (vq, &vq->vring.desc[i]); + vq->vring.desc[i].next =cpu_to_virtio16 (vq->vq.vdev, vq->free_head); + vq->free_head = head; + +/* Plus final descriptor */ + vq->vq.num_free++; + +if (vq->indirect) { +struct vring_desc *indir_desc = vq->desc_state[head].indir_desc; +u32 len; + +/* Free the indirect table, if any, now that it's unmapped. */ +if (!indir_desc) +return ; + + len =virtio32_to_cpu (vq->vq.vdev, vq->vring.desc[head].len); + +BUG_ON (!(vq->vring.desc[head].flags & +cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_INDIRECT ))); +BUG_ON (len ==0 || len %sizeof (struct vring_desc )); + +for (j =0 ; j < len /sizeof (struct vring_desc ); j++) +vring_unmap_one_split (vq, &indir_desc[j]); + +kfree (indir_desc); + vq->desc_state[head].indir_desc =NULL ; + }else if (ctx) { + *ctx = vq->desc_state[head].indir_desc; + } +} + +static inline bool more_used_split (const struct vring_virtqueue *vq) +{ +return vq->last_used_idx !=virtio16_to_cpu (vq->vq.vdev, vq->vring.used->idx); +} + +static void *virtqueue_get_buf_ctx_split (struct virtqueue *_vq, +unsigned int *len, +void **ctx) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +void *ret; +unsigned int i; +u16 last_used; + +START_USE (vq); + +if (unlikely (vq->broken)) { +END_USE (vq); +return NULL ; + } + +if (!more_used_split (vq)) { +pr_debug ("No more buffers in queue\n" ); +END_USE (vq); +return NULL ; + } + +/* Only get used array entries after they have been exposed by host. */ +virtio_rmb (vq->weak_barriers); + + last_used = (vq->last_used_idx & (vq->vring.num -1 )); + i =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].id); + *len =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].len); + +if (unlikely (i >= vq->vring.num)) { +BAD_RING (vq,"id %u out of range\n" , i); +return NULL ; + } +if (unlikely (!vq->desc_state[i].data)) { +BAD_RING (vq,"id %u is not a head!\n" , i); +return NULL ; + } + +/* detach_buf_split clears data, so grab it now. */ + ret = vq->desc_state[i].data; +detach_buf_split (vq, i, ctx); + vq->last_used_idx++; +/* If we expect an interrupt for the next entry, tell host +* by writing event index and flush out the write before +* the read in the next get_buf call. */ +if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) +virtio_store_mb (vq->weak_barriers, + &vring_used_event (&vq->vring), +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx)); + +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif + +END_USE (vq); +return ret; +} + +static void virtqueue_disable_cb_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) { + vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +} + +static unsigned virtqueue_enable_cb_prepare_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +u16 last_used_idx; + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ +/* Depending on the VIRTIO_RING_F_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. */ +if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { + vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +vring_used_event (&vq->vring) =cpu_to_virtio16 (_vq->vdev, last_used_idx = vq->last_used_idx); +END_USE (vq); +return last_used_idx; +} + +static bool virtqueue_poll_split (struct virtqueue *_vq,unsigned last_used_idx) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +virtio_mb (vq->weak_barriers); +return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); +} + +static bool virtqueue_enable_cb_delayed_split (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 update the event index to keep code simple. */ +if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { + vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); + } +/* TODO: tune this threshold */ + bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) *3 /4 ; + +virtio_store_mb (vq->weak_barriers, + &vring_used_event (&vq->vring), +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx + bufs)); + +if (unlikely ((u16)(virtio16_to_cpu (_vq->vdev, vq->vring.used->idx) - vq->last_used_idx) > bufs)) { +END_USE (vq); +return false; + } + +END_USE (vq); +return true; +} + +static void *virtqueue_detach_unused_buf_split (struct virtqueue *_vq) +{ +struct vring_virtqueue *vq =to_vvq (_vq); +unsigned int i; +void *buf; + +START_USE (vq); + +for (i =0 ; i < vq->vring.num; i++) { +if (!vq->desc_state[i].data) +continue ; +/* detach_buf clears data, so grab it now. */ + buf = vq->desc_state[i].data; +detach_buf_split (vq, i,NULL ); + vq->avail_idx_shadow--; + vq->vring.avail->idx =cpu_to_virtio16 (_vq->vdev, vq->avail_idx_shadow); +END_USE (vq); +return buf; + } +/* That should have freed everything. */ +BUG_ON (vq->vq.num_free != vq->vring.num); + +END_USE (vq); +return NULL ; +} + +/* +* The layout for the packed ring is a continuous chunk of memory +* which looks like this. +* +* struct vring_packed { +* // The actual descriptors (16 bytes each) +* struct vring_packed_desc desc[num]; +* +* // Padding to the next align boundary. +* char pad[]; +* +* // Driver Event Suppression +* struct vring_packed_desc_event driver; +* +* // Device Event Suppression +* struct vring_packed_desc_event device; +* }; +*/ +static inline void vring_init_packed (struct vring_packed *vr,unsigned int num, +void *p,unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->driver = (void *)ALIGN (((uintptr_t )p + +sizeof (struct vring_packed_desc ) * num), align); + vr->device = vr->driver +1 ; +} + +static inline unsigned vring_size_packed (unsigned int num,unsigned long align) +{ +return ((sizeof (struct vring_packed_desc ) * num + align -1 ) + & ~(align -1 )) +sizeof (struct vring_packed_desc_event ) *2 ; +} + +static inline int virtqueue_add_packed (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) +{ +return -EIO ; +} + +static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) +{ +return false; +} + +static inline bool more_used_packed (const struct vring_virtqueue *vq) +{ +return false; +} + +static void *virtqueue_get_buf_ctx_packed (struct virtqueue *_vq, +unsigned int *len, +void **ctx) +{ +return NULL ; +} + +static void virtqueue_disable_cb_packed (struct virtqueue *_vq) +{ +} + +static unsigned virtqueue_enable_cb_prepare_packed (struct virtqueue *_vq) +{ +return 0 ; +} + +static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned last_used_idx) +{ +return false; +} + +static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) +{ +return false; +} + +static void *virtqueue_detach_unused_buf_packed (struct virtqueue *_vq) +{ +return NULL ; +} + +static inline int virtqueue_add (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) +{ +struct vring_virtqueue *vq =to_vvq (_vq); + +return vq->packed ?virtqueue_add_packed (_vq, sgs, total_sg, out_sgs, + in_sgs, data, ctx, gfp) : +virtqueue_add_split (_vq, sgs, total_sg, out_sgs, + in_sgs, data, ctx, gfp); +} +/** * virtqueue_add_sgs - expose buffers to other end * @vq: the struct virtqueue we're talking about.
@@ -550,34 +943,9 @@ EXPORT_SYMBOL_GPL(virtqueue_add_inbuf_ctx);bool virtqueue_kick_prepare (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 new, old; -bool needs_kick; -START_USE (vq); -/* We need to expose available array entries before checking avail -* event. */ -virtio_mb (vq->weak_barriers); - - old = vq->avail_idx_shadow - vq->num_added; - new = vq->avail_idx_shadow; - 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 (virtio16_to_cpu (_vq->vdev,vring_avail_event (&vq->vring)), - new, old); - }else { - needs_kick = !(vq->vring.used->flags &cpu_to_virtio16 (_vq->vdev,VRING_USED_F_NO_NOTIFY )); - } -END_USE (vq); -return needs_kick; +return vq->packed ?virtqueue_kick_prepare_packed (_vq) : +virtqueue_kick_prepare_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_kick_prepare);
@@ -625,58 +993,9 @@ bool virtqueue_kick(struct virtqueue *vq) }EXPORT_SYMBOL_GPL (virtqueue_kick); -static void detach_buf (struct vring_virtqueue *vq,unsigned int head, -void **ctx) -{ -unsigned int i, j; -__virtio16 nextflag =cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_NEXT ); - -/* Clear data ptr. */ - vq->desc_state[head].data =NULL ; - -/* Put back on free list: unmap first-level descriptors and find end */ - i = head; - -while (vq->vring.desc[i].flags & nextflag) { -vring_unmap_one (vq, &vq->vring.desc[i]); - i =virtio16_to_cpu (vq->vq.vdev, vq->vring.desc[i].next); - vq->vq.num_free++; - } - -vring_unmap_one (vq, &vq->vring.desc[i]); - vq->vring.desc[i].next =cpu_to_virtio16 (vq->vq.vdev, vq->free_head); - vq->free_head = head; - -/* Plus final descriptor */ - vq->vq.num_free++; - -if (vq->indirect) { -struct vring_desc *indir_desc = vq->desc_state[head].indir_desc; -u32 len; - -/* Free the indirect table, if any, now that it's unmapped. */ -if (!indir_desc) -return ; - - len =virtio32_to_cpu (vq->vq.vdev, vq->vring.desc[head].len); - -BUG_ON (!(vq->vring.desc[head].flags & -cpu_to_virtio16 (vq->vq.vdev,VRING_DESC_F_INDIRECT ))); -BUG_ON (len ==0 || len %sizeof (struct vring_desc )); - -for (j =0 ; j < len /sizeof (struct vring_desc ); j++) -vring_unmap_one (vq, &indir_desc[j]); - -kfree (indir_desc); - vq->desc_state[head].indir_desc =NULL ; - }else if (ctx) { - *ctx = vq->desc_state[head].indir_desc; - } -} -static inline bool more_used (const struct vring_virtqueue *vq) { -return vq->last_used_idx !=virtio16_to_cpu (vq->vq.vdev, vq->vring.used->idx); +return vq->packed ?more_used_packed (vq) :more_used_split (vq); }/**
@@ -699,57 +1018,9 @@ void *virtqueue_get_buf_ctx(struct virtqueue *_vq, unsigned int *len, void **ctx) {struct vring_virtqueue *vq =to_vvq (_vq); -void *ret; -unsigned int i; -u16 last_used; -START_USE (vq); - -if (unlikely (vq->broken)) { -END_USE (vq); -return NULL ; - } - -if (!more_used (vq)) { -pr_debug ("No more buffers in queue\n" ); -END_USE (vq); -return NULL ; - } - -/* Only get used array entries after they have been exposed by host. */ -virtio_rmb (vq->weak_barriers); - - last_used = (vq->last_used_idx & (vq->vring.num -1 )); - i =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].id); - *len =virtio32_to_cpu (_vq->vdev, vq->vring.used->ring[last_used].len); - -if (unlikely (i >= vq->vring.num)) { -BAD_RING (vq,"id %u out of range\n" , i); -return NULL ; - } -if (unlikely (!vq->desc_state[i].data)) { -BAD_RING (vq,"id %u is not a head!\n" , i); -return NULL ; - } - -/* detach_buf clears data, so grab it now. */ - ret = vq->desc_state[i].data; -detach_buf (vq, i, ctx); - vq->last_used_idx++; -/* If we expect an interrupt for the next entry, tell host -* by writing event index and flush out the write before -* the read in the next get_buf call. */ -if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) -virtio_store_mb (vq->weak_barriers, - &vring_used_event (&vq->vring), -cpu_to_virtio16 (_vq->vdev, vq->last_used_idx)); - -#ifdef DEBUG - vq->last_add_time_valid = false; -#endif - -END_USE (vq); -return ret; +return vq->packed ?virtqueue_get_buf_ctx_packed (_vq, len, ctx) : +virtqueue_get_buf_ctx_split (_vq, len, ctx); }EXPORT_SYMBOL_GPL (virtqueue_get_buf_ctx);
@@ -771,12 +1042,10 @@ void virtqueue_disable_cb(struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -if (!(vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT )) { - vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } - +if (vq->packed) +virtqueue_disable_cb_packed (_vq); +else +virtqueue_disable_cb_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_disable_cb);
@@ -795,23 +1064,9 @@ EXPORT_SYMBOL_GPL(virtqueue_disable_cb);unsigned virtqueue_enable_cb_prepare (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 last_used_idx; -START_USE (vq); - -/* We optimistically turn back on interrupts, then check if there was -* more to do. */ -/* Depending on the VIRTIO_RING_F_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. */ -if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { - vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } -vring_used_event (&vq->vring) =cpu_to_virtio16 (_vq->vdev, last_used_idx = vq->last_used_idx); -END_USE (vq); -return last_used_idx; +return vq->packed ?virtqueue_enable_cb_prepare_packed (_vq) : +virtqueue_enable_cb_prepare_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_enable_cb_prepare);
@@ -828,8 +1083,8 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); -virtio_mb (vq->weak_barriers); -return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); +return vq->packed ?virtqueue_poll_packed (_vq, last_used_idx) : +virtqueue_poll_split (_vq, last_used_idx); }EXPORT_SYMBOL_GPL (virtqueue_poll);
@@ -867,34 +1122,9 @@ 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 update the event index to keep code simple. */ -if (vq->avail_flags_shadow &VRING_AVAIL_F_NO_INTERRUPT ) { - vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (_vq->vdev, vq->avail_flags_shadow); - } -/* TODO: tune this threshold */ - bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) *3 /4 ; - -virtio_store_mb (vq->weak_barriers, - &vring_used_event (&vq->vring), -cpu_to_virtio16 (_vq->vdev, vq->last_used_idx + bufs)); - -if (unlikely ((u16)(virtio16_to_cpu (_vq->vdev, vq->vring.used->idx) - vq->last_used_idx) > bufs)) { -END_USE (vq); -return false; - } - -END_USE (vq); -return true; +return vq->packed ?virtqueue_enable_cb_delayed_packed (_vq) : +virtqueue_enable_cb_delayed_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_enable_cb_delayed);
@@ -909,27 +1139,9 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed);void *virtqueue_detach_unused_buf (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -unsigned int i; -void *buf; -START_USE (vq); - -for (i =0 ; i < vq->vring.num; i++) { -if (!vq->desc_state[i].data) -continue ; -/* detach_buf clears data, so grab it now. */ - buf = vq->desc_state[i].data; -detach_buf (vq, i,NULL ); - vq->avail_idx_shadow--; - vq->vring.avail->idx =cpu_to_virtio16 (_vq->vdev, vq->avail_idx_shadow); -END_USE (vq); -return buf; - } -/* That should have freed everything. */ -BUG_ON (vq->vq.num_free != vq->vring.num); - -END_USE (vq); -return NULL ; +return vq->packed ?virtqueue_detach_unused_buf_packed (_vq) : +virtqueue_detach_unused_buf_split (_vq); }EXPORT_SYMBOL_GPL (virtqueue_detach_unused_buf);
@@ -954,7 +1166,8 @@ irqreturn_t vring_interrupt(int irq, void *_vq)EXPORT_SYMBOL_GPL (vring_interrupt); struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool context,
@@ -962,19 +1175,22 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *), const char *name) { - unsigned int i; struct vring_virtqueue *vq; +unsigned int num, i; +size_t size; - vq =kmalloc (sizeof (*vq) + vring.num *sizeof (struct vring_desc_state ), -GFP_KERNEL ); + num = packed ? vring.vring_packed.num : vring.vring_split.num; + size = packed ? num *sizeof (struct vring_desc_state_packed ) : + num *sizeof (struct vring_desc_state ); + + vq =kmalloc (sizeof (*vq) + size,GFP_KERNEL );if (!vq)return NULL ; - vq->vring = vring; vq->vq.callback = callback; vq->vq.vdev = vdev; vq->vq.name = name; - vq->vq.num_free = vring.num; + vq->vq.num_free = num; vq->vq.index = index; vq->we_own_ring = false; vq->queue_dma_addr =0 ;
@@ -983,9 +1199,8 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, vq->weak_barriers = weak_barriers; vq->broken = false; vq->last_used_idx =0 ; - vq->avail_flags_shadow =0 ; - vq->avail_idx_shadow =0 ; vq->num_added =0 ; + vq->packed = packed;list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG vq->in_use = false;
@@ -996,19 +1211,48 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, !context; vq->event =virtio_has_feature (vdev,VIRTIO_RING_F_EVENT_IDX ); +if (vq->packed) { + vq->vring_packed = vring.vring_packed; + vq->next_avail_idx =0 ; + vq->avail_wrap_counter =1 ; + vq->used_wrap_counter =1 ; + vq->event_flags_shadow =0 ; + +memset (vq->desc_state_packed,0 , + num *sizeof (struct vring_desc_state_packed )); + +/* Put everything in free lists. */ + vq->free_head =0 ; +for (i =0 ; i < num-1 ; i++) + vq->desc_state_packed[i].next = i +1 ; + }else { + vq->vring = vring.vring_split; + vq->avail_flags_shadow =0 ; + vq->avail_idx_shadow =0 ; + +/* Put everything in free lists. */ + vq->free_head =0 ; +for (i =0 ; i < num-1 ; i++) + vq->vring.desc[i].next =cpu_to_virtio16 (vdev, i +1 ); + +memset (vq->desc_state,0 , + num *sizeof (struct vring_desc_state )); + } +/* No callback? Tell other side not to bother us. */ if (!callback) { - vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; -if (!vq->event) - vq->vring.avail->flags =cpu_to_virtio16 (vdev, vq->avail_flags_shadow); +if (packed) { + vq->event_flags_shadow =VRING_EVENT_F_DISABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (vdev, + vq->event_flags_shadow); + }else { + vq->avail_flags_shadow |=VRING_AVAIL_F_NO_INTERRUPT ; +if (!vq->event) + vq->vring.avail->flags =cpu_to_virtio16 (vdev, + vq->avail_flags_shadow); + } } -/* Put everything in free lists. */ - vq->free_head =0 ; -for (i =0 ; i < vring.num-1 ; i++) - vq->vring.desc[i].next =cpu_to_virtio16 (vdev, i +1 ); -memset (vq->desc_state,0 , vring.num *sizeof (struct vring_desc_state )); -return &vq->vq; }EXPORT_SYMBOL_GPL (__vring_new_virtqueue);
@@ -1055,6 +1299,12 @@ static void vring_free_queue(struct virtio_device *vdev, size_t size, } } +static inline int +__vring_size (unsigned int num,unsigned long align,bool packed) +{ +return packed ?vring_size_packed (num, align) :vring_size (num, align); +} + struct virtqueue *vring_create_virtqueue( unsigned int index, unsigned int num,
@@ -1071,7 +1321,8 @@ struct virtqueue *vring_create_virtqueue(void *queue =NULL ;dma_addr_t dma_addr;size_t queue_size_in_bytes; -struct vring vring; +union vring_union vring; +bool packed;/* We assume num is a power of 2. */ if (num & (num -1 )) {
@@ -1079,9 +1330,13 @@ struct virtqueue *vring_create_virtqueue(return NULL ; } + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED ); +/* TODO: allocate each queue chunk individually */ -for (; num &&vring_size (num, vring_align) >PAGE_SIZE ; num /=2 ) { - queue =vring_alloc_queue (vdev,vring_size (num, vring_align), +for (; num &&__vring_size (num, vring_align, packed) >PAGE_SIZE ; + num /=2 ) { + queue =vring_alloc_queue (vdev,__vring_size (num, vring_align, + packed), &dma_addr,GFP_KERNEL |__GFP_NOWARN|__GFP_ZERO);if (queue)
@@ -1093,17 +1348,21 @@ struct virtqueue *vring_create_virtqueue(if (!queue) {/* Try to get a single page. You are my only hope! */ - queue =vring_alloc_queue (vdev,vring_size (num, vring_align), + queue =vring_alloc_queue (vdev,__vring_size (num, vring_align, + packed), &dma_addr,GFP_KERNEL |__GFP_ZERO); }if (!queue)return NULL ; - queue_size_in_bytes =vring_size (num, vring_align); -vring_init (&vring, num, queue, vring_align); + queue_size_in_bytes =__vring_size (num, vring_align, packed); +if (packed) +vring_init_packed (&vring.vring_packed, num, queue, vring_align); +else +vring_init (&vring.vring_split, num, queue, vring_align); - vq =__vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + vq =__vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name);if (!vq) {vring_free_queue (vdev, queue_size_in_bytes, queue, dma_addr);
@@ -1129,10 +1388,17 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; -vring_init (&vring, num, pages, vring_align); -return __vring_new_virtqueue (index, vring, vdev, weak_barriers, context, - notify, callback, name); + union vring_union vring; +bool packed; + + packed =virtio_has_feature (vdev,VIRTIO_F_RING_PACKED ); +if (packed) +vring_init_packed (&vring.vring_packed, num, pages, vring_align); +else +vring_init (&vring.vring_split, num, pages, vring_align); + +return __vring_new_virtqueue (index, vring, packed, vdev, weak_barriers, + context, notify, callback, name); }EXPORT_SYMBOL_GPL (vring_new_virtqueue);
@@ -1142,7 +1408,9 @@ void vring_del_virtqueue(struct virtqueue *_vq)if (vq->we_own_ring) {vring_free_queue (vq->vq.vdev, vq->queue_size_in_bytes, - vq->vring.desc, vq->queue_dma_addr); + vq->packed ? (void *)vq->vring_packed.desc : + (void *)vq->vring.desc, + vq->queue_dma_addr); }list_del (&_vq->list);kfree (vq);
@@ -1184,7 +1452,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)struct vring_virtqueue *vq =to_vvq (_vq); -return vq->vring.num; +return vq->packed ? vq->vring_packed.num : vq->vring.num; }EXPORT_SYMBOL_GPL (virtqueue_get_vring_size);
@@ -1227,6 +1495,10 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.driver - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); }
@@ -1238,11 +1510,16 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)BUG_ON (!vq->we_own_ring); +if (vq->packed) +return vq->queue_dma_addr + ((char *)vq->vring_packed.device - + (char *)vq->vring_packed.desc); +return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); }EXPORT_SYMBOL_GPL (virtqueue_get_used_addr); +/* Only available for split ring */ const struct vring *virtqueue_get_vring (struct virtqueue *vq) {return &to_vvq (vq)->vring;
include/linux/virtio_ring.h +7 -1
@@ -60,6 +60,11 @@ static inline void virtio_store_mb(bool weak_barriers,struct virtio_device ;struct virtqueue ; +union vring_union { +struct vring vring_split; +struct vring_packed vring_packed; +}; +/* * Creates a virtqueue and allocates the descriptor ring. If * may_reduce_num is set, then this may allocate a smaller ring than
@@ -79,7 +84,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index,/* Creates a virtqueue with a custom layout. */ struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, + union vring_union vring, + bool packed, struct virtio_device *vdev, bool weak_barriers, bool ctx,
Signature · 1 line
2.18.0
PATCH v2 3/5 virtio_ring: add packed ring support
In reply to Tiwei BieThis commit introduces the support (without EVENT_IDX) for packed ring.
--- drivers/virtio/virtio_ring.c | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 8 deletions(-)
drivers/virtio/virtio_ring.c +487 -8
@@ -55,12 +55,21 @@#define END_USE (vq)#endif +#define _VRING_DESC_F_AVAIL (b) ((__u16)(b) << 7) +#define _VRING_DESC_F_USED (b) ((__u16)(b) << 15) +struct vring_desc_state {void *data;/* Data for callback. */ struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };struct vring_desc_state_packed { +void *data;/* Data for callback. */ +struct vring_packed_desc *indir_desc;/* Indirect descriptor, if any. */ +int num;/* Descriptor list length. */ +dma_addr_t addr;/* Buffer DMA addr. */ +u32 len;/* Buffer length. */ +u16 flags;/* Descriptor flags. */ int next;/* The next desc state. */ };
@@ -660,7 +669,6 @@ static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); -virtio_mb (vq->weak_barriers);return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); }
@@ -757,6 +765,72 @@ static inline unsigned vring_size_packed(unsigned int num, unsigned long align) & ~(align - 1)) + sizeof(struct vring_packed_desc_event) * 2; } +static void vring_unmap_state_packed (const struct vring_virtqueue *vq, +struct vring_desc_state_packed *state) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags = state->flags; + +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +} + +static void vring_unmap_desc_packed (const struct vring_virtqueue *vq, +struct vring_packed_desc *desc) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags =virtio16_to_cpu (vq->vq.vdev, desc->flags); + +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +} + +static struct vring_packed_desc *alloc_indirect_packed (struct virtqueue *_vq, +unsigned int total_sg, +gfp_t gfp) +{ +struct vring_packed_desc *desc; + +/* +* We require lowmem mappings for the descriptors because +* otherwise virt_to_phys will give us bogus addresses in the +* virtqueue. +*/ + gfp &= ~__GFP_HIGHMEM; + + desc =kmalloc (total_sg *sizeof (struct vring_packed_desc ), gfp); + +return desc; +} + static inline int virtqueue_add_packed(struct virtqueue *_vq, struct scatterlist *sgs[], unsigned int total_sg,
@@ -766,47 +840,449 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, void *ctx, gfp_t gfp) { +struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_packed_desc *desc; +struct scatterlist *sg; +unsigned int i, n, descs_used,uninitialized_var (prev ), err_idx; +__virtio16 uninitialized_var (head_flags ), flags; +u16 head, avail_wrap_counter, id, curr; +bool indirect; + +START_USE (vq); + +BUG_ON (data ==NULL ); +BUG_ON (ctx && vq->indirect); + +if (unlikely (vq->broken)) { +END_USE (vq); +return -EIO ; + } + +#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 + +BUG_ON (total_sg ==0 ); + + head = vq->next_avail_idx; + avail_wrap_counter = vq->avail_wrap_counter; + +if (virtqueue_use_indirect (_vq, total_sg)) + desc =alloc_indirect_packed (_vq, total_sg, gfp); +else { + desc =NULL ; +WARN_ON_ONCE (total_sg > vq->vring_packed.num && !vq->indirect); + } + +if (desc) { +/* Use a single buffer which doesn't continue */ + indirect = true; +/* Set up rest to use this indirect table. */ + i =0 ; + descs_used =1 ; + }else { + indirect = false; + desc = vq->vring_packed.desc; + i = head; + descs_used = total_sg; + } + +if (vq->vq.num_free < descs_used) { +pr_debug ("Can't add buf len %i - avail = %i\n" , + descs_used, vq->vq.num_free); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out_sgs) + vq->notify (&vq->vq); +if (indirect) +kfree (desc); +END_USE (vq); +return -ENOSPC ; + } + + id = vq->free_head; +BUG_ON (id == vq->vring_packed.num); + + curr = id; +for (n =0 ; n < out_sgs + in_sgs; n++) { +for (sg = sgs[n]; sg; sg =sg_next (sg)) { +dma_addr_t addr =vring_map_one_sg (vq, sg, n < out_sgs ? +DMA_TO_DEVICE :DMA_FROM_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_NEXT | + (n < out_sgs ?0 :VRING_DESC_F_WRITE ) | +_VRING_DESC_F_AVAIL (vq->avail_wrap_counter) | +_VRING_DESC_F_USED (!vq->avail_wrap_counter)); +if (!indirect && i == head) + head_flags = flags; +else + desc[i].flags = flags; + + desc[i].addr =cpu_to_virtio64 (_vq->vdev, addr); + desc[i].len =cpu_to_virtio32 (_vq->vdev, sg->length); + i++; +if (!indirect) { +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[curr].addr = addr; + vq->desc_state_packed[curr].len = + sg->length; + vq->desc_state_packed[curr].flags = +virtio16_to_cpu (_vq->vdev, + flags); + } + curr = vq->desc_state_packed[curr].next; + +if (i >= vq->vring_packed.num) { + i =0 ; + vq->avail_wrap_counter ^=1 ; + } + } + } + } + + prev = (i >0 ? i : vq->vring_packed.num) -1 ; + desc[prev].id =cpu_to_virtio16 (_vq->vdev, id); + +/* Last one doesn't continue. */ +if (total_sg ==1 ) + head_flags &=cpu_to_virtio16 (_vq->vdev, ~VRING_DESC_F_NEXT ); +else + desc[prev].flags &=cpu_to_virtio16 (_vq->vdev, + ~VRING_DESC_F_NEXT ); + +if (indirect) { +/* Now that the indirect table is filled in, map it. */ +dma_addr_t addr =vring_map_single ( + vq, desc, total_sg *sizeof (struct vring_packed_desc ), +DMA_TO_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + head_flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_INDIRECT | +_VRING_DESC_F_AVAIL (avail_wrap_counter) | +_VRING_DESC_F_USED (!avail_wrap_counter)); + vq->vring_packed.desc[head].addr =cpu_to_virtio64 (_vq->vdev, + addr); + vq->vring_packed.desc[head].len =cpu_to_virtio32 (_vq->vdev, + total_sg *sizeof (struct vring_packed_desc )); + vq->vring_packed.desc[head].id =cpu_to_virtio16 (_vq->vdev, id); + +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[id].addr = addr; + vq->desc_state_packed[id].len = total_sg * +sizeof (struct vring_packed_desc ); + vq->desc_state_packed[id].flags = +virtio16_to_cpu (_vq->vdev, head_flags); + } + } + +/* We're using some buffers from the free list. */ + vq->vq.num_free -= descs_used; + +/* Update free pointer */ +if (indirect) { + n = head +1 ; +if (n >= vq->vring_packed.num) { + n =0 ; + vq->avail_wrap_counter ^=1 ; + } + vq->next_avail_idx = n; + vq->free_head = vq->desc_state_packed[id].next; + }else { + vq->next_avail_idx = i; + vq->free_head = curr; + } + +/* Store token and indirect buffer state. */ + vq->desc_state_packed[id].num = descs_used; + vq->desc_state_packed[id].data = data; +if (indirect) + vq->desc_state_packed[id].indir_desc = desc; +else + vq->desc_state_packed[id].indir_desc = ctx; + +/* A driver MUST NOT make the first descriptor in the list +* available before all subsequent descriptors comprising +* the list are made available. */ +virtio_wmb (vq->weak_barriers); + vq->vring_packed.desc[head].flags = head_flags; + vq->num_added += descs_used; + +pr_debug ("Added buffer head %i to %p\n" , head, vq); +END_USE (vq); + +return 0 ; + +unmap_release: + err_idx = i; + i = head; + +for (n =0 ; n < total_sg; n++) { +if (i == err_idx) +break ; +vring_unmap_desc_packed (vq, &desc[i]); + i++; +if (!indirect && i >= vq->vring_packed.num) + i =0 ; + } + + vq->avail_wrap_counter = avail_wrap_counter; + +if (indirect) +kfree (desc); + +END_USE (vq);return -EIO ; }static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 flags; +bool needs_kick; +u32 snapshot; + +START_USE (vq); +/* We need to expose the new flags value before checking notification +* suppressions. */ +virtio_mb (vq->weak_barriers); + + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ; + +#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 + + needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +END_USE (vq); +return needs_kick; +} + +static void detach_buf_packed (struct vring_virtqueue *vq, +unsigned int id,void **ctx) +{ +struct vring_desc_state_packed *state =NULL ; +struct vring_packed_desc *desc; +unsigned int curr, i; + +/* Clear data ptr. */ + vq->desc_state_packed[id].data =NULL ; + + curr = id; +for (i =0 ; i < vq->desc_state_packed[id].num; i++) { + state = &vq->desc_state_packed[curr]; +vring_unmap_state_packed (vq, state); + curr = state->next; + } + +BUG_ON (state ==NULL ); + vq->vq.num_free += vq->desc_state_packed[id].num; + state->next = vq->free_head; + vq->free_head = id; + +if (vq->indirect) { +u32 len; + +/* Free the indirect table, if any, now that it's unmapped. */ + desc = vq->desc_state_packed[id].indir_desc; +if (!desc) +return ; + +if (vring_use_dma_api (vq->vq.vdev)) { + len = vq->desc_state_packed[id].len; +for (i =0 ; i < len /sizeof (struct vring_packed_desc ); + i++) +vring_unmap_desc_packed (vq, &desc[i]); + } +kfree (desc); + vq->desc_state_packed[id].indir_desc =NULL ; + }else if (ctx) { + *ctx = vq->desc_state_packed[id].indir_desc; + } +} + +static inline bool is_used_desc_packed (const struct vring_virtqueue *vq, +u16 idx,bool used_wrap_counter) +{ +u16 flags; +bool avail, used; + + flags =virtio16_to_cpu (vq->vq.vdev, + vq->vring_packed.desc[idx].flags); + avail = !!(flags &VRING_DESC_F_AVAIL ); + used = !!(flags &VRING_DESC_F_USED ); + +return avail == used && used == used_wrap_counter; }static inline bool more_used_packed (const struct vring_virtqueue *vq) { -return false; +return is_used_desc_packed (vq, vq->last_used_idx, + vq->used_wrap_counter); }static void *virtqueue_get_buf_ctx_packed (struct virtqueue *_vq,unsigned int *len,void **ctx) { -return NULL ; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 last_used, id; +void *ret; + +START_USE (vq); + +if (unlikely (vq->broken)) { +END_USE (vq); +return NULL ; + } + +if (!more_used_packed (vq)) { +pr_debug ("No more buffers in queue\n" ); +END_USE (vq); +return NULL ; + } + +/* Only get used elements after they have been exposed by host. */ +virtio_rmb (vq->weak_barriers); + + last_used = vq->last_used_idx; + id =virtio16_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].id); + *len =virtio32_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].len); + +if (unlikely (id >= vq->vring_packed.num)) { +BAD_RING (vq,"id %u out of range\n" , id); +return NULL ; + } +if (unlikely (!vq->desc_state_packed[id].data)) { +BAD_RING (vq,"id %u is not a head!\n" , id); +return NULL ; + } + + vq->last_used_idx += vq->desc_state_packed[id].num; +if (vq->last_used_idx >= vq->vring_packed.num) { + vq->last_used_idx -= vq->vring_packed.num; + vq->used_wrap_counter ^=1 ; + } + +/* detach_buf_packed clears data, so grab it now. */ + ret = vq->desc_state_packed[id].data; +detach_buf_packed (vq, id, ctx); + +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif + +END_USE (vq); +return ret; }static void virtqueue_disable_cb_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); + +if (vq->event_flags_shadow !=VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_DISABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } }static unsigned virtqueue_enable_cb_prepare_packed (struct virtqueue *_vq) { -return 0 ; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } + +END_USE (vq); +return vq->last_used_idx | ((u16 )vq->used_wrap_counter <<15 ); } -static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned last_used_idx) +static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned off_wrap) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +bool wrap_counter; +u16 used_idx; + + wrap_counter = off_wrap >>15 ; + used_idx = off_wrap & ~(1 <<15 ); + +return is_used_desc_packed (vq, used_idx, wrap_counter); }static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers); + } + +if (more_used_packed (vq)) { +END_USE (vq); +return false; + } + +END_USE (vq); +return true; }static void *virtqueue_detach_unused_buf_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); +unsigned int i; +void *buf; + +START_USE (vq); + +for (i =0 ; i < vq->vring_packed.num; i++) { +if (!vq->desc_state_packed[i].data) +continue ; +/* detach_buf clears data, so grab it now. */ + buf = vq->desc_state_packed[i].data; +detach_buf_packed (vq, i,NULL ); +END_USE (vq); +return buf; + } +/* That should have freed everything. */ +BUG_ON (vq->vq.num_free != vq->vring_packed.num); + +END_USE (vq);return NULL ; }
@@ -1083,6 +1559,9 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers);return vq->packed ?virtqueue_poll_packed (_vq, last_used_idx) :virtqueue_poll_split (_vq, last_used_idx); }
Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 13 lines · Tiwei Bie
This commit introduces the support (without
EVENT_IDX) for packed ring.Signed-off-by Tiwei Bie <[email protected]>--- drivers/virtio/virtio_ring.c | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 8 deletions(-)drivers/virtio/virtio_ring.c +0
-0
drivers/virtio/virtio_ring.c line 59
Quoted 5 lines
drivers/virtio/virtio_ring.c +2
-0@@ -55,12 +55,21 @@#define END_USE (vq)#endif +#define _VRING_DESC_F_AVAIL (b) ((__u16)(b) << 7) +#define _VRING_DESC_F_USED (b) ((__u16)(b) << 15)
Underscore followed by an upper case letter isn't a good idea for a symbol. And it's not nice that this does not use the VRING_DESC_F_USED from the header.
How about ((b) ? VRING_DESC_F_USED : 0) instead?
Is produced code worse then?
drivers/virtio/virtio_ring.c line 68
Quoted 9 lines
drivers/virtio/virtio_ring.c +3
-0+struct vring_desc_state {void *data;/* Data for callback. */ struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };struct vring_desc_state_packed { +void *data;/* Data for callback. */ +struct vring_packed_desc *indir_desc;/* Indirect descriptor, if any. */
Include vring_desc_state for these?
drivers/virtio/virtio_ring.c line 72
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+int num;/* Descriptor list length. */ +dma_addr_t addr;/* Buffer DMA addr. */ +u32 len;/* Buffer length. */ +u16 flags;/* Descriptor flags. */
This seems only to be used for indirect. Check indirect field above instead and drop this.
drivers/virtio/virtio_ring.c line 73
Quoted 1 line
drivers/virtio/virtio_ring.c +0
-0int next;/* The next desc state. */
Packing things into 16 bit integers will help reduce cache pressure here.
Also, this is only used for unmap, so when dma API is not used, maintaining addr and len list management is unnecessary. How about we maintain addr/len in a separate array, avoiding accesses on unmap in that case?
Also, lots of architectures have a nop unmap in the DMA API. See a proposed patch at the end for optimizing that case.
drivers/virtio/virtio_ring.c static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx) · line 673
Quoted 9 lines
drivers/virtio/virtio_ring.c +0
-1};@@ -660,7 +669,6 @@ static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); -virtio_mb (vq->weak_barriers);return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); }
why is this changing the split queue implementation?
Quoted 27 lines
drivers/virtio/virtio_ring.c +22
-0@@ -757,6 +765,72 @@ static inline unsigned vring_size_packed(unsigned int num, unsigned long align) & ~(align - 1)) + sizeof(struct vring_packed_desc_event) * 2; } +static void vring_unmap_state_packed (const struct vring_virtqueue *vq, +struct vring_desc_state_packed *state) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags = state->flags; + +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +}
drivers/virtio/virtio_ring.c static inline unsigned vring_size_packed(unsigned int num, unsigned long align) · line 799
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+ +static void vring_unmap_desc_packed (const struct vring_virtqueue *vq, +struct vring_packed_desc *desc) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags =virtio16_to_cpu (vq->vq.vdev, desc->flags);
I see no reason to use virtioXX wrappers for the packed ring. That's there to support legacy devices. Just use leXX.
Quoted 16 lines
drivers/virtio/virtio_ring.c +16
-0+ +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +} +
drivers/virtio/virtio_ring.c static inline unsigned vring_size_packed(unsigned int num, unsigned long align) · line 825
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+static struct vring_packed_desc *alloc_indirect_packed (struct virtqueue *_vq, +unsigned int total_sg, +gfp_t gfp) +{ +struct vring_packed_desc *desc; + +/* +* We require lowmem mappings for the descriptors because +* otherwise virt_to_phys will give us bogus addresses in the +* virtqueue.
Where is virt_to_phys used? I don't see it in this patch.
Quoted 35 lines
drivers/virtio/virtio_ring.c +28
-0+*/ + gfp &= ~__GFP_HIGHMEM; + + desc =kmalloc (total_sg *sizeof (struct vring_packed_desc ), gfp); + +return desc; +} + static inline int virtqueue_add_packed(struct virtqueue *_vq, struct scatterlist *sgs[], unsigned int total_sg,@@ -766,47 +840,449 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, void *ctx, gfp_t gfp) { +struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_packed_desc *desc; +struct scatterlist *sg; +unsigned int i, n, descs_used,uninitialized_var (prev ), err_idx; +__virtio16 uninitialized_var (head_flags ), flags; +u16 head, avail_wrap_counter, id, curr; +bool indirect; + +START_USE (vq); + +BUG_ON (data ==NULL ); +BUG_ON (ctx && vq->indirect); + +if (unlikely (vq->broken)) { +END_USE (vq); +return -EIO ; + } + +#ifdef DEBUG + {
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 872
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+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
Add incline helpers for this debug stuff rather than duplicate it from split ring?
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 884
Quoted 12 lines
drivers/virtio/virtio_ring.c +12
-0+ +BUG_ON (total_sg ==0 ); + + head = vq->next_avail_idx; + avail_wrap_counter = vq->avail_wrap_counter; + +if (virtqueue_use_indirect (_vq, total_sg)) + desc =alloc_indirect_packed (_vq, total_sg, gfp); +else { + desc =NULL ; +WARN_ON_ONCE (total_sg > vq->vring_packed.num && !vq->indirect); + }
Apparently this attempts to treat indirect descriptors same as direct ones. This is how it is in the split ring, but not in the packed one. I think you want two separate functions, for direct and indirect.
Quoted 33 lines
drivers/virtio/virtio_ring.c +33
-0+ +if (desc) { +/* Use a single buffer which doesn't continue */ + indirect = true; +/* Set up rest to use this indirect table. */ + i =0 ; + descs_used =1 ; + }else { + indirect = false; + desc = vq->vring_packed.desc; + i = head; + descs_used = total_sg; + } + +if (vq->vq.num_free < descs_used) { +pr_debug ("Can't add buf len %i - avail = %i\n" , + descs_used, vq->vq.num_free); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out_sgs) + vq->notify (&vq->vq); +if (indirect) +kfree (desc); +END_USE (vq); +return -ENOSPC ; + } + + id = vq->free_head; +BUG_ON (id == vq->vring_packed.num); + + curr = id; +for (n =0 ; n < out_sgs + in_sgs; n++) {
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 927
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+for (sg = sgs[n]; sg; sg =sg_next (sg)) { +dma_addr_t addr =vring_map_one_sg (vq, sg, n < out_sgs ? +DMA_TO_DEVICE :DMA_FROM_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_NEXT | + (n < out_sgs ?0 :VRING_DESC_F_WRITE ) | +_VRING_DESC_F_AVAIL (vq->avail_wrap_counter) | +_VRING_DESC_F_USED (!vq->avail_wrap_counter));
Spec says:
The VIRTQ_DESC_F_WRITE flags bit is the only valid flag for descriptors in the indirect table.
All this logic isn't needed for indirect.
Also, why re-calculate avail/used flags every time? They only change when you wrap around.
Quoted 19 lines
drivers/virtio/virtio_ring.c +19
-0+if (!indirect && i == head) + head_flags = flags; +else + desc[i].flags = flags; + + desc[i].addr =cpu_to_virtio64 (_vq->vdev, addr); + desc[i].len =cpu_to_virtio32 (_vq->vdev, sg->length); + i++; +if (!indirect) { +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[curr].addr = addr; + vq->desc_state_packed[curr].len = + sg->length; + vq->desc_state_packed[curr].flags = +virtio16_to_cpu (_vq->vdev, + flags); + } + curr = vq->desc_state_packed[curr].next; +
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 956
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+if (i >= vq->vring_packed.num) { + i =0 ; + vq->avail_wrap_counter ^=1 ; + } + } + } + } + + prev = (i >0 ? i : vq->vring_packed.num) -1 ; + desc[prev].id =cpu_to_virtio16 (_vq->vdev, id);
Is it easier to write this out in all descriptors, to avoid the need to calculate prev?
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 963
Quoted 7 lines
drivers/virtio/virtio_ring.c +7
-0+ +/* Last one doesn't continue. */ +if (total_sg ==1 ) + head_flags &=cpu_to_virtio16 (_vq->vdev, ~VRING_DESC_F_NEXT ); +else + desc[prev].flags &=cpu_to_virtio16 (_vq->vdev, + ~VRING_DESC_F_NEXT );
Wouldn't it be easier to avoid setting VRING_DESC_F_NEXT in the first place?
if (n != in_sgs - 1 && n != out_sgs - 1)
must be better than writing descriptor an extra time.
Quoted 91 lines
drivers/virtio/virtio_ring.c +85
-1+ +if (indirect) { +/* Now that the indirect table is filled in, map it. */ +dma_addr_t addr =vring_map_single ( + vq, desc, total_sg *sizeof (struct vring_packed_desc ), +DMA_TO_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + head_flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_INDIRECT | +_VRING_DESC_F_AVAIL (avail_wrap_counter) | +_VRING_DESC_F_USED (!avail_wrap_counter)); + vq->vring_packed.desc[head].addr =cpu_to_virtio64 (_vq->vdev, + addr); + vq->vring_packed.desc[head].len =cpu_to_virtio32 (_vq->vdev, + total_sg *sizeof (struct vring_packed_desc )); + vq->vring_packed.desc[head].id =cpu_to_virtio16 (_vq->vdev, id); + +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[id].addr = addr; + vq->desc_state_packed[id].len = total_sg * +sizeof (struct vring_packed_desc ); + vq->desc_state_packed[id].flags = +virtio16_to_cpu (_vq->vdev, head_flags); + } + } + +/* We're using some buffers from the free list. */ + vq->vq.num_free -= descs_used; + +/* Update free pointer */ +if (indirect) { + n = head +1 ; +if (n >= vq->vring_packed.num) { + n =0 ; + vq->avail_wrap_counter ^=1 ; + } + vq->next_avail_idx = n; + vq->free_head = vq->desc_state_packed[id].next; + }else { + vq->next_avail_idx = i; + vq->free_head = curr; + } + +/* Store token and indirect buffer state. */ + vq->desc_state_packed[id].num = descs_used; + vq->desc_state_packed[id].data = data; +if (indirect) + vq->desc_state_packed[id].indir_desc = desc; +else + vq->desc_state_packed[id].indir_desc = ctx; + +/* A driver MUST NOT make the first descriptor in the list +* available before all subsequent descriptors comprising +* the list are made available. */ +virtio_wmb (vq->weak_barriers); + vq->vring_packed.desc[head].flags = head_flags; + vq->num_added += descs_used; + +pr_debug ("Added buffer head %i to %p\n" , head, vq); +END_USE (vq); + +return 0 ; + +unmap_release: + err_idx = i; + i = head; + +for (n =0 ; n < total_sg; n++) { +if (i == err_idx) +break ; +vring_unmap_desc_packed (vq, &desc[i]); + i++; +if (!indirect && i >= vq->vring_packed.num) + i =0 ; + } + + vq->avail_wrap_counter = avail_wrap_counter; + +if (indirect) +kfree (desc); + +END_USE (vq);return -EIO ; }static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 flags;
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 1063
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+bool needs_kick; +u32 snapshot; + +START_USE (vq); +/* We need to expose the new flags value before checking notification +* suppressions. */ +virtio_mb (vq->weak_barriers); + + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ;
What are all these hard-coded things? Also either we do READ_ONCE everywhere or nowhere. Why is this place special? Any why read 32 bit if you only want the 16?
And doesn't sparse complain about cast to __virtio16?
Quoted 59 lines
drivers/virtio/virtio_ring.c +59
-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 + + needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +END_USE (vq); +return needs_kick; +} + +static void detach_buf_packed (struct vring_virtqueue *vq, +unsigned int id,void **ctx) +{ +struct vring_desc_state_packed *state =NULL ; +struct vring_packed_desc *desc; +unsigned int curr, i; + +/* Clear data ptr. */ + vq->desc_state_packed[id].data =NULL ; + + curr = id; +for (i =0 ; i < vq->desc_state_packed[id].num; i++) { + state = &vq->desc_state_packed[curr]; +vring_unmap_state_packed (vq, state); + curr = state->next; + } + +BUG_ON (state ==NULL ); + vq->vq.num_free += vq->desc_state_packed[id].num; + state->next = vq->free_head; + vq->free_head = id; + +if (vq->indirect) { +u32 len; + +/* Free the indirect table, if any, now that it's unmapped. */ + desc = vq->desc_state_packed[id].indir_desc; +if (!desc) +return ; + +if (vring_use_dma_api (vq->vq.vdev)) { + len = vq->desc_state_packed[id].len; +for (i =0 ; i < len /sizeof (struct vring_packed_desc ); + i++) +vring_unmap_desc_packed (vq, &desc[i]); + } +kfree (desc); + vq->desc_state_packed[id].indir_desc =NULL ; + }else if (ctx) { + *ctx = vq->desc_state_packed[id].indir_desc; + } +} + +static inline bool is_used_desc_packed (const struct vring_virtqueue *vq, +u16 idx,bool used_wrap_counter)
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 1132
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+{ +u16 flags; +bool avail, used; + + flags =virtio16_to_cpu (vq->vq.vdev, + vq->vring_packed.desc[idx].flags); + avail = !!(flags &VRING_DESC_F_AVAIL ); + used = !!(flags &VRING_DESC_F_USED ); + +return avail == used && used == used_wrap_counter;
I think that you don't need to look at avail flag to detect a used descriptor. The reason device writes it is to avoid confusing *device* next time descriptor wraps.
Quoted 98 lines
drivers/virtio/virtio_ring.c +74
-4}static inline bool more_used_packed (const struct vring_virtqueue *vq) { -return false; +return is_used_desc_packed (vq, vq->last_used_idx, + vq->used_wrap_counter); }static void *virtqueue_get_buf_ctx_packed (struct virtqueue *_vq,unsigned int *len,void **ctx) { -return NULL ; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 last_used, id; +void *ret; + +START_USE (vq); + +if (unlikely (vq->broken)) { +END_USE (vq); +return NULL ; + } + +if (!more_used_packed (vq)) { +pr_debug ("No more buffers in queue\n" ); +END_USE (vq); +return NULL ; + } + +/* Only get used elements after they have been exposed by host. */ +virtio_rmb (vq->weak_barriers); + + last_used = vq->last_used_idx; + id =virtio16_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].id); + *len =virtio32_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].len); + +if (unlikely (id >= vq->vring_packed.num)) { +BAD_RING (vq,"id %u out of range\n" , id); +return NULL ; + } +if (unlikely (!vq->desc_state_packed[id].data)) { +BAD_RING (vq,"id %u is not a head!\n" , id); +return NULL ; + } + + vq->last_used_idx += vq->desc_state_packed[id].num; +if (vq->last_used_idx >= vq->vring_packed.num) { + vq->last_used_idx -= vq->vring_packed.num; + vq->used_wrap_counter ^=1 ; + } + +/* detach_buf_packed clears data, so grab it now. */ + ret = vq->desc_state_packed[id].data; +detach_buf_packed (vq, id, ctx); + +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif + +END_USE (vq); +return ret; }static void virtqueue_disable_cb_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); + +if (vq->event_flags_shadow !=VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_DISABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } }static unsigned virtqueue_enable_cb_prepare_packed (struct virtqueue *_vq) { -return 0 ; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } + +END_USE (vq); +return vq->last_used_idx | ((u16 )vq->used_wrap_counter <<15 ); } -static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned last_used_idx) +static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned off_wrap)
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 1235
Quoted 10 lines
drivers/virtio/virtio_ring.c +8
-1{ -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +bool wrap_counter; +u16 used_idx; + + wrap_counter = off_wrap >>15 ; + used_idx = off_wrap & ~(1 <<15 ); + +return is_used_desc_packed (vq, used_idx, wrap_counter);
These >> 15 << 15 all over the place duplicate info. Pls put 15 in the header.
Also can you maintain the counters properly shifted? Then just use them.
Quoted 55 lines
drivers/virtio/virtio_ring.c +42
-1}static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers); + } + +if (more_used_packed (vq)) { +END_USE (vq); +return false; + } + +END_USE (vq); +return true; }static void *virtqueue_detach_unused_buf_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); +unsigned int i; +void *buf; + +START_USE (vq); + +for (i =0 ; i < vq->vring_packed.num; i++) { +if (!vq->desc_state_packed[i].data) +continue ; +/* detach_buf clears data, so grab it now. */ + buf = vq->desc_state_packed[i].data; +detach_buf_packed (vq, i,NULL ); +END_USE (vq); +return buf; + } +/* That should have freed everything. */ +BUG_ON (vq->vq.num_free != vq->vring_packed.num); + +END_USE (vq);return NULL ; }
drivers/virtio/virtio_ring.c bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) · line 1567
Quoted 9 lines
drivers/virtio/virtio_ring.c +3
-0@@ -1083,6 +1559,9 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers);return vq->packed ?virtqueue_poll_packed (_vq, last_used_idx) :virtqueue_poll_split (_vq, last_used_idx); }
Possible optimization for when dma API is in use:
--->
dma: detecting nop unmap
drivers need to maintain the dma address for unmap purposes, but these cycles are wasted when unmap callback is not defined. Add an API for drivers to check that and avoid unmap completely. Debug builds still have unmap.
----
include/linux/dma-debug.h +10 -0
@@ -42,6 +42,11 @@ extern void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr);extern void debug_dma_unmap_page (struct device *dev,dma_addr_t addr,size_t size,int direction,bool map_single); +static inline bool has_debug_dma_unmap (struct device *dev) +{ +return true; +} +extern void debug_dma_map_sg (struct device *dev,struct scatterlist *sg,int nents,int mapped_ents,int direction);
@@ -121,6 +126,11 @@ static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, { } +static inline bool has_debug_dma_unmap (struct device *dev) +{ +return false; +} +static inline void debug_dma_map_sg (struct device *dev,struct scatterlist *sg,int nents,int mapped_ents,int direction) {
include/linux/dma-mapping.h +8 -0
@@ -241,6 +241,14 @@ static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,return addr; } +static inline bool has_dma_unmap (struct device *dev) +{ +const struct dma_map_ops *ops =get_dma_ops (dev); + +return ops->unmap_page || ops->unmap_sg || ops->unmap_resource || +has_dma_unmap (dev); +} + static inline void dma_unmap_single_attrs(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir,
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 17 lines · Michael S. Tsirkin
Quoted 16 lines · Tiwei Bie
This commit introduces the support (without
EVENT_IDX) for packed ring.Signed-off-by Tiwei Bie <[email protected]>--- drivers/virtio/virtio_ring.c | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 8 deletions(-)drivers/virtio/virtio_ring.c +0
-0@@ -55,12 +55,21 @@#define END_USE (vq)#endif
Quoted 8 lines
drivers/virtio/virtio_ring.c line 59
Quoted 2 lines
drivers/virtio/virtio_ring.c +2
-0+#define _VRING_DESC_F_AVAIL (b) ((__u16)(b) << 7) +#define _VRING_DESC_F_USED (b) ((__u16)(b) << 15)Underscore followed by an upper case letter isn't a good idea for a symbol. And it's not nice that this does not use the
VRING_DESC_F_USEDfrom the header.
How about ((b) ?VRING_DESC_F_USED: 0) instead?
Is produced code worse then?
Yes, I think the produced code is worse when we use conditional expression. Below is a simple test:
#define foo1(b) ((b) << 10)
#define foo2(b) ((b) ? (1 << 10) : 0)
unsigned short bar(unsigned short b)
{
return foo1(b);
}unsigned short baz(unsigned short b)
{
return foo2(b);
}With gcc -O3 -S, I got below assembly code:
.file "tmp.c" .text .p2align 4,,15 .globl bar .type bar, @function bar: .LFB0: .cfi_startproc movl %edi, %eax sall $10, %eax ret .cfi_endproc .LFE0: .size bar, .-bar .p2align 4,,15 .globl baz .type baz, @function baz: .LFB1: .cfi_startproc xorl %eax, %eax testw %di, %di setne %al sall $10, %eax ret .cfi_endproc .LFE1: .size baz, .-baz .ident "GCC: (Debian 8.2.0-4) 8.2.0" .section .note.GNU-stack,"",@progbits
That is to say, for ((b) << 10), it will shift the register directly. But for ((b) ? (1 << 10) : 0), in above code, it will zero eax first, and set al to 1 depend on whether di is 0, and shift eax.
Quoted 7 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +1
-0+struct vring_desc_state {void *data;/* Data for callback. */ struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };
Quoted 5 lines
drivers/virtio/virtio_ring.c line 68
Quoted 3 lines
drivers/virtio/virtio_ring.c +2
-0struct vring_desc_state_packed { +void *data;/* Data for callback. */ +struct vring_packed_desc *indir_desc;/* Indirect descriptor, if any. */ Include
vring_desc_statefor these?
Sure.
Quoted 8 lines
drivers/virtio/virtio_ring.c line 72
Quoted 5 lines
drivers/virtio/virtio_ring.c +4
-0+int num;/* Descriptor list length. */ +dma_addr_t addr;/* Buffer DMA addr. */ +u32 len;/* Buffer length. */ +u16 flags;/* Descriptor flags. */ This seems only to be used for indirect. Check indirect field above instead and drop this.
The flags is also needed to know the direction, i.e. DMA_FROM_DEVICE or DMA_TO_DEVICE when do DMA unmap.
Quoted 9 lines
drivers/virtio/virtio_ring.c line 73
Quoted 2 lines
drivers/virtio/virtio_ring.c +0
-0int next;/* The next desc state. */ Packing things into 16 bit integers will help reduce cache pressure here.
Also, this is only used for unmap, so when dma API is not used, maintaining addr and len list management is unnecessary. How about we maintain addr/len in a separate array, avoiding accesses on unmap in that case?
Sure. I'll give it a try.
Quoted 3 lines
Also, lots of architectures have a nop unmap in the DMA API. See a proposed patch at the end for optimizing that case.
Got it. Thanks.
Quoted 7 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +0
-0};@@ -660,7 +669,6 @@ static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq);
Quoted 5 lines
drivers/virtio/virtio_ring.c static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx) · line 673
Quoted 3 lines
drivers/virtio/virtio_ring.c +0
-1-virtio_mb (vq->weak_barriers);return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); }why is this changing the split queue implementation?
Because above barrier is also needed by virtqueue_poll_packed(), so I moved it to virtqueue_poll() and also added some comments for it.
Quoted 34 lines
Quoted 34 lines
drivers/virtio/virtio_ring.c +27
-0@@ -757,6 +765,72 @@ static inline unsigned vring_size_packed(unsigned int num, unsigned long align) & ~(align - 1)) + sizeof(struct vring_packed_desc_event) * 2; } +static void vring_unmap_state_packed (const struct vring_virtqueue *vq, +struct vring_desc_state_packed *state) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags = state->flags; + +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +} + +static void vring_unmap_desc_packed (const struct vring_virtqueue *vq, +struct vring_packed_desc *desc) +{ +u16 flags;
Quoted 8 lines
drivers/virtio/virtio_ring.c static inline unsigned vring_size_packed(unsigned int num, unsigned long align) · line 799
Quoted 5 lines
drivers/virtio/virtio_ring.c +5
-0+ +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags =virtio16_to_cpu (vq->vq.vdev, desc->flags);I see no reason to use virtioXX wrappers for the packed ring. That's there to support legacy devices. Just use leXX.
Okay.
Quoted 21 lines
Quoted 21 lines
drivers/virtio/virtio_ring.c +20
-0+ +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +} + +static struct vring_packed_desc *alloc_indirect_packed (struct virtqueue *_vq, +unsigned int total_sg, +gfp_t gfp) +{
Quoted 8 lines
drivers/virtio/virtio_ring.c static inline unsigned vring_size_packed(unsigned int num, unsigned long align) · line 825
Quoted 6 lines
drivers/virtio/virtio_ring.c +6
-0+struct vring_packed_desc *desc; + +/* +* We require lowmem mappings for the descriptors because +* otherwise virt_to_phys will give us bogus addresses in the +* virtqueue. Where is
virt_to_physused? I don't see it in this patch.
In vring_map_single(), virt_to_phys() will be used to translate the address to phys if dma api isn't used:
https://github.com/torvalds/linux/blob/a49a9dcce802/drivers/virtio/virtio_ring.c#L197
Quoted 41 lines
Quoted 41 lines
drivers/virtio/virtio_ring.c +33
-0+*/ + gfp &= ~__GFP_HIGHMEM; + + desc =kmalloc (total_sg *sizeof (struct vring_packed_desc ), gfp); + +return desc; +} + static inline int virtqueue_add_packed(struct virtqueue *_vq, struct scatterlist *sgs[], unsigned int total_sg,@@ -766,47 +840,449 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, void *ctx, gfp_t gfp) { +struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_packed_desc *desc; +struct scatterlist *sg; +unsigned int i, n, descs_used,uninitialized_var (prev ), err_idx; +__virtio16 uninitialized_var (head_flags ), flags; +u16 head, avail_wrap_counter, id, curr; +bool indirect; + +START_USE (vq); + +BUG_ON (data ==NULL ); +BUG_ON (ctx && vq->indirect); + +if (unlikely (vq->broken)) { +END_USE (vq); +return -EIO ; + } + +#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))
Quoted 8 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 872
Quoted 5 lines
drivers/virtio/virtio_ring.c +5
-0+ > 100); + vq->last_add_time = now; + vq->last_add_time_valid = true; + } +#endif Add incline helpers for this debug stuff rather than duplicate it from split ring?
Sure. I'd like to do that.
Quoted 12 lines
Quoted 12 lines
drivers/virtio/virtio_ring.c +10
-0+ +BUG_ON (total_sg ==0 ); + + head = vq->next_avail_idx; + avail_wrap_counter = vq->avail_wrap_counter; + +if (virtqueue_use_indirect (_vq, total_sg)) + desc =alloc_indirect_packed (_vq, total_sg, gfp); +else { + desc =NULL ;
Quoted 8 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 884
Quoted 2 lines
drivers/virtio/virtio_ring.c +2
-0+WARN_ON_ONCE (total_sg > vq->vring_packed.num && !vq->indirect); + }Apparently this attempts to treat indirect descriptors same as direct ones. This is how it is in the split ring, but not in the packed one. I think you want two separate functions, for direct and indirect.
Okay, I'll do that.
Quoted 49 lines
Quoted 34 lines
drivers/virtio/virtio_ring.c +33
-0+ +if (desc) { +/* Use a single buffer which doesn't continue */ + indirect = true; +/* Set up rest to use this indirect table. */ + i =0 ; + descs_used =1 ; + }else { + indirect = false; + desc = vq->vring_packed.desc; + i = head; + descs_used = total_sg; + } + +if (vq->vq.num_free < descs_used) { +pr_debug ("Can't add buf len %i - avail = %i\n" , + descs_used, vq->vq.num_free); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out_sgs) + vq->notify (&vq->vq); +if (indirect) +kfree (desc); +END_USE (vq); +return -ENOSPC ; + } + + id = vq->free_head; +BUG_ON (id == vq->vring_packed.num); + + curr = id; +for (n =0 ; n < out_sgs + in_sgs; n++) {drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 927
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+for (sg = sgs[n]; sg; sg =sg_next (sg)) { +dma_addr_t addr =vring_map_one_sg (vq, sg, n < out_sgs ? +DMA_TO_DEVICE :DMA_FROM_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_NEXT | + (n < out_sgs ?0 :VRING_DESC_F_WRITE ) | +_VRING_DESC_F_AVAIL (vq->avail_wrap_counter) | +_VRING_DESC_F_USED (!vq->avail_wrap_counter));Spec says:
TheVIRTQ_DESC_F_WRITEflags bit is the only valid flag for descriptors in the indirect table.
Quoted 4 lines
All this logic isn't needed for indirect.
Also, why re-calculate avail/used flags every time? They only change when you wrap around.
Will do that. Thanks.
Quoted 26 lines
Quoted 26 lines
drivers/virtio/virtio_ring.c +24
-0+if (!indirect && i == head) + head_flags = flags; +else + desc[i].flags = flags; + + desc[i].addr =cpu_to_virtio64 (_vq->vdev, addr); + desc[i].len =cpu_to_virtio32 (_vq->vdev, sg->length); + i++; +if (!indirect) { +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[curr].addr = addr; + vq->desc_state_packed[curr].len = + sg->length; + vq->desc_state_packed[curr].flags = +virtio16_to_cpu (_vq->vdev, + flags); + } + curr = vq->desc_state_packed[curr].next; + +if (i >= vq->vring_packed.num) { + i =0 ; + vq->avail_wrap_counter ^=1 ; + } + }
Quoted 8 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 956
Quoted 5 lines
drivers/virtio/virtio_ring.c +5
-0+ } + } + + prev = (i >0 ? i : vq->vring_packed.num) -1 ; + desc[prev].id =cpu_to_virtio16 (_vq->vdev, id);Is it easier to write this out in all descriptors, to avoid the need to calculate prev?
Yeah, I'll do that.
Quoted 11 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 963
Quoted 8 lines
drivers/virtio/virtio_ring.c +7
-0+ +/* Last one doesn't continue. */ +if (total_sg ==1 ) + head_flags &=cpu_to_virtio16 (_vq->vdev, ~VRING_DESC_F_NEXT ); +else + desc[prev].flags &=cpu_to_virtio16 (_vq->vdev, + ~VRING_DESC_F_NEXT );Wouldn't it be easier to avoid setting
VRING_DESC_F_NEXTin the first place?
Quoted 1 line
if (n != in_sgs - 1 && n != out_sgs - 1)
Will this affect the branch prediction?
Quoted 2 lines
must be better than writing descriptor an extra time.
Not quite sure about this. I think this descriptor has just been written, it should still be in the cache.
Quoted 98 lines
Quoted 98 lines
drivers/virtio/virtio_ring.c +91
-1+ +if (indirect) { +/* Now that the indirect table is filled in, map it. */ +dma_addr_t addr =vring_map_single ( + vq, desc, total_sg *sizeof (struct vring_packed_desc ), +DMA_TO_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + head_flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_INDIRECT | +_VRING_DESC_F_AVAIL (avail_wrap_counter) | +_VRING_DESC_F_USED (!avail_wrap_counter)); + vq->vring_packed.desc[head].addr =cpu_to_virtio64 (_vq->vdev, + addr); + vq->vring_packed.desc[head].len =cpu_to_virtio32 (_vq->vdev, + total_sg *sizeof (struct vring_packed_desc )); + vq->vring_packed.desc[head].id =cpu_to_virtio16 (_vq->vdev, id); + +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[id].addr = addr; + vq->desc_state_packed[id].len = total_sg * +sizeof (struct vring_packed_desc ); + vq->desc_state_packed[id].flags = +virtio16_to_cpu (_vq->vdev, head_flags); + } + } + +/* We're using some buffers from the free list. */ + vq->vq.num_free -= descs_used; + +/* Update free pointer */ +if (indirect) { + n = head +1 ; +if (n >= vq->vring_packed.num) { + n =0 ; + vq->avail_wrap_counter ^=1 ; + } + vq->next_avail_idx = n; + vq->free_head = vq->desc_state_packed[id].next; + }else { + vq->next_avail_idx = i; + vq->free_head = curr; + } + +/* Store token and indirect buffer state. */ + vq->desc_state_packed[id].num = descs_used; + vq->desc_state_packed[id].data = data; +if (indirect) + vq->desc_state_packed[id].indir_desc = desc; +else + vq->desc_state_packed[id].indir_desc = ctx; + +/* A driver MUST NOT make the first descriptor in the list +* available before all subsequent descriptors comprising +* the list are made available. */ +virtio_wmb (vq->weak_barriers); + vq->vring_packed.desc[head].flags = head_flags; + vq->num_added += descs_used; + +pr_debug ("Added buffer head %i to %p\n" , head, vq); +END_USE (vq); + +return 0 ; + +unmap_release: + err_idx = i; + i = head; + +for (n =0 ; n < total_sg; n++) { +if (i == err_idx) +break ; +vring_unmap_desc_packed (vq, &desc[i]); + i++; +if (!indirect && i >= vq->vring_packed.num) + i =0 ; + } + + vq->avail_wrap_counter = avail_wrap_counter; + +if (indirect) +kfree (desc); + +END_USE (vq);return -EIO ; }static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 flags; +bool needs_kick; +u32 snapshot; + +START_USE (vq); +/* We need to expose the new flags value before checking notification +* suppressions. */
Quoted 8 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 1063
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+virtio_mb (vq->weak_barriers); + + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ;What are all these hard-coded things? Also either we do
READ_ONCEeverywhere or nowhere. Why is this place special? Any why read 32 bit if you only want the 16?
Yeah, READ_ONCE() and reading 32bits are not really needed in this patch. But it's needed in the next patch. I thought it's not wrong to do this, so I introduced them from the first place.
Just to double check: is the below code (apart from the hard-coded value and virtio16) from the next patch OK?
"""
snapshot = READ_ONCE(*(u32 *)vq->vring_packed.device);
+ off_wrap = virtio16_to_cpu(_vq->vdev, (__virtio16)(snapshot & 0xffff));
flags = virtio16_to_cpu(_vq->vdev, (__virtio16)(snapshot >> 16)) & 0x3;"""
Quoted 2 lines
And doesn't sparse complain about cast to
__virtio16?
I'll give it a try in the next version.
Quoted 66 lines
Quoted 66 lines
drivers/virtio/virtio_ring.c +65
-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 + + needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +END_USE (vq); +return needs_kick; +} + +static void detach_buf_packed (struct vring_virtqueue *vq, +unsigned int id,void **ctx) +{ +struct vring_desc_state_packed *state =NULL ; +struct vring_packed_desc *desc; +unsigned int curr, i; + +/* Clear data ptr. */ + vq->desc_state_packed[id].data =NULL ; + + curr = id; +for (i =0 ; i < vq->desc_state_packed[id].num; i++) { + state = &vq->desc_state_packed[curr]; +vring_unmap_state_packed (vq, state); + curr = state->next; + } + +BUG_ON (state ==NULL ); + vq->vq.num_free += vq->desc_state_packed[id].num; + state->next = vq->free_head; + vq->free_head = id; + +if (vq->indirect) { +u32 len; + +/* Free the indirect table, if any, now that it's unmapped. */ + desc = vq->desc_state_packed[id].indir_desc; +if (!desc) +return ; + +if (vring_use_dma_api (vq->vq.vdev)) { + len = vq->desc_state_packed[id].len; +for (i =0 ; i < len /sizeof (struct vring_packed_desc ); + i++) +vring_unmap_desc_packed (vq, &desc[i]); + } +kfree (desc); + vq->desc_state_packed[id].indir_desc =NULL ; + }else if (ctx) { + *ctx = vq->desc_state_packed[id].indir_desc; + } +} + +static inline bool is_used_desc_packed (const struct vring_virtqueue *vq, +u16 idx,bool used_wrap_counter) +{ +u16 flags; +bool avail, used; + + flags =virtio16_to_cpu (vq->vq.vdev, + vq->vring_packed.desc[idx].flags);
Quoted 8 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 1132
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+ avail = !!(flags &VRING_DESC_F_AVAIL ); + used = !!(flags &VRING_DESC_F_USED ); + +return avail == used && used == used_wrap_counter;I think that you don't need to look at avail flag to detect a used descriptor. The reason device writes it is to avoid confusing *device* next time descriptor wraps.
Okay, I'll just check the used flag.
Quoted 97 lines
Quoted 97 lines
drivers/virtio/virtio_ring.c +73
-3}static inline bool more_used_packed (const struct vring_virtqueue *vq) { -return false; +return is_used_desc_packed (vq, vq->last_used_idx, + vq->used_wrap_counter); }static void *virtqueue_get_buf_ctx_packed (struct virtqueue *_vq,unsigned int *len,void **ctx) { -return NULL ; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 last_used, id; +void *ret; + +START_USE (vq); + +if (unlikely (vq->broken)) { +END_USE (vq); +return NULL ; + } + +if (!more_used_packed (vq)) { +pr_debug ("No more buffers in queue\n" ); +END_USE (vq); +return NULL ; + } + +/* Only get used elements after they have been exposed by host. */ +virtio_rmb (vq->weak_barriers); + + last_used = vq->last_used_idx; + id =virtio16_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].id); + *len =virtio32_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].len); + +if (unlikely (id >= vq->vring_packed.num)) { +BAD_RING (vq,"id %u out of range\n" , id); +return NULL ; + } +if (unlikely (!vq->desc_state_packed[id].data)) { +BAD_RING (vq,"id %u is not a head!\n" , id); +return NULL ; + } + + vq->last_used_idx += vq->desc_state_packed[id].num; +if (vq->last_used_idx >= vq->vring_packed.num) { + vq->last_used_idx -= vq->vring_packed.num; + vq->used_wrap_counter ^=1 ; + } + +/* detach_buf_packed clears data, so grab it now. */ + ret = vq->desc_state_packed[id].data; +detach_buf_packed (vq, id, ctx); + +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif + +END_USE (vq); +return ret; }static void virtqueue_disable_cb_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); + +if (vq->event_flags_shadow !=VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_DISABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } }static unsigned virtqueue_enable_cb_prepare_packed (struct virtqueue *_vq) { -return 0 ; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } + +END_USE (vq); +return vq->last_used_idx | ((u16 )vq->used_wrap_counter <<15 ); }
Quoted 15 lines
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 1235
Quoted 12 lines
drivers/virtio/virtio_ring.c +9
-2-static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned last_used_idx) +static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned off_wrap) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +bool wrap_counter; +u16 used_idx; + + wrap_counter = off_wrap >>15 ; + used_idx = off_wrap & ~(1 <<15 ); + +return is_used_desc_packed (vq, used_idx, wrap_counter);These >> 15 << 15 all over the place duplicate info. Pls put 15 in the header.
Sure.
Quoted 3 lines
Also can you maintain the counters properly shifted? Then just use them.
Then, we may need to maintain both of the shifted wrapper counters and un-shifted wrapper counters at the same time.
Quoted 60 lines
Quoted 60 lines
drivers/virtio/virtio_ring.c +42
-1}static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers); + } + +if (more_used_packed (vq)) { +END_USE (vq); +return false; + } + +END_USE (vq); +return true; }static void *virtqueue_detach_unused_buf_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); +unsigned int i; +void *buf; + +START_USE (vq); + +for (i =0 ; i < vq->vring_packed.num; i++) { +if (!vq->desc_state_packed[i].data) +continue ; +/* detach_buf clears data, so grab it now. */ + buf = vq->desc_state_packed[i].data; +detach_buf_packed (vq, i,NULL ); +END_USE (vq); +return buf; + } +/* That should have freed everything. */ +BUG_ON (vq->vq.num_free != vq->vring_packed.num); + +END_USE (vq);return NULL ; }@@ -1083,6 +1559,9 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq);
Quoted 8 lines
drivers/virtio/virtio_ring.c bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) · line 1567
Quoted 6 lines
drivers/virtio/virtio_ring.c +3
-0+/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers);return vq->packed ?virtqueue_poll_packed (_vq, last_used_idx) :virtqueue_poll_split (_vq, last_used_idx); }Possible optimization for when dma API is in use:
Got it. Will give it a try!
Best regards,
Tiwei Bie
Quoted 61 lines
--->
dma: detecting nop unmap
drivers need to maintain the dma address for unmap purposes, but these cycles are wasted when unmap callback is not defined. Add an API for drivers to check that and avoid unmap completely. Debug builds still have unmap.
Signed-off-by Michael S. Tsirkin <[email protected]>----
include/linux/dma-debug.h +10
-0@@ -42,6 +42,11 @@ extern void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr);extern void debug_dma_unmap_page (struct device *dev,dma_addr_t addr,size_t size,int direction,bool map_single); +static inline bool has_debug_dma_unmap (struct device *dev) +{ +return true; +} +extern void debug_dma_map_sg (struct device *dev,struct scatterlist *sg,int nents,int mapped_ents,int direction);@@ -121,6 +126,11 @@ static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, { } +static inline bool has_debug_dma_unmap (struct device *dev) +{ +return false; +} +static inline void debug_dma_map_sg (struct device *dev,struct scatterlist *sg,int nents,int mapped_ents,int direction) {include/linux/dma-mapping.h +8
-0@@ -241,6 +241,14 @@ static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,return addr; } +static inline bool has_dma_unmap (struct device *dev) +{ +const struct dma_map_ops *ops =get_dma_ops (dev); + +return ops->unmap_page || ops->unmap_sg || ops->unmap_resource || +has_dma_unmap (dev); +} + static inline void dma_unmap_single_attrs(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir,
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Tiwei BieQuoted 13 lines · Tiwei Bie
This commit introduces the support (without
EVENT_IDX) for packed ring.Signed-off-by Tiwei Bie <[email protected]>--- drivers/virtio/virtio_ring.c | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 8 deletions(-)drivers/virtio/virtio_ring.c +0
-0
drivers/virtio/virtio_ring.c line 59
Quoted 5 lines
drivers/virtio/virtio_ring.c +2
-0@@ -55,12 +55,21 @@#define END_USE (vq)#endif +#define _VRING_DESC_F_AVAIL (b) ((__u16)(b) << 7) +#define _VRING_DESC_F_USED (b) ((__u16)(b) << 15)
Underscore followed by an upper case letter isn't a good idea for a symbol. And it's not nice that this does not use the VRING_DESC_F_USED from the header.
How about ((b) ? VRING_DESC_F_USED : 0) instead?
Is produced code worse then?
drivers/virtio/virtio_ring.c line 68
Quoted 9 lines
drivers/virtio/virtio_ring.c +3
-0+struct vring_desc_state {void *data;/* Data for callback. */ struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };struct vring_desc_state_packed { +void *data;/* Data for callback. */ +struct vring_packed_desc *indir_desc;/* Indirect descriptor, if any. */
Include vring_desc_state for these?
drivers/virtio/virtio_ring.c line 72
Quoted 4 lines
drivers/virtio/virtio_ring.c +4
-0+int num;/* Descriptor list length. */ +dma_addr_t addr;/* Buffer DMA addr. */ +u32 len;/* Buffer length. */ +u16 flags;/* Descriptor flags. */
This seems only to be used for indirect. Check indirect field above instead and drop this.
drivers/virtio/virtio_ring.c line 73
Quoted 1 line
drivers/virtio/virtio_ring.c +0
-0int next;/* The next desc state. */
Packing things into 16 bit integers will help reduce cache pressure here.
Also, this is only used for unmap, so when dma API is not used, maintaining addr and len list management is unnecessary. How about we maintain addr/len in a separate array, avoiding accesses on unmap in that case?
Also, lots of architectures have a nop unmap in the DMA API. See a proposed patch at the end for optimizing that case.
drivers/virtio/virtio_ring.c static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx) · line 673
Quoted 9 lines
drivers/virtio/virtio_ring.c +0
-1};@@ -660,7 +669,6 @@ static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); -virtio_mb (vq->weak_barriers);return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); }
why is this changing the split queue implementation?
Quoted 27 lines
drivers/virtio/virtio_ring.c +22
-0@@ -757,6 +765,72 @@ static inline unsigned vring_size_packed(unsigned int num, unsigned long align) & ~(align - 1)) + sizeof(struct vring_packed_desc_event) * 2; } +static void vring_unmap_state_packed (const struct vring_virtqueue *vq, +struct vring_desc_state_packed *state) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags = state->flags; + +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +}
drivers/virtio/virtio_ring.c static inline unsigned vring_size_packed(unsigned int num, unsigned long align) · line 799
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+ +static void vring_unmap_desc_packed (const struct vring_virtqueue *vq, +struct vring_packed_desc *desc) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags =virtio16_to_cpu (vq->vq.vdev, desc->flags);
I see no reason to use virtioXX wrappers for the packed ring. That's there to support legacy devices. Just use leXX.
Quoted 16 lines
drivers/virtio/virtio_ring.c +16
-0+ +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +} +
drivers/virtio/virtio_ring.c static inline unsigned vring_size_packed(unsigned int num, unsigned long align) · line 825
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+static struct vring_packed_desc *alloc_indirect_packed (struct virtqueue *_vq, +unsigned int total_sg, +gfp_t gfp) +{ +struct vring_packed_desc *desc; + +/* +* We require lowmem mappings for the descriptors because +* otherwise virt_to_phys will give us bogus addresses in the +* virtqueue.
Where is virt_to_phys used? I don't see it in this patch.
Quoted 35 lines
drivers/virtio/virtio_ring.c +28
-0+*/ + gfp &= ~__GFP_HIGHMEM; + + desc =kmalloc (total_sg *sizeof (struct vring_packed_desc ), gfp); + +return desc; +} + static inline int virtqueue_add_packed(struct virtqueue *_vq, struct scatterlist *sgs[], unsigned int total_sg,@@ -766,47 +840,449 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, void *ctx, gfp_t gfp) { +struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_packed_desc *desc; +struct scatterlist *sg; +unsigned int i, n, descs_used,uninitialized_var (prev ), err_idx; +__virtio16 uninitialized_var (head_flags ), flags; +u16 head, avail_wrap_counter, id, curr; +bool indirect; + +START_USE (vq); + +BUG_ON (data ==NULL ); +BUG_ON (ctx && vq->indirect); + +if (unlikely (vq->broken)) { +END_USE (vq); +return -EIO ; + } + +#ifdef DEBUG + {
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 872
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+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
Add incline helpers for this debug stuff rather than duplicate it from split ring?
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 884
Quoted 12 lines
drivers/virtio/virtio_ring.c +12
-0+ +BUG_ON (total_sg ==0 ); + + head = vq->next_avail_idx; + avail_wrap_counter = vq->avail_wrap_counter; + +if (virtqueue_use_indirect (_vq, total_sg)) + desc =alloc_indirect_packed (_vq, total_sg, gfp); +else { + desc =NULL ; +WARN_ON_ONCE (total_sg > vq->vring_packed.num && !vq->indirect); + }
Apparently this attempts to treat indirect descriptors same as direct ones. This is how it is in the split ring, but not in the packed one. I think you want two separate functions, for direct and indirect.
Quoted 33 lines
drivers/virtio/virtio_ring.c +33
-0+ +if (desc) { +/* Use a single buffer which doesn't continue */ + indirect = true; +/* Set up rest to use this indirect table. */ + i =0 ; + descs_used =1 ; + }else { + indirect = false; + desc = vq->vring_packed.desc; + i = head; + descs_used = total_sg; + } + +if (vq->vq.num_free < descs_used) { +pr_debug ("Can't add buf len %i - avail = %i\n" , + descs_used, vq->vq.num_free); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out_sgs) + vq->notify (&vq->vq); +if (indirect) +kfree (desc); +END_USE (vq); +return -ENOSPC ; + } + + id = vq->free_head; +BUG_ON (id == vq->vring_packed.num); + + curr = id; +for (n =0 ; n < out_sgs + in_sgs; n++) {
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 927
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+for (sg = sgs[n]; sg; sg =sg_next (sg)) { +dma_addr_t addr =vring_map_one_sg (vq, sg, n < out_sgs ? +DMA_TO_DEVICE :DMA_FROM_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_NEXT | + (n < out_sgs ?0 :VRING_DESC_F_WRITE ) | +_VRING_DESC_F_AVAIL (vq->avail_wrap_counter) | +_VRING_DESC_F_USED (!vq->avail_wrap_counter));
Spec says:
The VIRTQ_DESC_F_WRITE flags bit is the only valid flag for descriptors in the indirect table.
All this logic isn't needed for indirect.
Also, why re-calculate avail/used flags every time? They only change when you wrap around.
Quoted 19 lines
drivers/virtio/virtio_ring.c +19
-0+if (!indirect && i == head) + head_flags = flags; +else + desc[i].flags = flags; + + desc[i].addr =cpu_to_virtio64 (_vq->vdev, addr); + desc[i].len =cpu_to_virtio32 (_vq->vdev, sg->length); + i++; +if (!indirect) { +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[curr].addr = addr; + vq->desc_state_packed[curr].len = + sg->length; + vq->desc_state_packed[curr].flags = +virtio16_to_cpu (_vq->vdev, + flags); + } + curr = vq->desc_state_packed[curr].next; +
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 956
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+if (i >= vq->vring_packed.num) { + i =0 ; + vq->avail_wrap_counter ^=1 ; + } + } + } + } + + prev = (i >0 ? i : vq->vring_packed.num) -1 ; + desc[prev].id =cpu_to_virtio16 (_vq->vdev, id);
Is it easier to write this out in all descriptors, to avoid the need to calculate prev?
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 963
Quoted 7 lines
drivers/virtio/virtio_ring.c +7
-0+ +/* Last one doesn't continue. */ +if (total_sg ==1 ) + head_flags &=cpu_to_virtio16 (_vq->vdev, ~VRING_DESC_F_NEXT ); +else + desc[prev].flags &=cpu_to_virtio16 (_vq->vdev, + ~VRING_DESC_F_NEXT );
Wouldn't it be easier to avoid setting VRING_DESC_F_NEXT in the first place?
if (n != in_sgs - 1 && n != out_sgs - 1)
must be better than writing descriptor an extra time.
Quoted 91 lines
drivers/virtio/virtio_ring.c +85
-1+ +if (indirect) { +/* Now that the indirect table is filled in, map it. */ +dma_addr_t addr =vring_map_single ( + vq, desc, total_sg *sizeof (struct vring_packed_desc ), +DMA_TO_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + head_flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_INDIRECT | +_VRING_DESC_F_AVAIL (avail_wrap_counter) | +_VRING_DESC_F_USED (!avail_wrap_counter)); + vq->vring_packed.desc[head].addr =cpu_to_virtio64 (_vq->vdev, + addr); + vq->vring_packed.desc[head].len =cpu_to_virtio32 (_vq->vdev, + total_sg *sizeof (struct vring_packed_desc )); + vq->vring_packed.desc[head].id =cpu_to_virtio16 (_vq->vdev, id); + +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[id].addr = addr; + vq->desc_state_packed[id].len = total_sg * +sizeof (struct vring_packed_desc ); + vq->desc_state_packed[id].flags = +virtio16_to_cpu (_vq->vdev, head_flags); + } + } + +/* We're using some buffers from the free list. */ + vq->vq.num_free -= descs_used; + +/* Update free pointer */ +if (indirect) { + n = head +1 ; +if (n >= vq->vring_packed.num) { + n =0 ; + vq->avail_wrap_counter ^=1 ; + } + vq->next_avail_idx = n; + vq->free_head = vq->desc_state_packed[id].next; + }else { + vq->next_avail_idx = i; + vq->free_head = curr; + } + +/* Store token and indirect buffer state. */ + vq->desc_state_packed[id].num = descs_used; + vq->desc_state_packed[id].data = data; +if (indirect) + vq->desc_state_packed[id].indir_desc = desc; +else + vq->desc_state_packed[id].indir_desc = ctx; + +/* A driver MUST NOT make the first descriptor in the list +* available before all subsequent descriptors comprising +* the list are made available. */ +virtio_wmb (vq->weak_barriers); + vq->vring_packed.desc[head].flags = head_flags; + vq->num_added += descs_used; + +pr_debug ("Added buffer head %i to %p\n" , head, vq); +END_USE (vq); + +return 0 ; + +unmap_release: + err_idx = i; + i = head; + +for (n =0 ; n < total_sg; n++) { +if (i == err_idx) +break ; +vring_unmap_desc_packed (vq, &desc[i]); + i++; +if (!indirect && i >= vq->vring_packed.num) + i =0 ; + } + + vq->avail_wrap_counter = avail_wrap_counter; + +if (indirect) +kfree (desc); + +END_USE (vq);return -EIO ; }static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 flags;
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 1063
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+bool needs_kick; +u32 snapshot; + +START_USE (vq); +/* We need to expose the new flags value before checking notification +* suppressions. */ +virtio_mb (vq->weak_barriers); + + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ;
What are all these hard-coded things? Also either we do READ_ONCE everywhere or nowhere. Why is this place special? Any why read 32 bit if you only want the 16?
And doesn't sparse complain about cast to __virtio16?
Quoted 59 lines
drivers/virtio/virtio_ring.c +59
-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 + + needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +END_USE (vq); +return needs_kick; +} + +static void detach_buf_packed (struct vring_virtqueue *vq, +unsigned int id,void **ctx) +{ +struct vring_desc_state_packed *state =NULL ; +struct vring_packed_desc *desc; +unsigned int curr, i; + +/* Clear data ptr. */ + vq->desc_state_packed[id].data =NULL ; + + curr = id; +for (i =0 ; i < vq->desc_state_packed[id].num; i++) { + state = &vq->desc_state_packed[curr]; +vring_unmap_state_packed (vq, state); + curr = state->next; + } + +BUG_ON (state ==NULL ); + vq->vq.num_free += vq->desc_state_packed[id].num; + state->next = vq->free_head; + vq->free_head = id; + +if (vq->indirect) { +u32 len; + +/* Free the indirect table, if any, now that it's unmapped. */ + desc = vq->desc_state_packed[id].indir_desc; +if (!desc) +return ; + +if (vring_use_dma_api (vq->vq.vdev)) { + len = vq->desc_state_packed[id].len; +for (i =0 ; i < len /sizeof (struct vring_packed_desc ); + i++) +vring_unmap_desc_packed (vq, &desc[i]); + } +kfree (desc); + vq->desc_state_packed[id].indir_desc =NULL ; + }else if (ctx) { + *ctx = vq->desc_state_packed[id].indir_desc; + } +} + +static inline bool is_used_desc_packed (const struct vring_virtqueue *vq, +u16 idx,bool used_wrap_counter)
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 1132
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+{ +u16 flags; +bool avail, used; + + flags =virtio16_to_cpu (vq->vq.vdev, + vq->vring_packed.desc[idx].flags); + avail = !!(flags &VRING_DESC_F_AVAIL ); + used = !!(flags &VRING_DESC_F_USED ); + +return avail == used && used == used_wrap_counter;
I think that you don't need to look at avail flag to detect a used descriptor. The reason device writes it is to avoid confusing *device* next time descriptor wraps.
Quoted 98 lines
drivers/virtio/virtio_ring.c +74
-4}static inline bool more_used_packed (const struct vring_virtqueue *vq) { -return false; +return is_used_desc_packed (vq, vq->last_used_idx, + vq->used_wrap_counter); }static void *virtqueue_get_buf_ctx_packed (struct virtqueue *_vq,unsigned int *len,void **ctx) { -return NULL ; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 last_used, id; +void *ret; + +START_USE (vq); + +if (unlikely (vq->broken)) { +END_USE (vq); +return NULL ; + } + +if (!more_used_packed (vq)) { +pr_debug ("No more buffers in queue\n" ); +END_USE (vq); +return NULL ; + } + +/* Only get used elements after they have been exposed by host. */ +virtio_rmb (vq->weak_barriers); + + last_used = vq->last_used_idx; + id =virtio16_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].id); + *len =virtio32_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].len); + +if (unlikely (id >= vq->vring_packed.num)) { +BAD_RING (vq,"id %u out of range\n" , id); +return NULL ; + } +if (unlikely (!vq->desc_state_packed[id].data)) { +BAD_RING (vq,"id %u is not a head!\n" , id); +return NULL ; + } + + vq->last_used_idx += vq->desc_state_packed[id].num; +if (vq->last_used_idx >= vq->vring_packed.num) { + vq->last_used_idx -= vq->vring_packed.num; + vq->used_wrap_counter ^=1 ; + } + +/* detach_buf_packed clears data, so grab it now. */ + ret = vq->desc_state_packed[id].data; +detach_buf_packed (vq, id, ctx); + +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif + +END_USE (vq); +return ret; }static void virtqueue_disable_cb_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); + +if (vq->event_flags_shadow !=VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_DISABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } }static unsigned virtqueue_enable_cb_prepare_packed (struct virtqueue *_vq) { -return 0 ; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } + +END_USE (vq); +return vq->last_used_idx | ((u16 )vq->used_wrap_counter <<15 ); } -static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned last_used_idx) +static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned off_wrap)
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 1235
Quoted 10 lines
drivers/virtio/virtio_ring.c +8
-1{ -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +bool wrap_counter; +u16 used_idx; + + wrap_counter = off_wrap >>15 ; + used_idx = off_wrap & ~(1 <<15 ); + +return is_used_desc_packed (vq, used_idx, wrap_counter);
These >> 15 << 15 all over the place duplicate info. Pls put 15 in the header.
Also can you maintain the counters properly shifted? Then just use them.
Quoted 55 lines
drivers/virtio/virtio_ring.c +42
-1}static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers); + } + +if (more_used_packed (vq)) { +END_USE (vq); +return false; + } + +END_USE (vq); +return true; }static void *virtqueue_detach_unused_buf_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); +unsigned int i; +void *buf; + +START_USE (vq); + +for (i =0 ; i < vq->vring_packed.num; i++) { +if (!vq->desc_state_packed[i].data) +continue ; +/* detach_buf clears data, so grab it now. */ + buf = vq->desc_state_packed[i].data; +detach_buf_packed (vq, i,NULL ); +END_USE (vq); +return buf; + } +/* That should have freed everything. */ +BUG_ON (vq->vq.num_free != vq->vring_packed.num); + +END_USE (vq);return NULL ; }
drivers/virtio/virtio_ring.c bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) · line 1567
Quoted 9 lines
drivers/virtio/virtio_ring.c +3
-0@@ -1083,6 +1559,9 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers);return vq->packed ?virtqueue_poll_packed (_vq, last_used_idx) :virtqueue_poll_split (_vq, last_used_idx); }
Possible optimization for when dma API is in use:
--->
dma: detecting nop unmap
drivers need to maintain the dma address for unmap purposes, but these cycles are wasted when unmap callback is not defined. Add an API for drivers to check that and avoid unmap completely. Debug builds still have unmap.
----
include/linux/dma-debug.h +10 -0
@@ -42,6 +42,11 @@ extern void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr);extern void debug_dma_unmap_page (struct device *dev,dma_addr_t addr,size_t size,int direction,bool map_single); +static inline bool has_debug_dma_unmap (struct device *dev) +{ +return true; +} +extern void debug_dma_map_sg (struct device *dev,struct scatterlist *sg,int nents,int mapped_ents,int direction);
@@ -121,6 +126,11 @@ static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, { } +static inline bool has_debug_dma_unmap (struct device *dev) +{ +return false; +} +static inline void debug_dma_map_sg (struct device *dev,struct scatterlist *sg,int nents,int mapped_ents,int direction) {
include/linux/dma-mapping.h +8 -0
@@ -241,6 +241,14 @@ static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,return addr; } +static inline bool has_dma_unmap (struct device *dev) +{ +const struct dma_map_ops *ops =get_dma_ops (dev); + +return ops->unmap_page || ops->unmap_sg || ops->unmap_resource || +has_dma_unmap (dev); +} + static inline void dma_unmap_single_attrs(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir,
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Tiwei BieQuoted 24 lines · Tiwei Bie
This commit introduces the support (without
EVENT_IDX) for packed ring.Signed-off-by Tiwei Bie <[email protected]>--- drivers/virtio/virtio_ring.c | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 8 deletions(-)drivers/virtio/virtio_ring.c +3
-0@@ -55,12 +55,21 @@#define END_USE (vq)#endif +#define _VRING_DESC_F_AVAIL (b) ((__u16)(b) << 7) +#define _VRING_DESC_F_USED (b) ((__u16)(b) << 15) +struct vring_desc_state {void *data;/* Data for callback. */ struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };
drivers/virtio/virtio_ring.c line 75
Quoted 10 lines
drivers/virtio/virtio_ring.c +6
-0struct vring_desc_state_packed { +void *data;/* Data for callback. */ +struct vring_packed_desc *indir_desc;/* Indirect descriptor, if any. */ +int num;/* Descriptor list length. */ +dma_addr_t addr;/* Buffer DMA addr. */ +u32 len;/* Buffer length. */ +u16 flags;/* Descriptor flags. */ int next;/* The next desc state. */ };
Idea: how about using data to chain these descriptors? You can validate it's pointing within an array to distinguish e.g. on detach.
Signature · 1 line
MST
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Tiwei BieQuoted 24 lines · Tiwei Bie
This commit introduces the support (without
EVENT_IDX) for packed ring.Signed-off-by Tiwei Bie <[email protected]>--- drivers/virtio/virtio_ring.c | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 8 deletions(-)drivers/virtio/virtio_ring.c +3
-0@@ -55,12 +55,21 @@#define END_USE (vq)#endif +#define _VRING_DESC_F_AVAIL (b) ((__u16)(b) << 7) +#define _VRING_DESC_F_USED (b) ((__u16)(b) << 15) +struct vring_desc_state {void *data;/* Data for callback. */ struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };
drivers/virtio/virtio_ring.c line 75
Quoted 10 lines
drivers/virtio/virtio_ring.c +6
-0struct vring_desc_state_packed { +void *data;/* Data for callback. */ +struct vring_packed_desc *indir_desc;/* Indirect descriptor, if any. */ +int num;/* Descriptor list length. */ +dma_addr_t addr;/* Buffer DMA addr. */ +u32 len;/* Buffer length. */ +u16 flags;/* Descriptor flags. */ int next;/* The next desc state. */ };
Idea: how about using data to chain these descriptors? You can validate it's pointing within an array to distinguish e.g. on detach.
Signature · 1 line
MST
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Tiwei BieQuoted 68 lines · Tiwei Bie
This commit introduces the support (without
EVENT_IDX) for packed ring.Signed-off-by Tiwei Bie <[email protected]>--- drivers/virtio/virtio_ring.c | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 8 deletions(-)drivers/virtio/virtio_ring.c +31
-1@@ -55,12 +55,21 @@#define END_USE (vq)#endif +#define _VRING_DESC_F_AVAIL (b) ((__u16)(b) << 7) +#define _VRING_DESC_F_USED (b) ((__u16)(b) << 15) +struct vring_desc_state {void *data;/* Data for callback. */ struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };struct vring_desc_state_packed { +void *data;/* Data for callback. */ +struct vring_packed_desc *indir_desc;/* Indirect descriptor, if any. */ +int num;/* Descriptor list length. */ +dma_addr_t addr;/* Buffer DMA addr. */ +u32 len;/* Buffer length. */ +u16 flags;/* Descriptor flags. */ int next;/* The next desc state. */ };@@ -660,7 +669,6 @@ static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); -virtio_mb (vq->weak_barriers);return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); }@@ -757,6 +765,72 @@ static inline unsigned vring_size_packed(unsigned int num, unsigned long align) & ~(align - 1)) + sizeof(struct vring_packed_desc_event) * 2; } +static void vring_unmap_state_packed (const struct vring_virtqueue *vq, +struct vring_desc_state_packed *state) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags = state->flags; + +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +}
drivers/virtio/virtio_ring.c static inline unsigned vring_size_packed(unsigned int num, unsigned long align) · line 799
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+ +static void vring_unmap_desc_packed (const struct vring_virtqueue *vq, +struct vring_packed_desc *desc) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags =virtio16_to_cpu (vq->vq.vdev, desc->flags);
BTW this stuff is only used on error etc. Is there a way to reuse vring_unmap_state_packed?
Quoted 93 lines
drivers/virtio/virtio_ring.c +86
-0+ +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +} + +static struct vring_packed_desc *alloc_indirect_packed (struct virtqueue *_vq, +unsigned int total_sg, +gfp_t gfp) +{ +struct vring_packed_desc *desc; + +/* +* We require lowmem mappings for the descriptors because +* otherwise virt_to_phys will give us bogus addresses in the +* virtqueue. +*/ + gfp &= ~__GFP_HIGHMEM; + + desc =kmalloc (total_sg *sizeof (struct vring_packed_desc ), gfp); + +return desc; +} + static inline int virtqueue_add_packed(struct virtqueue *_vq, struct scatterlist *sgs[], unsigned int total_sg,@@ -766,47 +840,449 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, void *ctx, gfp_t gfp) { +struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_packed_desc *desc; +struct scatterlist *sg; +unsigned int i, n, descs_used,uninitialized_var (prev ), err_idx; +__virtio16 uninitialized_var (head_flags ), flags; +u16 head, avail_wrap_counter, id, curr; +bool indirect; + +START_USE (vq); + +BUG_ON (data ==NULL ); +BUG_ON (ctx && vq->indirect); + +if (unlikely (vq->broken)) { +END_USE (vq); +return -EIO ; + } + +#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 + +BUG_ON (total_sg ==0 ); + + head = vq->next_avail_idx; + avail_wrap_counter = vq->avail_wrap_counter; + +if (virtqueue_use_indirect (_vq, total_sg)) + desc =alloc_indirect_packed (_vq, total_sg, gfp); +else { + desc =NULL ; +WARN_ON_ONCE (total_sg > vq->vring_packed.num && !vq->indirect); + } + +if (desc) { +/* Use a single buffer which doesn't continue */ + indirect = true; +/* Set up rest to use this indirect table. */ + i =0 ; + descs_used =1 ; + }else { + indirect = false; + desc = vq->vring_packed.desc;
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 904
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+ i = head; + descs_used = total_sg; + } + +if (vq->vq.num_free < descs_used) { +pr_debug ("Can't add buf len %i - avail = %i\n" , + descs_used, vq->vq.num_free); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */
I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 403 lines
drivers/virtio/virtio_ring.c +350
-7+if (out_sgs) + vq->notify (&vq->vq); +if (indirect) +kfree (desc); +END_USE (vq); +return -ENOSPC ; + } + + id = vq->free_head; +BUG_ON (id == vq->vring_packed.num); + + curr = id; +for (n =0 ; n < out_sgs + in_sgs; n++) { +for (sg = sgs[n]; sg; sg =sg_next (sg)) { +dma_addr_t addr =vring_map_one_sg (vq, sg, n < out_sgs ? +DMA_TO_DEVICE :DMA_FROM_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_NEXT | + (n < out_sgs ?0 :VRING_DESC_F_WRITE ) | +_VRING_DESC_F_AVAIL (vq->avail_wrap_counter) | +_VRING_DESC_F_USED (!vq->avail_wrap_counter)); +if (!indirect && i == head) + head_flags = flags; +else + desc[i].flags = flags; + + desc[i].addr =cpu_to_virtio64 (_vq->vdev, addr); + desc[i].len =cpu_to_virtio32 (_vq->vdev, sg->length); + i++; +if (!indirect) { +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[curr].addr = addr; + vq->desc_state_packed[curr].len = + sg->length; + vq->desc_state_packed[curr].flags = +virtio16_to_cpu (_vq->vdev, + flags); + } + curr = vq->desc_state_packed[curr].next; + +if (i >= vq->vring_packed.num) { + i =0 ; + vq->avail_wrap_counter ^=1 ; + } + } + } + } + + prev = (i >0 ? i : vq->vring_packed.num) -1 ; + desc[prev].id =cpu_to_virtio16 (_vq->vdev, id); + +/* Last one doesn't continue. */ +if (total_sg ==1 ) + head_flags &=cpu_to_virtio16 (_vq->vdev, ~VRING_DESC_F_NEXT ); +else + desc[prev].flags &=cpu_to_virtio16 (_vq->vdev, + ~VRING_DESC_F_NEXT ); + +if (indirect) { +/* Now that the indirect table is filled in, map it. */ +dma_addr_t addr =vring_map_single ( + vq, desc, total_sg *sizeof (struct vring_packed_desc ), +DMA_TO_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + head_flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_INDIRECT | +_VRING_DESC_F_AVAIL (avail_wrap_counter) | +_VRING_DESC_F_USED (!avail_wrap_counter)); + vq->vring_packed.desc[head].addr =cpu_to_virtio64 (_vq->vdev, + addr); + vq->vring_packed.desc[head].len =cpu_to_virtio32 (_vq->vdev, + total_sg *sizeof (struct vring_packed_desc )); + vq->vring_packed.desc[head].id =cpu_to_virtio16 (_vq->vdev, id); + +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[id].addr = addr; + vq->desc_state_packed[id].len = total_sg * +sizeof (struct vring_packed_desc ); + vq->desc_state_packed[id].flags = +virtio16_to_cpu (_vq->vdev, head_flags); + } + } + +/* We're using some buffers from the free list. */ + vq->vq.num_free -= descs_used; + +/* Update free pointer */ +if (indirect) { + n = head +1 ; +if (n >= vq->vring_packed.num) { + n =0 ; + vq->avail_wrap_counter ^=1 ; + } + vq->next_avail_idx = n; + vq->free_head = vq->desc_state_packed[id].next; + }else { + vq->next_avail_idx = i; + vq->free_head = curr; + } + +/* Store token and indirect buffer state. */ + vq->desc_state_packed[id].num = descs_used; + vq->desc_state_packed[id].data = data; +if (indirect) + vq->desc_state_packed[id].indir_desc = desc; +else + vq->desc_state_packed[id].indir_desc = ctx; + +/* A driver MUST NOT make the first descriptor in the list +* available before all subsequent descriptors comprising +* the list are made available. */ +virtio_wmb (vq->weak_barriers); + vq->vring_packed.desc[head].flags = head_flags; + vq->num_added += descs_used; + +pr_debug ("Added buffer head %i to %p\n" , head, vq); +END_USE (vq); + +return 0 ; + +unmap_release: + err_idx = i; + i = head; + +for (n =0 ; n < total_sg; n++) { +if (i == err_idx) +break ; +vring_unmap_desc_packed (vq, &desc[i]); + i++; +if (!indirect && i >= vq->vring_packed.num) + i =0 ; + } + + vq->avail_wrap_counter = avail_wrap_counter; + +if (indirect) +kfree (desc); + +END_USE (vq);return -EIO ; }static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 flags; +bool needs_kick; +u32 snapshot; + +START_USE (vq); +/* We need to expose the new flags value before checking notification +* suppressions. */ +virtio_mb (vq->weak_barriers); + + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ; + +#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 + + needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +END_USE (vq); +return needs_kick; +} + +static void detach_buf_packed (struct vring_virtqueue *vq, +unsigned int id,void **ctx) +{ +struct vring_desc_state_packed *state =NULL ; +struct vring_packed_desc *desc; +unsigned int curr, i; + +/* Clear data ptr. */ + vq->desc_state_packed[id].data =NULL ; + + curr = id; +for (i =0 ; i < vq->desc_state_packed[id].num; i++) { + state = &vq->desc_state_packed[curr]; +vring_unmap_state_packed (vq, state); + curr = state->next; + } + +BUG_ON (state ==NULL ); + vq->vq.num_free += vq->desc_state_packed[id].num; + state->next = vq->free_head; + vq->free_head = id; + +if (vq->indirect) { +u32 len; + +/* Free the indirect table, if any, now that it's unmapped. */ + desc = vq->desc_state_packed[id].indir_desc; +if (!desc) +return ; + +if (vring_use_dma_api (vq->vq.vdev)) { + len = vq->desc_state_packed[id].len; +for (i =0 ; i < len /sizeof (struct vring_packed_desc ); + i++) +vring_unmap_desc_packed (vq, &desc[i]); + } +kfree (desc); + vq->desc_state_packed[id].indir_desc =NULL ; + }else if (ctx) { + *ctx = vq->desc_state_packed[id].indir_desc; + } +} + +static inline bool is_used_desc_packed (const struct vring_virtqueue *vq, +u16 idx,bool used_wrap_counter) +{ +u16 flags; +bool avail, used; + + flags =virtio16_to_cpu (vq->vq.vdev, + vq->vring_packed.desc[idx].flags); + avail = !!(flags &VRING_DESC_F_AVAIL ); + used = !!(flags &VRING_DESC_F_USED ); + +return avail == used && used == used_wrap_counter; }static inline bool more_used_packed (const struct vring_virtqueue *vq) { -return false; +return is_used_desc_packed (vq, vq->last_used_idx, + vq->used_wrap_counter); }static void *virtqueue_get_buf_ctx_packed (struct virtqueue *_vq,unsigned int *len,void **ctx) { -return NULL ; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 last_used, id; +void *ret; + +START_USE (vq); + +if (unlikely (vq->broken)) { +END_USE (vq); +return NULL ; + } + +if (!more_used_packed (vq)) { +pr_debug ("No more buffers in queue\n" ); +END_USE (vq); +return NULL ; + } + +/* Only get used elements after they have been exposed by host. */ +virtio_rmb (vq->weak_barriers); + + last_used = vq->last_used_idx; + id =virtio16_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].id); + *len =virtio32_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].len); + +if (unlikely (id >= vq->vring_packed.num)) { +BAD_RING (vq,"id %u out of range\n" , id); +return NULL ; + } +if (unlikely (!vq->desc_state_packed[id].data)) { +BAD_RING (vq,"id %u is not a head!\n" , id); +return NULL ; + } + + vq->last_used_idx += vq->desc_state_packed[id].num; +if (vq->last_used_idx >= vq->vring_packed.num) { + vq->last_used_idx -= vq->vring_packed.num; + vq->used_wrap_counter ^=1 ; + } + +/* detach_buf_packed clears data, so grab it now. */ + ret = vq->desc_state_packed[id].data; +detach_buf_packed (vq, id, ctx); + +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif + +END_USE (vq); +return ret; }static void virtqueue_disable_cb_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); + +if (vq->event_flags_shadow !=VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_DISABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } }static unsigned virtqueue_enable_cb_prepare_packed (struct virtqueue *_vq) { -return 0 ; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } + +END_USE (vq); +return vq->last_used_idx | ((u16 )vq->used_wrap_counter <<15 ); } -static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned last_used_idx) +static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned off_wrap) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +bool wrap_counter; +u16 used_idx; + + wrap_counter = off_wrap >>15 ; + used_idx = off_wrap & ~(1 <<15 ); + +return is_used_desc_packed (vq, used_idx, wrap_counter); }static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers); + } + +if (more_used_packed (vq)) { +END_USE (vq); +return false; + } + +END_USE (vq); +return true; }static void *virtqueue_detach_unused_buf_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); +unsigned int i; +void *buf; + +START_USE (vq); + +for (i =0 ; i < vq->vring_packed.num; i++) { +if (!vq->desc_state_packed[i].data) +continue ; +/* detach_buf clears data, so grab it now. */ + buf = vq->desc_state_packed[i].data; +detach_buf_packed (vq, i,NULL ); +END_USE (vq); +return buf; + } +/* That should have freed everything. */ +BUG_ON (vq->vq.num_free != vq->vring_packed.num); + +END_USE (vq);return NULL ; }@@ -1083,6 +1559,9 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers);return vq->packed ?virtqueue_poll_packed (_vq, last_used_idx) :virtqueue_poll_split (_vq, last_used_idx); }Signature · 1 line
2.18.0
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 8 lines · Michael S. Tsirkin
Quoted 7 lines · Tiwei Bie
This commit introduces the support (without
EVENT_IDX) for packed ring.Signed-off-by Tiwei Bie <[email protected]>--- drivers/virtio/virtio_ring.c | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 8 deletions(-)
[...]
Quoted 28 lines
Quoted 28 lines
(quoted diff) +27
-0+static void vring_unmap_state_packed(const struct vring_virtqueue *vq, + struct vring_desc_state_packed *state) +{ + u16 flags; + + if (!vring_use_dma_api(vq->vq.vdev)) + return; + + flags = state->flags; + + if (flags & VRING_DESC_F_INDIRECT) { + dma_unmap_single(vring_dma_dev(vq), + state->addr, state->len, + (flags & VRING_DESC_F_WRITE) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } else { + dma_unmap_page(vring_dma_dev(vq), + state->addr, state->len, + (flags & VRING_DESC_F_WRITE) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } +} + +static void vring_unmap_desc_packed(const struct vring_virtqueue *vq, + struct vring_packed_desc *desc) +{ + u16 flags;
Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ + if (!vring_use_dma_api(vq->vq.vdev)) + return; + + flags = virtio16_to_cpu(vq->vq.vdev, desc->flags);BTW this stuff is only used on error etc. Is there a way to reuse
vring_unmap_state_packed?
It's also used by the INDIRECT path. We don't allocate desc state for INDIRECT descriptors to save DMA addr/len etc.
Quoted 16 lines
Quoted 16 lines
(quoted diff) +15
-0+ + if (flags & VRING_DESC_F_INDIRECT) { + dma_unmap_single(vring_dma_dev(vq), + virtio64_to_cpu(vq->vq.vdev, desc->addr), + virtio32_to_cpu(vq->vq.vdev, desc->len), + (flags & VRING_DESC_F_WRITE) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } else { + dma_unmap_page(vring_dma_dev(vq), + virtio64_to_cpu(vq->vq.vdev, desc->addr), + virtio32_to_cpu(vq->vq.vdev, desc->len), + (flags & VRING_DESC_F_WRITE) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } +}
[...]
Quoted 61 lines
Quoted 61 lines
(quoted diff) +57
-0@@ -766,47 +840,449 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, void *ctx, gfp_t gfp) { + struct vring_virtqueue *vq = to_vvq(_vq); + struct vring_packed_desc *desc; + struct scatterlist *sg; + unsigned int i, n, descs_used, uninitialized_var(prev), err_idx; + __virtio16 uninitialized_var(head_flags), flags; + u16 head, avail_wrap_counter, id, curr; + bool indirect; + + START_USE(vq); + + BUG_ON(data == NULL); + BUG_ON(ctx && vq->indirect); + + if (unlikely(vq->broken)) { + END_USE(vq); + return -EIO; + } + +#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 + + BUG_ON(total_sg == 0); + + head = vq->next_avail_idx; + avail_wrap_counter = vq->avail_wrap_counter; + + if (virtqueue_use_indirect(_vq, total_sg)) + desc = alloc_indirect_packed(_vq, total_sg, gfp); + else { + desc = NULL; + WARN_ON_ONCE(total_sg > vq->vring_packed.num && !vq->indirect); + } + + if (desc) { + /* Use a single buffer which doesn't continue */ + indirect = true; + /* Set up rest to use this indirect table. */ + i = 0; + descs_used = 1; + } else { + indirect = false; + desc = vq->vring_packed.desc; + i = head; + descs_used = total_sg; + } + + if (vq->vq.num_free < descs_used) {
Quoted 8 lines
Review static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 904
Quoted 5 lines
(quoted diff) +5
-0+ pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
commit 44653eae1407f79dff6f52fcf594ae84cb165ec4
Author: Rusty Russell <[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500
virtio: don't always force a notification when ring is full
We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136
drivers/virtio/virtio_ring.c +5 -2
@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify *even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Quoted 9 lines
Quoted 9 lines
(quoted diff) +8
-0+ if (out_sgs) + vq->notify(&vq->vq); + if (indirect) + kfree(desc); + END_USE(vq); + return -ENOSPC; + } +
[...]
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 9 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 4 lines
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
According to the commit log, it seems like a workaround of lguest networking backend. I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 46 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full
We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +5
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 11 lines · Jason Wang
Quoted 9 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 8 lines
Quoted 4 lines
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
According to the commit log, it seems like a workaround of lguest networking backend.
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Quoted 51 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 42 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +5
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 18 lines · Tiwei Bie
Quoted 17 lines · Jason Wang
Quoted 13 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 6 lines
Quoted 2 lines
According to the commit log, it seems like a workaround of lguest networking backend.
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Yes. QEMU has a mode where it disables notifications and processes TX ring periodically from a timer. It's off by default but used to be on by default a long time ago. If ring becomes full this causes traffic stalls. As a work-around Rusty put in this hack to kick on ring full even with notifications disabled. It's easy enough to make sure QEMU does not combine devices with packed ring support with the timer hack. And I am guessing it's safe enough to also block that option completely e.g. when virtio 1.0 is enabled.
Quoted 52 lines
Quoted 52 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 42 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +5
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 26 lines · Michael S. Tsirkin
Quoted 18 lines · Tiwei Bie
Quoted 17 lines · Jason Wang
Quoted 13 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 6 lines
Quoted 2 lines
According to the commit log, it seems like a workaround of lguest networking backend.
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Quoted 8 lines
Yes. QEMU has a mode where it disables notifications and processes TX ring periodically from a timer. It's off by default but used to be on by default a long time ago. If ring becomes full this causes traffic stalls. As a work-around Rusty put in this hack to kick on ring full even with notifications disabled. It's easy enough to make sure QEMU does not combine devices with packed ring support with the timer hack. And I am guessing it's safe enough to also block that option completely e.g. when virtio 1.0 is enabled.
I see. Thanks!
Quoted 53 lines
Quoted 53 lines
Quoted 53 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 42 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +5
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Michael S. TsirkinQuoted 26 lines · Michael S. Tsirkin
Quoted 18 lines · Tiwei Bie
Quoted 17 lines · Jason Wang
Quoted 13 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 6 lines
Quoted 2 lines
According to the commit log, it seems like a workaround of lguest networking backend.
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Quoted 8 lines
Yes. QEMU has a mode where it disables notifications and processes TX ring periodically from a timer. It's off by default but used to be on by default a long time ago. If ring becomes full this causes traffic stalls. As a work-around Rusty put in this hack to kick on ring full even with notifications disabled. It's easy enough to make sure QEMU does not combine devices with packed ring support with the timer hack. And I am guessing it's safe enough to also block that option completely e.g. when virtio 1.0 is enabled.
I see. Thanks!
Quoted 45 lines
Quoted 45 lines
Quoted 45 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 34 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +2
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/virtio/virtio_ring.c static int vring_add_buf(struct virtqueue *_vq, · line 97
Quoted 8 lines
drivers/virtio/virtio_ring.c +3
-0+* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Michael S. TsirkinQuoted 19 lines · Michael S. Tsirkin
Quoted 18 lines · Tiwei Bie
Quoted 17 lines · Jason Wang
Quoted 9 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 4 lines
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
According to the commit log, it seems like a workaround of lguest networking backend.
Quoted 7 lines
Quoted 3 lines
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Yes. QEMU has a mode where it disables notifications and processes TX ring periodically from a timer. It's off by default but used to be on by default a long time ago. If ring becomes full this causes traffic stalls.
Do you mean tx-timer? If yes, we can still enable it for packed ring and the timer will finally fired and we can go.
Quoted 2 lines
As a work-around Rusty put in this hack to kick on ring full even with notifications disabled.
From the commit log it looks more like a performance workaround instead of a bug fix.
Quoted 4 lines
It's easy enough to make sure QEMU
does not combine devices with packed ring support with the timer hack. And I am guessing it's safe enough to also block that option completely e.g. when virtio 1.0 is enabled.
I agree.
Thanks
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 41 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +5
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 24 lines · Jason Wang
Quoted 22 lines · Michael S. Tsirkin
Quoted 21 lines · Tiwei Bie
Quoted 10 lines · Jason Wang
Quoted 9 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 7 lines
Quoted 4 lines
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
According to the commit log, it seems like a workaround of lguest networking backend.
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Quoted 7 lines
Quoted 4 lines
Yes. QEMU has a mode where it disables notifications and processes TX ring periodically from a timer. It's off by default but used to be on by default a long time ago. If ring becomes full this causes traffic stalls.
Do you mean tx-timer? If yes, we can still enable it for packed ring
Yes we can but I doubt anyone does.
Quoted 2 lines
and the
timer will finally fired and we can go.
on tx ring full we probably don't want to wait for timer. But I think we can just prevent qemu from using tx timer with virtio 1.
Quoted 7 lines
Quoted 3 lines
As a work-around Rusty put in this hack to kick on ring full even with notifications disabled.
From the commit log it looks more like a performance workaround instead of a bug fix.
it's a quality of implementation issue, yes.
Quoted 62 lines
Quoted 5 lines
It's easy enough to make sure QEMU
does not combine devices with packed ring support with the timer hack. And I am guessing it's safe enough to also block that option completely e.g. when virtio 1.0 is enabled.I agree.
Thanks
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 41 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +5
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 28 lines · Michael S. Tsirkin
Quoted 27 lines · Jason Wang
Quoted 25 lines · Michael S. Tsirkin
Quoted 15 lines · Tiwei Bie
Quoted 14 lines · Jason Wang
Quoted 13 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 5 lines
Quoted 2 lines
According to the commit log, it seems like a workaround of lguest networking backend.
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Yes. QEMU has a mode where it disables notifications and processes TX ring periodically from a timer. It's off by default but used to be on by default a long time ago. If ring becomes full this causes traffic stalls.
Quoted 8 lines
Quoted 1 line
Do you mean tx-timer? If yes, we can still enable it for packed ring
Yes we can but I doubt anyone does.
Quoted 2 lines
and the
timer will finally fired and we can go.on tx ring full we probably don't want to wait for timer. But I think we can just prevent qemu from using tx timer with virtio 1.
Yes, we can.
Thanks
Quoted 68 lines
Quoted 6 lines
Quoted 3 lines
As a work-around Rusty put in this hack to kick on ring full even with notifications disabled.
From the commit log it looks more like a performance workaround instead of a
bug fix.it's a quality of implementation issue, yes.
Quoted 60 lines
Quoted 4 lines
It's easy enough to make sure QEMU
does not combine devices with packed ring support with the timer hack. And I am guessing it's safe enough to also block that option completely e.g. when virtio 1.0 is enabled.I agree.
Thanks
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 41 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +5
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Michael S. TsirkinQuoted 28 lines · Michael S. Tsirkin
Quoted 27 lines · Jason Wang
Quoted 25 lines · Michael S. Tsirkin
Quoted 15 lines · Tiwei Bie
Quoted 14 lines · Jason Wang
Quoted 13 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 5 lines
Quoted 2 lines
According to the commit log, it seems like a workaround of lguest networking backend.
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Yes. QEMU has a mode where it disables notifications and processes TX ring periodically from a timer. It's off by default but used to be on by default a long time ago. If ring becomes full this causes traffic stalls.
Quoted 8 lines
Quoted 1 line
Do you mean tx-timer? If yes, we can still enable it for packed ring
Yes we can but I doubt anyone does.
Quoted 2 lines
and the
timer will finally fired and we can go.on tx ring full we probably don't want to wait for timer. But I think we can just prevent qemu from using tx timer with virtio 1.
Yes, we can.
Thanks
Quoted 60 lines
Quoted 6 lines
Quoted 3 lines
As a work-around Rusty put in this hack to kick on ring full even with notifications disabled.
From the commit log it looks more like a performance workaround instead of a
bug fix.it's a quality of implementation issue, yes.
Quoted 52 lines
Quoted 4 lines
It's easy enough to make sure QEMU
does not combine devices with packed ring support with the timer hack. And I am guessing it's safe enough to also block that option completely e.g. when virtio 1.0 is enabled.I agree.
Thanks
Quoted 42 lines
Quoted 42 lines
Quoted 42 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 33 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +2
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/virtio/virtio_ring.c static int vring_add_buf(struct virtqueue *_vq, · line 97
Quoted 8 lines
drivers/virtio/virtio_ring.c +3
-0+* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Jason WangQuoted 24 lines · Jason Wang
Quoted 22 lines · Michael S. Tsirkin
Quoted 21 lines · Tiwei Bie
Quoted 10 lines · Jason Wang
Quoted 9 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 7 lines
Quoted 4 lines
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
According to the commit log, it seems like a workaround of lguest networking backend.
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Quoted 7 lines
Quoted 4 lines
Yes. QEMU has a mode where it disables notifications and processes TX ring periodically from a timer. It's off by default but used to be on by default a long time ago. If ring becomes full this causes traffic stalls.
Do you mean tx-timer? If yes, we can still enable it for packed ring
Yes we can but I doubt anyone does.
Quoted 2 lines
and the
timer will finally fired and we can go.
on tx ring full we probably don't want to wait for timer. But I think we can just prevent qemu from using tx timer with virtio 1.
Quoted 7 lines
Quoted 3 lines
As a work-around Rusty put in this hack to kick on ring full even with notifications disabled.
From the commit log it looks more like a performance workaround instead of a bug fix.
it's a quality of implementation issue, yes.
Quoted 54 lines
Quoted 5 lines
It's easy enough to make sure QEMU
does not combine devices with packed ring support with the timer hack. And I am guessing it's safe enough to also block that option completely e.g. when virtio 1.0 is enabled.I agree.
Thanks
Quoted 42 lines
Quoted 42 lines
Quoted 42 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 33 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +2
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/virtio/virtio_ring.c static int vring_add_buf(struct virtqueue *_vq, · line 97
Quoted 8 lines
drivers/virtio/virtio_ring.c +3
-0+* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Michael S. TsirkinQuoted 19 lines · Michael S. Tsirkin
Quoted 18 lines · Tiwei Bie
Quoted 17 lines · Jason Wang
Quoted 9 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 4 lines
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
According to the commit log, it seems like a workaround of lguest networking backend.
Quoted 7 lines
Quoted 3 lines
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Yes. QEMU has a mode where it disables notifications and processes TX ring periodically from a timer. It's off by default but used to be on by default a long time ago. If ring becomes full this causes traffic stalls.
Do you mean tx-timer? If yes, we can still enable it for packed ring and the timer will finally fired and we can go.
Quoted 2 lines
As a work-around Rusty put in this hack to kick on ring full even with notifications disabled.
From the commit log it looks more like a performance workaround instead of a bug fix.
Quoted 4 lines
It's easy enough to make sure QEMU
does not combine devices with packed ring support with the timer hack. And I am guessing it's safe enough to also block that option completely e.g. when virtio 1.0 is enabled.
I agree.
Thanks
Quoted 42 lines
Quoted 42 lines
Quoted 42 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 33 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +2
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/virtio/virtio_ring.c static int vring_add_buf(struct virtqueue *_vq, · line 97
Quoted 8 lines
drivers/virtio/virtio_ring.c +3
-0+* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Tiwei BieQuoted 18 lines · Tiwei Bie
Quoted 17 lines · Jason Wang
Quoted 13 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 6 lines
Quoted 2 lines
According to the commit log, it seems like a workaround of lguest networking backend.
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Yes. QEMU has a mode where it disables notifications and processes TX ring periodically from a timer. It's off by default but used to be on by default a long time ago. If ring becomes full this causes traffic stalls. As a work-around Rusty put in this hack to kick on ring full even with notifications disabled. It's easy enough to make sure QEMU does not combine devices with packed ring support with the timer hack. And I am guessing it's safe enough to also block that option completely e.g. when virtio 1.0 is enabled.
Quoted 44 lines
Quoted 44 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 34 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +2
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the
Quoted 8 lines
Quoted 8 lines
drivers/virtio/virtio_ring.c static int vring_add_buf(struct virtqueue *_vq, · line 97
Quoted 8 lines
drivers/virtio/virtio_ring.c +3
-0+* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Jason WangQuoted 11 lines · Jason Wang
Quoted 9 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 8 lines
Quoted 4 lines
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
According to the commit log, it seems like a workaround of lguest networking backend.
Do you know why removing this notify in Tx will break "the heuristics which KVM uses"? Or what does "the heuristics which KVM uses" refer to?
Quoted 43 lines
I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 34 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +2
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the
Quoted 8 lines
drivers/virtio/virtio_ring.c static int vring_add_buf(struct virtqueue *_vq, · line 97
Quoted 8 lines
drivers/virtio/virtio_ring.c +3
-0+* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Jason WangQuoted 17 lines · Jason Wang
Quoted 9 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 4 lines
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 5 lines
According to the commit log, it seems like a workaround of lguest networking backend. I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair.
I don't think this ever triggers to be frank. When would it?
Quoted 4 lines
Consider the removal of lguest support recently,
maybe we can drop this for split ring as well?Thanks
If it's helpful, then for sure we can drop it for virtio 1. Can you see any perf differences at all? With which device?
Quoted 43 lines
Quoted 43 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +5
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 16 lines · Michael S. Tsirkin
Quoted 15 lines · Jason Wang
Quoted 13 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 6 lines
Quoted 5 lines
According to the commit log, it seems like a workaround of lguest networking backend. I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair.
I don't think this ever triggers to be frank. When would it?
I think it can happen e.g in the path of XDP transmission in __virtnet_xdp_xmit_one():
err = virtqueue_add_outbuf(sq->vq, sq->sg, 1, xdpf, GFP_ATOMIC); if (unlikely(err))
return -ENOSPC; /* Caller handle free/refcnt */
Quoted 7 lines
Quoted 5 lines
Consider the removal of lguest support recently,
maybe we can drop this for split ring as well?Thanks
If it's helpful, then for sure we can drop it for virtio 1. Can you see any perf differences at all? With which device?
I don't test but consider the case of XDP_TX in guest plus vhost_net in host. Since vhost_net is half duplex, it's pretty easier to trigger this condition.
Thanks
Quoted 42 lines
Quoted 42 lines
Quoted 42 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +5
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 26 lines · Jason Wang
Quoted 16 lines · Michael S. Tsirkin
Quoted 15 lines · Jason Wang
Quoted 13 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 6 lines
Quoted 5 lines
According to the commit log, it seems like a workaround of lguest networking backend. I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair.
I don't think this ever triggers to be frank. When would it?
Quoted 8 lines
I think it can happen e.g in the path of XDP transmission in
__virtnet_xdp_xmit_one():err =
virtqueue_add_outbuf(sq->vq, sq->sg, 1, xdpf, GFP_ATOMIC); if (unlikely(err))
return-ENOSPC; /* Caller handle free/refcnt */
I see. We used to do it for regular xmit but stopped doing it. Is it fine for xdp then?
Quoted 9 lines
Quoted 7 lines
Quoted 5 lines
Consider the removal of lguest support recently,
maybe we can drop this for split ring as well?Thanks
If it's helpful, then for sure we can drop it for virtio 1. Can you see any perf differences at all? With which device?
Quoted 5 lines
I don't test but consider the case of
XDP_TXin guest plusvhost_netin host. Sincevhost_netis half duplex, it's pretty easier to trigger this condition.Thanks
Sounds reasonable. Worth testing before we change things though.
Quoted 43 lines
Quoted 43 lines
Quoted 43 lines
Quoted 43 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +5
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
Quoted 28 lines · Michael S. Tsirkin
Quoted 27 lines · Jason Wang
Quoted 15 lines · Michael S. Tsirkin
Quoted 14 lines · Jason Wang
Quoted 13 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 6 lines
Quoted 5 lines
According to the commit log, it seems like a workaround of lguest networking backend. I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair.
I don't think this ever triggers to be frank. When would it?
I think it can happen e.g in the path of XDP transmission in
__virtnet_xdp_xmit_one():
Quoted 6 lines
Quoted 4 lines
err =
virtqueue_add_outbuf(sq->vq, sq->sg, 1, xdpf, GFP_ATOMIC); if (unlikely(err))
return-ENOSPC; /* Caller handle free/refcnt */I see. We used to do it for regular xmit but stopped doing it. Is it fine for xdp then?
There's no traffic control in XDP, so it was the only thing we can do.
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
Quoted 5 lines
Consider the removal of lguest support recently,
maybe we can drop this for split ring as well?Thanks
If it's helpful, then for sure we can drop it for virtio 1. Can you see any perf differences at all? With which device?
Quoted 6 lines
Quoted 5 lines
I don't test but consider the case of
XDP_TXin guest plusvhost_netin host. Sincevhost_netis half duplex, it's pretty easier to trigger this condition.Thanks
Sounds reasonable. Worth testing before we change things though.
Let me test and submit a patch.
Thanks
Quoted 34 lines
Quoted 34 lines
Quoted 34 lines
Quoted 34 lines
Quoted 34 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +2
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/virtio/virtio_ring.c static int vring_add_buf(struct virtqueue *_vq, · line 97
Quoted 8 lines
drivers/virtio/virtio_ring.c +3
-0+* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Jason WangQuoted 26 lines · Jason Wang
Quoted 16 lines · Michael S. Tsirkin
Quoted 15 lines · Jason Wang
Quoted 13 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 6 lines
Quoted 5 lines
According to the commit log, it seems like a workaround of lguest networking backend. I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair.
I don't think this ever triggers to be frank. When would it?
Quoted 8 lines
I think it can happen e.g in the path of XDP transmission in
__virtnet_xdp_xmit_one():err =
virtqueue_add_outbuf(sq->vq, sq->sg, 1, xdpf, GFP_ATOMIC); if (unlikely(err))
return-ENOSPC; /* Caller handle free/refcnt */
I see. We used to do it for regular xmit but stopped doing it. Is it fine for xdp then?
Quoted 9 lines
Quoted 7 lines
Quoted 5 lines
Consider the removal of lguest support recently,
maybe we can drop this for split ring as well?Thanks
If it's helpful, then for sure we can drop it for virtio 1. Can you see any perf differences at all? With which device?
Quoted 5 lines
I don't test but consider the case of
XDP_TXin guest plusvhost_netin host. Sincevhost_netis half duplex, it's pretty easier to trigger this condition.Thanks
Sounds reasonable. Worth testing before we change things though.
Quoted 35 lines
Quoted 35 lines
Quoted 35 lines
Quoted 35 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +2
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/virtio/virtio_ring.c static int vring_add_buf(struct virtqueue *_vq, · line 97
Quoted 8 lines
drivers/virtio/virtio_ring.c +3
-0+* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Michael S. TsirkinQuoted 16 lines · Michael S. Tsirkin
Quoted 15 lines · Jason Wang
Quoted 13 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 6 lines
Quoted 5 lines
According to the commit log, it seems like a workaround of lguest networking backend. I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair.
I don't think this ever triggers to be frank. When would it?
I think it can happen e.g in the path of XDP transmission in __virtnet_xdp_xmit_one():
err = virtqueue_add_outbuf(sq->vq, sq->sg, 1, xdpf, GFP_ATOMIC); if (unlikely(err))
return -ENOSPC; /* Caller handle free/refcnt */
Quoted 7 lines
Quoted 5 lines
Consider the removal of lguest support recently,
maybe we can drop this for split ring as well?Thanks
If it's helpful, then for sure we can drop it for virtio 1. Can you see any perf differences at all? With which device?
I don't test but consider the case of XDP_TX in guest plus vhost_net in host. Since vhost_net is half duplex, it's pretty easier to trigger this condition.
Thanks
Quoted 34 lines
Quoted 34 lines
Quoted 34 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +2
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the
Quoted 8 lines
Quoted 8 lines
drivers/virtio/virtio_ring.c static int vring_add_buf(struct virtqueue *_vq, · line 97
Quoted 8 lines
drivers/virtio/virtio_ring.c +3
-0+* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Jason WangQuoted 17 lines · Jason Wang
Quoted 9 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 4 lines
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
Quoted 5 lines
According to the commit log, it seems like a workaround of lguest networking backend. I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair.
I don't think this ever triggers to be frank. When would it?
Quoted 4 lines
Consider the removal of lguest support recently,
maybe we can drop this for split ring as well?Thanks
If it's helpful, then for sure we can drop it for virtio 1. Can you see any perf differences at all? With which device?
Quoted 35 lines
Quoted 35 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +2
-2@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the
Quoted 8 lines
drivers/virtio/virtio_ring.c static int vring_add_buf(struct virtqueue *_vq, · line 97
Quoted 8 lines
drivers/virtio/virtio_ring.c +3
-0+* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Tiwei BieQuoted 9 lines · Tiwei Bie
Quoted 9 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+ + if (vq->vq.num_free < descs_used) { + pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 4 lines
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
According to the commit log, it seems like a workaround of lguest networking backend. I agree to drop it, we should not have such burden.
But we should notice that, with this removed, the compare between packed vs split is kind of unfair. Consider the removal of lguest support recently, maybe we can drop this for split ring as well?
Thanks
Quoted 36 lines
commit
44653eae1407f79dff6f52fcf594ae84cb165ec4Author: Rusty Russell<[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500virtio: don't always force a notification when ring is full
We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136Signed-off-by Rusty Russell<[email protected]>drivers/virtio/virtio_ring.c +0
-0@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free);
drivers/virtio/virtio_ring.c static int vring_add_buf(struct virtqueue *_vq, · line 97
Quoted 10 lines
drivers/virtio/virtio_ring.c +5
-2-/* We notify*even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
Re: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Michael S. TsirkinQuoted 8 lines · Michael S. Tsirkin
Quoted 7 lines · Tiwei Bie
This commit introduces the support (without
EVENT_IDX) for packed ring.Signed-off-by Tiwei Bie <[email protected]>--- drivers/virtio/virtio_ring.c | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 8 deletions(-)
[...]
Quoted 28 lines
Quoted 28 lines
(quoted diff) +27
-0+static void vring_unmap_state_packed(const struct vring_virtqueue *vq, + struct vring_desc_state_packed *state) +{ + u16 flags; + + if (!vring_use_dma_api(vq->vq.vdev)) + return; + + flags = state->flags; + + if (flags & VRING_DESC_F_INDIRECT) { + dma_unmap_single(vring_dma_dev(vq), + state->addr, state->len, + (flags & VRING_DESC_F_WRITE) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } else { + dma_unmap_page(vring_dma_dev(vq), + state->addr, state->len, + (flags & VRING_DESC_F_WRITE) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } +} + +static void vring_unmap_desc_packed(const struct vring_virtqueue *vq, + struct vring_packed_desc *desc) +{ + u16 flags;
Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ + if (!vring_use_dma_api(vq->vq.vdev)) + return; + + flags = virtio16_to_cpu(vq->vq.vdev, desc->flags);BTW this stuff is only used on error etc. Is there a way to reuse
vring_unmap_state_packed?
It's also used by the INDIRECT path. We don't allocate desc state for INDIRECT descriptors to save DMA addr/len etc.
Quoted 16 lines
Quoted 16 lines
(quoted diff) +15
-0+ + if (flags & VRING_DESC_F_INDIRECT) { + dma_unmap_single(vring_dma_dev(vq), + virtio64_to_cpu(vq->vq.vdev, desc->addr), + virtio32_to_cpu(vq->vq.vdev, desc->len), + (flags & VRING_DESC_F_WRITE) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } else { + dma_unmap_page(vring_dma_dev(vq), + virtio64_to_cpu(vq->vq.vdev, desc->addr), + virtio32_to_cpu(vq->vq.vdev, desc->len), + (flags & VRING_DESC_F_WRITE) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } +}
[...]
Quoted 61 lines
Quoted 61 lines
(quoted diff) +57
-0@@ -766,47 +840,449 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, void *ctx, gfp_t gfp) { + struct vring_virtqueue *vq = to_vvq(_vq); + struct vring_packed_desc *desc; + struct scatterlist *sg; + unsigned int i, n, descs_used, uninitialized_var(prev), err_idx; + __virtio16 uninitialized_var(head_flags), flags; + u16 head, avail_wrap_counter, id, curr; + bool indirect; + + START_USE(vq); + + BUG_ON(data == NULL); + BUG_ON(ctx && vq->indirect); + + if (unlikely(vq->broken)) { + END_USE(vq); + return -EIO; + } + +#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 + + BUG_ON(total_sg == 0); + + head = vq->next_avail_idx; + avail_wrap_counter = vq->avail_wrap_counter; + + if (virtqueue_use_indirect(_vq, total_sg)) + desc = alloc_indirect_packed(_vq, total_sg, gfp); + else { + desc = NULL; + WARN_ON_ONCE(total_sg > vq->vring_packed.num && !vq->indirect); + } + + if (desc) { + /* Use a single buffer which doesn't continue */ + indirect = true; + /* Set up rest to use this indirect table. */ + i = 0; + descs_used = 1; + } else { + indirect = false; + desc = vq->vring_packed.desc; + i = head; + descs_used = total_sg; + } + + if (vq->vq.num_free < descs_used) {
Quoted 8 lines
Review static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 904
Quoted 5 lines
(quoted diff) +5
-0+ pr_debug("Can't add buf len %i - avail = %i\n", + descs_used, vq->vq.num_free); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Based on the original commit log, it seems that the notify here is just an "optimization". But I don't quite understand what does the "the heuristics which KVM uses" refer to. If it's safe to drop this in packed ring, I'd like to do it.
commit 44653eae1407f79dff6f52fcf594ae84cb165ec4
Author: Rusty Russell <[email protected]> Date: Fri Jul 25 12:06:04 2008 -0500
virtio: don't always force a notification when ring is full
We force notification when the ring is full, even if the host has indicated it doesn't want to know. This seemed like a good idea at the time: if we fill the transmit ring, we should tell the host immediately.
Unfortunately this logic also applies to the receiving ring, which is refilled constantly. We should introduce real notification thesholds to replace this logic. Meanwhile, removing the logic altogether breaks the heuristics which KVM uses, so we use a hack: only notify if there are outgoing parts of the new buffer.
Here are the number of exits with lguest's crappy network implementation:
Before:
network xmit 7859051 recv 236420
After:
network xmit 7858610 recv 118136
drivers/virtio/virtio_ring.c +5 -2
@@ -87,8 +87,11 @@ static int vring_add_buf(struct virtqueue *_vq,if (vq->num_free < out + in) {pr_debug ("Can't add buf len %i - avail = %i\n" , out + in, vq->num_free); -/* We notify *even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify (&vq->vq); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out) + vq->notify (&vq->vq);END_USE (vq);return -ENOSPC ; }
Quoted 9 lines
Quoted 9 lines
(quoted diff) +8
-0+ if (out_sgs) + vq->notify(&vq->vq); + if (indirect) + kfree(desc); + END_USE(vq); + return -ENOSPC; + } +
[...]
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 3/5] virtio_ring: add packed ring support
In reply to Tiwei BieQuoted 68 lines · Tiwei Bie
This commit introduces the support (without
EVENT_IDX) for packed ring.Signed-off-by Tiwei Bie <[email protected]>--- drivers/virtio/virtio_ring.c | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 8 deletions(-)drivers/virtio/virtio_ring.c +31
-1@@ -55,12 +55,21 @@#define END_USE (vq)#endif +#define _VRING_DESC_F_AVAIL (b) ((__u16)(b) << 7) +#define _VRING_DESC_F_USED (b) ((__u16)(b) << 15) +struct vring_desc_state {void *data;/* Data for callback. */ struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };struct vring_desc_state_packed { +void *data;/* Data for callback. */ +struct vring_packed_desc *indir_desc;/* Indirect descriptor, if any. */ +int num;/* Descriptor list length. */ +dma_addr_t addr;/* Buffer DMA addr. */ +u32 len;/* Buffer length. */ +u16 flags;/* Descriptor flags. */ int next;/* The next desc state. */ };@@ -660,7 +669,6 @@ static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); -virtio_mb (vq->weak_barriers);return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); }@@ -757,6 +765,72 @@ static inline unsigned vring_size_packed(unsigned int num, unsigned long align) & ~(align - 1)) + sizeof(struct vring_packed_desc_event) * 2; } +static void vring_unmap_state_packed (const struct vring_virtqueue *vq, +struct vring_desc_state_packed *state) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags = state->flags; + +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +}
drivers/virtio/virtio_ring.c static inline unsigned vring_size_packed(unsigned int num, unsigned long align) · line 799
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+ +static void vring_unmap_desc_packed (const struct vring_virtqueue *vq, +struct vring_packed_desc *desc) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags =virtio16_to_cpu (vq->vq.vdev, desc->flags);
BTW this stuff is only used on error etc. Is there a way to reuse vring_unmap_state_packed?
Quoted 93 lines
drivers/virtio/virtio_ring.c +86
-0+ +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +} + +static struct vring_packed_desc *alloc_indirect_packed (struct virtqueue *_vq, +unsigned int total_sg, +gfp_t gfp) +{ +struct vring_packed_desc *desc; + +/* +* We require lowmem mappings for the descriptors because +* otherwise virt_to_phys will give us bogus addresses in the +* virtqueue. +*/ + gfp &= ~__GFP_HIGHMEM; + + desc =kmalloc (total_sg *sizeof (struct vring_packed_desc ), gfp); + +return desc; +} + static inline int virtqueue_add_packed(struct virtqueue *_vq, struct scatterlist *sgs[], unsigned int total_sg,@@ -766,47 +840,449 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, void *ctx, gfp_t gfp) { +struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_packed_desc *desc; +struct scatterlist *sg; +unsigned int i, n, descs_used,uninitialized_var (prev ), err_idx; +__virtio16 uninitialized_var (head_flags ), flags; +u16 head, avail_wrap_counter, id, curr; +bool indirect; + +START_USE (vq); + +BUG_ON (data ==NULL ); +BUG_ON (ctx && vq->indirect); + +if (unlikely (vq->broken)) { +END_USE (vq); +return -EIO ; + } + +#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 + +BUG_ON (total_sg ==0 ); + + head = vq->next_avail_idx; + avail_wrap_counter = vq->avail_wrap_counter; + +if (virtqueue_use_indirect (_vq, total_sg)) + desc =alloc_indirect_packed (_vq, total_sg, gfp); +else { + desc =NULL ; +WARN_ON_ONCE (total_sg > vq->vring_packed.num && !vq->indirect); + } + +if (desc) { +/* Use a single buffer which doesn't continue */ + indirect = true; +/* Set up rest to use this indirect table. */ + i =0 ; + descs_used =1 ; + }else { + indirect = false; + desc = vq->vring_packed.desc;
drivers/virtio/virtio_ring.c static inline int virtqueue_add_packed(struct virtqueue *_vq, · line 904
Quoted 10 lines
drivers/virtio/virtio_ring.c +10
-0+ i = head; + descs_used = total_sg; + } + +if (vq->vq.num_free < descs_used) { +pr_debug ("Can't add buf len %i - avail = %i\n" , + descs_used, vq->vq.num_free); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */
I don't think we have a reason to do this for packed ring. No historical baggage there, right?
Quoted 403 lines
drivers/virtio/virtio_ring.c +350
-7+if (out_sgs) + vq->notify (&vq->vq); +if (indirect) +kfree (desc); +END_USE (vq); +return -ENOSPC ; + } + + id = vq->free_head; +BUG_ON (id == vq->vring_packed.num); + + curr = id; +for (n =0 ; n < out_sgs + in_sgs; n++) { +for (sg = sgs[n]; sg; sg =sg_next (sg)) { +dma_addr_t addr =vring_map_one_sg (vq, sg, n < out_sgs ? +DMA_TO_DEVICE :DMA_FROM_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_NEXT | + (n < out_sgs ?0 :VRING_DESC_F_WRITE ) | +_VRING_DESC_F_AVAIL (vq->avail_wrap_counter) | +_VRING_DESC_F_USED (!vq->avail_wrap_counter)); +if (!indirect && i == head) + head_flags = flags; +else + desc[i].flags = flags; + + desc[i].addr =cpu_to_virtio64 (_vq->vdev, addr); + desc[i].len =cpu_to_virtio32 (_vq->vdev, sg->length); + i++; +if (!indirect) { +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[curr].addr = addr; + vq->desc_state_packed[curr].len = + sg->length; + vq->desc_state_packed[curr].flags = +virtio16_to_cpu (_vq->vdev, + flags); + } + curr = vq->desc_state_packed[curr].next; + +if (i >= vq->vring_packed.num) { + i =0 ; + vq->avail_wrap_counter ^=1 ; + } + } + } + } + + prev = (i >0 ? i : vq->vring_packed.num) -1 ; + desc[prev].id =cpu_to_virtio16 (_vq->vdev, id); + +/* Last one doesn't continue. */ +if (total_sg ==1 ) + head_flags &=cpu_to_virtio16 (_vq->vdev, ~VRING_DESC_F_NEXT ); +else + desc[prev].flags &=cpu_to_virtio16 (_vq->vdev, + ~VRING_DESC_F_NEXT ); + +if (indirect) { +/* Now that the indirect table is filled in, map it. */ +dma_addr_t addr =vring_map_single ( + vq, desc, total_sg *sizeof (struct vring_packed_desc ), +DMA_TO_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + head_flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_INDIRECT | +_VRING_DESC_F_AVAIL (avail_wrap_counter) | +_VRING_DESC_F_USED (!avail_wrap_counter)); + vq->vring_packed.desc[head].addr =cpu_to_virtio64 (_vq->vdev, + addr); + vq->vring_packed.desc[head].len =cpu_to_virtio32 (_vq->vdev, + total_sg *sizeof (struct vring_packed_desc )); + vq->vring_packed.desc[head].id =cpu_to_virtio16 (_vq->vdev, id); + +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[id].addr = addr; + vq->desc_state_packed[id].len = total_sg * +sizeof (struct vring_packed_desc ); + vq->desc_state_packed[id].flags = +virtio16_to_cpu (_vq->vdev, head_flags); + } + } + +/* We're using some buffers from the free list. */ + vq->vq.num_free -= descs_used; + +/* Update free pointer */ +if (indirect) { + n = head +1 ; +if (n >= vq->vring_packed.num) { + n =0 ; + vq->avail_wrap_counter ^=1 ; + } + vq->next_avail_idx = n; + vq->free_head = vq->desc_state_packed[id].next; + }else { + vq->next_avail_idx = i; + vq->free_head = curr; + } + +/* Store token and indirect buffer state. */ + vq->desc_state_packed[id].num = descs_used; + vq->desc_state_packed[id].data = data; +if (indirect) + vq->desc_state_packed[id].indir_desc = desc; +else + vq->desc_state_packed[id].indir_desc = ctx; + +/* A driver MUST NOT make the first descriptor in the list +* available before all subsequent descriptors comprising +* the list are made available. */ +virtio_wmb (vq->weak_barriers); + vq->vring_packed.desc[head].flags = head_flags; + vq->num_added += descs_used; + +pr_debug ("Added buffer head %i to %p\n" , head, vq); +END_USE (vq); + +return 0 ; + +unmap_release: + err_idx = i; + i = head; + +for (n =0 ; n < total_sg; n++) { +if (i == err_idx) +break ; +vring_unmap_desc_packed (vq, &desc[i]); + i++; +if (!indirect && i >= vq->vring_packed.num) + i =0 ; + } + + vq->avail_wrap_counter = avail_wrap_counter; + +if (indirect) +kfree (desc); + +END_USE (vq);return -EIO ; }static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 flags; +bool needs_kick; +u32 snapshot; + +START_USE (vq); +/* We need to expose the new flags value before checking notification +* suppressions. */ +virtio_mb (vq->weak_barriers); + + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ; + +#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 + + needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +END_USE (vq); +return needs_kick; +} + +static void detach_buf_packed (struct vring_virtqueue *vq, +unsigned int id,void **ctx) +{ +struct vring_desc_state_packed *state =NULL ; +struct vring_packed_desc *desc; +unsigned int curr, i; + +/* Clear data ptr. */ + vq->desc_state_packed[id].data =NULL ; + + curr = id; +for (i =0 ; i < vq->desc_state_packed[id].num; i++) { + state = &vq->desc_state_packed[curr]; +vring_unmap_state_packed (vq, state); + curr = state->next; + } + +BUG_ON (state ==NULL ); + vq->vq.num_free += vq->desc_state_packed[id].num; + state->next = vq->free_head; + vq->free_head = id; + +if (vq->indirect) { +u32 len; + +/* Free the indirect table, if any, now that it's unmapped. */ + desc = vq->desc_state_packed[id].indir_desc; +if (!desc) +return ; + +if (vring_use_dma_api (vq->vq.vdev)) { + len = vq->desc_state_packed[id].len; +for (i =0 ; i < len /sizeof (struct vring_packed_desc ); + i++) +vring_unmap_desc_packed (vq, &desc[i]); + } +kfree (desc); + vq->desc_state_packed[id].indir_desc =NULL ; + }else if (ctx) { + *ctx = vq->desc_state_packed[id].indir_desc; + } +} + +static inline bool is_used_desc_packed (const struct vring_virtqueue *vq, +u16 idx,bool used_wrap_counter) +{ +u16 flags; +bool avail, used; + + flags =virtio16_to_cpu (vq->vq.vdev, + vq->vring_packed.desc[idx].flags); + avail = !!(flags &VRING_DESC_F_AVAIL ); + used = !!(flags &VRING_DESC_F_USED ); + +return avail == used && used == used_wrap_counter; }static inline bool more_used_packed (const struct vring_virtqueue *vq) { -return false; +return is_used_desc_packed (vq, vq->last_used_idx, + vq->used_wrap_counter); }static void *virtqueue_get_buf_ctx_packed (struct virtqueue *_vq,unsigned int *len,void **ctx) { -return NULL ; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 last_used, id; +void *ret; + +START_USE (vq); + +if (unlikely (vq->broken)) { +END_USE (vq); +return NULL ; + } + +if (!more_used_packed (vq)) { +pr_debug ("No more buffers in queue\n" ); +END_USE (vq); +return NULL ; + } + +/* Only get used elements after they have been exposed by host. */ +virtio_rmb (vq->weak_barriers); + + last_used = vq->last_used_idx; + id =virtio16_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].id); + *len =virtio32_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].len); + +if (unlikely (id >= vq->vring_packed.num)) { +BAD_RING (vq,"id %u out of range\n" , id); +return NULL ; + } +if (unlikely (!vq->desc_state_packed[id].data)) { +BAD_RING (vq,"id %u is not a head!\n" , id); +return NULL ; + } + + vq->last_used_idx += vq->desc_state_packed[id].num; +if (vq->last_used_idx >= vq->vring_packed.num) { + vq->last_used_idx -= vq->vring_packed.num; + vq->used_wrap_counter ^=1 ; + } + +/* detach_buf_packed clears data, so grab it now. */ + ret = vq->desc_state_packed[id].data; +detach_buf_packed (vq, id, ctx); + +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif + +END_USE (vq); +return ret; }static void virtqueue_disable_cb_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); + +if (vq->event_flags_shadow !=VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_DISABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } }static unsigned virtqueue_enable_cb_prepare_packed (struct virtqueue *_vq) { -return 0 ; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } + +END_USE (vq); +return vq->last_used_idx | ((u16 )vq->used_wrap_counter <<15 ); } -static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned last_used_idx) +static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned off_wrap) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +bool wrap_counter; +u16 used_idx; + + wrap_counter = off_wrap >>15 ; + used_idx = off_wrap & ~(1 <<15 ); + +return is_used_desc_packed (vq, used_idx, wrap_counter); }static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers); + } + +if (more_used_packed (vq)) { +END_USE (vq); +return false; + } + +END_USE (vq); +return true; }static void *virtqueue_detach_unused_buf_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); +unsigned int i; +void *buf; + +START_USE (vq); + +for (i =0 ; i < vq->vring_packed.num; i++) { +if (!vq->desc_state_packed[i].data) +continue ; +/* detach_buf clears data, so grab it now. */ + buf = vq->desc_state_packed[i].data; +detach_buf_packed (vq, i,NULL ); +END_USE (vq); +return buf; + } +/* That should have freed everything. */ +BUG_ON (vq->vq.num_free != vq->vring_packed.num); + +END_USE (vq);return NULL ; }@@ -1083,6 +1559,9 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers);return vq->packed ?virtqueue_poll_packed (_vq, last_used_idx) :virtqueue_poll_split (_vq, last_used_idx); }Signature · 1 line
2.18.0
PATCH v2 3/5 virtio_ring: add packed ring support
In reply to Tiwei BieThis commit introduces the support (without EVENT_IDX) for packed ring.
--- drivers/virtio/virtio_ring.c | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 8 deletions(-)
drivers/virtio/virtio_ring.c +487 -8
@@ -55,12 +55,21 @@#define END_USE (vq)#endif +#define _VRING_DESC_F_AVAIL (b) ((__u16)(b) << 7) +#define _VRING_DESC_F_USED (b) ((__u16)(b) << 15) +struct vring_desc_state {void *data;/* Data for callback. */ struct vring_desc *indir_desc;/* Indirect descriptor, if any. */ };struct vring_desc_state_packed { +void *data;/* Data for callback. */ +struct vring_packed_desc *indir_desc;/* Indirect descriptor, if any. */ +int num;/* Descriptor list length. */ +dma_addr_t addr;/* Buffer DMA addr. */ +u32 len;/* Buffer length. */ +u16 flags;/* Descriptor flags. */ int next;/* The next desc state. */ };
@@ -660,7 +669,6 @@ static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); -virtio_mb (vq->weak_barriers);return (u16 )last_used_idx !=virtio16_to_cpu (_vq->vdev, vq->vring.used->idx); }
@@ -757,6 +765,72 @@ static inline unsigned vring_size_packed(unsigned int num, unsigned long align) & ~(align - 1)) + sizeof(struct vring_packed_desc_event) * 2; } +static void vring_unmap_state_packed (const struct vring_virtqueue *vq, +struct vring_desc_state_packed *state) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags = state->flags; + +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), + state->addr, state->len, + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +} + +static void vring_unmap_desc_packed (const struct vring_virtqueue *vq, +struct vring_packed_desc *desc) +{ +u16 flags; + +if (!vring_use_dma_api (vq->vq.vdev)) +return ; + + flags =virtio16_to_cpu (vq->vq.vdev, desc->flags); + +if (flags &VRING_DESC_F_INDIRECT ) { +dma_unmap_single (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + }else { +dma_unmap_page (vring_dma_dev (vq), +virtio64_to_cpu (vq->vq.vdev, desc->addr), +virtio32_to_cpu (vq->vq.vdev, desc->len), + (flags &VRING_DESC_F_WRITE ) ? +DMA_FROM_DEVICE :DMA_TO_DEVICE ); + } +} + +static struct vring_packed_desc *alloc_indirect_packed (struct virtqueue *_vq, +unsigned int total_sg, +gfp_t gfp) +{ +struct vring_packed_desc *desc; + +/* +* We require lowmem mappings for the descriptors because +* otherwise virt_to_phys will give us bogus addresses in the +* virtqueue. +*/ + gfp &= ~__GFP_HIGHMEM; + + desc =kmalloc (total_sg *sizeof (struct vring_packed_desc ), gfp); + +return desc; +} + static inline int virtqueue_add_packed(struct virtqueue *_vq, struct scatterlist *sgs[], unsigned int total_sg,
@@ -766,47 +840,449 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, void *ctx, gfp_t gfp) { +struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_packed_desc *desc; +struct scatterlist *sg; +unsigned int i, n, descs_used,uninitialized_var (prev ), err_idx; +__virtio16 uninitialized_var (head_flags ), flags; +u16 head, avail_wrap_counter, id, curr; +bool indirect; + +START_USE (vq); + +BUG_ON (data ==NULL ); +BUG_ON (ctx && vq->indirect); + +if (unlikely (vq->broken)) { +END_USE (vq); +return -EIO ; + } + +#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 + +BUG_ON (total_sg ==0 ); + + head = vq->next_avail_idx; + avail_wrap_counter = vq->avail_wrap_counter; + +if (virtqueue_use_indirect (_vq, total_sg)) + desc =alloc_indirect_packed (_vq, total_sg, gfp); +else { + desc =NULL ; +WARN_ON_ONCE (total_sg > vq->vring_packed.num && !vq->indirect); + } + +if (desc) { +/* Use a single buffer which doesn't continue */ + indirect = true; +/* Set up rest to use this indirect table. */ + i =0 ; + descs_used =1 ; + }else { + indirect = false; + desc = vq->vring_packed.desc; + i = head; + descs_used = total_sg; + } + +if (vq->vq.num_free < descs_used) { +pr_debug ("Can't add buf len %i - avail = %i\n" , + descs_used, vq->vq.num_free); +/* FIXME: for historical reasons, we force a notify here if +* there are outgoing parts to the buffer. Presumably the +* host should service the ring ASAP. */ +if (out_sgs) + vq->notify (&vq->vq); +if (indirect) +kfree (desc); +END_USE (vq); +return -ENOSPC ; + } + + id = vq->free_head; +BUG_ON (id == vq->vring_packed.num); + + curr = id; +for (n =0 ; n < out_sgs + in_sgs; n++) { +for (sg = sgs[n]; sg; sg =sg_next (sg)) { +dma_addr_t addr =vring_map_one_sg (vq, sg, n < out_sgs ? +DMA_TO_DEVICE :DMA_FROM_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_NEXT | + (n < out_sgs ?0 :VRING_DESC_F_WRITE ) | +_VRING_DESC_F_AVAIL (vq->avail_wrap_counter) | +_VRING_DESC_F_USED (!vq->avail_wrap_counter)); +if (!indirect && i == head) + head_flags = flags; +else + desc[i].flags = flags; + + desc[i].addr =cpu_to_virtio64 (_vq->vdev, addr); + desc[i].len =cpu_to_virtio32 (_vq->vdev, sg->length); + i++; +if (!indirect) { +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[curr].addr = addr; + vq->desc_state_packed[curr].len = + sg->length; + vq->desc_state_packed[curr].flags = +virtio16_to_cpu (_vq->vdev, + flags); + } + curr = vq->desc_state_packed[curr].next; + +if (i >= vq->vring_packed.num) { + i =0 ; + vq->avail_wrap_counter ^=1 ; + } + } + } + } + + prev = (i >0 ? i : vq->vring_packed.num) -1 ; + desc[prev].id =cpu_to_virtio16 (_vq->vdev, id); + +/* Last one doesn't continue. */ +if (total_sg ==1 ) + head_flags &=cpu_to_virtio16 (_vq->vdev, ~VRING_DESC_F_NEXT ); +else + desc[prev].flags &=cpu_to_virtio16 (_vq->vdev, + ~VRING_DESC_F_NEXT ); + +if (indirect) { +/* Now that the indirect table is filled in, map it. */ +dma_addr_t addr =vring_map_single ( + vq, desc, total_sg *sizeof (struct vring_packed_desc ), +DMA_TO_DEVICE ); +if (vring_mapping_error (vq, addr)) + goto unmap_release; + + head_flags =cpu_to_virtio16 (_vq->vdev,VRING_DESC_F_INDIRECT | +_VRING_DESC_F_AVAIL (avail_wrap_counter) | +_VRING_DESC_F_USED (!avail_wrap_counter)); + vq->vring_packed.desc[head].addr =cpu_to_virtio64 (_vq->vdev, + addr); + vq->vring_packed.desc[head].len =cpu_to_virtio32 (_vq->vdev, + total_sg *sizeof (struct vring_packed_desc )); + vq->vring_packed.desc[head].id =cpu_to_virtio16 (_vq->vdev, id); + +if (vring_use_dma_api (_vq->vdev)) { + vq->desc_state_packed[id].addr = addr; + vq->desc_state_packed[id].len = total_sg * +sizeof (struct vring_packed_desc ); + vq->desc_state_packed[id].flags = +virtio16_to_cpu (_vq->vdev, head_flags); + } + } + +/* We're using some buffers from the free list. */ + vq->vq.num_free -= descs_used; + +/* Update free pointer */ +if (indirect) { + n = head +1 ; +if (n >= vq->vring_packed.num) { + n =0 ; + vq->avail_wrap_counter ^=1 ; + } + vq->next_avail_idx = n; + vq->free_head = vq->desc_state_packed[id].next; + }else { + vq->next_avail_idx = i; + vq->free_head = curr; + } + +/* Store token and indirect buffer state. */ + vq->desc_state_packed[id].num = descs_used; + vq->desc_state_packed[id].data = data; +if (indirect) + vq->desc_state_packed[id].indir_desc = desc; +else + vq->desc_state_packed[id].indir_desc = ctx; + +/* A driver MUST NOT make the first descriptor in the list +* available before all subsequent descriptors comprising +* the list are made available. */ +virtio_wmb (vq->weak_barriers); + vq->vring_packed.desc[head].flags = head_flags; + vq->num_added += descs_used; + +pr_debug ("Added buffer head %i to %p\n" , head, vq); +END_USE (vq); + +return 0 ; + +unmap_release: + err_idx = i; + i = head; + +for (n =0 ; n < total_sg; n++) { +if (i == err_idx) +break ; +vring_unmap_desc_packed (vq, &desc[i]); + i++; +if (!indirect && i >= vq->vring_packed.num) + i =0 ; + } + + vq->avail_wrap_counter = avail_wrap_counter; + +if (indirect) +kfree (desc); + +END_USE (vq);return -EIO ; }static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 flags; +bool needs_kick; +u32 snapshot; + +START_USE (vq); +/* We need to expose the new flags value before checking notification +* suppressions. */ +virtio_mb (vq->weak_barriers); + + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ; + +#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 + + needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +END_USE (vq); +return needs_kick; +} + +static void detach_buf_packed (struct vring_virtqueue *vq, +unsigned int id,void **ctx) +{ +struct vring_desc_state_packed *state =NULL ; +struct vring_packed_desc *desc; +unsigned int curr, i; + +/* Clear data ptr. */ + vq->desc_state_packed[id].data =NULL ; + + curr = id; +for (i =0 ; i < vq->desc_state_packed[id].num; i++) { + state = &vq->desc_state_packed[curr]; +vring_unmap_state_packed (vq, state); + curr = state->next; + } + +BUG_ON (state ==NULL ); + vq->vq.num_free += vq->desc_state_packed[id].num; + state->next = vq->free_head; + vq->free_head = id; + +if (vq->indirect) { +u32 len; + +/* Free the indirect table, if any, now that it's unmapped. */ + desc = vq->desc_state_packed[id].indir_desc; +if (!desc) +return ; + +if (vring_use_dma_api (vq->vq.vdev)) { + len = vq->desc_state_packed[id].len; +for (i =0 ; i < len /sizeof (struct vring_packed_desc ); + i++) +vring_unmap_desc_packed (vq, &desc[i]); + } +kfree (desc); + vq->desc_state_packed[id].indir_desc =NULL ; + }else if (ctx) { + *ctx = vq->desc_state_packed[id].indir_desc; + } +} + +static inline bool is_used_desc_packed (const struct vring_virtqueue *vq, +u16 idx,bool used_wrap_counter) +{ +u16 flags; +bool avail, used; + + flags =virtio16_to_cpu (vq->vq.vdev, + vq->vring_packed.desc[idx].flags); + avail = !!(flags &VRING_DESC_F_AVAIL ); + used = !!(flags &VRING_DESC_F_USED ); + +return avail == used && used == used_wrap_counter; }static inline bool more_used_packed (const struct vring_virtqueue *vq) { -return false; +return is_used_desc_packed (vq, vq->last_used_idx, + vq->used_wrap_counter); }static void *virtqueue_get_buf_ctx_packed (struct virtqueue *_vq,unsigned int *len,void **ctx) { -return NULL ; +struct vring_virtqueue *vq =to_vvq (_vq); +u16 last_used, id; +void *ret; + +START_USE (vq); + +if (unlikely (vq->broken)) { +END_USE (vq); +return NULL ; + } + +if (!more_used_packed (vq)) { +pr_debug ("No more buffers in queue\n" ); +END_USE (vq); +return NULL ; + } + +/* Only get used elements after they have been exposed by host. */ +virtio_rmb (vq->weak_barriers); + + last_used = vq->last_used_idx; + id =virtio16_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].id); + *len =virtio32_to_cpu (_vq->vdev, vq->vring_packed.desc[last_used].len); + +if (unlikely (id >= vq->vring_packed.num)) { +BAD_RING (vq,"id %u out of range\n" , id); +return NULL ; + } +if (unlikely (!vq->desc_state_packed[id].data)) { +BAD_RING (vq,"id %u is not a head!\n" , id); +return NULL ; + } + + vq->last_used_idx += vq->desc_state_packed[id].num; +if (vq->last_used_idx >= vq->vring_packed.num) { + vq->last_used_idx -= vq->vring_packed.num; + vq->used_wrap_counter ^=1 ; + } + +/* detach_buf_packed clears data, so grab it now. */ + ret = vq->desc_state_packed[id].data; +detach_buf_packed (vq, id, ctx); + +#ifdef DEBUG + vq->last_add_time_valid = false; +#endif + +END_USE (vq); +return ret; }static void virtqueue_disable_cb_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); + +if (vq->event_flags_shadow !=VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_DISABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } }static unsigned virtqueue_enable_cb_prepare_packed (struct virtqueue *_vq) { -return 0 ; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); + } + +END_USE (vq); +return vq->last_used_idx | ((u16 )vq->used_wrap_counter <<15 ); } -static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned last_used_idx) +static bool virtqueue_poll_packed (struct virtqueue *_vq,unsigned off_wrap) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); +bool wrap_counter; +u16 used_idx; + + wrap_counter = off_wrap >>15 ; + used_idx = off_wrap & ~(1 <<15 ); + +return is_used_desc_packed (vq, used_idx, wrap_counter); }static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) { -return false; +struct vring_virtqueue *vq =to_vvq (_vq); + +START_USE (vq); + +/* We optimistically turn back on interrupts, then check if there was +* more to do. */ + +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { + vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, + vq->event_flags_shadow); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers); + } + +if (more_used_packed (vq)) { +END_USE (vq); +return false; + } + +END_USE (vq); +return true; }static void *virtqueue_detach_unused_buf_packed (struct virtqueue *_vq) { +struct vring_virtqueue *vq =to_vvq (_vq); +unsigned int i; +void *buf; + +START_USE (vq); + +for (i =0 ; i < vq->vring_packed.num; i++) { +if (!vq->desc_state_packed[i].data) +continue ; +/* detach_buf clears data, so grab it now. */ + buf = vq->desc_state_packed[i].data; +detach_buf_packed (vq, i,NULL ); +END_USE (vq); +return buf; + } +/* That should have freed everything. */ +BUG_ON (vq->vq.num_free != vq->vring_packed.num); + +END_USE (vq);return NULL ; }
@@ -1083,6 +1559,9 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) {struct vring_virtqueue *vq =to_vvq (_vq); +/* We need to enable interrupts first before re-checking +* for more used buffers. */ +virtio_mb (vq->weak_barriers);return vq->packed ?virtqueue_poll_packed (_vq, last_used_idx) :virtqueue_poll_split (_vq, last_used_idx); }
Signature · 1 line
2.18.0
PATCH v2 4/5 virtio_ring: add event idx support in packed ring
In reply to Tiwei BieThis commit introduces the EVENT_IDX support in packed ring.
--- drivers/virtio/virtio_ring.c | 73 ++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 8 deletions(-)
drivers/virtio/virtio_ring.c +65 -8
@@ -1050,7 +1050,7 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq,static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 flags; +u16 new, old, off_wrap, flags, wrap_counter, event_idx;bool needs_kick;u32 snapshot;
@@ -1059,9 +1059,19 @@ static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq)* suppressions. */ virtio_mb (vq->weak_barriers); + old = vq->next_avail_idx - vq->num_added; + new = vq->next_avail_idx; + vq->num_added =0 ; + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + off_wrap =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot &0xffff )); flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ; + wrap_counter = off_wrap >>15 ; + event_idx = off_wrap & ~(1 <<15 ); +if (wrap_counter != vq->avail_wrap_counter) + event_idx -= vq->vring_packed.num; +#ifdef DEBUG if (vq->last_add_time_valid) { WARN_ON(ktime_to_ms(ktime_sub(ktime_get(),
@@ -1070,7 +1080,10 @@ static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq) vq->last_add_time_valid = false;#endif - needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +if (flags ==VRING_EVENT_F_DESC ) + needs_kick =vring_need_event (event_idx, new, old); +else + needs_kick = (flags !=VRING_EVENT_F_DISABLE );END_USE (vq);return needs_kick; }
@@ -1185,6 +1198,15 @@ static void *virtqueue_get_buf_ctx_packed(struct virtqueue *_vq, ret = vq->desc_state_packed[id].data;detach_buf_packed (vq, id, ctx); +/* If we expect an interrupt for the next entry, tell host +* by writing event index and flush out the write before +* the read in the next get_buf call. */ +if (vq->event_flags_shadow ==VRING_EVENT_F_DESC ) +virtio_store_mb (vq->weak_barriers, + &vq->vring_packed.driver->off_wrap, +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx | + ((u16 )vq->used_wrap_counter <<15 ))); +#ifdef DEBUG vq->last_add_time_valid = false;#endif
@@ -1213,8 +1235,18 @@ static unsigned virtqueue_enable_cb_prepare_packed(struct virtqueue *_vq)/* We optimistically turn back on interrupts, then check if there was * more to do. */ +if (vq->event) { + vq->vring_packed.driver->off_wrap =cpu_to_virtio16 (_vq->vdev, + vq->last_used_idx | + ((u16 )vq->used_wrap_counter <<15 )); +/* We need to update event offset and event wrap +* counter first before updating event flags. */ +virtio_wmb (vq->weak_barriers); + } +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { - vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->event_flags_shadow = vq->event ?VRING_EVENT_F_DESC : +VRING_EVENT_F_ENABLE ; vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, vq->event_flags_shadow); }
@@ -1238,22 +1270,47 @@ static bool virtqueue_poll_packed(struct virtqueue *_vq, unsigned off_wrap)static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); +u16 bufs, used_idx, wrap_counter;START_USE (vq);/* We optimistically turn back on interrupts, then check if there was * more to do. */ +if (vq->event) { +/* TODO: tune this threshold */ + bufs = (vq->vring_packed.num - _vq->num_free) *3 /4 ; + wrap_counter = vq->used_wrap_counter; + + used_idx = vq->last_used_idx + bufs; +if (used_idx >= vq->vring_packed.num) { + used_idx -= vq->vring_packed.num; + wrap_counter ^=1 ; + } + + vq->vring_packed.driver->off_wrap =cpu_to_virtio16 (_vq->vdev, + used_idx | (wrap_counter <<15 )); + +/* We need to update event offset and event wrap +* counter first before updating event flags. */ +virtio_wmb (vq->weak_barriers); + }else { + used_idx = vq->last_used_idx; + wrap_counter = vq->used_wrap_counter; + } +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { - vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->event_flags_shadow = vq->event ?VRING_EVENT_F_DESC : +VRING_EVENT_F_ENABLE ; vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, vq->event_flags_shadow); -/* We need to enable interrupts first before re-checking -* for more used buffers. */ -virtio_mb (vq->weak_barriers); } -if (more_used_packed (vq)) { +/* We need to update event suppression structure first +* before re-checking for more used buffers. */ +virtio_mb (vq->weak_barriers); + +if (is_used_desc_packed (vq, used_idx, wrap_counter)) {END_USE (vq);return false; }
Signature · 1 line
2.18.0
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 4/5] virtio_ring: add event idx support in packed ring
Quoted 3 lines · Tiwei Bie
This commit introduces the
EVENT_IDXsupport in packed ring.Signed-off-by Tiwei Bie <[email protected]>
Besides the usual comment about hard-coded constants like <<15:
does this actually do any good for performance? We don't have to if we do not want to.
Quoted 18 lines
--- drivers/virtio/virtio_ring.c | 73 ++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 8 deletions(-)drivers/virtio/virtio_ring.c +1
-1@@ -1050,7 +1050,7 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq,static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 flags; +u16 new, old, off_wrap, flags, wrap_counter, event_idx;bool needs_kick;u32 snapshot;
drivers/virtio/virtio_ring.c static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq) · line 1068
Quoted 10 lines
drivers/virtio/virtio_ring.c +5
-0@@ -1059,9 +1059,19 @@ static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq)* suppressions. */ virtio_mb (vq->weak_barriers); + old = vq->next_avail_idx - vq->num_added; + new = vq->next_avail_idx; + vq->num_added =0 ; + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + off_wrap =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot &0xffff )); flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ;
some kind of struct union would be helpful to make this readable.
Quoted 96 lines
drivers/virtio/virtio_ring.c +54
-3+ wrap_counter = off_wrap >>15 ; + event_idx = off_wrap & ~(1 <<15 ); +if (wrap_counter != vq->avail_wrap_counter) + event_idx -= vq->vring_packed.num; +#ifdef DEBUG if (vq->last_add_time_valid) { WARN_ON(ktime_to_ms(ktime_sub(ktime_get(),@@ -1070,7 +1080,10 @@ static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq) vq->last_add_time_valid = false;#endif - needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +if (flags ==VRING_EVENT_F_DESC ) + needs_kick =vring_need_event (event_idx, new, old); +else + needs_kick = (flags !=VRING_EVENT_F_DISABLE );END_USE (vq);return needs_kick; }@@ -1185,6 +1198,15 @@ static void *virtqueue_get_buf_ctx_packed(struct virtqueue *_vq, ret = vq->desc_state_packed[id].data;detach_buf_packed (vq, id, ctx); +/* If we expect an interrupt for the next entry, tell host +* by writing event index and flush out the write before +* the read in the next get_buf call. */ +if (vq->event_flags_shadow ==VRING_EVENT_F_DESC ) +virtio_store_mb (vq->weak_barriers, + &vq->vring_packed.driver->off_wrap, +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx | + ((u16 )vq->used_wrap_counter <<15 ))); +#ifdef DEBUG vq->last_add_time_valid = false;#endif @@ -1213,8 +1235,18 @@ static unsigned virtqueue_enable_cb_prepare_packed(struct virtqueue *_vq)/* We optimistically turn back on interrupts, then check if there was * more to do. */ +if (vq->event) { + vq->vring_packed.driver->off_wrap =cpu_to_virtio16 (_vq->vdev, + vq->last_used_idx | + ((u16 )vq->used_wrap_counter <<15 )); +/* We need to update event offset and event wrap +* counter first before updating event flags. */ +virtio_wmb (vq->weak_barriers); + } +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { - vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->event_flags_shadow = vq->event ?VRING_EVENT_F_DESC : +VRING_EVENT_F_ENABLE ; vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, vq->event_flags_shadow); }@@ -1238,22 +1270,47 @@ static bool virtqueue_poll_packed(struct virtqueue *_vq, unsigned off_wrap)static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); +u16 bufs, used_idx, wrap_counter;START_USE (vq);/* We optimistically turn back on interrupts, then check if there was * more to do. */ +if (vq->event) { +/* TODO: tune this threshold */ + bufs = (vq->vring_packed.num - _vq->num_free) *3 /4 ; + wrap_counter = vq->used_wrap_counter; + + used_idx = vq->last_used_idx + bufs; +if (used_idx >= vq->vring_packed.num) { + used_idx -= vq->vring_packed.num; + wrap_counter ^=1 ; + } + + vq->vring_packed.driver->off_wrap =cpu_to_virtio16 (_vq->vdev, + used_idx | (wrap_counter <<15 )); + +/* We need to update event offset and event wrap +* counter first before updating event flags. */ +virtio_wmb (vq->weak_barriers); + }else { + used_idx = vq->last_used_idx; + wrap_counter = vq->used_wrap_counter; + } +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { - vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->event_flags_shadow = vq->event ?VRING_EVENT_F_DESC : +VRING_EVENT_F_ENABLE ; vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, vq->event_flags_shadow);
drivers/virtio/virtio_ring.c static bool virtqueue_poll_packed(struct virtqueue *_vq, unsigned off_wrap) · line 1312
Quoted 10 lines
drivers/virtio/virtio_ring.c +4
-4-/* We need to enable interrupts first before re-checking -* for more used buffers. */ -virtio_mb (vq->weak_barriers); } -if (more_used_packed (vq)) { +/* We need to update event suppression structure first +* before re-checking for more used buffers. */ +virtio_mb (vq->weak_barriers); +
mb is expensive. We should not do it if we changed nothing.
Quoted 6 lines
drivers/virtio/virtio_ring.c +1
-0+if (is_used_desc_packed (vq, used_idx, wrap_counter)) {END_USE (vq);return false; }Signature · 1 line
2.18.0
Re: [virtio-dev] Re: [PATCH net-next v2 4/5] virtio_ring: add event idx support in packed ring
Quoted 8 lines · Michael S. Tsirkin
Quoted 3 lines · Tiwei Bie
This commit introduces the
EVENT_IDXsupport in packed ring.Signed-off-by Tiwei Bie <[email protected]>Besides the usual comment about hard-coded constants like <<15:
does this actually do any good for performance? We don't have to if we do not want to.
Got it. Thanks.
Quoted 22 lines
Quoted 22 lines
--- drivers/virtio/virtio_ring.c | 73 ++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 8 deletions(-)drivers/virtio/virtio_ring.c +1
-1@@ -1050,7 +1050,7 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq,static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 flags; +u16 new, old, off_wrap, flags, wrap_counter, event_idx;bool needs_kick;u32 snapshot;@@ -1059,9 +1059,19 @@ static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq)* suppressions. */ virtio_mb (vq->weak_barriers);
Quoted 10 lines
drivers/virtio/virtio_ring.c static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq) · line 1068
Quoted 7 lines
drivers/virtio/virtio_ring.c +5
-0+ old = vq->next_avail_idx - vq->num_added; + new = vq->next_avail_idx; + vq->num_added =0 ; + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + off_wrap =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot &0xffff )); flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ;some kind of struct union would be helpful to make this readable.
I will define a struct/union for this.
Quoted 102 lines
Quoted 102 lines
drivers/virtio/virtio_ring.c +54
-6+ wrap_counter = off_wrap >>15 ; + event_idx = off_wrap & ~(1 <<15 ); +if (wrap_counter != vq->avail_wrap_counter) + event_idx -= vq->vring_packed.num; +#ifdef DEBUG if (vq->last_add_time_valid) { WARN_ON(ktime_to_ms(ktime_sub(ktime_get(),@@ -1070,7 +1080,10 @@ static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq) vq->last_add_time_valid = false;#endif - needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +if (flags ==VRING_EVENT_F_DESC ) + needs_kick =vring_need_event (event_idx, new, old); +else + needs_kick = (flags !=VRING_EVENT_F_DISABLE );END_USE (vq);return needs_kick; }@@ -1185,6 +1198,15 @@ static void *virtqueue_get_buf_ctx_packed(struct virtqueue *_vq, ret = vq->desc_state_packed[id].data;detach_buf_packed (vq, id, ctx); +/* If we expect an interrupt for the next entry, tell host +* by writing event index and flush out the write before +* the read in the next get_buf call. */ +if (vq->event_flags_shadow ==VRING_EVENT_F_DESC ) +virtio_store_mb (vq->weak_barriers, + &vq->vring_packed.driver->off_wrap, +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx | + ((u16 )vq->used_wrap_counter <<15 ))); +#ifdef DEBUG vq->last_add_time_valid = false;#endif @@ -1213,8 +1235,18 @@ static unsigned virtqueue_enable_cb_prepare_packed(struct virtqueue *_vq)/* We optimistically turn back on interrupts, then check if there was * more to do. */ +if (vq->event) { + vq->vring_packed.driver->off_wrap =cpu_to_virtio16 (_vq->vdev, + vq->last_used_idx | + ((u16 )vq->used_wrap_counter <<15 )); +/* We need to update event offset and event wrap +* counter first before updating event flags. */ +virtio_wmb (vq->weak_barriers); + } +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { - vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->event_flags_shadow = vq->event ?VRING_EVENT_F_DESC : +VRING_EVENT_F_ENABLE ; vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, vq->event_flags_shadow); }@@ -1238,22 +1270,47 @@ static bool virtqueue_poll_packed(struct virtqueue *_vq, unsigned off_wrap)static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); +u16 bufs, used_idx, wrap_counter;START_USE (vq);/* We optimistically turn back on interrupts, then check if there was * more to do. */ +if (vq->event) { +/* TODO: tune this threshold */ + bufs = (vq->vring_packed.num - _vq->num_free) *3 /4 ; + wrap_counter = vq->used_wrap_counter; + + used_idx = vq->last_used_idx + bufs; +if (used_idx >= vq->vring_packed.num) { + used_idx -= vq->vring_packed.num; + wrap_counter ^=1 ; + } + + vq->vring_packed.driver->off_wrap =cpu_to_virtio16 (_vq->vdev, + used_idx | (wrap_counter <<15 )); + +/* We need to update event offset and event wrap +* counter first before updating event flags. */ +virtio_wmb (vq->weak_barriers); + }else { + used_idx = vq->last_used_idx; + wrap_counter = vq->used_wrap_counter; + } +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { - vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->event_flags_shadow = vq->event ?VRING_EVENT_F_DESC : +VRING_EVENT_F_ENABLE ; vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, vq->event_flags_shadow); -/* We need to enable interrupts first before re-checking -* for more used buffers. */ -virtio_mb (vq->weak_barriers); }
Quoted 7 lines
drivers/virtio/virtio_ring.c static bool virtqueue_poll_packed(struct virtqueue *_vq, unsigned off_wrap) · line 1312
Quoted 5 lines
drivers/virtio/virtio_ring.c +4
-1-if (more_used_packed (vq)) { +/* We need to update event suppression structure first +* before re-checking for more used buffers. */ +virtio_mb (vq->weak_barriers); +mb is expensive. We should not do it if we changed nothing.
I will try to avoid it when possible.
Quoted 12 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +1
-0+if (is_used_desc_packed (vq, used_idx, wrap_counter)) {END_USE (vq);return false; }Signature · 1 line
2.18.0---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
Re: [virtio-dev] Re: [PATCH net-next v2 4/5] virtio_ring: add event idx support in packed ring
In reply to Michael S. TsirkinQuoted 8 lines · Michael S. Tsirkin
Quoted 3 lines · Tiwei Bie
This commit introduces the
EVENT_IDXsupport in packed ring.Signed-off-by Tiwei Bie <[email protected]>Besides the usual comment about hard-coded constants like <<15:
does this actually do any good for performance? We don't have to if we do not want to.
Got it. Thanks.
Quoted 22 lines
Quoted 22 lines
--- drivers/virtio/virtio_ring.c | 73 ++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 8 deletions(-)drivers/virtio/virtio_ring.c +1
-1@@ -1050,7 +1050,7 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq,static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 flags; +u16 new, old, off_wrap, flags, wrap_counter, event_idx;bool needs_kick;u32 snapshot;@@ -1059,9 +1059,19 @@ static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq)* suppressions. */ virtio_mb (vq->weak_barriers);
Quoted 10 lines
drivers/virtio/virtio_ring.c static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq) · line 1068
Quoted 7 lines
drivers/virtio/virtio_ring.c +5
-0+ old = vq->next_avail_idx - vq->num_added; + new = vq->next_avail_idx; + vq->num_added =0 ; + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + off_wrap =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot &0xffff )); flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ;some kind of struct union would be helpful to make this readable.
I will define a struct/union for this.
Quoted 102 lines
Quoted 102 lines
drivers/virtio/virtio_ring.c +54
-6+ wrap_counter = off_wrap >>15 ; + event_idx = off_wrap & ~(1 <<15 ); +if (wrap_counter != vq->avail_wrap_counter) + event_idx -= vq->vring_packed.num; +#ifdef DEBUG if (vq->last_add_time_valid) { WARN_ON(ktime_to_ms(ktime_sub(ktime_get(),@@ -1070,7 +1080,10 @@ static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq) vq->last_add_time_valid = false;#endif - needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +if (flags ==VRING_EVENT_F_DESC ) + needs_kick =vring_need_event (event_idx, new, old); +else + needs_kick = (flags !=VRING_EVENT_F_DISABLE );END_USE (vq);return needs_kick; }@@ -1185,6 +1198,15 @@ static void *virtqueue_get_buf_ctx_packed(struct virtqueue *_vq, ret = vq->desc_state_packed[id].data;detach_buf_packed (vq, id, ctx); +/* If we expect an interrupt for the next entry, tell host +* by writing event index and flush out the write before +* the read in the next get_buf call. */ +if (vq->event_flags_shadow ==VRING_EVENT_F_DESC ) +virtio_store_mb (vq->weak_barriers, + &vq->vring_packed.driver->off_wrap, +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx | + ((u16 )vq->used_wrap_counter <<15 ))); +#ifdef DEBUG vq->last_add_time_valid = false;#endif @@ -1213,8 +1235,18 @@ static unsigned virtqueue_enable_cb_prepare_packed(struct virtqueue *_vq)/* We optimistically turn back on interrupts, then check if there was * more to do. */ +if (vq->event) { + vq->vring_packed.driver->off_wrap =cpu_to_virtio16 (_vq->vdev, + vq->last_used_idx | + ((u16 )vq->used_wrap_counter <<15 )); +/* We need to update event offset and event wrap +* counter first before updating event flags. */ +virtio_wmb (vq->weak_barriers); + } +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { - vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->event_flags_shadow = vq->event ?VRING_EVENT_F_DESC : +VRING_EVENT_F_ENABLE ; vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, vq->event_flags_shadow); }@@ -1238,22 +1270,47 @@ static bool virtqueue_poll_packed(struct virtqueue *_vq, unsigned off_wrap)static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); +u16 bufs, used_idx, wrap_counter;START_USE (vq);/* We optimistically turn back on interrupts, then check if there was * more to do. */ +if (vq->event) { +/* TODO: tune this threshold */ + bufs = (vq->vring_packed.num - _vq->num_free) *3 /4 ; + wrap_counter = vq->used_wrap_counter; + + used_idx = vq->last_used_idx + bufs; +if (used_idx >= vq->vring_packed.num) { + used_idx -= vq->vring_packed.num; + wrap_counter ^=1 ; + } + + vq->vring_packed.driver->off_wrap =cpu_to_virtio16 (_vq->vdev, + used_idx | (wrap_counter <<15 )); + +/* We need to update event offset and event wrap +* counter first before updating event flags. */ +virtio_wmb (vq->weak_barriers); + }else { + used_idx = vq->last_used_idx; + wrap_counter = vq->used_wrap_counter; + } +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { - vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->event_flags_shadow = vq->event ?VRING_EVENT_F_DESC : +VRING_EVENT_F_ENABLE ; vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, vq->event_flags_shadow); -/* We need to enable interrupts first before re-checking -* for more used buffers. */ -virtio_mb (vq->weak_barriers); }
Quoted 7 lines
drivers/virtio/virtio_ring.c static bool virtqueue_poll_packed(struct virtqueue *_vq, unsigned off_wrap) · line 1312
Quoted 5 lines
drivers/virtio/virtio_ring.c +4
-1-if (more_used_packed (vq)) { +/* We need to update event suppression structure first +* before re-checking for more used buffers. */ +virtio_mb (vq->weak_barriers); +mb is expensive. We should not do it if we changed nothing.
I will try to avoid it when possible.
Quoted 12 lines
Quoted 7 lines
drivers/virtio/virtio_ring.c +1
-0+if (is_used_desc_packed (vq, used_idx, wrap_counter)) {END_USE (vq);return false; }Signature · 1 line
2.18.0---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
Michael S. Tsirkin
maintainerRe: [PATCH net-next v2 4/5] virtio_ring: add event idx support in packed ring
In reply to Tiwei BieQuoted 3 lines · Tiwei Bie
This commit introduces the
EVENT_IDXsupport in packed ring.Signed-off-by Tiwei Bie <[email protected]>
Besides the usual comment about hard-coded constants like <<15:
does this actually do any good for performance? We don't have to if we do not want to.
Quoted 18 lines
--- drivers/virtio/virtio_ring.c | 73 ++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 8 deletions(-)drivers/virtio/virtio_ring.c +1
-1@@ -1050,7 +1050,7 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq,static bool virtqueue_kick_prepare_packed (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); -u16 flags; +u16 new, old, off_wrap, flags, wrap_counter, event_idx;bool needs_kick;u32 snapshot;
drivers/virtio/virtio_ring.c static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq) · line 1068
Quoted 10 lines
drivers/virtio/virtio_ring.c +5
-0@@ -1059,9 +1059,19 @@ static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq)* suppressions. */ virtio_mb (vq->weak_barriers); + old = vq->next_avail_idx - vq->num_added; + new = vq->next_avail_idx; + vq->num_added =0 ; + snapshot =READ_ONCE (*(u32 *)vq->vring_packed.device); + off_wrap =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot &0xffff )); flags =virtio16_to_cpu (_vq->vdev, (__virtio16)(snapshot >>16 )) &0x3 ;
some kind of struct union would be helpful to make this readable.
Quoted 96 lines
drivers/virtio/virtio_ring.c +54
-3+ wrap_counter = off_wrap >>15 ; + event_idx = off_wrap & ~(1 <<15 ); +if (wrap_counter != vq->avail_wrap_counter) + event_idx -= vq->vring_packed.num; +#ifdef DEBUG if (vq->last_add_time_valid) { WARN_ON(ktime_to_ms(ktime_sub(ktime_get(),@@ -1070,7 +1080,10 @@ static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq) vq->last_add_time_valid = false;#endif - needs_kick = (flags !=VRING_EVENT_F_DISABLE ); +if (flags ==VRING_EVENT_F_DESC ) + needs_kick =vring_need_event (event_idx, new, old); +else + needs_kick = (flags !=VRING_EVENT_F_DISABLE );END_USE (vq);return needs_kick; }@@ -1185,6 +1198,15 @@ static void *virtqueue_get_buf_ctx_packed(struct virtqueue *_vq, ret = vq->desc_state_packed[id].data;detach_buf_packed (vq, id, ctx); +/* If we expect an interrupt for the next entry, tell host +* by writing event index and flush out the write before +* the read in the next get_buf call. */ +if (vq->event_flags_shadow ==VRING_EVENT_F_DESC ) +virtio_store_mb (vq->weak_barriers, + &vq->vring_packed.driver->off_wrap, +cpu_to_virtio16 (_vq->vdev, vq->last_used_idx | + ((u16 )vq->used_wrap_counter <<15 ))); +#ifdef DEBUG vq->last_add_time_valid = false;#endif @@ -1213,8 +1235,18 @@ static unsigned virtqueue_enable_cb_prepare_packed(struct virtqueue *_vq)/* We optimistically turn back on interrupts, then check if there was * more to do. */ +if (vq->event) { + vq->vring_packed.driver->off_wrap =cpu_to_virtio16 (_vq->vdev, + vq->last_used_idx | + ((u16 )vq->used_wrap_counter <<15 )); +/* We need to update event offset and event wrap +* counter first before updating event flags. */ +virtio_wmb (vq->weak_barriers); + } +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { - vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->event_flags_shadow = vq->event ?VRING_EVENT_F_DESC : +VRING_EVENT_F_ENABLE ; vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, vq->event_flags_shadow); }@@ -1238,22 +1270,47 @@ static bool virtqueue_poll_packed(struct virtqueue *_vq, unsigned off_wrap)static bool virtqueue_enable_cb_delayed_packed (struct virtqueue *_vq) {struct vring_virtqueue *vq =to_vvq (_vq); +u16 bufs, used_idx, wrap_counter;START_USE (vq);/* We optimistically turn back on interrupts, then check if there was * more to do. */ +if (vq->event) { +/* TODO: tune this threshold */ + bufs = (vq->vring_packed.num - _vq->num_free) *3 /4 ; + wrap_counter = vq->used_wrap_counter; + + used_idx = vq->last_used_idx + bufs; +if (used_idx >= vq->vring_packed.num) { + used_idx -= vq->vring_packed.num; + wrap_counter ^=1 ; + } + + vq->vring_packed.driver->off_wrap =cpu_to_virtio16 (_vq->vdev, + used_idx | (wrap_counter <<15 )); + +/* We need to update event offset and event wrap +* counter first before updating event flags. */ +virtio_wmb (vq->weak_barriers); + }else { + used_idx = vq->last_used_idx; + wrap_counter = vq->used_wrap_counter; + } +if (vq->event_flags_shadow ==VRING_EVENT_F_DISABLE ) { - vq->event_flags_shadow =VRING_EVENT_F_ENABLE ; + vq->event_flags_shadow = vq->event ?VRING_EVENT_F_DESC : +VRING_EVENT_F_ENABLE ; vq->vring_packed.driver->flags =cpu_to_virtio16 (_vq->vdev, vq->event_flags_shadow);
drivers/virtio/virtio_ring.c static bool virtqueue_poll_packed(struct virtqueue *_vq, unsigned off_wrap) · line 1312
Quoted 10 lines
drivers/virtio/virtio_ring.c +4
-4-/* We need to enable interrupts first before re-checking -* for more used buffers. */ -virtio_mb (vq->weak_barriers); } -if (more_used_packed (vq)) { +/* We need to update event suppression structure first +* before re-checking for more used buffers. */ +virtio_mb (vq->weak_barriers); +
mb is expensive. We should not do it if we changed nothing.
Quoted 6 lines
drivers/virtio/virtio_ring.c +1
-0+if (is_used_desc_packed (vq, used_idx, wrap_counter)) {END_USE (vq);return false; }Signature · 1 line
2.18.0