|
@@ -0,0 +1,220 @@
|
|
|
|
+/*
|
|
|
|
+ * Copyright (C) 2016 Netronome Systems, Inc.
|
|
|
|
+ *
|
|
|
|
+ * This software is dual licensed under the GNU General License Version 2,
|
|
|
|
+ * June 1991 as shown in the file COPYING in the top-level directory of this
|
|
|
|
+ * source tree or the BSD 2-Clause License provided below. You have the
|
|
|
|
+ * option to license this software under the complete terms of either license.
|
|
|
|
+ *
|
|
|
|
+ * The BSD 2-Clause License:
|
|
|
|
+ *
|
|
|
|
+ * Redistribution and use in source and binary forms, with or
|
|
|
|
+ * without modification, are permitted provided that the following
|
|
|
|
+ * conditions are met:
|
|
|
|
+ *
|
|
|
|
+ * 1. Redistributions of source code must retain the above
|
|
|
|
+ * copyright notice, this list of conditions and the following
|
|
|
|
+ * disclaimer.
|
|
|
|
+ *
|
|
|
|
+ * 2. Redistributions in binary form must reproduce the above
|
|
|
|
+ * copyright notice, this list of conditions and the following
|
|
|
|
+ * disclaimer in the documentation and/or other materials
|
|
|
|
+ * provided with the distribution.
|
|
|
|
+ *
|
|
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
|
|
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
|
|
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
+ * SOFTWARE.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * nfp_net_offload.c
|
|
|
|
+ * Netronome network device driver: TC offload functions for PF and VF
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include <linux/kernel.h>
|
|
|
|
+#include <linux/netdevice.h>
|
|
|
|
+#include <linux/pci.h>
|
|
|
|
+#include <linux/jiffies.h>
|
|
|
|
+#include <linux/timer.h>
|
|
|
|
+#include <linux/list.h>
|
|
|
|
+
|
|
|
|
+#include <net/pkt_cls.h>
|
|
|
|
+#include <net/tc_act/tc_gact.h>
|
|
|
|
+#include <net/tc_act/tc_mirred.h>
|
|
|
|
+
|
|
|
|
+#include "nfp_bpf.h"
|
|
|
|
+#include "nfp_net_ctrl.h"
|
|
|
|
+#include "nfp_net.h"
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+nfp_net_bpf_get_act(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf)
|
|
|
|
+{
|
|
|
|
+ const struct tc_action *a;
|
|
|
|
+ LIST_HEAD(actions);
|
|
|
|
+
|
|
|
|
+ /* TC direct action */
|
|
|
|
+ if (cls_bpf->exts_integrated)
|
|
|
|
+ return -ENOTSUPP;
|
|
|
|
+
|
|
|
|
+ /* TC legacy mode */
|
|
|
|
+ if (!tc_single_action(cls_bpf->exts))
|
|
|
|
+ return -ENOTSUPP;
|
|
|
|
+
|
|
|
|
+ tcf_exts_to_list(cls_bpf->exts, &actions);
|
|
|
|
+ list_for_each_entry(a, &actions, list) {
|
|
|
|
+ if (is_tcf_gact_shot(a))
|
|
|
|
+ return NN_ACT_TC_DROP;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return -ENOTSUPP;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+nfp_net_bpf_offload_prepare(struct nfp_net *nn,
|
|
|
|
+ struct tc_cls_bpf_offload *cls_bpf,
|
|
|
|
+ struct nfp_bpf_result *res,
|
|
|
|
+ void **code, dma_addr_t *dma_addr, u16 max_instr)
|
|
|
|
+{
|
|
|
|
+ unsigned int code_sz = max_instr * sizeof(u64);
|
|
|
|
+ enum nfp_bpf_action_type act;
|
|
|
|
+ u16 start_off, done_off;
|
|
|
|
+ unsigned int max_mtu;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ ret = nfp_net_bpf_get_act(nn, cls_bpf);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ return ret;
|
|
|
|
+ act = ret;
|
|
|
|
+
|
|
|
|
+ max_mtu = nn_readb(nn, NFP_NET_CFG_BPF_INL_MTU) * 64 - 32;
|
|
|
|
+ if (max_mtu < nn->netdev->mtu) {
|
|
|
|
+ nn_info(nn, "BPF offload not supported with MTU larger than HW packet split boundary\n");
|
|
|
|
+ return -ENOTSUPP;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ start_off = nn_readw(nn, NFP_NET_CFG_BPF_START);
|
|
|
|
+ done_off = nn_readw(nn, NFP_NET_CFG_BPF_DONE);
|
|
|
|
+
|
|
|
|
+ *code = dma_zalloc_coherent(&nn->pdev->dev, code_sz, dma_addr,
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
+ if (!*code)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ ret = nfp_bpf_jit(cls_bpf->prog, *code, act, start_off, done_off,
|
|
|
|
+ max_instr, res);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ dma_free_coherent(&nn->pdev->dev, code_sz, *code, *dma_addr);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+nfp_net_bpf_load_and_start(struct nfp_net *nn, u32 tc_flags,
|
|
|
|
+ void *code, dma_addr_t dma_addr,
|
|
|
|
+ unsigned int code_sz, unsigned int n_instr,
|
|
|
|
+ bool dense_mode)
|
|
|
|
+{
|
|
|
|
+ u64 bpf_addr = dma_addr;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ nn->bpf_offload_skip_sw = !!(tc_flags & TCA_CLS_FLAGS_SKIP_SW);
|
|
|
|
+
|
|
|
|
+ if (dense_mode)
|
|
|
|
+ bpf_addr |= NFP_NET_CFG_BPF_CFG_8CTX;
|
|
|
|
+
|
|
|
|
+ nn_writew(nn, NFP_NET_CFG_BPF_SIZE, n_instr);
|
|
|
|
+ nn_writeq(nn, NFP_NET_CFG_BPF_ADDR, bpf_addr);
|
|
|
|
+
|
|
|
|
+ /* Load up the JITed code */
|
|
|
|
+ err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_BPF);
|
|
|
|
+ if (err)
|
|
|
|
+ nn_err(nn, "FW command error while loading BPF: %d\n", err);
|
|
|
|
+
|
|
|
|
+ /* Enable passing packets through BPF function */
|
|
|
|
+ nn->ctrl |= NFP_NET_CFG_CTRL_BPF;
|
|
|
|
+ nn_writel(nn, NFP_NET_CFG_CTRL, nn->ctrl);
|
|
|
|
+ err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN);
|
|
|
|
+ if (err)
|
|
|
|
+ nn_err(nn, "FW command error while enabling BPF: %d\n", err);
|
|
|
|
+
|
|
|
|
+ dma_free_coherent(&nn->pdev->dev, code_sz, code, dma_addr);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nfp_net_bpf_stop(struct nfp_net *nn)
|
|
|
|
+{
|
|
|
|
+ if (!(nn->ctrl & NFP_NET_CFG_CTRL_BPF))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ nn->ctrl &= ~NFP_NET_CFG_CTRL_BPF;
|
|
|
|
+ nn_writel(nn, NFP_NET_CFG_CTRL, nn->ctrl);
|
|
|
|
+
|
|
|
|
+ nn->bpf_offload_skip_sw = 0;
|
|
|
|
+
|
|
|
|
+ return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+nfp_net_bpf_offload(struct nfp_net *nn, u32 handle, __be16 proto,
|
|
|
|
+ struct tc_cls_bpf_offload *cls_bpf)
|
|
|
|
+{
|
|
|
|
+ struct nfp_bpf_result res;
|
|
|
|
+ dma_addr_t dma_addr;
|
|
|
|
+ u16 max_instr;
|
|
|
|
+ void *code;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ max_instr = nn_readw(nn, NFP_NET_CFG_BPF_MAX_LEN);
|
|
|
|
+
|
|
|
|
+ switch (cls_bpf->command) {
|
|
|
|
+ case TC_CLSBPF_REPLACE:
|
|
|
|
+ /* There is nothing stopping us from implementing seamless
|
|
|
|
+ * replace but the simple method of loading I adopted in
|
|
|
|
+ * the firmware does not handle atomic replace (i.e. we have to
|
|
|
|
+ * stop the BPF offload and re-enable it). Leaking-in a few
|
|
|
|
+ * frames which didn't have BPF applied in the hardware should
|
|
|
|
+ * be fine if software fallback is available, though.
|
|
|
|
+ */
|
|
|
|
+ if (nn->bpf_offload_skip_sw)
|
|
|
|
+ return -EBUSY;
|
|
|
|
+
|
|
|
|
+ err = nfp_net_bpf_offload_prepare(nn, cls_bpf, &res, &code,
|
|
|
|
+ &dma_addr, max_instr);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ nfp_net_bpf_stop(nn);
|
|
|
|
+ nfp_net_bpf_load_and_start(nn, cls_bpf->gen_flags, code,
|
|
|
|
+ dma_addr, max_instr * sizeof(u64),
|
|
|
|
+ res.n_instr, res.dense_mode);
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ case TC_CLSBPF_ADD:
|
|
|
|
+ if (nn->ctrl & NFP_NET_CFG_CTRL_BPF)
|
|
|
|
+ return -EBUSY;
|
|
|
|
+
|
|
|
|
+ err = nfp_net_bpf_offload_prepare(nn, cls_bpf, &res, &code,
|
|
|
|
+ &dma_addr, max_instr);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ nfp_net_bpf_load_and_start(nn, cls_bpf->gen_flags, code,
|
|
|
|
+ dma_addr, max_instr * sizeof(u64),
|
|
|
|
+ res.n_instr, res.dense_mode);
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ case TC_CLSBPF_DESTROY:
|
|
|
|
+ return nfp_net_bpf_stop(nn);
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ return -ENOTSUPP;
|
|
|
|
+ }
|
|
|
|
+}
|