@ -213,6 +213,9 @@ struct cm_id_private {
spinlock_t lock ; /* Do not acquire inside cm.lock */
struct completion comp ;
atomic_t refcount ;
/* Number of clients sharing this ib_cm_id. Only valid for listeners.
* Protected by the cm . lock spinlock . */
int listen_sharecount ;
struct ib_mad_send_buf * msg ;
struct cm_timewait_info * timewait_info ;
@ -859,9 +862,15 @@ retest:
spin_lock_irq ( & cm_id_priv - > lock ) ;
switch ( cm_id - > state ) {
case IB_CM_LISTEN :
cm_id - > state = IB_CM_IDLE ;
spin_unlock_irq ( & cm_id_priv - > lock ) ;
spin_lock_irq ( & cm . lock ) ;
if ( - - cm_id_priv - > listen_sharecount > 0 ) {
/* The id is still shared. */
cm_deref_id ( cm_id_priv ) ;
spin_unlock_irq ( & cm . lock ) ;
return ;
}
rb_erase ( & cm_id_priv - > service_node , & cm . listen_service_table ) ;
spin_unlock_irq ( & cm . lock ) ;
break ;
@ -941,11 +950,32 @@ void ib_destroy_cm_id(struct ib_cm_id *cm_id)
}
EXPORT_SYMBOL ( ib_destroy_cm_id ) ;
int ib_cm_listen ( struct ib_cm_id * cm_id , __be64 service_id , __be64 service_mask ,
struct ib_cm_compare_data * compare_data )
/**
* __ib_cm_listen - Initiates listening on the specified service ID for
* connection and service ID resolution requests .
* @ cm_id : Connection identifier associated with the listen request .
* @ service_id : Service identifier matched against incoming connection
* and service ID resolution requests . The service ID should be specified
* network - byte order . If set to IB_CM_ASSIGN_SERVICE_ID , the CM will
* assign a service ID to the caller .
* @ service_mask : Mask applied to service ID used to listen across a
* range of service IDs . If set to 0 , the service ID is matched
* exactly . This parameter is ignored if % service_id is set to
* IB_CM_ASSIGN_SERVICE_ID .
* @ compare_data : This parameter is optional . It specifies data that must
* appear in the private data of a connection request for the specified
* listen request .
* @ lock : If set , lock the cm . lock spin - lock when adding the id to the
* listener tree . When false , the caller must already hold the spin - lock ,
* and compare_data must be NULL .
*/
static int __ib_cm_listen ( struct ib_cm_id * cm_id , __be64 service_id ,
__be64 service_mask ,
struct ib_cm_compare_data * compare_data ,
bool lock )
{
struct cm_id_private * cm_id_priv , * cur_cm_id_priv ;
unsigned long flags ;
unsigned long flags = 0 ;
int ret = 0 ;
service_mask = service_mask ? service_mask : ~ cpu_to_be64 ( 0 ) ;
@ -970,8 +1000,10 @@ int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask,
}
cm_id - > state = IB_CM_LISTEN ;
if ( lock )
spin_lock_irqsave ( & cm . lock , flags ) ;
spin_lock_irqsave ( & cm . lock , flags ) ;
+ + cm_id_priv - > listen_sharecount ;
if ( service_id = = IB_CM_ASSIGN_SERVICE_ID ) {
cm_id - > service_id = cpu_to_be64 ( cm . listen_service_id + + ) ;
cm_id - > service_mask = ~ cpu_to_be64 ( 0 ) ;
@ -980,18 +1012,96 @@ int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask,
cm_id - > service_mask = service_mask ;
}
cur_cm_id_priv = cm_insert_listen ( cm_id_priv ) ;
spin_unlock_irqrestore ( & cm . lock , flags ) ;
if ( cur_cm_id_priv ) {
cm_id - > state = IB_CM_IDLE ;
- - cm_id_priv - > listen_sharecount ;
kfree ( cm_id_priv - > compare_data ) ;
cm_id_priv - > compare_data = NULL ;
ret = - EBUSY ;
}
if ( lock )
spin_unlock_irqrestore ( & cm . lock , flags ) ;
return ret ;
}
int ib_cm_listen ( struct ib_cm_id * cm_id , __be64 service_id , __be64 service_mask ,
struct ib_cm_compare_data * compare_data )
{
return __ib_cm_listen ( cm_id , service_id , service_mask , compare_data ,
true ) ;
}
EXPORT_SYMBOL ( ib_cm_listen ) ;
/**
* Create a new listening ib_cm_id and listen on the given service ID .
*
* If there ' s an existing ID listening on that same device and service ID ,
* return it .
*
* @ device : Device associated with the cm_id . All related communication will
* be associated with the specified device .
* @ cm_handler : Callback invoked to notify the user of CM events .
* @ service_id : Service identifier matched against incoming connection
* and service ID resolution requests . The service ID should be specified
* network - byte order . If set to IB_CM_ASSIGN_SERVICE_ID , the CM will
* assign a service ID to the caller .
*
* Callers should call ib_destroy_cm_id when done with the listener ID .
*/
struct ib_cm_id * ib_cm_insert_listen ( struct ib_device * device ,
ib_cm_handler cm_handler ,
__be64 service_id )
{
struct cm_id_private * cm_id_priv ;
struct ib_cm_id * cm_id ;
unsigned long flags ;
int err = 0 ;
/* Create an ID in advance, since the creation may sleep */
cm_id = ib_create_cm_id ( device , cm_handler , NULL ) ;
if ( IS_ERR ( cm_id ) )
return cm_id ;
spin_lock_irqsave ( & cm . lock , flags ) ;
if ( service_id = = IB_CM_ASSIGN_SERVICE_ID )
goto new_id ;
/* Find an existing ID */
cm_id_priv = cm_find_listen ( device , service_id , NULL ) ;
if ( cm_id_priv ) {
if ( cm_id - > cm_handler ! = cm_handler | | cm_id - > context ) {
/* Sharing an ib_cm_id with different handlers is not
* supported */
spin_unlock_irqrestore ( & cm . lock , flags ) ;
return ERR_PTR ( - EINVAL ) ;
}
atomic_inc ( & cm_id_priv - > refcount ) ;
+ + cm_id_priv - > listen_sharecount ;
spin_unlock_irqrestore ( & cm . lock , flags ) ;
ib_destroy_cm_id ( cm_id ) ;
cm_id = & cm_id_priv - > id ;
return cm_id ;
}
new_id :
/* Use newly created ID */
err = __ib_cm_listen ( cm_id , service_id , 0 , NULL , false ) ;
spin_unlock_irqrestore ( & cm . lock , flags ) ;
if ( err ) {
ib_destroy_cm_id ( cm_id ) ;
return ERR_PTR ( err ) ;
}
return cm_id ;
}
EXPORT_SYMBOL ( ib_cm_insert_listen ) ;
static __be64 cm_form_tid ( struct cm_id_private * cm_id_priv ,
enum cm_msg_sequence msg_seq )
{