1
- use std:: alloc:: { self , Layout } ;
1
+ use std:: alloc:: Layout ;
2
2
3
3
use rustc_index:: bit_set:: DenseBitSet ;
4
4
@@ -11,7 +11,7 @@ const COMPRESSION_FACTOR: usize = 4;
11
11
pub struct IsolatedAlloc {
12
12
/// Pointers to page-aligned memory that has been claimed by the allocator.
13
13
/// Every pointer here must point to a page-sized allocation claimed via
14
- /// the global allocator . These pointers are used for "small" allocations.
14
+ /// mmap . These pointers are used for "small" allocations.
15
15
page_ptrs : Vec < * mut u8 > ,
16
16
/// Metadata about which bytes have been allocated on each page. The length
17
17
/// of this vector must be the same as that of `page_ptrs`, and the domain
@@ -52,20 +52,26 @@ impl IsolatedAlloc {
52
52
Layout :: from_size_align ( size, align) . unwrap ( )
53
53
}
54
54
55
- /// Returns the layout used to allocate the pages that hold small allocations.
55
+ /// For greater-than-page-sized allocations, returns the allocation size we need to request
56
+ /// including the slack we need to satisfy the alignment request.
56
57
#[ inline]
57
- fn page_layout ( & self ) -> Layout {
58
- Layout :: from_size_align ( self . page_size , self . page_size ) . unwrap ( )
59
- }
60
-
61
- /// If the allocation is greater than a page, then round to the nearest page #.
62
- #[ inline]
63
- fn huge_normalized_layout ( layout : Layout , page_size : usize ) -> Layout {
58
+ fn huge_normalized_layout ( & self , layout : Layout ) -> usize {
64
59
// Allocate in page-sized chunks
65
- let size = layout. size ( ) . next_multiple_of ( page_size) ;
60
+ let size = layout. size ( ) . next_multiple_of ( self . page_size ) ;
66
61
// And make sure the align is at least one page
67
- let align = std:: cmp:: max ( layout. align ( ) , page_size) ;
68
- Layout :: from_size_align ( size, align) . unwrap ( )
62
+ let align = std:: cmp:: max ( layout. align ( ) , self . page_size ) ;
63
+ // pg_count gives us the # of pages needed to satisfy the size. For
64
+ // align > page_size where align = n * page_size, a sufficently-aligned
65
+ // address must exist somewhere in the range of
66
+ // some_page_aligned_address..some_page_aligned_address + (n-1) * page_size
67
+ // (since if some_page_aligned_address + n * page_size is sufficently aligned,
68
+ // then so is some_page_aligned_address itself per the definition of n, so we
69
+ // can avoid using that 1 extra page).
70
+ // Thus we allocate n-1 extra pages
71
+ let pg_count = size. div_ceil ( self . page_size ) ;
72
+ let extra_pages = align. strict_div ( self . page_size ) . saturating_sub ( 1 ) ;
73
+
74
+ pg_count. strict_add ( extra_pages) . strict_mul ( self . page_size )
69
75
}
70
76
71
77
/// Determined whether a given normalized (size, align) should be sent to
@@ -78,15 +84,15 @@ impl IsolatedAlloc {
78
84
/// Allocates memory as described in `Layout`. This memory should be deallocated
79
85
/// by calling `dealloc` on this same allocator.
80
86
///
81
- /// SAFETY: See `alloc::alloc()`
87
+ /// SAFETY: See `alloc::alloc()`.
82
88
pub unsafe fn alloc ( & mut self , layout : Layout ) -> * mut u8 {
83
89
// SAFETY: Upheld by caller
84
90
unsafe { self . allocate ( layout, false ) }
85
91
}
86
92
87
93
/// Same as `alloc`, but zeroes out the memory.
88
94
///
89
- /// SAFETY: See `alloc::alloc_zeroed()`
95
+ /// SAFETY: See `alloc::alloc_zeroed()`.
90
96
pub unsafe fn alloc_zeroed ( & mut self , layout : Layout ) -> * mut u8 {
91
97
// SAFETY: Upheld by caller
92
98
unsafe { self . allocate ( layout, true ) }
@@ -95,14 +101,13 @@ impl IsolatedAlloc {
95
101
/// Abstracts over the logic of `alloc_zeroed` vs `alloc`, as determined by
96
102
/// the `zeroed` argument.
97
103
///
98
- /// SAFETY: See `alloc::alloc()`, with the added restriction that `page_size`
99
- /// corresponds to the host pagesize.
104
+ /// SAFETY: See `alloc::alloc()`.
100
105
unsafe fn allocate ( & mut self , layout : Layout , zeroed : bool ) -> * mut u8 {
101
106
let layout = IsolatedAlloc :: normalized_layout ( layout) ;
102
107
if self . is_huge_alloc ( & layout) {
103
108
// SAFETY: Validity of `layout` upheld by caller; we checked that
104
109
// the size and alignment are appropriate for being a huge alloc
105
- unsafe { self . alloc_huge ( layout, zeroed ) }
110
+ unsafe { self . alloc_huge ( layout) }
106
111
} else {
107
112
for ( & mut page, pinfo) in std:: iter:: zip ( & mut self . page_ptrs , & mut self . page_infos ) {
108
113
// SAFETY: The value in `self.page_size` is used to allocate
@@ -171,8 +176,19 @@ impl IsolatedAlloc {
171
176
172
177
/// Expands the available memory pool by adding one page.
173
178
fn add_page ( & mut self ) -> ( * mut u8 , & mut DenseBitSet < usize > ) {
174
- // SAFETY: The system page size, which is the layout size, cannot be 0
175
- let page_ptr = unsafe { alloc:: alloc ( self . page_layout ( ) ) } ;
179
+ // SAFETY: mmap is always safe to call when requesting anonymous memory
180
+ let page_ptr = unsafe {
181
+ libc:: mmap (
182
+ std:: ptr:: null_mut ( ) ,
183
+ self . page_size ,
184
+ libc:: PROT_READ | libc:: PROT_WRITE ,
185
+ libc:: MAP_PRIVATE | libc:: MAP_ANONYMOUS ,
186
+ -1 ,
187
+ 0 ,
188
+ )
189
+ . cast :: < u8 > ( )
190
+ } ;
191
+ assert_ne ! ( page_ptr. addr( ) , usize :: MAX , "mmap failed" ) ;
176
192
// `page_infos` has to have one bit for each `COMPRESSION_FACTOR`-sized chunk of bytes in the page.
177
193
assert ! ( self . page_size % COMPRESSION_FACTOR == 0 ) ;
178
194
self . page_infos . push ( DenseBitSet :: new_empty ( self . page_size / COMPRESSION_FACTOR ) ) ;
@@ -181,15 +197,28 @@ impl IsolatedAlloc {
181
197
}
182
198
183
199
/// Allocates in multiples of one page on the host system.
200
+ /// Will always be zeroed.
184
201
///
185
202
/// SAFETY: Same as `alloc()`.
186
- unsafe fn alloc_huge ( & mut self , layout : Layout , zeroed : bool ) -> * mut u8 {
187
- let layout = IsolatedAlloc :: huge_normalized_layout ( layout, self . page_size ) ;
188
- // SAFETY: Upheld by caller
189
- let ret =
190
- unsafe { if zeroed { alloc:: alloc_zeroed ( layout) } else { alloc:: alloc ( layout) } } ;
191
- self . huge_ptrs . push ( ( ret, layout. size ( ) ) ) ;
192
- ret
203
+ unsafe fn alloc_huge ( & mut self , layout : Layout ) -> * mut u8 {
204
+ let size = self . huge_normalized_layout ( layout) ;
205
+ // SAFETY: mmap is always safe to call when requesting anonymous memory
206
+ let ret = unsafe {
207
+ libc:: mmap (
208
+ std:: ptr:: null_mut ( ) ,
209
+ size,
210
+ libc:: PROT_READ | libc:: PROT_WRITE ,
211
+ libc:: MAP_PRIVATE | libc:: MAP_ANONYMOUS ,
212
+ -1 ,
213
+ 0 ,
214
+ )
215
+ . cast :: < u8 > ( )
216
+ } ;
217
+ assert_ne ! ( ret. addr( ) , usize :: MAX , "mmap failed" ) ;
218
+ self . huge_ptrs . push ( ( ret, size) ) ;
219
+ // huge_normalized_layout ensures that we've overallocated enough space
220
+ // for this to be valid.
221
+ ret. map_addr ( |a| a. next_multiple_of ( layout. align ( ) ) )
193
222
}
194
223
195
224
/// Deallocates a pointer from this allocator.
@@ -218,15 +247,15 @@ impl IsolatedAlloc {
218
247
let page_ptr = self . page_ptrs . remove ( idx) ;
219
248
// SAFETY: We checked that there are no outstanding allocations
220
249
// from us pointing to this page, and we know it was allocated
221
- // with this layout
250
+ // in add_page as exactly a single page.
222
251
unsafe {
223
- alloc :: dealloc ( page_ptr, self . page_layout ( ) ) ;
252
+ assert_eq ! ( libc :: munmap ( page_ptr. cast ( ) , self . page_size ) , 0 ) ;
224
253
}
225
254
}
226
255
}
227
256
}
228
257
229
- /// Returns the index of the page that this was deallocated from
258
+ /// Returns the index of the page that this was deallocated from.
230
259
///
231
260
/// SAFETY: the pointer must have been allocated with `alloc_small`.
232
261
unsafe fn dealloc_small ( & mut self , ptr : * mut u8 , layout : Layout ) -> usize {
@@ -255,18 +284,22 @@ impl IsolatedAlloc {
255
284
/// SAFETY: Same as `dealloc()` with the added requirement that `layout`
256
285
/// must ask for a size larger than the host pagesize.
257
286
unsafe fn dealloc_huge ( & mut self , ptr : * mut u8 , layout : Layout ) {
258
- let layout = IsolatedAlloc :: huge_normalized_layout ( layout, self . page_size ) ;
259
- // Find the pointer matching in address with the one we got
287
+ let size = self . huge_normalized_layout ( layout) ;
288
+ // Find the huge allocation containing `ptr`.
260
289
let idx = self
261
290
. huge_ptrs
262
291
. iter ( )
263
- . position ( |pg| ptr. addr ( ) == pg. 0 . addr ( ) )
292
+ . position ( |& ( pg, size) | {
293
+ pg. addr ( ) <= ptr. addr ( ) && ptr. addr ( ) < pg. addr ( ) . strict_add ( size)
294
+ } )
264
295
. expect ( "Freeing unallocated pages" ) ;
265
296
// And kick it from the list
266
- self . huge_ptrs . remove ( idx) ;
267
- // SAFETY: Caller ensures validity of the layout
297
+ let ( un_offset_ptr, size2) = self . huge_ptrs . remove ( idx) ;
298
+ assert_eq ! ( size, size2, "got wrong layout in dealloc" ) ;
299
+ // SAFETY: huge_ptrs contains allocations made with mmap with the size recorded there.
268
300
unsafe {
269
- alloc:: dealloc ( ptr, layout) ;
301
+ let ret = libc:: munmap ( un_offset_ptr. cast ( ) , size) ;
302
+ assert_eq ! ( ret, 0 ) ;
270
303
}
271
304
}
272
305
}
@@ -350,12 +383,15 @@ mod tests {
350
383
sizes. append ( & mut vec ! [ 256 ; 12 ] ) ;
351
384
// Give it some multi-page ones too
352
385
sizes. append ( & mut vec ! [ 32 * 1024 ; 4 ] ) ;
386
+ sizes. push ( 4 * 1024 ) ;
353
387
354
388
// Matching aligns for the sizes
355
389
let mut aligns = vec ! [ 16 ; 12 ] ;
356
390
aligns. append ( & mut vec ! [ 256 ; 2 ] ) ;
357
391
aligns. append ( & mut vec ! [ 64 ; 12 ] ) ;
358
392
aligns. append ( & mut vec ! [ 4096 ; 4 ] ) ;
393
+ // And one that requests align > page_size
394
+ aligns. push ( 64 * 1024 ) ;
359
395
360
396
// Make sure we didn't mess up in the test itself!
361
397
assert_eq ! ( sizes. len( ) , aligns. len( ) ) ;
0 commit comments