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); }