@ -24,93 +24,138 @@ dev_to_scif_peer(struct device *dev)
return container_of ( dev , struct scif_peer_dev , dev ) ;
}
static inline struct scif_peer_driver *
drv_to_scif_peer ( struct device_driver * drv )
{
return container_of ( drv , struct scif_peer_driver , driver ) ;
}
struct bus_type scif_peer_bus = {
. name = " scif_peer_bus " ,
} ;
static int scif_peer_dev_match ( struct device * dv , struct device_driver * dr )
static void scif_peer_release_dev ( struct device * d )
{
return ! strncmp ( dev_name ( dv ) , dr - > name , 4 ) ;
struct scif_peer_dev * sdev = dev_to_scif_peer ( d ) ;
struct scif_dev * scifdev = & scif_dev [ sdev - > dnode ] ;
scif_cleanup_scifdev ( scifdev ) ;
kfree ( sdev ) ;
}
static int scif_peer_dev_probe ( struct device * d )
static int scif_peer_initialize_devic e ( struct scif_ dev * scif dev )
{
struct scif_peer_dev * dev = dev_to_scif_peer ( d ) ;
struct scif_peer_driver * drv = drv_to_scif_peer ( dev - > dev . driver ) ;
struct scif_peer_dev * sp dev;
int ret ;
return drv - > probe ( dev ) ;
}
spdev = kzalloc ( sizeof ( * spdev ) , GFP_KERNEL ) ;
if ( ! spdev ) {
ret = - ENOMEM ;
goto err ;
}
static int scif_peer_dev_remove ( struct device * d )
{
struct scif_peer_dev * dev = dev_to_scif_peer ( d ) ;
struct scif_peer_driver * drv = drv_to_scif_peer ( dev - > dev . driver ) ;
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 ) ;
drv - > remove ( dev ) ;
mutex_lock ( & scif_info . conflock ) ;
scif_info . total + + ;
scif_info . maxid = max_t ( u32 , spdev - > dnode , scif_info . maxid ) ;
mutex_unlock ( & scif_info . conflock ) ;
return 0 ;
err :
dev_err ( & scifdev - > sdev - > dev ,
" dnode %d: initialize_device rc %d \n " , scifdev - > node , ret ) ;
return ret ;
}
static struct bus_type scif_peer_bus = {
. name = " scif_peer_bus " ,
. match = scif_peer_dev_match ,
. probe = scif_peer_dev_probe ,
. remove = scif_peer_dev_remove ,
} ;
int scif_peer_register_driver ( struct scif_peer_driver * driver )
static int scif_peer_add_device ( struct scif_dev * scifdev )
{
driver - > driver . bus = & scif_peer_bus ;
return driver_register ( & driver - > driver ) ;
struct scif_peer_dev * spdev = rcu_dereference ( scifdev - > spdev ) ;
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 ;
}
dev_dbg ( & spdev - > dev , " Added peer dnode %d \n " , spdev - > dnode ) ;
return 0 ;
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 ;
}
void scif_peer_unregister_driver ( struct scif_peer_driver * driver )
void scif_add_peer_device ( struct work_struct * work )
{
driver_unregister ( & driver - > driver ) ;
struct scif_dev * scifdev = container_of ( work , struct scif_dev ,
peer_add_work ) ;
scif_peer_add_device ( scifdev ) ;
}
static void scif_peer_release_dev ( struct device * d )
/*
* 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 )
{
struct scif_peer_dev * sdev = dev_to_scif_peer ( d ) ;
struct scif_dev * scifdev = & scif_dev [ sdev - > dnode ] ;
int ret ;
scif_cleanup_scifdev ( scifdev ) ;
kfree ( sdev ) ;
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 ) ;
}
struct scif_peer_dev *
scif_peer_register_device ( struct scif_dev * scifdev )
int scif_peer_unregister_device ( struct scif_dev * scifdev )
{
int ret ;
struct scif_peer_dev * spdev ;
spdev = kzalloc ( sizeof ( * spdev ) , GFP_KERNEL ) ;
if ( ! spdev )
return ERR_PTR ( - ENOMEM ) ;
spdev - > dev . parent = scifdev - > sdev - > dev . parent ;
spdev - > dev . release = scif_peer_release_dev ;
spdev - > dnode = scifdev - > node ;
spdev - > dev . bus = & scif_peer_bus ;
mutex_lock ( & scifdev - > lock ) ;
/* Flush work to ensure device register is complete */
flush_work ( & scifdev - > peer_add_work ) ;
dev_set_name ( & spdev - > dev , " scif_peer-dev%u " , spdev - > dnode ) ;
/*
* device_register ( ) causes the bus infrastructure to look for a
* matching driver .
* Continue holding scifdev - > lock since theoretically unregister_device
* can be called simultaneously from multiple threads
*/
ret = device_register ( & spdev - > dev ) ;
if ( ret )
goto free_spdev ;
return spdev ;
free_spdev :
kfree ( spdev ) ;
return ERR_PTR ( ret ) ;
}
void scif_peer_unregister_device ( struct scif_peer_dev * sdev )
{
device_unregister ( & sdev - > dev ) ;
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 ;
}
int scif_peer_bus_init ( void )