gss_krb5: Introduce encryption type framework
[safe/jmp/linux-2.6] / net / core / gen_estimator.c
index b07c029..cf8e703 100644 (file)
@@ -15,7 +15,7 @@
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
-#include <asm/bitops.h>
+#include <linux/bitops.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -31,6 +31,8 @@
 #include <linux/skbuff.h>
 #include <linux/rtnetlink.h>
 #include <linux/init.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
 #include <net/sock.h>
 #include <net/gen_stats.h>
 
@@ -65,9 +67,9 @@
 
    NOTES.
 
-   * The stored value for avbps is scaled by 2^5, so that maximal
-     rate is ~1Gbit, avpps is scaled by 2^10.
-
+   * avbps is scaled by 2^5, avpps is scaled by 2^10.
+   * both values are reported as 32 bit unsigned values. bps can
+     overflow for fast links : max speed being 34360Mbit/sec
    * Minimal interval is HZ/4=250msec (it is the greatest common divisor
      for HZ=100 and HZ=1024 8)), maximal interval
      is (HZ*2^EST_MAX_INTERVAL)/4 = 8sec. Shorter intervals
 
 struct gen_estimator
 {
-       struct gen_estimator    *next;
-       struct gnet_stats_basic *bstats;
+       struct list_head        list;
+       struct gnet_stats_basic_packed  *bstats;
        struct gnet_stats_rate_est      *rate_est;
        spinlock_t              *stats_lock;
-       unsigned                interval;
        int                     ewma_log;
        u64                     last_bytes;
+       u64                     avbps;
        u32                     last_packets;
        u32                     avpps;
-       u32                     avbps;
+       struct rcu_head         e_rcu;
+       struct rb_node          node;
 };
 
 struct gen_estimator_head
 {
        struct timer_list       timer;
-       struct gen_estimator    *list;
+       struct list_head        list;
 };
 
 static struct gen_estimator_head elist[EST_MAX_INTERVAL+1];
 
-/* Estimator array lock */
+/* Protects against NULL dereference */
 static DEFINE_RWLOCK(est_lock);
 
+/* Protects against soft lockup during large deletion */
+static struct rb_root est_root = RB_ROOT;
+
 static void est_timer(unsigned long arg)
 {
        int idx = (int)arg;
        struct gen_estimator *e;
 
-       read_lock(&est_lock);
-       for (e = elist[idx].list; e; e = e->next) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(e, &elist[idx].list, list) {
                u64 nbytes;
+               u64 brate;
                u32 npackets;
                u32 rate;
 
                spin_lock(e->stats_lock);
+               read_lock(&est_lock);
+               if (e->bstats == NULL)
+                       goto skip;
+
                nbytes = e->bstats->bytes;
                npackets = e->bstats->packets;
-               rate = (nbytes - e->last_bytes)<<(7 - idx);
+               brate = (nbytes - e->last_bytes)<<(7 - idx);
                e->last_bytes = nbytes;
-               e->avbps += ((long)rate - (long)e->avbps) >> e->ewma_log;
+               e->avbps += (brate >> e->ewma_log) - (e->avbps >> e->ewma_log);
                e->rate_est->bps = (e->avbps+0xF)>>5;
 
                rate = (npackets - e->last_packets)<<(12 - idx);
                e->last_packets = npackets;
-               e->avpps += ((long)rate - (long)e->avpps) >> e->ewma_log;
+               e->avpps += (rate >> e->ewma_log) - (e->avpps >> e->ewma_log);
                e->rate_est->pps = (e->avpps+0x1FF)>>10;
+skip:
+               read_unlock(&est_lock);
                spin_unlock(e->stats_lock);
        }
 
-       mod_timer(&elist[idx].timer, jiffies + ((HZ<<idx)/4));
-       read_unlock(&est_lock);
+       if (!list_empty(&elist[idx].list))
+               mod_timer(&elist[idx].timer, jiffies + ((HZ/4) << idx));
+       rcu_read_unlock();
+}
+
+static void gen_add_node(struct gen_estimator *est)
+{
+       struct rb_node **p = &est_root.rb_node, *parent = NULL;
+
+       while (*p) {
+               struct gen_estimator *e;
+
+               parent = *p;
+               e = rb_entry(parent, struct gen_estimator, node);
+
+               if (est->bstats > e->bstats)
+                       p = &parent->rb_right;
+               else
+                       p = &parent->rb_left;
+       }
+       rb_link_node(&est->node, parent, p);
+       rb_insert_color(&est->node, &est_root);
+}
+
+static
+struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats,
+                                   const struct gnet_stats_rate_est *rate_est)
+{
+       struct rb_node *p = est_root.rb_node;
+
+       while (p) {
+               struct gen_estimator *e;
+
+               e = rb_entry(p, struct gen_estimator, node);
+
+               if (bstats > e->bstats)
+                       p = p->rb_right;
+               else if (bstats < e->bstats || rate_est != e->rate_est)
+                       p = p->rb_left;
+               else
+                       return e;
+       }
+       return NULL;
 }
 
 /**
@@ -144,27 +198,31 @@ static void est_timer(unsigned long arg)
  * configuration TLV is created. Upon each interval, the latest statistics
  * will be read from &bstats and the estimated rate will be stored in
  * &rate_est with the statistics lock grabed during this period.
- * 
+ *
  * Returns 0 on success or a negative error code.
+ *
+ * NOTE: Called under rtnl_mutex
  */
