1 /* ckrm_numtasks.c - "Number of tasks" resource controller for CKRM
3 * Copyright (C) Chandra Seetharaman, IBM Corp. 2003
5 * Latest version, more details at http://ckrm.sf.net
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
15 * CKRM Resource controller for tracking number of tasks in a class.
18 #include <linux/module.h>
19 #include <linux/init.h>
20 #include <linux/slab.h>
21 #include <asm/errno.h>
22 #include <asm/div64.h>
23 #include <linux/list.h>
24 #include <linux/spinlock.h>
25 #include <linux/ckrm_rc.h>
26 #include <linux/ckrm_tc.h>
27 #include <linux/ckrm_tsk.h>
29 #define TOTAL_NUM_TASKS (131072) /* 128 K */
30 #define NUMTASKS_DEBUG
31 #define NUMTASKS_NAME "numtasks"
33 struct ckrm_numtasks {
34 struct ckrm_core_class *core; /* the core i am part of... */
35 struct ckrm_core_class *parent; /* parent of the core above. */
36 struct ckrm_shares shares;
37 spinlock_t cnt_lock; /* always grab parent's lock before child's */
38 int cnt_guarantee; /* num_tasks guarantee in local units */
39 int cnt_unused; /* has to borrow if more than this is needed */
40 int cnt_limit; /* no tasks over this limit. */
41 atomic_t cnt_cur_alloc; /* current alloc from self */
42 atomic_t cnt_borrowed; /* borrowed from the parent */
44 int over_guarantee; /* turn on/off when cur_alloc goes */
45 /* over/under guarantee */
47 /* internally maintained statictics to compare with max numbers */
48 int limit_failures; /* # failures as request was over the limit */
49 int borrow_sucesses; /* # successful borrows */
50 int borrow_failures; /* # borrow failures */
52 /* Maximum the specific statictics has reached. */
53 int max_limit_failures;
54 int max_borrow_sucesses;
55 int max_borrow_failures;
57 /* Total number of specific statistics */
58 int tot_limit_failures;
59 int tot_borrow_sucesses;
60 int tot_borrow_failures;
63 struct ckrm_res_ctlr numtasks_rcbs;
65 /* Initialize rescls values
66 * May be called on each rcfs unmount or as part of error recovery
67 * to make share values sane.
68 * Does not traverse hierarchy reinitializing children.
70 static void numtasks_res_initcls_one(struct ckrm_numtasks * res)
72 res->shares.my_guarantee = CKRM_SHARE_DONTCARE;
73 res->shares.my_limit = CKRM_SHARE_DONTCARE;
74 res->shares.total_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE;
75 res->shares.max_limit = CKRM_SHARE_DFLT_MAX_LIMIT;
76 res->shares.unused_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE;
77 res->shares.cur_max_limit = 0;
79 res->cnt_guarantee = CKRM_SHARE_DONTCARE;
80 res->cnt_unused = CKRM_SHARE_DONTCARE;
81 res->cnt_limit = CKRM_SHARE_DONTCARE;
83 res->over_guarantee = 0;
85 res->limit_failures = 0;
86 res->borrow_sucesses = 0;
87 res->borrow_failures = 0;
89 res->max_limit_failures = 0;
90 res->max_borrow_sucesses = 0;
91 res->max_borrow_failures = 0;
93 res->tot_limit_failures = 0;
94 res->tot_borrow_sucesses = 0;
95 res->tot_borrow_failures = 0;
97 atomic_set(&res->cnt_cur_alloc, 0);
98 atomic_set(&res->cnt_borrowed, 0);
102 static int numtasks_get_ref_local(struct ckrm_core_class *core, int force)
104 int rc, resid = numtasks_rcbs.resid;
105 struct ckrm_numtasks *res;
107 if ((resid < 0) || (core == NULL))
110 res = ckrm_get_res_class(core, resid, struct ckrm_numtasks);
114 atomic_inc(&res->cnt_cur_alloc);
117 if (((res->parent) && (res->cnt_unused == CKRM_SHARE_DONTCARE)) ||
118 (atomic_read(&res->cnt_cur_alloc) > res->cnt_unused)) {
121 if (!force && (res->cnt_limit != CKRM_SHARE_DONTCARE) &&
122 (atomic_read(&res->cnt_cur_alloc) > res->cnt_limit)) {
123 res->limit_failures++;
124 res->tot_limit_failures++;
125 } else if (res->parent != NULL) {
127 numtasks_get_ref_local(res->parent, force)) == 1) {
128 atomic_inc(&res->cnt_borrowed);
129 res->borrow_sucesses++;
130 res->tot_borrow_sucesses++;
131 res->over_guarantee = 1;
133 res->borrow_failures++;
134 res->tot_borrow_failures++;
138 } else if (res->over_guarantee) {
139 res->over_guarantee = 0;
141 if (res->max_limit_failures < res->limit_failures)
142 res->max_limit_failures = res->limit_failures;
143 if (res->max_borrow_sucesses < res->borrow_sucesses)
144 res->max_borrow_sucesses = res->borrow_sucesses;
145 if (res->max_borrow_failures < res->borrow_failures)
146 res->max_borrow_failures = res->borrow_failures;
147 res->limit_failures = 0;
148 res->borrow_sucesses = 0;
149 res->borrow_failures = 0;
153 atomic_dec(&res->cnt_cur_alloc);
157 static void numtasks_put_ref_local(struct ckrm_core_class *core)
159 int resid = numtasks_rcbs.resid;
160 struct ckrm_numtasks *res;
162 if ((resid == -1) || (core == NULL))
165 res = ckrm_get_res_class(core, resid, struct ckrm_numtasks);
169 if (atomic_read(&res->cnt_cur_alloc)==0)
172 atomic_dec(&res->cnt_cur_alloc);
174 if (atomic_read(&res->cnt_borrowed) > 0) {
175 atomic_dec(&res->cnt_borrowed);
176 numtasks_put_ref_local(res->parent);
181 static void *numtasks_res_alloc(struct ckrm_core_class *core,
182 struct ckrm_core_class *parent)
184 struct ckrm_numtasks *res;
186 res = kmalloc(sizeof(struct ckrm_numtasks), GFP_ATOMIC);
189 memset(res, 0, sizeof(struct ckrm_numtasks));
191 res->parent = parent;
192 numtasks_res_initcls_one(res);
193 res->cnt_lock = SPIN_LOCK_UNLOCKED;
194 if (parent == NULL) {
196 * I am part of root class. So set the max tasks
197 * to available default.
199 res->cnt_guarantee = TOTAL_NUM_TASKS;
200 res->cnt_unused = TOTAL_NUM_TASKS;
201 res->cnt_limit = TOTAL_NUM_TASKS;
203 try_module_get(THIS_MODULE);
206 "numtasks_res_alloc: failed GFP_ATOMIC alloc\n");
212 * No locking of this resource class object necessary as we are not
213 * supposed to be assigned (or used) when/after this function is called.
215 static void numtasks_res_free(void *my_res)
217 struct ckrm_numtasks *res = my_res, *parres, *childres;
218 struct ckrm_core_class *child = NULL;
219 int i, borrowed, maxlimit, resid = numtasks_rcbs.resid;
224 /* Assuming there will be no children when this function is called */
226 parres = ckrm_get_res_class(res->parent, resid, struct ckrm_numtasks);
228 if ((borrowed = atomic_read(&res->cnt_borrowed)) > 0)
229 for (i = 0; i < borrowed; i++)
230 numtasks_put_ref_local(parres->core);
232 /* return child's limit/guarantee to parent node */
233 spin_lock(&parres->cnt_lock);
234 child_guarantee_changed(&parres->shares, res->shares.my_guarantee, 0);
236 /* run thru parent's children and get the new max_limit of the parent */
237 ckrm_lock_hier(parres->core);
239 while ((child = ckrm_get_next_child(parres->core, child)) != NULL) {
240 childres = ckrm_get_res_class(child, resid, struct ckrm_numtasks);
241 if (maxlimit < childres->shares.my_limit)
242 maxlimit = childres->shares.my_limit;
244 ckrm_unlock_hier(parres->core);
245 if (parres->shares.cur_max_limit < maxlimit)
246 parres->shares.cur_max_limit = maxlimit;
248 spin_unlock(&parres->cnt_lock);
250 module_put(THIS_MODULE);
255 * Recalculate the guarantee and limit in real units... and propagate the
257 * Caller is responsible for protecting res and for the integrity of parres
260 recalc_and_propagate(struct ckrm_numtasks * res, struct ckrm_numtasks * parres)
262 struct ckrm_core_class *child = NULL;
263 struct ckrm_numtasks *childres;
264 int resid = numtasks_rcbs.resid;
267 struct ckrm_shares *par = &parres->shares;
268 struct ckrm_shares *self = &res->shares;
270 /* calculate cnt_guarantee and cnt_limit */
271 if ((parres->cnt_guarantee == CKRM_SHARE_DONTCARE) ||
272 (self->my_guarantee == CKRM_SHARE_DONTCARE))
273 res->cnt_guarantee = CKRM_SHARE_DONTCARE;
274 else if (par->total_guarantee) {
275 u64 temp = (u64) self->my_guarantee * parres->cnt_guarantee;
276 do_div(temp, par->total_guarantee);
277 res->cnt_guarantee = (int) temp;
279 res->cnt_guarantee = 0;
281 if ((parres->cnt_limit == CKRM_SHARE_DONTCARE) ||
282 (self->my_limit == CKRM_SHARE_DONTCARE))
283 res->cnt_limit = CKRM_SHARE_DONTCARE;
284 else if (par->max_limit) {
285 u64 temp = (u64) self->my_limit * parres->cnt_limit;
286 do_div(temp, par->max_limit);
287 res->cnt_limit = (int) temp;
291 /* Calculate unused units */
292 if ((res->cnt_guarantee == CKRM_SHARE_DONTCARE) ||
293 (self->my_guarantee == CKRM_SHARE_DONTCARE))
294 res->cnt_unused = CKRM_SHARE_DONTCARE;
295 else if (self->total_guarantee) {
296 u64 temp = (u64) self->unused_guarantee * res->cnt_guarantee;
297 do_div(temp, self->total_guarantee);
298 res->cnt_unused = (int) temp;
303 /* propagate to children */
304 ckrm_lock_hier(res->core);
305 while ((child = ckrm_get_next_child(res->core, child)) != NULL) {
306 childres = ckrm_get_res_class(child, resid, struct ckrm_numtasks);
308 spin_lock(&childres->cnt_lock);
309 recalc_and_propagate(childres, res);
310 spin_unlock(&childres->cnt_lock);
312 ckrm_unlock_hier(res->core);
316 static int numtasks_set_share_values(void *my_res, struct ckrm_shares *new)
318 struct ckrm_numtasks *parres, *res = my_res;
319 struct ckrm_shares *cur = &res->shares, *par;
320 int rc = -EINVAL, resid = numtasks_rcbs.resid;
327 ckrm_get_res_class(res->parent, resid, struct ckrm_numtasks);
328 spin_lock(&parres->cnt_lock);
329 spin_lock(&res->cnt_lock);
330 par = &parres->shares;
332 spin_lock(&res->cnt_lock);
337 rc = set_shares(new, cur, par);
339 if ((rc == 0) && parres) {
340 /* Calculate parent's unused units */
341 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE)
342 parres->cnt_unused = CKRM_SHARE_DONTCARE;
343 else if (par->total_guarantee) {
344 u64 temp = (u64) par->unused_guarantee * parres->cnt_guarantee;
345 do_div(temp, par->total_guarantee);
346 parres->cnt_unused = (int) temp;
348 parres->cnt_unused = 0;
349 recalc_and_propagate(res, parres);
351 spin_unlock(&res->cnt_lock);
353 spin_unlock(&parres->cnt_lock);
357 static int numtasks_get_share_values(void *my_res, struct ckrm_shares *shares)
359 struct ckrm_numtasks *res = my_res;
363 *shares = res->shares;
367 static int numtasks_get_stats(void *my_res, struct seq_file *sfile)
369 struct ckrm_numtasks *res = my_res;
374 seq_printf(sfile, "---------Number of tasks stats start---------\n");
375 seq_printf(sfile, "Total Over limit failures: %d\n",
376 res->tot_limit_failures);
377 seq_printf(sfile, "Total Over guarantee sucesses: %d\n",
378 res->tot_borrow_sucesses);
379 seq_printf(sfile, "Total Over guarantee failures: %d\n",
380 res->tot_borrow_failures);
382 seq_printf(sfile, "Maximum Over limit failures: %d\n",
383 res->max_limit_failures);
384 seq_printf(sfile, "Maximum Over guarantee sucesses: %d\n",
385 res->max_borrow_sucesses);
386 seq_printf(sfile, "Maximum Over guarantee failures: %d\n",
387 res->max_borrow_failures);
388 seq_printf(sfile, "---------Number of tasks stats end---------\n");
389 #ifdef NUMTASKS_DEBUG
391 "cur_alloc %d; borrowed %d; cnt_guar %d; cnt_limit %d "
392 "cnt_unused %d, unused_guarantee %d, cur_max_limit %d\n",
393 atomic_read(&res->cnt_cur_alloc),
394 atomic_read(&res->cnt_borrowed), res->cnt_guarantee,
395 res->cnt_limit, res->cnt_unused,
396 res->shares.unused_guarantee,
397 res->shares.cur_max_limit);
403 static int numtasks_show_config(void *my_res, struct seq_file *sfile)
405 struct ckrm_numtasks *res = my_res;
410 seq_printf(sfile, "res=%s,parameter=somevalue\n", NUMTASKS_NAME);
414 static int numtasks_set_config(void *my_res, const char *cfgstr)
416 struct ckrm_numtasks *res = my_res;
420 printk("numtasks config='%s'\n", cfgstr);
424 static void numtasks_change_resclass(void *task, void *old, void *new)
426 struct ckrm_numtasks *oldres = old;
427 struct ckrm_numtasks *newres = new;
429 if (oldres != (void *)-1) {
430 struct task_struct *tsk = task;
432 struct ckrm_core_class *old_core =
433 &(tsk->parent->taskclass->core);
435 ckrm_get_res_class(old_core, numtasks_rcbs.resid,
436 struct ckrm_numtasks);
439 numtasks_put_ref_local(oldres->core);
442 (void)numtasks_get_ref_local(newres->core, 1);
445 struct ckrm_res_ctlr numtasks_rcbs = {
446 .res_name = NUMTASKS_NAME,
449 .res_alloc = numtasks_res_alloc,
450 .res_free = numtasks_res_free,
451 .set_share_values = numtasks_set_share_values,
452 .get_share_values = numtasks_get_share_values,
453 .get_stats = numtasks_get_stats,
454 .show_config = numtasks_show_config,
455 .set_config = numtasks_set_config,
456 .change_resclass = numtasks_change_resclass,
459 int __init init_ckrm_numtasks_res(void)
461 struct ckrm_classtype *clstype;
462 int resid = numtasks_rcbs.resid;
464 clstype = ckrm_find_classtype_by_name("taskclass");
465 if (clstype == NULL) {
466 printk(KERN_INFO " Unknown ckrm classtype<taskclass>");
471 resid = ckrm_register_res_ctlr(clstype, &numtasks_rcbs);
472 printk("........init_ckrm_numtasks_res -> %d\n", resid);
474 ckrm_numtasks_register(numtasks_get_ref_local,
475 numtasks_put_ref_local);
476 numtasks_rcbs.classtype = clstype;
482 void __exit exit_ckrm_numtasks_res(void)
484 if (numtasks_rcbs.resid != -1)
485 ckrm_numtasks_register(NULL, NULL);
486 ckrm_unregister_res_ctlr(&numtasks_rcbs);
487 numtasks_rcbs.resid = -1;
490 module_init(init_ckrm_numtasks_res)
491 module_exit(exit_ckrm_numtasks_res)
493 MODULE_LICENSE("GPL");