Add AdminQ specific message requests and completion handling.

Signed-off-by: Shannon Nelson <snel...@pensando.io>
---
 drivers/net/ethernet/pensando/ionic/ionic.h   |   7 ++
 .../net/ethernet/pensando/ionic/ionic_main.c  | 106 ++++++++++++++++++
 2 files changed, 113 insertions(+)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h 
b/drivers/net/ethernet/pensando/ionic/ionic.h
index f1e7c754bcda..c79bf5450495 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -46,9 +46,16 @@ struct ionic {
        DECLARE_BITMAP(intrs, INTR_CTRL_REGS_MAX);
 };
 
+struct ionic_admin_ctx {
+       struct completion work;
+       union adminq_cmd cmd;
+       union adminq_comp comp;
+};
+
 int ionic_napi(struct napi_struct *napi, int budget, ionic_cq_cb cb,
               ionic_cq_done_cb done_cb, void *done_arg);
 
+int ionic_adminq_post_wait(struct lif *lif, struct ionic_admin_ctx *ctx);
 int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_wait);
 int ionic_set_dma_mask(struct ionic *ionic);
 int ionic_setup(struct ionic *ionic);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c 
b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index 17d8802e69ae..9713e0f584aa 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -169,6 +169,112 @@ static const char *ionic_opcode_to_str(enum cmd_opcode 
opcode)
        }
 }
 
+static void ionic_adminq_flush(struct lif *lif)
+{
+       struct queue *adminq = &lif->adminqcq->q;
+
+       spin_lock(&lif->adminq_lock);
+
+       while (adminq->tail != adminq->head) {
+               memset(adminq->tail->desc, 0, sizeof(union adminq_cmd));
+               adminq->tail->cb = NULL;
+               adminq->tail->cb_arg = NULL;
+               adminq->tail = adminq->tail->next;
+       }
+       spin_unlock(&lif->adminq_lock);
+}
+
+static int ionic_adminq_check_err(struct lif *lif, struct ionic_admin_ctx *ctx,
+                                 bool timeout)
+{
+       struct net_device *netdev = lif->netdev;
+       const char *opcode_str;
+       const char *status_str;
+       int err = 0;
+
+       if (ctx->comp.comp.status || timeout) {
+               opcode_str = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
+               status_str = ionic_error_to_str(ctx->comp.comp.status);
+               err = timeout ? -ETIMEDOUT :
+                               ionic_error_to_errno(ctx->comp.comp.status);
+
+               netdev_err(netdev, "%s (%d) failed: %s (%d)\n",
+                          opcode_str, ctx->cmd.cmd.opcode,
+                          timeout ? "TIMEOUT" : status_str, err);
+
+               if (timeout)
+                       ionic_adminq_flush(lif);
+       }
+
+       return err;
+}
+
+static void ionic_adminq_cb(struct queue *q, struct desc_info *desc_info,
+                           struct cq_info *cq_info, void *cb_arg)
+{
+       struct ionic_admin_ctx *ctx = cb_arg;
+       struct admin_comp *comp = cq_info->cq_desc;
+       struct device *dev = &q->lif->netdev->dev;
+
+       if (!ctx)
+               return;
+
+       memcpy(&ctx->comp, comp, sizeof(*comp));
+
+       dev_dbg(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);
+}
+
+static int ionic_adminq_post(struct lif *lif, struct ionic_admin_ctx *ctx)
+{
+       struct queue *adminq = &lif->adminqcq->q;
+       int err = 0;
+
+       WARN_ON(in_interrupt());
+
+       spin_lock(&lif->adminq_lock);
+       if (!ionic_q_has_space(adminq, 1)) {
+               err = -ENOSPC;
+               goto err_out;
+       }
+
+       memcpy(adminq->head->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(adminq, true, ionic_adminq_cb, ctx);
+
+err_out:
+       spin_unlock(&lif->adminq_lock);
+
+       return err;
+}
+
+int ionic_adminq_post_wait(struct lif *lif, struct ionic_admin_ctx *ctx)
+{
+       struct net_device *netdev = lif->netdev;
+       unsigned long remaining;
+       const char *name;
+       int err;
+
+       err = ionic_adminq_post(lif, ctx);
+       if (err) {
+               name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
+               netdev_err(netdev, "Posting of %s (%d) failed: %d\n",
+                          name, ctx->cmd.cmd.opcode, err);
+               return err;
+       }
+
+       remaining = wait_for_completion_timeout(&ctx->work,
+                                               HZ * (ulong)devcmd_timeout);
+       return ionic_adminq_check_err(lif, ctx, (remaining == 0));
+}
+
 int ionic_napi(struct napi_struct *napi, int budget, ionic_cq_cb cb,
               ionic_cq_done_cb done_cb, void *done_arg)
 {
-- 
2.17.1

Reply via email to