resource: add configurable 'fifo' and 'lifo' ordering to application classes
[profile/ivi/murphy.git] / src / resource / application-class.c
1 /*
2  * Copyright (c) 2012, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *  * Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *  * Neither the name of Intel Corporation nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include <murphy/common/mm.h>
35 #include <murphy/common/hashtbl.h>
36 #include <murphy/common/utils.h>
37 #include <murphy/common/log.h>
38
39 #include <murphy/resource/manager-api.h>
40 #include <murphy/resource/client-api.h>
41 #include <murphy/resource/config-api.h>
42
43 #include <murphy-db/mqi.h>
44
45 #include "application-class.h"
46 #include "resource-set.h"
47 #include "resource-owner.h"
48 #include "zone.h"
49
50 #define CLASS_MAX        64
51 #define NAME_LENGTH      24
52
53 #define CLASS_NAME_IDX   0
54 #define PRIORITY_IDX     1
55
56
57 /*
58  * sorting key bit layout
59  *
60  * +---------+----+----+--------+
61  * | 31 - 29 | 28 | 27 | 26 - 0 |
62  * +---------+----+----+--------+
63  *      |      |    |       |
64  *      |      |    |       +---- 0x07ffffff stamp of the last request
65  *      |      |    +------------ 0x08000000 state (set if acquiring)
66  *      |      +----------------- 0x10000000 usage (set if shared)
67  *      +------------------------ 0xe0000000 priority (0-7)
68  */
69 #define MASK(b)   (((uint32_t)1 << (b)) - (uint32_t)1)
70
71 #define STAMP_SHIFT     0
72 #define STATE_SHIFT     (STAMP_SHIFT + MRP_KEY_STAMP_BITS)
73 #define USAGE_SHIFT     (STATE_SHIFT + MRP_KEY_STATE_BITS)
74 #define PRIORITY_SHIFT  (USAGE_SHIFT + MRP_KEY_USAGE_BITS)
75
76 #define STAMP_MASK      MASK(MRP_KEY_STAMP_BITS)
77 #define STATE_MASK      MASK(MRP_KEY_STATE_BITS)
78 #define USAGE_MASK      MASK(MRP_KEY_USAGE_BITS)
79 #define PRIORITY_MASK   MASK(MRP_KEY_PRIORITY_BITS)
80
81 #define STAMP_KEY(p)    (((uint32_t)(p) & STAMP_MASK)    << STAMP_SHIFT)
82 #define STATE_KEY(p)    (((uint32_t)(p) & STATE_MASK)    << STATE_SHIFT)
83 #define USAGE_KEY(p)    (((uint32_t)(p) & USAGE_MASK)    << USAGE_SHIFT)
84 #define PRIORITY_KEY(p) (((uint32_t)(p) & PRIORITY_MASK) << PRIORITY_SHIFT)
85
86 #define STAMP_MAX       STAMP_MASK
87
88 typedef struct {
89     const char *class_name;
90     uint32_t    priority;
91 } class_row_t;
92
93
94 static MRP_LIST_HOOK(class_list);
95 static mrp_htbl_t *name_hash;
96
97 static void init_name_hash(void);
98 static int  add_to_name_hash(mrp_application_class_t *);
99 #if 0
100 static void remove_from_name_hash(mrp_application_class_t *);
101 #endif
102
103 static mqi_handle_t get_database_table(void);
104 static void insert_into_application_class_table(const char *, uint32_t);
105
106
107 mrp_application_class_t *mrp_application_class_create(const char *name,
108                                                     uint32_t pri,
109                                                     bool modal,
110                                                     bool share,
111                                                     mrp_resource_order_t order)
112 {
113     mrp_application_class_t *class;
114     mrp_list_hook_t *insert_before, *clhook, *n;
115     uint32_t zone;
116
117     MRP_ASSERT(name, "invalid argument");
118
119     if (modal && share) {
120         mrp_log_error("Class '%s' is both modal and shared. "
121                       "Sharing will be disabled", name);
122         share = false;
123     }
124
125     /* looping through all classes to check the uniqueness of the
126        name & priority of the new class and find the insertion point */
127     insert_before = &class_list;
128
129     mrp_list_foreach_back(&class_list, clhook, n) {
130         class = mrp_list_entry(clhook, mrp_application_class_t, list);
131
132         if (!strcasecmp(name, class->name)) {
133             mrp_log_warning("Multiple definitions for class '%s'", name);
134             return NULL;
135         }
136
137         if (pri == class->priority) {
138             mrp_log_error("Priority clash. Classes '%s' and '%s' would have "
139                           "the same priority", name, class->name);
140         }
141
142         if (pri < class->priority)
143             insert_before = &class->list;
144     }
145
146     if (!(class = mrp_allocz(sizeof(mrp_application_class_t)))) {
147         mrp_log_error("Memory alloc failure. Can't create resource class '%s'",
148                       name);
149         return NULL;
150     }
151
152     class->name = mrp_strdup(name);
153     class->priority = pri;
154     class->modal = modal;
155     class->share = share;
156     class->order = order;
157
158     for (zone = 0;  zone < MRP_ZONE_MAX;  zone++)
159         mrp_list_init(&class->resource_sets[zone]);
160
161     /* list do not have insert_before function,
162        so don't be mislead by the name */
163     mrp_list_append(insert_before, &class->list);
164
165     add_to_name_hash(class);
166
167     insert_into_application_class_table(class->name, class->priority);
168
169     return class;
170 }
171
172
173 mrp_application_class_t *mrp_application_class_find(const char *name)
174 {
175     mrp_application_class_t *class = NULL;
176
177     if (name_hash && name)
178         class = mrp_htbl_lookup(name_hash, (void *)name);
179
180     return class;
181 }
182
183 mrp_application_class_t *mrp_application_class_iterate_classes(void **cursor)
184 {
185     mrp_list_hook_t *entry;
186
187     MRP_ASSERT(cursor, "invalid argument");
188
189     entry = (*cursor == NULL) ? class_list.prev : (mrp_list_hook_t *)*cursor;
190
191     if (entry == &class_list)
192         return NULL;
193
194     *cursor = entry->prev;
195
196     return mrp_list_entry(entry, mrp_application_class_t, list);
197 }
198
199 mrp_resource_set_t *
200 mrp_application_class_iterate_rsets(mrp_application_class_t *class,
201                                     uint32_t                 zone,
202                                     void                   **cursor)
203 {
204     mrp_list_hook_t *list, *entry;
205
206     MRP_ASSERT(class && zone < MRP_ZONE_MAX && cursor, "invalid argument");
207
208     list  = class->resource_sets + zone;
209     entry = (*cursor == NULL) ? list->prev : (mrp_list_hook_t *)*cursor;
210
211     if (entry == list)
212         return NULL;
213
214     *cursor = entry->prev;
215
216     return mrp_list_entry(entry, mrp_resource_set_t, class.list);
217 }
218
219 const char **mrp_application_class_get_all_names(uint32_t buflen,
220                                                  const char **buf)
221 {
222     mrp_list_hook_t *entry, *n;
223     mrp_application_class_t *class;
224     bool freeit = false;
225     uint32_t i  = 0;
226
227     MRP_ASSERT(!buf || (buf && buflen > 1), "invalid argument");
228
229     if (!buf) {
230         freeit = true;
231         buflen = CLASS_MAX;
232         if (!(buf = mrp_allocz(sizeof(const char *) * buflen))) {
233             mrp_log_error("Memory alloc failure. Can't get class names");
234             return NULL;
235         }
236     }
237
238     mrp_list_foreach(&class_list, entry, n) {
239         class = mrp_list_entry(entry, mrp_application_class_t, list);
240
241         if (i >= buflen-1) {
242             if (freeit)
243                 mrp_free(buf);
244             return NULL;
245         }
246
247         buf[i++] = class->name;
248     }
249
250     buf[i] = NULL;
251
252     return buf;
253 }
254
255
256 int mrp_application_class_add_resource_set(const char *class_name,
257                                            const char *zone_name,
258                                            mrp_resource_set_t *rset,
259                                            uint32_t reqid)
260 {
261     mrp_application_class_t *class;
262     mrp_zone_t *zone;
263
264
265     MRP_ASSERT(class_name && rset && zone_name, "invalid argument");
266     MRP_ASSERT(!rset->class.ptr || !mrp_list_empty(&rset->class.list),
267                "attempt to add multiple times the same resource set");
268
269     if (!(class = mrp_application_class_find(class_name)))
270         return -1;
271
272     if (!(zone = mrp_zone_find_by_name(zone_name)))
273         return -1;
274
275     rset->class.ptr = class;
276     rset->zone = mrp_zone_get_id(zone);
277
278     if (rset->state == mrp_resource_acquire)
279         mrp_resource_set_acquire(rset, reqid);
280     else {
281         rset->request.id = reqid;
282
283         if (rset->state == mrp_resource_no_request)
284             rset->state = mrp_resource_release;
285
286         mrp_application_class_move_resource_set(rset);
287         mrp_resource_owner_update_zone(rset->zone, rset, reqid);
288     }
289     
290     return 0;
291 }
292
293 void mrp_application_class_move_resource_set(mrp_resource_set_t *rset)
294 {
295     mrp_application_class_t *class;
296     mrp_list_hook_t *list, *lentry, *n, *insert_before;
297     mrp_resource_set_t *rentry;
298     uint32_t key;
299     uint32_t zone;
300
301     MRP_ASSERT(rset, "invalid argument");
302
303     mrp_list_delete(&rset->class.list);
304
305     class = rset->class.ptr;
306     zone  = rset->zone;
307
308     list = insert_before = class->resource_sets + zone;
309     key  = mrp_application_class_get_sorting_key(rset);
310
311     mrp_list_foreach_back(list, lentry, n) {
312         rentry = mrp_list_entry(lentry, mrp_resource_set_t, class.list);
313
314          if (key >= mrp_application_class_get_sorting_key(rentry))
315              break;
316
317          insert_before = lentry;
318     }
319
320     mrp_list_append(insert_before, &rset->class.list);
321 }
322
323 uint32_t mrp_application_class_get_sorting_key(mrp_resource_set_t *rset)
324 {
325     mrp_application_class_t *class;
326     bool     lifo;
327     uint32_t rqstamp;
328     uint32_t priority;
329     uint32_t usage;
330     uint32_t state;
331     uint32_t stamp;
332     uint32_t key;
333
334     MRP_ASSERT(rset, "invalid argument");
335
336     class = rset->class.ptr;
337     lifo  = (class->order == MRP_RESOURCE_ORDER_LIFO);
338
339     rqstamp  = rset->request.stamp;
340
341     priority = PRIORITY_KEY(rset->class.priority);
342     usage    = USAGE_KEY(rset->resource.share ? 1 : 0);
343     state    = STATE_KEY(rset->state == mrp_resource_acquire ? 1 : 0);
344     stamp    = STAMP_KEY(lifo ? rqstamp : STAMP_MAX - rqstamp);
345
346     key = priority | usage | state | stamp;
347
348     return key;
349 }
350
351
352 int mrp_application_class_print(char *buf, int len, bool with_rsets)
353 {
354 #define PRINT(fmt, args...) \
355     do { if (p<e) { p += snprintf(p, e-p, fmt , ##args); } } while (0)
356
357     mrp_zone_t *zone;
358     mrp_application_class_t *class;
359     mrp_resource_set_t *rset;
360     mrp_list_hook_t *clen, *n;
361     mrp_list_hook_t *list, *rsen, *m;
362     uint32_t zid;
363     char *p, *e;
364     int width, l;
365     int clcnt, rscnt;
366
367     MRP_ASSERT(buf && len > 0, "invalid argument");
368
369     e = (p = buf) + len;
370     clcnt = rscnt = 0;
371
372     if (!with_rsets) {
373         width = 0;
374         mrp_list_foreach(&class_list, clen, n) {
375             class = mrp_list_entry(clen, mrp_application_class_t, list);
376             if ((l = strlen(class->name)) > width)
377                 width = l;
378         }
379     }
380
381     PRINT("Application classes:\n");
382
383     mrp_list_foreach_back(&class_list, clen, n) {
384         class = mrp_list_entry(clen, mrp_application_class_t, list);
385         clcnt++;
386
387         if (with_rsets)
388             PRINT("  %3u - %s ", class->priority, class->name);
389         else
390             PRINT("   %-*s ", width, class->name);
391
392         if (class->modal)
393             PRINT(" modal");
394         if (class->share)
395             PRINT(" share");
396
397         PRINT("\n");
398
399         if (!with_rsets)
400             continue;
401
402         for (zid = 0;   zid < MRP_ZONE_MAX;   zid++) {
403             zone = mrp_zone_find_by_id(zid);
404             list = class->resource_sets + zid;
405
406             if (!mrp_list_empty(list)) {
407                 if (!zone) {
408                     PRINT("           Resource-sets in zone %u:\n", zid);
409                 }
410                 else {
411                     PRINT("           Resource-sets in %s zone:", zone->name);
412                     p += mrp_zone_attribute_print(zone, p, e-p);
413                     PRINT("\n");
414                 }
415
416                 mrp_list_foreach_back(list, rsen, m) {
417                     rset = mrp_list_entry(rsen, mrp_resource_set_t,class.list);
418                     p += mrp_resource_set_print(rset, 13, p, e-p);
419                 }
420             }
421         }
422     }
423
424     if (!clcnt)
425         PRINT("   <none>\n");
426
427     return p - buf;
428
429 #undef PRINT
430 }
431
432
433 static void init_name_hash(void)
434 {
435     mrp_htbl_config_t  cfg;
436
437     if (!name_hash) {
438         cfg.nentry  = CLASS_MAX;
439         cfg.comp    = mrp_string_comp;
440         cfg.hash    = mrp_string_hash;
441         cfg.free    = NULL;
442         cfg.nbucket = cfg.nentry / 2;
443
444         name_hash = mrp_htbl_create(&cfg);
445
446         MRP_ASSERT(name_hash, "failed to make name_hash for resource classes");
447     }
448 }
449
450
451 static int add_to_name_hash(mrp_application_class_t *class)
452 {
453     MRP_ASSERT(class && class->name, "invalid argument");
454
455     init_name_hash();
456
457     if (!mrp_htbl_insert(name_hash, (void *)class->name, class))
458         return -1;
459
460     return 0;
461 }
462
463 #if 0
464 static void remove_from_name_hash(mrp_application_class_t *class)
465 {
466     mrp_application_class_t *deleted;
467
468     if (class && class->name && name_hash) {
469         deleted = mrp_htbl_remove(name_hash, (void *)class->name, false);
470
471         MRP_ASSERT(!deleted || deleted == class, "confused with data "
472                    "structures when deleting resource-class from name hash");
473
474         /* in case we were not compiled with debug enabled */
475         if (deleted != class) {
476             mrp_log_error("confused with data structures when deleting "
477                           "resource-class '%s' from name hash", class->name);
478         }
479     }
480 }
481 #endif
482
483
484 static mqi_handle_t get_database_table(void)
485 {
486     MQI_COLUMN_DEFINITION_LIST(coldefs,
487         MQI_COLUMN_DEFINITION( "name"     , MQI_VARCHAR(NAME_LENGTH) ),
488         MQI_COLUMN_DEFINITION( "priority" , MQI_UNSIGNED             )
489     );
490
491     MQI_INDEX_DEFINITION(indexdef,
492         MQI_INDEX_COLUMN("priority")
493     );
494
495     static mqi_handle_t  table = MQI_HANDLE_INVALID;
496     static char         *name  = "application_classes";
497
498     if (table == MQI_HANDLE_INVALID) {
499         mqi_open();
500
501         table = MQI_CREATE_TABLE(name, MQI_TEMPORARY, coldefs, indexdef);
502
503         if (table == MQI_HANDLE_INVALID)
504             mrp_log_error("Can't create table '%s': %s", name,strerror(errno));
505     }
506
507     return table;
508 }
509
510 static void insert_into_application_class_table(const char *name, uint32_t pri)
511 {
512     MQI_COLUMN_SELECTION_LIST(cols,
513         MQI_COLUMN_SELECTOR(CLASS_NAME_IDX, class_row_t, class_name),
514         MQI_COLUMN_SELECTOR(PRIORITY_IDX  , class_row_t, priority  )
515     );
516
517     class_row_t   row;
518     mqi_handle_t  table   = get_database_table();
519     class_row_t  *rows[2] = {&row, NULL};
520
521     MRP_ASSERT(name, "invalid argument");
522     MRP_ASSERT(table != MQI_HANDLE_INVALID, "database problem");
523
524     row.class_name = name;
525     row.priority = pri;
526
527     if (MQI_INSERT_INTO(table, cols, rows) != 1)
528         mrp_log_error("Failed to add application class '%s' to database",name);
529 }
530
531
532
533 /*
534  * Local Variables:
535  * c-basic-offset: 4
536  * indent-tabs-mode: nil
537  * End:
538  *
539  */