mt_sched.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. #include <linux/cdev.h>
  2. #include <linux/device.h>
  3. #include <linux/seq_file.h>
  4. #include <linux/cpu.h>
  5. #include <linux/security.h>
  6. #include <linux/cpuset.h>
  7. #include <linux/poll.h>
  8. #include <linux/proc_fs.h>
  9. #include <linux/module.h>
  10. #include <linux/version.h>
  11. #include "mt_sched_drv.h"
  12. #define SCHED_DEV_NAME "sched"
  13. struct mt_task {
  14. pid_t pid;
  15. struct task_struct *p;
  16. struct cpumask mask;
  17. struct list_head list;
  18. };
  19. static struct mt_task mt_task_head;
  20. static DEFINE_SPINLOCK(mt_sched_spinlock);
  21. static int get_user_cpu_mask(unsigned long __user *user_mask_ptr, unsigned len,
  22. struct cpumask *new_mask)
  23. {
  24. if (len < cpumask_size())
  25. cpumask_clear(new_mask);
  26. else if (len > cpumask_size())
  27. len = cpumask_size();
  28. return copy_from_user(new_mask, user_mask_ptr, len) ? -EFAULT : 0;
  29. }
  30. /**
  31. * find_process_by_pid - find a process with a matching PID value.
  32. * @pid: the pid in question.
  33. */
  34. static struct task_struct *find_process_by_pid(pid_t pid)
  35. {
  36. return pid ? find_task_by_vpid(pid) : current;
  37. }
  38. /*
  39. * check the target process has a UID that matches the current process's
  40. */
  41. static bool check_same_owner(struct task_struct *p)
  42. {
  43. const struct cred *cred = current_cred(), *pcred;
  44. bool match;
  45. rcu_read_lock();
  46. pcred = __task_cred(p);
  47. match = (uid_eq(cred->euid, pcred->euid) || uid_eq(cred->euid, pcred->uid));
  48. rcu_read_unlock();
  49. return match;
  50. }
  51. /*
  52. * check the task link list. If the task is exit, delete the task.
  53. */
  54. static void mt_sched_check_tasks(void)
  55. {
  56. struct mt_task *tmp, *tmp2;
  57. unsigned long irq_flags;
  58. spin_lock_irqsave(&mt_sched_spinlock, irq_flags);
  59. list_for_each_entry_safe(tmp, tmp2, &mt_task_head.list, list) {
  60. if (tmp->pid != tmp->p->pid) {
  61. list_del(&(tmp->list));
  62. kfree(tmp);
  63. }
  64. }
  65. spin_unlock_irqrestore(&mt_sched_spinlock, irq_flags);
  66. }
  67. static long __mt_sched_addaffinity(struct task_struct *p, const struct cpumask *new_mask)
  68. {
  69. #if MT_SCHED_AFFININTY_DEBUG
  70. pr_debug("MT_SCHED: %s pid[%d]\n", __func__, p->pid);
  71. #endif
  72. struct mt_task *new;
  73. struct mt_task *tmp, *tmp2;
  74. unsigned long irq_flags;
  75. int find = 0;
  76. new = kmalloc(sizeof(struct mt_task), GFP_KERNEL);
  77. if (!new)
  78. return -ENOMEM;
  79. INIT_LIST_HEAD(&(new->list));
  80. new->pid = p->pid;
  81. new->p = p;
  82. cpumask_copy(&new->mask, new_mask);
  83. spin_lock_irqsave(&mt_sched_spinlock, irq_flags);
  84. list_for_each_entry_safe(tmp, tmp2, &mt_task_head.list, list) {
  85. if (tmp->pid != tmp->p->pid) {
  86. list_del(&(tmp->list));
  87. kfree(tmp);
  88. continue;
  89. }
  90. if (!find && (tmp->p == p)) {
  91. find = 1;
  92. cpumask_copy(&tmp->mask, new_mask);
  93. }
  94. }
  95. if (!find)
  96. list_add(&(new->list), &(mt_task_head.list));
  97. spin_unlock_irqrestore(&mt_sched_spinlock, irq_flags);
  98. if (find)
  99. kfree(new);
  100. return 0;
  101. }
  102. static long __mt_sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
  103. {
  104. cpumask_var_t cpus_allowed, new_mask;
  105. struct task_struct *p;
  106. int retval;
  107. #if MT_SCHED_AFFININTY_DEBUG
  108. pr_debug("MT_SCHED: %s pid[%d]\n", __func__, pid);
  109. #endif
  110. rcu_read_lock();
  111. p = find_process_by_pid(pid);
  112. if (!p) {
  113. rcu_read_unlock();
  114. pr_debug("MT_SCHED: setaffinity find process %d fail\n", pid);
  115. return -ESRCH;
  116. }
  117. /* Prevent p going away */
  118. get_task_struct(p);
  119. rcu_read_unlock();
  120. if (p->flags & PF_NO_SETAFFINITY) {
  121. retval = -EINVAL;
  122. pr_debug("MT_SCHED: setaffinity flags PF_NO_SETAFFINITY fail\n");
  123. goto out_put_task;
  124. }
  125. if (!alloc_cpumask_var(&cpus_allowed, GFP_KERNEL)) {
  126. retval = -ENOMEM;
  127. pr_debug("MT_SCHED: setaffinity allo_cpumask_var for cpus_allowed fail\n");
  128. goto out_put_task;
  129. }
  130. if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) {
  131. retval = -ENOMEM;
  132. pr_debug("MT_SCHED: setaffinity allo_cpumask_var for new_mask fail\n");
  133. goto out_free_cpus_allowed;
  134. }
  135. retval = -EPERM;
  136. if (!check_same_owner(p)) {
  137. rcu_read_lock();
  138. if (!ns_capable(__task_cred(p)->user_ns, CAP_SYS_NICE)) {
  139. rcu_read_unlock();
  140. pr_debug("MT_SCHED: setaffinity check_same_owner and task_ns_capable fail\n");
  141. goto out_free_new_mask;
  142. }
  143. rcu_read_unlock();
  144. }
  145. retval = security_task_setscheduler(p);
  146. if (retval) {
  147. pr_debug("MT_SCHED: setaffinity security_task_setscheduler fail, status: %d\n",
  148. retval);
  149. goto out_free_new_mask;
  150. }
  151. cpuset_cpus_allowed(p, cpus_allowed);
  152. cpumask_and(new_mask, in_mask, cpus_allowed);
  153. /*
  154. * Since bandwidth control happens on root_domain basis,
  155. * if admission test is enabled, we only admit -deadline
  156. * tasks allowed to run on all the CPUs in the task's
  157. * root_domain.
  158. */
  159. #ifdef CONFIG_SMP
  160. if (task_has_dl_policy(p) && dl_bandwidth_enabled()) {
  161. rcu_read_lock();
  162. if (!cpumask_subset(task_rq(p)->rd->span, new_mask)) {
  163. retval = -EBUSY;
  164. rcu_read_unlock();
  165. pr_debug("MT_SCHED: setaffinity deadline tasks fail, status: %d\n", retval);
  166. goto out_free_new_mask;
  167. }
  168. rcu_read_unlock();
  169. }
  170. #endif
  171. again:
  172. retval = set_cpus_allowed_ptr(p, new_mask);
  173. if (retval)
  174. pr_debug("MT_SCHED: set_cpus_allowed_ptr status %d\n", retval);
  175. if (!retval) {
  176. cpuset_cpus_allowed(p, cpus_allowed);
  177. if (!cpumask_subset(new_mask, cpus_allowed)) {
  178. /*
  179. * We must have raced with a concurrent cpuset
  180. * update. Just reset the cpus_allowed to the
  181. * cpuset's cpus_allowed
  182. */
  183. cpumask_copy(new_mask, cpus_allowed);
  184. goto again;
  185. }
  186. }
  187. /* modify for the mt_sched_setaffinity */
  188. if ((!retval) || (!cpumask_intersects(new_mask, cpu_active_mask)))
  189. retval = __mt_sched_addaffinity(p, new_mask);
  190. out_free_new_mask:
  191. free_cpumask_var(new_mask);
  192. out_free_cpus_allowed:
  193. free_cpumask_var(cpus_allowed);
  194. out_put_task:
  195. put_task_struct(p);
  196. if (retval)
  197. pr_debug("MT_SCHED: setaffinity status %d\n", retval);
  198. return retval;
  199. }
  200. static long __mt_sched_getaffinity(pid_t pid, struct cpumask *mask, struct cpumask *mt_mask)
  201. {
  202. struct task_struct *p;
  203. struct mt_task *tmp;
  204. unsigned long irq_flags;
  205. unsigned long flags;
  206. int retval;
  207. rcu_read_lock();
  208. #if MT_SCHED_AFFININTY_DEBUG
  209. pr_debug("MT_SCHED: %s pid[%d]\n", __func__, pid);
  210. #endif
  211. retval = -ESRCH;
  212. p = find_process_by_pid(pid);
  213. if (!p) {
  214. pr_debug("MT_SCHED: getaffinity find process %d fail\n", pid);
  215. goto out_unlock;
  216. }
  217. retval = security_task_getscheduler(p);
  218. if (retval) {
  219. pr_debug("MT_SCHED: getaffinity security_task_getscheduler fail, status: %d\n",
  220. retval);
  221. goto out_unlock;
  222. }
  223. raw_spin_lock_irqsave(&p->pi_lock, flags);
  224. cpumask_and(mask, &p->cpus_allowed, cpu_active_mask);
  225. raw_spin_unlock_irqrestore(&p->pi_lock, flags);
  226. /* add for the mt_mask */
  227. cpumask_clear(mt_mask);
  228. spin_lock_irqsave(&mt_sched_spinlock, irq_flags);
  229. list_for_each_entry(tmp, &mt_task_head.list, list) {
  230. if ((p == tmp->p) && (p->pid == tmp->pid)) {
  231. cpumask_copy(mt_mask, &tmp->mask);
  232. break;
  233. }
  234. }
  235. spin_unlock_irqrestore(&mt_sched_spinlock, irq_flags);
  236. out_unlock:
  237. rcu_read_unlock();
  238. if (retval)
  239. pr_debug("MT_SCHED: getaffinity status %d\n", retval);
  240. return retval;
  241. }
  242. static long __mt_sched_exitaffinity(pid_t pid)
  243. {
  244. struct mt_task *tmp, *tmp2;
  245. unsigned long irq_flags;
  246. int find = 0;
  247. if (0 == pid)
  248. pid = current->pid;
  249. #if MT_SCHED_AFFININTY_DEBUG
  250. pr_debug("MT_SCHED: %s pid[%d]\n", __func__, pid);
  251. #endif
  252. spin_lock_irqsave(&mt_sched_spinlock, irq_flags);
  253. list_for_each_entry_safe(tmp, tmp2, &mt_task_head.list, list) {
  254. if (pid == tmp->pid) {
  255. list_del(&(tmp->list));
  256. kfree(tmp);
  257. find = 1;
  258. break;
  259. }
  260. }
  261. spin_unlock_irqrestore(&mt_sched_spinlock, irq_flags);
  262. if (!find) {
  263. pr_debug("MT_SCHED: exit affinity find process %d fail.\n", pid);
  264. return -ESRCH;
  265. }
  266. return 0;
  267. }
  268. static long sched_ioctl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  269. {
  270. int retval;
  271. pid_t pid;
  272. struct ioctl_arg data;
  273. cpumask_var_t new_mask, mask, mt_mask;
  274. int len;
  275. memset(&data, 0, sizeof(data));
  276. switch (cmd) {
  277. case IOCTL_SETAFFINITY:
  278. if (copy_from_user(&data, (int __user *)arg, sizeof(data))) {
  279. retval = -EFAULT;
  280. goto done;
  281. }
  282. #if MT_SCHED_AFFININTY_DEBUG
  283. pr_debug("MT_SCHED: %s [IOCTL_SETAFFINITY]\n", __func__);
  284. #endif
  285. if (!alloc_cpumask_var(&new_mask, GFP_KERNEL))
  286. return -ENOMEM;
  287. retval = get_user_cpu_mask(data.mask, data.len, new_mask);
  288. if (retval == 0)
  289. retval = __mt_sched_setaffinity(data.pid, new_mask);
  290. if (retval)
  291. pr_debug("MT_SCHED: setaffinity status %d\n", retval);
  292. free_cpumask_var(new_mask);
  293. break;
  294. case IOCTL_GETAFFINITY:
  295. if (copy_from_user(&data, (int __user *)arg, sizeof(data))) {
  296. retval = -EFAULT;
  297. goto done;
  298. }
  299. #if MT_SCHED_AFFININTY_DEBUG
  300. pr_debug("MT_SCHED: %s [IOCTL_GETAFFINITY]\n", __func__);
  301. #endif
  302. len = data.len;
  303. if ((len * BITS_PER_BYTE) < nr_cpu_ids)
  304. return -EINVAL;
  305. if (len & (sizeof(unsigned int) - 1))
  306. return -EINVAL;
  307. if (!alloc_cpumask_var(&mask, GFP_KERNEL))
  308. return -ENOMEM;
  309. if (!alloc_cpumask_var(&mt_mask, GFP_KERNEL)) {
  310. goto getaffinity_free1;
  311. return -ENOMEM;
  312. }
  313. retval = __mt_sched_getaffinity(data.pid, mask, mt_mask);
  314. if (retval == 0) {
  315. size_t retlen = min_t(size_t, len, cpumask_size());
  316. if (copy_to_user((int __user *)data.mask, mask, retlen)) {
  317. retval = -EFAULT;
  318. goto getaffinity_free;
  319. } else {
  320. retval = retlen;
  321. }
  322. if (copy_to_user((int __user *)data.mt_mask, mt_mask, retlen))
  323. retval = -EFAULT;
  324. else
  325. retval = retlen;
  326. }
  327. getaffinity_free:
  328. free_cpumask_var(mt_mask);
  329. getaffinity_free1:
  330. free_cpumask_var(mask);
  331. break;
  332. case IOCTL_EXITAFFINITY:
  333. #if MT_SCHED_AFFININTY_DEBUG
  334. pr_debug("MT_SCHED: %s [IOCTL_EXITAFFINITY]\n", __func__);
  335. #endif
  336. if (copy_from_user(&pid, (int __user *)arg, sizeof(pid))) {
  337. retval = -EFAULT;
  338. goto done;
  339. }
  340. retval = __mt_sched_exitaffinity(pid);
  341. break;
  342. default:
  343. retval = -ENOTTY;
  344. }
  345. done:
  346. return retval;
  347. }
  348. const struct file_operations sched_ioctl_fops = {
  349. .owner = THIS_MODULE,
  350. .unlocked_ioctl = sched_ioctl_ioctl,
  351. #ifdef CONFIG_COMPAT
  352. .compat_ioctl = sched_ioctl_compat,
  353. #endif
  354. };
  355. static struct cdev *sched_ioctl_cdev;
  356. static dev_t sched_dev_num;
  357. struct class *sched_class;
  358. static int __init sched_ioctl_init(void)
  359. {
  360. int ret;
  361. struct device *class_dev = NULL;
  362. if (alloc_chrdev_region(&sched_dev_num, 0, 1, SCHED_DEV_NAME)) {
  363. pr_debug("MT_SCHED: Device major number allocation failed\n");
  364. return -EAGAIN;
  365. }
  366. sched_ioctl_cdev = cdev_alloc();
  367. if (NULL == sched_ioctl_cdev) {
  368. pr_debug("MT_SCHED: cdev_alloc failed\n");
  369. ret = -1;
  370. goto out_err2;
  371. }
  372. cdev_init(sched_ioctl_cdev, &sched_ioctl_fops);
  373. sched_ioctl_cdev->owner = THIS_MODULE;
  374. ret = cdev_add(sched_ioctl_cdev, sched_dev_num, 1);
  375. if (ret) {
  376. pr_debug("MT_SCHED: Char device add failed\n");
  377. goto out_err2;
  378. }
  379. sched_class = class_create(THIS_MODULE, "scheddrv");
  380. if (IS_ERR(sched_class)) {
  381. pr_debug("Unable to create class, err = %d\n", (int)PTR_ERR(sched_class));
  382. goto out_err1;
  383. }
  384. class_dev = device_create(sched_class, NULL, sched_dev_num, NULL, "mtk_sched");
  385. pr_alert("MT_SCHED: Init complete, device major number = %d\n", MAJOR(sched_dev_num));
  386. goto out;
  387. class_destroy(sched_class);
  388. out_err1:
  389. cdev_del(sched_ioctl_cdev);
  390. out_err2:
  391. unregister_chrdev_region(sched_dev_num, 1);
  392. out:
  393. return ret;
  394. }
  395. /**
  396. * /proc/mtk_sched/affinity_status
  397. */
  398. static int sched_status_show(struct seq_file *seq, void *v);
  399. static int sched_status_open(struct inode *inode, struct file *file);
  400. static unsigned int sched_status_poll(struct file *file, poll_table *wait);
  401. static const struct file_operations sched_status_fops = {
  402. .open = sched_status_open,
  403. .read = seq_read,
  404. .poll = sched_status_poll,
  405. .llseek = seq_lseek,
  406. .release = single_release
  407. };
  408. static int sched_status_open(struct inode *inode, struct file *file)
  409. {
  410. return single_open(file, sched_status_show, inode->i_private);
  411. }
  412. static int sched_status_show(struct seq_file *seq, void *v)
  413. {
  414. struct mt_task *tmp;
  415. struct cpumask mask;
  416. unsigned long irq_flags;
  417. int i;
  418. for_each_online_cpu(i)
  419. seq_printf(seq, "CPU%d:\t\tonline\n", i);
  420. mt_sched_check_tasks();
  421. spin_lock_irqsave(&mt_sched_spinlock, irq_flags);
  422. seq_puts(seq, "\n PID REAL BACKUP CMD\n");
  423. list_for_each_entry(tmp, &mt_task_head.list, list) {
  424. cpumask_and(&mask, &tmp->p->cpus_allowed, cpu_online_mask);
  425. seq_printf(seq, "%5d %4lu %4lu %s\n", tmp->pid, *mask.bits, *tmp->mask.bits,
  426. tmp->p->comm);
  427. }
  428. spin_unlock_irqrestore(&mt_sched_spinlock, irq_flags);
  429. return 0;
  430. }
  431. static unsigned int sched_status_poll(struct file *file, poll_table *wait)
  432. {
  433. return 0;
  434. }
  435. static int __init sched_proc_init(void)
  436. {
  437. struct proc_dir_entry *pe;
  438. if (!proc_mkdir("mtk_sched", NULL))
  439. return -1;
  440. pe = proc_create("mtk_sched/affinity_status", 0444, NULL, &sched_status_fops);
  441. if (!pe)
  442. return -ENOMEM;
  443. return 0;
  444. }
  445. /**
  446. * sched_cpu_notify - sched cpu notifer callback function.
  447. */
  448. static int sched_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
  449. {
  450. long cpu = (long)hcpu;
  451. struct mt_task *tmp, *tmp2;
  452. unsigned long irq_flags;
  453. struct task_struct *p;
  454. switch (action) {
  455. case CPU_ONLINE:
  456. spin_lock_irqsave(&mt_sched_spinlock, irq_flags);
  457. list_for_each_entry_safe(tmp, tmp2, &mt_task_head.list, list) {
  458. if (cpumask_test_cpu(cpu, &(tmp->mask))) {
  459. p = find_process_by_pid(tmp->pid);
  460. if (p) {
  461. if (p == tmp->p)
  462. cpumask_copy(&tmp->p->cpus_allowed, &(tmp->mask));
  463. else{
  464. list_del(&(tmp->list));
  465. kfree(tmp);
  466. }
  467. } else {
  468. list_del(&(tmp->list));
  469. kfree(tmp);
  470. }
  471. }
  472. }
  473. spin_unlock_irqrestore(&mt_sched_spinlock, irq_flags);
  474. break;
  475. case CPU_DOWN_FAILED:
  476. break;
  477. case CPU_DOWN_PREPARE:
  478. break;
  479. default:
  480. break;
  481. }
  482. return NOTIFY_OK;
  483. }
  484. static struct notifier_block sched_cpu_nb = {
  485. .notifier_call = sched_cpu_notify,
  486. };
  487. static int __init sched_module_init(void)
  488. {
  489. int ret;
  490. unsigned long irq_flags;
  491. ret = sched_ioctl_init();
  492. if (ret)
  493. return ret;
  494. ret = sched_proc_init();
  495. if (ret)
  496. return ret;
  497. ret = register_cpu_notifier(&sched_cpu_nb);
  498. if (ret)
  499. return ret;
  500. spin_lock_irqsave(&mt_sched_spinlock, irq_flags);
  501. INIT_LIST_HEAD(&(mt_task_head.list));
  502. spin_unlock_irqrestore(&mt_sched_spinlock, irq_flags);
  503. return ret;
  504. }
  505. static void sched_module_exit(void)
  506. {
  507. class_destroy(sched_class);
  508. cdev_del(sched_ioctl_cdev);
  509. unregister_chrdev_region(sched_dev_num, 1);
  510. pr_alert("MT_SCHED: driver removed.\n");
  511. }
  512. module_init(sched_module_init);
  513. module_exit(sched_module_exit);
  514. MODULE_LICENSE("GPL");
  515. MODULE_AUTHOR("YaTing Chang <yt.chang@mediatek.com>");
  516. MODULE_DESCRIPTION("This is sched module.");