Compare commits

...

3 Commits

11 changed files with 205 additions and 11 deletions

View File

@@ -74,7 +74,7 @@
#define MAX_NUM_SLICES 1024
// RLC Entity
#define RLC_TX_MAXSIZE 10000000
#define RLC_TX_MAXSIZE 5000000
#define RLC_RX_MAXSIZE 10000000
#define MAX_ANT 8
// CBA constant

View File

@@ -120,6 +120,11 @@ int nr_rlc_get_available_tx_space(const rnti_t rntiP, const logical_chan_id_t ch
return 0;
}
void nr_rlc_discard_sdu(int ue_id, int rb_id, int sdu_id)
{
abort();
}
f1ap_cudu_inst_t *getCxt(instance_t instanceP)
{
// the E1 module uses F1's getCxt() to decide whether there is F1-U and if

View File

@@ -2096,6 +2096,10 @@ gNB_RRC_INST *RCconfig_NRRRC()
nrrrc_config.um_on_default_drb = *(GNBParamList.paramarray[i][GNB_UMONDEFAULTDRB_IDX].uptr);
LOG_I(GNB_APP, "Data Radio Bearer count %d\n", nrrrc_config.drbs);
int pdcp_discard_timer = *GNBParamList.paramarray[i][GNB_PDCP_DISCARD_TIMER_IDX].iptr;
nr_pdcp_set_discard_timer(pdcp_discard_timer);
LOG_I(GNB_APP, "PDCP discard timer set to %d ms\n", pdcp_discard_timer);
}//
}//End for (k=0; k <num_gnbs ; k++)
openair_rrc_gNB_configuration(rrc, &nrrrc_config);

View File

