diff options
Diffstat (limited to 'drivers/net/ethernet/qualcomm')
-rw-r--r-- | drivers/net/ethernet/qualcomm/emac/emac-mac.c | 11 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 140 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/emac/emac-sgmii.h | 32 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/emac/emac.c | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | 13 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c | 21 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c | 24 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 64 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 55 |
9 files changed, 252 insertions, 117 deletions
diff --git a/drivers/net/ethernet/qualcomm/emac/emac-mac.c b/drivers/net/ethernet/qualcomm/emac/emac-mac.c index d5a32b7c7dc5..031f6e6ee9c1 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac-mac.c +++ b/drivers/net/ethernet/qualcomm/emac/emac-mac.c @@ -683,10 +683,11 @@ static int emac_tx_q_desc_alloc(struct emac_adapter *adpt, struct emac_tx_queue *tx_q) { struct emac_ring_header *ring_header = &adpt->ring_header; + int node = dev_to_node(adpt->netdev->dev.parent); size_t size; size = sizeof(struct emac_buffer) * tx_q->tpd.count; - tx_q->tpd.tpbuff = kzalloc(size, GFP_KERNEL); + tx_q->tpd.tpbuff = kzalloc_node(size, GFP_KERNEL, node); if (!tx_q->tpd.tpbuff) return -ENOMEM; @@ -723,11 +724,12 @@ static void emac_rx_q_bufs_free(struct emac_adapter *adpt) static int emac_rx_descs_alloc(struct emac_adapter *adpt) { struct emac_ring_header *ring_header = &adpt->ring_header; + int node = dev_to_node(adpt->netdev->dev.parent); struct emac_rx_queue *rx_q = &adpt->rx_q; size_t size; size = sizeof(struct emac_buffer) * rx_q->rfd.count; - rx_q->rfd.rfbuff = kzalloc(size, GFP_KERNEL); + rx_q->rfd.rfbuff = kzalloc_node(size, GFP_KERNEL, node); if (!rx_q->rfd.rfbuff) return -ENOMEM; @@ -920,14 +922,13 @@ static void emac_mac_rx_descs_refill(struct emac_adapter *adpt, static void emac_adjust_link(struct net_device *netdev) { struct emac_adapter *adpt = netdev_priv(netdev); - struct emac_sgmii *sgmii = &adpt->phy; struct phy_device *phydev = netdev->phydev; if (phydev->link) { emac_mac_start(adpt); - sgmii->link_up(adpt); + emac_sgmii_link_change(adpt, true); } else { - sgmii->link_down(adpt); + emac_sgmii_link_change(adpt, false); emac_mac_stop(adpt); } diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c index e8ab512ee7e3..e78e5db39458 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c @@ -53,6 +53,46 @@ #define SERDES_START_WAIT_TIMES 100 +int emac_sgmii_init(struct emac_adapter *adpt) +{ + if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->init)) + return 0; + + return adpt->phy.sgmii_ops->init(adpt); +} + +int emac_sgmii_open(struct emac_adapter *adpt) +{ + if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->open)) + return 0; + + return adpt->phy.sgmii_ops->open(adpt); +} + +void emac_sgmii_close(struct emac_adapter *adpt) +{ + if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->close)) + return; + + adpt->phy.sgmii_ops->close(adpt); +} + +int emac_sgmii_link_change(struct emac_adapter *adpt, bool link_state) +{ + if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->link_change)) + return 0; + + return adpt->phy.sgmii_ops->link_change(adpt, link_state); +} + +void emac_sgmii_reset(struct emac_adapter *adpt) +{ + if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->reset)) + return; + + adpt->phy.sgmii_ops->reset(adpt); +} + /* Initialize the SGMII link between the internal and external PHYs. */ static void emac_sgmii_link_init(struct emac_adapter *adpt) { @@ -163,21 +203,21 @@ static void emac_sgmii_reset_prepare(struct emac_adapter *adpt) msleep(50); } -void emac_sgmii_reset(struct emac_adapter *adpt) +static void emac_sgmii_common_reset(struct emac_adapter *adpt) { int ret; emac_sgmii_reset_prepare(adpt); emac_sgmii_link_init(adpt); - ret = adpt->phy.initialize(adpt); + ret = emac_sgmii_init(adpt); if (ret) netdev_err(adpt->netdev, "could not reinitialize internal PHY (error=%i)\n", ret); } -static int emac_sgmii_open(struct emac_adapter *adpt) +static int emac_sgmii_common_open(struct emac_adapter *adpt) { struct emac_sgmii *sgmii = &adpt->phy; int ret; @@ -201,43 +241,63 @@ static int emac_sgmii_open(struct emac_adapter *adpt) return 0; } -static int emac_sgmii_close(struct emac_adapter *adpt) +static void emac_sgmii_common_close(struct emac_adapter *adpt) { struct emac_sgmii *sgmii = &adpt->phy; /* Make sure interrupts are disabled */ writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK); free_irq(sgmii->irq, adpt); - - return 0; } /* The error interrupts are only valid after the link is up */ -static int emac_sgmii_link_up(struct emac_adapter *adpt) +static int emac_sgmii_common_link_change(struct emac_adapter *adpt, bool linkup) { struct emac_sgmii *sgmii = &adpt->phy; int ret; - /* Clear and enable interrupts */ - ret = emac_sgmii_irq_clear(adpt, 0xff); - if (ret) - return ret; + if (linkup) { + /* Clear and enable interrupts */ + ret = emac_sgmii_irq_clear(adpt, 0xff); + if (ret) + return ret; - writel(SGMII_ISR_MASK, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK); + writel(SGMII_ISR_MASK, + sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK); + } else { + /* Disable interrupts */ + writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK); + synchronize_irq(sgmii->irq); + } return 0; } -static int emac_sgmii_link_down(struct emac_adapter *adpt) -{ - struct emac_sgmii *sgmii = &adpt->phy; +static struct sgmii_ops fsm9900_ops = { + .init = emac_sgmii_init_fsm9900, + .open = emac_sgmii_common_open, + .close = emac_sgmii_common_close, + .link_change = emac_sgmii_common_link_change, + .reset = emac_sgmii_common_reset, +}; - /* Disable interrupts */ - writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK); - synchronize_irq(sgmii->irq); +static struct sgmii_ops qdf2432_ops = { + .init = emac_sgmii_init_qdf2432, + .open = emac_sgmii_common_open, + .close = emac_sgmii_common_close, + .link_change = emac_sgmii_common_link_change, + .reset = emac_sgmii_common_reset, +}; - return 0; -} +#ifdef CONFIG_ACPI +static struct sgmii_ops qdf2400_ops = { + .init = emac_sgmii_init_qdf2400, + .open = emac_sgmii_common_open, + .close = emac_sgmii_common_close, + .link_change = emac_sgmii_common_link_change, + .reset = emac_sgmii_common_reset, +}; +#endif static int emac_sgmii_acpi_match(struct device *dev, void *data) { @@ -249,7 +309,7 @@ static int emac_sgmii_acpi_match(struct device *dev, void *data) {} }; const struct acpi_device_id *id = acpi_match_device(match_table, dev); - emac_sgmii_function *initialize = data; + struct sgmii_ops **ops = data; if (id) { acpi_handle handle = ACPI_HANDLE(dev); @@ -270,10 +330,10 @@ static int emac_sgmii_acpi_match(struct device *dev, void *data) switch (hrv) { case 1: - *initialize = emac_sgmii_init_qdf2432; + *ops = &qdf2432_ops; return 1; case 2: - *initialize = emac_sgmii_init_qdf2400; + *ops = &qdf2400_ops; return 1; } } @@ -285,23 +345,15 @@ static int emac_sgmii_acpi_match(struct device *dev, void *data) static const struct of_device_id emac_sgmii_dt_match[] = { { .compatible = "qcom,fsm9900-emac-sgmii", - .data = emac_sgmii_init_fsm9900, + .data = &fsm9900_ops, }, { .compatible = "qcom,qdf2432-emac-sgmii", - .data = emac_sgmii_init_qdf2432, + .data = &qdf2432_ops, }, {} }; -/* Dummy function for systems without an internal PHY. This avoids having - * to check for NULL pointers before calling the functions. - */ -static int emac_sgmii_dummy(struct emac_adapter *adpt) -{ - return 0; -} - int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) { struct platform_device *sgmii_pdev = NULL; @@ -312,22 +364,11 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) if (has_acpi_companion(&pdev->dev)) { struct device *dev; - dev = device_find_child(&pdev->dev, &phy->initialize, + dev = device_find_child(&pdev->dev, &phy->sgmii_ops, emac_sgmii_acpi_match); if (!dev) { dev_warn(&pdev->dev, "cannot find internal phy node\n"); - /* There is typically no internal PHY on emulation - * systems, so if we can't find the node, assume - * we are on an emulation system and stub-out - * support for the internal PHY. These systems only - * use ACPI. - */ - phy->open = emac_sgmii_dummy; - phy->close = emac_sgmii_dummy; - phy->link_up = emac_sgmii_dummy; - phy->link_down = emac_sgmii_dummy; - return 0; } @@ -355,14 +396,9 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) goto error_put_device; } - phy->initialize = (emac_sgmii_function)match->data; + phy->sgmii_ops = (struct sgmii_ops *)match->data; } - phy->open = emac_sgmii_open; - phy->close = emac_sgmii_close; - phy->link_up = emac_sgmii_link_up; - phy->link_down = emac_sgmii_link_down; - /* Base address is the first address */ res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 0); if (!res) { @@ -386,7 +422,7 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) } } - ret = phy->initialize(adpt); + ret = emac_sgmii_init(adpt); if (ret) goto error; diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h index e7c0c3b2baa4..31ba21eb61d2 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h @@ -16,36 +16,44 @@ struct emac_adapter; struct platform_device; -typedef int (*emac_sgmii_function)(struct emac_adapter *adpt); +/** emac_sgmii - internal emac phy + * @init initialization function + * @open called when the driver is opened + * @close called when the driver is closed + * @link_change called when the link state changes + */ +struct sgmii_ops { + int (*init)(struct emac_adapter *adpt); + int (*open)(struct emac_adapter *adpt); + void (*close)(struct emac_adapter *adpt); + int (*link_change)(struct emac_adapter *adpt, bool link_state); + void (*reset)(struct emac_adapter *adpt); +}; /** emac_sgmii - internal emac phy * @base base address * @digital per-lane digital block * @irq the interrupt number * @decode_error_count reference count of consecutive decode errors - * @initialize initialization function - * @open called when the driver is opened - * @close called when the driver is closed - * @link_up called when the link comes up - * @link_down called when the link comes down + * @sgmii_ops sgmii ops */ struct emac_sgmii { void __iomem *base; void __iomem *digital; unsigned int irq; atomic_t decode_error_count; - emac_sgmii_function initialize; - emac_sgmii_function open; - emac_sgmii_function close; - emac_sgmii_function link_up; - emac_sgmii_function link_down; + struct sgmii_ops *sgmii_ops; }; int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt); -void emac_sgmii_reset(struct emac_adapter *adpt); int emac_sgmii_init_fsm9900(struct emac_adapter *adpt); int emac_sgmii_init_qdf2432(struct emac_adapter *adpt); int emac_sgmii_init_qdf2400(struct emac_adapter *adpt); +int emac_sgmii_init(struct emac_adapter *adpt); +int emac_sgmii_open(struct emac_adapter *adpt); +void emac_sgmii_close(struct emac_adapter *adpt); +int emac_sgmii_link_change(struct emac_adapter *adpt, bool link_state); +void emac_sgmii_reset(struct emac_adapter *adpt); #endif diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c index 13235baf4766..2a0cbc535a2e 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac.c +++ b/drivers/net/ethernet/qualcomm/emac/emac.c @@ -253,7 +253,7 @@ static int emac_open(struct net_device *netdev) return ret; } - ret = adpt->phy.open(adpt); + ret = emac_sgmii_open(adpt); if (ret) { emac_mac_rx_tx_rings_free_all(adpt); free_irq(irq->irq, irq); @@ -264,7 +264,7 @@ static int emac_open(struct net_device *netdev) if (ret) { emac_mac_rx_tx_rings_free_all(adpt); free_irq(irq->irq, irq); - adpt->phy.close(adpt); + emac_sgmii_close(adpt); return ret; } @@ -278,7 +278,7 @@ static int emac_close(struct net_device *netdev) mutex_lock(&adpt->reset_lock); - adpt->phy.close(adpt); + emac_sgmii_close(adpt); emac_mac_down(adpt); emac_mac_rx_tx_rings_free_all(adpt); @@ -761,11 +761,10 @@ static void emac_shutdown(struct platform_device *pdev) { struct net_device *netdev = dev_get_drvdata(&pdev->dev); struct emac_adapter *adpt = netdev_priv(netdev); - struct emac_sgmii *sgmii = &adpt->phy; if (netdev->flags & IFF_UP) { /* Closing the SGMII turns off its interrupts */ - sgmii->close(adpt); + emac_sgmii_close(adpt); /* Resetting the MAC turns off all DMA and its interrupts */ emac_mac_reset(adpt); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h index 0b5b5da80198..34ac45a774e7 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h @@ -54,11 +54,24 @@ struct rmnet_pcpu_stats { struct u64_stats_sync syncp; }; +struct rmnet_priv_stats { + u64 csum_ok; + u64 csum_valid_unset; + u64 csum_validation_failed; + u64 csum_err_bad_buffer; + u64 csum_err_invalid_ip_version; + u64 csum_err_invalid_transport; + u64 csum_fragmented_pkt; + u64 csum_skipped; + u64 csum_sw; +}; + struct rmnet_priv { u8 mux_id; struct net_device *real_dev; struct rmnet_pcpu_stats __percpu *pcpu_stats; struct gro_cells gro_cells; + struct rmnet_priv_stats stats; }; struct rmnet_port *rmnet_get_port(struct net_device *real_dev); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c index 6fcd586e9804..7fd86d40a337 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c @@ -148,7 +148,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, if (skb_headroom(skb) < required_headroom) { if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL)) - goto fail; + return -ENOMEM; } if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) @@ -156,17 +156,13 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, map_header = rmnet_map_add_map_header(skb, additional_header_len, 0); if (!map_header) - goto fail; + return -ENOMEM; map_header->mux_id = mux_id; skb->protocol = htons(ETH_P_MAP); return 0; - -fail: - kfree_skb(skb); - return -ENOMEM; } static void @@ -228,15 +224,18 @@ void rmnet_egress_handler(struct sk_buff *skb) mux_id = priv->mux_id; port = rmnet_get_port(skb->dev); - if (!port) { - kfree_skb(skb); - return; - } + if (!port) + goto drop; if (rmnet_map_egress_handler(skb, port, mux_id, orig_dev)) - return; + goto drop; rmnet_vnd_tx_fixup(skb, orig_dev); dev_queue_xmit(skb); + return; + +drop: + this_cpu_inc(priv->pcpu_stats->stats.tx_drops); + kfree_skb(skb); } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c index 78fdad0c6f76..3ee8ae9b6838 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c @@ -67,28 +67,20 @@ static void rmnet_map_send_ack(struct sk_buff *skb, struct rmnet_port *port) { struct rmnet_map_control_command *cmd; - int xmit_status; - - if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { - if (skb->len < sizeof(struct rmnet_map_header) + - RMNET_MAP_GET_LENGTH(skb) + - sizeof(struct rmnet_map_dl_csum_trailer)) { - kfree_skb(skb); - return; - } - - skb_trim(skb, skb->len - - sizeof(struct rmnet_map_dl_csum_trailer)); - } + struct net_device *dev = skb->dev; + + if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) + skb_trim(skb, + skb->len - sizeof(struct rmnet_map_dl_csum_trailer)); skb->protocol = htons(ETH_P_MAP); cmd = RMNET_MAP_GET_CMD_START(skb); cmd->cmd_type = type & 0x03; - netif_tx_lock(skb->dev); - xmit_status = skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev); - netif_tx_unlock(skb->dev); + netif_tx_lock(dev); + dev->netdev_ops->ndo_start_xmit(skb, dev); + netif_tx_unlock(dev); } /* Process MAP command frame and send N/ACK message as appropriate. Message cmd diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index a6ea09416f8d..57a9c314a665 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -48,7 +48,8 @@ static __sum16 *rmnet_map_get_csum_field(unsigned char protocol, static int rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb, - struct rmnet_map_dl_csum_trailer *csum_trailer) + struct rmnet_map_dl_csum_trailer *csum_trailer, + struct rmnet_priv *priv) { __sum16 *csum_field, csum_temp, pseudo_csum, hdr_csum, ip_payload_csum; u16 csum_value, csum_value_final; @@ -58,19 +59,25 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb, ip4h = (struct iphdr *)(skb->data); if ((ntohs(ip4h->frag_off) & IP_MF) || - ((ntohs(ip4h->frag_off) & IP_OFFSET) > 0)) + ((ntohs(ip4h->frag_off) & IP_OFFSET) > 0)) { + priv->stats.csum_fragmented_pkt++; return -EOPNOTSUPP; + } txporthdr = skb->data + ip4h->ihl * 4; csum_field = rmnet_map_get_csum_field(ip4h->protocol, txporthdr); - if (!csum_field) + if (!csum_field) { + priv->stats.csum_err_invalid_transport++; return -EPROTONOSUPPORT; + } /* RFC 768 - Skip IPv4 UDP packets where sender checksum field is 0 */ - if (*csum_field == 0 && ip4h->protocol == IPPROTO_UDP) + if (*csum_field == 0 && ip4h->protocol == IPPROTO_UDP) { + priv->stats.csum_skipped++; return 0; + } csum_value = ~ntohs(csum_trailer->csum_value); hdr_csum = ~ip_fast_csum(ip4h, (int)ip4h->ihl); @@ -102,16 +109,20 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb, } } - if (csum_value_final == ntohs((__force __be16)*csum_field)) + if (csum_value_final == ntohs((__force __be16)*csum_field)) { + priv->stats.csum_ok++; return 0; - else + } else { + priv->stats.csum_validation_failed++; return -EINVAL; + } } #if IS_ENABLED(CONFIG_IPV6) static int rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb, - struct rmnet_map_dl_csum_trailer *csum_trailer) + struct rmnet_map_dl_csum_trailer *csum_trailer, + struct rmnet_priv *priv) { __sum16 *csum_field, ip6_payload_csum, pseudo_csum, csum_temp; u16 csum_value, csum_value_final; @@ -125,8 +136,10 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb, txporthdr = skb->data + sizeof(struct ipv6hdr); csum_field = rmnet_map_get_csum_field(ip6h->nexthdr, txporthdr); - if (!csum_field) + if (!csum_field) { + priv->stats.csum_err_invalid_transport++; return -EPROTONOSUPPORT; + } csum_value = ~ntohs(csum_trailer->csum_value); ip6_hdr_csum = (__force __be16) @@ -164,10 +177,13 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb, } } - if (csum_value_final == ntohs((__force __be16)*csum_field)) + if (csum_value_final == ntohs((__force __be16)*csum_field)) { + priv->stats.csum_ok++; return 0; - else + } else { + priv->stats.csum_validation_failed++; return -EINVAL; + } } #endif @@ -339,24 +355,34 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, */ int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len) { + struct rmnet_priv *priv = netdev_priv(skb->dev); struct rmnet_map_dl_csum_trailer *csum_trailer; - if (unlikely(!(skb->dev->features & NETIF_F_RXCSUM))) + if (unlikely(!(skb->dev->features & NETIF_F_RXCSUM))) { + priv->stats.csum_sw++; return -EOPNOTSUPP; + } csum_trailer = (struct rmnet_map_dl_csum_trailer *)(skb->data + len); - if (!csum_trailer->valid) + if (!csum_trailer->valid) { + priv->stats.csum_valid_unset++; return -EINVAL; + } - if (skb->protocol == htons(ETH_P_IP)) - return rmnet_map_ipv4_dl_csum_trailer(skb, csum_trailer); - else if (skb->protocol == htons(ETH_P_IPV6)) + if (skb->protocol == htons(ETH_P_IP)) { + return rmnet_map_ipv4_dl_csum_trailer(skb, csum_trailer, priv); + } else if (skb->protocol == htons(ETH_P_IPV6)) { #if IS_ENABLED(CONFIG_IPV6) - return rmnet_map_ipv6_dl_csum_trailer(skb, csum_trailer); + return rmnet_map_ipv6_dl_csum_trailer(skb, csum_trailer, priv); #else + priv->stats.csum_err_invalid_ip_version++; return -EPROTONOSUPPORT; #endif + } else { + priv->stats.csum_err_invalid_ip_version++; + return -EPROTONOSUPPORT; + } return 0; } @@ -367,6 +393,7 @@ int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len) void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, struct net_device *orig_dev) { + struct rmnet_priv *priv = netdev_priv(orig_dev); struct rmnet_map_ul_csum_header *ul_header; void *iphdr; @@ -389,8 +416,11 @@ void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, rmnet_map_ipv6_ul_csum_header(iphdr, ul_header, skb); return; #else + priv->stats.csum_err_invalid_ip_version++; goto sw_csum; #endif + } else { + priv->stats.csum_err_invalid_ip_version++; } } @@ -399,4 +429,6 @@ sw_csum: ul_header->csum_insert_offset = 0; ul_header->csum_enabled = 0; ul_header->udp_ip4_ind = 0; + + priv->stats.csum_sw++; } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c index 2ea16a088de8..b9a7548ec6a0 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -152,6 +152,56 @@ static const struct net_device_ops rmnet_vnd_ops = { .ndo_get_stats64 = rmnet_get_stats64, }; +static const char rmnet_gstrings_stats[][ETH_GSTRING_LEN] = { + "Checksum ok", + "Checksum valid bit not set", + "Checksum validation failed", + "Checksum error bad buffer", + "Checksum error bad ip version", + "Checksum error bad transport", + "Checksum skipped on ip fragment", + "Checksum skipped", + "Checksum computed in software", +}; + +static void rmnet_get_strings(struct net_device *dev, u32 stringset, u8 *buf) +{ + switch (stringset) { + case ETH_SS_STATS: + memcpy(buf, &rmnet_gstrings_stats, + sizeof(rmnet_gstrings_stats)); + break; + } +} + +static int rmnet_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(rmnet_gstrings_stats); + default: + return -EOPNOTSUPP; + } +} + +static void rmnet_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct rmnet_priv *priv = netdev_priv(dev); + struct rmnet_priv_stats *st = &priv->stats; + + if (!data) + return; + + memcpy(data, st, ARRAY_SIZE(rmnet_gstrings_stats) * sizeof(u64)); +} + +static const struct ethtool_ops rmnet_ethtool_ops = { + .get_ethtool_stats = rmnet_get_ethtool_stats, + .get_strings = rmnet_get_strings, + .get_sset_count = rmnet_get_sset_count, +}; + /* Called by kernel whenever a new rmnet<n> device is created. Sets MTU, * flags, ARP type, needed headroom, etc... */ @@ -170,6 +220,11 @@ void rmnet_vnd_setup(struct net_device *rmnet_dev) rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); rmnet_dev->needs_free_netdev = true; + rmnet_dev->ethtool_ops = &rmnet_ethtool_ops; + + /* This perm addr will be used as interface identifier by IPv6 */ + rmnet_dev->addr_assign_type = NET_ADDR_RANDOM; + eth_random_addr(rmnet_dev->perm_addr); } /* Exposed API */ |