2015-04-29 05:32:34 -07:00
/*
* 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"
2015-04-29 05:32:36 -07:00
/**
* 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 ) {
2015-09-29 18:16:25 -07:00
scif_unmap_all_windows ( ep ) ;
2015-04-29 05:32:36 -07:00
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 ) ;
2015-09-29 18:16:25 -07:00
scif_unmap_all_windows ( ep ) ;
2015-04-29 05:32:36 -07:00
}
}
mutex_unlock ( & scif_info . connlock ) ;
}
2015-04-29 05:32:34 -07:00
void scif_free_qp ( struct scif_dev * scifdev )
{
struct scif_qp * qp = scifdev - > qpairs ;
if ( ! qp )
return ;
2015-09-29 18:16:25 -07:00
scif_unmap_single ( qp - > local_buf , scifdev , qp - > inbound_q . size ) ;
kfree ( qp - > inbound_q . rb_base ) ;
2015-04-29 05:32:34 -07:00
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 ) ;
}
2015-09-29 18:16:25 -07:00
flush_work ( & scif_info . misc_work ) ;
2015-04-29 05:32:34 -07:00
scif_destroy_p2p ( dev ) ;
2015-04-29 05:32:36 -07:00
scif_invalidate_ep ( dev - > node ) ;
2015-09-29 18:16:25 -07:00
scif_zap_mmaps ( dev - > node ) ;
scif_cleanup_rma_for_zombies ( dev - > node ) ;
flush_work ( & scif_info . misc_work ) ;
2015-04-29 05:32:34 -07:00
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 ] ;
2015-09-29 18:11:15 -07:00
if ( scif_peer_unregister_device ( scifdev ) )
2015-04-29 05:32:34 -07:00
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 , & notif_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 ) ;
}
2015-04-29 05:32:37 -07:00
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 ) ;
}