|
@@ -68,6 +68,8 @@
|
|
|
*/
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
+#include <linux/bootmem.h>
|
|
|
+#include <linux/swap.h>
|
|
|
#include <linux/mm.h>
|
|
|
#include <linux/mman.h>
|
|
|
#include <linux/workqueue.h>
|
|
@@ -92,6 +94,15 @@ static unsigned int selfballoon_uphysteresis __read_mostly = 1;
|
|
|
/* In HZ, controls frequency of worker invocation. */
|
|
|
static unsigned int selfballoon_interval __read_mostly = 5;
|
|
|
|
|
|
+/*
|
|
|
+ * Minimum usable RAM in MB for selfballooning target for balloon.
|
|
|
+ * If non-zero, it is added to totalreserve_pages and self-ballooning
|
|
|
+ * will not balloon below the sum. If zero, a piecewise linear function
|
|
|
+ * is calculated as a minimum and added to totalreserve_pages. Note that
|
|
|
+ * setting this value indiscriminately may cause OOMs and crashes.
|
|
|
+ */
|
|
|
+static unsigned int selfballoon_min_usable_mb;
|
|
|
+
|
|
|
static void selfballoon_process(struct work_struct *work);
|
|
|
static DECLARE_DELAYED_WORK(selfballoon_worker, selfballoon_process);
|
|
|
|
|
@@ -188,20 +199,23 @@ static int __init xen_selfballooning_setup(char *s)
|
|
|
__setup("selfballooning", xen_selfballooning_setup);
|
|
|
#endif /* CONFIG_FRONTSWAP */
|
|
|
|
|
|
+#define MB2PAGES(mb) ((mb) << (20 - PAGE_SHIFT))
|
|
|
+
|
|
|
/*
|
|
|
* Use current balloon size, the goal (vm_committed_as), and hysteresis
|
|
|
* parameters to set a new target balloon size
|
|
|
*/
|
|
|
static void selfballoon_process(struct work_struct *work)
|
|
|
{
|
|
|
- unsigned long cur_pages, goal_pages, tgt_pages;
|
|
|
+ unsigned long cur_pages, goal_pages, tgt_pages, floor_pages;
|
|
|
+ unsigned long useful_pages;
|
|
|
bool reset_timer = false;
|
|
|
|
|
|
if (xen_selfballooning_enabled) {
|
|
|
- cur_pages = balloon_stats.current_pages;
|
|
|
+ cur_pages = totalram_pages;
|
|
|
tgt_pages = cur_pages; /* default is no change */
|
|
|
goal_pages = percpu_counter_read_positive(&vm_committed_as) +
|
|
|
- balloon_stats.current_pages - totalram_pages;
|
|
|
+ totalreserve_pages;
|
|
|
#ifdef CONFIG_FRONTSWAP
|
|
|
/* allow space for frontswap pages to be repatriated */
|
|
|
if (frontswap_selfshrinking && frontswap_enabled)
|
|
@@ -216,7 +230,26 @@ static void selfballoon_process(struct work_struct *work)
|
|
|
((goal_pages - cur_pages) /
|
|
|
selfballoon_uphysteresis);
|
|
|
/* else if cur_pages == goal_pages, no change */
|
|
|
- balloon_set_new_target(tgt_pages);
|
|
|
+ useful_pages = max_pfn - totalreserve_pages;
|
|
|
+ if (selfballoon_min_usable_mb != 0)
|
|
|
+ floor_pages = totalreserve_pages +
|
|
|
+ MB2PAGES(selfballoon_min_usable_mb);
|
|
|
+ /* piecewise linear function ending in ~3% slope */
|
|
|
+ else if (useful_pages < MB2PAGES(16))
|
|
|
+ floor_pages = max_pfn; /* not worth ballooning */
|
|
|
+ else if (useful_pages < MB2PAGES(64))
|
|
|
+ floor_pages = totalreserve_pages + MB2PAGES(16) +
|
|
|
+ ((useful_pages - MB2PAGES(16)) >> 1);
|
|
|
+ else if (useful_pages < MB2PAGES(512))
|
|
|
+ floor_pages = totalreserve_pages + MB2PAGES(40) +
|
|
|
+ ((useful_pages - MB2PAGES(40)) >> 3);
|
|
|
+ else /* useful_pages >= MB2PAGES(512) */
|
|
|
+ floor_pages = totalreserve_pages + MB2PAGES(99) +
|
|
|
+ ((useful_pages - MB2PAGES(99)) >> 5);
|
|
|
+ if (tgt_pages < floor_pages)
|
|
|
+ tgt_pages = floor_pages;
|
|
|
+ balloon_set_new_target(tgt_pages +
|
|
|
+ balloon_stats.current_pages - totalram_pages);
|
|
|
reset_timer = true;
|
|
|
}
|
|
|
#ifdef CONFIG_FRONTSWAP
|
|
@@ -339,6 +372,31 @@ static ssize_t store_selfballoon_uphys(struct sys_device *dev,
|
|
|
static SYSDEV_ATTR(selfballoon_uphysteresis, S_IRUGO | S_IWUSR,
|
|
|
show_selfballoon_uphys, store_selfballoon_uphys);
|
|
|
|
|
|
+SELFBALLOON_SHOW(selfballoon_min_usable_mb, "%d\n",
|
|
|
+ selfballoon_min_usable_mb);
|
|
|
+
|
|
|
+static ssize_t store_selfballoon_min_usable_mb(struct sys_device *dev,
|
|
|
+ struct sysdev_attribute *attr,
|
|
|
+ const char *buf,
|
|
|
+ size_t count)
|
|
|
+{
|
|
|
+ unsigned long val;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (!capable(CAP_SYS_ADMIN))
|
|
|
+ return -EPERM;
|
|
|
+ err = strict_strtoul(buf, 10, &val);
|
|
|
+ if (err || val == 0)
|
|
|
+ return -EINVAL;
|
|
|
+ selfballoon_min_usable_mb = val;
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static SYSDEV_ATTR(selfballoon_min_usable_mb, S_IRUGO | S_IWUSR,
|
|
|
+ show_selfballoon_min_usable_mb,
|
|
|
+ store_selfballoon_min_usable_mb);
|
|
|
+
|
|
|
+
|
|
|
#ifdef CONFIG_FRONTSWAP
|
|
|
SELFBALLOON_SHOW(frontswap_selfshrinking, "%d\n", frontswap_selfshrinking);
|
|
|
|
|
@@ -420,6 +478,7 @@ static struct attribute *selfballoon_attrs[] = {
|
|
|
&attr_selfballoon_interval.attr,
|
|
|
&attr_selfballoon_downhysteresis.attr,
|
|
|
&attr_selfballoon_uphysteresis.attr,
|
|
|
+ &attr_selfballoon_min_usable_mb.attr,
|
|
|
#ifdef CONFIG_FRONTSWAP
|
|
|
&attr_frontswap_selfshrinking.attr,
|
|
|
&attr_frontswap_hysteresis.attr,
|