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.
16 * 31 Mar 2004: Created
21 * Code Description: TBD
24 #include <linux/module.h>
25 #include <linux/init.h>
26 #include <linux/slab.h>
27 #include <asm/errno.h>
28 #include <asm/div64.h>
29 #include <linux/list.h>
30 #include <linux/spinlock.h>
31 #include <linux/ckrm.h>
32 #include <linux/ckrm_rc.h>
33 #include <linux/ckrm_tc.h>
34 #include <linux/ckrm_tsk.h>
36 #define TOTAL_NUM_TASKS (131072) // 128 K
37 #define NUMTASKS_DEBUG
38 #define NUMTASKS_NAME "numtasks"
40 typedef struct ckrm_numtasks {
41 struct ckrm_core_class *core; // the core i am part of...
42 struct ckrm_core_class *parent; // parent of the core above.
43 struct ckrm_shares shares;
44 spinlock_t cnt_lock; // always grab parent's lock before child's
45 int cnt_guarantee; // num_tasks guarantee in local units
46 int cnt_unused; // has to borrow if more than this is needed
47 int cnt_limit; // no tasks over this limit.
48 atomic_t cnt_cur_alloc; // current alloc from self
49 atomic_t cnt_borrowed; // borrowed from the parent
51 int over_guarantee; // turn on/off when cur_alloc goes
52 // over/under guarantee
54 // internally maintained statictics to compare with max numbers
55 int limit_failures; // # failures as request was over the limit
56 int borrow_sucesses; // # successful borrows
57 int borrow_failures; // # borrow failures
59 // Maximum the specific statictics has reached.
60 int max_limit_failures;
61 int max_borrow_sucesses;
62 int max_borrow_failures;
64 // Total number of specific statistics
65 int tot_limit_failures;
66 int tot_borrow_sucesses;
67 int tot_borrow_failures;
70 struct ckrm_res_ctlr numtasks_rcbs;
72 /* Initialize rescls values
73 * May be called on each rcfs unmount or as part of error recovery
74 * to make share values sane.
75 * Does not traverse hierarchy reinitializing children.
77 static void numtasks_res_initcls_one(ckrm_numtasks_t * res)
79 res->shares.my_guarantee = CKRM_SHARE_DONTCARE;
80 res->shares.my_limit = CKRM_SHARE_DONTCARE;
81 res->shares.total_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE;
82 res->shares.max_limit = CKRM_SHARE_DFLT_MAX_LIMIT;
83 res->shares.unused_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE;
84 res->shares.cur_max_limit = 0;
86 res->cnt_guarantee = CKRM_SHARE_DONTCARE;
87 res->cnt_unused = CKRM_SHARE_DONTCARE;
88 res->cnt_limit = CKRM_SHARE_DONTCARE;
90 res->over_guarantee = 0;
92 res->limit_failures = 0;
93 res->borrow_sucesses = 0;
94 res->borrow_failures = 0;
96 res->max_limit_failures = 0;
97 res->max_borrow_sucesses = 0;
98 res->max_borrow_failures = 0;
100 res->tot_limit_failures = 0;
101 res->tot_borrow_sucesses = 0;
102 res->tot_borrow_failures = 0;
104 atomic_set(&res->cnt_cur_alloc, 0);
105 atomic_set(&res->cnt_borrowed, 0);
110 static void numtasks_res_initcls(void *my_res)
112 ckrm_numtasks_t *res = my_res;
114 /* Write a version which propagates values all the way down
115 and replace rcbs callback with that version */
120 static int numtasks_get_ref_local(void *arg, int force)
122 int rc, resid = numtasks_rcbs.resid;
123 ckrm_numtasks_t *res;
124 ckrm_core_class_t *core = arg;
126 if ((resid < 0) || (core == NULL))
129 res = ckrm_get_res_class(core, resid, ckrm_numtasks_t);
133 atomic_inc(&res->cnt_cur_alloc);
136 if (((res->parent) && (res->cnt_unused == CKRM_SHARE_DONTCARE)) ||
137 (atomic_read(&res->cnt_cur_alloc) > res->cnt_unused)) {
140 if (!force && (res->cnt_limit != CKRM_SHARE_DONTCARE) &&
141 (atomic_read(&res->cnt_cur_alloc) > res->cnt_limit)) {
142 res->limit_failures++;
143 res->tot_limit_failures++;
144 } else if (res->parent != NULL) {
146 numtasks_get_ref_local(res->parent, force)) == 1) {
147 atomic_inc(&res->cnt_borrowed);
148 res->borrow_sucesses++;
149 res->tot_borrow_sucesses++;
150 res->over_guarantee = 1;
152 res->borrow_failures++;
153 res->tot_borrow_failures++;
158 } else if (res->over_guarantee) {
159 res->over_guarantee = 0;
161 if (res->max_limit_failures < res->limit_failures) {
162 res->max_limit_failures = res->limit_failures;
164 if (res->max_borrow_sucesses < res->borrow_sucesses) {
165 res->max_borrow_sucesses = res->borrow_sucesses;
167 if (res->max_borrow_failures < res->borrow_failures) {
168 res->max_borrow_failures = res->borrow_failures;
170 res->limit_failures = 0;
171 res->borrow_sucesses = 0;
172 res->borrow_failures = 0;
176 atomic_dec(&res->cnt_cur_alloc);
181 static void numtasks_put_ref_local(void *arg)
183 int resid = numtasks_rcbs.resid;
184 ckrm_numtasks_t *res;
185 ckrm_core_class_t *core = arg;
187 if ((resid == -1) || (core == NULL)) {
191 res = ckrm_get_res_class(core, resid, ckrm_numtasks_t);
194 if (unlikely(atomic_read(&res->cnt_cur_alloc) == 0)) {
195 printk(KERN_WARNING "numtasks_put_ref: Trying to decrement "
196 "counter below 0\n");
199 atomic_dec(&res->cnt_cur_alloc);
200 if (atomic_read(&res->cnt_borrowed) > 0) {
201 atomic_dec(&res->cnt_borrowed);
202 numtasks_put_ref_local(res->parent);
207 static void *numtasks_res_alloc(struct ckrm_core_class *core,
208 struct ckrm_core_class *parent)
210 ckrm_numtasks_t *res;
212 res = kmalloc(sizeof(ckrm_numtasks_t), GFP_ATOMIC);
215 memset(res, 0, sizeof(ckrm_numtasks_t));
217 res->parent = parent;
218 numtasks_res_initcls_one(res);
219 res->cnt_lock = SPIN_LOCK_UNLOCKED;
220 if (parent == NULL) {
221 // I am part of root class. So set the max tasks
222 // to available default
223 res->cnt_guarantee = TOTAL_NUM_TASKS;
224 res->cnt_unused = TOTAL_NUM_TASKS;
225 res->cnt_limit = TOTAL_NUM_TASKS;
227 try_module_get(THIS_MODULE);
230 "numtasks_res_alloc: failed GFP_ATOMIC alloc\n");
236 * No locking of this resource class object necessary as we are not
237 * supposed to be assigned (or used) when/after this function is called.
239 static void numtasks_res_free(void *my_res)
241 ckrm_numtasks_t *res = my_res, *parres, *childres;
242 ckrm_core_class_t *child = NULL;
243 int i, borrowed, maxlimit, resid = numtasks_rcbs.resid;
248 // Assuming there will be no children when this function is called
250 parres = ckrm_get_res_class(res->parent, resid, ckrm_numtasks_t);
252 if (unlikely(atomic_read(&res->cnt_cur_alloc) < 0)) {
253 printk(KERN_WARNING "numtasks_res: counter below 0\n");
255 if (unlikely(atomic_read(&res->cnt_cur_alloc) > 0 ||
256 atomic_read(&res->cnt_borrowed) > 0)) {
257 printk(KERN_WARNING "numtasks_res_free: resource still "
258 "alloc'd %p\n", res);
259 if ((borrowed = atomic_read(&res->cnt_borrowed)) > 0) {
260 for (i = 0; i < borrowed; i++) {
261 numtasks_put_ref_local(parres->core);
265 // return child's limit/guarantee to parent node
266 spin_lock(&parres->cnt_lock);
267 child_guarantee_changed(&parres->shares, res->shares.my_guarantee, 0);
269 // run thru parent's children and get the new max_limit of the parent
270 ckrm_lock_hier(parres->core);
272 while ((child = ckrm_get_next_child(parres->core, child)) != NULL) {
273 childres = ckrm_get_res_class(child, resid, ckrm_numtasks_t);
274 if (maxlimit < childres->shares.my_limit) {
275 maxlimit = childres->shares.my_limit;
278 ckrm_unlock_hier(parres->core);
279 if (parres->shares.cur_max_limit < maxlimit) {
280 parres->shares.cur_max_limit = maxlimit;
283 spin_unlock(&parres->cnt_lock);
285 module_put(THIS_MODULE);
290 * Recalculate the guarantee and limit in real units... and propagate the
292 * Caller is responsible for protecting res and for the integrity of parres
295 recalc_and_propagate(ckrm_numtasks_t * res, ckrm_numtasks_t * parres)
297 ckrm_core_class_t *child = NULL;
298 ckrm_numtasks_t *childres;
299 int resid = numtasks_rcbs.resid;
302 struct ckrm_shares *par = &parres->shares;
303 struct ckrm_shares *self = &res->shares;
305 // calculate cnt_guarantee and cnt_limit
307 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE) {
308 res->cnt_guarantee = CKRM_SHARE_DONTCARE;
309 } else if (par->total_guarantee) {
310 u64 temp = (u64) self->my_guarantee * parres->cnt_guarantee;
311 do_div(temp, par->total_guarantee);
312 res->cnt_guarantee = (int) temp;
314 res->cnt_guarantee = 0;
317 if (parres->cnt_limit == CKRM_SHARE_DONTCARE) {
318 res->cnt_limit = CKRM_SHARE_DONTCARE;
319 } else if (par->max_limit) {
320 u64 temp = (u64) self->my_limit * parres->cnt_limit;
321 do_div(temp, par->max_limit);
322 res->cnt_limit = (int) temp;
327 // Calculate unused units
328 if (res->cnt_guarantee == CKRM_SHARE_DONTCARE) {
329 res->cnt_unused = CKRM_SHARE_DONTCARE;
330 } else if (self->total_guarantee) {
331 u64 temp = (u64) self->unused_guarantee * res->cnt_guarantee;
332 do_div(temp, self->total_guarantee);
333 res->cnt_unused = (int) temp;
338 // propagate to children
339 ckrm_lock_hier(res->core);
340 while ((child = ckrm_get_next_child(res->core, child)) != NULL) {
341 childres = ckrm_get_res_class(child, resid, ckrm_numtasks_t);
343 spin_lock(&childres->cnt_lock);
344 recalc_and_propagate(childres, res);
345 spin_unlock(&childres->cnt_lock);
347 ckrm_unlock_hier(res->core);
351 static int numtasks_set_share_values(void *my_res, struct ckrm_shares *new)
353 ckrm_numtasks_t *parres, *res = my_res;
354 struct ckrm_shares *cur = &res->shares, *par;
355 int rc = -EINVAL, resid = numtasks_rcbs.resid;
362 ckrm_get_res_class(res->parent, resid, ckrm_numtasks_t);
363 spin_lock(&parres->cnt_lock);
364 spin_lock(&res->cnt_lock);
365 par = &parres->shares;
367 spin_lock(&res->cnt_lock);
372 rc = set_shares(new, cur, par);
374 if ((rc == 0) && parres) {
375 // Calculate parent's unused units
376 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE) {
377 parres->cnt_unused = CKRM_SHARE_DONTCARE;
378 } else if (par->total_guarantee) {
379 u64 temp = (u64) par->unused_guarantee * parres->cnt_guarantee;
380 do_div(temp, par->total_guarantee);
381 parres->cnt_unused = (int) temp;
383 parres->cnt_unused = 0;
385 recalc_and_propagate(res, parres);
387 spin_unlock(&res->cnt_lock);
389 spin_unlock(&parres->cnt_lock);
394 static int numtasks_get_share_values(void *my_res, struct ckrm_shares *shares)
396 ckrm_numtasks_t *res = my_res;
400 *shares = res->shares;
404 static int numtasks_get_stats(void *my_res, struct seq_file *sfile)
406 ckrm_numtasks_t *res = my_res;
411 seq_printf(sfile, "Number of tasks resource:\n");
412 seq_printf(sfile, "Total Over limit failures: %d\n",
413 res->tot_limit_failures);
414 seq_printf(sfile, "Total Over guarantee sucesses: %d\n",
415 res->tot_borrow_sucesses);
416 seq_printf(sfile, "Total Over guarantee failures: %d\n",
417 res->tot_borrow_failures);
419 seq_printf(sfile, "Maximum Over limit failures: %d\n",
420 res->max_limit_failures);
421 seq_printf(sfile, "Maximum Over guarantee sucesses: %d\n",
422 res->max_borrow_sucesses);
423 seq_printf(sfile, "Maximum Over guarantee failures: %d\n",
424 res->max_borrow_failures);
425 #ifdef NUMTASKS_DEBUG
427 "cur_alloc %d; borrowed %d; cnt_guar %d; cnt_limit %d "
428 "cnt_unused %d, unused_guarantee %d, cur_max_limit %d\n",
429 atomic_read(&res->cnt_cur_alloc),
430 atomic_read(&res->cnt_borrowed), res->cnt_guarantee,
431 res->cnt_limit, res->cnt_unused,
432 res->shares.unused_guarantee,
433 res->shares.cur_max_limit);
439 static int numtasks_show_config(void *my_res, struct seq_file *sfile)
441 ckrm_numtasks_t *res = my_res;
446 seq_printf(sfile, "res=%s,parameter=somevalue\n", NUMTASKS_NAME);
450 static int numtasks_set_config(void *my_res, const char *cfgstr)
452 ckrm_numtasks_t *res = my_res;
456 printk(KERN_DEBUG "numtasks config='%s'\n", cfgstr);
460 static void numtasks_change_resclass(void *task, void *old, void *new)
462 ckrm_numtasks_t *oldres = old;
463 ckrm_numtasks_t *newres = new;
465 if (oldres != (void *)-1) {
466 struct task_struct *tsk = task;
468 struct ckrm_core_class *old_core =
469 &(tsk->parent->taskclass->core);
471 ckrm_get_res_class(old_core, numtasks_rcbs.resid,
474 numtasks_put_ref_local(oldres->core);
477 (void)numtasks_get_ref_local(newres->core, 1);
481 struct ckrm_res_ctlr numtasks_rcbs = {
482 .res_name = NUMTASKS_NAME,
485 .res_alloc = numtasks_res_alloc,
486 .res_free = numtasks_res_free,
487 .set_share_values = numtasks_set_share_values,
488 .get_share_values = numtasks_get_share_values,
489 .get_stats = numtasks_get_stats,
490 .show_config = numtasks_show_config,
491 .set_config = numtasks_set_config,
492 .change_resclass = numtasks_change_resclass,
495 int __init init_ckrm_numtasks_res(void)
497 struct ckrm_classtype *clstype;
498 int resid = numtasks_rcbs.resid;
500 clstype = ckrm_find_classtype_by_name("taskclass");
501 if (clstype == NULL) {
502 printk(KERN_INFO " Unknown ckrm classtype<taskclass>");
507 resid = ckrm_register_res_ctlr(clstype, &numtasks_rcbs);
508 printk(KERN_DEBUG "........init_ckrm_numtasks_res -> %d\n", resid);
510 ckrm_numtasks_register(numtasks_get_ref_local,
511 numtasks_put_ref_local);
512 numtasks_rcbs.classtype = clstype;
518 void __exit exit_ckrm_numtasks_res(void)
520 if (numtasks_rcbs.resid != -1) {
521 ckrm_numtasks_register(NULL, NULL);
523 ckrm_unregister_res_ctlr(&numtasks_rcbs);
524 numtasks_rcbs.resid = -1;
527 module_init(init_ckrm_numtasks_res)
528 module_exit(exit_ckrm_numtasks_res)
530 MODULE_LICENSE("GPL");