From 6a2997ec9b8c579d9b5dab232cb92117fbefc77d Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 19 Jun 2025 16:41:02 +0200 Subject: [PATCH 01/86] T hacks: add ant0 utility to dump IQ data as float complex Or as int16. --- common/utils/T/tracer/hacks/Makefile | 12 +- common/utils/T/tracer/hacks/ant0.c | 259 +++++++++++++++++++++++++++ 2 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 common/utils/T/tracer/hacks/ant0.c diff --git a/common/utils/T/tracer/hacks/Makefile b/common/utils/T/tracer/hacks/Makefile index 7157406796..f693094fb6 100644 --- a/common/utils/T/tracer/hacks/Makefile +++ b/common/utils/T/tracer/hacks/Makefile @@ -3,7 +3,7 @@ CFLAGS=-Wall -g -pthread -DT_TRACER -I. -I.. LIBS=-lX11 -lm -lpng -lXft -all: dump_nack_signal time_meas timeplot multi-rru-clean +all: dump_nack_signal time_meas timeplot multi-rru-clean ant0 dump_nack_signal: ../utils.o ../database.o ../configuration.o ../event.o \ dump_nack_signal.o @@ -19,10 +19,18 @@ timplot: timeplot.o multi-rru-clean: ../utils.o ../database.o ../event.o ../configuration.o multi-rru-clean.o $(CC) $(CFLAGS) -o $@ $^ $(LIBS) +ant0: ant0.o ../utils.o ../database.o ../configuration.o ../event.o + $(CC) $(CFLAGS) -o $@ $^ -lm + +#ant0 needs good compilation, conversion to float is heavy +ant0.o: ant0.c + $(CC) $(CFLAGS) -c -o $@ $< -march=native -ffast-math -O3 + .PHONY: all %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< clean: - rm -f *.o core dump_nack_signal time_meas timeplot multi-rru-clean + rm -f *.o core dump_nack_signal time_meas timeplot multi-rru-clean \ + ant0 diff --git a/common/utils/T/tracer/hacks/ant0.c b/common/utils/T/tracer/hacks/ant0.c new file mode 100644 index 0000000000..3de4dac325 --- /dev/null +++ b/common/utils/T/tracer/hacks/ant0.c @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include +#include "database.h" +//#include "utils.h" +#include "configuration.h" +#include "event.h" + +void usage(void) +{ + fprintf(stderr, +"options:\n" +" -d this option is mandatory\n" +" -o this option is mandatory (use - to dump to stdout)\n" +" -ip connect to given IP address (default %s)\n" +" -p connect to given port (default %d)\n" +" -n save maximum 'max' IQ points (may be a bit more)\n" +" -nosig don't intercept ctrl+c, ctrl+z (it may mess up when piping to another tool)\n" +" -e trace given event (default 'USRP_TX_ANT0')\n" +" -b trace given buffer (default 'data')\n" +" -no-timestamp don't use 'timestamp' to fill missing transmission\n" +" -scale divide 16bits input by given value for float output (default 32768.)\n" +" -s16 output short instead of float\n", + DEFAULT_REMOTE_IP, + DEFAULT_REMOTE_PORT + ); + exit(1); +} + +volatile int run = 1; + +static int socket = -1; + +void force_stop(int x) +{ + fprintf(stderr, "\ngently quit...\n"); + close(socket); + socket = -1; + run = 0; +} + +int main(int n, char **v) +{ + char *database_filename = NULL; + char *output_filename = NULL; + FILE *out; + void *database; + char *ip = DEFAULT_REMOTE_IP; + int port = DEFAULT_REMOTE_PORT; + int *is_on; + int number_of_events; + int i; + char mt; + long max = -1; + long dumped = 0; + int nosig = 0; + char *event_name = "USRP_TX_ANT0"; + char *buffer_name = "data"; + int no_timestamp = 0; + float scale = 32768; + int do_float = 1; + + /* write on a socket fails if the other end is closed and we get SIGPIPE */ + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) abort(); + + for (i = 1; i < n; i++) { + if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage(); + if (!strcmp(v[i], "-d")) { if (i > n-2) usage(); database_filename = v[++i]; continue; } + if (!strcmp(v[i], "-o")) { if (i > n-2) usage(); output_filename = v[++i]; continue; } + if (!strcmp(v[i], "-ip")) { if (i > n-2) usage(); ip = v[++i]; continue; } + if (!strcmp(v[i], "-p")) { if (i > n-2) usage(); port = atoi(v[++i]); continue; } + if (!strcmp(v[i], "-n")) { if (i > n-2) usage(); max = atol(v[++i]); continue; } + if (!strcmp(v[i], "-nosig")) { nosig = 1; continue; } + if (!strcmp(v[i], "-e")) { if (i > n-2) usage(); event_name = v[++i]; continue; } + if (!strcmp(v[i], "-b")) { if (i > n-2) usage(); buffer_name = v[++i]; continue; } + if (!strcmp(v[i], "-no-timestamp")) { no_timestamp = 1; continue; } + if (!strcmp(v[i], "-scale")) { if (i > n-2) usage(); scale = atof(v[++i]); continue; } + if (!strcmp(v[i], "-s16")) { do_float = 0; continue; } + usage(); + } + + if (database_filename == NULL) { + fprintf(stderr, "ERROR: provide a database file (-d)\n"); + exit(1); + } + if (output_filename == NULL) { + fprintf(stderr, "ERROR: provide an output file (-o)\n"); + exit(1); + } + + database = parse_database(database_filename); + load_config_file(database_filename); + + /* get the event and fields from database */ + int tx_ant0_id = event_id_from_name(database, event_name); + database_event_format f = get_format(database, tx_ant0_id); + +#define G(var_name, var_type, var) \ + if (!strcmp(f.name[i], var_name)) { \ + if (strcmp(f.type[i], var_type)) abort(); \ + var = i; \ + continue; \ + } + + int timestamp_id = -1; + int data_id = -1; + for (i = 0; i < f.count; i++) { + /* there is a typo in T_messages.txt, "timestap" instead of "timestamp" */ + if (!no_timestamp) + G("timestap", "int", timestamp_id); + G(buffer_name, "buffer", data_id); + } + if ((!no_timestamp && timestamp_id == -1) || data_id == -1) { + if (data_id == -1) + fprintf(stderr, "fatal: field '%s' of event '%s' does not exist\n", + buffer_name, event_name); + if (!no_timestamp && timestamp_id == -1) + fprintf(stderr, "fatal: field 'timestamp' of event '%s' does not exist\n", + event_name); + exit(1); + } + + number_of_events = number_of_ids(database); + is_on = calloc(number_of_events, sizeof(int)); + if (is_on == NULL) abort(); + + on_off(database, event_name, is_on, 1); + + /* connect to tracee */ + socket = connect_to(ip, port); + + /* activate trace */ + mt = 1; + if (socket_send(socket, &mt, 1) == -1 || + socket_send(socket, &number_of_events, sizeof(int)) == -1 || + socket_send(socket, is_on, number_of_events * sizeof(int)) == -1) + abort(); + + /* - means to output to stdout */ + if (!strcmp(output_filename, "-")) { + out = stdout; + } else { + out = fopen(output_filename, "w"); + if (out == NULL) { + perror(output_filename); + exit(1); + } + } + + if (!nosig) { + /* exit on ctrl+c and ctrl+z */ + if (signal(SIGQUIT, force_stop) == SIG_ERR) abort(); + if (signal(SIGINT, force_stop) == SIG_ERR) abort(); + if (signal(SIGTSTP, force_stop) == SIG_ERR) abort(); + } + + OBUF ebuf = {.osize = 0, .omaxsize = 0, .obuf = NULL}; + + /* read messages */ + unsigned int last_timestamp = 0; + int last_count = 0; + int first = 1; + + /* stop if max reached */ + while ((max == -1 || dumped < max) && run) { + event e; + e = get_event(socket, &ebuf, database); + + if (e.type == -1) break; + if (e.type != tx_ant0_id) continue; + + unsigned int timestamp = 0; + if (!no_timestamp) + timestamp = e.e[timestamp_id].i; + int count = e.e[data_id].bsize / 4; + +#if 0 + fprintf(stderr, "timestamp %u buffer %p count %d not transmitted %d delta %d\n", + timestamp, + e.e[data_id].b, + count, + timestamp - (last_timestamp + last_count), + timestamp - last_timestamp); +#endif + + /* rfsimulator may send 1 sample with old time when a new client connects + * let's reject this packet + */ + if (!first && timestamp < last_timestamp + last_count) { + /* more than 1 sample is not accepted */ + if (count != 1) { + fprintf(stderr, "fatal: past transmission detected (%d samples)\n", count); + abort(); + } + fprintf(stderr, "past transmission detected (%d samples), ignore\n", count); + continue; + } + + /* insert missing samples */ + if (!no_timestamp && !first) { + int missing = timestamp - (last_timestamp + last_count); + if (missing < 0) abort(); + while (missing) { + /* limit size of array miss[], it's allocated on the stack */ + int count = missing > 65536 ? 65536 : missing; + float miss[count * 2]; + memset(miss, 0, count * 2 * sizeof(float)); + + if (fwrite(miss, count * 2 * sizeof(float), 1, out) != 1) { + fprintf(stderr, "error writing to %s\n", output_filename); + if (strcmp(output_filename, "-")) + fclose(out); + close(socket); + exit(1); + } + dumped += count; + missing -= count; + } + } + + first = 0; + + if (do_float) { + /* transform to float */ + float iq[count * 2]; + short *iq_short = (short *)e.e[data_id].b; + for (int i = 0; i < count * 2; i++) + iq[i] = iq_short[i] / scale; + + /* save */ + if (fwrite(iq, count * 2 * sizeof(float), 1, out) != 1) { + fprintf(stderr, "error writing to %s\n", output_filename); + if (strcmp(output_filename, "-")) + fclose(out); + exit(1); + } + } else { + /* save directly as short */ + if (fwrite(e.e[data_id].b, count * 2 * 2, 1, out) != 1) { + fprintf(stderr, "error writing to %s\n", output_filename); + if (strcmp(output_filename, "-")) + fclose(out); + exit(1); + } + } + dumped += count; + + last_timestamp = timestamp; + last_count = count; + } + + if (strcmp(output_filename, "-")) + fclose(out); + close(socket); + + return 0; +} From 4a0d8ce637e12c57c80a27c0968dcc67d0cac7d3 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 19 Jun 2025 16:43:19 +0200 Subject: [PATCH 02/86] rfsim: add a T trace to dump IQ data Only for one antenna. --- radio/rfsimulator/simulator.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 32648758c8..3a2a0ae2d5 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -738,6 +738,10 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp timestamp, timestamp + nsamps, signal_energy(samplesVoid[0], nsamps)); + + /* trace only first antenna */ + T(T_USRP_TX_ANT0, T_INT(timestamp), T_BUFFER(samplesVoid[0], sampleToByte(nsamps, 1))); + return nsamps; } From 17b59413677f714cccc9a1c0d774c359c6fbfc82 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 19 Jun 2025 16:44:13 +0200 Subject: [PATCH 03/86] T: dump logs to stderr, not stdout This is a problem with the new tool ant0 when we dump IQ to stdout. The logs will mess up the recording. --- common/utils/T/tracer/utils.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/utils/T/tracer/utils.c b/common/utils/T/tracer/utils.c index 78b01a2516..66dd2a0b48 100644 --- a/common/utils/T/tracer/utils.c +++ b/common/utils/T/tracer/utils.c @@ -159,7 +159,7 @@ int socket_send(int socket, void *buffer, int size) { int get_connection(char *addr, int port) { int s, t; - printf("waiting for connection on %s:%d\n", addr, port); + fprintf(stderr, "waiting for connection on %s:%d\n", addr, port); s = create_listen_socket(addr, port); t = socket_accept(s); @@ -169,7 +169,7 @@ int get_connection(char *addr, int port) { } close(s); - printf("connected\n"); + fprintf(stderr, "connected\n"); return t; } @@ -216,12 +216,12 @@ int try_connect_to(char *addr, int port) { int connect_to(char *addr, int port) { int s; - printf("connecting to %s:%d\n", addr, port); + fprintf(stderr, "connecting to %s:%d\n", addr, port); again: s = try_connect_to(addr, port); if (s == -1) { - printf("trying again in 1s\n"); + fprintf(stderr, "trying again in 1s\n"); sleep(1); goto again; } From a10420e015e93826e83c9e11906631b8e508b4c5 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Sun, 22 Jun 2025 18:59:38 +0200 Subject: [PATCH 04/86] T hacks: add ofdm-plot Used to... surprise... plot OFDM data as output (or input) by gnb! Limitation: only one antenna. --- common/utils/T/tracer/hacks/Makefile | 11 +- common/utils/T/tracer/hacks/plot-ofdm.c | 351 ++++++++++++++++++++++++ 2 files changed, 360 insertions(+), 2 deletions(-) create mode 100644 common/utils/T/tracer/hacks/plot-ofdm.c diff --git a/common/utils/T/tracer/hacks/Makefile b/common/utils/T/tracer/hacks/Makefile index f693094fb6..2c96e67823 100644 --- a/common/utils/T/tracer/hacks/Makefile +++ b/common/utils/T/tracer/hacks/Makefile @@ -3,7 +3,7 @@ CFLAGS=-Wall -g -pthread -DT_TRACER -I. -I.. LIBS=-lX11 -lm -lpng -lXft -all: dump_nack_signal time_meas timeplot multi-rru-clean ant0 +all: dump_nack_signal time_meas timeplot multi-rru-clean ant0 plot-ofdm dump_nack_signal: ../utils.o ../database.o ../configuration.o ../event.o \ dump_nack_signal.o @@ -26,6 +26,13 @@ ant0: ant0.o ../utils.o ../database.o ../configuration.o ../event.o ant0.o: ant0.c $(CC) $(CFLAGS) -c -o $@ $< -march=native -ffast-math -O3 +plot-ofdm: plot-ofdm.o ../utils.o + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) -lfftw3f + +#plot-ofdm needs good compilation +plot-ofdm.o: plot-ofdm.c + $(CC) $(CFLAGS) -c -o $@ $< -march=native -ffast-math -O3 + .PHONY: all %.o: %.c @@ -33,4 +40,4 @@ ant0.o: ant0.c clean: rm -f *.o core dump_nack_signal time_meas timeplot multi-rru-clean \ - ant0 + ant0 plot-ofdm diff --git a/common/utils/T/tracer/hacks/plot-ofdm.c b/common/utils/T/tracer/hacks/plot-ofdm.c new file mode 100644 index 0000000000..3f6e0b9dce --- /dev/null +++ b/common/utils/T/tracer/hacks/plot-ofdm.c @@ -0,0 +1,351 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +const int width = 14 * 20 * 4; +int height; + +/* when vzoom = 1 each pixel represents 1 RE + * when 2, it is max of 2 vertical REs + * useful if height of plot is to big + * (with 106 RB we already reach 106*12 = 1272 pixels, so with even bigger + * bandwidths...) + */ +int vzoom = 1; +/* when hzoom = 1 each pixel represents 1 RE + * when 2, it is max of 2 horizontal REs + * useful to slow down display + */ +int hzoom = 1; +/* intensity is to scale the input + * normally, as float, cabs(input) is in [0..1] + * to get pixel color we *255 + * but maybe it's too intense, or not enough, + * so let's have a control the user can set at will + */ +float intensity = 255; + +pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + +void lock(void) +{ + pthread_mutex_lock(&m); +} + +void unlock(void) +{ + pthread_mutex_unlock(&m); +} + +typedef struct { + Display *d; + Window w; + XImage *img; + unsigned char *img_data; + int img_pos; +} gui_t; + +/* delay management begin */ + +long double frame_time; + +void time_start(void) +{ + struct timespec l; + if (clock_gettime(CLOCK_REALTIME, &l)) abort(); + frame_time = l.tv_sec * 1000000000. + l.tv_nsec; +} + +void delay(int fps) +{ + struct timespec t; + struct timespec delay; + long double cur_time; + + frame_time += 1000000000. / fps; + + if (clock_gettime(CLOCK_REALTIME, &t)) abort(); + + cur_time = t.tv_sec * 1000000000. + t.tv_nsec; + + if (frame_time < cur_time) { fprintf(stderr, "ERROR: we late\n"); return; } + + delay.tv_nsec = ((uint64_t)(frame_time-cur_time)) % 1000000000; + delay.tv_sec = ((uint64_t)(frame_time-cur_time)) / 1000000000; + + if (pselect(0, NULL, NULL, NULL, &delay, NULL)) { fprintf(stderr, "ERROR: pselect fails\n"); exit(1); } +} + +/* delay management end */ + +static void *gui_thread(void *_g) +{ + time_start(); + + gui_t *g = _g; + while (1) { + XEvent e; while (XPending(g->d)) XNextEvent(g->d, &e); + lock(); + XPutImage(g->d, g->w, DefaultGC(g->d, DefaultScreen(g->d)), g->img, 0, 0, 0, 0, + width, height/vzoom); + unlock(); + delay(10); + } + return 0; +} + +unsigned char re_to_pixel(float re) +{ + float v = fminf(255, re * intensity); + return v; +} + +unsigned char mix(unsigned char a, unsigned char b) +{ + return a > b ? a : b; +} + +void usage(void) +{ + printf("options:\n"); + printf(" -f input is float, not s16\n"); + printf(" -srate set sampling rate (default 61440000)\n"); + printf(" -prb set number of PRBs (default 106)\n"); + printf(" -mu set mu (default 1, 0 is for 15KHz, 1 for 30KHz, etc.)\n"); + printf(" -no-fft don't do fft, input is in frequency domain\n"); + printf(" -vzoom add some sort of vertical zoom, default 1, passing 2 divides height by 2, etc.\n"); + printf(" -hzoom add some sort of speed control, default 1, passing 2 means 2 REs per pixel, etc.\n"); + printf(" -delay delay 'delay' microseconds between processing of each symbol\n"); + printf(" -intensity set intensity of data pixel (default 255.)\n"); + printf(" -skip skip 'count' samples at beginning\n"); + exit(0); +} + +int main(int n, char **v) +{ + int input_is_float = 0; + int no_fft = 0; + int delay = 0; + int skip = 0; + int symbols_per_subframe; + long sampling_rate = 61440000; + int mu = 1; + int prb = 106; + + /* parse command line arguments */ + for (int i = 1; i < n; i++) { + if (!strcmp(v[i], "-f")) { input_is_float = 1; continue; } + if (!strcmp(v[i], "-no-fft")) { no_fft = 1; continue; } + if (!strcmp(v[i], "-vzoom")) { if (i>n-2) usage(); vzoom = atoi(v[++i]); continue; } + if (!strcmp(v[i], "-hzoom")) { if (i>n-2) usage(); hzoom = atoi(v[++i]); continue; } + if (!strcmp(v[i], "-delay")) { if (i>n-2) usage(); delay = atoi(v[++i]); continue; } + if (!strcmp(v[i], "-intensity")) { if (i>n-2) usage(); intensity = atof(v[++i]); continue; } + if (!strcmp(v[i], "-srate")) { if (i>n-2) usage(); sampling_rate = atol(v[++i]); continue; } + if (!strcmp(v[i], "-prb")) { if (i>n-2) usage(); prb = atoi(v[++i]); continue; } + if (!strcmp(v[i], "-mu")) { if (i>n-2) usage(); mu = atoi(v[++i]); continue; } + if (!strcmp(v[i], "-skip")) { if (i>n-2) usage(); skip = atoi(v[++i]); continue; } + usage(); + } + + symbols_per_subframe = 14 * (1 << mu); + + /* compute durations from sampling rate and mu (scs) */ + /* this is 38.211 5.3.1, a bit simplified/reordered */ + int ofdm_duration = sampling_rate / 15000 / (1 << mu); + int l0_cycle_prefix = sampling_rate / 480000 * (144*64 / (1 << mu) + 16*64) / 4096; + int cycle_prefix = sampling_rate / 480000 * (144*64 / (1 << mu)) / 4096; + + /* small sanity check */ + if (prb * 12 > ofdm_duration) { + printf("fatal: too many PRBs for this sampling rate / mu\n"); + exit(1); + } + + height = prb * 12; + + if (height % vzoom) { fprintf(stderr, "bad vzoom for this height, try 1, 2, 4, ...\n"); return 1; } + + gui_t g = { 0 }; + + /* open X11 display */ + g.d = XOpenDisplay(0); + if (!g.d) { + fprintf(stderr, "could not open display\n"); + return 1; + } + + /* create window */ + g.w = XCreateSimpleWindow(g.d, DefaultRootWindow(g.d), 0, 0, + width, height / vzoom, 0, WhitePixel(g.d, DefaultScreen(g.d)), + WhitePixel(g.d, DefaultScreen(g.d))); + XStoreName(g.d, g.w, "IQ"); + /* enable backing store */ + { + XSetWindowAttributes att; + att.backing_store = Always; + XChangeWindowAttributes(g.d, g.w, CWBackingStore, &att); + } + XSelectInput(g.d, g.w, + KeyPressMask | + ButtonPressMask | + ButtonReleaseMask | + PointerMotionMask | + ExposureMask | + StructureNotifyMask); + XMapWindow(g.d, g.w); + + g.img_data = calloc(1, width * height/vzoom * 4); if (!g.img_data) abort(); + g.img = XCreateImage(g.d, DefaultVisual(g.d, DefaultScreen(g.d)), 24 /*depth*/, + ZPixmap, 0 /*offset*/, (char*)g.img_data, width, height/vzoom, 8, 0); + + new_thread(gui_thread, &g); + + /* fft data */ + int fft_size = ofdm_duration; + float complex *in = fftwf_malloc(sizeof(float complex) * fft_size); + float complex *out = fftwf_malloc(sizeof(float complex) * fft_size); + fftwf_plan p_fft = fftwf_plan_dft_1d(fft_size, in, out, + FFTW_FORWARD, FFTW_ESTIMATE); + memset(in, 0, sizeof(float complex) * fft_size); + fftwf_execute(p_fft); + + int symbol = 0; + int back_color = 0; + int hpixel = 0; + + if (skip) { + if (input_is_float) { + float intime[skip * 2]; + if (fread(intime, skip * 8, 1, stdin) != 1) abort(); + } else { + short intime[skip * 2]; + if (fread(intime, skip * 4, 1, stdin) != 1) abort(); + } + } + + while (1) { + int cur_cycle_prefix; + if (symbol == 0 || symbol == 7 * (1 << mu)) + cur_cycle_prefix = l0_cycle_prefix; + else + cur_cycle_prefix = cycle_prefix; + + if (no_fft) goto without_fft; + + /* input signal is in time domain, timing aligned, do FFT */ + + /* read time domain data */ + if (input_is_float) { + float intime[(ofdm_duration + cur_cycle_prefix) * 2]; + if (fread(intime, (ofdm_duration + cur_cycle_prefix) * 8, 1, stdin) != 1) break; + + for (int i = 0; i < fft_size; i++) + /* skip cycle prefix */ + in[i] = intime[(cur_cycle_prefix + i) * 2] + I * intime[(cur_cycle_prefix + i) * 2 + 1]; + } else { + short intime[(ofdm_duration + cur_cycle_prefix) * 2]; + if (fread(intime, (ofdm_duration + cur_cycle_prefix) * 4, 1, stdin) != 1) break; + + for (int i = 0; i < fft_size; i++) + /* skip cycle prefix */ + in[i] = intime[(cur_cycle_prefix + i) * 2] / 2048. + I * intime[(cur_cycle_prefix + i) * 2 + 1] / 2048.; + } + + /* go to freq domain */ + fftwf_execute(p_fft); + + goto after_fft; + +without_fft: + /* input data is symbols, each containing full FFT data */ + if (input_is_float) { + float infreq[fft_size * 2]; + if (fread(infreq, fft_size * 2 * 4, 1, stdin) != 1) break; + + for (int i = 0; i < fft_size; i++) + out[i] = infreq[i * 2] + I * infreq[i * 2 + 1]; + } else { + short infreq[ofdm_duration * 2]; + if (fread(infreq, fft_size * 2 * 2, 1, stdin) != 1) break; + + for (int i = 0; i < fft_size; i++) + out[i] = infreq[i * 2] / 2048. + I * infreq[i * 2 + 1] / 2048.; + } + +after_fft: + lock(); + + /* extract data of this ofdm symbol */ + unsigned char *p = g.img_data + g.img_pos * 4; + int front_pos = (g.img_pos + 8) % width; + unsigned char *pfront = g.img_data + front_pos * 4; + for (int i = prb * 12 - 1; i >= 0; i -= vzoom) { + /* take the max */ + float out_val = 0; + for (int j = 0; j < vzoom; j++) { + int start = - prb * 12 / 2; + int pos = start + i + j; + /* neg freq are at the end of fft data array */ + int pos_in_fft = pos >= 0 + ? pos + : pos + fft_size; + float v = cabs(out[pos_in_fft]); + if (v > out_val) out_val = v; + } + int color = re_to_pixel(out_val); + /* grey level image mixed with some background changing every slot */ + unsigned char back_col[2][3] = { + { 100, 90, 90 }, + { 100, 60, 60, } + }; + if (hpixel == 0) { + p[0] = p[1] = p[2] = color; + } else { + p[0] = mix(p[0], color); + p[1] = mix(p[1], color); + p[2] = mix(p[2], color); + } + if (hpixel == hzoom - 1) { + p[0] = mix(p[0], back_col[back_color][0]); + p[1] = mix(p[1], back_col[back_color][1]); + p[2] = mix(p[2], back_col[back_color][2]); + } + p[3] = 255; + p += width * 4; + pfront[0] = 255; + pfront[1] = 128; + pfront[2] = 128; + pfront[3] = 255; + pfront += width * 4; + } + + unlock(); + + hpixel++; + if (hpixel == hzoom) { + hpixel = 0; + g.img_pos++; + if (g.img_pos == width) g.img_pos = 0; + } + + symbol++; + if (symbol == symbols_per_subframe) symbol = 0; + + /* switch back color every slot */ + if (symbol % 14 == 0) + back_color = 1 - back_color; + + if (delay) usleep(delay); + } + + return 0; +} From c607413eac6578a1570db34624138b75abc23cc3 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Sun, 22 Jun 2025 19:08:35 +0200 Subject: [PATCH 05/86] rfsim: add a T trace to dump RX IQ data Only for one antenna. --- radio/rfsimulator/simulator.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 3a2a0ae2d5..fca1b8a2e0 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -1077,6 +1077,10 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest *ptimestamp, t->nextRxTstamp, signal_energy(samplesVoid[0], nsamps)); + + /* trace only first antenna */ + T(T_USRP_RX_ANT0, T_INT(t->nextRxTstamp), T_BUFFER(samplesVoid[0], sampleToByte(nsamps, 1))); + return nsamps; } From fffe9531c360434cbd6cccb269146b5f7bd17dfd Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 22 Jul 2025 13:51:05 +0200 Subject: [PATCH 06/86] add assert in a case of wrong gNB configuration file --- common/utils/nr/nr_common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index 7875fe546a..7954db7d54 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -1160,6 +1160,7 @@ uint32_t get_ssb_offset_to_pointA(uint32_t absoluteFrequencySSB, const int ssb_offset_point_a = ((scaled_abs_diff / 12) - 10) * scaling; // Offset to point A needs to be divisible by scaling AssertFatal(ssb_offset_point_a % scaling == 0, "PRB offset %d not valid for scs %d\n", ssb_offset_point_a, ssbSubcarrierSpacing); + AssertFatal(ssb_offset_point_a >= 0, "ssb offset is negative %d for scs %d\n", ssb_offset_point_a, ssbSubcarrierSpacing); return ssb_offset_point_a; } From 3ca12f78e5a9f1abff7e57b4ae2b900d470b9085 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 23 Jul 2025 07:48:17 +0200 Subject: [PATCH 07/86] simplify function nr_pdcch_extract_rbs_single() --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 107 ++++++++------------------ 1 file changed, 33 insertions(+), 74 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index a027b042ca..7dcbbbe969 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -269,9 +269,9 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, LOG_DDD("dl_ch0 = &dl_ch_estimates[aarx = (%d)][0]\n", aarx); const int offset = symbol * coreset_nbr_rb * NBR_RE_PER_RB_WITH_DMRS; - c16_t *dl_ch0_ext = &dl_ch_estimates_ext[aarx][offset]; + c16_t *dl_ch0_ext = dl_ch_estimates_ext[aarx] + offset; LOG_DDD("dl_ch0_ext = &dl_ch_estimates_ext[aarx = (%d)][symbol * (frame_parms->N_RB_DL * 9) = (%d)]\n", aarx, offset); - c16_t *rxF_ext = &rxdataF_ext[aarx][offset]; + c16_t *rxF_ext = rxdataF_ext[aarx] + offset; LOG_DDD("rxF_ext = &rxdataF_ext[aarx = (%d)][symbol * (frame_parms->N_RB_DL * 9) = (%d)]\n", aarx, offset); /* @@ -301,81 +301,40 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, c_rb += 6; c_rb_by6 = c_rb / 6; } - - // first we set initial conditions for pointer to rxdataF depending on the situation of the first RB within the CORESET - // (c_rb = n_BWP_start) - if ((frame_parms->N_RB_DL & 1) == 1 && (c_rb + n_BWP_start) == (frame_parms->N_RB_DL >> 1)) { - // treatment of RB containing the DC - // if odd number RBs in system bandwidth and first RB to be treated is higher than middle system bandwidth (around DC) - // we have to treat the RB in two parts: first part from i=0 to 5, the data is at the end of rxdataF (pointing at the - // end of the table) - c16_t *rxF = rxFbase + frame_parms->first_carrier_offset + 12 * (c_rb + n_BWP_start); - - int i = 0, j = 0; - for (; i < 6; i++) { // treating first part of the RB note that i=5 would correspond to DC. We treat it in NR - if ((i != 1) && (i != 5)) { - dl_ch0_ext[j] = dl_ch0[i]; - rxF_ext[j] = rxF[i]; - LOG_DSYMB(""); - j++; - } else { - LOG_DSYMB("\t\t <==> DM-RS PDCCH, this is a pilot symbol\n"); - } - } - - // then we point at the begining of the symbol part of rxdataF do process second part of RB - for (; i < 12; i++) { - if ((i != 9)) { - dl_ch0_ext[j] = dl_ch0[i]; - rxF_ext[j] = rxFbase[i - 6]; - LOG_DSYMB(""); - j++; - } else { - LOG_DSYMB("\t\t <==> DM-RS PDCCH, this is a pilot symbol\n"); - } - } - - } else { // treatment of any RB that does not contain the DC - c16_t *rxF=NULL; - if ((frame_parms->N_RB_DL & 1) == 0) { - if ((c_rb + n_BWP_start) < (frame_parms->N_RB_DL >> 1)) - // if RB to be treated is lower than middle system bandwidth then rxdataF pointed - // at (offset + c_br + symbol * ofdm_symbol_size): even case - rxF = rxFbase + (frame_parms->first_carrier_offset + 12 * c_rb) + n_BWP_start * 12; - else - // number of RBs is even and c_rb is higher than half system bandwidth (we don't skip DC) - // if these conditions are true the pointer has to be situated at the 1st part of the rxdataF - // we point at the 1st part of the rxdataF in symbol - rxF = rxFbase + 12 * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1)); - } else { - if ((c_rb + n_BWP_start) < (frame_parms->N_RB_DL >> 1)) - // if RB to be treated is lower than middle system bandwidth then rxdataF pointed - // at (offset + c_br + symbol * ofdm_symbol_size): odd case - rxF = rxFbase + frame_parms->first_carrier_offset + 12 * (c_rb + n_BWP_start); - else if ((c_rb + n_BWP_start) > (frame_parms->N_RB_DL >> 1)) - // number of RBs is odd and c_rb is higher than half system bandwidth + 1 - // if these conditions are true the pointer has to be situated at the 1st part of - // the rxdataF just after the first IQ symbols of the RB containing DC - // we point at the 1st part of the rxdataF in symbol - rxF = rxFbase + 12 * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1)) - 6; - } - AssertFatal(rxF, "bug"); - int j = 0; - - for (int i = 0; i < 12; i++) { - if ((i != 1) && (i != 5) && (i != 9)) { - rxF_ext[j] = rxF[i]; - dl_ch0_ext[j] = dl_ch0[i]; - LOG_DSYMB(""); - j++; - } else { - LOG_DSYMB("\t\t <==> DM-RS PDCCH, this is a pilot symbol\n"); - } - } + c16_t *rxF = NULL; + if ((frame_parms->N_RB_DL & 1) == 0) { + if ((c_rb + n_BWP_start) < (frame_parms->N_RB_DL >> 1)) + // if RB to be treated is lower than middle system bandwidth then rxdataF pointed + // at (offset + c_br + symbol * ofdm_symbol_size): even case + rxF = rxFbase + (frame_parms->first_carrier_offset + 12 * c_rb) + n_BWP_start * 12; + else + // number of RBs is even and c_rb is higher than half system bandwidth (we don't skip DC) + // if these conditions are true the pointer has to be situated at the 1st part of the rxdataF + // we point at the 1st part of the rxdataF in symbol + rxF = rxFbase + 12 * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1)); + } else { + if ((c_rb + n_BWP_start) < (frame_parms->N_RB_DL >> 1)) + // if RB to be treated is lower than middle system bandwidth then rxdataF pointed + // at (offset + c_br + symbol * ofdm_symbol_size): odd case + rxF = rxFbase + frame_parms->first_carrier_offset + 12 * (c_rb + n_BWP_start); + else if ((c_rb + n_BWP_start) > (frame_parms->N_RB_DL >> 1)) + // number of RBs is odd and c_rb is higher than half system bandwidth + 1 + // if these conditions are true the pointer has to be situated at the 1st part of + // the rxdataF just after the first IQ symbols of the RB containing DC + // we point at the 1st part of the rxdataF in symbol + rxF = rxFbase + 12 * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1)) - 6; + else + rxF = rxFbase + frame_parms->first_carrier_offset + 12 * (c_rb + n_BWP_start); + } + const int valid_re[NBR_RE_PER_RB_WITHOUT_DMRS] = {0, 2, 3, 4, 6, 7, 8, 10, 11}; + for (int i = 0; i < sizeofArray(valid_re); i++) { + rxF_ext[i] = rxF[valid_re[i]]; + dl_ch0_ext[i] = dl_ch0[valid_re[i]]; + LOG_DSYMB(""); } dl_ch0_ext += NBR_RE_PER_RB_WITHOUT_DMRS; rxF_ext += NBR_RE_PER_RB_WITHOUT_DMRS; - dl_ch0 += 12; + dl_ch0 += NBR_RE_PER_RB_WITH_DMRS; } } } From 2c22f49a456075323d8cfb3d68dccdef191c5773 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 23 Jul 2025 07:49:37 +0200 Subject: [PATCH 08/86] simplify nr_pdcch_channel_compensation() --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 38 +++++++++++---------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 7dcbbbe969..c5ea4e35a0 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -352,29 +352,23 @@ void nr_pdcch_channel_compensation(int32_t rx_size, uint32_t coreset_nbr_rb) { for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { - simde__m128i *dl_ch128 = (simde__m128i *)&dl_ch_estimates_ext[aarx][symbol * coreset_nbr_rb * 12]; - simde__m128i *rxdataF128 = (simde__m128i *)&rxdataF_ext[aarx][symbol * coreset_nbr_rb * 12]; - simde__m128i *rxdataF_comp128 = (simde__m128i *)&rxdataF_comp[aarx][symbol * coreset_nbr_rb * 12]; - //printf("ch compensation dl_ch ext addr %p \n", &dl_ch_estimates_ext[(aatx<<1)+aarx][symbol*20*12]); - //printf("rxdataf ext addr %p symbol %d\n", &rxdataF_ext[aarx][symbol*20*12], symbol); - //printf("rxdataf_comp addr %p\n",&rxdataF_comp[(aatx<<1)+aarx][symbol*20*12]); - + const int sz = coreset_nbr_rb * 12; + c16_t *dl_ch = dl_ch_estimates_ext[aarx] + symbol * sz; + c16_t *rxdataF = rxdataF_ext[aarx] + symbol * sz; + c16_t *rxdataF_c = rxdataF_comp[aarx] + symbol * sz; // multiply by conjugated channel - mult_cpx_conj_vector((c16_t *)dl_ch128, (c16_t *)rxdataF128, (c16_t *)rxdataF_comp128, 12 * coreset_nbr_rb, output_shift); - - for (int rb = 0; rb < 12 * coreset_nbr_rb; rb++) { - LOG_DDD("rxdataF128[%d]=(%d,%d) X dlch[%d]=(%d,%d) rxdataF_comp128[%d]=(%d,%d)\n", - rb, - ((c16_t *)rxdataF128)[rb].r, - ((c16_t *)rxdataF128)[rb].i, - rb, - ((c16_t *)dl_ch128)[rb].r, - ((c16_t *)dl_ch128)[rb].i, - rb, - ((c16_t *)rxdataF_comp128)[rb].r, - ((c16_t *)rxdataF_comp128)[rb].i); - - } + mult_cpx_conj_vector(dl_ch, rxdataF, rxdataF_c, sz, output_shift); + for (int rb = 0; rb < sz; rb++) + LOG_DDD("rxdataF[%d]=(%d,%d) X dlch[%d]=(%d,%d) rxdataF_comp[%d]=(%d,%d)\n", + rb, + rxdataF[rb].r, + rxdataF[rb].i, + rb, + dl_ch[rb].r, + dl_ch[rb].i, + rb, + rxdataF_c[rb].r, + rxdataF_c[rb].i); } } From b30da5c0b22476f8567931ddcc0ecbc8e85654f3 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 23 Jul 2025 07:52:08 +0200 Subject: [PATCH 09/86] reduce size and make local rxdataF_comp in nr_rx_pdcch() --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 87 +++++++++++---------------- 1 file changed, 35 insertions(+), 52 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index c5ea4e35a0..29f8fdb6b3 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -44,9 +44,7 @@ #include "assertions.h" #include "T.h" -//#define DEBUG_DCI_DECODING 1 - -//#define NR_PDCCH_DCI_DEBUG // activates NR_PDCCH_DCI_DEBUG logs +// #define NR_PDCCH_DCI_DEBUG // activates NR_PDCCH_DCI_DEBUG logs #ifdef NR_PDCCH_DCI_DEBUG #define LOG_DDD(a, ...) printf("<-NR_PDCCH_DCI_DEBUG (%s)-> " a, __func__, ##__VA_ARGS__ ) #define LOG_DSYMB(b) \ @@ -188,20 +186,23 @@ static void nr_pdcch_demapping_deinterleaving(c16_t *llr, static void nr_pdcch_llr(NR_DL_FRAME_PARMS *frame_parms, int32_t rx_size, - c16_t rxdataF_comp[][rx_size], + uint32_t coreset_nbr_rb, + c16_t rxdataF_comp[coreset_nbr_rb * 12], c16_t *pdcch_llr, - uint8_t symbol, - uint32_t coreset_nbr_rb) + uint8_t symbol) { - c16_t *rxF = &rxdataF_comp[0][(symbol * coreset_nbr_rb * 12)]; - c16_t *pdcch_llrp = &pdcch_llr[symbol * coreset_nbr_rb * 9]; + c16_t *rxF = rxdataF_comp; + c16_t *llr = pdcch_llr + symbol * coreset_nbr_rb * 9; - if (!pdcch_llrp) { + if (!llr) { LOG_E(NR_PHY_DCI, "pdcch_qpsk_llr: llr is null, symbol %d\n", symbol); return; } - LOG_DDD("llr logs: pdcch qpsk llr for symbol %d (pos %d), llr offset %ld\n",symbol,(symbol*frame_parms->N_RB_DL*12),pdcch_llrp-pdcch_llr); + LOG_DDD("llr logs: pdcch qpsk llr for symbol %d (pos %d), llr offset %ld\n", + symbol, + symbol * frame_parms->N_RB_DL * 12, + llr - pdcch_llr); for (int i = 0; i < coreset_nbr_rb * 9; i++) { // We clip the signal @@ -210,10 +211,9 @@ static void nr_pdcch_llr(NR_DL_FRAME_PARMS *frame_parms, res.r = max(-32, res.r); res.i = min(rxF->i, 31); res.i = max(-32, res.i); - *pdcch_llrp = res; - LOG_DDD("llr logs: rb=%d i=%d *rxF:%d => *pdcch_llrp:%d\n",i/18,i,*rxF,*pdcch_llrp); + *llr++ = res; + LOG_DDD("llr logs: rb=%d i=%d rxF:%d,%d => pdcch_llr:%d,%d\n", i / 18, i, rxF->r, rxF->i, llr->r, llr->i); rxF++; - pdcch_llrp++; } } @@ -330,7 +330,6 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, for (int i = 0; i < sizeofArray(valid_re); i++) { rxF_ext[i] = rxF[valid_re[i]]; dl_ch0_ext[i] = dl_ch0[valid_re[i]]; - LOG_DSYMB(""); } dl_ch0_ext += NBR_RE_PER_RB_WITHOUT_DMRS; rxF_ext += NBR_RE_PER_RB_WITHOUT_DMRS; @@ -339,23 +338,21 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, } } -#define print_shorts(s,x) printf("%s %d,%d,%d,%d,%d,%d,%d,%d\n",s,(x)[0],(x)[1],(x)[2],(x)[3],(x)[4],(x)[5],(x)[6],(x)[7]) - -void nr_pdcch_channel_compensation(int32_t rx_size, - c16_t rxdataF_ext[][rx_size], - c16_t dl_ch_estimates_ext[][rx_size], - c16_t rxdataF_comp[][rx_size], - int32_t **rho, - NR_DL_FRAME_PARMS *frame_parms, - uint8_t symbol, - uint8_t output_shift, - uint32_t coreset_nbr_rb) +static void nr_pdcch_channel_compensation(int32_t rx_size, + uint32_t coreset_nbr_rb, + c16_t rxdataF_ext[][rx_size], + c16_t dl_ch_estimates_ext[][rx_size], + c16_t rxdataF_comp[][coreset_nbr_rb * 12], + int32_t **rho, + NR_DL_FRAME_PARMS *frame_parms, + uint8_t symbol, + uint8_t output_shift) { for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { const int sz = coreset_nbr_rb * 12; c16_t *dl_ch = dl_ch_estimates_ext[aarx] + symbol * sz; c16_t *rxdataF = rxdataF_ext[aarx] + symbol * sz; - c16_t *rxdataF_c = rxdataF_comp[aarx] + symbol * sz; + c16_t *rxdataF_c = rxdataF_comp[aarx]; // multiply by conjugated channel mult_cpx_conj_vector(dl_ch, rxdataF, rxdataF_c, sz, output_shift); for (int rb = 0; rb < sz; rb++) @@ -375,10 +372,11 @@ void nr_pdcch_channel_compensation(int32_t rx_size, static void nr_pdcch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, int32_t rx_size, c16_t rxdataF_comp[][rx_size], int symbol) { if (frame_parms->nb_antennas_rx>1) { + LOG_D(NR_PHY_DCI, "we enter nr_pdcch_detection_mrc(frame_parms->nb_antennas_rx=%d)\n", frame_parms->nb_antennas_rx); const int sz = frame_parms->N_RB_DL * 12; - - simde__m128i *rxdataF_comp128_0 = (simde__m128i *)&rxdataF_comp[0][symbol * sz]; - simde__m128i *rxdataF_comp128_1 = (simde__m128i *)&rxdataF_comp[1][symbol * sz]; + + simde__m128i *rxdataF_comp128_0 = (simde__m128i *)rxdataF_comp[0]; + simde__m128i *rxdataF_comp128_1 = (simde__m128i *)rxdataF_comp[1]; // MRC on each re of rb for (int i = 0; i < sz >> 2; i++) { @@ -406,15 +404,10 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, // Pointers to extracted PDCCH symbols in frequency-domain. int32_t rx_size = ((4 * frame_parms->N_RB_DL * 12 + 31) >> 5) << 5; __attribute__((aligned(32))) c16_t rxdataF_ext[frame_parms->nb_antennas_rx][rx_size]; - __attribute__((aligned(32))) c16_t rxdataF_comp[frame_parms->nb_antennas_rx][rx_size]; __attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[frame_parms->nb_antennas_rx][rx_size]; - - memset(rxdataF_comp, 0, sizeof(rxdataF_comp)); - // Pointer to llrs, 4-bit resolution. int32_t llr_size = 4 * n_rb * 9; c16_t llr[llr_size]; - memset(llr, 0, sizeof(llr)); LOG_D(NR_PHY_DCI, @@ -466,36 +459,26 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, LOG_D(NR_PHY_DCI, "we enter nr_pdcch_channel_compensation(log2_maxh=%d)\n", log2_maxh); LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_compensation(rxdataF_ext x dl_ch_estimates_ext -> rxdataF_comp)\n"); // compute LLRs for ofdm symbol 0 only - nr_pdcch_channel_compensation(rx_size, rxdataF_ext, + __attribute__((aligned(32))) c16_t rxdataF_comp[frame_parms->nb_antennas_rx][n_rb * 12]; + nr_pdcch_channel_compensation(rx_size, + n_rb, + rxdataF_ext, pdcch_dl_ch_estimates_ext, rxdataF_comp, NULL, frame_parms, s, - log2_maxh, - n_rb); // log2_maxh+I0_shift + log2_maxh); // log2_maxh+I0_shift - UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), frame_parms->nb_antennas_rx, rx_size, 0); + UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), frame_parms->nb_antennas_rx, n_rb * 12, 0); - if (frame_parms->nb_antennas_rx > 1) { - LOG_D(NR_PHY_DCI, "we enter nr_pdcch_detection_mrc(frame_parms->nb_antennas_rx=%d)\n", frame_parms->nb_antennas_rx); - nr_pdcch_detection_mrc(frame_parms, rx_size, rxdataF_comp,s); - } + nr_pdcch_detection_mrc(frame_parms, n_rb * 12, rxdataF_comp, s); LOG_D(NR_PHY_DCI, "we enter nr_pdcch_llr(for symbol %d), pdcch_vars[eNB_id]->rxdataF_comp ---> pdcch_vars[eNB_id]->llr \n", s); LOG_D(NR_PHY_DCI, "in nr_pdcch_llr(rxdataF_comp -> llr)\n"); - nr_pdcch_llr(frame_parms, - rx_size, - rxdataF_comp, - llr, - s, - n_rb); + nr_pdcch_llr(frame_parms, rx_size, n_rb, rxdataF_comp[0], llr, s); UEscopeCopy(ue, pdcchLlr, llr, sizeof(c16_t), 1, llr_size, 0); - -#ifdef DEBUG_DCI_DECODING - printf("demapping: slot %u, mi %d\n",slot,get_mi(frame_parms,slot)); -#endif } nr_pdcch_demapping_deinterleaving(llr, From e8f6d14c47f501793e2a70581c6314afada8e743 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 23 Jul 2025 07:56:33 +0200 Subject: [PATCH 10/86] simplify and make more local variables in nr_rx_pdcch() and fix small bugs (size 9 RE instaed of 12 RE) --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 116 ++++++++++---------------- 1 file changed, 42 insertions(+), 74 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 29f8fdb6b3..112739e09b 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -218,35 +218,29 @@ static void nr_pdcch_llr(NR_DL_FRAME_PARMS *frame_parms, } //compute average channel_level on each (TX,RX) antenna pair -void nr_pdcch_channel_level(int32_t rx_size, - c16_t dl_ch_estimates_ext[][rx_size], - NR_DL_FRAME_PARMS *frame_parms, - int32_t *avg, - int symbol, - int nb_rb) +static int32_t nr_pdcch_channel_level(int nb_rb, c16_t dl_ch_estimates_ext[][nb_rb * 9], int nb_antennas_rx, int symbol) { - for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { - - simde__m128i *dl_ch128 = (simde__m128i *)&dl_ch_estimates_ext[aarx][symbol * nb_rb * 12]; - + int32_t avgs = 0; + for (int aarx = 0; aarx < nb_antennas_rx; aarx++) { + simde__m128i *dl_ch128 = (simde__m128i *)&dl_ch_estimates_ext[aarx]; //compute average level - avg[aarx] = simde_mm_average(dl_ch128, nb_rb * 12, 0, nb_rb * 12); - //LOG_DDD("Channel level : %d\n", avg[aarx]); + int32_t tmp = simde_mm_average(dl_ch128, nb_rb * 9, 0, nb_rb * 9); + avgs = cmax(avgs, tmp); } + return avgs; } // This function will extract the mapped DM-RS PDCCH REs as per 38.211 Section 7.4.1.3.2 (Mapping to physical resources) static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, + uint32_t coreset_nbr_rb, c16_t rxdataF[][rxdataF_sz], int32_t est_size, c16_t dl_ch_estimates[][est_size], - int32_t rx_size, - c16_t rxdataF_ext[][rx_size], - c16_t dl_ch_estimates_ext[][rx_size], + c16_t rxdataF_ext[][coreset_nbr_rb * 9], + c16_t dl_ch_estimates_ext[][coreset_nbr_rb * 9], int symbol, NR_DL_FRAME_PARMS *frame_parms, uint8_t *coreset_freq_dom, - uint32_t coreset_nbr_rb, uint32_t n_BWP_start) { /* @@ -268,11 +262,8 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, c16_t *rxFbase = rxdataF[aarx] + frame_parms->ofdm_symbol_size * symbol; LOG_DDD("dl_ch0 = &dl_ch_estimates[aarx = (%d)][0]\n", aarx); - const int offset = symbol * coreset_nbr_rb * NBR_RE_PER_RB_WITH_DMRS; - c16_t *dl_ch0_ext = dl_ch_estimates_ext[aarx] + offset; - LOG_DDD("dl_ch0_ext = &dl_ch_estimates_ext[aarx = (%d)][symbol * (frame_parms->N_RB_DL * 9) = (%d)]\n", aarx, offset); - c16_t *rxF_ext = rxdataF_ext[aarx] + offset; - LOG_DDD("rxF_ext = &rxdataF_ext[aarx = (%d)][symbol * (frame_parms->N_RB_DL * 9) = (%d)]\n", aarx, offset); + c16_t *dl_ch0_ext = dl_ch_estimates_ext[aarx]; + c16_t *rxF_ext = rxdataF_ext[aarx]; /* * The following for loop handles treatment of PDCCH contained in table rxdataF (in frequency domain) @@ -338,20 +329,17 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, } } -static void nr_pdcch_channel_compensation(int32_t rx_size, - uint32_t coreset_nbr_rb, - c16_t rxdataF_ext[][rx_size], - c16_t dl_ch_estimates_ext[][rx_size], +static void nr_pdcch_channel_compensation(uint32_t coreset_nbr_rb, + c16_t rxdataF_ext[][coreset_nbr_rb * 9], + c16_t dl_ch_estimates_ext[][coreset_nbr_rb * 9], c16_t rxdataF_comp[][coreset_nbr_rb * 12], - int32_t **rho, - NR_DL_FRAME_PARMS *frame_parms, - uint8_t symbol, + int antRx, uint8_t output_shift) { - for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { - const int sz = coreset_nbr_rb * 12; - c16_t *dl_ch = dl_ch_estimates_ext[aarx] + symbol * sz; - c16_t *rxdataF = rxdataF_ext[aarx] + symbol * sz; + for (int aarx = 0; aarx < antRx; aarx++) { + const int sz = coreset_nbr_rb * 9; + c16_t *dl_ch = dl_ch_estimates_ext[aarx]; + c16_t *rxdataF = rxdataF_ext[aarx]; c16_t *rxdataF_c = rxdataF_comp[aarx]; // multiply by conjugated channel mult_cpx_conj_vector(dl_ch, rxdataF, rxdataF_c, sz, output_shift); @@ -369,19 +357,18 @@ static void nr_pdcch_channel_compensation(int32_t rx_size, } } -static void nr_pdcch_detection_mrc(NR_DL_FRAME_PARMS *frame_parms, int32_t rx_size, c16_t rxdataF_comp[][rx_size], int symbol) +static void nr_pdcch_detection_mrc(int n_rb, c16_t rxdataF_comp[][n_rb * 9]) { - if (frame_parms->nb_antennas_rx>1) { - LOG_D(NR_PHY_DCI, "we enter nr_pdcch_detection_mrc(frame_parms->nb_antennas_rx=%d)\n", frame_parms->nb_antennas_rx); - const int sz = frame_parms->N_RB_DL * 12; + LOG_D(NR_PHY_DCI, "we enter nr_pdcch_detection_mrc (hard coded 2 antennas)\n"); + const int sz = n_rb * 9; - simde__m128i *rxdataF_comp128_0 = (simde__m128i *)rxdataF_comp[0]; - simde__m128i *rxdataF_comp128_1 = (simde__m128i *)rxdataF_comp[1]; + simde__m128i *rxdataF_comp128_0 = (simde__m128i *)rxdataF_comp[0]; + simde__m128i *rxdataF_comp128_1 = (simde__m128i *)rxdataF_comp[1]; - // MRC on each re of rb - for (int i = 0; i < sz >> 2; i++) { - rxdataF_comp128_0[i] = simde_mm_adds_epi16(simde_mm_srai_epi16(rxdataF_comp128_0[i], 1), simde_mm_srai_epi16(rxdataF_comp128_1[i], 1)); - } + // MRC on each re of rb + for (int i = 0; i < sz >> 2; i++) { + rxdataF_comp128_0[i] = + simde_mm_adds_epi16(simde_mm_srai_epi16(rxdataF_comp128_0[i], 1), simde_mm_srai_epi16(rxdataF_comp128_1[i], 1)); } } @@ -395,16 +382,12 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, { NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms; - uint8_t log2_maxh, aarx; - int32_t avgs; - int32_t avgP[4]; int n_rb,rb_offset; get_coreset_rballoc(rel15->coreset.frequency_domain_resource,&n_rb,&rb_offset); + const int antRx = frame_parms->nb_antennas_rx; // Pointers to extracted PDCCH symbols in frequency-domain. int32_t rx_size = ((4 * frame_parms->N_RB_DL * 12 + 31) >> 5) << 5; - __attribute__((aligned(32))) c16_t rxdataF_ext[frame_parms->nb_antennas_rx][rx_size]; - __attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[frame_parms->nb_antennas_rx][rx_size]; // Pointer to llrs, 4-bit resolution. int32_t llr_size = 4 * n_rb * 9; c16_t llr[llr_size]; @@ -418,61 +401,46 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, for (int s=rel15->coreset.StartSymbolIndex; s<(rel15->coreset.StartSymbolIndex+rel15->coreset.duration); s++) { LOG_D(NR_PHY_DCI, "in nr_pdcch_extract_rbs_single(rxdataF -> rxdataF_ext || dl_ch_estimates -> dl_ch_estimates_ext)\n"); + __attribute__((aligned(32))) c16_t rxdataF_ext[antRx][n_rb * 9]; + __attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[antRx][n_rb * 9]; nr_pdcch_extract_rbs_single(ue->frame_parms.samples_per_slot_wCP, + n_rb, rxdataF, pdcch_est_size, pdcch_dl_ch_estimates, - rx_size, rxdataF_ext, pdcch_dl_ch_estimates_ext, s, frame_parms, rel15->coreset.frequency_domain_resource, - n_rb, rel15->BWPStart); - LOG_D(NR_PHY_DCI, - "we enter nr_pdcch_channel_level(avgP=%d) => compute channel level based on ofdm symbol 0, " - "pdcch_vars[eNB_id]->dl_ch_estimates_ext\n", - *avgP); LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_level(dl_ch_estimates_ext -> dl_ch_estimates_ext)\n"); // compute channel level based on ofdm symbol 0 - nr_pdcch_channel_level(rx_size, - pdcch_dl_ch_estimates_ext, - frame_parms, - avgP, - s, - n_rb); - avgs = 0; - - for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) - avgs = cmax(avgs, avgP[aarx]); - - log2_maxh = (log2_approx(avgs) / 2) + 5; //+frame_parms->nb_antennas_rx; + int32_t avgs = nr_pdcch_channel_level(n_rb, pdcch_dl_ch_estimates_ext, antRx, s); + uint8_t log2_maxh = (log2_approx(avgs) / 2) + 5; //+antRx; #ifdef UE_DEBUG_TRACE LOG_D(NR_PHY_DCI, "slot %d: pdcch log2_maxh = %d (%d,%d)\n", proc->nr_slot_rx, log2_maxh, avgP[0], avgs); #endif #if T_TRACER - T(T_UE_PHY_PDCCH_ENERGY, T_INT(0), T_INT(0), T_INT(proc->frame_rx % 1024), T_INT(proc->nr_slot_rx), T_INT(avgP[0]), T_INT(avgP[1]), T_INT(avgP[2]), T_INT(avgP[3])); + T(T_UE_PHY_PDCCH_ENERGY, T_INT(0), T_INT(0), T_INT(proc->frame_rx % 1024), T_INT(proc->nr_slot_rx), T_INT(avgs)); #endif LOG_D(NR_PHY_DCI, "we enter nr_pdcch_channel_compensation(log2_maxh=%d)\n", log2_maxh); LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_compensation(rxdataF_ext x dl_ch_estimates_ext -> rxdataF_comp)\n"); // compute LLRs for ofdm symbol 0 only - __attribute__((aligned(32))) c16_t rxdataF_comp[frame_parms->nb_antennas_rx][n_rb * 12]; - nr_pdcch_channel_compensation(rx_size, - n_rb, + __attribute__((aligned(32))) c16_t rxdataF_comp[antRx][n_rb * 12]; + nr_pdcch_channel_compensation(n_rb, rxdataF_ext, pdcch_dl_ch_estimates_ext, rxdataF_comp, - NULL, - frame_parms, - s, + antRx, log2_maxh); // log2_maxh+I0_shift - UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), frame_parms->nb_antennas_rx, n_rb * 12, 0); + UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), antRx, n_rb * 12, 0); - nr_pdcch_detection_mrc(frame_parms, n_rb * 12, rxdataF_comp, s); + if (antRx > 1) + nr_pdcch_detection_mrc(n_rb * 12, rxdataF_comp); LOG_D(NR_PHY_DCI, "we enter nr_pdcch_llr(for symbol %d), pdcch_vars[eNB_id]->rxdataF_comp ---> pdcch_vars[eNB_id]->llr \n", s); LOG_D(NR_PHY_DCI, "in nr_pdcch_llr(rxdataF_comp -> llr)\n"); From 19d783f6d983b83e0072f79d43056fc11c4679f4 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 23 Jul 2025 08:03:48 +0200 Subject: [PATCH 11/86] generalize constant nb re per rn in dci_nr.c --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 80 +++++++++++++-------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 112739e09b..5f41880251 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -61,14 +61,14 @@ #define LOG_DDD(a...) #define LOG_DSYMB(a...) #endif + #define NR_NBR_CORESET_ACT_BWP 3 // The number of CoreSets per BWP is limited to 3 (including initial CORESET: ControlResourceId 0) #define NR_NBR_SEARCHSPACE_ACT_BWP \ 10 // The number of SearSpaces per BWP is limited to 10 (including initial SEARCHSPACE: SearchSpaceId 0) -#ifdef LOG_I -#undef LOG_I -#define LOG_I(A, B...) printf(B) -#endif +#define RE_PER_RB 12 +// after removing the 3 DMRS RE, the RB contains 9 RE with PDCCH +#define RE_PER_RB_OUT_DMRS 9 static void nr_pdcch_demapping_deinterleaving(c16_t *llr, c16_t *e_rx, @@ -168,7 +168,7 @@ static void nr_pdcch_demapping_deinterleaving(c16_t *llr, } int rb_count = 0; - const int data_sc = 9; // 9 sub-carriers with data per PRB + const int data_sc = RE_PER_RB_OUT_DMRS; // 9 sub-carriers with data per PRB for (int c_id = 0; c_id < number_of_candidates; c_id++) { for (int symbol_idx = start_symbol; symbol_idx < start_symbol + coreset_time_dur; symbol_idx++) { for (int cce_count = 0; cce_count < L[c_id]; cce_count++) { @@ -187,12 +187,12 @@ static void nr_pdcch_demapping_deinterleaving(c16_t *llr, static void nr_pdcch_llr(NR_DL_FRAME_PARMS *frame_parms, int32_t rx_size, uint32_t coreset_nbr_rb, - c16_t rxdataF_comp[coreset_nbr_rb * 12], + c16_t rxdataF_comp[coreset_nbr_rb * RE_PER_RB], c16_t *pdcch_llr, uint8_t symbol) { c16_t *rxF = rxdataF_comp; - c16_t *llr = pdcch_llr + symbol * coreset_nbr_rb * 9; + c16_t *llr = pdcch_llr + symbol * coreset_nbr_rb * RE_PER_RB_OUT_DMRS; if (!llr) { LOG_E(NR_PHY_DCI, "pdcch_qpsk_llr: llr is null, symbol %d\n", symbol); @@ -201,10 +201,10 @@ static void nr_pdcch_llr(NR_DL_FRAME_PARMS *frame_parms, LOG_DDD("llr logs: pdcch qpsk llr for symbol %d (pos %d), llr offset %ld\n", symbol, - symbol * frame_parms->N_RB_DL * 12, + symbol * frame_parms->N_RB_DL * RE_PER_RB, llr - pdcch_llr); - for (int i = 0; i < coreset_nbr_rb * 9; i++) { + for (int i = 0; i < coreset_nbr_rb * RE_PER_RB_OUT_DMRS; i++) { // We clip the signal c16_t res; res.r = min(rxF->r, 31); @@ -218,13 +218,16 @@ static void nr_pdcch_llr(NR_DL_FRAME_PARMS *frame_parms, } //compute average channel_level on each (TX,RX) antenna pair -static int32_t nr_pdcch_channel_level(int nb_rb, c16_t dl_ch_estimates_ext[][nb_rb * 9], int nb_antennas_rx, int symbol) +static int32_t nr_pdcch_channel_level(int nb_rb, + c16_t dl_ch_estimates_ext[][nb_rb * RE_PER_RB_OUT_DMRS], + int nb_antennas_rx, + int symbol) { int32_t avgs = 0; for (int aarx = 0; aarx < nb_antennas_rx; aarx++) { simde__m128i *dl_ch128 = (simde__m128i *)&dl_ch_estimates_ext[aarx]; //compute average level - int32_t tmp = simde_mm_average(dl_ch128, nb_rb * 9, 0, nb_rb * 9); + int32_t tmp = simde_mm_average(dl_ch128, nb_rb * RE_PER_RB_OUT_DMRS, 0, nb_rb * RE_PER_RB_OUT_DMRS); avgs = cmax(avgs, tmp); } return avgs; @@ -236,8 +239,8 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, c16_t rxdataF[][rxdataF_sz], int32_t est_size, c16_t dl_ch_estimates[][est_size], - c16_t rxdataF_ext[][coreset_nbr_rb * 9], - c16_t dl_ch_estimates_ext[][coreset_nbr_rb * 9], + c16_t rxdataF_ext[][coreset_nbr_rb * RE_PER_RB_OUT_DMRS], + c16_t dl_ch_estimates_ext[][coreset_nbr_rb * RE_PER_RB_OUT_DMRS], int symbol, NR_DL_FRAME_PARMS *frame_parms, uint8_t *coreset_freq_dom, @@ -254,9 +257,6 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, * */ -#define NBR_RE_PER_RB_WITH_DMRS 12 - // after removing the 3 DMRS RE, the RB contains 9 RE with PDCCH -#define NBR_RE_PER_RB_WITHOUT_DMRS 9 for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { const c16_t *dl_ch0 = dl_ch_estimates[aarx] + frame_parms->ofdm_symbol_size * symbol; c16_t *rxFbase = rxdataF[aarx] + frame_parms->ofdm_symbol_size * symbol; @@ -297,47 +297,45 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, if ((c_rb + n_BWP_start) < (frame_parms->N_RB_DL >> 1)) // if RB to be treated is lower than middle system bandwidth then rxdataF pointed // at (offset + c_br + symbol * ofdm_symbol_size): even case - rxF = rxFbase + (frame_parms->first_carrier_offset + 12 * c_rb) + n_BWP_start * 12; + rxF = rxFbase + (frame_parms->first_carrier_offset + RE_PER_RB * c_rb) + n_BWP_start * RE_PER_RB; else // number of RBs is even and c_rb is higher than half system bandwidth (we don't skip DC) // if these conditions are true the pointer has to be situated at the 1st part of the rxdataF // we point at the 1st part of the rxdataF in symbol - rxF = rxFbase + 12 * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1)); + rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1)); } else { if ((c_rb + n_BWP_start) < (frame_parms->N_RB_DL >> 1)) // if RB to be treated is lower than middle system bandwidth then rxdataF pointed // at (offset + c_br + symbol * ofdm_symbol_size): odd case - rxF = rxFbase + frame_parms->first_carrier_offset + 12 * (c_rb + n_BWP_start); + rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); else if ((c_rb + n_BWP_start) > (frame_parms->N_RB_DL >> 1)) // number of RBs is odd and c_rb is higher than half system bandwidth + 1 // if these conditions are true the pointer has to be situated at the 1st part of // the rxdataF just after the first IQ symbols of the RB containing DC // we point at the 1st part of the rxdataF in symbol - rxF = rxFbase + 12 * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1)) - 6; + rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1)) - 6; else - rxF = rxFbase + frame_parms->first_carrier_offset + 12 * (c_rb + n_BWP_start); + rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); } - const int valid_re[NBR_RE_PER_RB_WITHOUT_DMRS] = {0, 2, 3, 4, 6, 7, 8, 10, 11}; + const int valid_re[RE_PER_RB_OUT_DMRS] = {0, 2, 3, 4, 6, 7, 8, 10, 11}; for (int i = 0; i < sizeofArray(valid_re); i++) { - rxF_ext[i] = rxF[valid_re[i]]; - dl_ch0_ext[i] = dl_ch0[valid_re[i]]; + *rxF_ext++ = rxF[valid_re[i]]; + *dl_ch0_ext++ = dl_ch0[valid_re[i]]; } - dl_ch0_ext += NBR_RE_PER_RB_WITHOUT_DMRS; - rxF_ext += NBR_RE_PER_RB_WITHOUT_DMRS; - dl_ch0 += NBR_RE_PER_RB_WITH_DMRS; + dl_ch0 += RE_PER_RB; } } } static void nr_pdcch_channel_compensation(uint32_t coreset_nbr_rb, - c16_t rxdataF_ext[][coreset_nbr_rb * 9], - c16_t dl_ch_estimates_ext[][coreset_nbr_rb * 9], - c16_t rxdataF_comp[][coreset_nbr_rb * 12], + c16_t rxdataF_ext[][coreset_nbr_rb * RE_PER_RB_OUT_DMRS], + c16_t dl_ch_estimates_ext[][coreset_nbr_rb * RE_PER_RB_OUT_DMRS], + c16_t rxdataF_comp[][coreset_nbr_rb * RE_PER_RB], int antRx, uint8_t output_shift) { for (int aarx = 0; aarx < antRx; aarx++) { - const int sz = coreset_nbr_rb * 9; + const int sz = coreset_nbr_rb * RE_PER_RB_OUT_DMRS; c16_t *dl_ch = dl_ch_estimates_ext[aarx]; c16_t *rxdataF = rxdataF_ext[aarx]; c16_t *rxdataF_c = rxdataF_comp[aarx]; @@ -357,10 +355,10 @@ static void nr_pdcch_channel_compensation(uint32_t coreset_nbr_rb, } } -static void nr_pdcch_detection_mrc(int n_rb, c16_t rxdataF_comp[][n_rb * 9]) +static void nr_pdcch_detection_mrc(int n_rb, c16_t rxdataF_comp[][n_rb * RE_PER_RB_OUT_DMRS]) { LOG_D(NR_PHY_DCI, "we enter nr_pdcch_detection_mrc (hard coded 2 antennas)\n"); - const int sz = n_rb * 9; + const int sz = n_rb * RE_PER_RB_OUT_DMRS; simde__m128i *rxdataF_comp128_0 = (simde__m128i *)rxdataF_comp[0]; simde__m128i *rxdataF_comp128_1 = (simde__m128i *)rxdataF_comp[1]; @@ -387,9 +385,9 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, const int antRx = frame_parms->nb_antennas_rx; // Pointers to extracted PDCCH symbols in frequency-domain. - int32_t rx_size = ((4 * frame_parms->N_RB_DL * 12 + 31) >> 5) << 5; + int32_t rx_size = ((4 * frame_parms->N_RB_DL * RE_PER_RB + 31) >> 5) << 5; // Pointer to llrs, 4-bit resolution. - int32_t llr_size = 4 * n_rb * 9; + int32_t llr_size = 4 * n_rb * RE_PER_RB_OUT_DMRS; c16_t llr[llr_size]; memset(llr, 0, sizeof(llr)); @@ -401,8 +399,8 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, for (int s=rel15->coreset.StartSymbolIndex; s<(rel15->coreset.StartSymbolIndex+rel15->coreset.duration); s++) { LOG_D(NR_PHY_DCI, "in nr_pdcch_extract_rbs_single(rxdataF -> rxdataF_ext || dl_ch_estimates -> dl_ch_estimates_ext)\n"); - __attribute__((aligned(32))) c16_t rxdataF_ext[antRx][n_rb * 9]; - __attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[antRx][n_rb * 9]; + __attribute__((aligned(32))) c16_t rxdataF_ext[antRx][n_rb * RE_PER_RB_OUT_DMRS]; + __attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[antRx][n_rb * RE_PER_RB_OUT_DMRS]; nr_pdcch_extract_rbs_single(ue->frame_parms.samples_per_slot_wCP, n_rb, rxdataF, @@ -429,7 +427,7 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, LOG_D(NR_PHY_DCI, "we enter nr_pdcch_channel_compensation(log2_maxh=%d)\n", log2_maxh); LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_compensation(rxdataF_ext x dl_ch_estimates_ext -> rxdataF_comp)\n"); // compute LLRs for ofdm symbol 0 only - __attribute__((aligned(32))) c16_t rxdataF_comp[antRx][n_rb * 12]; + __attribute__((aligned(32))) c16_t rxdataF_comp[antRx][n_rb * RE_PER_RB]; nr_pdcch_channel_compensation(n_rb, rxdataF_ext, pdcch_dl_ch_estimates_ext, @@ -437,10 +435,10 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, antRx, log2_maxh); // log2_maxh+I0_shift - UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), antRx, n_rb * 12, 0); + UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), antRx, n_rb * RE_PER_RB, 0); if (antRx > 1) - nr_pdcch_detection_mrc(n_rb * 12, rxdataF_comp); + nr_pdcch_detection_mrc(n_rb * RE_PER_RB, rxdataF_comp); LOG_D(NR_PHY_DCI, "we enter nr_pdcch_llr(for symbol %d), pdcch_vars[eNB_id]->rxdataF_comp ---> pdcch_vars[eNB_id]->llr \n", s); LOG_D(NR_PHY_DCI, "in nr_pdcch_llr(rxdataF_comp -> llr)\n"); @@ -572,6 +570,6 @@ void nr_dci_decoding_procedure(PHY_VARS_NR_UE *ue, rel15->dci_format_options[k]); } } - e_rx_cand_idx += 9 * L * 6; // e_rx index for next candidate (L CCEs, 6 REGs per CCE and 9 REs per REG ) + e_rx_cand_idx += RE_PER_RB_OUT_DMRS * L * 6; // e_rx index for next candidate (L CCEs, 6 REGs per CCE and 9 REs per REG ) } } From b41c9e2e851e17683dfdd8b1879c017088049b87 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 22 Jul 2025 19:35:51 +0200 Subject: [PATCH 12/86] move static some functions that are used only in one file --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 2 +- openair2/LAYER2/NR_MAC_UE/mac_proto.h | 49 +--------- .../NR_MAC_UE/nr_ue_dci_configuration.c | 10 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 98 +++++++++++++------ openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 10 +- 5 files changed, 80 insertions(+), 89 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 297be42261..dc01be2f98 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -548,7 +548,7 @@ static void config_common_ue(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t } } -void release_common_ss_cset(NR_BWP_PDCCH_t *pdcch) +static void release_common_ss_cset(NR_BWP_PDCCH_t *pdcch) { pdcch->otherSI_SS_id = -1; pdcch->ra_SS_id = -1; diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index aacbbdcab2..29c1d7c51b 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -59,8 +59,6 @@ void nr_ue_mac_default_configs(NR_UE_MAC_INST_t *mac); void nr_ue_decode_mib(NR_UE_MAC_INST_t *mac, int cc_id); -void release_common_ss_cset(NR_BWP_PDCCH_t *pdcch); - /**\brief decode SIB1 and other SIs pdus in NR_UE, from if_module dl_ind \param mac pointer to MAC instance \param cc_id component carrier id @@ -117,12 +115,6 @@ void nr_ue_dl_scheduler(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info csi_payload_t nr_ue_aperiodic_csi_reporting(NR_UE_MAC_INST_t *mac, dci_field_t csi_request, int tda, long *K2); -/*! \fn int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id); - \brief This function schedules a positive or negative SR for schedulingRequestID sr_id - depending on the presence of any active SR and the prohibit timer. - If the max number of retransmissions is reached, it triggers a new RA */ -int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id); - nr_dci_format_t nr_ue_process_dci_indication_pdu(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_dci_indication_pdu_t *dci); int8_t nr_ue_process_csirs_measurements(NR_UE_MAC_INST_t *mac, @@ -138,30 +130,11 @@ bool trigger_periodic_scheduling_request(NR_UE_MAC_INST_t *mac, int nr_get_csi_measurements(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, PUCCH_sched_t *pucch); -csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac, - struct NR_CSI_ReportConfig *csi_reportconfig, - NR_CSI_ResourceConfigId_t csi_ResourceConfigId, - NR_CSI_MeasConfig_t *csi_MeasConfig); - -csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac, - struct NR_CSI_ReportConfig *csi_reportconfig, - NR_CSI_ResourceConfigId_t csi_ResourceConfigId, - NR_CSI_MeasConfig_t *csi_MeasConfig, - CSI_mapping_t mapping_type); - -csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac, - struct NR_CSI_ReportConfig *csi_reportconfig, - NR_CSI_ResourceConfigId_t csi_ResourceConfigId, - const NR_CSI_MeasConfig_t *csi_MeasConfig); - csi_payload_t nr_get_csi_payload(NR_UE_MAC_INST_t *mac, int csi_report_id, CSI_mapping_t mapping_type, NR_CSI_MeasConfig_t *csi_MeasConfig); -uint8_t get_rsrp_index(int rsrp); -uint8_t get_rsrp_diff_index(int best_rsrp,int current_rsrp); - /* \brief Get payload (MAC PDU) from UE PHY @param dl_info pointer to dl indication @param ul_time_alignment pointer to timing advance parameters @@ -170,8 +143,6 @@ uint8_t get_rsrp_diff_index(int best_rsrp,int current_rsrp); */ void nr_ue_send_sdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id); -void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac,nr_downlink_indication_t *dl_info, int pdu_id); - typedef struct { union { NR_BSR_SHORT s; @@ -187,28 +158,12 @@ int nr_write_ce_ulsch_pdu(uint8_t *mac_ce, const type_bsr_t *bsr, uint8_t *mac_ce_end); -void config_dci_pdu(NR_UE_MAC_INST_t *mac, - fapi_nr_dl_config_request_t *dl_config, - const int rnti_type, - const int slot, - const NR_SearchSpace_t *ss); - void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl_config, const frame_t frame, const int slot); void set_precoding_information_parameters(nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, int n_antenna_port, long transformPrecoder, int precoding_information, NR_PUSCH_Config_t *pusch_Config); -void set_harq_status(NR_UE_MAC_INST_t *mac, - uint8_t pucch_id, - uint8_t harq_id, - int8_t delta_pucch, - uint16_t data_toul_fb, - uint8_t dai, - int n_CCE, - int N_CCE, - frame_t frame, - int slot); bool get_downlink_ack(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, PUCCH_sched_t *pucch); initial_pucch_resource_t get_initial_pucch_resource(const int idx); @@ -285,9 +240,7 @@ and fills the PRACH PDU per each FD occasion. @param slotP Slot index @returns void */ -void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP); -void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot); -void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot); + void configure_csi_resource_mapping(fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu, NR_CSI_RS_ResourceMapping_t *resourceMapping, uint32_t bwp_size, diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 7a2e9c6ed9..388b28f846 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -104,11 +104,11 @@ NR_ControlResourceSet_t *ue_get_coreset(const NR_BWP_PDCCH_t *config, const int return coreset; } -void config_dci_pdu(NR_UE_MAC_INST_t *mac, - fapi_nr_dl_config_request_t *dl_config, - const int rnti_type, - const int slot, - const NR_SearchSpace_t *ss) +static void config_dci_pdu(NR_UE_MAC_INST_t *mac, + fapi_nr_dl_config_request_t *dl_config, + const int rnti_type, + const int slot, + const NR_SearchSpace_t *ss) { const NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP; const NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 8a892557b2..e623900f29 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -113,6 +113,43 @@ random-access procedure */ static void nr_ue_process_rar(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id); +/*! \fn int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id); + \brief This function schedules a positive or negative SR for schedulingRequestID sr_id + depending on the presence of any active SR and the prohibit timer. + If the max number of retransmissions is reached, it triggers a new RA */ +static int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id); + +static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac, + struct NR_CSI_ReportConfig *csi_reportconfig, + NR_CSI_ResourceConfigId_t csi_ResourceConfigId, + NR_CSI_MeasConfig_t *csi_MeasConfig); + +static csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac, + struct NR_CSI_ReportConfig *csi_reportconfig, + NR_CSI_ResourceConfigId_t csi_ResourceConfigId, + NR_CSI_MeasConfig_t *csi_MeasConfig, + CSI_mapping_t mapping_type); +static csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac, + struct NR_CSI_ReportConfig *csi_reportconfig, + NR_CSI_ResourceConfigId_t csi_ResourceConfigId, + const NR_CSI_MeasConfig_t *csi_MeasConfig); +static uint8_t get_rsrp_index(int rsrp); + +static uint8_t get_rsrp_diff_index(int best_rsrp, int current_rsrp); + +static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id); + +static void set_harq_status(NR_UE_MAC_INST_t *mac, + uint8_t pucch_id, + uint8_t harq_id, + int8_t delta_pucch, + uint16_t data_toul_fb, + uint8_t dai, + int n_CCE, + int N_CCE, + frame_t frame, + int slot); + int get_pucch0_mcs(const int O_ACK, const int O_SR, const int ack_payload, const int sr_payload) { int mcs = 0; @@ -1442,16 +1479,16 @@ int8_t nr_ue_process_csirs_measurements(NR_UE_MAC_INST_t *mac, return 0; } -void set_harq_status(NR_UE_MAC_INST_t *mac, - uint8_t pucch_id, - uint8_t harq_id, - int8_t delta_pucch, - uint16_t data_toul_fb, - uint8_t dai, - int n_CCE, - int N_CCE, - frame_t frame, - int slot) +static void set_harq_status(NR_UE_MAC_INST_t *mac, + uint8_t pucch_id, + uint8_t harq_id, + int8_t delta_pucch, + uint16_t data_toul_fb, + uint8_t dai, + int n_CCE, + int N_CCE, + frame_t frame, + int slot) { NR_UE_DL_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[harq_id]; current_harq->active = true; @@ -2527,7 +2564,7 @@ bool trigger_periodic_scheduling_request(NR_UE_MAC_INST_t *mac, PUCCH_sched_t *p return sr_count > 0; } -int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id) +static int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id) { // no UL-SCH resources available for this tti && UE has a valid PUCCH resources for SR configuration for this tti NR_UE_SCHEDULING_INFO *si = &mac->scheduling_info; @@ -2811,11 +2848,10 @@ csi_payload_t nr_get_csi_payload(NR_UE_MAC_INST_t *mac, return csi; } - -csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac, - struct NR_CSI_ReportConfig *csi_reportconfig, - NR_CSI_ResourceConfigId_t csi_ResourceConfigId, - NR_CSI_MeasConfig_t *csi_MeasConfig) +static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac, + struct NR_CSI_ReportConfig *csi_reportconfig, + NR_CSI_ResourceConfigId_t csi_ResourceConfigId, + NR_CSI_MeasConfig_t *csi_MeasConfig) { int nb_ssb = 0; // nb of ssb in the resource int nb_meas = 0; // nb of ssb to report measurements on @@ -2892,11 +2928,11 @@ csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac, return csi; } -csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac, - struct NR_CSI_ReportConfig *csi_reportconfig, - NR_CSI_ResourceConfigId_t csi_ResourceConfigId, - NR_CSI_MeasConfig_t *csi_MeasConfig, - CSI_mapping_t mapping_type) +static csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac, + struct NR_CSI_ReportConfig *csi_reportconfig, + NR_CSI_ResourceConfigId_t csi_ResourceConfigId, + NR_CSI_MeasConfig_t *csi_MeasConfig, + CSI_mapping_t mapping_type) { int p1_bits = 0; int p2_bits = 0; @@ -2980,10 +3016,10 @@ csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac, return csi; } -csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac, - struct NR_CSI_ReportConfig *csi_reportconfig, - NR_CSI_ResourceConfigId_t csi_ResourceConfigId, - const NR_CSI_MeasConfig_t *csi_MeasConfig) +static csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac, + struct NR_CSI_ReportConfig *csi_reportconfig, + NR_CSI_ResourceConfigId_t csi_ResourceConfigId, + const NR_CSI_MeasConfig_t *csi_MeasConfig) { int n_bits = 0; uint64_t temp_payload = 0; @@ -3047,8 +3083,8 @@ csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac, // returns index from RSRP // according to Table 10.1.6.1-1 in 38.133 -uint8_t get_rsrp_index(int rsrp) { - +static uint8_t get_rsrp_index(int rsrp) +{ int index = rsrp + 157; if (rsrp>-44) index = 113; @@ -3058,17 +3094,15 @@ uint8_t get_rsrp_index(int rsrp) { return index; } - // returns index from differential RSRP // according to Table 10.1.6.1-2 in 38.133 -uint8_t get_rsrp_diff_index(int best_rsrp,int current_rsrp) { - +static uint8_t get_rsrp_diff_index(int best_rsrp, int current_rsrp) +{ int diff = best_rsrp-current_rsrp; if (diff>30) return 15; else return (diff>>1); - } void nr_ue_send_sdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id) @@ -3697,7 +3731,7 @@ static int nr_ue_validate_successrar(uint8_t *pduP, // F: lenght of L is 0:8 or 1:16 bits wide // R: Reserved bit, set to zero. //////////////////////////////// -void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id) +static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id) { frame_t frameP = dl_info->frame; int slot = dl_info->slot; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 6c257bcacf..3e90445aa3 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -78,6 +78,10 @@ static uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac, int P_MAX, bool *BSRsent); +static void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP); +static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot); +static void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot); + static void clear_ul_config_request(NR_UE_MAC_INST_t *mac) { int slots = mac->frame_structure.numb_slots_frame; @@ -1709,7 +1713,7 @@ static bool schedule_uci_on_pusch(NR_UE_MAC_INST_t *mac, return mux_done; } -void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP) +static void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP) { PUCCH_sched_t pucch[3] = {0}; // TODO the size might change in the future in case of multiple SR or multiple CSI in a slot @@ -1784,7 +1788,7 @@ void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP) } } -void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot) +static void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot) { if (!mac->sc_info.csi_MeasConfig) return; @@ -2025,7 +2029,7 @@ void configure_csi_resource_mapping(fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_c } } -void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot) +static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot) { if (!mac->sc_info.csi_MeasConfig) return; From 343b983aa99d8526bbdc7a1e42822f5eeccd36c9 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 23 Jul 2025 08:23:26 +0200 Subject: [PATCH 13/86] fix SIMD tail length in nr_pdcch_detection_mrc() and replace >> operator by / --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 5f41880251..9449ee3084 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -185,7 +185,6 @@ static void nr_pdcch_demapping_deinterleaving(c16_t *llr, } static void nr_pdcch_llr(NR_DL_FRAME_PARMS *frame_parms, - int32_t rx_size, uint32_t coreset_nbr_rb, c16_t rxdataF_comp[coreset_nbr_rb * RE_PER_RB], c16_t *pdcch_llr, @@ -288,13 +287,13 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, int c_rb_by6 = c_rb / 6; // skip zeros in frequency domain bitmap - while ((coreset_freq_dom[c_rb_by6 >> 3] & (1 << (7 - (c_rb_by6 & 7)))) == 0) { + while ((coreset_freq_dom[c_rb_by6 / 8] & (1 << (7 - (c_rb_by6 & 7)))) == 0) { c_rb += 6; c_rb_by6 = c_rb / 6; } c16_t *rxF = NULL; if ((frame_parms->N_RB_DL & 1) == 0) { - if ((c_rb + n_BWP_start) < (frame_parms->N_RB_DL >> 1)) + if ((c_rb + n_BWP_start) < frame_parms->N_RB_DL / 2) // if RB to be treated is lower than middle system bandwidth then rxdataF pointed // at (offset + c_br + symbol * ofdm_symbol_size): even case rxF = rxFbase + (frame_parms->first_carrier_offset + RE_PER_RB * c_rb) + n_BWP_start * RE_PER_RB; @@ -302,18 +301,18 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, // number of RBs is even and c_rb is higher than half system bandwidth (we don't skip DC) // if these conditions are true the pointer has to be situated at the 1st part of the rxdataF // we point at the 1st part of the rxdataF in symbol - rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1)); + rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2); } else { - if ((c_rb + n_BWP_start) < (frame_parms->N_RB_DL >> 1)) + if ((c_rb + n_BWP_start) < frame_parms->N_RB_DL / 2) // if RB to be treated is lower than middle system bandwidth then rxdataF pointed // at (offset + c_br + symbol * ofdm_symbol_size): odd case rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); - else if ((c_rb + n_BWP_start) > (frame_parms->N_RB_DL >> 1)) + else if ((c_rb + n_BWP_start) > frame_parms->N_RB_DL / 2) // number of RBs is odd and c_rb is higher than half system bandwidth + 1 // if these conditions are true the pointer has to be situated at the 1st part of // the rxdataF just after the first IQ symbols of the RB containing DC // we point at the 1st part of the rxdataF in symbol - rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - (frame_parms->N_RB_DL >> 1)) - 6; + rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2) - 6; else rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); } @@ -360,14 +359,17 @@ static void nr_pdcch_detection_mrc(int n_rb, c16_t rxdataF_comp[][n_rb * RE_PER_ LOG_D(NR_PHY_DCI, "we enter nr_pdcch_detection_mrc (hard coded 2 antennas)\n"); const int sz = n_rb * RE_PER_RB_OUT_DMRS; - simde__m128i *rxdataF_comp128_0 = (simde__m128i *)rxdataF_comp[0]; - simde__m128i *rxdataF_comp128_1 = (simde__m128i *)rxdataF_comp[1]; + c16_t *rx0 = rxdataF_comp[0]; + c16_t *rx1 = rxdataF_comp[1]; // MRC on each re of rb - for (int i = 0; i < sz >> 2; i++) { - rxdataF_comp128_0[i] = - simde_mm_adds_epi16(simde_mm_srai_epi16(rxdataF_comp128_0[i], 1), simde_mm_srai_epi16(rxdataF_comp128_1[i], 1)); + int i = 0; + for (; i < (sz & ~3); i += 4) { + *(simde__m128i *)(rx0 + i) = + simde_mm_adds_epi16(simde_mm_srai_epi16(*(simde__m128i *)(rx0 + i), 1), simde_mm_srai_epi16(*(simde__m128i *)(rx1 + i), 1)); } + for (; i < sz; i++) + rx0[i] = (c16_t){(rx0[i].r + rx1[i].r) / 2, (rx0[i].i + rx1[i].i) / 2}; } void nr_rx_pdcch(PHY_VARS_NR_UE *ue, @@ -384,8 +386,6 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, get_coreset_rballoc(rel15->coreset.frequency_domain_resource,&n_rb,&rb_offset); const int antRx = frame_parms->nb_antennas_rx; - // Pointers to extracted PDCCH symbols in frequency-domain. - int32_t rx_size = ((4 * frame_parms->N_RB_DL * RE_PER_RB + 31) >> 5) << 5; // Pointer to llrs, 4-bit resolution. int32_t llr_size = 4 * n_rb * RE_PER_RB_OUT_DMRS; c16_t llr[llr_size]; @@ -442,7 +442,7 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, LOG_D(NR_PHY_DCI, "we enter nr_pdcch_llr(for symbol %d), pdcch_vars[eNB_id]->rxdataF_comp ---> pdcch_vars[eNB_id]->llr \n", s); LOG_D(NR_PHY_DCI, "in nr_pdcch_llr(rxdataF_comp -> llr)\n"); - nr_pdcch_llr(frame_parms, rx_size, n_rb, rxdataF_comp[0], llr, s); + nr_pdcch_llr(frame_parms, n_rb, rxdataF_comp[0], llr, s); UEscopeCopy(ue, pdcchLlr, llr, sizeof(c16_t), 1, llr_size, 0); } From f835705dd143286abbbb2178cba3d87ed88da083 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Fri, 25 Jul 2025 14:59:05 +0200 Subject: [PATCH 14/86] fix array sizes and aligment issues in 5G UE dci decoder --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 112 ++++++++------------------ 1 file changed, 35 insertions(+), 77 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 9449ee3084..bf2d91bdc8 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -184,26 +184,9 @@ static void nr_pdcch_demapping_deinterleaving(c16_t *llr, } } -static void nr_pdcch_llr(NR_DL_FRAME_PARMS *frame_parms, - uint32_t coreset_nbr_rb, - c16_t rxdataF_comp[coreset_nbr_rb * RE_PER_RB], - c16_t *pdcch_llr, - uint8_t symbol) +static void nr_pdcch_llr(uint32_t sz, c16_t *rxF, c16_t *llr) { - c16_t *rxF = rxdataF_comp; - c16_t *llr = pdcch_llr + symbol * coreset_nbr_rb * RE_PER_RB_OUT_DMRS; - - if (!llr) { - LOG_E(NR_PHY_DCI, "pdcch_qpsk_llr: llr is null, symbol %d\n", symbol); - return; - } - - LOG_DDD("llr logs: pdcch qpsk llr for symbol %d (pos %d), llr offset %ld\n", - symbol, - symbol * frame_parms->N_RB_DL * RE_PER_RB, - llr - pdcch_llr); - - for (int i = 0; i < coreset_nbr_rb * RE_PER_RB_OUT_DMRS; i++) { + for (int i = 0; i < sz; i++) { // We clip the signal c16_t res; res.r = min(rxF->r, 31); @@ -217,16 +200,14 @@ static void nr_pdcch_llr(NR_DL_FRAME_PARMS *frame_parms, } //compute average channel_level on each (TX,RX) antenna pair -static int32_t nr_pdcch_channel_level(int nb_rb, - c16_t dl_ch_estimates_ext[][nb_rb * RE_PER_RB_OUT_DMRS], - int nb_antennas_rx, - int symbol) +static int32_t nr_pdcch_channel_level(int nb_re, int arraySz, c16_t dl_ch_estimates_ext[][arraySz], int nb_antennas_rx) { int32_t avgs = 0; for (int aarx = 0; aarx < nb_antennas_rx; aarx++) { simde__m128i *dl_ch128 = (simde__m128i *)&dl_ch_estimates_ext[aarx]; - //compute average level - int32_t tmp = simde_mm_average(dl_ch128, nb_rb * RE_PER_RB_OUT_DMRS, 0, nb_rb * RE_PER_RB_OUT_DMRS); + // compute average level + // if nb_re is not %4, some samples are missed inside simde_mm_average, so let's discard it + int32_t tmp = simde_mm_average(dl_ch128, nb_re & ~3, 0, nb_re & ~3); avgs = cmax(avgs, tmp); } return avgs; @@ -238,8 +219,9 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, c16_t rxdataF[][rxdataF_sz], int32_t est_size, c16_t dl_ch_estimates[][est_size], - c16_t rxdataF_ext[][coreset_nbr_rb * RE_PER_RB_OUT_DMRS], - c16_t dl_ch_estimates_ext[][coreset_nbr_rb * RE_PER_RB_OUT_DMRS], + int arraySz, + c16_t rxdataF_ext[][arraySz], + c16_t dl_ch_estimates_ext[][arraySz], int symbol, NR_DL_FRAME_PARMS *frame_parms, uint8_t *coreset_freq_dom, @@ -296,25 +278,23 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, if ((c_rb + n_BWP_start) < frame_parms->N_RB_DL / 2) // if RB to be treated is lower than middle system bandwidth then rxdataF pointed // at (offset + c_br + symbol * ofdm_symbol_size): even case - rxF = rxFbase + (frame_parms->first_carrier_offset + RE_PER_RB * c_rb) + n_BWP_start * RE_PER_RB; + rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); else // number of RBs is even and c_rb is higher than half system bandwidth (we don't skip DC) // if these conditions are true the pointer has to be situated at the 1st part of the rxdataF // we point at the 1st part of the rxdataF in symbol rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2); } else { - if ((c_rb + n_BWP_start) < frame_parms->N_RB_DL / 2) + if ((c_rb + n_BWP_start) <= frame_parms->N_RB_DL / 2) // if RB to be treated is lower than middle system bandwidth then rxdataF pointed // at (offset + c_br + symbol * ofdm_symbol_size): odd case rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); - else if ((c_rb + n_BWP_start) > frame_parms->N_RB_DL / 2) - // number of RBs is odd and c_rb is higher than half system bandwidth + 1 + else + // number of RBs is odd and c_rb is higher than half system bandwidth + 1 // if these conditions are true the pointer has to be situated at the 1st part of // the rxdataF just after the first IQ symbols of the RB containing DC // we point at the 1st part of the rxdataF in symbol rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2) - 6; - else - rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); } const int valid_re[RE_PER_RB_OUT_DMRS] = {0, 2, 3, 4, 6, 7, 8, 10, 11}; for (int i = 0; i < sizeofArray(valid_re); i++) { @@ -326,50 +306,31 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, } } -static void nr_pdcch_channel_compensation(uint32_t coreset_nbr_rb, - c16_t rxdataF_ext[][coreset_nbr_rb * RE_PER_RB_OUT_DMRS], - c16_t dl_ch_estimates_ext[][coreset_nbr_rb * RE_PER_RB_OUT_DMRS], - c16_t rxdataF_comp[][coreset_nbr_rb * RE_PER_RB], +static void nr_pdcch_channel_compensation(int arraySz, + c16_t rxdataF_ext[][arraySz], + c16_t dl_ch_estimates_ext[][arraySz], + c16_t rxdataF_comp[][arraySz], int antRx, uint8_t output_shift) { for (int aarx = 0; aarx < antRx; aarx++) { - const int sz = coreset_nbr_rb * RE_PER_RB_OUT_DMRS; - c16_t *dl_ch = dl_ch_estimates_ext[aarx]; - c16_t *rxdataF = rxdataF_ext[aarx]; - c16_t *rxdataF_c = rxdataF_comp[aarx]; - // multiply by conjugated channel - mult_cpx_conj_vector(dl_ch, rxdataF, rxdataF_c, sz, output_shift); - for (int rb = 0; rb < sz; rb++) - LOG_DDD("rxdataF[%d]=(%d,%d) X dlch[%d]=(%d,%d) rxdataF_comp[%d]=(%d,%d)\n", - rb, - rxdataF[rb].r, - rxdataF[rb].i, - rb, - dl_ch[rb].r, - dl_ch[rb].i, - rb, - rxdataF_c[rb].r, - rxdataF_c[rb].i); + // multiply by conjugated channel, this function require size in _m128i, else it doesn't process all samples + mult_cpx_conj_vector(dl_ch_estimates_ext[aarx], rxdataF_ext[aarx], rxdataF_comp[aarx], arraySz, output_shift); } } -static void nr_pdcch_detection_mrc(int n_rb, c16_t rxdataF_comp[][n_rb * RE_PER_RB_OUT_DMRS]) +static void nr_pdcch_detection_mrc(int sz, c16_t rxdataF_comp[][sz]) { LOG_D(NR_PHY_DCI, "we enter nr_pdcch_detection_mrc (hard coded 2 antennas)\n"); - const int sz = n_rb * RE_PER_RB_OUT_DMRS; - c16_t *rx0 = rxdataF_comp[0]; c16_t *rx1 = rxdataF_comp[1]; // MRC on each re of rb - int i = 0; - for (; i < (sz & ~3); i += 4) { + // input always aligned and accepting tail padding to process all actual samples + for (int i = 0; i < sz; i += 4) { *(simde__m128i *)(rx0 + i) = simde_mm_adds_epi16(simde_mm_srai_epi16(*(simde__m128i *)(rx0 + i), 1), simde_mm_srai_epi16(*(simde__m128i *)(rx1 + i), 1)); } - for (; i < sz; i++) - rx0[i] = (c16_t){(rx0[i].r + rx1[i].r) / 2, (rx0[i].i + rx1[i].i) / 2}; } void nr_rx_pdcch(PHY_VARS_NR_UE *ue, @@ -387,25 +348,25 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, const int antRx = frame_parms->nb_antennas_rx; // Pointer to llrs, 4-bit resolution. - int32_t llr_size = 4 * n_rb * RE_PER_RB_OUT_DMRS; + int32_t llr_size = (rel15->coreset.duration-rel15->coreset.StartSymbolIndex) * n_rb * RE_PER_RB_OUT_DMRS; c16_t llr[llr_size]; - memset(llr, 0, sizeof(llr)); LOG_D(NR_PHY_DCI, "pdcch coreset: freq %x, n_rb %d, rb_offset %d\n", rel15->coreset.frequency_domain_resource[0], n_rb, rb_offset); - for (int s=rel15->coreset.StartSymbolIndex; s<(rel15->coreset.StartSymbolIndex+rel15->coreset.duration); s++) { + for (int s = rel15->coreset.StartSymbolIndex; s < rel15->coreset.StartSymbolIndex + rel15->coreset.duration; s++) { LOG_D(NR_PHY_DCI, "in nr_pdcch_extract_rbs_single(rxdataF -> rxdataF_ext || dl_ch_estimates -> dl_ch_estimates_ext)\n"); - - __attribute__((aligned(32))) c16_t rxdataF_ext[antRx][n_rb * RE_PER_RB_OUT_DMRS]; - __attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[antRx][n_rb * RE_PER_RB_OUT_DMRS]; + const int arraySz = ceil_mod(n_rb * RE_PER_RB_OUT_DMRS, 32); + __attribute__((aligned(32))) c16_t rxdataF_ext[antRx][arraySz]; + __attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[antRx][arraySz]; nr_pdcch_extract_rbs_single(ue->frame_parms.samples_per_slot_wCP, n_rb, rxdataF, pdcch_est_size, pdcch_dl_ch_estimates, + arraySz, rxdataF_ext, pdcch_dl_ch_estimates_ext, s, @@ -415,7 +376,7 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_level(dl_ch_estimates_ext -> dl_ch_estimates_ext)\n"); // compute channel level based on ofdm symbol 0 - int32_t avgs = nr_pdcch_channel_level(n_rb, pdcch_dl_ch_estimates_ext, antRx, s); + int32_t avgs = nr_pdcch_channel_level(n_rb, arraySz, pdcch_dl_ch_estimates_ext, antRx); uint8_t log2_maxh = (log2_approx(avgs) / 2) + 5; //+antRx; #ifdef UE_DEBUG_TRACE @@ -427,25 +388,22 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, LOG_D(NR_PHY_DCI, "we enter nr_pdcch_channel_compensation(log2_maxh=%d)\n", log2_maxh); LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_compensation(rxdataF_ext x dl_ch_estimates_ext -> rxdataF_comp)\n"); // compute LLRs for ofdm symbol 0 only - __attribute__((aligned(32))) c16_t rxdataF_comp[antRx][n_rb * RE_PER_RB]; - nr_pdcch_channel_compensation(n_rb, + __attribute__((aligned(32))) c16_t rxdataF_comp[antRx][arraySz]; + nr_pdcch_channel_compensation(arraySz, rxdataF_ext, pdcch_dl_ch_estimates_ext, rxdataF_comp, antRx, log2_maxh); // log2_maxh+I0_shift - UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), antRx, n_rb * RE_PER_RB, 0); + UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), antRx, n_rb * RE_PER_RB_OUT_DMRS, 0); if (antRx > 1) - nr_pdcch_detection_mrc(n_rb * RE_PER_RB, rxdataF_comp); + nr_pdcch_detection_mrc(arraySz, rxdataF_comp); - LOG_D(NR_PHY_DCI, "we enter nr_pdcch_llr(for symbol %d), pdcch_vars[eNB_id]->rxdataF_comp ---> pdcch_vars[eNB_id]->llr \n", s); - LOG_D(NR_PHY_DCI, "in nr_pdcch_llr(rxdataF_comp -> llr)\n"); - nr_pdcch_llr(frame_parms, n_rb, rxdataF_comp[0], llr, s); - - UEscopeCopy(ue, pdcchLlr, llr, sizeof(c16_t), 1, llr_size, 0); + nr_pdcch_llr(n_rb * RE_PER_RB_OUT_DMRS, rxdataF_comp[0], llr + s * n_rb * RE_PER_RB_OUT_DMRS); } + UEscopeCopy(ue, pdcchLlr, llr, sizeof(c16_t), 1, llr_size, 0); nr_pdcch_demapping_deinterleaving(llr, pdcch_e_rx, From 9a7d80ef7daf63190b3b61be634a221e79996723 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 30 Jul 2025 08:57:02 +0200 Subject: [PATCH 15/86] CMake module path to beginning Move the path to the beginning, so that we can use it early. Specifically, it was after the SCTP module, and the next commit will use the Findsctp.cmake module to find SCTP. Use the cmake-proper variable. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 99c537dc22..4c78030c30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,8 @@ if(NOT DEFINED ENV{CPM_SOURCE_CACHE}) endif() include("cmake_targets/CPM.cmake") +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}") + ############################## ### CCache: reduce compilation time ############################## @@ -1710,7 +1712,6 @@ include_directories("${NFAPI_DIR}/pnf_sim/inc") add_library(oai_iqplayer MODULE ${OPENAIR_DIR}/radio/iqplayer/iqplayer_lib.c ) -set(CMAKE_MODULE_PATH "${OPENAIR_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}") ################################# # add executables for operation From 83bb8017cfd8fc038230486cb7798d3ea91894ba Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 30 Jul 2025 09:14:01 +0200 Subject: [PATCH 16/86] Correct Findsctp.cmake - Provide some documentation - Make cmake keywards and sctp lib name lowercase - Use standard form of find_package_handle_standard_args() - Make an imported target sctp::sctp that will fail if being used in cmake unless defined, which will avoid that "sctp" appears in the cmake code but is silently discarded See also: https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#a-sample-find-module --- cmake_targets/tools/MODULES/FindSCTP.cmake | 46 ---------------- cmake_targets/tools/MODULES/Findsctp.cmake | 64 ++++++++++++++++++++++ 2 files changed, 64 insertions(+), 46 deletions(-) delete mode 100755 cmake_targets/tools/MODULES/FindSCTP.cmake create mode 100644 cmake_targets/tools/MODULES/Findsctp.cmake diff --git a/cmake_targets/tools/MODULES/FindSCTP.cmake b/cmake_targets/tools/MODULES/FindSCTP.cmake deleted file mode 100755 index de8e878a99..0000000000 --- a/cmake_targets/tools/MODULES/FindSCTP.cmake +++ /dev/null @@ -1,46 +0,0 @@ -# - Try to find SCTP library and headers -# Once done, this will define -# -# SCTP_FOUND - system has SCTP -# SCTP_INCLUDE_DIR - the SCTP include directories -# SCTP_LIBRARIES - link these to use SCTP - -if (SCTP_INCLUDE_DIR AND SCTP_LIBRARIES) - set(SCTP_FIND_QUIETLY TRUE) -endif (SCTP_INCLUDE_DIR AND SCTP_LIBRARIES) - -# Include dir -find_path(SCTP_INCLUDE_DIR - NAMES netinet/sctp.h -) - -# Library -find_library(SCTP_LIBRARY - NAMES sctp -) - -# Set the include dir variables and the libraries and let libfind_process do the rest. -# NOTE: Singular variables for this library, plural for libraries this this lib depends on. -#set(SCTP_PROCESS_INCLUDES SCTP_INCLUDE_DIR) -#set(SCTP_PROCESS_LIBS SCTP_LIBRARY) -#libfind_process(SCTP) - - -# handle the QUIETLY and REQUIRED arguments and set SCTP_FOUND to TRUE if -# all listed variables are TRUE -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(SCTP DEFAULT_MSG SCTP_LIBRARY SCTP_INCLUDE_DIR) - -# If we successfully found the sctp library then add the library to the -# SCTP_LIBRARIES cmake variable otherwise set SCTP_LIBRARIES to nothing. -IF(SCTP_FOUND) - SET( SCTP_LIBRARIES ${SCTP_LIBRARY} ) -ELSE(SCTP_FOUND) - SET( SCTP_LIBRARIES ) -ENDIF(SCTP_FOUND) - - -# Lastly make it so that the SCTP_LIBRARY and SCTP_INCLUDE_DIR variables -# only show up under the advanced options in the gui cmake applications. -MARK_AS_ADVANCED( SCTP_LIBRARY SCTP_INCLUDE_DIR ) - diff --git a/cmake_targets/tools/MODULES/Findsctp.cmake b/cmake_targets/tools/MODULES/Findsctp.cmake new file mode 100644 index 0000000000..44e3ee6a2b --- /dev/null +++ b/cmake_targets/tools/MODULES/Findsctp.cmake @@ -0,0 +1,64 @@ +# - Try to find SCTP library and headers +# +# Imported Targets +# ^^^^^^^^^^^^^^^^ +# +# This module provides the following imported targets, if found: +# +# ``sctp::sctp`` +# The SCTP library +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# this will define +# +# sctp_FOUND - system has SCTP +# sctp_INCLUDE_DIRS - the SCTP include directories +# sctp_LIBRARIES - link these to use SCTP + +# Include dir +find_path(sctp_INCLUDE_DIR + NAMES netinet/sctp.h +) + +# Library +find_library(sctp_LIBRARY + NAMES sctp +) + +# Set the include dir variables and the libraries and let libfind_process do the rest. +# NOTE: Singular variables for this library, plural for libraries this this lib depends on. +#set(sctp_PROCESS_INCLUDES sctp_INCLUDE_DIR) +#set(sctp_PROCESS_LIBS sctp_LIBRARY) +#libfind_process(sctp) + + +# handle the QUIETLY and REQUIRED arguments and set sctp_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(sctp + FOUND_VAR sctp_FOUND + REQUIRED_VARS + sctp_LIBRARY + sctp_INCLUDE_DIR +) + +# If we successfully found the sctp library then add the library to the +# sctp_LIBRARIES cmake variable otherwise set sctp_LIBRARIES to nothing. +if(sctp_FOUND) + set(sctp_LIBRARIES ${sctp_LIBRARY}) + set(sctp_INCLUDE_DIRS ${sctp_INCLUDE_DIR}) +endif() + +if(sctp_FOUND AND NOT TARGET sctp::sctp) + add_library(sctp::sctp UNKNOWN IMPORTED) + set_target_properties(sctp::sctp PROPERTIES + IMPORTED_LOCATION "${sctp_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${sctp_INCLUDE_DIRS}" + ) +endif() + +# Lastly make it so that the SCTP_LIBRARY and SCTP_INCLUDE_DIR variables +# only show up under the advanced options in the gui cmake applications. +mark_as_advanced(SCTP_LIBRARY SCTP_INCLUDE_DIR) From aa82d83228c79f07628b686f4a634cced9427589 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 20 Jan 2025 14:48:59 +0100 Subject: [PATCH 17/86] Correctly link sctp into SCTP_CLIENT Remove sctp from all other targets, since we use sctp::sctp on SCTP_CLIENT (would fail if it does not exist), and SCTP_CLIENT already fulfils the SCTP dependency. --- CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c78030c30..84d33a0590 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1408,12 +1408,14 @@ add_library(MME_APP ${MME_APP_SRC}) target_link_libraries(MME_APP PRIVATE m2ap m3ap) target_link_libraries(MME_APP PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) +find_package(sctp REQUIRED) set(SCTP_SRC ${OPENAIR3_DIR}/SCTP/sctp_common.c ${OPENAIR3_DIR}/SCTP/sctp_eNB_task.c ${OPENAIR3_DIR}/SCTP/sctp_eNB_itti_messaging.c ) add_library(SCTP_CLIENT ${SCTP_SRC}) +target_link_libraries(SCTP_CLIENT PRIVATE sctp::sctp) target_link_libraries(SCTP_CLIENT PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) set(NAS_SRC ${OPENAIR3_DIR}/NAS/) @@ -1758,7 +1760,7 @@ target_link_libraries(lte-softmodem PRIVATE ${NAS_UE_LIB} ITTI SIMU shlib_loader -Wl,--end-group z dl) -target_link_libraries(lte-softmodem PRIVATE pthread m CONFIG_LIB rt sctp) +target_link_libraries(lte-softmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(lte-softmodem PRIVATE ${T_LIB}) target_link_libraries(lte-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) target_link_libraries(lte-softmodem PRIVATE ${blas_LIBRARIES} ${cblas_LIBRARIES} ${lapacke_LIBRARIES} ${lapack_LIBRARIES}) @@ -1814,7 +1816,7 @@ target_link_libraries(lte-uesoftmodem PRIVATE ${NAS_UE_LIB} ITTI shlib_loader -Wl,--end-group z dl) -target_link_libraries(lte-uesoftmodem PRIVATE pthread m CONFIG_LIB rt sctp) +target_link_libraries(lte-uesoftmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(lte-uesoftmodem PRIVATE ${T_LIB}) target_link_libraries(lte-uesoftmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) target_link_libraries(lte-uesoftmodem PRIVATE ${blas_LIBRARIES} ${cblas_LIBRARIES} ${lapacke_LIBRARIES} ${lapack_LIBRARIES}) @@ -1851,7 +1853,7 @@ target_link_libraries(nr-softmodem PRIVATE time_management -Wl,--end-group z dl) -target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt sctp) +target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(nr-softmodem PRIVATE ${T_LIB}) target_link_libraries(nr-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) target_link_libraries(nr-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) @@ -1888,7 +1890,7 @@ target_link_libraries(nr-cuup PRIVATE CONFIG_LIB ITTI SCTP_CLIENT GTPV1U e1ap f1ap time_management - z sctp dl pthread shlib_loader ${T_LIB}) + z dl pthread shlib_loader ${T_LIB}) target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) if(E2_AGENT) target_link_libraries(nr-cuup PRIVATE e2_agent e2_agent_arg e2_ran_func_cuup) From 1e39f20d872f1489198280643f21379bbbe410f2 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 20 Jan 2025 14:49:37 +0100 Subject: [PATCH 18/86] Make GTP compile on its own, without LTE Avoid the include gtpv1_u_messages_types.h, as this pulls in e.g., LTE RRC definitions. The CU-UP load tester integrated in one of the following commits should be able to build without LTE RRC, if it was for GTP. Unfortunately, ITTI also pulls in LTE RRC, so it's also somewhat useless... Put some forward declarations instead. That also shows that the API is unnecessarily complex, and would need some cleanup. --- openair3/ocp-gtpu/gtp_itf.h | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index 7a8c4da627..a86dbefcf1 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -1,16 +1,41 @@ #ifndef __GTPUNEW_ITF_H__ #define __GTPUNEW_ITF_H__ +#include +#include +#include +#include + #define GTPNOK -1 # define GTPU_HEADER_OVERHEAD_MAX 64 -#include "openair2/COMMON/gtpv1_u_messages_types.h" +#include "common/platform_types.h" #include "common/utils/hashtable/hashtable.h" #ifdef __cplusplus extern "C" { #endif +/* forward declaration */ +struct protocol_ctxt_s; +typedef struct protocol_ctxt_s protocol_ctxt_t; +struct gtpv1u_enb_create_tunnel_req_s; +typedef struct gtpv1u_enb_create_tunnel_req_s gtpv1u_enb_create_tunnel_req_t; +struct gtpv1u_enb_create_tunnel_resp_s; +typedef struct gtpv1u_enb_create_tunnel_resp_s gtpv1u_enb_create_tunnel_resp_t; +struct gtpv1u_enb_delete_tunnel_req_s; +typedef struct gtpv1u_enb_delete_tunnel_req_s gtpv1u_enb_delete_tunnel_req_t; +struct gtpv1u_enb_create_x2u_tunnel_req_s; +typedef struct gtpv1u_enb_create_x2u_tunnel_req_s gtpv1u_enb_create_x2u_tunnel_req_t; +struct gtpv1u_enb_create_x2u_tunnel_resp_s; +typedef struct gtpv1u_enb_create_x2u_tunnel_resp_s gtpv1u_enb_create_x2u_tunnel_resp_t; +struct gtpv1u_gnb_create_tunnel_req_s; +typedef struct gtpv1u_gnb_create_tunnel_req_s gtpv1u_gnb_create_tunnel_req_t; +struct gtpv1u_gnb_create_tunnel_resp_s; +typedef struct gtpv1u_gnb_create_tunnel_resp_s gtpv1u_gnb_create_tunnel_resp_t; +struct gtpv1u_gnb_delete_tunnel_req_s; +typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; + typedef bool (*gtpCallback)(protocol_ctxt_t *ctxt_pP, const srb_flag_t srb_flagP, const rb_id_t rb_idP, @@ -116,7 +141,7 @@ extern "C" { hash_table_t *ue_mapping; } gtpv1u_data_t; #define GTPV1U_BEARER_OFFSET 3 -#define GTPV1U_MAX_BEARERS_ID (max_val_LTE_DRB_Identity - GTPV1U_BEARER_OFFSET) +#define GTPV1U_MAX_BEARERS_ID (32 - GTPV1U_BEARER_OFFSET) /* max_val_LTE_DRB_Identity */ typedef enum { BEARER_DOWN = 0, BEARER_IN_CONFIG, From dbe9e8684be3861d29ee2f65184fda797e3e587b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 3 Feb 2025 08:06:21 +0100 Subject: [PATCH 19/86] OAI hashtable not used in GTP --- openair3/ocp-gtpu/gtp_itf.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index a86dbefcf1..f0af60a5f7 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -11,7 +11,6 @@ # define GTPU_HEADER_OVERHEAD_MAX 64 #include "common/platform_types.h" -#include "common/utils/hashtable/hashtable.h" #ifdef __cplusplus extern "C" { #endif @@ -135,11 +134,6 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; instance_t gtpv1Init(openAddr_t context); void *gtpv1uTask(void *args); - // Legacy to fix - typedef struct gtpv1u_data_s { - /* RB tree of UEs */ - hash_table_t *ue_mapping; - } gtpv1u_data_t; #define GTPV1U_BEARER_OFFSET 3 #define GTPV1U_MAX_BEARERS_ID (32 - GTPV1U_BEARER_OFFSET) /* max_val_LTE_DRB_Identity */ typedef enum { From 44933f18c61e00b42567976118eab795dd55d9ee Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 4 Feb 2025 19:33:20 +0100 Subject: [PATCH 20/86] Code does nothing: gtpv1u_data_g never populated, always HASH_TABLE_KEY_NOT_EXISTS --- common/ran_context.h | 3 --- openair2/RRC/LTE/rrc_eNB.c | 37 ------------------------------------- 2 files changed, 40 deletions(-) diff --git a/common/ran_context.h b/common/ran_context.h index e009d0987f..91baa78d84 100644 --- a/common/ran_context.h +++ b/common/ran_context.h @@ -46,7 +46,6 @@ struct gNB_RRC_INST_s; struct eNB_MAC_INST_s; struct eNB_MAC_INST_NB_IoT_s; struct gNB_MAC_INST_s; -struct gtpv1u_data_s; struct RU_t_s; typedef struct { @@ -100,8 +99,6 @@ typedef struct { struct eNB_MAC_INST_NB_IoT_s **nb_iot_mac; /// NR MAC context variables struct gNB_MAC_INST_s **nrmac; - /// GTPu descriptor - struct gtpv1u_data_s *gtpv1u_data_g; /// RU descriptors. These describe what each radio unit is supposed to do and contain the necessary functions for fronthaul interfaces struct RU_t_s **ru; /// Mask to indicate fronthaul setup status of RU (hard-limit to 64 RUs) diff --git a/openair2/RRC/LTE/rrc_eNB.c b/openair2/RRC/LTE/rrc_eNB.c index 8d0fd2f8ed..90b2d3b86f 100644 --- a/openair2/RRC/LTE/rrc_eNB.c +++ b/openair2/RRC/LTE/rrc_eNB.c @@ -7721,9 +7721,6 @@ void *rrc_enb_process_itti_msg(void *notUsed) { case X2AP_HANDOVER_REQ_ACK: { struct rrc_eNB_ue_context_s *ue_context_p = NULL; - x2ap_handover_req_ack_t *x2ap_handover_req_ack = NULL; - hashtable_rc_t hash_rc = HASH_TABLE_KEY_NOT_EXISTS; - gtpv1u_ue_data_t *gtpv1u_ue_data_p = NULL; ue_context_p = rrc_eNB_get_ue_context(RC.rrc[instance], X2AP_HANDOVER_REQ_ACK(msg_p).rnti); if (ue_context_p == NULL) { @@ -7738,40 +7735,6 @@ void *rrc_enb_process_itti_msg(void *notUsed) { if (ue_context_p->ue_context.handover_info->state != HO_REQUEST) abort(); - hash_rc = hashtable_get(RC.gtpv1u_data_g->ue_mapping, ue_context_p->ue_context.rnti, (void **)>pv1u_ue_data_p); - - /* set target enb gtp teid */ - if (hash_rc == HASH_TABLE_KEY_NOT_EXISTS) { - LOG_E(RRC, "X2AP_HANDOVER_REQ_ACK func(), hashtable_get failed: while getting ue rnti %x in hashtable ue_mapping\n", ue_context_p->ue_context.rnti); - } else { - uint8_t nb_e_rabs_tobesetup = 0; - ebi_t eps_bearer_id = 0; - int ip_offset = 0; - in_addr_t in_addr; - x2ap_handover_req_ack = &X2AP_HANDOVER_REQ_ACK(msg_p); - nb_e_rabs_tobesetup = x2ap_handover_req_ack->nb_e_rabs_tobesetup; - ue_context_p->ue_context.nb_x2u_e_rabs = nb_e_rabs_tobesetup; - - for (int i = 0; i < nb_e_rabs_tobesetup; i++) { - ip_offset = 0; - eps_bearer_id = x2ap_handover_req_ack->e_rabs_tobesetup[i].e_rab_id; - ue_context_p->ue_context.enb_gtp_x2u_ebi[i] = eps_bearer_id; - ue_context_p->ue_context.enb_gtp_x2u_teid[i] = x2ap_handover_req_ack->e_rabs_tobesetup[i].gtp_teid; - gtpv1u_ue_data_p->bearers[eps_bearer_id - GTPV1U_BEARER_OFFSET].teid_teNB = x2ap_handover_req_ack->e_rabs_tobesetup[i].gtp_teid; - - if ((x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 4) || (x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 20)) { - in_addr = *((in_addr_t *)x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.buffer); - ip_offset = 4; - gtpv1u_ue_data_p->bearers[eps_bearer_id - GTPV1U_BEARER_OFFSET].tenb_ip_addr = in_addr; - ue_context_p->ue_context.enb_gtp_x2u_addrs[i] = x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr; - } - - if ((x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 16) || (x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 20)) { - memcpy(gtpv1u_ue_data_p->bearers[eps_bearer_id - GTPV1U_BEARER_OFFSET].tenb_ip6_addr.s6_addr, &x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.buffer[ip_offset], 16); - } - } - } - rrc_eNB_process_handoverCommand(instance, ue_context_p, &X2AP_HANDOVER_REQ_ACK(msg_p)); ue_context_p->ue_context.handover_info->state = HO_PREPARE; break; From 228f64275507aef5594a2216685ddc2dc7228cf5 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 31 Jul 2025 15:30:09 +0200 Subject: [PATCH 21/86] config_isparamset doesn't work for array types, using numelt instead --- openair2/GNB_APP/gnb_config.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 5274a4696d..2e6915016a 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1503,7 +1503,8 @@ static seq_arr_t *fill_du_sibs(paramdef_t *GNBparamarray) if (NODE_IS_DU(get_node_type())) AssertFatal(GNBparamarray[GNB_CU_SIBS_IDX].numelt == 0, "This is DU, do not input CU SIBs\n"); - if (!config_isparamset(GNBparamarray, GNB_DU_SIBS_IDX)) + // TODO config_isparamset doesn't seem to work for array types, checking numelt instead + if (GNBparamarray[GNB_DU_SIBS_IDX].numelt == 0) return NULL; seq_arr_t *du_SIBs = malloc(sizeof(seq_arr_t)); @@ -1808,12 +1809,13 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) beam_info->beams_per_period = beams_per_period; beam_info->beam_allocation_size = -1; // to be initialized once we have information on frame configuration } - if (config_isparamset(MacRLC_ParamList.paramarray[j], MACRLC_BEAMWEIGHTS_IDX)) { + // TODO config_isparamset doesn't seem to work for array types, checking numelt instead + int n = MacRLC_ParamList.paramarray[j][MACRLC_BEAMWEIGHTS_IDX].numelt; + if (n > 0) { if (NFAPI_MODE == NFAPI_MONOLITHIC) { GET_PARAMS_LIST(L1_ParamList, L1_Params, L1PARAMS_DESC, CONFIG_STRING_L1_LIST, NULL); AssertFatal(*(L1_ParamList.paramarray[j][L1_ANALOG_DAS].uptr) == 0, "No need to set beam weights in case of DAS\n"); } - int n = MacRLC_ParamList.paramarray[j][MACRLC_BEAMWEIGHTS_IDX].numelt; int num_beam = n; if (!ab) { AssertFatal(n % num_tx == 0, "Error! Number of beam input needs to be multiple of TX antennas\n"); @@ -1998,7 +2000,8 @@ static seq_arr_t *fill_cu_sibs(paramdef_t *GNBparamarray) if (NODE_IS_CU(get_node_type())) AssertFatal(GNBparamarray[GNB_DU_SIBS_IDX].numelt == 0, "This is CU, do not input DU SIBs\n"); - if (!config_isparamset(GNBparamarray, GNB_CU_SIBS_IDX)) + // TODO config_isparamset doesn't seem to work for array types, checking numelt instead + if (GNBparamarray[GNB_CU_SIBS_IDX].numelt == 0) return NULL; seq_arr_t *SIBs = malloc(sizeof(seq_arr_t)); From 73cfaba2dc40a6f0599ad85f7cbbbe2bc70dd793 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Thu, 24 Jul 2025 21:04:28 +0200 Subject: [PATCH 22/86] CI: refactor iperf3 UDP analysis function - Add helper function to normalize bitrate values to Mbps for calculations. - Add helper function to extract data from iperf test report. - Clean up the UDP analysis function, improve test report formatting. - Fix an issue where Gbps values were incorrectly reported due to inconsistent unit parsing. - Update CI unit test report checks to match the new formatting. --- ci-scripts/cls_oaicitest.py | 101 ++++++++++---------- ci-scripts/tests/log/iperf_udp_msg_nok.txt | 6 +- ci-scripts/tests/log/iperf_udp_msg_nok2.txt | 6 +- ci-scripts/tests/log/iperf_udp_msg_nok3.txt | 6 +- ci-scripts/tests/log/iperf_udp_msg_ok.txt | 6 +- ci-scripts/tests/log/iperf_udp_msg_ok2.txt | 6 +- 6 files changed, 67 insertions(+), 64 deletions(-) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 53bdacaacc..5cb2708efc 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -83,6 +83,29 @@ def Iperf_ComputeTime(args): raise Exception('Iperf time not found!') return int(result.group('iperf_time')) +def convert_to_mbps(value, magnitude): + value = float(value) + if magnitude == 'K' or magnitude == 'k': + return value / 1000 + elif magnitude == 'M': + return value + elif magnitude == 'G': + return value * 1000 + else: + return value + +def extract_iperf_data(res): + if not res: + return None + bitrate_val = res.group('bitrate') + magnitude = res.group('magnitude') + return { + 'bitrate_mbps': convert_to_mbps(bitrate_val, magnitude), + 'jitter': res.group('jitter'), + 'packetloss': res.group('packetloss'), + 'bitrate_disp': f'{float(bitrate_val):.2f} {magnitude}bps' + } + def Iperf_analyzeV3TCPJson(filename, iperf_tcp_rate_target): try: with open(filename) as f: @@ -130,54 +153,37 @@ def Iperf_analyzeV3BIDIRJson(filename): return (True, msg) def Iperf_analyzeV3UDP(filename, iperf_bitrate_threshold, iperf_packetloss_threshold, target_bitrate): - if (not os.path.isfile(filename)): + if not os.path.isfile(filename): return (False, 'Iperf3 UDP: Log file not present') - if (os.path.getsize(filename)==0): + if os.path.getsize(filename) == 0: return (False, 'Iperf3 UDP: Log file is empty') - sender_bitrate = None - receiver_bitrate = None - with open(filename, 'r') as server_file: - for line in server_file.readlines(): - res_sender = re.search(r'(?P[0-9\.]+)\s+(?P[KMG]?bits\/sec)\s+(?P[0-9\.]+\s+ms)\s+(?P-?\d+)/(?P-?\d+) \((?P[0-9\.]+).*?\s+(sender)', line) - res_receiver = re.search(r'(?P[0-9\.]+)\s+(?P[KMG]?bits\/sec)\s+(?P[0-9\.]+\s+ms)\s+(?P-?\d+)/(?P-?\d+)\s+\((?P[0-9\.]+)%\).*?(receiver)', line) - if res_sender is not None: - sender_bitrate = res_sender.group('bitrate') - sender_unit = res_sender.group('unit') - sender_jitter = res_sender.group('jitter') - sender_lostPack = res_sender.group('lostPack') - sender_sentPack = res_sender.group('sentPack') - sender_packetloss = res_sender.group('lost') - if res_receiver is not None: - receiver_bitrate = res_receiver.group('bitrate') - receiver_unit = res_receiver.group('unit') - receiver_jitter = res_receiver.group('jitter') - receiver_lostPack = res_receiver.group('lostPack') - receiver_receivedPack = res_receiver.group('receivedPack') - receiver_packetloss = res_receiver.group('lost') - if receiver_bitrate is not None and sender_bitrate is not None: - if sender_unit == 'Kbits/sec': - sender_bitrate = float(sender_bitrate) / 1000 - if receiver_unit == 'Kbits/sec': - receiver_bitrate = float(receiver_bitrate) / 1000 - br_perf = 100 * float(receiver_bitrate) / float(target_bitrate) - br_perf = '%.2f ' % br_perf - sender_bitrate = '%.2f ' % float(sender_bitrate) - receiver_bitrate = '%.2f ' % float(receiver_bitrate) - req_msg = f'Sender Bitrate : {sender_bitrate} Mbps' - bir_msg = f'Receiver Bitrate: {receiver_bitrate} Mbps' - brl_msg = f'{br_perf}%' - jit_msg = f'Jitter : {receiver_jitter}' - pal_msg = f'Packet Loss : {receiver_packetloss} %' - if float(br_perf) < float(iperf_bitrate_threshold): - brl_msg = f'too low! < {iperf_bitrate_threshold}%' - if float(receiver_packetloss) > float(iperf_packetloss_threshold): - pal_msg += f' (too high! > {iperf_packetloss_threshold}%)' - result = float(br_perf) >= float(iperf_bitrate_threshold) and float(receiver_packetloss) <= float(iperf_packetloss_threshold) - return (result, f'{req_msg}\n{bir_msg} ({brl_msg})\n{jit_msg}\n{pal_msg}') - else: + sender_data = None + receiver_data = None + with open(filename, 'r') as server_file: + for line in server_file: + res_sender = re.search(r'(?P[0-9\.]+)\s+(?P[kKMG]?)bits\/sec\s+(?P[0-9\.]+\s+ms)\s+(?P-?\d+)/(?P-?\d+)\s+\((?P[0-9\.eE\-\+]+).*?\s+(sender)', line) + res_receiver = re.search(r'(?P[0-9\.]+)\s+(?P[kKMG]?)bits\/sec\s+(?P[0-9\.]+\s+ms)\s+(?P-?\d+)/(?P-?\d+)\s+\((?P[0-9\.eE\-\+]+)%\).*?(receiver)', line) + if res_sender: + sender_data = extract_iperf_data(res_sender) + if res_receiver: + receiver_data = extract_iperf_data(res_receiver) + if not sender_data or not receiver_data: return (False, 'Could not analyze iperf report') + br_perf = 100 * receiver_data['bitrate_mbps'] / float(target_bitrate) + br_perf_str = f'{br_perf:.2f}%' + req_msg = f"Sender Bitrate : {sender_data['bitrate_disp']}" + bir_msg = f"Receiver Bitrate: {receiver_data['bitrate_disp']} ({br_perf_str})" + jit_msg = f"Jitter : {receiver_data['jitter']}" + pal_msg = f"Packet Loss : {receiver_data['packetloss']}%" + if br_perf < float(iperf_bitrate_threshold): + bir_msg += f' (too low! < {iperf_bitrate_threshold}%)' + if float(receiver_data['packetloss']) > float(iperf_packetloss_threshold): + pal_msg += f' (too high! > {iperf_packetloss_threshold}%)' + result = br_perf >= float(iperf_bitrate_threshold) and float(receiver_data['packetloss']) <= float(iperf_packetloss_threshold) + return (result, f'{req_msg}\n{bir_msg}\n{jit_msg}\n{pal_msg}') + def Iperf_analyzeV2UDP(server_filename, iperf_bitrate_threshold, iperf_packetloss_threshold, target_bitrate): result = None if (not os.path.isfile(server_filename)): @@ -190,12 +196,9 @@ def Iperf_analyzeV2UDP(server_filename, iperf_bitrate_threshold, iperf_packetlos result = re.search(statusTemplate, str(line)) or result if result is None: return (False, 'Could not parse server report!') - bitrate = float(result.group('bitrate')) - magn = result.group('magnitude') - if magn == "k" or magn == "K": - bitrate /= 1000 - elif magn == "G": # we assume bitrate in Mbps, therefore it must be G now - bitrate *= 1000 + bitrate_val = float(result.group('bitrate')) + magnitude = result.group('magnitude') + bitrate = convert_to_mbps(bitrate_val, magnitude) jitter = float(result.group('jitter')) packetloss = float(result.group('packetloss')) br_perf = float(bitrate)/float(target_bitrate) * 100 diff --git a/ci-scripts/tests/log/iperf_udp_msg_nok.txt b/ci-scripts/tests/log/iperf_udp_msg_nok.txt index 0e03a2dc76..a2ec0e22d6 100644 --- a/ci-scripts/tests/log/iperf_udp_msg_nok.txt +++ b/ci-scripts/tests/log/iperf_udp_msg_nok.txt @@ -1,4 +1,4 @@ -Sender Bitrate : 0.52 Mbps -Receiver Bitrate: 0.52 Mbps (too low! < 99%) +Sender Bitrate : 524.00 Kbps +Receiver Bitrate: 524.00 Kbps (52.40%) (too low! < 99%) Jitter : 0.026 ms -Packet Loss : 10 % (too high! > 0%) +Packet Loss : 10% (too high! > 0%) diff --git a/ci-scripts/tests/log/iperf_udp_msg_nok2.txt b/ci-scripts/tests/log/iperf_udp_msg_nok2.txt index c0c34193f2..e24cc1402a 100644 --- a/ci-scripts/tests/log/iperf_udp_msg_nok2.txt +++ b/ci-scripts/tests/log/iperf_udp_msg_nok2.txt @@ -1,4 +1,4 @@ -Sender Bitrate : 0.00 Mbps -Receiver Bitrate: 3.98 Mbps (too low! < 99%) +Sender Bitrate : 0.00 bps +Receiver Bitrate: 3.98 Mbps (4.97%) (too low! < 99%) Jitter : 3.847 ms -Packet Loss : 0.038 % (too high! > 0%) +Packet Loss : 0.038% (too high! > 0%) diff --git a/ci-scripts/tests/log/iperf_udp_msg_nok3.txt b/ci-scripts/tests/log/iperf_udp_msg_nok3.txt index c302493e85..b027491913 100644 --- a/ci-scripts/tests/log/iperf_udp_msg_nok3.txt +++ b/ci-scripts/tests/log/iperf_udp_msg_nok3.txt @@ -1,4 +1,4 @@ -Sender Bitrate : 1.03 Mbps -Receiver Bitrate: 0.00 Mbps (too low! < 99%) +Sender Bitrate : 1.03 Mbps +Receiver Bitrate: 0.00 bps (0.00%) (too low! < 99%) Jitter : 0.000 ms -Packet Loss : 0 % +Packet Loss : 0% diff --git a/ci-scripts/tests/log/iperf_udp_msg_ok.txt b/ci-scripts/tests/log/iperf_udp_msg_ok.txt index defea05c79..a771c6fdb1 100644 --- a/ci-scripts/tests/log/iperf_udp_msg_ok.txt +++ b/ci-scripts/tests/log/iperf_udp_msg_ok.txt @@ -1,4 +1,4 @@ -Sender Bitrate : 1.00 Mbps -Receiver Bitrate: 1.00 Mbps (99.60 %) +Sender Bitrate : 996.00 Kbps +Receiver Bitrate: 996.00 Kbps (99.60%) Jitter : 0.029 ms -Packet Loss : 0 % +Packet Loss : 0% diff --git a/ci-scripts/tests/log/iperf_udp_msg_ok2.txt b/ci-scripts/tests/log/iperf_udp_msg_ok2.txt index 31a5ec14cd..856b851112 100644 --- a/ci-scripts/tests/log/iperf_udp_msg_ok2.txt +++ b/ci-scripts/tests/log/iperf_udp_msg_ok2.txt @@ -1,4 +1,4 @@ -Sender Bitrate : 4.67 Mbps -Receiver Bitrate: 4.67 Mbps (100.00 %) +Sender Bitrate : 4.67 Mbps +Receiver Bitrate: 4.67 Mbps (100.00%) Jitter : 3.413 ms -Packet Loss : 0 % +Packet Loss : 0% From a9f10bd794ad828af12b14dbd7db05965984362c Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Thu, 31 Jul 2025 11:26:55 +0200 Subject: [PATCH 23/86] Add CI unit test for highrate iperf test --- ci-scripts/tests/ping-iperf.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ci-scripts/tests/ping-iperf.py b/ci-scripts/tests/ping-iperf.py index 225fe4e868..de68122cee 100644 --- a/ci-scripts/tests/ping-iperf.py +++ b/ci-scripts/tests/ping-iperf.py @@ -60,5 +60,20 @@ class TestPingIperf(unittest.TestCase): success = self.ci.Iperf2_Unidir(self.html, self.cont, infra_file=infra_file) self.assertTrue(success) + def test_iperf_highrate(self): + # note: needs to be five seconds because Iperf() adds -O 3, so if it is + # too short, the server is terminated before the client loaded + # everything + self.ci.iperf_args = "-u -t 5 -b 1000M -R -O 0" + self.ci.svr_id = "test" + self.ci.svr_node = "localhost" + self.ci.iperf_packetloss_threshold = "0" + self.ci.iperf_bitrate_threshold = "0" + self.ci.iperf_profile = "balanced" + infra_file = "tests/config/infra_ping_iperf.yaml" + # TODO Should need nothing but options and UE(s) to use + success = self.ci.Iperf(self.html, self.cont, infra_file=infra_file) + self.assertTrue(success) + if __name__ == '__main__': unittest.main() From af3302d2d6d41f7b8a4b2da6f790f086c3537c30 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 1 Aug 2025 11:32:49 +0200 Subject: [PATCH 24/86] Remove unused statement --- ci-scripts/ran.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ci-scripts/ran.py b/ci-scripts/ran.py index f0f790ba2c..46711b1b73 100644 --- a/ci-scripts/ran.py +++ b/ci-scripts/ran.py @@ -124,7 +124,6 @@ class RANManagement(): self.testCase_id = HTML.testCase_id mySSH = SSH.SSHConnection() - cwd = os.getcwd() mySSH.open(lIpAddr, lUserName, lPassWord) mySSH.command('cd ' + lSourcePath, '\$', 5) From be60dbd38c280064c18afe79eaf259e859e38d00 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 31 Jul 2025 18:28:46 +0200 Subject: [PATCH 25/86] CI: shorten UE names (IP might not be available) --- ci-scripts/cls_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-scripts/cls_module.py b/ci-scripts/cls_module.py index 3ec6ffc9e5..65edbfdb56 100644 --- a/ci-scripts/cls_module.py +++ b/ci-scripts/cls_module.py @@ -65,7 +65,7 @@ class Module_UE: logging.info(f'initialized UE {self} from {filename}') def __str__(self): - return f"{self.module_name}@{self.host} [IP: {self.getIP()}]" + return f"{self.module_name}@{self.host}" def __repr__(self): return self.__str__() From 442c29cfcd51b9f00db1433ef262638c450b0069 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 31 Jul 2025 18:29:00 +0200 Subject: [PATCH 26/86] CI: use getConnection() to simplify code --- ci-scripts/cls_module.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ci-scripts/cls_module.py b/ci-scripts/cls_module.py index 65edbfdb56..3fed4f3dd6 100644 --- a/ci-scripts/cls_module.py +++ b/ci-scripts/cls_module.py @@ -73,12 +73,8 @@ class Module_UE: def _command(self, cmd, silent = False): if cmd is None: raise Exception("no command provided") - if self.host == "" or self.host == "localhost": - c = cls_cmd.LocalCmd() - else: - c = cls_cmd.RemoteCmd(self.host) - response = c.run(cmd, silent=silent) - c.close() + with cls_cmd.getConnection(self.host) as c: + response = c.run(cmd, silent=silent) return response #-----------------$ From bbf4cd4a90109dcae1e996e6d79488e4fe0621d8 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 1 Aug 2025 13:38:05 +0200 Subject: [PATCH 27/86] gNB: fix scheduling of rel-17 SIBs --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index a6d48037a7..134b43f583 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -775,7 +775,7 @@ static bool test_other_sib_sched_occasion(int window_pos, int rel_frame, int rel_slot) { - int x = window_pos * window_len; + int x = (window_pos - 1) * window_len; int T = 8 << period; int test_frame = (frame - rel_frame) % MAX_FRAME_NUMBER; int si_slot = (x % n_slots_frame) + rel_slot; @@ -848,7 +848,7 @@ void schedule_nr_other_sib(module_id_t module_idP, for (int ssb = 0; ssb < num_ssb; ssb++) { for (int i = 0; i < schedInfo->schedulingInfoList.list.count; i++) { NR_SchedulingInfo_t *schedulingInfo = schedInfo->schedulingInfoList.list.array[i]; - if (test_other_sib_sched_occasion(i, + if (test_other_sib_sched_occasion(i + 1, window_length_sl, schedulingInfo->si_Periodicity, n_slots_frame, From 53208bd163ab9aad4387a30597b1bb561a468b46 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 1 Aug 2025 13:39:28 +0200 Subject: [PATCH 28/86] NR UE: fix calculation of SI window expiry --- .../NR_MAC_UE/nr_ue_dci_configuration.c | 41 +++++++------------ 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 7a2e9c6ed9..4b0623d6fe 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -385,12 +385,14 @@ bool search_space_monitoring_ocasion_other_si(NR_UE_MAC_INST_t *mac, return false; } -bool is_window_valid(NR_UE_MAC_INST_t *mac, int window_slots, int abs_slot) +static bool is_window_valid(NR_UE_MAC_INST_t *mac, int window_slots, int abs_slot, int slots_per_frame) { if (mac->si_SchedInfo.si_window_start == -1) { // out of window return false; - } else if (abs_slot > mac->si_SchedInfo.si_window_start + window_slots) { + } else if ((abs_slot - mac->si_SchedInfo.si_window_start + MAX_FRAME_NUMBER * slots_per_frame) + % (MAX_FRAME_NUMBER * slots_per_frame) + >= window_slots) { // window expired mac->si_SchedInfo.si_window_start = -1; return false; @@ -407,33 +409,18 @@ static bool monitor_dci_for_other_SI(NR_UE_MAC_INST_t *mac, { // according to 5.2.2.3.2 in 331 const int abs_slot = frame * slots_per_frame + slot; + const si_schedinfo_config_t *config = mac->si_SchedInfo.si_SchedInfo_list.array[si_idx]; + const int window_slots = 5 << mac->si_SchedInfo.si_WindowLength; + const int x = (config->si_WindowPosition - 1) * window_slots; + const int T = 8 << config->si_Periodicity; // radio frame periodicity - si_schedinfo_config_t *config = mac->si_SchedInfo.si_SchedInfo_list.array[si_idx]; - int window_slots = 5 << mac->si_SchedInfo.si_WindowLength; - int x = (config->si_WindowPosition - 1) * window_slots; - int T = 8 << config->si_Periodicity; // radio frame periodicity - bool check_valid; - switch (config->type) { - case NR_SI_INFO : - if (mac->si_SchedInfo.si_window_start == -1) { - if ((frame % T) == (x / slots_per_frame) && (x % slots_per_frame == 0)) - mac->si_SchedInfo.si_window_start = abs_slot; // in terms of absolute slot number - } - check_valid = is_window_valid(mac, window_slots, abs_slot); - if (check_valid && search_space_monitoring_ocasion_other_si(mac, ss, abs_slot, frame, slot, slots_per_frame)) - return true; - break; - case NR_SI_INFO_v1700 : - if (mac->si_SchedInfo.si_window_start == -1) { - if ((frame % T == floor(x / slots_per_frame)) && (slot == x % slots_per_frame)) - mac->si_SchedInfo.si_window_start = abs_slot; - } - if (is_window_valid(mac, window_slots, abs_slot)) - return search_space_monitoring_ocasion_other_si(mac, ss, abs_slot, frame, slot, slots_per_frame); - break; - default : - AssertFatal(false, "Invalid SI-SchedulingInfo case\n"); + if (mac->si_SchedInfo.si_window_start == -1) { + if ((frame % T) == (x / slots_per_frame) && (slot == x % slots_per_frame)) + mac->si_SchedInfo.si_window_start = abs_slot; // in terms of absolute slot number } + if (is_window_valid(mac, window_slots, abs_slot, slots_per_frame)) + return search_space_monitoring_ocasion_other_si(mac, ss, abs_slot, frame, slot, slots_per_frame); + return false; } From d4f4cefd6bb15d4021b2dee0ca0d0999c3e110ff Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 25 Jul 2025 11:54:43 +0000 Subject: [PATCH 29/86] set ntn-UlSyncValidityDuration-r17 to 5 seconds for LEO scenario Now that we reliably receive SIB19, we can set the ntn-UlSyncValidityDuration-r17 to a sane value and thus make sure that the UE always has up-to-date SIB19 information. --- .../conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn-leo.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn-leo.conf b/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn-leo.conf index d08d900098..59438e8800 100644 --- a/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn-leo.conf +++ b/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn-leo.conf @@ -150,6 +150,8 @@ gNBs = #ext2 #ntn_Config_r17 + # This can be of values {s5, s10, s15, s20, s25, s30, s35, s40, s45, s50, s55, s60, s120, s180, s240, s900} + ntn-UlSyncValidityDuration-r17 = 5; cellSpecificKoffset_r17 = 40; ta-Common-r17 = 4627000; # 18.84 ms ta-CommonDrift-r17 = -230000; # -46 µs/s From 7827a40f19c2d1e19377e518decd6f39ce1ed526 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 25 Jul 2025 11:57:54 +0000 Subject: [PATCH 30/86] NTN config: setting sib1_tda is not required anymore --- ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn-leo.conf | 1 - ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf | 1 - 2 files changed, 2 deletions(-) diff --git a/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn-leo.conf b/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn-leo.conf index 59438e8800..010e8be87e 100644 --- a/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn-leo.conf +++ b/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn-leo.conf @@ -16,7 +16,6 @@ gNBs = ////////// Physical parameters: - sib1_tda = 5; min_rxtxtime = 6; num_dlharq = 32; num_ulharq = 32; diff --git a/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf b/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf index 7fbaac5f5b..bf3e6b295f 100644 --- a/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf +++ b/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf @@ -16,7 +16,6 @@ gNBs = ////////// Physical parameters: - sib1_tda = 5; min_rxtxtime = 6; disable_harq = 1; cu_sibs = [2]; From 16fd1bb67cd0e3b3adcc1232a08d6d1946034aff Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 25 Jul 2025 12:00:04 +0000 Subject: [PATCH 31/86] gNB: fix case when multiple Rel-17 SIBs would be defined The boolean value v17_sib was not set to true when allocating si_schedulingInfo_v17 and schedulingInfo2_r17. So multiple Rel-17 SIBs would lead to multiple allocations and only the last SIB being actually configured. Fix this by removing the redundant v17_sib variable and directly check if the memory was already allocated. Also move the definition and allocation of si_schedulingInfo_v17 closer to its actual usage. --- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index eba1152cd0..13e3014ca9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -2418,9 +2418,7 @@ void update_SIB1_NR_SI(NR_BCCH_DL_SCH_Message_t *sib1_bcch, int num_sibs, int si schedulingInfo->si_BroadcastStatus = NR_SchedulingInfo__si_BroadcastStatus_broadcasting; schedulingInfo->si_Periodicity = NR_SchedulingInfo__si_Periodicity_rf16; bool reg_sib = false; - NR_SI_SchedulingInfo_v1700_t *si_schedulingInfo_v17 = NULL; NR_SchedulingInfo2_r17_t *schedulingInfo2_r17 = NULL; - bool v17_sib = false; for (int i = 0; i < num_sibs; i++) { int sib_num = sibs[i]; AssertFatal(sib_num > 1 && sib_num < 21, "other SIB invalid type\n"); @@ -2438,8 +2436,7 @@ void update_SIB1_NR_SI(NR_BCCH_DL_SCH_Message_t *sib1_bcch, int num_sibs, int si } asn1cSeqAdd(&schedulingInfo->sib_MappingInfo.list, mapping); } else { - if (v17_sib == false) { - si_schedulingInfo_v17 = calloc(1, sizeof(*si_schedulingInfo_v17)); + if (schedulingInfo2_r17 == NULL) { schedulingInfo2_r17 = calloc(1, sizeof(*schedulingInfo2_r17)); schedulingInfo2_r17->si_BroadcastStatus_r17 = NR_SchedulingInfo2_r17__si_BroadcastStatus_r17_broadcasting; schedulingInfo2_r17->si_Periodicity_r17 = NR_SchedulingInfo2_r17__si_Periodicity_r17_rf16; @@ -2458,7 +2455,8 @@ void update_SIB1_NR_SI(NR_BCCH_DL_SCH_Message_t *sib1_bcch, int num_sibs, int si AssertFatal(reg_sib, "At least 1 SIB from SchedulingInfo (SIB2 to SIB14 included) needs to be present\n"); asn1cSeqAdd(&info->schedulingInfoList.list, schedulingInfo); sib1->si_SchedulingInfo = info; - if (si_schedulingInfo_v17) { + if (schedulingInfo2_r17) { + NR_SI_SchedulingInfo_v1700_t *si_schedulingInfo_v17 = calloc_or_fail(1, sizeof(*si_schedulingInfo_v17)); asn1cSeqAdd(&si_schedulingInfo_v17->schedulingInfoList2_r17.list, schedulingInfo2_r17); NR_SIB1_v1610_IEs_t *sib1_v1610 = NULL; if (sib1->nonCriticalExtension) From 506dc7da52b05b3806775e11fe6425ffd576520b Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 1 Aug 2025 16:04:23 +0200 Subject: [PATCH 32/86] NR UE: invalidate SI window on successful SI reception to avoid wrongly reusing this old SI window for new SI reception This fix was suggested by Francesco Mani --- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 1 + 1 file changed, 1 insertion(+) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index fb0645560e..7752fcad4d 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -310,6 +310,7 @@ void nr_ue_decode_BCCH_DL_SCH(NR_UE_MAC_INST_t *mac, if (mac->get_otherSI[i]) mac->get_otherSI[i] = false; } + mac->si_SchedInfo.si_window_start = -1; T(T_NRUE_MAC_DL_PDU_WITH_DATA, T_INT(SI_RNTI), T_INT(-1 /* frame, unavailable here */), From 07629e29d67dbb6ecb30345568e1b66b7e342b5f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 20 Jan 2025 14:49:21 +0100 Subject: [PATCH 33/86] Add CU-UP load tester and documentation This is a simple CU-UP load tester. It sets up a configurable number of UEs in the CU-UP, then sends GTP traffic in downlink/uplink in the N3-F1/F1-N3 direction at the CU-UP. It then compares the sent and received data. Additional parameters, such as amount of traffic, test length, packet size, and IP addresses/ports are configurable. To run: mkdir build && cd build && cmake .. -GNinja && ninja nr-cuup nr-cuup-load-test params_libconfig ./tests/nr-cuup/nr-cuup-load-test ./nr-cuup -O ../tests/nr-cuup/load-test.conf The tester should be able to detect lost and reordered packets. Example lost: Receive order 2,3,5,6 (lost packet 4) => on packet 2, expected 2, next=expected+1=3 => on packet 3, expected 3, next=expected+1=4 => on packet 5, expected 4, diff=5-4=1 (lost 1), next=payload+1=6 => on packet 6, expected 6 => 1 lost Example reordered: Receiver order 2,5,3,6 (lost packet 4) => on packet 2, expected 2, next=expected+1=3 => on packet 5, expected 3, diff=5-3=2 (lost 2), next=payload+1=6 => on packet 3, expected 6, (reduce lost by 1 to 1), next=expected=6 => on packet 6, expected 6 => 1 lost Example reordered: Receiver order 2,5,3,4,6 (no losses) --- CMakeLists.txt | 1 + doc/README.md | 2 + tests/CMakeLists.txt | 1 + tests/nr-cuup/CMakeLists.txt | 4 + tests/nr-cuup/README.md | 78 ++++ tests/nr-cuup/load-test.conf | 58 +++ tests/nr-cuup/nr-cuup-load-test.c | 673 ++++++++++++++++++++++++++++++ 7 files changed, 817 insertions(+) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/nr-cuup/CMakeLists.txt create mode 100644 tests/nr-cuup/README.md create mode 100644 tests/nr-cuup/load-test.conf create mode 100644 tests/nr-cuup/nr-cuup-load-test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 84d33a0590..5e14642507 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2184,3 +2184,4 @@ add_subdirectory(openair1) add_subdirectory(openair2) add_subdirectory(openair3) add_subdirectory(radio) +add_subdirectory(tests) diff --git a/doc/README.md b/doc/README.md index 756294fb5d..27c2688673 100644 --- a/doc/README.md +++ b/doc/README.md @@ -130,6 +130,8 @@ The other SDRs (AW2S, LimeSDR, ...) have no READMEs. # Testing - [UnitTests.md](./UnitTests.md) explains the unit testing setup +- Component tests are under `tests/`. Currently, there is a simple CU-UP + tester, see the corresponding [README.md](../tests/nr-cuup/README.md). - [TESTBenches.md](./TESTBenches.md) lists the CI setup and links to pipelines # Developer tools diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000000..cefa52d456 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(nr-cuup) diff --git a/tests/nr-cuup/CMakeLists.txt b/tests/nr-cuup/CMakeLists.txt new file mode 100644 index 0000000000..02becb2496 --- /dev/null +++ b/tests/nr-cuup/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(nr-cuup-load-test nr-cuup-load-test.c) +target_link_libraries(nr-cuup-load-test PRIVATE GTPV1U ${T_LIB} utils ITTI e1ap + SCTP_CLIENT) +target_link_libraries(nr-cuup-load-test PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) # ITTI diff --git a/tests/nr-cuup/README.md b/tests/nr-cuup/README.md new file mode 100644 index 0000000000..3095e5f600 --- /dev/null +++ b/tests/nr-cuup/README.md @@ -0,0 +1,78 @@ +This is a simple tester for the CU-UP. It configures the CU-UP via E1, and can +stream data via GTP in uplink and downlink directions. + +[[_TOC_]] + +# Overview + +From a schematic point of view, the tester/CU-UP interaction looks like this: + +``` + +-------+ ++------>| CU-UP |<------+ +| +-------+ | +| | | +| F1-U | E1 | NG-U +| (data) | (control) | (data) +| | | +| +-------+ | ++------>|tester |<------+ + +-------+ +``` + +The tester is for performance testing of a CU-UP, and behaves like an +integrated DU/CU-CP/UPF from the CU-UP's point of view. + +The tester and CU-UP have an E1 connection through which control information is +exchanged, e.g., configuration of UEs/bearers and associated information. In +other words, towards the CU-UP, the tester appears like a CU-CP. + +Further, for each UE/bearer, CU-UP is configured with GTP IP information and +the corresponding TEID. The CU-UP will forward downlink data arriving on NG-U +on the F1-U interface, and uplink data arriving on F1-U on the NG-U interface. +In other words, on the F1-U interface, the tester appears like a DU to the +CU-UP, and on the NG-U interface, the tester appears like a UPF to the CU-UP. + +A test scenario is fixed to these steps: + +1. The tester sets up a number of UEs via the E1 interface (it is possible to + see the message exchange in Wireshark). +2. The tester streams data in downlink/uplink (from the CU-UP's point of view) + and measures how much data is lost. +3. The tester releases all the UEs via the E1 interface, and disconnects from + the CU-UP. + +Note: The tester uses the same GTP module as the CU-UP. Thus, data might not +only be lost at the GTP interface of the CU-UP, but also at the tester. + +# Usage + +You can build the CU-UP and the tester like so: + + cd ~/openairinterface5g + mkdir build && cd build && cmake .. -GNinja && ninja nr-cuup nr-cuup-load-test params_libconfig + ./tests/nr-cuup/nr-cuup-load-test + ./nr-cuup -O ../tests/nr-cuup/load-test.conf + +This builds both tester and CU-UP in the directory `build/`, then starts the +load tester with default values + +- 1 UE +- 10s of Test +- 60 Mbps of traffic in both downlik/uplink with a packet size of 1400 bytes + +To see the available options, run + + ./tests/nr-cuup/nr-cuup-load-test -h + +The configuration file [`load-test.conf`](./load-test.conf) matches the default +tester configuration (F1-U GTP traffic over non-standard port 2153, NG-U GTP +over 2152, tests on localhost). The configuration file includes this +non-standard port 2153 as the GTP module, as of now, cannot bind on the same +interface and port for both F1-U and NG-U due to internal limitations. + +# Limitations + +- The tester does not yet create/remove UEs during a traffic test. +- It might be possible to integrate with an external GTP traffic generator, + but we did not test this. diff --git a/tests/nr-cuup/load-test.conf b/tests/nr-cuup/load-test.conf new file mode 100644 index 0000000000..9829640a02 --- /dev/null +++ b/tests/nr-cuup/load-test.conf @@ -0,0 +1,58 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_CU_UP_ID = 0xe00; + +# cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 222; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) }); + + + tr_s_preference = "f1"; + + local_s_address = "127.0.0.6"; + remote_s_address = "127.0.0.1"; + local_s_portc = 501; + local_s_portd = 2153; + remote_s_portc = 500; + remote_s_portd = 2153; + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + E1_INTERFACE = + ( + { + type = "up"; + ipv4_cucp = "127.0.0.1"; + ipv4_cuup = "127.0.0.6"; + } + ) + + NETWORK_INTERFACES : + { + GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.109"; + GNB_IPV4_ADDRESS_FOR_NGU = "127.0.0.7"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + } +); + +log_config: { + global_log_level = "info"; +}; diff --git a/tests/nr-cuup/nr-cuup-load-test.c b/tests/nr-cuup/nr-cuup-load-test.c new file mode 100644 index 0000000000..48e088fc3c --- /dev/null +++ b/tests/nr-cuup/nr-cuup-load-test.c @@ -0,0 +1,673 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include + +#include "common/utils/LOG/log.h" +#include "common/utils/ocp_itti/intertask_interface.h" // this pulls in all of OAI+4G+5G +#include "openair3/SCTP/sctp_eNB_task.h" +#include "openair2/E1AP/e1ap.h" +#include "openair3/ocp-gtpu/gtp_itf.h" + +configmodule_interface_t *uniqCfg; + +#define DL 0 +#define UL 1 + +int nr_rlc_get_available_tx_space(const rnti_t rntiP, const logical_chan_id_t channel_idP) { abort(); return 0; } /* in GTP */ +softmodem_params_t *get_softmodem_params(void) { static softmodem_params_t p = {0}; return &p; }; /* in ITTI */ +void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) { abort(); } /* CU-UP */ +void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) { abort(); } /* CU-UP */ +void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd) { abort(); } /* CU-UP */ +void e1_reset(void) { abort(); } /* CU-UP */ +instance_t *N3GTPUInst = NULL; /* CU-UP */ +instance_t CUuniqInstance=0; /* CU-UP */ + +void exit_function(const char *file, const char *function, const int line, const char *s, const int assert) +{ + LOG_E(GNB_APP, "error at %s:%d:%s: %s\n", file, line, function, s); + abort(); +} + +/* @brief Dummy function to pass a fptr to ITTI for staring RRC queue, without + * actually handling any messages. Functions called by main() will send/receive + * ITTI messages to E1, as if it was RRC. */ +static void *cuup_tester_rrc(void *) +{ + itti_mark_task_ready(TASK_RRC_GNB); + LOG_I(GNB_APP, "created RRC queue\n"); + return NULL; +} + +/* @brief Initialize E1 interface (as if it was CU-CP) */ +static void init_e1() +{ + net_ip_address_t ip = {.ipv4 = 1, .ipv4_address = "127.0.0.1" }; + e1ap_net_config_t conf = { .CUCP_e1_ip_address = ip }; + MessageDef *msg = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_REGISTER_REQ); + e1ap_net_config_t *e1ap_nc = &E1AP_REGISTER_REQ(msg).net_config; + *e1ap_nc = conf; + itti_send_msg_to_task(TASK_CUCP_E1, 0, msg); +} + +/* @brief Setup CU-UP connection and retrieve parameters for further + * connection. + * + * Waits for E1 Setup Request, sends response, and returns SCTP association ID + * and NSSAI used by this CU-UP. + */ +static void setup_cuup(sctp_assoc_t *assoc_id, e1ap_nssai_t *nssai) +{ + /* Wait for E1AP setup request, store CU-UP info */ + MessageDef *itti_req = NULL; + itti_receive_msg(TASK_RRC_GNB, &itti_req); + MessagesIds id = ITTI_MSG_ID(itti_req); + AssertFatal(id == E1AP_SETUP_REQ, "expected E1AP setup request, received %s instead\n", itti_get_message_name(id)); + *assoc_id = itti_req->ittiMsgHeader.originInstance; + e1ap_setup_req_t *req = &E1AP_SETUP_REQ(itti_req); + long t_id = req->transac_id; + DevAssert(req->supported_plmns = 1); + DevAssert(req->plmn[0].slice != NULL); + *nssai = *req->plmn[0].slice; + DevAssert(assoc_id != 0); + itti_free(TASK_GNB_APP, itti_req); + + // acknowledge the E1 setup request with a response + MessageDef *itti_resp = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_SETUP_RESP); + itti_resp->ittiMsgHeader.originInstance = *assoc_id; + e1ap_setup_resp_t *resp = &E1AP_SETUP_RESP(itti_resp); + resp->transac_id = t_id; + itti_send_msg_to_task(TASK_CUCP_E1, 0, itti_resp); +} + +/* @brief get E1AP bearer setup request based on parameters */ +static e1ap_bearer_setup_req_t get_breq(uint32_t ue_id, long pdu_id, long drb_id, const e1ap_nssai_t *nssai, const UP_TL_information_t *tnl) +{ + e1ap_bearer_setup_req_t bearer_req = { + .gNB_cu_cp_ue_id = ue_id, + .secInfo.cipheringAlgorithm = 0, /* no ciphering/NEA0 */ + .secInfo.integrityProtectionAlgorithm = 0, /* no integrity/NIA0 */ + /* encryptionKey not needed */ + /* integrityProtectionKey not needed */ + .numPDUSessions = 1, + }; + pdu_session_to_setup_t *pdu = &bearer_req.pduSession[0]; + pdu->sessionId = pdu_id; + pdu->nssai = *nssai; + pdu->securityIndication.integrityProtectionIndication = SECURITY_NOT_NEEDED; + pdu->securityIndication.confidentialityProtectionIndication = SECURITY_NOT_NEEDED; + pdu->UP_TL_information = *tnl; + pdu->numDRB2Setup = 1; + DRB_nGRAN_to_setup_t *drb = &pdu->DRBnGRanList[0]; + drb->id = drb_id; + drb->numQosFlow2Setup = 1; + drb->numCellGroups = 1; + + return bearer_req; +} + +/* @brief check for packet loss by verifing the sequence number. Can handle + * packet losses and reordering. + * @param payload the sequence number of the current packet + * @param expected the expected sequence number + * @param [inout] pointer to number of lost packets, which will be updated by + * the difference between payload and expected. + * @return the next expected packet number. */ +static uint32_t estimate_packetloss(const uint32_t payload, uint32_t expected, uint32_t *lost) +{ + if (payload == expected) { + /* all good */ + return expected + 1; + } else if (payload > expected) { + /* there was packet loss, assume all packets in between have been lost */ + uint32_t diff = payload - expected; + *lost += diff; + LOG_W(GNB_APP, "packet loss: expected %d received %d diff %d (total loss %d)\n", expected, payload, diff, *lost); + return payload + 1; + } else { /* payload < expected */ + /* reordered: we assumed current packet to be lost, so reduce the + * loss for this packet as it arrived in the end. */ + *lost -= 1; + LOG_W(GNB_APP, "packet reordering occurred: expected %d received %d\n", expected, payload); + return expected; /* we still wait for the next packet */ + } +} + +static struct ue_stat { + uint32_t count; + uint32_t lost; + uint64_t received; +} ue_stat[10][2] = {0}; +/* @brief ulReceived callback for F1-U + * + * Checks for packet loss by verifying that the first 4 bytes are corresponding + * to a continuous 4-byte (word) enumeration. */ +static bool recv_ng(protocol_ctxt_t *ctxt, + const ue_id_t ue_id, + const srb_flag_t flag, + const rb_id_t rb, + const mui_t mui, + const confirm_t confirm, + const sdu_size_t size, + unsigned char *const buf, + const pdcp_transmission_mode_t modeP, + const uint32_t *sourceL2Id, + const uint32_t *destinationL2Id, + const uint8_t qfi, + const bool rqi, + const int pdusession_id) +{ + uint32_t *payload = (uint32_t *) buf; + DevAssert(size % 4 == 0); + struct ue_stat *s = &ue_stat[ue_id][UL]; + s->count = estimate_packetloss(*payload, s->count, &s->lost); + s->received += size; + + return true; +} + +/* @brief Set up the NG-U (north-bound) GTP tunnel, and inform CU-UP via E1 */ +static up_params_t setup_cuup_ue_ng(sctp_assoc_t assoc_id, const e1ap_nssai_t *nssai, uint32_t ue_id, instance_t gtp_inst, const char *ip, uint16_t port) +{ + long pdu_id = 1; + long drb_id = pdu_id + 3; + in_addr_t addr_lo; + inet_pton(AF_INET, ip, &addr_lo); + + /* create the local tunnel (i.e., as if we were UPF) */ + transport_layer_addr_t null_addr = {.length = 32}; + teid_t teid_lo = newGtpuCreateTunnel(gtp_inst, ue_id, pdu_id, pdu_id, -1, -1, null_addr, port, NULL, recv_ng); + UP_TL_information_t tnl = {.teId = teid_lo}; + memcpy(&tnl.tlAddress, &addr_lo, 4); + + /* Create a new bearer */ + MessageDef *itti_breq = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_BEARER_CONTEXT_SETUP_REQ); + itti_breq->ittiMsgHeader.originInstance = assoc_id; + e1ap_bearer_setup_req_t *breq = &E1AP_BEARER_CONTEXT_SETUP_REQ(itti_breq); + *breq = get_breq(ue_id, pdu_id, drb_id, nssai, &tnl); + itti_send_msg_to_task(TASK_CUCP_E1, 0, itti_breq); + + /* Wait for the ack */ + MessageDef *itti_bresp; + itti_receive_msg(TASK_RRC_GNB, &itti_bresp); + MessagesIds id = ITTI_MSG_ID(itti_bresp); + AssertFatal(id == E1AP_BEARER_CONTEXT_SETUP_RESP, "expected E1AP bearer context setup response, received %s instead\n", itti_get_message_name(id)); + DevAssert(assoc_id == itti_bresp->ittiMsgHeader.originInstance); + e1ap_bearer_setup_resp_t *bresp = &E1AP_BEARER_CONTEXT_SETUP_RESP(itti_bresp); + DevAssert(bresp->gNB_cu_cp_ue_id == ue_id); + DevAssert(bresp->gNB_cu_up_ue_id == ue_id); + DevAssert(bresp->numPDUSessions == 1); + teid_t teid_rm = bresp->pduSession[0].tl_info.teId; + in_addr_t addr_rm; + memcpy(&addr_rm, &bresp->pduSession[0].tl_info.tlAddress, 4); + DevAssert(bresp->pduSession[0].numDRBSetup == 1); + DRB_nGRAN_setup_t *drb = &bresp->pduSession[0].DRBnGRanList[0]; + DevAssert(drb->id == drb_id); + DevAssert(drb->numUpParam == 1); + up_params_t f1_up = drb->UpParamList[0]; + itti_free(TASK_GNB_APP, itti_bresp); + + /* print diagnostics */ + char ip_lo[32] = {0}; + inet_ntop(AF_INET, &addr_lo, ip_lo, sizeof(ip_lo)); + char ip_rm[32] = {0}; + inet_ntop(AF_INET, &addr_rm, ip_rm, sizeof(ip_rm)); + GtpuUpdateTunnelOutgoingAddressAndTeid(gtp_inst, ue_id, pdu_id, addr_rm, teid_rm); + LOG_I(GNB_APP, "CU-UP created NG-U, local %s/%x remote %s/%x (port %d)\n", ip_lo, teid_lo, ip_rm, teid_rm, port); + + inet_ntop(AF_INET, &f1_up.tl_info.tlAddress, ip_rm, sizeof(ip_rm)); + LOG_I(GNB_APP, "CU-UP created F1-U, remove %s/%x\n", ip_rm, f1_up.tl_info.teId); + return f1_up; +} + +/* @brief get E1AP bearer modification request based on parameters */ +static e1ap_bearer_mod_req_t get_bmod(uint32_t ue_id, long pdu_id, up_params_t up) +{ + e1ap_bearer_mod_req_t bearer_mod = { + .gNB_cu_cp_ue_id = ue_id, + .gNB_cu_up_ue_id = ue_id, + .numPDUSessionsMod = 1, + }; + pdu_session_to_mod_t *pdum = &bearer_mod.pduSessionMod[0]; + pdum->sessionId = pdu_id; + pdum->numDRB2Modify = 1; + DRB_nGRAN_to_mod_t *drb = &pdum->DRBnGRanModList[0]; + drb->id = 4; + drb->numDlUpParam = 1; + drb->DlUpParamList[0] = up; + + return bearer_mod; +} + +/* @brief Received callback for F1-U + * + * Checks for packet loss by verifying that the first 4 bytes are corresponding + * to a continuous 4-byte (word) enumeration. */ +static bool recv_f1(protocol_ctxt_t *ctxt, + const srb_flag_t flag, + const rb_id_t rb, + const mui_t mui, + const confirm_t confirm, + const sdu_size_t size, + unsigned char *const buf, + const pdcp_transmission_mode_t modeP, + const uint32_t *sourceL2Id, + const uint32_t *destinationL2Id) +{ + int skip_bytes = 2; + uint32_t *payload = (uint32_t *) &buf[skip_bytes]; + DevAssert((size - skip_bytes) % 4 == 0); + int ue_id = ctxt->rntiMaybeUEid; + struct ue_stat *s = &ue_stat[ue_id][DL]; + s->count = estimate_packetloss(*payload, s->count, &s->lost); + s->received += size - skip_bytes; + + return true; +} + +/* @brief Set up the F1-U (south-bound) GTP tunnel, and inform CU-UP via E1 */ +static void setup_cuup_ue_f1(sctp_assoc_t assoc_id, uint32_t ue_id, instance_t gtp_inst, const char *ip, uint16_t port, up_params_t rm) +{ + int pdu_id = 1; + int drb_id = pdu_id + 3; + + /* create tunnel (i.e., as if we were DU) */ + transport_layer_addr_t addr = {.length = 32}; + memcpy(addr.buffer, &rm.tl_info.tlAddress, 4); + teid_t teid_lo = newGtpuCreateTunnel(gtp_inst, ue_id, drb_id, drb_id, rm.tl_info.teId, -1, addr, port, recv_f1, NULL); + + up_params_t lo = {.tl_info.teId = teid_lo, .cell_group_id = rm.cell_group_id}; + inet_pton(AF_INET, ip, &lo.tl_info.tlAddress); + + /* modify existing tunnel via E1 to pass in F1-U TEID */ + MessageDef *itti_bmod = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_BEARER_CONTEXT_MODIFICATION_REQ); + itti_bmod->ittiMsgHeader.originInstance = assoc_id; + e1ap_bearer_mod_req_t *bmod = &E1AP_BEARER_CONTEXT_MODIFICATION_REQ(itti_bmod); + *bmod = get_bmod(ue_id, pdu_id, lo); + itti_send_msg_to_task(TASK_CUCP_E1, 0, itti_bmod); + + /* Wait for the ack */ + MessageDef *itti_bmodr; + itti_receive_msg(TASK_RRC_GNB, &itti_bmodr); + MessagesIds id = ITTI_MSG_ID(itti_bmodr); + AssertFatal(id == E1AP_BEARER_CONTEXT_MODIFICATION_RESP, "expected E1AP bearer context modification response, received %s instead\n", itti_get_message_name(id)); + //DevAssert(assoc_id == itti_bmodr->ittiMsgHeader.originInstance); + itti_free(TASK_GNB_APP, itti_bmodr); + + /* print diagnostics */ + char ip_lo[32] = {0}; + inet_ntop(AF_INET, &lo.tl_info.tlAddress, ip_lo, sizeof(ip_lo)); + char ip_rm[32] = {0}; + inet_ntop(AF_INET, &rm.tl_info.tlAddress, ip_rm, sizeof(ip_rm)); + LOG_I(GNB_APP, "CU-UP created F1-U, local %s/%x remote %s/%x (port %d)\n", ip_lo, lo.tl_info.teId, ip_rm, rm.tl_info.teId, port); +} + +/* @brief release the UE over E1, and delete corresponding GTP tunnels */ +static void e1_release_ue(sctp_assoc_t assoc_id, uint32_t ue_id, instance_t f1_inst, instance_t ng_inst) +{ + MessageDef *itti_cmd = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_BEARER_CONTEXT_RELEASE_CMD); + itti_cmd->ittiMsgHeader.originInstance = assoc_id; + e1ap_bearer_release_cmd_t *cmd = &E1AP_BEARER_CONTEXT_RELEASE_CMD(itti_cmd); + cmd->gNB_cu_cp_ue_id = cmd->gNB_cu_up_ue_id = ue_id; + cmd->cause.type = E1AP_CAUSE_RADIO_NETWORK; + cmd->cause.value = E1AP_RADIO_CAUSE_NORMAL_RELEASE; + itti_send_msg_to_task(TASK_CUCP_E1, 0, itti_cmd); + + /* Wait for the ack */ + MessageDef *itti_cplt; + itti_receive_msg(TASK_RRC_GNB, &itti_cplt); + MessagesIds id = ITTI_MSG_ID(itti_cplt); + AssertFatal(id == E1AP_BEARER_CONTEXT_RELEASE_CPLT, "expected E1AP bearer context release complete, received %s instead\n", itti_get_message_name(id)); + itti_free(TASK_GNB_APP, itti_cplt); + + newGtpuDeleteAllTunnels(f1_inst, ue_id); + newGtpuDeleteAllTunnels(ng_inst, ue_id); +} + +/* @brief initialize GTP interface for IP:port */ +static instance_t init_gtp(const char *ip, uint16_t port) +{ + openAddr_t iface = {0}; + DevAssert(strlen(ip) <= sizeof(iface.originHost)); + strcpy(iface.originHost, ip); + snprintf(iface.originService, sizeof(iface.originService), "%d", port); + snprintf(iface.destinationService, sizeof(iface.destinationService), "%d", port); + return gtpv1Init(iface); +} + +/* @brief calculate time difference of two struct timespec */ +static float time_diff(struct timespec s, struct timespec e) +{ + //printf("start %ld.%ld end %ld.%ld\n", s.tv_sec, s.tv_nsec, e.tv_sec, e.tv_nsec); + float dur = e.tv_sec - s.tv_sec + (e.tv_nsec - s.tv_nsec) / 1000000000.f; + //printf("duration %f s %f us\n", dur, dur * 1000000.f); + return dur * 1000000.f; +} + +enum pdcp_len {PDCP_SN_LEN_12, PDCP_SN_LEN_18}; +/* @brief generate hardcoded PDCP header in case of UL transmission */ +static void write_pdcp_header(uint32_t sn, enum pdcp_len len, uint8_t *buf) +{ + DevAssert(len == PDCP_SN_LEN_12); + /* see nr_pdcp_entity_process_sdu() or 38.323 sec 6.2.2.2 */ + buf[0] = 0x80 | ((sn >> 8) & 0xf); + buf[1] = sn & 0xff; + //printf("%02x %02x\n", buf[0], buf[1]); +} + +/* @brief the configuration for a thread to send GTP traffic */ +struct thr_data { + /// pthread ID to join this thread + pthread_t t; + + /* in */ + /// GTP instance to send on + instance_t inst; + /// UE ID to be used + ue_id_t ue_id; + /// flag to decide if PDCP/SDAP header is to be prepended + bool is_ul; + /// length of data packet + unsigned int data_len; + /// target throughput to reach + uint64_t throughput; + /// duration of the data traffic to last + unsigned int duration; + + /* out */ + /// exact measured transmission time + float total_time; + /// exact measured data that has been sent + uint64_t total_data; +}; +/* @brief Main traffic generation/transmission function. + * + * The function calculates the amount of packets to be sent during each TTI + * (one millisecond) (as a float, to send the exact amount of packets). It then + * enters a loop, in which (1) it sends the packets of the current TTI, possibly + * including additional packets if they could not be sent in the last TTI, (2) + * measure the time since the last TTI boundary, and sleep, if necessary, to + * adjust to the exact throughput (3) update for additional packets to send if + * the last TTI took too long. The last point ensure that we send exactly an + * exact amount of data, possibly taking longer than specified. + * + * @param v pointer to generation function of type `struct thr_data`. + * @return always NULL */ +static void *sender_thread(void *v) +{ + struct thr_data *d = v; + + size_t oh = d->is_ul ? 3 : 0; + size_t packet_len = d->data_len + oh; + uint8_t buf[packet_len]; + memset(buf, 0, packet_len); + uint32_t *payload = (uint32_t *)(buf + oh); + uint64_t to_send = (float)d->throughput / 8.0 * d->duration; + const float pps_1ms = (float) d->throughput / 8.0 / d->data_len / 1000; + float pps_1ms_nextwin = pps_1ms; + int bearer = d->is_ul ? 4 : 1; + + + //printf("sending %ld Mbps for %ld s => data %ld, %.3f pps\n", (float) d->throughput / 1000000.0, d->duration, to_send, pps_1ms); + + int i_1ms = 0; + struct timespec start; + clock_gettime(CLOCK_MONOTONIC, &start); + size_t packet_count = 0; + while (true) { + int pps_now = pps_1ms_nextwin; + pps_1ms_nextwin = pps_1ms + pps_1ms_nextwin - (float) pps_now; + //printf("this window send %d packets next window %.3f (pps_1ms %.3f)\n", pps_now, pps_1ms_nextwin, pps_1ms); + for (uint32_t i = 0; i < pps_now; ++i) { + if (d->is_ul) + write_pdcp_header(packet_count, PDCP_SN_LEN_12, buf); + *payload = packet_count++; + gtpv1uSendDirect(d->inst, d->ue_id, bearer, (uint8_t *)buf, packet_len, false, false); + d->total_data += d->data_len; + } + + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + float diff_us = time_diff(start, t); + int us_passed = diff_us - i_1ms*1000; + int iter_advance = us_passed / 1000 + 1; + int wait_usec = 1000 - us_passed; + //printf("diff %.3fus i_1ms %d us_passed %d iter %d wait %d (%.3f %%)\n", diff_us, i_1ms, us_passed, iter_advance, wait_usec, 100.0 * d->total_data / to_send); + DevAssert(iter_advance > 0); + if (wait_usec > 0) + usleep(wait_usec); + + if (iter_advance > 1) /* we have to send more in the next window */ + pps_1ms_nextwin += (iter_advance - 1) * pps_1ms_nextwin; + i_1ms += iter_advance; + if (d->total_data >= to_send) + break; + } + struct timespec end; + clock_gettime(CLOCK_MONOTONIC, &end); + d->total_time = time_diff(start, end) / 1000000.f; + return NULL; +} + +/* @brief print statistics of a UE (amount of data, achieved rate, if data was + * lost). */ +static bool print_stats(const char *pref, int ue_id, uint64_t total_data, float duration, const struct ue_stat *s) +{ + float tthr = 8.0 * total_data / duration / 1000000.f; + float rthr = 8.0 * s->received / duration / 1000000.f; + LOG_I(GNB_APP, "%d %s sent %ld in %.3f s received %ld bytes => rates [Mbps] transmit %.3f receive %.3f\n", + ue_id, + pref, + total_data, + duration, + s->received, + tthr, + rthr); + if (total_data == s->received) { + DevAssert(s->lost == 0); + LOG_A(GNB_APP, "%d %s received all data sent\n", ue_id, pref); + return true; + } else { + uint64_t m = total_data - s->received; + LOG_W(GNB_APP, "%d %s warning: missing %ld bytes (%.3f %%, %d packets)\n", ue_id, pref, m, 100.f * m / total_data, s->lost); + return false; + } +} + +/* @brief start a thread if specified through template, and in direction of + * given GTP bearer. + * @param template specifies if thread is to be started (throughput and + * duration are positive). If so, the template data is stored inside the + * thread. + * @param thr place to store thread data + * @param b the GTP bearer to use */ +static struct thr_data *start_thread(const struct thr_data *template, struct thr_data *thr, instance_t inst, ue_id_t ue_id) +{ + if (template->throughput == 0 || template->duration == 0) + return NULL; + + *thr = *template; + thr->inst = inst; + thr->ue_id = ue_id; + pthread_create(&thr->t, NULL, sender_thread, thr); + return thr; +} + +/* @brief print progress of a thread specified in thr_data, and prepend DL/UL + * based on dir. data access to thread data is not private, but we only read, + * so don't care. */ +static bool print_progress(const int dir, const struct thr_data *obs) +{ + if (!obs) + return false; + + uint64_t target = (uint64_t) obs->throughput / 8 * obs->duration; + float progress = (float)obs->total_data / target; + printf(" %s sent %3.f %%", dir == DL ? "DL" : "UL", 100.0 * progress); + return progress < 0.99; +} + +int main(int argc, char *argv[]) +{ + if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == NULL) { + exit_fun("[SOFTMODEM] Error, configuration module init failed\n"); + } + logInit(); + itti_init(TASK_MAX, tasks_info); + + unsigned int n_ue; + unsigned int duration_s; + unsigned int packet_size; + uint64_t target_rate_dl; + uint64_t target_rate_ul; + char *ng_ip = malloc_or_fail(100); + uint16_t ng_port; + char *f1_ip = malloc_or_fail(100); + uint16_t f1_port; + + paramdef_t param_def[] = { + {"n", "number of UEs\n", 0, .uptr = &n_ue, .defuintval = 1, TYPE_UINT}, + {"t", "duration of test (s)\n", 0, .uptr = &duration_s, .defuintval = 10, TYPE_UINT}, + {"p", "packet size\n", 0, .uptr = &packet_size, .defuintval = 1400, TYPE_UINT}, + {"d", "DL target rate (Mbps, per UE)\n", 0, .u64ptr = &target_rate_dl, .defint64val = 60, TYPE_UINT64}, + {"u", "UL target rate (Mbps, per UE)\n", 0, .u64ptr = &target_rate_ul, .defint64val = 60, TYPE_UINT64}, + {"ng-ip", "Tester NG-U IP\n", 0, .strptr = &ng_ip, .defstrval = "127.0.0.1", TYPE_STRING, 100}, + {"ng-port", "Tester NG-U port\n", 0, .u16ptr = &ng_port, .defuintval = 2152, TYPE_UINT16}, + {"f1-ip", "Tester F1-U IP\n", 0, .strptr = &f1_ip, .defstrval = "127.0.0.1", TYPE_STRING, 100}, + {"f1-port", "Tester F1-U port\n", 0, .u16ptr = &f1_port, .defuintval = 2153, TYPE_UINT16}, + }; + config_get(uniqCfg, param_def, sizeofArray(param_def), NULL); + // on command line, target rate is in Mbps, convert to bps + target_rate_dl *= 1000000LL; + target_rate_ul *= 1000000LL; + LOG_I(GNB_APP, + "CU-UP load tester: running test for\n" + "- Number of UEs %d\n" + "- Duration %d seconds\n" + "- Packet size %d\n" + "- Target DL rate %.3f Mbps (per UE)\n" + "- Target UL rate %.3f Mbps (per UE)\n" + "- Tester endpoint for CU-UP NG-U: %s:%d\n" + "- Tester endpoint for CU-UP F1-U: %s:%d\n", + n_ue, duration_s, packet_size, + (float) target_rate_dl / 1e6, + (float) target_rate_ul / 1e6, + ng_ip, ng_port, f1_ip, f1_port); + + struct thr_data dl_template = {.is_ul = false, .data_len = packet_size, .throughput = target_rate_dl, .duration = duration_s}; + struct thr_data ul_template = {.is_ul = true, .data_len = packet_size, .throughput = target_rate_ul, .duration = duration_s}; + + /* init GTP for NG-U comm with CU-UP */ + instance_t ng_inst = init_gtp(ng_ip, ng_port); + AssertFatal(ng_inst > 0, "could not initialize GTP interface on %s:%d towards CU-UP NG-U\n", ng_ip, ng_port); + + /* init GTP for F1-U comm with CU-UP */ + instance_t f1_inst = init_gtp(f1_ip, f1_port); + AssertFatal(f1_inst > 0, "could not initialize GTP interface on %s:%d towards CU-UP F1-U\n", f1_ip, f1_port); + + /* Start relevant services: SCTP, CUCP_E1: E1; GTP not actively used; RRC to + * register the queue so we can receive what E1 sends us back */ + int rc; + rc = itti_create_task(TASK_SCTP, sctp_eNB_task, NULL); + AssertFatal(rc >= 0, "Create task for SCTP failed\n"); + rc = itti_create_task(TASK_CUCP_E1, E1AP_CUCP_task, NULL); + AssertFatal(rc >= 0, "Create task for CUUP E1 failed\n"); + rc = itti_create_task(TASK_GTPV1_U, gtpv1uTask, NULL); + AssertFatal(rc >= 0, "Create task for GTPV1U failed\n"); + rc = itti_create_task(TASK_RRC_GNB, cuup_tester_rrc, NULL); + AssertFatal(rc >= 0, "Create task for RRC failed\n"); + init_e1(); + + /* Set up CU-UP connection: waits for CU-UP-initiated SCTP+E1 setup request */ + sctp_assoc_t assoc_id; + e1ap_nssai_t nssai; + setup_cuup(&assoc_id, &nssai); + + /* Set up UE contexts in CU-UP. the below two calls correspond to + * - setup_cuup_ue_ng(): E1 bearer context setup <=> completely sets up NG-U + * in CU-UP, gets TEID for F1-U UL + * - setup_cuup_ue_f1(): E1 bearer context modification <=> send TEID for + * F1-U DL (as if we had talked to the DU in the middle) */ + for (int ue = 0; ue < n_ue; ++ue) { + up_params_t f1_up = setup_cuup_ue_ng(assoc_id, &nssai, ue, ng_inst, ng_ip, ng_port); + setup_cuup_ue_f1(assoc_id, ue, f1_inst, f1_ip, f1_port, f1_up); + } + + usleep(1000); + + /* for each UE, start DL&UL data streams in dedicated thread, if configured */ + struct thr_data thrd[n_ue][2]; + memset(thrd, 0, sizeof(thrd)); + struct thr_data *obs_dl = NULL, *obs_ul = NULL; + for (int ue = 0; ue < n_ue; ++ue) { + obs_dl = start_thread(&dl_template, &thrd[ue][DL], ng_inst, ue); + obs_ul = start_thread(&ul_template, &thrd[ue][UL], f1_inst, ue); + } + /* show progress of one thread (all take the same time) */ + while (obs_dl || obs_ul) { + bool dlrunning = print_progress(DL, obs_dl); + bool ulrunning = print_progress(UL, obs_ul); + printf("\r"); + fflush(stdout); + if (!dlrunning && !ulrunning) + break; + usleep(100000); + } + /* join threads, if applicable */ + for (int ue = 0; ue < n_ue; ++ue) { + struct thr_data *dl = &thrd[ue][DL]; + if (dl->t) + pthread_join(dl->t, NULL); + struct thr_data *ul = &thrd[ue][UL]; + if (ul->t) + pthread_join(ul->t, NULL); + } + + /* wait a bit, to ensure all data is received after having sent it above */ + sleep(1); + + for (int ue = 0; ue < n_ue; ++ue) { + e1_release_ue(assoc_id, ue, f1_inst, ng_inst); + } + + /* show stats of all threads */ + bool success = true; + for (int ue = 0; ue < n_ue; ++ue) { + struct thr_data *thrddl = &thrd[ue][DL]; + struct ue_stat *statdl = &ue_stat[ue][DL]; + if (thrddl->t) + if (!print_stats("DL", ue, thrddl->total_data, thrddl->total_time, statdl)) + success = false; + struct thr_data *thrdul = &thrd[ue][UL]; + struct ue_stat *statul = &ue_stat[ue][UL]; + if (thrdul->t) + if (!print_stats("UL", ue, thrdul->total_data, thrdul->total_time, statul)) + success = false; + } + + if (success) + LOG_A(GNB_APP, "test succeeded\n"); + else + LOG_W(GNB_APP, "TEST FAILED\n"); + return success ? 0 : 1; +} From 905fe3fb714688bbf5c430177fcd58c7ba6072b8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 3 Feb 2025 08:02:48 +0100 Subject: [PATCH 34/86] DU Buffer Report/DL Delivery Status: remove unused code --- openair2/COMMON/gtpv1_u_messages_def.h | 1 - openair2/COMMON/gtpv1_u_messages_types.h | 9 ---- openair3/ocp-gtpu/gtp_itf.cpp | 62 ------------------------ 3 files changed, 72 deletions(-) diff --git a/openair2/COMMON/gtpv1_u_messages_def.h b/openair2/COMMON/gtpv1_u_messages_def.h index 19001b883e..6fad3df938 100644 --- a/openair2/COMMON/gtpv1_u_messages_def.h +++ b/openair2/COMMON/gtpv1_u_messages_def.h @@ -25,4 +25,3 @@ MESSAGE_DEF(GTPV1U_ENB_DATA_FORWARDING_IND, MESSAGE_PRIORITY_MED, gtpv1u_enb_dat MESSAGE_DEF(GTPV1U_ENB_END_MARKER_REQ, MESSAGE_PRIORITY_MED, gtpv1u_enb_end_marker_req_t, Gtpv1uEndMarkerReq) MESSAGE_DEF(GTPV1U_ENB_END_MARKER_IND, MESSAGE_PRIORITY_MED, gtpv1u_enb_end_marker_ind_t, Gtpv1uEndMarkerInd) MESSAGE_DEF(GTPV1U_REQ, MESSAGE_PRIORITY_MED, Gtpv1uReq, gtpv1uReq) -MESSAGE_DEF(GTPV1U_DU_BUFFER_REPORT_REQ, MESSAGE_PRIORITY_MED, gtpv1u_DU_buffer_report_req_t, NRGtpv1uBufferReportReq) diff --git a/openair2/COMMON/gtpv1_u_messages_types.h b/openair2/COMMON/gtpv1_u_messages_types.h index 30471c9c4f..05ca7d7a74 100644 --- a/openair2/COMMON/gtpv1_u_messages_types.h +++ b/openair2/COMMON/gtpv1_u_messages_types.h @@ -37,8 +37,6 @@ #define GTPV1U_REQ(mSGpTR) (mSGpTR)->ittiMsg.gtpv1uReq -#define GTPV1U_DU_BUFFER_REPORT_REQ(mSGpTR) (mSGpTR)->ittiMsg.NRGtpv1uBufferReportReq - #define GTPV1U_ALL_TUNNELS_TEID (teid_t)0xFFFFFFFF typedef struct gtpv1u_enb_create_x2u_tunnel_req_s { @@ -188,11 +186,4 @@ typedef struct gtpv1u_gnb_delete_tunnel_resp_s { teid_t gnb_NGu_teid; ///< local NGU Tunnel Endpoint Identifier to be deleted } gtpv1u_gnb_delete_tunnel_resp_t; - -typedef struct gtpv1u_DU_buffer_report_req_s { - uint32_t buffer_availability; - ue_id_t ue_id; - int pdusession_id; -} gtpv1u_DU_buffer_report_req_t; - #endif /* GTPV1_U_MESSAGES_TYPES_H_ */ diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 58be5ed638..174f75d89b 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -369,63 +369,6 @@ void gtpv1uSendDirect(instance_t instance, } } -static void fillDlDeliveryStatusReport(extensionHeader_t *extensionHeader, uint32_t RLC_buffer_availability, uint32_t NR_PDCP_PDU_SN){ - - extensionHeader->buffer[0] = (1+sizeof(DlDataDeliveryStatus_flagsT)+(NR_PDCP_PDU_SN>0?3:0)+(NR_PDCP_PDU_SN>0?1:0)+1)/4; - DlDataDeliveryStatus_flagsT DlDataDeliveryStatus; - DlDataDeliveryStatus.deliveredPdcpSn = 0; - DlDataDeliveryStatus.transmittedPdcpSn= NR_PDCP_PDU_SN>0?1:0; - DlDataDeliveryStatus.pduType = 1; - DlDataDeliveryStatus.drbBufferSize = htonl(RLC_buffer_availability); - memcpy(extensionHeader->buffer+1, &DlDataDeliveryStatus, sizeof(DlDataDeliveryStatus_flagsT)); - uint8_t offset = sizeof(DlDataDeliveryStatus_flagsT)+1; - - if(NR_PDCP_PDU_SN>0){ - extensionHeader->buffer[offset] = (NR_PDCP_PDU_SN >> 16) & 0xff; - extensionHeader->buffer[offset+1] = (NR_PDCP_PDU_SN >> 8) & 0xff; - extensionHeader->buffer[offset+2] = NR_PDCP_PDU_SN & 0xff; - LOG_D(GTPU, "Octets reporting NR_PDCP_PDU_SN, extensionHeader->buffer[offset]: %u, extensionHeader->buffer[offset+1]:%u, extensionHeader->buffer[offset+2]:%u \n", extensionHeader->buffer[offset], extensionHeader->buffer[offset+1],extensionHeader->buffer[offset+2]); - extensionHeader->buffer[offset+3] = 0x00; //Padding octet - offset = offset+3; - } - extensionHeader->buffer[offset] = 0x00; //No more extension headers - /*Total size of DDD_status PDU = size of mandatory part + - * 3 octets for highest transmitted/delivered PDCP SN + - * 1 octet for padding + 1 octet for next extension header type, - * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ - extensionHeader->length = 1+sizeof(DlDataDeliveryStatus_flagsT)+ - (NR_PDCP_PDU_SN>0?3:0)+ - (NR_PDCP_PDU_SN>0?1:0)+1; -} - -static void gtpv1uSendDlDeliveryStatus(instance_t instance, gtpv1u_DU_buffer_report_req_t *req){ - ue_id_t ue_id=req->ue_id; - int bearer_id=req->pdusession_id; - pthread_mutex_lock(&globGtp.gtp_lock); - getInstRetVoid(compatInst(instance)); - getUeRetVoid(inst, ue_id); - - auto ptr2=ptrUe->second.bearers.find(bearer_id); - - if ( ptr2 == ptrUe->second.bearers.end() ) { - LOG_D(GTPU,"GTP-U instance: %ld sending a packet to a non existant UE ID:RAB: %lu/%x\n", instance, ue_id, bearer_id); - pthread_mutex_unlock(&globGtp.gtp_lock); - return; - } - - extensionHeader_t *extensionHeader; - extensionHeader = (extensionHeader_t *) calloc(1, sizeof(extensionHeader_t)); - fillDlDeliveryStatusReport(extensionHeader, req->buffer_availability,0); - - LOG_I(GTPU,"[%ld] GTP-U sending DL Data Delivery status to UE ID:RAB:teid %lu/%x/%x, oldseq %d, oldnum %d\n", - instance, ue_id, bearer_id,ptr2->second.teid_outgoing, ptr2->second.seqNum,ptr2->second.npduNum ); - // copy to release the mutex - gtpv1u_bearer_t tmp=ptr2->second; - pthread_mutex_unlock(&globGtp.gtp_lock); - gtpv1uCreateAndSendMsg( - compatInst(instance), tmp.outgoing_ip_addr, tmp.outgoing_port, GTP_GPDU, tmp.teid_outgoing, NULL, 0, false, false, 0, 0, NR_RAN_CONTAINER, extensionHeader->buffer, extensionHeader->length); -} - static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_end_marker_req_t *req) { ue_id_t ue_id=req->rnti; @@ -1312,11 +1255,6 @@ void *gtpv1uTask(void *args) { switch (msgType) { // DATA TO BE SENT TO UDP - case GTPV1U_DU_BUFFER_REPORT_REQ:{ - gtpv1uSendDlDeliveryStatus(compatInst(myInstance), >PV1U_DU_BUFFER_REPORT_REQ(message_p)); - } - break; - case TERMINATE_MESSAGE: break; From d1a03c993c904e59b96369d9255f8c3677b80fd2 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 3 Feb 2025 09:28:26 +0100 Subject: [PATCH 35/86] Reformat gtp_itf.cpp --- openair3/ocp-gtpu/gtp_itf.cpp | 854 ++++++++++++++++++---------------- 1 file changed, 442 insertions(+), 412 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 174f75d89b..50c46b809f 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -25,85 +25,85 @@ extern "C" { #pragma pack(1) typedef struct Gtpv1uMsgHeader { - uint8_t PN:1; - uint8_t S:1; - uint8_t E:1; - uint8_t spare:1; - uint8_t PT:1; - uint8_t version:3; + uint8_t PN: 1; + uint8_t S: 1; + uint8_t E: 1; + uint8_t spare: 1; + uint8_t PT: 1; + uint8_t version: 3; uint8_t msgType; uint16_t msgLength; teid_t teid; } __attribute__((packed)) Gtpv1uMsgHeaderT; -//TS 38.425, Figure 5.5.2.2-1 +// TS 38.425, Figure 5.5.2.2-1 typedef struct DlDataDeliveryStatus_flags { - uint8_t LPR:1; //Lost packet report - uint8_t FFI:1; //Final Frame Ind - uint8_t deliveredPdcpSn:1; //Highest Delivered NR PDCP SN Ind - uint8_t transmittedPdcpSn:1; //Highest Transmitted NR PDCP SN Ind - uint8_t pduType:4; //PDU type - uint8_t CR:1; //Cause Report - uint8_t deliveredReTxPdcpSn:1; //Delivered retransmitted NR PDCP SN Ind - uint8_t reTxPdcpSn:1; //Retransmitted NR PDCP SN Ind - uint8_t DRI:1; //Data Rate Indication - uint8_t deliveredPdcpSnRange:1; //Delivered NR PDCP SN Range Ind - uint8_t spare:3; - uint32_t drbBufferSize; //Desired buffer size for the data radio bearer + uint8_t LPR: 1; // Lost packet report + uint8_t FFI: 1; // Final Frame Ind + uint8_t deliveredPdcpSn: 1; // Highest Delivered NR PDCP SN Ind + uint8_t transmittedPdcpSn: 1; // Highest Transmitted NR PDCP SN Ind + uint8_t pduType: 4; // PDU type + uint8_t CR: 1; // Cause Report + uint8_t deliveredReTxPdcpSn: 1; // Delivered retransmitted NR PDCP SN Ind + uint8_t reTxPdcpSn: 1; // Retransmitted NR PDCP SN Ind + uint8_t DRI: 1; // Data Rate Indication + uint8_t deliveredPdcpSnRange: 1; // Delivered NR PDCP SN Range Ind + uint8_t spare: 3; + uint32_t drbBufferSize; // Desired buffer size for the data radio bearer } __attribute__((packed)) DlDataDeliveryStatus_flagsT; typedef struct Gtpv1uMsgHeaderOptFields { uint8_t seqNum1Oct; uint8_t seqNum2Oct; uint8_t NPDUNum; - uint8_t NextExtHeaderType; + uint8_t NextExtHeaderType; } __attribute__((packed)) Gtpv1uMsgHeaderOptFieldsT; #define DL_PDU_SESSION_INFORMATION 0 #define UL_PDU_SESSION_INFORMATION 1 - typedef struct PDUSessionContainer { - uint8_t spare:4; - uint8_t PDU_type:4; - uint8_t QFI:6; - uint8_t Reflective_QoS_activation:1; - uint8_t Paging_Policy_Indicator:1; +typedef struct PDUSessionContainer { + uint8_t spare: 4; + uint8_t PDU_type: 4; + uint8_t QFI: 6; + uint8_t Reflective_QoS_activation: 1; + uint8_t Paging_Policy_Indicator: 1; } __attribute__((packed)) PDUSessionContainerT; typedef struct Gtpv1uExtHeader { uint8_t ExtHeaderLen; PDUSessionContainerT pdusession_cntr; uint8_t NextExtHeaderType; -}__attribute__((packed)) Gtpv1uExtHeaderT; +} __attribute__((packed)) Gtpv1uExtHeaderT; #pragma pack() // TS 29.281, fig 5.2.1-3 -#define PDU_SESSION_CONTAINER (0x85) -#define NR_RAN_CONTAINER (0x84) +#define PDU_SESSION_CONTAINER (0x85) +#define NR_RAN_CONTAINER (0x84) // TS 29.281, 5.2.1 -#define EXT_HDR_LNTH_OCTET_UNITS (4) -#define NO_MORE_EXT_HDRS (0) +#define EXT_HDR_LNTH_OCTET_UNITS (4) +#define NO_MORE_EXT_HDRS (0) // TS 29.060, table 7.1 defines the possible message types // here are all the possible messages (3GPP R16) -#define GTP_ECHO_REQ (1) -#define GTP_ECHO_RSP (2) -#define GTP_ERROR_INDICATION (26) -#define GTP_SUPPORTED_EXTENSION_HEADER_INDICATION (31) -#define GTP_END_MARKER (254) -#define GTP_GPDU (255) +#define GTP_ECHO_REQ (1) +#define GTP_ECHO_RSP (2) +#define GTP_ERROR_INDICATION (26) +#define GTP_SUPPORTED_EXTENSION_HEADER_INDICATION (31) +#define GTP_END_MARKER (254) +#define GTP_GPDU (255) typedef struct gtpv1u_bearer_s { /* TEID used in dl and ul */ - teid_t teid_incoming; ///< eNB TEID - teid_t teid_outgoing; ///< Remote TEID - in_addr_t outgoing_ip_addr; + teid_t teid_incoming; ///< eNB TEID + teid_t teid_outgoing; ///< Remote TEID + in_addr_t outgoing_ip_addr; struct in6_addr outgoing_ip6_addr; - tcp_udp_port_t outgoing_port; - uint16_t seqNum; - uint8_t npduNum; + tcp_udp_port_t outgoing_port; + uint16_t seqNum; + uint8_t npduNum; int outgoing_qfi; } gtpv1u_bearer_t; @@ -131,24 +131,27 @@ class gtpEndPoint { // we use the same port number for source and destination address // this allow using non standard gtp port number (different from 2152) // and so, for example tu run 4G and 5G cores on one system - tcp_udp_port_t get_dstport() { + tcp_udp_port_t get_dstport() + { return (tcp_udp_port_t)atol(addr.destinationService); } }; class gtpEndPoints { public: - pthread_mutex_t gtp_lock=PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_t gtp_lock = PTHREAD_MUTEX_INITIALIZER; // the instance id will be the Linux socket handler, as this is uniq map instances; map te2ue_mapping; - gtpEndPoints() { + gtpEndPoints() + { unsigned int seed; fill_random(&seed, sizeof(seed)); srandom(seed); } - ~gtpEndPoints() { + ~gtpEndPoints() + { // automatically close all sockets on quit for (const auto &p : instances) close(p.first); @@ -158,7 +161,8 @@ class gtpEndPoints { static gtpEndPoints globGtp; // note TEid 0 is reserved for specific usage: echo req/resp, error and supported extensions -static teid_t gtpv1uNewTeid(void) { +static teid_t gtpv1uNewTeid(void) +{ #ifdef GTPV1U_LINEAR_TEID_ALLOCATION g_gtpv1u_teid = g_gtpv1u_teid + 1; return g_gtpv1u_teid; @@ -167,46 +171,45 @@ static teid_t gtpv1uNewTeid(void) { #endif } -instance_t legacyInstanceMapping=0; -#define compatInst(a) ((a)==0 || (a)==INSTANCE_DEFAULT ? legacyInstanceMapping:a) +instance_t legacyInstanceMapping = 0; +#define compatInst(a) ((a) == 0 || (a) == INSTANCE_DEFAULT ? legacyInstanceMapping : a) -#define getInstRetVoid(insT) \ - auto instChk=globGtp.instances.find(compatInst(insT)); \ - if (instChk == globGtp.instances.end()) { \ - LOG_E(GTPU,"try to get a gtp-u not existing output\n"); \ - pthread_mutex_unlock(&globGtp.gtp_lock); \ - return; \ - } \ - gtpEndPoint * inst=&instChk->second; - -#define getInstRetInt(insT) \ - auto instChk=globGtp.instances.find(compatInst(insT)); \ - if (instChk == globGtp.instances.end()) { \ - LOG_E(GTPU,"try to get a gtp-u not existing output\n"); \ - pthread_mutex_unlock(&globGtp.gtp_lock); \ - return GTPNOK; \ - } \ - gtpEndPoint * inst=&instChk->second; +#define getInstRetVoid(insT) \ + auto instChk = globGtp.instances.find(compatInst(insT)); \ + if (instChk == globGtp.instances.end()) { \ + LOG_E(GTPU, "try to get a gtp-u not existing output\n"); \ + pthread_mutex_unlock(&globGtp.gtp_lock); \ + return; \ + } \ + gtpEndPoint *inst = &instChk->second; +#define getInstRetInt(insT) \ + auto instChk = globGtp.instances.find(compatInst(insT)); \ + if (instChk == globGtp.instances.end()) { \ + LOG_E(GTPU, "try to get a gtp-u not existing output\n"); \ + pthread_mutex_unlock(&globGtp.gtp_lock); \ + return GTPNOK; \ + } \ + gtpEndPoint *inst = &instChk->second; -#define getUeRetVoid(insT, Ue) \ - auto ptrUe=insT->ue2te_mapping.find(Ue); \ - \ - if ( ptrUe==insT->ue2te_mapping.end() ) { \ +#define getUeRetVoid(insT, Ue) \ + auto ptrUe = insT->ue2te_mapping.find(Ue); \ + \ + if (ptrUe == insT->ue2te_mapping.end()) { \ LOG_E(GTPU, "[%ld] %s failed: while getting ue id %ld in hashtable ue_mapping\n", instance, __func__, ue_id); \ - pthread_mutex_unlock(&globGtp.gtp_lock); \ - return; \ + pthread_mutex_unlock(&globGtp.gtp_lock); \ + return; \ } - -#define getUeRetInt(insT, Ue) \ - auto ptrUe=insT->ue2te_mapping.find(Ue); \ - \ - if ( ptrUe==insT->ue2te_mapping.end() ) { \ + +#define getUeRetInt(insT, Ue) \ + auto ptrUe = insT->ue2te_mapping.find(Ue); \ + \ + if (ptrUe == insT->ue2te_mapping.end()) { \ LOG_E(GTPU, "[%ld] %s failed: while getting ue id %ld in hashtable ue_mapping\n", instance, __func__, ue_id); \ - pthread_mutex_unlock(&globGtp.gtp_lock); \ - return GTPNOK; \ + pthread_mutex_unlock(&globGtp.gtp_lock); \ + return GTPNOK; \ } - + #define HDR_MAX 256 // 256 is supposed to be larger than any gtp header static int gtpv1uCreateAndSendMsg(int h, uint32_t peerIp, @@ -221,29 +224,30 @@ static int gtpv1uCreateAndSendMsg(int h, int npduNum, int extHdrType, uint8_t *extensionHeader_buffer, - uint8_t extensionHeader_length) { + uint8_t extensionHeader_length) +{ LOG_D(GTPU, "Peer IP:%u peer port:%u outgoing teid:%u \n", peerIp, peerPort, teid); DevAssert(msgLen + HDR_MAX < 65536); // maximum size of UDP packet - uint8_t buffer[msgLen+HDR_MAX]; - uint8_t *curPtr=buffer; - Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)buffer ; + uint8_t buffer[msgLen + HDR_MAX]; + uint8_t *curPtr = buffer; + Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)buffer; // N should be 0 for us (it was used only in 2G and 3G) - msgHdr->PN=npduNumFlag; - msgHdr->S=seqNumFlag; + msgHdr->PN = npduNumFlag; + msgHdr->S = seqNumFlag; msgHdr->E = extHdrType != NO_MORE_EXT_HDRS; - msgHdr->spare=0; - //PT=0 is for GTP' TS 32.295 (charging) - msgHdr->PT=1; - msgHdr->version=1; - msgHdr->msgType=msgType; - msgHdr->teid=htonl(teid); + msgHdr->spare = 0; + // PT=0 is for GTP' TS 32.295 (charging) + msgHdr->PT = 1; + msgHdr->version = 1; + msgHdr->msgType = msgType; + msgHdr->teid = htonl(teid); - curPtr+=sizeof(Gtpv1uMsgHeaderT); + curPtr += sizeof(Gtpv1uMsgHeaderT); if (seqNumFlag || (extHdrType != NO_MORE_EXT_HDRS) || npduNumFlag) { *(uint16_t *)curPtr = seqNumFlag ? seqNum : 0x0000; - curPtr+=sizeof(uint16_t); + curPtr += sizeof(uint16_t); *(uint8_t *)curPtr = npduNumFlag ? npduNum : 0x00; curPtr++; *(uint8_t *)curPtr = extHdrType; @@ -255,7 +259,10 @@ static int gtpv1uCreateAndSendMsg(int h, if (extensionHeader_length > 0) { memcpy(curPtr, extensionHeader_buffer, extensionHeader_length); curPtr += extensionHeader_length; - LOG_D(GTPU, "Extension Header for DDD added. The length is: %d, extension header type is: %x \n", extensionHeader_length, *((uint8_t *)(buffer + 11))); + LOG_D(GTPU, + "Extension Header for DDD added. The length is: %d, extension header type is: %x \n", + extensionHeader_length, + *((uint8_t *)(buffer + 11))); extHdrType = extensionHeader_buffer[extensionHeader_length - 1]; LOG_D(GTPU, "Next extension header type is: %x \n", *((uint8_t *)(buffer + 11))); } else { @@ -263,28 +270,34 @@ static int gtpv1uCreateAndSendMsg(int h, } } - if (Msg!= NULL){ + if (Msg != NULL) { memcpy(curPtr, Msg, msgLen); - curPtr+=msgLen; + curPtr += msgLen; } - msgHdr->msgLength = htons(curPtr-(buffer+sizeof(Gtpv1uMsgHeaderT))); - AssertFatal(curPtr-(buffer+msgLen) < HDR_MAX, "fixed max size of all headers too short"); + msgHdr->msgLength = htons(curPtr - (buffer + sizeof(Gtpv1uMsgHeaderT))); + AssertFatal(curPtr - (buffer + msgLen) < HDR_MAX, "fixed max size of all headers too short"); // Fix me: add IPv6 support, using flag ipVersion - struct sockaddr_in to= {0}; - to.sin_family = AF_INET; - to.sin_port = htons(peerPort); - to.sin_addr.s_addr = peerIp ; - LOG_D(GTPU,"sending packet size: %ld to %s\n",curPtr-buffer, inet_ntoa(to.sin_addr) ); + struct sockaddr_in to = {0}; + to.sin_family = AF_INET; + to.sin_port = htons(peerPort); + to.sin_addr.s_addr = peerIp; + LOG_D(GTPU, "sending packet size: %ld to %s\n", curPtr - buffer, inet_ntoa(to.sin_addr)); int ret; - if ((ret=sendto(h, (void *)buffer, curPtr-buffer, 0,(struct sockaddr *)&to, sizeof(to) )) != curPtr-buffer ) { - LOG_E(GTPU, "[SD %d] Failed to send data to " IPV4_ADDR " on port %d, buffer size %lu, ret: %d, errno: %d\n", - h, IPV4_ADDR_FORMAT(peerIp), peerPort, curPtr-buffer, ret, errno); + if ((ret = sendto(h, (void *)buffer, curPtr - buffer, 0, (struct sockaddr *)&to, sizeof(to))) != curPtr - buffer) { + LOG_E(GTPU, + "[SD %d] Failed to send data to " IPV4_ADDR " on port %d, buffer size %lu, ret: %d, errno: %d\n", + h, + IPV4_ADDR_FORMAT(peerIp), + peerPort, + curPtr - buffer, + ret, + errno); return GTPNOK; } - return !GTPNOK; + return !GTPNOK; } void gtpv1uSendDirect(instance_t instance, @@ -371,99 +384,104 @@ void gtpv1uSendDirect(instance_t instance, static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_end_marker_req_t *req) { - ue_id_t ue_id=req->rnti; - int bearer_id=req->rab_id; + ue_id_t ue_id = req->rnti; + int bearer_id = req->rab_id; pthread_mutex_lock(&globGtp.gtp_lock); getInstRetVoid(compatInst(instance)); getUeRetVoid(inst, ue_id); - auto ptr2=ptrUe->second.bearers.find(bearer_id); + auto ptr2 = ptrUe->second.bearers.find(bearer_id); - if ( ptr2 == ptrUe->second.bearers.end() ) { - LOG_E(GTPU,"[%ld] GTP-U sending a packet to a non existant UE:RAB: %lx/%x\n", instance, ue_id, bearer_id); + if (ptr2 == ptrUe->second.bearers.end()) { + LOG_E(GTPU, "[%ld] GTP-U sending a packet to a non existant UE:RAB: %lx/%x\n", instance, ue_id, bearer_id); pthread_mutex_unlock(&globGtp.gtp_lock); return; } - LOG_D(GTPU,"[%ld] sending a end packet packet to UE:RAB:teid %lx/%x/%x\n", - instance, ue_id, bearer_id,ptr2->second.teid_outgoing); - gtpv1u_bearer_t tmp=ptr2->second; + LOG_D(GTPU, + "[%ld] sending a end packet packet to UE:RAB:teid %lx/%x/%x\n", + instance, + ue_id, + bearer_id, + ptr2->second.teid_outgoing); + gtpv1u_bearer_t tmp = ptr2->second; pthread_mutex_unlock(&globGtp.gtp_lock); - Gtpv1uMsgHeaderT msgHdr; + Gtpv1uMsgHeaderT msgHdr; // N should be 0 for us (it was used only in 2G and 3G) - msgHdr.PN=0; - msgHdr.S=0; - msgHdr.E=0; - msgHdr.spare=0; - //PT=0 is for GTP' TS 32.295 (charging) - msgHdr.PT=1; - msgHdr.version=1; - msgHdr.msgType=GTP_END_MARKER; - msgHdr.msgLength=htons(0); - msgHdr.teid=htonl(tmp.teid_outgoing); + msgHdr.PN = 0; + msgHdr.S = 0; + msgHdr.E = 0; + msgHdr.spare = 0; + // PT=0 is for GTP' TS 32.295 (charging) + msgHdr.PT = 1; + msgHdr.version = 1; + msgHdr.msgType = GTP_END_MARKER; + msgHdr.msgLength = htons(0); + msgHdr.teid = htonl(tmp.teid_outgoing); // Fix me: add IPv6 support, using flag ipVersion - static struct sockaddr_in to= {0}; - to.sin_family = AF_INET; - to.sin_port = htons(tmp.outgoing_port); + static struct sockaddr_in to = {0}; + to.sin_family = AF_INET; + to.sin_port = htons(tmp.outgoing_port); to.sin_addr.s_addr = tmp.outgoing_ip_addr; char ip4[INET_ADDRSTRLEN]; - //char ip6[INET6_ADDRSTRLEN]; - LOG_D(GTPU,"[%ld] sending end packet to %s\n", instance, inet_ntoa(to.sin_addr) ); + // char ip6[INET6_ADDRSTRLEN]; + LOG_D(GTPU, "[%ld] sending end packet to %s\n", instance, inet_ntoa(to.sin_addr)); - if (sendto(compatInst(instance), (void *)&msgHdr, sizeof(msgHdr), 0,(struct sockaddr *)&to, sizeof(to) ) != sizeof(msgHdr)) { + if (sendto(compatInst(instance), (void *)&msgHdr, sizeof(msgHdr), 0, (struct sockaddr *)&to, sizeof(to)) != sizeof(msgHdr)) { LOG_E(GTPU, "[%ld] Failed to send data to %s on port %d, buffer size %lu\n", - compatInst(instance), inet_ntop(AF_INET, &tmp.outgoing_ip_addr, ip4, INET_ADDRSTRLEN), tmp.outgoing_port, sizeof(msgHdr)); + compatInst(instance), + inet_ntop(AF_INET, &tmp.outgoing_ip_addr, ip4, INET_ADDRSTRLEN), + tmp.outgoing_port, + sizeof(msgHdr)); } } -static int udpServerSocket(openAddr_s addr) { +static int udpServerSocket(openAddr_s addr) +{ LOG_I(GTPU, "Initializing UDP for local address %s with port %s\n", addr.originHost, addr.originService); int status; - struct addrinfo hints= {0}, *servinfo, *p; + struct addrinfo hints = {0}, *servinfo, *p; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; if ((status = getaddrinfo(addr.originHost, addr.originService, &hints, &servinfo)) != 0) { - LOG_E(GTPU,"getaddrinfo error: %s\n", gai_strerror(status)); + LOG_E(GTPU, "getaddrinfo error: %s\n", gai_strerror(status)); return -1; } - int sockfd=-1; + int sockfd = -1; // loop through all the results and bind to the first we can - for(p = servinfo; p != NULL; p = p->ai_next) { - if ((sockfd = socket(p->ai_family, p->ai_socktype, - p->ai_protocol)) == -1) { - LOG_W(GTPU,"socket: %s\n", strerror(errno)); + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { + LOG_W(GTPU, "socket: %s\n", strerror(errno)); continue; } if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); - LOG_W(GTPU,"bind: %s\n", strerror(errno)); + LOG_W(GTPU, "bind: %s\n", strerror(errno)); continue; } else { // We create the gtp instance on the socket - globGtp.instances[sockfd].addr=addr; + globGtp.instances[sockfd].addr = addr; if (p->ai_family == AF_INET) { - struct sockaddr_in *ipv4=(struct sockaddr_in *)p->ai_addr; - memcpy(globGtp.instances[sockfd].foundAddr, - &ipv4->sin_addr.s_addr, sizeof(ipv4->sin_addr.s_addr)); - globGtp.instances[sockfd].foundAddrLen=sizeof(ipv4->sin_addr.s_addr); - globGtp.instances[sockfd].ipVersion=4; + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + memcpy(globGtp.instances[sockfd].foundAddr, &ipv4->sin_addr.s_addr, sizeof(ipv4->sin_addr.s_addr)); + globGtp.instances[sockfd].foundAddrLen = sizeof(ipv4->sin_addr.s_addr); + globGtp.instances[sockfd].ipVersion = 4; break; } else if (p->ai_family == AF_INET6) { - LOG_W(GTPU,"Local address is IP v6\n"); - struct sockaddr_in6 *ipv6=(struct sockaddr_in6 *)p->ai_addr; - memcpy(globGtp.instances[sockfd].foundAddr, - &ipv6->sin6_addr.s6_addr, sizeof(ipv6->sin6_addr.s6_addr)); - globGtp.instances[sockfd].foundAddrLen=sizeof(ipv6->sin6_addr.s6_addr); - globGtp.instances[sockfd].ipVersion=6; + LOG_W(GTPU, "Local address is IP v6\n"); + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; + memcpy(globGtp.instances[sockfd].foundAddr, &ipv6->sin6_addr.s6_addr, sizeof(ipv6->sin6_addr.s6_addr)); + globGtp.instances[sockfd].foundAddrLen = sizeof(ipv6->sin6_addr.s6_addr); + globGtp.instances[sockfd].ipVersion = 6; } else - AssertFatal(false,"Local address is not IPv4 or IPv6"); + AssertFatal(false, "Local address is not IPv4 or IPv6"); } break; // if we get here, we must have connected successfully @@ -473,46 +491,61 @@ static int udpServerSocket(openAddr_s addr) { if (p == NULL) { // looped off the end of the list with no successful bind - LOG_E(GTPU,"failed to bind socket: %s %s \n", addr.originHost, addr.originService); + LOG_E(GTPU, "failed to bind socket: %s %s \n", addr.originHost, addr.originService); return -1; } - int sendbuff = 1000*1000*10; - AssertFatal(0==setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff)),""); - LOG_D(GTPU,"[%d] Created listener for paquets to: %s:%s, send buffer size: %d\n", sockfd, addr.originHost, addr.originService,sendbuff); + int sendbuff = 1000 * 1000 * 10; + AssertFatal(0 == setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff)), ""); + LOG_D(GTPU, + "[%d] Created listener for paquets to: %s:%s, send buffer size: %d\n", + sockfd, + addr.originHost, + addr.originService, + sendbuff); return sockfd; } -instance_t gtpv1Init(openAddr_t context) { +instance_t gtpv1Init(openAddr_t context) +{ pthread_mutex_lock(&globGtp.gtp_lock); - int id=udpServerSocket(context); + int id = udpServerSocket(context); - if (id>=0) { + if (id >= 0) { itti_subscribe_event_fd(TASK_GTPV1_U, id); } else - LOG_E(GTPU,"can't create GTP-U instance\n"); + LOG_E(GTPU, "can't create GTP-U instance\n"); pthread_mutex_unlock(&globGtp.gtp_lock); LOG_I(GTPU, "Created gtpu instance id: %d\n", id); return id; } -void GtpuUpdateTunnelOutgoingAddressAndTeid(instance_t instance, ue_id_t ue_id, ebi_t bearer_id, in_addr_t newOutgoingAddr, teid_t newOutgoingTeid) { +void GtpuUpdateTunnelOutgoingAddressAndTeid(instance_t instance, + ue_id_t ue_id, + ebi_t bearer_id, + in_addr_t newOutgoingAddr, + teid_t newOutgoingTeid) +{ pthread_mutex_lock(&globGtp.gtp_lock); getInstRetVoid(compatInst(instance)); getUeRetVoid(inst, ue_id); - auto ptr2=ptrUe->second.bearers.find(bearer_id); + auto ptr2 = ptrUe->second.bearers.find(bearer_id); - if ( ptr2 == ptrUe->second.bearers.end() ) { - LOG_E(GTPU,"[%ld] Update tunnel for a existing ue id %lu, but wrong bearer_id %u\n", instance, ue_id, bearer_id); + if (ptr2 == ptrUe->second.bearers.end()) { + LOG_E(GTPU, "[%ld] Update tunnel for a existing ue id %lu, but wrong bearer_id %u\n", instance, ue_id, bearer_id); pthread_mutex_unlock(&globGtp.gtp_lock); return; } ptr2->second.outgoing_ip_addr = newOutgoingAddr; ptr2->second.teid_outgoing = newOutgoingTeid; - LOG_I(GTPU, "[%ld] Tunnel Outgoing TEID updated to %x and address to %x\n", instance, ptr2->second.teid_outgoing, ptr2->second.outgoing_ip_addr); + LOG_I(GTPU, + "[%ld] Tunnel Outgoing TEID updated to %x and address to %x\n", + instance, + ptr2->second.teid_outgoing, + ptr2->second.outgoing_ip_addr); pthread_mutex_unlock(&globGtp.gtp_lock); return; } @@ -526,21 +559,22 @@ teid_t newGtpuCreateTunnel(instance_t instance, transport_layer_addr_t remoteAddr, int port, gtpCallback callBack, - gtpCallbackSDAP callBackSDAP) { + gtpCallbackSDAP callBackSDAP) +{ pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); - auto it=inst->ue2te_mapping.find(ue_id); + auto it = inst->ue2te_mapping.find(ue_id); - if ( it != inst->ue2te_mapping.end() && it->second.bearers.find(outgoing_bearer_id) != it->second.bearers.end()) { - LOG_W(GTPU,"[%ld] Create a config for a already existing GTP tunnel (ue id %lu)\n", instance, ue_id); + if (it != inst->ue2te_mapping.end() && it->second.bearers.find(outgoing_bearer_id) != it->second.bearers.end()) { + LOG_W(GTPU, "[%ld] Create a config for a already existing GTP tunnel (ue id %lu)\n", instance, ue_id); inst->ue2te_mapping.erase(it); } - teid_t incoming_teid=gtpv1uNewTeid(); + teid_t incoming_teid = gtpv1uNewTeid(); while (globGtp.te2ue_mapping.find(incoming_teid) != globGtp.te2ue_mapping.end()) { LOG_W(GTPU, "[%ld] generated a random Teid that exists, re-generating (%x)\n", instance, incoming_teid); - incoming_teid=gtpv1uNewTeid(); + incoming_teid = gtpv1uNewTeid(); }; globGtp.te2ue_mapping[incoming_teid].ue_id = ue_id; @@ -550,34 +584,31 @@ teid_t newGtpuCreateTunnel(instance_t instance, globGtp.te2ue_mapping[incoming_teid].callBackSDAP = callBackSDAP; globGtp.te2ue_mapping[incoming_teid].pdusession_id = (uint8_t)outgoing_bearer_id; - gtpv1u_bearer_t *tmp=&inst->ue2te_mapping[ue_id].bearers[outgoing_bearer_id]; + gtpv1u_bearer_t *tmp = &inst->ue2te_mapping[ue_id].bearers[outgoing_bearer_id]; int addrs_length_in_bytes = remoteAddr.length / 8; switch (addrs_length_in_bytes) { case 4: - memcpy(&tmp->outgoing_ip_addr,remoteAddr.buffer,4); + memcpy(&tmp->outgoing_ip_addr, remoteAddr.buffer, 4); break; case 16: - memcpy(tmp->outgoing_ip6_addr.s6_addr,remoteAddr.buffer, - 16); + memcpy(tmp->outgoing_ip6_addr.s6_addr, remoteAddr.buffer, 16); break; case 20: - memcpy(&tmp->outgoing_ip_addr,remoteAddr.buffer,4); - memcpy(tmp->outgoing_ip6_addr.s6_addr, - remoteAddr.buffer+4, - 16); + memcpy(&tmp->outgoing_ip_addr, remoteAddr.buffer, 4); + memcpy(tmp->outgoing_ip6_addr.s6_addr, remoteAddr.buffer + 4, 16); default: AssertFatal(false, "SGW Address size impossible"); } tmp->teid_incoming = incoming_teid; - tmp->outgoing_port=port; - tmp->teid_outgoing= outgoing_teid; - tmp->outgoing_qfi=outgoing_qfi; + tmp->outgoing_port = port; + tmp->teid_outgoing = outgoing_teid; + tmp->outgoing_qfi = outgoing_qfi; pthread_mutex_unlock(&globGtp.gtp_lock); char ip4[INET_ADDRSTRLEN]; char ip6[INET6_ADDRSTRLEN]; @@ -593,28 +624,29 @@ teid_t newGtpuCreateTunnel(instance_t instance, } int gtpv1u_create_s1u_tunnel(instance_t instance, - const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req, + const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req, gtpv1u_enb_create_tunnel_resp_t *create_tunnel_resp, gtpCallback callBack) { - LOG_D(GTPU, "[%ld] Start create tunnels for UE ID %u, num_tunnels %d, sgw_S1u_teid %x\n", + LOG_D(GTPU, + "[%ld] Start create tunnels for UE ID %u, num_tunnels %d, sgw_S1u_teid %x\n", instance, create_tunnel_req->rnti, create_tunnel_req->num_tunnels, create_tunnel_req->sgw_S1u_teid[0]); pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); - - tcp_udp_port_t dstport=inst->get_dstport(); + + tcp_udp_port_t dstport = inst->get_dstport(); uint8_t addr[inst->foundAddrLen]; memcpy(addr, inst->foundAddr, inst->foundAddrLen); pthread_mutex_unlock(&globGtp.gtp_lock); - + for (int i = 0; i < create_tunnel_req->num_tunnels; i++) { AssertFatal(create_tunnel_req->eps_bearer_id[i] > 4, "From legacy code not clear, seems impossible (bearer=%d)\n", create_tunnel_req->eps_bearer_id[i]); - int incoming_rb_id=create_tunnel_req->eps_bearer_id[i]-4; + int incoming_rb_id = create_tunnel_req->eps_bearer_id[i] - 4; teid_t teid = newGtpuCreateTunnel(compatInst(instance), create_tunnel_req->rnti, incoming_rb_id, @@ -625,24 +657,24 @@ int gtpv1u_create_s1u_tunnel(instance_t instance, dstport, callBack, NULL); - create_tunnel_resp->status=0; - create_tunnel_resp->rnti=create_tunnel_req->rnti; - create_tunnel_resp->num_tunnels=create_tunnel_req->num_tunnels; - create_tunnel_resp->enb_S1u_teid[i]=teid; + create_tunnel_resp->status = 0; + create_tunnel_resp->rnti = create_tunnel_req->rnti; + create_tunnel_resp->num_tunnels = create_tunnel_req->num_tunnels; + create_tunnel_resp->enb_S1u_teid[i] = teid; create_tunnel_resp->eps_bearer_id[i] = create_tunnel_req->eps_bearer_id[i]; - memcpy(create_tunnel_resp->enb_addr.buffer,addr,sizeof(addr)); - create_tunnel_resp->enb_addr.length= sizeof(addr); + memcpy(create_tunnel_resp->enb_addr.buffer, addr, sizeof(addr)); + create_tunnel_resp->enb_addr.length = sizeof(addr); } return !GTPNOK; } -int gtpv1u_update_s1u_tunnel( - const instance_t instance, - const gtpv1u_enb_create_tunnel_req_t *const create_tunnel_req, - const rnti_t prior_rnti -) { - LOG_D(GTPU, "[%ld] Start update tunnels for old RNTI %x, new RNTI %x, num_tunnels %d, sgw_S1u_teid %x, eps_bearer_id %x\n", +int gtpv1u_update_s1u_tunnel(const instance_t instance, + const gtpv1u_enb_create_tunnel_req_t *const create_tunnel_req, + const rnti_t prior_rnti) +{ + LOG_D(GTPU, + "[%ld] Start update tunnels for old RNTI %x, new RNTI %x, num_tunnels %d, sgw_S1u_teid %x, eps_bearer_id %x\n", instance, prior_rnti, create_tunnel_req->rnti, @@ -652,22 +684,28 @@ int gtpv1u_update_s1u_tunnel( pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); - if ( inst->ue2te_mapping.find(create_tunnel_req->rnti) == inst->ue2te_mapping.end() ) { - LOG_E(GTPU,"[%ld] Update not already existing tunnel (new rnti %x, old rnti %x)\n", - instance, create_tunnel_req->rnti, prior_rnti); + if (inst->ue2te_mapping.find(create_tunnel_req->rnti) == inst->ue2te_mapping.end()) { + LOG_E(GTPU, + "[%ld] Update not already existing tunnel (new rnti %x, old rnti %x)\n", + instance, + create_tunnel_req->rnti, + prior_rnti); } - auto it=inst->ue2te_mapping.find(prior_rnti); + auto it = inst->ue2te_mapping.find(prior_rnti); - if ( it != inst->ue2te_mapping.end() ) { + if (it != inst->ue2te_mapping.end()) { pthread_mutex_unlock(&globGtp.gtp_lock); - AssertFatal(false, "logic bug: update of non-existing tunnel (new ue id %u, old ue id %u)\n", create_tunnel_req->rnti, prior_rnti); + AssertFatal(false, + "logic bug: update of non-existing tunnel (new ue id %u, old ue id %u)\n", + create_tunnel_req->rnti, + prior_rnti); /* we don't know if we need 4G or 5G PDCP and can therefore not create a * new tunnel */ return 0; } - inst->ue2te_mapping[create_tunnel_req->rnti]=it->second; + inst->ue2te_mapping[create_tunnel_req->rnti] = it->second; inst->ue2te_mapping.erase(it); pthread_mutex_unlock(&globGtp.gtp_lock); return 0; @@ -679,7 +717,8 @@ int gtpv1u_create_ngu_tunnel(const instance_t instance, gtpCallback callBack, gtpCallbackSDAP callBackSDAP) { - LOG_D(GTPU, "[%ld] Start create tunnels for ue id %lu, num_tunnels %d, sgw_S1u_teid %x\n", + LOG_D(GTPU, + "[%ld] Start create tunnels for ue id %lu, num_tunnels %d, sgw_S1u_teid %x\n", instance, create_tunnel_req->ue_id, create_tunnel_req->num_tunnels, @@ -702,12 +741,12 @@ int gtpv1u_create_ngu_tunnel(const instance_t instance, dstport, callBack, callBackSDAP); - create_tunnel_resp->status=0; - create_tunnel_resp->ue_id=create_tunnel_req->ue_id; - create_tunnel_resp->num_tunnels=create_tunnel_req->num_tunnels; - create_tunnel_resp->gnb_NGu_teid[i]=teid; - memcpy(create_tunnel_resp->gnb_addr.buffer,addr,sizeof(addr)); - create_tunnel_resp->gnb_addr.length= sizeof(addr); + create_tunnel_resp->status = 0; + create_tunnel_resp->ue_id = create_tunnel_req->ue_id; + create_tunnel_resp->num_tunnels = create_tunnel_req->num_tunnels; + create_tunnel_resp->gnb_NGu_teid[i] = teid; + memcpy(create_tunnel_resp->gnb_addr.buffer, addr, sizeof(addr)); + create_tunnel_resp->gnb_addr.length = sizeof(addr); create_tunnel_resp->pdusession_id[i] = create_tunnel_req->pdusession_id[i]; } @@ -742,11 +781,11 @@ int gtpv1u_update_ue_id(const instance_t instanceP, ue_id_t old_ue_id, ue_id_t n return !GTPNOK; } -int gtpv1u_create_x2u_tunnel( - const instance_t instanceP, - const gtpv1u_enb_create_x2u_tunnel_req_t *const create_tunnel_req_pP, - gtpv1u_enb_create_x2u_tunnel_resp_t *const create_tunnel_resp_pP) { - AssertFatal( false, "to be developped\n"); +int gtpv1u_create_x2u_tunnel(const instance_t instanceP, + const gtpv1u_enb_create_x2u_tunnel_req_t *const create_tunnel_req_pP, + gtpv1u_enb_create_x2u_tunnel_resp_t *const create_tunnel_resp_pP) +{ + AssertFatal(false, "to be developped\n"); } int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id) @@ -770,23 +809,21 @@ int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id) ue_it->second.bearers.erase(rb_id); pthread_mutex_unlock(&globGtp.gtp_lock); LOG_I(GTPU, "Deleted tunnel TEID %d (RB %d) for ue id %ld, remaining bearers:\n", teid, rb_id, ue_id); - for (auto b: ue_it->second.bearers) + for (auto b : ue_it->second.bearers) LOG_I(GTPU, "bearer %ld\n", b.first); return !GTPNOK; } -int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id) { - LOG_D(GTPU, "[%ld] Start delete tunnels for ue id %lu\n", - instance, ue_id); +int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id) +{ + LOG_D(GTPU, "[%ld] Start delete tunnels for ue id %lu\n", instance, ue_id); pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); getUeRetInt(inst, ue_id); - int nb=0; + int nb = 0; - for (auto j=ptrUe->second.bearers.begin(); - j!=ptrUe->second.bearers.end(); - ++j) { + for (auto j = ptrUe->second.bearers.begin(); j != ptrUe->second.bearers.end(); ++j) { globGtp.te2ue_mapping.erase(j->second.teid_incoming); nb++; } @@ -797,8 +834,8 @@ int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id) { return !GTPNOK; } -int gtpv1u_delete_s1u_tunnel( const instance_t instance, - const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { +int gtpv1u_delete_s1u_tunnel(const instance_t instance, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) +{ LOG_D(GTPU, "[%ld] Start delete tunnels for RNTI %x\n", instance, req_pP->rnti); pthread_mutex_lock(&globGtp.gtp_lock); auto inst = &globGtp.instances[compatInst(instance)]; @@ -842,25 +879,24 @@ int gtpv1u_delete_all_s1u_tunnel(const instance_t instance, const rnti_t rnti) int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, int *pdusession_id) { - LOG_D(GTPU, "[%ld] Start delete tunnels for ue id %lu\n", - instance, ue_id); + LOG_D(GTPU, "[%ld] Start delete tunnels for ue id %lu\n", instance, ue_id); pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); getUeRetInt(inst, ue_id); - int nb=0; + int nb = 0; - for (int i=0; isecond.bearers.find(pdusession_id[i]); + for (int i = 0; i < nbTunnels; i++) { + auto ptr2 = ptrUe->second.bearers.find(pdusession_id[i]); - if ( ptr2 == ptrUe->second.bearers.end() ) { - LOG_E(GTPU,"[%ld] GTP-U instance: delete of not existing tunnel UE ID:RAB: %ld/%x\n", instance, ue_id, pdusession_id[i]); + if (ptr2 == ptrUe->second.bearers.end()) { + LOG_E(GTPU, "[%ld] GTP-U instance: delete of not existing tunnel UE ID:RAB: %ld/%x\n", instance, ue_id, pdusession_id[i]); } else { globGtp.te2ue_mapping.erase(ptr2->second.teid_incoming); nb++; } } - if (ptrUe->second.bearers.size() == 0 ) + if (ptrUe->second.bearers.size() == 0) // no tunnels on this ue id, erase the ue entry inst->ue2te_mapping.erase(ptrUe); @@ -869,56 +905,60 @@ int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, int return !GTPNOK; } -int gtpv1u_delete_x2u_tunnel( const instance_t instanceP, - const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { - LOG_E(GTPU,"x2 tunnel not implemented\n"); +int gtpv1u_delete_x2u_tunnel(const instance_t instanceP, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) +{ + LOG_E(GTPU, "x2 tunnel not implemented\n"); return 0; } -int gtpv1u_delete_ngu_tunnel( const instance_t instance, - gtpv1u_gnb_delete_tunnel_req_t *req) { - return newGtpuDeleteTunnels(instance, req->ue_id, req->num_pdusession, req->pdusession_id); +int gtpv1u_delete_ngu_tunnel(const instance_t instance, gtpv1u_gnb_delete_tunnel_req_t *req) +{ + return newGtpuDeleteTunnels(instance, req->ue_id, req->num_pdusession, req->pdusession_id); } -static int Gtpv1uHandleEchoReq(int h, - uint8_t *msgBuf, - uint32_t msgBufLen, - uint16_t peerPort, - uint32_t peerIp) { - Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *) msgBuf; +static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp) +{ + Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf; - if ( msgHdr->version != 1 || msgHdr->PT != 1 ) { + if (msgHdr->version != 1 || msgHdr->PT != 1) { LOG_E(GTPU, "[%d] Received a packet that is not GTP header\n", h); return GTPNOK; } - if ( msgHdr->S != 1 ) { + if (msgHdr->S != 1) { LOG_E(GTPU, "[%d] Received a echo request packet with no sequence number \n", h); return GTPNOK; } - uint16_t seq=ntohs(*(uint16_t *)(msgHdr+1)); + uint16_t seq = ntohs(*(uint16_t *)(msgHdr + 1)); LOG_D(GTPU, "[%d] Received a echo request, TEID: %d, seq: %hu\n", h, msgHdr->teid, seq); - uint8_t recovery[2]= {14,0}; - return gtpv1uCreateAndSendMsg(h, peerIp, peerPort, GTP_ECHO_RSP, ntohl(msgHdr->teid), recovery, sizeof recovery, true, false, seq, 0, NO_MORE_EXT_HDRS, NULL, 0); + uint8_t recovery[2] = {14, 0}; + return gtpv1uCreateAndSendMsg(h, + peerIp, + peerPort, + GTP_ECHO_RSP, + ntohl(msgHdr->teid), + recovery, + sizeof recovery, + true, + false, + seq, + 0, + NO_MORE_EXT_HDRS, + NULL, + 0); } -static int Gtpv1uHandleError(int h, - uint8_t *msgBuf, - uint32_t msgBufLen, - uint16_t peerPort, - uint32_t peerIp) { +static int Gtpv1uHandleError(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp) +{ LOG_E(GTPU, "Received GTP error indication (error handling is missing/not implemented)\n"); int rc = GTPNOK; return rc; } -static int Gtpv1uHandleSupportedExt(int h, - uint8_t *msgBuf, - uint32_t msgBufLen, - uint16_t peerPort, - uint32_t peerIp) { - LOG_E(GTPU,"Supported extensions to be dev\n"); +static int Gtpv1uHandleSupportedExt(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp) +{ + LOG_E(GTPU, "Supported extensions to be dev\n"); int rc = GTPNOK; return rc; } @@ -926,14 +966,11 @@ static int Gtpv1uHandleSupportedExt(int h, // When end marker arrives, we notify the client with buffer size = 0 // The client will likely call "delete tunnel" // nevertheless we don't take the initiative -static int Gtpv1uHandleEndMarker(int h, - uint8_t *msgBuf, - uint32_t msgBufLen, - uint16_t peerPort, - uint32_t peerIp) { - Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *) msgBuf; +static int Gtpv1uHandleEndMarker(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp) +{ + Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf; - if ( msgHdr->version != 1 || msgHdr->PT != 1 ) { + if (msgHdr->version != 1 || msgHdr->PT != 1) { LOG_E(GTPU, "[%d] Received a packet that is not GTP header\n", h); return GTPNOK; } @@ -945,7 +982,7 @@ static int Gtpv1uHandleEndMarker(int h, auto tunnel = globGtp.te2ue_mapping.find(ntohl(msgHdr->teid)); if (tunnel == globGtp.te2ue_mapping.end()) { - LOG_E(GTPU,"[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, msgHdr->teid); + LOG_E(GTPU, "[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, msgHdr->teid); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; } @@ -962,39 +999,27 @@ static int Gtpv1uHandleEndMarker(int h, ctxt.subframe = 0; ctxt.eNB_index = 0; ctxt.brOption = 0; - const srb_flag_t srb_flag=SRB_FLAG_NO; - const rb_id_t rb_id=tunnel->second.incoming_rb_id; - const mui_t mui=RLC_MUI_UNDEFINED; - const confirm_t confirm=RLC_SDU_CONFIRM_NO; - const pdcp_transmission_mode_t mode=PDCP_TRANSMISSION_MODE_DATA; - const uint32_t sourceL2Id=0; - const uint32_t destinationL2Id=0; + const srb_flag_t srb_flag = SRB_FLAG_NO; + const rb_id_t rb_id = tunnel->second.incoming_rb_id; + const mui_t mui = RLC_MUI_UNDEFINED; + const confirm_t confirm = RLC_SDU_CONFIRM_NO; + const pdcp_transmission_mode_t mode = PDCP_TRANSMISSION_MODE_DATA; + const uint32_t sourceL2Id = 0; + const uint32_t destinationL2Id = 0; pthread_mutex_unlock(&globGtp.gtp_lock); - if ( !tunnel->second.callBack(&ctxt, - srb_flag, - rb_id, - mui, - confirm, - 0, - NULL, - mode, - &sourceL2Id, - &destinationL2Id) ) - LOG_E(GTPU,"[%d] down layer refused incoming packet\n", h); + if (!tunnel->second.callBack(&ctxt, srb_flag, rb_id, mui, confirm, 0, NULL, mode, &sourceL2Id, &destinationL2Id)) + LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h); - LOG_D(GTPU,"[%d] Received END marker packet for: teid:%x\n", h, ntohl(msgHdr->teid)); + LOG_D(GTPU, "[%d] Received END marker packet for: teid:%x\n", h, ntohl(msgHdr->teid)); return !GTPNOK; } -static int Gtpv1uHandleGpdu(int h, - uint8_t *msgBuf, - uint32_t msgBufLen, - uint16_t peerPort, - uint32_t peerIp) { - Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *) msgBuf; +static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp) +{ + Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf; - if ( msgHdr->version != 1 || msgHdr->PT != 1 ) { + if (msgHdr->version != 1 || msgHdr->PT != 1) { LOG_E(GTPU, "[%d] Received a packet that is not GTP header\n", h); return GTPNOK; } @@ -1005,13 +1030,13 @@ static int Gtpv1uHandleGpdu(int h, auto tunnel = globGtp.te2ue_mapping.find(ntohl(msgHdr->teid)); if (tunnel == globGtp.te2ue_mapping.end()) { - LOG_E(GTPU,"[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, ntohl(msgHdr->teid)); + LOG_E(GTPU, "[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, ntohl(msgHdr->teid)); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; } /* see TS 29.281 5.1 */ - //Minimum length of GTP-U header if non of the optional fields are present + // Minimum length of GTP-U header if non of the optional fields are present unsigned int offset = sizeof(Gtpv1uMsgHeaderT); int8_t qfi = -1; @@ -1019,7 +1044,7 @@ static int Gtpv1uHandleGpdu(int h, uint32_t NR_PDCP_PDU_SN = 0; /* if E, S, or PN is set then there are 4 more bytes of header */ - if( msgHdr->E || msgHdr->S ||msgHdr->PN) + if (msgHdr->E || msgHdr->S || msgHdr->PN) offset += 4; if (msgHdr->E) { @@ -1030,53 +1055,53 @@ static int Gtpv1uHandleGpdu(int h, extension_header_length = msgBuf[offset]; switch (next_extension_header_type) { case PDU_SESSION_CONTAINER: { - if (offset + sizeof(PDUSessionContainerT) > msgBufLen ) { - LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); - return GTPNOK; - } + if (offset + sizeof(PDUSessionContainerT) > msgBufLen) { + LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); + return GTPNOK; + } PDUSessionContainerT *pdusession_cntr = (PDUSessionContainerT *)(msgBuf + offset + 1); qfi = pdusession_cntr->QFI; rqi = pdusession_cntr->Reflective_QoS_activation; break; } case NR_RAN_CONTAINER: { - if (offset + 1 > msgBufLen ) { - LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); - return GTPNOK; - } - uint8_t PDU_type = (msgBuf[offset+1]>>4) & 0x0f; - if (PDU_type == 0){ //DL USER Data Format - int additional_offset = 6; //Additional offset capturing the first non-mandatory octet (TS 38.425, Figure 5.5.2.1-1) - if(msgBuf[offset+1]>>2 & 0x1){ //DL Discard Blocks flag is present - LOG_I(GTPU, "DL User Data: DL Discard Blocks handling not enabled\n"); - additional_offset = additional_offset + 9; //For the moment ignore + if (offset + 1 > msgBufLen) { + LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); + return GTPNOK; + } + uint8_t PDU_type = (msgBuf[offset + 1] >> 4) & 0x0f; + if (PDU_type == 0) { // DL USER Data Format + int additional_offset = 6; // Additional offset capturing the first non-mandatory octet (TS 38.425, Figure 5.5.2.1-1) + if (msgBuf[offset + 1] >> 2 & 0x1) { // DL Discard Blocks flag is present + LOG_I(GTPU, "DL User Data: DL Discard Blocks handling not enabled\n"); + additional_offset = additional_offset + 9; // For the moment ignore } - if(msgBuf[offset+1]>>1 & 0x1){ //DL Flush flag is present + if (msgBuf[offset + 1] >> 1 & 0x1) { // DL Flush flag is present LOG_I(GTPU, "DL User Data: DL Flush handling not enabled\n"); - additional_offset = additional_offset + 3; //For the moment ignore + additional_offset = additional_offset + 3; // For the moment ignore } - if((msgBuf[offset+2]>>3)& 0x1){ //"Report delivered" enabled (TS 38.425, 5.4) + if ((msgBuf[offset + 2] >> 3) & 0x1) { //"Report delivered" enabled (TS 38.425, 5.4) /*Store the NR PDCP PDU SN for which a delivery status report shall be generated once the *PDU gets forwarded to the lower layers*/ - //NR_PDCP_PDU_SN = msgBuf[offset+6] << 16 | msgBuf[offset+7] << 8 | msgBuf[offset+8]; - NR_PDCP_PDU_SN = msgBuf[offset+additional_offset] << 16 | msgBuf[offset+additional_offset+1] << 8 | msgBuf[offset+additional_offset+2]; - LOG_D(GTPU, " NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN); + // NR_PDCP_PDU_SN = msgBuf[offset+6] << 16 | msgBuf[offset+7] << 8 | msgBuf[offset+8]; + NR_PDCP_PDU_SN = msgBuf[offset + additional_offset] << 16 | msgBuf[offset + additional_offset + 1] << 8 + | msgBuf[offset + additional_offset + 2]; + LOG_D(GTPU, " NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN); } - } - else{ + } else { LOG_W(GTPU, "NR-RAN container type: %d not supported \n", PDU_type); } break; } default: LOG_W(GTPU, "unhandled extension 0x%2.2x, skipping\n", next_extension_header_type); - break; + break; } offset += extension_header_length * EXT_HDR_LNTH_OCTET_UNITS; - if (offset > msgBufLen ) { - LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); - return GTPNOK; + if (offset > msgBufLen) { + LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); + return GTPNOK; } next_extension_header_type = msgBuf[offset - 1]; } @@ -1094,51 +1119,43 @@ static int Gtpv1uHandleGpdu(int h, ctxt.subframe = 0; ctxt.eNB_index = 0; ctxt.brOption = 0; - const srb_flag_t srb_flag=SRB_FLAG_NO; - const rb_id_t rb_id=tunnel->second.incoming_rb_id; - const mui_t mui=RLC_MUI_UNDEFINED; - const confirm_t confirm=RLC_SDU_CONFIRM_NO; + const srb_flag_t srb_flag = SRB_FLAG_NO; + const rb_id_t rb_id = tunnel->second.incoming_rb_id; + const mui_t mui = RLC_MUI_UNDEFINED; + const confirm_t confirm = RLC_SDU_CONFIRM_NO; const sdu_size_t sdu_buffer_size = msgBufLen - offset; - unsigned char *const sdu_buffer=msgBuf+offset; - const pdcp_transmission_mode_t mode=PDCP_TRANSMISSION_MODE_DATA; - const uint32_t sourceL2Id=0; - const uint32_t destinationL2Id=0; + unsigned char *const sdu_buffer = msgBuf + offset; + const pdcp_transmission_mode_t mode = PDCP_TRANSMISSION_MODE_DATA; + const uint32_t sourceL2Id = 0; + const uint32_t destinationL2Id = 0; pthread_mutex_unlock(&globGtp.gtp_lock); if (sdu_buffer_size > 0) { if (qfi != -1 && tunnel->second.callBackSDAP) { - if ( !tunnel->second.callBackSDAP(&ctxt, - tunnel->second.ue_id, - srb_flag, - rb_id, - mui, - confirm, - sdu_buffer_size, - sdu_buffer, - mode, - &sourceL2Id, - &destinationL2Id, - qfi, - rqi, - tunnel->second.pdusession_id) ) - LOG_E(GTPU,"[%d] down layer refused incoming packet\n", h); + if (!tunnel->second.callBackSDAP(&ctxt, + tunnel->second.ue_id, + srb_flag, + rb_id, + mui, + confirm, + sdu_buffer_size, + sdu_buffer, + mode, + &sourceL2Id, + &destinationL2Id, + qfi, + rqi, + tunnel->second.pdusession_id)) + LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h); } else { - if ( !tunnel->second.callBack(&ctxt, - srb_flag, - rb_id, - mui, - confirm, - sdu_buffer_size, - sdu_buffer, - mode, - &sourceL2Id, - &destinationL2Id) ) - LOG_E(GTPU,"[%d] down layer refused incoming packet\n", h); + if (!tunnel->second + .callBack(&ctxt, srb_flag, rb_id, mui, confirm, sdu_buffer_size, sdu_buffer, mode, &sourceL2Id, &destinationL2Id)) + LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h); } } - if(NR_PDCP_PDU_SN > 0 && NR_PDCP_PDU_SN %5 ==0){ - LOG_D (GTPU, "Create and send DL DATA Delivery status for the previously received PDU, NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN); + if (NR_PDCP_PDU_SN > 0 && NR_PDCP_PDU_SN % 5 == 0) { + LOG_D(GTPU, "Create and send DL DATA Delivery status for the previously received PDU, NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN); int rlc_tx_buffer_space = nr_rlc_get_available_tx_space(ctxt.rntiMaybeUEid, rb_id + 3); LOG_D(GTPU, "Available buffer size in RLC for Tx: %d \n", rlc_tx_buffer_space); /*Total size of DDD_status PDU = 1 octet to report extension header length @@ -1146,87 +1163,99 @@ static int Gtpv1uHandleGpdu(int h, * 1 octet for padding + 1 octet for next extension header type, * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ extensionHeader_t *extensionHeader; - extensionHeader = (extensionHeader_t *) calloc(1, sizeof(extensionHeader_t)) ; - extensionHeader->buffer[0] = (1+sizeof(DlDataDeliveryStatus_flagsT)+3+1+1)/4; + extensionHeader = (extensionHeader_t *)calloc(1, sizeof(extensionHeader_t)); + extensionHeader->buffer[0] = (1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1) / 4; DlDataDeliveryStatus_flagsT DlDataDeliveryStatus; DlDataDeliveryStatus.deliveredPdcpSn = 0; - DlDataDeliveryStatus.transmittedPdcpSn= 1; + DlDataDeliveryStatus.transmittedPdcpSn = 1; DlDataDeliveryStatus.pduType = 1; - DlDataDeliveryStatus.drbBufferSize = htonl(rlc_tx_buffer_space); //htonl(10000000); //hardcoded for now but normally we should extract it from RLC - memcpy(extensionHeader->buffer+1, &DlDataDeliveryStatus, sizeof(DlDataDeliveryStatus_flagsT)); - uint8_t offset = sizeof(DlDataDeliveryStatus_flagsT)+1; + DlDataDeliveryStatus.drbBufferSize = + htonl(rlc_tx_buffer_space); // htonl(10000000); //hardcoded for now but normally we should extract it from RLC + memcpy(extensionHeader->buffer + 1, &DlDataDeliveryStatus, sizeof(DlDataDeliveryStatus_flagsT)); + uint8_t offset = sizeof(DlDataDeliveryStatus_flagsT) + 1; - extensionHeader->buffer[offset] = (NR_PDCP_PDU_SN >> 16) & 0xff; - extensionHeader->buffer[offset+1] = (NR_PDCP_PDU_SN >> 8) & 0xff; - extensionHeader->buffer[offset+2] = NR_PDCP_PDU_SN & 0xff; - LOG_D(GTPU, "Octets reporting NR_PDCP_PDU_SN, extensionHeader-> %u:%u:%u \n", + extensionHeader->buffer[offset] = (NR_PDCP_PDU_SN >> 16) & 0xff; + extensionHeader->buffer[offset + 1] = (NR_PDCP_PDU_SN >> 8) & 0xff; + extensionHeader->buffer[offset + 2] = NR_PDCP_PDU_SN & 0xff; + LOG_D(GTPU, + "Octets reporting NR_PDCP_PDU_SN, extensionHeader-> %u:%u:%u \n", extensionHeader->buffer[offset], - extensionHeader->buffer[offset+1], - extensionHeader->buffer[offset+2]); - extensionHeader->buffer[offset+3] = 0x00; //Padding octet - extensionHeader->buffer[offset+4] = 0x00; //No more extension headers + extensionHeader->buffer[offset + 1], + extensionHeader->buffer[offset + 2]); + extensionHeader->buffer[offset + 3] = 0x00; // Padding octet + extensionHeader->buffer[offset + 4] = 0x00; // No more extension headers /*Total size of DDD_status PDU = size of mandatory part + * 3 octets for highest transmitted/delivered PDCP SN + * 1 octet for padding + 1 octet for next extension header type, * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ - extensionHeader->length = 1+sizeof(DlDataDeliveryStatus_flagsT)+3+1+1; - gtpv1uCreateAndSendMsg( - h, peerIp, peerPort, GTP_GPDU, globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid, NULL, 0, false, false, 0, 0, NR_RAN_CONTAINER, extensionHeader->buffer, extensionHeader->length); + extensionHeader->length = 1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1; + gtpv1uCreateAndSendMsg(h, + peerIp, + peerPort, + GTP_GPDU, + globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid, + NULL, + 0, + false, + false, + 0, + 0, + NR_RAN_CONTAINER, + extensionHeader->buffer, + extensionHeader->length); } - LOG_D(GTPU,"[%d] Received a %d bytes packet for: teid:%x\n", h, - msgBufLen-offset, - ntohl(msgHdr->teid)); + LOG_D(GTPU, "[%d] Received a %d bytes packet for: teid:%x\n", h, msgBufLen - offset, ntohl(msgHdr->teid)); return !GTPNOK; } -void gtpv1uReceiver(int h) { - uint8_t udpData[65536]; - int udpDataLen; - socklen_t from_len; +void gtpv1uReceiver(int h) +{ + uint8_t udpData[65536]; + int udpDataLen; + socklen_t from_len; struct sockaddr_in addr; from_len = (socklen_t)sizeof(struct sockaddr_in); - if ((udpDataLen = recvfrom(h, udpData, sizeof(udpData), 0, - (struct sockaddr *)&addr, &from_len)) < 0) { + if ((udpDataLen = recvfrom(h, udpData, sizeof(udpData), 0, (struct sockaddr *)&addr, &from_len)) < 0) { LOG_E(GTPU, "[%d] Recvfrom failed (%s)\n", h, strerror(errno)); return; } else if (udpDataLen == 0) { LOG_W(GTPU, "[%d] Recvfrom returned 0\n", h); return; } else { - if ( udpDataLen < (int)sizeof(Gtpv1uMsgHeaderT) ) { + if (udpDataLen < (int)sizeof(Gtpv1uMsgHeaderT)) { LOG_W(GTPU, "[%d] received malformed gtp packet \n", h); return; } - Gtpv1uMsgHeaderT* msg=(Gtpv1uMsgHeaderT*) udpData; - if ( (int)(ntohs(msg->msgLength) + sizeof(Gtpv1uMsgHeaderT)) != udpDataLen ) { + Gtpv1uMsgHeaderT *msg = (Gtpv1uMsgHeaderT *)udpData; + if ((int)(ntohs(msg->msgLength) + sizeof(Gtpv1uMsgHeaderT)) != udpDataLen) { LOG_W(GTPU, "[%d] received malformed gtp packet length\n", h); return; } LOG_D(GTPU, "[%d] Received GTP data, msg type: %x\n", h, msg->msgType); - switch(msg->msgType) { + switch (msg->msgType) { case GTP_ECHO_RSP: break; case GTP_ECHO_REQ: - Gtpv1uHandleEchoReq( h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleEchoReq(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); break; case GTP_ERROR_INDICATION: - Gtpv1uHandleError( h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleError(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); break; case GTP_SUPPORTED_EXTENSION_HEADER_INDICATION: - Gtpv1uHandleSupportedExt( h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleSupportedExt(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); break; case GTP_END_MARKER: - Gtpv1uHandleEndMarker( h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleEndMarker(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); break; case GTP_GPDU: - Gtpv1uHandleGpdu( h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleGpdu(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); break; default: @@ -1238,8 +1267,9 @@ void gtpv1uReceiver(int h) { #include -void *gtpv1uTask(void *args) { - while(1) { +void *gtpv1uTask(void *args) +{ + while (1) { /* Trying to fetch a message from the message queue. If the queue is empty, this function will block till a message is sent to the task. @@ -1247,8 +1277,8 @@ void *gtpv1uTask(void *args) { MessageDef *message_p = NULL; itti_receive_msg(TASK_GTPV1_U, &message_p); - if (message_p != NULL ) { - openAddr_t addr= {{0}}; + if (message_p != NULL) { + openAddr_t addr = {{0}}; const instance_t myInstance = ITTI_MSG_DESTINATION_INSTANCE(message_p); const int msgType = ITTI_MSG_ID(message_p); LOG_D(GTPU, "GTP-U received %s for instance %ld\n", messages_info[msgType].name, myInstance); @@ -1278,8 +1308,8 @@ void *gtpv1uTask(void *args) { // to be dev: should be removed, to use API strcpy(addr.originHost, GTPV1U_REQ(message_p).localAddrStr); strcpy(addr.originService, GTPV1U_REQ(message_p).localPortStr); - strcpy(addr.destinationService,addr.originService); - AssertFatal((legacyInstanceMapping=gtpv1Init(addr))!=0,"Instance 0 reserved for legacy\n"); + strcpy(addr.destinationService, addr.originService); + AssertFatal((legacyInstanceMapping = gtpv1Init(addr)) != 0, "Instance 0 reserved for legacy\n"); break; default: @@ -1288,14 +1318,14 @@ void *gtpv1uTask(void *args) { break; } - AssertFatal(EXIT_SUCCESS==itti_free(TASK_GTPV1_U, message_p), "Failed to free memory!\n"); + AssertFatal(EXIT_SUCCESS == itti_free(TASK_GTPV1_U, message_p), "Failed to free memory!\n"); } struct epoll_event events[20]; int nb_events = itti_get_events(TASK_GTPV1_U, events, 20); for (int i = 0; i < nb_events; i++) - if ((events[i].events&EPOLLIN)) + if ((events[i].events & EPOLLIN)) gtpv1uReceiver(events[i].data.fd); } From 510814a79a02b1b6db639c8fb9d646c32aa203a5 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 3 Feb 2025 12:10:27 +0100 Subject: [PATCH 36/86] Remove unused struct member --- openair3/ocp-gtpu/gtp_itf.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 50c46b809f..65c5c9d118 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -109,7 +109,6 @@ typedef struct gtpv1u_bearer_s { typedef struct { map bearers; - teid_t outgoing_teid; } teidData_t; typedef struct { From 483d610a9b62eeedf6cb9c36e91025e21b6c5eb6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 4 Feb 2025 15:10:06 +0100 Subject: [PATCH 37/86] Reduce receiver mutex scope This seems to reduce packet loss on my machine. --- openair3/ocp-gtpu/gtp_itf.cpp | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 65c5c9d118..47f923e29c 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -1024,8 +1024,6 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t } pthread_mutex_lock(&globGtp.gtp_lock); - // the socket Linux file handler is the instance id - getInstRetInt(h); auto tunnel = globGtp.te2ue_mapping.find(ntohl(msgHdr->teid)); if (tunnel == globGtp.te2ue_mapping.end()) { @@ -1033,6 +1031,8 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; } + ueidData_t uedata = tunnel->second; + pthread_mutex_unlock(&globGtp.gtp_lock); /* see TS 29.281 5.1 */ // Minimum length of GTP-U header if non of the optional fields are present @@ -1109,17 +1109,9 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t // This context is not good for gtp // frame, ... has no meaning // manyother attributes may come from create tunnel - protocol_ctxt_t ctxt; - ctxt.module_id = 0; - ctxt.enb_flag = 1; - ctxt.instance = inst->addr.originInstance; - ctxt.rntiMaybeUEid = tunnel->second.ue_id; - ctxt.frame = 0; - ctxt.subframe = 0; - ctxt.eNB_index = 0; - ctxt.brOption = 0; + protocol_ctxt_t ctxt = { .enb_flag = 1, .rntiMaybeUEid = uedata.ue_id, }; const srb_flag_t srb_flag = SRB_FLAG_NO; - const rb_id_t rb_id = tunnel->second.incoming_rb_id; + const rb_id_t rb_id = uedata.incoming_rb_id; const mui_t mui = RLC_MUI_UNDEFINED; const confirm_t confirm = RLC_SDU_CONFIRM_NO; const sdu_size_t sdu_buffer_size = msgBufLen - offset; @@ -1127,12 +1119,11 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t const pdcp_transmission_mode_t mode = PDCP_TRANSMISSION_MODE_DATA; const uint32_t sourceL2Id = 0; const uint32_t destinationL2Id = 0; - pthread_mutex_unlock(&globGtp.gtp_lock); if (sdu_buffer_size > 0) { - if (qfi != -1 && tunnel->second.callBackSDAP) { - if (!tunnel->second.callBackSDAP(&ctxt, - tunnel->second.ue_id, + if (qfi != -1 && uedata.callBackSDAP) { + if (!uedata.callBackSDAP(&ctxt, + uedata.ue_id, srb_flag, rb_id, mui, @@ -1144,11 +1135,10 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t &destinationL2Id, qfi, rqi, - tunnel->second.pdusession_id)) + uedata.pdusession_id)) LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h); } else { - if (!tunnel->second - .callBack(&ctxt, srb_flag, rb_id, mui, confirm, sdu_buffer_size, sdu_buffer, mode, &sourceL2Id, &destinationL2Id)) + if (!uedata.callBack(&ctxt, srb_flag, rb_id, mui, confirm, sdu_buffer_size, sdu_buffer, mode, &sourceL2Id, &destinationL2Id)) LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h); } } From 7c34825daeaab3f096c028e10e5dcb5c34897262 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 24 Jul 2025 19:27:42 +0200 Subject: [PATCH 38/86] Receiver thread: independent of ITTI This also seems to reduce the packet loss on my machine. Especially in uplink there are no losses anymore. --- openair3/ocp-gtpu/gtp_itf.cpp | 35 +++++++++++++++++++++++++---------- openair3/ocp-gtpu/gtp_itf.h | 1 - 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 47f923e29c..1197767947 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -11,6 +11,7 @@ extern "C" { #include #include "common/platform_types.h" +#include "common/utils/system.h" #include #include "common/utils/LOG/log.h" #include @@ -120,12 +121,18 @@ typedef struct { int pdusession_id; } ueidData_t; +typedef struct { + int h; + pthread_t t; +} gtpThread_t; + class gtpEndPoint { public: openAddr_t addr; uint8_t foundAddr[20]; int foundAddrLen; int ipVersion; + gtpThread_t thrData; map ue2te_mapping; // we use the same port number for source and destination address // this allow using non standard gtp port number (different from 2152) @@ -505,18 +512,23 @@ static int udpServerSocket(openAddr_s addr) return sockfd; } +static void* gtpv1uReceiver(void *thr); instance_t gtpv1Init(openAddr_t context) { pthread_mutex_lock(&globGtp.gtp_lock); int id = udpServerSocket(context); if (id >= 0) { - itti_subscribe_event_fd(TASK_GTPV1_U, id); + LOG_I(GTPU, "Created gtpu instance id: %d\n", id); + getInstRetInt(compatInst(id)); + inst->thrData.h = id; + char name[32]; + snprintf(name, sizeof(name), "GTPrx_%d", id); + threadCreate(&inst->thrData.t, gtpv1uReceiver, &inst->thrData, name, -1, OAI_PRIORITY_RT); } else LOG_E(GTPU, "can't create GTP-U instance\n"); pthread_mutex_unlock(&globGtp.gtp_lock); - LOG_I(GTPU, "Created gtpu instance id: %d\n", id); return id; } @@ -1198,7 +1210,7 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t return !GTPNOK; } -void gtpv1uReceiver(int h) +static void gtpv1uReceiveHandleMessage(int h) { uint8_t udpData[65536]; int udpDataLen; @@ -1254,6 +1266,16 @@ void gtpv1uReceiver(int h) } } +static void* gtpv1uReceiver(void *thr) +{ + gtpThread_t *gt = (gtpThread_t *)thr; + while (true) { + gtpv1uReceiveHandleMessage(gt->h); + } + LOG_W(GTPU, "exiting thread\n"); + return NULL; +} + #include void *gtpv1uTask(void *args) @@ -1309,13 +1331,6 @@ void *gtpv1uTask(void *args) AssertFatal(EXIT_SUCCESS == itti_free(TASK_GTPV1_U, message_p), "Failed to free memory!\n"); } - - struct epoll_event events[20]; - int nb_events = itti_get_events(TASK_GTPV1_U, events, 20); - - for (int i = 0; i < nb_events; i++) - if ((events[i].events & EPOLLIN)) - gtpv1uReceiver(events[i].data.fd); } return NULL; diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index f0af60a5f7..4449cba244 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -76,7 +76,6 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; // the init function create a gtp instance and return the gtp instance id // the parameter originInstance will be sent back in each message from gtp to the creator - void gtpv1uReceiver(int h); void gtpv1uProcessTimeout(int handle,void *arg); int gtpv1u_create_s1u_tunnel(const instance_t instance, const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req, From 5d9db1688ac7747a02de9aa0e12ae3a033c083ab Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 30 Jul 2025 18:46:08 +0200 Subject: [PATCH 39/86] Refactor port number out of newGtpuCreateTunnel() The (remote) port number of the GTP module is configured during initialization. It is therefore not required when calling newGtpuCreateTunnel(), and could even lead to bugs (if another port number is used than specified during GTP init). --- openair3/ocp-gtpu/gtp_itf.cpp | 7 +------ openair3/ocp-gtpu/gtp_itf.h | 1 - tests/nr-cuup/nr-cuup-load-test.c | 16 ++++++++-------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 1197767947..6f7f300bea 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -568,7 +568,6 @@ teid_t newGtpuCreateTunnel(instance_t instance, teid_t outgoing_teid, int outgoing_qfi, transport_layer_addr_t remoteAddr, - int port, gtpCallback callBack, gtpCallbackSDAP callBackSDAP) { @@ -617,7 +616,7 @@ teid_t newGtpuCreateTunnel(instance_t instance, } tmp->teid_incoming = incoming_teid; - tmp->outgoing_port = port; + tmp->outgoing_port = inst->get_dstport(); tmp->teid_outgoing = outgoing_teid; tmp->outgoing_qfi = outgoing_qfi; pthread_mutex_unlock(&globGtp.gtp_lock); @@ -648,7 +647,6 @@ int gtpv1u_create_s1u_tunnel(instance_t instance, pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); - tcp_udp_port_t dstport = inst->get_dstport(); uint8_t addr[inst->foundAddrLen]; memcpy(addr, inst->foundAddr, inst->foundAddrLen); pthread_mutex_unlock(&globGtp.gtp_lock); @@ -665,7 +663,6 @@ int gtpv1u_create_s1u_tunnel(instance_t instance, create_tunnel_req->sgw_S1u_teid[i], -1, // no pdu session in 4G create_tunnel_req->sgw_addr[i], - dstport, callBack, NULL); create_tunnel_resp->status = 0; @@ -737,7 +734,6 @@ int gtpv1u_create_ngu_tunnel(const instance_t instance, pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); - tcp_udp_port_t dstport = inst->get_dstport(); uint8_t addr[inst->foundAddrLen]; memcpy(addr, inst->foundAddr, inst->foundAddrLen); pthread_mutex_unlock(&globGtp.gtp_lock); @@ -749,7 +745,6 @@ int gtpv1u_create_ngu_tunnel(const instance_t instance, create_tunnel_req->outgoing_teid[i], create_tunnel_req->outgoing_qfi[i], create_tunnel_req->dst_addr[i], - dstport, callBack, callBackSDAP); create_tunnel_resp->status = 0; diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index 4449cba244..d341acb311 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -114,7 +114,6 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; teid_t teid, int outgoing_qfi, transport_layer_addr_t remoteAddr, - int port, gtpCallback callBack, gtpCallbackSDAP callBackSDAP); diff --git a/tests/nr-cuup/nr-cuup-load-test.c b/tests/nr-cuup/nr-cuup-load-test.c index 48e088fc3c..0c417ca5aa 100644 --- a/tests/nr-cuup/nr-cuup-load-test.c +++ b/tests/nr-cuup/nr-cuup-load-test.c @@ -187,7 +187,7 @@ static bool recv_ng(protocol_ctxt_t *ctxt, } /* @brief Set up the NG-U (north-bound) GTP tunnel, and inform CU-UP via E1 */ -static up_params_t setup_cuup_ue_ng(sctp_assoc_t assoc_id, const e1ap_nssai_t *nssai, uint32_t ue_id, instance_t gtp_inst, const char *ip, uint16_t port) +static up_params_t setup_cuup_ue_ng(sctp_assoc_t assoc_id, const e1ap_nssai_t *nssai, uint32_t ue_id, instance_t gtp_inst, const char *ip) { long pdu_id = 1; long drb_id = pdu_id + 3; @@ -196,7 +196,7 @@ static up_params_t setup_cuup_ue_ng(sctp_assoc_t assoc_id, const e1ap_nssai_t *n /* create the local tunnel (i.e., as if we were UPF) */ transport_layer_addr_t null_addr = {.length = 32}; - teid_t teid_lo = newGtpuCreateTunnel(gtp_inst, ue_id, pdu_id, pdu_id, -1, -1, null_addr, port, NULL, recv_ng); + teid_t teid_lo = newGtpuCreateTunnel(gtp_inst, ue_id, pdu_id, pdu_id, -1, -1, null_addr, NULL, recv_ng); UP_TL_information_t tnl = {.teId = teid_lo}; memcpy(&tnl.tlAddress, &addr_lo, 4); @@ -233,7 +233,7 @@ static up_params_t setup_cuup_ue_ng(sctp_assoc_t assoc_id, const e1ap_nssai_t *n char ip_rm[32] = {0}; inet_ntop(AF_INET, &addr_rm, ip_rm, sizeof(ip_rm)); GtpuUpdateTunnelOutgoingAddressAndTeid(gtp_inst, ue_id, pdu_id, addr_rm, teid_rm); - LOG_I(GNB_APP, "CU-UP created NG-U, local %s/%x remote %s/%x (port %d)\n", ip_lo, teid_lo, ip_rm, teid_rm, port); + LOG_I(GNB_APP, "CU-UP created NG-U, local %s/%x remote %s/%x\n", ip_lo, teid_lo, ip_rm, teid_rm); inet_ntop(AF_INET, &f1_up.tl_info.tlAddress, ip_rm, sizeof(ip_rm)); LOG_I(GNB_APP, "CU-UP created F1-U, remove %s/%x\n", ip_rm, f1_up.tl_info.teId); @@ -286,7 +286,7 @@ static bool recv_f1(protocol_ctxt_t *ctxt, } /* @brief Set up the F1-U (south-bound) GTP tunnel, and inform CU-UP via E1 */ -static void setup_cuup_ue_f1(sctp_assoc_t assoc_id, uint32_t ue_id, instance_t gtp_inst, const char *ip, uint16_t port, up_params_t rm) +static void setup_cuup_ue_f1(sctp_assoc_t assoc_id, uint32_t ue_id, instance_t gtp_inst, const char *ip, up_params_t rm) { int pdu_id = 1; int drb_id = pdu_id + 3; @@ -294,7 +294,7 @@ static void setup_cuup_ue_f1(sctp_assoc_t assoc_id, uint32_t ue_id, instance_t g /* create tunnel (i.e., as if we were DU) */ transport_layer_addr_t addr = {.length = 32}; memcpy(addr.buffer, &rm.tl_info.tlAddress, 4); - teid_t teid_lo = newGtpuCreateTunnel(gtp_inst, ue_id, drb_id, drb_id, rm.tl_info.teId, -1, addr, port, recv_f1, NULL); + teid_t teid_lo = newGtpuCreateTunnel(gtp_inst, ue_id, drb_id, drb_id, rm.tl_info.teId, -1, addr, recv_f1, NULL); up_params_t lo = {.tl_info.teId = teid_lo, .cell_group_id = rm.cell_group_id}; inet_pton(AF_INET, ip, &lo.tl_info.tlAddress); @@ -319,7 +319,7 @@ static void setup_cuup_ue_f1(sctp_assoc_t assoc_id, uint32_t ue_id, instance_t g inet_ntop(AF_INET, &lo.tl_info.tlAddress, ip_lo, sizeof(ip_lo)); char ip_rm[32] = {0}; inet_ntop(AF_INET, &rm.tl_info.tlAddress, ip_rm, sizeof(ip_rm)); - LOG_I(GNB_APP, "CU-UP created F1-U, local %s/%x remote %s/%x (port %d)\n", ip_lo, lo.tl_info.teId, ip_rm, rm.tl_info.teId, port); + LOG_I(GNB_APP, "CU-UP created F1-U, local %s/%x remote %s/%x\n", ip_lo, lo.tl_info.teId, ip_rm, rm.tl_info.teId); } /* @brief release the UE over E1, and delete corresponding GTP tunnels */ @@ -609,8 +609,8 @@ int main(int argc, char *argv[]) * - setup_cuup_ue_f1(): E1 bearer context modification <=> send TEID for * F1-U DL (as if we had talked to the DU in the middle) */ for (int ue = 0; ue < n_ue; ++ue) { - up_params_t f1_up = setup_cuup_ue_ng(assoc_id, &nssai, ue, ng_inst, ng_ip, ng_port); - setup_cuup_ue_f1(assoc_id, ue, f1_inst, f1_ip, f1_port, f1_up); + up_params_t f1_up = setup_cuup_ue_ng(assoc_id, &nssai, ue, ng_inst, ng_ip); + setup_cuup_ue_f1(assoc_id, ue, f1_inst, f1_ip, f1_up); } usleep(1000); From 38ac898672258abdcf98bdfee149b91966727976 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 30 Jul 2025 19:06:20 +0200 Subject: [PATCH 40/86] Refactor GTP: pass addr structure directly This should simplify future transition to IPv6, as we can pass the right address directly. --- openair3/ocp-gtpu/gtp_itf.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 6f7f300bea..3099fbc455 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -922,7 +922,7 @@ int gtpv1u_delete_ngu_tunnel(const instance_t instance, gtpv1u_gnb_delete_tunnel return newGtpuDeleteTunnels(instance, req->ue_id, req->num_pdusession, req->pdusession_id); } -static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp) +static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, const struct sockaddr_in *addr) { Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf; @@ -940,8 +940,8 @@ static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint1 LOG_D(GTPU, "[%d] Received a echo request, TEID: %d, seq: %hu\n", h, msgHdr->teid, seq); uint8_t recovery[2] = {14, 0}; return gtpv1uCreateAndSendMsg(h, - peerIp, - peerPort, + addr->sin_addr.s_addr, + htons(addr->sin_port), GTP_ECHO_RSP, ntohl(msgHdr->teid), recovery, @@ -955,14 +955,14 @@ static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint1 0); } -static int Gtpv1uHandleError(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp) +static int Gtpv1uHandleError(int h, uint8_t *msgBuf, uint32_t msgBufLen, const struct sockaddr_in *addr) { LOG_E(GTPU, "Received GTP error indication (error handling is missing/not implemented)\n"); int rc = GTPNOK; return rc; } -static int Gtpv1uHandleSupportedExt(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp) +static int Gtpv1uHandleSupportedExt(int h, uint8_t *msgBuf, uint32_t msgBufLen, const sockaddr_in *addr) { LOG_E(GTPU, "Supported extensions to be dev\n"); int rc = GTPNOK; @@ -972,7 +972,7 @@ static int Gtpv1uHandleSupportedExt(int h, uint8_t *msgBuf, uint32_t msgBufLen, // When end marker arrives, we notify the client with buffer size = 0 // The client will likely call "delete tunnel" // nevertheless we don't take the initiative -static int Gtpv1uHandleEndMarker(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp) +static int Gtpv1uHandleEndMarker(int h, uint8_t *msgBuf, uint32_t msgBufLen, const sockaddr_in *addr) { Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf; @@ -1021,7 +1021,7 @@ static int Gtpv1uHandleEndMarker(int h, uint8_t *msgBuf, uint32_t msgBufLen, uin return !GTPNOK; } -static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t peerPort, uint32_t peerIp) +static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const struct sockaddr_in *addr) { Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf; @@ -1186,8 +1186,8 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, uint16_t * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ extensionHeader->length = 1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1; gtpv1uCreateAndSendMsg(h, - peerIp, - peerPort, + addr->sin_addr.s_addr, + htons(addr->sin_port), GTP_GPDU, globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid, NULL, @@ -1235,23 +1235,23 @@ static void gtpv1uReceiveHandleMessage(int h) break; case GTP_ECHO_REQ: - Gtpv1uHandleEchoReq(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleEchoReq(h, udpData, udpDataLen, &addr); break; case GTP_ERROR_INDICATION: - Gtpv1uHandleError(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleError(h, udpData, udpDataLen, &addr); break; case GTP_SUPPORTED_EXTENSION_HEADER_INDICATION: - Gtpv1uHandleSupportedExt(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleSupportedExt(h, udpData, udpDataLen, &addr); break; case GTP_END_MARKER: - Gtpv1uHandleEndMarker(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleEndMarker(h, udpData, udpDataLen, &addr); break; case GTP_GPDU: - Gtpv1uHandleGpdu(h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleGpdu(h, udpData, udpDataLen, &addr); break; default: From 9140b36a2d9fefd990c4e19a911d77acaedb6eae Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 4 Feb 2025 12:56:39 +0100 Subject: [PATCH 41/86] Refactor gtpv1u_bearer_t and use internally Reuse the gtpv1u_bearer_t for internal functions. Hopefully this will also simplify implementation of IPv6, as it encapsulates IP address handling within gtpv1u_bearer_t. --- openair3/ocp-gtpu/gtp_itf.cpp | 193 +++++++++++++++------------------- 1 file changed, 87 insertions(+), 106 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 3099fbc455..3bc214df62 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -96,13 +96,12 @@ typedef struct Gtpv1uExtHeader { #define GTP_END_MARKER (254) #define GTP_GPDU (255) +// GTP bearer context: for sending data typedef struct gtpv1u_bearer_s { - /* TEID used in dl and ul */ - teid_t teid_incoming; ///< eNB TEID - teid_t teid_outgoing; ///< Remote TEID - in_addr_t outgoing_ip_addr; - struct in6_addr outgoing_ip6_addr; - tcp_udp_port_t outgoing_port; + int sock_fd; + struct sockaddr_storage ip; + teid_t teid_incoming; + teid_t teid_outgoing; uint16_t seqNum; uint8_t npduNum; int outgoing_qfi; @@ -217,22 +216,16 @@ instance_t legacyInstanceMapping = 0; } #define HDR_MAX 256 // 256 is supposed to be larger than any gtp header -static int gtpv1uCreateAndSendMsg(int h, - uint32_t peerIp, - uint16_t peerPort, +static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, int msgType, - teid_t teid, uint8_t *Msg, int msgLen, bool seqNumFlag, bool npduNumFlag, - int seqNum, - int npduNum, int extHdrType, uint8_t *extensionHeader_buffer, uint8_t extensionHeader_length) { - LOG_D(GTPU, "Peer IP:%u peer port:%u outgoing teid:%u \n", peerIp, peerPort, teid); DevAssert(msgLen + HDR_MAX < 65536); // maximum size of UDP packet uint8_t buffer[msgLen + HDR_MAX]; @@ -247,14 +240,14 @@ static int gtpv1uCreateAndSendMsg(int h, msgHdr->PT = 1; msgHdr->version = 1; msgHdr->msgType = msgType; - msgHdr->teid = htonl(teid); + msgHdr->teid = htonl(bearer->teid_outgoing); curPtr += sizeof(Gtpv1uMsgHeaderT); if (seqNumFlag || (extHdrType != NO_MORE_EXT_HDRS) || npduNumFlag) { - *(uint16_t *)curPtr = seqNumFlag ? seqNum : 0x0000; + *(uint16_t *)curPtr = seqNumFlag ? bearer->seqNum : 0x0000; curPtr += sizeof(uint16_t); - *(uint8_t *)curPtr = npduNumFlag ? npduNum : 0x00; + *(uint8_t *)curPtr = npduNumFlag ? bearer->npduNum : 0x00; curPtr++; *(uint8_t *)curPtr = extHdrType; curPtr++; @@ -283,20 +276,20 @@ static int gtpv1uCreateAndSendMsg(int h, msgHdr->msgLength = htons(curPtr - (buffer + sizeof(Gtpv1uMsgHeaderT))); AssertFatal(curPtr - (buffer + msgLen) < HDR_MAX, "fixed max size of all headers too short"); - // Fix me: add IPv6 support, using flag ipVersion - struct sockaddr_in to = {0}; - to.sin_family = AF_INET; - to.sin_port = htons(peerPort); - to.sin_addr.s_addr = peerIp; - LOG_D(GTPU, "sending packet size: %ld to %s\n", curPtr - buffer, inet_ntoa(to.sin_addr)); - int ret; - if ((ret = sendto(h, (void *)buffer, curPtr - buffer, 0, (struct sockaddr *)&to, sizeof(to))) != curPtr - buffer) { + // Fix me: add IPv6 support + DevAssert(bearer->ip.ss_family == AF_INET); + struct sockaddr_in *to = (struct sockaddr_in *)&bearer->ip; + LOG_D(GTPU, + "Peer IP:" IPV4_ADDR " port:%u outgoing teid:%x\n", + IPV4_ADDR_FORMAT(to->sin_addr.s_addr), + htons(to->sin_port), + bearer->teid_outgoing); + int ret = sendto(bearer->sock_fd, buffer, curPtr - buffer, 0, (struct sockaddr *)to, sizeof(*to)); + if (ret != curPtr - buffer) { LOG_E(GTPU, - "[SD %d] Failed to send data to " IPV4_ADDR " on port %d, buffer size %lu, ret: %d, errno: %d\n", - h, - IPV4_ADDR_FORMAT(peerIp), - peerPort, + "[SD %d] Failed to send data buffer size %lu, ret: %d, errno: %d\n", + bearer->sock_fd, curPtr - buffer, ret, errno); @@ -343,49 +336,33 @@ void gtpv1uSendDirect(instance_t instance, ptr2->second.npduNum++; // copy to release the mutex - gtpv1u_bearer_t tmp = ptr2->second; + gtpv1u_bearer_t bearer = ptr2->second; pthread_mutex_unlock(&globGtp.gtp_lock); - if (tmp.outgoing_qfi != -1) { - Gtpv1uExtHeaderT ext = {0}; + Gtpv1uExtHeaderT *opt_ext = NULL; + Gtpv1uExtHeaderT ext = {0}; + if (bearer.outgoing_qfi != -1) { + opt_ext = &ext; ext.ExtHeaderLen = 1; // in quad bytes EXT_HDR_LNTH_OCTET_UNITS ext.pdusession_cntr.spare = 0; ext.pdusession_cntr.PDU_type = UL_PDU_SESSION_INFORMATION; - ext.pdusession_cntr.QFI = tmp.outgoing_qfi; + ext.pdusession_cntr.QFI = bearer.outgoing_qfi; ext.pdusession_cntr.Reflective_QoS_activation = false; ext.pdusession_cntr.Paging_Policy_Indicator = false; ext.NextExtHeaderType = NO_MORE_EXT_HDRS; - gtpv1uCreateAndSendMsg(compatInst(instance), - tmp.outgoing_ip_addr, - tmp.outgoing_port, - GTP_GPDU, - tmp.teid_outgoing, - buf, - len, - seqNumFlag, - npduNumFlag, - tmp.seqNum, - tmp.npduNum, - PDU_SESSION_CONTAINER, - (uint8_t *)&ext, - sizeof(ext)); - } else { - gtpv1uCreateAndSendMsg(compatInst(instance), - tmp.outgoing_ip_addr, - tmp.outgoing_port, - GTP_GPDU, - tmp.teid_outgoing, - buf, - len, - seqNumFlag, - npduNumFlag, - tmp.seqNum, - tmp.npduNum, - NO_MORE_EXT_HDRS, - NULL, - 0); } + + DevAssert(compatInst(instance) == bearer.sock_fd); + gtpv1uCreateAndSendMsg(&bearer, + GTP_GPDU, + buf, + len, + seqNumFlag, + npduNumFlag, + opt_ext ? PDU_SESSION_CONTAINER : NO_MORE_EXT_HDRS, + (uint8_t *)opt_ext, + sizeof(*opt_ext)); } static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_end_marker_req_t *req) @@ -424,22 +401,16 @@ static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_end_marker_req_t *re msgHdr.msgType = GTP_END_MARKER; msgHdr.msgLength = htons(0); msgHdr.teid = htonl(tmp.teid_outgoing); - // Fix me: add IPv6 support, using flag ipVersion - static struct sockaddr_in to = {0}; - to.sin_family = AF_INET; - to.sin_port = htons(tmp.outgoing_port); - to.sin_addr.s_addr = tmp.outgoing_ip_addr; - char ip4[INET_ADDRSTRLEN]; - // char ip6[INET6_ADDRSTRLEN]; - LOG_D(GTPU, "[%ld] sending end packet to %s\n", instance, inet_ntoa(to.sin_addr)); - if (sendto(compatInst(instance), (void *)&msgHdr, sizeof(msgHdr), 0, (struct sockaddr *)&to, sizeof(to)) != sizeof(msgHdr)) { - LOG_E(GTPU, - "[%ld] Failed to send data to %s on port %d, buffer size %lu\n", - compatInst(instance), - inet_ntop(AF_INET, &tmp.outgoing_ip_addr, ip4, INET_ADDRSTRLEN), - tmp.outgoing_port, - sizeof(msgHdr)); + // Fix me: add IPv6 support + DevAssert(instance == tmp.sock_fd); + DevAssert(tmp.ip.ss_family == AF_INET); + struct sockaddr_in *to = (struct sockaddr_in *)&tmp.ip; + LOG_D(GTPU, "[%ld] sending end packet to " IPV4_ADDR " port %d\n", instance, IPV4_ADDR_FORMAT(to->sin_addr.s_addr), htons(to->sin_port)); + + ssize_t ret = sendto(tmp.sock_fd, &msgHdr, sizeof(msgHdr), 0, (struct sockaddr *)to, sizeof(*to)); + if (ret != sizeof(msgHdr)) { + LOG_E(GTPU, "[%d] Failed to send data with buffer size %lu: ret %ld errno %d\n", tmp.sock_fd, sizeof(msgHdr), ret, errno); } } @@ -550,13 +521,17 @@ void GtpuUpdateTunnelOutgoingAddressAndTeid(instance_t instance, return; } - ptr2->second.outgoing_ip_addr = newOutgoingAddr; + struct sockaddr_in *sockaddr = (struct sockaddr_in *)&ptr2->second.ip; + sockaddr->sin_family = AF_INET; + memcpy(&sockaddr->sin_addr, &newOutgoingAddr, sizeof(newOutgoingAddr)); + AssertFatal(ptr2->second.ip.ss_family == AF_INET, "only IPv4 is supported\n"); ptr2->second.teid_outgoing = newOutgoingTeid; LOG_I(GTPU, - "[%ld] Tunnel Outgoing TEID updated to %x and address to %x\n", + "[%ld] Tunnel Outgoing TEID updated to %x and IPv4 address to " IPV4_ADDR "\n", instance, ptr2->second.teid_outgoing, - ptr2->second.outgoing_ip_addr); + IPV4_ADDR_FORMAT(sockaddr->sin_addr.s_addr)); + pthread_mutex_unlock(&globGtp.gtp_lock); return; } @@ -594,42 +569,48 @@ teid_t newGtpuCreateTunnel(instance_t instance, globGtp.te2ue_mapping[incoming_teid].callBackSDAP = callBackSDAP; globGtp.te2ue_mapping[incoming_teid].pdusession_id = (uint8_t)outgoing_bearer_id; - gtpv1u_bearer_t *tmp = &inst->ue2te_mapping[ue_id].bearers[outgoing_bearer_id]; + gtpv1u_bearer_t bearer = { + .sock_fd = (int) compatInst(instance), // avoid warning on narrowing conversion: instance is long, sock_fd is int + .teid_incoming = incoming_teid, + .teid_outgoing = outgoing_teid, + .outgoing_qfi = outgoing_qfi, + }; int addrs_length_in_bytes = remoteAddr.length / 8; - + struct sockaddr_in *sa4 = (struct sockaddr_in *)&bearer.ip; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&bearer.ip; switch (addrs_length_in_bytes) { case 4: - memcpy(&tmp->outgoing_ip_addr, remoteAddr.buffer, 4); + memcpy(&sa4->sin_addr, remoteAddr.buffer, 4); + sa4->sin_family = AF_INET; + sa4->sin_port = htons(inst->get_dstport()); break; case 16: - memcpy(tmp->outgoing_ip6_addr.s6_addr, remoteAddr.buffer, 16); + AssertFatal(false, "IPv6 not supported\n"); break; case 20: - memcpy(&tmp->outgoing_ip_addr, remoteAddr.buffer, 4); - memcpy(tmp->outgoing_ip6_addr.s6_addr, remoteAddr.buffer + 4, 16); + AssertFatal(false, "dual-IPv4/v6 not supported\n"); + break; default: AssertFatal(false, "SGW Address size impossible"); } - tmp->teid_incoming = incoming_teid; - tmp->outgoing_port = inst->get_dstport(); - tmp->teid_outgoing = outgoing_teid; - tmp->outgoing_qfi = outgoing_qfi; + inst->ue2te_mapping[ue_id].bearers[outgoing_bearer_id] = bearer; pthread_mutex_unlock(&globGtp.gtp_lock); char ip4[INET_ADDRSTRLEN]; char ip6[INET6_ADDRSTRLEN]; LOG_I(GTPU, - "[%ld] Created tunnel for UE ID %lu, teid for incoming: %x, teid for outgoing %x to remote IPv4: %s, IPv6 %s\n", + "[%ld] Created tunnel for UE ID %lu, teid for incoming: %x, teid for outgoing %x to remote IPv4: %s, IPv6 %s, port %d\n", instance, ue_id, - tmp->teid_incoming, - tmp->teid_outgoing, - inet_ntop(AF_INET, (void *)&tmp->outgoing_ip_addr, ip4, INET_ADDRSTRLEN), - inet_ntop(AF_INET6, (void *)&tmp->outgoing_ip6_addr.s6_addr, ip6, INET6_ADDRSTRLEN)); + bearer.teid_incoming, + bearer.teid_outgoing, + inet_ntop(AF_INET, &sa4->sin_addr, ip4, INET_ADDRSTRLEN), + inet_ntop(AF_INET6, &sa6->sin6_addr, ip6, INET6_ADDRSTRLEN), + ntohs(sa4->sin_port)); return incoming_teid; } @@ -922,6 +903,13 @@ int gtpv1u_delete_ngu_tunnel(const instance_t instance, gtpv1u_gnb_delete_tunnel return newGtpuDeleteTunnels(instance, req->ue_id, req->num_pdusession, req->pdusession_id); } +static gtpv1u_bearer_t create_bearer(int socket, const struct sockaddr_in *addr, uint32_t teid, uint16_t seq) +{ + gtpv1u_bearer_t bearer = {.sock_fd = socket, .teid_outgoing = teid, .seqNum = seq}; + memcpy(&bearer.ip, addr, sizeof(*addr)); + return bearer; +} + static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, const struct sockaddr_in *addr) { Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf; @@ -939,17 +927,13 @@ static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, const uint16_t seq = ntohs(*(uint16_t *)(msgHdr + 1)); LOG_D(GTPU, "[%d] Received a echo request, TEID: %d, seq: %hu\n", h, msgHdr->teid, seq); uint8_t recovery[2] = {14, 0}; - return gtpv1uCreateAndSendMsg(h, - addr->sin_addr.s_addr, - htons(addr->sin_port), + gtpv1u_bearer_t bearer = create_bearer(h, addr, ntohl(msgHdr->teid), seq); + return gtpv1uCreateAndSendMsg(&bearer, GTP_ECHO_RSP, - ntohl(msgHdr->teid), recovery, sizeof recovery, true, false, - seq, - 0, NO_MORE_EXT_HDRS, NULL, 0); @@ -1185,17 +1169,14 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st * 1 octet for padding + 1 octet for next extension header type, * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ extensionHeader->length = 1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1; - gtpv1uCreateAndSendMsg(h, - addr->sin_addr.s_addr, - htons(addr->sin_port), + uint32_t teid = globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid; + gtpv1u_bearer_t bearer = create_bearer(h, addr, teid, 0); + gtpv1uCreateAndSendMsg(&bearer, GTP_GPDU, - globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid, NULL, 0, false, false, - 0, - 0, NR_RAN_CONTAINER, extensionHeader->buffer, extensionHeader->length); From 8d5501edc283c244111fb6c6a8cd7e3f80eb9d75 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 30 Jul 2025 13:35:11 +0200 Subject: [PATCH 42/86] Harmonize GTP log messages Make a common pattern, which makes it simpler to find them in gNB logs, and, maybe more importantly, link tunnel creation and update (the update message was missing the "incoming" TEID, so it is not easy to link to creation apart from the UE ID, but even then the UE might have multiple tunnels). --- openair3/ocp-gtpu/gtp_itf.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 3bc214df62..fec0f035eb 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -526,11 +526,19 @@ void GtpuUpdateTunnelOutgoingAddressAndTeid(instance_t instance, memcpy(&sockaddr->sin_addr, &newOutgoingAddr, sizeof(newOutgoingAddr)); AssertFatal(ptr2->second.ip.ss_family == AF_INET, "only IPv4 is supported\n"); ptr2->second.teid_outgoing = newOutgoingTeid; + char ip4[INET_ADDRSTRLEN]; + char ip6[INET6_ADDRSTRLEN]; + struct sockaddr_in *sa4 = (struct sockaddr_in *)sockaddr; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sockaddr; LOG_I(GTPU, - "[%ld] Tunnel Outgoing TEID updated to %x and IPv4 address to " IPV4_ADDR "\n", + "[%ld] UE ID %ld: Update tunnel TEID incoming %x outgoing %x to remote IPv4 %s, IPv6 %s, port %d\n", instance, + ue_id, + ptr2->second.teid_incoming, ptr2->second.teid_outgoing, - IPV4_ADDR_FORMAT(sockaddr->sin_addr.s_addr)); + inet_ntop(AF_INET, &sa4->sin_addr, ip4, INET_ADDRSTRLEN), + inet_ntop(AF_INET6, &sa6->sin6_addr, ip6, INET6_ADDRSTRLEN), + ntohs(sa4->sin_port)); pthread_mutex_unlock(&globGtp.gtp_lock); return; @@ -603,7 +611,7 @@ teid_t newGtpuCreateTunnel(instance_t instance, char ip4[INET_ADDRSTRLEN]; char ip6[INET6_ADDRSTRLEN]; LOG_I(GTPU, - "[%ld] Created tunnel for UE ID %lu, teid for incoming: %x, teid for outgoing %x to remote IPv4: %s, IPv6 %s, port %d\n", + "[%ld] UE ID %ld: Create tunnel TEID incoming %x outgoing %x to remote IPv4 %s, IPv6 %s, port %d\n", instance, ue_id, bearer.teid_incoming, @@ -817,7 +825,7 @@ int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id) inst->ue2te_mapping.erase(ptrUe); pthread_mutex_unlock(&globGtp.gtp_lock); - LOG_I(GTPU, "[%ld] Deleted all tunnels for ue id %ld (%d tunnels deleted)\n", instance, ue_id, nb); + LOG_I(GTPU, "[%ld] UE ID %ld: Delete all tunnels (%d tunnels)\n", instance, ue_id, nb); return !GTPNOK; } From 0a5130cf106c50a6403db320e015f35578f48a96 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 31 Jul 2025 08:48:24 +0200 Subject: [PATCH 43/86] Remove unused control port number from F1 Remove these F1-C port numbers. The configuration is not actually applied, and the default F1-C port number 38472 (see F1AP_PORT_NUMBER) is hardcoded in the F1 module. For the moment, I leave the configuration option, in case we want to allow to configure the port numbers later). --- ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf | 2 -- ci-scripts/conf_files/gnb-cu.sa.f1.conf | 2 -- ci-scripts/conf_files/gnb-cucp.sa.f1.conf | 2 -- ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf | 2 -- ci-scripts/conf_files/gnb-cuup.sa.f1.conf | 2 -- ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf | 2 -- ci-scripts/conf_files/gnb-du.sa.band1.52prb.usrpb210.conf | 2 -- ci-scripts/conf_files/gnb-du.sa.band78.106prb.rfsim.conf | 2 -- ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf | 2 -- doc/F1AP/F1-design.md | 2 -- targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf | 2 -- targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf | 2 -- targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf | 2 -- .../GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf | 2 -- .../GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf | 2 -- .../CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf | 2 -- ...gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf | 2 -- .../CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf | 2 -- .../GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf | 2 -- .../GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf | 2 -- 20 files changed, 40 deletions(-) diff --git a/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf b/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf index 311c8551fd..051ebd04fc 100644 --- a/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf +++ b/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf @@ -23,9 +23,7 @@ gNBs = local_s_address = "192.168.71.150"; remote_s_address = "192.168.71.171"; - local_s_portc = 501; local_s_portd = 2153; - remote_s_portc = 500; remote_s_portd = 2153; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-cu.sa.f1.conf b/ci-scripts/conf_files/gnb-cu.sa.f1.conf index 84e5dbadb0..280281997e 100644 --- a/ci-scripts/conf_files/gnb-cu.sa.f1.conf +++ b/ci-scripts/conf_files/gnb-cu.sa.f1.conf @@ -23,9 +23,7 @@ gNBs = local_s_address = "127.0.0.4"; remote_s_address = "127.0.0.5"; - local_s_portc = 501; local_s_portd = 2153; - remote_s_portc = 500; remote_s_portd = 2153; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-cucp.sa.f1.conf b/ci-scripts/conf_files/gnb-cucp.sa.f1.conf index 28bb7cfc33..214b5dd519 100644 --- a/ci-scripts/conf_files/gnb-cucp.sa.f1.conf +++ b/ci-scripts/conf_files/gnb-cucp.sa.f1.conf @@ -23,9 +23,7 @@ gNBs = local_s_address = "192.168.71.150"; remote_s_address = "0.0.0.0"; # multiple DUs - local_s_portc = 501; local_s_portd = 2152; - remote_s_portc = 500; remote_s_portd = 2152; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf b/ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf index 8cebc8640a..6a76eb7689 100644 --- a/ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf +++ b/ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf @@ -23,9 +23,7 @@ gNBs = local_s_address = "127.0.0.4"; remote_s_address = "127.0.0.5"; - local_s_portc = 501; local_s_portd = 2153; - remote_s_portc = 500; remote_s_portd = 2153; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-cuup.sa.f1.conf b/ci-scripts/conf_files/gnb-cuup.sa.f1.conf index 5dd6c77a9c..e87ac862b1 100644 --- a/ci-scripts/conf_files/gnb-cuup.sa.f1.conf +++ b/ci-scripts/conf_files/gnb-cuup.sa.f1.conf @@ -22,9 +22,7 @@ gNBs = local_s_address = "192.168.72.161"; remote_s_address = "192.168.72.171"; - local_s_portc = 501; local_s_portd = 2153; - remote_s_portc = 500; remote_s_portd = 2153; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf b/ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf index b9f65cdf53..dbe510ea3f 100644 --- a/ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf +++ b/ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf @@ -22,9 +22,7 @@ gNBs = local_s_address = "127.0.0.6"; remote_s_address = "127.0.0.5"; - local_s_portc = 501; local_s_portd = 2153; - remote_s_portc = 500; remote_s_portd = 2153; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-du.sa.band1.52prb.usrpb210.conf b/ci-scripts/conf_files/gnb-du.sa.band1.52prb.usrpb210.conf index 2af3185ffc..50bed1fb5d 100644 --- a/ci-scripts/conf_files/gnb-du.sa.band1.52prb.usrpb210.conf +++ b/ci-scripts/conf_files/gnb-du.sa.band1.52prb.usrpb210.conf @@ -163,9 +163,7 @@ MACRLCs = ( tr_n_preference = "f1"; local_n_address = "127.0.0.5"; remote_n_address = "127.0.0.4"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 200; pucch_TargetSNRx10 = 200; diff --git a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.rfsim.conf b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.rfsim.conf index fce5e6dc6e..328efe1b90 100644 --- a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.rfsim.conf +++ b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.rfsim.conf @@ -168,9 +168,7 @@ MACRLCs = ( tr_n_preference = "f1"; local_n_address = "192.168.71.171"; remote_n_address = "192.168.71.150"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 200; pucch_TargetSNRx10 = 200; diff --git a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf index 45482642b9..408111630f 100644 --- a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf +++ b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf @@ -195,9 +195,7 @@ MACRLCs = ( tr_n_preference = "f1"; local_n_address = "127.0.0.5"; remote_n_address = "127.0.0.4"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 200; pucch_TargetSNRx10 = 200; diff --git a/doc/F1AP/F1-design.md b/doc/F1AP/F1-design.md index 65ea6b38cf..afc1e268b9 100644 --- a/doc/F1AP/F1-design.md +++ b/doc/F1AP/F1-design.md @@ -221,9 +221,7 @@ In the CU file: ignored, but we recommend to put `0.0.0.0` ("any") - Ports should match the ones in the DU config, but for simplicity and standards-conformity, simply set all to 2152: - - `local_s_portc` in CU should match `remote_n_portc` in DU - `local_s_portd` in CU should match `remote_n_portd` in DU - - `remote_s_portc` in CU should match `local_n_portc` in DU - `remote_s_portd` in CU should match `local_n_portd` in DU In the DU file: diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf index 1666ea4a70..3261f716c7 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf @@ -22,9 +22,7 @@ gNBs = local_s_address = "127.0.0.4"; remote_s_address = "127.0.0.3"; - local_s_portc = 501; local_s_portd = 2152; - remote_s_portc = 500; remote_s_portd = 2152; # ------- SCTP definitions diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf index 02b1e89b88..ef180cab63 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf @@ -168,9 +168,7 @@ MACRLCs = ( tr_n_preference = "f1"; local_n_address = "127.0.0.3"; remote_n_address = "127.0.0.4"; - local_n_portc = 500; local_n_portd = 2152; - remote_n_portc = 501; remote_n_portd = 2152; } diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf index f57725cc9b..87190eef4f 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf @@ -20,9 +20,7 @@ gNBs = local_s_if_name = "lo"; local_s_address = "127.0.0.3"; remote_s_address = "0.0.0.0"; # multiple DUs - local_s_portc = 501; local_s_portd = 2152; - remote_s_portc = 500; remote_s_portd = 2152; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf index c2e28725bd..c2401c29c5 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf @@ -166,9 +166,7 @@ MACRLCs = ({ local_n_if_name = "lo"; local_n_address = "127.0.0.4"; remote_n_address = "127.0.0.3"; - local_n_portc = 500; local_n_portd = 2152; - remote_n_portc = 501; remote_n_portd = 2152; pusch_FailureThres = 1000; }); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf index c5c84c9919..f2e7113e74 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf @@ -166,9 +166,7 @@ MACRLCs = ({ local_n_if_name = "lo"; local_n_address = "127.0.0.5"; remote_n_address = "127.0.0.3"; - local_n_portc = 500; local_n_portd = 2152; - remote_n_portc = 501; remote_n_portd = 2152; pusch_FailureThres = 1000; }); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf index a267d2e864..08548e62d6 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf @@ -175,9 +175,7 @@ MACRLCs = ( local_n_if_name = "enp193s0f0"; local_n_address = "172.21.16.51"; remote_n_address = "172.21.6.90"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 170; pucch_TargetSNRx10 = 230; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf index 28f9baf6f8..615ff5f3e4 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf @@ -175,9 +175,7 @@ MACRLCs = ( local_n_if_name = "enp193s0f0"; local_n_address = "172.21.16.51"; remote_n_address = "172.21.6.90"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 120; pucch_TargetSNRx10 = 200; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf index 18f885945f..163871e45a 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf @@ -175,9 +175,7 @@ MACRLCs = ( local_n_if_name = "enp193s0f0"; local_n_address = "172.21.16.51"; remote_n_address = "172.21.6.90"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 120; pucch_TargetSNRx10 = 200; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf index 3221813bef..97acbdc54e 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf @@ -163,9 +163,7 @@ MACRLCs = ({ local_n_if_name = "lo"; local_n_address = "127.0.0.4"; remote_n_address = "127.0.0.3"; - local_n_portc = 500; local_n_portd = 2152; - remote_n_portc = 501; remote_n_portd = 2152; pusch_FailureThres = 1000; }); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf index c7d8cb3269..b05e1de4a4 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf @@ -163,9 +163,7 @@ MACRLCs = ({ local_n_if_name = "lo"; local_n_address = "127.0.0.5"; remote_n_address = "127.0.0.3"; - local_n_portc = 500; local_n_portd = 2152; - remote_n_portc = 501; remote_n_portd = 2152; pusch_FailureThres = 1000; }); From faef56e4323f4d9581010fc1d82159834d86cd74 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 31 Jul 2025 08:51:29 +0200 Subject: [PATCH 44/86] Remove old 4G F1 configuration files 4G F1 has been removed from the code base, so these configs are now useless. See also commit 9dc2282249d1 ("Remove 4G F1") --- .../PROJECTS/GENERIC-LTE-EPC/CONF/cu.lte.conf | 196 ------------------ .../CONF/du.lte.band7.10MHz.if4p5.conf | 102 --------- 2 files changed, 298 deletions(-) delete mode 100644 targets/PROJECTS/GENERIC-LTE-EPC/CONF/cu.lte.conf delete mode 100644 targets/PROJECTS/GENERIC-LTE-EPC/CONF/du.lte.band7.10MHz.if4p5.conf diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/cu.lte.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/cu.lte.conf deleted file mode 100644 index e486be3605..0000000000 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/cu.lte.conf +++ /dev/null @@ -1,196 +0,0 @@ -Active_eNBs = ( "eNB-CU-Eurecom-LTEBox"); -# Asn1_verbosity, choice in: none, info, annoying -Asn1_verbosity = "none"; - -eNBs = ( - { - ////////// Identification parameters: - eNB_ID = 0xe00; - - cell_type = "CELL_MACRO_ENB"; - - eNB_name = "eNB-CU-Eurecom-LTEBox"; - - // Tracking area code, 0x0000 and 0xfffe are reserved values - tracking_area_code = 1; - plmn_list = ( { mcc = 208; mnc = 95; mnc_length = 2; } ) - - nr_cellid = 12345678L - - tr_s_preference = "f1" - - remote_s_address = "192.168.12.4"; - local_s_address = "192.168.12.45"; - local_s_portc = 501; - remote_s_portc = 500; - local_s_portd = 601; - remote_s_portd = 600; - - ////////// Physical parameters: - - component_carriers = ( - { - node_function = "3GPP_eNodeB"; - node_timing = "synch_to_ext_device"; - node_synch_ref = 0; - frame_type = "FDD"; - tdd_config = 3; - tdd_config_s = 0; - prefix_type = "NORMAL"; - eutra_band = 7; - downlink_frequency = 2665000000L; - uplink_frequency_offset = -120000000; - Nid_cell = 0; - N_RB_DL = 50; - pbch_repetition = "FALSE"; - prach_root = 0; - prach_config_index = 0; - prach_high_speed = "DISABLE"; - prach_zero_correlation = 1; - prach_freq_offset = 2; - pucch_delta_shift = 1; - pucch_nRB_CQI = 0; - pucch_nCS_AN = 0; - pucch_n1_AN = 0; - pdsch_referenceSignalPower = -27; - pdsch_p_b = 0; - pusch_n_SB = 1; - pusch_enable64QAM = "DISABLE"; - pusch_hoppingMode = "interSubFrame"; - pusch_hoppingOffset = 0; - pusch_groupHoppingEnable = "ENABLE"; - pusch_groupAssignment = 0; - pusch_sequenceHoppingEnabled = "DISABLE"; - pusch_nDMRS1 = 1; - phich_duration = "NORMAL"; - phich_resource = "ONESIXTH"; - srs_enable = "DISABLE"; - /* - srs_BandwidthConfig =; - srs_SubframeConfig =; - srs_ackNackST =; - srs_MaxUpPts =; - */ - - pusch_p0_Nominal = -96; - pusch_alpha = "AL1"; - pucch_p0_Nominal = -104; - msg3_delta_Preamble = 6; - pucch_deltaF_Format1 = "deltaF2"; - pucch_deltaF_Format1b = "deltaF3"; - pucch_deltaF_Format2 = "deltaF0"; - pucch_deltaF_Format2a = "deltaF0"; - pucch_deltaF_Format2b = "deltaF0"; - - rach_numberOfRA_Preambles = 64; - rach_preamblesGroupAConfig = "DISABLE"; - /* - rach_sizeOfRA_PreamblesGroupA = ; - rach_messageSizeGroupA = ; - rach_messagePowerOffsetGroupB = ; - */ - rach_powerRampingStep = 4; - rach_preambleInitialReceivedTargetPower = -108; - rach_preambleTransMax = 10; - rach_raResponseWindowSize = 10; - rach_macContentionResolutionTimer = 48; - rach_maxHARQ_Msg3Tx = 4; - - pcch_default_PagingCycle = 128; - pcch_nB = "oneT"; - bcch_modificationPeriodCoeff= 2; - ue_TimersAndConstants_t300 = 1000; - ue_TimersAndConstants_t301 = 1000; - ue_TimersAndConstants_t310 = 1000; - ue_TimersAndConstants_t311 = 10000; - ue_TimersAndConstants_n310 = 20; - ue_TimersAndConstants_n311 = 1; - ue_TransmissionMode = 1; - - //Parameters for SIB18 - rxPool_sc_CP_Len = "normal"; - rxPool_sc_Period = "sf40"; - rxPool_data_CP_Len = "normal"; - rxPool_ResourceConfig_prb_Num = 20; - rxPool_ResourceConfig_prb_Start = 5; - rxPool_ResourceConfig_prb_End = 44; - rxPool_ResourceConfig_offsetIndicator_present = "prSmall"; - rxPool_ResourceConfig_offsetIndicator_choice = 0; - rxPool_ResourceConfig_subframeBitmap_present = "prBs40"; - rxPool_ResourceConfig_subframeBitmap_choice_bs_buf = "00000000000000000000"; - rxPool_ResourceConfig_subframeBitmap_choice_bs_size = 5; - rxPool_ResourceConfig_subframeBitmap_choice_bs_bits_unused = 0; - /* - rxPool_dataHoppingConfig_hoppingParameter = 0; - rxPool_dataHoppingConfig_numSubbands = "ns1"; - rxPool_dataHoppingConfig_rbOffset = 0; - rxPool_commTxResourceUC-ReqAllowed = "TRUE"; - */ - - // Parameters for SIB19 - discRxPool_cp_Len = "normal" - discRxPool_discPeriod = "rf32" - discRxPool_numRetx = 1; - discRxPool_numRepetition = 2; - discRxPool_ResourceConfig_prb_Num = 5; - discRxPool_ResourceConfig_prb_Start = 3; - discRxPool_ResourceConfig_prb_End = 21; - discRxPool_ResourceConfig_offsetIndicator_present = "prSmall"; - discRxPool_ResourceConfig_offsetIndicator_choice = 0; - discRxPool_ResourceConfig_subframeBitmap_present = "prBs40"; - discRxPool_ResourceConfig_subframeBitmap_choice_bs_buf = "f0ffffffff"; - discRxPool_ResourceConfig_subframeBitmap_choice_bs_size = 5; - discRxPool_ResourceConfig_subframeBitmap_choice_bs_bits_unused = 0; - } - ); - - - srb1_parameters : - { - # timer_poll_retransmit = (ms) [5, 10, 15, 20,... 250, 300, 350, ... 500] - timer_poll_retransmit = 80; - - # timer_reordering = (ms) [0,5, ... 100, 110, 120, ... ,200] - timer_reordering = 35; - - # timer_reordering = (ms) [0,5, ... 250, 300, 350, ... ,500] - timer_status_prohibit = 0; - - # poll_pdu = [4, 8, 16, 32 , 64, 128, 256, infinity(>10000)] - poll_pdu = 4; - - # poll_byte = (kB) [25,50,75,100,125,250,375,500,750,1000,1250,1500,2000,3000,infinity(>10000)] - poll_byte = 99999; - - # max_retx_threshold = [1, 2, 3, 4 , 6, 8, 16, 32] - max_retx_threshold = 4; - } - - # ------- SCTP definitions - SCTP : - { - # Number of streams to use in input/output - SCTP_INSTREAMS = 2; - SCTP_OUTSTREAMS = 2; - }; - - - ////////// MME parameters: - mme_ip_address = ({ ipv4 = "127.0.1.10"; }); - - NETWORK_INTERFACES : { - ENB_IPV4_ADDRESS_FOR_S1_MME = "127.0.1.30/24"; - ENB_IPV4_ADDRESS_FOR_S1U = "127.0.1.30/24"; - ENB_PORT_FOR_S1U = 2152; # Spec 2152 - ENB_IPV4_ADDRESS_FOR_X2C = "127.0.1.30/24"; - ENB_PORT_FOR_X2C = 36422; # Spec 36422 - }; - } -); - -log_config = { - global_log_level = "info"; - pdcp_log_level = "info"; - rrc_log_level = "info"; - gtp_log_level = "info"; -}; diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/du.lte.band7.10MHz.if4p5.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/du.lte.band7.10MHz.if4p5.conf deleted file mode 100644 index f66bc1b29e..0000000000 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/du.lte.band7.10MHz.if4p5.conf +++ /dev/null @@ -1,102 +0,0 @@ -Active_eNBs = ( "eNB-Eurecom-DU"); -# Asn1_verbosity, choice in: none, info, annoying -Asn1_verbosity = "none"; - -eNBs = -( - { - ////////// Identification parameters: - eNB_CU_ID = 0xe00; - - eNB_name = "eNB-Eurecom-DU"; - - // Tracking area code, 0x0000 and 0xfffe are reserved values - tracking_area_code = 1; - plmn_list = ( { mcc = 208; mnc = 95; mnc_length = 2; } ) - - nr_cellid = 12345678L - - ////////// Physical parameters: - - component_carriers = ( - { - node_function = "3GPP_eNODEB"; - node_timing = "synch_to_ext_device"; - node_synch_ref = 0; - frame_type = "FDD"; - tdd_config = 3; - tdd_config_s = 0; - prefix_type = "NORMAL"; - eutra_band = 7; - downlink_frequency = 2665000000L; - uplink_frequency_offset = -120000000; - Nid_cell = 0; - N_RB_DL = 50; - Nid_cell_mbsfn = 0; - nb_antenna_ports = 1; - nb_antennas_tx = 1; - nb_antennas_rx = 1; - tx_gain = 90; - rx_gain = 125; - - pucch_deltaF_Format1 = "deltaF2"; - pucch_deltaF_Format1b = "deltaF3"; - pucch_deltaF_Format2 = "deltaF0"; - pucch_deltaF_Format2a = "deltaF0"; - pucch_deltaF_Format2b = "deltaF0"; - } - ); - - - # ------- SCTP definitions - SCTP : - { - # Number of streams to use in input/output - SCTP_INSTREAMS = 2; - SCTP_OUTSTREAMS = 2; - }; - } -); - -MACRLCs = ( - { - num_cc = 1; - tr_s_preference = "local_L1"; - tr_n_preference = "f1"; - remote_n_address = "127.0.0.4"; - local_n_address = "127.0.0.3"; - local_n_portc = 500; - remote_n_portc = 501; - local_n_portd = 600; - remote_n_portd = 601; - } -); - -L1s = ( - { - num_cc = 1; - tr_n_preference = "local_mac"; - } -); - -RUs = ( - { - local_rf = "yes"; - nb_tx = 1; - nb_rx = 1; - att_tx = 10; - att_rx = 10; - bands = [7]; - max_pdschReferenceSignalPower = -27; - max_rxgain = 125; - eNB_instances = [0]; - } -); - -log_config = { - global_log_level = "info"; - hw_log_level = "info"; - phy_log_level = "info"; - mac_log_level = "info"; - rlc_log_level = "info"; -}; From 6bf88bd3d9f8db8bffe7ac3e09aaefc3bb838486 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 4 Aug 2025 08:36:42 +0200 Subject: [PATCH 45/86] Remove F1 interface names from configuration files These options are not taken into account, they are not even defined in the configuration module. --- targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf | 1 - .../GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf | 1 - .../GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf | 1 - .../CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf | 1 - .../gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf | 1 - .../CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf | 1 - .../GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf | 1 - .../GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf | 1 - 8 files changed, 8 deletions(-) diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf index 87190eef4f..102c23018e 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf @@ -17,7 +17,6 @@ gNBs = tr_s_preference = "f1"; - local_s_if_name = "lo"; local_s_address = "127.0.0.3"; remote_s_address = "0.0.0.0"; # multiple DUs local_s_portd = 2152; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf index c2401c29c5..f2ac964033 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf @@ -163,7 +163,6 @@ MACRLCs = ({ num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "lo"; local_n_address = "127.0.0.4"; remote_n_address = "127.0.0.3"; local_n_portd = 2152; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf index f2e7113e74..96acca0d10 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf @@ -163,7 +163,6 @@ MACRLCs = ({ num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "lo"; local_n_address = "127.0.0.5"; remote_n_address = "127.0.0.3"; local_n_portd = 2152; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf index 08548e62d6..5c6506beb3 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf @@ -172,7 +172,6 @@ MACRLCs = ( num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "enp193s0f0"; local_n_address = "172.21.16.51"; remote_n_address = "172.21.6.90"; local_n_portd = 2153; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf index 615ff5f3e4..522266a70e 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf @@ -172,7 +172,6 @@ MACRLCs = ( num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "enp193s0f0"; local_n_address = "172.21.16.51"; remote_n_address = "172.21.6.90"; local_n_portd = 2153; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf index 163871e45a..90692c7b8e 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf @@ -172,7 +172,6 @@ MACRLCs = ( num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "enp193s0f0"; local_n_address = "172.21.16.51"; remote_n_address = "172.21.6.90"; local_n_portd = 2153; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf index 97acbdc54e..fa456a03cf 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf @@ -160,7 +160,6 @@ MACRLCs = ({ num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "lo"; local_n_address = "127.0.0.4"; remote_n_address = "127.0.0.3"; local_n_portd = 2152; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf index b05e1de4a4..350ce578d3 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf @@ -160,7 +160,6 @@ MACRLCs = ({ num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "lo"; local_n_address = "127.0.0.5"; remote_n_address = "127.0.0.3"; local_n_portd = 2152; From 1becaf06714c288212cb1f9907f09e3b0df6b1cf Mon Sep 17 00:00:00 2001 From: Sakthivel Velumani Date: Wed, 30 Jul 2025 15:42:02 -0400 Subject: [PATCH 46/86] Fix periodic CSI scheduling in UE Changed the search structure to have periodic resoruce id in the outer loop and configured resource set as inner loop to prevent configuring resource that is not needed. Use const for input pointers. --- openair2/LAYER2/NR_MAC_UE/mac_proto.h | 2 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 21 ++++++++++---------- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index aacbbdcab2..dd29351629 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -289,7 +289,7 @@ void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP); void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot); void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot); void configure_csi_resource_mapping(fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu, - NR_CSI_RS_ResourceMapping_t *resourceMapping, + const NR_CSI_RS_ResourceMapping_t *resourceMapping, uint32_t bwp_size, uint32_t bwp_start); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 8a892557b2..de8dbb47e4 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -249,26 +249,27 @@ void nr_ue_decode_mib(NR_UE_MAC_INST_t *mac, int cc_id) } static void configure_ratematching_csi(fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_pdu, - fapi_nr_dl_config_request_t *dl_config, + const fapi_nr_dl_config_request_t *dl_config, int rnti_type, int frame, int slot, int mu, int slots_per_frame, - NR_PDSCH_Config_t *pdsch_config) + const NR_PDSCH_Config_t *pdsch_config) { // only for C-RNTI, MCS-C-RNTI, CS-RNTI (and only C-RNTI is supported for now) if (rnti_type != TYPE_C_RNTI_) return; if (pdsch_config && pdsch_config->zp_CSI_RS_ResourceToAddModList) { - for (int i = 0; i < pdsch_config->zp_CSI_RS_ResourceToAddModList->list.count; i++) { - NR_ZP_CSI_RS_Resource_t *zp_res = pdsch_config->zp_CSI_RS_ResourceToAddModList->list.array[i]; - NR_ZP_CSI_RS_ResourceId_t id = zp_res->zp_CSI_RS_ResourceId; - NR_SetupRelease_ZP_CSI_RS_ResourceSet_t *zp_set = pdsch_config->p_ZP_CSI_RS_ResourceSet; - AssertFatal(zp_set && zp_set->choice.setup, "Only periodic ZP resource set is implemented\n"); - bool found = false; - for (int j = 0; j < zp_set->choice.setup->zp_CSI_RS_ResourceIdList.list.count; j++) { + bool found = false; + const NR_SetupRelease_ZP_CSI_RS_ResourceSet_t *zp_set = pdsch_config->p_ZP_CSI_RS_ResourceSet; + AssertFatal(zp_set && zp_set->choice.setup, "Only periodic ZP resource set is implemented\n"); + const NR_ZP_CSI_RS_Resource_t *zp_res = NULL; + for (int j = 0; j < zp_set->choice.setup->zp_CSI_RS_ResourceIdList.list.count; j++) { + for (int i = 0; i < pdsch_config->zp_CSI_RS_ResourceToAddModList->list.count; i++) { + zp_res = pdsch_config->zp_CSI_RS_ResourceToAddModList->list.array[i]; + NR_ZP_CSI_RS_ResourceId_t id = zp_res->zp_CSI_RS_ResourceId; if (*zp_set->choice.setup->zp_CSI_RS_ResourceIdList.list.array[j] == id) { found = true; break; @@ -291,7 +292,7 @@ static void configure_ratematching_csi(fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsc for (int i = 0; i < dl_config->number_pdus; i++) { // This assumes that CSI-RS are scheduled before this moment which is true in current implementation - fapi_nr_dl_config_request_pdu_t *csi_req = &dl_config->dl_config_list[i]; + const fapi_nr_dl_config_request_pdu_t *csi_req = &dl_config->dl_config_list[i]; if (csi_req->pdu_type == FAPI_NR_DL_CONFIG_TYPE_CSI_RS) { AssertFatal(dlsch_pdu->numCsiRsForRateMatching < NFAPI_MAX_NUM_CSI_RATEMATCH, "csiRsForRateMatching out of bounds\n"); dlsch_pdu->csiRsForRateMatching[dlsch_pdu->numCsiRsForRateMatching] = csi_req->csirs_config_pdu.csirs_config_rel15; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 6c257bcacf..5eaf492304 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -1919,7 +1919,7 @@ uint8_t set_csirs_measurement_bitmap(NR_CSI_MeasConfig_t *csi_measconfig, NR_CSI } void configure_csi_resource_mapping(fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu, - NR_CSI_RS_ResourceMapping_t *resourceMapping, + const NR_CSI_RS_ResourceMapping_t *resourceMapping, uint32_t bwp_size, uint32_t bwp_start) { From 61ea0a9e81a4bd3b048a776bb8be65cf44335741 Mon Sep 17 00:00:00 2001 From: Xin Zhe Khooi Date: Fri, 28 Mar 2025 03:00:19 +0000 Subject: [PATCH 47/86] ldpc_t2: generalize for broader BBDEV support (ACC100/200) - Generalized LDPC T2 implementation to support other DPDK BBDEV accelerators (e.g., Intel ACC100/200) - Added BBDEV capability checks during initialization - Introduced LLR scaling to improve decoding performance on Intel ACC cards - Implemented self-managed HARQ buffers for ACC200 (no internal HARQ memory) - Library usage documentation update, and added developer documentation Co-authored-by: Romain Beurdouche --- doc/LDPC_T2_OFFLOAD_SETUP.md | 307 ++++++-- openair1/PHY/CODING/DOC/LDPCImplementation.md | 2 +- .../nrLDPC_coding_t2/CMakeLists.txt | 24 +- .../nrLDPC_coding/nrLDPC_coding_t2/README.md | 143 ++++ .../nrLDPC_coding_t2/nrLDPC_coding_t2.c | 686 +++++++++++++----- .../nrLDPC_coding_t2/nrLDPC_coding_t2.h | 5 +- 6 files changed, 928 insertions(+), 239 deletions(-) create mode 100644 openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/README.md diff --git a/doc/LDPC_T2_OFFLOAD_SETUP.md b/doc/LDPC_T2_OFFLOAD_SETUP.md index 24d36e39cf..0e9506db78 100644 --- a/doc/LDPC_T2_OFFLOAD_SETUP.md +++ b/doc/LDPC_T2_OFFLOAD_SETUP.md @@ -7,7 +7,7 @@ - OAI T1/T2 LDPC offload + OAI LDPC offload (O-RAN AAL/DPDK BBDEV) @@ -16,19 +16,57 @@ [[_TOC_]] -This documentation aims to provide a tutorial for AMD Xilinx T2 Telco card integration into OAI and its usage. +This documentation describes the integration of LDPC coding for lookaside acceleration using O-RAN AAL/DPDK BBDEV in OAI, along with its usage. +For details on the implementation, please consult the [developer notes](../openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/README.md). # Requirements - - bitstream image and PMD driver for the T2 card provided by AccelerComm - - DPDK 20.11.9 with patch from Accelercomm: version ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch - - tested on RHEL7.9, RHEL9.2, Ubuntu 22.04 +In principle, any lookaside LDPC accelerator supporting the O-RAN AAL/DPDK BBDEV should work. +However, the current implementation has only been validated for the Xilinx T2, Intel ACC100, and Intel ACC200 (VRB1). +Therefore, your mileage may vary when using other BBDEV devices as there may be some hardware-specific changes required -- contributions are welcome! -# DPDK setup +## DPDK Version Requirements + +The following DPDK versions are supported: +- For the Xilinx T2 card, DPDK20.11+ is supported. +- As for the Intel ACC100/ACC200, only DPDK22.11+ is supported. + +## Tested Devices/ DPDK versions + +### Xilinx T2 + +- DPDK20.11.9*. +- DPDK22.11.7*. +> Note: FPGA bitstream image and the corresponding patch file (e.g., `ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch` for DPDK20.11) from Accelercomm required. + +### Intel ACC100 + +- DPDK22.11.7*. +- DPDK23.11.3*. +- DPDK24.11.2. +> Note: [Patch]((https://github.com/DPDK/dpdk/commit/fdde63a1dfc129d0a510a831aa98253b36a2a1cd)) required for pre-DPDK24.11 versions when using the Intel ACC100. + +### Intel ACC200 (also known as VRB1) +- DPDK22.11.7. +- DPDK23.11.3. +- DPDK24.11.2. + +# System Setup ## DPDK installation -*Note: Following instructions are valid for ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch version, which is compatible with DPDK 20.11.9. Installation steps, which should be followed for older versions of the patch file (for example ACL_BBDEV_DPDK20.11.3_BL_1006_build_1105_dev_branch_MCT_optimisations_1106_physical_std.patch) are present in older version of this documentation, under the tag 2023.w48.* -``` +> Important: +> - If you are using the Xilinx T2 card, you will need to apply the vendor-supplied patches before compiling DPDK. +> - If you are using the Intel ACC100, you will need to [patch](https://github.com/DPDK/dpdk/commit/fdde63a1dfc129d0a510a831aa98253b36a2a1cd) the ACC100's driver if you are using DPDK22.11 or DPDK23.11. + + +Refer to the guide [here](./ORAN_FHI7.2_Tutorial.md?ref_type=heads#dpdk-data-plane-development-kit) to install, and then validate your DPDK installation. + +
+ Notes on DPDK patching/installation for Xilinx T2. + +*Note: The following instructions apply to `ACCL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch`, compatible with DPDK 20.11.9. For older patches (e.g., `ACL_BBDEV_DPDK20.11.3_BL_1006_build_1105_dev_branch_MCT_optimisations_1106_physical_std.patch`), refer to the T2 documentation in `2023.w48`.* + +```bash # Get DPDK source code git clone https://github.com/DPDK/dpdk-stable.git ~/dpdk-stable cd ~/dpdk-stable @@ -37,50 +75,127 @@ git apply ~/ACL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch ``` Replace `~/ACL_BBDEV_DPDK20.11.3_ldpc_3.1.918.patch` by patch file provided by Accelercomm. -``` + +If you would like to install DPDK to a custom directory, here is an example. +```bash cd ~/dpdk-stable -meson setup build -# meson setup --prefix=/opt/dpdk-t2 build for installation with non-default installation prefix +# meson setup build +meson setup --prefix=/opt/dpdk-t2 build # for installation with non-default installation prefix cd build ninja sudo ninja install sudo ldconfig + ``` -## DPDK configuration - - load required kernel module +
+ +## System configuration + +### Setting up Hugepages + +First, we must setup hugepages on the system. +In our setup, we setup 16 of the 1G hugepages. +Apart from 1G, 2MB hugepages works too, but make sure to allocate a sufficient number of them. + ``` -sudo modprobe vfio-pci -``` - - check presence of the card and its PCI addres on the host machine -``` -lspci | grep "Xilinx" -``` - - bind the card with vfio-pci driver -``` -sudo python3 ~/dpdk-stable/usertools/dpdk-devbind.py --bind=vfio-pci 41:00.0 -``` -Replace PCI address of the card *41:00.0* by address detected by *lspci | grep "Xilinx"* command - - hugepages setup (10 x 1GB hugepages) -``` -sudo python3 ~/dpdk-stable/usertools/dpdk-hugepages.py -p 1G --setup 10G +# sudo dpdk-hugepages.py -p 1G --setup 16G ``` -*Note: device binding and hugepages setup has to be done after every reboot of -the host machine* +### Locating the Accelerator -# Modifications in the OAI code -## DPDK lib and PMD path specification -If DPDK library was installed into custom path, you have to point to the right directory with `PKG_CONFIG_PATH`, prior to the OAI build. Sample command to set the DPDK path to `/opt/dpdk-t2/lib64/pkgconfig/`: +Next, we check whether our system can detect our accelerator using `dpdk-devbind.py`. +You should see Baseband devices detected by DPDK, as follows: ``` -export PKG_CONFIG_PATH=/opt/dpdk-t2/lib64/pkgconfig/:$PKG_CONFIG_PATH +# sudo dpdk-devbind.py -s +... +Baseband devices using DPDK-compatible driver +============================================= +0000:f7:00.0 'Device 57c0' unused=vfio-pci +... ``` -# OAI Build +As you can see here, our Intel ACC200 has the address of `0000:f7:00.0`. +Depending on the accelerator you are using, the address may vary. + +### Loading VFIO-PCI and enabling SR-IOV +Following, make sure to load the `vfio-pci` kernel modules and ensure that SR-IOV is enabled. + +``` +# sudo modprobe vfio-pci enable_sriov=1 disable_idle_d3=1 +``` + +### Binding the Accelerator with `vfio-pci` + +Lastly, we bind our accelerator with the `vfio-pci` driver. +``` +# sudo dpdk-devbind.py --bind=vfio-pci 0000:f7:00.0 +``` + +> Note: For the Xilinx T2, we can use this device directly. +If you use an Intel vRAN accelerator, read on. + +### Additional Steps for Intel vRAN Accelerators + +> IMPORTANT NOTE: +> - Currently, we only support using the Virtual Functions (VFs) of the Intel vRAN accelerators, but not the Physical Function (PF). +> - One key advantage of using VFs is that this allows us to share the accelerator with other DU instances on the same machine, which is common in practice. + +If you are using an Intel vRAN accelerator, you will need to use the [pf_bb_config](https://github.com/intel/pf-bb-config) tool to configure the accelerator beforehand. + +#### pf_bb_config +For more details, please consult the `pf_bb_config` README. + +``` +# git clone https://github.com/intel/pf-bb-config +# cd ~/pf-bb-config +# ./build.sh +``` +This clones and builds the `pf_bb_config` binary. + +Next, we show an example for the Intel ACC200. +We use an existing configuration located at `./vrb1/vrb1_config_16vf.cfg`. + +Here, it is necessary to specify a VFIO token (in this case, we use the UUID `00112233-4455-6677-8899-aabbccddeeff`). +Note that in practice, a random UUID should be used. +``` +# sudo ./pf_bb_config VRB1 -v 00112233-4455-6677-8899-aabbccddeeff -c vrb1/vrb1_config_16vf.cfg +== pf_bb_config Version v25.01-0-g812e032 == +VRB1 PF [0000:f7:00.0] configuration complete! +Log file = /var/log/pf_bb_cfg_0000:f7:00.0.log +``` + +#### Creating VFs + +Finally, we create the VF(s) for our accelerator. +In this example, we only create one SR-IOV VF. +``` +# echo 1 | sudo tee /sys/bus/pci/devices/0000:f7:00.0/sriov_numvfs +``` + +If you encounter any error when creating the VF(s), e.g., `tee: '/sys/bus/pci/devices/0000:f7:00.0/sriov_numvfs': No such file or directory`, then try enabling SR-IOV again. +``` +# echo 1 | sudo tee /sys/module/vfio_pci/parameters/enable_sriov +``` + +After you have successfully created the VF, you should see an additional baseband device, in our case, it is `0000:f7:00.1`. +We will use this device with OAI later. +The newly created VF should also be using the same `vfio-pci` driver as the PF, if it is not, you will need to do a `dpdk-devbind.py` to bind it with `vfio-pci`. +``` +# sudo dpdk-devbind.py -s +... +Baseband devices using DPDK-compatible driver +============================================= +0000:f7:00.0 'Device 57c0' drv=vfio-pci unused= +0000:f7:00.1 'Device 57c0' drv=vfio-pci unused= +... +``` + +# Building OAI with ORAN-AAL OTA deployment is precisely described in the following tutorial: - [NR_SA_Tutorial_COTS_UE](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_COTS_UE.md) -Instead of section *3.2 Build OAI gNB* from the tutorial, run following commands: +Instead of section *3.2 Build OAI gNB* from the tutorial, run the following commands: -``` +```bash # Get openairinterface5g source code git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g cd ~/openairinterface5g @@ -97,21 +212,35 @@ cd cmake_targets ./build_oai -w USRP --ninja --gNB -P --build-lib "ldpc_t2" -C ``` -Shared object file *libldpc_t2.so* is created during the compilation. This object is conditionally compiled. Selection of the library to compile is done using *--build-lib ldpc_t2*. +A shared object file `libldpc_t2.so` will be created during the compilation. +This object is conditionally compiled. +The selection of the library to compile is done using `--build-lib ldpc_t2`. -*Required poll mode driver has to be present on the host machine and required DPDK version has to be installed on the host, prior to the build of OAI* +> Note: The required DPDK poll mode driver has to be present on the host machine and required DPDK version has to be installed on the host, prior to building OAI. -# Setup of T2-related DPDK EAL parameters -To configure T2-related DPDK Environment Abstraction Layer (EAL) parameters, you can set the following parameters via the command line of PHY simulators or softmodem: -- `nrLDPC_coding_t2.dpdk_dev` - **mandatory** parameter, specifies PCI address of the T2 card. PCI address of the T2 card can be detected by `lspci | grep "Xilinx"` command. -- `nrLDPC_coding_t2.dpdk_core_list` - **mandatory** parameter, specifies CPU cores assigned to DPDK for T2 processing. Ensure that the CPU cores specified in *nrLDPC_coding_t2.dpdk_core_list* are available and not used by other processes to avoid conflicts. -- `nrLDPC_coding_t2.dpdk_prefix` - DPDK shared data file prefix, by default set to *b6*. +# O-RAN AAL DPDK EAL parameters +To configure O-RAN AAL/DPDK BBDEV, you can set the following parameters via the command line of PHY simulators or nr-softmodem: -**Note:** These parameters can also be provided in a configuration file: +- `nrLDPC_coding_t2.dpdk_dev` - **mandatory** parameter, specifies the PCI address of our accelerator. It must follow the format `WWWW:XX:YY.Z`. + +- `nrLDPC_coding_t2.dpdk_core_list` - **mandatory** parameter, specifies the CPU cores assigned to DPDK . +Ensure that the CPU cores specified in `nrLDPC_coding_t2.dpdk_core_list` are available and not used by other processes to avoid conflicts. + +- `nrLDPC_coding_t2.dpdk_prefix` - optional parameter, DPDK shared data file prefix, by default set to *b6*. + +- `nrLDPC_coding_t2.vfio_vf_token` - optional parameter, VFIO token set for the VF, if applicable. + +- `nrLDPC_coding_t2.num_harq_codeblock` - optional parameter, size of the HARQ buffer in terms of the number of 32kB blocks, by default set to *512* (maximum for the T2; as for the ACCs, this can be further increased). + +- `nrLDPC_coding_t2.eal_init_bbdev` - optional parameter, set this to 1 when using with FHI72 with the Intel ACCs. For the T2, this is not required. + +**Note:** These parameters can also be provided in a configuration file. +Example for the ACC200: ``` nrLDPC_coding_t2 : { - dpdk_dev : "41:00.0"; + dpdk_dev : "0000:f7:00.1"; dpdk_core_list : "14-15"; + vfio_vf_token: "00112233-4455-6677-8899-aabbccddeeff"; }; loader : { @@ -121,38 +250,90 @@ loader : { }; ``` -# 5G PHY simulators +# Running OAI with O-RAN AAL -## nr_ulsim test -Offload of the channel decoding to the T2 card is in nr_ulsim specified by *--loader.ldpc.shlibversion _t2* option. Example command for running nr_ulsim with LDPC decoding offload to the T2 card: -``` +In general, to offload of the channel coding to the LDPC accelerator, we use use the `--loader.ldpc.shlibversion _t2` option. + +## 5G PHY simulators + +### nr_ulsim + +Example command: +```bash cd ~/openairinterface5g source oaienv cd cmake_targets/ran_build/build -sudo ./nr_ulsim -n100 -s20 -m20 -r273 -R273 --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1 -``` -## nr_dlsim test -Offload of the channel encoding to the AMD Xilinx T2 card is in nr_dlsim specified by *-c* option. Example command for running nr_dlsim with LDPC encoding offload to the T2 card: +sudo ./nr_ulsim -n100 -s20 -m20 -r273 -R273 --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 0-1 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff ``` +### nr_dlsim + +Example command: +```bash cd ~/openairinterface5g source oaienv cd cmake_targets/ran_build/build -sudo ./nr_dlsim -n300 -s30 -R 106 -e 27 --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1 +sudo ./nr_dlsim -n300 -s30 -R 106 -e 27 --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 0-1 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff ``` -# OTA test -Offload of the channel encoding and decoding to the AMD Xilinx T2 card is enabled by *--loader.ldpc.shlibversion _t2* option. +## OTA test -## Run OAI gNB with USRP B210 -``` +### Running OAI gNB with USRP B210/FHI72 + +Example command: +```bash cd ~/openairinterface5g source oaienv cd cmake_targets/ran_build/build -sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1 +sudo ./nr-softmodem -O ~/gnb.conf --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 14-15 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff --nrLDPC_coding_t2.eal_init_bbdev 1 ``` -# Limitations -## AMD Xilinx T2 card - - functionality of the LDPC encoding and decoding offload verified in OTA SISO setup with USRP N310 and Quectel RM500Q, blocking of the card reported for MIMO setup (2 layers) +# Known Issue(s) -*Note: AMD Xilinx T1 Telco card is not supported anymore.* +## BBDEV CPU Usage + +When running the E2E setup (this applies to both USRP and FHI72), BBDEV may not be using the list of CPU cores as specified by `nrLDPC_coding_t2.dpdk_core_list` accordingly. +This is an issue under investigation, and subject for future fixes. +In the meantime, we recommend allocating idle, and isolated CPU cores in the configuration for BBDEV. + +## Potential Low Throughput + +The current implementation has been tested to work in an end-to-end setup and is functional. +However, there are still opportunities for optimization, particularly in LDPC decoding performance, which is an area of ongoing improvement. +As such, downlink/uplink throughput may be suboptimal with the default configurations, but enhancements are actively being explored. + +To achieve better E2E performance with the current implementation, we recommend the following adjustments: +1. Increasing the number of LDPC decoding iterations of the L1, e.g., `max_ldpc_iterations` to 200. +2. Increasing the BLER targets of the MAC scheduler. + +Example configuration snippet: +``` +... +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 180; + pucch_TargetSNRx10 = 220; + dl_bler_target_upper = .35; + dl_bler_target_lower = .15; + ul_bler_target_upper = .35; + ul_bler_target_lower = .15; + pusch_FailureThres = 1000; + ul_max_mcs = 28; + +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 120; + pucch0_dtx_threshold = 100; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 + max_ldpc_iterations = 200; +} +); +... +``` diff --git a/openair1/PHY/CODING/DOC/LDPCImplementation.md b/openair1/PHY/CODING/DOC/LDPCImplementation.md index 0fae4e1574..dc2959bc73 100644 --- a/openair1/PHY/CODING/DOC/LDPCImplementation.md +++ b/openair1/PHY/CODING/DOC/LDPCImplementation.md @@ -39,7 +39,7 @@ Building C object CMakeFiles/ldpc_t2.dir/openair1/PHY/CODING/nrLDPC_coding/nrLDP Linking C shared module libldpc_t2.so ``` -At runtime, to successfully use the T2 board, you need to install vendor specific drivers and tools.\ +At runtime, to successfully use LDPC accelerators (e.g., Xilinx T2/Intel ACCs), you will need to install the corresponding drivers and tools. Please refer to the dedicated documentation at [LDPC_T2_OFFLOAD_SETUP.md](file://../../../../doc/LDPC_T2_OFFLOAD_SETUP.md). `./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1` diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt index f64384568a..b6a0cacf39 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt @@ -1,25 +1,35 @@ ########################################################## - -# LDPC offload library - AMD T2 Accelerator Card +# LDPC offload library - DPDK BBDEV ########################################################## add_boolean_option(ENABLE_LDPC_T2 OFF "Build support for LDPC Offload to T2 library" OFF) if (ENABLE_LDPC_T2) - pkg_check_modules(LIBDPDK_T2 REQUIRED libdpdk>=20.11.9) + if(LIBDPDK_T2_VERSION VERSION_LESS "21.11") + add_compile_definitions(DPDK_VER_PRE_21_11) + endif() + find_library(PMD_T2 NAMES rte_baseband_accl_ldpc HINTS ${LIBDPDK_T2_LIBRARY_DIRS}) - if (NOT PMD_T2) - message(FATAL_ERROR "could not find poll-mode driver for AccelerComm T2 LDPC Offload (rte_baseband_accl_ldpc.so)") + if (PMD_T2) + add_compile_definitions(LDPC_T2) + message(STATUS "found poll-mode driver for AccelerComm T2 LDPC Offload (rte_baseband_accl_ldpc.so)") + else() + if (LIBDPDK_T2_VERSION VERSION_LESS "22.11") + message(FATAL_ERROR "for non-T2 BBDEV PMDs, we support the Intel ACC100 and ACC200 (VRB1), using DPDK 22.11 or later.") + endif() endif() - message(STATUS "T2 build: use ${PMD_T2}") add_library(ldpc_t2 MODULE nrLDPC_coding_t2.c) set_target_properties(ldpc_t2 PROPERTIES COMPILE_FLAGS "-DALLOW_EXPERIMENTAL_API") set_target_properties(ldpc_t2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - target_link_libraries(ldpc_t2 PRIVATE ldpc_gen_HEADERS ${LIBDPDK_T2_LDFLAGS} ${PMD_T2}) + target_include_directories(ldpc_t2 PRIVATE ${LIBDPDK_T2_INCLUDE_DIRS}) + target_link_libraries(ldpc_t2 PRIVATE ldpc_gen_HEADERS ${LIBDPDK_T2_LDFLAGS}) + if (PMD_T2) + target_link_libraries(ldpc_t2 PRIVATE ${PMD_T2}) + endif() add_dependencies(nr-softmodem ldpc_t2) add_dependencies(nr-uesoftmodem ldpc_t2) diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/README.md b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/README.md new file mode 100644 index 0000000000..ee55f7ff21 --- /dev/null +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/README.md @@ -0,0 +1,143 @@ +This document highlights some of the key aspects of the implementation for O-RAN AAL/DPDK BBDEV; for user documentation, please consult the [setup guide](../../../../../doc/LDPC_T2_OFFLOAD_SETUP.md) instead. + +## Overview + +The current implementation supports the following BBDEV devices and respective DPDK versions: +- Xilinx T2 on DPDK20.11+ +- Intel ACC100* on DPDK22.11+ +- Intel ACC200 (VRB1) on DPDK22.11+ + +> Important notes: +> - For the Intel ACC100, for DPDK22.11 and DPDK23.11, users are required to [patch](https://github.com/DPDK/dpdk/commit/fdde63a1dfc129d0a510a831aa98253b36a2a1cd) the ACC100's driver. +> - If you intend to use this implementation in an FHI7.2 setup, only the F-release and beyond is supported. + +## Implementation Details + +Below, we highlight some of the important details in this implementation. + +### HARQ + +To maintain the HARQ buffers in the LDPC decoding process, the BBDEV either uses its internal memory (not visible to DPDK), or using external memory (i.e., DDR) managed by OAI. +For the Xilinx T2 and Intel ACC100, they use the internal memory while the Intel ACC200 uses the DDR memory. + +If internal memory is supported by the device, we will set `RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_IN_ENABLE` and `RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_OUT_ENABLE`. + +The following two fields are important for HARQ: `harq_combined_input` and `harq_combined_output`. + +For the first transmission, there is no HARQ input. +Thus, the flag `RTE_BBDEV_LDPC_HQ_COMBINE_IN_ENABLE` is not set. +However, there will be a HARQ output, so we set the flag `RTE_BBDEV_LDPC_HQ_COMBINE_OUT_ENABLE`. +We also set the corresponding fields for `harq_combined_output`. + +For the second transmission (or subsequent retransmissions), we need to provide a HARQ input at the same time, there will also be a HARQ output. +Therefore, both fields, `harq_combined_input` and `harq_combined_output` must be set accordingly. + +As HARQ requires the output of the previous transmission/ HARQ round as its input, we need to maintain a separate variable to hold the previous round's information. +To do so, we define `harq_buffers` (which is a pointer to an array of size `num_harq_codeblock` allocated in the heap later) in the `active_device` struct (see the following) in our implementation. +```C +struct active_device { + ... + uint32_t num_harq_codeblock; + /* Persistent data structure to keep track of HARQ-related information */ + // Note: This is used to store/keep track of the combined output information across iterations + struct rte_bbdev_op_data *harq_buffers; + ... +} active_dev; +``` + + +In the following, we elaborate on how the HARQ-related fields are set. + +#### Internal Memory + +When using the internal memory, we need to specify the *offset* of the hardware's memory where we want to store the HARQ combined output, and read from it accordingly later. + +In the current implementation, we use a fixed offset of 32K, i.e., this means that each block is 32K. + +An index is derived from a unique HARQ PID assigned by OAI, modulo `active_dev.num_harq_codeblock` (which is configurable by the user through `nrLDPC_coding_t2.num_harq_codeblock`). +Using that index, we then derived the corresponding offset in the memory (i.e., `harq_combined_offset`). + +```C +... + +// Calculate offset in the HARQ combined buffers +// Unique segment offset +uint32_t segment_offset = (nrLDPC_slot_decoding_parameters->TBs[h].harq_unique_pid * NR_LDPC_MAX_NUM_CB) + i; +// Prune to avoid shooting above maximum id +uint32_t pruned_segment_offset = segment_offset % active_dev.num_harq_codeblock; +// Segment offset to byte offset +uint32_t harq_combined_offset = pruned_segment_offset * LDPC_MAX_CB_SIZE; + +... +``` + +This `harq_combined_offset` is then set for `harq_combined_input.offset` and `harq_combined_output.offset` to tell BBDEV where to read/write the HARQ buffers. + +Especially for the Intel ACCs, when providing the HARQ input, the `harq_combined_input.length` must be provided. +To do so, we maintain the length of previous round's output using `harq_buffers` by copying it in `retrieve_ldpc_dec_op`. + +#### External Memory + +Different from the Xilinx T2 and Intel ACC100, the Intel ACC200 uses DDR memory to maintain the HARQ buffers which is managed by OAI. + +In short, we use `harq_buffers` from the `active_device` struct, which have been initialzed with corresponding DPDK mempools for BBDEV to read/write the HARQ input/outputs. + +For indexing, we use the `pruned_segment_offset`, which is derived from the unique HARQ PID assigned by OAI, modulo `active_dev.num_harq_codeblock` (which is configurable by the user through `nrLDPC_coding_t2.num_harq_codeblock`). + +`harq_combined_input.offset` and `harq_combined_output.offset` is always set to 0 in our implementation. +Instead, we provide BBDEV the pointers to our allocated mempool regions through `harq_combined_input.data` and `harq_combined_output.data`. + +In `retrieve_ldpc_dec_op`, we perform a `rte_memcpy` to copy the HARQ combined outputs to `harq_buffers`, along with other necessary metadata information to prepare for subsequent HARQ rounds. +Similar to the Intel ACC100, when providing the HARQ input, the `harq_combined_input.length` must be provided. + +### LLR Scaling + +The LLR output from OAI's demodulation block is in 16 bits. +However, accelerators typically assume that the LLR inputs are scaled down using fixed-point arithmetic, e.g., 8 bits. + +For the Xilinx T2, saturation is performed. +However, our tests have shown that this implementation did not work well for the Intel ACCs. + +To that end, we perform LLR scaling differently for the Intel ACCs. +Through the function `llr_scaling`, we look at the min and max LLR values, perform scaling and subsequently saturation accordingly. + +The fixed-point representations supported by the hardware is defined by the `llr_size` and `llr_decimal`. +For example, if `llr_size=8`, and `llr_decimal=1`, it means that we use a S8.1 representation (1 fractional bit), the min and max values are -64 to 63.5. + +> *Limitation:* +> We observe that in certain configurations, such as when using 2 MIMO layers, the LLR (Log-Likelihood Ratio) outputs exhibit a much wider dynamic range (e.g., from -10,000 to 10,000) compared to the typical sub-1,000 range seen in most other cases. +> This suggests potential inconsistencies in the LLR values generated by the demodulator. +> We believe that the decoding performance, when compared to the CPU implementation under the same number of LDPC decoding iterations, could be improved by refining the LLR scaling approach or revisiting the LLR generation process during demodulation. +> This remains an area of active investigation. +> +> *Workaround:* +> As a temporary measure, increasing the number of LDPC decoding iterations—e.g., to 150—has shown to yield a decoding success rate comparable to that of the CPU implementation. +> This comes at a negligible increase in decoding time based on our current evaluation. + +### Special Case(s) + +#### TB mode + +For the T2, only CB mode is supported. + +For the Intel ACCs, while we also use CB mode mostly, there is a special case that must be handled properly as specified by the BBDEV documentations: +``` +... The case when one CB belongs to TB and is being enqueued individually to BBDEV, this case is considered as a special case of partial TB where its number of CBs is 1. Therefore, it requires to get processed in TB-mode. ... +``` + +In such a case, we perform a check on whether there is only one TB, AND if the TB has only one CB. +If so, this has to be handled separately by using TB mode. + +### Future Work(s) + +#### TB mode + +The current channel coding interface assumes LDPC encoding and decoding are performed on a per-code block basis. +However, to fully leverage Transport Block (TB) mode as supported in BBDEV, which can improve efficiency, the L1 implementation surrounding channel coding needs to be refactored. +This remains an area for future investigation. + +#### CRC24A/CRC16 + +CRC24A/CRC16 is supported by the BBDEV device (i.e., Intel ACC200). +However, we do not make use of them and rely on OAI's existing CRC functions. +To use of BBDEV's CRC24A/CRC16 capability will be looked into in future MRs. diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c index 04ad571de0..bd1aad254d 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -47,16 +48,20 @@ #define TIME_OUT_POLL 1e8 /* Headroom for filler LLRs insertion in HARQ buffer */ #define FILLER_HEADROOM 1024 -/* Number of segments that could be stored in HARQ combined buffers */ -#define HARQ_CODEBLOCK_ID_MAX (16 << 5) pthread_mutex_t encode_mutex; pthread_mutex_t decode_mutex; -/* Represents tested active devices */ +/* we assume one active baseband device only */ struct active_device { const char *driver_name; uint8_t dev_id; + struct rte_bbdev_info info; + uint32_t num_harq_codeblock; + /* Persistent data structure to keep track of HARQ-related information */ + // Note: This is used to store/keep track of the combined output information across iterations + struct rte_bbdev_op_data *harq_buffers; + bool support_internal_harq_memory; int dec_queue; int enc_queue; uint16_t queue_ids[MAX_QUEUES]; @@ -65,13 +70,15 @@ struct active_device { struct rte_mempool *bbdev_enc_op_pool; struct rte_mempool *in_mbuf_pool; struct rte_mempool *hard_out_mbuf_pool; -} active_devs[RTE_BBDEV_MAX_DEVS]; -static int nb_active_devs; + struct rte_mempool *harq_in_mbuf_pool; + struct rte_mempool *harq_out_mbuf_pool; +} active_dev; /* Data buffers used by BBDEV ops */ struct data_buffers { struct rte_bbdev_op_data *inputs; struct rte_bbdev_op_data *hard_outputs; + struct rte_bbdev_op_data *harq_outputs; }; /* Operation parameters specific for given test case */ @@ -93,26 +100,34 @@ struct thread_params { struct rte_mempool *bbdev_op_pool; }; -static uint16_t nb_segments_decoding(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) { +static uint16_t nb_segments_decoding(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) +{ uint16_t nb_segments = 0; for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { - nb_segments+=nrLDPC_slot_decoding_parameters->TBs[h].C; + nb_segments += nrLDPC_slot_decoding_parameters->TBs[h].C; } return nb_segments; } -static uint16_t nb_segments_encoding(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) { +static uint16_t nb_segments_encoding(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) +{ uint16_t nb_segments = 0; for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) { - nb_segments+=nrLDPC_slot_encoding_parameters->TBs[h].C; + nb_segments += nrLDPC_slot_encoding_parameters->TBs[h].C; } return nb_segments; } +/* Read flag value 0/1 from bitmap */ +// DPDK BBDEV copy +static inline bool check_bit(uint32_t bitmap, uint32_t bitmask) +{ + return bitmap & bitmask; +} + /* calculates optimal mempool size not smaller than the val */ // DPDK BBDEV copy -static unsigned int -optimal_mempool_size(unsigned int val) +static unsigned int optimal_mempool_size(unsigned int val) { return rte_align32pow2(val + 1) - 1; } @@ -131,17 +146,16 @@ static int create_mempools(struct active_device *ad, int socket_id, uint16_t num OPS_POOL_SIZE_MIN)); /* Decoder ops mempool */ - ad->bbdev_dec_op_pool = rte_bbdev_op_pool_create("bbdev_op_pool_dec", RTE_BBDEV_OP_LDPC_DEC, - /* Encoder ops mempool */ ops_pool_size, OPS_CACHE_SIZE, socket_id); - ad->bbdev_enc_op_pool = rte_bbdev_op_pool_create("bbdev_op_pool_enc", RTE_BBDEV_OP_LDPC_ENC, - ops_pool_size, OPS_CACHE_SIZE, socket_id); + ad->bbdev_dec_op_pool = rte_bbdev_op_pool_create("bbdev_op_pool_dec", + RTE_BBDEV_OP_LDPC_DEC, + /* Encoder ops mempool */ ops_pool_size, + OPS_CACHE_SIZE, + socket_id); + ad->bbdev_enc_op_pool = + rte_bbdev_op_pool_create("bbdev_op_pool_enc", RTE_BBDEV_OP_LDPC_ENC, ops_pool_size, OPS_CACHE_SIZE, socket_id); if ((ad->bbdev_dec_op_pool == NULL) || (ad->bbdev_enc_op_pool == NULL)) - AssertFatal(1 == 0, - "ERROR Failed to create %u items ops pool for dev %u on socket %d.", - ops_pool_size, - ad->dev_id, - socket_id); + AssertFatal(1 == 0, "ERROR Failed to create %u items ops pool for dev %u on socket %d.", ops_pool_size, ad->dev_id, socket_id); /* Inputs */ mbuf_pool_size = optimal_mempool_size(ops_pool_size * nb_segments); @@ -149,9 +163,9 @@ static int create_mempools(struct active_device *ad, int socket_id, uint16_t num ad->in_mbuf_pool = rte_pktmbuf_pool_create("in_mbuf_pool", mbuf_pool_size, 0, 0, data_room_size, socket_id); AssertFatal(ad->in_mbuf_pool != NULL, "ERROR Failed to create %u items input pktmbuf pool for dev %u on socket %d.", - mbuf_pool_size, - ad->dev_id, - socket_id); + mbuf_pool_size, + ad->dev_id, + socket_id); /* Hard outputs */ data_room_size = RTE_MAX(out_buff_sz + RTE_PKTMBUF_HEADROOM + FILLER_HEADROOM, (unsigned int)RTE_MBUF_DEFAULT_BUF_SIZE); @@ -161,6 +175,27 @@ static int create_mempools(struct active_device *ad, int socket_id, uint16_t num mbuf_pool_size, ad->dev_id, socket_id); + + /* HARQ outputs */ + data_room_size = LDPC_MAX_CB_SIZE; + ad->harq_out_mbuf_pool = rte_pktmbuf_pool_create("harq_out_mbuf_pool", mbuf_pool_size, 0, 0, data_room_size, socket_id); + AssertFatal(ad->harq_out_mbuf_pool != NULL, + "ERROR Failed to create %u items harq output pktmbuf pool for dev %u on socket %d.", + mbuf_pool_size, + ad->dev_id, + socket_id); + + /* HARQ inputs */ + // Note: This is used as our harq buffer to store the combined outputs across iterations + data_room_size = LDPC_MAX_CB_SIZE; + ad->harq_in_mbuf_pool = + rte_pktmbuf_pool_create("harq_in_mbuf_pool", active_dev.num_harq_codeblock, 0, 0, data_room_size, socket_id); + AssertFatal(ad->harq_in_mbuf_pool != NULL, + "ERROR Failed to create %u items harq input pktmbuf pool for dev %u on socket %d.", + active_dev.num_harq_codeblock, + ad->dev_id, + socket_id); + return 0; } @@ -237,52 +272,127 @@ const char *ldpcdec_flag_bitmask[] = { "RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_FILLERS", }; -// based on DPDK BBDEV add_bbdev_dev -static int add_dev(uint8_t dev_id, struct rte_bbdev_info *info) +void debug_dev_capabilities(uint8_t dev_id, struct rte_bbdev_info *info) { - int ret; - struct active_device *ad = &active_devs[nb_active_devs]; - unsigned int nb_queues; - nb_queues = RTE_MIN(rte_lcore_count(), info->drv.max_num_queues); - nb_queues = RTE_MIN(nb_queues, (unsigned int)MAX_QUEUES); - /* Display for debug the capabilities of the card */ for (int i = 0; info->drv.capabilities[i].type != RTE_BBDEV_OP_NONE; i++) { - printf("device: %d, capability[%d]=%s\n", dev_id, i, rte_bbdev_op_type_str(info->drv.capabilities[i].type)); + LOG_D(NR_PHY, "device: %d, capability[%d]=%s\n", dev_id, i, rte_bbdev_op_type_str(info->drv.capabilities[i].type)); if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_ENC) { const struct rte_bbdev_op_cap_ldpc_enc cap = info->drv.capabilities[i].cap.ldpc_enc; - printf(" buffers: src = %d, dst = %d\n capabilites: ", cap.num_buffers_src, cap.num_buffers_dst); + LOG_D(NR_PHY, " buffers: src = %d, dst = %d\n capabilites: ", cap.num_buffers_src, cap.num_buffers_dst); for (int j = 0; j < sizeof(cap.capability_flags) * 8; j++) if (cap.capability_flags & (1ULL << j)) - printf("%s ", ldpcenc_flag_bitmask[j]); - printf("\n"); + LOG_D(NR_PHY, "%s ", ldpcenc_flag_bitmask[j]); + LOG_D(NR_PHY, "\n"); } if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_DEC) { const struct rte_bbdev_op_cap_ldpc_dec cap = info->drv.capabilities[i].cap.ldpc_dec; - printf(" buffers: src = %d, hard out = %d, soft_out %d, llr size %d, llr decimals %d \n capabilities: ", - cap.num_buffers_src, - cap.num_buffers_hard_out, - cap.num_buffers_soft_out, - cap.llr_size, - cap.llr_decimals); + LOG_D(NR_PHY, + " buffers: src = %d, hard out = %d, soft_out %d, llr size %d, llr decimals %d \n capabilities: ", + cap.num_buffers_src, + cap.num_buffers_hard_out, + cap.num_buffers_soft_out, + cap.llr_size, + cap.llr_decimals); for (int j = 0; j < sizeof(cap.capability_flags) * 8; j++) if (cap.capability_flags & (1ULL << j)) - printf("%s ", ldpcdec_flag_bitmask[j]); - printf("\n"); + LOG_D(NR_PHY, "%s ", ldpcdec_flag_bitmask[j]); + LOG_D(NR_PHY, "\n"); } } +} - /* setup device */ - ret = rte_bbdev_setup_queues(dev_id, nb_queues, info->socket_id); - if (ret < 0) { - printf("rte_bbdev_setup_queues(%u, %u, %d) ret %i\n", dev_id, nb_queues, info->socket_id, ret); - return -1; +void check_required_dev_capabilities(struct rte_bbdev_info *info) +{ + // check ldpc enc/ dec support + bool ldpc_enc = false; + bool ldpc_dec = false; + for (int i = 0; info->drv.capabilities[i].type != RTE_BBDEV_OP_NONE; i++) { + if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_ENC) { + ldpc_enc = true; + } + if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_DEC) { + ldpc_dec = true; + } } + AssertFatal(ldpc_enc, "ERROR: bbdev device does not support LDPC encoding\n"); + AssertFatal(ldpc_dec, "ERROR: bbdev device does not support LDPC decoding\n"); + + for (int i = 0; info->drv.capabilities[i].type != RTE_BBDEV_OP_NONE; i++) { + if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_ENC) { + // check encoding capabilities + bool rate_match = check_bit(info->drv.capabilities[i].cap.ldpc_enc.capability_flags, RTE_BBDEV_LDPC_RATE_MATCH); + AssertFatal(rate_match, "ERROR: bbdev device does not support LDPC encoding with rate matching\n"); + } + if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_DEC) { + // check decoding capabilities + bool iter_stop = check_bit(info->drv.capabilities[i].cap.ldpc_dec.capability_flags, RTE_BBDEV_LDPC_ITERATION_STOP_ENABLE); + AssertFatal(iter_stop, "ERROR: bbdev device does not support LDPC decoding with iteration stop\n"); + + bool crc_24b_drop = check_bit(info->drv.capabilities[i].cap.ldpc_dec.capability_flags, RTE_BBDEV_LDPC_CRC_TYPE_24B_DROP); + AssertFatal(crc_24b_drop, "ERROR: bbdev device does not support LDPC decoding with CRC-24B drop\n"); + + bool crc_24b_check = check_bit(info->drv.capabilities[i].cap.ldpc_dec.capability_flags, RTE_BBDEV_LDPC_CRC_TYPE_24B_CHECK); + AssertFatal(crc_24b_check, "ERROR: bbdev device does not support LDPC decoding with CRC-24B check\n"); + } + } +} + +bool check_internal_harq_memory_capabilities(struct rte_bbdev_info *info) +{ + for (int i = 0; info->drv.capabilities[i].type != RTE_BBDEV_OP_NONE; i++) { + if (info->drv.capabilities[i].type == RTE_BBDEV_OP_LDPC_DEC) { + bool harq_in = + check_bit(info->drv.capabilities[i].cap.ldpc_dec.capability_flags, RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_IN_ENABLE); + bool harq_out = + check_bit(info->drv.capabilities[i].cap.ldpc_dec.capability_flags, RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_OUT_ENABLE); + bool internal_harq_memory_support = harq_in & harq_out; + if (internal_harq_memory_support) { + LOG_I(NR_PHY, "bbdev device supports internal HARQ memory\n"); + } + return internal_harq_memory_support; + } + } + return false; +} + +// based on DPDK BBDEV add_bbdev_dev +static int add_dev(uint8_t dev_id, uint32_t num_harq_codeblock) +{ + int ret; + unsigned int nb_queues; + + // retrieve device capabilities + rte_bbdev_info_get(dev_id, &active_dev.info); + LOG_I(NR_PHY, "using bbdev %d: %s\n", dev_id, active_dev.info.dev_name); + + active_dev.driver_name = active_dev.info.drv.driver_name; + active_dev.dev_id = dev_id; + + nb_queues = RTE_MIN(rte_lcore_count(), active_dev.info.drv.max_num_queues); + nb_queues = RTE_MIN(nb_queues, (unsigned int)MAX_QUEUES); + + // debug device capabilities + debug_dev_capabilities(dev_id, &active_dev.info); + + // check required device capabilities + check_required_dev_capabilities(&active_dev.info); + + // check internal harq memory capabilities + active_dev.support_internal_harq_memory = check_internal_harq_memory_capabilities(&active_dev.info); + + // setup harq buffers + active_dev.num_harq_codeblock = num_harq_codeblock; + active_dev.harq_buffers = malloc(sizeof(struct rte_bbdev_op_data) * active_dev.num_harq_codeblock); + + // device setup + ret = rte_bbdev_setup_queues(dev_id, nb_queues, active_dev.info.socket_id); + AssertFatal(ret == 0, "rte_bbdev_setup_queues(%u, %u, %d) ret %i\n", dev_id, nb_queues, active_dev.info.socket_id, ret); /* setup device queues */ struct rte_bbdev_queue_conf qconf = { - .socket = info->socket_id, - .queue_size = info->drv.default_queue_conf.queue_size, + .socket = active_dev.info.socket_id, + .queue_size = active_dev.info.drv.default_queue_conf.queue_size, }; // Search a queue linked to HW capability ldpc decoding @@ -291,10 +401,10 @@ static int add_dev(uint8_t dev_id, struct rte_bbdev_info *info) for (queue_id = 0; queue_id < nb_queues; ++queue_id) { ret = rte_bbdev_queue_configure(dev_id, queue_id, &qconf); if (ret == 0) { - printf("Found LDCP encoding queue (id=%u) at prio%u on dev%u\n", queue_id, qconf.priority, dev_id); + LOG_I(NR_PHY, "Found LDPC encoding queue (id=%u) at prio%u on dev%u\n", queue_id, qconf.priority, dev_id); qconf.priority++; - ad->enc_queue = queue_id; - ad->queue_ids[queue_id] = queue_id; + active_dev.enc_queue = queue_id; + active_dev.queue_ids[queue_id] = queue_id; break; } } @@ -305,15 +415,30 @@ static int add_dev(uint8_t dev_id, struct rte_bbdev_info *info) for (queue_id++; queue_id < nb_queues; ++queue_id) { ret = rte_bbdev_queue_configure(dev_id, queue_id, &qconf); if (ret == 0) { - printf("Found LDCP decoding queue (id=%u) at prio%u on dev%u\n", queue_id, qconf.priority, dev_id); + LOG_I(NR_PHY, "Found LDPC decoding queue (id=%u) at prio%u on dev%u\n", queue_id, qconf.priority, dev_id); qconf.priority++; - ad->dec_queue = queue_id; - ad->queue_ids[queue_id] = queue_id; + active_dev.dec_queue = queue_id; + active_dev.queue_ids[queue_id] = queue_id; break; } } AssertFatal(queue_id != nb_queues, "ERROR Failed to configure encoding queues on dev %u", dev_id); - ad->nb_queues = 2; + active_dev.nb_queues = 2; + return 0; +} + +static int init_op_data_objs_harq(struct rte_bbdev_op_data *bufs, struct rte_mempool *mbuf_pool) +{ + for (int i = 0; i < active_dev.num_harq_codeblock; i++) { + struct rte_mbuf *m_head = rte_pktmbuf_alloc(mbuf_pool); + AssertFatal(m_head != NULL, + "Not enough mbufs in HARQ mbuf pool (needed %u, available %u)", + active_dev.num_harq_codeblock, + mbuf_pool->size); + bufs[i].data = m_head; + bufs[i].offset = 0; + bufs[i].length = 0; + } return 0; } @@ -337,7 +462,7 @@ static int init_op_data_objs_dec(struct rte_bbdev_op_data *bufs, op_type, nb_segments_decoding(nrLDPC_slot_decoding_parameters), mbuf_pool->size); - + if (data_len > RTE_BBDEV_LDPC_E_MAX_MBUF) { printf("Warning: Larger input size than DPDK mbuf %u\n", data_len); large_input = true; @@ -393,7 +518,7 @@ static int init_op_data_objs_enc(struct rte_bbdev_op_data *bufs, op_type, nb_segments_encoding(nrLDPC_slot_encoding_parameters), mbuf_pool->size); - + if (data_len > RTE_BBDEV_LDPC_E_MAX_MBUF) { printf("Warning: Larger input size than DPDK mbuf %u\n", data_len); large_input = true; @@ -430,7 +555,6 @@ static int init_op_data_objs_enc(struct rte_bbdev_op_data *bufs, return 0; } - // DPDK BBEV copy static int allocate_buffers_on_socket(struct rte_bbdev_op_data **buffers, const int len, const int socket) { @@ -457,53 +581,96 @@ static void free_mempools(struct active_device *ad) rte_mempool_free(ad->bbdev_enc_op_pool); rte_mempool_free(ad->in_mbuf_pool); rte_mempool_free(ad->hard_out_mbuf_pool); + rte_mempool_free(ad->harq_in_mbuf_pool); + rte_mempool_free(ad->harq_out_mbuf_pool); } // based on DPDK BBDEV copy_reference_ldpc_dec_op -static void -set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, - struct rte_bbdev_op_data *inputs, - struct rte_bbdev_op_data *outputs, - nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) +static void set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, + struct rte_bbdev_op_data *inputs, + struct rte_bbdev_op_data *outputs, + struct rte_bbdev_op_data *harq_outputs, + nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) { int j = 0; +#ifdef LDPC_T2 + // The T2 only supports CB mode, and does not TB mode special case handling. + bool special_case_tb_mode = false; +#else + bool special_case_tb_mode = + (nrLDPC_slot_decoding_parameters->nb_TBs == 1) && (nb_segments_decoding(nrLDPC_slot_decoding_parameters) == 1); +#endif for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { for (int i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) { - ops[j]->ldpc_dec.cb_params.e = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E; ops[j]->ldpc_dec.basegraph = nrLDPC_slot_decoding_parameters->TBs[h].BG; ops[j]->ldpc_dec.z_c = nrLDPC_slot_decoding_parameters->TBs[h].Z; ops[j]->ldpc_dec.q_m = nrLDPC_slot_decoding_parameters->TBs[h].Qm; ops[j]->ldpc_dec.n_filler = nrLDPC_slot_decoding_parameters->TBs[h].F; - ops[j]->ldpc_dec.n_cb = (nrLDPC_slot_decoding_parameters->TBs[h].BG == 1) ? (66 * nrLDPC_slot_decoding_parameters->TBs[h].Z) : (50 * nrLDPC_slot_decoding_parameters->TBs[h].Z); + ops[j]->ldpc_dec.n_cb = (nrLDPC_slot_decoding_parameters->TBs[h].BG == 1) ? (66 * nrLDPC_slot_decoding_parameters->TBs[h].Z) + : (50 * nrLDPC_slot_decoding_parameters->TBs[h].Z); ops[j]->ldpc_dec.iter_max = nrLDPC_slot_decoding_parameters->TBs[h].max_ldpc_iterations; ops[j]->ldpc_dec.rv_index = nrLDPC_slot_decoding_parameters->TBs[h].rv_index; - ops[j]->ldpc_dec.op_flags = RTE_BBDEV_LDPC_ITERATION_STOP_ENABLE | - RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_IN_ENABLE | - RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_OUT_ENABLE | - RTE_BBDEV_LDPC_HQ_COMBINE_OUT_ENABLE; + ops[j]->ldpc_dec.op_flags = RTE_BBDEV_LDPC_ITERATION_STOP_ENABLE | RTE_BBDEV_LDPC_HQ_COMBINE_OUT_ENABLE; if (*nrLDPC_slot_decoding_parameters->TBs[h].segments[i].d_to_be_cleared) { *nrLDPC_slot_decoding_parameters->TBs[h].segments[i].d_to_be_cleared = false; - *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0; } else { ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_HQ_COMBINE_IN_ENABLE; + if (active_dev.support_internal_harq_memory) { + ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_IN_ENABLE; + ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_OUT_ENABLE; + } } - if (nrLDPC_slot_decoding_parameters->TBs[h].C > 1) { - ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_CRC_TYPE_24B_DROP; - ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_CRC_TYPE_24B_CHECK; + +#ifdef LDPC_T2 + // Note: For the T2, we do not reset the processedSegments in case of HARQ. + // This is because if T2 is asked to decode a segment that as already successfully + // decoded in the previous round, the T2 reports a failure. + if (*nrLDPC_slot_decoding_parameters->TBs[h].segments[i].d_to_be_cleared) *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0; +#else + *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0; +#endif + if (!special_case_tb_mode) { + ops[j]->ldpc_dec.code_block_mode = 1; + ops[j]->ldpc_dec.cb_params.e = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E; + if (nrLDPC_slot_decoding_parameters->TBs[h].C > 1) { + ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_CRC_TYPE_24B_DROP; + ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_CRC_TYPE_24B_CHECK; + } + } else { + /** + * This is a special case when #TB = 1 and #CB = 1 + * In this case, we must use TB mode + * Quoted from: https://doc.dpdk.org/guides-23.11/prog_guide/bbdev.html#bbdev-ldpc-decode-operation + * The case when one CB belongs to TB and is being enqueued individually to BBDEV, this case is considered as a + * special case of partial TB where its number of CBs is 1. Therefore, it requires to get processed in TB-mode. + */ + ops[j]->ldpc_dec.code_block_mode = 0; + ops[j]->ldpc_dec.tb_params.c = 1; + ops[j]->ldpc_dec.tb_params.r = 0; + ops[j]->ldpc_dec.tb_params.cab = 1; + ops[j]->ldpc_dec.tb_params.ea = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E; + ops[j]->ldpc_dec.tb_params.eb = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E; } - ops[j]->ldpc_dec.code_block_mode = 1; - // Calculate offset in the HARQ combined buffers // Unique segment offset uint32_t segment_offset = (nrLDPC_slot_decoding_parameters->TBs[h].harq_unique_pid * NR_LDPC_MAX_NUM_CB) + i; // Prune to avoid shooting above maximum id - uint32_t pruned_segment_offset = segment_offset % HARQ_CODEBLOCK_ID_MAX; + uint32_t pruned_segment_offset = segment_offset % active_dev.num_harq_codeblock; // Segment offset to byte offset uint32_t harq_combined_offset = pruned_segment_offset * LDPC_MAX_CB_SIZE; - ops[j]->ldpc_dec.harq_combined_input.offset = harq_combined_offset; - ops[j]->ldpc_dec.harq_combined_output.offset = harq_combined_offset; - + if (active_dev.support_internal_harq_memory) { + // retrieve corresponding HARQ output information from previous iteration, especially the length + ops[j]->ldpc_dec.harq_combined_input = active_dev.harq_buffers[pruned_segment_offset]; + // Note: When using INTERNAL_HARQ memory, the "offset" is used to point to a particular address + // within the BBDEV's onboard memory, and the address should be multiples of 32K. + harq_outputs[j].offset = harq_combined_offset; + ops[j]->ldpc_dec.harq_combined_output = harq_outputs[j]; + } else { + // retrieve corresponding HARQ buffers from previous iteration + ops[j]->ldpc_dec.harq_combined_input = active_dev.harq_buffers[pruned_segment_offset]; + ops[j]->ldpc_dec.harq_combined_output = harq_outputs[j]; + } ops[j]->ldpc_dec.hard_output = outputs[j]; ops[j]->ldpc_dec.input = inputs[j]; ++j; @@ -512,28 +679,51 @@ set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, } // based on DPDK BBDEV copy_reference_ldpc_enc_op -static void -set_ldpc_enc_op(struct rte_bbdev_enc_op **ops, - struct rte_bbdev_op_data *inputs, - struct rte_bbdev_op_data *outputs, - nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) +static void set_ldpc_enc_op(struct rte_bbdev_enc_op **ops, + struct rte_bbdev_op_data *inputs, + struct rte_bbdev_op_data *outputs, + nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) { int j = 0; +#ifdef LDPC_T2 + // The T2 only supports CB mode, and does not TB mode special case handling. + bool special_case_tb_mode = false; +#else + bool special_case_tb_mode = + (nrLDPC_slot_encoding_parameters->nb_TBs == 1) && (nb_segments_encoding(nrLDPC_slot_encoding_parameters) == 1); +#endif for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) { for (int i = 0; i < nrLDPC_slot_encoding_parameters->TBs[h].C; ++i) { - ops[j]->ldpc_enc.cb_params.e = nrLDPC_slot_encoding_parameters->TBs[h].segments[i].E; ops[j]->ldpc_enc.basegraph = nrLDPC_slot_encoding_parameters->TBs[h].BG; ops[j]->ldpc_enc.z_c = nrLDPC_slot_encoding_parameters->TBs[h].Z; ops[j]->ldpc_enc.q_m = nrLDPC_slot_encoding_parameters->TBs[h].Qm; ops[j]->ldpc_enc.n_filler = nrLDPC_slot_encoding_parameters->TBs[h].F; - ops[j]->ldpc_enc.n_cb = (nrLDPC_slot_encoding_parameters->TBs[h].BG == 1) ? (66 * nrLDPC_slot_encoding_parameters->TBs[h].Z) : (50 * nrLDPC_slot_encoding_parameters->TBs[h].Z); + ops[j]->ldpc_enc.n_cb = (nrLDPC_slot_encoding_parameters->TBs[h].BG == 1) ? (66 * nrLDPC_slot_encoding_parameters->TBs[h].Z) + : (50 * nrLDPC_slot_encoding_parameters->TBs[h].Z); if (nrLDPC_slot_encoding_parameters->TBs[h].tbslbrm != 0) { uint32_t Nref = 3 * nrLDPC_slot_encoding_parameters->TBs[h].tbslbrm / (2 * nrLDPC_slot_encoding_parameters->TBs[h].C); ops[j]->ldpc_enc.n_cb = min(ops[j]->ldpc_enc.n_cb, Nref); } ops[j]->ldpc_enc.rv_index = nrLDPC_slot_encoding_parameters->TBs[h].rv_index; ops[j]->ldpc_enc.op_flags = RTE_BBDEV_LDPC_RATE_MATCH; - ops[j]->ldpc_enc.code_block_mode = 1; + if (!special_case_tb_mode) { + ops[j]->ldpc_enc.code_block_mode = 1; + ops[j]->ldpc_enc.cb_params.e = nrLDPC_slot_encoding_parameters->TBs[h].segments[i].E; + } else { + /** + * This is a special case when #TB = 1 and #CB = 1 + * In this case, we must use TB mode + * Quoted from: https://doc.dpdk.org/guides-23.11/prog_guide/bbdev.html#bbdev-ldpc-decode-operation + * The case when one CB belongs to TB and is being enqueued individually to BBDEV, this case is considered as a + * special case of partial TB where its number of CBs is 1. Therefore, it requires to get processed in TB-mode. + */ + ops[j]->ldpc_enc.code_block_mode = 0; + ops[j]->ldpc_enc.tb_params.c = 1; + ops[j]->ldpc_enc.tb_params.r = 0; + ops[j]->ldpc_enc.tb_params.cab = 1; + ops[j]->ldpc_enc.tb_params.ea = nrLDPC_slot_encoding_parameters->TBs[h].segments[i].E; + ops[j]->ldpc_enc.tb_params.eb = nrLDPC_slot_encoding_parameters->TBs[h].segments[i].E; + } ops[j]->ldpc_enc.output = outputs[j]; ops[j]->ldpc_enc.input = inputs[j]; ++j; @@ -551,19 +741,30 @@ static int retrieve_ldpc_dec_op(struct rte_bbdev_dec_op **ops, nrLDPC_slot_decod uint16_t data_len = rte_pktmbuf_data_len(m) - hard_output->offset; uint8_t *data = rte_pktmbuf_mtod_offset(m, uint8_t *, hard_output->offset); memcpy(nrLDPC_slot_decoding_parameters->TBs[h].segments[i].c, data, data_len); + + uint32_t segment_offset = (nrLDPC_slot_decoding_parameters->TBs[h].harq_unique_pid * NR_LDPC_MAX_NUM_CB) + i; + uint32_t pruned_segment_offset = segment_offset % active_dev.num_harq_codeblock; + struct rte_bbdev_op_data *harq_output = &ops[j]->ldpc_dec.harq_combined_output; + if (!active_dev.support_internal_harq_memory) { + struct rte_mbuf *m_src = harq_output->data; + uint8_t *data_src = rte_pktmbuf_mtod_offset(m_src, uint8_t *, 0); + struct rte_mbuf *m_dst = active_dev.harq_buffers[pruned_segment_offset].data; + uint8_t *data_dst = rte_pktmbuf_mtod_offset(m_dst, uint8_t *, 0); + rte_memcpy(data_dst, data_src, harq_output->length); + } + active_dev.harq_buffers[pruned_segment_offset].offset = harq_output->offset; + active_dev.harq_buffers[pruned_segment_offset].length = harq_output->length; ++j; } } return 0; } -static int -retrieve_ldpc_enc_op(struct rte_bbdev_enc_op **ops, - nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) +static int retrieve_ldpc_enc_op(struct rte_bbdev_enc_op **ops, nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) { uint8_t *p_out = NULL; int j = 0; - for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h){ + for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) { int E_sum = 0; int bit_offset = 0; int byte_offset = 0; @@ -606,8 +807,7 @@ retrieve_ldpc_enc_op(struct rte_bbdev_enc_op **ops, } // based on DPDK BBDEV throughput_pmd_lcore_ldpc_dec -static int -pmd_lcore_ldpc_dec(void *arg) +static int pmd_lcore_ldpc_dec(void *arg) { struct thread_params *tp = arg; nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters = tp->nrLDPC_slot_decoding_parameters; @@ -620,15 +820,12 @@ pmd_lcore_ldpc_dec(void *arg) AssertFatal((num_segments < MAX_BURST), "BURST_SIZE should be <= %u", MAX_BURST); - struct rte_bbdev_info info; - rte_bbdev_info_get(tp->dev_id, &info); - while (rte_atomic16_read(&tp->op_params->sync) == SYNC_WAIT) rte_pause(); int ret = rte_bbdev_dec_op_alloc_bulk(tp->bbdev_op_pool, ops_enq, num_segments); AssertFatal(ret == 0, "Allocation failed for %d ops", num_segments); - set_ldpc_dec_op(ops_enq, bufs->inputs, bufs->hard_outputs, nrLDPC_slot_decoding_parameters); + set_ldpc_dec_op(ops_enq, bufs->inputs, bufs->hard_outputs, bufs->harq_outputs, nrLDPC_slot_decoding_parameters); uint16_t enq = 0, deq = 0; while (enq < num_segments) { @@ -656,24 +853,19 @@ pmd_lcore_ldpc_dec(void *arg) // Check if CRC is available otherwise rely on ops_enq[j]->status to detect decoding success // CRC is NOT available if the CRC type is 24_B which is when C is greater than 1 - if (nrLDPC_slot_decoding_parameters->TBs[h].C > 1) { - *status = (ops_enq[j]->status == 0); - } else { - uint8_t *decoded_bytes = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].c; uint8_t crc_type = crcType(nrLDPC_slot_decoding_parameters->TBs[h].C, nrLDPC_slot_decoding_parameters->TBs[h].A); uint32_t len_with_crc = lenWithCrc(nrLDPC_slot_decoding_parameters->TBs[h].C, nrLDPC_slot_decoding_parameters->TBs[h].A); *status = check_crc(decoded_bytes, len_with_crc, crc_type); - } if (*status) { - *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments + 1; + *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = + *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments + 1; } - ++j; } } @@ -698,9 +890,6 @@ static int pmd_lcore_ldpc_enc(void *arg) AssertFatal((num_segments < MAX_BURST), "BURST_SIZE should be <= %u", MAX_BURST); - struct rte_bbdev_info info; - rte_bbdev_info_get(tp->dev_id, &info); - while (rte_atomic16_read(&tp->op_params->sync) == SYNC_WAIT) rte_pause(); @@ -763,7 +952,8 @@ int start_pmd_dec(struct active_device *ad, t_params[0].nrLDPC_slot_decoding_parameters = nrLDPC_slot_decoding_parameters; used_cores++; // For now, we never enter here, we don't use the DPDK thread pool - RTE_LCORE_FOREACH_WORKER(lcore_id) { + RTE_LCORE_FOREACH_WORKER(lcore_id) + { if (used_cores >= num_lcores) break; t_params[used_cores].dev_id = ad->dev_id; @@ -807,7 +997,8 @@ int32_t start_pmd_enc(struct active_device *ad, t_params[0].nrLDPC_slot_encoding_parameters = nrLDPC_slot_encoding_parameters; used_cores++; // For now, we never enter here, we don't use the DPDK thread pool - RTE_LCORE_FOREACH_WORKER(lcore_id) { + RTE_LCORE_FOREACH_WORKER(lcore_id) + { if (used_cores >= num_lcores) break; t_params[used_cores].dev_id = ad->dev_id; @@ -828,46 +1019,110 @@ int32_t start_pmd_enc(struct active_device *ad, struct test_op_params *op_params = NULL; +static int normalize_dpdk_dev(const char *input, char *output, size_t out_len) +{ + regex_t regex_full, regex_short; + int reti; + + // patterns + const char *pattern_full = "^[0]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\\.[0-7]$"; + const char *pattern_short = "^[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\\.[0-7]$"; + regcomp(®ex_full, pattern_full, REG_EXTENDED); + regcomp(®ex_short, pattern_short, REG_EXTENDED); + + // check full format + reti = regexec(®ex_full, input, 0, NULL, 0); + if (reti == 0) { + // already in full format + strncpy(output, input, out_len - 1); + output[out_len - 1] = '\0'; + } else { + // check short format + reti = regexec(®ex_short, input, 0, NULL, 0); + if (reti == 0) { + // convert to full format + snprintf(output, out_len, "0000:%s", input); + } else { + // invalid format + regfree(®ex_full); + regfree(®ex_short); + return -1; + } + } + regfree(®ex_full); + regfree(®ex_short); + return 0; +} + // OAI CODE int32_t nrLDPC_coding_init() { pthread_mutex_init(&encode_mutex, NULL); pthread_mutex_init(&decode_mutex, NULL); + int ret; - int dev_id = 0; - struct rte_bbdev_info info; - struct active_device *ad = active_devs; - char *dpdk_dev = NULL; //PCI address of the card - char *dpdk_core_list = NULL; // cores used by DPDK for T2 + int dev_id = -1; + + char *dpdk_dev = NULL; // PCI address of the card + char *dpdk_core_list = NULL; // cores used by DPDK for bbdev char *dpdk_file_prefix = NULL; + char *vfio_vf_token = NULL; // vfio token for the bbdev card + uint32_t num_harq_codeblock = 0; // size of the HARQ buffer in terms of the number of 32K blocks paramdef_t LoaderParams[] = { - {"dpdk_dev", NULL, 0, .strptr = &dpdk_dev, .defstrval = NULL, TYPE_STRING, 0, NULL}, - {"dpdk_core_list", NULL, 0, .strptr = &dpdk_core_list, .defstrval = NULL, TYPE_STRING, 0, NULL}, - {"dpdk_file_prefix", NULL, 0, .strptr = &dpdk_file_prefix, .defstrval = "b6", TYPE_STRING, 0, NULL} + {"dpdk_dev", NULL, 0, .strptr = &dpdk_dev, .defstrval = NULL, TYPE_STRING, 0, NULL}, + {"dpdk_core_list", NULL, 0, .strptr = &dpdk_core_list, .defstrval = NULL, TYPE_STRING, 0, NULL}, + {"dpdk_file_prefix", NULL, 0, .strptr = &dpdk_file_prefix, .defstrval = "b6", TYPE_STRING, 0, NULL}, + {"vfio_vf_token", NULL, 0, .strptr = &vfio_vf_token, .defstrval = NULL, TYPE_STRING, 0, NULL}, + {"num_harq_codeblock", NULL, 0, .uptr = &num_harq_codeblock, .defintval = 512, TYPE_UINT32, 0, NULL}, }; config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_t2"); - AssertFatal(dpdk_dev!=NULL, "nrLDPC_coding_t2.dpdk_dev was not provided"); - AssertFatal(dpdk_core_list!=NULL, "nrLDPC_coding_t2.dpdk_core_list was not provided"); - char *argv_re[] = {"bbdev", "-a", dpdk_dev, "-l", dpdk_core_list, "--file-prefix", dpdk_file_prefix, "--"}; - // EAL initialization, if already initialized (init in xran lib) try to probe DPDK device - ret = rte_eal_init(sizeofArray(argv_re), argv_re); + AssertFatal(dpdk_dev != NULL, "nrLDPC_coding_t2.dpdk_dev was not provided"); + AssertFatal(dpdk_core_list != NULL, "nrLDPC_coding_t2.dpdk_core_list was not provided"); + + char dpdk_dev_full[32]; + if (normalize_dpdk_dev(dpdk_dev, dpdk_dev_full, sizeof(dpdk_dev_full)) != 0) { + LOG_E(NR_PHY, "invalid DPDK device format: %s\n", dpdk_dev); + return -1; + } + + int argc = 7; + char *argv[11] = {"bbdev", "-l", dpdk_core_list, "-a", dpdk_dev_full, "--file-prefix", dpdk_file_prefix, "--", "--", "--", "--"}; + if (vfio_vf_token != NULL) { + argc += 2; + argv[7] = "--vfio-vf-token"; + argv[8] = vfio_vf_token; + } + ret = rte_eal_init(argc, argv); if (ret < 0) { - printf("EAL initialization failed, probing DPDK device %s\n", dpdk_dev); - if (rte_dev_probe(dpdk_dev) != 0) { - LOG_E(PHY, "T2 card %s not found\n", dpdk_dev); + LOG_W(NR_PHY, "EAL initialization failed, probing DPDK device %s\n", dpdk_dev_full); + if (rte_dev_probe(dpdk_dev_full) != 0) { + LOG_E(NR_PHY, "bbdev %s not found\n", dpdk_dev_full); return (-1); } } - // Use only device 0 - first detected device - rte_bbdev_info_get(0, &info); - // Set number of queues based on number of initialized cores (-l option) and driver - // capabilities - AssertFatal(add_dev(dev_id, &info)== 0, "Failed to setup bbdev"); + uint16_t nb_bbdevs = rte_bbdev_count(); + AssertFatal(nb_bbdevs > 0, "no bbdev found"); + + // find the baseband device that matches the dpdk_dev specified in the configurations + struct rte_bbdev_info info; + LOG_I(NR_PHY, "detected %u bbdev.\n", nb_bbdevs); + for (uint16_t device_id = 0; device_id < nb_bbdevs; device_id++) { + rte_bbdev_info_get(device_id, &info); + // check if info matches the dpdk_dev that we are looking for + if (strcmp(info.dev_name, dpdk_dev_full) == 0) { + LOG_I(NR_PHY, "bbdev %s found.\n", info.dev_name); + dev_id = device_id; + break; + } + } + AssertFatal(dev_id != -1, "bbdev %s not found.", dpdk_dev_full); + + AssertFatal(add_dev(dev_id, num_harq_codeblock) == 0, "Failed to setup bbdev"); AssertFatal(rte_bbdev_stats_reset(dev_id) == 0, "Failed to reset stats of bbdev %u", dev_id); AssertFatal(rte_bbdev_start(dev_id) == 0, "Failed to start bbdev %u", dev_id); - //the previous calls have populated this global variable (beurk) - // One more global to remove, not thread safe global op_params + // the previous calls have populated this global variable (beurk) + // One more global to remove, not thread safe global op_params op_params = rte_zmalloc(NULL, sizeof(struct test_op_params), RTE_CACHE_LINE_SIZE); AssertFatal(op_params != NULL, "Failed to alloc %zuB for op_params", @@ -877,34 +1132,119 @@ int32_t nrLDPC_coding_init() int out_max_sz = 8448; // max code block size (for BG1), 22 * 384 int in_max_sz = LDPC_MAX_CB_SIZE; // max number of encoded bits (for BG2 and MCS0) int num_queues = 1; - int f_ret = create_mempools(ad, socket_id, num_queues, out_max_sz, in_max_sz); + int f_ret = create_mempools(&active_dev, socket_id, num_queues, out_max_sz, in_max_sz); if (f_ret != 0) { printf("Couldn't create mempools"); return -1; } + + // initialize persistent data structure to keep track of HARQ-related information + init_op_data_objs_harq(active_dev.harq_buffers, active_dev.harq_in_mbuf_pool); + op_params->num_lcores = 1; return 0; } int32_t nrLDPC_coding_shutdown() { - struct active_device *ad = active_devs; int dev_id = 0; struct rte_bbdev_stats stats; - free_mempools(ad); + free(active_dev.harq_buffers); + free_mempools(&active_dev); rte_free(op_params); rte_bbdev_stats_get(dev_id, &stats); rte_bbdev_stop(dev_id); rte_bbdev_close(dev_id); - memset(active_devs, 0, sizeof(active_devs)); - nb_active_devs = 0; + memset(&active_dev, 0, sizeof(active_dev)); return 0; } +static void llr_scaling(int16_t *llr, int llr_len, uint8_t *llr_scaled, int8_t llr_size, int8_t llr_decimal, int8_t nb_layers, int8_t Qm) +{ + const int16_t llr_max = (1 << (llr_size - 1)) - 1; + const int16_t llr_min = -llr_max; + + // Step 1: Find the max absolute LLR + int16_t max_abs = 1; // prevent divide-by-zero + // SCALAR IMPLEMENTATION + // for (int i = 0; i < llr_len; i++) { + // int16_t abs_val = abs(llr[i]); + // if (abs_val > max_abs) max_abs = abs_val; + // } + // VECTORIZED IMPLEMENTATION + simde__m128i max_vec = simde_mm_set1_epi16(1); + for (int i = 0; i < llr_len; i += 8) { + simde__m128i llr_vec = simde_mm_loadu_si128((simde__m128i *)&llr[i]); + simde__m128i abs_vec = simde_mm_abs_epi16(llr_vec); + max_vec = simde_mm_max_epi16(max_vec, abs_vec); + } + // reduce max_vec to single max_abs + int16_t temp[8]; + simde_mm_storeu_si128((simde__m128i *)temp, max_vec); + for (int i = 0; i < 8; i++) { + if (temp[i] > max_abs) + max_abs = temp[i]; + } + + // Step 2: Compute dynamic scale factor + float fixed_point_range = (float)llr_max / (1 << llr_decimal); + float scale = fixed_point_range / (float)max_abs; + if (max_abs < fixed_point_range) { + scale = 1.0f; + } + + // Step 3: Scale and saturate + // SCALAR IMPLEMENTATION + // for (int i = 0; i < llr_len; i++) { + // float scaled = (float)llr[i] * scale; // map into fixed-point domain + // scaled = (int8_t)roundf(scaled * (1 << llr_decimal)); + // // Clamp to [-128, 127] + // if (scaled > llr_max) llr_scaled[i] = llr_max; + // else if (scaled < llr_min) llr_scaled[i] = llr_min; + // } + // VECTORIZED IMPLEMENTATION + simde__m128 scale_vec = simde_mm_set1_ps(scale); + simde__m128i llr_max_vec = simde_mm_set1_epi16(llr_max); + simde__m128i llr_min_vec = simde_mm_set1_epi16(llr_min); + simde__m128i decimal_shift = simde_mm_set1_epi16(1 << llr_decimal); + for (int i = 0; i < llr_len; i += 8) { + // load LLR values + simde__m128i llr_vec = simde_mm_loadu_si128((simde__m128i *)&llr[i]); + + // convert to float for scaling + simde__m128i llr_lo = simde_mm_cvtepi16_epi32(llr_vec); + simde__m128i llr_hi = simde_mm_cvtepi16_epi32(simde_mm_srli_si128(llr_vec, 8)); + simde__m128 float_lo = simde_mm_cvtepi32_ps(llr_lo); + simde__m128 float_hi = simde_mm_cvtepi32_ps(llr_hi); + + // scale + float_lo = simde_mm_mul_ps(float_lo, scale_vec); + float_hi = simde_mm_mul_ps(float_hi, scale_vec); + + // convert back to int16 with saturation + llr_lo = simde_mm_cvtps_epi32(float_lo); + llr_hi = simde_mm_cvtps_epi32(float_hi); + simde__m128i scaled_vec = simde_mm_packs_epi32(llr_lo, llr_hi); + + scaled_vec = simde_mm_mullo_epi16(scaled_vec, decimal_shift); + + // clamp to [llr_min, llr_max] + scaled_vec = simde_mm_min_epi16(scaled_vec, llr_max_vec); + scaled_vec = simde_mm_max_epi16(scaled_vec, llr_min_vec); + + // Pack to int8 and store + simde__m128i result = simde_mm_packs_epi16(scaled_vec, simde_mm_setzero_si128()); + simde_mm_storeu_si128((simde__m128i *)&llr_scaled[i], result); + } +} + int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) { pthread_mutex_lock(&decode_mutex); + int ret; + int socket_id = active_dev.info.socket_id; + const uint16_t num_segments = nb_segments_decoding(nrLDPC_slot_decoding_parameters); /* It is not unlikely that l_ol becomes big enough to overflow the stack @@ -913,27 +1253,41 @@ int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_dec */ uint8_t l_ol[num_segments * LDPC_MAX_CB_SIZE] __attribute__((aligned(16))); - // hardcoded we use first device - struct active_device *ad = active_devs; - struct rte_bbdev_info info; - int ret; - rte_bbdev_info_get(ad->dev_id, &info); - int socket_id = GET_SOCKET(info.socket_id); // fill_queue_buffers -> init_op_data_objs - struct rte_mempool *mbuf_pools[DATA_NUM_TYPES] = {ad->in_mbuf_pool, ad->hard_out_mbuf_pool}; + struct rte_mempool *mbuf_pools[DATA_NUM_TYPES] = {active_dev.in_mbuf_pool, + active_dev.hard_out_mbuf_pool, + active_dev.harq_out_mbuf_pool}; struct data_buffers data_buffers; - struct rte_bbdev_op_data **queue_ops[DATA_NUM_TYPES] = {&data_buffers.inputs, &data_buffers.hard_outputs}; - + struct rte_bbdev_op_data **queue_ops[DATA_NUM_TYPES] = {&data_buffers.inputs, + &data_buffers.hard_outputs, + &data_buffers.harq_outputs}; +#ifndef LDPC_T2 + int8_t llr_size = (active_dev.info.drv.capabilities)[RTE_BBDEV_OP_LDPC_DEC].cap.ldpc_dec.llr_size; + int8_t llr_decimal = (active_dev.info.drv.capabilities)[RTE_BBDEV_OP_LDPC_DEC].cap.ldpc_dec.llr_decimals; +#endif int offset = 0; for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { for (int r = 0; r < nrLDPC_slot_decoding_parameters->TBs[h].C; r++) { +#ifdef LDPC_T2 + // For the T2, we simply saturate the LLRs. uint16_t z_ol[LDPC_MAX_CB_SIZE] __attribute__((aligned(16))); - memcpy(z_ol, nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr, nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E * sizeof(uint16_t)); + memcpy(z_ol, + nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr, + nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E * sizeof(uint16_t)); simde__m128i *pv_ol128 = (simde__m128i *)z_ol; simde__m128i *pl_ol128 = (simde__m128i *)&l_ol[offset]; for (int i = 0, j = 0; j < ((nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E + 15) >> 4); i += 2, j++) { pl_ol128[j] = simde_mm_packs_epi16(pv_ol128[i], pv_ol128[i + 1]); } +#else + llr_scaling(nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr, + nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E, + &l_ol[offset], + llr_size, + llr_decimal, + nrLDPC_slot_decoding_parameters->TBs[h].nb_layers, + nrLDPC_slot_decoding_parameters->TBs[h].Qm); +#endif offset += LDPC_MAX_CB_SIZE; } } @@ -946,13 +1300,13 @@ int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_dec nrLDPC_slot_decoding_parameters, mbuf_pools[type], type, - info.drv.min_alignment); + active_dev.info.drv.min_alignment); AssertFatal(ret == 0, "Couldn't init rte_bbdev_op_data structs"); } - ret = start_pmd_dec(ad, op_params, &data_buffers, nrLDPC_slot_decoding_parameters); + ret = start_pmd_dec(&active_dev, op_params, &data_buffers, nrLDPC_slot_decoding_parameters); if (ret < 0) { - LOG_E(PHY, "Couldn't start pmd dec\n"); + LOG_E(NR_PHY, "Couldn't start pmd dec\n"); } for (enum op_data_type type = DATA_INPUT; type < DATA_NUM_TYPES; ++type) { @@ -973,27 +1327,28 @@ int32_t nrLDPC_coding_encoder(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_enc const uint16_t num_segments = nb_segments_encoding(nrLDPC_slot_encoding_parameters); - // hardcoded to use the first found board - struct active_device *ad = active_devs; int ret; - struct rte_bbdev_info info; - rte_bbdev_info_get(ad->dev_id, &info); - int socket_id = GET_SOCKET(info.socket_id); - // fill_queue_buffers -> init_op_data_objs - struct rte_mempool *mbuf_pools[DATA_NUM_TYPES] = {ad->in_mbuf_pool, ad->hard_out_mbuf_pool}; - struct data_buffers data_buffers; - struct rte_bbdev_op_data **queue_ops[DATA_NUM_TYPES] = {&data_buffers.inputs, &data_buffers.hard_outputs}; + int socket_id = active_dev.info.socket_id; - for (enum op_data_type type = DATA_INPUT; type < DATA_NUM_TYPES; ++type) { + // fill_queue_buffers -> init_op_data_objs + struct rte_mempool *mbuf_pools[2] = {active_dev.in_mbuf_pool, active_dev.hard_out_mbuf_pool}; + struct data_buffers data_buffers; + struct rte_bbdev_op_data **queue_ops[2] = {&data_buffers.inputs, &data_buffers.hard_outputs}; + + for (enum op_data_type type = DATA_INPUT; type < 2; ++type) { ret = allocate_buffers_on_socket(queue_ops[type], num_segments * sizeof(struct rte_bbdev_op_data), socket_id); AssertFatal(ret == 0, "Couldn't allocate memory for rte_bbdev_op_data structs"); - ret = init_op_data_objs_enc(*queue_ops[type], nrLDPC_slot_encoding_parameters, mbuf_pools[type], type, info.drv.min_alignment); + ret = init_op_data_objs_enc(*queue_ops[type], + nrLDPC_slot_encoding_parameters, + mbuf_pools[type], + type, + active_dev.info.drv.min_alignment); AssertFatal(ret == 0, "Couldn't init rte_bbdev_op_data structs"); } - ret = start_pmd_enc(ad, op_params, &data_buffers, nrLDPC_slot_encoding_parameters); + ret = start_pmd_enc(&active_dev, op_params, &data_buffers, nrLDPC_slot_encoding_parameters); - for (enum op_data_type type = DATA_INPUT; type < DATA_NUM_TYPES; ++type) { + for (enum op_data_type type = DATA_INPUT; type < 2; ++type) { for (int segment = 0; segment < num_segments; ++segment) rte_pktmbuf_free((*queue_ops[type])[segment].data); rte_free(*queue_ops[type]); @@ -1002,4 +1357,3 @@ int32_t nrLDPC_coding_encoder(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_enc pthread_mutex_unlock(&encode_mutex); return ret; } - diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h index 642ac8f55a..0337202b9b 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h @@ -2,8 +2,8 @@ * Copyright(c) 2017 Intel Corporation */ -#ifndef _MAIN_H_ -#define _MAIN_H_ +#ifndef __NRLDPC_CODING_T2_H__ +#define __NRLDPC_CODING_T2_H__ #include #include @@ -17,6 +17,7 @@ enum op_data_type { DATA_INPUT = 0, DATA_HARD_OUTPUT, + DATA_HARQ_OUTPUT, DATA_NUM_TYPES, }; From c0d797ec0e16ebb77da0cc94330c031e19c4dcf7 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 4 Aug 2025 16:47:12 +0200 Subject: [PATCH 48/86] Rename libldpc_t2 to libldpc_aal Rename the library to reflect that it's more a generic AAL driver than specifically for AAL. To this end, rename some documentation. As of this commit, the options are still called nrLDPC_coding_t2. The next commit will correct this. Co-authored-by: Xin Zhe Khooi --- ci-scripts/xml_files/gnb_usrp_build.xml | 2 +- .../xml_files/t2_offload_dec_nr_ulsim.xml | 18 ++++---- .../xml_files/t2_offload_enc_nr_dlsim.xml | 24 +++++------ cmake_targets/build_oai | 2 +- doc/BUILD.md | 2 +- ...OFFLOAD_SETUP.md => LDPC_OFFLOAD_SETUP.md} | 18 ++++---- doc/README.md | 2 +- openair1/PHY/CODING/DOC/LDPCImplementation.md | 24 +++++------ .../PHY/CODING/nrLDPC_coding/CMakeLists.txt | 2 +- .../nrLDPC_coding_aal/CMakeLists.txt | 37 +++++++++++++++++ .../README.md | 2 +- .../nrLDPC_coding_aal.c} | 9 +--- .../nrLDPC_coding_aal.h} | 0 .../nrLDPC_coding_t2/CMakeLists.txt | 41 ------------------- 14 files changed, 87 insertions(+), 96 deletions(-) rename doc/{LDPC_T2_OFFLOAD_SETUP.md => LDPC_OFFLOAD_SETUP.md} (92%) create mode 100644 openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/CMakeLists.txt rename openair1/PHY/CODING/nrLDPC_coding/{nrLDPC_coding_t2 => nrLDPC_coding_aal}/README.md (99%) rename openair1/PHY/CODING/nrLDPC_coding/{nrLDPC_coding_t2/nrLDPC_coding_t2.c => nrLDPC_coding_aal/nrLDPC_coding_aal.c} (99%) rename openair1/PHY/CODING/nrLDPC_coding/{nrLDPC_coding_t2/nrLDPC_coding_t2.h => nrLDPC_coding_aal/nrLDPC_coding_aal.h} (100%) delete mode 100644 openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt diff --git a/ci-scripts/xml_files/gnb_usrp_build.xml b/ci-scripts/xml_files/gnb_usrp_build.xml index f67098116b..db6450b259 100644 --- a/ci-scripts/xml_files/gnb_usrp_build.xml +++ b/ci-scripts/xml_files/gnb_usrp_build.xml @@ -40,7 +40,7 @@ TesteNB Build_eNB Build gNB (USRP) - --gNB -w USRP --ninja -c -P --build-lib ldpc_cuda --build-lib ldpc_t2 --cmake-opt -DASN1C_EXEC=/opt/asn1c/bin/asn1c + --gNB -w USRP --ninja -c -P --build-lib ldpc_cuda --build-lib ldpc_aal --cmake-opt -DASN1C_EXEC=/opt/asn1c/bin/asn1c True diff --git a/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml b/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml index 15b8921a5a..c721441907 100644 --- a/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml +++ b/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml @@ -62,7 +62,7 @@ true nr_ulsim 100 - -n1000 -s30 -S30.2 -m5 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -80,7 +80,7 @@ true nr_ulsim 150 - -n1000 -s30 -S30.2 -m15 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -98,7 +98,7 @@ true nr_ulsim 250 - -n1000 -s30 -S30.2 -m25 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -116,7 +116,7 @@ true nr_ulsim 150 - -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -134,7 +134,7 @@ true nr_ulsim 350 - -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -152,7 +152,7 @@ true nr_ulsim 550 - -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -170,7 +170,7 @@ true nr_ulsim 250 - -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -188,7 +188,7 @@ true nr_ulsim 650 - -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -206,7 +206,7 @@ true nr_ulsim 1100 - -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 diff --git a/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml b/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml index 7feb93741a..14f488dc6c 100644 --- a/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml +++ b/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml @@ -63,7 +63,7 @@ true nr_dlsim 100 - -n1000 -s30 -S30.2 -e5 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -81,7 +81,7 @@ true nr_dlsim 100 - -n1000 -s30 -S30.2 -e15 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -99,7 +99,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e25 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -117,7 +117,7 @@ true nr_dlsim 150 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -135,7 +135,7 @@ true nr_dlsim 250 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -153,7 +153,7 @@ true nr_dlsim 400 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -171,7 +171,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -189,7 +189,7 @@ true nr_dlsim 500 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -207,7 +207,7 @@ true nr_dlsim 500 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -225,7 +225,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -243,7 +243,7 @@ true nr_dlsim 300 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 @@ -261,6 +261,6 @@ true nr_dlsim 450 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index 0b6f31d930..292e8d0575 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -45,7 +45,7 @@ BUILD_DOXYGEN=0 DISABLE_HARDWARE_DEPENDENCY="False" CMAKE_BUILD_TYPE="RelWithDebInfo" CMAKE_CMD="$CMAKE" -OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_t2 ldpc_xdma websrv oai_iqplayer imscope imscope_record" +OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_aal ldpc_xdma websrv oai_iqplayer imscope imscope_record" TARGET_LIST="" BUILD_TOOL_OPT="-j$(nproc)" diff --git a/doc/BUILD.md b/doc/BUILD.md index 4d629007b0..8298204859 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -185,7 +185,7 @@ Some libraries have further dependencies and might not build on every system: - `enbscope`, `uescope`, `nrscope`: libforms/X - `ldpc_cuda`: CUDA - `websrv`: npm and others -- `ldpc_t2`: DPDK with patch +- `ldpc_aal`: DPDK with patch # Running `cmake` directly diff --git a/doc/LDPC_T2_OFFLOAD_SETUP.md b/doc/LDPC_OFFLOAD_SETUP.md similarity index 92% rename from doc/LDPC_T2_OFFLOAD_SETUP.md rename to doc/LDPC_OFFLOAD_SETUP.md index 0e9506db78..bada5c36df 100644 --- a/doc/LDPC_T2_OFFLOAD_SETUP.md +++ b/doc/LDPC_OFFLOAD_SETUP.md @@ -17,7 +17,7 @@ [[_TOC_]] This documentation describes the integration of LDPC coding for lookaside acceleration using O-RAN AAL/DPDK BBDEV in OAI, along with its usage. -For details on the implementation, please consult the [developer notes](../openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/README.md). +For details on the implementation, please consult the [developer notes](../openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/README.md). # Requirements @@ -209,12 +209,12 @@ cd ~/openairinterface5g/cmake_targets cd ~/openairinterface5g source oaienv cd cmake_targets -./build_oai -w USRP --ninja --gNB -P --build-lib "ldpc_t2" -C +./build_oai -w USRP --ninja --gNB -P --build-lib "ldpc_aal" -C ``` -A shared object file `libldpc_t2.so` will be created during the compilation. +A shared object file `libldpc_aal.so` will be created during the compilation. This object is conditionally compiled. -The selection of the library to compile is done using `--build-lib ldpc_t2`. +The selection of the library to compile is done using `--build-lib ldpc_aal`. > Note: The required DPDK poll mode driver has to be present on the host machine and required DPDK version has to be installed on the host, prior to building OAI. @@ -245,14 +245,14 @@ nrLDPC_coding_t2 : { loader : { ldpc : { - shlibversion : "_t2"; + shlibversion : "_aal"; }; }; ``` # Running OAI with O-RAN AAL -In general, to offload of the channel coding to the LDPC accelerator, we use use the `--loader.ldpc.shlibversion _t2` option. +In general, to offload of the channel coding to the LDPC accelerator, we use use the `--loader.ldpc.shlibversion _aal` option. ## 5G PHY simulators @@ -263,7 +263,7 @@ Example command: cd ~/openairinterface5g source oaienv cd cmake_targets/ran_build/build -sudo ./nr_ulsim -n100 -s20 -m20 -r273 -R273 --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 0-1 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff +sudo ./nr_ulsim -n100 -s20 -m20 -r273 -R273 --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 0-1 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff ``` ### nr_dlsim @@ -272,7 +272,7 @@ Example command: cd ~/openairinterface5g source oaienv cd cmake_targets/ran_build/build -sudo ./nr_dlsim -n300 -s30 -R 106 -e 27 --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 0-1 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff +sudo ./nr_dlsim -n300 -s30 -R 106 -e 27 --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 0-1 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff ``` ## OTA test @@ -284,7 +284,7 @@ Example command: cd ~/openairinterface5g source oaienv cd cmake_targets/ran_build/build -sudo ./nr-softmodem -O ~/gnb.conf --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 14-15 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff --nrLDPC_coding_t2.eal_init_bbdev 1 +sudo ./nr-softmodem -O ~/gnb.conf --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 14-15 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff --nrLDPC_coding_t2.eal_init_bbdev 1 ``` # Known Issue(s) diff --git a/doc/README.md b/doc/README.md index 756294fb5d..0776e627c9 100644 --- a/doc/README.md +++ b/doc/README.md @@ -64,7 +64,7 @@ There is some general information in the [OpenAirInterface Gitlab Wiki](https:// - [How to run with E2 agent](../openair2/E2AP/README.md) - [How to run the physical simulators](./physical-simulators.md) - [How to setup OAI with Nvidia Aerial and Foxconn](./Aerial_FAPI_Split_Tutorial.md) -- [How to setup OAI with AMD T2 Telco card](./LDPC_T2_OFFLOAD_SETUP.md) +- [How to setup OAI with LDPC accelerators (Xilinx T2/Intel ACCs)](./LDPC_OFFLOAD_SETUP.md) - [How to do a handover](./handover-tutorial.md) - [How to setup gNB frequency](./gNB_frequency_setup.md) diff --git a/openair1/PHY/CODING/DOC/LDPCImplementation.md b/openair1/PHY/CODING/DOC/LDPCImplementation.md index dc2959bc73..956dad97bf 100644 --- a/openair1/PHY/CODING/DOC/LDPCImplementation.md +++ b/openair1/PHY/CODING/DOC/LDPCImplementation.md @@ -28,35 +28,35 @@ shlib_path libldpc.so `libldpc.so` has its decoder implemented in [nrLDPC_coding_segment_decoder.c](file://../nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c).\ Its encoder is implemented in [nrLDPC_coding_segment_encoder.c](file://../nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c). -loading `libldpc_t2.so` instead of `libldpc.so`: +loading `libldpc_aal.so` instead of `libldpc.so`: -`make ldpc_t2` +`make ldpc_aal` -This command creates the `libldpc_t2.so` shared library. +This command creates the `libldpc_aal.so` shared library. ``` -Building C object CMakeFiles/ldpc_t2.dir/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c.o -Linking C shared module libldpc_t2.so +Building C object CMakeFiles/ldpc_aal.dir/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c.o +Linking C shared module libldpc_aal.so ``` At runtime, to successfully use LDPC accelerators (e.g., Xilinx T2/Intel ACCs), you will need to install the corresponding drivers and tools. -Please refer to the dedicated documentation at [LDPC_T2_OFFLOAD_SETUP.md](file://../../../../doc/LDPC_T2_OFFLOAD_SETUP.md). +Please refer to the dedicated documentation at [LDPC_OFFLOAD_SETUP.md](file://../../../../doc/LDPC_OFFLOAD_SETUP.md). -`./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _t2 --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1` +`./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1` ``` ....................... [CONFIG] loader.ldpc.shlibversion set to default value "" [LIBCONFIG] loader.ldpc: 2/2 parameters successfully set, (1 to default value) -[CONFIG] shlibversion set to _t2 from command line +[CONFIG] shlibversion set to _aal from command line [CONFIG] loader.ldpc 1 options set from command line -shlib_path libldpc_t2.so -[LOADER] library libldpc_t2.so successfully loaded +shlib_path libldpc_aal.so +[LOADER] library libldpc_aal.so successfully loaded ........................ ``` -`libldpc_t2.so` has its decoder and its encoder implemented in [nrLDPC_coding_t2.c](file://../nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c). +`libldpc_aal.so` has its decoder and its encoder implemented in [nrLDPC_coding_aal.c](file://../nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c). loading `libldpc_xdma.so` instead of `libldpc.so`: @@ -103,7 +103,7 @@ Libraries implementing the slotwise LDPC coding must be named `libldpc<_version> `libldpc.so` is completed. -`libldpc_t2.so` is completed. +`libldpc_aal.so` is completed. `libldpc_xdma.so` is completed. diff --git a/openair1/PHY/CODING/nrLDPC_coding/CMakeLists.txt b/openair1/PHY/CODING/nrLDPC_coding/CMakeLists.txt index e60ca479bb..356aa5f2a0 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/CMakeLists.txt +++ b/openair1/PHY/CODING/nrLDPC_coding/CMakeLists.txt @@ -1,3 +1,3 @@ add_subdirectory(nrLDPC_coding_segment) add_subdirectory(nrLDPC_coding_xdma) -add_subdirectory(nrLDPC_coding_t2) +add_subdirectory(nrLDPC_coding_aal) diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/CMakeLists.txt b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/CMakeLists.txt new file mode 100644 index 0000000000..eeff46cd14 --- /dev/null +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/CMakeLists.txt @@ -0,0 +1,37 @@ +########################################################## +# LDPC offload library - O-RAN AAL/DPDK BBDEV +########################################################## + +add_boolean_option(ENABLE_LDPC_AAL OFF "Build support for LDPC Offload using O-RAN AAL library" OFF) + +if (ENABLE_LDPC_AAL) + pkg_check_modules(LIBDPDK_AAL REQUIRED libdpdk>=20.11.9) + + find_library(PMD_T2 NAMES rte_baseband_accl_ldpc HINTS ${LIBDPDK_AAL_LIBRARY_DIRS}) + if (PMD_T2) + add_compile_definitions(LDPC_T2) + message(STATUS "found poll-mode driver for AccelerComm T2 LDPC Offload (rte_baseband_accl_ldpc.so)") + else() + if (LIBDPDK_AAL_VERSION VERSION_LESS "22.11") + message(FATAL_ERROR "for non-T2 BBDEV PMDs, we support the Intel ACC100 and ACC200 (VRB1), using DPDK 22.11 or later.") + endif() + endif() + + add_library(ldpc_aal MODULE nrLDPC_coding_aal.c) + + set_target_properties(ldpc_aal PROPERTIES COMPILE_FLAGS "-DALLOW_EXPERIMENTAL_API") + set_target_properties(ldpc_aal PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + target_include_directories(ldpc_aal PRIVATE ${LIBDPDK_AAL_INCLUDE_DIRS}) + target_link_libraries(ldpc_aal PRIVATE ldpc_gen_HEADERS ${LIBDPDK_AAL_LDFLAGS}) + if (PMD_T2) + target_link_libraries(ldpc_aal PRIVATE ${PMD_T2}) + endif() + + add_dependencies(nr-softmodem ldpc_aal) + add_dependencies(nr-uesoftmodem ldpc_aal) + add_dependencies(nr_ulsim ldpc_aal) + add_dependencies(nr_ulschsim ldpc_aal) + add_dependencies(nr_dlsim ldpc_aal) + add_dependencies(nr_dlschsim ldpc_aal) + +endif() diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/README.md b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/README.md similarity index 99% rename from openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/README.md rename to openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/README.md index ee55f7ff21..140fff5f1e 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/README.md +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/README.md @@ -1,4 +1,4 @@ -This document highlights some of the key aspects of the implementation for O-RAN AAL/DPDK BBDEV; for user documentation, please consult the [setup guide](../../../../../doc/LDPC_T2_OFFLOAD_SETUP.md) instead. +This document highlights some of the key aspects of the implementation for O-RAN AAL/DPDK BBDEV; for user documentation, please consult the [setup guide](../../../../../doc/LDPC_OFFLOAD_SETUP.md) instead. ## Overview diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c similarity index 99% rename from openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c rename to openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c index bd1aad254d..661fc0ce63 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c @@ -2,13 +2,8 @@ * Copyright(c) 2017 Intel Corporation */ -/*! \file PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.c - * \note: based on testbbdev test_bbdev_perf.c functions. Harq buffer offset added. - * \mbuf and mempool allocated at the init step, LDPC parameters updated from OAI. - */ - -#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h" -#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h" +#include "../nrLDPC_coding_interface.h" +#include "nrLDPC_coding_aal.h" #include "PHY/sse_intrin.h" #include #define NR_LDPC_ENABLE_PARITY_CHECK diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.h similarity index 100% rename from openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/nrLDPC_coding_t2.h rename to openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.h diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt deleted file mode 100644 index b6a0cacf39..0000000000 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_t2/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -########################################################## -# LDPC offload library - DPDK BBDEV -########################################################## - -add_boolean_option(ENABLE_LDPC_T2 OFF "Build support for LDPC Offload to T2 library" OFF) - -if (ENABLE_LDPC_T2) - pkg_check_modules(LIBDPDK_T2 REQUIRED libdpdk>=20.11.9) - - if(LIBDPDK_T2_VERSION VERSION_LESS "21.11") - add_compile_definitions(DPDK_VER_PRE_21_11) - endif() - - find_library(PMD_T2 NAMES rte_baseband_accl_ldpc HINTS ${LIBDPDK_T2_LIBRARY_DIRS}) - if (PMD_T2) - add_compile_definitions(LDPC_T2) - message(STATUS "found poll-mode driver for AccelerComm T2 LDPC Offload (rte_baseband_accl_ldpc.so)") - else() - if (LIBDPDK_T2_VERSION VERSION_LESS "22.11") - message(FATAL_ERROR "for non-T2 BBDEV PMDs, we support the Intel ACC100 and ACC200 (VRB1), using DPDK 22.11 or later.") - endif() - endif() - - add_library(ldpc_t2 MODULE nrLDPC_coding_t2.c) - - set_target_properties(ldpc_t2 PROPERTIES COMPILE_FLAGS "-DALLOW_EXPERIMENTAL_API") - set_target_properties(ldpc_t2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - target_include_directories(ldpc_t2 PRIVATE ${LIBDPDK_T2_INCLUDE_DIRS}) - target_link_libraries(ldpc_t2 PRIVATE ldpc_gen_HEADERS ${LIBDPDK_T2_LDFLAGS}) - if (PMD_T2) - target_link_libraries(ldpc_t2 PRIVATE ${PMD_T2}) - endif() - - add_dependencies(nr-softmodem ldpc_t2) - add_dependencies(nr-uesoftmodem ldpc_t2) - add_dependencies(nr_ulsim ldpc_t2) - add_dependencies(nr_ulschsim ldpc_t2) - add_dependencies(nr_dlsim ldpc_t2) - add_dependencies(nr_dlschsim ldpc_t2) - -endif() From 0d4246e3549f9b43b1811a2b62292d8b07f574eb Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 24 Jul 2025 13:53:02 +0200 Subject: [PATCH 49/86] Rename nrLDPC_coding_t2 options to nrLDPC_coding_aal --- .../xml_files/t2_offload_dec_nr_ulsim.xml | 18 ++++++------ .../xml_files/t2_offload_enc_nr_dlsim.xml | 24 ++++++++-------- doc/LDPC_OFFLOAD_SETUP.md | 28 +++++++++++-------- openair1/PHY/CODING/DOC/LDPCImplementation.md | 2 +- .../nrLDPC_coding_aal/nrLDPC_coding_aal.c | 8 ++++-- radio/fhi_72/oran-config.c | 4 +-- 6 files changed, 45 insertions(+), 39 deletions(-) diff --git a/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml b/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml index c721441907..5cfc1eeb72 100644 --- a/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml +++ b/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml @@ -62,7 +62,7 @@ true nr_ulsim 100 - -n1000 -s30 -S30.2 -m5 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -80,7 +80,7 @@ true nr_ulsim 150 - -n1000 -s30 -S30.2 -m15 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -98,7 +98,7 @@ true nr_ulsim 250 - -n1000 -s30 -S30.2 -m25 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -116,7 +116,7 @@ true nr_ulsim 150 - -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -134,7 +134,7 @@ true nr_ulsim 350 - -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -152,7 +152,7 @@ true nr_ulsim 550 - -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -170,7 +170,7 @@ true nr_ulsim 250 - -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -188,7 +188,7 @@ true nr_ulsim 650 - -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -206,7 +206,7 @@ true nr_ulsim 1100 - -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 diff --git a/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml b/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml index 14f488dc6c..57b6afe21a 100644 --- a/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml +++ b/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml @@ -63,7 +63,7 @@ true nr_dlsim 100 - -n1000 -s30 -S30.2 -e5 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -81,7 +81,7 @@ true nr_dlsim 100 - -n1000 -s30 -S30.2 -e15 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -99,7 +99,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e25 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -117,7 +117,7 @@ true nr_dlsim 150 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -135,7 +135,7 @@ true nr_dlsim 250 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -153,7 +153,7 @@ true nr_dlsim 400 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -171,7 +171,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -189,7 +189,7 @@ true nr_dlsim 500 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -207,7 +207,7 @@ true nr_dlsim 500 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -225,7 +225,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -243,7 +243,7 @@ true nr_dlsim 300 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 @@ -261,6 +261,6 @@ true nr_dlsim 450 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev af:00.0 --nrLDPC_coding_t2.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 diff --git a/doc/LDPC_OFFLOAD_SETUP.md b/doc/LDPC_OFFLOAD_SETUP.md index bada5c36df..265a94ff71 100644 --- a/doc/LDPC_OFFLOAD_SETUP.md +++ b/doc/LDPC_OFFLOAD_SETUP.md @@ -221,23 +221,27 @@ The selection of the library to compile is done using `--build-lib ldpc_aal`. # O-RAN AAL DPDK EAL parameters To configure O-RAN AAL/DPDK BBDEV, you can set the following parameters via the command line of PHY simulators or nr-softmodem: -- `nrLDPC_coding_t2.dpdk_dev` - **mandatory** parameter, specifies the PCI address of our accelerator. It must follow the format `WWWW:XX:YY.Z`. +> Note: the group parameter name has been renamed from `nrLDPC_coding_t2` to +> `nrLDPC_coding_aal` to better reflect that it is a generic AAL accelerator +> card. -- `nrLDPC_coding_t2.dpdk_core_list` - **mandatory** parameter, specifies the CPU cores assigned to DPDK . -Ensure that the CPU cores specified in `nrLDPC_coding_t2.dpdk_core_list` are available and not used by other processes to avoid conflicts. +- `nrLDPC_coding_aal.dpdk_dev` - **mandatory** parameter, specifies the PCI address of our accelerator. It must follow the format `WWWW:XX:YY.Z`. -- `nrLDPC_coding_t2.dpdk_prefix` - optional parameter, DPDK shared data file prefix, by default set to *b6*. +- `nrLDPC_coding_aal.dpdk_core_list` - **mandatory** parameter, specifies the CPU cores assigned to DPDK . +Ensure that the CPU cores specified in `nrLDPC_coding_aal.dpdk_core_list` are available and not used by other processes to avoid conflicts. -- `nrLDPC_coding_t2.vfio_vf_token` - optional parameter, VFIO token set for the VF, if applicable. +- `nrLDPC_coding_aal.dpdk_prefix` - optional parameter, DPDK shared data file prefix, by default set to *b6*. -- `nrLDPC_coding_t2.num_harq_codeblock` - optional parameter, size of the HARQ buffer in terms of the number of 32kB blocks, by default set to *512* (maximum for the T2; as for the ACCs, this can be further increased). +- `nrLDPC_coding_aal.vfio_vf_token` - optional parameter, VFIO token set for the VF, if applicable. -- `nrLDPC_coding_t2.eal_init_bbdev` - optional parameter, set this to 1 when using with FHI72 with the Intel ACCs. For the T2, this is not required. +- `nrLDPC_coding_aal.num_harq_codeblock` - optional parameter, size of the HARQ buffer in terms of the number of 32kB blocks, by default set to *512* (maximum for the T2; as for the ACCs, this can be further increased). + +- `nrLDPC_coding_aal.eal_init_bbdev` - optional parameter, set this to 1 when using with FHI72 with the Intel ACCs. For the T2, this is not required. **Note:** These parameters can also be provided in a configuration file. Example for the ACC200: ``` -nrLDPC_coding_t2 : { +nrLDPC_coding_aal : { dpdk_dev : "0000:f7:00.1"; dpdk_core_list : "14-15"; vfio_vf_token: "00112233-4455-6677-8899-aabbccddeeff"; @@ -263,7 +267,7 @@ Example command: cd ~/openairinterface5g source oaienv cd cmake_targets/ran_build/build -sudo ./nr_ulsim -n100 -s20 -m20 -r273 -R273 --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 0-1 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff +sudo ./nr_ulsim -n100 -s20 -m20 -r273 -R273 --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_aal.dpdk_core_list 0-1 --nrLDPC_coding_aal.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff ``` ### nr_dlsim @@ -272,7 +276,7 @@ Example command: cd ~/openairinterface5g source oaienv cd cmake_targets/ran_build/build -sudo ./nr_dlsim -n300 -s30 -R 106 -e 27 --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 0-1 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff +sudo ./nr_dlsim -n300 -s30 -R 106 -e 27 --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_aal.dpdk_core_list 0-1 --nrLDPC_coding_aal.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff ``` ## OTA test @@ -284,14 +288,14 @@ Example command: cd ~/openairinterface5g source oaienv cd cmake_targets/ran_build/build -sudo ./nr-softmodem -O ~/gnb.conf --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_t2.dpdk_core_list 14-15 --nrLDPC_coding_t2.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff --nrLDPC_coding_t2.eal_init_bbdev 1 +sudo ./nr-softmodem -O ~/gnb.conf --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_aal.dpdk_core_list 14-15 --nrLDPC_coding_aal.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff --nrLDPC_coding_aal.eal_init_bbdev 1 ``` # Known Issue(s) ## BBDEV CPU Usage -When running the E2E setup (this applies to both USRP and FHI72), BBDEV may not be using the list of CPU cores as specified by `nrLDPC_coding_t2.dpdk_core_list` accordingly. +When running the E2E setup (this applies to both USRP and FHI72), BBDEV may not be using the list of CPU cores as specified by `nrLDPC_coding_aal.dpdk_core_list` accordingly. This is an issue under investigation, and subject for future fixes. In the meantime, we recommend allocating idle, and isolated CPU cores in the configuration for BBDEV. diff --git a/openair1/PHY/CODING/DOC/LDPCImplementation.md b/openair1/PHY/CODING/DOC/LDPCImplementation.md index 956dad97bf..d537a8a299 100644 --- a/openair1/PHY/CODING/DOC/LDPCImplementation.md +++ b/openair1/PHY/CODING/DOC/LDPCImplementation.md @@ -42,7 +42,7 @@ Linking C shared module libldpc_aal.so At runtime, to successfully use LDPC accelerators (e.g., Xilinx T2/Intel ACCs), you will need to install the corresponding drivers and tools. Please refer to the dedicated documentation at [LDPC_OFFLOAD_SETUP.md](file://../../../../doc/LDPC_OFFLOAD_SETUP.md). -`./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _aal --nrLDPC_coding_t2.dpdk_dev 01:00.0 --nrLDPC_coding_t2.dpdk_core_list 0-1` +`./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 01:00.0 --nrLDPC_coding_aal.dpdk_core_list 0-1` ``` diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c index 661fc0ce63..71352ea887 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c @@ -1070,9 +1070,11 @@ int32_t nrLDPC_coding_init() {"vfio_vf_token", NULL, 0, .strptr = &vfio_vf_token, .defstrval = NULL, TYPE_STRING, 0, NULL}, {"num_harq_codeblock", NULL, 0, .uptr = &num_harq_codeblock, .defintval = 512, TYPE_UINT32, 0, NULL}, }; - config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_t2"); - AssertFatal(dpdk_dev != NULL, "nrLDPC_coding_t2.dpdk_dev was not provided"); - AssertFatal(dpdk_core_list != NULL, "nrLDPC_coding_t2.dpdk_core_list was not provided"); + config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_aal"); + if (dpdk_dev == NULL) + LOG_E(NR_PHY, "could not find mandatory --nrLDPC_coding_aal.dpdk_dev. If you used --nrLDPC_coding_t2.*, please rename all options to --nrLDPC_coding_aal.*\n"); + AssertFatal(dpdk_dev != NULL, "nrLDPC_coding_aal.dpdk_dev was not provided"); + AssertFatal(dpdk_core_list != NULL, "nrLDPC_coding_aal.dpdk_core_list was not provided"); char dpdk_dev_full[32]; if (normalize_dpdk_dev(dpdk_dev, dpdk_dev_full, sizeof(dpdk_dev_full)) != 0) { diff --git a/radio/fhi_72/oran-config.c b/radio/fhi_72/oran-config.c index ed8f37f857..58b8ea7490 100644 --- a/radio/fhi_72/oran-config.c +++ b/radio/fhi_72/oran-config.c @@ -494,10 +494,10 @@ static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, in {"dpdk_dev", NULL, 0, .strptr = &dpdk_dev, .defstrval = NULL, TYPE_STRING, 0, NULL}, {"vfio_vf_token", NULL, 0, .strptr = &vfio_vf_token, .defstrval = NULL, TYPE_STRING, 0, NULL} }; - config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_t2"); + config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_aal"); if (eal_init_bbdev != 0) { - AssertFatal(dpdk_dev!=NULL, "nrLDPC_coding_t2.dpdk_dev was not provided"); + AssertFatal(dpdk_dev!=NULL, "nrLDPC_coding_aal.dpdk_dev was not provided"); snprintf(&bbdev_dev[0], sizeof(bbdev_dev), "%s", dpdk_dev); io_cfg->bbdev_dev[0] = &bbdev_dev[0]; // BBDev dev name; max devices = 1 if(vfio_vf_token != NULL) { From f6aeb21d979d75425d83ae82b3a290043c630e79 Mon Sep 17 00:00:00 2001 From: Xin Zhe Khooi Date: Fri, 25 Jul 2025 20:29:42 +0800 Subject: [PATCH 50/86] ldpc_aal: handle T2 case in runtime --- .../xml_files/t2_offload_dec_nr_ulsim.xml | 18 ++-- .../xml_files/t2_offload_enc_nr_dlsim.xml | 24 ++--- doc/LDPC_OFFLOAD_SETUP.md | 5 +- .../nrLDPC_coding_aal/CMakeLists.txt | 1 - .../nrLDPC_coding_aal/nrLDPC_coding_aal.c | 88 +++++++++---------- radio/fhi_72/oran-config.c | 8 +- 6 files changed, 70 insertions(+), 74 deletions(-) diff --git a/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml b/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml index 5cfc1eeb72..f82c54e8de 100644 --- a/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml +++ b/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml @@ -62,7 +62,7 @@ true nr_ulsim 100 - -n1000 -s30 -S30.2 -m5 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -80,7 +80,7 @@ true nr_ulsim 150 - -n1000 -s30 -S30.2 -m15 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -98,7 +98,7 @@ true nr_ulsim 250 - -n1000 -s30 -S30.2 -m25 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r106 -R106 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -116,7 +116,7 @@ true nr_ulsim 150 - -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -134,7 +134,7 @@ true nr_ulsim 350 - -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -152,7 +152,7 @@ true nr_ulsim 550 - -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -170,7 +170,7 @@ true nr_ulsim 250 - -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -188,7 +188,7 @@ true nr_ulsim 650 - -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -206,7 +206,7 @@ true nr_ulsim 1100 - -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 diff --git a/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml b/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml index 57b6afe21a..9b7a84c51f 100644 --- a/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml +++ b/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml @@ -63,7 +63,7 @@ true nr_dlsim 100 - -n1000 -s30 -S30.2 -e5 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -81,7 +81,7 @@ true nr_dlsim 100 - -n1000 -s30 -S30.2 -e15 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -99,7 +99,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e25 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b106 -R106 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -117,7 +117,7 @@ true nr_dlsim 150 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -135,7 +135,7 @@ true nr_dlsim 250 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -153,7 +153,7 @@ true nr_dlsim 400 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -171,7 +171,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -189,7 +189,7 @@ true nr_dlsim 500 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -207,7 +207,7 @@ true nr_dlsim 500 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z2 -y2 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -225,7 +225,7 @@ true nr_dlsim 200 - -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e5 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -243,7 +243,7 @@ true nr_dlsim 300 - -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e15 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 @@ -261,6 +261,6 @@ true nr_dlsim 450 - -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 + -n1000 -s30 -S30.2 -e25 -b273 -R273 -X4,5,6,7,8,9 -x2 -z4 -y4 -P --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev af:00.0 --nrLDPC_coding_aal.dpdk_core_list 11-12 --nrLDPC_coding_aal.is_t2 1 diff --git a/doc/LDPC_OFFLOAD_SETUP.md b/doc/LDPC_OFFLOAD_SETUP.md index 265a94ff71..cabac2cf39 100644 --- a/doc/LDPC_OFFLOAD_SETUP.md +++ b/doc/LDPC_OFFLOAD_SETUP.md @@ -236,7 +236,7 @@ Ensure that the CPU cores specified in `nrLDPC_coding_aal.dpdk_core_list` are av - `nrLDPC_coding_aal.num_harq_codeblock` - optional parameter, size of the HARQ buffer in terms of the number of 32kB blocks, by default set to *512* (maximum for the T2; as for the ACCs, this can be further increased). -- `nrLDPC_coding_aal.eal_init_bbdev` - optional parameter, set this to 1 when using with FHI72 with the Intel ACCs. For the T2, this is not required. +- `nrLDPC_coding_aal.is_t2` - optional parameter, set this to 1 when using the Xilinx T2 card. **Note:** These parameters can also be provided in a configuration file. Example for the ACC200: @@ -257,6 +257,7 @@ loader : { # Running OAI with O-RAN AAL In general, to offload of the channel coding to the LDPC accelerator, we use use the `--loader.ldpc.shlibversion _aal` option. +Reminder, if you are using the Xilinx T2 card, make sure to set `--nrLDPC_coding_aal.is_t2 1`. ## 5G PHY simulators @@ -288,7 +289,7 @@ Example command: cd ~/openairinterface5g source oaienv cd cmake_targets/ran_build/build -sudo ./nr-softmodem -O ~/gnb.conf --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_aal.dpdk_core_list 14-15 --nrLDPC_coding_aal.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff --nrLDPC_coding_aal.eal_init_bbdev 1 +sudo ./nr-softmodem -O ~/gnb.conf --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 0000:f7:00.1 --nrLDPC_coding_aal.dpdk_core_list 14-15 --nrLDPC_coding_aal.vfio_vf_token 00112233-4455-6677-8899-aabbccddeeff ``` # Known Issue(s) diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/CMakeLists.txt b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/CMakeLists.txt index eeff46cd14..1bad804fba 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/CMakeLists.txt +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/CMakeLists.txt @@ -9,7 +9,6 @@ if (ENABLE_LDPC_AAL) find_library(PMD_T2 NAMES rte_baseband_accl_ldpc HINTS ${LIBDPDK_AAL_LIBRARY_DIRS}) if (PMD_T2) - add_compile_definitions(LDPC_T2) message(STATUS "found poll-mode driver for AccelerComm T2 LDPC Offload (rte_baseband_accl_ldpc.so)") else() if (LIBDPDK_AAL_VERSION VERSION_LESS "22.11") diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c index 71352ea887..62a20ef822 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c @@ -52,6 +52,7 @@ struct active_device { const char *driver_name; uint8_t dev_id; struct rte_bbdev_info info; + bool is_t2; uint32_t num_harq_codeblock; /* Persistent data structure to keep track of HARQ-related information */ // Note: This is used to store/keep track of the combined output information across iterations @@ -352,7 +353,7 @@ bool check_internal_harq_memory_capabilities(struct rte_bbdev_info *info) } // based on DPDK BBDEV add_bbdev_dev -static int add_dev(uint8_t dev_id, uint32_t num_harq_codeblock) +static int add_dev(uint8_t dev_id, bool is_t2, uint32_t num_harq_codeblock) { int ret; unsigned int nb_queues; @@ -380,6 +381,9 @@ static int add_dev(uint8_t dev_id, uint32_t num_harq_codeblock) active_dev.num_harq_codeblock = num_harq_codeblock; active_dev.harq_buffers = malloc(sizeof(struct rte_bbdev_op_data) * active_dev.num_harq_codeblock); + // is device T2? + active_dev.is_t2 = is_t2; + // device setup ret = rte_bbdev_setup_queues(dev_id, nb_queues, active_dev.info.socket_id); AssertFatal(ret == 0, "rte_bbdev_setup_queues(%u, %u, %d) ret %i\n", dev_id, nb_queues, active_dev.info.socket_id, ret); @@ -588,13 +592,9 @@ static void set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters) { int j = 0; -#ifdef LDPC_T2 // The T2 only supports CB mode, and does not TB mode special case handling. - bool special_case_tb_mode = false; -#else - bool special_case_tb_mode = - (nrLDPC_slot_decoding_parameters->nb_TBs == 1) && (nb_segments_decoding(nrLDPC_slot_decoding_parameters) == 1); -#endif + bool special_case_tb_mode = !active_dev.is_t2 && (nrLDPC_slot_decoding_parameters->nb_TBs == 1) + && (nb_segments_decoding(nrLDPC_slot_decoding_parameters) == 1); for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { for (int i = 0; i < nrLDPC_slot_decoding_parameters->TBs[h].C; ++i) { ops[j]->ldpc_dec.basegraph = nrLDPC_slot_decoding_parameters->TBs[h].BG; @@ -608,6 +608,8 @@ static void set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, ops[j]->ldpc_dec.op_flags = RTE_BBDEV_LDPC_ITERATION_STOP_ENABLE | RTE_BBDEV_LDPC_HQ_COMBINE_OUT_ENABLE; if (*nrLDPC_slot_decoding_parameters->TBs[h].segments[i].d_to_be_cleared) { *nrLDPC_slot_decoding_parameters->TBs[h].segments[i].d_to_be_cleared = false; + if (active_dev.is_t2) + *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0; } else { ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_HQ_COMBINE_IN_ENABLE; if (active_dev.support_internal_harq_memory) { @@ -615,15 +617,10 @@ static void set_ldpc_dec_op(struct rte_bbdev_dec_op **ops, ops[j]->ldpc_dec.op_flags |= RTE_BBDEV_LDPC_INTERNAL_HARQ_MEMORY_OUT_ENABLE; } } - -#ifdef LDPC_T2 - // Note: For the T2, we do not reset the processedSegments in case of HARQ. - // This is because if T2 is asked to decode a segment that as already successfully - // decoded in the previous round, the T2 reports a failure. - if (*nrLDPC_slot_decoding_parameters->TBs[h].segments[i].d_to_be_cleared) *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0; -#else - *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0; -#endif + + if (!active_dev.is_t2) + *nrLDPC_slot_decoding_parameters->TBs[h].processedSegments = 0; + if (!special_case_tb_mode) { ops[j]->ldpc_dec.code_block_mode = 1; ops[j]->ldpc_dec.cb_params.e = nrLDPC_slot_decoding_parameters->TBs[h].segments[i].E; @@ -680,13 +677,9 @@ static void set_ldpc_enc_op(struct rte_bbdev_enc_op **ops, nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters) { int j = 0; -#ifdef LDPC_T2 // The T2 only supports CB mode, and does not TB mode special case handling. - bool special_case_tb_mode = false; -#else - bool special_case_tb_mode = - (nrLDPC_slot_encoding_parameters->nb_TBs == 1) && (nb_segments_encoding(nrLDPC_slot_encoding_parameters) == 1); -#endif + bool special_case_tb_mode = !active_dev.is_t2 && (nrLDPC_slot_encoding_parameters->nb_TBs == 1) + && (nb_segments_encoding(nrLDPC_slot_encoding_parameters) == 1); for (int h = 0; h < nrLDPC_slot_encoding_parameters->nb_TBs; ++h) { for (int i = 0; i < nrLDPC_slot_encoding_parameters->TBs[h].C; ++i) { ops[j]->ldpc_enc.basegraph = nrLDPC_slot_encoding_parameters->TBs[h].BG; @@ -1063,16 +1056,20 @@ int32_t nrLDPC_coding_init() char *dpdk_file_prefix = NULL; char *vfio_vf_token = NULL; // vfio token for the bbdev card uint32_t num_harq_codeblock = 0; // size of the HARQ buffer in terms of the number of 32K blocks + uint32_t is_t2 = 0; paramdef_t LoaderParams[] = { {"dpdk_dev", NULL, 0, .strptr = &dpdk_dev, .defstrval = NULL, TYPE_STRING, 0, NULL}, {"dpdk_core_list", NULL, 0, .strptr = &dpdk_core_list, .defstrval = NULL, TYPE_STRING, 0, NULL}, {"dpdk_file_prefix", NULL, 0, .strptr = &dpdk_file_prefix, .defstrval = "b6", TYPE_STRING, 0, NULL}, {"vfio_vf_token", NULL, 0, .strptr = &vfio_vf_token, .defstrval = NULL, TYPE_STRING, 0, NULL}, {"num_harq_codeblock", NULL, 0, .uptr = &num_harq_codeblock, .defintval = 512, TYPE_UINT32, 0, NULL}, + {"is_t2", NULL, 0, .uptr = &is_t2, .defintval = 0, TYPE_UINT8, 0, NULL}, }; config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_aal"); if (dpdk_dev == NULL) - LOG_E(NR_PHY, "could not find mandatory --nrLDPC_coding_aal.dpdk_dev. If you used --nrLDPC_coding_t2.*, please rename all options to --nrLDPC_coding_aal.*\n"); + LOG_E(NR_PHY, + "could not find mandatory --nrLDPC_coding_aal.dpdk_dev. If you used --nrLDPC_coding_t2.*, please rename all options to " + "--nrLDPC_coding_aal.*\n"); AssertFatal(dpdk_dev != NULL, "nrLDPC_coding_aal.dpdk_dev was not provided"); AssertFatal(dpdk_core_list != NULL, "nrLDPC_coding_aal.dpdk_core_list was not provided"); @@ -1114,7 +1111,7 @@ int32_t nrLDPC_coding_init() } AssertFatal(dev_id != -1, "bbdev %s not found.", dpdk_dev_full); - AssertFatal(add_dev(dev_id, num_harq_codeblock) == 0, "Failed to setup bbdev"); + AssertFatal(add_dev(dev_id, is_t2, num_harq_codeblock) == 0, "Failed to setup bbdev"); AssertFatal(rte_bbdev_stats_reset(dev_id) == 0, "Failed to reset stats of bbdev %u", dev_id); AssertFatal(rte_bbdev_start(dev_id) == 0, "Failed to start bbdev %u", dev_id); @@ -1156,7 +1153,8 @@ int32_t nrLDPC_coding_shutdown() return 0; } -static void llr_scaling(int16_t *llr, int llr_len, uint8_t *llr_scaled, int8_t llr_size, int8_t llr_decimal, int8_t nb_layers, int8_t Qm) +static void +llr_scaling(int16_t *llr, int llr_len, uint8_t *llr_scaled, int8_t llr_size, int8_t llr_decimal, int8_t nb_layers, int8_t Qm) { const int16_t llr_max = (1 << (llr_size - 1)) - 1; const int16_t llr_min = -llr_max; @@ -1258,33 +1256,31 @@ int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_dec struct rte_bbdev_op_data **queue_ops[DATA_NUM_TYPES] = {&data_buffers.inputs, &data_buffers.hard_outputs, &data_buffers.harq_outputs}; -#ifndef LDPC_T2 int8_t llr_size = (active_dev.info.drv.capabilities)[RTE_BBDEV_OP_LDPC_DEC].cap.ldpc_dec.llr_size; int8_t llr_decimal = (active_dev.info.drv.capabilities)[RTE_BBDEV_OP_LDPC_DEC].cap.ldpc_dec.llr_decimals; -#endif int offset = 0; for (int h = 0; h < nrLDPC_slot_decoding_parameters->nb_TBs; ++h) { for (int r = 0; r < nrLDPC_slot_decoding_parameters->TBs[h].C; r++) { -#ifdef LDPC_T2 - // For the T2, we simply saturate the LLRs. - uint16_t z_ol[LDPC_MAX_CB_SIZE] __attribute__((aligned(16))); - memcpy(z_ol, - nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr, - nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E * sizeof(uint16_t)); - simde__m128i *pv_ol128 = (simde__m128i *)z_ol; - simde__m128i *pl_ol128 = (simde__m128i *)&l_ol[offset]; - for (int i = 0, j = 0; j < ((nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E + 15) >> 4); i += 2, j++) { - pl_ol128[j] = simde_mm_packs_epi16(pv_ol128[i], pv_ol128[i + 1]); + if (active_dev.is_t2) { + // For the T2, we simply saturate the LLRs. + uint16_t z_ol[LDPC_MAX_CB_SIZE] __attribute__((aligned(16))); + memcpy(z_ol, + nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr, + nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E * sizeof(uint16_t)); + simde__m128i *pv_ol128 = (simde__m128i *)z_ol; + simde__m128i *pl_ol128 = (simde__m128i *)&l_ol[offset]; + for (int i = 0, j = 0; j < ((nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E + 15) >> 4); i += 2, j++) { + pl_ol128[j] = simde_mm_packs_epi16(pv_ol128[i], pv_ol128[i + 1]); + } + } else { + llr_scaling(nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr, + nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E, + &l_ol[offset], + llr_size, + llr_decimal, + nrLDPC_slot_decoding_parameters->TBs[h].nb_layers, + nrLDPC_slot_decoding_parameters->TBs[h].Qm); } -#else - llr_scaling(nrLDPC_slot_decoding_parameters->TBs[h].segments[r].llr, - nrLDPC_slot_decoding_parameters->TBs[h].segments[r].E, - &l_ol[offset], - llr_size, - llr_decimal, - nrLDPC_slot_decoding_parameters->TBs[h].nb_layers, - nrLDPC_slot_decoding_parameters->TBs[h].Qm); -#endif offset += LDPC_MAX_CB_SIZE; } } diff --git a/radio/fhi_72/oran-config.c b/radio/fhi_72/oran-config.c index 58b8ea7490..4eff24aacf 100644 --- a/radio/fhi_72/oran-config.c +++ b/radio/fhi_72/oran-config.c @@ -485,18 +485,18 @@ static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, in char *shlibversion = NULL; // version of the LDPC coding library paramdef_t LoaderParams_shlibversion[] = {{"shlibversion", NULL, 0, .strptr = &shlibversion, .defstrval = NULL, TYPE_STRING, 0, NULL}}; config_get(config_get_if(), LoaderParams_shlibversion, sizeofArray(LoaderParams_shlibversion), "loader.ldpc"); - if (shlibversion != NULL && strncmp(shlibversion, "_t2", 3) == 0) { - uint32_t eal_init_bbdev = 0; // If not 0 then include the BBDEV device in the EAL init for FHI + if (shlibversion != NULL && strncmp(shlibversion, "_aal", 4) == 0) { + uint32_t is_t2 = 0; // If not 0 then include the BBDEV device in the EAL init for FHI char *dpdk_dev = NULL; // PCI address of the card char *vfio_vf_token = NULL; // vfio token for the bbdev card paramdef_t LoaderParams[] = { - {"eal_init_bbdev", NULL, 0, .uptr = &eal_init_bbdev, .defuintval = 0, TYPE_UINT, 0, NULL}, + {"is_t2", NULL, 0, .uptr = &is_t2, .defuintval = 0, TYPE_UINT, 0, NULL}, {"dpdk_dev", NULL, 0, .strptr = &dpdk_dev, .defstrval = NULL, TYPE_STRING, 0, NULL}, {"vfio_vf_token", NULL, 0, .strptr = &vfio_vf_token, .defstrval = NULL, TYPE_STRING, 0, NULL} }; config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_aal"); - if (eal_init_bbdev != 0) { + if (!is_t2) { AssertFatal(dpdk_dev!=NULL, "nrLDPC_coding_aal.dpdk_dev was not provided"); snprintf(&bbdev_dev[0], sizeof(bbdev_dev), "%s", dpdk_dev); io_cfg->bbdev_dev[0] = &bbdev_dev[0]; // BBDev dev name; max devices = 1 From eaffaf4058f6d1fdfed9551ed447c76349203fd9 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 6 Aug 2025 11:33:59 +0200 Subject: [PATCH 51/86] time manager tests: increase some sleep The CI fails sometimes, because some processes/threads are created in unpredictable order and if the client connects before the server is ready some tests fail, as seen in the following log: [UTIL] client/server iq-samples: launch sub-processes [UTIL] threadCreate() for time client: creating thread (no affinity, default priority) [UTIL] time manager configuration: [time source: server] [mode: client] [server IP: 127.0.0.1} [server port: 7374] [UTIL] time client: connection to 127.0.0.1:7374 failed, try again in 1s [UTIL] threadCreate() for time source iq samples: creating thread (no affinity, default priority) [UTIL] threadCreate() for time server: creating thread (no affinity, default priority) [UTIL] time manager configuration: [time source: iq_samples] [mode: server] [server IP: 127.0.0.1} [server port: 7374] [UTIL] client/server: rlc ticks: 1000 pdcp ticks: 1000 x2ap ticks: 1000 CMDLINE: "/oai-ran/build/common/utils/time_manager/tests/test_time_manager_auto" "--time_management.mode" "server" "--time_management.time_source" "iq_samples" [UTIL] client/server: rlc ticks: 0 pdcp ticks: 0 x2ap ticks: 0 CMDLINE: "/oai-ran/build/common/utils/time_manager/tests/test_time_manager_auto" "--time_management.mode" "client" [UTIL] client-server iq-samples: ERROR The client/server iq-samples test is started. Then we see this log: [UTIL] time manager configuration: [time source: server] [mode: client] [server IP: 127.0.0.1} [server port: 7374] Which means that the client is running. It tries to connect to the server (not running yet), but fails, so sleeps for 1 second before attempting another connection to the server: [UTIL] time client: connection to 127.0.0.1:7374 failed, try again in 1s The sleep is done by the time_client thread, the main thread of the test program continues and does a sleep(1). Then the server arrives: [UTIL] time manager configuration: [time source: iq_samples] [mode: server] [server IP: 127.0.0.1} [server port: 7374] It sends all the ticks (to no client, since the client is still sleeping). Then the test is over, the sleep(1) of the client finishes and the program exits. The client never connected to the server. We have this log: [UTIL] client/server: rlc ticks: 0 pdcp ticks: 0 x2ap ticks: 0 And the test fails. It's complicated to write an automatic test involving several uncoordinated programs. The operating system may create processes in any order. We use some sleeping to wait, but it may be too short or too long, leading to test failure. Let's see with more sleep if the CI fails less. --- common/utils/time_manager/tests/test_auto.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/utils/time_manager/tests/test_auto.c b/common/utils/time_manager/tests/test_auto.c index 817a518c4a..0db7881bb4 100644 --- a/common/utils/time_manager/tests/test_auto.c +++ b/common/utils/time_manager/tests/test_auto.c @@ -143,7 +143,7 @@ static int client_server(char *program_name, bool iq_samples_time_source) /* wait a little bit so that server is ready to accept clients * (this is hackish) */ - usleep(100); + usleep(1000); /* start client */ client = fork(); @@ -188,7 +188,7 @@ static int run_sub_client_server(bool iq_samples_time_source) * wait a bit for client to be there otherwise server * may finish too early */ - usleep(100); + usleep(100*1000); /* let's pretend we have 2000 samples per second */ for (int i = 0; i < 1000; i++) { From a1c354bd6eb203aaa81b342e830a3aaaaa0fc381 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 31 Jul 2025 18:30:23 +0200 Subject: [PATCH 52/86] CI: Remove old tracing information It will be replaced with a new mechanism in a later commit. --- ci-scripts/ci_infra.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ci-scripts/ci_infra.yaml b/ci-scripts/ci_infra.yaml index 804852c937..fd2d414a03 100644 --- a/ci-scripts/ci_infra.yaml +++ b/ci-scripts/ci_infra.yaml @@ -7,8 +7,6 @@ idefix: NetworkScript: ip a show dev wwan0 IF: wwan0 MTU: 1500 - Trace: True - LogStore: /media/usb-drive/ci_qlogs up2: Host: up2 @@ -46,7 +44,6 @@ adb_ue_1: NetworkScript: adb -s R3CM40LZPHT shell "ip address show | grep rmnet_data0" CmdPrefix: adb -s R3CM40LZPHT shell MTU: 1500 - LogStore: /media/usb-drive/ci_adb_1-logs adb_ue_2: Host: nano InitScript: /home/oaicicd/ci_ctl_adb.sh initialize 5200c00db4413517 @@ -59,7 +56,6 @@ adb_ue_2: NetworkScript: adb -s 5200c00db4413517 shell "ip address show | grep rmnet" CmdPrefix: adb -s 5200c00db4413517 shell MTU: 1500 - LogStore: /media/usb-drive/ci_adb_2-logs oc-cn5g: Host: avra From cc1b2315af6f354afb4bde5d057a5ab282a241ec Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 31 Jul 2025 16:32:23 +0200 Subject: [PATCH 53/86] CI: add simple unit test for UE handling --- .../tests/config/test_module_infra.yaml | 19 +++++++++ ci-scripts/tests/module.py | 41 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 ci-scripts/tests/config/test_module_infra.yaml create mode 100644 ci-scripts/tests/module.py diff --git a/ci-scripts/tests/config/test_module_infra.yaml b/ci-scripts/tests/config/test_module_infra.yaml new file mode 100644 index 0000000000..fadc721232 --- /dev/null +++ b/ci-scripts/tests/config/test_module_infra.yaml @@ -0,0 +1,19 @@ +test: + Host: localhost + InitScript: "" + TermScript: "" + AttachScript: "true" + DetachScript: "true" + NetworkScript: ip a show dev lo + IF: lo + MTU: 65536 + +test-fail: + Host: localhost + InitScript: "" + TermScript: "" + AttachScript: "false" + DetachScript: "true" + NetworkScript: ip a show dev TESTDEVICEDOESNOTEXIST + IF: TESTDEVICEDOESNOTEXIST + MTU: 123 diff --git a/ci-scripts/tests/module.py b/ci-scripts/tests/module.py new file mode 100644 index 0000000000..b6898405d4 --- /dev/null +++ b/ci-scripts/tests/module.py @@ -0,0 +1,41 @@ +import sys +import logging +logging.basicConfig( + level=logging.DEBUG, + stream=sys.stdout, + format="[%(asctime)s] %(levelname)8s: %(message)s" +) +import os + +import unittest + +sys.path.append('./') # to find OAI imports below +import cls_module +import cls_cmd + +class TestModule(unittest.TestCase): + def test_simple_module(self): + c = cls_module.Module_UE("test", filename="tests/config/test_module_infra.yaml") + success = c.initialize() + self.assertTrue(success) + ip = c.attach() + self.assertEqual(ip, "127.0.0.1") + self.assertTrue(c.checkMTU()) + c.detach() + logs = c.terminate() + self.assertEqual(logs, None) # no tracing + + @unittest.skip("this test takes long: it verifies the UE cannot attach") + def test_simple_fail(self): + c = cls_module.Module_UE("test-fail", filename="tests/config/test_module_infra.yaml") + success = c.initialize() + self.assertTrue(success) + ip = c.attach() + self.assertEqual(ip, None) + self.assertFalse(c.checkMTU()) + c.detach() + logs = c.terminate() + self.assertEqual(logs, None) # no tracing + +if __name__ == '__main__': + unittest.main() From de2d32be092774aec283b9be4465b2c27feca769 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 31 Jul 2025 18:29:19 +0200 Subject: [PATCH 54/86] CI UE Attach: add more frequent checks, improve output check for IP once before sleeping in case UE IP address exists, which should speed up the CI. With this, Only print the actual attach command once. Print errors only at the beginning. This should simplify attach command output logs. --- ci-scripts/cls_module.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ci-scripts/cls_module.py b/ci-scripts/cls_module.py index 3fed4f3dd6..d387783bf7 100644 --- a/ci-scripts/cls_module.py +++ b/ci-scripts/cls_module.py @@ -70,11 +70,11 @@ class Module_UE: def __repr__(self): return self.__str__() - def _command(self, cmd, silent = False): + def _command(self, cmd, silent=False, reportNonZero=True): if cmd is None: raise Exception("no command provided") with cls_cmd.getConnection(self.host) as c: - response = c.run(cmd, silent=silent) + response = c.run(cmd, silent=silent, reportNonZero=reportNonZero) return response #-----------------$ @@ -109,10 +109,11 @@ class Module_UE: self._command(self.cmd_dict["attach"]) timeout = attach_timeout logging.debug("Waiting for IP address to be assigned") + ip = self.getIP(silent=False, reportNonZero=True) while timeout > 0 and not ip: - time.sleep(5) - timeout -= 5 - ip = self.getIP() + time.sleep(1) + timeout -= 1 + ip = self.getIP(silent=True, reportNonZero=False) if ip: break logging.warning(f"UE did not receive IP address after {attach_timeout} s, detaching") @@ -156,8 +157,8 @@ class Module_UE: logging.error(message) return False - def getIP(self): - output = self._command(self.cmd_dict["getNetwork"], silent=True) + def getIP(self, silent=True, reportNonZero=True): + output = self._command(self.cmd_dict["getNetwork"], silent=silent, reportNonZero=reportNonZero) result = re.search(r'inet (?P[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)', output.stdout) if result and result.group('ip'): ip = result.group('ip') From a3d46ed0e6c07343f8085d829a9cb9dc9b00043a Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 6 Aug 2025 01:03:06 +0200 Subject: [PATCH 55/86] 5g rfsim NTN LEO/GEO: remove leading undeploy step It is not necessary (the final undeploys at the end are "always_exec"), and makes the CI potentially fail since there is nothing to copy at this point. --- ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml | 1 - ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml | 1 - 2 files changed, 2 deletions(-) diff --git a/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml b/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml index 2cefc32722..88173b3659 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml @@ -26,7 +26,6 @@ wrench 111111 - 100001 800813 000001 000002 diff --git a/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml b/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml index 61bf4d5e48..0d2634192d 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml @@ -26,7 +26,6 @@ wrench 111111 - 100001 800813 000001 000002 From ce9f2c9a45ff339c96254e81ac59cc192ed4c735 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 31 Jul 2025 18:28:16 +0200 Subject: [PATCH 56/86] Add possibility to trace UEs --- ci-scripts/cls_module.py | 58 ++++++++++++++----- .../tests/config/test_module_infra.yaml | 15 +++++ ci-scripts/tests/module.py | 17 ++++++ 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/ci-scripts/cls_module.py b/ci-scripts/cls_module.py index d387783bf7..5493511bd8 100644 --- a/ci-scripts/cls_module.py +++ b/ci-scripts/cls_module.py @@ -59,11 +59,20 @@ class Module_UE: } self.interface = m.get('IF') self.MTU = m.get('MTU') - self.trace = m.get('trace') == True - self.logStore = m.get('LogStore') self.cmd_prefix = m.get('CmdPrefix') logging.info(f'initialized UE {self} from {filename}') + t = m.get('Tracing') + self.trace = t is not None + if self.trace: + if t.get('Start') is None or t.get('Stop') is None or t.get('Collect')is None : + raise ValueError("need to have Start/Stop/Collect for tracing") + self.cmd_dict["traceStart"] = t.get('Start') + self.cmd_dict["traceStop"] = t.get('Stop') + self._logCollect = t.get('Collect') + if "%%log_dir%%" not in self._logCollect: + raise ValueError(f"(At least one) LogCollect expression for {module_name} must contain \"%%log_dir%%\"") + def __str__(self): return f"{self.module_name}@{self.host}" @@ -82,25 +91,23 @@ class Module_UE: #-----------------$ def initialize(self): - if self.trace: - raise Exception("UE tracing not implemented yet") - self._enableTrace() # we first terminate to make sure the UE has been stopped if self.cmd_dict["detach"]: self._command(self.cmd_dict["detach"], silent=True) self._command(self.cmd_dict["terminate"], silent=True) ret = self._command(self.cmd_dict["initialize"]) logging.info(f'For command: {ret.args} | return output: {ret.stdout} | Code: {ret.returncode}') + if self.trace: + self._enableTrace() # Here each UE returns differently for the successful initialization, requires check based on UE return ret.returncode == 0 - def terminate(self): + def terminate(self, log_dir=None): self._command(self.cmd_dict["terminate"]) - if self.trace: - raise Exception("UE tracing not implemented yet") + if self.trace and log_dir is not None: self._disableTrace() - return self._logCollect() + return self._collectTrace(log_dir) return None def attach(self, attach_tries = 4, attach_timeout = 60): @@ -188,10 +195,35 @@ class Module_UE: return self.cmd_prefix if self.cmd_prefix else "" def _enableTrace(self): - raise Exception("not implemented") + logging.info(f'UE {self}: start UE tracing') + self._command(self.cmd_dict["traceStart"]) def _disableTrace(self): - raise Exception("not implemented") + logging.info(f'UE {self}: stop UE tracing') + self._command(self.cmd_dict["traceStop"]) - def _logCollect(self): - raise Exception("not implemented") + def _collectTrace(self, log_dir): + logging.info(f'collecting logs into (local) {log_dir}') + remote_dir = "/tmp/ue-trace-logs" + with cls_cmd.getConnection(self.host) as c: + # create a directory for log collection + c.run(f'rm -rf {remote_dir}') + ret = c.run(f'mkdir {remote_dir}') + if ret.returncode != 0: + logging.error("cannot create directory for log collection") + return [] + log_cmd = self._logCollect.replace('%%log_dir%%', remote_dir) + self._command(log_cmd) + # enumerate collected files + ret = c.run(f'ls {remote_dir}/*') + if ret.returncode != 0: + logging.error("cannot enumerate log files") + return [] + log_files = [] + # copy them to the executor one by one, and store in log_dir + for f in ret.stdout.split("\n"): + l = f.replace(remote_dir, log_dir) + c.copyin(f, l) + log_files.append(l) + c.run(f'rm -rf {remote_dir}') + return log_files diff --git a/ci-scripts/tests/config/test_module_infra.yaml b/ci-scripts/tests/config/test_module_infra.yaml index fadc721232..7bfdfc1d9d 100644 --- a/ci-scripts/tests/config/test_module_infra.yaml +++ b/ci-scripts/tests/config/test_module_infra.yaml @@ -17,3 +17,18 @@ test-fail: NetworkScript: ip a show dev TESTDEVICEDOESNOTEXIST IF: TESTDEVICEDOESNOTEXIST MTU: 123 + +test-trace: + Host: localhost + InitScript: "" + TermScript: "" + AttachScript: "true" + DetachScript: "true" + Tracing: + # tttf: temp-trace-test-file + Start: "rm -f /tmp/tttf; for i in {1..8}; do echo $i >> /tmp/tttf; sleep 1; done &" + Stop: "while [[ $(wc -l /tmp/tttf) < 4 ]]; do sleep 1; done" + Collect: "cp /tmp/tttf %%log_dir%%" + NetworkScript: ip a show dev lo + IF: lo + MTU: 65536 diff --git a/ci-scripts/tests/module.py b/ci-scripts/tests/module.py index b6898405d4..366593873c 100644 --- a/ci-scripts/tests/module.py +++ b/ci-scripts/tests/module.py @@ -1,5 +1,6 @@ import sys import logging +import tempfile logging.basicConfig( level=logging.DEBUG, stream=sys.stdout, @@ -16,6 +17,7 @@ import cls_cmd class TestModule(unittest.TestCase): def test_simple_module(self): c = cls_module.Module_UE("test", filename="tests/config/test_module_infra.yaml") + self.assertFalse(c.trace) success = c.initialize() self.assertTrue(success) ip = c.attach() @@ -37,5 +39,20 @@ class TestModule(unittest.TestCase): logs = c.terminate() self.assertEqual(logs, None) # no tracing + def test_simple_trace(self): + c = cls_module.Module_UE("test-trace", filename="tests/config/test_module_infra.yaml") + self.assertTrue(c.trace) + success = c.initialize() + self.assertTrue(success) + ip = c.attach() + self.assertEqual(ip, "127.0.0.1") + self.assertTrue(c.checkMTU()) + c.detach() + tmp = tempfile.mkdtemp() + log_file = c.terminate(log_dir=tmp) + with cls_cmd.LocalCmd() as c: + c.run(f'rm -rf {tmp}') + self.assertEqual(log_file, [f"{tmp}/tttf"]) # matches test-trace UE collection file + if __name__ == '__main__': unittest.main() From fe2cda1309155fb2bdb8c12cdb8e4ab859b1cd3b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 31 Jul 2025 19:44:50 +0200 Subject: [PATCH 57/86] CI: log UE files in artifacts --- ci-scripts/cls_oaicitest.py | 4 ++-- ci-scripts/main.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 53bdacaacc..0a550016d4 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -835,10 +835,10 @@ class OaiCiTest(): global_status = CONST.OAI_UE_PROCESS_COULD_NOT_SYNC return global_status - def TerminateUE(self, HTML): + def TerminateUE(self, HTML, logPath): ues = [cls_module.Module_UE(n.strip()) for n in self.ue_ids] with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: - futures = [executor.submit(ue.terminate) for ue in ues] + futures = [executor.submit(ue.terminate, logPath) for ue in ues] archives = [f.result() for f in futures] archive_info = [f'Log at: {a}' if a else 'No log available' for a in archives] messages = [f"UE {ue.getName()}: {log}" for (ue, log) in zip(ues, archive_info)] diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 18956ae28f..fd794de22c 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -226,7 +226,7 @@ def ExecuteActionWithParam(action): elif action == 'Detach_UE': success = CiTestObj.DetachUE(HTML) elif action == 'Terminate_UE': - success = CiTestObj.TerminateUE(HTML) + success = CiTestObj.TerminateUE(HTML, logPath) elif action == 'CheckStatusUE': success = CiTestObj.CheckStatusUE(HTML) elif action == 'DataEnable_UE': From af580f3a2f012682a3b24ed0614e61cb32694271 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 31 Jul 2025 18:33:42 +0200 Subject: [PATCH 58/86] Add example tracing for Amarisoft UE sim Commented, as the resulting files are very big. --- ci-scripts/ci_infra.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ci-scripts/ci_infra.yaml b/ci-scripts/ci_infra.yaml index fd2d414a03..2f91576a60 100644 --- a/ci-scripts/ci_infra.yaml +++ b/ci-scripts/ci_infra.yaml @@ -146,6 +146,10 @@ amarisoft_ue: InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_aw2s_asue_20MHz/aw2s-multi-00102-20.cfg & TermScript: /root/lteue-linux-2025-03-15/ws.js -t 10 127.0.0.1:9002 '{"message":"quit"}' || killall -KILL lteue-avx2 NetworkScript: ip netns exec ue1 ip a show dev pdn0 + #Tracing: + # Start: '' # nothing to be done + # Stop: '' # nothing to be done + # Collect: 'cp /tmp/ue.log %%log_dir%%' amarisoft_ue_2x2: Host: amariue InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_aw2s_asue_20MHz_2x2/aw2s-multi-00102-2x2-v2.cfg & From 5492de0e0b92819bb0f2c58175235bf85e0630aa Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 1 Aug 2025 07:51:55 +0200 Subject: [PATCH 59/86] Example tracing for QLog Note: this does not actually work. In the pipelines, we see that for some reason, quectel-CM cannot establish a connection. Manually, it works though(?) --- ci-scripts/ci_infra.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ci-scripts/ci_infra.yaml b/ci-scripts/ci_infra.yaml index 2f91576a60..e5888ef6fe 100644 --- a/ci-scripts/ci_infra.yaml +++ b/ci-scripts/ci_infra.yaml @@ -5,6 +5,10 @@ idefix: AttachScript: sudo python3 ci_ctl_qtel.py /dev/ttyUSB2 wup DetachScript: sudo python3 ci_ctl_qtel.py /dev/ttyUSB2 detach NetworkScript: ip a show dev wwan0 + #Tracing: + # Start: 'sudo rm -f ci_qlog/*; nohup sudo -b QLog/QLog -s ci_qlog -f NR5G.cfg &' + # Stop: 'sudo killall QLog' + # Collect: 'sudo mv ci_qlog/* %%log_dir%%/' IF: wwan0 MTU: 1500 From 5c16fc6189b3669c8b409d3c4a99ec42d8bd671c Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 4 Aug 2025 09:25:11 +0200 Subject: [PATCH 60/86] Fix Python "invalid escape sequence" warnings cls_containerize.py:139: SyntaxWarning: invalid escape sequence '\.' lineHasCommit = re.search(f'COMMIT [a-zA-Z0-9\.:/\-]*{image}', str(line)) is not None cls_containerize.py:441: SyntaxWarning: invalid escape sequence '\-' result = re.search('Size *= *(?P[0-9\-]+) *bytes', cmd.getBefore()) cls_containerize.py:493: SyntaxWarning: invalid escape sequence '\-' result = re.search('Size *= *(?P[0-9\-]+) *bytes', cmd.getBefore()) cls_containerize.py:616: SyntaxWarning: invalid escape sequence '\-' result = re.search('Size *= *(?P[0-9\-]+) *bytes', ret.stdout) --- ci-scripts/cls_containerize.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index b878e82caa..3d63de0834 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -136,7 +136,7 @@ def AnalyzeBuildLogs(buildRoot, images, globalStatus): lineHasTag2 = re.search(f'naming to docker.io/library/{image}:', str(line)) is not None tagged = tagged or lineHasTag or lineHasTag2 # the OpenShift Cluster builder prepends image registry URL - lineHasCommit = re.search(f'COMMIT [a-zA-Z0-9\.:/\-]*{image}', str(line)) is not None + lineHasCommit = re.search(r'COMMIT [a-zA-Z0-9\.:/\-]*' + image, str(line)) is not None committed = committed or lineHasCommit errorandwarnings['errors'] = 0 if committed or tagged else 1 errorandwarnings['warnings'] = 0 @@ -441,7 +441,7 @@ class Containerize(): HTML.CreateHtmlTabFooter(False) return False else: - result = re.search('Size *= *(?P[0-9\-]+) *bytes', cmd.getBefore()) + result = re.search(r'Size *= *(?P[0-9\-]+) *bytes', cmd.getBefore()) if result is not None: size = float(result.group("size")) / 1000000 imageSizeStr = f'{size:.1f}' @@ -493,7 +493,7 @@ class Containerize(): allImagesSize[name] = 'N/A -- Build Failed' break else: - result = re.search('Size *= *(?P[0-9\-]+) *bytes', cmd.getBefore()) + result = re.search(r'Size *= *(?P[0-9\-]+) *bytes', cmd.getBefore()) if result is not None: size = float(result.group("size")) / 1000000 # convert to MB imageSizeStr = f'{size:.1f}' @@ -616,7 +616,7 @@ class Containerize(): collectInfo = {} collectInfo['proxy'] = files ret = ssh.run(f'docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' proxy:{tag}') - result = re.search('Size *= *(?P[0-9\-]+) *bytes', ret.stdout) + result = re.search(r'Size *= *(?P[0-9\-]+) *bytes', ret.stdout) # Cleaning any created tmp volume ssh.run('docker volume prune --force') ssh.close() From f766b21b5776ab94755292c4101e83cb5d29c83c Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 08:16:48 +0200 Subject: [PATCH 61/86] Add CI helper class This class provides a generic "context" for individual tasks about the current task step, and where to log results. It's used by archiveArtifact() that would automatically log files in the right directory, based on a directory in the context, and in a way that order of files is preserved for long-running pipelines. --- ci-scripts/cls_ci_helper.py | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 ci-scripts/cls_ci_helper.py diff --git a/ci-scripts/cls_ci_helper.py b/ci-scripts/cls_ci_helper.py new file mode 100644 index 0000000000..eb47a32892 --- /dev/null +++ b/ci-scripts/cls_ci_helper.py @@ -0,0 +1,44 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ + +import os +from typing import NamedTuple +import logging + +class TestCaseCtx(NamedTuple): + count: int + test_id: int + logPath: str + + def Default(logPath): + return TestCaseCtx(123, 112233, logPath) + def baseFilename(self): + # typically, the test ID is of form 001234 (6 digits) + return f"{self.logPath}/{self.count}-{self.test_id:06d}" + +def archiveArtifact(cmd, ctx, remote_path): + base = os.path.basename(remote_path) + local = f"{ctx.baseFilename()}-{base}" + logging.info(f"Archive artifact '{local}'") + success = cmd.copyin(remote_path, local) + if success: + cmd.run(f'rm {remote_path}', silent=True) + return local if success else None From 28ce0dc444fa206b21cbfe3655639ab7d2954d25 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 09:42:06 +0200 Subject: [PATCH 62/86] Main: pass in ctx Set the context for each task step. Set the artifact result path based on the XML, so it is harmonized; currently, most pipelines use the "YAML path" of docker-compose, but that does not always make sense. For instance, Ping/Iperf() needs to look up this path, but does not inherently belong to one specific YAML path (there might even be more). --- ci-scripts/main.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/ci-scripts/main.py b/ci-scripts/main.py index fd794de22c..3cc550eba5 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -43,8 +43,10 @@ import cls_containerize #class Containerize for all container-based operations import cls_static_code_analysis #class for static code analysis import cls_cluster # class for building/deploying on cluster import cls_native # class for all native/source-based operations +from cls_ci_helper import TestCaseCtx import ran +import cls_cmd import cls_oai_html @@ -91,7 +93,7 @@ def AssignParams(params_dict): setattr(RAN, key, value) setattr(HTML, key, value) -def ExecuteActionWithParam(action): +def ExecuteActionWithParam(action, ctx): global RAN global HTML global CONTAINERS @@ -565,6 +567,16 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re else: xml_test_file = cwd + "/" + CiTestObj.testXMLfiles[0] + # directory where all log artifacts will be placed + logPath = f"{cwd}/../cmake_targets/log/{CiTestObj.testXMLfiles[0].split('/')[-1]}.d" + # we run from within ci-scripts, but the logPath is absolute, so replace + # the ci-scripts/..; if it does not exist, nothing will happen + logPath = logPath.replace(r'/ci-scripts/..', '') + logging.info(f"placing all artifacts for this run in {logPath}/") + with cls_cmd.LocalCmd() as c: + c.run(f"rm -rf {logPath}") + c.run(f"mkdir -p {logPath}") + xmlTree = ET.parse(xml_test_file) xmlRoot = xmlTree.getroot() @@ -615,12 +627,15 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re task_set_succeeded = True HTML.startTime=int(round(time.time() * 1000)) + i = 0 for test_case_id in todo_tests: for test in all_tests: id = test.get('id') if test_case_id != id: continue + i += 1 CiTestObj.testCase_id = id + ctx = TestCaseCtx(i, int(id), logPath) HTML.testCase_id=CiTestObj.testCase_id CiTestObj.desc = test.findtext('desc') always_exec = test.findtext('always_exec') in ['True', 'true', 'Yes', 'yes'] @@ -637,7 +652,7 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re HTML.CreateHtmlTestRowQueue(msg, "SKIP", []) break try: - test_succeeded = ExecuteActionWithParam(action) + test_succeeded = ExecuteActionWithParam(action, ctx) if not test_succeeded and may_fail: logging.warning(f"test ID {test_case_id} action {action} may or may not fail, proceeding despite error") elif not test_succeeded: From dcbb3369e19d8cf6f20e6ac8e67ea1a01a923b84 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 09:58:06 +0200 Subject: [PATCH 63/86] Simplify ShowTestID() make it a non-class function, stateless, more information on the current CI step (the number). --- ci-scripts/cls_oaicitest.py | 7 ------- ci-scripts/main.py | 15 ++++++++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 0a550016d4..b2972b667d 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -258,7 +258,6 @@ class OaiCiTest(): self.testCase_id = '' self.testXMLfiles = [] - self.desc = '' self.ping_args = '' self.ping_packetloss_threshold = '' self.ping_rttavg_threshold ='' @@ -891,9 +890,3 @@ class OaiCiTest(): d = f'{SourceCodePath}/cmake_targets' cmd.run(f'rm -f {d}/build.log.zip') cmd.run(f'cd {d} && zip -r build.log.zip build_log_*/*') - - def ShowTestID(self): - logging.info(f'\u001B[1m----------------------------------------\u001B[0m') - logging.info(f'\u001B[1m Test ID: {self.testCase_id} \u001B[0m') - logging.info(f'\u001B[1m {self.desc} \u001B[0m') - logging.info(f'\u001B[1m----------------------------------------\u001B[0m') diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 3cc550eba5..275686abec 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -409,10 +409,11 @@ def test_in_list(test, list): def receive_signal(signum, frame): sys.exit(1) - - - - +def ShowTestID(ctx, desc): + logging.info(f'\u001B[1m----------------------------------------\u001B[0m') + logging.info(f'\u001B[1m Test ID: {ctx.test_id} (#{ctx.count}) \u001B[0m') + logging.info(f'\u001B[1m {desc} \u001B[0m') + logging.info(f'\u001B[1m----------------------------------------\u001B[0m') #----------------------------------------------------------- # MAIN PART @@ -637,15 +638,15 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re CiTestObj.testCase_id = id ctx = TestCaseCtx(i, int(id), logPath) HTML.testCase_id=CiTestObj.testCase_id - CiTestObj.desc = test.findtext('desc') + desc = test.findtext('desc') always_exec = test.findtext('always_exec') in ['True', 'true', 'Yes', 'yes'] may_fail = test.findtext('may_fail') in ['True', 'true', 'Yes', 'yes'] - HTML.desc=CiTestObj.desc + HTML.desc = desc action = test.findtext('class') if (CheckClassValidity(xml_class_list, action, id) == False): task_set_succeeded = False continue - CiTestObj.ShowTestID() + ShowTestID(ctx, desc) if not task_set_succeeded and not always_exec: msg = f"skipping test due to prior error" logging.warning(msg) From 2b4e0e5244601a7b895b4cc4d1a735813750141f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 09:41:40 +0200 Subject: [PATCH 64/86] Use archiveArtifact() for CN undeploy --- ci-scripts/cls_corenetwork.py | 17 ++++++++--------- ci-scripts/cls_oaicitest.py | 14 ++++---------- ci-scripts/main.py | 2 +- ci-scripts/tests/corenetwork.py | 18 +++++++++++++----- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/ci-scripts/cls_corenetwork.py b/ci-scripts/cls_corenetwork.py index 579bad585b..ad83eae622 100644 --- a/ci-scripts/cls_corenetwork.py +++ b/ci-scripts/cls_corenetwork.py @@ -29,6 +29,7 @@ import yaml import re import cls_cmd +from cls_ci_helper import archiveArtifact def listify(s): if s is None: @@ -114,8 +115,7 @@ class CoreNetwork: logging.info(f'deployed core network {self}, pingable IP address {ip}') return True, output - def _collect_logs(self, log_dir): - logging.info(f'collecting logs into (local) {log_dir}') + def _collect_logs(self, ctx): remote_dir = "/tmp/cn-undeploy-logs" with cls_cmd.getConnection(self._host) as c: # create a directory for log collection @@ -134,18 +134,17 @@ class CoreNetwork: logging.error("cannot enumerate log files") return [] log_files = [] - # copy them to the executor one by one, and store in log_dir + # copy them to the executor one by one for f in ret.stdout.split("\n"): - l = f.replace(remote_dir, log_dir) - c.copyin(f, l) - log_files.append(l) + name = archiveArtifact(c, ctx, f) + log_files.append(name) c.run(f'rm -rf {remote_dir}') return log_files - def undeploy(self, log_dir=None): + def undeploy(self, ctx=None): log_files = [] - if log_dir is not None: - log_files = self._collect_logs(log_dir) + if ctx is not None: + log_files = self._collect_logs(ctx) else: logging.warning("no directory for log collection specified, cannot retrieve core network logs") logging.info(f'undeploy core network {self}') diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index b2972b667d..290ad38598 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -844,7 +844,7 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue(f'N/A', 'OK', messages) return True - def DeployCoreNetwork(cn_id, HTML): + def DeployCoreNetwork(cn_id, ctx, HTML): core_name = cn_id.strip() cn = cls_corenetwork.CoreNetwork(core_name) success, output = cn.deploy() @@ -858,18 +858,12 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue(core_name, 'KO', [msg]) return success - def UndeployCoreNetwork(cn_id, HTML): - # Ping, Iperf, DeployObject put logs into a path based on YAML. We - # can't do this here (because there is no yaml), so hardcode a path for - # "cn_logs" for the moment - logPath = f'{os.getcwd()}/../cmake_targets/log/cn_logs' - with cls_cmd.getConnection('localhost') as local: - local.run(f'mkdir -p {logPath}', silent=True) + def UndeployCoreNetwork(cn_id, ctx, HTML): core_name = cn_id.strip() cn = cls_corenetwork.CoreNetwork(core_name) - logs, output = cn.undeploy(log_dir=logPath) + logs, output = cn.undeploy(ctx=ctx) logging.info(f"undeployed core network {core_name}, logs {logs}, output:\n{output}") - message = "Log files: " + ", ".join([os.path.basename(l) for l in logs]) + message = "Log files:\n" + "\n".join([os.path.basename(l) for l in logs]) HTML.CreateHtmlTestRowQueue(core_name, 'OK', [message]) return True diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 275686abec..a6da7b399d 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -302,7 +302,7 @@ def ExecuteActionWithParam(action, ctx): elif action == 'DeployCoreNetwork' or action == 'UndeployCoreNetwork': cn_id = test.findtext('cn_id') core_op = getattr(cls_oaicitest.OaiCiTest, action) - success = core_op(cn_id, HTML) + success = core_op(cn_id, ctx, HTML) elif action == 'Deploy_Object' or action == 'Undeploy_Object' or action == "Create_Workspace": eNB_instance=test.findtext('eNB_instance') diff --git a/ci-scripts/tests/corenetwork.py b/ci-scripts/tests/corenetwork.py index 77e5c7629e..7bc1603305 100644 --- a/ci-scripts/tests/corenetwork.py +++ b/ci-scripts/tests/corenetwork.py @@ -6,14 +6,22 @@ logging.basicConfig( format="[%(asctime)s] %(levelname)8s: %(message)s" ) import os +import tempfile import unittest sys.path.append('./') # to find OAI imports below import cls_corenetwork +from cls_ci_helper import TestCaseCtx import cls_cmd class TestCoreNetwork(unittest.TestCase): + def setUp(self): + self.ctx = TestCaseCtx.Default(tempfile.mkdtemp()) + def tearDown(self): + with cls_cmd.LocalCmd() as c: + c.run(f'rm -rf {self.ctx.logPath}') + def test_simple_core(self): c = cls_corenetwork.CoreNetwork("test", filename="tests/config/test_core_infra.yaml") success, output = c.deploy() @@ -22,14 +30,14 @@ class TestCoreNetwork(unittest.TestCase): # should not have it set in config self.assertTrue(c.runIperf3Server()) self.assertEqual(c.getIP(), "127.0.0.1") - log_files, output = c.undeploy(log_dir="/tmp") + log_files, output = c.undeploy(self.ctx) self.assertEqual(output, "undeploy") with cls_cmd.LocalCmd() as cmd: # there must be one log file for this (test) core self.assertEqual(len(log_files), 1) - # test core writes to %%log_dir%%/logs, which expands to /tmp/logs + # undeploy uses archiveArtifact(), which writes to {prefix}-logs l = log_files[0] - self.assertEqual(l, "/tmp/logs") + self.assertEqual(l, f"{self.ctx.baseFilename()}-logs") ret = cmd.run(f"cat {l}") self.assertEqual(ret.returncode, 0) # command must succeed self.assertEqual(ret.stdout, "logs") # output should be "logs" @@ -39,14 +47,14 @@ class TestCoreNetwork(unittest.TestCase): success, _ = c.deploy() self.assertTrue(success) self.assertFalse(c.runIperf3Server()) - c.undeploy(log_dir="/tmp") + c.undeploy(None) def test_core_fail(self): c = cls_corenetwork.CoreNetwork("test_fail", filename="tests/config/test_core_infra.yaml") success, _ = c.deploy() self.assertFalse(success) # undeployment should still work - c.undeploy(log_dir="/tmp") + c.undeploy(None) def test_core_script(self): c = cls_corenetwork.CoreNetwork("test_script", filename="tests/config/test_core_infra.yaml") From fa5c91e03f4c73455cfb7217f4e000ae7f313e39 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 09:40:28 +0200 Subject: [PATCH 65/86] Use archiveArtifact() for (Un-)DeployObject() --- ci-scripts/cls_containerize.py | 45 ++++++++++++++-------------------- ci-scripts/main.py | 4 +-- ci-scripts/tests/deployment.py | 32 ++++++++++++++---------- 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 3d63de0834..591d6d413d 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -46,6 +46,7 @@ import cls_cmd import helpreadme as HELP import constants as CONST import cls_oaicitest +from cls_ci_helper import archiveArtifact #----------------------------------------------------------- # Helper functions used here and in other classes @@ -202,12 +203,10 @@ def GetServices(ssh, requested, file): else: return requested -def CopyinServiceLog(ssh, lSourcePath, yaml, svcName, wd_yaml, filename): - remote_filename = f"{lSourcePath}/cmake_targets/log/{filename}" +def CopyinServiceLog(ssh, lSourcePath, svcName, wd_yaml, ctx): + remote_filename = f"{lSourcePath}/cmake_targets/log/{svcName}.logs" ssh.run(f'docker compose -f {wd_yaml} logs {svcName} --no-log-prefix &> {remote_filename}') - local_dir = f"{os.getcwd()}/../cmake_targets/log/{yaml}" - local_filename = f"{local_dir}/{filename}" - return ssh.copyin(remote_filename, local_filename) + return archiveArtifact(ssh, ctx, remote_filename) def GetRunningServices(ssh, file): ret = ssh.run(f'docker compose -f {file} config --services') @@ -229,14 +228,13 @@ def GetRunningServices(ssh, file): logging.info(f'stopping services: {running_services}') return running_services -def CheckLogs(self, yaml, service_name, HTML, RAN): - logPath = f'{os.getcwd()}/../cmake_targets/log/{yaml}' - filename = f'{logPath}/{service_name}-{HTML.testCase_id}.log' +def CheckLogs(self, filename, HTML, RAN): success = True - if (any(sub in service_name for sub in ['oai_ue','oai-nr-ue','lte_ue'])): + name = os.path.basename(filename) + if (any(sub in name for sub in ['oai_ue','oai-nr-ue','lte_ue'])): logging.debug(f'\u001B[1m Analyzing UE logfile {filename} \u001B[0m') logStatus = cls_oaicitest.OaiCiTest().AnalyzeLogFile_UE(filename, HTML, RAN) - opt = f"UE log analysis for service {service_name}" + opt = f"UE log analysis ({name})" # usage of htmlUEFailureMsg/htmleNBFailureMsg is because Analyze log files # abuse HTML to store their reports, and we here want to put custom options, # which is not possible with CreateHtmlTestRow @@ -247,13 +245,13 @@ def CheckLogs(self, yaml, service_name, HTML, RAN): else: HTML.CreateHtmlTestRowQueue(opt, 'OK', [HTML.htmlUEFailureMsg]) HTML.htmlUEFailureMsg = "" - elif service_name == 'nv-cubb': + elif 'nv-cubb' in name: msg = 'Undeploy PNF/Nvidia CUBB' HTML.CreateHtmlTestRow(msg, 'OK', CONST.ALL_PROCESSES_OK) - elif (any(sub in service_name for sub in ['enb','rru','rcc','cu','du','gnb'])): + elif (any(sub in name for sub in ['enb','rru','rcc','cu','du','gnb'])): logging.debug(f'\u001B[1m Analyzing XnB logfile {filename}\u001B[0m') logStatus = RAN.AnalyzeLogFile_eNB(filename, HTML, self.ran_checkers) - opt = f"xNB log analysis for service {service_name}" + opt = f"xNB log analysis ({name})" if (logStatus < 0): HTML.CreateHtmlTestRowQueue(opt, 'KO', [HTML.htmleNBFailureMsg]) success = False @@ -261,9 +259,9 @@ def CheckLogs(self, yaml, service_name, HTML, RAN): HTML.CreateHtmlTestRowQueue(opt, 'OK', [HTML.htmleNBFailureMsg]) HTML.htmleNBFailureMsg = "" else: - logging.info(f'Skipping to analyze log for service name {service_name}') - HTML.CreateHtmlTestRowQueue(f"service {service_name}", 'OK', ["no analysis function"]) - logging.debug(f"log check: service {service_name} passed analysis {success}") + logging.info(f"Skipping analysis of log '{filename}': no submatch for xNB/UE") + HTML.CreateHtmlTestRowQueue(f"file {name}", 'OK', ["no analysis function"]) + logging.debug(f"log check: file {filename} passed analysis {success}") return success #----------------------------------------------------------- @@ -823,18 +821,14 @@ class Containerize(): HTML.CreateHtmlTestRowQueue('N/A', 'KO', ["cannot create workspace"]) return success - def DeployObject(self, HTML): + def DeployObject(self, ctx, HTML): svr = self.eNB_serverId[self.eNB_instance] num_attempts = self.num_attempts lIpAddr, lSourcePath = self.GetCredentials(svr) logging.debug(f'Deploying OAI Object on server: {lIpAddr}') yaml = self.yamlPath[self.eNB_instance].strip('/') - # creating the log folder by default - local_dir = f"{os.getcwd()}/../cmake_targets/log/{yaml.split('/')[-1]}" - os.system(f'mkdir -p {local_dir}') wd = f'{lSourcePath}/{yaml}' wd_yaml = f'{wd}/docker-compose.y*ml' - yaml_dir = yaml.split('/')[-1] with cls_cmd.getConnection(lIpAddr) as ssh: services = GetServices(ssh, self.services[self.eNB_instance], wd_yaml) if services == [] or services == ' ' or services == None: @@ -861,7 +855,7 @@ class Containerize(): logging.warning(warning_msg) HTML.CreateHtmlTestRowQueue('N/A', 'NOK', [warning_msg]) for svc in services.split(): - CopyinServiceLog(ssh, lSourcePath, yaml_dir, svc, wd_yaml, f'{svc}-{HTML.testCase_id}-attempt{attempt}.log') + CopyinServiceLog(ssh, lSourcePath, svc, wd_yaml, ctx) ssh.run(f'docker compose -f {wd_yaml} down -- {services}') imagesInfo = info.stdout.splitlines()[1:] logging.debug(f'{info.stdout.splitlines()[1:]}') @@ -871,13 +865,12 @@ class Containerize(): HTML.CreateHtmlTestRowQueue('N/A', 'KO', ['\n'.join(imagesInfo)]) return deployed - def UndeployObject(self, HTML, RAN): + def UndeployObject(self, ctx, HTML, RAN): svr = self.eNB_serverId[self.eNB_instance] lIpAddr, lSourcePath = self.GetCredentials(svr) logging.debug(f'\u001B[1m Undeploying OAI Object from server: {lIpAddr}\u001B[0m') yaml = self.yamlPath[self.eNB_instance].strip('/') wd = f'{lSourcePath}/{yaml}' - yaml_dir = yaml.split('/')[-1] with cls_cmd.getConnection(lIpAddr) as ssh: ExistEnvFilePrint(ssh, wd) services = GetRunningServices(ssh, f"{wd}/docker-compose.y*ml") @@ -885,7 +878,7 @@ class Containerize(): if services is not None: all_serv = " ".join([s for s, _ in services]) ssh.run(f'docker compose -f {wd}/docker-compose.y*ml stop -- {all_serv}') - copyin_res = all(CopyinServiceLog(ssh, lSourcePath, yaml_dir, s, f"{wd}/docker-compose.y*ml", f'{s}-{HTML.testCase_id}.log') for s, c in services) + copyin_res = [CopyinServiceLog(ssh, lSourcePath, s, f"{wd}/docker-compose.y*ml", ctx) for s, _ in services] else: logging.warning('could not identify services to stop => no log file') ssh.run(f'docker compose -f {wd}/docker-compose.y*ml down -v') @@ -894,7 +887,7 @@ class Containerize(): HTML.CreateHtmlTestRowQueue('N/A', 'KO', ['Could not copy logfile(s)']) return False else: - log_results = [CheckLogs(self, yaml_dir, s, HTML, RAN) for s, _ in services] + log_results = [CheckLogs(self, f, HTML, RAN) for f in copyin_res] success = all(log_results) if success: logging.info('\u001B[1m Undeploying OAI Object Pass\u001B[0m') diff --git a/ci-scripts/main.py b/ci-scripts/main.py index a6da7b399d..7f5efefc34 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -330,9 +330,9 @@ def ExecuteActionWithParam(action, ctx): CONTAINERS.num_attempts = int(test.findtext('num_attempts') or 1) CONTAINERS.deploymentTag = cls_containerize.CreateTag(CONTAINERS.ranCommitID, CONTAINERS.ranBranch, CONTAINERS.ranAllowMerge) if action == 'Deploy_Object': - success = CONTAINERS.DeployObject(HTML) + success = CONTAINERS.DeployObject(ctx, HTML) elif action == 'Undeploy_Object': - success = CONTAINERS.UndeployObject(HTML, RAN) + success = CONTAINERS.UndeployObject(ctx, HTML, RAN) elif action == 'Create_Workspace': if force_local: # Do not create a working directory when running locally. Current repo directory will be used diff --git a/ci-scripts/tests/deployment.py b/ci-scripts/tests/deployment.py index a7337f6e52..d2fb0cda57 100644 --- a/ci-scripts/tests/deployment.py +++ b/ci-scripts/tests/deployment.py @@ -6,6 +6,7 @@ logging.basicConfig( format="[%(asctime)s] %(levelname)8s: %(message)s" ) import os +import tempfile os.system(f'rm -rf cmake_targets') os.system(f'mkdir -p cmake_targets/log') import unittest @@ -14,6 +15,7 @@ sys.path.append('./') # to find OAI imports below import cls_oai_html import cls_oaicitest import cls_containerize +from cls_ci_helper import TestCaseCtx import ran import cls_cmd @@ -48,12 +50,16 @@ class TestDeploymentMethods(unittest.TestCase): self.cont.eNBPassword = None self.cont.eNBSourceCodePath = os.getcwd() self.cont.num_attempts = 3 + self.ctx = TestCaseCtx.Default(tempfile.mkdtemp()) + def tearDown(self): + with cls_cmd.LocalCmd() as c: + c.run(f'rm -rf {self.ctx.logPath}') def test_deploy(self): self.cont.yamlPath[0] = 'tests/simple-dep/' self.cont.deploymentTag = "noble" - deploy = self.cont.DeployObject(self.html) - undeploy = self.cont.UndeployObject(self.html, self.ran) + deploy = self.cont.DeployObject(self.ctx, self.html) + undeploy = self.cont.UndeployObject(self.ctx, self.html, self.ran) self.assertTrue(deploy) self.assertTrue(undeploy) @@ -61,8 +67,8 @@ class TestDeploymentMethods(unittest.TestCase): # fails reliably old = self.cont.yamlPath self.cont.yamlPath[0] = 'tests/simple-fail/' - deploy = self.cont.DeployObject(self.html) - self.cont.UndeployObject(self.html, self.ran) + deploy = self.cont.DeployObject(self.ctx, self.html) + self.cont.UndeployObject(self.ctx, self.html, self.ran) self.assertFalse(deploy) self.cont.yamlPath = old @@ -70,8 +76,8 @@ class TestDeploymentMethods(unittest.TestCase): # fails reliably old = self.cont.yamlPath self.cont.yamlPath[0] = 'tests/simple-fail-2svc/' - deploy = self.cont.DeployObject(self.html) - self.cont.UndeployObject(self.html, self.ran) + deploy = self.cont.DeployObject(self.ctx, self.html) + self.cont.UndeployObject(self.ctx, self.html, self.ran) self.assertFalse(deploy) self.cont.yamlPath = old @@ -79,8 +85,8 @@ class TestDeploymentMethods(unittest.TestCase): self.cont.yamlPath[0] = 'yaml_files/5g_rfsimulator_tdd_dora' self.cont.services[0] = "oai-gnb" self.cont.deploymentTag = 'develop-12345678' - deploy = self.cont.DeployObject(self.html) - undeploy = self.cont.UndeployObject(self.html, self.ran) + deploy = self.cont.DeployObject(self.ctx, self.html) + undeploy = self.cont.UndeployObject(self.ctx, self.html, self.ran) self.assertTrue(deploy) self.assertTrue(undeploy) @@ -88,8 +94,8 @@ class TestDeploymentMethods(unittest.TestCase): self.cont.yamlPath[0] = 'yaml_files/5g_rfsimulator_tdd_dora' self.cont.services[0] = "oai-gnb oai-nr-ue" self.cont.deploymentTag = 'develop-12345678' - deploy = self.cont.DeployObject(self.html) - undeploy = self.cont.UndeployObject(self.html, self.ran) + deploy = self.cont.DeployObject(self.ctx, self.html) + undeploy = self.cont.UndeployObject(self.ctx, self.html, self.ran) self.assertTrue(deploy) self.assertTrue(undeploy) @@ -97,10 +103,10 @@ class TestDeploymentMethods(unittest.TestCase): self.cont.yamlPath[0] = 'yaml_files/5g_rfsimulator_tdd_dora' self.cont.services[0] = "oai-gnb" self.cont.deploymentTag = 'develop-12345678' - deploy1 = self.cont.DeployObject(self.html) + deploy1 = self.cont.DeployObject(self.ctx, self.html) self.cont.services[0] = "oai-nr-ue" - deploy2 = self.cont.DeployObject(self.html) - undeploy = self.cont.UndeployObject(self.html, self.ran) + deploy2 = self.cont.DeployObject(self.ctx, self.html) + undeploy = self.cont.UndeployObject(self.ctx, self.html, self.ran) self.assertTrue(deploy1) self.assertTrue(deploy2) self.assertTrue(undeploy) From 62ae72925b9b14a9effe6a88a84482548b31b07a Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 09:27:18 +0200 Subject: [PATCH 66/86] Use archiveArtifact() for module_UE --- ci-scripts/cls_module.py | 15 +++++++-------- ci-scripts/cls_oaicitest.py | 4 ++-- ci-scripts/main.py | 2 +- ci-scripts/tests/module.py | 14 ++++++++++---- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/ci-scripts/cls_module.py b/ci-scripts/cls_module.py index 5493511bd8..cfdd96477f 100644 --- a/ci-scripts/cls_module.py +++ b/ci-scripts/cls_module.py @@ -32,6 +32,7 @@ import re import yaml import cls_cmd +from cls_ci_helper import archiveArtifact class Module_UE: @@ -103,11 +104,11 @@ class Module_UE: return ret.returncode == 0 - def terminate(self, log_dir=None): + def terminate(self, ctx=None): self._command(self.cmd_dict["terminate"]) - if self.trace and log_dir is not None: + if self.trace and ctx is not None: self._disableTrace() - return self._collectTrace(log_dir) + return self._collectTrace(ctx) return None def attach(self, attach_tries = 4, attach_timeout = 60): @@ -202,8 +203,7 @@ class Module_UE: logging.info(f'UE {self}: stop UE tracing') self._command(self.cmd_dict["traceStop"]) - def _collectTrace(self, log_dir): - logging.info(f'collecting logs into (local) {log_dir}') + def _collectTrace(self, ctx): remote_dir = "/tmp/ue-trace-logs" with cls_cmd.getConnection(self.host) as c: # create a directory for log collection @@ -222,8 +222,7 @@ class Module_UE: log_files = [] # copy them to the executor one by one, and store in log_dir for f in ret.stdout.split("\n"): - l = f.replace(remote_dir, log_dir) - c.copyin(f, l) - log_files.append(l) + name = archiveArtifact(c, ctx, f) + log_files.append(name) c.run(f'rm -rf {remote_dir}') return log_files diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 290ad38598..52cea6a3c4 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -834,10 +834,10 @@ class OaiCiTest(): global_status = CONST.OAI_UE_PROCESS_COULD_NOT_SYNC return global_status - def TerminateUE(self, HTML, logPath): + def TerminateUE(self, ctx, HTML): ues = [cls_module.Module_UE(n.strip()) for n in self.ue_ids] with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: - futures = [executor.submit(ue.terminate, logPath) for ue in ues] + futures = [executor.submit(ue.terminate, ctx) for ue in ues] archives = [f.result() for f in futures] archive_info = [f'Log at: {a}' if a else 'No log available' for a in archives] messages = [f"UE {ue.getName()}: {log}" for (ue, log) in zip(ues, archive_info)] diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 7f5efefc34..509cd19109 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -228,7 +228,7 @@ def ExecuteActionWithParam(action, ctx): elif action == 'Detach_UE': success = CiTestObj.DetachUE(HTML) elif action == 'Terminate_UE': - success = CiTestObj.TerminateUE(HTML, logPath) + success = CiTestObj.TerminateUE(ctx, HTML) elif action == 'CheckStatusUE': success = CiTestObj.CheckStatusUE(HTML) elif action == 'DataEnable_UE': diff --git a/ci-scripts/tests/module.py b/ci-scripts/tests/module.py index 366593873c..47760cfa27 100644 --- a/ci-scripts/tests/module.py +++ b/ci-scripts/tests/module.py @@ -12,9 +12,16 @@ import unittest sys.path.append('./') # to find OAI imports below import cls_module +from cls_ci_helper import TestCaseCtx import cls_cmd class TestModule(unittest.TestCase): + def setUp(self): + self.ctx = TestCaseCtx.Default(tempfile.mkdtemp()) + def tearDown(self): + with cls_cmd.LocalCmd() as c: + c.run(f'rm -rf {self.ctx.logPath}') + def test_simple_module(self): c = cls_module.Module_UE("test", filename="tests/config/test_module_infra.yaml") self.assertFalse(c.trace) @@ -49,10 +56,9 @@ class TestModule(unittest.TestCase): self.assertTrue(c.checkMTU()) c.detach() tmp = tempfile.mkdtemp() - log_file = c.terminate(log_dir=tmp) - with cls_cmd.LocalCmd() as c: - c.run(f'rm -rf {tmp}') - self.assertEqual(log_file, [f"{tmp}/tttf"]) # matches test-trace UE collection file + log_file = c.terminate(self.ctx) + # undeploy uses archiveArtifact(), which writes to {prefix}-logs + self.assertEqual(log_file, [f"{self.ctx.baseFilename()}-tttf"]) # matches test-trace UE collection file if __name__ == '__main__': unittest.main() From da9cec0bfffcbf899f35ca8e992a498a23b37019 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 09:41:31 +0200 Subject: [PATCH 67/86] Use archiveArtifact() for Ping+IPerf --- ci-scripts/cls_oaicitest.py | 61 +++++++++++----------------------- ci-scripts/main.py | 6 ++-- ci-scripts/tests/ping-iperf.py | 15 ++++++--- 3 files changed, 33 insertions(+), 49 deletions(-) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 52cea6a3c4..ba322ca608 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -47,6 +47,7 @@ import constants as CONST import cls_module import cls_corenetwork import cls_cmd +from cls_ci_helper import archiveArtifact #----------------------------------------------------------- # Helper functions used here and in other classes @@ -256,7 +257,6 @@ class OaiCiTest(): self.ranAllowMerge = False self.ranTargetBranch = '' - self.testCase_id = '' self.testXMLfiles = [] self.ping_args = '' self.ping_packetloss_threshold = '' @@ -351,7 +351,7 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue('NA', 'OK', messages) return True - def Ping_common(self, cn, ue, logPath): + def Ping_common(self, ctx, cn, ue): ping_status = 0 ueIP = ue.getIP() if not ueIP: @@ -359,13 +359,12 @@ class OaiCiTest(): svrIP = cn.getIP() if not svrIP: return (False, f"CN {cn.getName()} has no IP address") - ping_log_file = f'ping_{self.testCase_id}_{ue.getName()}.log' + ping_log_file = f'/tmp/ping_{ue.getName()}.log' ping_time = re.findall(r"-c *(\d+)",str(self.ping_args)) - local_ping_log_file = f'{logPath}/{ping_log_file}' if re.search('%cn_ip%', self.ping_args) or re.search(r'[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', self.ping_args): raise Exception(f"ping_args should not have IP address: {self.ping_args}") interface = f'-I {ue.getIFName()}' if ue.getIFName() else '' - ping_cmd = f'{ue.getCmdPrefix()} ping {interface} {self.ping_args} {svrIP} 2>&1 | tee /tmp/{ping_log_file}' + ping_cmd = f'{ue.getCmdPrefix()} ping {interface} {self.ping_args} {svrIP} 2>&1 | tee {ping_log_file}' cmd = cls_cmd.getConnection(ue.getHost()) response = cmd.run(ping_cmd, timeout=int(ping_time[0])*1.5) ue_header = f'UE {ue.getName()} ({ueIP})' @@ -373,8 +372,7 @@ class OaiCiTest(): message = ue_header + ': ping crashed: TIMEOUT?' return (False, message) - #copy the ping log file to have it locally for analysis (ping stats) - cmd.copyin(src=f'/tmp/{ping_log_file}', tgt=local_ping_log_file) + local_ping_log_file = archiveArtifact(cmd, ctx, ping_log_file) cmd.close() with open(local_ping_log_file, 'r') as f: @@ -414,18 +412,13 @@ class OaiCiTest(): return (True, message) - def Ping(self, HTML, CONTAINERS, infra_file="ci_infra.yaml"): + def Ping(self, ctx, HTML, infra_file="ci_infra.yaml"): if self.ue_ids == [] or self.svr_id == None: raise Exception("no module names in self.ue_ids or/and self.svr_id provided") - # Creating destination log folder if needed on the python executor workspace - with cls_cmd.getConnection('localhost') as local: - ymlPath = CONTAINERS.yamlPath[0].split('/') - logPath = f'{os.getcwd()}/../cmake_targets/log/{ymlPath[-1]}' - local.run(f'mkdir -p {logPath}', silent=True) ues = [cls_module.Module_UE(ue_id, server_name, infra_file) for ue_id, server_name in zip(self.ue_ids, self.nodes)] cn = cls_corenetwork.CoreNetwork(self.svr_id, self.svr_node, filename=infra_file) with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: - futures = [executor.submit(self.Ping_common, cn, ue, logPath) for ue in ues] + futures = [executor.submit(self.Ping_common, ctx, cn, ue) for ue in ues] results = [f.result() for f in futures] # each result in results is a tuple, first member goes to successes, second to messages successes, messages = map(list, zip(*results)) @@ -446,7 +439,7 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue(self.ping_args, 'KO', messages) return success - def Iperf_Module(self, cn, ue, idx, ue_num, logPath): + def Iperf_Module(self, ctx, cn, ue, idx, ue_num): ueIP = ue.getIP() if not ueIP: return (False, f"UE {ue.getName()} has no IP address") @@ -459,7 +452,7 @@ class OaiCiTest(): serverReport = "" udpIperf = re.search('-u', iperf_opt) is not None bidirIperf = re.search('--bidir', iperf_opt) is not None - client_filename = f'iperf_client_{self.testCase_id}_{ue.getName()}.log' + client_filename = f'/tmp/iperf_client_{ue.getName()}.log' if udpIperf: target_bitrate, iperf_opt = Iperf_ComputeModifiedBW(idx, ue_num, self.iperf_profile, self.iperf_args) # note: for UDP testing we don't want to use json report - reports 0 Mbps received bitrate @@ -474,14 +467,11 @@ class OaiCiTest(): port = 5002 + idx # note: some core setups start an iperf3 server automatically, indicated in ci_infra by runIperf3Server: False` t = iperf_time * 2.5 - cmd_ue.run(f'rm /tmp/{client_filename}', reportNonZero=False, silent=True) + cmd_ue.run(f'rm {client_filename}', reportNonZero=False, silent=True) if cn.runIperf3Server(): cmd_svr.run(f'{cn.getCmdPrefix()} timeout -vk3 {t} iperf3 -s -B {svrIP} -p {port} -1 {jsonReport} >> /dev/null &', timeout=t) - cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -B {ueIP} -c {svrIP} -p {port} {iperf_opt} {jsonReport} {serverReport} -O 5 >> /tmp/{client_filename}', timeout=t) - # note: copy iperf3 log to the current directory for log analysis and log collection - dest_filename = f'{logPath}/{client_filename}' - cmd_ue.copyin(f'/tmp/{client_filename}', dest_filename) - cmd_ue.run(f'rm /tmp/{client_filename}', reportNonZero=False, silent=True) + cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -B {ueIP} -c {svrIP} -p {port} {iperf_opt} {jsonReport} {serverReport} -O 5 >> {client_filename}', timeout=t) + dest_filename = archiveArtifact(cmd_ue, ctx, client_filename) if udpIperf: status, msg = Iperf_analyzeV3UDP(dest_filename, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate) elif bidirIperf: @@ -491,20 +481,15 @@ class OaiCiTest(): return (status, f'{ue_header}\n{msg}') - def Iperf(self, HTML, CONTAINERS, infra_file="ci_infra.yaml"): + def Iperf(self, ctx, HTML, infra_file="ci_infra.yaml"): logging.debug(f'Iperf: iperf_args "{self.iperf_args}" iperf_packetloss_threshold "{self.iperf_packetloss_threshold}" iperf_bitrate_threshold "{self.iperf_bitrate_threshold}" iperf_profile "{self.iperf_profile}" iperf_options "{self.iperf_options}"') if self.ue_ids == [] or self.svr_id == None: raise Exception("no module names in self.ue_ids or/and self.svr_id provided") - # create log directory on executor node - with cls_cmd.getConnection('localhost') as local: - ymlPath = CONTAINERS.yamlPath[0].split('/') - logPath = f'{os.getcwd()}/../cmake_targets/log/{ymlPath[-1]}' - local.run(f'mkdir -p {logPath}', silent=True) ues = [cls_module.Module_UE(ue_id, server_name, infra_file) for ue_id, server_name in zip(self.ue_ids, self.nodes)] cn = cls_corenetwork.CoreNetwork(self.svr_id, self.svr_node, filename=infra_file) with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: - futures = [executor.submit(self.Iperf_Module, cn, ue, i, len(ues), logPath) for i, ue in enumerate(ues)] + futures = [executor.submit(self.Iperf_Module, ctx, cn, ue, i, len(ues)) for i, ue in enumerate(ues)] results = [f.result() for f in futures] # each result in results is a tuple, first member goes to successes, second to messages successes, messages = map(list, zip(*results)) @@ -525,7 +510,7 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', messages) return success - def Iperf2_Unidir(self, HTML, CONTAINERS, infra_file="ci_infra.yaml"): + def Iperf2_Unidir(self, ctx, HTML, infra_file="ci_infra.yaml"): if self.ue_ids == [] or self.svr_id == None or len(self.ue_ids) != 1: raise Exception("no module names in self.ue_ids or/and self.svr_id provided, multi UE scenario not supported") ue = cls_module.Module_UE(self.ue_ids[0].strip(),self.nodes[0].strip(), infra_file) @@ -536,23 +521,17 @@ class OaiCiTest(): svrIP = cn.getIP() if not svrIP: return False - server_filename = f'iperf_server_{self.testCase_id}_{ue.getName()}.log' - ymlPath = CONTAINERS.yamlPath[0].split('/') - logPath = f'{os.getcwd()}/../cmake_targets/log/{ymlPath[-1]}' + server_filename = f'/tmp/iperf_server_{ue.getName()}.log' iperf_time = Iperf_ComputeTime(self.iperf_args) target_bitrate, iperf_opt = Iperf_ComputeModifiedBW(0, 1, self.iperf_profile, self.iperf_args) t = iperf_time*2.5 - with cls_cmd.getConnection('localhost') as local: - local.run(f'mkdir -p {logPath}') with cls_cmd.getConnection(ue.getHost()) as cmd_ue, cls_cmd.getConnection(cn.getHost()) as cmd_svr: - cmd_ue.run(f'rm /tmp/{server_filename}', reportNonZero=False) - cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} iperf -B {ueIP} -s -u -i1 >> /tmp/{server_filename} &', timeout=t) + cmd_ue.run(f'rm {server_filename}', reportNonZero=False) + cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} iperf -B {ueIP} -s -u -i1 >> {server_filename} &', timeout=t) cmd_svr.run(f'{cn.getCmdPrefix()} timeout -vk3 {t} iperf -c {ueIP} -B {svrIP} {iperf_opt} -i1 >> /dev/null', timeout=t) localPath = f'{os.getcwd()}' - # note: copy iperf2 log to the directory for log collection - cmd_ue.copyin(f'/tmp/{server_filename}', f'{logPath}/{server_filename}') - cmd_ue.run(f'rm /tmp/{server_filename}', reportNonZero=False) - success, msg = Iperf_analyzeV2UDP(f'{logPath}/{server_filename}', self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate) + local = archiveArtifact(cmd_ue, ctx, server_filename) + success, msg = Iperf_analyzeV2UDP(local, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate) ue_header = f'UE {ue.getName()} ({ueIP})' logging.info(f'\u001B[1;37;45m iperf result for {ue_header}\u001B[0m') for l in msg.split('\n'): diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 509cd19109..7ec30aa477 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -255,7 +255,7 @@ def ExecuteActionWithParam(action, ctx): else: CiTestObj.nodes = [None] * len(CiTestObj.ue_ids) ping_rttavg_threshold = test.findtext('ping_rttavg_threshold') or '' - success = CiTestObj.Ping(HTML, CONTAINERS) + success = CiTestObj.Ping(ctx, HTML) elif action == 'Iperf' or action == 'Iperf2_Unidir': CiTestObj.iperf_args = test.findtext('iperf_args') @@ -286,9 +286,9 @@ def ExecuteActionWithParam(action, ctx): logging.error('test-case has wrong option ' + CiTestObj.iperf_options) CiTestObj.iperf_options = 'check' if action == 'Iperf': - success = CiTestObj.Iperf(HTML, CONTAINERS) + success = CiTestObj.Iperf(ctx, HTML) elif action == 'Iperf2_Unidir': - success = CiTestObj.Iperf2_Unidir(HTML, CONTAINERS) + success = CiTestObj.Iperf2_Unidir(ctx, HTML) elif action == 'IdleSleep': st = test.findtext('idle_sleep_time_in_sec') or "5" diff --git a/ci-scripts/tests/ping-iperf.py b/ci-scripts/tests/ping-iperf.py index 225fe4e868..9d50567a07 100644 --- a/ci-scripts/tests/ping-iperf.py +++ b/ci-scripts/tests/ping-iperf.py @@ -9,11 +9,13 @@ import os os.system(f'rm -rf cmake_targets') os.system(f'mkdir -p cmake_targets/log') import unittest +import tempfile sys.path.append('./') # to find OAI imports below import cls_oai_html +from cls_ci_helper import TestCaseCtx import cls_oaicitest -import cls_containerize +import cls_cmd class TestPingIperf(unittest.TestCase): def setUp(self): @@ -22,7 +24,10 @@ class TestPingIperf(unittest.TestCase): self.ci = cls_oaicitest.OaiCiTest() self.ci.ue_ids = ["test"] self.ci.nodes = ["localhost"] - self.cont = cls_containerize.Containerize() + self.ctx = TestCaseCtx.Default(tempfile.mkdtemp()) + def tearDown(self): + with cls_cmd.LocalCmd() as c: + c.run(f'rm -rf {self.ctx.logPath}') def test_ping(self): self.ci.ping_args = "-c3" @@ -30,7 +35,7 @@ class TestPingIperf(unittest.TestCase): self.ci.svr_id = "test" infra_file = "tests/config/infra_ping_iperf.yaml" # TODO Should need nothing but options and UE(s) to use - success = self.ci.Ping(self.html, self.cont, infra_file=infra_file) + success = self.ci.Ping(self.ctx, self.html, infra_file=infra_file) self.assertTrue(success) def test_iperf(self): @@ -45,7 +50,7 @@ class TestPingIperf(unittest.TestCase): self.ci.iperf_profile = "balanced" infra_file = "tests/config/infra_ping_iperf.yaml" # TODO Should need nothing but options and UE(s) to use - success = self.ci.Iperf(self.html, self.cont, infra_file=infra_file) + success = self.ci.Iperf(self.ctx, self.html, infra_file=infra_file) self.assertTrue(success) def test_iperf2_unidir(self): @@ -57,7 +62,7 @@ class TestPingIperf(unittest.TestCase): self.ci.iperf_profile = "balanced" infra_file = "tests/config/infra_ping_iperf.yaml" # TODO Should need nothing but options and UE(s) to use - success = self.ci.Iperf2_Unidir(self.html, self.cont, infra_file=infra_file) + success = self.ci.Iperf2_Unidir(self.ctx, self.html, infra_file=infra_file) self.assertTrue(success) if __name__ == '__main__': From 1d3e795c0cbe5e1fca282a5b6a05028fd22a0c92 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 09:40:42 +0200 Subject: [PATCH 68/86] Use archiveArtifact() for deploy_oc_physim() --- ci-scripts/cls_analysis.py | 4 ++-- ci-scripts/cls_cluster.py | 18 +++++++----------- ci-scripts/main.py | 2 +- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/ci-scripts/cls_analysis.py b/ci-scripts/cls_analysis.py index ac591b52fb..236a4743a1 100644 --- a/ci-scripts/cls_analysis.py +++ b/ci-scripts/cls_analysis.py @@ -84,7 +84,7 @@ class Analysis(): return success,msg - def analyze_oc_physim(result_junit, details_json): + def analyze_oc_physim(result_junit, details_json, logPath): try: tree = ET.parse(result_junit) root = tree.getroot() @@ -124,7 +124,7 @@ class Analysis(): output = test.findtext("system-out") output_check = "exceeds the threshold" not in output # collect logs - log_dir = f'../cmake_targets/log/{test_exec}' + log_dir = f'{logPath}/{test_exec}' os.makedirs(log_dir, exist_ok=True) with open(f'{log_dir}/{test_name}.log', 'w') as f: f.write(output) diff --git a/ci-scripts/cls_cluster.py b/ci-scripts/cls_cluster.py index 7e88c6b07b..7d41e9d9cc 100644 --- a/ci-scripts/cls_cluster.py +++ b/ci-scripts/cls_cluster.py @@ -460,7 +460,7 @@ class Cluster: return status - def deploy_oc_physim(self, HTML, oc_release, svr_id): + def deploy_oc_physim(self, ctx, HTML, oc_release, svr_id): if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '': HELP.GenericHelp(CONST.Version) raise ValueError(f'Insufficient Parameter: ranRepository {self.ranRepository} ranBranch {self.ranBranch} ranCommitID {self.ranCommitID}') @@ -470,17 +470,13 @@ class Cluster: options = f"oaicicd-core-for-ci-ran {oc_release} {image_tag} {self.eNBSourceCodePath}" ret = cls_cmd.runScript(svr_id, script, 600, options) logging.debug(f'"{script}" finished with code {ret.returncode}, output:\n{ret.stdout}') - log_dir = f'{os.getcwd()}/../cmake_targets/log' - os.makedirs(log_dir, exist_ok=True) - result_junit = f'{oc_release}-run.xml' - details_json = f'{oc_release}-tests.json' with cls_cmd.getConnection(svr_id) as ssh: - ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/{details_json}', tgt=f'{log_dir}/{details_json}') - ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/{result_junit}', tgt=f'{log_dir}/{result_junit}') - ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/physim_log.txt', tgt=f'{log_dir}/physim_log.txt') - ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/physim_pods_summary.txt', tgt=f'{log_dir}/physim_pods_summary.txt') - ssh.copyin(src=f'{self.eNBSourceCodePath}/ci-scripts/LastTestsFailed.log', tgt=f'{log_dir}/LastTestsFailed.log') - test_status, test_summary, test_result = cls_analysis.Analysis.analyze_oc_physim(f'{log_dir}/{result_junit}', f'{log_dir}/{details_json}') + details_json = archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/{oc_release}-tests.json') + result_junit = archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/{oc_release}-run.xml') + archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/physim_log.txt') + archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/physim_pods_summary.txt') + archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/LastTestsFailed.log') + test_status, test_summary, test_result = cls_analysis.Analysis.analyze_oc_physim(result_junit, details_json, ctx.logPath) if test_summary: if test_status: HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 7ec30aa477..035437ac8a 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -297,7 +297,7 @@ def ExecuteActionWithParam(action, ctx): elif action == 'Deploy_Run_PhySim': oc_release = test.findtext('oc_release') svr_id = test.findtext('svr_id') or None - success = CLUSTER.deploy_oc_physim(HTML, oc_release, svr_id) + success = CLUSTER.deploy_oc_physim(ctx, HTML, oc_release, svr_id) elif action == 'DeployCoreNetwork' or action == 'UndeployCoreNetwork': cn_id = test.findtext('cn_id') From 7ef8829167a342466342bc5a67a09830e310e989 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 09:41:01 +0200 Subject: [PATCH 69/86] Use archiveArtifact() for cls_native.Build() --- ci-scripts/cls_native.py | 13 +++++-------- ci-scripts/main.py | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/ci-scripts/cls_native.py b/ci-scripts/cls_native.py index fe508b84e3..86268a7dc1 100644 --- a/ci-scripts/cls_native.py +++ b/ci-scripts/cls_native.py @@ -28,27 +28,24 @@ import cls_cmd import cls_oai_html import cls_analysis import constants as CONST +from cls_ci_helper import archiveArtifact -LOG_PATH_PHYSIM = 'phy_sim_logs' DPDK_PATH = '/opt/dpdk-t2-22.11.0' class Native(): - def Build(test_case, HTML, host, directory, options): + def Build(ctx, test_case, HTML, host, directory, options): logging.debug(f'Building on server: {host}') HTML.testCase_id = test_case with cls_cmd.getConnection(host) as ssh: base = f"{directory}/cmake_targets" - ret = ssh.run(f"C_INCLUDE_PATH={DPDK_PATH}/include/ PKG_CONFIG_PATH={DPDK_PATH}/lib64/pkgconfig/ {base}/build_oai {options} > {base}/log/build_oai.log", timeout=900) + ret = ssh.run(f"C_INCLUDE_PATH={DPDK_PATH}/include/ PKG_CONFIG_PATH={DPDK_PATH}/lib64/pkgconfig/ {base}/build_oai {options} > {base}/build_oai.log", timeout=900) success = ret.returncode == 0 - logs = ssh.run(f"cat {base}/log/build_oai.log", silent=True) + logs = ssh.run(f"cat {base}/build_oai.log", silent=True) logging.debug(f"build finished with code {ret.returncode}, output:\n{logs.stdout}") - # create log directory, and copy build logs - target = f"{base}/build_log_{test_case}/" - ssh.run(f"mkdir -p {target}") - ssh.run(f"mv {base}/log/* {target}") + archiveArtifact(ssh, ctx, f'{base}/build_oai.log') # check if build artifacts are there # NOTE: build_oai should fail with exit code if it could not build, but it does not diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 035437ac8a..317b32b0f9 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -136,7 +136,7 @@ def ExecuteActionWithParam(action, ctx): if proxy_commit is not None: CONTAINERS.proxyCommit = proxy_commit if action == 'Build_eNB': - success = cls_native.Native.Build(HTML.testCase_id, HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, RAN.Build_eNB_args) + success = cls_native.Native.Build(ctx, HTML.testCase_id, HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, RAN.Build_eNB_args) elif action == 'Build_Image': success = CONTAINERS.BuildImage(HTML) elif action == 'Build_Proxy': From 83d037057bbfb0828cc4d5b870dfeab3af1d2b83 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 09:41:08 +0200 Subject: [PATCH 70/86] Use archiveArtifact() for cls_native.run_physim() --- ci-scripts/cls_native.py | 11 +++++------ ci-scripts/main.py | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/ci-scripts/cls_native.py b/ci-scripts/cls_native.py index 86268a7dc1..3dde286c3a 100644 --- a/ci-scripts/cls_native.py +++ b/ci-scripts/cls_native.py @@ -65,15 +65,14 @@ class Native(): HTML.CreateHtmlTestRow(options, 'KO', CONST.ALL_PROCESSES_OK) return success - def Run_Physim(HTML, host, directory, options, physim_test, threshold): + def Run_Physim(ctx, HTML, host, directory, options, physim_test, threshold): logging.debug(f'Runnin {physim_test} on server: {host}') workSpacePath = f'{directory}/cmake_targets' - os.system(f'mkdir -p ./{LOG_PATH_PHYSIM}') - runLogFile=f'physim_{HTML.testCase_id}.log' + runLogFile=f'{workSpacePath}/physim.log' with cls_cmd.getConnection(host) as cmd: - cmd.run(f'sudo LD_LIBRARY_PATH=.:{DPDK_PATH}/lib64/ {workSpacePath}/ran_build/build/{physim_test} {options} >> {workSpacePath}/{runLogFile}') - cmd.copyin(src=f'{workSpacePath}/{runLogFile}', tgt=f'{LOG_PATH_PHYSIM}/{runLogFile}') - success, msg = cls_analysis.Analysis.analyze_physim(f'{LOG_PATH_PHYSIM}/{runLogFile}', physim_test, options, threshold) + cmd.run(f'sudo LD_LIBRARY_PATH=.:{DPDK_PATH}/lib64/ {workSpacePath}/ran_build/build/{physim_test} {options} >> {runLogFile}') + physim_file = archiveArtifact(cmd, ctx, runLogFile) + success, msg = cls_analysis.Analysis.analyze_physim(physim_file, physim_test, options, threshold) if success: HTML.CreateHtmlTestRowQueue(options, 'OK', [msg]) else: diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 317b32b0f9..34f0946de0 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -343,7 +343,7 @@ def ExecuteActionWithParam(action, ctx): physim_options = test.findtext('physim_run_args') physim_test = test.findtext('physim_test') physim_threshold = test.findtext('physim_time_threshold') or 'inf' - success = cls_native.Native.Run_Physim(HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, physim_options, physim_test, physim_threshold) + success = cls_native.Native.Run_Physim(ctx, HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, physim_options, physim_test, physim_threshold) elif action == 'LicenceAndFormattingCheck': success = SCA.LicenceAndFormattingCheck(HTML) From 1ffbba525de7c3c77fd0451659752d3268bf20d8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 17:41:56 +0200 Subject: [PATCH 71/86] Use archiveArtifact() for BuildRunTests() Collect also unit test logs, so that developers could see the reason for failed logs. --- ci-scripts/cls_containerize.py | 22 +++++++++++++--------- ci-scripts/main.py | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 591d6d413d..4b84b9ab86 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -636,7 +636,7 @@ class Containerize(): HTML.CreateHtmlTabFooter(False) return False - def BuildRunTests(self, HTML): + def BuildRunTests(self, ctx, HTML): svr = self.eNB_serverId[self.eNB_instance] lIpAddr, lSourcePath = self.GetCredentials(svr) logging.debug('Building on server: ' + lIpAddr) @@ -653,7 +653,6 @@ class Containerize(): if self.forcedWorkspaceCleanup: cmd.run(f'sudo -S rm -Rf {lSourcePath}') - self.testCase_id = HTML.testCase_id # check that ran-base image exists as we expect it baseImage = 'ran-base' @@ -673,10 +672,10 @@ class Containerize(): # build ran-unittests image dockerfile = "ci-scripts/docker/Dockerfile.unittest.ubuntu22" - ret = cmd.run(f'docker build --progress=plain --tag ran-unittests:{baseTag} --file {dockerfile} . &> {lSourcePath}/cmake_targets/log/unittest-build.log') + logfile = f'{lSourcePath}/cmake_targets/log/unittest-build.log' + ret = cmd.run(f'docker build --progress=plain --tag ran-unittests:{baseTag} --file {dockerfile} . &> {logfile}') + archiveArtifact(cmd, ctx, logfile) if ret.returncode != 0: - build_log_name = f'build_log_{self.testCase_id}' - CopyLogsToExecutor(cmd, lSourcePath, build_log_name) logging.error(f'Cannot build unit tests') HTML.CreateHtmlTestRow("Unit test build failed", 'KO', [dockerfile]) HTML.CreateHtmlTabFooter(False) @@ -685,10 +684,15 @@ class Containerize(): HTML.CreateHtmlTestRowQueue("Build unit tests", 'OK', [dockerfile]) # it worked, build and execute tests, and close connection - ret = cmd.run(f'docker run -a STDOUT --workdir /oai-ran/build/ --env LD_LIBRARY_PATH=/oai-ran/build/ --rm ran-unittests:{baseTag} ctest --no-label-summary -j$(nproc)') - cmd.run(f'docker rmi ran-unittests:{baseTag}') - build_log_name = f'build_log_{self.testCase_id}' - CopyLogsToExecutor(cmd, lSourcePath, build_log_name) + # I would like to run it with --rm and mount the ctest result directory to avoid 'docker cp' + # below, but then permissions are messed up and we can't remove the directory without sudo + # making the next pipeline fail + ret = cmd.run(f'docker run -a STDOUT --workdir /oai-ran/build/ --env LD_LIBRARY_PATH=/oai-ran/build/ --name ran-unittests ran-unittests:{baseTag} ctest --no-label-summary -j$(nproc)') + cmd.run('docker cp ran-unittests:/oai-ran/build/Testing/Temporary/LastTest.log .') + archiveArtifact(cmd, ctx, f'{lSourcePath}/LastTest.log') + cmd.run('docker cp ran-unittests:/oai-ran/build/Testing/Temporary/LastTestsFailed.log .') + archiveArtifact(cmd, ctx, f'{lSourcePath}/LastTestsFailed.log') + cmd.run('docker rm ran-unittests') cmd.close() if ret.returncode == 0: diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 34f0946de0..4a8e63f8cb 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -144,7 +144,7 @@ def ExecuteActionWithParam(action, ctx): elif action == 'Build_Cluster_Image': success = CLUSTER.BuildClusterImage(HTML) elif action == 'Build_Run_Tests': - success = CONTAINERS.BuildRunTests(HTML) + success = CONTAINERS.BuildRunTests(ctx, HTML) elif action == 'Initialize_eNB': datalog_rt_stats_file=test.findtext('rt_stats_cfg') From 63f6368ca7c0c363f8dcd0329bdf16fa33908c67 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 17:41:40 +0200 Subject: [PATCH 72/86] use archiveArtifact() for BuildProxy() --- ci-scripts/cls_containerize.py | 11 +++-------- ci-scripts/main.py | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 4b84b9ab86..381e955127 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -534,7 +534,7 @@ class Containerize(): HTML.CreateHtmlTabFooter(False) return False - def BuildProxy(self, HTML): + def BuildProxy(self, ctx, HTML): svr = self.eNB_serverId[self.eNB_instance] lIpAddr, lSourcePath = self.GetCredentials(svr) logging.debug('Building on server: ' + lIpAddr) @@ -566,15 +566,10 @@ class Containerize(): if not success: raise Exception("could not clone proxy repository") - filename = f'build_log_{self.testCase_id}' - fullpath = f'{lSourcePath}/{filename}' + fullpath = f'{lSourcePath}/proxy_build.log' ssh.run(f'docker build --target oai-lte-multi-ue-proxy --tag proxy:{tag} --file {lSourcePath}/docker/Dockerfile.ubuntu18.04 {lSourcePath} > {fullpath} 2>&1') - ssh.run(f'zip -r -qq {fullpath}.zip {fullpath}') - local_file = f"{os.getcwd()}/../cmake_targets/log/{filename}.zip" - ssh.copyin(f'{fullpath}.zip', local_file) - # don't delete such that we might recover the zips - #ssh.run(f'rm -f {fullpath}.zip') + archiveArtifact(ssh, ctx, fullpath) ssh.run('docker image prune --force') ret = ssh.run(f'docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' proxy:{tag}') diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 4a8e63f8cb..f201bdbcc6 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -140,7 +140,7 @@ def ExecuteActionWithParam(action, ctx): elif action == 'Build_Image': success = CONTAINERS.BuildImage(HTML) elif action == 'Build_Proxy': - success = CONTAINERS.BuildProxy(HTML) + success = CONTAINERS.BuildProxy(ctx, HTML) elif action == 'Build_Cluster_Image': success = CLUSTER.BuildClusterImage(HTML) elif action == 'Build_Run_Tests': From 5ffa4315141d8dc689b7ceddcdc09d81909f09c2 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 17:41:34 +0200 Subject: [PATCH 73/86] Use archiveArtifact() for Build()/BuildCluster() I have to make both together, since they both depend on AnalyzeBuildLogs(). - remove mkdir for legacy sub-logfiles in build_oai, that does not exist anymore; also, remove corresponding log analysis, I think we don't use it and it is overkill (either build works or not) - manually try to copy "all.txt" - explicitly list log files to copy, then analyze - AnalyzeBuildLogs(): pass log files to inspect explicitly --- ci-scripts/cls_cluster.py | 91 ++++++++++++++------------ ci-scripts/cls_containerize.py | 116 +++++++++++---------------------- ci-scripts/main.py | 4 +- 3 files changed, 91 insertions(+), 120 deletions(-) diff --git a/ci-scripts/cls_cluster.py b/ci-scripts/cls_cluster.py index 7d41e9d9cc..185e90a13e 100644 --- a/ci-scripts/cls_cluster.py +++ b/ci-scripts/cls_cluster.py @@ -39,6 +39,7 @@ import constants as CONST import helpreadme as HELP import cls_containerize import cls_cmd +from cls_ci_helper import archiveArtifact IMAGE_REGISTRY_SERVICE_NAME = "image-registry.openshift-image-registry.svc" NAMESPACE = "oaicicd-ran" @@ -207,7 +208,12 @@ class Cluster: HTML.CreateHtmlTestRowQueue(param, 'KO', [msg]) return success - def BuildClusterImage(self, HTML): + def _retrieveOCLog(self, ctx, job, lSourcePath, image): + fn = f'{lSourcePath}/cmake_targets/log/{image}.log' + self.cmd.run(f'oc logs {job} &> {fn}') + return (image, archiveArtifact(self.cmd, ctx, fn)) + + def BuildClusterImage(self, ctx, HTML): if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '': HELP.GenericHelp(CONST.Version) raise ValueError(f'Insufficient Parameter: ranRepository {self.ranRepository} ranBranch {ranBranch} ranCommitID {self.ranCommitID}') @@ -280,22 +286,27 @@ class Cluster: self._recreate_entitlements() status = True # flag to abandon compiling if any image fails - attemptedImages = [] + log_files = [] + build_metrics = f"{lSourcePath}/cmake_targets/log/build-metrics.log" if forceBaseImageBuild: self._recreate_is_tag('ran-base', baseTag, 'openshift/ran-base-is.yaml') self._recreate_bc('ran-base', baseTag, 'openshift/ran-base-bc.yaml') ranbase_job = self._start_build('ran-base') - attemptedImages += ['ran-base'] status = ranbase_job is not None and self._wait_build_end([ranbase_job], 1000) if not status: logging.error('failure during build of ran-base') - self.cmd.run(f'oc logs {ranbase_job} &> cmake_targets/log/ran-base.log') # cannot use cmd.run because of redirect + log_files.append(self._retrieveOCLog(ctx, ranbase_job, lSourcePath, 'ran-base')) + # recover logs by mounting image self._retag_image_statement('ran-base', 'ran-base', baseTag, 'openshift/ran-base-log-retrieval.yaml') pod = self._deploy_pod('openshift/ran-base-log-retrieval.yaml') if pod is not None: - self.cmd.run(f'mkdir -p cmake_targets/log/ran-base') - self.cmd.run(f'oc rsync {pod}:/oai-ran/cmake_targets/log/ cmake_targets/log/ran-base') + logdir = f'{lSourcePath}/cmake_targets/log/ran-base' + self.cmd.run(f'mkdir -p {logdir}') + self.cmd.run(f'oc rsync {pod}:/oai-ran/cmake_targets/log/ {logdir}') self._undeploy_pod('openshift/ran-base-log-retrieval.yaml') + ret = self.cmd.run(f'ls {logdir}') + for f in ret.stdout.splitlines(): + archiveArtifact(self.cmd, ctx, f"{logdir}/{f}") else: status = False @@ -304,27 +315,24 @@ class Cluster: self._recreate_bc('oai-physim', imageTag, 'openshift/oai-physim-bc.yaml') self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.phySim.rhel9') physim_job = self._start_build('oai-physim') - attemptedImages += ['oai-physim'] self._recreate_is_tag('ran-build', imageTag, 'openshift/ran-build-is.yaml') self._recreate_bc('ran-build', imageTag, 'openshift/ran-build-bc.yaml') self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.build.rhel9') ranbuild_job = self._start_build('ran-build') - attemptedImages += ['ran-build'] self._recreate_is_tag('oai-clang', imageTag, 'openshift/oai-clang-is.yaml') self._recreate_bc('oai-clang', imageTag, 'openshift/oai-clang-bc.yaml') self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.clang.rhel9') clang_job = self._start_build('oai-clang') - attemptedImages += ['oai-clang'] wait = ranbuild_job is not None and physim_job is not None and clang_job is not None and self._wait_build_end([ranbuild_job, physim_job, clang_job], 1200) if not wait: logging.error('error during build of ranbuild_job or physim_job or clang_job') status = status and wait - self.cmd.run(f'oc logs {ranbuild_job} &> cmake_targets/log/ran-build.log') - self.cmd.run(f'oc logs {physim_job} &> cmake_targets/log/oai-physim.log') - self.cmd.run(f'oc logs {clang_job} &> cmake_targets/log/oai-clang.log') - self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log') + log_files.append(self._retrieveOCLog(ctx, ranbuild_job, lSourcePath, 'ran-build')) + log_files.append(self._retrieveOCLog(ctx, physim_job, lSourcePath, 'oai-physim')) + log_files.append(self._retrieveOCLog(ctx, clang_job, lSourcePath, 'oai-clang')) + self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}') if status: self._recreate_is_tag('oai-enb', imageTag, 'openshift/oai-enb-is.yaml') @@ -332,72 +340,66 @@ class Cluster: self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.eNB.rhel9') self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.eNB.rhel9') enb_job = self._start_build('oai-enb') - attemptedImages += ['oai-enb'] self._recreate_is_tag('oai-gnb', imageTag, 'openshift/oai-gnb-is.yaml') self._recreate_bc('oai-gnb', imageTag, 'openshift/oai-gnb-bc.yaml') self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.rhel9') self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.gNB.rhel9') gnb_job = self._start_build('oai-gnb') - attemptedImages += ['oai-gnb'] self._recreate_is_tag('oai-gnb-aw2s', imageTag, 'openshift/oai-gnb-aw2s-is.yaml') self._recreate_bc('oai-gnb-aw2s', imageTag, 'openshift/oai-gnb-aw2s-bc.yaml') self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.aw2s.rhel9') self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.gNB.aw2s.rhel9') gnb_aw2s_job = self._start_build('oai-gnb-aw2s') - attemptedImages += ['oai-gnb-aw2s'] wait = enb_job is not None and gnb_job is not None and gnb_aw2s_job is not None and self._wait_build_end([enb_job, gnb_job, gnb_aw2s_job], 800) if not wait: logging.error('error during build of eNB/gNB') status = status and wait # recover logs - self.cmd.run(f'oc logs {enb_job} &> cmake_targets/log/oai-enb.log') - self.cmd.run(f'oc logs {gnb_job} &> cmake_targets/log/oai-gnb.log') - self.cmd.run(f'oc logs {gnb_aw2s_job} &> cmake_targets/log/oai-gnb-aw2s.log') + log_files.append(self._retrieveOCLog(ctx, enb_job, lSourcePath, 'oai-enb')) + log_files.append(self._retrieveOCLog(ctx, gnb_job, lSourcePath, 'oai-gnb')) + log_files.append(self._retrieveOCLog(ctx, gnb_aw2s_job, lSourcePath, 'oai-gnb-aw2s')) + self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}') self._recreate_is_tag('oai-nr-cuup', imageTag, 'openshift/oai-nr-cuup-is.yaml') self._recreate_bc('oai-nr-cuup', imageTag, 'openshift/oai-nr-cuup-bc.yaml') self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.nr-cuup.rhel9') self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.nr-cuup.rhel9') nr_cuup_job = self._start_build('oai-nr-cuup') - attemptedImages += ['oai-nr-cuup'] self._recreate_is_tag('oai-lte-ue', imageTag, 'openshift/oai-lte-ue-is.yaml') self._recreate_bc('oai-lte-ue', imageTag, 'openshift/oai-lte-ue-bc.yaml') self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.lteUE.rhel9') self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.lteUE.rhel9') lteue_job = self._start_build('oai-lte-ue') - attemptedImages += ['oai-lte-ue'] self._recreate_is_tag('oai-nr-ue', imageTag, 'openshift/oai-nr-ue-is.yaml') self._recreate_bc('oai-nr-ue', imageTag, 'openshift/oai-nr-ue-bc.yaml') self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.nrUE.rhel9') self._retag_image_statement('ran-build', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build', imageTag, 'docker/Dockerfile.nrUE.rhel9') nrue_job = self._start_build('oai-nr-ue') - attemptedImages += ['oai-nr-ue'] wait = nr_cuup_job is not None and lteue_job is not None and nrue_job is not None and self._wait_build_end([nr_cuup_job, lteue_job, nrue_job], 800) if not wait: logging.error('error during build of nr-cuup/lteUE/nrUE') status = status and wait # recover logs - self.cmd.run(f'oc logs {nr_cuup_job} &> cmake_targets/log/oai-nr-cuup.log') - self.cmd.run(f'oc logs {lteue_job} &> cmake_targets/log/oai-lte-ue.log') - self.cmd.run(f'oc logs {nrue_job} &> cmake_targets/log/oai-nr-ue.log') - self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log') + log_files.append(self._retrieveOCLog(ctx, nr_cuup_job, lSourcePath, 'oai-nr-cuup')) + log_files.append(self._retrieveOCLog(ctx, lteue_job, lSourcePath, 'oai-lte-ue')) + log_files.append(self._retrieveOCLog(ctx, nrue_job, lSourcePath, 'oai-nr-ue')) + self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}') if status: self._recreate_is_tag('ran-build-fhi72', imageTag, 'openshift/ran-build-fhi72-is.yaml') self._recreate_bc('ran-build-fhi72', imageTag, 'openshift/ran-build-fhi72-bc.yaml') self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.build.fhi72.rhel9') ranbuildfhi72_job = self._start_build('ran-build-fhi72') - attemptedImages += ['ran-build-fhi72'] wait = ranbuildfhi72_job is not None and self._wait_build_end([ranbuildfhi72_job], 1200) if not wait: logging.error('error during build of ranbuildfhi72_job') status = status and wait - self.cmd.run(f'oc logs {ranbuildfhi72_job} &> cmake_targets/log/ran-build-fhi72.log') - self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log') + log_files.append(self._retrieveOCLog(ctx, ranbuildfhi72_job, lSourcePath, 'ran-build-fhi72')) + self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}') if status: self._recreate_is_tag('oai-gnb-fhi72', imageTag, 'openshift/oai-gnb-fhi72-is.yaml') @@ -405,19 +407,17 @@ class Cluster: self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.fhi72.rhel9') self._retag_image_statement('ran-build-fhi72', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build-fhi72', imageTag, 'docker/Dockerfile.gNB.fhi72.rhel9') gnb_fhi72_job = self._start_build('oai-gnb-fhi72') - attemptedImages += ['oai-gnb-fhi72'] wait = gnb_fhi72_job is not None and self._wait_build_end([gnb_fhi72_job], 600) if not wait: logging.error('error during build of gNB-fhi72') status = status and wait # recover logs - self.cmd.run(f'oc logs {gnb_fhi72_job} &> cmake_targets/log/oai-gnb-fhi72.log') - self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log') + log_files.append(self._retrieveOCLog(ctx, gnb_fhi72_job, lSourcePath, 'oai-gnb-fhi72')) + self.cmd.run(f'oc get pods.metrics.k8s.io &>> {build_metrics}') # split and analyze logs imageSize = {} - for image in attemptedImages: - self.cmd.run(f'mkdir -p cmake_targets/log/{image}') + for image, _ in log_files: tag = imageTag if image != 'ran-base' else baseTag size = self._get_image_size(image, tag) if size <= 0: @@ -428,12 +428,14 @@ class Cluster: imageSize[image] = f'{sizeMb:.1f} Mbytes (uncompressed: ~{sizeMb*2.5:.1f} Mbytes)' logging.info(f'\u001B[1m{image} size is {imageSize[image]}\u001B[0m') - grep_exp = r"\|".join(attemptedImages) - self.cmd.run(f'oc get images | grep -e \'{grep_exp}\' &> cmake_targets/log/image_registry.log'); - self.cmd.run(f'for pod in $(oc get pods | tail -n +2 | awk \'{{print $1}}\'); do oc get pod $pod -o json &>> cmake_targets/log/build_pod_summary.log; done') - - build_log_name = f'build_log_{self.testCase_id}' - cls_containerize.CopyLogsToExecutor(self.cmd, lSourcePath, build_log_name) + archiveArtifact(self.cmd, ctx, build_metrics) + logfile = f'{lSourcePath}/cmake_targets/log/image_registry.log' + grep_exp = r"\|".join([i for i,f in log_files]) + self.cmd.run(f'oc get images | grep -e \'{grep_exp}\' &> {logfile}'); + archiveArtifact(self.cmd, ctx, logfile) + logfile = f'{lSourcePath}/cmake_targets/log/build_pod_summary.log' + self.cmd.run(f'for pod in $(oc get pods | tail -n +2 | awk \'{{print $1}}\'); do oc get pod $pod -o json &>> {logfile}; done') + archiveArtifact(self.cmd, ctx, logfile) self.cmd.run('for pod in $(oc get pods | tail -n +2 | awk \'{print $1}\'); do oc delete pod ${pod}; done') @@ -442,7 +444,7 @@ class Cluster: self.cmd.close() # Analyze the logs - collectInfo = cls_containerize.AnalyzeBuildLogs(build_log_name, attemptedImages, status) + collectInfo = cls_containerize.AnalyzeBuildLogs(log_files, status) for img in collectInfo: for f in collectInfo[img]: status = status and collectInfo[img][f]['status'] @@ -458,6 +460,13 @@ class Cluster: HTML.CreateHtmlNextTabHeaderTestRow(collectInfo, imageSize) + # TODO fix groovy script, remove the following. + # the groovy scripts expects all logs in + # //ci-scripts, so copy it there + with cls_cmd.LocalCmd() as c: + c.run(f'mkdir -p {os.getcwd()}/test_log_{ctx.test_id}/') + c.run(f'cp -r {ctx.logPath} {os.getcwd()}/test_log_{ctx.test_id}/') + return status def deploy_oc_physim(self, ctx, HTML, oc_release, svr_id): diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 381e955127..3af34dca14 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -82,67 +82,25 @@ def CreateTag(ranCommitID, ranBranch, ranAllowMerge): tagToUse = f'develop-{shortCommit}' return tagToUse -def CopyLogsToExecutor(cmd, sourcePath, log_name): - cmd.cd(f'{sourcePath}/cmake_targets') - cmd.run(f'rm -f {log_name}.zip') - cmd.run(f'mkdir -p {log_name}') - cmd.run(f'mv log/* {log_name}') - cmd.run(f'zip -r -qq {log_name}.zip {log_name}') - - # copy zip to executor for analysis - if (os.path.isfile(f'./{log_name}.zip')): - os.remove(f'./{log_name}.zip') - if (os.path.isdir(f'./{log_name}')): - shutil.rmtree(f'./{log_name}') - cmd.copyin(src=f'{sourcePath}/cmake_targets/{log_name}.zip', tgt=f'{os.getcwd()}/{log_name}.zip') - cmd.run(f'rm -f {log_name}.zip') - ZipFile(f'{log_name}.zip').extractall('.') - -def AnalyzeBuildLogs(buildRoot, images, globalStatus): +def AnalyzeBuildLogs(logfiles, globalStatus): collectInfo = {} - for image in images: + for image, lf in logfiles: files = {} - file_list = [f for f in os.listdir(f'{buildRoot}/{image}') if os.path.isfile(os.path.join(f'{buildRoot}/{image}', f)) and f.endswith('.txt')] - # Analyze the "sub-logs" of every target image - for fil in file_list: - errorandwarnings = {} - warningsNo = 0 - errorsNo = 0 - with open(f'{buildRoot}/{image}/{fil}', mode='r') as inputfile: - for line in inputfile: - result = re.search(' ERROR ', str(line)) - if result is not None: - errorsNo += 1 - result = re.search(' error:', str(line)) - if result is not None: - errorsNo += 1 - result = re.search(' WARNING ', str(line)) - if result is not None: - warningsNo += 1 - result = re.search(' warning:', str(line)) - if result is not None: - warningsNo += 1 - errorandwarnings['errors'] = errorsNo - errorandwarnings['warnings'] = warningsNo - errorandwarnings['status'] = globalStatus - files[fil] = errorandwarnings - # Analyze the target image - if os.path.isfile(f'{buildRoot}/{image}.log'): - errorandwarnings = {} - committed = False - tagged = False - with open(f'{buildRoot}/{image}.log', mode='r') as inputfile: - for line in inputfile: - lineHasTag = re.search(f'Successfully tagged {image}:', str(line)) is not None - lineHasTag2 = re.search(f'naming to docker.io/library/{image}:', str(line)) is not None - tagged = tagged or lineHasTag or lineHasTag2 - # the OpenShift Cluster builder prepends image registry URL - lineHasCommit = re.search(r'COMMIT [a-zA-Z0-9\.:/\-]*' + image, str(line)) is not None - committed = committed or lineHasCommit - errorandwarnings['errors'] = 0 if committed or tagged else 1 - errorandwarnings['warnings'] = 0 - errorandwarnings['status'] = committed or tagged - files['Target Image Creation'] = errorandwarnings + errorandwarnings = {} + committed = False + tagged = False + with open(lf, mode='r') as inputfile: + for line in inputfile: + lineHasTag = re.search(f'Successfully tagged {image}:', str(line)) is not None + lineHasTag2 = re.search(f'naming to docker.io/library/{image}:', str(line)) is not None + tagged = tagged or lineHasTag or lineHasTag2 + # the OpenShift Cluster builder prepends image registry URL + lineHasCommit = re.search(r'COMMIT [a-zA-Z0-9\.:/\-]*' + image, str(line)) is not None + committed = committed or lineHasCommit + errorandwarnings['errors'] = 0 if committed or tagged else 1 + errorandwarnings['warnings'] = 0 + errorandwarnings['status'] = committed or tagged + files['Target Image Creation'] = errorandwarnings collectInfo[image] = files return collectInfo @@ -328,11 +286,12 @@ class Containerize(): raise ValueError(f'Insufficient Parameter: IP/node {ip}, path {path}') return (ip, path) - def BuildImage(self, HTML): + def BuildImage(self, ctx, HTML): svr = self.eNB_serverId[self.eNB_instance] lIpAddr, lSourcePath = self.GetCredentials(svr) logging.debug('Building on server: ' + lIpAddr) cmd = cls_cmd.getConnection(lIpAddr) + log_files = [] # Checking the hostname to get adapted on cli and dockerfileprefixes cmd.run('hostnamectl') @@ -424,7 +383,18 @@ class Containerize(): # On when the base image docker file is being modified. if forceBaseImageBuild: cmd.run(f"{self.cli} image rm {baseImage}:{baseTag}") - cmd.run(f"{self.cli} build {self.cliBuildOptions} --target {baseImage} --tag {baseImage}:{baseTag} --file docker/Dockerfile.base{self.dockerfileprefix} . &> cmake_targets/log/ran-base.log", timeout=1600) + logfile = f'{lSourcePath}/cmake_targets/log/ran-base.docker.log' + cmd.run(f"{self.cli} build {self.cliBuildOptions} --target {baseImage} --tag {baseImage}:{baseTag} --file docker/Dockerfile.base{self.dockerfileprefix} . &> {logfile}", timeout=1600) + t = ("ran-base", archiveArtifact(cmd, ctx, logfile)) + log_files.append(t) + + # Recover build logs, for the moment only possible when build is successful + cmd.run(f"{self.cli} create --name test {baseImage}:{baseTag}") + cmd.run("mkdir -p cmake_targets/log/ran-base") + logfile = f'{lSourcePath}/cmake_targets/log/ran-base.log' + cmd.run(f"{self.cli} cp test:/oai-ran/cmake_targets/log/all.txt {logfile}") + cmd.run(f"{self.cli} rm -f test") + archiveArtifact(cmd, ctx, logfile) # First verify if the base image was properly created. ret = cmd.run(f"{self.cli} image inspect --format=\'Size = {{{{.Size}}}} bytes\' {baseImage}:{baseTag}") allImagesSize = {} @@ -448,17 +418,9 @@ class Containerize(): else: logging.debug('ran-base size is unknown') - # Recover build logs, for the moment only possible when build is successful - cmd.run(f"{self.cli} create --name test {baseImage}:{baseTag}") - cmd.run("mkdir -p cmake_targets/log/ran-base") - cmd.run(f"{self.cli} cp test:/oai-ran/cmake_targets/log/. cmake_targets/log/ran-base") - cmd.run(f"{self.cli} rm -f test") - # Build the target image(s) status = True - attemptedImages = ['ran-base'] for image,pattern,name,option in imageNames: - attemptedImages += [name] # the archived Dockerfiles have "ran-base:latest" as base image # we need to update them with proper tag cmd.run(f'git checkout -- docker/Dockerfile.{pattern}{self.dockerfileprefix}') @@ -472,15 +434,18 @@ class Containerize(): cmd.run(f'sed -i -e "s#ran-build:latest#ran-build:{imageTag}#" docker/Dockerfile.{pattern}{self.dockerfileprefix}') if image == 'oai-gnb-aerial': cmd.run('cp -f /opt/nvidia-ipc/nvipc.src.2025.05.20.tar.gz .') - ret = cmd.run(f'{self.cli} build {self.cliBuildOptions} --target {image} --tag {name}:{imageTag} --file docker/Dockerfile.{pattern}{self.dockerfileprefix} {option} . > cmake_targets/log/{name}.log 2>&1', timeout=1200) + logfile = f'{lSourcePath}/cmake_targets/log/{name}.docker.log' + ret = cmd.run(f'{self.cli} build {self.cliBuildOptions} --target {image} --tag {name}:{imageTag} --file docker/Dockerfile.{pattern}{self.dockerfileprefix} {option} . > {logfile} 2>&1', timeout=1200) + t = (name, archiveArtifact(cmd, ctx, logfile)) + log_files.append(t) if image == 'oai-gnb-aerial': cmd.run('rm -f nvipc.src.2025.05.20.tar.gz') if image == 'ran-build' and ret.returncode == 0: cmd.run(f"docker run --name test-log -d {name}:{imageTag} /bin/true") - cmd.run(f"docker cp test-log:/oai-ran/cmake_targets/log/ cmake_targets/log/{name}/") + logfile = f'{lSourcePath}/{image}.log' + cmd.run(f"docker cp test-log:/oai-ran/cmake_targets/log/all.txt {logfile}") cmd.run(f"docker rm -f test-log") - else: - cmd.run(f"mkdir -p cmake_targets/log/{name}") + archiveArtifact(cmd, ctx, logfile) # check the status of the build ret = cmd.run(f"{self.cli} image inspect --format=\'Size = {{{{.Size}}}} bytes\' {name}:{imageTag}") if ret.returncode != 0: @@ -514,13 +479,10 @@ class Containerize(): logging.debug(cmd.run("df -h").stdout) logging.debug(cmd.run("docker system df").stdout) - # create a zip with all logs - build_log_name = f'build_log_{self.testCase_id}' - CopyLogsToExecutor(cmd, lSourcePath, build_log_name) cmd.close() # Analyze the logs - collectInfo = AnalyzeBuildLogs(build_log_name, attemptedImages, status) + collectInfo = AnalyzeBuildLogs(log_files, status) if status: logging.info('\u001B[1m Building OAI Image(s) Pass\u001B[0m') diff --git a/ci-scripts/main.py b/ci-scripts/main.py index f201bdbcc6..dcca9b521f 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -138,11 +138,11 @@ def ExecuteActionWithParam(action, ctx): if action == 'Build_eNB': success = cls_native.Native.Build(ctx, HTML.testCase_id, HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, RAN.Build_eNB_args) elif action == 'Build_Image': - success = CONTAINERS.BuildImage(HTML) + success = CONTAINERS.BuildImage(ctx, HTML) elif action == 'Build_Proxy': success = CONTAINERS.BuildProxy(ctx, HTML) elif action == 'Build_Cluster_Image': - success = CLUSTER.BuildClusterImage(HTML) + success = CLUSTER.BuildClusterImage(ctx, HTML) elif action == 'Build_Run_Tests': success = CONTAINERS.BuildRunTests(ctx, HTML) From 810dad97ae0a3016ea0f1d65347bd326eaffb3ca Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 17:53:28 +0200 Subject: [PATCH 74/86] Collect the ninja logs for various ran-build images --- ci-scripts/cls_containerize.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 3af34dca14..e632349960 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -440,9 +440,9 @@ class Containerize(): log_files.append(t) if image == 'oai-gnb-aerial': cmd.run('rm -f nvipc.src.2025.05.20.tar.gz') - if image == 'ran-build' and ret.returncode == 0: + if image == 'ran-build' or image == 'ran-build-asan' or image == 'ran-build-fhi72': cmd.run(f"docker run --name test-log -d {name}:{imageTag} /bin/true") - logfile = f'{lSourcePath}/{image}.log' + logfile = f'{lSourcePath}/{image}.ninja.log' cmd.run(f"docker cp test-log:/oai-ran/cmake_targets/log/all.txt {logfile}") cmd.run(f"docker rm -f test-log") archiveArtifact(cmd, ctx, logfile) From 65ec00c5fdf3a1fe87ffdf3fabd8f708ffc76748 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 14:30:05 +0200 Subject: [PATCH 75/86] No need for all the useless docker image rm --- ci-scripts/cls_containerize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index e632349960..0c1bcb530e 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -377,7 +377,7 @@ class Containerize(): # Let's remove any previous run artifacts if still there cmd.run(f"{self.cli} image prune --force") for image,pattern,name,option in imageNames: - cmd.run(f"{self.cli} image rm {name}:{imageTag}") + cmd.run(f"{self.cli} image rm {name}:{imageTag}", reportNonZero=False) # Build the base image only on Push Events (not on Merge Requests) # On when the base image docker file is being modified. From 025a6dbd1dd57ace93715abebb975066d5dbe28e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 17:43:05 +0200 Subject: [PATCH 76/86] Remove unused testCase_Id --- ci-scripts/cls_containerize.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 0c1bcb530e..306f9a9a47 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -255,8 +255,6 @@ class Containerize(): self.services = ['', '', ''] self.deploymentTag = '' - self.testCase_id = '' - self.cli = '' self.cliBuildOptions = '' self.dockerfileprefix = '' @@ -347,7 +345,6 @@ class Containerize(): imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue', '')) imageNames.append(('oai-gnb-aerial', 'gNB.aerial', 'oai-gnb-aerial', '')) - self.testCase_id = HTML.testCase_id cmd.cd(lSourcePath) # if asterix, copy the entitlement and subscription manager configurations if self.host == 'Red Hat': @@ -502,7 +499,6 @@ class Containerize(): logging.debug('Building on server: ' + lIpAddr) ssh = cls_cmd.getConnection(lIpAddr) - self.testCase_id = HTML.testCase_id oldRanCommidID = self.ranCommitID oldRanRepository = self.ranRepository oldRanAllowMerge = self.ranAllowMerge From 27e37a4181feda612ea7c879baf192b90808f910 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 16:14:53 +0200 Subject: [PATCH 77/86] Use archiveArtifact() for LicenceAndFormattingCheck() --- ci-scripts/cls_static_code_analysis.py | 12 ++++++------ ci-scripts/main.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ci-scripts/cls_static_code_analysis.py b/ci-scripts/cls_static_code_analysis.py index f2951b0528..33337a4700 100644 --- a/ci-scripts/cls_static_code_analysis.py +++ b/ci-scripts/cls_static_code_analysis.py @@ -43,6 +43,7 @@ from pathlib import Path import helpreadme as HELP import constants as CONST import cls_cmd +from cls_ci_helper import archiveArtifact #----------------------------------------------------------- # Class Declaration @@ -213,7 +214,7 @@ class StaticCodeAnalysis(): return True - def LicenceAndFormattingCheck(self, HTML): + def LicenceAndFormattingCheck(self, ctx, HTML): # Workspace is no longer recreated from scratch. # It implies that this method shall be called last within a build pipeline # where workspace is already created @@ -238,7 +239,7 @@ class StaticCodeAnalysis(): else: check_options += f' --build-arg TARGET_BRANCH={self.ranTargetBranch}' - logDir = f'{lSourcePath}/cmake_targets/build_log_{self.testCase_id}' + logDir = f'{lSourcePath}/cmake_targets/log/' cmd.run(f'mkdir -p {logDir}') cmd.run('docker image rm oai-formatting-check:latest') cmd.run(f'docker build --target oai-formatting-check --tag oai-formatting-check:latest {check_options} --file {lSourcePath}/ci-scripts/docker/Dockerfile.formatting.bionic {lSourcePath} > {logDir}/oai-formatting-check.txt 2>&1') @@ -247,12 +248,11 @@ class StaticCodeAnalysis(): cmd.run('docker image prune --force') cmd.run('docker volume prune --force') - # Analyzing the logs - cmd.copyin(f'{logDir}/oai-formatting-check.txt', 'oai-formatting-check.txt') + file = archiveArtifact(cmd, ctx, f'{logDir}/oai-formatting-check.txt') cmd.close() finalStatus = 0 - if (os.path.isfile('./oai-formatting-check.txt')): + if (os.path.isfile(file)): analyzed = False nbFilesNotFormatted = 0 listFiles = False @@ -263,7 +263,7 @@ class StaticCodeAnalysis(): gnuGplLicenceFiles = [] suspectLicence = False suspectLicenceFiles = [] - with open('./oai-formatting-check.txt', 'r') as logfile: + with open(file, 'r') as logfile: for line in logfile: ret = re.search('./ci-scripts/checkCodingFormattingRules.sh', str(line)) if ret is not None: diff --git a/ci-scripts/main.py b/ci-scripts/main.py index dcca9b521f..d4ef09668a 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -346,7 +346,7 @@ def ExecuteActionWithParam(action, ctx): success = cls_native.Native.Run_Physim(ctx, HTML, RAN.eNBIPAddress, RAN.eNBSourceCodePath, physim_options, physim_test, physim_threshold) elif action == 'LicenceAndFormattingCheck': - success = SCA.LicenceAndFormattingCheck(HTML) + success = SCA.LicenceAndFormattingCheck(ctx, HTML) elif action == 'Cppcheck_Analysis': success = SCA.CppCheckAnalysis(HTML) From 03a47721896bb76a57118b0a7bc9a6f8311b2ac7 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 6 Aug 2025 01:23:25 +0200 Subject: [PATCH 78/86] Remove cppcheck reference file: nobody knows about it, nobody checks --- ci-scripts/cls_static_code_analysis.py | 33 -------------------------- 1 file changed, 33 deletions(-) diff --git a/ci-scripts/cls_static_code_analysis.py b/ci-scripts/cls_static_code_analysis.py index 33337a4700..bdfb308a09 100644 --- a/ci-scripts/cls_static_code_analysis.py +++ b/ci-scripts/cls_static_code_analysis.py @@ -122,19 +122,6 @@ class StaticCodeAnalysis(): CCR_ref = CppCheckResults() vId = 0 for variant in CCR.variants: - refAvailable = False - if self.ranAllowMerge: - refFolder = str(Path.home()) + '/cppcheck-references' - if (os.path.isfile(refFolder + '/cppcheck-'+ variant + '.txt')): - refAvailable = True - with open(refFolder + '/cppcheck-'+ variant + '.txt', 'r') as refFile: - for line in refFile: - ret = re.search(' (?P[0-9\.]+) errors', str(line)) - if ret is not None: - CCR_ref.nbErrors[vId] = int(ret.group('nb_errors')) - ret = re.search(' (?P[0-9\.]+) warnings', str(line)) - if ret is not None: - CCR_ref.nbWarnings[vId] = int(ret.group('nb_warnings')) if (os.path.isfile('./cppcheck-'+ variant + '.txt')): xmlStart = False with open('./cppcheck-'+ variant + '.txt', 'r') as logfile: @@ -186,26 +173,6 @@ class StaticCodeAnalysis(): vMsg += ' Wrong Scanf Nb Args: ' + str(CCR.nbWrongScanfArg[vId]) + '\n' for vLine in vMsg.split('\n'): logging.debug(vLine) - if self.ranAllowMerge and refAvailable: - if CCR_ref.nbErrors[vId] == CCR.nbErrors[vId]: - logging.debug(' No change in number of errors') - elif CCR_ref.nbErrors[vId] > CCR.nbErrors[vId]: - logging.debug(' Good! Decrease in number of errors') - else: - logging.debug(' Bad! increase in number of errors') - if CCR_ref.nbWarnings[vId] == CCR.nbWarnings[vId]: - logging.debug(' No change in number of warnings') - elif CCR_ref.nbWarnings[vId] > CCR.nbWarnings[vId]: - logging.debug(' Good! Decrease in number of warnings') - else: - logging.debug(' Bad! increase in number of warnings') - # Create new reference file - if not self.ranAllowMerge: - refFolder = str(Path.home()) + '/cppcheck-references' - if not os.path.isdir(refFolder): - os.mkdir(refFolder) - with open(refFolder + '/cppcheck-'+ variant + '.txt', 'w') as refFile: - refFile.write(vMsg) vId += 1 HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) From 14389235dfa1e713d3ba1907c3030d8893894f66 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 5 Aug 2025 16:19:36 +0200 Subject: [PATCH 79/86] Use archiveArtifact() for SCA.CppCheckAnalysis() --- ci-scripts/cls_static_code_analysis.py | 17 ++++++++++------- ci-scripts/main.py | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/ci-scripts/cls_static_code_analysis.py b/ci-scripts/cls_static_code_analysis.py index bdfb308a09..8b0ec05352 100644 --- a/ci-scripts/cls_static_code_analysis.py +++ b/ci-scripts/cls_static_code_analysis.py @@ -81,7 +81,7 @@ class StaticCodeAnalysis(): self.eNBPassword = '' self.eNBSourceCodePath = '' - def CppCheckAnalysis(self, HTML): + def CppCheckAnalysis(self, ctx, HTML): if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '': HELP.GenericHelp(CONST.Version) sys.exit('Insufficient Parameter') @@ -104,7 +104,7 @@ class StaticCodeAnalysis(): full_ran_repo_name = self.ranRepository + '.git' cmd.cd(lSourcePath) - logDir = f'{lSourcePath}/cmake_targets/build_log_{self.testCase_id}' + logDir = f'{lSourcePath}/cmake_targets/log' cmd.run(f'mkdir -p {logDir}') cmd.run('docker image rm oai-cppcheck:bionic oai-cppcheck:focal') cmd.run(f'sed -e "s@xenial@bionic@" {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.xenial > {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.bionic') @@ -113,18 +113,21 @@ class StaticCodeAnalysis(): cmd.run(f'docker build --tag oai-cppcheck:focal --file {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.focal . > {logDir}/cppcheck-focal.txt 2>&1') cmd.run('docker image rm oai-cppcheck:bionic oai-cppcheck:focal') - # Analyzing the logs - cmd.copyin(f'{logDir}/cppcheck-bionic.txt', 'cppcheck-bionic.txt') - cmd.copyin(f'{logDir}/cppcheck-focal.txt', 'cppcheck-focal.txt') + bionic = archiveArtifact(cmd, ctx, f'{logDir}/cppcheck-bionic.txt') + focal = archiveArtifact(cmd, ctx, f'{logDir}/cppcheck-focal.txt') cmd.close() CCR = CppCheckResults() CCR_ref = CppCheckResults() vId = 0 for variant in CCR.variants: - if (os.path.isfile('./cppcheck-'+ variant + '.txt')): + filename = ctx.baseFilename() + '-cppcheck-'+ variant + '.txt' + logging.info(f"will check file '{filename}'") + if not os.path.isfile(filename): + raise FileNotFoundError(f"{filename} is not a file") + else: xmlStart = False - with open('./cppcheck-'+ variant + '.txt', 'r') as logfile: + with open(filename, 'r') as logfile: for line in logfile: ret = re.search('cppcheck version="(?P[0-9\.]+)"', str(line)) if ret is not None: diff --git a/ci-scripts/main.py b/ci-scripts/main.py index d4ef09668a..836f6aee3b 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -349,7 +349,7 @@ def ExecuteActionWithParam(action, ctx): success = SCA.LicenceAndFormattingCheck(ctx, HTML) elif action == 'Cppcheck_Analysis': - success = SCA.CppCheckAnalysis(HTML) + success = SCA.CppCheckAnalysis(ctx, HTML) elif action == 'Push_Local_Registry': svr_id = test.findtext('svr_id') From 8c2b356c1d3170c6423edb92da335fe6a9ec9a02 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 6 Aug 2025 01:29:30 +0200 Subject: [PATCH 80/86] Remove unused function --- ci-scripts/ran.py | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/ci-scripts/ran.py b/ci-scripts/ran.py index 46711b1b73..5c5513a317 100644 --- a/ci-scripts/ran.py +++ b/ci-scripts/ran.py @@ -248,41 +248,6 @@ class RANManagement(): logging.debug('\u001B[1m Initialize eNB/gNB Completed\u001B[0m') return enbDidSync - def CheckeNBProcess(self, status_queue): - # At least the instance 0 SHALL be on! - if self.eNBstatuses[0] == 0: - lIpAddr = self.eNBIPAddress - lUserName = self.eNBUserName - lPassWord = self.eNBPassword - elif self.eNBstatuses[0] == 1: - lIpAddr = self.eNB1IPAddress - lUserName = self.eNB1UserName - lPassWord = self.eNB1Password - elif self.eNBstatuses[0] == 2: - lIpAddr = self.eNB2IPAddress - lUserName = self.eNB2UserName - lPassWord = self.eNB2Password - else: - lIpAddr = self.eNBIPAddress - lUserName = self.eNBUserName - lPassWord = self.eNBPassword - mySSH = SSH.SSHConnection() - mySSH.open(lIpAddr, lUserName, lPassWord) - if self.air_interface[self.eNB_instance] == '': - pattern = 'softmodem' - else: - pattern = self.air_interface[self.eNB_instance] - mySSH.command('stdbuf -o0 ps -aux | grep --color=never ' + pattern + ' | grep -v grep', '\$', 5) - result = re.search(pattern, mySSH.getBefore()) - success = result is not None - if not success: - logging.debug('\u001B[1;37;41m eNB Process Not Found! \u001B[0m') - status_queue.put(CONST.ENB_PROCESS_FAILED) - else: - status_queue.put(CONST.ENB_PROCESS_OK) - mySSH.close() - return success - def TerminateeNB(self, HTML): if self.eNB_serverId[self.eNB_instance] == '0': lIpAddr = self.eNBIPAddress From 5090ce6f0abd4a863928cece4ff02cf55a147be2 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 6 Aug 2025 01:45:02 +0200 Subject: [PATCH 81/86] Ignore main.py TerminateeNB mode No pipeline uses it. With the changes in the next commit, pipelines would start to fail. Remove it for simplicity. --- ci-scripts/main.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 836f6aee3b..8a52b7cf18 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -469,15 +469,7 @@ if py_param_file_present == True: cwd = os.getcwd() if re.match('^TerminateeNB$', mode, re.IGNORECASE): - if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - if RAN.eNBIPAddress == 'none': - sys.exit(0) - RAN.eNB_instance=0 - RAN.eNB_serverId[0]='0' - RAN.eNBSourceCodePath='/tmp/' - RAN.TerminateeNB(HTML) + logging.warning("Option TerminateeNB ignored") elif re.match('^TerminateHSS$', mode, re.IGNORECASE): logging.warning("Option TerminateHSS ignored") elif re.match('^TerminateMME$', mode, re.IGNORECASE): From 244ea8fc22048051fd7a3f5a28bb6a4a7a5c50e6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 6 Aug 2025 01:37:47 +0200 Subject: [PATCH 82/86] Use archiveArtifact() for TerminateeNB() --- ci-scripts/main.py | 2 +- ci-scripts/ran.py | 32 ++++++++++++++++++-------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 8a52b7cf18..1b06539a2c 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -206,7 +206,7 @@ def ExecuteActionWithParam(action, ctx): RAN.air_interface[RAN.eNB_instance] = 'lte-softmodem' else: RAN.air_interface[RAN.eNB_instance] = air_interface.lower() +'-softmodem' - success = RAN.TerminateeNB(HTML) + success = RAN.TerminateeNB(ctx, HTML) elif action == 'Initialize_UE' or action == 'Attach_UE' or action == 'Detach_UE' or action == 'Terminate_UE' or action == 'CheckStatusUE' or action == 'DataEnable_UE' or action == 'DataDisable_UE': CiTestObj.ue_ids = test.findtext('id').split(' ') diff --git a/ci-scripts/ran.py b/ci-scripts/ran.py index 5c5513a317..48039aa2e7 100644 --- a/ci-scripts/ran.py +++ b/ci-scripts/ran.py @@ -46,6 +46,7 @@ import cls_cmd import sshconnection as SSH import helpreadme as HELP import constants as CONST +from cls_ci_helper import archiveArtifact #----------------------------------------------------------- # Class Declaration @@ -248,7 +249,7 @@ class RANManagement(): logging.debug('\u001B[1m Initialize eNB/gNB Completed\u001B[0m') return enbDidSync - def TerminateeNB(self, HTML): + def TerminateeNB(self, ctx, HTML): if self.eNB_serverId[self.eNB_instance] == '0': lIpAddr = self.eNBIPAddress lUserName = self.eNBUserName @@ -320,13 +321,11 @@ class RANManagement(): fileToAnalyze = self.eNBLogFiles[int(self.eNB_instance)] self.eNBLogFiles[int(self.eNB_instance)] = '' if analyzeFile: - #*stats.log files + pickle + png - mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*stats.log', '.') - mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*.pickle', '.') - mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/*.png', '.') - # - copyin_res = mySSH.copyin(lIpAddr, lUserName, lPassWord, lSourcePath + '/cmake_targets/' + fileToAnalyze, '.') - if (copyin_res == -1): + with cls_cmd.getConnection(lIpAddr) as cmd: + file = archiveArtifact(cmd, ctx, lSourcePath + '/cmake_targets/' + fileToAnalyze) + archiveArtifact(cmd, ctx, lSourcePath + '/cmake_targets/nrL1_stats.log') + archiveArtifact(cmd, ctx, lSourcePath + '/cmake_targets/nrMAC_stats.log') + if file is None: logging.debug('\u001B[1;37;41m Could not copy ' + nodeB_prefix + 'NB logfile to analyze it! \u001B[0m') HTML.htmleNBFailureMsg='Could not copy ' + nodeB_prefix + 'NB logfile to analyze it!' HTML.CreateHtmlTestRow('N/A', 'KO', CONST.ENB_PROCESS_NOLOGFILE_TO_ANALYZE) @@ -339,8 +338,8 @@ class RANManagement(): mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './nrL1_stats.log', self.eNBSourceCodePath + '/cmake_targets/') mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './nrMAC_stats.log', self.eNBSourceCodePath + '/cmake_targets/') mySSH.copyout(self.eNBIPAddress, self.eNBUserName, self.eNBPassword, './' + fileToAnalyze, self.eNBSourceCodePath + '/cmake_targets/') - logging.debug('\u001B[1m Analyzing ' + nodeB_prefix + 'NB logfile \u001B[0m ' + fileToAnalyze) - logStatus = self.AnalyzeLogFile_eNB(fileToAnalyze, HTML, self.ran_checkers) + logging.debug('\u001B[1m Analyzing ' + nodeB_prefix + 'NB logfile \u001B[0m ' + file) + logStatus = self.AnalyzeLogFile_eNB(file, HTML, self.ran_checkers) if (logStatus < 0): HTML.CreateHtmlTestRow('N/A', 'KO', logStatus) #display rt stats for gNB only @@ -698,11 +697,16 @@ class RANManagement(): datalog_rt_stats = yaml.load(f,Loader=yaml.FullLoader) rt_keys = datalog_rt_stats['Ref'] #we use the keys from the Ref field - if os.path.isfile('./nrL1_stats.log') and os.path.isfile('./nrMAC_stats.log'): + # nrL1_stats.log/nrMAC_stats.log should be in the same directory as main log file + # currently the link is only implicit as below based on pattern matching + # I will rework this to give the file explicitly + l1_stats_fn = re.sub(r'enb_[0-9]+', 'nrL1_stats', eNBlogFile) + mac_stats_fn = re.sub(r'enb_[0-9]+', 'nrMAC_stats', eNBlogFile) + if os.path.isfile(l1_stats_fn) and os.path.isfile(mac_stats_fn): # don't use CI-nrL1_stats.log, as this will increase the processing time for # no reason, we just need the last occurence - nrL1_stats = open('./nrL1_stats.log', 'r') - nrMAC_stats = open('./nrMAC_stats.log', 'r') + nrL1_stats = open(l1_stats_fn, 'r') + nrMAC_stats = open(mac_stats_fn, 'r') for line in nrL1_stats.readlines(): for k in rt_keys: result = re.search(k, line) @@ -722,7 +726,7 @@ class RANManagement(): nrL1_stats.close() nrMAC_stats.close() else: - logging.debug("NR Stats files for RT analysis not found") + logging.debug(f"NR Stats files for RT analysis not found: {l1_stats_fn}, {mac_stats_fn}") #stdout log file and stat log files analysis completed logging.debug(' File analysis (stdout, stats) completed') From cc0d0ae0ab044f5827a2bde09822e8ef1785dc7d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 6 Aug 2025 12:07:01 +0200 Subject: [PATCH 83/86] gNB caracal USRP build: don't stop if we can't reboot Sometimes the connection fails because of the reboot. Without may_fail, it would stop the build, which will then run the wrong code, possibly. Instead, continue; if there is a problem with USRP, it will be visible when starting nr-softmodem. --- ci-scripts/xml_files/gnb_usrp_build.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/ci-scripts/xml_files/gnb_usrp_build.xml b/ci-scripts/xml_files/gnb_usrp_build.xml index f67098116b..cd5e91b02f 100644 --- a/ci-scripts/xml_files/gnb_usrp_build.xml +++ b/ci-scripts/xml_files/gnb_usrp_build.xml @@ -47,6 +47,7 @@ Custom_Command Reboot USRP + true caracal ssh root@192.168.20.2 reboot From 40efa2a545dcb5766748d8a2624cd611daf2e558 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 6 Aug 2025 00:51:35 +0200 Subject: [PATCH 84/86] Remove LogCollectBuild step: not used by any pipeline --- ci-scripts/Jenkinsfile-tmp-full-ran | 18 ------------------ ci-scripts/Jenkinsfile-tmp-multi-enb | 18 ------------------ ci-scripts/Jenkinsfile-tmp-multi-enb-nsa | 18 ------------------ ci-scripts/Jenkinsfile-tmp-ran | 18 ------------------ ci-scripts/cls_oaicitest.py | 18 ------------------ ci-scripts/main.py | 7 +------ 6 files changed, 1 insertion(+), 96 deletions(-) diff --git a/ci-scripts/Jenkinsfile-tmp-full-ran b/ci-scripts/Jenkinsfile-tmp-full-ran index f1baa5b04d..3bc1d6ad8e 100644 --- a/ci-scripts/Jenkinsfile-tmp-full-ran +++ b/ci-scripts/Jenkinsfile-tmp-full-ran @@ -216,24 +216,6 @@ pipeline { } stage('Log Collection') { parallel { - stage('Log Collection (eNB - Build)') { - steps { - withCredentials([ - [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'] - ]) { - echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m' - sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}" - - echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m' - sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true" - } - script { - if(fileExists("build.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "build.log.${env.BUILD_ID}.zip" - } - } - } - } stage('Log Collection (eNB - Run)') { steps { withCredentials([ diff --git a/ci-scripts/Jenkinsfile-tmp-multi-enb b/ci-scripts/Jenkinsfile-tmp-multi-enb index 2680fbe863..7c7397f30b 100644 --- a/ci-scripts/Jenkinsfile-tmp-multi-enb +++ b/ci-scripts/Jenkinsfile-tmp-multi-enb @@ -222,24 +222,6 @@ pipeline { } stage('Log Collection') { parallel { - stage('Log Collection (eNB - Build)') { - steps { - withCredentials([ - [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'] - ]) { - echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m' - sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}" - - echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m' - sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true" - } - script { - if(fileExists("build.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "build.log.${env.BUILD_ID}.zip" - } - } - } - } stage('Log Collection (eNB - Run)') { steps { withCredentials([ diff --git a/ci-scripts/Jenkinsfile-tmp-multi-enb-nsa b/ci-scripts/Jenkinsfile-tmp-multi-enb-nsa index 5e60b199b4..89daaec8c1 100644 --- a/ci-scripts/Jenkinsfile-tmp-multi-enb-nsa +++ b/ci-scripts/Jenkinsfile-tmp-multi-enb-nsa @@ -210,24 +210,6 @@ pipeline { } stage('Log Collection') { parallel { - stage('Log Collection (eNB - Build)') { - steps { - withCredentials([ - [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'] - ]) { - echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m' - sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}" - - echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m' - sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true" - } - script { - if(fileExists("build.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "build.log.${env.BUILD_ID}.zip" - } - } - } - } stage('Log Collection (eNB - Run)') { steps { withCredentials([ diff --git a/ci-scripts/Jenkinsfile-tmp-ran b/ci-scripts/Jenkinsfile-tmp-ran index 03c0f9a0cb..82db6641e3 100644 --- a/ci-scripts/Jenkinsfile-tmp-ran +++ b/ci-scripts/Jenkinsfile-tmp-ran @@ -227,24 +227,6 @@ pipeline { } stage('Log Collection') { parallel { - stage('Log Collection (eNB - Build)') { - steps { - withCredentials([ - [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.eNB_Credentials}", usernameVariable: 'eNB_Username', passwordVariable: 'eNB_Password'] - ]) { - echo '\u2705 \u001B[32mLog Collection (eNB - Build)\u001B[0m' - sh "python3 ci-scripts/main.py --mode=LogCollectBuild --eNBIPAddress=${params.eNB_IPAddress} --eNBUserName=${eNB_Username} --eNBPassword=${eNB_Password} --eNBSourceCodePath=${params.eNB_SourceCodePath}" - - echo '\u2705 \u001B[32mLog Transfer (eNB - Build)\u001B[0m' - sh "sshpass -p \'${eNB_Password}\' scp -o 'StrictHostKeyChecking no' -o 'ConnectTimeout 10' ${eNB_Username}@${params.eNB_IPAddress}:${eNB_SourceCodePath}/cmake_targets/build.log.zip ./build.log.${env.BUILD_ID}.zip || true" - } - script { - if(fileExists("build.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "build.log.${env.BUILD_ID}.zip" - } - } - } - } stage('Log Collection (eNB - Run)') { steps { withCredentials([ diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index ba322ca608..efdb589cff 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -845,21 +845,3 @@ class OaiCiTest(): message = "Log files:\n" + "\n".join([os.path.basename(l) for l in logs]) HTML.CreateHtmlTestRowQueue(core_name, 'OK', [message]) return True - - def LogCollectBuild(self,RAN): - # Some pipelines are using "none" IP / Credentials - # In that case, just forget about it - if RAN.eNBIPAddress == 'none': - sys.exit(0) - - if (RAN.eNBIPAddress != '' and RAN.eNBUserName != '' and RAN.eNBPassword != ''): - IPAddress = RAN.eNBIPAddress - UserName = RAN.eNBUserName - Password = RAN.eNBPassword - SourceCodePath = RAN.eNBSourceCodePath - else: - sys.exit('Insufficient Parameter') - with cls_cmd.getConnection(IPAddress) as cmd: - d = f'{SourceCodePath}/cmake_targets' - cmd.run(f'rm -f {d}/build.log.zip') - cmd.run(f'cd {d} && zip -r build.log.zip build_log_*/*') diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 1b06539a2c..0fdf538052 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -477,12 +477,7 @@ elif re.match('^TerminateMME$', mode, re.IGNORECASE): elif re.match('^TerminateSPGW$', mode, re.IGNORECASE): logging.warning("Option TerminateSPGW ignored") elif re.match('^LogCollectBuild$', mode, re.IGNORECASE): - if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '' or RAN.eNBSourceCodePath == '': - logging.warning("nothing to collect (eNBIPAddress/eNBUserName/eNBPassword/eNBSourceCodePath is '')") - sys.exit(0) - if RAN.eNBIPAddress == 'none': - sys.exit(0) - CiTestObj.LogCollectBuild(RAN) + logging.warning("Option LogCollectBuild ignored") elif re.match('^LogCollecteNB$', mode, re.IGNORECASE): if RAN.eNBIPAddress == '' or RAN.eNBUserName == '' or RAN.eNBPassword == '' or RAN.eNBSourceCodePath == '': HELP.GenericHelp(CONST.Version) From 43e2fd57d4054dcc384028e1ea6dc068ef1129da Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 7 Aug 2025 09:01:55 +0200 Subject: [PATCH 85/86] Use Ninja for FHI72 builds --- docker/Dockerfile.build.fhi72.native_arm.ubuntu22 | 2 +- docker/Dockerfile.build.fhi72.rhel9 | 2 +- docker/Dockerfile.build.fhi72.ubuntu22 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile.build.fhi72.native_arm.ubuntu22 b/docker/Dockerfile.build.fhi72.native_arm.ubuntu22 index 4ea1bcd7bb..b796122bf3 100644 --- a/docker/Dockerfile.build.fhi72.native_arm.ubuntu22 +++ b/docker/Dockerfile.build.fhi72.native_arm.ubuntu22 @@ -78,7 +78,7 @@ RUN /bin/sh oaienv && \ cd cmake_targets && \ mkdir -p log && \ ./build_oai \ - --gNB \ + --ninja --gNB \ --build-lib "telnetsrv enbscope uescope nrscope" \ -t oran_fhlib_5g --cmake-opt -Dxran_LOCATION=/opt/phy/fhi_lib/lib \ --build-e2 --cmake-opt -DXAPP_MULTILANGUAGE=OFF --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION \ diff --git a/docker/Dockerfile.build.fhi72.rhel9 b/docker/Dockerfile.build.fhi72.rhel9 index b8ad1560ba..472378935d 100644 --- a/docker/Dockerfile.build.fhi72.rhel9 +++ b/docker/Dockerfile.build.fhi72.rhel9 @@ -58,7 +58,7 @@ RUN /bin/sh oaienv && \ mkdir -p log && \ scl enable gcc-toolset-13 "\ ./build_oai \ - --gNB \ + --ninja --gNB \ --build-lib 'telnetsrv enbscope uescope nrscope' \ -t oran_fhlib_5g --cmake-opt -Dxran_LOCATION=/opt/phy/fhi_lib/lib \ --build-e2 --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION" &&\ diff --git a/docker/Dockerfile.build.fhi72.ubuntu22 b/docker/Dockerfile.build.fhi72.ubuntu22 index 5da6d0b608..4674648285 100644 --- a/docker/Dockerfile.build.fhi72.ubuntu22 +++ b/docker/Dockerfile.build.fhi72.ubuntu22 @@ -68,7 +68,7 @@ RUN /bin/sh oaienv && \ cd cmake_targets && \ mkdir -p log && \ ./build_oai \ - --gNB \ + --ninja --gNB \ --build-lib "telnetsrv enbscope uescope nrscope" \ -t oran_fhlib_5g --cmake-opt -Dxran_LOCATION=/opt/phy/fhi_lib/lib \ --build-e2 --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION \ From 667bcf920c0b384019f6bf57650dc751bd9bcf47 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 7 Aug 2025 15:15:34 +0200 Subject: [PATCH 86/86] Trace analysis results --- ci-scripts/cls_containerize.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 306f9a9a47..34522d9eef 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -100,6 +100,7 @@ def AnalyzeBuildLogs(logfiles, globalStatus): errorandwarnings['errors'] = 0 if committed or tagged else 1 errorandwarnings['warnings'] = 0 errorandwarnings['status'] = committed or tagged + logging.info(f"Analyzing {image}, file {lf}: {errorandwarnings}") files['Target Image Creation'] = errorandwarnings collectInfo[image] = files return collectInfo