// SPDX-License-Identifier: MPL-2.0 #include "memory/pez/pez.h" #include "memory/pez/pez_debug.h" #include "kernel/log.h" sh_bool sh_pez_is_pez_available=SH_FALSE; sh_pez_PHYSICAL_PLANE *reference_phys_plane=SH_NULLPTR; void sh_pez_set_available() { sh_pez_is_pez_available=SH_TRUE; } sh_bool sh_pez_is_available() { return sh_pez_is_pez_available; } static void sh_pez_set_reference_phys_plane(sh_pez_PHYSICAL_PLANE *phys_plane) { reference_phys_plane=phys_plane; } sh_pez_PHYSICAL_PLANE* sh_pez_get_reference_phys_plane() { return reference_phys_plane; } static inline void sh_pez_internal_pack_index(sh_uint8 *dest,sh_uint32 index) { dest[0]=(sh_uint8)(index & 0xFF); dest[1]=(sh_uint8)((index>>8)&0xFF); dest[2]=(sh_uint8)((index>>16)&0xFF); return; } static inline sh_uint32 sh_pez_internal_unpack_index(sh_uint8 *src) { return (sh_uint32)src[0] | ((sh_uint32)src[1]<<8) | ((sh_uint32)src[2]<<16); } typedef struct { sh_uint32 region_index; sh_uint32 previous_index; } sh_pez_internal_BOUNDARY_ENTRY; static inline sh_page_VIRTUAL_ADDRESS sh_pez_internal_pack_boundary(sh_uint32 region,sh_uint32 prev) { return (sh_page_VIRTUAL_ADDRESS)(((sh_uint64)prev<<32) | region); } static inline sh_pez_internal_BOUNDARY_ENTRY sh_pez_internal_unpack_boundary(sh_page_VIRTUAL_ADDRESS value) { sh_pez_internal_BOUNDARY_ENTRY entry; entry.region_index=(sh_uint32)(value & 0xFFFFFFFF); entry.previous_index=(sh_uint32)(value>>32); return entry; } static SH_STATUS sh_pez_internal_scan_physical_bitmap(sh_pez_PHYSICAL_PLANE *phys_plane) { if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_log_fdebug(SH_LOG_SOURCE_PEZ,"Scanning physical bitmap:\n"); sh_uint64 *bitmap64=(sh_uint64*)phys_plane->physical_bitmap; sh_uint32 current_page=0; sh_uint64 free_region_count=0; sh_pez_debug_BITMAP bitmap_struct; while (current_pagephysical_page_count) { if ((current_page%64)==0) { while (current_page+64<=phys_plane->physical_page_count && bitmap64[current_page/64]==0xFFFFFFFFFFFFFFFF) { current_page+=64; } } if (current_pagephysical_page_count && (sh_page_is_allocated(phys_plane->physical_bitmap,current_page)==0)) { sh_uint32 start=current_page; while (current_pagephysical_page_count && sh_page_is_allocated(phys_plane->physical_bitmap,current_page)==0) { current_page++; } sh_uint32 size=current_page-start; bitmap_struct.regions[free_region_count].start=start; bitmap_struct.regions[free_region_count].size=size; free_region_count++; if (free_region_count==512) { return SH_STATUS_TOO_MUCH_DATA; } } else if (current_pagephysical_page_count) { current_page++; } } bitmap_struct.count=(sh_uint32)free_region_count; sh_pez_debug_send_bitmap(&bitmap_struct); return SH_STATUS_SUCCESS; } static SH_STATUS sh_pez_internal_scan_boundary_radix_physical(sh_pez_PHYSICAL_PLANE *phys_plane) { if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_log_fdebug(SH_LOG_SOURCE_PEZ,"Scanning boundary radix:\n"); sh_uint64 free_region_count=0; sh_uint64 stop=sh_page_get_physical_memory_amount_pages(); sh_pez_debug_BOUNDARY_RADIX boundary_radix; sh_bool is_in_region=SH_FALSE; for (sh_iter64 i=0;iboundary_radix_tree,i,&boundary); if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; if (status==SH_STATUS_NOT_FOUND) continue; if (is_in_region==SH_FALSE) { boundary_radix.regions[free_region_count].pos_start=(sh_uint32)i; sh_pez_internal_BOUNDARY_ENTRY boundary_unpack=sh_pez_internal_unpack_boundary(boundary); boundary_radix.regions[free_region_count].idx_start=boundary_unpack.region_index; boundary_radix.regions[free_region_count].prev_start=boundary_unpack.previous_index; is_in_region=SH_TRUE; continue; } else { sh_pez_internal_BOUNDARY_ENTRY boundary_unpack=sh_pez_internal_unpack_boundary(boundary); // check to see if region is one page wide or more if (boundary_unpack.region_index==boundary_radix.regions[free_region_count].idx_start) { boundary_radix.regions[free_region_count].pos_end=(sh_uint32)i; boundary_radix.regions[free_region_count].idx_end=boundary_unpack.region_index; boundary_radix.regions[free_region_count].prev_end=boundary_unpack.previous_index; is_in_region=SH_FALSE; free_region_count++; if (free_region_count==512) { return SH_STATUS_TOO_MUCH_DATA; } continue; } else { boundary_radix.regions[free_region_count].pos_end=boundary_radix.regions[free_region_count].pos_start; boundary_radix.regions[free_region_count].idx_end=boundary_radix.regions[free_region_count].idx_start; boundary_radix.regions[free_region_count].prev_end=boundary_radix.regions[free_region_count].prev_start; is_in_region=SH_FALSE; free_region_count++; if (free_region_count==512) { return SH_STATUS_TOO_MUCH_DATA; } continue; } } } boundary_radix.count=(sh_uint32)free_region_count; sh_pez_debug_send_boundary_radix(&boundary_radix); return SH_STATUS_SUCCESS; } static SH_STATUS sh_pez_internal_check_physical_radix(sh_pez_PHYSICAL_PLANE *phys_plane) { if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_page_MEM_STATS mem_stats; SH_STATUS status=sh_page_get_memory_stats(&mem_stats); for (sh_iter64 i=1;iregion_radix_tree,i,&index); if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; sh_slab_reg_phys_OBJECT_INDEX idx=(sh_uint32)index; if (status==SH_STATUS_NOT_FOUND) continue; while (SH_TRUE) { reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,idx); if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; if (reg->region_size_pages==0 || reg->start_page_index==0) { sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that have either his address or his size starting at 0 (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i); return SH_STATUS_PEZ_CORRUPTED; } if (reg->region_size_pages!=i) { sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that doesn't have his placement (#%8u in size list) and size matching : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i); return SH_STATUS_PEZ_CORRUPTED; } for (sh_uint64 y=reg->start_page_index;y<(sh_uint64)(reg->start_page_index+reg->region_size_pages);y++) { if (sh_page_is_allocated(phys_plane->physical_bitmap,y)) { sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that have a page allocated (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages; Concerned page: 0x%x\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i,((sh_uint64)y)*SH_PAGE_SIZE); return SH_STATUS_PEZ_CORRUPTED; } } if (!sh_page_is_allocated(phys_plane->physical_bitmap,reg->start_page_index-1)) { sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that doesn't fully extend at left, found a page free at left (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages; Concerned page: 0x%x\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i,((sh_uint64)reg->start_page_index-1)*SH_PAGE_SIZE); return SH_STATUS_PEZ_CORRUPTED; } if (!sh_page_is_allocated(phys_plane->physical_bitmap,reg->start_page_index+reg->region_size_pages) && reg->start_page_index+reg->region_size_pages!=sh_page_get_physical_memory_amount_pages()) { sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that doesn't fully extend at right, found a page free at right (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages; Concerned page: 0x%x\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i,((sh_uint64)(reg->start_page_index+reg->region_size_pages))*SH_PAGE_SIZE); return SH_STATUS_PEZ_CORRUPTED; } if (sh_pez_internal_unpack_index(reg->next_region_index)==0) { break; } else { idx=sh_pez_internal_unpack_index(reg->next_region_index); pos++; } } } return SH_STATUS_SUCCESS; } static SH_STATUS sh_pez_internal_scan_size_list_physical(sh_pez_PHYSICAL_PLANE *phys_plane) { if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_page_MEM_STATS mem_stats; SH_STATUS status=sh_page_get_memory_stats(&mem_stats); sh_log_fdebug(SH_LOG_SOURCE_PEZ,"Scanning size list radix:\n"); sh_uint64 free_region_count=0; sh_log_byte(SH_PEZ_DEBUG_SIZE_LIST_BLOCK_HEADER); sh_log_byte('\n'); for (sh_iter64 i=1;iregion_radix_tree,i,&index); if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; sh_slab_reg_phys_OBJECT_INDEX idx=(sh_uint32)index; if (status==SH_STATUS_NOT_FOUND) continue; sh_uint64 size_list_region_count=0; sh_pez_debug_SIZE_LIST size_list; size_list.size=(sh_uint32)i; while (SH_TRUE) { reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,idx); if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; size_list.regions[size_list_region_count].start=reg->start_page_index; size_list.regions[size_list_region_count].size=reg->region_size_pages; size_list.regions[size_list_region_count].idx=idx; size_list.regions[size_list_region_count].next_idx=sh_pez_internal_unpack_index(reg->next_region_index); free_region_count++; size_list_region_count++; if (size_list_region_count==512) { return SH_STATUS_TOO_MUCH_DATA; } if (sh_pez_internal_unpack_index(reg->next_region_index)==0) { break; } else { idx=sh_pez_internal_unpack_index(reg->next_region_index); } } size_list.count=(sh_uint32)size_list_region_count; sh_pez_debug_send_size_list(&size_list); } return SH_STATUS_SUCCESS; } static SH_STATUS sh_pez_internal_create_free_region_physical(sh_pez_PHYSICAL_PLANE *phys_plane,sh_uint32 region_start,sh_uint32 region_size,sh_pez_REGION_PHYSICAL_OBJECT **region) { if (phys_plane==SH_NULLPTR || region_size==0 || region==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_slab_reg_phys_OBJECT_INDEX reg_idx; SH_STATUS status=sh_slab_reg_phys_alloc(phys_plane->slab_reg_phys,phys_plane->kernel_ptp,®_idx); if (sh_status_error(status)) return status; sh_pez_REGION_PHYSICAL_OBJECT *reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,reg_idx); if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; reg->start_page_index=region_start; reg->region_size_pages=region_size; sh_page_VIRTUAL_ADDRESS head_index=0; status=sh_radix_tree_get_value(&phys_plane->region_radix_tree,(sh_uint64)region_size,&head_index); if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; sh_page_VIRTUAL_ADDRESS new_boundary_val=sh_pez_internal_pack_boundary(reg_idx,0); status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)region_start,new_boundary_val); if (sh_status_error(status)) return status; status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)region_start+region_size-1,new_boundary_val); if (sh_status_error(status)) return status; if (head_index!=0) { sh_pez_REGION_PHYSICAL_OBJECT *old_head_ptr=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,(sh_uint32)head_index); if (old_head_ptr==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; sh_page_VIRTUAL_ADDRESS updated_old_head_boundary=sh_pez_internal_pack_boundary((sh_uint32)head_index,reg_idx); status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index,updated_old_head_boundary); if (sh_status_error(status)) return status; sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index+old_head_ptr->region_size_pages-1,updated_old_head_boundary); if (sh_status_error(status)) return status; sh_pez_internal_pack_index(reg->next_region_index,(sh_uint32)head_index); } else { sh_pez_internal_pack_index(reg->next_region_index,0); } status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->region_radix_tree,(sh_uint64)region_size,(sh_page_VIRTUAL_ADDRESS)reg_idx); if (sh_status_error(status)) return status; *region=reg; return SH_STATUS_SUCCESS; } static SH_STATUS sh_pez_internal_check_region_integrity_physical(sh_pez_PHYSICAL_PLANE *phys_plane,sh_pez_REGION_PHYSICAL_OBJECT *reg,sh_bool *optional_is_first,sh_slab_reg_phys_OBJECT_INDEX *optional_reg_idx,sh_slab_reg_phys_OBJECT_INDEX *optional_prev_idx) { if (phys_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_page_VIRTUAL_ADDRESS boundary_left; sh_page_VIRTUAL_ADDRESS boundary_right; SH_STATUS status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,reg->start_page_index,&boundary_left); if (sh_status_error(status)) return status; status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1,&boundary_right); if (sh_status_error(status)) return status; if (boundary_left!=boundary_right) return SH_STATUS_PEZ_CORRUPTED; sh_pez_internal_BOUNDARY_ENTRY boundary=sh_pez_internal_unpack_boundary(boundary_left); sh_bool should_be_first; if (boundary.previous_index==0) { should_be_first=SH_TRUE; } else { should_be_first=SH_FALSE; } sh_page_VIRTUAL_ADDRESS reg_idx; status=sh_radix_tree_get_value(&phys_plane->region_radix_tree,reg->region_size_pages,®_idx); if (sh_status_error(status)) return status; sh_pez_REGION_PHYSICAL_OBJECT *region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,(sh_uint32)reg_idx); if (region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; if ((!should_be_first && region==reg) || (should_be_first && region!=reg)) { return SH_STATUS_PEZ_CORRUPTED; } if (optional_is_first!=SH_NULLPTR) { *optional_is_first=should_be_first; } if (optional_reg_idx!=SH_NULLPTR) { *optional_reg_idx=boundary.region_index; } if (optional_prev_idx!=SH_NULLPTR) { *optional_prev_idx=boundary.previous_index; } return SH_STATUS_SUCCESS; } static SH_STATUS sh_pez_internal_delete_free_region_physical(sh_pez_PHYSICAL_PLANE *phys_plane,sh_pez_REGION_PHYSICAL_OBJECT *reg) { if (phys_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_bool is_first; sh_slab_reg_phys_OBJECT_INDEX reg_idx; sh_slab_reg_phys_OBJECT_INDEX prev_idx; SH_STATUS status=sh_pez_internal_check_region_integrity_physical(phys_plane,reg,&is_first,®_idx,&prev_idx); if (sh_status_error(status)) return status; if (is_first) { // if region is the first of the list if (sh_pez_internal_unpack_index(reg->next_region_index)==0) { // if region was alone in his list // delete radix entry status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->region_radix_tree,reg->region_size_pages); if (sh_status_error(status)) return status; // delete left boundary status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index); if (sh_status_error(status)) return status; // delete right boundary if (reg->region_size_pages!=1) { status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); if (sh_status_error(status)) return status; } // delete region object status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx); if (sh_status_error(status)) return status; return SH_STATUS_SUCCESS; } else { // if region isn't alone in his list // obtain next region informations sh_slab_reg_phys_OBJECT_INDEX next_reg_idx=sh_pez_internal_unpack_index(reg->next_region_index); sh_pez_REGION_PHYSICAL_OBJECT *next_region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,next_reg_idx); if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; // insert next region as first inside size list status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->region_radix_tree,reg->region_size_pages,next_reg_idx); if (sh_status_error(status)) return status; // update left boundary of next region sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,0); status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region); if (sh_status_error(status)) return status; // update right boundary of next region status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region); if (sh_status_error(status)) return status; // delete left boundary status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index); if (sh_status_error(status)) return status; // delete right boundary if (reg->region_size_pages!=1) { status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); if (sh_status_error(status)) return status; } // delete region object status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx); if (sh_status_error(status)) return status; return SH_STATUS_SUCCESS; } } else { // if region isn't the first of his list // obtain information about prev region sh_pez_REGION_PHYSICAL_OBJECT *prev_region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,prev_idx); if (prev_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; if (sh_pez_internal_unpack_index(reg->next_region_index)==0) { // if region is at the end of the size list // put next_idx of prev region to 0 sh_pez_internal_pack_index(prev_region->next_region_index,0); // delete left boundary status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index); if (sh_status_error(status)) return status; // delete right boundary if (reg->region_size_pages!=1) { status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); if (sh_status_error(status)) return status; } // delete region object status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx); if (sh_status_error(status)) return status; return SH_STATUS_SUCCESS; } else { // if region is in the middle of the size list // obtain information about next region sh_slab_reg_phys_OBJECT_INDEX next_reg_idx=sh_pez_internal_unpack_index(reg->next_region_index); sh_pez_REGION_PHYSICAL_OBJECT *next_region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,next_reg_idx); if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; // put next_idx of prev region to next_region_idx sh_pez_internal_pack_index(prev_region->next_region_index,next_reg_idx); // update left boundary of next region sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,prev_idx); status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region); if (sh_status_error(status)) return status; // update right boundary of next region status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region); if (sh_status_error(status)) return status; // delete left boundary status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index); if (sh_status_error(status)) return status; // delete right boundary if (reg->region_size_pages!=1) { status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); if (sh_status_error(status)) return status; } // delete region object status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx); if (sh_status_error(status)) return status; return SH_STATUS_SUCCESS; } } } SH_STATUS sh_pez_init_physical_plane(sh_uint8 *physical_bitmap,sh_uint64 physical_page_count,sh_slab_reg_phys_SLAB_ALLOCATOR *slab_reg_phys,struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_pez_PHYSICAL_PLANE *phys_plane) { if (physical_bitmap==SH_NULLPTR || physical_page_count==0 || slab_reg_phys==SH_NULLPTR || slab_radix_node==SH_NULLPTR || kernel_ptp==SH_NULLPTR || phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_radix_tree_init(slab_radix_node,kernel_ptp,&phys_plane->region_radix_tree,8); sh_radix_tree_init(slab_radix_node,kernel_ptp,&phys_plane->boundary_radix_tree,8); phys_plane->free_pages=0; phys_plane->used_pages=0; phys_plane->physical_bitmap=physical_bitmap; phys_plane->physical_page_count=physical_page_count; phys_plane->slab_reg_phys=slab_reg_phys; phys_plane->slab_radix_node=slab_radix_node; phys_plane->kernel_ptp=kernel_ptp; sh_uint64 *bitmap64=(sh_uint64*)physical_bitmap; sh_uint32 current_page=0; while (current_pagefree_pages=(sh_uint32)mem_stats.free_pages; phys_plane->used_pages=(sh_uint32)mem_stats.used_pages; sh_pez_set_reference_phys_plane(phys_plane); return SH_STATUS_SUCCESS; } SH_STATUS sh_pez_alloc_physical_pages(sh_pez_PHYSICAL_PLANE *phys_plane,sh_uint32 pages_count,sh_page_PHYSICAL_ADDRESS *address) { if (phys_plane==SH_NULLPTR || pages_count==0 || address==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_page_VIRTUAL_ADDRESS reg_idx; SH_STATUS status=sh_radix_tree_search_smallest_min_bound(phys_plane->slab_radix_node,&phys_plane->region_radix_tree,pages_count,®_idx); if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; if (status==SH_STATUS_NOT_FOUND) return SH_STATUS_OUT_OF_MEMORY; sh_pez_REGION_PHYSICAL_OBJECT *reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,(sh_uint32)reg_idx); if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; if (reg->region_size_pagesregion_size_pages==pages_count) { // exact fit case // save metadata for later modifications sh_uint32 start=reg->start_page_index; // delete free region status=sh_pez_internal_delete_free_region_physical(phys_plane,reg); if (sh_status_error(status)) return status; // mark pages as occupied inside physical bitmap status=sh_page_set_pages_range_bitmap(phys_plane->physical_bitmap,phys_plane->physical_page_count,start,pages_count,SH_TRUE); if (sh_status_error(status)) return status; // give the address *address=((sh_page_PHYSICAL_ADDRESS)start)*SH_PAGE_SIZE; } else if (reg->region_size_pages>pages_count) { // best fit case // save metadata for later modifications sh_uint32 start=reg->start_page_index; // compute new size and index sh_uint32 new_start=reg->start_page_index+pages_count; if (new_start==0) return SH_STATUS_PEZ_CORRUPTED; sh_uint32 new_size=reg->region_size_pages-pages_count; if (new_size==0) return SH_STATUS_PEZ_CORRUPTED; // delete free region status=sh_pez_internal_delete_free_region_physical(phys_plane,reg); if (sh_status_error(status)) return status; // mark pages as occupied inside physical bitmap status=sh_page_set_pages_range_bitmap(phys_plane->physical_bitmap,phys_plane->physical_page_count,start,pages_count,SH_TRUE); if (sh_status_error(status)) return status; // create new free region with splitted part sh_pez_REGION_PHYSICAL_OBJECT *new_reg; status=sh_pez_internal_create_free_region_physical(phys_plane,new_start,new_size,&new_reg); if (sh_status_error(status)) return status; // give the address *address=((sh_page_PHYSICAL_ADDRESS)start)*SH_PAGE_SIZE; } phys_plane->free_pages=phys_plane->free_pages-pages_count; phys_plane->used_pages=phys_plane->used_pages+pages_count; return SH_STATUS_SUCCESS; } SH_STATUS sh_pez_free_physical_pages(sh_pez_PHYSICAL_PLANE *phys_plane,sh_page_PHYSICAL_ADDRESS *address,sh_uint32 pages_count) { if (phys_plane==SH_NULLPTR || address==SH_NULLPTR || pages_count==0) return SH_STATUS_INVALID_PARAMETER; // save freeed region data sh_uint32 start_reg=(sh_uint32)((*address)/SH_PAGE_SIZE); if (start_reg==0) return SH_STATUS_INVALID_PARAMETER; sh_uint32 size_reg=pages_count; // checking if region is entirely used for (sh_iter64 i=0;iphysical_bitmap,start_reg+i)) { return SH_STATUS_PAGE_ALREADY_FREE; } } // marking pages as free SH_STATUS status=sh_page_set_pages_range_bitmap(phys_plane->physical_bitmap,phys_plane->physical_page_count,start_reg,size_reg,SH_FALSE); if (sh_status_error(status)) return status; // identifying neighbours // left sh_page_VIRTUAL_ADDRESS left_neighbour; status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,start_reg-1,&left_neighbour); if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; sh_bool left_fusion; if (status==SH_STATUS_NOT_FOUND) { left_fusion=SH_FALSE; } else { left_fusion=SH_TRUE; } // right sh_page_VIRTUAL_ADDRESS right_neighbour; status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,start_reg+size_reg,&right_neighbour); if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; sh_bool right_fusion; if (status==SH_STATUS_NOT_FOUND) { right_fusion=SH_FALSE; } else { right_fusion=SH_TRUE; } // preparing fusion sh_uint32 new_start=start_reg; sh_uint32 new_size=size_reg; // fusionning left region if (left_fusion) { // obtaining information about left region sh_pez_internal_BOUNDARY_ENTRY left_boundary=sh_pez_internal_unpack_boundary(left_neighbour); sh_slab_reg_phys_OBJECT_INDEX left_reg_idx=left_boundary.region_index; sh_pez_REGION_PHYSICAL_OBJECT *left_reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,left_reg_idx); if (left_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; // updating new region informations new_start=left_reg->start_page_index; if (new_start==0 || new_start>sh_page_get_physical_memory_amount_pages()) return SH_STATUS_PEZ_CORRUPTED; new_size=new_size+left_reg->region_size_pages; if (new_size==0 || new_size>sh_page_get_physical_memory_amount_pages()) return SH_STATUS_PEZ_CORRUPTED; // deleting left region status=sh_pez_internal_delete_free_region_physical(phys_plane,left_reg); if (sh_status_error(status)) return status; } // fusionning right region if (right_fusion) { // obtaining information about right region sh_pez_internal_BOUNDARY_ENTRY right_boundary=sh_pez_internal_unpack_boundary(right_neighbour); sh_slab_reg_phys_OBJECT_INDEX right_reg_idx=right_boundary.region_index; sh_pez_REGION_PHYSICAL_OBJECT *right_reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,right_reg_idx); if (right_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; // updating new region informations new_size=new_size+right_reg->region_size_pages; if (new_size==0 || new_size>sh_page_get_physical_memory_amount_pages()) return SH_STATUS_PEZ_CORRUPTED; // deleting right region status=sh_pez_internal_delete_free_region_physical(phys_plane,right_reg); if (sh_status_error(status)) return status; } // inserting new region sh_pez_REGION_PHYSICAL_OBJECT *new_reg; status=sh_pez_internal_create_free_region_physical(phys_plane,new_start,new_size,&new_reg); if (sh_status_error(status)) return status; phys_plane->free_pages=phys_plane->free_pages+pages_count; phys_plane->used_pages=phys_plane->used_pages-pages_count; *address=0; return SH_STATUS_SUCCESS; } SH_STATUS sh_pez_debug_physical(sh_pez_PHYSICAL_PLANE *phys_plane) { sh_pez_internal_check_physical_radix(phys_plane); sh_pez_internal_scan_physical_bitmap(phys_plane); sh_pez_internal_scan_size_list_physical(phys_plane); sh_pez_internal_scan_boundary_radix_physical(phys_plane); return SH_STATUS_SUCCESS; } static SH_STATUS sh_pez_internal_create_free_region_virtual(sh_pez_VIRTUAL_PLANE *virt_plane,sh_uint32 region_start,sh_uint32 region_size,sh_pez_REGION_VIRTUAL_OBJECT **region) { if (virt_plane==SH_NULLPTR || region_size==0 || region==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_slab_reg_virt_OBJECT_INDEX reg_idx; SH_STATUS status=sh_slab_reg_virt_alloc(virt_plane->slab_reg_virt,virt_plane->kernel_ptp,®_idx); if (sh_status_error(status)) return status; sh_pez_REGION_VIRTUAL_OBJECT *reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,reg_idx); if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; reg->start_page_index=region_start; reg->region_size_pages=region_size; sh_page_VIRTUAL_ADDRESS head_index=0; status=sh_radix_tree_get_value(&virt_plane->region_radix_tree,(sh_uint64)region_size,&head_index); if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; sh_page_VIRTUAL_ADDRESS new_boundary_val=sh_pez_internal_pack_boundary(reg_idx,0); status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)region_start,new_boundary_val); if (sh_status_error(status)) return status; status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)region_start+region_size-1,new_boundary_val); if (sh_status_error(status)) return status; if (head_index!=0) { sh_pez_REGION_PHYSICAL_OBJECT *old_head_ptr=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,(sh_uint32)head_index); if (old_head_ptr==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; sh_page_VIRTUAL_ADDRESS updated_old_head_boundary=sh_pez_internal_pack_boundary((sh_uint32)head_index,reg_idx); status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index,updated_old_head_boundary); if (sh_status_error(status)) return status; sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index+old_head_ptr->region_size_pages-1,updated_old_head_boundary); if (sh_status_error(status)) return status; reg->next_region_index=(sh_uint32)head_index; } else { reg->next_region_index=0; } status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->region_radix_tree,(sh_uint64)region_size,(sh_page_VIRTUAL_ADDRESS)reg_idx); if (sh_status_error(status)) return status; *region=reg; return SH_STATUS_SUCCESS; } static SH_STATUS sh_pez_internal_check_region_integrity_virtual(sh_pez_VIRTUAL_PLANE *virt_plane,sh_pez_REGION_VIRTUAL_OBJECT *reg,sh_bool *optional_is_first,sh_slab_reg_virt_OBJECT_INDEX *optional_reg_idx,sh_slab_reg_virt_OBJECT_INDEX *optional_prev_idx) { if (virt_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_page_VIRTUAL_ADDRESS boundary_left; sh_page_VIRTUAL_ADDRESS boundary_right; SH_STATUS status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,reg->start_page_index,&boundary_left); if (sh_status_error(status)) return status; status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1,&boundary_right); if (sh_status_error(status)) return status; if (boundary_left!=boundary_right) return SH_STATUS_PEZ_CORRUPTED; sh_pez_internal_BOUNDARY_ENTRY boundary=sh_pez_internal_unpack_boundary(boundary_left); sh_bool should_be_first; if (boundary.previous_index==0) { should_be_first=SH_TRUE; } else { should_be_first=SH_FALSE; } sh_page_VIRTUAL_ADDRESS reg_idx; status=sh_radix_tree_get_value(&virt_plane->region_radix_tree,reg->region_size_pages,®_idx); if (sh_status_error(status)) return status; sh_pez_REGION_VIRTUAL_OBJECT *region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,(sh_uint32)reg_idx); if (region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; if ((!should_be_first && region==reg) || (should_be_first && region!=reg)) { return SH_STATUS_PEZ_CORRUPTED; } if (optional_is_first!=SH_NULLPTR) { *optional_is_first=should_be_first; } if (optional_reg_idx!=SH_NULLPTR) { *optional_reg_idx=boundary.region_index; } if (optional_prev_idx!=SH_NULLPTR) { *optional_prev_idx=boundary.previous_index; } return SH_STATUS_SUCCESS; } static SH_STATUS sh_pez_internal_delete_free_region_virtual(sh_pez_VIRTUAL_PLANE *virt_plane,sh_pez_REGION_VIRTUAL_OBJECT *reg) { if (virt_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_bool is_first; sh_slab_reg_virt_OBJECT_INDEX reg_idx; sh_slab_reg_virt_OBJECT_INDEX prev_idx; SH_STATUS status=sh_pez_internal_check_region_integrity_virtual(virt_plane,reg,&is_first,®_idx,&prev_idx); if (sh_status_error(status)) return status; if (is_first) { // if region is the first of the list if (reg->next_region_index==0) { // if region was alone in his list // delete radix entry status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->region_radix_tree,reg->region_size_pages); if (sh_status_error(status)) return status; // delete left boundary status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index); if (sh_status_error(status)) return status; // delete right boundary if (reg->region_size_pages!=1) { status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); if (sh_status_error(status)) return status; } // delete region object status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx); if (sh_status_error(status)) return status; return SH_STATUS_SUCCESS; } else { // if region isn't alone in his list // obtain next region informations sh_slab_reg_virt_OBJECT_INDEX next_reg_idx=reg->next_region_index; sh_pez_REGION_VIRTUAL_OBJECT *next_region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,next_reg_idx); if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; // insert next region as first inside size list status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->region_radix_tree,reg->region_size_pages,next_reg_idx); if (sh_status_error(status)) return status; // update left boundary of next region sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,0); status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region); if (sh_status_error(status)) return status; // update right boundary of next region status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region); if (sh_status_error(status)) return status; // delete left boundary status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index); if (sh_status_error(status)) return status; // delete right boundary if (reg->region_size_pages!=1) { status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); if (sh_status_error(status)) return status; } // delete region object status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx); if (sh_status_error(status)) return status; return SH_STATUS_SUCCESS; } } else { // if region isn't the first of his list // obtain information about prev region sh_pez_REGION_VIRTUAL_OBJECT *prev_region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,prev_idx); if (prev_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; if (reg->next_region_index==0) { // if region is at the end of the size list // put next_idx of prev region to 0 prev_region->next_region_index=0; // delete left boundary status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index); if (sh_status_error(status)) return status; // delete right boundary if (reg->region_size_pages!=1) { status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); if (sh_status_error(status)) return status; } // delete region object status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx); if (sh_status_error(status)) return status; return SH_STATUS_SUCCESS; } else { // if region is in the middle of the size list // obtain information about next region sh_slab_reg_virt_OBJECT_INDEX next_reg_idx=reg->next_region_index; sh_pez_REGION_VIRTUAL_OBJECT *next_region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,next_reg_idx); if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; // put next_idx of prev region to next_region_idx prev_region->next_region_index=next_reg_idx; // update left boundary of next region sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,prev_idx); status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region); if (sh_status_error(status)) return status; // update right boundary of next region status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region); if (sh_status_error(status)) return status; // delete left boundary status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index); if (sh_status_error(status)) return status; // delete right boundary if (reg->region_size_pages!=1) { status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); if (sh_status_error(status)) return status; } // delete region object status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx); if (sh_status_error(status)) return status; return SH_STATUS_SUCCESS; } } } SH_STATUS sh_pez_init_virtual_plane(sh_page_VIRTUAL_ADDRESS plane_offset,sh_slab_reg_virt_SLAB_ALLOCATOR *slab_reg_virt,struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_page_PAGE_TABLE_POOL *reference_ptp,sh_pez_VIRTUAL_PLANE *virt_plane) { if (slab_reg_virt==SH_NULLPTR || slab_radix_node==SH_NULLPTR || kernel_ptp==SH_NULLPTR || reference_ptp==SH_NULLPTR || virt_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_radix_tree_init(slab_radix_node,kernel_ptp,&virt_plane->region_radix_tree,8); sh_radix_tree_init(slab_radix_node,kernel_ptp,&virt_plane->boundary_radix_tree,8); virt_plane->free_pages=0xFFFFFFFF-1; virt_plane->used_pages=0; virt_plane->slab_reg_virt=slab_reg_virt; virt_plane->slab_radix_node=slab_radix_node; virt_plane->kernel_ptp=kernel_ptp; virt_plane->reference_ptp=reference_ptp; virt_plane->plane_offset=plane_offset; sh_pez_REGION_VIRTUAL_OBJECT *reg; SH_STATUS status=sh_pez_internal_create_free_region_virtual(virt_plane,0,0xFFFFFFFF-1,®); if (sh_status_error(status)) return status; return SH_STATUS_SUCCESS; } SH_STATUS sh_pez_alloc_virtual_pages(sh_pez_VIRTUAL_PLANE *virt_plane,sh_uint32 pages_count,sh_page_VIRTUAL_ADDRESS *address) { if (virt_plane==SH_NULLPTR || pages_count==0 || address==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; sh_page_VIRTUAL_ADDRESS reg_idx; SH_STATUS status=sh_radix_tree_search_smallest_min_bound(virt_plane->slab_radix_node,&virt_plane->region_radix_tree,pages_count,®_idx); if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; if (status==SH_STATUS_NOT_FOUND) return SH_STATUS_OUT_OF_MEMORY; sh_pez_REGION_VIRTUAL_OBJECT *reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,(sh_uint32)reg_idx); if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; if (reg->region_size_pagesregion_size_pages==pages_count) { // exact fit case // save metadata for later modifications sh_uint32 start=reg->start_page_index; // delete free region status=sh_pez_internal_delete_free_region_virtual(virt_plane,reg); if (sh_status_error(status)) return status; // give the address *address=((sh_page_VIRTUAL_ADDRESS)start)*SH_PAGE_SIZE+virt_plane->plane_offset; } else if (reg->region_size_pages>pages_count) { // best fit case // save metadata for later modifications sh_uint32 start=reg->start_page_index; // compute new size and index sh_uint32 new_start=reg->start_page_index+pages_count; if (new_start==0) return SH_STATUS_PEZ_CORRUPTED; sh_uint32 new_size=reg->region_size_pages-pages_count; if (new_size==0) return SH_STATUS_PEZ_CORRUPTED; // delete free region status=sh_pez_internal_delete_free_region_virtual(virt_plane,reg); if (sh_status_error(status)) return status; // create new free region with splitted part sh_pez_REGION_VIRTUAL_OBJECT *new_reg; status=sh_pez_internal_create_free_region_virtual(virt_plane,new_start,new_size,&new_reg); if (sh_status_error(status)) return status; // give the address *address=((sh_page_VIRTUAL_ADDRESS)start)*SH_PAGE_SIZE+virt_plane->plane_offset; } virt_plane->free_pages=virt_plane->free_pages-pages_count; virt_plane->used_pages=virt_plane->used_pages+pages_count; return SH_STATUS_SUCCESS; } SH_STATUS sh_pez_free_virtual_pages(sh_pez_VIRTUAL_PLANE *virt_plane,sh_page_VIRTUAL_ADDRESS *address,sh_uint32 pages_count) { if (virt_plane==SH_NULLPTR || address==SH_NULLPTR || pages_count==0) return SH_STATUS_INVALID_PARAMETER; // save freeed region data sh_uint32 start_reg=(sh_uint32)((*address)/SH_PAGE_SIZE-virt_plane->plane_offset); sh_uint32 size_reg=pages_count; // identifying neighbours // left sh_page_VIRTUAL_ADDRESS left_neighbour; SH_STATUS status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,start_reg-1,&left_neighbour); if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; sh_bool left_fusion; if (status==SH_STATUS_NOT_FOUND) { left_fusion=SH_FALSE; } else { left_fusion=SH_TRUE; } // right sh_page_VIRTUAL_ADDRESS right_neighbour; status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,start_reg+size_reg,&right_neighbour); if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; sh_bool right_fusion; if (status==SH_STATUS_NOT_FOUND) { right_fusion=SH_FALSE; } else { right_fusion=SH_TRUE; } // preparing fusion sh_uint32 new_start=start_reg; sh_uint32 new_size=size_reg; // fusionning left region if (left_fusion) { // obtaining information about left region sh_pez_internal_BOUNDARY_ENTRY left_boundary=sh_pez_internal_unpack_boundary(left_neighbour); sh_slab_reg_virt_OBJECT_INDEX left_reg_idx=left_boundary.region_index; sh_pez_REGION_VIRTUAL_OBJECT *left_reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,left_reg_idx); if (left_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; // updating new region informations new_start=left_reg->start_page_index; new_size=new_size+left_reg->region_size_pages; if (new_size==0) return SH_STATUS_PEZ_CORRUPTED; // deleting left region status=sh_pez_internal_delete_free_region_virtual(virt_plane,left_reg); if (sh_status_error(status)) return status; } // fusionning right region if (right_fusion) { // obtaining information about right region sh_pez_internal_BOUNDARY_ENTRY right_boundary=sh_pez_internal_unpack_boundary(right_neighbour); sh_slab_reg_virt_OBJECT_INDEX right_reg_idx=right_boundary.region_index; sh_pez_REGION_VIRTUAL_OBJECT *right_reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,right_reg_idx); if (right_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; // updating new region informations new_size=new_size+right_reg->region_size_pages; if (new_size==0) return SH_STATUS_PEZ_CORRUPTED; // deleting right region status=sh_pez_internal_delete_free_region_virtual(virt_plane,right_reg); if (sh_status_error(status)) return status; } // inserting new region sh_pez_REGION_VIRTUAL_OBJECT *new_reg; status=sh_pez_internal_create_free_region_virtual(virt_plane,new_start,new_size,&new_reg); if (sh_status_error(status)) return status; virt_plane->free_pages=virt_plane->free_pages+pages_count; virt_plane->used_pages=virt_plane->used_pages-pages_count; *address=0; return SH_STATUS_SUCCESS; }