On Mon, 2019-07-22 at 14:40 -0700, Shannon Nelson wrote:
> The AdminQ is fine for sending messages and requests to the NIC,
> but we also need to have events published from the NIC to the
> driver.  The NotifyQ handles this for us, using the same interrupt
> as AdminQ.
> 
> Signed-off-by: Shannon Nelson <snel...@pensando.io>
> ---
>  .../ethernet/pensando/ionic/ionic_debugfs.c   |  16 ++
>  .../net/ethernet/pensando/ionic/ionic_lif.c   | 181
> +++++++++++++++++-
>  .../net/ethernet/pensando/ionic/ionic_lif.h   |   4 +
>  3 files changed, 200 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
> b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
> index 9af15c69b2a6..1d05b23de303 100644
> --- a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
> +++ b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
> @@ -126,6 +126,7 @@ int ionic_debugfs_add_qcq(struct lif *lif, struct
> qcq *qcq)
>       struct debugfs_blob_wrapper *desc_blob;
>       struct device *dev = lif->ionic->dev;
>       struct intr *intr = &qcq->intr;
> +     struct dentry *stats_dentry;
>       struct queue *q = &qcq->q;
>       struct cq *cq = &qcq->cq;
>  
> @@ -219,6 +220,21 @@ int ionic_debugfs_add_qcq(struct lif *lif,
> struct qcq *qcq)
>                                       intr_ctrl_regset);
>       }
>  
> +     if (qcq->flags & QCQ_F_NOTIFYQ) {
> +             stats_dentry = debugfs_create_dir("notifyblock",
> qcq_dentry);
> +             if (IS_ERR_OR_NULL(stats_dentry))
> +                     return PTR_ERR(stats_dentry);
> +
> +             debugfs_create_u64("eid", 0400, stats_dentry,
> +                                (u64 *)&lif->info->status.eid);
> +             debugfs_create_u16("link_status", 0400, stats_dentry,
> +                                (u16 *)&lif->info-
> >status.link_status);
> +             debugfs_create_u32("link_speed", 0400, stats_dentry,
> +                                (u32 *)&lif->info-
> >status.link_speed);
> +             debugfs_create_u16("link_down_count", 0400,
> stats_dentry,
> +                                (u16 *)&lif->info-
> >status.link_down_count);
> +     }
> +

you never write to these lif->info->status.xyz ..
and link state and speed are/should be available  in "ethtool <ifname>"
so this looks redundant to me. you can also use ethtool -S to report
linkdown count.

