[PATCH 2/3] NetXen: 64-bit memory fixes

Subsystems: networking drivers, the rest

STALE7179d

19 messages, 7 authors, 2006-12-07 · open the first message on its own page

[PATCH 2/3] NetXen: 64-bit memory fixes

From: Amit S. Kale <hidden>
Date: 2006-12-01 13:40:14

NetXen: 1G/10G Ethernet driver updates
	- These fixes take care of driver on machines with >4G memory
	- Driver cleanup

Signed-off-by: Amit S. Kale <redacted>

 netxen_nic.h          |   29 +++++--
 netxen_nic_ethtool.c  |   19 ++--
 netxen_nic_hw.c       |    4 
 netxen_nic_hw.h       |    4 
 netxen_nic_init.c     |   51 +++++++++++-
 netxen_nic_isr.c      |    3 
 netxen_nic_main.c     |  204 +++++++++++++++++++++++++++++++++++++++++++++++---
 netxen_nic_phan_reg.h |   10 +-
 8 files changed, 286 insertions(+), 38 deletions(-)

diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h
index d925053..029e6c7 100644
--- a/drivers/net/netxen/netxen_nic.h
+++ b/drivers/net/netxen/netxen_nic.h
@@ -6,12 +6,12 @@
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
- *                            
+ *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *                                   
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
@@ -89,8 +89,8 @@
  * normalize a 64MB crb address to 32MB PCI window 
  * To use NETXEN_CRB_NORMALIZE, window _must_ be set to 1
  */
-#define NETXEN_CRB_NORMAL(reg)        \
-	(reg) - NETXEN_CRB_PCIX_HOST2 + NETXEN_CRB_PCIX_HOST
+#define NETXEN_CRB_NORMAL(reg)	\
+	((reg) - NETXEN_CRB_PCIX_HOST2 + NETXEN_CRB_PCIX_HOST)
 
 #define NETXEN_CRB_NORMALIZE(adapter, reg) \
 	pci_base_offset(adapter, NETXEN_CRB_NORMAL(reg))
