lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <18edfd09-37b0-d185-fb0e-6f502ae4951d@amd.com>
Date:   Wed, 13 Jan 2021 09:19:56 -0600
From:   Brijesh Singh <brijesh.singh@....com>
To:     Vipin Sharma <vipinsh@...gle.com>, thomas.lendacky@....com,
        jon.grimm@....com, eric.vantassell@....com, pbonzini@...hat.com,
        seanjc@...gle.com, tj@...nel.org, lizefan@...wei.com,
        hannes@...xchg.org, frankja@...ux.ibm.com, borntraeger@...ibm.com,
        corbet@....net
Cc:     joro@...tes.org, vkuznets@...hat.com, wanpengli@...cent.com,
        jmattson@...gle.com, tglx@...utronix.de, mingo@...hat.com,
        bp@...en8.de, hpa@...or.com, gingell@...gle.com,
        rientjes@...gle.com, dionnaglaze@...gle.com, kvm@...r.kernel.org,
        x86@...nel.org, cgroups@...r.kernel.org, linux-doc@...r.kernel.org,
        linux-kernel@...r.kernel.org
Subject: Re: [Patch v4 1/2] cgroup: svm: Add Encryption ID controller


On 1/7/2021 7:28 PM, Vipin Sharma wrote:
> Hardware memory encryption is available on multiple generic CPUs. For
> example AMD has Secure Encrypted Virtualization (SEV) and SEV -
> Encrypted State (SEV-ES).
>
> These memory encryptions are useful in creating encrypted virtual
> machines (VMs) and user space programs.
>
> There are limited number of encryption IDs that can be used
> simultaneously on a machine for encryption. This generates a need for
> the system admin to track, limit, allocate resources, and optimally
> schedule VMs and user workloads in the cloud infrastructure. Some
> malicious programs can exhaust all of these resources on a host causing
> starvation of other workloads.
>
> Encryption ID controller allows control of these resources using
> Cgroups.
>
> Controller is enabled by CGROUP_ENCRYPTION_IDS config option.
> Encryption controller provide 3 interface files for each encryption ID
> type. For example, in SEV:
>
> 1. encrpytion_ids.sev.max
> 	Sets the maximum usage of SEV IDs in the cgroup.
> 2. encryption_ids.sev.current
> 	Current usage of SEV IDs in the cgroup and its children.
> 3. encryption_ids.sev.stat
> 	Shown only at the root cgroup. Displays total SEV IDs available
> 	on the platform and current usage count.
>
> Other ID types can be easily added in the controller in the same way.
>
> Signed-off-by: Vipin Sharma <vipinsh@...gle.com>
> Reviewed-by: David Rientjes <rientjes@...gle.com>
> Reviewed-by: Dionna Glaze <dionnaglaze@...gle.com>


Acked-by: Brijesh Singh <brijesh.singh@....com>

