Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 25 Jun 2015 07:52:51 +0000 (UTC)
From:      Michael Gmelin <grembo@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r284803 - head/sys/dev/ichiic
Message-ID:  <201506250752.t5P7qpYD011567@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: grembo (ports committer)
Date: Thu Jun 25 07:52:51 2015
New Revision: 284803
URL: https://svnweb.freebsd.org/changeset/base/284803

Log:
  Protect smbus ioctls in ig4 driver using a shared lock.
  Document locking semantics.
  
  Differential Revision:	https://reviews.freebsd.org/D2744
  Reviewed by:	jah, kib
  Approved by:	kib

Modified:
  head/sys/dev/ichiic/ig4_iic.c
  head/sys/dev/ichiic/ig4_pci.c
  head/sys/dev/ichiic/ig4_var.h

Modified: head/sys/dev/ichiic/ig4_iic.c
==============================================================================
--- head/sys/dev/ichiic/ig4_iic.c	Thu Jun 25 07:25:40 2015	(r284802)
+++ head/sys/dev/ichiic/ig4_iic.c	Thu Jun 25 07:52:51 2015	(r284803)
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
  * Intel fourth generation mobile cpus integrated I2C device, smbus driver.
  *
  * See ig4_reg.h for datasheet reference and notes.
+ * See ig4_var.h for locking semantics.
  */
 
 #include <sys/param.h>
@@ -49,6 +50,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/errno.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
+#include <sys/sx.h>
 #include <sys/syslog.h>
 #include <sys/bus.h>
 #include <sys/sysctl.h>
@@ -115,7 +117,7 @@ set_controller(ig4iic_softc_t *sc, uint3
 			error = 0;
 			break;
 		}
-		mtx_sleep(sc, &sc->mutex, 0, "i2cslv", 1);
+		mtx_sleep(sc, &sc->io_lock, 0, "i2cslv", 1);
 	}
 	return (error);
 }
