Skip to content

Commit

Permalink
interconnect: Add sync state support
Browse files Browse the repository at this point in the history
The bootloaders often do some initial configuration of the interconnects
in the system and we want to keep this configuration until all consumers
have probed and expressed their bandwidth needs. This is because we don't
want to change the configuration by starting to disable unused paths until
every user had a chance to request the amount of bandwidth it needs.

To accomplish this we will implement an interconnect specific sync_state
callback which will synchronize (aggregate and set) the current bandwidth
settings when all consumers have been probed.

Link: https://lore.kernel.org/r/[email protected]
Reviewed-by: Saravana Kannan <[email protected]>
Signed-off-by: Georgi Djakov <[email protected]>
  • Loading branch information
Georgi Djakov committed Sep 18, 2020
1 parent cc80d10 commit b1d681d
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
67 changes: 67 additions & 0 deletions drivers/interconnect/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

static DEFINE_IDR(icc_idr);
static LIST_HEAD(icc_providers);
static int providers_count;
static bool synced_state;
static DEFINE_MUTEX(icc_lock);
static struct dentry *icc_debugfs_dir;

Expand Down Expand Up @@ -267,6 +269,12 @@ static int aggregate_requests(struct icc_node *node)
}
p->aggregate(node, r->tag, avg_bw, peak_bw,
&node->avg_bw, &node->peak_bw);

/* during boot use the initial bandwidth as a floor value */
if (!synced_state) {
node->avg_bw = max(node->avg_bw, node->init_avg);
node->peak_bw = max(node->peak_bw, node->init_peak);
}
}

return 0;
Expand Down Expand Up @@ -931,6 +939,19 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider)
node->provider = provider;
list_add_tail(&node->node_list, &provider->nodes);

/* get the initial bandwidth values and sync them with hardware */
if (provider->get_bw) {
provider->get_bw(node, &node->init_avg, &node->init_peak);
} else {
node->init_avg = INT_MAX;
node->init_peak = INT_MAX;
}
node->avg_bw = node->init_avg;
node->peak_bw = node->init_peak;
provider->set(node, node);
node->avg_bw = 0;
node->peak_bw = 0;

mutex_unlock(&icc_lock);
}
EXPORT_SYMBOL_GPL(icc_node_add);
Expand Down Expand Up @@ -1026,8 +1047,54 @@ int icc_provider_del(struct icc_provider *provider)
}
EXPORT_SYMBOL_GPL(icc_provider_del);

static int of_count_icc_providers(struct device_node *np)
{
struct device_node *child;
int count = 0;

for_each_available_child_of_node(np, child) {
if (of_property_read_bool(child, "#interconnect-cells"))
count++;
count += of_count_icc_providers(child);
}
of_node_put(np);

return count;
}

void icc_sync_state(struct device *dev)
{
struct icc_provider *p;
struct icc_node *n;
static int count;

count++;

if (count < providers_count)
return;

mutex_lock(&icc_lock);
synced_state = true;
list_for_each_entry(p, &icc_providers, provider_list) {
dev_dbg(p->dev, "interconnect provider is in synced state\n");
list_for_each_entry(n, &p->nodes, node_list) {
if (n->init_avg || n->init_peak) {
aggregate_requests(n);
p->set(n, n);
}
}
}
mutex_unlock(&icc_lock);
}
EXPORT_SYMBOL_GPL(icc_sync_state);

static int __init icc_init(void)
{
struct device_node *root = of_find_node_by_path("/");

providers_count = of_count_icc_providers(root);
of_node_put(root);

icc_debugfs_dir = debugfs_create_dir("interconnect", NULL);
debugfs_create_file("interconnect_summary", 0444,
icc_debugfs_dir, NULL, &icc_summary_fops);
Expand Down
5 changes: 5 additions & 0 deletions include/linux/interconnect-provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ struct icc_provider {
* @req_list: a list of QoS constraint requests associated with this node
* @avg_bw: aggregated value of average bandwidth requests from all consumers
* @peak_bw: aggregated value of peak bandwidth requests from all consumers
* @init_avg: average bandwidth value that is read from the hardware during init
* @init_peak: peak bandwidth value that is read from the hardware during init
* @data: pointer to private data
*/
struct icc_node {
Expand All @@ -91,6 +93,8 @@ struct icc_node {
struct hlist_head req_list;
u32 avg_bw;
u32 peak_bw;
u32 init_avg;
u32 init_peak;
void *data;
};

Expand All @@ -108,6 +112,7 @@ int icc_nodes_remove(struct icc_provider *provider);
int icc_provider_add(struct icc_provider *provider);
int icc_provider_del(struct icc_provider *provider);
struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec);
void icc_sync_state(struct device *dev);

#else

Expand Down

0 comments on commit b1d681d

Please sign in to comment.