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