Skip to content

Send dApp reports to the xApps through the gNB's E2SM-DAPP - #14

Open
aferaudo wants to merge 4 commits into
mainfrom
feat/e2-dapp-reports
Open

aferaudo wants to merge 4 commits into
mainfrom
feat/e2-dapp-reports

Conversation

@aferaudo

Copy link
Copy Markdown
Collaborator

Summary

  • dApp reports now reach the xApps on OCUDU. When a dApp sends an E3 report (for example the PRBs where it found an incumbent), the controller passes it to a new codelet, dapp_report_relay. ocudu's E2SM-DAPP bridge then sends it to the subscribed xApps as an E2 indication (wineslab/ocudu-e3-private#3). While no xApp is subscribed, the controller drops the reports, and the dApp is not told.
  • The Spectrum SM now uses the grammar from spear-flexric, because the xApp decodes the reports with it. RAN function data and controls are sent in the agent's encoding. The legacy IQ indication keeps its bytes.
  • Two build fixes for the SM types. They are now generated with libe3's codec set. And the asn1c cleanup now actually removes asn1c's runtime: it was looking in the wrong directory.

Type of change

  • Bug fix (asn1c codec set and cleanup)
  • New feature / enhancement (dApp reports to an xApp)
  • New Service Model
  • Refactor (no behavior change)
  • Documentation
  • Test / CI / packaging
  • Other (explain):

Linked issue

None.

Mandatory test checklist

  • Each of the four commits builds on its own on Ubuntu 22.04 (x86_64, GCC 13, libe3 0.2.0), from a clean build directory: ./build.sh, the verifier and make -C codelets. At each commit the codelets pass the verifier and the tests that exist there pass. The rebuilt codelet objects are identical to the committed ones. The only compiler warnings come from jbpf and its ebpf-verifier, not from this code.
  • test_spectrum_sm (new, -DE3C_BUILD_TESTS=ON) checks three things:
    • The legacy IQ indication still produces the bytes of the old grammar.
    • APER controls decode from vectors encoded by hand.
    • The JSON controls the adaptive_cpu dApp builds are decoded, and the bad ones are refused.
  • test_xapp_presence (new) checks when the controller considers an xApp present.
  • All three codelets pass the verifier. dapp_report_relay terminates within 307 instructions.
  • Over the air on aarch64, with wineslab/ocudu-e3-private#3. The setup was a Foxconn RU, the adaptive_cpu dApp, an OSC near-RT RIC and xDevSM's basic_dapp_sync_xapp:
    • Without a RIC, the controller loaded the codelet with the first report. It then dropped all 9676 reports as no_xapp, with 0 send_failed. The L1-KPM IQ path was not affected (IQ age p50 0.1 ms).
    • With the RIC, the xApp received 40 reports/s. Every sequence id arrived once and in order. The first report the xApp got was the one right after the last one dropped as no_xapp, and after the xApp unsubscribed the controller went back to dropping.
  • x86 over the air: not run with this branch.
  • Provenance: the over-the-air runs used this code on top of Build on arm64 and add a NEON path to the slot convert #13 (arm64). Only the docs changed since.
  • VERSION, libe3.pc, new dependencies: N/A, none changed.

CI checklist

  • build and measure is green for the head commit (to check once CI has run).
  • Commit policy: libe3/scripts/check_commit_trailers.py --base origin/main --head HEAD passes.

Twin-repo coordination

  • This PR does not change the E3 wire protocol or public ABI, OR a paired PR exists in each affected twin repo (link below).

The E3 protocol and libe3 are unchanged. codelets/include/jbpf_e3_dapp_api.h has to stay byte-identical with ocudu's ocudu_janus/e3/jbpf_e3_dapp_api.h, and ocudu's configure compares the two. ocudu skips that check against an E3Controller that does not have the header yet, so the two PRs can be merged in either order.

Paired PR: wineslab/ocudu-e3-private#3.

The dApp only needs to send its reports to the E3Controller. The adaptive_cpu dApp sends them to an agent whose name is fixed in its code: either give the E3Controller's session that name in the dApp config, or use the e3_manager.spectrum_agent key from spear-aerial-sample-apps feat/configurable-spectrum-agent (not opened as a PR yet).

What does not work yet

  • Controls from the xApp do not come back to the dApp. The gNB answers them with a failure.
  • E2SM-DAPP report style 2 (the map of dApps and their RAN functions) sends nothing.
  • After a gNB restart, the controller has to be restarted too. It loads the codelet only once, so until then every report is dropped as no_xapp.
  • Reports larger than 2048 bytes are dropped (too_big). A Spectrum report for 273 PRBs is about 310 bytes.

