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 
nfp_ccm_all_tags_busy(struct nfp_ccm * ccm)14 static 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 
nfp_ccm_alloc_tag(struct nfp_ccm * ccm)23 static int nfp_ccm_alloc_tag(struct nfp_ccm *ccm)
24 {
25 	/* CCM is for FW communication which is request-reply.  To make sure
26 	 * we don't reuse the message ID too early after timeout - limit the
27 	 * 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 
nfp_ccm_free_tag(struct nfp_ccm * ccm,u16 tag)38 static 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 
__nfp_ccm_reply(struct nfp_ccm * ccm,u16 tag)47 static 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 
64 static struct sk_buff *
nfp_ccm_reply(struct nfp_ccm * ccm,struct nfp_app * app,u16 tag)65 nfp_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 
76 static struct sk_buff *
nfp_ccm_reply_drop_tag(struct nfp_ccm * ccm,struct nfp_app * app,u16 tag)77 nfp_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 
90 static struct sk_buff *
nfp_ccm_wait_reply(struct nfp_ccm * ccm,struct nfp_app * app,enum nfp_ccm_type type,int tag)91 nfp_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 drop
109 	 * 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 
127 struct sk_buff *
nfp_ccm_communicate(struct nfp_ccm * ccm,struct sk_buff * skb,enum nfp_ccm_type type,unsigned int reply_size)128 nfp_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;
170 err_free:
171 	dev_kfree_skb_any(skb);
172 	return ERR_PTR(-EIO);
173 }
174 
nfp_ccm_rx(struct nfp_ccm * ccm,struct sk_buff * skb)175 void 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 
200 err_unlock:
201 	nfp_ctrl_unlock(app->ctrl);
202 err_free:
203 	dev_kfree_skb_any(skb);
204 }
205 
nfp_ccm_init(struct nfp_ccm * ccm,struct nfp_app * app)206 int 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 
nfp_ccm_clean(struct nfp_ccm * ccm)214 void nfp_ccm_clean(struct nfp_ccm *ccm)
215 {
216 	WARN_ON(!skb_queue_empty(&ccm->replies));
217 }
218