Commit beeb9dea authored by Kirill Smelkov's avatar Kirill Smelkov

amari.drb: Move handling of xl_use_avg and rank from _QCI_Flow to higher level at _UE

Since 2a016d48 (Draft support for E-UTRAN IP Throughput KPI) there was a
hardcoded limitation that x.drb_stats generation works with 1-cell
configurations only. However we do use multicell eNB configurations and
on such configurations `xamari xlog x.drb_stats` was failing on eNB
side with

    raise RuntimeError(("ue #%s belongs to %d cells;  "+
        "but only single-cell configurations are supported") % (ue_id, len(ju(['cells']))))

Start fixing that.

As the first preparatory step to support multiple cells with x.drb_stats
reorganize amari/drb.py code a bit: _QCI_Flow works at whole QCI
transmission level which can aggregate and cover multiple cells, but
xl_use_avg and rank are per cell things and they can be different for
different cells.

-> Start preparing to handle them in per-cell way by adjusting the code
   to take those values into account at higher level in computation stack
   where we still have cell context.

No support for multicell yet - only preparatory non-functional changes
currently.
parent 1b9d4e1a
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2023 Nexedi SA and Contributors. # Copyright (C) 2023-2024 Nexedi SA and Contributors.
# Kirill Smelkov <kirr@nexedi.com> # Kirill Smelkov <kirr@nexedi.com>
# #
# This program is free software: you can Use, Study, Modify and Redistribute # This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your # it under the terms of the GNU General Public License version 3, or (at your
...@@ -334,6 +334,13 @@ def _update_qci_flows(ue, bitnext, qci_samples): ...@@ -334,6 +334,13 @@ def _update_qci_flows(ue, bitnext, qci_samples):
for (δt, tx_bytes, tx, u) in bitnext: for (δt, tx_bytes, tx, u) in bitnext:
qflows_live = set() # of qci qci flows that get updated from current utx entry qflows_live = set() # of qci qci flows that get updated from current utx entry
# estimate time for current transmission
# normalize transport blocks to time in TTI units (if it is e.g.
# 2x2 mimo, we have 2x more transport blocks).
δt_tti = δt / tti
tx /= u.rank
tx = min(tx, δt_tti) # protection (should not happen)
# it might happen that even with correct bitsync we could end up with receiving tx=0 here. # it might happen that even with correct bitsync we could end up with receiving tx=0 here.
# for example it happens if finish interrupts proper bitsync workflow e.g. as follows: # for example it happens if finish interrupts proper bitsync workflow e.g. as follows:
# #
...@@ -344,9 +351,21 @@ def _update_qci_flows(ue, bitnext, qci_samples): ...@@ -344,9 +351,21 @@ def _update_qci_flows(ue, bitnext, qci_samples):
# if we see #tx = 0 we say that it might be anything in between 1 and δt. # if we see #tx = 0 we say that it might be anything in between 1 and δt.
tx_lo = tx_hi = tx tx_lo = tx_hi = tx
if tx == 0: if tx == 0:
tx_hi = δt/tti tx_hi = δt_tti
tx_lo = min(1, tx_hi) tx_lo = min(1, tx_hi)
# tx time on the cell is somewhere in [tx, δt_tti]
if u.xl_use_avg < 0.9:
# not congested: it likely took the time to transmit ≈ tx
pass
else:
# potentially congested: we don't know how much congested it is and
# which QCIs are affected more and which less
# -> all we can say tx_time is only somewhere in between limits
tx_hi = δt_tti
# share/distribute tx time over all QCIs.
for qci, tx_bytes_qci in u.qtx_bytes.items(): for qci, tx_bytes_qci in u.qtx_bytes.items():
qflows_live.add(qci) qflows_live.add(qci)
...@@ -354,8 +373,6 @@ def _update_qci_flows(ue, bitnext, qci_samples): ...@@ -354,8 +373,6 @@ def _update_qci_flows(ue, bitnext, qci_samples):
if qf is None: if qf is None:
qf = ue.qci_flows[qci] = _QCI_Flow() qf = ue.qci_flows[qci] = _QCI_Flow()
# share/distribute #tx transport blocks over all QCIs.
#
# Consider two streams "x" and "o" and how LTE scheduler might # Consider two streams "x" and "o" and how LTE scheduler might
# place them into resource map: if the streams have the same # place them into resource map: if the streams have the same
# priority they might be scheduled e.g. as shown in case "a". # priority they might be scheduled e.g. as shown in case "a".
...@@ -387,7 +404,7 @@ def _update_qci_flows(ue, bitnext, qci_samples): ...@@ -387,7 +404,7 @@ def _update_qci_flows(ue, bitnext, qci_samples):
if qtx_lo > tx_hi: # e.g. 6.6 * 11308 / 11308 = 6.6 + ~1e-15 if qtx_lo > tx_hi: # e.g. 6.6 * 11308 / 11308 = 6.6 + ~1e-15
qtx_lo -= 1e-4 qtx_lo -= 1e-4
assert 0 < qtx_lo <= tx_hi, (qtx_lo, tx_hi, tx_bytes_qci, tx_bytes) assert 0 < qtx_lo <= tx_hi, (qtx_lo, tx_hi, tx_bytes_qci, tx_bytes)
_ = qf.update(δt, tx_bytes_qci, qtx_lo, tx_hi, u.rank, u.xl_use_avg) _ = qf.update(δt, tx_bytes_qci, qtx_lo, tx_hi)
for sample in _: for sample in _:
qci_samples.setdefault(qci, []).append(sample) qci_samples.setdefault(qci, []).append(sample)
...@@ -407,39 +424,22 @@ def __init__(qf): ...@@ -407,39 +424,22 @@ def __init__(qf):
qf.tx_time_err = 0 qf.tx_time_err = 0
# update updates flow with information that so many bytes were transmitted during # update updates flow with information that so many bytes were transmitted during
# δt with using #tx transport blocks somewhere in [tx_lo,tx_hi] and with # δt with using tx transmission time somewhere in [tx_lo,tx_hi].
# specified rank. It is also known that overall average usage of resource
# blocks corresponding to tx direction in the resource map is xl_use_avg.
@func(_QCI_Flow) @func(_QCI_Flow)
def update(qf, δt, tx_bytes, tx_lo, tx_hi, rank, xl_use_avg): # -> []Sample def update(qf, δt, tx_bytes, tx_lo, tx_hi): # -> []Sample
#_debug('QF.update %.2ftti %5db %.1f-%.1ftx %drank %.2fuse' % (δt/tti, tx_bytes, tx_lo, tx_hi, rank, xl_use_avg)) #_debug('QF.update %.2ftti %5db %.1f-%.1ftx' % (δt/tti, tx_bytes, tx_lo, tx_hi))
tx_lo /= rank # normalize TB to TTI (if it is e.g. 2x2 mimo, we have 2x more transport blocks)
tx_hi /= rank
vout = [] vout = []
s = qf._update(δt, tx_bytes, tx_lo, tx_hi, xl_use_avg) s = qf._update(δt, tx_bytes, tx_lo, tx_hi)
if s is not None: if s is not None:
vout.append(s) vout.append(s)
return vout return vout
@func(_QCI_Flow) @func(_QCI_Flow)
def _update(qf, δt, tx_bytes, tx_lo, tx_hi, xl_use_avg): # -> ?Sample def _update(qf, δt, tx_bytes, tx_lo, tx_hi): # -> ?Sample
assert tx_bytes > 0 assert tx_bytes > 0
δt_tti = δt / tti δt_tti = δt / tti
tx_lo = min(tx_lo, δt_tti) # protection (should not happen)
tx_hi = min(tx_hi, δt_tti) # protection (should not happen)
# tx time is somewhere in [tx, δt_tti]
if xl_use_avg < 0.9:
# not congested: it likely took the time to transmit ≈ #tx
pass
else:
# potentially congested: we don't know how much congested it is and
# which QCIs are affected more and which less
# -> all we can say tx_time is only somewhere in between limits
tx_hi = δt_tti
tx_time = (tx_lo + tx_hi) / 2 * tti tx_time = (tx_lo + tx_hi) / 2 * tti
tx_time_err = (tx_hi - tx_lo) / 2 * tti tx_time_err = (tx_hi - tx_lo) / 2 * tti
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment