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 /net | |
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/core/dev_addr_lists.c | 85 |
1 files changed, 85 insertions, 0 deletions
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; |