.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. |
---|
2 | 3 | * |
---|
3 | | - * This program is free software; you can redistribute it and/or modify |
---|
4 | | - * it under the terms of the GNU General Public License version 2 and |
---|
5 | | - * only version 2 as published by the Free Software Foundation. |
---|
6 | | - * |
---|
7 | | - * This program is distributed in the hope that it will be useful, |
---|
8 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
9 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
10 | | - * GNU General Public License for more details. |
---|
11 | | - * |
---|
12 | | - * |
---|
13 | 4 | * RMNET Data virtual network driver |
---|
14 | | - * |
---|
15 | 5 | */ |
---|
16 | 6 | |
---|
17 | 7 | #include <linux/etherdevice.h> |
---|
.. | .. |
---|
68 | 58 | return NETDEV_TX_OK; |
---|
69 | 59 | } |
---|
70 | 60 | |
---|
| 61 | +static int rmnet_vnd_headroom(struct rmnet_port *port) |
---|
| 62 | +{ |
---|
| 63 | + u32 headroom; |
---|
| 64 | + |
---|
| 65 | + headroom = sizeof(struct rmnet_map_header); |
---|
| 66 | + |
---|
| 67 | + if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) |
---|
| 68 | + headroom += sizeof(struct rmnet_map_ul_csum_header); |
---|
| 69 | + |
---|
| 70 | + return headroom; |
---|
| 71 | +} |
---|
| 72 | + |
---|
71 | 73 | static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) |
---|
72 | 74 | { |
---|
73 | | - if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE) |
---|
| 75 | + struct rmnet_priv *priv = netdev_priv(rmnet_dev); |
---|
| 76 | + struct rmnet_port *port; |
---|
| 77 | + u32 headroom; |
---|
| 78 | + |
---|
| 79 | + port = rmnet_get_port_rtnl(priv->real_dev); |
---|
| 80 | + |
---|
| 81 | + headroom = rmnet_vnd_headroom(port); |
---|
| 82 | + |
---|
| 83 | + if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE || |
---|
| 84 | + new_mtu > (priv->real_dev->mtu - headroom)) |
---|
74 | 85 | return -EINVAL; |
---|
75 | 86 | |
---|
76 | 87 | rmnet_dev->mtu = new_mtu; |
---|
.. | .. |
---|
114 | 125 | struct rtnl_link_stats64 *s) |
---|
115 | 126 | { |
---|
116 | 127 | struct rmnet_priv *priv = netdev_priv(dev); |
---|
117 | | - struct rmnet_vnd_stats total_stats; |
---|
| 128 | + struct rmnet_vnd_stats total_stats = { }; |
---|
118 | 129 | struct rmnet_pcpu_stats *pcpu_ptr; |
---|
| 130 | + struct rmnet_vnd_stats snapshot; |
---|
119 | 131 | unsigned int cpu, start; |
---|
120 | | - |
---|
121 | | - memset(&total_stats, 0, sizeof(struct rmnet_vnd_stats)); |
---|
122 | 132 | |
---|
123 | 133 | for_each_possible_cpu(cpu) { |
---|
124 | 134 | pcpu_ptr = per_cpu_ptr(priv->pcpu_stats, cpu); |
---|
125 | 135 | |
---|
126 | 136 | do { |
---|
127 | 137 | start = u64_stats_fetch_begin_irq(&pcpu_ptr->syncp); |
---|
128 | | - total_stats.rx_pkts += pcpu_ptr->stats.rx_pkts; |
---|
129 | | - total_stats.rx_bytes += pcpu_ptr->stats.rx_bytes; |
---|
130 | | - total_stats.tx_pkts += pcpu_ptr->stats.tx_pkts; |
---|
131 | | - total_stats.tx_bytes += pcpu_ptr->stats.tx_bytes; |
---|
| 138 | + snapshot = pcpu_ptr->stats; /* struct assignment */ |
---|
132 | 139 | } while (u64_stats_fetch_retry_irq(&pcpu_ptr->syncp, start)); |
---|
133 | 140 | |
---|
134 | | - total_stats.tx_drops += pcpu_ptr->stats.tx_drops; |
---|
| 141 | + total_stats.rx_pkts += snapshot.rx_pkts; |
---|
| 142 | + total_stats.rx_bytes += snapshot.rx_bytes; |
---|
| 143 | + total_stats.tx_pkts += snapshot.tx_pkts; |
---|
| 144 | + total_stats.tx_bytes += snapshot.tx_bytes; |
---|
| 145 | + total_stats.tx_drops += snapshot.tx_drops; |
---|
135 | 146 | } |
---|
136 | 147 | |
---|
137 | 148 | s->rx_packets = total_stats.rx_pkts; |
---|
.. | .. |
---|
222 | 233 | rmnet_dev->needs_free_netdev = true; |
---|
223 | 234 | rmnet_dev->ethtool_ops = &rmnet_ethtool_ops; |
---|
224 | 235 | |
---|
| 236 | + rmnet_dev->features |= NETIF_F_LLTX; |
---|
| 237 | + |
---|
225 | 238 | /* This perm addr will be used as interface identifier by IPv6 */ |
---|
226 | 239 | rmnet_dev->addr_assign_type = NET_ADDR_RANDOM; |
---|
227 | 240 | eth_random_addr(rmnet_dev->perm_addr); |
---|
.. | .. |
---|
232 | 245 | int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, |
---|
233 | 246 | struct rmnet_port *port, |
---|
234 | 247 | struct net_device *real_dev, |
---|
235 | | - struct rmnet_endpoint *ep) |
---|
| 248 | + struct rmnet_endpoint *ep, |
---|
| 249 | + struct netlink_ext_ack *extack) |
---|
| 250 | + |
---|
236 | 251 | { |
---|
237 | 252 | struct rmnet_priv *priv = netdev_priv(rmnet_dev); |
---|
| 253 | + u32 headroom; |
---|
238 | 254 | int rc; |
---|
239 | 255 | |
---|
240 | | - if (ep->egress_dev) |
---|
241 | | - return -EINVAL; |
---|
242 | | - |
---|
243 | | - if (rmnet_get_endpoint(port, id)) |
---|
| 256 | + if (rmnet_get_endpoint(port, id)) { |
---|
| 257 | + NL_SET_ERR_MSG_MOD(extack, "MUX ID already exists"); |
---|
244 | 258 | return -EBUSY; |
---|
| 259 | + } |
---|
245 | 260 | |
---|
246 | 261 | rmnet_dev->hw_features = NETIF_F_RXCSUM; |
---|
247 | 262 | rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; |
---|
248 | 263 | rmnet_dev->hw_features |= NETIF_F_SG; |
---|
249 | 264 | |
---|
250 | 265 | priv->real_dev = real_dev; |
---|
| 266 | + |
---|
| 267 | + headroom = rmnet_vnd_headroom(port); |
---|
| 268 | + |
---|
| 269 | + if (rmnet_vnd_change_mtu(rmnet_dev, real_dev->mtu - headroom)) { |
---|
| 270 | + NL_SET_ERR_MSG_MOD(extack, "Invalid MTU on real dev"); |
---|
| 271 | + return -EINVAL; |
---|
| 272 | + } |
---|
251 | 273 | |
---|
252 | 274 | rc = register_netdevice(rmnet_dev); |
---|
253 | 275 | if (!rc) { |
---|
.. | .. |
---|
290 | 312 | |
---|
291 | 313 | return 0; |
---|
292 | 314 | } |
---|
| 315 | + |
---|
| 316 | +int rmnet_vnd_validate_real_dev_mtu(struct net_device *real_dev) |
---|
| 317 | +{ |
---|
| 318 | + struct hlist_node *tmp_ep; |
---|
| 319 | + struct rmnet_endpoint *ep; |
---|
| 320 | + struct rmnet_port *port; |
---|
| 321 | + unsigned long bkt_ep; |
---|
| 322 | + u32 headroom; |
---|
| 323 | + |
---|
| 324 | + port = rmnet_get_port_rtnl(real_dev); |
---|
| 325 | + |
---|
| 326 | + headroom = rmnet_vnd_headroom(port); |
---|
| 327 | + |
---|
| 328 | + hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) { |
---|
| 329 | + if (ep->egress_dev->mtu > (real_dev->mtu - headroom)) |
---|
| 330 | + return -1; |
---|
| 331 | + } |
---|
| 332 | + |
---|
| 333 | + return 0; |
---|
| 334 | +} |
---|
| 335 | + |
---|
| 336 | +int rmnet_vnd_update_dev_mtu(struct rmnet_port *port, |
---|
| 337 | + struct net_device *real_dev) |
---|
| 338 | +{ |
---|
| 339 | + struct hlist_node *tmp_ep; |
---|
| 340 | + struct rmnet_endpoint *ep; |
---|
| 341 | + unsigned long bkt_ep; |
---|
| 342 | + u32 headroom; |
---|
| 343 | + |
---|
| 344 | + headroom = rmnet_vnd_headroom(port); |
---|
| 345 | + |
---|
| 346 | + hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) { |
---|
| 347 | + if (ep->egress_dev->mtu <= (real_dev->mtu - headroom)) |
---|
| 348 | + continue; |
---|
| 349 | + |
---|
| 350 | + if (rmnet_vnd_change_mtu(ep->egress_dev, |
---|
| 351 | + real_dev->mtu - headroom)) |
---|
| 352 | + return -1; |
---|
| 353 | + } |
---|
| 354 | + |
---|
| 355 | + return 0; |
---|
| 356 | +} |
---|