@@ -164,7 +164,7 @@ enum {
 
 #define MAX_CMD_DESCRIPTORS		1024
 #define MAX_RCV_DESCRIPTORS		32768
-#define MAX_JUMBO_RCV_DESCRIPTORS	1024
+#define MAX_JUMBO_RCV_DESCRIPTORS	4096
 #define MAX_RCVSTATUS_DESCRIPTORS	MAX_RCV_DESCRIPTORS
 #define MAX_JUMBO_RCV_DESC	MAX_JUMBO_RCV_DESCRIPTORS
 #define MAX_RCV_DESC		MAX_RCV_DESCRIPTORS
@@ -591,6 +591,16 @@ struct netxen_skb_frag {
 	u32 length;
 };
 
+/* Bounce buffer index */
+struct bounce_index {
+	/* Index of a buffer */
+	unsigned buffer_index;
+	/* Offset inside the buffer */
+	unsigned buffer_offset;
+};
+
+#define IS_BOUNCE 0xcafebb
+
 /*    Following defines are for the state of the buffers    */
 #define	NETXEN_BUFFER_FREE	0
 #define	NETXEN_BUFFER_BUSY	1
@@ -610,6 +620,8 @@ struct netxen_cmd_buffer {
 	unsigned long time_stamp;
 	u32 state;
 	u32 no_of_descriptors;
+	u32 tx_bounce_buff;
+	struct bounce_index bnext;
 };
 
 /* In rx_buffer, we do not need multiple fragments as is a single buffer */
@@ -618,6 +630,9 @@ struct netxen_rx_buffer {
 	u64 dma;
 	u16 ref_handle;
 	u16 state;
+	u32 rx_bounce_buff;
+	struct bounce_index bnext;
+	char *bounce_ptr;
 };
 
 /* Board types */
@@ -702,6 +717,7 @@ struct netxen_recv_context {
 };
 
 #define NETXEN_NIC_MSI_ENABLED 0x02
+#define NETXEN_DMA_MASK	0xfffffffe
 
 struct netxen_drvops;
 
@@ -1018,6 +1034,9 @@ static inline void get_brd_name_by_type(
 
 int netxen_is_flash_supported(struct netxen_adapter *adapter);
 int netxen_get_flash_mac_addr(struct netxen_adapter *adapter, u64 mac[]);
+int netxen_get_next_bounce_buffer(struct bounce_index *head,
+				  struct bounce_index *tail,
+				  struct bounce_index *biret, unsigned len);
 
 extern void netxen_change_ringparam(struct netxen_adapter *adapter);
 extern int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr,
diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c
index 9a914ae..8d8e5e1 100644
--- a/drivers/net/netxen/netxen_nic_ethtool.c
+++ b/drivers/net/netxen/netxen_nic_ethtool.c
@@ -6,12 +6,12 @@
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
- *                            
+ *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *                                   
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
@@ -118,7 +118,7 @@ netxen_nic_get_drvinfo(struct net_device
 	u32 fw_minor = 0;
 	u32 fw_build = 0;
 
-	strncpy(drvinfo->driver, "netxen_nic", 32);
+	strncpy(drvinfo->driver, netxen_nic_driver_name, 32);
 	strncpy(drvinfo->version, NETXEN_NIC_LINUX_VERSIONID, 32);
 	fw_major = readl(NETXEN_CRB_NORMALIZE(adapter,
 					      NETXEN_FW_VERSION_MAJOR));
@@ -210,7 +210,6 @@ netxen_nic_get_settings(struct net_devic
 		printk(KERN_ERR "netxen-nic: Unsupported board model %d\n",
 		       (netxen_brdtype_t) boardinfo->board_type);
 		return -EIO;
-
 	}
 
 	return 0;
@@ -460,20 +459,22 @@ netxen_nic_get_ringparam(struct net_devi
 {
 	struct netxen_port *port = netdev_priv(dev);
 	struct netxen_adapter *adapter = port->adapter;
-	int i, j;
+	int i;
 
 	ring->rx_pending = 0;
+	ring->rx_jumbo_pending = 0;
 	for (i = 0; i < MAX_RCV_CTX; ++i) {
-		for (j = 0; j < NUM_RCV_DESC_RINGS; j++)
-			ring->rx_pending +=
-			    adapter->recv_ctx[i].rcv_desc[j].rcv_pending;
+		ring->rx_pending += adapter->recv_ctx[i].
+		    rcv_desc[RCV_DESC_NORMAL_CTXID].rcv_pending;
+		ring->rx_jumbo_pending += adapter->recv_ctx[i].
+		    rcv_desc[RCV_DESC_JUMBO_CTXID].rcv_pending;
 	}
 
 	ring->rx_max_pending = adapter->max_rx_desc_count;
 	ring->tx_max_pending = adapter->max_tx_desc_count;
+	ring->rx_jumbo_max_pending = adapter->max_jumbo_rx_desc_count;
 	ring->rx_mini_max_pending = 0;
 	ring->rx_mini_pending = 0;
-	ring->rx_jumbo_max_pending = 0;
 	ring->rx_jumbo_pending = 0;
 }
 
diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c
index 105c24f..84c69a4 100644
--- a/drivers/net/netxen/netxen_nic_hw.c
+++ b/drivers/net/netxen/netxen_nic_hw.c
@@ -648,7 +648,7 @@ void netxen_nic_reg_write(struct netxen_
 
 	addr = NETXEN_CRB_NORMALIZE(adapter, off);
 	DPRINTK(INFO, "writing to base %lx offset %llx addr %p data %x\n",
-		pci_base(adapter, off), off, addr);
+		pci_base(adapter, off), off, addr, val);
 	writel(val, addr);
 
 }
@@ -660,7 +660,7 @@ int netxen_nic_reg_read(struct netxen_ad
 
 	addr = NETXEN_CRB_NORMALIZE(adapter, off);
 	DPRINTK(INFO, "reading from base %lx offset %llx addr %p\n",
-		adapter->ahw.pci_base, off, addr);
+		pci_base(adapter, off), off, addr);
 	val = readl(addr);
 	writel(val, addr);
 
diff --git a/drivers/net/netxen/netxen_nic_hw.h b/drivers/net/netxen/netxen_nic_hw.h
index 201a636..e5620a6 100644
--- a/drivers/net/netxen/netxen_nic_hw.h
+++ b/drivers/net/netxen/netxen_nic_hw.h
@@ -83,8 +83,8 @@ struct netxen_adapter;
 #define NETXEN_PCI_MAPSIZE_BYTES  (NETXEN_PCI_MAPSIZE << 20)
 
 #define NETXEN_NIC_LOCKED_READ_REG(X, Y)	\
-	addr = pci_base_offset(adapter, (X));	\
-	*(u32 *)Y = readl(addr);
+	addr = pci_base_offset(adapter, X);	\
+	*(u32 *)Y = readl((void __iomem*) addr);
 
 struct netxen_port;
 void netxen_nic_set_link_parameters(struct netxen_port *port);
diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c
index 0dca029..b7e83a9 100644
--- a/drivers/net/netxen/netxen_nic_init.c
+++ b/drivers/net/netxen/netxen_nic_init.c
@@ -53,6 +53,11 @@ static unsigned int crb_addr_xform[NETXE
 
 #define NETXEN_NIC_XDMA_RESET 0x8000ff
 
+extern char *rx_bounce_ptr;
+extern struct bounce_index tx_bounce_head, tx_bounce_tail,
+    rx_bounce_head, rx_bounce_tail;
+extern spinlock_t rx_bounce_lock, tx_bounce_lock;
+
 static inline void
 netxen_nic_locked_write_reg(struct netxen_adapter *adapter,
 			    unsigned long off, int *data)
@@ -191,8 +196,6 @@ void netxen_initialize_adapter_sw(struct
 			}
 		}
 	}
-	DPRINTK(INFO, "initialized buffers for %s and %s\n",
-		"adapter->free_cmd_buf_list", "adapter->free_rxbuf");
 }
 
 void netxen_initialize_adapter_hw(struct netxen_adapter *adapter)
@@ -383,8 +386,8 @@ int netxen_rom_wip_poll(struct netxen_ad
 	return 0;
 }
 
-static inline int do_rom_fast_write(struct netxen_adapter *adapter,
-				    int addr, int data)
+static inline int do_rom_fast_write(struct netxen_adapter *adapter, int addr,
+				    int data)
 {
 	if (netxen_rom_wren(adapter)) {
 		return -1;
@@ -774,6 +777,11 @@ netxen_process_rcv(struct netxen_adapter
 			 PCI_DMA_FROMDEVICE);
 
 	skb = (struct sk_buff *)buffer->skb;
+	if (buffer->rx_bounce_buff == IS_BOUNCE) {
+		buffer->rx_bounce_buff = 0;
+		memcpy(skb->data, buffer->bounce_ptr, rcv_desc->dma_size);
+		rx_bounce_tail = buffer->bnext;
+	}
 
 	if (likely(STATUS_DESC_STATUS(desc) == STATUS_CKSUM_OK)) {
 		port->stats.csummed++;
@@ -938,6 +946,10 @@ void netxen_process_cmd_ring(unsigned lo
 					       PCI_DMA_TODEVICE);
 			}
 
+			if (buffer->tx_bounce_buff == IS_BOUNCE) {
+				buffer->tx_bounce_buff = 0;
+				tx_bounce_tail = buffer->bnext;
+			}
 			port->stats.skbfreed++;
 			dev_kfree_skb_any(skb);
 			skb = NULL;
@@ -1006,6 +1018,8 @@ void netxen_post_rx_buffers(struct netxe
 	struct netxen_rx_buffer *buffer;
 	int count = 0;
 	int index = 0;
+	unsigned long bounce_flags;
+	struct bounce_index tmpbi;
 
 	adapter->stats.post_called++;
 	rcv_desc = &recv_ctx->rcv_desc[ringid];
@@ -1029,6 +1043,7 @@ void netxen_post_rx_buffers(struct netxe
 		count++;	/* now there should be no failure */
 		pdesc = &rcv_desc->desc_head[producer];
 		skb_reserve(skb, NET_IP_ALIGN);
+		buffer->rx_bounce_buff = 0;
 		/* 
 		 * This will be setup when we receive the
 		 * buffer after it has been filled
@@ -1039,6 +1054,34 @@ void netxen_post_rx_buffers(struct netxe
 		buffer->dma = pci_map_single(pdev, skb->data,
 					     rcv_desc->dma_size,
 					     PCI_DMA_FROMDEVICE);
+		if (buffer->dma > NETXEN_DMA_MASK) {
+			pci_unmap_single(pdev, buffer->dma, rcv_desc->dma_size,
+					 PCI_DMA_FROMDEVICE);
+			spin_lock_irqsave(&rx_bounce_lock, bounce_flags);
+			if (netxen_get_next_bounce_buffer(&rx_bounce_head,
+							  &rx_bounce_tail,
+							  &tmpbi,
+							  rcv_desc->dma_size)) {
+				spin_unlock_irqrestore(&rx_bounce_lock,
+						       bounce_flags);
+				dev_kfree_skb_any(skb);
+				skb = NULL;
+				buffer->skb = NULL;
+				buffer->state = NETXEN_BUFFER_FREE;
+				count--;
+				break;
+			}
+			spin_unlock_irqrestore(&rx_bounce_lock, bounce_flags);
+			buffer->rx_bounce_buff = IS_BOUNCE;
+			buffer->bnext = rx_bounce_head;
+			buffer->bounce_ptr = (void *)(ptrdiff_t)
+			    (rx_bounce_ptr[tmpbi.buffer_index]
+			     + tmpbi.buffer_offset);
+			buffer->dma = pci_map_single(pdev, buffer->bounce_ptr,
+						     rcv_desc->dma_size,
+						     PCI_DMA_FROMDEVICE);
+		}
+
 		/* make a rcv descriptor  */
 		pdesc->reference_handle = le16_to_cpu(buffer->ref_handle);
 		pdesc->buffer_length = le16_to_cpu(rcv_desc->dma_size);
diff --git a/drivers/net/netxen/netxen_nic_isr.c b/drivers/net/netxen/netxen_nic_isr.c
index ae180fe..f6ae9fd 100644
--- a/drivers/net/netxen/netxen_nic_isr.c
+++ b/drivers/net/netxen/netxen_nic_isr.c
@@ -68,8 +68,7 @@ struct net_device_stats *netxen_nic_get_
 void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 portno,
 				 u32 link)
 {
-	struct netxen_port *pport = adapter->port[portno];
-	struct net_device *netdev = pport->netdev;
+	struct net_device *netdev = (adapter->port[portno])->netdev;
 
 	if (link)
 		netif_carrier_on(netdev);
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index 1cb662d..1f9abc6 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -48,7 +48,7 @@ MODULE_DESCRIPTION("NetXen Multi port (1
 MODULE_LICENSE("GPL");
 MODULE_VERSION(NETXEN_NIC_LINUX_VERSIONID);
 
-char netxen_nic_driver_name[] = "netxen";
+char netxen_nic_driver_name[] = "netxen-nic";
 static char netxen_nic_driver_string[] = "NetXen Network Driver version "
     NETXEN_NIC_LINUX_VERSIONID;
 
@@ -56,6 +56,19 @@ static char netxen_nic_driver_string[] =
 #define NETXEN_ADAPTER_UP_MAGIC 777
 #define NETXEN_NIC_PEG_TUNE 0
 
+/* Number of bounce buffers. Has to be a power of two */
+#define NUM_BOUNCE 256
+char *tx_bounce_ptr[NUM_BOUNCE];
+char *rx_bounce_ptr[NUM_BOUNCE];
+
+struct bounce_index tx_bounce_head, tx_bounce_tail,
+    rx_bounce_head, rx_bounce_tail;
+
+spinlock_t rx_bounce_lock, tx_bounce_lock;
+
+#define BOUNCE_BUFFER_ORDER 2
+#define BOUNCE_BUFFER_SIZE (PAGE_SIZE << BOUNCE_BUFFER_ORDER)
+
 /* Local functions to NetXen NIC driver */
 static int __devinit netxen_nic_probe(struct pci_dev *pdev,
 				      const struct pci_device_id *ent);
@@ -88,6 +101,114 @@ static struct pci_device_id netxen_pci_t
 MODULE_DEVICE_TABLE(pci, netxen_pci_tbl);
 
 /*
+ * Whenever we cross the 16K boundary of bounce buffer, we use the next
+ * 16K buffer and wrap up if its the last buffer.
+ */
+int netxen_get_next_bounce_buffer(struct bounce_index *head,
+				  struct bounce_index *tail,
+				  struct bounce_index *biret, unsigned len)
+{
+	struct bounce_index tmpbi;
+
+	tmpbi.buffer_index = head->buffer_index;
+	tmpbi.buffer_offset = head->buffer_offset;
+
+	if ((tmpbi.buffer_offset + len) > BOUNCE_BUFFER_SIZE) {
+		if ((tmpbi.buffer_index == tail->buffer_index) &&
+		    (tmpbi.buffer_offset < tail->buffer_offset)) {
+			return -1;
+		}
+		tmpbi.buffer_index =
+		    (tmpbi.buffer_index + 1) & (NUM_BOUNCE - 1);
+		tmpbi.buffer_offset = 0;
+	}
+
+	if (tmpbi.buffer_index == tail->buffer_index &&
+	    tmpbi.buffer_offset < tail->buffer_offset &&
+	    (tmpbi.buffer_offset + len) >= tail->buffer_offset) {
+		return -1;
+	}
+	head->buffer_index = tmpbi.buffer_index;
+	head->buffer_offset = tmpbi.buffer_offset + len;
+	*biret = tmpbi;
+	return 0;
+}
+
+static void netxen_free_bounce_buffers(void)
+{
+	int i;
+
+	for (i = 0; i < NUM_BOUNCE && tx_bounce_ptr[i]; i++) {
+		free_pages((unsigned long)tx_bounce_ptr[i],
+			   BOUNCE_BUFFER_ORDER);
+		tx_bounce_ptr[i] = NULL;
+	}
+
+	for (i = 0; i < NUM_BOUNCE && rx_bounce_ptr[i]; i++) {
+		free_pages((unsigned long)rx_bounce_ptr[i],
+			   BOUNCE_BUFFER_ORDER);
+		rx_bounce_ptr[i] = NULL;
+	}
+}
+
+/*
+ * We have 4MB space reserved for bounce buffers.
+ * The 4MB space is divided in 256 chunks of 16K buffers.
+ */
+static int netxen_alloc_bounce_buffers(void)
+{
+	int i;
+
+	memset(tx_bounce_ptr, 0, sizeof(tx_bounce_ptr));
+	memset(rx_bounce_ptr, 0, sizeof(rx_bounce_ptr));
+
+	for (i = 0; i < NUM_BOUNCE; i++) {
+		tx_bounce_ptr[i] = (char *)__get_free_pages(GFP_KERNEL,
+							    BOUNCE_BUFFER_ORDER);
+		if (!tx_bounce_ptr[i])
+			goto err_out;
+		if (virt_to_phys(tx_bounce_ptr[i])
+		    + BOUNCE_BUFFER_SIZE > NETXEN_DMA_MASK) {
+
+			free_pages((unsigned long)tx_bounce_ptr[i],
+				   BOUNCE_BUFFER_ORDER);
+			tx_bounce_ptr[i] = (char *)__get_free_pages(GFP_DMA,
+								    BOUNCE_BUFFER_ORDER);
+		}
+		if (!tx_bounce_ptr[i])
+			goto err_out;
+
+	}
+	tx_bounce_head.buffer_index = tx_bounce_tail.buffer_index = 0;
+	tx_bounce_head.buffer_offset = tx_bounce_tail.buffer_offset = 0;
+
+	for (i = 0; i < NUM_BOUNCE; i++) {
+		rx_bounce_ptr[i] = (char *)
+		    __get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
+		if (!rx_bounce_ptr[i])
+			goto err_out;
+		if (virt_to_phys(rx_bounce_ptr[i])
+		    + BOUNCE_BUFFER_SIZE > NETXEN_DMA_MASK) {
+			free_pages((unsigned long)rx_bounce_ptr[i],
+				   BOUNCE_BUFFER_ORDER);
+			rx_bounce_ptr[i] = (char *)
+			    __get_free_pages(GFP_DMA, BOUNCE_BUFFER_ORDER);
+		}
+		if (!rx_bounce_ptr[i])
+			goto err_out;
+
+	}
+	rx_bounce_head.buffer_index = rx_bounce_tail.buffer_index = 0;
+	rx_bounce_head.buffer_offset = rx_bounce_tail.buffer_offset = 0;
+	return 0;
+
+      err_out:
+	netxen_free_bounce_buffers();
+	return -ENOMEM;
+
+}
+
+/*
  * netxen_nic_probe()
  *
  * The Linux system will invoke this after identifying the vendor ID and
@@ -105,9 +226,9 @@ netxen_nic_probe(struct pci_dev *pdev, c
 	struct net_device *netdev = NULL;
 	struct netxen_adapter *adapter = NULL;
 	struct netxen_port *port = NULL;
-	u8 *mem_ptr0 = NULL;
-	u8 *mem_ptr1 = NULL;
-	u8 *mem_ptr2 = NULL;
+	void __iomem *mem_ptr0 = NULL;
+	void __iomem *mem_ptr1 = NULL;
+	void __iomem *mem_ptr2 = NULL;
 
 	unsigned long mem_base, mem_len;
 	int pci_using_dac, i, err;
@@ -198,6 +319,13 @@ netxen_nic_probe(struct pci_dev *pdev, c
 		goto err_out_free_adapter;
 	}
 	memset(cmd_buf_arr, 0, TX_RINGSIZE);
+	spin_lock_init(&tx_bounce_lock);
+	spin_lock_init(&rx_bounce_lock);
+
+	/*Only one set of bounce buffers for all adapters */
+	err = netxen_alloc_bounce_buffers();
+	if (err)
+		goto err_out_fcba;
 
 	for (i = 0; i < MAX_RCV_CTX; ++i) {
 		recv_ctx = &adapter->recv_ctx[i];
@@ -308,6 +436,7 @@ netxen_nic_probe(struct pci_dev *pdev, c
 	netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE);
 
 	/* initialize the all the ports */
+	adapter->active_ports = 0;
 
 	for (i = 0; i < adapter->ahw.max_ports; i++) {
 		netdev = alloc_etherdev(sizeof(struct netxen_port));
@@ -392,7 +521,6 @@ netxen_nic_probe(struct pci_dev *pdev, c
 			goto err_out_free_dev;
 		}
 		adapter->port_count++;
-		adapter->active_ports = 0;
 		adapter->port[i] = port;
 	}
 
@@ -441,10 +569,9 @@ netxen_nic_probe(struct pci_dev *pdev, c
 		}
 	}
 
+      err_out_fcba:
 	vfree(cmd_buf_arr);
 
-	kfree(adapter->port);
-
       err_out_free_adapter:
 	pci_set_drvdata(pdev, NULL);
 	kfree(adapter);
@@ -471,6 +598,7 @@ static void __devexit netxen_nic_remove(
 	int i;
 	int ctxid, ring;
 
+	netxen_free_bounce_buffers();
 	adapter = pci_get_drvdata(pdev);
 	if (adapter == NULL)
 		return;
@@ -596,6 +724,9 @@ static int netxen_nic_open(struct net_de
 	netxen_nic_set_link_parameters(port);
 
 	netxen_nic_set_multi(netdev);
+	if (adapter->ops->set_mtu)
+		adapter->ops->set_mtu(port, netdev->mtu);
+
 	if (!adapter->driver_mismatch)
 		netif_start_queue(netdev);
 
@@ -675,6 +806,9 @@ static int netxen_nic_xmit_frame(struct
 	u32 max_tx_desc_count = 0;
 	u32 last_cmd_consumer = 0;
 	int no_of_desc;
+	struct bounce_index tmpbi;
+	char *bounce_data;
+	unsigned long bounce_flags;
 
 	port->stats.xmitcalled++;
 	frag_count = skb_shinfo(skb)->nr_frags + 1;
@@ -792,6 +926,7 @@ static int netxen_nic_xmit_frame(struct
 	buffrag = &pbuf->frag_array[0];
 	buffrag->dma = pci_map_single(port->pdev, skb->data, first_seg_len,
 				      PCI_DMA_TODEVICE);
+	pbuf->tx_bounce_buff = 0;
 	buffrag->length = first_seg_len;
 	CMD_DESC_TOTAL_LENGTH_WRT(hwdesc, skb->len);
 	hwdesc->num_of_buffers = frag_count;
@@ -801,11 +936,33 @@ static int netxen_nic_xmit_frame(struct
 	hwdesc->buffer1_length = cpu_to_le16(first_seg_len);
 	hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma);
 
+	if (buffrag->dma > NETXEN_DMA_MASK) {
+		pci_unmap_single(port->pdev, buffrag->dma, first_seg_len,
+				 PCI_DMA_TODEVICE);
+		spin_lock_irqsave(&tx_bounce_lock, bounce_flags);
+		if (netxen_get_next_bounce_buffer
+		    (&tx_bounce_head, &tx_bounce_tail, &tmpbi, first_seg_len)) {
+			spin_unlock_irqrestore(&tx_bounce_lock, bounce_flags);
+			return NETDEV_TX_BUSY;
+		}
+		spin_unlock_irqrestore(&tx_bounce_lock, bounce_flags);
+		pbuf->tx_bounce_buff = IS_BOUNCE;
+		bounce_data = tx_bounce_ptr[tmpbi.buffer_index] +
+		    tmpbi.buffer_offset;
+		buffrag->dma = pci_map_single(port->pdev, bounce_data,
+					      first_seg_len, PCI_DMA_TODEVICE);
+		hwdesc->addr_buffer1 = buffrag->dma;
+		memcpy(bounce_data, skb->data, first_seg_len);
+		pbuf->bnext = tx_bounce_head;
+	}
+
 	for (i = 1, k = 1; i < frag_count; i++, k++) {
 		struct skb_frag_struct *frag;
 		int len, temp_len;
 		unsigned long offset;
 		dma_addr_t temp_dma;
+		struct page *bounce_frag_page;
+		u32 bounce_page_offset;
 
 		/* move to next desc. if there is a need */
 		if ((i & 0x3) == 0) {
@@ -827,6 +984,34 @@ static int netxen_nic_xmit_frame(struct
 		buffrag->dma = temp_dma;
 		buffrag->length = temp_len;
 
+		if (temp_dma > NETXEN_DMA_MASK) {
+			pci_unmap_single(port->pdev, temp_dma, len,
+					 PCI_DMA_TODEVICE);
+			spin_lock_irqsave(&tx_bounce_lock, bounce_flags);
+			if (netxen_get_next_bounce_buffer(&tx_bounce_head,
+							  &tx_bounce_tail,
+							  &tmpbi, len)) {
+				spin_unlock_irqrestore(&tx_bounce_lock,
+						       bounce_flags);
+				return NETDEV_TX_BUSY;
+			}
+			spin_unlock_irqrestore(&tx_bounce_lock, bounce_flags);
+			pbuf->tx_bounce_buff = IS_BOUNCE;
+			bounce_data = tx_bounce_ptr[tmpbi.buffer_index] +
+			    tmpbi.buffer_offset;
+
+			bounce_frag_page = virt_to_page(bounce_data);
+			bounce_page_offset = (unsigned long)bounce_data -
+			    (unsigned long)page_address(bounce_frag_page);
+			temp_dma = pci_map_page(port->pdev, bounce_frag_page,
+						bounce_page_offset, len,
+						PCI_DMA_TODEVICE);
+			buffrag->dma = temp_dma;
+			memcpy(bounce_data, page_address(frag->page) + offset,
+			       len);
+			pbuf->bnext = tx_bounce_head;
+		}
+
 		DPRINTK(INFO, "for loop. i=%d k=%d\n", i, k);
 		switch (k) {
 		case 0:
@@ -1116,8 +1301,9 @@ netxen_nic_ioctl(struct net_device *netd
 		if (ifr->ifr_data) {
 			sprintf(dev_name, "%s-%d", NETXEN_NIC_NAME_RSP,
 				port->portnum);
-			nr_bytes = copy_to_user((char *)ifr->ifr_data, dev_name,
-						NETXEN_NIC_NAME_LEN);
+			nr_bytes =
+			    copy_to_user((char __user *)ifr->ifr_data, dev_name,
+					 NETXEN_NIC_NAME_LEN);
 			if (nr_bytes)
 				err = -EIO;
 
diff --git a/drivers/net/netxen/netxen_nic_phan_reg.h b/drivers/net/netxen/netxen_nic_phan_reg.h
index 8181d43..1da7093 100644
--- a/drivers/net/netxen/netxen_nic_phan_reg.h
+++ b/drivers/net/netxen/netxen_nic_phan_reg.h
@@ -85,17 +85,17 @@
 #define CRB_TX_PKT_TIMER		NETXEN_NIC_REG(0x94)
 #define CRB_RX_PKT_CNT			NETXEN_NIC_REG(0x98)
 #define CRB_RX_TMR_CNT			NETXEN_NIC_REG(0x9c)
-#define CRB_INT_THRESH		 NETXEN_NIC_REG(0xa4)
+#define CRB_INT_THRESH			NETXEN_NIC_REG(0xa4)
 
 /* Register for communicating XG link status */
 #define CRB_XG_STATE			NETXEN_NIC_REG(0xa0)
 
 /* Register for communicating card temperature */
 /* Upper 16 bits are temperature value. Lower 16 bits are the state */
-#define CRB_TEMP_STATE		 NETXEN_NIC_REG(0xa8)
-#define nx_get_temp_val(x)	     ((x) >> 16)
-#define nx_get_temp_state(x)	   ((x) & 0xffff)
-#define nx_encode_temp(val, state)     (((val) << 16) | (state))
+#define CRB_TEMP_STATE			NETXEN_NIC_REG(0xa8)
+#define nx_get_temp_val(x)		((x) >> 16)
+#define nx_get_temp_state(x)		((x) & 0xffff)
+#define nx_encode_temp(val, state)	(((val) << 16) | (state))
 
 /* Debug registers for controlling NIC pkt gen agent */
 #define CRB_AGENT_GO			NETXEN_NIC_REG(0xb0)

Re: [PATCH 2/3] NetXen: 64-bit memory fixes

From: Stephen Hemminger <hidden>
Date: 2006-12-01 18:58:16

Please don't mix whitespace and other fixes at same time.
Add a cleanup patch at beginning or end of series.

Please don't add other things like the driver name this in with bounce buffer
patch. Why the name change from netxen to netxen_nic?

+/* Bounce buffer index */
+struct bounce_index {
+	/* Index of a buffer */
+	unsigned buffer_index;
+	/* Offset inside the buffer */
+	unsigned buffer_offset;
+};
+
+#define IS_BOUNCE 0xcafebb
Magic number? is this safe? why? there is a Documentation
file for these magic-number.txt.
quoted hunk
diff --git a/drivers/net/netxen/netxen_nic_isr.c b/drivers/net/netxen/netxen_nic_isr.c
index ae180fe..f6ae9fd 100644
--- a/drivers/net/netxen/netxen_nic_isr.c
+++ b/drivers/net/netxen/netxen_nic_isr.c
@@ -68,8 +68,7 @@ struct net_device_stats *netxen_nic_get_
 void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 portno,
 				 u32 link)
 {
-	struct netxen_port *pport = adapter->port[portno];
-	struct net_device *netdev = pport->netdev;
+	struct net_device *netdev = (adapter->port[portno])->netdev;
 
Parens unnecessary here.

-- 
Stephen Hemminger [off-list ref]

Re: [PATCH 2/3] NetXen: 64-bit memory fixes

From: Jeff Garzik <hidden>
Date: 2006-12-02 05:33:00

Amit S. Kale wrote:
NetXen: 1G/10G Ethernet driver updates
	- These fixes take care of driver on machines with >4G memory
	- Driver cleanup

Signed-off-by: Amit S. Kale <redacted>

 netxen_nic.h          |   29 +++++--
 netxen_nic_ethtool.c  |   19 ++--
 netxen_nic_hw.c       |    4 
 netxen_nic_hw.h       |    4 
 netxen_nic_init.c     |   51 +++++++++++-
 netxen_nic_isr.c      |    3 
 netxen_nic_main.c     |  204 +++++++++++++++++++++++++++++++++++++++++++++++---
 netxen_nic_phan_reg.h |   10 +-
NAK, the driver itself should not be doing bounce buffering

network devices don't handle pci_dma_mapping_error()'s

From: Stephen Hemminger <hidden>
Date: 2006-12-04 18:40:20

On Sat, 02 Dec 2006 00:32:55 -0500
Jeff Garzik [off-list ref] wrote:
Amit S. Kale wrote:
quoted
NetXen: 1G/10G Ethernet driver updates
	- These fixes take care of driver on machines with >4G memory
	- Driver cleanup

Signed-off-by: Amit S. Kale <redacted>

 netxen_nic.h          |   29 +++++--
 netxen_nic_ethtool.c  |   19 ++--
 netxen_nic_hw.c       |    4 
 netxen_nic_hw.h       |    4 
 netxen_nic_init.c     |   51 +++++++++++-
 netxen_nic_isr.c      |    3 
 netxen_nic_main.c     |  204 +++++++++++++++++++++++++++++++++++++++++++++++---
 netxen_nic_phan_reg.h |   10 +-
NAK, the driver itself should not be doing bounce buffering


-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
I notice that no current network driver handles dma mapping errors.
Might that be part of the problem.  On i386, this never happens, and
it would be rare on most others.

Why don't drivers do some checking/unwind.  Here is what it would look like on
Tx for sky2...
--- sky2.orig/drivers/net/sky2.c	2006-12-04 10:12:16.000000000 -0800
+++ sky2/drivers/net/sky2.c	2006-12-04 10:37:42.000000000 -0800
@@ -1277,6 +1277,38 @@
 	return count;
 }
 
+
+static inline void tx_le_done(struct sky2_port *sky2, unsigned idx)
+{
+	struct pci_dev *pdev = sky2->hw->pdev;
+	struct sky2_tx_le *le = sky2->tx_le + idx;
+	struct tx_ring_info *re = sky2->tx_ring + idx;
+
+	switch(le->opcode & ~HW_OWNER) {
+	case OP_LARGESEND:
+	case OP_PACKET:
+		pci_unmap_single(pdev,
+				 pci_unmap_addr(re, mapaddr),
+				 pci_unmap_len(re, maplen),
+				 PCI_DMA_TODEVICE);
+		break;
+	case OP_BUFFER:
+		pci_unmap_page(pdev, pci_unmap_addr(re, mapaddr),
+			       pci_unmap_len(re, maplen),
+			       PCI_DMA_TODEVICE);
+		break;
+	}
+
+	if (le->ctrl & EOP) {
+		if (unlikely(netif_msg_tx_done(sky2)))
+			printk(KERN_DEBUG "%s: tx done %u\n", sky2->netdev->name,
+			       idx);
+		dev_kfree_skb_any(re->skb);
+	}
+
+	le->opcode = 0;	/* paranoia */
+}
+
 /*
  * Put one packet in ring for transmit.
  * A single packet can generate multiple list elements, and
@@ -1292,7 +1324,7 @@
 	unsigned i, len;
 	dma_addr_t mapping;
 	u32 addr64;
-	u16 mss;
+	u16 mss, first;
 	u8 ctrl;
 
  	if (unlikely(tx_avail(sky2) < tx_le_req(skb)))
@@ -1303,7 +1335,13 @@
 		       dev->name, sky2->tx_prod, skb->len);
 
 	len = skb_headlen(skb);
+	first = sky2->tx_prod;
 	mapping = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE);
+	if (pci_dma_mapping_error(mapping)) {
+		printk(KERN_INFO "%s: tx dma mapping error\n", dev->name);
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
+	}
 	addr64 = high32(mapping);
 
 	/* Send high bits if changed or crosses boundary */
@@ -1383,6 +1421,10 @@
 
 		mapping = pci_map_page(hw->pdev, frag->page, frag->page_offset,
 				       frag->size, PCI_DMA_TODEVICE);
+
+		if (pci_dma_mapping_error(mapping))
+			goto map_error;
+
 		addr64 = high32(mapping);
 		if (addr64 != sky2->tx_addr64) {
 			le = get_tx_le(sky2);
@@ -1413,6 +1455,15 @@
 
 	dev->trans_start = jiffies;
 	return NETDEV_TX_OK;
+
+map_error:
+	/* map failure on fragmented send, free work from first..sky2->tx_prod */
+	printk(KERN_INFO "%s: tx dma page mapping error\n", dev->name);
+	le->ctrl |= EOP;
+	for (i = first; i != sky2->tx_prod; i = RING_NEXT(i, TX_RING_SIZE))
+		tx_le_done(sky2, i);
+	sky2->tx_prod = first;
+	return NETDEV_TX_OK;
 }
 
 /*
@@ -1424,40 +1475,12 @@
 static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
 {
 	struct net_device *dev = sky2->netdev;
-	struct pci_dev *pdev = sky2->hw->pdev;
 	unsigned idx;
 
 	BUG_ON(done >= TX_RING_SIZE);
 
-	for (idx = sky2->tx_cons; idx != done;
-	     idx = RING_NEXT(idx, TX_RING_SIZE)) {
-		struct sky2_tx_le *le = sky2->tx_le + idx;
-		struct tx_ring_info *re = sky2->tx_ring + idx;
-
-		switch(le->opcode & ~HW_OWNER) {
-		case OP_LARGESEND:
-		case OP_PACKET:
-			pci_unmap_single(pdev,
-					 pci_unmap_addr(re, mapaddr),
-					 pci_unmap_len(re, maplen),
-					 PCI_DMA_TODEVICE);
-			break;
-		case OP_BUFFER:
-			pci_unmap_page(pdev, pci_unmap_addr(re, mapaddr),
-				       pci_unmap_len(re, maplen),
-				       PCI_DMA_TODEVICE);
-			break;
-		}
-
-		if (le->ctrl & EOP) {
-			if (unlikely(netif_msg_tx_done(sky2)))
-				printk(KERN_DEBUG "%s: tx done %u\n",
-				       dev->name, idx);
-			dev_kfree_skb_any(re->skb);
-		}
-
-		le->opcode = 0;	/* paranoia */
-	}
+	for (idx = sky2->tx_cons; idx != done; idx = RING_NEXT(idx, TX_RING_SIZE))
+		tx_le_done(sky2, idx);
 
 	sky2->tx_cons = idx;
 	if (tx_avail(sky2) > MAX_SKB_TX_LE + 4)

Re: network devices don't handle pci_dma_mapping_error()'s

From: Muli Ben-Yehuda <hidden>
Date: 2006-12-05 07:00:48

On Mon, Dec 04, 2006 at 10:39:49AM -0800, Stephen Hemminger wrote:
I notice that no current network driver handles dma mapping errors.
Might that be part of the problem.  On i386, this never happens, and
it would be rare on most others.
IOMMUs are already available on x86-64 and are going to get widespread
with the the introduction of IOMMUs from Intel and AMD. Might as well
fix it now...

How about CONFIG_DEBUG_DMA_API that does book-keeping and yells if a
driver is mis-using the DMA API?

Cheers,
Muli

Re: network devices don't handle pci_dma_mapping_error()'s

From: Stephen Hemminger <hidden>
Date: 2006-12-06 18:30:36

On Tue, 5 Dec 2006 09:00:45 +0200
Muli Ben-Yehuda [off-list ref] wrote:
On Mon, Dec 04, 2006 at 10:39:49AM -0800, Stephen Hemminger wrote:
quoted
I notice that no current network driver handles dma mapping errors.
Might that be part of the problem.  On i386, this never happens, and
it would be rare on most others.
IOMMUs are already available on x86-64 and are going to get widespread
with the the introduction of IOMMUs from Intel and AMD. Might as well
fix it now...

How about CONFIG_DEBUG_DMA_API that does book-keeping and yells if a
driver is mis-using the DMA API?

Cheers,
Muli
I think it is really only an issue for drivers that turn on HIGH_DMA
and have limited mask values. The majority of drivers either only handle
32 bit (!HIGH_DMA) or do full 64 bit mapping. I don't know the details
of how we manage IOMMU, but doesn't mapping always work for those drivers.

That just leaves devices with odd size mask values that need to be
handle mapping errors.

-- 
Stephen Hemminger [off-list ref]

Re: network devices don't handle pci_dma_mapping_error()'s

From: Muli Ben-Yehuda <hidden>
Date: 2006-12-06 19:33:32

On Wed, Dec 06, 2006 at 10:16:44AM -0800, Stephen Hemminger wrote:
I think it is really only an issue for drivers that turn on HIGH_DMA
and have limited mask values. The majority of drivers either only
handle 32 bit (!HIGH_DMA) or do full 64 bit mapping. I don't know
the details of how we manage IOMMU, but doesn't mapping always work
for those drivers.
It's up to an IOMMU (DMA-API) implementation to define what
constitutes a mapping error, e.g., Calgary and GART on x86-64 will
return bad_dma_address from the mapping functions when they run out of
entries in the IO space, which can happen regardless of the mask.

Cheers,
Muli

Re: network devices don't handle pci_dma_mapping_error()'s

From: David Miller <davem@davemloft.net>
Date: 2006-12-07 00:54:08

From: Stephen Hemminger <redacted>
Date: Wed, 6 Dec 2006 10:16:44 -0800
I think it is really only an issue for drivers that turn on HIGH_DMA
and have limited mask values. The majority of drivers either only handle
32 bit (!HIGH_DMA) or do full 64 bit mapping. I don't know the details
of how we manage IOMMU, but doesn't mapping always work for those drivers.

That just leaves devices with odd size mask values that need to be
handle mapping errors.
Not true.

On platforms such as sparc64 the IOMMU is used for all DMA mappings,
no matter what, because only IOMMU based mappings can do prefetching
and write-combining in the PCI controller.

The problem with just silently dropping packets that can't get DMA
mapped is that you're going to drop a very large sequence of these
while the IOMMU is out of space, and that to me looks like a bad
quality of implementation decision.

The IOMMU layer really needs a way to callback the driver to tell it
when space is available, or something similar.

FWIW, Solaris handles this by blocking when the IOMMU is out of space
since under Solaris even interrupt contexts can block (via interrupt
threads).

Re: network devices don't handle pci_dma_mapping_error()'s

From: Stephen Hemminger <hidden>
Date: 2006-12-07 01:02:25

On Wed, 06 Dec 2006 16:54:18 -0800 (PST)
David Miller [off-list ref] wrote:
From: Stephen Hemminger <redacted>
Date: Wed, 6 Dec 2006 10:16:44 -0800
quoted
I think it is really only an issue for drivers that turn on HIGH_DMA
and have limited mask values. The majority of drivers either only handle
32 bit (!HIGH_DMA) or do full 64 bit mapping. I don't know the details
of how we manage IOMMU, but doesn't mapping always work for those drivers.

That just leaves devices with odd size mask values that need to be
handle mapping errors.
Not true.

On platforms such as sparc64 the IOMMU is used for all DMA mappings,
no matter what, because only IOMMU based mappings can do prefetching
and write-combining in the PCI controller.

The problem with just silently dropping packets that can't get DMA
mapped is that you're going to drop a very large sequence of these
while the IOMMU is out of space, and that to me looks like a bad
quality of implementation decision.

The IOMMU layer really needs a way to callback the driver to tell it
when space is available, or something similar.

FWIW, Solaris handles this by blocking when the IOMMU is out of space
since under Solaris even interrupt contexts can block (via interrupt
threads).
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
The more robust way would be to stop the queue (like flow control)
and return busy. You would need a timer though to handle the case
where some disk i/o stole all the mappings and then network device flow
blocked.

Re: network devices don't handle pci_dma_mapping_error()'s

From: David Miller <davem@davemloft.net>
Date: 2006-12-07 01:13:07

From: Stephen Hemminger <redacted>
Date: Wed, 6 Dec 2006 16:58:35 -0800
The more robust way would be to stop the queue (like flow control)
and return busy. You would need a timer though to handle the case
where some disk i/o stole all the mappings and then network device flow
blocked.
You need some kind of fairness, yes, that's why I suggested a
callback.  When your DMA allocation fails, you get into the rear of
the FIFO, when a free occurs, we callback starting from the head of
the FIFO.  You don't get removed from the FIFO unless at least one of
your DMA allocation retries succeed.

Re: network devices don't handle pci_dma_mapping_error()'s

From: Rick Jones <hidden>
Date: 2006-12-07 02:18:55

David Miller wrote:
From: Stephen Hemminger <redacted>
Date: Wed, 6 Dec 2006 16:58:35 -0800

quoted
The more robust way would be to stop the queue (like flow control)
and return busy. You would need a timer though to handle the case
where some disk i/o stole all the mappings and then network device flow
blocked.

You need some kind of fairness, yes, that's why I suggested a
callback.  When your DMA allocation fails, you get into the rear of
the FIFO, when a free occurs, we callback starting from the head of
the FIFO.  You don't get removed from the FIFO unless at least one of
your DMA allocation retries succeed.
While tossing a TCP|UDP|SCTP|etc packet could be plusungood, especially 
if the IOMMU fills frequently (for some suitable definiton of 
frequently), is it really worth the effort to save say an ACK?

rick jones

Re: network devices don't handle pci_dma_mapping_error()'s

From: David Miller <davem@davemloft.net>
Date: 2006-12-07 02:31:15

From: Rick Jones <redacted>
Date: Wed, 06 Dec 2006 18:18:52 -0800
While tossing a TCP|UDP|SCTP|etc packet could be plusungood, especially 
if the IOMMU fills frequently (for some suitable definiton of 
frequently), is it really worth the effort to save say an ACK?
ACKs are less important than data packets sure.

But the drivers shouldn't be parsing packets at transmit time to
decide what to do.

And when this kind of thing fails, it's going to fail for all
the packets currently queued up to the device for transmit.
So it's likely not "just an ACK", but rather a set of several
packets composed of ACKs and data packets.

Re: network devices don't handle pci_dma_mapping_error()'s

From: Amit S. Kale <hidden>
Date: 2006-12-07 06:18:00

On Thursday 07 December 2006 01:03, Muli Ben-Yehuda wrote:
On Wed, Dec 06, 2006 at 10:16:44AM -0800, Stephen Hemminger wrote:
quoted
I think it is really only an issue for drivers that turn on HIGH_DMA
and have limited mask values. The majority of drivers either only
handle 32 bit (!HIGH_DMA) or do full 64 bit mapping. I don't know
the details of how we manage IOMMU, but doesn't mapping always work
for those drivers.
It's up to an IOMMU (DMA-API) implementation to define what
constitutes a mapping error, e.g., Calgary and GART on x86-64 will
return bad_dma_address from the mapping functions when they run out of
entries in the IO space, which can happen regardless of the mask.
We've seen IOMMU space running out on ia64 systems. Would this be the case 
with other 10G driver requiring IOMMU remapping? We need frequent map-unmap 
at near 10G throughput.

On the x86_64 boxes that don't feature iommu functionality (because the 
motherboard disables it or because Linux can't handle it) Linux bounce buffer 
framework automatically comes into picture. Could we have the same framework 
take over when IOMMU space is over? I don't think this is possible with 
present code, though. We probably can have fallback_dma_ops in addition to 
dma_ops.

-Amit

Re: network devices don't handle pci_dma_mapping_error()'s

From: Amit S. Kale <hidden>
Date: 2006-12-07 06:25:07

We can let a driver handle dma mapping errors using these->

1.Reduce the size of a receive ring. This will free some possibly remapped 
memory, reducing pressure on iommu. We also need to printk a message so that 
a user knows the reason why receive ring was shrunk. Growing it when iommu 
pressure goes down will result in a ping-pong.
2. Force processing of receive and transmit ring. This will ensure that the 
buffers processed by hardware are freed, reducing iommu pressure.

3. If we need to do (1) and (2) a predefined number of times (say 20), stop 
the queue. Stopping the queue in general will cause a ping-pong, so it should 
be avoided as far as possible.

-Amit


On Thursday 07 December 2006 06:28, Stephen Hemminger wrote:
The more robust way would be to stop the queue (like flow control)
and return busy. You would need a timer though to handle the case
where some disk i/o stole all the mappings and then network device flow
blocked.

Re: network devices don't handle pci_dma_mapping_error()'s

From: Stephen Hemminger <hidden>
Date: 2006-12-07 06:53:33

Amit S. Kale wrote:
We can let a driver handle dma mapping errors using these->

1.Reduce the size of a receive ring. This will free some possibly remapped 
memory, reducing pressure on iommu. We also need to printk a message so that 
a user knows the reason why receive ring was shrunk. Growing it when iommu 
pressure goes down will result in a ping-pong.
2. Force processing of receive and transmit ring. This will ensure that the 
buffers processed by hardware are freed, reducing iommu pressure.

3. If we need to do (1) and (2) a predefined number of times (say 20), stop 
the queue. Stopping the queue in general will cause a ping-pong, so it should 
be avoided as far as possible.

  
But what if it isn't the network device that is using all the IOMMU 
resources.
Linux is already crap at handling out of memory, lets not add another 
starvation
path.

In this case, the device does have some idea about "worst case" i/o's in 
flight,
couldn't we have some sort of reservation/management system to avoid 
overcommitting?
Worst case map usage for a network device can be fairly high because of 
the possiblity
of on transmit with a high number of pages when using TSO. Perhaps the 
transmit
ring needs to be accounted for in maps used rather than packets pending.

Re: network devices don't handle pci_dma_mapping_error()'s

From: Amit S. Kale <hidden>
Date: 2006-12-07 06:55:24

On Thursday 07 December 2006 12:16, Stephen Hemminger wrote:
Amit S. Kale wrote:
quoted
We can let a driver handle dma mapping errors using these->

1.Reduce the size of a receive ring. This will free some possibly
remapped memory, reducing pressure on iommu. We also need to printk a
message so that a user knows the reason why receive ring was shrunk.
Growing it when iommu pressure goes down will result in a ping-pong.
2. Force processing of receive and transmit ring. This will ensure that
the buffers processed by hardware are freed, reducing iommu pressure.

3. If we need to do (1) and (2) a predefined number of times (say 20),
stop the queue. Stopping the queue in general will cause a ping-pong, so
it should be avoided as far as possible.
But what if it isn't the network device that is using all the IOMMU
resources.
Linux is already crap at handling out of memory, lets not add another
starvation
path.

In this case, the device does have some idea about "worst case" i/o's in
flight,
couldn't we have some sort of reservation/management system to avoid
overcommitting?
Worst case map usage for a network device can be fairly high because of
the possiblity
of on transmit with a high number of pages when using TSO. Perhaps the
transmit
ring needs to be accounted for in maps used rather than packets pending.
I am afraid I don't have a good answer for that. Any kind of reservation may 
result in underutilization and transparently shared resources may result in 
starvation.

Designing heuristics for handling these cases may be the only possible wayout, 
though these heuristics need to be validated frequently to ensure that they 
aren't out of date.
-Amit

Re: network devices don't handle pci_dma_mapping_error()'s

From: David Miller <davem@davemloft.net>
Date: 2006-12-07 07:24:48

From: "Amit S. Kale" <redacted>
Date: Thu, 7 Dec 2006 11:55:22 +0530
We can let a driver handle dma mapping errors using these->

1.Reduce the size of a receive ring. This will free some possibly remapped 
memory, reducing pressure on iommu. We also need to printk a message so that 
a user knows the reason why receive ring was shrunk. Growing it when iommu 
pressure goes down will result in a ping-pong.
2. Force processing of receive and transmit ring. This will ensure that the 
buffers processed by hardware are freed, reducing iommu pressure.

3. If we need to do (1) and (2) a predefined number of times (say 20), stop 
the queue. Stopping the queue in general will cause a ping-pong, so it should 
be avoided as far as possible.
This scheme assumes the networking card is the culprit.  In many
workloads it will not be and these efforts will be in vain and perhaps
even make the situation worse.  There's not reason to run the RX and
TX queues, and even shrink them, when the FC controller has most of
the IOMMU entires tied up.

That's why users needs to queue up and get feedback when IOMMU space
is made available.

Re: network devices don't handle pci_dma_mapping_error()'s

From: Muli Ben-Yehuda <hidden>
Date: 2006-12-07 13:04:20

On Thu, Dec 07, 2006 at 11:48:14AM +0530, Amit S. Kale wrote:
On the x86_64 boxes that don't feature iommu functionality (because the 
motherboard disables it or because Linux can't handle it) Linux bounce buffer 
framework automatically comes into picture. Could we have the same framework 
take over when IOMMU space is over? I don't think this is possible with 
present code, though. We probably can have fallback_dma_ops in addition to 
dma_ops.
In the general case, no - some platforms (including x86-64 on IBM's
high end servers!) have an isolation capable IOMMU, which means all
DMA mappings need to go through it, so a general mechanism to cope
with DMA mappings running out is still needed.

Cheers,
Muli

Re: network devices don't handle pci_dma_mapping_error()'s

From: Stephen Hemminger <hidden>
Date: 2006-12-07 20:09:28

On Wed, 06 Dec 2006 23:24:59 -0800 (PST)
David Miller [off-list ref] wrote:
From: "Amit S. Kale" <redacted>
Date: Thu, 7 Dec 2006 11:55:22 +0530
quoted
We can let a driver handle dma mapping errors using these->

1.Reduce the size of a receive ring. This will free some possibly remapped 
memory, reducing pressure on iommu. We also need to printk a message so that 
a user knows the reason why receive ring was shrunk. Growing it when iommu 
pressure goes down will result in a ping-pong.
2. Force processing of receive and transmit ring. This will ensure that the 
buffers processed by hardware are freed, reducing iommu pressure.

3. If we need to do (1) and (2) a predefined number of times (say 20), stop 
the queue. Stopping the queue in general will cause a ping-pong, so it should 
be avoided as far as possible.
This scheme assumes the networking card is the culprit.  In many
workloads it will not be and these efforts will be in vain and perhaps
even make the situation worse.  There's not reason to run the RX and
TX queues, and even shrink them, when the FC controller has most of
the IOMMU entires tied up.

That's why users needs to queue up and get feedback when IOMMU space
is made available.
Looking at other subsystems, the disk code seems to return I/O errors
if dma mapping fails. Perhaps this discussion needs to move off to lkml
or the platform lists.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help