diff options
Diffstat (limited to 'drivers/acpi/cppc_acpi.c')
| -rw-r--r-- | drivers/acpi/cppc_acpi.c | 153 |
1 files changed, 101 insertions, 52 deletions
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 0f0cce383007..2ad45725f3e0 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -36,6 +36,7 @@ #include <linux/delay.h> #include <linux/iopoll.h> #include <linux/ktime.h> +#include <linux/mutex.h> #include <linux/rwsem.h> #include <linux/wait.h> #include <linux/topology.h> @@ -81,6 +82,7 @@ struct cppc_pcc_data { /* Array to represent the PCC channel per subspace ID */ static struct cppc_pcc_data *pcc_data[MAX_PCC_SUBSPACES]; +static DEFINE_MUTEX(pcc_data_lock); /* The cpu_pcc_subspace_idx contains per CPU subspace ID */ static DEFINE_PER_CPU(int, cpu_pcc_subspace_idx); @@ -760,35 +762,51 @@ EXPORT_SYMBOL_GPL(acpi_get_psd_map); static int register_pcc_channel(int pcc_ss_idx) { + struct cppc_pcc_data *data; struct pcc_mbox_chan *pcc_chan; u64 usecs_lat; + int ret = 0; - if (pcc_ss_idx >= 0) { - pcc_chan = pcc_mbox_request_channel(&cppc_mbox_cl, pcc_ss_idx); - - if (IS_ERR(pcc_chan)) { - pr_err("Failed to find PCC channel for subspace %d\n", - pcc_ss_idx); - return -ENODEV; - } + if (pcc_ss_idx < 0 || pcc_ss_idx >= MAX_PCC_SUBSPACES) + return -EINVAL; - pcc_data[pcc_ss_idx]->pcc_channel = pcc_chan; - /* - * cppc_ss->latency is just a Nominal value. In reality - * the remote processor could be much slower to reply. - * So add an arbitrary amount of wait on top of Nominal. - */ - usecs_lat = NUM_RETRIES * pcc_chan->latency; - pcc_data[pcc_ss_idx]->deadline_us = usecs_lat; - pcc_data[pcc_ss_idx]->pcc_mrtt = pcc_chan->min_turnaround_time; - pcc_data[pcc_ss_idx]->pcc_mpar = pcc_chan->max_access_rate; - pcc_data[pcc_ss_idx]->pcc_nominal = pcc_chan->latency; + mutex_lock(&pcc_data_lock); + data = pcc_data[pcc_ss_idx]; + if (!data) { + ret = -ENODEV; + goto out_unlock; + } + if (data->pcc_channel_acquired) + goto out_unlock; - /* Set flag so that we don't come here for each CPU. */ - pcc_data[pcc_ss_idx]->pcc_channel_acquired = true; + pcc_chan = pcc_mbox_request_channel(&cppc_mbox_cl, pcc_ss_idx); + if (IS_ERR(pcc_chan)) { + pr_err("Failed to find PCC channel for subspace %d\n", + pcc_ss_idx); + ret = -ENODEV; + goto out_unlock; } - return 0; + data->pcc_channel = pcc_chan; + /* + * cppc_ss->latency is just a Nominal value. In reality + * the remote processor could be much slower to reply. + * So add an arbitrary amount of wait on top of Nominal. + */ + usecs_lat = NUM_RETRIES * pcc_chan->latency; + data->deadline_us = usecs_lat; + data->pcc_mrtt = pcc_chan->min_turnaround_time; + data->pcc_mpar = pcc_chan->max_access_rate; + data->pcc_nominal = pcc_chan->latency; + init_rwsem(&data->pcc_lock); + init_waitqueue_head(&data->pcc_write_wait_q); + + /* Reuse this channel when another CPU references the same subspace. */ + data->pcc_channel_acquired = true; + +out_unlock: + mutex_unlock(&pcc_data_lock); + return ret; } /** @@ -830,19 +848,49 @@ bool __weak cpc_supported_by_cpu(void) */ static int pcc_data_alloc(int pcc_ss_id) { + struct cppc_pcc_data *data; + int ret = 0; + if (pcc_ss_id < 0 || pcc_ss_id >= MAX_PCC_SUBSPACES) return -EINVAL; - if (pcc_data[pcc_ss_id]) { - pcc_data[pcc_ss_id]->refcount++; - } else { - pcc_data[pcc_ss_id] = kzalloc_obj(struct cppc_pcc_data); - if (!pcc_data[pcc_ss_id]) - return -ENOMEM; - pcc_data[pcc_ss_id]->refcount++; + mutex_lock(&pcc_data_lock); + data = pcc_data[pcc_ss_id]; + if (!data) { + data = kzalloc_obj(struct cppc_pcc_data); + if (!data) { + ret = -ENOMEM; + goto out_unlock; + } + pcc_data[pcc_ss_id] = data; } + data->refcount++; - return 0; +out_unlock: + mutex_unlock(&pcc_data_lock); + return ret; +} + +static void pcc_data_put(int pcc_ss_id) +{ + struct cppc_pcc_data *data; + + if (pcc_ss_id < 0 || pcc_ss_id >= MAX_PCC_SUBSPACES) + return; + + mutex_lock(&pcc_data_lock); + data = pcc_data[pcc_ss_id]; + if (!data || --data->refcount) + goto out_unlock; + + pcc_data[pcc_ss_id] = NULL; + if (data->pcc_channel_acquired) + pcc_mbox_free_channel(data->pcc_channel); + + kfree(data); + +out_unlock: + mutex_unlock(&pcc_data_lock); } /* @@ -890,11 +938,17 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) acpi_handle handle = pr->handle; unsigned int num_ent, i, cpc_rev; int pcc_subspace_id = -1; + bool pcc_data_ref = false; bool cpc_present = false; acpi_status status; int ret = -ENODATA; int err; + if (per_cpu(cpc_desc_ptr, pr->id)) + return 0; + + per_cpu(cpu_pcc_subspace_idx, pr->id) = -1; + if (!osc_sb_cppc2_support_acked) { pr_debug("CPPC v2 _OSC not acked\n"); if (!cpc_supported_by_cpu()) { @@ -1039,6 +1093,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) ret = err; goto out_free; } + pcc_data_ref = true; } else if (pcc_subspace_id != gas_t->access_width) { pr_debug("Mismatched PCC ids in _CPC for CPU:%d\n", pr->id); @@ -1171,13 +1226,10 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) goto out_free; /* Register PCC channel once for all PCC subspace ID. */ - if (pcc_subspace_id >= 0 && !pcc_data[pcc_subspace_id]->pcc_channel_acquired) { + if (pcc_subspace_id >= 0) { ret = register_pcc_channel(pcc_subspace_id); if (ret) goto out_free; - - init_rwsem(&pcc_data[pcc_subspace_id]->pcc_lock); - init_waitqueue_head(&pcc_data[pcc_subspace_id]->pcc_write_wait_q); } /* Everything looks okay */ @@ -1198,7 +1250,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) if (ret) { per_cpu(cpc_desc_ptr, pr->id) = NULL; kobject_put(&cpc_ptr->kobj); - goto out_buf_free; + goto out_pcc_put; } kfree(output.pointer); @@ -1207,6 +1259,11 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) out_free: cppc_free_desc(cpc_ptr); +out_pcc_put: + if (pcc_data_ref) + pcc_data_put(pcc_subspace_id); + per_cpu(cpu_pcc_subspace_idx, pr->id) = -1; + out_buf_free: if (cpc_present) pr_err("CPU%d: failed to initialize _CPC: %d\n", pr->id, ret); @@ -1224,28 +1281,20 @@ EXPORT_SYMBOL_GPL(acpi_cppc_processor_probe); void acpi_cppc_processor_exit(struct acpi_processor *pr) { struct cpc_desc *cpc_ptr; - int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, pr->id); + int pcc_ss_id; cpc_ptr = per_cpu(cpc_desc_ptr, pr->id); - if (cpc_ptr) { - per_cpu(cpc_desc_ptr, pr->id) = NULL; - kobject_del(&cpc_ptr->kobj); + if (!cpc_ptr) { + per_cpu(cpu_pcc_subspace_idx, pr->id) = -1; + return; } - if (pcc_ss_id >= 0 && pcc_data[pcc_ss_id]) { - if (pcc_data[pcc_ss_id]->pcc_channel_acquired) { - pcc_data[pcc_ss_id]->refcount--; - if (!pcc_data[pcc_ss_id]->refcount) { - pcc_mbox_free_channel(pcc_data[pcc_ss_id]->pcc_channel); - kfree(pcc_data[pcc_ss_id]); - pcc_data[pcc_ss_id] = NULL; - } - } - } - per_cpu(cpu_pcc_subspace_idx, pr->id) = -1; + pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, pr->id); + per_cpu(cpc_desc_ptr, pr->id) = NULL; + kobject_del(&cpc_ptr->kobj); - if (!cpc_ptr) - return; + pcc_data_put(pcc_ss_id); + per_cpu(cpu_pcc_subspace_idx, pr->id) = -1; kobject_put(&cpc_ptr->kobj); } |
