One more attempt to fix the numtask problem that Steve reported.
[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         atomic_dec(&res->cnt_cur_alloc);
169         if (atomic_read(&res->cnt_borrowed) > 0) {
170                 atomic_dec(&res->cnt_borrowed);
171                 numtasks_put_ref_local(res->parent);
172         }
173         return;
174 }
175
176 static void *numtasks_res_alloc(struct ckrm_core_class *core,
177                                 struct ckrm_core_class *parent)
178 {
179         struct ckrm_numtasks *res;
180
181         res = kmalloc(sizeof(struct ckrm_numtasks), GFP_ATOMIC);
182
183         if (res) {
184                 memset(res, 0, sizeof(struct ckrm_numtasks));
185                 res->core = core;
186                 res->parent = parent;
187                 numtasks_res_initcls_one(res);
188                 res->cnt_lock = SPIN_LOCK_UNLOCKED;
189                 if (parent == NULL) {
190                         /*
191                          * I am part of root class. So set the max tasks 
192                          * to available default.
193                          */
194                         res->cnt_guarantee = TOTAL_NUM_TASKS;
195                         res->cnt_unused = TOTAL_NUM_TASKS;
196                         res->cnt_limit = TOTAL_NUM_TASKS;
197                 }
198                 try_module_get(THIS_MODULE);
199         } else {
200                 printk(KERN_ERR
201                        "numtasks_res_alloc: failed GFP_ATOMIC alloc\n");
202         }
203         return res;
204 }
205
206 /*
207  * No locking of this resource class object necessary as we are not
208  * supposed to be assigned (or used) when/after this function is called.
209  */
210 static void numtasks_res_free(void *my_res)
211 {
212         struct ckrm_numtasks *res = my_res, *parres, *childres;
213         struct ckrm_core_class *child = NULL;
214         int i, borrowed, maxlimit, resid = numtasks_rcbs.resid;
215
216         if (!res)
217                 return;
218
219         /* Assuming there will be no children when this function is called */
220
221         parres = ckrm_get_res_class(res->parent, resid, struct ckrm_numtasks);
222
223         if ((borrowed = atomic_read(&res->cnt_borrowed)) > 0)
224                 for (i = 0; i < borrowed; i++)
225                         numtasks_put_ref_local(parres->core);
226
227         /* return child's limit/guarantee to parent node */
228         spin_lock(&parres->cnt_lock);
229         child_guarantee_changed(&parres->shares, res->shares.my_guarantee, 0);
230
231         /* run thru parent's children and get the new max_limit of the parent */
232         ckrm_lock_hier(parres->core);
233         maxlimit = 0;
234         while ((child = ckrm_get_next_child(parres->core, child)) != NULL) {
235                 childres = ckrm_get_res_class(child, resid, struct ckrm_numtasks);
236                 if (maxlimit < childres->shares.my_limit)
237                         maxlimit = childres->shares.my_limit;
238         }
239         ckrm_unlock_hier(parres->core);
240         if (parres->shares.cur_max_limit < maxlimit)
241                 parres->shares.cur_max_limit = maxlimit;
242
243         spin_unlock(&parres->cnt_lock);
244         kfree(res);
245         module_put(THIS_MODULE);
246         return;
247 }
248
249 /*
250  * Recalculate the guarantee and limit in real units... and propagate the
251  * same to children.
252  * Caller is responsible for protecting res and for the integrity of parres
253  */
254 static void
255 recalc_and_propagate(struct ckrm_numtasks * res, struct ckrm_numtasks * parres)
256 {
257         struct ckrm_core_class *child = NULL;
258         struct ckrm_numtasks *childres;
259         int resid = numtasks_rcbs.resid;
260
261         if (parres) {
262                 struct ckrm_shares *par = &parres->shares;
263                 struct ckrm_shares *self = &res->shares;
264
265                 /* calculate cnt_guarantee and cnt_limit */
266                 if ((parres->cnt_guarantee == CKRM_SHARE_DONTCARE) ||
267                                 (self->my_guarantee == CKRM_SHARE_DONTCARE))
268                         res->cnt_guarantee = CKRM_SHARE_DONTCARE;
269                 else if (par->total_guarantee) {
270                         u64 temp = (u64) self->my_guarantee * parres->cnt_guarantee;
271                         do_div(temp, par->total_guarantee);
272                         res->cnt_guarantee = (int) temp;
273                 } else
274                         res->cnt_guarantee = 0;
275
276                 if ((parres->cnt_limit == CKRM_SHARE_DONTCARE) ||
277                                 (self->my_limit == CKRM_SHARE_DONTCARE))
278                         res->cnt_limit = CKRM_SHARE_DONTCARE;
279                 else if (par->max_limit) {
280                         u64 temp = (u64) self->my_limit * parres->cnt_limit;
281                         do_div(temp, par->max_limit);
282                         res->cnt_limit = (int) temp;
283                 } else
284                         res->cnt_limit = 0;
285
286                 /* Calculate unused units */
287                 if ((res->cnt_guarantee == CKRM_SHARE_DONTCARE) ||
288                                 (self->my_guarantee == CKRM_SHARE_DONTCARE))
289                         res->cnt_unused = CKRM_SHARE_DONTCARE;
290                 else if (self->total_guarantee) {
291                         u64 temp = (u64) self->unused_guarantee * res->cnt_guarantee;
292                         do_div(temp, self->total_guarantee);
293                         res->cnt_unused = (int) temp;
294                 } else
295                         res->cnt_unused = 0;
296         }
297
298         /* propagate to children */
299         ckrm_lock_hier(res->core);
300         while ((child = ckrm_get_next_child(res->core, child)) != NULL) {
301                 childres = ckrm_get_res_class(child, resid, struct ckrm_numtasks);
302
303                 spin_lock(&childres->cnt_lock);
304                 recalc_and_propagate(childres, res);
305                 spin_unlock(&childres->cnt_lock);
306         }
307         ckrm_unlock_hier(res->core);
308         return;
309 }
310
311 static int numtasks_set_share_values(void *my_res, struct ckrm_shares *new)
312 {
313         struct ckrm_numtasks *parres, *res = my_res;
314         struct ckrm_shares *cur = &res->shares, *par;
315         int rc = -EINVAL, resid = numtasks_rcbs.resid;
316
317         if (!res)
318                 return rc;
319
320         if (res->parent) {
321                 parres =
322                     ckrm_get_res_class(res->parent, resid, struct ckrm_numtasks);
323                 spin_lock(&parres->cnt_lock);
324                 spin_lock(&res->cnt_lock);
325                 par = &parres->shares;
326         } else {
327                 spin_lock(&res->cnt_lock);
328                 par = NULL;
329                 parres = NULL;
330         }
331
332         rc = set_shares(new, cur, par);
333
334         if ((rc == 0) && parres) {
335                 /* Calculate parent's unused units */
336                 if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE)
337                         parres->cnt_unused = CKRM_SHARE_DONTCARE;
338                 else if (par->total_guarantee) {
339                         u64 temp = (u64) par->unused_guarantee * parres->cnt_guarantee;
340                         do_div(temp, par->total_guarantee);
341                         parres->cnt_unused = (int) temp;
342                 } else
343                         parres->cnt_unused = 0;
344                 recalc_and_propagate(res, parres);
345         }
346         spin_unlock(&res->cnt_lock);
347         if (res->parent)
348                 spin_unlock(&parres->cnt_lock);
349         return rc;
350 }
351
352 static int numtasks_get_share_values(void *my_res, struct ckrm_shares *shares)
353 {
354         struct ckrm_numtasks *res = my_res;
355
356         if (!res)
357                 return -EINVAL;
358         *shares = res->shares;
359         return 0;
360 }
361
362 static int numtasks_get_stats(void *my_res, struct seq_file *sfile)
363 {
364         struct ckrm_numtasks *res = my_res;
365
366         if (!res)
367                 return -EINVAL;
368
369         seq_printf(sfile, "---------Number of tasks stats start---------\n");
370         seq_printf(sfile, "Total Over limit failures: %d\n",
371                    res->tot_limit_failures);
372         seq_printf(sfile, "Total Over guarantee sucesses: %d\n",
373                    res->tot_borrow_sucesses);
374         seq_printf(sfile, "Total Over guarantee failures: %d\n",
375                    res->tot_borrow_failures);
376
377         seq_printf(sfile, "Maximum Over limit failures: %d\n",
378                    res->max_limit_failures);
379         seq_printf(sfile, "Maximum Over guarantee sucesses: %d\n",
380                    res->max_borrow_sucesses);
381         seq_printf(sfile, "Maximum Over guarantee failures: %d\n",
382                    res->max_borrow_failures);
383         seq_printf(sfile, "---------Number of tasks stats end---------\n");
384 #ifdef NUMTASKS_DEBUG
385         seq_printf(sfile,
386                    "cur_alloc %d; borrowed %d; cnt_guar %d; cnt_limit %d "
387                    "cnt_unused %d, unused_guarantee %d, cur_max_limit %d\n",
388                    atomic_read(&res->cnt_cur_alloc),
389                    atomic_read(&res->cnt_borrowed), res->cnt_guarantee,
390                    res->cnt_limit, res->cnt_unused,
391                    res->shares.unused_guarantee,
392                    res->shares.cur_max_limit);
393 #endif
394
395         return 0;
396 }
397
398 static int numtasks_show_config(void *my_res, struct seq_file *sfile)
399 {
400         struct ckrm_numtasks *res = my_res;
401
402         if (!res)
403                 return -EINVAL;
404
405         seq_printf(sfile, "res=%s,parameter=somevalue\n", NUMTASKS_NAME);
406         return 0;
407 }
408
409 static int numtasks_set_config(void *my_res, const char *cfgstr)
410 {
411         struct ckrm_numtasks *res = my_res;
412
413         if (!res)
414                 return -EINVAL;
415         printk("numtasks config='%s'\n", cfgstr);
416         return 0;
417 }
418
419 static void numtasks_change_resclass(void *task, void *old, void *new)
420 {
421         struct ckrm_numtasks *oldres = old;
422         struct ckrm_numtasks *newres = new;
423
424         if (oldres != (void *)-1) {
425                 struct task_struct *tsk = task;
426                 if (!oldres) {
427                         struct ckrm_core_class *old_core =
428                             &(tsk->parent->taskclass->core);
429                         oldres =
430                             ckrm_get_res_class(old_core, numtasks_rcbs.resid,
431                                                struct ckrm_numtasks);
432                 }
433                 if (oldres)
434                         numtasks_put_ref_local(oldres->core);
435         }
436         if (newres)
437                 (void)numtasks_get_ref_local(newres->core, 1);
438 }
439
440 struct ckrm_res_ctlr numtasks_rcbs = {
441         .res_name = NUMTASKS_NAME,
442         .res_hdepth = 1,
443         .resid = -1,
444         .res_alloc = numtasks_res_alloc,
445         .res_free = numtasks_res_free,
446         .set_share_values = numtasks_set_share_values,
447         .get_share_values = numtasks_get_share_values,
448         .get_stats = numtasks_get_stats,
449         .show_config = numtasks_show_config,
450         .set_config = numtasks_set_config,
451         .change_resclass = numtasks_change_resclass,
452 };
453
454 int __init init_ckrm_numtasks_res(void)
455 {
456         struct ckrm_classtype *clstype;
457         int resid = numtasks_rcbs.resid;
458
459         clstype = ckrm_find_classtype_by_name("taskclass");
460         if (clstype == NULL) {
461                 printk(KERN_INFO " Unknown ckrm classtype<taskclass>");
462                 return -ENOENT;
463         }
464
465         if (resid == -1) {
466                 resid = ckrm_register_res_ctlr(clstype, &numtasks_rcbs);
467                 printk("........init_ckrm_numtasks_res -> %d\n", resid);
468                 if (resid != -1) {
469                         ckrm_numtasks_register(numtasks_get_ref_local,
470                                                numtasks_put_ref_local);
471                         numtasks_rcbs.classtype = clstype;
472                 }
473         }
474         return 0;
475 }
476
477 void __exit exit_ckrm_numtasks_res(void)
478 {
479         if (numtasks_rcbs.resid != -1)
480                 ckrm_numtasks_register(NULL, NULL);
481         ckrm_unregister_res_ctlr(&numtasks_rcbs);
482         numtasks_rcbs.resid = -1;
483 }
484
485 module_init(init_ckrm_numtasks_res)
486 module_exit(exit_ckrm_numtasks_res)
487
488 MODULE_LICENSE("GPL");