Most recent kernel where this bug did not occur: 2.4.30? Hardware Environment: Motherboard PMB-532LF Software Environment: a private driver depending on syncppp Problem Description: tcpdump on a interface registered by a driver derived from syncppp panics kernel.
Created attachment 13017 [details] image from panicked screen
Reply-To: akpm@linux-foundation.org On Tue, 2 Oct 2007 08:00:47 -0700 (PDT) bugme-daemon@bugzilla.kernel.org wrote: > http://bugzilla.kernel.org/show_bug.cgi?id=9112 > > Summary: tcpdump on a syncppp interface panics kernel > Product: Networking > Version: 2.5 > KernelVersion: 2.6.22.9 > Platform: All > OS/Version: Linux > Tree: Mainline > Status: NEW > Severity: normal > Priority: P1 > Component: Other > AssignedTo: acme@ghostprotocols.net > ReportedBy: mattilinnanvuori@yahoo.com > > > Most recent kernel where this bug did not occur: 2.4.30? > Hardware Environment: Motherboard PMB-532LF > Software Environment: a private driver depending on syncppp > Problem Description: tcpdump on a interface registered by a driver derived > from > syncppp panics kernel. > Well that's cute. Who maintains syncppp?
skb_push in driver.
The panic appears when a peer pings the interface and tcpdump -i <interface> is running. int etp_netdev_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct etp_channel_private *cp = ((struct etp_netdev_priv *)(netdev_priv(dev)))->cp; unsigned last_transmitted; unsigned int tx_length; unsigned char *data; if (cp->hdlc_mode >= HDLC_MODE_RETINA_OVER_G703) { bool under; // make room for CALP header tx_length = skb->len + 2; under = tx_length < ETH_ZLEN; if (unlikely(under)) tx_length = ETH_ZLEN; data = kmalloc(tx_length, GFP_ATOMIC); if (unlikely(!data)) return -ENOMEM; *data = 0x0; //the CALP header data[1] = 0x40; //the CALP header memcpy(data + 2, skb->data, skb->len); if (unlikely(under)) memset(data + 2 + skb->len, 0, ETH_ZLEN - skb->len); //add CALP header length (+2), minus CRC (-4) //check } else if (unlikely(skb->len < ETH_ZLEN)) { data = kmalloc(tx_length = ETH_ZLEN, GFP_ATOMIC); if (unlikely(!data)) return -ENOMEM; memcpy(data, skb->data, skb->len); memset(data + skb->len, 0, ETH_ZLEN - skb->len); } else { tx_length = skb->len; data = skb->data; } { dma_addr_t bus_address = pci_map_single(cp->this_if_priv->this_dev_priv->pci_dev, data, tx_length, PCI_DMA_TODEVICE); if (likely(!pci_dma_mapping_error(bus_address))) { struct txdesc *txdesc; last_transmitted = cp->last_tx_desc_transmitted; if (data != skb->data) { dev_kfree_skb(skb); cp->tx_buffer[last_transmitted] = data; } txdesc = cp->txdesc_p[last_transmitted]; outl(bus_address, &txdesc->desc_a); outl((tx_length & TX_DESCB_LENGT_MASK) | TX_DESCB_TRANSFER, &txdesc->desc_b); } else { if (data != skb->data) kfree(data); return -ENOMEM; } } cp->tx_skb[last_transmitted] = skb; { // Calculate the next transmission descriptor entry. unsigned next = (last_transmitted + 1) & (DESCRIPTORS_PER_CHANNEL - 1); cp->last_tx_desc_transmitted = next; // If the next descriptor is busy, discontinue taking new ones. if (cp->tx_skb[next] != NULL) netif_stop_queue(dev); } dev->trans_start = jiffies; return 0; } static void tx_task_hdlc(struct etp_channel_private *cp) { struct net_device *netdev = cp->this_netdev; unsigned d; unsigned int desc_b; struct sk_buff *skb; struct txdesc *txdesc; d = cp->last_tx_desc_released + 1; d &= (DESCRIPTORS_PER_CHANNEL - 1); while (((skb = cp->tx_skb[d]) != NULL) && // skbuffer exists (((desc_b = inl(&(txdesc = cp->txdesc_p[d])-> desc_b)) & TX_DESCB_TRANSFER) == 0)) { // has been sent unsigned char *buffer; pci_unmap_single(cp->this_if_priv->this_dev_priv->pci_dev, inl(&txdesc->desc_a), desc_b & TX_DESCB_LENGT_MASK, PCI_DMA_TODEVICE); // update statistics (for proc interface): etp_update_tx_descriptor_statistics(&cp->stats, desc_b); etp_update_tx_descriptor_statistics_netdev(&cp-> netdev_stats, desc_b); buffer = cp->tx_buffer[d]; if (buffer) { kfree(buffer); cp->tx_buffer[d] = NULL; } else dev_kfree_skb(skb); cp->tx_skb[d] = NULL; cp->last_tx_desc_released = d; d++; d &= (DESCRIPTORS_PER_CHANNEL - 1); } if (netif_queue_stopped(netdev) && netif_tx_trylock(netdev)) { // if the next tx descriptor is free, continue taking new ones if (cp->tx_skb[cp->last_tx_desc_transmitted] == NULL) netif_wake_queue(netdev); netif_tx_unlock(netdev); } } static void rx_task_hdlc(struct etp_channel_private *cp) { unsigned d = cp->last_rx_desc_received; struct net_device *netdev = cp->this_netdev; struct rxdesc *rxdesc; struct sk_buff *skb; unsigned int descb; while (((descb = inl(&(rxdesc = cp->rxdesc_p[d])-> desc_b)) & RX_DESCB_TRANSFER) == 0) { //transfer done if (likely((skb = cp->rx_skb[d]) != NULL)) { // skb exists //update statistics (for proc interface): etp_update_rx_descriptor_statistics(&cp->stats, descb); etp_update_rx_descriptor_statistics_netdev(&cp-> netdev_stats, descb); if (unlikely(descb & (RX_DESCB_FIFO_ERR | RX_DESCB_SIZE_ERR | RX_DESCB_CRC_ERR | RX_DESCB_OCTET_ERR))) { //if error (do not care about CRC error) //reuse old skbuff outl(RX_DESCB_TRANSFER, &rxdesc->desc_b); } else { //if no error pci_unmap_single(cp->this_if_priv-> this_dev_priv->pci_dev, inl(&rxdesc->desc_a), TX_DESCB_LENGT_MASK, PCI_DMA_FROMDEVICE); if (cp->hdlc_mode < HDLC_MODE_RETINA_OVER_G703) { skb_put(skb, (descb & RX_DESCB_LENGT_MASK) - 2); //"-2" is the CRC // Select correct protocolstack: skb->protocol = __constant_htons(ETH_P_WAN_PPP); } else { //Retina ethernet mode skb_put(skb, descb & RX_DESCB_LENGT_MASK); skb_pull(skb, 2); //remove CALP header skb->protocol = eth_type_trans(skb, netdev); if (likely(netdev-> flags & IFF_POINTOPOINT)) { // everything received is for us. if (unlikely(netdev-> flags & IFF_NOARP)) { // NOARP applied -> // destination MAC addresses are bogus if (skb-> pkt_type == PACKET_OTHERHOST) { skb-> pkt_type = PACKET_HOST; } } else { // NOARP not applied -> // destination MAC addresses are broadcast if (skb-> pkt_type == PACKET_BROADCAST) { skb-> pkt_type = PACKET_HOST; } } // IFF_NOARP } // IFF_POINTOPOINT } netif_rx(skb); netdev->last_rx = jiffies; } } // reserve new skb: skb = dev_alloc_skb(TX_DESCB_LENGT_MASK); if (likely(skb)) { dma_addr_t bus_address = pci_map_single(cp->this_if_priv-> this_dev_priv->pci_dev, skb->data, TX_DESCB_LENGT_MASK, PCI_DMA_FROMDEVICE); if (likely(!pci_dma_mapping_error(bus_address))) { skb->dev = netdev; // Mark as being used by this device. skb->ip_summed = CHECKSUM_NONE; cp->rx_skb[d] = skb; outl(bus_address, &rxdesc->desc_a); outl(RX_DESCB_TRANSFER, &rxdesc->desc_b); } else { dev_kfree_skb(skb); if (printk_ratelimit()) printk(KERN_NOTICE "%s: failed to map DMA buffer\n", netdev->name); } d++; d &= DESCRIPTORS_PER_CHANNEL - 1; } else { if (printk_ratelimit()) printk(KERN_NOTICE "%s: low on memory\n", netdev->name); d++; d &= DESCRIPTORS_PER_CHANNEL - 1; if (unlikely(d == cp->last_rx_desc_received)) break; } } cp->last_rx_desc_received = d; } Copyright (C) 2006 Jouni Kujala, Flexibilis Oy.
skb_reset_mac_header(skb) was missing.