diff options
Diffstat (limited to 'kernel/sched/core.c')
| -rw-r--r-- | kernel/sched/core.c | 77 | 
1 files changed, 67 insertions, 10 deletions
| diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 25b582b6ee5f..e838feb6adc5 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2604,27 +2604,71 @@ void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask)  		.user_mask = NULL,  		.flags     = SCA_USER,	/* clear the user requested mask */  	}; +	union cpumask_rcuhead { +		cpumask_t cpumask; +		struct rcu_head rcu; +	};  	__do_set_cpus_allowed(p, &ac); -	kfree(ac.user_mask); + +	/* +	 * Because this is called with p->pi_lock held, it is not possible +	 * to use kfree() here (when PREEMPT_RT=y), therefore punt to using +	 * kfree_rcu(). +	 */ +	kfree_rcu((union cpumask_rcuhead *)ac.user_mask, rcu); +} + +static cpumask_t *alloc_user_cpus_ptr(int node) +{ +	/* +	 * See do_set_cpus_allowed() above for the rcu_head usage. +	 */ +	int size = max_t(int, cpumask_size(), sizeof(struct rcu_head)); + +	return kmalloc_node(size, GFP_KERNEL, node);  }  int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src,  		      int node)  { +	cpumask_t *user_mask;  	unsigned long flags; -	if (!src->user_cpus_ptr) +	/* +	 * Always clear dst->user_cpus_ptr first as their user_cpus_ptr's +	 * may differ by now due to racing. +	 */ +	dst->user_cpus_ptr = NULL; + +	/* +	 * This check is racy and losing the race is a valid situation. +	 * It is not worth the extra overhead of taking the pi_lock on +	 * every fork/clone. +	 */ +	if (data_race(!src->user_cpus_ptr))  		return 0; -	dst->user_cpus_ptr = kmalloc_node(cpumask_size(), GFP_KERNEL, node); -	if (!dst->user_cpus_ptr) +	user_mask = alloc_user_cpus_ptr(node); +	if (!user_mask)  		return -ENOMEM; -	/* Use pi_lock to protect content of user_cpus_ptr */ +	/* +	 * Use pi_lock to protect content of user_cpus_ptr +	 * +	 * Though unlikely, user_cpus_ptr can be reset to NULL by a concurrent +	 * do_set_cpus_allowed(). +	 */  	raw_spin_lock_irqsave(&src->pi_lock, flags); -	cpumask_copy(dst->user_cpus_ptr, src->user_cpus_ptr); +	if (src->user_cpus_ptr) { +		swap(dst->user_cpus_ptr, user_mask); +		cpumask_copy(dst->user_cpus_ptr, src->user_cpus_ptr); +	}  	raw_spin_unlock_irqrestore(&src->pi_lock, flags); + +	if (unlikely(user_mask)) +		kfree(user_mask); +  	return 0;  } @@ -3581,6 +3625,11 @@ static inline bool rq_has_pinned_tasks(struct rq *rq)  	return false;  } +static inline cpumask_t *alloc_user_cpus_ptr(int node) +{ +	return NULL; +} +  #endif /* !CONFIG_SMP */  static void @@ -5504,7 +5553,9 @@ void scheduler_tick(void)  	unsigned long thermal_pressure;  	u64 resched_latency; -	arch_scale_freq_tick(); +	if (housekeeping_cpu(cpu, HK_TYPE_TICK)) +		arch_scale_freq_tick(); +  	sched_clock_tick();  	rq_lock(rq, &rf); @@ -8239,12 +8290,18 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)  	if (retval)  		goto out_put_task; -	user_mask = kmalloc(cpumask_size(), GFP_KERNEL); -	if (!user_mask) { +	/* +	 * With non-SMP configs, user_cpus_ptr/user_mask isn't used and +	 * alloc_user_cpus_ptr() returns NULL. +	 */ +	user_mask = alloc_user_cpus_ptr(NUMA_NO_NODE); +	if (user_mask) { +		cpumask_copy(user_mask, in_mask); +	} else if (IS_ENABLED(CONFIG_SMP)) {  		retval = -ENOMEM;  		goto out_put_task;  	} -	cpumask_copy(user_mask, in_mask); +  	ac = (struct affinity_context){  		.new_mask  = in_mask,  		.user_mask = user_mask, |