-int gen_new_estimator(struct gnet_stats_basic *bstats,
-       struct gnet_stats_rate_est *rate_est, spinlock_t *stats_lock, struct rtattr *opt)
+int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
+                     struct gnet_stats_rate_est *rate_est,
+                     spinlock_t *stats_lock,
+                     struct nlattr *opt)
 {
        struct gen_estimator *est;
-       struct gnet_estimator *parm = RTA_DATA(opt);
+       struct gnet_estimator *parm = nla_data(opt);
+       int idx;
 
-       if (RTA_PAYLOAD(opt) < sizeof(*parm))
+       if (nla_len(opt) < sizeof(*parm))
                return -EINVAL;
 
        if (parm->interval < -2 || parm->interval > 3)
                return -EINVAL;
 
-       est = kmalloc(sizeof(*est), GFP_KERNEL);
+       est = kzalloc(sizeof(*est), GFP_KERNEL);
        if (est == NULL)
                return -ENOBUFS;
 
-       memset(est, 0, sizeof(*est));
-       est->interval = parm->interval + 2;
+       idx = parm->interval + 2;
        est->bstats = bstats;
        est->rate_est = rate_est;
        est->stats_lock = stats_lock;
@@ -174,57 +232,57 @@ int gen_new_estimator(struct gnet_stats_basic *bstats,
        est->last_packets = bstats->packets;
        est->avpps = rate_est->pps<<10;
 
-       est->next = elist[est->interval].list;
-       if (est->next == NULL) {
-               init_timer(&elist[est->interval].timer);
-               elist[est->interval].timer.data = est->interval;
-               elist[est->interval].timer.expires = jiffies + ((HZ<<est->interval)/4);
-               elist[est->interval].timer.function = est_timer;
-               add_timer(&elist[est->interval].timer);
+       if (!elist[idx].timer.function) {
+               INIT_LIST_HEAD(&elist[idx].list);
+               setup_timer(&elist[idx].timer, est_timer, idx);
        }
-       write_lock_bh(&est_lock);
-       elist[est->interval].list = est;
-       write_unlock_bh(&est_lock);
+
+       if (list_empty(&elist[idx].list))
+               mod_timer(&elist[idx].timer, jiffies + ((HZ/4) << idx));
+
+       list_add_rcu(&est->list, &elist[idx].list);
+       gen_add_node(est);
+
        return 0;
 }
+EXPORT_SYMBOL(gen_new_estimator);
+
+static void __gen_kill_estimator(struct rcu_head *head)
+{
+       struct gen_estimator *e = container_of(head,
+                                       struct gen_estimator, e_rcu);
+       kfree(e);
+}
 
 /**
  * gen_kill_estimator - remove a rate estimator
  * @bstats: basic statistics
  * @rate_est: rate estimator statistics
  *
- * Removes the rate estimator specified by &bstats and &rate_est
- * and deletes the timer.
+ * Removes the rate estimator specified by &bstats and &rate_est.
+ *
+ * NOTE: Called under rtnl_mutex
  */
-void gen_kill_estimator(struct gnet_stats_basic *bstats,
-       struct gnet_stats_rate_est *rate_est)
+void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
+                       struct gnet_stats_rate_est *rate_est)
 {
-       int idx;
-       struct gen_estimator *est, **pest;
-
-       for (idx=0; idx <= EST_MAX_INTERVAL; idx++) {
-               int killed = 0;
-               pest = &elist[idx].list;
-               while ((est=*pest) != NULL) {
-                       if (est->rate_est != rate_est || est->bstats != bstats) {
-                               pest = &est->next;
-                               continue;
-                       }
-
-                       write_lock_bh(&est_lock);
-                       *pest = est->next;
-                       write_unlock_bh(&est_lock);
-
-                       kfree(est);
-                       killed++;
-               }
-               if (killed && elist[idx].list == NULL)
-                       del_timer(&elist[idx].timer);
+       struct gen_estimator *e;
+
+       while ((e = gen_find_node(bstats, rate_est))) {
+               rb_erase(&e->node, &est_root);
+
+               write_lock_bh(&est_lock);
+               e->bstats = NULL;
+               write_unlock_bh(&est_lock);
+
+               list_del_rcu(&e->list);
+               call_rcu(&e->e_rcu, __gen_kill_estimator);
        }
 }
+EXPORT_SYMBOL(gen_kill_estimator);
 
 /**
- * gen_replace_estimator - replace rate estimator configruation
+ * gen_replace_estimator - replace rate estimator configuration
  * @bstats: basic statistics
  * @rate_est: rate estimator statistics
  * @stats_lock: statistics lock
@@ -232,19 +290,30 @@ void gen_kill_estimator(struct gnet_stats_basic *bstats,
  *
  * Replaces the configuration of a rate estimator by calling
  * gen_kill_estimator() and gen_new_estimator().
- * 
+ *
  * Returns 0 on success or a negative error code.
  */
-int
-gen_replace_estimator(struct gnet_stats_basic *bstats,
-       struct gnet_stats_rate_est *rate_est, spinlock_t *stats_lock,
-       struct rtattr *opt)
+int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
+                         struct gnet_stats_rate_est *rate_est,
+                         spinlock_t *stats_lock, struct nlattr *opt)
 {
-    gen_kill_estimator(bstats, rate_est);
-    return gen_new_estimator(bstats, rate_est, stats_lock, opt);
+       gen_kill_estimator(bstats, rate_est);
+       return gen_new_estimator(bstats, rate_est, stats_lock, opt);
 }
-    
-
-EXPORT_SYMBOL(gen_kill_estimator);
-EXPORT_SYMBOL(gen_new_estimator);
 EXPORT_SYMBOL(gen_replace_estimator);
+
+/**
+ * gen_estimator_active - test if estimator is currently in use
+ * @bstats: basic statistics
+ * @rate_est: rate estimator statistics
+ *
+ * Returns true if estimator is active, and false if not.
+ */
+bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
+                         const struct gnet_stats_rate_est *rate_est)
+{
+       ASSERT_RTNL();
+
+       return gen_find_node(bstats, rate_est) != NULL;
+}
+EXPORT_SYMBOL(gen_estimator_active);