]>
Commit | Line | Data |
---|---|---|
4e43d779 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
7cb46d9b AD |
2 | /* |
3 | * Intel MIC Platform Software Stack (MPSS) | |
4 | * | |
5 | * Copyright(c) 2015 Intel Corporation. | |
6 | * | |
7cb46d9b | 7 | * Intel MIC COSM Client Driver |
7cb46d9b AD |
8 | */ |
9 | #include <linux/module.h> | |
10 | #include <linux/delay.h> | |
11 | #include <linux/reboot.h> | |
12 | #include <linux/kthread.h> | |
3f07c014 IM |
13 | #include <linux/sched/signal.h> |
14 | ||
7cb46d9b AD |
15 | #include "../cosm/cosm_main.h" |
16 | ||
17 | #define COSM_SCIF_MAX_RETRIES 10 | |
18 | #define COSM_HEARTBEAT_SEND_MSEC (COSM_HEARTBEAT_SEND_SEC * MSEC_PER_SEC) | |
19 | ||
20 | static struct task_struct *client_thread; | |
21 | static scif_epd_t client_epd; | |
22 | static struct scif_peer_dev *client_spdev; | |
23 | ||
24 | /* | |
25 | * Reboot notifier: receives shutdown status from the OS and communicates it | |
26 | * back to the COSM process on the host | |
27 | */ | |
28 | static int cosm_reboot_event(struct notifier_block *this, unsigned long event, | |
29 | void *ptr) | |
30 | { | |
31 | struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN_STATUS }; | |
32 | int rc; | |
33 | ||
34 | event = (event == SYS_RESTART) ? SYSTEM_RESTART : event; | |
35 | dev_info(&client_spdev->dev, "%s %d received event %ld\n", | |
36 | __func__, __LINE__, event); | |
37 | ||
38 | msg.shutdown_status = event; | |
39 | rc = scif_send(client_epd, &msg, sizeof(msg), SCIF_SEND_BLOCK); | |
40 | if (rc < 0) | |
41 | dev_err(&client_spdev->dev, "%s %d scif_send rc %d\n", | |
42 | __func__, __LINE__, rc); | |
43 | ||
44 | return NOTIFY_DONE; | |
45 | } | |
46 | ||
47 | static struct notifier_block cosm_reboot = { | |
48 | .notifier_call = cosm_reboot_event, | |
49 | }; | |
50 | ||
51 | /* Set system time from timespec value received from the host */ | |
52 | static void cosm_set_time(struct cosm_msg *msg) | |
53 | { | |
6051e79b AB |
54 | struct timespec64 ts = { |
55 | .tv_sec = msg->timespec.tv_sec, | |
56 | .tv_nsec = msg->timespec.tv_nsec, | |
57 | }; | |
58 | int rc = do_settimeofday64(&ts); | |
7cb46d9b AD |
59 | |
60 | if (rc) | |
61 | dev_err(&client_spdev->dev, "%s: %d settimeofday rc %d\n", | |
62 | __func__, __LINE__, rc); | |
63 | } | |
64 | ||
65 | /* COSM client receive message processing */ | |
66 | static void cosm_client_recv(void) | |
67 | { | |
68 | struct cosm_msg msg; | |
69 | int rc; | |
70 | ||
71 | while (1) { | |
72 | rc = scif_recv(client_epd, &msg, sizeof(msg), 0); | |
73 | if (!rc) { | |
74 | return; | |
75 | } else if (rc < 0) { | |
76 | dev_err(&client_spdev->dev, "%s: %d rc %d\n", | |
77 | __func__, __LINE__, rc); | |
78 | return; | |
79 | } | |
80 | ||
81 | dev_dbg(&client_spdev->dev, "%s: %d rc %d id 0x%llx\n", | |
82 | __func__, __LINE__, rc, msg.id); | |
83 | ||
84 | switch (msg.id) { | |
85 | case COSM_MSG_SYNC_TIME: | |
86 | cosm_set_time(&msg); | |
87 | break; | |
88 | case COSM_MSG_SHUTDOWN: | |
89 | orderly_poweroff(true); | |
90 | break; | |
91 | default: | |
92 | dev_err(&client_spdev->dev, "%s: %d unknown id %lld\n", | |
93 | __func__, __LINE__, msg.id); | |
94 | break; | |
95 | } | |
96 | } | |
97 | } | |
98 | ||
99 | /* Initiate connection to the COSM server on the host */ | |
100 | static int cosm_scif_connect(void) | |
101 | { | |
102 | struct scif_port_id port_id; | |
103 | int i, rc; | |
104 | ||
105 | client_epd = scif_open(); | |
106 | if (!client_epd) { | |
107 | dev_err(&client_spdev->dev, "%s %d scif_open failed\n", | |
108 | __func__, __LINE__); | |
109 | return -ENOMEM; | |
110 | } | |
111 | ||
112 | port_id.node = 0; | |
113 | port_id.port = SCIF_COSM_LISTEN_PORT; | |
114 | ||
115 | for (i = 0; i < COSM_SCIF_MAX_RETRIES; i++) { | |
116 | rc = scif_connect(client_epd, &port_id); | |
117 | if (rc < 0) | |
118 | msleep(1000); | |
119 | else | |
120 | break; | |
121 | } | |
122 | ||
123 | if (rc < 0) { | |
124 | dev_err(&client_spdev->dev, "%s %d scif_connect rc %d\n", | |
125 | __func__, __LINE__, rc); | |
126 | scif_close(client_epd); | |
127 | client_epd = NULL; | |
128 | } | |
129 | return rc < 0 ? rc : 0; | |
130 | } | |
131 | ||
132 | /* Close host SCIF connection */ | |
133 | static void cosm_scif_connect_exit(void) | |
134 | { | |
135 | if (client_epd) { | |
136 | scif_close(client_epd); | |
137 | client_epd = NULL; | |
138 | } | |
139 | } | |
140 | ||
141 | /* | |
142 | * COSM SCIF client thread function: waits for messages from the host and sends | |
143 | * a heartbeat to the host | |
144 | */ | |
145 | static int cosm_scif_client(void *unused) | |
146 | { | |
147 | struct cosm_msg msg = { .id = COSM_MSG_HEARTBEAT }; | |
148 | struct scif_pollepd pollepd; | |
149 | int rc; | |
150 | ||
151 | allow_signal(SIGKILL); | |
152 | ||
153 | while (!kthread_should_stop()) { | |
154 | pollepd.epd = client_epd; | |
a9a08845 | 155 | pollepd.events = EPOLLIN; |
7cb46d9b AD |
156 | |
157 | rc = scif_poll(&pollepd, 1, COSM_HEARTBEAT_SEND_MSEC); | |
158 | if (rc < 0) { | |
159 | if (-EINTR != rc) | |
160 | dev_err(&client_spdev->dev, | |
161 | "%s %d scif_poll rc %d\n", | |
162 | __func__, __LINE__, rc); | |
163 | continue; | |
164 | } | |
165 | ||
a9a08845 | 166 | if (pollepd.revents & EPOLLIN) |
7cb46d9b AD |
167 | cosm_client_recv(); |
168 | ||
169 | msg.id = COSM_MSG_HEARTBEAT; | |
170 | rc = scif_send(client_epd, &msg, sizeof(msg), SCIF_SEND_BLOCK); | |
171 | if (rc < 0) | |
172 | dev_err(&client_spdev->dev, "%s %d scif_send rc %d\n", | |
173 | __func__, __LINE__, rc); | |
174 | } | |
175 | ||
176 | dev_dbg(&client_spdev->dev, "%s %d Client thread stopped\n", | |
177 | __func__, __LINE__); | |
178 | return 0; | |
179 | } | |
180 | ||
181 | static void cosm_scif_probe(struct scif_peer_dev *spdev) | |
182 | { | |
183 | int rc; | |
184 | ||
185 | dev_dbg(&spdev->dev, "%s %d: dnode %d\n", | |
186 | __func__, __LINE__, spdev->dnode); | |
187 | ||
188 | /* We are only interested in the host with spdev->dnode == 0 */ | |
189 | if (spdev->dnode) | |
190 | return; | |
191 | ||
192 | client_spdev = spdev; | |
193 | rc = cosm_scif_connect(); | |
194 | if (rc) | |
195 | goto exit; | |
196 | ||
197 | rc = register_reboot_notifier(&cosm_reboot); | |
198 | if (rc) { | |
199 | dev_err(&spdev->dev, | |
200 | "reboot notifier registration failed rc %d\n", rc); | |
201 | goto connect_exit; | |
202 | } | |
203 | ||
204 | client_thread = kthread_run(cosm_scif_client, NULL, "cosm_client"); | |
205 | if (IS_ERR(client_thread)) { | |
206 | rc = PTR_ERR(client_thread); | |
207 | dev_err(&spdev->dev, "%s %d kthread_run rc %d\n", | |
208 | __func__, __LINE__, rc); | |
209 | goto unreg_reboot; | |
210 | } | |
211 | return; | |
212 | unreg_reboot: | |
213 | unregister_reboot_notifier(&cosm_reboot); | |
214 | connect_exit: | |
215 | cosm_scif_connect_exit(); | |
216 | exit: | |
217 | client_spdev = NULL; | |
218 | } | |
219 | ||
220 | static void cosm_scif_remove(struct scif_peer_dev *spdev) | |
221 | { | |
222 | int rc; | |
223 | ||
224 | dev_dbg(&spdev->dev, "%s %d: dnode %d\n", | |
225 | __func__, __LINE__, spdev->dnode); | |
226 | ||
227 | if (spdev->dnode) | |
228 | return; | |
229 | ||
230 | if (!IS_ERR_OR_NULL(client_thread)) { | |
231 | rc = send_sig(SIGKILL, client_thread, 0); | |
232 | if (rc) { | |
233 | pr_err("%s %d send_sig rc %d\n", | |
234 | __func__, __LINE__, rc); | |
235 | return; | |
236 | } | |
237 | kthread_stop(client_thread); | |
238 | } | |
239 | unregister_reboot_notifier(&cosm_reboot); | |
240 | cosm_scif_connect_exit(); | |
241 | client_spdev = NULL; | |
242 | } | |
243 | ||
244 | static struct scif_client scif_client_cosm = { | |
245 | .name = KBUILD_MODNAME, | |
246 | .probe = cosm_scif_probe, | |
247 | .remove = cosm_scif_remove, | |
248 | }; | |
249 | ||
250 | static int __init cosm_client_init(void) | |
251 | { | |
252 | int rc = scif_client_register(&scif_client_cosm); | |
253 | ||
254 | if (rc) | |
255 | pr_err("scif_client_register failed rc %d\n", rc); | |
256 | return rc; | |
257 | } | |
258 | ||
259 | static void __exit cosm_client_exit(void) | |
260 | { | |
261 | scif_client_unregister(&scif_client_cosm); | |
262 | } | |
263 | ||
264 | module_init(cosm_client_init); | |
265 | module_exit(cosm_client_exit); | |
266 | ||
267 | MODULE_AUTHOR("Intel Corporation"); | |
268 | MODULE_DESCRIPTION("Intel(R) MIC card OS state management client driver"); | |
269 | MODULE_LICENSE("GPL v2"); |