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
14static 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
23static 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
38static 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
47static 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, list: &ccm->replies);
57 return skb;
58 }
59 }
60
61 return NULL;
62}
63
64static struct sk_buff *
65nfp_ccm_reply(struct nfp_ccm *ccm, struct nfp_app *app, u16 tag)
66{
67 struct sk_buff *skb;
68
69 nfp_ctrl_lock(nn: app->ctrl);
70 skb = __nfp_ccm_reply(ccm, tag);
71 nfp_ctrl_unlock(nn: app->ctrl);
72
73 return skb;
74}
75
76static struct sk_buff *
77nfp_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(nn: app->ctrl);
82 skb = __nfp_ccm_reply(ccm, tag);
83 if (!skb)
84 nfp_ccm_free_tag(ccm, tag);
85 nfp_ctrl_unlock(nn: app->ctrl);
86
87 return skb;
88}
89
90static struct sk_buff *
91nfp_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(error: err);
118 }
119 if (!skb) {
120 ccm_warn(app, "timeout waiting for response to 0x%02x\n", type);
121 return ERR_PTR(error: -ETIMEDOUT);
122 }
123
124 return skb;
125}
126
127struct sk_buff *
128nfp_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(nn: app->ctrl);
136 tag = nfp_ccm_alloc_tag(ccm);
137 if (tag < 0) {
138 nfp_ctrl_unlock(nn: app->ctrl);
139 dev_kfree_skb_any(skb);
140 return ERR_PTR(error: 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(nn: app->ctrl);
151
152 skb = nfp_ccm_wait_reply(ccm, app, type, tag);
153 if (IS_ERR(ptr: 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;
170err_free:
171 dev_kfree_skb_any(skb);
172 return ERR_PTR(error: -EIO);
173}
174
175void 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(nn: 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(list: &ccm->replies, newsk: skb);
195 wake_up_interruptible_all(&ccm->wq);
196
197 nfp_ctrl_unlock(nn: app->ctrl);
198 return;
199
200err_unlock:
201 nfp_ctrl_unlock(nn: app->ctrl);
202err_free:
203 dev_kfree_skb_any(skb);
204}
205
206int nfp_ccm_init(struct nfp_ccm *ccm, struct nfp_app *app)
207{
208 ccm->app = app;
209 skb_queue_head_init(list: &ccm->replies);
210 init_waitqueue_head(&ccm->wq);
211 return 0;
212}
213
214void nfp_ccm_clean(struct nfp_ccm *ccm)
215{
216 WARN_ON(!skb_queue_empty(&ccm->replies));
217}
218

source code of linux/drivers/net/ethernet/netronome/nfp/ccm.c