From 7f208378ce55e27e56ee52ea5975b37260a355aa Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 11 Oct 2013 13:33:59 +0200 Subject: dyn PDCH: add do_pdch_defrag() in new pdch_defrag.[hc] Will be used in the subsequent commit. Original patch by jolly (1c8a3d828612a6bea343a268072a7dddf4a22480), with extensive review and changes by nhofmeyr: * remove code dup: common find_lchan() replaces find_shared_forward() and find_shared_reverse() and simplifies invocation. * use LOGTSP() instead of debug_bts_trx_ts(), tweak debug logging (missing \n, in-code line breaking). * reword comment for do_pdch_defrag(). * remove code dup: calculate move direction by maths instead of 4 conditionals, and have only a single debug log for "does not lower fragmentation". * add two FIXME comments. Change-Id: Idcaf7ac40cbaf83593946cfe24a6a18a6a688fd5 Patch-by: Andreas Eversberg Tweaked-by: Neels Hofmeyr --- openbsc/include/openbsc/Makefile.am | 3 +- openbsc/include/openbsc/pdch_defrag.h | 3 + openbsc/src/libbsc/Makefile.am | 3 +- openbsc/src/libbsc/pdch_defrag.c | 159 ++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 openbsc/include/openbsc/pdch_defrag.h create mode 100644 openbsc/src/libbsc/pdch_defrag.c diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index c272b14de..5071b411e 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -18,7 +18,8 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ gprs_gb_parse.h smpp.h meas_feed.h \ gprs_gsup_client.h bsc_msg_filter.h \ oap.h oap_messages.h \ - gtphub.h + gtphub.h \ + pdch_defrag.h openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/pdch_defrag.h b/openbsc/include/openbsc/pdch_defrag.h new file mode 100644 index 000000000..b7de2260d --- /dev/null +++ b/openbsc/include/openbsc/pdch_defrag.h @@ -0,0 +1,3 @@ +#pragma once + +void do_pdch_defrag(struct gsm_bts *bts); diff --git a/openbsc/src/libbsc/Makefile.am b/openbsc/src/libbsc/Makefile.am index 48880d965..ea0bddc6b 100644 --- a/openbsc/src/libbsc/Makefile.am +++ b/openbsc/src/libbsc/Makefile.am @@ -24,5 +24,6 @@ libbsc_a_SOURCES = abis_nm.c abis_nm_vty.c \ arfcn_range_encode.c bsc_ctrl_commands.c \ bsc_ctrl_lookup.c \ net_init.c \ - bsc_dyn_pdch.c + bsc_dyn_pdch.c \ + pdch_defrag.c diff --git a/openbsc/src/libbsc/pdch_defrag.c b/openbsc/src/libbsc/pdch_defrag.c new file mode 100644 index 000000000..f8b314542 --- /dev/null +++ b/openbsc/src/libbsc/pdch_defrag.c @@ -0,0 +1,159 @@ +/* (C) 2013 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +static struct gsm_lchan *find_lchan(struct gsm_bts *bts, + enum gsm_phys_chan_config type, + bool forward) +{ + struct gsm_bts_trx *trx; + struct gsm_lchan *lc; + int i, start, stop, dir; + + if (forward) { + /* check TS 0..7 */ + start = 0; + stop = 8; + dir = 1; + } else { + /* check TS 7..0 */ + start = 7; + stop = -1; + dir = -1; + } + + llist_for_each_entry(trx, &bts->trx_list, list) { + for (i = start; i != stop; i += dir) { + if (trx->ts[i].pchan == type) { + lc = &trx->ts[i].lchan[0]; + if (lc->state == LCHAN_S_ACTIVE) + return lc; + } + } + } + + return NULL; +} + +#define LOGTSP(ss, ll, ts, fmt, args...) \ + LOGP(ss, ll, "(BTS %u, TRX %u, TS %u) " fmt, \ + ts->trx->bts->nr, ts->trx->nr, ts->nr, \ + ##args) + +/* Defragment TCH/F+PDCH channels to remove gaps between active + * GSM_PCHAN_TCH_F_PDCH slots. Assign active connections to standard TCH/F or + * to consecutive TCH/F+PDCH channels. + * + * This function depends on being re-triggered whenever a channel has been + * released: each reassignment for defragmentation includes a channel release, + * which should trigger another invocation, and defragmentation will continue. + * + * Look up the last active TCH/F+PDCH (highest TRX/TS number). If there + * is a TCH/F or a free TCH/F+PDCH on a lower TRX/TS number, re-assign it. + * If allocation order is reversed, re-assign in reverse direction instead. + */ +void do_pdch_defrag(struct gsm_bts *bts) +{ + struct gsm_lchan *old_lchan = NULL, *new_lchan; + int rc; + int want_dir; + int is_dir; + + /* Find the "last" lchan in use, according to chan_alloc_reverse: if + * reversed, find the highest-numbered lchan == start looking from the + * back. */ + old_lchan = find_lchan(bts, GSM_PCHAN_TCH_F_PDCH, + bts->chan_alloc_reverse? true : false); + + /* if there is no candidate on GSM_PCHAN_TCH_F_PDCH, we are done */ + /* FIXME: for multi-TRX, if the first TRX's last lchan doesn't need + * re-assignment, the other TRXes are not going to be checked. */ + if (!old_lchan) + return; + + LOGTSP(DHODEC, LOGL_DEBUG, old_lchan->ts, + "A shared TCH/F+PDCH is in use, check for reassignment.\n"); + + /* allocate new lchan */ + new_lchan = lchan_alloc(bts, GSM_LCHAN_TCH_F, 0); + /* no free destination for candidate */ + if (!new_lchan) { + LOGTSP(DHODEC, LOGL_DEBUG, old_lchan->ts, + "-> Cannot move, no other TCH/F available.\n"); + return; + } + + /* if new TS is also a GSM_PCHAN_TCH_F_PDCH */ + if (new_lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH) { + LOGTSP(DHODEC, LOGL_DEBUG, new_lchan->ts, + "<- New channel for candidate is also a TCH/F+PDCH.\n"); + /* be sure to move to lower number (or to higher number in case + * of reverse allocation) */ + want_dir = bts->chan_alloc_reverse ? 1 : -1; + is_dir = new_lchan->ts->trx->nr - old_lchan->ts->trx->nr; + + if (is_dir == 0) { + /* TRX number stays the same, check TS numbers. */ + is_dir = new_lchan->ts->nr - old_lchan->ts->nr; + } + + /* If is_dir were 0 here, new_lchan == old_lchan, which should + * never happen. Even if it does, that would mean no decrease + * in fragmentation... */ + + if ((is_dir > 0) != (want_dir > 0)) { + LOGP(DHODEC, LOGL_DEBUG, + "-> Move does not lower fragmentation:" + " TRX %u TS %u to TRX %u TS %u%s\n", + old_lchan->ts->trx->nr, old_lchan->ts->nr, + new_lchan->ts->trx->nr, new_lchan->ts->nr, + bts->chan_alloc_reverse ? " (reverse allocation)" : ""); + lchan_free(new_lchan); + return; + } + } else { + LOGTSP(DHODEC, LOGL_DEBUG, new_lchan->ts, + "<- New channel for candidate is a TCH/F.\n"); + } + + rc = bsc_handover_start(old_lchan, new_lchan, new_lchan->ts->trx->bts); + if (rc) { + LOGP(DHODEC, LOGL_NOTICE, + "Cannot trigger assignment to defragment TCH/F+PDCH slots.\n"); + } else { + LOGP(DHODEC, LOGL_NOTICE, + "Triggered assignment to defragment TCH/F+PDCH slots.\n"); + } + + /* We do not need to take further action here regardless of + * re-assignment success or failure, because in both cases a channel is + * freed, and this defragmentation should be triggered again. + */ + /* FIXME: does re-assignment failure mean infinite looping/recursion!? */ +} + -- cgit v1.2.3