[PATCH 34 of 39] IB/ipath - fix a bug that results in addresses near0 being written via DMA

From: Bryan O'Sullivan
Date: Thu Jun 29 2006 - 17:47:17 EST


We can't tell for sure if any packets are in the infinipath receive buffer
when we shut down a chip port. Normally this is taken care of by orderly
shutdown, but when processes are terminated, or sending process has a bug,
we can continue to receive packets. So rather than writing zero to the
address registers for the closing port, we point it at a dummy memory.

Signed-off-by: Dave Olson <dave.olson@xxxxxxxxxx>
Signed-off-by: Bryan O'Sullivan <bryan.osullivan@xxxxxxxxxx>

diff -r a7c1ad1e090b -r b6ebaf2dd2fd drivers/infiniband/hw/ipath/ipath_driver.c
--- a/drivers/infiniband/hw/ipath/ipath_driver.c Thu Jun 29 14:33:26 2006 -0700
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c Thu Jun 29 14:33:26 2006 -0700
@@ -1824,6 +1824,12 @@ static void cleanup_device(struct ipath_
dd->ipath_pioavailregs_phys);
dd->ipath_pioavailregs_dma = NULL;
}
+ if (dd->ipath_dummy_hdrq) {
+ dma_free_coherent(&dd->pcidev->dev,
+ dd->ipath_pd[0]->port_rcvhdrq_size,
+ dd->ipath_dummy_hdrq, dd->ipath_dummy_hdrq_phys);
+ dd->ipath_dummy_hdrq = NULL;
+ }

if (dd->ipath_pageshadow) {
struct page **tmpp = dd->ipath_pageshadow;
diff -r a7c1ad1e090b -r b6ebaf2dd2fd drivers/infiniband/hw/ipath/ipath_file_ops.c
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c Thu Jun 29 14:33:26 2006 -0700
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c Thu Jun 29 14:33:26 2006 -0700
@@ -1486,41 +1486,50 @@ static int ipath_close(struct inode *in,
}

if (dd->ipath_kregbase) {
- ipath_write_kreg_port(
- dd, dd->ipath_kregs->kr_rcvhdrtailaddr,
- port, 0ULL);
- ipath_write_kreg_port(
- dd, dd->ipath_kregs->kr_rcvhdraddr,
- pd->port_port, 0);
+ int i;
+ /* atomically clear receive enable port. */
+ clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port,
+ &dd->ipath_rcvctrl);
+ ipath_write_kreg( dd, dd->ipath_kregs->kr_rcvctrl,
+ dd->ipath_rcvctrl);
+ /* and read back from chip to be sure that nothing
+ * else is in flight when we do the rest */
+ (void)ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);

/* clean up the pkeys for this port user */
ipath_clean_part_key(pd, dd);

- if (port < dd->ipath_cfgports) {
- int i = dd->ipath_pbufsport * (port - 1);
- ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport);
-
- /* atomically clear receive enable port. */
- clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port,
- &dd->ipath_rcvctrl);
- ipath_write_kreg(
- dd,
- dd->ipath_kregs->kr_rcvctrl,
- dd->ipath_rcvctrl);
-
- if (dd->ipath_pageshadow)
- unlock_expected_tids(pd);
- ipath_stats.sps_ports--;
- ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n",
- pd->port_comm, pd->port_pid,
- dd->ipath_unit, port);
- }
+
+ /*
+ * be paranoid, and never write 0's to these, just use an
+ * unused part of the port 0 tail page. Of course,
+ * rcvhdraddr points to a large chunk of memory, so this
+ * could still trash things, but at least it won't trash
+ * page 0, and by disabling the port, it should stop "soon",
+ * even if a packet or two is in already in flight after we
+ * disabled the port.
+ */
+ ipath_write_kreg_port(dd,
+ dd->ipath_kregs->kr_rcvhdrtailaddr, port,
+ dd->ipath_dummy_hdrq_phys);
+ ipath_write_kreg_port(dd, dd->ipath_kregs->kr_rcvhdraddr,
+ pd->port_port, dd->ipath_dummy_hdrq_phys);
+
+ i = dd->ipath_pbufsport * (port - 1);
+ ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport);
+
+ if (dd->ipath_pageshadow)
+ unlock_expected_tids(pd);
+ ipath_stats.sps_ports--;
+ ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n",
+ pd->port_comm, pd->port_pid,
+ dd->ipath_unit, port);
+
+ dd->ipath_f_clear_tids(dd, pd->port_port);
}

pd->port_cnt = 0;
pd->port_pid = 0;
-
- dd->ipath_f_clear_tids(dd, pd->port_port);

dd->ipath_pd[pd->port_port] = NULL; /* before releasing mutex */
mutex_unlock(&ipath_mutex);
diff -r a7c1ad1e090b -r b6ebaf2dd2fd drivers/infiniband/hw/ipath/ipath_init_chip.c
--- a/drivers/infiniband/hw/ipath/ipath_init_chip.c Thu Jun 29 14:33:26 2006 -0700
+++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c Thu Jun 29 14:33:26 2006 -0700
@@ -647,6 +647,7 @@ int ipath_init_chip(struct ipath_devdata
u32 val32, kpiobufs;
u64 val;
struct ipath_portdata *pd = NULL; /* keep gcc4 happy */
+ gfp_t gfp_flags = GFP_USER | __GFP_COMP;

ret = init_housekeeping(dd, &pd, reinit);
if (ret)
@@ -833,6 +834,22 @@ int ipath_init_chip(struct ipath_devdata
"rcvhdrq and/or egr bufs\n");
else
enable_chip(dd, pd, reinit);
+
+
+ if (!ret && !reinit) {
+ /* used when we close a port, for DMA already in flight at close */
+ dd->ipath_dummy_hdrq = dma_alloc_coherent(
+ &dd->pcidev->dev, pd->port_rcvhdrq_size,
+ &dd->ipath_dummy_hdrq_phys,
+ gfp_flags);
+ if (!dd->ipath_dummy_hdrq ) {
+ dev_info(&dd->pcidev->dev,
+ "Couldn't allocate 0x%lx bytes for dummy hdrq\n",
+ pd->port_rcvhdrq_size);
+ /* fallback to just 0'ing */
+ dd->ipath_dummy_hdrq_phys = 0UL;
+ }
+ }

/*
* cause retrigger of pending interrupts ignored during init,
diff -r a7c1ad1e090b -r b6ebaf2dd2fd drivers/infiniband/hw/ipath/ipath_kernel.h
--- a/drivers/infiniband/hw/ipath/ipath_kernel.h Thu Jun 29 14:33:26 2006 -0700
+++ b/drivers/infiniband/hw/ipath/ipath_kernel.h Thu Jun 29 14:33:26 2006 -0700
@@ -352,6 +352,8 @@ struct ipath_devdata {
/* check for stale messages in rcv queue */
/* only allow one intr at a time. */
unsigned long ipath_rcv_pending;
+ void *ipath_dummy_hdrq; /* used after port close */
+ dma_addr_t ipath_dummy_hdrq_phys;

/*
* Shadow copies of registers; size indicates read access size.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/