Commit | Line | Data |
---|---|---|
fa86ee90 MT |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * cpuidle driver for haltpoll governor. | |
4 | * | |
5 | * Copyright 2019 Red Hat, Inc. and/or its affiliates. | |
6 | * | |
7 | * This work is licensed under the terms of the GNU GPL, version 2. See | |
8 | * the COPYING file in the top-level directory. | |
9 | * | |
10 | * Authors: Marcelo Tosatti <mtosatti@redhat.com> | |
11 | */ | |
12 | ||
13 | #include <linux/init.h> | |
97d3eb9d | 14 | #include <linux/cpu.h> |
fa86ee90 MT |
15 | #include <linux/cpuidle.h> |
16 | #include <linux/module.h> | |
17 | #include <linux/sched/idle.h> | |
18 | #include <linux/kvm_para.h> | |
a1c4423b | 19 | #include <linux/cpuidle_haltpoll.h> |
fa86ee90 | 20 | |
dd52551f MS |
21 | static bool force __read_mostly; |
22 | module_param(force, bool, 0444); | |
23 | MODULE_PARM_DESC(force, "Load unconditionally"); | |
24 | ||
97d3eb9d JM |
25 | static struct cpuidle_device __percpu *haltpoll_cpuidle_devices; |
26 | static enum cpuhp_state haltpoll_hp_state; | |
27 | ||
fa86ee90 MT |
28 | static int default_enter_idle(struct cpuidle_device *dev, |
29 | struct cpuidle_driver *drv, int index) | |
30 | { | |
31 | if (current_clr_polling_and_test()) { | |
32 | local_irq_enable(); | |
33 | return index; | |
34 | } | |
35 | default_idle(); | |
36 | return index; | |
37 | } | |
38 | ||
39 | static struct cpuidle_driver haltpoll_driver = { | |
40 | .name = "haltpoll", | |
73214408 | 41 | .governor = "haltpoll", |
fa86ee90 MT |
42 | .states = { |
43 | { /* entry 0 is for polling */ }, | |
44 | { | |
45 | .enter = default_enter_idle, | |
46 | .exit_latency = 1, | |
47 | .target_residency = 1, | |
48 | .power_usage = -1, | |
49 | .name = "haltpoll idle", | |
50 | .desc = "default architecture idle", | |
51 | }, | |
52 | }, | |
53 | .safe_state_index = 0, | |
54 | .state_count = 2, | |
55 | }; | |
56 | ||
97d3eb9d JM |
57 | static int haltpoll_cpu_online(unsigned int cpu) |
58 | { | |
59 | struct cpuidle_device *dev; | |
60 | ||
61 | dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu); | |
62 | if (!dev->registered) { | |
63 | dev->cpu = cpu; | |
64 | if (cpuidle_register_device(dev)) { | |
65 | pr_notice("cpuidle_register_device %d failed!\n", cpu); | |
66 | return -EIO; | |
67 | } | |
68 | arch_haltpoll_enable(cpu); | |
69 | } | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | static int haltpoll_cpu_offline(unsigned int cpu) | |
75 | { | |
76 | struct cpuidle_device *dev; | |
77 | ||
78 | dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu); | |
79 | if (dev->registered) { | |
80 | arch_haltpoll_disable(cpu); | |
81 | cpuidle_unregister_device(dev); | |
82 | } | |
83 | ||
84 | return 0; | |
85 | } | |
86 | ||
87 | static void haltpoll_uninit(void) | |
88 | { | |
89 | if (haltpoll_hp_state) | |
90 | cpuhp_remove_state(haltpoll_hp_state); | |
91 | cpuidle_unregister_driver(&haltpoll_driver); | |
92 | ||
93 | free_percpu(haltpoll_cpuidle_devices); | |
94 | haltpoll_cpuidle_devices = NULL; | |
95 | } | |
96 | ||
dd52551f MS |
97 | static bool haltpool_want(void) |
98 | { | |
99 | return kvm_para_has_hint(KVM_HINTS_REALTIME) || force; | |
100 | } | |
101 | ||
fa86ee90 MT |
102 | static int __init haltpoll_init(void) |
103 | { | |
a1c4423b | 104 | int ret; |
fa86ee90 MT |
105 | struct cpuidle_driver *drv = &haltpoll_driver; |
106 | ||
31d85140 ZD |
107 | /* Do not load haltpoll if idle= is passed */ |
108 | if (boot_option_idle_override != IDLE_NO_OVERRIDE) | |
109 | return -ENODEV; | |
110 | ||
fa86ee90 MT |
111 | cpuidle_poll_state_init(drv); |
112 | ||
dd52551f | 113 | if (!kvm_para_available() || !haltpool_want()) |
5cc59f59 | 114 | return -ENODEV; |
fa86ee90 | 115 | |
97d3eb9d JM |
116 | ret = cpuidle_register_driver(drv); |
117 | if (ret < 0) | |
118 | return ret; | |
119 | ||
120 | haltpoll_cpuidle_devices = alloc_percpu(struct cpuidle_device); | |
121 | if (haltpoll_cpuidle_devices == NULL) { | |
122 | cpuidle_unregister_driver(drv); | |
123 | return -ENOMEM; | |
124 | } | |
125 | ||
126 | ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpuidle/haltpoll:online", | |
127 | haltpoll_cpu_online, haltpoll_cpu_offline); | |
128 | if (ret < 0) { | |
129 | haltpoll_uninit(); | |
130 | } else { | |
131 | haltpoll_hp_state = ret; | |
132 | ret = 0; | |
133 | } | |
a1c4423b MT |
134 | |
135 | return ret; | |
fa86ee90 MT |
136 | } |
137 | ||
138 | static void __exit haltpoll_exit(void) | |
139 | { | |
97d3eb9d | 140 | haltpoll_uninit(); |
fa86ee90 MT |
141 | } |
142 | ||
143 | module_init(haltpoll_init); | |
144 | module_exit(haltpoll_exit); | |
145 | MODULE_LICENSE("GPL"); | |
146 | MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>"); |