18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * RMNET Data virtual network driver 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 88c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 98c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 108c2ecf20Sopenharmony_ci#include "rmnet_config.h" 118c2ecf20Sopenharmony_ci#include "rmnet_handlers.h" 128c2ecf20Sopenharmony_ci#include "rmnet_private.h" 138c2ecf20Sopenharmony_ci#include "rmnet_map.h" 148c2ecf20Sopenharmony_ci#include "rmnet_vnd.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* RX/TX Fixup */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_civoid rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 218c2ecf20Sopenharmony_ci struct rmnet_pcpu_stats *pcpu_ptr; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci pcpu_ptr = this_cpu_ptr(priv->pcpu_stats); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci u64_stats_update_begin(&pcpu_ptr->syncp); 268c2ecf20Sopenharmony_ci pcpu_ptr->stats.rx_pkts++; 278c2ecf20Sopenharmony_ci pcpu_ptr->stats.rx_bytes += skb->len; 288c2ecf20Sopenharmony_ci u64_stats_update_end(&pcpu_ptr->syncp); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_civoid rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 348c2ecf20Sopenharmony_ci struct rmnet_pcpu_stats *pcpu_ptr; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci pcpu_ptr = this_cpu_ptr(priv->pcpu_stats); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci u64_stats_update_begin(&pcpu_ptr->syncp); 398c2ecf20Sopenharmony_ci pcpu_ptr->stats.tx_pkts++; 408c2ecf20Sopenharmony_ci pcpu_ptr->stats.tx_bytes += skb->len; 418c2ecf20Sopenharmony_ci u64_stats_update_end(&pcpu_ptr->syncp); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Network Device Operations */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, 478c2ecf20Sopenharmony_ci struct net_device *dev) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct rmnet_priv *priv; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 528c2ecf20Sopenharmony_ci if (priv->real_dev) { 538c2ecf20Sopenharmony_ci rmnet_egress_handler(skb); 548c2ecf20Sopenharmony_ci } else { 558c2ecf20Sopenharmony_ci this_cpu_inc(priv->pcpu_stats->stats.tx_drops); 568c2ecf20Sopenharmony_ci kfree_skb(skb); 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int rmnet_vnd_headroom(struct rmnet_port *port) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci u32 headroom; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci headroom = sizeof(struct rmnet_map_header); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) 688c2ecf20Sopenharmony_ci headroom += sizeof(struct rmnet_map_ul_csum_header); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return headroom; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(rmnet_dev); 768c2ecf20Sopenharmony_ci struct rmnet_port *port; 778c2ecf20Sopenharmony_ci u32 headroom; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci port = rmnet_get_port_rtnl(priv->real_dev); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci headroom = rmnet_vnd_headroom(port); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE || 848c2ecf20Sopenharmony_ci new_mtu > (priv->real_dev->mtu - headroom)) 858c2ecf20Sopenharmony_ci return -EINVAL; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci rmnet_dev->mtu = new_mtu; 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int rmnet_vnd_get_iflink(const struct net_device *dev) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return priv->real_dev->ifindex; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int rmnet_vnd_init(struct net_device *dev) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 1018c2ecf20Sopenharmony_ci int err; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci priv->pcpu_stats = alloc_percpu(struct rmnet_pcpu_stats); 1048c2ecf20Sopenharmony_ci if (!priv->pcpu_stats) 1058c2ecf20Sopenharmony_ci return -ENOMEM; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci err = gro_cells_init(&priv->gro_cells, dev); 1088c2ecf20Sopenharmony_ci if (err) { 1098c2ecf20Sopenharmony_ci free_percpu(priv->pcpu_stats); 1108c2ecf20Sopenharmony_ci return err; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void rmnet_vnd_uninit(struct net_device *dev) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci gro_cells_destroy(&priv->gro_cells); 1218c2ecf20Sopenharmony_ci free_percpu(priv->pcpu_stats); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void rmnet_get_stats64(struct net_device *dev, 1258c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *s) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 1288c2ecf20Sopenharmony_ci struct rmnet_vnd_stats total_stats = { }; 1298c2ecf20Sopenharmony_ci struct rmnet_pcpu_stats *pcpu_ptr; 1308c2ecf20Sopenharmony_ci struct rmnet_vnd_stats snapshot; 1318c2ecf20Sopenharmony_ci unsigned int cpu, start; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 1348c2ecf20Sopenharmony_ci pcpu_ptr = per_cpu_ptr(priv->pcpu_stats, cpu); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci do { 1378c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&pcpu_ptr->syncp); 1388c2ecf20Sopenharmony_ci snapshot = pcpu_ptr->stats; /* struct assignment */ 1398c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&pcpu_ptr->syncp, start)); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci total_stats.rx_pkts += snapshot.rx_pkts; 1428c2ecf20Sopenharmony_ci total_stats.rx_bytes += snapshot.rx_bytes; 1438c2ecf20Sopenharmony_ci total_stats.tx_pkts += snapshot.tx_pkts; 1448c2ecf20Sopenharmony_ci total_stats.tx_bytes += snapshot.tx_bytes; 1458c2ecf20Sopenharmony_ci total_stats.tx_drops += snapshot.tx_drops; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci s->rx_packets = total_stats.rx_pkts; 1498c2ecf20Sopenharmony_ci s->rx_bytes = total_stats.rx_bytes; 1508c2ecf20Sopenharmony_ci s->tx_packets = total_stats.tx_pkts; 1518c2ecf20Sopenharmony_ci s->tx_bytes = total_stats.tx_bytes; 1528c2ecf20Sopenharmony_ci s->tx_dropped = total_stats.tx_drops; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic const struct net_device_ops rmnet_vnd_ops = { 1568c2ecf20Sopenharmony_ci .ndo_start_xmit = rmnet_vnd_start_xmit, 1578c2ecf20Sopenharmony_ci .ndo_change_mtu = rmnet_vnd_change_mtu, 1588c2ecf20Sopenharmony_ci .ndo_get_iflink = rmnet_vnd_get_iflink, 1598c2ecf20Sopenharmony_ci .ndo_add_slave = rmnet_add_bridge, 1608c2ecf20Sopenharmony_ci .ndo_del_slave = rmnet_del_bridge, 1618c2ecf20Sopenharmony_ci .ndo_init = rmnet_vnd_init, 1628c2ecf20Sopenharmony_ci .ndo_uninit = rmnet_vnd_uninit, 1638c2ecf20Sopenharmony_ci .ndo_get_stats64 = rmnet_get_stats64, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const char rmnet_gstrings_stats[][ETH_GSTRING_LEN] = { 1678c2ecf20Sopenharmony_ci "Checksum ok", 1688c2ecf20Sopenharmony_ci "Checksum valid bit not set", 1698c2ecf20Sopenharmony_ci "Checksum validation failed", 1708c2ecf20Sopenharmony_ci "Checksum error bad buffer", 1718c2ecf20Sopenharmony_ci "Checksum error bad ip version", 1728c2ecf20Sopenharmony_ci "Checksum error bad transport", 1738c2ecf20Sopenharmony_ci "Checksum skipped on ip fragment", 1748c2ecf20Sopenharmony_ci "Checksum skipped", 1758c2ecf20Sopenharmony_ci "Checksum computed in software", 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void rmnet_get_strings(struct net_device *dev, u32 stringset, u8 *buf) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci switch (stringset) { 1818c2ecf20Sopenharmony_ci case ETH_SS_STATS: 1828c2ecf20Sopenharmony_ci memcpy(buf, &rmnet_gstrings_stats, 1838c2ecf20Sopenharmony_ci sizeof(rmnet_gstrings_stats)); 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int rmnet_get_sset_count(struct net_device *dev, int sset) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci switch (sset) { 1918c2ecf20Sopenharmony_ci case ETH_SS_STATS: 1928c2ecf20Sopenharmony_ci return ARRAY_SIZE(rmnet_gstrings_stats); 1938c2ecf20Sopenharmony_ci default: 1948c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void rmnet_get_ethtool_stats(struct net_device *dev, 1998c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 2028c2ecf20Sopenharmony_ci struct rmnet_priv_stats *st = &priv->stats; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (!data) 2058c2ecf20Sopenharmony_ci return; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci memcpy(data, st, ARRAY_SIZE(rmnet_gstrings_stats) * sizeof(u64)); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic const struct ethtool_ops rmnet_ethtool_ops = { 2118c2ecf20Sopenharmony_ci .get_ethtool_stats = rmnet_get_ethtool_stats, 2128c2ecf20Sopenharmony_ci .get_strings = rmnet_get_strings, 2138c2ecf20Sopenharmony_ci .get_sset_count = rmnet_get_sset_count, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* Called by kernel whenever a new rmnet<n> device is created. Sets MTU, 2178c2ecf20Sopenharmony_ci * flags, ARP type, needed headroom, etc... 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_civoid rmnet_vnd_setup(struct net_device *rmnet_dev) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci rmnet_dev->netdev_ops = &rmnet_vnd_ops; 2228c2ecf20Sopenharmony_ci rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE; 2238c2ecf20Sopenharmony_ci rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM; 2248c2ecf20Sopenharmony_ci eth_random_addr(rmnet_dev->dev_addr); 2258c2ecf20Sopenharmony_ci rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* Raw IP mode */ 2288c2ecf20Sopenharmony_ci rmnet_dev->header_ops = NULL; /* No header */ 2298c2ecf20Sopenharmony_ci rmnet_dev->type = ARPHRD_RAWIP; 2308c2ecf20Sopenharmony_ci rmnet_dev->hard_header_len = 0; 2318c2ecf20Sopenharmony_ci rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci rmnet_dev->needs_free_netdev = true; 2348c2ecf20Sopenharmony_ci rmnet_dev->ethtool_ops = &rmnet_ethtool_ops; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci rmnet_dev->features |= NETIF_F_LLTX; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* This perm addr will be used as interface identifier by IPv6 */ 2398c2ecf20Sopenharmony_ci rmnet_dev->addr_assign_type = NET_ADDR_RANDOM; 2408c2ecf20Sopenharmony_ci eth_random_addr(rmnet_dev->perm_addr); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* Exposed API */ 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciint rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, 2468c2ecf20Sopenharmony_ci struct rmnet_port *port, 2478c2ecf20Sopenharmony_ci struct net_device *real_dev, 2488c2ecf20Sopenharmony_ci struct rmnet_endpoint *ep, 2498c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(rmnet_dev); 2538c2ecf20Sopenharmony_ci u32 headroom; 2548c2ecf20Sopenharmony_ci int rc; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (rmnet_get_endpoint(port, id)) { 2578c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "MUX ID already exists"); 2588c2ecf20Sopenharmony_ci return -EBUSY; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci rmnet_dev->hw_features = NETIF_F_RXCSUM; 2628c2ecf20Sopenharmony_ci rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; 2638c2ecf20Sopenharmony_ci rmnet_dev->hw_features |= NETIF_F_SG; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci priv->real_dev = real_dev; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci headroom = rmnet_vnd_headroom(port); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (rmnet_vnd_change_mtu(rmnet_dev, real_dev->mtu - headroom)) { 2708c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid MTU on real dev"); 2718c2ecf20Sopenharmony_ci return -EINVAL; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci rc = register_netdevice(rmnet_dev); 2758c2ecf20Sopenharmony_ci if (!rc) { 2768c2ecf20Sopenharmony_ci ep->egress_dev = rmnet_dev; 2778c2ecf20Sopenharmony_ci ep->mux_id = id; 2788c2ecf20Sopenharmony_ci port->nr_rmnet_devs++; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci rmnet_dev->rtnl_link_ops = &rmnet_link_ops; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci priv->mux_id = id; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci netdev_dbg(rmnet_dev, "rmnet dev created\n"); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return rc; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ciint rmnet_vnd_dellink(u8 id, struct rmnet_port *port, 2918c2ecf20Sopenharmony_ci struct rmnet_endpoint *ep) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci if (id >= RMNET_MAX_LOGICAL_EP || !ep->egress_dev) 2948c2ecf20Sopenharmony_ci return -EINVAL; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci ep->egress_dev = NULL; 2978c2ecf20Sopenharmony_ci port->nr_rmnet_devs--; 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ciint rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci netdev_dbg(rmnet_dev, "Setting VND TX queue state to %d\n", enable); 3048c2ecf20Sopenharmony_ci /* Although we expect similar number of enable/disable 3058c2ecf20Sopenharmony_ci * commands, optimize for the disable. That is more 3068c2ecf20Sopenharmony_ci * latency sensitive than enable 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci if (unlikely(enable)) 3098c2ecf20Sopenharmony_ci netif_wake_queue(rmnet_dev); 3108c2ecf20Sopenharmony_ci else 3118c2ecf20Sopenharmony_ci netif_stop_queue(rmnet_dev); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ciint rmnet_vnd_validate_real_dev_mtu(struct net_device *real_dev) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct hlist_node *tmp_ep; 3198c2ecf20Sopenharmony_ci struct rmnet_endpoint *ep; 3208c2ecf20Sopenharmony_ci struct rmnet_port *port; 3218c2ecf20Sopenharmony_ci unsigned long bkt_ep; 3228c2ecf20Sopenharmony_ci u32 headroom; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci port = rmnet_get_port_rtnl(real_dev); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci headroom = rmnet_vnd_headroom(port); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) { 3298c2ecf20Sopenharmony_ci if (ep->egress_dev->mtu > (real_dev->mtu - headroom)) 3308c2ecf20Sopenharmony_ci return -1; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ciint rmnet_vnd_update_dev_mtu(struct rmnet_port *port, 3378c2ecf20Sopenharmony_ci struct net_device *real_dev) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct hlist_node *tmp_ep; 3408c2ecf20Sopenharmony_ci struct rmnet_endpoint *ep; 3418c2ecf20Sopenharmony_ci unsigned long bkt_ep; 3428c2ecf20Sopenharmony_ci u32 headroom; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci headroom = rmnet_vnd_headroom(port); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) { 3478c2ecf20Sopenharmony_ci if (ep->egress_dev->mtu <= (real_dev->mtu - headroom)) 3488c2ecf20Sopenharmony_ci continue; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (rmnet_vnd_change_mtu(ep->egress_dev, 3518c2ecf20Sopenharmony_ci real_dev->mtu - headroom)) 3528c2ecf20Sopenharmony_ci return -1; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci} 357