This commit was generated by cvs2svn to compensate for changes in r1129,
[linux-2.6.git] / net / ipv4 / netfilter / ipt_hashlimit.c
index 04c95d8..4fe48c1 100644 (file)
 #include <linux/sctp.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-
-#define ASSERT_READ_LOCK(x) 
-#define ASSERT_WRITE_LOCK(x) 
-#include <linux/netfilter_ipv4/lockhelp.h>
-#include <linux/netfilter_ipv4/listhelp.h>
+#include <linux/list.h>
 
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_hashlimit.h>
@@ -45,8 +41,6 @@
 /* FIXME: this is just for IP_NF_ASSERRT */
 #include <linux/netfilter_ipv4/ip_conntrack.h>
 
-#define MS2JIFFIES(x) ((x*HZ)/1000)
-
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
 MODULE_DESCRIPTION("iptables match for limiting per hash-bucket");
@@ -67,7 +61,7 @@ struct dsthash_dst {
 
 struct dsthash_ent {
        /* static / read-only parts in the beginning */
-       struct list_head list;
+       struct hlist_node node;
        struct dsthash_dst dst;
 
        /* modified structure members in the end */
@@ -80,7 +74,7 @@ struct dsthash_ent {
 };
 
 struct ipt_hashlimit_htable {
-       struct list_head list;          /* global list of all htables */
+       struct hlist_node node;         /* global list of all htables */
        atomic_t use;
 
        struct hashlimit_cfg cfg;       /* config */
@@ -94,12 +88,13 @@ struct ipt_hashlimit_htable {
        /* seq_file stuff */
        struct proc_dir_entry *pde;
 
-       struct list_head hash[0];       /* hashtable itself */
+       struct hlist_head hash[0];      /* hashtable itself */
 };
 
-DECLARE_RWLOCK(hashlimit_lock);                /* protects htables list */
-static LIST_HEAD(hashlimit_htables);
-static kmem_cache_t *hashlimit_cachep;
+static DEFINE_SPINLOCK(hashlimit_lock);        /* protects htables list */
+static DECLARE_MUTEX(hlimit_mutex);    /* additional checkentry protection */
+static HLIST_HEAD(hashlimit_htables);
+static kmem_cache_t *hashlimit_cachep __read_mostly;
 
 static inline int dst_cmp(const struct dsthash_ent *ent, struct dsthash_dst *b)
 {
@@ -112,7 +107,7 @@ static inline int dst_cmp(const struct dsthash_ent *ent, struct dsthash_dst *b)
 static inline u_int32_t
 hash_dst(const struct ipt_hashlimit_htable *ht, const struct dsthash_dst *dst)
 {
-       return (jhash_3words(dst->dst_ip, (dst->dst_port<<16 & dst->src_port), 
+       return (jhash_3words(dst->dst_ip, (dst->dst_port<<16 | dst->src_port), 
                             dst->src_ip, ht->rnd) % ht->cfg.size);
 }
 
@@ -120,9 +115,17 @@ static inline struct dsthash_ent *
 __dsthash_find(const struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst)
 {
        struct dsthash_ent *ent;
+       struct hlist_node *pos;
        u_int32_t hash = hash_dst(ht, dst);
-       ent = LIST_FIND(&ht->hash[hash], dst_cmp, struct dsthash_ent *, dst);
-       return ent;
+
+       if (!hlist_empty(&ht->hash[hash]))
+               hlist_for_each_entry(ent, pos, &ht->hash[hash], node) {
+                       if (dst_cmp(ent, dst)) {
+                               return ent;
+                       }
+               }
+       
+       return NULL;
 }
 
 /* allocate dsthash_ent, initialize dst, put in htable and lock it */
@@ -161,7 +164,7 @@ __dsthash_alloc_init(struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst)
        ent->dst.src_ip = dst->src_ip;
        ent->dst.src_port = dst->src_port;
 
-       list_add(&ent->list, &ht->hash[hash_dst(ht, dst)]);
+       hlist_add_head(&ent->node, &ht->hash[hash_dst(ht, dst)]);
 
        return ent;
 }
@@ -169,7 +172,7 @@ __dsthash_alloc_init(struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst)
 static inline void 
 __dsthash_free(struct ipt_hashlimit_htable *ht, struct dsthash_ent *ent)
 {
-       list_del(&ent->list);
+       hlist_del(&ent->node);
        kmem_cache_free(hashlimit_cachep, ent);
        atomic_dec(&ht->count);
 }
@@ -209,7 +212,7 @@ static int htable_create(struct ipt_hashlimit_info *minfo)
                hinfo->cfg.max = hinfo->cfg.size;
 
        for (i = 0; i < hinfo->cfg.size; i++)
-               INIT_LIST_HEAD(&hinfo->hash[i]);
+               INIT_HLIST_HEAD(&hinfo->hash[i]);
 
        atomic_set(&hinfo->count, 0);
        atomic_set(&hinfo->use, 1);
@@ -224,14 +227,14 @@ static int htable_create(struct ipt_hashlimit_info *minfo)
        hinfo->pde->data = hinfo;
 
        init_timer(&hinfo->timer);
-       hinfo->timer.expires = jiffies + MS2JIFFIES(hinfo->cfg.gc_interval);
+       hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);
        hinfo->timer.data = (unsigned long )hinfo;
        hinfo->timer.function = htable_gc;
        add_timer(&hinfo->timer);
 
-       WRITE_LOCK(&hashlimit_lock);
-       list_add(&hinfo->list, &hashlimit_htables);
-       WRITE_UNLOCK(&hashlimit_lock);
+       spin_lock_bh(&hashlimit_lock);
+       hlist_add_head(&hinfo->node, &hashlimit_htables);
+       spin_unlock_bh(&hashlimit_lock);
 
        return 0;
 }
@@ -257,8 +260,9 @@ static void htable_selective_cleanup(struct ipt_hashlimit_htable *ht,
        /* lock hash table and iterate over it */
        spin_lock_bh(&ht->lock);
        for (i = 0; i < ht->cfg.size; i++) {
-               struct dsthash_ent *dh, *n;
-               list_for_each_entry_safe(dh, n, &ht->hash[i], list) {
+               struct dsthash_ent *dh;
+               struct hlist_node *pos, *n;
+               hlist_for_each_entry_safe(dh, pos, n, &ht->hash[i], node) {
                        if ((*select)(ht, dh))
                                __dsthash_free(ht, dh);
                }
@@ -274,7 +278,7 @@ static void htable_gc(unsigned long htlong)
        htable_selective_cleanup(ht, select_gc);
 
        /* re-add the timer accordingly */
-       ht->timer.expires = jiffies + MS2JIFFIES(ht->cfg.gc_interval);
+       ht->timer.expires = jiffies + msecs_to_jiffies(ht->cfg.gc_interval);
        add_timer(&ht->timer);
 }
 
@@ -294,16 +298,17 @@ static void htable_destroy(struct ipt_hashlimit_htable *hinfo)
 static struct ipt_hashlimit_htable *htable_find_get(char *name)
 {
        struct ipt_hashlimit_htable *hinfo;
+       struct hlist_node *pos;
 
-       READ_LOCK(&hashlimit_lock);
-       list_for_each_entry(hinfo, &hashlimit_htables, list) {
+       spin_lock_bh(&hashlimit_lock);
+       hlist_for_each_entry(hinfo, pos, &hashlimit_htables, node) {
                if (!strcmp(name, hinfo->pde->name)) {
                        atomic_inc(&hinfo->use);
-                       READ_UNLOCK(&hashlimit_lock);
+                       spin_unlock_bh(&hashlimit_lock);
                        return hinfo;
                }
        }
-       READ_UNLOCK(&hashlimit_lock);
+       spin_unlock_bh(&hashlimit_lock);
 
        return NULL;
 }
@@ -311,9 +316,9 @@ static struct ipt_hashlimit_htable *htable_find_get(char *name)
 static void htable_put(struct ipt_hashlimit_htable *hinfo)
 {
        if (atomic_dec_and_test(&hinfo->use)) {
-               WRITE_LOCK(&hashlimit_lock);
-               list_del(&hinfo->list);
-               WRITE_UNLOCK(&hashlimit_lock);
+               spin_lock_bh(&hashlimit_lock);
+               hlist_del(&hinfo->node);
+               spin_unlock_bh(&hashlimit_lock);
                htable_destroy(hinfo);
        }
 }
@@ -424,6 +429,7 @@ hashlimit_match(const struct sk_buff *skb,
                const struct net_device *out,
                const void *matchinfo,
                int offset,
+               unsigned int protoff,
                int *hotdrop)
 {
        struct ipt_hashlimit_info *r = 
@@ -467,7 +473,7 @@ hashlimit_match(const struct sk_buff *skb,
                        return 0;
                }
 
-               dh->expires = jiffies + MS2JIFFIES(hinfo->cfg.expire);
+               dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire);
 
                dh->rateinfo.prev = jiffies;
                dh->rateinfo.credit = user2credits(hinfo->cfg.avg * 
@@ -481,7 +487,7 @@ hashlimit_match(const struct sk_buff *skb,
        }
 
        /* update expiration timeout */
-       dh->expires = now + MS2JIFFIES(hinfo->cfg.expire);
+       dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire);
 
        rateinfo_recalc(dh, now);
        if (dh->rateinfo.credit >= dh->rateinfo.cost) {
@@ -499,7 +505,7 @@ hashlimit_match(const struct sk_buff *skb,
 
 static int
 hashlimit_checkentry(const char *tablename,
-                    const struct ipt_ip *ip,
+                    const void *inf,
                     void *matchinfo,
                     unsigned int matchsize,
                     unsigned int hook_mask)
@@ -531,10 +537,19 @@ hashlimit_checkentry(const char *tablename,
        if (!r->cfg.expire)
                return 0;
 
+       /* This is the best we've got: We cannot release and re-grab lock,
+        * since checkentry() is called before ip_tables.c grabs ipt_mutex.  
+        * We also cannot grab the hashtable spinlock, since htable_create will 
+        * call vmalloc, and that can sleep.  And we cannot just re-search
+        * the list of htable's in htable_create(), since then we would
+        * create duplicate proc files. -HW */
+       down(&hlimit_mutex);
        r->hinfo = htable_find_get(r->name);
        if (!r->hinfo && (htable_create(r) != 0)) {
+               up(&hlimit_mutex);
                return 0;
        }
+       up(&hlimit_mutex);
 
        /* Ugly hack: For SMP, we only want to use one set */
        r->u.master = r;
@@ -570,7 +585,7 @@ static void *dl_seq_start(struct seq_file *s, loff_t *pos)
        if (*pos >= htable->cfg.size)
                return NULL;
 
-       bucket = kmalloc(sizeof(unsigned int), GFP_KERNEL);
+       bucket = kmalloc(sizeof(unsigned int), GFP_ATOMIC);
        if (!bucket)
                return ERR_PTR(-ENOMEM);
 
@@ -609,7 +624,7 @@ static inline int dl_seq_real_show(struct dsthash_ent *ent, struct seq_file *s)
        rateinfo_recalc(ent, jiffies);
 
        return seq_printf(s, "%ld %u.%u.%u.%u:%u->%u.%u.%u.%u:%u %u %u %u\n",
-                       (ent->expires - jiffies)/HZ,
+                       (long)(ent->expires - jiffies)/HZ,
                        NIPQUAD(ent->dst.src_ip), ntohs(ent->dst.src_port),
                        NIPQUAD(ent->dst.dst_ip), ntohs(ent->dst.dst_port),
                        ent->rateinfo.credit, ent->rateinfo.credit_cap,
@@ -621,12 +636,17 @@ static int dl_seq_show(struct seq_file *s, void *v)
        struct proc_dir_entry *pde = s->private;
        struct ipt_hashlimit_htable *htable = pde->data;
        unsigned int *bucket = (unsigned int *)v;
-
-       if (LIST_FIND_W(&htable->hash[*bucket], dl_seq_real_show,
-                     struct dsthash_ent *, s)) {
-               /* buffer was filled and unable to print that tuple */
-               return 1;
-       }
+       struct dsthash_ent *ent;
+       struct hlist_node *pos;
+
+       if (!hlist_empty(&htable->hash[*bucket]))
+               hlist_for_each_entry(ent, pos, &htable->hash[*bucket], node) {
+                       if (dl_seq_real_show(ent, s)) {
+                               /* buffer was filled and unable to print that tuple */
+                               return 1;
+                       }
+               }
+       
        return 0;
 }
 
@@ -668,11 +688,9 @@ static int init_or_fini(int fini)
                goto cleanup_nothing;
        }
 
-       /* FIXME: do we really want HWCACHE_ALIGN since our objects are
-        * quite small ? */
        hashlimit_cachep = kmem_cache_create("ipt_hashlimit",
                                            sizeof(struct dsthash_ent), 0,
-                                           SLAB_HWCACHE_ALIGN, NULL, NULL);
+                                           0, NULL, NULL);
        if (!hashlimit_cachep) {
                printk(KERN_ERR "Unable to create ipt_hashlimit slab cache\n");
                ret = -ENOMEM;