Module Name: src Committed By: martin Date: Fri Oct 25 16:25:14 UTC 2019
Modified Files: src/sys/dev/onewire: onewire.c onewirereg.h owtemp.c Log Message: PR kern/54617: onewire(4): - Alter locking strategy to avoid deadlock on detach. - Auto bus probe chews CPU. Increase interval from 3s to 10s. - Put temp sensor S/N in dev description so it can be identified. - Use mutex/condvar. Patch from Andrew Doran. To generate a diff of this commit: cvs rdiff -u -r1.16 -r1.17 src/sys/dev/onewire/onewire.c cvs rdiff -u -r1.3 -r1.4 src/sys/dev/onewire/onewirereg.h cvs rdiff -u -r1.17 -r1.18 src/sys/dev/onewire/owtemp.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/onewire/onewire.c diff -u src/sys/dev/onewire/onewire.c:1.16 src/sys/dev/onewire/onewire.c:1.17 --- src/sys/dev/onewire/onewire.c:1.16 Fri Jul 25 08:10:38 2014 +++ src/sys/dev/onewire/onewire.c Fri Oct 25 16:25:14 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: onewire.c,v 1.16 2014/07/25 08:10:38 dholland Exp $ */ +/* $NetBSD: onewire.c,v 1.17 2019/10/25 16:25:14 martin Exp $ */ /* $OpenBSD: onewire.c,v 1.1 2006/03/04 16:27:03 grange Exp $ */ /* @@ -18,7 +18,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: onewire.c,v 1.16 2014/07/25 08:10:38 dholland Exp $"); +__KERNEL_RCSID(0, "$NetBSD: onewire.c,v 1.17 2019/10/25 16:25:14 martin Exp $"); /* * 1-Wire bus driver. @@ -30,8 +30,7 @@ __KERNEL_RCSID(0, "$NetBSD: onewire.c,v #include <sys/device.h> #include <sys/kernel.h> #include <sys/kthread.h> -#include <sys/rwlock.h> -#include <sys/malloc.h> +#include <sys/kmem.h> #include <sys/proc.h> #include <sys/queue.h> #include <sys/module.h> @@ -45,18 +44,16 @@ __KERNEL_RCSID(0, "$NetBSD: onewire.c,v #define DPRINTF(x) #endif -//#define ONEWIRE_MAXDEVS 256 -#define ONEWIRE_MAXDEVS 8 -#define ONEWIRE_SCANTIME 3 +int onewire_maxdevs = 8; +int onewire_scantime = 10; /* was 3 seconds - too often */ struct onewire_softc { device_t sc_dev; - struct onewire_bus * sc_bus; - krwlock_t sc_rwlock; + kmutex_t sc_lock; + kcondvar_t sc_scancv; struct lwp * sc_thread; TAILQ_HEAD(, onewire_device) sc_devs; - int sc_dying; }; @@ -64,7 +61,7 @@ struct onewire_device { TAILQ_ENTRY(onewire_device) d_list; device_t d_dev; u_int64_t d_rom; - int d_present; + bool d_present; }; static int onewire_match(device_t, cfdata_t, void *); @@ -79,21 +76,6 @@ static void onewire_scan(struct onewire_ CFATTACH_DECL_NEW(onewire, sizeof(struct onewire_softc), onewire_match, onewire_attach, onewire_detach, onewire_activate); -const struct cdevsw onewire_cdevsw = { - .d_open = noopen, - .d_close = noclose, - .d_read = noread, - .d_write = nowrite, - .d_ioctl = noioctl, - .d_stop = nostop, - .d_tty = notty, - .d_poll = nopoll, - .d_mmap = nommap, - .d_kqfilter = nokqfilter, - .d_discard = nodiscard, - .d_flag = D_OTHER -}; - extern struct cfdriver onewire_cd; static int @@ -110,14 +92,19 @@ onewire_attach(device_t parent, device_t sc->sc_dev = self; sc->sc_bus = oba->oba_bus; - rw_init(&sc->sc_rwlock); + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); + cv_init(&sc->sc_scancv, "owscan"); TAILQ_INIT(&sc->sc_devs); aprint_normal("\n"); - if (kthread_create(PRI_NONE, 0, NULL, onewire_thread, sc, - &sc->sc_thread, "%s", device_xname(self)) != 0) + if (kthread_create(PRI_NONE, KTHREAD_MUSTJOIN | KTHREAD_MPSAFE, NULL, + onewire_thread, sc, &sc->sc_thread, "%s", device_xname(self)) != 0) { aprint_error_dev(self, "can't create kernel thread\n"); + /* Normally the kthread destroys these. */ + mutex_destroy(&sc->sc_lock); + cv_destroy(&sc->sc_scancv); + } } static int @@ -126,17 +113,17 @@ onewire_detach(device_t self, int flags) struct onewire_softc *sc = device_private(self); int rv; - sc->sc_dying = 1; if (sc->sc_thread != NULL) { - wakeup(sc->sc_thread); - tsleep(&sc->sc_dying, PWAIT, "owdt", 0); + mutex_enter(&sc->sc_lock); + sc->sc_dying = 1; + cv_broadcast(&sc->sc_scancv); + mutex_exit(&sc->sc_lock); + /* Must no longer touch sc_lock nor sc_scancv. */ + kthread_join(sc->sc_thread); } - onewire_lock(sc); //rv = config_detach_children(self, flags); rv = 0; /* XXX riz */ - onewire_unlock(sc); - rw_destroy(&sc->sc_rwlock); return rv; } @@ -170,7 +157,7 @@ onewire_print(void *aux, const char *pnp (uint)ONEWIRE_ROM_FAMILY_TYPE(oa->oa_rom)); else aprint_normal("\"%s\"", famname); - aprint_normal(" sn %012llx", ONEWIRE_ROM_SN(oa->oa_rom)); + aprint_normal(" sn %012" PRIx64, ONEWIRE_ROM_SN(oa->oa_rom)); if (pnp != NULL) aprint_normal(" at %s", pnp); @@ -192,7 +179,7 @@ onewire_lock(void *arg) { struct onewire_softc *sc = arg; - rw_enter(&sc->sc_rwlock, RW_WRITER); + mutex_enter(&sc->sc_lock); } void @@ -200,7 +187,7 @@ onewire_unlock(void *arg) { struct onewire_softc *sc = arg; - rw_exit(&sc->sc_rwlock); + mutex_exit(&sc->sc_lock); } int @@ -209,6 +196,8 @@ onewire_reset(void *arg) struct onewire_softc *sc = arg; struct onewire_bus *bus = sc->sc_bus; + KASSERT(mutex_owned(&sc->sc_lock)); + return bus->bus_reset(bus->bus_cookie); } @@ -218,6 +207,8 @@ onewire_bit(void *arg, int value) struct onewire_softc *sc = arg; struct onewire_bus *bus = sc->sc_bus; + KASSERT(mutex_owned(&sc->sc_lock)); + return bus->bus_bit(bus->bus_cookie, value); } @@ -229,6 +220,8 @@ onewire_read_byte(void *arg) uint8_t value = 0; int i; + KASSERT(mutex_owned(&sc->sc_lock)); + if (bus->bus_read_byte != NULL) return bus->bus_read_byte(bus->bus_cookie); @@ -245,6 +238,8 @@ onewire_write_byte(void *arg, int value) struct onewire_bus *bus = sc->sc_bus; int i; + KASSERT(mutex_owned(&sc->sc_lock)); + if (bus->bus_write_byte != NULL) return bus->bus_write_byte(bus->bus_cookie, value); @@ -259,6 +254,8 @@ onewire_triplet(void *arg, int dir) struct onewire_bus *bus = sc->sc_bus; int rv; + KASSERT(mutex_owned(&sc->sc_lock)); + if (bus->bus_triplet != NULL) return bus->bus_triplet(bus->bus_cookie, dir); @@ -283,29 +280,38 @@ onewire_triplet(void *arg, int dir) void onewire_read_block(void *arg, void *buf, int len) { + struct onewire_softc *sc = arg; uint8_t *p = buf; + KASSERT(mutex_owned(&sc->sc_lock)); + while (len--) - *p++ = onewire_read_byte(arg); + *p++ = onewire_read_byte(sc); } void onewire_write_block(void *arg, const void *buf, int len) { + struct onewire_softc *sc = arg; const uint8_t *p = buf; + KASSERT(mutex_owned(&sc->sc_lock)); + while (len--) - onewire_write_byte(arg, *p++); + onewire_write_byte(sc, *p++); } void onewire_matchrom(void *arg, u_int64_t rom) { + struct onewire_softc *sc = arg; int i; - onewire_write_byte(arg, ONEWIRE_CMD_MATCH_ROM); + KASSERT(mutex_owned(&sc->sc_lock)); + + onewire_write_byte(sc, ONEWIRE_CMD_MATCH_ROM); for (i = 0; i < 8; i++) - onewire_write_byte(arg, (rom >> (i * 8)) & 0xff); + onewire_write_byte(sc, (rom >> (i * 8)) & 0xff); } static void @@ -313,13 +319,17 @@ onewire_thread(void *arg) { struct onewire_softc *sc = arg; + mutex_enter(&sc->sc_lock); while (!sc->sc_dying) { onewire_scan(sc); - tsleep(sc->sc_thread, PWAIT, "owidle", ONEWIRE_SCANTIME * hz); + (void)cv_timedwait(&sc->sc_scancv, &sc->sc_lock, + onewire_scantime * hz); } + mutex_exit(&sc->sc_lock); - sc->sc_thread = NULL; - wakeup(&sc->sc_dying); + /* Caller has set sc_dying and will no longer touch these. */ + cv_destroy(&sc->sc_scancv); + mutex_destroy(&sc->sc_lock); kthread_exit(0); } @@ -328,29 +338,28 @@ onewire_scan(struct onewire_softc *sc) { struct onewire_device *d, *next, *nd; struct onewire_attach_args oa; - device_t dev; int search = 1, count = 0, present; int dir, rv; uint64_t mask, rom = 0, lastrom; uint8_t data[8]; int i, i0 = -1, lastd = -1; - TAILQ_FOREACH(d, &sc->sc_devs, d_list) - d->d_present = 0; + TAILQ_FOREACH(d, &sc->sc_devs, d_list) { + d->d_present = false; + KASSERT(d->d_dev != NULL); + } - while (search && count++ < ONEWIRE_MAXDEVS) { - /* XXX: yield processor */ - tsleep(sc, PWAIT, "owscan", hz / 10); + KASSERT(mutex_owned(&sc->sc_lock)); + KASSERT(curlwp == sc->sc_thread); + while (search && count++ < onewire_maxdevs) { /* * Reset the bus. If there's no presence pulse * don't search for any devices. */ - onewire_lock(sc); if (onewire_reset(sc) != 0) { DPRINTF(("%s: scan: no presence pulse\n", device_xname(sc->sc_dev))); - onewire_unlock(sc); break; } @@ -390,13 +399,11 @@ onewire_scan(struct onewire_softc *sc) DPRINTF(("%s: scan: triplet error 0x%x, " "step %d\n", device_xname(sc->sc_dev), rv, i)); - onewire_unlock(sc); return; } rom |= (mask << i); } lastd = i0; - onewire_unlock(sc); if (rom == 0) continue; @@ -418,42 +425,60 @@ onewire_scan(struct onewire_softc *sc) present = 0; TAILQ_FOREACH(d, &sc->sc_devs, d_list) { if (d->d_rom == rom) { - d->d_present = 1; + d->d_present = true; present = 1; break; } } if (!present) { - memset(&oa, 0, sizeof(oa)); - oa.oa_onewire = sc; - oa.oa_rom = rom; - if ((dev = config_found(sc->sc_dev, &oa, - onewire_print)) == NULL) - continue; - - nd = malloc(sizeof(struct onewire_device), - M_DEVBUF, M_NOWAIT); - if (nd == NULL) - continue; - nd->d_dev = dev; + nd = kmem_alloc(sizeof(*nd), KM_SLEEP); + nd->d_dev = NULL; nd->d_rom = rom; - nd->d_present = 1; + nd->d_present = true; TAILQ_INSERT_TAIL(&sc->sc_devs, nd, d_list); } + + /* + * Yield processor, but continue to hold the lock + * so that scan is not interrupted. + */ + kpause("owscan", false, 1, NULL); } - /* Detach disappeared devices */ - onewire_lock(sc); - for (d = TAILQ_FIRST(&sc->sc_devs); - d != NULL; d = next) { + /* + * Detach disappeared devices, and attach new devices. Drop the + * lock when doing this in order to prevent lock order reversal + * against sysmon. This is safe because nothing other than this + * kthread modifies our device list. + */ + for (d = TAILQ_FIRST(&sc->sc_devs); d != NULL; d = next) { next = TAILQ_NEXT(d, d_list); if (!d->d_present) { + mutex_exit(&sc->sc_lock); + + KERNEL_LOCK(1, NULL); /* XXXSMP */ config_detach(d->d_dev, DETACH_FORCE); + d->d_dev = NULL; + KERNEL_UNLOCK_ONE(NULL); /* XXXSMP */ + + mutex_enter(&sc->sc_lock); + } else if (d->d_dev == NULL) { + memset(&oa, 0, sizeof(oa)); + oa.oa_onewire = sc; + oa.oa_rom = d->d_rom; + mutex_exit(&sc->sc_lock); + + KERNEL_LOCK(1, NULL); /* XXXSMP */ + d->d_dev = config_found(sc->sc_dev, &oa, onewire_print); + KERNEL_UNLOCK_ONE(NULL); /* XXXSMP */ + + mutex_enter(&sc->sc_lock); + } + if (d->d_dev == NULL) { TAILQ_REMOVE(&sc->sc_devs, d, d_list); - free(d, M_DEVBUF); + kmem_free(d, sizeof(*d)); } } - onewire_unlock(sc); } MODULE(MODULE_CLASS_DRIVER, onewire, NULL); Index: src/sys/dev/onewire/onewirereg.h diff -u src/sys/dev/onewire/onewirereg.h:1.3 src/sys/dev/onewire/onewirereg.h:1.4 --- src/sys/dev/onewire/onewirereg.h:1.3 Fri Apr 14 18:38:50 2006 +++ src/sys/dev/onewire/onewirereg.h Fri Oct 25 16:25:14 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: onewirereg.h,v 1.3 2006/04/14 18:38:50 riz Exp $ */ +/* $NetBSD: onewirereg.h,v 1.4 2019/10/25 16:25:14 martin Exp $ */ /* $OpenBSD: onewirereg.h,v 1.1 2006/03/04 16:27:03 grange Exp $ */ /* @@ -34,7 +34,7 @@ #define ONEWIRE_ROM_FAMILY_CUSTOM(x) (((x) >> 7) & 0x1) /* Serial number */ -#define ONEWIRE_ROM_SN(x) (((x) >> 8) & 0xffffffffffffULL) +#define ONEWIRE_ROM_SN(x) (((x) >> 8) & ((uint64_t)0xffffffffffffULL)) /* CRC */ #define ONEWIRE_ROM_CRC(x) (((x) >> 56) & 0xff) Index: src/sys/dev/onewire/owtemp.c diff -u src/sys/dev/onewire/owtemp.c:1.17 src/sys/dev/onewire/owtemp.c:1.18 --- src/sys/dev/onewire/owtemp.c:1.17 Wed May 14 08:14:56 2014 +++ src/sys/dev/onewire/owtemp.c Fri Oct 25 16:25:14 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: owtemp.c,v 1.17 2014/05/14 08:14:56 kardel Exp $ */ +/* $NetBSD: owtemp.c,v 1.18 2019/10/25 16:25:14 martin Exp $ */ /* $OpenBSD: owtemp.c,v 1.1 2006/03/04 16:27:03 grange Exp $ */ /* @@ -22,7 +22,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: owtemp.c,v 1.17 2014/05/14 08:14:56 kardel Exp $"); +__KERNEL_RCSID(0, "$NetBSD: owtemp.c,v 1.18 2019/10/25 16:25:14 martin Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -40,8 +40,10 @@ __KERNEL_RCSID(0, "$NetBSD: owtemp.c,v 1 #define DS_CMD_READ_SCRATCHPAD 0xbe struct owtemp_softc { - void * sc_onewire; + device_t sc_dv; + void *sc_onewire; u_int64_t sc_rom; + const char *sc_chipname; envsys_data_t sc_sensor; struct sysmon_envsys *sc_sme; @@ -89,15 +91,21 @@ owtemp_attach(device_t parent, device_t aprint_naive("\n"); + sc->sc_dv = self; sc->sc_onewire = oa->oa_onewire; sc->sc_rom = oa->oa_rom; switch(ONEWIRE_ROM_FAMILY_TYPE(sc->sc_rom)) { case ONEWIRE_FAMILY_DS18B20: + sc->sc_chipname = "DS18B20"; + sc->sc_owtemp_decode = owtemp_decode_ds18b20; + break; case ONEWIRE_FAMILY_DS1822: + sc->sc_chipname = "DS1822"; sc->sc_owtemp_decode = owtemp_decode_ds18b20; break; case ONEWIRE_FAMILY_DS1920: + sc->sc_chipname = "DS1920"; sc->sc_owtemp_decode = owtemp_decode_ds1920; break; } @@ -109,6 +117,8 @@ owtemp_attach(device_t parent, device_t sc->sc_sensor.state = ENVSYS_SINVALID; (void)strlcpy(sc->sc_sensor.desc, device_xname(self), sizeof(sc->sc_sensor.desc)); + (void)snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), + "%s S/N %012" PRIx64, sc->sc_chipname, ONEWIRE_ROM_SN(sc->sc_rom)); if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { sysmon_envsys_destroy(sc->sc_sme); return; @@ -159,8 +169,10 @@ owtemp_update(void *arg) u_int8_t data[9]; onewire_lock(sc->sc_onewire); - if (onewire_reset(sc->sc_onewire) != 0) + if (onewire_reset(sc->sc_onewire) != 0) { + aprint_error_dev(sc->sc_dv, "owtemp_update: 1st reset failed\n"); goto done; + } onewire_matchrom(sc->sc_onewire, sc->sc_rom); /* @@ -168,13 +180,15 @@ owtemp_update(void *arg) * After sending the command, the data line must be held high for * at least 750ms to provide power during the conversion process. * As such, no other activity may take place on the 1-Wire bus for - * at least this period. + * at least this period. Keep the parent bus locked while waiting. */ onewire_write_byte(sc->sc_onewire, DS_CMD_CONVERT); - tsleep(sc, PRIBIO, "owtemp", hz); + kpause("owtemp", false, mstohz(750 + 10), NULL); - if (onewire_reset(sc->sc_onewire) != 0) + if (onewire_reset(sc->sc_onewire) != 0) { + aprint_error_dev(sc->sc_dv, "owtemp_update: 2nd reset failed\n"); goto done; + } onewire_matchrom(sc->sc_onewire, sc->sc_rom); /*