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

Commit 70c51da2 authored by Arthur Jones's avatar Arthur Jones Committed by Roland Dreier
Browse files

IB/ipath: Use counters in ipath_poll and cleanup interrupts in ipath_close



ipath_poll() suffered from a couple subtle bugs.  Under the right
conditions we could leave recv interrupts enabled on an ipath user
context on close, thereby taking potentially unwanted interrupts on the
next open -- this is fixed by unconditionally turning off recv
interrupts on close.  Also, we now use counters rather than set/clear
bits which allows us to make sure we catch all interrupts at the cost of
changing the semantics slightly (it's now give me all events since the
last time I called poll() rather than give me all events since I called
_this_ poll routine).  We also added some memory barriers which may help
ensure we get all notifications in a timely manner.

Signed-off-by: default avatarArthur Jones <arthur.jones@qlogic.com>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent 542869a1
Loading
Loading
Loading
Loading
+41 −26
Original line number Diff line number Diff line
@@ -1341,6 +1341,19 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
	return ret;
}

static unsigned ipath_poll_hdrqfull(struct ipath_portdata *pd)
{
	unsigned pollflag = 0;

	if ((pd->poll_type & IPATH_POLL_TYPE_OVERFLOW) &&
	    pd->port_hdrqfull != pd->port_hdrqfull_poll) {
		pollflag |= POLLIN | POLLRDNORM;
		pd->port_hdrqfull_poll = pd->port_hdrqfull;
	}

	return pollflag;
}

static unsigned int ipath_poll_urgent(struct ipath_portdata *pd,
				      struct file *fp,
				      struct poll_table_struct *pt)
@@ -1350,22 +1363,20 @@ static unsigned int ipath_poll_urgent(struct ipath_portdata *pd,

	dd = pd->port_dd;

	if (test_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag)) {
		pollflag |= POLLERR;
		clear_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag);
	}
	/* variable access in ipath_poll_hdrqfull() needs this */
	rmb();
	pollflag = ipath_poll_hdrqfull(pd);

	if (test_bit(IPATH_PORT_WAITING_URG, &pd->int_flag)) {
	if (pd->port_urgent != pd->port_urgent_poll) {
		pollflag |= POLLIN | POLLRDNORM;
		clear_bit(IPATH_PORT_WAITING_URG, &pd->int_flag);
		pd->port_urgent_poll = pd->port_urgent;
	}

	if (!pollflag) {
		/* this saves a spin_lock/unlock in interrupt handler... */
		set_bit(IPATH_PORT_WAITING_URG, &pd->port_flag);
		if (pd->poll_type & IPATH_POLL_TYPE_OVERFLOW)
			set_bit(IPATH_PORT_WAITING_OVERFLOW,
				&pd->port_flag);

		/* flush waiting flag so don't miss an event... */
		wmb();
		poll_wait(fp, &pd->port_wait, pt);
	}

@@ -1376,31 +1387,27 @@ static unsigned int ipath_poll_next(struct ipath_portdata *pd,
				    struct file *fp,
				    struct poll_table_struct *pt)
{
	u32 head, tail;
	u32 head;
	u32 tail;
	unsigned pollflag = 0;
	struct ipath_devdata *dd;

	dd = pd->port_dd;

	/* variable access in ipath_poll_hdrqfull() needs this */
	rmb();
	pollflag = ipath_poll_hdrqfull(pd);

	head = ipath_read_ureg32(dd, ur_rcvhdrhead, pd->port_port);
	tail = *(volatile u64 *)pd->port_rcvhdrtail_kvaddr;

	if (test_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag)) {
		pollflag |= POLLERR;
		clear_bit(IPATH_PORT_WAITING_OVERFLOW, &pd->int_flag);
	}

	if (tail != head ||
	    test_bit(IPATH_PORT_WAITING_RCV, &pd->int_flag)) {
	if (head != tail)
		pollflag |= POLLIN | POLLRDNORM;
		clear_bit(IPATH_PORT_WAITING_RCV, &pd->int_flag);
	}

	if (!pollflag) {
	else {
		/* this saves a spin_lock/unlock in interrupt handler */
		set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag);
		if (pd->poll_type & IPATH_POLL_TYPE_OVERFLOW)
			set_bit(IPATH_PORT_WAITING_OVERFLOW,
				&pd->port_flag);
		/* flush waiting flag so we don't miss an event */
		wmb();

		set_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT,
			&dd->ipath_rcvctrl);
@@ -1917,6 +1924,12 @@ static int ipath_do_user_init(struct file *fp,
	ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n",
		pd->port_port, head32);
	pd->port_tidcursor = 0;	/* start at beginning after open */

	/* initialize poll variables... */
	pd->port_urgent = 0;
	pd->port_urgent_poll = 0;
	pd->port_hdrqfull_poll = pd->port_hdrqfull;

	/*
	 * now enable the port; the tail registers will be written to memory
	 * by the chip as soon as it sees the write to
@@ -2039,9 +2052,11 @@ static int ipath_close(struct inode *in, struct file *fp)

	if (dd->ipath_kregbase) {
		int i;
		/* atomically clear receive enable port. */
		/* atomically clear receive enable port and intr avail. */
		clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port,
			  &dd->ipath_rcvctrl);
		clear_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT,
			  &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
