diff --git a/xt_ratelimit.c b/xt_ratelimit.c index 1f184bb..b413775 100644 --- a/xt_ratelimit.c +++ b/xt_ratelimit.c @@ -77,23 +77,33 @@ struct ratelimit_net { struct proc_dir_entry *ipt_ratelimit; }; +typedef union { + struct { + u32 tc; + u32 te; + } flds; + long long_v; +} +tce_t; + /* CAR accounting */ struct ratelimit_car { - unsigned long last; /* last refill (jiffies) */ - u32 tc; /* committed token bucket counter */ - u32 te; /* exceeded token bucket counter */ + atomic64_t last; /* last refill (jiffies) */ + + /* committed token bucket counter */ + /* exceeded token bucket counter */ + atomic64_t tce; u32 cbs; /* committed burst size (bytes) */ u32 ebs; /* extended burst size (bytes) */ - u32 cir; /* committed information rate (bits/s) */ + u32 cir; /* committed information rate (bits/s) / (HZ * 8) */ }; struct ratelimit_stat { - u64 green_bytes; - u64 red_bytes; - u32 green_pkt; - u32 red_pkt; - unsigned long first; /* first time seen */ + atomic64_t green_bytes; + atomic64_t red_bytes; + atomic_t green_pkt; + atomic_t red_pkt; #ifdef RATE_ESTIMATOR #define RATEST_SECONDS 4 /* length of rate estimator time slot */ @@ -184,6 +194,8 @@ static int ratelimit_seq_ent_show(struct ratelimit_match *mt, { struct ratelimit_ent *ent = mt->ent; int i; + unsigned long last; + tce_t tce; /* to print entities only once, we print only entities * where match is first element */ @@ -198,23 +210,28 @@ static int ratelimit_seq_ent_show(struct ratelimit_match *mt, &ent->matches[i].addr); } seq_printf(s, " cir %u cbs %u ebs %u;", - ent->car.cir, ent->car.cbs, ent->car.ebs); + ent->car.cir * (HZ * BITS_PER_BYTE), ent->car.cbs, ent->car.ebs); + + tce.long_v = atomic64_read(&ent->car.tce); + seq_printf(s, " tc %u te %u last", tce.flds.tc, tce.flds.te); - seq_printf(s, " tc %u te %u last", ent->car.tc, ent->car.te); - if (ent->car.last) - seq_printf(s, " %ld;", jiffies - ent->car.last); + last = atomic64_read(&ent->car.last); + if (last) + seq_printf(s, " %ld;", jiffies - last); else seq_printf(s, " never;"); seq_printf(s, " conf %u/%llu", - ent->stat.green_pkt, ent->stat.green_bytes); + (u32)atomic_read(&ent->stat.green_pkt), + (u64)atomic64_read(&ent->stat.green_bytes)); #ifdef RATE_ESTIMATOR seq_printf(s, " %lu bps", calc_rate_est(&ent->stat)); #endif seq_printf(s, ", rej %u/%llu", - ent->stat.red_pkt, ent->stat.red_bytes); + (u32)atomic_read(&ent->stat.red_pkt), + (u64)atomic64_read(&ent->stat.red_bytes)); seq_printf(s, "\n"); @@ -442,7 +459,7 @@ static int parse_rule(struct xt_ratelimit_htable *ht, char *str, size_t size) val = simple_strtoul(v, NULL, 10); switch (i) { case 0: - ent->car.cir = val; + ent->car.cir = val / (HZ * BITS_PER_BYTE); /* autoconfigure optimal parameters */ val = val / 8 + (val / 8 / 2); /* FALLTHROUGH */ @@ -841,37 +858,66 @@ ratelimit_mt(const struct sk_buff *skb, struct xt_action_param *par) if (ent) { struct ratelimit_car *car = &ent->car; const unsigned int len = skb->len; /* L3 */ - const unsigned long delta_ms = (now - car->last) * (MSEC_PER_SEC / HZ); - spin_lock(&ent->lock_bh); - car->tc += len; - if (delta_ms) { - const u32 tok = delta_ms * (car->cir / (BITS_PER_BYTE * MSEC_PER_SEC)); - - car->tc -= min(tok, car->tc); - if (!ent->stat.first) - ent->stat.first = now; - car->last = now; + unsigned long time_passed, old, last; + u32 tc, te, tok; + tce_t tce, new_tce; + long old_tce; + + /* + * calculate how much time has passed since the last update + * and then update last update time + */ + while (true) { /* cas loop */ + last = atomic64_read(&car->last); + time_passed = now - last; + old = atomic64_cmpxchg(&car->last, last, now); + if (old == last) { + break; /* success */ + } } - if (car->tc > car->cbs) { /* extended burst */ - car->te += car->tc - car->cbs; - if (car->te > car->ebs) { - car->te = 0; - match = true; /* match is drop */ + + /* + * process packet's bytes + */ + while (true) { /* cas loop */ + tce.long_v = atomic64_read(&car->tce); + tc = tce.flds.tc; + te = tce.flds.te; + match = false; + + tc += len; + + if (time_passed) { + tok = time_passed * car->cir; + tc -= min(tok, tc); + } + + if (tc > car->cbs) { /* extended burst */ + te += tc - car->cbs; + if (te > car->ebs) { + te = 0; + tc -= len; + match = true; /* drop */ + } + } + + /* new tce */ + new_tce.flds.tc = tc; + new_tce.flds.te = te; + old_tce = atomic64_cmpxchg(&car->tce, tce.long_v, new_tce.long_v); + if (old_tce == tce.long_v) { + break; /* success */ } } - if (match) { - ent->stat.red_bytes += len; - ent->stat.red_pkt++; - car->tc -= len; + + if (likely(match)) { + atomic64_add(len, &ent->stat.red_bytes); + atomic_inc(&ent->stat.red_pkt); } else { - ent->stat.green_bytes += len; - ent->stat.green_pkt++; -#ifdef RATE_ESTIMATOR - rate_estimator(&ent->stat, now / RATEST_JIFFIES, len); -#endif + atomic64_add(len, &ent->stat.green_bytes); + atomic_inc(&ent->stat.green_pkt); } - spin_unlock(&ent->lock_bh); } else { if (ht->other == OT_MATCH) match = true; /* match is drop */