This commit was manufactured by cvs2svn to create branch 'ckrm'.
[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
34 #define TOTAL_NUM_TASKS (131072) // 128 K
35 #define NUMTASKS_DEBUG
36 #define NUMTASKS_NAME "numtasks"
37
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
48
49         int over_guarantee; //turn on/off when cur_alloc goes over/under guarantee
50
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
55
56         // Maximum the specific statictics has reached.
57         int max_limit_failures;
58         int max_borrow_sucesses;
59         int max_borrow_failures;
60
61         // Total number of specific statistics
62         int tot_limit_failures;
63         int tot_borrow_sucesses;
64         int tot_borrow_failures;
65 } ckrm_numtasks_t;
66
67 struct ckrm_res_ctlr numtasks_rcbs;
68
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.
73  */
74 static void
75 numtasks_res_initcls_one(ckrm_numtasks_t *res)
76 {
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;
83
84         res->cnt_guarantee           = CKRM_SHARE_DONTCARE;
85         res->cnt_unused              = CKRM_SHARE_DONTCARE;
86         res->cnt_limit               = CKRM_SHARE_DONTCARE;
87
88         res->over_guarantee          = 0;
89
90         res->limit_failures          = 0;
91         res->borrow_sucesses         = 0;
92         res->borrow_failures         = 0;
93
94         res->max_limit_failures      = 0;
95         res->max_borrow_sucesses     = 0;
96         res->max_borrow_failures     = 0;
97
98         res->tot_limit_failures      = 0;
99         res->tot_borrow_sucesses     = 0;
100         res->tot_borrow_failures     = 0;
101
102         atomic_set(&res->cnt_cur_alloc, 0);
103         atomic_set(&res->cnt_borrowed, 0);
104         return;
105 }
106
107 #if 0   
108 static void
109 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 int
120 numtasks_get_ref(void *arg, int force)
121 {
122         int rc, resid = numtasks_rcbs.resid;
123         ckrm_numtasks_t *res;
124         ckrm_core_class_t *core = arg;
125
126         if ((resid < 0) || (core == NULL))
127                 return 1;
128
129         res = ckrm_get_res_class(core, resid, ckrm_numtasks_t);
130         if (res == NULL) 
131                 return 1;
132
133         atomic_inc(&res->cnt_cur_alloc);
134
135         rc = 1;
136         if (((res->parent) && (res->cnt_unused == CKRM_SHARE_DONTCARE)) ||
137                         (atomic_read(&res->cnt_cur_alloc) > res->cnt_unused)) {
138
139                 rc = 0;
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;
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 void
181 numtasks_put_ref(void *arg)
182 {
183         int resid = numtasks_rcbs.resid;
184         ckrm_numtasks_t *res;
185         ckrm_core_class_t *core = arg;
186
187         if ((resid == -1) || (core == NULL)) {
188                 return;
189         }
190
191         res = ckrm_get_res_class(core, resid, ckrm_numtasks_t);
192         if (res == NULL) 
193                 return;
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);
198         }
199         return;
200 }
201
202 static void *
203 numtasks_res_alloc(struct ckrm_core_class *core, struct ckrm_core_class *parent)
204 {
205         ckrm_numtasks_t *res;
206         
207         res = kmalloc(sizeof(ckrm_numtasks_t), GFP_ATOMIC);
208         
209         if (res) {
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 to available
216                         // default
217                         res->cnt_guarantee = TOTAL_NUM_TASKS;
218                         res->cnt_unused =  TOTAL_NUM_TASKS;
219                         res->cnt_limit = TOTAL_NUM_TASKS;
220                 }
221         } else {
222                 printk(KERN_ERR "numtasks_res_alloc: failed GFP_ATOMIC alloc\n");
223         }
224         return res;
225 }
226
227 /*
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.
230  */
231 static void
232 numtasks_res_free(void *my_res)
233 {
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;
237
238         if (!res) 
239                 return;
240
241         // Assuming there will be no children when this function is called
242         
243         parres = ckrm_get_res_class(res->parent, resid, ckrm_numtasks_t);
244
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);
251                         }
252                 }
253         }
254
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);
258
259         // run thru parent's children and get the new max_limit of the parent
260         ckrm_lock_hier(parres->core);
261         maxlimit = 0;
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;
266                 }
267         }
268         ckrm_unlock_hier(parres->core);
269         if (parres->shares.cur_max_limit < maxlimit) {
270                 parres->shares.cur_max_limit = maxlimit;
271         }
272
273         spin_unlock(&parres->cnt_lock);
274         kfree(res);
275         return;
276 }
277 /*
278  * Recalculate the guarantee and limit in real units... and propagate the
279  * same to children.
280  * Caller is responsible for protecting res and for the integrity of parres
281  */
282 static void
283 recalc_and_propagate(ckrm_numtasks_t *res, ckrm_numtasks_t *parres)
284 {
285         ckrm_core_class_t *child = NULL;
286         ckrm_numtasks_t *childres;
287         int resid = numtasks_rcbs.resid;
288
289         if (parres) {
290                 struct ckrm_shares *par = &parres->shares;
291                 struct ckrm_shares *self = &res->shares;
292
293                 // calculate cnt_guarantee and cnt_limit
294                 //
295                 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE) {
296                         res->cnt_guarantee = CKRM_SHARE_DONTCARE;
297                 } else {
298                         res->cnt_guarantee = (self->my_guarantee * parres->cnt_guarantee) 
299                                         / par->total_guarantee;
300                 }
301                 if (parres->cnt_limit == CKRM_SHARE_DONTCARE) {
302                         res->cnt_limit = CKRM_SHARE_DONTCARE;
303                 } else {
304                         res->cnt_limit = (self->my_limit * parres->cnt_limit)
305                                         / par->max_limit;
306                 }
307
308                 // Calculate unused units
309                 if (res->cnt_guarantee == CKRM_SHARE_DONTCARE) {
310                         res->cnt_unused = CKRM_SHARE_DONTCARE;
311                 } else {
312                         res->cnt_unused = (self->unused_guarantee *
313                                         res->cnt_guarantee) / self->total_guarantee;
314                 }
315         }
316
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);
321
322                 spin_lock(&childres->cnt_lock);
323                 recalc_and_propagate(childres, res);
324                 spin_unlock(&childres->cnt_lock);
325         }
326         ckrm_unlock_hier(res->core);
327         return;
328 }
329
330 static int
331 numtasks_set_share_values(void *my_res, struct ckrm_shares *new)
332 {
333         ckrm_numtasks_t *parres, *res = my_res;
334         struct ckrm_shares *cur = &res->shares, *par;
335         int rc = -EINVAL, resid = numtasks_rcbs.resid;
336
337         if (!res) 
338                 return rc;
339
340         if (res->parent) {
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;
345         } else {
346                 spin_lock(&res->cnt_lock);
347                 par = NULL;
348                 parres = NULL;
349         }
350
351         rc = set_shares(new, cur, par);
352
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;
357                 } else {
358                         parres->cnt_unused = (par->unused_guarantee *
359                                         parres->cnt_guarantee) / par->total_guarantee;
360                 }
361
362                 recalc_and_propagate(res, parres);
363         }
364         spin_unlock(&res->cnt_lock);
365         if (res->parent) {
366                 spin_unlock(&parres->cnt_lock);
367         }
368         return rc;
369 }
370
371
372 static int
373 numtasks_get_share_values(void *my_res, struct ckrm_shares *shares)
374 {
375         ckrm_numtasks_t *res = my_res;
376
377         if (!res) 
378                 return -EINVAL;
379         *shares = res->shares;
380         return 0;
381 }
382
383 static int  
384 numtasks_get_stats(void *my_res, struct seq_file *sfile)
385 {
386         ckrm_numtasks_t *res = my_res;
387
388         if (!res) 
389                 return -EINVAL;
390
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);
398
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),
410                         res->cnt_guarantee,
411                         res->cnt_limit,
412                         res->shares.unused_guarantee,
413                         res->shares.cur_max_limit);
414 #endif
415
416         return 0;
417 }
418
419 static int  
420 numtasks_show_config(void *my_res, struct seq_file *sfile)
421 {
422         ckrm_numtasks_t *res = my_res;
423
424         if (!res) 
425                 return -EINVAL;
426
427         seq_printf(sfile, "res=%s,parameter=somevalue\n",NUMTASKS_NAME);
428         return 0;
429 }
430
431 static int  
432 numtasks_set_config(void *my_res, const char *cfgstr)
433 {
434         ckrm_numtasks_t *res = my_res;
435
436         if (!res) 
437                 return -EINVAL;
438         printk("numtasks config='%s'\n",cfgstr);
439         return 0;
440 }
441
442 static void
443 numtasks_change_resclass(void *task, void *old, void *new)
444 {
445         ckrm_numtasks_t *oldres = old;
446         ckrm_numtasks_t *newres = new;
447
448         if (oldres != (void *) -1) {
449                 struct task_struct *tsk = task;
450                 if (!oldres) {
451                         struct ckrm_core_class *old_core = &(tsk->parent->taskclass->core);
452                         oldres = ckrm_get_res_class(old_core, numtasks_rcbs.resid,
453                                         ckrm_numtasks_t);
454                 }
455                 numtasks_put_ref(oldres->core);
456         }
457         if (newres) {
458                 (void) numtasks_get_ref(newres->core, 1);
459         }
460 }
461
462 struct ckrm_res_ctlr numtasks_rcbs = {
463         .res_name          = NUMTASKS_NAME,
464         .res_hdepth        = 1,
465         .resid             = -1,
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,
474 };
475
476 int __init
477 init_ckrm_numtasks_res(void)
478 {
479         struct ckrm_classtype *clstype;
480         int resid = numtasks_rcbs.resid;
481
482         clstype = ckrm_find_classtype_by_name("taskclass");
483         if (clstype == NULL) {
484                 printk(KERN_INFO " Unknown ckrm classtype<taskclass>");
485                 return -ENOENT;
486         }
487
488         if (resid == -1) {
489                 resid = ckrm_register_res_ctlr(clstype,&numtasks_rcbs);
490                 printk("........init_ckrm_numtasks_res -> %d\n",resid);
491         }
492         return 0;
493 }       
494
495 void __exit
496 exit_ckrm_numtasks_res(void)
497 {
498         ckrm_unregister_res_ctlr(&numtasks_rcbs);
499         numtasks_rcbs.resid = -1;
500 }
501
502 module_init(init_ckrm_numtasks_res)
503 module_exit(exit_ckrm_numtasks_res)
504
505 EXPORT_SYMBOL(numtasks_get_ref);
506 EXPORT_SYMBOL(numtasks_put_ref);
507
508 MODULE_LICENSE("GPL");
509