dns_cache: Replace cache timeout mechanism with time wheel algorithm to reduce CPU usage
This commit is contained in:
@@ -15,8 +15,8 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
BIN=smartdns
|
||||
OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o
|
||||
OBJS_MAIN=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o lib/conf.o lib/nftset.o
|
||||
OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/timer_wheel.o
|
||||
OBJS_MAIN=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o timer.o lib/conf.o lib/nftset.o
|
||||
OBJS=$(OBJS_MAIN) $(OBJS_LIB)
|
||||
|
||||
# cflags
|
||||
|
||||
339
src/dns_cache.c
339
src/dns_cache.c
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "dns_cache.h"
|
||||
#include "stringutil.h"
|
||||
#include "timer.h"
|
||||
#include "tlog.h"
|
||||
#include "util.h"
|
||||
#include <errno.h>
|
||||
@@ -27,31 +28,28 @@
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define DNS_CACHE_MAX_HITNUM 5000
|
||||
#define DNS_CACHE_HITNUM_STEP 2
|
||||
#define DNS_CACHE_MAX_HITNUM 6000
|
||||
#define DNS_CACHE_HITNUM_STEP 3
|
||||
#define DNS_CACHE_HITNUM_STEP_MAX 6
|
||||
#define DNS_CACHE_READ_TIMEOUT 60
|
||||
|
||||
struct dns_cache_head {
|
||||
struct hash_table cache_hash;
|
||||
struct list_head cache_list;
|
||||
struct list_head inactive_list;
|
||||
atomic_t num;
|
||||
int size;
|
||||
int enable_inactive;
|
||||
int inactive_list_expired;
|
||||
pthread_mutex_t lock;
|
||||
struct dns_cache *last_active_inserted;
|
||||
dns_cache_callback timeout_callback;
|
||||
};
|
||||
|
||||
typedef int (*dns_cache_read_callback)(struct dns_cache_record *cache_record, struct dns_cache_data *cache_data);
|
||||
|
||||
static struct dns_cache_head dns_cache_head;
|
||||
|
||||
int dns_cache_init(int size, int enable_inactive, int inactive_list_expired)
|
||||
int dns_cache_init(int size, dns_cache_callback timeout_callback)
|
||||
{
|
||||
int bits = 0;
|
||||
INIT_LIST_HEAD(&dns_cache_head.cache_list);
|
||||
INIT_LIST_HEAD(&dns_cache_head.inactive_list);
|
||||
|
||||
bits = ilog2(size) - 1;
|
||||
if (bits >= 20) {
|
||||
@@ -63,35 +61,14 @@ int dns_cache_init(int size, int enable_inactive, int inactive_list_expired)
|
||||
hash_table_init(dns_cache_head.cache_hash, bits, malloc);
|
||||
atomic_set(&dns_cache_head.num, 0);
|
||||
dns_cache_head.size = size;
|
||||
dns_cache_head.enable_inactive = enable_inactive;
|
||||
dns_cache_head.inactive_list_expired = inactive_list_expired;
|
||||
dns_cache_head.last_active_inserted = NULL;
|
||||
dns_cache_head.timeout_callback = timeout_callback;
|
||||
pthread_mutex_init(&dns_cache_head.lock, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) struct dns_cache *_dns_cache_last(void)
|
||||
static struct dns_cache *_dns_cache_first(void)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
|
||||
dns_cache = list_last_entry(&dns_cache_head.inactive_list, struct dns_cache, list);
|
||||
if (dns_cache) {
|
||||
return dns_cache;
|
||||
}
|
||||
|
||||
return list_last_entry(&dns_cache_head.cache_list, struct dns_cache, list);
|
||||
}
|
||||
|
||||
static struct dns_cache *_dns_inactive_cache_first(void)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
|
||||
dns_cache = list_first_entry_or_null(&dns_cache_head.inactive_list, struct dns_cache, list);
|
||||
if (dns_cache) {
|
||||
return dns_cache;
|
||||
}
|
||||
|
||||
return list_first_entry_or_null(&dns_cache_head.cache_list, struct dns_cache, list);
|
||||
}
|
||||
|
||||
@@ -117,6 +94,7 @@ void dns_cache_release(struct dns_cache *dns_cache)
|
||||
if (dns_cache == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!atomic_dec_and_test(&dns_cache->ref)) {
|
||||
return;
|
||||
}
|
||||
@@ -128,23 +106,8 @@ static void _dns_cache_remove(struct dns_cache *dns_cache)
|
||||
{
|
||||
hash_del(&dns_cache->node);
|
||||
list_del_init(&dns_cache->list);
|
||||
dns_timer_del(&dns_cache->timer);
|
||||
dns_cache_release(dns_cache);
|
||||
|
||||
if (dns_cache == dns_cache_head.last_active_inserted) {
|
||||
dns_cache_release(dns_cache_head.last_active_inserted);
|
||||
dns_cache_head.last_active_inserted = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void _dns_cache_move_inactive(struct dns_cache *dns_cache)
|
||||
{
|
||||
list_del(&dns_cache->list);
|
||||
list_add_tail(&dns_cache->list, &dns_cache_head.inactive_list);
|
||||
if (dns_cache == dns_cache_head.last_active_inserted) {
|
||||
dns_cache_release(dns_cache_head.last_active_inserted);
|
||||
dns_cache_head.last_active_inserted = NULL;
|
||||
}
|
||||
time(&dns_cache->info.replace_time);
|
||||
}
|
||||
|
||||
enum CACHE_TYPE dns_cache_data_type(struct dns_cache_data *cache_data)
|
||||
@@ -271,41 +234,33 @@ struct dns_cache_data *dns_cache_new_data_packet(void *packet, size_t packet_len
|
||||
return (struct dns_cache_data *)cache_packet;
|
||||
}
|
||||
|
||||
static void _dns_cache_insert_sorted(struct dns_cache *dns_cache, struct list_head *head)
|
||||
static void dns_cache_timer_relase(struct tw_timer_list *timer, void *data)
|
||||
{
|
||||
time_t ttl;
|
||||
struct dns_cache *tmp = NULL;
|
||||
struct list_head *insert_head = head;
|
||||
|
||||
ttl = dns_cache->info.insert_time + dns_cache->info.ttl;
|
||||
if (dns_cache_head.last_active_inserted && dns_cache != dns_cache_head.last_active_inserted) {
|
||||
time_t ttl_last =
|
||||
dns_cache_head.last_active_inserted->info.insert_time + dns_cache_head.last_active_inserted->info.ttl;
|
||||
if (ttl == ttl_last) {
|
||||
insert_head = &(dns_cache_head.last_active_inserted->list);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* ascending order */
|
||||
list_for_each_entry_reverse(tmp, head, list)
|
||||
{
|
||||
if ((tmp->info.insert_time + tmp->info.ttl) <= ttl) {
|
||||
insert_head = &tmp->list;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (dns_cache_head.last_active_inserted) {
|
||||
dns_cache_release(dns_cache_head.last_active_inserted);
|
||||
}
|
||||
list_add(&dns_cache->list, insert_head);
|
||||
dns_cache_head.last_active_inserted = dns_cache;
|
||||
dns_cache_get(dns_cache);
|
||||
struct dns_cache *dns_cache = data;
|
||||
dns_cache_release(dns_cache);
|
||||
}
|
||||
|
||||
static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive, int inactive,
|
||||
static void dns_cache_expired(struct tw_timer_list *timer, void *data, unsigned long timestamp)
|
||||
{
|
||||
struct dns_cache *dns_cache = data;
|
||||
|
||||
if (dns_cache->del_pending == 1) {
|
||||
dns_cache_release(dns_cache);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dns_cache_head.timeout_callback) {
|
||||
if (dns_cache_head.timeout_callback(dns_cache) != 0) {
|
||||
dns_cache_release(dns_cache);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dns_cache->del_pending = 1;
|
||||
dns_timer_mod(&dns_cache->timer, 3);
|
||||
}
|
||||
|
||||
static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int timeout, int update_time,
|
||||
struct dns_cache_data *cache_data)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
@@ -318,7 +273,7 @@ static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int spee
|
||||
/* lookup existing cache */
|
||||
dns_cache = dns_cache_lookup(cache_key);
|
||||
if (dns_cache == NULL) {
|
||||
return dns_cache_insert(cache_key, ttl, speed, no_inactive, cache_data);
|
||||
return dns_cache_insert(cache_key, ttl, speed, timeout, cache_data);
|
||||
}
|
||||
|
||||
if (ttl < DNS_CACHE_TTL_MIN) {
|
||||
@@ -333,38 +288,35 @@ static int _dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int spee
|
||||
dns_cache->info.query_flag = cache_key->query_flag;
|
||||
dns_cache->info.ttl = ttl;
|
||||
dns_cache->info.speed = speed;
|
||||
dns_cache->info.no_inactive = no_inactive;
|
||||
dns_cache->info.timeout = timeout;
|
||||
dns_cache->info.is_visited = 1;
|
||||
if (cache_data) {
|
||||
old_cache_data = dns_cache->cache_data;
|
||||
dns_cache->cache_data = cache_data;
|
||||
list_del(&dns_cache->list);
|
||||
|
||||
if (inactive == 0) {
|
||||
time(&dns_cache->info.insert_time);
|
||||
time(&dns_cache->info.replace_time);
|
||||
_dns_cache_insert_sorted(dns_cache, &dns_cache_head.cache_list);
|
||||
} else {
|
||||
time(&dns_cache->info.replace_time);
|
||||
list_add_tail(&dns_cache->list, &dns_cache_head.inactive_list);
|
||||
if (old_cache_data == cache_data) {
|
||||
old_cache_data = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
if (update_time) {
|
||||
time(&dns_cache->info.insert_time);
|
||||
}
|
||||
time(&dns_cache->info.replace_time);
|
||||
list_del(&dns_cache->list);
|
||||
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
|
||||
dns_timer_mod(&dns_cache->timer, timeout);
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
if (old_cache_data) {
|
||||
dns_cache_data_free(old_cache_data);
|
||||
}
|
||||
dns_cache_release(dns_cache);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive,
|
||||
int dns_cache_replace(struct dns_cache_key *cache_key, int ttl, int speed, int timeout, int update_time,
|
||||
struct dns_cache_data *cache_data)
|
||||
{
|
||||
return _dns_cache_replace(cache_key, ttl, speed, no_inactive, 0, cache_data);
|
||||
}
|
||||
|
||||
int dns_cache_replace_inactive(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive,
|
||||
struct dns_cache_data *cache_data)
|
||||
{
|
||||
return _dns_cache_replace(cache_key, ttl, speed, no_inactive, 1, cache_data);
|
||||
return _dns_cache_replace(cache_key, ttl, speed, timeout, update_time, cache_data);
|
||||
}
|
||||
|
||||
static void _dns_cache_remove_by_domain(struct dns_cache_key *cache_key)
|
||||
@@ -430,24 +382,26 @@ static int _dns_cache_insert(struct dns_cache_info *info, struct dns_cache_data
|
||||
memcpy(&dns_cache->info, info, sizeof(*info));
|
||||
dns_cache->del_pending = 0;
|
||||
dns_cache->cache_data = cache_data;
|
||||
dns_cache->timer.function = dns_cache_expired;
|
||||
dns_cache->timer.del_function = dns_cache_timer_relase;
|
||||
dns_cache->timer.expires = info->timeout;
|
||||
dns_cache->timer.data = dns_cache;
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
hash_table_add(dns_cache_head.cache_hash, &dns_cache->node, key);
|
||||
if (head == &dns_cache_head.inactive_list) {
|
||||
list_add_tail(&dns_cache->list, head);
|
||||
} else {
|
||||
_dns_cache_insert_sorted(dns_cache, head);
|
||||
}
|
||||
INIT_LIST_HEAD(&dns_cache->check_list);
|
||||
|
||||
/* Release extra cache, remove oldest cache record */
|
||||
if (atomic_inc_return(&dns_cache_head.num) > dns_cache_head.size) {
|
||||
struct dns_cache *del_cache = NULL;
|
||||
del_cache = _dns_inactive_cache_first();
|
||||
del_cache = _dns_cache_first();
|
||||
if (del_cache) {
|
||||
_dns_cache_remove(del_cache);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
dns_cache_get(dns_cache);
|
||||
dns_timer_add(&dns_cache->timer);
|
||||
|
||||
return 0;
|
||||
errout:
|
||||
@@ -458,7 +412,7 @@ errout:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dns_cache_insert(struct dns_cache_key *cache_key, int ttl, int speed, int no_inactive,
|
||||
int dns_cache_insert(struct dns_cache_key *cache_key, int ttl, int speed, int timeout,
|
||||
struct dns_cache_data *cache_data)
|
||||
{
|
||||
struct dns_cache_info info;
|
||||
@@ -485,7 +439,7 @@ int dns_cache_insert(struct dns_cache_key *cache_key, int ttl, int speed, int no
|
||||
info.ttl = ttl;
|
||||
info.hitnum_update_add = DNS_CACHE_HITNUM_STEP;
|
||||
info.speed = speed;
|
||||
info.no_inactive = no_inactive;
|
||||
info.timeout = timeout;
|
||||
info.is_visited = 1;
|
||||
time(&info.insert_time);
|
||||
time(&info.replace_time);
|
||||
@@ -535,14 +489,8 @@ struct dns_cache *dns_cache_lookup(struct dns_cache_key *cache_key)
|
||||
}
|
||||
|
||||
if (dns_cache_ret) {
|
||||
/* Return NULL if the cache times out */
|
||||
if (dns_cache_head.enable_inactive == 0 && (now - dns_cache_ret->info.insert_time > dns_cache_ret->info.ttl)) {
|
||||
_dns_cache_remove(dns_cache_ret);
|
||||
dns_cache_ret = NULL;
|
||||
} else {
|
||||
dns_cache_get(dns_cache_ret);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
@@ -638,6 +586,8 @@ void dns_cache_update(struct dns_cache *dns_cache)
|
||||
{
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
if (!list_empty(&dns_cache->list)) {
|
||||
list_del_init(&dns_cache->list);
|
||||
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
|
||||
dns_cache->info.hitnum += dns_cache->info.hitnum_update_add;
|
||||
if (dns_cache->info.hitnum > DNS_CACHE_MAX_HITNUM) {
|
||||
dns_cache->info.hitnum = DNS_CACHE_MAX_HITNUM;
|
||||
@@ -651,141 +601,20 @@ void dns_cache_update(struct dns_cache *dns_cache)
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
}
|
||||
|
||||
static void _dns_cache_remove_expired_ttl(dns_cache_callback inactive_precallback, int ttl_inactive_pre,
|
||||
unsigned int max_callback_num, const time_t *now)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
struct dns_cache *tmp = NULL;
|
||||
unsigned int callback_num = 0;
|
||||
int ttl = 0;
|
||||
LIST_HEAD(checklist);
|
||||
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.inactive_list, list)
|
||||
{
|
||||
ttl = dns_cache->info.insert_time + dns_cache->info.ttl - *now;
|
||||
if (ttl > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dns_cache_head.inactive_list_expired + ttl < 0) {
|
||||
_dns_cache_remove(dns_cache);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inactive_precallback == NULL) {
|
||||
if (dns_cache_head.inactive_list_expired + ttl > 0) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ttl = *now - dns_cache->info.replace_time;
|
||||
if (ttl < ttl_inactive_pre) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (callback_num >= max_callback_num) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (dns_cache->del_pending == 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the TTL time is in the pre-timeout range, call callback function */
|
||||
dns_cache_get(dns_cache);
|
||||
list_add_tail(&dns_cache->check_list, &checklist);
|
||||
dns_cache->del_pending = 1;
|
||||
callback_num++;
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
list_for_each_entry_safe(dns_cache, tmp, &checklist, check_list)
|
||||
{
|
||||
/* run inactive_precallback */
|
||||
if (inactive_precallback) {
|
||||
inactive_precallback(dns_cache);
|
||||
}
|
||||
dns_cache_release(dns_cache);
|
||||
}
|
||||
}
|
||||
|
||||
void dns_cache_invalidate(dns_cache_callback precallback, int ttl_pre, unsigned int max_callback_num,
|
||||
dns_cache_callback inactive_precallback, int ttl_inactive_pre)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
struct dns_cache *tmp = NULL;
|
||||
time_t now = 0;
|
||||
int ttl = 0;
|
||||
LIST_HEAD(checklist);
|
||||
unsigned int callback_num = 0;
|
||||
|
||||
if (max_callback_num <= 0) {
|
||||
max_callback_num = -1;
|
||||
}
|
||||
|
||||
if (dns_cache_head.size <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
time(&now);
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list)
|
||||
{
|
||||
ttl = dns_cache->info.insert_time + dns_cache->info.ttl - now;
|
||||
if (ttl > ttl_pre) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ttl > 0 && ttl < ttl_pre) {
|
||||
/* If the TTL time is in the pre-timeout range, call callback function */
|
||||
if (precallback && dns_cache->del_pending == 0 && callback_num < max_callback_num) {
|
||||
list_add_tail(&dns_cache->check_list, &checklist);
|
||||
dns_cache_get(dns_cache);
|
||||
dns_cache->del_pending = 1;
|
||||
callback_num++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ttl <= 0) {
|
||||
if (dns_cache_head.enable_inactive && dns_cache->info.no_inactive == 0) {
|
||||
_dns_cache_move_inactive(dns_cache);
|
||||
} else {
|
||||
_dns_cache_remove(dns_cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
if (dns_cache_head.enable_inactive && dns_cache_head.inactive_list_expired != 0) {
|
||||
_dns_cache_remove_expired_ttl(inactive_precallback, ttl_inactive_pre, max_callback_num, &now);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(dns_cache, tmp, &checklist, check_list)
|
||||
{
|
||||
/* run callback */
|
||||
if (precallback) {
|
||||
precallback(dns_cache);
|
||||
}
|
||||
list_del(&dns_cache->check_list);
|
||||
dns_cache_release(dns_cache);
|
||||
}
|
||||
}
|
||||
|
||||
static int _dns_cache_read_to_cache(struct dns_cache_record *cache_record, struct dns_cache_data *cache_data)
|
||||
{
|
||||
struct list_head *head = NULL;
|
||||
|
||||
if (cache_record->type == CACHE_RECORD_TYPE_ACTIVE) {
|
||||
head = &dns_cache_head.cache_list;
|
||||
} else if (cache_record->type == CACHE_RECORD_TYPE_INACTIVE) {
|
||||
head = &dns_cache_head.inactive_list;
|
||||
} else {
|
||||
tlog(TLOG_ERROR, "read cache record type is invalid.");
|
||||
goto errout;
|
||||
struct dns_cache_info *info = &cache_record->info;
|
||||
|
||||
time_t now = time(NULL);
|
||||
unsigned int seed_tmp = now;
|
||||
int passed_time = now - info->replace_time;
|
||||
int timeout = info->timeout - passed_time;
|
||||
if (timeout < DNS_CACHE_READ_TIMEOUT * 2) {
|
||||
timeout = DNS_CACHE_READ_TIMEOUT + (rand_r(&seed_tmp) % DNS_CACHE_READ_TIMEOUT);
|
||||
}
|
||||
info->timeout = timeout;
|
||||
|
||||
if (_dns_cache_insert(&cache_record->info, cache_data, head) != 0) {
|
||||
tlog(TLOG_ERROR, "insert cache data failed.");
|
||||
@@ -854,11 +683,6 @@ static int _dns_cache_read_record(int fd, uint32_t cache_number, dns_cache_read_
|
||||
cache_record.info.is_visited = 0;
|
||||
cache_record.info.domain[DNS_MAX_CNAME_LEN - 1] = '\0';
|
||||
cache_record.info.dns_group_name[DNS_GROUP_NAME_LEN - 1] = '\0';
|
||||
if (cache_record.type >= CACHE_RECORD_TYPE_END) {
|
||||
tlog(TLOG_ERROR, "read cache record type is invalid.");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
ret = callback(&cache_record, cache_data);
|
||||
if (ret == -2) {
|
||||
cache_data = NULL;
|
||||
@@ -930,7 +754,7 @@ int dns_cache_load(const char *file)
|
||||
return _dns_cache_file_read(file, _dns_cache_read_to_cache);
|
||||
}
|
||||
|
||||
static int _dns_cache_write_record(int fd, uint32_t *cache_number, enum CACHE_RECORD_TYPE type, struct list_head *head)
|
||||
static int _dns_cache_write_record(int fd, uint32_t *cache_number, struct list_head *head)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
struct dns_cache *tmp = NULL;
|
||||
@@ -940,7 +764,6 @@ static int _dns_cache_write_record(int fd, uint32_t *cache_number, enum CACHE_RE
|
||||
list_for_each_entry_safe(dns_cache, tmp, head, list)
|
||||
{
|
||||
cache_record.magic = MAGIC_RECORD;
|
||||
cache_record.type = type;
|
||||
memcpy(&cache_record.info, &dns_cache->info, sizeof(struct dns_cache_info));
|
||||
ssize_t ret = write(fd, &cache_record, sizeof(cache_record));
|
||||
if (ret != sizeof(cache_record)) {
|
||||
@@ -968,11 +791,7 @@ errout:
|
||||
|
||||
static int _dns_cache_write_records(int fd, uint32_t *cache_number)
|
||||
{
|
||||
if (_dns_cache_write_record(fd, cache_number, CACHE_RECORD_TYPE_ACTIVE, &dns_cache_head.cache_list) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_dns_cache_write_record(fd, cache_number, CACHE_RECORD_TYPE_INACTIVE, &dns_cache_head.inactive_list) != 0) {
|
||||
if (_dns_cache_write_record(fd, cache_number, &dns_cache_head.cache_list) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1060,17 +879,7 @@ void dns_cache_destroy(void)
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
struct dns_cache *tmp = NULL;
|
||||
|
||||
if (dns_cache_head.last_active_inserted) {
|
||||
dns_cache_release(dns_cache_head.last_active_inserted);
|
||||
dns_cache_head.last_active_inserted = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.inactive_list, list)
|
||||
{
|
||||
_dns_cache_delete(dns_cache);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list)
|
||||
{
|
||||
_dns_cache_delete(dns_cache);
|
||||
@@ -1083,6 +892,6 @@ void dns_cache_destroy(void)
|
||||
|
||||
const char *dns_cache_file_version(void)
|
||||
{
|
||||
const char *version = "cache ver 1.0";
|
||||
const char *version = "cache ver 1.2";
|
||||
return version;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "hash.h"
|
||||
#include "hashtable.h"
|
||||
#include "list.h"
|
||||
#include "timer.h"
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
@@ -45,12 +46,6 @@ enum CACHE_TYPE {
|
||||
CACHE_TYPE_PACKET,
|
||||
};
|
||||
|
||||
enum CACHE_RECORD_TYPE {
|
||||
CACHE_RECORD_TYPE_ACTIVE,
|
||||
CACHE_RECORD_TYPE_INACTIVE,
|
||||
CACHE_RECORD_TYPE_END,
|
||||
};
|
||||
|
||||
struct dns_cache_data_head {
|
||||
enum CACHE_TYPE cache_type;
|
||||
int is_soa;
|
||||
@@ -90,7 +85,7 @@ struct dns_cache_info {
|
||||
int ttl;
|
||||
int hitnum;
|
||||
int speed;
|
||||
int no_inactive;
|
||||
int timeout;
|
||||
int hitnum_update_add;
|
||||
int is_visited;
|
||||
time_t insert_time;
|
||||
@@ -99,7 +94,6 @@ struct dns_cache_info {
|
||||
|
||||
struct dns_cache_record {
|
||||
uint32_t magic;
|
||||
enum CACHE_RECORD_TYPE type;
|
||||
struct dns_cache_info info;
|
||||
};
|
||||
|
||||
@@ -113,6 +107,8 @@ struct dns_cache {
|
||||
|
||||
struct dns_cache_info info;
|
||||
struct dns_cache_data *cache_data;
|
||||
|
||||
struct tw_timer_list timer;
|
||||
};
|
||||
|
||||
struct dns_cache_file {
|
||||
@@ -138,15 +134,14 @@ void dns_cache_data_free(struct dns_cache_data *data);
|
||||
|
||||
struct dns_cache_data *dns_cache_new_data_packet(void *packet, size_t packet_len);
|
||||
|
||||
int dns_cache_init(int size, int enable_inactive, int inactive_list_expired);
|
||||
typedef int (*dns_cache_callback)(struct dns_cache *dns_cache);
|
||||
|
||||
int dns_cache_replace(struct dns_cache_key *key, int ttl, int speed, int no_inactive,
|
||||
int dns_cache_init(int size, dns_cache_callback timeout_callback);
|
||||
|
||||
int dns_cache_replace(struct dns_cache_key *key, int ttl, int speed, int tiemout, int update_time,
|
||||
struct dns_cache_data *cache_data);
|
||||
|
||||
int dns_cache_replace_inactive(struct dns_cache_key *key, int ttl, int speed, int no_inactive,
|
||||
struct dns_cache_data *cache_data);
|
||||
|
||||
int dns_cache_insert(struct dns_cache_key *key, int ttl, int speed, int no_inactive, struct dns_cache_data *cache_data);
|
||||
int dns_cache_insert(struct dns_cache_key *key, int ttl, int speed, int timeout, struct dns_cache_data *cache_data);
|
||||
|
||||
struct dns_cache *dns_cache_lookup(struct dns_cache_key *key);
|
||||
|
||||
@@ -162,11 +157,6 @@ int dns_cache_is_visited(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_update(struct dns_cache *dns_cache);
|
||||
|
||||
typedef void dns_cache_callback(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_invalidate(dns_cache_callback precallback, int ttl_pre, unsigned int max_callback_num,
|
||||
dns_cache_callback inactive_precallback, int ttl_inactive_pre);
|
||||
|
||||
int dns_cache_get_ttl(struct dns_cache *dns_cache);
|
||||
|
||||
int dns_cache_get_cname_ttl(struct dns_cache *dns_cache);
|
||||
|
||||
140
src/dns_server.c
140
src/dns_server.c
@@ -1253,6 +1253,43 @@ static int _dns_reply_inpacket(struct dns_request *request, unsigned char *inpac
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _dns_server_get_cache_timeout(struct dns_request *request, int ttl)
|
||||
{
|
||||
int timeout = 0;
|
||||
if (dns_conf_prefetch) {
|
||||
if (dns_conf_serve_expired) {
|
||||
timeout = dns_conf_serve_expired_prefetch_time;
|
||||
if (timeout == 0) {
|
||||
timeout = dns_conf_serve_expired_ttl / 2;
|
||||
if (timeout == 0 || timeout > EXPIRED_DOMAIN_PREFETCH_TIME) {
|
||||
timeout = EXPIRED_DOMAIN_PREFETCH_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
if (request->prefetch == 0) {
|
||||
timeout += ttl;
|
||||
}
|
||||
} else {
|
||||
timeout = ttl - 3;
|
||||
}
|
||||
} else {
|
||||
timeout = ttl;
|
||||
if (dns_conf_serve_expired) {
|
||||
timeout += dns_conf_serve_expired_ttl;
|
||||
}
|
||||
}
|
||||
|
||||
if (request->prefetch) {
|
||||
timeout -= 1;
|
||||
}
|
||||
|
||||
if (timeout <= 0) {
|
||||
timeout = 1;
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static int _dns_server_request_update_cache(struct dns_request *request, dns_type_t qtype,
|
||||
struct dns_cache_data *cache_data, int has_soa, int cache_ttl)
|
||||
{
|
||||
@@ -1293,18 +1330,13 @@ static int _dns_server_request_update_cache(struct dns_request *request, dns_typ
|
||||
cache_key.query_flag = request->server_flags;
|
||||
|
||||
if (request->prefetch) {
|
||||
if (request->prefetch_expired_domain == 0) {
|
||||
if (dns_cache_replace(&cache_key, ttl, speed, request->no_serve_expired, cache_data) != 0) {
|
||||
if (dns_cache_replace(&cache_key, ttl, speed, _dns_server_get_cache_timeout(request, ttl),
|
||||
!request->prefetch_expired_domain, cache_data) != 0) {
|
||||
goto errout;
|
||||
}
|
||||
} else {
|
||||
if (dns_cache_replace_inactive(&cache_key, ttl, speed, request->no_serve_expired, cache_data) != 0) {
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* insert result to cache */
|
||||
if (dns_cache_insert(&cache_key, ttl, speed, request->no_serve_expired, cache_data) != 0) {
|
||||
if (dns_cache_insert(&cache_key, ttl, speed, _dns_server_get_cache_timeout(request, ttl), cache_data) != 0) {
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
@@ -1441,18 +1473,13 @@ static int _dns_cache_cname_packet(struct dns_server_post_context *context)
|
||||
cache_key.query_flag = request->server_flags;
|
||||
|
||||
if (request->prefetch) {
|
||||
if (request->prefetch_expired_domain == 0) {
|
||||
if (dns_cache_replace(&cache_key, ttl, speed, request->no_serve_expired, cache_packet) != 0) {
|
||||
if (dns_cache_replace(&cache_key, ttl, speed, _dns_server_get_cache_timeout(request, ttl),
|
||||
!request->prefetch_expired_domain, cache_packet) != 0) {
|
||||
goto errout;
|
||||
}
|
||||
} else {
|
||||
if (dns_cache_replace_inactive(&cache_key, ttl, speed, request->no_serve_expired, cache_packet) != 0) {
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* insert result to cache */
|
||||
if (dns_cache_insert(&cache_key, ttl, speed, request->no_serve_expired, cache_packet) != 0) {
|
||||
if (dns_cache_insert(&cache_key, ttl, speed, _dns_server_get_cache_timeout(request, ttl), cache_packet) != 0) {
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
@@ -1484,12 +1511,15 @@ static int _dns_cache_packet(struct dns_server_post_context *context)
|
||||
cache_key.query_flag = request->server_flags;
|
||||
|
||||
if (request->prefetch) {
|
||||
if (dns_cache_replace(&cache_key, context->reply_ttl, -1, request->no_serve_expired, cache_packet) != 0) {
|
||||
if (dns_cache_replace(&cache_key, context->reply_ttl, -1,
|
||||
_dns_server_get_cache_timeout(request, context->reply_ttl),
|
||||
!request->prefetch_expired_domain, cache_packet) != 0) {
|
||||
goto errout;
|
||||
}
|
||||
} else {
|
||||
/* insert result to cache */
|
||||
if (dns_cache_insert(&cache_key, context->reply_ttl, -1, request->no_serve_expired, cache_packet) != 0) {
|
||||
if (dns_cache_insert(&cache_key, context->reply_ttl, -1,
|
||||
_dns_server_get_cache_timeout(request, context->reply_ttl), cache_packet) != 0) {
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
@@ -3061,9 +3091,9 @@ static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_reque
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(ip, sizeof(ip), "[%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x]", paddr[0], paddr[1],
|
||||
paddr[2], paddr[3], paddr[4], paddr[5], paddr[6], paddr[7], paddr[8], paddr[9], paddr[10], paddr[11],
|
||||
paddr[12], paddr[13], paddr[14], paddr[15]);
|
||||
snprintf(ip, sizeof(ip), "[%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x]", paddr[0],
|
||||
paddr[1], paddr[2], paddr[3], paddr[4], paddr[5], paddr[6], paddr[7], paddr[8], paddr[9], paddr[10],
|
||||
paddr[11], paddr[12], paddr[13], paddr[14], paddr[15]);
|
||||
|
||||
/* start ping */
|
||||
_dns_server_request_get(request);
|
||||
@@ -6450,13 +6480,13 @@ static int _dns_server_second_ping_check(struct dns_request *request)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _dns_server_prefetch_domain(struct dns_cache *dns_cache)
|
||||
static int _dns_server_prefetch_domain(struct dns_cache *dns_cache)
|
||||
{
|
||||
/* If there are still hits, continue pre-fetching */
|
||||
struct dns_server_query_option server_query_option;
|
||||
int hitnum = dns_cache_hitnum_dec_get(dns_cache);
|
||||
if (hitnum <= 0) {
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* start prefetch domain */
|
||||
@@ -6467,11 +6497,19 @@ static void _dns_server_prefetch_domain(struct dns_cache *dns_cache)
|
||||
server_query_option.ecs_enable_flag = 0;
|
||||
if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, 0, &server_query_option) != 0) {
|
||||
tlog(TLOG_ERROR, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _dns_server_prefetch_expired_domain(struct dns_cache *dns_cache)
|
||||
static int _dns_server_prefetch_expired_domain(struct dns_cache *dns_cache)
|
||||
{
|
||||
time_t ttl = dns_cache->info.insert_time + dns_cache->info.ttl + dns_conf_serve_expired_ttl - time(NULL);
|
||||
if (ttl < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* start prefetch domain */
|
||||
tlog(TLOG_DEBUG, "expired domain, prefetch by cache %s, qtype %d, ttl %d", dns_cache->info.domain,
|
||||
dns_cache->info.qtype, dns_cache->info.ttl);
|
||||
@@ -6483,7 +6521,23 @@ static void _dns_server_prefetch_expired_domain(struct dns_cache *dns_cache)
|
||||
|
||||
if (_dns_server_prefetch_request(dns_cache->info.domain, dns_cache->info.qtype, 1, &server_query_option) != 0) {
|
||||
tlog(TLOG_DEBUG, "prefetch domain %s, qtype %d, failed.", dns_cache->info.domain, dns_cache->info.qtype);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dns_server_cache_expired(struct dns_cache *dns_cache)
|
||||
{
|
||||
if (dns_conf_prefetch == 1) {
|
||||
if (dns_conf_serve_expired == 1) {
|
||||
return _dns_server_prefetch_expired_domain(dns_cache);
|
||||
} else {
|
||||
return _dns_server_prefetch_domain(dns_cache);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void _dns_server_tcp_idle_check(void)
|
||||
@@ -6592,44 +6646,8 @@ static void _dns_server_save_cache_to_file(void)
|
||||
static void _dns_server_period_run_second(void)
|
||||
{
|
||||
static unsigned int sec = 0;
|
||||
static time_t last = 0;
|
||||
time_t now = 0;
|
||||
sec++;
|
||||
|
||||
time(&now);
|
||||
if (last == 0) {
|
||||
last = now;
|
||||
}
|
||||
|
||||
if (now - 180 > last) {
|
||||
dns_cache_invalidate(NULL, 0, 0, NULL, 0);
|
||||
tlog(TLOG_WARN, "Service paused for 180s, force invalidate cache.");
|
||||
}
|
||||
|
||||
last = now;
|
||||
|
||||
if (sec % 2 == 0) {
|
||||
if (dns_conf_prefetch) {
|
||||
/* do pre-fetching */
|
||||
if (dns_conf_serve_expired) {
|
||||
int prefetch_time = dns_conf_serve_expired_prefetch_time;
|
||||
|
||||
if (prefetch_time == 0) {
|
||||
prefetch_time = dns_conf_serve_expired_ttl / 2;
|
||||
if (prefetch_time == 0 || prefetch_time > EXPIRED_DOMAIN_PREFETCH_TIME) {
|
||||
prefetch_time = EXPIRED_DOMAIN_PREFETCH_TIME;
|
||||
}
|
||||
}
|
||||
dns_cache_invalidate(NULL, 0, DNS_MAX_DOMAIN_REFETCH_NUM, _dns_server_prefetch_expired_domain,
|
||||
prefetch_time);
|
||||
} else {
|
||||
dns_cache_invalidate(_dns_server_prefetch_domain, 3, DNS_MAX_DOMAIN_REFETCH_NUM, NULL, 0);
|
||||
}
|
||||
} else {
|
||||
dns_cache_invalidate(NULL, 0, 0, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
_dns_server_tcp_idle_check();
|
||||
_dns_server_check_need_exit();
|
||||
|
||||
@@ -7212,7 +7230,7 @@ static int _dns_server_audit_init(void)
|
||||
|
||||
static int _dns_server_cache_init(void)
|
||||
{
|
||||
if (dns_cache_init(dns_conf_cachesize, dns_conf_serve_expired, dns_conf_serve_expired_ttl) != 0) {
|
||||
if (dns_cache_init(dns_conf_cachesize, _dns_server_cache_expired) != 0) {
|
||||
tlog(TLOG_ERROR, "init cache failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
50
src/include/timer_wheel.h
Normal file
50
src/include/timer_wheel.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
|
||||
*
|
||||
* smartdns is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* smartdns is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __TIMER_WHEEL_H
|
||||
#define __TIMER_WHEEL_H
|
||||
|
||||
#include "list.h"
|
||||
|
||||
struct tw_base;
|
||||
struct tw_timer_list;
|
||||
|
||||
typedef void (*tw_func)(struct tw_timer_list *, void *, unsigned long);
|
||||
typedef void (*tw_del_func)(struct tw_timer_list *, void *);
|
||||
|
||||
struct tw_timer_list {
|
||||
void *data;
|
||||
unsigned long expires;
|
||||
tw_func function;
|
||||
tw_del_func del_function;
|
||||
struct list_head entry;
|
||||
};
|
||||
|
||||
struct tw_base *tw_init_timers(void);
|
||||
|
||||
int tw_cleanup_timers(struct tw_base *);
|
||||
|
||||
void tw_add_timer(struct tw_base *, struct tw_timer_list *);
|
||||
|
||||
int tw_del_timer(struct tw_base *, struct tw_timer_list *);
|
||||
|
||||
int tw_mod_timer_pending(struct tw_base *, struct tw_timer_list *, unsigned long);
|
||||
|
||||
int tw_mod_timer(struct tw_base *, struct tw_timer_list *, unsigned long);
|
||||
|
||||
#endif
|
||||
419
src/lib/timer_wheel.c
Normal file
419
src/lib/timer_wheel.c
Normal file
@@ -0,0 +1,419 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
|
||||
*
|
||||
* smartdns is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* smartdns is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "bitops.h"
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "timer_wheel.h"
|
||||
|
||||
#define TVR_BITS 8
|
||||
#define TVN_BITS 6
|
||||
#define TVR_SIZE (1 << TVR_BITS)
|
||||
#define TVN_SIZE (1 << TVN_BITS)
|
||||
#define TVR_MASK (TVR_SIZE - 1)
|
||||
#define TVN_MASK (TVN_SIZE - 1)
|
||||
#define INDEX(N) ((base->jiffies >> (TVR_BITS + N * TVN_BITS)) & TVN_MASK)
|
||||
|
||||
struct tvec {
|
||||
struct list_head vec[TVN_SIZE];
|
||||
};
|
||||
|
||||
struct tvec_root {
|
||||
struct list_head vec[TVR_SIZE];
|
||||
};
|
||||
|
||||
struct tw_base {
|
||||
pthread_spinlock_t lock;
|
||||
|
||||
pthread_t runner;
|
||||
|
||||
unsigned long jiffies;
|
||||
|
||||
struct tvec_root tv1;
|
||||
struct tvec tv2;
|
||||
struct tvec tv3;
|
||||
struct tvec tv4;
|
||||
struct tvec tv5;
|
||||
};
|
||||
|
||||
static inline void _tw_add_timer(struct tw_base *base, struct tw_timer_list *timer)
|
||||
{
|
||||
int i;
|
||||
unsigned long idx;
|
||||
unsigned long expires;
|
||||
struct list_head *vec;
|
||||
|
||||
expires = timer->expires;
|
||||
|
||||
idx = expires - base->jiffies;
|
||||
|
||||
if (idx < TVR_SIZE) {
|
||||
i = expires & TVR_MASK;
|
||||
vec = base->tv1.vec + i;
|
||||
} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
|
||||
i = (expires >> TVR_BITS) & TVN_MASK;
|
||||
vec = base->tv2.vec + i;
|
||||
} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
|
||||
i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
|
||||
vec = base->tv3.vec + i;
|
||||
} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
|
||||
i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
|
||||
vec = base->tv4.vec + i;
|
||||
} else if ((long)idx < 0) {
|
||||
vec = base->tv1.vec + (base->jiffies & TVR_MASK);
|
||||
} else {
|
||||
i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
|
||||
vec = base->tv5.vec + i;
|
||||
}
|
||||
|
||||
list_add_tail(&timer->entry, vec);
|
||||
}
|
||||
|
||||
static inline unsigned long _apply_slack(struct tw_base *base, struct tw_timer_list *timer)
|
||||
{
|
||||
long delta;
|
||||
unsigned long mask, expires, expires_limit;
|
||||
|
||||
expires = timer->expires;
|
||||
|
||||
delta = expires - base->jiffies;
|
||||
if (delta < 256) {
|
||||
return expires;
|
||||
}
|
||||
|
||||
expires_limit = expires + delta / 256;
|
||||
mask = expires ^ expires_limit;
|
||||
if (mask == 0) {
|
||||
return expires;
|
||||
}
|
||||
|
||||
int bit = fls_long(mask);
|
||||
mask = (1UL << bit) - 1;
|
||||
|
||||
expires_limit = expires_limit & ~(mask);
|
||||
return expires_limit;
|
||||
}
|
||||
|
||||
static inline void _tw_detach_timer(struct tw_timer_list *timer)
|
||||
{
|
||||
struct list_head *entry = &timer->entry;
|
||||
|
||||
list_del(entry);
|
||||
entry->next = NULL;
|
||||
}
|
||||
|
||||
static inline int _tw_cascade(struct tw_base *base, struct tvec *tv, int index)
|
||||
{
|
||||
struct tw_timer_list *timer, *tmp;
|
||||
struct list_head tv_list;
|
||||
|
||||
list_replace_init(tv->vec + index, &tv_list);
|
||||
|
||||
list_for_each_entry_safe(timer, tmp, &tv_list, entry)
|
||||
{
|
||||
_tw_add_timer(base, timer);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static inline int timer_pending(struct tw_timer_list *timer)
|
||||
{
|
||||
struct list_head *entry = &timer->entry;
|
||||
|
||||
return (entry->next != NULL);
|
||||
}
|
||||
|
||||
static inline int __detach_if_pending(struct tw_timer_list *timer)
|
||||
{
|
||||
if (!timer_pending(timer)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
_tw_detach_timer(timer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int __mod_timer(struct tw_base *base, struct tw_timer_list *timer, int pending_only)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = __detach_if_pending(timer);
|
||||
if (!ret && pending_only) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
_tw_add_timer(base, timer);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void tw_add_timer(struct tw_base *base, struct tw_timer_list *timer)
|
||||
{
|
||||
if (timer->function == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_spin_lock(&base->lock);
|
||||
{
|
||||
timer->expires += base->jiffies;
|
||||
timer->expires = _apply_slack(base, timer);
|
||||
_tw_add_timer(base, timer);
|
||||
}
|
||||
pthread_spin_unlock(&base->lock);
|
||||
}
|
||||
|
||||
int tw_del_timer(struct tw_base *base, struct tw_timer_list *timer)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pthread_spin_lock(&base->lock);
|
||||
{
|
||||
if (timer_pending(timer)) {
|
||||
ret = 1;
|
||||
_tw_detach_timer(timer);
|
||||
}
|
||||
}
|
||||
pthread_spin_unlock(&base->lock);
|
||||
|
||||
if (ret == 1 && timer->del_function) {
|
||||
timer->del_function(timer, timer->data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tw_mod_timer_pending(struct tw_base *base, struct tw_timer_list *timer, unsigned long expires)
|
||||
{
|
||||
int ret = 1;
|
||||
|
||||
pthread_spin_lock(&base->lock);
|
||||
{
|
||||
timer->expires = expires + base->jiffies;
|
||||
timer->expires = _apply_slack(base, timer);
|
||||
|
||||
ret = __mod_timer(base, timer, 1);
|
||||
}
|
||||
pthread_spin_unlock(&base->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tw_mod_timer(struct tw_base *base, struct tw_timer_list *timer, unsigned long expires)
|
||||
{
|
||||
int ret = 1;
|
||||
|
||||
pthread_spin_lock(&base->lock);
|
||||
{
|
||||
if (timer_pending(timer) && timer->expires == expires) {
|
||||
goto unblock;
|
||||
}
|
||||
|
||||
timer->expires = expires + base->jiffies;
|
||||
timer->expires = _apply_slack(base, timer);
|
||||
|
||||
ret = __mod_timer(base, timer, 0);
|
||||
}
|
||||
unblock:
|
||||
pthread_spin_unlock(&base->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tw_cleanup_timers(struct tw_base *base)
|
||||
{
|
||||
int ret = 0;
|
||||
void *res = NULL;
|
||||
|
||||
ret = pthread_cancel(base->runner);
|
||||
if (ret != 0) {
|
||||
goto errout;
|
||||
}
|
||||
ret = pthread_join(base->runner, &res);
|
||||
if (ret != 0) {
|
||||
goto errout;
|
||||
}
|
||||
if (res != PTHREAD_CANCELED) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
ret = pthread_spin_destroy(&base->lock);
|
||||
if (ret != 0) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
free(base);
|
||||
return 0;
|
||||
|
||||
errout:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void run_timers(struct tw_base *base)
|
||||
{
|
||||
unsigned long index, call_time;
|
||||
struct tw_timer_list *timer;
|
||||
|
||||
struct list_head work_list;
|
||||
struct list_head *head = &work_list;
|
||||
|
||||
pthread_spin_lock(&base->lock);
|
||||
{
|
||||
index = base->jiffies & TVR_MASK;
|
||||
|
||||
if (!index && (!_tw_cascade(base, &base->tv2, INDEX(0))) && (!_tw_cascade(base, &base->tv3, INDEX(1))) &&
|
||||
(!_tw_cascade(base, &base->tv4, INDEX(2))))
|
||||
_tw_cascade(base, &base->tv5, INDEX(3));
|
||||
|
||||
call_time = base->jiffies++;
|
||||
list_replace_init(base->tv1.vec + index, head);
|
||||
while (!list_empty(head)) {
|
||||
tw_func fn;
|
||||
void *data;
|
||||
|
||||
timer = list_first_entry(head, struct tw_timer_list, entry);
|
||||
fn = timer->function;
|
||||
data = timer->data;
|
||||
|
||||
_tw_detach_timer(timer);
|
||||
pthread_spin_unlock(&base->lock);
|
||||
{
|
||||
fn(timer, data, call_time);
|
||||
}
|
||||
|
||||
pthread_spin_lock(&base->lock);
|
||||
if ( (timer_pending(timer) == 0 && timer->del_function) ) {
|
||||
pthread_spin_unlock(&base->lock);
|
||||
timer->del_function(timer, timer->data);
|
||||
pthread_spin_lock(&base->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_spin_unlock(&base->lock);
|
||||
}
|
||||
|
||||
static unsigned long _tw_tick_count(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
|
||||
}
|
||||
|
||||
static void *timer_work(void *arg)
|
||||
{
|
||||
struct tw_base *base = arg;
|
||||
int sleep = 1000;
|
||||
int sleep_time = 0;
|
||||
unsigned long now = {0};
|
||||
unsigned long last = {0};
|
||||
unsigned long expect_time = 0;
|
||||
|
||||
sleep_time = sleep;
|
||||
now = _tw_tick_count() - sleep;
|
||||
last = now;
|
||||
expect_time = now + sleep;
|
||||
while (1) {
|
||||
run_timers(base);
|
||||
|
||||
now = _tw_tick_count();
|
||||
if (sleep_time > 0) {
|
||||
sleep_time -= now - last;
|
||||
if (sleep_time <= 0) {
|
||||
sleep_time = 0;
|
||||
}
|
||||
|
||||
int cnt = sleep_time / sleep;
|
||||
expect_time -= cnt * sleep;
|
||||
sleep_time -= cnt * sleep;
|
||||
}
|
||||
|
||||
if (now >= expect_time) {
|
||||
sleep_time = sleep - (now - expect_time);
|
||||
if (sleep_time < 0) {
|
||||
sleep_time = 0;
|
||||
expect_time = now;
|
||||
}
|
||||
expect_time += sleep;
|
||||
}
|
||||
last = now;
|
||||
|
||||
usleep(sleep_time * 1000);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct tw_base *tw_init_timers(void)
|
||||
{
|
||||
int j = 0;
|
||||
int ret = 0;
|
||||
struct timeval tv = {
|
||||
0,
|
||||
};
|
||||
struct tw_base *base = NULL;
|
||||
|
||||
base = malloc(sizeof(*base));
|
||||
if (!base) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
ret = pthread_spin_init(&base->lock, 0);
|
||||
if (ret != 0) {
|
||||
goto errout2;
|
||||
}
|
||||
|
||||
for (j = 0; j < TVN_SIZE; j++) {
|
||||
INIT_LIST_HEAD(base->tv5.vec + j);
|
||||
INIT_LIST_HEAD(base->tv4.vec + j);
|
||||
INIT_LIST_HEAD(base->tv3.vec + j);
|
||||
INIT_LIST_HEAD(base->tv2.vec + j);
|
||||
}
|
||||
|
||||
for (j = 0; j < TVR_SIZE; j++) {
|
||||
INIT_LIST_HEAD(base->tv1.vec + j);
|
||||
}
|
||||
|
||||
ret = gettimeofday(&tv, 0);
|
||||
if (ret < 0) {
|
||||
goto errout1;
|
||||
}
|
||||
base->jiffies = tv.tv_sec;
|
||||
|
||||
ret = pthread_create(&base->runner, NULL, timer_work, base);
|
||||
if (ret != 0) {
|
||||
goto errout1;
|
||||
}
|
||||
return base;
|
||||
|
||||
errout1:
|
||||
(void)pthread_spin_destroy(&base->lock);
|
||||
errout2:
|
||||
free(base);
|
||||
errout:
|
||||
return NULL;
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "rbtree.h"
|
||||
#include "tlog.h"
|
||||
#include "util.h"
|
||||
#include "timer.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
@@ -486,6 +487,11 @@ static int _smartdns_init(void)
|
||||
tlog(TLOG_NOTICE, "smartdns starting...(Copyright (C) Nick Peng <pymumu@gmail.com>, build: %s %s)", __DATE__,
|
||||
__TIME__);
|
||||
|
||||
if (dns_timer_init() != 0) {
|
||||
tlog(TLOG_ERROR, "init timer failed.");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (_smartdns_init_ssl() != 0) {
|
||||
tlog(TLOG_ERROR, "init ssl failed.");
|
||||
goto errout;
|
||||
@@ -573,6 +579,7 @@ static void _smartdns_exit(void)
|
||||
fast_ping_exit();
|
||||
dns_server_exit();
|
||||
_smartdns_destroy_ssl();
|
||||
dns_timer_destroy();
|
||||
tlog_exit();
|
||||
dns_server_load_exit();
|
||||
}
|
||||
|
||||
70
src/timer.c
Normal file
70
src/timer.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
|
||||
*
|
||||
* smartdns is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* smartdns is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "timer.h"
|
||||
#include "timer_wheel.h"
|
||||
|
||||
static struct tw_base *dns_timer_base = NULL;
|
||||
|
||||
int dns_timer_init(void)
|
||||
{
|
||||
struct tw_base *tw = tw_init_timers();
|
||||
if (tw == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dns_timer_base = tw;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dns_timer_destroy(void)
|
||||
{
|
||||
if (dns_timer_base != NULL) {
|
||||
tw_cleanup_timers(dns_timer_base);
|
||||
dns_timer_base = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void dns_timer_add(struct tw_timer_list *timer)
|
||||
{
|
||||
if (dns_timer_base == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
tw_add_timer(dns_timer_base, timer);
|
||||
}
|
||||
|
||||
int dns_timer_del(struct tw_timer_list *timer)
|
||||
{
|
||||
if (dns_timer_base == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tw_del_timer(dns_timer_base, timer);
|
||||
}
|
||||
|
||||
int dns_timer_mod(struct tw_timer_list *timer, unsigned long expires)
|
||||
{
|
||||
if (dns_timer_base == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tw_mod_timer(dns_timer_base, timer, expires);
|
||||
}
|
||||
|
||||
41
src/timer.h
Normal file
41
src/timer.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
|
||||
*
|
||||
* smartdns is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* smartdns is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SMART_DNS_TIMER_H
|
||||
#define SMART_DNS_TIMER_H
|
||||
|
||||
#include "timer_wheel.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /*__cplusplus */
|
||||
|
||||
int dns_timer_init(void);
|
||||
|
||||
void dns_timer_add(struct tw_timer_list *timer);
|
||||
|
||||
int dns_timer_del(struct tw_timer_list *timer);
|
||||
|
||||
int dns_timer_mod(struct tw_timer_list *timer, unsigned long expires);
|
||||
|
||||
void dns_timer_destroy(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus */
|
||||
#endif
|
||||
@@ -23,8 +23,8 @@ CXXFLAGS += -g
|
||||
CXXFLAGS += -DTEST
|
||||
CXXFLAGS += -I./ -I../src -I../src/include
|
||||
|
||||
SMARTDNS_OBJS = lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o lib/nftset.o
|
||||
SMARTDNS_OBJS += smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o
|
||||
SMARTDNS_OBJS = lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o lib/nftset.o lib/timer_wheel.o
|
||||
SMARTDNS_OBJS += smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o proxy.o timer.o
|
||||
OBJS = $(addprefix ../src/, $(SMARTDNS_OBJS))
|
||||
|
||||
TEST_SOURCES := $(wildcard *.cc) $(wildcard */*.cc) $(wildcard */*/*.cc)
|
||||
|
||||
Reference in New Issue
Block a user