>       return 0;
>  }
>  
> diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
> b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
> index 19c046502a26..01f9665611d4 100644
> --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
> +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
> @@ -12,6 +12,8 @@
>  #include "ionic_lif.h"
>  #include "ionic_debugfs.h"
>  
> +static int ionic_notifyq_clean(struct lif *lif, int budget);
> +
>  static bool ionic_adminq_service(struct cq *cq, struct cq_info
> *cq_info)
>  {
>       struct admin_comp *comp = cq_info->cq_desc;
> @@ -26,7 +28,90 @@ static bool ionic_adminq_service(struct cq *cq,
> struct cq_info *cq_info)
>  
>  static int ionic_adminq_napi(struct napi_struct *napi, int budget)
>  {
> -     return ionic_napi(napi, budget, ionic_adminq_service, NULL,
> NULL);
> +     struct lif *lif = napi_to_cq(napi)->lif;
> +     int n_work = 0;
> +     int a_work = 0;
> +
> +     if (likely(lif->notifyqcq && lif->notifyqcq->flags &
> QCQ_F_INITED))
> +             n_work = ionic_notifyq_clean(lif, budget);
> +     a_work = ionic_napi(napi, budget, ionic_adminq_service, NULL,
> NULL);
> +
> +     return max(n_work, a_work);
> +}
> +
> +static bool ionic_notifyq_service(struct cq *cq, struct cq_info
> *cq_info)
> +{
> +     union notifyq_comp *comp = cq_info->cq_desc;
> +     struct net_device *netdev;
> +     struct queue *q;
> +     struct lif *lif;
> +     u64 eid;
> +
> +     q = cq->bound_q;
> +     lif = q->info[0].cb_arg;
> +     netdev = lif->netdev;
> +     eid = le64_to_cpu(comp->event.eid);
> +
> +     /* Have we run out of new completions to process? */
> +     if (eid <= lif->last_eid)
> +             return false;
> +
> +     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 EVENT_OPCODE_LINK_CHANGE:
> +             netdev_info(netdev, "Notifyq EVENT_OPCODE_LINK_CHANGE
> eid=%lld\n",
> +                         eid);
> +             netdev_info(netdev,
> +                         "  link_status=%d link_speed=%d\n",
> +                         le16_to_cpu(comp->link_change.link_status),
> +                         le32_to_cpu(comp->link_change.link_speed));
> +             break;
> +     case EVENT_OPCODE_RESET:
> +             netdev_info(netdev, "Notifyq EVENT_OPCODE_RESET
> eid=%lld\n",
> +                         eid);
> +             netdev_info(netdev, "  reset_code=%d state=%d\n",
> +                         comp->reset.reset_code,
> +                         comp->reset.state);
> +             break;
> +     case EVENT_OPCODE_LOG:
> +             netdev_info(netdev, "Notifyq EVENT_OPCODE_LOG
> eid=%lld\n", eid);
> +             print_hex_dump(KERN_INFO, "notifyq ",
> DUMP_PREFIX_OFFSET, 16, 1,
> +                            comp->log.data, sizeof(comp->log.data),
> true);

So your device can generate log buffer dump into the kernel log .. 
I am not sure how acceptable this is, maybe trace buffer is more
appropriate for this.

> +             break;
> +     default:
> +             netdev_warn(netdev, "Notifyq unknown event ecode=%d
> eid=%lld\n",
> +                         comp->event.ecode, eid);
> +             break;
> +     }
> +
> +     return true;
> +}
> +
> +static int ionic_notifyq_clean(struct lif *lif, int budget)
> +{
> +     struct ionic_dev *idev = &lif->ionic->idev;
> +     struct cq *cq = &lif->notifyqcq->cq;
> +     u32 work_done;
> +
> +     work_done = ionic_cq_service(cq, budget, ionic_notifyq_service,
> +                                  NULL, NULL);
> +     if (work_done)
> +             ionic_intr_credits(idev->intr_ctrl, cq->bound_intr-
> >index,
> +                                work_done,
> IONIC_INTR_CRED_RESET_COALESCE);
> +
> +     /* If we ran out of budget, there are more events
> +      * to process and napi will reschedule us soon
> +      */
> +     if (work_done == budget)
> +             goto return_to_napi;
> +
> +return_to_napi:
> +     return work_done;
>  }
>  
>  static irqreturn_t ionic_isr(int irq, void *data)
> @@ -62,6 +147,17 @@ static void ionic_intr_free(struct lif *lif, int
> index)
>               clear_bit(index, lif->ionic->intrs);
>  }
>  
> +static void ionic_link_qcq_interrupts(struct qcq *src_qcq, struct
> qcq *n_qcq)
> +{
> +     if (WARN_ON(n_qcq->flags & QCQ_F_INTR)) {
> +             ionic_intr_free(n_qcq->cq.lif, n_qcq->intr.index);
> +             n_qcq->flags &= ~QCQ_F_INTR;
> +     }
> +
> +     n_qcq->intr.vector = src_qcq->intr.vector;
> +     n_qcq->intr.index = src_qcq->intr.index;
> +}
> +
>  static int ionic_qcq_alloc(struct lif *lif, unsigned int type,
>                          unsigned int index,
>                          const char *name, unsigned int flags,
> @@ -236,11 +332,36 @@ static int ionic_qcqs_alloc(struct lif *lif)
>       if (err)
>               return err;
>  
> +     if (lif->ionic->nnqs_per_lif) {
> +             flags = QCQ_F_NOTIFYQ;
> +             err = ionic_qcq_alloc(lif, IONIC_QTYPE_NOTIFYQ, 0,
> "notifyq",
> +                                   flags, IONIC_NOTIFYQ_LENGTH,
> +                                   sizeof(struct notifyq_cmd),
> +                                   sizeof(union notifyq_comp),
> +                                   0, lif->kern_pid, &lif-
> >notifyqcq);
> +             if (err)
> +                     goto err_out_free_adminqcq;
> +
> +             /* Let the notifyq ride on the adminq interrupt */
> +             ionic_link_qcq_interrupts(lif->adminqcq, lif-
> >notifyqcq);
> +     }
> +
>       return 0;
> +
> +err_out_free_adminqcq:
> +     ionic_qcq_free(lif, lif->adminqcq);
> +     lif->adminqcq = NULL;
> +
> +     return err;
>  }
>  
>  static void ionic_qcqs_free(struct lif *lif)
>  {
> +     if (lif->notifyqcq) {
> +             ionic_qcq_free(lif, lif->notifyqcq);
> +             lif->notifyqcq = NULL;
> +     }
> +
>       if (lif->adminqcq) {
>               ionic_qcq_free(lif, lif->adminqcq);
>               lif->adminqcq = NULL;
> @@ -400,6 +521,7 @@ static void ionic_lif_deinit(struct lif *lif)
>       clear_bit(LIF_INITED, lif->state);
>  
>       napi_disable(&lif->adminqcq->napi);
> +     ionic_lif_qcq_deinit(lif, lif->notifyqcq);
>       ionic_lif_qcq_deinit(lif, lif->adminqcq);
>  
>       ionic_lif_reset(lif);
> @@ -484,6 +606,55 @@ static int ionic_lif_adminq_init(struct lif
> *lif)
>       return 0;
>  }
>  
> +static int ionic_lif_notifyq_init(struct lif *lif)
> +{
> +     struct device *dev = lif->ionic->dev;
> +     struct qcq *qcq = lif->notifyqcq;
> +     struct queue *q = &qcq->q;
> +     int err;
> +
> +     struct ionic_admin_ctx ctx = {
> +             .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
> +             .cmd.q_init = {
> +                     .opcode = CMD_OPCODE_Q_INIT,
> +                     .lif_index = cpu_to_le16(lif->index),
> +                     .type = q->type,
> +                     .index = cpu_to_le32(q->index),
> +                     .flags = cpu_to_le16(IONIC_QINIT_F_IRQ |
> +                                          IONIC_QINIT_F_ENA),
> +                     .intr_index = cpu_to_le16(lif->adminqcq-
> >intr.index),
> +                     .pid = cpu_to_le16(q->pid),
> +                     .ring_size = ilog2(q->num_descs),
> +                     .ring_base = cpu_to_le64(q->base_pa),
> +             }
> +     };
> +
> +     dev_dbg(dev, "notifyq_init.pid %d\n", ctx.cmd.q_init.pid);
> +     dev_dbg(dev, "notifyq_init.index %d\n", ctx.cmd.q_init.index);
> +     dev_dbg(dev, "notifyq_init.ring_base 0x%llx\n",
> ctx.cmd.q_init.ring_base);
> +     dev_dbg(dev, "notifyq_init.ring_size %d\n",
> ctx.cmd.q_init.ring_size);
> +
> +     err = ionic_adminq_post_wait(lif, &ctx);
> +     if (err)
> +             return err;
> +
> +     q->hw_type = ctx.comp.q_init.hw_type;
> +     q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index);
> +     q->dbval = IONIC_DBELL_QID(q->hw_index);
> +
> +     dev_dbg(dev, "notifyq->hw_type %d\n", q->hw_type);
> +     dev_dbg(dev, "notifyq->hw_index %d\n", q->hw_index);
> +
> +     /* preset the callback info */
> +     q->info[0].cb_arg = lif;
> +
> +     qcq->flags |= QCQ_F_INITED;
> +
> +     ionic_debugfs_add_qcq(lif, qcq);
> +
> +     return 0;
> +}
> +
>  static int ionic_lif_init(struct lif *lif)
>  {
>       struct ionic_dev *idev = &lif->ionic->idev;
> @@ -534,10 +705,18 @@ static int ionic_lif_init(struct lif *lif)
>       if (err)
>               goto err_out_adminq_deinit;
>  
> +     if (lif->ionic->nnqs_per_lif) {
> +             err = ionic_lif_notifyq_init(lif);
> +             if (err)
> +                     goto err_out_notifyq_deinit;
> +     }
> +
>       set_bit(LIF_INITED, lif->state);
>  
>       return 0;
>  
> +err_out_notifyq_deinit:
> +     ionic_lif_qcq_deinit(lif, lif->notifyqcq);
>  err_out_adminq_deinit:
>       ionic_lif_qcq_deinit(lif, lif->adminqcq);
>       ionic_lif_reset(lif);
> diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
> b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
> index 28ab92b43a64..80eec0778f40 100644
> --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
> +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
> @@ -7,6 +7,7 @@
>  #include <linux/pci.h>
>  
>  #define IONIC_ADMINQ_LENGTH  16      /* must be a power of two */
> +#define IONIC_NOTIFYQ_LENGTH 64      /* must be a power of two */
>  
>  #define GET_NAPI_CNTR_IDX(work_done) (work_done)
>  #define MAX_NUM_NAPI_CNTR    (NAPI_POLL_WEIGHT + 1)
> @@ -26,6 +27,7 @@ struct rx_stats {
>  #define QCQ_F_INITED         BIT(0)
>  #define QCQ_F_SG             BIT(1)
>  #define QCQ_F_INTR           BIT(2)
> +#define QCQ_F_NOTIFYQ                BIT(5)
>  
>  struct napi_stats {
>       u64 poll_count;
> @@ -78,6 +80,8 @@ struct lif {
>       u64 __iomem *kern_dbpage;
>       spinlock_t adminq_lock;         /* lock for AdminQ operations
> */
>       struct qcq *adminqcq;
> +     struct qcq *notifyqcq;
> +     u64 last_eid;
>       unsigned int neqs;
>       unsigned int nxqs;
>  

Reply via email to