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