Skip to content

Commit 111f77a

Browse files
dedekindrafaeljw
authored andcommitted
intel_idle: Add cmdline option to adjust C-states table
Add a new module parameter that allows adjusting the C-states table used by the driver. Currently, the C-states table is hardcoded in the driver based on the CPU model. The goal is to have good enough defaults for most users. However, C-state characteristics, such as exit latency and residency, can vary between different variants of the same CPU model and BIOS settings. Moreover, different platform usage models and user preferences may benefit from different C-state target_residency values. Provide a way for users to adjust the C-states table via a module parameter "table". The general format is: "state1:latency1:target_residency1,state2:latency2:target_residency2,..." In other words, represent each C-state by its name, exit latency (in microseconds), and target residency (in microseconds), separated by colons. Separate multiple C-states by commas. For example, suppose a CPU has 3 C-states with the following characteristics: C1: exit_latency=1, target_residency=2 C1E: exit_latency=10, target_residency=10 C6: exit_latency=100, target_residency=500 Users can specify a custom C-states table as follows: 1. intel_idle.table="C1:2:2,C1E:5:20,C6:150:600" Result: C1: exit_latency=2, target_residency=2 C1E: exit_latency=5, target_residency=20 C6: exit_latency=150, target_residency=600 2. intel_idle.table="C6::400" Result: C1: exit_latency=1, target_residency=2 (unchanged) C1E: exit_latency=10, target_residency=10 (unchanged) C6: exit_latency=100, target_residency=400 (only target_residency changed) Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Link: https://patch.msgid.link/20251216080402.156988-3-dedekind1@gmail.com Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent ff24f31 commit 111f77a

1 file changed

Lines changed: 169 additions & 0 deletions

File tree

drivers/idle/intel_idle.c

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ static unsigned int disabled_states_mask __read_mostly;
7373
static bool force_irq_on __read_mostly;
7474
static bool ibrs_off __read_mostly;
7575

76+
/* The maximum allowed length for the 'table' module parameter */
77+
#define MAX_CMDLINE_TABLE_LEN 256
78+
static char cmdline_table_str[MAX_CMDLINE_TABLE_LEN] __read_mostly;
79+
7680
static struct cpuidle_device __percpu *intel_idle_cpuidle_devices;
7781

7882
static unsigned long auto_demotion_disable_flags;
@@ -104,6 +108,9 @@ static struct device *sysfs_root __initdata;
104108
static const struct idle_cpu *icpu __initdata;
105109
static struct cpuidle_state *cpuidle_state_table __initdata;
106110

111+
/* C-states data from the 'intel_idle.table' cmdline parameter */
112+
static struct cpuidle_state cmdline_states[CPUIDLE_STATE_MAX] __initdata;
113+
107114
static unsigned int mwait_substates __initdata;
108115

