misc: mic: SCIF open close bind and listen APIs
[linux-2.6-block.git] / drivers / misc / mic / scif / scif_api.c
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 <linux/scif.h>
19 #include "scif_main.h"
20 #include "scif_map.h"
21
22 static const char * const scif_ep_states[] = {
23         "Unbound",
24         "Bound",
25         "Listening",
26         "Connected",
27         "Connecting",
28         "Mapping",
29         "Closing",
30         "Close Listening",
31         "Disconnected",
32         "Zombie"};
33
34 enum conn_async_state {
35         ASYNC_CONN_IDLE = 1,    /* ep setup for async connect */
36         ASYNC_CONN_INPROGRESS,  /* async connect in progress */
37         ASYNC_CONN_FLUSH_WORK   /* async work flush in progress  */
38 };
39
40 scif_epd_t scif_open(void)
41 {
42         struct scif_endpt *ep;
43
44         might_sleep();
45         ep = kzalloc(sizeof(*ep), GFP_KERNEL);
46         if (!ep)
47                 goto err_ep_alloc;
48
49         ep->qp_info.qp = kzalloc(sizeof(*ep->qp_info.qp), GFP_KERNEL);
50         if (!ep->qp_info.qp)
51                 goto err_qp_alloc;
52
53         spin_lock_init(&ep->lock);
54         mutex_init(&ep->sendlock);
55         mutex_init(&ep->recvlock);
56
57         ep->state = SCIFEP_UNBOUND;
58         dev_dbg(scif_info.mdev.this_device,
59                 "SCIFAPI open: ep %p success\n", ep);
60         return ep;
61
62 err_qp_alloc:
63         kfree(ep);
64 err_ep_alloc:
65         return NULL;
66 }
67 EXPORT_SYMBOL_GPL(scif_open);
68
69 /*
70  * scif_disconnect_ep - Disconnects the endpoint if found
71  * @epd: The end point returned from scif_open()
72  */
73 static struct scif_endpt *scif_disconnect_ep(struct scif_endpt *ep)
74 {
75         struct scifmsg msg;
76         struct scif_endpt *fep = NULL;
77         struct scif_endpt *tmpep;
78         struct list_head *pos, *tmpq;
79         int err;
80
81         /*
82          * Wake up any threads blocked in send()/recv() before closing
83          * out the connection. Grabbing and releasing the send/recv lock
84          * will ensure that any blocked senders/receivers have exited for
85          * Ring 0 endpoints. It is a Ring 0 bug to call send/recv after
86          * close. Ring 3 endpoints are not affected since close will not
87          * be called while there are IOCTLs executing.
88          */
89         wake_up_interruptible(&ep->sendwq);
90         wake_up_interruptible(&ep->recvwq);
91         mutex_lock(&ep->sendlock);
92         mutex_unlock(&ep->sendlock);
93         mutex_lock(&ep->recvlock);
94         mutex_unlock(&ep->recvlock);
95
96         /* Remove from the connected list */
97         mutex_lock(&scif_info.connlock);
98         list_for_each_safe(pos, tmpq, &scif_info.connected) {
99                 tmpep = list_entry(pos, struct scif_endpt, list);
100                 if (tmpep == ep) {
101                         list_del(pos);
102                         fep = tmpep;
103                         spin_lock(&ep->lock);
104                         break;
105                 }
106         }
107
108         if (!fep) {
109                 /*
110                  * The other side has completed the disconnect before
111                  * the end point can be removed from the list. Therefore
112                  * the ep lock is not locked, traverse the disconnected
113                  * list to find the endpoint and release the conn lock.
114                  */
115                 list_for_each_safe(pos, tmpq, &scif_info.disconnected) {
116                         tmpep = list_entry(pos, struct scif_endpt, list);
117                         if (tmpep == ep) {
118                                 list_del(pos);
119                                 break;
120                         }
121                 }
122                 mutex_unlock(&scif_info.connlock);
123                 return NULL;
124         }
125
126         init_completion(&ep->discon);
127         msg.uop = SCIF_DISCNCT;
128         msg.src = ep->port;
129         msg.dst = ep->peer;
130         msg.payload[0] = (u64)ep;
131         msg.payload[1] = ep->remote_ep;
132
133         err = scif_nodeqp_send(ep->remote_dev, &msg);
134         spin_unlock(&ep->lock);
135         mutex_unlock(&scif_info.connlock);
136
137         if (!err)
138                 /* Wait for the remote node to respond with SCIF_DISCNT_ACK */
139                 wait_for_completion_timeout(&ep->discon,
140                                             SCIF_NODE_ALIVE_TIMEOUT);
141         return ep;
142 }
143
144 int scif_close(scif_epd_t epd)
145 {
146         struct scif_endpt *ep = (struct scif_endpt *)epd;
147         struct scif_endpt *tmpep;
148         struct list_head *pos, *tmpq;
149         enum scif_epd_state oldstate;
150         bool flush_conn;
151
152         dev_dbg(scif_info.mdev.this_device, "SCIFAPI close: ep %p %s\n",
153                 ep, scif_ep_states[ep->state]);
154         might_sleep();
155         spin_lock(&ep->lock);
156         flush_conn = (ep->conn_async_state == ASYNC_CONN_INPROGRESS);
157         spin_unlock(&ep->lock);
158
159         if (flush_conn)
160                 flush_work(&scif_info.conn_work);
161
162         spin_lock(&ep->lock);
163         oldstate = ep->state;
164
165         ep->state = SCIFEP_CLOSING;
166
167         switch (oldstate) {
168         case SCIFEP_ZOMBIE:
169         case SCIFEP_DISCONNECTED:
170                 spin_unlock(&ep->lock);
171                 /* Remove from the disconnected list */
172                 mutex_lock(&scif_info.connlock);
173                 list_for_each_safe(pos, tmpq, &scif_info.disconnected) {
174                         tmpep = list_entry(pos, struct scif_endpt, list);
175                         if (tmpep == ep) {
176                                 list_del(pos);
177                                 break;
178                         }
179                 }
180                 mutex_unlock(&scif_info.connlock);
181                 break;
182         case SCIFEP_UNBOUND:
183         case SCIFEP_BOUND:
184         case SCIFEP_CONNECTING:
185                 spin_unlock(&ep->lock);
186                 break;
187         case SCIFEP_MAPPING:
188         case SCIFEP_CONNECTED:
189         case SCIFEP_CLOSING:
190         {
191                 spin_unlock(&ep->lock);
192                 scif_disconnect_ep(ep);
193                 break;
194         }
195         case SCIFEP_LISTENING:
196         case SCIFEP_CLLISTEN:
197         {
198                 struct scif_conreq *conreq;
199                 struct scifmsg msg;
200                 struct scif_endpt *aep;
201
202                 spin_unlock(&ep->lock);
203                 spin_lock(&scif_info.eplock);
204
205                 /* remove from listen list */
206                 list_for_each_safe(pos, tmpq, &scif_info.listen) {
207                         tmpep = list_entry(pos, struct scif_endpt, list);
208                         if (tmpep == ep)
209                                 list_del(pos);
210                 }
211                 /* Remove any dangling accepts */
212                 while (ep->acceptcnt) {
213                         aep = list_first_entry(&ep->li_accept,
214                                                struct scif_endpt, liacceptlist);
215                         list_del(&aep->liacceptlist);
216                         scif_put_port(aep->port.port);
217                         list_for_each_safe(pos, tmpq, &scif_info.uaccept) {
218                                 tmpep = list_entry(pos, struct scif_endpt,
219                                                    miacceptlist);
220                                 if (tmpep == aep) {
221                                         list_del(pos);
222                                         break;
223                                 }
224                         }
225                         spin_unlock(&scif_info.eplock);
226                         mutex_lock(&scif_info.connlock);
227                         list_for_each_safe(pos, tmpq, &scif_info.connected) {
228                                 tmpep = list_entry(pos,
229                                                    struct scif_endpt, list);
230                                 if (tmpep == aep) {
231                                         list_del(pos);
232                                         break;
233                                 }
234                         }
235                         list_for_each_safe(pos, tmpq, &scif_info.disconnected) {
236                                 tmpep = list_entry(pos,
237                                                    struct scif_endpt, list);
238                                 if (tmpep == aep) {
239                                         list_del(pos);
240                                         break;
241                                 }
242                         }
243                         mutex_unlock(&scif_info.connlock);
244                         scif_teardown_ep(aep);
245                         spin_lock(&scif_info.eplock);
246                         scif_add_epd_to_zombie_list(aep, SCIF_EPLOCK_HELD);
247                         ep->acceptcnt--;
248                 }
249
250                 spin_lock(&ep->lock);
251                 spin_unlock(&scif_info.eplock);
252
253                 /* Remove and reject any pending connection requests. */
254                 while (ep->conreqcnt) {
255                         conreq = list_first_entry(&ep->conlist,
256                                                   struct scif_conreq, list);
257                         list_del(&conreq->list);
258
259                         msg.uop = SCIF_CNCT_REJ;
260                         msg.dst.node = conreq->msg.src.node;
261                         msg.dst.port = conreq->msg.src.port;
262                         msg.payload[0] = conreq->msg.payload[0];
263                         msg.payload[1] = conreq->msg.payload[1];
264                         /*
265                          * No Error Handling on purpose for scif_nodeqp_send().
266                          * If the remote node is lost we still want free the
267                          * connection requests on the self node.
268                          */
269                         scif_nodeqp_send(&scif_dev[conreq->msg.src.node],
270                                          &msg);
271                         ep->conreqcnt--;
272                         kfree(conreq);
273                 }
274
275                 spin_unlock(&ep->lock);
276                 /* If a kSCIF accept is waiting wake it up */
277                 wake_up_interruptible(&ep->conwq);
278                 break;
279         }
280         }
281         scif_put_port(ep->port.port);
282         scif_teardown_ep(ep);
283         scif_add_epd_to_zombie_list(ep, !SCIF_EPLOCK_HELD);
284         return 0;
285 }
286 EXPORT_SYMBOL_GPL(scif_close);
287
288 /**
289  * scif_flush() - Wakes up any blocking accepts. The endpoint will no longer
290  *                      accept new connections.
291  * @epd: The end point returned from scif_open()
292  */
293 int __scif_flush(scif_epd_t epd)
294 {
295         struct scif_endpt *ep = (struct scif_endpt *)epd;
296
297         switch (ep->state) {
298         case SCIFEP_LISTENING:
299         {
300                 ep->state = SCIFEP_CLLISTEN;
301
302                 /* If an accept is waiting wake it up */
303                 wake_up_interruptible(&ep->conwq);
304                 break;
305         }
306         default:
307                 break;
308         }
309         return 0;
310 }
311
312 int scif_bind(scif_epd_t epd, u16 pn)
313 {
314         struct scif_endpt *ep = (struct scif_endpt *)epd;
315         int ret = 0;
316         int tmp;
317
318         dev_dbg(scif_info.mdev.this_device,
319                 "SCIFAPI bind: ep %p %s requested port number %d\n",
320                 ep, scif_ep_states[ep->state], pn);
321         if (pn) {
322                 /*
323                  * Similar to IETF RFC 1700, SCIF ports below
324                  * SCIF_ADMIN_PORT_END can only be bound by system (or root)
325                  * processes or by processes executed by privileged users.
326                  */
327                 if (pn < SCIF_ADMIN_PORT_END && !capable(CAP_SYS_ADMIN)) {
328                         ret = -EACCES;
329                         goto scif_bind_admin_exit;
330                 }
331         }
332
333         spin_lock(&ep->lock);
334         if (ep->state == SCIFEP_BOUND) {
335                 ret = -EINVAL;
336                 goto scif_bind_exit;
337         } else if (ep->state != SCIFEP_UNBOUND) {
338                 ret = -EISCONN;
339                 goto scif_bind_exit;
340         }
341
342         if (pn) {
343                 tmp = scif_rsrv_port(pn);
344                 if (tmp != pn) {
345                         ret = -EINVAL;
346                         goto scif_bind_exit;
347                 }
348         } else {
349                 pn = scif_get_new_port();
350                 if (!pn) {
351                         ret = -ENOSPC;
352                         goto scif_bind_exit;
353                 }
354         }
355
356         ep->state = SCIFEP_BOUND;
357         ep->port.node = scif_info.nodeid;
358         ep->port.port = pn;
359         ep->conn_async_state = ASYNC_CONN_IDLE;
360         ret = pn;
361         dev_dbg(scif_info.mdev.this_device,
362                 "SCIFAPI bind: bound to port number %d\n", pn);
363 scif_bind_exit:
364         spin_unlock(&ep->lock);
365 scif_bind_admin_exit:
366         return ret;
367 }
368 EXPORT_SYMBOL_GPL(scif_bind);
369
370 int scif_listen(scif_epd_t epd, int backlog)
371 {
372         struct scif_endpt *ep = (struct scif_endpt *)epd;
373
374         dev_dbg(scif_info.mdev.this_device,
375                 "SCIFAPI listen: ep %p %s\n", ep, scif_ep_states[ep->state]);
376         spin_lock(&ep->lock);
377         switch (ep->state) {
378         case SCIFEP_ZOMBIE:
379         case SCIFEP_CLOSING:
380         case SCIFEP_CLLISTEN:
381         case SCIFEP_UNBOUND:
382         case SCIFEP_DISCONNECTED:
383                 spin_unlock(&ep->lock);
384                 return -EINVAL;
385         case SCIFEP_LISTENING:
386         case SCIFEP_CONNECTED:
387         case SCIFEP_CONNECTING:
388         case SCIFEP_MAPPING:
389                 spin_unlock(&ep->lock);
390                 return -EISCONN;
391         case SCIFEP_BOUND:
392                 break;
393         }
394
395         ep->state = SCIFEP_LISTENING;
396         ep->backlog = backlog;
397
398         ep->conreqcnt = 0;
399         ep->acceptcnt = 0;
400         INIT_LIST_HEAD(&ep->conlist);
401         init_waitqueue_head(&ep->conwq);
402         INIT_LIST_HEAD(&ep->li_accept);
403         spin_unlock(&ep->lock);
404
405         /*
406          * Listen status is complete so delete the qp information not needed
407          * on a listen before placing on the list of listening ep's
408          */
409         scif_teardown_ep(ep);
410         ep->qp_info.qp = NULL;
411
412         spin_lock(&scif_info.eplock);
413         list_add_tail(&ep->list, &scif_info.listen);
414         spin_unlock(&scif_info.eplock);
415         return 0;
416 }
417 EXPORT_SYMBOL_GPL(scif_listen);