162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * RMNET Data virtual network driver 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/etherdevice.h> 862306a36Sopenharmony_ci#include <linux/ethtool.h> 962306a36Sopenharmony_ci#include <linux/if_arp.h> 1062306a36Sopenharmony_ci#include <net/pkt_sched.h> 1162306a36Sopenharmony_ci#include "rmnet_config.h" 1262306a36Sopenharmony_ci#include "rmnet_handlers.h" 1362306a36Sopenharmony_ci#include "rmnet_private.h" 1462306a36Sopenharmony_ci#include "rmnet_map.h" 1562306a36Sopenharmony_ci#include "rmnet_vnd.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* RX/TX Fixup */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_civoid rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 2262306a36Sopenharmony_ci struct rmnet_pcpu_stats *pcpu_ptr; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci pcpu_ptr = this_cpu_ptr(priv->pcpu_stats); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci u64_stats_update_begin(&pcpu_ptr->syncp); 2762306a36Sopenharmony_ci pcpu_ptr->stats.rx_pkts++; 2862306a36Sopenharmony_ci pcpu_ptr->stats.rx_bytes += skb->len; 2962306a36Sopenharmony_ci u64_stats_update_end(&pcpu_ptr->syncp); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_civoid rmnet_vnd_tx_fixup_len(unsigned int len, struct net_device *dev) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 3562306a36Sopenharmony_ci struct rmnet_pcpu_stats *pcpu_ptr; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci pcpu_ptr = this_cpu_ptr(priv->pcpu_stats); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci u64_stats_update_begin(&pcpu_ptr->syncp); 4062306a36Sopenharmony_ci pcpu_ptr->stats.tx_pkts++; 4162306a36Sopenharmony_ci pcpu_ptr->stats.tx_bytes += len; 4262306a36Sopenharmony_ci u64_stats_update_end(&pcpu_ptr->syncp); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_civoid rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci rmnet_vnd_tx_fixup_len(skb->len, dev); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* Network Device Operations */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, 5362306a36Sopenharmony_ci struct net_device *dev) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct rmnet_priv *priv; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci priv = netdev_priv(dev); 5862306a36Sopenharmony_ci if (priv->real_dev) { 5962306a36Sopenharmony_ci rmnet_egress_handler(skb); 6062306a36Sopenharmony_ci } else { 6162306a36Sopenharmony_ci this_cpu_inc(priv->pcpu_stats->stats.tx_drops); 6262306a36Sopenharmony_ci kfree_skb(skb); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci return NETDEV_TX_OK; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int rmnet_vnd_headroom(struct rmnet_port *port) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci u32 headroom; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci headroom = sizeof(struct rmnet_map_header); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) 7462306a36Sopenharmony_ci headroom += sizeof(struct rmnet_map_ul_csum_header); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return headroom; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(rmnet_dev); 8262306a36Sopenharmony_ci struct rmnet_port *port; 8362306a36Sopenharmony_ci u32 headroom; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci port = rmnet_get_port_rtnl(priv->real_dev); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci headroom = rmnet_vnd_headroom(port); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE || 9062306a36Sopenharmony_ci new_mtu > (priv->real_dev->mtu - headroom)) 9162306a36Sopenharmony_ci return -EINVAL; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci rmnet_dev->mtu = new_mtu; 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int rmnet_vnd_get_iflink(const struct net_device *dev) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return priv->real_dev->ifindex; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int rmnet_vnd_init(struct net_device *dev) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 10762306a36Sopenharmony_ci int err; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci priv->pcpu_stats = alloc_percpu(struct rmnet_pcpu_stats); 11062306a36Sopenharmony_ci if (!priv->pcpu_stats) 11162306a36Sopenharmony_ci return -ENOMEM; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci err = gro_cells_init(&priv->gro_cells, dev); 11462306a36Sopenharmony_ci if (err) { 11562306a36Sopenharmony_ci free_percpu(priv->pcpu_stats); 11662306a36Sopenharmony_ci return err; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void rmnet_vnd_uninit(struct net_device *dev) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci gro_cells_destroy(&priv->gro_cells); 12762306a36Sopenharmony_ci free_percpu(priv->pcpu_stats); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void rmnet_get_stats64(struct net_device *dev, 13162306a36Sopenharmony_ci struct rtnl_link_stats64 *s) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 13462306a36Sopenharmony_ci struct rmnet_vnd_stats total_stats = { }; 13562306a36Sopenharmony_ci struct rmnet_pcpu_stats *pcpu_ptr; 13662306a36Sopenharmony_ci struct rmnet_vnd_stats snapshot; 13762306a36Sopenharmony_ci unsigned int cpu, start; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 14062306a36Sopenharmony_ci pcpu_ptr = per_cpu_ptr(priv->pcpu_stats, cpu); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci do { 14362306a36Sopenharmony_ci start = u64_stats_fetch_begin(&pcpu_ptr->syncp); 14462306a36Sopenharmony_ci snapshot = pcpu_ptr->stats; /* struct assignment */ 14562306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&pcpu_ptr->syncp, start)); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci total_stats.rx_pkts += snapshot.rx_pkts; 14862306a36Sopenharmony_ci total_stats.rx_bytes += snapshot.rx_bytes; 14962306a36Sopenharmony_ci total_stats.tx_pkts += snapshot.tx_pkts; 15062306a36Sopenharmony_ci total_stats.tx_bytes += snapshot.tx_bytes; 15162306a36Sopenharmony_ci total_stats.tx_drops += snapshot.tx_drops; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci s->rx_packets = total_stats.rx_pkts; 15562306a36Sopenharmony_ci s->rx_bytes = total_stats.rx_bytes; 15662306a36Sopenharmony_ci s->tx_packets = total_stats.tx_pkts; 15762306a36Sopenharmony_ci s->tx_bytes = total_stats.tx_bytes; 15862306a36Sopenharmony_ci s->tx_dropped = total_stats.tx_drops; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic const struct net_device_ops rmnet_vnd_ops = { 16262306a36Sopenharmony_ci .ndo_start_xmit = rmnet_vnd_start_xmit, 16362306a36Sopenharmony_ci .ndo_change_mtu = rmnet_vnd_change_mtu, 16462306a36Sopenharmony_ci .ndo_get_iflink = rmnet_vnd_get_iflink, 16562306a36Sopenharmony_ci .ndo_add_slave = rmnet_add_bridge, 16662306a36Sopenharmony_ci .ndo_del_slave = rmnet_del_bridge, 16762306a36Sopenharmony_ci .ndo_init = rmnet_vnd_init, 16862306a36Sopenharmony_ci .ndo_uninit = rmnet_vnd_uninit, 16962306a36Sopenharmony_ci .ndo_get_stats64 = rmnet_get_stats64, 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic const char rmnet_gstrings_stats[][ETH_GSTRING_LEN] = { 17362306a36Sopenharmony_ci "Checksum ok", 17462306a36Sopenharmony_ci "Bad IPv4 header checksum", 17562306a36Sopenharmony_ci "Checksum valid bit not set", 17662306a36Sopenharmony_ci "Checksum validation failed", 17762306a36Sopenharmony_ci "Checksum error bad buffer", 17862306a36Sopenharmony_ci "Checksum error bad ip version", 17962306a36Sopenharmony_ci "Checksum error bad transport", 18062306a36Sopenharmony_ci "Checksum skipped on ip fragment", 18162306a36Sopenharmony_ci "Checksum skipped", 18262306a36Sopenharmony_ci "Checksum computed in software", 18362306a36Sopenharmony_ci "Checksum computed in hardware", 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic void rmnet_get_strings(struct net_device *dev, u32 stringset, u8 *buf) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci switch (stringset) { 18962306a36Sopenharmony_ci case ETH_SS_STATS: 19062306a36Sopenharmony_ci memcpy(buf, &rmnet_gstrings_stats, 19162306a36Sopenharmony_ci sizeof(rmnet_gstrings_stats)); 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int rmnet_get_sset_count(struct net_device *dev, int sset) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci switch (sset) { 19962306a36Sopenharmony_ci case ETH_SS_STATS: 20062306a36Sopenharmony_ci return ARRAY_SIZE(rmnet_gstrings_stats); 20162306a36Sopenharmony_ci default: 20262306a36Sopenharmony_ci return -EOPNOTSUPP; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void rmnet_get_ethtool_stats(struct net_device *dev, 20762306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 21062306a36Sopenharmony_ci struct rmnet_priv_stats *st = &priv->stats; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (!data) 21362306a36Sopenharmony_ci return; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci memcpy(data, st, ARRAY_SIZE(rmnet_gstrings_stats) * sizeof(u64)); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int rmnet_get_coalesce(struct net_device *dev, 21962306a36Sopenharmony_ci struct ethtool_coalesce *coal, 22062306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 22162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 22462306a36Sopenharmony_ci struct rmnet_port *port; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci port = rmnet_get_port_rtnl(priv->real_dev); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci memset(kernel_coal, 0, sizeof(*kernel_coal)); 22962306a36Sopenharmony_ci kernel_coal->tx_aggr_max_bytes = port->egress_agg_params.bytes; 23062306a36Sopenharmony_ci kernel_coal->tx_aggr_max_frames = port->egress_agg_params.count; 23162306a36Sopenharmony_ci kernel_coal->tx_aggr_time_usecs = div_u64(port->egress_agg_params.time_nsec, 23262306a36Sopenharmony_ci NSEC_PER_USEC); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int rmnet_set_coalesce(struct net_device *dev, 23862306a36Sopenharmony_ci struct ethtool_coalesce *coal, 23962306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 24062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(dev); 24362306a36Sopenharmony_ci struct rmnet_port *port; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci port = rmnet_get_port_rtnl(priv->real_dev); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (kernel_coal->tx_aggr_max_frames < 1 || kernel_coal->tx_aggr_max_frames > 64) 24862306a36Sopenharmony_ci return -EINVAL; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (kernel_coal->tx_aggr_max_bytes > 32768) 25162306a36Sopenharmony_ci return -EINVAL; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci rmnet_map_update_ul_agg_config(port, kernel_coal->tx_aggr_max_bytes, 25462306a36Sopenharmony_ci kernel_coal->tx_aggr_max_frames, 25562306a36Sopenharmony_ci kernel_coal->tx_aggr_time_usecs); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic const struct ethtool_ops rmnet_ethtool_ops = { 26162306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_TX_AGGR, 26262306a36Sopenharmony_ci .get_coalesce = rmnet_get_coalesce, 26362306a36Sopenharmony_ci .set_coalesce = rmnet_set_coalesce, 26462306a36Sopenharmony_ci .get_ethtool_stats = rmnet_get_ethtool_stats, 26562306a36Sopenharmony_ci .get_strings = rmnet_get_strings, 26662306a36Sopenharmony_ci .get_sset_count = rmnet_get_sset_count, 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* Called by kernel whenever a new rmnet<n> device is created. Sets MTU, 27062306a36Sopenharmony_ci * flags, ARP type, needed headroom, etc... 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_civoid rmnet_vnd_setup(struct net_device *rmnet_dev) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci rmnet_dev->netdev_ops = &rmnet_vnd_ops; 27562306a36Sopenharmony_ci rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE; 27662306a36Sopenharmony_ci rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM; 27762306a36Sopenharmony_ci eth_hw_addr_random(rmnet_dev); 27862306a36Sopenharmony_ci rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Raw IP mode */ 28162306a36Sopenharmony_ci rmnet_dev->header_ops = NULL; /* No header */ 28262306a36Sopenharmony_ci rmnet_dev->type = ARPHRD_RAWIP; 28362306a36Sopenharmony_ci rmnet_dev->hard_header_len = 0; 28462306a36Sopenharmony_ci rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci rmnet_dev->needs_free_netdev = true; 28762306a36Sopenharmony_ci rmnet_dev->ethtool_ops = &rmnet_ethtool_ops; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci rmnet_dev->features |= NETIF_F_LLTX; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* This perm addr will be used as interface identifier by IPv6 */ 29262306a36Sopenharmony_ci rmnet_dev->addr_assign_type = NET_ADDR_RANDOM; 29362306a36Sopenharmony_ci eth_random_addr(rmnet_dev->perm_addr); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/* Exposed API */ 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ciint rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, 29962306a36Sopenharmony_ci struct rmnet_port *port, 30062306a36Sopenharmony_ci struct net_device *real_dev, 30162306a36Sopenharmony_ci struct rmnet_endpoint *ep, 30262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(rmnet_dev); 30662306a36Sopenharmony_ci u32 headroom; 30762306a36Sopenharmony_ci int rc; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (rmnet_get_endpoint(port, id)) { 31062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "MUX ID already exists"); 31162306a36Sopenharmony_ci return -EBUSY; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci rmnet_dev->hw_features = NETIF_F_RXCSUM; 31562306a36Sopenharmony_ci rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; 31662306a36Sopenharmony_ci rmnet_dev->hw_features |= NETIF_F_SG; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci priv->real_dev = real_dev; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci headroom = rmnet_vnd_headroom(port); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (rmnet_vnd_change_mtu(rmnet_dev, real_dev->mtu - headroom)) { 32362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid MTU on real dev"); 32462306a36Sopenharmony_ci return -EINVAL; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci rc = register_netdevice(rmnet_dev); 32862306a36Sopenharmony_ci if (!rc) { 32962306a36Sopenharmony_ci ep->egress_dev = rmnet_dev; 33062306a36Sopenharmony_ci ep->mux_id = id; 33162306a36Sopenharmony_ci port->nr_rmnet_devs++; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci rmnet_dev->rtnl_link_ops = &rmnet_link_ops; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci priv->mux_id = id; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci netdev_dbg(rmnet_dev, "rmnet dev created\n"); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return rc; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ciint rmnet_vnd_dellink(u8 id, struct rmnet_port *port, 34462306a36Sopenharmony_ci struct rmnet_endpoint *ep) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci if (id >= RMNET_MAX_LOGICAL_EP || !ep->egress_dev) 34762306a36Sopenharmony_ci return -EINVAL; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ep->egress_dev = NULL; 35062306a36Sopenharmony_ci port->nr_rmnet_devs--; 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ciint rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci netdev_dbg(rmnet_dev, "Setting VND TX queue state to %d\n", enable); 35762306a36Sopenharmony_ci /* Although we expect similar number of enable/disable 35862306a36Sopenharmony_ci * commands, optimize for the disable. That is more 35962306a36Sopenharmony_ci * latency sensitive than enable 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci if (unlikely(enable)) 36262306a36Sopenharmony_ci netif_wake_queue(rmnet_dev); 36362306a36Sopenharmony_ci else 36462306a36Sopenharmony_ci netif_stop_queue(rmnet_dev); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciint rmnet_vnd_validate_real_dev_mtu(struct net_device *real_dev) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct hlist_node *tmp_ep; 37262306a36Sopenharmony_ci struct rmnet_endpoint *ep; 37362306a36Sopenharmony_ci struct rmnet_port *port; 37462306a36Sopenharmony_ci unsigned long bkt_ep; 37562306a36Sopenharmony_ci u32 headroom; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci port = rmnet_get_port_rtnl(real_dev); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci headroom = rmnet_vnd_headroom(port); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) { 38262306a36Sopenharmony_ci if (ep->egress_dev->mtu > (real_dev->mtu - headroom)) 38362306a36Sopenharmony_ci return -1; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciint rmnet_vnd_update_dev_mtu(struct rmnet_port *port, 39062306a36Sopenharmony_ci struct net_device *real_dev) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct hlist_node *tmp_ep; 39362306a36Sopenharmony_ci struct rmnet_endpoint *ep; 39462306a36Sopenharmony_ci unsigned long bkt_ep; 39562306a36Sopenharmony_ci u32 headroom; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci headroom = rmnet_vnd_headroom(port); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) { 40062306a36Sopenharmony_ci if (ep->egress_dev->mtu <= (real_dev->mtu - headroom)) 40162306a36Sopenharmony_ci continue; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (rmnet_vnd_change_mtu(ep->egress_dev, 40462306a36Sopenharmony_ci real_dev->mtu - headroom)) 40562306a36Sopenharmony_ci return -1; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 410