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