Commit a63f1704 authored by Cedric Roux's avatar Cedric Roux

nr pdcp: add --pdcp-drop option to drop packets in PDCP instead of RLC

This only works in monolithic. F1 split to be done later, it's a bit more
complicated.

And actually, the drop in RLC is still there, but it should not happen.
Or much less.

The logic around rlc_data_queue_occupancy() is not satisfying. It is
done globally for all the UEs so we may underestimate the real free
space in RLC for a given UE, and thus reject more packets than
necessary.
parent 5b943f59
......@@ -105,10 +105,11 @@ extern "C"
#define CONFIG_HLP_NFAPI "Change the nFAPI mode for NR 'MONOLITHIC', 'PNF', 'VNF','UE_STUB_PNF','UE_STUB_OFFNET','STANDALONE_PNF'\n"
#define CONFIG_L1_EMULATOR "Run in L1 emulated mode (disable PHY layer)\n"
#define CONFIG_HLP_CONTINUOUS_TX "perform continuous transmission, even in TDD mode (to work around USRP issues)\n"
#define CONFIG_HLP_STATS_DISABLE "disable globally the stats generation and persistence"
#define CONFIG_HLP_STATS_DISABLE "disable globally the stats generation and persistence\n"
#define CONFIG_HLP_SYNC_REF "Sync Reference in Sidelink\n"
#define CONFIG_HLP_NID1 "Set NID1 value in Sidelink\n"
#define CONFIG_HLP_NID2 "Set NID2 value in Sidelink\n"
#define CONFIG_HLP_PDCP_DROP "drop overloaded traffic at PDCP instead of RLC\n"
/*-----------------------------------------------------------------------------------------------------------------------------------------------------*/
/* command line parameters common to eNodeB and UE */
......@@ -145,6 +146,8 @@ extern "C"
#define NID2 softmodem_params.nid2
#define REORDER_THREAD_DISABLE softmodem_params.reorder_thread_disable
#define PDCP_DROP softmodem_params.pdcp_drop
#define DEFAULT_RFCONFIG_FILE "/usr/local/etc/syriq/ue.band7.tm1.PRB100.NR40.dat";
extern int usrp_tx_thread;
......@@ -190,6 +193,7 @@ extern int usrp_tx_thread;
{"disable-stats", CONFIG_HLP_STATS_DISABLE, PARAMFLAG_BOOL, .iptr=&stats_disabled, .defintval=0, TYPE_INT, 0}, \
{"nid1", CONFIG_HLP_NID1, 0, .iptr=&NID1, .defintval=10, TYPE_INT, 0}, \
{"nid2", CONFIG_HLP_NID2, 0, .iptr=&NID2, .defintval=1, TYPE_INT, 0}, \
{"pdcp-drop", CONFIG_HLP_PDCP_DROP, PARAMFLAG_BOOL, .iptr=&PDCP_DROP, .defintval=0, TYPE_INT, 0}, \
}
// clang-format on
......@@ -238,6 +242,7 @@ extern int usrp_tx_thread;
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
}
// clang-format on
......@@ -349,6 +354,7 @@ typedef struct {
int sync_ref;
int nid1;
int nid2;
int pdcp_drop;
} softmodem_params_t;
extern uint64_t get_softmodem_optmask(void);
......
......@@ -31,6 +31,7 @@
#include "nr_pdcp_sdu.h"
#include "LOG/log.h"
#include "executables/softmodem-common.h"
static void nr_pdcp_entity_recv_pdu(nr_pdcp_entity_t *entity,
char *_buffer, int size)
......@@ -193,6 +194,27 @@ static int nr_pdcp_entity_process_sdu(nr_pdcp_entity_t *entity,
entity->stats.rxsdu_pkts++;
entity->stats.rxsdu_bytes += size;
if (entity->sn_size == 12) {
header_size = 2;
} else {
header_size = 3;
}
/* SRBs always have MAC-I, even if integrity is not active */
if (entity->has_integrity || entity->type == NR_PDCP_SRB) {
integrity_size = 4;
} else {
integrity_size = 0;
}
/* if dropping is enabled, check if RLC has enough space to receive the PDU */
if (entity->do_drop) {
if (entity->type != NR_PDCP_SRB)
if (header_size + size + integrity_size > entity->rlc_tx_freesize)
return -1;
}
entity->rlc_tx_freesize -= size;
count = entity->tx_next;
sn = entity->tx_next & entity->sn_max;
......@@ -207,19 +229,10 @@ static int nr_pdcp_entity_process_sdu(nr_pdcp_entity_t *entity,
if (entity->sn_size == 12) {
buf[0] = dc_bit | ((sn >> 8) & 0xf);
buf[1] = sn & 0xff;
header_size = 2;
} else {
buf[0] = dc_bit | ((sn >> 16) & 0x3);
buf[1] = (sn >> 8) & 0xff;
buf[2] = sn & 0xff;
header_size = 3;
}
/* SRBs always have MAC-I, even if integrity is not active */
if (entity->has_integrity || entity->type == NR_PDCP_SRB) {
integrity_size = 4;
} else {
integrity_size = 0;
}
memcpy(buf + header_size, buffer, size);
......@@ -362,11 +375,25 @@ static void check_t_reordering(nr_pdcp_entity_t *entity)
}
}
void nr_pdcp_entity_set_time(struct nr_pdcp_entity_t *entity, uint64_t now)
static void log_dropped_packets(nr_pdcp_entity_t *entity)
{
/* log only every 2 seconds */
if (entity->t_current < entity->dropped_lastlog_time + 2000)
return;
if (entity->dropped_sdus == 0)
return;
LOG_W(PDCP, "%"PRIu64" SDUs (%"PRIu64" bytes) dropped\n", entity->dropped_sdus, entity->dropped_bytes);
entity->dropped_sdus = 0;
entity->dropped_bytes = 0;
entity->dropped_lastlog_time = entity->t_current;
}
void nr_pdcp_entity_set_time(nr_pdcp_entity_t *entity, uint64_t now)
{
entity->t_current = now;
check_t_reordering(entity);
log_dropped_packets(entity);
}
void nr_pdcp_entity_delete(nr_pdcp_entity_t *entity)
......@@ -449,9 +476,18 @@ nr_pdcp_entity_t *new_nr_pdcp_entity(
ret->is_gnb = is_gnb;
ret->rlc_rnti = -1;
ret->do_drop = get_softmodem_params()->pdcp_drop;
nr_pdcp_entity_set_security(ret,
integrity_algorithm, (char *)integrity_key,
ciphering_algorithm, (char *)ciphering_key);
return ret;
}
void nr_pdcp_entity_set_rlc_ids(nr_pdcp_entity_t *entity, int rlc_rnti, int rlc_channel_id)
{
entity->rlc_rnti = rlc_rnti;
entity->rlc_channel_id = rlc_channel_id;
}
......@@ -152,6 +152,16 @@ typedef struct nr_pdcp_entity_t {
int rx_maxsize;
nr_pdcp_statistics_t stats;
/* RLC TX buffer size estimation */
bool do_drop; /* dropping is optional */
/* PDCP entity drops a packet if its size is > rlc_tx_freesize */
int rlc_tx_freesize;
int rlc_rnti; /* set to -1 if unknown (think CU entity) */
int rlc_channel_id;
uint64_t dropped_sdus;
uint64_t dropped_bytes;
uint64_t dropped_lastlog_time;
// WARNING: This is a hack!
// 3GPP TS 38.331 (RRC) version 15.3
// Section 5.3.4.3 Reception of the SecurityModeCommand by the UE
......@@ -187,4 +197,6 @@ nr_pdcp_entity_t *new_nr_pdcp_entity(
unsigned char *ciphering_key,
unsigned char *integrity_key);
void nr_pdcp_entity_set_rlc_ids(nr_pdcp_entity_t *entity, int rlc_rnti, int rlc_channel_id);
#endif /* _NR_PDCP_ENTITY_H_ */
......@@ -103,6 +103,7 @@ typedef struct {
volatile int length;
pthread_mutex_t m;
pthread_cond_t c;
int bytes_stored;
} rlc_data_req_queue;
static rlc_data_req_queue q;
......@@ -135,6 +136,8 @@ static void *rlc_data_req_thread(void *_)
q.length--;
q.start = (q.start + 1) % RLC_DATA_REQ_QUEUE_SIZE;
q.bytes_stored -= q.q[i].sdu_sizeP;
if (pthread_cond_signal(&q.c) != 0) abort();
if (pthread_mutex_unlock(&q.m) != 0) abort();
}
......@@ -177,6 +180,8 @@ static void enqueue_rlc_data_req(const protocol_ctxt_t *const ctxt_pP,
i = (q.start + q.length) % RLC_DATA_REQ_QUEUE_SIZE;
q.length++;
q.bytes_stored += sdu_sizeP;
q.q[i].ctxt_pP = *ctxt_pP;
q.q[i].srb_flagP = srb_flagP;
q.q[i].MBMS_flagP = MBMS_flagP;
......@@ -208,6 +213,21 @@ void du_rlc_data_req(const protocol_ctxt_t *const ctxt_pP,
sdu_pP);
}
static int rlc_data_queue_occupancy(void)
{
int ret;
/* no queue for CU */
if (NODE_IS_CU(node_type))
return 0;
if (pthread_mutex_lock(&q.m) != 0) abort();
ret = q.bytes_stored;
if (pthread_mutex_unlock(&q.m) != 0) abort();
return ret;
}
/****************************************************************************/
/* rlc_data_req queue - end */
/****************************************************************************/
......@@ -1033,7 +1053,12 @@ bool nr_pdcp_data_req_srb(ue_id_t ue_id,
LOG_D(PDCP, "%s() called, size %d\n", __func__, sdu_buffer_size);
nr_pdcp_ue_t *ue;
nr_pdcp_entity_t *rb;
int rlc_rnti;
int rlc_channel_id;
int rlc_tx_freesize = -1;
bool rlc_tx_freesize_updated = false;
try_to_process_again:
nr_pdcp_manager_lock(nr_pdcp_ue_manager);
ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id);
......@@ -1051,12 +1076,40 @@ bool nr_pdcp_data_req_srb(ue_id_t ue_id,
int max_size = sdu_buffer_size + 3 + 4; // 3: max header, 4: max integrity
char pdu_buf[max_size];
if (rlc_tx_freesize != -1)
rb->rlc_tx_freesize = rlc_tx_freesize;
int pdu_size = rb->process_sdu(rb, (char *)sdu_buffer, sdu_buffer_size, muiP, pdu_buf, max_size);
rlc_rnti = rb->rlc_rnti;
rlc_channel_id = rb->rlc_channel_id;
AssertFatal(rb->deliver_pdu == NULL, "SRB callback should be NULL, to be provided on every invocation\n");
if (pdu_size == -1 && rlc_tx_freesize_updated) {
/* update stats of dropped SDUs */
rb->dropped_sdus++;
rb->dropped_bytes += sdu_buffer_size;
}
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
deliver_pdu_cb(data, ue_id, rb_id, pdu_buf, pdu_size, muiP);
/* nr_rlc_get_available_tx_space() should be called by rb->process_sdu()
* but because the RLC lock must not be acquired when the PDCP lock is acquired,
* we need to call it here and have this logic of calling rb->process_sdu()
* a second time if the first call failed.
* If the locking logic changes, this may need to be changed.
* (And the update of stats of dropped SDUs will then need to be changed
* too, not done here anymore.)
*/
if (pdu_size == -1 && !rlc_tx_freesize_updated && rlc_rnti != -1) {
rlc_tx_freesize = nr_rlc_get_available_tx_space(rlc_rnti, rlc_channel_id)
- rlc_data_queue_occupancy();
if (rlc_tx_freesize < 0)
rlc_tx_freesize = 0;
rlc_tx_freesize_updated = true;
goto try_to_process_again;
}
if (pdu_size != -1)
deliver_pdu_cb(data, ue_id, rb_id, pdu_buf, pdu_size, muiP);
return 1;
}
......@@ -1111,6 +1164,10 @@ bool nr_pdcp_data_req_drb(protocol_ctxt_t *ctxt_pP,
nr_pdcp_ue_t *ue;
nr_pdcp_entity_t *rb;
ue_id_t ue_id = ctxt_pP->rntiMaybeUEid;
int rlc_rnti;
int rlc_channel_id;
int rlc_tx_freesize = -1;
bool rlc_tx_freesize_updated = false;
if (ctxt_pP->module_id != 0 ||
//ctxt_pP->enb_flag != 1 ||
......@@ -1122,6 +1179,7 @@ bool nr_pdcp_data_req_drb(protocol_ctxt_t *ctxt_pP,
exit(1);
}
try_to_process_again:
nr_pdcp_manager_lock(nr_pdcp_ue_manager);
ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id);
......@@ -1139,12 +1197,40 @@ bool nr_pdcp_data_req_drb(protocol_ctxt_t *ctxt_pP,
int max_size = sdu_buffer_size + 3 + 4; // 3: max header, 4: max integrity
char pdu_buf[max_size];
if (rlc_tx_freesize != -1)
rb->rlc_tx_freesize = rlc_tx_freesize;
int pdu_size = rb->process_sdu(rb, (char *)sdu_buffer, sdu_buffer_size, muiP, pdu_buf, max_size);
rlc_rnti = rb->rlc_rnti;
rlc_channel_id = rb->rlc_channel_id;
deliver_pdu deliver_pdu_cb = rb->deliver_pdu;
if (pdu_size == -1 && rlc_tx_freesize_updated) {
/* update stats of dropped SDUs */
rb->dropped_sdus++;
rb->dropped_bytes += sdu_buffer_size;
}
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
deliver_pdu_cb(NULL, ue_id, rb_id, pdu_buf, pdu_size, muiP);
/* nr_rlc_get_available_tx_space() should be called by rb->process_sdu()
* but because the RLC lock must not be acquired when then PDCP lock is acquired,
* we need to call it here and have this logic of calling rb->process_sdu()
* a second time if the first call failed.
* If the locking logic changes, this may need to be changed.
* (And the update of stats of dropped SDUs will then need to be changed
* too, not done here anymore.)
*/
if (pdu_size == -1 && !rlc_tx_freesize_updated && rlc_rnti != -1) {
rlc_tx_freesize = nr_rlc_get_available_tx_space(rlc_rnti, rlc_channel_id)
- rlc_data_queue_occupancy();
if (rlc_tx_freesize < 0)
rlc_tx_freesize = 0;
rlc_tx_freesize_updated = true;
goto try_to_process_again;
}
if (pdu_size != -1)
deliver_pdu_cb(NULL, ue_id, rb_id, pdu_buf, pdu_size, muiP);
return 1;
}
......@@ -1258,3 +1344,32 @@ const bool nr_pdcp_get_statistics(ue_id_t ue_id, int srb_flag, int rb_id, nr_pdc
return ret;
}
void nr_pdcp_set_rlc_ids(ue_id_t ue_id, bool srb_flag, int rb_id, int rlc_rnti, int rlc_channel_id)
{
nr_pdcp_ue_t *ue;
nr_pdcp_entity_t *rb;
nr_pdcp_manager_lock(nr_pdcp_ue_manager);
ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id);
if (srb_flag) {
if (rb_id < 1 || rb_id > 2)
rb = NULL;
else
rb = ue->srb[rb_id - 1];
} else {
if (rb_id < 1 || rb_id > 5)
rb = NULL;
else
rb = ue->drb[rb_id - 1];
}
if (rb != NULL) {
nr_pdcp_entity_set_rlc_ids(rb, rlc_rnti, rlc_channel_id);
} else {
LOG_E(PDCP, "ue %"PRIx64" not found\n", ue_id);
}
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
}
......@@ -105,4 +105,6 @@ nr_pdcp_ue_manager_t *nr_pdcp_sdap_get_ue_manager();
const bool nr_pdcp_get_statistics(ue_id_t ue_id, int srb_flag, int rb_id, nr_pdcp_statistics_t *out);
void nr_pdcp_set_rlc_ids(ue_id_t ue_id, bool srb_flag, int rb_id, int rlc_rnti, int rlc_channel_id);
#endif /* NR_PDCP_OAI_API_H */
......@@ -189,6 +189,17 @@ static void cucp_cuup_bearer_context_setup_direct(e1ap_bearer_setup_req_t *const
// the code is very badly organized, it is not possible here to call freeDRBlist()
ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList,DRB_configList );
if (NODE_IS_MONOLITHIC(RC.nrrrc[ctxt.module_id]->node_type)) {
for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
if (UE->established_drbs[i].status != DRB_INACTIVE) {
int rlc_rnti = UE->rnti;
int drb_id = UE->established_drbs[i].drb_id;
int rlc_channel_id = drb_id + 3;
nr_pdcp_set_rlc_ids(UE->rnti, false, drb_id, rlc_rnti, rlc_channel_id);
}
}
}
// Used to store teids: if monolithic, will simply be NULL
if(!NODE_IS_CU(RC.nrrrc[ctxt.module_id]->node_type)) {
// intentionally empty
......
......@@ -1680,6 +1680,14 @@ int32_t nr_rrc_ue_establish_drb(module_id_t ue_mod_idP,
kUPenc,
kUPint,
ue_rrc->cell_group_config->rlc_BearerToAddModList);
for (int i = 0; i < radioBearerConfig->drb_ToAddModList->list.count; i++) {
NR_DRB_ToAddMod_t *drb = radioBearerConfig->drb_ToAddModList->list.array[i];
int rlc_rnti = ctxt_pP->rntiMaybeUEid;
int rlc_channel_id = drb->drb_Identity + 3;
nr_pdcp_set_rlc_ids(ctxt_pP->rntiMaybeUEid, false, drb->drb_Identity, rlc_rnti, rlc_channel_id);
}
// Refresh DRBs
nr_rrc_addmod_drbs(ctxt_pP->rntiMaybeUEid,
radioBearerConfig->drb_ToAddModList,
......
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