// SPDX-License-Identifier: GPL-2.0/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ #include <linux/printk.h>#include <linux/dynamic_debug.h>#include <linux/module.h>#include <linux/netdevice.h>#include <linux/utsname.h>#include <generated/utsrelease.h>#include <linux/ctype.h> #include "ionic.h"#include "ionic_bus.h"#include "ionic_lif.h"#include "ionic_debugfs.h" MODULE_DESCRIPTION(IONIC_DRV_DESCRIPTION);MODULE_AUTHOR("Shannon Nelson <shannon.nelson@amd.com>");MODULE_LICENSE("GPL"); static const char *ionic_error_to_str(enum ionic_status_code code){ switch (code) { case IONIC_RC_SUCCESS: return "IONIC_RC_SUCCESS"; case IONIC_RC_EVERSION: return "IONIC_RC_EVERSION"; case IONIC_RC_EOPCODE: return "IONIC_RC_EOPCODE"; case IONIC_RC_EIO: return "IONIC_RC_EIO"; case IONIC_RC_EPERM: return "IONIC_RC_EPERM"; case IONIC_RC_EQID: return "IONIC_RC_EQID"; case IONIC_RC_EQTYPE: return "IONIC_RC_EQTYPE"; case IONIC_RC_ENOENT: return "IONIC_RC_ENOENT"; case IONIC_RC_EINTR: return "IONIC_RC_EINTR"; case IONIC_RC_EAGAIN: return "IONIC_RC_EAGAIN"; case IONIC_RC_ENOMEM: return "IONIC_RC_ENOMEM"; case IONIC_RC_EFAULT: return "IONIC_RC_EFAULT"; case IONIC_RC_EBUSY: return "IONIC_RC_EBUSY"; case IONIC_RC_EEXIST: return "IONIC_RC_EEXIST"; case IONIC_RC_EINVAL: return "IONIC_RC_EINVAL"; case IONIC_RC_ENOSPC: return "IONIC_RC_ENOSPC"; case IONIC_RC_ERANGE: return "IONIC_RC_ERANGE"; case IONIC_RC_BAD_ADDR: return "IONIC_RC_BAD_ADDR"; case IONIC_RC_DEV_CMD: return "IONIC_RC_DEV_CMD"; case IONIC_RC_ENOSUPP: return "IONIC_RC_ENOSUPP"; case IONIC_RC_ERROR: return "IONIC_RC_ERROR"; case IONIC_RC_ERDMA: return "IONIC_RC_ERDMA"; case IONIC_RC_EBAD_FW: return "IONIC_RC_EBAD_FW"; default: return "IONIC_RC_UNKNOWN"; }} int ionic_error_to_errno(enum ionic_status_code code){ switch (code) { case IONIC_RC_SUCCESS: return 0; case IONIC_RC_EVERSION: case IONIC_RC_EQTYPE: case IONIC_RC_EQID: case IONIC_RC_EINVAL: return -EINVAL; case IONIC_RC_ENOSUPP: return -EOPNOTSUPP; case IONIC_RC_EPERM: return -EPERM; case IONIC_RC_ENOENT: return -ENOENT; case IONIC_RC_EAGAIN: return -EAGAIN; case IONIC_RC_ENOMEM: return -ENOMEM; case IONIC_RC_EFAULT: return -EFAULT; case IONIC_RC_EBUSY: return -EBUSY; case IONIC_RC_EEXIST: return -EEXIST; case IONIC_RC_ENOSPC: return -ENOSPC; case IONIC_RC_ERANGE: return -ERANGE; case IONIC_RC_BAD_ADDR: return -EFAULT; case IONIC_RC_EOPCODE: case IONIC_RC_EINTR: case IONIC_RC_DEV_CMD: case IONIC_RC_ERROR: case IONIC_RC_ERDMA: case IONIC_RC_EIO: default: return -EIO; }}EXPORT_SYMBOL_NS(ionic_error_to_errno, "NET_IONIC"); static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode){ switch (opcode) { case IONIC_CMD_NOP: return "IONIC_CMD_NOP"; case IONIC_CMD_INIT: return "IONIC_CMD_INIT"; case IONIC_CMD_RESET: return "IONIC_CMD_RESET"; case IONIC_CMD_IDENTIFY: return "IONIC_CMD_IDENTIFY"; case IONIC_CMD_GETATTR: return "IONIC_CMD_GETATTR"; case IONIC_CMD_SETATTR: return "IONIC_CMD_SETATTR"; case IONIC_CMD_PORT_IDENTIFY: return "IONIC_CMD_PORT_IDENTIFY"; case IONIC_CMD_PORT_INIT: return "IONIC_CMD_PORT_INIT"; case IONIC_CMD_PORT_RESET: return "IONIC_CMD_PORT_RESET"; case IONIC_CMD_PORT_GETATTR: return "IONIC_CMD_PORT_GETATTR"; case IONIC_CMD_PORT_SETATTR: return "IONIC_CMD_PORT_SETATTR"; case IONIC_CMD_LIF_INIT: return "IONIC_CMD_LIF_INIT"; case IONIC_CMD_LIF_RESET: return "IONIC_CMD_LIF_RESET"; case IONIC_CMD_LIF_IDENTIFY: return "IONIC_CMD_LIF_IDENTIFY"; case IONIC_CMD_LIF_SETATTR: return "IONIC_CMD_LIF_SETATTR"; case IONIC_CMD_LIF_GETATTR: return "IONIC_CMD_LIF_GETATTR"; case IONIC_CMD_LIF_SETPHC: return "IONIC_CMD_LIF_SETPHC"; case IONIC_CMD_RX_MODE_SET: return "IONIC_CMD_RX_MODE_SET"; case IONIC_CMD_RX_FILTER_ADD: return "IONIC_CMD_RX_FILTER_ADD"; case IONIC_CMD_RX_FILTER_DEL: return "IONIC_CMD_RX_FILTER_DEL"; case IONIC_CMD_Q_IDENTIFY: return "IONIC_CMD_Q_IDENTIFY"; case IONIC_CMD_Q_INIT: return "IONIC_CMD_Q_INIT"; case IONIC_CMD_Q_CONTROL: return "IONIC_CMD_Q_CONTROL"; case IONIC_CMD_RDMA_RESET_LIF: return "IONIC_CMD_RDMA_RESET_LIF"; case IONIC_CMD_RDMA_CREATE_EQ: return "IONIC_CMD_RDMA_CREATE_EQ"; case IONIC_CMD_RDMA_CREATE_CQ: return "IONIC_CMD_RDMA_CREATE_CQ"; case IONIC_CMD_RDMA_CREATE_ADMINQ: return "IONIC_CMD_RDMA_CREATE_ADMINQ"; case IONIC_CMD_FW_DOWNLOAD: return "IONIC_CMD_FW_DOWNLOAD"; case IONIC_CMD_FW_CONTROL: return "IONIC_CMD_FW_CONTROL"; case IONIC_CMD_FW_DOWNLOAD_V1: return "IONIC_CMD_FW_DOWNLOAD_V1"; case IONIC_CMD_FW_CONTROL_V1: return "IONIC_CMD_FW_CONTROL_V1"; case IONIC_CMD_VF_GETATTR: return "IONIC_CMD_VF_GETATTR"; case IONIC_CMD_VF_SETATTR: return "IONIC_CMD_VF_SETATTR"; default: return "DEVCMD_UNKNOWN"; }} static void ionic_adminq_flush(struct ionic_lif *lif){ struct ionic_admin_desc_info *desc_info; struct ionic_admin_cmd *desc; unsigned long irqflags; struct ionic_queue *q; spin_lock_irqsave(&lif->adminq_lock, irqflags); if (!lif->adminqcq) { spin_unlock_irqrestore(&lif->adminq_lock, irqflags); return; } q = &lif->adminqcq->q; while (q->tail_idx != q->head_idx) { desc = &q->adminq[q->tail_idx]; desc_info = &q->admin_info[q->tail_idx]; memset(desc, 0, sizeof(union ionic_adminq_cmd)); desc_info->ctx = NULL; q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); } spin_unlock_irqrestore(&lif->adminq_lock, irqflags);} void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode, u8 status, int err){ const char *stat_str; stat_str = (err == -ETIMEDOUT) ? "TIMEOUT" : ionic_error_to_str(status); netdev_err(lif->netdev, "%s (%d) failed: %s (%d)\n", ionic_opcode_to_str(opcode), opcode, stat_str, err);} static int ionic_adminq_check_err(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, const bool timeout, const bool do_msg){ int err = 0; if (ctx->comp.comp.status || timeout) { err = timeout ? -ETIMEDOUT : ionic_error_to_errno(ctx->comp.comp.status); if (do_msg) ionic_adminq_netdev_err_print(lif, ctx->cmd.cmd.opcode, ctx->comp.comp.status, err); if (timeout) ionic_adminq_flush(lif); } return err;} bool ionic_notifyq_service(struct ionic_cq *cq){ struct ionic_deferred_work *work; union ionic_notifyq_comp *comp; struct net_device *netdev; struct ionic_queue *q; struct ionic_lif *lif; u64 eid; comp = &((union ionic_notifyq_comp *)cq->base)[cq->tail_idx]; q = cq->bound_q; lif = q->admin_info[0].ctx; netdev = lif->netdev; eid = le64_to_cpu(comp->event.eid); /* Have we run out of new completions to process? */ if ((s64)(eid - lif->last_eid) <= 0) return false; dma_rmb(); lif->last_eid = eid; dev_dbg(lif->ionic->dev, "notifyq event:\n"); dynamic_hex_dump("event ", DUMP_PREFIX_OFFSET, 16, 1, comp, sizeof(*comp), true); switch (le16_to_cpu(comp->event.ecode)) { case IONIC_EVENT_LINK_CHANGE: ionic_link_status_check_request(lif, CAN_NOT_SLEEP); break; case IONIC_EVENT_RESET: if (lif->ionic->idev.fw_status_ready && !test_bit(IONIC_LIF_F_FW_RESET, lif->state) && !test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { work = kzalloc_obj(*work, GFP_ATOMIC); if (!work) { netdev_err(lif->netdev, "Reset event dropped\n"); clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state); } else { work->type = IONIC_DW_TYPE_LIF_RESET; ionic_lif_deferred_enqueue(lif, work); } } break; default: netdev_warn(netdev, "Notifyq event ecode=%d eid=%lld\n", comp->event.ecode, eid); break; } return true;} bool ionic_adminq_service(struct ionic_cq *cq){ struct ionic_admin_desc_info *desc_info; struct ionic_queue *q = cq->bound_q; struct ionic_admin_comp *comp; u16 index; comp = &((struct ionic_admin_comp *)cq->base)[cq->tail_idx]; if (!color_match(comp->color, cq->done_color)) return false; dma_rmb(); /* check for empty queue */ if (q->tail_idx == q->head_idx) return false; do { desc_info = &q->admin_info[q->tail_idx]; index = q->tail_idx; q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); if (likely(desc_info->ctx)) { struct ionic_admin_ctx *ctx = desc_info->ctx; memcpy(&ctx->comp, comp, sizeof(*comp)); dev_dbg(q->dev, "comp admin queue command:\n"); dynamic_hex_dump("comp ", DUMP_PREFIX_OFFSET, 16, 1, &ctx->comp, sizeof(ctx->comp), true); complete_all(&ctx->work); desc_info->ctx = NULL; } } while (index != le16_to_cpu(comp->comp_index)); return true;} bool ionic_adminq_poke_doorbell(struct ionic_queue *q){ struct ionic_lif *lif = q->lif; unsigned long now, then, dif; unsigned long irqflags; spin_lock_irqsave(&lif->adminq_lock, irqflags); if (q->tail_idx == q->head_idx) { spin_unlock_irqrestore(&lif->adminq_lock, irqflags); return false; } now = READ_ONCE(jiffies); then = q->dbell_jiffies; dif = now - then; if (dif > q->dbell_deadline) { ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type, q->dbval | q->head_idx); q->dbell_jiffies = now; } spin_unlock_irqrestore(&lif->adminq_lock, irqflags); return true;} int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx){ struct ionic_admin_desc_info *desc_info; struct ionic_admin_cmd *desc; unsigned long irqflags; struct ionic_queue *q; int err = 0; spin_lock_irqsave(&lif->adminq_lock, irqflags); if (!lif->adminqcq) { spin_unlock_irqrestore(&lif->adminq_lock, irqflags); return -EIO; } q = &lif->adminqcq->q; if (!ionic_q_has_space(q, 1)) { err = -ENOSPC; goto err_out; } err = ionic_heartbeat_check(lif->ionic); if (err) goto err_out; desc_info = &q->admin_info[q->head_idx]; desc_info->ctx = ctx; desc = &q->adminq[q->head_idx]; memcpy(desc, &ctx->cmd, sizeof(ctx->cmd)); dev_dbg(&lif->netdev->dev, "post admin queue command:\n"); dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1, &ctx->cmd, sizeof(ctx->cmd), true); ionic_q_post(q, true); err_out: spin_unlock_irqrestore(&lif->adminq_lock, irqflags); return err;} int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, const int err, const bool do_msg){ struct net_device *netdev = lif->netdev; unsigned long time_limit; unsigned long time_start; unsigned long time_done; unsigned long remaining; const char *name; name = ionic_opcode_to_str(ctx->cmd.cmd.opcode); if (err) { if (do_msg && !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) netdev_err(netdev, "Posting of %s (%d) failed: %d\n", name, ctx->cmd.cmd.opcode, err); ctx->comp.comp.status = IONIC_RC_ERROR; return err; } time_start = jiffies; time_limit = time_start + HZ * (ulong)DEVCMD_TIMEOUT; do { remaining = wait_for_completion_timeout(&ctx->work, IONIC_ADMINQ_TIME_SLICE); /* check for done */ if (remaining) break; /* force a check of FW status and break out if FW reset */ ionic_heartbeat_check(lif->ionic); if ((test_bit(IONIC_LIF_F_FW_RESET, lif->state) && !lif->ionic->idev.fw_status_ready) || test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { if (do_msg) netdev_warn(netdev, "%s (%d) interrupted, FW in reset\n", name, ctx->cmd.cmd.opcode); ctx->comp.comp.status = IONIC_RC_ERROR; return -ENXIO; } } while (time_before(jiffies, time_limit)); time_done = jiffies; dev_dbg(lif->ionic->dev, "%s: elapsed %d msecs\n", __func__, jiffies_to_msecs(time_done - time_start)); return ionic_adminq_check_err(lif, ctx, time_after_eq(time_done, time_limit), do_msg);} static int __ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, const bool do_msg){ int err; if (!ionic_is_fw_running(&lif->ionic->idev)) return 0; err = ionic_adminq_post(lif, ctx); return ionic_adminq_wait(lif, ctx, err, do_msg);} int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx){ return __ionic_adminq_post_wait(lif, ctx, true);}EXPORT_SYMBOL_NS(ionic_adminq_post_wait, "NET_IONIC"); int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx *ctx){ return __ionic_adminq_post_wait(lif, ctx, false);} static void ionic_dev_cmd_clean(struct ionic *ionic){ struct ionic_dev *idev = &ionic->idev; if (!idev->dev_cmd_regs) return;Lines 1–500 of 814. Load more
// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ #include <linux/printk.h> #include <linux/dynamic_debug.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/utsname.h> #include <generated/utsrelease.h> #include <linux/ctype.h> #include "ionic.h" #include "ionic_bus.h" #include "ionic_lif.h" #include "ionic_debugfs.h" MODULE_DESCRIPTION(IONIC_DRV_DESCRIPTION); MODULE_AUTHOR("Shannon Nelson <shannon.nelson@amd.com>"); MODULE_LICENSE("GPL"); static const char *ionic_error_to_str(enum ionic_status_code code) { switch (code) { case IONIC_RC_SUCCESS: return "IONIC_RC_SUCCESS"; case IONIC_RC_EVERSION: return "IONIC_RC_EVERSION"; case IONIC_RC_EOPCODE: return "IONIC_RC_EOPCODE"; case IONIC_RC_EIO: return "IONIC_RC_EIO"; case IONIC_RC_EPERM: return "IONIC_RC_EPERM"; case IONIC_RC_EQID: return "IONIC_RC_EQID"; case IONIC_RC_EQTYPE: return "IONIC_RC_EQTYPE"; case IONIC_RC_ENOENT: return "IONIC_RC_ENOENT"; case IONIC_RC_EINTR: return "IONIC_RC_EINTR"; case IONIC_RC_EAGAIN: return "IONIC_RC_EAGAIN"; case IONIC_RC_ENOMEM: return "IONIC_RC_ENOMEM"; case IONIC_RC_EFAULT: return "IONIC_RC_EFAULT"; case IONIC_RC_EBUSY: return "IONIC_RC_EBUSY"; case IONIC_RC_EEXIST: return "IONIC_RC_EEXIST"; case IONIC_RC_EINVAL: return "IONIC_RC_EINVAL"; case IONIC_RC_ENOSPC: return "IONIC_RC_ENOSPC"; case IONIC_RC_ERANGE: return "IONIC_RC_ERANGE"; case IONIC_RC_BAD_ADDR: return "IONIC_RC_BAD_ADDR"; case IONIC_RC_DEV_CMD: return "IONIC_RC_DEV_CMD"; case IONIC_RC_ENOSUPP: return "IONIC_RC_ENOSUPP"; case IONIC_RC_ERROR: return "IONIC_RC_ERROR"; case IONIC_RC_ERDMA: return "IONIC_RC_ERDMA"; case IONIC_RC_EBAD_FW: return "IONIC_RC_EBAD_FW"; default: return "IONIC_RC_UNKNOWN"; } } int ionic_error_to_errno(enum ionic_status_code code) { switch (code) { case IONIC_RC_SUCCESS: return 0; case IONIC_RC_EVERSION: case IONIC_RC_EQTYPE: case IONIC_RC_EQID: case IONIC_RC_EINVAL: return -EINVAL; case IONIC_RC_ENOSUPP: return -EOPNOTSUPP; case IONIC_RC_EPERM: return -EPERM; case IONIC_RC_ENOENT: return -ENOENT; case IONIC_RC_EAGAIN: return -EAGAIN; case IONIC_RC_ENOMEM: return -ENOMEM; case IONIC_RC_EFAULT: return -EFAULT; case IONIC_RC_EBUSY: return -EBUSY; case IONIC_RC_EEXIST: return -EEXIST; case IONIC_RC_ENOSPC: return -ENOSPC; case IONIC_RC_ERANGE: return -ERANGE; case IONIC_RC_BAD_ADDR: return -EFAULT; case IONIC_RC_EOPCODE: case IONIC_RC_EINTR: case IONIC_RC_DEV_CMD: case IONIC_RC_ERROR: case IONIC_RC_ERDMA: case IONIC_RC_EIO: default: return -EIO; } } EXPORT_SYMBOL_NS(ionic_error_to_errno, "NET_IONIC"); static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode) { switch (opcode) { case IONIC_CMD_NOP: return "IONIC_CMD_NOP"; case IONIC_CMD_INIT: return "IONIC_CMD_INIT"; case IONIC_CMD_RESET: return "IONIC_CMD_RESET"; case IONIC_CMD_IDENTIFY: return "IONIC_CMD_IDENTIFY"; case IONIC_CMD_GETATTR: return "IONIC_CMD_GETATTR"; case IONIC_CMD_SETATTR: return "IONIC_CMD_SETATTR"; case IONIC_CMD_PORT_IDENTIFY: return "IONIC_CMD_PORT_IDENTIFY"; case IONIC_CMD_PORT_INIT: return "IONIC_CMD_PORT_INIT"; case IONIC_CMD_PORT_RESET: return "IONIC_CMD_PORT_RESET"; case IONIC_CMD_PORT_GETATTR: return "IONIC_CMD_PORT_GETATTR"; case IONIC_CMD_PORT_SETATTR: return "IONIC_CMD_PORT_SETATTR"; case IONIC_CMD_LIF_INIT: return "IONIC_CMD_LIF_INIT"; case IONIC_CMD_LIF_RESET: return "IONIC_CMD_LIF_RESET"; case IONIC_CMD_LIF_IDENTIFY: return "IONIC_CMD_LIF_IDENTIFY"; case IONIC_CMD_LIF_SETATTR: return "IONIC_CMD_LIF_SETATTR"; case IONIC_CMD_LIF_GETATTR: return "IONIC_CMD_LIF_GETATTR"; case IONIC_CMD_LIF_SETPHC: return "IONIC_CMD_LIF_SETPHC"; case IONIC_CMD_RX_MODE_SET: return "IONIC_CMD_RX_MODE_SET"; case IONIC_CMD_RX_FILTER_ADD: return "IONIC_CMD_RX_FILTER_ADD"; case IONIC_CMD_RX_FILTER_DEL: return "IONIC_CMD_RX_FILTER_DEL"; case IONIC_CMD_Q_IDENTIFY: return "IONIC_CMD_Q_IDENTIFY"; case IONIC_CMD_Q_INIT: return "IONIC_CMD_Q_INIT"; case IONIC_CMD_Q_CONTROL: return "IONIC_CMD_Q_CONTROL"; case IONIC_CMD_RDMA_RESET_LIF: return "IONIC_CMD_RDMA_RESET_LIF"; case IONIC_CMD_RDMA_CREATE_EQ: return "IONIC_CMD_RDMA_CREATE_EQ"; case IONIC_CMD_RDMA_CREATE_CQ: return "IONIC_CMD_RDMA_CREATE_CQ"; case IONIC_CMD_RDMA_CREATE_ADMINQ: return "IONIC_CMD_RDMA_CREATE_ADMINQ"; case IONIC_CMD_FW_DOWNLOAD: return "IONIC_CMD_FW_DOWNLOAD"; case IONIC_CMD_FW_CONTROL: return "IONIC_CMD_FW_CONTROL"; case IONIC_CMD_FW_DOWNLOAD_V1: return "IONIC_CMD_FW_DOWNLOAD_V1"; case IONIC_CMD_FW_CONTROL_V1: return "IONIC_CMD_FW_CONTROL_V1"; case IONIC_CMD_VF_GETATTR: return "IONIC_CMD_VF_GETATTR"; case IONIC_CMD_VF_SETATTR: return "IONIC_CMD_VF_SETATTR"; default: return "DEVCMD_UNKNOWN"; } } static void ionic_adminq_flush(struct ionic_lif *lif) { struct ionic_admin_desc_info *desc_info; struct ionic_admin_cmd *desc; unsigned long irqflags; struct ionic_queue *q; spin_lock_irqsave(&lif->adminq_lock, irqflags); if (!lif->adminqcq) { spin_unlock_irqrestore(&lif->adminq_lock, irqflags); return; } q = &lif->adminqcq->q; while (q->tail_idx != q->head_idx) { desc = &q->adminq[q->tail_idx]; desc_info = &q->admin_info[q->tail_idx]; memset(desc, 0, sizeof(union ionic_adminq_cmd)); desc_info->ctx = NULL; q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); } spin_unlock_irqrestore(&lif->adminq_lock, irqflags); } void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode, u8 status, int err) { const char *stat_str; stat_str = (err == -ETIMEDOUT) ? "TIMEOUT" : ionic_error_to_str(status); netdev_err(lif->netdev, "%s (%d) failed: %s (%d)\n", ionic_opcode_to_str(opcode), opcode, stat_str, err); } static int ionic_adminq_check_err(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, const bool timeout, const bool do_msg) { int err = 0; if (ctx->comp.comp.status || timeout) { err = timeout ? -ETIMEDOUT : ionic_error_to_errno(ctx->comp.comp.status); if (do_msg) ionic_adminq_netdev_err_print(lif, ctx->cmd.cmd.opcode, ctx->comp.comp.status, err); if (timeout) ionic_adminq_flush(lif); } return err; } bool ionic_notifyq_service(struct ionic_cq *cq) { struct ionic_deferred_work *work; union ionic_notifyq_comp *comp; struct net_device *netdev; struct ionic_queue *q; struct ionic_lif *lif; u64 eid; comp = &((union ionic_notifyq_comp *)cq->base)[cq->tail_idx]; q = cq->bound_q; lif = q->admin_info[0].ctx; netdev = lif->netdev; eid = le64_to_cpu(comp->event.eid); /* Have we run out of new completions to process? */ if ((s64)(eid - lif->last_eid) <= 0) return false; dma_rmb(); lif->last_eid = eid; dev_dbg(lif->ionic->dev, "notifyq event:\n"); dynamic_hex_dump("event ", DUMP_PREFIX_OFFSET, 16, 1, comp, sizeof(*comp), true); switch (le16_to_cpu(comp->event.ecode)) { case IONIC_EVENT_LINK_CHANGE: ionic_link_status_check_request(lif, CAN_NOT_SLEEP); break; case IONIC_EVENT_RESET: if (lif->ionic->idev.fw_status_ready && !test_bit(IONIC_LIF_F_FW_RESET, lif->state) && !test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { work = kzalloc_obj(*work, GFP_ATOMIC); if (!work) { netdev_err(lif->netdev, "Reset event dropped\n"); clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state); } else { work->type = IONIC_DW_TYPE_LIF_RESET; ionic_lif_deferred_enqueue(lif, work); } } break; default: netdev_warn(netdev, "Notifyq event ecode=%d eid=%lld\n", comp->event.ecode, eid); break; } return true; } bool ionic_adminq_service(struct ionic_cq *cq) { struct ionic_admin_desc_info *desc_info; struct ionic_queue *q = cq->bound_q; struct ionic_admin_comp *comp; u16 index; comp = &((struct ionic_admin_comp *)cq->base)[cq->tail_idx]; if (!color_match(comp->color, cq->done_color)) return false; dma_rmb(); /* check for empty queue */ if (q->tail_idx == q->head_idx) return false; do { desc_info = &q->admin_info[q->tail_idx]; index = q->tail_idx; q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); if (likely(desc_info->ctx)) { struct ionic_admin_ctx *ctx = desc_info->ctx; memcpy(&ctx->comp, comp, sizeof(*comp)); dev_dbg(q->dev, "comp admin queue command:\n"); dynamic_hex_dump("comp ", DUMP_PREFIX_OFFSET, 16, 1, &ctx->comp, sizeof(ctx->comp), true); complete_all(&ctx->work); desc_info->ctx = NULL; } } while (index != le16_to_cpu(comp->comp_index)); return true; } bool ionic_adminq_poke_doorbell(struct ionic_queue *q) { struct ionic_lif *lif = q->lif; unsigned long now, then, dif; unsigned long irqflags; spin_lock_irqsave(&lif->adminq_lock, irqflags); if (q->tail_idx == q->head_idx) { spin_unlock_irqrestore(&lif->adminq_lock, irqflags); return false; } now = READ_ONCE(jiffies); then = q->dbell_jiffies; dif = now - then; if (dif > q->dbell_deadline) { ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type, q->dbval | q->head_idx); q->dbell_jiffies = now; } spin_unlock_irqrestore(&lif->adminq_lock, irqflags); return true; } int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) { struct ionic_admin_desc_info *desc_info; struct ionic_admin_cmd *desc; unsigned long irqflags; struct ionic_queue *q; int err = 0; spin_lock_irqsave(&lif->adminq_lock, irqflags); if (!lif->adminqcq) { spin_unlock_irqrestore(&lif->adminq_lock, irqflags); return -EIO; } q = &lif->adminqcq->q; if (!ionic_q_has_space(q, 1)) { err = -ENOSPC; goto err_out; } err = ionic_heartbeat_check(lif->ionic); if (err) goto err_out; desc_info = &q->admin_info[q->head_idx]; desc_info->ctx = ctx; desc = &q->adminq[q->head_idx]; memcpy(desc, &ctx->cmd, sizeof(ctx->cmd)); dev_dbg(&lif->netdev->dev, "post admin queue command:\n"); dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1, &ctx->cmd, sizeof(ctx->cmd), true); ionic_q_post(q, true); err_out: spin_unlock_irqrestore(&lif->adminq_lock, irqflags); return err; } int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, const int err, const bool do_msg) { struct net_device *netdev = lif->netdev; unsigned long time_limit; unsigned long time_start; unsigned long time_done; unsigned long remaining; const char *name; name = ionic_opcode_to_str(ctx->cmd.cmd.opcode); if (err) { if (do_msg && !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) netdev_err(netdev, "Posting of %s (%d) failed: %d\n", name, ctx->cmd.cmd.opcode, err); ctx->comp.comp.status = IONIC_RC_ERROR; return err; } time_start = jiffies; time_limit = time_start + HZ * (ulong)DEVCMD_TIMEOUT; do { remaining = wait_for_completion_timeout(&ctx->work, IONIC_ADMINQ_TIME_SLICE); /* check for done */ if (remaining) break; /* force a check of FW status and break out if FW reset */ ionic_heartbeat_check(lif->ionic); if ((test_bit(IONIC_LIF_F_FW_RESET, lif->state) && !lif->ionic->idev.fw_status_ready) || test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { if (do_msg) netdev_warn(netdev, "%s (%d) interrupted, FW in reset\n", name, ctx->cmd.cmd.opcode); ctx->comp.comp.status = IONIC_RC_ERROR; return -ENXIO; } } while (time_before(jiffies, time_limit)); time_done = jiffies; dev_dbg(lif->ionic->dev, "%s: elapsed %d msecs\n", __func__, jiffies_to_msecs(time_done - time_start)); return ionic_adminq_check_err(lif, ctx, time_after_eq(time_done, time_limit), do_msg); } static int __ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, const bool do_msg) { int err; if (!ionic_is_fw_running(&lif->ionic->idev)) return 0; err = ionic_adminq_post(lif, ctx); return ionic_adminq_wait(lif, ctx, err, do_msg); } int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) { return __ionic_adminq_post_wait(lif, ctx, true); } EXPORT_SYMBOL_NS(ionic_adminq_post_wait, "NET_IONIC"); int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) { return __ionic_adminq_post_wait(lif, ctx, false); } static void ionic_dev_cmd_clean(struct ionic *ionic) { struct ionic_dev *idev = &ionic->idev; if (!idev->dev_cmd_regs) return;