Skip to content

Commit dd8ed18

Browse files
author
Sebastien Boeuf
committed
virtio-queue: Add helpers for accessing queue
These helpers are meant to help crate's consumers getting and setting information about the queue. Signed-off-by: Sebastien Boeuf <[email protected]>
1 parent c712790 commit dd8ed18

File tree

5 files changed

+127
-10
lines changed

5 files changed

+127
-10
lines changed

crates/virtio-queue/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ pub trait QueueStateT: for<'a> QueueStateGuard<'a> {
154154
/// Read the `idx` field from the available ring.
155155
fn avail_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error>;
156156

157+
/// Read the `idx` field from the used ring.
158+
fn used_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error>;
159+
157160
/// Put a used descriptor head into the used ring.
158161
fn add_used<M: GuestMemory>(&mut self, mem: &M, head_index: u16, len: u32)
159162
-> Result<(), Error>;
@@ -179,6 +182,12 @@ pub trait QueueStateT: for<'a> QueueStateGuard<'a> {
179182
/// Return the index of the next entry in the available ring.
180183
fn next_avail(&self) -> u16;
181184

185+
/// Return the index for the next descriptor in the used ring.
186+
fn next_used(&self) -> u16;
187+
182188
/// Set the index of the next entry in the available ring.
183189
fn set_next_avail(&mut self, next_avail: u16);
190+
191+
/// Set the index for the next descriptor in the used ring.
192+
fn set_next_used(&mut self, next_used: u16);
184193
}

crates/virtio-queue/src/queue.rs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,14 @@ impl<M: GuestAddressSpace, S: QueueStateT> Queue<M, S> {
197197
self.state.avail_idx(self.mem.memory().deref(), order)
198198
}
199199

200+
/// Reads the `idx` field from the used ring.
201+
///
202+
/// # Arguments
203+
/// * `order` - the memory ordering used to access the `idx` field from memory.
204+
pub fn used_idx(&self, order: Ordering) -> Result<Wrapping<u16>, Error> {
205+
self.state.used_idx(self.mem.memory().deref(), order)
206+
}
207+
200208
/// Put a used descriptor head into the used ring.
201209
///
202210
/// # Arguments
@@ -236,13 +244,26 @@ impl<M: GuestAddressSpace, S: QueueStateT> Queue<M, S> {
236244
self.state.next_avail()
237245
}
238246

247+
/// Returns the index for the next descriptor in the used ring.
248+
pub fn next_used(&self) -> u16 {
249+
self.state.next_used()
250+
}
251+
239252
/// Set the index of the next entry in the available ring.
240253
///
241254
/// # Arguments
242255
/// * `next_avail` - the index of the next available ring entry.
243256
pub fn set_next_avail(&mut self, next_avail: u16) {
244257
self.state.set_next_avail(next_avail);
245258
}
259+
260+
/// Sets the index for the next descriptor in the used ring.
261+
///
262+
/// # Arguments
263+
/// * `next_used` - the index of the next used ring entry.
264+
pub fn set_next_used(&mut self, next_used: u16) {
265+
self.state.set_next_used(next_used);
266+
}
246267
}
247268

