Merge branch 'for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
Pull cgroup changes from Tejun Heo: "The only notable change is Vipin's new misc cgroup controller. This implements generic support for resources which can be controlled by simply counting and limiting the number of resource instances - ie there's X number of these on the system and this cgroup subtree can have upto Y of those. The first user is the address space IDs used for virtual machine memory encryption and expected future usages are similar - niche hardware features with concrete resource limits and simple usage models" * 'for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup: cgroup: use tsk->in_iowait instead of delayacct_is_task_waiting_on_io() cgroup/cpuset: fix typos in comments cgroup: misc: mark dummy misc_cg_res_total_usage() static inline svm/sev: Register SEV and SEV-ES ASIDs to the misc controller cgroup: Miscellaneous cgroup documentation. cgroup: Add misc cgroup controller
This commit is contained in:
commit
55e6be657b
@ -17,6 +17,7 @@ Control Groups version 1
|
||||
hugetlb
|
||||
memcg_test
|
||||
memory
|
||||
misc
|
||||
net_cls
|
||||
net_prio
|
||||
pids
|
||||
|
4
Documentation/admin-guide/cgroup-v1/misc.rst
Normal file
4
Documentation/admin-guide/cgroup-v1/misc.rst
Normal file
@ -0,0 +1,4 @@
|
||||
===============
|
||||
Misc controller
|
||||
===============
|
||||
Please refer "Misc" documentation in Documentation/admin-guide/cgroup-v2.rst
|
@ -65,8 +65,11 @@ v1 is available under :ref:`Documentation/admin-guide/cgroup-v1/index.rst <cgrou
|
||||
5-7-1. RDMA Interface Files
|
||||
5-8. HugeTLB
|
||||
5.8-1. HugeTLB Interface Files
|
||||
5-8. Misc
|
||||
5-8-1. perf_event
|
||||
5-9. Misc
|
||||
5.9-1 Miscellaneous cgroup Interface Files
|
||||
5.9-2 Migration and Ownership
|
||||
5-10. Others
|
||||
5-10-1. perf_event
|
||||
5-N. Non-normative information
|
||||
5-N-1. CPU controller root cgroup process behaviour
|
||||
5-N-2. IO controller root cgroup process behaviour
|
||||
@ -2171,6 +2174,72 @@ HugeTLB Interface Files
|
||||
Misc
|
||||
----
|
||||
|
||||
The Miscellaneous cgroup provides the resource limiting and tracking
|
||||
mechanism for the scalar resources which cannot be abstracted like the other
|
||||
cgroup resources. Controller is enabled by the CONFIG_CGROUP_MISC config
|
||||
option.
|
||||
|
||||
A resource can be added to the controller via enum misc_res_type{} in the
|
||||
include/linux/misc_cgroup.h file and the corresponding name via misc_res_name[]
|
||||
in the kernel/cgroup/misc.c file. Provider of the resource must set its
|
||||
capacity prior to using the resource by calling misc_cg_set_capacity().
|
||||
|
||||
Once a capacity is set then the resource usage can be updated using charge and
|
||||
uncharge APIs. All of the APIs to interact with misc controller are in
|
||||
include/linux/misc_cgroup.h.
|
||||
|
||||
Misc Interface Files
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Miscellaneous controller provides 3 interface files. If two misc resources (res_a and res_b) are registered then:
|
||||
|
||||
misc.capacity
|
||||
A read-only flat-keyed file shown only in the root cgroup. It shows
|
||||
miscellaneous scalar resources available on the platform along with
|
||||
their quantities::
|
||||
|
||||
$ cat misc.capacity
|
||||
res_a 50
|
||||
res_b 10
|
||||
|
||||
misc.current
|
||||
A read-only flat-keyed file shown in the non-root cgroups. It shows
|
||||
the current usage of the resources in the cgroup and its children.::
|
||||
|
||||
$ cat misc.current
|
||||
res_a 3
|
||||
res_b 0
|
||||
|
||||
misc.max
|
||||
A read-write flat-keyed file shown in the non root cgroups. Allowed
|
||||
maximum usage of the resources in the cgroup and its children.::
|
||||
|
||||
$ cat misc.max
|
||||
res_a max
|
||||
res_b 4
|
||||
|
||||
Limit can be set by::
|
||||
|
||||
# echo res_a 1 > misc.max
|
||||
|
||||
Limit can be set to max by::
|
||||
|
||||
# echo res_a max > misc.max
|
||||
|
||||
Limits can be set higher than the capacity value in the misc.capacity
|
||||
file.
|
||||
|
||||
Migration and Ownership
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A miscellaneous scalar resource is charged to the cgroup in which it is used
|
||||
first, and stays charged to that cgroup until that resource is freed. Migrating
|
||||
a process to a different cgroup does not move the charge to the destination
|
||||
cgroup where the process has moved.
|
||||
|
||||
Others
|
||||
------
|
||||
|
||||
perf_event
|
||||
~~~~~~~~~~
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/psp-sev.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/misc_cgroup.h>
|
||||
#include <linux/processor.h>
|
||||
#include <linux/trace_events.h>
|
||||
#include <asm/fpu/internal.h>
|
||||
@ -28,6 +29,21 @@
|
||||
|
||||
#define __ex(x) __kvm_handle_fault_on_reboot(x)
|
||||
|
||||
#ifndef CONFIG_KVM_AMD_SEV
|
||||
/*
|
||||
* When this config is not defined, SEV feature is not supported and APIs in
|
||||
* this file are not used but this file still gets compiled into the KVM AMD
|
||||
* module.
|
||||
*
|
||||
* We will not have MISC_CG_RES_SEV and MISC_CG_RES_SEV_ES entries in the enum
|
||||
* misc_res_type {} defined in linux/misc_cgroup.h.
|
||||
*
|
||||
* Below macros allow compilation to succeed.
|
||||
*/
|
||||
#define MISC_CG_RES_SEV MISC_CG_RES_TYPES
|
||||
#define MISC_CG_RES_SEV_ES MISC_CG_RES_TYPES
|
||||
#endif
|
||||
|
||||
static u8 sev_enc_bit;
|
||||
static int sev_flush_asids(void);
|
||||
static DECLARE_RWSEM(sev_deactivate_lock);
|
||||
@ -89,8 +105,19 @@ static bool __sev_recycle_asids(int min_asid, int max_asid)
|
||||
|
||||
static int sev_asid_new(struct kvm_sev_info *sev)
|
||||
{
|
||||
int pos, min_asid, max_asid;
|
||||
int pos, min_asid, max_asid, ret;
|
||||
bool retry = true;
|
||||
enum misc_res_type type;
|
||||
|
||||
type = sev->es_active ? MISC_CG_RES_SEV_ES : MISC_CG_RES_SEV;
|
||||
WARN_ON(sev->misc_cg);
|
||||
sev->misc_cg = get_current_misc_cg();
|
||||
ret = misc_cg_try_charge(type, sev->misc_cg, 1);
|
||||
if (ret) {
|
||||
put_misc_cg(sev->misc_cg);
|
||||
sev->misc_cg = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&sev_bitmap_lock);
|
||||
|
||||
@ -108,7 +135,8 @@ again:
|
||||
goto again;
|
||||
}
|
||||
mutex_unlock(&sev_bitmap_lock);
|
||||
return -EBUSY;
|
||||
ret = -EBUSY;
|
||||
goto e_uncharge;
|
||||
}
|
||||
|
||||
__set_bit(pos, sev_asid_bitmap);
|
||||
@ -116,6 +144,11 @@ again:
|
||||
mutex_unlock(&sev_bitmap_lock);
|
||||
|
||||
return pos + 1;
|
||||
e_uncharge:
|
||||
misc_cg_uncharge(type, sev->misc_cg, 1);
|
||||
put_misc_cg(sev->misc_cg);
|
||||
sev->misc_cg = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sev_get_asid(struct kvm *kvm)
|
||||
@ -125,14 +158,15 @@ 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_sev_info *sev)
|
||||
{
|
||||
struct svm_cpu_data *sd;
|
||||
int cpu, pos;
|
||||
enum misc_res_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) {
|
||||
@ -141,6 +175,11 @@ static void sev_asid_free(int asid)
|
||||
}
|
||||
|
||||
mutex_unlock(&sev_bitmap_lock);
|
||||
|
||||
type = sev->es_active ? MISC_CG_RES_SEV_ES : MISC_CG_RES_SEV;
|
||||
misc_cg_uncharge(type, sev->misc_cg, 1);
|
||||
put_misc_cg(sev->misc_cg);
|
||||
sev->misc_cg = NULL;
|
||||
}
|
||||
|
||||
static void sev_unbind_asid(struct kvm *kvm, unsigned int handle)
|
||||
@ -188,19 +227,20 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
|
||||
asid = sev_asid_new(sev);
|
||||
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(sev);
|
||||
sev->asid = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1315,12 +1355,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(sev);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -1352,7 +1392,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 (misc_cg_set_capacity(MISC_CG_RES_SEV, sev_asid_count))
|
||||
goto out;
|
||||
|
||||
pr_info("SEV supported: %u ASIDs\n", sev_asid_count);
|
||||
sev_supported = true;
|
||||
|
||||
/* SEV-ES support requested? */
|
||||
@ -1367,7 +1411,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 (misc_cg_set_capacity(MISC_CG_RES_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:
|
||||
@ -1382,6 +1430,8 @@ void sev_hardware_teardown(void)
|
||||
|
||||
bitmap_free(sev_asid_bitmap);
|
||||
bitmap_free(sev_reclaim_asid_bitmap);
|
||||
misc_cg_set_capacity(MISC_CG_RES_SEV, 0);
|
||||
misc_cg_set_capacity(MISC_CG_RES_SEV_ES, 0);
|
||||
|
||||
sev_flush_asids();
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ struct kvm_sev_info {
|
||||
unsigned long pages_locked; /* Number of pages locked */
|
||||
struct list_head regions_list; /* List of registered regions */
|
||||
u64 ap_jump_table; /* SEV-ES AP Jump Table address */
|
||||
struct misc_cg *misc_cg; /* For misc cgroup accounting */
|
||||
};
|
||||
|
||||
struct kvm_svm {
|
||||
|
@ -61,6 +61,10 @@ SUBSYS(pids)
|
||||
SUBSYS(rdma)
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_CGROUP_MISC)
|
||||
SUBSYS(misc)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The following subsystems are not supported on the default hierarchy.
|
||||
*/
|
||||
|
132
include/linux/misc_cgroup.h
Normal file
132
include/linux/misc_cgroup.h
Normal file
@ -0,0 +1,132 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Miscellaneous cgroup controller.
|
||||
*
|
||||
* Copyright 2020 Google LLC
|
||||
* Author: Vipin Sharma <vipinsh@google.com>
|
||||
*/
|
||||
#ifndef _MISC_CGROUP_H_
|
||||
#define _MISC_CGROUP_H_
|
||||
|
||||
/**
|
||||
* Types of misc cgroup entries supported by the host.
|
||||
*/
|
||||
enum misc_res_type {
|
||||
#ifdef CONFIG_KVM_AMD_SEV
|
||||
/* AMD SEV ASIDs resource */
|
||||
MISC_CG_RES_SEV,
|
||||
/* AMD SEV-ES ASIDs resource */
|
||||
MISC_CG_RES_SEV_ES,
|
||||
#endif
|
||||
MISC_CG_RES_TYPES
|
||||
};
|
||||
|
||||
struct misc_cg;
|
||||
|
||||
#ifdef CONFIG_CGROUP_MISC
|
||||
|
||||
#include <linux/cgroup.h>
|
||||
|
||||
/**
|
||||
* struct misc_res: Per cgroup per misc type resource
|
||||
* @max: Maximum limit on the resource.
|
||||
* @usage: Current usage of the resource.
|
||||
* @failed: True if charged failed for the resource in a cgroup.
|
||||
*/
|
||||
struct misc_res {
|
||||
unsigned long max;
|
||||
atomic_long_t usage;
|
||||
bool failed;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct misc_cg - Miscellaneous controller's cgroup structure.
|
||||
* @css: cgroup subsys state object.
|
||||
* @res: Array of misc resources usage in the cgroup.
|
||||
*/
|
||||
struct misc_cg {
|
||||
struct cgroup_subsys_state css;
|
||||
struct misc_res res[MISC_CG_RES_TYPES];
|
||||
};
|
||||
|
||||
unsigned long misc_cg_res_total_usage(enum misc_res_type type);
|
||||
int misc_cg_set_capacity(enum misc_res_type type, unsigned long capacity);
|
||||
int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg,
|
||||
unsigned long amount);
|
||||
void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg,
|
||||
unsigned long amount);
|
||||
|
||||
/**
|
||||
* css_misc() - Get misc cgroup from the css.
|
||||
* @css: cgroup subsys state object.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return:
|
||||
* * %NULL - If @css is null.
|
||||
* * struct misc_cg* - misc cgroup pointer of the passed css.
|
||||
*/
|
||||
static inline struct misc_cg *css_misc(struct cgroup_subsys_state *css)
|
||||
{
|
||||
return css ? container_of(css, struct misc_cg, css) : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_current_misc_cg() - Find and get the misc cgroup of the current task.
|
||||
*
|
||||
* Returned cgroup has its ref count increased by 1. Caller must call
|
||||
* put_misc_cg() to return the reference.
|
||||
*
|
||||
* Return: Misc cgroup to which the current task belongs to.
|
||||
*/
|
||||
static inline struct misc_cg *get_current_misc_cg(void)
|
||||
{
|
||||
return css_misc(task_get_css(current, misc_cgrp_id));
|
||||
}
|
||||
|
||||
/*
|
||||
* put_misc_cg() - Put the misc cgroup and reduce its ref count.
|
||||
* @cg - cgroup to put.
|
||||
*/
|
||||
static inline void put_misc_cg(struct misc_cg *cg)
|
||||
{
|
||||
if (cg)
|
||||
css_put(&cg->css);
|
||||
}
|
||||
|
||||
#else /* !CONFIG_CGROUP_MISC */
|
||||
|
||||
static inline unsigned long misc_cg_res_total_usage(enum misc_res_type type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int misc_cg_set_capacity(enum misc_res_type type,
|
||||
unsigned long capacity)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int misc_cg_try_charge(enum misc_res_type type,
|
||||
struct misc_cg *cg,
|
||||
unsigned long amount)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void misc_cg_uncharge(enum misc_res_type type,
|
||||
struct misc_cg *cg,
|
||||
unsigned long amount)
|
||||
{
|
||||
}
|
||||
|
||||
static inline struct misc_cg *get_current_misc_cg(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void put_misc_cg(struct misc_cg *cg)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_CGROUP_MISC */
|
||||
#endif /* _MISC_CGROUP_H_ */
|
14
init/Kconfig
14
init/Kconfig
@ -1110,6 +1110,20 @@ config CGROUP_BPF
|
||||
BPF_CGROUP_INET_INGRESS will be executed on the ingress path of
|
||||
inet sockets.
|
||||
|
||||
config CGROUP_MISC
|
||||
bool "Misc resource controller"
|
||||
default n
|
||||
help
|
||||
Provides a controller for miscellaneous resources on a host.
|
||||
|
||||
Miscellaneous scalar resources are the resources on the host system
|
||||
which cannot be abstracted like the other cgroups. This controller
|
||||
tracks and limits the miscellaneous resources used by a process
|
||||
attached to a cgroup hierarchy.
|
||||
|
||||
For more information, please check misc cgroup section in
|
||||
/Documentation/admin-guide/cgroup-v2.rst.
|
||||
|
||||
config CGROUP_DEBUG
|
||||
bool "Debug controller"
|
||||
default n
|
||||
|
@ -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_MISC) += misc.o
|
||||
obj-$(CONFIG_CGROUP_DEBUG) += debug.o
|
||||
|
@ -727,7 +727,7 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry)
|
||||
stats->nr_stopped++;
|
||||
break;
|
||||
default:
|
||||
if (delayacct_is_task_waiting_on_io(tsk))
|
||||
if (tsk->in_iowait)
|
||||
stats->nr_io_wait++;
|
||||
break;
|
||||
}
|
||||
|
@ -585,7 +585,7 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial)
|
||||
|
||||
par = parent_cs(cur);
|
||||
|
||||
/* On legacy hiearchy, we must be a subset of our parent cpuset. */
|
||||
/* On legacy hierarchy, we must be a subset of our parent cpuset. */
|
||||
ret = -EACCES;
|
||||
if (!is_in_v2_mode() && !is_cpuset_subset(trial, par))
|
||||
goto out;
|
||||
@ -1726,7 +1726,7 @@ static void update_tasks_nodemask(struct cpuset *cs)
|
||||
* When configured nodemask is changed, the effective nodemasks of this cpuset
|
||||
* and all its descendants need to be updated.
|
||||
*
|
||||
* On legacy hiearchy, effective_mems will be the same with mems_allowed.
|
||||
* On legacy hierarchy, effective_mems will be the same with mems_allowed.
|
||||
*
|
||||
* Called with cpuset_mutex held
|
||||
*/
|
||||
@ -2500,7 +2500,7 @@ static s64 cpuset_read_s64(struct cgroup_subsys_state *css, struct cftype *cft)
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Unrechable but makes gcc happy */
|
||||
/* Unreachable but makes gcc happy */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
407
kernel/cgroup/misc.c
Normal file
407
kernel/cgroup/misc.c
Normal file
@ -0,0 +1,407 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Miscellaneous cgroup controller
|
||||
*
|
||||
* Copyright 2020 Google LLC
|
||||
* Author: Vipin Sharma <vipinsh@google.com>
|
||||
*/
|
||||
|
||||
#include <linux/limits.h>
|
||||
#include <linux/cgroup.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/misc_cgroup.h>
|
||||
|
||||
#define MAX_STR "max"
|
||||
#define MAX_NUM ULONG_MAX
|
||||
|
||||
/* Miscellaneous res name, keep it in sync with enum misc_res_type */
|
||||
static const char *const misc_res_name[] = {
|
||||
#ifdef CONFIG_KVM_AMD_SEV
|
||||
/* AMD SEV ASIDs resource */
|
||||
"sev",
|
||||
/* AMD SEV-ES ASIDs resource */
|
||||
"sev_es",
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Root misc cgroup */
|
||||
static struct misc_cg root_cg;
|
||||
|
||||
/*
|
||||
* Miscellaneous resources capacity for the entire machine. 0 capacity means
|
||||
* resource is not initialized or not present in the host.
|
||||
*
|
||||
* root_cg.max and capacity are independent of each other. root_cg.max can be
|
||||
* more than the actual capacity. We are using Limits resource distribution
|
||||
* model of cgroup for miscellaneous controller.
|
||||
*/
|
||||
static unsigned long misc_res_capacity[MISC_CG_RES_TYPES];
|
||||
|
||||
/**
|
||||
* parent_misc() - Get the parent of the passed misc cgroup.
|
||||
* @cgroup: cgroup whose parent needs to be fetched.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return:
|
||||
* * struct misc_cg* - Parent of the @cgroup.
|
||||
* * %NULL - If @cgroup is null or the passed cgroup does not have a parent.
|
||||
*/
|
||||
static struct misc_cg *parent_misc(struct misc_cg *cgroup)
|
||||
{
|
||||
return cgroup ? css_misc(cgroup->css.parent) : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* valid_type() - Check if @type is valid or not.
|
||||
* @type: misc res type.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return:
|
||||
* * true - If valid type.
|
||||
* * false - If not valid type.
|
||||
*/
|
||||
static inline bool valid_type(enum misc_res_type type)
|
||||
{
|
||||
return type >= 0 && type < MISC_CG_RES_TYPES;
|
||||
}
|
||||
|
||||
/**
|
||||
* misc_cg_res_total_usage() - Get the current total usage of the resource.
|
||||
* @type: misc res type.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: Current total usage of the resource.
|
||||
*/
|
||||
unsigned long misc_cg_res_total_usage(enum misc_res_type type)
|
||||
{
|
||||
if (valid_type(type))
|
||||
return atomic_long_read(&root_cg.res[type].usage);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(misc_cg_res_total_usage);
|
||||
|
||||
/**
|
||||
* misc_cg_set_capacity() - Set the capacity of the misc cgroup res.
|
||||
* @type: Type of the misc res.
|
||||
* @capacity: Supported capacity of the misc res on the host.
|
||||
*
|
||||
* If capacity is 0 then the charging a misc cgroup fails for that type.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return:
|
||||
* * %0 - Successfully registered the capacity.
|
||||
* * %-EINVAL - If @type is invalid.
|
||||
*/
|
||||
int misc_cg_set_capacity(enum misc_res_type type, unsigned long capacity)
|
||||
{
|
||||
if (!valid_type(type))
|
||||
return -EINVAL;
|
||||
|
||||
WRITE_ONCE(misc_res_capacity[type], capacity);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(misc_cg_set_capacity);
|
||||
|
||||
/**
|
||||
* misc_cg_cancel_charge() - Cancel the charge from the misc cgroup.
|
||||
* @type: Misc res type in misc cg to cancel the charge from.
|
||||
* @cg: Misc cgroup to cancel charge from.
|
||||
* @amount: Amount to cancel.
|
||||
*
|
||||
* Context: Any context.
|
||||
*/
|
||||
static void misc_cg_cancel_charge(enum misc_res_type type, struct misc_cg *cg,
|
||||
unsigned long amount)
|
||||
{
|
||||
WARN_ONCE(atomic_long_add_negative(-amount, &cg->res[type].usage),
|
||||
"misc cgroup resource %s became less than 0",
|
||||
misc_res_name[type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* misc_cg_try_charge() - Try charging the misc cgroup.
|
||||
* @type: Misc res type to charge.
|
||||
* @cg: Misc cgroup which will be charged.
|
||||
* @amount: Amount to charge.
|
||||
*
|
||||
* Charge @amount to the misc cgroup. Caller must use the same cgroup during
|
||||
* the uncharge call.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return:
|
||||
* * %0 - If successfully charged.
|
||||
* * -EINVAL - If @type is invalid or misc res has 0 capacity.
|
||||
* * -EBUSY - If max limit will be crossed or total usage will be more than the
|
||||
* capacity.
|
||||
*/
|
||||
int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg,
|
||||
unsigned long amount)
|
||||
{
|
||||
struct misc_cg *i, *j;
|
||||
int ret;
|
||||
struct misc_res *res;
|
||||
int new_usage;
|
||||
|
||||
if (!(valid_type(type) && cg && READ_ONCE(misc_res_capacity[type])))
|
||||
return -EINVAL;
|
||||
|
||||
if (!amount)
|
||||
return 0;
|
||||
|
||||
for (i = cg; i; i = parent_misc(i)) {
|
||||
res = &i->res[type];
|
||||
|
||||
new_usage = atomic_long_add_return(amount, &res->usage);
|
||||
if (new_usage > READ_ONCE(res->max) ||
|
||||
new_usage > READ_ONCE(misc_res_capacity[type])) {
|
||||
if (!res->failed) {
|
||||
pr_info("cgroup: charge rejected by the misc controller for %s resource in ",
|
||||
misc_res_name[type]);
|
||||
pr_cont_cgroup_path(i->css.cgroup);
|
||||
pr_cont("\n");
|
||||
res->failed = true;
|
||||
}
|
||||
ret = -EBUSY;
|
||||
goto err_charge;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_charge:
|
||||
for (j = cg; j != i; j = parent_misc(j))
|
||||
misc_cg_cancel_charge(type, j, amount);
|
||||
misc_cg_cancel_charge(type, i, amount);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(misc_cg_try_charge);
|
||||
|
||||
/**
|
||||
* misc_cg_uncharge() - Uncharge the misc cgroup.
|
||||
* @type: Misc res type which was charged.
|
||||
* @cg: Misc cgroup which will be uncharged.
|
||||
* @amount: Charged amount.
|
||||
*
|
||||
* Context: Any context.
|
||||
*/
|
||||
void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg,
|
||||
unsigned long amount)
|
||||
{
|
||||
struct misc_cg *i;
|
||||
|
||||
if (!(amount && valid_type(type) && cg))
|
||||
return;
|
||||
|
||||
for (i = cg; i; i = parent_misc(i))
|
||||
misc_cg_cancel_charge(type, i, amount);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(misc_cg_uncharge);
|
||||
|
||||
/**
|
||||
* misc_cg_max_show() - Show the misc cgroup max limit.
|
||||
* @sf: Interface file
|
||||
* @v: Arguments passed
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: 0 to denote successful print.
|
||||
*/
|
||||
static int misc_cg_max_show(struct seq_file *sf, void *v)
|
||||
{
|
||||
int i;
|
||||
struct misc_cg *cg = css_misc(seq_css(sf));
|
||||
unsigned long max;
|
||||
|
||||
for (i = 0; i < MISC_CG_RES_TYPES; i++) {
|
||||
if (READ_ONCE(misc_res_capacity[i])) {
|
||||
max = READ_ONCE(cg->res[i].max);
|
||||
if (max == MAX_NUM)
|
||||
seq_printf(sf, "%s max\n", misc_res_name[i]);
|
||||
else
|
||||
seq_printf(sf, "%s %lu\n", misc_res_name[i],
|
||||
max);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* misc_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.
|
||||
*
|
||||
* User can pass data like:
|
||||
* echo sev 23 > misc.max, OR
|
||||
* echo sev max > misc.max
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return:
|
||||
* * >= 0 - Number of bytes processed in the input.
|
||||
* * -EINVAL - If buf is not valid.
|
||||
* * -ERANGE - If number is bigger than the unsigned long capacity.
|
||||
*/
|
||||
static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf,
|
||||
size_t nbytes, loff_t off)
|
||||
{
|
||||
struct misc_cg *cg;
|
||||
unsigned long max;
|
||||
int ret = 0, i;
|
||||
enum misc_res_type type = MISC_CG_RES_TYPES;
|
||||
char *token;
|
||||
|
||||
buf = strstrip(buf);
|
||||
token = strsep(&buf, " ");
|
||||
|
||||
if (!token || !buf)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < MISC_CG_RES_TYPES; i++) {
|
||||
if (!strcmp(misc_res_name[i], token)) {
|
||||
type = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == MISC_CG_RES_TYPES)
|
||||
return -EINVAL;
|
||||
|
||||
if (!strcmp(MAX_STR, buf)) {
|
||||
max = MAX_NUM;
|
||||
} else {
|
||||
ret = kstrtoul(buf, 0, &max);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
cg = css_misc(of_css(of));
|
||||
|
||||
if (READ_ONCE(misc_res_capacity[type]))
|
||||
WRITE_ONCE(cg->res[type].max, max);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
return ret ? ret : nbytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* misc_cg_current_show() - Show the current usage of the misc cgroup.
|
||||
* @sf: Interface file
|
||||
* @v: Arguments passed
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: 0 to denote successful print.
|
||||
*/
|
||||
static int misc_cg_current_show(struct seq_file *sf, void *v)
|
||||
{
|
||||
int i;
|
||||
unsigned long usage;
|
||||
struct misc_cg *cg = css_misc(seq_css(sf));
|
||||
|
||||
for (i = 0; i < MISC_CG_RES_TYPES; i++) {
|
||||
usage = atomic_long_read(&cg->res[i].usage);
|
||||
if (READ_ONCE(misc_res_capacity[i]) || usage)
|
||||
seq_printf(sf, "%s %lu\n", misc_res_name[i], usage);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* misc_cg_capacity_show() - Show the total capacity of misc res on the host.
|
||||
* @sf: Interface file
|
||||
* @v: Arguments passed
|
||||
*
|
||||
* Only present in the root cgroup directory.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: 0 to denote successful print.
|
||||
*/
|
||||
static int misc_cg_capacity_show(struct seq_file *sf, void *v)
|
||||
{
|
||||
int i;
|
||||
unsigned long cap;
|
||||
|
||||
for (i = 0; i < MISC_CG_RES_TYPES; i++) {
|
||||
cap = READ_ONCE(misc_res_capacity[i]);
|
||||
if (cap)
|
||||
seq_printf(sf, "%s %lu\n", misc_res_name[i], cap);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Misc cgroup interface files */
|
||||
static struct cftype misc_cg_files[] = {
|
||||
{
|
||||
.name = "max",
|
||||
.write = misc_cg_max_write,
|
||||
.seq_show = misc_cg_max_show,
|
||||
.flags = CFTYPE_NOT_ON_ROOT,
|
||||
},
|
||||
{
|
||||
.name = "current",
|
||||
.seq_show = misc_cg_current_show,
|
||||
.flags = CFTYPE_NOT_ON_ROOT,
|
||||
},
|
||||
{
|
||||
.name = "capacity",
|
||||
.seq_show = misc_cg_capacity_show,
|
||||
.flags = CFTYPE_ONLY_ON_ROOT,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* misc_cg_alloc() - Allocate misc 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 *
|
||||
misc_cg_alloc(struct cgroup_subsys_state *parent_css)
|
||||
{
|
||||
enum misc_res_type i;
|
||||
struct misc_cg *cg;
|
||||
|
||||
if (!parent_css) {
|
||||
cg = &root_cg;
|
||||
} else {
|
||||
cg = kzalloc(sizeof(*cg), GFP_KERNEL);
|
||||
if (!cg)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
for (i = 0; i < MISC_CG_RES_TYPES; i++) {
|
||||
WRITE_ONCE(cg->res[i].max, MAX_NUM);
|
||||
atomic_long_set(&cg->res[i].usage, 0);
|
||||
}
|
||||
|
||||
return &cg->css;
|
||||
}
|
||||
|
||||
/**
|
||||
* misc_cg_free() - Free the misc cgroup.
|
||||
* @css: cgroup subsys object.
|
||||
*
|
||||
* Context: Any context.
|
||||
*/
|
||||
static void misc_cg_free(struct cgroup_subsys_state *css)
|
||||
{
|
||||
kfree(css_misc(css));
|
||||
}
|
||||
|
||||
/* Cgroup controller callbacks */
|
||||
struct cgroup_subsys misc_cgrp_subsys = {
|
||||
.css_alloc = misc_cg_alloc,
|
||||
.css_free = misc_cg_free,
|
||||
.legacy_cftypes = misc_cg_files,
|
||||
.dfl_cftypes = misc_cg_files,
|
||||
};
|
Loading…
Reference in New Issue
Block a user