Module Name: src Committed By: snj Date: Sat Sep 23 17:22:49 UTC 2017
Modified Files: src/sys/dev/sysmon [netbsd-8]: sysmon_envsys.c sysmon_envsys_events.c sysmonvar.h Log Message: Pull up following revision(s) (requested by pgoyette in ticket #281): sys/dev/sysmon/sysmon_envsys.c: 1.140-1.141 sys/dev/sysmon/sysmon_envsys_events.c: 1.120-1.121 sys/dev/sysmon/sysmonvar.h: 1.50 Fixes a problem that some driver(e.g. acpitz(4) or coretemp(5)) which use sysmon_envsys sleep waiting at "rndsrc" when "drvctl -d". Don't call rnd_detach_source() in sme_remove_event() which is called from sme_event_unregister_all(). Instead, call rnd_detach_source() in sysmon_envsys_sensor_detach() and call sysmon_envsys_sensor_detach() before sme_event_unregister_sensor(). Each sensor(envsys_data) has each rnd_src, but some sme_events point to the same rnd_src in a sensor. Calling rnd_detach_souce() twice with the same rnd_src brokes a reference count in rnd_src. OK'd by pgoyette@. -- Improve tracking of the state of an event's callout, and protect all queries or modifications of the state with the sme_mtx mutex. Detach the rndsrc before re-attaching it (with potentially new values). Clean up some lock-ordering issues. And a couple of KNF issues for good measure! Should address PR kern/52533 To generate a diff of this commit: cvs rdiff -u -r1.139 -r1.139.10.1 src/sys/dev/sysmon/sysmon_envsys.c cvs rdiff -u -r1.119 -r1.119.2.1 src/sys/dev/sysmon/sysmon_envsys_events.c cvs rdiff -u -r1.49 -r1.49.10.1 src/sys/dev/sysmon/sysmonvar.h 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/sysmon/sysmon_envsys.c diff -u src/sys/dev/sysmon/sysmon_envsys.c:1.139 src/sys/dev/sysmon/sysmon_envsys.c:1.139.10.1 --- src/sys/dev/sysmon/sysmon_envsys.c:1.139 Mon Dec 14 01:08:47 2015 +++ src/sys/dev/sysmon/sysmon_envsys.c Sat Sep 23 17:22:48 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sysmon_envsys.c,v 1.139 2015/12/14 01:08:47 pgoyette Exp $ */ +/* $NetBSD: sysmon_envsys.c,v 1.139.10.1 2017/09/23 17:22:48 snj Exp $ */ /*- * Copyright (c) 2007, 2008 Juan Romero Pardines. @@ -64,7 +64,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sysmon_envsys.c,v 1.139 2015/12/14 01:08:47 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sysmon_envsys.c,v 1.139.10.1 2017/09/23 17:22:48 snj Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -525,6 +525,8 @@ sysmon_envsys_create(void) { struct sysmon_envsys *sme; + CTASSERT(SME_CALLOUT_INVALID == 0); + sme = kmem_zalloc(sizeof(*sme), KM_SLEEP); TAILQ_INIT(&sme->sme_sensors_list); LIST_INIT(&sme->sme_events_list); @@ -650,9 +652,12 @@ sysmon_envsys_sensor_detach(struct sysmo /* * remove it, unhook from rnd(4), and decrement the sensors count. */ + if (oedata->flags & ENVSYS_FHAS_ENTROPY) + rnd_detach_source(&oedata->rnd_src); sme_event_unregister_sensor(sme, edata); if (LIST_EMPTY(&sme->sme_events_list)) { - sme_events_halt_callout(sme); + if (sme->sme_callout_state == SME_CALLOUT_READY) + sme_events_halt_callout(sme); destroy = true; } TAILQ_REMOVE(&sme->sme_sensors_list, edata, sensors_head); @@ -970,6 +975,7 @@ sysmon_envsys_unregister(struct sysmon_e { prop_array_t array; struct sysmon_envsys *osme; + envsys_data_t *edata; KASSERT(sme != NULL); @@ -987,6 +993,10 @@ sysmon_envsys_unregister(struct sysmon_e LIST_REMOVE(sme, sme_list); mutex_exit(&sme_global_mtx); + TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { + sysmon_envsys_sensor_detach(sme, edata); + } + /* * Unregister all events associated with device. */ @@ -1262,15 +1272,23 @@ sme_remove_userprops(void) } /* - * Finally, remove any old limits event, then - * install a new event (which will update the - * dictionary) + * If the sensor is providing entropy data, + * get rid of the rndsrc; we'll provide a new + * one shortly. + */ + if (edata->flags & ENVSYS_FHAS_ENTROPY) + rnd_detach_source(&edata->rnd_src); + + /* + * Remove the old limits event, if any */ sme_event_unregister(sme, edata->desc, PENVSYS_EVENT_LIMITS); /* - * Find the correct units for this sensor. + * Create and install a new event (which will + * update the dictionary) with the correct + * units. */ sdt_units = sme_find_table_entry(SME_DESC_UNITS, edata->units); @@ -1283,6 +1301,11 @@ sme_remove_userprops(void) &lims, props, PENVSYS_EVENT_LIMITS, sdt_units->crittype); } + + /* Finally, if the sensor provides entropy, + * create an additional event entry and attach + * the rndsrc + */ if (edata->flags & ENVSYS_FHAS_ENTROPY) { sme_event_register(sdict, edata, sme, &lims, props, PENVSYS_EVENT_NULL, @@ -1301,8 +1324,17 @@ sme_remove_userprops(void) * Restore default timeout value. */ sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; + + /* + * Note that we need to hold the sme_mtx while calling + * sme_schedule_callout(). Thus to avoid dropping, + * reacquiring, and dropping it again, we just tell + * sme_envsys_release() that the mutex is already owned. + */ + mutex_enter(&sme->sme_mtx); sme_schedule_callout(sme); - sysmon_envsys_release(sme, false); + sysmon_envsys_release(sme, true); + mutex_exit(&sme->sme_mtx); } mutex_exit(&sme_global_mtx); } @@ -1343,7 +1375,9 @@ sme_add_property_dictionary(struct sysmo */ if (sme->sme_events_timeout == 0) { sme->sme_events_timeout = SME_EVENTS_DEFTIMEOUT; + mutex_enter(&sme->sme_mtx); sme_schedule_callout(sme); + mutex_exit(&sme->sme_mtx); } if (!prop_dictionary_set_uint64(pdict, "refresh-timeout", @@ -1817,7 +1851,7 @@ sme_userset_dictionary(struct sysmon_env sme_schedule_callout(sme); } mutex_exit(&sme->sme_mtx); - } + } } return error; Index: src/sys/dev/sysmon/sysmon_envsys_events.c diff -u src/sys/dev/sysmon/sysmon_envsys_events.c:1.119 src/sys/dev/sysmon/sysmon_envsys_events.c:1.119.2.1 --- src/sys/dev/sysmon/sysmon_envsys_events.c:1.119 Thu Jun 1 02:45:11 2017 +++ src/sys/dev/sysmon/sysmon_envsys_events.c Sat Sep 23 17:22:48 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sysmon_envsys_events.c,v 1.119 2017/06/01 02:45:11 chs Exp $ */ +/* $NetBSD: sysmon_envsys_events.c,v 1.119.2.1 2017/09/23 17:22:48 snj Exp $ */ /*- * Copyright (c) 2007, 2008 Juan Romero Pardines. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_events.c,v 1.119 2017/06/01 02:45:11 chs Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_events.c,v 1.119.2.1 2017/09/23 17:22:48 snj Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -313,7 +313,7 @@ sme_event_register(prop_dictionary_t sdi /* * Initialize the events framework if it wasn't initialized before. */ - if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0) + if (sme->sme_callout_state == SME_CALLOUT_INVALID) error = sme_events_init(sme); /* @@ -374,7 +374,7 @@ sme_event_unregister_all(struct sysmon_e } if (LIST_EMPTY(&sme->sme_events_list) && - sme->sme_flags & SME_CALLOUT_INITIALIZED) { + sme->sme_callout_state == SME_CALLOUT_READY) { sme_events_halt_callout(sme); destroy = true; } @@ -480,8 +480,6 @@ sme_remove_event(sme_event_t *see, struc KASSERT(mutex_owned(&sme->sme_mtx)); - if (see->see_edata->flags & ENVSYS_FHAS_ENTROPY) - rnd_detach_source(&see->see_edata->rnd_src); LIST_REMOVE(see, see_list); kmem_free(see, sizeof(*see)); } @@ -572,7 +570,7 @@ sme_events_init(struct sysmon_envsys *sm callout_init(&sme->sme_callout, CALLOUT_MPSAFE); callout_setfunc(&sme->sme_callout, sme_events_check, sme); - sme->sme_flags |= SME_CALLOUT_INITIALIZED; + sme->sme_callout_state = SME_CALLOUT_READY; sme_schedule_callout(sme); DPRINTF(("%s: events framework initialized for '%s'\n", __func__, sme->sme_name)); @@ -591,8 +589,9 @@ sme_schedule_callout(struct sysmon_envsy uint64_t timo; KASSERT(sme != NULL); + KASSERT(mutex_owned(&sme->sme_mtx)); - if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0) + if (sme->sme_callout_state != SME_CALLOUT_READY) return; if (sme->sme_events_timeout) @@ -612,14 +611,18 @@ sme_schedule_callout(struct sysmon_envsy void sme_events_halt_callout(struct sysmon_envsys *sme) { + KASSERT(mutex_owned(&sme->sme_mtx)); + KASSERT(sme->sme_callout_state == SME_CALLOUT_READY); /* - * Unset before callout_halt to ensure callout is not scheduled again - * during callout_halt. + * Set HALTED before callout_halt to ensure callout is not + * scheduled again during callout_halt. (callout_halt() + * can potentially release the mutex, so an active callout + * could reschedule itself if it grabs the mutex.) */ - sme->sme_flags &= ~SME_CALLOUT_INITIALIZED; + sme->sme_callout_state = SME_CALLOUT_HALTED; callout_halt(&sme->sme_callout, &sme->sme_mtx); } @@ -632,11 +635,15 @@ sme_events_halt_callout(struct sysmon_en void sme_events_destroy(struct sysmon_envsys *sme) { + KASSERT(!mutex_owned(&sme->sme_mtx)); - KASSERT((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0); - callout_destroy(&sme->sme_callout); - workqueue_destroy(sme->sme_wq); + if (sme->sme_callout_state == SME_CALLOUT_HALTED) { + callout_destroy(&sme->sme_callout); + sme->sme_callout_state = SME_CALLOUT_INVALID; + workqueue_destroy(sme->sme_wq); + } + KASSERT(sme->sme_callout_state == SME_CALLOUT_INVALID); DPRINTF(("%s: events framework destroyed for '%s'\n", __func__, sme->sme_name)); @@ -736,13 +743,7 @@ sme_events_check(void *arg) mutex_exit(&sme->sme_work_mtx); return; } - if (!mutex_tryenter(&sme->sme_mtx)) { - /* can't get lock - try again later */ - if (!sysmon_low_power) - sme_schedule_callout(sme); - mutex_exit(&sme->sme_work_mtx); - return; - } + mutex_enter(&sme->sme_mtx); LIST_FOREACH(see, &sme->sme_events_list, see_list) { workqueue_enqueue(sme->sme_wq, &see->see_wk, NULL); see->see_edata->flags |= ENVSYS_FNEED_REFRESH; @@ -750,8 +751,8 @@ sme_events_check(void *arg) } if (!sysmon_low_power) sme_schedule_callout(sme); - mutex_exit(&sme->sme_work_mtx); mutex_exit(&sme->sme_mtx); + mutex_exit(&sme->sme_work_mtx); } /* @@ -1005,12 +1006,16 @@ sme_deliver_event(sme_event_t *see) */ if (!sysmon_low_power && sme_event_check_low_power()) { struct penvsys_state pes; + struct sysmon_envsys *sme = see->see_sme; /* * Stop the callout and send the 'low-power' event. */ sysmon_low_power = true; - callout_stop(&see->see_sme->sme_callout); + KASSERT(mutex_owned(&sme->sme_mtx)); + KASSERT(sme->sme_callout_state == SME_CALLOUT_READY); + callout_stop(&sme->sme_callout); + sme->sme_callout_state = SME_CALLOUT_HALTED; pes.pes_type = PENVSYS_TYPE_BATTERY; sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER); } Index: src/sys/dev/sysmon/sysmonvar.h diff -u src/sys/dev/sysmon/sysmonvar.h:1.49 src/sys/dev/sysmon/sysmonvar.h:1.49.10.1 --- src/sys/dev/sysmon/sysmonvar.h:1.49 Thu Apr 23 23:22:03 2015 +++ src/sys/dev/sysmon/sysmonvar.h Sat Sep 23 17:22:48 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sysmonvar.h,v 1.49 2015/04/23 23:22:03 pgoyette Exp $ */ +/* $NetBSD: sysmonvar.h,v 1.49.10.1 2017/09/23 17:22:48 snj Exp $ */ /*- * Copyright (c) 2000 Zembu Labs, Inc. @@ -163,7 +163,6 @@ struct sysmon_envsys { int sme_flags; /* additional flags */ #define SME_FLAG_BUSY 0x00000001 /* device busy */ #define SME_DISABLE_REFRESH 0x00000002 /* disable sme_refresh */ -#define SME_CALLOUT_INITIALIZED 0x00000004 /* callout was initialized */ #define SME_INIT_REFRESH 0x00000008 /* call sme_refresh() after interrupts are enabled in the autoconf(9) process. */ @@ -187,6 +186,12 @@ struct sysmon_envsys { struct workqueue *sme_wq; /* the workqueue for the events */ struct callout sme_callout; /* for the events */ + int sme_callout_state; /* state of the event's callout */ + +#define SME_CALLOUT_INVALID 0x0 /* callout is not initialized */ +#define SME_CALLOUT_READY 0x1 /* callout is ready for use */ +#define SME_CALLOUT_HALTED 0x2 /* callout can be destroyed */ + uint64_t sme_events_timeout; /* the timeout used in the callout */ /*