From 293e4365a1adb1d3747dcb7d12a52950e0e232a2 Mon Sep 17 00:00:00 2001 From: Giuseppe Cavallaro Date: Mon, 29 Feb 2016 14:27:29 +0100 Subject: stmmac: change descriptor layout This patch completely changes the descriptor layout to improve the whole performances due to the single read usage of the descriptors in critical paths. Signed-off-by: Giuseppe Cavallaro Signed-off-by: Alexandre TORGUE Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 190 ++++++++++++++----------- 1 file changed, 103 insertions(+), 87 deletions(-) (limited to 'drivers/net/ethernet/stmicro/stmmac/enh_desc.c') diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 7d944449f5ef..716b80740b58 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -1,7 +1,7 @@ /******************************************************************************* This contains the functions to handle the enhanced descriptors. - Copyright (C) 2007-2009 STMicroelectronics Ltd + Copyright (C) 2007-2014 STMicroelectronics Ltd This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -29,44 +29,44 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x, struct dma_desc *p, void __iomem *ioaddr) { - int ret = 0; struct net_device_stats *stats = (struct net_device_stats *)data; + unsigned int tdes0 = p->des0; + int ret = 0; - if (unlikely(p->des01.etx.error_summary)) { - if (unlikely(p->des01.etx.jabber_timeout)) + if (unlikely(tdes0 & ETDES0_ERROR_SUMMARY)) { + if (unlikely(tdes0 & ETDES0_JABBER_TIMEOUT)) x->tx_jabber++; - if (unlikely(p->des01.etx.frame_flushed)) { + if (unlikely(tdes0 & ETDES0_FRAME_FLUSHED)) { x->tx_frame_flushed++; dwmac_dma_flush_tx_fifo(ioaddr); } - if (unlikely(p->des01.etx.loss_carrier)) { + if (unlikely(tdes0 & ETDES0_LOSS_CARRIER)) { x->tx_losscarrier++; stats->tx_carrier_errors++; } - if (unlikely(p->des01.etx.no_carrier)) { + if (unlikely(tdes0 & ETDES0_NO_CARRIER)) { x->tx_carrier++; stats->tx_carrier_errors++; } - if (unlikely(p->des01.etx.late_collision)) - stats->collisions += p->des01.etx.collision_count; - - if (unlikely(p->des01.etx.excessive_collisions)) - stats->collisions += p->des01.etx.collision_count; + if (unlikely((tdes0 & ETDES0_LATE_COLLISION) || + (tdes0 & ETDES0_EXCESSIVE_COLLISIONS))) + stats->collisions += + (tdes0 & ETDES0_COLLISION_COUNT_MASK) >> 3; - if (unlikely(p->des01.etx.excessive_deferral)) + if (unlikely(tdes0 & ETDES0_EXCESSIVE_DEFERRAL)) x->tx_deferred++; - if (unlikely(p->des01.etx.underflow_error)) { + if (unlikely(tdes0 & ETDES0_UNDERFLOW_ERROR)) { dwmac_dma_flush_tx_fifo(ioaddr); x->tx_underflow++; } - if (unlikely(p->des01.etx.ip_header_error)) + if (unlikely(tdes0 & ETDES0_IP_HEADER_ERROR)) x->tx_ip_header_error++; - if (unlikely(p->des01.etx.payload_error)) { + if (unlikely(tdes0 & ETDES0_PAYLOAD_ERROR)) { x->tx_payload_error++; dwmac_dma_flush_tx_fifo(ioaddr); } @@ -74,11 +74,11 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x, ret = -1; } - if (unlikely(p->des01.etx.deferred)) + if (unlikely(tdes0 & ETDES0_DEFERRED)) x->tx_deferred++; #ifdef STMMAC_VLAN_TAG_USED - if (p->des01.etx.vlan_frame) + if (tdes0 & ETDES0_VLAN_FRAME) x->tx_vlan++; #endif @@ -87,7 +87,7 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x, static int enh_desc_get_tx_len(struct dma_desc *p) { - return p->des01.etx.buffer1_size; + return (p->des1 & ETDES1_BUFFER1_SIZE_MASK); } static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err) @@ -126,50 +126,55 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err) static void enh_desc_get_ext_status(void *data, struct stmmac_extra_stats *x, struct dma_extended_desc *p) { - if (unlikely(p->basic.des01.erx.rx_mac_addr)) { - if (p->des4.erx.ip_hdr_err) + unsigned int rdes0 = p->basic.des0; + unsigned int rdes4 = p->des4; + + if (unlikely(rdes0 & ERDES0_RX_MAC_ADDR)) { + int message_type = (rdes4 & ERDES4_MSG_TYPE_MASK) >> 8; + + if (rdes4 & ERDES4_IP_HDR_ERR) x->ip_hdr_err++; - if (p->des4.erx.ip_payload_err) + if (rdes4 & ERDES4_IP_PAYLOAD_ERR) x->ip_payload_err++; - if (p->des4.erx.ip_csum_bypassed) + if (rdes4 & ERDES4_IP_CSUM_BYPASSED) x->ip_csum_bypassed++; - if (p->des4.erx.ipv4_pkt_rcvd) + if (rdes4 & ERDES4_IPV4_PKT_RCVD) x->ipv4_pkt_rcvd++; - if (p->des4.erx.ipv6_pkt_rcvd) + if (rdes4 & ERDES4_IPV6_PKT_RCVD) x->ipv6_pkt_rcvd++; - if (p->des4.erx.msg_type == RDES_EXT_SYNC) + if (message_type == RDES_EXT_SYNC) x->rx_msg_type_sync++; - else if (p->des4.erx.msg_type == RDES_EXT_FOLLOW_UP) + else if (message_type == RDES_EXT_FOLLOW_UP) x->rx_msg_type_follow_up++; - else if (p->des4.erx.msg_type == RDES_EXT_DELAY_REQ) + else if (message_type == RDES_EXT_DELAY_REQ) x->rx_msg_type_delay_req++; - else if (p->des4.erx.msg_type == RDES_EXT_DELAY_RESP) + else if (message_type == RDES_EXT_DELAY_RESP) x->rx_msg_type_delay_resp++; - else if (p->des4.erx.msg_type == RDES_EXT_PDELAY_REQ) + else if (message_type == RDES_EXT_PDELAY_REQ) x->rx_msg_type_pdelay_req++; - else if (p->des4.erx.msg_type == RDES_EXT_PDELAY_RESP) + else if (message_type == RDES_EXT_PDELAY_RESP) x->rx_msg_type_pdelay_resp++; - else if (p->des4.erx.msg_type == RDES_EXT_PDELAY_FOLLOW_UP) + else if (message_type == RDES_EXT_PDELAY_FOLLOW_UP) x->rx_msg_type_pdelay_follow_up++; else x->rx_msg_type_ext_no_ptp++; - if (p->des4.erx.ptp_frame_type) + if (rdes4 & ERDES4_PTP_FRAME_TYPE) x->ptp_frame_type++; - if (p->des4.erx.ptp_ver) + if (rdes4 & ERDES4_PTP_VER) x->ptp_ver++; - if (p->des4.erx.timestamp_dropped) + if (rdes4 & ERDES4_TIMESTAMP_DROPPED) x->timestamp_dropped++; - if (p->des4.erx.av_pkt_rcvd) + if (rdes4 & ERDES4_AV_PKT_RCVD) x->av_pkt_rcvd++; - if (p->des4.erx.av_tagged_pkt_rcvd) + if (rdes4 & ERDES4_AV_TAGGED_PKT_RCVD) x->av_tagged_pkt_rcvd++; - if (p->des4.erx.vlan_tag_priority_val) + if ((rdes4 & ERDES4_VLAN_TAG_PRI_VAL_MASK) >> 18) x->vlan_tag_priority_val++; - if (p->des4.erx.l3_filter_match) + if (rdes4 & ERDES4_L3_FILTER_MATCH) x->l3_filter_match++; - if (p->des4.erx.l4_filter_match) + if (rdes4 & ERDES4_L4_FILTER_MATCH) x->l4_filter_match++; - if (p->des4.erx.l3_l4_filter_no_match) + if ((rdes4 & ERDES4_L3_L4_FILT_NO_MATCH_MASK) >> 26) x->l3_l4_filter_no_match++; } } @@ -177,30 +182,30 @@ static void enh_desc_get_ext_status(void *data, struct stmmac_extra_stats *x, static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, struct dma_desc *p) { - int ret = good_frame; struct net_device_stats *stats = (struct net_device_stats *)data; + unsigned int rdes0 = p->des0; + int ret = good_frame; - if (unlikely(p->des01.erx.error_summary)) { - if (unlikely(p->des01.erx.descriptor_error)) { + if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) { + if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) { x->rx_desc++; stats->rx_length_errors++; } - if (unlikely(p->des01.erx.overflow_error)) + if (unlikely(rdes0 & RDES0_OVERFLOW_ERROR)) x->rx_gmac_overflow++; - if (unlikely(p->des01.erx.ipc_csum_error)) + if (unlikely(rdes0 & RDES0_IPC_CSUM_ERROR)) pr_err("\tIPC Csum Error/Giant frame\n"); - if (unlikely(p->des01.erx.late_collision)) { + if (unlikely(rdes0 & RDES0_COLLISION)) stats->collisions++; - } - if (unlikely(p->des01.erx.receive_watchdog)) + if (unlikely(rdes0 & RDES0_RECEIVE_WATCHDOG)) x->rx_watchdog++; - if (unlikely(p->des01.erx.error_gmii)) + if (unlikely(rdes0 & RDES0_MII_ERROR)) /* GMII */ x->rx_mii++; - if (unlikely(p->des01.erx.crc_error)) { + if (unlikely(rdes0 & RDES0_CRC_ERROR)) { x->rx_crc++; stats->rx_crc_errors++; } @@ -211,26 +216,27 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, * It doesn't match with the information reported into the databook. * At any rate, we need to understand if the CSUM hw computation is ok * and report this info to the upper layers. */ - ret = enh_desc_coe_rdes0(p->des01.erx.ipc_csum_error, - p->des01.erx.frame_type, p->des01.erx.rx_mac_addr); + ret = enh_desc_coe_rdes0(!!(rdes0 & RDES0_IPC_CSUM_ERROR), + !!(rdes0 & RDES0_FRAME_TYPE), + !!(rdes0 & ERDES0_RX_MAC_ADDR)); - if (unlikely(p->des01.erx.dribbling)) + if (unlikely(rdes0 & RDES0_DRIBBLING)) x->dribbling_bit++; - if (unlikely(p->des01.erx.sa_filter_fail)) { + if (unlikely(rdes0 & RDES0_SA_FILTER_FAIL)) { x->sa_rx_filter_fail++; ret = discard_frame; } - if (unlikely(p->des01.erx.da_filter_fail)) { + if (unlikely(rdes0 & RDES0_DA_FILTER_FAIL)) { x->da_rx_filter_fail++; ret = discard_frame; } - if (unlikely(p->des01.erx.length_error)) { + if (unlikely(rdes0 & RDES0_LENGTH_ERROR)) { x->rx_length++; ret = discard_frame; } #ifdef STMMAC_VLAN_TAG_USED - if (p->des01.erx.vlan_tag) + if (rdes0 & RDES0_VLAN_TAG) x->rx_vlan++; #endif @@ -240,60 +246,59 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode, int end) { - p->des01.all_flags = 0; - p->des01.erx.own = 1; - p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1; + p->des0 |= RDES0_OWN; + p->des1 |= ((BUF_SIZE_8KiB - 1) & ERDES1_BUFFER1_SIZE_MASK); if (mode == STMMAC_CHAIN_MODE) - ehn_desc_rx_set_on_chain(p, end); + ehn_desc_rx_set_on_chain(p); else ehn_desc_rx_set_on_ring(p, end); if (disable_rx_ic) - p->des01.erx.disable_ic = 1; + p->des1 |= ERDES1_DISABLE_IC; } static void enh_desc_init_tx_desc(struct dma_desc *p, int mode, int end) { - p->des01.all_flags = 0; + p->des0 &= ~ETDES0_OWN; if (mode == STMMAC_CHAIN_MODE) - ehn_desc_tx_set_on_chain(p, end); + enh_desc_end_tx_desc_on_chain(p); else - ehn_desc_tx_set_on_ring(p, end); + enh_desc_end_tx_desc_on_ring(p, end); } static int enh_desc_get_tx_owner(struct dma_desc *p) { - return p->des01.etx.own; + return (p->des0 & ETDES0_OWN) >> 31; } static int enh_desc_get_rx_owner(struct dma_desc *p) { - return p->des01.erx.own; + return (p->des0 & RDES0_OWN) >> 31; } static void enh_desc_set_tx_owner(struct dma_desc *p) { - p->des01.etx.own = 1; + p->des0 |= ETDES0_OWN; } static void enh_desc_set_rx_owner(struct dma_desc *p) { - p->des01.erx.own = 1; + p->des0 |= RDES0_OWN; } static int enh_desc_get_tx_ls(struct dma_desc *p) { - return p->des01.etx.last_segment; + return (p->des0 & ETDES0_LAST_SEGMENT) >> 29; } static void enh_desc_release_tx_desc(struct dma_desc *p, int mode) { - int ter = p->des01.etx.end_ring; + int ter = (p->des0 & ETDES0_END_RING) >> 21; memset(p, 0, offsetof(struct dma_desc, des2)); if (mode == STMMAC_CHAIN_MODE) - enh_desc_end_tx_desc_on_chain(p, ter); + enh_desc_end_tx_desc_on_chain(p); else enh_desc_end_tx_desc_on_ring(p, ter); } @@ -301,49 +306,60 @@ static void enh_desc_release_tx_desc(struct dma_desc *p, int mode) static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, int csum_flag, int mode) { - p->des01.etx.first_segment = is_fs; + unsigned int tdes0 = p->des0; + + if (is_fs) + tdes0 |= ETDES0_FIRST_SEGMENT; + else + tdes0 &= ~ETDES0_FIRST_SEGMENT; + + if (likely(csum_flag)) + tdes0 |= (TX_CIC_FULL << ETDES0_CHECKSUM_INSERTION_SHIFT); + else + tdes0 &= ~(TX_CIC_FULL << ETDES0_CHECKSUM_INSERTION_SHIFT); + + p->des0 = tdes0; if (mode == STMMAC_CHAIN_MODE) enh_set_tx_desc_len_on_chain(p, len); else enh_set_tx_desc_len_on_ring(p, len); - - if (likely(csum_flag)) - p->des01.etx.checksum_insertion = cic_full; } static void enh_desc_clear_tx_ic(struct dma_desc *p) { - p->des01.etx.interrupt = 0; + p->des0 &= ~ETDES0_INTERRUPT; } static void enh_desc_close_tx_desc(struct dma_desc *p) { - p->des01.etx.last_segment = 1; - p->des01.etx.interrupt = 1; + p->des0 |= ETDES0_LAST_SEGMENT | ETDES0_INTERRUPT; } static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) { + unsigned int csum = 0; /* The type-1 checksum offload engines append the checksum at * the end of frame and the two bytes of checksum are added in * the length. * Adjust for that in the framelen for type-1 checksum offload - * engines. */ + * engines. + */ if (rx_coe_type == STMMAC_RX_COE_TYPE1) - return p->des01.erx.frame_length - 2; - else - return p->des01.erx.frame_length; + csum = 2; + + return (((p->des0 & RDES0_FRAME_LEN_MASK) >> RDES0_FRAME_LEN_SHIFT) - + csum); } static void enh_desc_enable_tx_timestamp(struct dma_desc *p) { - p->des01.etx.time_stamp_enable = 1; + p->des0 |= ETDES0_TIME_STAMP_ENABLE; } static int enh_desc_get_tx_timestamp_status(struct dma_desc *p) { - return p->des01.etx.time_stamp_status; + return (p->des0 & ETDES0_TIME_STAMP_STATUS) >> 17; } static u64 enh_desc_get_timestamp(void *desc, u32 ats) @@ -368,7 +384,7 @@ static int enh_desc_get_rx_timestamp_status(void *desc, u32 ats) { if (ats) { struct dma_extended_desc *p = (struct dma_extended_desc *)desc; - return p->basic.des01.erx.ipc_csum_error; + return (p->basic.des0 & RDES0_IPC_CSUM_ERROR) >> 7; } else { struct dma_desc *p = (struct dma_desc *)desc; if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff)) -- cgit v1.2.3 From c1fa3212be5503d802a5c4c451dd4e673fdc603a Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Mon, 29 Feb 2016 14:27:34 +0100 Subject: stmmac: merge get_rx_owner into rx_status routine. The RDES0 register can be read several times while doing RX of a packet. This patch slightly improves RX path performance by reading rdes0 once for two operation: check rx owner, get rx status bits. Signed-off-by: Fabrice Gasnier Acked-by: Giuseppe Cavallaro Signed-off-by: Alexandre TORGUE Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 10 +++++----- drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 9 +++------ drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 9 +++------ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 9 +++++---- 4 files changed, 16 insertions(+), 21 deletions(-) (limited to 'drivers/net/ethernet/stmicro/stmmac/enh_desc.c') diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 09291af19039..3ba268e93bc9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -238,10 +238,11 @@ struct stmmac_extra_stats { /* Rx IPC status */ enum rx_frame_status { - good_frame = 0, - discard_frame = 1, - csum_none = 2, - llc_snap = 4, + good_frame = 0x0, + discard_frame = 0x1, + csum_none = 0x2, + llc_snap = 0x4, + dma_own = 0x8, }; enum dma_irq_status { @@ -356,7 +357,6 @@ struct stmmac_desc_ops { /* Get the buffer size from the descriptor */ int (*get_tx_len) (struct dma_desc *p); /* Handle extra events on specific interrupts hw dependent */ - int (*get_rx_owner) (struct dma_desc *p); void (*set_rx_owner) (struct dma_desc *p); /* Get the receive frame size */ int (*get_rx_frame_len) (struct dma_desc *p, int rx_coe_type); diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 716b80740b58..1a2fce988548 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -186,6 +186,9 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, unsigned int rdes0 = p->des0; int ret = good_frame; + if (unlikely(rdes0 & RDES0_OWN)) + return dma_own; + if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) { if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) { x->rx_desc++; @@ -272,11 +275,6 @@ static int enh_desc_get_tx_owner(struct dma_desc *p) return (p->des0 & ETDES0_OWN) >> 31; } -static int enh_desc_get_rx_owner(struct dma_desc *p) -{ - return (p->des0 & RDES0_OWN) >> 31; -} - static void enh_desc_set_tx_owner(struct dma_desc *p) { p->des0 |= ETDES0_OWN; @@ -402,7 +400,6 @@ const struct stmmac_desc_ops enh_desc_ops = { .init_rx_desc = enh_desc_init_rx_desc, .init_tx_desc = enh_desc_init_tx_desc, .get_tx_owner = enh_desc_get_tx_owner, - .get_rx_owner = enh_desc_get_rx_owner, .release_tx_desc = enh_desc_release_tx_desc, .prepare_tx_desc = enh_desc_prepare_tx_desc, .clear_tx_ic = enh_desc_clear_tx_ic, diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index 460c573dd3e1..5a91932ff639 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -82,6 +82,9 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x, unsigned int rdes0 = p->des0; struct net_device_stats *stats = (struct net_device_stats *)data; + if (unlikely(rdes0 & RDES0_OWN)) + return dma_own; + if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) { pr_warn("%s: Oversized frame spanned multiple buffers\n", __func__); @@ -155,11 +158,6 @@ static int ndesc_get_tx_owner(struct dma_desc *p) return (p->des0 & TDES0_OWN) >> 31; } -static int ndesc_get_rx_owner(struct dma_desc *p) -{ - return (p->des0 & RDES0_OWN) >> 31; -} - static void ndesc_set_tx_owner(struct dma_desc *p) { p->des0 |= TDES0_OWN; @@ -277,7 +275,6 @@ const struct stmmac_desc_ops ndesc_ops = { .init_rx_desc = ndesc_init_rx_desc, .init_tx_desc = ndesc_init_tx_desc, .get_tx_owner = ndesc_get_tx_owner, - .get_rx_owner = ndesc_get_rx_owner, .release_tx_desc = ndesc_release_tx_desc, .prepare_tx_desc = ndesc_prepare_tx_desc, .clear_tx_ic = ndesc_clear_tx_ic, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 0194a8f26f8c..796d7c69f902 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2205,7 +2205,11 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) else p = priv->dma_rx + entry; - if (priv->hw->desc->get_rx_owner(p)) + /* read the status of the incoming frame */ + status = priv->hw->desc->rx_status(&priv->dev->stats, + &priv->xstats, p); + /* check if managed by the DMA otherwise go ahead */ + if (unlikely(status & dma_own)) break; count++; @@ -2218,9 +2222,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) else prefetch(priv->dma_rx + next_entry); - /* read the status of the incoming frame */ - status = priv->hw->desc->rx_status(&priv->dev->stats, - &priv->xstats, p); if ((priv->extend_desc) && (priv->hw->desc->rx_extended_status)) priv->hw->desc->rx_extended_status(&priv->dev->stats, &priv->xstats, -- cgit v1.2.3 From be434d5075d6be0cda996200b2a20035e1565215 Mon Sep 17 00:00:00 2001 From: Giuseppe Cavallaro Date: Mon, 29 Feb 2016 14:27:35 +0100 Subject: stmmac: optimize tx desc management This patch is to optimize the way to manage the TDES inside the xmit function. When prepare the frame, some settings (e.g. OWN bit) can be merged. This has been reworked to improve the tx performances. Signed-off-by: Fabrice Gasnier Signed-off-by: Giuseppe Cavallaro Signed-off-by: Alexandre TORGUE Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/chain_mode.c | 13 ++++++++----- drivers/net/ethernet/stmicro/stmmac/common.h | 5 ++--- drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 22 +++++++++++++++------- drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 15 ++++++++------- drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 7 +++---- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 16 ++++++---------- 6 files changed, 42 insertions(+), 36 deletions(-) (limited to 'drivers/net/ethernet/stmicro/stmmac/enh_desc.c') diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index dacb6542c6dd..b3e669af3005 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c @@ -50,7 +50,9 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) return -1; priv->tx_skbuff_dma[entry].buf = desc->des2; priv->tx_skbuff_dma[entry].len = bmax; - priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE); + /* do not close the descriptor and do not set own bit */ + priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE, + 0, false); while (len != 0) { priv->tx_skbuff[entry] = NULL; @@ -66,8 +68,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) priv->tx_skbuff_dma[entry].buf = desc->des2; priv->tx_skbuff_dma[entry].len = bmax; priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum, - STMMAC_CHAIN_MODE); - priv->hw->desc->set_tx_owner(desc); + STMMAC_CHAIN_MODE, 1, + false); len -= bmax; i++; } else { @@ -78,9 +80,10 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) return -1; priv->tx_skbuff_dma[entry].buf = desc->des2; priv->tx_skbuff_dma[entry].len = len; + /* last descriptor can be set now */ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, - STMMAC_CHAIN_MODE); - priv->hw->desc->set_tx_owner(desc); + STMMAC_CHAIN_MODE, 1, + true); len = 0; } } diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 3ba268e93bc9..885c0f9808b6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -338,12 +338,11 @@ struct stmmac_desc_ops { /* Invoked by the xmit function to prepare the tx descriptor */ void (*prepare_tx_desc) (struct dma_desc *p, int is_fs, int len, - int csum_flag, int mode); + bool csum_flag, int mode, bool tx_own, + bool ls_ic); /* Set/get the owner of the descriptor */ void (*set_tx_owner) (struct dma_desc *p); int (*get_tx_owner) (struct dma_desc *p); - /* Invoked by the xmit function to close the tx descriptor */ - void (*close_tx_desc) (struct dma_desc *p); /* Clean the tx descriptor as soon as the tx irq is received */ void (*release_tx_desc) (struct dma_desc *p, int mode); /* Clear interrupt on tx frame completion. When this bit is diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 1a2fce988548..1abd80ed09f3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -302,7 +302,8 @@ static void enh_desc_release_tx_desc(struct dma_desc *p, int mode) } static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, - int csum_flag, int mode) + bool csum_flag, int mode, bool tx_own, + bool ls_ic) { unsigned int tdes0 = p->des0; @@ -316,6 +317,19 @@ static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, else tdes0 &= ~(TX_CIC_FULL << ETDES0_CHECKSUM_INSERTION_SHIFT); + if (tx_own) + tdes0 |= ETDES0_OWN; + + if (is_fs & tx_own) + /* When the own bit, for the first frame, has to be set, all + * descriptors for the same frame has to be set before, to + * avoid race condition. + */ + wmb(); + + if (ls_ic) + tdes0 |= ETDES0_LAST_SEGMENT | ETDES0_INTERRUPT; + p->des0 = tdes0; if (mode == STMMAC_CHAIN_MODE) @@ -329,11 +343,6 @@ static void enh_desc_clear_tx_ic(struct dma_desc *p) p->des0 &= ~ETDES0_INTERRUPT; } -static void enh_desc_close_tx_desc(struct dma_desc *p) -{ - p->des0 |= ETDES0_LAST_SEGMENT | ETDES0_INTERRUPT; -} - static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) { unsigned int csum = 0; @@ -403,7 +412,6 @@ const struct stmmac_desc_ops enh_desc_ops = { .release_tx_desc = enh_desc_release_tx_desc, .prepare_tx_desc = enh_desc_prepare_tx_desc, .clear_tx_ic = enh_desc_clear_tx_ic, - .close_tx_desc = enh_desc_close_tx_desc, .get_tx_ls = enh_desc_get_tx_ls, .set_tx_owner = enh_desc_set_tx_owner, .set_rx_owner = enh_desc_set_rx_owner, diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index 5a91932ff639..19cc12dd0f17 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -185,7 +185,8 @@ static void ndesc_release_tx_desc(struct dma_desc *p, int mode) } static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, - int csum_flag, int mode) + bool csum_flag, int mode, bool tx_own, + bool ls_ic) { unsigned int tdes1 = p->des1; @@ -199,6 +200,12 @@ static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, else tdes1 &= ~(TX_CIC_FULL << TDES1_CHECKSUM_INSERTION_SHIFT); + if (tx_own) + tdes1 |= TDES0_OWN; + + if (ls_ic) + tdes1 |= TDES1_LAST_SEGMENT | TDES1_INTERRUPT; + p->des1 = tdes1; if (mode == STMMAC_CHAIN_MODE) @@ -212,11 +219,6 @@ static void ndesc_clear_tx_ic(struct dma_desc *p) p->des1 &= ~TDES1_INTERRUPT; } -static void ndesc_close_tx_desc(struct dma_desc *p) -{ - p->des1 |= TDES1_LAST_SEGMENT | TDES1_INTERRUPT; -} - static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) { unsigned int csum = 0; @@ -278,7 +280,6 @@ const struct stmmac_desc_ops ndesc_ops = { .release_tx_desc = ndesc_release_tx_desc, .prepare_tx_desc = ndesc_prepare_tx_desc, .clear_tx_ic = ndesc_clear_tx_ic, - .close_tx_desc = ndesc_close_tx_desc, .get_tx_ls = ndesc_get_tx_ls, .set_tx_owner = ndesc_set_tx_owner, .set_rx_owner = ndesc_set_rx_owner, diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index c6487746b235..11c71644f126 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -61,7 +61,7 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des3 = desc->des2 + BUF_SIZE_4KiB; priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, - STMMAC_RING_MODE); + STMMAC_RING_MODE, 0, false); wmb(); priv->tx_skbuff[entry] = NULL; entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); @@ -81,9 +81,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des3 = desc->des2 + BUF_SIZE_4KiB; priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, - STMMAC_RING_MODE); + STMMAC_RING_MODE, 1, true); wmb(); - priv->hw->desc->set_tx_owner(desc); } else { desc->des2 = dma_map_single(priv->device, skb->data, nopaged_len, DMA_TO_DEVICE); @@ -94,7 +93,7 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) priv->tx_skbuff_dma[entry].is_jumbo = true; desc->des3 = desc->des2 + BUF_SIZE_4KiB; priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum, - STMMAC_RING_MODE); + STMMAC_RING_MODE, 0, true); } priv->cur_tx = entry; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 796d7c69f902..24c36084e3f5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1991,8 +1991,10 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) goto dma_map_err; priv->tx_skbuff_dma[entry].buf = desc->des2; priv->tx_skbuff_dma[entry].len = nopaged_len; + /* do not set the own at this stage */ priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, - csum_insertion, priv->mode); + csum_insertion, priv->mode, 0, + nfrags == 0); } else { desc = first; entry = priv->hw->mode->jumbo_frm(priv, skb, csum_insertion); @@ -2003,6 +2005,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) for (i = 0; i < nfrags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; int len = skb_frag_size(frag); + bool last_segment = (i == (nfrags - 1)); priv->tx_skbuff[entry] = NULL; entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); @@ -2021,19 +2024,12 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) priv->tx_skbuff_dma[entry].map_as_page = true; priv->tx_skbuff_dma[entry].len = len; priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion, - priv->mode); - wmb(); - priv->hw->desc->set_tx_owner(desc); - wmb(); + priv->mode, 1, last_segment); + priv->tx_skbuff_dma[entry].last_segment = last_segment; } priv->tx_skbuff[entry] = skb; - /* Finalize the latest segment. */ - priv->hw->desc->close_tx_desc(desc); - priv->tx_skbuff_dma[entry].last_segment = true; - - wmb(); /* According to the coalesce parameter the IC bit for the latest * segment could be reset and the timer re-started to invoke the * stmmac_tx function. This approach takes care about the fragments. -- cgit v1.2.3 From c363b6586cd424431e84d921267e101ec67207f5 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Mon, 29 Feb 2016 14:27:36 +0100 Subject: stmmac: optimize tx clean function This patch "inline" get_tx_owner and get_ls routines. It Results in a unique read to tdes0, instead of three, to check TX_OWN and LS bits, and other status bits. It helps improve driver TX path by removing two uncached read/writes inside TX clean loop for enhanced descriptors but not for normal ones because the des1 must be read in any case. Signed-off-by: Fabrice Gasnier Acked-by: Giuseppe Cavallaro Signed-off-by: Alexandre TORGUE Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 8 +++++++ drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 12 ++++++++-- drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 13 +++++++++-- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 27 +++++++++++------------ 4 files changed, 42 insertions(+), 18 deletions(-) (limited to 'drivers/net/ethernet/stmicro/stmmac/enh_desc.c') diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 885c0f9808b6..7ccb147710fd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -245,6 +245,14 @@ enum rx_frame_status { dma_own = 0x8, }; +/* Tx status */ +enum tx_frame_status { + tx_done = 0x0, + tx_not_ls = 0x1, + tx_err = 0x2, + tx_dma_own = 0x4, +}; + enum dma_irq_status { tx_hard_error = 0x1, tx_hard_error_bump_tc = 0x2, diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 1abd80ed09f3..957610b72ace 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -31,7 +31,15 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x, { struct net_device_stats *stats = (struct net_device_stats *)data; unsigned int tdes0 = p->des0; - int ret = 0; + int ret = tx_done; + + /* Get tx owner first */ + if (unlikely(tdes0 & ETDES0_OWN)) + return tx_dma_own; + + /* Verify tx error by looking at the last segment. */ + if (likely(!(tdes0 & ETDES0_LAST_SEGMENT))) + return tx_not_ls; if (unlikely(tdes0 & ETDES0_ERROR_SUMMARY)) { if (unlikely(tdes0 & ETDES0_JABBER_TIMEOUT)) @@ -71,7 +79,7 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x, dwmac_dma_flush_tx_fifo(ioaddr); } - ret = -1; + ret = tx_err; } if (unlikely(tdes0 & ETDES0_DEFERRED)) diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index 19cc12dd0f17..122fb5ad234b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -31,7 +31,16 @@ static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x, { struct net_device_stats *stats = (struct net_device_stats *)data; unsigned int tdes0 = p->des0; - int ret = 0; + unsigned int tdes1 = p->des1; + int ret = tx_done; + + /* Get tx owner first */ + if (unlikely(tdes0 & TDES0_OWN)) + return tx_dma_own; + + /* Verify tx error by looking at the last segment. */ + if (likely(!(tdes1 & TDES1_LAST_SEGMENT))) + return tx_not_ls; if (unlikely(tdes0 & TDES0_ERROR_SUMMARY)) { if (unlikely(tdes0 & TDES0_UNDERFLOW_ERROR)) { @@ -54,7 +63,7 @@ static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x, collisions = (tdes0 & TDES0_COLLISION_COUNT_MASK) >> 3; stats->collisions += collisions; } - ret = -1; + ret = tx_err; } if (tdes0 & TDES0_VLAN_FRAME) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 24c36084e3f5..d31179f597a8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1313,32 +1313,31 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) priv->xstats.tx_clean++; while (entry != priv->cur_tx) { - int last; struct sk_buff *skb = priv->tx_skbuff[entry]; struct dma_desc *p; + int status; if (priv->extend_desc) p = (struct dma_desc *)(priv->dma_etx + entry); else p = priv->dma_tx + entry; - /* Check if the descriptor is owned by the DMA. */ - if (priv->hw->desc->get_tx_owner(p)) - break; - - /* Verify tx error by looking at the last segment. */ - last = priv->tx_skbuff_dma[entry].last_segment; - if (likely(last)) { - int tx_error = - priv->hw->desc->tx_status(&priv->dev->stats, + status = priv->hw->desc->tx_status(&priv->dev->stats, &priv->xstats, p, priv->ioaddr); - if (likely(tx_error == 0)) { + /* Check if the descriptor is owned by the DMA */ + if (unlikely(status & tx_dma_own)) + break; + + /* Just consider the last segment and ...*/ + if (likely(!(status & tx_not_ls))) { + /* ... verify the status error condition */ + if (unlikely(status & tx_err)) { + priv->dev->stats.tx_errors++; + } else { priv->dev->stats.tx_packets++; priv->xstats.tx_pkt_n++; - } else - priv->dev->stats.tx_errors++; - + } stmmac_get_tx_hwtstamp(priv, entry, skb); } if (netif_msg_tx_done(priv)) -- cgit v1.2.3 From 0e80bdc9a72df3b31a9fc2012102a6cc8d664e93 Mon Sep 17 00:00:00 2001 From: Giuseppe Cavallaro Date: Mon, 29 Feb 2016 14:27:38 +0100 Subject: stmmac: first frame prep at the end of xmit routine This patch is to fill the first descriptor just before granting the DMA engine so at the end of the xmit. The patch takes care about the algorithm adopted to mitigate the interrupts, then it fixes the last segment in case of no fragments. Moreover, this new implementation does not pass any "ter" field when prepare the descriptors because this is not necessary. The patch also details the memory barrier in the xmit. As final results, this patch guarantees the same performances but fixing a case if small datagram are sent. In fact, this kind of test is impacted if no coalesce is done. Signed-off-by: Fabrice Gasnier Signed-off-by: Giuseppe Cavallaro Signed-off-by: Alexandre TORGUE Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 6 +- drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 25 ++--- drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 24 ++--- drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 2 - .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 2 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 114 ++++++++++++--------- 6 files changed, 95 insertions(+), 78 deletions(-) (limited to 'drivers/net/ethernet/stmicro/stmmac/enh_desc.c') diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 7ccb147710fd..f96d257308b0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -100,7 +100,7 @@ struct stmmac_extra_stats { unsigned long napi_poll; unsigned long tx_normal_irq_n; unsigned long tx_clean; - unsigned long tx_reset_ic_bit; + unsigned long tx_set_ic_bit; unsigned long irq_receive_pmt_irq_n; /* MMC info */ unsigned long mmc_tx_irq_n; @@ -347,7 +347,7 @@ struct stmmac_desc_ops { /* Invoked by the xmit function to prepare the tx descriptor */ void (*prepare_tx_desc) (struct dma_desc *p, int is_fs, int len, bool csum_flag, int mode, bool tx_own, - bool ls_ic); + bool ls); /* Set/get the owner of the descriptor */ void (*set_tx_owner) (struct dma_desc *p); int (*get_tx_owner) (struct dma_desc *p); @@ -355,7 +355,7 @@ struct stmmac_desc_ops { void (*release_tx_desc) (struct dma_desc *p, int mode); /* Clear interrupt on tx frame completion. When this bit is * set an interrupt happens as soon as the frame is transmitted */ - void (*clear_tx_ic) (struct dma_desc *p); + void (*set_tx_ic)(struct dma_desc *p); /* Last tx segment reports the transmit status */ int (*get_tx_ls) (struct dma_desc *p); /* Return the transmit status looking at the TDES1 */ diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 957610b72ace..cfb018c7c5eb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -311,10 +311,15 @@ static void enh_desc_release_tx_desc(struct dma_desc *p, int mode) static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, bool csum_flag, int mode, bool tx_own, - bool ls_ic) + bool ls) { unsigned int tdes0 = p->des0; + if (mode == STMMAC_CHAIN_MODE) + enh_set_tx_desc_len_on_chain(p, len); + else + enh_set_tx_desc_len_on_ring(p, len); + if (is_fs) tdes0 |= ETDES0_FIRST_SEGMENT; else @@ -325,6 +330,10 @@ static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, else tdes0 &= ~(TX_CIC_FULL << ETDES0_CHECKSUM_INSERTION_SHIFT); + if (ls) + tdes0 |= ETDES0_LAST_SEGMENT; + + /* Finally set the OWN bit. Later the DMA will start! */ if (tx_own) tdes0 |= ETDES0_OWN; @@ -335,20 +344,12 @@ static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, */ wmb(); - if (ls_ic) - tdes0 |= ETDES0_LAST_SEGMENT | ETDES0_INTERRUPT; - p->des0 = tdes0; - - if (mode == STMMAC_CHAIN_MODE) - enh_set_tx_desc_len_on_chain(p, len); - else - enh_set_tx_desc_len_on_ring(p, len); } -static void enh_desc_clear_tx_ic(struct dma_desc *p) +static void enh_desc_set_tx_ic(struct dma_desc *p) { - p->des0 &= ~ETDES0_INTERRUPT; + p->des0 |= ETDES0_INTERRUPT; } static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) @@ -419,7 +420,7 @@ const struct stmmac_desc_ops enh_desc_ops = { .get_tx_owner = enh_desc_get_tx_owner, .release_tx_desc = enh_desc_release_tx_desc, .prepare_tx_desc = enh_desc_prepare_tx_desc, - .clear_tx_ic = enh_desc_clear_tx_ic, + .set_tx_ic = enh_desc_set_tx_ic, .get_tx_ls = enh_desc_get_tx_ls, .set_tx_owner = enh_desc_set_tx_owner, .set_rx_owner = enh_desc_set_rx_owner, diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index 122fb5ad234b..e13228f115f0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -195,10 +195,15 @@ static void ndesc_release_tx_desc(struct dma_desc *p, int mode) static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, bool csum_flag, int mode, bool tx_own, - bool ls_ic) + bool ls) { unsigned int tdes1 = p->des1; + if (mode == STMMAC_CHAIN_MODE) + norm_set_tx_desc_len_on_chain(p, len); + else + norm_set_tx_desc_len_on_ring(p, len); + if (is_fs) tdes1 |= TDES1_FIRST_SEGMENT; else @@ -209,23 +214,18 @@ static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, else tdes1 &= ~(TX_CIC_FULL << TDES1_CHECKSUM_INSERTION_SHIFT); + if (ls) + tdes1 |= TDES1_LAST_SEGMENT; + if (tx_own) tdes1 |= TDES0_OWN; - if (ls_ic) - tdes1 |= TDES1_LAST_SEGMENT | TDES1_INTERRUPT; - p->des1 = tdes1; - - if (mode == STMMAC_CHAIN_MODE) - norm_set_tx_desc_len_on_chain(p, len); - else - norm_set_tx_desc_len_on_ring(p, len); } -static void ndesc_clear_tx_ic(struct dma_desc *p) +static void ndesc_set_tx_ic(struct dma_desc *p) { - p->des1 &= ~TDES1_INTERRUPT; + p->des1 |= TDES1_INTERRUPT; } static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) @@ -288,7 +288,7 @@ const struct stmmac_desc_ops ndesc_ops = { .get_tx_owner = ndesc_get_tx_owner, .release_tx_desc = ndesc_release_tx_desc, .prepare_tx_desc = ndesc_prepare_tx_desc, - .clear_tx_ic = ndesc_clear_tx_ic, + .set_tx_ic = ndesc_set_tx_ic, .get_tx_ls = ndesc_get_tx_ls, .set_tx_owner = ndesc_set_tx_owner, .set_rx_owner = ndesc_set_rx_owner, diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index 11c71644f126..7723b5d2499a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -62,7 +62,6 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des3 = desc->des2 + BUF_SIZE_4KiB; priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_RING_MODE, 0, false); - wmb(); priv->tx_skbuff[entry] = NULL; entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); @@ -82,7 +81,6 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des3 = desc->des2 + BUF_SIZE_4KiB; priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, STMMAC_RING_MODE, 1, true); - wmb(); } else { desc->des2 = dma_map_single(priv->device, skb->data, nopaged_len, DMA_TO_DEVICE); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 4c6486cc80fb..c803d4cfa044 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -97,7 +97,7 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { STMMAC_STAT(napi_poll), STMMAC_STAT(tx_normal_irq_n), STMMAC_STAT(tx_clean), - STMMAC_STAT(tx_reset_ic_bit), + STMMAC_STAT(tx_set_ic_bit), STMMAC_STAT(irq_receive_pmt_irq_n), /* MMC info */ STMMAC_STAT(mmc_tx_irq_n), diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 2e4c10ab78bd..90b2612db03a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1942,12 +1942,12 @@ static int stmmac_release(struct net_device *dev) static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); - int entry; + unsigned int nopaged_len = skb_headlen(skb); int i, csum_insertion = 0, is_jumbo = 0; int nfrags = skb_shinfo(skb)->nr_frags; + unsigned int entry, first_entry; struct dma_desc *desc, *first; - unsigned int nopaged_len = skb_headlen(skb); - unsigned int enh_desc = priv->plat->enh_desc; + unsigned int enh_desc; spin_lock(&priv->tx_lock); @@ -1965,34 +1965,25 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) stmmac_disable_eee_mode(priv); entry = priv->cur_tx; - + first_entry = entry; csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL); - if (priv->extend_desc) + if (likely(priv->extend_desc)) desc = (struct dma_desc *)(priv->dma_etx + entry); else desc = priv->dma_tx + entry; first = desc; + priv->tx_skbuff[first_entry] = skb; + + enh_desc = priv->plat->enh_desc; /* To program the descriptors according to the size of the frame */ if (enh_desc) is_jumbo = priv->hw->mode->is_jumbo_frm(skb->len, enh_desc); - if (likely(!is_jumbo)) { - desc->des2 = dma_map_single(priv->device, skb->data, - nopaged_len, DMA_TO_DEVICE); - if (dma_mapping_error(priv->device, desc->des2)) - goto dma_map_err; - priv->tx_skbuff_dma[entry].buf = desc->des2; - priv->tx_skbuff_dma[entry].len = nopaged_len; - /* do not set the own at this stage */ - priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, - csum_insertion, priv->mode, 0, - nfrags == 0); - } else { - desc = first; + if (unlikely(is_jumbo)) { entry = priv->hw->mode->jumbo_frm(priv, skb, csum_insertion); if (unlikely(entry < 0)) goto dma_map_err; @@ -2003,10 +1994,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) int len = skb_frag_size(frag); bool last_segment = (i == (nfrags - 1)); - priv->tx_skbuff[entry] = NULL; entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); - if (priv->extend_desc) + if (likely(priv->extend_desc)) desc = (struct dma_desc *)(priv->dma_etx + entry); else desc = priv->dma_tx + entry; @@ -2016,41 +2006,25 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (dma_mapping_error(priv->device, desc->des2)) goto dma_map_err; /* should reuse desc w/o issues */ + priv->tx_skbuff[entry] = NULL; priv->tx_skbuff_dma[entry].buf = desc->des2; priv->tx_skbuff_dma[entry].map_as_page = true; priv->tx_skbuff_dma[entry].len = len; + priv->tx_skbuff_dma[entry].last_segment = last_segment; + + /* Prepare the descriptor and set the own bit too */ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion, priv->mode, 1, last_segment); - priv->tx_skbuff_dma[entry].last_segment = last_segment; } - priv->tx_skbuff[entry] = skb; - - /* According to the coalesce parameter the IC bit for the latest - * segment could be reset and the timer re-started to invoke the - * stmmac_tx function. This approach takes care about the fragments. - */ - priv->tx_count_frames += nfrags + 1; - if (priv->tx_coal_frames > priv->tx_count_frames) { - priv->hw->desc->clear_tx_ic(desc); - priv->xstats.tx_reset_ic_bit++; - mod_timer(&priv->txtimer, - STMMAC_COAL_TIMER(priv->tx_coal_timer)); - } else - priv->tx_count_frames = 0; - - /* To avoid raise condition */ - priv->hw->desc->set_tx_owner(first); - wmb(); - entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); priv->cur_tx = entry; if (netif_msg_pktdata(priv)) { - pr_debug("%s: curr %d dirty=%d entry=%d, first=%p, nfrags=%d", - __func__, (priv->cur_tx % DMA_TX_SIZE), - (priv->dirty_tx % DMA_TX_SIZE), entry, first, nfrags); + pr_debug("%s: curr=%d dirty=%d f=%d, e=%d, first=%p, nfrags=%d", + __func__, priv->cur_tx, priv->dirty_tx, first_entry, + entry, first, nfrags); if (priv->extend_desc) stmmac_display_ring((void *)priv->dma_etx, @@ -2062,6 +2036,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) pr_debug(">>> frame to be transmitted: "); print_pkt(skb->data, skb->len); } + if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { if (netif_msg_hw(priv)) pr_debug("%s: stop transmitted packets\n", __func__); @@ -2070,16 +2045,59 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) dev->stats.tx_bytes += skb->len; - if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && - priv->hwts_tx_en)) { - /* declare that device is doing timestamping */ - skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; - priv->hw->desc->enable_tx_timestamp(first); + /* According to the coalesce parameter the IC bit for the latest + * segment is reset and the timer re-started to clean the tx status. + * This approach takes care about the fragments: desc is the first + * element in case of no SG. + */ + priv->tx_count_frames += nfrags + 1; + if (likely(priv->tx_coal_frames > priv->tx_count_frames)) { + mod_timer(&priv->txtimer, + STMMAC_COAL_TIMER(priv->tx_coal_timer)); + } else { + priv->tx_count_frames = 0; + priv->hw->desc->set_tx_ic(desc); + priv->xstats.tx_set_ic_bit++; } if (!priv->hwts_tx_en) skb_tx_timestamp(skb); + /* Ready to fill the first descriptor and set the OWN bit w/o any + * problems because all the descriptors are actually ready to be + * passed to the DMA engine. + */ + if (likely(!is_jumbo)) { + bool last_segment = (nfrags == 0); + + first->des2 = dma_map_single(priv->device, skb->data, + nopaged_len, DMA_TO_DEVICE); + if (dma_mapping_error(priv->device, first->des2)) + goto dma_map_err; + + priv->tx_skbuff_dma[first_entry].buf = first->des2; + priv->tx_skbuff_dma[first_entry].len = nopaged_len; + priv->tx_skbuff_dma[first_entry].last_segment = last_segment; + + if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + priv->hwts_tx_en)) { + /* declare that device is doing timestamping */ + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + priv->hw->desc->enable_tx_timestamp(first); + } + + /* Prepare the first descriptor setting the OWN bit too */ + priv->hw->desc->prepare_tx_desc(first, 1, nopaged_len, + csum_insertion, priv->mode, 1, + last_segment); + + /* The own bit must be the latest setting done when prepare the + * descriptor and then barrier is needed to make sure that + * all is coherent before granting the DMA engine. + */ + smp_wmb(); + } + netdev_sent_queue(dev, skb->len); priv->hw->dma->enable_dma_transmission(priv->ioaddr); -- cgit v1.2.3