diff options
| author | Alexander Duyck <alexander.h.duyck@intel.com> | 2014-05-28 21:44:46 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2014-06-02 13:40:54 -0400 |
| commit | 670e5b8eaf85704742bc3cb1df51fdd3ce08fc15 (patch) | |
| tree | b11726054cc9e4741d00666998c9010b59608f7b | |
| parent | 3e820811583e7c7f8d7793775d82898e5136a855 (diff) | |
net: Add support for device specific address syncing
This change provides a function to be used in order to break the
ndo_set_rx_mode call into a set of address add and remove calls. The code
is based on the implementation of dev_uc_sync/dev_mc_sync. Since they
essentially do the same thing but with only one dev I simply named my
functions __dev_uc_sync/__dev_mc_sync.
I also implemented an unsync version of the functions as well to allow for
cleanup on close.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | include/linux/netdevice.h | 73 | ||||
| -rw-r--r-- | net/core/dev_addr_lists.c | 85 |
2 files changed, 158 insertions, 0 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 2db1610bf109..774e5391eb8e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
| @@ -3003,6 +3003,15 @@ int __hw_addr_sync(struct netdev_hw_addr_list *to_list, | |||
| 3003 | struct netdev_hw_addr_list *from_list, int addr_len); | 3003 | struct netdev_hw_addr_list *from_list, int addr_len); |
| 3004 | void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, | 3004 | void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, |
| 3005 | struct netdev_hw_addr_list *from_list, int addr_len); | 3005 | struct netdev_hw_addr_list *from_list, int addr_len); |
| 3006 | int __hw_addr_sync_dev(struct netdev_hw_addr_list *list, | ||
| 3007 | struct net_device *dev, | ||
| 3008 | int (*sync)(struct net_device *, const unsigned char *), | ||
| 3009 | int (*unsync)(struct net_device *, | ||
| 3010 | const unsigned char *)); | ||
| 3011 | void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list, | ||
| 3012 | struct net_device *dev, | ||
| 3013 | int (*unsync)(struct net_device *, | ||
| 3014 | const unsigned char *)); | ||
| 3006 | void __hw_addr_init(struct netdev_hw_addr_list *list); | 3015 | void __hw_addr_init(struct netdev_hw_addr_list *list); |
| 3007 | 3016 | ||
| 3008 | /* Functions used for device addresses handling */ | 3017 | /* Functions used for device addresses handling */ |
| @@ -3023,6 +3032,38 @@ void dev_uc_unsync(struct net_device *to, struct net_device *from); | |||
| 3023 | void dev_uc_flush(struct net_device *dev); | 3032 | void dev_uc_flush(struct net_device *dev); |
| 3024 | void dev_uc_init(struct net_device *dev); | 3033 | void dev_uc_init(struct net_device *dev); |
| 3025 | 3034 | ||
| 3035 | /** | ||
| 3036 | * __dev_uc_sync - Synchonize device's unicast list | ||
| 3037 | * @dev: device to sync | ||
| 3038 | * @sync: function to call if address should be added | ||
| 3039 | * @unsync: function to call if address should be removed | ||
| 3040 | * | ||
| 3041 | * Add newly added addresses to the interface, and release | ||
| 3042 | * addresses that have been deleted. | ||
| 3043 | **/ | ||
| 3044 | static inline int __dev_uc_sync(struct net_device *dev, | ||
| 3045 | int (*sync)(struct net_device *, | ||
| 3046 | const unsigned char *), | ||
| 3047 | int (*unsync)(struct net_device *, | ||
| 3048 | const unsigned char *)) | ||
| 3049 | { | ||
| 3050 | return __hw_addr_sync_dev(&dev->uc, dev, sync, unsync); | ||
| 3051 | } | ||
| 3052 | |||
| 3053 | /** | ||
| 3054 | * __dev_uc_unsync - Remove synchonized addresses from device | ||
| 3055 | * @dev: device to sync | ||
| 3056 | * @unsync: function to call if address should be removed | ||
| 3057 | * | ||
| 3058 | * Remove all addresses that were added to the device by dev_uc_sync(). | ||
| 3059 | **/ | ||
| 3060 | static inline void __dev_uc_unsync(struct net_device *dev, | ||
| 3061 | int (*unsync)(struct net_device *, | ||
| 3062 | const unsigned char *)) | ||
| 3063 | { | ||
| 3064 | __hw_addr_unsync_dev(&dev->uc, dev, unsync); | ||
| 3065 | } | ||
| 3066 | |||
| 3026 | /* Functions used for multicast addresses handling */ | 3067 | /* Functions used for multicast addresses handling */ |
| 3027 | int dev_mc_add(struct net_device *dev, const unsigned char *addr); | 3068 | int dev_mc_add(struct net_device *dev, const unsigned char *addr); |
| 3028 | int dev_mc_add_global(struct net_device *dev, const unsigned char *addr); | 3069 | int dev_mc_add_global(struct net_device *dev, const unsigned char *addr); |
| @@ -3035,6 +3076,38 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from); | |||
| 3035 | void dev_mc_flush(struct net_device *dev); | 3076 | void dev_mc_flush(struct net_device *dev); |
| 3036 | void dev_mc_init(struct net_device *dev); | 3077 | void dev_mc_init(struct net_device *dev); |
| 3037 | 3078 | ||
| 3079 | /** | ||
| 3080 | * __dev_mc_sync - Synchonize device's multicast list | ||
| 3081 | * @dev: device to sync | ||
| 3082 | * @sync: function to call if address should be added | ||
| 3083 | * @unsync: function to call if address should be removed | ||
| 3084 | * | ||
| 3085 | * Add newly added addresses to the interface, and release | ||
| 3086 | * addresses that have been deleted. | ||
| 3087 | **/ | ||
| 3088 | static inline int __dev_mc_sync(struct net_device *dev, | ||
| 3089 | int (*sync)(struct net_device *, | ||
| 3090 | const unsigned char *), | ||
| 3091 | int (*unsync)(struct net_device *, | ||
| 3092 | const unsigned char *)) | ||
| 3093 | { | ||
| 3094 | return __hw_addr_sync_dev(&dev->mc, dev, sync, unsync); | ||
| 3095 | } | ||
| 3096 | |||
| 3097 | /** | ||
| 3098 | * __dev_mc_unsync - Remove synchonized addresses from device | ||
| 3099 | * @dev: device to sync | ||
| 3100 | * @unsync: function to call if address should be removed | ||
| 3101 | * | ||
| 3102 | * Remove all addresses that were added to the device by dev_mc_sync(). | ||
| 3103 | **/ | ||
| 3104 | static inline void __dev_mc_unsync(struct net_device *dev, | ||
| 3105 | int (*unsync)(struct net_device *, | ||
| 3106 | const unsigned char *)) | ||
| 3107 | { | ||
| 3108 | __hw_addr_unsync_dev(&dev->mc, dev, unsync); | ||
| 3109 | } | ||
| 3110 | |||
| 3038 | /* Functions used for secondary unicast and multicast support */ | 3111 | /* Functions used for secondary unicast and multicast support */ |
| 3039 | void dev_set_rx_mode(struct net_device *dev); | 3112 | void dev_set_rx_mode(struct net_device *dev); |
| 3040 | void __dev_set_rx_mode(struct net_device *dev); | 3113 | void __dev_set_rx_mode(struct net_device *dev); |
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 329d5794e7dc..b6b230600b97 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c | |||
| @@ -225,6 +225,91 @@ void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, | |||
| 225 | } | 225 | } |
| 226 | EXPORT_SYMBOL(__hw_addr_unsync); | 226 | EXPORT_SYMBOL(__hw_addr_unsync); |
| 227 | 227 | ||
| 228 | /** | ||
| 229 | * __hw_addr_sync_dev - Synchonize device's multicast list | ||
| 230 | * @list: address list to syncronize | ||
| 231 | * @dev: device to sync | ||
| 232 | * @sync: function to call if address should be added | ||
| 233 | * @unsync: function to call if address should be removed | ||
| 234 | * | ||
| 235 | * This funciton is intended to be called from the ndo_set_rx_mode | ||
| 236 | * function of devices that require explicit address add/remove | ||
| 237 | * notifications. The unsync function may be NULL in which case | ||
| 238 | * the addresses requiring removal will simply be removed without | ||
| 239 | * any notification to the device. | ||
| 240 | **/ | ||
| 241 | int __hw_addr_sync_dev(struct netdev_hw_addr_list *list, | ||
| 242 | struct net_device *dev, | ||
| 243 | int (*sync)(struct net_device *, const unsigned char *), | ||
| 244 | int (*unsync)(struct net_device *, | ||
| 245 | const unsigned char *)) | ||
| 246 | { | ||
| 247 | struct netdev_hw_addr *ha, *tmp; | ||
| 248 | int err; | ||
| 249 | |||
| 250 | /* first go through and flush out any stale entries */ | ||
| 251 | list_for_each_entry_safe(ha, tmp, &list->list, list) { | ||
| 252 | if (!ha->sync_cnt || ha->refcount != 1) | ||
| 253 | continue; | ||
| 254 | |||
| 255 | /* if unsync is defined and fails defer unsyncing address */ | ||
| 256 | if (unsync && unsync(dev, ha->addr)) | ||
| 257 | continue; | ||
| 258 | |||
| 259 | ha->sync_cnt--; | ||
| 260 | __hw_addr_del_entry(list, ha, false, false); | ||
| 261 | } | ||
| 262 | |||
| 263 | /* go through and sync new entries to the list */ | ||
| 264 | list_for_each_entry_safe(ha, tmp, &list->list, list) { | ||
| 265 | if (ha->sync_cnt) | ||
| 266 | continue; | ||
| 267 | |||
| 268 | err = sync(dev, ha->addr); | ||
| 269 | if (err) | ||
| 270 | return err; | ||
| 271 | |||
| 272 | ha->sync_cnt++; | ||
| 273 | ha->refcount++; | ||
| 274 | } | ||
| 275 | |||
| 276 | return 0; | ||
| 277 | } | ||
| 278 | EXPORT_SYMBOL(__hw_addr_sync_dev); | ||
| 279 | |||
| 280 | /** | ||
| 281 | * __hw_addr_unsync_dev - Remove synchonized addresses from device | ||
| 282 | * @list: address list to remove syncronized addresses from | ||
| 283 | * @dev: device to sync | ||
| 284 | * @unsync: function to call if address should be removed | ||
| 285 | * | ||
| 286 | * Remove all addresses that were added to the device by __hw_addr_sync_dev(). | ||
| 287 | * This function is intended to be called from the ndo_stop or ndo_open | ||
| 288 | * functions on devices that require explicit address add/remove | ||
| 289 | * notifications. If the unsync function pointer is NULL then this function | ||
| 290 | * can be used to just reset the sync_cnt for the addresses in the list. | ||
| 291 | **/ | ||
| 292 | void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list, | ||
| 293 | struct net_device *dev, | ||
| 294 | int (*unsync)(struct net_device *, | ||
| 295 | const unsigned char *)) | ||
| 296 | { | ||
| 297 | struct netdev_hw_addr *ha, *tmp; | ||
| 298 | |||
| 299 | list_for_each_entry_safe(ha, tmp, &list->list, list) { | ||
| 300 | if (!ha->sync_cnt) | ||
| 301 | continue; | ||
| 302 | |||
| 303 | /* if unsync is defined and fails defer unsyncing address */ | ||
| 304 | if (unsync && unsync(dev, ha->addr)) | ||
| 305 | continue; | ||
| 306 | |||
| 307 | ha->sync_cnt--; | ||
| 308 | __hw_addr_del_entry(list, ha, false, false); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | EXPORT_SYMBOL(__hw_addr_unsync_dev); | ||
| 312 | |||
| 228 | static void __hw_addr_flush(struct netdev_hw_addr_list *list) | 313 | static void __hw_addr_flush(struct netdev_hw_addr_list *list) |
| 229 | { | 314 | { |
| 230 | struct netdev_hw_addr *ha, *tmp; | 315 | struct netdev_hw_addr *ha, *tmp; |
