290 lines
8.8 KiB
C
290 lines
8.8 KiB
C
/* si_cache.c
|
|
*
|
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
|
*
|
|
* This program 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 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <talloc.h>
|
|
#include <inttypes.h>
|
|
|
|
#include <osmocom/core/utils.h>
|
|
|
|
#include <neigh_cache.h>
|
|
#include <gprs_debug.h>
|
|
|
|
static void neigh_cache_schedule_cleanup(struct neigh_cache *cache);
|
|
static void neigh_cache_cleanup_cb(void *data)
|
|
{
|
|
struct timespec now, threshold;
|
|
struct neigh_cache *cache = (struct neigh_cache *)data;
|
|
struct neigh_cache_entry *it, *tmp;
|
|
|
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
/* Instead of adding keep_time_intval to each, substract it from now once */
|
|
timespecsub(&now, &cache->keep_time_intval, &threshold);
|
|
|
|
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
|
if (timespeccmp(&threshold, &it->update_ts, <))
|
|
break;
|
|
LOGP(DNACC, LOGL_DEBUG,
|
|
"neigh_cache: Removing entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
|
|
NEIGH_CACHE_ENTRY_KEY_ARGS(&it->key), osmo_cgi_ps_name(&it->value));
|
|
llist_del(&it->list);
|
|
talloc_free(it);
|
|
}
|
|
|
|
neigh_cache_schedule_cleanup(cache);
|
|
}
|
|
|
|
static void neigh_cache_schedule_cleanup(struct neigh_cache *cache)
|
|
{
|
|
struct neigh_cache_entry *it;
|
|
struct timespec now, threshold, result;
|
|
|
|
/* First item is the one with oldest update_ts */
|
|
it = llist_first_entry_or_null(&cache->list, struct neigh_cache_entry, list);
|
|
if (!it)
|
|
return;
|
|
|
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
|
|
|
|
if (timespeccmp(&now, &threshold, >=)) {
|
|
/* Too late, let's flush asynchonously so newly added isn't
|
|
* immediatelly freed before return. */
|
|
result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
|
|
} else {
|
|
timespecsub(&threshold, &now, &result);
|
|
}
|
|
osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
|
|
}
|
|
|
|
struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec)
|
|
{
|
|
struct neigh_cache *cache = talloc_zero(ctx, struct neigh_cache);
|
|
OSMO_ASSERT(cache);
|
|
INIT_LLIST_HEAD(&cache->list);
|
|
osmo_timer_setup(&cache->cleanup_timer, neigh_cache_cleanup_cb, cache);
|
|
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
|
return cache;
|
|
|
|
}
|
|
|
|
void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int keep_time_sec)
|
|
{
|
|
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
|
neigh_cache_schedule_cleanup(cache);
|
|
}
|
|
|
|
struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
|
|
const struct neigh_cache_entry_key *key,
|
|
const struct osmo_cell_global_id_ps *value)
|
|
{
|
|
struct neigh_cache_entry *it;
|
|
|
|
/* First check if it already exists. If so, simply update timer+value */
|
|
it = neigh_cache_lookup_entry(cache, key);
|
|
if (!it) {
|
|
LOGP(DNACC, LOGL_DEBUG,
|
|
"neigh_cache: Inserting new entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
|
|
NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(value));
|
|
it = talloc_zero(cache, struct neigh_cache_entry);
|
|
OSMO_ASSERT(it);
|
|
memcpy(&it->key, key, sizeof(it->key));
|
|
} else {
|
|
LOGP(DNACC, LOGL_DEBUG,
|
|
"neigh_cache: Updating entry " NEIGH_CACHE_ENTRY_KEY_FMT " => (%s -> %s)\n",
|
|
NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(&it->value), osmo_cgi_ps_name2(value));
|
|
/* remove item, we'll add it to the end to have them sorted by last update */
|
|
llist_del(&it->list);
|
|
}
|
|
|
|
memcpy(&it->value, value, sizeof(it->value));
|
|
OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
|
|
llist_add_tail(&it->list, &cache->list);
|
|
neigh_cache_schedule_cleanup(cache);
|
|
return it;
|
|
}
|
|
|
|
struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
|
|
const struct neigh_cache_entry_key *key)
|
|
{
|
|
struct neigh_cache_entry *tmp;
|
|
llist_for_each_entry(tmp, &cache->list, list) {
|
|
if (neigh_cache_entry_key_eq(&tmp->key, key))
|
|
return tmp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
|
|
const struct neigh_cache_entry_key *key)
|
|
{
|
|
struct neigh_cache_entry *it = neigh_cache_lookup_entry(cache, key);
|
|
if (it)
|
|
return &it->value;
|
|
return NULL;
|
|
}
|
|
|
|
void neigh_cache_free(struct neigh_cache *cache)
|
|
{
|
|
struct neigh_cache_entry *it, *tmp;
|
|
if (!cache)
|
|
return;
|
|
|
|
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
|
llist_del(&it->list);
|
|
talloc_free(it);
|
|
}
|
|
osmo_timer_del(&cache->cleanup_timer);
|
|
talloc_free(cache);
|
|
}
|
|
|
|
|
|
////////////////////
|
|
// SI CACHE
|
|
///////////////////
|
|
|
|
static void si_cache_schedule_cleanup(struct si_cache *cache);
|
|
static void si_cache_cleanup_cb(void *data)
|
|
{
|
|
struct timespec now, threshold;
|
|
struct si_cache *cache = (struct si_cache *)data;
|
|
struct si_cache_entry *it, *tmp;
|
|
|
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
/* Instead of adding keep_time_intval to each, substract it from now once */
|
|
timespecsub(&now, &cache->keep_time_intval, &threshold);
|
|
|
|
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
|
if (timespeccmp(&threshold, &it->update_ts, <))
|
|
break;
|
|
LOGP(DNACC, LOGL_DEBUG, "si_cache: Removing entry %s\n",
|
|
osmo_cgi_ps_name(&it->key));
|
|
llist_del(&it->list);
|
|
talloc_free(it);
|
|
}
|
|
|
|
si_cache_schedule_cleanup(cache);
|
|
}
|
|
|
|
static void si_cache_schedule_cleanup(struct si_cache *cache)
|
|
{
|
|
struct si_cache_entry *it;
|
|
struct timespec now, threshold, result;
|
|
|
|
/* First item is the one with oldest update_ts */
|
|
it = llist_first_entry_or_null(&cache->list, struct si_cache_entry, list);
|
|
if (!it)
|
|
return;
|
|
|
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
|
|
|
|
if (timespeccmp(&now, &threshold, >=)) {
|
|
/* Too late, let's flush asynchonously so newly added isn't
|
|
* immediatelly freed before return. */
|
|
result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
|
|
} else {
|
|
timespecsub(&threshold, &now, &result);
|
|
}
|
|
osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
|
|
}
|
|
|
|
struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec)
|
|
{
|
|
struct si_cache *cache = talloc_zero(ctx, struct si_cache);
|
|
OSMO_ASSERT(cache);
|
|
INIT_LLIST_HEAD(&cache->list);
|
|
osmo_timer_setup(&cache->cleanup_timer, si_cache_cleanup_cb, cache);
|
|
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
|
return cache;
|
|
}
|
|
|
|
void si_cache_set_keep_time_interval(struct si_cache *cache, unsigned int keep_time_sec)
|
|
{
|
|
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
|
si_cache_schedule_cleanup(cache);
|
|
}
|
|
|
|
struct si_cache_entry *si_cache_add(struct si_cache *cache,
|
|
const struct osmo_cell_global_id_ps *key,
|
|
const struct si_cache_value *value)
|
|
{
|
|
struct si_cache_entry *it;
|
|
|
|
/* First check if it already exists. If so, simply update timer+value */
|
|
it = si_cache_lookup_entry(cache, key);
|
|
if (!it) {
|
|
LOGP(DNACC, LOGL_DEBUG, "si_cache: Inserting new entry %s\n",
|
|
osmo_cgi_ps_name(key));
|
|
it = talloc_zero(cache, struct si_cache_entry);
|
|
OSMO_ASSERT(it);
|
|
memcpy(&it->key, key, sizeof(it->key));
|
|
} else {
|
|
LOGP(DNACC, LOGL_DEBUG, "si_cache: Updating entry %s\n",
|
|
osmo_cgi_ps_name(&it->key));
|
|
/* remove item, we'll add it to the end to have them sorted by last update */
|
|
llist_del(&it->list);
|
|
}
|
|
|
|
memcpy(&it->value, value, sizeof(it->value));
|
|
OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
|
|
llist_add_tail(&it->list, &cache->list);
|
|
si_cache_schedule_cleanup(cache);
|
|
return it;
|
|
}
|
|
|
|
struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,
|
|
const struct osmo_cell_global_id_ps *key)
|
|
{
|
|
struct si_cache_entry *tmp;
|
|
llist_for_each_entry(tmp, &cache->list, list) {
|
|
if (osmo_cgi_ps_cmp(&tmp->key, key) == 0)
|
|
return tmp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,
|
|
const struct osmo_cell_global_id_ps *key)
|
|
{
|
|
struct si_cache_entry *it = si_cache_lookup_entry(cache, key);
|
|
if (it)
|
|
return &it->value;
|
|
return NULL;
|
|
}
|
|
|
|
void si_cache_free(struct si_cache *cache)
|
|
{
|
|
struct si_cache_entry *it, *tmp;
|
|
if (!cache)
|
|
return;
|
|
|
|
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
|
llist_del(&it->list);
|
|
talloc_free(it);
|
|
}
|
|
osmo_timer_del(&cache->cleanup_timer);
|
|
talloc_free(cache);
|
|
}
|