18c2ecf20Sopenharmony_ci/******************************************************************************
28c2ecf20Sopenharmony_ci * Xen balloon driver - enables returning/claiming memory to/from Xen.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2003, B Dragovic
58c2ecf20Sopenharmony_ci * Copyright (c) 2003-2004, M Williamson, K Fraser
68c2ecf20Sopenharmony_ci * Copyright (c) 2005 Dan M. Smith, IBM Corporation
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
98c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License version 2
108c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; or, when distributed
118c2ecf20Sopenharmony_ci * separately from the Linux kernel or incorporated into other
128c2ecf20Sopenharmony_ci * software packages, subject to the following license:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
158c2ecf20Sopenharmony_ci * of this source file (the "Software"), to deal in the Software without
168c2ecf20Sopenharmony_ci * restriction, including without limitation the rights to use, copy, modify,
178c2ecf20Sopenharmony_ci * merge, publish, distribute, sublicense, and/or sell copies of the Software,
188c2ecf20Sopenharmony_ci * and to permit persons to whom the Software is furnished to do so, subject to
198c2ecf20Sopenharmony_ci * the following conditions:
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
228c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
258c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
268c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
278c2ecf20Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
288c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
298c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
308c2ecf20Sopenharmony_ci * IN THE SOFTWARE.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <linux/kernel.h>
368c2ecf20Sopenharmony_ci#include <linux/errno.h>
378c2ecf20Sopenharmony_ci#include <linux/mm_types.h>
388c2ecf20Sopenharmony_ci#include <linux/init.h>
398c2ecf20Sopenharmony_ci#include <linux/capability.h>
408c2ecf20Sopenharmony_ci#include <linux/memory_hotplug.h>
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include <xen/xen.h>
438c2ecf20Sopenharmony_ci#include <xen/interface/xen.h>
448c2ecf20Sopenharmony_ci#include <xen/balloon.h>
458c2ecf20Sopenharmony_ci#include <xen/xenbus.h>
468c2ecf20Sopenharmony_ci#include <xen/features.h>
478c2ecf20Sopenharmony_ci#include <xen/page.h>
488c2ecf20Sopenharmony_ci#include <xen/mem-reservation.h>
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define PAGES2KB(_p) ((_p)<<(PAGE_SHIFT-10))
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define BALLOON_CLASS_NAME "xen_memory"
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG
558c2ecf20Sopenharmony_ciu64 xen_saved_max_mem_size = 0;
568c2ecf20Sopenharmony_ci#endif
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic struct device balloon_dev;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int register_balloon(struct device *dev);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* React to a change in the target key */
638c2ecf20Sopenharmony_cistatic void watch_target(struct xenbus_watch *watch,
648c2ecf20Sopenharmony_ci			 const char *path, const char *token)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	unsigned long long new_target, static_max;
678c2ecf20Sopenharmony_ci	int err;
688c2ecf20Sopenharmony_ci	static bool watch_fired;
698c2ecf20Sopenharmony_ci	static long target_diff;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG
728c2ecf20Sopenharmony_ci	/* The balloon driver will take care of adding memory now. */
738c2ecf20Sopenharmony_ci	if (xen_saved_max_mem_size)
748c2ecf20Sopenharmony_ci		max_mem_size = xen_saved_max_mem_size;
758c2ecf20Sopenharmony_ci#endif
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	err = xenbus_scanf(XBT_NIL, "memory", "target", "%llu", &new_target);
788c2ecf20Sopenharmony_ci	if (err != 1) {
798c2ecf20Sopenharmony_ci		/* This is ok (for domain0 at least) - so just return */
808c2ecf20Sopenharmony_ci		return;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* The given memory/target value is in KiB, so it needs converting to
848c2ecf20Sopenharmony_ci	 * pages. PAGE_SHIFT converts bytes to pages, hence PAGE_SHIFT - 10.
858c2ecf20Sopenharmony_ci	 */
868c2ecf20Sopenharmony_ci	new_target >>= PAGE_SHIFT - 10;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (!watch_fired) {
898c2ecf20Sopenharmony_ci		watch_fired = true;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		if ((xenbus_scanf(XBT_NIL, "memory", "static-max",
928c2ecf20Sopenharmony_ci				  "%llu", &static_max) == 1) ||
938c2ecf20Sopenharmony_ci		    (xenbus_scanf(XBT_NIL, "memory", "memory_static_max",
948c2ecf20Sopenharmony_ci				  "%llu", &static_max) == 1))
958c2ecf20Sopenharmony_ci			static_max >>= PAGE_SHIFT - 10;
968c2ecf20Sopenharmony_ci		else
978c2ecf20Sopenharmony_ci			static_max = balloon_stats.current_pages;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		target_diff = (xen_pv_domain() || xen_initial_domain()) ? 0
1008c2ecf20Sopenharmony_ci				: static_max - balloon_stats.target_pages;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	balloon_set_new_target(new_target - target_diff);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_cistatic struct xenbus_watch target_watch = {
1068c2ecf20Sopenharmony_ci	.node = "memory/target",
1078c2ecf20Sopenharmony_ci	.callback = watch_target,
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int balloon_init_watcher(struct notifier_block *notifier,
1128c2ecf20Sopenharmony_ci				unsigned long event,
1138c2ecf20Sopenharmony_ci				void *data)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	int err;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	err = register_xenbus_watch(&target_watch);
1188c2ecf20Sopenharmony_ci	if (err)
1198c2ecf20Sopenharmony_ci		pr_err("Failed to set balloon watcher\n");
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic struct notifier_block xenstore_notifier = {
1258c2ecf20Sopenharmony_ci	.notifier_call = balloon_init_watcher,
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_civoid xen_balloon_init(void)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	register_balloon(&balloon_dev);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	register_xenstore_notifier(&xenstore_notifier);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_balloon_init);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci#define BALLOON_SHOW(name, format, args...)				\
1378c2ecf20Sopenharmony_ci	static ssize_t show_##name(struct device *dev,			\
1388c2ecf20Sopenharmony_ci				   struct device_attribute *attr,	\
1398c2ecf20Sopenharmony_ci				   char *buf)				\
1408c2ecf20Sopenharmony_ci	{								\
1418c2ecf20Sopenharmony_ci		return sprintf(buf, format, ##args);			\
1428c2ecf20Sopenharmony_ci	}								\
1438c2ecf20Sopenharmony_ci	static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ciBALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(balloon_stats.current_pages));
1468c2ecf20Sopenharmony_ciBALLOON_SHOW(low_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_low));
1478c2ecf20Sopenharmony_ciBALLOON_SHOW(high_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_high));
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic DEVICE_ULONG_ATTR(schedule_delay, 0444, balloon_stats.schedule_delay);
1508c2ecf20Sopenharmony_cistatic DEVICE_ULONG_ATTR(max_schedule_delay, 0644, balloon_stats.max_schedule_delay);
1518c2ecf20Sopenharmony_cistatic DEVICE_ULONG_ATTR(retry_count, 0444, balloon_stats.retry_count);
1528c2ecf20Sopenharmony_cistatic DEVICE_ULONG_ATTR(max_retry_count, 0644, balloon_stats.max_retry_count);
1538c2ecf20Sopenharmony_cistatic DEVICE_BOOL_ATTR(scrub_pages, 0644, xen_scrub_pages);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic ssize_t show_target_kb(struct device *dev, struct device_attribute *attr,
1568c2ecf20Sopenharmony_ci			      char *buf)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	return sprintf(buf, "%lu\n", PAGES2KB(balloon_stats.target_pages));
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic ssize_t store_target_kb(struct device *dev,
1628c2ecf20Sopenharmony_ci			       struct device_attribute *attr,
1638c2ecf20Sopenharmony_ci			       const char *buf,
1648c2ecf20Sopenharmony_ci			       size_t count)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	char *endchar;
1678c2ecf20Sopenharmony_ci	unsigned long long target_bytes;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
1708c2ecf20Sopenharmony_ci		return -EPERM;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	target_bytes = simple_strtoull(buf, &endchar, 0) * 1024;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	balloon_set_new_target(target_bytes >> PAGE_SHIFT);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return count;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic DEVICE_ATTR(target_kb, S_IRUGO | S_IWUSR,
1808c2ecf20Sopenharmony_ci		   show_target_kb, store_target_kb);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic ssize_t show_target(struct device *dev, struct device_attribute *attr,
1848c2ecf20Sopenharmony_ci			      char *buf)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	return sprintf(buf, "%llu\n",
1878c2ecf20Sopenharmony_ci		       (unsigned long long)balloon_stats.target_pages
1888c2ecf20Sopenharmony_ci		       << PAGE_SHIFT);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic ssize_t store_target(struct device *dev,
1928c2ecf20Sopenharmony_ci			    struct device_attribute *attr,
1938c2ecf20Sopenharmony_ci			    const char *buf,
1948c2ecf20Sopenharmony_ci			    size_t count)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	char *endchar;
1978c2ecf20Sopenharmony_ci	unsigned long long target_bytes;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
2008c2ecf20Sopenharmony_ci		return -EPERM;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	target_bytes = memparse(buf, &endchar);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	balloon_set_new_target(target_bytes >> PAGE_SHIFT);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return count;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic DEVICE_ATTR(target, S_IRUGO | S_IWUSR,
2108c2ecf20Sopenharmony_ci		   show_target, store_target);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic struct attribute *balloon_attrs[] = {
2148c2ecf20Sopenharmony_ci	&dev_attr_target_kb.attr,
2158c2ecf20Sopenharmony_ci	&dev_attr_target.attr,
2168c2ecf20Sopenharmony_ci	&dev_attr_schedule_delay.attr.attr,
2178c2ecf20Sopenharmony_ci	&dev_attr_max_schedule_delay.attr.attr,
2188c2ecf20Sopenharmony_ci	&dev_attr_retry_count.attr.attr,
2198c2ecf20Sopenharmony_ci	&dev_attr_max_retry_count.attr.attr,
2208c2ecf20Sopenharmony_ci	&dev_attr_scrub_pages.attr.attr,
2218c2ecf20Sopenharmony_ci	NULL
2228c2ecf20Sopenharmony_ci};
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic const struct attribute_group balloon_group = {
2258c2ecf20Sopenharmony_ci	.attrs = balloon_attrs
2268c2ecf20Sopenharmony_ci};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic struct attribute *balloon_info_attrs[] = {
2298c2ecf20Sopenharmony_ci	&dev_attr_current_kb.attr,
2308c2ecf20Sopenharmony_ci	&dev_attr_low_kb.attr,
2318c2ecf20Sopenharmony_ci	&dev_attr_high_kb.attr,
2328c2ecf20Sopenharmony_ci	NULL
2338c2ecf20Sopenharmony_ci};
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic const struct attribute_group balloon_info_group = {
2368c2ecf20Sopenharmony_ci	.name = "info",
2378c2ecf20Sopenharmony_ci	.attrs = balloon_info_attrs
2388c2ecf20Sopenharmony_ci};
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic const struct attribute_group *balloon_groups[] = {
2418c2ecf20Sopenharmony_ci	&balloon_group,
2428c2ecf20Sopenharmony_ci	&balloon_info_group,
2438c2ecf20Sopenharmony_ci	NULL
2448c2ecf20Sopenharmony_ci};
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic struct bus_type balloon_subsys = {
2478c2ecf20Sopenharmony_ci	.name = BALLOON_CLASS_NAME,
2488c2ecf20Sopenharmony_ci	.dev_name = BALLOON_CLASS_NAME,
2498c2ecf20Sopenharmony_ci};
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int register_balloon(struct device *dev)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	int error;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	error = subsys_system_register(&balloon_subsys, NULL);
2568c2ecf20Sopenharmony_ci	if (error)
2578c2ecf20Sopenharmony_ci		return error;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	dev->id = 0;
2608c2ecf20Sopenharmony_ci	dev->bus = &balloon_subsys;
2618c2ecf20Sopenharmony_ci	dev->groups = balloon_groups;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	error = device_register(dev);
2648c2ecf20Sopenharmony_ci	if (error) {
2658c2ecf20Sopenharmony_ci		bus_unregister(&balloon_subsys);
2668c2ecf20Sopenharmony_ci		return error;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return 0;
2708c2ecf20Sopenharmony_ci}
271