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 | |
97d3eb9d JM |
21 | static struct cpuidle_device __percpu *haltpoll_cpuidle_devices; |
22 | static enum cpuhp_state haltpoll_hp_state; | |
23 | ||
fa86ee90 MT |
24 | static int default_enter_idle(struct cpuidle_device *dev, |
25 | struct cpuidle_driver *drv, int index) | |
26 | { | |
27 | if (current_clr_polling_and_test()) { | |
28 | local_irq_enable(); | |
29 | return index; | |
30 | } | |
31 | default_idle(); | |
32 | return index; | |
33 | } | |
34 | ||
35 | static struct cpuidle_driver haltpoll_driver = { | |
36 | .name = "haltpoll", | |
73214408 | 37 | .governor = "haltpoll", |
fa86ee90 MT |
38 | .states = { |
39 | { /* entry 0 is for polling */ }, | |
40 | { | |
41 | .enter = default_enter_idle, | |
42 | .exit_latency = 1, | |
43 | .target_residency = 1, | |
44 | .power_usage = -1, | |
45 | .name = "haltpoll idle", | |
46 | .desc = "default architecture idle", | |
47 | }, | |
48 | }, | |
49 | .safe_state_index = 0, | |
50 | .state_count = 2, | |
51 | }; | |
52 | ||
97d3eb9d JM |
53 | static int haltpoll_cpu_online(unsigned int cpu) |
54 | { | |
55 | struct cpuidle_device *dev; | |
56 | ||
57 | dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu); | |
58 | if (!dev->registered) { | |
59 | dev->cpu = cpu; | |
60 | if (cpuidle_register_device(dev)) { | |
61 | pr_notice("cpuidle_register_device %d failed!\n", cpu); | |
62 | return -EIO; | |
63 | } | |
64 | arch_haltpoll_enable(cpu); | |
65 | } | |
66 | ||
67 | return 0; | |
68 | } | |
69 | ||
70 | static int haltpoll_cpu_offline(unsigned int cpu) | |
71 | { | |
72 | struct cpuidle_device *dev; | |
73 | ||
74 | dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu); | |
75 | if (dev->registered) { | |
76 | arch_haltpoll_disable(cpu); | |
77 | cpuidle_unregister_device(dev); | |
78 | } | |
79 | ||
80 | return 0; | |
81 | } | |
82 | ||
83 | static void haltpoll_uninit(void) | |
84 | { | |
85 | if (haltpoll_hp_state) | |
86 | cpuhp_remove_state(haltpoll_hp_state); | |
87 | cpuidle_unregister_driver(&haltpoll_driver); | |
88 | ||
89 | free_percpu(haltpoll_cpuidle_devices); | |
90 | haltpoll_cpuidle_devices = NULL; | |
91 | } | |
92 | ||
fa86ee90 MT |
93 | static int __init haltpoll_init(void) |
94 | { | |
a1c4423b | 95 | int ret; |
fa86ee90 MT |
96 | struct cpuidle_driver *drv = &haltpoll_driver; |
97 | ||
98 | cpuidle_poll_state_init(drv); | |
99 | ||
1328edca WL |
100 | if (!kvm_para_available() || |
101 | !kvm_para_has_hint(KVM_HINTS_REALTIME)) | |
5cc59f59 | 102 | return -ENODEV; |
fa86ee90 | 103 | |
97d3eb9d JM |
104 | ret = cpuidle_register_driver(drv); |
105 | if (ret < 0) | |
106 | return ret; | |
107 | ||
108 | haltpoll_cpuidle_devices = alloc_percpu(struct cpuidle_device); | |
109 | if (haltpoll_cpuidle_devices == NULL) { | |
110 | cpuidle_unregister_driver(drv); | |
111 | return -ENOMEM; | |
112 | } | |
113 | ||
114 | ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpuidle/haltpoll:online", | |
115 | haltpoll_cpu_online, haltpoll_cpu_offline); | |
116 | if (ret < 0) { | |
117 | haltpoll_uninit(); | |
118 | } else { | |
119 | haltpoll_hp_state = ret; | |
120 | ret = 0; | |
121 | } | |
a1c4423b MT |
122 | |
123 | return ret; | |
fa86ee90 MT |
124 | } |
125 | ||
126 | static void __exit haltpoll_exit(void) | |
127 | { | |
97d3eb9d | 128 | haltpoll_uninit(); |
fa86ee90 MT |
129 | } |
130 | ||
131 | module_init(haltpoll_init); | |
132 | module_exit(haltpoll_exit); | |
133 | MODULE_LICENSE("GPL"); | |
134 | MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>"); |