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> |
14 | #include <linux/cpu.h> |
15 | #include <linux/cpuidle.h> |
16 | #include <linux/module.h> |
17 | #include <linux/sched/idle.h> |
18 | #include <linux/kvm_para.h> |
19 | #include <linux/cpuidle_haltpoll.h> |
20 | |
21 | static bool force __read_mostly; |
22 | module_param(force, bool, 0444); |
23 | MODULE_PARM_DESC(force, "Load unconditionally" ); |
24 | |
25 | static struct cpuidle_device __percpu *haltpoll_cpuidle_devices; |
26 | static enum cpuhp_state haltpoll_hp_state; |
27 | |
28 | static __cpuidle int default_enter_idle(struct cpuidle_device *dev, |
29 | struct cpuidle_driver *drv, int index) |
30 | { |
31 | if (current_clr_polling_and_test()) |
32 | return index; |
33 | |
34 | arch_cpu_idle(); |
35 | return index; |
36 | } |
37 | |
38 | static struct cpuidle_driver haltpoll_driver = { |
39 | .name = "haltpoll" , |
40 | .governor = "haltpoll" , |
41 | .states = { |
42 | { /* entry 0 is for polling */ }, |
43 | { |
44 | .enter = default_enter_idle, |
45 | .exit_latency = 1, |
46 | .target_residency = 1, |
47 | .power_usage = -1, |
48 | .name = "haltpoll idle" , |
49 | .desc = "default architecture idle" , |
50 | }, |
51 | }, |
52 | .safe_state_index = 0, |
53 | .state_count = 2, |
54 | }; |
55 | |
56 | static int haltpoll_cpu_online(unsigned int cpu) |
57 | { |
58 | struct cpuidle_device *dev; |
59 | |
60 | dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu); |
61 | if (!dev->registered) { |
62 | dev->cpu = cpu; |
63 | if (cpuidle_register_device(dev)) { |
64 | pr_notice("cpuidle_register_device %d failed!\n" , cpu); |
65 | return -EIO; |
66 | } |
67 | arch_haltpoll_enable(cpu); |
68 | } |
69 | |
70 | return 0; |
71 | } |
72 | |
73 | static int haltpoll_cpu_offline(unsigned int cpu) |
74 | { |
75 | struct cpuidle_device *dev; |
76 | |
77 | dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu); |
78 | if (dev->registered) { |
79 | arch_haltpoll_disable(cpu); |
80 | cpuidle_unregister_device(dev); |
81 | } |
82 | |
83 | return 0; |
84 | } |
85 | |
86 | static void haltpoll_uninit(void) |
87 | { |
88 | if (haltpoll_hp_state) |
89 | cpuhp_remove_state(state: haltpoll_hp_state); |
90 | cpuidle_unregister_driver(drv: &haltpoll_driver); |
91 | |
92 | free_percpu(pdata: haltpoll_cpuidle_devices); |
93 | haltpoll_cpuidle_devices = NULL; |
94 | } |
95 | |
96 | static bool haltpoll_want(void) |
97 | { |
98 | return kvm_para_has_hint(KVM_HINTS_REALTIME) || force; |
99 | } |
100 | |
101 | static int __init haltpoll_init(void) |
102 | { |
103 | int ret; |
104 | struct cpuidle_driver *drv = &haltpoll_driver; |
105 | |
106 | /* Do not load haltpoll if idle= is passed */ |
107 | if (boot_option_idle_override != IDLE_NO_OVERRIDE) |
108 | return -ENODEV; |
109 | |
110 | if (!kvm_para_available() || !haltpoll_want()) |
111 | return -ENODEV; |
112 | |
113 | cpuidle_poll_state_init(drv); |
114 | |
115 | ret = cpuidle_register_driver(drv); |
116 | if (ret < 0) |
117 | return ret; |
118 | |
119 | haltpoll_cpuidle_devices = alloc_percpu(struct cpuidle_device); |
120 | if (haltpoll_cpuidle_devices == NULL) { |
121 | cpuidle_unregister_driver(drv); |
122 | return -ENOMEM; |
123 | } |
124 | |
125 | ret = cpuhp_setup_state(state: CPUHP_AP_ONLINE_DYN, name: "cpuidle/haltpoll:online" , |
126 | startup: haltpoll_cpu_online, teardown: haltpoll_cpu_offline); |
127 | if (ret < 0) { |
128 | haltpoll_uninit(); |
129 | } else { |
130 | haltpoll_hp_state = ret; |
131 | ret = 0; |
132 | } |
133 | |
134 | return ret; |
135 | } |
136 | |
137 | static void __exit haltpoll_exit(void) |
138 | { |
139 | haltpoll_uninit(); |
140 | } |
141 | |
142 | module_init(haltpoll_init); |
143 | module_exit(haltpoll_exit); |
144 | MODULE_LICENSE("GPL" ); |
145 | MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>" ); |
146 | |