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