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