net, xdp: Introduce xdp_prepare_buff utility routine
[linux-2.6-microblaze.git] / drivers / net / hyperv / netvsc_bpf.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2019, Microsoft Corporation.
3  *
4  * Author:
5  *   Haiyang Zhang <haiyangz@microsoft.com>
6  */
7
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9
10 #include <linux/netdevice.h>
11 #include <linux/etherdevice.h>
12 #include <linux/ethtool.h>
13 #include <linux/bpf.h>
14 #include <linux/bpf_trace.h>
15 #include <linux/kernel.h>
16 #include <net/xdp.h>
17
18 #include <linux/mutex.h>
19 #include <linux/rtnetlink.h>
20
21 #include "hyperv_net.h"
22
23 u32 netvsc_run_xdp(struct net_device *ndev, struct netvsc_channel *nvchan,
24                    struct xdp_buff *xdp)
25 {
26         void *data = nvchan->rsc.data[0];
27         u32 len = nvchan->rsc.len[0];
28         struct page *page = NULL;
29         struct bpf_prog *prog;
30         u32 act = XDP_PASS;
31
32         xdp->data_hard_start = NULL;
33
34         rcu_read_lock();
35         prog = rcu_dereference(nvchan->bpf_prog);
36
37         if (!prog)
38                 goto out;
39
40         /* allocate page buffer for data */
41         page = alloc_page(GFP_ATOMIC);
42         if (!page) {
43                 act = XDP_DROP;
44                 goto out;
45         }
46
47         xdp_init_buff(xdp, PAGE_SIZE, &nvchan->xdp_rxq);
48         xdp_prepare_buff(xdp, page_address(page), NETVSC_XDP_HDRM, len, false);
49
50         memcpy(xdp->data, data, len);
51
52         act = bpf_prog_run_xdp(prog, xdp);
53
54         switch (act) {
55         case XDP_PASS:
56         case XDP_TX:
57         case XDP_DROP:
58                 break;
59
60         case XDP_ABORTED:
61                 trace_xdp_exception(ndev, prog, act);
62                 break;
63
64         default:
65                 bpf_warn_invalid_xdp_action(act);
66         }
67
68 out:
69         rcu_read_unlock();
70
71         if (page && act != XDP_PASS && act != XDP_TX) {
72                 __free_page(page);
73                 xdp->data_hard_start = NULL;
74         }
75
76         return act;
77 }
78
79 unsigned int netvsc_xdp_fraglen(unsigned int len)
80 {
81         return SKB_DATA_ALIGN(len) +
82                SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
83 }
84
85 struct bpf_prog *netvsc_xdp_get(struct netvsc_device *nvdev)
86 {
87         return rtnl_dereference(nvdev->chan_table[0].bpf_prog);
88 }
89
90 int netvsc_xdp_set(struct net_device *dev, struct bpf_prog *prog,
91                    struct netlink_ext_ack *extack,
92                    struct netvsc_device *nvdev)
93 {
94         struct bpf_prog *old_prog;
95         int buf_max, i;
96
97         old_prog = netvsc_xdp_get(nvdev);
98
99         if (!old_prog && !prog)
100                 return 0;
101
102         buf_max = NETVSC_XDP_HDRM + netvsc_xdp_fraglen(dev->mtu + ETH_HLEN);
103         if (prog && buf_max > PAGE_SIZE) {
104                 netdev_err(dev, "XDP: mtu:%u too large, buf_max:%u\n",
105                            dev->mtu, buf_max);
106                 NL_SET_ERR_MSG_MOD(extack, "XDP: mtu too large");
107
108                 return -EOPNOTSUPP;
109         }
110
111         if (prog && (dev->features & NETIF_F_LRO)) {
112                 netdev_err(dev, "XDP: not support LRO\n");
113                 NL_SET_ERR_MSG_MOD(extack, "XDP: not support LRO");
114
115                 return -EOPNOTSUPP;
116         }
117
118         if (prog)
119                 bpf_prog_add(prog, nvdev->num_chn - 1);
120
121         for (i = 0; i < nvdev->num_chn; i++)
122                 rcu_assign_pointer(nvdev->chan_table[i].bpf_prog, prog);
123
124         if (old_prog)
125                 for (i = 0; i < nvdev->num_chn; i++)
126                         bpf_prog_put(old_prog);
127
128         return 0;
129 }
130
131 int netvsc_vf_setxdp(struct net_device *vf_netdev, struct bpf_prog *prog)
132 {
133         struct netdev_bpf xdp;
134         bpf_op_t ndo_bpf;
135         int ret;
136
137         ASSERT_RTNL();
138
139         if (!vf_netdev)
140                 return 0;
141
142         ndo_bpf = vf_netdev->netdev_ops->ndo_bpf;
143         if (!ndo_bpf)
144                 return 0;
145
146         memset(&xdp, 0, sizeof(xdp));
147
148         if (prog)
149                 bpf_prog_inc(prog);
150
151         xdp.command = XDP_SETUP_PROG;
152         xdp.prog = prog;
153
154         ret = ndo_bpf(vf_netdev, &xdp);
155
156         if (ret && prog)
157                 bpf_prog_put(prog);
158
159         return ret;
160 }
161
162 int netvsc_bpf(struct net_device *dev, struct netdev_bpf *bpf)
163 {
164         struct net_device_context *ndevctx = netdev_priv(dev);
165         struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev);
166         struct net_device *vf_netdev = rtnl_dereference(ndevctx->vf_netdev);
167         struct netlink_ext_ack *extack = bpf->extack;
168         int ret;
169
170         if (!nvdev || nvdev->destroy) {
171                 return -ENODEV;
172         }
173
174         switch (bpf->command) {
175         case XDP_SETUP_PROG:
176                 ret = netvsc_xdp_set(dev, bpf->prog, extack, nvdev);
177
178                 if (ret)
179                         return ret;
180
181                 ret = netvsc_vf_setxdp(vf_netdev, bpf->prog);
182
183                 if (ret) {
184                         netdev_err(dev, "vf_setxdp failed:%d\n", ret);
185                         NL_SET_ERR_MSG_MOD(extack, "vf_setxdp failed");
186
187                         netvsc_xdp_set(dev, NULL, extack, nvdev);
188                 }
189
190                 return ret;
191
192         default:
193                 return -EINVAL;
194         }
195 }