1 // SPDX-License-Identifier: GPL-2.0 2 3 // Copyright (C) 2024 Google LLC. 4 5 use kernel::{ 6 page::{PAGE_MASK, PAGE_SIZE}, 7 prelude::*, 8 seq_file::SeqFile, 9 seq_print, 10 task::Pid, 11 }; 12 13 use crate::range_alloc::{DescriptorState, FreedRange, Range}; 14 15 /// Keeps track of allocations in a process' mmap. 16 /// 17 /// Each process has an mmap where the data for incoming transactions will be placed. This struct 18 /// keeps track of allocations made in the mmap. For each allocation, we store a descriptor that 19 /// has metadata related to the allocation. We also keep track of available free space. 20 pub(super) struct ArrayRangeAllocator<T> { 21 /// This stores all ranges that are allocated. Unlike the tree based allocator, we do *not* 22 /// store the free ranges. 23 /// 24 /// Sorted by offset. 25 pub(super) ranges: KVec<Range<T>>, 26 size: usize, 27 free_oneway_space: usize, 28 } 29 30 struct FindEmptyRes { 31 /// Which index in `ranges` should we insert the new range at? 32 /// 33 /// Inserting the new range at this index keeps `ranges` sorted. 34 insert_at_idx: usize, 35 /// Which offset should we insert the new range at? 36 insert_at_offset: usize, 37 } 38 39 impl<T> ArrayRangeAllocator<T> { new(size: usize, alloc: EmptyArrayAlloc<T>) -> Self40 pub(crate) fn new(size: usize, alloc: EmptyArrayAlloc<T>) -> Self { 41 Self { 42 ranges: alloc.ranges, 43 size, 44 free_oneway_space: size / 2, 45 } 46 } 47 free_oneway_space(&self) -> usize48 pub(crate) fn free_oneway_space(&self) -> usize { 49 self.free_oneway_space 50 } 51 count_buffers(&self) -> usize52 pub(crate) fn count_buffers(&self) -> usize { 53 self.ranges.len() 54 } 55 total_size(&self) -> usize56 pub(crate) fn total_size(&self) -> usize { 57 self.size 58 } 59 is_full(&self) -> bool60 pub(crate) fn is_full(&self) -> bool { 61 self.ranges.len() == self.ranges.capacity() 62 } 63 debug_print(&self, m: &SeqFile) -> Result<()>64 pub(crate) fn debug_print(&self, m: &SeqFile) -> Result<()> { 65 for range in &self.ranges { 66 seq_print!( 67 m, 68 " buffer {}: {} size {} pid {} oneway {}", 69 0, 70 range.offset, 71 range.size, 72 range.state.pid(), 73 range.state.is_oneway(), 74 ); 75 if let DescriptorState::Reserved(_) = range.state { 76 seq_print!(m, " reserved\n"); 77 } else { 78 seq_print!(m, " allocated\n"); 79 } 80 } 81 Ok(()) 82 } 83 84 /// Find somewhere to put a new range. 85 /// 86 /// Unlike the tree implementation, we do not bother to find the smallest gap. The idea is that 87 /// fragmentation isn't a big issue when we don't have many ranges. 88 /// 89 /// Returns the index that the new range should have in `self.ranges` after insertion. find_empty_range(&self, size: usize) -> Option<FindEmptyRes>90 fn find_empty_range(&self, size: usize) -> Option<FindEmptyRes> { 91 let after_last_range = self.ranges.last().map(Range::endpoint).unwrap_or(0); 92 93 if size <= self.total_size() - after_last_range { 94 // We can put the range at the end, so just do that. 95 Some(FindEmptyRes { 96 insert_at_idx: self.ranges.len(), 97 insert_at_offset: after_last_range, 98 }) 99 } else { 100 let mut end_of_prev = 0; 101 for (i, range) in self.ranges.iter().enumerate() { 102 // Does it fit before the i'th range? 103 if size <= range.offset - end_of_prev { 104 return Some(FindEmptyRes { 105 insert_at_idx: i, 106 insert_at_offset: end_of_prev, 107 }); 108 } 109 end_of_prev = range.endpoint(); 110 } 111 None 112 } 113 } 114 reserve_new( &mut self, debug_id: usize, size: usize, is_oneway: bool, pid: Pid, ) -> Result<usize>115 pub(crate) fn reserve_new( 116 &mut self, 117 debug_id: usize, 118 size: usize, 119 is_oneway: bool, 120 pid: Pid, 121 ) -> Result<usize> { 122 // Compute new value of free_oneway_space, which is set only on success. 123 let new_oneway_space = if is_oneway { 124 match self.free_oneway_space.checked_sub(size) { 125 Some(new_oneway_space) => new_oneway_space, 126 None => return Err(ENOSPC), 127 } 128 } else { 129 self.free_oneway_space 130 }; 131 132 let FindEmptyRes { 133 insert_at_idx, 134 insert_at_offset, 135 } = self.find_empty_range(size).ok_or(ENOSPC)?; 136 self.free_oneway_space = new_oneway_space; 137 138 let new_range = Range { 139 offset: insert_at_offset, 140 size, 141 state: DescriptorState::new(is_oneway, debug_id, pid), 142 }; 143 // Insert the value at the given index to keep the array sorted. 144 self.ranges 145 .insert_within_capacity(insert_at_idx, new_range) 146 .ok() 147 .unwrap(); 148 149 Ok(insert_at_offset) 150 } 151 reservation_abort(&mut self, offset: usize) -> Result<FreedRange>152 pub(crate) fn reservation_abort(&mut self, offset: usize) -> Result<FreedRange> { 153 // This could use a binary search, but linear scans are usually faster for small arrays. 154 let i = self 155 .ranges 156 .iter() 157 .position(|range| range.offset == offset) 158 .ok_or(EINVAL)?; 159 let range = &self.ranges[i]; 160 161 if let DescriptorState::Allocated(_) = range.state { 162 return Err(EPERM); 163 } 164 165 let size = range.size; 166 let offset = range.offset; 167 168 if range.state.is_oneway() { 169 self.free_oneway_space += size; 170 } 171 172 // This computes the range of pages that are no longer used by *any* allocated range. The 173 // caller will mark them as unused, which means that they can be freed if the system comes 174 // under memory pressure. 175 let mut freed_range = FreedRange::interior_pages(offset, size); 176 if offset % PAGE_SIZE != 0 { 177 if i == 0 || self.ranges[i - 1].endpoint() <= (offset & PAGE_MASK) { 178 freed_range.start_page_idx -= 1; 179 } 180 } 181 if range.endpoint() % PAGE_SIZE != 0 { 182 let page_after = (range.endpoint() & PAGE_MASK) + PAGE_SIZE; 183 if i + 1 == self.ranges.len() || page_after <= self.ranges[i + 1].offset { 184 freed_range.end_page_idx += 1; 185 } 186 } 187 188 self.ranges.remove(i)?; 189 Ok(freed_range) 190 } 191 reservation_commit(&mut self, offset: usize, data: &mut Option<T>) -> Result192 pub(crate) fn reservation_commit(&mut self, offset: usize, data: &mut Option<T>) -> Result { 193 // This could use a binary search, but linear scans are usually faster for small arrays. 194 let range = self 195 .ranges 196 .iter_mut() 197 .find(|range| range.offset == offset) 198 .ok_or(ENOENT)?; 199 200 let DescriptorState::Reserved(reservation) = &range.state else { 201 return Err(ENOENT); 202 }; 203 204 range.state = DescriptorState::Allocated(reservation.clone().allocate(data.take())); 205 Ok(()) 206 } 207 reserve_existing(&mut self, offset: usize) -> Result<(usize, usize, Option<T>)>208 pub(crate) fn reserve_existing(&mut self, offset: usize) -> Result<(usize, usize, Option<T>)> { 209 // This could use a binary search, but linear scans are usually faster for small arrays. 210 let range = self 211 .ranges 212 .iter_mut() 213 .find(|range| range.offset == offset) 214 .ok_or(ENOENT)?; 215 216 let DescriptorState::Allocated(allocation) = &mut range.state else { 217 return Err(ENOENT); 218 }; 219 220 let data = allocation.take(); 221 let debug_id = allocation.reservation.debug_id; 222 range.state = DescriptorState::Reserved(allocation.reservation.clone()); 223 Ok((range.size, debug_id, data)) 224 } 225 take_for_each<F: Fn(usize, usize, usize, Option<T>)>(&mut self, callback: F)226 pub(crate) fn take_for_each<F: Fn(usize, usize, usize, Option<T>)>(&mut self, callback: F) { 227 for range in self.ranges.iter_mut() { 228 if let DescriptorState::Allocated(allocation) = &mut range.state { 229 callback( 230 range.offset, 231 range.size, 232 allocation.reservation.debug_id, 233 allocation.data.take(), 234 ); 235 } 236 } 237 } 238 } 239 240 pub(crate) struct EmptyArrayAlloc<T> { 241 ranges: KVec<Range<T>>, 242 } 243 244 impl<T> EmptyArrayAlloc<T> { try_new(capacity: usize) -> Result<Self>245 pub(crate) fn try_new(capacity: usize) -> Result<Self> { 246 Ok(Self { 247 ranges: KVec::with_capacity(capacity, GFP_KERNEL)?, 248 }) 249 } 250 } 251