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);
/*