Merge "defconfig: arm64: Enable hyp core control driver for Lahaiana"

This commit is contained in:
qctecmdr 2020-03-14 18:51:02 -07:00 • committed by Gerrit - the friendly Code Review server
commit 4d500eb9cd
11 changed files with 997 additions and 0 deletions

View file

@ -16,6 +16,7 @@ CONFIG_IOMMU_IO_PGTABLE_FAST=y
# CONFIG_IOMMU_IO_PGTABLE_FAST_PROVE_TLB is not set
CONFIG_QCOM_LLCC_PERFMON=m
CONFIG_SCHED_WALT=y
CONFIG_QCOM_HYP_CORE_CTL=y
CONFIG_QGKI=y
CONFIG_REGULATOR_QTI_DEBUG=y
CONFIG_REGMAP_QTI_DEBUG=y

View file

@ -773,4 +773,13 @@ config QCOM_GUESTVM
Resource Manager driver to start the boot of VMs once it has
successfully loaded the VM images in the designated memory.
config QCOM_HYP_CORE_CTL
bool "CPU reservation scheme for Hypervisor"
depends on QCOM_GUESTVM
help
This driver reserve the specified CPUS by isolating them. The reserved
CPUs can be assigned to the other guest OS by the hypervisor.
An offline CPU is considered as a reserved CPU since this OS can't use
it.
endmenu

View file

@ -72,4 +72,5 @@ obj-$(CONFIG_MSM_SPCOM) += spcom.o
obj-$(CONFIG_QCOM_FSA4480_I2C) += fsa4480-i2c.o
obj-$(CONFIG_QCOM_EUD) += eud.o
obj-$(CONFIG_QCOM_GUESTVM) += guestvm_loader.o
obj-$(CONFIG_QCOM_HYP_CORE_CTL) += hyp_core_ctl.o
obj-$(CONFIG_MSM_QBT_HANDLER) += qbt_handler.o

View file

