Remove the release invoke variable
[platform/core/multimedia/mm-resource-manager.git] / src / daemon / mm_resource_manager_daemon_priv.c
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <glib.h>
18 #include <unistd.h>
19 #include <poll.h>
20 #include <inttypes.h>
21 #include <errno.h>
22 #ifdef RM_GCOV_TEST
23 #include <gcov.h>
24 #endif
25
26 #include "daemon/mm_resource_manager_daemon_priv.h"
27 #include "daemon/mm_resource_manager_daemon_conf.h"
28 #include "daemon/mm_resource_manager_daemon_dbus.h"
29 #include "daemon/backend/mm_resource_manager_backend.h"
30 #include "common/mm_resource_manager_utils.h"
31
32 #define MM_RESOURCE_MANAGER_RESERVED_RM_ARRAY_SIZE 64 /* preallocated size */
33 #define MM_RESOURCE_MANAGER_RESERVED_PART_ARRAY_SIZE 4 /* preallocated size */
34 #define MM_RESOURCE_MANAGER_RESERVED_CALLBACK_ARRAY_SIZE 32 /* preallocated size */
35 #define MM_RESOURCE_MANGER_NOT_FOUND -1
36 #define RELEASE_CB_SYNC_TIMEOUT 5000 /* milliseconds */
37
38 typedef enum {
39         MM_RESOURCE_MANAGER_RES_STATE_FOR_ACQUIRE,
40         MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED,
41         MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE
42 } mm_resource_manager_res_state_e;
43
44 typedef struct {
45         mm_resource_manager_res_state_e state;
46
47         /* NULL means, the resource is acquired fully or is not acquired at all */
48         GArray *parts;
49
50 } mm_resource_manager_dmn_res_s;
51 typedef mm_resource_manager_dmn_res_s *mm_resource_manager_dmn_res_p;
52
53 typedef struct {
54         mm_resource_manager_id id;
55         mm_resource_manager_app_class_e app_class;
56         mm_resource_manager_res_type_e type;
57         mm_resource_manager_res_volume volume;
58         int pid;
59         /* if an element is NULL, there is no such a resource for the current platform. */
60         mm_resource_manager_dmn_res_p resources[MM_RESOURCE_MANAGER_RES_TYPE_MAX];
61
62 } mm_resource_manager_dmn_s;
63 typedef mm_resource_manager_dmn_s *mm_resource_manager_dmn_p;
64
65 typedef struct {
66         mm_resource_manager_dmn_p manager;
67         mm_resource_manager_res_type_e type;
68         mm_resource_manager_res_volume volume;
69         int pid;
70 } mm_resource_manager_dmn_release_cb_request_s;
71
72 static const char *res_state_str[] = {
73         "FOR ACQUIRE",
74         "ACQUIRED",
75         "FOR RELEASE",
76 };
77
78 static GPtrArray *managers;
79 static int res_count[MM_RESOURCE_MANAGER_RES_TYPE_MAX];
80
81 static void __destroy_resource(mm_resource_manager_dmn_res_p res);
82 static void __destroy_manager(void *m);
83 static int __search_manager_index(mm_resource_manager_id id);
84 static inline mm_resource_manager_dmn_p __search_manager(mm_resource_manager_id id);
85 static mm_resource_manager_error_e __check_release_requests(mm_resource_manager_dmn_p manager,
86                 mm_resource_manager_dmn_res_request_s *releases);
87 static mm_resource_manager_dmn_res_request_s *__create_dmn_res_requests(
88                 mm_resource_manager_dmn_res_request_s *releases, mm_resource_manager_dmn_res_request_s *acquires);
89 static mm_resource_manager_error_e __check_requests_conflict(mm_resource_manager_dmn_p manager,
90                 mm_resource_manager_dmn_res_request_s *requests);
91 static void __sync_increase_acquire_requests(mm_resource_manager_dmn_res_request_s *increases,
92                 mm_resource_manager_dmn_res_request_s *acquires);
93 static void __handle_release_requests(mm_resource_manager_dmn_p manager, mm_resource_manager_dmn_res_request_s *requests);
94 static GArray* __handle_acquire_requests(mm_resource_manager_dmn_p manager, mm_resource_manager_dmn_res_request_s *requests);
95 static void __handle_release_callbacks(GArray *requests);
96 static int __open_release_cb_sync_fd(void);
97 static void __close_release_cb_sync_fd(int fd);
98 static gboolean __is_process_alive(int pid);
99 static inline void __add_cb_request(GArray *cb_requests, mm_resource_manager_dmn_p mgr,
100                 mm_resource_manager_res_type_e type, mm_resource_manager_res_volume volume);
101 static void __destroy_all_resources(mm_resource_manager_dmn_p manager);
102 static gboolean __poll(struct pollfd sync, mm_resource_manager_id id);
103 static gboolean __wait_for_release_cb_sync(mm_resource_manager_id id, int sync_fd);
104
105
106
107
108 gboolean _mmrm_dmn_init(void)
109 {
110         MM_RM_RETVMS_IF(access(RELEASE_CB_SYNC_PATH, F_OK) != 0, FALSE,
111                 "access(%s) is failed errno[%d]", RELEASE_CB_SYNC_PATH, errno);
112
113         MM_RM_RETVM_IF(mm_resource_manager_backend_init() != MM_RESOURCE_MANAGER_ERROR_NONE,
114                 FALSE, "Back-end cannot be initialized");
115
116         if (_mmrm_dmn_dbus_init() != MM_RESOURCE_MANAGER_ERROR_NONE) {
117                 mm_resource_manager_backend_deinit();
118                 MM_RM_ERROR("D-bus server cannot be created");
119                 return FALSE;
120         }
121
122         managers = g_ptr_array_new_full(MM_RESOURCE_MANAGER_RESERVED_RM_ARRAY_SIZE, __destroy_manager);
123         MM_RM_RETVM_IF(managers == NULL, FALSE, "Daemon cannot be initialized");
124
125         return TRUE;
126 }
127
128 gboolean _mmrm_dmn_deinit(void)
129 {
130         int i;
131
132         if (mm_resource_manager_backend_deinit() != MM_RESOURCE_MANAGER_ERROR_NONE)
133                 MM_RM_ERROR("Error during back-end deinitialization");
134
135         if (_mmrm_dmn_dbus_deinit() != MM_RESOURCE_MANAGER_ERROR_NONE)
136                 MM_RM_ERROR("Error during d-bus server shutdown");
137
138         if (managers) {
139                 for (i = 0; i < managers->len; i++)
140                         __destroy_all_resources((mm_resource_manager_dmn_p)managers->pdata[i]);
141
142                 mm_resource_manager_backend_commit_all();
143                 g_ptr_array_free(managers, TRUE);
144         } else {
145                 MM_RM_ERROR("Trying to deinit uninitialized daemon");
146         }
147
148         if (unlink(RELEASE_CB_SYNC_PATH) == -1)
149                 MM_RM_ERROR("Release callback sync cannot be removed");
150
151         return TRUE;
152 }
153
154 mm_resource_manager_error_e _mmrm_dmn_create(
155                 mm_resource_manager_app_class_e app_class, mm_resource_manager_id *id)
156 {
157         mm_resource_manager_dmn_p mgr;
158         mm_resource_manager_conf_s *conf = mm_resource_manager_get_conf();
159         int i;
160
161         MM_RM_RETVM_IF(app_class < 0 || app_class >= MM_RESOURCE_MANAGER_APP_CLASS_MAX,
162                         MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER, "Wrong app class");
163
164         MM_RM_RETVM_IF(conf->priority[app_class] == MM_RESOURCE_MANAGER_NO_APP_CLASS,
165                         MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER, "App class is not supported for the platform");
166
167         mgr = g_new0(mm_resource_manager_dmn_s, 1);
168         MM_RM_RAND64(mgr->id);
169         mgr->app_class = app_class;
170         for (i = 0; i < MM_RESOURCE_MANAGER_RES_TYPE_MAX; i++) {
171                 mgr->resources[i] = conf->max_volume[i] == MM_RESOURCE_MANAGER_NO_RES ?
172                                 NULL : g_malloc0(sizeof(mm_resource_manager_dmn_s));
173         }
174
175         g_ptr_array_add(managers, mgr);
176
177         MM_RM_INFO("managers length %d", managers->len);
178
179         *id = mgr->id;
180
181         return MM_RESOURCE_MANAGER_ERROR_NONE;
182 }
183
184 mm_resource_manager_error_e _mmrm_dmn_destroy(mm_resource_manager_id id)
185 {
186         int idx = __search_manager_index(id);
187         mm_resource_manager_conf_s *conf = mm_resource_manager_get_conf();
188         mm_resource_manager_res_type_e type = MM_RESOURCE_MANAGER_RES_TYPE_MAX;
189         mm_resource_manager_dmn_p i_mgr = __search_manager(id);
190
191         MM_RM_RETVM_IF(idx == MM_RESOURCE_MANGER_NOT_FOUND, MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER,
192                         "Resource manager #%"PRIu64" doesn't exist", _mm_rm_hash64(id));
193         MM_RM_RETVM_IF(conf == NULL, MM_RESOURCE_MANAGER_ERROR_NONE, "conf is null");
194
195         type = i_mgr->type;
196
197         __destroy_all_resources(i_mgr);
198         mm_resource_manager_backend_commit_all();
199
200         g_ptr_array_remove_index_fast(managers, idx);
201
202         MM_RM_INFO("remaining managers #%d type %s available volume %d",
203                 managers->len, _mm_resource_manager_get_res_str(type), conf->max_volume[type]);
204
205 #ifdef RM_GCOV_TEST
206         __gcov_flush();
207 #endif
208
209         return MM_RESOURCE_MANAGER_ERROR_NONE;
210 }
211
212 mm_resource_manager_error_e _mmrm_dmn_commit(mm_resource_manager_id id,
213                 mm_resource_manager_dmn_res_request_s *releases, mm_resource_manager_dmn_res_request_s *acquires)
214 {
215         mm_resource_manager_dmn_p mgr = __search_manager(id);
216         mm_resource_manager_error_e ret = MM_RESOURCE_MANAGER_ERROR_NONE;
217         mm_resource_manager_dmn_res_request_s *increases = NULL;
218         GArray *cb_requests;
219
220         MM_RM_RETVM_IF(mgr == NULL, MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER,
221                         "Resource manager #%"PRIu64" doesn't exist", _mm_rm_hash64(id));
222         MM_RM_RETVM_IF((releases == NULL || releases[0].type == MM_RESOURCE_MANAGER_NO_RES) &&
223                         (acquires == NULL || acquires[0].type == MM_RESOURCE_MANAGER_NO_RES),
224                         MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER, "Commit request is empty");
225
226         MM_RM_INFO("RM #%"PRIu64" mgr [%p] release type (%s) acquires type (%s)", _mm_rm_hash64(mgr->id), mgr,
227                 releases[0].type == MM_RESOURCE_MANAGER_NO_RES ? "nil" : _mm_resource_manager_get_res_str(releases[0].type),
228                 acquires[0].type == MM_RESOURCE_MANAGER_NO_RES ? "nil" : _mm_resource_manager_get_res_str(acquires[0].type));
229
230         ret = __check_release_requests(mgr, releases);
231         MM_RM_RETVM_IF(ret != MM_RESOURCE_MANAGER_ERROR_NONE, ret, "check_release_requests is failed [0x%x]", ret);
232
233         increases = __create_dmn_res_requests(releases, acquires);
234         MM_RM_RETVM_IF(increases == NULL, MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION, "create_increase_requests is failed");
235
236         ret = __check_requests_conflict(mgr, increases);
237         if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
238                 __sync_increase_acquire_requests(increases, acquires);
239                 g_free(increases);
240                 return ret;
241         }
242         g_free(increases);
243
244         __handle_release_requests(mgr, releases);
245         cb_requests = __handle_acquire_requests(mgr, acquires);
246         MM_RM_RETVM_IF(cb_requests == NULL, MM_RESOURCE_MANAGER_ERROR_NOT_ENOUGH, "not enough free resource volume");
247         __handle_release_callbacks(cb_requests);
248         if (!mm_resource_manager_backend_commit_all()) {
249                 /*
250                  * The commit_all function can fail only if resource management back-end is not solely used by mm-resource-manager or
251                  * if the back-end cannot acquire/release a resource because of some internal error.
252                  *
253                  * Initiate status disconnected and reload the back-end and the RM daemon.
254                  */
255                 kill(getpid(), SIGHUP);
256                 ret = MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION;
257         }
258         g_array_free(cb_requests, TRUE);
259
260         return ret;
261 }
262
263 void _mmrm_dmn_status_callback(mm_resource_manager_status_e status)
264 {
265         _mmrm_dmn_dbus_status_callback(status);
266 }
267
268 static void __destroy_resource(mm_resource_manager_dmn_res_p res)
269 {
270         if (res == NULL)
271                 return;
272
273         if (res->parts)
274                 g_array_free(res->parts, TRUE);
275
276         g_free(res);
277 }
278
279 static void __destroy_manager(void *m)
280 {
281         mm_resource_manager_dmn_p mgr = (mm_resource_manager_dmn_p) m;
282         int i;
283
284         MM_RM_RETM_IF(mgr == NULL, "NULL pointer");
285
286         for (i = 0; i < MM_RESOURCE_MANAGER_RES_TYPE_MAX; i++)
287                 __destroy_resource(mgr->resources[i]);
288
289         g_free(mgr);
290 }
291
292 static int __search_manager_index(mm_resource_manager_id id)
293 {
294         int i;
295
296         for (i = managers->len - 1; i >= 0; i--) {
297                 if (((mm_resource_manager_dmn_p)managers->pdata[i])->id == id)
298                         return i;
299         }
300
301         return MM_RESOURCE_MANGER_NOT_FOUND;
302 }
303
304 static inline mm_resource_manager_dmn_p __search_manager(mm_resource_manager_id id)
305 {
306         int i = __search_manager_index(id);
307         return i == MM_RESOURCE_MANGER_NOT_FOUND ? NULL : managers->pdata[i];
308 }
309
310 static mm_resource_manager_error_e __check_release_requests(mm_resource_manager_dmn_p manager,
311                         mm_resource_manager_dmn_res_request_s *releases)
312 {
313         int i;
314         mm_resource_manager_conf_s *conf = mm_resource_manager_get_conf();
315         mm_resource_manager_res_type_e type = MM_RESOURCE_MANAGER_RES_TYPE_MAX;
316         const char *res_name = NULL;
317
318         MM_RM_RETVM_IF(conf == NULL, MM_RESOURCE_MANAGER_ERROR_NONE, "conf is null");
319         MM_RM_RETVM_IF(releases == NULL, MM_RESOURCE_MANAGER_ERROR_NONE, "requests is null");
320
321         for (; releases->type != MM_RESOURCE_MANAGER_NO_RES; releases++) {
322
323                 type = releases->type;
324                 res_name = _mm_resource_manager_get_res_str(type);
325
326                 MM_RM_RETVM_IF(manager->resources[type] == NULL,
327                         MM_RESOURCE_MANAGER_ERROR_NOT_SUPPORTED, "There is no resource %s for the platform", res_name);
328
329                 if (manager->resources[type]->state == MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED) {
330                         if (manager->resources[type]->parts == NULL) {
331                                 if (releases->volume != MM_RESOURCE_MANAGER_RES_VOLUME_FULL) {
332                                         MM_RM_ERROR("Resource %s is acquired fully, but a resource part is tried to be released", res_name);
333                                         return MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER;
334                                 }
335                         } else {
336                                 for (i = 0; i < manager->resources[type]->parts->len &&
337                                         ((mm_resource_manager_res_volume*)manager->resources[type]->parts->data)[i] != releases->volume; i++);
338                                 if (i == manager->resources[type]->parts->len) {
339                                         MM_RM_ERROR("Part of %s of volume %d is not acquired", res_name, releases->volume);
340                                         return MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER;
341                                 }
342                         }
343                 } else {
344                         MM_RM_ERROR("Resource %s is not acquired", res_name);
345                         return MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER;
346                 }
347                 MM_RM_DEBUG("Release requests are OK type %s available volume %d", res_name, conf->max_volume[type]);
348         }
349
350         return MM_RESOURCE_MANAGER_ERROR_NONE;
351 }
352
353 static mm_resource_manager_dmn_res_request_s *__create_dmn_res_requests(
354                 mm_resource_manager_dmn_res_request_s *releases, mm_resource_manager_dmn_res_request_s *acquires)
355 {
356         int i;
357         int result_len = 1;
358         mm_resource_manager_conf_s *conf = mm_resource_manager_get_conf();
359         mm_resource_manager_dmn_res_request_s *result = NULL;
360         mm_resource_manager_dmn_res_request_s *result_iter = NULL;
361         mm_resource_manager_res_volume resources[MM_RESOURCE_MANAGER_RES_TYPE_MAX] = { 0 };
362
363         MM_RM_RETVM_IF(conf == NULL, MM_RESOURCE_MANAGER_ERROR_NONE, "conf is null");
364
365         for (; acquires->type != MM_RESOURCE_MANAGER_NO_RES; acquires++) {
366
367                 if ((resources[acquires->type] > 0 || resources[acquires->type] == MM_RESOURCE_MANAGER_RES_VOLUME_FULL) &&
368                         acquires->volume == MM_RESOURCE_MANAGER_RES_VOLUME_FULL) {
369                         MM_RM_ERROR("The client tries to acquire %s by part and fully at once", _mm_resource_manager_get_res_str(acquires->type));
370                         return NULL;
371                 }
372
373                 if (acquires->volume == MM_RESOURCE_MANAGER_RES_VOLUME_FULL) {
374                         resources[acquires->type] = MM_RESOURCE_MANAGER_RES_VOLUME_FULL;
375                 } else {
376                         if (conf->max_volume[acquires->type] > 1)
377                                 resources[acquires->type] += acquires->volume;
378                         else
379                                 resources[acquires->type]++;
380                 }
381
382                 MM_RM_DEBUG("(type, vol) = (%d, %d)", acquires->type, resources[acquires->type]);
383         }
384
385         for (; releases->type != MM_RESOURCE_MANAGER_NO_RES; releases++) {
386                 if (resources[releases->type] > 1) {
387                         resources[releases->type] += releases->volume;
388                         MM_RM_INFO("type %s available volume %d", _mm_resource_manager_get_res_str(releases->type), resources[releases->type]);
389                 }
390         }
391
392         for (i = 0; i < MM_RESOURCE_MANAGER_RES_TYPE_MAX; i++)
393                 if (resources[i] > 0 || resources[i] == MM_RESOURCE_MANAGER_RES_VOLUME_FULL)
394                         result_len++;
395
396         result_iter = result = g_new0(mm_resource_manager_dmn_res_request_s, result_len);
397         for (i = 0; i < MM_RESOURCE_MANAGER_RES_TYPE_MAX; i++) {
398                 if (resources[i] > 0 || resources[i] == MM_RESOURCE_MANAGER_RES_VOLUME_FULL) {
399                         result_iter->type = i;
400                         result_iter->volume = resources[i];
401                         result_iter++;
402                 }
403         }
404         result_iter->type = MM_RESOURCE_MANAGER_NO_RES;
405
406         return result;
407 }
408
409 static mm_resource_manager_error_e __check_requests_conflict(mm_resource_manager_dmn_p manager,
410                         mm_resource_manager_dmn_res_request_s *requests)
411 {
412         mm_resource_manager_res_volume remaining_volume;
413         mm_resource_manager_dmn_p i_mgr;
414         mm_resource_manager_conf_s *conf = mm_resource_manager_get_conf();
415         int i, j, len;
416         mm_resource_manager_res_type_e type = MM_RESOURCE_MANAGER_RES_TYPE_MAX;
417         const char *res_name = NULL;
418
419         MM_RM_RETVM_IF(conf == NULL, MM_RESOURCE_MANAGER_ERROR_NONE, "conf is null");
420         MM_RM_RETVM_IF(manager == NULL, MM_RESOURCE_MANAGER_ERROR_NONE, "manager is null");
421         MM_RM_RETVM_IF(requests == NULL, MM_RESOURCE_MANAGER_ERROR_NONE, "requests is null");
422
423         len = managers->len;
424
425         for (; requests->type != MM_RESOURCE_MANAGER_NO_RES; requests++) {
426                 type = requests->type;
427                 res_name = _mm_resource_manager_get_res_str(type);
428
429                 MM_RM_RETVM_IF(type < MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_DECODER || type >= MM_RESOURCE_MANAGER_RES_TYPE_MAX,
430                         MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER, "wrong type %s", res_name);
431                 MM_RM_RETVM_IF(manager->resources[type] == NULL, MM_RESOURCE_MANAGER_ERROR_NOT_SUPPORTED,
432                         "There is no resource %s for the platform", res_name);
433
434                 remaining_volume = conf->max_volume[type];
435
436                 for (i = 0; i < len; i++) {
437                         i_mgr = (mm_resource_manager_dmn_p)managers->pdata[i];
438
439                         if (i_mgr != manager && conf->priority[i_mgr->app_class] > conf->priority[manager->app_class] &&
440                                 i_mgr->resources[type]->state == MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED) {
441                                 if (i_mgr->resources[type]->parts) {
442                                         if (requests->volume == MM_RESOURCE_MANAGER_RES_VOLUME_FULL) {
443                                                 requests->priority_error = TRUE;
444                                                 MM_RM_DEBUG("Resource conflict. Full volume is requested, but only part is available");
445                                                 return MM_RESOURCE_MANAGER_ERROR_LOW_PRIORITY;
446                                         } else {
447                                                 for (j = 0; j < i_mgr->resources[type]->parts->len; j++)
448                                                         remaining_volume -= g_array_index(i_mgr->resources[type]->parts, mm_resource_manager_res_volume, j);
449
450                                                 if (remaining_volume < requests->volume) {
451                                                         requests->priority_error = TRUE;
452                                                         MM_RM_DEBUG("Resource conflict. %d of %s are available, but %d required",
453                                                                         remaining_volume, res_name, requests->volume);
454                                                         return MM_RESOURCE_MANAGER_ERROR_LOW_PRIORITY;
455                                                 }
456                                         }
457                                 } else {
458                                         requests->priority_error = TRUE;
459                                         MM_RM_DEBUG("Resource conflict. %s is already acquired fully", res_name);
460                                         return MM_RESOURCE_MANAGER_ERROR_LOW_PRIORITY;
461                                 }
462                         }
463                 }
464         }
465
466         return MM_RESOURCE_MANAGER_ERROR_NONE;
467 }
468
469 static void __sync_increase_acquire_requests(mm_resource_manager_dmn_res_request_s *increases,
470                         mm_resource_manager_dmn_res_request_s *acquires)
471 {
472         mm_resource_manager_dmn_res_request_s *increase_iter;
473         mm_resource_manager_dmn_res_request_s *acquire_iter;
474
475         for (increase_iter = increases; increase_iter->type != MM_RESOURCE_MANAGER_NO_RES; increase_iter++) {
476                 for (acquire_iter = acquires; acquire_iter->type != MM_RESOURCE_MANAGER_NO_RES; acquire_iter++) {
477                         if (acquire_iter->type == increase_iter->type)
478                                 acquire_iter->priority_error = increase_iter->priority_error;
479                 }
480         }
481 }
482
483 static inline void __add_cb_request(GArray *cb_requests,mm_resource_manager_dmn_p mgr,
484                         mm_resource_manager_res_type_e type, mm_resource_manager_res_volume volume)
485 {
486         mm_resource_manager_dmn_release_cb_request_s *cb_request;
487
488         g_array_set_size(cb_requests, cb_requests->len + 1);
489         cb_request = &g_array_index(cb_requests, mm_resource_manager_dmn_release_cb_request_s, cb_requests->len - 1);
490
491         cb_request->manager = mgr;
492         cb_request->type = type;
493         cb_request->volume = volume;
494 }
495
496 static GArray *__handle_acquire_requests(mm_resource_manager_dmn_p manager,
497                 mm_resource_manager_dmn_res_request_s *requests)
498 {
499         mm_resource_manager_conf_s *conf = mm_resource_manager_get_conf();
500         mm_resource_manager_res_volume acquired_volume = 0;
501         mm_resource_manager_dmn_p i_mgr = NULL, j_mgr = NULL;
502         const char *res_name = NULL;
503         GArray *cb_requests = NULL;
504         GArray *res = NULL;
505         int i = 0, j = 0;
506         int pid;
507         mm_resource_manager_res_type_e type = MM_RESOURCE_MANAGER_RES_TYPE_MAX;
508         mm_resource_manager_res_volume volume = MM_RESOURCE_MANAGER_RES_VOLUME_FULL;
509
510         MM_RM_RETVM_IF(conf == NULL, NULL, "conf is NULL");
511         MM_RM_RETVM_IF(requests == NULL, NULL, "requests is NULL");
512
513         cb_requests = g_array_sized_new(FALSE, FALSE, sizeof(mm_resource_manager_dmn_release_cb_request_s),
514                         MM_RESOURCE_MANAGER_RESERVED_CALLBACK_ARRAY_SIZE);
515
516         for (; requests->type != MM_RESOURCE_MANAGER_NO_RES; requests++) {
517                 type = requests->type;
518                 res_name = _mm_resource_manager_get_res_str(type);
519                 volume = requests->volume;
520                 pid = requests->pid;
521
522                 if (volume == MM_RESOURCE_MANAGER_RES_VOLUME_FULL) {
523                         MM_RM_DEBUG("Resource of %s is requested (mgr %p)", res_name, manager);
524
525                         for (i = 0; i < managers->len; i++) {
526                                 i_mgr = (mm_resource_manager_dmn_p)managers->pdata[i];
527
528                                 if (manager->id == i_mgr->id || conf->priority[i_mgr->app_class] >conf->priority[manager->app_class]) {
529                                         i_mgr->resources[type]->state = MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED;
530                                         i_mgr->type = type;
531                                         i_mgr->volume = volume;
532                                         i_mgr->pid = pid;
533                                         MM_RM_DEBUG("[PID %d] Reset the value of acquire in RM #%"PRIu64" (type %s mgr %p)",
534                                                 i_mgr->pid, _mm_rm_hash64(i_mgr->id), res_name, i_mgr);
535                                         if (conf->max_instance[type] > 0)
536                                                 res_count[type]++;
537                                         continue;
538                                 }
539
540                                 if ((res = i_mgr->resources[type]->parts)) { /* always null, which is no conflict case now */
541                                         for (j = 0; j < res->len; j++) {
542                                                 __add_cb_request(cb_requests, i_mgr, type, g_array_index(res, mm_resource_manager_res_volume, j));
543                                                 mm_resource_manager_backend_release(type);
544                                         }
545                                         g_array_free(res, TRUE);
546                                         i_mgr->resources[type]->parts = NULL;
547
548                                         MM_RM_WARNING("[PID %d] Resource of %s is conflicted in RM #%"PRIu64, i_mgr->pid, res_name, _mm_rm_hash64(i_mgr->id));
549                                 } else {
550                                         __add_cb_request(cb_requests, i_mgr, type, MM_RESOURCE_MANAGER_RES_VOLUME_FULL);
551
552                                         mm_resource_manager_backend_release(type);
553
554                                         MM_RM_DEBUG("[PID %d] Resource %s will be released (%s state) in RM #%"PRIu64" available volume %d", i_mgr->pid, res_name,
555                                                 res_state_str[i_mgr->resources[type]->state], _mm_rm_hash64(i_mgr->id), conf->max_volume[type]);
556                                 }
557                         }
558                 } else {
559                         MM_RM_INFO("[managers len #%d] [%s #%d (max inst #%d)] [%d (max vol %d)] are requested",
560                                 managers->len, res_name, res_count[type] + 1, conf->max_instance[type], volume, conf->max_volume[type]);
561
562                         if (conf->max_instance[type] > 0 && conf->max_instance[type] == res_count[type]) {
563                                 for (i= 0; i < managers->len; i++) {
564                                         i_mgr = (mm_resource_manager_dmn_p)managers->pdata[j];
565                                         res = i_mgr->resources[type]->parts;
566
567                                         if (res && i_mgr->resources[type]->state == MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED) {
568                                                 MM_RM_INFO("[#%d] [#%d / #%d] will be released %s in RM #%"PRIu64,
569                                                 managers->len, i + 1, res->len, res_name, _mm_rm_hash64(i_mgr->id));
570
571                                                 __add_cb_request(cb_requests, i_mgr, type, g_array_index(res, mm_resource_manager_res_volume, 0));
572
573                                                 mm_resource_manager_backend_release(type);
574                                                 break;
575                                         }
576                                 }
577                         } else {
578                                 for (i = 0; i < managers->len; i++) {
579                                         i_mgr = (mm_resource_manager_dmn_p)managers->pdata[i];
580                                         res = i_mgr->resources[type]->parts;
581
582                                         if (manager->id == i_mgr->id || conf->priority[i_mgr->app_class] > conf->priority[manager->app_class]) {
583
584                                                 if (conf->volume_would_be_checked[type]) {
585                                                         if (i_mgr->resources[type]->state != MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED && conf->max_volume[type] >= 0) {
586                                                                 i_mgr->resources[type]->state = MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED;
587                                                                 i_mgr->type = type;
588                                                                 i_mgr->volume = volume;
589                                                                 i_mgr->pid = pid;
590                                                                 conf->max_volume[type] -= volume;
591                                                                 MM_RM_INFO("[type %s] - %d = %d", res_name, volume, conf->max_volume[type]);
592                                                         }
593
594                                                         if (conf->volume_would_be_checked[type] && conf->max_volume[type] < 0
595                                                                 && acquired_volume < volume) {
596                                                                 for (j = 0; j < managers->len; j++) {
597                                                                         j_mgr = (mm_resource_manager_dmn_p)managers->pdata[j];
598                                                                         res = j_mgr->resources[type]->parts;
599
600                                                                         if (j_mgr->resources[type]->state != MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED)
601                                                                                 continue;
602
603                                                                         acquired_volume += g_array_index(res, mm_resource_manager_res_volume, 0);
604                                                                         MM_RM_INFO("[#%d] [#%d / #%d] There are %d units of %s in RM #%"PRIu64,
605                                                                                 managers->len, j + 1, res->len, acquired_volume, res_name, _mm_rm_hash64(j_mgr->id));
606
607                                                                         __add_cb_request(cb_requests, j_mgr, type, g_array_index(res, mm_resource_manager_res_volume, 0));
608
609                                                                         mm_resource_manager_backend_release(type);
610
611                                                                         if (acquired_volume >= volume)
612                                                                                 break;
613                                                                 }
614                                                         }
615                                                 }
616
617                                                 if (i_mgr->resources[type]->state == MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED)
618                                                         res_count[type]++;
619
620                                                 i_mgr->resources[type]->state = MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED;
621                                         }
622                                 }
623
624                                 res = manager->resources[requests->type]->parts;
625                                 if (!res) {
626                                         res = g_array_sized_new(FALSE, FALSE, sizeof(mm_resource_manager_res_volume),
627                                                 MM_RESOURCE_MANAGER_RESERVED_PART_ARRAY_SIZE);
628                                         manager->resources[requests->type]->parts = res;
629                                 }
630                                 g_array_append_val(res, requests->volume);
631                         }
632                 }
633                 manager->resources[type]->state = MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED;
634                 mm_resource_manager_backend_acquire(type);
635                 if (i_mgr)
636                         MM_RM_DEBUG("[PID %d] RM #%"PRIu64" (type %s mgr %p) is acquired", i_mgr->pid, _mm_rm_hash64(i_mgr->id), res_name, i_mgr);
637         }
638
639         return cb_requests;
640 }
641
642 static int __open_release_cb_sync_fd(void)
643 {
644         int fd = open(RELEASE_CB_SYNC_PATH, O_RDONLY | O_NONBLOCK);
645         MM_RM_RETVM_IF(fd == -1, fd, "[%s] open failed (%d)", RELEASE_CB_SYNC_PATH, errno);
646
647         MM_RM_DEBUG("[%d] opened %s", fd, RELEASE_CB_SYNC_PATH);
648
649         return fd;
650 }
651
652 static void __close_release_cb_sync_fd(int fd)
653 {
654         MM_RM_RETM_IF(fd == -1, "[fd %d] invalid parameter", fd);
655         MM_RM_RETM_IF(close(fd) == -1, "[%s] close failed (%d)", RELEASE_CB_SYNC_PATH, errno);
656
657         MM_RM_DEBUG("[%d] closed %s", fd, RELEASE_CB_SYNC_PATH);
658 }
659
660 static gboolean __is_process_alive(int pid)
661 {
662         char path[PID_MSG_LEN];
663
664         snprintf(path, sizeof(path), "/proc/%d", pid);
665
666         return access(path, F_OK) == 0;
667 }
668
669 static void __handle_release_callbacks(GArray *requests)
670 {
671         int i;
672         int sync_fd;
673         int pid;
674         mm_resource_manager_id id;
675         mm_resource_manager_dmn_release_cb_request_s *request;
676         mm_resource_manager_res_type_e type = MM_RESOURCE_MANAGER_RES_TYPE_MAX;
677         mm_resource_manager_res_volume volume = MM_RESOURCE_MANAGER_RES_VOLUME_FULL;
678         mm_resource_manager_dmn_p mgr = NULL;
679         const char *res_name = NULL;
680         mm_resource_manager_conf_s *conf = mm_resource_manager_get_conf();
681
682         MM_RM_RETM_IF(requests == NULL, "requests is NULL");
683         MM_RM_RETM_IF(conf == NULL, "conf is NULL");
684
685         for (i = 0; i < requests->len; i++) {
686                 request = &g_array_index(requests, mm_resource_manager_dmn_release_cb_request_s, i);
687                 mgr = request->manager;
688                 type = request->type;
689                 res_name = _mm_resource_manager_get_res_str(type);
690                 volume = request->volume;
691
692                 if (mgr->resources[type]->state != MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED)
693                         continue;
694
695                 pid = mgr->pid;
696                 id = mgr->id;
697
698                 if (!__is_process_alive(pid)) {
699                         MM_RM_ERROR("[PID %d] is already terminated", pid);
700                         _mmrm_dmn_destroy(id);
701                         continue;
702                 }
703
704                 MM_RM_HASH64(id);
705
706                 if (mgr->type == type && mgr->volume == volume && mgr->resources[type]->state == MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE) {
707                         MM_RM_WARNING("Resource (mgr %p) release callback is already invoked RM #%"PRIu64" for %s of volume %d",
708                                 mgr, id, res_name, volume);
709                         continue;
710                 }
711
712                 /* Avoid to emit release callback of resource manager's other resource, which can be changed by release policy*/
713                 if (conf->is_release_all && mgr->resources[type]->state == MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE) {
714                         MM_RM_WARNING("Already sending release callback to [mgr %p] RM #%"PRIu64" for %s of volume %d", mgr, id, res_name, volume);
715                         continue;
716                 }
717
718                 MM_RM_INFO("Sending release callback to [mgr %p] RM #%"PRIu64" for %s of volume %d", mgr, id, res_name, volume);
719
720                 sync_fd = __open_release_cb_sync_fd();
721                 MM_RM_RETM_IF(sync_fd == -1, "[%s] open failed", RELEASE_CB_SYNC_PATH);
722
723                 _mmrm_dmn_dbus_release_callback(id, type, volume);
724
725                 if (__wait_for_release_cb_sync(id, sync_fd)) {
726                         MM_RM_INFO("[SYNC] Release callback success RM #%"PRIu64, id);
727                         if (conf->volume_would_be_checked[type] && volume != MM_RESOURCE_MANAGER_RES_VOLUME_FULL) {
728                                 conf->max_volume[type] += volume;
729                                 MM_RM_DEBUG("type %s available volume + %d = %d", res_name, volume, conf->max_volume[type]);
730                         }
731
732                         if (conf->max_instance[type] > 0 && res_count[type] > 0) {
733                                 res_count[type]--;
734                                 MM_RM_DEBUG("The number of type %s #%d", res_name, res_count[type]);
735                         }
736
737                         mgr->resources[type]->state = MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE;
738
739                         MM_RM_DEBUG("RM #%"PRIu64" (type %s mgr %p) set acquired value as state (%s)",
740                                 id, res_name, mgr, res_state_str[mgr->resources[type]->state]);
741                 } else {
742                         MM_RM_ERROR("Wait for release callback sync failed RM #%"PRIu64" (type %s)", id, res_name);
743                 }
744
745                 __close_release_cb_sync_fd(sync_fd);
746         }
747 }
748
749 static void __handle_release_requests(mm_resource_manager_dmn_p manager,
750                 mm_resource_manager_dmn_res_request_s *requests)
751 {
752         GArray *parts;
753         int i;
754         mm_resource_manager_res_type_e type = MM_RESOURCE_MANAGER_RES_TYPE_MAX;
755         mm_resource_manager_res_volume volume = MM_RESOURCE_MANAGER_RES_VOLUME_FULL;
756         const char *res_name = NULL;
757         mm_resource_manager_conf_s *conf = mm_resource_manager_get_conf();
758
759         MM_RM_RETM_IF(conf == NULL, "conf is NULL");
760         MM_RM_RETM_IF(requests == NULL, "requests is NULL");
761
762         for (; requests->type != MM_RESOURCE_MANAGER_NO_RES; requests++) {
763
764                 type = requests->type;
765                 res_name = _mm_resource_manager_get_res_str(type);
766                 volume = requests->volume;
767
768                 if (volume == MM_RESOURCE_MANAGER_RES_VOLUME_FULL) {
769                         manager->resources[type]->state = MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE;
770                 } else {
771                         parts = manager->resources[type]->parts;
772                         for (i = 0; i < parts->len && ((mm_resource_manager_res_volume*)parts->data)[i] != volume; i++);
773
774                         g_array_remove_index_fast(parts, i);
775
776                         if (parts->len == 0) {
777                                 g_array_free(parts, TRUE);
778                                 manager->resources[type]->parts = NULL;
779                                 manager->resources[type]->state = MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE;
780                         }
781                 }
782
783                 if (conf->volume_would_be_checked[type]) {
784                         conf->max_volume[type] += volume;
785                         MM_RM_INFO("[type %s] + %d = %d", res_name, volume, conf->max_volume[type]);
786                 }
787
788                 if (conf->max_instance[type] > 0 && res_count[type] > 0) {
789                         res_count[type]--;
790                         MM_RM_INFO("The number of type %s #%d", res_name, res_count[type]);
791                 }
792
793                 mm_resource_manager_backend_release(type);
794         }
795 }
796
797 static void __destroy_all_resources(mm_resource_manager_dmn_p manager)
798 {
799         int i, j;
800
801         for (i = 0; i < MM_RESOURCE_MANAGER_RES_TYPE_MAX; i++) {
802                 if (manager->resources[i] && manager->resources[i]->state == MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED) {
803                         if (manager->resources[i]->parts) {
804                                 for (j = 0; j < manager->resources[i]->parts->len; j++)
805                                         mm_resource_manager_backend_release(i);
806                         } else {
807                                 mm_resource_manager_backend_release(i);
808                         }
809                         manager->resources[i]->state = MM_RESOURCE_MANAGER_RES_STATE_FOR_ACQUIRE;
810                 }
811         }
812 }
813
814 static gboolean __poll(struct pollfd sync, mm_resource_manager_id id)
815 {
816         int errsv = 0;
817         int try_cnt = 0;
818         int ret = 0;
819
820         while (try_cnt++ < 3) {
821                 sync.events = POLLIN;
822                 sync.revents = 0;
823
824                 ret = poll(&sync, 1, RELEASE_CB_SYNC_TIMEOUT);
825
826                 if (ret == -1) {
827                         errsv = errno;
828                         MM_RM_ERROR("[%d ms] Polling is failed [fd %d errno %d] RM #%"PRIu64" revents : 0x%x",
829                                 RELEASE_CB_SYNC_TIMEOUT, sync.fd, errsv, id, sync.revents);
830                         if (errsv == EAGAIN)
831                                 continue;
832                         break;
833                 } else if (ret == 0) {
834                         MM_RM_WARNING("Wait timeout [%d ms] is elapsed [fd %d] RM #%"PRIu64" revents : 0x%x",
835                                 RELEASE_CB_SYNC_TIMEOUT, sync.fd, id, sync.revents);
836                         break;
837                 }
838
839                 MM_RM_DEBUG("[fd %d] RM #%"PRIu64" revents : 0x%x", sync.fd, id, sync.revents);
840                 return TRUE;
841         }
842
843         return FALSE;
844 }
845
846 static gboolean __wait_for_release_cb_sync(mm_resource_manager_id id, int sync_fd)
847 {
848         gboolean ret = FALSE;
849         struct pollfd sync = {0};
850         mm_resource_manager_id recv_id;
851         ssize_t read_size;
852
853         MM_RM_DEBUG("Enter %d", sync_fd);
854
855         MM_RM_RETVM_IF(sync_fd == -1, FALSE, "[sync_fd %d] invalid parameter", sync_fd);
856
857         sync.fd = sync_fd;
858
859         if (!__poll(sync, id))
860                 return FALSE;
861
862         read_size = read(sync.fd, &recv_id, sizeof(recv_id));
863         if (read_size != sizeof(recv_id)) {
864                 MM_RM_ERROR("Read is failed (revents=%hd, read_size=%zd)", sync.revents, read_size);
865                 return FALSE;
866         }
867
868         if (id != _mm_rm_hash64(recv_id))
869                 MM_RM_WARNING("Sync is received from wrong client #%"PRIu64, id);
870
871         ret = __poll(sync, id);
872
873         return ret;
874 }