@@ -6,7 +6,6 @@ use std::os::fd::AsRawFd;
66use libc:: { c_int, c_void, iovec, off_t, size_t} ;
77use memfd;
88
9- use super :: queue:: FIRECRACKER_MAX_QUEUE_SIZE ;
109use crate :: arch:: PAGE_SIZE ;
1110
1211#[ derive( Debug , thiserror:: Error , displaydoc:: Display ) ]
@@ -69,26 +68,42 @@ pub enum IovDequeError {
6968//
7069// Like that, the elements stored in the buffer are always laid out in contiguous virtual memory,
7170// so making a slice out of them does not require any copies.
71+ //
72+ // The `L` const generic determines the maximum number of `iovec` elements the queue should hold.
73+ //
74+ // ```Rust
75+ // pub struct iovec {
76+ // pub iov_base: *mut ::c_void,
77+ // pub iov_len: ::size_t,
78+ // }
79+ // ```
80+ //
81+ // This value must be a multiple of 256 because this is the maximum number of `iovec` can fit into
82+ // 1 memory page: 256 * sizeof(iovec) == 4096 == PAGE_SIZE. IovDeque only operates on `PAGE_SIZE`
83+ // granularity.
7284#[ derive( Debug ) ]
73- pub struct IovDeque {
85+ pub struct IovDeque < const L : u16 > {
7486 pub iov : * mut libc:: iovec ,
7587 pub start : u16 ,
7688 pub len : u16 ,
7789}
7890
7991// SAFETY: This is `Send`. We hold sole ownership of the underlying buffer.
80- unsafe impl Send for IovDeque { }
92+ unsafe impl < const L : u16 > Send for IovDeque < L > { }
93+
94+ impl < const L : u16 > IovDeque < L > {
95+ const BYTES : usize = L as usize * std:: mem:: size_of :: < iovec > ( ) ;
96+ const _ASSERT: ( ) = assert ! ( Self :: BYTES % PAGE_SIZE == 0 ) ;
8197
82- impl IovDeque {
8398 /// Create a [`memfd`] object that represents a single physical page
8499 fn create_memfd ( ) -> Result < memfd:: Memfd , IovDequeError > {
85100 // Create a sealable memfd.
86101 let opts = memfd:: MemfdOptions :: default ( ) . allow_sealing ( true ) ;
87- let mfd = opts. create ( "sized-1K " ) ?;
102+ let mfd = opts. create ( "iov_deque " ) ?;
88103
89104 // Resize to system page size.
90105 mfd. as_file ( )
91- . set_len ( PAGE_SIZE . try_into ( ) . unwrap ( ) )
106+ . set_len ( Self :: BYTES . try_into ( ) . unwrap ( ) )
92107 . map_err ( IovDequeError :: MemfdResize ) ?;
93108
94109 // Add seals to prevent further resizing.
@@ -121,35 +136,13 @@ impl IovDeque {
121136
122137 /// Allocate memory for our ring buffer
123138 ///
124- /// This will allocate exactly two pages of virtual memory. In order to implement the
125- /// optimization that allows us to always have elements in contiguous memory we need
126- /// allocations at the granularity of `PAGE_SIZE`. Now, our queues are at maximum 256
127- /// descriptors long and `struct iovec` looks like this:
128- ///
129- /// ```Rust
130- /// pub struct iovec {
131- /// pub iov_base: *mut ::c_void,
132- /// pub iov_len: ::size_t,
133- /// }
134- /// ```
135- ///
136- /// so, it's 16 bytes long. As a result, we need a single page for holding the actual data of
137- /// our buffer.
139+ /// This will allocate 2 * `Self::BYTES` bytes in virtual memory.
138140 fn allocate_ring_buffer_memory ( ) -> Result < * mut c_void , IovDequeError > {
139- // The fact that we allocate two pages is due to the size of `struct iovec` times our queue
140- // size equals the page size. Add here a debug assertion to reflect that and ensure that we
141- // will adapt our logic if the assumption changes in the future.
142- const {
143- assert ! (
144- std:: mem:: size_of:: <iovec>( ) * FIRECRACKER_MAX_QUEUE_SIZE as usize == PAGE_SIZE
145- ) ;
146- }
147-
148141 // SAFETY: We are calling the system call with valid arguments
149142 unsafe {
150143 Self :: mmap (
151144 std:: ptr:: null_mut ( ) ,
152- PAGE_SIZE * 2 ,
145+ Self :: BYTES * 2 ,
153146 libc:: PROT_NONE ,
154147 libc:: MAP_PRIVATE | libc:: MAP_ANONYMOUS ,
155148 -1 ,
@@ -169,7 +162,7 @@ impl IovDeque {
169162 let _ = unsafe {
170163 Self :: mmap (
171164 buffer,
172- PAGE_SIZE ,
165+ Self :: BYTES ,
173166 libc:: PROT_READ | libc:: PROT_WRITE ,
174167 libc:: MAP_SHARED | libc:: MAP_FIXED ,
175168 raw_memfd,
@@ -180,19 +173,17 @@ impl IovDeque {
180173 // Map the second page of virtual memory to the physical page described by the memfd object
181174 //
182175 // SAFETY: This is safe because:
183- // * Both `buffer` and the result of `buffer.add(PAGE_SIZE )` are within bounds of the
176+ // * Both `buffer` and the result of `buffer.add(Self::BYTES )` are within bounds of the
184177 // allocation we got from `Self::allocate_ring_buffer_memory`.
185- // * The computed offset is `PAGE_SIZE * size_of::<c_void>() == PAGE_SIZE bytes` which fits
186- // in `isize`
187178 // * The resulting pointer is the beginning of the second page of our allocation, so it
188179 // doesn't wrap around the address space.
189- let next_page = unsafe { buffer. add ( PAGE_SIZE ) } ;
180+ let next_page = unsafe { buffer. add ( Self :: BYTES ) } ;
190181
191182 // SAFETY: We are calling the system call with valid arguments
192183 let _ = unsafe {
193184 Self :: mmap (
194185 next_page,
195- PAGE_SIZE ,
186+ Self :: BYTES ,
196187 libc:: PROT_READ | libc:: PROT_WRITE ,
197188 libc:: MAP_SHARED | libc:: MAP_FIXED ,
198189 raw_memfd,
@@ -216,7 +207,7 @@ impl IovDeque {
216207 /// Returns `true` if the [`IovDeque`] is full, `false` otherwise
217208 #[ inline( always) ]
218209 pub fn is_full ( & self ) -> bool {
219- self . len ( ) == FIRECRACKER_MAX_QUEUE_SIZE
210+ self . len ( ) == L
220211 }
221212
222213 /// Resets the queue, dropping all its elements.
@@ -261,8 +252,8 @@ impl IovDeque {
261252
262253 self . start += nr_iovecs;
263254 self . len -= nr_iovecs;
264- if self . start >= FIRECRACKER_MAX_QUEUE_SIZE {
265- self . start -= FIRECRACKER_MAX_QUEUE_SIZE ;
255+ if self . start >= L {
256+ self . start -= L ;
266257 }
267258 }
268259
@@ -318,11 +309,11 @@ impl IovDeque {
318309 }
319310}
320311
321- impl Drop for IovDeque {
312+ impl < const L : u16 > Drop for IovDeque < L > {
322313 fn drop ( & mut self ) {
323314 // SAFETY: We are passing an address that we got from a previous allocation of `2 *
324- // PAGE_SIZE ` bytes by calling mmap
325- let _ = unsafe { libc:: munmap ( self . iov . cast ( ) , PAGE_SIZE * 2 ) } ;
315+ // Self::BYTES ` bytes by calling mmap
316+ let _ = unsafe { libc:: munmap ( self . iov . cast ( ) , Self :: BYTES * 2 ) } ;
326317 }
327318}
328319
0 commit comments