@ -0,0 +1,864 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "hyp_core_ctl: " fmt
#include <linux/init.h>
#include <linux/cpumask.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/sched/rt.h>
#include <linux/slab.h>
#include <linux/cpuhotplug.h>
#include <uapi/linux/sched/types.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/cpu_cooling.h>
#include <linux/mutex.h>
#include <linux/debugfs.h>
#include <linux/haven/hcall.h>
#include <linux/haven/hh_errno.h>
#define MAX_RESERVE_CPUS (num_possible_cpus()/2)
/**
* struct hyp_core_ctl_cpumap - vcpu to pcpu mapping for the other guest
* @sid: System call id to be used while referring to this vcpu
* @pcpu: The physical CPU number corresponding to this vcpu
* @curr_pcpu: The current physical CPU number corresponding to this vcpu.
* The curr_pcu is set to another CPU when the original assigned
* CPU i.e pcpu can't be used due to thermal condition.
*
*/
struct hyp_core_ctl_cpu_map {
hh_capid_t sid;
hh_label_t pcpu;
hh_label_t curr_pcpu;
};
/**
* struct hyp_core_ctl_data - The private data structure of this driver
* @lock: spinlock to serialize task wakeup and enable/reserve_cpus
* @task: task_struct pointer to the thread running the state machine
* @pending: state machine work pending status
* @reservation_enabled: status of the reservation
* @reservation_mutex: synchronization between thermal handling and
* reservation. The physical CPUs are re-assigned
* during thermal conditions while reservation is
* not enabled. So this synchronization is needed.
* @reserve_cpus: The CPUs to be reserved. input.
* @our_isolated_cpus: The CPUs isolated by hyp_core_ctl driver. output.
* @final_reserved_cpus: The CPUs reserved for the Hypervisor. output.
* @cpumap: The vcpu to pcpu mapping table
*/
struct hyp_core_ctl_data {
spinlock_t lock;
struct task_struct *task;
bool pending;
bool reservation_enabled;
struct mutex reservation_mutex;
cpumask_t reserve_cpus;
cpumask_t our_isolated_cpus;
cpumask_t final_reserved_cpus;
struct hyp_core_ctl_cpu_map cpumap[NR_CPUS];
};
#define CREATE_TRACE_POINTS
#include <trace/events/hyp_core_ctl.h>
static struct hyp_core_ctl_data *the_hcd;
static struct hyp_core_ctl_cpu_map hh_cpumap[NR_CPUS];
static bool populated_vcpu_info;
static bool init_done;
static int nr_vcpus;
static inline void hyp_core_ctl_print_status(char *msg)
{
trace_hyp_core_ctl_status(the_hcd, msg);
pr_debug("%s: reserve=%*pbl reserved=%*pbl our_isolated=%*pbl online=%*pbl isolated=%*pbl thermal=%*pbl\n",
msg, cpumask_pr_args(&the_hcd->reserve_cpus),
cpumask_pr_args(&the_hcd->final_reserved_cpus),
cpumask_pr_args(&the_hcd->our_isolated_cpus),
cpumask_pr_args(cpu_online_mask),
cpumask_pr_args(cpu_isolated_mask),
cpumask_pr_args(cpu_cooling_get_max_level_cpumask()));
}
static void hyp_core_ctl_undo_reservation(struct hyp_core_ctl_data *hcd)
{
int cpu, ret;
hyp_core_ctl_print_status("undo_reservation_start");
for_each_cpu(cpu, &hcd->our_isolated_cpus) {
ret = sched_unisolate_cpu(cpu);
if (ret < 0) {
pr_err("fail to un-isolate CPU%d. ret=%d\n", cpu, ret);
continue;
}
cpumask_clear_cpu(cpu, &hcd->our_isolated_cpus);
}
hyp_core_ctl_print_status("undo_reservation_end");
}
static void finalize_reservation(struct hyp_core_ctl_data *hcd, cpumask_t *temp)
{
cpumask_t vcpu_adjust_mask;
int i, orig_cpu, curr_cpu, replacement_cpu;
int err;
/*
* When thermal conditions are not present, we return
* from here.
*/
if (cpumask_equal(temp, &hcd->final_reserved_cpus))
return;
/*
* When we can't match with the original reserve CPUs request,
* don't change the existing scheme. We can't assign the
* same physical CPU to multiple virtual CPUs.
*
* This may only happen when thermal isolate more CPUs.
*/
if (cpumask_weight(temp) < cpumask_weight(&hcd->reserve_cpus)) {
pr_debug("Fail to reserve some CPUs\n");
return;
}
cpumask_copy(&hcd->final_reserved_cpus, temp);
cpumask_clear(&vcpu_adjust_mask);
/*
* In the first pass, we traverse all virtual CPUs and try
* to assign their original physical CPUs if they are
* reserved. if the original physical CPU is not reserved,
* then check the current physical CPU is reserved or not.
* so that we continue to use the current physical CPU.
*
* If both original CPU and the current CPU are not reserved,
* we have to find a replacement. These virtual CPUs are
* maintained in vcpu_adjust_mask and processed in the 2nd pass.
*/
for (i = 0; i < MAX_RESERVE_CPUS; i++) {
if (hcd->cpumap[i].sid == 0)
break;
orig_cpu = hcd->cpumap[i].pcpu;
curr_cpu = hcd->cpumap[i].curr_pcpu;
if (cpumask_test_cpu(orig_cpu, &hcd->final_reserved_cpus)) {
cpumask_clear_cpu(orig_cpu, temp);
if (orig_cpu == curr_cpu)
continue;
/*
* The original pcpu corresponding to this vcpu i.e i
* is available in final_reserved_cpus. so restore
* the assignment.
*/
err = hh_hcall_vcpu_affinity_set(hcd->cpumap[i].sid,
orig_cpu);
if (err != HH_ERROR_OK) {
pr_err("fail to assign pcpu for vcpu#%d\n", i);
continue;
}
hcd->cpumap[i].curr_pcpu = orig_cpu;
pr_debug("err=%u vcpu=%d pcpu=%u curr_cpu=%u\n",
err, i, hcd->cpumap[i].pcpu,
hcd->cpumap[i].curr_pcpu);
continue;
}
/*
* The original CPU is not available but the previously
* assigned CPU i.e curr_cpu is still available. so keep
* using it.
*/
if (cpumask_test_cpu(curr_cpu, &hcd->final_reserved_cpus)) {
cpumask_clear_cpu(curr_cpu, temp);
continue;
}
/*
* A replacement CPU is found in the 2nd pass below. Make
* a note of this virtual CPU for which both original and
* current physical CPUs are not available in the
* final_reserved_cpus.
*/
cpumask_set_cpu(i, &vcpu_adjust_mask);
}
/*
* The vcpu_adjust_mask contain the virtual CPUs that needs
* re-assignment. The temp CPU mask contains the remaining
* reserved CPUs. so we pick one by one from the remaining
* reserved CPUs and assign them to the pending virtual
* CPUs.
*/
for_each_cpu(i, &vcpu_adjust_mask) {
replacement_cpu = cpumask_any(temp);
cpumask_clear_cpu(replacement_cpu, temp);
err = hh_hcall_vcpu_affinity_set(hcd->cpumap[i].sid,
replacement_cpu);
if (err != HH_ERROR_OK) {
pr_err("fail to assign pcpu for vcpu#%d\n", i);
continue;
}
hcd->cpumap[i].curr_pcpu = replacement_cpu;
pr_debug("adjust err=%u vcpu=%d pcpu=%u curr_cpu=%u\n",
err, i, hcd->cpumap[i].pcpu,
hcd->cpumap[i].curr_pcpu);
}
/* Did we reserve more CPUs than needed? */
WARN_ON(!cpumask_empty(temp));
}
static void hyp_core_ctl_do_reservation(struct hyp_core_ctl_data *hcd)
{
cpumask_t offline_cpus, iter_cpus, temp_reserved_cpus;
int i, ret, iso_required, iso_done;
const cpumask_t *thermal_cpus = cpu_cooling_get_max_level_cpumask();
cpumask_clear(&offline_cpus);
cpumask_clear(&temp_reserved_cpus);
hyp_core_ctl_print_status("reservation_start");
/*
* Iterate all reserve CPUs and isolate them if not done already.
* The offline CPUs can't be isolated but they are considered
* reserved. When an offline and reserved CPU comes online, it
* will be isolated to honor the reservation.
*/
cpumask_andnot(&iter_cpus, &hcd->reserve_cpus, &hcd->our_isolated_cpus);
cpumask_andnot(&iter_cpus, &iter_cpus, thermal_cpus);
for_each_cpu(i, &iter_cpus) {
if (!cpu_online(i)) {
cpumask_set_cpu(i, &offline_cpus);
continue;
}
ret = sched_isolate_cpu(i);
if (ret < 0) {
pr_debug("fail to isolate CPU%d. ret=%d\n", i, ret);
continue;
}
cpumask_set_cpu(i, &hcd->our_isolated_cpus);
}
cpumask_andnot(&iter_cpus, &hcd->reserve_cpus, &offline_cpus);
iso_required = cpumask_weight(&iter_cpus);
iso_done = cpumask_weight(&hcd->our_isolated_cpus);
if (iso_done < iso_required) {
int isolate_need;
/*
* We have isolated fewer CPUs than required. This happens
* when some of the CPUs from the reserved_cpus mask
* are managed by thermal. Find the replacement CPUs and
* isolate them.
*/
isolate_need = iso_required - iso_done;
/*
* Create a cpumask from which replacement CPUs can be
* picked. Exclude our isolated CPUs, thermal managed
* CPUs and offline CPUs, which are already considered
* as reserved.
*/
cpumask_andnot(&iter_cpus, cpu_possible_mask,
&hcd->our_isolated_cpus);
cpumask_andnot(&iter_cpus, &iter_cpus, thermal_cpus);
cpumask_andnot(&iter_cpus, &iter_cpus, &offline_cpus);
/*
* Keep the replacement policy simple. The offline CPUs
* comes for free. so pick them first.
*/
for_each_cpu(i, &iter_cpus) {
if (!cpu_online(i)) {
cpumask_set_cpu(i, &offline_cpus);
if (--isolate_need == 0)
goto done;
}
}
cpumask_andnot(&iter_cpus, &iter_cpus, &offline_cpus);
for_each_cpu(i, &iter_cpus) {
ret = sched_isolate_cpu(i);
if (ret < 0) {
pr_debug("fail to isolate CPU%d. ret=%d\n",
i, ret);
continue;
}
cpumask_set_cpu(i, &hcd->our_isolated_cpus);
if (--isolate_need == 0)
break;
}
} else if (iso_done > iso_required) {
int unisolate_need;
/*
* We have isolated more CPUs than required. Un-isolate
* the additional CPUs which are not part of the
* reserve_cpus mask.
*
* This happens in the following scenario.
*
* - Lets say reserve CPUs are CPU4 and CPU5. They are
* isolated.
* - CPU4 is isolated by thermal. We found CPU0 as the
* replacement CPU. Now CPU0 and CPU5 are isolated by
* us.
* - CPU4 is un-isolated by thermal. We first isolate CPU4
* since it is part of our reserve CPUs. Now CPU0, CPU4
* and CPU5 are isolated by us.
* - Since iso_done (3) > iso_required (2), un-isolate
* a CPU which is not part of the reserve CPU. i.e CPU0.
*/
unisolate_need = iso_done - iso_required;
cpumask_andnot(&iter_cpus, &hcd->our_isolated_cpus,
&hcd->reserve_cpus);
for_each_cpu(i, &iter_cpus) {
ret = sched_unisolate_cpu(i);
if (ret < 0) {
pr_err("fail to unisolate CPU%d. ret=%d\n",
i, ret);
continue;
}
cpumask_clear_cpu(i, &hcd->our_isolated_cpus);
if (--unisolate_need == 0)
break;
}
}
done:
cpumask_or(&temp_reserved_cpus, &hcd->our_isolated_cpus, &offline_cpus);
finalize_reservation(hcd, &temp_reserved_cpus);
hyp_core_ctl_print_status("reservation_end");
}
static int hyp_core_ctl_thread(void *data)
{
struct hyp_core_ctl_data *hcd = data;
while (1) {
spin_lock(&hcd->lock);
if (!hcd->pending) {
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock(&hcd->lock);
schedule();
spin_lock(&hcd->lock);
set_current_state(TASK_RUNNING);
}
hcd->pending = false;
spin_unlock(&hcd->lock);
if (kthread_should_stop())
break;
/*
* The reservation mutex synchronize the reservation
* happens in this thread against the thermal handling.
* The CPU re-assignment happens directly from the
* thermal callback context when the reservation is
* not enabled, since there is no need for isolating.
*/
mutex_lock(&hcd->reservation_mutex);
if (hcd->reservation_enabled)
hyp_core_ctl_do_reservation(hcd);
else
hyp_core_ctl_undo_reservation(hcd);
mutex_unlock(&hcd->reservation_mutex);
}
return 0;
}
static void hyp_core_ctl_handle_thermal(struct hyp_core_ctl_data *hcd,
int cpu, bool throttled)
{
cpumask_t temp_mask, iter_cpus;
const cpumask_t *thermal_cpus = cpu_cooling_get_max_level_cpumask();
bool notify = false;
int replacement_cpu;
hyp_core_ctl_print_status("handle_thermal_start");
/*
* Take a copy of the final_reserved_cpus and adjust the mask
* based on the notified CPU's thermal state.
*/
cpumask_copy(&temp_mask, &hcd->final_reserved_cpus);
if (throttled) {
/*
* Find a replacement CPU for this throttled CPU. Select
* any CPU that is not managed by thermal and not already
* part of the assigned CPUs.
*/
cpumask_andnot(&iter_cpus, cpu_possible_mask, thermal_cpus);
cpumask_andnot(&iter_cpus, &iter_cpus,
&hcd->final_reserved_cpus);
replacement_cpu = cpumask_any(&iter_cpus);
if (replacement_cpu < nr_cpu_ids) {
cpumask_clear_cpu(cpu, &temp_mask);
cpumask_set_cpu(replacement_cpu, &temp_mask);
notify = true;
}
} else {
/*
* One of the original assigned CPU is unthrottled by thermal.
* Swap this CPU with any one of the replacement CPUs.
*/
cpumask_andnot(&iter_cpus, &hcd->final_reserved_cpus,
&hcd->reserve_cpus);
replacement_cpu = cpumask_any(&iter_cpus);
if (replacement_cpu < nr_cpu_ids) {
cpumask_clear_cpu(replacement_cpu, &temp_mask);
cpumask_set_cpu(cpu, &temp_mask);
notify = true;
}
}
if (notify)
finalize_reservation(hcd, &temp_mask);
hyp_core_ctl_print_status("handle_thermal_end");
}
static int hyp_core_ctl_cpu_cooling_cb(struct notifier_block *nb,
unsigned long val, void *data)
{
int cpu = (long) data;
const cpumask_t *thermal_cpus = cpu_cooling_get_max_level_cpumask();
if (!the_hcd)
return NOTIFY_DONE;
mutex_lock(&the_hcd->reservation_mutex);
pr_debug("CPU%d is %s by thermal\n", cpu,
val ? "throttled" : "unthrottled");
if (val) {
/*
* The thermal mitigated CPU is not part of our reserved
* CPUs. So nothing to do.
*/
if (!cpumask_test_cpu(cpu, &the_hcd->final_reserved_cpus))
goto out;
/*
* The thermal mitigated CPU is part of our reserved CPUs.
*
* If it is isolated by us, unisolate it. If it is not
* isolated, probably it is offline. In both cases, kick
* the state machine to find a replacement CPU.
*/
if (cpumask_test_cpu(cpu, &the_hcd->our_isolated_cpus)) {
sched_unisolate_cpu(cpu);
cpumask_clear_cpu(cpu, &the_hcd->our_isolated_cpus);
}
} else {
/*
* A CPU is unblocked by thermal. We are interested if
*
* (1) This CPU is part of the original reservation request
* In this case, this CPU should be swapped with one of
* the replacement CPU that is currently reserved.
* (2) When some of the thermal mitigated CPUs are currently
* reserved due to unavailability of CPUs. Now that
* thermal unblocked a CPU, swap this with one of the
* thermal mitigated CPU that is currently reserved.
*/
if (!cpumask_test_cpu(cpu, &the_hcd->reserve_cpus) &&
!cpumask_intersects(&the_hcd->final_reserved_cpus,
thermal_cpus))
goto out;
}
if (the_hcd->reservation_enabled) {
spin_lock(&the_hcd->lock);
the_hcd->pending = true;
wake_up_process(the_hcd->task);
spin_unlock(&the_hcd->lock);
} else {
/*
* When the reservation is enabled, the state machine
* takes care of finding the new replacement CPU or
* isolating the unthrottled CPU. However when the
* reservation is not enabled, we still want to
* re-assign another CPU for a throttled CPU.
*/
hyp_core_ctl_handle_thermal(the_hcd, cpu, val);
}
out:
mutex_unlock(&the_hcd->reservation_mutex);
return NOTIFY_OK;
}
static struct notifier_block hyp_core_ctl_nb = {
.notifier_call = hyp_core_ctl_cpu_cooling_cb,
};
static int hyp_core_ctl_hp_offline(unsigned int cpu)
{
if (!the_hcd || !the_hcd->reservation_enabled)
return 0;
/*
* A CPU can't be left in isolated state while it is
* going offline. So unisolate the CPU if it is
* isolated by us. An offline CPU is considered
* as reserved. So no further action is needed.
*/
if (cpumask_test_and_clear_cpu(cpu, &the_hcd->our_isolated_cpus))
sched_unisolate_cpu_unlocked(cpu);
return 0;
}
static int hyp_core_ctl_hp_online(unsigned int cpu)
{
if (!the_hcd || !the_hcd->reservation_enabled)
return 0;
/*
* A reserved CPU is coming online. It should be isolated
* to honor the reservation. So kick the state machine.
*/
spin_lock(&the_hcd->lock);
if (cpumask_test_cpu(cpu, &the_hcd->final_reserved_cpus)) {
the_hcd->pending = true;
wake_up_process(the_hcd->task);
}
spin_unlock(&the_hcd->lock);
return 0;
}
static int hyp_core_ctl_init_reserve_cpus(struct hyp_core_ctl_data *hcd)
{
int i, ret = 0;
spin_lock(&hcd->lock);
cpumask_clear(&hcd->reserve_cpus);
for (i = 0; i < MAX_RESERVE_CPUS; i++) {
if (hh_cpumap[i].sid == 0)
break;
hcd->cpumap[i].sid = hh_cpumap[i].sid;
hcd->cpumap[i].pcpu = hh_cpumap[i].pcpu;
hcd->cpumap[i].curr_pcpu = hh_cpumap[i].curr_pcpu;
cpumask_set_cpu(hcd->cpumap[i].pcpu, &hcd->reserve_cpus);
pr_debug("vcpu%u map to pcpu%u\n", i, hcd->cpumap[i].pcpu);
}
cpumask_copy(&hcd->final_reserved_cpus, &hcd->reserve_cpus);
spin_unlock(&hcd->lock);
pr_info("reserve_cpus=%*pbl ret=%d\n",
cpumask_pr_args(&hcd->reserve_cpus), ret);
return ret;
}
int hh_vcpu_populate_affinity_info(u32 cpu_idx, u64 cap_id)
{
static struct hyp_core_ctl_cpu_map hh_cpumap[NR_CPUS];
hh_cpumap[nr_vcpus].sid = cap_id;
hh_cpumap[nr_vcpus].pcpu = cpu_idx;
hh_cpumap[nr_vcpus].curr_pcpu = cpu_idx;
if (!populated_vcpu_info)
populated_vcpu_info = true;
if (init_done)
hyp_core_ctl_init_reserve_cpus(the_hcd);
nr_vcpus++;
pr_debug("cpu_index:%u vcpu_cap_id:%llu nr_vcpus:%d\n",
cpu_idx, cap_id, nr_vcpus);
return 0;
}
static void hyp_core_ctl_enable(bool enable)
{
spin_lock(&the_hcd->lock);
if (enable == the_hcd->reservation_enabled)
goto out;
trace_hyp_core_ctl_enable(enable);
pr_debug("reservation %s\n", enable ? "enabled" : "disabled");
the_hcd->reservation_enabled = enable;
the_hcd->pending = true;
wake_up_process(the_hcd->task);
out:
spin_unlock(&the_hcd->lock);
}
static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
bool enable;
int ret;
ret = kstrtobool(buf, &enable);
if (ret < 0)
return -EINVAL;
hyp_core_ctl_enable(enable);
return count;
}
static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%u\n", the_hcd->reservation_enabled);
}
static DEVICE_ATTR_RW(enable);
static ssize_t status_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct hyp_core_ctl_data *hcd = the_hcd;
ssize_t count;
int i;
mutex_lock(&hcd->reservation_mutex);
count = scnprintf(buf, PAGE_SIZE, "enabled=%d\n",
hcd->reservation_enabled);
count += scnprintf(buf + count, PAGE_SIZE - count,
"reserve_cpus=%*pbl\n",
cpumask_pr_args(&hcd->reserve_cpus));
count += scnprintf(buf + count, PAGE_SIZE - count,
"reserved_cpus=%*pbl\n",
cpumask_pr_args(&hcd->final_reserved_cpus));
count += scnprintf(buf + count, PAGE_SIZE - count,
"our_isolated_cpus=%*pbl\n",
cpumask_pr_args(&hcd->our_isolated_cpus));
count += scnprintf(buf + count, PAGE_SIZE - count,
"online_cpus=%*pbl\n",
cpumask_pr_args(cpu_online_mask));
count += scnprintf(buf + count, PAGE_SIZE - count,
"isolated_cpus=%*pbl\n",
cpumask_pr_args(cpu_isolated_mask));
count += scnprintf(buf + count, PAGE_SIZE - count,
"thermal_cpus=%*pbl\n",
cpumask_pr_args(cpu_cooling_get_max_level_cpumask()));
count += scnprintf(buf + count, PAGE_SIZE - count,
"Vcpu to Pcpu mappings:\n");
for (i = 0; i < MAX_RESERVE_CPUS; i++) {
if (hcd->cpumap[i].sid == 0)
break;
count += scnprintf(buf + count, PAGE_SIZE - count,
"vcpu=%d pcpu=%u curr_pcpu=%u\n",
i, hcd->cpumap[i].pcpu, hcd->cpumap[i].curr_pcpu);
}
mutex_unlock(&hcd->reservation_mutex);
return count;
}
static DEVICE_ATTR_RO(status);
static struct attribute *hyp_core_ctl_attrs[] = {
&dev_attr_enable.attr,
&dev_attr_status.attr,
NULL
};
static struct attribute_group hyp_core_ctl_attr_group = {
.attrs = hyp_core_ctl_attrs,
.name = "hyp_core_ctl",
};
#define CPULIST_SZ 32
static ssize_t read_reserve_cpus(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
char kbuf[CPULIST_SZ];
int ret;
ret = scnprintf(kbuf, CPULIST_SZ, "%*pbl\n",
cpumask_pr_args(&the_hcd->reserve_cpus));
return simple_read_from_buffer(ubuf, count, ppos, kbuf, ret);
}
static ssize_t write_reserve_cpus(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
char kbuf[CPULIST_SZ];
int ret;
cpumask_t temp_mask;
ret = simple_write_to_buffer(kbuf, CPULIST_SZ - 1, ppos, ubuf, count);
if (ret < 0)
return ret;
kbuf[ret] = '\0';
ret = cpulist_parse(kbuf, &temp_mask);
if (ret < 0)
return ret;
if (cpumask_weight(&temp_mask) !=
cpumask_weight(&the_hcd->reserve_cpus)) {
pr_err("incorrect reserve CPU count. expected=%u\n",
cpumask_weight(&the_hcd->reserve_cpus));
return -EINVAL;
}
spin_lock(&the_hcd->lock);
if (the_hcd->reservation_enabled) {
count = -EPERM;
pr_err("reservation is enabled, can't change reserve_cpus\n");
} else {
cpumask_copy(&the_hcd->reserve_cpus, &temp_mask);
}
spin_unlock(&the_hcd->lock);
return count;
}
static const struct file_operations debugfs_reserve_cpus_ops = {
.read = read_reserve_cpus,
.write = write_reserve_cpus,
};
static void hyp_core_ctl_debugfs_init(void)
{
struct dentry *dir, *file;
dir = debugfs_create_dir("hyp_core_ctl", NULL);
if (IS_ERR_OR_NULL(dir))
return;
file = debugfs_create_file("reserve_cpus", 0644, dir, NULL,
&debugfs_reserve_cpus_ops);
if (!file)
debugfs_remove(dir);
}
static int hyp_core_ctl_probe(struct platform_device *pdev)
{
int ret;
struct hyp_core_ctl_data *hcd;
struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
if (!populated_vcpu_info) {
pr_debug("VCPU info isn't populated, retry\n");
ret = -EPROBE_DEFER;
goto out;
}
hcd = kzalloc(sizeof(*hcd), GFP_KERNEL);
if (!hcd) {
ret = -ENOMEM;
goto out;
}
ret = hyp_core_ctl_init_reserve_cpus(hcd);
if (ret < 0) {
pr_err("Fail to get reserve CPUs from Hyp. ret=%d\n", ret);
goto free_hcd;
}
spin_lock_init(&hcd->lock);
mutex_init(&hcd->reservation_mutex);
hcd->task = kthread_run(hyp_core_ctl_thread, (void *) hcd,
"hyp_core_ctl");
if (IS_ERR(hcd->task)) {
ret = PTR_ERR(hcd->task);
goto free_hcd;
}
sched_setscheduler_nocheck(hcd->task, SCHED_FIFO, &param);
ret = sysfs_create_group(&cpu_subsys.dev_root->kobj,
&hyp_core_ctl_attr_group);
if (ret < 0) {
pr_err("Fail to create sysfs files. ret=%d\n", ret);
goto stop_task;
}
cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
"qcom/hyp_core_ctl:online",
hyp_core_ctl_hp_online, NULL);
cpuhp_setup_state_nocalls(CPUHP_HYP_CORE_CTL_ISOLATION_DEAD,
"qcom/hyp_core_ctl:dead",
NULL, hyp_core_ctl_hp_offline);
cpu_cooling_max_level_notifier_register(&hyp_core_ctl_nb);
hyp_core_ctl_debugfs_init();
the_hcd = hcd;
init_done = true;
return 0;
stop_task:
kthread_stop(hcd->task);
free_hcd:
kfree(hcd);
out:
return ret;
}
static const struct of_device_id hyp_core_ctl_match_table[] = {
{ .compatible = "qcom,hyp-core-ctl" },
{},
};
static struct platform_driver hyp_core_ctl_driver = {
.probe = hyp_core_ctl_probe,
.driver = {
.name = "hyp_core_ctl",
.owner = THIS_MODULE,
.of_match_table = hyp_core_ctl_match_table,
},
};
builtin_platform_driver(hyp_core_ctl_driver);
MODULE_DESCRIPTION("Core Control for Hypervisor");
MODULE_LICENSE("GPL v2");

View file

@ -16,6 +16,7 @@
#include <linux/completion.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <linux/haven/hh_msgq.h>
@ -667,6 +668,9 @@ static int hh_rm_populate_hyp_res(void)
ret = hh_msgq_populate_cap_info(label, cap_id,
HH_MSGQ_DIRECTION_RX, linux_irq);
break;
case HH_RM_RES_TYPE_VCPU:
ret = hh_vcpu_populate_affinity_info(label, cap_id);
break;
case HH_RM_RES_TYPE_DB_TX:
break;
case HH_RM_RES_TYPE_DB_RX:

View file

@ -139,6 +139,7 @@ struct hh_vm_console_write_resp_payload {
#define HH_RM_RES_TYPE_DB_RX 1
#define HH_RM_RES_TYPE_MQ_TX 2
#define HH_RM_RES_TYPE_MQ_RX 3
#define HH_RM_RES_TYPE_VCPU 4
struct hh_vm_get_hyp_res_req_payload {
u16 vmid;

View file

@ -62,4 +62,24 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy)
}
#endif /* defined(CONFIG_THERMAL_OF) && defined(CONFIG_CPU_THERMAL) */
#ifdef CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE
extern void cpu_cooling_max_level_notifier_register(struct notifier_block *n);
extern void cpu_cooling_max_level_notifier_unregister(struct notifier_block *n);
extern const struct cpumask *cpu_cooling_get_max_level_cpumask(void);
#else
static inline
void cpu_cooling_max_level_notifier_register(struct notifier_block *n)
{
}
static inline
void cpu_cooling_max_level_notifier_unregister(struct notifier_block *n)
{
}
static inline const struct cpumask *cpu_cooling_get_max_level_cpumask(void)
{
return cpu_none_mask;
}
#endif /* CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE */
#endif /* __CPU_COOLING_H__ */

View file

@ -71,6 +71,9 @@ enum cpuhp_state {
CPUHP_RCUTREE_PREP,
#ifdef CONFIG_SCHED_WALT
CPUHP_CORE_CTL_ISOLATION_DEAD,
#endif
#ifdef CONFIG_QCOM_HYP_CORE_CTL
CPUHP_HYP_CORE_CTL_ISOLATION_DEAD,
#endif
CPUHP_CPUIDLE_COUPLED_PREPARE,
CPUHP_POWERPC_PMAC_PREPARE,

View file

@ -270,4 +270,17 @@ static inline int hh_hcall_msgq_configure_recv(hh_capid_t msgq_capid,
return ret;
}
static inline int hh_hcall_vcpu_affinity_set(hh_capid_t vcpu_capid,
uint32_t cpu_index)
{
int ret;
struct hh_hcall_resp _resp = {0};
ret = _hh_hcall(0x603d,
(struct hh_hcall_args){ vcpu_capid, cpu_index, -1 },
&_resp);
return ret;
}
#endif

View file

@ -540,6 +540,15 @@ struct cpu_cycle_counter_cb {
DECLARE_PER_CPU_READ_MOSTLY(int, sched_load_boost);
#ifdef CONFIG_QCOM_HYP_CORE_CTL
extern int hh_vcpu_populate_affinity_info(u32 cpu_index, u64 cap_id);
#else
static inline int hh_vcpu_populate_affinity_info(u32 cpu_index, u64 cap_id)
{
return 0;
}
#endif /* CONFIG_QCOM_HYP_CORE_CTL */
#ifdef CONFIG_SCHED_WALT
extern void sched_exit(struct task_struct *p);
extern int

View file

@ -0,0 +1,72 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
*Copyright (c) 2018 The Linux Foundation. All rights reserved.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hyp_core_ctl
#if !defined(_TRACE_HYP_CORE_CTL_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_HYP_CORE_CTL_H
#include <linux/tracepoint.h>
TRACE_EVENT(hyp_core_ctl_enable,
TP_PROTO(bool enable),
TP_ARGS(enable),
TP_STRUCT__entry(
__field(bool, enable)
),
TP_fast_assign(
__entry->enable = enable;
),
TP_printk("enable=%d", __entry->enable)
);
TRACE_EVENT(hyp_core_ctl_status,
TP_PROTO(struct hyp_core_ctl_data *hcd, const char *event),
TP_ARGS(hcd, event),
TP_STRUCT__entry(
__string(event, event)
__array(char, reserve, 32)
__array(char, reserved, 32)
__array(char, our_isolated, 32)
__array(char, online, 32)
__array(char, isolated, 32)
__array(char, thermal, 32)
),
TP_fast_assign(
__assign_str(event, event);
scnprintf(__entry->reserve, sizeof(__entry->reserve), "%*pbl",
cpumask_pr_args(&hcd->reserve_cpus));
scnprintf(__entry->reserved, sizeof(__entry->reserve), "%*pbl",
cpumask_pr_args(&hcd->final_reserved_cpus));
scnprintf(__entry->our_isolated, sizeof(__entry->reserve),
"%*pbl", cpumask_pr_args(&hcd->our_isolated_cpus));
scnprintf(__entry->online, sizeof(__entry->reserve), "%*pbl",
cpumask_pr_args(cpu_online_mask));
scnprintf(__entry->isolated, sizeof(__entry->reserve), "%*pbl",
cpumask_pr_args(cpu_isolated_mask));
scnprintf(__entry->thermal, sizeof(__entry->reserve), "%*pbl",
cpumask_pr_args(cpu_cooling_get_max_level_cpumask()));
),
TP_printk("event=%s reserve=%s reserved=%s our_isolated=%s online=%s isolated=%s thermal=%s",
__get_str(event), __entry->reserve, __entry->reserved,
__entry->our_isolated, __entry->online, __entry->isolated,
__entry->thermal)
);
#endif /* _TRACE_HYP_CORE_CTL_H */
/* This part must be outside protection */
#include <trace/define_trace.h>