248269
impl<M: GuestAddressSpace> Queue<M, QueueState> {
@@ -348,6 +369,7 @@ mod tests {
348369
let vq = MockSplitQueue::new(m, 16);
349370
let mut q = vq.create_queue(m);
350371

372+
assert_eq!(q.used_idx(Ordering::Acquire).unwrap(), Wrapping(0));
351373
assert_eq!(u16::from_le(vq.used().idx().load()), 0);
352374

353375
// index too large
@@ -357,6 +379,7 @@ mod tests {
357379
// should be ok
358380
q.add_used(1, 0x1000).unwrap();
359381
assert_eq!(q.state.next_used, Wrapping(1));
382+
assert_eq!(q.used_idx(Ordering::Acquire).unwrap(), Wrapping(1));
360383
assert_eq!(u16::from_le(vq.used().idx().load()), 1);
361384

362385
let x = vq.used().ring().ref_at(0).load();
@@ -377,7 +400,7 @@ mod tests {
377400
// Same for `event_idx_enabled`, `next_avail` `next_used` and `signalled_used`.
378401
q.set_event_idx(true);
379402
q.set_next_avail(2);
380-
q.add_used(1, 200).unwrap();
403+
q.set_next_used(4);
381404
q.state.signalled_used = Some(Wrapping(15));
382405
assert_eq!(q.state.size, 8);
383406
// `create_queue` also marks the queue as ready.
@@ -533,10 +556,14 @@ mod tests {
533556
i += 1;
534557
q.disable_notification().unwrap();
535558

536-
while let Some(_chain) = q.iter().unwrap().next() {
559+
while let Some(chain) = q.iter().unwrap().next() {
537560
// Here the device would consume entries from the available ring, add an entry in
538561
// the used ring and optionally notify the driver. For the purpose of this test, we
539562
// don't need to do anything with the chain, only consume it.
563+
let head_index = chain.head_index();
564+
let mut desc_len = 0;
565+
chain.for_each(|d| desc_len += d.len());
566+
q.add_used(head_index, desc_len).unwrap();
540567
}
541568
if !q.enable_notification().unwrap() {
542569
break;
@@ -547,6 +574,7 @@ mod tests {
547574
assert_eq!(i, 1);
548575
// The next chain that can be consumed should have index 2.
549576
assert_eq!(q.next_avail(), 2);
577+
assert_eq!(q.next_used(), 2);
550578
// Let the device know it can consume one more chain.
551579
vq.avail().idx().store(u16::to_le(3));
552580
i = 0;
@@ -555,8 +583,12 @@ mod tests {
555583
i += 1;
556584
q.disable_notification().unwrap();
557585

558-
while let Some(_chain) = q.iter().unwrap().next() {
586+
while let Some(chain) = q.iter().unwrap().next() {
559587
// In a real use case, we would do something with the chain here.
588+
let head_index = chain.head_index();
589+
let mut desc_len = 0;
590+
chain.for_each(|d| desc_len += d.len());
591+
q.add_used(head_index, desc_len).unwrap();
560592
}
561593

562594
// For the simplicity of the test we are updating here the `idx` value of the available
@@ -571,21 +603,27 @@ mod tests {
571603
assert_eq!(i, 2);
572604
// The next chain that can be consumed should have index 4.
573605
assert_eq!(q.next_avail(), 4);
606+
assert_eq!(q.next_used(), 4);
574607

575608
// Set an `idx` that is bigger than the number of entries added in the ring.
576609
// This is an allowed scenario, but the indexes of the chain will have unexpected values.
577610
vq.avail().idx().store(u16::to_le(7));
578611
loop {
579612
q.disable_notification().unwrap();
580613

581-
while let Some(_chain) = q.iter().unwrap().next() {
614+
while let Some(chain) = q.iter().unwrap().next() {
582615
// In a real use case, we would do something with the chain here.
616+
let head_index = chain.head_index();
617+
let mut desc_len = 0;
618+
chain.for_each(|d| desc_len += d.len());
619+
q.add_used(head_index, desc_len).unwrap();
583620
}
584621
if !q.enable_notification().unwrap() {
585622
break;
586623
}
587624
}
588625
assert_eq!(q.next_avail(), 7);
626+
assert_eq!(q.next_used(), 7);
589627
}
590628

591629
#[test]
@@ -619,14 +657,19 @@ mod tests {
619657
vq.avail().idx().store(u16::to_le(3));
620658
// No descriptor chains are consumed at this point.
621659
assert_eq!(q.next_avail(), 0);
660+
assert_eq!(q.next_used(), 0);
622661

623662
loop {
624663
q.disable_notification().unwrap();
625664

626-
while let Some(_chain) = q.iter().unwrap().next() {
665+
while let Some(chain) = q.iter().unwrap().next() {
627666
// Here the device would consume entries from the available ring, add an entry in
628667
// the used ring and optionally notify the driver. For the purpose of this test, we
629668
// don't need to do anything with the chain, only consume it.
669+
let head_index = chain.head_index();
670+
let mut desc_len = 0;
671+
chain.for_each(|d| desc_len += d.len());
672+
q.add_used(head_index, desc_len).unwrap();
630673
}
631674
if !q.enable_notification().unwrap() {
632675
break;
@@ -635,6 +678,8 @@ mod tests {
635678
// The next chain that can be consumed should have index 3.
636679
assert_eq!(q.next_avail(), 3);
637680
assert_eq!(q.avail_idx(Ordering::Acquire).unwrap(), Wrapping(3));
681+
assert_eq!(q.next_used(), 3);
682+
assert_eq!(q.used_idx(Ordering::Acquire).unwrap(), Wrapping(3));
638683
assert!(q.lock().ready());
639684

640685
// Decrement `idx` which should be forbidden. We don't enforce this thing, but we should

crates/virtio-queue/src/queue_guard.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ where
138138
self.state.avail_idx(self.mem.deref(), order)
139139
}
140140

141+
/// Read the `idx` field from the used ring.
142+
pub fn used_idx(&self, order: Ordering) -> Result<Wrapping<u16>, Error> {
143+
self.state.used_idx(self.mem.deref(), order)
144+
}
145+
141146
/// Put a used descriptor head into the used ring.
142147
pub fn add_used(&mut self, head_index: u16, len: u32) -> Result<(), Error> {
143148
self.state.add_used(self.mem.deref(), head_index, len)
@@ -172,11 +177,21 @@ where
172177
self.state.next_avail()
173178
}
174179

180+
/// Return the index of the next entry in the used ring.
181+
pub fn next_used(&self) -> u16 {
182+
self.state.next_used()
183+
}
184+
175185
/// Set the index of the next entry in the available ring.
176186
pub fn set_next_avail(&mut self, next_avail: u16) {
177187
self.state.set_next_avail(next_avail);
178188
}
179189

190+
/// Set the index of the next entry in the used ring.
191+
pub fn set_next_used(&mut self, next_used: u16) {
192+
self.state.set_next_used(next_used);
193+
}
194+
180195
/// Get a consuming iterator over all available descriptor chain heads offered by the driver.
181196
pub fn iter(&mut self) -> Result<AvailIter<'_, M>, Error> {
182197
self.state.deref_mut().iter(self.mem.clone())
@@ -223,14 +238,19 @@ mod tests {
223238
vq.avail().idx().store(3);
224239
// No descriptor chains are consumed at this point.
225240
assert_eq!(g.next_avail(), 0);
241+
assert_eq!(g.next_used(), 0);
226242

227243
loop {
228244
g.disable_notification().unwrap();
229245

230-
while let Some(_chain) = g.iter().unwrap().next() {
246+
while let Some(chain) = g.iter().unwrap().next() {
231247
// Here the device would consume entries from the available ring, add an entry in
232248
// the used ring and optionally notify the driver. For the purpose of this test, we
233249
// don't need to do anything with the chain, only consume it.
250+
let head_index = chain.head_index();
251+
let mut desc_len = 0;
252+
chain.for_each(|d| desc_len += d.len());
253+
g.add_used(head_index, desc_len).unwrap();
234254
}
235255
if !g.enable_notification().unwrap() {
236256
break;
@@ -239,6 +259,8 @@ mod tests {
239259
// The next chain that can be consumed should have index 3.
240260
assert_eq!(g.next_avail(), 3);
241261
assert_eq!(g.avail_idx(Ordering::Acquire).unwrap(), Wrapping(3));
262+
assert_eq!(g.next_used(), 3);
263+
assert_eq!(g.used_idx(Ordering::Acquire).unwrap(), Wrapping(3));
242264
assert!(g.ready());
243265

244266
// Decrement `idx` which should be forbidden. We don't enforce this thing, but we should

crates/virtio-queue/src/state.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,17 @@ impl QueueStateT for QueueState {
311311
.map_err(Error::GuestMemory)
312312
}
313313

314+
fn used_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> {
315+
let addr = self
316+
.used_ring
317+
.checked_add(2)
318+
.ok_or(Error::AddressOverflow)?;
319+
320+
mem.load(addr, order)
321+
.map(Wrapping)
322+
.map_err(Error::GuestMemory)
323+
}
324+
314325
fn add_used<M: GuestMemory>(
315326
&mut self,
316327
mem: &M,
@@ -415,7 +426,15 @@ impl QueueStateT for QueueState {
415426
self.next_avail.0
416427
}
417428

429+
fn next_used(&self) -> u16 {
430+
self.next_used.0
431+
}
432+
418433
fn set_next_avail(&mut self, next_avail: u16) {
419434
self.next_avail = Wrapping(next_avail);
420435
}
436+
437+
fn set_next_used(&mut self, next_used: u16) {
438+
self.next_used = Wrapping(next_used);
439+
}
421440
}

crates/virtio-queue/src/state_sync.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ impl QueueStateT for QueueStateSync {
107107
self.lock_state().avail_idx(mem, order)
108108
}
109109

110+
fn used_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> {
111+
self.lock_state().used_idx(mem, order)
112+
}
113+
110114
fn add_used<M: GuestMemory>(
111115
&mut self,
112116
mem: &M,
@@ -132,9 +136,17 @@ impl QueueStateT for QueueStateSync {
132136
self.lock_state().next_avail()
133137
}
134138

139+
fn next_used(&self) -> u16 {
140+
self.lock_state().next_used()
141+
}
142+
135143
fn set_next_avail(&mut self, next_avail: u16) {
136144
self.lock_state().set_next_avail(next_avail);
137145
}
146+
147+
fn set_next_used(&mut self, next_used: u16) {
148+
self.lock_state().set_next_used(next_used);
149+
}
138150
}
139151

140152
#[cfg(test)]
@@ -202,20 +214,30 @@ mod tests {
202214
assert_eq!(q.max_size(), 0x100);
203215
q.set_next_avail(5);
204216
assert_eq!(q.next_avail(), 5);
217+
q.set_next_used(3);
218+
assert_eq!(q.next_used(), 3);
205219
assert_eq!(
206220
q.avail_idx(m.memory(), Ordering::Acquire).unwrap(),
207221
Wrapping(0)
208222
);
223+
assert_eq!(
224+
q.used_idx(m.memory(), Ordering::Acquire).unwrap(),
225+
Wrapping(0)
226+
);
209227

210-
assert_eq!(q.lock_state().next_used, Wrapping(0));
228+
assert_eq!(q.next_used(), 3);
211229

212230
// index too large
213231
assert!(q.add_used(m.memory(), 0x200, 0x1000).is_err());
214-
assert_eq!(q.lock_state().next_used, Wrapping(0));
232+
assert_eq!(q.next_used(), 3);
215233

216234
// should be ok
217235
q.add_used(m.memory(), 1, 0x1000).unwrap();
218-
assert_eq!(q.lock_state().next_used, Wrapping(1));
236+
assert_eq!(q.next_used(), 4);
237+
assert_eq!(
238+
q.used_idx(m.memory(), Ordering::Acquire).unwrap(),
239+
Wrapping(4)
240+
);
219241
}
220242

221243
#[test]
@@ -228,11 +250,11 @@ mod tests {
228250
q.set_used_ring_address(Some(0x3000), None);
229251
q.set_event_idx(true);
230252
q.set_next_avail(2);
253+
q.set_next_used(2);
231254
q.set_size(0x8);
232255
q.set_ready(true);
233256
assert!(q.is_valid(m.memory()));
234257

235-
q.add_used(m.memory(), 1, 0x100).unwrap();
236258
q.needs_notification(m.memory()).unwrap();
237259

238260
assert_eq!(q.lock_state().size, 0x8);

0 commit comments

Comments
 (0)