diff options
Diffstat (limited to 'runlist_procfs.c')
| -rw-r--r-- | runlist_procfs.c | 645 |
1 files changed, 636 insertions, 9 deletions
diff --git a/runlist_procfs.c b/runlist_procfs.c index b2159f6..a3a6df3 100644 --- a/runlist_procfs.c +++ b/runlist_procfs.c | |||
| @@ -1,12 +1,117 @@ | |||
| 1 | #include <linux/seq_file.h> // For seq_* functions and types | 1 | #include <linux/seq_file.h> // For seq_* functions and types |
| 2 | #include <linux/version.h> // Macros to detect kernel version | 2 | #include <linux/version.h> // Macros to detect kernel version |
| 3 | #include <linux/platform_device.h> // For platform_get_resource() | ||
| 4 | #include <linux/pci.h> // For pci_resource_start() | ||
| 5 | #include <linux/iommu.h> // For iommu_ functions | ||
| 6 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0) | ||
| 7 | #include <linux/dma-map-ops.h> // For get_dma_ops() | ||
| 8 | #endif | ||
| 3 | 9 | ||
| 4 | #include "nvdebug_linux.h" | 10 | #include "nvdebug_linux.h" |
| 5 | 11 | ||
| 6 | // Uncomment to expand channel status information when printing the runlist | 12 | // We cannot touch PRAMIN (via page table operations or ctxsw access) if we're |
| 13 | // using it to walk the runlist | ||
| 14 | //#ifndef FALLBACK_TO_PRAMIN | ||
| 15 | // Uncomment to expand channel status, instance, and context information when | ||
| 16 | // printing the runlist | ||
| 7 | #define DETAILED_CHANNEL_INFO | 17 | #define DETAILED_CHANNEL_INFO |
| 18 | //#endif | ||
| 8 | 19 | ||
| 9 | #ifdef DETAILED_CHANNEL_INFO | 20 | #ifdef DETAILED_CHANNEL_INFO |
| 21 | // Print the channel instance and context swtich blocks | ||
| 22 | // XXX: THIS IS UNSAFE ON KEPLER! | ||
| 23 | // instance_deref() will call into the page table logic, which may move PRAMIN | ||
| 24 | // PRAMIN appears heavily utilized by the driver on Bonham (at least), and | ||
| 25 | // moving it causes problems. | ||
| 26 | static int runlist_detail_seq_show_inst(struct seq_file *s, struct nvdebug_state *g, char *prefix, uint64_t instance_ptr, enum INST_TARGET instance_target) { | ||
| 27 | instance_ctrl_t *inst = NULL; | ||
| 28 | context_switch_ctrl_t *ctxsw = NULL; | ||
| 29 | int i; | ||
| 30 | |||
| 31 | #ifdef FALLBACK_TO_PRAMIN | ||
| 32 | bar0_window_t win; | ||
| 33 | win.raw = nvdebug_readl(g, NV_XAL_EP_BAR0_WINDOW_BASE); | ||
| 34 | inst = g->regs + NV_PRAMIN + addr_to_pramin_mut(g, instance_ptr, instance_target); | ||
| 35 | #else | ||
| 36 | if (IS_ERR(inst = instance_deref(g, instance_ptr, instance_target))) | ||
| 37 | return PTR_ERR(ctxsw); | ||
| 38 | #endif // FALLBACK_TO_PRAMIN | ||
| 39 | // If unable to access instance block, skip | ||
| 40 | if (!inst) | ||
| 41 | return 0; | ||
| 42 | |||
| 43 | // Print the channel instance block | ||
| 44 | // As an ID, use upper 52 bits of the instance address (lower 12 are zero) | ||
| 45 | //seq_printf(s, "%s+- Inst %-13llx-+\n", prefix, instance_ptr >> 12); | ||
| 46 | seq_printf(s, "%s|= Instance Block ====|\n", prefix); | ||
| 47 | seq_printf(s, "%s| Target Engine: %2d|\n", prefix, inst->fc_target); | ||
| 48 | seq_printf(s, "%s| Privileged: %1d|\n", prefix, inst->fc_config_is_priv); | ||
| 49 | seq_printf(s, "%s| Channel VEID: %2d|\n", prefix, inst->fc_chan_info_veid); | ||
| 50 | seq_printf(s, "%s| WFI PTR: |\n", prefix); | ||
| 51 | seq_printf(s, "%s| %#018llx|\n", prefix, (uint64_t)inst->engine_wfi_ptr << 12); | ||
| 52 | seq_printf(s, "%s| %20s|\n", prefix, target_to_text(inst->engine_wfi_target)); | ||
| 53 | seq_printf(s, "%s| Virtual address? %d|\n", prefix, inst->engine_wfi_is_virtual); | ||
| 54 | seq_printf(s, "%s| WFI VEID: %2d|\n", prefix, inst->engine_wfi_veid); | ||
| 55 | seq_printf(s, "%s| All PDB PTR: |\n", prefix); | ||
| 56 | seq_printf(s, "%s| %#018llx|\n", prefix, (u64)inst->pdb.page_dir << 12); | ||
| 57 | seq_printf(s, "%s| %20s|\n", prefix, target_to_text(inst->pdb.target)); | ||
| 58 | seq_printf(s, "%s| %20s|\n", prefix, inst->pdb.is_volatile ? "volatile" : "non-volatile"); | ||
| 59 | // seq_printf(s, "%s|raw: %0#10lx|\n", prefix, inst->pdb.raw); | ||
| 60 | seq_printf(s, "%s| Num subcontexts: %2ld|\n", prefix, hweight64(inst->subcontext_pdb_valid)); | ||
| 61 | // Print configuration of every enabled subcontext | ||
| 62 | for (i = 0; i < 64; i++) { | ||
| 63 | // Skip subcontexts without their enable bit set | ||
| 64 | if (!(1 & (inst->subcontext_pdb_valid >> i))) | ||
| 65 | continue; | ||
| 66 | seq_printf(s, "%s| CPU SC%02d ASID%7d|\n", prefix, i, inst->subcontext[i].pasid); | ||
| 67 | seq_printf(s, "%s| SC%02d PDB PTR: |\n", prefix, i); | ||
| 68 | seq_printf(s, "%s| %#018llx|\n", prefix, ((u64)inst->subcontext[i].pdb.page_dir_hi << 32) | ((u64)inst->subcontext[i].pdb.page_dir_lo << 12)); | ||
| 69 | seq_printf(s, "%s| %20s|\n", prefix, target_to_text(inst->subcontext[i].pdb.target)); | ||
| 70 | seq_printf(s, "%s| %20s|\n", prefix, inst->subcontext[i].pdb.is_volatile ? "volatile" : "non-volatile"); | ||
| 71 | // seq_printf(s, "%s|raw: %0#10lx|\n", prefix, inst->subcontext[i].pdb.raw); | ||
| 72 | } | ||
| 73 | |||
| 74 | // XXX: CTXSW is only accessible via PRAMIN. Accessing PRAMIN appears to | ||
| 75 | // either be broken, or race with the driver on Kepler (gk104 tested). So, | ||
| 76 | // do not attempt to touch the CTXSW block on Kepler. | ||
| 77 | // TODO: This check should be moved into addr_to_pramin_mut(). | ||
| 78 | if (g->chip_id < NV_CHIP_ID_MAXWELL) | ||
| 79 | return 0; | ||
| 80 | // End XXX | ||
| 81 | |||
| 82 | if (IS_ERR(ctxsw = get_ctxsw(g, inst))) { | ||
| 83 | #ifdef FALLBACK_TO_PRAMIN | ||
| 84 | nvdebug_writel(g, NV_XAL_EP_BAR0_WINDOW_BASE, win.raw); | ||
| 85 | #endif | ||
| 86 | return PTR_ERR(ctxsw); | ||
| 87 | } | ||
| 88 | // If unable to access CTXSW block, skip | ||
| 89 | if (!ctxsw) { | ||
| 90 | #ifdef FALLBACK_TO_PRAMIN | ||
| 91 | nvdebug_writel(g, NV_XAL_EP_BAR0_WINDOW_BASE, win.raw); | ||
| 92 | #endif | ||
| 93 | return 0; | ||
| 94 | } | ||
| 95 | // Access and print the preemption mode and context ID | ||
| 96 | seq_printf(s, "%s|= Context State =====|\n", prefix); | ||
| 97 | seq_printf(s, "%s| Ctx. ID: %#10x|\n", prefix, ctxsw->context_id); | ||
| 98 | // No other CTXSW fields are supported pre-Pascal | ||
| 99 | if (g->chip_id < NV_CHIP_ID_PASCAL) | ||
| 100 | return 0; | ||
| 101 | seq_printf(s, "%s| Gfx. Preemption:%4s|\n", prefix, | ||
| 102 | graphics_preempt_type_to_text(ctxsw->graphics_preemption_options)); | ||
| 103 | seq_printf(s, "%s| Cmp. Preemption:%4s|\n", prefix, | ||
| 104 | compute_preempt_type_to_text(ctxsw->compute_preemption_options)); | ||
| 105 | seq_printf(s, "%s| #WFI Saves:%9d|\n", prefix, ctxsw->num_wfi_save_operations); | ||
| 106 | seq_printf(s, "%s| #CTA Saves:%9d|\n", prefix, ctxsw->num_cta_save_operations); | ||
| 107 | seq_printf(s, "%s| #GFXP Saves:%8d|\n", prefix, ctxsw->num_gfxp_save_operations); | ||
| 108 | seq_printf(s, "%s| #CILP Saves:%8d|\n", prefix, ctxsw->num_cilp_save_operations); | ||
| 109 | #ifdef FALLBACK_TO_PRAMIN | ||
| 110 | nvdebug_writel(g, NV_XAL_EP_BAR0_WINDOW_BASE, win.raw); | ||
| 111 | #endif | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 10 | /* Print channel details using PCCSR (Programmable Channel Control System RAM?) | 115 | /* Print channel details using PCCSR (Programmable Channel Control System RAM?) |
| 11 | @param s Pointer to state from seq_file subsystem to pass to seq_printf | 116 | @param s Pointer to state from seq_file subsystem to pass to seq_printf |
| 12 | @param g Pointer to our internal GPU state | 117 | @param g Pointer to our internal GPU state |
| @@ -32,16 +137,19 @@ static int runlist_detail_seq_show_chan(struct seq_file *s, struct nvdebug_state | |||
| 32 | seq_printf(s, "%s| %#018llx|\n", prefix, instance_ptr); | 137 | seq_printf(s, "%s| %#018llx|\n", prefix, instance_ptr); |
| 33 | seq_printf(s, "%s| %20s|\n", prefix, target_to_text(chan.inst_target)); | 138 | seq_printf(s, "%s| %20s|\n", prefix, target_to_text(chan.inst_target)); |
| 34 | seq_printf(s, "%s| Instance bound: %d|\n", prefix, chan.inst_bind); | 139 | seq_printf(s, "%s| Instance bound: %d|\n", prefix, chan.inst_bind); |
| 35 | return 0; | 140 | // Print instance block |
| 141 | return runlist_detail_seq_show_inst(s, g, prefix, instance_ptr, chan.inst_target); | ||
| 36 | } | 142 | } |
| 37 | 143 | ||
| 38 | /* `runlist_detail_seq_show_chan()`, but for Ampere+ | 144 | /* `runlist_detail_seq_show_chan()`, but for Ampere+ |
| 145 | @param instance_ptr Address for the channel instance block | ||
| 146 | @param instance_target Aperture of `instance_ptr` | ||
| 39 | @param runlist_pri_base Base of the RLRAM region for this runlist | 147 | @param runlist_pri_base Base of the RLRAM region for this runlist |
| 40 | 148 | ||
| 41 | `runlist_pri_base` is necessary, since Channel RAM is now per-runlist on | 149 | `runlist_pri_base` is necessary, since Channel RAM is now per-runlist on |
| 42 | Ampere+, and its location is configured in Runlist RAM. | 150 | Ampere+, and its location is configured in Runlist RAM. |
| 43 | */ | 151 | */ |
| 44 | static int runlist_detail_seq_show_chan_ga100(struct seq_file *s, struct nvdebug_state *g, uint32_t chid, char *prefix, uint32_t runlist_pri_base) { | 152 | static int runlist_detail_seq_show_chan_ga100(struct seq_file *s, struct nvdebug_state *g, uint32_t chid, char *prefix, uint32_t runlist_pri_base, uint64_t instance_ptr, enum INST_TARGET instance_target) { |
| 45 | runlist_channel_config_t channel_config; | 153 | runlist_channel_config_t channel_config; |
| 46 | channel_ctrl_ga100_t chan; | 154 | channel_ctrl_ga100_t chan; |
| 47 | 155 | ||
| @@ -63,7 +171,7 @@ static int runlist_detail_seq_show_chan_ga100(struct seq_file *s, struct nvdebug | |||
| 63 | seq_printf(s, "%s| PBDMA Busy: %d|\n", prefix, chan.pbdma_busy); | 171 | seq_printf(s, "%s| PBDMA Busy: %d|\n", prefix, chan.pbdma_busy); |
| 64 | seq_printf(s, "%s| ENG Busy: %d|\n", prefix, chan.eng_busy); | 172 | seq_printf(s, "%s| ENG Busy: %d|\n", prefix, chan.eng_busy); |
| 65 | seq_printf(s, "%s| Acquire Fail: %d|\n", prefix, chan.acquire_fail); | 173 | seq_printf(s, "%s| Acquire Fail: %d|\n", prefix, chan.acquire_fail); |
| 66 | return 0; | 174 | return runlist_detail_seq_show_inst(s, g, prefix, instance_ptr, instance_target); |
| 67 | } | 175 | } |
| 68 | #endif | 176 | #endif |
| 69 | 177 | ||
| @@ -173,7 +281,7 @@ static int runlist_file_seq_show(struct seq_file *s, void *raw_rl_iter) { | |||
| 173 | if (g->chip_id < NV_CHIP_ID_AMPERE) | 281 | if (g->chip_id < NV_CHIP_ID_AMPERE) |
| 174 | runlist_detail_seq_show_chan(s, g, chid(g, entry), indt); | 282 | runlist_detail_seq_show_chan(s, g, chid(g, entry), indt); |
| 175 | else | 283 | else |
| 176 | runlist_detail_seq_show_chan_ga100(s, g, chid(g, entry), indt, rl_iter->runlist_pri_base); | 284 | runlist_detail_seq_show_chan_ga100(s, g, chid(g, entry), indt, rl_iter->runlist_pri_base, instance_ptr, inst_target(g, entry)); |
| 177 | #endif | 285 | #endif |
| 178 | seq_printf(s, "%s+---------------------+\n", indt); | 286 | seq_printf(s, "%s+---------------------+\n", indt); |
| 179 | } | 287 | } |
| @@ -232,15 +340,17 @@ struct file_operations preempt_tsg_file_ops = { | |||
| 232 | 340 | ||
| 233 | ssize_t resubmit_runlist_file_write(struct file *f, const char __user *buffer, | 341 | ssize_t resubmit_runlist_file_write(struct file *f, const char __user *buffer, |
| 234 | size_t count, loff_t *off) { | 342 | size_t count, loff_t *off) { |
| 235 | uint32_t target_runlist; | 343 | uint32_t target_runlist, target_offset; |
| 236 | struct nvdebug_state *g = &g_nvdebug_state[file2gpuidx(f)]; | 344 | struct nvdebug_state *g = &g_nvdebug_state[file2gpuidx(f)]; |
| 237 | // Passing 0 as the base to kstrtou32 indicates autodetect hex/octal/dec | 345 | // Passing 0 as the base to kstrtou32 indicates autodetect hex/octal/dec |
| 238 | int err = kstrtou32_from_user(buffer, count, 0, &target_runlist); | 346 | int err = kstrtou32_from_user(buffer, count, 0, &target_offset); |
| 239 | if (err) | 347 | if (err) |
| 240 | return err; | 348 | return err; |
| 349 | // (Ab)use the PDE_DATA field for the runlist ID | ||
| 350 | target_runlist = file2gpuidx(f); | ||
| 241 | 351 | ||
| 242 | // resubmit_runlist() checks that target_runlist is valid | 352 | // resubmit_runlist() checks that target_runlist is valid |
| 243 | if ((err = resubmit_runlist(g, target_runlist))) | 353 | if ((err = resubmit_runlist(g, target_runlist, target_offset))) |
| 244 | return err; | 354 | return err; |
| 245 | 355 | ||
| 246 | return count; | 356 | return count; |
| @@ -351,6 +461,54 @@ struct file_operations enable_channel_file_ops = { | |||
| 351 | .llseek = default_llseek, | 461 | .llseek = default_llseek, |
| 352 | }; | 462 | }; |
| 353 | 463 | ||
| 464 | ssize_t comm_preempt_channel_file_write(struct file *f, const char __user *buf, | ||
| 465 | size_t count, loff_t *off, | ||
| 466 | enum COMPUTE_PREEMPT_TYPE mode) { | ||
| 467 | uint32_t target_channel, target_runlist; | ||
| 468 | struct nvdebug_state *g = &g_nvdebug_state[file2parentgpuidx(f)]; | ||
| 469 | // Passing 0 as the base to kstrtou32 indicates autodetect hex/octal/dec | ||
| 470 | int err = kstrtou32_from_user(buf, count, 0, &target_channel); | ||
| 471 | if (err) | ||
| 472 | return err; | ||
| 473 | // (Ab)use the PDE_DATA field used by file2gpuidx() for the runlist ID | ||
| 474 | target_runlist = file2gpuidx(f); | ||
| 475 | // Set preemption mode for the context of this channel | ||
