Skip to content

Commit f001fde

Browse files
Jiri Pirkodavem330
Jiri Pirko
authored andcommitted
net: introduce a list of device addresses dev_addr_list (v6)
v5 -> v6 (current): -removed so far unused static functions -corrected dev_addr_del_multiple to call del instead of add v4 -> v5: -added device address type (suggested by davem) -removed refcounting (better to have simplier code then safe potentially few bytes) v3 -> v4: -changed kzalloc to kmalloc in __hw_addr_add_ii() -ASSERT_RTNL() avoided in dev_addr_flush() and dev_addr_init() v2 -> v3: -removed unnecessary rcu read locking -moved dev_addr_flush() calling to ensure no null dereference of dev_addr v1 -> v2: -added forgotten ASSERT_RTNL to dev_addr_init and dev_addr_flush -removed unnecessary rcu_read locking in dev_addr_init -use compare_ether_addr_64bits instead of compare_ether_addr -use L1_CACHE_BYTES as size for allocating struct netdev_hw_addr -use call_rcu instead of rcu_synchronize -moved is_etherdev_addr into __KERNEL__ ifdef This patch introduces a new list in struct net_device and brings a set of functions to handle the work with device address list. The list is a replacement for the original dev_addr field and because in some situations there is need to carry several device addresses with the net device. To be backward compatible, dev_addr is made to point to the first member of the list so original drivers sees no difference. Signed-off-by: Jiri Pirko <jpirko@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent b6907b0 commit f001fde

File tree

3 files changed

+312
-2
lines changed

3 files changed

+312
-2
lines changed

Diff for: include/linux/etherdevice.h

+27
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,33 @@ static inline unsigned compare_ether_addr_64bits(const u8 addr1[6+2],
182182
return compare_ether_addr(addr1, addr2);
183183
#endif
184184
}
185+
186+
/**
187+
* is_etherdev_addr - Tell if given Ethernet address belongs to the device.
188+
* @dev: Pointer to a device structure
189+
* @addr: Pointer to a six-byte array containing the Ethernet address
190+
*
191+
* Compare passed address with all addresses of the device. Return true if the
192+
* address if one of the device addresses.
193+
*
194+
* Note that this function calls compare_ether_addr_64bits() so take care of
195+
* the right padding.
196+
*/
197+
static inline bool is_etherdev_addr(const struct net_device *dev,
198+
const u8 addr[6 + 2])
199+
{
200+
struct netdev_hw_addr *ha;
201+
int res = 1;
202+
203+
rcu_read_lock();
204+
for_each_dev_addr(dev, ha) {
205+
res = compare_ether_addr_64bits(addr, ha->addr);
206+
if (!res)
207+
break;
208+
}
209+
rcu_read_unlock();
210+
return !res;
211+
}
185212
#endif /* __KERNEL__ */
186213

187214
/**

Diff for: include/linux/netdevice.h

+35-2
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,16 @@ struct dev_addr_list
210210
#define dmi_users da_users
211211
#define dmi_gusers da_gusers
212212

213+
struct netdev_hw_addr {
214+
struct list_head list;
215+
unsigned char addr[MAX_ADDR_LEN];
216+
unsigned char type;
217+
#define NETDEV_HW_ADDR_T_LAN 1
218+
#define NETDEV_HW_ADDR_T_SAN 2
219+
#define NETDEV_HW_ADDR_T_SLAVE 3
220+
struct rcu_head rcu_head;
221+
};
222+
213223
struct hh_cache
214224
{
215225
struct hh_cache *hh_next; /* Next entry */
@@ -784,8 +794,11 @@ struct net_device
784794
*/
785795
unsigned long last_rx; /* Time of last Rx */
786796
/* Interface address info used in eth_type_trans() */
787-
unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address, (before bcast
788-
because most packets are unicast) */
797+
unsigned char *dev_addr; /* hw address, (before bcast
798+
because most packets are
799+
unicast) */
800+
801+
struct list_head dev_addr_list; /* list of device hw addresses */
789802

790803
unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */
791804

