qos β€” Quality of Service Memory Policies

Scheduling and prioritization framework for memory controllers β€” enabling fair, differentiated, and QoS-aware memory access across multiple requestors.

21
Files
2
Scheduling Policies
1
Turnaround Policy
C++
Language
Multi-tenant
Target Workload
Table of Contents

    Overview

    📚 What is this & why does it exist?

    Analogy β€” Airport Priority Lanes

    Think of the QoS memory system as an airport with multiple priority lanes. Economy passengers (low-priority requestors) and business class passengers (high-priority requestors) both need to get through security (the memory controller). The Fixed Priority policy always lets business class go first. The Proportional Fair policy gives everyone a fair share based on their needs β€” if an economy passenger has been waiting a long time, they get bumped up. The Turnaround Policy decides when to switch the security lanes from departures (reads) to arrivals (writes) and back, minimizing the overhead of switching direction at the DRAM bus.

    The qos/ directory provides a QoS framework for gem5 memory controllers. It sits between the Cache Hierarchy | gem5 Wiki" href="../cache/index.html">cache hierarchy and the DRAM/NVM backend, enabling differentiated memory service based on requestor identity, priority, or fairness objectives.

    The framework consists of three key abstractions:

    • QoS Memory System | gem5 Wiki" class="xref">MemCtrl β€” QoS-aware memory controller base class that integrates policies into the scheduling pipeline
    • QoS Policy β€” Priority assignment algorithm (Fixed Priority, Proportional Fair)
    • Turnaround Policy β€” Read/write bus direction switching policy
    • Queue Policy β€” Per-requestor queue management and admission control

    Architecture

    Click to expand
    flowchart TD
        CPU0["CPU 0\n(Requestor 0)"]
        CPU1["CPU 1\n(Requestor 1)"]
        GPU["GPU\n(Requestor 2)"]
        CACHE["Cache Hierarchy"]
        XBAR["Coherent XBar"]
        QMC["QoS MemCtrl"]
    
        subgraph QoS_Pipeline["QoS Pipeline"]
          POLICY["Priority Policy\n(FixedPrio / PF)"]
          QPOL["Queue Policy\n(Admission Control)"]
          SCHED["Request Scheduler"]
          TP["Turnaround Policy"]
        end
    
        READQ["Read Queue"]
        WRITEQ["Write Queue"]
        DRAM["DRAM Interface"]
    
        CPU0 --> CACHE
        CPU1 --> CACHE
        GPU --> CACHE
        CACHE --> XBAR
        XBAR --> QMC
        QMC --> POLICY
        POLICY --> QPOL
        QPOL --> SCHED
        SCHED --> TP
        TP --> READQ
        TP --> WRITEQ
        READQ --> DRAM
        WRITEQ --> DRAM
      

    QoS Request Flow

    Click to expand
    sequenceDiagram
        participant REQ as Requestor
        participant MC as QoS MemCtrl
        participant POL as Policy
        participant QP as Queue Policy
        participant TP as Turnaround Policy
        participant DRAM as DRAM
    
        REQ->>MC: Memory Request (pkt)
        MC->>POL: schedule(requestorId, pkt)
        POL-->>MC: priority level
        MC->>QP: enqueue(pkt, priority)
        QP-->>MC: accepted / rejected
        Note over MC: Request placed in
    priority-ordered queue MC->>TP: selectBusState() TP-->>MC: READ or WRITE MC->>DRAM: issue(pkt) DRAM-->>MC: response MC-->>REQ: Response (pkt)

    Narrated Walkthrough: QoS-Aware Memory Access

    Request arrives β€” A memory request arrives at the QoS-aware memory controller from the crossbar. The packet carries a requestorId identifying the source (CPU 0, CPU 1, GPU, etc.).
    Priority assignment β€” The controller calls Policy::schedule() to determine the request's priority. With FixedPriority, priority is a static mapping from requestorId. With ProportionalFair, priority is dynamically adjusted based on each requestor's recent bandwidth consumption.
    Queue admission β€” The QueuePolicy decides if the request can enter the queue. It may reject low-priority requests when the queue is congested, or reorder entries to maintain priority ordering.
    Scheduling β€” The scheduler selects the next request to issue from the read or write queue. Higher-priority requests are served first. Within the same priority level, FR-FCFS (First-Ready First-Come-First-Serve) is typically used.
    Bus turnaround β€” The TurnaroundPolicy determines whether to switch the DRAM bus between read and write mode. The IdealTurnaround policy minimizes bus turnaround overhead by batching reads and writes.
    DRAM access β€” The request is issued to the DRAM interface, which models the detailed timing of activate, read/write, and precharge commands.
    Pipeline β€” gem5 Wiki" class="xref">Statistics update β€” The controller updates per-requestor statistics: bandwidth consumed, latency, and priority distribution. This feeds back into Proportional Fair priority adjustment.

    Key Classes Reference

    ClassFileDescription
    qos::MemCtrlmem_ctrl.hhQoS-aware memory controller base class. Extends ClockedObject and integrates QoS policies into request scheduling. Maintains per-requestor priority mappings, read/write queues with priority ordering, and detailed per-requestor statistics. All QoS-enabled memory controllers inherit from this.
    qos::MemSinkCtrlmem_sink.hhAtomic and Timing Simple CPUs β€” gem5 Wiki" href="../../cpu_doc/simple/index.html">Simple QoS memory controller implementation for testing. Acts as a "sink" that consumes requests with configurable latency β€” useful for isolating QoS policy behavior from DRAM timing complexity.
    qos::Policypolicy.hhAbstract base class for QoS scheduling policies. Defines the schedule(requestorId, pkt) interface that maps a request to a priority level. Subclasses implement specific priority assignment algorithms.
    qos::FixedPriorityPolicypolicy_fixed_prio.hhStatic priority mapping β€” each requestor is assigned a fixed priority level at configuration time. Simple and deterministic; suitable when workload priorities are known a priori.
    qos::PropFairPolicypolicy_pf.hhProportional Fair scheduling β€” dynamically adjusts priority based on each requestor's recent bandwidth usage. Requestors that have consumed less bandwidth relative to their "fair share" receive higher priority. Balances throughput and fairness.
    qos::QueuePolicyq_policy.hhQueue management policy controlling admission and ordering within the read/write queues. Can enforce per-requestor queue limits to prevent monopolization.
    qos::TurnaroundPolicyturnaround_policy.hhAbstract base for read/write bus direction policies. Determines when to switch the DRAM data bus between reading and writing to minimize turnaround penalty.
    qos::TurnaroundPolicyIdealturnaround_policy_ideal.hhIdeal turnaround policy that switches bus direction with minimal overhead β€” serves as an upper bound for turnaround efficiency.

    Scheduling Policies Deep Dive

    Fixed Priority Policy

    The simplest QoS policy: each requestor has a statically configured priority level. The memory controller always serves higher-priority requestors first.

    Click to expand
    flowchart LR
        REQ["Incoming Request"]
        MAP["Priority Map\n(requestorId β†’ priority)"]
        PRI["Assigned Priority"]
        Q["Priority Queue"]
    
        REQ --> MAP
        MAP --> PRI
        PRI --> Q
    
        MAP -.- CFG["Configuration:\nCPU0 β†’ 3 (high)\nCPU1 β†’ 2\nGPU β†’ 1 (low)"]
      

    Characteristics:

    • Zero runtime overhead β€” priority is a table lookup
    • Deterministic behavior β€” easy to reason about
    • Risk of starvation for low-priority requestors under heavy load
    • Best for: systems with well-defined priority hierarchies (e.g., real-time CPU over background GPU)

    Proportional Fair Policy

    A dynamic policy inspired by wireless networking's proportional fair scheduling. Each requestor's priority is inversely proportional to its recent bandwidth consumption.

    Click to expand
    flowchart TD
        REQ["Incoming Request"]
        HIST["Bandwidth History\n(per requestor)"]
        SCORE["Score = Weight / Recent_BW"]
        RANK["Rank by Score"]
        PRI["Priority Assignment"]
    
        REQ --> HIST
        HIST --> SCORE
        SCORE --> RANK
        RANK --> PRI
      

    Characteristics:

    • Adapts to changing workload behavior at runtime
    • Prevents starvation β€” underserved requestors naturally receive higher priority
    • Configurable weight per requestor to bias the "fair share"
    • Tracks bandwidth over a sliding time window
    • Best for: multi-tenant workloads where fairness matters

    Turnaround Policy

    DRAM bus direction switching is expensive (typically 5-15ns turnaround penalty). The turnaround policy batches reads and writes to minimize switches:

    • IdealTurnaround β€” Switches only when the current direction's queue is empty or a high-priority request in the other direction is waiting. Minimizes turnaround count.
    • Custom policies can implement hysteresis, threshold-based switching, or age-based urgency.

    Queue Management

    The QueuePolicy controls how requests are admitted into and ordered within the memory controller's queues:

    AspectMechanismPurpose
    Per-requestor limitsMaximum queue entries per requestorIdPrevents one requestor from monopolizing the queue
    Priority orderingRequests sorted by assigned QoS priorityEnsures higher-priority requests are served first
    Admission controlReject/stall low-priority requests when queue is fullBack-pressure to prevent buffer overflow
    Separate read/writeIndependent queues for reads and writesEnables read-priority or write-draining modes

    QoS Statistics

    The QoS framework collects detailed per-requestor statistics:

    StatisticDescription
    avgPriorityAverage priority level assigned to each requestor
    readBW / writeBWBandwidth consumed by each requestor (reads and writes separately)
    totalReadLatency / totalWriteLatencyCumulative latency per requestor
    numReads / numWritesRequest count per requestor
    busStateSwitchNumber of read↔write bus direction switches
    priorityDistributionHistogram of assigned priority levels per requestor
    queueOccupancyAverage queue depth per requestor

    Configuration Examples

    // Python configuration: Fixed Priority QoS
    system.mem_ctrl = MemCtrl(
        dram = DDR4_2400_16x4(),
        qos_policy = QoSFixedPriority(
            # CPU cores get high priority, GPU gets low
            qos_priorities = {
                'system.cpu0': 3,
                'system.cpu1': 3,
                'system.gpu':  1,
                'system.dma':  0
            }
        ),
        qos_turnaround_policy = QoSIdealTurnaround()
    )
    // Python configuration: Proportional Fair QoS
    system.mem_ctrl = MemCtrl(
        dram = DDR4_2400_16x4(),
        qos_policy = QoSPropFairPolicy(
            # Weight each requestor's fair share
            weight = {
                'system.cpu0': 2.0,   # Double share
                'system.cpu1': 1.0,   # Normal share
                'system.gpu':  0.5    # Half share
            },
            # Sliding window for bandwidth tracking
            scoring_interval = '10us'
        ),
        qos_turnaround_policy = QoSIdealTurnaround()
    )
    // C++ internals: Policy interface
    namespace qos {
    
    class Policy : public SimObject {
      public:
        // Called for each incoming memory request
        // Returns priority level (0 = lowest, N = highest)
        virtual uint8_t schedule(const RequestorID id,
                                 const uint64_t data) = 0;
    };
    
    class FixedPriorityPolicy : public Policy {
        // priority_map: RequestorID -> priority level
        std::map<RequestorID, uint8_t> priority_map;
    
      public:
        uint8_t schedule(const RequestorID id,
                         const uint64_t data) override {
            auto it = priority_map.find(id);
            return (it != priority_map.end()) ? it->second : 0;
        }
    };
    
    } // namespace qos

    Cross-Module Connections

    Click to expand
    flowchart LR
        subgraph Cache["mem/cache/"]
          BC["BaseCache"]
        end
        subgraph QoS["mem/qos/"]
          QMC["qos::MemCtrl"]
          POL["qos::Policy"]
          TP["TurnaroundPolicy"]
          QP["QueuePolicy"]
        end
        subgraph MemCtrl["mem/"]
          MC["MemCtrl"]
          DI["DRAMInterface"]
        end
        subgraph Core["sim/"]
          CO["ClockedObject"]
        end
    
        CO --> QMC
        QMC --> POL
        QMC --> TP
        QMC --> QP
        BC -->|"Writeback / Miss"| QMC
        QMC --> MC
        MC --> DI
      

    The QoS framework integrates at the memory controller level:

    • Upstream: Receives requests from the cache hierarchy via ResponsePort
    • Policies: Pluggable policy objects attached to the controller
    • Downstream: Issues scheduled requests to DRAMInterface or NVMInterface
    • Base class: qos::MemCtrl extends ClockedObject and can be used as a base for custom memory controllers

    Configuration Reference

    ParameterSimObjectDescriptionDefault
    qos_policyQoSMemCtrlQoS scheduling policy objectNone (no QoS)
    qos_turnaround_policyQoSMemCtrlBus turnaround policy objectNone
    qos_q_policyQoSMemCtrlQueue management policyNone
    qos_prioritiesFixedPriorityPolicyMap of requestor β†’ priority levelAll priority 0
    weightPropFairPolicyPer-requestor fair-share weight1.0
    scoring_intervalPropFairPolicyBandwidth measurement windowSystem-dependent
    read_buffer_sizeQoSMemCtrlRead queue capacity32
    write_buffer_sizeQoSMemCtrlWrite queue capacity64
    write_high_threshQoSMemCtrlWrite drain trigger threshold (%)85%
    write_low_threshQoSMemCtrlWrite drain stop threshold (%)50%

    Directory Map

    src/mem/qos/ β”œβ”€β”€ mem_ctrl.hh / mem_ctrl.cc # QoS MemCtrl β€” QoS-aware controller base β”œβ”€β”€ mem_sink.hh / mem_sink.cc # MemSinkCtrl β€” test/benchmark sink β”œβ”€β”€ policy.hh / policy.cc # Policy β€” abstract QoS policy base β”œβ”€β”€ policy_fixed_prio.hh / .cc # FixedPriorityPolicy β€” static priorities β”œβ”€β”€ policy_pf.hh / .cc # PropFairPolicy β€” proportional fair β”œβ”€β”€ q_policy.hh / q_policy.cc # QueuePolicy β€” queue management β”œβ”€β”€ turnaround_policy.hh # TurnaroundPolicy β€” abstract base β”œβ”€β”€ turnaround_policy_ideal.hh / .cc # IdealTurnaround β€” minimal switching β”œβ”€β”€ QoSMemCtrl.py # Python SimObject: QoS MemCtrl β”œβ”€β”€ QoSMemSinkCtrl.py # Python SimObject: MemSink β”œβ”€β”€ QoSMemSinkInterface.py # Python SimObject: MemSink Interface β”œβ”€β”€ QoSPolicy.py # Python SimObject: Policies β”œβ”€β”€ QoSTurnaround.py # Python SimObject: Turnaround └── SConscript # Build configuration

    Glossary

    TermDefinition
    RequestorA unique source of memory requests, identified by RequestorID. Typically one per CPU core, GPU CU, or DMA engine.
    Priority LevelInteger value (0 = lowest) assigned to requests by the QoS policy. Higher-priority requests are scheduled before lower ones.
    Proportional FairScheduling algorithm that balances fairness and throughput by favoring requestors that have recently received less service.
    Bus TurnaroundThe time penalty incurred when switching the DRAM data bus direction between reads and writes (typically tWTR or tRTW).
    FR-FCFSFirst-Ready First-Come-First-Served β€” common DRAM scheduling policy that prioritizes row-hit requests, then by arrival order.
    Write DrainMode where the controller exclusively processes writes to free write buffer space, triggered when the write queue exceeds a threshold.