Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 35783ec0 authored by Bryan O'Sullivan's avatar Bryan O'Sullivan Committed by Linus Torvalds
Browse files

[PATCH] IB/ipath: fix a bug that results in addresses near 0 being written via DMA



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: default avatarDave Olson <dave.olson@qlogic.com>
Signed-off-by: default avatarBryan O'Sullivan <bryan.osullivan@qlogic.com>
Cc: "Michael S. Tsirkin" <mst@mellanox.co.il>
Cc: Roland Dreier <rolandd@cisco.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 6d8e9dd0
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -1824,6 +1824,12 @@ static void cleanup_device(struct ipath_devdata *dd)
				  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;
+35 −26
Original line number Diff line number Diff line
@@ -1486,27 +1486,37 @@ static int ipath_close(struct inode *in, struct file *fp)
	}

	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);
		/*
		 * 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);
@@ -1514,14 +1524,13 @@ static int ipath_close(struct inode *in, struct file *fp)
		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);
	ipath_free_pddata(dd, pd); /* after releasing the mutex */
+17 −0
Original line number Diff line number Diff line
@@ -647,6 +647,7 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit)
	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)
@@ -834,6 +835,22 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit)
	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,
	 * even if we had errors
+2 −0
Original line number Diff line number Diff line
@@ -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.