cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ioprio.c (4908B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * fs/ioprio.c
      4 *
      5 * Copyright (C) 2004 Jens Axboe <axboe@kernel.dk>
      6 *
      7 * Helper functions for setting/querying io priorities of processes. The
      8 * system calls closely mimmick getpriority/setpriority, see the man page for
      9 * those. The prio argument is a composite of prio class and prio data, where
     10 * the data argument has meaning within that class. The standard scheduling
     11 * classes have 8 distinct prio levels, with 0 being the highest prio and 7
     12 * being the lowest.
     13 *
     14 * IOW, setting BE scheduling class with prio 2 is done ala:
     15 *
     16 * unsigned int prio = (IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT) | 2;
     17 *
     18 * ioprio_set(PRIO_PROCESS, pid, prio);
     19 *
     20 * See also Documentation/block/ioprio.rst
     21 *
     22 */
     23#include <linux/gfp.h>
     24#include <linux/kernel.h>
     25#include <linux/ioprio.h>
     26#include <linux/cred.h>
     27#include <linux/blkdev.h>
     28#include <linux/capability.h>
     29#include <linux/syscalls.h>
     30#include <linux/security.h>
     31#include <linux/pid_namespace.h>
     32
     33int ioprio_check_cap(int ioprio)
     34{
     35	int class = IOPRIO_PRIO_CLASS(ioprio);
     36	int data = IOPRIO_PRIO_DATA(ioprio);
     37
     38	switch (class) {
     39		case IOPRIO_CLASS_RT:
     40			/*
     41			 * Originally this only checked for CAP_SYS_ADMIN,
     42			 * which was implicitly allowed for pid 0 by security
     43			 * modules such as SELinux. Make sure we check
     44			 * CAP_SYS_ADMIN first to avoid a denial/avc for
     45			 * possibly missing CAP_SYS_NICE permission.
     46			 */
     47			if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_NICE))
     48				return -EPERM;
     49			fallthrough;
     50			/* rt has prio field too */
     51		case IOPRIO_CLASS_BE:
     52			if (data >= IOPRIO_NR_LEVELS || data < 0)
     53				return -EINVAL;
     54			break;
     55		case IOPRIO_CLASS_IDLE:
     56			break;
     57		case IOPRIO_CLASS_NONE:
     58			if (data)
     59				return -EINVAL;
     60			break;
     61		default:
     62			return -EINVAL;
     63	}
     64
     65	return 0;
     66}
     67
     68SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio)
     69{
     70	struct task_struct *p, *g;
     71	struct user_struct *user;
     72	struct pid *pgrp;
     73	kuid_t uid;
     74	int ret;
     75
     76	ret = ioprio_check_cap(ioprio);
     77	if (ret)
     78		return ret;
     79
     80	ret = -ESRCH;
     81	rcu_read_lock();
     82	switch (which) {
     83		case IOPRIO_WHO_PROCESS:
     84			if (!who)
     85				p = current;
     86			else
     87				p = find_task_by_vpid(who);
     88			if (p)
     89				ret = set_task_ioprio(p, ioprio);
     90			break;
     91		case IOPRIO_WHO_PGRP:
     92			if (!who)
     93				pgrp = task_pgrp(current);
     94			else
     95				pgrp = find_vpid(who);
     96
     97			read_lock(&tasklist_lock);
     98			do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {
     99				ret = set_task_ioprio(p, ioprio);
    100				if (ret) {
    101					read_unlock(&tasklist_lock);
    102					goto out;
    103				}
    104			} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
    105			read_unlock(&tasklist_lock);
    106
    107			break;
    108		case IOPRIO_WHO_USER:
    109			uid = make_kuid(current_user_ns(), who);
    110			if (!uid_valid(uid))
    111				break;
    112			if (!who)
    113				user = current_user();
    114			else
    115				user = find_user(uid);
    116
    117			if (!user)
    118				break;
    119
    120			for_each_process_thread(g, p) {
    121				if (!uid_eq(task_uid(p), uid) ||
    122				    !task_pid_vnr(p))
    123					continue;
    124				ret = set_task_ioprio(p, ioprio);
    125				if (ret)
    126					goto free_uid;
    127			}
    128free_uid:
    129			if (who)
    130				free_uid(user);
    131			break;
    132		default:
    133			ret = -EINVAL;
    134	}
    135
    136out:
    137	rcu_read_unlock();
    138	return ret;
    139}
    140
    141static int get_task_ioprio(struct task_struct *p)
    142{
    143	int ret;
    144
    145	ret = security_task_getioprio(p);
    146	if (ret)
    147		goto out;
    148	ret = IOPRIO_DEFAULT;
    149	task_lock(p);
    150	if (p->io_context)
    151		ret = p->io_context->ioprio;
    152	task_unlock(p);
    153out:
    154	return ret;
    155}
    156
    157int ioprio_best(unsigned short aprio, unsigned short bprio)
    158{
    159	if (!ioprio_valid(aprio))
    160		aprio = IOPRIO_DEFAULT;
    161	if (!ioprio_valid(bprio))
    162		bprio = IOPRIO_DEFAULT;
    163
    164	return min(aprio, bprio);
    165}
    166
    167SYSCALL_DEFINE2(ioprio_get, int, which, int, who)
    168{
    169	struct task_struct *g, *p;
    170	struct user_struct *user;
    171	struct pid *pgrp;
    172	kuid_t uid;
    173	int ret = -ESRCH;
    174	int tmpio;
    175
    176	rcu_read_lock();
    177	switch (which) {
    178		case IOPRIO_WHO_PROCESS:
    179			if (!who)
    180				p = current;
    181			else
    182				p = find_task_by_vpid(who);
    183			if (p)
    184				ret = get_task_ioprio(p);
    185			break;
    186		case IOPRIO_WHO_PGRP:
    187			if (!who)
    188				pgrp = task_pgrp(current);
    189			else
    190				pgrp = find_vpid(who);
    191			read_lock(&tasklist_lock);
    192			do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {
    193				tmpio = get_task_ioprio(p);
    194				if (tmpio < 0)
    195					continue;
    196				if (ret == -ESRCH)
    197					ret = tmpio;
    198				else
    199					ret = ioprio_best(ret, tmpio);
    200			} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
    201			read_unlock(&tasklist_lock);
    202
    203			break;
    204		case IOPRIO_WHO_USER:
    205			uid = make_kuid(current_user_ns(), who);
    206			if (!who)
    207				user = current_user();
    208			else
    209				user = find_user(uid);
    210
    211			if (!user)
    212				break;
    213
    214			for_each_process_thread(g, p) {
    215				if (!uid_eq(task_uid(p), user->uid) ||
    216				    !task_pid_vnr(p))
    217					continue;
    218				tmpio = get_task_ioprio(p);
    219				if (tmpio < 0)
    220					continue;
    221				if (ret == -ESRCH)
    222					ret = tmpio;
    223				else
    224					ret = ioprio_best(ret, tmpio);
    225			}
    226
    227			if (who)
    228				free_uid(user);
    229			break;
    230		default:
    231			ret = -EINVAL;
    232	}
    233
    234	rcu_read_unlock();
    235	return ret;
    236}