Skip to content

Commit 9d2f844

Browse files
committed
reset support for command list. Not part of the paper atm
1 parent b475b92 commit 9d2f844

4 files changed

Lines changed: 92 additions & 4 deletions

File tree

‎backends/ze/ze_validator_entry_exit_helpers.rb‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,50 @@ def check_command_list_closed(state, ctx, defi)
286286
end
287287

288288

289+
# ADDED: validate a zeCommandListReset. Runs at ENTRY, before the model applies
290+
# the reset, reading the list handle from defi. Reports three misuses:
291+
# * already-destroyed list -- resetting a destroyed handle is a usage error.
292+
# * immediate command list -- zeCommandListReset is invalid on an immediate list
293+
# (it has no closed/execute lifecycle to reset); the runtime returns
294+
# ZE_RESULT_ERROR_INVALID_ARGUMENT.
295+
# * reset while in-flight -- the list is still executing a prior
296+
# zeCommandQueueExecuteCommandLists submission. Resetting it now races the
297+
# device and is undefined behavior in Level Zero.
298+
# Every command list handle is tracked, so the list is always found.
299+
# Reported once per (command list, reason) pair.
300+
def check_command_list_reset(state, ctx, defi)
301+
handle = defi['hCommandList']
302+
cmd_list = state.find_objects(ctx, 'command_list')[handle]
303+
304+
if cmd_list.status == ZEModel::CommandList.class_variable_get(:@@DESTROYED)
305+
key = "clreset-destroyed-#{state.get_handle_str(handle)}"
306+
if state.print_tracker[key] == 0
307+
state.print_tracker[key] = 1
308+
state.print_usage_error(ctx, "command list #{state.get_handle_str(handle)} was already destroyed before zeCommandListReset")
309+
end
310+
return
311+
end
312+
313+
if cmd_list.immediate
314+
key = "clreset-immediate-#{state.get_handle_str(handle)}"
315+
if state.print_tracker[key] == 0
316+
state.print_tracker[key] = 1
317+
state.print_usage_error(ctx, "zeCommandListReset called on immediate command list #{state.get_handle_str(handle)}; " \
318+
"immediate command lists cannot be reset")
319+
end
320+
end
321+
322+
if state.command_list_in_flight?(ctx, handle)
323+
key = "clreset-inflight-#{state.get_handle_str(handle)}"
324+
if state.print_tracker[key] == 0
325+
state.print_tracker[key] = 1
326+
state.print_usage_error(ctx, "command list #{state.get_handle_str(handle)} is being reset while a prior " \
327+
"zeCommandQueueExecuteCommandLists submission is still in-flight; the device may " \
328+
"still be executing it (undefined behavior)")
329+
end
330+
end
331+
end
332+
289333
def check_valid_module(state,ctx,defi)
290334
module_handle = state.find_param(ctx, 'hModule')
291335
if !module_handle || module_handle == 0

‎backends/ze/ze_validator_function_entry_exit_callbacks.rb‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,27 @@
7373
api: 'zeCommandListAppendLaunchKernel'))
7474
}
7575

