xref: /openbmc/linux/drivers/xen/balloon.c (revision a419aef8)
11775826cSJeremy Fitzhardinge /******************************************************************************
21775826cSJeremy Fitzhardinge  * balloon.c
31775826cSJeremy Fitzhardinge  *
41775826cSJeremy Fitzhardinge  * Xen balloon driver - enables returning/claiming memory to/from Xen.
51775826cSJeremy Fitzhardinge  *
61775826cSJeremy Fitzhardinge  * Copyright (c) 2003, B Dragovic
71775826cSJeremy Fitzhardinge  * Copyright (c) 2003-2004, M Williamson, K Fraser
81775826cSJeremy Fitzhardinge  * Copyright (c) 2005 Dan M. Smith, IBM Corporation
91775826cSJeremy Fitzhardinge  *
101775826cSJeremy Fitzhardinge  * This program is free software; you can redistribute it and/or
111775826cSJeremy Fitzhardinge  * modify it under the terms of the GNU General Public License version 2
121775826cSJeremy Fitzhardinge  * as published by the Free Software Foundation; or, when distributed
131775826cSJeremy Fitzhardinge  * separately from the Linux kernel or incorporated into other
141775826cSJeremy Fitzhardinge  * software packages, subject to the following license:
151775826cSJeremy Fitzhardinge  *
161775826cSJeremy Fitzhardinge  * Permission is hereby granted, free of charge, to any person obtaining a copy
171775826cSJeremy Fitzhardinge  * of this source file (the "Software"), to deal in the Software without
181775826cSJeremy Fitzhardinge  * restriction, including without limitation the rights to use, copy, modify,
191775826cSJeremy Fitzhardinge  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
201775826cSJeremy Fitzhardinge  * and to permit persons to whom the Software is furnished to do so, subject to
211775826cSJeremy Fitzhardinge  * the following conditions:
221775826cSJeremy Fitzhardinge  *
231775826cSJeremy Fitzhardinge  * The above copyright notice and this permission notice shall be included in
241775826cSJeremy Fitzhardinge  * all copies or substantial portions of the Software.
251775826cSJeremy Fitzhardinge  *
261775826cSJeremy Fitzhardinge  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
271775826cSJeremy Fitzhardinge  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
281775826cSJeremy Fitzhardinge  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
291775826cSJeremy Fitzhardinge  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
301775826cSJeremy Fitzhardinge  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
311775826cSJeremy Fitzhardinge  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
321775826cSJeremy Fitzhardinge  * IN THE SOFTWARE.
331775826cSJeremy Fitzhardinge  */
341775826cSJeremy Fitzhardinge 
351775826cSJeremy Fitzhardinge #include <linux/kernel.h>
361775826cSJeremy Fitzhardinge #include <linux/module.h>
371775826cSJeremy Fitzhardinge #include <linux/sched.h>
381775826cSJeremy Fitzhardinge #include <linux/errno.h>
391775826cSJeremy Fitzhardinge #include <linux/mm.h>
401775826cSJeremy Fitzhardinge #include <linux/bootmem.h>
411775826cSJeremy Fitzhardinge #include <linux/pagemap.h>
421775826cSJeremy Fitzhardinge #include <linux/highmem.h>
431775826cSJeremy Fitzhardinge #include <linux/mutex.h>
441775826cSJeremy Fitzhardinge #include <linux/list.h>
451775826cSJeremy Fitzhardinge #include <linux/sysdev.h>
461775826cSJeremy Fitzhardinge 
471775826cSJeremy Fitzhardinge #include <asm/page.h>
481775826cSJeremy Fitzhardinge #include <asm/pgalloc.h>
491775826cSJeremy Fitzhardinge #include <asm/pgtable.h>
501775826cSJeremy Fitzhardinge #include <asm/uaccess.h>
511775826cSJeremy Fitzhardinge #include <asm/tlb.h>
521775826cSJeremy Fitzhardinge 
53ecbf29cdSJeremy Fitzhardinge #include <asm/xen/hypervisor.h>
54ecbf29cdSJeremy Fitzhardinge #include <asm/xen/hypercall.h>
55ecbf29cdSJeremy Fitzhardinge #include <xen/interface/xen.h>
561775826cSJeremy Fitzhardinge #include <xen/interface/memory.h>
571775826cSJeremy Fitzhardinge #include <xen/xenbus.h>
581775826cSJeremy Fitzhardinge #include <xen/features.h>
591775826cSJeremy Fitzhardinge #include <xen/page.h>
601775826cSJeremy Fitzhardinge 
611775826cSJeremy Fitzhardinge #define PAGES2KB(_p) ((_p)<<(PAGE_SHIFT-10))
621775826cSJeremy Fitzhardinge 
63167e6cf6SJeremy Fitzhardinge #define BALLOON_CLASS_NAME "xen_memory"
641775826cSJeremy Fitzhardinge 
651775826cSJeremy Fitzhardinge struct balloon_stats {
661775826cSJeremy Fitzhardinge 	/* We aim for 'current allocation' == 'target allocation'. */
671775826cSJeremy Fitzhardinge 	unsigned long current_pages;
681775826cSJeremy Fitzhardinge 	unsigned long target_pages;
691775826cSJeremy Fitzhardinge 	/* We may hit the hard limit in Xen. If we do then we remember it. */
701775826cSJeremy Fitzhardinge 	unsigned long hard_limit;
711775826cSJeremy Fitzhardinge 	/*
721775826cSJeremy Fitzhardinge 	 * Drivers may alter the memory reservation independently, but they
731775826cSJeremy Fitzhardinge 	 * must inform the balloon driver so we avoid hitting the hard limit.
741775826cSJeremy Fitzhardinge 	 */
751775826cSJeremy Fitzhardinge 	unsigned long driver_pages;
761775826cSJeremy Fitzhardinge 	/* Number of pages in high- and low-memory balloons. */
771775826cSJeremy Fitzhardinge 	unsigned long balloon_low;
781775826cSJeremy Fitzhardinge 	unsigned long balloon_high;
791775826cSJeremy Fitzhardinge };
801775826cSJeremy Fitzhardinge 
811775826cSJeremy Fitzhardinge static DEFINE_MUTEX(balloon_mutex);
821775826cSJeremy Fitzhardinge 
831775826cSJeremy Fitzhardinge static struct sys_device balloon_sysdev;
841775826cSJeremy Fitzhardinge 
851775826cSJeremy Fitzhardinge static int register_balloon(struct sys_device *sysdev);
861775826cSJeremy Fitzhardinge 
871775826cSJeremy Fitzhardinge /*
881775826cSJeremy Fitzhardinge  * Protects atomic reservation decrease/increase against concurrent increases.
891775826cSJeremy Fitzhardinge  * Also protects non-atomic updates of current_pages and driver_pages, and
901775826cSJeremy Fitzhardinge  * balloon lists.
911775826cSJeremy Fitzhardinge  */
921775826cSJeremy Fitzhardinge static DEFINE_SPINLOCK(balloon_lock);
931775826cSJeremy Fitzhardinge 
941775826cSJeremy Fitzhardinge static struct balloon_stats balloon_stats;
951775826cSJeremy Fitzhardinge 
961775826cSJeremy Fitzhardinge /* We increase/decrease in batches which fit in a page */
971775826cSJeremy Fitzhardinge static unsigned long frame_list[PAGE_SIZE / sizeof(unsigned long)];
981775826cSJeremy Fitzhardinge 
991775826cSJeremy Fitzhardinge /* VM /proc information for memory */
1001775826cSJeremy Fitzhardinge extern unsigned long totalram_pages;
1011775826cSJeremy Fitzhardinge 
1021775826cSJeremy Fitzhardinge #ifdef CONFIG_HIGHMEM
1031775826cSJeremy Fitzhardinge extern unsigned long totalhigh_pages;
1041775826cSJeremy Fitzhardinge #define inc_totalhigh_pages() (totalhigh_pages++)
1051775826cSJeremy Fitzhardinge #define dec_totalhigh_pages() (totalhigh_pages--)
1061775826cSJeremy Fitzhardinge #else
1071775826cSJeremy Fitzhardinge #define inc_totalhigh_pages() do {} while(0)
1081775826cSJeremy Fitzhardinge #define dec_totalhigh_pages() do {} while(0)
1091775826cSJeremy Fitzhardinge #endif
1101775826cSJeremy Fitzhardinge 
1111775826cSJeremy Fitzhardinge /* List of ballooned pages, threaded through the mem_map array. */
1121775826cSJeremy Fitzhardinge static LIST_HEAD(ballooned_pages);
1131775826cSJeremy Fitzhardinge 
1141775826cSJeremy Fitzhardinge /* Main work function, always executed in process context. */
1151775826cSJeremy Fitzhardinge static void balloon_process(struct work_struct *work);
1161775826cSJeremy Fitzhardinge static DECLARE_WORK(balloon_worker, balloon_process);
1171775826cSJeremy Fitzhardinge static struct timer_list balloon_timer;
1181775826cSJeremy Fitzhardinge 
1191775826cSJeremy Fitzhardinge /* When ballooning out (allocating memory to return to Xen) we don't really
1201775826cSJeremy Fitzhardinge    want the kernel to try too hard since that can trigger the oom killer. */
1211775826cSJeremy Fitzhardinge #define GFP_BALLOON \
1221775826cSJeremy Fitzhardinge 	(GFP_HIGHUSER | __GFP_NOWARN | __GFP_NORETRY | __GFP_NOMEMALLOC)
1231775826cSJeremy Fitzhardinge 
1241775826cSJeremy Fitzhardinge static void scrub_page(struct page *page)
1251775826cSJeremy Fitzhardinge {
1261775826cSJeremy Fitzhardinge #ifdef CONFIG_XEN_SCRUB_PAGES
12726a3e991SJeremy Fitzhardinge 	clear_highpage(page);
1281775826cSJeremy Fitzhardinge #endif
1291775826cSJeremy Fitzhardinge }
1301775826cSJeremy Fitzhardinge 
1311775826cSJeremy Fitzhardinge /* balloon_append: add the given page to the balloon. */
1321775826cSJeremy Fitzhardinge static void balloon_append(struct page *page)
1331775826cSJeremy Fitzhardinge {
1341775826cSJeremy Fitzhardinge 	/* Lowmem is re-populated first, so highmem pages go at list tail. */
1351775826cSJeremy Fitzhardinge 	if (PageHighMem(page)) {
1361775826cSJeremy Fitzhardinge 		list_add_tail(&page->lru, &ballooned_pages);
1371775826cSJeremy Fitzhardinge 		balloon_stats.balloon_high++;
1381775826cSJeremy Fitzhardinge 		dec_totalhigh_pages();
1391775826cSJeremy Fitzhardinge 	} else {
1401775826cSJeremy Fitzhardinge 		list_add(&page->lru, &ballooned_pages);
1411775826cSJeremy Fitzhardinge 		balloon_stats.balloon_low++;
1421775826cSJeremy Fitzhardinge 	}
1431775826cSJeremy Fitzhardinge }
1441775826cSJeremy Fitzhardinge 
1451775826cSJeremy Fitzhardinge /* balloon_retrieve: rescue a page from the balloon, if it is not empty. */
1461775826cSJeremy Fitzhardinge static struct page *balloon_retrieve(void)
1471775826cSJeremy Fitzhardinge {
1481775826cSJeremy Fitzhardinge 	struct page *page;
1491775826cSJeremy Fitzhardinge 
1501775826cSJeremy Fitzhardinge 	if (list_empty(&ballooned_pages))
1511775826cSJeremy Fitzhardinge 		return NULL;
1521775826cSJeremy Fitzhardinge 
1531775826cSJeremy Fitzhardinge 	page = list_entry(ballooned_pages.next, struct page, lru);
1541775826cSJeremy Fitzhardinge 	list_del(&page->lru);
1551775826cSJeremy Fitzhardinge 
1561775826cSJeremy Fitzhardinge 	if (PageHighMem(page)) {
1571775826cSJeremy Fitzhardinge 		balloon_stats.balloon_high--;
1581775826cSJeremy Fitzhardinge 		inc_totalhigh_pages();
1591775826cSJeremy Fitzhardinge 	}
1601775826cSJeremy Fitzhardinge 	else
1611775826cSJeremy Fitzhardinge 		balloon_stats.balloon_low--;
1621775826cSJeremy Fitzhardinge 
1631775826cSJeremy Fitzhardinge 	return page;
1641775826cSJeremy Fitzhardinge }
1651775826cSJeremy Fitzhardinge 
1661775826cSJeremy Fitzhardinge static struct page *balloon_first_page(void)
1671775826cSJeremy Fitzhardinge {
1681775826cSJeremy Fitzhardinge 	if (list_empty(&ballooned_pages))
1691775826cSJeremy Fitzhardinge 		return NULL;
1701775826cSJeremy Fitzhardinge 	return list_entry(ballooned_pages.next, struct page, lru);
1711775826cSJeremy Fitzhardinge }
1721775826cSJeremy Fitzhardinge 
1731775826cSJeremy Fitzhardinge static struct page *balloon_next_page(struct page *page)
1741775826cSJeremy Fitzhardinge {
1751775826cSJeremy Fitzhardinge 	struct list_head *next = page->lru.next;
1761775826cSJeremy Fitzhardinge 	if (next == &ballooned_pages)
1771775826cSJeremy Fitzhardinge 		return NULL;
1781775826cSJeremy Fitzhardinge 	return list_entry(next, struct page, lru);
1791775826cSJeremy Fitzhardinge }
1801775826cSJeremy Fitzhardinge 
1811775826cSJeremy Fitzhardinge static void balloon_alarm(unsigned long unused)
1821775826cSJeremy Fitzhardinge {
1831775826cSJeremy Fitzhardinge 	schedule_work(&balloon_worker);
1841775826cSJeremy Fitzhardinge }
1851775826cSJeremy Fitzhardinge 
1861775826cSJeremy Fitzhardinge static unsigned long current_target(void)
1871775826cSJeremy Fitzhardinge {
1881775826cSJeremy Fitzhardinge 	unsigned long target = min(balloon_stats.target_pages, balloon_stats.hard_limit);
1891775826cSJeremy Fitzhardinge 
1901775826cSJeremy Fitzhardinge 	target = min(target,
1911775826cSJeremy Fitzhardinge 		     balloon_stats.current_pages +
1921775826cSJeremy Fitzhardinge 		     balloon_stats.balloon_low +
1931775826cSJeremy Fitzhardinge 		     balloon_stats.balloon_high);
1941775826cSJeremy Fitzhardinge 
1951775826cSJeremy Fitzhardinge 	return target;
1961775826cSJeremy Fitzhardinge }
1971775826cSJeremy Fitzhardinge 
1981775826cSJeremy Fitzhardinge static int increase_reservation(unsigned long nr_pages)
1991775826cSJeremy Fitzhardinge {
2001775826cSJeremy Fitzhardinge 	unsigned long  pfn, i, flags;
2011775826cSJeremy Fitzhardinge 	struct page   *page;
2021775826cSJeremy Fitzhardinge 	long           rc;
2031775826cSJeremy Fitzhardinge 	struct xen_memory_reservation reservation = {
2041775826cSJeremy Fitzhardinge 		.address_bits = 0,
2051775826cSJeremy Fitzhardinge 		.extent_order = 0,
2061775826cSJeremy Fitzhardinge 		.domid        = DOMID_SELF
2071775826cSJeremy Fitzhardinge 	};
2081775826cSJeremy Fitzhardinge 
2091775826cSJeremy Fitzhardinge 	if (nr_pages > ARRAY_SIZE(frame_list))
2101775826cSJeremy Fitzhardinge 		nr_pages = ARRAY_SIZE(frame_list);
2111775826cSJeremy Fitzhardinge 
2121775826cSJeremy Fitzhardinge 	spin_lock_irqsave(&balloon_lock, flags);
2131775826cSJeremy Fitzhardinge 
2141775826cSJeremy Fitzhardinge 	page = balloon_first_page();
2151775826cSJeremy Fitzhardinge 	for (i = 0; i < nr_pages; i++) {
2161775826cSJeremy Fitzhardinge 		BUG_ON(page == NULL);
217a419aef8SJoe Perches 		frame_list[i] = page_to_pfn(page);
2181775826cSJeremy Fitzhardinge 		page = balloon_next_page(page);
2191775826cSJeremy Fitzhardinge 	}
2201775826cSJeremy Fitzhardinge 
221a90971ebSIsaku Yamahata 	set_xen_guest_handle(reservation.extent_start, frame_list);
2221775826cSJeremy Fitzhardinge 	reservation.nr_extents = nr_pages;
223fde28e8fSJeremy Fitzhardinge 	rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation);
2241775826cSJeremy Fitzhardinge 	if (rc < nr_pages) {
2251775826cSJeremy Fitzhardinge 		if (rc > 0) {
2261775826cSJeremy Fitzhardinge 			int ret;
2271775826cSJeremy Fitzhardinge 
2281775826cSJeremy Fitzhardinge 			/* We hit the Xen hard limit: reprobe. */
2291775826cSJeremy Fitzhardinge 			reservation.nr_extents = rc;
2301775826cSJeremy Fitzhardinge 			ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
2311775826cSJeremy Fitzhardinge 						   &reservation);
2321775826cSJeremy Fitzhardinge 			BUG_ON(ret != rc);
2331775826cSJeremy Fitzhardinge 		}
2341775826cSJeremy Fitzhardinge 		if (rc >= 0)
2351775826cSJeremy Fitzhardinge 			balloon_stats.hard_limit = (balloon_stats.current_pages + rc -
2361775826cSJeremy Fitzhardinge 						    balloon_stats.driver_pages);
2371775826cSJeremy Fitzhardinge 		goto out;
2381775826cSJeremy Fitzhardinge 	}
2391775826cSJeremy Fitzhardinge 
2401775826cSJeremy Fitzhardinge 	for (i = 0; i < nr_pages; i++) {
2411775826cSJeremy Fitzhardinge 		page = balloon_retrieve();
2421775826cSJeremy Fitzhardinge 		BUG_ON(page == NULL);
2431775826cSJeremy Fitzhardinge 
2441775826cSJeremy Fitzhardinge 		pfn = page_to_pfn(page);
2451775826cSJeremy Fitzhardinge 		BUG_ON(!xen_feature(XENFEAT_auto_translated_physmap) &&
2461775826cSJeremy Fitzhardinge 		       phys_to_machine_mapping_valid(pfn));
2471775826cSJeremy Fitzhardinge 
2481775826cSJeremy Fitzhardinge 		set_phys_to_machine(pfn, frame_list[i]);
2491775826cSJeremy Fitzhardinge 
2501775826cSJeremy Fitzhardinge 		/* Link back into the page tables if not highmem. */
2511775826cSJeremy Fitzhardinge 		if (pfn < max_low_pfn) {
2521775826cSJeremy Fitzhardinge 			int ret;
2531775826cSJeremy Fitzhardinge 			ret = HYPERVISOR_update_va_mapping(
2541775826cSJeremy Fitzhardinge 				(unsigned long)__va(pfn << PAGE_SHIFT),
2551775826cSJeremy Fitzhardinge 				mfn_pte(frame_list[i], PAGE_KERNEL),
2561775826cSJeremy Fitzhardinge 				0);
2571775826cSJeremy Fitzhardinge 			BUG_ON(ret);
2581775826cSJeremy Fitzhardinge 		}
2591775826cSJeremy Fitzhardinge 
2601775826cSJeremy Fitzhardinge 		/* Relinquish the page back to the allocator. */
2611775826cSJeremy Fitzhardinge 		ClearPageReserved(page);
2621775826cSJeremy Fitzhardinge 		init_page_count(page);
2631775826cSJeremy Fitzhardinge 		__free_page(page);
2641775826cSJeremy Fitzhardinge 	}
2651775826cSJeremy Fitzhardinge 
2661775826cSJeremy Fitzhardinge 	balloon_stats.current_pages += nr_pages;
2671775826cSJeremy Fitzhardinge 	totalram_pages = balloon_stats.current_pages;
2681775826cSJeremy Fitzhardinge 
2691775826cSJeremy Fitzhardinge  out:
2701775826cSJeremy Fitzhardinge 	spin_unlock_irqrestore(&balloon_lock, flags);
2711775826cSJeremy Fitzhardinge 
2721775826cSJeremy Fitzhardinge 	return 0;
2731775826cSJeremy Fitzhardinge }
2741775826cSJeremy Fitzhardinge 
2751775826cSJeremy Fitzhardinge static int decrease_reservation(unsigned long nr_pages)
2761775826cSJeremy Fitzhardinge {
2771775826cSJeremy Fitzhardinge 	unsigned long  pfn, i, flags;
2781775826cSJeremy Fitzhardinge 	struct page   *page;
2791775826cSJeremy Fitzhardinge 	int            need_sleep = 0;
2801775826cSJeremy Fitzhardinge 	int ret;
2811775826cSJeremy Fitzhardinge 	struct xen_memory_reservation reservation = {
2821775826cSJeremy Fitzhardinge 		.address_bits = 0,
2831775826cSJeremy Fitzhardinge 		.extent_order = 0,
2841775826cSJeremy Fitzhardinge 		.domid        = DOMID_SELF
2851775826cSJeremy Fitzhardinge 	};
2861775826cSJeremy Fitzhardinge 
2871775826cSJeremy Fitzhardinge 	if (nr_pages > ARRAY_SIZE(frame_list))
2881775826cSJeremy Fitzhardinge 		nr_pages = ARRAY_SIZE(frame_list);
2891775826cSJeremy Fitzhardinge 
2901775826cSJeremy Fitzhardinge 	for (i = 0; i < nr_pages; i++) {
2911775826cSJeremy Fitzhardinge 		if ((page = alloc_page(GFP_BALLOON)) == NULL) {
2921775826cSJeremy Fitzhardinge 			nr_pages = i;
2931775826cSJeremy Fitzhardinge 			need_sleep = 1;
2941775826cSJeremy Fitzhardinge 			break;
2951775826cSJeremy Fitzhardinge 		}
2961775826cSJeremy Fitzhardinge 
2971775826cSJeremy Fitzhardinge 		pfn = page_to_pfn(page);
2981775826cSJeremy Fitzhardinge 		frame_list[i] = pfn_to_mfn(pfn);
2991775826cSJeremy Fitzhardinge 
3001775826cSJeremy Fitzhardinge 		scrub_page(page);
3011058a75fSDan Magenheimer 
302ff4ce8c3SIan Campbell 		if (!PageHighMem(page)) {
3031058a75fSDan Magenheimer 			ret = HYPERVISOR_update_va_mapping(
3041058a75fSDan Magenheimer 				(unsigned long)__va(pfn << PAGE_SHIFT),
3051058a75fSDan Magenheimer 				__pte_ma(0), 0);
3061058a75fSDan Magenheimer 			BUG_ON(ret);
3071775826cSJeremy Fitzhardinge                 }
3081775826cSJeremy Fitzhardinge 
309ff4ce8c3SIan Campbell 	}
310ff4ce8c3SIan Campbell 
3111775826cSJeremy Fitzhardinge 	/* Ensure that ballooned highmem pages don't have kmaps. */
3121775826cSJeremy Fitzhardinge 	kmap_flush_unused();
3131775826cSJeremy Fitzhardinge 	flush_tlb_all();
3141775826cSJeremy Fitzhardinge 
3151775826cSJeremy Fitzhardinge 	spin_lock_irqsave(&balloon_lock, flags);
3161775826cSJeremy Fitzhardinge 
3171775826cSJeremy Fitzhardinge 	/* No more mappings: invalidate P2M and add to balloon. */
3181775826cSJeremy Fitzhardinge 	for (i = 0; i < nr_pages; i++) {
3191775826cSJeremy Fitzhardinge 		pfn = mfn_to_pfn(frame_list[i]);
3201775826cSJeremy Fitzhardinge 		set_phys_to_machine(pfn, INVALID_P2M_ENTRY);
3211775826cSJeremy Fitzhardinge 		balloon_append(pfn_to_page(pfn));
3221775826cSJeremy Fitzhardinge 	}
3231775826cSJeremy Fitzhardinge 
324a90971ebSIsaku Yamahata 	set_xen_guest_handle(reservation.extent_start, frame_list);
3251775826cSJeremy Fitzhardinge 	reservation.nr_extents   = nr_pages;
3261775826cSJeremy Fitzhardinge 	ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
3271775826cSJeremy Fitzhardinge 	BUG_ON(ret != nr_pages);
3281775826cSJeremy Fitzhardinge 
3291775826cSJeremy Fitzhardinge 	balloon_stats.current_pages -= nr_pages;
3301775826cSJeremy Fitzhardinge 	totalram_pages = balloon_stats.current_pages;
3311775826cSJeremy Fitzhardinge 
3321775826cSJeremy Fitzhardinge 	spin_unlock_irqrestore(&balloon_lock, flags);
3331775826cSJeremy Fitzhardinge 
3341775826cSJeremy Fitzhardinge 	return need_sleep;
3351775826cSJeremy Fitzhardinge }
3361775826cSJeremy Fitzhardinge 
3371775826cSJeremy Fitzhardinge /*
3381775826cSJeremy Fitzhardinge  * We avoid multiple worker processes conflicting via the balloon mutex.
3391775826cSJeremy Fitzhardinge  * We may of course race updates of the target counts (which are protected
3401775826cSJeremy Fitzhardinge  * by the balloon lock), or with changes to the Xen hard limit, but we will
3411775826cSJeremy Fitzhardinge  * recover from these in time.
3421775826cSJeremy Fitzhardinge  */
3431775826cSJeremy Fitzhardinge static void balloon_process(struct work_struct *work)
3441775826cSJeremy Fitzhardinge {
3451775826cSJeremy Fitzhardinge 	int need_sleep = 0;
3461775826cSJeremy Fitzhardinge 	long credit;
3471775826cSJeremy Fitzhardinge 
3481775826cSJeremy Fitzhardinge 	mutex_lock(&balloon_mutex);
3491775826cSJeremy Fitzhardinge 
3501775826cSJeremy Fitzhardinge 	do {
3511775826cSJeremy Fitzhardinge 		credit = current_target() - balloon_stats.current_pages;
3521775826cSJeremy Fitzhardinge 		if (credit > 0)
3531775826cSJeremy Fitzhardinge 			need_sleep = (increase_reservation(credit) != 0);
3541775826cSJeremy Fitzhardinge 		if (credit < 0)
3551775826cSJeremy Fitzhardinge 			need_sleep = (decrease_reservation(-credit) != 0);
3561775826cSJeremy Fitzhardinge 
3571775826cSJeremy Fitzhardinge #ifndef CONFIG_PREEMPT
3581775826cSJeremy Fitzhardinge 		if (need_resched())
3591775826cSJeremy Fitzhardinge 			schedule();
3601775826cSJeremy Fitzhardinge #endif
3611775826cSJeremy Fitzhardinge 	} while ((credit != 0) && !need_sleep);
3621775826cSJeremy Fitzhardinge 
3631775826cSJeremy Fitzhardinge 	/* Schedule more work if there is some still to be done. */
3641775826cSJeremy Fitzhardinge 	if (current_target() != balloon_stats.current_pages)
3651775826cSJeremy Fitzhardinge 		mod_timer(&balloon_timer, jiffies + HZ);
3661775826cSJeremy Fitzhardinge 
3671775826cSJeremy Fitzhardinge 	mutex_unlock(&balloon_mutex);
3681775826cSJeremy Fitzhardinge }
3691775826cSJeremy Fitzhardinge 
3701775826cSJeremy Fitzhardinge /* Resets the Xen limit, sets new target, and kicks off processing. */
371955d6f17SAdrian Bunk static void balloon_set_new_target(unsigned long target)
3721775826cSJeremy Fitzhardinge {
3731775826cSJeremy Fitzhardinge 	/* No need for lock. Not read-modify-write updates. */
3741775826cSJeremy Fitzhardinge 	balloon_stats.hard_limit   = ~0UL;
3751775826cSJeremy Fitzhardinge 	balloon_stats.target_pages = target;
3761775826cSJeremy Fitzhardinge 	schedule_work(&balloon_worker);
3771775826cSJeremy Fitzhardinge }
3781775826cSJeremy Fitzhardinge 
3791775826cSJeremy Fitzhardinge static struct xenbus_watch target_watch =
3801775826cSJeremy Fitzhardinge {
3811775826cSJeremy Fitzhardinge 	.node = "memory/target"
3821775826cSJeremy Fitzhardinge };
3831775826cSJeremy Fitzhardinge 
3841775826cSJeremy Fitzhardinge /* React to a change in the target key */
3851775826cSJeremy Fitzhardinge static void watch_target(struct xenbus_watch *watch,
3861775826cSJeremy Fitzhardinge 			 const char **vec, unsigned int len)
3871775826cSJeremy Fitzhardinge {
3881775826cSJeremy Fitzhardinge 	unsigned long long new_target;
3891775826cSJeremy Fitzhardinge 	int err;
3901775826cSJeremy Fitzhardinge 
3911775826cSJeremy Fitzhardinge 	err = xenbus_scanf(XBT_NIL, "memory", "target", "%llu", &new_target);
3921775826cSJeremy Fitzhardinge 	if (err != 1) {
3931775826cSJeremy Fitzhardinge 		/* This is ok (for domain0 at least) - so just return */
3941775826cSJeremy Fitzhardinge 		return;
3951775826cSJeremy Fitzhardinge 	}
3961775826cSJeremy Fitzhardinge 
3971775826cSJeremy Fitzhardinge 	/* The given memory/target value is in KiB, so it needs converting to
3981775826cSJeremy Fitzhardinge 	 * pages. PAGE_SHIFT converts bytes to pages, hence PAGE_SHIFT - 10.
3991775826cSJeremy Fitzhardinge 	 */
4001775826cSJeremy Fitzhardinge 	balloon_set_new_target(new_target >> (PAGE_SHIFT - 10));
4011775826cSJeremy Fitzhardinge }
4021775826cSJeremy Fitzhardinge 
4031775826cSJeremy Fitzhardinge static int balloon_init_watcher(struct notifier_block *notifier,
4041775826cSJeremy Fitzhardinge 				unsigned long event,
4051775826cSJeremy Fitzhardinge 				void *data)
4061775826cSJeremy Fitzhardinge {
4071775826cSJeremy Fitzhardinge 	int err;
4081775826cSJeremy Fitzhardinge 
4091775826cSJeremy Fitzhardinge 	err = register_xenbus_watch(&target_watch);
4101775826cSJeremy Fitzhardinge 	if (err)
4111775826cSJeremy Fitzhardinge 		printk(KERN_ERR "Failed to set balloon watcher\n");
4121775826cSJeremy Fitzhardinge 
4131775826cSJeremy Fitzhardinge 	return NOTIFY_DONE;
4141775826cSJeremy Fitzhardinge }
4151775826cSJeremy Fitzhardinge 
4161775826cSJeremy Fitzhardinge static struct notifier_block xenstore_notifier;
4171775826cSJeremy Fitzhardinge 
4181775826cSJeremy Fitzhardinge static int __init balloon_init(void)
4191775826cSJeremy Fitzhardinge {
4201775826cSJeremy Fitzhardinge 	unsigned long pfn;
4211775826cSJeremy Fitzhardinge 	struct page *page;
4221775826cSJeremy Fitzhardinge 
4236e833587SJeremy Fitzhardinge 	if (!xen_pv_domain())
4241775826cSJeremy Fitzhardinge 		return -ENODEV;
4251775826cSJeremy Fitzhardinge 
4261775826cSJeremy Fitzhardinge 	pr_info("xen_balloon: Initialising balloon driver.\n");
4271775826cSJeremy Fitzhardinge 
4281775826cSJeremy Fitzhardinge 	balloon_stats.current_pages = min(xen_start_info->nr_pages, max_pfn);
4291775826cSJeremy Fitzhardinge 	totalram_pages   = balloon_stats.current_pages;
4301775826cSJeremy Fitzhardinge 	balloon_stats.target_pages  = balloon_stats.current_pages;
4311775826cSJeremy Fitzhardinge 	balloon_stats.balloon_low   = 0;
4321775826cSJeremy Fitzhardinge 	balloon_stats.balloon_high  = 0;
4331775826cSJeremy Fitzhardinge 	balloon_stats.driver_pages  = 0UL;
4341775826cSJeremy Fitzhardinge 	balloon_stats.hard_limit    = ~0UL;
4351775826cSJeremy Fitzhardinge 
4361775826cSJeremy Fitzhardinge 	init_timer(&balloon_timer);
4371775826cSJeremy Fitzhardinge 	balloon_timer.data = 0;
4381775826cSJeremy Fitzhardinge 	balloon_timer.function = balloon_alarm;
4391775826cSJeremy Fitzhardinge 
4401775826cSJeremy Fitzhardinge 	register_balloon(&balloon_sysdev);
4411775826cSJeremy Fitzhardinge 
4421775826cSJeremy Fitzhardinge 	/* Initialise the balloon with excess memory space. */
4431775826cSJeremy Fitzhardinge 	for (pfn = xen_start_info->nr_pages; pfn < max_pfn; pfn++) {
4441775826cSJeremy Fitzhardinge 		page = pfn_to_page(pfn);
4451775826cSJeremy Fitzhardinge 		if (!PageReserved(page))
4461775826cSJeremy Fitzhardinge 			balloon_append(page);
4471775826cSJeremy Fitzhardinge 	}
4481775826cSJeremy Fitzhardinge 
4491775826cSJeremy Fitzhardinge 	target_watch.callback = watch_target;
4501775826cSJeremy Fitzhardinge 	xenstore_notifier.notifier_call = balloon_init_watcher;
4511775826cSJeremy Fitzhardinge 
4521775826cSJeremy Fitzhardinge 	register_xenstore_notifier(&xenstore_notifier);
4531775826cSJeremy Fitzhardinge 
4541775826cSJeremy Fitzhardinge 	return 0;
4551775826cSJeremy Fitzhardinge }
4561775826cSJeremy Fitzhardinge 
4571775826cSJeremy Fitzhardinge subsys_initcall(balloon_init);
4581775826cSJeremy Fitzhardinge 
4591775826cSJeremy Fitzhardinge static void balloon_exit(void)
4601775826cSJeremy Fitzhardinge {
4611775826cSJeremy Fitzhardinge     /* XXX - release balloon here */
4621775826cSJeremy Fitzhardinge     return;
4631775826cSJeremy Fitzhardinge }
4641775826cSJeremy Fitzhardinge 
4651775826cSJeremy Fitzhardinge module_exit(balloon_exit);
4661775826cSJeremy Fitzhardinge 
4671775826cSJeremy Fitzhardinge #define BALLOON_SHOW(name, format, args...)				\
4681775826cSJeremy Fitzhardinge 	static ssize_t show_##name(struct sys_device *dev,		\
469167e6cf6SJeremy Fitzhardinge 				   struct sysdev_attribute *attr,	\
4701775826cSJeremy Fitzhardinge 				   char *buf)				\
4711775826cSJeremy Fitzhardinge 	{								\
4721775826cSJeremy Fitzhardinge 		return sprintf(buf, format, ##args);			\
4731775826cSJeremy Fitzhardinge 	}								\
4741775826cSJeremy Fitzhardinge 	static SYSDEV_ATTR(name, S_IRUGO, show_##name, NULL)
4751775826cSJeremy Fitzhardinge 
4761775826cSJeremy Fitzhardinge BALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(balloon_stats.current_pages));
4771775826cSJeremy Fitzhardinge BALLOON_SHOW(low_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_low));
4781775826cSJeremy Fitzhardinge BALLOON_SHOW(high_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_high));
4791775826cSJeremy Fitzhardinge BALLOON_SHOW(hard_limit_kb,
4801775826cSJeremy Fitzhardinge 	     (balloon_stats.hard_limit!=~0UL) ? "%lu\n" : "???\n",
4811775826cSJeremy Fitzhardinge 	     (balloon_stats.hard_limit!=~0UL) ? PAGES2KB(balloon_stats.hard_limit) : 0);
4821775826cSJeremy Fitzhardinge BALLOON_SHOW(driver_kb, "%lu\n", PAGES2KB(balloon_stats.driver_pages));
4831775826cSJeremy Fitzhardinge 
484167e6cf6SJeremy Fitzhardinge static ssize_t show_target_kb(struct sys_device *dev, struct sysdev_attribute *attr,
485167e6cf6SJeremy Fitzhardinge 			      char *buf)
4861775826cSJeremy Fitzhardinge {
4871775826cSJeremy Fitzhardinge 	return sprintf(buf, "%lu\n", PAGES2KB(balloon_stats.target_pages));
4881775826cSJeremy Fitzhardinge }
4891775826cSJeremy Fitzhardinge 
4901775826cSJeremy Fitzhardinge static ssize_t store_target_kb(struct sys_device *dev,
4914a0b2b4dSAndi Kleen 			       struct sysdev_attribute *attr,
4921775826cSJeremy Fitzhardinge 			       const char *buf,
4931775826cSJeremy Fitzhardinge 			       size_t count)
4941775826cSJeremy Fitzhardinge {
495167e6cf6SJeremy Fitzhardinge 	char *endchar;
4961775826cSJeremy Fitzhardinge 	unsigned long long target_bytes;
4971775826cSJeremy Fitzhardinge 
4981775826cSJeremy Fitzhardinge 	if (!capable(CAP_SYS_ADMIN))
4991775826cSJeremy Fitzhardinge 		return -EPERM;
5001775826cSJeremy Fitzhardinge 
501618b2c8dSJeremy Fitzhardinge 	target_bytes = simple_strtoull(buf, &endchar, 0) * 1024;
5021775826cSJeremy Fitzhardinge 
5031775826cSJeremy Fitzhardinge 	balloon_set_new_target(target_bytes >> PAGE_SHIFT);
5041775826cSJeremy Fitzhardinge 
5051775826cSJeremy Fitzhardinge 	return count;
5061775826cSJeremy Fitzhardinge }
5071775826cSJeremy Fitzhardinge 
5081775826cSJeremy Fitzhardinge static SYSDEV_ATTR(target_kb, S_IRUGO | S_IWUSR,
5091775826cSJeremy Fitzhardinge 		   show_target_kb, store_target_kb);
5101775826cSJeremy Fitzhardinge 
511618b2c8dSJeremy Fitzhardinge 
512618b2c8dSJeremy Fitzhardinge static ssize_t show_target(struct sys_device *dev, struct sysdev_attribute *attr,
513618b2c8dSJeremy Fitzhardinge 			      char *buf)
514618b2c8dSJeremy Fitzhardinge {
515618b2c8dSJeremy Fitzhardinge 	return sprintf(buf, "%llu\n",
5160692698cSJan Beulich 		       (unsigned long long)balloon_stats.target_pages
5170692698cSJan Beulich 		       << PAGE_SHIFT);
518618b2c8dSJeremy Fitzhardinge }
519618b2c8dSJeremy Fitzhardinge 
520618b2c8dSJeremy Fitzhardinge static ssize_t store_target(struct sys_device *dev,
521618b2c8dSJeremy Fitzhardinge 			    struct sysdev_attribute *attr,
522618b2c8dSJeremy Fitzhardinge 			    const char *buf,
523618b2c8dSJeremy Fitzhardinge 			    size_t count)
524618b2c8dSJeremy Fitzhardinge {
525618b2c8dSJeremy Fitzhardinge 	char *endchar;
526618b2c8dSJeremy Fitzhardinge 	unsigned long long target_bytes;
527618b2c8dSJeremy Fitzhardinge 
528618b2c8dSJeremy Fitzhardinge 	if (!capable(CAP_SYS_ADMIN))
529618b2c8dSJeremy Fitzhardinge 		return -EPERM;
530618b2c8dSJeremy Fitzhardinge 
531618b2c8dSJeremy Fitzhardinge 	target_bytes = memparse(buf, &endchar);
532618b2c8dSJeremy Fitzhardinge 
533618b2c8dSJeremy Fitzhardinge 	balloon_set_new_target(target_bytes >> PAGE_SHIFT);
534618b2c8dSJeremy Fitzhardinge 
535618b2c8dSJeremy Fitzhardinge 	return count;
536618b2c8dSJeremy Fitzhardinge }
537618b2c8dSJeremy Fitzhardinge 
538618b2c8dSJeremy Fitzhardinge static SYSDEV_ATTR(target, S_IRUGO | S_IWUSR,
539618b2c8dSJeremy Fitzhardinge 		   show_target, store_target);
540618b2c8dSJeremy Fitzhardinge 
541618b2c8dSJeremy Fitzhardinge 
5421775826cSJeremy Fitzhardinge static struct sysdev_attribute *balloon_attrs[] = {
5431775826cSJeremy Fitzhardinge 	&attr_target_kb,
544618b2c8dSJeremy Fitzhardinge 	&attr_target,
5451775826cSJeremy Fitzhardinge };
5461775826cSJeremy Fitzhardinge 
5471775826cSJeremy Fitzhardinge static struct attribute *balloon_info_attrs[] = {
5481775826cSJeremy Fitzhardinge 	&attr_current_kb.attr,
5491775826cSJeremy Fitzhardinge 	&attr_low_kb.attr,
5501775826cSJeremy Fitzhardinge 	&attr_high_kb.attr,
5511775826cSJeremy Fitzhardinge 	&attr_hard_limit_kb.attr,
5521775826cSJeremy Fitzhardinge 	&attr_driver_kb.attr,
5531775826cSJeremy Fitzhardinge 	NULL
5541775826cSJeremy Fitzhardinge };
5551775826cSJeremy Fitzhardinge 
5561775826cSJeremy Fitzhardinge static struct attribute_group balloon_info_group = {
5571775826cSJeremy Fitzhardinge 	.name = "info",
5581775826cSJeremy Fitzhardinge 	.attrs = balloon_info_attrs,
5591775826cSJeremy Fitzhardinge };
5601775826cSJeremy Fitzhardinge 
5611775826cSJeremy Fitzhardinge static struct sysdev_class balloon_sysdev_class = {
5621775826cSJeremy Fitzhardinge 	.name = BALLOON_CLASS_NAME,
5631775826cSJeremy Fitzhardinge };
5641775826cSJeremy Fitzhardinge 
5651775826cSJeremy Fitzhardinge static int register_balloon(struct sys_device *sysdev)
5661775826cSJeremy Fitzhardinge {
5671775826cSJeremy Fitzhardinge 	int i, error;
5681775826cSJeremy Fitzhardinge 
5691775826cSJeremy Fitzhardinge 	error = sysdev_class_register(&balloon_sysdev_class);
5701775826cSJeremy Fitzhardinge 	if (error)
5711775826cSJeremy Fitzhardinge 		return error;
5721775826cSJeremy Fitzhardinge 
5731775826cSJeremy Fitzhardinge 	sysdev->id = 0;
5741775826cSJeremy Fitzhardinge 	sysdev->cls = &balloon_sysdev_class;
5751775826cSJeremy Fitzhardinge 
5761775826cSJeremy Fitzhardinge 	error = sysdev_register(sysdev);
5771775826cSJeremy Fitzhardinge 	if (error) {
5781775826cSJeremy Fitzhardinge 		sysdev_class_unregister(&balloon_sysdev_class);
5791775826cSJeremy Fitzhardinge 		return error;
5801775826cSJeremy Fitzhardinge 	}
5811775826cSJeremy Fitzhardinge 
5821775826cSJeremy Fitzhardinge 	for (i = 0; i < ARRAY_SIZE(balloon_attrs); i++) {
5831775826cSJeremy Fitzhardinge 		error = sysdev_create_file(sysdev, balloon_attrs[i]);
5841775826cSJeremy Fitzhardinge 		if (error)
5851775826cSJeremy Fitzhardinge 			goto fail;
5861775826cSJeremy Fitzhardinge 	}
5871775826cSJeremy Fitzhardinge 
5881775826cSJeremy Fitzhardinge 	error = sysfs_create_group(&sysdev->kobj, &balloon_info_group);
5891775826cSJeremy Fitzhardinge 	if (error)
5901775826cSJeremy Fitzhardinge 		goto fail;
5911775826cSJeremy Fitzhardinge 
5921775826cSJeremy Fitzhardinge 	return 0;
5931775826cSJeremy Fitzhardinge 
5941775826cSJeremy Fitzhardinge  fail:
5951775826cSJeremy Fitzhardinge 	while (--i >= 0)
5961775826cSJeremy Fitzhardinge 		sysdev_remove_file(sysdev, balloon_attrs[i]);
5971775826cSJeremy Fitzhardinge 	sysdev_unregister(sysdev);
5981775826cSJeremy Fitzhardinge 	sysdev_class_unregister(&balloon_sysdev_class);
5991775826cSJeremy Fitzhardinge 	return error;
6001775826cSJeremy Fitzhardinge }
6011775826cSJeremy Fitzhardinge 
6021775826cSJeremy Fitzhardinge MODULE_LICENSE("GPL");
603