1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2016-2019 Netronome Systems, Inc. */
4 #include <linux/bitops.h>
10 #define NFP_CCM_TYPE_REPLY_BIT 7
11 #define __NFP_CCM_REPLY(req) (BIT(NFP_CCM_TYPE_REPLY_BIT) | (req))
13 #define ccm_warn(app, msg...) nn_dp_warn(&(app)->ctrl->dp, msg)
15 #define NFP_CCM_TAG_ALLOC_SPAN (U16_MAX / 4)
17 static bool nfp_ccm_all_tags_busy(struct nfp_ccm
*ccm
)
21 used_tags
= ccm
->tag_alloc_next
- ccm
->tag_alloc_last
;
23 return used_tags
> NFP_CCM_TAG_ALLOC_SPAN
;
26 static int nfp_ccm_alloc_tag(struct nfp_ccm
*ccm
)
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.
32 if (unlikely(nfp_ccm_all_tags_busy(ccm
))) {
33 ccm_warn(ccm
->app
, "all FW request contexts busy!\n");
37 WARN_ON(__test_and_set_bit(ccm
->tag_alloc_next
, ccm
->tag_allocator
));
38 return ccm
->tag_alloc_next
++;
41 static void nfp_ccm_free_tag(struct nfp_ccm
*ccm
, u16 tag
)
43 WARN_ON(!__test_and_clear_bit(tag
, ccm
->tag_allocator
));
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
++;
50 static struct sk_buff
*__nfp_ccm_reply(struct nfp_ccm
*ccm
, u16 tag
)
55 skb_queue_walk(&ccm
->replies
, skb
) {
56 msg_tag
= nfp_ccm_get_tag(skb
);
58 nfp_ccm_free_tag(ccm
, tag
);
59 __skb_unlink(skb
, &ccm
->replies
);
67 static struct sk_buff
*
68 nfp_ccm_reply(struct nfp_ccm
*ccm
, struct nfp_app
*app
, u16 tag
)
72 nfp_ctrl_lock(app
->ctrl
);
73 skb
= __nfp_ccm_reply(ccm
, tag
);
74 nfp_ctrl_unlock(app
->ctrl
);
79 static struct sk_buff
*
80 nfp_ccm_reply_drop_tag(struct nfp_ccm
*ccm
, struct nfp_app
*app
, u16 tag
)
84 nfp_ctrl_lock(app
->ctrl
);
85 skb
= __nfp_ccm_reply(ccm
, tag
);
87 nfp_ccm_free_tag(ccm
, tag
);
88 nfp_ctrl_unlock(app
->ctrl
);
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
)
100 for (i
= 0; i
< 50; i
++) {
102 skb
= nfp_ccm_reply(ccm
, app
, tag
);
107 err
= wait_event_interruptible_timeout(ccm
->wq
,
108 skb
= nfp_ccm_reply(ccm
, app
,
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.
115 skb
= nfp_ccm_reply_drop_tag(ccm
, app
, tag
);
117 ccm_warn(app
, "%s waiting for response to 0x%02x: %d\n",
118 err
== ERESTARTSYS
? "interrupted" : "error",
123 ccm_warn(app
, "timeout waiting for response to 0x%02x\n", type
);
124 return ERR_PTR(-ETIMEDOUT
);
131 nfp_ccm_communicate(struct nfp_ccm
*ccm
, struct sk_buff
*skb
,
132 enum nfp_ccm_type type
, unsigned int reply_size
)
134 struct nfp_app
*app
= ccm
->app
;
135 struct nfp_ccm_hdr
*hdr
;
138 nfp_ctrl_lock(app
->ctrl
);
139 tag
= nfp_ccm_alloc_tag(ccm
);
141 nfp_ctrl_unlock(app
->ctrl
);
142 dev_kfree_skb_any(skb
);
146 hdr
= (void *)skb
->data
;
147 hdr
->ver
= NFP_CCM_ABI_VERSION
;
149 hdr
->tag
= cpu_to_be16(tag
);
151 __nfp_app_ctrl_tx(app
, skb
);
153 nfp_ctrl_unlock(app
->ctrl
);
155 skb
= nfp_ccm_wait_reply(ccm
, app
, type
, tag
);
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
));
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
);
174 dev_kfree_skb_any(skb
);
175 return ERR_PTR(-EIO
);
178 void nfp_ccm_rx(struct nfp_ccm
*ccm
, struct sk_buff
*skb
)
180 struct nfp_app
*app
= ccm
->app
;
183 if (unlikely(skb
->len
< sizeof(struct nfp_ccm_hdr
))) {
184 ccm_warn(app
, "cmsg drop - too short %d!\n", skb
->len
);
188 nfp_ctrl_lock(app
->ctrl
);
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",
197 __skb_queue_tail(&ccm
->replies
, skb
);
198 wake_up_interruptible_all(&ccm
->wq
);
200 nfp_ctrl_unlock(app
->ctrl
);
204 nfp_ctrl_unlock(app
->ctrl
);
206 dev_kfree_skb_any(skb
);
209 int nfp_ccm_init(struct nfp_ccm
*ccm
, struct nfp_app
*app
)
212 skb_queue_head_init(&ccm
->replies
);
213 init_waitqueue_head(&ccm
->wq
);
217 void nfp_ccm_clean(struct nfp_ccm
*ccm
)
219 WARN_ON(!skb_queue_empty(&ccm
->replies
));