blob: 5f81dd2f4ac76351e399d899fadc3fe1acc2679c [file]
/*
* 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;
}
}