blob: 991208f95e6d4fc2f839db04e007047e9b88be5f [file] [log] [blame]
Sunil Paidimarri5139aa22017-02-13 11:07:32 -08001/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
Amir Levy9659e592016-10-27 18:08:27 +03002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/slab.h>
14#include "ipa_rm_resource.h"
15#include "ipa_rm_i.h"
16#include "ipa_common_i.h"
17/**
18 * ipa_rm_dep_prod_index() - producer name to producer index mapping
19 * @resource_name: [in] resource name (should be of producer)
20 *
21 * Returns: resource index mapping, IPA_RM_INDEX_INVALID
22 * in case provided resource name isn't contained
23 * in enum ipa_rm_resource_name or is not of producers.
24 *
25 */
26int ipa_rm_prod_index(enum ipa_rm_resource_name resource_name)
27{
28 int result = resource_name;
29
30 switch (resource_name) {
31 case IPA_RM_RESOURCE_Q6_PROD:
32 case IPA_RM_RESOURCE_USB_PROD:
33 case IPA_RM_RESOURCE_USB_DPL_DUMMY_PROD:
34 case IPA_RM_RESOURCE_HSIC_PROD:
35 case IPA_RM_RESOURCE_STD_ECM_PROD:
36 case IPA_RM_RESOURCE_RNDIS_PROD:
37 case IPA_RM_RESOURCE_WWAN_0_PROD:
38 case IPA_RM_RESOURCE_WLAN_PROD:
39 case IPA_RM_RESOURCE_ODU_ADAPT_PROD:
40 case IPA_RM_RESOURCE_MHI_PROD:
Sunil Paidimarri5139aa22017-02-13 11:07:32 -080041 case IPA_RM_RESOURCE_ETHERNET_PROD:
Amir Levy9659e592016-10-27 18:08:27 +030042 break;
43 default:
44 result = IPA_RM_INDEX_INVALID;
45 break;
46 }
47
48 return result;
49}
50
51/**
52 * ipa_rm_cons_index() - consumer name to consumer index mapping
53 * @resource_name: [in] resource name (should be of consumer)
54 *
55 * Returns: resource index mapping, IPA_RM_INDEX_INVALID
56 * in case provided resource name isn't contained
57 * in enum ipa_rm_resource_name or is not of consumers.
58 *
59 */
60int ipa_rm_cons_index(enum ipa_rm_resource_name resource_name)
61{
62 int result = resource_name;
63
64 switch (resource_name) {
65 case IPA_RM_RESOURCE_Q6_CONS:
66 case IPA_RM_RESOURCE_USB_CONS:
67 case IPA_RM_RESOURCE_HSIC_CONS:
68 case IPA_RM_RESOURCE_WLAN_CONS:
69 case IPA_RM_RESOURCE_APPS_CONS:
70 case IPA_RM_RESOURCE_ODU_ADAPT_CONS:
71 case IPA_RM_RESOURCE_MHI_CONS:
72 case IPA_RM_RESOURCE_USB_DPL_CONS:
Sunil Paidimarri5139aa22017-02-13 11:07:32 -080073 case IPA_RM_RESOURCE_ETHERNET_CONS:
Amir Levy9659e592016-10-27 18:08:27 +030074 break;
75 default:
76 result = IPA_RM_INDEX_INVALID;
77 break;
78 }
79
80 return result;
81}
82
83int ipa_rm_resource_consumer_release_work(
84 struct ipa_rm_resource_cons *consumer,
85 enum ipa_rm_resource_state prev_state,
86 bool notify_completion)
87{
88 int driver_result;
89
90 IPA_RM_DBG_LOW("calling driver CB\n");
91 driver_result = consumer->release_resource();
92 IPA_RM_DBG_LOW("driver CB returned with %d\n", driver_result);
93 /*
94 * Treat IPA_RM_RELEASE_IN_PROGRESS as IPA_RM_RELEASED
95 * for CONS which remains in RELEASE_IN_PROGRESS.
96 */
97 if (driver_result == -EINPROGRESS)
98 driver_result = 0;
99 if (driver_result != 0 && driver_result != -EINPROGRESS) {
100 IPA_RM_ERR("driver CB returned error %d\n", driver_result);
101 consumer->resource.state = prev_state;
102 goto bail;
103 }
104 if (driver_result == 0) {
105 if (notify_completion)
106 ipa_rm_resource_consumer_handle_cb(consumer,
107 IPA_RM_RESOURCE_RELEASED);
108 else
109 consumer->resource.state = IPA_RM_RELEASED;
110 }
111 complete_all(&consumer->request_consumer_in_progress);
112
113 ipa_rm_perf_profile_change(consumer->resource.name);
114bail:
115 return driver_result;
116}
117
118int ipa_rm_resource_consumer_request_work(struct ipa_rm_resource_cons *consumer,
119 enum ipa_rm_resource_state prev_state,
120 u32 prod_needed_bw,
Gidon Studinski3021a6f2016-11-10 12:48:48 +0200121 bool notify_completion,
122 bool dec_client_on_err)
Amir Levy9659e592016-10-27 18:08:27 +0300123{
124 int driver_result;
125
126 IPA_RM_DBG_LOW("calling driver CB\n");
127 driver_result = consumer->request_resource();
128 IPA_RM_DBG_LOW("driver CB returned with %d\n", driver_result);
129 if (driver_result == 0) {
130 if (notify_completion) {
131 ipa_rm_resource_consumer_handle_cb(consumer,
132 IPA_RM_RESOURCE_GRANTED);
133 } else {
134 consumer->resource.state = IPA_RM_GRANTED;
135 ipa_rm_perf_profile_change(consumer->resource.name);
136 ipa_resume_resource(consumer->resource.name);
137 }
138 } else if (driver_result != -EINPROGRESS) {
139 consumer->resource.state = prev_state;
140 consumer->resource.needed_bw -= prod_needed_bw;
Gidon Studinski3021a6f2016-11-10 12:48:48 +0200141 if (dec_client_on_err)
142 consumer->usage_count--;
Amir Levy9659e592016-10-27 18:08:27 +0300143 }
144
145 return driver_result;
146}
147
148int ipa_rm_resource_consumer_request(
149 struct ipa_rm_resource_cons *consumer,
150 u32 prod_needed_bw,
151 bool inc_usage_count,
152 bool wake_client)
153{
154 int result = 0;
155 enum ipa_rm_resource_state prev_state;
156 struct ipa_active_client_logging_info log_info;
157
158 IPA_RM_DBG_LOW("%s state: %d\n",
159 ipa_rm_resource_str(consumer->resource.name),
160 consumer->resource.state);
161
162 prev_state = consumer->resource.state;
163 consumer->resource.needed_bw += prod_needed_bw;
164 switch (consumer->resource.state) {
165 case IPA_RM_RELEASED:
166 case IPA_RM_RELEASE_IN_PROGRESS:
167 reinit_completion(&consumer->request_consumer_in_progress);
168 consumer->resource.state = IPA_RM_REQUEST_IN_PROGRESS;
169 IPA_ACTIVE_CLIENTS_PREP_RESOURCE(log_info,
170 ipa_rm_resource_str(consumer->resource.name));
171 if (prev_state == IPA_RM_RELEASE_IN_PROGRESS ||
172 ipa_inc_client_enable_clks_no_block(&log_info) != 0) {
173 IPA_RM_DBG_LOW("async resume work for %s\n",
174 ipa_rm_resource_str(consumer->resource.name));
175 ipa_rm_wq_send_resume_cmd(consumer->resource.name,
176 prev_state,
Gidon Studinski3021a6f2016-11-10 12:48:48 +0200177 prod_needed_bw,
178 inc_usage_count);
Amir Levy9659e592016-10-27 18:08:27 +0300179 result = -EINPROGRESS;
180 break;
181 }
182 result = ipa_rm_resource_consumer_request_work(consumer,
183 prev_state,
184 prod_needed_bw,
Gidon Studinski3021a6f2016-11-10 12:48:48 +0200185 false,
186 inc_usage_count);
Amir Levy9659e592016-10-27 18:08:27 +0300187 break;
188 case IPA_RM_GRANTED:
189 if (wake_client) {
190 result = ipa_rm_resource_consumer_request_work(
Gidon Studinski3021a6f2016-11-10 12:48:48 +0200191 consumer, prev_state, prod_needed_bw, false,
192 inc_usage_count);
Amir Levy9659e592016-10-27 18:08:27 +0300193 break;
194 }
195 ipa_rm_perf_profile_change(consumer->resource.name);
196 break;
197 case IPA_RM_REQUEST_IN_PROGRESS:
198 result = -EINPROGRESS;
199 break;
200 default:
201 consumer->resource.needed_bw -= prod_needed_bw;
202 result = -EPERM;
203 goto bail;
204 }
205 if (inc_usage_count)
206 consumer->usage_count++;
207bail:
208 IPA_RM_DBG_LOW("%s new state: %d\n",
209 ipa_rm_resource_str(consumer->resource.name),
210 consumer->resource.state);
211 IPA_RM_DBG_LOW("EXIT with %d\n", result);
212
213 return result;
214}
215
216int ipa_rm_resource_consumer_release(
217 struct ipa_rm_resource_cons *consumer,
218 u32 prod_needed_bw,
219 bool dec_usage_count)
220{
221 int result = 0;
222 enum ipa_rm_resource_state save_state;
223
224 IPA_RM_DBG_LOW("%s state: %d\n",
225 ipa_rm_resource_str(consumer->resource.name),
226 consumer->resource.state);
227 save_state = consumer->resource.state;
228 consumer->resource.needed_bw -= prod_needed_bw;
229 switch (consumer->resource.state) {
230 case IPA_RM_RELEASED:
231 break;
232 case IPA_RM_GRANTED:
233 case IPA_RM_REQUEST_IN_PROGRESS:
234 if (dec_usage_count && consumer->usage_count > 0)
235 consumer->usage_count--;
236 if (consumer->usage_count == 0) {
237 consumer->resource.state = IPA_RM_RELEASE_IN_PROGRESS;
238 if (save_state == IPA_RM_REQUEST_IN_PROGRESS ||
239 ipa_suspend_resource_no_block(
240 consumer->resource.name) != 0) {
241 ipa_rm_wq_send_suspend_cmd(
242 consumer->resource.name,
243 save_state,
244 prod_needed_bw);
245 result = -EINPROGRESS;
246 goto bail;
247 }
248 result = ipa_rm_resource_consumer_release_work(consumer,
249 save_state, false);
250 goto bail;
251 } else if (consumer->resource.state == IPA_RM_GRANTED) {
252 ipa_rm_perf_profile_change(consumer->resource.name);
253 }
254 break;
255 case IPA_RM_RELEASE_IN_PROGRESS:
256 if (dec_usage_count && consumer->usage_count > 0)
257 consumer->usage_count--;
258 result = -EINPROGRESS;
259 break;
260 default:
261 result = -EPERM;
262 goto bail;
263 }
264bail:
265 IPA_RM_DBG_LOW("%s new state: %d\n",
266 ipa_rm_resource_str(consumer->resource.name),
267 consumer->resource.state);
268 IPA_RM_DBG_LOW("EXIT with %d\n", result);
269
270 return result;
271}
272
273/**
274 * ipa_rm_resource_producer_notify_clients() - notify
275 * all registered clients of given producer
276 * @producer: producer
277 * @event: event to notify
278 * @notify_registered_only: notify only clients registered by
279 * ipa_rm_register()
280 */
281void ipa_rm_resource_producer_notify_clients(
282 struct ipa_rm_resource_prod *producer,
283 enum ipa_rm_event event,
284 bool notify_registered_only)
285{
286 struct ipa_rm_notification_info *reg_info;
287
288 IPA_RM_DBG_LOW("%s event: %d notify_registered_only: %d\n",
289 ipa_rm_resource_str(producer->resource.name),
290 event,
291 notify_registered_only);
292
293 list_for_each_entry(reg_info, &(producer->event_listeners), link) {
294 if (notify_registered_only && !reg_info->explicit)
295 continue;
296
297 IPA_RM_DBG_LOW("Notifying %s event: %d\n",
298 ipa_rm_resource_str(producer->resource.name), event);
299 reg_info->reg_params.notify_cb(reg_info->reg_params.user_data,
300 event,
301 0);
302 IPA_RM_DBG_LOW("back from client CB\n");
303 }
304}
305
306static int ipa_rm_resource_producer_create(struct ipa_rm_resource **resource,
307 struct ipa_rm_resource_prod **producer,
308 struct ipa_rm_create_params *create_params,
309 int *max_peers)
310{
311 int result = 0;
312
313 *producer = kzalloc(sizeof(**producer), GFP_ATOMIC);
314 if (*producer == NULL) {
315 IPA_RM_ERR("no mem\n");
316 result = -ENOMEM;
317 goto bail;
318 }
319
320 INIT_LIST_HEAD(&((*producer)->event_listeners));
321 result = ipa_rm_resource_producer_register(*producer,
322 &(create_params->reg_params),
323 false);
324 if (result) {
325 IPA_RM_ERR("ipa_rm_resource_producer_register() failed\n");
326 goto register_fail;
327 }
328
329 (*resource) = (struct ipa_rm_resource *) (*producer);
330 (*resource)->type = IPA_RM_PRODUCER;
Skylar Changa9516582017-05-09 11:36:47 -0700331 *max_peers = IPA_RM_RESOURCE_MAX;
Amir Levy9659e592016-10-27 18:08:27 +0300332 goto bail;
333register_fail:
334 kfree(*producer);
335bail:
336 return result;
337}
338
339static void ipa_rm_resource_producer_delete(
340 struct ipa_rm_resource_prod *producer)
341{
342 struct ipa_rm_notification_info *reg_info;
343 struct list_head *pos, *q;
344
345 ipa_rm_resource_producer_release(producer);
346 list_for_each_safe(pos, q, &(producer->event_listeners)) {
347 reg_info = list_entry(pos,
348 struct ipa_rm_notification_info,
349 link);
350 list_del(pos);
351 kfree(reg_info);
352 }
353}
354
355static int ipa_rm_resource_consumer_create(struct ipa_rm_resource **resource,
356 struct ipa_rm_resource_cons **consumer,
357 struct ipa_rm_create_params *create_params,
358 int *max_peers)
359{
360 int result = 0;
361
362 *consumer = kzalloc(sizeof(**consumer), GFP_ATOMIC);
363 if (*consumer == NULL) {
364 IPA_RM_ERR("no mem\n");
365 result = -ENOMEM;
366 goto bail;
367 }
368
369 (*consumer)->request_resource = create_params->request_resource;
370 (*consumer)->release_resource = create_params->release_resource;
371 (*resource) = (struct ipa_rm_resource *) (*consumer);
372 (*resource)->type = IPA_RM_CONSUMER;
373 init_completion(&((*consumer)->request_consumer_in_progress));
Skylar Changa9516582017-05-09 11:36:47 -0700374 *max_peers = IPA_RM_RESOURCE_MAX;
Amir Levy9659e592016-10-27 18:08:27 +0300375bail:
376 return result;
377}
378
379/**
380 * ipa_rm_resource_create() - creates resource
381 * @create_params: [in] parameters needed
382 * for resource initialization with IPA RM
383 * @resource: [out] created resource
384 *
385 * Returns: 0 on success, negative on failure
386 */
387int ipa_rm_resource_create(
388 struct ipa_rm_create_params *create_params,
389 struct ipa_rm_resource **resource)
390{
391 struct ipa_rm_resource_cons *consumer;
392 struct ipa_rm_resource_prod *producer;
393 int max_peers;
394 int result = 0;
395
396 if (!create_params) {
397 result = -EINVAL;
398 goto bail;
399 }
400
401 if (IPA_RM_RESORCE_IS_PROD(create_params->name)) {
402 result = ipa_rm_resource_producer_create(resource,
403 &producer,
404 create_params,
405 &max_peers);
406 if (result) {
407 IPA_RM_ERR("ipa_rm_resource_producer_create failed\n");
408 goto bail;
409 }
410 } else if (IPA_RM_RESORCE_IS_CONS(create_params->name)) {
411 result = ipa_rm_resource_consumer_create(resource,
412 &consumer,
413 create_params,
414 &max_peers);
415 if (result) {
416 IPA_RM_ERR("ipa_rm_resource_producer_create failed\n");
417 goto bail;
418 }
419 } else {
420 IPA_RM_ERR("invalied resource\n");
421 result = -EPERM;
422 goto bail;
423 }
424
425 result = ipa_rm_peers_list_create(max_peers,
426 &((*resource)->peers_list));
427 if (result) {
428 IPA_RM_ERR("ipa_rm_peers_list_create failed\n");
429 goto peers_alloc_fail;
430 }
431 (*resource)->name = create_params->name;
432 (*resource)->floor_voltage = create_params->floor_voltage;
433 (*resource)->state = IPA_RM_RELEASED;
434 goto bail;
435
436peers_alloc_fail:
437 ipa_rm_resource_delete(*resource);
438bail:
439 return result;
440}
441
442/**
443 * ipa_rm_resource_delete() - deletes resource
444 * @resource: [in] resource
445 * for resource initialization with IPA RM
446 *
447 * Returns: 0 on success, negative on failure
448 */
449int ipa_rm_resource_delete(struct ipa_rm_resource *resource)
450{
451 struct ipa_rm_resource *consumer;
452 struct ipa_rm_resource *producer;
453 int peers_index;
454 int result = 0;
455 int list_size;
456 bool userspace_dep;
457
458 if (!resource) {
459 IPA_RM_ERR("invalid params\n");
460 return -EINVAL;
461 }
462
463 IPA_RM_DBG("ipa_rm_resource_delete ENTER with resource %d\n",
464 resource->name);
465 if (resource->type == IPA_RM_PRODUCER) {
466 if (resource->peers_list) {
467 list_size = ipa_rm_peers_list_get_size(
468 resource->peers_list);
469 for (peers_index = 0;
470 peers_index < list_size;
471 peers_index++) {
472 consumer = ipa_rm_peers_list_get_resource(
473 peers_index,
474 resource->peers_list);
475 if (consumer) {
476 userspace_dep =
477 ipa_rm_peers_list_get_userspace_dep(
478 peers_index,
479 resource->peers_list);
480 ipa_rm_resource_delete_dependency(
481 resource,
482 consumer,
483 userspace_dep);
484 }
485 }
486 }
487
488 ipa_rm_resource_producer_delete(
489 (struct ipa_rm_resource_prod *) resource);
490 } else if (resource->type == IPA_RM_CONSUMER) {
491 if (resource->peers_list) {
492 list_size = ipa_rm_peers_list_get_size(
493 resource->peers_list);
494 for (peers_index = 0;
495 peers_index < list_size;
496 peers_index++){
497 producer = ipa_rm_peers_list_get_resource(
498 peers_index,
499 resource->peers_list);
500 if (producer) {
501 userspace_dep =
502 ipa_rm_peers_list_get_userspace_dep(
503 peers_index,
504 resource->peers_list);
505 ipa_rm_resource_delete_dependency(
506 producer,
507 resource,
508 userspace_dep);
509 }
510 }
511 }
512 }
513 ipa_rm_peers_list_delete(resource->peers_list);
514 kfree(resource);
515 return result;
516}
517
518/**
519 * ipa_rm_resource_register() - register resource
520 * @resource: [in] resource
521 * @reg_params: [in] registration parameters
522 * @explicit: [in] registered explicitly by ipa_rm_register()
523 *
524 * Returns: 0 on success, negative on failure
525 *
526 * Producer resource is expected for this call.
527 *
528 */
529int ipa_rm_resource_producer_register(struct ipa_rm_resource_prod *producer,
530 struct ipa_rm_register_params *reg_params,
531 bool explicit)
532{
533 int result = 0;
534 struct ipa_rm_notification_info *reg_info;
535 struct list_head *pos;
536
537 if (!producer || !reg_params) {
538 IPA_RM_ERR("invalid params\n");
539 result = -EPERM;
540 goto bail;
541 }
542
543 list_for_each(pos, &(producer->event_listeners)) {
544 reg_info = list_entry(pos,
545 struct ipa_rm_notification_info,
546 link);
547 if (reg_info->reg_params.notify_cb ==
548 reg_params->notify_cb) {
549 IPA_RM_ERR("already registered\n");
550 result = -EPERM;
551 goto bail;
552 }
553
554 }
555
556 reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
557 if (reg_info == NULL) {
558 IPA_RM_ERR("no mem\n");
559 result = -ENOMEM;
560 goto bail;
561 }
562
563 reg_info->reg_params.user_data = reg_params->user_data;
564 reg_info->reg_params.notify_cb = reg_params->notify_cb;
565 reg_info->explicit = explicit;
566 INIT_LIST_HEAD(&reg_info->link);
567 list_add(&reg_info->link, &producer->event_listeners);
568bail:
569 return result;
570}
571
572/**
573 * ipa_rm_resource_deregister() - register resource
574 * @resource: [in] resource
575 * @reg_params: [in] registration parameters
576 *
577 * Returns: 0 on success, negative on failure
578 *
579 * Producer resource is expected for this call.
580 * This function deleted only single instance of
581 * registration info.
582 *
583 */
584int ipa_rm_resource_producer_deregister(struct ipa_rm_resource_prod *producer,
585 struct ipa_rm_register_params *reg_params)
586{
587 int result = -EINVAL;
588 struct ipa_rm_notification_info *reg_info;
589 struct list_head *pos, *q;
590
591 if (!producer || !reg_params) {
592 IPA_RM_ERR("invalid params\n");
593 return -EINVAL;
594 }
595
596 list_for_each_safe(pos, q, &(producer->event_listeners)) {
597 reg_info = list_entry(pos,
598 struct ipa_rm_notification_info,
599 link);
600 if (reg_info->reg_params.notify_cb ==
601 reg_params->notify_cb) {
602 list_del(pos);
603 kfree(reg_info);
604 result = 0;
605 goto bail;
606 }
607 }
608bail:
609 return result;
610}
611
612/**
613 * ipa_rm_resource_add_dependency() - add dependency between two
614 * given resources
615 * @resource: [in] resource resource
616 * @depends_on: [in] depends_on resource
617 *
618 * Returns: 0 on success, negative on failure
619 */
620int ipa_rm_resource_add_dependency(struct ipa_rm_resource *resource,
621 struct ipa_rm_resource *depends_on,
622 bool userspace_dep)
623{
624 int result = 0;
625 int consumer_result;
626 bool add_dep_by_userspace;
627
628 if (!resource || !depends_on) {
629 IPA_RM_ERR("invalid params\n");
630 return -EINVAL;
631 }
632
633 if (ipa_rm_peers_list_check_dependency(resource->peers_list,
634 resource->name,
635 depends_on->peers_list,
636 depends_on->name,
637 &add_dep_by_userspace)) {
638 IPA_RM_ERR("dependency already exists, added by %s\n",
639 add_dep_by_userspace ? "userspace" : "kernel");
640 return -EEXIST;
641 }
642
643 ipa_rm_peers_list_add_peer(resource->peers_list, depends_on,
644 userspace_dep);
645 ipa_rm_peers_list_add_peer(depends_on->peers_list, resource,
646 userspace_dep);
647 IPA_RM_DBG("%s state: %d\n", ipa_rm_resource_str(resource->name),
648 resource->state);
649
650 resource->needed_bw += depends_on->max_bw;
651 switch (resource->state) {
652 case IPA_RM_RELEASED:
653 case IPA_RM_RELEASE_IN_PROGRESS:
654 break;
655 case IPA_RM_GRANTED:
656 case IPA_RM_REQUEST_IN_PROGRESS:
657 {
658 enum ipa_rm_resource_state prev_state = resource->state;
659
660 resource->state = IPA_RM_REQUEST_IN_PROGRESS;
661 ((struct ipa_rm_resource_prod *)
662 resource)->pending_request++;
663 consumer_result = ipa_rm_resource_consumer_request(
664 (struct ipa_rm_resource_cons *)depends_on,
665 resource->max_bw,
666 true, false);
667 if (consumer_result != -EINPROGRESS) {
668 resource->state = prev_state;
669 ((struct ipa_rm_resource_prod *)
670 resource)->pending_request--;
671 ipa_rm_perf_profile_change(resource->name);
672 }
673 result = consumer_result;
674 break;
675 }
676 default:
677 IPA_RM_ERR("invalid state\n");
678 result = -EPERM;
679 goto bail;
680 }
681bail:
682 IPA_RM_DBG("%s new state: %d\n", ipa_rm_resource_str(resource->name),
683 resource->state);
684 IPA_RM_DBG("EXIT with %d\n", result);
685
686 return result;
687}
688
689/**
690 * ipa_rm_resource_delete_dependency() - add dependency between two
691 * given resources
692 * @resource: [in] resource resource
693 * @depends_on: [in] depends_on resource
694 *
695 * Returns: 0 on success, negative on failure
696 * In case the resource state was changed, a notification
697 * will be sent to the RM client
698 */
699int ipa_rm_resource_delete_dependency(struct ipa_rm_resource *resource,
700 struct ipa_rm_resource *depends_on,
701 bool userspace_dep)
702{
703 int result = 0;
704 bool state_changed = false;
705 bool release_consumer = false;
706 enum ipa_rm_event evt;
707 bool add_dep_by_userspace;
708
709 if (!resource || !depends_on) {
710 IPA_RM_ERR("invalid params\n");
711 return -EINVAL;
712 }
713
714 if (!ipa_rm_peers_list_check_dependency(resource->peers_list,
715 resource->name,
716 depends_on->peers_list,
717 depends_on->name,
718 &add_dep_by_userspace)) {
719 IPA_RM_ERR("dependency does not exist\n");
720 return -EINVAL;
721 }
722
723 /*
724 * to avoid race conditions between kernel and userspace
725 * need to check that the dependency was added by same entity
726 */
727 if (add_dep_by_userspace != userspace_dep) {
728 IPA_RM_DBG("dependency was added by %s\n",
729 add_dep_by_userspace ? "userspace" : "kernel");
730 IPA_RM_DBG("ignore request to delete dependency by %s\n",
731 userspace_dep ? "userspace" : "kernel");
732 return 0;
733 }
734
735 IPA_RM_DBG("%s state: %d\n", ipa_rm_resource_str(resource->name),
736 resource->state);
737
738 resource->needed_bw -= depends_on->max_bw;
739 switch (resource->state) {
740 case IPA_RM_RELEASED:
741 break;
742 case IPA_RM_GRANTED:
743 ipa_rm_perf_profile_change(resource->name);
744 release_consumer = true;
745 break;
746 case IPA_RM_RELEASE_IN_PROGRESS:
747 if (((struct ipa_rm_resource_prod *)
748 resource)->pending_release > 0)
749 ((struct ipa_rm_resource_prod *)
750 resource)->pending_release--;
751 if (depends_on->state == IPA_RM_RELEASE_IN_PROGRESS &&
752 ((struct ipa_rm_resource_prod *)
753 resource)->pending_release == 0) {
754 resource->state = IPA_RM_RELEASED;
755 state_changed = true;
756 evt = IPA_RM_RESOURCE_RELEASED;
757 ipa_rm_perf_profile_change(resource->name);
758 }
759 break;
760 case IPA_RM_REQUEST_IN_PROGRESS:
761 release_consumer = true;
762 if (((struct ipa_rm_resource_prod *)
763 resource)->pending_request > 0)
764 ((struct ipa_rm_resource_prod *)
765 resource)->pending_request--;
766 if (depends_on->state == IPA_RM_REQUEST_IN_PROGRESS &&
767 ((struct ipa_rm_resource_prod *)
768 resource)->pending_request == 0) {
769 resource->state = IPA_RM_GRANTED;
770 state_changed = true;
771 evt = IPA_RM_RESOURCE_GRANTED;
772 ipa_rm_perf_profile_change(resource->name);
773 }
774 break;
775 default:
776 result = -EINVAL;
777 goto bail;
778 }
779 if (state_changed) {
780 (void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
781 resource->name,
782 evt,
783 false);
784 }
785 IPA_RM_DBG("%s new state: %d\n", ipa_rm_resource_str(resource->name),
786 resource->state);
787 ipa_rm_peers_list_remove_peer(resource->peers_list,
788 depends_on->name);
789 ipa_rm_peers_list_remove_peer(depends_on->peers_list,
790 resource->name);
791 if (release_consumer)
792 (void) ipa_rm_resource_consumer_release(
793 (struct ipa_rm_resource_cons *)depends_on,
794 resource->max_bw,
795 true);
796bail:
797 IPA_RM_DBG("EXIT with %d\n", result);
798
799 return result;
800}
801
802/**
803 * ipa_rm_resource_producer_request() - producer resource request
804 * @producer: [in] producer
805 *
806 * Returns: 0 on success, negative on failure
807 */
808int ipa_rm_resource_producer_request(struct ipa_rm_resource_prod *producer)
809{
810 int peers_index;
811 int result = 0;
812 struct ipa_rm_resource *consumer;
813 int consumer_result;
814 enum ipa_rm_resource_state state;
815
816 state = producer->resource.state;
817 switch (producer->resource.state) {
818 case IPA_RM_RELEASED:
819 case IPA_RM_RELEASE_IN_PROGRESS:
820 producer->resource.state = IPA_RM_REQUEST_IN_PROGRESS;
821 break;
822 case IPA_RM_GRANTED:
823 goto unlock_and_bail;
824 case IPA_RM_REQUEST_IN_PROGRESS:
825 result = -EINPROGRESS;
826 goto unlock_and_bail;
827 default:
828 result = -EINVAL;
829 goto unlock_and_bail;
830 }
831
832 producer->pending_request = 0;
833 for (peers_index = 0;
834 peers_index < ipa_rm_peers_list_get_size(
835 producer->resource.peers_list);
836 peers_index++) {
837 consumer = ipa_rm_peers_list_get_resource(peers_index,
838 producer->resource.peers_list);
839 if (consumer) {
840 producer->pending_request++;
841 consumer_result = ipa_rm_resource_consumer_request(
842 (struct ipa_rm_resource_cons *)consumer,
843 producer->resource.max_bw,
844 true, false);
845 if (consumer_result == -EINPROGRESS) {
846 result = -EINPROGRESS;
847 } else {
848 producer->pending_request--;
849 if (consumer_result != 0) {
850 result = consumer_result;
851 goto bail;
852 }
853 }
854 }
855 }
856
857 if (producer->pending_request == 0) {
858 producer->resource.state = IPA_RM_GRANTED;
859 ipa_rm_perf_profile_change(producer->resource.name);
860 (void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
861 producer->resource.name,
862 IPA_RM_RESOURCE_GRANTED,
863 true);
864 result = 0;
865 }
866unlock_and_bail:
867 if (state != producer->resource.state)
868 IPA_RM_DBG_LOW("%s state changed %d->%d\n",
869 ipa_rm_resource_str(producer->resource.name),
870 state,
871 producer->resource.state);
872bail:
873 return result;
874}
875
876/**
877 * ipa_rm_resource_producer_release() - producer resource release
878 * producer: [in] producer resource
879 *
880 * Returns: 0 on success, negative on failure
881 *
882 */
883int ipa_rm_resource_producer_release(struct ipa_rm_resource_prod *producer)
884{
885 int peers_index;
886 int result = 0;
887 struct ipa_rm_resource *consumer;
888 int consumer_result;
889 enum ipa_rm_resource_state state;
890
891 state = producer->resource.state;
892 switch (producer->resource.state) {
893 case IPA_RM_RELEASED:
894 goto bail;
895 case IPA_RM_GRANTED:
896 case IPA_RM_REQUEST_IN_PROGRESS:
897 producer->resource.state = IPA_RM_RELEASE_IN_PROGRESS;
898 break;
899 case IPA_RM_RELEASE_IN_PROGRESS:
900 result = -EINPROGRESS;
901 goto bail;
902 default:
903 result = -EPERM;
904 goto bail;
905 }
906
907 producer->pending_release = 0;
908 for (peers_index = 0;
909 peers_index < ipa_rm_peers_list_get_size(
910 producer->resource.peers_list);
911 peers_index++) {
912 consumer = ipa_rm_peers_list_get_resource(peers_index,
913 producer->resource.peers_list);
914 if (consumer) {
915 producer->pending_release++;
916 consumer_result = ipa_rm_resource_consumer_release(
917 (struct ipa_rm_resource_cons *)consumer,
918 producer->resource.max_bw,
919 true);
920 producer->pending_release--;
921 }
922 }
923
924 if (producer->pending_release == 0) {
925 producer->resource.state = IPA_RM_RELEASED;
926 ipa_rm_perf_profile_change(producer->resource.name);
927 (void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
928 producer->resource.name,
929 IPA_RM_RESOURCE_RELEASED,
930 true);
931 }
932bail:
933 if (state != producer->resource.state)
934 IPA_RM_DBG_LOW("%s state changed %d->%d\n",
935 ipa_rm_resource_str(producer->resource.name),
936 state,
937 producer->resource.state);
938
939 return result;
940}
941
942static void ipa_rm_resource_producer_handle_cb(
943 struct ipa_rm_resource_prod *producer,
944 enum ipa_rm_event event)
945{
946 IPA_RM_DBG_LOW("%s state: %d event: %d pending_request: %d\n",
947 ipa_rm_resource_str(producer->resource.name),
948 producer->resource.state,
949 event,
950 producer->pending_request);
951
952 switch (producer->resource.state) {
953 case IPA_RM_REQUEST_IN_PROGRESS:
954 if (event != IPA_RM_RESOURCE_GRANTED)
955 goto unlock_and_bail;
956 if (producer->pending_request > 0) {
957 producer->pending_request--;
958 if (producer->pending_request == 0) {
959 producer->resource.state =
960 IPA_RM_GRANTED;
961 ipa_rm_perf_profile_change(
962 producer->resource.name);
963 ipa_rm_resource_producer_notify_clients(
964 producer,
965 IPA_RM_RESOURCE_GRANTED,
966 false);
967 goto bail;
968 }
969 }
970 break;
971 case IPA_RM_RELEASE_IN_PROGRESS:
972 if (event != IPA_RM_RESOURCE_RELEASED)
973 goto unlock_and_bail;
974 if (producer->pending_release > 0) {
975 producer->pending_release--;
976 if (producer->pending_release == 0) {
977 producer->resource.state =
978 IPA_RM_RELEASED;
979 ipa_rm_perf_profile_change(
980 producer->resource.name);
981 ipa_rm_resource_producer_notify_clients(
982 producer,
983 IPA_RM_RESOURCE_RELEASED,
984 false);
985 goto bail;
986 }
987 }
988 break;
989 case IPA_RM_GRANTED:
990 case IPA_RM_RELEASED:
991 default:
992 goto unlock_and_bail;
993 }
994unlock_and_bail:
995 IPA_RM_DBG_LOW("%s new state: %d\n",
996 ipa_rm_resource_str(producer->resource.name),
997 producer->resource.state);
998bail:
999 return;
1000}
1001
1002/**
1003 * ipa_rm_resource_consumer_handle_cb() - propagates resource
1004 * notification to all dependent producers
1005 * @consumer: [in] notifying resource
1006 *
1007 */
1008void ipa_rm_resource_consumer_handle_cb(struct ipa_rm_resource_cons *consumer,
1009 enum ipa_rm_event event)
1010{
1011 int peers_index;
1012 struct ipa_rm_resource *producer;
1013
1014 if (!consumer) {
1015 IPA_RM_ERR("invalid params\n");
1016 return;
1017 }
1018 IPA_RM_DBG_LOW("%s state: %d event: %d\n",
1019 ipa_rm_resource_str(consumer->resource.name),
1020 consumer->resource.state,
1021 event);
1022
1023 switch (consumer->resource.state) {
1024 case IPA_RM_REQUEST_IN_PROGRESS:
1025 if (event == IPA_RM_RESOURCE_RELEASED)
1026 goto bail;
1027 consumer->resource.state = IPA_RM_GRANTED;
1028 ipa_rm_perf_profile_change(consumer->resource.name);
1029 ipa_resume_resource(consumer->resource.name);
1030 complete_all(&consumer->request_consumer_in_progress);
1031 break;
1032 case IPA_RM_RELEASE_IN_PROGRESS:
1033 if (event == IPA_RM_RESOURCE_GRANTED)
1034 goto bail;
1035 consumer->resource.state = IPA_RM_RELEASED;
1036 break;
1037 case IPA_RM_GRANTED:
1038 case IPA_RM_RELEASED:
1039 default:
1040 goto bail;
1041 }
1042
1043 for (peers_index = 0;
1044 peers_index < ipa_rm_peers_list_get_size(
1045 consumer->resource.peers_list);
1046 peers_index++) {
1047 producer = ipa_rm_peers_list_get_resource(peers_index,
1048 consumer->resource.peers_list);
1049 if (producer)
1050 ipa_rm_resource_producer_handle_cb(
1051 (struct ipa_rm_resource_prod *)
1052 producer,
1053 event);
1054 }
1055
1056 return;
1057bail:
1058 IPA_RM_DBG_LOW("%s new state: %d\n",
1059 ipa_rm_resource_str(consumer->resource.name),
1060 consumer->resource.state);
1061}
1062
1063/*
1064 * ipa_rm_resource_set_perf_profile() - sets the performance profile to
1065 * resource.
1066 *
1067 * @resource: [in] resource
1068 * @profile: [in] profile to be set
1069 *
1070 * sets the profile to the given resource, In case the resource is
1071 * granted, update bandwidth vote of the resource
1072 */
1073int ipa_rm_resource_set_perf_profile(struct ipa_rm_resource *resource,
1074 struct ipa_rm_perf_profile *profile)
1075{
1076 int peers_index;
1077 struct ipa_rm_resource *peer;
1078
1079 if (!resource || !profile) {
1080 IPA_RM_ERR("invalid params\n");
1081 return -EINVAL;
1082 }
1083
1084 if (profile->max_supported_bandwidth_mbps == resource->max_bw) {
1085 IPA_RM_DBG_LOW("same profile\n");
1086 return 0;
1087 }
1088
1089 if ((resource->type == IPA_RM_PRODUCER &&
1090 (resource->state == IPA_RM_GRANTED ||
1091 resource->state == IPA_RM_REQUEST_IN_PROGRESS)) ||
1092 resource->type == IPA_RM_CONSUMER) {
1093 for (peers_index = 0;
1094 peers_index < ipa_rm_peers_list_get_size(
1095 resource->peers_list);
1096 peers_index++) {
1097 peer = ipa_rm_peers_list_get_resource(peers_index,
1098 resource->peers_list);
1099 if (!peer)
1100 continue;
1101 peer->needed_bw -= resource->max_bw;
1102 peer->needed_bw +=
1103 profile->max_supported_bandwidth_mbps;
1104 if (peer->state == IPA_RM_GRANTED)
1105 ipa_rm_perf_profile_change(peer->name);
1106 }
1107 }
1108
1109 resource->max_bw = profile->max_supported_bandwidth_mbps;
1110 if (resource->state == IPA_RM_GRANTED)
1111 ipa_rm_perf_profile_change(resource->name);
1112
1113 return 0;
1114}
1115
1116
1117/*
1118 * ipa_rm_resource_producer_print_stat() - print the
1119 * resource status and all his dependencies
1120 *
1121 * @resource: [in] Resource resource
1122 * @buff: [in] The buf used to print
1123 * @size: [in] Buf size
1124 *
1125 * Returns: number of bytes used on success, negative on failure
1126 */
1127int ipa_rm_resource_producer_print_stat(
1128 struct ipa_rm_resource *resource,
1129 char *buf,
1130 int size){
1131
1132 int i;
1133 int nbytes;
1134 int cnt = 0;
1135 struct ipa_rm_resource *consumer;
1136
1137 if (!buf || size < 0)
1138 return -EINVAL;
1139
1140 nbytes = scnprintf(buf + cnt, size - cnt,
1141 ipa_rm_resource_str(resource->name));
1142 cnt += nbytes;
1143 nbytes = scnprintf(buf + cnt, size - cnt, "[%d, ", resource->max_bw);
1144 cnt += nbytes;
1145
1146 switch (resource->state) {
1147 case IPA_RM_RELEASED:
1148 nbytes = scnprintf(buf + cnt, size - cnt,
1149 "Released] -> ");
1150 cnt += nbytes;
1151 break;
1152 case IPA_RM_REQUEST_IN_PROGRESS:
1153 nbytes = scnprintf(buf + cnt, size - cnt,
1154 "Request In Progress] -> ");
1155 cnt += nbytes;
1156 break;
1157 case IPA_RM_GRANTED:
1158 nbytes = scnprintf(buf + cnt, size - cnt,
1159 "Granted] -> ");
1160 cnt += nbytes;
1161 break;
1162 case IPA_RM_RELEASE_IN_PROGRESS:
1163 nbytes = scnprintf(buf + cnt, size - cnt,
1164 "Release In Progress] -> ");
1165 cnt += nbytes;
1166 break;
1167 default:
1168 return -EPERM;
1169 }
1170
1171 for (i = 0; i < resource->peers_list->max_peers; ++i) {
1172 consumer =
1173 ipa_rm_peers_list_get_resource(
1174 i,
1175 resource->peers_list);
1176 if (consumer) {
1177 nbytes = scnprintf(buf + cnt, size - cnt,
1178 ipa_rm_resource_str(consumer->name));
1179 cnt += nbytes;
1180 nbytes = scnprintf(buf + cnt, size - cnt, "[%d, ",
1181 consumer->max_bw);
1182 cnt += nbytes;
1183
1184 switch (consumer->state) {
1185 case IPA_RM_RELEASED:
1186 nbytes = scnprintf(buf + cnt, size - cnt,
1187 "Released], ");
1188 cnt += nbytes;
1189 break;
1190 case IPA_RM_REQUEST_IN_PROGRESS:
1191 nbytes = scnprintf(buf + cnt, size - cnt,
1192 "Request In Progress], ");
1193 cnt += nbytes;
1194 break;
1195 case IPA_RM_GRANTED:
1196 nbytes = scnprintf(buf + cnt, size - cnt,
1197 "Granted], ");
1198 cnt += nbytes;
1199 break;
1200 case IPA_RM_RELEASE_IN_PROGRESS:
1201 nbytes = scnprintf(buf + cnt, size - cnt,
1202 "Release In Progress], ");
1203 cnt += nbytes;
1204 break;
1205 default:
1206 return -EPERM;
1207 }
1208 }
1209 }
1210 nbytes = scnprintf(buf + cnt, size - cnt, "\n");
1211 cnt += nbytes;
1212
1213 return cnt;
1214}