1- use std:: alloc:: { self , Layout } ;
1+ use std:: alloc:: Layout ;
22
33use rustc_index:: bit_set:: DenseBitSet ;
44
@@ -11,7 +11,7 @@ const COMPRESSION_FACTOR: usize = 4;
1111pub struct IsolatedAlloc {
1212 /// Pointers to page-aligned memory that has been claimed by the allocator.
1313 /// 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.
1515 page_ptrs : Vec < * mut u8 > ,
1616 /// Metadata about which bytes have been allocated on each page. The length
1717 /// of this vector must be the same as that of `page_ptrs`, and the domain
@@ -52,20 +52,26 @@ impl IsolatedAlloc {
5252 Layout :: from_size_align ( size, align) . unwrap ( )
5353 }
5454
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.
5657 #[ 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 {
6459 // 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 ) ;
6661 // 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 )
6975 }
7076
7177 /// Determined whether a given normalized (size, align) should be sent to
@@ -78,15 +84,15 @@ impl IsolatedAlloc {
7884 /// Allocates memory as described in `Layout`. This memory should be deallocated
7985 /// by calling `dealloc` on this same allocator.
8086 ///
81- /// SAFETY: See `alloc::alloc()`
87+ /// SAFETY: See `alloc::alloc()`.
8288 pub unsafe fn alloc ( & mut self , layout : Layout ) -> * mut u8 {
8389 // SAFETY: Upheld by caller
8490 unsafe { self . allocate ( layout, false ) }
8591 }
8692
8793 /// Same as `alloc`, but zeroes out the memory.
8894 ///
89- /// SAFETY: See `alloc::alloc_zeroed()`
95+ /// SAFETY: See `alloc::alloc_zeroed()`.
9096 pub unsafe fn alloc_zeroed ( & mut self , layout : Layout ) -> * mut u8 {
9197 // SAFETY: Upheld by caller
9298 unsafe { self . allocate ( layout, true ) }
@@ -95,14 +101,13 @@ impl IsolatedAlloc {
95101 /// Abstracts over the logic of `alloc_zeroed` vs `alloc`, as determined by
96102 /// the `zeroed` argument.
97103 ///
98- /// SAFETY: See `alloc::alloc()`, with the added restriction that `page_size`
99- /// corresponds to the host pagesize.
104+ /// SAFETY: See `alloc::alloc()`.
100105 unsafe fn allocate ( & mut self , layout : Layout , zeroed : bool ) -> * mut u8 {
101106 let layout = IsolatedAlloc :: normalized_layout ( layout) ;
102107 if self . is_huge_alloc ( & layout) {
103108 // SAFETY: Validity of `layout` upheld by caller; we checked that
104109 // the size and alignment are appropriate for being a huge alloc
105- unsafe { self . alloc_huge ( layout, zeroed ) }
110+ unsafe { self . alloc_huge ( layout) }
106111 } else {
107112 for ( & mut page, pinfo) in std:: iter:: zip ( & mut self . page_ptrs , & mut self . page_infos ) {
108113 // SAFETY: The value in `self.page_size` is used to allocate
@@ -171,8 +176,19 @@ impl IsolatedAlloc {
171176
172177 /// Expands the available memory pool by adding one page.
173178 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" ) ;
176192 // `page_infos` has to have one bit for each `COMPRESSION_FACTOR`-sized chunk of bytes in the page.
177193 assert ! ( self . page_size % COMPRESSION_FACTOR == 0 ) ;
178194 self . page_infos . push ( DenseBitSet :: new_empty ( self . page_size / COMPRESSION_FACTOR ) ) ;
@@ -181,15 +197,28 @@ impl IsolatedAlloc {
181197 }
182198
183199 /// Allocates in multiples of one page on the host system.
200+ /// Will always be zeroed.
184201 ///
185202 /// 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 ( ) ) )
193222 }
194223
195224 /// Deallocates a pointer from this allocator.
@@ -218,15 +247,15 @@ impl IsolatedAlloc {
218247 let page_ptr = self . page_ptrs . remove ( idx) ;
219248 // SAFETY: We checked that there are no outstanding allocations
220249 // 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.
222251 unsafe {
223- alloc :: dealloc ( page_ptr, self . page_layout ( ) ) ;
252+ assert_eq ! ( libc :: munmap ( page_ptr. cast ( ) , self . page_size ) , 0 ) ;
224253 }
225254 }
226255 }
227256 }
228257
229- /// Returns the index of the page that this was deallocated from
258+ /// Returns the index of the page that this was deallocated from.
230259 ///
231260 /// SAFETY: the pointer must have been allocated with `alloc_small`.
232261 unsafe fn dealloc_small ( & mut self , ptr : * mut u8 , layout : Layout ) -> usize {
@@ -255,18 +284,22 @@ impl IsolatedAlloc {
255284 /// SAFETY: Same as `dealloc()` with the added requirement that `layout`
256285 /// must ask for a size larger than the host pagesize.
257286 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`.
260289 let idx = self
261290 . huge_ptrs
262291 . 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+ } )
264295 . expect ( "Freeing unallocated pages" ) ;
265296 // 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.
268300 unsafe {
269- alloc:: dealloc ( ptr, layout) ;
301+ let ret = libc:: munmap ( un_offset_ptr. cast ( ) , size) ;
302+ assert_eq ! ( ret, 0 ) ;
270303 }
271304 }
272305}
@@ -350,12 +383,15 @@ mod tests {
350383 sizes. append ( & mut vec ! [ 256 ; 12 ] ) ;
351384 // Give it some multi-page ones too
352385 sizes. append ( & mut vec ! [ 32 * 1024 ; 4 ] ) ;
386+ sizes. push ( 4 * 1024 ) ;
353387
354388 // Matching aligns for the sizes
355389 let mut aligns = vec ! [ 16 ; 12 ] ;
356390 aligns. append ( & mut vec ! [ 256 ; 2 ] ) ;
357391 aligns. append ( & mut vec ! [ 64 ; 12 ] ) ;
358392 aligns. append ( & mut vec ! [ 4096 ; 4 ] ) ;
393+ // And one that requests align > page_size
394+ aligns. push ( 64 * 1024 ) ;
359395
360396 // Make sure we didn't mess up in the test itself!
361397 assert_eq ! ( sizes. len( ) , aligns. len( ) ) ;
0 commit comments