@@ -1791,6 +1804,13 @@ static inline void netif_addr_unlock_bh(struct net_device *dev)
17911804
spin_unlock_bh(&dev->addr_list_lock);
17921805
}
17931806

1807+
/*
1808+
* dev_addr_list walker. Should be used only for read access. Call with
1809+
* rcu_read_lock held.
1810+
*/
1811+
#define for_each_dev_addr(dev, ha) \
1812+
list_for_each_entry_rcu(ha, &dev->dev_addr_list, list)
1813+
17941814
/* These functions live elsewhere (drivers/net/net_init.c, but related) */
17951815

17961816
extern void ether_setup(struct net_device *dev);
@@ -1803,6 +1823,19 @@ extern struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
18031823
alloc_netdev_mq(sizeof_priv, name, setup, 1)
18041824
extern int register_netdev(struct net_device *dev);
18051825
extern void unregister_netdev(struct net_device *dev);
1826+
1827+
/* Functions used for device addresses handling */
1828+
extern int dev_addr_add(struct net_device *dev, unsigned char *addr,
1829+
unsigned char addr_type);
1830+
extern int dev_addr_del(struct net_device *dev, unsigned char *addr,
1831+
unsigned char addr_type);
1832+
extern int dev_addr_add_multiple(struct net_device *to_dev,
1833+
struct net_device *from_dev,
1834+
unsigned char addr_type);
1835+
extern int dev_addr_del_multiple(struct net_device *to_dev,
1836+
struct net_device *from_dev,
1837+
unsigned char addr_type);
1838+
18061839
/* Functions used for secondary unicast and multicast support */
18071840
extern void dev_set_rx_mode(struct net_device *dev);
18081841
extern void __dev_set_rx_mode(struct net_device *dev);

Diff for: net/core/dev.c

+250
Original file line numberDiff line numberDiff line change
@@ -3434,6 +3434,252 @@ void dev_set_rx_mode(struct net_device *dev)
34343434
netif_addr_unlock_bh(dev);
34353435
}
34363436

