This patch adds a new critical-section primitive pair: "migration_disable()" and "migration_enable()" This allows you to force a task to remain on the current cpu, while still remaining fully preemptible. This is a better alternative to modifying current->cpus_allowed because you dont have to worry about colliding with another entity also modifying the cpumask_t while in the critical section. In fact, modifying the cpumask_t while in the critical section is fully supported, but note that the behavior of set_cpus_allowed() has slightly different behavior. In the old code, the mask update was synchronous: e.g. the task would be on a legal cpu when the call returned. The new behavior makes this asynchronous if the task is currently in a migration-disabled critical section. The task will migrate to a legal cpu once the critical section ends. This concept will be used later in the series. Signed-off-by: Gregory Haskins <ghaskins@xxxxxxxxxx> --- include/linux/init_task.h | 1 + include/linux/sched.h | 8 +++++ kernel/fork.c | 1 + kernel/sched.c | 70 ++++++++++++++++++++++++++++++++++++--------- kernel/sched_rt.c | 6 +++- 5 files changed, 70 insertions(+), 16 deletions(-) diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 316a184..151197b 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -137,6 +137,7 @@ extern struct group_info init_groups; .usage = ATOMIC_INIT(2), \ .flags = 0, \ .lock_depth = -1, \ + .migration_disable_depth = 0, \ .prio = MAX_PRIO-20, \ .static_prio = MAX_PRIO-20, \ .normal_prio = MAX_PRIO-20, \ diff --git a/include/linux/sched.h b/include/linux/sched.h index c87d46a..ab7768a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1109,6 +1109,7 @@ struct task_struct { unsigned int ptrace; int lock_depth; /* BKL lock depth */ + int migration_disable_depth; #ifdef CONFIG_SMP #ifdef __ARCH_WANT_UNLOCKED_CTXSW @@ -2284,10 +2285,17 @@ static inline void inc_syscw(struct task_struct *tsk) #ifdef CONFIG_SMP void migration_init(void); +int migration_disable(struct task_struct *tsk); +void migration_enable(struct task_struct *tsk); #else static inline void migration_init(void) { } +static inline int migration_disable(struct task_struct *tsk) +{ + return 0; +} +#define migration_enable(tsk) do {} while (0) #endif #endif /* __KERNEL__ */ diff --git a/kernel/fork.c b/kernel/fork.c index 8c00b55..7745937 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1127,6 +1127,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, INIT_LIST_HEAD(&p->cpu_timers[2]); p->posix_timer_list = NULL; p->lock_depth = -1; /* -1 = no lock */ + p->migration_disable_depth = 0; do_posix_clock_monotonic_gettime(&p->start_time); p->real_start_time = p->start_time; monotonic_to_bootbased(&p->real_start_time); diff --git a/kernel/sched.c b/kernel/sched.c index e6ad493..cf32000 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1231,6 +1231,8 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu) *new_cfsrq = cpu_cfs_rq(old_cfsrq, new_cpu); u64 clock_offset; + BUG_ON(p->migration_disable_depth); + clock_offset = old_rq->clock - new_rq->clock; #ifdef CONFIG_SCHEDSTATS @@ -1632,7 +1634,9 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int sync, int mutex) if (unlikely(task_running(rq, p))) goto out_activate; - cpu = p->sched_class->select_task_rq(p, sync); + if (!p->migration_disable_depth) + cpu = p->sched_class->select_task_rq(p, sync); + if (cpu != orig_cpu) { set_task_cpu(p, cpu); task_rq_unlock(rq, &flags); @@ -5422,11 +5426,12 @@ static inline void sched_init_granularity(void) */ int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask) { - struct migration_req req; unsigned long flags; struct rq *rq; int ret = 0; + migration_disable(p); + rq = task_rq_lock(p, &flags); if (!cpus_intersects(new_mask, cpu_online_map)) { ret = -EINVAL; @@ -5440,21 +5445,11 @@ int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask) p->nr_cpus_allowed = cpus_weight(new_mask); } - /* Can the task run on the task's current CPU? If so, we're done */ - if (cpu_isset(task_cpu(p), new_mask)) - goto out; - - if (migrate_task(p, any_online_cpu(new_mask), &req)) { - /* Need help from migration thread: drop lock and wait. */ - task_rq_unlock(rq, &flags); - wake_up_process(rq->migration_thread); - wait_for_completion(&req.done); - tlb_migrate_finish(p->mm); - return 0; - } out: task_rq_unlock(rq, &flags); + migration_enable(p); + return ret; } EXPORT_SYMBOL_GPL(set_cpus_allowed); @@ -7883,3 +7878,50 @@ struct cgroup_subsys cpuacct_subsys = { .subsys_id = cpuacct_subsys_id, }; #endif /* CONFIG_CGROUP_CPUACCT */ + +#ifdef CONFIG_SMP +int migration_disable(struct task_struct *p) +{ + unsigned long flags; + struct rq *rq = task_rq_lock(p, &flags); + int cpu = raw_smp_processor_id(); + + p->migration_disable_depth++; + + task_rq_unlock(rq, &flags); + + return cpu; +} +EXPORT_SYMBOL(migration_disable); + +void migration_enable(struct task_struct *p) +{ + struct migration_req req; + unsigned long flags; + struct rq *rq = task_rq_lock(p, &flags); + + BUG_ON(!p->migration_disable_depth); + p->migration_disable_depth--; + + /* + * If the task is still not migratable, or if it is already on + * an allowed CPU, just bail out + */ + if (p->migration_disable_depth + || cpu_isset(task_cpu(p), p->cpus_allowed)) + goto out; + + if (migrate_task(p, any_online_cpu(p->cpus_allowed), &req)) { + /* Need help from migration thread: drop lock and wait. */ + task_rq_unlock(rq, &flags); + wake_up_process(rq->migration_thread); + wait_for_completion(&req.done); + tlb_migrate_finish(p->mm); + return; + } + + out: + task_rq_unlock(rq, &flags); +} +EXPORT_SYMBOL(migration_enable); +#endif diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c index f1b5652..bec362c 100644 --- a/kernel/sched_rt.c +++ b/kernel/sched_rt.c @@ -300,7 +300,8 @@ static int pick_rt_task(struct rq *rq, struct task_struct *p, int cpu) { if (!task_running(rq, p) && (cpu < 0 || cpu_isset(cpu, p->cpus_allowed)) && - (p->nr_cpus_allowed > 1)) + (p->nr_cpus_allowed > 1) && + !p->migration_disable_depth) return 1; return 0; } @@ -397,7 +398,8 @@ static int find_lowest_rq(struct task_struct *task) int cpu = task_cpu(task); int count; - if (task->nr_cpus_allowed == 1) + if (task->nr_cpus_allowed == 1 + || task->migration_disable_depth) return -1; /* No other targets possible */ count = find_lowest_cpus(task, lowest_mask); - To unsubscribe from this list: send the line "unsubscribe linux-rt-users" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html