Commit 7476a636 authored by Waiman Long's avatar Waiman Long Committed by Tejun Heo
Browse files

cgroup/cpuset: Show invalid partition reason string



There are a number of different reasons which can cause a partition to
become invalid. A user seeing an invalid partition may not know exactly
why. To help user to get a better understanding of the underlying reason,
The cpuset.cpus.partition control file, when read, will now report the
reason why a partition become invalid. When a partition does become
invalid, reading the control file will show "root invalid (<reason>)"
where <reason> is a string that describes why the partition is invalid.

Signed-off-by: default avatarWaiman Long <longman@redhat.com>
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
parent f28e2244
Loading
Loading
Loading
Loading
+75 −18
Original line number Diff line number Diff line
@@ -86,6 +86,30 @@ struct fmeter {
	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,
};

static const char * const perr_strings[] = {
	[PERR_INVCPUS]   = "Invalid cpu list in cpuset.cpus",
	[PERR_INVPARENT] = "Parent is an invalid partition root",
	[PERR_NOTPART]   = "Parent is not a partition root",
	[PERR_NOTEXCL]   = "Cpu list in cpuset.cpus not exclusive",
	[PERR_NOCPUS]    = "Parent unable to distribute cpu downstream",
	[PERR_HOTPLUG]   = "No cpu available due to hotplug",
	[PERR_CPUSEMPTY] = "cpuset.cpus is empty",
};

struct cpuset {
	struct cgroup_subsys_state css;

@@ -169,6 +193,9 @@ struct cpuset {
	int use_parent_ecpus;
	int child_ecpus_count;

	/* Invalid partition error code, not lock protected */
	enum prs_errcode prs_err;

	/* Handle for cpuset.cpus.partition */
	struct cgroup_file partition_file;
};
@@ -298,6 +325,10 @@ static inline void notify_partition_change(struct cpuset *cs, int old_prs)
	if (old_prs == cs->partition_root_state)
		return;
	cgroup_file_notify(&cs->partition_file);

	/* Reset prs_err if not invalid */
	if (is_partition_valid(cs))
		WRITE_ONCE(cs->prs_err, PERR_NONE);
}

static struct cpuset top_cpuset = {
@@ -1235,7 +1266,7 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
 * @cmd:     Partition root state change command
 * @newmask: Optional new cpumask for partcmd_update
 * @tmp:     Temporary addmask and delmask
 * Return:   0 or -1 (error)
 * Return:   0 or a partition root state error code
 *
 * For partcmd_enable, the cpuset is being transformed from a non-partition
 * root to a partition root. The cpus_allowed mask of the given cpuset will
@@ -1261,7 +1292,7 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
 *
 * The partcmd_update command is used by update_cpumasks_hier() with newmask
 * NULL and update_cpumask() with newmask set. The callers won't check for
 * error and so partition_root_state will be updated directly.
 * error and so partition_root_state and prs_error will be updated directly.
 */
static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
					  struct cpumask *newmask,
@@ -1271,7 +1302,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
	int adding;	/* Moving cpus from effective_cpus to subparts_cpus */
	int deleting;	/* Moving cpus from subparts_cpus to effective_cpus */
	int old_prs, new_prs;
	bool part_error = false;	/* Partition error? */
	int part_error = PERR_NONE;	/* Partition error? */

	percpu_rwsem_assert_held(&cpuset_rwsem);

@@ -1280,10 +1311,13 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
	 * The new cpumask, if present, or the current cpus_allowed must
	 * not be empty.
	 */
	if (!is_partition_valid(parent) ||
	   (newmask && cpumask_empty(newmask)) ||
	if (!is_partition_valid(parent)) {
		return is_partition_invalid(parent)
		       ? PERR_INVPARENT : PERR_NOTPART;
	}
	if ((newmask && cpumask_empty(newmask)) ||
	   (!newmask && cpumask_empty(cs->cpus_allowed)))
		return -1;
		return PERR_CPUSEMPTY;

	/*
	 * new_prs will only be changed for the partcmd_update command.
@@ -1296,7 +1330,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
		 * doesn't overlap parent's cpus_allowed.
		 */
		if (!cpumask_intersects(cs->cpus_allowed, parent->cpus_allowed))
			return -1;
			return PERR_INVCPUS;

		/*
		 * A parent can be left with no CPU as long as there is no
@@ -1304,7 +1338,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
		 */
		if (!cpumask_intersects(cs->cpus_allowed, parent->effective_cpus) &&
		    partition_is_populated(parent, cs))
			return -1;
			return PERR_NOCPUS;

		cpumask_copy(tmp->addmask, cs->cpus_allowed);
		adding = true;
@@ -1341,7 +1375,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
		    cpumask_subset(parent->effective_cpus, tmp->addmask) &&
		    !cpumask_intersects(tmp->delmask, cpu_active_mask) &&
		    partition_is_populated(parent, cs)) {
			part_error = true;
			part_error = PERR_NOCPUS;
			adding = false;
			deleting = cpumask_and(tmp->delmask, cs->cpus_allowed,
					       parent->subparts_cpus);
@@ -1373,7 +1407,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
		    (adding &&
		     cpumask_subset(parent->effective_cpus, tmp->addmask) &&
		     partition_is_populated(parent, cs))) {
			part_error = true;
			part_error = PERR_NOCPUS;
			adding = false;
		}

@@ -1382,6 +1416,8 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
			deleting = cpumask_and(tmp->delmask, cs->cpus_allowed,
					       parent->subparts_cpus);
	}
	if (part_error)
		WRITE_ONCE(cs->prs_err, part_error);

	if (cmd == partcmd_update) {
		/*
@@ -1412,7 +1448,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
	if (old_prs != new_prs) {
		if (is_prs_invalid(old_prs) && !is_cpu_exclusive(cs) &&
		    (update_flag(CS_CPU_EXCLUSIVE, cs, 1) < 0))
			return -1;
			return PERR_NOTEXCL;
		if (is_prs_invalid(new_prs) && is_cpu_exclusive(cs))
			update_flag(CS_CPU_EXCLUSIVE, cs, 0);
	}
@@ -1547,6 +1583,9 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
				 */
				if (is_partition_valid(cp))
					new_prs = -cp->partition_root_state;
				WRITE_ONCE(cp->prs_err,
					   is_partition_invalid(parent)
					   ? PERR_INVPARENT : PERR_NOTPART);
				break;
			}
		}
