| .. | .. |
|---|
| 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 | +} |
|---|