162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (C) 2016-2019 Netronome Systems, Inc. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/bitops.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "ccm.h"
762306a36Sopenharmony_ci#include "nfp_app.h"
862306a36Sopenharmony_ci#include "nfp_net.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define ccm_warn(app, msg...)	nn_dp_warn(&(app)->ctrl->dp, msg)
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define NFP_CCM_TAG_ALLOC_SPAN	(U16_MAX / 4)
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic bool nfp_ccm_all_tags_busy(struct nfp_ccm *ccm)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	u16 used_tags;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	used_tags = ccm->tag_alloc_next - ccm->tag_alloc_last;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	return used_tags > NFP_CCM_TAG_ALLOC_SPAN;
2162306a36Sopenharmony_ci}
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int nfp_ccm_alloc_tag(struct nfp_ccm *ccm)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	/* CCM is for FW communication which is request-reply.  To make sure
2662306a36Sopenharmony_ci	 * we don't reuse the message ID too early after timeout - limit the
2762306a36Sopenharmony_ci	 * number of requests in flight.
2862306a36Sopenharmony_ci	 */
2962306a36Sopenharmony_ci	if (unlikely(nfp_ccm_all_tags_busy(ccm))) {
3062306a36Sopenharmony_ci		ccm_warn(ccm->app, "all FW request contexts busy!\n");
3162306a36Sopenharmony_ci		return -EAGAIN;
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	WARN_ON(__test_and_set_bit(ccm->tag_alloc_next, ccm->tag_allocator));
3562306a36Sopenharmony_ci	return ccm->tag_alloc_next++;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void nfp_ccm_free_tag(struct nfp_ccm *ccm, u16 tag)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	WARN_ON(!__test_and_clear_bit(tag, ccm->tag_allocator));
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	while (!test_bit(ccm->tag_alloc_last, ccm->tag_allocator) &&
4362306a36Sopenharmony_ci	       ccm->tag_alloc_last != ccm->tag_alloc_next)
4462306a36Sopenharmony_ci		ccm->tag_alloc_last++;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic struct sk_buff *__nfp_ccm_reply(struct nfp_ccm *ccm, u16 tag)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	unsigned int msg_tag;
5062306a36Sopenharmony_ci	struct sk_buff *skb;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	skb_queue_walk(&ccm->replies, skb) {
5362306a36Sopenharmony_ci		msg_tag = nfp_ccm_get_tag(skb);
5462306a36Sopenharmony_ci		if (msg_tag == tag) {
5562306a36Sopenharmony_ci			nfp_ccm_free_tag(ccm, tag);
5662306a36Sopenharmony_ci			__skb_unlink(skb, &ccm->replies);
5762306a36Sopenharmony_ci			return skb;
5862306a36Sopenharmony_ci		}
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return NULL;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic struct sk_buff *
6562306a36Sopenharmony_cinfp_ccm_reply(struct nfp_ccm *ccm, struct nfp_app *app, u16 tag)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct sk_buff *skb;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	nfp_ctrl_lock(app->ctrl);
7062306a36Sopenharmony_ci	skb = __nfp_ccm_reply(ccm, tag);
7162306a36Sopenharmony_ci	nfp_ctrl_unlock(app->ctrl);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return skb;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct sk_buff *
7762306a36Sopenharmony_cinfp_ccm_reply_drop_tag(struct nfp_ccm *ccm, struct nfp_app *app, u16 tag)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct sk_buff *skb;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	nfp_ctrl_lock(app->ctrl);
8262306a36Sopenharmony_ci	skb = __nfp_ccm_reply(ccm, tag);
8362306a36Sopenharmony_ci	if (!skb)
8462306a36Sopenharmony_ci		nfp_ccm_free_tag(ccm, tag);
8562306a36Sopenharmony_ci	nfp_ctrl_unlock(app->ctrl);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return skb;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct sk_buff *
9162306a36Sopenharmony_cinfp_ccm_wait_reply(struct nfp_ccm *ccm, struct nfp_app *app,
9262306a36Sopenharmony_ci		   enum nfp_ccm_type type, int tag)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct sk_buff *skb;
9562306a36Sopenharmony_ci	int i, err;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	for (i = 0; i < 50; i++) {
9862306a36Sopenharmony_ci		udelay(4);
9962306a36Sopenharmony_ci		skb = nfp_ccm_reply(ccm, app, tag);
10062306a36Sopenharmony_ci		if (skb)
10162306a36Sopenharmony_ci			return skb;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	err = wait_event_interruptible_timeout(ccm->wq,
10562306a36Sopenharmony_ci					       skb = nfp_ccm_reply(ccm, app,
10662306a36Sopenharmony_ci								   tag),
10762306a36Sopenharmony_ci					       msecs_to_jiffies(5000));
10862306a36Sopenharmony_ci	/* We didn't get a response - try last time and atomically drop
10962306a36Sopenharmony_ci	 * the tag even if no response is matched.
11062306a36Sopenharmony_ci	 */
11162306a36Sopenharmony_ci	if (!skb)
11262306a36Sopenharmony_ci		skb = nfp_ccm_reply_drop_tag(ccm, app, tag);
11362306a36Sopenharmony_ci	if (err < 0) {
11462306a36Sopenharmony_ci		ccm_warn(app, "%s waiting for response to 0x%02x: %d\n",
11562306a36Sopenharmony_ci			 err == ERESTARTSYS ? "interrupted" : "error",
11662306a36Sopenharmony_ci			 type, err);
11762306a36Sopenharmony_ci		return ERR_PTR(err);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci	if (!skb) {
12062306a36Sopenharmony_ci		ccm_warn(app, "timeout waiting for response to 0x%02x\n", type);
12162306a36Sopenharmony_ci		return ERR_PTR(-ETIMEDOUT);
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return skb;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistruct sk_buff *
12862306a36Sopenharmony_cinfp_ccm_communicate(struct nfp_ccm *ccm, struct sk_buff *skb,
12962306a36Sopenharmony_ci		    enum nfp_ccm_type type, unsigned int reply_size)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct nfp_app *app = ccm->app;
13262306a36Sopenharmony_ci	struct nfp_ccm_hdr *hdr;
13362306a36Sopenharmony_ci	int reply_type, tag;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	nfp_ctrl_lock(app->ctrl);
13662306a36Sopenharmony_ci	tag = nfp_ccm_alloc_tag(ccm);
13762306a36Sopenharmony_ci	if (tag < 0) {
13862306a36Sopenharmony_ci		nfp_ctrl_unlock(app->ctrl);
13962306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
14062306a36Sopenharmony_ci		return ERR_PTR(tag);
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	hdr = (void *)skb->data;
14462306a36Sopenharmony_ci	hdr->ver = NFP_CCM_ABI_VERSION;
14562306a36Sopenharmony_ci	hdr->type = type;
14662306a36Sopenharmony_ci	hdr->tag = cpu_to_be16(tag);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	__nfp_app_ctrl_tx(app, skb);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	nfp_ctrl_unlock(app->ctrl);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	skb = nfp_ccm_wait_reply(ccm, app, type, tag);
15362306a36Sopenharmony_ci	if (IS_ERR(skb))
15462306a36Sopenharmony_ci		return skb;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	reply_type = nfp_ccm_get_type(skb);
15762306a36Sopenharmony_ci	if (reply_type != __NFP_CCM_REPLY(type)) {
15862306a36Sopenharmony_ci		ccm_warn(app, "cmsg drop - wrong type 0x%02x != 0x%02lx!\n",
15962306a36Sopenharmony_ci			 reply_type, __NFP_CCM_REPLY(type));
16062306a36Sopenharmony_ci		goto err_free;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci	/* 0 reply_size means caller will do the validation */
16362306a36Sopenharmony_ci	if (reply_size && skb->len != reply_size) {
16462306a36Sopenharmony_ci		ccm_warn(app, "cmsg drop - type 0x%02x wrong size %d != %d!\n",
16562306a36Sopenharmony_ci			 type, skb->len, reply_size);
16662306a36Sopenharmony_ci		goto err_free;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return skb;
17062306a36Sopenharmony_cierr_free:
17162306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
17262306a36Sopenharmony_ci	return ERR_PTR(-EIO);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_civoid nfp_ccm_rx(struct nfp_ccm *ccm, struct sk_buff *skb)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct nfp_app *app = ccm->app;
17862306a36Sopenharmony_ci	unsigned int tag;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (unlikely(skb->len < sizeof(struct nfp_ccm_hdr))) {
18162306a36Sopenharmony_ci		ccm_warn(app, "cmsg drop - too short %d!\n", skb->len);
18262306a36Sopenharmony_ci		goto err_free;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	nfp_ctrl_lock(app->ctrl);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	tag = nfp_ccm_get_tag(skb);
18862306a36Sopenharmony_ci	if (unlikely(!test_bit(tag, ccm->tag_allocator))) {
18962306a36Sopenharmony_ci		ccm_warn(app, "cmsg drop - no one is waiting for tag %u!\n",
19062306a36Sopenharmony_ci			 tag);
19162306a36Sopenharmony_ci		goto err_unlock;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	__skb_queue_tail(&ccm->replies, skb);
19562306a36Sopenharmony_ci	wake_up_interruptible_all(&ccm->wq);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	nfp_ctrl_unlock(app->ctrl);
19862306a36Sopenharmony_ci	return;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cierr_unlock:
20162306a36Sopenharmony_ci	nfp_ctrl_unlock(app->ctrl);
20262306a36Sopenharmony_cierr_free:
20362306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ciint nfp_ccm_init(struct nfp_ccm *ccm, struct nfp_app *app)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	ccm->app = app;
20962306a36Sopenharmony_ci	skb_queue_head_init(&ccm->replies);
21062306a36Sopenharmony_ci	init_waitqueue_head(&ccm->wq);
21162306a36Sopenharmony_ci	return 0;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_civoid nfp_ccm_clean(struct nfp_ccm *ccm)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	WARN_ON(!skb_queue_empty(&ccm->replies));
21762306a36Sopenharmony_ci}
218