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