This series adds new events to make it easier for tools
like gpuvis or umr to graph the GPUs, kernel and applications
activity.
UMR patches using these events can be found here:
https://gitlab.freedesktop.org/tomstdenis/umr/-/merge_requests/37
V1:
https://patchwork.kernel.org/project/linux-media/patch/20240117184329.479554-1-pierre-eric.pelloux-prayer@amd.com/
Changes from V1:
* uses trace_dma_fence_sync_to from dma-fence-chain.c
* new amdgpu events
* new drm plane commit event
Changes from V2:
* uses trace_dma_fence_used_as_dependency from drm_sched_job_add_dependency
* add devname attribute to the trace_amdgpu_sched_run_job event
* addressed review comments
Pierre-Eric Pelloux-Prayer (8):
tracing, dma-buf: add a trace_dma_fence_sync_to event
dma-buf/fence-chain: use trace_dma_fence_sync_to
amdgpu: use trace_dma_fence_sync_to in amdgpu_fence_sync
drm/amdgpu: add a amdgpu_bo_fill trace event
drm/amdgpu: add a amdgpu_cs_start trace event
drm: add drm_mode_atomic_commit event
drm/sched: use trace_dma_fence_used_as_dependency
drm/amdgpu: add devname to trace_amdgpu_sched_run_job
drivers/dma-buf/dma-fence-chain.c | 4 +++
drivers/dma-buf/dma-fence.c | 1 +
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 2 ++
drivers/gpu/drm/amd/amdgpu/amdgpu_job.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c | 9 +++--
drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h | 4 ++-
drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h | 42 ++++++++++++++++++++---
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 2 ++
drivers/gpu/drm/drm_atomic_uapi.c | 21 ++++++++++++
drivers/gpu/drm/drm_trace.h | 23 +++++++++++++
drivers/gpu/drm/scheduler/sched_main.c | 4 +++
include/trace/events/dma_fence.h | 27 +++++++++++++++
12 files changed, 133 insertions(+), 8 deletions(-)
--
2.40.1
This new event can be used to trace where a given dma_fence is added
as a dependency of some other work.
I plan to use it in amdgpu.
Signed-off-by: Pierre-Eric Pelloux-Prayer <redacted>
---
drivers/dma-buf/dma-fence.c | 1 +
include/trace/events/dma_fence.h | 27 +++++++++++++++++++++++++++
2 files changed, 28 insertions(+)
@@ -83,6 +83,33 @@ DEFINE_EVENT(dma_fence, dma_fence_wait_end,TP_ARGS(fence));+TRACE_EVENT(dma_fence_used_as_dependency,++TP_PROTO(structdma_fence*fence,constchar*reason),++TP_ARGS(fence,reason),++TP_STRUCT__entry(+__string(driver,fence->ops->get_driver_name(fence))+__string(timeline,fence->ops->get_timeline_name(fence))+__field(unsignedint,context)+__field(unsignedint,seqno)+__string(reason,reason)+),++TP_fast_assign(+__assign_str(driver,fence->ops->get_driver_name(fence));+__assign_str(timeline,fence->ops->get_timeline_name(fence));+__entry->context=fence->context;+__entry->seqno=fence->seqno;+__assign_str(reason,reason);+),++TP_printk("driver=%s timeline=%s context=%u seqno=%u reason=%s",+__get_str(driver),__get_str(timeline),__entry->context,+__entry->seqno,__get_str(reason))+);+#endif /* _TRACE_DMA_FENCE_H *//* This part must be outside protection */
To inform tools about the relationship between the fences.
Signed-off-by: Pierre-Eric Pelloux-Prayer <redacted>
---
drivers/dma-buf/dma-fence-chain.c | 4 ++++
1 file changed, 4 insertions(+)
@@ -251,6 +253,8 @@ void dma_fence_chain_init(struct dma_fence_chain *chain,chain->fence=fence;chain->prev_seqno=0;+trace_dma_fence_used_as_dependency(fence,__func__);+/* Try to reuse the context of the previous chain node. */if(prev_chain&&__dma_fence_is_later(seqno,prev->seqno,prev->ops)){context=prev->context;
This makes it possible to understand the dependencies between jobs.
Possible usage of this trace:
* stuttering issues like Mesa !9189
* incorrect synchronization: I don't have a link for this one, but having
these events was very useful to debug a virtio-gpu / native-context /
radeonsi sync issue
I have prototype code using this in UMR, as can be see here:
https://gitlab.freedesktop.org/tomstdenis/umr/-/merge_requests/37
v2: add a macro since every caller passes __func__ as the reason parameter
Signed-off-by: Pierre-Eric Pelloux-Prayer <redacted>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c | 9 +++++++--
drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h | 4 +++-
2 files changed, 10 insertions(+), 3 deletions(-)
amdgpu_cs_ioctl already exists but serves a different
purpose.
amdgpu_cs_start marks the beginning of the kernel processing of
the ioctl which is useful for tools to map which events belong to
the same submission (without this, the first event would be the
amdgpu_bo_set_list ones).
v2: renamed to amdgpu_cs_start
Signed-off-by: Pierre-Eric Pelloux-Prayer <redacted>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 2 ++
drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h | 12 ++++++++++++
2 files changed, 14 insertions(+)
@@ -66,6 +66,29 @@ TRACE_EVENT(drm_vblank_event_delivered,__entry->seq));+TRACE_EVENT(drm_mode_atomic_commit,+TP_PROTO(structdrm_file*file,int*crtcs,intncrtcs,uint32_tflags),+TP_ARGS(file,crtcs,ncrtcs,flags),+TP_STRUCT__entry(+__field(structdrm_file*,file)+__dynamic_array(u32,crtcs,ncrtcs)+__field(uint32_t,ncrtcs)+__field(uint32_t,flags)+),+TP_fast_assign(+unsignedinti;++__entry->file=file;+for(i=0;i<ncrtcs;i++)+((u32*)__get_dynamic_array(crtcs))[i]=crtcs[i];+__entry->ncrtcs=ncrtcs;+__entry->flags=flags;+),+TP_printk("file=%p, pid=%8d, flags=%08x, crtcs=%s",__entry->file,+pid_nr(__entry->file->pid),__entry->flags,+__print_array(__get_dynamic_array(crtcs),__entry->ncrtcs,4))+);+#endif /* _DRM_TRACE_H_ *//* This part must be outside protection */
With the move to work queues for the drm scheduler it becomes
impossible for a tool to match the events to the GPU.
Before this move, the event source was fixed (eg: gfx_0.0.0-598),
so even if the system had multiple GPUs with identical queue names
it was possible to map the events using the PID.
With work queues, the source is now something like: "kworker/u64:0-15248"
(and the PID isn't stable), so the "timeline=gfx_0.0.0" attribute
isn't enough in multi-GPU setups.
This commit adds a dev=devname attribute to resolve this issue.
Signed-off-by: Pierre-Eric Pelloux-Prayer <redacted>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_job.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h | 12 ++++++++----
2 files changed, 9 insertions(+), 5 deletions(-)
@@ -292,7 +292,7 @@ static struct dma_fence *amdgpu_job_run(struct drm_sched_job *sched_job)job=to_amdgpu_job(sched_job);finished=&job->base.s_fence->finished;-trace_amdgpu_sched_run_job(job);+trace_amdgpu_sched_run_job(job,adev);/* Skip job if VRAM is lost and never resubmit gangs */if(job->generation!=amdgpu_vm_generation(adev,job->vm)||
I need to separate this patch set a bit. The DMA-buf stuff usually goes
upstream through drm-misc-next while the amdgpu only patches go upstream
through our internal branch.
I will keep you looped in which patch I pick from this set to which branch.
Oh, that's going to be fun.
Christian.
Changes from V1:
* uses trace_dma_fence_sync_to from dma-fence-chain.c
* new amdgpu events
* new drm plane commit event
Changes from V2:
* uses trace_dma_fence_used_as_dependency from drm_sched_job_add_dependency
* add devname attribute to the trace_amdgpu_sched_run_job event
* addressed review comments
Pierre-Eric Pelloux-Prayer (8):
tracing, dma-buf: add a trace_dma_fence_sync_to event
dma-buf/fence-chain: use trace_dma_fence_sync_to
amdgpu: use trace_dma_fence_sync_to in amdgpu_fence_sync
drm/amdgpu: add a amdgpu_bo_fill trace event
drm/amdgpu: add a amdgpu_cs_start trace event
drm: add drm_mode_atomic_commit event
drm/sched: use trace_dma_fence_used_as_dependency
drm/amdgpu: add devname to trace_amdgpu_sched_run_job
drivers/dma-buf/dma-fence-chain.c | 4 +++
drivers/dma-buf/dma-fence.c | 1 +
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 2 ++
drivers/gpu/drm/amd/amdgpu/amdgpu_job.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c | 9 +++--
drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h | 4 ++-
drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h | 42 ++++++++++++++++++++---
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 2 ++
drivers/gpu/drm/drm_atomic_uapi.c | 21 ++++++++++++
drivers/gpu/drm/drm_trace.h | 23 +++++++++++++
drivers/gpu/drm/scheduler/sched_main.c | 4 +++
include/trace/events/dma_fence.h | 27 +++++++++++++++
12 files changed, 133 insertions(+), 8 deletions(-)
From: Christian König <christian.koenig@amd.com> Date: 2024-02-16 15:29:07
Am 16.02.24 um 16:09 schrieb Pierre-Eric Pelloux-Prayer:
quoted hunk
This new event can be used to trace where a given dma_fence is added
as a dependency of some other work.
I plan to use it in amdgpu.
Signed-off-by: Pierre-Eric Pelloux-Prayer <redacted>
---
drivers/dma-buf/dma-fence.c | 1 +
include/trace/events/dma_fence.h | 27 +++++++++++++++++++++++++++
2 files changed, 28 insertions(+)
I noted it before that this needs to be an u64 and not unsigned int.
Otherwise we will lose the higher 32bits.
The existing trace points have that bug as well, so you might also want
to provide a patch to fix this.
Christian.
@@ -251,6 +253,8 @@ void dma_fence_chain_init(struct dma_fence_chain *chain,chain->fence=fence;chain->prev_seqno=0;+trace_dma_fence_used_as_dependency(fence,__func__);+/* Try to reuse the context of the previous chain node. */if(prev_chain&&__dma_fence_is_later(seqno,prev->seqno,prev->ops)){context=prev->context;
From: Christian König <christian.koenig@amd.com> Date: 2024-02-16 15:56:27
Am 16.02.24 um 16:09 schrieb Pierre-Eric Pelloux-Prayer:
This makes it possible to understand the dependencies between jobs.
Possible usage of this trace:
* stuttering issues like Mesa !9189
* incorrect synchronization: I don't have a link for this one, but having
these events was very useful to debug a virtio-gpu / native-context /
radeonsi sync issue
I have prototype code using this in UMR, as can be see here:
https://gitlab.freedesktop.org/tomstdenis/umr/-/merge_requests/37
v2: add a macro since every caller passes __func__ as the reason parameter
Signed-off-by: Pierre-Eric Pelloux-Prayer <redacted>
Reviewed-by: Christian König <christian.koenig@amd.com>
@@ -1503,6 +1504,26 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,drm_mode_object_put(obj);}+if(trace_drm_mode_atomic_commit_enabled()){+structdrm_crtc_state*crtc_state;+structdrm_crtc*crtc;+int*crtcs;+inti,num_crtcs;++crtcs=kcalloc(dev->mode_config.num_crtc,sizeof(int),+GFP_KERNEL);++if(crtcs){+num_crtcs=0;+for_each_new_crtc_in_state(state,crtc,crtc_state,i)+crtcs[num_crtcs++]=drm_crtc_index(crtc);
Hmm, looking deeper into this, could you just do the loop the trace event?
That is how different is the config.num_crtc compared to the final num_crtcs?
That way, we don't need to do this allocation if it's not too different.
That is, pass in the dev->mode_config.num_crtc to the tracepoint instead of
num_crtcs.
Here the ncrtcs is what is passed in. It will always be allocated to that
size though.
+ __field(uint32_t, ncrtcs)
+ __field(uint32_t, flags)
+ ),
+ TP_fast_assign(
+ unsigned int i;
+
+ __entry->file = file;
+ for (i = 0; i < ncrtcs; i++)
+ ((u32 *)__get_dynamic_array(crtcs))[i] = crtcs[i];
Here we have:
int n = 0;
for_each_new_crtc_in_state(state, crtc, crtc_state, i)
((u32 *)__get_dynamic_array(crtcs))[n++] = drm_crtc_index(crtc);
__entry->ncrtcs = n;
But this is only viable if the ncrtcs is close to the same size as dev->mode_config.num_crtc,
otherwise it's not worth it.
-- Steve
+ __entry->ncrtcs = ncrtcs;
+ __entry->flags = flags;
+ ),
+ TP_printk("file=%p, pid=%8d, flags=%08x, crtcs=%s", __entry->file,
+ pid_nr(__entry->file->pid), __entry->flags,
+ __print_array(__get_dynamic_array(crtcs), __entry->ncrtcs, 4))
+);
+
#endif /* _DRM_TRACE_H_ */
/* This part must be outside protection */
@@ -1503,6 +1504,26 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,drm_mode_object_put(obj);}+if(trace_drm_mode_atomic_commit_enabled()){+structdrm_crtc_state*crtc_state;+structdrm_crtc*crtc;+int*crtcs;+inti,num_crtcs;++crtcs=kcalloc(dev->mode_config.num_crtc,sizeof(int),+GFP_KERNEL);++if(crtcs){+num_crtcs=0;+for_each_new_crtc_in_state(state,crtc,crtc_state,i)+crtcs[num_crtcs++]=drm_crtc_index(crtc);++trace_drm_mode_atomic_commit(file_priv,crtcs,num_crtcs,arg->flags);++kfree(crtcs);+}+}
I think the current drm trace events are sort of semi-useless.
The problems are:
- no device id in the events so good luck with multi gpu systems
- vblank trace events are only emitted from some vblank
codepaths but not others
I'm also not sure putting an event straight into the atomic ioctl is
particularly useful.
First of all it means that any commit not initiated by the atomic
ioctl will not be traced.
It would also seem more useful to me if the driver can emit the
trace just before it commits the frame to the hardware, so that
we can also observe the latency between userspace submitting
the frame vs. when the hardware will actually see it.
Also if we want tools to use these I think we're going to have to
make some kind of abi promises about the events, so we should make
sure they are as future proof as we can make them (eg. regarding
mutli-gpu systems/etc.).
quoted hunk
+
ret = prepare_signaling(dev, state, arg, file_priv, &fence_state,
&num_fences);
if (ret)
@@ -66,6 +66,29 @@ TRACE_EVENT(drm_vblank_event_delivered,__entry->seq));+TRACE_EVENT(drm_mode_atomic_commit,+TP_PROTO(structdrm_file*file,int*crtcs,intncrtcs,uint32_tflags),+TP_ARGS(file,crtcs,ncrtcs,flags),+TP_STRUCT__entry(+__field(structdrm_file*,file)+__dynamic_array(u32,crtcs,ncrtcs)+__field(uint32_t,ncrtcs)+__field(uint32_t,flags)+),+TP_fast_assign(+unsignedinti;++__entry->file=file;+for(i=0;i<ncrtcs;i++)+((u32*)__get_dynamic_array(crtcs))[i]=crtcs[i];+__entry->ncrtcs=ncrtcs;+__entry->flags=flags;+),+TP_printk("file=%p, pid=%8d, flags=%08x, crtcs=%s",__entry->file,+pid_nr(__entry->file->pid),__entry->flags,+__print_array(__get_dynamic_array(crtcs),__entry->ncrtcs,4))+);+#endif /* _DRM_TRACE_H_ *//* This part must be outside protection */
@@ -1503,6 +1504,26 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,drm_mode_object_put(obj);}+if(trace_drm_mode_atomic_commit_enabled()){+structdrm_crtc_state*crtc_state;+structdrm_crtc*crtc;+int*crtcs;+inti,num_crtcs;++crtcs=kcalloc(dev->mode_config.num_crtc,sizeof(int),+GFP_KERNEL);++if(crtcs){+num_crtcs=0;+for_each_new_crtc_in_state(state,crtc,crtc_state,i)+crtcs[num_crtcs++]=drm_crtc_index(crtc);++trace_drm_mode_atomic_commit(file_priv,crtcs,num_crtcs,arg->flags);++kfree(crtcs);+}+}
I think the current drm trace events are sort of semi-useless.
The problems are:
- no device id in the events so good luck with multi gpu systems
- vblank trace events are only emitted from some vblank
codepaths but not others
I'm also not sure putting an event straight into the atomic ioctl is
particularly useful.
First of all it means that any commit not initiated by the atomic
ioctl will not be traced.
It would also seem more useful to me if the driver can emit the
trace just before it commits the frame to the hardware, so that
we can also observe the latency between userspace submitting
the frame vs. when the hardware will actually see it.
Also if we want tools to use these I think we're going to have to
make some kind of abi promises about the events, so we should make
sure they are as future proof as we can make them (eg. regarding
mutli-gpu systems/etc.).
Thanks for your feedback.
This series was also discussed on IRC with Sima [1], and the conclusion was
that it would be good to rework the series with the following goals in
mind:
* make sure the events are useful for any drivers using the core drm code,
not just amdgpu
* add new events or extend existing ones so that all the required information is
there (= no guessing needed)
* document the updated tracepoints (as UAPI?): how they should be interpreted
by tools (eg: how to reconstruct fence dependencies? how to measure latency? etc)
Pierre-Eric
[1]: https://dri.freedesktop.org/~cbrill/dri-log/?channel=dri-devel&date=2024-02-16
quoted
+
ret = prepare_signaling(dev, state, arg, file_priv, &fence_state,
&num_fences);
if (ret)
@@ -66,6 +66,29 @@ TRACE_EVENT(drm_vblank_event_delivered,__entry->seq));+TRACE_EVENT(drm_mode_atomic_commit,+TP_PROTO(structdrm_file*file,int*crtcs,intncrtcs,uint32_tflags),+TP_ARGS(file,crtcs,ncrtcs,flags),+TP_STRUCT__entry(+__field(structdrm_file*,file)+__dynamic_array(u32,crtcs,ncrtcs)+__field(uint32_t,ncrtcs)+__field(uint32_t,flags)+),+TP_fast_assign(+unsignedinti;++__entry->file=file;+for(i=0;i<ncrtcs;i++)+((u32*)__get_dynamic_array(crtcs))[i]=crtcs[i];+__entry->ncrtcs=ncrtcs;+__entry->flags=flags;+),+TP_printk("file=%p, pid=%8d, flags=%08x, crtcs=%s",__entry->file,+pid_nr(__entry->file->pid),__entry->flags,+__print_array(__get_dynamic_array(crtcs),__entry->ncrtcs,4))+);+#endif /* _DRM_TRACE_H_ *//* This part must be outside protection */