@@ -125,6 +125,8 @@ typedef enum {
#define GNB_CONFIG_STRING_CONFIG_REP "CSI_report_type"
#define GNB_CONFIG_STRING_1ST_ACTIVE_BWP "first_active_bwp"
#define GNB_CONFIG_STRING_LIMIT_RSRP_REPORT "max_num_RSRP_reported"
#define GNB_CONFIG_STRING_PDCP_DISCARD_TIMER "pdcp_discard_timer"
#define GNB_CONFIG_HLP_PDCP_DISCARD_TIMER "PDCP discard timer in ms (-1 = infinity/disabled)"
#define GNB_CONFIG_HLP_STRING_ENABLE_SDAP "enable the SDAP layer\n"
#define GNB_CONFIG_HLP_FORCE256QAMOFF "suppress activation of 256 QAM despite UE support"
@@ -189,6 +191,7 @@ typedef enum {
{GNB_CONFIG_STRING_CONFIG_REP, GNB_CONFIG_HLP_CONFIG_REP, 0, .strptr=NULL, .defstrval="ssb_rsrp", TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_1ST_ACTIVE_BWP, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
{GNB_CONFIG_STRING_LIMIT_RSRP_REPORT, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
{GNB_CONFIG_STRING_PDCP_DISCARD_TIMER, GNB_CONFIG_HLP_PDCP_DISCARD_TIMER, 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \
}
// clang-format on
@@ -234,10 +237,12 @@ typedef enum {
#define GNB_CONFIG_REP_IDX 38
#define GNB_1ST_ACTIVE_BWP_IDX 39
#define GNB_LIMIT_RSRP_REPORT_IDX 40
#define GNB_PDCP_DISCARD_TIMER_IDX 41
#define TRACKING_AREA_CODE_OKRANGE {0x0001,0xFFFD}
#define NUM_DL_HARQ_OKVALUES {2,4,6,8,10,12,16,32}
#define NUM_UL_HARQ_OKVALUES {16,32}
#define PDCP_DISCARD_TIMER_OKVALUES {-1,10,20,30,40,50,60,75,100,150,200,250,300,500,750,1500}
#define GNBPARAMS_CHECK { \
{ .s5 = { NULL } }, \
@@ -284,6 +289,7 @@ typedef enum {
3 } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s1 = { config_check_intval, PDCP_DISCARD_TIMER_OKVALUES, 16 } }, \
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/

View File

@@ -296,6 +296,11 @@ static int nr_pdcp_entity_process_sdu(nr_pdcp_entity_t *entity,
entity->tx_next++;
/* record submission timestamp for discard timer (TS 38.323 §5.2.1) */
if (entity->discard_ts != NULL) {
entity->discard_ts[count & (entity->discard_ring_size - 1)] = entity->t_current;
}
entity->stats.txpdu_pkts++;
entity->stats.txpdu_bytes += header_size + size + integrity_size;
entity->stats.txpdu_sn = sn;
@@ -453,11 +458,42 @@ static void check_t_reordering(nr_pdcp_entity_t *entity)
}
}
static void check_discard_timer(nr_pdcp_entity_t *entity)
{
if (entity->discard_ts == NULL || entity->discard_timer == -1)
return;
int mask = entity->discard_ring_size - 1;
int discards = 0;
/* handle ring overflow: advance tail silently — those SDUs are old enough
* that RLC has already transmitted or dropped them */
if ((entity->tx_next - entity->discard_tail) > (uint32_t)entity->discard_ring_size)
entity->discard_tail = entity->tx_next - entity->discard_ring_size;
while (entity->discard_tail < entity->tx_next && discards < 64) {
uint64_t ts = entity->discard_ts[entity->discard_tail & mask];
if (ts == 0) { entity->discard_tail++; continue; } /* already handled */
if (entity->t_current - ts <= (uint64_t)entity->discard_timer)
break; /* timestamps are monotonic — first unexpired means all after are too */
entity->discard_ts[entity->discard_tail & mask] = 0;
if (entity->notify_discard)
entity->notify_discard(entity->notify_discard_data, (int)entity->discard_tail);
entity->discard_tail++;
discards++;
}
if (discards > 0)
LOG_W(PDCP, "DRB %d: discarded %d SDUs (discard_timer=%d ms, age>%d ms)\n",
entity->rb_id, discards, entity->discard_timer, entity->discard_timer);
}
static void nr_pdcp_entity_set_time(struct nr_pdcp_entity_t *entity, uint64_t now)
{
entity->t_current = now;
check_t_reordering(entity);
check_discard_timer(entity);
}
static void deliver_all_sdus(nr_pdcp_entity_t *entity)
@@ -491,6 +527,11 @@ static void nr_pdcp_entity_suspend(nr_pdcp_entity_t *entity)
{
/* Transmitting PDCP entity */
entity->tx_next = 0;
/* reset discard timer tracking since tx_next is reset */
if (entity->discard_ts != NULL) {
memset(entity->discard_ts, 0, entity->discard_ring_size * sizeof(uint64_t));
entity->discard_tail = 0;
}
/* Receiving PDCP entity */
if (entity->t_reordering_start != 0) {
entity->t_reordering_start = 0;
@@ -541,6 +582,11 @@ static void nr_pdcp_entity_reestablish_drb_um(nr_pdcp_entity_t *entity,
{
/* transmitting entity procedures */
entity->tx_next = 0;
/* reset discard timer tracking since tx_next is reset */
if (entity->discard_ts != NULL) {
memset(entity->discard_ts, 0, entity->discard_ring_size * sizeof(uint64_t));
entity->discard_tail = 0;
}
/* receiving entity procedures */
/* deliver all SDUs if t_reordering is running */
@@ -588,6 +634,7 @@ static void nr_pdcp_entity_release(nr_pdcp_entity_t *entity)
static void nr_pdcp_entity_delete(nr_pdcp_entity_t *entity)
{
free_rx_list(entity);
free(entity->discard_ts);
if (entity->free_security != NULL)
entity->free_security(entity->security_context);
if (entity->free_integrity != NULL)
@@ -756,6 +803,16 @@ nr_pdcp_entity_t *new_nr_pdcp_entity(
ret->t_reordering = t_reordering;
ret->discard_timer = discard_timer;
/* discard timer tracking — initialized but ring not allocated yet.
* Ring allocation happens in nr_pdcp_oai_api.c at DRB creation time,
* where the deployment mode (monolithic vs CU) is known. */
ret->ue_id = 0;
ret->discard_ts = NULL;
ret->discard_ring_size = 0;
ret->discard_tail = 0;
ret->notify_discard = NULL;
ret->notify_discard_data = NULL;
ret->sn_max = (1 << sn_size) - 1;
ret->window_size = 1 << (sn_size - 1);

View File

@@ -41,6 +41,8 @@
#define PDCP_INTEGRITY_SIZE 4
/* K keys have 128 bits length */
#define NR_K_KEY_SIZE 16
/* Discard timer ring buffer size — must be power of 2 (512 KB per entity) */
#define PDCP_DISCARD_RING_SIZE 65536
typedef enum {
NR_PDCP_DRB_AM,
@@ -137,6 +139,15 @@ typedef struct nr_pdcp_entity_t {
int t_reordering; /* unit: ms, -1 for infinity */
int discard_timer; /* unit: ms, -1 for infinity */
/* discard timer enforcement (TX side, TS 38.323 §5.2.1 / §5.3) */
ue_id_t ue_id; /* PDCP-side UE ID, set at entity creation */
uint64_t *discard_ts; /* ring of submit timestamps (ms), NULL if disabled */
int discard_ring_size;
uint32_t discard_tail; /* oldest COUNT not yet checked */
/* pluggable discard notification — monolithic: enqueue to RLC, CU/DU: GTP-U */
void (*notify_discard)(void *notify_discard_data, int sdu_id);
void *notify_discard_data;
int sn_max; /* (2^SN_size) - 1 */
int window_size; /* 2^(SN_size - 1) */

View File

@@ -79,6 +79,13 @@ hash_table_t *pdcp_coll_p;
static uint64_t pdcp_optmask;
static ngran_node_t node_type;
static int pdcp_discard_timer_override = -1;
void nr_pdcp_set_discard_timer(int timer_ms)
{
pdcp_discard_timer_override = timer_ms;
LOG_I(PDCP, "PDCP discard timer override set to %d ms\n", timer_ms);
}
nr_pdcp_entity_t *nr_pdcp_get_rb(nr_pdcp_ue_t *ue, int rb_id, bool srb_flag)
{
@@ -153,12 +160,18 @@ static void *rlc_data_req_thread(void *_)
i = q.start;
if (pthread_mutex_unlock(&q.m) != 0) abort();
nr_rlc_data_req(&q.q[i].ctxt_pP,
q.q[i].srb_flagP,
q.q[i].rb_idP,
q.q[i].muiP,
q.q[i].sdu_sizeP,
q.q[i].sdu_pP);
if (q.q[i].sdu_pP == NULL && q.q[i].sdu_sizeP == 0) {
/* discard request: sdu_pP==NULL is the sentinel */
nr_rlc_discard_sdu(q.q[i].ctxt_pP.rntiMaybeUEid,
q.q[i].rb_idP, (int)q.q[i].muiP);
} else {
nr_rlc_data_req(&q.q[i].ctxt_pP,
q.q[i].srb_flagP,
q.q[i].rb_idP,
q.q[i].muiP,
q.q[i].sdu_sizeP,
q.q[i].sdu_pP);
}
if (pthread_mutex_lock(&q.m) != 0) abort();
@@ -218,6 +231,38 @@ static void enqueue_rlc_data_req(const protocol_ctxt_t *const ctxt_pP,
if (pthread_mutex_unlock(&q.m) != 0) abort();
}
/* Non-blocking variant for use from PDCP timer thread (holds PDCP lock).
* Returns false if queue is full — caller should skip the discard. */
static bool try_enqueue_rlc_data_req(const protocol_ctxt_t *const ctxt_pP,
const srb_flag_t srb_flagP,
const rb_id_t rb_idP,
const mui_t muiP,
confirm_t confirmP,
sdu_size_t sdu_sizeP,
uint8_t *sdu_pP)
{
if (pthread_mutex_lock(&q.m) != 0) abort();
if (q.length == RLC_DATA_REQ_QUEUE_SIZE) {
if (pthread_mutex_unlock(&q.m) != 0) abort();
return false;
}
int i = (q.start + q.length) % RLC_DATA_REQ_QUEUE_SIZE;
q.length++;
q.q[i].ctxt_pP = *ctxt_pP;
q.q[i].srb_flagP = srb_flagP;
q.q[i].rb_idP = rb_idP;
q.q[i].muiP = muiP;
q.q[i].confirmP = confirmP;
q.q[i].sdu_sizeP = sdu_sizeP;
q.q[i].sdu_pP = sdu_pP;
if (pthread_cond_signal(&q.c) != 0) abort();
if (pthread_mutex_unlock(&q.m) != 0) abort();
return true;
}
/****************************************************************************/
/* rlc_data_req queue - end */
/****************************************************************************/
@@ -447,6 +492,34 @@ static void deliver_sdu_drb(void *_ue, nr_pdcp_entity_t *entity,
}
}
/* Discard notification callback — called from PDCP timer thread via
* check_discard_timer(). We are inside the PDCP lock here, so we must
* NOT call RLC directly. Instead we enqueue a discard sentinel
* (sdu_pP == NULL, sdu_sizeP == 0) into the async RLC queue.
* The consumer thread (rlc_data_req_thread) recognises the sentinel
* and calls nr_rlc_discard_sdu(). */
static void pdcp_discard_callback(void *data, int sdu_id)
{
nr_pdcp_entity_t *entity = (nr_pdcp_entity_t *)data;
ue_id_t rlc_ue_id;
if (entity->is_gnb) {
f1_ue_data_t ue_data = cu_get_f1_ue_data(entity->ue_id);
rlc_ue_id = ue_data.secondary_ue;
} else {
rlc_ue_id = entity->ue_id;
}
protocol_ctxt_t ctxt = { .enb_flag = entity->is_gnb ? 1 : 0,
.rntiMaybeUEid = rlc_ue_id };
/* sdu_pP == NULL && sdu_sizeP == 0 is the discard sentinel.
* muiP carries the PDCP COUNT to identify the SDU in RLC. */
if (!try_enqueue_rlc_data_req(&ctxt, SRB_FLAG_NO, entity->rb_id,
(mui_t)sdu_id, 0, 0, NULL)) {
LOG_D(PDCP, "discard enqueue failed (queue full) for COUNT %d, skipping\n", sdu_id);
}
}
static void deliver_pdu_drb_ue(void *deliver_pdu_data, ue_id_t ue_id, int rb_id,
char *buf, int size, int sdu_id)
{
@@ -636,7 +709,24 @@ void nr_pdcp_add_drb(int is_gnb,
&actual_security_parameters);
nr_pdcp_ue_add_drb_pdcp_entity(ue, sdap->drb_id, pdcp_drb);
LOG_I(PDCP, "Added DRB %d to UE ID %ld\n", sdap->drb_id, UEid);
/* Apply config override (monolithic mode reads from gNBs config).
* -1 = infinity/disabled, otherwise timer in ms. */
if (!NODE_IS_CU(node_type))
pdcp_drb->discard_timer = pdcp_discard_timer_override;
/* Allocate discard timer ring and wire callback (monolithic only).
* In CU mode the discard path goes via GTP-U DL Discard Blocks (future). */
pdcp_drb->ue_id = UEid;
if (!NODE_IS_CU(node_type) && pdcp_drb->discard_timer != -1) {
pdcp_drb->discard_ring_size = PDCP_DISCARD_RING_SIZE;
pdcp_drb->discard_ts = calloc(PDCP_DISCARD_RING_SIZE, sizeof(uint64_t));
AssertFatal(pdcp_drb->discard_ts, "OOM for PDCP discard ring\n");
pdcp_drb->discard_tail = 0;
pdcp_drb->notify_discard = pdcp_discard_callback;
pdcp_drb->notify_discard_data = pdcp_drb;
}
LOG_I(PDCP, "Added DRB %d to UE ID %ld (discard_timer=%d)\n", sdap->drb_id, UEid, pdcp_drb->discard_timer);
}
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
}
@@ -966,11 +1056,14 @@ bool nr_pdcp_data_req_drb(protocol_ctxt_t *ctxt_pP,
return 0;
}
/* Use PDCP COUNT (tx_next - 1) as sdu_id so RLC can match individual SDUs
* for discard. muiP from GTP is always 0 which makes all SDUs indistinguishable. */
int pdcp_count = (int)(rb->tx_next - 1);
deliver_pdu deliver_pdu_cb = rb->deliver_pdu;
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
deliver_pdu_cb(NULL, ue_id, rb_id, pdu_buf, pdu_size, muiP);
deliver_pdu_cb(NULL, ue_id, rb_id, pdu_buf, pdu_size, pdcp_count);
return 1;
}

View File

@@ -2044,8 +2044,8 @@ void nr_rlc_entity_am_discard_sdu(nr_rlc_entity_t *_entity, int sdu_id)
entity->tx_size -= cur->sdu->size;
/* Uncomment to assert if SDU are ever discarded */
// assert(0 != 0 && "[RLC-TRAP] SDU discard should never be reached!");
LOG_W(RLC, "discarded SDU sdu_id %d (%d bytes) from TX buffer\n",
sdu_id, cur->sdu->size);
nr_rlc_free_sdu_segment(cur);
}

View File

@@ -707,6 +707,10 @@ void nr_rlc_entity_um_discard_sdu(nr_rlc_entity_t *_entity, int sdu_id)
/* update buffer status */
entity->common.bstatus.tx_size -= compute_pdu_header_size(entity, cur)
+ cur->size;
entity->tx_size -= cur->sdu->size;
LOG_W(RLC, "discarded SDU sdu_id %d (%d bytes) from UM TX buffer\n",
sdu_id, cur->sdu->size);
nr_rlc_free_sdu_segment(cur);
}

View File

@@ -1035,6 +1035,18 @@ bool nr_rlc_get_statistics(int ue_id, int srb_flag, int rb_id, nr_rlc_statistics
return ret;
}
void nr_rlc_discard_sdu(int ue_id, int rb_id, int sdu_id)
{
nr_rlc_manager_lock(nr_rlc_ue_manager);
nr_rlc_ue_t *ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, ue_id);
nr_rlc_entity_t *rb = NULL;
if (rb_id >= 1 && rb_id <= MAX_DRBS_PER_UE)
rb = ue->drb[rb_id - 1];
if (rb != NULL)
rb->discard_sdu(rb, sdu_id);
nr_rlc_manager_unlock(nr_rlc_ue_manager);
}
void nr_rlc_srb_recv_sdu(const int ue_id, const logical_chan_id_t channel_id, unsigned char *buf, int size)
{
T(T_ENB_RLC_DL, T_INT(0), T_INT(ue_id), T_INT(0), T_INT(size));

View File

@@ -91,3 +91,5 @@ bool nr_rlc_activate_srb0(int ue_id,
void (*send_initial_ul_rrc_message)(int rnti, const uint8_t *sdu, sdu_size_t sdu_len, void *data));
bool nr_rlc_get_statistics(int ue_id, int srb_flag, int rb_id, nr_rlc_statistics_t *out);
void nr_rlc_discard_sdu(int ue_id, int rb_id, int sdu_id);