]> granicus.if.org Git - ipset/commitdiff
Count non-static extension memory into the set memory size for userspace
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Fri, 26 Jun 2015 07:40:14 +0000 (09:40 +0200)
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Fri, 26 Jun 2015 07:40:14 +0000 (09:40 +0200)
Non-static (i.e. comment) extension was not counted into the memory
size. A new internal counter is introduced for this. In the case of
the hash types the sizes of the arrays are counted there as well so
that we can avoid to scan the whole set when just the header data
is requested.

kernel/include/linux/netfilter/ipset/ip_set.h
kernel/include/linux/netfilter/ipset/ip_set_comment.h
kernel/net/netfilter/ipset/ip_set_bitmap_gen.h
kernel/net/netfilter/ipset/ip_set_core.c
kernel/net/netfilter/ipset/ip_set_hash_gen.h
kernel/net/netfilter/ipset/ip_set_list_set.c

index 0ed3ac5f8720d8a1888196ad4aa819f357cffda0..1a5094e8692ef88e1dfa5f4ac3931ae1db631e98 100644 (file)
@@ -80,10 +80,12 @@ enum ip_set_ext_id {
        IPSET_EXT_ID_MAX,
 };
 
+struct ip_set;
+
 /* Extension type */
 struct ip_set_ext_type {
        /* Destroy extension private data (can be NULL) */
-       void (*destroy)(void *ext);
+       void (*destroy)(struct ip_set *set, void *ext);
        enum ip_set_extension type;
        enum ipset_cadt_flags flag;
        /* Size and minimal alignment */
@@ -249,6 +251,8 @@ struct ip_set {
        u32 timeout;
        /* Number of elements (vs timeout) */
        u32 elements;
+       /* Size of the dynamic extensions (vs timeout) */
+       size_t ext_size;
        /* Element data size */
        size_t dsize;
        /* Offsets to extensions in elements */
@@ -265,7 +269,7 @@ ip_set_ext_destroy(struct ip_set *set, void *data)
         */
        if (SET_WITH_COMMENT(set))
                ip_set_extensions[IPSET_EXT_ID_COMMENT].destroy(
-                       ext_comment(data, set));
+                       set, ext_comment(data, set));
 }
 
 static inline int
index b31169c36d74c864d2b19ed3efb9d6220e52d868..0537c37dacfabfe3c6b4269ebfbce3649e6f75e9 100644 (file)
@@ -20,13 +20,14 @@ ip_set_comment_uget(struct nlattr *tb)
  * The kadt functions don't use the comment extensions in any way.
  */
 static inline void
-ip_set_init_comment(struct ip_set_comment *comment,
+ip_set_init_comment(struct ip_set *set, 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(c)) {
+               set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
                kfree_rcu(c, rcu);
                rcu_assign_pointer(comment->c, NULL);
        }
@@ -38,6 +39,7 @@ ip_set_init_comment(struct ip_set_comment *comment,
        if (unlikely(!c))
                return;
        strlcpy(c->str, ext->comment, len + 1);
+       set->ext_size += sizeof(*c) + strlen(c->str) + 1;
        rcu_assign_pointer(comment->c, c);
 }
 
@@ -58,13 +60,14 @@ ip_set_put_comment(struct sk_buff *skb, const struct ip_set_comment *comment)
  * of the set data anymore.
  */
 static inline void
-ip_set_comment_free(struct ip_set_comment *comment)
+ip_set_comment_free(struct ip_set *set, struct ip_set_comment *comment)
 {
        struct ip_set_comment_rcu *c;
 
        c = rcu_dereference_protected(comment->c, 1);
        if (unlikely(!c))
                return;
+       set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
        kfree_rcu(c, rcu);
        rcu_assign_pointer(comment->c, NULL);
 }
index efe87cf15fa80440a9eb9b06ea6e86b73c2bedaa..9b5f81fa43a6ee45f31a63e36fcbb17cb0db38d5 100644 (file)
@@ -87,6 +87,7 @@ mtype_flush(struct ip_set *set)
                mtype_ext_cleanup(set);
        memset(map->members, 0, map->memsize);
        set->elements = 0;
+       set->ext_size = 0;
 }
 
 /* Calculate the actual memory size of the set data */
