core.c
来自「linux 内核源代码」· C语言 代码 · 共 2,457 行 · 第 1/5 页
C
2,457 行
goto again; } if (n) { dev->ack_slot = slot; if (netif_queue_stopped(dev->ndev) && dev->tx_cnt < EMAC_TX_WAKEUP_THRESH) netif_wake_queue(dev->ndev); DBG2(dev, "tx %d pkts" NL, n); } } netif_tx_unlock_bh(dev->ndev);}static inline void emac_recycle_rx_skb(struct emac_instance *dev, int slot, int len){ struct sk_buff *skb = dev->rx_skb[slot]; DBG2(dev, "recycle %d %d" NL, slot, len); if (len) dma_map_single(&dev->ofdev->dev, skb->data - 2, EMAC_DMA_ALIGN(len + 2), DMA_FROM_DEVICE); dev->rx_desc[slot].data_len = 0; wmb(); dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY | (slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);}static void emac_parse_rx_error(struct emac_instance *dev, u16 ctrl){ struct emac_error_stats *st = &dev->estats; DBG(dev, "BD RX error %04x" NL, ctrl); ++st->rx_bd_errors; if (ctrl & EMAC_RX_ST_OE) ++st->rx_bd_overrun; if (ctrl & EMAC_RX_ST_BP) ++st->rx_bd_bad_packet; if (ctrl & EMAC_RX_ST_RP) ++st->rx_bd_runt_packet; if (ctrl & EMAC_RX_ST_SE) ++st->rx_bd_short_event; if (ctrl & EMAC_RX_ST_AE) ++st->rx_bd_alignment_error; if (ctrl & EMAC_RX_ST_BFCS) ++st->rx_bd_bad_fcs; if (ctrl & EMAC_RX_ST_PTL) ++st->rx_bd_packet_too_long; if (ctrl & EMAC_RX_ST_ORE) ++st->rx_bd_out_of_range; if (ctrl & EMAC_RX_ST_IRE) ++st->rx_bd_in_range;}static inline void emac_rx_csum(struct emac_instance *dev, struct sk_buff *skb, u16 ctrl){#ifdef CONFIG_IBM_NEW_EMAC_TAH if (!ctrl && dev->tah_dev) { skb->ip_summed = CHECKSUM_UNNECESSARY; ++dev->stats.rx_packets_csum; }#endif}static inline int emac_rx_sg_append(struct emac_instance *dev, int slot){ if (likely(dev->rx_sg_skb != NULL)) { int len = dev->rx_desc[slot].data_len; int tot_len = dev->rx_sg_skb->len + len; if (unlikely(tot_len + 2 > dev->rx_skb_size)) { ++dev->estats.rx_dropped_mtu; dev_kfree_skb(dev->rx_sg_skb); dev->rx_sg_skb = NULL; } else { cacheable_memcpy(skb_tail_pointer(dev->rx_sg_skb), dev->rx_skb[slot]->data, len); skb_put(dev->rx_sg_skb, len); emac_recycle_rx_skb(dev, slot, len); return 0; } } emac_recycle_rx_skb(dev, slot, 0); return -1;}/* NAPI poll context */static int emac_poll_rx(void *param, int budget){ struct emac_instance *dev = param; int slot = dev->rx_slot, received = 0; DBG2(dev, "poll_rx(%d)" NL, budget); again: while (budget > 0) { int len; struct sk_buff *skb; u16 ctrl = dev->rx_desc[slot].ctrl; if (ctrl & MAL_RX_CTRL_EMPTY) break; skb = dev->rx_skb[slot]; mb(); len = dev->rx_desc[slot].data_len; if (unlikely(!MAL_IS_SINGLE_RX(ctrl))) goto sg; ctrl &= EMAC_BAD_RX_MASK; if (unlikely(ctrl && ctrl != EMAC_RX_TAH_BAD_CSUM)) { emac_parse_rx_error(dev, ctrl); ++dev->estats.rx_dropped_error; emac_recycle_rx_skb(dev, slot, 0); len = 0; goto next; } if (len && len < EMAC_RX_COPY_THRESH) { struct sk_buff *copy_skb = alloc_skb(len + EMAC_RX_SKB_HEADROOM + 2, GFP_ATOMIC); if (unlikely(!copy_skb)) goto oom; skb_reserve(copy_skb, EMAC_RX_SKB_HEADROOM + 2); cacheable_memcpy(copy_skb->data - 2, skb->data - 2, len + 2); emac_recycle_rx_skb(dev, slot, len); skb = copy_skb; } else if (unlikely(emac_alloc_rx_skb(dev, slot, GFP_ATOMIC))) goto oom; skb_put(skb, len); push_packet: skb->dev = dev->ndev; skb->protocol = eth_type_trans(skb, dev->ndev); emac_rx_csum(dev, skb, ctrl); if (unlikely(netif_receive_skb(skb) == NET_RX_DROP)) ++dev->estats.rx_dropped_stack; next: ++dev->stats.rx_packets; skip: dev->stats.rx_bytes += len; slot = (slot + 1) % NUM_RX_BUFF; --budget; ++received; continue; sg: if (ctrl & MAL_RX_CTRL_FIRST) { BUG_ON(dev->rx_sg_skb); if (unlikely(emac_alloc_rx_skb(dev, slot, GFP_ATOMIC))) { DBG(dev, "rx OOM %d" NL, slot); ++dev->estats.rx_dropped_oom; emac_recycle_rx_skb(dev, slot, 0); } else { dev->rx_sg_skb = skb; skb_put(skb, len); } } else if (!emac_rx_sg_append(dev, slot) && (ctrl & MAL_RX_CTRL_LAST)) { skb = dev->rx_sg_skb; dev->rx_sg_skb = NULL; ctrl &= EMAC_BAD_RX_MASK; if (unlikely(ctrl && ctrl != EMAC_RX_TAH_BAD_CSUM)) { emac_parse_rx_error(dev, ctrl); ++dev->estats.rx_dropped_error; dev_kfree_skb(skb); len = 0; } else goto push_packet; } goto skip; oom: DBG(dev, "rx OOM %d" NL, slot); /* Drop the packet and recycle skb */ ++dev->estats.rx_dropped_oom; emac_recycle_rx_skb(dev, slot, 0); goto next; } if (received) { DBG2(dev, "rx %d BDs" NL, received); dev->rx_slot = slot; } if (unlikely(budget && test_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags))) { mb(); if (!(dev->rx_desc[slot].ctrl & MAL_RX_CTRL_EMPTY)) { DBG2(dev, "rx restart" NL); received = 0; goto again; } if (dev->rx_sg_skb) { DBG2(dev, "dropping partial rx packet" NL); ++dev->estats.rx_dropped_error; dev_kfree_skb(dev->rx_sg_skb); dev->rx_sg_skb = NULL; } clear_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags); mal_enable_rx_channel(dev->mal, dev->mal_rx_chan); emac_rx_enable(dev); dev->rx_slot = 0; } return received;}/* NAPI poll context */static int emac_peek_rx(void *param){ struct emac_instance *dev = param; return !(dev->rx_desc[dev->rx_slot].ctrl & MAL_RX_CTRL_EMPTY);}/* NAPI poll context */static int emac_peek_rx_sg(void *param){ struct emac_instance *dev = param; int slot = dev->rx_slot; while (1) { u16 ctrl = dev->rx_desc[slot].ctrl; if (ctrl & MAL_RX_CTRL_EMPTY) return 0; else if (ctrl & MAL_RX_CTRL_LAST) return 1; slot = (slot + 1) % NUM_RX_BUFF; /* I'm just being paranoid here :) */ if (unlikely(slot == dev->rx_slot)) return 0; }}/* Hard IRQ */static void emac_rxde(void *param){ struct emac_instance *dev = param; ++dev->estats.rx_stopped; emac_rx_disable_async(dev);}/* Hard IRQ */static irqreturn_t emac_irq(int irq, void *dev_instance){ struct emac_instance *dev = dev_instance; struct emac_regs __iomem *p = dev->emacp; struct emac_error_stats *st = &dev->estats; u32 isr; spin_lock(&dev->lock); isr = in_be32(&p->isr); out_be32(&p->isr, isr); DBG(dev, "isr = %08x" NL, isr); if (isr & EMAC4_ISR_TXPE) ++st->tx_parity; if (isr & EMAC4_ISR_RXPE) ++st->rx_parity; if (isr & EMAC4_ISR_TXUE) ++st->tx_underrun; if (isr & EMAC4_ISR_RXOE) ++st->rx_fifo_overrun; if (isr & EMAC_ISR_OVR) ++st->rx_overrun; if (isr & EMAC_ISR_BP) ++st->rx_bad_packet; if (isr & EMAC_ISR_RP) ++st->rx_runt_packet; if (isr & EMAC_ISR_SE) ++st->rx_short_event; if (isr & EMAC_ISR_ALE) ++st->rx_alignment_error; if (isr & EMAC_ISR_BFCS) ++st->rx_bad_fcs; if (isr & EMAC_ISR_PTLE) ++st->rx_packet_too_long; if (isr & EMAC_ISR_ORE) ++st->rx_out_of_range; if (isr & EMAC_ISR_IRE) ++st->rx_in_range; if (isr & EMAC_ISR_SQE) ++st->tx_sqe; if (isr & EMAC_ISR_TE) ++st->tx_errors; spin_unlock(&dev->lock); return IRQ_HANDLED;}static struct net_device_stats *emac_stats(struct net_device *ndev){ struct emac_instance *dev = netdev_priv(ndev); struct emac_stats *st = &dev->stats; struct emac_error_stats *est = &dev->estats; struct net_device_stats *nst = &dev->nstats; unsigned long flags; DBG2(dev, "stats" NL); /* Compute "legacy" statistics */ spin_lock_irqsave(&dev->lock, flags); nst->rx_packets = (unsigned long)st->rx_packets; nst->rx_bytes = (unsigned long)st->rx_bytes; nst->tx_packets = (unsigned long)st->tx_packets; nst->tx_bytes = (unsigned long)st->tx_bytes; nst->rx_dropped = (unsigned long)(est->rx_dropped_oom + est->rx_dropped_error + est->rx_dropped_resize + est->rx_dropped_mtu); nst->tx_dropped = (unsigned long)est->tx_dropped; nst->rx_errors = (unsigned long)est->rx_bd_errors; nst->rx_fifo_errors = (unsigned long)(est->rx_bd_overrun + est->rx_fifo_overrun + est->rx_overrun); nst->rx_frame_errors = (unsigned long)(est->rx_bd_alignment_error + est->rx_alignment_error); nst->rx_crc_errors = (unsigned long)(est->rx_bd_bad_fcs + est->rx_bad_fcs); nst->rx_length_errors = (unsigned long)(est->rx_bd_runt_packet + est->rx_bd_short_event + est->rx_bd_packet_too_long + est->rx_bd_out_of_range + est->rx_bd_in_range + est->rx_runt_packet + est->rx_short_event + est->rx_packet_too_long + est->rx_out_of_range + est->rx_in_range); nst->tx_errors = (unsigned long)(est->tx_bd_errors + est->tx_errors); nst->tx_fifo_errors = (unsigned long)(est->tx_bd_underrun + est->tx_underrun); nst->tx_carrier_errors = (unsigned long)est->tx_bd_carrier_loss; nst->collisions = (unsigned long)(est->tx_bd_excessive_deferral + est->tx_bd_excessive_collisions + est->tx_bd_late_collision + est->tx_bd_multple_collisions); spin_unlock_irqrestore(&dev->lock, flags); return nst;}static struct mal_commac_ops emac_commac_ops = { .poll_tx = &emac_poll_tx, .poll_rx = &emac_poll_rx, .peek_rx = &emac_peek_rx, .rxde = &emac_rxde,};static struct mal_commac_ops emac_commac_sg_ops = { .poll_tx = &emac_poll_tx, .poll_rx = &emac_poll_rx, .peek_rx = &emac_peek_rx_sg, .rxde = &emac_rxde,};/* Ethtool support */static int emac_ethtool_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd){ struct emac_instance *dev = netdev_priv(ndev); cmd->supported = dev->phy.features; cmd->port = PORT_MII; cmd->phy_address = dev->phy.address; cmd->transceiver = dev->phy.address >= 0 ? XCVR_EXTERNAL : XCVR_INTERNAL; mutex_lock(&dev->link_lock); cmd->advertising = dev->phy.advertising; cmd->autoneg = dev->phy.autoneg; cmd->speed = dev->phy.speed; cmd->duplex = dev->phy.duplex; mutex_unlock(&dev->link_lock); return 0;}static int emac_ethtool_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd){ struct emac_instance *dev = netdev_priv(ndev); u32 f = dev->phy.features; DBG(dev, "set_settings(%d, %d, %d, 0x%08x)" NL, cmd->autoneg, cmd->speed, cmd->duplex, cmd->advertising); /* Basic sanity checks */ if (dev->phy.address < 0) return -EOPNOTSUPP; if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg != AUTONEG_DISABLE) return -EINVAL; if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0) return -EINVAL; if (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) return -EINVAL; if (cmd->autoneg == AUTONEG_DISABLE) { switch (cmd->speed) { case SPEED_10: if (cmd->duplex == DUPLEX_HALF && !(f & SUPPORTED_10baseT_Half)) return -EINVAL; if (cmd->duplex == DUPLEX_FULL && !(f & SUPPORTED_10baseT_Full)) return -EINVAL; break; case SPEED_100: if (cmd->duplex == DUPLEX_HALF && !(f & SUPPORTED_100baseT_Half)) return -EINVAL; if (cmd->duplex == DUPLEX_FULL && !(f & SUPPORTED_100baseT_Full)) return -EINVAL; break; case SPEED_1000: if (cmd->duplex == DUPLEX_HALF && !(f & SUPPORTED_1000baseT_Half)) return -EINVAL; if (cmd->duplex == DUPLEX_FULL && !(f & SUPPORTED_1000baseT_Full)) return -EINVAL; break; default: return -EINVAL; } mutex_lock(&dev->link_lock); dev->phy.def->ops->setup_forced(&dev->phy, cmd->speed, cmd->duplex); mutex_unlock(&dev->link_lock); } else { if (!(f & SUPPORTED_Autoneg)) return -EINVAL; mutex_lock(&dev->link_lock); dev->phy.def->ops->setup_aneg(&dev->phy, (cmd->advertising & f) | (dev->phy.advertising & (ADVERTISED_Pause | ADVERTISED_Asym_Pause))); mutex_unlock(&dev->link_lock); } emac_force_link_update(dev); return 0;}static void emac_ethtool_get_ringparam(struct net_device *ndev, struct ethtool_ringparam *rp){ rp->rx_max_pending = rp->rx_pending = NUM_RX_BUFF; rp->tx_max_pending = rp->tx_pending = NUM_TX_BUFF;}static void emac_ethtool_get_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *pp){ struct emac_instance *dev = netdev_priv(ndev); mutex_lock(&dev->link_lock); if ((dev->phy.features & SUPPORTED_Autoneg) && (dev->phy.advertising & (ADVERTISED_Pause | ADVERTISED_Asym_Pause))) pp->autoneg = 1; if (dev->phy.duplex == DUPLEX_FULL) { if (dev->phy.pause) pp->rx_pause = pp->tx_pause = 1; else if (dev->phy.asym_pause) pp->tx_pause = 1; } mutex_unlock(&dev->link_lock);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?