1 use log::{debug, warn}; 2 3 /// @Author: longjin@dragonos.org 4 /// @Author: kongweichao@dragonos.org 5 /// @Date: 2023-03-28 16:03:47 6 /// @FilePath: /DragonOS/kernel/src/mm/allocator/buddy.rs 7 /// @Description: 伙伴分配器 8 use crate::arch::MMArch; 9 use crate::mm::allocator::bump::BumpAllocator; 10 use crate::mm::allocator::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage}; 11 use crate::mm::{MemoryManagementArch, PhysAddr, PhysMemoryArea, VirtAddr}; 12 13 use core::cmp::min; 14 use core::fmt::Debug; 15 use core::intrinsics::{likely, unlikely}; 16 17 use core::{marker::PhantomData, mem}; 18 19 // 一个全局变量MAX_ORDER,用来表示buddy算法的最大阶数 [MIN_ORDER, MAX_ORDER)左闭右开区间 20 const MAX_ORDER: usize = 31; 21 // 4KB 22 const MIN_ORDER: usize = 12; 23 24 /// 保存buddy算法中每一页存放的BuddyEntry的信息,占据每个页的起始位置 25 #[derive(Debug)] 26 pub struct PageList<A> { 27 // 页存放entry的数量 28 entry_num: usize, 29 // 下一个页面的地址 30 next_page: PhysAddr, 31 phantom: PhantomData<A>, 32 } 33 34 impl<A> Clone for PageList<A> { 35 fn clone(&self) -> Self { 36 Self { 37 entry_num: self.entry_num, 38 next_page: self.next_page, 39 phantom: PhantomData, 40 } 41 } 42 } 43 44 impl<A> PageList<A> { 45 #[allow(dead_code)] 46 fn empty() -> Self { 47 Self { 48 entry_num: 0, 49 next_page: PhysAddr::new(0), 50 phantom: PhantomData, 51 } 52 } 53 fn new(entry_num: usize, next_page: PhysAddr) -> Self { 54 Self { 55 entry_num, 56 next_page, 57 phantom: PhantomData, 58 } 59 } 60 } 61 62 /// @brief: 用来表示 buddy 算法中的一个 buddy 块,整体存放在area的头部 63 // 这种方式会出现对齐问题 64 // #[repr(packed)] 65 #[repr(C)] 66 #[derive(Debug)] 67 pub struct BuddyAllocator<A> { 68 // 存放每个阶的空闲“链表”的头部地址 69 free_area: [PhysAddr; MAX_ORDER - MIN_ORDER], 70 /// 总页数 71 total: PageFrameCount, 72 phantom: PhantomData<A>, 73 } 74 75 impl<A: MemoryManagementArch> BuddyAllocator<A> { 76 const BUDDY_ENTRIES: usize = 77 // 定义一个变量记录buddy表的大小 78 (A::PAGE_SIZE - mem::size_of::<PageList<A>>()) / mem::size_of::<PhysAddr>(); 79 80 pub unsafe fn new(mut bump_allocator: BumpAllocator<A>) -> Option<Self> { 81 let initial_free_pages = bump_allocator.usage().free(); 82 let total_memory = bump_allocator.usage().total(); 83 debug!("Free pages before init buddy: {:?}", initial_free_pages); 84 debug!("Buddy entries: {}", Self::BUDDY_ENTRIES); 85 86 let mut free_area: [PhysAddr; MAX_ORDER - MIN_ORDER] = 87 [PhysAddr::new(0); MAX_ORDER - MIN_ORDER]; 88 89 // Buddy初始占用的空间从bump分配 90 for f in free_area.iter_mut() { 91 let curr_page = bump_allocator.allocate_one(); 92 // 保存每个阶的空闲链表的头部地址 93 *f = curr_page.unwrap(); 94 // 清空当前页 95 core::ptr::write_bytes(MMArch::phys_2_virt(*f)?.data() as *mut u8, 0, A::PAGE_SIZE); 96 97 let page_list: PageList<A> = PageList::new(0, PhysAddr::new(0)); 98 Self::write_page(*f, page_list); 99 } 100 101 let mut allocator = Self { 102 free_area, 103 total: PageFrameCount::new(0), 104 phantom: PhantomData, 105 }; 106 107 let mut total_pages_to_buddy = PageFrameCount::new(0); 108 let mut res_areas = [PhysMemoryArea::default(); 128]; 109 let mut offset_in_remain_area = bump_allocator 110 .remain_areas(&mut res_areas) 111 .expect("BuddyAllocator: failed to get remain areas from bump allocator"); 112 113 let remain_areas = &res_areas[0..]; 114 115 for area in remain_areas { 116 let mut paddr = (area.area_base_aligned() + offset_in_remain_area).data(); 117 let mut remain_pages = 118 PageFrameCount::from_bytes(area.area_end_aligned().data() - paddr).unwrap(); 119 120 if remain_pages.data() == 0 { 121 continue; 122 } 123 debug!("area: {area:?}, paddr: {paddr:#x}, remain_pages: {remain_pages:?}"); 124 125 total_pages_to_buddy += remain_pages; 126 127 if offset_in_remain_area != 0 { 128 offset_in_remain_area = 0; 129 } 130 131 // 先从低阶开始,尽可能地填满空闲链表 132 for i in MIN_ORDER..MAX_ORDER { 133 // debug!("i {i}, remain pages={}", remain_pages.data()); 134 if remain_pages.data() < (1 << (i - MIN_ORDER)) { 135 break; 136 } 137 138 assert!(paddr & ((1 << i) - 1) == 0); 139 140 if likely(i != MAX_ORDER - 1) { 141 // 要填写entry 142 if paddr & (1 << i) != 0 { 143 allocator.buddy_free(PhysAddr::new(paddr), i as u8); 144 145 paddr += 1 << i; 146 remain_pages -= 1 << (i - MIN_ORDER); 147 }; 148 } else { 149 // 往最大的阶数的链表中添加entry(注意要考虑到最大阶数的链表可能有多页) 150 // 断言剩余页面数量是MAX_ORDER-1阶的整数倍 151 152 let mut entries = (remain_pages.data() * A::PAGE_SIZE) >> i; 153 while entries > 0 { 154 allocator.buddy_free(PhysAddr::new(paddr), i as u8); 155 paddr += 1 << i; 156 remain_pages -= 1 << (i - MIN_ORDER); 157 158 entries -= 1; 159 } 160 } 161 } 162 // 然后从高往低,把剩余的页面加入链表 163 let mut remain_bytes = remain_pages.data() * A::PAGE_SIZE; 164 165 assert!(remain_bytes < (1 << MAX_ORDER) - 1); 166 167 for i in (MIN_ORDER..MAX_ORDER).rev() { 168 if remain_bytes >= (1 << i) { 169 assert!(paddr & ((1 << i) - 1) == 0); 170 allocator.buddy_free(PhysAddr::new(paddr), i as u8); 171 172 paddr += 1 << i; 173 remain_bytes -= 1 << i; 174 } 175 } 176 177 assert!(remain_bytes == 0); 178 } 179 180 debug!("Total pages to buddy: {:?}", total_pages_to_buddy); 181 allocator.total = total_memory; 182 183 Some(allocator) 184 } 185 /// 获取第j个entry的虚拟地址, 186 /// j从0开始计数 187 pub fn entry_virt_addr(base_addr: PhysAddr, j: usize) -> VirtAddr { 188 let entry_virt_addr = unsafe { A::phys_2_virt(Self::entry_addr(base_addr, j)) }; 189 return entry_virt_addr.unwrap(); 190 } 191 pub fn entry_addr(base_addr: PhysAddr, j: usize) -> PhysAddr { 192 let entry_addr = base_addr + mem::size_of::<PageList<A>>() + j * mem::size_of::<PhysAddr>(); 193 return entry_addr; 194 } 195 pub fn read_page<T>(addr: PhysAddr) -> T { 196 let page_list = unsafe { A::read(A::phys_2_virt(addr).unwrap()) }; 197 return page_list; 198 } 199 200 pub fn write_page(curr_page: PhysAddr, page_list: PageList<A>) { 201 // 把物理地址转换为虚拟地址 202 let virt_addr = unsafe { A::phys_2_virt(curr_page) }; 203 let virt_addr = virt_addr.unwrap(); 204 unsafe { A::write(virt_addr, page_list) }; 205 } 206 207 /// 从order转换为free_area的下标 208 /// 209 /// # 参数 210 /// 211 /// - `order` - order 212 /// 213 /// # 返回值 214 /// 215 /// free_area的下标 216 #[inline] 217 fn order2index(order: u8) -> usize { 218 order as usize - MIN_ORDER 219 } 220 221 /// 从空闲链表的开头,取出1个指定阶数的伙伴块,如果没有,则返回None 222 /// 223 /// ## 参数 224 /// 225 /// - `order` - 伙伴块的阶数 226 fn pop_front(&mut self, order: u8) -> Option<PhysAddr> { 227 let mut alloc_in_specific_order = |spec_order: u8| { 228 // 先尝试在order阶的“空闲链表”的开头位置分配一个伙伴块 229 let mut page_list_addr = self.free_area[Self::order2index(spec_order)]; 230 let mut page_list: PageList<A> = Self::read_page(page_list_addr); 231 232 // 循环删除头部的空闲链表页 233 while page_list.entry_num == 0 { 234 let next_page_list_addr = page_list.next_page; 235 // 找完了,都是空的 236 if next_page_list_addr.is_null() { 237 return None; 238 } 239 240 if !next_page_list_addr.is_null() { 241 // 此时page_list已经没有空闲伙伴块了,又因为非唯一页,需要删除该page_list 242 self.free_area[Self::order2index(spec_order)] = next_page_list_addr; 243 // debug!("FREE: page_list_addr={:b}", page_list_addr.data()); 244 unsafe { 245 self.buddy_free(page_list_addr, MMArch::PAGE_SHIFT as u8); 246 } 247 } 248 // 由于buddy_free可能导致首部的链表页发生变化,因此需要重新读取 249 let next_page_list_addr = self.free_area[Self::order2index(spec_order)]; 250 assert!(!next_page_list_addr.is_null()); 251 page_list = Self::read_page(next_page_list_addr); 252 page_list_addr = next_page_list_addr; 253 } 254 255 // 有空闲页面,直接分配 256 if page_list.entry_num > 0 { 257 let entry: PhysAddr = unsafe { 258 A::read(Self::entry_virt_addr( 259 page_list_addr, 260 page_list.entry_num - 1, 261 )) 262 }; 263 // 清除该entry 264 unsafe { 265 A::write( 266 Self::entry_virt_addr(page_list_addr, page_list.entry_num - 1), 267 PhysAddr::new(0), 268 ) 269 }; 270 if entry.is_null() { 271 panic!( 272 "entry is null, entry={:?}, order={}, entry_num = {}", 273 entry, 274 spec_order, 275 page_list.entry_num - 1 276 ); 277 } 278 // debug!("entry={entry:?}"); 279 280 // 更新page_list的entry_num 281 page_list.entry_num -= 1; 282 let tmp_current_entry_num = page_list.entry_num; 283 if page_list.entry_num == 0 { 284 if !page_list.next_page.is_null() { 285 // 此时page_list已经没有空闲伙伴块了,又因为非唯一页,需要删除该page_list 286 self.free_area[Self::order2index(spec_order)] = page_list.next_page; 287 let _ = page_list; 288 unsafe { self.buddy_free(page_list_addr, MMArch::PAGE_SHIFT as u8) }; 289 } else { 290 Self::write_page(page_list_addr, page_list); 291 } 292 } else { 293 // 若entry_num不为0,说明该page_list还有空闲伙伴块,需要更新该page_list 294 // 把更新后的page_list写回 295 Self::write_page(page_list_addr, page_list.clone()); 296 } 297 298 // 检测entry 是否对齐 299 if !entry.check_aligned(1 << spec_order) { 300 panic!("entry={:?} is not aligned, spec_order={spec_order}, page_list.entry_num={}", entry, tmp_current_entry_num); 301 } 302 return Some(entry); 303 } 304 return None; 305 }; 306 let result: Option<PhysAddr> = alloc_in_specific_order(order); 307 // debug!("result={:?}", result); 308 if result.is_some() { 309 return result; 310 } 311 // 尝试从更大的链表中分裂 312 313 let mut current_order = (order + 1) as usize; 314 let mut x: Option<PhysAddr> = None; 315 while current_order < MAX_ORDER { 316 x = alloc_in_specific_order(current_order as u8); 317 // debug!("current_order={:?}", current_order); 318 if x.is_some() { 319 break; 320 } 321 current_order += 1; 322 } 323 324 // debug!("x={:?}", x); 325 // 如果找到一个大的块,就进行分裂 326 if x.is_some() { 327 // 分裂到order阶 328 while current_order > order as usize { 329 current_order -= 1; 330 // 把后面那半块放回空闲链表 331 332 let buddy = *x.as_ref().unwrap() + (1 << current_order); 333 // debug!("x={:?}, buddy={:?}", x, buddy); 334 // debug!("current_order={:?}, buddy={:?}", current_order, buddy); 335 unsafe { self.buddy_free(buddy, current_order as u8) }; 336 } 337 return x; 338 } 339 340 return None; 341 } 342 343 /// 从伙伴系统中分配count个页面 344 /// 345 /// ## 参数 346 /// 347 /// - `count`:需要分配的页面数 348 /// 349 /// ## 返回值 350 /// 351 /// 返回分配的页面的物理地址和页面数 352 fn buddy_alloc(&mut self, count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> { 353 assert!(count.data().is_power_of_two()); 354 // 计算需要分配的阶数 355 let mut order = log2(count.data()); 356 if count.data() & ((1 << order) - 1) != 0 { 357 order += 1; 358 } 359 let order = (order + MIN_ORDER) as u8; 360 if order as usize >= MAX_ORDER { 361 return None; 362 } 363 364 // debug!("buddy_alloc: order = {}", order); 365 // 获取该阶数的一个空闲页面 366 let free_addr = self.pop_front(order); 367 // debug!( 368 // "buddy_alloc: order = {}, free_addr = {:?}", 369 // order, 370 // free_addr 371 // ); 372 return free_addr 373 .map(|addr| (addr, PageFrameCount::new(1 << (order as usize - MIN_ORDER)))); 374 } 375 376 /// 释放一个块 377 /// 378 /// ## 参数 379 /// 380 /// - `base` - 块的起始地址 381 /// - `order` - 块的阶数 382 unsafe fn buddy_free(&mut self, mut base: PhysAddr, order: u8) { 383 // debug!("buddy_free: base = {:?}, order = {}", base, order); 384 let mut order = order as usize; 385 386 while order < MAX_ORDER { 387 // 检测地址是否合法 388 if base.data() & ((1 << (order)) - 1) != 0 { 389 panic!( 390 "buddy_free: base is not aligned, base = {:#x}, order = {}", 391 base.data(), 392 order 393 ); 394 } 395 396 // 在链表中寻找伙伴块 397 // 伙伴块的地址是base ^ (1 << order) 398 let buddy_addr = PhysAddr::new(base.data() ^ (1 << order)); 399 400 let first_page_list_paddr = self.free_area[Self::order2index(order as u8)]; 401 let mut page_list_paddr = first_page_list_paddr; 402 let mut page_list: PageList<A> = Self::read_page(page_list_paddr); 403 let first_page_list = page_list.clone(); 404 405 let mut buddy_entry_virt_vaddr = None; 406 let mut buddy_entry_page_list_paddr = None; 407 // 除非order是最大的,否则尝试查找伙伴块 408 if likely(order != MAX_ORDER - 1) { 409 'outer: loop { 410 for i in 0..page_list.entry_num { 411 let entry_virt_addr = Self::entry_virt_addr(page_list_paddr, i); 412 let entry: PhysAddr = unsafe { A::read(entry_virt_addr) }; 413 if entry == buddy_addr { 414 // 找到了伙伴块,记录该entry相关信息,然后退出查找 415 buddy_entry_virt_vaddr = Some(entry_virt_addr); 416 buddy_entry_page_list_paddr = Some(page_list_paddr); 417 break 'outer; 418 } 419 } 420 if page_list.next_page.is_null() { 421 break; 422 } 423 page_list_paddr = page_list.next_page; 424 page_list = Self::read_page(page_list_paddr); 425 } 426 } 427 428 // 如果没有找到伙伴块 429 if let Some(buddy_entry_virt_addr) = buddy_entry_virt_vaddr { 430 // 如果找到了伙伴块,合并,向上递归 431 432 // 伙伴块所在的page_list的物理地址 433 let buddy_entry_page_list_paddr = buddy_entry_page_list_paddr.unwrap(); 434 435 let mut page_list_paddr = self.free_area[Self::order2index(order as u8)]; 436 let mut page_list = Self::read_page::<PageList<A>>(page_list_paddr); 437 // 找第一个有空闲块的链表页。跳过空闲链表页。不进行回收的原因是担心出现死循环 438 while page_list.entry_num == 0 { 439 if page_list.next_page.is_null() { 440 panic!( 441 "buddy_free: page_list.entry_num == 0 && page_list.next_page.is_null()" 442 ); 443 } 444 page_list_paddr = page_list.next_page; 445 page_list = Self::read_page(page_list_paddr); 446 } 447 448 // 如果伙伴块不在第一个链表页,则把第一个链表中的某个空闲块替换到伙伴块的位置 449 if page_list_paddr != buddy_entry_page_list_paddr { 450 let entry: PhysAddr = unsafe { 451 A::read(Self::entry_virt_addr( 452 page_list_paddr, 453 page_list.entry_num - 1, 454 )) 455 }; 456 // 把这个空闲块写入到伙伴块的位置 457 unsafe { 458 A::write(buddy_entry_virt_addr, entry); 459 } 460 // 设置刚才那个entry为空 461 unsafe { 462 A::write( 463 Self::entry_virt_addr(page_list_paddr, page_list.entry_num - 1), 464 PhysAddr::new(0), 465 ); 466 } 467 // 更新当前链表页的统计数据 468 page_list.entry_num -= 1; 469 Self::write_page(page_list_paddr, page_list); 470 } else { 471 // 伙伴块所在的链表页就是第一个链表页 472 let last_entry: PhysAddr = unsafe { 473 A::read(Self::entry_virt_addr( 474 page_list_paddr, 475 page_list.entry_num - 1, 476 )) 477 }; 478 479 // 如果最后一个空闲块不是伙伴块,则把最后一个空闲块移动到伙伴块的位置 480 // 否则后面的操作也将删除这个伙伴块 481 if last_entry != buddy_addr { 482 unsafe { 483 A::write(buddy_entry_virt_addr, last_entry); 484 A::write( 485 Self::entry_virt_addr(page_list_paddr, page_list.entry_num - 1), 486 PhysAddr::new(0), 487 ); 488 } 489 } else { 490 unsafe { 491 A::write( 492 Self::entry_virt_addr(page_list_paddr, page_list.entry_num - 1), 493 PhysAddr::new(0), 494 ); 495 } 496 } 497 // 更新当前链表页的统计数据 498 page_list.entry_num -= 1; 499 Self::write_page(page_list_paddr, page_list); 500 } 501 } else { 502 assert!( 503 page_list.entry_num <= Self::BUDDY_ENTRIES, 504 "buddy_free: page_list.entry_num > Self::BUDDY_ENTRIES" 505 ); 506 507 // 当前第一个page_list没有空间了 508 if first_page_list.entry_num == Self::BUDDY_ENTRIES { 509 // 如果当前order是最小的,那么就把这个块当作新的page_list使用 510 let new_page_list_addr = if order == MIN_ORDER { 511 base 512 } else { 513 // 否则分配新的page_list 514 // 请注意,分配之后,有可能当前的entry_num会减1(伙伴块分裂),造成出现整个链表为null的entry数量为Self::BUDDY_ENTRIES+1的情况 515 // 但是不影响,我们在后面插入链表项的时候,会处理这种情况,检查链表中的第2个页是否有空位 516 self.buddy_alloc(PageFrameCount::new(1)) 517 .expect("buddy_alloc failed: no enough memory") 518 .0 519 }; 520 521 // 清空这个页面 522 core::ptr::write_bytes( 523 A::phys_2_virt(new_page_list_addr) 524 .expect( 525 "Buddy free: failed to get virt address of [new_page_list_addr]", 526 ) 527 .as_ptr::<u8>(), 528 0, 529 1 << order, 530 ); 531 assert!( 532 first_page_list_paddr == self.free_area[Self::order2index(order as u8)] 533 ); 534 // 初始化新的page_list 535 let new_page_list = PageList::new(0, first_page_list_paddr); 536 Self::write_page(new_page_list_addr, new_page_list); 537 self.free_area[Self::order2index(order as u8)] = new_page_list_addr; 538 } 539 540 // 由于上面可能更新了第一个链表页,因此需要重新获取这个值 541 let first_page_list_paddr = self.free_area[Self::order2index(order as u8)]; 542 let first_page_list: PageList<A> = Self::read_page(first_page_list_paddr); 543 544 // 检查第二个page_list是否有空位 545 let second_page_list = if first_page_list.next_page.is_null() { 546 None 547 } else { 548 Some(Self::read_page::<PageList<A>>(first_page_list.next_page)) 549 }; 550 551 let (paddr, mut page_list) = if let Some(second) = second_page_list { 552 // 第二个page_list有空位 553 // 应当符合之前的假设:还有1个空位 554 assert!(second.entry_num == Self::BUDDY_ENTRIES - 1); 555 556 (first_page_list.next_page, second) 557 } else { 558 // 在第一个page list中分配 559 (first_page_list_paddr, first_page_list) 560 }; 561 562 // debug!("to write entry, page_list_base={paddr:?}, page_list.entry_num={}, value={base:?}", page_list.entry_num); 563 assert!(page_list.entry_num < Self::BUDDY_ENTRIES); 564 // 把要归还的块,写入到链表项中 565 unsafe { A::write(Self::entry_virt_addr(paddr, page_list.entry_num), base) } 566 page_list.entry_num += 1; 567 Self::write_page(paddr, page_list); 568 return; 569 } 570 base = min(base, buddy_addr); 571 order += 1; 572 } 573 // 走到这一步,order应该为MAX_ORDER-1 574 assert!(order == MAX_ORDER - 1); 575 } 576 } 577 578 impl<A: MemoryManagementArch> FrameAllocator for BuddyAllocator<A> { 579 unsafe fn allocate(&mut self, count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> { 580 return self.buddy_alloc(count); 581 } 582 583 /// 释放一个块 584 /// 585 /// ## 参数 586 /// 587 /// - `base` - 块的起始地址 588 /// - `count` - 块的页数(必须是2的幂) 589 /// 590 /// ## Panic 591 /// 592 /// 如果count不是2的幂,会panic 593 unsafe fn free(&mut self, base: PhysAddr, count: PageFrameCount) { 594 // 要求count是2的幂 595 if unlikely(!count.data().is_power_of_two()) { 596 warn!("buddy free: count is not power of two"); 597 } 598 let mut order = log2(count.data()); 599 if count.data() & ((1 << order) - 1) != 0 { 600 order += 1; 601 } 602 let order = (order + MIN_ORDER) as u8; 603 // debug!("free: base={:?}, count={:?}", base, count); 604 self.buddy_free(base, order); 605 } 606 607 unsafe fn usage(&self) -> PageFrameUsage { 608 let mut free_page_num: usize = 0; 609 for index in 0..(MAX_ORDER - MIN_ORDER) { 610 let mut pagelist: PageList<A> = Self::read_page(self.free_area[index]); 611 loop { 612 free_page_num += pagelist.entry_num << index; 613 if pagelist.next_page.is_null() { 614 break; 615 } 616 pagelist = Self::read_page(pagelist.next_page); 617 } 618 } 619 let free = PageFrameCount::new(free_page_num); 620 PageFrameUsage::new(self.total - free, self.total) 621 } 622 } 623 624 /// 一个用于计算整数的对数的函数,会向下取整。(由于内核不能进行浮点运算,因此需要这个函数) 625 fn log2(x: usize) -> usize { 626 let leading_zeros = x.leading_zeros() as usize; 627 let log2x = 63 - leading_zeros; 628 return log2x; 629 } 630