@@ -105,7 +106,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
 {
        const struct mtype *map = set->data;
        struct nlattr *nested;
-       size_t memsize = mtype_memsize(map, set->dsize);
+       size_t memsize = mtype_memsize(map, set->dsize) + set->ext_size;
 
        nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
        if (!nested)
@@ -179,7 +180,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
        if (SET_WITH_COUNTER(set))
                ip_set_init_counter(ext_counter(x, set), ext);
        if (SET_WITH_COMMENT(set))
-               ip_set_init_comment(ext_comment(x, set), ext);
+               ip_set_init_comment(set, ext_comment(x, set), ext);
        if (SET_WITH_SKBINFO(set))
                ip_set_init_skbinfo(ext_skbinfo(x, set), ext);
 
index 162469852ee8920ff5002831ad655b667ad0b939..334a4d8463262116154209256e2c00ade0c47b96 100644 (file)
@@ -329,7 +329,7 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr)
 }
 EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6);
 
-typedef void (*destroyer)(void *);
+typedef void (*destroyer)(struct ip_set *, void *);
 /* ipset data extension types, in size order */
 
 const struct ip_set_ext_type ip_set_extensions[] = {
index 191707df7092173523ebd5b8a0bdbed7287f3f07..d9a6699b72b12ffcc04f38ccb1c5003af7890314 100644 (file)
@@ -340,21 +340,13 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
 /* Calculate the actual memory size of the set data */
 static size_t
 mtype_ahash_memsize(const struct htype *h, const struct htable *t,
-                   u8 nets_length, size_t dsize)
+                   u8 nets_length)
 {
-       u32 i;
-       struct hbucket *n;
        size_t memsize = sizeof(*h) + sizeof(*t);
 
 #ifdef IP_SET_HASH_WITH_NETS
        memsize += sizeof(struct net_prefixes) * nets_length;
 #endif
-       for (i = 0; i < jhash_size(t->htable_bits); i++) {
-               n = rcu_dereference_bh(hbucket(t, i));
-               if (!n)
-                       continue;
-               memsize += sizeof(struct hbucket) + n->size * dsize;
-       }
 
        return memsize;
 }
@@ -397,6 +389,7 @@ mtype_flush(struct ip_set *set)
        memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN(set->family));
 #endif
        set->elements = 0;
+       set->ext_size = 0;
 }
 
 /* Destroy the hashtable part of the set */
@@ -523,6 +516,7 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize)
                                d++;
                        }
                        tmp->pos = d;
+                       set->ext_size -= AHASH_INIT_SIZE * dsize;
                        rcu_assign_pointer(hbucket(t, i), tmp);
                        kfree_rcu(n, rcu);
                }
@@ -554,7 +548,7 @@ mtype_resize(struct ip_set *set, bool retried)
        struct htype *h = set->data;
        struct htable *t, *orig;
        u8 htable_bits;
-       size_t dsize = set->dsize;
+       size_t extsize, dsize = set->dsize;
 #ifdef IP_SET_HASH_WITH_NETS
        u8 flags;
        struct mtype_elem *tmp;
@@ -597,6 +591,7 @@ retry:
        /* There can't be another parallel resizing, but dumping is possible */
        atomic_set(&orig->ref, 1);
        atomic_inc(&orig->uref);
+       extsize = 0;
        pr_debug("attempt to resize set %s from %u to %u, t %p\n",
                 set->name, orig->htable_bits, htable_bits, orig);
        for (i = 0; i < jhash_size(orig->htable_bits); i++) {
@@ -627,6 +622,7 @@ retry:
                                        goto cleanup;
                                }
                                m->size = AHASH_INIT_SIZE;
