2015-04-29 05:32:31 -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_main.h"
# include "../bus/scif_bus.h"
# include "scif_peer_bus.h"
static inline struct scif_peer_dev *
dev_to_scif_peer ( struct device * dev )
{
return container_of ( dev , struct scif_peer_dev , dev ) ;
}
2015-09-29 18:11:15 -07:00
struct bus_type scif_peer_bus = {
. name = " scif_peer_bus " ,
} ;
2015-04-29 05:32:31 -07:00
2015-09-29 18:11:15 -07:00
static void scif_peer_release_dev ( struct device * d )
2015-04-29 05:32:31 -07:00
{
2015-09-29 18:11:15 -07:00
struct scif_peer_dev * sdev = dev_to_scif_peer ( d ) ;
struct scif_dev * scifdev = & scif_dev [ sdev - > dnode ] ;
scif_cleanup_scifdev ( scifdev ) ;
kfree ( sdev ) ;
2015-04-29 05:32:31 -07:00
}
2015-09-29 18:11:15 -07:00
static int scif_peer_initialize_device ( struct scif_dev * scifdev )
2015-04-29 05:32:31 -07:00
{
2015-09-29 18:11:15 -07:00
struct scif_peer_dev * spdev ;
int ret ;
2015-04-29 05:32:31 -07:00
2015-09-29 18:11:15 -07:00
spdev = kzalloc ( sizeof ( * spdev ) , GFP_KERNEL ) ;
if ( ! spdev ) {
ret = - ENOMEM ;
goto err ;
}
2015-04-29 05:32:31 -07:00
2015-09-29 18:11:15 -07:00
spdev - > dev . parent = scifdev - > sdev - > dev . parent ;
spdev - > dev . release = scif_peer_release_dev ;
spdev - > dnode = scifdev - > node ;
spdev - > dev . bus = & scif_peer_bus ;
dev_set_name ( & spdev - > dev , " scif_peer-dev%u " , spdev - > dnode ) ;
device_initialize ( & spdev - > dev ) ;
get_device ( & spdev - > dev ) ;
rcu_assign_pointer ( scifdev - > spdev , spdev ) ;
2015-04-29 05:32:31 -07:00
2015-09-29 18:11:15 -07:00
mutex_lock ( & scif_info . conflock ) ;
scif_info . total + + ;
scif_info . maxid = max_t ( u32 , spdev - > dnode , scif_info . maxid ) ;
mutex_unlock ( & scif_info . conflock ) ;
2015-04-29 05:32:31 -07:00
return 0 ;
2015-09-29 18:11:15 -07:00
err :
dev_err ( & scifdev - > sdev - > dev ,
" dnode %d: initialize_device rc %d \n " , scifdev - > node , ret ) ;
return ret ;
2015-04-29 05:32:31 -07:00
}
2015-09-29 18:11:15 -07:00
static int scif_peer_add_device ( struct scif_dev * scifdev )
2015-04-29 05:32:31 -07:00
{
2015-09-29 18:11:15 -07:00
struct scif_peer_dev * spdev = rcu_dereference ( scifdev - > spdev ) ;
2015-09-29 18:16:25 -07:00
char pool_name [ 16 ] ;
2015-09-29 18:11:15 -07:00
int ret ;
ret = device_add ( & spdev - > dev ) ;
put_device ( & spdev - > dev ) ;
if ( ret ) {
dev_err ( & scifdev - > sdev - > dev ,
" dnode %d: peer device_add failed \n " , scifdev - > node ) ;
goto put_spdev ;
}
2015-09-29 18:16:25 -07:00
scnprintf ( pool_name , sizeof ( pool_name ) , " scif-%d " , spdev - > dnode ) ;
scifdev - > signal_pool = dmam_pool_create ( pool_name , & scifdev - > sdev - > dev ,
sizeof ( struct scif_status ) , 1 ,
0 ) ;
if ( ! scifdev - > signal_pool ) {
dev_err ( & scifdev - > sdev - > dev ,
" dnode %d: dmam_pool_create failed \n " , scifdev - > node ) ;
ret = - ENOMEM ;
goto del_spdev ;
}
2015-09-29 18:11:15 -07:00
dev_dbg ( & spdev - > dev , " Added peer dnode %d \n " , spdev - > dnode ) ;
return 0 ;
2015-09-29 18:16:25 -07:00
del_spdev :
device_del ( & spdev - > dev ) ;
2015-09-29 18:11:15 -07:00
put_spdev :
RCU_INIT_POINTER ( scifdev - > spdev , NULL ) ;
synchronize_rcu ( ) ;
put_device ( & spdev - > dev ) ;
mutex_lock ( & scif_info . conflock ) ;
scif_info . total - - ;
mutex_unlock ( & scif_info . conflock ) ;
return ret ;
2015-04-29 05:32:31 -07:00
}
2015-09-29 18:11:15 -07:00
void scif_add_peer_device ( struct work_struct * work )
2015-04-29 05:32:31 -07:00
{
2015-09-29 18:11:15 -07:00
struct scif_dev * scifdev = container_of ( work , struct scif_dev ,
peer_add_work ) ;
scif_peer_add_device ( scifdev ) ;
2015-04-29 05:32:31 -07:00
}
2015-09-29 18:11:15 -07:00
/*
* Peer device registration is split into a device_initialize and a device_add .
* The reason for doing this is as follows : First , peer device registration
* itself cannot be done in the message processing thread and must be delegated
* to another workqueue , otherwise if SCIF client probe , called during peer
* device registration , calls scif_connect ( . . ) , it will block the message
* processing thread causing a deadlock . Next , device_initialize is done in the
* " top-half " message processing thread and device_add in the " bottom-half "
* workqueue . If this is not done , SCIF_CNCT_REQ message processing executing
* concurrently with SCIF_INIT message processing is unable to get a reference
* on the peer device , thereby failing the connect request .
*/
void scif_peer_register_device ( struct scif_dev * scifdev )
2015-04-29 05:32:31 -07:00
{
2015-09-29 18:11:15 -07:00
int ret ;
2015-04-29 05:32:31 -07:00
2015-09-29 18:11:15 -07:00
mutex_lock ( & scifdev - > lock ) ;
ret = scif_peer_initialize_device ( scifdev ) ;
if ( ret )
goto exit ;
schedule_work ( & scifdev - > peer_add_work ) ;
exit :
mutex_unlock ( & scifdev - > lock ) ;
2015-04-29 05:32:31 -07:00
}
2015-09-29 18:11:15 -07:00
int scif_peer_unregister_device ( struct scif_dev * scifdev )
2015-04-29 05:32:31 -07:00
{
struct scif_peer_dev * spdev ;
2015-09-29 18:11:15 -07:00
mutex_lock ( & scifdev - > lock ) ;
/* Flush work to ensure device register is complete */
flush_work ( & scifdev - > peer_add_work ) ;
2015-04-29 05:32:31 -07:00
/*
2015-09-29 18:11:15 -07:00
* Continue holding scifdev - > lock since theoretically unregister_device
* can be called simultaneously from multiple threads
2015-04-29 05:32:31 -07:00
*/
2015-09-29 18:11:15 -07:00
spdev = rcu_dereference ( scifdev - > spdev ) ;
if ( ! spdev ) {
mutex_unlock ( & scifdev - > lock ) ;
return - ENODEV ;
}
RCU_INIT_POINTER ( scifdev - > spdev , NULL ) ;
synchronize_rcu ( ) ;
mutex_unlock ( & scifdev - > lock ) ;
dev_dbg ( & spdev - > dev , " Removing peer dnode %d \n " , spdev - > dnode ) ;
device_unregister ( & spdev - > dev ) ;
mutex_lock ( & scif_info . conflock ) ;
scif_info . total - - ;
mutex_unlock ( & scif_info . conflock ) ;
return 0 ;
2015-04-29 05:32:31 -07:00
}
int scif_peer_bus_init ( void )
{
return bus_register ( & scif_peer_bus ) ;
}
void scif_peer_bus_exit ( void )
{
bus_unregister ( & scif_peer_bus ) ;
}