| /* |
| * 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_peer_bus.h" |
| |
| #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) { |
| scif_unmap_all_windows(ep); |
| 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); |
| scif_unmap_all_windows(ep); |
| } |
| } |
| mutex_unlock(&scif_info.connlock); |
| } |
| |
| void scif_free_qp(struct scif_dev *scifdev) |
| { |
| struct scif_qp *qp = scifdev->qpairs; |
| |
| if (!qp) |
| return; |
| scif_unmap_single(qp->local_buf, scifdev, qp->inbound_q.size); |
| kfree(qp->inbound_q.rb_base); |
| scif_unmap_single(qp->local_qp, scifdev, sizeof(struct scif_qp)); |
| kfree(scifdev->qpairs); |
| scifdev->qpairs = NULL; |
| } |
| |
| static void scif_cleanup_qp(struct scif_dev *dev) |
| { |
| struct scif_qp *qp = &dev->qpairs[0]; |
| |
| if (!qp) |
| return; |
| scif_iounmap((void *)qp->remote_qp, sizeof(struct scif_qp), dev); |
| scif_iounmap((void *)qp->outbound_q.rb_base, |
| sizeof(struct scif_qp), dev); |
| qp->remote_qp = NULL; |
| qp->local_write = 0; |
| qp->inbound_q.current_write_offset = 0; |
| qp->inbound_q.current_read_offset = 0; |
| if (scifdev_is_p2p(dev)) |
| scif_free_qp(dev); |
| } |
| |
| void scif_send_acks(struct scif_dev *dev) |
| { |
| struct scifmsg msg; |
| |
| if (dev->node_remove_ack_pending) { |
| msg.uop = SCIF_NODE_REMOVE_ACK; |
| msg.src.node = scif_info.nodeid; |
| msg.dst.node = SCIF_MGMT_NODE; |
| msg.payload[0] = dev->node; |
| scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], &msg); |
| dev->node_remove_ack_pending = false; |
| } |
| if (dev->exit_ack_pending) { |
| msg.uop = SCIF_EXIT_ACK; |
| msg.src.node = scif_info.nodeid; |
| msg.dst.node = dev->node; |
| scif_nodeqp_send(dev, &msg); |
| dev->exit_ack_pending = false; |
| } |
| } |
| |
| /* |
| * scif_cleanup_scifdev |
| * |
| * @dev: Remote SCIF device. |
| * Uninitialize SCIF data structures for remote SCIF device. |
| */ |
| void scif_cleanup_scifdev(struct scif_dev *dev) |
| { |
| struct scif_hw_dev *sdev = dev->sdev; |
| |
| if (!dev->sdev) |
| return; |
| if (scifdev_is_p2p(dev)) { |
| if (dev->cookie) { |
| sdev->hw_ops->free_irq(sdev, dev->cookie, dev); |
| dev->cookie = NULL; |
| } |
| scif_destroy_intr_wq(dev); |
| } |
| flush_work(&scif_info.misc_work); |
| scif_destroy_p2p(dev); |
| scif_invalidate_ep(dev->node); |
| scif_zap_mmaps(dev->node); |
| scif_cleanup_rma_for_zombies(dev->node); |
| flush_work(&scif_info.misc_work); |
| scif_send_acks(dev); |
| if (!dev->node && scif_info.card_initiated_exit) { |
| /* |
| * Send an SCIF_EXIT message which is the last message from MIC |
| * to the Host and wait for a SCIF_EXIT_ACK |
| */ |
| scif_send_exit(dev); |
| scif_info.card_initiated_exit = false; |
| } |
| scif_cleanup_qp(dev); |
| } |
| |
| /* |
| * scif_remove_node: |
| * |
| * @node: Node to remove |
| */ |
| void scif_handle_remove_node(int node) |
| { |
| struct scif_dev *scifdev = &scif_dev[node]; |
| |
| if (scif_peer_unregister_device(scifdev)) |
| scif_send_acks(scifdev); |
| } |
| |
| static int scif_send_rmnode_msg(int node, int remove_node) |
| { |
| struct scifmsg notif_msg; |
| struct scif_dev *dev = &scif_dev[node]; |
| |
| notif_msg.uop = SCIF_NODE_REMOVE; |
| notif_msg.src.node = scif_info.nodeid; |
| notif_msg.dst.node = node; |
| notif_msg.payload[0] = remove_node; |
| return scif_nodeqp_send(dev, ¬if_msg); |
| } |
| |
| /** |
| * scif_node_disconnect: |
| * |
| * @node_id[in]: source node id. |
| * @mgmt_initiated: Disconnection initiated from the mgmt node |
| * |
| * Disconnect a node from the scif network. |
| */ |
| void scif_disconnect_node(u32 node_id, bool mgmt_initiated) |
| { |
| int ret; |
| int msg_cnt = 0; |
| u32 i = 0; |
| struct scif_dev *scifdev = &scif_dev[node_id]; |
| |
| if (!node_id) |
| return; |
| |
| atomic_set(&scifdev->disconn_rescnt, 0); |
| |
| /* Destroy p2p network */ |
| for (i = 1; i <= scif_info.maxid; i++) { |
| if (i == node_id) |
| continue; |
| ret = scif_send_rmnode_msg(i, node_id); |
| if (!ret) |
| msg_cnt++; |
| } |
| /* Wait for the remote nodes to respond with SCIF_NODE_REMOVE_ACK */ |
| ret = wait_event_timeout(scifdev->disconn_wq, |
| (atomic_read(&scifdev->disconn_rescnt) |
| == msg_cnt), SCIF_NODE_ALIVE_TIMEOUT); |
| /* Tell the card to clean up */ |
| if (mgmt_initiated && _scifdev_alive(scifdev)) |
| /* |
| * Send an SCIF_EXIT message which is the last message from Host |
| * to the MIC and wait for a SCIF_EXIT_ACK |
| */ |
| scif_send_exit(scifdev); |
| atomic_set(&scifdev->disconn_rescnt, 0); |
| /* Tell the mgmt node to clean up */ |
| ret = scif_send_rmnode_msg(SCIF_MGMT_NODE, node_id); |
| if (!ret) |
| /* Wait for mgmt node to respond with SCIF_NODE_REMOVE_ACK */ |
| wait_event_timeout(scifdev->disconn_wq, |
| (atomic_read(&scifdev->disconn_rescnt) == 1), |
| SCIF_NODE_ALIVE_TIMEOUT); |
| } |
| |
| void scif_get_node_info(void) |
| { |
| struct scifmsg msg; |
| DECLARE_COMPLETION_ONSTACK(node_info); |
| |
| msg.uop = SCIF_GET_NODE_INFO; |
| msg.src.node = scif_info.nodeid; |
| msg.dst.node = SCIF_MGMT_NODE; |
| msg.payload[3] = (u64)&node_info; |
| |
| if ((scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], &msg))) |
| return; |
| |
| /* Wait for a response with SCIF_GET_NODE_INFO */ |
| wait_for_completion(&node_info); |
| } |