blob: 0135020a697826116754c4945fcb1819a17e8cae [file] [log] [blame]
Neels Hofmeyr909e9722017-12-07 03:54:01 +01001/* Handover Decision Algorithm 2 for intra-BSC (inter-BTS) handover, public API for OsmoBSC. */
2
3/* (C) 2009 by Andreas Eversberg <jolly@eversberg.eu>
4 * (C) 2017-2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
5 *
6 * All Rights Reserved
7 *
8 * Author: Andreas Eversberg <jolly@eversberg.eu>
9 * Neels Hofmeyr <nhofmeyr@sysmocom.de>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Affero General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Affero General Public License for more details.
20 *
21 * You should have received a copy of the GNU Affero General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25#include <stdbool.h>
26#include <errno.h>
27
28#include <osmocom/bsc/debug.h>
29#include <osmocom/bsc/gsm_data.h>
30#include <osmocom/bsc/handover.h>
31#include <osmocom/bsc/handover_decision.h>
32#include <osmocom/bsc/handover_decision_2.h>
33#include <osmocom/bsc/handover_cfg.h>
34#include <osmocom/bsc/bsc_subscriber.h>
35#include <osmocom/bsc/chan_alloc.h>
36#include <osmocom/bsc/signal.h>
37#include <osmocom/bsc/penalty_timers.h>
38
39#define LOGPHOBTS(bts, level, fmt, args...) \
40 LOGP(DHODEC, level, "(BTS %u) " fmt, bts->nr, ## args)
41
42#define LOGPHOLCHAN(lchan, level, fmt, args...) \
43 LOGP(DHODEC, level, "(lchan %u.%u%u%u %s) (subscr %s) " fmt, \
44 lchan->ts->trx->bts->nr, \
45 lchan->ts->trx->nr, \
46 lchan->ts->nr, \
47 lchan->nr, \
48 gsm_pchan_name(lchan->ts->pchan), \
Neels Hofmeyr00727552018-02-21 14:33:15 +010049 bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \
Neels Hofmeyr909e9722017-12-07 03:54:01 +010050 ## args)
51
52#define LOGPHOLCHANTOBTS(lchan, new_bts, level, fmt, args...) \
53 LOGP(DHODEC, level, "(lchan %u.%u%u%u %s)->(BTS %u) (subscr %s) " fmt, \
54 lchan->ts->trx->bts->nr, \
55 lchan->ts->trx->nr, \
56 lchan->ts->nr, \
57 lchan->nr, \
58 gsm_pchan_name(lchan->ts->pchan), \
59 new_bts->nr, \
Neels Hofmeyr00727552018-02-21 14:33:15 +010060 bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \
Neels Hofmeyr909e9722017-12-07 03:54:01 +010061 ## args)
62
63#define REQUIREMENT_A_TCHF 0x01
64#define REQUIREMENT_B_TCHF 0x02
65#define REQUIREMENT_C_TCHF 0x04
66#define REQUIREMENT_A_TCHH 0x10
67#define REQUIREMENT_B_TCHH 0x20
68#define REQUIREMENT_C_TCHH 0x40
69#define REQUIREMENT_TCHF_MASK (REQUIREMENT_A_TCHF | REQUIREMENT_B_TCHF | REQUIREMENT_C_TCHF)
70#define REQUIREMENT_TCHH_MASK (REQUIREMENT_A_TCHH | REQUIREMENT_B_TCHH | REQUIREMENT_C_TCHH)
71#define REQUIREMENT_A_MASK (REQUIREMENT_A_TCHF | REQUIREMENT_A_TCHH)
72#define REQUIREMENT_B_MASK (REQUIREMENT_B_TCHF | REQUIREMENT_B_TCHH)
73#define REQUIREMENT_C_MASK (REQUIREMENT_C_TCHF | REQUIREMENT_C_TCHH)
74
75struct ho_candidate {
76 struct gsm_lchan *lchan; /* candidate for whom */
77 struct gsm_bts *bts; /* target BTS */
78 uint8_t requirements; /* what is fulfilled */
79 int avg; /* average RX level */
80};
81
82enum ho_reason {
83 HO_REASON_INTERFERENCE,
84 HO_REASON_BAD_QUALITY,
85 HO_REASON_LOW_RXLEVEL,
86 HO_REASON_MAX_DISTANCE,
87 HO_REASON_BETTER_CELL,
88 HO_REASON_CONGESTION,
89};
90
91static const struct value_string ho_reason_names[] = {
92 { HO_REASON_INTERFERENCE, "interference (bad quality)" },
93 { HO_REASON_BAD_QUALITY, "bad quality" },
94 { HO_REASON_LOW_RXLEVEL, "low rxlevel" },
95 { HO_REASON_MAX_DISTANCE, "maximum allowed distance" },
96 { HO_REASON_BETTER_CELL, "better cell" },
97 { HO_REASON_CONGESTION, "congestion" },
98 {0, NULL}
99};
100
101static const char *ho_reason_name(int value)
102{
103 return get_value_string(ho_reason_names, value);
104}
105
106
107static bool hodec2_initialized = false;
108static enum ho_reason global_ho_reason;
109
110static void congestion_check_cb(void *arg);
111
112/* This function gets called on ho2 init, whenever the congestion check interval is changed, and also
113 * when the timer has fired to trigger again after the next congestion check timeout. */
114static void reinit_congestion_timer(struct gsm_network *net)
115{
116 int congestion_check_interval_s;
117 bool was_active;
118
119 /* Don't setup timers from VTY config parsing before the main program has actually initialized
120 * the data structures. */
121 if (!hodec2_initialized)
122 return;
123
124 was_active = net->hodec2.congestion_check_timer.active;
125 if (was_active)
126 osmo_timer_del(&net->hodec2.congestion_check_timer);
127
128 congestion_check_interval_s = net->hodec2.congestion_check_interval_s;
129 if (congestion_check_interval_s < 1) {
130 if (was_active)
131 LOGP(DHODEC, LOGL_NOTICE, "HO algorithm 2: Disabling congestion check\n");
132 return;
133 }
134
135 LOGP(DHODEC, LOGL_DEBUG, "HO algorithm 2: next periodical congestion check in %u seconds\n",
136 congestion_check_interval_s);
137
138 osmo_timer_setup(&net->hodec2.congestion_check_timer,
139 congestion_check_cb, net);
140 osmo_timer_schedule(&net->hodec2.congestion_check_timer,
141 congestion_check_interval_s, 0);
142}
143
144void hodec2_on_change_congestion_check_interval(struct gsm_network *net, unsigned int new_interval)
145{
146 net->hodec2.congestion_check_interval_s = new_interval;
147 reinit_congestion_timer(net);
148}
149
150static void conn_penalty_time_add(struct gsm_subscriber_connection *conn, struct gsm_bts *bts,
151 int penalty_time)
152{
153 if (!conn->hodec2.penalty_timers) {
154 conn->hodec2.penalty_timers = penalty_timers_init(conn);
155 OSMO_ASSERT(conn->hodec2.penalty_timers);
156 }
157 penalty_timers_add(conn->hodec2.penalty_timers, bts, penalty_time);
158}
159
160static unsigned int conn_penalty_time_remaining(struct gsm_subscriber_connection *conn,
161 struct gsm_bts *bts)
162{
163 if (!conn->hodec2.penalty_timers)
164 return 0;
165 return penalty_timers_remaining(conn->hodec2.penalty_timers, bts);
166}
167
168/* did we get a RXLEV for a given cell in the given report? Mark matches as MRC_F_PROCESSED. */
169static struct gsm_meas_rep_cell *cell_in_rep(struct gsm_meas_rep *mr, uint16_t arfcn, uint8_t bsic)
170{
171 int i;
172
173 for (i = 0; i < mr->num_cell; i++) {
174 struct gsm_meas_rep_cell *mrc = &mr->cell[i];
175
176 if (mrc->arfcn != arfcn)
177 continue;
178 if (mrc->bsic != bsic)
179 continue;
180
181 return mrc;
182 }
183 return NULL;
184}
185
186/* obtain averaged rxlev for given neighbor */
187static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window)
188{
189 unsigned int i, idx;
190 int avg = 0;
191
192 /* reduce window to the actual number of existing measurements */
193 if (window > nmp->rxlev_cnt)
194 window = nmp->rxlev_cnt;
195 /* this should never happen */
196 if (window <= 0)
197 return 0;
198
199 idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev),
200 nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev),
201 window);
202
203 for (i = 0; i < window; i++) {
204 int j = (idx+i) % ARRAY_SIZE(nmp->rxlev);
205
206 avg += nmp->rxlev[j];
207 }
208
209 return avg / window;
210}
211
212/* Find empty slot or the worst neighbor. */
213static struct neigh_meas_proc *find_unused_or_worst_neigh(struct gsm_lchan *lchan)
214{
215 struct neigh_meas_proc *nmp_worst = NULL;
216 int worst;
217 int j;
218
219 /* First try to find an empty/unused slot. */
220 for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) {
221 struct neigh_meas_proc *nmp = &lchan->neigh_meas[j];
222 if (!nmp->arfcn)
223 return nmp;
224 }
225
226 /* No empty slot found. Return worst neighbor to be evicted. */
227 worst = 0; /* (overwritten on first loop, but avoid compiler warning) */
228 for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) {
229 struct neigh_meas_proc *nmp = &lchan->neigh_meas[j];
230 int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG);
231 if (nmp_worst && avg >= worst)
232 continue;
233 worst = avg;
234 nmp_worst = nmp;
235 }
236
237 return nmp_worst;
238}
239
240/* process neighbor cell measurement reports */
241static void process_meas_neigh(struct gsm_meas_rep *mr)
242{
243 int i, j, idx;
244
245 /* For each reported cell, try to update measurements we already have from previous reports. */
246 for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) {
247 struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j];
248 unsigned int idx;
249 struct gsm_meas_rep_cell *mrc;
250
251 /* skip unused entries */
252 if (!nmp->arfcn)
253 continue;
254
255 mrc = cell_in_rep(mr, nmp->arfcn, nmp->bsic);
256 idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev);
257 if (mrc) {
258 nmp->rxlev[idx] = mrc->rxlev;
259 nmp->last_seen_nr = mr->nr;
260 LOGPHOLCHAN(mr->lchan, LOGL_DEBUG, "neigh %u rxlev=%d last_seen_nr=%u\n",
261 nmp->arfcn, mrc->rxlev, nmp->last_seen_nr);
262 mrc->flags |= MRC_F_PROCESSED;
263 } else {
264 nmp->rxlev[idx] = 0;
265 LOGPHOLCHAN(mr->lchan, LOGL_DEBUG, "neigh %u not in report (last_seen_nr=%u)\n",
266 nmp->arfcn, nmp->last_seen_nr);
267 }
268 nmp->rxlev_cnt++;
269 }
270
271 /* Add cells that we don't know about yet, if necessary overwriting previous records that reflect
272 * cells with worse receive levels */
273 for (i = 0; i < mr->num_cell; i++) {
274 struct gsm_meas_rep_cell *mrc = &mr->cell[i];
275 struct neigh_meas_proc *nmp;
276
277 if (mrc->flags & MRC_F_PROCESSED)
278 continue;
279
280 nmp = find_unused_or_worst_neigh(mr->lchan);
281
282 nmp->arfcn = mrc->arfcn;
283 nmp->bsic = mrc->bsic;
284
285 nmp->rxlev_cnt = 0;
286 idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev);
287 nmp->rxlev[idx] = mrc->rxlev;
288 nmp->rxlev_cnt++;
289 nmp->last_seen_nr = mr->nr;
290 LOGPHOLCHAN(mr->lchan, LOGL_DEBUG, "neigh %u new in report rxlev=%d last_seen_nr=%u\n",
291 nmp->arfcn, mrc->rxlev, nmp->last_seen_nr);
292
293 mrc->flags |= MRC_F_PROCESSED;
294 }
295}
296
297static bool codec_type_is_supported(struct gsm_subscriber_connection *conn,
298 enum gsm0808_speech_codec_type type)
299{
300 int i;
301 struct gsm0808_speech_codec_list *clist = &conn->codec_list;
302
303 if (!conn->codec_list_present) {
304 /* We don't have a list of supported codecs. This should never happen. */
305 LOGPHOLCHAN(conn->lchan, LOGL_ERROR,
306 "No Speech Codec List present, accepting all codecs\n");
307 return true;
308 }
309
310 for (i = 0; i < clist->len; i++) {
311 if (clist->codec[i].type == type) {
312 LOGPHOLCHAN(conn->lchan, LOGL_DEBUG, "%s supported\n",
313 gsm0808_speech_codec_type_name(type));
314 return true;
315 }
316 }
317 LOGPHOLCHAN(conn->lchan, LOGL_DEBUG, "Codec not supported by MS or not allowed by MSC: %s\n",
318 gsm0808_speech_codec_type_name(type));
319 return false;
320}
321
322/*
323 * Check what requirements the given cell fulfills.
324 * A bit mask of fulfilled requirements is returned.
325 *
326 * Target cell requirement A -- ability to service the call
327 *
328 * In order to successfully handover/assign to a better cell, the target cell
329 * must be able to continue the current call. Therefore the cell must fulfill
330 * the following criteria:
331 *
332 * * The handover must be enabled for the target cell, if it differs from the
333 * originating cell.
334 * * The assignment must be enabled for the cell, if it equals the current
335 * cell.
336 * * The handover penalty timer must not run for the cell.
337 * * If FR, EFR or HR codec is used, the cell must support this codec.
338 * * If FR or EFR codec is used, the cell must have a TCH/F slot type
339 * available.
340 * * If HR codec is used, the cell must have a TCH/H slot type available.
341 * * If AMR codec is used, the cell must have a TCH/F slot available, if AFS
342 * is supported by mobile and BTS.
343 * * If AMR codec is used, the cell must have a TCH/H slot available, if AHS
344 * is supported by mobile and BTS.
345 * * osmo-nitb with built-in MNCC application:
346 * o If AMR codec is used, the cell must support AMR codec with equal codec
347 * rate or rates. (not meaning TCH types)
348 * * If defined, the number of maximum unsynchronized handovers to this cell
349 * may not be exceeded. (This limits processing load for random access
350 * bursts.)
351 *
352 *
353 * Target cell requirement B -- avoid congestion
354 *
355 * In order to prevent congestion of a target cell, the cell must fulfill the
356 * requirement A, but also:
357 *
358 * * The minimum free channels, that are defined for that cell must be
359 * maintained after handover/assignment.
360 * * The minimum free channels are defined for TCH/F and TCH/H slot types
361 * individually.
362 *
363 *
364 * Target cell requirement C -- balance congestion
365 *
366 * In order to balance congested cells, the target cell must fulfill the
367 * requirement A, but also:
368 *
369 * * The target cell (which is congested also) must have more or equal free
370 * slots after handover/assignment.
371 * * The number of free slots are checked for TCH/F and TCH/H slot types
372 * individually.
373 */
374static uint8_t check_requirements(struct gsm_lchan *lchan, struct gsm_bts *bts, int tchf_count, int tchh_count)
375{
376 int count;
377 uint8_t requirement = 0;
378 unsigned int penalty_time;
379 struct gsm_bts *current_bts = lchan->ts->trx->bts;
380
381 /* Requirement A */
382
383 /* the handover/assignment must not be disabled */
384 if (current_bts == bts) {
385 if (!ho_get_hodec2_as_active(bts->ho)) {
386 LOGPHOLCHAN(lchan, LOGL_DEBUG, "Assignment disabled\n");
387 return 0;
388 }
389 } else {
390 if (!ho_get_ho_active(bts->ho)) {
391 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
392 "not a candidate, handover is disabled in target BTS\n");
393 return 0;
394 }
395 }
396
397 /* the handover penalty timer must not run for this bts */
398 penalty_time = conn_penalty_time_remaining(lchan->conn, bts);
399 if (penalty_time) {
400 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, target BTS still in penalty time"
401 " (%u seconds left)\n", penalty_time);
402 return 0;
403 }
404
405 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "tch_mode='%s' type='%s'\n",
406 get_value_string(gsm48_chan_mode_names, lchan->tch_mode),
407 gsm_lchant_name(lchan->type));
408
409 /* compatibility check for codecs.
410 * if so, the candidates for full rate and half rate are selected */
411 switch (lchan->tch_mode) {
412 case GSM48_CMODE_SPEECH_V1:
413 switch (lchan->type) {
414 case GSM_LCHAN_TCH_F: /* mandatory */
415 requirement |= REQUIREMENT_A_TCHF;
416 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "tch_mode='%s' type='%s' supported\n",
417 get_value_string(gsm48_chan_mode_names, lchan->tch_mode),
418 gsm_lchant_name(lchan->type));
419 break;
420 case GSM_LCHAN_TCH_H:
421 if (!bts->codec.hr) {
422 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
423 "tch_mode='%s' type='%s' not supported\n",
424 get_value_string(gsm48_chan_mode_names,
425 lchan->tch_mode),
426 gsm_lchant_name(lchan->type));
427 break;
428 }
429 if (codec_type_is_supported(lchan->conn, GSM0808_SCT_HR1))
430 requirement |= REQUIREMENT_A_TCHH;
431 break;
432 default:
433 LOGPHOLCHAN(lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n",
434 get_value_string(gsm48_chan_mode_names, lchan->tch_mode));
435 return 0;
436 }
437 break;
438 case GSM48_CMODE_SPEECH_EFR:
439 if (!bts->codec.efr) {
440 LOGPHOBTS(bts, LOGL_DEBUG, "EFR not supported\n");
441 break;
442 }
443 if (codec_type_is_supported(lchan->conn, GSM0808_SCT_FR2))
444 requirement |= REQUIREMENT_A_TCHF;
445 break;
446 case GSM48_CMODE_SPEECH_AMR:
447 if (!bts->codec.amr) {
448 LOGPHOBTS(bts, LOGL_DEBUG, "AMR not supported\n");
449 break;
450 }
451 if (codec_type_is_supported(lchan->conn, GSM0808_SCT_FR3))
452 requirement |= REQUIREMENT_A_TCHF;
453 if (codec_type_is_supported(lchan->conn, GSM0808_SCT_HR3))
454 requirement |= REQUIREMENT_A_TCHH;
455 break;
456 default:
457 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "Not even considering: src is not a SPEECH mode lchan\n");
458 return 0;
459 }
460
461 /* no candidate, because new cell is incompatible */
462 if (!requirement) {
463 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, because codec of MS and BTS are incompatible\n");
464 return 0;
465 }
466
467 /* remove slot types that are not available */
468 if (!tchf_count && requirement & REQUIREMENT_A_TCHF) {
469 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
470 "removing TCH/F, since all TCH/F lchans are in use\n");
471 requirement &= ~(REQUIREMENT_A_TCHF);
472 }
473 if (!tchh_count && requirement & REQUIREMENT_A_TCHH) {
474 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
475 "removing TCH/H, since all TCH/H lchans are in use\n");
476 requirement &= ~(REQUIREMENT_A_TCHH);
477 }
478
479 if (!requirement) {
480 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, because no suitable slots available\n");
481 return 0;
482 }
483
484 /* omit same channel type on same BTS (will not change anything) */
485 if (bts == current_bts) {
486 switch (lchan->type) {
487 case GSM_LCHAN_TCH_F:
488 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
489 "removing TCH/F, already on TCH/F in this cell\n");
490 requirement &= ~(REQUIREMENT_A_TCHF);
491 break;
492 case GSM_LCHAN_TCH_H:
493 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
494 "removing TCH/H, already on TCH/H in this cell\n");
495 requirement &= ~(REQUIREMENT_A_TCHH);
496 break;
497 default:
498 break;
499 }
500
501 if (!requirement) {
502 LOGPHOLCHAN(lchan, LOGL_DEBUG,
503 "Reassignment within cell not an option, no differing channel types available\n");
504 return 0;
505 }
506 }
507
508#ifdef LEGACY
509 // This was useful in osmo-nitb. We're in osmo-bsc now and have no idea whether the osmo-msc does
510 // internal or external call control. Maybe a future config switch wants to add this behavior?
511 /* Built-in call control requires equal codec rates. Remove rates that are not equal. */
512 if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
513 && current_bts->network->mncc_recv != mncc_sock_from_cc) {
514 switch (lchan->type) {
515 case GSM_LCHAN_TCH_F:
516 if ((requirement & REQUIREMENT_A_TCHF)
517 && !!memcmp(&current_bts->mr_full, &bts->mr_full,
518 sizeof(struct amr_multirate_conf)))
519 requirement &= ~(REQUIREMENT_A_TCHF);
520 if ((requirement & REQUIREMENT_A_TCHH)
521 && !!memcmp(&current_bts->mr_full, &bts->mr_half,
522 sizeof(struct amr_multirate_conf)))
523 requirement &= ~(REQUIREMENT_A_TCHH);
524 break;
525 case GSM_LCHAN_TCH_H:
526 if ((requirement & REQUIREMENT_A_TCHF)
527 && !!memcmp(&current_bts->mr_half, &bts->mr_full,
528 sizeof(struct amr_multirate_conf)))
529 requirement &= ~(REQUIREMENT_A_TCHF);
530 if ((requirement & REQUIREMENT_A_TCHH)
531 && !!memcmp(&current_bts->mr_half, &bts->mr_half,
532 sizeof(struct amr_multirate_conf)))
533 requirement &= ~(REQUIREMENT_A_TCHH);
534 break;
535 default:
536 break;
537 }
538
539 if (!requirement) {
540 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
541 "not a candidate, cannot provide identical codec rate\n");
542 return 0;
543 }
544 }
545#endif
546
547 /* the maximum number of unsynchonized handovers must no be exceeded */
548 if (current_bts != bts
549 && bsc_ho_count(bts, true) >= ho_get_hodec2_ho_max(bts->ho)) {
550 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
551 "not a candidate, number of allowed handovers (%d) would be exceeded\n",
552 ho_get_hodec2_ho_max(bts->ho));
553 return 0;
554 }
555
556 /* Requirement B */
557
558 /* the minimum free timeslots that are defined for this cell must
559 * be maintained _after_ handover/assignment */
560 if (requirement & REQUIREMENT_A_TCHF) {
561 if (tchf_count - 1 >= ho_get_hodec2_tchf_min_slots(bts->ho)) {
562 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
563 "TCH/F would not be congested after HO\n");
564 requirement |= REQUIREMENT_B_TCHF;
565 } else {
566 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
567 "TCH/F would be congested after HO\n");
568 }
569 }
570 if (requirement & REQUIREMENT_A_TCHH) {
571 if (tchh_count - 1 >= ho_get_hodec2_tchh_min_slots(bts->ho)) {
572 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
573 "TCH/H would not be congested after HO\n");
574 requirement |= REQUIREMENT_B_TCHH;
575 } else {
576 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
577 "TCH/H would be congested after HO\n");
578 }
579 }
580
581 /* Requirement C */
582
583 /* the nr of free timeslots of the target cell must be >= the
584 * free slots of the current cell _after_ handover/assignment */
585 count = bts_count_free_ts(current_bts,
586 (lchan->type == GSM_LCHAN_TCH_H) ?
587 GSM_PCHAN_TCH_H : GSM_PCHAN_TCH_F);
588 if (requirement & REQUIREMENT_A_TCHF) {
589 if (tchf_count - 1 >= count + 1) {
590 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
591 "TCH/F would be less congested in target than source cell after HO\n");
592 requirement |= REQUIREMENT_C_TCHF;
593 } else {
594 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
595 "TCH/F would not be less congested in target than source cell after HO\n");
596 }
597 }
598 if (requirement & REQUIREMENT_A_TCHH) {
599 if (tchh_count - 1 >= count + 1) {
600 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
601 "TCH/H would be less congested in target than source cell after HO\n");
602 requirement |= REQUIREMENT_C_TCHH;
603 } else {
604 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
605 "TCH/H would not be less congested in target than source cell after HO\n");
606 }
607 }
608
609 LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "requirements=0x%x\n", requirement);
610
611 /* return mask of fulfilled requirements */
612 return requirement;
613}
614
615/* Trigger handover or assignment depending on the target BTS */
616static int trigger_handover_or_assignment(struct gsm_lchan *lchan, struct gsm_bts *new_bts, uint8_t requirements)
617{
618 struct gsm_bts *current_bts = lchan->ts->trx->bts;
619 int afs_bias = 0;
620 bool full_rate = false;
621
622 if (current_bts == new_bts)
623 LOGPHOLCHAN(lchan, LOGL_NOTICE, "Triggering Assignment\n");
624 else
625 LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_NOTICE, "Triggering Handover\n");
626
627 /* afs_bias becomes > 0, if AFS is used and is improved */
628 if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
629 afs_bias = ho_get_hodec2_afs_bias_rxlev(new_bts->ho);
630
631 /* select TCH rate, prefer TCH/F if AFS is improved */
632 switch (lchan->type) {
633 case GSM_LCHAN_TCH_F:
634 /* keep on full rate, if TCH/F is a candidate */
635 if ((requirements & REQUIREMENT_TCHF_MASK)) {
636 if (current_bts == new_bts) {
637 LOGPHOLCHAN(lchan, LOGL_INFO, "Not performing assignment: Already on target type\n");
638 return 0;
639 }
640 full_rate = true;
641 break;
642 }
643 /* change to half rate */
644 if (!(requirements & REQUIREMENT_TCHH_MASK)) {
645 LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_ERROR,
646 "neither TCH/F nor TCH/H requested, aborting ho/as\n");
647 return -EINVAL;
648 }
649 break;
650 case GSM_LCHAN_TCH_H:
651 /* change to full rate if AFS is improved and a candidate */
652 if (afs_bias > 0 && (requirements & REQUIREMENT_TCHF_MASK)) {
653 full_rate = true;
654 LOGPHOLCHAN(lchan, LOGL_DEBUG, "[Improve AHS->AFS]\n");
655 break;
656 }
657 /* change to full rate if the only candidate */
658 if ((requirements & REQUIREMENT_TCHF_MASK)
659 && !(requirements & REQUIREMENT_TCHH_MASK)) {
660 full_rate = true;
661 break;
662 }
663 /* keep on half rate */
664 if (!(requirements & REQUIREMENT_TCHH_MASK)) {
665 LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_ERROR,
666 "neither TCH/F nor TCH/H requested, aborting ho/as\n");
667 return -EINVAL;
668 }
669 if (current_bts == new_bts) {
670 LOGPHOLCHAN(lchan, LOGL_INFO, "Not performing assignment: Already on target type\n");
671 return 0;
672 }
673 break;
674 default:
675 LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_ERROR, "lchan is neither TCH/F nor TCH/H, aborting ho/as\n");
676 return -EINVAL;
677 }
678
679 /* trigger handover or assignment */
680 if (current_bts == new_bts)
681 LOGPHOLCHAN(lchan, LOGL_NOTICE, "Triggering assignment to %s, due to %s\n",
682 full_rate ? "TCH/F" : "TCH/H",
683 ho_reason_name(global_ho_reason));
684 else
685 LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_NOTICE,
686 "Triggering handover to %s, due to %s\n",
687 full_rate ? "TCH/F" : "TCH/H",
688 ho_reason_name(global_ho_reason));
689
690 return bsc_handover_start(HODEC2, lchan, current_bts == new_bts? NULL : new_bts,
691 full_rate? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H);
692}
693
694/* debug collected candidates */
695static inline void debug_candidate(struct ho_candidate *candidate,
696 int neighbor, int8_t rxlev, int tchf_count, int tchh_count)
697{
698 if (neighbor)
699 LOGP(DHODEC, LOGL_DEBUG, " - neighbor BTS %d, RX level "
700 "%d -> %d\n", candidate->bts->nr, rxlev2dbm(rxlev),
701 rxlev2dbm(candidate->avg));
702 else
703 LOGP(DHODEC, LOGL_DEBUG, " - current BTS %d, RX level %d\n",
704 candidate->bts->nr, rxlev2dbm(candidate->avg));
705
706 LOGP(DHODEC, LOGL_DEBUG, " o free TCH/F slots %d, minimum required "
707 "%d\n", tchf_count, ho_get_hodec2_tchf_min_slots(candidate->bts->ho));
708 LOGP(DHODEC, LOGL_DEBUG, " o free TCH/H slots %d, minimum required "
709 "%d\n", tchh_count, ho_get_hodec2_tchh_min_slots(candidate->bts->ho));
710
711 if ((candidate->requirements & REQUIREMENT_TCHF_MASK))
712 LOGP(DHODEC, LOGL_DEBUG, " o requirement ");
713 else
714 LOGP(DHODEC, LOGL_DEBUG, " o no requirement ");
715 if ((candidate->requirements & REQUIREMENT_A_TCHF))
716 LOGPC(DHODEC, LOGL_DEBUG, "A ");
717 if ((candidate->requirements & REQUIREMENT_B_TCHF))
718 LOGPC(DHODEC, LOGL_DEBUG, "B ");
719 if ((candidate->requirements & REQUIREMENT_C_TCHF))
720 LOGPC(DHODEC, LOGL_DEBUG, "C ");
721 LOGPC(DHODEC, LOGL_DEBUG, "fulfilled for TCHF");
722 if (!(candidate->requirements & REQUIREMENT_TCHF_MASK)) /* nothing */
723 LOGPC(DHODEC, LOGL_DEBUG, " (no %s possible)\n",
724 (neighbor) ? "handover" : "assignment");
725 else if ((candidate->requirements & REQUIREMENT_TCHF_MASK)
726 == REQUIREMENT_A_TCHF) /* only A */
727 LOGPC(DHODEC, LOGL_DEBUG, " (more congestion after %s)\n",
728 (neighbor) ? "handover" : "assignment");
729 else if ((candidate->requirements & REQUIREMENT_B_TCHF)) /* B incl. */
730 LOGPC(DHODEC, LOGL_DEBUG, " (not congested after %s)\n",
731 (neighbor) ? "handover" : "assignment");
732 else /* so it must include C */
733 LOGPC(DHODEC, LOGL_DEBUG, " (less or equally congested after "
734 "%s)\n", (neighbor) ? "handover" : "assignment");
735
736 if ((candidate->requirements & REQUIREMENT_TCHH_MASK))
737 LOGP(DHODEC, LOGL_DEBUG, " o requirement ");
738 else
739 LOGP(DHODEC, LOGL_DEBUG, " o no requirement ");
740 if ((candidate->requirements & REQUIREMENT_A_TCHH))
741 LOGPC(DHODEC, LOGL_DEBUG, "A ");
742 if ((candidate->requirements & REQUIREMENT_B_TCHH))
743 LOGPC(DHODEC, LOGL_DEBUG, "B ");
744 if ((candidate->requirements & REQUIREMENT_C_TCHH))
745 LOGPC(DHODEC, LOGL_DEBUG, "C ");
746 LOGPC(DHODEC, LOGL_DEBUG, "fulfilled for TCHH");
747 if (!(candidate->requirements & REQUIREMENT_TCHH_MASK)) /* nothing */
748 LOGPC(DHODEC, LOGL_DEBUG, " (no %s possible)\n",
749 (neighbor) ? "handover" : "assignment");
750 else if ((candidate->requirements & REQUIREMENT_TCHH_MASK)
751 == REQUIREMENT_A_TCHH) /* only A */
752 LOGPC(DHODEC, LOGL_DEBUG, " (more congestion after %s)\n",
753 (neighbor) ? "handover" : "assignment");
754 else if ((candidate->requirements & REQUIREMENT_B_TCHH)) /* B incl. */
755 LOGPC(DHODEC, LOGL_DEBUG, " (not congested after %s)\n",
756 (neighbor) ? "handover" : "assignment");
757 else /* so it must include C */
758 LOGPC(DHODEC, LOGL_DEBUG, " (less or equally congested after "
759 "%s)\n", (neighbor) ? "handover" : "assignment");
760}
761
762/* add candidate for re-assignment within the current cell */
763static void collect_assignment_candidate(struct gsm_lchan *lchan, struct ho_candidate *clist,
764 unsigned int *candidates, int av_rxlev)
765{
766 struct gsm_bts *bts = lchan->ts->trx->bts;
767 int tchf_count, tchh_count;
768 struct ho_candidate *c;
769
770 tchf_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_F);
771 tchh_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_H);
772
773 c = &clist[*candidates];
774 c->lchan = lchan;
775 c->bts = bts;
776 c->requirements = check_requirements(lchan, bts, tchf_count, tchh_count);
777 c->avg = av_rxlev;
778 debug_candidate(c, 0, 0, tchf_count, tchh_count);
779 (*candidates)++;
780}
781
782/* add candidates for handover to all neighbor cells */
783static void collect_handover_candidate(struct gsm_lchan *lchan, struct neigh_meas_proc *nmp,
784 struct ho_candidate *clist, unsigned int *candidates,
785 bool include_weaker_rxlev, int av_rxlev,
786 int *neighbors_count)
787{
788 struct gsm_bts *bts = lchan->ts->trx->bts;
789 int tchf_count, tchh_count;
790 struct gsm_bts *neighbor_bts;
791 int avg;
792 struct ho_candidate *c;
793 int min_rxlev;
794
795 /* skip empty slots */
796 if (nmp->arfcn == 0)
797 return;
798
799 if (neighbors_count)
800 (*neighbors_count)++;
801
802 /* skip if measurement report is old */
803 if (nmp->last_seen_nr != lchan->meas_rep_last_seen_nr) {
804 LOGPHOLCHAN(lchan, LOGL_DEBUG, "neighbor ARFCN %u measurement report is old"
805 " (nmp->last_seen_nr=%u lchan->meas_rep_last_seen_nr=%u)\n",
806 nmp->arfcn, nmp->last_seen_nr, lchan->meas_rep_last_seen_nr);
807 return;
808 }
809
810 neighbor_bts = bts_by_arfcn_bsic(bts->network, nmp->arfcn, nmp->bsic);
811 if (!neighbor_bts) {
812 LOGPHOBTS(bts, LOGL_DEBUG, "neighbor ARFCN %u does not belong to this network\n",
813 nmp->arfcn);
814 return;
815 }
816
817 /* in case we have measurements of our bts, due to misconfiguration */
818 if (neighbor_bts == bts) {
819 LOGPHOBTS(bts, LOGL_ERROR, "Configuration error: this BTS appears as its own neighbor\n");
820 return;
821 }
822
823 /* caculate average rxlev for this cell over the window */
824 avg = neigh_meas_avg(nmp, ho_get_hodec2_rxlev_neigh_avg_win(bts->ho));
825
826 /* Heed rxlev hysteresis only if the RXLEV/RXQUAL/TA levels of the MS aren't critically bad and
827 * we're just looking for an improvement. If levels are critical, we desperately need a handover
828 * and thus skip the hysteresis check. */
829 if (!include_weaker_rxlev) {
830 unsigned int pwr_hyst = ho_get_hodec2_pwr_hysteresis(bts->ho);
831 if (avg <= (av_rxlev + pwr_hyst)) {
832 LOGPHOLCHAN(lchan, LOGL_DEBUG,
833 "BTS %d is not a candidate, because RX level (%d) is lower"
834 " or equal than current RX level (%d) + hysteresis (%d)\n",
835 neighbor_bts->nr, rxlev2dbm(avg), rxlev2dbm(av_rxlev), pwr_hyst);
836 return;
837 }
838 }
839
840 /* if the minimum level is not reached */
841 min_rxlev = ho_get_hodec2_min_rxlev(neighbor_bts->ho);
842 if (rxlev2dbm(avg) < min_rxlev) {
843 LOGPHOLCHAN(lchan, LOGL_DEBUG,
844 "BTS %d is not a candidate, because RX level (%d) is lower"
845 " than its minimum required RX level (%d)\n",
846 neighbor_bts->nr, rxlev2dbm(avg), min_rxlev);
847 return;
848 }
849
850 tchf_count = bts_count_free_ts(neighbor_bts, GSM_PCHAN_TCH_F);
851 tchh_count = bts_count_free_ts(neighbor_bts, GSM_PCHAN_TCH_H);
852 c = &clist[*candidates];
853 c->lchan = lchan;
854 c->bts = neighbor_bts;
855 c->requirements = check_requirements(lchan, neighbor_bts, tchf_count,
856 tchh_count);
857 c->avg = avg;
858 debug_candidate(c, 1, av_rxlev, tchf_count, tchh_count);
859 (*candidates)++;
860}
861
862static void collect_candidates_for_lchan(struct gsm_lchan *lchan,
863 struct ho_candidate *clist, unsigned int *candidates,
864 int *_av_rxlev, bool include_weaker_rxlev)
865{
866 struct gsm_bts *bts = lchan->ts->trx->bts;
867 int av_rxlev;
868 unsigned int candidates_was;
869 bool assignment;
870 bool handover;
871 int neighbors_count = 0;
872 unsigned int rxlev_avg_win = ho_get_hodec2_rxlev_avg_win(bts->ho);
873
874 OSMO_ASSERT(candidates);
875 candidates_was = *candidates;
876
877 /* caculate average rxlev for this cell over the window */
878 av_rxlev = get_meas_rep_avg(lchan,
879 ho_get_hodec2_full_tdma(bts->ho) ?
880 MEAS_REP_DL_RXLEV_FULL : MEAS_REP_DL_RXLEV_SUB,
881 rxlev_avg_win);
882 if (_av_rxlev)
883 *_av_rxlev = av_rxlev;
884
885 /* in case there is no measurment report (yet) */
886 if (av_rxlev < 0) {
887 LOGPHOLCHAN(lchan, LOGL_DEBUG, "Not collecting candidates, not enough measurements"
888 " (got %d, want %u)\n",
889 lchan->meas_rep_count, rxlev_avg_win);
890 return;
891 }
892
893 assignment = ho_get_hodec2_as_active(bts->ho);
894 handover = ho_get_ho_active(bts->ho);
895
896 LOGPHOLCHAN(lchan, LOGL_DEBUG, "Collecting candidates for%s%s%s\n",
897 assignment ? " Assignment" : "",
898 assignment && handover ? " and" : "",
899 handover ? " Handover" : "");
900
901 if (assignment)
902 collect_assignment_candidate(lchan, clist, candidates, av_rxlev);
903
904 if (handover) {
905 int i;
906 for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) {
907 collect_handover_candidate(lchan, &lchan->neigh_meas[i],
908 clist, candidates,
909 include_weaker_rxlev, av_rxlev, &neighbors_count);
910 }
911 }
912
913 LOGPHOLCHAN(lchan, LOGL_DEBUG, "adding %u candidates from %u neighbors, total %u\n",
914 *candidates - candidates_was, neighbors_count, *candidates);
915}
916
917/*
918 * Search for a alternative / better cell.
919 *
920 * Do not trigger handover/assignment on slots which have already ongoing
921 * handover/assignment processes. If no AFS improvement offset is given, try to
922 * maintain the same TCH rate, if available.
923 * Do not perform this process, if handover and assignment are disabled for
924 * the current cell.
925 * Do not perform handover, if the minimum acceptable RX level
926 * is not reched for this cell.
927 *
928 * If one or more 'better cells' are available, check the current and neighbor
929 * cell measurements in descending order of their RX levels (down-link):
930 *
931 * * Select the best candidate that fulfills requirement B (no congestion
932 * after handover/assignment) and trigger handover or assignment.
933 * * If no candidate fulfills requirement B, select the best candidate that
934 * fulfills requirement C (less or equally congested cells after handover)
935 * and trigger handover or assignment.
936 * * If no candidate fulfills requirement C, do not perform handover nor
937 * assignment.
938 *
939 * If the RX level (down-link) or RX quality (down-link) of the current cell is
940 * below minimum acceptable level, or if the maximum allowed timing advance is
941 * reached or exceeded, check the RX levels (down-link) of the current and
942 * neighbor cells in descending order of their levels: (bad BTS case)
943 *
944 * * Select the best candidate that fulfills requirement B (no congestion after
945 * handover/assignment) and trigger handover or assignment.
946 * * If no candidate fulfills requirement B, select the best candidate that
947 * fulfills requirement C (less or equally congested cells after handover)
948 * and trigger handover or assignment.
949 * * If no candidate fulfills requirement C, select the best candidate that
950 * fulfills requirement A (ignore congestion after handover or assignment)
951 * and trigger handover or assignment.
952 * * If no candidate fulfills requirement A, do not perform handover nor
953 * assignment.
954 *
955 * RX levels (down-link) of current and neighbor cells:
956 *
957 * * The RX levels of the current cell and neighbor cells are improved by a
958 * given offset, if AFS (AMR on TCH/F) is used or is a candidate for
959 * handover/assignment.
960 * * If AMR is used, the requirement for handover is checked for TCH/F and
961 * TCH/H. Both results (if any) are used as a candidate.
962 * * If AMR is used, the requirement for assignment to a different TCH slot
963 * rate is checked. The result (if available) is used as a candidate.
964 *
965 * If minimum RXLEV, minimum RXQUAL or maximum TA are exceeded, the caller should pass
966 * include_weaker_rxlev=true so that handover is performed despite congestion.
967 */
968static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_rxlev)
969{
970 struct gsm_bts *bts = lchan->ts->trx->bts;
971 int ahs = (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
972 && lchan->type == GSM_LCHAN_TCH_H);
973 int av_rxlev;
974 struct ho_candidate clist[1 + ARRAY_SIZE(lchan->neigh_meas)];
975 unsigned int candidates = 0;
976 int i;
977 struct ho_candidate *best_cand = NULL;
978 unsigned int best_better_db;
979 bool best_applied_afs_bias = false;
980 int better;
981
982 /* check for disabled handover/assignment at the current cell */
983 if (!ho_get_hodec2_as_active(bts->ho)
984 && !ho_get_ho_active(bts->ho)) {
985 LOGP(DHODEC, LOGL_INFO, "Skipping, Handover and Assignment both disabled in this cell\n");
986 return 0;
987 }
988
989 collect_candidates_for_lchan(lchan, clist, &candidates, &av_rxlev, include_weaker_rxlev);
990
991 /* If assignment is disabled and no neighbor cell report exists, or no neighbor cell qualifies,
992 * we may not even have any candidates. */
993 if (!candidates)
994 goto no_candidates;
995
996 /* select best candidate that fulfills requirement B: no congestion after HO */
997 best_better_db = 0;
998 for (i = 0; i < candidates; i++) {
999 int afs_bias;
1000 if (!(clist[i].requirements & REQUIREMENT_B_MASK))
1001 continue;
1002
1003 better = clist[i].avg - av_rxlev;
1004 /* Apply AFS bias? */
1005 afs_bias = 0;
1006 if (ahs && (clist[i].requirements & REQUIREMENT_B_TCHF))
1007 afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
1008 better += afs_bias;
1009 if (better > best_better_db) {
1010 best_cand = &clist[i];
1011 best_better_db = better;
1012 best_applied_afs_bias = afs_bias? true : false;
1013 }
1014 }
1015
1016 /* perform handover, if there is a candidate */
1017 if (best_cand) {
1018 LOGPHOLCHANTOBTS(lchan, best_cand->bts, LOGL_INFO, "Best candidate, RX level %d%s\n",
1019 rxlev2dbm(best_cand->avg),
1020 best_applied_afs_bias ? " (applied AHS -> AFS rxlev bias)" : "");
1021 return trigger_handover_or_assignment(lchan, best_cand->bts,
1022 best_cand->requirements & REQUIREMENT_B_MASK);
1023 }
1024
1025 /* select best candidate that fulfills requirement C: less or equal congestion after HO */
1026 best_better_db = 0;
1027 for (i = 0; i < candidates; i++) {
1028 int afs_bias;
1029 if (!(clist[i].requirements & REQUIREMENT_C_MASK))
1030 continue;
1031
1032 better = clist[i].avg - av_rxlev;
1033 /* Apply AFS bias? */
1034 afs_bias = 0;
1035 if (ahs && (clist[i].requirements & REQUIREMENT_C_TCHF))
1036 afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
1037 better += afs_bias;
1038 if (better > best_better_db) {
1039 best_cand = &clist[i];
1040 best_better_db = better;
1041 best_applied_afs_bias = afs_bias? true : false;
1042 }
1043 }
1044
1045 /* perform handover, if there is a candidate */
1046 if (best_cand) {
1047 LOGPHOLCHANTOBTS(lchan, best_cand->bts, LOGL_INFO, "Best candidate, RX level %d%s\n",
1048 rxlev2dbm(best_cand->avg),
1049 best_applied_afs_bias? " (applied AHS -> AFS rxlev bias)" : "");
1050 return trigger_handover_or_assignment(lchan, best_cand->bts,
1051 best_cand->requirements & REQUIREMENT_C_MASK);
1052 }
1053
1054 /* we are done in case the MS RXLEV/RXQUAL/TA aren't critical and we're avoiding congestion. */
1055 if (!include_weaker_rxlev)
1056 goto no_candidates;
1057
1058 /* Select best candidate that fulfills requirement A: can service the call.
1059 * From above we know that there are no options that avoid congestion. Here we're trying to find
1060 * *any* free lchan that has no critically low RXLEV and is able to handle the MS. */
1061 best_better_db = 0;
1062 for (i = 0; i < candidates; i++) {
1063 int afs_bias;
1064 if (!(clist[i].requirements & REQUIREMENT_A_MASK))
1065 continue;
1066
1067 better = clist[i].avg - av_rxlev;
1068 /* Apply AFS bias? */
1069 afs_bias = 0;
1070 if (ahs && (clist[i].requirements & REQUIREMENT_A_TCHF))
1071 afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
1072 better += afs_bias;
1073 if (better > best_better_db) {
1074 best_cand = &clist[i];
1075 best_better_db = better;
1076 best_applied_afs_bias = afs_bias? true : false;
1077 }
1078 }
1079
1080 /* perform handover, if there is a candidate */
1081 if (best_cand) {
1082 LOGPHOLCHANTOBTS(lchan, best_cand->bts, LOGL_INFO, "Best candidate, RX level %d"
1083 " with greater congestion found%s\n",
1084 rxlev2dbm(best_cand->avg),
1085 best_applied_afs_bias ? " (applied AHS -> AFS rxlev bias)" : "");
1086 return trigger_handover_or_assignment(lchan, best_cand->bts,
1087 best_cand->requirements & REQUIREMENT_A_MASK);
1088 }
1089
1090 /* Damn, all is congested, has too low RXLEV or cannot service the voice call due to codec
1091 * restrictions or because all lchans are taken. */
1092
1093no_candidates:
1094 if (include_weaker_rxlev)
1095 LOGPHOLCHAN(lchan, LOGL_INFO, "No alternative lchan found\n");
1096 else
1097 LOGPHOLCHAN(lchan, LOGL_INFO, "No better/less congested neighbor cell found\n");
1098
1099 return 0;
1100}
1101
1102/*
1103 * Handover/assignment check, if measurement report is received
1104 *
1105 * Do not trigger handover/assignment on slots which have already ongoing
1106 * handover/assignment processes.
1107 *
1108 * In case of handover triggered because maximum allowed timing advance is
1109 * exceeded, the handover penalty timer is started for the originating cell.
1110 *
1111 */
1112static void on_measurement_report(struct gsm_meas_rep *mr)
1113{
1114 struct gsm_lchan *lchan = mr->lchan;
1115 struct gsm_bts *bts = lchan->ts->trx->bts;
1116 int av_rxlev = -EINVAL, av_rxqual = -EINVAL;
Neels Hofmeyr00727552018-02-21 14:33:15 +01001117 unsigned int pwr_interval;
Neels Hofmeyr909e9722017-12-07 03:54:01 +01001118
1119 /* we currently only do handover for TCH channels */
1120 switch (mr->lchan->type) {
1121 case GSM_LCHAN_TCH_F:
1122 case GSM_LCHAN_TCH_H:
1123 break;
1124 default:
1125 return;
1126 }
1127
1128 if (log_check_level(DHODEC, LOGL_DEBUG)) {
1129 int i;
1130 LOGPHOLCHAN(lchan, LOGL_DEBUG, "MEASUREMENT REPORT\n");
1131 for (i = 0; i < mr->num_cell; i++) {
1132 struct gsm_meas_rep_cell *mrc = &mr->cell[i];
1133 LOGPHOLCHAN(lchan, LOGL_DEBUG,
1134 " %d: arfcn=%u bsic=%u neigh_idx=%u rxlev=%u flags=%x\n",
1135 i, mrc->arfcn, mrc->bsic, mrc->neigh_idx, mrc->rxlev, mrc->flags);
1136 }
1137 }
1138
1139 /* parse actual neighbor cell info */
1140 if (mr->num_cell > 0 && mr->num_cell < 7)
1141 process_meas_neigh(mr);
1142
1143 /* check for ongoing handover/assignment */
1144 if (!lchan->conn) {
1145 LOGPHOLCHAN(lchan, LOGL_ERROR, "Skipping, No subscriber connection???\n");
1146 return;
1147 }
1148 if (lchan->conn->secondary_lchan) {
1149 LOGPHOLCHAN(lchan, LOGL_INFO, "Skipping, Initial Assignment is still ongoing\n");
1150 return;
1151 }
1152 if (lchan->conn->ho_lchan) {
1153 LOGPHOLCHAN(lchan, LOGL_INFO, "Skipping, Handover already triggered\n");
1154 return;
1155 }
1156
1157 LOGPHOLCHAN(lchan, LOGL_DEBUG, "HODEC2: evaluating measurement report\n");
1158
1159 /* get average levels. if not enought measurements yet, value is < 0 */
1160 av_rxlev = get_meas_rep_avg(lchan,
1161 ho_get_hodec2_full_tdma(bts->ho) ?
1162 MEAS_REP_DL_RXLEV_FULL : MEAS_REP_DL_RXLEV_SUB,
1163 ho_get_hodec2_rxlev_avg_win(bts->ho));
1164 av_rxqual = get_meas_rep_avg(lchan,
1165 ho_get_hodec2_full_tdma(bts->ho) ?
1166 MEAS_REP_DL_RXQUAL_FULL : MEAS_REP_DL_RXQUAL_SUB,
1167 ho_get_hodec2_rxqual_avg_win(bts->ho));
1168 if (av_rxlev < 0 && av_rxqual < 0) {
1169 LOGPHOLCHAN(lchan, LOGL_INFO, "Skipping, Not enough recent measuements\n");
1170 return;
1171 }
1172 if (av_rxlev >= 0) {
1173 LOGPHOLCHAN(lchan, LOGL_DEBUG, "Measurement report: average RX level = %d\n",
1174 rxlev2dbm(av_rxlev));
1175 }
1176 if (av_rxqual >= 0) {
1177 LOGPHOLCHAN(lchan, LOGL_DEBUG, "Measurement report: average RX quality = %d\n",
1178 av_rxqual);
1179 }
1180
1181 /* improve levels in case of AFS, if defined */
1182 if (lchan->type == GSM_LCHAN_TCH_F
1183 && lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
1184 int rxlev_bias = ho_get_hodec2_afs_bias_rxlev(bts->ho);
1185 int rxqual_bias = ho_get_hodec2_afs_bias_rxqual(bts->ho);
1186 if (av_rxlev >= 0 && rxlev_bias) {
1187 int imp = av_rxlev + rxlev_bias;
1188 LOGPHOLCHAN(lchan, LOGL_INFO, "Virtually improving RX level from %d to %d,"
1189 " due to AFS bias\n", rxlev2dbm(av_rxlev), rxlev2dbm(imp));
1190 av_rxlev = imp;
1191 }
1192 if (av_rxqual >= 0 && rxqual_bias) {
1193 int imp = av_rxqual - rxqual_bias;
1194 if (imp < 0)
1195 imp = 0;
1196 LOGPHOLCHAN(lchan, LOGL_INFO, "Virtually improving RX quality from %d to %d,"
1197 " due to AFS bias\n", rxlev2dbm(av_rxqual), rxlev2dbm(imp));
1198 av_rxqual = imp;
1199 }
1200 }
1201
1202 /* Bad Quality */
1203 if (av_rxqual >= 0 && av_rxqual > ho_get_hodec2_min_rxqual(bts->ho)) {
1204 if (rxlev2dbm(av_rxlev) > -85) {
1205 global_ho_reason = HO_REASON_INTERFERENCE;
1206 LOGPHOLCHAN(lchan, LOGL_INFO, "Trying handover/assignment"
1207 " due to interference (bad quality)\n");
1208 } else {
1209 global_ho_reason = HO_REASON_BAD_QUALITY;
1210 LOGPHOLCHAN(lchan, LOGL_INFO, "Trying handover/assignment due to bad quality\n");
1211 }
1212 find_alternative_lchan(lchan, true);
1213 return;
1214 }
1215
1216 /* Low Level */
1217 if (av_rxlev >= 0 && rxlev2dbm(av_rxlev) < ho_get_hodec2_min_rxlev(bts->ho)) {
1218 global_ho_reason = HO_REASON_LOW_RXLEVEL;
1219 LOGPHOLCHAN(lchan, LOGL_INFO, "Attempting handover/assignment due to low rxlev\n");
1220 find_alternative_lchan(lchan, true);
1221 return;
1222 }
1223
1224 /* Max Distance */
1225 if (lchan->meas_rep_count > 0
1226 && lchan->rqd_ta > ho_get_hodec2_max_distance(bts->ho)) {
1227 global_ho_reason = HO_REASON_MAX_DISTANCE;
1228 LOGPHOLCHAN(lchan, LOGL_INFO, "Attempting handover due to high TA\n");
1229 /* start penalty timer to prevent comming back too
1230 * early. it must be started before selecting a better cell,
1231 * so there is no assignment selected, due to running
1232 * penalty timer. */
1233 conn_penalty_time_add(lchan->conn, bts, ho_get_hodec2_penalty_max_dist(bts->ho));
1234 find_alternative_lchan(lchan, true);
1235 return;
1236 }
1237
Neels Hofmeyr00727552018-02-21 14:33:15 +01001238 /* pwr_interval's range is 1-99, clarifying that no div-zero shall happen in modulo below: */
1239 pwr_interval = ho_get_hodec2_pwr_interval(bts->ho);
1240 OSMO_ASSERT(pwr_interval);
1241
Neels Hofmeyr909e9722017-12-07 03:54:01 +01001242 /* try handover to a better cell */
Neels Hofmeyr00727552018-02-21 14:33:15 +01001243 if (av_rxlev >= 0 && (mr->nr % pwr_interval) == 0) {
Neels Hofmeyr909e9722017-12-07 03:54:01 +01001244 LOGPHOLCHAN(lchan, LOGL_INFO, "Looking whether a cell has better RXLEV\n");
1245 global_ho_reason = HO_REASON_BETTER_CELL;
1246 find_alternative_lchan(lchan, false);
1247 }
1248}
1249
1250/*
1251 * Handover/assignment check after timer timeout:
1252 *
1253 * Even if handover process tries to prevent a congestion, a cell might get
1254 * congested due to new call setups or handovers to prevent loss of radio link.
1255 * A cell is congested, if not the minimum number of free slots are available.
1256 * The minimum number can be defined for TCH/F and TCH/H individually.
1257 *
1258 * Do not perform congestion check, if no minimum free slots are defined for
1259 * a cell.
1260 * Do not trigger handover/assignment on slots which have already ongoing
1261 * handover/assignment processes. If no AFS improvement offset is given, try to
1262 * maintain the same TCH rate, if available.
1263 * Do not perform this process, if handover and assignment are disabled for
1264 * the current cell.
1265 * Do not perform handover, if the minimum acceptable RX level
1266 * is not reched for this cell.
1267 * Only check candidates that will solve/reduce congestion.
1268 *
1269 * If a cell is congested, all slots are checked for all their RX levels
1270 * (down-link) of the current and neighbor cell measurements in descending
1271 * order of their RX levels:
1272 *
1273 * * Select the best candidate that fulfills requirement B (no congestion after
1274 * handover/assignment), trigger handover or assignment. Candidates that will
1275 * cause an assignment from AHS (AMR on TCH/H) to AFS (AMR on TCH/F) are
1276 * omitted.
1277 * o This process repeated until the minimum required number of free slots
1278 * are restored or if all cell measurements are checked. The process ends
1279 * then, otherwise:
1280 * * Select the worst candidate that fulfills requirement B, trigger
1281 * assignment. Note that only assignment candidates for changing from AHS to
1282 * AFS are left.
1283 * o This process repeated until the minimum required number of free slots
1284 * are restored or if all cell measurements are checked. The process ends
1285 * then, otherwise:
1286 * * Select the best candidates that fulfill requirement C (less or equally
1287 * congested cells after handover/assignment), trigger handover or
1288 * assignment. Candidates that will cause an assignment from AHS (AMR on
1289 * TCH/H) to AFS (AMR on TCH/F) are omitted.
1290 * o This process repeated until the minimum required number of free slots
1291 * are restored or if all cell measurements are checked. The process ends
1292 * then, otherwise:
1293 * * Select the worst candidate that fulfills requirement C, trigger
1294 * assignment. Note that only assignment candidates for changing from AHS to
1295 * AFS are left.
1296 * o This process repeated until the minimum required number of free slots
1297 * are restored or if all cell measurements are checked.
1298 */
1299static int bts_resolve_congestion(struct gsm_bts *bts, int tchf_congestion, int tchh_congestion)
1300{
1301 struct gsm_lchan *lc;
1302 struct gsm_bts_trx *trx;
1303 struct gsm_bts_trx_ts *ts;
1304 int i, j;
1305 struct ho_candidate *clist;
1306 unsigned int candidates;
1307 struct ho_candidate *best_cand = NULL, *worst_cand = NULL;
1308 struct gsm_lchan *delete_lchan = NULL;
1309 unsigned int best_avg_db, worst_avg_db;
1310 int avg;
1311 int rc = 0;
1312 int any_ho = 0;
1313 int is_improved = 0;
1314
1315 if (tchf_congestion < 0)
1316 tchf_congestion = 0;
1317 if (tchh_congestion < 0)
1318 tchh_congestion = 0;
1319
1320 LOGPHOBTS(bts, LOGL_INFO, "congested: %d TCH/F and %d TCH/H should be moved\n",
1321 tchf_congestion, tchh_congestion);
1322
1323 /* allocate array of all bts */
1324 clist = talloc_zero_array(tall_bsc_ctx, struct ho_candidate,
1325 bts->num_trx * 8 * 2 * (1 + ARRAY_SIZE(lc->neigh_meas)));
1326 if (!clist)
1327 return 0;
1328
1329 candidates = 0;
1330
1331 /* loop through all active lchan and collect candidates */
1332 llist_for_each_entry(trx, &bts->trx_list, list) {
1333 if (!trx_is_usable(trx))
1334 continue;
1335
1336 for (i = 0; i < 8; i++) {
1337 ts = &trx->ts[i];
1338 if (!ts_is_usable(ts))
1339 continue;
1340
1341 /* (Do not consider dynamic TS that are in PDCH mode) */
1342 switch (ts_pchan(ts)) {
1343 case GSM_PCHAN_TCH_F:
1344 lc = &ts->lchan[0];
1345 /* omit if channel not active */
1346 if (lc->type != GSM_LCHAN_TCH_F
1347 || lc->state != LCHAN_S_ACTIVE)
1348 break;
1349 /* omit if there is an ongoing ho/as */
1350 if (!lc->conn || lc->conn->secondary_lchan
1351 || lc->conn->ho_lchan)
1352 break;
1353 /* We desperately want to resolve congestion, ignore rxlev when
1354 * collecting candidates by passing include_weaker_rxlev=true. */
1355 collect_candidates_for_lchan(lc, clist, &candidates, NULL, true);
1356 break;
1357 case GSM_PCHAN_TCH_H:
1358 for (j = 0; j < 2; j++) {
1359 lc = &ts->lchan[j];
1360 /* omit if channel not active */
1361 if (lc->type != GSM_LCHAN_TCH_H
1362 || lc->state != LCHAN_S_ACTIVE)
1363 continue;
1364 /* omit of there is an ongoing ho/as */
1365 if (!lc->conn
1366 || lc->conn->secondary_lchan
1367 || lc->conn->ho_lchan)
1368 continue;
1369 /* We desperately want to resolve congestion, ignore rxlev when
1370 * collecting candidates by passing include_weaker_rxlev=true. */
1371 collect_candidates_for_lchan(lc, clist, &candidates, NULL, true);
1372 }
1373 break;
1374 default:
1375 break;
1376 }
1377 }
1378 }
1379
1380 if (!candidates) {
1381 LOGPHOBTS(bts, LOGL_DEBUG, "No neighbor cells qualify to solve congestion\n");
1382 goto exit;
1383 }
1384 if (log_check_level(DHODEC, LOGL_DEBUG)) {
1385 LOGPHOBTS(bts, LOGL_DEBUG, "Considering %u candidates to solve congestion:\n", candidates);
1386 for (i = 0; i < candidates; i++) {
1387 LOGPHOLCHANTOBTS(clist[i].lchan, clist[i].bts, LOGL_DEBUG,
1388 "#%d: req=0x%x avg-rxlev=%d\n",
1389 i, clist[i].requirements, clist[i].avg);
1390 }
1391 }
1392
1393#if 0
1394next_b1:
1395#endif
1396 /* select best candidate that fulfills requirement B,
1397 * omit change from AHS to AFS */
1398 best_avg_db = 0;
1399 for (i = 0; i < candidates; i++) {
1400 /* delete subscriber that just have handovered */
1401 if (clist[i].lchan == delete_lchan)
1402 clist[i].lchan = NULL;
1403 /* omit all subscribers that are handovered */
1404 if (!clist[i].lchan)
1405 continue;
1406
1407 if (!(clist[i].requirements & REQUIREMENT_B_MASK))
1408 continue;
1409 /* omit assignment from AHS to AFS */
1410 if (clist[i].lchan->ts->trx->bts == clist[i].bts
1411 && clist[i].lchan->type == GSM_LCHAN_TCH_H
1412 && (clist[i].requirements & REQUIREMENT_B_TCHF))
1413 continue;
1414 /* omit candidates that will not solve/reduce congestion */
1415 if (clist[i].lchan->type == GSM_LCHAN_TCH_F
1416 && tchf_congestion <= 0)
1417 continue;
1418 if (clist[i].lchan->type == GSM_LCHAN_TCH_H
1419 && tchh_congestion <= 0)
1420 continue;
1421
1422 avg = clist[i].avg;
1423 /* improve AHS */
1424 if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
1425 && clist[i].lchan->type == GSM_LCHAN_TCH_H
1426 && (clist[i].requirements & REQUIREMENT_B_TCHF)) {
1427 avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
1428 is_improved = 1;
1429 } else
1430 is_improved = 0;
1431 LOGP(DHODEC, LOGL_DEBUG, "candidate %d: avg=%d best_avg_db=%d\n", i, avg, best_avg_db);
1432 if (avg > best_avg_db) {
1433 best_cand = &clist[i];
1434 best_avg_db = avg;
1435 }
1436 }
1437
1438 /* perform handover, if there is a candidate */
1439 if (best_cand) {
1440 any_ho = 1;
1441 LOGPHOLCHAN(best_cand->lchan, LOGL_INFO,
1442 "Best candidate BTS %u (RX level %d) without congestion found\n",
1443 best_cand->bts->nr, rxlev2dbm(best_cand->avg));
1444 if (is_improved)
1445 LOGP(DHODEC, LOGL_INFO, "(is improved due to "
1446 "AHS -> AFS)\n");
1447 trigger_handover_or_assignment(best_cand->lchan, best_cand->bts,
1448 best_cand->requirements & REQUIREMENT_B_MASK);
1449#if 0
1450 /* if there is still congestion, mark lchan as deleted
1451 * and redo this process */
1452 if (best_cand->lchan->type == GSM_LCHAN_TCH_H)
1453 tchh_congestion--;
1454 else
1455 tchf_congestion--;
1456 if (tchf_congestion > 0 || tchh_congestion > 0) {
1457 delete_lchan = best_cand->lchan;
1458 best_cand = NULL;
1459 goto next_b1;
1460 }
1461#else
1462 /* must exit here, because triggering handover/assignment
1463 * will cause change in requirements. more check for this
1464 * bts is performed in the next iteration.
1465 */
1466#endif
1467 goto exit;
1468 }
1469
1470 LOGPHOBTS(bts, LOGL_DEBUG, "Did not find a best candidate that fulfills requirement B"
1471 " (omitting change from AHS to AFS)\n");
1472
1473#if 0
1474next_b2:
1475#endif
1476 /* select worst candidate that fulfills requirement B,
1477 * select candidates that change from AHS to AFS only */
1478 if (tchh_congestion > 0) {
1479 /* since this will only check half rate channels, it will
1480 * only need to be checked, if tchh is congested */
1481 worst_avg_db = 999;
1482 for (i = 0; i < candidates; i++) {
1483 /* delete subscriber that just have handovered */
1484 if (clist[i].lchan == delete_lchan)
1485 clist[i].lchan = NULL;
1486 /* omit all subscribers that are handovered */
1487 if (!clist[i].lchan)
1488 continue;
1489
1490 if (!(clist[i].requirements & REQUIREMENT_B_MASK))
1491 continue;
1492 /* omit all but assignment from AHS to AFS */
1493 if (clist[i].lchan->ts->trx->bts != clist[i].bts
1494 || clist[i].lchan->type != GSM_LCHAN_TCH_H
1495 || !(clist[i].requirements & REQUIREMENT_B_TCHF))
1496 continue;
1497
1498 avg = clist[i].avg;
1499 /* improve AHS */
1500 if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
1501 && clist[i].lchan->type == GSM_LCHAN_TCH_H) {
1502 avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
1503 is_improved = 1;
1504 } else
1505 is_improved = 0;
1506 LOGP(DHODEC, LOGL_DEBUG, "candidate %d: avg=%d worst_avg_db=%d\n", i, avg,
1507 worst_avg_db);
1508 if (avg < worst_avg_db) {
1509 worst_cand = &clist[i];
1510 worst_avg_db = avg;
1511 }
1512 }
1513 }
1514
1515 /* perform handover, if there is a candidate */
1516 if (worst_cand) {
1517 any_ho = 1;
1518 LOGP(DHODEC, LOGL_INFO, "Worst candidate for assignment "
1519 "(RX level %d) from TCH/H -> TCH/F without congestion "
1520 "found\n", rxlev2dbm(worst_cand->avg));
1521 if (is_improved)
1522 LOGP(DHODEC, LOGL_INFO, "(is improved due to "
1523 "AHS -> AFS)\n");
1524 trigger_handover_or_assignment(worst_cand->lchan,
1525 worst_cand->bts,
1526 worst_cand->requirements & REQUIREMENT_B_MASK);
1527#if 0
1528 /* if there is still congestion, mark lchan as deleted
1529 * and redo this process */
1530 tchh_congestion--;
1531 if (tchh_congestion > 0) {
1532 delete_lchan = worst_cand->lchan;
1533 best_cand = NULL;
1534 goto next_b2;
1535 }
1536#else
1537 /* must exit here, because triggering handover/assignment
1538 * will cause change in requirements. more check for this
1539 * bts is performed in the next iteration.
1540 */
1541#endif
1542 goto exit;
1543 }
1544
1545 LOGPHOBTS(bts, LOGL_DEBUG, "Did not find a worst candidate that fulfills requirement B,"
1546 " selecting candidates that change from AHS to AFS only\n");
1547
1548#if 0
1549next_c1:
1550#endif
1551 /* select best candidate that fulfills requirement C,
1552 * omit change from AHS to AFS */
1553 best_avg_db = 0;
1554 for (i = 0; i < candidates; i++) {
1555 /* delete subscriber that just have handovered */
1556 if (clist[i].lchan == delete_lchan)
1557 clist[i].lchan = NULL;
1558 /* omit all subscribers that are handovered */
1559 if (!clist[i].lchan)
1560 continue;
1561
1562 if (!(clist[i].requirements & REQUIREMENT_C_MASK))
1563 continue;
1564 /* omit assignment from AHS to AFS */
1565 if (clist[i].lchan->ts->trx->bts == clist[i].bts
1566 && clist[i].lchan->type == GSM_LCHAN_TCH_H
1567 && (clist[i].requirements & REQUIREMENT_C_TCHF))
1568 continue;
1569 /* omit candidates that will not solve/reduce congestion */
1570 if (clist[i].lchan->type == GSM_LCHAN_TCH_F
1571 && tchf_congestion <= 0)
1572 continue;
1573 if (clist[i].lchan->type == GSM_LCHAN_TCH_H
1574 && tchh_congestion <= 0)
1575 continue;
1576
1577 avg = clist[i].avg;
1578 /* improve AHS */
1579 if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
1580 && clist[i].lchan->type == GSM_LCHAN_TCH_H
1581 && (clist[i].requirements & REQUIREMENT_C_TCHF)) {
1582 avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
1583 is_improved = 1;
1584 } else
1585 is_improved = 0;
1586 LOGP(DHODEC, LOGL_DEBUG, "candidate %d: avg=%d best_avg_db=%d\n", i, avg, best_avg_db);
1587 if (avg > best_avg_db) {
1588 best_cand = &clist[i];
1589 best_avg_db = avg;
1590 }
1591 }
1592
1593 /* perform handover, if there is a candidate */
1594 if (best_cand) {
1595 any_ho = 1;
1596 LOGP(DHODEC, LOGL_INFO, "Best candidate BTS %d (RX level %d) "
1597 "with less or equal congestion found\n",
1598 best_cand->bts->nr, rxlev2dbm(best_cand->avg));
1599 if (is_improved)
1600 LOGP(DHODEC, LOGL_INFO, "(is improved due to "
1601 "AHS -> AFS)\n");
1602 trigger_handover_or_assignment(best_cand->lchan, best_cand->bts,
1603 best_cand->requirements & REQUIREMENT_C_MASK);
1604#if 0
1605 /* if there is still congestion, mark lchan as deleted
1606 * and redo this process */
1607 if (best_cand->lchan->type == GSM_LCHAN_TCH_H)
1608 tchh_congestion--;
1609 else
1610 tchf_congestion--;
1611 if (tchf_congestion > 0 || tchh_congestion > 0) {
1612 delete_lchan = best_cand->lchan;
1613 best_cand = NULL;
1614 goto next_c1;
1615 }
1616#else
1617 /* must exit here, because triggering handover/assignment
1618 * will cause change in requirements. more check for this
1619 * bts is performed in the next iteration.
1620 */
1621#endif
1622 goto exit;
1623 }
1624
1625 LOGPHOBTS(bts, LOGL_DEBUG, "Did not find a best candidate that fulfills requirement C"
1626 " (omitting change from AHS to AFS)\n");
1627
1628#if 0
1629next_c2:
1630#endif
1631 /* select worst candidate that fulfills requirement C,
1632 * select candidates that change from AHS to AFS only */
1633 if (tchh_congestion > 0) {
1634 /* since this will only check half rate channels, it will
1635 * only need to be checked, if tchh is congested */
1636 worst_avg_db = 999;
1637 for (i = 0; i < candidates; i++) {
1638 /* delete subscriber that just have handovered */
1639 if (clist[i].lchan == delete_lchan)
1640 clist[i].lchan = NULL;
1641 /* omit all subscribers that are handovered */
1642 if (!clist[i].lchan)
1643 continue;
1644
1645 if (!(clist[i].requirements & REQUIREMENT_C_MASK))
1646 continue;
1647 /* omit all but assignment from AHS to AFS */
1648 if (clist[i].lchan->ts->trx->bts != clist[i].bts
1649 || clist[i].lchan->type != GSM_LCHAN_TCH_H
1650 || !(clist[i].requirements & REQUIREMENT_C_TCHF))
1651 continue;
1652
1653 avg = clist[i].avg;
1654 /* improve AHS */
1655 if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
1656 && clist[i].lchan->type == GSM_LCHAN_TCH_H) {
1657 avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
1658 is_improved = 1;
1659 } else
1660 is_improved = 0;
1661 LOGP(DHODEC, LOGL_DEBUG, "candidate %d: avg=%d worst_avg_db=%d\n", i, avg,
1662 worst_avg_db);
1663 if (avg < worst_avg_db) {
1664 worst_cand = &clist[i];
1665 worst_avg_db = avg;
1666 }
1667 }
1668 }
1669
1670 /* perform handover, if there is a candidate */
1671 if (worst_cand) {
1672 any_ho = 1;
1673 LOGP(DHODEC, LOGL_INFO, "Worst candidate for assignment "
1674 "(RX level %d) from TCH/H -> TCH/F with less or equal "
1675 "congestion found\n", rxlev2dbm(worst_cand->avg));
1676 if (is_improved)
1677 LOGP(DHODEC, LOGL_INFO, "(is improved due to "
1678 "AHS -> AFS)\n");
1679 trigger_handover_or_assignment(worst_cand->lchan,
1680 worst_cand->bts,
1681 worst_cand->requirements & REQUIREMENT_C_MASK);
1682#if 0
1683 /* if there is still congestion, mark lchan as deleted
1684 * and redo this process */
1685 tchh_congestion--;
1686 if (tchh_congestion > 0) {
1687 delete_lchan = worst_cand->lchan;
1688 worst_cand = NULL;
1689 goto next_c2;
1690 }
1691#else
1692 /* must exit here, because triggering handover/assignment
1693 * will cause change in requirements. more check for this
1694 * bts is performed in the next iteration.
1695 */
1696#endif
1697 goto exit;
1698 }
1699 LOGPHOBTS(bts, LOGL_DEBUG, "Did not find a worst candidate that fulfills requirement C,"
1700 " selecting candidates that change from AHS to AFS only\n");
1701
1702
1703exit:
1704 /* free array */
1705 talloc_free(clist);
1706
1707 if (tchf_congestion <= 0 && tchh_congestion <= 0)
1708 LOGP(DHODEC, LOGL_INFO, "Congestion at BTS %d solved!\n",
1709 bts->nr);
1710 else if (any_ho)
1711 LOGP(DHODEC, LOGL_INFO, "Congestion at BTS %d reduced!\n",
1712 bts->nr);
1713 else
1714 LOGP(DHODEC, LOGL_INFO, "Congestion at BTS %d can't be reduced/solved!\n", bts->nr);
1715
1716 return rc;
1717}
1718
1719static void bts_congestion_check(struct gsm_bts *bts)
1720{
1721 int min_free_tchf, min_free_tchh;
1722 int tchf_count, tchh_count;
1723
1724 global_ho_reason = HO_REASON_CONGESTION;
1725
1726 /* only check BTS if TRX 0 is usable */
1727 if (!trx_is_usable(bts->c0)) {
1728 LOGPHOBTS(bts, LOGL_DEBUG, "No congestion check: TRX 0 not usable\n");
1729 return;
1730 }
1731
1732 /* only check BTS if handover or assignment is enabled */
1733 if (!ho_get_hodec2_as_active(bts->ho)
1734 && !ho_get_ho_active(bts->ho)) {
1735 LOGPHOBTS(bts, LOGL_DEBUG, "No congestion check: Assignment and Handover both disabled\n");
1736 return;
1737 }
1738
1739 min_free_tchf = ho_get_hodec2_tchf_min_slots(bts->ho);
1740 min_free_tchh = ho_get_hodec2_tchh_min_slots(bts->ho);
1741
1742 /* only check BTS with congestion level set */
1743 if (!min_free_tchf && !min_free_tchh) {
1744 LOGPHOBTS(bts, LOGL_DEBUG, "No congestion check: no minimum for free TCH/F nor TCH/H set\n");
1745 return;
1746 }
1747
1748 tchf_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_F);
1749 tchh_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_H);
1750 LOGPHOBTS(bts, LOGL_INFO, "Congestion check: (free/want-free) TCH/F=%d/%d TCH/H=%d/%d\n",
1751 tchf_count, min_free_tchf, tchh_count, min_free_tchh);
1752
1753 /* only check BTS if congested */
1754 if (tchf_count >= min_free_tchf && tchh_count >= min_free_tchh) {
1755 LOGPHOBTS(bts, LOGL_DEBUG, "Not congested\n");
1756 return;
1757 }
1758
1759 LOGPHOBTS(bts, LOGL_DEBUG, "Attempting to resolve congestion...\n");
1760 bts_resolve_congestion(bts, min_free_tchf - tchf_count, min_free_tchh - tchh_count);
1761}
1762
1763void hodec2_congestion_check(struct gsm_network *net)
1764{
1765 struct gsm_bts *bts;
1766
1767 llist_for_each_entry(bts, &net->bts_list, list)
1768 bts_congestion_check(bts);
1769}
1770
1771static void congestion_check_cb(void *arg)
1772{
1773 struct gsm_network *net = arg;
1774 hodec2_congestion_check(net);
1775 reinit_congestion_timer(net);
1776}
1777
1778void on_ho_chan_activ_nack(struct bsc_handover *ho)
1779{
1780 struct gsm_bts *new_bts = ho->new_lchan->ts->trx->bts;
1781
1782 LOGPHO(ho, LOGL_ERROR, "Channel Activate Nack for %s, starting penalty timer\n", ho->inter_cell? "Handover" : "Assignment");
1783
1784 /* if channel failed, wait 10 seconds before allowing to retry handover */
1785 conn_penalty_time_add(ho->old_lchan->conn, new_bts, 10); /* FIXME configurable */
1786}
1787
1788void on_ho_failure(struct bsc_handover *ho)
1789{
1790 struct gsm_bts *old_bts = ho->old_lchan->ts->trx->bts;
1791 struct gsm_bts *new_bts = ho->new_lchan->ts->trx->bts;
1792 struct gsm_subscriber_connection *conn = ho->old_lchan->conn;
1793
1794 if (!conn) {
1795 LOGPHO(ho, LOGL_ERROR, "HO failure, but no conn");
1796 return;
1797 }
1798
1799 if (conn->hodec2.failures >= ho_get_hodec2_retries(old_bts->ho)) {
1800 int penalty = ho->inter_cell
1801 ? ho_get_hodec2_penalty_failed_ho(old_bts->ho)
1802 : ho_get_hodec2_penalty_failed_as(old_bts->ho);
1803 LOGPHO(ho, LOGL_NOTICE, "%s failed, starting penalty timer (%d s)\n",
1804 ho->inter_cell ? "Handover" : "Assignment",
1805 penalty);
1806 conn->hodec2.failures = 0;
1807 conn_penalty_time_add(conn, new_bts, penalty);
1808 } else {
1809 conn->hodec2.failures++;
1810 LOGPHO(ho, LOGL_NOTICE, "%s failed, allowing handover decision to try again"
1811 " (%d/%d attempts)\n",
1812 ho->inter_cell ? "Handover" : "Assignment",
1813 conn->hodec2.failures, ho_get_hodec2_retries(old_bts->ho));
1814 }
1815}
1816
1817struct handover_decision_callbacks hodec2_callbacks = {
1818 .hodec_id = 2,
1819 .on_measurement_report = on_measurement_report,
1820 .on_ho_chan_activ_nack = on_ho_chan_activ_nack,
1821 .on_ho_failure = on_ho_failure,
1822};
1823
1824void hodec2_init(struct gsm_network *net)
1825{
1826 handover_decision_callbacks_register(&hodec2_callbacks);
1827 hodec2_initialized = true;
1828 reinit_congestion_timer(net);
1829}