blob: 3217f40855172e8a8fe8e300d485d296b454c524 [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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#include <string.h>
22#include <talloc.h>
23#include <inttypes.h>
24
25#include <osmocom/core/utils.h>
26
27#include <neigh_cache.h>
28#include <gprs_debug.h>
29
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010030static void neigh_cache_schedule_cleanup(struct neigh_cache *cache);
31static void neigh_cache_cleanup_cb(void *data)
32{
33 struct timespec now, threshold;
34 struct neigh_cache *cache = (struct neigh_cache *)data;
35 struct neigh_cache_entry *it, *tmp;
36
37 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
38
39 /* Instead of adding keep_time_intval to each, substract it from now once */
40 timespecsub(&now, &cache->keep_time_intval, &threshold);
41
42 llist_for_each_entry_safe(it, tmp, &cache->list, list) {
43 if (timespeccmp(&threshold, &it->update_ts, <))
44 break;
45 LOGP(DNACC, LOGL_DEBUG,
46 "neigh_cache: Removing entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
47 NEIGH_CACHE_ENTRY_KEY_ARGS(&it->key), osmo_cgi_ps_name(&it->value));
48 llist_del(&it->list);
49 talloc_free(it);
50 }
51
52 neigh_cache_schedule_cleanup(cache);
53}
54
55static void neigh_cache_schedule_cleanup(struct neigh_cache *cache)
56{
57 struct neigh_cache_entry *it;
58 struct timespec now, threshold, result;
59
60 /* First item is the one with oldest update_ts */
61 it = llist_first_entry_or_null(&cache->list, struct neigh_cache_entry, list);
62 if (!it)
63 return;
64
65 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
66
67 timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
68
69 if (timespeccmp(&now, &threshold, >=)) {
70 /* Too late, let's flush asynchonously so newly added isn't
71 * immediatelly freed before return. */
72 result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
73 } else {
74 timespecsub(&threshold, &now, &result);
75 }
76 osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
77}
78
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +010079struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010080{
81 struct neigh_cache *cache = talloc_zero(ctx, struct neigh_cache);
82 OSMO_ASSERT(cache);
83 INIT_LLIST_HEAD(&cache->list);
84 osmo_timer_setup(&cache->cleanup_timer, neigh_cache_cleanup_cb, cache);
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +010085 cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010086 return cache;
87
88}
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +010089
90void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int keep_time_sec)
91{
92 cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
93 neigh_cache_schedule_cleanup(cache);
94}
95
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010096struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
97 const struct neigh_cache_entry_key *key,
98 const struct osmo_cell_global_id_ps *value)
99{
100 struct neigh_cache_entry *it;
101
102 /* First check if it already exists. If so, simply update timer+value */
103 it = neigh_cache_lookup_entry(cache, key);
104 if (!it) {
105 LOGP(DNACC, LOGL_DEBUG,
106 "neigh_cache: Inserting new entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
107 NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(value));
108 it = talloc_zero(cache, struct neigh_cache_entry);
109 OSMO_ASSERT(it);
110 memcpy(&it->key, key, sizeof(it->key));
111 } else {
112 LOGP(DNACC, LOGL_DEBUG,
113 "neigh_cache: Updating entry " NEIGH_CACHE_ENTRY_KEY_FMT " => (%s -> %s)\n",
114 NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(&it->value), osmo_cgi_ps_name2(value));
115 /* remove item, we'll add it to the end to have them sorted by last update */
116 llist_del(&it->list);
117 }
118
119 memcpy(&it->value, value, sizeof(it->value));
120 OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
121 llist_add_tail(&it->list, &cache->list);
122 neigh_cache_schedule_cleanup(cache);
123 return it;
124}
125
126struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
127 const struct neigh_cache_entry_key *key)
128{
129 struct neigh_cache_entry *tmp;
130 llist_for_each_entry(tmp, &cache->list, list) {
131 if (neigh_cache_entry_key_eq(&tmp->key, key))
132 return tmp;
133 }
134 return NULL;
135}
136
137const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
138 const struct neigh_cache_entry_key *key)
139{
140 struct neigh_cache_entry *it = neigh_cache_lookup_entry(cache, key);
141 if (it)
142 return &it->value;
143 return NULL;
144}
145
146void neigh_cache_free(struct neigh_cache *cache)
147{
148 struct neigh_cache_entry *it, *tmp;
149 if (!cache)
150 return;
151
152 llist_for_each_entry_safe(it, tmp, &cache->list, list) {
153 llist_del(&it->list);
154 talloc_free(it);
155 }
156 osmo_timer_del(&cache->cleanup_timer);
157 talloc_free(cache);
158}
159
160
161////////////////////
162// SI CACHE
163///////////////////
164
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100165static void si_cache_schedule_cleanup(struct si_cache *cache);
166static void si_cache_cleanup_cb(void *data)
167{
168 struct timespec now, threshold;
169 struct si_cache *cache = (struct si_cache *)data;
170 struct si_cache_entry *it, *tmp;
171
172 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
173
174 /* Instead of adding keep_time_intval to each, substract it from now once */
175 timespecsub(&now, &cache->keep_time_intval, &threshold);
176
177 llist_for_each_entry_safe(it, tmp, &cache->list, list) {
178 if (timespeccmp(&threshold, &it->update_ts, <))
179 break;
180 LOGP(DNACC, LOGL_DEBUG, "si_cache: Removing entry %s\n",
181 osmo_cgi_ps_name(&it->key));
182 llist_del(&it->list);
183 talloc_free(it);
184 }
185
186 si_cache_schedule_cleanup(cache);
187}
188
189static void si_cache_schedule_cleanup(struct si_cache *cache)
190{
191 struct si_cache_entry *it;
192 struct timespec now, threshold, result;
193
194 /* First item is the one with oldest update_ts */
195 it = llist_first_entry_or_null(&cache->list, struct si_cache_entry, list);
196 if (!it)
197 return;
198
199 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
200
201 timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
202
203 if (timespeccmp(&now, &threshold, >=)) {
204 /* Too late, let's flush asynchonously so newly added isn't
205 * immediatelly freed before return. */
206 result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
207 } else {
208 timespecsub(&threshold, &now, &result);
209 }
210 osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
211}
212
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +0100213struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100214{
215 struct si_cache *cache = talloc_zero(ctx, struct si_cache);
216 OSMO_ASSERT(cache);
217 INIT_LLIST_HEAD(&cache->list);
218 osmo_timer_setup(&cache->cleanup_timer, si_cache_cleanup_cb, cache);
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +0100219 cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100220 return cache;
221}
Pau Espin Pedrolab7159f2021-01-26 17:51:44 +0100222
223void si_cache_set_keep_time_interval(struct si_cache *cache, unsigned int keep_time_sec)
224{
225 cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
226 si_cache_schedule_cleanup(cache);
227}
228
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100229struct si_cache_entry *si_cache_add(struct si_cache *cache,
230 const struct osmo_cell_global_id_ps *key,
231 const struct si_cache_value *value)
232{
233 struct si_cache_entry *it;
234
235 /* First check if it already exists. If so, simply update timer+value */
236 it = si_cache_lookup_entry(cache, key);
237 if (!it) {
238 LOGP(DNACC, LOGL_DEBUG, "si_cache: Inserting new entry %s\n",
239 osmo_cgi_ps_name(key));
240 it = talloc_zero(cache, struct si_cache_entry);
241 OSMO_ASSERT(it);
242 memcpy(&it->key, key, sizeof(it->key));
243 } else {
244 LOGP(DNACC, LOGL_DEBUG, "si_cache: Updating entry %s\n",
245 osmo_cgi_ps_name(&it->key));
246 /* remove item, we'll add it to the end to have them sorted by last update */
247 llist_del(&it->list);
248 }
249
250 memcpy(&it->value, value, sizeof(it->value));
251 OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
252 llist_add_tail(&it->list, &cache->list);
253 si_cache_schedule_cleanup(cache);
254 return it;
255}
256
257struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,
258 const struct osmo_cell_global_id_ps *key)
259{
260 struct si_cache_entry *tmp;
261 llist_for_each_entry(tmp, &cache->list, list) {
262 if (osmo_cgi_ps_cmp(&tmp->key, key) == 0)
263 return tmp;
264 }
265 return NULL;
266}
267
268const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,
269 const struct osmo_cell_global_id_ps *key)
270{
271 struct si_cache_entry *it = si_cache_lookup_entry(cache, key);
272 if (it)
273 return &it->value;
274 return NULL;
275}
276
277void si_cache_free(struct si_cache *cache)
278{
279 struct si_cache_entry *it, *tmp;
280 if (!cache)
281 return;
282
283 llist_for_each_entry_safe(it, tmp, &cache->list, list) {
284 llist_del(&it->list);
285 talloc_free(it);
286 }
287 osmo_timer_del(&cache->cleanup_timer);
288 talloc_free(cache);
289}