xref: /openbmc/linux/kernel/sched/core_sched.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
16e33cad0SPeter Zijlstra // SPDX-License-Identifier: GPL-2.0-only
26e33cad0SPeter Zijlstra 
36e33cad0SPeter Zijlstra /*
46e33cad0SPeter Zijlstra  * A simple wrapper around refcount. An allocated sched_core_cookie's
56e33cad0SPeter Zijlstra  * address is used to compute the cookie of the task.
66e33cad0SPeter Zijlstra  */
76e33cad0SPeter Zijlstra struct sched_core_cookie {
86e33cad0SPeter Zijlstra 	refcount_t refcnt;
96e33cad0SPeter Zijlstra };
106e33cad0SPeter Zijlstra 
sched_core_alloc_cookie(void)11d07b2eeeSShaokun Zhang static unsigned long sched_core_alloc_cookie(void)
126e33cad0SPeter Zijlstra {
136e33cad0SPeter Zijlstra 	struct sched_core_cookie *ck = kmalloc(sizeof(*ck), GFP_KERNEL);
146e33cad0SPeter Zijlstra 	if (!ck)
156e33cad0SPeter Zijlstra 		return 0;
166e33cad0SPeter Zijlstra 
176e33cad0SPeter Zijlstra 	refcount_set(&ck->refcnt, 1);
186e33cad0SPeter Zijlstra 	sched_core_get();
196e33cad0SPeter Zijlstra 
206e33cad0SPeter Zijlstra 	return (unsigned long)ck;
216e33cad0SPeter Zijlstra }
226e33cad0SPeter Zijlstra 
sched_core_put_cookie(unsigned long cookie)23d07b2eeeSShaokun Zhang static void sched_core_put_cookie(unsigned long cookie)
246e33cad0SPeter Zijlstra {
256e33cad0SPeter Zijlstra 	struct sched_core_cookie *ptr = (void *)cookie;
266e33cad0SPeter Zijlstra 
276e33cad0SPeter Zijlstra 	if (ptr && refcount_dec_and_test(&ptr->refcnt)) {
286e33cad0SPeter Zijlstra 		kfree(ptr);
296e33cad0SPeter Zijlstra 		sched_core_put();
306e33cad0SPeter Zijlstra 	}
316e33cad0SPeter Zijlstra }
326e33cad0SPeter Zijlstra 
sched_core_get_cookie(unsigned long cookie)33d07b2eeeSShaokun Zhang static unsigned long sched_core_get_cookie(unsigned long cookie)
346e33cad0SPeter Zijlstra {
356e33cad0SPeter Zijlstra 	struct sched_core_cookie *ptr = (void *)cookie;
366e33cad0SPeter Zijlstra 
376e33cad0SPeter Zijlstra 	if (ptr)
386e33cad0SPeter Zijlstra 		refcount_inc(&ptr->refcnt);
396e33cad0SPeter Zijlstra 
406e33cad0SPeter Zijlstra 	return cookie;
416e33cad0SPeter Zijlstra }
426e33cad0SPeter Zijlstra 
436e33cad0SPeter Zijlstra /*
446e33cad0SPeter Zijlstra  * sched_core_update_cookie - replace the cookie on a task
456e33cad0SPeter Zijlstra  * @p: the task to update
466e33cad0SPeter Zijlstra  * @cookie: the new cookie
476e33cad0SPeter Zijlstra  *
486e33cad0SPeter Zijlstra  * Effectively exchange the task cookie; caller is responsible for lifetimes on
496e33cad0SPeter Zijlstra  * both ends.
506e33cad0SPeter Zijlstra  *
516e33cad0SPeter Zijlstra  * Returns: the old cookie
526e33cad0SPeter Zijlstra  */
sched_core_update_cookie(struct task_struct * p,unsigned long cookie)53d07b2eeeSShaokun Zhang static unsigned long sched_core_update_cookie(struct task_struct *p,
54d07b2eeeSShaokun Zhang 					      unsigned long cookie)
556e33cad0SPeter Zijlstra {
566e33cad0SPeter Zijlstra 	unsigned long old_cookie;
576e33cad0SPeter Zijlstra 	struct rq_flags rf;
586e33cad0SPeter Zijlstra 	struct rq *rq;
596e33cad0SPeter Zijlstra 
606e33cad0SPeter Zijlstra 	rq = task_rq_lock(p, &rf);
616e33cad0SPeter Zijlstra 
626e33cad0SPeter Zijlstra 	/*
636e33cad0SPeter Zijlstra 	 * Since creating a cookie implies sched_core_get(), and we cannot set
646e33cad0SPeter Zijlstra 	 * a cookie until after we've created it, similarly, we cannot destroy
656e33cad0SPeter Zijlstra 	 * a cookie until after we've removed it, we must have core scheduling
666e33cad0SPeter Zijlstra 	 * enabled here.
676e33cad0SPeter Zijlstra 	 */
686e33cad0SPeter Zijlstra 	SCHED_WARN_ON((p->core_cookie || cookie) && !sched_core_enabled(rq));
696e33cad0SPeter Zijlstra 
7091caa5aeSCruz Zhao 	if (sched_core_enqueued(p))
714feee7d1SJosh Don 		sched_core_dequeue(rq, p, DEQUEUE_SAVE);
726e33cad0SPeter Zijlstra 
736e33cad0SPeter Zijlstra 	old_cookie = p->core_cookie;
746e33cad0SPeter Zijlstra 	p->core_cookie = cookie;
756e33cad0SPeter Zijlstra 
7691caa5aeSCruz Zhao 	/*
7791caa5aeSCruz Zhao 	 * Consider the cases: !prev_cookie and !cookie.
7891caa5aeSCruz Zhao 	 */
7991caa5aeSCruz Zhao 	if (cookie && task_on_rq_queued(p))
806e33cad0SPeter Zijlstra 		sched_core_enqueue(rq, p);
816e33cad0SPeter Zijlstra 
826e33cad0SPeter Zijlstra 	/*
836e33cad0SPeter Zijlstra 	 * If task is currently running, it may not be compatible anymore after
846e33cad0SPeter Zijlstra 	 * the cookie change, so enter the scheduler on its CPU to schedule it
856e33cad0SPeter Zijlstra 	 * away.
864feee7d1SJosh Don 	 *
874feee7d1SJosh Don 	 * Note that it is possible that as a result of this cookie change, the
884feee7d1SJosh Don 	 * core has now entered/left forced idle state. Defer accounting to the
894feee7d1SJosh Don 	 * next scheduling edge, rather than always forcing a reschedule here.
906e33cad0SPeter Zijlstra 	 */
91*0b9d46fcSPeter Zijlstra 	if (task_on_cpu(rq, p))
926e33cad0SPeter Zijlstra 		resched_curr(rq);
936e33cad0SPeter Zijlstra 
946e33cad0SPeter Zijlstra 	task_rq_unlock(rq, p, &rf);
956e33cad0SPeter Zijlstra 
966e33cad0SPeter Zijlstra 	return old_cookie;
976e33cad0SPeter Zijlstra }
986e33cad0SPeter Zijlstra 
sched_core_clone_cookie(struct task_struct * p)996e33cad0SPeter Zijlstra static unsigned long sched_core_clone_cookie(struct task_struct *p)
1006e33cad0SPeter Zijlstra {
1016e33cad0SPeter Zijlstra 	unsigned long cookie, flags;
1026e33cad0SPeter Zijlstra 
1036e33cad0SPeter Zijlstra 	raw_spin_lock_irqsave(&p->pi_lock, flags);
1046e33cad0SPeter Zijlstra 	cookie = sched_core_get_cookie(p->core_cookie);
1056e33cad0SPeter Zijlstra 	raw_spin_unlock_irqrestore(&p->pi_lock, flags);
1066e33cad0SPeter Zijlstra 
1076e33cad0SPeter Zijlstra 	return cookie;
1086e33cad0SPeter Zijlstra }
1096e33cad0SPeter Zijlstra 
sched_core_fork(struct task_struct * p)11085dd3f61SPeter Zijlstra void sched_core_fork(struct task_struct *p)
11185dd3f61SPeter Zijlstra {
11285dd3f61SPeter Zijlstra 	RB_CLEAR_NODE(&p->core_node);
11385dd3f61SPeter Zijlstra 	p->core_cookie = sched_core_clone_cookie(current);
11485dd3f61SPeter Zijlstra }
11585dd3f61SPeter Zijlstra 
sched_core_free(struct task_struct * p)1166e33cad0SPeter Zijlstra void sched_core_free(struct task_struct *p)
1176e33cad0SPeter Zijlstra {
1186e33cad0SPeter Zijlstra 	sched_core_put_cookie(p->core_cookie);
1196e33cad0SPeter Zijlstra }
1207ac592aaSChris Hyser 
__sched_core_set(struct task_struct * p,unsigned long cookie)1217ac592aaSChris Hyser static void __sched_core_set(struct task_struct *p, unsigned long cookie)
1227ac592aaSChris Hyser {
1237ac592aaSChris Hyser 	cookie = sched_core_get_cookie(cookie);
1247ac592aaSChris Hyser 	cookie = sched_core_update_cookie(p, cookie);
1257ac592aaSChris Hyser 	sched_core_put_cookie(cookie);
1267ac592aaSChris Hyser }
1277ac592aaSChris Hyser 
1287ac592aaSChris Hyser /* Called from prctl interface: PR_SCHED_CORE */
sched_core_share_pid(unsigned int cmd,pid_t pid,enum pid_type type,unsigned long uaddr)1297ac592aaSChris Hyser int sched_core_share_pid(unsigned int cmd, pid_t pid, enum pid_type type,
1307ac592aaSChris Hyser 			 unsigned long uaddr)
1317ac592aaSChris Hyser {
1327ac592aaSChris Hyser 	unsigned long cookie = 0, id = 0;
1337ac592aaSChris Hyser 	struct task_struct *task, *p;
1347ac592aaSChris Hyser 	struct pid *grp;
1357ac592aaSChris Hyser 	int err = 0;
1367ac592aaSChris Hyser 
1377ac592aaSChris Hyser 	if (!static_branch_likely(&sched_smt_present))
1387ac592aaSChris Hyser 		return -ENODEV;
1397ac592aaSChris Hyser 
14061bc346cSEugene Syromiatnikov 	BUILD_BUG_ON(PR_SCHED_CORE_SCOPE_THREAD != PIDTYPE_PID);
14161bc346cSEugene Syromiatnikov 	BUILD_BUG_ON(PR_SCHED_CORE_SCOPE_THREAD_GROUP != PIDTYPE_TGID);
14261bc346cSEugene Syromiatnikov 	BUILD_BUG_ON(PR_SCHED_CORE_SCOPE_PROCESS_GROUP != PIDTYPE_PGID);
14361bc346cSEugene Syromiatnikov 
1447ac592aaSChris Hyser 	if (type > PIDTYPE_PGID || cmd >= PR_SCHED_CORE_MAX || pid < 0 ||
1457ac592aaSChris Hyser 	    (cmd != PR_SCHED_CORE_GET && uaddr))
1467ac592aaSChris Hyser 		return -EINVAL;
1477ac592aaSChris Hyser 
1487ac592aaSChris Hyser 	rcu_read_lock();
1497ac592aaSChris Hyser 	if (pid == 0) {
1507ac592aaSChris Hyser 		task = current;
1517ac592aaSChris Hyser 	} else {
1527ac592aaSChris Hyser 		task = find_task_by_vpid(pid);
1537ac592aaSChris Hyser 		if (!task) {
1547ac592aaSChris Hyser 			rcu_read_unlock();
1557ac592aaSChris Hyser 			return -ESRCH;
1567ac592aaSChris Hyser 		}
1577ac592aaSChris Hyser 	}
1587ac592aaSChris Hyser 	get_task_struct(task);
1597ac592aaSChris Hyser 	rcu_read_unlock();
1607ac592aaSChris Hyser 
1617ac592aaSChris Hyser 	/*
1627ac592aaSChris Hyser 	 * Check if this process has the right to modify the specified
1637ac592aaSChris Hyser 	 * process. Use the regular "ptrace_may_access()" checks.
1647ac592aaSChris Hyser 	 */
1657ac592aaSChris Hyser 	if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS)) {
1667ac592aaSChris Hyser 		err = -EPERM;
1677ac592aaSChris Hyser 		goto out;
1687ac592aaSChris Hyser 	}
1697ac592aaSChris Hyser 
1707ac592aaSChris Hyser 	switch (cmd) {
1717ac592aaSChris Hyser 	case PR_SCHED_CORE_GET:
1727ac592aaSChris Hyser 		if (type != PIDTYPE_PID || uaddr & 7) {
1737ac592aaSChris Hyser 			err = -EINVAL;
1747ac592aaSChris Hyser 			goto out;
1757ac592aaSChris Hyser 		}
1767ac592aaSChris Hyser 		cookie = sched_core_clone_cookie(task);
1777ac592aaSChris Hyser 		if (cookie) {
1787ac592aaSChris Hyser 			/* XXX improve ? */
1797ac592aaSChris Hyser 			ptr_to_hashval((void *)cookie, &id);
1807ac592aaSChris Hyser 		}
1817ac592aaSChris Hyser 		err = put_user(id, (u64 __user *)uaddr);
1827ac592aaSChris Hyser 		goto out;
1837ac592aaSChris Hyser 
1847ac592aaSChris Hyser 	case PR_SCHED_CORE_CREATE:
1857ac592aaSChris Hyser 		cookie = sched_core_alloc_cookie();
1867ac592aaSChris Hyser 		if (!cookie) {
1877ac592aaSChris Hyser 			err = -ENOMEM;
1887ac592aaSChris Hyser 			goto out;
1897ac592aaSChris Hyser 		}
1907ac592aaSChris Hyser 		break;
1917ac592aaSChris Hyser 
1927ac592aaSChris Hyser 	case PR_SCHED_CORE_SHARE_TO:
1937ac592aaSChris Hyser 		cookie = sched_core_clone_cookie(current);
1947ac592aaSChris Hyser 		break;
1957ac592aaSChris Hyser 
1967ac592aaSChris Hyser 	case PR_SCHED_CORE_SHARE_FROM:
1977ac592aaSChris Hyser 		if (type != PIDTYPE_PID) {
1987ac592aaSChris Hyser 			err = -EINVAL;
1997ac592aaSChris Hyser 			goto out;
2007ac592aaSChris Hyser 		}
2017ac592aaSChris Hyser 		cookie = sched_core_clone_cookie(task);
2027ac592aaSChris Hyser 		__sched_core_set(current, cookie);
2037ac592aaSChris Hyser 		goto out;
2047ac592aaSChris Hyser 
2057ac592aaSChris Hyser 	default:
2067ac592aaSChris Hyser 		err = -EINVAL;
2077ac592aaSChris Hyser 		goto out;
2088648f92aSXin Gao 	}
2097ac592aaSChris Hyser 
2107ac592aaSChris Hyser 	if (type == PIDTYPE_PID) {
2117ac592aaSChris Hyser 		__sched_core_set(task, cookie);
2127ac592aaSChris Hyser 		goto out;
2137ac592aaSChris Hyser 	}
2147ac592aaSChris Hyser 
2157ac592aaSChris Hyser 	read_lock(&tasklist_lock);
2167ac592aaSChris Hyser 	grp = task_pid_type(task, type);
2177ac592aaSChris Hyser 
2187ac592aaSChris Hyser 	do_each_pid_thread(grp, type, p) {
2197ac592aaSChris Hyser 		if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS)) {
2207ac592aaSChris Hyser 			err = -EPERM;
2217ac592aaSChris Hyser 			goto out_tasklist;
2227ac592aaSChris Hyser 		}
2237ac592aaSChris Hyser 	} while_each_pid_thread(grp, type, p);
2247ac592aaSChris Hyser 
2257ac592aaSChris Hyser 	do_each_pid_thread(grp, type, p) {
2267ac592aaSChris Hyser 		__sched_core_set(p, cookie);
2277ac592aaSChris Hyser 	} while_each_pid_thread(grp, type, p);
2287ac592aaSChris Hyser out_tasklist:
2297ac592aaSChris Hyser 	read_unlock(&tasklist_lock);
2307ac592aaSChris Hyser 
2317ac592aaSChris Hyser out:
2327ac592aaSChris Hyser 	sched_core_put_cookie(cookie);
2337ac592aaSChris Hyser 	put_task_struct(task);
2347ac592aaSChris Hyser 	return err;
2357ac592aaSChris Hyser }
2367ac592aaSChris Hyser 
2374feee7d1SJosh Don #ifdef CONFIG_SCHEDSTATS
2384feee7d1SJosh Don 
2394feee7d1SJosh Don /* REQUIRES: rq->core's clock recently updated. */
__sched_core_account_forceidle(struct rq * rq)2404feee7d1SJosh Don void __sched_core_account_forceidle(struct rq *rq)
2414feee7d1SJosh Don {
2424feee7d1SJosh Don 	const struct cpumask *smt_mask = cpu_smt_mask(cpu_of(rq));
2434feee7d1SJosh Don 	u64 delta, now = rq_clock(rq->core);
2444feee7d1SJosh Don 	struct rq *rq_i;
2454feee7d1SJosh Don 	struct task_struct *p;
2464feee7d1SJosh Don 	int i;
2474feee7d1SJosh Don 
2484feee7d1SJosh Don 	lockdep_assert_rq_held(rq);
2494feee7d1SJosh Don 
2504feee7d1SJosh Don 	WARN_ON_ONCE(!rq->core->core_forceidle_count);
2514feee7d1SJosh Don 
2524feee7d1SJosh Don 	if (rq->core->core_forceidle_start == 0)
2534feee7d1SJosh Don 		return;
2544feee7d1SJosh Don 
2554feee7d1SJosh Don 	delta = now - rq->core->core_forceidle_start;
2564feee7d1SJosh Don 	if (unlikely((s64)delta <= 0))
2574feee7d1SJosh Don 		return;
2584feee7d1SJosh Don 
2594feee7d1SJosh Don 	rq->core->core_forceidle_start = now;
2604feee7d1SJosh Don 
2614feee7d1SJosh Don 	if (WARN_ON_ONCE(!rq->core->core_forceidle_occupation)) {
2624feee7d1SJosh Don 		/* can't be forced idle without a running task */
2634feee7d1SJosh Don 	} else if (rq->core->core_forceidle_count > 1 ||
2644feee7d1SJosh Don 		   rq->core->core_forceidle_occupation > 1) {
2654feee7d1SJosh Don 		/*
2664feee7d1SJosh Don 		 * For larger SMT configurations, we need to scale the charged
2674feee7d1SJosh Don 		 * forced idle amount since there can be more than one forced
2684feee7d1SJosh Don 		 * idle sibling and more than one running cookied task.
2694feee7d1SJosh Don 		 */
2704feee7d1SJosh Don 		delta *= rq->core->core_forceidle_count;
2714feee7d1SJosh Don 		delta = div_u64(delta, rq->core->core_forceidle_occupation);
2724feee7d1SJosh Don 	}
2734feee7d1SJosh Don 
2744feee7d1SJosh Don 	for_each_cpu(i, smt_mask) {
2754feee7d1SJosh Don 		rq_i = cpu_rq(i);
2764feee7d1SJosh Don 		p = rq_i->core_pick ?: rq_i->curr;
2774feee7d1SJosh Don 
278b171501fSCruz Zhao 		if (p == rq_i->idle)
2794feee7d1SJosh Don 			continue;
2804feee7d1SJosh Don 
2811fcf54deSJosh Don 		/*
2821fcf54deSJosh Don 		 * Note: this will account forceidle to the current cpu, even
2831fcf54deSJosh Don 		 * if it comes from our SMT sibling.
2841fcf54deSJosh Don 		 */
2851fcf54deSJosh Don 		__account_forceidle_time(p, delta);
2864feee7d1SJosh Don 	}
2874feee7d1SJosh Don }
2884feee7d1SJosh Don 
__sched_core_tick(struct rq * rq)2894feee7d1SJosh Don void __sched_core_tick(struct rq *rq)
2904feee7d1SJosh Don {
2914feee7d1SJosh Don 	if (!rq->core->core_forceidle_count)
2924feee7d1SJosh Don 		return;
2934feee7d1SJosh Don 
2944feee7d1SJosh Don 	if (rq != rq->core)
2954feee7d1SJosh Don 		update_rq_clock(rq->core);
2964feee7d1SJosh Don 
2974feee7d1SJosh Don 	__sched_core_account_forceidle(rq);
2984feee7d1SJosh Don }
2994feee7d1SJosh Don 
3004feee7d1SJosh Don #endif /* CONFIG_SCHEDSTATS */
301