@@ -42,41 +42,89 @@ class DeviceAssignment;
4242
4343namespace stream_executor {
4444
45- // Abstract base class for virtual memory map (VMM) allocators that separate
46- // virtual address reservation from physical memory allocation. It can be bound
47- // to one or more GPU devices at construction time and routes
48- // Allocate/Deallocate calls to per-device state based on device_ordinal.
45+ // Abstract base class for a DeviceAddressAllocator backed by virtual memory
46+ // management (VMM). VMM lets the allocator manage device memory in three
47+ // separate steps:
4948//
50- // Concurrency model: each device has its own absl::Mutex, so operations on
51- // different devices run fully in parallel. The per-device map is populated
52- // entirely at construction and never modified afterward, so lookups require no
53- // global lock.
49+ // 1. Allocate raw physical memory. This is the real device memory capacity.
50+ // 2. Reserve a virtual address (VA) range. This creates addresses but does not
51+ // make them usable yet.
52+ // 3. Map a VA range to raw physical memory. Device kernels access memory
53+ // through the mapped VA.
5454//
55- // For each device the allocator:
56- // 1. Allocates physical memory via MemoryAllocation (e.g. cuMemCreate).
57- // 2. Reserves virtual address space via MemoryReservation
58- // (e.g. cuMemAddressReserve).
59- // 3. Maps physical memory to virtual address via
60- // MemoryReservation::ScopedMapping (e.g. cuMemMap + cuMemSetAccess), which
61- // automatically unmaps on destruction.
55+ // A concrete subclass provides the platform-specific operations for those
56+ // steps, plus a stream-ordered timeline used to know when old mappings and
57+ // allocations are safe to release.
6258//
63- // The allocator tracks the ScopedMapping and underlying MemoryAllocation and
64- // MemoryReservation objects for each returned DeviceAddressBase. Callers can
65- // retrieve these via GetRawAllocation() and GetReservation(). Callers can also
66- // create non-owning aliases into caller-owned MemoryReservation ranges with
67- // Map(), then release those aliases with UnMap().
59+ // Caller-visible address roles:
6860//
69- // This allocator supports asynchronous deallocation: when Deallocate() is
70- // called, it records a GPU timeline write on the device's stream and defers
71- // the actual deallocation until the GPU reaches that point in the stream. This
72- // allows callers to deallocate memory while device kernels may still be
73- // consuming the data.
61+ // * Allocator address: any address returned by Allocate(). It owns the raw
62+ // physical allocation, can be used as the source address for Map(), and must
63+ // eventually be released with Deallocate().
64+ // * Reservation address: a caller-owned MemoryReservation slice
65+ // [reservation_base + offset, reservation_base + offset + size) that is
66+ // mapped as a non-owning alias of an allocator address. It must be released
67+ // with UnMap(), not Deallocate().
68+ //
69+ // clang-format off
70+ // NOLINTBEGIN(whitespace/line_length)
71+ // Allowed address behavior:
72+ //
73+ // +--------------------------------------------------------+---------------------+------------+-----+-------+
74+ // | Address | Role | Deallocate | Map | UnMap |
75+ // +--------------------------------------------------------+---------------------+------------+-----+-------+
76+ // | Allocate() return | allocator address | yes | yes | no |
77+ // | Allocate(..., return_reservation_address=true) return | allocator address | yes | yes | no |
78+ // | Allocate(..., return_reservation_address=false) return | allocator address | yes | yes | no |
79+ // | reservation slice from Allocate(..., false) | reservation address | no | no | yes |
80+ // | reservation slice from Map() | reservation address | no | no | yes |
81+ // +--------------------------------------------------------+---------------------+------------+-----+-------+
82+ // NOLINTEND(whitespace/line_length)
83+ // clang-format on
84+ //
85+ // The table uses "yes" for API calls that accept the address in that row. For
86+ // example, Map() takes an allocator address as its source, while UnMap() takes
87+ // a reservation address to tear down. Map() still requires the allocator
88+ // address to have no active reservation-address alias; for example, an
89+ // Allocate(..., return_reservation_address=false) result can be remapped only
90+ // after its initial reservation-address alias is released with UnMap().
91+ //
92+ // The main API flows are:
93+ //
94+ // 1. Allocate(size) creates an allocator-owned VA reservation, allocates raw
95+ // physical memory, maps that memory into the owned reservation, and returns
96+ // the allocator address.
97+ // 2. Allocate(..., return_reservation_address=true) allocates raw physical
98+ // memory and maps it directly into the caller reservation. The returned VA
99+ // comes from the caller reservation, but it is still the allocator address
100+ // for this allocation.
101+ // 3. Allocate(..., return_reservation_address=false) returns a separate
102+ // allocator-owned address and also maps the same raw physical allocation
103+ // into the caller reservation as a reservation address.
104+ // 4. Map(addr, reservation, ...) maps the raw physical allocation currently
105+ // backing allocator address `addr` into one caller reservation slice.
106+ // UnMap(reservation, ...) removes that reservation-address alias.
107+ //
108+ // Deallocate() accepts only allocator addresses and requires any active
109+ // reservation-address alias to be released with UnMap() first. UnMap() accepts
110+ // only reservation addresses created by Map() or by
111+ // Allocate(..., return_reservation_address=false). Passing an allocator address
112+ // to UnMap(), or a reservation address to Deallocate(), is an error.
113+ // Each allocator address may have at most one active reservation-address alias.
114+ //
115+ // Deallocate() and UnMap() are stream-ordered deferred operations. The
116+ // allocator records a per-device sequence number for the affected address and
117+ // keeps the old mapping or allocation alive until the stream reaches that
118+ // sequence number, so kernels already submitted to the stream can keep using
119+ // the old VA. When the sequence completes, dropping the ScopedMapping objects
120+ // performs the real unmap, then the allocator releases any owned reservation
121+ // and raw physical memory.
74122//
75123// Concrete subclasses implement the platform-specific virtual methods
76124// (InitializeDeviceState, CreateAllocation, CreateReservation,
77125// EnqueueDeferredDeallocation) and expose platform-specific Create() factories.
78126// Subclasses must also set PerDeviceState::destroy_fn in InitializeDeviceState
79- // to release platform-specific resources (e.g. pinned timeline memory) .
127+ // to release platform-specific resources such as pinned timeline memory.
80128//
81129// This allocator is thread-safe for concurrent use by multiple threads across
82130// any registered devices.
@@ -100,15 +148,24 @@ class DeviceAddressVmmAllocator : public DeviceAddressAllocator {
100148 int64_t memory_space) override ;
101149
102150 // Allocates raw physical memory and maps it into a caller-owned
103- // MemoryReservation range. `allocation_size` and `mapping_size` must be
104- // equal.
151+ // MemoryReservation range.
152+ // `allocation_size` and `mapping_size` must be equal.
153+ //
154+ // There are two modes:
155+ //
156+ // * `return_reservation_address=true`: the mapped reservation slice is
157+ // returned and is treated as the allocator address. The caller releases it
158+ // with Deallocate(), may use it as a Map() source, and must not pass it to
159+ // UnMap().
160+ // * `return_reservation_address=false`: the allocator creates and returns a
161+ // separate allocator-owned address. The same raw physical allocation is
162+ // also mapped into the caller reservation as a reservation address. The
163+ // returned allocator address is released with Deallocate(); the
164+ // reservation-address alias may be released earlier with UnMap().
105165 //
106- // If `return_reservation_address` is true, the returned allocator address is
107- // the reservation slice and must be released with Deallocate(); `reservation`
108- // must outlive the returned address and any pending deallocation. If false,
109- // the returned allocator address is a separate allocator-owned VA and the
110- // reservation slice is a non-owning alias that must be released with UnMap()
111- // before the returned allocator address is deallocated.
166+ // The caller owns `reservation` and must keep it alive while any mapping into
167+ // it is active or waiting for deferred unmap completion. Deallocate() never
168+ // destroys or takes ownership of `reservation`.
112169 absl::StatusOr<ScopedDeviceAddress<uint8_t >> Allocate (
113170 int device_ordinal, uint64_t allocation_size, bool retry_on_failure,
114171 int64_t memory_space, MemoryReservation* reservation,
@@ -132,34 +189,58 @@ class DeviceAddressVmmAllocator : public DeviceAddressAllocator {
132189 const xla::DeviceAssignment* previous_;
133190 };
134191
135- // Deallocates memory asynchronously. The caller can call this function even
136- // if device kernels are still consuming the data — the actual deallocation
137- // will be deferred until all previously enqueued work on the device's stream
138- // completes.
192+ // Deallocates an allocator address asynchronously. `mem` must be an address
193+ // returned by Allocate(), including reservation-derived addresses returned by
194+ // Allocate(..., return_reservation_address=true). Reservation addresses
195+ // created by Map() or by Allocate(..., return_reservation_address=false) must
196+ // not be passed to Deallocate(). If `mem` has an active reservation-address
197+ // alias, the caller must release that alias with UnMap() before calling
198+ // Deallocate(). The caller can call this function while device kernels are
199+ // still consuming the data; the actual release is deferred until earlier work
200+ // on the device stream completes.
139201 absl::Status Deallocate (int device_ordinal, DeviceAddressBase mem) override ;
140202
141- // Maps the physical allocation backing `addr` into `reservation` at
142- // `reservation_offset`. `addr` must be an active allocator address returned
143- // by Allocate(), and each allocator address may have at most one active
144- // reservation alias.
203+ // Adds a reservation-address alias for an existing allocator address by
204+ // mapping the physical allocation currently backing `addr` into
205+ // `reservation` at `reservation_offset`.
206+ //
207+ // `addr` must be an active allocator address returned by this allocator,
208+ // including reservation-derived addresses returned by
209+ // Allocate(..., return_reservation_address=true). Non-owning reservation
210+ // addresses created by Map() or by
211+ // Allocate(..., return_reservation_address=false), and addresses from other
212+ // allocators, are not supported. The physical allocation backing `addr` must
213+ // be at least `size` bytes. Each allocator address may have at most one
214+ // active reservation-address alias at a time. The caller owns `reservation`
215+ // and must keep it alive until UnMap() is called and the allocator stream
216+ // reaches that deferred unmap point.
145217 absl::Status Map (int device_ordinal, DeviceAddressBase addr,
146218 MemoryReservation* reservation, uint64_t reservation_offset,
147219 uint64_t size);
148220
149- // Defers unmapping the reservation alias created by Map() until all
150- // previously enqueued work on this allocator's stream has completed. The
151- // caller must pass the same full reservation range used for Map().
221+ // Defers unmapping the reservation address created by Map() or by
222+ // Allocate(..., return_reservation_address=false) for the given reservation
223+ // range until all previously enqueued work on the allocator stream has
224+ // completed.
225+ // The caller must pass the same full reservation range that created the
226+ // mapping. The reservation-derived allocator address returned by
227+ // Allocate(..., return_reservation_address=true) is not a reservation
228+ // address for this API and must be released with Deallocate() instead.
229+ //
230+ // On success this method moves the active mapping to the deferred unmap
231+ // queue. On error, active bookkeeping is unchanged. Empty mappings, such as
232+ // zero-size Map(), are treated as no-ops.
152233 absl::Status UnMap (int device_ordinal, MemoryReservation* reservation,
153234 uint64_t reservation_offset, uint64_t size);
154235
155- // Returns true — this allocator supports asynchronous deallocation.
236+ // Returns true: this allocator supports asynchronous deallocation.
156237 bool AllowsAsynchronousDeallocation () const override { return true ; }
157238
158239 // Returns the stream for the given device ordinal.
159240 absl::StatusOr<Stream*> GetStream (int device_ordinal) override ;
160241
161- // Waits for all pending stream-ordered deallocations on the given device to
162- // complete, then releases the corresponding allocator bookkeeping.
242+ // Waits for all pending stream-ordered deallocations and unmaps on the given
243+ // device to complete, then drops the corresponding deferred bookkeeping.
163244 absl::Status SynchronizePendingOperations (int device_ordinal);
164245
165246 // Returns the StreamExecutor for the given device ordinal.
0 commit comments