linux/drivers/net/ethernet/netronome/nfp/ccm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2016-2019 Netronome Systems, Inc. */
   3
   4#include <linux/bitops.h>
   5
   6#include "ccm.h"
   7#include "nfp_app.h"
   8#include "nfp_net.h"
   9
  10#define ccm_warn(app, msg...)   nn_dp_warn(&(app)->ctrl->dp, msg)
  11
  12#define NFP_CCM_TAG_ALLOC_SPAN  (U16_MAX / 4)
  13
  14static bool nfp_ccm_all_tags_busy(struct nfp_ccm *ccm)
  15{
  16        u16 used_tags;
  17
  18        used_tags = ccm->tag_alloc_next - ccm->tag_alloc_last;
  19
  20        return used_tags > NFP_CCM_TAG_ALLOC_SPAN;
  21}
  22
  23static int nfp_ccm_alloc_tag(struct nfp_ccm *ccm)
  24{
  25        /* CCM is for FW communication which is request-reply.  To make sure
  26         * we don't reuse the message ID too early after timeout - limit the
  27         * number of requests in flight.
  28         */
  29        if (unlikely(nfp_ccm_all_tags_busy(ccm))) {
  30                ccm_warn(ccm->app, "all FW request contexts busy!\n");
  31                return -EAGAIN;
  32        }
  33
  34        WARN_ON(__test_and_set_bit(ccm->tag_alloc_next, ccm->tag_allocator));
  35        return ccm->tag_alloc_next++;
  36}
  37
  38static void nfp_ccm_free_tag(struct nfp_ccm *ccm, u16 tag)
  39{
  40        WARN_ON(!__test_and_clear_bit(tag, ccm->tag_allocator));
  41
  42        while (!test_bit(ccm->tag_alloc_last, ccm->tag_allocator) &&
  43               ccm->tag_alloc_last != ccm->tag_alloc_next)
  44                ccm->tag_alloc_last++;
  45}
  46
  47static struct sk_buff *__nfp_ccm_reply(struct nfp_ccm *ccm, u16 tag)
  48{
  49        unsigned int msg_tag;
  50        struct sk_buff *skb;
  51
  52        skb_queue_walk(&ccm->replies, skb) {
  53                msg_tag = nfp_ccm_get_tag(skb);
  54                if (msg_tag == tag) {
  55                        nfp_ccm_free_tag(ccm, tag);
  56                        __skb_unlink(skb, &ccm->replies);
  57                        return skb;
  58                }
  59        }
  60
  61        return NULL;
  62}
  63
  64static struct sk_buff *
  65nfp_ccm_reply(struct nfp_ccm *ccm, struct nfp_app *app, u16 tag)
  66{
  67        struct sk_buff *skb;
  68
  69        nfp_ctrl_lock(app->ctrl);
  70        skb = __nfp_ccm_reply(ccm, tag);
  71        nfp_ctrl_unlock(app->ctrl);
  72
  73        return skb;
  74}
  75
  76static struct sk_buff *
  77nfp_ccm_reply_drop_tag(struct nfp_ccm *ccm, struct nfp_app *app, u16 tag)
  78{
  79        struct sk_buff *skb;
  80
  81        nfp_ctrl_lock(app->ctrl);
  82        skb = __nfp_ccm_reply(ccm, tag);
  83        if (!skb)
  84                nfp_ccm_free_tag(ccm, tag);
  85        nfp_ctrl_unlock(app->ctrl);
  86
  87        return skb;
  88}
  89
  90static struct sk_buff *
  91nfp_ccm_wait_reply(struct nfp_ccm *ccm, struct nfp_app *app,
  92                   enum nfp_ccm_type type, int tag)
  93{
  94        struct sk_buff *skb;
  95        int i, err;
  96
  97        for (i = 0; i < 50; i++) {
  98                udelay(4);
  99                skb = nfp_ccm_reply(ccm, app, tag);
 100                if (skb)
 101                        return skb;
 102        }
 103
 104        err = wait_event_interruptible_timeout(ccm->wq,
 105                                               skb = nfp_ccm_reply(ccm, app,
 106                                                                   tag),
 107                                               msecs_to_jiffies(5000));
 108        /* We didn't get a response - try last time and atomically drop
 109         * the tag even if no response is matched.
 110         */
 111        if (!skb)
 112                skb = nfp_ccm_reply_drop_tag(ccm, app, tag);
 113        if (err < 0) {
 114                ccm_warn(app, "%s waiting for response to 0x%02x: %d\n",
 115                         err == ERESTARTSYS ? "interrupted" : "error",
 116                         type, err);
 117                return ERR_PTR(err);
 118        }
 119        if (!skb) {
 120                ccm_warn(app, "timeout waiting for response to 0x%02x\n", type);
 121                return ERR_PTR(-ETIMEDOUT);
 122        }
 123
 124        return skb;
 125}
 126
 127struct sk_buff *
 128nfp_ccm_communicate(struct nfp_ccm *ccm, struct sk_buff *skb,
 129                    enum nfp_ccm_type type, unsigned int reply_size)
 130{
 131        struct nfp_app *app = ccm->app;
 132        struct nfp_ccm_hdr *hdr;
 133        int reply_type, tag;
 134
 135        nfp_ctrl_lock(app->ctrl);
 136        tag = nfp_ccm_alloc_tag(ccm);
 137        if (tag < 0) {
 138                nfp_ctrl_unlock(app->ctrl);
 139                dev_kfree_skb_any(skb);
 140                return ERR_PTR(tag);
 141        }
 142
 143        hdr = (void *)skb->data;
 144        hdr->ver = NFP_CCM_ABI_VERSION;
 145        hdr->type = type;
 146        hdr->tag = cpu_to_be16(tag);
 147
 148        __nfp_app_ctrl_tx(app, skb);
 149
 150        nfp_ctrl_unlock(app->ctrl);
 151
 152        skb = nfp_ccm_wait_reply(ccm, app, type, tag);
 153        if (IS_ERR(skb))
 154                return skb;
 155
 156        reply_type = nfp_ccm_get_type(skb);
 157        if (reply_type != __NFP_CCM_REPLY(type)) {
 158                ccm_warn(app, "cmsg drop - wrong type 0x%02x != 0x%02lx!\n",
 159                         reply_type, __NFP_CCM_REPLY(type));
 160                goto err_free;
 161        }
 162        /* 0 reply_size means caller will do the validation */
 163        if (reply_size && skb->len != reply_size) {
 164                ccm_warn(app, "cmsg drop - type 0x%02x wrong size %d != %d!\n",
 165                         type, skb->len, reply_size);
 166                goto err_free;
 167        }
 168
 169        return skb;
 170err_free:
 171        dev_kfree_skb_any(skb);
 172        return ERR_PTR(-EIO);
 173}
 174
 175void nfp_ccm_rx(struct nfp_ccm *ccm, struct sk_buff *skb)
 176{
 177        struct nfp_app *app = ccm->app;
 178        unsigned int tag;
 179
 180        if (unlikely(skb->len < sizeof(struct nfp_ccm_hdr))) {
 181                ccm_warn(app, "cmsg drop - too short %d!\n", skb->len);
 182                goto err_free;
 183        }
 184
 185        nfp_ctrl_lock(app->ctrl);
 186
 187        tag = nfp_ccm_get_tag(skb);
 188        if (unlikely(!test_bit(tag, ccm->tag_allocator))) {
 189                ccm_warn(app, "cmsg drop - no one is waiting for tag %u!\n",
 190                         tag);
 191                goto err_unlock;
 192        }
 193
 194        __skb_queue_tail(&ccm->replies, skb);
 195        wake_up_interruptible_all(&ccm->wq);
 196
 197        nfp_ctrl_unlock(app->ctrl);
 198        return;
 199
 200err_unlock:
 201        nfp_ctrl_unlock(app->ctrl);
 202err_free:
 203        dev_kfree_skb_any(skb);
 204}
 205
 206int nfp_ccm_init(struct nfp_ccm *ccm, struct nfp_app *app)
 207{
 208        ccm->app = app;
 209        skb_queue_head_init(&ccm->replies);
 210        init_waitqueue_head(&ccm->wq);
 211        return 0;
 212}
 213
 214void nfp_ccm_clean(struct nfp_ccm *ccm)
 215{
 216        WARN_ON(!skb_queue_empty(&ccm->replies));
 217}
 218