descriptor_table.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. package virtqueue
  2. import (
  3. "errors"
  4. "fmt"
  5. "math"
  6. "unsafe"
  7. "golang.org/x/sys/unix"
  8. )
  9. var (
  10. // ErrNotEnoughFreeDescriptors is returned when the free descriptors are
  11. // exhausted, meaning that the queue is full.
  12. ErrNotEnoughFreeDescriptors = errors.New("not enough free descriptors, queue is full")
  13. // ErrInvalidDescriptorChain is returned when a descriptor chain is not
  14. // valid for a given operation.
  15. ErrInvalidDescriptorChain = errors.New("invalid descriptor chain")
  16. )
  17. // noFreeHead is used to mark when all descriptors are in use and we have no
  18. // free chain. This value is impossible to occur as an index naturally, because
  19. // it exceeds the maximum queue size.
  20. const noFreeHead = uint16(math.MaxUint16)
  21. // descriptorTableSize is the number of bytes needed to store a
  22. // [DescriptorTable] with the given queue size in memory.
  23. func descriptorTableSize(queueSize int) int {
  24. return descriptorSize * queueSize
  25. }
  26. // descriptorTableAlignment is the minimum alignment of a [DescriptorTable]
  27. // in memory, as required by the virtio spec.
  28. const descriptorTableAlignment = 16
  29. // DescriptorTable is a table that holds [Descriptor]s, addressed via their
  30. // index in the slice.
  31. type DescriptorTable struct {
  32. descriptors []Descriptor
  33. // freeHeadIndex is the index of the head of the descriptor chain which
  34. // contains all currently unused descriptors. When all descriptors are in
  35. // use, this has the special value of noFreeHead.
  36. freeHeadIndex uint16
  37. // freeNum tracks the number of descriptors which are currently not in use.
  38. freeNum uint16
  39. bufferBase uintptr
  40. bufferSize int
  41. itemSize int
  42. }
  43. // newDescriptorTable creates a descriptor table that uses the given underlying
  44. // memory. The Length of the memory slice must match the size needed for the
  45. // descriptor table (see [descriptorTableSize]) for the given queue size.
  46. //
  47. // Before this descriptor table can be used, [initialize] must be called.
  48. func newDescriptorTable(queueSize int, mem []byte, itemSize int) *DescriptorTable {
  49. dtSize := descriptorTableSize(queueSize)
  50. if len(mem) != dtSize {
  51. panic(fmt.Sprintf("memory size (%v) does not match required size "+
  52. "for descriptor table: %v", len(mem), dtSize))
  53. }
  54. return &DescriptorTable{
  55. descriptors: unsafe.Slice((*Descriptor)(unsafe.Pointer(&mem[0])), queueSize),
  56. // We have no free descriptors until they were initialized.
  57. freeHeadIndex: noFreeHead,
  58. freeNum: 0,
  59. itemSize: itemSize, //todo configurable? needs to be page-aligned
  60. }
  61. }
  62. // Address returns the pointer to the beginning of the descriptor table in
  63. // memory. Do not modify the memory directly to not interfere with this
  64. // implementation.
  65. func (dt *DescriptorTable) Address() uintptr {
  66. if dt.descriptors == nil {
  67. panic("descriptor table is not initialized")
  68. }
  69. //should be same as dt.bufferBase
  70. return uintptr(unsafe.Pointer(&dt.descriptors[0]))
  71. }
  72. func (dt *DescriptorTable) Size() uintptr {
  73. if dt.descriptors == nil {
  74. panic("descriptor table is not initialized")
  75. }
  76. return uintptr(dt.bufferSize)
  77. }
  78. // BufferAddresses returns a map of pointer->size for all allocations used by the table
  79. func (dt *DescriptorTable) BufferAddresses() map[uintptr]int {
  80. if dt.descriptors == nil {
  81. panic("descriptor table is not initialized")
  82. }
  83. return map[uintptr]int{dt.bufferBase: dt.bufferSize}
  84. }
  85. // initializeDescriptors allocates buffers with the size of a full memory page
  86. // for each descriptor in the table. While this may be a bit wasteful, it makes
  87. // dealing with descriptors way easier. Without this preallocation, we would
  88. // have to allocate and free memory on demand, increasing complexity.
  89. //
  90. // All descriptors will be marked as free and will form a free chain. The
  91. // addresses of all descriptors will be populated while their length remains
  92. // zero.
  93. func (dt *DescriptorTable) initializeDescriptors() error {
  94. numDescriptors := len(dt.descriptors)
  95. // Allocate ONE large region for all buffers
  96. totalSize := dt.itemSize * numDescriptors
  97. basePtr, err := unix.MmapPtr(-1, 0, nil, uintptr(totalSize),
  98. unix.PROT_READ|unix.PROT_WRITE,
  99. unix.MAP_PRIVATE|unix.MAP_ANONYMOUS)
  100. if err != nil {
  101. return fmt.Errorf("allocate buffer memory for descriptors: %w", err)
  102. }
  103. // Store the base for cleanup later
  104. dt.bufferBase = uintptr(basePtr)
  105. dt.bufferSize = totalSize
  106. for i := range dt.descriptors {
  107. dt.descriptors[i] = Descriptor{
  108. address: dt.bufferBase + uintptr(i*dt.itemSize),
  109. length: 0,
  110. // All descriptors should form a free chain that loops around.
  111. flags: descriptorFlagHasNext,
  112. next: uint16((i + 1) % len(dt.descriptors)),
  113. }
  114. }
  115. // All descriptors are free to use now.
  116. dt.freeHeadIndex = 0
  117. dt.freeNum = uint16(len(dt.descriptors))
  118. return nil
  119. }
  120. // releaseBuffers releases all allocated buffers for this descriptor table.
  121. // The implementation will try to release as many buffers as possible and
  122. // collect potential errors before returning them.
  123. // The descriptor table should no longer be used after calling this.
  124. func (dt *DescriptorTable) releaseBuffers() error {
  125. for i := range dt.descriptors {
  126. descriptor := &dt.descriptors[i]
  127. descriptor.address = 0
  128. }
  129. // As a safety measure, make sure no descriptors can be used anymore.
  130. dt.freeHeadIndex = noFreeHead
  131. dt.freeNum = 0
  132. if dt.bufferBase != 0 {
  133. // The pointer points to memory not managed by Go, so this conversion
  134. // is safe. See https://github.com/golang/go/issues/58625
  135. dt.bufferBase = 0
  136. //goland:noinspection GoVetUnsafePointer
  137. err := unix.MunmapPtr(unsafe.Pointer(dt.bufferBase), uintptr(dt.bufferSize))
  138. if err != nil {
  139. return fmt.Errorf("release buffer memory: %w", err)
  140. }
  141. }
  142. return nil
  143. }
  144. func (dt *DescriptorTable) CreateDescriptorForOutputs() (uint16, uint32, error) {
  145. //todo just fill the damn table
  146. // Do we still have enough free descriptors?
  147. if 1 > dt.freeNum {
  148. return 0, 0, ErrNotEnoughFreeDescriptors
  149. }
  150. // Above validation ensured that there is at least one free descriptor, so
  151. // the free descriptor chain head should be valid.
  152. if dt.freeHeadIndex == noFreeHead {
  153. panic("free descriptor chain head is unset but there should be free descriptors")
  154. }
  155. // To avoid having to iterate over the whole table to find the descriptor
  156. // pointing to the head just to replace the free head, we instead always
  157. // create descriptor chains from the descriptors coming after the head.
  158. // This way we only have to touch the head as a last resort, when all other
  159. // descriptors are already used.
  160. head := dt.descriptors[dt.freeHeadIndex].next
  161. desc := &dt.descriptors[head]
  162. next := desc.next
  163. checkUnusedDescriptorLength(head, desc)
  164. // Give the device the maximum available number of bytes to write into.
  165. desc.length = uint32(dt.itemSize)
  166. desc.flags = 0 // descriptorFlagWritable
  167. desc.next = 0 // Not necessary to clear this, it's just for looks.
  168. dt.freeNum -= 1
  169. if dt.freeNum == 0 {
  170. // The last descriptor in the chain should be the free chain head
  171. // itself.
  172. if next != dt.freeHeadIndex {
  173. panic("descriptor chain takes up all free descriptors but does not end with the free chain head")
  174. }
  175. // When this new chain takes up all remaining descriptors, we no longer
  176. // have a free chain.
  177. dt.freeHeadIndex = noFreeHead
  178. } else {
  179. // We took some descriptors out of the free chain, so make sure to close
  180. // the circle again.
  181. dt.descriptors[dt.freeHeadIndex].next = next
  182. }
  183. return head, desc.length, nil
  184. }
  185. func (dt *DescriptorTable) createDescriptorForInputs() (uint16, error) {
  186. // Do we still have enough free descriptors?
  187. if 1 > dt.freeNum {
  188. return 0, ErrNotEnoughFreeDescriptors
  189. }
  190. // Above validation ensured that there is at least one free descriptor, so
  191. // the free descriptor chain head should be valid.
  192. if dt.freeHeadIndex == noFreeHead {
  193. panic("free descriptor chain head is unset but there should be free descriptors")
  194. }
  195. // To avoid having to iterate over the whole table to find the descriptor
  196. // pointing to the head just to replace the free head, we instead always
  197. // create descriptor chains from the descriptors coming after the head.
  198. // This way we only have to touch the head as a last resort, when all other
  199. // descriptors are already used.
  200. head := dt.descriptors[dt.freeHeadIndex].next
  201. desc := &dt.descriptors[head]
  202. next := desc.next
  203. checkUnusedDescriptorLength(head, desc)
  204. // Give the device the maximum available number of bytes to write into.
  205. desc.length = uint32(dt.itemSize)
  206. desc.flags = descriptorFlagWritable
  207. desc.next = 0 // Not necessary to clear this, it's just for looks.
  208. dt.freeNum -= 1
  209. if dt.freeNum == 0 {
  210. // The last descriptor in the chain should be the free chain head
  211. // itself.
  212. if next != dt.freeHeadIndex {
  213. panic("descriptor chain takes up all free descriptors but does not end with the free chain head")
  214. }
  215. // When this new chain takes up all remaining descriptors, we no longer
  216. // have a free chain.
  217. dt.freeHeadIndex = noFreeHead
  218. } else {
  219. // We took some descriptors out of the free chain, so make sure to close
  220. // the circle again.
  221. dt.descriptors[dt.freeHeadIndex].next = next
  222. }
  223. return head, nil
  224. }
  225. func (dt *DescriptorTable) getDescriptorItem(head uint16) []byte {
  226. desc := &dt.descriptors[head] //todo this is a pretty nasty hack with no checks
  227. // The descriptor address points to memory not managed by Go, so this
  228. // conversion is safe. See https://github.com/golang/go/issues/58625
  229. //goland:noinspection GoVetUnsafePointer
  230. return unsafe.Slice((*byte)(unsafe.Pointer(desc.address)), desc.length)
  231. }
  232. // checkUnusedDescriptorLength asserts that the length of an unused descriptor
  233. // is zero, as it should be.
  234. // This is not a requirement by the virtio spec but rather a thing we do to
  235. // notice when our algorithm goes sideways.
  236. func checkUnusedDescriptorLength(index uint16, desc *Descriptor) {
  237. if desc.length != 0 {
  238. panic(fmt.Sprintf("descriptor %d should be unused but has a non-zero length", index))
  239. }
  240. }