Skip to content

Commit d48ae23

Browse files
Jakub Kicinskiborkmann
authored andcommitted
nfp: bpf: add basic control channel communication
For map support we will need to send and receive control messages. Add basic support for sending a message to FW, and waiting for a reply. Control messages are tagged with a 16 bit ID. Add a simple ID allocator and make sure we don't allow too many messages in flight, to avoid request <> reply mismatches. Signed-off-by: Jakub Kicinski <[email protected]> Reviewed-by: Quentin Monnet <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]>
1 parent 4da98ee commit d48ae23

File tree

8 files changed

+317
-0
lines changed

8 files changed

+317
-0
lines changed

drivers/net/ethernet/netronome/nfp/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ endif
4444

4545
ifeq ($(CONFIG_BPF_SYSCALL),y)
4646
nfp-objs += \
47+
bpf/cmsg.o \
4748
bpf/main.o \
4849
bpf/offload.o \
4950
bpf/verifier.o \
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
/*
2+
* Copyright (C) 2017 Netronome Systems, Inc.
3+
*
4+
* This software is dual licensed under the GNU General License Version 2,
5+
* June 1991 as shown in the file COPYING in the top-level directory of this
6+
* source tree or the BSD 2-Clause License provided below. You have the
7+
* option to license this software under the complete terms of either license.
8+
*
9+
* The BSD 2-Clause License:
10+
*
11+
* Redistribution and use in source and binary forms, with or
12+
* without modification, are permitted provided that the following
13+
* conditions are met:
14+
*
15+
* 1. Redistributions of source code must retain the above
16+
* copyright notice, this list of conditions and the following
17+
* disclaimer.
18+
*
19+
* 2. Redistributions in binary form must reproduce the above
20+
* copyright notice, this list of conditions and the following
21+
* disclaimer in the documentation and/or other materials
22+
* provided with the distribution.
23+
*
24+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28+
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29+
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31+
* SOFTWARE.
32+
*/
33+
34+
#include <linux/bitops.h>
35+
#include <linux/bug.h>
36+
#include <linux/jiffies.h>
37+
#include <linux/skbuff.h>
38+
#include <linux/wait.h>
39+
40+
#include "../nfp_app.h"
41+
#include "../nfp_net.h"
42+
#include "fw.h"
43+
#include "main.h"
44+
45+
#define cmsg_warn(bpf, msg...) nn_dp_warn(&(bpf)->app->ctrl->dp, msg)
46+
47+
#define NFP_BPF_TAG_ALLOC_SPAN (U16_MAX / 4)
48+
49+
static bool nfp_bpf_all_tags_busy(struct nfp_app_bpf *bpf)
50+
{
51+
u16 used_tags;
52+
53+
used_tags = bpf->tag_alloc_next - bpf->tag_alloc_last;
54+
55+
return used_tags > NFP_BPF_TAG_ALLOC_SPAN;
56+
}
57+
58+
static int nfp_bpf_alloc_tag(struct nfp_app_bpf *bpf)
59+
{
60+
/* All FW communication for BPF is request-reply. To make sure we
61+
* don't reuse the message ID too early after timeout - limit the
62+
* number of requests in flight.
63+
*/
64+
if (nfp_bpf_all_tags_busy(bpf)) {
65+
cmsg_warn(bpf, "all FW request contexts busy!\n");
66+
return -EAGAIN;
67+
}
68+
69+
WARN_ON(__test_and_set_bit(bpf->tag_alloc_next, bpf->tag_allocator));
70+
return bpf->tag_alloc_next++;
71+
}
72+
73+
static void nfp_bpf_free_tag(struct nfp_app_bpf *bpf, u16 tag)
74+
{
75+
WARN_ON(!__test_and_clear_bit(tag, bpf->tag_allocator));
76+
77+
while (!test_bit(bpf->tag_alloc_last, bpf->tag_allocator) &&
78+
bpf->tag_alloc_last != bpf->tag_alloc_next)
79+
bpf->tag_alloc_last++;
80+
}
81+
82+
static unsigned int nfp_bpf_cmsg_get_tag(struct sk_buff *skb)
83+
{
84+
struct cmsg_hdr *hdr;
85+
86+
hdr = (struct cmsg_hdr *)skb->data;
87+
88+
return be16_to_cpu(hdr->tag);
89+
}
90+
91+
static struct sk_buff *__nfp_bpf_reply(struct nfp_app_bpf *bpf, u16 tag)
92+
{
93+
unsigned int msg_tag;
94+
struct sk_buff *skb;
95+
96+
skb_queue_walk(&bpf->cmsg_replies, skb) {
97+
msg_tag = nfp_bpf_cmsg_get_tag(skb);
98+
if (msg_tag == tag) {
99+
nfp_bpf_free_tag(bpf, tag);
100+
__skb_unlink(skb, &bpf->cmsg_replies);
101+
return skb;
102+
}
103+
}
104+
105+
return NULL;
106+
}
107+
108+
static struct sk_buff *nfp_bpf_reply(struct nfp_app_bpf *bpf, u16 tag)
109+
{
110+
struct sk_buff *skb;
111+
112+
nfp_ctrl_lock(bpf->app->ctrl);
113+
skb = __nfp_bpf_reply(bpf, tag);
114+
nfp_ctrl_unlock(bpf->app->ctrl);
115+
116+
return skb;
117+
}
118+
119+
static struct sk_buff *nfp_bpf_reply_drop_tag(struct nfp_app_bpf *bpf, u16 tag)
120+
{
121+
struct sk_buff *skb;
122+
123+
nfp_ctrl_lock(bpf->app->ctrl);
124+
skb = __nfp_bpf_reply(bpf, tag);
125+
if (!skb)
126+
nfp_bpf_free_tag(bpf, tag);
127+
nfp_ctrl_unlock(bpf->app->ctrl);
128+
129+
return skb;
130+
}
131+
132+
static struct sk_buff *
133+
nfp_bpf_cmsg_wait_reply(struct nfp_app_bpf *bpf, enum nfp_bpf_cmsg_type type,
134+
int tag)
135+
{
136+
struct sk_buff *skb;
137+
int err;
138+
139+
err = wait_event_interruptible_timeout(bpf->cmsg_wq,
140+
skb = nfp_bpf_reply(bpf, tag),
141+
msecs_to_jiffies(5000));
142+
/* We didn't get a response - try last time and atomically drop
143+
* the tag even if no response is matched.
144+
*/
145+
if (!skb)
146+
skb = nfp_bpf_reply_drop_tag(bpf, tag);
147+
if (err < 0) {
148+
cmsg_warn(bpf, "%s waiting for response to 0x%02x: %d\n",
149+
err == ERESTARTSYS ? "interrupted" : "error",
150+
type, err);
151+
return ERR_PTR(err);
152+
}
153+
if (!skb) {
154+
cmsg_warn(bpf, "timeout waiting for response to 0x%02x\n",
155+
type);
156+
return ERR_PTR(-ETIMEDOUT);
157+
}
158+
159+
return skb;
160+
}
161+
162+
struct sk_buff *
163+
nfp_bpf_cmsg_communicate(struct nfp_app_bpf *bpf, struct sk_buff *skb,
164+
enum nfp_bpf_cmsg_type type, unsigned int reply_size)
165+
{
166+
struct cmsg_hdr *hdr;
167+
int tag;
168+
169+
nfp_ctrl_lock(bpf->app->ctrl);
170+
tag = nfp_bpf_alloc_tag(bpf);
171+
if (tag < 0) {
172+
nfp_ctrl_unlock(bpf->app->ctrl);
173+
dev_kfree_skb_any(skb);
174+
return ERR_PTR(tag);
175+
}
176+
177+
hdr = (void *)skb->data;
178+
hdr->ver = CMSG_MAP_ABI_VERSION;
179+
hdr->type = type;
180+
hdr->tag = cpu_to_be16(tag);
181+
182+
__nfp_app_ctrl_tx(bpf->app, skb);
183+
184+
nfp_ctrl_unlock(bpf->app->ctrl);
185+
186+
skb = nfp_bpf_cmsg_wait_reply(bpf, type, tag);
187+
if (IS_ERR(skb))
188+
return skb;
189+
190+
hdr = (struct cmsg_hdr *)skb->data;
191+
/* 0 reply_size means caller will do the validation */
192+
if (reply_size && skb->len != reply_size) {
193+
cmsg_warn(bpf, "cmsg drop - wrong size %d != %d!\n",
194+
skb->len, reply_size);
195+
goto err_free;
196+
}
197+
if (hdr->type != __CMSG_REPLY(type)) {
198+
cmsg_warn(bpf, "cmsg drop - wrong type 0x%02x != 0x%02lx!\n",
199+
hdr->type, __CMSG_REPLY(type));
200+
goto err_free;
201+
}
202+
203+
return skb;
204+
err_free:
205+
dev_kfree_skb_any(skb);
206+
return ERR_PTR(-EIO);
207+
}
208+
209+
void nfp_bpf_ctrl_msg_rx(struct nfp_app *app, struct sk_buff *skb)
210+
{
211+
struct nfp_app_bpf *bpf = app->priv;
212+
unsigned int tag;
213+
214+
if (unlikely(skb->len < sizeof(struct cmsg_reply_map_simple))) {
215+
cmsg_warn(bpf, "cmsg drop - too short %d!\n", skb->len);
216+
goto err_free;
217+
}
218+
219+
nfp_ctrl_lock(bpf->app->ctrl);
220+
221+
tag = nfp_bpf_cmsg_get_tag(skb);
222+
if (unlikely(!test_bit(tag, bpf->tag_allocator))) {
223+
cmsg_warn(bpf, "cmsg drop - no one is waiting for tag %u!\n",
224+
tag);
225+
goto err_unlock;
226+
}
227+
228+
__skb_queue_tail(&bpf->cmsg_replies, skb);
229+
wake_up_interruptible_all(&bpf->cmsg_wq);
230+
231+
nfp_ctrl_unlock(bpf->app->ctrl);
232+
233+
return;
234+
err_unlock:
235+
nfp_ctrl_unlock(bpf->app->ctrl);
236+
err_free:
237+
dev_kfree_skb_any(skb);
238+
}

