- PL2545 WAR: disable CKRM numtasks controller
[linux-2.6.git] / kernel / ckrm / ckrm_numtasks.c
1 /* ckrm_numtasks.c - "Number of tasks" resource controller for CKRM
2  *
3  * Copyright (C) Chandra Seetharaman,  IBM Corp. 2003
4  * 
5  * Latest version, more details at http://ckrm.sf.net
6  * 
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.
11  *
12  */
13
14 /*
15  * CKRM Resource controller for tracking number of tasks in a class.
16  */
17
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>
28
29 #define TOTAL_NUM_TASKS (131072)        /* 128 K */
30 #define NUMTASKS_DEBUG
31 #define NUMTASKS_NAME "numtasks"
32
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 */
43
44         int over_guarantee;     /* turn on/off when cur_alloc goes  */
45                                 /* over/under guarantee */
46
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 */
51
52         /* Maximum the specific statictics has reached. */
53         int max_limit_failures;
54         int max_borrow_sucesses;
55         int max_borrow_failures;
56
57         /* Total number of specific statistics */
58         int tot_limit_failures;
59         int tot_borrow_sucesses;
60         int tot_borrow_failures;
61 };
62
63 struct ckrm_res_ctlr numtasks_rcbs;
64
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.
69  */
70 static void numtasks_res_initcls_one(struct ckrm_numtasks * res)
71 {
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;
78
79         res->cnt_guarantee = CKRM_SHARE_DONTCARE;
80         res->cnt_unused = CKRM_SHARE_DONTCARE;
81         res->cnt_limit = CKRM_SHARE_DONTCARE;
82
83         res->over_guarantee = 0;
84
85         res->limit_failures = 0;
86         res->borrow_sucesses = 0;
87         res->borrow_failures = 0;
88
89         res->max_limit_failures = 0;
90         res->max_borrow_sucesses = 0;
91         res->max_borrow_failures = 0;
92
93         res->tot_limit_failures = 0;
94         res->tot_borrow_sucesses = 0;
95         res->tot_borrow_failures = 0;
96
97         atomic_set(&res->cnt_cur_alloc, 0);
98         atomic_set(&res->cnt_borrowed, 0);
99         return;
100 }
101
102 static int numtasks_get_ref_local(struct ckrm_core_class *core, int force)
103 {
104         int rc, resid = numtasks_rcbs.resid;
105         struct ckrm_numtasks *res;
106
107         if ((resid < 0) || (core == NULL))
108                 return 1;
109
110         res = ckrm_get_res_class(core, resid, struct ckrm_numtasks);
111         if (res == NULL)
112                 return 1;
113
114         atomic_inc(&res->cnt_cur_alloc);
115
116         rc = 1;
117         if (((res->parent) && (res->cnt_unused == CKRM_SHARE_DONTCARE)) ||
118             (atomic_read(&res->cnt_cur_alloc) > res->cnt_unused)) {
119
120                 rc = 0;
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) {
126                         if ((rc =
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;
132                         } else {
133                                 res->borrow_failures++;
134                                 res->tot_borrow_failures++;
135                         }
136                 } else
137                         rc = force;
138         } else if (res->over_guarantee) {
139                 res->over_guarantee = 0;
140
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;
150         }
151
152         if (!rc)
153                 atomic_dec(&res->cnt_cur_alloc);
154         return rc;
155 }
156
157 static void numtasks_put_ref_local(struct ckrm_core_class *core)
158 {
159         int resid = numtasks_rcbs.resid;
160         struct ckrm_numtasks *res;
161
162         if ((resid == -1) || (core == NULL))
163                 return;
164
165         res = ckrm_get_res_class(core, resid, struct ckrm_numtasks);
166         if (res == NULL)
167                 return;
168
169         if (atomic_read(&res->cnt_cur_alloc)==0)
170                 return;
171
172         atomic_dec(&res->cnt_cur_alloc);
173
174         if (atomic_read(&res->cnt_borrowed) > 0) {
175                 atomic_dec(&res->cnt_borrowed);
176                 numtasks_put_ref_local(res->parent);
177         }
178         return;
179 }
180
181 static void *numtasks_res_alloc(struct ckrm_core_class *core,
182                                 struct ckrm_core_class *parent)
183 {
184         struct ckrm_numtasks *res;
185
186         res = kmalloc(sizeof(struct ckrm_numtasks), GFP_ATOMIC);
187
188         if (res) {
189                 memset(res, 0, sizeof(struct ckrm_numtasks));
190                 res->core = core;
191                 res->parent = parent;
192                 numtasks_res_initcls_one(res);
193                 res->cnt_lock = SPIN_LOCK_UNLOCKED;
194                 if (parent == NULL) {
195                         /*
196                          * I am part of root class. So set the max tasks 
197                          * to available default.
198                          */
199                         res->cnt_guarantee = TOTAL_NUM_TASKS;
200                         res->cnt_unused = TOTAL_NUM_TASKS;
201                         res->cnt_limit = TOTAL_NUM_TASKS;
202                 }
203                 try_module_get(THIS_MODULE);
204         } else {
205                 printk(KERN_ERR
206                        "numtasks_res_alloc: failed GFP_ATOMIC alloc\n");
207         }
208         return res;
209 }
210
211 /*
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.
214  */
215 static void numtasks_res_free(void *my_res)
216 {
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;
220
221         if (!res)
222                 return;
223
224         /* Assuming there will be no children when this function is called */
225
226         parres = ckrm_get_res_class(res->parent, resid, struct ckrm_numtasks);
227
228         if ((borrowed = atomic_read(&res->cnt_borrowed)) > 0)
229                 for (i = 0; i < borrowed; i++)
230                         numtasks_put_ref_local(parres->core);
231
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);
235
236         /* run thru parent's children and get the new max_limit of the parent */
237         ckrm_lock_hier(parres->core);
238         maxlimit = 0;
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;
243         }
244         ckrm_unlock_hier(parres->core);
245         if (parres->shares.cur_max_limit < maxlimit)
246                 parres->shares.cur_max_limit = maxlimit;
247
248         spin_unlock(&parres->cnt_lock);
249         kfree(res);
250         module_put(THIS_MODULE);
251         return;
252 }
253
254 /*
255  * Recalculate the guarantee and limit in real units... and propagate the
256  * same to children.
257  * Caller is responsible for protecting res and for the integrity of parres
258  */
259 static void
260 recalc_and_propagate(struct ckrm_numtasks * res, struct ckrm_numtasks * parres)
261 {
262         struct ckrm_core_class *child = NULL;
263         struct ckrm_numtasks *childres;
264         int resid = numtasks_rcbs.resid;
265
266         if (parres) {
267                 struct ckrm_shares *par = &parres->shares;
268                 struct ckrm_shares *self = &res->shares;
269
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;
278                 } else
279                         res->cnt_guarantee = 0;
280
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;
288                 } else
289                         res->cnt_limit = 0;
290
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;
299                 } else
300                         res->cnt_unused = 0;
301         }
302
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);
307                 if (childres) {
308                         spin_lock(&childres->cnt_lock);
309                         recalc_and_propagate(childres, res);
310                         spin_unlock(&childres->cnt_lock);
311                 } else {
312                         printk(KERN_ERR "%s: numtasks resclass missing\n",__FUNCTION__);
313                 }
314         }
315         ckrm_unlock_hier(res->core);
316         return;
317 }
318
319 static int numtasks_set_share_values(void *my_res, struct ckrm_shares *new)
320 {
321         struct ckrm_numtasks *parres, *res = my_res;
322         struct ckrm_shares *cur = &res->shares, *par;
323         int rc = -EINVAL, resid = numtasks_rcbs.resid;
324
325         if (!res)
326                 return rc;
327
328         if (res->parent) {
329                 parres =
330                    ckrm_get_res_class(res->parent, resid, struct ckrm_numtasks);
331                 spin_lock(&parres->cnt_lock);
332                 spin_lock(&res->cnt_lock);
333                 par = &parres->shares;
334         } else {
335                 spin_lock(&res->cnt_lock);
336                 par = NULL;
337                 parres = NULL;
338         }
339
340         rc = set_shares(new, cur, par);
341
342         if ((rc == 0) && parres) {
343                 /* Calculate parent's unused units */
344                 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE)
345                         parres->cnt_unused = CKRM_SHARE_DONTCARE;
346                 else if (par->total_guarantee) {
347                         u64 temp = (u64) par->unused_guarantee * parres->cnt_guarantee;
348                         do_div(temp, par->total_guarantee);
349                         parres->cnt_unused = (int) temp;
350                 } else
351                         parres->cnt_unused = 0;
352                 recalc_and_propagate(res, parres);
353         }
354         spin_unlock(&res->cnt_lock);
355         if (res->parent)
356                 spin_unlock(&parres->cnt_lock);
357         return rc;
358 }
359
360 static int numtasks_get_share_values(void *my_res, struct ckrm_shares *shares)
361 {
362         struct ckrm_numtasks *res = my_res;
363
364         if (!res)
365                 return -EINVAL;
366         *shares = res->shares;
367         return 0;
368 }
369
370 static int numtasks_get_stats(void *my_res, struct seq_file *sfile)
371 {
372         struct ckrm_numtasks *res = my_res;
373
374         if (!res)
375                 return -EINVAL;
376
377         seq_printf(sfile, "---------Number of tasks stats start---------\n");
378         seq_printf(sfile, "Total Over limit failures: %d\n",
379                    res->tot_limit_failures);
380         seq_printf(sfile, "Total Over guarantee sucesses: %d\n",
381                    res->tot_borrow_sucesses);
382         seq_printf(sfile, "Total Over guarantee failures: %d\n",
383                    res->tot_borrow_failures);
384
385         seq_printf(sfile, "Maximum Over limit failures: %d\n",
386                    res->max_limit_failures);
387         seq_printf(sfile, "Maximum Over guarantee sucesses: %d\n",
388                    res->max_borrow_sucesses);
389         seq_printf(sfile, "Maximum Over guarantee failures: %d\n",
390                    res->max_borrow_failures);
391         seq_printf(sfile, "---------Number of tasks stats end---------\n");
392 #ifdef NUMTASKS_DEBUG
393         seq_printf(sfile,
394                    "cur_alloc %d; borrowed %d; cnt_guar %d; cnt_limit %d "
395                    "cnt_unused %d, unused_guarantee %d, cur_max_limit %d\n",
396                    atomic_read(&res->cnt_cur_alloc),
397                    atomic_read(&res->cnt_borrowed), res->cnt_guarantee,
398                    res->cnt_limit, res->cnt_unused,
399                    res->shares.unused_guarantee,
400                    res->shares.cur_max_limit);
401 #endif
402
403         return 0;
404 }
405
406 static int numtasks_show_config(void *my_res, struct seq_file *sfile)
407 {
408         struct ckrm_numtasks *res = my_res;
409
410         if (!res)
411                 return -EINVAL;
412
413         seq_printf(sfile, "res=%s,parameter=somevalue\n", NUMTASKS_NAME);
414         return 0;
415 }
416
417 static int numtasks_set_config(void *my_res, const char *cfgstr)
418 {
419         struct ckrm_numtasks *res = my_res;
420
421         if (!res)
422                 return -EINVAL;
423         printk("numtasks config='%s'\n", cfgstr);
424         return 0;
425 }
426
427 static void numtasks_change_resclass(void *task, void *old, void *new)
428 {
429         struct ckrm_numtasks *oldres = old;
430         struct ckrm_numtasks *newres = new;
431
432         if (oldres != (void *)-1) {
433                 struct task_struct *tsk = task;
434                 if (!oldres) {
435                         struct ckrm_core_class *old_core =
436                             &(tsk->parent->taskclass->core);
437                         oldres =
438                             ckrm_get_res_class(old_core, numtasks_rcbs.resid,
439                                                struct ckrm_numtasks);
440                 }
441                 if (oldres)
442                         numtasks_put_ref_local(oldres->core);
443         }
444         if (newres)
445                 (void)numtasks_get_ref_local(newres->core, 1);
446 }
447
448 struct ckrm_res_ctlr numtasks_rcbs = {
449         .res_name = NUMTASKS_NAME,
450         .res_hdepth = 1,
451         .resid = -1,
452         .res_alloc = numtasks_res_alloc,
453         .res_free = numtasks_res_free,
454         .set_share_values = numtasks_set_share_values,
455         .get_share_values = numtasks_get_share_values,
456         .get_stats = numtasks_get_stats,
457         .show_config = numtasks_show_config,
458         .set_config = numtasks_set_config,
459         .change_resclass = numtasks_change_resclass,
460 };
461
462 int __init init_ckrm_numtasks_res(void)
463 {
464         struct ckrm_classtype *clstype;
465         int resid = numtasks_rcbs.resid;
466
467         clstype = ckrm_find_classtype_by_name("taskclass");
468         if (clstype == NULL) {
469                 printk(KERN_INFO " Unknown ckrm classtype<taskclass>");
470                 return -ENOENT;
471         }
472
473         if (resid == -1) {
474                 resid = ckrm_register_res_ctlr(clstype, &numtasks_rcbs);
475                 printk("........init_ckrm_numtasks_res -> %d\n", resid);
476                 if (resid != -1) {
477                         ckrm_numtasks_register(numtasks_get_ref_local,
478                                                numtasks_put_ref_local);
479                         numtasks_rcbs.classtype = clstype;
480                 }
481         }
482         return 0;
483 }
484
485 void __exit exit_ckrm_numtasks_res(void)
486 {
487         if (numtasks_rcbs.resid != -1)
488                 ckrm_numtasks_register(NULL, NULL);
489         ckrm_unregister_res_ctlr(&numtasks_rcbs);
490         numtasks_rcbs.resid = -1;
491 }
492
493 module_init(init_ckrm_numtasks_res)
494 module_exit(exit_ckrm_numtasks_res)
495
496 MODULE_LICENSE("GPL");