* IN THE SOFTWARE.
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
static unsigned long target_pages;
/* We increase/decrease in batches which fit in a page */
-static unsigned long frame_list[PAGE_SIZE / sizeof(unsigned long)];
+static unsigned long frame_list[PAGE_SIZE / sizeof(unsigned long)];
/* VM /proc information for memory */
extern unsigned long totalram_pages;
static unsigned long balloon_low, balloon_high;
/* Main work function, always executed in process context. */
-static void balloon_process(void *unused);
-static DECLARE_WORK(balloon_worker, balloon_process, NULL);
+static void balloon_process(struct work_struct *unusable);
+static DECLARE_WORK(balloon_worker, balloon_process);
static struct timer_list balloon_timer;
/* When ballooning out (allocating memory to return to Xen) we don't really
* by the balloon lock), or with changes to the Xen hard limit, but we will
* recover from these in time.
*/
-static void balloon_process(void *unused)
+static void balloon_process(struct work_struct *unusable)
{
int need_sleep = 0;
long credit;
"Requested target: %8lu kB\n"
"Low-mem balloon: %8lu kB\n"
"High-mem balloon: %8lu kB\n"
+ "Driver pages: %8lu kB\n"
"Xen hard limit: ",
PAGES2KB(current_pages), PAGES2KB(target_pages),
- PAGES2KB(balloon_low), PAGES2KB(balloon_high));
+ PAGES2KB(balloon_low), PAGES2KB(balloon_high),
+ PAGES2KB(driver_pages));
- if (hard_limit != ~0UL) {
- len += sprintf(
- page + len,
- "%8lu kB (inc. %8lu kB driver headroom)\n",
- PAGES2KB(hard_limit), PAGES2KB(driver_pages));
- } else {
- len += sprintf(
- page + len,
- " ??? kB\n");
- }
+ if (hard_limit != ~0UL)
+ len += sprintf(page + len, "%8lu kB\n", PAGES2KB(hard_limit));
+ else
+ len += sprintf(page + len, " ??? kB\n");
*eof = 1;
return len;
return 0;
}
-struct page *balloon_alloc_empty_page_range(unsigned long nr_pages)
+struct page **alloc_empty_pages_and_pagevec(int nr_pages)
{
- unsigned long vstart, flags;
- unsigned int order = get_order(nr_pages * PAGE_SIZE);
- int ret;
- unsigned long i;
- struct page *page;
+ unsigned long vaddr, flags;
+ struct page *page, **pagevec;
+ int i, ret;
- vstart = __get_free_pages(GFP_KERNEL, order);
- if (vstart == 0)
+ pagevec = kmalloc(sizeof(page) * nr_pages, GFP_KERNEL);
+ if (pagevec == NULL)
return NULL;
- scrub_pages(vstart, 1 << order);
-
- balloon_lock(flags);
- if (xen_feature(XENFEAT_auto_translated_physmap)) {
- unsigned long gmfn = __pa(vstart) >> PAGE_SHIFT;
- struct xen_memory_reservation reservation = {
- .nr_extents = 1,
- .extent_order = order,
- .domid = DOMID_SELF
- };
- set_xen_guest_handle(reservation.extent_start, &gmfn);
- ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
- &reservation);
- if (ret == -ENOSYS)
+ for (i = 0; i < nr_pages; i++) {
+ page = pagevec[i] = alloc_page(GFP_KERNEL);
+ if (page == NULL)
goto err;
- BUG_ON(ret != 1);
- } else {
- ret = apply_to_page_range(&init_mm, vstart, PAGE_SIZE << order,
- dealloc_pte_fn, NULL);
- if (ret == -ENOSYS)
+
+ vaddr = (unsigned long)page_address(page);
+
+ scrub_pages(vaddr, 1);
+
+ balloon_lock(flags);
+
+ if (xen_feature(XENFEAT_auto_translated_physmap)) {
+ unsigned long gmfn = page_to_pfn(page);
+ struct xen_memory_reservation reservation = {
+ .nr_extents = 1,
+ .extent_order = 0,
+ .domid = DOMID_SELF
+ };
+ set_xen_guest_handle(reservation.extent_start, &gmfn);
+ ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
+ &reservation);
+ if (ret == 1)
+ ret = 0; /* success */
+ } else {
+ ret = apply_to_page_range(&init_mm, vaddr, PAGE_SIZE,
+ dealloc_pte_fn, NULL);
+ }
+
+ if (ret != 0) {
+ balloon_unlock(flags);
+ __free_page(page);
goto err;
- BUG_ON(ret);
+ }
+
+ totalram_pages = --current_pages;
+
+ balloon_unlock(flags);
}
- current_pages -= 1UL << order;
- totalram_pages = current_pages;
- balloon_unlock(flags);
+ out:
schedule_work(&balloon_worker);
-
flush_tlb_all();
+ return pagevec;
- page = virt_to_page(vstart);
+ err:
+ balloon_lock(flags);
+ while (--i >= 0)
+ balloon_append(pagevec[i]);
+ balloon_unlock(flags);
+ kfree(pagevec);
+ pagevec = NULL;
+ goto out;
+}
- for (i = 0; i < (1UL << order); i++)
- init_page_count(page + i);
+void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages)
+{
+ unsigned long flags;
+ int i;
- return page;
+ if (pagevec == NULL)
+ return;
- err:
- free_pages(vstart, order);
+ balloon_lock(flags);
+ for (i = 0; i < nr_pages; i++) {
+ BUG_ON(page_count(pagevec[i]) != 1);
+ balloon_append(pagevec[i]);
+ }
balloon_unlock(flags);
- return NULL;
+
+ kfree(pagevec);
+
+ schedule_work(&balloon_worker);
}
-void balloon_dealloc_empty_page_range(
- struct page *page, unsigned long nr_pages)
+void balloon_release_driver_page(struct page *page)
{
- unsigned long i, flags;
- unsigned int order = get_order(nr_pages * PAGE_SIZE);
+ unsigned long flags;
balloon_lock(flags);
- for (i = 0; i < (1UL << order); i++) {
- BUG_ON(page_count(page + i) != 1);
- balloon_append(page + i);
- }
+ balloon_append(page);
+ driver_pages--;
balloon_unlock(flags);
schedule_work(&balloon_worker);
}
EXPORT_SYMBOL_GPL(balloon_update_driver_allowance);
-EXPORT_SYMBOL_GPL(balloon_alloc_empty_page_range);
-EXPORT_SYMBOL_GPL(balloon_dealloc_empty_page_range);
+EXPORT_SYMBOL_GPL(alloc_empty_pages_and_pagevec);
+EXPORT_SYMBOL_GPL(free_empty_pages_and_pagevec);
+EXPORT_SYMBOL_GPL(balloon_release_driver_page);
MODULE_LICENSE("Dual BSD/GPL");