|
45 | 45 | #include <linux/kernel.h> |
46 | 46 | #include <linux/cpuidle.h> |
47 | 47 | #include <linux/tick.h> |
| 48 | +#include <linux/time64.h> |
48 | 49 | #include <trace/events/power.h> |
49 | 50 | #include <linux/sched.h> |
50 | 51 | #include <linux/sched/smt.h> |
@@ -75,6 +76,11 @@ static bool ibrs_off __read_mostly; |
75 | 76 |
|
76 | 77 | /* The maximum allowed length for the 'table' module parameter */ |
77 | 78 | #define MAX_CMDLINE_TABLE_LEN 256 |
| 79 | +/* Maximum allowed C-state latency */ |
| 80 | +#define MAX_CMDLINE_LATENCY_US (5 * USEC_PER_MSEC) |
| 81 | +/* Maximum allowed C-state target residency */ |
| 82 | +#define MAX_CMDLINE_RESIDENCY_US (100 * USEC_PER_MSEC) |
| 83 | + |
78 | 84 | static char cmdline_table_str[MAX_CMDLINE_TABLE_LEN] __read_mostly; |
79 | 85 |
|
80 | 86 | static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; |
@@ -2434,6 +2440,41 @@ static char *get_cmdline_field(char *args, char **field, char sep) |
2434 | 2440 | return args + i + 1; |
2435 | 2441 | } |
2436 | 2442 |
|
| 2443 | +/** |
| 2444 | + * validate_cmdline_cstate - Validate a C-state from cmdline. |
| 2445 | + * @state: The C-state to validate. |
| 2446 | + * @prev_state: The previous C-state in the table or NULL. |
| 2447 | + * |
| 2448 | + * Return: 0 if the C-state is valid or -EINVAL otherwise. |
| 2449 | + */ |
| 2450 | +static int validate_cmdline_cstate(struct cpuidle_state *state, |
| 2451 | + struct cpuidle_state *prev_state) |
| 2452 | +{ |
| 2453 | + if (state->exit_latency == 0) |
| 2454 | + /* Exit latency 0 can only be used for the POLL state */ |
| 2455 | + return -EINVAL; |
| 2456 | + |
| 2457 | + if (state->exit_latency > MAX_CMDLINE_LATENCY_US) |
| 2458 | + return -EINVAL; |
| 2459 | + |
| 2460 | + if (state->target_residency > MAX_CMDLINE_RESIDENCY_US) |
| 2461 | + return -EINVAL; |
| 2462 | + |
| 2463 | + if (state->target_residency < state->exit_latency) |
| 2464 | + return -EINVAL; |
| 2465 | + |
| 2466 | + if (!prev_state) |
| 2467 | + return 0; |
| 2468 | + |
| 2469 | + if (state->exit_latency <= prev_state->exit_latency) |
| 2470 | + return -EINVAL; |
| 2471 | + |
| 2472 | + if (state->target_residency <= prev_state->target_residency) |
| 2473 | + return -EINVAL; |
| 2474 | + |
| 2475 | + return 0; |
| 2476 | +} |
| 2477 | + |
2437 | 2478 | /** |
2438 | 2479 | * cmdline_table_adjust - Adjust the C-states table with data from cmdline. |
2439 | 2480 | * @drv: cpuidle driver (assumed to point to intel_idle_driver). |
@@ -2532,6 +2573,19 @@ static void __init cmdline_table_adjust(struct cpuidle_driver *drv) |
2532 | 2573 | state->name, state->exit_latency, state->target_residency); |
2533 | 2574 | } |
2534 | 2575 |
|
| 2576 | + /* Validate the adjusted C-states, start with index 1 to skip POLL */ |
| 2577 | + for (i = 1; i < drv->state_count; i++) { |
| 2578 | + struct cpuidle_state *prev_state; |
| 2579 | + |
| 2580 | + state = &cmdline_states[i]; |
| 2581 | + prev_state = &cmdline_states[i - 1]; |
| 2582 | + |
| 2583 | + if (validate_cmdline_cstate(state, prev_state)) { |
| 2584 | + pr_err("C-state '%s' validation failed\n", state->name); |
| 2585 | + goto error; |
| 2586 | + } |
| 2587 | + } |
| 2588 | + |
2535 | 2589 | /* Copy the adjusted C-states table back */ |
2536 | 2590 | for (i = 1; i < drv->state_count; i++) |
2537 | 2591 | drv->states[i] = cmdline_states[i]; |
|
0 commit comments