Skip to content

Commit 87bc5ac

Browse files
committed
fix stack sync for cb events
1 parent 44580d7 commit 87bc5ac

File tree

3 files changed

+64
-2
lines changed

3 files changed

+64
-2
lines changed

unified-runtime/source/adapters/level_zero/v2/command_list_manager.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,26 @@ ur_result_t ur_command_list_manager::appendGenericFillUnlocked(
3636
hDevice.get(), ur_mem_buffer_t::device_access_mode_t::read_only, offset,
3737
size, zeCommandList.get(), waitListView));
3838

39+
// Store pattern in event if this is an async operation
40+
// This prevents use-after-return bug when pPattern points to stack memory
41+
const void *patternPtr = pPattern;
42+
if (phEvent && zeSignalEvent) {
43+
phEvent->retainFillPattern(pPattern, patternSize);
44+
patternPtr = phEvent->getFillPattern();
45+
}
46+
3947
// PatternSize must be a power of two for zeCommandListAppendMemoryFill.
4048
// When it's not, the fill is emulated with zeCommandListAppendMemoryCopy.
4149
// WORKAROUND: Level Zero driver rejects zeCommandListAppendMemoryFill when
4250
// patternSize == size, returning ZE_RESULT_ERROR_INVALID_SIZE (0x78000008).
4351
if (isPowerOf2(patternSize) && patternSize != size) {
4452
ZE2UR_CALL(zeCommandListAppendMemoryFill,
45-
(zeCommandList.get(), pDst, pPattern, patternSize, size,
53+
(zeCommandList.get(), pDst, patternPtr, patternSize, size,
4654
zeSignalEvent, waitListView.num, waitListView.handles));
4755
} else {
4856
// Copy pattern into every entry in memory array pointed by Ptr.
4957
uint32_t numOfCopySteps = size / patternSize;
50-
const void *src = pPattern;
58+
const void *src = patternPtr;
5159

5260
for (uint32_t step = 0; step < numOfCopySteps; ++step) {
5361
void *dst = reinterpret_cast<void *>(reinterpret_cast<uint8_t *>(pDst) +

unified-runtime/source/adapters/level_zero/v2/event.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,41 @@ uint64_t ur_event_handle_t_::getEventEndTimestamp() {
141141
return profilingData.getEventEndTimestamp();
142142
}
143143

144+
void ur_event_handle_t_::retainFillPattern(const void *pPattern, size_t patternSize) {
145+
if (!fillPattern) {
146+
fillPattern.emplace();
147+
}
148+
149+
auto &storage = fillPattern.value();
150+
storage.size = patternSize;
151+
152+
// Small buffer optimization: use inline buffer for patterns <= 16 bytes
153+
if (patternSize <= FillPatternStorage::INLINE_SIZE) {
154+
std::memcpy(storage.inlineBuffer.data(), pPattern, patternSize);
155+
storage.useHeap = false;
156+
} else {
157+
// Use heap buffer for larger patterns
158+
storage.heapBuffer.resize(patternSize);
159+
std::memcpy(storage.heapBuffer.data(), pPattern, patternSize);
160+
storage.useHeap = true;
161+
}
162+
}
163+
164+
const void *ur_event_handle_t_::getFillPattern() const {
165+
if (!fillPattern) {
166+
return nullptr;
167+
}
168+
return fillPattern->data();
169+
}
170+
144171
void ur_event_handle_t_::reset() {
145172
// consider make an abstraction for regular/counter based
146173
// events if there's more of this type of conditions
147174
if (!(flags & v2::EVENT_FLAGS_COUNTER)) {
148175
zeEventHostReset(getZeEvent());
149176
}
177+
// Clear the fill pattern to avoid memory leak when event is reused from pool
178+
fillPattern.reset();
150179
}
151180

152181
ze_event_handle_t ur_event_handle_t_::getZeEvent() const {

unified-runtime/source/adapters/level_zero/v2/event.hpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
//===----------------------------------------------------------------------===//
1010
#pragma once
1111

12+
#include <array>
13+
#include <optional>
1214
#include <stack>
15+
#include <vector>
1316

1417
#include <ur/ur.hpp>
1518
#include <ur_api.h>
@@ -83,6 +86,12 @@ struct ur_event_handle_t_ : ur_object {
8386
// deffered events list in the queue
8487
ur_result_t releaseDeferred();
8588

89+
// Store pattern data for USM fill commands to keep it alive
90+
void retainFillPattern(const void *pPattern, size_t patternSize);
91+
92+
// Get pointer to the retained fill pattern data
93+
const void *getFillPattern() const;
94+
8695
// Tells if this event was created as a timestamp event, allowing profiling
8796
// info even if profiling is not enabled.
8897
bool isTimestamped() const;
@@ -135,4 +144,20 @@ struct ur_event_handle_t_ : ur_object {
135144

136145
v2::event_flags_t flags;
137146
event_profiling_data_t profilingData;
147+
148+
// Storage for fill pattern to keep it alive during async execution
149+
// Uses std::optional to avoid overhead for non-fill events
150+
// Small buffer optimization: patterns ≤16 bytes use inline storage
151+
struct FillPatternStorage {
152+
static constexpr size_t INLINE_SIZE = 16;
153+
std::array<uint8_t, INLINE_SIZE> inlineBuffer;
154+
std::vector<uint8_t> heapBuffer;
155+
size_t size = 0;
156+
bool useHeap = false;
157+
158+
const void* data() const {
159+
return useHeap ? heapBuffer.data() : inlineBuffer.data();
160+
}
161+
};
162+
std::optional<FillPatternStorage> fillPattern;
138163
};

0 commit comments

Comments
 (0)