blob: 28e27e6510a1e732672c7d05a0dd51261b6ec857 [file] [log] [blame]
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001/* si_cache.c
2 *
3 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
4 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010015 */
16
17#include <string.h>
18#include <talloc.h>
19#include <inttypes.h>
20
21#include <osmocom/core/utils.h>
22
23#include <neigh_cache.h>
24#include <gprs_debug.h>
25
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010026static void neigh_cache_schedule_cleanup(struct neigh_cache *cache);
27static void neigh_cache_cleanup_cb(void *data)
28{
29 struct timespec now, threshold;
30 struct neigh_cache *cache = (struct neigh_cache *)data;
31 struct neigh_cache_entry *it, *tmp;
32
33 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
34
35 /* Instead of adding keep_time_intval to each, substract it from now once */
36 timespecsub(&now, &cache->keep_time_intval, &threshold);
37
38 llist_for_each_entry_safe(it, tmp, &cache->list, list) {
39 if (timespeccmp(&threshold, &it->update_ts, <))
40 break;
41 LOGP(DNACC, LOGL_DEBUG,
42 "neigh_cache: Removing entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
43 NEIGH_CACHE_ENTRY_KEY_ARGS(&it->key), osmo_cgi_ps_name(&it->value));
44 llist_del(&it->list);
45 talloc_free(it);
46 }
47
48 neigh_cache_schedule_cleanup(cache);
49}
50
51static void neigh_cache_schedule_cleanup(struct neigh_cache *cache)
52{
53 struct neigh_cache_entry *it;
54 struct timespec now, threshold, result;
55
56 /* First item is the one with oldest update_ts */
57 it = llist_first_entry_or_null(&cache->list, struct neigh_cache_entry, list);
58 if (!it)
59 return;
60
61 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
62
63 timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
64
65 if (timespeccmp(&now, &threshold, >=)) {
66 /* Too late, let's flush asynchonously so newly added isn't
67 * immediatelly freed before return. */
68 result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
69 } else {
70 timespecsub(&threshold, &now, &result);
71 }
Vadim Yanitskiy0eaa3d32022-07-20 00:11:40 +070072 osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec / 1000);
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010073}
74
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +010075struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010076{
77 struct neigh_cache *cache = talloc_zero(ctx, struct neigh_cache);
78 OSMO_ASSERT(cache);
79 INIT_LLIST_HEAD(&cache->list);
80 osmo_timer_setup(&cache->cleanup_timer, neigh_cache_cleanup_cb, cache);
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +010081 cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010082 return cache;
83
84}
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +010085
86void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int keep_time_sec)
87{
88 cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
89 neigh_cache_schedule_cleanup(cache);
90}
91
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010092struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
93 const struct neigh_cache_entry_key *key,
94 const struct osmo_cell_global_id_ps *value)
95{
96 struct neigh_cache_entry *it;
97
98 /* First check if it already exists. If so, simply update timer+value */
99 it = neigh_cache_lookup_entry(cache, key);
100 if (!it) {
101 LOGP(DNACC, LOGL_DEBUG,
102 "neigh_cache: Inserting new entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
103 NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(value));
104 it = talloc_zero(cache, struct neigh_cache_entry);
105 OSMO_ASSERT(it);
106 memcpy(&it->key, key, sizeof(it->key));
107 } else {
108 LOGP(DNACC, LOGL_DEBUG,
109 "neigh_cache: Updating entry " NEIGH_CACHE_ENTRY_KEY_FMT " => (%s -> %s)\n",
110 NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(&it->value), osmo_cgi_ps_name2(value));
111 /* remove item, we'll add it to the end to have them sorted by last update */
112 llist_del(&it->list);
113 }
114
115 memcpy(&it->value, value, sizeof(it->value));
116 OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
117 llist_add_tail(&it->list, &cache->list);
118 neigh_cache_schedule_cleanup(cache);
119 return it;
120}
121
122struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
123 const struct neigh_cache_entry_key *key)
124{
125 struct neigh_cache_entry *tmp;
126 llist_for_each_entry(tmp, &cache->list, list) {
127 if (neigh_cache_entry_key_eq(&tmp->key, key))
128 return tmp;
129 }
130 return NULL;
131}
132
133const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
134 const struct neigh_cache_entry_key *key)
135{
136 struct neigh_cache_entry *it = neigh_cache_lookup_entry(cache, key);
137 if (it)
138 return &it->value;
139 return NULL;
140}
141
142void neigh_cache_free(struct neigh_cache *cache)
143{
144 struct neigh_cache_entry *it, *tmp;
145 if (!cache)
146 return;
147
148 llist_for_each_entry_safe(it, tmp, &cache->list, list) {
149 llist_del(&it->list);
150 talloc_free(it);
151 }
152 osmo_timer_del(&cache->cleanup_timer);
153 talloc_free(cache);
154}
155
156
157////////////////////
158// SI CACHE
159///////////////////
160
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100161static void si_cache_schedule_cleanup(struct si_cache *cache);
162static void si_cache_cleanup_cb(void *data)
163{
164 struct timespec now, threshold;
165 struct si_cache *cache = (struct si_cache *)data;
166 struct si_cache_entry *it, *tmp;
167
168 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
169
170 /* Instead of adding keep_time_intval to each, substract it from now once */
171 timespecsub(&now, &cache->keep_time_intval, &threshold);
172
173 llist_for_each_entry_safe(it, tmp, &cache->list, list) {
174 if (timespeccmp(&threshold, &it->update_ts, <))
175 break;
176 LOGP(DNACC, LOGL_DEBUG, "si_cache: Removing entry %s\n",
177 osmo_cgi_ps_name(&it->key));
178 llist_del(&it->list);
179 talloc_free(it);
180 }
181
182 si_cache_schedule_cleanup(cache);
183}
184
185static void si_cache_schedule_cleanup(struct si_cache *cache)
186{
187 struct si_cache_entry *it;
188 struct timespec now, threshold, result;
189
190 /* First item is the one with oldest update_ts */
191 it = llist_first_entry_or_null(&cache->list, struct si_cache_entry, list);
192 if (!it)
193 return;
194
195 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
196
197 timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
198
199 if (timespeccmp(&now, &threshold, >=)) {
200 /* Too late, let's flush asynchonously so newly added isn't
201 * immediatelly freed before return. */
202 result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
203 } else {
204 timespecsub(&threshold, &now, &result);
205 }
Vadim Yanitskiy0eaa3d32022-07-20 00:11:40 +0700206 osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec / 1000);
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100207}
208
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +0100209struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100210{
211 struct si_cache *cache = talloc_zero(ctx, struct si_cache);
212 OSMO_ASSERT(cache);
213 INIT_LLIST_HEAD(&cache->list);
214 osmo_timer_setup(&cache->cleanup_timer, si_cache_cleanup_cb, cache);
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +0100215 cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100216 return cache;
217}
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +0100218
219void si_cache_set_keep_time_interval(struct si_cache *cache, unsigned int keep_time_sec)
220{
221 cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
222 si_cache_schedule_cleanup(cache);
223}
224
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100225struct si_cache_entry *si_cache_add(struct si_cache *cache,
226 const struct osmo_cell_global_id_ps *key,
227 const struct si_cache_value *value)
228{
229 struct si_cache_entry *it;
230
231 /* First check if it already exists. If so, simply update timer+value */
232 it = si_cache_lookup_entry(cache, key);
233 if (!it) {
234 LOGP(DNACC, LOGL_DEBUG, "si_cache: Inserting new entry %s\n",
235 osmo_cgi_ps_name(key));
236 it = talloc_zero(cache, struct si_cache_entry);
237 OSMO_ASSERT(it);
238 memcpy(&it->key, key, sizeof(it->key));
239 } else {
240 LOGP(DNACC, LOGL_DEBUG, "si_cache: Updating entry %s\n",
241 osmo_cgi_ps_name(&it->key));
242 /* remove item, we'll add it to the end to have them sorted by last update */
243 llist_del(&it->list);
244 }
245
246 memcpy(&it->value, value, sizeof(it->value));
247 OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
248 llist_add_tail(&it->list, &cache->list);
249 si_cache_schedule_cleanup(cache);
250 return it;
251}
252
253struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,
254 const struct osmo_cell_global_id_ps *key)
255{
256 struct si_cache_entry *tmp;
257 llist_for_each_entry(tmp, &cache->list, list) {
258 if (osmo_cgi_ps_cmp(&tmp->key, key) == 0)
259 return tmp;
260 }
261 return NULL;
262}
263
264const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,
265 const struct osmo_cell_global_id_ps *key)
266{
267 struct si_cache_entry *it = si_cache_lookup_entry(cache, key);
268 if (it)
269 return &it->value;
270 return NULL;
271}
272
273void si_cache_free(struct si_cache *cache)
274{
275 struct si_cache_entry *it, *tmp;
276 if (!cache)
277 return;
278
279 llist_for_each_entry_safe(it, tmp, &cache->list, list) {
280 llist_del(&it->list);
281 talloc_free(it);
282 }
283 osmo_timer_del(&cache->cleanup_timer);
284 talloc_free(cache);
285}