+                               extsize = sizeof(*m) + AHASH_INIT_SIZE * dsize;
                                RCU_INIT_POINTER(hbucket(t, key), m);
                        } else if (m->pos >= m->size) {
                                struct hbucket *ht;
@@ -646,6 +642,7 @@ retry:
                                memcpy(ht, m, sizeof(struct hbucket) +
                                              m->size * dsize);
                                ht->size = m->size + AHASH_INIT_SIZE;
+                               extsize += AHASH_INIT_SIZE * dsize;
                                kfree(m);
                                m = ht;
                                RCU_INIT_POINTER(hbucket(t, key), ht);
@@ -659,6 +656,7 @@ retry:
                }
        }
        rcu_assign_pointer(h->table, t);
+       set->ext_size = extsize;
 
        spin_unlock_bh(&set->lock);
 
@@ -732,6 +730,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
                if (!n)
                        return -ENOMEM;
                n->size = AHASH_INIT_SIZE;
+               set->ext_size += sizeof(*n) + AHASH_INIT_SIZE * set->dsize;
                goto copy_elem;
        }
        for (i = 0; i < n->pos; i++) {
@@ -795,6 +794,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
                memcpy(n, old, sizeof(struct hbucket) +
                       old->size * set->dsize);
                n->size = old->size + AHASH_INIT_SIZE;
+               set->ext_size += AHASH_INIT_SIZE * set->dsize;
        }
 
 copy_elem:
@@ -815,7 +815,7 @@ overwrite_extensions:
        if (SET_WITH_COUNTER(set))
                ip_set_init_counter(ext_counter(data, set), ext);
        if (SET_WITH_COMMENT(set))
-               ip_set_init_comment(ext_comment(data, set), ext);
+               ip_set_init_comment(set, ext_comment(data, set), ext);
        if (SET_WITH_SKBINFO(set))
                ip_set_init_skbinfo(ext_skbinfo(data, set), ext);
        /* Must come last for the case when timed out entry is reused */
@@ -887,6 +887,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
                                k++;
                }
                if (n->pos == 0 && k == 0) {
+                       set->ext_size -= sizeof(*n) + n->size * dsize;
                        rcu_assign_pointer(hbucket(t, key), NULL);
                        kfree_rcu(n, rcu);
                } else if (k >= AHASH_INIT_SIZE) {
@@ -905,6 +906,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
                                k++;
                        }
                        tmp->pos = k;
+                       set->ext_size -= AHASH_INIT_SIZE * dsize;
                        rcu_assign_pointer(hbucket(t, key), tmp);
                        kfree_rcu(n, rcu);
                }
@@ -1053,7 +1055,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
 
        rcu_read_lock_bh();
        t = rcu_dereference_bh_nfnl(h->table, NFNL_SUBSYS_IPSET);
-       memsize = mtype_ahash_memsize(h, t, NLEN(set->family), set->dsize);
+       memsize = mtype_ahash_memsize(h, t, NLEN(set->family)) + set->ext_size;
        htable_bits = t->htable_bits;
        rcu_read_unlock_bh();
 
index db0198d1d1a62bdd17510d1561b802a843c575ee..b11ba96b4fdf91857036c42407c82d929cca6c18 100644 (file)
@@ -228,7 +228,7 @@ list_set_init_extensions(struct ip_set *set, const struct ip_set_ext *ext,
        if (SET_WITH_COUNTER(set))
                ip_set_init_counter(ext_counter(e, set), ext);
        if (SET_WITH_COMMENT(set))
-               ip_set_init_comment(ext_comment(e, set), ext);
+               ip_set_init_comment(set, ext_comment(e, set), ext);
        if (SET_WITH_SKBINFO(set))
                ip_set_init_skbinfo(ext_skbinfo(e, set), ext);
        /* Update timeout last */
@@ -424,6 +424,7 @@ list_set_flush(struct ip_set *set)
        list_for_each_entry_safe(e, n, &map->members, list)
                list_set_del(set, e);
        set->elements = 0;
+       set->ext_size = 0;
 }
 
 static void
@@ -466,7 +467,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
 {
        const struct list_set *map = set->data;
        struct nlattr *nested;
-       size_t memsize = list_set_memsize(map, set->dsize);
+       size_t memsize = list_set_memsize(map, set->dsize) + set->ext_size;
 
        nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
        if (!nested)