1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2295cbf6dSRalf Baechle /*
3b633648cSRalf Baechle * General MIPS MT support routines, usable in AP/SP and SMVP.
4295cbf6dSRalf Baechle * Copyright (C) 2005 Mips Technologies, Inc
5295cbf6dSRalf Baechle */
6295cbf6dSRalf Baechle #include <linux/cpu.h>
717c04139SRalf Baechle #include <linux/cpuset.h>
8295cbf6dSRalf Baechle #include <linux/cpumask.h>
9295cbf6dSRalf Baechle #include <linux/delay.h>
10295cbf6dSRalf Baechle #include <linux/kernel.h>
11295cbf6dSRalf Baechle #include <linux/init.h>
12295cbf6dSRalf Baechle #include <linux/sched.h>
1329930025SIngo Molnar #include <linux/sched/task.h>
145b825c3aSIngo Molnar #include <linux/cred.h>
15295cbf6dSRalf Baechle #include <linux/security.h>
16295cbf6dSRalf Baechle #include <linux/types.h>
177c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
18295cbf6dSRalf Baechle
19295cbf6dSRalf Baechle /*
20295cbf6dSRalf Baechle * CPU mask used to set process affinity for MT VPEs/TCs with FPUs
21295cbf6dSRalf Baechle */
22295cbf6dSRalf Baechle cpumask_t mt_fpu_cpumask;
23295cbf6dSRalf Baechle
24295cbf6dSRalf Baechle static int fpaff_threshold = -1;
25982f6ffeSRalf Baechle unsigned long mt_fpemul_threshold;
26295cbf6dSRalf Baechle
27295cbf6dSRalf Baechle /*
28295cbf6dSRalf Baechle * Replacement functions for the sys_sched_setaffinity() and
29295cbf6dSRalf Baechle * sys_sched_getaffinity() system calls, so that we can integrate
30295cbf6dSRalf Baechle * FPU affinity with the user's requested processor affinity.
31295cbf6dSRalf Baechle * This code is 98% identical with the sys_sched_setaffinity()
32295cbf6dSRalf Baechle * and sys_sched_getaffinity() system calls, and should be
330a0fca9dSViresh Kumar * updated when kernel/sched/core.c changes.
34295cbf6dSRalf Baechle */
35295cbf6dSRalf Baechle
36295cbf6dSRalf Baechle /*
37295cbf6dSRalf Baechle * find_process_by_pid - find a process with a matching PID value.
380a0fca9dSViresh Kumar * used in sys_sched_set/getaffinity() in kernel/sched/core.c, so
39295cbf6dSRalf Baechle * cloned here.
40295cbf6dSRalf Baechle */
find_process_by_pid(pid_t pid)41295cbf6dSRalf Baechle static inline struct task_struct *find_process_by_pid(pid_t pid)
42295cbf6dSRalf Baechle {
430e568536SPavel Emelyanov return pid ? find_task_by_vpid(pid) : current;
44295cbf6dSRalf Baechle }
45295cbf6dSRalf Baechle
4617c04139SRalf Baechle /*
4717c04139SRalf Baechle * check the target process has a UID that matches the current process's
4817c04139SRalf Baechle */
check_same_owner(struct task_struct * p)4917c04139SRalf Baechle static bool check_same_owner(struct task_struct *p)
5017c04139SRalf Baechle {
5117c04139SRalf Baechle const struct cred *cred = current_cred(), *pcred;
5217c04139SRalf Baechle bool match;
5317c04139SRalf Baechle
5417c04139SRalf Baechle rcu_read_lock();
5517c04139SRalf Baechle pcred = __task_cred(p);
56b88fb18eSFlorian Fainelli match = (uid_eq(cred->euid, pcred->euid) ||
57b88fb18eSFlorian Fainelli uid_eq(cred->euid, pcred->uid));
5817c04139SRalf Baechle rcu_read_unlock();
5917c04139SRalf Baechle return match;
6017c04139SRalf Baechle }
61295cbf6dSRalf Baechle
62295cbf6dSRalf Baechle /*
63295cbf6dSRalf Baechle * mipsmt_sys_sched_setaffinity - set the cpu affinity of a process
64295cbf6dSRalf Baechle */
mipsmt_sys_sched_setaffinity(pid_t pid,unsigned int len,unsigned long __user * user_mask_ptr)65295cbf6dSRalf Baechle asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
66295cbf6dSRalf Baechle unsigned long __user *user_mask_ptr)
67295cbf6dSRalf Baechle {
6817c04139SRalf Baechle cpumask_var_t cpus_allowed, new_mask, effective_mask;
69293c5bd1SRalf Baechle struct thread_info *ti;
7017c04139SRalf Baechle struct task_struct *p;
7117c04139SRalf Baechle int retval;
72295cbf6dSRalf Baechle
73295cbf6dSRalf Baechle if (len < sizeof(new_mask))
74295cbf6dSRalf Baechle return -EINVAL;
75295cbf6dSRalf Baechle
76295cbf6dSRalf Baechle if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask)))
77295cbf6dSRalf Baechle return -EFAULT;
78295cbf6dSRalf Baechle
79*730d070aSSebastian Andrzej Siewior cpus_read_lock();
8017c04139SRalf Baechle rcu_read_lock();
81295cbf6dSRalf Baechle
82295cbf6dSRalf Baechle p = find_process_by_pid(pid);
83295cbf6dSRalf Baechle if (!p) {
8417c04139SRalf Baechle rcu_read_unlock();
85*730d070aSSebastian Andrzej Siewior cpus_read_unlock();
86295cbf6dSRalf Baechle return -ESRCH;
87295cbf6dSRalf Baechle }
88295cbf6dSRalf Baechle
8917c04139SRalf Baechle /* Prevent p going away */
90295cbf6dSRalf Baechle get_task_struct(p);
9117c04139SRalf Baechle rcu_read_unlock();
92295cbf6dSRalf Baechle
9317c04139SRalf Baechle if (!alloc_cpumask_var(&cpus_allowed, GFP_KERNEL)) {
9417c04139SRalf Baechle retval = -ENOMEM;
9517c04139SRalf Baechle goto out_put_task;
96295cbf6dSRalf Baechle }
9717c04139SRalf Baechle if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) {
9817c04139SRalf Baechle retval = -ENOMEM;
9917c04139SRalf Baechle goto out_free_cpus_allowed;
10017c04139SRalf Baechle }
10117c04139SRalf Baechle if (!alloc_cpumask_var(&effective_mask, GFP_KERNEL)) {
10217c04139SRalf Baechle retval = -ENOMEM;
10317c04139SRalf Baechle goto out_free_new_mask;
10417c04139SRalf Baechle }
105a45526bbSMarkus Elfring if (!check_same_owner(p) && !capable(CAP_SYS_NICE)) {
10617c04139SRalf Baechle retval = -EPERM;
10717c04139SRalf Baechle goto out_unlock;
108a45526bbSMarkus Elfring }
109295cbf6dSRalf Baechle
110a7f505c6SRalf Baechle retval = security_task_setscheduler(p);
111295cbf6dSRalf Baechle if (retval)
112295cbf6dSRalf Baechle goto out_unlock;
113295cbf6dSRalf Baechle
114295cbf6dSRalf Baechle /* Record new user-specified CPU set for future reference */
11517c04139SRalf Baechle cpumask_copy(&p->thread.user_cpus_allowed, new_mask);
116295cbf6dSRalf Baechle
11717c04139SRalf Baechle again:
118295cbf6dSRalf Baechle /* Compute new global allowed CPU set if necessary */
119293c5bd1SRalf Baechle ti = task_thread_info(p);
120293c5bd1SRalf Baechle if (test_ti_thread_flag(ti, TIF_FPUBOUND) &&
1218dd92891SRusty Russell cpumask_intersects(new_mask, &mt_fpu_cpumask)) {
1228dd92891SRusty Russell cpumask_and(effective_mask, new_mask, &mt_fpu_cpumask);
12317c04139SRalf Baechle retval = set_cpus_allowed_ptr(p, effective_mask);
124295cbf6dSRalf Baechle } else {
12517c04139SRalf Baechle cpumask_copy(effective_mask, new_mask);
126293c5bd1SRalf Baechle clear_ti_thread_flag(ti, TIF_FPUBOUND);
12717c04139SRalf Baechle retval = set_cpus_allowed_ptr(p, new_mask);
128295cbf6dSRalf Baechle }
129295cbf6dSRalf Baechle
13017c04139SRalf Baechle if (!retval) {
13117c04139SRalf Baechle cpuset_cpus_allowed(p, cpus_allowed);
13217c04139SRalf Baechle if (!cpumask_subset(effective_mask, cpus_allowed)) {
13317c04139SRalf Baechle /*
13417c04139SRalf Baechle * We must have raced with a concurrent cpuset
13517c04139SRalf Baechle * update. Just reset the cpus_allowed to the
13617c04139SRalf Baechle * cpuset's cpus_allowed
13717c04139SRalf Baechle */
13817c04139SRalf Baechle cpumask_copy(new_mask, cpus_allowed);
13917c04139SRalf Baechle goto again;
14017c04139SRalf Baechle }
14117c04139SRalf Baechle }
142295cbf6dSRalf Baechle out_unlock:
14317c04139SRalf Baechle free_cpumask_var(effective_mask);
14417c04139SRalf Baechle out_free_new_mask:
14517c04139SRalf Baechle free_cpumask_var(new_mask);
14617c04139SRalf Baechle out_free_cpus_allowed:
14717c04139SRalf Baechle free_cpumask_var(cpus_allowed);
14817c04139SRalf Baechle out_put_task:
149295cbf6dSRalf Baechle put_task_struct(p);
150*730d070aSSebastian Andrzej Siewior cpus_read_unlock();
151295cbf6dSRalf Baechle return retval;
152295cbf6dSRalf Baechle }
153295cbf6dSRalf Baechle
154295cbf6dSRalf Baechle /*
155295cbf6dSRalf Baechle * mipsmt_sys_sched_getaffinity - get the cpu affinity of a process
156295cbf6dSRalf Baechle */
mipsmt_sys_sched_getaffinity(pid_t pid,unsigned int len,unsigned long __user * user_mask_ptr)157295cbf6dSRalf Baechle asmlinkage long mipsmt_sys_sched_getaffinity(pid_t pid, unsigned int len,
158295cbf6dSRalf Baechle unsigned long __user *user_mask_ptr)
159295cbf6dSRalf Baechle {
160295cbf6dSRalf Baechle unsigned int real_len;
1611d62d737SFelix Fietkau cpumask_t allowed, mask;
162295cbf6dSRalf Baechle int retval;
163295cbf6dSRalf Baechle struct task_struct *p;
164295cbf6dSRalf Baechle
165295cbf6dSRalf Baechle real_len = sizeof(mask);
166295cbf6dSRalf Baechle if (len < real_len)
167295cbf6dSRalf Baechle return -EINVAL;
168295cbf6dSRalf Baechle
169*730d070aSSebastian Andrzej Siewior cpus_read_lock();
170f0100c7fSDavidlohr Bueso rcu_read_lock();
171295cbf6dSRalf Baechle
172295cbf6dSRalf Baechle retval = -ESRCH;
173295cbf6dSRalf Baechle p = find_process_by_pid(pid);
174295cbf6dSRalf Baechle if (!p)
175295cbf6dSRalf Baechle goto out_unlock;
176295cbf6dSRalf Baechle retval = security_task_getscheduler(p);
177295cbf6dSRalf Baechle if (retval)
178295cbf6dSRalf Baechle goto out_unlock;
179295cbf6dSRalf Baechle
1803bd37062SSebastian Andrzej Siewior cpumask_or(&allowed, &p->thread.user_cpus_allowed, p->cpus_ptr);
1811d62d737SFelix Fietkau cpumask_and(&mask, &allowed, cpu_active_mask);
182295cbf6dSRalf Baechle
183295cbf6dSRalf Baechle out_unlock:
184f0100c7fSDavidlohr Bueso rcu_read_unlock();
185*730d070aSSebastian Andrzej Siewior cpus_read_unlock();
186295cbf6dSRalf Baechle if (retval)
187295cbf6dSRalf Baechle return retval;
188295cbf6dSRalf Baechle if (copy_to_user(user_mask_ptr, &mask, real_len))
189295cbf6dSRalf Baechle return -EFAULT;
190295cbf6dSRalf Baechle return real_len;
191295cbf6dSRalf Baechle }
192295cbf6dSRalf Baechle
193295cbf6dSRalf Baechle
fpaff_thresh(char * str)194295cbf6dSRalf Baechle static int __init fpaff_thresh(char *str)
195295cbf6dSRalf Baechle {
196295cbf6dSRalf Baechle get_option(&str, &fpaff_threshold);
197295cbf6dSRalf Baechle return 1;
198295cbf6dSRalf Baechle }
199295cbf6dSRalf Baechle __setup("fpaff=", fpaff_thresh);
200295cbf6dSRalf Baechle
201295cbf6dSRalf Baechle /*
202295cbf6dSRalf Baechle * FPU Use Factor empirically derived from experiments on 34K
203295cbf6dSRalf Baechle */
2049cc12363SKevin D. Kissell #define FPUSEFACTOR 2000
205295cbf6dSRalf Baechle
mt_fp_affinity_init(void)206295cbf6dSRalf Baechle static __init int mt_fp_affinity_init(void)
207295cbf6dSRalf Baechle {
208295cbf6dSRalf Baechle if (fpaff_threshold >= 0) {
209295cbf6dSRalf Baechle mt_fpemul_threshold = fpaff_threshold;
210295cbf6dSRalf Baechle } else {
211295cbf6dSRalf Baechle mt_fpemul_threshold =
212295cbf6dSRalf Baechle (FPUSEFACTOR * (loops_per_jiffy/(500000/HZ))) / HZ;
213295cbf6dSRalf Baechle }
214295cbf6dSRalf Baechle printk(KERN_DEBUG "FPU Affinity set after %ld emulations\n",
215295cbf6dSRalf Baechle mt_fpemul_threshold);
216295cbf6dSRalf Baechle
217295cbf6dSRalf Baechle return 0;
218295cbf6dSRalf Baechle }
219295cbf6dSRalf Baechle arch_initcall(mt_fp_affinity_init);
220