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 <linux/list.h>
29 #include <linux/spinlock.h>
30 #include <linux/ckrm.h>
31 #include <linux/ckrm_rc.h>
32 #include <linux/ckrm_tc.h>
34 #define TOTAL_NUM_TASKS (131072) // 128 K
35 #define NUMTASKS_DEBUG
36 #define NUMTASKS_NAME "numtasks"
38 typedef struct ckrm_numtasks {
39 struct ckrm_core_class *core; // the core i am part of...
40 struct ckrm_core_class *parent; // parent of the core above.
41 struct ckrm_shares shares;
42 spinlock_t cnt_lock; // always grab parent's lock first and then child's
43 int cnt_guarantee; // num_tasks guarantee in local units
44 int cnt_unused; // has to borrow if more than this is needed
45 int cnt_limit; // no tasks over this limit.
46 atomic_t cnt_cur_alloc; // current alloc from self
47 atomic_t cnt_borrowed; // borrowed from the parent
49 int over_guarantee; //turn on/off when cur_alloc goes over/under guarantee
51 // internally maintained statictics to compare with max numbers
52 int limit_failures; // no. of failures 'cause the request was over the limit
53 int borrow_sucesses; // no. of successful borrows
54 int borrow_failures; // no. of borrow faileures
56 // Maximum the specific statictics has reached.
57 int max_limit_failures;
58 int max_borrow_sucesses;
59 int max_borrow_failures;
61 // Total number of specific statistics
62 int tot_limit_failures;
63 int tot_borrow_sucesses;
64 int tot_borrow_failures;
67 struct ckrm_res_ctlr numtasks_rcbs;
69 /* Initialize rescls values
70 * May be called on each rcfs unmount or as part of error recovery
71 * to make share values sane.
72 * Does not traverse hierarchy reinitializing children.
75 numtasks_res_initcls_one(ckrm_numtasks_t *res)
77 res->shares.my_guarantee = CKRM_SHARE_DONTCARE;
78 res->shares.my_limit = CKRM_SHARE_DONTCARE;
79 res->shares.total_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE;
80 res->shares.max_limit = CKRM_SHARE_DFLT_MAX_LIMIT;
81 res->shares.unused_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE;
82 res->shares.cur_max_limit = 0;
84 res->cnt_guarantee = CKRM_SHARE_DONTCARE;
85 res->cnt_unused = CKRM_SHARE_DONTCARE;
86 res->cnt_limit = CKRM_SHARE_DONTCARE;
88 res->over_guarantee = 0;
90 res->limit_failures = 0;
91 res->borrow_sucesses = 0;
92 res->borrow_failures = 0;
94 res->max_limit_failures = 0;
95 res->max_borrow_sucesses = 0;
96 res->max_borrow_failures = 0;
98 res->tot_limit_failures = 0;
99 res->tot_borrow_sucesses = 0;
100 res->tot_borrow_failures = 0;
102 atomic_set(&res->cnt_cur_alloc, 0);
103 atomic_set(&res->cnt_borrowed, 0);
109 numtasks_res_initcls(void *my_res)
111 ckrm_numtasks_t *res = my_res;
113 /* Write a version which propagates values all the way down
114 and replace rcbs callback with that version */
120 numtasks_get_ref(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) {
145 if ((rc = numtasks_get_ref(res->parent, force)) == 1) {
146 atomic_inc(&res->cnt_borrowed);
147 res->borrow_sucesses++;
148 res->tot_borrow_sucesses++;
149 res->over_guarantee = 1;
151 res->borrow_failures++;
152 res->tot_borrow_failures++;
157 } else if (res->over_guarantee) {
158 res->over_guarantee = 0;
160 if (res->max_limit_failures < res->limit_failures) {
161 res->max_limit_failures = res->limit_failures;
163 if (res->max_borrow_sucesses < res->borrow_sucesses) {
164 res->max_borrow_sucesses = res->borrow_sucesses;
166 if (res->max_borrow_failures < res->borrow_failures) {
167 res->max_borrow_failures = res->borrow_failures;
169 res->limit_failures = 0;
170 res->borrow_sucesses = 0;
171 res->borrow_failures = 0;
175 atomic_dec(&res->cnt_cur_alloc);
181 numtasks_put_ref(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 atomic_dec(&res->cnt_cur_alloc);
195 if (atomic_read(&res->cnt_borrowed) > 0) {
196 atomic_dec(&res->cnt_borrowed);
197 numtasks_put_ref(res->parent);
203 numtasks_res_alloc(struct ckrm_core_class *core, struct ckrm_core_class *parent)
205 ckrm_numtasks_t *res;
207 res = kmalloc(sizeof(ckrm_numtasks_t), GFP_ATOMIC);
211 res->parent = parent;
212 numtasks_res_initcls_one(res);
213 res->cnt_lock = SPIN_LOCK_UNLOCKED;
214 if (parent == NULL) {
215 // I am part of root class. so set the max tasks to available
217 res->cnt_guarantee = TOTAL_NUM_TASKS;
218 res->cnt_unused = TOTAL_NUM_TASKS;
219 res->cnt_limit = TOTAL_NUM_TASKS;
222 printk(KERN_ERR "numtasks_res_alloc: failed GFP_ATOMIC alloc\n");
228 * No locking of this resource class object necessary as we are not
229 * supposed to be assigned (or used) when/after this function is called.
232 numtasks_res_free(void *my_res)
234 ckrm_numtasks_t *res = my_res, *parres, *childres;
235 ckrm_core_class_t *child = NULL;
236 int i, borrowed, maxlimit, resid = numtasks_rcbs.resid;
241 // Assuming there will be no children when this function is called
243 parres = ckrm_get_res_class(res->parent, resid, ckrm_numtasks_t);
245 if (unlikely(atomic_read(&res->cnt_cur_alloc) != 0 ||
246 atomic_read(&res->cnt_borrowed))) {
247 printk(KERN_ERR "numtasks_res_free: resource still alloc'd %p\n", res);
248 if ((borrowed = atomic_read(&res->cnt_borrowed)) > 0) {
249 for (i = 0; i < borrowed; i++) {
250 numtasks_put_ref(parres->core);
255 // return child's limit/guarantee to parent node
256 spin_lock(&parres->cnt_lock);
257 child_guarantee_changed(&parres->shares, res->shares.my_guarantee, 0);
259 // run thru parent's children and get the new max_limit of the parent
260 ckrm_lock_hier(parres->core);
262 while ((child = ckrm_get_next_child(parres->core, child)) != NULL) {
263 childres = ckrm_get_res_class(child, resid, ckrm_numtasks_t);
264 if (maxlimit < childres->shares.my_limit) {
265 maxlimit = childres->shares.my_limit;
268 ckrm_unlock_hier(parres->core);
269 if (parres->shares.cur_max_limit < maxlimit) {
270 parres->shares.cur_max_limit = maxlimit;
273 spin_unlock(&parres->cnt_lock);
278 * Recalculate the guarantee and limit in real units... and propagate the
280 * Caller is responsible for protecting res and for the integrity of parres
283 recalc_and_propagate(ckrm_numtasks_t *res, ckrm_numtasks_t *parres)
285 ckrm_core_class_t *child = NULL;
286 ckrm_numtasks_t *childres;
287 int resid = numtasks_rcbs.resid;
290 struct ckrm_shares *par = &parres->shares;
291 struct ckrm_shares *self = &res->shares;
293 // calculate cnt_guarantee and cnt_limit
295 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE) {
296 res->cnt_guarantee = CKRM_SHARE_DONTCARE;
298 res->cnt_guarantee = (self->my_guarantee * parres->cnt_guarantee)
299 / par->total_guarantee;
301 if (parres->cnt_limit == CKRM_SHARE_DONTCARE) {
302 res->cnt_limit = CKRM_SHARE_DONTCARE;
304 res->cnt_limit = (self->my_limit * parres->cnt_limit)
308 // Calculate unused units
309 if (res->cnt_guarantee == CKRM_SHARE_DONTCARE) {
310 res->cnt_unused = CKRM_SHARE_DONTCARE;
312 res->cnt_unused = (self->unused_guarantee *
313 res->cnt_guarantee) / self->total_guarantee;
317 // propagate to children
318 ckrm_lock_hier(res->core);
319 while ((child = ckrm_get_next_child(res->core, child)) != NULL) {
320 childres = ckrm_get_res_class(child, resid, ckrm_numtasks_t);
322 spin_lock(&childres->cnt_lock);
323 recalc_and_propagate(childres, res);
324 spin_unlock(&childres->cnt_lock);
326 ckrm_unlock_hier(res->core);
331 numtasks_set_share_values(void *my_res, struct ckrm_shares *new)
333 ckrm_numtasks_t *parres, *res = my_res;
334 struct ckrm_shares *cur = &res->shares, *par;
335 int rc = -EINVAL, resid = numtasks_rcbs.resid;
341 parres = ckrm_get_res_class(res->parent, resid, ckrm_numtasks_t);
342 spin_lock(&parres->cnt_lock);
343 spin_lock(&res->cnt_lock);
344 par = &parres->shares;
346 spin_lock(&res->cnt_lock);
351 rc = set_shares(new, cur, par);
353 if ((rc == 0) && parres) {
354 // Calculate parent's unused units
355 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE) {
356 parres->cnt_unused = CKRM_SHARE_DONTCARE;
358 parres->cnt_unused = (par->unused_guarantee *
359 parres->cnt_guarantee) / par->total_guarantee;
362 recalc_and_propagate(res, parres);
364 spin_unlock(&res->cnt_lock);
366 spin_unlock(&parres->cnt_lock);
373 numtasks_get_share_values(void *my_res, struct ckrm_shares *shares)
375 ckrm_numtasks_t *res = my_res;
379 *shares = res->shares;
384 numtasks_get_stats(void *my_res, struct seq_file *sfile)
386 ckrm_numtasks_t *res = my_res;
391 seq_printf(sfile, "Number of tasks resource:\n");
392 seq_printf(sfile, "Total Over limit failures: %d\n",
393 res->tot_limit_failures);
394 seq_printf(sfile, "Total Over guarantee sucesses: %d\n",
395 res->tot_borrow_sucesses);
396 seq_printf(sfile, "Total Over guarantee failures: %d\n",
397 res->tot_borrow_failures);
399 seq_printf(sfile, "Maximum Over limit failures: %d\n",
400 res->max_limit_failures);
401 seq_printf(sfile, "Maximum Over guarantee sucesses: %d\n",
402 res->max_borrow_sucesses);
403 seq_printf(sfile, "Maximum Over guarantee failures: %d\n",
404 res->max_borrow_failures);
405 #ifdef NUMTASKS_DEBUG
406 seq_printf(sfile, "cur_alloc %d; borrowed %d; cnt_guar %d; cnt_limit %d "
407 "unused_guarantee %d, cur_max_limit %d\n",
408 atomic_read(&res->cnt_cur_alloc),
409 atomic_read(&res->cnt_borrowed),
412 res->shares.unused_guarantee,
413 res->shares.cur_max_limit);
420 numtasks_show_config(void *my_res, struct seq_file *sfile)
422 ckrm_numtasks_t *res = my_res;
427 seq_printf(sfile, "res=%s,parameter=somevalue\n",NUMTASKS_NAME);
432 numtasks_set_config(void *my_res, const char *cfgstr)
434 ckrm_numtasks_t *res = my_res;
438 printk("numtasks config='%s'\n",cfgstr);
443 numtasks_change_resclass(void *task, void *old, void *new)
445 ckrm_numtasks_t *oldres = old;
446 ckrm_numtasks_t *newres = new;
448 if (oldres != (void *) -1) {
449 struct task_struct *tsk = task;
451 struct ckrm_core_class *old_core = &(tsk->parent->taskclass->core);
452 oldres = ckrm_get_res_class(old_core, numtasks_rcbs.resid,
455 numtasks_put_ref(oldres->core);
458 (void) numtasks_get_ref(newres->core, 1);
462 struct ckrm_res_ctlr numtasks_rcbs = {
463 .res_name = NUMTASKS_NAME,
466 .res_alloc = numtasks_res_alloc,
467 .res_free = numtasks_res_free,
468 .set_share_values = numtasks_set_share_values,
469 .get_share_values = numtasks_get_share_values,
470 .get_stats = numtasks_get_stats,
471 .show_config = numtasks_show_config,
472 .set_config = numtasks_set_config,
473 .change_resclass = numtasks_change_resclass,
477 init_ckrm_numtasks_res(void)
479 struct ckrm_classtype *clstype;
480 int resid = numtasks_rcbs.resid;
482 clstype = ckrm_find_classtype_by_name("taskclass");
483 if (clstype == NULL) {
484 printk(KERN_INFO " Unknown ckrm classtype<taskclass>");
489 resid = ckrm_register_res_ctlr(clstype,&numtasks_rcbs);
490 printk("........init_ckrm_numtasks_res -> %d\n",resid);
496 exit_ckrm_numtasks_res(void)
498 ckrm_unregister_res_ctlr(&numtasks_rcbs);
499 numtasks_rcbs.resid = -1;
502 module_init(init_ckrm_numtasks_res)
503 module_exit(exit_ckrm_numtasks_res)
505 EXPORT_SYMBOL(numtasks_get_ref);
506 EXPORT_SYMBOL(numtasks_put_ref);
508 MODULE_LICENSE("GPL");