@@ -179,7 +181,7 @@ wait_status(ig4iic_softc_t *sc, uint32_t
 		 * work, otherwise poll with the lock held.
 		 */
 		if (status & IG4_STATUS_RX_NOTEMPTY) {
-			mtx_sleep(sc, &sc->mutex, PZERO, "i2cwait",
+			mtx_sleep(sc, &sc->io_lock, 0, "i2cwait",
 				  (hz + 99) / 100); /* sleep up to 10ms */
 			count_us += 10000;
 		} else {
@@ -522,6 +524,8 @@ ig4iic_attach(ig4iic_softc_t *sc)
 	 * Use a threshold of 1 so we get interrupted on each character,
 	 * allowing us to use mtx_sleep() in our poll code.  Not perfect
 	 * but this is better than using DELAY() for receiving data.
+	 *
+	 * See ig4_var.h for details on interrupt handler synchronization.
 	 */
 	reg_write(sc, IG4_REG_RX_TL, 1);
 
@@ -551,12 +555,12 @@ ig4iic_attach(ig4iic_softc_t *sc)
 	 */
 	reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET |
 					 IG4_INTR_RX_FULL);
-	mtx_lock(&sc->mutex);
+	mtx_lock(&sc->io_lock);
 	if (set_controller(sc, 0))
 		device_printf(sc->dev, "controller error during attach-1\n");
 	if (set_controller(sc, IG4_I2C_ENABLE))
 		device_printf(sc->dev, "controller error during attach-2\n");
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
 	error = bus_setup_intr(sc->dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE,
 			       NULL, ig4iic_intr, sc, &sc->intr_handle);
 	if (error) {
@@ -615,7 +619,8 @@ ig4iic_detach(ig4iic_softc_t *sc)
 	if (sc->intr_handle)
 		bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle);
 
-	mtx_lock(&sc->mutex);
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
 
 	sc->smb = NULL;
 	sc->intr_handle = NULL;
@@ -623,18 +628,16 @@ ig4iic_detach(ig4iic_softc_t *sc)
 	reg_read(sc, IG4_REG_CLR_INTR);
 	set_controller(sc, 0);
 
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
 	return (0);
 }
 
 int
 ig4iic_smb_callback(device_t dev, int index, void *data)
 {
-	ig4iic_softc_t *sc = device_get_softc(dev);
 	int error;
 
-	mtx_lock(&sc->mutex);
-
 	switch (index) {
 	case SMB_REQUEST_BUS:
 		error = 0;
@@ -647,8 +650,6 @@ ig4iic_smb_callback(device_t dev, int in
 		break;
 	}
 
-	mtx_unlock(&sc->mutex);
-
 	return (error);
 }
 
@@ -660,25 +661,8 @@ ig4iic_smb_callback(device_t dev, int in
 int
 ig4iic_smb_quick(device_t dev, u_char slave, int how)
 {
-	ig4iic_softc_t *sc = device_get_softc(dev);
-	int error;
-
-	mtx_lock(&sc->mutex);
 
-	switch (how) {
-	case SMB_QREAD:
-		error = SMB_ENOTSUPP;
-		break;
-	case SMB_QWRITE:
-		error = SMB_ENOTSUPP;
-		break;
-	default:
-		error = SMB_ENOTSUPP;
-		break;
-	}
-	mtx_unlock(&sc->mutex);
-
-	return (error);
+	return (SMB_ENOTSUPP);
 }
 
 /*
@@ -695,7 +679,8 @@ ig4iic_smb_sendb(device_t dev, u_char sl
 	uint32_t cmd;
 	int error;
 
-	mtx_lock(&sc->mutex);
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
 
 	set_slave_addr(sc, slave, 0);
 	cmd = byte;
@@ -706,7 +691,8 @@ ig4iic_smb_sendb(device_t dev, u_char sl
 		error = SMB_ETIMEOUT;
 	}
 
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
 	return (error);
 }
 
@@ -721,7 +707,8 @@ ig4iic_smb_recvb(device_t dev, u_char sl
 	ig4iic_softc_t *sc = device_get_softc(dev);
 	int error;
 
-	mtx_lock(&sc->mutex);
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
 
 	set_slave_addr(sc, slave, 0);
 	reg_write(sc, IG4_REG_DATA_CMD, IG4_DATA_COMMAND_RD);
@@ -733,7 +720,8 @@ ig4iic_smb_recvb(device_t dev, u_char sl
 		error = SMB_ETIMEOUT;
 	}
 
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
 	return (error);
 }
 
@@ -746,13 +734,15 @@ ig4iic_smb_writeb(device_t dev, u_char s
 	ig4iic_softc_t *sc = device_get_softc(dev);
 	int error;
 
-	mtx_lock(&sc->mutex);
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
 
 	set_slave_addr(sc, slave, 0);
 	error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
 				&byte, 1, NULL, 0, NULL);
 
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
 	return (error);
 }
 
@@ -766,7 +756,8 @@ ig4iic_smb_writew(device_t dev, u_char s
 	char buf[2];
 	int error;
 
-	mtx_lock(&sc->mutex);
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
 
 	set_slave_addr(sc, slave, 0);
 	buf[0] = word & 0xFF;
@@ -774,7 +765,8 @@ ig4iic_smb_writew(device_t dev, u_char s
 	error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
 				buf, 2, NULL, 0, NULL);
 
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
 	return (error);
 }
 
@@ -787,13 +779,15 @@ ig4iic_smb_readb(device_t dev, u_char sl
 	ig4iic_softc_t *sc = device_get_softc(dev);
 	int error;
 
-	mtx_lock(&sc->mutex);
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
 
 	set_slave_addr(sc, slave, 0);
 	error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
 				NULL, 0, byte, 1, NULL);
 
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
 	return (error);
 }
 
@@ -807,7 +801,8 @@ ig4iic_smb_readw(device_t dev, u_char sl
 	char buf[2];
 	int error;
 
-	mtx_lock(&sc->mutex);
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
 
 	set_slave_addr(sc, slave, 0);
 	if ((error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
@@ -815,7 +810,8 @@ ig4iic_smb_readw(device_t dev, u_char sl
 		*word = (u_char)buf[0] | ((u_char)buf[1] << 8);
 	}
 
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
 	return (error);
 }
 
@@ -831,7 +827,8 @@ ig4iic_smb_pcall(device_t dev, u_char sl
 	char wbuf[2];
 	int error;
 
-	mtx_lock(&sc->mutex);
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
 
 	set_slave_addr(sc, slave, 0);
 	wbuf[0] = sdata & 0xFF;
@@ -841,7 +838,8 @@ ig4iic_smb_pcall(device_t dev, u_char sl
 		*rdata = (u_char)rbuf[0] | ((u_char)rbuf[1] << 8);
 	}
 
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
 	return (error);
 }
 
@@ -852,13 +850,15 @@ ig4iic_smb_bwrite(device_t dev, u_char s
 	ig4iic_softc_t *sc = device_get_softc(dev);
 	int error;
 
-	mtx_lock(&sc->mutex);
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
 
 	set_slave_addr(sc, slave, 0);
 	error = smb_transaction(sc, cmd, 0,
 				buf, wcount, NULL, 0, NULL);
 
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
 	return (error);
 }
 
@@ -870,14 +870,16 @@ ig4iic_smb_bread(device_t dev, u_char sl
 	int rcount = *countp_char;
 	int error;
 
-	mtx_lock(&sc->mutex);
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
 
 	set_slave_addr(sc, slave, 0);
 	error = smb_transaction(sc, cmd, 0,
 				NULL, 0, buf, rcount, &rcount);
 	*countp_char = rcount;
 
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
 	return (error);
 }
 
@@ -889,18 +891,20 @@ ig4iic_smb_trans(device_t dev, int slave
 	ig4iic_softc_t *sc = device_get_softc(dev);
 	int error;
 
-	mtx_lock(&sc->mutex);
+	sx_xlock(&sc->call_lock);
+	mtx_lock(&sc->io_lock);
 
 	set_slave_addr(sc, slave, op);
 	error = smb_transaction(sc, cmd, op,
 				wbuf, wcount, rbuf, rcount, actualp);
 
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
+	sx_xunlock(&sc->call_lock);
 	return (error);
 }
 
 /*
- * Interrupt Operation
+ * Interrupt Operation, see ig4_var.h for locking semantics.
  */
 static void
 ig4iic_intr(void *cookie)
@@ -908,7 +912,7 @@ ig4iic_intr(void *cookie)
 	ig4iic_softc_t *sc = cookie;
 	uint32_t status;
 
-	mtx_lock(&sc->mutex);
+	mtx_lock(&sc->io_lock);
 /*	reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET);*/
 	status = reg_read(sc, IG4_REG_I2C_STA);
 	while (status & IG4_STATUS_RX_NOTEMPTY) {
@@ -919,7 +923,7 @@ ig4iic_intr(void *cookie)
 	}
 	reg_read(sc, IG4_REG_CLR_INTR);
 	wakeup(sc);
-	mtx_unlock(&sc->mutex);
+	mtx_unlock(&sc->io_lock);
 }
 
 #define REGDUMP(sc, reg)	\

Modified: head/sys/dev/ichiic/ig4_pci.c
==============================================================================
--- head/sys/dev/ichiic/ig4_pci.c	Thu Jun 25 07:25:40 2015	(r284802)
+++ head/sys/dev/ichiic/ig4_pci.c	Thu Jun 25 07:52:51 2015	(r284803)
@@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/errno.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
+#include <sys/sx.h>
 #include <sys/syslog.h>
 #include <sys/bus.h>
 
@@ -94,7 +95,8 @@ ig4iic_pci_attach(device_t dev)
 
 	bzero(sc, sizeof(*sc));
 
-	mtx_init(&sc->mutex, device_get_nameunit(dev), "ig4iic", MTX_DEF);
+	mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF);
+	sx_init(&sc->call_lock, "IG4 call lock");
 
 	sc->dev = dev;
 	sc->regs_rid = PCIR_BAR(0);
@@ -150,7 +152,10 @@ ig4iic_pci_detach(device_t dev)
 				     sc->regs_rid, sc->regs_res);
 		sc->regs_res = NULL;
 	}
