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