drivers/net/ethernet/netronome/nfp/bpf/fw.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,26 @@ struct nfp_bpf_cap_tlv_adjust_head {
5151

5252
#define NFP_BPF_ADJUST_HEAD_NO_META BIT(0)
5353

54+
/*
55+
* Types defined for map related control messages
56+
*/
57+
#define CMSG_MAP_ABI_VERSION 1
58+
59+
enum nfp_bpf_cmsg_type {
60+
__CMSG_TYPE_MAP_MAX,
61+
};
62+
63+
#define CMSG_TYPE_MAP_REPLY_BIT 7
64+
#define __CMSG_REPLY(req) (BIT(CMSG_TYPE_MAP_REPLY_BIT) | (req))
65+
66+
struct cmsg_hdr {
67+
u8 type;
68+
u8 ver;
69+
__be16 tag;
70+
};
71+
72+
struct cmsg_reply_map_simple {
73+
struct cmsg_hdr hdr;
74+
__be32 rc;
75+
};
5476
#endif

drivers/net/ethernet/netronome/nfp/bpf/main.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ static int nfp_bpf_init(struct nfp_app *app)
313313
bpf->app = app;
314314
app->priv = bpf;
315315

316+
skb_queue_head_init(&bpf->cmsg_replies);
317+
init_waitqueue_head(&bpf->cmsg_wq);
316318
INIT_LIST_HEAD(&bpf->map_list);
317319

318320
err = nfp_bpf_parse_capabilities(app);
@@ -330,6 +332,7 @@ static void nfp_bpf_clean(struct nfp_app *app)
330332
{
331333
struct nfp_app_bpf *bpf = app->priv;
332334

335+
WARN_ON(!skb_queue_empty(&bpf->cmsg_replies));
333336
WARN_ON(!list_empty(&bpf->map_list));
334337
kfree(bpf);
335338
}
@@ -348,6 +351,8 @@ const struct nfp_app_type app_bpf = {
348351
.vnic_alloc = nfp_bpf_vnic_alloc,
349352
.vnic_free = nfp_bpf_vnic_free,
350353

354+
.ctrl_msg_rx = nfp_bpf_ctrl_msg_rx,
355+
351356
.setup_tc = nfp_bpf_setup_tc,
352357
.tc_busy = nfp_bpf_tc_busy,
353358
.bpf = nfp_ndo_bpf,

drivers/net/ethernet/netronome/nfp/bpf/main.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,14 @@
3737
#include <linux/bitfield.h>
3838
#include <linux/bpf.h>
3939
#include <linux/bpf_verifier.h>
40+
#include <linux/kernel.h>
4041
#include <linux/list.h>
42+
#include <linux/skbuff.h>
4143
#include <linux/types.h>
44+
#include <linux/wait.h>
4245

4346
#include "../nfp_asm.h"
47+
#include "fw.h"
4448

4549
/* For relocation logic use up-most byte of branch instruction as scratch
4650
* area. Remember to clear this before sending instructions to HW!
@@ -93,6 +97,13 @@ enum pkt_vec {
9397
* struct nfp_app_bpf - bpf app priv structure
9498
* @app: backpointer to the app
9599
*
100+
* @tag_allocator: bitmap of control message tags in use
101+
* @tag_alloc_next: next tag bit to allocate
102+
* @tag_alloc_last: next tag bit to be freed
103+
*
104+
* @cmsg_replies: received cmsg replies waiting to be consumed
105+
* @cmsg_wq: work queue for waiting for cmsg replies
106+
*
96107
* @map_list: list of offloaded maps
97108
*
98109
* @adjust_head: adjust head capability
@@ -105,6 +116,13 @@ enum pkt_vec {
105116
struct nfp_app_bpf {
106117
struct nfp_app *app;
107118

119+
DECLARE_BITMAP(tag_allocator, U16_MAX + 1);
120+
u16 tag_alloc_next;
121+
u16 tag_alloc_last;
122+
123+
struct sk_buff_head cmsg_replies;
124+
struct wait_queue_head cmsg_wq;
125+
108126
struct list_head map_list;
109127

110128
struct nfp_bpf_cap_adjust_head {
@@ -284,4 +302,9 @@ nfp_bpf_goto_meta(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
284302
unsigned int insn_idx, unsigned int n_insns);
285303

286304
void *nfp_bpf_relo_for_vnic(struct nfp_prog *nfp_prog, struct nfp_bpf_vnic *bv);
305+
306+
struct sk_buff *
307+
nfp_bpf_cmsg_communicate(struct nfp_app_bpf *bpf, struct sk_buff *skb,
308+
enum nfp_bpf_cmsg_type type, unsigned int reply_size);
309+
void nfp_bpf_ctrl_msg_rx(struct nfp_app *app, struct sk_buff *skb);
287310
#endif

drivers/net/ethernet/netronome/nfp/nfp_app.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ struct nfp_app {
165165
void *priv;
166166
};
167167

168+
bool __nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb);
168169
bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb);
169170

170171
static inline int nfp_app_init(struct nfp_app *app)
@@ -326,6 +327,14 @@ static inline int nfp_app_xdp_offload(struct nfp_app *app, struct nfp_net *nn,
326327
return app->type->xdp_offload(app, nn, prog);
327328
}
328329

330+
static inline bool __nfp_app_ctrl_tx(struct nfp_app *app, struct sk_buff *skb)
331+
{
332+
trace_devlink_hwmsg(priv_to_devlink(app->pf), false, 0,
333+
skb->data, skb->len);
334+
335+
return __nfp_ctrl_tx(app->ctrl, skb);
336+
}
337+
329338
static inline bool nfp_app_ctrl_tx(struct nfp_app *app, struct sk_buff *skb)
330339
{
331340
trace_devlink_hwmsg(priv_to_devlink(app->pf), false, 0,

drivers/net/ethernet/netronome/nfp/nfp_net.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,18 @@ static inline const char *nfp_net_name(struct nfp_net *nn)
839839
return nn->dp.netdev ? nn->dp.netdev->name : "ctrl";
840840
}
841841

842+
static inline void nfp_ctrl_lock(struct nfp_net *nn)
843+
__acquires(&nn->r_vecs[0].lock)
844+
{
845+
spin_lock_bh(&nn->r_vecs[0].lock);
846+
}
847+
848+
static inline void nfp_ctrl_unlock(struct nfp_net *nn)
849+
__releases(&nn->r_vecs[0].lock)
850+
{
851+
spin_unlock_bh(&nn->r_vecs[0].lock);
852+
}
853+
842854
/* Globals */
843855
extern const char nfp_driver_version[];
844856

0 commit comments

Comments
 (0)