|
| 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_peer_bus.h" |
| 19 | + |
| 20 | +#include "scif_main.h" |
| 21 | +#include "scif_map.h" |
| 22 | + |
| 23 | +void scif_free_qp(struct scif_dev *scifdev) |
| 24 | +{ |
| 25 | + struct scif_qp *qp = scifdev->qpairs; |
| 26 | + |
| 27 | + if (!qp) |
| 28 | + return; |
| 29 | + scif_free_coherent((void *)qp->inbound_q.rb_base, |
| 30 | + qp->local_buf, scifdev, qp->inbound_q.size); |
| 31 | + scif_unmap_single(qp->local_qp, scifdev, sizeof(struct scif_qp)); |
| 32 | + kfree(scifdev->qpairs); |
| 33 | + scifdev->qpairs = NULL; |
| 34 | +} |
| 35 | + |
| 36 | +static void scif_cleanup_qp(struct scif_dev *dev) |
| 37 | +{ |
| 38 | + struct scif_qp *qp = &dev->qpairs[0]; |
| 39 | + |
| 40 | + if (!qp) |
| 41 | + return; |
| 42 | + scif_iounmap((void *)qp->remote_qp, sizeof(struct scif_qp), dev); |
| 43 | + scif_iounmap((void *)qp->outbound_q.rb_base, |
| 44 | + sizeof(struct scif_qp), dev); |
| 45 | + qp->remote_qp = NULL; |
| 46 | + qp->local_write = 0; |
| 47 | + qp->inbound_q.current_write_offset = 0; |
| 48 | + qp->inbound_q.current_read_offset = 0; |
| 49 | + if (scifdev_is_p2p(dev)) |
| 50 | + scif_free_qp(dev); |
| 51 | +} |
| 52 | + |
| 53 | +void scif_send_acks(struct scif_dev *dev) |
| 54 | +{ |
| 55 | + struct scifmsg msg; |
| 56 | + |
| 57 | + if (dev->node_remove_ack_pending) { |
| 58 | + msg.uop = SCIF_NODE_REMOVE_ACK; |
| 59 | + msg.src.node = scif_info.nodeid; |
| 60 | + msg.dst.node = SCIF_MGMT_NODE; |
| 61 | + msg.payload[0] = dev->node; |
| 62 | + scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], &msg); |
| 63 | + dev->node_remove_ack_pending = false; |
| 64 | + } |
| 65 | + if (dev->exit_ack_pending) { |
| 66 | + msg.uop = SCIF_EXIT_ACK; |
| 67 | + msg.src.node = scif_info.nodeid; |
| 68 | + msg.dst.node = dev->node; |
| 69 | + scif_nodeqp_send(dev, &msg); |
| 70 | + dev->exit_ack_pending = false; |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +/* |
| 75 | + * scif_cleanup_scifdev |
| 76 | + * |
| 77 | + * @dev: Remote SCIF device. |
| 78 | + * Uninitialize SCIF data structures for remote SCIF device. |
| 79 | + */ |
| 80 | +void scif_cleanup_scifdev(struct scif_dev *dev) |
| 81 | +{ |
| 82 | + struct scif_hw_dev *sdev = dev->sdev; |
| 83 | + |
| 84 | + if (!dev->sdev) |
| 85 | + return; |
| 86 | + if (scifdev_is_p2p(dev)) { |
| 87 | + if (dev->cookie) { |
| 88 | + sdev->hw_ops->free_irq(sdev, dev->cookie, dev); |
| 89 | + dev->cookie = NULL; |
| 90 | + } |
| 91 | + scif_destroy_intr_wq(dev); |
| 92 | + } |
| 93 | + scif_destroy_p2p(dev); |
| 94 | + scif_send_acks(dev); |
| 95 | + if (!dev->node && scif_info.card_initiated_exit) { |
| 96 | + /* |
| 97 | + * Send an SCIF_EXIT message which is the last message from MIC |
| 98 | + * to the Host and wait for a SCIF_EXIT_ACK |
| 99 | + */ |
| 100 | + scif_send_exit(dev); |
| 101 | + scif_info.card_initiated_exit = false; |
| 102 | + } |
| 103 | + scif_cleanup_qp(dev); |
| 104 | +} |
| 105 | + |
| 106 | +/* |
| 107 | + * scif_remove_node: |
| 108 | + * |
| 109 | + * @node: Node to remove |
| 110 | + */ |
| 111 | +void scif_handle_remove_node(int node) |
| 112 | +{ |
| 113 | + struct scif_dev *scifdev = &scif_dev[node]; |
| 114 | + struct scif_peer_dev *spdev; |
| 115 | + |
| 116 | + rcu_read_lock(); |
| 117 | + spdev = rcu_dereference(scifdev->spdev); |
| 118 | + rcu_read_unlock(); |
| 119 | + if (spdev) |
| 120 | + scif_peer_unregister_device(spdev); |
| 121 | + else |
| 122 | + scif_send_acks(scifdev); |
| 123 | +} |
| 124 | + |
| 125 | +static int scif_send_rmnode_msg(int node, int remove_node) |
| 126 | +{ |
| 127 | + struct scifmsg notif_msg; |
| 128 | + struct scif_dev *dev = &scif_dev[node]; |
| 129 | + |
| 130 | + notif_msg.uop = SCIF_NODE_REMOVE; |
| 131 | + notif_msg.src.node = scif_info.nodeid; |
| 132 | + notif_msg.dst.node = node; |
| 133 | + notif_msg.payload[0] = remove_node; |
| 134 | + return scif_nodeqp_send(dev, ¬if_msg); |
| 135 | +} |
| 136 | + |
| 137 | +/** |
| 138 | + * scif_node_disconnect: |
| 139 | + * |
| 140 | + * @node_id[in]: source node id. |
| 141 | + * @mgmt_initiated: Disconnection initiated from the mgmt node |
| 142 | + * |
| 143 | + * Disconnect a node from the scif network. |
| 144 | + */ |
| 145 | +void scif_disconnect_node(u32 node_id, bool mgmt_initiated) |
| 146 | +{ |
| 147 | + int ret; |
| 148 | + int msg_cnt = 0; |
| 149 | + u32 i = 0; |
| 150 | + struct scif_dev *scifdev = &scif_dev[node_id]; |
| 151 | + |
| 152 | + if (!node_id) |
| 153 | + return; |
| 154 | + |
| 155 | + atomic_set(&scifdev->disconn_rescnt, 0); |
| 156 | + |
| 157 | + /* Destroy p2p network */ |
| 158 | + for (i = 1; i <= scif_info.maxid; i++) { |
| 159 | + if (i == node_id) |
| 160 | + continue; |
| 161 | + ret = scif_send_rmnode_msg(i, node_id); |
| 162 | + if (!ret) |
| 163 | + msg_cnt++; |
| 164 | + } |
| 165 | + /* Wait for the remote nodes to respond with SCIF_NODE_REMOVE_ACK */ |
| 166 | + ret = wait_event_timeout(scifdev->disconn_wq, |
| 167 | + (atomic_read(&scifdev->disconn_rescnt) |
| 168 | + == msg_cnt), SCIF_NODE_ALIVE_TIMEOUT); |
| 169 | + /* Tell the card to clean up */ |
| 170 | + if (mgmt_initiated && _scifdev_alive(scifdev)) |
| 171 | + /* |
| 172 | + * Send an SCIF_EXIT message which is the last message from Host |
| 173 | + * to the MIC and wait for a SCIF_EXIT_ACK |
| 174 | + */ |
| 175 | + scif_send_exit(scifdev); |
| 176 | + atomic_set(&scifdev->disconn_rescnt, 0); |
| 177 | + /* Tell the mgmt node to clean up */ |
| 178 | + ret = scif_send_rmnode_msg(SCIF_MGMT_NODE, node_id); |
| 179 | + if (!ret) |
| 180 | + /* Wait for mgmt node to respond with SCIF_NODE_REMOVE_ACK */ |
| 181 | + wait_event_timeout(scifdev->disconn_wq, |
| 182 | + (atomic_read(&scifdev->disconn_rescnt) == 1), |
| 183 | + SCIF_NODE_ALIVE_TIMEOUT); |
| 184 | +} |
0 commit comments