109116
/*
@@ -2393,6 +2400,149 @@ static void __init intel_idle_sysfs_uninit(void)
23932400
put_device(sysfs_root);
23942401
}
23952402

2403+
/**
2404+
* get_cmdline_field - Get the current field from a cmdline string.
2405+
* @args: The cmdline string to get the current field from.
2406+
* @field: Pointer to the current field upon return.
2407+
* @sep: The fields separator character.
2408+
*
2409+
* Examples:
2410+
* Input: args="C1:1:1,C1E:2:10", sep=':'
2411+
* Output: field="C1", return "1:1,C1E:2:10"
2412+
* Input: args="C1:1:1,C1E:2:10", sep=','
2413+
* Output: field="C1:1:1", return "C1E:2:10"
2414+
* Ipnut: args="::", sep=':'
2415+
* Output: field="", return ":"
2416+
*
2417+
* Return: The continuation of the cmdline string after the field or NULL.
2418+
*/
2419+
static char *get_cmdline_field(char *args, char **field, char sep)
2420+
{
2421+
unsigned int i;
2422+
2423+
for (i = 0; args[i] && !isspace(args[i]); i++) {
2424+
if (args[i] == sep)
2425+
break;
2426+
}
2427+
2428+
*field = args;
2429+
2430+
if (args[i] != sep)
2431+
return NULL;
2432+
2433+
args[i] = '\0';
2434+
return args + i + 1;
2435+
}
2436+
2437+
/**
2438+
* cmdline_table_adjust - Adjust the C-states table with data from cmdline.
2439+
* @drv: cpuidle driver (assumed to point to intel_idle_driver).
2440+
*
2441+
* Adjust the C-states table with data from the 'intel_idle.table' module
2442+
* parameter (if specified).
2443+
*/
2444+
static void __init cmdline_table_adjust(struct cpuidle_driver *drv)
2445+
{
2446+
char *args = cmdline_table_str;
2447+
struct cpuidle_state *state;
2448+
int i;
2449+
2450+
if (args[0] == '\0')
2451+
/* The 'intel_idle.table' module parameter was not specified */
2452+
return;
2453+
2454+
/* Create a copy of the C-states table */
2455+
for (i = 0; i < drv->state_count; i++)
2456+
cmdline_states[i] = drv->states[i];
2457+
2458+
/*
2459+
* Adjust the C-states table copy with data from the 'intel_idle.table'
2460+
* module parameter.
2461+
*/
2462+
while (args) {
2463+
char *fields, *name, *val;
2464+
2465+
/*
2466+
* Get the next C-state definition, which is expected to be
2467+
* '<name>:<latency_us>:<target_residency_us>'. Treat "empty"
2468+
* fields as unchanged. For example,
2469+
* '<name>::<target_residency_us>' leaves the latency unchanged.
2470+
*/
2471+
args = get_cmdline_field(args, &fields, ',');
2472+
2473+
/* name */
2474+
fields = get_cmdline_field(fields, &name, ':');
2475+
if (!fields)
2476+
goto error;
2477+
2478+
if (!strcmp(name, "POLL")) {
2479+
pr_err("Cannot adjust POLL\n");
2480+
continue;
2481+
}
2482+
2483+
/* Find the C-state by its name */
2484+
state = NULL;
2485+
for (i = 0; i < drv->state_count; i++) {
2486+
if (!strcmp(name, drv->states[i].name)) {
2487+
state = &cmdline_states[i];
2488+
break;
2489+
}
2490+
}
2491+
2492+
if (!state) {
2493+
pr_err("C-state '%s' was not found\n", name);
2494+
continue;
2495+
}
2496+
2497+
/* Latency */
2498+
fields = get_cmdline_field(fields, &val, ':');
2499+
if (!fields)
2500+
goto error;
2501+
2502+
if (*val) {
2503+
if (kstrtouint(val, 0, &state->exit_latency))
2504+
goto error;
2505+
}
2506+
2507+
/* Target residency */
2508+
fields = get_cmdline_field(fields, &val, ':');
2509+
2510+
if (*val) {
2511+
if (kstrtouint(val, 0, &state->target_residency))
2512+
goto error;
2513+
}
2514+
2515+
/*
2516+
* Allow for 3 more fields, but ignore them. Helps to make
2517+
* possible future extensions of the cmdline format backward
2518+
* compatible.
2519+
*/
2520+
for (i = 0; fields && i < 3; i++) {
2521+
fields = get_cmdline_field(fields, &val, ':');
2522+
if (!fields)
2523+
break;
2524+
}
2525+
2526+
if (fields) {
2527+
pr_err("Too many fields for C-state '%s'\n", state->name);
2528+
goto error;
2529+
}
2530+
2531+
pr_info("C-state from cmdline: name=%s, latency=%u, residency=%u\n",
2532+
state->name, state->exit_latency, state->target_residency);
2533+
}
2534+
2535+
/* Copy the adjusted C-states table back */
2536+
for (i = 1; i < drv->state_count; i++)
2537+
drv->states[i] = cmdline_states[i];
2538+
2539+
pr_info("Adjusted C-states with data from 'intel_idle.table'\n");
2540+
return;
2541+
2542+
error:
2543+
pr_info("Failed to adjust C-states with data from 'intel_idle.table'\n");
2544+
}
2545+
23962546
static int __init intel_idle_init(void)
23972547
{
23982548
const struct x86_cpu_id *id;
@@ -2456,6 +2606,7 @@ static int __init intel_idle_init(void)
24562606
return -ENOMEM;
24572607

24582608
intel_idle_cpuidle_driver_init(&intel_idle_driver);
2609+
cmdline_table_adjust(&intel_idle_driver);
24592610

24602611
retval = intel_idle_sysfs_init();
24612612
if (retval)
@@ -2519,3 +2670,21 @@ module_param(force_irq_on, bool, 0444);
25192670
*/
25202671
module_param(ibrs_off, bool, 0444);
25212672
MODULE_PARM_DESC(ibrs_off, "Disable IBRS when idle");
2673+
2674+
/*
2675+
* Define the C-states table from a user input string. Expected format is
2676+
* 'name:latency:residency', where:
2677+
* - name: The C-state name.
2678+
* - latency: The C-state exit latency in us.
2679+
* - residency: The C-state target residency in us.
2680+
*
2681+
* Multiple C-states can be defined by separating them with commas:
2682+
* 'name1:latency1:residency1,name2:latency2:residency2'
2683+
*
2684+
* Example: intel_idle.table=C1:1:1,C1E:5:10,C6:100:600
2685+
*
2686+
* To leave latency or residency unchanged, use an empty field, for example:
2687+
* 'C1:1:1,C1E::10' - leaves C1E latency unchanged.
2688+
*/
2689+
module_param_string(table, cmdline_table_str, MAX_CMDLINE_TABLE_LEN, 0444);
2690+
MODULE_PARM_DESC(table, "Build the C-states table from a user input string");

0 commit comments

Comments
 (0)