Commit defdfc2b authored by Mark Karpilovskij's avatar Mark Karpilovskij Committed by Daniel Salzman

mod-rrl: refactor hopscotch hashing to be readable

parent bffa4eb4
...@@ -90,7 +90,7 @@ static uint8_t rrl_clsid(rrl_req_t *p) ...@@ -90,7 +90,7 @@ static uint8_t rrl_clsid(rrl_req_t *p)
{ {
/* Check error code */ /* Check error code */
int ret = CLS_NULL; int ret = CLS_NULL;
switch (knot_wire_get_rcode(p->w)) { switch (knot_wire_get_rcode(p->wire)) {
case KNOT_RCODE_NOERROR: ret = CLS_NORMAL; break; case KNOT_RCODE_NOERROR: ret = CLS_NORMAL; break;
case KNOT_RCODE_NXDOMAIN: return CLS_NXDOMAIN; break; case KNOT_RCODE_NXDOMAIN: return CLS_NXDOMAIN; break;
default: return CLS_ERROR; break; default: return CLS_ERROR; break;
...@@ -123,7 +123,7 @@ static uint8_t rrl_clsid(rrl_req_t *p) ...@@ -123,7 +123,7 @@ static uint8_t rrl_clsid(rrl_req_t *p)
} }
/* Check ancount */ /* Check ancount */
if (knot_wire_get_ancount(p->w) == 0) { if (knot_wire_get_ancount(p->wire) == 0) {
return CLS_EMPTY; return CLS_EMPTY;
} }
...@@ -198,82 +198,79 @@ static int rrl_classify(uint8_t *dst, size_t maxlen, const struct sockaddr_stora ...@@ -198,82 +198,79 @@ static int rrl_classify(uint8_t *dst, size_t maxlen, const struct sockaddr_stora
return blklen; return blklen;
} }
static int bucket_free(rrl_item_t *b, uint32_t now) static int bucket_free(rrl_item_t *bucket, uint32_t now)
{ {
return b->cls == CLS_NULL || (b->time + 1 < now); return bucket->cls == CLS_NULL || (bucket->time + 1 < now);
} }
static int bucket_match(rrl_item_t *b, rrl_item_t *m) static int bucket_match(rrl_item_t *bucket, rrl_item_t *match)
{ {
return b->cls == m->cls && return bucket->cls == match->cls &&
b->netblk == m->netblk && bucket->netblk == match->netblk &&
b->qname == m->qname; bucket->qname == match->qname;
} }
static int find_free(rrl_table_t *tbl, unsigned i, uint32_t now) static int find_free(rrl_table_t *tbl, unsigned id, uint32_t now)
{ {
rrl_item_t *np = tbl->arr + tbl->size; for (int i = id; i < tbl->size; i++) {
rrl_item_t *b = NULL; if (bucket_free(&tbl->arr[i], now)) {
for (b = tbl->arr + i; b != np; ++b) { return i - id;
if (bucket_free(b, now)) {
return b - (tbl->arr + i);
} }
} }
np = tbl->arr + i; for (int i = 0; i < id; i++) {
for (b = tbl->arr; b != np; ++b) { if (bucket_free(&tbl->arr[i], now)) {
if (bucket_free(b, now)) { return i + (tbl->size - id);
return (b - tbl->arr) + (tbl->size - i);
} }
} }
/* this happens if table is full... force vacate current elm */ /* this happens if table is full... force vacate current elm */
return i; return id;
} }
static inline unsigned find_match(rrl_table_t *tbl, uint32_t id, rrl_item_t *m) static inline unsigned find_match(rrl_table_t *tbl, uint32_t id, rrl_item_t *m)
{ {
unsigned f = 0; unsigned new_id = 0;
unsigned d = 0; unsigned hop = 0;
unsigned match = tbl->arr[id].hop; unsigned match_bitmap = tbl->arr[id].hop;
while (match != 0) { while (match_bitmap != 0) {
d = __builtin_ctz(match); hop = __builtin_ctz(match_bitmap); /* offset of next potential match */
f = (id + d) % tbl->size; new_id = (id + hop) % tbl->size;
if (bucket_match(tbl->arr + f, m)) { if (bucket_match(&tbl->arr[new_id], m)) {
return d; return hop;
} else { } else {
match &= ~(1<<d); /* clear potential match */ match_bitmap &= ~(1 << hop); /* clear potential match */
} }
} }
return HOP_LEN + 1; return HOP_LEN + 1;
} }
static inline unsigned reduce_dist(rrl_table_t *tbl, unsigned id, unsigned d, unsigned *f) static inline unsigned reduce_dist(rrl_table_t *tbl, unsigned id, unsigned dist, unsigned *free_id)
{ {
unsigned rd = HOP_LEN - 1; unsigned rd = HOP_LEN - 1;
while (rd > 0) { while (rd > 0) {
unsigned s = (tbl->size + *f - rd) % tbl->size; /* bucket to be vacated */ unsigned vacate_id = (tbl->size + *free_id - rd) % tbl->size; /* bucket to be vacated */
if (tbl->arr[s].hop != 0) { if (tbl->arr[vacate_id].hop != 0) {
unsigned o = __builtin_ctz(tbl->arr[s].hop); /* offset of first valid bucket */ unsigned hop = __builtin_ctz(tbl->arr[vacate_id].hop); /* offset of first valid bucket */
if (o < rd) { /* only offsets in <s, f> are interesting */ if (hop < rd) { /* only offsets in <vacate_id, free_id> are interesting */
unsigned e = (s + o) % tbl->size; /* this item will be displaced to [f] */ unsigned new_id = (vacate_id + hop) % tbl->size; /* this item will be displaced to [free_id] */
unsigned keep_hop = tbl->arr[*f].hop; /* unpredictable padding */ unsigned keep_hop = tbl->arr[*free_id].hop; /* unpredictable padding */
memcpy(tbl->arr + *f, tbl->arr + e, sizeof(rrl_item_t)); memcpy(tbl->arr + *free_id, tbl->arr + new_id, sizeof(rrl_item_t));
tbl->arr[*f].hop = keep_hop; tbl->arr[*free_id].hop = keep_hop;
tbl->arr[e].cls = CLS_NULL; tbl->arr[new_id].cls = CLS_NULL;
tbl->arr[s].hop &= ~(1<<o); tbl->arr[vacate_id].hop &= ~(1 << hop);
tbl->arr[s].hop |= 1<<rd; tbl->arr[vacate_id].hop |= 1 << rd;
*f = e; *free_id = new_id;
return d - (rd - o); return dist - (rd - hop);
} }
} }
--rd; --rd;
} }
assert(rd == 0); /* this happens with p=1/fact(HOP_LEN) */ assert(rd == 0); /* this happens with p=1/fact(HOP_LEN) */
*f = id; *free_id = id;
d = 0; /* force vacate initial element */ dist = 0; /* force vacate initial element */
return d; return dist;
} }
static void rrl_log_state(knotd_mod_t *mod, const struct sockaddr_storage *ss, static void rrl_log_state(knotd_mod_t *mod, const struct sockaddr_storage *ss,
...@@ -401,44 +398,44 @@ static rrl_item_t *rrl_hash(rrl_table_t *tbl, const struct sockaddr_storage *rem ...@@ -401,44 +398,44 @@ static rrl_item_t *rrl_hash(rrl_table_t *tbl, const struct sockaddr_storage *rem
.time = stamp .time = stamp
}; };
unsigned d = find_match(tbl, id, &match); unsigned dist = find_match(tbl, id, &match);
if (d > HOP_LEN) { /* not an exact match, find free element [f] */ if (dist > HOP_LEN) { /* not an exact match, find free element [f] */
d = find_free(tbl, id, stamp); dist = find_free(tbl, id, stamp);
} }
/* Reduce distance to fit <id, id + HOP_LEN) */ /* Reduce distance to fit <id, id + HOP_LEN) */
unsigned f = (id + d) % tbl->size; unsigned free_id = (id + dist) % tbl->size;
while (d >= HOP_LEN) { while (dist >= HOP_LEN) {
d = reduce_dist(tbl, id, d, &f); dist = reduce_dist(tbl, id, dist, &free_id);
} }
/* Assign granular lock and unlock lookup. */ /* Assign granular lock and unlock lookup. */
*lock = f % tbl->lk_count; *lock = free_id % tbl->lk_count;
rrl_lock(tbl, *lock); rrl_lock(tbl, *lock);
pthread_mutex_unlock(&tbl->ll); pthread_mutex_unlock(&tbl->ll);
/* found free elm 'k' which is in <id, id + HOP_LEN) */ /* found free bucket which is in <id, id + HOP_LEN) */
tbl->arr[id].hop |= (1 << d); tbl->arr[id].hop |= (1 << dist);
rrl_item_t *b = tbl->arr + f; rrl_item_t *bucket = &tbl->arr[free_id];
assert(f == (id+d) % tbl->size); assert(free_id == (id + dist) % tbl->size);
/* Inspect bucket state. */ /* Inspect bucket state. */
unsigned hop = b->hop; unsigned hop = bucket->hop;
if (b->cls == CLS_NULL) { if (bucket->cls == CLS_NULL) {
memcpy(b, &match, sizeof(rrl_item_t)); memcpy(bucket, &match, sizeof(rrl_item_t));
b->hop = hop; bucket->hop = hop;
} }
/* Check for collisions. */ /* Check for collisions. */
if (!bucket_match(b, &match)) { if (!bucket_match(bucket, &match)) {
if (!(b->flags & RRL_BF_SSTART)) { if (!(bucket->flags & RRL_BF_SSTART)) {
memcpy(b, &match, sizeof(rrl_item_t)); memcpy(bucket, &match, sizeof(rrl_item_t));
b->hop = hop; bucket->hop = hop;
b->ntok = tbl->rate + tbl->rate / RRL_SSTART; bucket->ntok = tbl->rate + tbl->rate / RRL_SSTART;
b->flags |= RRL_BF_SSTART; bucket->flags |= RRL_BF_SSTART;
} }
} }
return b; return bucket;
} }
int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote, int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote,
......
...@@ -72,7 +72,7 @@ typedef enum { ...@@ -72,7 +72,7 @@ typedef enum {
* \brief RRL request descriptor. * \brief RRL request descriptor.
*/ */
typedef struct { typedef struct {
const uint8_t *w; const uint8_t *wire;
uint16_t len; uint16_t len;
rrl_req_flag_t flags; rrl_req_flag_t flags;
knot_pkt_t *query; knot_pkt_t *query;
......
...@@ -97,7 +97,7 @@ static knotd_state_t ratelimit_apply(knotd_state_t state, knot_pkt_t *pkt, ...@@ -97,7 +97,7 @@ static knotd_state_t ratelimit_apply(knotd_state_t state, knot_pkt_t *pkt,
} }
rrl_req_t req = { rrl_req_t req = {
.w = pkt->wire, .wire = pkt->wire,
.query = qdata->query .query = qdata->query
}; };
......
...@@ -116,7 +116,7 @@ int main(int argc, char *argv[]) ...@@ -116,7 +116,7 @@ int main(int argc, char *argv[])
knot_wire_flags_set_qr(rbuf); knot_wire_flags_set_qr(rbuf);
rrl_req_t rq; rrl_req_t rq;
rq.w = rbuf; rq.wire = rbuf;
rq.len = rlen; rq.len = rlen;
rq.query = query; rq.query = query;
rq.flags = 0; rq.flags = 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment