@@ -77,10 +77,7 @@ pub enum IovDequeError {
77
77
// pub iov_len: ::size_t,
78
78
// }
79
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 == HOST_PAGE_SIZE. IovDeque only operates with
83
- // `HOST_PAGE_SIZE` granularity.
80
+
84
81
#[ derive( Debug ) ]
85
82
pub struct IovDeque < const L : u16 > {
86
83
pub iov : * mut libc:: iovec ,
@@ -92,17 +89,15 @@ pub struct IovDeque<const L: u16> {
92
89
unsafe impl < const L : u16 > Send for IovDeque < L > { }
93
90
94
91
impl < const L : u16 > IovDeque < L > {
95
- const BYTES : usize = L as usize * std:: mem:: size_of :: < iovec > ( ) ;
96
-
97
92
/// Create a [`memfd`] object that represents a single physical page
98
- fn create_memfd ( ) -> Result < memfd:: Memfd , IovDequeError > {
93
+ fn create_memfd ( pages_bytes : usize ) -> Result < memfd:: Memfd , IovDequeError > {
99
94
// Create a sealable memfd.
100
95
let opts = memfd:: MemfdOptions :: default ( ) . allow_sealing ( true ) ;
101
96
let mfd = opts. create ( "iov_deque" ) ?;
102
97
103
98
// Resize to system page size.
104
99
mfd. as_file ( )
105
- . set_len ( Self :: BYTES . try_into ( ) . unwrap ( ) )
100
+ . set_len ( pages_bytes . try_into ( ) . unwrap ( ) )
106
101
. map_err ( IovDequeError :: MemfdResize ) ?;
107
102
108
103
// Add seals to prevent further resizing.
@@ -135,13 +130,13 @@ impl<const L: u16> IovDeque<L> {
135
130
136
131
/// Allocate memory for our ring buffer
137
132
///
138
- /// This will allocate 2 * `Self::BYTES ` bytes of virtual memory.
139
- fn allocate_ring_buffer_memory ( ) -> Result < * mut c_void , IovDequeError > {
133
+ /// This will allocate 2 * `pages_bytes ` bytes of virtual memory.
134
+ fn allocate_ring_buffer_memory ( pages_bytes : usize ) -> Result < * mut c_void , IovDequeError > {
140
135
// SAFETY: We are calling the system call with valid arguments
141
136
unsafe {
142
137
Self :: mmap (
143
138
std:: ptr:: null_mut ( ) ,
144
- Self :: BYTES * 2 ,
139
+ pages_bytes * 2 ,
145
140
libc:: PROT_NONE ,
146
141
libc:: MAP_PRIVATE | libc:: MAP_ANONYMOUS ,
147
142
-1 ,
@@ -150,20 +145,29 @@ impl<const L: u16> IovDeque<L> {
150
145
}
151
146
}
152
147
148
+ /// Calculate a number of bytes in full pages required for
149
+ /// the type to operate.
150
+ fn pages_bytes ( ) -> usize {
151
+ let host_page_size = host_page_size ( ) ;
152
+ let bytes = L as usize * std:: mem:: size_of :: < iovec > ( ) ;
153
+ let num_host_pages = bytes. div_ceil ( host_page_size) ;
154
+ num_host_pages * host_page_size
155
+ }
156
+
153
157
/// Create a new [`IovDeque`] that can hold memory described by a single VirtIO queue.
154
158
pub fn new ( ) -> Result < Self , IovDequeError > {
155
- assert ! ( Self :: BYTES % host_page_size ( ) == 0 ) ;
159
+ let pages_bytes = Self :: pages_bytes ( ) ;
156
160
157
- let memfd = Self :: create_memfd ( ) ?;
161
+ let memfd = Self :: create_memfd ( pages_bytes ) ?;
158
162
let raw_memfd = memfd. as_file ( ) . as_raw_fd ( ) ;
159
- let buffer = Self :: allocate_ring_buffer_memory ( ) ?;
163
+ let buffer = Self :: allocate_ring_buffer_memory ( pages_bytes ) ?;
160
164
161
165
// Map the first page of virtual memory to the physical page described by the memfd object
162
166
// SAFETY: We are calling the system call with valid arguments
163
167
let _ = unsafe {
164
168
Self :: mmap (
165
169
buffer,
166
- Self :: BYTES ,
170
+ pages_bytes ,
167
171
libc:: PROT_READ | libc:: PROT_WRITE ,
168
172
libc:: MAP_SHARED | libc:: MAP_FIXED ,
169
173
raw_memfd,
@@ -174,17 +178,17 @@ impl<const L: u16> IovDeque<L> {
174
178
// Map the second page of virtual memory to the physical page described by the memfd object
175
179
//
176
180
// SAFETY: This is safe because:
177
- // * Both `buffer` and the result of `buffer.add(Self::BYTES )` are within bounds of the
181
+ // * Both `buffer` and the result of `buffer.add(pages_bytes )` are within bounds of the
178
182
// allocation we got from `Self::allocate_ring_buffer_memory`.
179
183
// * The resulting pointer is the beginning of the second page of our allocation, so it
180
184
// doesn't wrap around the address space.
181
- let next_page = unsafe { buffer. add ( Self :: BYTES ) } ;
185
+ let next_page = unsafe { buffer. add ( pages_bytes ) } ;
182
186
183
187
// SAFETY: We are calling the system call with valid arguments
184
188
let _ = unsafe {
185
189
Self :: mmap (
186
190
next_page,
187
- Self :: BYTES ,
191
+ pages_bytes ,
188
192
libc:: PROT_READ | libc:: PROT_WRITE ,
189
193
libc:: MAP_SHARED | libc:: MAP_FIXED ,
190
194
raw_memfd,
@@ -312,9 +316,10 @@ impl<const L: u16> IovDeque<L> {
312
316
313
317
impl < const L : u16 > Drop for IovDeque < L > {
314
318
fn drop ( & mut self ) {
319
+ let pages_bytes = Self :: pages_bytes ( ) ;
315
320
// SAFETY: We are passing an address that we got from a previous allocation of `2 *
316
- // Self::BYTES` bytes by calling mmap
317
- let _ = unsafe { libc:: munmap ( self . iov . cast ( ) , Self :: BYTES * 2 ) } ;
321
+ // pages_bytes` by calling mmap
322
+ let _ = unsafe { libc:: munmap ( self . iov . cast ( ) , 2 * pages_bytes ) } ;
318
323
}
319
324
}
320
325
@@ -332,6 +337,18 @@ mod tests {
332
337
assert_eq ! ( deque. len( ) , 0 ) ;
333
338
}
334
339
340
+ #[ test]
341
+ fn test_new_less_than_page ( ) {
342
+ let deque = super :: IovDeque :: < 128 > :: new ( ) . unwrap ( ) ;
343
+ assert_eq ! ( deque. len( ) , 0 ) ;
344
+ }
345
+
346
+ #[ test]
347
+ fn test_new_more_than_page ( ) {
348
+ let deque = super :: IovDeque :: < 512 > :: new ( ) . unwrap ( ) ;
349
+ assert_eq ! ( deque. len( ) , 0 ) ;
350
+ }
351
+
335
352
fn make_iovec ( id : u16 , len : u16 ) -> iovec {
336
353
iovec {
337
354
iov_base : id as * mut libc:: c_void ,
0 commit comments