* Re: Netpoll checksum issue
[not found] <6d6a94c50604190922m189b9d99gdd428a870e12c2c3@mail.gmail.com>
@ 2006-04-19 17:31 ` Stephen Hemminger
2006-04-19 21:33 ` Herbert Xu
2006-04-20 1:54 ` Aubrey
0 siblings, 2 replies; 11+ messages in thread
From: Stephen Hemminger @ 2006-04-19 17:31 UTC (permalink / raw)
To: Aubrey, Herbert Xu; +Cc: netdev
The changes to how hardware receive checksums are handled broke
the netpoll checksum code (for CHECKSUM_HW). Since this is not at
all performance critical, try this patch. It changes to always to
normal software checksum.
--- linux-2.6.orig/net/core/netpoll.c 2006-03-22 09:30:56.000000000 -0800
+++ linux-2.6/net/core/netpoll.c 2006-04-19 10:30:13.000000000 -0700
@@ -102,20 +102,11 @@
static int checksum_udp(struct sk_buff *skb, struct udphdr *uh,
unsigned short ulen, u32 saddr, u32 daddr)
{
- unsigned int psum;
-
if (uh->check == 0 || skb->ip_summed == CHECKSUM_UNNECESSARY)
return 0;
- psum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
-
- if (skb->ip_summed == CHECKSUM_HW &&
- !(u16)csum_fold(csum_add(psum, skb->csum)))
- return 0;
-
- skb->csum = psum;
-
- return __skb_checksum_complete(skb);
+ skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
+ return (u16) csum_fold(skb_checksum(skb, 0, skb->len, skb->csum));
}
/*
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Netpoll checksum issue
2006-04-19 17:31 ` Netpoll checksum issue Stephen Hemminger
@ 2006-04-19 21:33 ` Herbert Xu
2006-04-20 1:54 ` Aubrey
1 sibling, 0 replies; 11+ messages in thread
From: Herbert Xu @ 2006-04-19 21:33 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: Aubrey, netdev
On Wed, Apr 19, 2006 at 10:31:53AM -0700, Stephen Hemminger wrote:
> The changes to how hardware receive checksums are handled broke
> the netpoll checksum code (for CHECKSUM_HW). Since this is not at
> all performance critical, try this patch. It changes to always to
> normal software checksum.
Hmm, why don't we try to find out what's actually broken? Papering over
this means that we could miss driver bugs in future.
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Netpoll checksum issue
2006-04-19 17:31 ` Netpoll checksum issue Stephen Hemminger
2006-04-19 21:33 ` Herbert Xu
@ 2006-04-20 1:54 ` Aubrey
2006-04-23 11:34 ` Herbert Xu
1 sibling, 1 reply; 11+ messages in thread
From: Aubrey @ 2006-04-20 1:54 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: Herbert Xu, netdev
On 4/20/06, Stephen Hemminger <shemminger@osdl.org> wrote:
> The changes to how hardware receive checksums are handled broke
> the netpoll checksum code (for CHECKSUM_HW). Since this is not at
> all performance critical, try this patch. It changes to always to
> normal software checksum.
>
Hi Stephen - The patch doesn't work for my driver.
Hi Herbert - I'm working on blackfin uclinux platform. My network
driver is for blackfin on-chip EMAC. Of course it's open source, but
so far it's not committed into mainline tree. We can be found at here:
http://www.blackfin.uclinux.org
My driver didn't assign any value to skb->ip_summed, and didn't
define tx/rx checksum routine like "e1000_rx_checksum", but it did
work before on 2.6.12. I'm trying to find what's broken.
Thanks,
-Aubrey
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Netpoll checksum issue
2006-04-20 1:54 ` Aubrey
@ 2006-04-23 11:34 ` Herbert Xu
2006-04-24 5:42 ` Aubrey
0 siblings, 1 reply; 11+ messages in thread
From: Herbert Xu @ 2006-04-23 11:34 UTC (permalink / raw)
To: Aubrey; +Cc: Stephen Hemminger, netdev
On Thu, Apr 20, 2006 at 09:54:54AM +0800, Aubrey wrote:
>
> Hi Herbert - I'm working on blackfin uclinux platform. My network
> driver is for blackfin on-chip EMAC. Of course it's open source, but
> so far it's not committed into mainline tree. We can be found at here:
> http://www.blackfin.uclinux.org
Please send me a copy of the driver source that you were using when
this happened.
> My driver didn't assign any value to skb->ip_summed, and didn't
> define tx/rx checksum routine like "e1000_rx_checksum", but it did
> work before on 2.6.12. I'm trying to find what's broken.
Could you please add a printk in checksum_udp to print out the pertinent
values such as the expected checksum and actual checksum?
Thanks,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Netpoll checksum issue
2006-04-23 11:34 ` Herbert Xu
@ 2006-04-24 5:42 ` Aubrey
2006-04-24 7:46 ` Aubrey
2006-04-24 10:22 ` Herbert Xu
0 siblings, 2 replies; 11+ messages in thread
From: Aubrey @ 2006-04-24 5:42 UTC (permalink / raw)
To: Herbert Xu; +Cc: Stephen Hemminger, netdev
[-- Attachment #1: Type: text/plain, Size: 1532 bytes --]
On 4/23/06, Herbert Xu <herbert@gondor.apana.org.au> wrote:
> On Thu, Apr 20, 2006 at 09:54:54AM +0800, Aubrey wrote:
> Please send me a copy of the driver source that you were using when
> this happened.
The driver is attached.
> Could you please add a printk in checksum_udp to print out the pertinent
> values such as the expected checksum and actual checksum?
>
It seems not only "checksum_udp" in "netpoll", a few other routines
will call "__skb_checksum_complete", So the routine
"__skb_checksum_complete" must be ok.
And,
The routine "csum_tcpudp_nofold" should be ok, too. Because it's
called by many other routines, too.
But, I don't understand, why assign "psum=csum_tcpudp_nofold()" to
"skb->csum", and make "__skb_checksum_complete" to do the checksum?
Thanks for your any hints
Regards,
-Aubrey
================================================================
static int checksum_udp(struct sk_buff *skb, struct udphdr *uh,
unsigned short ulen, u32 saddr, u32 daddr)
{
unsigned int psum;
if (uh->check == 0 || skb->ip_summed == CHECKSUM_UNNECESSARY)
return 0;
psum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
if (skb->ip_summed == CHECKSUM_HW &&
!(u16)csum_fold(csum_add(psum, skb->csum)))
return 0;
skb->csum = psum;
return __skb_checksum_complete(skb);
}
==================================================================
[-- Attachment #2: bfin_mac.c --]
[-- Type: text/plain, Size: 21501 bytes --]
static const char version[] =
"bf53mac.c: v1.0, Aug 27 2005 by Luke Yang <luke.yang@analog.com>\n";
/* Debugging level */
#ifndef BF537MAC_DEBUG
#define BF537MAC_DEBUG 0
#endif
#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/crc32.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/platform_device.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/dma.h>
#include <linux/dma-mapping.h>
#include "bfin_mac.h"
#include <asm/irq.h>
#include <asm/blackfin.h>
#include <asm/delay.h>
MODULE_LICENSE("GPL");
#define CARDNAME "bfin_mac"
static void desc_list_free(void);
/* transmit net_dma_desc numbers */
#define INIT_DESC_NUM 12
#define MAX_DESC_NUM 64
#define MAX_RX_DESC_NUM 8
/* pointers to maintain transmit list */
struct net_dma_desc *tx_list_head;
struct net_dma_desc *tx_list_tail;
struct net_dma_desc *rx_list_head;
struct net_dma_desc *rx_list_tail;
struct net_dma_desc *current_rx_ptr;
struct net_dma_desc *current_tx_ptr;
int current_desc_num;
extern unsigned long l1_data_A_sram_alloc(unsigned long size);
extern unsigned long l1_data_A_sram_free(unsigned long size);
extern void get_bf537_ether_addr(char *addr);
static int desc_list_init(void)
{
struct net_dma_desc *tmp_desc;
int i;
dma_addr_t dma_handle;
/* init tx_list */
if (current_desc_num == 0) {
for (i=0;i < INIT_DESC_NUM;i++) {
tmp_desc = (struct net_dma_desc *)dma_alloc_coherent(NULL, sizeof(struct net_dma_desc), &dma_handle , GFP_DMA);
//tmp_desc = (struct net_dma_desc *)l1_data_A_sram_alloc(sizeof(struct net_dma_desc));
if (tmp_desc == NULL)
goto error;
else
memset(tmp_desc,0,sizeof(tmp_desc));
if (i == 0) {
tx_list_head = tmp_desc;
tx_list_tail = tmp_desc;
}
tmp_desc->desc_a.start_addr = (unsigned long)tmp_desc->packet;
tmp_desc->desc_a.x_count = 0;
tmp_desc->desc_a.config.b_DMA_EN = 0; //disabled
tmp_desc->desc_a.config.b_WNR = 0; //read from memory
tmp_desc->desc_a.config.b_WDSIZE = 2; //wordsize is 32 bits
tmp_desc->desc_a.config.b_NDSIZE = 6; //6 half words is desc size.
tmp_desc->desc_a.config.b_FLOW = 7; //large desc flow
tmp_desc->desc_a.next_dma_desc = &(tmp_desc->desc_b);
tmp_desc->desc_b.start_addr = (unsigned long)(&(tmp_desc->status));
tmp_desc->desc_b.x_count = 0;
tmp_desc->desc_b.config.b_DMA_EN = 1; //disabled
tmp_desc->desc_b.config.b_WNR = 1; //write to memory
tmp_desc->desc_b.config.b_WDSIZE = 2; //wordsize is 32 bits
tmp_desc->desc_b.config.b_DI_EN = 0; //disable interrupt
tmp_desc->desc_b.config.b_NDSIZE = 6;
tmp_desc->desc_b.config.b_FLOW = 7; //stop mode
tx_list_tail->desc_b.next_dma_desc = &(tmp_desc->desc_a);
tx_list_tail->next = tmp_desc;
tx_list_tail = tmp_desc;
}
tx_list_tail->next = tx_list_head; /* tx_list is a circle */
tx_list_tail->desc_b.next_dma_desc = &(tx_list_head->desc_a);
current_desc_num = INIT_DESC_NUM;
current_tx_ptr = tx_list_head;
}
/* init rx_list */
for (i = 0; i < MAX_RX_DESC_NUM; i++) {
//tmp_desc = (struct net_dma_desc *)dma_alloc_coherent(NULL, sizeof(struct net_dma_desc), &dma_handle , GFP_DMA);
tmp_desc = (struct net_dma_desc *)l1_data_A_sram_alloc(sizeof(struct net_dma_desc));
if (tmp_desc == NULL)
goto error;
else
memset(tmp_desc,0,sizeof(tmp_desc));
if (i == 0) {
rx_list_head = tmp_desc;
rx_list_tail = tmp_desc;
}
tmp_desc->desc_a.start_addr = (unsigned long)tmp_desc->packet;
tmp_desc->desc_a.x_count = 0;
tmp_desc->desc_a.config.b_DMA_EN = 1; //enabled
tmp_desc->desc_a.config.b_WNR = 1; //Write to memory
tmp_desc->desc_a.config.b_WDSIZE = 2; //wordsize is 32 bits
tmp_desc->desc_a.config.b_NDSIZE = 6; //6 half words is desc size.
tmp_desc->desc_a.config.b_FLOW = 7; //large desc flow
tmp_desc->desc_a.next_dma_desc = &(tmp_desc->desc_b);
tmp_desc->desc_b.start_addr = (unsigned long)(&(tmp_desc->status));
tmp_desc->desc_b.x_count = 0;
tmp_desc->desc_b.config.b_DMA_EN = 1; //enabled
tmp_desc->desc_b.config.b_WNR = 1; //Write to memory
tmp_desc->desc_b.config.b_WDSIZE = 2; //wordsize is 32 bits
tmp_desc->desc_b.config.b_NDSIZE = 6;
tmp_desc->desc_b.config.b_DI_EN = 1; //enable interrupt
tmp_desc->desc_b.config.b_FLOW = 7; //stop
rx_list_tail->desc_b.next_dma_desc = &(tmp_desc->desc_a);
rx_list_tail->next = tmp_desc;
rx_list_tail = tmp_desc;
}
rx_list_tail->next = rx_list_head; /* rx_list is a circle */
rx_list_tail->desc_b.next_dma_desc = &(rx_list_head->desc_a);
current_rx_ptr = rx_list_head;
return 0;
error:
desc_list_free();
printk("bf537mac: kmalloc failed. \n");
return -ENOMEM;
}
static void desc_list_free(void)
{
struct net_dma_desc *tmp_desc;
int i;
dma_addr_t dma_handle = 0;
tmp_desc = tx_list_head;
for (i = 0; i < INIT_DESC_NUM; i++) {
if (tmp_desc != NULL)
dma_free_coherent(NULL, sizeof(struct net_dma_desc), tmp_desc, dma_handle);
tmp_desc = tmp_desc->next;
}
tmp_desc = rx_list_head;
for (i = 0; i < MAX_RX_DESC_NUM; i++) {
if (tmp_desc != NULL)
l1_data_A_sram_free((unsigned long)tmp_desc);
tmp_desc = tmp_desc->next;
}
}
/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
//
//Set FER regs to MUX in Ethernet pins
//
static void SetupPinMux(void)
{
unsigned int fer_val;
// FER reg bug work-around
// read it once
fer_val = *pPORTH_FER;
fer_val = 0xffff;
// write it twice to the same value
*pPORTH_FER = fer_val;
*pPORTH_FER = fer_val;
}
//
//Wait until the previous MDC/MDIO transaction has completed
//
static void PollMdcDone(void)
{
// poll the STABUSY bit
while((*pEMAC_STAADD) & STABUSY) {};
}
//
//Read an off-chip register in a PHY through the MDC/MDIO port
//
static u16 RdPHYReg(u16 PHYAddr, u16 RegAddr)
{
PollMdcDone();
*pEMAC_STAADD = SET_PHYAD(PHYAddr) | SET_REGAD(RegAddr) | STABUSY; // read mode
PollMdcDone();
return (u16)*pEMAC_STADAT;
}
//
//Write an off-chip register in a PHY through the MDC/MDIO port
//
static void RawWrPHYReg(u16 PHYAddr, u16 RegAddr, u32 Data)
{
*pEMAC_STADAT = Data;
*pEMAC_STAADD = SET_PHYAD(PHYAddr) | SET_REGAD(RegAddr) |
STAOP | STABUSY; //write mode
PollMdcDone();
}
static void WrPHYReg(u16 PHYAddr, u16 RegAddr, u32 Data)
{
PollMdcDone();
RawWrPHYReg(PHYAddr,RegAddr,Data);
}
//
//set up the phy
//
static int bf537mac_setphy(struct net_device *dev)
{
u16 phydat;
u32 sysctl;
struct bf537mac_local *lp = netdev_priv(dev);
//printk("bf537_mac: start settting up phy\n");
//Program PHY registers
phydat = 0;
// issue a reset
RawWrPHYReg(lp->PhyAddr, PHYREG_MODECTL, 0x8000);
// wait half a second
udelay(500);
phydat = RdPHYReg(lp->PhyAddr, PHYREG_MODECTL);
// advertise flow control supported
phydat = RdPHYReg(lp->PhyAddr, PHYREG_ANAR);
phydat |= (1 << 10);
WrPHYReg(lp->PhyAddr, PHYREG_ANAR, phydat);
phydat = 0;
if (lp->Negotiate) {
phydat |= 0x1000;// enable auto negotiation
} else {
if (lp->FullDuplex) {
phydat |= (1 << 8);// full duplex
} else {
phydat &= (~(1 << 8));// half duplex
}
if (!lp->Port10) {
phydat |= (1 << 13);// 100 Mbps
} else {
phydat &= (~(1 << 13));// 10 Mbps
}
}
if (lp->Loopback) {
phydat |= (1 << 14);// enable TX->RX loopback
//WrPHYReg(lp->PhyAddr, PHYREG_MODECTL, phydat);
}
WrPHYReg(lp->PhyAddr, PHYREG_MODECTL, phydat);
udelay(500);
phydat = RdPHYReg(lp->PhyAddr, PHYREG_MODECTL);
// check for SMSC PHY
if ((RdPHYReg(lp->PhyAddr, PHYREG_PHYID1) == 0x7) && ((RdPHYReg(lp->PhyAddr, PHYREG_PHYID2)&0xfff0 ) == 0xC0A0)) {
// we have SMSC PHY so reqest interrupt on link down condition
WrPHYReg(lp->PhyAddr, 30, 0x0ff); // enable interrupts
// enable PHY_INT
sysctl = *pEMAC_SYSCTL;
sysctl |= 0x1;
//*pEMAC_SYSCTL = sysctl;
}
}
/**************************************************************************/
void SetupSystemRegs(struct net_device *dev)
{
int PHYADDR;
unsigned short sysctl, phydat;
struct bf537mac_local *lp = netdev_priv(dev);
PHYADDR = lp->PhyAddr;
/* Enable PHY output */
*pVR_CTL |= PHYCLKOE;
/* MDC = 2.5 MHz */
sysctl = SET_MDCDIV(24);
/* Odd word alignment for Receive Frame DMA word */
/* Configure checksum support and rcve frame word alignment */
sysctl |= RXDWA;
*pEMAC_SYSCTL = sysctl;
/* auto negotiation on */
/* full duplex */
/* 100 Mbps */
phydat = PHY_ANEG_EN | PHY_DUPLEX | PHY_SPD_SET;
WrPHYReg(PHYADDR, PHYREG_MODECTL, phydat);
//*pEMAC_MMC_CTL = RSTC | CROLL | MMCE;
*pEMAC_MMC_CTL = RSTC | CROLL;
/* Initialize the TX DMA channel registers */
*pDMA2_X_COUNT = 0;
*pDMA2_X_MODIFY = 4;
*pDMA2_Y_COUNT = 0;
*pDMA2_Y_MODIFY = 0;
/* Initialize the RX DMA channel registers */
*pDMA1_X_COUNT = 0;
*pDMA1_X_MODIFY = 4;
*pDMA1_Y_COUNT = 0;
*pDMA1_Y_MODIFY = 0;
}
void SetupMacAddr(u8 *mac_addr)
{
// this depends on a little-endian machine
*pEMAC_ADDRLO = *(u32 *)&mac_addr[0];
*pEMAC_ADDRHI = *(u16 *)&mac_addr[4];
}
static void adjust_tx_list(void)
{
int i = 0;
/* current's next can not be the head, otherwise the dma will not stop as we want */
if (current_tx_ptr->next->next == tx_list_head) {
while (tx_list_head->status.status_word == 0) {
udelay(100);
i++;
if (i == 10) {
//printk("tx list error!\n");
i = 0;
tx_list_head->desc_a.config.b_DMA_EN = 0;
tx_list_head = tx_list_head->next;
break;
}
}
}
if ((tx_list_head->status.status_word != 0)) {
tx_list_head->status.status_word = 0;
tx_list_head->desc_a.config.b_DMA_EN = 0;
tx_list_head = tx_list_head->next;
}
}
static int bf537mac_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct bf537mac_local *lp = netdev_priv(dev);
unsigned int data;
/* warning: printk in this function may cause error */
// Is skb->data always 16-bit aligned? Do we need to memcpy((char *)(tail->packet + 2),skb->data,len)?
if ( (((unsigned int)(skb->data))%4) == 2 ) {
//move skb->data to current_tx_ptr payload
data = (unsigned int)(skb->data);
data -= 2;
*((unsigned short *)data) = (unsigned short)(skb->len);
current_tx_ptr->desc_a.start_addr = (unsigned long)data;
blackfin_dcache_invalidate_range(data, (data+(skb->len)) + 2); //this is important!
} else {
*((unsigned short *)(current_tx_ptr->packet)) = (unsigned short)(skb->len);
memcpy((char *)(current_tx_ptr->packet + 2),skb->data,(skb->len));
current_tx_ptr->desc_a.start_addr = (unsigned long)current_tx_ptr->packet;
/*why we need to invalidate uncached memory? */
blackfin_dcache_invalidate_range((unsigned int)current_tx_ptr->packet, (unsigned int)(current_tx_ptr->packet + skb->len) + 2);
}
current_tx_ptr->desc_a.config.b_DMA_EN = 1; //enable this packet's dma
if (*pDMA2_IRQ_STATUS & 0x08) { //tx dma is running, just return
goto out;
} else { //tx dma is not running
*pDMA2_NEXT_DESC_PTR = (&(current_tx_ptr->desc_a));
*pDMA2_CONFIG = *((unsigned short *)(&(current_tx_ptr->desc_a.config)));; // dma enabled, read from memory, size is 6
// Turn on the EMAC tx
*pEMAC_OPMODE |= TE;
}
out:
adjust_tx_list();
current_tx_ptr = current_tx_ptr->next;
dev->trans_start = jiffies;
lp->stats.tx_packets++;
lp->stats.tx_bytes += (skb->len);
dev_kfree_skb(skb);
//printk("sending one...\n");
return 0;
}
static void bf537mac_rx(struct net_device *dev, unsigned char *pkt, int len)
{
struct sk_buff *skb;
struct bf537mac_local *lp = netdev_priv(dev);
skb = dev_alloc_skb(len + 2);
if (!skb) {
printk(KERN_NOTICE "bf537mac rx: low on mem - packet dropped\n");
lp->stats.rx_dropped++;
goto out;
}
skb_reserve(skb, 2);
/*
if (len >= 300) {
printk("going to copy the big packet\n");
for (i=0;i<len;i++){
printk("%.2x-",((unsigned char *)pkt)[i]);
if (((i%8)==0) && (i!=0)) printk("\n");
}
printk("\n");
}
*/
memcpy(skb_put(skb, len), pkt+2, len);
dev->last_rx = jiffies;
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
netif_rx(skb);
lp->stats.rx_packets++;
lp->stats.rx_bytes += len;
out:
return;
}
/* interrupt routine to handle rx and error signal */
static irqreturn_t bf537mac_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct net_device *dev = dev_id;
unsigned short len;
// printk("in mac_int\n");
get_one_packet:
if (current_rx_ptr->status.status_word == 0) { // no more new packet received
*pDMA1_IRQ_STATUS |= DMA_DONE|DMA_ERR;
//printk("now return..\n");
return IRQ_HANDLED;
}
len = (unsigned short)((current_rx_ptr->status.status_word) & RX_FRLEN);
bf537mac_rx(dev, (char *)(current_rx_ptr->packet), len);
current_rx_ptr->status.status_word = 0x00000000;
current_rx_ptr = current_rx_ptr->next;
goto get_one_packet;
}
#ifdef CONFIG_NET_POLL_CONTROLLER
static void bf537mac_poll(struct net_device* dev)
{
disable_irq(IRQ_MAC_RX);
bf537mac_interrupt(IRQ_MAC_RX, dev, NULL);
enable_irq(IRQ_MAC_RX);
}
#endif /* CONFIG_NET_POLL_CONTROLLER */
static void bf537mac_reset(void)
{
unsigned int opmode;
opmode = *pEMAC_OPMODE;
opmode &= (~RE);
opmode &= (~TE);
/* Turn off the EMAC */
*pEMAC_OPMODE &= opmode;
}
/*
* Enable Interrupts, Receive, and Transmit
*/
static int bf537mac_enable(struct net_device *dev)
{
u32 opmode;
//u32 pkt_status;
//printk("%s: %s\n", dev->name, __FUNCTION__);
/* Set RX DMA */
*pDMA1_NEXT_DESC_PTR = &(rx_list_head->desc_a);
*pDMA1_CONFIG = *((unsigned short *)(&(rx_list_head->desc_a.config)));
/* Wait MII done */
PollMdcDone();
/* We enable only RX here */
/* ASTP : Enable Automatic Pad Stripping
PR : Promiscuous Mode for test
PSF : Receive frames with total length less than 64 bytes.
FDMODE : Full Duplex Mode
LB : Internal Loopback for test
RE : Receiver Enable */
opmode = FDMODE|PSF;
opmode |= RE;
/* Turn on the EMAC rx */
*pEMAC_OPMODE = opmode;
return 0;
}
/* Our watchdog timed out. Called by the networking layer */
static void bf537mac_timeout(struct net_device *dev)
{
//printk("%s: %s\n", dev->name, __FUNCTION__);
bf537mac_reset();
/* reset tx queue */
tx_list_tail = tx_list_head->next;
bf537mac_enable(dev);
/* We can accept TX packets again */
dev->trans_start = jiffies;
netif_wake_queue(dev);
}
/*
* Get the current statistics.
* This may be called with the card open or closed.
*/
static struct net_device_stats *bf537mac_query_statistics(struct net_device *dev)
{
struct bf537mac_local *lp = netdev_priv(dev);
//printk("%s: %s\n", dev->name, __FUNCTION__);
return &lp->stats;
}
/*
* This routine will, depending on the values passed to it,
* either make it accept multicast packets, go into
* promiscuous mode (for TCPDUMP and cousins) or accept
* a select set of multicast packets
*/
static void bf537mac_set_multicast_list(struct net_device *dev)
{
u32 sysctl;
if (dev->flags & IFF_PROMISC) {
printk(KERN_INFO "%s: set to promisc mode\n", dev->name);
sysctl = *pEMAC_OPMODE;
sysctl |= RAF;
*pEMAC_OPMODE = sysctl;
} else if (dev->flags & IFF_ALLMULTI || dev->mc_count > 16) {
/* accept all multicast */
sysctl = *pEMAC_OPMODE;
sysctl |= PAM;
*pEMAC_OPMODE = sysctl;
} else if (dev->mc_count) {
/* set multicast */
} else {
/* clear promisc or multicast mode */
sysctl = *pEMAC_OPMODE;
sysctl &= ~(RAF | PAM);
*pEMAC_OPMODE = sysctl;
}
}
/*
* this puts the device in an inactive state
*/
static void bf537mac_shutdown(struct net_device *dev)
{
/* Turn off the EMAC */
*pEMAC_OPMODE = 0x00000000;
/* Turn off the EMAC RX DMA */
*pDMA1_CONFIG = 0x0000;
*pDMA2_CONFIG = 0x0000;
}
/*
* Open and Initialize the interface
*
* Set up everything, reset the card, etc..
*/
static int bf537mac_open(struct net_device *dev)
{
//printk("%s: %s\n", dev->name, __FUNCTION__);
/*
* Check that the address is valid. If its not, refuse
* to bring the device up. The user must specify an
* address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
*/
if (!is_valid_ether_addr(dev->dev_addr)) {
printk((KERN_DEBUG "bf537mac_open: no valid ethernet hw addr\n"));
return -EINVAL;
}
/* initial rx and tx list */
desc_list_init();
bf537mac_setphy(dev);
SetupSystemRegs(dev);
bf537mac_reset();
bf537mac_enable(dev);
printk("bf537_mac: hardware init finished\n");
netif_start_queue(dev);
netif_carrier_on(dev);
return 0;
}
/*
*
* this makes the board clean up everything that it can
* and not talk to the outside world. Caused by
* an 'ifconfig ethX down'
*/
static int bf537mac_close(struct net_device *dev)
{
//printk("%s: %s\n", dev->name, __FUNCTION__);
netif_stop_queue(dev);
netif_carrier_off(dev);
/* clear everything */
bf537mac_shutdown(dev);
/* free the rx/tx buffers */
desc_list_free();
return 0;
}
static int __init bf537mac_probe(struct net_device *dev)
{
struct bf537mac_local *lp = netdev_priv(dev);
int retval;
/* Grab the MAC address in the MAC */
*(u32 *)(&(dev->dev_addr[0])) = *pEMAC_ADDRLO;
*(u16 *)(&(dev->dev_addr[4])) = (u16)*pEMAC_ADDRHI;
/* probe mac */
/*todo: how to proble? which is revision_register */
*pEMAC_ADDRLO = 0x12345678;
if (*pEMAC_ADDRLO != 0x12345678) {
//printk("bf537_mac: can't detect bf537 mac!\n");
retval = -ENODEV;
goto err_out;
}
/*Is it valid? (Did bootloader initialize it?)*/
if (!is_valid_ether_addr(dev->dev_addr)) {
/* Grab the MAC from the board somehow - this is done in the
arch/blackfin/boards/bf537/boardname.c */
get_bf537_ether_addr(dev->dev_addr);
}
/* If still not valid, get a random one */
if (!is_valid_ether_addr(dev->dev_addr)) {
random_ether_addr(dev->dev_addr);
}
SetupMacAddr(dev->dev_addr);
/* Fill in the fields of the device structure with ethernet values. */
ether_setup(dev);
dev->open = bf537mac_open;
dev->stop = bf537mac_close;
dev->hard_start_xmit = bf537mac_hard_start_xmit;
dev->tx_timeout = bf537mac_timeout;
dev->get_stats = bf537mac_query_statistics;
dev->set_multicast_list = bf537mac_set_multicast_list;
// dev->ethtool_ops = &bf537mac_ethtool_ops;
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = bf537mac_poll;
#endif
/* fill in some of the fields */
lp->version = 1;
lp->PhyAddr = 0x01;
lp->CLKIN = 25;
lp->FullDuplex = 0;
lp->Negotiate = 1;
lp->FlowControl = 0;
// set the GPIO pins to Ethernet mode
SetupPinMux();
/* now, enable interrupts */
/* register irq handler */
if (request_irq(IRQ_MAC_RX, bf537mac_interrupt, SA_INTERRUPT|SA_SHIRQ, "BFIN537_MAC_RX",dev)) {
printk("Unable to attach BlackFin MAC RX interrupt\n");
return -EBUSY;
}
retval = register_netdev(dev);
if (retval == 0) {
/* now, print out the card info, in a short format.. */
printk(KERN_INFO "Blackfin 537 mac net device registered.\n");
}
err_out:
return retval;
}
static int bf537mac_drv_probe(struct device *dev)
{
struct net_device *ndev;
int ret=0;
ndev = alloc_etherdev(sizeof(struct bf537mac_local));
if (!ndev) {
printk("%s: could not allocate device.\n", CARDNAME);
ret = -ENOMEM;
return ret;
}
ret = bf537mac_probe(ndev);
if (ret != 0) {
dev_set_drvdata(dev, NULL);
free_netdev(ndev);
printk("%s: not found (%d).\n", CARDNAME, ret);
}
SET_MODULE_OWNER(ndev);
SET_NETDEV_DEV(ndev, dev);
dev_set_drvdata(dev, ndev);
//printk("bf537_mac: probe finished\n");
return ret;
}
static int bf537mac_drv_remove(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
dev_set_drvdata(dev, NULL);
unregister_netdev(ndev);
free_irq(IRQ_MAC_RX, ndev);
free_netdev(ndev);
return 0;
}
static int bf537mac_drv_suspend(struct device *dev, u32 state, u32 level)
{
return 0;
}
static int bf537mac_drv_resume(struct device *dev, u32 level)
{
return 0;
}
static struct device_driver bf537mac_driver = {
.name = CARDNAME,
.bus = &platform_bus_type,
.probe = bf537mac_drv_probe,
.remove = bf537mac_drv_remove,
.suspend = bf537mac_drv_suspend,
.resume = bf537mac_drv_resume,
};
static int __init bf537mac_init(void)
{
return driver_register(&bf537mac_driver);
}
static void __exit bf537mac_cleanup(void)
{
driver_unregister(&bf537mac_driver);
}
module_init(bf537mac_init);
module_exit(bf537mac_cleanup);
[-- Attachment #3: bfin_mac.h --]
[-- Type: text/plain, Size: 3647 bytes --]
// -----------------------------------------------------------------------
// PHY REGISTER NAMES //
// -----------------------------------------------------------------------
#define PHYREG_MODECTL 0x0000
#define PHYREG_MODESTAT 0x0001
#define PHYREG_PHYID1 0x0002
#define PHYREG_PHYID2 0x0003
#define PHYREG_ANAR 0x0004
#define PHYREG_ANLPAR 0x0005
#define PHYREG_ANER 0x0006
#define PHYREG_NSR 0x0010
#define PHYREG_LBREMR 0x0011
#define PHYREG_REC 0x0012
#define PHYREG_10CFG 0x0013
#define PHYREG_PHY1_1 0x0014
#define PHYREG_PHY1_2 0x0015
#define PHYREG_PHY2 0x0016
#define PHYREG_TW_1 0x0017
#define PHYREG_TW_2 0x0018
#define PHYREG_TEST 0x0019
#define PHY_RESET 0x8000
#define PHY_ANEG_EN 0x1000
#define PHY_DUPLEX 0x0100
#define PHY_SPD_SET 0x2000
typedef struct _DMA_CONFIG
{
unsigned short b_DMA_EN:1; //Bit 0 : DMA Enable
unsigned short b_WNR:1; //Bit 1 : DMA Direction
unsigned short b_WDSIZE:2; //Bit 2 & 3 : DMA Tranfer Word size
unsigned short b_DMA2D:1; //Bit 4 : DMA Mode 2D or 1D
unsigned short b_RESTART:1; //Bit 5 : Retain the FIFO
unsigned short b_DI_SEL:1; //Bit 6 : Data Interrupt Timing Select
unsigned short b_DI_EN:1; //Bit 7 : Data Interrupt Enable
unsigned short b_NDSIZE:4; //Bit 8 to 11 : Flex descriptor Size
unsigned short b_FLOW:3; //Bit 12 to 14 : FLOW
} DMA_CONFIG_REG;
struct dma_descriptor {
struct dma_descriptor *next_dma_desc;
unsigned long start_addr;
DMA_CONFIG_REG config;
unsigned short x_count;
};
/*
struct status_area {
unsigned short ip_hdr_chksum; // the IP header checksum
unsigned short ip_payload_chksum; // the IP header and payload checksum
unsigned long status_word; // the frame status word
};
*/
struct status_area {
unsigned long status_word; // the frame status word
};
/* use two descriptors for a packet */
struct net_dma_desc {
struct net_dma_desc *next;
struct dma_descriptor desc_a;
struct dma_descriptor desc_b;
volatile unsigned char packet[1560];
volatile struct status_area status;
};
struct bf537mac_local {
/*
* these are things that the kernel wants me to keep, so users
* can find out semi-useless statistics of how well the card is
* performing
*/
struct net_device_stats stats;
int version;
int FlowEnabled; // record if data flow is active
int EtherIntIVG; // IVG for the ethernet interrupt
int RXIVG; // IVG for the RX completion
int TXIVG; // IVG for the TX completion
int PhyAddr; // PHY address
int OpMode; // set these bits n the OPMODE regs
int Port10; // set port speed to 10 Mbit/s
int GenChksums; // IP checksums to be calculated
int NoRcveLnth; // dont insert recv length at start of buffer
int StripPads; // remove trailing pad bytes
int FullDuplex; // set full duplex mode
int Negotiate; // enable auto negotiation
int Loopback; // loopback at the PHY
int Cache; // Buffers may be cached
int FlowControl; // flow control active
int CLKIN; // clock in value in MHZ
unsigned short IntMask; // interrupt mask
unsigned char Mac[6]; // MAC address of the board
};
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Netpoll checksum issue
2006-04-24 5:42 ` Aubrey
@ 2006-04-24 7:46 ` Aubrey
2006-04-24 8:04 ` Aubrey
2006-04-24 10:22 ` Herbert Xu
1 sibling, 1 reply; 11+ messages in thread
From: Aubrey @ 2006-04-24 7:46 UTC (permalink / raw)
To: Herbert Xu; +Cc: Stephen Hemminger, netdev
Hi Herbert,
Things seem to be more clear for me.
When not in netpoll mode, before an udp package passed to "udp_rcv",
the ip layer will call the routine "__skb_checksum_complete(skb);"
to do the checksum. After ip checksum, "skb->ip_summed" will be
assigned to CHECKSUM_UNNECESSARY.
============================================================
unsigned int __skb_checksum_complete(struct sk_buff *skb)
{
unsigned int sum;
sum = (u16)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum));
if (likely(!sum)) {
if (unlikely(skb->ip_summed == CHECKSUM_HW))
netdev_rx_csum_fault(skb->dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
return sum;
}
============================================================
So, in the routine "udp_rcv", actually, the csum is not checked.
Consequently, udp has no problem in the non-netpoll mode.
When in the netpoll mode, ip checksum is implemented by the routine
"ip_fast_csum", which does not assgin CHECKSUM_UNNECESSARY to
"skb->ip_summed", so udp has to check sum. Although checksum_udp in
the netpoll mode uses the same method as non-netpoll mode(see below),
it failed still.
============================
skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
return __skb_checksum_complete(skb);
============================
So, Is the udp checksum algorithm wrong? or should we add one line
------------------------------
skb->ip_summed = CHECKSUM_UNNECESSARY;
------------------------------
after/into the routine "ip_fast_csum"?
Regards,
-Aubrey
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Netpoll checksum issue
2006-04-24 7:46 ` Aubrey
@ 2006-04-24 8:04 ` Aubrey
0 siblings, 0 replies; 11+ messages in thread
From: Aubrey @ 2006-04-24 8:04 UTC (permalink / raw)
To: Herbert Xu; +Cc: Stephen Hemminger, netdev
Hi Herbert,
The following change works properly on my side, it just do the same
thing as the driver not in netpoll mode. Let me know it's acceptable,
please.
Thanks,
-Aubrey
======================================================
int __netpoll_rx(struct sk_buff *skb)
{
----snip----
if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)
goto out;
+++ skb->ip_summed = CHECKSUM_UNNECESSARY;
len = ntohs(iph->tot_len);
if (skb->len < len || len < iph->ihl*4)
goto out;
----snip----
}
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Netpoll checksum issue
2006-04-24 5:42 ` Aubrey
2006-04-24 7:46 ` Aubrey
@ 2006-04-24 10:22 ` Herbert Xu
2006-04-24 13:22 ` Aubrey
1 sibling, 1 reply; 11+ messages in thread
From: Herbert Xu @ 2006-04-24 10:22 UTC (permalink / raw)
To: Aubrey; +Cc: Stephen Hemminger, netdev
On Mon, Apr 24, 2006 at 01:42:12PM +0800, Aubrey wrote:
>
> dev->last_rx = jiffies;
> skb->dev = dev;
> skb->protocol = eth_type_trans(skb, dev);
> skb->ip_summed = CHECKSUM_UNNECESSARY;
> netif_rx(skb);
This doesn't make sense. First of all you're setting ip_summed to
CHECK_UNNECESSARY unconditionally which is most likely wrong.
What's more, if this was the driver that you were using, then
checksum_udp couldn't possibly fail since the first thing it does
is check ip_summed.
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Netpoll checksum issue
2006-04-24 10:22 ` Herbert Xu
@ 2006-04-24 13:22 ` Aubrey
2006-04-27 12:57 ` Aubrey
0 siblings, 1 reply; 11+ messages in thread
From: Aubrey @ 2006-04-24 13:22 UTC (permalink / raw)
To: Herbert Xu; +Cc: Stephen Hemminger, netdev
On 4/24/06, Herbert Xu <herbert@gondor.apana.org.au> wrote:
> On Mon, Apr 24, 2006 at 01:42:12PM +0800, Aubrey wrote:
> >
> > dev->last_rx = jiffies;
> > skb->dev = dev;
> > skb->protocol = eth_type_trans(skb, dev);
> > skb->ip_summed = CHECKSUM_UNNECESSARY;
> > netif_rx(skb);
>
> This doesn't make sense. First of all you're setting ip_summed to
> CHECK_UNNECESSARY unconditionally which is most likely wrong.
>
> What's more, if this was the driver that you were using, then
> checksum_udp couldn't possibly fail since the first thing it does
> is check ip_summed.
Hmm, when I change the code in __netpoll_rx, there is no the following
one line in the driver:
==============================================
skb->ip_summed = CHECKSUM_UNNECESSARY;
==============================================
The above one line code in the driver is just a workaround.
Regards,
-Aubrey
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Netpoll checksum issue
2006-04-24 13:22 ` Aubrey
@ 2006-04-27 12:57 ` Aubrey
2006-04-27 21:29 ` Herbert Xu
0 siblings, 1 reply; 11+ messages in thread
From: Aubrey @ 2006-04-27 12:57 UTC (permalink / raw)
To: Herbert Xu; +Cc: Stephen Hemminger, netdev
Hi Herbert,
Is there any update of this issue?
Regards,
- Aubrey
On 4/24/06, Aubrey <aubreylee@gmail.com> wrote:
> On 4/24/06, Herbert Xu <herbert@gondor.apana.org.au> wrote:
> > On Mon, Apr 24, 2006 at 01:42:12PM +0800, Aubrey wrote:
> > >
> > > dev->last_rx = jiffies;
> > > skb->dev = dev;
> > > skb->protocol = eth_type_trans(skb, dev);
> > > skb->ip_summed = CHECKSUM_UNNECESSARY;
> > > netif_rx(skb);
> >
> > This doesn't make sense. First of all you're setting ip_summed to
> > CHECK_UNNECESSARY unconditionally which is most likely wrong.
> >
> > What's more, if this was the driver that you were using, then
> > checksum_udp couldn't possibly fail since the first thing it does
> > is check ip_summed.
>
> Hmm, when I change the code in __netpoll_rx, there is no the following
> one line in the driver:
> ==============================================
> skb->ip_summed = CHECKSUM_UNNECESSARY;
> ==============================================
>
> The above one line code in the driver is just a workaround.
>
> Regards,
> -Aubrey
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Netpoll checksum issue
2006-04-27 12:57 ` Aubrey
@ 2006-04-27 21:29 ` Herbert Xu
0 siblings, 0 replies; 11+ messages in thread
From: Herbert Xu @ 2006-04-27 21:29 UTC (permalink / raw)
To: Aubrey; +Cc: Stephen Hemminger, netdev
On Thu, Apr 27, 2006 at 08:57:33PM +0800, Aubrey wrote:
>
> Is there any update of this issue?
Assuming that the CHECKSUM_UNNECESSARY line wasn't there, then the
problem is simply that your packet has the wrong UDP checksum.
So I suggest that you print the packet out and compare it with
the original to see where the corruption is.
I don't see how the generic networking stack or netpoll could be
causing your problem.
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2006-04-27 21:29 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <6d6a94c50604190922m189b9d99gdd428a870e12c2c3@mail.gmail.com>
2006-04-19 17:31 ` Netpoll checksum issue Stephen Hemminger
2006-04-19 21:33 ` Herbert Xu
2006-04-20 1:54 ` Aubrey
2006-04-23 11:34 ` Herbert Xu
2006-04-24 5:42 ` Aubrey
2006-04-24 7:46 ` Aubrey
2006-04-24 8:04 ` Aubrey
2006-04-24 10:22 ` Herbert Xu
2006-04-24 13:22 ` Aubrey
2006-04-27 12:57 ` Aubrey
2006-04-27 21:29 ` Herbert Xu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).