Commit | Line | Data |
---|---|---|
83c9e13a JK |
1 | /* |
2 | * Copyright (C) 2017 Netronome Systems, Inc. | |
3 | * | |
4 | * This software is 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. | |
7 | * | |
8 | * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" | |
9 | * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, | |
10 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
11 | * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE | |
12 | * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME | |
13 | * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | |
14 | */ | |
15 | ||
31d3ad83 | 16 | #include <linux/debugfs.h> |
83c9e13a | 17 | #include <linux/etherdevice.h> |
f394d07b | 18 | #include <linux/ethtool_netlink.h> |
83c9e13a JK |
19 | #include <linux/kernel.h> |
20 | #include <linux/module.h> | |
21 | #include <linux/netdevice.h> | |
22 | #include <linux/slab.h> | |
f216306b | 23 | #include <net/netdev_queues.h> |
6917d207 | 24 | #include <net/netdev_rx_queue.h> |
1580cbcb | 25 | #include <net/page_pool/helpers.h> |
83c9e13a | 26 | #include <net/netlink.h> |
b3ea4164 | 27 | #include <net/net_shaper.h> |
8ef890df | 28 | #include <net/netdev_lock.h> |
31d3ad83 | 29 | #include <net/pkt_cls.h> |
83c9e13a | 30 | #include <net/rtnetlink.h> |
424be63a | 31 | #include <net/udp_tunnel.h> |
f71c549b | 32 | #include <net/busy_poll.h> |
83c9e13a JK |
33 | |
34 | #include "netdevsim.h" | |
35 | ||
6917d207 JK |
36 | MODULE_IMPORT_NS("NETDEV_INTERNAL"); |
37 | ||
3762ec05 DW |
38 | #define NSIM_RING_SIZE 256 |
39 | ||
40 | static int nsim_napi_rx(struct nsim_rq *rq, struct sk_buff *skb) | |
41 | { | |
42 | if (skb_queue_len(&rq->skb_queue) > NSIM_RING_SIZE) { | |
43 | dev_kfree_skb_any(skb); | |
44 | return NET_RX_DROP; | |
45 | } | |
46 | ||
47 | skb_queue_tail(&rq->skb_queue, skb); | |
48 | return NET_RX_SUCCESS; | |
49 | } | |
50 | ||
51 | static int nsim_forward_skb(struct net_device *dev, struct sk_buff *skb, | |
52 | struct nsim_rq *rq) | |
53 | { | |
54 | return __dev_forward_skb(dev, skb) ?: nsim_napi_rx(rq, skb); | |
55 | } | |
56 | ||
83c9e13a JK |
57 | static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev) |
58 | { | |
59 | struct netdevsim *ns = netdev_priv(dev); | |
3762ec05 | 60 | struct net_device *peer_dev; |
9eb95228 DW |
61 | unsigned int len = skb->len; |
62 | struct netdevsim *peer_ns; | |
3c836451 | 63 | struct netdev_config *cfg; |
3762ec05 DW |
64 | struct nsim_rq *rq; |
65 | int rxq; | |
83c9e13a | 66 | |
9eb95228 | 67 | rcu_read_lock(); |
7699353d | 68 | if (!nsim_ipsec_tx(ns, skb)) |
9eb95228 | 69 | goto out_drop_free; |
7699353d | 70 | |
9eb95228 DW |
71 | peer_ns = rcu_dereference(ns->peer); |
72 | if (!peer_ns) | |
73 | goto out_drop_free; | |
74 | ||
3762ec05 DW |
75 | peer_dev = peer_ns->netdev; |
76 | rxq = skb_get_queue_mapping(skb); | |
77 | if (rxq >= peer_dev->num_rx_queues) | |
78 | rxq = rxq % peer_dev->num_rx_queues; | |
915c82f8 | 79 | rq = peer_ns->rq[rxq]; |
3762ec05 | 80 | |
3c836451 | 81 | cfg = peer_dev->cfg; |
f394d07b | 82 | if (skb_is_nonlinear(skb) && |
3c836451 JK |
83 | (cfg->hds_config != ETHTOOL_TCP_DATA_SPLIT_ENABLED || |
84 | (cfg->hds_config == ETHTOOL_TCP_DATA_SPLIT_ENABLED && | |
85 | cfg->hds_thresh > len))) | |
f394d07b TY |
86 | skb_linearize(skb); |
87 | ||
9eb95228 | 88 | skb_tx_timestamp(skb); |
3762ec05 | 89 | if (unlikely(nsim_forward_skb(peer_dev, skb, rq) == NET_RX_DROP)) |
9eb95228 DW |
90 | goto out_drop_cnt; |
91 | ||
bf3624cf BL |
92 | if (!hrtimer_active(&rq->napi_timer)) |
93 | hrtimer_start(&rq->napi_timer, us_to_ktime(5), HRTIMER_MODE_REL); | |
3762ec05 | 94 | |
9eb95228 | 95 | rcu_read_unlock(); |
83c9e13a JK |
96 | u64_stats_update_begin(&ns->syncp); |
97 | ns->tx_packets++; | |
9eb95228 | 98 | ns->tx_bytes += len; |
83c9e13a | 99 | u64_stats_update_end(&ns->syncp); |
9eb95228 | 100 | return NETDEV_TX_OK; |
83c9e13a | 101 | |
9eb95228 | 102 | out_drop_free: |
83c9e13a | 103 | dev_kfree_skb(skb); |
9eb95228 DW |
104 | out_drop_cnt: |
105 | rcu_read_unlock(); | |
106 | u64_stats_update_begin(&ns->syncp); | |
107 | ns->tx_dropped++; | |
108 | u64_stats_update_end(&ns->syncp); | |
83c9e13a JK |
109 | return NETDEV_TX_OK; |
110 | } | |
111 | ||
112 | static void nsim_set_rx_mode(struct net_device *dev) | |
113 | { | |
114 | } | |
115 | ||
31d3ad83 JK |
116 | static int nsim_change_mtu(struct net_device *dev, int new_mtu) |
117 | { | |
118 | struct netdevsim *ns = netdev_priv(dev); | |
119 | ||
e016cf5f JK |
120 | if (ns->xdp.prog && !ns->xdp.prog->aux->xdp_has_frags && |
121 | new_mtu > NSIM_XDP_MAX_MTU) | |
31d3ad83 JK |
122 | return -EBUSY; |
123 | ||
1eb2cded | 124 | WRITE_ONCE(dev->mtu, new_mtu); |
31d3ad83 JK |
125 | |
126 | return 0; | |
127 | } | |
128 | ||
83c9e13a JK |
129 | static void |
130 | nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) | |
131 | { | |
132 | struct netdevsim *ns = netdev_priv(dev); | |
133 | unsigned int start; | |
134 | ||
135 | do { | |
068c38ad | 136 | start = u64_stats_fetch_begin(&ns->syncp); |
83c9e13a JK |
137 | stats->tx_bytes = ns->tx_bytes; |
138 | stats->tx_packets = ns->tx_packets; | |
9eb95228 | 139 | stats->tx_dropped = ns->tx_dropped; |
068c38ad | 140 | } while (u64_stats_fetch_retry(&ns->syncp, start)); |
83c9e13a JK |
141 | } |
142 | ||
31d3ad83 JK |
143 | static int |
144 | nsim_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) | |
145 | { | |
146 | return nsim_bpf_setup_tc_block_cb(type, type_data, cb_priv); | |
147 | } | |
148 | ||
79579220 JK |
149 | static int nsim_set_vf_mac(struct net_device *dev, int vf, u8 *mac) |
150 | { | |
151 | struct netdevsim *ns = netdev_priv(dev); | |
5e388f3d | 152 | struct nsim_dev *nsim_dev = ns->nsim_dev; |
79579220 JK |
153 | |
154 | /* Only refuse multicast addresses, zero address can mean unset/any. */ | |
5e388f3d | 155 | if (vf >= nsim_dev_get_vfs(nsim_dev) || is_multicast_ether_addr(mac)) |
79579220 | 156 | return -EINVAL; |
5e388f3d | 157 | memcpy(nsim_dev->vfconfigs[vf].vf_mac, mac, ETH_ALEN); |
79579220 JK |
158 | |
159 | return 0; | |
160 | } | |
161 | ||
162 | static int nsim_set_vf_vlan(struct net_device *dev, int vf, | |
163 | u16 vlan, u8 qos, __be16 vlan_proto) | |
164 | { | |
165 | struct netdevsim *ns = netdev_priv(dev); | |
5e388f3d | 166 | struct nsim_dev *nsim_dev = ns->nsim_dev; |
79579220 | 167 | |
5e388f3d | 168 | if (vf >= nsim_dev_get_vfs(nsim_dev) || vlan > 4095 || qos > 7) |
79579220 JK |
169 | return -EINVAL; |
170 | ||
5e388f3d JK |
171 | nsim_dev->vfconfigs[vf].vlan = vlan; |
172 | nsim_dev->vfconfigs[vf].qos = qos; | |
173 | nsim_dev->vfconfigs[vf].vlan_proto = vlan_proto; | |
79579220 JK |
174 | |
175 | return 0; | |
176 | } | |
177 | ||
178 | static int nsim_set_vf_rate(struct net_device *dev, int vf, int min, int max) | |
179 | { | |
180 | struct netdevsim *ns = netdev_priv(dev); | |
5e388f3d | 181 | struct nsim_dev *nsim_dev = ns->nsim_dev; |
79579220 | 182 | |
160dc373 DL |
183 | if (nsim_esw_mode_is_switchdev(ns->nsim_dev)) { |
184 | pr_err("Not supported in switchdev mode. Please use devlink API.\n"); | |
185 | return -EOPNOTSUPP; | |
186 | } | |
187 | ||
5e388f3d | 188 | if (vf >= nsim_dev_get_vfs(nsim_dev)) |
79579220 JK |
189 | return -EINVAL; |
190 | ||
5e388f3d JK |
191 | nsim_dev->vfconfigs[vf].min_tx_rate = min; |
192 | nsim_dev->vfconfigs[vf].max_tx_rate = max; | |
79579220 JK |
193 | |
194 | return 0; | |
195 | } | |
196 | ||
197 | static int nsim_set_vf_spoofchk(struct net_device *dev, int vf, bool val) | |
198 | { | |
199 | struct netdevsim *ns = netdev_priv(dev); | |
5e388f3d | 200 | struct nsim_dev *nsim_dev = ns->nsim_dev; |
79579220 | 201 | |
5e388f3d | 202 | if (vf >= nsim_dev_get_vfs(nsim_dev)) |
79579220 | 203 | return -EINVAL; |
5e388f3d | 204 | nsim_dev->vfconfigs[vf].spoofchk_enabled = val; |
79579220 JK |
205 | |
206 | return 0; | |
207 | } | |
208 | ||
209 | static int nsim_set_vf_rss_query_en(struct net_device *dev, int vf, bool val) | |
210 | { | |
211 | struct netdevsim *ns = netdev_priv(dev); | |
5e388f3d | 212 | struct nsim_dev *nsim_dev = ns->nsim_dev; |
79579220 | 213 | |
5e388f3d | 214 | if (vf >= nsim_dev_get_vfs(nsim_dev)) |
79579220 | 215 | return -EINVAL; |
5e388f3d | 216 | nsim_dev->vfconfigs[vf].rss_query_enabled = val; |
79579220 JK |
217 | |
218 | return 0; | |
219 | } | |
220 | ||
221 | static int nsim_set_vf_trust(struct net_device *dev, int vf, bool val) | |
222 | { | |
223 | struct netdevsim *ns = netdev_priv(dev); | |
5e388f3d | 224 | struct nsim_dev *nsim_dev = ns->nsim_dev; |
79579220 | 225 | |
5e388f3d | 226 | if (vf >= nsim_dev_get_vfs(nsim_dev)) |
79579220 | 227 | return -EINVAL; |
5e388f3d | 228 | nsim_dev->vfconfigs[vf].trusted = val; |
79579220 JK |
229 | |
230 | return 0; | |
231 | } | |
232 | ||
233 | static int | |
234 | nsim_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi) | |
235 | { | |
236 | struct netdevsim *ns = netdev_priv(dev); | |
5e388f3d | 237 | struct nsim_dev *nsim_dev = ns->nsim_dev; |
79579220 | 238 | |
5e388f3d | 239 | if (vf >= nsim_dev_get_vfs(nsim_dev)) |
79579220 JK |
240 | return -EINVAL; |
241 | ||
242 | ivi->vf = vf; | |
5e388f3d JK |
243 | ivi->linkstate = nsim_dev->vfconfigs[vf].link_state; |
244 | ivi->min_tx_rate = nsim_dev->vfconfigs[vf].min_tx_rate; | |
245 | ivi->max_tx_rate = nsim_dev->vfconfigs[vf].max_tx_rate; | |
246 | ivi->vlan = nsim_dev->vfconfigs[vf].vlan; | |
247 | ivi->vlan_proto = nsim_dev->vfconfigs[vf].vlan_proto; | |
248 | ivi->qos = nsim_dev->vfconfigs[vf].qos; | |
249 | memcpy(&ivi->mac, nsim_dev->vfconfigs[vf].vf_mac, ETH_ALEN); | |
250 | ivi->spoofchk = nsim_dev->vfconfigs[vf].spoofchk_enabled; | |
251 | ivi->trusted = nsim_dev->vfconfigs[vf].trusted; | |
252 | ivi->rss_query_en = nsim_dev->vfconfigs[vf].rss_query_enabled; | |
79579220 JK |
253 | |
254 | return 0; | |
255 | } | |
256 | ||
257 | static int nsim_set_vf_link_state(struct net_device *dev, int vf, int state) | |
258 | { | |
259 | struct netdevsim *ns = netdev_priv(dev); | |
5e388f3d | 260 | struct nsim_dev *nsim_dev = ns->nsim_dev; |
79579220 | 261 | |
5e388f3d | 262 | if (vf >= nsim_dev_get_vfs(nsim_dev)) |
79579220 JK |
263 | return -EINVAL; |
264 | ||
265 | switch (state) { | |
266 | case IFLA_VF_LINK_STATE_AUTO: | |
267 | case IFLA_VF_LINK_STATE_ENABLE: | |
268 | case IFLA_VF_LINK_STATE_DISABLE: | |
269 | break; | |
270 | default: | |
271 | return -EINVAL; | |
272 | } | |
273 | ||
5e388f3d | 274 | nsim_dev->vfconfigs[vf].link_state = state; |
79579220 JK |
275 | |
276 | return 0; | |
277 | } | |
278 | ||
35da47fe VO |
279 | static void nsim_taprio_stats(struct tc_taprio_qopt_stats *stats) |
280 | { | |
281 | stats->window_drops = 0; | |
282 | stats->tx_overruns = 0; | |
283 | } | |
284 | ||
285 | static int nsim_setup_tc_taprio(struct net_device *dev, | |
286 | struct tc_taprio_qopt_offload *offload) | |
287 | { | |
288 | int err = 0; | |
289 | ||
290 | switch (offload->cmd) { | |
291 | case TAPRIO_CMD_REPLACE: | |
292 | case TAPRIO_CMD_DESTROY: | |
293 | break; | |
294 | case TAPRIO_CMD_STATS: | |
295 | nsim_taprio_stats(&offload->stats); | |
296 | break; | |
297 | default: | |
298 | err = -EOPNOTSUPP; | |
299 | } | |
300 | ||
301 | return err; | |
302 | } | |
303 | ||
955bcb6e PNA |
304 | static LIST_HEAD(nsim_block_cb_list); |
305 | ||
31d3ad83 JK |
306 | static int |
307 | nsim_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data) | |
308 | { | |
4e95bc26 PNA |
309 | struct netdevsim *ns = netdev_priv(dev); |
310 | ||
31d3ad83 | 311 | switch (type) { |
35da47fe VO |
312 | case TC_SETUP_QDISC_TAPRIO: |
313 | return nsim_setup_tc_taprio(dev, type_data); | |
31d3ad83 | 314 | case TC_SETUP_BLOCK: |
955bcb6e PNA |
315 | return flow_block_cb_setup_simple(type_data, |
316 | &nsim_block_cb_list, | |
4e95bc26 PNA |
317 | nsim_setup_tc_block_cb, |
318 | ns, ns, true); | |
31d3ad83 JK |
319 | default: |
320 | return -EOPNOTSUPP; | |
321 | } | |
322 | } | |
323 | ||
324 | static int | |
325 | nsim_set_features(struct net_device *dev, netdev_features_t features) | |
326 | { | |
327 | struct netdevsim *ns = netdev_priv(dev); | |
328 | ||
329 | if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC)) | |
330 | return nsim_bpf_disable_tc(ns); | |
331 | ||
332 | return 0; | |
333 | } | |
334 | ||
8debcf58 DW |
335 | static int nsim_get_iflink(const struct net_device *dev) |
336 | { | |
337 | struct netdevsim *nsim, *peer; | |
338 | int iflink; | |
339 | ||
340 | nsim = netdev_priv(dev); | |
341 | ||
342 | rcu_read_lock(); | |
343 | peer = rcu_dereference(nsim->peer); | |
5add2f72 DW |
344 | iflink = peer ? READ_ONCE(peer->netdev->ifindex) : |
345 | READ_ONCE(dev->ifindex); | |
8debcf58 DW |
346 | rcu_read_unlock(); |
347 | ||
348 | return iflink; | |
349 | } | |
350 | ||
3762ec05 DW |
351 | static int nsim_rcv(struct nsim_rq *rq, int budget) |
352 | { | |
353 | struct sk_buff *skb; | |
354 | int i; | |
355 | ||
356 | for (i = 0; i < budget; i++) { | |
357 | if (skb_queue_empty(&rq->skb_queue)) | |
358 | break; | |
359 | ||
360 | skb = skb_dequeue(&rq->skb_queue); | |
f71c549b | 361 | skb_mark_napi_id(skb, &rq->napi); |
3762ec05 DW |
362 | netif_receive_skb(skb); |
363 | } | |
364 | ||
365 | return i; | |
366 | } | |
367 | ||
368 | static int nsim_poll(struct napi_struct *napi, int budget) | |
369 | { | |
370 | struct nsim_rq *rq = container_of(napi, struct nsim_rq, napi); | |
371 | int done; | |
372 | ||
373 | done = nsim_rcv(rq, budget); | |
12649710 JK |
374 | if (done < budget) |
375 | napi_complete_done(napi, done); | |
3762ec05 DW |
376 | |
377 | return done; | |
378 | } | |
379 | ||
5bc8e8db | 380 | static int nsim_create_page_pool(struct page_pool **p, struct napi_struct *napi) |
3762ec05 | 381 | { |
5bc8e8db | 382 | struct page_pool_params params = { |
3762ec05 DW |
383 | .order = 0, |
384 | .pool_size = NSIM_RING_SIZE, | |
385 | .nid = NUMA_NO_NODE, | |
5bc8e8db JK |
386 | .dev = &napi->dev->dev, |
387 | .napi = napi, | |
3762ec05 | 388 | .dma_dir = DMA_BIDIRECTIONAL, |
5bc8e8db | 389 | .netdev = napi->dev, |
3762ec05 | 390 | }; |
5bc8e8db | 391 | struct page_pool *pool; |
3762ec05 | 392 | |
5bc8e8db JK |
393 | pool = page_pool_create(¶ms); |
394 | if (IS_ERR(pool)) | |
395 | return PTR_ERR(pool); | |
3762ec05 | 396 | |
5bc8e8db | 397 | *p = pool; |
3762ec05 DW |
398 | return 0; |
399 | } | |
400 | ||
401 | static int nsim_init_napi(struct netdevsim *ns) | |
402 | { | |
403 | struct net_device *dev = ns->netdev; | |
404 | struct nsim_rq *rq; | |
405 | int err, i; | |
406 | ||
407 | for (i = 0; i < dev->num_rx_queues; i++) { | |
915c82f8 | 408 | rq = ns->rq[i]; |
3762ec05 | 409 | |
d4c22ec6 | 410 | netif_napi_add_config_locked(dev, &rq->napi, nsim_poll, i); |
3762ec05 DW |
411 | } |
412 | ||
413 | for (i = 0; i < dev->num_rx_queues; i++) { | |
915c82f8 | 414 | rq = ns->rq[i]; |
3762ec05 | 415 | |
5bc8e8db | 416 | err = nsim_create_page_pool(&rq->page_pool, &rq->napi); |
3762ec05 DW |
417 | if (err) |
418 | goto err_pp_destroy; | |
419 | } | |
420 | ||
421 | return 0; | |
422 | ||
423 | err_pp_destroy: | |
424 | while (i--) { | |
915c82f8 JK |
425 | page_pool_destroy(ns->rq[i]->page_pool); |
426 | ns->rq[i]->page_pool = NULL; | |
3762ec05 DW |
427 | } |
428 | ||
429 | for (i = 0; i < dev->num_rx_queues; i++) | |
d4c22ec6 | 430 | __netif_napi_del_locked(&ns->rq[i]->napi); |
3762ec05 DW |
431 | |
432 | return err; | |
433 | } | |
434 | ||
bf3624cf BL |
435 | static enum hrtimer_restart nsim_napi_schedule(struct hrtimer *timer) |
436 | { | |
437 | struct nsim_rq *rq; | |
438 | ||
439 | rq = container_of(timer, struct nsim_rq, napi_timer); | |
440 | napi_schedule(&rq->napi); | |
441 | ||
442 | return HRTIMER_NORESTART; | |
443 | } | |
444 | ||
445 | static void nsim_rq_timer_init(struct nsim_rq *rq) | |
446 | { | |
48ad7bbf TG |
447 | hrtimer_setup(&rq->napi_timer, nsim_napi_schedule, CLOCK_MONOTONIC, |
448 | HRTIMER_MODE_REL); | |
bf3624cf BL |
449 | } |
450 | ||
3762ec05 DW |
451 | static void nsim_enable_napi(struct netdevsim *ns) |
452 | { | |
453 | struct net_device *dev = ns->netdev; | |
454 | int i; | |
455 | ||
456 | for (i = 0; i < dev->num_rx_queues; i++) { | |
915c82f8 | 457 | struct nsim_rq *rq = ns->rq[i]; |
3762ec05 DW |
458 | |
459 | netif_queue_set_napi(dev, i, NETDEV_QUEUE_TYPE_RX, &rq->napi); | |
d4c22ec6 | 460 | napi_enable_locked(&rq->napi); |
3762ec05 DW |
461 | } |
462 | } | |
463 | ||
1580cbcb JK |
464 | static int nsim_open(struct net_device *dev) |
465 | { | |
466 | struct netdevsim *ns = netdev_priv(dev); | |
3762ec05 DW |
467 | int err; |
468 | ||
d4c22ec6 SF |
469 | netdev_assert_locked(dev); |
470 | ||
3762ec05 DW |
471 | err = nsim_init_napi(ns); |
472 | if (err) | |
473 | return err; | |
474 | ||
475 | nsim_enable_napi(ns); | |
1580cbcb | 476 | |
3762ec05 DW |
477 | return 0; |
478 | } | |
1580cbcb | 479 | |
3762ec05 DW |
480 | static void nsim_del_napi(struct netdevsim *ns) |
481 | { | |
482 | struct net_device *dev = ns->netdev; | |
483 | int i; | |
484 | ||
485 | for (i = 0; i < dev->num_rx_queues; i++) { | |
915c82f8 | 486 | struct nsim_rq *rq = ns->rq[i]; |
3762ec05 | 487 | |
d4c22ec6 SF |
488 | napi_disable_locked(&rq->napi); |
489 | __netif_napi_del_locked(&rq->napi); | |
3762ec05 DW |
490 | } |
491 | synchronize_net(); | |
492 | ||
493 | for (i = 0; i < dev->num_rx_queues; i++) { | |
915c82f8 JK |
494 | page_pool_destroy(ns->rq[i]->page_pool); |
495 | ns->rq[i]->page_pool = NULL; | |
3762ec05 | 496 | } |
1580cbcb JK |
497 | } |
498 | ||
499 | static int nsim_stop(struct net_device *dev) | |
500 | { | |
501 | struct netdevsim *ns = netdev_priv(dev); | |
3762ec05 DW |
502 | struct netdevsim *peer; |
503 | ||
d4c22ec6 SF |
504 | netdev_assert_locked(dev); |
505 | ||
3762ec05 DW |
506 | netif_carrier_off(dev); |
507 | peer = rtnl_dereference(ns->peer); | |
508 | if (peer) | |
509 | netif_carrier_off(peer->netdev); | |
1580cbcb | 510 | |
3762ec05 | 511 | nsim_del_napi(ns); |
1580cbcb JK |
512 | |
513 | return 0; | |
514 | } | |
515 | ||
b3ea4164 PA |
516 | static int nsim_shaper_set(struct net_shaper_binding *binding, |
517 | const struct net_shaper *shaper, | |
518 | struct netlink_ext_ack *extack) | |
519 | { | |
520 | return 0; | |
521 | } | |
522 | ||
523 | static int nsim_shaper_del(struct net_shaper_binding *binding, | |
524 | const struct net_shaper_handle *handle, | |
525 | struct netlink_ext_ack *extack) | |
526 | { | |
527 | return 0; | |
528 | } | |
529 | ||
530 | static int nsim_shaper_group(struct net_shaper_binding *binding, | |
531 | int leaves_count, | |
532 | const struct net_shaper *leaves, | |
533 | const struct net_shaper *root, | |
534 | struct netlink_ext_ack *extack) | |
535 | { | |
536 | return 0; | |
537 | } | |
538 | ||
539 | static void nsim_shaper_cap(struct net_shaper_binding *binding, | |
540 | enum net_shaper_scope scope, | |
541 | unsigned long *flags) | |
542 | { | |
543 | *flags = ULONG_MAX; | |
544 | } | |
545 | ||
546 | static const struct net_shaper_ops nsim_shaper_ops = { | |
547 | .set = nsim_shaper_set, | |
548 | .delete = nsim_shaper_del, | |
549 | .group = nsim_shaper_group, | |
550 | .capabilities = nsim_shaper_cap, | |
551 | }; | |
552 | ||
83c9e13a JK |
553 | static const struct net_device_ops nsim_netdev_ops = { |
554 | .ndo_start_xmit = nsim_start_xmit, | |
555 | .ndo_set_rx_mode = nsim_set_rx_mode, | |
556 | .ndo_set_mac_address = eth_mac_addr, | |
557 | .ndo_validate_addr = eth_validate_addr, | |
31d3ad83 | 558 | .ndo_change_mtu = nsim_change_mtu, |
83c9e13a | 559 | .ndo_get_stats64 = nsim_get_stats64, |
79579220 JK |
560 | .ndo_set_vf_mac = nsim_set_vf_mac, |
561 | .ndo_set_vf_vlan = nsim_set_vf_vlan, | |
562 | .ndo_set_vf_rate = nsim_set_vf_rate, | |
563 | .ndo_set_vf_spoofchk = nsim_set_vf_spoofchk, | |
564 | .ndo_set_vf_trust = nsim_set_vf_trust, | |
565 | .ndo_get_vf_config = nsim_get_vf_config, | |
566 | .ndo_set_vf_link_state = nsim_set_vf_link_state, | |
567 | .ndo_set_vf_rss_query_en = nsim_set_vf_rss_query_en, | |
31d3ad83 JK |
568 | .ndo_setup_tc = nsim_setup_tc, |
569 | .ndo_set_features = nsim_set_features, | |
8debcf58 | 570 | .ndo_get_iflink = nsim_get_iflink, |
31d3ad83 | 571 | .ndo_bpf = nsim_bpf, |
1580cbcb JK |
572 | .ndo_open = nsim_open, |
573 | .ndo_stop = nsim_stop, | |
b3ea4164 | 574 | .net_shaper_ops = &nsim_shaper_ops, |
83c9e13a JK |
575 | }; |
576 | ||
92ba1f29 DL |
577 | static const struct net_device_ops nsim_vf_netdev_ops = { |
578 | .ndo_start_xmit = nsim_start_xmit, | |
579 | .ndo_set_rx_mode = nsim_set_rx_mode, | |
580 | .ndo_set_mac_address = eth_mac_addr, | |
581 | .ndo_validate_addr = eth_validate_addr, | |
582 | .ndo_change_mtu = nsim_change_mtu, | |
583 | .ndo_get_stats64 = nsim_get_stats64, | |
584 | .ndo_setup_tc = nsim_setup_tc, | |
585 | .ndo_set_features = nsim_set_features, | |
92ba1f29 DL |
586 | }; |
587 | ||
f216306b JK |
588 | /* We don't have true per-queue stats, yet, so do some random fakery here. |
589 | * Only report stuff for queue 0. | |
590 | */ | |
591 | static void nsim_get_queue_stats_rx(struct net_device *dev, int idx, | |
592 | struct netdev_queue_stats_rx *stats) | |
593 | { | |
594 | struct rtnl_link_stats64 rtstats = {}; | |
595 | ||
596 | if (!idx) | |
597 | nsim_get_stats64(dev, &rtstats); | |
598 | ||
599 | stats->packets = rtstats.rx_packets - !!rtstats.rx_packets; | |
600 | stats->bytes = rtstats.rx_bytes; | |
601 | } | |
602 | ||
603 | static void nsim_get_queue_stats_tx(struct net_device *dev, int idx, | |
604 | struct netdev_queue_stats_tx *stats) | |
605 | { | |
606 | struct rtnl_link_stats64 rtstats = {}; | |
607 | ||
608 | if (!idx) | |
609 | nsim_get_stats64(dev, &rtstats); | |
610 | ||
611 | stats->packets = rtstats.tx_packets - !!rtstats.tx_packets; | |
612 | stats->bytes = rtstats.tx_bytes; | |
613 | } | |
614 | ||
615 | static void nsim_get_base_stats(struct net_device *dev, | |
616 | struct netdev_queue_stats_rx *rx, | |
617 | struct netdev_queue_stats_tx *tx) | |
618 | { | |
619 | struct rtnl_link_stats64 rtstats = {}; | |
620 | ||
621 | nsim_get_stats64(dev, &rtstats); | |
622 | ||
623 | rx->packets = !!rtstats.rx_packets; | |
624 | rx->bytes = 0; | |
625 | tx->packets = !!rtstats.tx_packets; | |
626 | tx->bytes = 0; | |
627 | } | |
628 | ||
629 | static const struct netdev_stat_ops nsim_stat_ops = { | |
630 | .get_queue_stats_tx = nsim_get_queue_stats_tx, | |
631 | .get_queue_stats_rx = nsim_get_queue_stats_rx, | |
632 | .get_base_stats = nsim_get_base_stats, | |
633 | }; | |
634 | ||
a565dd04 JK |
635 | static struct nsim_rq *nsim_queue_alloc(void) |
636 | { | |
637 | struct nsim_rq *rq; | |
638 | ||
639 | rq = kzalloc(sizeof(*rq), GFP_KERNEL_ACCOUNT); | |
640 | if (!rq) | |
641 | return NULL; | |
642 | ||
643 | skb_queue_head_init(&rq->skb_queue); | |
bf3624cf | 644 | nsim_rq_timer_init(rq); |
a565dd04 JK |
645 | return rq; |
646 | } | |
647 | ||
648 | static void nsim_queue_free(struct nsim_rq *rq) | |
649 | { | |
bf3624cf | 650 | hrtimer_cancel(&rq->napi_timer); |
a565dd04 JK |
651 | skb_queue_purge_reason(&rq->skb_queue, SKB_DROP_REASON_QUEUE_PURGE); |
652 | kfree(rq); | |
653 | } | |
654 | ||
5bc8e8db JK |
655 | /* Queue reset mode is controlled by ns->rq_reset_mode. |
656 | * - normal - new NAPI new pool (old NAPI enabled when new added) | |
657 | * - mode 1 - allocate new pool (NAPI is only disabled / enabled) | |
658 | * - mode 2 - new NAPI new pool (old NAPI removed before new added) | |
659 | * - mode 3 - new NAPI new pool (old NAPI disabled when new added) | |
660 | */ | |
661 | struct nsim_queue_mem { | |
662 | struct nsim_rq *rq; | |
663 | struct page_pool *pp; | |
664 | }; | |
665 | ||
666 | static int | |
667 | nsim_queue_mem_alloc(struct net_device *dev, void *per_queue_mem, int idx) | |
668 | { | |
669 | struct nsim_queue_mem *qmem = per_queue_mem; | |
670 | struct netdevsim *ns = netdev_priv(dev); | |
671 | int err; | |
672 | ||
673 | if (ns->rq_reset_mode > 3) | |
674 | return -EINVAL; | |
675 | ||
285b3f78 JK |
676 | if (ns->rq_reset_mode == 1) { |
677 | if (!netif_running(ns->netdev)) | |
678 | return -ENETDOWN; | |
5bc8e8db | 679 | return nsim_create_page_pool(&qmem->pp, &ns->rq[idx]->napi); |
285b3f78 | 680 | } |
5bc8e8db JK |
681 | |
682 | qmem->rq = nsim_queue_alloc(); | |
683 | if (!qmem->rq) | |
684 | return -ENOMEM; | |
685 | ||
686 | err = nsim_create_page_pool(&qmem->rq->page_pool, &qmem->rq->napi); | |
687 | if (err) | |
688 | goto err_free; | |
689 | ||
690 | if (!ns->rq_reset_mode) | |
cae03e5b SF |
691 | netif_napi_add_config_locked(dev, &qmem->rq->napi, nsim_poll, |
692 | idx); | |
5bc8e8db JK |
693 | |
694 | return 0; | |
695 | ||
696 | err_free: | |
697 | nsim_queue_free(qmem->rq); | |
698 | return err; | |
699 | } | |
700 | ||
701 | static void nsim_queue_mem_free(struct net_device *dev, void *per_queue_mem) | |
702 | { | |
703 | struct nsim_queue_mem *qmem = per_queue_mem; | |
704 | struct netdevsim *ns = netdev_priv(dev); | |
705 | ||
706 | page_pool_destroy(qmem->pp); | |
707 | if (qmem->rq) { | |
708 | if (!ns->rq_reset_mode) | |
cae03e5b | 709 | netif_napi_del_locked(&qmem->rq->napi); |
5bc8e8db JK |
710 | page_pool_destroy(qmem->rq->page_pool); |
711 | nsim_queue_free(qmem->rq); | |
712 | } | |
713 | } | |
714 | ||
715 | static int | |
716 | nsim_queue_start(struct net_device *dev, void *per_queue_mem, int idx) | |
717 | { | |
718 | struct nsim_queue_mem *qmem = per_queue_mem; | |
719 | struct netdevsim *ns = netdev_priv(dev); | |
720 | ||
cae03e5b SF |
721 | netdev_assert_locked(dev); |
722 | ||
5bc8e8db JK |
723 | if (ns->rq_reset_mode == 1) { |
724 | ns->rq[idx]->page_pool = qmem->pp; | |
cae03e5b | 725 | napi_enable_locked(&ns->rq[idx]->napi); |
5bc8e8db JK |
726 | return 0; |
727 | } | |
728 | ||
729 | /* netif_napi_add()/_del() should normally be called from alloc/free, | |
730 | * here we want to test various call orders. | |
731 | */ | |
732 | if (ns->rq_reset_mode == 2) { | |
cae03e5b SF |
733 | netif_napi_del_locked(&ns->rq[idx]->napi); |
734 | netif_napi_add_config_locked(dev, &qmem->rq->napi, nsim_poll, | |
735 | idx); | |
5bc8e8db | 736 | } else if (ns->rq_reset_mode == 3) { |
cae03e5b SF |
737 | netif_napi_add_config_locked(dev, &qmem->rq->napi, nsim_poll, |
738 | idx); | |
739 | netif_napi_del_locked(&ns->rq[idx]->napi); | |
5bc8e8db JK |
740 | } |
741 | ||
742 | ns->rq[idx] = qmem->rq; | |
cae03e5b | 743 | napi_enable_locked(&ns->rq[idx]->napi); |
5bc8e8db JK |
744 | |
745 | return 0; | |
746 | } | |
747 | ||
748 | static int nsim_queue_stop(struct net_device *dev, void *per_queue_mem, int idx) | |
749 | { | |
750 | struct nsim_queue_mem *qmem = per_queue_mem; | |
751 | struct netdevsim *ns = netdev_priv(dev); | |
752 | ||
cae03e5b SF |
753 | netdev_assert_locked(dev); |
754 | ||
755 | napi_disable_locked(&ns->rq[idx]->napi); | |
5bc8e8db JK |
756 | |
757 | if (ns->rq_reset_mode == 1) { | |
758 | qmem->pp = ns->rq[idx]->page_pool; | |
759 | page_pool_disable_direct_recycling(qmem->pp); | |
760 | } else { | |
761 | qmem->rq = ns->rq[idx]; | |
762 | } | |
763 | ||
764 | return 0; | |
765 | } | |
766 | ||
767 | static const struct netdev_queue_mgmt_ops nsim_queue_mgmt_ops = { | |
768 | .ndo_queue_mem_size = sizeof(struct nsim_queue_mem), | |
769 | .ndo_queue_mem_alloc = nsim_queue_mem_alloc, | |
770 | .ndo_queue_mem_free = nsim_queue_mem_free, | |
771 | .ndo_queue_start = nsim_queue_start, | |
772 | .ndo_queue_stop = nsim_queue_stop, | |
773 | }; | |
774 | ||
6917d207 JK |
775 | static ssize_t |
776 | nsim_qreset_write(struct file *file, const char __user *data, | |
777 | size_t count, loff_t *ppos) | |
778 | { | |
779 | struct netdevsim *ns = file->private_data; | |
780 | unsigned int queue, mode; | |
781 | char buf[32]; | |
782 | ssize_t ret; | |
783 | ||
784 | if (count >= sizeof(buf)) | |
785 | return -EINVAL; | |
786 | if (copy_from_user(buf, data, count)) | |
787 | return -EFAULT; | |
788 | buf[count] = '\0'; | |
789 | ||
790 | ret = sscanf(buf, "%u %u", &queue, &mode); | |
791 | if (ret != 2) | |
792 | return -EINVAL; | |
793 | ||
1d22d306 | 794 | netdev_lock(ns->netdev); |
6917d207 JK |
795 | if (queue >= ns->netdev->real_num_rx_queues) { |
796 | ret = -EINVAL; | |
797 | goto exit_unlock; | |
798 | } | |
799 | ||
800 | ns->rq_reset_mode = mode; | |
801 | ret = netdev_rx_queue_restart(ns->netdev, queue); | |
802 | ns->rq_reset_mode = 0; | |
803 | if (ret) | |
804 | goto exit_unlock; | |
805 | ||
806 | ret = count; | |
807 | exit_unlock: | |
1d22d306 | 808 | netdev_unlock(ns->netdev); |
6917d207 JK |
809 | return ret; |
810 | } | |
811 | ||
812 | static const struct file_operations nsim_qreset_fops = { | |
813 | .open = simple_open, | |
814 | .write = nsim_qreset_write, | |
815 | .owner = THIS_MODULE, | |
816 | }; | |
817 | ||
1580cbcb JK |
818 | static ssize_t |
819 | nsim_pp_hold_read(struct file *file, char __user *data, | |
820 | size_t count, loff_t *ppos) | |
821 | { | |
822 | struct netdevsim *ns = file->private_data; | |
823 | char buf[3] = "n\n"; | |
824 | ||
825 | if (ns->page) | |
826 | buf[0] = 'y'; | |
827 | ||
828 | return simple_read_from_buffer(data, count, ppos, buf, 2); | |
829 | } | |
830 | ||
831 | static ssize_t | |
832 | nsim_pp_hold_write(struct file *file, const char __user *data, | |
833 | size_t count, loff_t *ppos) | |
834 | { | |
835 | struct netdevsim *ns = file->private_data; | |
836 | ssize_t ret; | |
837 | bool val; | |
838 | ||
839 | ret = kstrtobool_from_user(data, count, &val); | |
840 | if (ret) | |
841 | return ret; | |
842 | ||
843 | rtnl_lock(); | |
844 | ret = count; | |
845 | if (val == !!ns->page) | |
846 | goto exit; | |
847 | ||
848 | if (!netif_running(ns->netdev) && val) { | |
849 | ret = -ENETDOWN; | |
850 | } else if (val) { | |
915c82f8 | 851 | ns->page = page_pool_dev_alloc_pages(ns->rq[0]->page_pool); |
1580cbcb JK |
852 | if (!ns->page) |
853 | ret = -ENOMEM; | |
854 | } else { | |
855 | page_pool_put_full_page(ns->page->pp, ns->page, false); | |
856 | ns->page = NULL; | |
857 | } | |
1580cbcb JK |
858 | |
859 | exit: | |
b9b8301d ED |
860 | rtnl_unlock(); |
861 | return ret; | |
1580cbcb JK |
862 | } |
863 | ||
864 | static const struct file_operations nsim_pp_hold_fops = { | |
865 | .open = simple_open, | |
866 | .read = nsim_pp_hold_read, | |
867 | .write = nsim_pp_hold_write, | |
868 | .llseek = generic_file_llseek, | |
869 | .owner = THIS_MODULE, | |
870 | }; | |
871 | ||
83c9e13a JK |
872 | static void nsim_setup(struct net_device *dev) |
873 | { | |
874 | ether_setup(dev); | |
875 | eth_hw_addr_random(dev); | |
876 | ||
83c9e13a | 877 | dev->tx_queue_len = 0; |
83c9e13a JK |
878 | dev->flags &= ~IFF_MULTICAST; |
879 | dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | | |
880 | IFF_NO_QUEUE; | |
881 | dev->features |= NETIF_F_HIGHDMA | | |
882 | NETIF_F_SG | | |
883 | NETIF_F_FRAGLIST | | |
884 | NETIF_F_HW_CSUM | | |
f792709e | 885 | NETIF_F_LRO | |
83c9e13a | 886 | NETIF_F_TSO; |
494bd83b SD |
887 | dev->hw_features |= NETIF_F_HW_TC | |
888 | NETIF_F_SG | | |
889 | NETIF_F_FRAGLIST | | |
890 | NETIF_F_HW_CSUM | | |
f792709e | 891 | NETIF_F_LRO | |
494bd83b | 892 | NETIF_F_TSO; |
83c9e13a | 893 | dev->max_mtu = ETH_MAX_MTU; |
66c0e13a | 894 | dev->xdp_features = NETDEV_XDP_ACT_HW_OFFLOAD; |
83c9e13a JK |
895 | } |
896 | ||
3762ec05 DW |
897 | static int nsim_queue_init(struct netdevsim *ns) |
898 | { | |
899 | struct net_device *dev = ns->netdev; | |
900 | int i; | |
901 | ||
915c82f8 JK |
902 | ns->rq = kcalloc(dev->num_rx_queues, sizeof(*ns->rq), |
903 | GFP_KERNEL_ACCOUNT); | |
3762ec05 DW |
904 | if (!ns->rq) |
905 | return -ENOMEM; | |
906 | ||
915c82f8 | 907 | for (i = 0; i < dev->num_rx_queues; i++) { |
a565dd04 | 908 | ns->rq[i] = nsim_queue_alloc(); |
915c82f8 JK |
909 | if (!ns->rq[i]) |
910 | goto err_free_prev; | |
915c82f8 | 911 | } |
3762ec05 DW |
912 | |
913 | return 0; | |
915c82f8 JK |
914 | |
915 | err_free_prev: | |
916 | while (i--) | |
917 | kfree(ns->rq[i]); | |
918 | kfree(ns->rq); | |
919 | return -ENOMEM; | |
3762ec05 DW |
920 | } |
921 | ||
a565dd04 | 922 | static void nsim_queue_uninit(struct netdevsim *ns) |
3762ec05 DW |
923 | { |
924 | struct net_device *dev = ns->netdev; | |
925 | int i; | |
926 | ||
a565dd04 JK |
927 | for (i = 0; i < dev->num_rx_queues; i++) |
928 | nsim_queue_free(ns->rq[i]); | |
3762ec05 | 929 | |
915c82f8 | 930 | kfree(ns->rq); |
3762ec05 DW |
931 | ns->rq = NULL; |
932 | } | |
933 | ||
92ba1f29 DL |
934 | static int nsim_init_netdevsim(struct netdevsim *ns) |
935 | { | |
b63e78fc | 936 | struct mock_phc *phc; |
92ba1f29 DL |
937 | int err; |
938 | ||
b63e78fc VO |
939 | phc = mock_phc_create(&ns->nsim_bus_dev->dev); |
940 | if (IS_ERR(phc)) | |
941 | return PTR_ERR(phc); | |
942 | ||
943 | ns->phc = phc; | |
92ba1f29 | 944 | ns->netdev->netdev_ops = &nsim_netdev_ops; |
f216306b | 945 | ns->netdev->stat_ops = &nsim_stat_ops; |
5bc8e8db | 946 | ns->netdev->queue_mgmt_ops = &nsim_queue_mgmt_ops; |
1901066a | 947 | netdev_lockdep_set_classes(ns->netdev); |
92ba1f29 DL |
948 | |
949 | err = nsim_udp_tunnels_info_create(ns->nsim_dev, ns->netdev); | |
950 | if (err) | |
b63e78fc | 951 | goto err_phc_destroy; |
92ba1f29 DL |
952 | |
953 | rtnl_lock(); | |
3762ec05 | 954 | err = nsim_queue_init(ns); |
92ba1f29 DL |
955 | if (err) |
956 | goto err_utn_destroy; | |
957 | ||
3762ec05 DW |
958 | err = nsim_bpf_init(ns); |
959 | if (err) | |
960 | goto err_rq_destroy; | |
961 | ||
02b34d03 | 962 | nsim_macsec_init(ns); |
92ba1f29 DL |
963 | nsim_ipsec_init(ns); |
964 | ||
965 | err = register_netdevice(ns->netdev); | |
966 | if (err) | |
967 | goto err_ipsec_teardown; | |
968 | rtnl_unlock(); | |
1901066a SF |
969 | |
970 | if (IS_ENABLED(CONFIG_DEBUG_NET)) { | |
971 | ns->nb.notifier_call = netdev_debug_event; | |
972 | if (register_netdevice_notifier_dev_net(ns->netdev, &ns->nb, | |
973 | &ns->nn)) | |
974 | ns->nb.notifier_call = NULL; | |
975 | } | |
976 | ||
92ba1f29 DL |
977 | return 0; |
978 | ||
979 | err_ipsec_teardown: | |
980 | nsim_ipsec_teardown(ns); | |
02b34d03 | 981 | nsim_macsec_teardown(ns); |
92ba1f29 | 982 | nsim_bpf_uninit(ns); |
3762ec05 | 983 | err_rq_destroy: |
a565dd04 | 984 | nsim_queue_uninit(ns); |
92ba1f29 DL |
985 | err_utn_destroy: |
986 | rtnl_unlock(); | |
987 | nsim_udp_tunnels_info_destroy(ns->netdev); | |
b63e78fc VO |
988 | err_phc_destroy: |
989 | mock_phc_destroy(ns->phc); | |
92ba1f29 DL |
990 | return err; |
991 | } | |
992 | ||
993 | static int nsim_init_netdevsim_vf(struct netdevsim *ns) | |
994 | { | |
995 | int err; | |
996 | ||
997 | ns->netdev->netdev_ops = &nsim_vf_netdev_ops; | |
998 | rtnl_lock(); | |
999 | err = register_netdevice(ns->netdev); | |
1000 | rtnl_unlock(); | |
1001 | return err; | |
1002 | } | |
1003 | ||
ea937f77 JK |
1004 | static void nsim_exit_netdevsim(struct netdevsim *ns) |
1005 | { | |
1006 | nsim_udp_tunnels_info_destroy(ns->netdev); | |
1007 | mock_phc_destroy(ns->phc); | |
1008 | } | |
1009 | ||
e05b2d14 JP |
1010 | struct netdevsim * |
1011 | nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) | |
eeeaaf18 | 1012 | { |
e05b2d14 JP |
1013 | struct net_device *dev; |
1014 | struct netdevsim *ns; | |
af9095f0 | 1015 | int err; |
eeeaaf18 | 1016 | |
d4861fc6 PY |
1017 | dev = alloc_netdev_mq(sizeof(*ns), "eth%d", NET_NAME_UNKNOWN, nsim_setup, |
1018 | nsim_dev->nsim_bus_dev->num_queues); | |
e05b2d14 JP |
1019 | if (!dev) |
1020 | return ERR_PTR(-ENOMEM); | |
1daf36c0 | 1021 | |
90d29913 | 1022 | dev_net_set(dev, nsim_dev_net(nsim_dev)); |
e05b2d14 JP |
1023 | ns = netdev_priv(dev); |
1024 | ns->netdev = dev; | |
863a42b2 | 1025 | u64_stats_init(&ns->syncp); |
e05b2d14 JP |
1026 | ns->nsim_dev = nsim_dev; |
1027 | ns->nsim_dev_port = nsim_dev_port; | |
1028 | ns->nsim_bus_dev = nsim_dev->nsim_bus_dev; | |
40e4fe4c | 1029 | SET_NETDEV_DEV(dev, &ns->nsim_bus_dev->dev); |
ac73d4bf | 1030 | SET_NETDEV_DEVLINK_PORT(dev, &nsim_dev_port->devlink_port); |
ff1f7c17 | 1031 | nsim_ethtool_init(ns); |
92ba1f29 DL |
1032 | if (nsim_dev_port_is_pf(nsim_dev_port)) |
1033 | err = nsim_init_netdevsim(ns); | |
1034 | else | |
1035 | err = nsim_init_netdevsim_vf(ns); | |
424be63a JK |
1036 | if (err) |
1037 | goto err_free_netdev; | |
1580cbcb JK |
1038 | |
1039 | ns->pp_dfs = debugfs_create_file("pp_hold", 0600, nsim_dev_port->ddir, | |
1040 | ns, &nsim_pp_hold_fops); | |
6917d207 JK |
1041 | ns->qr_dfs = debugfs_create_file("queue_reset", 0200, |
1042 | nsim_dev_port->ddir, ns, | |
1043 | &nsim_qreset_fops); | |
1580cbcb | 1044 | |
e05b2d14 JP |
1045 | return ns; |
1046 | ||
e05b2d14 JP |
1047 | err_free_netdev: |
1048 | free_netdev(dev); | |
1049 | return ERR_PTR(err); | |
1050 | } | |
1051 | ||
1052 | void nsim_destroy(struct netdevsim *ns) | |
1053 | { | |
1054 | struct net_device *dev = ns->netdev; | |
f532957d | 1055 | struct netdevsim *peer; |
e05b2d14 | 1056 | |
6917d207 | 1057 | debugfs_remove(ns->qr_dfs); |
1580cbcb JK |
1058 | debugfs_remove(ns->pp_dfs); |
1059 | ||
1901066a SF |
1060 | if (ns->nb.notifier_call) |
1061 | unregister_netdevice_notifier_dev_net(ns->netdev, &ns->nb, | |
1062 | &ns->nn); | |
1063 | ||
e05b2d14 | 1064 | rtnl_lock(); |
f532957d DW |
1065 | peer = rtnl_dereference(ns->peer); |
1066 | if (peer) | |
1067 | RCU_INIT_POINTER(peer->peer, NULL); | |
1068 | RCU_INIT_POINTER(ns->peer, NULL); | |
e05b2d14 | 1069 | unregister_netdevice(dev); |
92ba1f29 | 1070 | if (nsim_dev_port_is_pf(ns->nsim_dev_port)) { |
02b34d03 | 1071 | nsim_macsec_teardown(ns); |
92ba1f29 DL |
1072 | nsim_ipsec_teardown(ns); |
1073 | nsim_bpf_uninit(ns); | |
a565dd04 | 1074 | nsim_queue_uninit(ns); |
92ba1f29 | 1075 | } |
e05b2d14 | 1076 | rtnl_unlock(); |
92ba1f29 | 1077 | if (nsim_dev_port_is_pf(ns->nsim_dev_port)) |
ea937f77 | 1078 | nsim_exit_netdevsim(ns); |
1580cbcb JK |
1079 | |
1080 | /* Put this intentionally late to exercise the orphaning path */ | |
1081 | if (ns->page) { | |
1082 | page_pool_put_full_page(ns->page->pp, ns->page, false); | |
1083 | ns->page = NULL; | |
1084 | } | |
1085 | ||
e05b2d14 JP |
1086 | free_netdev(dev); |
1087 | } | |
1088 | ||
f532957d DW |
1089 | bool netdev_is_nsim(struct net_device *dev) |
1090 | { | |
1091 | return dev->netdev_ops == &nsim_netdev_ops; | |
1092 | } | |
1093 | ||
e05b2d14 JP |
1094 | static int nsim_validate(struct nlattr *tb[], struct nlattr *data[], |
1095 | struct netlink_ext_ack *extack) | |
1096 | { | |
d4861fc6 PY |
1097 | NL_SET_ERR_MSG_MOD(extack, |
1098 | "Please use: echo \"[ID] [PORT_COUNT] [NUM_QUEUES]\" > /sys/bus/netdevsim/new_device"); | |
e05b2d14 | 1099 | return -EOPNOTSUPP; |
eeeaaf18 JK |
1100 | } |
1101 | ||
83c9e13a JK |
1102 | static struct rtnl_link_ops nsim_link_ops __read_mostly = { |
1103 | .kind = DRV_NAME, | |
83c9e13a JK |
1104 | .validate = nsim_validate, |
1105 | }; | |
1106 | ||
1107 | static int __init nsim_module_init(void) | |
1108 | { | |
31d3ad83 JK |
1109 | int err; |
1110 | ||
d514f41e | 1111 | err = nsim_dev_init(); |
af9095f0 | 1112 | if (err) |
ab1d0cc0 | 1113 | return err; |
eeeaaf18 | 1114 | |
925f5afe | 1115 | err = nsim_bus_init(); |
31d3ad83 | 1116 | if (err) |
d514f41e | 1117 | goto err_dev_exit; |
31d3ad83 | 1118 | |
59c84b9f DA |
1119 | err = rtnl_link_register(&nsim_link_ops); |
1120 | if (err) | |
a5facc4c | 1121 | goto err_bus_exit; |
59c84b9f | 1122 | |
31d3ad83 JK |
1123 | return 0; |
1124 | ||
925f5afe JP |
1125 | err_bus_exit: |
1126 | nsim_bus_exit(); | |
d514f41e JP |
1127 | err_dev_exit: |
1128 | nsim_dev_exit(); | |
31d3ad83 | 1129 | return err; |
83c9e13a JK |
1130 | } |
1131 | ||
1132 | static void __exit nsim_module_exit(void) | |
1133 | { | |
1134 | rtnl_link_unregister(&nsim_link_ops); | |
925f5afe | 1135 | nsim_bus_exit(); |
d514f41e | 1136 | nsim_dev_exit(); |
83c9e13a JK |
1137 | } |
1138 | ||
1139 | module_init(nsim_module_init); | |
1140 | module_exit(nsim_module_exit); | |
1141 | MODULE_LICENSE("GPL"); | |
1fff1f79 | 1142 | MODULE_DESCRIPTION("Simulated networking device for testing"); |
83c9e13a | 1143 | MODULE_ALIAS_RTNL_LINK(DRV_NAME); |