Commit | Line | Data |
---|---|---|
800f879a AT |
1 | /* |
2 | * Copyright 2010-2017 Intel Corporation. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License, version 2, | |
6 | * as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
11 | * General Public License for more details. | |
12 | * | |
13 | * Disclaimer: The codes contained in these modules may be specific to | |
14 | * the Intel Software Development Platform codenamed Knights Ferry, | |
15 | * and the Intel product codenamed Knights Corner, and are not backward | |
16 | * compatible with other Intel products. Additionally, Intel will NOT | |
17 | * support the codes or instruction set in future products. | |
18 | * | |
19 | * Intel offers no warranty of any kind regarding the code. This code is | |
20 | * licensed on an "AS IS" basis and Intel is not obligated to provide | |
21 | * any support, assistance, installation, training, or other services | |
22 | * of any kind. Intel is also not obligated to provide any updates, | |
23 | * enhancements or extensions. Intel specifically disclaims any warranty | |
24 | * of merchantability, non-infringement, fitness for any particular | |
25 | * purpose, and any other warranty. | |
26 | * | |
27 | * Further, Intel disclaims all liability of any kind, including but | |
28 | * not limited to liability for infringement of any proprietary rights, | |
29 | * relating to the use of the code, even if Intel is notified of the | |
30 | * possibility of such liability. Except as expressly stated in an Intel | |
31 | * license agreement provided with this code and agreed upon with Intel, | |
32 | * no license, express or implied, by estoppel or otherwise, to any | |
33 | * intellectual property rights is granted herein. | |
34 | */ | |
35 | ||
36 | /* SCIF Node Management */ | |
37 | ||
38 | #include "mic/micscif.h" | |
39 | #ifndef _MIC_SCIF_ | |
40 | #include "mic_common.h" | |
41 | ||
42 | #endif | |
43 | #include "mic/micscif_map.h" | |
44 | #include "mic/micscif_intr.h" | |
45 | #ifdef _MIC_SCIF_ | |
46 | extern mic_dma_handle_t mic_dma_handle; | |
47 | #else | |
48 | extern bool mic_crash_dump_enabled; | |
49 | #endif | |
50 | ||
51 | ||
52 | /** | |
53 | * micscif_create_node_dep: | |
54 | * | |
55 | * @dev: Remote SCIF device. | |
56 | * @nr_pages: number of pages* | |
57 | * | |
58 | * Increment the map SCIF device ref count and notify the host if this is the | |
59 | * first dependency being create between the two nodes. | |
60 | */ | |
61 | void | |
62 | micscif_create_node_dep(struct micscif_dev *dev, int nr_pages) | |
63 | { | |
64 | #ifdef SCIF_ENABLE_PM | |
65 | struct nodemsg notif_msg; | |
66 | ||
67 | if (dev) { | |
68 | mutex_lock(&dev->sd_lock); | |
69 | if (!dev->scif_map_ref_cnt) { | |
70 | /* Notify Host if this is the first dependency being created */ | |
71 | notif_msg.uop = SCIF_NODE_CREATE_DEP; | |
72 | notif_msg.src.node = ms_info.mi_nodeid; | |
73 | notif_msg.payload[0] = dev->sd_node; | |
74 | /* No error handling for Host SCIF device */ | |
75 | micscif_nodeqp_send(&scif_dev[SCIF_HOST_NODE], ¬if_msg, NULL); | |
76 | } | |
77 | dev->scif_map_ref_cnt += nr_pages; | |
78 | mutex_unlock(&dev->sd_lock); | |
79 | } | |
80 | #endif | |
81 | } | |
82 | ||
83 | /** | |
84 | * micscif_destroy_node_dep: | |
85 | * | |
86 | * @dev: Remote SCIF device. | |
87 | * @nr_pages: number of pages | |
88 | * | |
89 | * Decrement the map SCIF device ref count and notify the host if a dependency | |
90 | * no longer exists between two nodes. | |
91 | */ | |
92 | void | |
93 | micscif_destroy_node_dep(struct micscif_dev *dev, int nr_pages) | |
94 | { | |
95 | #ifdef SCIF_ENABLE_PM | |
96 | struct nodemsg notif_msg; | |
97 | ||
98 | if (dev) { | |
99 | mutex_lock(&dev->sd_lock); | |
100 | dev->scif_map_ref_cnt -= nr_pages; | |
101 | if (!dev->scif_map_ref_cnt) { | |
102 | /* Notify Host if all dependencies have been destroyed */ | |
103 | notif_msg.uop = SCIF_NODE_DESTROY_DEP; | |
104 | notif_msg.src.node = ms_info.mi_nodeid; | |
105 | notif_msg.payload[0] = dev->sd_node; | |
106 | /* No error handling for Host SCIF device */ | |
107 | micscif_nodeqp_send(&scif_dev[SCIF_HOST_NODE], ¬if_msg, NULL); | |
108 | } | |
109 | mutex_unlock(&dev->sd_lock); | |
110 | } | |
111 | #endif | |
112 | } | |
113 | ||
114 | /** | |
115 | * micscif_callback: | |
116 | * | |
117 | * @node: node id of the node added/removed. | |
118 | * @event_type: SCIF_NODE_ADDED if a new node is added | |
119 | * SCIF_NODE_REMOVED if a new node is removed | |
120 | * | |
121 | * Calls the callback function whenever a new node is added/removed | |
122 | */ | |
123 | static void micscif_callback(uint16_t node, enum scif_event_type event_type) | |
124 | { | |
125 | struct list_head *pos; | |
126 | struct scif_callback *temp; | |
127 | union eventd event; | |
128 | ||
129 | switch (event_type) { | |
130 | case SCIF_NODE_ADDED: | |
131 | event.scif_node_added = node; | |
132 | break; | |
133 | case SCIF_NODE_REMOVED: | |
134 | event.scif_node_removed = node; | |
135 | break; | |
136 | default: | |
137 | return; | |
138 | } | |
139 | ||
140 | mutex_lock(&ms_info.mi_event_cblock); | |
141 | list_for_each(pos, &ms_info.mi_event_cb) { | |
142 | temp = list_entry(pos, struct scif_callback, list_member); | |
143 | temp->callback_handler(event_type, event); | |
144 | } | |
145 | mutex_unlock(&ms_info.mi_event_cblock); | |
146 | } | |
147 | ||
148 | /** | |
149 | * micscif_node_remove_callback: | |
150 | * | |
151 | * @node: node id of the node removed. | |
152 | * | |
153 | * Calls the callback function whenever a new node is removed | |
154 | */ | |
155 | static void micscif_node_remove_callback(int node) | |
156 | { | |
157 | micscif_callback((uint16_t)node, SCIF_NODE_REMOVED); | |
158 | } | |
159 | ||
160 | /** | |
161 | * micscif_node_add_callback: | |
162 | * | |
163 | * @node: node id of the node added. | |
164 | * | |
165 | * Calls the callback function whenever a new node is added | |
166 | */ | |
167 | void micscif_node_add_callback(int node) | |
168 | { | |
169 | micscif_callback((uint16_t)node, SCIF_NODE_ADDED); | |
170 | } | |
171 | ||
172 | void micscif_cleanup_qp(struct micscif_dev *dev) | |
173 | { | |
174 | struct micscif_qp *qp; | |
175 | ||
176 | qp = &dev->qpairs[0]; | |
177 | ||
178 | if (!qp) | |
179 | return; | |
180 | ||
181 | scif_iounmap((void*)qp->remote_qp, sizeof(struct micscif_qp), dev); | |
182 | scif_iounmap((void*)dev->qpairs[0].outbound_q.rb_base, sizeof(struct micscif_qp), dev); | |
183 | qp->remote_qp = NULL; | |
184 | dev->qpairs[0].local_write = 0; | |
185 | dev->qpairs[0].inbound_q.current_write_offset = 0; | |
186 | dev->qpairs[0].inbound_q.current_read_offset = 0; | |
187 | #ifdef _MIC_SCIF_ | |
188 | kfree((void*)(qp->inbound_q.rb_base)); | |
189 | kfree(dev->qpairs); | |
190 | qp = NULL; | |
191 | #endif | |
192 | } | |
193 | ||
194 | /* | |
195 | * micscif_cleanup_scifdev | |
196 | * | |
197 | * @dev: Remote SCIF device. | |
198 | * Uninitialize SCIF data structures for remote SCIF device. | |
199 | */ | |
200 | void micscif_cleanup_scifdev(struct micscif_dev *dev, bool destroy_wq) | |
201 | { | |
202 | int64_t ret; | |
203 | #ifndef _MIC_SCIF_ | |
204 | mic_ctx_t *mic_ctx; | |
205 | #endif | |
206 | if (SCIFDEV_NOTPRESENT == dev->sd_state) { | |
207 | #ifdef _MIC_SCIF_ | |
208 | /* | |
209 | * If there are any stale qp allocated due to | |
210 | * p2p connection failures then cleanup now | |
211 | */ | |
212 | micscif_cleanup_qp(dev); | |
213 | #endif | |
214 | return; | |
215 | } | |
216 | ||
217 | dev->sd_wait_status = OP_FAILED; | |
218 | wake_up(&dev->sd_wq); | |
219 | ||
220 | #ifdef _MIC_SCIF_ | |
221 | /* | |
222 | * Need to protect destruction of the workqueue since this code | |
223 | * can be called from two contexts: | |
224 | * a) Remove Node Handling. | |
225 | * b) SCIF driver unload | |
226 | */ | |
227 | mutex_lock(&dev->sd_lock); | |
228 | if ((SCIFDEV_RUNNING != dev->sd_state) && (SCIFDEV_SLEEPING != dev->sd_state)) | |
229 | goto unlock; | |
230 | dev->sd_state = SCIFDEV_STOPPED; | |
231 | wake_up(&dev->sd_p2p_wq); | |
232 | mutex_unlock(&dev->sd_lock); | |
233 | deregister_scif_intr_handler(dev); | |
234 | if (destroy_wq && dev->sd_intr_wq) { | |
235 | destroy_workqueue(dev->sd_intr_wq); | |
236 | dev->sd_intr_wq = NULL; | |
237 | } | |
238 | #endif | |
239 | ||
240 | mutex_lock(&dev->sd_lock); | |
241 | #ifndef _MIC_SCIF_ | |
242 | if ((SCIFDEV_RUNNING != dev->sd_state) && (SCIFDEV_SLEEPING != dev->sd_state)) | |
243 | goto unlock; | |
244 | dev->sd_state = SCIFDEV_STOPPED; | |
245 | #endif | |
246 | /* | |
247 | * Change the state of the remote SCIF device | |
248 | * to idle as soon as the activity counter is | |
249 | * zero. The node state and ref count is | |
250 | * maintained within a single atomic_long_t. | |
251 | * No timeout for this tight loop since we expect | |
252 | * the node to complete the API it is currently | |
253 | * executing following which the scif_ref_count | |
254 | * will drop to zero. | |
255 | */ | |
256 | do { | |
257 | ret = atomic_long_cmpxchg( | |
258 | &dev->scif_ref_cnt, 0, SCIF_NODE_IDLE); | |
259 | cpu_relax(); | |
260 | } while (ret && ret != SCIF_NODE_IDLE); | |
261 | ||
262 | mutex_unlock(&dev->sd_lock); | |
263 | /* Cleanup temporary registered windows */ | |
264 | flush_workqueue(ms_info.mi_misc_wq); | |
265 | mutex_lock(&dev->sd_lock); | |
266 | ||
267 | #ifdef _MIC_SCIF_ | |
268 | drain_dma_global(mic_dma_handle); | |
269 | #else | |
270 | mic_ctx = get_per_dev_ctx(dev->sd_node - 1); | |
271 | drain_dma_global(mic_ctx->dma_handle); | |
272 | micscif_destroy_p2p(mic_ctx); | |
273 | #endif | |
274 | scif_invalidate_ep(dev->sd_node); | |
275 | micscif_kill_apps_with_mmaps(dev->sd_node); | |
276 | ||
277 | micscif_cleanup_qp(dev); | |
278 | mutex_unlock(&dev->sd_lock); | |
279 | #ifndef _MIC_SCIF_ | |
280 | mutex_lock(&ms_info.mi_conflock); | |
281 | ms_info.mi_mask &= ~(0x1 << dev->sd_node); | |
282 | ms_info.mi_total--; | |
283 | mutex_unlock(&ms_info.mi_conflock); | |
284 | #endif | |
285 | ||
286 | /* Wait for all applications to unmap remote memory mappings. */ | |
287 | wait_event(dev->sd_mmap_wq, | |
288 | !micscif_rma_do_apps_have_mmaps(dev->sd_node)); | |
289 | micscif_cleanup_rma_for_zombies(dev->sd_node); | |
290 | micscif_node_remove_callback(dev->sd_node); | |
291 | return; | |
292 | unlock: | |
293 | mutex_unlock(&dev->sd_lock); | |
294 | } | |
295 | ||
296 | /* | |
297 | * micscif_remove_node: | |
298 | * | |
299 | * @mask: bitmask of nodes in the deactivation set. | |
300 | * @flags: Type of deactivation set i.e. Power Management, | |
301 | * RAS, Maintenance Mode etc. | |
302 | * @block: Can block. | |
303 | * | |
304 | * Attempt to deactivate a set of remote SCIF devices nodes passed in mask. | |
305 | * If the SCIF activity ref count is positive for a remote node then | |
306 | * the approporiate bit in the input bitmask is reset and the resultant | |
307 | * bitmask is returned. | |
308 | */ | |
309 | uint64_t micscif_handle_remove_node(uint64_t mask, uint64_t payload) | |
310 | { | |
311 | int64_t ret; | |
312 | int err = 0; | |
313 | uint32_t i; | |
314 | struct micscif_dev *dev; | |
315 | uint64_t flags = 0; | |
316 | flags = payload & 0x00000000FFFFFFFF; | |
317 | ||
318 | switch(flags) { | |
319 | case DISCONN_TYPE_POWER_MGMT: | |
320 | { | |
321 | uint8_t *nodemask_buf = NULL; | |
322 | int size = payload >> 32; | |
323 | ||
324 | #ifndef _MIC_SCIF_ | |
325 | nodemask_buf = mic_data.dd_pm.nodemask; | |
326 | #else | |
327 | nodemask_buf = scif_ioremap(mask, size, &scif_dev[SCIF_HOST_NODE]); | |
328 | #endif | |
329 | if (!nodemask_buf) { | |
330 | err = EAGAIN; | |
331 | break; | |
332 | } | |
333 | ||
334 | for (i = 0; i <= ms_info.mi_maxid; i++) { | |
335 | dev = &scif_dev[i]; | |
336 | if (!get_nodemask_bit(nodemask_buf , i)) | |
337 | continue; | |
338 | /* | |
339 | * Try for the SCIF device lock. Bail out if | |
340 | * it is already grabbed since some other | |
341 | * thread is already working on some other | |
342 | * node state transition for this remote SCIF device. | |
343 | */ | |
344 | if (mutex_trylock(&dev->sd_lock)) { | |
345 | ||
346 | if (SCIFDEV_RUNNING != dev->sd_state) { | |
347 | mutex_unlock(&dev->sd_lock); | |
348 | continue; | |
349 | } | |
350 | /* | |
351 | * Change the state of the remote SCIF device | |
352 | * to idle only if the activity counter is | |
353 | * already zero. The node state and ref count | |
354 | * is maintained within a single atomic_long_t. | |
355 | */ | |
356 | ret = atomic_long_cmpxchg( | |
357 | &dev->scif_ref_cnt, 0, SCIF_NODE_IDLE); | |
358 | ||
359 | if (!ret || ret == SCIF_NODE_IDLE) { | |
360 | if (!ret) { | |
361 | #ifdef _MIC_SCIF_ | |
362 | drain_dma_global(mic_dma_handle); | |
363 | #else | |
364 | mic_ctx_t *mic_ctx = get_per_dev_ctx(dev->sd_node - 1); | |
365 | drain_dma_global(mic_ctx->dma_handle); | |
366 | #endif | |
367 | } | |
368 | /* | |
369 | * Turn off the remote SCIF device. | |
370 | * Any communication to this SCIF | |
371 | * after this point will require a | |
372 | * wake up message to the host. | |
373 | */ | |
374 | dev->sd_state = SCIFDEV_SLEEPING; | |
375 | err = 0; | |
376 | } | |
377 | else { | |
378 | /* | |
379 | * Cannot put the remote SCIF device | |
380 | * to sleep. | |
381 | */ | |
382 | err = EAGAIN; | |
383 | mutex_unlock(&dev->sd_lock); | |
384 | break; | |
385 | } | |
386 | mutex_unlock(&dev->sd_lock); | |
387 | } else { | |
388 | err = EAGAIN; | |
389 | break; | |
390 | } | |
391 | } | |
392 | ||
393 | #ifndef _MIC_SCIF_ | |
394 | scif_iounmap(nodemask_buf, size, &scif_dev[SCIF_HOST_NODE]); | |
395 | #endif | |
396 | ||
397 | break; | |
398 | } | |
399 | case DISCONN_TYPE_LOST_NODE: | |
400 | { | |
401 | /* In the case of lost node, first paramater | |
402 | * is the node id and not a mask. | |
403 | */ | |
404 | dev = &scif_dev[mask]; | |
405 | micscif_cleanup_scifdev(dev, !DESTROY_WQ); | |
406 | break; | |
407 | } | |
408 | default: | |
409 | { | |
410 | /* Unknown remove node flags */ | |
411 | BUG_ON(1); | |
412 | } | |
413 | } | |
414 | ||
415 | return err; | |
416 | } | |
417 | ||
418 | /** | |
419 | * set_nodemask_bit: | |
420 | * | |
421 | * @node_id[in]: node id to be set in the mask | |
422 | * | |
423 | * Set bit in the nodemask. each bit represents node. set bit to add node in to | |
424 | * activation/de-activation set | |
425 | */ | |
426 | //void | |
427 | //set_nodemask_bit(uint64_t *nodemask, uint32_t node_id) | |
428 | void | |
429 | set_nodemask_bit(uint8_t* nodemask, uint32_t node_id, int val) | |
430 | { | |
431 | int index = 0; | |
432 | uint8_t *temp_mask; | |
433 | ||
434 | index = (int) node_id / 8; | |
435 | temp_mask = nodemask + index; | |
436 | node_id = node_id - (index * 8); | |
437 | if (val) | |
438 | *temp_mask |= (1ULL << node_id); | |
439 | else | |
440 | *temp_mask &= ~(1ULL << node_id); | |
441 | } | |
442 | ||
443 | /** | |
444 | * check_nodemask_bit: | |
445 | * | |
446 | * @node_id[in]: node id to be set in the mask | |
447 | * | |
448 | * Check if a bit in the nodemask corresponding to a | |
449 | * node id is set. | |
450 | * | |
451 | * return 1 if the bit is set. 0 if the bit is cleared. | |
452 | */ | |
453 | int | |
454 | get_nodemask_bit(uint8_t* nodemask, uint32_t node_id) { | |
455 | int index = 0; | |
456 | uint8_t *temp_mask; | |
457 | ||
458 | index = (int) node_id / 8; | |
459 | temp_mask = nodemask + index; | |
460 | node_id = node_id - (index * 8); | |
461 | return *temp_mask & (1ULL << node_id); | |
462 | ||
463 | } | |
464 | /** | |
465 | * nodemask_isvalid - Check if a nodemask is valid after | |
466 | * calculating the de-activation set. | |
467 | * | |
468 | * @nodemask[in]: The nodemask to be checked. | |
469 | * | |
470 | * Returns true if valid. | |
471 | */ | |
472 | bool nodemask_isvalid(uint8_t* nodemask) { | |
473 | uint32_t i; | |
474 | for (i = 0; i <= ms_info.mi_maxid; i++) { | |
475 | if (get_nodemask_bit(nodemask, i)) | |
476 | return true; | |
477 | } | |
478 | ||
479 | return false; | |
480 | } | |
481 | ||
482 | #ifndef _MIC_SCIF_ | |
483 | /* | |
484 | * micscif_send_rmnode_msg: | |
485 | * | |
486 | * @mask: Bitmask of nodes in the deactivation set. | |
487 | * @node: Destination node for a deactivation set. | |
488 | * @flags: Type of deactivation set i.e. Power Management, | |
489 | * RAS, Maintenance Mode etc. | |
490 | * @orig_node: The node which triggered this remove node message. | |
491 | * | |
492 | * Sends a deactivation request to the valid nodes not included in the | |
493 | * deactivation set from the Host and waits for a response. | |
494 | * Returns the response mask received from the node. | |
495 | */ | |
496 | uint64_t micscif_send_pm_rmnode_msg(int node, uint64_t nodemask_addr, | |
497 | uint64_t nodemask_size, int orig_node) { | |
498 | ||
499 | uint64_t ret; | |
500 | struct nodemsg notif_msg; | |
501 | struct micscif_dev *dev = &scif_dev[node]; | |
502 | ||
503 | /* | |
504 | * Send remove node msg only to running nodes. | |
505 | * An idle node need not know about another _lost_ node | |
506 | * until it wakes up. When it does, it will request the | |
507 | * host to wake up the _lost_ node to which the host will | |
508 | * respond with a NACK | |
509 | */ | |
510 | ||
511 | if (SCIFDEV_RUNNING != dev->sd_state) | |
512 | return -ENODEV; | |
513 | ||
514 | notif_msg.uop = SCIF_NODE_REMOVE; | |
515 | notif_msg.src.node = ms_info.mi_nodeid; | |
516 | notif_msg.dst.node = node; | |
517 | notif_msg.payload[0] = nodemask_addr; | |
518 | notif_msg.payload[1] = DISCONN_TYPE_POWER_MGMT; | |
519 | notif_msg.payload[1] |= (nodemask_size << 32); | |
520 | notif_msg.payload[2] = atomic_long_read(&ms_info.mi_unique_msgid); | |
521 | notif_msg.payload[3] = orig_node; | |
522 | /* Send the request to remove a set of nodes */ | |
523 | pr_debug("Send PM rmnode msg for node %d to node %d\n", orig_node, node); | |
524 | ret = micscif_nodeqp_send(dev, ¬if_msg, NULL); | |
525 | ||
526 | return ret; | |
527 | } | |
528 | ||
529 | uint64_t micscif_send_lost_node_rmnode_msg(int node, int orig_node) { | |
530 | uint64_t ret; | |
531 | struct nodemsg notif_msg; | |
532 | struct micscif_dev *dev = &scif_dev[node]; | |
533 | ||
534 | /* | |
535 | * Send remove node msg only to running nodes. | |
536 | * An idle node need not know about another _lost_ node | |
537 | * until it wakes up. When it does, it will request the | |
538 | * host to wake up the _lost_ node to which the host will | |
539 | * respond with a NACK | |
540 | */ | |
541 | if (SCIFDEV_RUNNING != dev->sd_state) | |
542 | return -ENODEV; | |
543 | ||
544 | micscif_inc_node_refcnt(dev, 1); | |
545 | notif_msg.uop = SCIF_NODE_REMOVE; | |
546 | notif_msg.src.node = ms_info.mi_nodeid; | |
547 | notif_msg.dst.node = node; | |
548 | notif_msg.payload[0] = orig_node; | |
549 | notif_msg.payload[1] = DISCONN_TYPE_LOST_NODE; | |
550 | notif_msg.payload[3] = orig_node; | |
551 | /* Send the request to remove a set of nodes */ | |
552 | ret = micscif_nodeqp_send(dev, ¬if_msg, NULL); | |
553 | micscif_dec_node_refcnt(dev, 1); | |
554 | ||
555 | return ret; | |
556 | } | |
557 | ||
558 | /* | |
559 | * micpm_nodemask_uninit: | |
560 | * @node - node to uninitalize | |
561 | * | |
562 | * Deallocate memory for per-card nodemask buffer | |
563 | */ | |
564 | void | |
565 | micpm_nodemask_uninit(mic_ctx_t* mic_ctx) | |
566 | { | |
567 | if (mic_ctx && mic_ctx->micpm_ctx.nodemask.va) { | |
568 | mic_ctx_unmap_single(mic_ctx, mic_ctx->micpm_ctx.nodemask.pa, | |
569 | mic_ctx->micpm_ctx.nodemask.len); | |
570 | kfree(mic_ctx->micpm_ctx.nodemask.va); | |
571 | } | |
572 | } | |
573 | ||
574 | /* | |
575 | * micpm_nodemask_init: | |
576 | * @num_devs - no of scif nodes including the host | |
577 | * @node - node to initialize | |
578 | * | |
579 | * Allocate memory for per-card nodemask buffer | |
580 | */ | |
581 | int | |
582 | micpm_nodemask_init(uint32_t num_devs, mic_ctx_t* mic_ctx) | |
583 | { | |
584 | if (!mic_ctx) | |
585 | return 0; | |
586 | ||
587 | mic_ctx->micpm_ctx.nodemask.len = ((int) (num_devs / 8) + | |
588 | ((num_devs % 8) ? 1 : 0)); | |
589 | mic_ctx->micpm_ctx.nodemask.va = (uint8_t *) | |
590 | kzalloc(mic_ctx->micpm_ctx.nodemask.len, GFP_KERNEL); | |
591 | ||
592 | if (!mic_ctx->micpm_ctx.nodemask.va) { | |
593 | PM_DEBUG("Error allocating nodemask buffer\n"); | |
594 | return -ENOMEM; | |
595 | } | |
596 | ||
597 | mic_ctx->micpm_ctx.nodemask.pa = mic_ctx_map_single(mic_ctx, | |
598 | mic_ctx->micpm_ctx.nodemask.va, | |
599 | mic_ctx->micpm_ctx.nodemask.len); | |
600 | ||
601 | if(mic_map_error(mic_ctx->micpm_ctx.nodemask.pa)) { | |
602 | PM_PRINT("Error Mapping nodemask buffer\n"); | |
603 | kfree(mic_ctx->micpm_ctx.nodemask.va); | |
604 | } | |
605 | return 0; | |
606 | } | |
607 | ||
608 | /** | |
609 | * micpm_disconn_uninit: | |
610 | * @num_devs - no of scif nodes including host | |
611 | * Note - can not use ms_info.mi_total(total no of scif nodes) as it is updated after the driver load is complete | |
612 | * | |
613 | * Reset/re-initialize data structures needed for PM disconnection. This is necessary everytime the board is reset. | |
614 | * Since host(node 0)represents one of the node in network, it is necessary to clear dependency of host with the given node | |
615 | */ | |
616 | int | |
617 | micpm_disconn_uninit(uint32_t num_devs) | |
618 | { | |
619 | uint32_t i; | |
620 | uint32_t status = 0; | |
621 | ||
622 | /* | |
623 | * ms_info.mi_total is updated after the driver load is complete | |
624 | * switching back to static allocation of max nodes | |
625 | */ | |
626 | ||
627 | if (ms_info.mi_depmtrx) { | |
628 | ||
629 | for (i = 0; i < (int)num_devs; i++) { | |
630 | if (ms_info.mi_depmtrx[i]) { | |
631 | kfree(ms_info.mi_depmtrx[i]); | |
632 | } | |
633 | } | |
634 | kfree(ms_info.mi_depmtrx); | |
635 | } | |
636 | ||
637 | if (mic_data.dd_pm.nodemask) | |
638 | kfree(mic_data.dd_pm.nodemask); | |
639 | ||
640 | return status; | |
641 | } | |
642 | ||
643 | /** | |
644 | * micpm_disconn_init: | |
645 | * @num_devs - no of scif nodes including host | |
646 | * Note - can not use ms_info.mi_total(total no of scif nodes) as it is updated after the driver load is complete | |
647 | * | |
648 | * Allocate memory for dependency graph. Initialize dependencies for the node. | |
649 | * The memory allocated is based on the no of devices present during driver load. | |
650 | */ | |
651 | int | |
652 | micpm_disconn_init(uint32_t num_devs) | |
653 | { | |
654 | uint32_t i; | |
655 | uint32_t status = 0; | |
656 | mic_ctx_t *mic_ctx; | |
657 | ||
658 | if (ms_info.mi_depmtrx) | |
659 | return status; | |
660 | ||
661 | ms_info.mi_depmtrx = (uint32_t**)kzalloc(sizeof(uint32_t*) * num_devs, GFP_KERNEL); | |
662 | if (!ms_info.mi_depmtrx) { | |
663 | pr_debug("dependency graph initialization failed\n"); | |
664 | status = -ENOMEM; | |
665 | goto exit; | |
666 | } | |
667 | ||
668 | for (i = 0; i < (int)num_devs; i++) { | |
669 | ms_info.mi_depmtrx[i] = (uint32_t*)kzalloc(sizeof(uint32_t) * num_devs, GFP_KERNEL); | |
670 | if (!ms_info.mi_depmtrx[i]) { | |
671 | micpm_disconn_uninit(num_devs); | |
672 | pr_debug("dependency graph initialization failed\n"); | |
673 | status = -ENOMEM; | |
674 | goto exit; | |
675 | } | |
676 | } | |
677 | init_waitqueue_head(&ms_info.mi_disconn_wq); | |
678 | atomic_long_set(&ms_info.mi_unique_msgid, 0); | |
679 | ||
680 | //In Windows, this code is executed during micpm_probe | |
681 | for(i = 0; i < (num_devs - 1); i++) { | |
682 | mic_ctx = get_per_dev_ctx(i); | |
683 | status = micpm_nodemask_init(num_devs, mic_ctx); | |
684 | if (status) | |
685 | goto exit; | |
686 | } | |
687 | ||
688 | /* Set up a nodemask buffer for Host scif node in a common pm_ctx */ | |
689 | mic_data.dd_pm.nodemask_len = ((int) (num_devs / 8) + | |
690 | ((num_devs % 8) ? 1 : 0)); | |
691 | mic_data.dd_pm.nodemask = (uint8_t *) | |
692 | kzalloc(mic_data.dd_pm.nodemask_len, GFP_KERNEL); | |
693 | ||
694 | if (!mic_data.dd_pm.nodemask) { | |
695 | PM_DEBUG("Error allocating nodemask buffer\n"); | |
696 | status = -ENOMEM; | |
697 | goto exit; | |
698 | } | |
699 | ||
700 | exit: | |
701 | return status; | |
702 | } | |
703 | ||
704 | /** | |
705 | * micscif_set_nodedep: | |
706 | * | |
707 | * @src_node: node which is creating dependency. | |
708 | * @dst_node: node on which dependency is being created | |
709 | * | |
710 | * sets the given value in dependency graph for src_node -> dst_node | |
711 | */ | |
712 | void | |
713 | micscif_set_nodedep(uint32_t src_node, uint32_t dst_node, enum dependency_state state) | |
714 | { | |
715 | /* We dont need to lock dependency graph while updating | |
716 | * as every node will modify its own row | |
717 | */ | |
718 | if (ms_info.mi_depmtrx) | |
719 | ms_info.mi_depmtrx[src_node][dst_node] = state; | |
720 | } | |
721 | ||
722 | /** | |
723 | * micscif_get_nodedep: | |
724 | * | |
725 | * @src_node: node which has/has not created dependency. | |
726 | * @dst_node: node on which dependency was/was not created | |
727 | * | |
728 | * gets the current value in dependency graph for src_node -> dst_node | |
729 | */ | |
730 | enum dependency_state | |
731 | micscif_get_nodedep(uint32_t src_node, uint32_t dst_node) | |
732 | { | |
733 | enum dependency_state state = DEP_STATE_NOT_DEPENDENT; | |
734 | if (ms_info.mi_depmtrx) | |
735 | state = ms_info.mi_depmtrx[src_node][dst_node]; | |
736 | return state; | |
737 | } | |
738 | ||
739 | /** | |
740 | * init_depgraph_stack: | |
741 | * | |
742 | * @stack_ptr: list head. | |
743 | * | |
744 | * Initialize linked list to be used as stack | |
745 | */ | |
746 | int | |
747 | init_depgraph_stack(struct list_head *stack_ptr) | |
748 | { | |
749 | int status = 0; | |
750 | ||
751 | if (!stack_ptr) { | |
752 | pr_debug("%s argument stack_ptr is invalid\n", __func__); | |
753 | status = -EINVAL; | |
754 | goto exit; | |
755 | } | |
756 | /* Initialize stack */ | |
757 | INIT_LIST_HEAD(stack_ptr); | |
758 | ||
759 | exit: | |
760 | return status; | |
761 | } | |
762 | ||
763 | /** | |
764 | * uninit_depgraph_stack: | |
765 | * | |
766 | * @stack_ptr: list head for linked list(stack). | |
767 | * | |
768 | * Empty stack(linked list). Pop all the nodes left in the stack. | |
769 | */ | |
770 | int | |
771 | uninit_depgraph_stack(struct list_head *stack_ptr) | |
772 | { | |
773 | int status = 0; | |
774 | uint32_t node_id; | |
775 | if (!stack_ptr) { | |
776 | pr_debug("%s argument stack_ptr is invalid\n", __func__); | |
777 | status = -EINVAL; | |
778 | goto exit; | |
779 | } | |
780 | ||
781 | /* pop all the nodes left in the stack */ | |
782 | while (!is_stack_empty(stack_ptr)) { | |
783 | status = stack_pop_node(stack_ptr, &node_id); | |
784 | if (status) { | |
785 | pr_debug("%s error while cleaning up depgraph stack\n", __func__); | |
786 | status = -EINVAL; | |
787 | goto exit; | |
788 | } | |
789 | } | |
790 | ||
791 | exit: | |
792 | return status; | |
793 | } | |
794 | ||
795 | /** | |
796 | * is_stack_empty: | |
797 | * | |
798 | * @stack_ptr: list head for linked list(stack). | |
799 | * | |
800 | * returns true if the stack is empty. | |
801 | */ | |
802 | int | |
803 | is_stack_empty(struct list_head *stack_ptr) | |
804 | { | |
805 | if(list_empty(stack_ptr)) { | |
806 | return 1; | |
807 | } | |
808 | return 0; | |
809 | } | |
810 | ||
811 | /** | |
812 | * stack_push_node: | |
813 | * | |
814 | * @stack_ptr[in]: list head for linked list(stack). | |
815 | * @node_id[in]: node id to be pushed | |
816 | * | |
817 | * Push node in to the stack i.e. create node and add it at the start of linked list | |
818 | */ | |
819 | int | |
820 | stack_push_node(struct list_head *stack_ptr, uint32_t node_id) | |
821 | { | |
822 | int status = 0; | |
823 | struct stack_node *datanode = NULL; | |
824 | ||
825 | datanode = kmalloc(sizeof(struct stack_node), GFP_KERNEL); | |
826 | if (!datanode) { | |
827 | pr_debug("%s error allocating memory to stack node.\n", __func__); | |
828 | status = -ENOMEM; | |
829 | goto exit; | |
830 | } | |
831 | ||
832 | datanode->node_id = node_id; | |
833 | list_add(&datanode->next, stack_ptr); | |
834 | exit: | |
835 | return status; | |
836 | } | |
837 | ||
838 | /** | |
839 | * stack_pop_node: | |
840 | * | |
841 | * @stack_ptr[in]: list head for linked list(stack). | |
842 | * @node_id[out]: pointer to the node id to be popped | |
843 | * | |
844 | * Pop node from the stack i.e. delete first entry of linked list and return its data. | |
845 | */ | |
846 | int | |
847 | stack_pop_node(struct list_head *stack_ptr, uint32_t *node_id) | |
848 | { | |
849 | int status = 0; | |
850 | struct stack_node *datanode = NULL; | |
851 | ||
852 | if(is_stack_empty(stack_ptr)) { | |
853 | pr_debug("%s stack found empty when tried to pop\n", __func__); | |
854 | status = -EFAULT; | |
855 | goto exit; | |
856 | } | |
857 | ||
858 | datanode = list_first_entry(stack_ptr, struct stack_node, next); | |
859 | if (!datanode) { | |
860 | pr_debug("%s Unable to pop from stack\n", __func__); | |
861 | status = -EFAULT; | |
862 | goto exit; | |
863 | } | |
864 | *node_id = datanode->node_id; | |
865 | ||
866 | list_del(&datanode->next); | |
867 | if (datanode) { | |
868 | kfree(datanode); | |
869 | } | |
870 | ||
871 | exit: | |
872 | return status; | |
873 | } | |
874 | ||
875 | /** | |
876 | * micscif_get_activeset: | |
877 | * | |
878 | * @node_id[in]: source node id. | |
879 | * @nodemask[out]: bitmask of nodes present in activation set | |
880 | * | |
881 | * Algorithm to find out activation set for the given source node. Activation set is used to re-connect node into | |
882 | * the scif network. | |
883 | */ | |
884 | int | |
885 | micscif_get_activeset(uint32_t node_id, uint8_t *nodemask) | |
886 | { | |
887 | int status = 0; | |
888 | uint32_t i = 0; | |
889 | struct list_head stack; | |
890 | uint8_t visited[128] = {0}; // 128 is max number of nodes. | |
891 | uint32_t num_nodes = ms_info.mi_maxid + 1; | |
892 | mic_ctx_t *mic_ctx; | |
893 | ||
894 | if (!ms_info.mi_depmtrx) { | |
895 | status = -EINVAL; | |
896 | goto exit; | |
897 | } | |
898 | ||
899 | status = init_depgraph_stack(&stack); | |
900 | if (status) { | |
901 | pr_debug("%s failed to initilize depgraph stack\n", __func__); | |
902 | goto exit; | |
903 | } | |
904 | ||
905 | status = stack_push_node(&stack, node_id); | |
906 | if (status) { | |
907 | pr_debug("%s error while running activation set algorithm\n", __func__); | |
908 | goto exit; | |
909 | } | |
910 | ||
911 | /* mark node visited to avoid repetition of the algorithm for the same node */ | |
912 | visited[node_id] = 1; | |
913 | ||
914 | while (!is_stack_empty(&stack)) { | |
915 | status = stack_pop_node(&stack, &node_id); | |
916 | if (status) { | |
917 | pr_debug("%s error while running activation set algorithm\n", __func__); | |
918 | goto exit; | |
919 | } | |
920 | ||
921 | /* include node_id in the activation set*/ | |
922 | set_nodemask_bit(nodemask, node_id, 1); | |
923 | ||
924 | for (i = 0; i < num_nodes; i++) { | |
925 | /* check if node has dependency on any node 'i' which is also disconnected at this time*/ | |
926 | if ((!visited[i]) && (ms_info.mi_depmtrx[node_id][i] == DEP_STATE_DISCONNECTED)) { | |
927 | visited[i] = 1; | |
928 | if (i == 0) | |
929 | continue; | |
930 | mic_ctx = get_per_dev_ctx(i - 1); | |
931 | if ((mic_ctx->micpm_ctx.idle_state == PM_IDLE_STATE_PC3) || | |
932 | (mic_ctx->micpm_ctx.idle_state == PM_IDLE_STATE_PC6)) { | |
933 | status = stack_push_node(&stack, i); | |
934 | if (status) { | |
935 | pr_debug("%s error while running activation set algorithm\n", __func__); | |
936 | goto exit; | |
937 | } | |
938 | } | |
939 | } | |
940 | } | |
941 | } /* end of while (!is_stack_empty(&stack)) */ | |
942 | exit: | |
943 | uninit_depgraph_stack(&stack); | |
944 | return status; | |
945 | } | |
946 | ||
947 | /** | |
948 | * micscif_get_minimal_deactiveset: | |
949 | * | |
950 | * @node_id[in]: source node id. | |
951 | * @nodemask[out]: bitmask of nodes present in de-activation set | |
952 | * @visited[in/out]: information of which nodes are already visited in de-activation set algorithm | |
953 | * | |
954 | * Algorithm to find out minimum/must de-activation set for the given source node. This method is part of and used by | |
955 | * micscif_get_deactiveset. | |
956 | */ | |
957 | int micscif_get_minimal_deactiveset(uint32_t node_id, uint8_t *nodemask, uint8_t *visited) | |
958 | { | |
959 | int status = 0; | |
960 | uint32_t i = 0; | |
961 | struct list_head stack; | |
962 | uint32_t num_nodes = ms_info.mi_maxid + 1; | |
963 | ||
964 | if (!ms_info.mi_depmtrx) { | |
965 | status = -EINVAL; | |
966 | goto exit; | |
967 | } | |
968 | ||
969 | status = init_depgraph_stack(&stack); | |
970 | if (!visited) { | |
971 | pr_debug("%s invalid parameter visited", __func__); | |
972 | status = -EINVAL; | |
973 | goto exit_pop; | |
974 | } | |
975 | ||
976 | if (status) { | |
977 | pr_debug("%s failed to initilize depgraph stack\n", __func__); | |
978 | goto exit_pop; | |
979 | } | |
980 | ||
981 | status = stack_push_node(&stack, node_id); | |
982 | if (status) { | |
983 | pr_debug("%s error while running de-activation set algorithm\n", __func__); | |
984 | goto exit_pop; | |
985 | } | |
986 | ||
987 | /* mark node visited to avoid repetition of the algorithm for the same node */ | |
988 | visited[node_id] = 1; | |
989 | ||
990 | while (!is_stack_empty(&stack)) { | |
991 | ||
992 | status = stack_pop_node(&stack, &node_id); | |
993 | if (status) { | |
994 | pr_debug("%s error while running de-activation set algorithm\n", __func__); | |
995 | goto exit_pop; | |
996 | } | |
997 | ||
998 | /* include node_id in the activation set*/ | |
999 | set_nodemask_bit(nodemask, node_id, 1); | |
1000 | ||
1001 | for (i = 0; i < num_nodes; i++) { | |
1002 | if (!visited[i]) { | |
1003 | if (ms_info.mi_depmtrx[i][node_id] == DEP_STATE_DEPENDENT) { | |
1004 | /* The algorithm terminates, if we find any dependent node active */ | |
1005 | status = -EOPNOTSUPP; | |
1006 | goto exit_pop; | |
1007 | } else if(ms_info.mi_depmtrx[i][node_id] == DEP_STATE_DISCONNECT_READY) { | |
1008 | /* node is dependent but ready to get disconnected */ | |
1009 | visited[i] = 1; | |
1010 | status = stack_push_node(&stack, i); | |
1011 | if (status) { | |
1012 | pr_debug("%s error while running de-activation set algorithm\n", __func__); | |
1013 | goto exit_pop; | |
1014 | } | |
1015 | } | |
1016 | } | |
1017 | } | |
1018 | }/*end of while(!is_stack_empty(&stack))*/ | |
1019 | ||
1020 | exit_pop: | |
1021 | while (!is_stack_empty(&stack)) { | |
1022 | status = stack_pop_node(&stack, &node_id); | |
1023 | if (status) { | |
1024 | pr_debug("%s error while running activation set algorithm\n", __func__); | |
1025 | break; | |
1026 | } | |
1027 | if (visited) | |
1028 | visited[node_id] = 0; | |
1029 | } | |
1030 | exit: | |
1031 | return status; | |
1032 | } | |
1033 | ||
1034 | /** | |
1035 | * micscif_get_deactiveset: | |
1036 | * | |
1037 | * @node_id[in]: source node id. | |
1038 | * @nodemask[out]: bitmask of nodes present in de-activation set | |
1039 | * @max_disconn: flag to restrict de-activation set algoritthm to minimum/must set. | |
1040 | * True value indicates maximum de-activation set | |
1041 | * | |
1042 | * Algorithm to find out de-activation set for the given source node. De-activation set is used to disconnect node into | |
1043 | * the scif network. The algorithm can find out maximum possible de-activation set(required in situations like | |
1044 | * power management)if the max_possible flag is set. | |
1045 | */ | |
1046 | int | |
1047 | micscif_get_deactiveset(uint32_t node_id, uint8_t *nodemask, int max_disconn) | |
1048 | { | |
1049 | int status = 0; | |
1050 | uint32_t i = 0; | |
1051 | struct list_head stack; | |
1052 | uint8_t *visited = NULL; | |
1053 | uint8_t cont_next_step = 0; | |
1054 | uint32_t num_nodes = ms_info.mi_maxid + 1; | |
1055 | mic_ctx_t *mic_ctx; | |
1056 | ||
1057 | if (!ms_info.mi_depmtrx) { | |
1058 | status = -EINVAL; | |
1059 | goto exit; | |
1060 | } | |
1061 | ||
1062 | status = init_depgraph_stack(&stack); | |
1063 | if (status) { | |
1064 | pr_debug("%s failed to initilize depgraph stack\n", __func__); | |
1065 | goto exit; | |
1066 | } | |
1067 | ||
1068 | visited = kzalloc(sizeof(uint8_t) * num_nodes, GFP_KERNEL); | |
1069 | if (!visited) { | |
1070 | pr_debug("%s failed to allocated memory for visited array", __func__); | |
1071 | status = -ENOMEM; | |
1072 | goto exit; | |
1073 | } | |
1074 | ||
1075 | status = stack_push_node(&stack, node_id); | |
1076 | if (status) { | |
1077 | pr_debug("%s error while running de-activation set algorithm\n", __func__); | |
1078 | goto exit; | |
1079 | } | |
1080 | ||
1081 | while (!is_stack_empty(&stack)) { | |
1082 | ||
1083 | status = stack_pop_node(&stack, &node_id); | |
1084 | if (status) { | |
1085 | pr_debug("%s error while running de-activation set algorithm\n", __func__); | |
1086 | goto exit; | |
1087 | } | |
1088 | ||
1089 | /* check if we want to find out maximum possible de-activation set */ | |
1090 | if (max_disconn) { | |
1091 | cont_next_step = 1; | |
1092 | } | |
1093 | ||
1094 | if (!visited[node_id]) { | |
1095 | status = micscif_get_minimal_deactiveset(node_id, nodemask, visited); | |
1096 | if (status) { | |
1097 | if (status == -EOPNOTSUPP) { | |
1098 | pr_debug("%s No deactivation set found for node %d", __func__, node_id); | |
1099 | cont_next_step = 0; | |
1100 | } | |
1101 | else { | |
1102 | pr_debug("%s Failed to calculate deactivation set", __func__); | |
1103 | goto exit; | |
1104 | } | |
1105 | } | |
1106 | ||
1107 | } /* end for if (!visited[node_id]) */ | |
1108 | ||
1109 | if (cont_next_step) { | |
1110 | for (i = 0; i < num_nodes; i++) { | |
1111 | /* check if we can put more nodes 'i' in de-activation set if this node(dependent node) | |
1112 | * is de-activating | |
1113 | */ | |
1114 | if ((!visited[i]) && | |
1115 | (ms_info.mi_depmtrx[node_id][i] == DEP_STATE_DISCONNECT_READY)) { | |
1116 | if (i == 0) | |
1117 | continue; | |
1118 | mic_ctx = get_per_dev_ctx(i - 1); | |
1119 | if (mic_ctx->micpm_ctx.idle_state == | |
1120 | PM_IDLE_STATE_PC3_READY) { | |
1121 | /* This node might be able to get into deactivation set */ | |
1122 | status = stack_push_node(&stack, i); | |
1123 | if (status) { | |
1124 | pr_debug("%s error while running de-activation set algorithm\n", __func__); | |
1125 | goto exit; | |
1126 | } | |
1127 | } | |
1128 | } | |
1129 | } | |
1130 | } | |
1131 | } /* end for while (!is_stack_empty(&stack)) */ | |
1132 | ||
1133 | if (!nodemask_isvalid(nodemask)) { | |
1134 | pr_debug("%s No deactivation set found for node %d", | |
1135 | __func__, node_id); | |
1136 | status = -EOPNOTSUPP; | |
1137 | } | |
1138 | exit: | |
1139 | if (visited) { | |
1140 | kfree(visited); | |
1141 | } | |
1142 | uninit_depgraph_stack(&stack); | |
1143 | return status; | |
1144 | } | |
1145 | ||
1146 | /* micscif_update_p2p_state: | |
1147 | * | |
1148 | * Update the p2p_disc_state of peer node peer_id in the p2p list of node node_id. | |
1149 | * | |
1150 | * @node_id: The node id whose p2p list needs to be updated. | |
1151 | * @peer_id: The node id in the p2p list of the node_id that will get updated. | |
1152 | * @scif_state: The state to be updated to. | |
1153 | * | |
1154 | */ | |
1155 | void micscif_update_p2p_state(uint32_t node_id, uint32_t peer_id, enum scif_state state) { | |
1156 | ||
1157 | struct micscif_dev *dev; | |
1158 | struct list_head *pos, *tmp; | |
1159 | struct scif_p2p_info *p2p; | |
1160 | ||
1161 | dev = &scif_dev[node_id]; | |
1162 | if (!list_empty(&dev->sd_p2p)) { | |
1163 | list_for_each_safe(pos, tmp, &dev->sd_p2p) { | |
1164 | p2p = list_entry(pos, struct scif_p2p_info, | |
1165 | ppi_list); | |
1166 | if(p2p->ppi_peer_id == peer_id) { | |
1167 | p2p->ppi_disc_state = state; | |
1168 | break; | |
1169 | } | |
1170 | } | |
1171 | } | |
1172 | } | |
1173 | ||
1174 | /* micscif_p2p_node_exists: Check if a node exists in the | |
1175 | * list of nodes that have been sent an rmnode message. | |
1176 | * | |
1177 | * node_list: The list that contains the nodes that has been | |
1178 | * sent the rmnode message for this transaction. | |
1179 | * node_id: the node to be searched for. | |
1180 | * | |
1181 | * returns: true of the node exists.False otherwise | |
1182 | */ | |
1183 | bool micscif_rmnode_msg_sent(struct list_head *node_list, uint32_t node_id) { | |
1184 | ||
1185 | struct list_head *pos1, *tmp1; | |
1186 | struct stack_node *added_node; | |
1187 | ||
1188 | if (!list_empty(node_list)) { | |
1189 | list_for_each_safe(pos1, tmp1, node_list) { | |
1190 | added_node = list_entry(pos1, struct stack_node, next); | |
1191 | if(added_node->node_id == node_id) | |
1192 | return true; | |
1193 | } | |
1194 | } | |
1195 | return false; | |
1196 | } | |
1197 | ||
1198 | /** | |
1199 | * micscif_execute_disconnecte: Perform PM disconnection of a node | |
1200 | * with its neighboring nodes. | |
1201 | * | |
1202 | * node_id: The node to be disconnected. | |
1203 | * nodemask: Mask containing the list of nodes (including node_id) | |
1204 | * to be disconnected. | |
1205 | * node_list: List of nodes that received the disconnection message. | |
1206 | */ | |
1207 | int micscif_execute_disconnect(uint32_t node_id, | |
1208 | uint8_t *nodemask, | |
1209 | struct list_head *node_list) | |
1210 | { | |
1211 | uint32_t status = 0; | |
1212 | int ret; | |
1213 | uint64_t msg_cnt = 0; | |
1214 | uint32_t i = 0; | |
1215 | int pending_wakeups = 0; | |
1216 | mic_ctx_t *send_rmnode_ctx; | |
1217 | uint32_t node; | |
1218 | mic_ctx_t *mic_ctx = get_per_dev_ctx(node_id - 1); | |
1219 | struct scif_p2p_info *p2p; | |
1220 | struct list_head *pos, *tmp; | |
1221 | struct micscif_dev *dev; | |
1222 | ||
1223 | ||
1224 | /* Always send rmnode msg to SCIF_HOST_NODE */ | |
1225 | memcpy(mic_data.dd_pm.nodemask, nodemask, | |
1226 | mic_data.dd_pm.nodemask_len); | |
1227 | ret = (int) micscif_send_pm_rmnode_msg(SCIF_HOST_NODE, 0, mic_data.dd_pm.nodemask_len, | |
1228 | node_id); | |
1229 | /* Add this node to msg list. */ | |
1230 | if(!ret) { | |
1231 | msg_cnt++; | |
1232 | stack_push_node(node_list, SCIF_HOST_NODE); | |
1233 | } | |
1234 | ||
1235 | if((ret == 0)||(ret == -ENODEV)) { | |
1236 | status = 0; | |
1237 | } | |
1238 | ||
1239 | /* For each node in the nodemask, traverse its p2p list | |
1240 | * and send rmnode_msg to those nodes 1) That are not also | |
1241 | * in the node mask and 2) That have not been already sent | |
1242 | * rmnode messages in this transaction and 3) That have | |
1243 | * their disconnection state as RUNNING. | |
1244 | */ | |
1245 | for (i = 0; i <= ms_info.mi_maxid; i++) { | |
1246 | /* verify if the node is present in deactivation set */ | |
1247 | if (!get_nodemask_bit(nodemask, i)) | |
1248 | continue; | |
1249 | ||
1250 | /* Get to the p2p list of this node */ | |
1251 | dev = &scif_dev[i]; | |
1252 | list_for_each_safe(pos, tmp, &dev->sd_p2p) { | |
1253 | p2p = list_entry(pos, struct scif_p2p_info, | |
1254 | ppi_list); | |
1255 | ||
1256 | if (get_nodemask_bit(nodemask, p2p->ppi_peer_id)) | |
1257 | continue; | |
1258 | if (p2p->ppi_disc_state == SCIFDEV_SLEEPING) | |
1259 | continue; | |
1260 | ||
1261 | if(micscif_rmnode_msg_sent(node_list, p2p->ppi_peer_id)) | |
1262 | continue; | |
1263 | send_rmnode_ctx = get_per_dev_ctx(p2p->ppi_peer_id - 1); | |
1264 | if (!send_rmnode_ctx->micpm_ctx.nodemask.va) { | |
1265 | status = -EINVAL; | |
1266 | goto list_cleanup; | |
1267 | } | |
1268 | ||
1269 | memcpy(send_rmnode_ctx->micpm_ctx.nodemask.va, nodemask, | |
1270 | send_rmnode_ctx->micpm_ctx.nodemask.len); | |
1271 | ret = (int) micscif_send_pm_rmnode_msg(p2p->ppi_peer_id, | |
1272 | send_rmnode_ctx->micpm_ctx.nodemask.pa, | |
1273 | send_rmnode_ctx->micpm_ctx.nodemask.len,node_id); | |
1274 | ||
1275 | /* Add this node to msg list. */ | |
1276 | if(!ret) { | |
1277 | msg_cnt++; | |
1278 | stack_push_node(node_list, p2p->ppi_peer_id); | |
1279 | } | |
1280 | ||
1281 | if((ret == 0)||(ret == -ENODEV)) { | |
1282 | status = 0; | |
1283 | } | |
1284 | } | |
1285 | } | |
1286 | ||
1287 | ret = wait_event_timeout(ms_info.mi_disconn_wq, | |
1288 | (atomic_read(&mic_ctx->disconn_rescnt) == msg_cnt) || | |
1289 | (pending_wakeups = atomic_read(&mic_data.dd_pm.wakeup_in_progress)), | |
1290 | NODE_ALIVE_TIMEOUT); | |
1291 | if ((!ret) || (atomic_read(&mic_ctx->disconn_rescnt) != msg_cnt) | |
1292 | || (ms_info.mi_disconnect_status == OP_FAILED)) { | |
1293 | pr_debug("SCIF disconnect failed. " | |
1294 | "remove_node messages sent: = %llu " | |
1295 | "remove_node acks received: %d " | |
1296 | "Pending wakeups: %d ret = %d\n", msg_cnt, | |
1297 | atomic_read(&mic_ctx->disconn_rescnt), | |
1298 | pending_wakeups, ret); | |
1299 | ||
1300 | status = -EAGAIN; | |
1301 | goto list_cleanup; | |
1302 | } | |
1303 | return status; | |
1304 | ||
1305 | list_cleanup: | |
1306 | while (!is_stack_empty(node_list)) | |
1307 | stack_pop_node(node_list, &node); | |
1308 | return status; | |
1309 | } | |
1310 | ||
1311 | /** | |
1312 | * micscif_node_disconnect: | |
1313 | * | |
1314 | * @node_id[in]: source node id. | |
1315 | * @nodemask[out]: bitmask of nodes that have to be disconnected together. | |
1316 | * it represents node_id | |
1317 | * @disconn_type[in]: flag to identify disconnection type. (for example - power mgmt, lost node, maintenance mode etc) | |
1318 | * | |
1319 | * Method responsible for disconnecting node from the scif network. considers dependencies with other node. | |
1320 | * finds out deactivation set. Sends node queue pair messages to all the scif nodes outside deactivation set | |
1321 | * returns error if node can not be disconnected from the network. | |
1322 | */ | |
1323 | int micscif_disconnect_node(uint32_t node_id, uint8_t *nodemask, enum disconn_type type) | |
1324 | { | |
1325 | uint32_t status = 0; | |
1326 | int ret; | |
1327 | uint64_t msg_cnt = 0; | |
1328 | uint32_t i = 0; | |
1329 | mic_ctx_t *mic_ctx = 0; | |
1330 | struct list_head node_list; | |
1331 | uint32_t node; | |
1332 | ||
1333 | if (!node_id) | |
1334 | return -EINVAL; | |
1335 | ||
1336 | mic_ctx = get_per_dev_ctx(node_id - 1); | |
1337 | ||
1338 | if (!mic_ctx) | |
1339 | return -EINVAL; | |
1340 | ||
1341 | switch(type) { | |
1342 | case DISCONN_TYPE_POWER_MGMT: | |
1343 | { | |
1344 | if (!nodemask) | |
1345 | return -EINVAL; | |
1346 | ||
1347 | atomic_long_add(1, &ms_info.mi_unique_msgid); | |
1348 | atomic_set(&mic_ctx->disconn_rescnt, 0); | |
1349 | ms_info.mi_disconnect_status = OP_IN_PROGRESS; | |
1350 | INIT_LIST_HEAD(&node_list); | |
1351 | ||
1352 | status = micscif_execute_disconnect(node_id, | |
1353 | nodemask, &node_list); | |
1354 | if (status) | |
1355 | return status; | |
1356 | ||
1357 | /* Reset unique msg_id */ | |
1358 | atomic_long_set(&ms_info.mi_unique_msgid, 0); | |
1359 | ||
1360 | while (!is_stack_empty(&node_list)) { | |
1361 | status = stack_pop_node(&node_list, &node); | |
1362 | if (status) | |
1363 | break; | |
1364 | ||
1365 | for (i = 0; i <= ms_info.mi_maxid; i++) { | |
1366 | if (!get_nodemask_bit(nodemask, i)) | |
1367 | continue; | |
1368 | micscif_update_p2p_state(i, node, SCIFDEV_SLEEPING); | |
1369 | } | |
1370 | } | |
1371 | break; | |
1372 | } | |
1373 | case DISCONN_TYPE_LOST_NODE: | |
1374 | { | |
1375 | atomic_long_add(1, &ms_info.mi_unique_msgid); | |
1376 | atomic_set(&mic_ctx->disconn_rescnt, 0); | |
1377 | ||
1378 | for (i = 0; ((i <= ms_info.mi_maxid) && (i != node_id)); i++) { | |
1379 | ret = (int)micscif_send_lost_node_rmnode_msg(i, node_id); | |
1380 | if(!ret) | |
1381 | msg_cnt++; | |
1382 | if((ret == 0)||(ret == -ENODEV)) { | |
1383 | status = 0; | |
1384 | } | |
1385 | } | |
1386 | ||
1387 | ret = wait_event_timeout(ms_info.mi_disconn_wq, | |
1388 | (atomic_read(&mic_ctx->disconn_rescnt) == msg_cnt), | |
1389 | NODE_ALIVE_TIMEOUT); | |
1390 | break; | |
1391 | } | |
1392 | default: | |
1393 | status = -EINVAL; | |
1394 | } | |
1395 | ||
1396 | return status; | |
1397 | } | |
1398 | ||
1399 | /** | |
1400 | * micscif_node_connect: | |
1401 | * | |
1402 | * @node_id[in]: node to wakeup. | |
1403 | * @bool get_ref[in]: Also get node reference after wakeup by incrementing the PM reference count | |
1404 | * | |
1405 | * Method responsible for connecting node into the scif network. considers dependencies with other node. | |
1406 | * finds out activation set. connects all the depenendent nodes in the activation set | |
1407 | * returns error if node can not be connected from the network. | |
1408 | */ | |
1409 | int | |
1410 | micscif_connect_node(uint32_t node_id, bool get_ref) | |
1411 | { | |
1412 | return do_idlestate_exit(get_per_dev_ctx(node_id - 1), get_ref); | |
1413 | } | |
1414 | ||
1415 | uint64_t micscif_send_node_alive(int node) | |
1416 | { | |
1417 | struct nodemsg alive_msg; | |
1418 | struct micscif_dev *dev = &scif_dev[node]; | |
1419 | int err; | |
1420 | ||
1421 | alive_msg.uop = SCIF_NODE_ALIVE; | |
1422 | alive_msg.src.node = ms_info.mi_nodeid; | |
1423 | alive_msg.dst.node = node; | |
1424 | pr_debug("node alive msg sent to node %d\n", node); | |
1425 | micscif_inc_node_refcnt(dev, 1); | |
1426 | err = micscif_nodeqp_send(dev, &alive_msg, NULL); | |
1427 | micscif_dec_node_refcnt(dev, 1); | |
1428 | return err; | |
1429 | } | |
1430 | ||
1431 | int micscif_handle_lostnode(uint32_t node_id) | |
1432 | { | |
1433 | mic_ctx_t *mic_ctx; | |
1434 | uint32_t status = -EOPNOTSUPP; | |
1435 | #ifdef MM_HANDLER_ENABLE | |
1436 | uint8_t *mmio_va; | |
1437 | sbox_scratch1_reg_t scratch1reg = {0}; | |
1438 | #endif | |
1439 | ||
1440 | printk("%s %d node %d\n", __func__, __LINE__, node_id); | |
1441 | mic_ctx = get_per_dev_ctx(node_id - 1); | |
1442 | ||
1443 | if (mic_ctx->state != MIC_ONLINE && mic_ctx->state != MIC_SHUTDOWN) | |
1444 | return 0; | |
1445 | ||
1446 | if (mic_crash_dump_enabled) { | |
1447 | if (!(status = vmcore_create(mic_ctx))) | |
1448 | printk("%s %d node %d ready for crash dump!\n", | |
1449 | __func__, __LINE__, node_id); | |
1450 | else | |
1451 | printk(KERN_ERR "%s %d node %d crash dump failed status %d\n", | |
1452 | __func__, __LINE__, node_id, status); | |
1453 | } | |
1454 | ||
1455 | mic_ctx->crash_count++; | |
1456 | mutex_lock(&mic_ctx->state_lock); | |
1457 | if (mic_ctx->state == MIC_ONLINE || | |
1458 | mic_ctx->state == MIC_SHUTDOWN) | |
1459 | mic_setstate(mic_ctx, MIC_LOST); | |
1460 | mutex_unlock(&mic_ctx->state_lock); | |
1461 | ||
1462 | /* mpssd will handle core dump and reset/auto reboot */ | |
1463 | if (mic_crash_dump_enabled && !status) | |
1464 | return status; | |
1465 | ||
1466 | printk("%s %d stopping node %d to recover lost node!\n", | |
1467 | __func__, __LINE__, node_id); | |
1468 | status = adapter_stop_device(mic_ctx, 1, !RESET_REATTEMPT); | |
1469 | wait_for_reset(mic_ctx); | |
1470 | ||
1471 | if (!ms_info.mi_watchdog_auto_reboot) { | |
1472 | printk("%s %d cannot boot node %d to recover lost node since auto_reboot is off\n", | |
1473 | __func__, __LINE__, node_id); | |
1474 | return status; | |
1475 | } | |
1476 | ||
1477 | /* Disabling MM handler invocation till it is ready to handle errors | |
1478 | * till then we just reboot the card | |
1479 | */ | |
1480 | #ifdef MM_HANDLER_ENABLE | |
1481 | mmio_va = mic_ctx->mmio.va; | |
1482 | scratch1reg.bits.status = FLASH_CMD_INVALID; | |
1483 | ||
1484 | if(mic_ctx->bi_family == FAMILY_ABR) { | |
1485 | printk("Node %d lost. Cannot recover in KNF\n", node_id); | |
1486 | status = adapter_start_device(mic_ctx); | |
1487 | return status; | |
1488 | } | |
1489 | ||
1490 | printk("Booting maintenance mode handler\n"); | |
1491 | status = set_card_usage_mode(mic_ctx, USAGE_MODE_MAINTENANCE, NULL, 0); | |
1492 | if(status) { | |
1493 | printk("Unable to boot maintenance mode\n"); | |
1494 | return status; | |
1495 | } | |
1496 | ||
1497 | status = send_flash_cmd(mic_ctx, RAS_CMD, NULL, 0); | |
1498 | if(status) { | |
1499 | printk("Unable to recover node\n"); | |
1500 | return status; | |
1501 | } | |
1502 | while(scratch1reg.bits.status != FLASH_CMD_COMPLETED) { | |
1503 | ret = SBOX_READ(mmio_va, SBOX_SCRATCH1); | |
1504 | scratch1reg.value = ret; | |
1505 | msleep(1); | |
1506 | i++; | |
1507 | printk("Looping for status (time = %d ms)\n", i); | |
1508 | if(i > NODE_ALIVE_TIMEOUT) { | |
1509 | status = -ETIME; | |
1510 | printk("Unable to recover node. Status bit is : %d\n", | |
1511 | scratch1reg.bits.status); | |
1512 | return status; | |
1513 | } | |
1514 | ||
1515 | } | |
1516 | #endif | |
1517 | printk("%s %d booting node %d to recover lost node!\n", | |
1518 | __func__, __LINE__, node_id); | |
1519 | status = adapter_start_device(mic_ctx); | |
1520 | return status; | |
1521 | } | |
1522 | ||
1523 | void micscif_watchdog_handler(struct work_struct *work) | |
1524 | { | |
1525 | struct micscif_dev *dev = | |
1526 | container_of(to_delayed_work(work), | |
1527 | struct micscif_dev, sd_watchdog_work); | |
1528 | struct _mic_ctx_t *mic_ctx; | |
1529 | int i = dev->sd_node, err, ret; | |
1530 | ||
1531 | mic_ctx = get_per_dev_ctx(i - 1); | |
1532 | ||
1533 | switch (mic_ctx->sdbic1) { | |
1534 | case SYSTEM_HALT: | |
1535 | case SYSTEM_POWER_OFF: | |
1536 | { | |
1537 | adapter_stop_device(mic_ctx, 1, !RESET_REATTEMPT); | |
1538 | wait_for_reset(mic_ctx); | |
1539 | mic_ctx->sdbic1 = 0; | |
1540 | break; | |
1541 | } | |
1542 | case SYSTEM_RESTART: | |
1543 | { | |
1544 | mic_setstate(mic_ctx, MIC_LOST); | |
1545 | mic_ctx->sdbic1 = 0; | |
1546 | break; | |
1547 | } | |
1548 | case SYSTEM_BOOTING: | |
1549 | case SYSTEM_RUNNING: | |
1550 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)) | |
1551 | case SYSTEM_SUSPEND_DISK: | |
1552 | #endif | |
1553 | break; | |
1554 | case 0xdead: | |
1555 | if (mic_crash_dump_enabled) | |
1556 | micscif_handle_lostnode(i); | |
1557 | mic_ctx->sdbic1 = 0; | |
1558 | break; | |
1559 | default: | |
1560 | break; | |
1561 | } | |
1562 | ||
1563 | switch (mic_ctx->state) { | |
1564 | case MIC_ONLINE: | |
1565 | break; | |
1566 | case MIC_BOOT: | |
1567 | goto restart_timer; | |
1568 | case MIC_SHUTDOWN: | |
1569 | case MIC_LOST: | |
1570 | case MIC_READY: | |
1571 | case MIC_NORESPONSE: | |
1572 | case MIC_BOOTFAIL: | |
1573 | case MIC_RESET: | |
1574 | case MIC_RESETFAIL: | |
1575 | case MIC_INVALID: | |
1576 | return; | |
1577 | } | |
1578 | ||
1579 | if (!ms_info.mi_watchdog_enabled) | |
1580 | return; | |
1581 | ||
1582 | err = micpm_get_reference(mic_ctx, false); | |
1583 | if (err == -EAGAIN) { | |
1584 | goto restart_timer; | |
1585 | } else if (err == -ENODEV) { | |
1586 | micscif_handle_lostnode(i); | |
1587 | goto restart_timer; | |
1588 | } | |
1589 | ||
1590 | if (1 != atomic_cmpxchg(&dev->sd_node_alive, 1, 0)) { | |
1591 | ||
1592 | err = (int)(micscif_send_node_alive(i)); | |
1593 | ||
1594 | if (err) { | |
1595 | micpm_put_reference(mic_ctx); | |
1596 | goto restart_timer; | |
1597 | } | |
1598 | ||
1599 | ret = wait_event_timeout(dev->sd_watchdog_wq, | |
1600 | (atomic_cmpxchg(&dev->sd_node_alive, 1, 0) == 1), | |
1601 | NODE_ALIVE_TIMEOUT); | |
1602 | if (!ret || err) | |
1603 | micscif_handle_lostnode(i); | |
1604 | } | |
1605 | micpm_put_reference(mic_ctx); | |
1606 | ||
1607 | restart_timer: | |
1608 | if (dev->sd_ln_wq) | |
1609 | queue_delayed_work(dev->sd_ln_wq, | |
1610 | &dev->sd_watchdog_work, NODE_ALIVE_TIMEOUT); | |
1611 | } | |
1612 | #else | |
1613 | ||
1614 | long micscif_suspend(uint8_t* nodemask) { | |
1615 | long ret = 0; | |
1616 | int i; | |
1617 | struct micscif_dev *dev; | |
1618 | ||
1619 | for (i = 0; i <= ms_info.mi_maxid; i++) { | |
1620 | if (get_nodemask_bit(nodemask , i)) { | |
1621 | dev = &scif_dev[i]; | |
1622 | if (SCIFDEV_RUNNING != dev->sd_state) | |
1623 | continue; | |
1624 | ||
1625 | ret = atomic_long_cmpxchg( | |
1626 | &dev->scif_ref_cnt, 0, SCIF_NODE_IDLE); | |
1627 | if (!ret || ret == SCIF_NODE_IDLE) { | |
1628 | dev->sd_state = SCIFDEV_SLEEPING; | |
1629 | ret = 0; | |
1630 | } | |
1631 | else { | |
1632 | set_nodemask_bit(nodemask, i, 0); | |
1633 | ret = EAGAIN; | |
1634 | } | |
1635 | } | |
1636 | } | |
1637 | return ret; | |
1638 | } | |
1639 | /* | |
1640 | * scif_suspend_handler - SCIF tasks before transition to low power state. | |
1641 | */ | |
1642 | int micscif_suspend_handler(struct notifier_block *this, | |
1643 | unsigned long event, void *ptr) | |
1644 | { | |
1645 | int ret = 0; | |
1646 | #ifdef SCIF_ENABLE_PM | |
1647 | int node = 0; | |
1648 | int size; | |
1649 | uint8_t *nodemask_buf; | |
1650 | ||
1651 | size = ((int) ((ms_info.mi_maxid + 1) / 8) + | |
1652 | (((ms_info.mi_maxid + 1) % 8) ? 1 : 0)); | |
1653 | nodemask_buf = (uint8_t*)kzalloc(size, GFP_ATOMIC); | |
1654 | if(!nodemask_buf) | |
1655 | return -ENOMEM; | |
1656 | ||
1657 | for (node = 0; node <= ms_info.mi_maxid; node++) { | |
1658 | if ((node != SCIF_HOST_NODE) && (node != ms_info.mi_nodeid)) | |
1659 | set_nodemask_bit(nodemask_buf, node, 1); | |
1660 | } | |
1661 | ||
1662 | if (micscif_suspend(nodemask_buf)){ | |
1663 | ret = -EBUSY; | |
1664 | goto clean_up; | |
1665 | } | |
1666 | ||
1667 | dma_suspend(mic_dma_handle); | |
1668 | clean_up: | |
1669 | kfree(nodemask_buf); | |
1670 | #endif | |
1671 | return ret; | |
1672 | } | |
1673 | ||
1674 | /* | |
1675 | * micscif_resume_handler - SCIF tasks after wake up from low power state. | |
1676 | */ | |
1677 | int micscif_resume_handler(struct notifier_block *this, | |
1678 | unsigned long event, void *ptr) | |
1679 | { | |
1680 | #ifdef SCIF_ENABLE_PM | |
1681 | #ifdef _MIC_SCIF_ | |
1682 | queue_work(ms_info.mi_misc_wq, &ms_info.mi_misc_work); | |
1683 | #endif | |
1684 | dma_resume(mic_dma_handle); | |
1685 | #endif | |
1686 | return 0; | |
1687 | } | |
1688 | ||
1689 | /* | |
1690 | * scif_fail_suspend_handler - SCIF tasks if a previous scif_suspend call has | |
1691 | * failed since a low power state transition could not be completed. | |
1692 | */ | |
1693 | int micscif_fail_suspend_handler(struct notifier_block *this, | |
1694 | unsigned long event, void *ptr) | |
1695 | { | |
1696 | /* Stub out function since it is an optimization that isn't working properly */ | |
1697 | #if 0 | |
1698 | #ifdef SCIF_ENABLE_PM | |
1699 | int node = 0; | |
1700 | long ret; | |
1701 | struct micscif_dev *dev; | |
1702 | ||
1703 | for (node = 0; node <= ms_info.mi_maxid; node++) { | |
1704 | dev = &scif_dev[node]; | |
1705 | ret = atomic_long_cmpxchg(&dev->scif_ref_cnt, SCIF_NODE_IDLE, 0); | |
1706 | if (ret != SCIF_NODE_IDLE) | |
1707 | continue; | |
1708 | if (SCIFDEV_SLEEPING == dev->sd_state) | |
1709 | dev->sd_state = SCIFDEV_RUNNING; | |
1710 | } | |
1711 | #endif | |
1712 | #endif | |
1713 | return 0; | |
1714 | } | |
1715 | ||
1716 | void micscif_get_node_info(void) | |
1717 | { | |
1718 | struct nodemsg msg; | |
1719 | struct get_node_info node_info; | |
1720 | ||
1721 | init_waitqueue_head(&node_info.wq); | |
1722 | node_info.state = OP_IN_PROGRESS; | |
1723 | micscif_inc_node_refcnt(&scif_dev[SCIF_HOST_NODE], 1); | |
1724 | msg.uop = SCIF_GET_NODE_INFO; | |
1725 | msg.src.node = ms_info.mi_nodeid; | |
1726 | msg.dst.node = SCIF_HOST_NODE; | |
1727 | msg.payload[3] = (uint64_t)&node_info; | |
1728 | ||
1729 | if ((micscif_nodeqp_send(&scif_dev[SCIF_HOST_NODE], &msg, NULL))) | |
1730 | goto done; | |
1731 | ||
1732 | wait_event(node_info.wq, node_info.state != OP_IN_PROGRESS); | |
1733 | done: | |
1734 | micscif_dec_node_refcnt(&scif_dev[SCIF_HOST_NODE], 1); | |
1735 | /* Synchronize with the thread waking us up */ | |
1736 | mutex_lock(&ms_info.mi_conflock); | |
1737 | mutex_unlock(&ms_info.mi_conflock); | |
1738 | ; | |
1739 | } | |
1740 | #endif /* _MIC_SCIF_ */ |