@@ -2121,13 +2160,13 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
 * update_prstate - update partition_root_state
 * @cs: the cpuset to update
 * @new_prs: new partition root state
 * Return: 0 if successful, < 0 if error
 * Return: 0 if successful, != 0 if error
 *
 * Call with cpuset_rwsem held.
 */
static int update_prstate(struct cpuset *cs, int new_prs)
{
	int err = 0, old_prs = cs->partition_root_state;
	int err = PERR_NONE, old_prs = cs->partition_root_state;
	bool sched_domain_rebuilt = false;
	struct cpuset *parent = parent_cs(cs);
	struct tmpmasks tmpmask;
@@ -2154,13 +2193,15 @@ static int update_prstate(struct cpuset *cs, int new_prs)
		 * cannot be empty.
		 */
		if (cpumask_empty(cs->cpus_allowed)) {
			err = 1;
			err = PERR_CPUSEMPTY;
			goto out;
		}

		err = update_flag(CS_CPU_EXCLUSIVE, cs, 1);
		if (err)
		if (err) {
			err = PERR_NOTEXCL;
			goto out;
		}

		err = update_parent_subparts_cpumask(cs, partcmd_enable,
						     NULL, &tmpmask);
@@ -2730,6 +2771,7 @@ static s64 cpuset_read_s64(struct cgroup_subsys_state *css, struct cftype *cft)
static int sched_partition_show(struct seq_file *seq, void *v)
{
	struct cpuset *cs = css_cs(seq_css(seq));
	const char *err, *type = NULL;

	switch (cs->partition_root_state) {
	case PRS_ROOT:
@@ -2742,9 +2784,17 @@ static int sched_partition_show(struct seq_file *seq, void *v)
		seq_puts(seq, "member\n");
		break;
	case PRS_INVALID_ROOT:
		seq_puts(seq, "root invalid\n");
		break;
		type = "root";
		fallthrough;
	case PRS_INVALID_ISOLATED:
		if (!type)
			type = "isolated";
		err = perr_strings[READ_ONCE(cs->prs_err)];
		if (err)
			seq_printf(seq, "%s invalid (%s)\n", type, err);
		else
			seq_printf(seq, "%s invalid\n", type);
		break;
		seq_puts(seq, "isolated invalid\n");
		break;
	}
@@ -3335,7 +3385,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
	 */
	if (is_partition_valid(cs) && (!parent->nr_subparts_cpus ||
	   (cpumask_empty(&new_cpus) && partition_is_populated(cs, NULL)))) {
		int old_prs;
		int old_prs, parent_prs;

		update_parent_subparts_cpumask(cs, partcmd_disable, NULL, tmp);
		if (cs->nr_subparts_cpus) {
@@ -3347,10 +3397,17 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
		}

		old_prs = cs->partition_root_state;
		parent_prs = parent->partition_root_state;
		if (is_partition_valid(cs)) {
			spin_lock_irq(&callback_lock);
			make_partition_invalid(cs);
			spin_unlock_irq(&callback_lock);
			if (is_prs_invalid(parent_prs))
				WRITE_ONCE(cs->prs_err, PERR_INVPARENT);
			else if (!parent_prs)
				WRITE_ONCE(cs->prs_err, PERR_NOTPART);
			else
				WRITE_ONCE(cs->prs_err, PERR_HOTPLUG);
			notify_partition_change(cs, old_prs);
		}
		cpuset_force_rebuild();