diff options
author | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2011-04-04 09:19:25 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2011-04-04 09:19:25 -0400 |
commit | 2f9f28b212a2bd4948c8ceaaec33ce0123632129 (patch) | |
tree | 8c9961f59a190ab05a568216d69f4809ab3041e2 /net | |
parent | 512d06b5b64fb422d90f199b1be188082729edf9 (diff) |
netfilter: ipset: references are protected by rwlock instead of mutex
The timeout variant of the list:set type must reference the member sets.
However, its garbage collector runs at timer interrupt so the mutex
protection of the references is a no go. Therefore the reference protection
is converted to rwlock.
Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/ipset/ip_set_bitmap_ip.c | 3 | ||||
-rw-r--r-- | net/netfilter/ipset/ip_set_bitmap_ipmac.c | 3 | ||||
-rw-r--r-- | net/netfilter/ipset/ip_set_bitmap_port.c | 3 | ||||
-rw-r--r-- | net/netfilter/ipset/ip_set_core.c | 109 | ||||
-rw-r--r-- | net/netfilter/ipset/ip_set_list_set.c | 6 |
5 files changed, 71 insertions, 53 deletions
diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index bca96990218d..a113ff066928 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c | |||
@@ -338,8 +338,7 @@ bitmap_ip_head(struct ip_set *set, struct sk_buff *skb) | |||
338 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); | 338 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); |
339 | if (map->netmask != 32) | 339 | if (map->netmask != 32) |
340 | NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask); | 340 | NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask); |
341 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, | 341 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)); |
342 | htonl(atomic_read(&set->ref) - 1)); | ||
343 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, | 342 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, |
344 | htonl(sizeof(*map) + map->memsize)); | 343 | htonl(sizeof(*map) + map->memsize)); |
345 | if (with_timeout(map->timeout)) | 344 | if (with_timeout(map->timeout)) |
diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 5e790172deff..00a33242e90c 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c | |||
@@ -434,8 +434,7 @@ bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb) | |||
434 | goto nla_put_failure; | 434 | goto nla_put_failure; |
435 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip)); | 435 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip)); |
436 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); | 436 | NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); |
437 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, | 437 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)); |
438 | htonl(atomic_read(&set->ref) - 1)); | ||
439 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, | 438 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, |
440 | htonl(sizeof(*map) | 439 | htonl(sizeof(*map) |
441 | + (map->last_ip - map->first_ip + 1) * map->dsize)); | 440 | + (map->last_ip - map->first_ip + 1) * map->dsize)); |
diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index 165f09b1a9cb..6b38eb8f6ed8 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c | |||
@@ -320,8 +320,7 @@ bitmap_port_head(struct ip_set *set, struct sk_buff *skb) | |||
320 | goto nla_put_failure; | 320 | goto nla_put_failure; |
321 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port)); | 321 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port)); |
322 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port)); | 322 | NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port)); |
323 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, | 323 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)); |
324 | htonl(atomic_read(&set->ref) - 1)); | ||
325 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, | 324 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, |
326 | htonl(sizeof(*map) + map->memsize)); | 325 | htonl(sizeof(*map) + map->memsize)); |
327 | if (with_timeout(map->timeout)) | 326 | if (with_timeout(map->timeout)) |
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index d6b48230a540..e88ac3c3ed07 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c | |||
@@ -26,6 +26,7 @@ | |||
26 | 26 | ||
27 | static LIST_HEAD(ip_set_type_list); /* all registered set types */ | 27 | static LIST_HEAD(ip_set_type_list); /* all registered set types */ |
28 | static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ | 28 | static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ |
29 | static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */ | ||
29 | 30 | ||
30 | static struct ip_set **ip_set_list; /* all individual sets */ | 31 | static struct ip_set **ip_set_list; /* all individual sets */ |
31 | static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */ | 32 | static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */ |
@@ -301,13 +302,18 @@ EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6); | |||
301 | static inline void | 302 | static inline void |
302 | __ip_set_get(ip_set_id_t index) | 303 | __ip_set_get(ip_set_id_t index) |
303 | { | 304 | { |
304 | atomic_inc(&ip_set_list[index]->ref); | 305 | write_lock_bh(&ip_set_ref_lock); |
306 | ip_set_list[index]->ref++; | ||
307 | write_unlock_bh(&ip_set_ref_lock); | ||
305 | } | 308 | } |
306 | 309 | ||
307 | static inline void | 310 | static inline void |
308 | __ip_set_put(ip_set_id_t index) | 311 | __ip_set_put(ip_set_id_t index) |
309 | { | 312 | { |
310 | atomic_dec(&ip_set_list[index]->ref); | 313 | write_lock_bh(&ip_set_ref_lock); |
314 | BUG_ON(ip_set_list[index]->ref == 0); | ||
315 | ip_set_list[index]->ref--; | ||
316 | write_unlock_bh(&ip_set_ref_lock); | ||
311 | } | 317 | } |
312 | 318 | ||
313 | /* | 319 | /* |
@@ -324,7 +330,7 @@ ip_set_test(ip_set_id_t index, const struct sk_buff *skb, | |||
324 | struct ip_set *set = ip_set_list[index]; | 330 | struct ip_set *set = ip_set_list[index]; |
325 | int ret = 0; | 331 | int ret = 0; |
326 | 332 | ||
327 | BUG_ON(set == NULL || atomic_read(&set->ref) == 0); | 333 | BUG_ON(set == NULL); |
328 | pr_debug("set %s, index %u\n", set->name, index); | 334 | pr_debug("set %s, index %u\n", set->name, index); |
329 | 335 | ||
330 | if (dim < set->type->dimension || | 336 | if (dim < set->type->dimension || |
@@ -356,7 +362,7 @@ ip_set_add(ip_set_id_t index, const struct sk_buff *skb, | |||
356 | struct ip_set *set = ip_set_list[index]; | 362 | struct ip_set *set = ip_set_list[index]; |
357 | int ret; | 363 | int ret; |
358 | 364 | ||
359 | BUG_ON(set == NULL || atomic_read(&set->ref) == 0); | 365 | BUG_ON(set == NULL); |
360 | pr_debug("set %s, index %u\n", set->name, index); | 366 | pr_debug("set %s, index %u\n", set->name, index); |
361 | 367 | ||
362 | if (dim < set->type->dimension || | 368 | if (dim < set->type->dimension || |
@@ -378,7 +384,7 @@ ip_set_del(ip_set_id_t index, const struct sk_buff *skb, | |||
378 | struct ip_set *set = ip_set_list[index]; | 384 | struct ip_set *set = ip_set_list[index]; |
379 | int ret = 0; | 385 | int ret = 0; |
380 | 386 | ||
381 | BUG_ON(set == NULL || atomic_read(&set->ref) == 0); | 387 | BUG_ON(set == NULL); |
382 | pr_debug("set %s, index %u\n", set->name, index); | 388 | pr_debug("set %s, index %u\n", set->name, index); |
383 | 389 | ||
384 | if (dim < set->type->dimension || | 390 | if (dim < set->type->dimension || |
@@ -397,7 +403,6 @@ EXPORT_SYMBOL_GPL(ip_set_del); | |||
397 | * Find set by name, reference it once. The reference makes sure the | 403 | * Find set by name, reference it once. The reference makes sure the |
398 | * thing pointed to, does not go away under our feet. | 404 | * thing pointed to, does not go away under our feet. |
399 | * | 405 | * |
400 | * The nfnl mutex must already be activated. | ||
401 | */ | 406 | */ |
402 | ip_set_id_t | 407 | ip_set_id_t |
403 | ip_set_get_byname(const char *name, struct ip_set **set) | 408 | ip_set_get_byname(const char *name, struct ip_set **set) |
@@ -423,15 +428,12 @@ EXPORT_SYMBOL_GPL(ip_set_get_byname); | |||
423 | * reference count by 1. The caller shall not assume the index | 428 | * reference count by 1. The caller shall not assume the index |
424 | * to be valid, after calling this function. | 429 | * to be valid, after calling this function. |
425 | * | 430 | * |
426 | * The nfnl mutex must already be activated. | ||
427 | */ | 431 | */ |
428 | void | 432 | void |
429 | ip_set_put_byindex(ip_set_id_t index) | 433 | ip_set_put_byindex(ip_set_id_t index) |
430 | { | 434 | { |
431 | if (ip_set_list[index] != NULL) { | 435 | if (ip_set_list[index] != NULL) |
432 | BUG_ON(atomic_read(&ip_set_list[index]->ref) == 0); | ||
433 | __ip_set_put(index); | 436 | __ip_set_put(index); |
434 | } | ||
435 | } | 437 | } |
436 | EXPORT_SYMBOL_GPL(ip_set_put_byindex); | 438 | EXPORT_SYMBOL_GPL(ip_set_put_byindex); |
437 | 439 | ||
@@ -441,7 +443,6 @@ EXPORT_SYMBOL_GPL(ip_set_put_byindex); | |||
441 | * can't be destroyed. The set cannot be renamed due to | 443 | * can't be destroyed. The set cannot be renamed due to |
442 | * the referencing either. | 444 | * the referencing either. |
443 | * | 445 | * |
444 | * The nfnl mutex must already be activated. | ||
445 | */ | 446 | */ |
446 | const char * | 447 | const char * |
447 | ip_set_name_byindex(ip_set_id_t index) | 448 | ip_set_name_byindex(ip_set_id_t index) |
@@ -449,7 +450,7 @@ ip_set_name_byindex(ip_set_id_t index) | |||
449 | const struct ip_set *set = ip_set_list[index]; | 450 | const struct ip_set *set = ip_set_list[index]; |
450 | 451 | ||
451 | BUG_ON(set == NULL); | 452 | BUG_ON(set == NULL); |
452 | BUG_ON(atomic_read(&set->ref) == 0); | 453 | BUG_ON(set->ref == 0); |
453 | 454 | ||
454 | /* Referenced, so it's safe */ | 455 | /* Referenced, so it's safe */ |
455 | return set->name; | 456 | return set->name; |
@@ -515,10 +516,7 @@ void | |||
515 | ip_set_nfnl_put(ip_set_id_t index) | 516 | ip_set_nfnl_put(ip_set_id_t index) |
516 | { | 517 | { |
517 | nfnl_lock(); | 518 | nfnl_lock(); |
518 | if (ip_set_list[index] != NULL) { | 519 | ip_set_put_byindex(index); |
519 | BUG_ON(atomic_read(&ip_set_list[index]->ref) == 0); | ||
520 | __ip_set_put(index); | ||
521 | } | ||
522 | nfnl_unlock(); | 520 | nfnl_unlock(); |
523 | } | 521 | } |
524 | EXPORT_SYMBOL_GPL(ip_set_nfnl_put); | 522 | EXPORT_SYMBOL_GPL(ip_set_nfnl_put); |
@@ -526,7 +524,7 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_put); | |||
526 | /* | 524 | /* |
527 | * Communication protocol with userspace over netlink. | 525 | * Communication protocol with userspace over netlink. |
528 | * | 526 | * |
529 | * We already locked by nfnl_lock. | 527 | * The commands are serialized by the nfnl mutex. |
530 | */ | 528 | */ |
531 | 529 | ||
532 | static inline bool | 530 | static inline bool |
@@ -657,7 +655,6 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, | |||
657 | return -ENOMEM; | 655 | return -ENOMEM; |
658 | rwlock_init(&set->lock); | 656 | rwlock_init(&set->lock); |
659 | strlcpy(set->name, name, IPSET_MAXNAMELEN); | 657 | strlcpy(set->name, name, IPSET_MAXNAMELEN); |
660 | atomic_set(&set->ref, 0); | ||
661 | set->family = family; | 658 | set->family = family; |
662 | 659 | ||
663 | /* | 660 | /* |
@@ -690,8 +687,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, | |||
690 | 687 | ||
691 | /* | 688 | /* |
692 | * Here, we have a valid, constructed set and we are protected | 689 | * Here, we have a valid, constructed set and we are protected |
693 | * by nfnl_lock. Find the first free index in ip_set_list and | 690 | * by the nfnl mutex. Find the first free index in ip_set_list |
694 | * check clashing. | 691 | * and check clashing. |
695 | */ | 692 | */ |
696 | if ((ret = find_free_id(set->name, &index, &clash)) != 0) { | 693 | if ((ret = find_free_id(set->name, &index, &clash)) != 0) { |
697 | /* If this is the same set and requested, ignore error */ | 694 | /* If this is the same set and requested, ignore error */ |
@@ -751,31 +748,51 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, | |||
751 | const struct nlattr * const attr[]) | 748 | const struct nlattr * const attr[]) |
752 | { | 749 | { |
753 | ip_set_id_t i; | 750 | ip_set_id_t i; |
751 | int ret = 0; | ||
754 | 752 | ||
755 | if (unlikely(protocol_failed(attr))) | 753 | if (unlikely(protocol_failed(attr))) |
756 | return -IPSET_ERR_PROTOCOL; | 754 | return -IPSET_ERR_PROTOCOL; |
757 | 755 | ||
758 | /* References are protected by the nfnl mutex */ | 756 | /* Commands are serialized and references are |
757 | * protected by the ip_set_ref_lock. | ||
758 | * External systems (i.e. xt_set) must call | ||
759 | * ip_set_put|get_nfnl_* functions, that way we | ||
760 | * can safely check references here. | ||
761 | * | ||
762 | * list:set timer can only decrement the reference | ||
763 | * counter, so if it's already zero, we can proceed | ||
764 | * without holding the lock. | ||
765 | */ | ||
766 | read_lock_bh(&ip_set_ref_lock); | ||
759 | if (!attr[IPSET_ATTR_SETNAME]) { | 767 | if (!attr[IPSET_ATTR_SETNAME]) { |
760 | for (i = 0; i < ip_set_max; i++) { | 768 | for (i = 0; i < ip_set_max; i++) { |
761 | if (ip_set_list[i] != NULL && | 769 | if (ip_set_list[i] != NULL && ip_set_list[i]->ref) { |
762 | (atomic_read(&ip_set_list[i]->ref))) | 770 | ret = IPSET_ERR_BUSY; |
763 | return -IPSET_ERR_BUSY; | 771 | goto out; |
772 | } | ||
764 | } | 773 | } |
774 | read_unlock_bh(&ip_set_ref_lock); | ||
765 | for (i = 0; i < ip_set_max; i++) { | 775 | for (i = 0; i < ip_set_max; i++) { |
766 | if (ip_set_list[i] != NULL) | 776 | if (ip_set_list[i] != NULL) |
767 | ip_set_destroy_set(i); | 777 | ip_set_destroy_set(i); |
768 | } | 778 | } |
769 | } else { | 779 | } else { |
770 | i = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); | 780 | i = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); |
771 | if (i == IPSET_INVALID_ID) | 781 | if (i == IPSET_INVALID_ID) { |
772 | return -ENOENT; | 782 | ret = -ENOENT; |
773 | else if (atomic_read(&ip_set_list[i]->ref)) | 783 | goto out; |
774 | return -IPSET_ERR_BUSY; | 784 | } else if (ip_set_list[i]->ref) { |
785 | ret = -IPSET_ERR_BUSY; | ||
786 | goto out; | ||
787 | } | ||
788 | read_unlock_bh(&ip_set_ref_lock); | ||
775 | 789 | ||
776 | ip_set_destroy_set(i); | 790 | ip_set_destroy_set(i); |
777 | } | 791 | } |
778 | return 0; | 792 | return 0; |
793 | out: | ||
794 | read_unlock_bh(&ip_set_ref_lock); | ||
795 | return ret; | ||
779 | } | 796 | } |
780 | 797 | ||
781 | /* Flush sets */ | 798 | /* Flush sets */ |
@@ -834,6 +851,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, | |||
834 | struct ip_set *set; | 851 | struct ip_set *set; |
835 | const char *name2; | 852 | const char *name2; |
836 | ip_set_id_t i; | 853 | ip_set_id_t i; |
854 | int ret = 0; | ||
837 | 855 | ||
838 | if (unlikely(protocol_failed(attr) || | 856 | if (unlikely(protocol_failed(attr) || |
839 | attr[IPSET_ATTR_SETNAME] == NULL || | 857 | attr[IPSET_ATTR_SETNAME] == NULL || |
@@ -843,25 +861,33 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, | |||
843 | set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); | 861 | set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); |
844 | if (set == NULL) | 862 | if (set == NULL) |
845 | return -ENOENT; | 863 | return -ENOENT; |
846 | if (atomic_read(&set->ref) != 0) | 864 | |
847 | return -IPSET_ERR_REFERENCED; | 865 | read_lock_bh(&ip_set_ref_lock); |
866 | if (set->ref != 0) { | ||
867 | ret = -IPSET_ERR_REFERENCED; | ||
868 | goto out; | ||
869 | } | ||
848 | 870 | ||
849 | name2 = nla_data(attr[IPSET_ATTR_SETNAME2]); | 871 | name2 = nla_data(attr[IPSET_ATTR_SETNAME2]); |
850 | for (i = 0; i < ip_set_max; i++) { | 872 | for (i = 0; i < ip_set_max; i++) { |
851 | if (ip_set_list[i] != NULL && | 873 | if (ip_set_list[i] != NULL && |
852 | STREQ(ip_set_list[i]->name, name2)) | 874 | STREQ(ip_set_list[i]->name, name2)) { |
853 | return -IPSET_ERR_EXIST_SETNAME2; | 875 | ret = -IPSET_ERR_EXIST_SETNAME2; |
876 | goto out; | ||
877 | } | ||
854 | } | 878 | } |
855 | strncpy(set->name, name2, IPSET_MAXNAMELEN); | 879 | strncpy(set->name, name2, IPSET_MAXNAMELEN); |
856 | 880 | ||
857 | return 0; | 881 | out: |
882 | read_unlock_bh(&ip_set_ref_lock); | ||
883 | return ret; | ||
858 | } | 884 | } |
859 | 885 | ||
860 | /* Swap two sets so that name/index points to the other. | 886 | /* Swap two sets so that name/index points to the other. |
861 | * References and set names are also swapped. | 887 | * References and set names are also swapped. |
862 | * | 888 | * |
863 | * We are protected by the nfnl mutex and references are | 889 | * The commands are serialized by the nfnl mutex and references are |
864 | * manipulated only by holding the mutex. The kernel interfaces | 890 | * protected by the ip_set_ref_lock. The kernel interfaces |
865 | * do not hold the mutex but the pointer settings are atomic | 891 | * do not hold the mutex but the pointer settings are atomic |
866 | * so the ip_set_list always contains valid pointers to the sets. | 892 | * so the ip_set_list always contains valid pointers to the sets. |
867 | */ | 893 | */ |
@@ -874,7 +900,6 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, | |||
874 | struct ip_set *from, *to; | 900 | struct ip_set *from, *to; |
875 | ip_set_id_t from_id, to_id; | 901 | ip_set_id_t from_id, to_id; |
876 | char from_name[IPSET_MAXNAMELEN]; | 902 | char from_name[IPSET_MAXNAMELEN]; |
877 | u32 from_ref; | ||
878 | 903 | ||
879 | if (unlikely(protocol_failed(attr) || | 904 | if (unlikely(protocol_failed(attr) || |
880 | attr[IPSET_ATTR_SETNAME] == NULL || | 905 | attr[IPSET_ATTR_SETNAME] == NULL || |
@@ -899,17 +924,15 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, | |||
899 | from->type->family == to->type->family)) | 924 | from->type->family == to->type->family)) |
900 | return -IPSET_ERR_TYPE_MISMATCH; | 925 | return -IPSET_ERR_TYPE_MISMATCH; |
901 | 926 | ||
902 | /* No magic here: ref munging protected by the nfnl_lock */ | ||
903 | strncpy(from_name, from->name, IPSET_MAXNAMELEN); | 927 | strncpy(from_name, from->name, IPSET_MAXNAMELEN); |
904 | from_ref = atomic_read(&from->ref); | ||
905 | |||
906 | strncpy(from->name, to->name, IPSET_MAXNAMELEN); | 928 | strncpy(from->name, to->name, IPSET_MAXNAMELEN); |
907 | atomic_set(&from->ref, atomic_read(&to->ref)); | ||
908 | strncpy(to->name, from_name, IPSET_MAXNAMELEN); | 929 | strncpy(to->name, from_name, IPSET_MAXNAMELEN); |
909 | atomic_set(&to->ref, from_ref); | ||
910 | 930 | ||
931 | write_lock_bh(&ip_set_ref_lock); | ||
932 | swap(from->ref, to->ref); | ||
911 | ip_set_list[from_id] = to; | 933 | ip_set_list[from_id] = to; |
912 | ip_set_list[to_id] = from; | 934 | ip_set_list[to_id] = from; |
935 | write_unlock_bh(&ip_set_ref_lock); | ||
913 | 936 | ||
914 | return 0; | 937 | return 0; |
915 | } | 938 | } |
@@ -926,7 +949,7 @@ ip_set_dump_done(struct netlink_callback *cb) | |||
926 | { | 949 | { |
927 | if (cb->args[2]) { | 950 | if (cb->args[2]) { |
928 | pr_debug("release set %s\n", ip_set_list[cb->args[1]]->name); | 951 | pr_debug("release set %s\n", ip_set_list[cb->args[1]]->name); |
929 | __ip_set_put((ip_set_id_t) cb->args[1]); | 952 | ip_set_put_byindex((ip_set_id_t) cb->args[1]); |
930 | } | 953 | } |
931 | return 0; | 954 | return 0; |
932 | } | 955 | } |
@@ -1068,7 +1091,7 @@ release_refcount: | |||
1068 | /* If there was an error or set is done, release set */ | 1091 | /* If there was an error or set is done, release set */ |
1069 | if (ret || !cb->args[2]) { | 1092 | if (ret || !cb->args[2]) { |
1070 | pr_debug("release set %s\n", ip_set_list[index]->name); | 1093 | pr_debug("release set %s\n", ip_set_list[index]->name); |
1071 | __ip_set_put(index); | 1094 | ip_set_put_byindex(index); |
1072 | } | 1095 | } |
1073 | 1096 | ||
1074 | /* If we dump all sets, continue with dumping last ones */ | 1097 | /* If we dump all sets, continue with dumping last ones */ |
diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index f4a46c0d25f3..e9159e99fc4b 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c | |||
@@ -366,8 +366,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb) | |||
366 | NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size)); | 366 | NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size)); |
367 | if (with_timeout(map->timeout)) | 367 | if (with_timeout(map->timeout)) |
368 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); | 368 | NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); |
369 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, | 369 | NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)); |
370 | htonl(atomic_read(&set->ref) - 1)); | ||
371 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, | 370 | NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, |
372 | htonl(sizeof(*map) + map->size * map->dsize)); | 371 | htonl(sizeof(*map) + map->size * map->dsize)); |
373 | ipset_nest_end(skb, nested); | 372 | ipset_nest_end(skb, nested); |
@@ -457,8 +456,7 @@ list_set_gc(unsigned long ul_set) | |||
457 | struct list_set *map = set->data; | 456 | struct list_set *map = set->data; |
458 | struct set_telem *e; | 457 | struct set_telem *e; |
459 | u32 i; | 458 | u32 i; |
460 | 459 | ||
461 | /* nfnl_lock should be called */ | ||
462 | write_lock_bh(&set->lock); | 460 | write_lock_bh(&set->lock); |
463 | for (i = 0; i < map->size; i++) { | 461 | for (i = 0; i < map->size; i++) { |
464 | e = list_set_telem(map, i); | 462 | e = list_set_telem(map, i); |