Index: sys/kern/subr_epoch.c =================================================================== --- sys/kern/subr_epoch.c +++ sys/kern/subr_epoch.c @@ -72,6 +72,16 @@ volatile struct epoch_tdlist er_tdlist; volatile uint32_t er_gen; uint32_t er_cpuid; +#ifdef INVARIANTS + /* + * This is perhaps not intuitive, but specifically record the thread + * that's in this epoch for non-preemptible epochs (only!). We'll use + * it to do some trivial sanity checks, mostly ensuring that the caller + * didn't have some kind of critical nesting hijinks happen underneath + * it. + */ + struct thread *er_td; +#endif } __aligned(EPOCH_ALIGN) *epoch_record_t; struct epoch { @@ -377,6 +387,9 @@ void epoch_free(epoch_t epoch) { +#ifdef INVARIANTS + int cpu; +#endif EPOCH_LOCK(); @@ -390,6 +403,21 @@ * to zero, by calling epoch_wait() on the global_epoch: */ epoch_wait(global_epoch); +#ifdef INVARIANTS + CPU_FOREACH(cpu) { + epoch_record_t er; + + er = zpcpu_get_cpu(epoch->e_pcpu_record, cpu); + + /* + * Sanity check: none of the records should be in use anymore. + * We drained callbacks above and freeing the pcpu records is + * imminent. + */ + MPASS(er->er_td == NULL); + MPASS(TAILQ_EMPTY(&er->er_tdlist)); + } +#endif uma_zfree_pcpu(pcpu_zone_record, epoch->e_pcpu_record); mtx_destroy(&epoch->e_drain_mtx); sx_destroy(&epoch->e_drain_sx); @@ -434,6 +462,10 @@ sched_pin(); td->td_pre_epoch_prio = td->td_priority; er = epoch_currecord(epoch); +#ifdef INVARIANTS + /* Record-level tracking is reserved for non-preemptible epochs. */ + MPASS(er->er_td == NULL); +#endif TAILQ_INSERT_TAIL(&er->er_tdlist, et, et_link); ck_epoch_begin(&er->er_record, &et->et_section); critical_exit(); @@ -448,6 +480,15 @@ INIT_CHECK(epoch); critical_enter(); er = epoch_currecord(epoch); +#ifdef INVARIANTS + if (er->er_record.active == 0) { + MPASS(er->er_td == NULL); + er->er_td = curthread; + } else { + /* We've recursed, just make sure our accounting isn't wrong. */ + MPASS(er->er_td == curthread); + } +#endif ck_epoch_begin(&er->er_record, NULL); } @@ -468,6 +509,8 @@ MPASS(et->et_td == td); #ifdef INVARIANTS et->et_td = (void*)0xDEADBEEF; + /* Record-level tracking is reserved for non-preemptible epochs. */ + MPASS(er->er_td == NULL); #endif ck_epoch_end(&er->er_record, &et->et_section); TAILQ_REMOVE(&er->er_tdlist, et, et_link); @@ -488,6 +531,11 @@ INIT_CHECK(epoch); er = epoch_currecord(epoch); ck_epoch_end(&er->er_record, NULL); +#ifdef INVARIANTS + MPASS(er->er_td == curthread); + if (er->er_record.active == 0) + er->er_td = NULL; +#endif critical_exit(); } @@ -777,18 +825,18 @@ } } -int -in_epoch_verbose(epoch_t epoch, int dump_onfail) +static int +in_epoch_verbose_preempt(epoch_t epoch, int dump_onfail) { + epoch_record_t er; struct epoch_tracker *tdwait; struct thread *td; - epoch_record_t er; + MPASS(epoch != NULL); + MPASS((epoch->e_flags & EPOCH_PREEMPT) != 0); td = curthread; if (THREAD_CAN_SLEEP()) return (0); - if (__predict_false((epoch) == NULL)) - return (0); critical_enter(); er = epoch_currecord(epoch); TAILQ_FOREACH(tdwait, &er->er_tdlist, et_link) @@ -809,6 +857,50 @@ return (0); } +int +in_epoch_verbose(epoch_t epoch, int dump_onfail) +{ + epoch_record_t er; + struct thread *td; + bool entered; + + if (__predict_false((epoch) == NULL)) + return (0); + if ((epoch->e_flags & EPOCH_PREEMPT) != 0) + return (in_epoch_verbose_preempt(epoch, dump_onfail)); + + td = curthread; + entered = false; + + /* + * Non-preemptible epoch's a little easier to decide. If we're not in + * a critical section, then we're certainly not in an epoch. + */ + if (td->td_critnest == 0) + goto out; + + /* + * The current cpu is in a critical section, so the epoch record will be + * stable for the rest of this function. Knowing that the record is not + * active is sufficient for knowing whether we're in this epoch or not, + * since it's a pcpu record. + */ + er = epoch_currecord(epoch); + if (er->er_record.active == 0) + goto out; + + entered = true; +out: +#ifdef INVARIANTS + if (!entered && dump_onfail) { + printf("%s: id: %d\n", __func__, td->td_tid); + } + + MPASS(!entered || er->er_td == td); +#endif + return (entered ? 1 : 0); +} + int in_epoch(epoch_t epoch) {