Commit 0c60d745 authored by Thomas Klausner's avatar Thomas Klausner

Store redirect information for ALL_SESSION and ALL_USER in a hash.

This is a speedup if many of these exist (compared to checking a
linked list for matches as before).

Refactor a bit while here.

Use the uthash code from Troy D. Hanson,
http://troydhanson.github.com/uthash/
parent 7397ae97
......@@ -8,6 +8,7 @@ SET(RT_REDIR_SRC
redir_expiry.c
redir_fwd.c
redir_out.c
uthash.h
)
# Compile as a module
......
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* Authors: Sebastien Decugis <sdecugis@freediameter.net> *
* and Thomas Klausner <tk@giga.or.at> *
* *
* Copyright (c) 2011, WIDE Project and NICT *
* Copyright (c) 2011, 2014, WIDE Project and NICT *
* All rights reserved. *
* *
* Redistribution and use of this software in source and binary forms, with or without modification, are *
......@@ -34,10 +35,14 @@
*********************************************************************************************************/
#include "rt_redir.h"
#include "uthash.h"
/* The array with all entries ordered by their data */
struct redir_line redirects_usages[H_U_MAX + 1];
/* for symmetry reasons, hash tables for all types exist, but only ALL_SESSION and ALL_USER will be used */
struct redir_entry *redirect_hash_table[H_U_MAX+1];
/* Initialize the array */
int redir_entry_init()
{
......@@ -49,8 +54,14 @@ int redir_entry_init()
memset(&redirects_usages, 0, sizeof(redirects_usages));
for (i = 0; i <= H_U_MAX; i++) {
/* only one of the two will be used for each type, but initialize both to be on the safe side */
/* initialize list */
CHECK_POSIX( pthread_rwlock_init( &redirects_usages[i].lock, NULL) );
fd_list_init( &redirects_usages[i].sentinel, &redirects_usages[i] );
/* initialize hash table */
redirect_hash_table[i] = NULL;
}
/* initialize the scores */
......@@ -65,6 +76,37 @@ int redir_entry_init()
return 0;
}
int redir_entry_fini()
{
int i;
struct redir_entry *current_entry, *tmp;
/* Empty all entries */
CHECK_POSIX_DO( pthread_mutex_lock(&redir_exp_peer_lock), );
for (i = 0; i <= H_U_MAX; i++) {
CHECK_POSIX_DO( pthread_rwlock_wrlock( &redirects_usages[i].lock), );
switch(i) {
case ALL_SESSION:
case ALL_USER:
HASH_ITER(hh, redirect_hash_table[i], current_entry, tmp) {
HASH_DEL(redirect_hash_table[i], current_entry);
CHECK_FCT_DO( redir_entry_destroy(current_entry), );
}
break;
default:
while (!FD_IS_LIST_EMPTY(&redirects_usages[i].sentinel)) {
struct redir_entry * e = redirects_usages[i].sentinel.next->o;
fd_list_unlink(&e->redir_list);
CHECK_FCT_DO( redir_entry_destroy(e), );
}
}
CHECK_POSIX_DO( pthread_rwlock_unlock( &redirects_usages[i].lock), );
CHECK_POSIX_DO( pthread_rwlock_destroy( &redirects_usages[i].lock), );
}
CHECK_POSIX_DO( pthread_mutex_unlock(&redir_exp_peer_lock), );
return 0;
}
/* Create a new redir_entry and add the correct data */
int redir_entry_new(struct redir_entry ** e, struct fd_list * targets, uint32_t rhu, struct msg * qry, DiamId_t nh, size_t nhlen, os0_t oh, size_t ohlen)
......@@ -90,6 +132,7 @@ int redir_entry_new(struct redir_entry ** e, struct fd_list * targets, uint32_t
fd_list_init(&entry->exp_list, entry);
entry->type = rhu;
/* list entry for putting into redirects_usage; also doubles as pointer into that list so it can be removed easily */
fd_list_init(&entry->redir_list, entry);
/* finally initialize the data */
switch (rhu) {
......@@ -237,6 +280,7 @@ int (*redir_entry_cmp_key[H_U_MAX +1])(union matchdata * , union matchdata * ) =
int redir_entry_insert(struct redir_entry * e)
{
struct fd_list * li;
struct redir_entry * r = NULL;
TRACE_ENTRY("%p", e);
CHECK_PARAMS(e && (e->eyec == REDIR_ENTRY_EYEC));
......@@ -244,15 +288,37 @@ int redir_entry_insert(struct redir_entry * e)
/* Write-Lock the line */
CHECK_POSIX( pthread_rwlock_wrlock( RWLOCK_REDIR(e) ) );
for (li = redirects_usages[e->type].sentinel.next; li != &redirects_usages[e->type].sentinel; li = li->next) {
struct redir_entry * n = li->o;
int cmp = redir_entry_cmp_key[e->type](&e->data, &n->data);
if (cmp <= 0)
break;
switch (e->type) {
case ALL_SESSION:
HASH_FIND(hh, redirect_hash_table[e->type], e->data.session.s, e->data.session.l, r);
if (r) {
/* previously existing entry, delete it from hash and free it */
HASH_DELETE(hh, redirect_hash_table[e->type], r);
CHECK_FCT_DO( redir_entry_destroy(r), );
}
HASH_ADD_KEYPTR(hh, redirect_hash_table[e->type], e->data.session.s, e->data.session.l, e);
break;
case ALL_USER:
HASH_FIND(hh, redirect_hash_table[e->type], e->data.user.s, e->data.user.l, r);
if (r) {
/* previously existing entry, delete it from hash and free it */
HASH_DELETE(hh, redirect_hash_table[e->type], r);
CHECK_FCT_DO( redir_entry_destroy(r), );
}
HASH_ADD_KEYPTR(hh, redirect_hash_table[e->type], e->data.user.s, e->data.user.l, e);
break;
default:
for (li = redirects_usages[e->type].sentinel.next; li != &redirects_usages[e->type].sentinel; li = li->next) {
struct redir_entry * n = li->o;
int cmp = redir_entry_cmp_key[e->type](&e->data, &n->data);
if (cmp <= 0)
break;
}
fd_list_insert_before(li, &e->redir_list);
break;
}
fd_list_insert_before(li, &e->redir_list);
/* unLock the line */
CHECK_POSIX( pthread_rwlock_unlock( RWLOCK_REDIR(e) ) );
......@@ -262,14 +328,39 @@ int redir_entry_insert(struct redir_entry * e)
/* Destroy -- the exp_peer_lock must be held when this function is called */
int redir_entry_destroy(struct redir_entry * e)
{
struct redir_entry *match;
TRACE_ENTRY("%p", e);
CHECK_PARAMS(e && (e->eyec == REDIR_ENTRY_EYEC));
/* If the entry is linked, lock the rwlock also */
if (!FD_IS_LIST_EMPTY(&e->redir_list)) {
CHECK_POSIX( pthread_rwlock_wrlock( RWLOCK_REDIR(e) ) );
fd_list_unlink(&e->redir_list);
CHECK_POSIX( pthread_rwlock_unlock( RWLOCK_REDIR(e) ) );
switch (e->type) {
case ALL_SESSION:
/* If the entry is in the hash table, lock the rwlock also */
HASH_FIND(hh, redirect_hash_table[e->type], e->data.session.s, e->data.session.l, match);
if (match) {
/* TODO: check if e == match? */
CHECK_POSIX( pthread_rwlock_wrlock( RWLOCK_REDIR(e) ) );
HASH_DELETE(hh, redirect_hash_table[e->type], match);
CHECK_POSIX( pthread_rwlock_unlock( RWLOCK_REDIR(e) ) );
}
break;
case ALL_USER:
/* If the entry is in the hash table, lock the rwlock also */
HASH_FIND(hh, redirect_hash_table[e->type], e->data.user.s, e->data.user.l, match);
if (match) {
/* TODO: check if e == match? */
CHECK_POSIX( pthread_rwlock_wrlock( RWLOCK_REDIR(e) ) );
HASH_DELETE(hh, redirect_hash_table[e->type], match);
CHECK_POSIX( pthread_rwlock_unlock( RWLOCK_REDIR(e) ) );
}
break;
default:
/* If the entry is linked, lock the rwlock also */
if (!FD_IS_LIST_EMPTY(&e->redir_list)) {
CHECK_POSIX( pthread_rwlock_wrlock( RWLOCK_REDIR(e) ) );
fd_list_unlink(&e->redir_list);
CHECK_POSIX( pthread_rwlock_unlock( RWLOCK_REDIR(e) ) );
}
break;
}
/* Now unlink from other list */
......
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* Authors: Sebastien Decugis <sdecugis@freediameter.net> *
* and Thomas Klausner <tk@giga.or.at> *
* *
* Copyright (c) 2013, WIDE Project and NICT *
* Copyright (c) 2013, 2014, WIDE Project and NICT *
* All rights reserved. *
* *
* Redistribution and use of this software in source and binary forms, with or without modification, are *
......@@ -217,6 +218,72 @@ static int apply_rule(struct redir_entry * e, struct msg * msg, struct fd_list *
return 0;
}
static int redir_exist_for_type(int rule_type)
{
int ret;
switch(rule_type) {
case ALL_SESSION:
case ALL_USER:
ret = redirect_hash_table[rule_type] != NULL;
break;
default:
ret = !FD_IS_LIST_EMPTY(&redirects_usages[rule_type].sentinel);
break;
}
return ret;
}
static int match_message(int rule_type, struct msg *msg, union matchdata *data, struct fd_list * candidates)
{
struct fd_list * li;
struct redir_entry * e = NULL;
int ret = 0;
switch(rule_type) {
case ALL_SESSION:
HASH_FIND(hh, redirect_hash_table[rule_type], data->session.s, data->session.l, e);
if (e) {
/* This message matches a rule, apply */
CHECK_FCT_DO( ret = apply_rule(e, msg, candidates), break );
}
break;
case ALL_USER:
HASH_FIND(hh, redirect_hash_table[rule_type], data->user.s, data->user.l, e);
if (e) {
/* This message matches a rule, apply */
CHECK_FCT_DO( ret = apply_rule(e, msg, candidates), break );
}
break;
default:
/* Attempt each rule we have stored */
for (li = redirects_usages[rule_type].sentinel.next; li != &redirects_usages[rule_type].sentinel; li = li->next) {
e = li->o;
/* Does it match ? */
if (rule_type != ALL_HOST) { /* this one is an exception, we handle it separately */
int cmp = redir_entry_cmp_key[rule_type](data, &e->data);
if (cmp > 0)
continue;
if (cmp < 0)
break;
}
/* This rule matches (or we are in ALL_HOST), apply */
CHECK_FCT_DO( ret = apply_rule(e, msg, candidates), break );
/* If this was a DONT_CACHE rule, we unlink it, so that it will not be used again */
if (rule_type == DONT_CACHE) {
li = li->prev;
fd_list_unlink( li->next );
/* We cannot delete here without taking the mutex, which would mean we have first to release the lock...
just let expiry garbage collect the rule */
}
}
}
return ret;
}
/* OUT callback */
int redir_out_cb(void * cbdata, struct msg ** pmsg, struct fd_list * candidates)
......@@ -234,42 +301,16 @@ int redir_out_cb(void * cbdata, struct msg ** pmsg, struct fd_list * candidates)
CHECK_POSIX( pthread_rwlock_rdlock( &redirects_usages[i].lock ) );
}
if (!FD_IS_LIST_EMPTY(&redirects_usages[i].sentinel)) {
if (redir_exist_for_type(i)) {
union matchdata data;
int nodata; /* The message does not allow to apply this rule, skip */
/* Retrieve the data that may match in the message */
CHECK_FCT_DO( ret = get_data_to_match(i, msg, &data, &nodata), goto out );
/* If this message may match some of our rules */
if (!nodata) {
struct fd_list * li;
/* Attempt each rule we have stored */
for (li = redirects_usages[i].sentinel.next; li != &redirects_usages[i].sentinel; li = li->next) {
struct redir_entry * e = li->o;
/* Does it match ? */
if (i != ALL_HOST) { /* this one is an exception, we handle it separately */
int cmp = redir_entry_cmp_key[i](&data, &e->data);
if (cmp > 0)
continue;
if (cmp < 0)
break;
}
/* This rule matches (or we are in ALL_HOST), apply */
CHECK_FCT_DO( ret = apply_rule(e, msg, candidates), goto out );
/* If this was a DONT_CACHE rule, we unlink it, so that it will not be used again */
if (i == DONT_CACHE) {
li=li->prev;
fd_list_unlink( li->next );
/* We cannot delete here without taking the mutex, which would mean we have first to release the lock...
just let expiry garbage collet the rule */
}
}
}
/* If data found for this type of rule, then try matching it */
if (!nodata)
ret = match_message(i, msg, &data, candidates);
}
out:
CHECK_POSIX( pthread_rwlock_unlock( &redirects_usages[i].lock ) );
......
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* Authors: Sebastien Decugis <sdecugis@freediameter.net> *
* and Thomas Klausner <tk@giga.or.at> *
* *
* Copyright (c) 2011, WIDE Project and NICT *
* Copyright (c) 2011, 2014, WIDE Project and NICT *
* All rights reserved. *
* *
* Redistribution and use of this software in source and binary forms, with or without modification, are *
......@@ -71,8 +72,6 @@ EXTENSION_ENTRY("rt_redirect", redir_entry);
/* And terminate it */
void fd_ext_fini(void)
{
int i;
/* Unregister the callbacks */
if (fwd_hdl) {
CHECK_FCT_DO( fd_rt_fwd_unregister(fwd_hdl, NULL), );
......@@ -85,18 +84,7 @@ void fd_ext_fini(void)
CHECK_FCT_DO( fd_thr_term(&exp_thr), );
/* Empty all entries */
CHECK_POSIX_DO( pthread_mutex_lock(&redir_exp_peer_lock), );
for (i = 0; i <= H_U_MAX; i++) {
CHECK_POSIX_DO( pthread_rwlock_wrlock( &redirects_usages[i].lock), );
while (!FD_IS_LIST_EMPTY(&redirects_usages[i].sentinel)) {
struct redir_entry * e = redirects_usages[i].sentinel.next->o;
fd_list_unlink(&e->redir_list);
CHECK_FCT_DO( redir_entry_destroy(e), );
}
CHECK_POSIX_DO( pthread_rwlock_unlock( &redirects_usages[i].lock), );
CHECK_POSIX_DO( pthread_rwlock_destroy( &redirects_usages[i].lock), );
}
CHECK_POSIX_DO( pthread_mutex_unlock(&redir_exp_peer_lock), );
redir_entry_fini();
return;
}
/*********************************************************************************************************
* Software License Agreement (BSD License) *
* Author: Sebastien Decugis <sdecugis@freediameter.net> *
* *
* Copyright (c) 2013, WIDE Project and NICT *
* Author: Sebastien Decugis <sdecugis@freediameter.net> and *
* Thomas Klausner <tk@giga.or.at> *
* *
* Copyright (c) 2013, 2014, WIDE Project and NICT *
* All rights reserved. *
* *
* Redistribution and use of this software in source and binary forms, with or without modification, are *
......@@ -36,6 +37,8 @@
/* Diameter Redirect management */
#include <freeDiameter/extension.h>
#include "uthash.h"
/* There are 2 locks in this module. The priority is established as follow to avoid deadlocks:
exp_peer mutex > usages rwlock.
(e.g., the rwlock can be taken while holding the mutex, but not the other way)
......@@ -129,12 +132,13 @@ struct redir_entry {
struct fd_list target_peers_list; /* The list of Redirect-Hosts for this entry */
struct timespec timeout; /* When does this entry expires? */
struct timespec timeout; /* When does this entry expire? */
struct fd_list exp_list; /* chain in the expire_list list, ordered by expiration date, protected by exp_peer_lock */
enum redir_h_u type; /* Type of this entry */
enum redir_h_u type; /* Type of this entry */
struct fd_list redir_list; /* link in redirects_usages lists. Lists are ordered by the data value. Protected by rw locks */
union matchdata data; /* The strings are duplicated & must be freed in this structure */
union matchdata data; /* The strings are duplicated & must be freed in this structure */
UT_hash_handle hh; /* magic entry for hash table */
};
/* The array where the redir_entries are stored */
......@@ -144,6 +148,8 @@ struct redir_line {
struct fd_list sentinel; /* list of redir_entry, the "o" field of the sentinel points to the redir_line entry */
};
extern struct redir_line redirects_usages[];
/* the hash table where entries are stored for ALL_SESSION and ALL_USER */
extern struct redir_entry *redirect_hash_table[];
/* Accelerator to the line lock */
#define RWLOCK_REDIR( _entry ) ( &(redirects_usages[(_entry)->type].lock) )
......@@ -157,6 +163,7 @@ extern struct dict_object * redir_dict_un;
/* Functions on redir_entry */
int redir_entry_init();
int redir_entry_fini();
int redir_entry_new(struct redir_entry ** e, struct fd_list * targets, uint32_t rhu, struct msg * qry, DiamId_t nh, size_t nhlen, os0_t oh, size_t ohlen);
extern int (*redir_entry_cmp_key[])(union matchdata * , union matchdata *); /* compare functions */
int redir_entry_insert(struct redir_entry * e);
......
This diff is collapsed.
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