feat(qos): add N5 QoS core processing and N7 commit and notify - #2
feat(qos): add N5 QoS core processing and N7 commit and notify#2tariromukute wants to merge 42 commits into
Conversation
Sequence chart from GitLab MRsequenceDiagram
participant AF as Application Function
participant API as api-server
participant PA as pcf_policy_authorization.cpp
participant EVENT as pcf_event.hpp
participant SMPC as pcf_sm_policy_control.cpp
participant ASSOC as individual_sm_association.cpp
participant QOS as app_session.cpp
participant SMF as SMF
AF->>API: HTTP POST /app-sessions
Note over AF,API: AppSessionContext with MediaComponent containing qosReference
API->>PA: post_app_sessions_handler(context, app_session_id, problem_details)
Note over PA,ASSOC: Session Binding - locate N7 SM Policy association
PA->>EVENT: sm_session_binding(ueIpv4, supi, dnn, association_id, current_decision)
EVENT->>SMPC: handle_session_binding_request(ipv4, supi, dnn, assoc_id, decision)
SMPC->>SMPC: m_policy_storage->find_association(ipv4, supi, dnn)
SMPC->>ASSOC: get_sm_policy_decision_dto()
ASSOC-->>SMPC: SmPolicyDecision (base N7 decision)
SMPC-->>PA: association_id and current_decision populated as out params
Note over PA,QOS: QoS Parameter Processing
PA->>PA: authorize_service_info(reqData)
loop For each MediaComponent where qosReferenceIsSet()
PA->>QOS: handle_qos_requirements(current_decision)
QOS->>QOS: create_qos_data_from_media_component(current_decision)
QOS->>QOS: create_qos_characteristics(current_decision)
QOS->>QOS: setup_qos_monitoring(current_decision)
QOS-->>PA: handler_result OK
PA->>PA: qos_flow_processed = true
end
PA->>QOS: validate_qos_authorization()
QOS-->>PA: handler_result OK
PA->>PA: validate_and_merge_decision(request_decision, current_decision)
PA->>PA: app_session(reqContext, current_decision, app_session_id)
PA->>PA: m_app_sessions.insert(app_session_id, app_session)
Note over PA,SMF: Cross-Service Coordination and SMF Notification
PA->>EVENT: sm_update_decision(association_id, current_decision)
EVENT->>SMPC: handle_update_decision_request(association_id, decision)
SMPC->>ASSOC: set_sm_policy_decision(decision)
Note over ASSOC: association now stores SmPolicyDecision with qosDecs and pccRules
SMPC->>SMPC: send_sm_policy_control_update_notify(association)
SMPC->>SMF: HTTP POST notificationUri/update with smPolicyDecision containing pccRules and qosDecs
alt 200 OK
SMF-->>SMPC: 200 OK
else 4xx or 5xx
SMF-->>SMPC: error response, propagated to AF
end
Note over PA,AF: AF Notification Infrastructure
PA--xAF: notify_af_qos_status()
Note over PA,AF: send Npcf_PolicyAuthorization_Notify to AF callback URI on QoS status changes
Note over SMPC,SMF: Phase 4 - QoS Monitoring Framework
SMPC--xSMF: configure QosMonitoringData thresholds
Note over SMPC,SMF: rovision QosMonitoringData to SMF and deliver measurement reports to AF
PA-->>API: status_code::CREATED
API-->>AF: 201 Created Location /app-sessions/{app_session_id}
|
29079ab to
858098a
Compare
phine-lukas
left a comment
There was a problem hiding this comment.
It seems a second common-src was added by mistake.
|
CI Build: #22 | Failed on the following stages:
Git operation failure or internal CI error |
8f914da to
1c08586
Compare
|
CI Build: #26 | Failed on the following stages:
Git operation failure or internal CI error, likely conflicts with develop; please resolve it. |
Classifies SMF Npcf_SMPolicyControl_UpdateNotify responses per TS 29.512 Table 5.7.3-2 (smf_notify_outcome: applied/temporary_rejection/ permanent_rejection/transport_ambiguous) instead of the previous 204-blind, cause-blind handling, and acts on the classification instead of just logging it: - SM-side bounded retry-drain queue (retry_drain_queue) for temporary/ ambiguous outcomes, drained off the now-actually-started task_tick heartbeat (task_manager was constructed nowhere before this). - PA-side pending_rollback_tracker recording what each commit would need to undo, populated at apply_with_retry's existing commit point. - A new SM->PA signal (sm_policy_update_failed) fired only on a confirmed permanent rejection, driving a compensating-delta rollback through apply_with_retry's own CAS-retry loop, filtered by a per-key staleness check (compute_rollback_delta) so a key touched by a later, unrelated commit is left alone rather than orphaned. - notify_af_qos_update_failed: Phase 3 stub, logs only. - retry-drain/rollback-tracker TTL, cap, and retry/backoff are config- tunable (pcf.notify_failure_recovery), not hardcoded. Fixes two bugs found during implementation, both covered by regression tests: a stale pre-commit snapshot fed to apply_with_retry's CAS loop that silently no-op'd the rollback whenever nothing else touched the association first (added sm_get_association_decision to fetch live state, extracted perform_compensating_rollback so the "always fetch fresh" contract is directly testable); and an uncaught nlohmann::json exception on an empty/non-object SMF error body, found while extracting the classification logic into its own testable, HTTP-free function (classify_smf_notify_response). 225 tests passing (21 new: response classification, retry-drain queue, pending-rollback tracker, rollback delta computation, rollback orchestration). Deferred, tracked separately: distinguishing connection-failure from timeout in http_client (needs a cross-repo oai-cn5g-common-src change); a 200 response shaped as array<PolicyDecisionFailureCode> is currently misclassified as success . Assisted-By: Claude:claude-5-sonnet Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
Extracts the CAS-retry loop into policy_auth::apply_decision_with_retry (sm_update_decision injected, pending_rollback_tracker passed directly since it's cheap and already tested). apply_with_retry is now a thin wrapper -- no behavior change for any of its 4 call sites. Also fixes a stale doc comment (exhaustion returns FORBIDDEN, not INTERNAL_SERVER_ERROR, since the earlier §11.1 fix). Adds test coverage notify_failure_recovery_config/builder were missing, matching their qos_authorization_config/ operator_qos_policy_builder siblings. 236 tests passing (11 new). Assisted-By: Claude:claude-5-sonnet Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
…osChars tests getPccRules()/getTraffContDecs()/getQosChars() return maps by value, so calling them twice (once for .find(), again for .end()) compares an iterator into an already-destroyed temporary against a different temporary's end() -- undefined behavior whose outcome depends on whether the compiler reuses the same stack slot for both temporaries. validate_and_merge_decision's duplicate PCC-rule-ID and Traffic- Control-ID rejection checks relied on this by luck; a toolchain change could silently flip accept/reject. Hoist each map into one local before comparing, which also drops the redundant per-key map copy. Same fix applied to the six QosCharacteristics tests exercising the same pattern, found via the same audit that turned up the ApplyDecisionWithRetry CI failure (52871a0). Assisted-By: Claude:claude-5-sonnet Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
…ns for decision updates and rollbacks Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
…d cause constants for clarity Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
Extract decision_applier (CAS-retry loop) and qos_deriver out of pcf_policy_authorization/app_session so sm_update_decision, the pending_rollback_tracker, and QoS policy/reference stores are bound once at construction instead of threaded per-call. Inject an http_send_fn seam into pcf_smpc so SMF-notify sends are mockable without touching the non-virtual http_client submodule type. Split pcf_smpc's monolithic update-decision handling into handle_commit_decision_request (CAS + persist only) and handle_notify_committed_decision_request (re-fetch under lock, notify, classify, return outcome directly). This removes the prior race where pending_rollback_tracker recording happened after sm_update_decision returned while a permanent-rejection signal could already have fired synchronously inside that same call. Policy Authorization's new push_decision_change is now the single choke point that commits, notifies, and triggers compensate_if_pending (renamed from handle_sm_policy_update_failed) on a permanent rejection -- with no callback passed into SM and no attempt_id/staging needed. Also fix pcf_sm_policy_control.hpp pulling in the heavy http_definitions.hpp (cpr/curl/fmt/nlohmann) for just one type alias; api-server's CMakeLists.txt has no include path for it, breaking the PCF_API Docker build target. Swap to the lightweight 3gpp_29.500.h and forward-declare oai::http::request/response instead. Assisted-By: Claude:claude-5-sonnet Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
…handling and logging Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
Fold notify_failure_recovery_policy.hpp/_builder.* and
operator_qos_policy.hpp/_builder.* (6 files) into a new
pcf_runtime_policy.{hpp,cpp}: both were single, always-built-together
collaborators for the runtime-policy aggregates pcf_app.cpp builds once
and injects into pcf_smpc/policy_auth_context, so the split forced
pcf_app.cpp, pcf_sm_policy_control.hpp, qos_session_authorization.hpp,
policy_auth_context.hpp, and qos_deriver.hpp to each pull in multiple
headers for what's really one seam.
Fold pending_rollback_tracker.{hpp,cpp}, rollback_delta.{hpp,cpp}, and
rollback_orchestration.{hpp,cpp} into decision_applier.{hpp,cpp}: the
four collaborators (pending_rollback_tracker, decision_applier,
compute_rollback_delta, perform_compensating_rollback) are one
dependency chain with no external consumer of the intermediate links.
Fold app_session_record.hpp into app_session.hpp, qos_types.hpp into
qos_context.hpp, qos_derivation_helpers.hpp into qos_deriver.hpp,
policy_authorization_causes.hpp into
pcf_policy_authorization_status_code.hpp, qos_reference_loader.hpp
into qos_reference_store.hpp, and smf_notify_causes.hpp into
smf_notify_response_classifier.hpp -- each was a single-consumer header
(one struct or a couple of cause constants) whose one caller already
includes a natural host.
Per N5_QoS_Refactor_File_Consolidation_Plan.md: "A header earns its own
file when it has independent consumers or is an independent seam."
Pure move -- no behavior change; only include paths,
CMakeLists.txt's add_library(PCF ...) source list, and test #includes
updated. 18 files removed, 2 added (pcf_runtime_policy.{hpp,cpp}).
Assisted-By: Claude:claude-5-sonnet
Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
Adds an Unreleased CHANGELOG section for the N5 Policy Authorization service, QoS derivation, the N7 UpdateNotify path and the new test infrastructure. Corrects FEATURE_SET.md: N5 becomes partially supported (app-session lifecycle works, AF notification does not), the "UpdateNotify feature not supported" footnote is dropped now that it is implemented, and rows 1 and 9 are updated since AF service information feeds PCC decisions. Adds :heavy_minus_sign: with a legend for partial support. Assisted-By: Claude:claude-5-opus Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
Comment-only; no functional change. The two reworded warn lines in rollback_map() are the only observable difference. Takes the Phase 1/2 TODO blocks from 82 to 30: deletes blocks describing work that is now done, deletes the commented-out coordination signal typedefs (recording why they were dropped -- validation runs in-process and the commit is the coordination point), and collapses the AF-notification and monitoring blocks to short phase/clause pointers. Fixes two comments that misdescribed the code: authorize_service_info() listed QoS authorization as a TODO there when it runs in qos_deriver, and setup_qos_monitoring() was labelled a mock when it is a no-op. Rewrites the rollback_map() note, which claimed the per-key staleness skip avoids orphaning a dependent PCC rule when it can cause one -- and perform_compensating_rollback()'s derive never calls validate_policy_decision(), so the inconsistent result is committed and notified to the SMF. Behaviour unchanged; the fix needs its own tests. Assisted-By: Claude:claude-5-opus Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
- Add missing #endif in SlicePolicyDecision.h (root cause of the reported error) - Fix stale oai::model namespace (-> oai::_3gpp::model) across pcf_app - Stub out unimplemented SFC handling (AfSfcRequirement was removed) - Fix assorted typos/bugs surfaced along the way (const-void, to_string, etc.) - Fix stale cmake paths and missing include dir blocking the checks test target Assisted-By: Claude:claude-5-sonnet Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
…ription data Signed-off-by: Tariro Mukute <tariro.mukute@phine.tech>
Explicitly set packetFilterUsage=true in flow_info_from_desc() for all PCF-generated FlowInformation objects. This ensures the N7 SmPolicyDecision payload explicitly carries packetFilterUsage=true for dynamic N5 (AF) flows, preventing the downstream SMF N1 NAS encoder from dropping the flow filter and omitting the rule in PduSessionModificationCommand Signed-off-by: Lukas Rotheneder <lukas.rotheneder@phine.tech>
1c08586 to
b0b15a2
Compare
|
CI Build: #29 | Failed on the following stages:
Git operation failure or internal CI error, likely conflicts with develop; please resolve it. |
|
@tariromukute @phine-lukas please rebase your branch on latest develop and resolve any merge conflicts. |
Signed-off-by: Lukas Rotheneder <lukas.rotheneder@phine.tech>
b0b15a2 to
e6d500c
Compare
|
CI Build: #30 | Failed on the following stages: |
Signed-off-by: Lukas Rotheneder <lukas.rotheneder@phine.tech>
|
CI Build: #31 | Failed on the following stages: |
Signed-off-by: Lukas Rotheneder <lukas.rotheneder@phine.tech>
|
CI Build: #32 | Failed on the following stages: |
|
CI Build: #33 | Failed on the following stages: |
|
CI Build: #34 | Failed on the following stages: Validation: SHA recognized in 99a0222, using "origin/feat-n5-qos-support" as branch name Please use 'git commit -s' or 'git commit --signoff' to sign your commits. For detailed instructions, refer to the CONTRIBUTING file at the root of this repository. |
Sanitize ARP priorityLevel in SessionRule and QosData decisions to ensure it falls within the valid 3GPP TS 38.413 range (1-15). Defaults invalid (0) values to 1 to prevent SMF NGAP encoding failures. Signed-off-by: Lukas Rotheneder <lukas.rotheneder@phine.tech>
99a0222 to
fc1c223
Compare
Merge Requests on GitLab
Description
The use case: an Application Function asks the 5G core for better-than-best-effort treatment for one media flow — "4 Mbps down, guaranteed, under 150 ms" — and the network delivers it on a PDU session (an established user data connection) that is already up, without interrupting the user's traffic.
Structurally the PCF is a policy decision service: an HTTP/2 JSON API (N5) over one CRUD resource (an app-session = one AF quality request for one flow), which validates against operator limits and the subscriber's contract, translates into a policy document, attaches it to the right per-user session record, and pushes the result over a second HTTP/2 call (N7) to the SMF, which programs the data plane (UPF).
Steps [2]-[4] are pure, which is what makes the retry at [5] safe.
PATCHandDELETEwalk the same six steps:PATCHre-derives the samemedCompN(the AF's own media-component key, reused as a stable id) so a repeat request upserts rather than duplicates;DELETEderives a removals-only delta from the session's ledger.Two properties shaped the design:
SmPolicyDelta(only the keys it touched) applied under an optimistic version-CAS (compare-and-swap), re-deriving against the committed base on conflict (step [5]).Legend: ✅ real · 🟡 partial · 🔶 mock · ⬜ deferred to a later MR in this series
Phase 1 — Core QoS Processing (
[QOS])sm_session_binding) — resolves which established user session an AF request applies to, keyed on UE IP / subscriber id / data-network name.handle_qos_requirements()— templated overMediaComponent(create) andMediaComponentRm(PATCH), the generated create/update variants of one wire object, so there is one implementation rather than two that drift.create_qos_data_from_media_component()— realQosData+PccRuleper TS 29.513 §7.3.3: MBR (ceiling) frommarBw/summed per-SDF, GBR (floor) frommirBw, 5QI from a latency heuristic, SDF filters frommedSubComps— or the operator's preset whenqosReferenceresolves. DeterministicmedCompN-keyed ids, so aPATCHmodifies in place with precedence reused, not reassigned.resPrio→ ARPpriorityLevelis not mapped (see Known Issues).create_qos_characteristics()— real entry (priority level, packet delay budget, packet error rate, resource type) for non-standardized 5QIs; a no-op for standardized ones, whose meaning every downstream node already knows.validate_qos_authorization()— dynamic-5QI allow-list, ARP range, per-flow MBR cap, cumulative non-GBR vs. authorized Session-AMBR, GBR≤MBR. The one real policy call: when the network never sent the user's bandwidth budget, allow or deny? Configurable, defaults to allow per the spec's own wording.validate_and_merge_decision()—insert_or_assignso other app-sessions' entries survive, plus referential-integrity checks (no danglingrefQosData/refTcData, no duplicate ids, unambiguous precedence).validate_policy_decision()— pre-notification gate on both create andPATCH; a self-inconsistent decision is our bug (500), never sent downstream.PATCH(§1.6) — modify in place, add, or remove viafStatus: REMOVED(including one sub-component of a retained component); all three re-run create's authorization/merge/validation gates.ascReqData(merge_patch_context()) — scalars replaced, components and sub-components merged/added/removed, so aGETafter aPATCHreflects the change.GET /app-sessions/{id};POST→201+Location+ negotiatedascRespData.suppFeat;PATCH→200+ merged context;Content-Typeenforced on every body-carrying endpoint (415otherwise).app_sessionaggregate root with aqos_contextledger holding identifiers only — theQosData/PccRulepayload's single source of truth stays the association's decision, so the two cannot drift. The ledger is written only after a successful commit; derivation targets a scratch object, so a rejected or retried attempt leaves no trace.app_session_storageover a generic injectablecrud_store— restart-safe UUID ids, 1:Nassociation_id→ app-sessions index.app_session_recordfixes the durable schema now so MR 3 is a backend swap at this seam; this MR ships the in-memory backend.{PUT,DELETE} /app-sessions/{id}/events-subscription, §4.2.6) still return404— held for MR 2, since nothing consumes a subscription until the notification infrastructure exists.Phase 2 — Cross-Service Coordination (
[QOS-SMF])The PCF's two internal services (Policy Authorization owns the AF-facing API; SM Policy Control owns the session records and the downstream call) communicate over an in-process
boost::signals2event bus rather than calling each other directly.SmPolicyDelta— incremental change set overqosDecs/pccRules/qosChars/traffContDecs, withcompute_sm_policy_delta()/apply_sm_policy_delta(). Pushing a delta rather than a whole decision removes the lost-update race on concurrentPATCH: unchanged keys are omitted, so an apply only touches what that request changed. Same reasoning asPATCHrather thanPUTon a shared resource.handle_commit_decision_request()applies the delta copy-on-write under the association lock only if still at that version, else returns current version + decision for the caller to re-derive (decision_applier::apply, 3 attempts). Closes two races: the ordinary lost update, and the subtler one where two requests each pass a cumulative check against the same stale snapshot — two AFs requesting 60 Mbps each against a 100 Mbps Session-AMBR can no longer both succeed.send_sm_policy_control_update_notify()— delivers the full decision (pccRules,qosDecs,qosChars), confirmed against a live SMF. Full-not-delta is deliberate, not an oversight: the delta is PCF-internal because the partner SMF diffs the full object itself and a partialSmPolicyDecisionon the wire is an interop hazard. Internally incremental, externally complete.classify_smf_notify_response()— maps (HTTP status, body) toapplied/permanent_rejection(onlyPCC_RULE_EVENT, the one cause proving a retry is futile) /temporary_rejection/transport_ambiguousper Table 5.7.3-2. Handles the awkward200-with-PartialSuccessReport(success status, failure body) and defaults unmodeled statuses to ambiguous. Pure function, so the whole matrix is unit-tested without mocking HTTP.retry_drain_queue— re-attempts temporary/ambiguous notifies off thetask_tickheartbeat with exponential backoff, per-entry TTL and a hard cap; each attempt re-fetches live state. Never rolls back. Both bounds present so it cannot grow or retry forever.pending_rollback_trackerrecords what each commit would undo;compute_rollback_delta()reverts a key only if unchanged since that commit (else skipped and logged), which stops a blind revert orphaning a rule a later request depends on;perform_compensating_rollback()always re-reads live state, never the tracker's stale snapshot. The rollback re-commits through the same commit+notify path — one path, no special case to keep in sync.sm_policy_update_failedfrom the drain) converge on onecompensate_if_pending(), so there is nothing to race.pcf.notify_failure_recovery); post-commit snapshot persisted to policy storage off-lock; association decision version-bumped copy-on-write.notify_af_qos_update_failed()— logs-only stub kept on the rollback path so the call site, arguments and tests are ready for MR 2 to fill in.qosMonDecs(Phase 4, MR 2).Tested Environment (e.g., radios, interfaces, CN)
Full OAI 5GC under Docker Compose from
oai-cn5g-fed(docker-compose-basic-nrf-qos.yaml+docker-compose-ueransim-qos.yaml): NRF, UDR, UDM, AUSF, AMF, SMF (oai-smf:qos-mod), UPF (oai-upf:fix-pfcp-mod), this PCF, UERANSIM gNB + 2 UEs, curl-based AF container.BUILD_TESTING=ON→ thepcf_unit_teststarget, discovered by CTest). Build via the Dockerchecksstage if a native build hits the pre-existing missing-<cstdint>issue in the common submodule.pa_app_session_tests.py, 19/19 assertions passing against the live stack. New in this MR; walks the full app-session lifecycle (create → read → modify in place / add / remove → delete) plus a deliberately invalid modification that must be rejected without side effects.Configuration Updates (e.g., files, parameters)
All blocks optional; omitting one keeps documented defaults.
etc/config.yaml→pcf.local_policy.qos_reference_path(operator QoS presets, e.g.OAI_QOS_GBR_VIDEO_1).etc/config.yaml→pcf.qos_authorization:allowed_dynamic_5qi,max_flow_mbr_ul/dl,max_session_ambr_ul/dl,reject_on_missing_subscription. Defaults permissive (no cap / any 5QI / fail-open).etc/config.yaml→pcf.notify_failure_recovery:retry_drain_ttl_seconds,retry_drain_max_entries,max_notify_retries,retry_backoff_initial_ms,rollback_tracker_ttl_seconds,rollback_tracker_max_entries. Unprescribed by TS 29.512/29.514, so operator-tunable; defaults 30 s TTLs, 10 000-entry caps, 3 retries, 500 ms backoff doubling.etc/policies/qos_references/oai_qos_references.yaml. Configured aspcf.local_policy.qos_reference_path(default: /openair-pcf/policies/qos_references)
-DUSE_ODB(defaultOFF) gates all ODB/MySQL code and linkage.Known Issues / Limitations
Scheduled — resolved by a later MR in this series
Npcf_SMPolicyControl_UpdatecarryingruleReportsis accepted and discarded, so an SMF that accepts our notify and only later fails to install the rules leaves the PCF believing the QoS is active, with no rollback and no AF notification. TS 29.512 §4.2.4.15, §4.2.4.7 (ruleStatus=INACTIVE,failureCode=RES_ALLO_FAIL), carried in §5.6.2.19; AF-facing half is TS 29.514 §4.2.5.8Accepted — remain until a future refactor, feature, or upstream fix
1000 + <monotonic uid>, so past 1000 rules it exceeds1000-1999. Harmless today (nothing enforces the SM-side band either).403), never resolved by pre-empting a lower-priority service.QosDataplus aPccRulereferencing it and another writer later changed only the rule, the rollback removes theQosDataand skips the rule, leaving a dangling reference — andperform_compensating_rollback()'s derive does not callvalidate_policy_decision()(unlike the POST/PATCH derives), so that decision is committed and sent to the SMF. Needs the pre-notification gate on the rollback path plus a reference-aware pruning pass. Requires concurrent writers and a confirmed permanent rejection, so it is rare today and largely masked by the408row below; expect it to become reachable once that is fixed.PartialSuccessReportread shallowlyfailureCauseroutes retry-vs-rollback, so a partial failure is attributed to the whole commit rather than the rules the SMF named. TS 29.512 §4.2.3.16; types at §5.6.2.27 / §5.6.3.8 / §5.6.3.9.resPrio→ ARPpriorityLevelnot mappedReservPrioritymodel is empty (an upstreamoai-cn5g-common-srcgenerator defect, not introduced here), and TS 29.513 Table 7.3.2-1 leaves the mapping itself undefined ("application specific algorithm").preemptCap/preemptVulnare unaffected.minDesBwDl/Ulnot readIMS_SBIfeature (TS 29.514 Table 5.8-1), which this PCF does not negotiate — and that feature also covers unrelated charging behaviour.desMaxLossnot readQoSHint/FLUSapplicability, also unnegotiated, and the spec prescribes no mapping formula. Should be taken on together withdesMaxLatency(read today as best-effort under the same gate).Checklist
CHANGELOG.md,docs/FEATURE_SET.md— N5 now partially supported, N7 UpdateNotify supported — andci-scripts/tests/README.mdfor the new lifecycle script)Log files or packet captures (PCAPs)
Additional Notes
The QoS feature on PCF is split into three MRs.
Why the split, and why these seams. The original branch carried four phases plus persistence in ~40 commits — not reviewable in one pass. Each MR is cut where the code already has a seam, so none leaves a half-built abstraction:
notify_af_qos_update_failed(),setup_qos_monitoring(), the commented-out AF/monitoring signal typedefs inpcf_event_sig.hpp, and theaf()/mon()aspect slots reserved onapp_session.crud_storebackend injected inpcf_app.cppagainst this MR'sapp_session_record. Last on purpose: the schema should be shaped by all the state Phases 3-4 add, not migrated twice.