Module Name: src
Committed By: thorpej
Date: Tue Dec 24 06:27:17 UTC 2019
Modified Files:
src/sys/dev/pci: ichsmb.c piixpm.c
Log Message:
Make ichsmb and piixpm MP-safe:
- Synchronize with the interrupt handler using a mutex.
- Use a condvar to wait for completion, rather than tsleep().
- Mark our interrupt handler as such.
Also, other general correctness fixes:
- Loop around testing the completion condition to protect aginst
spurious wakes.
- The "i2c exec" function returns an error code, so actually do so.
To generate a diff of this commit:
cvs rdiff -u -r1.64 -r1.65 src/sys/dev/pci/ichsmb.c
cvs rdiff -u -r1.59 -r1.60 src/sys/dev/pci/piixpm.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/pci/ichsmb.c
diff -u src/sys/dev/pci/ichsmb.c:1.64 src/sys/dev/pci/ichsmb.c:1.65
--- src/sys/dev/pci/ichsmb.c:1.64 Mon Dec 23 15:34:40 2019
+++ src/sys/dev/pci/ichsmb.c Tue Dec 24 06:27:17 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: ichsmb.c,v 1.64 2019/12/23 15:34:40 thorpej Exp $ */
+/* $NetBSD: ichsmb.c,v 1.65 2019/12/24 06:27:17 thorpej Exp $ */
/* $OpenBSD: ichiic.c,v 1.18 2007/05/03 09:36:26 dlg Exp $ */
/*
@@ -22,13 +22,14 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ichsmb.c,v 1.64 2019/12/23 15:34:40 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ichsmb.c,v 1.65 2019/12/24 06:27:17 thorpej Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/mutex.h>
+#include <sys/condvar.h>
#include <sys/proc.h>
#include <sys/module.h>
@@ -62,13 +63,17 @@ struct ichsmb_softc {
int sc_poll;
pci_intr_handle_t *sc_pihp;
+ kmutex_t sc_exec_lock;
+ kcondvar_t sc_exec_wait;
+
struct i2c_controller sc_i2c_tag;
struct {
i2c_op_t op;
void * buf;
size_t len;
int flags;
- volatile int error;
+ int error;
+ bool done;
} sc_i2c_xfer;
device_t sc_i2c_device;
};
@@ -161,6 +166,9 @@ ichsmb_attach(device_t parent, device_t
pci_aprint_devinfo(pa, NULL);
+ mutex_init(&sc->sc_exec_lock, MUTEX_DEFAULT, IPL_BIO);
+ cv_init(&sc->sc_exec_wait, device_xname(self));
+
/* Read configuration */
conf = pci_conf_read(pa->pa_pc, pa->pa_tag, LPCIB_SMB_HOSTC);
DPRINTF(("%s: conf 0x%08x\n", device_xname(sc->sc_dev), conf));
@@ -187,6 +195,8 @@ ichsmb_attach(device_t parent, device_t
if (pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0) == 0) {
intrstr = pci_intr_string(pa->pa_pc, sc->sc_pihp[0],
intrbuf, sizeof(intrbuf));
+ pci_intr_setattr(pa->pa_pc, &sc->sc_pihp[0],
+ PCI_INTR_MPSAFE, true);
sc->sc_ih = pci_intr_establish_xname(pa->pa_pc,
sc->sc_pihp[0], IPL_BIO, ichsmb_intr, sc,
device_xname(sc->sc_dev));
@@ -262,6 +272,9 @@ ichsmb_detach(device_t self, int flags)
if (sc->sc_size != 0)
bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
+ mutex_destroy(&sc->sc_exec_lock);
+ cv_destroy(&sc->sc_exec_wait);
+
return 0;
}
@@ -288,6 +301,8 @@ ichsmb_i2c_exec(void *cookie, i2c_op_t o
"flags 0x%02x\n", device_xname(sc->sc_dev), op, addr, cmdlen,
len, flags));
+ mutex_enter(&sc->sc_exec_lock);
+
/* Clear status bits */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HS,
LPCIB_SMB_HS_INTR | LPCIB_SMB_HS_DEVERR |
@@ -306,15 +321,19 @@ ichsmb_i2c_exec(void *cookie, i2c_op_t o
snprintb(fbuf, sizeof(fbuf), LPCIB_SMB_HS_BITS, st);
printf("%s: exec: st %s\n", device_xname(sc->sc_dev), fbuf);
#endif
- if (st & LPCIB_SMB_HS_BUSY)
- return (1);
+ if (st & LPCIB_SMB_HS_BUSY) {
+ mutex_exit(&sc->sc_exec_lock);
+ return (EBUSY);
+ }
if (sc->sc_poll)
flags |= I2C_F_POLL;
if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2 ||
- (cmdlen == 0 && len > 1))
- return (1);
+ (cmdlen == 0 && len > 1)) {
+ mutex_exit(&sc->sc_exec_lock);
+ return (EINVAL);
+ }
/* Setup transfer */
sc->sc_i2c_xfer.op = op;
@@ -322,6 +341,7 @@ ichsmb_i2c_exec(void *cookie, i2c_op_t o
sc->sc_i2c_xfer.len = len;
sc->sc_i2c_xfer.flags = flags;
sc->sc_i2c_xfer.error = 0;
+ sc->sc_i2c_xfer.done = false;
/* Set slave address and transfer direction */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_TXSLVA,
@@ -380,14 +400,17 @@ ichsmb_i2c_exec(void *cookie, i2c_op_t o
ichsmb_intr(sc);
} else {
/* Wait for interrupt */
- if (tsleep(sc, PRIBIO, "iicexec", ICHIIC_TIMEOUT * hz))
- goto timeout;
+ while (! sc->sc_i2c_xfer.done) {
+ if (cv_timedwait(&sc->sc_exec_wait, &sc->sc_exec_lock,
+ ICHIIC_TIMEOUT * hz))
+ goto timeout;
+ }
}
- if (sc->sc_i2c_xfer.error)
- return (1);
+ int error = sc->sc_i2c_xfer.error;
+ mutex_exit(&sc->sc_exec_lock);
- return (0);
+ return (error);
timeout:
/*
@@ -408,7 +431,8 @@ timeout:
fbuf);
}
bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HS, st);
- return (1);
+ mutex_exit(&sc->sc_exec_lock);
+ return (ETIMEDOUT);
}
static int
@@ -435,12 +459,15 @@ ichsmb_intr(void *arg)
printf("%s: intr st %s\n", device_xname(sc->sc_dev), fbuf);
#endif
+ if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
+ mutex_enter(&sc->sc_exec_lock);
+
/* Clear status bits */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, LPCIB_SMB_HS, st);
/* Check for errors */
if (st & (LPCIB_SMB_HS_DEVERR | LPCIB_SMB_HS_BUSERR | LPCIB_SMB_HS_FAILED)) {
- sc->sc_i2c_xfer.error = 1;
+ sc->sc_i2c_xfer.error = EIO;
goto done;
}
@@ -460,8 +487,11 @@ ichsmb_intr(void *arg)
}
done:
- if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
- wakeup(sc);
+ sc->sc_i2c_xfer.done = true;
+ if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0) {
+ cv_signal(&sc->sc_exec_wait);
+ mutex_exit(&sc->sc_exec_lock);
+ }
return (1);
}
Index: src/sys/dev/pci/piixpm.c
diff -u src/sys/dev/pci/piixpm.c:1.59 src/sys/dev/pci/piixpm.c:1.60
--- src/sys/dev/pci/piixpm.c:1.59 Tue Dec 24 03:43:34 2019
+++ src/sys/dev/pci/piixpm.c Tue Dec 24 06:27:17 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: piixpm.c,v 1.59 2019/12/24 03:43:34 msaitoh Exp $ */
+/* $NetBSD: piixpm.c,v 1.60 2019/12/24 06:27:17 thorpej Exp $ */
/* $OpenBSD: piixpm.c,v 1.39 2013/10/01 20:06:02 sf Exp $ */
/*
@@ -22,13 +22,14 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: piixpm.c,v 1.59 2019/12/24 03:43:34 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: piixpm.c,v 1.60 2019/12/24 06:27:17 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/mutex.h>
+#include <sys/condvar.h>
#include <sys/proc.h>
#include <sys/bus.h>
@@ -98,12 +99,16 @@ struct piixpm_softc {
struct piixpm_smbus sc_busses[4];
struct i2c_controller sc_i2c_tags[4];
+ kmutex_t sc_exec_lock;
+ kcondvar_t sc_exec_wait;
+
struct {
i2c_op_t op;
void * buf;
size_t len;
int flags;
- volatile int error;
+ int error;
+ bool done;
} sc_i2c_xfer;
pcireg_t sc_devact[2];
@@ -197,6 +202,9 @@ piixpm_attach(device_t parent, device_t
pci_aprint_devinfo(pa, NULL);
+ mutex_init(&sc->sc_exec_lock, MUTEX_DEFAULT, IPL_BIO);
+ cv_init(&sc->sc_exec_wait, device_xname(self));
+
if (!pmf_device_register(self, piixpm_suspend, piixpm_resume))
aprint_error_dev(self, "couldn't establish power handler\n");
@@ -272,6 +280,8 @@ setintr:
if (pci_intr_map(pa, &ih) == 0) {
intrstr = pci_intr_string(pa->pa_pc, ih,
intrbuf, sizeof(intrbuf));
+ pci_intr_setattr(pa->pa_pc, &ih,
+ PCI_INTR_MPSAFE, true);
sc->sc_smb_ih = pci_intr_establish_xname(
pa->pa_pc, ih, IPL_BIO, piixpm_intr,
sc, device_xname(sc->sc_dev));
@@ -557,6 +567,8 @@ piixpm_i2c_exec(void *cookie, i2c_op_t o
"flags 0x%x\n",
device_xname(sc->sc_dev), op, addr, cmdlen, len, flags));
+ mutex_enter(&sc->sc_exec_lock);
+
/* Clear status bits */
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS,
PIIX_SMB_HS_INTR | PIIX_SMB_HS_DEVERR |
@@ -573,15 +585,19 @@ piixpm_i2c_exec(void *cookie, i2c_op_t o
DELAY(PIIXPM_DELAY);
}
DPRINTF(("%s: exec: st %#x\n", device_xname(sc->sc_dev), st & 0xff));
- if (st & PIIX_SMB_HS_BUSY)
- return (1);
+ if (st & PIIX_SMB_HS_BUSY) {
+ mutex_exit(&sc->sc_exec_lock);
+ return (EBUSY);
+ }
if (sc->sc_poll)
flags |= I2C_F_POLL;
if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2 ||
- (cmdlen == 0 && len > 1))
- return (1);
+ (cmdlen == 0 && len > 1)) {
+ mutex_exit(&sc->sc_exec_lock);
+ return (EINVAL);
+ }
/* Setup transfer */
sc->sc_i2c_xfer.op = op;
@@ -589,6 +605,7 @@ piixpm_i2c_exec(void *cookie, i2c_op_t o
sc->sc_i2c_xfer.len = len;
sc->sc_i2c_xfer.flags = flags;
sc->sc_i2c_xfer.error = 0;
+ sc->sc_i2c_xfer.done = false;
/* Set slave address and transfer direction */
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_TXSLVA,
@@ -653,14 +670,17 @@ piixpm_i2c_exec(void *cookie, i2c_op_t o
piixpm_intr(sc);
} else {
/* Wait for interrupt */
- if (tsleep(sc, PRIBIO, "piixpm", PIIXPM_TIMEOUT * hz))
- goto timeout;
+ while (! sc->sc_i2c_xfer.done) {
+ if (cv_timedwait(&sc->sc_exec_wait, &sc->sc_exec_lock,
+ PIIXPM_TIMEOUT * hz))
+ goto timeout;
+ }
}
- if (sc->sc_i2c_xfer.error)
- return (1);
+ int error = sc->sc_i2c_xfer.error;
+ mutex_exit(&sc->sc_exec_lock);
- return (0);
+ return (error);
timeout:
/*
@@ -680,7 +700,8 @@ timeout:
*/
if (PIIXPM_IS_CSB5(sc))
piixpm_csb5_reset(sc);
- return (1);
+ mutex_exit(&sc->sc_exec_lock);
+ return (ETIMEDOUT);
}
static int
@@ -701,13 +722,16 @@ piixpm_intr(void *arg)
DPRINTF(("%s: intr st %#x\n", device_xname(sc->sc_dev), st & 0xff));
+ if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
+ mutex_enter(&sc->sc_exec_lock);
+
/* Clear status bits */
bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS, st);
/* Check for errors */
if (st & (PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR |
PIIX_SMB_HS_FAILED)) {
- sc->sc_i2c_xfer.error = 1;
+ sc->sc_i2c_xfer.error = EIO;
goto done;
}
@@ -727,7 +751,10 @@ piixpm_intr(void *arg)
}
done:
- if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
- wakeup(sc);
+ sc->sc_i2c_xfer.done = true;
+ if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0) {
+ cv_signal(&sc->sc_exec_wait);
+ mutex_exit(&sc->sc_exec_lock);
+ }
return (1);
}