brintos

brintos / linux-shallow public Read only

0
0
Text · 4.8 KiB · 71afd11 Raw
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