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 + -
显示快捷键?