[Bridge] [PATCH 1/3] net: introduce a list of device addresses dev_addr_list (v2)

Jiri Pirko jpirko at redhat.com
Thu Apr 16 01:46:05 PDT 2009


Wed, Apr 15, 2009 at 08:54:05PM CEST, dada1 at cosmosbay.com wrote:
>Jiri Pirko a écrit :

<snip>

>> +static int __hw_addr_add_ii(struct list_head *list, unsigned char *addr,
>> +			    int addr_len, int ignore_index)
>> +{
>> +	struct netdev_hw_addr *ha;
>> +	int i = 0;
>> +
>> +	if (addr_len > MAX_ADDR_LEN)
>> +		return -EINVAL;
>> +
>
>Please put here the ASSERT_RTNL(), not in various callers, since
>this is the place where we really assume rtnl lock is locked by us.

Well I'd like to have ASSERT_RTNL in callers. The reason is that for this
purpose (dev_addr) the guarding lock is rtnl. But for example for multicast
addresses it won't be. It will be most probably a spin lock. But those callers
(multicast) will use this __hw_addr_xxx functions too. Therefore I'd like to
leave locking on current level.
>
>You still use rcu_read_lock()/unlock() and rcu variant here...

Yes this is unecessrary and confusing I agree. Will remove these read locks in
places where there is guarded by rtnl mutex.
>
>But caller of this function has RTNL (or other lock) so dont use rcu here, as it seems
>inconsistent with kzalloc() code that comes next.
>
>> +	rcu_read_lock();
>> +	list_for_each_entry_rcu(ha, list, list) {
>> +		if (i++ != ignore_index &&
>> +		    !memcmp(ha->addr, addr, addr_len)) {
>> +			ha->refcount++;
>> +			rcu_read_unlock();
>> +			return 0;
>> +		}
>> +	}
>> +	rcu_read_unlock();
>> +
>> +	ha = kzalloc(max(sizeof(*ha), L1_CACHE_BYTES), GFP_ATOMIC);
>> +	if (!ha)
>> +		return -ENOMEM;
>> +	memcpy(ha->addr, addr, addr_len);
>> +	ha->refcount = 1;
>> +	list_add_tail_rcu(&ha->list, list);
>> +	return 0;

<snip>

>> +static int __hw_addr_add_multiple_ii(struct list_head *to_list,
>> +				     struct list_head *from_list,
>> +				     int addr_len, int ignore_index)
>> +{
>> +	int err = 0;
>> +	struct netdev_hw_addr *ha, *ha2;
>> +
>
>same here, no need for rcu_read_lock(), since you are going to change list, you
>have RTNL lock or equivalent.
>
Yes, I wanted to show that for "from_list" this is a reader...
....unnecessary,foolish -> removing...
>> +	rcu_read_lock();
>> +	list_for_each_entry_rcu(ha, from_list, list) {
>> +		err = __hw_addr_add_ii(to_list, ha->addr, addr_len, 0);
>> +		if (err)
>> +			goto unroll;
>> +	}
>> +	goto unlock;
>> +unroll:
>> +	list_for_each_entry_rcu(ha2, from_list, list) {
>> +		if (ha2 == ha)
>> +			break;
>> +		__hw_addr_del_ii(to_list, ha2->addr, addr_len, 0);
>> +	}
>> +unlock:
>> +	rcu_read_unlock();
>> +	return err;
>> +}
>> +

<snip>

>> +static void dev_addr_flush(struct net_device *dev)
>> +{
>> +	ASSERT_RTNL();
>> +
>> +	__hw_addr_flush(&dev->dev_addr_list);
>> +	dev->dev_addr = NULL;
>
>seems risky here to set this to NULL... You could use a static var to avoid
>further NULL dereference.
>
>static char nulladdr[MAX_ADDR_LEN];
>dev->dev_addr = nulladdr;
>
>> +}
>> +

<snip>

>> @@ -4257,6 +4521,9 @@ static void rollback_registered(struct net_device *dev)
>>  	 */
>>  	dev_addr_discard(dev);
>>  
>> +	/* Flush device addresses */
>> +	dev_addr_flush(dev);
>> +
>
>Are you sure that no driver in tree will dereference dev->dev_addr after this point ?

I assume that driver might not use dev_addr after it calls
unregister_netdevice(). But ok - I would rather move calling dev_addr_flush()
somewhere later where there is a guarantee that dev_addr should not be
referenced. Perhaps in free_netdev() ? It would also correspond with calling
dev_addr_init() in alloc_netdev_mq()...
>
>>  	if (dev->netdev_ops->ndo_uninit)
>>  		dev->netdev_ops->ndo_uninit(dev);
>>  
>> @@ -4779,6 +5046,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
>>  
>>  	dev->gso_max_size = GSO_MAX_SIZE;
>>  
>> +	dev_addr_init(dev);
>>  	netdev_init_queues(dev);
>>  
>>  	INIT_LIST_HEAD(&dev->napi_list);
>> @@ -4965,6 +5233,9 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
>>  	 */
>>  	dev_addr_discard(dev);
>>  
>> +	/* Flush device addresses */
>> +	dev_addr_flush(dev);
>> +
>>  	netdev_unregister_kobject(dev);
>>  
>>  	/* Actually switch the network namespace */
>
>


More information about the Bridge mailing list