218 lines · c
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 sure26 * we don't reuse the message ID too early after timeout - limit the27 * 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 drop109 * 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