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