head is defined in idxd->evl as a shadow of head in the EVLSTATUS register. There are two issues related to the shadow head: 1. Mismatch between the shadow head and the state of the EVLSTATUS register: If Event Log is supported, upon completion of the Enable Device command, the Event Log head in the variable idxd->evl->head should be cleared to match the state of the EVLSTATUS register. But the variable is not reset currently, leading mismatch between the variable and the register state. The mismatch causes incorrect processing of Event Log entries. 2. Unnecessary shadow head definition: The shadow head is unnecessary as head can be read directly from the EVLSTATUS register. Reading head from the register incurs no additional cost because event log head and tail are always read together and tail is already read directly from the register as required by hardware. Remove the shadow Event Log head stored in idxd->evl to address the mentioned issues. Fixes: 244da66cda35 ("dmaengine: idxd: setup event log configuration") Signed-off-by: Fenghua Yu <fenghua.yu@intel.com> Reviewed-by: Dave Jiang <dave.jiang@intel.com> Link: https://lore.kernel.org/r/20240215024931.1739621-1-fenghua.yu@intel.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
139 lines
3.4 KiB
C
139 lines
3.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright(c) 2021 Intel Corporation. All rights rsvd. */
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/io-64-nonatomic-lo-hi.h>
|
|
#include <uapi/linux/idxd.h>
|
|
#include "idxd.h"
|
|
#include "registers.h"
|
|
|
|
static struct dentry *idxd_debugfs_dir;
|
|
|
|
static void dump_event_entry(struct idxd_device *idxd, struct seq_file *s,
|
|
u16 index, int *count, bool processed)
|
|
{
|
|
struct idxd_evl *evl = idxd->evl;
|
|
struct dsa_evl_entry *entry;
|
|
struct dsa_completion_record *cr;
|
|
u64 *raw;
|
|
int i;
|
|
int evl_strides = evl_ent_size(idxd) / sizeof(u64);
|
|
|
|
entry = (struct dsa_evl_entry *)evl->log + index;
|
|
|
|
if (!entry->e.desc_valid)
|
|
return;
|
|
|
|
seq_printf(s, "Event Log entry %d (real index %u) processed: %u\n",
|
|
*count, index, processed);
|
|
|
|
seq_printf(s, "desc valid %u wq idx valid %u\n"
|
|
"batch %u fault rw %u priv %u error 0x%x\n"
|
|
"wq idx %u op %#x pasid %u batch idx %u\n"
|
|
"fault addr %#llx\n",
|
|
entry->e.desc_valid, entry->e.wq_idx_valid,
|
|
entry->e.batch, entry->e.fault_rw, entry->e.priv,
|
|
entry->e.error, entry->e.wq_idx, entry->e.operation,
|
|
entry->e.pasid, entry->e.batch_idx, entry->e.fault_addr);
|
|
|
|
cr = &entry->cr;
|
|
seq_printf(s, "status %#x result %#x fault_info %#x bytes_completed %u\n"
|
|
"fault addr %#llx inv flags %#x\n\n",
|
|
cr->status, cr->result, cr->fault_info, cr->bytes_completed,
|
|
cr->fault_addr, cr->invalid_flags);
|
|
|
|
raw = (u64 *)entry;
|
|
|
|
for (i = 0; i < evl_strides; i++)
|
|
seq_printf(s, "entry[%d] = %#llx\n", i, raw[i]);
|
|
|
|
seq_puts(s, "\n");
|
|
*count += 1;
|
|
}
|
|
|
|
static int debugfs_evl_show(struct seq_file *s, void *d)
|
|
{
|
|
struct idxd_device *idxd = s->private;
|
|
struct idxd_evl *evl = idxd->evl;
|
|
union evl_status_reg evl_status;
|
|
u16 h, t, evl_size, i;
|
|
int count = 0;
|
|
bool processed = true;
|
|
|
|
if (!evl || !evl->log)
|
|
return 0;
|
|
|
|
spin_lock(&evl->lock);
|
|
|
|
evl_status.bits = ioread64(idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
|
|
t = evl_status.tail;
|
|
h = evl_status.head;
|
|
evl_size = evl->size;
|
|
|
|
seq_printf(s, "Event Log head %u tail %u interrupt pending %u\n\n",
|
|
evl_status.head, evl_status.tail, evl_status.int_pending);
|
|
|
|
i = t;
|
|
while (1) {
|
|
i = (i + 1) % evl_size;
|
|
if (i == t)
|
|
break;
|
|
|
|
if (processed && i == h)
|
|
processed = false;
|
|
dump_event_entry(idxd, s, i, &count, processed);
|
|
}
|
|
|
|
spin_unlock(&evl->lock);
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_SHOW_ATTRIBUTE(debugfs_evl);
|
|
|
|
int idxd_device_init_debugfs(struct idxd_device *idxd)
|
|
{
|
|
if (IS_ERR_OR_NULL(idxd_debugfs_dir))
|
|
return 0;
|
|
|
|
idxd->dbgfs_dir = debugfs_create_dir(dev_name(idxd_confdev(idxd)), idxd_debugfs_dir);
|
|
if (IS_ERR(idxd->dbgfs_dir))
|
|
return PTR_ERR(idxd->dbgfs_dir);
|
|
|
|
if (idxd->evl) {
|
|
idxd->dbgfs_evl_file = debugfs_create_file("event_log", 0400,
|
|
idxd->dbgfs_dir, idxd,
|
|
&debugfs_evl_fops);
|
|
if (IS_ERR(idxd->dbgfs_evl_file)) {
|
|
debugfs_remove_recursive(idxd->dbgfs_dir);
|
|
idxd->dbgfs_dir = NULL;
|
|
return PTR_ERR(idxd->dbgfs_evl_file);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void idxd_device_remove_debugfs(struct idxd_device *idxd)
|
|
{
|
|
debugfs_remove_recursive(idxd->dbgfs_dir);
|
|
}
|
|
|
|
int idxd_init_debugfs(void)
|
|
{
|
|
if (!debugfs_initialized())
|
|
return 0;
|
|
|
|
idxd_debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
|
|
if (IS_ERR(idxd_debugfs_dir))
|
|
return PTR_ERR(idxd_debugfs_dir);
|
|
return 0;
|
|
}
|
|
|
|
void idxd_remove_debugfs(void)
|
|
{
|
|
debugfs_remove_recursive(idxd_debugfs_dir);
|
|
}
|