This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / kernel / ckrm / ckrm_tasks.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 /* Changes
15  * 
16  * 31 Mar 2004: Created
17  * 
18  */
19
20 /*
21  * Code Description: TBD
22  */
23
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>
33 #include <linux/ckrm_tsk.h>
34
35 #define TOTAL_NUM_TASKS (131072)        // 128 K
36 #define NUMTASKS_DEBUG
37 #define NUMTASKS_NAME "numtasks"
38
39 typedef struct ckrm_numtasks {
40         struct ckrm_core_class *core;   // the core i am part of...
41         struct ckrm_core_class *parent; // parent of the core above.
42         struct ckrm_shares shares;
43         spinlock_t cnt_lock;    // always grab parent's lock before child's
44         int cnt_guarantee;      // num_tasks guarantee in local units
45         int cnt_unused;         // has to borrow if more than this is needed
46         int cnt_limit;          // no tasks over this limit.
47         atomic_t cnt_cur_alloc; // current alloc from self
48         atomic_t cnt_borrowed;  // borrowed from the parent
49
50         int over_guarantee;     // turn on/off when cur_alloc goes 
51                                 // over/under guarantee
52
53         // internally maintained statictics to compare with max numbers
54         int limit_failures;     // # failures as request was over the limit
55         int borrow_sucesses;    // # successful borrows
56         int borrow_failures;    // # borrow failures
57
58         // Maximum the specific statictics has reached.
59         int max_limit_failures;
60         int max_borrow_sucesses;
61         int max_borrow_failures;
62
63         // Total number of specific statistics
64         int tot_limit_failures;
65         int tot_borrow_sucesses;
66         int tot_borrow_failures;
67 } ckrm_numtasks_t;
68
69 struct ckrm_res_ctlr numtasks_rcbs;
70
71 /* Initialize rescls values
72  * May be called on each rcfs unmount or as part of error recovery
73  * to make share values sane.
74  * Does not traverse hierarchy reinitializing children.
75  */
76 static void numtasks_res_initcls_one(ckrm_numtasks_t * res)
77 {
78         res->shares.my_guarantee = CKRM_SHARE_DONTCARE;
79         res->shares.my_limit = CKRM_SHARE_DONTCARE;
80         res->shares.total_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE;
81         res->shares.max_limit = CKRM_SHARE_DFLT_MAX_LIMIT;
82         res->shares.unused_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE;
83         res->shares.cur_max_limit = 0;
84
85         res->cnt_guarantee = CKRM_SHARE_DONTCARE;
86         res->cnt_unused = CKRM_SHARE_DONTCARE;
87         res->cnt_limit = CKRM_SHARE_DONTCARE;
88
89         res->over_guarantee = 0;
90
91         res->limit_failures = 0;
92         res->borrow_sucesses = 0;
93         res->borrow_failures = 0;
94
95         res->max_limit_failures = 0;
96         res->max_borrow_sucesses = 0;
97         res->max_borrow_failures = 0;
98
99         res->tot_limit_failures = 0;
100         res->tot_borrow_sucesses = 0;
101         res->tot_borrow_failures = 0;
102
103         atomic_set(&res->cnt_cur_alloc, 0);
104         atomic_set(&res->cnt_borrowed, 0);
105         return;
106 }
107
108 #if 0
109 static void numtasks_res_initcls(void *my_res)
110 {
111         ckrm_numtasks_t *res = my_res;
112
113         /* Write a version which propagates values all the way down 
114            and replace rcbs callback with that version */
115
116 }
117 #endif
118
119 static int numtasks_get_ref_local(void *arg, int force)
120 {
121         int rc, resid = numtasks_rcbs.resid;
122         ckrm_numtasks_t *res;
123         ckrm_core_class_t *core = arg;
124
125         if ((resid < 0) || (core == NULL))
126                 return 1;
127
128         res = ckrm_get_res_class(core, resid, ckrm_numtasks_t);
129         if (res == NULL)
130                 return 1;
131
132         atomic_inc(&res->cnt_cur_alloc);
133
134         rc = 1;
135         if (((res->parent) && (res->cnt_unused == CKRM_SHARE_DONTCARE)) ||
136             (atomic_read(&res->cnt_cur_alloc) > res->cnt_unused)) {
137
138                 rc = 0;
139                 if (!force && (res->cnt_limit != CKRM_SHARE_DONTCARE) &&
140                     (atomic_read(&res->cnt_cur_alloc) > res->cnt_limit)) {
141                         res->limit_failures++;
142                         res->tot_limit_failures++;
143                 } else if (res->parent != NULL) {
144                         if ((rc =
145                              numtasks_get_ref_local(res->parent, force)) == 1) {
146                                 atomic_inc(&res->cnt_borrowed);
147                                 res->borrow_sucesses++;
148                                 res->tot_borrow_sucesses++;
149                                 res->over_guarantee = 1;
150                         } else {
151                                 res->borrow_failures++;
152                                 res->tot_borrow_failures++;
153                         }
154                 } else {
155                         rc = force;
156                 }
157         } else if (res->over_guarantee) {
158                 res->over_guarantee = 0;
159
160                 if (res->max_limit_failures < res->limit_failures) {
161                         res->max_limit_failures = res->limit_failures;
162                 }
163                 if (res->max_borrow_sucesses < res->borrow_sucesses) {
164                         res->max_borrow_sucesses = res->borrow_sucesses;
165                 }
166                 if (res->max_borrow_failures < res->borrow_failures) {
167                         res->max_borrow_failures = res->borrow_failures;
168                 }
169                 res->limit_failures = 0;
170                 res->borrow_sucesses = 0;
171                 res->borrow_failures = 0;
172         }
173
174         if (!rc) {
175                 atomic_dec(&res->cnt_cur_alloc);
176         }
177         return rc;
178 }
179
180 static void numtasks_put_ref_local(void *arg)
181 {
182         int resid = numtasks_rcbs.resid;
183         ckrm_numtasks_t *res;
184         ckrm_core_class_t *core = arg;
185
186         if ((resid == -1) || (core == NULL)) {
187                 return;
188         }
189
190         res = ckrm_get_res_class(core, resid, ckrm_numtasks_t);
191         if (res == NULL)
192                 return;
193         atomic_dec(&res->cnt_cur_alloc);
194         if (atomic_read(&res->cnt_borrowed) > 0) {
195                 atomic_dec(&res->cnt_borrowed);
196                 numtasks_put_ref_local(res->parent);
197         }
198         return;
199 }
200
201 static void *numtasks_res_alloc(struct ckrm_core_class *core,
202                                 struct ckrm_core_class *parent)
203 {
204         ckrm_numtasks_t *res;
205
206         res = kmalloc(sizeof(ckrm_numtasks_t), GFP_ATOMIC);
207
208         if (res) {
209                 memset(res, 0, sizeof(ckrm_numtasks_t));
210                 res->core = core;
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 
216                         // to available default
217                         res->cnt_guarantee = TOTAL_NUM_TASKS;
218                         res->cnt_unused = TOTAL_NUM_TASKS;
219                         res->cnt_limit = TOTAL_NUM_TASKS;
220                 }
221                 try_module_get(THIS_MODULE);
222         } else {
223                 printk(KERN_ERR
224                        "numtasks_res_alloc: failed GFP_ATOMIC alloc\n");
225         }
226         return res;
227 }
228
229 /*
230  * No locking of this resource class object necessary as we are not
231  * supposed to be assigned (or used) when/after this function is called.
232  */
233 static void numtasks_res_free(void *my_res)
234 {
235         ckrm_numtasks_t *res = my_res, *parres, *childres;
236         ckrm_core_class_t *child = NULL;
237         int i, borrowed, maxlimit, resid = numtasks_rcbs.resid;
238
239         if (!res)
240                 return;
241
242         // Assuming there will be no children when this function is called
243
244         parres = ckrm_get_res_class(res->parent, resid, ckrm_numtasks_t);
245
246         if (unlikely(atomic_read(&res->cnt_cur_alloc) != 0 ||
247                      atomic_read(&res->cnt_borrowed))) {
248                 printk(KERN_ERR
249                        "numtasks_res_free: resource still alloc'd %p\n", res);
250                 if ((borrowed = atomic_read(&res->cnt_borrowed)) > 0) {
251                         for (i = 0; i < borrowed; i++) {
252                                 numtasks_put_ref_local(parres->core);
253                         }
254                 }
255         }
256         // return child's limit/guarantee to parent node
257         spin_lock(&parres->cnt_lock);
258         child_guarantee_changed(&parres->shares, res->shares.my_guarantee, 0);
259
260         // run thru parent's children and get the new max_limit of the parent
261         ckrm_lock_hier(parres->core);
262         maxlimit = 0;
263         while ((child = ckrm_get_next_child(parres->core, child)) != NULL) {
264                 childres = ckrm_get_res_class(child, resid, ckrm_numtasks_t);
265                 if (maxlimit < childres->shares.my_limit) {
266                         maxlimit = childres->shares.my_limit;
267                 }
268         }
269         ckrm_unlock_hier(parres->core);
270         if (parres->shares.cur_max_limit < maxlimit) {
271                 parres->shares.cur_max_limit = maxlimit;
272         }
273
274         spin_unlock(&parres->cnt_lock);
275         kfree(res);
276         module_put(THIS_MODULE);
277         return;
278 }
279
280 /*
281  * Recalculate the guarantee and limit in real units... and propagate the
282  * same to children.
283  * Caller is responsible for protecting res and for the integrity of parres
284  */
285 static void
286 recalc_and_propagate(ckrm_numtasks_t * res, ckrm_numtasks_t * parres)
287 {
288         ckrm_core_class_t *child = NULL;
289         ckrm_numtasks_t *childres;
290         int resid = numtasks_rcbs.resid;
291
292         if (parres) {
293                 struct ckrm_shares *par = &parres->shares;
294                 struct ckrm_shares *self = &res->shares;
295
296                 // calculate cnt_guarantee and cnt_limit
297                 //
298                 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE) {
299                         res->cnt_guarantee = CKRM_SHARE_DONTCARE;
300                 } else if (par->total_guarantee) {
301                         res->cnt_guarantee =
302                             (self->my_guarantee * parres->cnt_guarantee)
303                             / par->total_guarantee;
304                 } else {
305                         res->cnt_guarantee = 0;
306                 }
307
308                 if (parres->cnt_limit == CKRM_SHARE_DONTCARE) {
309                         res->cnt_limit = CKRM_SHARE_DONTCARE;
310                 } else if (par->max_limit) {
311                         res->cnt_limit = (self->my_limit * parres->cnt_limit)
312                             / par->max_limit;
313                 } else {
314                         res->cnt_limit = 0;
315                 }
316
317                 // Calculate unused units
318                 if (res->cnt_guarantee == CKRM_SHARE_DONTCARE) {
319                         res->cnt_unused = CKRM_SHARE_DONTCARE;
320                 } else if (self->total_guarantee) {
321                         res->cnt_unused = (self->unused_guarantee *
322                                            res->cnt_guarantee) /
323                             self->total_guarantee;
324                 } else {
325                         res->cnt_unused = 0;
326                 }
327         }
328         // propagate to children
329         ckrm_lock_hier(res->core);
330         while ((child = ckrm_get_next_child(res->core, child)) != NULL) {
331                 childres = ckrm_get_res_class(child, resid, ckrm_numtasks_t);
332
333                 spin_lock(&childres->cnt_lock);
334                 recalc_and_propagate(childres, res);
335                 spin_unlock(&childres->cnt_lock);
336         }
337         ckrm_unlock_hier(res->core);
338         return;
339 }
340
341 static int numtasks_set_share_values(void *my_res, struct ckrm_shares *new)
342 {
343         ckrm_numtasks_t *parres, *res = my_res;
344         struct ckrm_shares *cur = &res->shares, *par;
345         int rc = -EINVAL, resid = numtasks_rcbs.resid;
346
347         if (!res)
348                 return rc;
349
350         if (res->parent) {
351                 parres =
352                     ckrm_get_res_class(res->parent, resid, ckrm_numtasks_t);
353                 spin_lock(&parres->cnt_lock);
354                 spin_lock(&res->cnt_lock);
355                 par = &parres->shares;
356         } else {
357                 spin_lock(&res->cnt_lock);
358                 par = NULL;
359                 parres = NULL;
360         }
361
362         rc = set_shares(new, cur, par);
363
364         if ((rc == 0) && parres) {
365                 // Calculate parent's unused units
366                 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE) {
367                         parres->cnt_unused = CKRM_SHARE_DONTCARE;
368                 } else if (par->total_guarantee) {
369                         parres->cnt_unused = (par->unused_guarantee *
370                                               parres->cnt_guarantee) /
371                             par->total_guarantee;
372                 } else {
373                         parres->cnt_unused = 0;
374                 }
375                 recalc_and_propagate(res, parres);
376         }
377         spin_unlock(&res->cnt_lock);
378         if (res->parent) {
379                 spin_unlock(&parres->cnt_lock);
380         }
381         return rc;
382 }
383
384 static int numtasks_get_share_values(void *my_res, struct ckrm_shares *shares)
385 {
386         ckrm_numtasks_t *res = my_res;
387
388         if (!res)
389                 return -EINVAL;
390         *shares = res->shares;
391         return 0;
392 }
393
394 static int numtasks_get_stats(void *my_res, struct seq_file *sfile)
395 {
396         ckrm_numtasks_t *res = my_res;
397
398         if (!res)
399                 return -EINVAL;
400
401         seq_printf(sfile, "Number of tasks resource:\n");
402         seq_printf(sfile, "Total Over limit failures: %d\n",
403                    res->tot_limit_failures);
404         seq_printf(sfile, "Total Over guarantee sucesses: %d\n",
405                    res->tot_borrow_sucesses);
406         seq_printf(sfile, "Total Over guarantee failures: %d\n",
407                    res->tot_borrow_failures);
408
409         seq_printf(sfile, "Maximum Over limit failures: %d\n",
410                    res->max_limit_failures);
411         seq_printf(sfile, "Maximum Over guarantee sucesses: %d\n",
412                    res->max_borrow_sucesses);
413         seq_printf(sfile, "Maximum Over guarantee failures: %d\n",
414                    res->max_borrow_failures);
415 #ifdef NUMTASKS_DEBUG
416         seq_printf(sfile,
417                    "cur_alloc %d; borrowed %d; cnt_guar %d; cnt_limit %d "
418                    "unused_guarantee %d, cur_max_limit %d\n",
419                    atomic_read(&res->cnt_cur_alloc),
420                    atomic_read(&res->cnt_borrowed), res->cnt_guarantee,
421                    res->cnt_limit, res->shares.unused_guarantee,
422                    res->shares.cur_max_limit);
423 #endif
424
425         return 0;
426 }
427
428 static int numtasks_show_config(void *my_res, struct seq_file *sfile)
429 {
430         ckrm_numtasks_t *res = my_res;
431
432         if (!res)
433                 return -EINVAL;
434
435         seq_printf(sfile, "res=%s,parameter=somevalue\n", NUMTASKS_NAME);
436         return 0;
437 }
438
439 static int numtasks_set_config(void *my_res, const char *cfgstr)
440 {
441         ckrm_numtasks_t *res = my_res;
442
443         if (!res)
444                 return -EINVAL;
445         printk("numtasks config='%s'\n", cfgstr);
446         return 0;
447 }
448
449 static void numtasks_change_resclass(void *task, void *old, void *new)
450 {
451         ckrm_numtasks_t *oldres = old;
452         ckrm_numtasks_t *newres = new;
453
454         if (oldres != (void *)-1) {
455                 struct task_struct *tsk = task;
456                 if (!oldres) {
457                         struct ckrm_core_class *old_core =
458                             &(tsk->parent->taskclass->core);
459                         oldres =
460                             ckrm_get_res_class(old_core, numtasks_rcbs.resid,
461                                                ckrm_numtasks_t);
462                 }
463                 numtasks_put_ref_local(oldres->core);
464         }
465         if (newres) {
466                 (void)numtasks_get_ref_local(newres->core, 1);
467         }
468 }
469
470 struct ckrm_res_ctlr numtasks_rcbs = {
471         .res_name = NUMTASKS_NAME,
472         .res_hdepth = 1,
473         .resid = -1,
474         .res_alloc = numtasks_res_alloc,
475         .res_free = numtasks_res_free,
476         .set_share_values = numtasks_set_share_values,
477         .get_share_values = numtasks_get_share_values,
478         .get_stats = numtasks_get_stats,
479         .show_config = numtasks_show_config,
480         .set_config = numtasks_set_config,
481         .change_resclass = numtasks_change_resclass,
482 };
483
484 int __init init_ckrm_numtasks_res(void)
485 {
486         struct ckrm_classtype *clstype;
487         int resid = numtasks_rcbs.resid;
488
489         clstype = ckrm_find_classtype_by_name("taskclass");
490         if (clstype == NULL) {
491                 printk(KERN_INFO " Unknown ckrm classtype<taskclass>");
492                 return -ENOENT;
493         }
494
495         if (resid == -1) {
496                 resid = ckrm_register_res_ctlr(clstype, &numtasks_rcbs);
497                 printk("........init_ckrm_numtasks_res -> %d\n", resid);
498                 if (resid != -1) {
499                         ckrm_numtasks_register(numtasks_get_ref_local,
500                                                numtasks_put_ref_local);
501                         numtasks_rcbs.classtype = clstype;
502                 }
503         }
504         return 0;
505 }
506
507 void __exit exit_ckrm_numtasks_res(void)
508 {
509         if (numtasks_rcbs.resid != -1) {
510                 ckrm_numtasks_register(NULL, NULL);
511         }
512         ckrm_unregister_res_ctlr(&numtasks_rcbs);
513         numtasks_rcbs.resid = -1;
514 }
515
516 module_init(init_ckrm_numtasks_res)
517     module_exit(exit_ckrm_numtasks_res)
518
519     MODULE_LICENSE("GPL");