]>
Commit | Line | Data |
---|---|---|
40cb5942 SD |
1 | /* |
2 | * Intel MIC Platform Software Stack (MPSS) | |
3 | * | |
4 | * Copyright(c) 2014 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 | * Intel SCIF driver. | |
16 | * | |
17 | */ | |
18 | #include "scif_peer_bus.h" | |
19 | ||
20 | #include "scif_main.h" | |
21 | #include "scif_map.h" | |
22 | ||
76371c7c NR |
23 | /** |
24 | * scif_invalidate_ep() - Set state for all connected endpoints | |
25 | * to disconnected and wake up all send/recv waitqueues | |
26 | */ | |
27 | static void scif_invalidate_ep(int node) | |
28 | { | |
29 | struct scif_endpt *ep; | |
30 | struct list_head *pos, *tmpq; | |
31 | ||
32 | flush_work(&scif_info.conn_work); | |
33 | mutex_lock(&scif_info.connlock); | |
34 | list_for_each_safe(pos, tmpq, &scif_info.disconnected) { | |
35 | ep = list_entry(pos, struct scif_endpt, list); | |
36 | if (ep->remote_dev->node == node) { | |
37 | spin_lock(&ep->lock); | |
38 | scif_cleanup_ep_qp(ep); | |
39 | spin_unlock(&ep->lock); | |
40 | } | |
41 | } | |
42 | list_for_each_safe(pos, tmpq, &scif_info.connected) { | |
43 | ep = list_entry(pos, struct scif_endpt, list); | |
44 | if (ep->remote_dev->node == node) { | |
45 | list_del(pos); | |
46 | spin_lock(&ep->lock); | |
47 | ep->state = SCIFEP_DISCONNECTED; | |
48 | list_add_tail(&ep->list, &scif_info.disconnected); | |
49 | scif_cleanup_ep_qp(ep); | |
50 | wake_up_interruptible(&ep->sendwq); | |
51 | wake_up_interruptible(&ep->recvwq); | |
52 | spin_unlock(&ep->lock); | |
53 | } | |
54 | } | |
55 | mutex_unlock(&scif_info.connlock); | |
56 | } | |
57 | ||
40cb5942 SD |
58 | void scif_free_qp(struct scif_dev *scifdev) |
59 | { | |
60 | struct scif_qp *qp = scifdev->qpairs; | |
61 | ||
62 | if (!qp) | |
63 | return; | |
64 | scif_free_coherent((void *)qp->inbound_q.rb_base, | |
65 | qp->local_buf, scifdev, qp->inbound_q.size); | |
66 | scif_unmap_single(qp->local_qp, scifdev, sizeof(struct scif_qp)); | |
67 | kfree(scifdev->qpairs); | |
68 | scifdev->qpairs = NULL; | |
69 | } | |
70 | ||
71 | static void scif_cleanup_qp(struct scif_dev *dev) | |
72 | { | |
73 | struct scif_qp *qp = &dev->qpairs[0]; | |
74 | ||
75 | if (!qp) | |
76 | return; | |
77 | scif_iounmap((void *)qp->remote_qp, sizeof(struct scif_qp), dev); | |
78 | scif_iounmap((void *)qp->outbound_q.rb_base, | |
79 | sizeof(struct scif_qp), dev); | |
80 | qp->remote_qp = NULL; | |
81 | qp->local_write = 0; | |
82 | qp->inbound_q.current_write_offset = 0; | |
83 | qp->inbound_q.current_read_offset = 0; | |
84 | if (scifdev_is_p2p(dev)) | |
85 | scif_free_qp(dev); | |
86 | } | |
87 | ||
88 | void scif_send_acks(struct scif_dev *dev) | |
89 | { | |
90 | struct scifmsg msg; | |
91 | ||
92 | if (dev->node_remove_ack_pending) { | |
93 | msg.uop = SCIF_NODE_REMOVE_ACK; | |
94 | msg.src.node = scif_info.nodeid; | |
95 | msg.dst.node = SCIF_MGMT_NODE; | |
96 | msg.payload[0] = dev->node; | |
97 | scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], &msg); | |
98 | dev->node_remove_ack_pending = false; | |
99 | } | |
100 | if (dev->exit_ack_pending) { | |
101 | msg.uop = SCIF_EXIT_ACK; | |
102 | msg.src.node = scif_info.nodeid; | |
103 | msg.dst.node = dev->node; | |
104 | scif_nodeqp_send(dev, &msg); | |
105 | dev->exit_ack_pending = false; | |
106 | } | |
107 | } | |
108 | ||
109 | /* | |
110 | * scif_cleanup_scifdev | |
111 | * | |
112 | * @dev: Remote SCIF device. | |
113 | * Uninitialize SCIF data structures for remote SCIF device. | |
114 | */ | |
115 | void scif_cleanup_scifdev(struct scif_dev *dev) | |
116 | { | |
117 | struct scif_hw_dev *sdev = dev->sdev; | |
118 | ||
119 | if (!dev->sdev) | |
120 | return; | |
121 | if (scifdev_is_p2p(dev)) { | |
122 | if (dev->cookie) { | |
123 | sdev->hw_ops->free_irq(sdev, dev->cookie, dev); | |
124 | dev->cookie = NULL; | |
125 | } | |
126 | scif_destroy_intr_wq(dev); | |
127 | } | |
128 | scif_destroy_p2p(dev); | |
76371c7c | 129 | scif_invalidate_ep(dev->node); |
40cb5942 SD |
130 | scif_send_acks(dev); |
131 | if (!dev->node && scif_info.card_initiated_exit) { | |
132 | /* | |
133 | * Send an SCIF_EXIT message which is the last message from MIC | |
134 | * to the Host and wait for a SCIF_EXIT_ACK | |
135 | */ | |
136 | scif_send_exit(dev); | |
137 | scif_info.card_initiated_exit = false; | |
138 | } | |
139 | scif_cleanup_qp(dev); | |
140 | } | |
141 | ||
142 | /* | |
143 | * scif_remove_node: | |
144 | * | |
145 | * @node: Node to remove | |
146 | */ | |
147 | void scif_handle_remove_node(int node) | |
148 | { | |
149 | struct scif_dev *scifdev = &scif_dev[node]; | |
150 | struct scif_peer_dev *spdev; | |
151 | ||
152 | rcu_read_lock(); | |
153 | spdev = rcu_dereference(scifdev->spdev); | |
154 | rcu_read_unlock(); | |
155 | if (spdev) | |
156 | scif_peer_unregister_device(spdev); | |
157 | else | |
158 | scif_send_acks(scifdev); | |
159 | } | |
160 | ||
161 | static int scif_send_rmnode_msg(int node, int remove_node) | |
162 | { | |
163 | struct scifmsg notif_msg; | |
164 | struct scif_dev *dev = &scif_dev[node]; | |
165 | ||
166 | notif_msg.uop = SCIF_NODE_REMOVE; | |
167 | notif_msg.src.node = scif_info.nodeid; | |
168 | notif_msg.dst.node = node; | |
169 | notif_msg.payload[0] = remove_node; | |
170 | return scif_nodeqp_send(dev, ¬if_msg); | |
171 | } | |
172 | ||
173 | /** | |
174 | * scif_node_disconnect: | |
175 | * | |
176 | * @node_id[in]: source node id. | |
177 | * @mgmt_initiated: Disconnection initiated from the mgmt node | |
178 | * | |
179 | * Disconnect a node from the scif network. | |
180 | */ | |
181 | void scif_disconnect_node(u32 node_id, bool mgmt_initiated) | |
182 | { | |
183 | int ret; | |
184 | int msg_cnt = 0; | |
185 | u32 i = 0; | |
186 | struct scif_dev *scifdev = &scif_dev[node_id]; | |
187 | ||
188 | if (!node_id) | |
189 | return; | |
190 | ||
191 | atomic_set(&scifdev->disconn_rescnt, 0); | |
192 | ||
193 | /* Destroy p2p network */ | |
194 | for (i = 1; i <= scif_info.maxid; i++) { | |
195 | if (i == node_id) | |
196 | continue; | |
197 | ret = scif_send_rmnode_msg(i, node_id); | |
198 | if (!ret) | |
199 | msg_cnt++; | |
200 | } | |
201 | /* Wait for the remote nodes to respond with SCIF_NODE_REMOVE_ACK */ | |
202 | ret = wait_event_timeout(scifdev->disconn_wq, | |
203 | (atomic_read(&scifdev->disconn_rescnt) | |
204 | == msg_cnt), SCIF_NODE_ALIVE_TIMEOUT); | |
205 | /* Tell the card to clean up */ | |
206 | if (mgmt_initiated && _scifdev_alive(scifdev)) | |
207 | /* | |
208 | * Send an SCIF_EXIT message which is the last message from Host | |
209 | * to the MIC and wait for a SCIF_EXIT_ACK | |
210 | */ | |
211 | scif_send_exit(scifdev); | |
212 | atomic_set(&scifdev->disconn_rescnt, 0); | |
213 | /* Tell the mgmt node to clean up */ | |
214 | ret = scif_send_rmnode_msg(SCIF_MGMT_NODE, node_id); | |
215 | if (!ret) | |
216 | /* Wait for mgmt node to respond with SCIF_NODE_REMOVE_ACK */ | |
217 | wait_event_timeout(scifdev->disconn_wq, | |
218 | (atomic_read(&scifdev->disconn_rescnt) == 1), | |
219 | SCIF_NODE_ALIVE_TIMEOUT); | |
220 | } | |
fdd9fd5c SD |
221 | |
222 | void scif_get_node_info(void) | |
223 | { | |
224 | struct scifmsg msg; | |
225 | DECLARE_COMPLETION_ONSTACK(node_info); | |
226 | ||
227 | msg.uop = SCIF_GET_NODE_INFO; | |
228 | msg.src.node = scif_info.nodeid; | |
229 | msg.dst.node = SCIF_MGMT_NODE; | |
230 | msg.payload[3] = (u64)&node_info; | |
231 | ||
232 | if ((scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], &msg))) | |
233 | return; | |
234 | ||
235 | /* Wait for a response with SCIF_GET_NODE_INFO */ | |
236 | wait_for_completion(&node_info); | |
237 | } |