> ---
>   arch/x86/kvm/svm/sev.c                |  52 +++-
>   include/linux/cgroup_subsys.h         |   4 +
>   include/linux/encryption_ids_cgroup.h |  72 +++++
>   include/linux/kvm_host.h              |   4 +
>   init/Kconfig                          |  14 +
>   kernel/cgroup/Makefile                |   1 +
>   kernel/cgroup/encryption_ids.c        | 422 ++++++++++++++++++++++++++
>   7 files changed, 557 insertions(+), 12 deletions(-)
>   create mode 100644 include/linux/encryption_ids_cgroup.h
>   create mode 100644 kernel/cgroup/encryption_ids.c
>
> diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
> index 9858d5ae9ddd..1924ab2eaf11 100644
> --- a/arch/x86/kvm/svm/sev.c
> +++ b/arch/x86/kvm/svm/sev.c
> @@ -14,6 +14,7 @@
>   #include <linux/psp-sev.h>
>   #include <linux/pagemap.h>
>   #include <linux/swap.h>
> +#include <linux/encryption_ids_cgroup.h>
>   #include <linux/processor.h>
>   #include <linux/trace_events.h>
>   #include <asm/fpu/internal.h>
> @@ -86,10 +87,18 @@ static bool __sev_recycle_asids(int min_asid, int max_asid)
>   	return true;
>   }
>   
> -static int sev_asid_new(struct kvm_sev_info *sev)
> +static int sev_asid_new(struct kvm *kvm)
>   {
> -	int pos, min_asid, max_asid;
> +	int pos, min_asid, max_asid, ret;
> +	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
>   	bool retry = true;
> +	enum encryption_id_type type;
> +
> +	type = sev->es_active ? ENCRYPTION_ID_SEV_ES : ENCRYPTION_ID_SEV;
> +
> +	ret = enc_id_cg_try_charge(kvm, type, 1);
> +	if (ret)
> +		return ret;
>   
>   	mutex_lock(&sev_bitmap_lock);
>   
> @@ -107,7 +116,8 @@ static int sev_asid_new(struct kvm_sev_info *sev)
>   			goto again;
>   		}
>   		mutex_unlock(&sev_bitmap_lock);
> -		return -EBUSY;
> +		ret = -EBUSY;
> +		goto e_uncharge;
>   	}
>   
>   	__set_bit(pos, sev_asid_bitmap);
> @@ -115,6 +125,9 @@ static int sev_asid_new(struct kvm_sev_info *sev)
>   	mutex_unlock(&sev_bitmap_lock);
>   
>   	return pos + 1;
> +e_uncharge:
> +	enc_id_cg_uncharge(kvm, type, 1);
> +	return ret;
>   }
>   
>   static int sev_get_asid(struct kvm *kvm)
> @@ -124,14 +137,16 @@ static int sev_get_asid(struct kvm *kvm)
>   	return sev->asid;
>   }
>   
> -static void sev_asid_free(int asid)
> +static void sev_asid_free(struct kvm *kvm)
>   {
>   	struct svm_cpu_data *sd;
>   	int cpu, pos;
> +	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
> +	enum encryption_id_type type;
>   
>   	mutex_lock(&sev_bitmap_lock);
>   
> -	pos = asid - 1;
> +	pos = sev->asid - 1;
>   	__set_bit(pos, sev_reclaim_asid_bitmap);
>   
>   	for_each_possible_cpu(cpu) {
> @@ -140,6 +155,9 @@ static void sev_asid_free(int asid)
>   	}
>   
>   	mutex_unlock(&sev_bitmap_lock);
> +
> +	type = sev->es_active ? ENCRYPTION_ID_SEV_ES : ENCRYPTION_ID_SEV;
> +	enc_id_cg_uncharge(kvm, type, 1);
>   }
>   
>   static void sev_unbind_asid(struct kvm *kvm, unsigned int handle)
> @@ -184,22 +202,22 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
>   	if (unlikely(sev->active))
>   		return ret;
>   
> -	asid = sev_asid_new(sev);
> +	asid = sev_asid_new(kvm);
>   	if (asid < 0)
>   		return ret;
> +	sev->asid = asid;
>   
>   	ret = sev_platform_init(&argp->error);
>   	if (ret)
>   		goto e_free;
>   
>   	sev->active = true;
> -	sev->asid = asid;
>   	INIT_LIST_HEAD(&sev->regions_list);
>   
>   	return 0;
>   
>   e_free:
> -	sev_asid_free(asid);
> +	sev_asid_free(kvm);
>   	return ret;
>   }
>   
> @@ -1240,12 +1258,12 @@ void sev_vm_destroy(struct kvm *kvm)
>   	mutex_unlock(&kvm->lock);
>   
>   	sev_unbind_asid(kvm, sev->handle);
> -	sev_asid_free(sev->asid);
> +	sev_asid_free(kvm);
>   }
>   
>   void __init sev_hardware_setup(void)
>   {
> -	unsigned int eax, ebx, ecx, edx;
> +	unsigned int eax, ebx, ecx, edx, sev_asid_count, sev_es_asid_count;
>   	bool sev_es_supported = false;
>   	bool sev_supported = false;
>   
> @@ -1277,7 +1295,11 @@ void __init sev_hardware_setup(void)
>   	if (!sev_reclaim_asid_bitmap)
>   		goto out;
>   
> -	pr_info("SEV supported: %u ASIDs\n", max_sev_asid - min_sev_asid + 1);
> +	sev_asid_count = max_sev_asid - min_sev_asid + 1;
> +	if (enc_id_cg_set_capacity(ENCRYPTION_ID_SEV, sev_asid_count))
> +		goto out;
> +
> +	pr_info("SEV supported: %u ASIDs\n", sev_asid_count);
>   	sev_supported = true;
>   
>   	/* SEV-ES support requested? */
> @@ -1292,7 +1314,11 @@ void __init sev_hardware_setup(void)
>   	if (min_sev_asid == 1)
>   		goto out;
>   
> -	pr_info("SEV-ES supported: %u ASIDs\n", min_sev_asid - 1);
> +	sev_es_asid_count = min_sev_asid - 1;
> +	if (enc_id_cg_set_capacity(ENCRYPTION_ID_SEV_ES, sev_es_asid_count))
> +		goto out;
> +
> +	pr_info("SEV-ES supported: %u ASIDs\n", sev_es_asid_count);
>   	sev_es_supported = true;
>   
>   out:
> @@ -1307,6 +1333,8 @@ void sev_hardware_teardown(void)
>   
>   	bitmap_free(sev_asid_bitmap);
>   	bitmap_free(sev_reclaim_asid_bitmap);
> +	enc_id_cg_set_capacity(ENCRYPTION_ID_SEV, 0);
> +	enc_id_cg_set_capacity(ENCRYPTION_ID_SEV_ES, 0);
>   
>   	sev_flush_asids();
>   }
> diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h
> index acb77dcff3b4..83754f58c05e 100644
> --- a/include/linux/cgroup_subsys.h
> +++ b/include/linux/cgroup_subsys.h
> @@ -61,6 +61,10 @@ SUBSYS(pids)
>   SUBSYS(rdma)
>   #endif
>   
> +#if IS_ENABLED(CONFIG_CGROUP_ENCRYPTION_IDS)
> +SUBSYS(encryption_ids)
> +#endif
> +
>   /*
>    * The following subsystems are not supported on the default hierarchy.
>    */
> diff --git a/include/linux/encryption_ids_cgroup.h b/include/linux/encryption_ids_cgroup.h
> new file mode 100644
> index 000000000000..af428a4beb28
> --- /dev/null
> +++ b/include/linux/encryption_ids_cgroup.h
> @@ -0,0 +1,72 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Encryption IDs cgroup controller.
> + *
> + * Copyright 2020 Google LLC
> + * Author: Vipin Sharma <vipinsh@...gle.com>
> + */
> +#ifndef _ENCRYPTION_IDS_CGROUP_H_
> +#define _ENCRYPTION_IDS_CGROUP_H_
> +
> +#include <linux/cgroup-defs.h>
> +#include <linux/kvm_types.h>
> +
> +/**
> + * Types of encryption IDs supported by the host.
> + */
> +enum encryption_id_type {
> +#ifdef CONFIG_KVM_AMD_SEV
> +	ENCRYPTION_ID_SEV,
> +	ENCRYPTION_ID_SEV_ES,
> +#endif
> +	ENCRYPTION_ID_TYPES
> +};
> +
> +#ifdef CONFIG_CGROUP_ENCRYPTION_IDS
> +
> +/**
> + * struct encryption_id_res: Per cgroup per encryption ID resource
> + * @max: Maximum count of encryption ID that can be used.
> + * @usage: Current usage of encryption ID in the cgroup.
> + */
> +struct encryption_id_res {
> +	unsigned int max;
> +	unsigned int usage;
> +};
> +
> +/**
> + * struct encryption_id_cgroup - Encryption IDs controller's cgroup structure.
> + * @css: cgroup subsys state object.
> + * @ids: Array of encryption IDs resource usage in the cgroup.
> + */
> +struct encryption_id_cgroup {
> +	struct cgroup_subsys_state css;
> +	struct encryption_id_res res[ENCRYPTION_ID_TYPES];
> +};
> +
> +int enc_id_cg_set_capacity(enum encryption_id_type type, unsigned int capacity);
> +int enc_id_cg_try_charge(struct kvm *kvm, enum encryption_id_type type,
> +			 unsigned int amount);
> +void enc_id_cg_uncharge(struct kvm *kvm, enum encryption_id_type type,
> +			unsigned int amount);
> +#else
> +static inline int enc_id_cg_set_capacity(enum encryption_id_type type,
> +					 unsigned int capacity)
> +{
> +	return 0;
> +}
> +
> +static inline int enc_id_cg_try_charge(struct kvm *kvm,
> +				       enum encryption_id_type type,
> +				       unsigned int amount)
> +{
> +	return 0;
> +}
> +
> +static inline void enc_id_cg_uncharge(struct kvm *kvm,
> +				      enum encryption_id_type type,
> +				      unsigned int amount)
> +{
> +}
> +#endif /* CONFIG_CGROUP_ENCRYPTION_IDS */
> +#endif /* _ENCRYPTION_CGROUP_H_ */
> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
> index f3b1013fb22c..ae9fde0d4267 100644
> --- a/include/linux/kvm_host.h
> +++ b/include/linux/kvm_host.h
> @@ -27,6 +27,7 @@
>   #include <linux/refcount.h>
>   #include <linux/nospec.h>
>   #include <asm/signal.h>
> +#include <linux/encryption_ids_cgroup.h>
>   
>   #include <linux/kvm.h>
>   #include <linux/kvm_para.h>
> @@ -513,6 +514,9 @@ struct kvm {
>   	pid_t userspace_pid;
>   	unsigned int max_halt_poll_ns;
>   	u32 dirty_ring_size;
> +#ifdef CONFIG_CGROUP_ENCRYPTION_IDS
> +	struct encryption_id_cgroup *enc_id_cg;
> +#endif
>   };
>   
>   #define kvm_err(fmt, ...) \
> diff --git a/init/Kconfig b/init/Kconfig
> index b77c60f8b963..6c0bd0e7c08d 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -1106,6 +1106,20 @@ config CGROUP_BPF
>   	  BPF_CGROUP_INET_INGRESS will be executed on the ingress path of
>   	  inet sockets.
>   
> +config CGROUP_ENCRYPTION_IDS
> +	bool "Encryption IDs controller"
> +	depends on KVM_AMD_SEV
> +	default n
> +	help
> +	  Provides a controller for CPU encryption IDs on a host.
> +
> +	  Some platforms have limited number of encryption IDs which can be
> +	  used simultaneously, e.g., AMD's Secure Encrypted Virtualization
> +	  (SEV). This controller tracks and limits the total number of IDs used
> +	  by processes attached to a cgroup hierarchy. For more information,
> +	  please check Encryption IDs section in
> +	  /Documentation/admin-guide/cgroup-v2.rst.
> +
>   config CGROUP_DEBUG
>   	bool "Debug controller"
>   	default n
> diff --git a/kernel/cgroup/Makefile b/kernel/cgroup/Makefile
> index 5d7a76bfbbb7..6c19208dfb7f 100644
> --- a/kernel/cgroup/Makefile
> +++ b/kernel/cgroup/Makefile
> @@ -5,4 +5,5 @@ obj-$(CONFIG_CGROUP_FREEZER) += legacy_freezer.o
>   obj-$(CONFIG_CGROUP_PIDS) += pids.o
>   obj-$(CONFIG_CGROUP_RDMA) += rdma.o
>   obj-$(CONFIG_CPUSETS) += cpuset.o
> +obj-$(CONFIG_CGROUP_ENCRYPTION_IDS) += encryption_ids.o
>   obj-$(CONFIG_CGROUP_DEBUG) += debug.o
> diff --git a/kernel/cgroup/encryption_ids.c b/kernel/cgroup/encryption_ids.c
> new file mode 100644
> index 000000000000..7cd7d3951bb9
> --- /dev/null
> +++ b/kernel/cgroup/encryption_ids.c
> @@ -0,0 +1,422 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Encryption IDs cgroup controller
> + *
> + * Copyright 2020 Google LLC
> + * Author: Vipin Sharma <vipinsh@...gle.com>
> + */
> +
> +#include <linux/limits.h>
> +#include <linux/cgroup.h>
> +#include <linux/errno.h>
> +#include <linux/spinlock.h>
> +#include <linux/lockdep.h>
> +#include <linux/slab.h>
> +#include <linux/kvm_host.h>
> +#include <linux/encryption_ids_cgroup.h>
> +
> +#define MAX_STR "max"
> +#define MAX_NUM UINT_MAX
> +
> +/* Root Encryption ID cgroup */
> +static struct encryption_id_cgroup root_cg;
> +
> +/* Lock for tracking and updating encryption ID resources. */
> +static DEFINE_SPINLOCK(enc_id_cg_lock);
> +
> +/* Encryption ID types capacity. */
> +static unsigned int enc_id_capacity[ENCRYPTION_ID_TYPES];
> +
> +/**
> + * css_enc() - Get encryption ID cgroup from the css.
> + * @css: cgroup subsys state object.
> + *
> + * Context: Any context.
> + * Return:
> + * * %NULL - If @css is null.
> + * * struct encryption_id_cgroup* - Encryption ID cgroup pointer of the passed
> + *				    css.
> + */
> +static struct encryption_id_cgroup *css_enc(struct cgroup_subsys_state *css)
> +{
> +	return css ? container_of(css, struct encryption_id_cgroup, css) : NULL;
> +}
> +
> +/**
> + * parent_enc() - Get the parent of the passed encryption ID cgroup.
> + * @cgroup: cgroup whose parent needs to be fetched.
> + *
> + * Context: Any context.
> + * Return:
> + * * struct encryption_id_cgroup* - Parent of the @cgroup.
> + * * %NULL - If @cgroup is null or the passed cgroup does not have a parent.
> + */
> +static struct encryption_id_cgroup *
> +parent_enc(struct encryption_id_cgroup *cgroup)
> +{
> +	return cgroup ? css_enc(cgroup->css.parent) : NULL;
> +}
> +
> +/**
> + * valid_type() - Check if @type is valid or not.
> + * @type: encryption ID type.
> + *
> + * Context: Any context.
> + * Return:
> + * * true - If valid type.
> + * * false - If not valid type.
> + */
> +static inline bool valid_type(enum encryption_id_type type)
> +{
> +	return type >= 0 && type < ENCRYPTION_ID_TYPES;
> +}
> +
> +/**
> + * enc_id_cg_uncharge_hierarchy() - Uncharge the enryption ID cgroup hierarchy.
> + * @start_cg: Starting cgroup.
> + * @stop_cg: cgroup at which uncharge stops.
> + * @type: type of encryption ID to uncharge.
> + * @amount: Charge amount.
> + *
> + * Uncharge the cgroup tree from the given start cgroup to the stop cgroup.
> + *
> + * Context: Any context. Expects enc_id_cg_lock to be held by the caller.
> + */
> +static void enc_id_cg_uncharge_hierarchy(struct encryption_id_cgroup *start_cg,
> +					 struct encryption_id_cgroup *stop_cg,
> +					 enum encryption_id_type type,
> +					 unsigned int amount)
> +{
> +	struct encryption_id_cgroup *i;
> +
> +	lockdep_assert_held(&enc_id_cg_lock);
> +
> +	for (i = start_cg; i != stop_cg; i = parent_enc(i)) {
> +		WARN_ON_ONCE(i->res[type].usage < amount);
> +		i->res[type].usage -= amount;
> +	}
> +	css_put(&start_cg->css);
> +}
> +
> +/**
> + * enc_id_cg_set_capacity() - Set the capacity of the encryption ID.
> + * @type: Type of the encryption ID.
> + * @capacity: Supported capacity of the encryption ID on the host.
> + *
> + * If capacity is 0 then the charging a cgroup fails for the encryption ID.
> + *
> + * Context: Any context. Takes and releases enc_id_cg_lock.
> + * Return:
> + * * %0 - Successfully registered the capacity.
> + * * %-EINVAL - If @type is invalid.
> + * * %-EBUSY - If current usage is more than the capacity.
> + */
> +int enc_id_cg_set_capacity(enum encryption_id_type type, unsigned int capacity)
> +{
> +	int ret = 0;
> +	unsigned long flags;
> +
> +	if (!valid_type(type))
> +		return -EINVAL;
> +
> +	spin_lock_irqsave(&enc_id_cg_lock, flags);
> +
> +	if (WARN_ON_ONCE(root_cg.res[type].usage > capacity))
> +		ret = -EBUSY;
> +	else
> +		enc_id_capacity[type] = capacity;
> +
> +	spin_unlock_irqrestore(&enc_id_cg_lock, flags);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(enc_id_cg_set_capacity);
> +
> +/**
> + * enc_id_cg_try_charge() - Try charging encryption ID cgroup.
> + * @kvm: kvm to store charged cgroup.
> + * @type: Encryption ID type to charge.
> + * @amount: Amount to charge.
> + *
> + * Charge @amount to the cgroup to which the current task belongs to. Charged
> + * cgroup will be pointed by @cg. Caller must use the same cgroup during
> + * uncharge call.
> + *
> + * Context: Any context. Takes and releases enc_id_cg_lock.
> + * Return:
> + * * %0 - If successfully charged.
> + * * -EINVAL - If @type is invalid or encryption ID has 0 capacity.
> + * * -EBUSY - If max limit will be crossed or total usage will be more than the
> + *	      capacity.
> + */
> +int enc_id_cg_try_charge(struct kvm *kvm, enum encryption_id_type type,
> +			 unsigned int amount)
> +{
> +	struct encryption_id_cgroup *task_cg, *i;
> +	struct encryption_id_res *id_res;
> +	int ret;
> +	unsigned int new_usage;
> +	unsigned long flags;
> +
> +	if (!valid_type(type) || !kvm)
> +		return -EINVAL;
> +
> +	if (!amount)
> +		return 0;
> +
> +	spin_lock_irqsave(&enc_id_cg_lock, flags);
> +
> +	if (!enc_id_capacity[type]) {
> +		ret = -EINVAL;
> +		goto err_capacity;
> +	}
> +
> +	task_cg = css_enc(task_get_css(current, encryption_ids_cgrp_id));
> +
> +	for (i = task_cg; i; i = parent_enc(i)) {
> +		id_res = &i->res[type];
> +
> +		new_usage = id_res->usage + amount;
> +		WARN_ON_ONCE(new_usage < id_res->usage);
> +
> +		if (new_usage > id_res->max ||
> +		    new_usage > enc_id_capacity[type]) {
> +			ret = -EBUSY;
> +			goto err_charge;
> +		}
> +
> +		id_res->usage = new_usage;
> +	}
> +
> +	kvm->enc_id_cg = task_cg;
> +	spin_unlock_irqrestore(&enc_id_cg_lock, flags);
> +	return 0;
> +
> +err_charge:
> +	enc_id_cg_uncharge_hierarchy(task_cg, i, type, amount);
> +err_capacity:
> +	spin_unlock_irqrestore(&enc_id_cg_lock, flags);
> +	return ret;
> +}
> +EXPORT_SYMBOL(enc_id_cg_try_charge);
> +
> +/**
> + * enc_id_cg_uncharge() - Uncharge the encryption ID cgroup.
> + * @kvm: kvm containing the corresponding encryption ID cgroup.
> + * @type: Encryption ID which was charged.
> + * @amount: Charged amount.
> + *
> + * Context: Any context. Takes and releases enc_id_cg_lock.
> + */
> +void enc_id_cg_uncharge(struct kvm *kvm, enum encryption_id_type type,
> +			unsigned int amount)
> +{
> +	unsigned long flags;
> +
> +	if (!amount)
> +		return;
> +	if (!valid_type(type))
> +		return;
> +	if (!kvm || WARN_ON_ONCE(!(kvm->enc_id_cg)))
> +		return;
> +
> +	spin_lock_irqsave(&enc_id_cg_lock, flags);
> +	enc_id_cg_uncharge_hierarchy(kvm->enc_id_cg, NULL, type, amount);
> +	spin_unlock_irqrestore(&enc_id_cg_lock, flags);
> +
> +	kvm->enc_id_cg = NULL;
> +}
> +EXPORT_SYMBOL(enc_id_cg_uncharge);
> +
> +/**
> + * enc_id_cg_max_show() - Show encryption ID cgroup max limit.
> + * @sf: Interface file
> + * @v: Arguments passed
> + *
> + * Uses cft->private value to determine for which enryption ID type results be
> + * shown.
> + *
> + * Context: Any context.
> + * Return: 0 to denote successful print.
> + */
> +static int enc_id_cg_max_show(struct seq_file *sf, void *v)
> +{
> +	struct encryption_id_cgroup *cg = css_enc(seq_css(sf));
> +	enum encryption_id_type type = seq_cft(sf)->private;
> +
> +	if (cg->res[type].max == MAX_NUM)
> +		seq_printf(sf, "%s\n", MAX_STR);
> +	else
> +		seq_printf(sf, "%u\n", cg->res[type].max);
> +
> +	return 0;
> +}
> +
> +/**
> + * enc_id_cg_max_write() - Update the maximum limit of the cgroup.
> + * @of: Handler for the file.
> + * @buf: Data from the user. It should be either "max", 0, or a positive
> + *	 integer.
> + * @nbytes: Number of bytes of the data.
> + * @off: Offset in the file.
> + *
> + * Uses cft->private value to determine for which enryption ID type results be
> + * shown.
> + *
> + * Context: Any context. Takes and releases enc_id_cg_lock.
> + * Return:
> + * * >= 0 - Number of bytes processed in the input.
> + * * -EINVAL - If buf is not valid.
> + * * -ERANGE - If number is bigger than unsigned int capacity.
> + * * -EBUSY - If usage can become more than max limit.
> + */
> +static ssize_t enc_id_cg_max_write(struct kernfs_open_file *of, char *buf,
> +				   size_t nbytes, loff_t off)
> +{
> +	struct encryption_id_cgroup *cg;
> +	unsigned int max;
> +	int ret = 0;
> +	enum encryption_id_type type;
> +
> +	buf = strstrip(buf);
> +	if (!strcmp(MAX_STR, buf)) {
> +		max = UINT_MAX;
> +	} else {
> +		ret = kstrtouint(buf, 0, &max);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	cg = css_enc(of_css(of));
> +	type = of_cft(of)->private;
> +	cg->res[type].max = max;
> +
> +	return nbytes;
> +}
> +
> +/**
> + * enc_id_cg_current_read() - Show current usage of the encryption ID.
> + * @css: css pointer of the cgroup.
> + * @cft: cft pointer of the cgroup.
> + *
> + * Uses cft->private value to determine for which enryption ID type results be
> + * shown.
> + *
> + * Context: Any context.
> + * Return: 0 to denote successful print.
> + */
> +static u64 enc_id_cg_current_read(struct cgroup_subsys_state *css,
> +				  struct cftype *cft)
> +{
> +	struct encryption_id_cgroup *cg = css_enc(css);
> +	enum encryption_id_type type = cft->private;
> +
> +	return cg->res[type].usage;
> +}
> +
> +/**
> + * enc_id_cg_stat_show() - Show the current stat of the cgroup.
> + * @sf: Interface file
> + * @v: Arguments passed
> + *
> + * Shows the total capacity of the encryption ID and its current usage.
> + * Only shows in root cgroup directory.
> + *
> + * Uses cft->private value to determine for which enryption ID type results be
> + * shown.
> + *
> + * Context: Any context. Takes and releases enc_id_cg_lock.
> + * Return: 0 to denote successful print.
> + */
> +static int enc_id_cg_stat_show(struct seq_file *sf, void *v)
> +{
> +	unsigned long flags;
> +	enum encryption_id_type type = seq_cft(sf)->private;
> +
> +	spin_lock_irqsave(&enc_id_cg_lock, flags);
> +
> +	seq_printf(sf, "total %u\n", enc_id_capacity[type]);
> +	seq_printf(sf, "used %u\n", root_cg.res[type].usage);
> +
> +	spin_unlock_irqrestore(&enc_id_cg_lock, flags);
> +	return 0;
> +}
> +
> +/* Each encryption ID type has these cgroup files. */
> +#define ENC_ID_CGROUP_FILES(id_name, id_type)		\
> +	[(id_type) * 3] = {				\
> +		.name = id_name ".max",			\
> +		.write = enc_id_cg_max_write,		\
> +		.seq_show = enc_id_cg_max_show,		\
> +		.flags = CFTYPE_NOT_ON_ROOT,		\
> +		.private = id_type,			\
> +	},						\
> +	[((id_type) * 3) + 1] = {			\
> +		.name = id_name ".current",		\
> +		.read_u64 = enc_id_cg_current_read,	\
> +		.flags = CFTYPE_NOT_ON_ROOT,		\
> +		.private = id_type,			\
> +	},						\
> +	[((id_type) * 3) + 2] = {			\
> +		.name = id_name ".stat",		\
> +		.seq_show = enc_id_cg_stat_show,	\
> +		.flags = CFTYPE_ONLY_ON_ROOT,		\
> +		.private = id_type,			\
> +	}
> +
> +/* Encryption ID cgroup interface files */
> +static struct cftype enc_id_cg_files[] = {
> +#ifdef CONFIG_KVM_AMD_SEV
> +	ENC_ID_CGROUP_FILES("sev", ENCRYPTION_ID_SEV),
> +	ENC_ID_CGROUP_FILES("sev_es", ENCRYPTION_ID_SEV_ES),
> +#endif
> +	{}
> +};
> +
> +/**
> + * enc_id_cg_alloc() - Allocate encryption ID cgroup.
> + * @parent_css: Parent cgroup.
> + *
> + * Context: Process context.
> + * Return:
> + * * struct cgroup_subsys_state* - css of the allocated cgroup.
> + * * ERR_PTR(-ENOMEM) - No memory available to allocate.
> + */
> +static struct cgroup_subsys_state *
> +enc_id_cg_alloc(struct cgroup_subsys_state *parent_css)
> +{
> +	enum encryption_id_type i;
> +	struct encryption_id_cgroup *cg;
> +
> +	if (!parent_css) {
> +		cg = &root_cg;
> +	} else {
> +		cg = kzalloc(sizeof(*cg), GFP_KERNEL);
> +		if (!cg)
> +			return ERR_PTR(-ENOMEM);
> +	}
> +
> +	for (i = 0; i < ENCRYPTION_ID_TYPES; i++)
> +		cg->res[i].max = MAX_NUM;
> +
> +	return &cg->css;
> +}
> +
> +/**
> + * enc_id_cg_free() - Free the encryption ID cgroup.
> + * @css: cgroup subsys object.
> + *
> + * Context: Any context.
> + */
> +static void enc_id_cg_free(struct cgroup_subsys_state *css)
> +{
> +	kfree(css_enc(css));
> +}
> +
> +/* Cgroup controller callbacks */
> +struct cgroup_subsys encryption_ids_cgrp_subsys = {
> +	.css_alloc = enc_id_cg_alloc,
> +	.css_free = enc_id_cg_free,
> +	.legacy_cftypes = enc_id_cg_files,
> +	.dfl_cftypes = enc_id_cg_files,
> +};

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