3437+
/* hw addresses list handling functions */
3438+
3439+
static int __hw_addr_add(struct list_head *list, unsigned char *addr,
3440+
int addr_len, unsigned char addr_type)
3441+
{
3442+
struct netdev_hw_addr *ha;
3443+
int alloc_size;
3444+
3445+
if (addr_len > MAX_ADDR_LEN)
3446+
return -EINVAL;
3447+
3448+
alloc_size = sizeof(*ha);
3449+
if (alloc_size < L1_CACHE_BYTES)
3450+
alloc_size = L1_CACHE_BYTES;
3451+
ha = kmalloc(alloc_size, GFP_ATOMIC);
3452+
if (!ha)
3453+
return -ENOMEM;
3454+
memcpy(ha->addr, addr, addr_len);
3455+
ha->type = addr_type;
3456+
list_add_tail_rcu(&ha->list, list);
3457+
return 0;
3458+
}
3459+
3460+
static void ha_rcu_free(struct rcu_head *head)
3461+
{
3462+
struct netdev_hw_addr *ha;
3463+
3464+
ha = container_of(head, struct netdev_hw_addr, rcu_head);
3465+
kfree(ha);
3466+
}
3467+
3468+
static int __hw_addr_del_ii(struct list_head *list, unsigned char *addr,
3469+
int addr_len, unsigned char addr_type,
3470+
int ignore_index)
3471+
{
3472+
struct netdev_hw_addr *ha;
3473+
int i = 0;
3474+
3475+
list_for_each_entry(ha, list, list) {
3476+
if (i++ != ignore_index &&
3477+
!memcmp(ha->addr, addr, addr_len) &&
3478+
(ha->type == addr_type || !addr_type)) {
3479+
list_del_rcu(&ha->list);
3480+
call_rcu(&ha->rcu_head, ha_rcu_free);
3481+
return 0;
3482+
}
3483+
}
3484+
return -ENOENT;
3485+
}
3486+
3487+
static int __hw_addr_add_multiple_ii(struct list_head *to_list,
3488+
struct list_head *from_list,
3489+
int addr_len, unsigned char addr_type,
3490+
int ignore_index)
3491+
{
3492+
int err;
3493+
struct netdev_hw_addr *ha, *ha2;
3494+
unsigned char type;
3495+
3496+
list_for_each_entry(ha, from_list, list) {
3497+
type = addr_type ? addr_type : ha->type;
3498+
err = __hw_addr_add(to_list, ha->addr, addr_len, type);
3499+
if (err)
3500+
goto unroll;
3501+
}
3502+
return 0;
3503+
3504+
unroll:
3505+
list_for_each_entry(ha2, from_list, list) {
3506+
if (ha2 == ha)
3507+
break;
3508+
type = addr_type ? addr_type : ha2->type;
3509+
__hw_addr_del_ii(to_list, ha2->addr, addr_len, type,
3510+
ignore_index);
3511+
}
3512+
return err;
3513+
}
3514+
3515+
static void __hw_addr_del_multiple_ii(struct list_head *to_list,
3516+
struct list_head *from_list,
3517+
int addr_len, unsigned char addr_type,
3518+
int ignore_index)
3519+
{
3520+
struct netdev_hw_addr *ha;
3521+
unsigned char type;
3522+
3523+
list_for_each_entry(ha, from_list, list) {
3524+
type = addr_type ? addr_type : ha->type;
3525+
__hw_addr_del_ii(to_list, ha->addr, addr_len, addr_type,
3526+
ignore_index);
3527+
}
3528+
}
3529+
3530+
static void __hw_addr_flush(struct list_head *list)
3531+
{
3532+
struct netdev_hw_addr *ha, *tmp;
3533+
3534+
list_for_each_entry_safe(ha, tmp, list, list) {
3535+
list_del_rcu(&ha->list);
3536+
call_rcu(&ha->rcu_head, ha_rcu_free);
3537+
}
3538+
}
3539+
3540+
/* Device addresses handling functions */
3541+
3542+
static void dev_addr_flush(struct net_device *dev)
3543+
{
3544+
/* rtnl_mutex must be held here */
3545+
3546+
__hw_addr_flush(&dev->dev_addr_list);
3547+
dev->dev_addr = NULL;
3548+
}
3549+
3550+
static int dev_addr_init(struct net_device *dev)
3551+
{
3552+
unsigned char addr[MAX_ADDR_LEN];
3553+
struct netdev_hw_addr *ha;
3554+
int err;
3555+
3556+
/* rtnl_mutex must be held here */
3557+
3558+
INIT_LIST_HEAD(&dev->dev_addr_list);
3559+
memset(addr, 0, sizeof(*addr));
3560+
err = __hw_addr_add(&dev->dev_addr_list, addr, sizeof(*addr),
3561+
NETDEV_HW_ADDR_T_LAN);
3562+
if (!err) {
3563+
/*
3564+
* Get the first (previously created) address from the list
3565+
* and set dev_addr pointer to this location.
3566+
*/
3567+
ha = list_first_entry(&dev->dev_addr_list,
3568+
struct netdev_hw_addr, list);
3569+
dev->dev_addr = ha->addr;
3570+
}
3571+
return err;
3572+
}
3573+
3574+
/**
3575+
* dev_addr_add - Add a device address
3576+
* @dev: device
3577+
* @addr: address to add
3578+
* @addr_type: address type
3579+
*
3580+
* Add a device address to the device or increase the reference count if
3581+
* it already exists.
3582+
*
3583+
* The caller must hold the rtnl_mutex.
3584+
*/
3585+
int dev_addr_add(struct net_device *dev, unsigned char *addr,
3586+
unsigned char addr_type)
3587+
{
3588+
int err;
3589+
3590+
ASSERT_RTNL();
3591+
3592+
err = __hw_addr_add(&dev->dev_addr_list, addr, dev->addr_len,
3593+
addr_type);
3594+
if (!err)
3595+
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
3596+
return err;
3597+
}
3598+
EXPORT_SYMBOL(dev_addr_add);
3599+
3600+
/**
3601+
* dev_addr_del - Release a device address.
3602+
* @dev: device
3603+
* @addr: address to delete
3604+
* @addr_type: address type
3605+
*
3606+
* Release reference to a device address and remove it from the device
3607+
* if the reference count drops to zero.
3608+
*
3609+
* The caller must hold the rtnl_mutex.
3610+
*/
3611+
int dev_addr_del(struct net_device *dev, unsigned char *addr,
3612+
unsigned char addr_type)
3613+
{
3614+
int err;
3615+
3616+
ASSERT_RTNL();
3617+
3618+
err = __hw_addr_del_ii(&dev->dev_addr_list, addr, dev->addr_len,
3619+
addr_type, 0);
3620+
if (!err)
3621+
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
3622+
return err;
3623+
}
3624+
EXPORT_SYMBOL(dev_addr_del);
3625+
3626+
/**
3627+
* dev_addr_add_multiple - Add device addresses from another device
3628+
* @to_dev: device to which addresses will be added
3629+
* @from_dev: device from which addresses will be added
3630+
* @addr_type: address type - 0 means type will be used from from_dev
3631+
*
3632+
* Add device addresses of the one device to another.
3633+
**
3634+
* The caller must hold the rtnl_mutex.
3635+
*/
3636+
int dev_addr_add_multiple(struct net_device *to_dev,
3637+
struct net_device *from_dev,
3638+
unsigned char addr_type)
3639+
{
3640+
int err;
3641+
3642+
ASSERT_RTNL();
3643+
3644+
if (from_dev->addr_len != to_dev->addr_len)
3645+
return -EINVAL;
3646+
err = __hw_addr_add_multiple_ii(&to_dev->dev_addr_list,
3647+
&from_dev->dev_addr_list,
3648+
to_dev->addr_len, addr_type, 0);
3649+
if (!err)
3650+
call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
3651+
return err;
3652+
}
3653+
EXPORT_SYMBOL(dev_addr_add_multiple);
3654+
3655+
/**
3656+
* dev_addr_del_multiple - Delete device addresses by another device
3657+
* @to_dev: device where the addresses will be deleted
3658+
* @from_dev: device by which addresses the addresses will be deleted
3659+
* @addr_type: address type - 0 means type will used from from_dev
3660+
*
3661+
* Deletes addresses in to device by the list of addresses in from device.
3662+
*
3663+
* The caller must hold the rtnl_mutex.
3664+
*/
3665+
int dev_addr_del_multiple(struct net_device *to_dev,
3666+
struct net_device *from_dev,
3667+
unsigned char addr_type)
3668+
{
3669+
ASSERT_RTNL();
3670+
3671+
if (from_dev->addr_len != to_dev->addr_len)
3672+
return -EINVAL;
3673+
__hw_addr_del_multiple_ii(&to_dev->dev_addr_list,
3674+
&from_dev->dev_addr_list,
3675+
to_dev->addr_len, addr_type, 0);
3676+
call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
3677+
return 0;
3678+
}
3679+
EXPORT_SYMBOL(dev_addr_del_multiple);
3680+
3681+
/* unicast and multicast addresses handling functions */
3682+
34373683
int __dev_addr_delete(struct dev_addr_list **list, int *count,
34383684
void *addr, int alen, int glbl)
34393685
{
@@ -4776,6 +5022,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
47765022

47775023
dev->gso_max_size = GSO_MAX_SIZE;
47785024

5025+
dev_addr_init(dev);
47795026
netdev_init_queues(dev);
47805027

47815028
INIT_LIST_HEAD(&dev->napi_list);
@@ -4801,6 +5048,9 @@ void free_netdev(struct net_device *dev)
48015048

48025049
kfree(dev->_tx);
48035050

5051+
/* Flush device addresses */
5052+
dev_addr_flush(dev);
5053+
48045054
list_for_each_entry_safe(p, n, &dev->napi_list, dev_list)
48055055
netif_napi_del(p);
48065056

0 commit comments

Comments
 (0)