|
@@ -31,6 +31,7 @@
|
|
#include <linux/skbuff.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <linux/init.h>
|
|
#include <linux/init.h>
|
|
|
|
+#include <linux/rbtree.h>
|
|
#include <net/sock.h>
|
|
#include <net/sock.h>
|
|
#include <net/gen_stats.h>
|
|
#include <net/gen_stats.h>
|
|
|
|
|
|
@@ -89,6 +90,7 @@ struct gen_estimator
|
|
u32 avpps;
|
|
u32 avpps;
|
|
u32 avbps;
|
|
u32 avbps;
|
|
struct rcu_head e_rcu;
|
|
struct rcu_head e_rcu;
|
|
|
|
+ struct rb_node node;
|
|
};
|
|
};
|
|
|
|
|
|
struct gen_estimator_head
|
|
struct gen_estimator_head
|
|
@@ -102,6 +104,9 @@ static struct gen_estimator_head elist[EST_MAX_INTERVAL+1];
|
|
/* Protects against NULL dereference */
|
|
/* Protects against NULL dereference */
|
|
static DEFINE_RWLOCK(est_lock);
|
|
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)
|
|
static void est_timer(unsigned long arg)
|
|
{
|
|
{
|
|
int idx = (int)arg;
|
|
int idx = (int)arg;
|
|
@@ -139,6 +144,45 @@ skip:
|
|
rcu_read_unlock();
|
|
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(struct gnet_stats_basic *bstats,
|
|
|
|
+ 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;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* gen_new_estimator - create a new rate estimator
|
|
* gen_new_estimator - create a new rate estimator
|
|
* @bstats: basic statistics
|
|
* @bstats: basic statistics
|
|
@@ -194,6 +238,8 @@ int gen_new_estimator(struct gnet_stats_basic *bstats,
|
|
mod_timer(&elist[idx].timer, jiffies + ((HZ/4) << idx));
|
|
mod_timer(&elist[idx].timer, jiffies + ((HZ/4) << idx));
|
|
|
|
|
|
list_add_rcu(&est->list, &elist[idx].list);
|
|
list_add_rcu(&est->list, &elist[idx].list);
|
|
|
|
+ gen_add_node(est);
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -209,34 +255,24 @@ static void __gen_kill_estimator(struct rcu_head *head)
|
|
* @bstats: basic statistics
|
|
* @bstats: basic statistics
|
|
* @rate_est: rate estimator 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
|
|
* NOTE: Called under rtnl_mutex
|
|
*/
|
|
*/
|
|
void gen_kill_estimator(struct gnet_stats_basic *bstats,
|
|
void gen_kill_estimator(struct gnet_stats_basic *bstats,
|
|
- struct gnet_stats_rate_est *rate_est)
|
|
|
|
|
|
+ struct gnet_stats_rate_est *rate_est)
|
|
{
|
|
{
|
|
- int idx;
|
|
|
|
- struct gen_estimator *e, *n;
|
|
|
|
-
|
|
|
|
- for (idx=0; idx <= EST_MAX_INTERVAL; idx++) {
|
|
|
|
-
|
|
|
|
- /* Skip non initialized indexes */
|
|
|
|
- if (!elist[idx].timer.function)
|
|
|
|
- continue;
|
|
|
|
|
|
+ struct gen_estimator *e;
|
|
|
|
|
|
- list_for_each_entry_safe(e, n, &elist[idx].list, list) {
|
|
|
|
- if (e->rate_est != rate_est || e->bstats != bstats)
|
|
|
|
- continue;
|
|
|
|
|
|
+ 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);
|
|
|
|
|
|
+ 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);
|
|
|
|
- }
|
|
|
|
|
|
+ list_del_rcu(&e->list);
|
|
|
|
+ call_rcu(&e->e_rcu, __gen_kill_estimator);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|