| /* |
| * Copyright 2018, Data61 |
| * Commonwealth Scientific and Industrial Research Organisation (CSIRO) |
| * ABN 41 687 119 230. |
| * |
| * This software may be distributed and modified according to the terms of |
| * the GNU General Public License version 2. Note that NO WARRANTY is provided. |
| * See "LICENSE_GPLv2.txt" for details. |
| * |
| * @TAG(DATA61_GPL) |
| */ |
| |
| $esc:(#include <linux_hdrs.h>) |
| $esc:(#include <wrapper.h>) |
| #include <abstract.h> |
| #include <generated.c> |
| |
| /* Forward declaration of C functions */ |
| static void cg_loopback_setup_ac(struct net_device *dev); |
| |
| #include <plat/linux/linux_api.ac> |
| |
| /* The higher levels take care of making this non-reentrant (it's |
| * called with bh's disabled). |
| */ |
| static netdev_tx_t cg_loopback_xmit(struct sk_buff *skb, |
| struct net_device *dev) |
| { |
| struct pcpu_lstats *lb_stats; |
| int len; |
| |
| skb_tx_timestamp(skb); |
| skb_orphan(skb); |
| |
| /* Before queueing this packet to netif_rx(), |
| * make sure dst is refcounted. |
| */ |
| skb_dst_force(skb); |
| |
| skb->protocol = eth_type_trans(skb, dev); |
| |
| /* it's OK to use per_cpu_ptr() because BHs are off */ |
| lb_stats = this_cpu_ptr(dev->lstats); |
| |
| len = skb->len; |
| if (likely(netif_rx(skb) == NET_RX_SUCCESS)) { |
| u64_stats_update_begin(&lb_stats->syncp); |
| lb_stats->bytes += len; |
| lb_stats->packets++; |
| u64_stats_update_end(&lb_stats->syncp); |
| } |
| |
| return NETDEV_TX_OK; |
| } |
| |
| static void cg_loopback_get_stats64(struct net_device *dev, |
| struct rtnl_link_stats64 *stats) |
| { |
| u64 bytes = 0; |
| u64 packets = 0; |
| int i; |
| |
| for (i = -1; i = cpumask_next(i, cpu_possible_mask), i < nr_cpu_ids;) { |
| const struct pcpu_lstats *lb_stats; |
| u64 tbytes, tpackets; |
| unsigned int start; |
| |
| lb_stats = per_cpu_ptr(dev->lstats, i); |
| do { |
| start = u64_stats_fetch_begin_irq(&lb_stats->syncp); |
| tbytes = lb_stats->bytes; |
| tpackets = lb_stats->packets; |
| } while (u64_stats_fetch_retry_irq(&lb_stats->syncp, start)); |
| bytes += tbytes; |
| packets += tpackets; |
| } |
| stats->rx_packets = packets; |
| stats->tx_packets = packets; |
| stats->rx_bytes = bytes; |
| stats->tx_bytes = bytes; |
| } |
| |
| static u32 cg_always_on_ac(struct net_device *dev) |
| { |
| $ty:((NetDeviceAbstractType!, U32)) ret; |
| |
| ret = cg_always_on_cg(dev); |
| return ret.p2; |
| } |
| |
| static int cg_loopback_get_ts_info(struct net_device *netdev, |
| struct ethtool_ts_info *ts_info) |
| { |
| $ty:(()) empty; |
| |
| ts_info->so_timestamping = get_ts_info_timestamp_flags_cg(empty); |
| ts_info->phc_index = -1; |
| |
| return 0; |
| } |
| |
| static const struct ethtool_ops cg_loopback_ethtool_ops = { |
| .get_link = cg_always_on_ac, |
| .get_ts_info = cg_loopback_get_ts_info, |
| }; |
| |
| static int cg_loopback_dev_init(struct net_device *dev) |
| { |
| SysState state; |
| $ty:((SysState, NetDeviceAbstractType)) args; |
| $ty:(RR (SysState, NetDeviceAbstractType) () ()) ret; |
| |
| args.p1 = &state; |
| args.p2 = dev; |
| |
| ret = cg_loopback_dev_init_cg(args); |
| if (ret.p2.tag == TAG_ENUM_Success) { |
| return 0; |
| } else { |
| return -ENOMEM; |
| } |
| } |
| |
| static void cg_loopback_dev_free(struct net_device *dev) |
| { |
| SysState state; |
| $ty:((SysState, NetDeviceAbstractType)) args; |
| |
| args.p1 = &state; |
| args.p2 = dev; |
| |
| cg_loopback_dev_free_cg(args); |
| } |
| |
| static const struct net_device_ops cg_loopback_ops = { |
| .ndo_init = cg_loopback_dev_init, |
| .ndo_start_xmit = cg_loopback_xmit, |
| .ndo_get_stats64 = cg_loopback_get_stats64, |
| .ndo_set_mac_address = eth_mac_addr, |
| }; |
| |
| static void cg_loopback_setup_ac(struct net_device *dev) |
| { |
| dev->mtu = 64 * 1024; |
| dev->hard_header_len = ETH_HLEN; /* 14 */ |
| dev->min_header_len = ETH_HLEN; /* 14 */ |
| dev->addr_len = ETH_ALEN; /* 6 */ |
| dev->type = ARPHRD_LOOPBACK; /* 0x0001*/ |
| dev->flags = IFF_LOOPBACK; |
| dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; |
| netif_keep_dst(dev); |
| dev->hw_features = NETIF_F_GSO_SOFTWARE; |
| dev->features = NETIF_F_SG | NETIF_F_FRAGLIST |
| | NETIF_F_GSO_SOFTWARE |
| | NETIF_F_HW_CSUM |
| | NETIF_F_RXCSUM |
| | NETIF_F_SCTP_CRC |
| | NETIF_F_HIGHDMA |
| | NETIF_F_LLTX |
| | NETIF_F_NETNS_LOCAL |
| | NETIF_F_VLAN_CHALLENGED |
| | NETIF_F_LOOPBACK; |
| dev->ethtool_ops = &cg_loopback_ethtool_ops; |
| ether_setup(dev); |
| dev->netdev_ops = &cg_loopback_ops; |
| dev->needs_free_netdev = true; |
| dev->priv_destructor = cg_loopback_dev_free; |
| } |
| |
| int cg_loopback_net_init_ac(struct net *net) |
| { |
| SysState state; |
| $ty:((SysState, NetAbstractType)) args; |
| $ty:(RR (SysState, NetAbstractType) () ()) ret; |
| |
| args.p1 = &state; |
| args.p2 = net; |
| |
| ret = cg_loopback_net_init_cg(args); |
| if (ret.p2.tag == TAG_ENUM_Success) { |
| return 0; |
| } else { |
| return -ENOMEM; |
| } |
| } |