/** * cmm_free_pages - Free pages and mark them as active * @nr: number of pages to free * * Return value: * number of pages requested to be freed which were not **/ static long cmm_free_pages(long nr) { struct cmm_page_array *pa; unsigned long addr; cmm_dbg("Begin free of %ld pages.\n", nr); spin_lock(&cmm_lock); pa = cmm_page_list; while (nr) { if (!pa || pa->index <= 0) break; addr = pa->page[--pa->index]; if (pa->index == 0) { pa = pa->next; free_page((unsigned long) cmm_page_list); cmm_page_list = pa; } plpar_page_set_active(__pa(addr)); free_page(addr); loaned_pages--; nr--; totalram_pages++; } spin_unlock(&cmm_lock); cmm_dbg("End request with %ld pages unfulfilled\n", nr); return nr; }
/** * cmm_mem_going_offline - Unloan pages where memory is to be removed * @arg: memory_notify structure with page range to be offlined * * Return value: * 0 on success **/ static int cmm_mem_going_offline(void *arg) { struct memory_notify *marg = arg; unsigned long start_page = (unsigned long)pfn_to_kaddr(marg->start_pfn); unsigned long end_page = start_page + (marg->nr_pages << PAGE_SHIFT); struct cmm_page_array *pa_curr, *pa_last, *npa; unsigned long idx; unsigned long freed = 0; cmm_dbg("Memory going offline, searching 0x%lx (%ld pages).\n", start_page, marg->nr_pages); spin_lock(&cmm_lock); /* Search the page list for pages in the range to be offlined */ pa_last = pa_curr = cmm_page_list; while (pa_curr) { for (idx = (pa_curr->index - 1); (idx + 1) > 0; idx--) { if ((pa_curr->page[idx] < start_page) || (pa_curr->page[idx] >= end_page)) continue; plpar_page_set_active(__pa(pa_curr->page[idx])); free_page(pa_curr->page[idx]); freed++; loaned_pages--; totalram_pages++; pa_curr->page[idx] = pa_last->page[--pa_last->index]; if (pa_last->index == 0) { if (pa_curr == pa_last) pa_curr = pa_last->next; pa_last = pa_last->next; free_page((unsigned long)cmm_page_list); cmm_page_list = pa_last; continue; } } pa_curr = pa_curr->next; } /* Search for page list structures in the range to be offlined */ pa_last = NULL; pa_curr = cmm_page_list; while (pa_curr) { if (((unsigned long)pa_curr >= start_page) && ((unsigned long)pa_curr < end_page)) { npa = (struct cmm_page_array *)__get_free_page( GFP_NOIO | __GFP_NOWARN | __GFP_NORETRY | __GFP_NOMEMALLOC); if (!npa) { spin_unlock(&cmm_lock); cmm_dbg("Failed to allocate memory for list " "management. Memory hotplug " "failed.\n"); return ENOMEM; } memcpy(npa, pa_curr, PAGE_SIZE); if (pa_curr == cmm_page_list) cmm_page_list = npa; if (pa_last) pa_last->next = npa; free_page((unsigned long) pa_curr); freed++; pa_curr = npa; } pa_last = pa_curr; pa_curr = pa_curr->next; } spin_unlock(&cmm_lock); cmm_dbg("Released %ld pages in the search range.\n", freed); return 0; }