76+
# ADDED: zeCommandListReset returns a command list to its initial, empty,
77+
# appendable state so it can be reused without destroy+recreate. Misuse checks run
78+
# at ENTRY (a reset can be rejected/crash without emitting an _exit -- e.g. on an
79+
# immediate list) reading the input handle from defi.
80+
$upon_entry["zeCommandListReset"] = lambda { |state, ctx, defi|
81+
check_command_list_reset(state, ctx, defi)
82+
}
83+
84+
# ADDED: on success the list is empty and open again. Clear the recorded ops so a
85+
# later close/execute replays only ops appended after the reset (in-flight
86+
# executions from before are unaffected -- enqueue_deferred_execution snapshotted
87+
# a dup of the ops at submit time), and return the status to INITIALIZED so the
88+
# closed-before-execute check applies to the reused list.
89+
$on_successful_exit["zeCommandListReset"] = lambda { |state, ctx, defi|
90+
command_lists = state.find_objects(ctx, 'command_list')
91+
cmd_list = command_lists[state.find_param(ctx, 'hCommandList')]
92+
return unless cmd_list
93+
cmd_list.ops.clear
94+
cmd_list.status = ZEModel::CommandList.class_variable_get(:@@INITIALIZED)
95+
}
96+
7697
#when command queue is executed, the associated fence's status is set to IN_USE
7798
$on_successful_exit["zeCommandListClose"] = lambda { |state, ctx, defi|
7899
command_lists = state.find_objects(ctx, 'command_list')

‎backends/ze/ze_validator_state_object.rb‎

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,22 @@ def each_inflight_copy_op(context)
275275
end
276276
end
277277

278+
# ADDED: true if command list `handle` still has an in-flight deferred
279+
# execution in this process -- a prior zeCommandQueueExecuteCommandLists whose
280+
# ops have not all drained yet. Used by zeCommandListReset, which must not run
281+
# while the list is still executing (undefined behavior in Level Zero). Matches
282+
# on the unit's originating list handle and the process it belongs to (deferred
283+
# units carry no pid, so we compare context host+pid, mirroring
284+
# each_inflight_copy_op).
285+
def command_list_in_flight?(context, handle)
286+
@deferred_units.any? do |unit|
287+
unit.cmd_list_handle == handle &&
288+
unit.context['hostname'] == context['hostname'] &&
289+
unit.context['vpid'] == context['vpid'] &&
290+
!unit.done?
291+
end
292+
end
293+
278294
def to_struct(memory, klass)
279295
memory.size > 0 ? klass.new(FFI::MemoryPointer.from_string(memory)) : nil
280296
end
@@ -402,8 +418,9 @@ def pump_deferred
402418
# ADDED: register a command list's ops as a deferred unit and pump. `ops` is a
403419
# snapshot (dup) taken by the caller so a later reset+re-append on the same
404420
# list cannot mutate an in-flight execution.
405-
def run_deferred_list(context, ops, label, in_order: false)
406-
@deferred_units << ZEModel::DeferredUnit.new(ops, context, label, in_order: in_order)
421+
def run_deferred_list(context, ops, label, in_order: false, cmd_list_handle: nil)
422+
@deferred_units << ZEModel::DeferredUnit.new(ops, context, label, in_order: in_order,
423+
cmd_list_handle: cmd_list_handle)
407424
pump_deferred
408425
end
409426

@@ -416,7 +433,7 @@ def enqueue_deferred_execution(context, command_lists)
416433
command_lists.each do |cl|
417434
next unless cl
418435
run_deferred_list(context, cl.ops.dup, "command_list (#{get_handle_str(cl.handle)})",
419-
in_order: cl.in_order)
436+
in_order: cl.in_order, cmd_list_handle: cl.handle)
420437
end
421438
end
422439

‎backends/ze/ze_validator_zemodel.rb‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,14 +360,20 @@ class DeferredUnit
360360
# ADDED: whether the originating command list is in-order (see CommandList#in_order).
361361
# The intra-list self-deadlock check only applies to in-order units.
362362
attr_reader :in_order
363+
# ADDED: handle of the command list this unit was submitted from (nil if
364+
# unknown/immediate without a handle). Lets checks scoped to a specific list --
365+
# e.g. resetting a list while a prior submission is still in-flight -- find the
366+
# deferred units that belong to it without matching on the label string.
367+
attr_reader :cmd_list_handle
363368

364-
def initialize(ops, context, label, in_order: false)
369+
def initialize(ops, context, label, in_order: false, cmd_list_handle: nil)
365370
@ops = ops
366371
@context = context
367372
@label = label
368373
@cursor = 0
369374
@blocked_on = []
370375
@in_order = in_order # ADDED
376+
@cmd_list_handle = cmd_list_handle # ADDED
371377
#every event this unit will eventually signal, for the wait-for graph
372378
@pending_signals = ops.map { |op| op.signal }.compact
373379
end

0 commit comments

Comments
 (0)