diff options
Diffstat (limited to 'drivers/net/r8169.c')
| -rw-r--r-- | drivers/net/r8169.c | 111 | 
1 files changed, 61 insertions, 50 deletions
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 0b6e8c89683..3b19e0ce290 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -66,7 +66,6 @@ static const int multicast_filter_limit = 32;  #define RX_DMA_BURST	6	/* Maximum PCI burst, '6' is 1024 */  #define TX_DMA_BURST	6	/* Maximum PCI burst, '6' is 1024 */  #define EarlyTxThld	0x3F	/* 0x3F means NO early transmit */ -#define RxPacketMaxSize	0x3FE8	/* 16K - 1 - ETH_HLEN - VLAN - CRC... */  #define SafeMtu		0x1c20	/* ... actually life sucks beyond ~7k */  #define InterFrameGap	0x03	/* 3 means InterFrameGap = the shortest one */ @@ -2357,10 +2356,10 @@ static u16 rtl_rw_cpluscmd(void __iomem *ioaddr)  	return cmd;  } -static void rtl_set_rx_max_size(void __iomem *ioaddr) +static void rtl_set_rx_max_size(void __iomem *ioaddr, unsigned int rx_buf_sz)  {  	/* Low hurts. Let's disable the filtering. */ -	RTL_W16(RxMaxSize, 16383); +	RTL_W16(RxMaxSize, rx_buf_sz);  }  static void rtl8169_set_magic_reg(void __iomem *ioaddr, unsigned mac_version) @@ -2407,7 +2406,7 @@ static void rtl_hw_start_8169(struct net_device *dev)  	RTL_W8(EarlyTxThres, EarlyTxThld); -	rtl_set_rx_max_size(ioaddr); +	rtl_set_rx_max_size(ioaddr, tp->rx_buf_sz);  	if ((tp->mac_version == RTL_GIGA_MAC_VER_01) ||  	    (tp->mac_version == RTL_GIGA_MAC_VER_02) || @@ -2668,7 +2667,7 @@ static void rtl_hw_start_8168(struct net_device *dev)  	RTL_W8(EarlyTxThres, EarlyTxThld); -	rtl_set_rx_max_size(ioaddr); +	rtl_set_rx_max_size(ioaddr, tp->rx_buf_sz);  	tp->cp_cmd |= RTL_R16(CPlusCmd) | PktCntrDisable | INTT_1; @@ -2846,7 +2845,7 @@ static void rtl_hw_start_8101(struct net_device *dev)  	RTL_W8(EarlyTxThres, EarlyTxThld); -	rtl_set_rx_max_size(ioaddr); +	rtl_set_rx_max_size(ioaddr, tp->rx_buf_sz);  	tp->cp_cmd |= rtl_rw_cpluscmd(ioaddr) | PCIMulRW; @@ -3554,54 +3553,64 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)  	int handled = 0;  	int status; +	/* loop handling interrupts until we have no new ones or +	 * we hit a invalid/hotplug case. +	 */  	status = RTL_R16(IntrStatus); +	while (status && status != 0xffff) { +		handled = 1; -	/* hotplug/major error/no more work/shared irq */ -	if ((status == 0xffff) || !status) -		goto out; +		/* Handle all of the error cases first. These will reset +		 * the chip, so just exit the loop. +		 */ +		if (unlikely(!netif_running(dev))) { +			rtl8169_asic_down(ioaddr); +			break; +		} -	handled = 1; +		/* Work around for rx fifo overflow */ +		if (unlikely(status & RxFIFOOver) && +		(tp->mac_version == RTL_GIGA_MAC_VER_11)) { +			netif_stop_queue(dev); +			rtl8169_tx_timeout(dev); +			break; +		} -	if (unlikely(!netif_running(dev))) { -		rtl8169_asic_down(ioaddr); -		goto out; -	} +		if (unlikely(status & SYSErr)) { +			rtl8169_pcierr_interrupt(dev); +			break; +		} -	status &= tp->intr_mask; -	RTL_W16(IntrStatus, -		(status & RxFIFOOver) ? (status | RxOverflow) : status); +		if (status & LinkChg) +			rtl8169_check_link_status(dev, tp, ioaddr); -	if (!(status & tp->intr_event)) -		goto out; +		/* We need to see the lastest version of tp->intr_mask to +		 * avoid ignoring an MSI interrupt and having to wait for +		 * another event which may never come. +		 */ +		smp_rmb(); +		if (status & tp->intr_mask & tp->napi_event) { +			RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event); +			tp->intr_mask = ~tp->napi_event; -	/* Work around for rx fifo overflow */ -	if (unlikely(status & RxFIFOOver) && -	    (tp->mac_version == RTL_GIGA_MAC_VER_11)) { -		netif_stop_queue(dev); -		rtl8169_tx_timeout(dev); -		goto out; -	} +			if (likely(napi_schedule_prep(&tp->napi))) +				__napi_schedule(&tp->napi); +			else if (netif_msg_intr(tp)) { +				printk(KERN_INFO "%s: interrupt %04x in poll\n", +				dev->name, status); +			} +		} -	if (unlikely(status & SYSErr)) { -		rtl8169_pcierr_interrupt(dev); -		goto out; +		/* We only get a new MSI interrupt when all active irq +		 * sources on the chip have been acknowledged. So, ack +		 * everything we've seen and check if new sources have become +		 * active to avoid blocking all interrupts from the chip. +		 */ +		RTL_W16(IntrStatus, +			(status & RxFIFOOver) ? (status | RxOverflow) : status); +		status = RTL_R16(IntrStatus);  	} -	if (status & LinkChg) -		rtl8169_check_link_status(dev, tp, ioaddr); - -	if (status & tp->napi_event) { -		RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event); -		tp->intr_mask = ~tp->napi_event; - -		if (likely(napi_schedule_prep(&tp->napi))) -			__napi_schedule(&tp->napi); -		else if (netif_msg_intr(tp)) { -			printk(KERN_INFO "%s: interrupt %04x in poll\n", -			       dev->name, status); -		} -	} -out:  	return IRQ_RETVAL(handled);  } @@ -3617,13 +3626,15 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)  	if (work_done < budget) {  		napi_complete(napi); -		tp->intr_mask = 0xffff; -		/* -		 * 20040426: the barrier is not strictly required but the -		 * behavior of the irq handler could be less predictable -		 * without it. Btw, the lack of flush for the posted pci -		 * write is safe - FR + +		/* We need for force the visibility of tp->intr_mask +		 * for other CPUs, as we can loose an MSI interrupt +		 * and potentially wait for a retransmit timeout if we don't. +		 * The posted write to IntrMask is safe, as it will +		 * eventually make it to the chip and we won't loose anything +		 * until it does.  		 */ +		tp->intr_mask = 0xffff;  		smp_wmb();  		RTL_W16(IntrMask, tp->intr_event);  	}  |