blob: 1014ea3820661e618915458d3ce1f7b1bd468c49 [file] [log] [blame]
Jacob Erlbecke04e0b02015-05-06 18:30:48 +02001/* gprs_ms.cpp
2 *
3 * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
4 * Author: Jacob Erlbeck <jerlbeck@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
22#include "gprs_ms.h"
23
Jacob Erlbecka700dd92015-06-02 16:00:41 +020024#include "bts.h"
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020025#include "tbf.h"
26#include "gprs_debug.h"
27
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020028#include <time.h>
29
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020030extern "C" {
31 #include <osmocom/core/talloc.h>
32 #include <osmocom/core/utils.h>
33}
34
35extern void *tall_pcu_ctx;
36
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020037static int64_t now_msec()
38{
39 struct timespec ts;
40 clock_gettime(CLOCK_MONOTONIC, &ts);
41
42 return int64_t(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
43}
44
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020045struct GprsMsDefaultCallback: public GprsMs::Callback {
46 virtual void ms_idle(class GprsMs *ms) {
47 delete ms;
48 }
49 virtual void ms_active(class GprsMs *) {}
50};
51
52static GprsMsDefaultCallback gprs_default_cb;
53
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020054GprsMs::Guard::Guard(GprsMs *ms) :
55 m_ms(ms ? ms->ref() : NULL)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020056{
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020057}
58
59GprsMs::Guard::~Guard()
60{
61 if (m_ms)
62 m_ms->unref();
63}
64
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020065void GprsMs::timeout(void *priv_)
66{
67 GprsMs *ms = static_cast<GprsMs *>(priv_);
68
69 LOGP(DRLCMAC, LOGL_INFO, "Timeout for MS object, TLLI = 0x%08x\n",
70 ms->tlli());
71
72 if (ms->m_timer.data) {
73 ms->m_timer.data = NULL;
74 ms->unref();
75 }
76}
77
Jacob Erlbeck17214bb2015-06-02 14:06:12 +020078GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
79 m_bts(bts),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020080 m_cb(&gprs_default_cb),
81 m_ul_tbf(NULL),
82 m_dl_tbf(NULL),
83 m_tlli(tlli),
Jacob Erlbeck93990462015-05-15 15:50:43 +020084 m_new_ul_tlli(0),
85 m_new_dl_tlli(0),
Jacob Erlbeck9200ce62015-05-22 17:48:04 +020086 m_ta(0),
Jacob Erlbeckbefc7602015-06-02 12:33:30 +020087 m_ms_class(0),
Jacob Erlbecka700dd92015-06-02 16:00:41 +020088 m_current_cs_ul(1),
89 m_current_cs_dl(1),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020090 m_is_idle(true),
Jacob Erlbeck53670862015-05-12 17:54:33 +020091 m_ref(0),
Jacob Erlbeck25db7c62015-06-11 12:59:01 +020092 m_list(this),
Jacob Erlbeck04a10862015-06-12 16:01:56 +020093 m_delay(0),
94 m_nack_rate_dl(0)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020095{
96 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +020097
98 m_imsi[0] = 0;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020099 memset(&m_timer, 0, sizeof(m_timer));
100 m_timer.cb = GprsMs::timeout;
Jacob Erlbeck489a2b32015-05-28 19:07:01 +0200101 m_llc_queue.init();
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200102 if (m_bts) {
103 m_current_cs_ul = m_bts->bts_data()->initial_cs_ul;
104 if (m_current_cs_ul < 1)
105 m_current_cs_ul = 1;
106
107 m_current_cs_dl = m_bts->bts_data()->initial_cs_dl;
108 if (m_current_cs_dl < 1)
109 m_current_cs_dl = 1;
110 }
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200111 m_last_cs_not_low = now_msec();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200112}
113
114GprsMs::~GprsMs()
115{
116 LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200117
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200118 if (osmo_timer_pending(&m_timer))
119 osmo_timer_del(&m_timer);
120
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200121 if (m_ul_tbf) {
122 m_ul_tbf->set_ms(NULL);
123 m_ul_tbf = NULL;
124 }
125
126 if (m_dl_tbf) {
127 m_dl_tbf->set_ms(NULL);
128 m_dl_tbf = NULL;
129 }
Jacob Erlbeck17214bb2015-06-02 14:06:12 +0200130 m_llc_queue.clear(m_bts);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200131}
132
133void* GprsMs::operator new(size_t size)
134{
135 static void *tall_ms_ctx = NULL;
136 if (!tall_ms_ctx)
137 tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
138
139 return talloc_size(tall_ms_ctx, size);
140}
141
142void GprsMs::operator delete(void* p)
143{
144 talloc_free(p);
145}
146
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200147GprsMs *GprsMs::ref()
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200148{
149 m_ref += 1;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200150 return this;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200151}
152
153void GprsMs::unref()
154{
155 OSMO_ASSERT(m_ref >= 0);
156 m_ref -= 1;
157 if (m_ref == 0)
158 update_status();
159}
160
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200161void GprsMs::start_timer()
162{
163 if (m_delay == 0)
164 return;
165
166 if (!m_timer.data)
167 m_timer.data = ref();
168
169 osmo_timer_schedule(&m_timer, m_delay, 0);
170}
171
172void GprsMs::stop_timer()
173{
174 if (!m_timer.data)
175 return;
176
177 osmo_timer_del(&m_timer);
178 m_timer.data = NULL;
179 unref();
180}
181
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200182void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
183{
184 if (tbf->direction == GPRS_RLCMAC_DL_TBF)
185 attach_dl_tbf(static_cast<gprs_rlcmac_dl_tbf *>(tbf));
186 else
187 attach_ul_tbf(static_cast<gprs_rlcmac_ul_tbf *>(tbf));
188}
189
190void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
191{
192 if (m_ul_tbf == tbf)
193 return;
194
195 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
196 tlli(), tbf->name());
197
198 Guard guard(this);
199
200 if (m_ul_tbf)
201 detach_tbf(m_ul_tbf);
202
203 m_ul_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200204
205 if (tbf)
206 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200207}
208
209void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
210{
211 if (m_dl_tbf == tbf)
212 return;
213
214 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
215 tlli(), tbf->name());
216
217 Guard guard(this);
218
219 if (m_dl_tbf)
220 detach_tbf(m_dl_tbf);
221
222 m_dl_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200223
224 if (tbf)
225 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200226}
227
228void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
229{
230 if (m_ul_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf))
231 m_ul_tbf = NULL;
232 else if (m_dl_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf))
233 m_dl_tbf = NULL;
234 else
235 return;
236
237 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
238 tlli(), tbf->name());
239
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200240 if (tbf->ms() == this)
241 tbf->set_ms(NULL);
242
Jacob Erlbeck6d866282015-06-19 09:08:23 +0200243 if (!m_dl_tbf && !m_ul_tbf && tlli() != 0)
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200244 start_timer();
245
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200246 update_status();
247}
248
249void GprsMs::update_status()
250{
251 if (m_ref > 0)
252 return;
253
254 if (is_idle() && !m_is_idle) {
255 m_is_idle = true;
256 m_cb->ms_idle(this);
257 /* this can be deleted by now, do not access it */
258 return;
259 }
260
261 if (!is_idle() && m_is_idle) {
262 m_is_idle = false;
263 m_cb->ms_active(this);
264 }
265}
266
267void GprsMs::set_tlli(uint32_t tlli)
268{
Jacob Erlbeck93990462015-05-15 15:50:43 +0200269 if (tlli == m_tlli || tlli == m_new_ul_tlli)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200270 return;
271
Jacob Erlbeck93990462015-05-15 15:50:43 +0200272 if (tlli != m_new_dl_tlli) {
273 LOGP(DRLCMAC, LOGL_INFO,
274 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
275 "not yet confirmed\n",
276 this->tlli(), tlli);
277 m_new_ul_tlli = tlli;
278 return;
279 }
280
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200281 LOGP(DRLCMAC, LOGL_INFO,
Jacob Erlbeck93990462015-05-15 15:50:43 +0200282 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
283 "already confirmed partly\n",
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200284 m_tlli, tlli);
285
286 m_tlli = tlli;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200287 m_new_dl_tlli = 0;
288 m_new_ul_tlli = 0;
289}
290
291bool GprsMs::confirm_tlli(uint32_t tlli)
292{
293 if (tlli == m_tlli || tlli == m_new_dl_tlli)
294 return false;
295
296 if (tlli != m_new_ul_tlli) {
297 /* The MS has not sent a message with the new TLLI, which may
298 * happen according to the spec [TODO: add reference]. */
299
300 LOGP(DRLCMAC, LOGL_INFO,
301 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
302 "partly confirmed\n", tlli);
303 /* Use the network's idea of TLLI as candidate, this does not
304 * change the result value of tlli() */
305 m_new_dl_tlli = tlli;
306 return false;
307 }
308
309 LOGP(DRLCMAC, LOGL_INFO,
310 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
311
312 m_tlli = tlli;
313 m_new_dl_tlli = 0;
314 m_new_ul_tlli = 0;
315
316 return true;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200317}
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200318
319void GprsMs::set_imsi(const char *imsi)
320{
321 if (!imsi) {
322 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
323 return;
324 }
325
326 if (imsi[0] && strlen(imsi) < 3) {
327 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
328 imsi);
329 return;
330 }
331
332 if (strcmp(imsi, m_imsi) == 0)
333 return;
334
335 LOGP(DRLCMAC, LOGL_INFO,
336 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
337 tlli(), m_imsi, imsi);
338
339 strncpy(m_imsi, imsi, sizeof(m_imsi));
340 m_imsi[sizeof(m_imsi) - 1] = '\0';
341}
342
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200343void GprsMs::set_ta(uint8_t ta_)
344{
345 if (ta_ == m_ta)
346 return;
347
348 LOGP(DRLCMAC, LOGL_INFO,
349 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
350 tlli(), m_ta, ta_);
351
352 m_ta = ta_;
353}
354
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200355void GprsMs::set_ms_class(uint8_t ms_class_)
356{
357 if (ms_class_ == m_ms_class)
358 return;
359
360 LOGP(DRLCMAC, LOGL_INFO,
361 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
362 tlli(), m_ms_class, ms_class_);
363
364 m_ms_class = ms_class_;
365}
366
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200367void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
368{
369 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200370 int64_t now;
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200371 uint8_t max_cs_dl = 4;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200372
373 OSMO_ASSERT(m_bts != NULL);
374 bts_data = m_bts->bts_data();
375
376 if (error_rate < 0)
377 return;
378
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200379 now = now_msec();
380
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200381 if (bts_data->max_cs_dl)
382 max_cs_dl = bts_data->max_cs_dl;
383
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200384 /* TODO: Check for TBF direction */
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200385 /* TODO: Support different CS values for UL and DL */
386
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200387 m_nack_rate_dl = error_rate;
388
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200389 if (error_rate > bts_data->cs_adj_upper_limit) {
390 if (m_current_cs_dl > 1) {
391 m_current_cs_dl -= 1;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200392 LOGP(DRLCMACDL, LOGL_INFO,
393 "MS (IMSI %s): High error rate %d%%, "
394 "reducing CS level to %d\n",
395 imsi(), error_rate, m_current_cs_dl);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200396 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200397 }
398 } else if (error_rate < bts_data->cs_adj_lower_limit) {
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200399 if (m_current_cs_dl < max_cs_dl) {
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200400 if (now - m_last_cs_not_low > 1000) {
401 m_current_cs_dl += 1;
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200402
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200403 LOGP(DRLCMACDL, LOGL_INFO,
404 "MS (IMSI %s): Low error rate %d%%, "
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200405 "increasing DL CS level to %d\n",
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200406 imsi(), error_rate, m_current_cs_dl);
407 m_last_cs_not_low = now;
408 } else {
409 LOGP(DRLCMACDL, LOGL_DEBUG,
410 "MS (IMSI %s): Low error rate %d%%, "
411 "ignored (within blocking period)\n",
412 imsi(), error_rate);
413 }
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200414 }
415 } else {
416 LOGP(DRLCMACDL, LOGL_DEBUG,
417 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
418 imsi(), error_rate);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200419 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200420 }
421}
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200422
423void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
424{
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200425 struct gprs_rlcmac_bts *bts_data;
426 uint8_t max_cs_ul = 4;
Jacob Erlbeck51b11512015-06-11 16:54:50 +0200427 unsigned i;
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200428
429 OSMO_ASSERT(m_bts != NULL);
430 bts_data = m_bts->bts_data();
431
432 if (bts_data->max_cs_ul)
433 max_cs_ul = bts_data->max_cs_ul;
434
435 if (meas->have_link_qual) {
436 int old_link_qual = meas->link_qual;
437 int low = bts_data->cs_lqual_ranges[current_cs_ul()-1].low;
438 int high = bts_data->cs_lqual_ranges[current_cs_ul()-1].high;
439 uint8_t new_cs_ul = m_current_cs_ul;
440
441 if (m_l1_meas.have_link_qual)
442 old_link_qual = m_l1_meas.link_qual;
443
444 if (meas->link_qual < low && old_link_qual < low)
445 new_cs_ul = m_current_cs_ul - 1;
446 else if (meas->link_qual > high && old_link_qual > high &&
447 m_current_cs_ul < max_cs_ul)
448 new_cs_ul = m_current_cs_ul + 1;
449
450 if (m_current_cs_ul != new_cs_ul) {
451 LOGP(DRLCMACDL, LOGL_INFO,
452 "MS (IMSI %s): "
453 "Link quality %ddB (%ddB) left window [%d, %d], "
454 "modifying uplink CS level: %d -> %d\n",
455 imsi(), meas->link_qual, old_link_qual,
456 low, high,
457 m_current_cs_ul, new_cs_ul);
458
459 m_current_cs_ul = new_cs_ul;
460 }
461 }
462
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200463 if (meas->have_rssi)
464 m_l1_meas.set_rssi(meas->rssi);
465 if (meas->have_bto)
466 m_l1_meas.set_bto(meas->bto);
467 if (meas->have_ber)
468 m_l1_meas.set_ber(meas->ber);
469 if (meas->have_link_qual)
470 m_l1_meas.set_link_qual(meas->link_qual);
Jacob Erlbeck51b11512015-06-11 16:54:50 +0200471
472 if (meas->have_ms_rx_qual)
473 m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
474 if (meas->have_ms_c_value)
475 m_l1_meas.set_ms_c_value(meas->ms_c_value);
476 if (meas->have_ms_sign_var)
477 m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
478
479 if (meas->have_ms_i_level) {
480 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
481 if (meas->ts[i].have_ms_i_level)
482 m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
483 else
484 m_l1_meas.ts[i].have_ms_i_level = 0;
485 }
486 }
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200487}
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200488
489uint8_t GprsMs::current_cs_dl() const
490{
491 uint8_t cs = m_current_cs_dl;
492 size_t unencoded_octets;
493
494 if (!m_bts)
495 return cs;
496
497 unencoded_octets = m_llc_queue.octets();
498
499 /* If the DL TBF is active, add number of unencoded chunk octets */
500 if (m_dl_tbf)
501 unencoded_octets = m_dl_tbf->m_llc.chunk_size();
502
503 /* There are many unencoded octets, don't reduce */
504 if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
505 return cs;
506
507 /* RF conditions are good, don't reduce */
508 if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
509 return cs;
510
511 /* The throughput would probably be better if the CS level was reduced */
512 cs -= 1;
513
514 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
515 if (cs == 2)
516 cs -= 1;
517
518 return cs;
519}
520
Jacob Erlbeck699b8dc2015-06-29 14:05:55 +0200521int GprsMs::first_common_ts() const
522{
523 if (m_dl_tbf)
524 return m_dl_tbf->first_common_ts;
525
526 if (m_ul_tbf)
527 return m_ul_tbf->first_common_ts;
528
529 return -1;
530}
531