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