Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 | /*
* Intel MIC Platform Software Stack (MPSS)
*
* Copyright(c) 2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* Intel SCIF driver.
*
*/
#include "scif_main.h"
#include "scif_map.h"
void scif_cleanup_ep_qp(struct scif_endpt *ep)
{
struct scif_qp *qp = ep->qp_info.qp;
if (qp->outbound_q.rb_base) {
scif_iounmap((void *)qp->outbound_q.rb_base,
qp->outbound_q.size, ep->remote_dev);
qp->outbound_q.rb_base = NULL;
}
if (qp->remote_qp) {
scif_iounmap((void *)qp->remote_qp,
sizeof(struct scif_qp), ep->remote_dev);
qp->remote_qp = NULL;
}
if (qp->local_qp) {
scif_unmap_single(qp->local_qp, ep->remote_dev,
sizeof(struct scif_qp));
qp->local_qp = 0x0;
}
if (qp->local_buf) {
scif_unmap_single(qp->local_buf, ep->remote_dev,
SCIF_ENDPT_QP_SIZE);
qp->local_buf = 0;
}
}
void scif_teardown_ep(void *endpt)
{
struct scif_endpt *ep = endpt;
struct scif_qp *qp = ep->qp_info.qp;
if (qp) {
spin_lock(&ep->lock);
scif_cleanup_ep_qp(ep);
spin_unlock(&ep->lock);
kfree(qp->inbound_q.rb_base);
kfree(qp);
}
}
/*
* Enqueue the endpoint to the zombie list for cleanup.
* The endpoint should not be accessed once this API returns.
*/
void scif_add_epd_to_zombie_list(struct scif_endpt *ep, bool eplock_held)
{
if (!eplock_held)
mutex_lock(&scif_info.eplock);
spin_lock(&ep->lock);
ep->state = SCIFEP_ZOMBIE;
spin_unlock(&ep->lock);
list_add_tail(&ep->list, &scif_info.zombie);
scif_info.nr_zombies++;
if (!eplock_held)
mutex_unlock(&scif_info.eplock);
schedule_work(&scif_info.misc_work);
}
static struct scif_endpt *scif_find_listen_ep(u16 port)
{
struct scif_endpt *ep = NULL;
struct list_head *pos, *tmpq;
mutex_lock(&scif_info.eplock);
list_for_each_safe(pos, tmpq, &scif_info.listen) {
ep = list_entry(pos, struct scif_endpt, list);
if (ep->port.port == port) {
mutex_unlock(&scif_info.eplock);
return ep;
}
}
mutex_unlock(&scif_info.eplock);
return NULL;
}
void scif_cleanup_zombie_epd(void)
{
struct list_head *pos, *tmpq;
struct scif_endpt *ep;
mutex_lock(&scif_info.eplock);
list_for_each_safe(pos, tmpq, &scif_info.zombie) {
ep = list_entry(pos, struct scif_endpt, list);
if (scif_rma_ep_can_uninit(ep)) {
list_del(pos);
scif_info.nr_zombies--;
put_iova_domain(&ep->rma_info.iovad);
kfree(ep);
}
}
mutex_unlock(&scif_info.eplock);
}
/**
* scif_cnctreq() - Respond to SCIF_CNCT_REQ interrupt message
* @msg: Interrupt message
*
* This message is initiated by the remote node to request a connection
* to the local node. This function looks for an end point in the
* listen state on the requested port id.
*
* If it finds a listening port it places the connect request on the
* listening end points queue and wakes up any pending accept calls.
*
* If it does not find a listening end point it sends a connection
* reject message to the remote node.
*/
void scif_cnctreq(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = NULL;
struct scif_conreq *conreq;
conreq = kmalloc(sizeof(*conreq), GFP_KERNEL);
if (!conreq)
/* Lack of resources so reject the request. */
goto conreq_sendrej;
ep = scif_find_listen_ep(msg->dst.port);
if (!ep)
/* Send reject due to no listening ports */
goto conreq_sendrej_free;
else
spin_lock(&ep->lock);
if (ep->backlog <= ep->conreqcnt) {
/* Send reject due to too many pending requests */
spin_unlock(&ep->lock);
goto conreq_sendrej_free;
}
conreq->msg = *msg;
list_add_tail(&conreq->list, &ep->conlist);
ep->conreqcnt++;
wake_up_interruptible(&ep->conwq);
spin_unlock(&ep->lock);
return;
conreq_sendrej_free:
kfree(conreq);
conreq_sendrej:
msg->uop = SCIF_CNCT_REJ;
scif_nodeqp_send(&scif_dev[msg->src.node], msg);
}
/**
* scif_cnctgnt() - Respond to SCIF_CNCT_GNT interrupt message
* @msg: Interrupt message
*
* An accept() on the remote node has occurred and sent this message
* to indicate success. Place the end point in the MAPPING state and
* save the remote nodes memory information. Then wake up the connect
* request so it can finish.
*/
void scif_cnctgnt(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
spin_lock(&ep->lock);
if (SCIFEP_CONNECTING == ep->state) {
ep->peer.node = msg->src.node;
ep->peer.port = msg->src.port;
ep->qp_info.gnt_pld = msg->payload[1];
ep->remote_ep = msg->payload[2];
ep->state = SCIFEP_MAPPING;
wake_up(&ep->conwq);
}
spin_unlock(&ep->lock);
}
/**
* scif_cnctgnt_ack() - Respond to SCIF_CNCT_GNTACK interrupt message
* @msg: Interrupt message
*
* The remote connection request has finished mapping the local memory.
* Place the connection in the connected state and wake up the pending
* accept() call.
*/
void scif_cnctgnt_ack(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
mutex_lock(&scif_info.connlock);
spin_lock(&ep->lock);
/* New ep is now connected with all resources set. */
ep->state = SCIFEP_CONNECTED;
list_add_tail(&ep->list, &scif_info.connected);
wake_up(&ep->conwq);
spin_unlock(&ep->lock);
mutex_unlock(&scif_info.connlock);
}
/**
* scif_cnctgnt_nack() - Respond to SCIF_CNCT_GNTNACK interrupt message
* @msg: Interrupt message
*
* The remote connection request failed to map the local memory it was sent.
* Place the end point in the CLOSING state to indicate it and wake up
* the pending accept();
*/
void scif_cnctgnt_nack(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
spin_lock(&ep->lock);
ep->state = SCIFEP_CLOSING;
wake_up(&ep->conwq);
spin_unlock(&ep->lock);
}
/**
* scif_cnctrej() - Respond to SCIF_CNCT_REJ interrupt message
* @msg: Interrupt message
*
* The remote end has rejected the connection request. Set the end
* point back to the bound state and wake up the pending connect().
*/
void scif_cnctrej(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
spin_lock(&ep->lock);
if (SCIFEP_CONNECTING == ep->state) {
ep->state = SCIFEP_BOUND;
wake_up(&ep->conwq);
}
spin_unlock(&ep->lock);
}
/**
* scif_discnct() - Respond to SCIF_DISCNCT interrupt message
* @msg: Interrupt message
*
* The remote node has indicated close() has been called on its end
* point. Remove the local end point from the connected list, set its
* state to disconnected and ensure accesses to the remote node are
* shutdown.
*
* When all accesses to the remote end have completed then send a
* DISCNT_ACK to indicate it can remove its resources and complete
* the close routine.
*/
void scif_discnct(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = NULL;
struct scif_endpt *tmpep;
struct list_head *pos, *tmpq;
mutex_lock(&scif_info.connlock);
list_for_each_safe(pos, tmpq, &scif_info.connected) {
tmpep = list_entry(pos, struct scif_endpt, list);
/*
* The local ep may have sent a disconnect and and been closed
* due to a message response time out. It may have been
* allocated again and formed a new connection so we want to
* check if the remote ep matches
*/
if (((u64)tmpep == msg->payload[1]) &&
((u64)tmpep->remote_ep == msg->payload[0])) {
list_del(pos);
ep = tmpep;
spin_lock(&ep->lock);
break;
}
}
/*
* If the terminated end is not found then this side started closing
* before the other side sent the disconnect. If so the ep will no
* longer be on the connected list. Regardless the other side
* needs to be acked to let it know close is complete.
*/
if (!ep) {
mutex_unlock(&scif_info.connlock);
goto discnct_ack;
}
ep->state = SCIFEP_DISCONNECTED;
list_add_tail(&ep->list, &scif_info.disconnected);
wake_up_interruptible(&ep->sendwq);
wake_up_interruptible(&ep->recvwq);
spin_unlock(&ep->lock);
mutex_unlock(&scif_info.connlock);
discnct_ack:
msg->uop = SCIF_DISCNT_ACK;
scif_nodeqp_send(&scif_dev[msg->src.node], msg);
}
/**
* scif_discnct_ack() - Respond to SCIF_DISCNT_ACK interrupt message
* @msg: Interrupt message
*
* Remote side has indicated it has not more references to local resources
*/
void scif_discnt_ack(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
spin_lock(&ep->lock);
ep->state = SCIFEP_DISCONNECTED;
spin_unlock(&ep->lock);
complete(&ep->discon);
}
/**
* scif_clientsend() - Respond to SCIF_CLIENT_SEND interrupt message
* @msg: Interrupt message
*
* Remote side is confirming send or receive interrupt handling is complete.
*/
void scif_clientsend(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
spin_lock(&ep->lock);
if (SCIFEP_CONNECTED == ep->state)
wake_up_interruptible(&ep->recvwq);
spin_unlock(&ep->lock);
}
/**
* scif_clientrcvd() - Respond to SCIF_CLIENT_RCVD interrupt message
* @msg: Interrupt message
*
* Remote side is confirming send or receive interrupt handling is complete.
*/
void scif_clientrcvd(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
spin_lock(&ep->lock);
if (SCIFEP_CONNECTED == ep->state)
wake_up_interruptible(&ep->sendwq);
spin_unlock(&ep->lock);
}
|