Commit 76371c7c authored by Nikhil Rao's avatar Nikhil Rao Committed by Greg Kroah-Hartman

misc: mic: SCIF connections APIs i.e. accept and connect

SCIF connection APIs which establish a SCIF connection between
a pair of SCIF endpoints. A SCIF connection consists of a
dedicated queue-pair between the endpoints. Client messages are
sent over the queue-pair whereas the signaling associated with the
message is multiplexed over the node queue-pair. Similarly other
control messages such as exposing registered memory are also sent
over the node queue-pair. The SCIF endpoints must be in connected
state to exchange messages, register memory, map remote memory and
trigger DMA transfers. SCIF connections can be set up
asynchronously or synchronously.

Thanks to Johnnie S Peters for authoring parts of this patch during
early bring up of the SCIF driver.
Reviewed-by: default avatarAshutosh Dixit <ashutosh.dixit@intel.com>
Signed-off-by: default avatarSudeep Dutt <sudeep.dutt@intel.com>
Signed-off-by: default avatarNikhil Rao <nikhil.rao@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e9089f43
This diff is collapsed.
......@@ -76,6 +76,24 @@ void scif_add_epd_to_zombie_list(struct scif_endpt *ep, bool eplock_held)
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;
spin_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) {
spin_lock(&ep->lock);
spin_unlock(&scif_info.eplock);
return ep;
}
}
spin_unlock(&scif_info.eplock);
return NULL;
}
void scif_cleanup_zombie_epd(void)
{
struct list_head *pos, *tmpq;
......@@ -90,3 +108,214 @@ void scif_cleanup_zombie_epd(void)
}
spin_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;
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);
}
......@@ -144,5 +144,13 @@ int scif_rsrv_port(u16 port);
void scif_get_port(u16 port);
int scif_get_new_port(void);
void scif_put_port(u16 port);
void scif_cnctreq(struct scif_dev *scifdev, struct scifmsg *msg);
void scif_cnctgnt(struct scif_dev *scifdev, struct scifmsg *msg);
void scif_cnctgnt_ack(struct scif_dev *scifdev, struct scifmsg *msg);
void scif_cnctgnt_nack(struct scif_dev *scifdev, struct scifmsg *msg);
void scif_cnctrej(struct scif_dev *scifdev, struct scifmsg *msg);
void scif_discnct(struct scif_dev *scifdev, struct scifmsg *msg);
void scif_discnt_ack(struct scif_dev *scifdev, struct scifmsg *msg);
int __scif_connect(scif_epd_t epd, struct scif_port_id *dst, bool non_block);
int __scif_flush(scif_epd_t epd);
#endif /* SCIF_EPD_H */
......@@ -68,6 +68,7 @@ static long scif_fdioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
struct scif_endpt *priv = f->private_data;
void __user *argp = (void __user *)arg;
int err = 0;
bool non_block = false;
non_block = !!(f->f_flags & O_NONBLOCK);
......@@ -91,6 +92,111 @@ static long scif_fdioctl(struct file *f, unsigned int cmd, unsigned long arg)
}
case SCIF_LISTEN:
return scif_listen(priv, arg);
case SCIF_CONNECT:
{
struct scifioctl_connect req;
struct scif_endpt *ep = (struct scif_endpt *)priv;
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
err = __scif_connect(priv, &req.peer, non_block);
if (err < 0)
return err;
req.self.node = ep->port.node;
req.self.port = ep->port.port;
if (copy_to_user(argp, &req, sizeof(req)))
return -EFAULT;
return 0;
}
/*
* Accept is done in two halves. The request ioctl does the basic
* functionality of accepting the request and returning the information
* about it including the internal ID of the end point. The register
* is done with the internal ID on a new file descriptor opened by the
* requesting process.
*/
case SCIF_ACCEPTREQ:
{
struct scifioctl_accept request;
scif_epd_t *ep = (scif_epd_t *)&request.endpt;
if (copy_from_user(&request, argp, sizeof(request)))
return -EFAULT;
err = scif_accept(priv, &request.peer, ep, request.flags);
if (err < 0)
return err;
if (copy_to_user(argp, &request, sizeof(request))) {
scif_close(*ep);
return -EFAULT;
}
/*
* Add to the list of user mode eps where the second half
* of the accept is not yet completed.
*/
spin_lock(&scif_info.eplock);
list_add_tail(&((*ep)->miacceptlist), &scif_info.uaccept);
list_add_tail(&((*ep)->liacceptlist), &priv->li_accept);
(*ep)->listenep = priv;
priv->acceptcnt++;
spin_unlock(&scif_info.eplock);
return 0;
}
case SCIF_ACCEPTREG:
{
struct scif_endpt *priv = f->private_data;
struct scif_endpt *newep;
struct scif_endpt *lisep;
struct scif_endpt *fep = NULL;
struct scif_endpt *tmpep;
struct list_head *pos, *tmpq;
/* Finally replace the pointer to the accepted endpoint */
if (copy_from_user(&newep, argp, sizeof(void *)))
return -EFAULT;
/* Remove form the user accept queue */
spin_lock(&scif_info.eplock);
list_for_each_safe(pos, tmpq, &scif_info.uaccept) {
tmpep = list_entry(pos,
struct scif_endpt, miacceptlist);
if (tmpep == newep) {
list_del(pos);
fep = tmpep;
break;
}
}
if (!fep) {
spin_unlock(&scif_info.eplock);
return -ENOENT;
}
lisep = newep->listenep;
list_for_each_safe(pos, tmpq, &lisep->li_accept) {
tmpep = list_entry(pos,
struct scif_endpt, liacceptlist);
if (tmpep == newep) {
list_del(pos);
lisep->acceptcnt--;
break;
}
}
spin_unlock(&scif_info.eplock);
/* Free the resources automatically created from the open. */
scif_teardown_ep(priv);
scif_add_epd_to_zombie_list(priv, !SCIF_EPLOCK_HELD);
f->private_data = newep;
return 0;
}
}
return -EINVAL;
}
......
......@@ -331,6 +331,7 @@ static int _scif_init(void)
scif_info.en_msg_log = 0;
scif_info.p2p_enable = 1;
INIT_WORK(&scif_info.misc_work, scif_misc_handler);
INIT_WORK(&scif_info.conn_work, scif_conn_handler);
idr_init(&scif_ports);
return 0;
}
......
......@@ -20,6 +20,41 @@
#include "scif_main.h"
#include "scif_map.h"
/**
* scif_invalidate_ep() - Set state for all connected endpoints
* to disconnected and wake up all send/recv waitqueues
*/
static void scif_invalidate_ep(int node)
{
struct scif_endpt *ep;
struct list_head *pos, *tmpq;
flush_work(&scif_info.conn_work);
mutex_lock(&scif_info.connlock);
list_for_each_safe(pos, tmpq, &scif_info.disconnected) {
ep = list_entry(pos, struct scif_endpt, list);
if (ep->remote_dev->node == node) {
spin_lock(&ep->lock);
scif_cleanup_ep_qp(ep);
spin_unlock(&ep->lock);
}
}
list_for_each_safe(pos, tmpq, &scif_info.connected) {
ep = list_entry(pos, struct scif_endpt, list);
if (ep->remote_dev->node == node) {
list_del(pos);
spin_lock(&ep->lock);
ep->state = SCIFEP_DISCONNECTED;
list_add_tail(&ep->list, &scif_info.disconnected);
scif_cleanup_ep_qp(ep);
wake_up_interruptible(&ep->sendwq);
wake_up_interruptible(&ep->recvwq);
spin_unlock(&ep->lock);
}
}
mutex_unlock(&scif_info.connlock);
}
void scif_free_qp(struct scif_dev *scifdev)
{
struct scif_qp *qp = scifdev->qpairs;
......@@ -91,6 +126,7 @@ void scif_cleanup_scifdev(struct scif_dev *dev)
scif_destroy_intr_wq(dev);
}
scif_destroy_p2p(dev);
scif_invalidate_ep(dev->node);
scif_send_acks(dev);
if (!dev->node && scif_info.card_initiated_exit) {
/*
......
......@@ -563,7 +563,14 @@ static char *message_types[] = {"BAD",
"SCIF_NODE_ADD_ACK",
"SCIF_NODE_ADD_NACK",
"REMOVE_NODE",
"REMOVE_NODE_ACK"};
"REMOVE_NODE_ACK",
"CNCT_REQ",
"CNCT_GNT",
"CNCT_GNTACK",
"CNCT_GNTNACK",
"CNCT_REJ",
"DISCNCT",
"DISCNT_ACK"};
static void
scif_display_message(struct scif_dev *scifdev, struct scifmsg *msg,
......@@ -964,6 +971,13 @@ static void (*scif_intr_func[SCIF_MAX_MSG + 1])
scif_node_add_nack, /* SCIF_NODE_ADD_NACK */
scif_node_remove, /* SCIF_NODE_REMOVE */
scif_node_remove_ack, /* SCIF_NODE_REMOVE_ACK */
scif_cnctreq, /* SCIF_CNCT_REQ */
scif_cnctgnt, /* SCIF_CNCT_GNT */
scif_cnctgnt_ack, /* SCIF_CNCT_GNTACK */
scif_cnctgnt_nack, /* SCIF_CNCT_GNTNACK */
scif_cnctrej, /* SCIF_CNCT_REJ */
scif_discnct, /* SCIF_DISCNCT */
scif_discnt_ack, /* SCIF_DISCNT_ACK */
};
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment