Bug 9112 - tcpdump on a syncppp interface panics kernel
Summary: tcpdump on a syncppp interface panics kernel
Status: REJECTED INVALID
Alias: None
Product: Networking
Classification: Unclassified
Component: Other (show other bugs)
Hardware: All Linux
: P1 normal
Assignee: Arnaldo Carvalho de Melo
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-10-02 08:00 UTC by Matti Linnanvuori
Modified: 2007-10-03 23:51 UTC (History)
0 users

See Also:
Kernel Version: 2.6.22.9
Subsystem:
Regression: ---
Bisected commit-id:


Attachments
image from panicked screen (37.36 KB, image/jpeg)
2007-10-02 08:01 UTC, Matti Linnanvuori
Details

Description Matti Linnanvuori 2007-10-02 08:00:46 UTC
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.
Comment 1 Matti Linnanvuori 2007-10-02 08:01:32 UTC
Created attachment 13017 [details]
image from panicked screen
Comment 2 Anonymous Emailer 2007-10-02 11:04:55 UTC
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?
Comment 3 Matti Linnanvuori 2007-10-02 11:07:58 UTC
skb_push in driver.
Comment 4 Matti Linnanvuori 2007-10-03 04:49:32 UTC
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.
Comment 5 Matti Linnanvuori 2007-10-03 23:51:06 UTC
skb_reset_mac_header(skb) was missing.

Note You need to log in before you can comment on or make changes to this bug.