These are listed in docs/known-limitations.md. The codelet's README (codelets/dapp_report_relay/README.md) explains the path and the counters on both sides.

Workflow confirmation

  • Linear, fast-forward-able descendant of main (fbdf325), with no merge commits.
  • Four commits, each with an imperative subject and a what/why body, each building on its own.
  • I have read and followed CONTRIBUTING.md (libe3's, applied to this repository).

🤖 Generated with Claude Code

Two problems in how the SM types were generated, both hidden so far:

- asn1c generated BER and JER code for our types, while libe3's runtime
  is built without them. asn_config.h sets the layout of the type tables
  from that codec set, so our tables could be laid out differently from
  the runtime they link against. Generate with -no-gen-OER -no-gen-BER
  -no-gen-JER, like libe3.
- cleanup_asn1c.cmake runs with -P, where CMAKE_BINARY_DIR is the working
  directory (<build>/src/e3sm/asn), not the build root. It looked for a
  directory that does not exist and returned, so the whole asn1c runtime
  stayed next to our types, asn_config.h included, and could shadow
  libe3's. The directory is now passed in, and a missing one is an error.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
The dApp, the controller and the xApp all encode Spectrum types, and the
xApp decodes the dApp's reports with OAI's grammar. Ours was an older one.
e3sm_spectrum.asn is now the copy in wineslab/spear-flexric
(src/sm/dapp_sm/e3/service_models/spectrum_sm/asn/defs/e3sm_spectrum.asn),
unchanged, plus a marked section at the end with Spectrum-IQDataIndication,
which only the legacy eCPRI IQ path uses. Its bytes do not change.

- RAN function data goes out in the agent's encoding: APER, or in JSON
  the same fields as an object, as OAI's Spectrum SM sends it.
- A dApp control is Spectrum-DAppControlData in APER, or OAI's JSON
  envelope ({"controlType":"prbBlacklist","controlPayload":{...}}).
  Only control id 1 (PRB blacklist) is accepted; the other ids and payloads
  get a NACK. The blacklist is still only logged and acknowledged.
- Spectrum-SensingPolicyControl has a BOOLEAN and libe3 ships no BOOLEAN
  runtime, so asn1c's is compiled with libe3's codec set, only while
  libe3 has none.
- tests/test_spectrum_sm.cpp (E3C_BUILD_TESTS=ON): the legacy IQ
  indication against bytes from the previous grammar, APER controls
  against vectors encoded by hand, and the JSON controls the dApp builds.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
On OAI, the gNB's E3 agent hands a dApp report to its E2 agent, which
sends it to the xApps over E2SM-DAPP. On OCUDU the E3 agent is this
process, and jbpf is its only way into the gNB, so a codelet carries the
reports.

dapp_report_relay attaches to e3_dapp_report_poll, a hook that ocudu's
E2SM-DAPP bridge fires every 0.5 ms while an xApp has a data
subscription. Each call it takes up to 8 reports from its control input
report_in and hands them to the bridge with the helper
e3_dapp_report_push. It also sends the number of subscriptions on
presence_out, when it changes and every 100 ms, so that the controller
knows when someone is listening.

codelets/include/jbpf_e3_dapp_api.h has the messages, the hook context,
the helper id (CUSTOM_HELPER_START_ID + 2) and the program type. ocudu
keeps a byte-identical copy in ocudu_janus/e3/ and checks it at
configure. The verifier knows the new program type and helper.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
libe3 already receives the dApps' E3 reports, but nothing used them.
DAppReportRelay (src/e3sm/dapp_report_relay.*) takes them from libe3's
report worker and queues them; the main loop, which owns the jbpf io
context, sends them to the dapp_report_relay codelet. The codelet is
loaded with the first report, and a failed load is retried at most every
2 s. The report bytes are not decoded.

The relay forwards only while an xApp is subscribed: the codelet's
presence messages say how many subscriptions there are, and
XAppPresence treats the xApp as gone after 300 ms without one. Otherwise
the report is dropped here, because nothing in the gNB would drain the
codelet's queue. The dApp is not told, as with OAI.

Every report ends up in one counter (forwarded, no_xapp, too_big,
no_codelet, queue_full, send_failed), printed once a second while reports
arrive and in the shutdown summary. A new dapp_reports section in the
config turns the relay off or sizes its queue.

Docs: the codelet's README has the whole path, the contract with ocudu
and how to read the counters on both sides; the service models and known
limitations pages point to it. tests/test_xapp_presence.cpp covers the
presence logic.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant