used_ring.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package virtqueue
  2. import (
  3. "fmt"
  4. "unsafe"
  5. )
  6. // usedRingFlag is a flag that describes a [UsedRing].
  7. type usedRingFlag uint16
  8. const (
  9. // usedRingFlagNoNotify is used by the host to advise the guest to not
  10. // kick it when adding a buffer. It's unreliable, so it's simply an
  11. // optimization. Guest will still kick when it's out of buffers.
  12. usedRingFlagNoNotify usedRingFlag = 1 << iota
  13. )
  14. // usedRingSize is the number of bytes needed to store a [UsedRing] with the
  15. // given queue size in memory.
  16. func usedRingSize(queueSize int) int {
  17. return 6 + usedElementSize*queueSize
  18. }
  19. // usedRingAlignment is the minimum alignment of a [UsedRing] in memory, as
  20. // required by the virtio spec.
  21. const usedRingAlignment = 4
  22. // UsedRing is where the device returns descriptor chains once it is done with
  23. // them. Each ring entry is a [UsedElement]. It is only written to by the device
  24. // and read by the driver.
  25. //
  26. // Because the size of the ring depends on the queue size, we cannot define a
  27. // Go struct with a static size that maps to the memory of the ring. Instead,
  28. // this struct only contains pointers to the corresponding memory areas.
  29. type UsedRing struct {
  30. initialized bool
  31. // flags that describe this ring.
  32. flags *usedRingFlag
  33. // ringIndex indicates where the device would put the next entry into the
  34. // ring (modulo the queue size).
  35. ringIndex *uint16
  36. // ring contains the [UsedElement]s. It wraps around at queue size.
  37. ring []UsedElement
  38. // availableEvent is not used by this implementation, but we reserve it
  39. // anyway to avoid issues in case a device may try to write to it, contrary
  40. // to the virtio specification.
  41. availableEvent *uint16
  42. // lastIndex is the internal ringIndex up to which all [UsedElement]s were
  43. // processed.
  44. lastIndex uint16
  45. //mu sync.Mutex
  46. }
  47. // newUsedRing creates a used ring that uses the given underlying memory. The
  48. // length of the memory slice must match the size needed for the ring (see
  49. // [usedRingSize]) for the given queue size.
  50. func newUsedRing(queueSize int, mem []byte) *UsedRing {
  51. ringSize := usedRingSize(queueSize)
  52. if len(mem) != ringSize {
  53. panic(fmt.Sprintf("memory size (%v) does not match required size "+
  54. "for used ring: %v", len(mem), ringSize))
  55. }
  56. r := UsedRing{
  57. initialized: true,
  58. flags: (*usedRingFlag)(unsafe.Pointer(&mem[0])),
  59. ringIndex: (*uint16)(unsafe.Pointer(&mem[2])),
  60. ring: unsafe.Slice((*UsedElement)(unsafe.Pointer(&mem[4])), queueSize),
  61. availableEvent: (*uint16)(unsafe.Pointer(&mem[ringSize-2])),
  62. }
  63. r.lastIndex = *r.ringIndex
  64. return &r
  65. }
  66. // Address returns the pointer to the beginning of the ring in memory.
  67. // Do not modify the memory directly to not interfere with this implementation.
  68. func (r *UsedRing) Address() uintptr {
  69. if !r.initialized {
  70. panic("used ring is not initialized")
  71. }
  72. return uintptr(unsafe.Pointer(r.flags))
  73. }
  74. func (r *UsedRing) availableToTake() int {
  75. ringIndex := *r.ringIndex
  76. if ringIndex == r.lastIndex {
  77. // Nothing new.
  78. return 0
  79. }
  80. // Calculate the number new used elements that we can read from the ring.
  81. // The ring index may wrap, so special handling for that case is needed.
  82. count := int(ringIndex - r.lastIndex)
  83. if count < 0 {
  84. count += 0xffff
  85. }
  86. return count
  87. }
  88. // take returns all new [UsedElement]s that the device put into the ring and
  89. // that weren't already returned by a previous call to this method.
  90. func (r *UsedRing) take(maxToTake int) (int, []UsedElement) {
  91. count := r.availableToTake()
  92. if count == 0 {
  93. return 0, nil
  94. }
  95. stillNeedToTake := 0
  96. if maxToTake > 0 {
  97. stillNeedToTake = count - maxToTake
  98. if stillNeedToTake < 0 {
  99. stillNeedToTake = 0
  100. }
  101. count = min(count, maxToTake)
  102. }
  103. // The number of new elements can never exceed the queue size.
  104. if count > len(r.ring) {
  105. panic("used ring contains more new elements than the ring is long")
  106. }
  107. elems := make([]UsedElement, count)
  108. for i := range count {
  109. elems[i] = r.ring[r.lastIndex%uint16(len(r.ring))]
  110. r.lastIndex++
  111. }
  112. return stillNeedToTake, elems
  113. }
  114. func (r *UsedRing) takeOne() (UsedElement, bool) {
  115. //r.mu.Lock()
  116. //defer r.mu.Unlock()
  117. count := r.availableToTake()
  118. if count == 0 {
  119. return UsedElement{}, false
  120. }
  121. // The number of new elements can never exceed the queue size.
  122. if count > len(r.ring) {
  123. panic("used ring contains more new elements than the ring is long")
  124. }
  125. out := r.ring[r.lastIndex%uint16(len(r.ring))]
  126. r.lastIndex++
  127. return out, true
  128. }
  129. // InitOfferSingle is only used to pre-fill the used queue at startup, and should not be used if the device is running!
  130. func (r *UsedRing) InitOfferSingle(x uint16, size uint32) {
  131. offset := 0
  132. // Add descriptor chain heads to the ring.
  133. // The 16-bit ring index may overflow. This is expected and is not an
  134. // issue because the size of the ring array (which equals the queue
  135. // size) is always a power of 2 and smaller than the highest possible
  136. // 16-bit value.
  137. insertIndex := int(*r.ringIndex+uint16(offset)) % len(r.ring)
  138. r.ring[insertIndex].DescriptorIndex = uint32(x)
  139. r.ring[insertIndex].Length = size
  140. // Increase the ring index by the number of descriptor chains added to the ring.
  141. *r.ringIndex += 1
  142. }