resource: add callback support for events
[profile/ivi/murphy.git] / src / resource / resource-owner.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 <ctype.h>
33 #include <errno.h>
34
35 #include <murphy/common/mm.h>
36 #include <murphy/common/hashtbl.h>
37 #include <murphy/common/utils.h>
38 #include <murphy/common/log.h>
39
40 #include <murphy/resource/config-api.h>
41 #include <murphy-db/mqi.h>
42
43 #include "resource-owner.h"
44 #include "resource-class.h"
45 #include "resource-set.h"
46 #include "resource.h"
47 #include "zone.h"
48
49
50 #define RESOURCE_MAX        (sizeof(mrp_resource_mask_t) * 8)
51 #define NAME_LENGTH          24
52
53 #define ZONE_ID_IDX          0
54 #define ZONE_NAME_IDX        1
55 #define CLASS_NAME_IDX       2
56 #define FIRST_ATTRIBUTE_IDX  3
57
58 typedef struct {
59     uint32_t          zone_id;
60     const char       *zone_name;
61     const char       *class_name;
62     mrp_attr_value_t  attrs[MQI_COLUMN_MAX];
63 } owner_row_t;
64
65 static mrp_resource_owner_t  resource_owners[MRP_ZONE_MAX * RESOURCE_MAX];
66 static mqi_handle_t          owner_tables[RESOURCE_MAX];
67
68 static mrp_resource_owner_t *get_owner(uint32_t, uint32_t);
69 static void reset_owners(uint32_t, mrp_resource_owner_t *);
70 static bool grant_ownership(mrp_resource_owner_t *, mrp_zone_t *,
71                             mrp_resource_class_t *, mrp_resource_set_t *,
72                             mrp_resource_t *);
73 static bool advice_ownership(mrp_resource_owner_t *, mrp_zone_t *,
74                              mrp_resource_class_t *, mrp_resource_set_t *,
75                              mrp_resource_t *);
76
77 static void manager_start_transaction(mrp_zone_t *);
78 static void manager_end_transaction(mrp_zone_t *);
79
80 static void delete_resource_owner(mrp_zone_t *, mrp_resource_t *);
81 static void insert_resource_owner(mrp_zone_t *, mrp_resource_class_t *,
82                                   mrp_resource_t *);
83 static void update_resource_owner(mrp_zone_t *, mrp_resource_class_t *,
84                                   mrp_resource_t *);
85 static void set_attr_descriptors(mqi_column_desc_t *, mrp_resource_t *);
86
87
88 int mrp_resource_owner_create_database_table(mrp_resource_def_t *rdef)
89 {
90     MQI_COLUMN_DEFINITION_LIST(base_coldefs,
91         MQI_COLUMN_DEFINITION( "zone_id"       , MQI_UNSIGNED            , 0 ),
92         MQI_COLUMN_DEFINITION( "zone_name"     , MQI_VARCHAR(NAME_LENGTH), 0 ),
93         MQI_COLUMN_DEFINITION( "resource_class", MQI_VARCHAR(NAME_LENGTH), 0 )
94     );
95
96     MQI_INDEX_DEFINITION(indexdef,
97         MQI_INDEX_COLUMN("zone_id")
98     );
99
100     static bool initialized = false;
101
102     char name[256];
103     mqi_column_def_t  coldefs[MQI_COLUMN_MAX + 1];
104     mqi_column_def_t *col;
105     mrp_attr_def_t *atd;
106     mqi_handle_t table;
107     char c, *p;
108     size_t i,j;
109
110     if (!initialized) {
111         mqi_open();
112         for (i = 0;  i < RESOURCE_MAX;  i++)
113             owner_tables[i] = MQI_HANDLE_INVALID;
114         initialized = true;
115     }
116
117     MRP_ASSERT(sizeof(base_coldefs) < sizeof(coldefs),"too many base columns");
118     MRP_ASSERT(rdef, "invalid argument");
119     MRP_ASSERT(rdef->id < RESOURCE_MAX, "confused with data structures");
120     MRP_ASSERT(owner_tables[rdef->id] == MQI_HANDLE_INVALID,
121                "owner table already exist");
122
123     snprintf(name, sizeof(name), "%s_owner", rdef->name);
124     for (p = name; (c = *p);  p++) {
125         if (!isascii(c) || (!isalnum(c) && c != '_'))
126             *p = '_';
127     }
128
129     j = MQI_DIMENSION(base_coldefs) - 1;
130     memcpy(coldefs, base_coldefs, j * sizeof(mqi_column_def_t));
131
132     for (i = 0;  i < rdef->nattr && j < MQI_COLUMN_MAX;  i++, j++) {
133         col = coldefs + j;
134         atd = rdef->attrdefs + i;
135
136         col->name   = atd->name;
137         col->type   = atd->type;
138         col->length = (col->type == mqi_string) ? NAME_LENGTH : 0;
139         col->flags  = 0;
140     }
141
142     memset(coldefs + j, 0, sizeof(mqi_column_def_t));
143
144     table = MQI_CREATE_TABLE(name, MQI_TEMPORARY, coldefs, indexdef);
145
146     if (table == MQI_HANDLE_INVALID) {
147         mrp_log_error("Can't create table '%s': %s", name, strerror(errno));
148         return -1;
149     }
150
151     owner_tables[rdef->id] = table;
152
153     return 0;
154 }
155
156
157 void mrp_resource_owner_update_zone(uint32_t zoneid, uint32_t reqid)
158 {
159     typedef struct {
160         uint32_t reqid;
161         mrp_resource_set_t *rset;
162     } event_t;
163
164     mrp_resource_owner_t oldowners[RESOURCE_MAX];
165     mrp_resource_owner_t backup[RESOURCE_MAX];
166     mrp_zone_t *zone;
167     mrp_resource_class_t *class;
168     mrp_resource_set_t *rset;
169     mrp_resource_t *res;
170     mrp_resource_def_t *rdef;
171     mrp_resource_mgr_ftbl_t *ftbl;
172     mrp_resource_owner_t *owner, *old;
173     mrp_resource_mask_t mask;
174     mrp_resource_mask_t mandatory;
175     mrp_resource_mask_t grant;
176     mrp_resource_mask_t advice;
177     void *clc, *rsc, *rc;
178     uint32_t rid;
179     uint32_t rcnt;
180     bool changed;
181     uint32_t nevent, maxev;
182     event_t *events, *ev, *lastev;
183
184     MRP_ASSERT(zoneid < MRP_ZONE_MAX, "invalid argument");
185
186     zone = mrp_zone_find_by_id(zoneid);
187
188     MRP_ASSERT(zone, "zone is not defined");
189
190     maxev  = mrp_get_resource_set_count();
191     nevent = 0;
192     events = mrp_alloc(sizeof(event_t) * maxev);
193
194     MRP_ASSERT(events, "Memory alloc failure. Can't update zone");
195
196     reset_owners(zoneid, oldowners);
197     manager_start_transaction(zone);
198
199
200     rcnt = mrp_resource_definition_count();
201     clc  = NULL;
202
203     while ((class = mrp_resource_class_iterate_classes(&clc))) {
204         rsc = NULL;
205
206         while ((rset = mrp_resource_class_iterate_rsets(class,zoneid,&rsc))) {
207             mandatory = rset->resource.mask.mandatory;
208             grant = 0;
209             advice = 0;
210             rc = NULL;
211
212             switch (rset->request.type) {
213
214             case mrp_resource_acquire:
215                 while ((res = mrp_resource_set_iterate_resources(rset, &rc))) {
216                     rdef  = res->def;
217                     rid   = rdef->id;
218                     owner = get_owner(zoneid, rid);
219
220                     backup[rid] = *owner;
221
222                     if (grant_ownership(owner, zone, class, rset, res))
223                         grant |= ((mrp_resource_mask_t)1 << rid);
224                 }
225                 if (mandatory && (grant & mandatory) == mandatory)
226                     advice = grant;
227                 else {
228                     /* rollback, ie. restore the backed up state */
229                     rc = NULL;
230                     while ((res=mrp_resource_set_iterate_resources(rset,&rc))){
231                          rdef  = res->def;
232                          rid   = rdef->id;
233                          mask  = (mrp_resource_mask_t)1 << rid;
234                          owner = get_owner(zoneid, rid);
235                         *owner = backup[rid];
236
237                         if ((grant & mask)) {
238                             if ((ftbl = rdef->manager.ftbl) && ftbl->free)
239                                 ftbl->free(zone, res, rdef->manager.userdata);
240                         }
241
242                         if (advice_ownership(owner, zone, class, rset, res))
243                             advice |= mask;
244                     }
245
246                     /* nothing is granted */
247                     grant = 0;
248                 }
249                 break;
250
251             case mrp_resource_release:
252                 while ((res = mrp_resource_set_iterate_resources(rset, &rc))) {
253                     rdef  = res->def;
254                     rid   = rdef->id;
255                     owner = get_owner(zoneid, rid);
256
257                     if (advice_ownership(owner, zone, class, rset, res))
258                         advice |= ((uint32_t)1 << rid);
259                 }
260                 if (mandatory && (advice & mandatory) != mandatory)
261                     advice = 0;
262                 break;
263
264             default:
265                 break;
266             }
267
268             changed = false;
269
270             if (grant != rset->resource.mask.grant) {
271                 rset->resource.mask.grant = grant;
272                 changed = true;
273             }
274
275             if (advice != rset->resource.mask.advice) {
276                 rset->resource.mask.advice = advice;
277                 changed = true;
278             }
279
280             if (changed && rset->event) {
281                 ev = events + nevent++;
282
283                 ev->reqid = (reqid == rset->request.id) ? reqid : 0;
284                 ev->rset  = rset;
285             }
286         } /* while rset */
287     } /* while class */
288
289     manager_end_transaction(zone);
290
291     for (lastev = (ev = events) + nevent;     ev < lastev;     ev++) {
292         rset = ev->rset;
293         rset->event(ev->reqid, rset, rset->user_data);
294     }
295
296     mrp_free(events);
297
298     for (rid = 0;  rid < rcnt;  rid++) {
299         owner = get_owner(zoneid, rid);
300         old   = oldowners + rid;
301
302         if (owner->class != old->class ||
303             owner->rset  != old->rset  ||
304             owner->res   != old->res     )
305         {
306             if (!owner->res)
307                 delete_resource_owner(zone, old->res);
308             else if (!old->res)
309                 insert_resource_owner(zone, owner->class, owner->res);
310             else
311                 update_resource_owner(zone, owner->class, owner->res);
312         }
313     }
314 }
315
316 int mrp_resource_owner_print(char *buf, int len)
317 {
318 #define PRINT(fmt, args...)  if (p<e) { p += snprintf(p, e-p, fmt , ##args); }
319
320     mrp_zone_t *zone;
321     mrp_resource_owner_t *owner;
322     mrp_resource_class_t *class;
323     mrp_resource_set_t *rset;
324     mrp_resource_t *res;
325     mrp_resource_def_t *rdef;
326     uint32_t rcnt, rid;
327     uint32_t zcnt, zid;
328     char *p, *e;
329
330     MRP_ASSERT(buf && len > 0, "invalid argument");
331
332     rcnt = mrp_resource_definition_count();
333     zcnt = mrp_zone_count();
334
335     e = (p = buf) + len;
336
337     PRINT("Resource owners:\n");
338
339     for (zid = 0;  zid < zcnt;  zid++) {
340         zone = mrp_zone_find_by_id(zid);
341
342         if (!zone) {
343             PRINT("   Zone %u:\n", zid);
344         }
345         else {
346             PRINT("   Zone %s:", zone->name);
347             p += mrp_zone_attribute_print(zone, p, e-p);
348             PRINT("\n");
349         }
350
351         for (rid = 0;   rid < rcnt;   rid++) {
352             if (!(rdef = mrp_resource_definition_find_by_id(rid)))
353                 continue;
354
355             PRINT("      %-15s: ", rdef->name);
356
357             owner = get_owner(zid, rid);
358
359             if (!(class = owner->class) ||
360                 !(rset  = owner->rset ) ||
361                 !(res   = owner->res  )    )
362             {
363                 PRINT("<nobody>");
364             }
365             else {
366                 MRP_ASSERT(rdef == res->def, "confused with data structures");
367
368                 PRINT("%-15s", class->name);
369
370                 p += mrp_resource_attribute_print(res, p, e-p);
371             }
372
373             PRINT("\n");
374         }
375     }
376
377     return p - buf;
378
379 #undef PRINT
380 }
381
382
383 static mrp_resource_owner_t *get_owner(uint32_t zone, uint32_t resid)
384 {
385     MRP_ASSERT(zone < MRP_ZONE_MAX && resid < RESOURCE_MAX,"invalid argument");
386
387     return resource_owners + (zone * RESOURCE_MAX + resid);
388 }
389
390 static void reset_owners(uint32_t zone, mrp_resource_owner_t *oldowners)
391 {
392     void   *ptr  = get_owner(zone, 0);
393     size_t  size = sizeof(mrp_resource_owner_t) * RESOURCE_MAX;
394
395     if (oldowners)
396         memcpy(oldowners, ptr, size);
397
398     memset(ptr, 0, size);
399 }
400
401 static bool grant_ownership(mrp_resource_owner_t *owner,
402                             mrp_zone_t           *zone,
403                             mrp_resource_class_t *class,
404                             mrp_resource_set_t   *rset,
405                             mrp_resource_t       *res)
406 {
407     mrp_resource_def_t      *rdef = res->def;
408     mrp_resource_mgr_ftbl_t *ftbl = rdef->manager.ftbl;
409     bool                     set_owner = false;
410
411     /*
412       if (forbid_grant())
413         return false;
414      */
415
416     do { /* not a loop */
417         if (!owner->class && !owner->rset) {
418             /* nobody owns this, so grab it */
419             set_owner = true;
420             break;
421         }
422
423         if (owner->class == class && owner->rset == rset) {
424             /* we happen to already own it */
425             break;
426         }
427
428         if (owner->share) {
429             /* OK, someone else owns it bu
430                the owner is ready to share it with us */
431             owner->share = res->shared;
432             break;
433         }
434
435         return false;
436
437     } while(0);
438
439     if (ftbl && ftbl->allocate) {
440         if (!ftbl->allocate(zone, res, rdef->manager.userdata))
441             return false;
442     }
443
444     if (set_owner) {
445         owner->class = class;
446         owner->rset  = rset;
447         owner->res   = res;
448         owner->share = res->shared;
449     }
450
451     return true;
452 }
453
454 static bool advice_ownership(mrp_resource_owner_t *owner,
455                              mrp_zone_t           *zone,
456                              mrp_resource_class_t *class,
457                              mrp_resource_set_t   *rset,
458                              mrp_resource_t       *res)
459 {
460     mrp_resource_def_t      *rdef = res->def;
461     mrp_resource_mgr_ftbl_t *ftbl = rdef->manager.ftbl;
462
463     (void)zone;
464
465     /*
466       if (forbid_grant())
467         return false;
468      */
469
470     do { /* not a loop */
471         if (!owner->class && !owner->rset)
472             /* nobody owns this */
473             break;
474
475         if (owner->share)
476             /* someone else owns it but it can be shared */
477             break;
478
479
480         if (owner->class == class) {
481             if (owner->rset->class.priority == rset->class.priority)
482                 break;
483         }
484
485         return false;
486
487     } while(0);
488
489     if (ftbl && ftbl->advice) {
490         if (!ftbl->advice(zone, res, rdef->manager.userdata))
491             return false;
492     }
493
494     return true;
495 }
496
497 static void manager_start_transaction(mrp_zone_t *zone)
498 {
499     mrp_resource_def_t *rdef;
500     mrp_resource_mgr_ftbl_t *ftbl;
501     void *cursor = NULL;
502
503     while ((rdef = mrp_resource_definition_iterate_manager(&cursor))) {
504         ftbl = rdef->manager.ftbl;
505
506         MRP_ASSERT(ftbl, "confused with data structures");
507
508         if (ftbl->init)
509             ftbl->init(zone, rdef->manager.userdata);
510     }
511 }
512
513 static void manager_end_transaction(mrp_zone_t *zone)
514 {
515     mrp_resource_def_t *rdef;
516     mrp_resource_mgr_ftbl_t *ftbl;
517     void *cursor = NULL;
518
519     while ((rdef = mrp_resource_definition_iterate_manager(&cursor))) {
520         ftbl = rdef->manager.ftbl;
521
522         MRP_ASSERT(ftbl, "confused with data structures");
523
524         if (ftbl->commit)
525             ftbl->commit(zone, rdef->manager.userdata);
526     }
527 }
528
529
530 static void delete_resource_owner(mrp_zone_t *zone, mrp_resource_t *res)
531 {
532     static uint32_t zone_id;
533
534     MQI_WHERE_CLAUSE(where,
535         MQI_EQUAL( MQI_COLUMN(0), MQI_UNSIGNED_VAR(zone_id) )
536     );
537
538     mrp_resource_def_t *rdef;
539     int n;
540
541     MRP_ASSERT(res, "invalid argument");
542
543     rdef = res->def;
544     zone_id = zone->id;
545
546     if ((n = MQI_DELETE(owner_tables[rdef->id], where)) != 1)
547         mrp_log_error("Could not delete resource owner");
548 }
549
550 static void insert_resource_owner(mrp_zone_t *zone,
551                                   mrp_resource_class_t *class,
552                                   mrp_resource_t *res)
553 {
554     mrp_resource_def_t *rdef = res->def;
555     uint32_t i;
556     int n;
557     owner_row_t row;
558     owner_row_t *rows[2];
559     mqi_column_desc_t cdsc[FIRST_ATTRIBUTE_IDX + MQI_COLUMN_MAX + 1];
560
561     MRP_ASSERT(FIRST_ATTRIBUTE_IDX + rdef->nattr <= MQI_COLUMN_MAX,
562                "too many attributes for a table");
563
564     row.zone_id    = zone->id;
565     row.zone_name  = zone->name;
566     row.class_name = class->name;
567     memcpy(row.attrs, res->attrs, rdef->nattr * sizeof(mrp_attr_value_t));
568
569     i = 0;
570     cdsc[i].cindex = ZONE_ID_IDX;
571     cdsc[i].offset = MQI_OFFSET(owner_row_t, zone_id);
572
573     i++;
574     cdsc[i].cindex = ZONE_NAME_IDX;
575     cdsc[i].offset = MQI_OFFSET(owner_row_t, zone_name);
576
577     i++;
578     cdsc[i].cindex = CLASS_NAME_IDX;
579     cdsc[i].offset = MQI_OFFSET(owner_row_t, class_name);
580
581     set_attr_descriptors(cdsc + (i+1), res);
582
583     rows[0] = &row;
584     rows[1] = NULL;
585
586     if ((n = MQI_INSERT_INTO(owner_tables[rdef->id], cdsc, rows)) != 1)
587         mrp_log_error("can't insert row into owner table");
588 }
589
590 static void update_resource_owner(mrp_zone_t *zone,
591                                   mrp_resource_class_t *class,
592                                   mrp_resource_t *res)
593 {
594     static uint32_t zone_id;
595
596     MQI_WHERE_CLAUSE(where,
597         MQI_EQUAL( MQI_COLUMN(0), MQI_UNSIGNED_VAR(zone_id) )
598     );
599
600     mrp_resource_def_t *rdef = res->def;
601     uint32_t i;
602     int n;
603     owner_row_t row;
604     mqi_column_desc_t cdsc[FIRST_ATTRIBUTE_IDX + MQI_COLUMN_MAX + 1];
605
606     zone_id = zone->id;
607
608     MRP_ASSERT(1 + rdef->nattr <= MQI_COLUMN_MAX,
609                "too many attributes for a table");
610
611     row.class_name = class->name;
612     memcpy(row.attrs, res->attrs, rdef->nattr * sizeof(mrp_attr_value_t));
613
614     i = 0;
615     cdsc[i].cindex = CLASS_NAME_IDX;
616     cdsc[i].offset = MQI_OFFSET(owner_row_t, class_name);
617
618     set_attr_descriptors(cdsc + (i+1), res);
619
620
621     if ((n = MQI_UPDATE(owner_tables[rdef->id], cdsc, &row, where)) != 1)
622         mrp_log_error("can't update row in owner table");
623 }
624
625
626 static void set_attr_descriptors(mqi_column_desc_t *cdsc, mrp_resource_t *res)
627 {
628     mrp_resource_def_t *rdef = res->def;
629     uint32_t i,j;
630     int o;
631
632     for (i = j = 0;  j < rdef->nattr;  j++) {
633         switch (rdef->attrdefs[j].type) {
634         case mqi_string:   o = MQI_OFFSET(owner_row_t,attrs[j].string);  break;
635         case mqi_integer:  o = MQI_OFFSET(owner_row_t,attrs[j].integer); break;
636         case mqi_unsignd:  o = MQI_OFFSET(owner_row_t,attrs[j].unsignd); break;
637         case mqi_floating: o = MQI_OFFSET(owner_row_t,attrs[j].floating);break;
638         default:           /* skip this */                            continue;
639         }
640
641         cdsc[i].cindex = FIRST_ATTRIBUTE_IDX + j;
642         cdsc[i].offset = o;
643         i++;
644     }
645
646     cdsc[i].cindex = -1;
647     cdsc[i].offset =  1;
648 }
649
650
651 /*
652  * Local Variables:
653  * c-basic-offset: 4
654  * indent-tabs-mode: nil
655  * End:
656  *
657  */