/****************************************************************************** * Xen balloon driver - enables returning/claiming memory to/from Xen. * * Copyright (c) 2003, B Dragovic * Copyright (c) 2003-2004, M Williamson, K Fraser * Copyright (c) 2005 Dan M. Smith, IBM Corporation * Copyright (c) 2010 Daniel Kiper * * Memory hotplug support was written by Daniel Kiper. Work on * it was sponsored by Google under Google Summer of Code 2010 * program. Jeremy Fitzhardinge from Citrix was the mentor for * this project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation; or, when distributed * separately from the Linux kernel or incorporated into other * software packages, subject to the following license: * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this source file (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE.
*/
/* We increase/decrease in batches which fit in a page */ static xen_pfn_t frame_list[PAGE_SIZE / sizeof(xen_pfn_t)];
/* List of ballooned pages, threaded through the mem_map array. */ static LIST_HEAD(ballooned_pages); static DECLARE_WAIT_QUEUE_HEAD(balloon_wq);
/* When ballooning out (allocating memory to return to Xen) we don't really
want the kernel to try too hard since that can trigger the oom killer. */ #define GFP_BALLOON \
(GFP_HIGHUSER | __GFP_NOWARN | __GFP_NORETRY | __GFP_NOMEMALLOC)
/* balloon_append: add the given page to the balloon. */ staticvoid balloon_append(struct page *page)
{ if (!PageOffline(page))
__SetPageOffline(page);
/* Lowmem is re-populated first, so highmem pages go at list tail. */ if (PageHighMem(page)) {
list_add_tail(&page->lru, &ballooned_pages);
balloon_stats.balloon_high++;
} else {
list_add(&page->lru, &ballooned_pages);
balloon_stats.balloon_low++;
}
inc_node_page_state(page, NR_BALLOON_PAGES);
wake_up(&balloon_wq);
}
/* balloon_retrieve: rescue a page from the balloon, if it is not empty. */ staticstruct page *balloon_retrieve(bool require_lowmem)
{ struct page *page;
if (balloon_stats.schedule_delay > balloon_stats.max_schedule_delay)
balloon_stats.schedule_delay = balloon_stats.max_schedule_delay;
balloon_state = BP_EAGAIN;
}
#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG staticvoid release_memory_resource(struct resource *resource)
{ if (!resource) return;
/* * No need to reset region to identity mapped since we now * know that no I/O can be in this region
*/
release_resource(resource);
kfree(resource);
}
staticstruct resource *additional_memory_resource(phys_addr_t size)
{ struct resource *res; int ret;
res = kzalloc(sizeof(*res), GFP_KERNEL); if (!res) return NULL;
resource = additional_memory_resource(balloon_hotplug * PAGE_SIZE); if (!resource) goto err;
nid = memory_add_physaddr_to_nid(resource->start);
#ifdef CONFIG_XEN_HAVE_PVMMU /* * We don't support PV MMU when Linux and Xen is using * different page granularity.
*/
BUILD_BUG_ON(XEN_PAGE_SIZE != PAGE_SIZE);
/* * add_memory() will build page tables for the new memory so * the p2m must contain invalid entries so the correct * non-present PTEs will be written. * * If a failure occurs, the original (identity) p2m entries * are not restored since this region is now known not to * conflict with any devices.
*/ if (!xen_feature(XENFEAT_auto_translated_physmap)) { unsignedlong pfn, i;
pfn = PFN_DOWN(resource->start); for (i = 0; i < balloon_hotplug; i++) { if (!set_phys_to_machine(pfn + i, INVALID_P2M_ENTRY)) {
pr_warn("set_phys_to_machine() failed, no memory added\n"); goto err;
}
}
} #endif
/* * add_memory_resource() will call online_pages() which in its turn * will call xen_online_page() callback causing deadlock if we don't * release balloon_mutex here. Unlocking here is safe because the * callers drop the mutex before trying again.
*/
mutex_unlock(&balloon_mutex); /* add_memory_resource() requires the device_hotplug lock */
lock_device_hotplug();
rc = add_memory_resource(nid, resource, MHP_MERGE_RESOURCE);
unlock_device_hotplug();
mutex_lock(&balloon_mutex);
/* * Relinquish the page back to the allocator. Note that * some pages, including ones added via xen_online_page(), might * not be marked reserved; free_reserved_page() will handle that.
*/
free_reserved_page(page);
}
if (nr_pages > ARRAY_SIZE(frame_list))
nr_pages = ARRAY_SIZE(frame_list);
for (i = 0; i < nr_pages; i++) {
page = alloc_page(gfp); if (page == NULL) {
nr_pages = i;
state = BP_EAGAIN; break;
}
adjust_managed_page_count(page, -1);
xenmem_reservation_scrub_page(page);
list_add(&page->lru, &pages);
}
/* * Ensure that ballooned highmem pages don't have kmaps. * * Do this before changing the p2m as kmap_flush_unused() * reads PTEs to obtain pages (and hence needs the original * p2m entry).
*/
kmap_flush_unused();
/* * Setup the frame, update direct mapping, invalidate P2M, * and add to balloon.
*/
i = 0;
list_for_each_entry_safe(page, tmp, &pages, lru) {
frame_list[i++] = xen_page_to_gfn(page);
xenmem_reservation_va_mapping_reset(1, &page);
list_del(&page->lru);
balloon_append(page);
}
flush_tlb_all();
ret = xenmem_reservation_decrease(nr_pages, frame_list);
BUG_ON(ret != nr_pages);
balloon_stats.current_pages -= nr_pages;
return state;
}
/* * Stop waiting if either state is BP_DONE and ballooning action is * needed, or if the credit has changed while state is not BP_DONE.
*/ staticbool balloon_thread_cond(long credit)
{ if (balloon_state == BP_DONE)
credit = 0;
/* * As this is a kthread it is guaranteed to run as a single instance only. * We may of course race updates of the target counts (which are protected * by the balloon lock), or with changes to the Xen hard limit, but we will * recover from these in time.
*/ staticint balloon_thread(void *unused)
{ long credit; unsignedlong timeout;
set_freezable(); for (;;) { switch (balloon_state) { case BP_DONE: case BP_ECANCELED:
timeout = 3600 * HZ; break; case BP_EAGAIN:
timeout = balloon_stats.schedule_delay * HZ; break; case BP_WAIT:
timeout = HZ; break;
}
/* Resets the Xen limit, sets new target, and kicks off processing. */ void balloon_set_new_target(unsignedlong target)
{ /* No need for lock. Not read-modify-write updates. */
balloon_stats.target_pages = target;
wake_up(&balloon_thread_wq);
}
EXPORT_SYMBOL_GPL(balloon_set_new_target);
if (si_mem_available() < nr_pages) return -ENOMEM;
st = decrease_reservation(nr_pages, GFP_USER); if (st != BP_DONE) return -ENOMEM;
return 0;
}
/** * xen_alloc_ballooned_pages - get pages that have been ballooned out * @nr_pages: Number of pages to get * @pages: pages returned * @return 0 on success, error otherwise
*/ int xen_alloc_ballooned_pages(unsignedint nr_pages, struct page **pages)
{ unsignedint pgno = 0; struct page *page; int ret;
mutex_lock(&balloon_mutex);
balloon_stats.target_unpopulated += nr_pages;
while (pgno < nr_pages) {
page = balloon_retrieve(true); if (page) {
pages[pgno++] = page; #ifdef CONFIG_XEN_HAVE_PVMMU /* * We don't support PV MMU when Linux and Xen is using * different page granularity.
*/
BUILD_BUG_ON(XEN_PAGE_SIZE != PAGE_SIZE);
if (!xen_feature(XENFEAT_auto_translated_physmap)) {
ret = xen_alloc_p2m_entry(page_to_pfn(page)); if (ret < 0) goto out_undo;
} #endif
} else {
ret = add_ballooned_pages(nr_pages - pgno); if (ret < 0) goto out_undo;
}
}
mutex_unlock(&balloon_mutex); return 0;
out_undo:
mutex_unlock(&balloon_mutex);
xen_free_ballooned_pages(pgno, pages); /* * NB: xen_free_ballooned_pages will only subtract pgno pages, but since * target_unpopulated is incremented with nr_pages at the start we need * to remove the remaining ones also, or accounting will be screwed.
*/
balloon_stats.target_unpopulated -= nr_pages - pgno; return ret;
}
EXPORT_SYMBOL(xen_alloc_ballooned_pages);
/** * xen_free_ballooned_pages - return pages retrieved with get_ballooned_pages * @nr_pages: Number of pages * @pages: pages to return
*/ void xen_free_ballooned_pages(unsignedint nr_pages, struct page **pages)
{ unsignedint i;
mutex_lock(&balloon_mutex);
for (i = 0; i < nr_pages; i++) { if (pages[i])
balloon_append(pages[i]);
}
balloon_stats.target_unpopulated -= nr_pages;
/* The balloon may be too large now. Shrink it if needed. */ if (current_credit())
wake_up(&balloon_thread_wq);
for (i = 0; i < XEN_EXTRA_MEM_MAX_REGIONS; i++) {
pages = xen_extra_mem[i].n_pfns; if (!pages) continue;
start_pfn = xen_extra_mem[i].start_pfn;
/* * If the amount of usable memory has been limited (e.g., with * the 'mem' command line parameter), don't add pages beyond * this limit.
*/
extra_pfn_end = min(max_pfn, start_pfn + pages);
for (pfn = start_pfn; pfn < extra_pfn_end; pfn++)
balloon_append(pfn_to_page(pfn));
/* * Extra regions are accounted for in the physmap, but need * decreasing from current_pages and target_pages to balloon * down the initial allocation, because they are already * accounted for in total_pages.
*/
pages = extra_pfn_end - start_pfn; if (pages >= balloon_stats.current_pages ||
pages >= balloon_stats.target_pages) {
WARN(1, "Extra pages underflow current target"); return -ERANGE;
}
balloon_stats.current_pages -= pages;
balloon_stats.target_pages -= pages;
}
return 0;
}
staticint __init balloon_init(void)
{ struct task_struct *task; int rc;
if (!xen_domain()) return -ENODEV;
pr_info("Initialising balloon driver\n");
if (xen_released_pages >= get_num_physpages()) {
WARN(1, "Released pages underflow current target"); return -ERANGE;
}
task = kthread_run(balloon_thread, NULL, "xen-balloon"); if (IS_ERR(task)) {
pr_err("xen-balloon thread could not be started, ballooning will not work!\n"); return PTR_ERR(task);
}
/* Init the xen-balloon driver. */
xen_balloon_init();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.