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/config.h>
25 #include <linux/module.h>
26 #include <linux/init.h>
27 #include <linux/slab.h>
28 #include <asm/errno.h>
29 #include <asm/div64.h>
30 #include <linux/list.h>
31 #include <linux/spinlock.h>
32 #include <linux/parser.h>
33 #include <linux/ckrm_rc.h>
34 #include <linux/ckrm_tc.h>
35 #include <linux/ckrm_tsk.h>
37 #define DEF_TOTAL_NUM_TASKS (131072) // 128 K
38 #define DEF_FORKRATE (1000000) // 1 million tasks
39 #define DEF_FORKRATE_INTERVAL (3600) // per hour
40 #define NUMTASKS_DEBUG
41 #define NUMTASKS_NAME "numtasks"
42 #define SYS_TOTAL_TASKS "sys_total_tasks"
43 #define FORKRATE "forkrate"
44 #define FORKRATE_INTERVAL "forkrate_interval"
46 static int total_numtasks = DEF_TOTAL_NUM_TASKS;
47 static int total_cnt_alloc = 0;
48 static int forkrate = DEF_FORKRATE;
49 static int forkrate_interval = DEF_FORKRATE_INTERVAL;
50 static ckrm_core_class_t *root_core;
52 typedef struct ckrm_numtasks {
53 struct ckrm_core_class *core; // the core i am part of...
54 struct ckrm_core_class *parent; // parent of the core above.
55 struct ckrm_shares shares;
56 spinlock_t cnt_lock; // always grab parent's lock before child's
57 int cnt_guarantee; // num_tasks guarantee in local units
58 int cnt_unused; // has to borrow if more than this is needed
59 int cnt_limit; // no tasks over this limit.
60 atomic_t cnt_cur_alloc; // current alloc from self
61 atomic_t cnt_borrowed; // borrowed from the parent
63 int over_guarantee; // turn on/off when cur_alloc goes
64 // over/under guarantee
66 // internally maintained statictics to compare with max numbers
67 int limit_failures; // # failures as request was over the limit
68 int borrow_sucesses; // # successful borrows
69 int borrow_failures; // # borrow failures
71 // Maximum the specific statictics has reached.
72 int max_limit_failures;
73 int max_borrow_sucesses;
74 int max_borrow_failures;
76 // Total number of specific statistics
77 int tot_limit_failures;
78 int tot_borrow_sucesses;
79 int tot_borrow_failures;
83 unsigned long period_start;
86 struct ckrm_res_ctlr numtasks_rcbs;
88 /* Initialize rescls values
89 * May be called on each rcfs unmount or as part of error recovery
90 * to make share values sane.
91 * Does not traverse hierarchy reinitializing children.
93 static void numtasks_res_initcls_one(ckrm_numtasks_t * res)
95 res->shares.my_guarantee = CKRM_SHARE_DONTCARE;
96 res->shares.my_limit = CKRM_SHARE_DONTCARE;
97 res->shares.total_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE;
98 res->shares.max_limit = CKRM_SHARE_DFLT_MAX_LIMIT;
99 res->shares.unused_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE;
100 res->shares.cur_max_limit = 0;
102 res->cnt_guarantee = CKRM_SHARE_DONTCARE;
103 res->cnt_unused = CKRM_SHARE_DONTCARE;
104 res->cnt_limit = CKRM_SHARE_DONTCARE;
106 res->over_guarantee = 0;
108 res->limit_failures = 0;
109 res->borrow_sucesses = 0;
110 res->borrow_failures = 0;
112 res->max_limit_failures = 0;
113 res->max_borrow_sucesses = 0;
114 res->max_borrow_failures = 0;
116 res->tot_limit_failures = 0;
117 res->tot_borrow_sucesses = 0;
118 res->tot_borrow_failures = 0;
120 res->forks_in_period = 0;
121 res->period_start = jiffies;
123 atomic_set(&res->cnt_cur_alloc, 0);
124 atomic_set(&res->cnt_borrowed, 0);
129 static void numtasks_res_initcls(void *my_res)
131 ckrm_numtasks_t *res = my_res;
133 /* Write a version which propagates values all the way down
134 and replace rcbs callback with that version */
139 static int numtasks_get_ref_local(void *arg, int force)
141 int rc, resid = numtasks_rcbs.resid, borrowed = 0;
142 unsigned long now = jiffies, chg_at;
143 ckrm_numtasks_t *res;
144 ckrm_core_class_t *core = arg;
146 if ((resid < 0) || (core == NULL))
149 res = ckrm_get_res_class(core, resid, ckrm_numtasks_t);
153 #ifdef CONFIG_CKRM_RES_NUMTASKS_FORKRATE
154 // force is not associated with fork. So, if force is specified
155 // we don't have to bother about forkrate.
157 // Take care of wraparound situation
158 chg_at = res->period_start + forkrate_interval * HZ;
159 if (chg_at < res->period_start) {
160 chg_at += forkrate_interval * HZ;
161 now += forkrate_interval * HZ;
164 res->period_start = now;
165 res->forks_in_period = 0;
168 if (res->forks_in_period >= forkrate) {
174 atomic_inc(&res->cnt_cur_alloc);
177 if (((res->parent) && (res->cnt_unused == CKRM_SHARE_DONTCARE)) ||
178 (atomic_read(&res->cnt_cur_alloc) > res->cnt_unused)) {
181 if (!force && (res->cnt_limit != CKRM_SHARE_DONTCARE) &&
182 (atomic_read(&res->cnt_cur_alloc) > res->cnt_limit)) {
183 res->limit_failures++;
184 res->tot_limit_failures++;
185 } else if (res->parent != NULL) {
187 numtasks_get_ref_local(res->parent, force)) == 1) {
188 atomic_inc(&res->cnt_borrowed);
189 res->borrow_sucesses++;
190 res->tot_borrow_sucesses++;
191 res->over_guarantee = 1;
194 res->borrow_failures++;
195 res->tot_borrow_failures++;
200 } else if (res->over_guarantee) {
201 res->over_guarantee = 0;
203 if (res->max_limit_failures < res->limit_failures) {
204 res->max_limit_failures = res->limit_failures;
206 if (res->max_borrow_sucesses < res->borrow_sucesses) {
207 res->max_borrow_sucesses = res->borrow_sucesses;
209 if (res->max_borrow_failures < res->borrow_failures) {
210 res->max_borrow_failures = res->borrow_failures;
212 res->limit_failures = 0;
213 res->borrow_sucesses = 0;
214 res->borrow_failures = 0;
218 atomic_dec(&res->cnt_cur_alloc);
219 } else if (!borrowed) {
221 #ifdef CONFIG_CKRM_RES_NUMTASKS_FORKRATE
222 if (!force) { // force is not associated with a real fork.
223 res->forks_in_period++;
230 static void numtasks_put_ref_local(void *arg)
232 int resid = numtasks_rcbs.resid;
233 ckrm_numtasks_t *res;
234 ckrm_core_class_t *core = arg;
236 if ((resid == -1) || (core == NULL)) {
240 res = ckrm_get_res_class(core, resid, ckrm_numtasks_t);
243 if (unlikely(atomic_read(&res->cnt_cur_alloc) == 0)) {
244 printk(KERN_WARNING "numtasks_put_ref: Trying to decrement "
245 "counter below 0\n");
248 atomic_dec(&res->cnt_cur_alloc);
249 if (atomic_read(&res->cnt_borrowed) > 0) {
250 atomic_dec(&res->cnt_borrowed);
251 numtasks_put_ref_local(res->parent);
259 static void *numtasks_res_alloc(struct ckrm_core_class *core,
260 struct ckrm_core_class *parent)
262 ckrm_numtasks_t *res;
264 res = kmalloc(sizeof(ckrm_numtasks_t), GFP_ATOMIC);
267 memset(res, 0, sizeof(ckrm_numtasks_t));
269 res->parent = parent;
270 numtasks_res_initcls_one(res);
271 res->cnt_lock = SPIN_LOCK_UNLOCKED;
272 if (parent == NULL) {
273 // I am part of root class. So set the max tasks
274 // to available default
275 res->cnt_guarantee = total_numtasks;
276 res->cnt_unused = total_numtasks;
277 res->cnt_limit = total_numtasks;
278 root_core = core; // store the root core.
280 try_module_get(THIS_MODULE);
283 "numtasks_res_alloc: failed GFP_ATOMIC alloc\n");
289 * No locking of this resource class object necessary as we are not
290 * supposed to be assigned (or used) when/after this function is called.
292 static void numtasks_res_free(void *my_res)
294 ckrm_numtasks_t *res = my_res, *parres, *childres;
295 ckrm_core_class_t *child = NULL;
296 int i, borrowed, maxlimit, resid = numtasks_rcbs.resid;
301 // Assuming there will be no children when this function is called
303 parres = ckrm_get_res_class(res->parent, resid, ckrm_numtasks_t);
305 if (unlikely(atomic_read(&res->cnt_cur_alloc) < 0)) {
306 printk(KERN_WARNING "numtasks_res: counter below 0\n");
308 if (unlikely(atomic_read(&res->cnt_cur_alloc) > 0 ||
309 atomic_read(&res->cnt_borrowed) > 0)) {
310 printk(KERN_WARNING "numtasks_res_free: resource still "
311 "alloc'd %p\n", res);
312 if ((borrowed = atomic_read(&res->cnt_borrowed)) > 0) {
313 for (i = 0; i < borrowed; i++) {
314 numtasks_put_ref_local(parres->core);
318 // return child's limit/guarantee to parent node
319 spin_lock(&parres->cnt_lock);
320 child_guarantee_changed(&parres->shares, res->shares.my_guarantee, 0);
322 // run thru parent's children and get the new max_limit of the parent
323 ckrm_lock_hier(parres->core);
325 while ((child = ckrm_get_next_child(parres->core, child)) != NULL) {
326 childres = ckrm_get_res_class(child, resid, ckrm_numtasks_t);
327 if (maxlimit < childres->shares.my_limit) {
328 maxlimit = childres->shares.my_limit;
331 ckrm_unlock_hier(parres->core);
332 if (parres->shares.cur_max_limit < maxlimit) {
333 parres->shares.cur_max_limit = maxlimit;
336 spin_unlock(&parres->cnt_lock);
338 module_put(THIS_MODULE);
344 * Recalculate the guarantee and limit in real units... and propagate the
346 * Caller is responsible for protecting res and for the integrity of parres
349 recalc_and_propagate(ckrm_numtasks_t * res, ckrm_numtasks_t * parres)
351 ckrm_core_class_t *child = NULL;
352 ckrm_numtasks_t *childres;
353 int resid = numtasks_rcbs.resid;
356 struct ckrm_shares *par = &parres->shares;
357 struct ckrm_shares *self = &res->shares;
359 // calculate cnt_guarantee and cnt_limit
361 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE) {
362 res->cnt_guarantee = CKRM_SHARE_DONTCARE;
363 } else if (par->total_guarantee) {
364 u64 temp = (u64) self->my_guarantee * parres->cnt_guarantee;
365 do_div(temp, par->total_guarantee);
366 res->cnt_guarantee = (int) temp;
368 res->cnt_guarantee = 0;
371 if (parres->cnt_limit == CKRM_SHARE_DONTCARE) {
372 res->cnt_limit = CKRM_SHARE_DONTCARE;
373 } else if (par->max_limit) {
374 u64 temp = (u64) self->my_limit * parres->cnt_limit;
375 do_div(temp, par->max_limit);
376 res->cnt_limit = (int) temp;
381 // Calculate unused units
382 if (res->cnt_guarantee == CKRM_SHARE_DONTCARE) {
383 res->cnt_unused = CKRM_SHARE_DONTCARE;
384 } else if (self->total_guarantee) {
385 u64 temp = (u64) self->unused_guarantee * res->cnt_guarantee;
386 do_div(temp, self->total_guarantee);
387 res->cnt_unused = (int) temp;
392 // propagate to children
393 ckrm_lock_hier(res->core);
394 while ((child = ckrm_get_next_child(res->core, child)) != NULL) {
395 childres = ckrm_get_res_class(child, resid, ckrm_numtasks_t);
397 spin_lock(&childres->cnt_lock);
398 recalc_and_propagate(childres, res);
399 spin_unlock(&childres->cnt_lock);
401 printk(KERN_ERR "%s: numtasks resclass missing\n",__FUNCTION__);
404 ckrm_unlock_hier(res->core);
408 static int numtasks_set_share_values(void *my_res, struct ckrm_shares *new)
410 ckrm_numtasks_t *parres, *res = my_res;
411 struct ckrm_shares *cur = &res->shares, *par;
412 int rc = -EINVAL, resid = numtasks_rcbs.resid;
419 ckrm_get_res_class(res->parent, resid, ckrm_numtasks_t);
420 spin_lock(&parres->cnt_lock);
421 spin_lock(&res->cnt_lock);
422 par = &parres->shares;
424 spin_lock(&res->cnt_lock);
429 rc = set_shares(new, cur, par);
431 if ((rc == 0) && parres) {
432 // Calculate parent's unused units
433 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE) {
434 parres->cnt_unused = CKRM_SHARE_DONTCARE;
435 } else if (par->total_guarantee) {
436 u64 temp = (u64) par->unused_guarantee * parres->cnt_guarantee;
437 do_div(temp, par->total_guarantee);
438 parres->cnt_unused = (int) temp;
440 parres->cnt_unused = 0;
442 recalc_and_propagate(res, parres);
444 spin_unlock(&res->cnt_lock);
446 spin_unlock(&parres->cnt_lock);
451 static int numtasks_get_share_values(void *my_res, struct ckrm_shares *shares)
453 ckrm_numtasks_t *res = my_res;
457 *shares = res->shares;
461 static int numtasks_get_stats(void *my_res, struct seq_file *sfile)
463 ckrm_numtasks_t *res = my_res;
468 seq_printf(sfile, "Number of tasks resource:\n");
469 seq_printf(sfile, "Total Over limit failures: %d\n",
470 res->tot_limit_failures);
471 seq_printf(sfile, "Total Over guarantee sucesses: %d\n",
472 res->tot_borrow_sucesses);
473 seq_printf(sfile, "Total Over guarantee failures: %d\n",
474 res->tot_borrow_failures);
476 seq_printf(sfile, "Maximum Over limit failures: %d\n",
477 res->max_limit_failures);
478 seq_printf(sfile, "Maximum Over guarantee sucesses: %d\n",
479 res->max_borrow_sucesses);
480 seq_printf(sfile, "Maximum Over guarantee failures: %d\n",
481 res->max_borrow_failures);
482 #ifdef NUMTASKS_DEBUG
484 "cur_alloc %d; borrowed %d; cnt_guar %d; cnt_limit %d "
485 "cnt_unused %d, unused_guarantee %d, cur_max_limit %d\n",
486 atomic_read(&res->cnt_cur_alloc),
487 atomic_read(&res->cnt_borrowed), res->cnt_guarantee,
488 res->cnt_limit, res->cnt_unused,
489 res->shares.unused_guarantee,
490 res->shares.cur_max_limit);
496 static int numtasks_show_config(void *my_res, struct seq_file *sfile)
498 ckrm_numtasks_t *res = my_res;
503 seq_printf(sfile, "res=%s,%s=%d,%s=%d,%s=%d\n", NUMTASKS_NAME,
504 SYS_TOTAL_TASKS, total_numtasks,
506 FORKRATE_INTERVAL, forkrate_interval);
510 enum numtasks_token_t {
511 numtasks_token_total,
512 numtasks_token_forkrate,
513 numtasks_token_interval,
517 static match_table_t numtasks_tokens = {
518 {numtasks_token_total, SYS_TOTAL_TASKS "=%d"},
519 {numtasks_token_forkrate, FORKRATE "=%d"},
520 {numtasks_token_interval, FORKRATE_INTERVAL "=%d"},
521 {numtasks_token_err, NULL},
524 static void reset_forkrates(ckrm_core_class_t *parent, unsigned long now)
526 ckrm_numtasks_t *parres;
527 ckrm_core_class_t *child = NULL;
529 parres = ckrm_get_res_class(parent, numtasks_rcbs.resid,
534 parres->forks_in_period = 0;
535 parres->period_start = now;
537 ckrm_lock_hier(parent);
538 while ((child = ckrm_get_next_child(parent, child)) != NULL) {
539 reset_forkrates(child, now);
541 ckrm_unlock_hier(parent);
544 static int numtasks_set_config(void *my_res, const char *cfgstr)
547 ckrm_numtasks_t *res = my_res;
548 int new_total, fr = 0, itvl = 0, err = 0;
553 while ((p = strsep((char**)&cfgstr, ",")) != NULL) {
554 substring_t args[MAX_OPT_ARGS];
559 token = match_token(p, numtasks_tokens, args);
561 case numtasks_token_total:
562 if (match_int(args, &new_total) ||
563 (new_total < total_cnt_alloc)) {
566 total_numtasks = new_total;
568 // res is the default class, as config is present only
570 spin_lock(&res->cnt_lock);
571 res->cnt_guarantee = total_numtasks;
572 res->cnt_unused = total_numtasks;
573 res->cnt_limit = total_numtasks;
574 recalc_and_propagate(res, NULL);
575 spin_unlock(&res->cnt_lock);
578 case numtasks_token_forkrate:
579 if (match_int(args, &fr) || (fr <= 0)) {
585 case numtasks_token_interval:
586 if (match_int(args, &itvl) || (itvl <= 0)) {
589 forkrate_interval = itvl;
596 if ((fr > 0) || (itvl > 0)) {
597 reset_forkrates(root_core, jiffies);
602 static void numtasks_change_resclass(void *task, void *old, void *new)
604 ckrm_numtasks_t *oldres = old;
605 ckrm_numtasks_t *newres = new;
607 if (oldres != (void *)-1) {
608 struct task_struct *tsk = task;
610 struct ckrm_core_class *old_core =
611 &(tsk->parent->taskclass->core);
613 ckrm_get_res_class(old_core, numtasks_rcbs.resid,
616 numtasks_put_ref_local(oldres->core);
619 (void)numtasks_get_ref_local(newres->core, 1);
623 struct ckrm_res_ctlr numtasks_rcbs = {
624 .res_name = NUMTASKS_NAME,
627 .res_alloc = numtasks_res_alloc,
628 .res_free = numtasks_res_free,
629 .set_share_values = numtasks_set_share_values,
630 .get_share_values = numtasks_get_share_values,
631 .get_stats = numtasks_get_stats,
632 .show_config = numtasks_show_config,
633 .set_config = numtasks_set_config,
634 .change_resclass = numtasks_change_resclass,
637 int __init init_ckrm_numtasks_res(void)
639 struct ckrm_classtype *clstype;
640 int resid = numtasks_rcbs.resid;
642 clstype = ckrm_find_classtype_by_name("taskclass");
643 if (clstype == NULL) {
644 printk(KERN_INFO " Unknown ckrm classtype<taskclass>");
649 resid = ckrm_register_res_ctlr(clstype, &numtasks_rcbs);
650 printk(KERN_DEBUG "........init_ckrm_numtasks_res -> %d\n", resid);
652 ckrm_numtasks_register(numtasks_get_ref_local,
653 numtasks_put_ref_local);
654 numtasks_rcbs.classtype = clstype;
660 void __exit exit_ckrm_numtasks_res(void)
662 if (numtasks_rcbs.resid != -1) {
663 ckrm_numtasks_register(NULL, NULL);
665 ckrm_unregister_res_ctlr(&numtasks_rcbs);
666 numtasks_rcbs.resid = -1;
669 module_init(init_ckrm_numtasks_res)
670 module_exit(exit_ckrm_numtasks_res)
672 MODULE_LICENSE("GPL");