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