mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git
synced 2026-10-28 12:53:14 +01:00
There are 2 possible scenarios where the cgroup_taskset structure passed into the cgroup can_attach() and attach() methods can contain task migration data with multiple source cpusets. - A multithread application with threads in different cpusets is fully migrated into a new cpuset. - Disabling v2 cpuset controller will move all the tasks in child cpusets to the parent cpuset. The current cpuset_can_attach() and cpuset_attach() functions still expect task migration is from one source cpuset to one destination cpuset. Fix that by tracking the set of source (old) cpusets in singly linked lists. The list will be iterated when necessary to properly update internal data. To ensure proper DL tasks accounting, the nr_migrate_dl_tasks in both the source and destination cpusets are decremented/incremented with their values added to nr_deadline_tasks when the migration is successful. The setting of the global attach_ctx.cpus_updated and attach_ctx.mems_updated flags are also moved from cpuset_attach() to cpuset_can_attach() as the correct source cpuset can no longer be determined in cpuset_attach() and cpuset states will not be changed between cpuset_attach() and cpuset_can_attach() with an earlier patch. Signed-off-by: Waiman Long <[email protected]> Reviewed-by: Ridong Chen <[email protected]> Signed-off-by: Tejun Heo <[email protected]>
351 lines
10 KiB
C
351 lines
10 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#ifndef __CPUSET_INTERNAL_H
|
|
#define __CPUSET_INTERNAL_H
|
|
|
|
#include <linux/cgroup.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/cpumask.h>
|
|
#include <linux/cpuset.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/union_find.h>
|
|
#include <linux/sched/isolation.h>
|
|
|
|
/* See "Frequency meter" comments, below. */
|
|
|
|
struct fmeter {
|
|
int cnt; /* unprocessed events count */
|
|
int val; /* most recent output value */
|
|
time64_t time; /* clock (secs) when val computed */
|
|
spinlock_t lock; /* guards read or write of above */
|
|
};
|
|
|
|
/*
|
|
* Invalid partition error code
|
|
*/
|
|
enum prs_errcode {
|
|
PERR_NONE = 0,
|
|
PERR_INVCPUS,
|
|
PERR_INVPARENT,
|
|
PERR_NOTPART,
|
|
PERR_NOTEXCL,
|
|
PERR_NOCPUS,
|
|
PERR_HOTPLUG,
|
|
PERR_CPUSEMPTY,
|
|
PERR_HKEEPING,
|
|
PERR_ACCESS,
|
|
PERR_REMOTE,
|
|
};
|
|
|
|
/* bits in struct cpuset flags field */
|
|
typedef enum {
|
|
CS_CPU_EXCLUSIVE,
|
|
CS_MEM_EXCLUSIVE,
|
|
CS_MEM_HARDWALL,
|
|
CS_MEMORY_MIGRATE,
|
|
CS_SCHED_LOAD_BALANCE,
|
|
CS_SPREAD_PAGE,
|
|
CS_SPREAD_SLAB,
|
|
} cpuset_flagbits_t;
|
|
|
|
/* The various types of files and directories in a cpuset file system */
|
|
|
|
typedef enum {
|
|
FILE_MEMORY_MIGRATE,
|
|
FILE_CPULIST,
|
|
FILE_MEMLIST,
|
|
FILE_EFFECTIVE_CPULIST,
|
|
FILE_EFFECTIVE_MEMLIST,
|
|
FILE_SUBPARTS_CPULIST,
|
|
FILE_EXCLUSIVE_CPULIST,
|
|
FILE_EFFECTIVE_XCPULIST,
|
|
FILE_ISOLATED_CPULIST,
|
|
FILE_CPU_EXCLUSIVE,
|
|
FILE_MEM_EXCLUSIVE,
|
|
FILE_MEM_HARDWALL,
|
|
FILE_SCHED_LOAD_BALANCE,
|
|
FILE_PARTITION_ROOT,
|
|
FILE_SCHED_RELAX_DOMAIN_LEVEL,
|
|
FILE_MEMORY_PRESSURE_ENABLED,
|
|
FILE_MEMORY_PRESSURE,
|
|
FILE_SPREAD_PAGE,
|
|
FILE_SPREAD_SLAB,
|
|
} cpuset_filetype_t;
|
|
|
|
struct cpuset {
|
|
struct cgroup_subsys_state css;
|
|
|
|
unsigned long flags; /* "unsigned long" so bitops work */
|
|
|
|
/*
|
|
* On default hierarchy:
|
|
*
|
|
* The user-configured masks can only be changed by writing to
|
|
* cpuset.cpus and cpuset.mems, and won't be limited by the
|
|
* parent masks.
|
|
*
|
|
* The effective masks is the real masks that apply to the tasks
|
|
* in the cpuset. They may be changed if the configured masks are
|
|
* changed or hotplug happens.
|
|
*
|
|
* effective_mask == configured_mask & parent's effective_mask,
|
|
* and if it ends up empty, it will inherit the parent's mask.
|
|
*
|
|
*
|
|
* On legacy hierarchy:
|
|
*
|
|
* The user-configured masks are always the same with effective masks.
|
|
*/
|
|
|
|
/* user-configured CPUs and Memory Nodes allow to tasks */
|
|
cpumask_var_t cpus_allowed;
|
|
nodemask_t mems_allowed;
|
|
|
|
/* effective CPUs and Memory Nodes allow to tasks */
|
|
cpumask_var_t effective_cpus;
|
|
nodemask_t effective_mems;
|
|
|
|
/*
|
|
* Exclusive CPUs dedicated to current cgroup (default hierarchy only)
|
|
*
|
|
* The effective_cpus of a valid partition root comes solely from its
|
|
* effective_xcpus and some of the effective_xcpus may be distributed
|
|
* to sub-partitions below & hence excluded from its effective_cpus.
|
|
* For a valid partition root, its effective_cpus have no relationship
|
|
* with cpus_allowed unless its exclusive_cpus isn't set.
|
|
*
|
|
* This value will only be set if either exclusive_cpus is set or
|
|
* when this cpuset becomes a local partition root.
|
|
*/
|
|
cpumask_var_t effective_xcpus;
|
|
|
|
/*
|
|
* Exclusive CPUs as requested by the user (default hierarchy only)
|
|
*
|
|
* Its value is independent of cpus_allowed and designates the set of
|
|
* CPUs that can be granted to the current cpuset or its children when
|
|
* it becomes a valid partition root. The effective set of exclusive
|
|
* CPUs granted (effective_xcpus) depends on whether those exclusive
|
|
* CPUs are passed down by its ancestors and not yet taken up by
|
|
* another sibling partition root along the way.
|
|
*
|
|
* If its value isn't set, it defaults to cpus_allowed.
|
|
*/
|
|
cpumask_var_t exclusive_cpus;
|
|
|
|
/*
|
|
* This is old Memory Nodes tasks took on.
|
|
*
|
|
* - top_cpuset.old_mems_allowed is initialized to mems_allowed.
|
|
* - A new cpuset's old_mems_allowed is initialized when some
|
|
* task is moved into it.
|
|
* - old_mems_allowed is used in cpuset_migrate_mm() when we change
|
|
* cpuset.mems_allowed and have tasks' nodemask updated, and
|
|
* then old_mems_allowed is updated to mems_allowed.
|
|
*/
|
|
nodemask_t old_mems_allowed;
|
|
|
|
/*
|
|
* For linking impacted cpusets during an attach operation.
|
|
*/
|
|
struct llist_node attach_node;
|
|
|
|
/* partition root state */
|
|
int partition_root_state;
|
|
|
|
/*
|
|
* Whether cpuset is a remote partition.
|
|
* It used to be a list anchoring all remote partitions — we can switch back
|
|
* to a list if we need to iterate over the remote partitions.
|
|
*/
|
|
bool remote_partition;
|
|
|
|
/*
|
|
* number of SCHED_DEADLINE tasks attached to this cpuset, so that we
|
|
* know when to rebuild associated root domain bandwidth information.
|
|
*/
|
|
atomic_t nr_deadline_tasks;
|
|
int nr_migrate_dl_tasks;
|
|
/* DL bandwidth that needs destination reservation for this attach. */
|
|
u64 sum_migrate_dl_bw;
|
|
/*
|
|
* CPU used for temporary DL bandwidth allocation during attach;
|
|
* -1 if no DL bandwidth was allocated in the current attach.
|
|
*/
|
|
int dl_bw_cpu;
|
|
|
|
/* Invalid partition error code, not lock protected */
|
|
enum prs_errcode prs_err;
|
|
|
|
/* Handle for cpuset.cpus.partition */
|
|
struct cgroup_file partition_file;
|
|
|
|
#ifdef CONFIG_CPUSETS_V1
|
|
struct fmeter fmeter; /* memory_pressure filter */
|
|
|
|
/* for custom sched domain */
|
|
int relax_domain_level;
|
|
|
|
/* Used to merge intersecting subsets for generate_sched_domains */
|
|
struct uf_node node;
|
|
#endif
|
|
};
|
|
|
|
extern struct cpuset top_cpuset;
|
|
|
|
static inline struct cpuset *css_cs(struct cgroup_subsys_state *css)
|
|
{
|
|
return css ? container_of(css, struct cpuset, css) : NULL;
|
|
}
|
|
|
|
/* Retrieve the cpuset for a task */
|
|
static inline struct cpuset *task_cs(struct task_struct *task)
|
|
{
|
|
return css_cs(task_css(task, cpuset_cgrp_id));
|
|
}
|
|
|
|
static inline struct cpuset *parent_cs(struct cpuset *cs)
|
|
{
|
|
return css_cs(cs->css.parent);
|
|
}
|
|
|
|
/* convenient tests for these bits */
|
|
static inline bool is_cpuset_online(struct cpuset *cs)
|
|
{
|
|
return css_is_online(&cs->css) && !css_is_dying(&cs->css);
|
|
}
|
|
|
|
static inline int is_cpu_exclusive(const struct cpuset *cs)
|
|
{
|
|
return test_bit(CS_CPU_EXCLUSIVE, &cs->flags);
|
|
}
|
|
|
|
static inline int is_mem_exclusive(const struct cpuset *cs)
|
|
{
|
|
return test_bit(CS_MEM_EXCLUSIVE, &cs->flags);
|
|
}
|
|
|
|
static inline int is_mem_hardwall(const struct cpuset *cs)
|
|
{
|
|
return test_bit(CS_MEM_HARDWALL, &cs->flags);
|
|
}
|
|
|
|
static inline int is_sched_load_balance(const struct cpuset *cs)
|
|
{
|
|
return test_bit(CS_SCHED_LOAD_BALANCE, &cs->flags);
|
|
}
|
|
|
|
static inline int is_memory_migrate(const struct cpuset *cs)
|
|
{
|
|
return test_bit(CS_MEMORY_MIGRATE, &cs->flags);
|
|
}
|
|
|
|
static inline int is_spread_page(const struct cpuset *cs)
|
|
{
|
|
return test_bit(CS_SPREAD_PAGE, &cs->flags);
|
|
}
|
|
|
|
static inline int is_spread_slab(const struct cpuset *cs)
|
|
{
|
|
return test_bit(CS_SPREAD_SLAB, &cs->flags);
|
|
}
|
|
|
|
/*
|
|
* Helper routine for generate_sched_domains().
|
|
* Do cpusets a, b have overlapping effective cpus_allowed masks?
|
|
*/
|
|
static inline int cpusets_overlap(struct cpuset *a, struct cpuset *b)
|
|
{
|
|
return cpumask_intersects(a->effective_cpus, b->effective_cpus);
|
|
}
|
|
|
|
static inline int nr_cpusets(void)
|
|
{
|
|
/* jump label reference count + the top-level cpuset */
|
|
return static_key_count(&cpusets_enabled_key.key) + 1;
|
|
}
|
|
|
|
static inline bool cpuset_is_populated(struct cpuset *cs)
|
|
{
|
|
lockdep_assert_cpuset_lock_held();
|
|
return cgroup_is_populated(cs->css.cgroup);
|
|
}
|
|
|
|
/**
|
|
* cpuset_for_each_child - traverse online children of a cpuset
|
|
* @child_cs: loop cursor pointing to the current child
|
|
* @pos_css: used for iteration
|
|
* @parent_cs: target cpuset to walk children of
|
|
*
|
|
* Walk @child_cs through the online children of @parent_cs. Must be used
|
|
* with RCU read locked.
|
|
*/
|
|
#define cpuset_for_each_child(child_cs, pos_css, parent_cs) \
|
|
css_for_each_child((pos_css), &(parent_cs)->css) \
|
|
if (is_cpuset_online(((child_cs) = css_cs((pos_css)))))
|
|
|
|
/**
|
|
* cpuset_for_each_descendant_pre - pre-order walk of a cpuset's descendants
|
|
* @des_cs: loop cursor pointing to the current descendant
|
|
* @pos_css: used for iteration
|
|
* @root_cs: target cpuset to walk ancestor of
|
|
*
|
|
* Walk @des_cs through the online descendants of @root_cs. Must be used
|
|
* with RCU read locked. The caller may modify @pos_css by calling
|
|
* css_rightmost_descendant() to skip subtree. @root_cs is included in the
|
|
* iteration and the first node to be visited.
|
|
*/
|
|
#define cpuset_for_each_descendant_pre(des_cs, pos_css, root_cs) \
|
|
css_for_each_descendant_pre((pos_css), &(root_cs)->css) \
|
|
if (is_cpuset_online(((des_cs) = css_cs((pos_css)))))
|
|
|
|
void rebuild_sched_domains_locked(void);
|
|
void cpuset_callback_lock_irq(void);
|
|
void cpuset_callback_unlock_irq(void);
|
|
void cpuset_update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus);
|
|
void cpuset_update_tasks_nodemask(struct cpuset *cs);
|
|
int cpuset_update_flag(cpuset_flagbits_t bit, struct cpuset *cs, int turning_on);
|
|
ssize_t cpuset_write_resmask(struct kernfs_open_file *of,
|
|
char *buf, size_t nbytes, loff_t off);
|
|
int cpuset_common_seq_show(struct seq_file *sf, void *v);
|
|
void cpuset_full_lock(void);
|
|
void cpuset_full_unlock(void);
|
|
|
|
/*
|
|
* cpuset-v1.c
|
|
*/
|
|
#ifdef CONFIG_CPUSETS_V1
|
|
extern struct cftype cpuset1_files[];
|
|
void cpuset1_update_task_spread_flags(struct cpuset *cs,
|
|
struct task_struct *tsk);
|
|
void cpuset1_update_tasks_flags(struct cpuset *cs);
|
|
void cpuset1_hotplug_update_tasks(struct cpuset *cs,
|
|
struct cpumask *new_cpus, nodemask_t *new_mems,
|
|
bool cpus_updated, bool mems_updated);
|
|
int cpuset1_validate_change(struct cpuset *cur, struct cpuset *trial);
|
|
bool cpuset1_cpus_excl_conflict(struct cpuset *cs1, struct cpuset *cs2);
|
|
void cpuset1_init(struct cpuset *cs);
|
|
void cpuset1_online_css(struct cgroup_subsys_state *css);
|
|
int cpuset1_generate_sched_domains(cpumask_var_t **domains,
|
|
struct sched_domain_attr **attributes);
|
|
|
|
#else
|
|
static inline void cpuset1_update_task_spread_flags(struct cpuset *cs,
|
|
struct task_struct *tsk) {}
|
|
static inline void cpuset1_update_tasks_flags(struct cpuset *cs) {}
|
|
static inline void cpuset1_hotplug_update_tasks(struct cpuset *cs,
|
|
struct cpumask *new_cpus, nodemask_t *new_mems,
|
|
bool cpus_updated, bool mems_updated) {}
|
|
static inline int cpuset1_validate_change(struct cpuset *cur,
|
|
struct cpuset *trial) { return 0; }
|
|
static inline bool cpuset1_cpus_excl_conflict(struct cpuset *cs1,
|
|
struct cpuset *cs2) { return false; }
|
|
static inline void cpuset1_init(struct cpuset *cs) {}
|
|
static inline void cpuset1_online_css(struct cgroup_subsys_state *css) {}
|
|
static inline int cpuset1_generate_sched_domains(cpumask_var_t **domains,
|
|
struct sched_domain_attr **attributes) { return 0; };
|
|
|
|
#endif /* CONFIG_CPUSETS_V1 */
|
|
|
|
#endif /* __CPUSET_INTERNAL_H */
|