return nla_data(tb);
 }
 
+/* Called from uadd only, protected by the set spinlock.
+ * The kadt functions don't use the comment extensions in any way.
+ */
 static inline void
 ip_set_init_comment(struct ip_set_comment *comment,
                    const struct ip_set_ext *ext)
 {
+       struct ip_set_comment_rcu *c = rcu_dereference_protected(comment->c, 1);
        size_t len = ext->comment ? strlen(ext->comment) : 0;
 
-       if (unlikely(comment->str)) {
-               kfree(comment->str);
-               comment->str = NULL;
+       if (unlikely(c)) {
+               kfree_rcu(c, rcu);
+               rcu_assign_pointer(comment->c, NULL);
        }
        if (!len)
                return;
        if (unlikely(len > IPSET_MAX_COMMENT_SIZE))
                len = IPSET_MAX_COMMENT_SIZE;
-       comment->str = kzalloc(len + 1, GFP_ATOMIC);
-       if (unlikely(!comment->str))
+       c = kzalloc(sizeof(*c) + len + 1, GFP_ATOMIC);
+       if (unlikely(!c))
                return;
-       strlcpy(comment->str, ext->comment, len + 1);
+       strlcpy(c->str, ext->comment, len + 1);
+       rcu_assign_pointer(comment->c, c);
 }
 
+/* Used only when dumping a set, protected by rcu_read_lock_bh() */
 static inline int
 ip_set_put_comment(struct sk_buff *skb, struct ip_set_comment *comment)
 {
-       if (!comment->str)
+       struct ip_set_comment_rcu *c = rcu_dereference_bh(comment->c);
+
+       if (!c)
                return 0;
-       return nla_put_string(skb, IPSET_ATTR_COMMENT, comment->str);
+       return nla_put_string(skb, IPSET_ATTR_COMMENT, c->str);
 }
 
+/* Called from uadd/udel, flush or the garbage collectors protected
+ * by the set spinlock.
+ * Called when the set is destroyed and when there can't be any user
+ * of the set data anymore.
+ */
 static inline void
 ip_set_comment_free(struct ip_set_comment *comment)
 {
-       if (unlikely(!comment->str))
+       struct ip_set_comment_rcu *c;
+
+       c = rcu_dereference_protected(comment->c, 1);
+       if (unlikely(!c))
                return;
-       kfree(comment->str);
-       comment->str = NULL;
+       kfree_rcu(c, rcu);
+       rcu_assign_pointer(comment->c, NULL);
 }
 
 #endif
 
                pr_warn("ip_set type %s, family %s with revision min %u already registered!\n",
                        type->name, family_name(type->family),
                        type->revision_min);
-               ret = -EINVAL;
-               goto unlock;
+               ip_set_type_unlock();
+               return -EINVAL;
        }
        list_add_rcu(&type->list, &ip_set_type_list);
        pr_debug("type %s, family %s, revision %u:%u registered.\n",
                 type->name, family_name(type->family),
                 type->revision_min, type->revision_max);
-unlock:
        ip_set_type_unlock();
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(ip_set_type_register);
                pr_warn("ip_set type %s, family %s with revision min %u not registered\n",
                        type->name, family_name(type->family),
                        type->revision_min);
-               goto unlock;
+               ip_set_type_unlock();
+               return;
        }
        list_del_rcu(&type->list);
        pr_debug("type %s, family %s with revision min %u unregistered.\n",
                 type->name, family_name(type->family), type->revision_min);
-unlock:
        ip_set_type_unlock();
 
        synchronize_rcu();
            !(opt->family == set->family || set->family == NFPROTO_UNSPEC))
                return 0;
 
-       read_lock_bh(&set->lock);
+       rcu_read_lock_bh();
        ret = set->variant->kadt(set, skb, par, IPSET_TEST, opt);
-       read_unlock_bh(&set->lock);
+       rcu_read_unlock_bh();
 
        if (ret == -EAGAIN) {
                /* Type requests element to be completed */
                pr_debug("element must be completed, ADD is triggered\n");
-               write_lock_bh(&set->lock);
+               spin_lock_bh(&set->lock);
                set->variant->kadt(set, skb, par, IPSET_ADD, opt);
-               write_unlock_bh(&set->lock);
+               spin_unlock_bh(&set->lock);
                ret = 1;
        } else {
                /* --return-nomatch: invert matched element */
            !(opt->family == set->family || set->family == NFPROTO_UNSPEC))
                return -IPSET_ERR_TYPE_MISMATCH;
 
-       write_lock_bh(&set->lock);
+       spin_lock_bh(&set->lock);
        ret = set->variant->kadt(set, skb, par, IPSET_ADD, opt);
-       write_unlock_bh(&set->lock);
+       spin_unlock_bh(&set->lock);
 
        return ret;
 }
            !(opt->family == set->family || set->family == NFPROTO_UNSPEC))
                return -IPSET_ERR_TYPE_MISMATCH;
 
-       write_lock_bh(&set->lock);
+       spin_lock_bh(&set->lock);
        ret = set->variant->kadt(set, skb, par, IPSET_DEL, opt);
-       write_unlock_bh(&set->lock);
+       spin_unlock_bh(&set->lock);
 
        return ret;
 }
        set = kzalloc(sizeof(struct ip_set), GFP_KERNEL);
        if (!set)
                return -ENOMEM;
-       rwlock_init(&set->lock);
+       spin_lock_init(&set->lock);
        strlcpy(set->name, name, IPSET_MAXNAMELEN);
        set->family = family;
        set->revision = revision;
 {
        pr_debug("set: %s\n",  set->name);
 
-       write_lock_bh(&set->lock);
+       spin_lock_bh(&set->lock);
        set->variant->flush(set);
-       write_unlock_bh(&set->lock);
+       spin_unlock_bh(&set->lock);
 }
 
 static int
                                set->variant->uref(set, cb, true);
                        /* Fall through and add elements */
                default:
-                       read_lock_bh(&set->lock);
+                       rcu_read_lock_bh();
                        ret = set->variant->list(set, skb, cb);
-                       read_unlock_bh(&set->lock);
+                       rcu_read_unlock_bh();
                        if (!cb->args[IPSET_CB_ARG0])
                                /* Set is done, proceed with next one */
                                goto next_set;
        bool eexist = flags & IPSET_FLAG_EXIST, retried = false;
 
        do {
-               write_lock_bh(&set->lock);
+               spin_lock_bh(&set->lock);
                ret = set->variant->uadt(set, tb, adt, &lineno, flags, retried);
-               write_unlock_bh(&set->lock);
+               spin_unlock_bh(&set->lock);
                retried = true;
        } while (ret == -EAGAIN &&
                 set->variant->resize &&
                             set->type->adt_policy))
                return -IPSET_ERR_PROTOCOL;
 
-       read_lock_bh(&set->lock);
+       rcu_read_lock_bh();
        ret = set->variant->uadt(set, tb, IPSET_TEST, NULL, 0, 0);
-       read_unlock_bh(&set->lock);
+       rcu_read_unlock_bh();
        /* Userspace can't trigger element to be re-added */
        if (ret == -EAGAIN)
                ret = 1;