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