+10 −23
Original line number Diff line number Diff line
@@ -688,17 +688,9 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
					chkerrpkts = 1;
				dd->ipath_lastrcvhdrqtails[i] = tl;
				pd->port_hdrqfull++;
				if (test_bit(IPATH_PORT_WAITING_OVERFLOW,
					     &pd->port_flag)) {
					clear_bit(
					  IPATH_PORT_WAITING_OVERFLOW,
					  &pd->port_flag);
					set_bit(
					  IPATH_PORT_WAITING_OVERFLOW,
					  &pd->int_flag);
					wake_up_interruptible(
					  &pd->port_wait);
				}
				/* flush hdrqfull so that poll() sees it */
				wmb();
				wake_up_interruptible(&pd->port_wait);
			}
		}
	}
@@ -960,6 +952,8 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
	int i;
	int rcvdint = 0;

	/* test_bit below needs this... */
	rmb();
	portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) &
		 dd->ipath_i_rcvavail_mask)
		| ((istat >> INFINIPATH_I_RCVURG_SHIFT) &
@@ -967,22 +961,15 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
	for (i = 1; i < dd->ipath_cfgports; i++) {
		struct ipath_portdata *pd = dd->ipath_pd[i];
		if (portr & (1 << i) && pd && pd->port_cnt) {
			if (test_bit(IPATH_PORT_WAITING_RCV,
			if (test_and_clear_bit(IPATH_PORT_WAITING_RCV,
					       &pd->port_flag)) {
				clear_bit(IPATH_PORT_WAITING_RCV,
					  &pd->port_flag);
				set_bit(IPATH_PORT_WAITING_RCV,
					&pd->int_flag);
				clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT,
					  &dd->ipath_rcvctrl);
				wake_up_interruptible(&pd->port_wait);
				rcvdint = 1;
			} else if (test_bit(IPATH_PORT_WAITING_URG,
			} else if (test_and_clear_bit(IPATH_PORT_WAITING_URG,
						      &pd->port_flag)) {
				clear_bit(IPATH_PORT_WAITING_URG,
					  &pd->port_flag);
				set_bit(IPATH_PORT_WAITING_URG,
					&pd->int_flag);
				pd->port_urgent++;
				wake_up_interruptible(&pd->port_wait);
			}
		}
+6 −2
Original line number Diff line number Diff line
@@ -139,6 +139,12 @@ struct ipath_portdata {
	u32 port_pionowait;
	/* total number of rcvhdrqfull errors */
	u32 port_hdrqfull;
	/* saved total number of rcvhdrqfull errors for poll edge trigger */
	u32 port_hdrqfull_poll;
	/* total number of polled urgent packets */
	u32 port_urgent;
	/* saved total number of polled urgent packets for poll edge trigger */
	u32 port_urgent_poll;
	/* pid of process using this port */
	pid_t port_pid;
	/* same size as task_struct .comm[] */
@@ -757,8 +763,6 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
#define IPATH_PORT_MASTER_UNINIT 4
		/* waiting for an urgent packet to arrive */
#define IPATH_PORT_WAITING_URG 5
		/* waiting for a header overflow */
#define IPATH_PORT_WAITING_OVERFLOW 6

/* free up any allocated data at closes */
void ipath_free_data(struct ipath_portdata *dd);