diff options
author | Jiri Pirko <jpirko@redhat.com> | 2009-05-04 22:48:28 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-05-05 15:26:24 -0400 |
commit | f001fde5eadd915f4858d22ed70d7040f48767cf (patch) | |
tree | f03fd0216b411e4a076e464861aa959e5b51edb0 /net/core/dev.c | |
parent | b6907b0c705b6db221f937b4d343e2a6b280c8c5 (diff) |
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>
Diffstat (limited to 'net/core/dev.c')
-rw-r--r-- | net/core/dev.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 3c8073fe970a..637ea71b0a0d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -3434,6 +3434,252 @@ void dev_set_rx_mode(struct net_device *dev) | |||
3434 | netif_addr_unlock_bh(dev); | 3434 | netif_addr_unlock_bh(dev); |
3435 | } | 3435 | } |
3436 | 3436 | ||
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 | |||
3437 | int __dev_addr_delete(struct dev_addr_list **list, int *count, | 3683 | int __dev_addr_delete(struct dev_addr_list **list, int *count, |
3438 | void *addr, int alen, int glbl) | 3684 | void *addr, int alen, int glbl) |
3439 | { | 3685 | { |
@@ -4776,6 +5022,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, | |||
4776 | 5022 | ||
4777 | dev->gso_max_size = GSO_MAX_SIZE; | 5023 | dev->gso_max_size = GSO_MAX_SIZE; |
4778 | 5024 | ||
5025 | dev_addr_init(dev); | ||
4779 | netdev_init_queues(dev); | 5026 | netdev_init_queues(dev); |
4780 | 5027 | ||
4781 | INIT_LIST_HEAD(&dev->napi_list); | 5028 | INIT_LIST_HEAD(&dev->napi_list); |
@@ -4801,6 +5048,9 @@ void free_netdev(struct net_device *dev) | |||
4801 | 5048 | ||
4802 | kfree(dev->_tx); | 5049 | kfree(dev->_tx); |
4803 | 5050 | ||
5051 | /* Flush device addresses */ | ||
5052 | dev_addr_flush(dev); | ||
5053 | |||
4804 | list_for_each_entry_safe(p, n, &dev->napi_list, dev_list) | 5054 | list_for_each_entry_safe(p, n, &dev->napi_list, dev_list) |
4805 | netif_napi_del(p); | 5055 | netif_napi_del(p); |
4806 | 5056 | ||