aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorgi Djakov <georgi.djakov@linaro.org>2019-10-17 11:41:08 +0300
committerGeorgi Djakov <georgi.djakov@linaro.org>2020-02-11 19:52:33 +0200
commite43cb6a03f8df8a874afae11c2dc11bb1509c1d2 (patch)
tree1ff02dea8e07fd66cddd3ad2df86d30a3716aa85
parentd8b41992dca4dc5a2b9396ea5b8b7d1c87a76804 (diff)
interconnect: Introduce per-provider locks
Currently, there is a single lock, the icc_lock, that used for handling all icc_set_bw() requests, including those for unrelated paths. This means that some request are unnecessarily serialized, which may in turn have impact on performance. To address this issue, we are introducing a provider specific locks. Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
-rw-r--r--drivers/interconnect/core.c21
-rw-r--r--include/linux/interconnect-provider.h2
2 files changed, 19 insertions, 4 deletions
diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
index f277e467156f..e18b51012f1b 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -50,6 +50,7 @@ static int icc_summary_show(struct seq_file *s, void *data)
list_for_each_entry(provider, &icc_providers, provider_list) {
struct icc_node *n;
+ mutex_lock(&provider->lock);
list_for_each_entry(n, &provider->nodes, node_list) {
struct icc_req *r;
@@ -63,6 +64,7 @@ static int icc_summary_show(struct seq_file *s, void *data)
r->peak_bw);
}
}
+ mutex_unlock(&provider->lock);
}
mutex_unlock(&icc_lock);
@@ -246,12 +248,15 @@ static int aggregate_requests(struct icc_node *node)
node->avg_bw = 0;
node->peak_bw = 0;
+ mutex_lock(&p->lock);
+
if (p->pre_aggregate)
p->pre_aggregate(node);
hlist_for_each_entry(r, &node->req_list, req_node)
p->aggregate(node, r->tag, r->avg_bw, r->peak_bw,
&node->avg_bw, &node->peak_bw);
+ mutex_unlock(&p->lock);
return 0;
}
@@ -262,6 +267,8 @@ static int apply_constraints(struct icc_path *path)
int ret = -EINVAL;
int i;
+ mutex_lock(&icc_lock);
+
for (i = 0; i < path->num_nodes; i++) {
next = path->reqs[i].node;
@@ -282,6 +289,8 @@ static int apply_constraints(struct icc_path *path)
prev = next;
}
out:
+ mutex_unlock(&icc_lock);
+
return ret;
}
@@ -501,8 +510,6 @@ int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
if (WARN_ON(IS_ERR(path) || !path->num_nodes))
return -EINVAL;
- mutex_lock(&icc_lock);
-
old_avg = path->reqs[0].avg_bw;
old_peak = path->reqs[0].peak_bw;
@@ -533,8 +540,6 @@ int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
apply_constraints(path);
}
- mutex_unlock(&icc_lock);
-
trace_icc_set_bw_end(path, ret);
return ret;
@@ -608,9 +613,11 @@ void icc_put(struct icc_path *path)
mutex_lock(&icc_lock);
for (i = 0; i < path->num_nodes; i++) {
node = path->reqs[i].node;
+ mutex_lock(&node->provider->lock);
hlist_del(&path->reqs[i].req_node);
if (!WARN_ON(!node->provider->users))
node->provider->users--;
+ mutex_unlock(&node->provider->lock);
}
mutex_unlock(&icc_lock);
@@ -791,8 +798,10 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider)
{
mutex_lock(&icc_lock);
+ mutex_lock(&provider->lock);
node->provider = provider;
list_add_tail(&node->node_list, &provider->nodes);
+ mutex_unlock(&provider->lock);
mutex_unlock(&icc_lock);
}
@@ -806,7 +815,9 @@ void icc_node_del(struct icc_node *node)
{
mutex_lock(&icc_lock);
+ mutex_lock(&node->provider->lock);
list_del(&node->node_list);
+ mutex_unlock(&node->provider->lock);
mutex_unlock(&icc_lock);
}
@@ -847,6 +858,8 @@ int icc_provider_add(struct icc_provider *provider)
if (WARN_ON(!provider->xlate))
return -EINVAL;
+ mutex_init(&provider->lock);
+
mutex_lock(&icc_lock);
INIT_LIST_HEAD(&provider->nodes);
diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h
index 5b64e267bfb3..cb2bed5c78f9 100644
--- a/include/linux/interconnect-provider.h
+++ b/include/linux/interconnect-provider.h
@@ -34,6 +34,7 @@ struct icc_node *of_icc_xlate_onecell(struct of_phandle_args *spec,
*
* @provider_list: list of the registered interconnect providers
* @nodes: internal list of the interconnect provider nodes
+ * @lock: interconnect provider specific lock
* @set: pointer to device specific set operation function
* @aggregate: pointer to device specific aggregate operation function
* @pre_aggregate: pointer to device specific function that is called
@@ -46,6 +47,7 @@ struct icc_node *of_icc_xlate_onecell(struct of_phandle_args *spec,
struct icc_provider {
struct list_head provider_list;
struct list_head nodes;
+ struct mutex lock;
int (*set)(struct icc_node *src, struct icc_node *dst);
int (*aggregate)(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak);