diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index c63710cd7b60..641ea8602a8f 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -1,5 +1,4 @@ # CONFIG_LOCALVERSION_AUTO is not set -CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_FHANDLE=y CONFIG_AUDIT=y @@ -26,7 +25,6 @@ CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set -# CONFIG_IPC_NS is not set # CONFIG_NET_NS is not set CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 7293e33a53e2..9ef26672fa4d 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -1,5 +1,4 @@ # CONFIG_LOCALVERSION_AUTO is not set -CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_FHANDLE=y CONFIG_AUDIT=y @@ -26,7 +25,6 @@ CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set -# CONFIG_IPC_NS is not set # CONFIG_NET_NS is not set CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index e58e40c303c6..1c62c5f8e3c7 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -1,5 +1,4 @@ # CONFIG_LOCALVERSION_AUTO is not set -CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_FHANDLE=y CONFIG_AUDIT=y @@ -26,7 +25,6 @@ CONFIG_CGROUP_HUGETLB=y CONFIG_RT_GROUP_SCHED=y CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set -# CONFIG_IPC_NS is not set # CONFIG_NET_NS is not set CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 2cad34da23fd..23cbac2625dc 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -1,5 +1,4 @@ # CONFIG_LOCALVERSION_AUTO is not set -CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_FHANDLE=y CONFIG_AUDIT=y @@ -26,7 +25,6 @@ CONFIG_CGROUP_HUGETLB=y CONFIG_RT_GROUP_SCHED=y CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set -# CONFIG_IPC_NS is not set # CONFIG_NET_NS is not set CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 6d9b9ab954ee..2babb640d38c 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -468,6 +468,13 @@ config MSM_ADSP_LOADER for the platforms that use APRv2. Say M if you want to enable this module. +config MSM_PERFORMANCE + tristate "Core control driver to support userspace hotplug requests" + help + This driver is used to provide CPU hotplug support to userspace. + It ensures that no more than a user specified number of CPUs stay + online at any given point in time. + endif # ARCH_QCOM config MSM_SUBSYSTEM_RESTART diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 75f80e19029a..5b81701da99f 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -40,6 +40,8 @@ obj-$(CONFIG_MSM_CORE_CTL_HELPER) += core_ctl_helper.o obj-$(CONFIG_MSM_PFE_WA) += pfe-wa.o obj-$(CONFIG_ARCH_MSM8996) += msm_cpu_voltage.o +obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o + ifdef CONFIG_MSM_SUBSYSTEM_RESTART obj-y += subsystem_notif.o obj-y += subsystem_restart.o diff --git a/drivers/soc/qcom/msm_performance.c b/drivers/soc/qcom/msm_performance.c new file mode 100644 index 000000000000..f307c664615e --- /dev/null +++ b/drivers/soc/qcom/msm_performance.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include + +/* Delay in jiffies for hotplugging to complete */ +#define MIN_HOTPLUG_DELAY 3 + +/* Number of CPUs to maintain online */ +static unsigned int max_cpus; + +/* List of CPUs managed by this module */ +static struct cpumask managed_cpus; +static struct mutex managed_cpus_lock; + +/* To keep track of CPUs that the module decides to offline */ +static struct cpumask managed_offline_cpus; + +/* Work to evaluate the onlining/offlining CPUs */ +struct delayed_work try_hotplug_work; + +static unsigned int num_online_managed(void); + +static int set_max_cpus(const char *buf, const struct kernel_param *kp) +{ + unsigned int val; + + if (sscanf(buf, "%u\n", &val) != 1) + return -EINVAL; + if (val > cpumask_weight(&managed_cpus)) + return -EINVAL; + + max_cpus = val; + schedule_delayed_work(&try_hotplug_work, 0); + trace_set_max_cpus(cpumask_bits(&managed_cpus)[0], max_cpus); + + return 0; +} + +static int get_max_cpus(char *buf, const struct kernel_param *kp) +{ + return snprintf(buf, PAGE_SIZE, "%u", max_cpus); +} + +static const struct kernel_param_ops param_ops_max_cpus = { + .set = set_max_cpus, + .get = get_max_cpus, +}; + +device_param_cb(max_cpus, ¶m_ops_max_cpus, NULL, 0644); + +static int set_managed_cpus(const char *buf, const struct kernel_param *kp) +{ + int ret; + + mutex_lock(&managed_cpus_lock); + ret = cpulist_parse(buf, &managed_cpus); + cpumask_clear(&managed_offline_cpus); + mutex_unlock(&managed_cpus_lock); + + return ret; +} + +static int get_managed_cpus(char *buf, const struct kernel_param *kp) +{ + return cpulist_scnprintf(buf, PAGE_SIZE, &managed_cpus); +} + +static const struct kernel_param_ops param_ops_managed_cpus = { + .set = set_managed_cpus, + .get = get_managed_cpus, +}; +device_param_cb(managed_cpus, ¶m_ops_managed_cpus, NULL, 0644); + +/* To display all the online managed CPUs */ +static int get_managed_online_cpus(char *buf, const struct kernel_param *kp) +{ + struct cpumask tmp_mask; + + cpumask_clear(&tmp_mask); + mutex_lock(&managed_cpus_lock); + cpumask_complement(&tmp_mask, &managed_offline_cpus); + cpumask_and(&tmp_mask, &managed_cpus, &tmp_mask); + mutex_unlock(&managed_cpus_lock); + + return cpulist_scnprintf(buf, PAGE_SIZE, &tmp_mask); +} + +static const struct kernel_param_ops param_ops_managed_online_cpus = { + .get = get_managed_online_cpus, +}; +device_param_cb(managed_online_cpus, ¶m_ops_managed_online_cpus, + NULL, 0444); + +static unsigned int num_online_managed(void) +{ + struct cpumask tmp_mask; + + cpumask_clear(&tmp_mask); + cpumask_and(&tmp_mask, &managed_cpus, cpu_online_mask); + + return cpumask_weight(&tmp_mask); +} + +/* + * try_hotplug tries to online/offline cores based on the current requirement. + * It loops through the currently managed CPUs and tries to online/offline + * them until the max_cpus criteria is met. + */ +static void __ref try_hotplug(struct work_struct *work) +{ + unsigned int i; + + if (cpumask_empty(&managed_cpus) || (num_online_managed() == max_cpus)) + return; + + pr_debug("msm_perf: Trying hotplug...%d:%d\n", num_online_managed(), + num_online_cpus()); + + mutex_lock(&managed_cpus_lock); + if (num_online_managed() > max_cpus) { + for (i = num_present_cpus() - 1; i >= 0; i--) { + if (!cpumask_test_cpu(i, &managed_cpus) || + !cpu_online(i)) + continue; + + pr_debug("msm_perf: Offlining CPU%d\n", i); + cpumask_set_cpu(i, &managed_offline_cpus); + if (cpu_down(i)) { + cpumask_clear_cpu(i, &managed_offline_cpus); + pr_debug("msm_perf: Offlining CPU%d failed\n", + i); + continue; + } + if (num_online_managed() <= max_cpus) + break; + } + } else { + for_each_cpu(i, &managed_cpus) { + if (cpu_online(i)) + continue; + pr_debug("msm_perf: Onlining CPU%d\n", i); + if (cpu_up(i)) { + pr_debug("msm_perf: Onlining CPU%d failed\n", + i); + continue; + } + cpumask_clear_cpu(i, &managed_offline_cpus); + if (num_online_managed() >= max_cpus) + break; + } + } + mutex_unlock(&managed_cpus_lock); +} + +static int __ref msm_performance_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + uint32_t cpu = (uintptr_t)hcpu; + + if (!cpumask_test_cpu(cpu, &managed_cpus)) + return NOTIFY_OK; + + if (action == CPU_UP_PREPARE || action == CPU_UP_PREPARE_FROZEN) { + /* + * Prevent onlining of a managed CPU if max_cpu criteria is + * already satisfied + */ + if (max_cpus <= num_online_managed()) { + pr_debug("msm_perf: Prevent CPU%d onlining\n", cpu); + return NOTIFY_BAD; + } + cpumask_clear_cpu(cpu, &managed_offline_cpus); + + } else if (!cpumask_test_cpu(cpu, &managed_offline_cpus) && + (action == CPU_DEAD)) { + /* + * Schedule a re-evaluation to check if any more CPUs can be + * brought online to meet the max_cpus requirement. This work + * is delayed to account for CPU hotplug latencies + */ + if (schedule_delayed_work(&try_hotplug_work, 0)) { + trace_reevaluate_hotplug(cpumask_bits(&managed_cpus)[0], + max_cpus); + pr_debug("msm_perf: Re-evaluation scheduled %d\n", cpu); + } else { + pr_debug("msm_perf: Work scheduling failed %d\n", cpu); + } + } + + return NOTIFY_OK; +} + +static struct notifier_block __refdata msm_performance_cpu_notifier = { + .notifier_call = msm_performance_cpu_callback, +}; + +static int __init msm_performance_init(void) +{ + + INIT_DELAYED_WORK(&try_hotplug_work, try_hotplug); + mutex_init(&managed_cpus_lock); + cpumask_clear(&managed_offline_cpus); + + register_cpu_notifier(&msm_performance_cpu_notifier); + return 0; +} +late_initcall(msm_performance_init); + diff --git a/include/trace/events/power.h b/include/trace/events/power.h index 47d25a651c2a..0af804eb97c7 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -765,6 +765,35 @@ TRACE_EVENT(memlat_dev_update, __entry->vote) ); +DECLARE_EVENT_CLASS(kpm_module, + + TP_PROTO(unsigned int managed_cpus, unsigned int max_cpus), + + TP_ARGS(managed_cpus, max_cpus), + + TP_STRUCT__entry( + __field(u32, managed_cpus) + __field(u32, max_cpus) + ), + + TP_fast_assign( + __entry->managed_cpus = managed_cpus; + __entry->max_cpus = max_cpus; + ), + + TP_printk("managed:%x max_cpus=%u", (unsigned int)__entry->managed_cpus, + (unsigned int)__entry->max_cpus) +); + +DEFINE_EVENT(kpm_module, set_max_cpus, + TP_PROTO(unsigned int managed_cpus, unsigned int max_cpus), + TP_ARGS(managed_cpus, max_cpus) +); + +DEFINE_EVENT(kpm_module, reevaluate_hotplug, + TP_PROTO(unsigned int managed_cpus, unsigned int max_cpus), + TP_ARGS(managed_cpus, max_cpus) +); #endif /* _TRACE_POWER_H */ /* This part must be outside protection */