mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 12:40:28 +00:00
Compare commits
3 Commits
pucch2_deb
...
pdcp-disca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afa6baa234 | ||
|
|
f9c2045ccc | ||
|
|
cbaeecd56e |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 } }, \
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) */
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user