Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e44193d3 authored by Li Zefan's avatar Li Zefan Committed by Tejun Heo
Browse files

cpuset: let hotplug propagation work wait for task attaching



Instead of triggering propagation work in cpuset_attach(), we make
hotplug propagation work wait until there's no task attaching in
progress.

IMO this is more robust. We won't see empty masks in cpuset_attach().

Also it's a preparation for removing propagation work. Without asynchronous
propagation we can't call move_tasks_in_empty_cpuset() in cpuset_attach(),
because otherwise we'll deadlock on cgroup_mutex.

tj: typo fixes.

Signed-off-by: default avatarLi Zefan <lizefan@huawei.com>
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
parent a73456f3
Loading
Loading
Loading
Loading
+17 −12
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/cgroup.h>
#include <linux/wait.h>

/*
 * Tracks how many cpusets are currently defined in system.
@@ -275,6 +276,8 @@ static void schedule_cpuset_propagate_hotplug(struct cpuset *cs);

static DECLARE_WORK(cpuset_hotplug_work, cpuset_hotplug_workfn);

static DECLARE_WAIT_QUEUE_HEAD(cpuset_attach_wq);

/*
 * This is ugly, but preserves the userspace API for existing cpuset
 * users. If someone tries to mount the "cpuset" filesystem, we
@@ -1436,14 +1439,8 @@ static void cpuset_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
	}

	cs->attach_in_progress--;

	/*
	 * We may have raced with CPU/memory hotunplug.  Trigger hotplug
	 * propagation if @cs doesn't have any CPU or memory.  It will move
	 * the newly added tasks to the nearest parent which can execute.
	 */
	if (cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))
		schedule_cpuset_propagate_hotplug(cs);
	if (!cs->attach_in_progress)
		wake_up(&cpuset_attach_wq);

	mutex_unlock(&cpuset_mutex);
}
@@ -1555,10 +1552,6 @@ static int cpuset_write_resmask(struct cgroup *cgrp, struct cftype *cft,
	 * resources, wait for the previously scheduled operations before
	 * proceeding, so that we don't end up keep removing tasks added
	 * after execution capability is restored.
	 *
	 * Flushing cpuset_hotplug_work is enough to synchronize against
	 * hotplug hanlding; however, cpuset_attach() may schedule
	 * propagation work directly.  Flush the workqueue too.
	 */
	flush_work(&cpuset_hotplug_work);
	flush_workqueue(cpuset_propagate_hotplug_wq);
@@ -2005,8 +1998,20 @@ static void cpuset_propagate_hotplug_workfn(struct work_struct *work)
	struct cpuset *cs = container_of(work, struct cpuset, hotplug_work);
	bool is_empty;

retry:
	wait_event(cpuset_attach_wq, cs->attach_in_progress == 0);

	mutex_lock(&cpuset_mutex);

	/*
	 * We have raced with task attaching. We wait until attaching
	 * is finished, so we won't attach a task to an empty cpuset.
	 */
	if (cs->attach_in_progress) {
		mutex_unlock(&cpuset_mutex);
		goto retry;
	}

	cpumask_andnot(&off_cpus, cs->cpus_allowed, top_cpuset.cpus_allowed);
	nodes_andnot(off_mems, cs->mems_allowed, top_cpuset.mems_allowed);