// SPDX-License-Identifier: GPL-2.0/* * VMware Balloon driver. * * Copyright (C) 2000-2018, VMware, Inc. All Rights Reserved. * * This is VMware physical memory management driver for Linux. The driver * acts like a "balloon" that can be inflated to reclaim physical pages by * reserving them in the guest and invalidating them in the monitor, * freeing up the underlying machine pages so they can be allocated to * other guests. The balloon can also be deflated to allow the guest to * use more physical memory. Higher level policies can control the sizes * of balloons in VMs in order to manage physical memory resources. */ //#define DEBUG#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/types.h>#include <linux/io.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/vmalloc.h>#include <linux/sched.h>#include <linux/module.h>#include <linux/workqueue.h>#include <linux/debugfs.h>#include <linux/seq_file.h>#include <linux/rwsem.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/balloon.h>#include <linux/vmw_vmci_defs.h>#include <linux/vmw_vmci_api.h>#include <asm/hypervisor.h> MODULE_AUTHOR("VMware, Inc.");MODULE_DESCRIPTION("VMware Memory Control (Balloon) Driver");MODULE_ALIAS("dmi:*:svnVMware*:*");MODULE_ALIAS("vmware_vmmemctl");MODULE_LICENSE("GPL"); static bool __read_mostly vmwballoon_shrinker_enable;module_param(vmwballoon_shrinker_enable, bool, 0444);MODULE_PARM_DESC(vmwballoon_shrinker_enable, "Enable non-cooperative out-of-memory protection. Disabled by default as it may degrade performance."); /* Delay in seconds after shrink before inflation. */#define VMBALLOON_SHRINK_DELAY (5) /* Maximum number of refused pages we accumulate during inflation cycle */#define VMW_BALLOON_MAX_REFUSED 16 /* Magic number for the balloon mount-point */#define BALLOON_VMW_MAGIC 0x0ba11007 /* * Hypervisor communication port definitions. */#define VMW_BALLOON_HV_PORT 0x5670#define VMW_BALLOON_HV_MAGIC 0x456c6d6f#define VMW_BALLOON_GUEST_ID 1 /* Linux */ enum vmwballoon_capabilities { /* * Bit 0 is reserved and not associated to any capability. */ VMW_BALLOON_BASIC_CMDS = (1 << 1), VMW_BALLOON_BATCHED_CMDS = (1 << 2), VMW_BALLOON_BATCHED_2M_CMDS = (1 << 3), VMW_BALLOON_SIGNALLED_WAKEUP_CMD = (1 << 4), VMW_BALLOON_64_BIT_TARGET = (1 << 5)}; #define VMW_BALLOON_CAPABILITIES_COMMON (VMW_BALLOON_BASIC_CMDS \ | VMW_BALLOON_BATCHED_CMDS \ | VMW_BALLOON_BATCHED_2M_CMDS \ | VMW_BALLOON_SIGNALLED_WAKEUP_CMD) #define VMW_BALLOON_2M_ORDER (PMD_SHIFT - PAGE_SHIFT) /* * 64-bit targets are only supported in 64-bit */#ifdef CONFIG_64BIT#define VMW_BALLOON_CAPABILITIES (VMW_BALLOON_CAPABILITIES_COMMON \ | VMW_BALLOON_64_BIT_TARGET)#else#define VMW_BALLOON_CAPABILITIES VMW_BALLOON_CAPABILITIES_COMMON#endif enum vmballoon_page_size_type { VMW_BALLOON_4K_PAGE, VMW_BALLOON_2M_PAGE, VMW_BALLOON_LAST_SIZE = VMW_BALLOON_2M_PAGE}; #define VMW_BALLOON_NUM_PAGE_SIZES (VMW_BALLOON_LAST_SIZE + 1) static const char * const vmballoon_page_size_names[] = { [VMW_BALLOON_4K_PAGE] = "4k", [VMW_BALLOON_2M_PAGE] = "2M"}; enum vmballoon_op { VMW_BALLOON_INFLATE, VMW_BALLOON_DEFLATE}; enum vmballoon_op_stat_type { VMW_BALLOON_OP_STAT, VMW_BALLOON_OP_FAIL_STAT}; #define VMW_BALLOON_OP_STAT_TYPES (VMW_BALLOON_OP_FAIL_STAT + 1) /** * enum vmballoon_cmd_type - backdoor commands. * * Availability of the commands is as followed: * * %VMW_BALLOON_CMD_START, %VMW_BALLOON_CMD_GET_TARGET and * %VMW_BALLOON_CMD_GUEST_ID are always available. * * If the host reports %VMW_BALLOON_BASIC_CMDS are supported then * %VMW_BALLOON_CMD_LOCK and %VMW_BALLOON_CMD_UNLOCK commands are available. * * If the host reports %VMW_BALLOON_BATCHED_CMDS are supported then * %VMW_BALLOON_CMD_BATCHED_LOCK and VMW_BALLOON_CMD_BATCHED_UNLOCK commands * are available. * * If the host reports %VMW_BALLOON_BATCHED_2M_CMDS are supported then * %VMW_BALLOON_CMD_BATCHED_2M_LOCK and %VMW_BALLOON_CMD_BATCHED_2M_UNLOCK * are supported. * * If the host reports VMW_BALLOON_SIGNALLED_WAKEUP_CMD is supported then * VMW_BALLOON_CMD_VMCI_DOORBELL_SET command is supported. * * @VMW_BALLOON_CMD_START: Communicating supported version with the hypervisor. * @VMW_BALLOON_CMD_GET_TARGET: Gets the balloon target size. * @VMW_BALLOON_CMD_LOCK: Informs the hypervisor about a ballooned page. * @VMW_BALLOON_CMD_UNLOCK: Informs the hypervisor about a page that is about * to be deflated from the balloon. * @VMW_BALLOON_CMD_GUEST_ID: Informs the hypervisor about the type of OS that * runs in the VM. * @VMW_BALLOON_CMD_BATCHED_LOCK: Inform the hypervisor about a batch of * ballooned pages (up to 512). * @VMW_BALLOON_CMD_BATCHED_UNLOCK: Inform the hypervisor about a batch of * pages that are about to be deflated from the * balloon (up to 512). * @VMW_BALLOON_CMD_BATCHED_2M_LOCK: Similar to @VMW_BALLOON_CMD_BATCHED_LOCK * for 2MB pages. * @VMW_BALLOON_CMD_BATCHED_2M_UNLOCK: Similar to * @VMW_BALLOON_CMD_BATCHED_UNLOCK for 2MB * pages. * @VMW_BALLOON_CMD_VMCI_DOORBELL_SET: A command to set doorbell notification * that would be invoked when the balloon * size changes. * @VMW_BALLOON_CMD_LAST: Value of the last command. */enum vmballoon_cmd_type { VMW_BALLOON_CMD_START, VMW_BALLOON_CMD_GET_TARGET, VMW_BALLOON_CMD_LOCK, VMW_BALLOON_CMD_UNLOCK, VMW_BALLOON_CMD_GUEST_ID, /* No command 5 */ VMW_BALLOON_CMD_BATCHED_LOCK = 6, VMW_BALLOON_CMD_BATCHED_UNLOCK, VMW_BALLOON_CMD_BATCHED_2M_LOCK, VMW_BALLOON_CMD_BATCHED_2M_UNLOCK, VMW_BALLOON_CMD_VMCI_DOORBELL_SET, VMW_BALLOON_CMD_LAST = VMW_BALLOON_CMD_VMCI_DOORBELL_SET,}; #define VMW_BALLOON_CMD_NUM (VMW_BALLOON_CMD_LAST + 1) enum vmballoon_error_codes { VMW_BALLOON_SUCCESS, VMW_BALLOON_ERROR_CMD_INVALID, VMW_BALLOON_ERROR_PPN_INVALID, VMW_BALLOON_ERROR_PPN_LOCKED, VMW_BALLOON_ERROR_PPN_UNLOCKED, VMW_BALLOON_ERROR_PPN_PINNED, VMW_BALLOON_ERROR_PPN_NOTNEEDED, VMW_BALLOON_ERROR_RESET, VMW_BALLOON_ERROR_BUSY}; #define VMW_BALLOON_SUCCESS_WITH_CAPABILITIES (0x03000000) #define VMW_BALLOON_CMD_WITH_TARGET_MASK \ ((1UL << VMW_BALLOON_CMD_GET_TARGET) | \ (1UL << VMW_BALLOON_CMD_LOCK) | \ (1UL << VMW_BALLOON_CMD_UNLOCK) | \ (1UL << VMW_BALLOON_CMD_BATCHED_LOCK) | \ (1UL << VMW_BALLOON_CMD_BATCHED_UNLOCK) | \ (1UL << VMW_BALLOON_CMD_BATCHED_2M_LOCK) | \ (1UL << VMW_BALLOON_CMD_BATCHED_2M_UNLOCK)) static const char * const vmballoon_cmd_names[] = { [VMW_BALLOON_CMD_START] = "start", [VMW_BALLOON_CMD_GET_TARGET] = "target", [VMW_BALLOON_CMD_LOCK] = "lock", [VMW_BALLOON_CMD_UNLOCK] = "unlock", [VMW_BALLOON_CMD_GUEST_ID] = "guestType", [VMW_BALLOON_CMD_BATCHED_LOCK] = "batchLock", [VMW_BALLOON_CMD_BATCHED_UNLOCK] = "batchUnlock", [VMW_BALLOON_CMD_BATCHED_2M_LOCK] = "2m-lock", [VMW_BALLOON_CMD_BATCHED_2M_UNLOCK] = "2m-unlock", [VMW_BALLOON_CMD_VMCI_DOORBELL_SET] = "doorbellSet"}; enum vmballoon_stat_page { VMW_BALLOON_PAGE_STAT_ALLOC, VMW_BALLOON_PAGE_STAT_ALLOC_FAIL, VMW_BALLOON_PAGE_STAT_REFUSED_ALLOC, VMW_BALLOON_PAGE_STAT_REFUSED_FREE, VMW_BALLOON_PAGE_STAT_FREE, VMW_BALLOON_PAGE_STAT_LAST = VMW_BALLOON_PAGE_STAT_FREE}; #define VMW_BALLOON_PAGE_STAT_NUM (VMW_BALLOON_PAGE_STAT_LAST + 1) enum vmballoon_stat_general { VMW_BALLOON_STAT_TIMER, VMW_BALLOON_STAT_DOORBELL, VMW_BALLOON_STAT_RESET, VMW_BALLOON_STAT_SHRINK, VMW_BALLOON_STAT_SHRINK_FREE, VMW_BALLOON_STAT_LAST = VMW_BALLOON_STAT_SHRINK_FREE}; #define VMW_BALLOON_STAT_NUM (VMW_BALLOON_STAT_LAST + 1) static DEFINE_STATIC_KEY_TRUE(vmw_balloon_batching);static DEFINE_STATIC_KEY_FALSE(balloon_stat_enabled); struct vmballoon_ctl { struct list_head pages; struct list_head refused_pages; struct list_head prealloc_pages; unsigned int n_refused_pages; unsigned int n_pages; enum vmballoon_page_size_type page_size; enum vmballoon_op op;}; /** * struct vmballoon_batch_entry - a batch entry for lock or unlock. * * @status: the status of the operation, which is written by the hypervisor. * @reserved: reserved for future use. Must be set to zero. * @pfn: the physical frame number of the page to be locked or unlocked. */struct vmballoon_batch_entry { u64 status : 5; u64 reserved : PAGE_SHIFT - 5; u64 pfn : 52;} __packed; struct vmballoon { /** * @max_page_size: maximum supported page size for ballooning. * * Protected by @conf_sem */ enum vmballoon_page_size_type max_page_size; /** * @size: balloon actual size in basic page size (frames). * * While we currently do not support size which is bigger than 32-bit, * in preparation for future support, use 64-bits. */ atomic64_t size; /** * @target: balloon target size in basic page size (frames). * * We do not protect the target under the assumption that setting the * value is always done through a single write. If this assumption ever * breaks, we would have to use X_ONCE for accesses, and suffer the less * optimized code. Although we may read stale target value if multiple * accesses happen at once, the performance impact should be minor. */ unsigned long target; /** * @reset_required: reset flag * * Setting this flag may introduce races, but the code is expected to * handle them gracefully. In the worst case, another operation will * fail as reset did not take place. Clearing the flag is done while * holding @conf_sem for write. */ bool reset_required; /** * @capabilities: hypervisor balloon capabilities. * * Protected by @conf_sem. */ unsigned long capabilities; /** * @batch_page: pointer to communication batch page. * * When batching is used, batch_page points to a page, which holds up to * %VMW_BALLOON_BATCH_MAX_PAGES entries for locking or unlocking. */ struct vmballoon_batch_entry *batch_page; /** * @batch_max_pages: maximum pages that can be locked/unlocked. * * Indicates the number of pages that the hypervisor can lock or unlock * at once, according to whether batching is enabled. If batching is * disabled, only a single page can be locked/unlock on each operation. * * Protected by @conf_sem. */ unsigned int batch_max_pages; /** * @page: page to be locked/unlocked by the hypervisor * * @page is only used when batching is disabled and a single page is * reclaimed on each iteration. * * Protected by @comm_lock. */ struct page *page; /** * @shrink_timeout: timeout until the next inflation. * * After an shrink event, indicates the time in jiffies after which * inflation is allowed again. Can be written concurrently with reads, * so must use READ_ONCE/WRITE_ONCE when accessing. */ unsigned long shrink_timeout; /* statistics */ struct vmballoon_stats *stats; /** * @b_dev_info: balloon device information descriptor. */ struct balloon_dev_info b_dev_info; struct delayed_work dwork; /** * @huge_pages - list of the inflated 2MB pages. * * Protected by @huge_pages_lock. */ struct list_head huge_pages; /** * @huge_pages_lock: lock for the list of inflated 2MB pages. */ spinlock_t huge_pages_lock; /** * @vmci_doorbell. * * Protected by @conf_sem. */ struct vmci_handle vmci_doorbell; /** * @conf_sem: semaphore to protect the configuration and the statistics. */ struct rw_semaphore conf_sem; /** * @comm_lock: lock to protect the communication with the host. * * Lock ordering: @conf_sem -> @comm_lock . */ spinlock_t comm_lock; /** * @shrinker: shrinker interface that is used to avoid over-inflation. */ struct shrinker *shrinker;}; static struct vmballoon balloon; struct vmballoon_stats { /* timer / doorbell operations */ atomic64_t general_stat[VMW_BALLOON_STAT_NUM]; /* allocation statistics for huge and small pages */ atomic64_t page_stat[VMW_BALLOON_PAGE_STAT_NUM][VMW_BALLOON_NUM_PAGE_SIZES]; /* Monitor operations: total operations, and failures */ atomic64_t ops[VMW_BALLOON_CMD_NUM][VMW_BALLOON_OP_STAT_TYPES];}; static inline bool is_vmballoon_stats_on(void){ return IS_ENABLED(CONFIG_DEBUG_FS) && static_branch_unlikely(&balloon_stat_enabled);} static inline void vmballoon_stats_op_inc(struct vmballoon *b, unsigned int op, enum vmballoon_op_stat_type type){ if (is_vmballoon_stats_on()) atomic64_inc(&b->stats->ops[op][type]);} static inline void vmballoon_stats_gen_inc(struct vmballoon *b, enum vmballoon_stat_general stat){ if (is_vmballoon_stats_on()) atomic64_inc(&b->stats->general_stat[stat]);} static inline void vmballoon_stats_gen_add(struct vmballoon *b, enum vmballoon_stat_general stat, unsigned int val){ if (is_vmballoon_stats_on()) atomic64_add(val, &b->stats->general_stat[stat]);} static inline void vmballoon_stats_page_inc(struct vmballoon *b, enum vmballoon_stat_page stat, enum vmballoon_page_size_type size){ if (is_vmballoon_stats_on()) atomic64_inc(&b->stats->page_stat[stat][size]);} static inline void vmballoon_stats_page_add(struct vmballoon *b, enum vmballoon_stat_page stat, enum vmballoon_page_size_type size, unsigned int val){ if (is_vmballoon_stats_on()) atomic64_add(val, &b->stats->page_stat[stat][size]);} static inline unsigned long__vmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, unsigned long arg2, unsigned long *result){ unsigned long status, dummy1, dummy2, dummy3, local_result; vmballoon_stats_op_inc(b, cmd, VMW_BALLOON_OP_STAT); asm volatile ("inl %%dx" : "=a"(status), "=c"(dummy1), "=d"(dummy2), "=b"(local_result), "=S"(dummy3) : "0"(VMW_BALLOON_HV_MAGIC), "1"(cmd), "2"(VMW_BALLOON_HV_PORT), "3"(arg1), "4"(arg2) : "memory"); /* update the result if needed */ if (result) *result = (cmd == VMW_BALLOON_CMD_START) ? dummy1 : local_result; /* update target when applicable */ if (status == VMW_BALLOON_SUCCESS && ((1ul << cmd) & VMW_BALLOON_CMD_WITH_TARGET_MASK)) WRITE_ONCE(b->target, local_result); if (status != VMW_BALLOON_SUCCESS && status != VMW_BALLOON_SUCCESS_WITH_CAPABILITIES) { vmballoon_stats_op_inc(b, cmd, VMW_BALLOON_OP_FAIL_STAT); pr_debug("%s: %s [0x%lx,0x%lx) failed, returned %ld\n", __func__, vmballoon_cmd_names[cmd], arg1, arg2, status); } /* mark reset required accordingly */ if (status == VMW_BALLOON_ERROR_RESET) b->reset_required = true; return status;} static __always_inline unsigned longvmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, unsigned long arg2){ unsigned long dummy;Lines 1–500 of 1873. Load more
// SPDX-License-Identifier: GPL-2.0 /* * VMware Balloon driver. * * Copyright (C) 2000-2018, VMware, Inc. All Rights Reserved. * * This is VMware physical memory management driver for Linux. The driver * acts like a "balloon" that can be inflated to reclaim physical pages by * reserving them in the guest and invalidating them in the monitor, * freeing up the underlying machine pages so they can be allocated to * other guests. The balloon can also be deflated to allow the guest to * use more physical memory. Higher level policies can control the sizes * of balloons in VMs in order to manage physical memory resources. */ //#define DEBUG #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/types.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/sched.h> #include <linux/module.h> #include <linux/workqueue.h> #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/rwsem.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/balloon.h> #include <linux/vmw_vmci_defs.h> #include <linux/vmw_vmci_api.h> #include <asm/hypervisor.h> MODULE_AUTHOR("VMware, Inc."); MODULE_DESCRIPTION("VMware Memory Control (Balloon) Driver"); MODULE_ALIAS("dmi:*:svnVMware*:*"); MODULE_ALIAS("vmware_vmmemctl"); MODULE_LICENSE("GPL"); static bool __read_mostly vmwballoon_shrinker_enable; module_param(vmwballoon_shrinker_enable, bool, 0444); MODULE_PARM_DESC(vmwballoon_shrinker_enable, "Enable non-cooperative out-of-memory protection. Disabled by default as it may degrade performance."); /* Delay in seconds after shrink before inflation. */ #define VMBALLOON_SHRINK_DELAY (5) /* Maximum number of refused pages we accumulate during inflation cycle */ #define VMW_BALLOON_MAX_REFUSED 16 /* Magic number for the balloon mount-point */ #define BALLOON_VMW_MAGIC 0x0ba11007 /* * Hypervisor communication port definitions. */ #define VMW_BALLOON_HV_PORT 0x5670 #define VMW_BALLOON_HV_MAGIC 0x456c6d6f #define VMW_BALLOON_GUEST_ID 1 /* Linux */ enum vmwballoon_capabilities { /* * Bit 0 is reserved and not associated to any capability. */ VMW_BALLOON_BASIC_CMDS = (1 << 1), VMW_BALLOON_BATCHED_CMDS = (1 << 2), VMW_BALLOON_BATCHED_2M_CMDS = (1 << 3), VMW_BALLOON_SIGNALLED_WAKEUP_CMD = (1 << 4), VMW_BALLOON_64_BIT_TARGET = (1 << 5) }; #define VMW_BALLOON_CAPABILITIES_COMMON (VMW_BALLOON_BASIC_CMDS \ | VMW_BALLOON_BATCHED_CMDS \ | VMW_BALLOON_BATCHED_2M_CMDS \ | VMW_BALLOON_SIGNALLED_WAKEUP_CMD) #define VMW_BALLOON_2M_ORDER (PMD_SHIFT - PAGE_SHIFT) /* * 64-bit targets are only supported in 64-bit */ #ifdef CONFIG_64BIT #define VMW_BALLOON_CAPABILITIES (VMW_BALLOON_CAPABILITIES_COMMON \ | VMW_BALLOON_64_BIT_TARGET) #else #define VMW_BALLOON_CAPABILITIES VMW_BALLOON_CAPABILITIES_COMMON #endif enum vmballoon_page_size_type { VMW_BALLOON_4K_PAGE, VMW_BALLOON_2M_PAGE, VMW_BALLOON_LAST_SIZE = VMW_BALLOON_2M_PAGE }; #define VMW_BALLOON_NUM_PAGE_SIZES (VMW_BALLOON_LAST_SIZE + 1) static const char * const vmballoon_page_size_names[] = { [VMW_BALLOON_4K_PAGE] = "4k", [VMW_BALLOON_2M_PAGE] = "2M" }; enum vmballoon_op { VMW_BALLOON_INFLATE, VMW_BALLOON_DEFLATE }; enum vmballoon_op_stat_type { VMW_BALLOON_OP_STAT, VMW_BALLOON_OP_FAIL_STAT }; #define VMW_BALLOON_OP_STAT_TYPES (VMW_BALLOON_OP_FAIL_STAT + 1) /** * enum vmballoon_cmd_type - backdoor commands. * * Availability of the commands is as followed: * * %VMW_BALLOON_CMD_START, %VMW_BALLOON_CMD_GET_TARGET and * %VMW_BALLOON_CMD_GUEST_ID are always available. * * If the host reports %VMW_BALLOON_BASIC_CMDS are supported then * %VMW_BALLOON_CMD_LOCK and %VMW_BALLOON_CMD_UNLOCK commands are available. * * If the host reports %VMW_BALLOON_BATCHED_CMDS are supported then * %VMW_BALLOON_CMD_BATCHED_LOCK and VMW_BALLOON_CMD_BATCHED_UNLOCK commands * are available. * * If the host reports %VMW_BALLOON_BATCHED_2M_CMDS are supported then * %VMW_BALLOON_CMD_BATCHED_2M_LOCK and %VMW_BALLOON_CMD_BATCHED_2M_UNLOCK * are supported. * * If the host reports VMW_BALLOON_SIGNALLED_WAKEUP_CMD is supported then * VMW_BALLOON_CMD_VMCI_DOORBELL_SET command is supported. * * @VMW_BALLOON_CMD_START: Communicating supported version with the hypervisor. * @VMW_BALLOON_CMD_GET_TARGET: Gets the balloon target size. * @VMW_BALLOON_CMD_LOCK: Informs the hypervisor about a ballooned page. * @VMW_BALLOON_CMD_UNLOCK: Informs the hypervisor about a page that is about * to be deflated from the balloon. * @VMW_BALLOON_CMD_GUEST_ID: Informs the hypervisor about the type of OS that * runs in the VM. * @VMW_BALLOON_CMD_BATCHED_LOCK: Inform the hypervisor about a batch of * ballooned pages (up to 512). * @VMW_BALLOON_CMD_BATCHED_UNLOCK: Inform the hypervisor about a batch of * pages that are about to be deflated from the * balloon (up to 512). * @VMW_BALLOON_CMD_BATCHED_2M_LOCK: Similar to @VMW_BALLOON_CMD_BATCHED_LOCK * for 2MB pages. * @VMW_BALLOON_CMD_BATCHED_2M_UNLOCK: Similar to * @VMW_BALLOON_CMD_BATCHED_UNLOCK for 2MB * pages. * @VMW_BALLOON_CMD_VMCI_DOORBELL_SET: A command to set doorbell notification * that would be invoked when the balloon * size changes. * @VMW_BALLOON_CMD_LAST: Value of the last command. */ enum vmballoon_cmd_type { VMW_BALLOON_CMD_START, VMW_BALLOON_CMD_GET_TARGET, VMW_BALLOON_CMD_LOCK, VMW_BALLOON_CMD_UNLOCK, VMW_BALLOON_CMD_GUEST_ID, /* No command 5 */ VMW_BALLOON_CMD_BATCHED_LOCK = 6, VMW_BALLOON_CMD_BATCHED_UNLOCK, VMW_BALLOON_CMD_BATCHED_2M_LOCK, VMW_BALLOON_CMD_BATCHED_2M_UNLOCK, VMW_BALLOON_CMD_VMCI_DOORBELL_SET, VMW_BALLOON_CMD_LAST = VMW_BALLOON_CMD_VMCI_DOORBELL_SET, }; #define VMW_BALLOON_CMD_NUM (VMW_BALLOON_CMD_LAST + 1) enum vmballoon_error_codes { VMW_BALLOON_SUCCESS, VMW_BALLOON_ERROR_CMD_INVALID, VMW_BALLOON_ERROR_PPN_INVALID, VMW_BALLOON_ERROR_PPN_LOCKED, VMW_BALLOON_ERROR_PPN_UNLOCKED, VMW_BALLOON_ERROR_PPN_PINNED, VMW_BALLOON_ERROR_PPN_NOTNEEDED, VMW_BALLOON_ERROR_RESET, VMW_BALLOON_ERROR_BUSY }; #define VMW_BALLOON_SUCCESS_WITH_CAPABILITIES (0x03000000) #define VMW_BALLOON_CMD_WITH_TARGET_MASK \ ((1UL << VMW_BALLOON_CMD_GET_TARGET) | \ (1UL << VMW_BALLOON_CMD_LOCK) | \ (1UL << VMW_BALLOON_CMD_UNLOCK) | \ (1UL << VMW_BALLOON_CMD_BATCHED_LOCK) | \ (1UL << VMW_BALLOON_CMD_BATCHED_UNLOCK) | \ (1UL << VMW_BALLOON_CMD_BATCHED_2M_LOCK) | \ (1UL << VMW_BALLOON_CMD_BATCHED_2M_UNLOCK)) static const char * const vmballoon_cmd_names[] = { [VMW_BALLOON_CMD_START] = "start", [VMW_BALLOON_CMD_GET_TARGET] = "target", [VMW_BALLOON_CMD_LOCK] = "lock", [VMW_BALLOON_CMD_UNLOCK] = "unlock", [VMW_BALLOON_CMD_GUEST_ID] = "guestType", [VMW_BALLOON_CMD_BATCHED_LOCK] = "batchLock", [VMW_BALLOON_CMD_BATCHED_UNLOCK] = "batchUnlock", [VMW_BALLOON_CMD_BATCHED_2M_LOCK] = "2m-lock", [VMW_BALLOON_CMD_BATCHED_2M_UNLOCK] = "2m-unlock", [VMW_BALLOON_CMD_VMCI_DOORBELL_SET] = "doorbellSet" }; enum vmballoon_stat_page { VMW_BALLOON_PAGE_STAT_ALLOC, VMW_BALLOON_PAGE_STAT_ALLOC_FAIL, VMW_BALLOON_PAGE_STAT_REFUSED_ALLOC, VMW_BALLOON_PAGE_STAT_REFUSED_FREE, VMW_BALLOON_PAGE_STAT_FREE, VMW_BALLOON_PAGE_STAT_LAST = VMW_BALLOON_PAGE_STAT_FREE }; #define VMW_BALLOON_PAGE_STAT_NUM (VMW_BALLOON_PAGE_STAT_LAST + 1) enum vmballoon_stat_general { VMW_BALLOON_STAT_TIMER, VMW_BALLOON_STAT_DOORBELL, VMW_BALLOON_STAT_RESET, VMW_BALLOON_STAT_SHRINK, VMW_BALLOON_STAT_SHRINK_FREE, VMW_BALLOON_STAT_LAST = VMW_BALLOON_STAT_SHRINK_FREE }; #define VMW_BALLOON_STAT_NUM (VMW_BALLOON_STAT_LAST + 1) static DEFINE_STATIC_KEY_TRUE(vmw_balloon_batching); static DEFINE_STATIC_KEY_FALSE(balloon_stat_enabled); struct vmballoon_ctl { struct list_head pages; struct list_head refused_pages; struct list_head prealloc_pages; unsigned int n_refused_pages; unsigned int n_pages; enum vmballoon_page_size_type page_size; enum vmballoon_op op; }; /** * struct vmballoon_batch_entry - a batch entry for lock or unlock. * * @status: the status of the operation, which is written by the hypervisor. * @reserved: reserved for future use. Must be set to zero. * @pfn: the physical frame number of the page to be locked or unlocked. */ struct vmballoon_batch_entry { u64 status : 5; u64 reserved : PAGE_SHIFT - 5; u64 pfn : 52; } __packed; struct vmballoon { /** * @max_page_size: maximum supported page size for ballooning. * * Protected by @conf_sem */ enum vmballoon_page_size_type max_page_size; /** * @size: balloon actual size in basic page size (frames). * * While we currently do not support size which is bigger than 32-bit, * in preparation for future support, use 64-bits. */ atomic64_t size; /** * @target: balloon target size in basic page size (frames). * * We do not protect the target under the assumption that setting the * value is always done through a single write. If this assumption ever * breaks, we would have to use X_ONCE for accesses, and suffer the less * optimized code. Although we may read stale target value if multiple * accesses happen at once, the performance impact should be minor. */ unsigned long target; /** * @reset_required: reset flag * * Setting this flag may introduce races, but the code is expected to * handle them gracefully. In the worst case, another operation will * fail as reset did not take place. Clearing the flag is done while * holding @conf_sem for write. */ bool reset_required; /** * @capabilities: hypervisor balloon capabilities. * * Protected by @conf_sem. */ unsigned long capabilities; /** * @batch_page: pointer to communication batch page. * * When batching is used, batch_page points to a page, which holds up to * %VMW_BALLOON_BATCH_MAX_PAGES entries for locking or unlocking. */ struct vmballoon_batch_entry *batch_page; /** * @batch_max_pages: maximum pages that can be locked/unlocked. * * Indicates the number of pages that the hypervisor can lock or unlock * at once, according to whether batching is enabled. If batching is * disabled, only a single page can be locked/unlock on each operation. * * Protected by @conf_sem. */ unsigned int batch_max_pages; /** * @page: page to be locked/unlocked by the hypervisor * * @page is only used when batching is disabled and a single page is * reclaimed on each iteration. * * Protected by @comm_lock. */ struct page *page; /** * @shrink_timeout: timeout until the next inflation. * * After an shrink event, indicates the time in jiffies after which * inflation is allowed again. Can be written concurrently with reads, * so must use READ_ONCE/WRITE_ONCE when accessing. */ unsigned long shrink_timeout; /* statistics */ struct vmballoon_stats *stats; /** * @b_dev_info: balloon device information descriptor. */ struct balloon_dev_info b_dev_info; struct delayed_work dwork; /** * @huge_pages - list of the inflated 2MB pages. * * Protected by @huge_pages_lock. */ struct list_head huge_pages; /** * @huge_pages_lock: lock for the list of inflated 2MB pages. */ spinlock_t huge_pages_lock; /** * @vmci_doorbell. * * Protected by @conf_sem. */ struct vmci_handle vmci_doorbell; /** * @conf_sem: semaphore to protect the configuration and the statistics. */ struct rw_semaphore conf_sem; /** * @comm_lock: lock to protect the communication with the host. * * Lock ordering: @conf_sem -> @comm_lock . */ spinlock_t comm_lock; /** * @shrinker: shrinker interface that is used to avoid over-inflation. */ struct shrinker *shrinker; }; static struct vmballoon balloon; struct vmballoon_stats { /* timer / doorbell operations */ atomic64_t general_stat[VMW_BALLOON_STAT_NUM]; /* allocation statistics for huge and small pages */ atomic64_t page_stat[VMW_BALLOON_PAGE_STAT_NUM][VMW_BALLOON_NUM_PAGE_SIZES]; /* Monitor operations: total operations, and failures */ atomic64_t ops[VMW_BALLOON_CMD_NUM][VMW_BALLOON_OP_STAT_TYPES]; }; static inline bool is_vmballoon_stats_on(void) { return IS_ENABLED(CONFIG_DEBUG_FS) && static_branch_unlikely(&balloon_stat_enabled); } static inline void vmballoon_stats_op_inc(struct vmballoon *b, unsigned int op, enum vmballoon_op_stat_type type) { if (is_vmballoon_stats_on()) atomic64_inc(&b->stats->ops[op][type]); } static inline void vmballoon_stats_gen_inc(struct vmballoon *b, enum vmballoon_stat_general stat) { if (is_vmballoon_stats_on()) atomic64_inc(&b->stats->general_stat[stat]); } static inline void vmballoon_stats_gen_add(struct vmballoon *b, enum vmballoon_stat_general stat, unsigned int val) { if (is_vmballoon_stats_on()) atomic64_add(val, &b->stats->general_stat[stat]); } static inline void vmballoon_stats_page_inc(struct vmballoon *b, enum vmballoon_stat_page stat, enum vmballoon_page_size_type size) { if (is_vmballoon_stats_on()) atomic64_inc(&b->stats->page_stat[stat][size]); } static inline void vmballoon_stats_page_add(struct vmballoon *b, enum vmballoon_stat_page stat, enum vmballoon_page_size_type size, unsigned int val) { if (is_vmballoon_stats_on()) atomic64_add(val, &b->stats->page_stat[stat][size]); } static inline unsigned long __vmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, unsigned long arg2, unsigned long *result) { unsigned long status, dummy1, dummy2, dummy3, local_result; vmballoon_stats_op_inc(b, cmd, VMW_BALLOON_OP_STAT); asm volatile ("inl %%dx" : "=a"(status), "=c"(dummy1), "=d"(dummy2), "=b"(local_result), "=S"(dummy3) : "0"(VMW_BALLOON_HV_MAGIC), "1"(cmd), "2"(VMW_BALLOON_HV_PORT), "3"(arg1), "4"(arg2) : "memory"); /* update the result if needed */ if (result) *result = (cmd == VMW_BALLOON_CMD_START) ? dummy1 : local_result; /* update target when applicable */ if (status == VMW_BALLOON_SUCCESS && ((1ul << cmd) & VMW_BALLOON_CMD_WITH_TARGET_MASK)) WRITE_ONCE(b->target, local_result); if (status != VMW_BALLOON_SUCCESS && status != VMW_BALLOON_SUCCESS_WITH_CAPABILITIES) { vmballoon_stats_op_inc(b, cmd, VMW_BALLOON_OP_FAIL_STAT); pr_debug("%s: %s [0x%lx,0x%lx) failed, returned %ld\n", __func__, vmballoon_cmd_names[cmd], arg1, arg2, status); } /* mark reset required accordingly */ if (status == VMW_BALLOON_ERROR_RESET) b->reset_required = true; return status; } static __always_inline unsigned long vmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, unsigned long arg2) { unsigned long dummy;