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