-	mtx_destroy(&sc->mutex);
+	if (mtx_initialized(&sc->io_lock)) {
+		mtx_destroy(&sc->io_lock);
+		sx_destroy(&sc->call_lock);
+	}
 
 	return (0);
 }
@@ -179,9 +184,9 @@ static device_method_t ig4iic_pci_method
 };
 
 static driver_t ig4iic_pci_driver = {
-        "ig4iic",
-        ig4iic_pci_methods,
-        sizeof(struct ig4iic_softc)
+	"ig4iic",
+	ig4iic_pci_methods,
+	sizeof(struct ig4iic_softc)
 };
 
 static devclass_t ig4iic_pci_devclass;

Modified: head/sys/dev/ichiic/ig4_var.h
==============================================================================
--- head/sys/dev/ichiic/ig4_var.h	Thu Jun 25 07:25:40 2015	(r284802)
+++ head/sys/dev/ichiic/ig4_var.h	Thu Jun 25 07:52:51 2015	(r284803)
@@ -70,7 +70,30 @@ struct ig4iic_softc {
 	int		slave_valid : 1;
 	int		read_started : 1;
 	int		write_started : 1;
-	struct mtx	mutex;
+
+	/*
+	 * Locking semantics:
+	 *
+	 * Functions implementing the smbus interface that interact
+	 * with the controller acquire an exclusive lock on call_lock
+	 * to prevent interleaving of calls to the interface and a lock on
+	 * io_lock right afterwards, to synchronize controller I/O activity.
+	 * 
+	 * The interrupt handler can only read data while no ig4iic_smb_* call
+	 * is in progress or while io_lock is dropped during mtx_sleep in
+	 * wait_status and set_controller. It is safe to drop io_lock in those
+	 * places, because the interrupt handler only accesses those registers:
+	 *
+	 * - IG4_REG_I2C_STA  (I2C Status)
+	 * - IG4_REG_DATA_CMD (Data Buffer and Command)
+	 * - IG4_REG_CLR_INTR (Clear Interrupt)
+	 *
+	 * Locking outside of those places is required to make the content
+	 * of rpos/rnext predictable (e.g. whenever data_read is called and in
+	 * smb_transaction).
+	 */
+	struct sx	call_lock;
+	struct mtx	io_lock;
 };
 
 typedef struct ig4iic_softc ig4iic_softc_t;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201506250752.t5P7qpYD011567>