Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/i2c ihidev(4): Fix locking and interrupt handler.
details: https://anonhg.NetBSD.org/src/rev/67144db8599f
branches: trunk
changeset: 359621:67144db8599f
user: riastradh <riastradh%NetBSD.org@localhost>
date: Fri Jan 14 22:25:49 2022 +0000
description:
ihidev(4): Fix locking and interrupt handler.
- Can't run iic_exec in softint because it does cv_wait, at least on
some i2c controllers -- defer to workqueue instead.
- Fix violations of locking rules:
. Do not take a lock at higher IPL than it is defined at!
. Do not sleep under a lock!
. Definitely do not sleep under a spin lock!
In this case, sc_intr_lock was defined at IPL_VM but used at IPL_TTY,
and i2c transactions -- possibly causing sleep for cv_wait -- were
issued under it.
But in this case, the interrupt handler needs only a single bit to
mark whether the work is pending, so just use atomic_swap for that.
- Use an adaptive lock (IPL_NONE) for i2c transactions.
- Detach children, and do so before freeing anything.
diffstat:
sys/dev/i2c/ihidev.c | 94 ++++++++++++++++++++++++++++-----------------------
sys/dev/i2c/ihidev.h | 9 +++-
2 files changed, 57 insertions(+), 46 deletions(-)
diffs (299 lines):
diff -r a81c5dfc920d -r 67144db8599f sys/dev/i2c/ihidev.c
--- a/sys/dev/i2c/ihidev.c Fri Jan 14 21:59:50 2022 +0000
+++ b/sys/dev/i2c/ihidev.c Fri Jan 14 22:25:49 2022 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: ihidev.c,v 1.20 2021/08/07 16:19:11 thorpej Exp $ */
+/* $NetBSD: ihidev.c,v 1.21 2022/01/14 22:25:49 riastradh Exp $ */
/* $OpenBSD ihidev.c,v 1.13 2017/04/08 02:57:23 deraadt Exp $ */
/*-
@@ -54,13 +54,13 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ihidev.c,v 1.20 2021/08/07 16:19:11 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ihidev.c,v 1.21 2022/01/14 22:25:49 riastradh Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kmem.h>
-
+#include <sys/workqueue.h>
#include <dev/i2c/i2cvar.h>
#include <dev/i2c/ihidev.h>
@@ -110,14 +110,14 @@
CFATTACH_DECL_NEW(ihidev, sizeof(struct ihidev_softc),
ihidev_match, ihidev_attach, ihidev_detach, NULL);
-static bool ihiddev_intr_init(struct ihidev_softc *);
-static void ihiddev_intr_fini(struct ihidev_softc *);
+static bool ihidev_intr_init(struct ihidev_softc *);
+static void ihidev_intr_fini(struct ihidev_softc *);
static bool ihidev_suspend(device_t, const pmf_qual_t *);
static bool ihidev_resume(device_t, const pmf_qual_t *);
static int ihidev_hid_command(struct ihidev_softc *, int, void *, bool);
static int ihidev_intr(void *);
-static void ihidev_softintr(void *);
+static void ihidev_work(struct work *, void *);
static int ihidev_reset(struct ihidev_softc *, bool);
static int ihidev_hid_desc_parse(struct ihidev_softc *);
@@ -160,14 +160,14 @@
sc->sc_dev = self;
sc->sc_tag = ia->ia_tag;
sc->sc_addr = ia->ia_addr;
- mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM);
+ mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
sc->sc_phandle = ia->ia_cookie;
if (ia->ia_cookietype != I2C_COOKIE_ACPI) {
aprint_error(": unsupported device tree type\n");
return;
}
- if (! ihidev_acpi_get_info(sc)) {
+ if (!ihidev_acpi_get_info(sc)) {
return;
}
@@ -208,7 +208,7 @@
repsz));
}
sc->sc_ibuf = kmem_zalloc(sc->sc_isize, KM_SLEEP);
- if (! ihiddev_intr_init(sc)) {
+ if (!ihidev_intr_init(sc)) {
return;
}
@@ -245,7 +245,8 @@
}
/* power down until we're opened */
- if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF, false)) {
+ if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF,
+ false)) {
aprint_error_dev(sc->sc_dev, "failed to power down\n");
return;
}
@@ -257,13 +258,19 @@
ihidev_detach(device_t self, int flags)
{
struct ihidev_softc *sc = device_private(self);
+ int error;
- mutex_enter(&sc->sc_intr_lock);
- ihiddev_intr_fini(sc);
- if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER,
- &I2C_HID_POWER_OFF, true))
- aprint_error_dev(sc->sc_dev, "failed to power down\n");
- mutex_exit(&sc->sc_intr_lock);
+ error = config_detach_children(self, flags);
+ if (error)
+ return error;
+
+ pmf_device_deregister(self);
+ ihidev_intr_fini(sc);
+
+ if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF,
+ true))
+ aprint_error_dev(sc->sc_dev, "failed to power down\n");
+
if (sc->sc_ibuf != NULL) {
kmem_free(sc->sc_ibuf, sc->sc_isize);
sc->sc_ibuf = NULL;
@@ -272,8 +279,9 @@
if (sc->sc_report != NULL)
kmem_free(sc->sc_report, sc->sc_reportlen);
- pmf_device_deregister(self);
- return (0);
+ mutex_destroy(&sc->sc_lock);
+
+ return 0;
}
static bool
@@ -281,14 +289,14 @@
{
struct ihidev_softc *sc = device_private(self);
- mutex_enter(&sc->sc_intr_lock);
+ mutex_enter(&sc->sc_lock);
if (sc->sc_refcnt > 0) {
printf("ihidev power off\n");
if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER,
&I2C_HID_POWER_OFF, true))
aprint_error_dev(sc->sc_dev, "failed to power down\n");
}
- mutex_exit(&sc->sc_intr_lock);
+ mutex_exit(&sc->sc_lock);
return true;
}
@@ -297,12 +305,12 @@
{
struct ihidev_softc *sc = device_private(self);
- mutex_enter(&sc->sc_intr_lock);
+ mutex_enter(&sc->sc_lock);
if (sc->sc_refcnt > 0) {
printf("ihidev power reset\n");
ihidev_reset(sc, true);
}
- mutex_exit(&sc->sc_intr_lock);
+ mutex_exit(&sc->sc_lock);
return true;
}
@@ -646,7 +654,7 @@
}
static bool
-ihiddev_intr_init(struct ihidev_softc *sc)
+ihidev_intr_init(struct ihidev_softc *sc)
{
#if NACPICA > 0
ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle;
@@ -682,12 +690,13 @@
aprint_normal_dev(sc->sc_dev, "interrupting at %s\n",
acpi_intr_string(sc->sc_ih, buf, sizeof(buf)));
- sc->sc_sih = softint_establish(SOFTINT_SERIAL, ihidev_softintr, sc);
- if (sc->sc_sih == NULL) {
+ if (workqueue_create(&sc->sc_wq, device_xname(sc->sc_dev), ihidev_work,
+ sc, PRI_NONE, IPL_TTY, WQ_MPSAFE)) {
aprint_error_dev(sc->sc_dev,
- "can't establish soft interrupt\n");
+ "can't establish workqueue\n");
return false;
}
+ sc->sc_work_pending = 0;
return true;
#else
@@ -697,14 +706,14 @@
}
static void
-ihiddev_intr_fini(struct ihidev_softc *sc)
+ihidev_intr_fini(struct ihidev_softc *sc)
{
#if NACPICA > 0
if (sc->sc_ih != NULL) {
acpi_intr_disestablish(sc->sc_ih);
}
- if (sc->sc_sih != NULL) {
- softint_disestablish(sc->sc_sih);
+ if (sc->sc_wq != NULL) {
+ workqueue_destroy(sc->sc_wq);
}
#endif
}
@@ -736,23 +745,19 @@
{
struct ihidev_softc * const sc = arg;
- mutex_enter(&sc->sc_intr_lock);
-
/*
- * Schedule our soft interrupt handler. If we're using a level-
- * triggered interrupt, we have to mask it off while we wait
- * for service.
+ * Schedule our work. If we're using a level-triggered
+ * interrupt, we have to mask it off while we wait for service.
*/
- softint_schedule(sc->sc_sih);
+ if (atomic_swap_uint(&sc->sc_work_pending, 1) == 0)
+ workqueue_enqueue(sc->sc_wq, &sc->sc_work, NULL);
ihidev_intr_mask(sc);
- mutex_exit(&sc->sc_intr_lock);
-
return 1;
}
static void
-ihidev_softintr(void *arg)
+ihidev_work(struct work *wk, void *arg)
{
struct ihidev_softc * const sc = arg;
struct ihidev *scd;
@@ -761,13 +766,14 @@
u_char *p;
u_int rep = 0;
- mutex_enter(&sc->sc_intr_lock);
+ atomic_store_relaxed(&sc->sc_work_pending, 0);
+
+ mutex_enter(&sc->sc_lock);
+
iic_acquire_bus(sc->sc_tag, 0);
res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0,
sc->sc_ibuf, sc->sc_isize, 0);
iic_release_bus(sc->sc_tag, 0);
- mutex_exit(&sc->sc_intr_lock);
-
if (res != 0)
goto out;
@@ -807,6 +813,8 @@
scd->sc_intr(scd, p, psize);
out:
+ mutex_exit(&sc->sc_lock);
+
/*
* If our interrupt is level-triggered, re-enable it now.
*/
@@ -837,7 +845,7 @@
if (iha->reportid == IHIDEV_CLAIM_ALLREPORTID)
return (QUIET);
-
+
if (pnp)
aprint_normal("hid at %s", pnp);
@@ -974,7 +982,7 @@
static bool
ihidev_acpi_get_info(struct ihidev_softc *sc)
-{
+{
ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle;
ACPI_STATUS status;
ACPI_INTEGER val;
diff -r a81c5dfc920d -r 67144db8599f sys/dev/i2c/ihidev.h
--- a/sys/dev/i2c/ihidev.h Fri Jan 14 21:59:50 2022 +0000
+++ b/sys/dev/i2c/ihidev.h Fri Jan 14 22:25:49 2022 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: ihidev.h,v 1.5 2022/01/14 21:32:27 riastradh Exp $ */
+/* $NetBSD: ihidev.h,v 1.6 2022/01/14 22:25:49 riastradh Exp $ */
/* $OpenBSD ihidev.h,v 1.4 2016/01/31 18:24:35 jcs Exp $ */
/*-
@@ -106,6 +106,7 @@
#include <sys/device.h>
#include <sys/mutex.h>
+#include <sys/workqueue.h>
#include <dev/i2c/i2cvar.h>
@@ -114,10 +115,12 @@
i2c_tag_t sc_tag;
i2c_addr_t sc_addr;
uint64_t sc_phandle;
+ kmutex_t sc_lock;
void * sc_ih;
- void * sc_sih;
- kmutex_t sc_intr_lock;
+ struct workqueue *sc_wq;
+ struct work sc_work;
+ volatile unsigned sc_work_pending;
int sc_intr_type;
u_int sc_hid_desc_addr;
Home |
Main Index |
Thread Index |
Old Index