// print buddy system status //void dump_print(struct mem_zone * zone); //void dump_print_dot(struct mem_zone * zone); //---###--- public static void buddy_system_init(mem_zone *zone, page *start_page, uint start_addr, uint page_num) { uint i; page * page = null; free_area *area = null; // init memory zone zone->page_num = page_num; zone->page_size = BUDDY_PAGE_SIZE; zone->first_page = start_page; zone->start_addr = start_addr; zone->end_addr = start_addr + (page_num * BUDDY_PAGE_SIZE); // TODO: init zone->lock // init each area for (i = 0; i < BUDDY_MAX_ORDER; i++) { area = zone->free_area + i; INIT_LIST_HEAD(&area->free_list); area->nr_free = 0; } memset((byte *)start_page, 0, page_num * (uint)sizeof(page)); // init and free each page for (i = 0; i < page_num; i++) { page = zone->first_page + i; INIT_LIST_HEAD(&page->lru); // TODO: init page->lock buddy_free_pages(zone, page); } }
//--- // The Linux kernel records the order of the combined page in the prev pointer of the second page. // This system records the order of the combined page in the page->order field of the first page. static byte compound_order(page *page) { if (!PageHead(page)) { return(0); // single page } return(page->order); }
static bool page_is_buddy(page *page, int order) { if (PagePrivate(page) && (page_order(page) == order) && !PageReserved(page) && page_count(page) == 0) { return(true); } return(false); }
//////////////////////////////////////////////// public static void *page_to_virt(mem_zone *zone, page *page) { uint page_idx = 0; uint address = 0; page_idx = (uint)(page - zone->first_page); address = zone->start_addr + (page_idx * BUDDY_PAGE_SIZE); return((void *)address); }
/// <summary> /// Set the properties of the combined page /// </summary> static void prepare_compound_pages(page *page, byte order) { uint i; uint nr_pages = (1U << order); // The first page record combination page order value set_compound_order(page, order); __SetPageHead(page); // home page set head flag for (i = 1; i < nr_pages; i++) { page *p = page + i; __SetPageTail(p); // The rest of the pages set the tail flag p->first_page = page; } }
public static page *buddy_get_pages(mem_zone *zone, byte order) { page *page = null; if (order >= BUDDY_MAX_ORDER) { BUDDY_BUG("error"); return(null); } //TODO: lock zone->lock page = __alloc_page(order, zone); //TODO: unlock zone->lock return(page); }
static page *expand(zone *zone, page *page, int low, int high, free_area *area) { uint size = (uint)1 << (byte)high; while (high > low) { area--; high--; size >>= 1; //BUG_ON(bad_range(zone, &page[size])); list_add(&page[size].Lru, &area->Free_list); area->Nr_free++; set_page_order(&page[size], high); } return(page); }
static page *buffered_rmqueue(zone *zone, int order, int gfp_flags) { ulong flags; page *page = null; //int cold = !!(gfp_flags & __GFP_COLD); //if (order == 0) //{ // per_cpu_pages* pcp; // pcp = &zone->pageset[get_cpu()].pcp[cold]; // local_irq_save(flags); // if (pcp->count <= pcp->low) // pcp->count += rmqueue_bulk(zone, 0, // pcp->batch, &pcp->list); // if (pcp->count) // { // page = list_entry(pcp->list.next, page, lru); // list_del(&page->lru); // pcp->count--; // } // local_irq_restore(flags); // put_cpu(); //} //if (page == null) //{ // //spin_lock_irqsave(&zone->lock_, flags); // page = __rmqueue(zone, order); // //spin_unlock_irqrestore(&zone->lock_, flags); //} //if (page != null) //{ // BUG_ON(bad_range(zone, page)); // //mod_page_state_zone(zone, pgalloc, 1 << order); // prep_new_page(page, order); // if (gfp_flags & __GFP_ZERO) // prep_zero_page(page, order, gfp_flags); // if (order && (gfp_flags & __GFP_COMP)) // prep_compound_page(page, order); //} return(page); }
/// <summary> /// Split the combined page to get the page of the desired size /// </summary> static void expand(mem_zone *zone, page *page, byte low_order, byte high_order, free_area *area) { uint size = (1U << high_order); while (high_order > low_order) { area--; high_order--; size >>= 1; list_add(&page[size].lru, &area->free_list); area->nr_free++; // set page order set_page_order_buddy(&page[size], high_order); } }
public static page *virt_to_page(mem_zone *zone, void *ptr) { uint page_idx = 0; page *page = null; uint address = (uint)ptr; if ((address < zone->start_addr) || (address > zone->end_addr)) { //printf("start_addr=0x%lx, end_addr=0x%lx, address=0x%lx\n", // zone->start_addr, zone->end_addr, address); BUDDY_BUG("error"); return(null); } page_idx = (address - zone->start_addr) >> BUDDY_PAGE_SHIFT; page = zone->first_page + page_idx; return(page); }
//////////////////////////////////////////////// /// <summary> /// Destroy the combined page /// </summary> static bool destroy_compound_pages(page *page, byte order) { int bad = 0; uint i; uint nr_pages = (1U << order); __ClearPageHead(page); for (i = 1; i < nr_pages; i++) { page *p = page + i; if (!PageTail(p) || p->first_page != page) { bad++; BUDDY_BUG("error"); } __ClearPageTail(p); } return(bad != 0); }
public static void buddy_free_pages(mem_zone *zone, page *page) { byte order = compound_order(page); uint buddy_idx = 0, combinded_idx = 0; uint page_idx = (uint)(page - zone->first_page); //TODO: lock zone->lock if (PageCompound(page)) { if (destroy_compound_pages(page, order)) { BUDDY_BUG("error"); } } while (order < BUDDY_MAX_ORDER - 1) { page *buddy; // find and delete buddy to combine buddy_idx = __find_buddy_index(page_idx, order); buddy = page + (buddy_idx - page_idx); if (!page_is_buddy(buddy, order)) { break; } list_del(&buddy->lru); zone->free_area[order].nr_free--; // remove buddy's flag and order rmv_page_order_buddy(buddy); // update page and page_idx after combined combinded_idx = __find_combined_index(page_idx, order); page = page + (combinded_idx - page_idx); page_idx = combinded_idx; order++; } set_page_order_buddy(page, order); list_add(&page->lru, &zone->free_area[order].free_list); zone->free_area[order].nr_free++; //TODO: unlock zone->lock }
static page *__alloc_page(byte order, mem_zone *zone) { page * page = null; free_area *area = null; byte current_order = 0; for (current_order = order; current_order < BUDDY_MAX_ORDER; current_order++) { area = zone->free_area + current_order; if (list_empty(&area->free_list)) { continue; } // remove closest size page //page = list_entry(area->free_list.next, struct page, lru); page = (page *)&((page *)area->free_list.next)->lru; list_del(&page->lru); rmv_page_order_buddy(page); area->nr_free--; // expand to lower order expand(zone, page, order, current_order, area); // compound page if (order > 0) { prepare_compound_pages(page, order); } else // single page { page->order = 0; } return(page); } return(null); }
private static bool PageCompound(page *page) { return((page->flags & ((1U << (byte)pageflags.PG_head) | (1U << (byte)pageflags.PG_tail))) != 0); }
static bool PagePrivate(page *page) { return(page->Flags.IsBitSet(PG_private)); }
static uint page_order(page *page) { return(page->Private_); }
static void __SetPageBuddy(page *page) { page->flags |= (1U << (byte)pageflags.PG_buddy); }
static void set_page_order(page *page, int order) { page->Private_ = (byte)order; page->Flags = page->Flags.SetBit(PG_private); }
static void set_compound_order(page *page, byte order) { //page[1].lru.prev = (void *)order; page->order = order; }
// The page is divided into two categories: one is a single page(zero page). // One type is a compound page. // The first of the combined pages is head and the rest is tail. static void __SetPageHead(page *page) { page->flags |= (1U << (byte)pageflags.PG_head); }
static void rmv_page_order_buddy(page *page) { page->order = 0; __ClearPageBuddy(page); }
static void rmv_page_order(page *page) { page->Flags = page->Flags.ClearBit(PG_private); page->Private_ = 0; }
static bool PageBuddy(page *page) { return((page->flags & (1U << (byte)pageflags.PG_buddy)) != 0); }
static bool PageTail(page *page) { return((page->flags & (1U << (byte)pageflags.PG_tail)) != 0); }
/**/ static void __ClearPageHead(page *page) { page->flags &= ~(1U << (byte)pageflags.PG_head); } static void __ClearPageTail(page *page) { page->flags &= ~(1U << (byte)pageflags.PG_tail); } static void __ClearPageBuddy(page *page) { page->flags &= ~(1U << (byte)pageflags.PG_buddy); } /**/ static bool PageHead(page *page) { return((page->flags & (1U << (byte)pageflags.PG_head)) != 0); }
private static bool page_is_buddy(page *page, byte order) { return(PageBuddy(page) && (page->order == order)); }
static uint page_count(page *page) { return(page->_count + 1); }
static bool PageReserved(page *page) { return(page->Flags.IsBitSet(PG_reserved)); }
// Set the page's order and PG_buddy flags static void set_page_order_buddy(page *page, byte order) { page->order = order; __SetPageBuddy(page); }
static void __SetPageTail(page *page) { page->flags |= (1U << (byte)pageflags.PG_tail); }