* [PATCH v2 0/3] Support active context utilization
@ 2025-04-26 0:29 Umesh Nerlige Ramappa
2025-04-26 0:29 ` [PATCH v2 1/3] drm/xe: Save CTX_TIMESTAMP mmio value instead of LRC value Umesh Nerlige Ramappa
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Umesh Nerlige Ramappa @ 2025-04-26 0:29 UTC (permalink / raw)
To: intel-xe, Lucas De Marchi, matthew.brost; +Cc: Matt Roper
Add support for active context utilization using WA BB.
v2: (Matt)
- Fix breakage in TDR due to this feature.
- Other review comments
Signed-off-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>
Umesh Nerlige Ramappa (3):
drm/xe: Save CTX_TIMESTAMP mmio value instead of LRC value
drm/xe: Save the gt pointer in lrc and drop the tile
drm/xe: Add WA BB to capture active context utilization
.../gpu/drm/xe/instructions/xe_mi_commands.h | 4 +
drivers/gpu/drm/xe/regs/xe_engine_regs.h | 4 +
drivers/gpu/drm/xe/regs/xe_lrc_layout.h | 1 +
drivers/gpu/drm/xe/xe_lrc.c | 171 +++++++++++++++++-
drivers/gpu/drm/xe/xe_lrc_types.h | 7 +-
drivers/gpu/drm/xe/xe_ring_ops.c | 7 +-
6 files changed, 183 insertions(+), 11 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v2 1/3] drm/xe: Save CTX_TIMESTAMP mmio value instead of LRC value 2025-04-26 0:29 [PATCH v2 0/3] Support active context utilization Umesh Nerlige Ramappa @ 2025-04-26 0:29 ` Umesh Nerlige Ramappa 2025-04-26 4:42 ` Matthew Brost 2025-04-26 0:29 ` [PATCH v2 2/3] drm/xe: Save the gt pointer in lrc and drop the tile Umesh Nerlige Ramappa 2025-04-26 0:29 ` [PATCH v2 3/3] drm/xe: Add WA BB to capture active context utilization Umesh Nerlige Ramappa 2 siblings, 1 reply; 7+ messages in thread From: Umesh Nerlige Ramappa @ 2025-04-26 0:29 UTC (permalink / raw) To: intel-xe, Lucas De Marchi, matthew.brost; +Cc: Matt Roper For determining actual job execution time, save the current value of the CTX_TIMESTAMP register rather than the value saved in LRC since the current register value is the closest to the start time of the job. Fixes: 65921374c48f ("drm/xe: Emit ctx timestamp copy in ring ops") Signed-off-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com> --- drivers/gpu/drm/xe/xe_ring_ops.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_ring_ops.c b/drivers/gpu/drm/xe/xe_ring_ops.c index a7582b097ae6..bc1689db4cd7 100644 --- a/drivers/gpu/drm/xe/xe_ring_ops.c +++ b/drivers/gpu/drm/xe/xe_ring_ops.c @@ -234,13 +234,10 @@ static u32 get_ppgtt_flag(struct xe_sched_job *job) static int emit_copy_timestamp(struct xe_lrc *lrc, u32 *dw, int i) { - dw[i++] = MI_COPY_MEM_MEM | MI_COPY_MEM_MEM_SRC_GGTT | - MI_COPY_MEM_MEM_DST_GGTT; + dw[i++] = MI_STORE_REGISTER_MEM | MI_SRM_USE_GGTT | MI_SRM_ADD_CS_OFFSET; + dw[i++] = RING_CTX_TIMESTAMP(0).addr; dw[i++] = xe_lrc_ctx_job_timestamp_ggtt_addr(lrc); dw[i++] = 0; - dw[i++] = xe_lrc_ctx_timestamp_ggtt_addr(lrc); - dw[i++] = 0; - dw[i++] = MI_NOOP; return i; } -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/3] drm/xe: Save CTX_TIMESTAMP mmio value instead of LRC value 2025-04-26 0:29 ` [PATCH v2 1/3] drm/xe: Save CTX_TIMESTAMP mmio value instead of LRC value Umesh Nerlige Ramappa @ 2025-04-26 4:42 ` Matthew Brost 0 siblings, 0 replies; 7+ messages in thread From: Matthew Brost @ 2025-04-26 4:42 UTC (permalink / raw) To: Umesh Nerlige Ramappa; +Cc: intel-xe, Lucas De Marchi, Matt Roper On Fri, Apr 25, 2025 at 05:29:49PM -0700, Umesh Nerlige Ramappa wrote: > For determining actual job execution time, save the current value of the > CTX_TIMESTAMP register rather than the value saved in LRC since the > current register value is the closest to the start time of the job. > > Fixes: 65921374c48f ("drm/xe: Emit ctx timestamp copy in ring ops") > Signed-off-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com> Reviewed-by: Matthew Brost <matthew.brost@intel.com> > --- > drivers/gpu/drm/xe/xe_ring_ops.c | 7 ++----- > 1 file changed, 2 insertions(+), 5 deletions(-) > > diff --git a/drivers/gpu/drm/xe/xe_ring_ops.c b/drivers/gpu/drm/xe/xe_ring_ops.c > index a7582b097ae6..bc1689db4cd7 100644 > --- a/drivers/gpu/drm/xe/xe_ring_ops.c > +++ b/drivers/gpu/drm/xe/xe_ring_ops.c > @@ -234,13 +234,10 @@ static u32 get_ppgtt_flag(struct xe_sched_job *job) > > static int emit_copy_timestamp(struct xe_lrc *lrc, u32 *dw, int i) > { > - dw[i++] = MI_COPY_MEM_MEM | MI_COPY_MEM_MEM_SRC_GGTT | > - MI_COPY_MEM_MEM_DST_GGTT; > + dw[i++] = MI_STORE_REGISTER_MEM | MI_SRM_USE_GGTT | MI_SRM_ADD_CS_OFFSET; > + dw[i++] = RING_CTX_TIMESTAMP(0).addr; > dw[i++] = xe_lrc_ctx_job_timestamp_ggtt_addr(lrc); > dw[i++] = 0; > - dw[i++] = xe_lrc_ctx_timestamp_ggtt_addr(lrc); > - dw[i++] = 0; > - dw[i++] = MI_NOOP; > > return i; > } > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 2/3] drm/xe: Save the gt pointer in lrc and drop the tile 2025-04-26 0:29 [PATCH v2 0/3] Support active context utilization Umesh Nerlige Ramappa 2025-04-26 0:29 ` [PATCH v2 1/3] drm/xe: Save CTX_TIMESTAMP mmio value instead of LRC value Umesh Nerlige Ramappa @ 2025-04-26 0:29 ` Umesh Nerlige Ramappa 2025-04-26 4:43 ` Matthew Brost 2025-04-26 0:29 ` [PATCH v2 3/3] drm/xe: Add WA BB to capture active context utilization Umesh Nerlige Ramappa 2 siblings, 1 reply; 7+ messages in thread From: Umesh Nerlige Ramappa @ 2025-04-26 0:29 UTC (permalink / raw) To: intel-xe, Lucas De Marchi, matthew.brost; +Cc: Matt Roper Save the gt pointer in the lrc so that it can used for gt based helpers. Signed-off-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com> --- drivers/gpu/drm/xe/xe_lrc.c | 4 ++-- drivers/gpu/drm/xe/xe_lrc_types.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index 855c8acaf3f1..089b195e2635 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -867,7 +867,7 @@ static void *empty_lrc_data(struct xe_hw_engine *hwe) static void xe_lrc_set_ppgtt(struct xe_lrc *lrc, struct xe_vm *vm) { - u64 desc = xe_vm_pdp4_descriptor(vm, lrc->tile); + u64 desc = xe_vm_pdp4_descriptor(vm, gt_to_tile(lrc->gt)); xe_lrc_write_ctx_reg(lrc, CTX_PDP0_UDW, upper_32_bits(desc)); xe_lrc_write_ctx_reg(lrc, CTX_PDP0_LDW, lower_32_bits(desc)); @@ -900,6 +900,7 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, int err; kref_init(&lrc->refcount); + lrc->gt = gt; lrc->flags = 0; lrc_size = ring_size + xe_gt_lrc_size(gt, hwe->class); if (xe_gt_has_indirect_ring_state(gt)) @@ -921,7 +922,6 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, return PTR_ERR(lrc->bo); lrc->size = lrc_size; - lrc->tile = gt_to_tile(hwe->gt); lrc->ring.size = ring_size; lrc->ring.tail = 0; lrc->ctx_timestamp = 0; diff --git a/drivers/gpu/drm/xe/xe_lrc_types.h b/drivers/gpu/drm/xe/xe_lrc_types.h index 71ecb453f811..cd38586ae989 100644 --- a/drivers/gpu/drm/xe/xe_lrc_types.h +++ b/drivers/gpu/drm/xe/xe_lrc_types.h @@ -25,8 +25,8 @@ struct xe_lrc { /** @size: size of lrc including any indirect ring state page */ u32 size; - /** @tile: tile which this LRC belongs to */ - struct xe_tile *tile; + /** @gt: gt which this LRC belongs to */ + struct xe_gt *gt; /** @flags: LRC flags */ #define XE_LRC_FLAG_INDIRECT_RING_STATE 0x1 -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/3] drm/xe: Save the gt pointer in lrc and drop the tile 2025-04-26 0:29 ` [PATCH v2 2/3] drm/xe: Save the gt pointer in lrc and drop the tile Umesh Nerlige Ramappa @ 2025-04-26 4:43 ` Matthew Brost 0 siblings, 0 replies; 7+ messages in thread From: Matthew Brost @ 2025-04-26 4:43 UTC (permalink / raw) To: Umesh Nerlige Ramappa; +Cc: intel-xe, Lucas De Marchi, Matt Roper On Fri, Apr 25, 2025 at 05:29:50PM -0700, Umesh Nerlige Ramappa wrote: > Save the gt pointer in the lrc so that it can used for gt based helpers. > > Signed-off-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com> Reviewed-by: Matthew Brost <matthew.brost@intel.com> > --- > drivers/gpu/drm/xe/xe_lrc.c | 4 ++-- > drivers/gpu/drm/xe/xe_lrc_types.h | 4 ++-- > 2 files changed, 4 insertions(+), 4 deletions(-) > > diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c > index 855c8acaf3f1..089b195e2635 100644 > --- a/drivers/gpu/drm/xe/xe_lrc.c > +++ b/drivers/gpu/drm/xe/xe_lrc.c > @@ -867,7 +867,7 @@ static void *empty_lrc_data(struct xe_hw_engine *hwe) > > static void xe_lrc_set_ppgtt(struct xe_lrc *lrc, struct xe_vm *vm) > { > - u64 desc = xe_vm_pdp4_descriptor(vm, lrc->tile); > + u64 desc = xe_vm_pdp4_descriptor(vm, gt_to_tile(lrc->gt)); > > xe_lrc_write_ctx_reg(lrc, CTX_PDP0_UDW, upper_32_bits(desc)); > xe_lrc_write_ctx_reg(lrc, CTX_PDP0_LDW, lower_32_bits(desc)); > @@ -900,6 +900,7 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, > int err; > > kref_init(&lrc->refcount); > + lrc->gt = gt; > lrc->flags = 0; > lrc_size = ring_size + xe_gt_lrc_size(gt, hwe->class); > if (xe_gt_has_indirect_ring_state(gt)) > @@ -921,7 +922,6 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, > return PTR_ERR(lrc->bo); > > lrc->size = lrc_size; > - lrc->tile = gt_to_tile(hwe->gt); > lrc->ring.size = ring_size; > lrc->ring.tail = 0; > lrc->ctx_timestamp = 0; > diff --git a/drivers/gpu/drm/xe/xe_lrc_types.h b/drivers/gpu/drm/xe/xe_lrc_types.h > index 71ecb453f811..cd38586ae989 100644 > --- a/drivers/gpu/drm/xe/xe_lrc_types.h > +++ b/drivers/gpu/drm/xe/xe_lrc_types.h > @@ -25,8 +25,8 @@ struct xe_lrc { > /** @size: size of lrc including any indirect ring state page */ > u32 size; > > - /** @tile: tile which this LRC belongs to */ > - struct xe_tile *tile; > + /** @gt: gt which this LRC belongs to */ > + struct xe_gt *gt; > > /** @flags: LRC flags */ > #define XE_LRC_FLAG_INDIRECT_RING_STATE 0x1 > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 3/3] drm/xe: Add WA BB to capture active context utilization 2025-04-26 0:29 [PATCH v2 0/3] Support active context utilization Umesh Nerlige Ramappa 2025-04-26 0:29 ` [PATCH v2 1/3] drm/xe: Save CTX_TIMESTAMP mmio value instead of LRC value Umesh Nerlige Ramappa 2025-04-26 0:29 ` [PATCH v2 2/3] drm/xe: Save the gt pointer in lrc and drop the tile Umesh Nerlige Ramappa @ 2025-04-26 0:29 ` Umesh Nerlige Ramappa 2025-04-26 4:54 ` Matthew Brost 2 siblings, 1 reply; 7+ messages in thread From: Umesh Nerlige Ramappa @ 2025-04-26 0:29 UTC (permalink / raw) To: intel-xe, Lucas De Marchi, matthew.brost; +Cc: Matt Roper Context Timestamp (CTX_TIMESTAMP) in the lrc accumulates the run ticks of the context, but only gets updated when the context switches out. In order to check how long a context has been active before it switches out, two things are required: (1) Determine if the context is running: To do so, we program the wa bb to set an initial value for CTX_TIMESTAMP in the lrc. The value chosen is 1 since 0 is the initial value when the lrc is initialized. During a query, we just check for this value to determine if the context is active. If the context switched out, it would overwrite this location with the actual CTX_TIMESTAMP MMIO value. Note that wa bb will run after the context has been restored, so reusing this lrc location will not clobber anything. (2) Calculate the time that the context has been active for: The CTX_TIMESTAMP ticks only when the context is active. If a context is active, we just use the CTX_TIMESTAMP MMIO as the new value of utilization. While doing so, we need to read the CTX_TIMESTAMP MMIO for the specific engine instance. Since we do not know which instance the context is running on until it is scheduled, we also read the ENGINE_ID MMIO in the wa bb and store it in the PPHSWP. Using the above 2 instructions in a WA BB, capture active context utilization. v2: (Matt Brost) - This breaks TDR, fix it by saving the CTX_TIMESTAMP register "drm/xe: Save CTX_TIMESTAMP mmio value instead of LRC value" - Drop tile from lrc if using gt "drm/xe: Save the gt pointer in lrc and drop the tile" Signed-off-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com> --- .../gpu/drm/xe/instructions/xe_mi_commands.h | 4 + drivers/gpu/drm/xe/regs/xe_engine_regs.h | 4 + drivers/gpu/drm/xe/regs/xe_lrc_layout.h | 1 + drivers/gpu/drm/xe/xe_lrc.c | 167 +++++++++++++++++- drivers/gpu/drm/xe/xe_lrc_types.h | 3 + 5 files changed, 177 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h index eba582058d55..9153a7cd2ceb 100644 --- a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h +++ b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h @@ -62,6 +62,10 @@ #define MI_LOAD_REGISTER_MEM (__MI_INSTR(0x29) | XE_INSTR_NUM_DW(4)) #define MI_LRM_USE_GGTT REG_BIT(22) +#define MI_STORE_REGISTER_MEM (__MI_INSTR(0x24) | XE_INSTR_NUM_DW(4)) +#define MI_SRM_USE_GGTT REG_BIT(22) +#define MI_SRM_ADD_CS_OFFSET REG_BIT(19) + #define MI_LOAD_REGISTER_REG (__MI_INSTR(0x2a) | XE_INSTR_NUM_DW(3)) #define MI_LRR_DST_CS_MMIO REG_BIT(19) #define MI_LRR_SRC_CS_MMIO REG_BIT(18) diff --git a/drivers/gpu/drm/xe/regs/xe_engine_regs.h b/drivers/gpu/drm/xe/regs/xe_engine_regs.h index da713634d6a0..cc698136a6f6 100644 --- a/drivers/gpu/drm/xe/regs/xe_engine_regs.h +++ b/drivers/gpu/drm/xe/regs/xe_engine_regs.h @@ -43,6 +43,10 @@ #define XEHPC_BCS8_RING_BASE 0x3ee000 #define GSCCS_RING_BASE 0x11a000 +#define ENGINE_ID(base) XE_REG((base) + 0x8c) +#define ENGINE_INSTANCE_ID REG_GENMASK(9, 4) +#define ENGINE_CLASS_ID REG_GENMASK(2, 0) + #define RING_TAIL(base) XE_REG((base) + 0x30) #define TAIL_ADDR REG_GENMASK(20, 3) diff --git a/drivers/gpu/drm/xe/regs/xe_lrc_layout.h b/drivers/gpu/drm/xe/regs/xe_lrc_layout.h index 57944f90bbf6..210be0ef12f2 100644 --- a/drivers/gpu/drm/xe/regs/xe_lrc_layout.h +++ b/drivers/gpu/drm/xe/regs/xe_lrc_layout.h @@ -11,6 +11,7 @@ #define CTX_RING_TAIL (0x06 + 1) #define CTX_RING_START (0x08 + 1) #define CTX_RING_CTL (0x0a + 1) +#define CTX_BB_PER_CTX_PTR (0x12 + 1) #define CTX_TIMESTAMP (0x22 + 1) #define CTX_INDIRECT_RING_STATE (0x26 + 1) #define CTX_PDP0_UDW (0x30 + 1) diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index 089b195e2635..a78acec1a358 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -24,6 +24,7 @@ #include "xe_hw_fence.h" #include "xe_map.h" #include "xe_memirq.h" +#include "xe_mmio.h" #include "xe_sriov.h" #include "xe_trace_lrc.h" #include "xe_vm.h" @@ -654,6 +655,7 @@ u32 xe_lrc_pphwsp_offset(struct xe_lrc *lrc) #define LRC_START_SEQNO_PPHWSP_OFFSET (LRC_SEQNO_PPHWSP_OFFSET + 8) #define LRC_CTX_JOB_TIMESTAMP_OFFSET (LRC_START_SEQNO_PPHWSP_OFFSET + 8) #define LRC_PARALLEL_PPHWSP_OFFSET 2048 +#define LRC_ENGINE_ID_PPHWSP_OFFSET 2096 u32 xe_lrc_regs_offset(struct xe_lrc *lrc) { @@ -697,11 +699,21 @@ static inline u32 __xe_lrc_parallel_offset(struct xe_lrc *lrc) return xe_lrc_pphwsp_offset(lrc) + LRC_PARALLEL_PPHWSP_OFFSET; } +static inline u32 __xe_lrc_engine_id_offset(struct xe_lrc *lrc) +{ + return xe_lrc_pphwsp_offset(lrc) + LRC_ENGINE_ID_PPHWSP_OFFSET; +} + static u32 __xe_lrc_ctx_timestamp_offset(struct xe_lrc *lrc) { return __xe_lrc_regs_offset(lrc) + CTX_TIMESTAMP * sizeof(u32); } +static u32 __xe_lrc_bb_per_ctx_ptr_offset(struct xe_lrc *lrc) +{ + return __xe_lrc_regs_offset(lrc) + CTX_BB_PER_CTX_PTR * sizeof(u32); +} + static inline u32 __xe_lrc_indirect_ring_offset(struct xe_lrc *lrc) { /* Indirect ring state page is at the very end of LRC */ @@ -731,6 +743,8 @@ DECL_MAP_ADDR_HELPERS(ctx_job_timestamp) DECL_MAP_ADDR_HELPERS(ctx_timestamp) DECL_MAP_ADDR_HELPERS(parallel) DECL_MAP_ADDR_HELPERS(indirect_ring) +DECL_MAP_ADDR_HELPERS(bb_per_ctx_ptr) +DECL_MAP_ADDR_HELPERS(engine_id) #undef DECL_MAP_ADDR_HELPERS @@ -880,6 +894,58 @@ static void xe_lrc_finish(struct xe_lrc *lrc) xe_bo_unpin(lrc->bo); xe_bo_unlock(lrc->bo); xe_bo_put(lrc->bo); + xe_bo_unpin_map_no_vm(lrc->bb_per_ctx_bo); +} + +/* + * xe_lrc_setup_utilization() - Setup wa bb to assist in calculating active + * context run ticks. + * @lrc: Pointer to the lrc. + * + * Context Timestamp (CTX_TIMESTAMP) in the lrc accumulates the run ticks of the + * context, but only gets updated when the context switches out. In order to + * check how long a context has been running before it switches out, two things + * are required: + * + * (1) Determine if the context is running + * To do so, we program the wa bb to set an initial value for CTX_TIMESTAMP in + * the lrc. The value chosen is 1 since 0 is the initial value when the lrc is + * initialized. During a query, we just check for this value to determine if the + * context is active. If the context switched out, it would overwrite this + * location with the actual CTX_TIMESTAMP MMIO value. Note that wa bb will run + * after the context has been restored, so reusing this lrc location will not + * clobber anything. + * + * (2) Calculate the time that the context has been running for + * The CTX_TIMESTAMP ticks only when the context is active. If a context is + * active, we just use the CTX_TIMESTAMP MMIO as the new value of utilization. + * While doing so, we need to read the CTX_TIMESTAMP MMIO from the specific + * engine instance. Since we do not know which instance the context is running + * on until it is scheduled, we also read the ENGINE_ID MMIO in the wa bb and + * store it in the PPHSWP. + */ + +static void xe_lrc_setup_utilization(struct xe_lrc *lrc) +{ + u32 *cmd; + + cmd = lrc->bb_per_ctx_bo->vmap.vaddr; + + *cmd++ = MI_STORE_REGISTER_MEM | MI_SRM_USE_GGTT | MI_SRM_ADD_CS_OFFSET; + *cmd++ = ENGINE_ID(0).addr; + *cmd++ = __xe_lrc_engine_id_ggtt_addr(lrc); + *cmd++ = 0; + + *cmd++ = MI_STORE_DATA_IMM | MI_SDI_GGTT | MI_SDI_NUM_DW(1); + *cmd++ = __xe_lrc_ctx_timestamp_ggtt_addr(lrc); + *cmd++ = 0; + *cmd++ = 1; + + *cmd++ = MI_BATCH_BUFFER_END; + + xe_lrc_write_ctx_reg(lrc, CTX_BB_PER_CTX_PTR, + xe_bo_ggtt_addr(lrc->bb_per_ctx_bo) | 1); + } #define PVC_CTX_ASID (0x2e + 1) @@ -921,6 +987,14 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, if (IS_ERR(lrc->bo)) return PTR_ERR(lrc->bo); + lrc->bb_per_ctx_bo = xe_bo_create_pin_map(xe, tile, NULL, SZ_4K, + ttm_bo_type_kernel, + bo_flags); + if (IS_ERR(lrc->bb_per_ctx_bo)) { + err = PTR_ERR(lrc->bb_per_ctx_bo); + goto err_lrc_finish; + } + lrc->size = lrc_size; lrc->ring.size = ring_size; lrc->ring.tail = 0; @@ -1026,6 +1100,8 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, map = __xe_lrc_start_seqno_map(lrc); xe_map_write32(lrc_to_xe(lrc), &map, lrc->fence_ctx.next_seqno - 1); + xe_lrc_setup_utilization(lrc); + return 0; err_lrc_finish: @@ -1245,6 +1321,21 @@ struct iosys_map xe_lrc_parallel_map(struct xe_lrc *lrc) return __xe_lrc_parallel_map(lrc); } +/** + * xe_lrc_engine_id() - Read engine id value + * @lrc: Pointer to the lrc. + * + * Returns: context id value + */ +static u32 xe_lrc_engine_id(struct xe_lrc *lrc) +{ + struct xe_device *xe = lrc_to_xe(lrc); + struct iosys_map map; + + map = __xe_lrc_engine_id_map(lrc); + return xe_map_read32(xe, &map); +} + static int instr_dw(u32 cmd_header) { /* GFXPIPE "SINGLE_DW" opcodes are a single dword */ @@ -1792,21 +1883,93 @@ void xe_lrc_snapshot_free(struct xe_lrc_snapshot *snapshot) kfree(snapshot); } +static u32 get_ctx_timestamp(struct xe_lrc *lrc, u32 engine_id) +{ + static u32 base[XE_ENGINE_CLASS_MAX][XE_HW_ENGINE_MAX_INSTANCE] = { + { + RENDER_RING_BASE, + }, + { + BSD_RING_BASE, + BSD2_RING_BASE, + BSD3_RING_BASE, + BSD4_RING_BASE, + XEHP_BSD5_RING_BASE, + XEHP_BSD6_RING_BASE, + XEHP_BSD7_RING_BASE, + XEHP_BSD8_RING_BASE, + }, + { + VEBOX_RING_BASE, + VEBOX2_RING_BASE, + XEHP_VEBOX3_RING_BASE, + XEHP_VEBOX4_RING_BASE, + }, + { + BLT_RING_BASE, + XEHPC_BCS1_RING_BASE, + XEHPC_BCS2_RING_BASE, + XEHPC_BCS3_RING_BASE, + XEHPC_BCS4_RING_BASE, + XEHPC_BCS5_RING_BASE, + XEHPC_BCS6_RING_BASE, + XEHPC_BCS7_RING_BASE, + XEHPC_BCS8_RING_BASE, + }, + { 0 }, + { + COMPUTE0_RING_BASE, + COMPUTE1_RING_BASE, + COMPUTE2_RING_BASE, + COMPUTE3_RING_BASE, + }, + }; + u32 c = REG_FIELD_GET(ENGINE_CLASS_ID, engine_id); + u32 i = REG_FIELD_GET(ENGINE_INSTANCE_ID, engine_id); + u32 mmio_base = base[c][i]; + + if (xe_gt_WARN_ONCE(lrc->gt, !mmio_base, + "Unexpected engine c:i %d:%d for context utilization\n", + c, i)) + return 0; + + return xe_mmio_read32(&lrc->gt->mmio, RING_CTX_TIMESTAMP(mmio_base)); +} + /** * xe_lrc_update_timestamp() - Update ctx timestamp * @lrc: Pointer to the lrc. * @old_ts: Old timestamp value * * Populate @old_ts current saved ctx timestamp, read new ctx timestamp and - * update saved value. + * update saved value. With support for active contexts, the calculation may be + * slightly racy, so follow a read-again logic to ensure that the context is + * still active before returning the right timestamp. * * Returns: New ctx timestamp value */ u32 xe_lrc_update_timestamp(struct xe_lrc *lrc, u32 *old_ts) { + u32 ctx_lrc, ctx_reg, engine_id; + *old_ts = lrc->ctx_timestamp; - lrc->ctx_timestamp = xe_lrc_ctx_timestamp(lrc); + ctx_lrc = xe_lrc_ctx_timestamp(lrc); + if (ctx_lrc == 1) { + engine_id = xe_lrc_engine_id(lrc); + ctx_reg = get_ctx_timestamp(lrc, engine_id); + if (!ctx_reg) + lrc->ctx_timestamp = *old_ts; + else + lrc->ctx_timestamp = ctx_reg; + + /* read lrc again to ensure context is still active */ + ctx_lrc = xe_lrc_ctx_timestamp(lrc); + } + + /* If context switched out, just use the lrc value */ + if (ctx_lrc != 1) + lrc->ctx_timestamp = ctx_lrc; trace_xe_lrc_update_timestamp(lrc, *old_ts); diff --git a/drivers/gpu/drm/xe/xe_lrc_types.h b/drivers/gpu/drm/xe/xe_lrc_types.h index cd38586ae989..a62d0b8ca8a4 100644 --- a/drivers/gpu/drm/xe/xe_lrc_types.h +++ b/drivers/gpu/drm/xe/xe_lrc_types.h @@ -53,6 +53,9 @@ struct xe_lrc { /** @ctx_timestamp: readout value of CTX_TIMESTAMP on last update */ u32 ctx_timestamp; + + /** @bb_per_ctx_bo: buffer object for per context batch wa buffer */ + struct xe_bo *bb_per_ctx_bo; }; struct xe_lrc_snapshot; -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 3/3] drm/xe: Add WA BB to capture active context utilization 2025-04-26 0:29 ` [PATCH v2 3/3] drm/xe: Add WA BB to capture active context utilization Umesh Nerlige Ramappa @ 2025-04-26 4:54 ` Matthew Brost 0 siblings, 0 replies; 7+ messages in thread From: Matthew Brost @ 2025-04-26 4:54 UTC (permalink / raw) To: Umesh Nerlige Ramappa; +Cc: intel-xe, Lucas De Marchi, Matt Roper On Fri, Apr 25, 2025 at 05:29:51PM -0700, Umesh Nerlige Ramappa wrote: > Context Timestamp (CTX_TIMESTAMP) in the lrc accumulates the run ticks > of the context, but only gets updated when the context switches out. In > order to check how long a context has been active before it switches > out, two things are required: > > (1) Determine if the context is running: > > To do so, we program the wa bb to set an initial value for CTX_TIMESTAMP > in the lrc. The value chosen is 1 since 0 is the initial value when the > lrc is initialized. During a query, we just check for this value to > determine if the context is active. If the context switched out, it > would overwrite this location with the actual CTX_TIMESTAMP MMIO value. > Note that wa bb will run after the context has been restored, so reusing > this lrc location will not clobber anything. > > (2) Calculate the time that the context has been active for: > > The CTX_TIMESTAMP ticks only when the context is active. If a context is > active, we just use the CTX_TIMESTAMP MMIO as the new value of > utilization. While doing so, we need to read the CTX_TIMESTAMP MMIO > for the specific engine instance. Since we do not know which instance > the context is running on until it is scheduled, we also read the > ENGINE_ID MMIO in the wa bb and store it in the PPHSWP. > > Using the above 2 instructions in a WA BB, capture active context > utilization. > > v2: (Matt Brost) > - This breaks TDR, fix it by saving the CTX_TIMESTAMP register > "drm/xe: Save CTX_TIMESTAMP mmio value instead of LRC value" > - Drop tile from lrc if using gt > "drm/xe: Save the gt pointer in lrc and drop the tile" > > Signed-off-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com> > --- > .../gpu/drm/xe/instructions/xe_mi_commands.h | 4 + > drivers/gpu/drm/xe/regs/xe_engine_regs.h | 4 + > drivers/gpu/drm/xe/regs/xe_lrc_layout.h | 1 + > drivers/gpu/drm/xe/xe_lrc.c | 167 +++++++++++++++++- > drivers/gpu/drm/xe/xe_lrc_types.h | 3 + > 5 files changed, 177 insertions(+), 2 deletions(-) > > diff --git a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h > index eba582058d55..9153a7cd2ceb 100644 > --- a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h > +++ b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h > @@ -62,6 +62,10 @@ > #define MI_LOAD_REGISTER_MEM (__MI_INSTR(0x29) | XE_INSTR_NUM_DW(4)) > #define MI_LRM_USE_GGTT REG_BIT(22) > > +#define MI_STORE_REGISTER_MEM (__MI_INSTR(0x24) | XE_INSTR_NUM_DW(4)) > +#define MI_SRM_USE_GGTT REG_BIT(22) > +#define MI_SRM_ADD_CS_OFFSET REG_BIT(19) > + > #define MI_LOAD_REGISTER_REG (__MI_INSTR(0x2a) | XE_INSTR_NUM_DW(3)) > #define MI_LRR_DST_CS_MMIO REG_BIT(19) > #define MI_LRR_SRC_CS_MMIO REG_BIT(18) > diff --git a/drivers/gpu/drm/xe/regs/xe_engine_regs.h b/drivers/gpu/drm/xe/regs/xe_engine_regs.h > index da713634d6a0..cc698136a6f6 100644 > --- a/drivers/gpu/drm/xe/regs/xe_engine_regs.h > +++ b/drivers/gpu/drm/xe/regs/xe_engine_regs.h > @@ -43,6 +43,10 @@ > #define XEHPC_BCS8_RING_BASE 0x3ee000 > #define GSCCS_RING_BASE 0x11a000 > > +#define ENGINE_ID(base) XE_REG((base) + 0x8c) > +#define ENGINE_INSTANCE_ID REG_GENMASK(9, 4) > +#define ENGINE_CLASS_ID REG_GENMASK(2, 0) > + > #define RING_TAIL(base) XE_REG((base) + 0x30) > #define TAIL_ADDR REG_GENMASK(20, 3) > > diff --git a/drivers/gpu/drm/xe/regs/xe_lrc_layout.h b/drivers/gpu/drm/xe/regs/xe_lrc_layout.h > index 57944f90bbf6..210be0ef12f2 100644 > --- a/drivers/gpu/drm/xe/regs/xe_lrc_layout.h > +++ b/drivers/gpu/drm/xe/regs/xe_lrc_layout.h > @@ -11,6 +11,7 @@ > #define CTX_RING_TAIL (0x06 + 1) > #define CTX_RING_START (0x08 + 1) > #define CTX_RING_CTL (0x0a + 1) > +#define CTX_BB_PER_CTX_PTR (0x12 + 1) > #define CTX_TIMESTAMP (0x22 + 1) > #define CTX_INDIRECT_RING_STATE (0x26 + 1) > #define CTX_PDP0_UDW (0x30 + 1) > diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c > index 089b195e2635..a78acec1a358 100644 > --- a/drivers/gpu/drm/xe/xe_lrc.c > +++ b/drivers/gpu/drm/xe/xe_lrc.c > @@ -24,6 +24,7 @@ > #include "xe_hw_fence.h" > #include "xe_map.h" > #include "xe_memirq.h" > +#include "xe_mmio.h" > #include "xe_sriov.h" > #include "xe_trace_lrc.h" > #include "xe_vm.h" > @@ -654,6 +655,7 @@ u32 xe_lrc_pphwsp_offset(struct xe_lrc *lrc) > #define LRC_START_SEQNO_PPHWSP_OFFSET (LRC_SEQNO_PPHWSP_OFFSET + 8) > #define LRC_CTX_JOB_TIMESTAMP_OFFSET (LRC_START_SEQNO_PPHWSP_OFFSET + 8) > #define LRC_PARALLEL_PPHWSP_OFFSET 2048 > +#define LRC_ENGINE_ID_PPHWSP_OFFSET 2096 > > u32 xe_lrc_regs_offset(struct xe_lrc *lrc) > { > @@ -697,11 +699,21 @@ static inline u32 __xe_lrc_parallel_offset(struct xe_lrc *lrc) > return xe_lrc_pphwsp_offset(lrc) + LRC_PARALLEL_PPHWSP_OFFSET; > } > > +static inline u32 __xe_lrc_engine_id_offset(struct xe_lrc *lrc) > +{ > + return xe_lrc_pphwsp_offset(lrc) + LRC_ENGINE_ID_PPHWSP_OFFSET; > +} > + > static u32 __xe_lrc_ctx_timestamp_offset(struct xe_lrc *lrc) > { > return __xe_lrc_regs_offset(lrc) + CTX_TIMESTAMP * sizeof(u32); > } > > +static u32 __xe_lrc_bb_per_ctx_ptr_offset(struct xe_lrc *lrc) > +{ > + return __xe_lrc_regs_offset(lrc) + CTX_BB_PER_CTX_PTR * sizeof(u32); > +} > + > static inline u32 __xe_lrc_indirect_ring_offset(struct xe_lrc *lrc) > { > /* Indirect ring state page is at the very end of LRC */ > @@ -731,6 +743,8 @@ DECL_MAP_ADDR_HELPERS(ctx_job_timestamp) > DECL_MAP_ADDR_HELPERS(ctx_timestamp) > DECL_MAP_ADDR_HELPERS(parallel) > DECL_MAP_ADDR_HELPERS(indirect_ring) > +DECL_MAP_ADDR_HELPERS(bb_per_ctx_ptr) This appears to be unused. > +DECL_MAP_ADDR_HELPERS(engine_id) > > #undef DECL_MAP_ADDR_HELPERS > > @@ -880,6 +894,58 @@ static void xe_lrc_finish(struct xe_lrc *lrc) > xe_bo_unpin(lrc->bo); > xe_bo_unlock(lrc->bo); > xe_bo_put(lrc->bo); > + xe_bo_unpin_map_no_vm(lrc->bb_per_ctx_bo); > +} > + > +/* > + * xe_lrc_setup_utilization() - Setup wa bb to assist in calculating active > + * context run ticks. > + * @lrc: Pointer to the lrc. > + * > + * Context Timestamp (CTX_TIMESTAMP) in the lrc accumulates the run ticks of the > + * context, but only gets updated when the context switches out. In order to > + * check how long a context has been running before it switches out, two things > + * are required: > + * > + * (1) Determine if the context is running > + * To do so, we program the wa bb to set an initial value for CTX_TIMESTAMP in > + * the lrc. The value chosen is 1 since 0 is the initial value when the lrc is > + * initialized. During a query, we just check for this value to determine if the > + * context is active. If the context switched out, it would overwrite this > + * location with the actual CTX_TIMESTAMP MMIO value. Note that wa bb will run > + * after the context has been restored, so reusing this lrc location will not > + * clobber anything. > + * > + * (2) Calculate the time that the context has been running for > + * The CTX_TIMESTAMP ticks only when the context is active. If a context is > + * active, we just use the CTX_TIMESTAMP MMIO as the new value of utilization. > + * While doing so, we need to read the CTX_TIMESTAMP MMIO from the specific > + * engine instance. Since we do not know which instance the context is running > + * on until it is scheduled, we also read the ENGINE_ID MMIO in the wa bb and > + * store it in the PPHSWP. > + */ > + > +static void xe_lrc_setup_utilization(struct xe_lrc *lrc) > +{ > + u32 *cmd; > + > + cmd = lrc->bb_per_ctx_bo->vmap.vaddr; > + > + *cmd++ = MI_STORE_REGISTER_MEM | MI_SRM_USE_GGTT | MI_SRM_ADD_CS_OFFSET; > + *cmd++ = ENGINE_ID(0).addr; > + *cmd++ = __xe_lrc_engine_id_ggtt_addr(lrc); > + *cmd++ = 0; > + > + *cmd++ = MI_STORE_DATA_IMM | MI_SDI_GGTT | MI_SDI_NUM_DW(1); > + *cmd++ = __xe_lrc_ctx_timestamp_ggtt_addr(lrc); > + *cmd++ = 0; > + *cmd++ = 1; Nit: Rather than '1' can we have define indicating this is a special value. Also there is a small possibily (1 in 4 billion - I think) of a race here. It is possible than an LRC switches out with a value of '1' and I believe we'd read garbage from get_ctx_timestamp() ... Is there really not another register in the LRC we can steal a bit from? If not, maybe we can live with this race? At minimum we'd need comment somewhere explaining this - at worst we'd get some bad stats or in a very unlikely case get false job timeout if we switch the TDR to use this new code path. The patch / design LGTM to good me though. Matt > + > + *cmd++ = MI_BATCH_BUFFER_END; > + > + xe_lrc_write_ctx_reg(lrc, CTX_BB_PER_CTX_PTR, > + xe_bo_ggtt_addr(lrc->bb_per_ctx_bo) | 1); > + > } > > #define PVC_CTX_ASID (0x2e + 1) > @@ -921,6 +987,14 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, > if (IS_ERR(lrc->bo)) > return PTR_ERR(lrc->bo); > > + lrc->bb_per_ctx_bo = xe_bo_create_pin_map(xe, tile, NULL, SZ_4K, > + ttm_bo_type_kernel, > + bo_flags); > + if (IS_ERR(lrc->bb_per_ctx_bo)) { > + err = PTR_ERR(lrc->bb_per_ctx_bo); > + goto err_lrc_finish; > + } > + > lrc->size = lrc_size; > lrc->ring.size = ring_size; > lrc->ring.tail = 0; > @@ -1026,6 +1100,8 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, > map = __xe_lrc_start_seqno_map(lrc); > xe_map_write32(lrc_to_xe(lrc), &map, lrc->fence_ctx.next_seqno - 1); > > + xe_lrc_setup_utilization(lrc); > + > return 0; > > err_lrc_finish: > @@ -1245,6 +1321,21 @@ struct iosys_map xe_lrc_parallel_map(struct xe_lrc *lrc) > return __xe_lrc_parallel_map(lrc); > } > > +/** > + * xe_lrc_engine_id() - Read engine id value > + * @lrc: Pointer to the lrc. > + * > + * Returns: context id value > + */ > +static u32 xe_lrc_engine_id(struct xe_lrc *lrc) > +{ > + struct xe_device *xe = lrc_to_xe(lrc); > + struct iosys_map map; > + > + map = __xe_lrc_engine_id_map(lrc); > + return xe_map_read32(xe, &map); > +} > + > static int instr_dw(u32 cmd_header) > { > /* GFXPIPE "SINGLE_DW" opcodes are a single dword */ > @@ -1792,21 +1883,93 @@ void xe_lrc_snapshot_free(struct xe_lrc_snapshot *snapshot) > kfree(snapshot); > } > > +static u32 get_ctx_timestamp(struct xe_lrc *lrc, u32 engine_id) > +{ > + static u32 base[XE_ENGINE_CLASS_MAX][XE_HW_ENGINE_MAX_INSTANCE] = { > + { > + RENDER_RING_BASE, > + }, > + { > + BSD_RING_BASE, > + BSD2_RING_BASE, > + BSD3_RING_BASE, > + BSD4_RING_BASE, > + XEHP_BSD5_RING_BASE, > + XEHP_BSD6_RING_BASE, > + XEHP_BSD7_RING_BASE, > + XEHP_BSD8_RING_BASE, > + }, > + { > + VEBOX_RING_BASE, > + VEBOX2_RING_BASE, > + XEHP_VEBOX3_RING_BASE, > + XEHP_VEBOX4_RING_BASE, > + }, > + { > + BLT_RING_BASE, > + XEHPC_BCS1_RING_BASE, > + XEHPC_BCS2_RING_BASE, > + XEHPC_BCS3_RING_BASE, > + XEHPC_BCS4_RING_BASE, > + XEHPC_BCS5_RING_BASE, > + XEHPC_BCS6_RING_BASE, > + XEHPC_BCS7_RING_BASE, > + XEHPC_BCS8_RING_BASE, > + }, > + { 0 }, > + { > + COMPUTE0_RING_BASE, > + COMPUTE1_RING_BASE, > + COMPUTE2_RING_BASE, > + COMPUTE3_RING_BASE, > + }, > + }; > + u32 c = REG_FIELD_GET(ENGINE_CLASS_ID, engine_id); > + u32 i = REG_FIELD_GET(ENGINE_INSTANCE_ID, engine_id); > + u32 mmio_base = base[c][i]; > + > + if (xe_gt_WARN_ONCE(lrc->gt, !mmio_base, > + "Unexpected engine c:i %d:%d for context utilization\n", > + c, i)) > + return 0; > + > + return xe_mmio_read32(&lrc->gt->mmio, RING_CTX_TIMESTAMP(mmio_base)); > +} > + > /** > * xe_lrc_update_timestamp() - Update ctx timestamp > * @lrc: Pointer to the lrc. > * @old_ts: Old timestamp value > * > * Populate @old_ts current saved ctx timestamp, read new ctx timestamp and > - * update saved value. > + * update saved value. With support for active contexts, the calculation may be > + * slightly racy, so follow a read-again logic to ensure that the context is > + * still active before returning the right timestamp. > * > * Returns: New ctx timestamp value > */ > u32 xe_lrc_update_timestamp(struct xe_lrc *lrc, u32 *old_ts) > { > + u32 ctx_lrc, ctx_reg, engine_id; > + > *old_ts = lrc->ctx_timestamp; > > - lrc->ctx_timestamp = xe_lrc_ctx_timestamp(lrc); > + ctx_lrc = xe_lrc_ctx_timestamp(lrc); > + if (ctx_lrc == 1) { > + engine_id = xe_lrc_engine_id(lrc); > + ctx_reg = get_ctx_timestamp(lrc, engine_id); > + if (!ctx_reg) > + lrc->ctx_timestamp = *old_ts; > + else > + lrc->ctx_timestamp = ctx_reg; > + > + /* read lrc again to ensure context is still active */ > + ctx_lrc = xe_lrc_ctx_timestamp(lrc); > + } > + > + /* If context switched out, just use the lrc value */ > + if (ctx_lrc != 1) > + lrc->ctx_timestamp = ctx_lrc; > > trace_xe_lrc_update_timestamp(lrc, *old_ts); > > diff --git a/drivers/gpu/drm/xe/xe_lrc_types.h b/drivers/gpu/drm/xe/xe_lrc_types.h > index cd38586ae989..a62d0b8ca8a4 100644 > --- a/drivers/gpu/drm/xe/xe_lrc_types.h > +++ b/drivers/gpu/drm/xe/xe_lrc_types.h > @@ -53,6 +53,9 @@ struct xe_lrc { > > /** @ctx_timestamp: readout value of CTX_TIMESTAMP on last update */ > u32 ctx_timestamp; > + > + /** @bb_per_ctx_bo: buffer object for per context batch wa buffer */ > + struct xe_bo *bb_per_ctx_bo; > }; > > struct xe_lrc_snapshot; > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-04-26 4:53 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-04-26 0:29 [PATCH v2 0/3] Support active context utilization Umesh Nerlige Ramappa 2025-04-26 0:29 ` [PATCH v2 1/3] drm/xe: Save CTX_TIMESTAMP mmio value instead of LRC value Umesh Nerlige Ramappa 2025-04-26 4:42 ` Matthew Brost 2025-04-26 0:29 ` [PATCH v2 2/3] drm/xe: Save the gt pointer in lrc and drop the tile Umesh Nerlige Ramappa 2025-04-26 4:43 ` Matthew Brost 2025-04-26 0:29 ` [PATCH v2 3/3] drm/xe: Add WA BB to capture active context utilization Umesh Nerlige Ramappa 2025-04-26 4:54 ` Matthew Brost
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.