linux-stable/drivers/net/ethernet/amd/pds_core/core.h

319 lines
8.9 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2023 Advanced Micro Devices, Inc */
#ifndef _PDSC_H_
#define _PDSC_H_
#include <linux/debugfs.h>
#include <net/devlink.h>
#include <linux/pds/pds_common.h>
#include <linux/pds/pds_core_if.h>
#include <linux/pds/pds_adminq.h>
#include <linux/pds/pds_intr.h>
#define PDSC_DRV_DESCRIPTION "AMD/Pensando Core Driver"
#define PDSC_WATCHDOG_SECS 5
#define PDSC_QUEUE_NAME_MAX_SZ 16
#define PDSC_ADMINQ_MIN_LENGTH 16 /* must be a power of two */
#define PDSC_NOTIFYQ_LENGTH 64 /* must be a power of two */
#define PDSC_TEARDOWN_RECOVERY false
#define PDSC_TEARDOWN_REMOVING true
#define PDSC_SETUP_RECOVERY false
#define PDSC_SETUP_INIT true
struct pdsc_dev_bar {
void __iomem *vaddr;
phys_addr_t bus_addr;
unsigned long len;
int res_index;
};
struct pdsc;
struct pdsc_vf {
struct pds_auxiliary_dev *padev;
struct pdsc *vf;
u16 index;
__le16 vif_types[PDS_DEV_TYPE_MAX];
};
struct pdsc_devinfo {
u8 asic_type;
u8 asic_rev;
char fw_version[PDS_CORE_DEVINFO_FWVERS_BUFLEN + 1];
char serial_num[PDS_CORE_DEVINFO_SERIAL_BUFLEN + 1];
};
struct pdsc_queue {
struct pdsc_q_info *info;
u64 dbval;
u16 head_idx;
u16 tail_idx;
u8 hw_type;
unsigned int index;
unsigned int num_descs;
u64 dbell_count;
u64 features;
unsigned int type;
unsigned int hw_index;
union {
void *base;
struct pds_core_admin_cmd *adminq;
};
dma_addr_t base_pa; /* must be page aligned */
unsigned int desc_size;
unsigned int pid;
char name[PDSC_QUEUE_NAME_MAX_SZ];
};
#define PDSC_INTR_NAME_MAX_SZ 32
struct pdsc_intr_info {
char name[PDSC_INTR_NAME_MAX_SZ];
unsigned int index;
unsigned int vector;
void *data;
};
struct pdsc_cq_info {
void *comp;
};
struct pdsc_buf_info {
struct page *page;
dma_addr_t dma_addr;
u32 page_offset;
u32 len;
};
struct pdsc_q_info {
union {
void *desc;
struct pdsc_admin_cmd *adminq_desc;
};
unsigned int bytes;
unsigned int nbufs;
struct pdsc_buf_info bufs[PDS_CORE_MAX_FRAGS];
struct pdsc_wait_context *wc;
void *dest;
};
struct pdsc_cq {
struct pdsc_cq_info *info;
struct pdsc_queue *bound_q;
struct pdsc_intr_info *bound_intr;
u16 tail_idx;
bool done_color;
unsigned int num_descs;
unsigned int desc_size;
void *base;
dma_addr_t base_pa; /* must be page aligned */
} ____cacheline_aligned_in_smp;
struct pdsc_qcq {
struct pdsc *pdsc;
void *q_base;
dma_addr_t q_base_pa; /* might not be page aligned */
void *cq_base;
dma_addr_t cq_base_pa; /* might not be page aligned */
u32 q_size;
u32 cq_size;
bool armed;
unsigned int flags;
struct work_struct work;
struct pdsc_queue q;
struct pdsc_cq cq;
int intx;
u32 accum_work;
struct dentry *dentry;
};
struct pdsc_viftype {
char *name;
bool supported;
bool enabled;
int dl_id;
int vif_id;
struct pds_auxiliary_dev *padev;
};
/* No state flags set means we are in a steady running state */
enum pdsc_state_flags {
PDSC_S_FW_DEAD, /* stopped, wait on startup or recovery */
PDSC_S_INITING_DRIVER, /* initial startup from probe */
PDSC_S_STOPPING_DRIVER, /* driver remove */
/* leave this as last */
PDSC_S_STATE_SIZE
};
struct pdsc {
struct pci_dev *pdev;
struct dentry *dentry;
struct device *dev;
struct pdsc_dev_bar bars[PDS_CORE_BARS_MAX];
struct pdsc_vf *vfs;
int num_vfs;
int vf_id;
int hw_index;
int uid;
unsigned long state;
u8 fw_status;
u8 fw_generation;
unsigned long last_fw_time;
u32 last_hb;
struct timer_list wdtimer;
unsigned int wdtimer_period;
struct work_struct health_work;
struct devlink_health_reporter *fw_reporter;
u32 fw_recoveries;
struct pdsc_devinfo dev_info;
struct pds_core_dev_identity dev_ident;
unsigned int nintrs;
struct pdsc_intr_info *intr_info; /* array of nintrs elements */
struct workqueue_struct *wq;
unsigned int devcmd_timeout;
struct mutex devcmd_lock; /* lock for dev_cmd operations */
struct mutex config_lock; /* lock for configuration operations */
spinlock_t adminq_lock; /* lock for adminq operations */
pds_core: Prevent race issues involving the adminq There are multiple paths that can result in using the pdsc's adminq. [1] pdsc_adminq_isr and the resulting work from queue_work(), i.e. pdsc_work_thread()->pdsc_process_adminq() [2] pdsc_adminq_post() When the device goes through reset via PCIe reset and/or a fw_down/fw_up cycle due to bad PCIe state or bad device state the adminq is destroyed and recreated. A NULL pointer dereference can happen if [1] or [2] happens after the adminq is already destroyed. In order to fix this, add some further state checks and implement reference counting for adminq uses. Reference counting was used because multiple threads can attempt to access the adminq at the same time via [1] or [2]. Additionally, multiple clients (i.e. pds-vfio-pci) can be using [2] at the same time. The adminq_refcnt is initialized to 1 when the adminq has been allocated and is ready to use. Users/clients of the adminq (i.e. [1] and [2]) will increment the refcnt when they are using the adminq. When the driver goes into a fw_down cycle it will set the PDSC_S_FW_DEAD bit and then wait for the adminq_refcnt to hit 1. Setting the PDSC_S_FW_DEAD before waiting will prevent any further adminq_refcnt increments. Waiting for the adminq_refcnt to hit 1 allows for any current users of the adminq to finish before the driver frees the adminq. Once the adminq_refcnt hits 1 the driver clears the refcnt to signify that the adminq is deleted and cannot be used. On the fw_up cycle the driver will once again initialize the adminq_refcnt to 1 allowing the adminq to be used again. Fixes: 01ba61b55b20 ("pds_core: Add adminq processing and commands") Signed-off-by: Brett Creeley <brett.creeley@amd.com> Reviewed-by: Shannon Nelson <shannon.nelson@amd.com> Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com> Link: https://lore.kernel.org/r/20240129234035.69802-5-brett.creeley@amd.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-01-29 23:40:33 +00:00
refcount_t adminq_refcnt;
struct pds_core_dev_info_regs __iomem *info_regs;
struct pds_core_dev_cmd_regs __iomem *cmd_regs;
struct pds_core_intr __iomem *intr_ctrl;
u64 __iomem *intr_status;
u64 __iomem *db_pages;
dma_addr_t phy_db_pages;
u64 __iomem *kern_dbpage;
struct pdsc_qcq adminqcq;
struct pdsc_qcq notifyqcq;
u64 last_eid;
struct pdsc_viftype *viftype_status;
pds_core: Fix pdsc_check_pci_health function to use work thread [ Upstream commit 81665adf25d28a00a986533f1d3a5df76b79cad9 ] When the driver notices fw_status == 0xff it tries to perform a PCI reset on itself via pci_reset_function() in the context of the driver's health thread. However, pdsc_reset_prepare calls pdsc_stop_health_thread(), which attempts to stop/flush the health thread. This results in a deadlock because the stop/flush will never complete since the driver called pci_reset_function() from the health thread context. Fix by changing the pdsc_check_pci_health_function() to queue a newly introduced pdsc_pci_reset_thread() on the pdsc's work queue. Unloading the driver in the fw_down/dead state uncovered another issue, which can be seen in the following trace: WARNING: CPU: 51 PID: 6914 at kernel/workqueue.c:1450 __queue_work+0x358/0x440 [...] RIP: 0010:__queue_work+0x358/0x440 [...] Call Trace: <TASK> ? __warn+0x85/0x140 ? __queue_work+0x358/0x440 ? report_bug+0xfc/0x1e0 ? handle_bug+0x3f/0x70 ? exc_invalid_op+0x17/0x70 ? asm_exc_invalid_op+0x1a/0x20 ? __queue_work+0x358/0x440 queue_work_on+0x28/0x30 pdsc_devcmd_locked+0x96/0xe0 [pds_core] pdsc_devcmd_reset+0x71/0xb0 [pds_core] pdsc_teardown+0x51/0xe0 [pds_core] pdsc_remove+0x106/0x200 [pds_core] pci_device_remove+0x37/0xc0 device_release_driver_internal+0xae/0x140 driver_detach+0x48/0x90 bus_remove_driver+0x6d/0xf0 pci_unregister_driver+0x2e/0xa0 pdsc_cleanup_module+0x10/0x780 [pds_core] __x64_sys_delete_module+0x142/0x2b0 ? syscall_trace_enter.isra.18+0x126/0x1a0 do_syscall_64+0x3b/0x90 entry_SYSCALL_64_after_hwframe+0x72/0xdc RIP: 0033:0x7fbd9d03a14b [...] Fix this by preventing the devcmd reset if the FW is not running. Fixes: d9407ff11809 ("pds_core: Prevent health thread from running during reset/remove") Reviewed-by: Shannon Nelson <shannon.nelson@amd.com> Signed-off-by: Brett Creeley <brett.creeley@amd.com> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Sasha Levin <sashal@kernel.org>
2024-04-08 16:35:40 +00:00
struct work_struct pci_reset_work;
};
/** enum pds_core_dbell_bits - bitwise composition of dbell values.
*
* @PDS_CORE_DBELL_QID_MASK: unshifted mask of valid queue id bits.
* @PDS_CORE_DBELL_QID_SHIFT: queue id shift amount in dbell value.
* @PDS_CORE_DBELL_QID: macro to build QID component of dbell value.
*
* @PDS_CORE_DBELL_RING_MASK: unshifted mask of valid ring bits.
* @PDS_CORE_DBELL_RING_SHIFT: ring shift amount in dbell value.
* @PDS_CORE_DBELL_RING: macro to build ring component of dbell value.
*
* @PDS_CORE_DBELL_RING_0: ring zero dbell component value.
* @PDS_CORE_DBELL_RING_1: ring one dbell component value.
* @PDS_CORE_DBELL_RING_2: ring two dbell component value.
* @PDS_CORE_DBELL_RING_3: ring three dbell component value.
*
* @PDS_CORE_DBELL_INDEX_MASK: bit mask of valid index bits, no shift needed.
*/
enum pds_core_dbell_bits {
PDS_CORE_DBELL_QID_MASK = 0xffffff,
PDS_CORE_DBELL_QID_SHIFT = 24,
#define PDS_CORE_DBELL_QID(n) \
(((u64)(n) & PDS_CORE_DBELL_QID_MASK) << PDS_CORE_DBELL_QID_SHIFT)
PDS_CORE_DBELL_RING_MASK = 0x7,
PDS_CORE_DBELL_RING_SHIFT = 16,
#define PDS_CORE_DBELL_RING(n) \
(((u64)(n) & PDS_CORE_DBELL_RING_MASK) << PDS_CORE_DBELL_RING_SHIFT)
PDS_CORE_DBELL_RING_0 = 0,
PDS_CORE_DBELL_RING_1 = PDS_CORE_DBELL_RING(1),
PDS_CORE_DBELL_RING_2 = PDS_CORE_DBELL_RING(2),
PDS_CORE_DBELL_RING_3 = PDS_CORE_DBELL_RING(3),
PDS_CORE_DBELL_INDEX_MASK = 0xffff,
};
static inline void pds_core_dbell_ring(u64 __iomem *db_page,
enum pds_core_logical_qtype qtype,
u64 val)
{
writeq(val, &db_page[qtype]);
}
int pdsc_fw_reporter_diagnose(struct devlink_health_reporter *reporter,
struct devlink_fmsg *fmsg,
struct netlink_ext_ack *extack);
int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
struct netlink_ext_ack *extack);
int pdsc_dl_flash_update(struct devlink *dl,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack);
int pdsc_dl_enable_get(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx);
int pdsc_dl_enable_set(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx);
int pdsc_dl_enable_validate(struct devlink *dl, u32 id,
union devlink_param_value val,
struct netlink_ext_ack *extack);
void __iomem *pdsc_map_dbpage(struct pdsc *pdsc, int page_num);
void pdsc_debugfs_create(void);
void pdsc_debugfs_destroy(void);
void pdsc_debugfs_add_dev(struct pdsc *pdsc);
void pdsc_debugfs_del_dev(struct pdsc *pdsc);
void pdsc_debugfs_add_ident(struct pdsc *pdsc);
void pdsc_debugfs_add_viftype(struct pdsc *pdsc);
void pdsc_debugfs_add_irqs(struct pdsc *pdsc);
void pdsc_debugfs_add_qcq(struct pdsc *pdsc, struct pdsc_qcq *qcq);
void pdsc_debugfs_del_qcq(struct pdsc_qcq *qcq);
int pdsc_err_to_errno(enum pds_core_status_code code);
bool pdsc_is_fw_running(struct pdsc *pdsc);
bool pdsc_is_fw_good(struct pdsc *pdsc);
int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds);
int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds);
int pdsc_devcmd_init(struct pdsc *pdsc);
int pdsc_devcmd_reset(struct pdsc *pdsc);
int pdsc_dev_init(struct pdsc *pdsc);
int pdsc_intr_alloc(struct pdsc *pdsc, char *name,
irq_handler_t handler, void *data);
void pdsc_intr_free(struct pdsc *pdsc, int index);
void pdsc_qcq_free(struct pdsc *pdsc, struct pdsc_qcq *qcq);
int pdsc_qcq_alloc(struct pdsc *pdsc, unsigned int type, unsigned int index,
const char *name, unsigned int flags, unsigned int num_descs,
unsigned int desc_size, unsigned int cq_desc_size,
unsigned int pid, struct pdsc_qcq *qcq);
int pdsc_setup(struct pdsc *pdsc, bool init);
void pdsc_teardown(struct pdsc *pdsc, bool removing);
int pdsc_start(struct pdsc *pdsc);
void pdsc_stop(struct pdsc *pdsc);
void pdsc_health_thread(struct work_struct *work);
int pdsc_register_notify(struct notifier_block *nb);
void pdsc_unregister_notify(struct notifier_block *nb);
void pdsc_notify(unsigned long event, void *data);
int pdsc_auxbus_dev_add(struct pdsc *cf, struct pdsc *pf);
int pdsc_auxbus_dev_del(struct pdsc *cf, struct pdsc *pf);
void pdsc_process_adminq(struct pdsc_qcq *qcq);
void pdsc_work_thread(struct work_struct *work);
irqreturn_t pdsc_adminq_isr(int irq, void *data);
int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
struct netlink_ext_ack *extack);
void pdsc_fw_down(struct pdsc *pdsc);
void pdsc_fw_up(struct pdsc *pdsc);
pds_core: Fix pdsc_check_pci_health function to use work thread [ Upstream commit 81665adf25d28a00a986533f1d3a5df76b79cad9 ] When the driver notices fw_status == 0xff it tries to perform a PCI reset on itself via pci_reset_function() in the context of the driver's health thread. However, pdsc_reset_prepare calls pdsc_stop_health_thread(), which attempts to stop/flush the health thread. This results in a deadlock because the stop/flush will never complete since the driver called pci_reset_function() from the health thread context. Fix by changing the pdsc_check_pci_health_function() to queue a newly introduced pdsc_pci_reset_thread() on the pdsc's work queue. Unloading the driver in the fw_down/dead state uncovered another issue, which can be seen in the following trace: WARNING: CPU: 51 PID: 6914 at kernel/workqueue.c:1450 __queue_work+0x358/0x440 [...] RIP: 0010:__queue_work+0x358/0x440 [...] Call Trace: <TASK> ? __warn+0x85/0x140 ? __queue_work+0x358/0x440 ? report_bug+0xfc/0x1e0 ? handle_bug+0x3f/0x70 ? exc_invalid_op+0x17/0x70 ? asm_exc_invalid_op+0x1a/0x20 ? __queue_work+0x358/0x440 queue_work_on+0x28/0x30 pdsc_devcmd_locked+0x96/0xe0 [pds_core] pdsc_devcmd_reset+0x71/0xb0 [pds_core] pdsc_teardown+0x51/0xe0 [pds_core] pdsc_remove+0x106/0x200 [pds_core] pci_device_remove+0x37/0xc0 device_release_driver_internal+0xae/0x140 driver_detach+0x48/0x90 bus_remove_driver+0x6d/0xf0 pci_unregister_driver+0x2e/0xa0 pdsc_cleanup_module+0x10/0x780 [pds_core] __x64_sys_delete_module+0x142/0x2b0 ? syscall_trace_enter.isra.18+0x126/0x1a0 do_syscall_64+0x3b/0x90 entry_SYSCALL_64_after_hwframe+0x72/0xdc RIP: 0033:0x7fbd9d03a14b [...] Fix this by preventing the devcmd reset if the FW is not running. Fixes: d9407ff11809 ("pds_core: Prevent health thread from running during reset/remove") Reviewed-by: Shannon Nelson <shannon.nelson@amd.com> Signed-off-by: Brett Creeley <brett.creeley@amd.com> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Sasha Levin <sashal@kernel.org>
2024-04-08 16:35:40 +00:00
void pdsc_pci_reset_thread(struct work_struct *work);
#endif /* _PDSC_H_ */