@ -1217,6 +1217,57 @@ static void igmp_group_added(struct ip_mc_list *im)
* Multicast list managers
*/
static u32 ip_mc_hash ( const struct ip_mc_list * im )
{
return hash_32 ( ( u32 ) im - > multiaddr , MC_HASH_SZ_LOG ) ;
}
static void ip_mc_hash_add ( struct in_device * in_dev ,
struct ip_mc_list * im )
{
struct ip_mc_list __rcu * * mc_hash ;
u32 hash ;
mc_hash = rtnl_dereference ( in_dev - > mc_hash ) ;
if ( mc_hash ) {
hash = ip_mc_hash ( im ) ;
im - > next_hash = rtnl_dereference ( mc_hash [ hash ] ) ;
rcu_assign_pointer ( mc_hash [ hash ] , im ) ;
return ;
}
/* do not use a hash table for small number of items */
if ( in_dev - > mc_count < 4 )
return ;
mc_hash = kzalloc ( sizeof ( struct ip_mc_list * ) < < MC_HASH_SZ_LOG ,
GFP_KERNEL ) ;
if ( ! mc_hash )
return ;
for_each_pmc_rtnl ( in_dev , im ) {
hash = ip_mc_hash ( im ) ;
im - > next_hash = rtnl_dereference ( mc_hash [ hash ] ) ;
RCU_INIT_POINTER ( mc_hash [ hash ] , im ) ;
}
rcu_assign_pointer ( in_dev - > mc_hash , mc_hash ) ;
}
static void ip_mc_hash_remove ( struct in_device * in_dev ,
struct ip_mc_list * im )
{
struct ip_mc_list __rcu * * mc_hash = rtnl_dereference ( in_dev - > mc_hash ) ;
struct ip_mc_list * aux ;
if ( ! mc_hash )
return ;
mc_hash + = ip_mc_hash ( im ) ;
while ( ( aux = rtnl_dereference ( * mc_hash ) ) ! = im )
mc_hash = & aux - > next_hash ;
* mc_hash = im - > next_hash ;
}
/*
* A socket has joined a multicast group on device dev .
@ -1258,6 +1309,8 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
in_dev - > mc_count + + ;
rcu_assign_pointer ( in_dev - > mc_list , im ) ;
ip_mc_hash_add ( in_dev , im ) ;
# ifdef CONFIG_IP_MULTICAST
igmpv3_del_delrec ( in_dev , im - > multiaddr ) ;
# endif
@ -1314,6 +1367,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
ip = & i - > next_rcu ) {
if ( i - > multiaddr = = addr ) {
if ( - - i - > users = = 0 ) {
ip_mc_hash_remove ( in_dev , i ) ;
* ip = i - > next_rcu ;
in_dev - > mc_count - - ;
igmp_group_dropped ( i ) ;
@ -2321,12 +2375,25 @@ void ip_mc_drop_socket(struct sock *sk)
int ip_check_mc_rcu ( struct in_device * in_dev , __be32 mc_addr , __be32 src_addr , u16 proto )
{
struct ip_mc_list * im ;
struct ip_mc_list __rcu * * mc_hash ;
struct ip_sf_list * psf ;
int rv = 0 ;
for_each_pmc_rcu ( in_dev , im ) {
if ( im - > multiaddr = = mc_addr )
break ;
mc_hash = rcu_dereference ( in_dev - > mc_hash ) ;
if ( mc_hash ) {
u32 hash = hash_32 ( ( u32 ) mc_addr , MC_HASH_SZ_LOG ) ;
for ( im = rcu_dereference ( mc_hash [ hash ] ) ;
im ! = NULL ;
im = rcu_dereference ( im - > next_hash ) ) {
if ( im - > multiaddr = = mc_addr )
break ;
}
} else {
for_each_pmc_rcu ( in_dev , im ) {
if ( im - > multiaddr = = mc_addr )
break ;
}
}
if ( im & & proto = = IPPROTO_IGMP ) {
rv = 1 ;