| .. | .. |
|---|
| 31 | 31 | |
|---|
| 32 | 32 | #if IS_ENABLED(CONFIG_DEBUG_FS) |
|---|
| 33 | 33 | |
|---|
| 34 | +#define SHOW_GPU_MEM_DATA(type, format) \ |
|---|
| 35 | +{ \ |
|---|
| 36 | + unsigned int i, j; \ |
|---|
| 37 | + const type *ptr = (type *)cpu_addr; \ |
|---|
| 38 | + const unsigned int col_width = sizeof(type); \ |
|---|
| 39 | + const unsigned int row_width = (col_width == sizeof(u64)) ? 32 : 16; \ |
|---|
| 40 | + const unsigned int num_cols = row_width / col_width; \ |
|---|
| 41 | + for (i = 0; i < PAGE_SIZE; i += row_width) { \ |
|---|
| 42 | + seq_printf(m, "%016llx:", gpu_addr + i); \ |
|---|
| 43 | + for (j = 0; j < num_cols; j++) \ |
|---|
| 44 | + seq_printf(m, format, ptr[j]); \ |
|---|
| 45 | + ptr += num_cols; \ |
|---|
| 46 | + seq_putc(m, '\n'); \ |
|---|
| 47 | + } \ |
|---|
| 48 | +} |
|---|
| 49 | + |
|---|
| 34 | 50 | struct debug_mem_mapping { |
|---|
| 35 | 51 | struct list_head node; |
|---|
| 36 | 52 | |
|---|
| .. | .. |
|---|
| 44 | 60 | struct debug_mem_data { |
|---|
| 45 | 61 | struct list_head mapping_list; |
|---|
| 46 | 62 | struct kbase_context *kctx; |
|---|
| 63 | + unsigned int column_width; |
|---|
| 47 | 64 | }; |
|---|
| 48 | 65 | |
|---|
| 49 | 66 | struct debug_mem_seq_off { |
|---|
| .. | .. |
|---|
| 111 | 128 | struct debug_mem_data *mem_data = m->private; |
|---|
| 112 | 129 | struct debug_mem_seq_off *data = v; |
|---|
| 113 | 130 | struct debug_mem_mapping *map; |
|---|
| 114 | | - int i, j; |
|---|
| 131 | + unsigned long long gpu_addr; |
|---|
| 115 | 132 | struct page *page; |
|---|
| 116 | | - uint32_t *mapping; |
|---|
| 133 | + void *cpu_addr; |
|---|
| 117 | 134 | pgprot_t prot = PAGE_KERNEL; |
|---|
| 118 | 135 | |
|---|
| 119 | 136 | map = list_entry(data->lh, struct debug_mem_mapping, node); |
|---|
| .. | .. |
|---|
| 130 | 147 | prot = pgprot_writecombine(prot); |
|---|
| 131 | 148 | |
|---|
| 132 | 149 | page = as_page(map->alloc->pages[data->offset]); |
|---|
| 133 | | - mapping = vmap(&page, 1, VM_MAP, prot); |
|---|
| 134 | | - if (!mapping) |
|---|
| 150 | + cpu_addr = vmap(&page, 1, VM_MAP, prot); |
|---|
| 151 | + if (!cpu_addr) |
|---|
| 135 | 152 | goto out; |
|---|
| 136 | 153 | |
|---|
| 137 | | - for (i = 0; i < PAGE_SIZE; i += 4*sizeof(*mapping)) { |
|---|
| 138 | | - seq_printf(m, "%016llx:", i + ((map->start_pfn + |
|---|
| 139 | | - data->offset) << PAGE_SHIFT)); |
|---|
| 154 | + gpu_addr = (map->start_pfn + data->offset) << PAGE_SHIFT; |
|---|
| 140 | 155 | |
|---|
| 141 | | - for (j = 0; j < 4*sizeof(*mapping); j += sizeof(*mapping)) |
|---|
| 142 | | - seq_printf(m, " %08x", mapping[(i+j)/sizeof(*mapping)]); |
|---|
| 143 | | - seq_putc(m, '\n'); |
|---|
| 156 | + /* Cases for 4 supported values of column_width for showing |
|---|
| 157 | + * the GPU memory contents. |
|---|
| 158 | + */ |
|---|
| 159 | + switch (mem_data->column_width) { |
|---|
| 160 | + case 1: |
|---|
| 161 | + SHOW_GPU_MEM_DATA(u8, " %02hhx"); |
|---|
| 162 | + break; |
|---|
| 163 | + case 2: |
|---|
| 164 | + SHOW_GPU_MEM_DATA(u16, " %04hx"); |
|---|
| 165 | + break; |
|---|
| 166 | + case 4: |
|---|
| 167 | + SHOW_GPU_MEM_DATA(u32, " %08x"); |
|---|
| 168 | + break; |
|---|
| 169 | + case 8: |
|---|
| 170 | + SHOW_GPU_MEM_DATA(u64, " %016llx"); |
|---|
| 171 | + break; |
|---|
| 172 | + default: |
|---|
| 173 | + dev_warn(mem_data->kctx->kbdev->dev, "Unexpected column width"); |
|---|
| 144 | 174 | } |
|---|
| 145 | 175 | |
|---|
| 146 | | - vunmap(mapping); |
|---|
| 176 | + vunmap(cpu_addr); |
|---|
| 147 | 177 | |
|---|
| 148 | 178 | seq_putc(m, '\n'); |
|---|
| 149 | 179 | |
|---|
| .. | .. |
|---|
| 207 | 237 | if (get_file_rcu(kctx->filp) == 0) |
|---|
| 208 | 238 | return -ENOENT; |
|---|
| 209 | 239 | |
|---|
| 240 | + /* Check if file was opened in write mode. GPU memory contents |
|---|
| 241 | + * are returned only when the file is not opened in write mode. |
|---|
| 242 | + */ |
|---|
| 243 | + if (file->f_mode & FMODE_WRITE) { |
|---|
| 244 | + file->private_data = kctx; |
|---|
| 245 | + return 0; |
|---|
| 246 | + } |
|---|
| 247 | + |
|---|
| 210 | 248 | ret = seq_open(file, &ops); |
|---|
| 211 | 249 | if (ret) |
|---|
| 212 | 250 | goto open_fail; |
|---|
| .. | .. |
|---|
| 222 | 260 | INIT_LIST_HEAD(&mem_data->mapping_list); |
|---|
| 223 | 261 | |
|---|
| 224 | 262 | kbase_gpu_vm_lock(kctx); |
|---|
| 263 | + |
|---|
| 264 | + mem_data->column_width = kctx->mem_view_column_width; |
|---|
| 225 | 265 | |
|---|
| 226 | 266 | ret = debug_mem_zone_open(&kctx->reg_rbtree_same, mem_data); |
|---|
| 227 | 267 | if (ret != 0) { |
|---|
| .. | .. |
|---|
| 240 | 280 | kbase_gpu_vm_unlock(kctx); |
|---|
| 241 | 281 | goto out; |
|---|
| 242 | 282 | } |
|---|
| 283 | + |
|---|
| 284 | +#if MALI_USE_CSF |
|---|
| 285 | + ret = debug_mem_zone_open(&kctx->reg_rbtree_exec_fixed, mem_data); |
|---|
| 286 | + if (ret != 0) { |
|---|
| 287 | + kbase_gpu_vm_unlock(kctx); |
|---|
| 288 | + goto out; |
|---|
| 289 | + } |
|---|
| 290 | + |
|---|
| 291 | + ret = debug_mem_zone_open(&kctx->reg_rbtree_fixed, mem_data); |
|---|
| 292 | + if (ret != 0) { |
|---|
| 293 | + kbase_gpu_vm_unlock(kctx); |
|---|
| 294 | + goto out; |
|---|
| 295 | + } |
|---|
| 296 | +#endif |
|---|
| 243 | 297 | |
|---|
| 244 | 298 | kbase_gpu_vm_unlock(kctx); |
|---|
| 245 | 299 | |
|---|
| .. | .. |
|---|
| 270 | 324 | static int debug_mem_release(struct inode *inode, struct file *file) |
|---|
| 271 | 325 | { |
|---|
| 272 | 326 | struct kbase_context *const kctx = inode->i_private; |
|---|
| 273 | | - struct seq_file *sfile = file->private_data; |
|---|
| 274 | | - struct debug_mem_data *mem_data = sfile->private; |
|---|
| 275 | | - struct debug_mem_mapping *mapping; |
|---|
| 276 | 327 | |
|---|
| 277 | | - seq_release(inode, file); |
|---|
| 328 | + /* If the file wasn't opened in write mode, then release the |
|---|
| 329 | + * memory allocated to show the GPU memory contents. |
|---|
| 330 | + */ |
|---|
| 331 | + if (!(file->f_mode & FMODE_WRITE)) { |
|---|
| 332 | + struct seq_file *sfile = file->private_data; |
|---|
| 333 | + struct debug_mem_data *mem_data = sfile->private; |
|---|
| 334 | + struct debug_mem_mapping *mapping; |
|---|
| 278 | 335 | |
|---|
| 279 | | - while (!list_empty(&mem_data->mapping_list)) { |
|---|
| 280 | | - mapping = list_first_entry(&mem_data->mapping_list, |
|---|
| 336 | + seq_release(inode, file); |
|---|
| 337 | + |
|---|
| 338 | + while (!list_empty(&mem_data->mapping_list)) { |
|---|
| 339 | + mapping = list_first_entry(&mem_data->mapping_list, |
|---|
| 281 | 340 | struct debug_mem_mapping, node); |
|---|
| 282 | | - kbase_mem_phy_alloc_put(mapping->alloc); |
|---|
| 283 | | - list_del(&mapping->node); |
|---|
| 284 | | - kfree(mapping); |
|---|
| 285 | | - } |
|---|
| 341 | + kbase_mem_phy_alloc_put(mapping->alloc); |
|---|
| 342 | + list_del(&mapping->node); |
|---|
| 343 | + kfree(mapping); |
|---|
| 344 | + } |
|---|
| 286 | 345 | |
|---|
| 287 | | - kfree(mem_data); |
|---|
| 346 | + kfree(mem_data); |
|---|
| 347 | + } |
|---|
| 288 | 348 | |
|---|
| 289 | 349 | fput(kctx->filp); |
|---|
| 290 | 350 | |
|---|
| 291 | 351 | return 0; |
|---|
| 352 | +} |
|---|
| 353 | + |
|---|
| 354 | +static ssize_t debug_mem_write(struct file *file, const char __user *ubuf, |
|---|
| 355 | + size_t count, loff_t *ppos) |
|---|
| 356 | +{ |
|---|
| 357 | + struct kbase_context *const kctx = file->private_data; |
|---|
| 358 | + unsigned int column_width = 0; |
|---|
| 359 | + int ret = 0; |
|---|
| 360 | + |
|---|
| 361 | + CSTD_UNUSED(ppos); |
|---|
| 362 | + |
|---|
| 363 | + ret = kstrtouint_from_user(ubuf, count, 0, &column_width); |
|---|
| 364 | + |
|---|
| 365 | + if (ret) |
|---|
| 366 | + return ret; |
|---|
| 367 | + if (!is_power_of_2(column_width)) { |
|---|
| 368 | + dev_dbg(kctx->kbdev->dev, |
|---|
| 369 | + "Column width %u not a multiple of power of 2", column_width); |
|---|
| 370 | + return -EINVAL; |
|---|
| 371 | + } |
|---|
| 372 | + if (column_width > 8) { |
|---|
| 373 | + dev_dbg(kctx->kbdev->dev, |
|---|
| 374 | + "Column width %u greater than 8 not supported", column_width); |
|---|
| 375 | + return -EINVAL; |
|---|
| 376 | + } |
|---|
| 377 | + |
|---|
| 378 | + kbase_gpu_vm_lock(kctx); |
|---|
| 379 | + kctx->mem_view_column_width = column_width; |
|---|
| 380 | + kbase_gpu_vm_unlock(kctx); |
|---|
| 381 | + |
|---|
| 382 | + return count; |
|---|
| 292 | 383 | } |
|---|
| 293 | 384 | |
|---|
| 294 | 385 | static const struct file_operations kbase_debug_mem_view_fops = { |
|---|
| .. | .. |
|---|
| 296 | 387 | .open = debug_mem_open, |
|---|
| 297 | 388 | .release = debug_mem_release, |
|---|
| 298 | 389 | .read = seq_read, |
|---|
| 390 | + .write = debug_mem_write, |
|---|
| 299 | 391 | .llseek = seq_lseek |
|---|
| 300 | 392 | }; |
|---|
| 301 | 393 | |
|---|
| .. | .. |
|---|
| 308 | 400 | WARN_ON(IS_ERR_OR_NULL(kctx->kctx_dentry))) |
|---|
| 309 | 401 | return; |
|---|
| 310 | 402 | |
|---|
| 403 | + /* Default column width is 4 */ |
|---|
| 404 | + kctx->mem_view_column_width = sizeof(u32); |
|---|
| 405 | + |
|---|
| 311 | 406 | debugfs_create_file("mem_view", 0400, kctx->kctx_dentry, kctx, |
|---|
| 312 | 407 | &kbase_debug_mem_view_fops); |
|---|
| 313 | 408 | } |
|---|