// SPDX-License-Identifier: GPL-2.0 /* * KernelSpace Profiles * * This Linux kernel module provides a framework for managing and switching between system profiles or modes * at the kernel level. Each profile represents a specific configuration of kernel features and settings * optimized for different use cases such as battery life, balanced performance, or maximum performance. * * The module supports various subsystems, including MSM DRM, MI DRM, and framebuffer (FB). It integrates * with these subsystems to receive notifications about screen state changes and adjust the active profile * accordingly. * * The module offers functions for setting the profile mode, overriding the mode temporarily, and retrieving * the active profile mode. Profiles can be dynamically switched based on system events, user requests, or * time-based rules. * * For more information and usage examples, refer to the README file at: * https://github.com/beakthoven/Kprofiles/blob/main/README.md * * Copyright (C) 2021-2025 Dakkshesh * Version: 6.0.0 * License: GPL-2.0 */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include "version.h" static BLOCKING_NOTIFIER_HEAD(kp_mode_notifier); unsigned int KP_MODE_CHANGE = 0x80000000; EXPORT_SYMBOL_GPL(KP_MODE_CHANGE); static unsigned int kp_override_mode; static bool kp_override; static bool auto_kp __read_mostly = 0; module_param(auto_kp, bool, 0664); MODULE_PARM_DESC(auto_kp, "Enable/disable automatic kernel profile management"); static unsigned int kp_mode = CONFIG_KP_DEFAULT_MODE; static struct kobject *kp_kobj; DEFINE_MUTEX(kp_set_mode_rb_lock); #ifdef CONFIG_KP_VERBOSE_DEBUG #define kp_dbg(fmt, ...) pr_info(fmt, ##__VA_ARGS__) #else #define kp_dbg(fmt, ...) #endif #define kp_err(fmt, ...) pr_err(fmt, ##__VA_ARGS__) #define kp_info(fmt, ...) pr_info(fmt, ##__VA_ARGS__) /* * Mapping: * - 0 -> 2 (kprofiles "disabled" behaves like mode 2 to in-kernel users) * - 1..3 unchanged */ static __always_inline unsigned int kp_effective_mode(unsigned int stored) { if (stored == 0) return 2; return stored; } static void kp_trigger_mode_change_event(void); static __always_inline int __kp_set_mode(unsigned int level) { if (unlikely(level > 3)) return -EINVAL; WRITE_ONCE(kp_mode, level); return 0; } /** * kp_set_mode_rollback - Change profile to a given mode for a specific duration * @level: The profile mode level to set (0-3) * @duration_ms: The duration to keep the profile mode in milliseconds * * This function changes the profile to the specified mode for a specific * duration during any in-kernel event, and then returns to the previously * active mode. * * Usage example: kp_set_mode_rollback(3, 55); */ void kp_set_mode_rollback(unsigned int level, unsigned int duration_ms) { if (unlikely(level > 3)) { kp_err("Invalid mode requested, skipping mode change.\n"); return; } mutex_lock(&kp_set_mode_rb_lock); WRITE_ONCE(kp_override_mode, level); WRITE_ONCE(kp_override, true); kp_trigger_mode_change_event(); msleep(duration_ms); WRITE_ONCE(kp_override, false); kp_trigger_mode_change_event(); mutex_unlock(&kp_set_mode_rb_lock); } EXPORT_SYMBOL_GPL(kp_set_mode_rollback); /** * kp_set_mode - Change profile to a given mode * @level: The profile mode level to set (0-3) * * This function changes the profile to the specified mode during any * in-kernel event. * * Usage example: kp_set_mode(3); */ void kp_set_mode(unsigned int level) { int ret = 0; mutex_lock(&kp_set_mode_rb_lock); ret = __kp_set_mode(level); if (ret) { kp_err("Invalid mode requested, skipping mode change.\n"); mutex_unlock(&kp_set_mode_rb_lock); return; } kp_trigger_mode_change_event(); mutex_unlock(&kp_set_mode_rb_lock); } EXPORT_SYMBOL_GPL(kp_set_mode); /** * kp_active_mode - Get the currently active profile mode * * This function returns a number from 0 to 3 depending on the active profile mode. * The returned value can be used in conditions to disable/enable or tune kernel * features according to the profile mode. * * Return: * The currently active profile mode (0-3) * * Usage example: * * switch (kp_active_mode()) { * case 1: * // Things to be done when battery profile is active * break; * case 2: * // Things to be done when balanced profile is active * break; * case 3: * // Things to be done when performance profile is active * break; * default: * // Things to be done when kprofiles is disabled (forced to balanced in kernel) * break; * } */ int kp_active_mode(void) { unsigned int mode; if (READ_ONCE(kp_override)) { mode = READ_ONCE(kp_override_mode); if (unlikely(mode > 3)) mode = 0; return kp_effective_mode(mode); } mode = READ_ONCE(kp_mode); if (unlikely(mode > 3)) { WRITE_ONCE(kp_mode, 0); kp_err("Invalid value passed, falling back to level 0\n"); mode = 0; } return kp_effective_mode(mode); } EXPORT_SYMBOL_GPL(kp_active_mode); /** * kp_trigger_mode_change_event - Trigger a mode change event * * This function triggers a mode change event by calling the blocking notifier * chain for kp_mode_notifier. It informs all registered listeners about the * change in the profile mode. */ static __always_inline void kp_trigger_mode_change_event(void) { unsigned int current_mode = kp_active_mode(); blocking_notifier_call_chain(&kp_mode_notifier, KP_MODE_CHANGE, (void *)(uintptr_t)current_mode); } /** * kp_notifier_register_client - Register a notifier client for profile mode changes * @nb: The notifier block to register * * This function registers a notifier client to receive notifications about profile mode changes. * * Return: * 0 on success, or an error code on failure. */ int kp_notifier_register_client(struct notifier_block *nb) { return blocking_notifier_chain_register(&kp_mode_notifier, nb); } EXPORT_SYMBOL_GPL(kp_notifier_register_client); /** * kp_notifier_unregister_client - Unregister a notifier client for profile mode changes * @nb: The notifier block to unregister * * This function unregisters a previously registered notifier client for profile mode changes. * * Return: * 0 on success, or an error code on failure. */ int kp_notifier_unregister_client(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&kp_mode_notifier, nb); } EXPORT_SYMBOL_GPL(kp_notifier_unregister_client); static inline ssize_t kp_mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return sysfs_emit(buf, "%u\n", READ_ONCE(kp_mode)); } static inline ssize_t kp_mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { unsigned int new_mode; int ret; ret = kstrtouint(buf, 10, &new_mode); if (ret) return ret; mutex_lock(&kp_set_mode_rb_lock); ret = __kp_set_mode(new_mode); if (ret) { kp_err("User changed mode is invalid, skipping mode change.\n"); mutex_unlock(&kp_set_mode_rb_lock); return ret; } kp_trigger_mode_change_event(); mutex_unlock(&kp_set_mode_rb_lock); return count; } static struct kobj_attribute kp_mode_attribute = __ATTR(kp_mode, 0664, kp_mode_show, kp_mode_store); static struct attribute *kp_attrs[] = { &kp_mode_attribute.attr, NULL, }; static struct attribute_group kp_attr_group = { .attrs = kp_attrs, }; static int __init kp_init(void) { int ret = 0; kp_kobj = kobject_create_and_add("kprofiles", kernel_kobj); if (!kp_kobj) { kp_err("Failed to create Kprofiles kobject\n"); return -ENOMEM; } ret = sysfs_create_group(kp_kobj, &kp_attr_group); if (ret) { kp_err("Failed to create sysfs attributes for Kprofiles\n"); kobject_put(kp_kobj); return ret; } kp_info("Kprofiles " KPROFILES_VERSION " loaded successfully. For further details, visit https://github.com/beakthoven/Kprofiles/blob/main/README.md\n"); kp_info("Copyright (C) 2021-2025 Dakkshesh .\n"); return ret; } module_init(kp_init); static void __exit kp_exit(void) { sysfs_remove_group(kp_kobj, &kp_attr_group); kobject_put(kp_kobj); } module_exit(kp_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("KernelSpace Profiles"); MODULE_AUTHOR("Dakkshesh "); MODULE_VERSION(KPROFILES_VERSION);