1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
- */
- #include <linux/slab.h>
- #include "ipa_rm_resource.h"
- #include "ipa_rm_i.h"
- #include "ipa_common_i.h"
- /**
- * ipa_rm_dep_prod_index() - producer name to producer index mapping
- * @resource_name: [in] resource name (should be of producer)
- *
- * Returns: resource index mapping, IPA_RM_INDEX_INVALID
- * in case provided resource name isn't contained
- * in enum ipa_rm_resource_name or is not of producers.
- *
- */
- int ipa_rm_prod_index(enum ipa_rm_resource_name resource_name)
- {
- int result = resource_name;
- switch (resource_name) {
- case IPA_RM_RESOURCE_Q6_PROD:
- case IPA_RM_RESOURCE_USB_PROD:
- case IPA_RM_RESOURCE_USB_DPL_DUMMY_PROD:
- case IPA_RM_RESOURCE_HSIC_PROD:
- case IPA_RM_RESOURCE_STD_ECM_PROD:
- case IPA_RM_RESOURCE_RNDIS_PROD:
- case IPA_RM_RESOURCE_WWAN_0_PROD:
- case IPA_RM_RESOURCE_WLAN_PROD:
- case IPA_RM_RESOURCE_ODU_ADAPT_PROD:
- case IPA_RM_RESOURCE_MHI_PROD:
- case IPA_RM_RESOURCE_ETHERNET_PROD:
- break;
- default:
- result = IPA_RM_INDEX_INVALID;
- break;
- }
- return result;
- }
- /**
- * ipa_rm_cons_index() - consumer name to consumer index mapping
- * @resource_name: [in] resource name (should be of consumer)
- *
- * Returns: resource index mapping, IPA_RM_INDEX_INVALID
- * in case provided resource name isn't contained
- * in enum ipa_rm_resource_name or is not of consumers.
- *
- */
- int ipa_rm_cons_index(enum ipa_rm_resource_name resource_name)
- {
- int result = resource_name;
- switch (resource_name) {
- case IPA_RM_RESOURCE_Q6_CONS:
- case IPA_RM_RESOURCE_USB_CONS:
- case IPA_RM_RESOURCE_HSIC_CONS:
- case IPA_RM_RESOURCE_WLAN_CONS:
- case IPA_RM_RESOURCE_APPS_CONS:
- case IPA_RM_RESOURCE_ODU_ADAPT_CONS:
- case IPA_RM_RESOURCE_MHI_CONS:
- case IPA_RM_RESOURCE_USB_DPL_CONS:
- case IPA_RM_RESOURCE_ETHERNET_CONS:
- break;
- default:
- result = IPA_RM_INDEX_INVALID;
- break;
- }
- return result;
- }
- int ipa_rm_resource_consumer_release_work(
- struct ipa_rm_resource_cons *consumer,
- enum ipa_rm_resource_state prev_state,
- bool notify_completion)
- {
- int driver_result;
- IPA_RM_DBG_LOW("calling driver CB\n");
- driver_result = consumer->release_resource();
- IPA_RM_DBG_LOW("driver CB returned with %d\n", driver_result);
- /*
- * Treat IPA_RM_RELEASE_IN_PROGRESS as IPA_RM_RELEASED
- * for CONS which remains in RELEASE_IN_PROGRESS.
- */
- if (driver_result == -EINPROGRESS)
- driver_result = 0;
- if (driver_result != 0 && driver_result != -EINPROGRESS) {
- IPA_RM_ERR("driver CB returned error %d\n", driver_result);
- consumer->resource.state = prev_state;
- goto bail;
- }
- if (driver_result == 0) {
- if (notify_completion)
- ipa_rm_resource_consumer_handle_cb(consumer,
- IPA_RM_RESOURCE_RELEASED);
- else
- consumer->resource.state = IPA_RM_RELEASED;
- }
- complete_all(&consumer->request_consumer_in_progress);
- ipa_rm_perf_profile_change(consumer->resource.name);
- bail:
- return driver_result;
- }
- int ipa_rm_resource_consumer_request_work(struct ipa_rm_resource_cons *consumer,
- enum ipa_rm_resource_state prev_state,
- u32 prod_needed_bw,
- bool notify_completion,
- bool dec_client_on_err)
- {
- int driver_result;
- IPA_RM_DBG_LOW("calling driver CB\n");
- driver_result = consumer->request_resource();
- IPA_RM_DBG_LOW("driver CB returned with %d\n", driver_result);
- if (driver_result == 0) {
- if (notify_completion) {
- ipa_rm_resource_consumer_handle_cb(consumer,
- IPA_RM_RESOURCE_GRANTED);
- } else {
- consumer->resource.state = IPA_RM_GRANTED;
- ipa_rm_perf_profile_change(consumer->resource.name);
- ipa_resume_resource(consumer->resource.name);
- }
- } else if (driver_result != -EINPROGRESS) {
- consumer->resource.state = prev_state;
- consumer->resource.needed_bw -= prod_needed_bw;
- if (dec_client_on_err)
- consumer->usage_count--;
- }
- return driver_result;
- }
- int ipa_rm_resource_consumer_request(
- struct ipa_rm_resource_cons *consumer,
- u32 prod_needed_bw,
- bool inc_usage_count,
- bool wake_client)
- {
- int result = 0;
- enum ipa_rm_resource_state prev_state;
- struct ipa_active_client_logging_info log_info;
- IPA_RM_DBG_LOW("%s state: %d\n",
- ipa_rm_resource_str(consumer->resource.name),
- consumer->resource.state);
- prev_state = consumer->resource.state;
- consumer->resource.needed_bw += prod_needed_bw;
- switch (consumer->resource.state) {
- case IPA_RM_RELEASED:
- case IPA_RM_RELEASE_IN_PROGRESS:
- reinit_completion(&consumer->request_consumer_in_progress);
- consumer->resource.state = IPA_RM_REQUEST_IN_PROGRESS;
- IPA_ACTIVE_CLIENTS_PREP_RESOURCE(log_info,
- ipa_rm_resource_str(consumer->resource.name));
- if (prev_state == IPA_RM_RELEASE_IN_PROGRESS ||
- ipa_inc_client_enable_clks_no_block(&log_info) != 0) {
- IPA_RM_DBG_LOW("async resume work for %s\n",
- ipa_rm_resource_str(consumer->resource.name));
- ipa_rm_wq_send_resume_cmd(consumer->resource.name,
- prev_state,
- prod_needed_bw,
- inc_usage_count);
- result = -EINPROGRESS;
- break;
- }
- result = ipa_rm_resource_consumer_request_work(consumer,
- prev_state,
- prod_needed_bw,
- false,
- inc_usage_count);
- break;
- case IPA_RM_GRANTED:
- if (wake_client) {
- result = ipa_rm_resource_consumer_request_work(
- consumer, prev_state, prod_needed_bw, false,
- inc_usage_count);
- break;
- }
- ipa_rm_perf_profile_change(consumer->resource.name);
- break;
- case IPA_RM_REQUEST_IN_PROGRESS:
- result = -EINPROGRESS;
- break;
- default:
- consumer->resource.needed_bw -= prod_needed_bw;
- result = -EPERM;
- goto bail;
- }
- if (inc_usage_count)
- consumer->usage_count++;
- bail:
- IPA_RM_DBG_LOW("%s new state: %d\n",
- ipa_rm_resource_str(consumer->resource.name),
- consumer->resource.state);
- IPA_RM_DBG_LOW("EXIT with %d\n", result);
- return result;
- }
- int ipa_rm_resource_consumer_release(
- struct ipa_rm_resource_cons *consumer,
- u32 prod_needed_bw,
- bool dec_usage_count)
- {
- int result = 0;
- enum ipa_rm_resource_state save_state;
- IPA_RM_DBG_LOW("%s state: %d\n",
- ipa_rm_resource_str(consumer->resource.name),
- consumer->resource.state);
- save_state = consumer->resource.state;
- consumer->resource.needed_bw -= prod_needed_bw;
- switch (consumer->resource.state) {
- case IPA_RM_RELEASED:
- break;
- case IPA_RM_GRANTED:
- case IPA_RM_REQUEST_IN_PROGRESS:
- if (dec_usage_count && consumer->usage_count > 0)
- consumer->usage_count--;
- if (consumer->usage_count == 0) {
- consumer->resource.state = IPA_RM_RELEASE_IN_PROGRESS;
- if (save_state == IPA_RM_REQUEST_IN_PROGRESS ||
- ipa_suspend_resource_no_block(
- consumer->resource.name) != 0) {
- ipa_rm_wq_send_suspend_cmd(
- consumer->resource.name,
- save_state,
- prod_needed_bw);
- result = -EINPROGRESS;
- goto bail;
- }
- result = ipa_rm_resource_consumer_release_work(consumer,
- save_state, false);
- goto bail;
- } else if (consumer->resource.state == IPA_RM_GRANTED) {
- ipa_rm_perf_profile_change(consumer->resource.name);
- }
- break;
- case IPA_RM_RELEASE_IN_PROGRESS:
- if (dec_usage_count && consumer->usage_count > 0)
- consumer->usage_count--;
- result = -EINPROGRESS;
- break;
- default:
- result = -EPERM;
- goto bail;
- }
- bail:
- IPA_RM_DBG_LOW("%s new state: %d\n",
- ipa_rm_resource_str(consumer->resource.name),
- consumer->resource.state);
- IPA_RM_DBG_LOW("EXIT with %d\n", result);
- return result;
- }
- /**
- * ipa_rm_resource_producer_notify_clients() - notify
- * all registered clients of given producer
- * @producer: producer
- * @event: event to notify
- * @notify_registered_only: notify only clients registered by
- * ipa_rm_register()
- */
- void ipa_rm_resource_producer_notify_clients(
- struct ipa_rm_resource_prod *producer,
- enum ipa_rm_event event,
- bool notify_registered_only)
- {
- struct ipa_rm_notification_info *reg_info;
- IPA_RM_DBG_LOW("%s event: %d notify_registered_only: %d\n",
- ipa_rm_resource_str(producer->resource.name),
- event,
- notify_registered_only);
- list_for_each_entry(reg_info, &(producer->event_listeners), link) {
- if (notify_registered_only && !reg_info->explicit)
- continue;
- IPA_RM_DBG_LOW("Notifying %s event: %d\n",
- ipa_rm_resource_str(producer->resource.name), event);
- reg_info->reg_params.notify_cb(reg_info->reg_params.user_data,
- event,
- 0);
- IPA_RM_DBG_LOW("back from client CB\n");
- }
- }
- static int ipa_rm_resource_producer_create(struct ipa_rm_resource **resource,
- struct ipa_rm_resource_prod **producer,
- struct ipa_rm_create_params *create_params,
- int *max_peers)
- {
- int result = 0;
- *producer = kzalloc(sizeof(**producer), GFP_ATOMIC);
- if (*producer == NULL) {
- result = -ENOMEM;
- goto bail;
- }
- INIT_LIST_HEAD(&((*producer)->event_listeners));
- result = ipa_rm_resource_producer_register(*producer,
- &(create_params->reg_params),
- false);
- if (result) {
- IPA_RM_ERR("ipa_rm_resource_producer_register() failed\n");
- goto register_fail;
- }
- (*resource) = (struct ipa_rm_resource *) (*producer);
- (*resource)->type = IPA_RM_PRODUCER;
- *max_peers = IPA_RM_RESOURCE_MAX;
- goto bail;
- register_fail:
- kfree(*producer);
- bail:
- return result;
- }
- static void ipa_rm_resource_producer_delete(
- struct ipa_rm_resource_prod *producer)
- {
- struct ipa_rm_notification_info *reg_info;
- struct list_head *pos, *q;
- ipa_rm_resource_producer_release(producer);
- list_for_each_safe(pos, q, &(producer->event_listeners)) {
- reg_info = list_entry(pos,
- struct ipa_rm_notification_info,
- link);
- list_del(pos);
- kfree(reg_info);
- }
- }
- static int ipa_rm_resource_consumer_create(struct ipa_rm_resource **resource,
- struct ipa_rm_resource_cons **consumer,
- struct ipa_rm_create_params *create_params,
- int *max_peers)
- {
- int result = 0;
- *consumer = kzalloc(sizeof(**consumer), GFP_ATOMIC);
- if (*consumer == NULL) {
- result = -ENOMEM;
- goto bail;
- }
- (*consumer)->request_resource = create_params->request_resource;
- (*consumer)->release_resource = create_params->release_resource;
- (*resource) = (struct ipa_rm_resource *) (*consumer);
- (*resource)->type = IPA_RM_CONSUMER;
- init_completion(&((*consumer)->request_consumer_in_progress));
- *max_peers = IPA_RM_RESOURCE_MAX;
- bail:
- return result;
- }
- /**
- * ipa_rm_resource_create() - creates resource
- * @create_params: [in] parameters needed
- * for resource initialization with IPA RM
- * @resource: [out] created resource
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa_rm_resource_create(
- struct ipa_rm_create_params *create_params,
- struct ipa_rm_resource **resource)
- {
- struct ipa_rm_resource_cons *consumer;
- struct ipa_rm_resource_prod *producer;
- int max_peers;
- int result = 0;
- if (!create_params) {
- result = -EINVAL;
- goto bail;
- }
- if (IPA_RM_RESORCE_IS_PROD(create_params->name)) {
- result = ipa_rm_resource_producer_create(resource,
- &producer,
- create_params,
- &max_peers);
- if (result) {
- IPA_RM_ERR("ipa_rm_resource_producer_create failed\n");
- goto bail;
- }
- } else if (IPA_RM_RESORCE_IS_CONS(create_params->name)) {
- result = ipa_rm_resource_consumer_create(resource,
- &consumer,
- create_params,
- &max_peers);
- if (result) {
- IPA_RM_ERR("ipa_rm_resource_producer_create failed\n");
- goto bail;
- }
- } else {
- IPA_RM_ERR("invalid resource\n");
- result = -EPERM;
- goto bail;
- }
- result = ipa_rm_peers_list_create(max_peers,
- &((*resource)->peers_list));
- if (result) {
- IPA_RM_ERR("ipa_rm_peers_list_create failed\n");
- goto peers_alloc_fail;
- }
- (*resource)->name = create_params->name;
- (*resource)->floor_voltage = create_params->floor_voltage;
- (*resource)->state = IPA_RM_RELEASED;
- goto bail;
- peers_alloc_fail:
- ipa_rm_resource_delete(*resource);
- bail:
- return result;
- }
- /**
- * ipa_rm_resource_delete() - deletes resource
- * @resource: [in] resource
- * for resource initialization with IPA RM
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa_rm_resource_delete(struct ipa_rm_resource *resource)
- {
- struct ipa_rm_resource *consumer;
- struct ipa_rm_resource *producer;
- int peers_index;
- int result = 0;
- int list_size;
- bool userspace_dep;
- if (!resource) {
- IPA_RM_ERR("invalid params\n");
- return -EINVAL;
- }
- IPA_RM_DBG("ENTER with resource %d\n", resource->name);
- if (resource->type == IPA_RM_PRODUCER) {
- if (resource->peers_list) {
- list_size = ipa_rm_peers_list_get_size(
- resource->peers_list);
- for (peers_index = 0;
- peers_index < list_size;
- peers_index++) {
- consumer = ipa_rm_peers_list_get_resource(
- peers_index,
- resource->peers_list);
- if (consumer) {
- userspace_dep =
- ipa_rm_peers_list_get_userspace_dep(
- peers_index,
- resource->peers_list);
- ipa_rm_resource_delete_dependency(
- resource,
- consumer,
- userspace_dep);
- }
- }
- }
- ipa_rm_resource_producer_delete(
- (struct ipa_rm_resource_prod *) resource);
- } else if (resource->type == IPA_RM_CONSUMER) {
- if (resource->peers_list) {
- list_size = ipa_rm_peers_list_get_size(
- resource->peers_list);
- for (peers_index = 0;
- peers_index < list_size;
- peers_index++){
- producer = ipa_rm_peers_list_get_resource(
- peers_index,
- resource->peers_list);
- if (producer) {
- userspace_dep =
- ipa_rm_peers_list_get_userspace_dep(
- peers_index,
- resource->peers_list);
- ipa_rm_resource_delete_dependency(
- producer,
- resource,
- userspace_dep);
- }
- }
- }
- }
- ipa_rm_peers_list_delete(resource->peers_list);
- kfree(resource);
- return result;
- }
- /**
- * ipa_rm_resource_register() - register resource
- * @resource: [in] resource
- * @reg_params: [in] registration parameters
- * @explicit: [in] registered explicitly by ipa_rm_register()
- *
- * Returns: 0 on success, negative on failure
- *
- * Producer resource is expected for this call.
- *
- */
- int ipa_rm_resource_producer_register(struct ipa_rm_resource_prod *producer,
- struct ipa_rm_register_params *reg_params,
- bool explicit)
- {
- int result = 0;
- struct ipa_rm_notification_info *reg_info;
- struct list_head *pos;
- if (!producer || !reg_params) {
- IPA_RM_ERR("invalid params\n");
- result = -EPERM;
- goto bail;
- }
- list_for_each(pos, &(producer->event_listeners)) {
- reg_info = list_entry(pos,
- struct ipa_rm_notification_info,
- link);
- if (reg_info->reg_params.notify_cb ==
- reg_params->notify_cb) {
- IPA_RM_ERR("already registered\n");
- result = -EPERM;
- goto bail;
- }
- }
- reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
- if (reg_info == NULL) {
- result = -ENOMEM;
- goto bail;
- }
- reg_info->reg_params.user_data = reg_params->user_data;
- reg_info->reg_params.notify_cb = reg_params->notify_cb;
- reg_info->explicit = explicit;
- INIT_LIST_HEAD(®_info->link);
- list_add(®_info->link, &producer->event_listeners);
- bail:
- return result;
- }
- /**
- * ipa_rm_resource_deregister() - register resource
- * @resource: [in] resource
- * @reg_params: [in] registration parameters
- *
- * Returns: 0 on success, negative on failure
- *
- * Producer resource is expected for this call.
- * This function deleted only single instance of
- * registration info.
- *
- */
- int ipa_rm_resource_producer_deregister(struct ipa_rm_resource_prod *producer,
- struct ipa_rm_register_params *reg_params)
- {
- int result = -EINVAL;
- struct ipa_rm_notification_info *reg_info;
- struct list_head *pos, *q;
- if (!producer || !reg_params) {
- IPA_RM_ERR("invalid params\n");
- return -EINVAL;
- }
- list_for_each_safe(pos, q, &(producer->event_listeners)) {
- reg_info = list_entry(pos,
- struct ipa_rm_notification_info,
- link);
- if (reg_info->reg_params.notify_cb ==
- reg_params->notify_cb) {
- list_del(pos);
- kfree(reg_info);
- result = 0;
- goto bail;
- }
- }
- bail:
- return result;
- }
- /**
- * ipa_rm_resource_add_dependency() - add dependency between two
- * given resources
- * @resource: [in] resource resource
- * @depends_on: [in] depends_on resource
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa_rm_resource_add_dependency(struct ipa_rm_resource *resource,
- struct ipa_rm_resource *depends_on,
- bool userspace_dep)
- {
- int result = 0;
- int consumer_result;
- bool add_dep_by_userspace;
- if (!resource || !depends_on) {
- IPA_RM_ERR("invalid params\n");
- return -EINVAL;
- }
- if (ipa_rm_peers_list_check_dependency(resource->peers_list,
- resource->name,
- depends_on->peers_list,
- depends_on->name,
- &add_dep_by_userspace)) {
- IPA_RM_ERR("dependency already exists, added by %s\n",
- add_dep_by_userspace ? "userspace" : "kernel");
- return -EEXIST;
- }
- ipa_rm_peers_list_add_peer(resource->peers_list, depends_on,
- userspace_dep);
- ipa_rm_peers_list_add_peer(depends_on->peers_list, resource,
- userspace_dep);
- IPA_RM_DBG("%s state: %d\n", ipa_rm_resource_str(resource->name),
- resource->state);
- resource->needed_bw += depends_on->max_bw;
- switch (resource->state) {
- case IPA_RM_RELEASED:
- case IPA_RM_RELEASE_IN_PROGRESS:
- break;
- case IPA_RM_GRANTED:
- case IPA_RM_REQUEST_IN_PROGRESS:
- {
- enum ipa_rm_resource_state prev_state = resource->state;
- resource->state = IPA_RM_REQUEST_IN_PROGRESS;
- ((struct ipa_rm_resource_prod *)
- resource)->pending_request++;
- consumer_result = ipa_rm_resource_consumer_request(
- (struct ipa_rm_resource_cons *)depends_on,
- resource->max_bw,
- true, false);
- if (consumer_result != -EINPROGRESS) {
- resource->state = prev_state;
- ((struct ipa_rm_resource_prod *)
- resource)->pending_request--;
- ipa_rm_perf_profile_change(resource->name);
- }
- result = consumer_result;
- break;
- }
- default:
- IPA_RM_ERR("invalid state\n");
- result = -EPERM;
- goto bail;
- }
- bail:
- IPA_RM_DBG("%s new state: %d\n", ipa_rm_resource_str(resource->name),
- resource->state);
- IPA_RM_DBG("EXIT with %d\n", result);
- return result;
- }
- /**
- * ipa_rm_resource_delete_dependency() - add dependency between two
- * given resources
- * @resource: [in] resource resource
- * @depends_on: [in] depends_on resource
- *
- * Returns: 0 on success, negative on failure
- * In case the resource state was changed, a notification
- * will be sent to the RM client
- */
- int ipa_rm_resource_delete_dependency(struct ipa_rm_resource *resource,
- struct ipa_rm_resource *depends_on,
- bool userspace_dep)
- {
- int result = 0;
- bool state_changed = false;
- bool release_consumer = false;
- enum ipa_rm_event evt;
- bool add_dep_by_userspace;
- if (!resource || !depends_on) {
- IPA_RM_ERR("invalid params\n");
- return -EINVAL;
- }
- if (!ipa_rm_peers_list_check_dependency(resource->peers_list,
- resource->name,
- depends_on->peers_list,
- depends_on->name,
- &add_dep_by_userspace)) {
- IPA_RM_ERR("dependency does not exist\n");
- return -EINVAL;
- }
- /*
- * to avoid race conditions between kernel and userspace
- * need to check that the dependency was added by same entity
- */
- if (add_dep_by_userspace != userspace_dep) {
- IPA_RM_DBG("dependency was added by %s\n",
- add_dep_by_userspace ? "userspace" : "kernel");
- IPA_RM_DBG("ignore request to delete dependency by %s\n",
- userspace_dep ? "userspace" : "kernel");
- return 0;
- }
- IPA_RM_DBG("%s state: %d\n", ipa_rm_resource_str(resource->name),
- resource->state);
- resource->needed_bw -= depends_on->max_bw;
- switch (resource->state) {
- case IPA_RM_RELEASED:
- break;
- case IPA_RM_GRANTED:
- ipa_rm_perf_profile_change(resource->name);
- release_consumer = true;
- break;
- case IPA_RM_RELEASE_IN_PROGRESS:
- if (((struct ipa_rm_resource_prod *)
- resource)->pending_release > 0)
- ((struct ipa_rm_resource_prod *)
- resource)->pending_release--;
- if (depends_on->state == IPA_RM_RELEASE_IN_PROGRESS &&
- ((struct ipa_rm_resource_prod *)
- resource)->pending_release == 0) {
- resource->state = IPA_RM_RELEASED;
- state_changed = true;
- evt = IPA_RM_RESOURCE_RELEASED;
- ipa_rm_perf_profile_change(resource->name);
- }
- break;
- case IPA_RM_REQUEST_IN_PROGRESS:
- release_consumer = true;
- if (((struct ipa_rm_resource_prod *)
- resource)->pending_request > 0)
- ((struct ipa_rm_resource_prod *)
- resource)->pending_request--;
- if (depends_on->state == IPA_RM_REQUEST_IN_PROGRESS &&
- ((struct ipa_rm_resource_prod *)
- resource)->pending_request == 0) {
- resource->state = IPA_RM_GRANTED;
- state_changed = true;
- evt = IPA_RM_RESOURCE_GRANTED;
- ipa_rm_perf_profile_change(resource->name);
- }
- break;
- default:
- result = -EINVAL;
- goto bail;
- }
- if (state_changed) {
- (void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
- resource->name,
- evt,
- false);
- }
- IPA_RM_DBG("%s new state: %d\n", ipa_rm_resource_str(resource->name),
- resource->state);
- ipa_rm_peers_list_remove_peer(resource->peers_list,
- depends_on->name);
- ipa_rm_peers_list_remove_peer(depends_on->peers_list,
- resource->name);
- if (release_consumer)
- (void) ipa_rm_resource_consumer_release(
- (struct ipa_rm_resource_cons *)depends_on,
- resource->max_bw,
- true);
- bail:
- IPA_RM_DBG("EXIT with %d\n", result);
- return result;
- }
- /**
- * ipa_rm_resource_producer_request() - producer resource request
- * @producer: [in] producer
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa_rm_resource_producer_request(struct ipa_rm_resource_prod *producer)
- {
- int peers_index;
- int result = 0;
- struct ipa_rm_resource *consumer;
- int consumer_result;
- enum ipa_rm_resource_state state;
- state = producer->resource.state;
- switch (producer->resource.state) {
- case IPA_RM_RELEASED:
- case IPA_RM_RELEASE_IN_PROGRESS:
- producer->resource.state = IPA_RM_REQUEST_IN_PROGRESS;
- break;
- case IPA_RM_GRANTED:
- goto unlock_and_bail;
- case IPA_RM_REQUEST_IN_PROGRESS:
- result = -EINPROGRESS;
- goto unlock_and_bail;
- default:
- result = -EINVAL;
- goto unlock_and_bail;
- }
- producer->pending_request = 0;
- for (peers_index = 0;
- peers_index < ipa_rm_peers_list_get_size(
- producer->resource.peers_list);
- peers_index++) {
- consumer = ipa_rm_peers_list_get_resource(peers_index,
- producer->resource.peers_list);
- if (consumer) {
- producer->pending_request++;
- consumer_result = ipa_rm_resource_consumer_request(
- (struct ipa_rm_resource_cons *)consumer,
- producer->resource.max_bw,
- true, false);
- if (consumer_result == -EINPROGRESS) {
- result = -EINPROGRESS;
- } else {
- producer->pending_request--;
- if (consumer_result != 0) {
- result = consumer_result;
- goto bail;
- }
- }
- }
- }
- if (producer->pending_request == 0) {
- producer->resource.state = IPA_RM_GRANTED;
- ipa_rm_perf_profile_change(producer->resource.name);
- (void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
- producer->resource.name,
- IPA_RM_RESOURCE_GRANTED,
- true);
- result = 0;
- }
- unlock_and_bail:
- if (state != producer->resource.state)
- IPA_RM_DBG_LOW("%s state changed %d->%d\n",
- ipa_rm_resource_str(producer->resource.name),
- state,
- producer->resource.state);
- bail:
- return result;
- }
- /**
- * ipa_rm_resource_producer_release() - producer resource release
- * producer: [in] producer resource
- *
- * Returns: 0 on success, negative on failure
- *
- */
- int ipa_rm_resource_producer_release(struct ipa_rm_resource_prod *producer)
- {
- int peers_index;
- int result = 0;
- struct ipa_rm_resource *consumer;
- int consumer_result;
- enum ipa_rm_resource_state state;
- state = producer->resource.state;
- switch (producer->resource.state) {
- case IPA_RM_RELEASED:
- goto bail;
- case IPA_RM_GRANTED:
- case IPA_RM_REQUEST_IN_PROGRESS:
- producer->resource.state = IPA_RM_RELEASE_IN_PROGRESS;
- break;
- case IPA_RM_RELEASE_IN_PROGRESS:
- result = -EINPROGRESS;
- goto bail;
- default:
- result = -EPERM;
- goto bail;
- }
- producer->pending_release = 0;
- for (peers_index = 0;
- peers_index < ipa_rm_peers_list_get_size(
- producer->resource.peers_list);
- peers_index++) {
- consumer = ipa_rm_peers_list_get_resource(peers_index,
- producer->resource.peers_list);
- if (consumer) {
- producer->pending_release++;
- consumer_result = ipa_rm_resource_consumer_release(
- (struct ipa_rm_resource_cons *)consumer,
- producer->resource.max_bw,
- true);
- producer->pending_release--;
- }
- }
- if (producer->pending_release == 0) {
- producer->resource.state = IPA_RM_RELEASED;
- ipa_rm_perf_profile_change(producer->resource.name);
- (void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
- producer->resource.name,
- IPA_RM_RESOURCE_RELEASED,
- true);
- }
- bail:
- if (state != producer->resource.state)
- IPA_RM_DBG_LOW("%s state changed %d->%d\n",
- ipa_rm_resource_str(producer->resource.name),
- state,
- producer->resource.state);
- return result;
- }
- static void ipa_rm_resource_producer_handle_cb(
- struct ipa_rm_resource_prod *producer,
- enum ipa_rm_event event)
- {
- IPA_RM_DBG_LOW("%s state: %d event: %d pending_request: %d\n",
- ipa_rm_resource_str(producer->resource.name),
- producer->resource.state,
- event,
- producer->pending_request);
- switch (producer->resource.state) {
- case IPA_RM_REQUEST_IN_PROGRESS:
- if (event != IPA_RM_RESOURCE_GRANTED)
- goto unlock_and_bail;
- if (producer->pending_request > 0) {
- producer->pending_request--;
- if (producer->pending_request == 0) {
- producer->resource.state =
- IPA_RM_GRANTED;
- ipa_rm_perf_profile_change(
- producer->resource.name);
- ipa_rm_resource_producer_notify_clients(
- producer,
- IPA_RM_RESOURCE_GRANTED,
- false);
- goto bail;
- }
- }
- break;
- case IPA_RM_RELEASE_IN_PROGRESS:
- if (event != IPA_RM_RESOURCE_RELEASED)
- goto unlock_and_bail;
- if (producer->pending_release > 0) {
- producer->pending_release--;
- if (producer->pending_release == 0) {
- producer->resource.state =
- IPA_RM_RELEASED;
- ipa_rm_perf_profile_change(
- producer->resource.name);
- ipa_rm_resource_producer_notify_clients(
- producer,
- IPA_RM_RESOURCE_RELEASED,
- false);
- goto bail;
- }
- }
- break;
- case IPA_RM_GRANTED:
- case IPA_RM_RELEASED:
- default:
- goto unlock_and_bail;
- }
- unlock_and_bail:
- IPA_RM_DBG_LOW("%s new state: %d\n",
- ipa_rm_resource_str(producer->resource.name),
- producer->resource.state);
- bail:
- return;
- }
- /**
- * ipa_rm_resource_consumer_handle_cb() - propagates resource
- * notification to all dependent producers
- * @consumer: [in] notifying resource
- *
- */
- void ipa_rm_resource_consumer_handle_cb(struct ipa_rm_resource_cons *consumer,
- enum ipa_rm_event event)
- {
- int peers_index;
- struct ipa_rm_resource *producer;
- if (!consumer) {
- IPA_RM_ERR("invalid params\n");
- return;
- }
- IPA_RM_DBG_LOW("%s state: %d event: %d\n",
- ipa_rm_resource_str(consumer->resource.name),
- consumer->resource.state,
- event);
- switch (consumer->resource.state) {
- case IPA_RM_REQUEST_IN_PROGRESS:
- if (event == IPA_RM_RESOURCE_RELEASED)
- goto bail;
- consumer->resource.state = IPA_RM_GRANTED;
- ipa_rm_perf_profile_change(consumer->resource.name);
- ipa_resume_resource(consumer->resource.name);
- complete_all(&consumer->request_consumer_in_progress);
- break;
- case IPA_RM_RELEASE_IN_PROGRESS:
- if (event == IPA_RM_RESOURCE_GRANTED)
- goto bail;
- consumer->resource.state = IPA_RM_RELEASED;
- break;
- case IPA_RM_GRANTED:
- case IPA_RM_RELEASED:
- default:
- goto bail;
- }
- for (peers_index = 0;
- peers_index < ipa_rm_peers_list_get_size(
- consumer->resource.peers_list);
- peers_index++) {
- producer = ipa_rm_peers_list_get_resource(peers_index,
- consumer->resource.peers_list);
- if (producer)
- ipa_rm_resource_producer_handle_cb(
- (struct ipa_rm_resource_prod *)
- producer,
- event);
- }
- return;
- bail:
- IPA_RM_DBG_LOW("%s new state: %d\n",
- ipa_rm_resource_str(consumer->resource.name),
- consumer->resource.state);
- }
- /*
- * ipa_rm_resource_set_perf_profile() - sets the performance profile to
- * resource.
- *
- * @resource: [in] resource
- * @profile: [in] profile to be set
- *
- * sets the profile to the given resource, In case the resource is
- * granted, update bandwidth vote of the resource
- */
- int ipa_rm_resource_set_perf_profile(struct ipa_rm_resource *resource,
- struct ipa_rm_perf_profile *profile)
- {
- int peers_index;
- struct ipa_rm_resource *peer;
- if (!resource || !profile) {
- IPA_RM_ERR("invalid params\n");
- return -EINVAL;
- }
- if (profile->max_supported_bandwidth_mbps == resource->max_bw) {
- IPA_RM_DBG_LOW("same profile\n");
- return 0;
- }
- if ((resource->type == IPA_RM_PRODUCER &&
- (resource->state == IPA_RM_GRANTED ||
- resource->state == IPA_RM_REQUEST_IN_PROGRESS)) ||
- resource->type == IPA_RM_CONSUMER) {
- for (peers_index = 0;
- peers_index < ipa_rm_peers_list_get_size(
- resource->peers_list);
- peers_index++) {
- peer = ipa_rm_peers_list_get_resource(peers_index,
- resource->peers_list);
- if (!peer)
- continue;
- peer->needed_bw -= resource->max_bw;
- peer->needed_bw +=
- profile->max_supported_bandwidth_mbps;
- if (peer->state == IPA_RM_GRANTED)
- ipa_rm_perf_profile_change(peer->name);
- }
- }
- resource->max_bw = profile->max_supported_bandwidth_mbps;
- if (resource->state == IPA_RM_GRANTED)
- ipa_rm_perf_profile_change(resource->name);
- return 0;
- }
- /*
- * ipa_rm_resource_producer_print_stat() - print the
- * resource status and all his dependencies
- *
- * @resource: [in] Resource resource
- * @buff: [in] The buf used to print
- * @size: [in] Buf size
- *
- * Returns: number of bytes used on success, negative on failure
- */
- int ipa_rm_resource_producer_print_stat(
- struct ipa_rm_resource *resource,
- char *buf,
- int size)
- {
- int i;
- int nbytes;
- int cnt = 0;
- struct ipa_rm_resource *consumer;
- if (!buf || size < 0)
- return -EINVAL;
- nbytes = scnprintf(buf + cnt, size - cnt,
- ipa_rm_resource_str(resource->name));
- cnt += nbytes;
- nbytes = scnprintf(buf + cnt, size - cnt, "[%d, ", resource->max_bw);
- cnt += nbytes;
- switch (resource->state) {
- case IPA_RM_RELEASED:
- nbytes = scnprintf(buf + cnt, size - cnt,
- "Released] -> ");
- cnt += nbytes;
- break;
- case IPA_RM_REQUEST_IN_PROGRESS:
- nbytes = scnprintf(buf + cnt, size - cnt,
- "Request In Progress] -> ");
- cnt += nbytes;
- break;
- case IPA_RM_GRANTED:
- nbytes = scnprintf(buf + cnt, size - cnt,
- "Granted] -> ");
- cnt += nbytes;
- break;
- case IPA_RM_RELEASE_IN_PROGRESS:
- nbytes = scnprintf(buf + cnt, size - cnt,
- "Release In Progress] -> ");
- cnt += nbytes;
- break;
- default:
- return -EPERM;
- }
- for (i = 0; i < resource->peers_list->max_peers; ++i) {
- consumer =
- ipa_rm_peers_list_get_resource(
- i,
- resource->peers_list);
- if (consumer) {
- nbytes = scnprintf(buf + cnt, size - cnt,
- ipa_rm_resource_str(consumer->name));
- cnt += nbytes;
- nbytes = scnprintf(buf + cnt, size - cnt, "[%d, ",
- consumer->max_bw);
- cnt += nbytes;
- switch (consumer->state) {
- case IPA_RM_RELEASED:
- nbytes = scnprintf(buf + cnt, size - cnt,
- "Released], ");
- cnt += nbytes;
- break;
- case IPA_RM_REQUEST_IN_PROGRESS:
- nbytes = scnprintf(buf + cnt, size - cnt,
- "Request In Progress], ");
- cnt += nbytes;
- break;
- case IPA_RM_GRANTED:
- nbytes = scnprintf(buf + cnt, size - cnt,
- "Granted], ");
- cnt += nbytes;
- break;
- case IPA_RM_RELEASE_IN_PROGRESS:
- nbytes = scnprintf(buf + cnt, size - cnt,
- "Release In Progress], ");
- cnt += nbytes;
- break;
- default:
- return -EPERM;
- }
- }
- }
- nbytes = scnprintf(buf + cnt, size - cnt, "\n");
- cnt += nbytes;
- return cnt;
- }
|