diff --git a/module/os/freebsd/spl/spl_sysevent.c b/module/os/freebsd/spl/spl_sysevent.c index d5d50080fafd..16188c71b53d 100644 --- a/module/os/freebsd/spl/spl_sysevent.c +++ b/module/os/freebsd/spl/spl_sysevent.c @@ -1,262 +1,272 @@ /* * Copyright (c) 2010 Pawel Jakub Dawidek * Copyright (c) 2020 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int log_sysevent(nvlist_t *event) { struct sbuf *sb; const char *type; char typestr[128]; nvpair_t *elem = NULL; sb = sbuf_new_auto(); if (sb == NULL) return (ENOMEM); type = NULL; while ((elem = nvlist_next_nvpair(event, elem)) != NULL) { switch (nvpair_type(elem)) { case DATA_TYPE_BOOLEAN: { boolean_t value; (void) nvpair_value_boolean_value(elem, &value); sbuf_printf(sb, " %s=%s", nvpair_name(elem), value ? "true" : "false"); break; } case DATA_TYPE_UINT8: { uint8_t value; (void) nvpair_value_uint8(elem, &value); sbuf_printf(sb, " %s=%hhu", nvpair_name(elem), value); break; } case DATA_TYPE_INT32: { int32_t value; (void) nvpair_value_int32(elem, &value); sbuf_printf(sb, " %s=%jd", nvpair_name(elem), (intmax_t)value); break; } case DATA_TYPE_UINT32: { uint32_t value; (void) nvpair_value_uint32(elem, &value); sbuf_printf(sb, " %s=%ju", nvpair_name(elem), (uintmax_t)value); break; } case DATA_TYPE_INT64: { int64_t value; (void) nvpair_value_int64(elem, &value); sbuf_printf(sb, " %s=%jd", nvpair_name(elem), (intmax_t)value); break; } case DATA_TYPE_UINT64: { uint64_t value; (void) nvpair_value_uint64(elem, &value); sbuf_printf(sb, " %s=%ju", nvpair_name(elem), (uintmax_t)value); break; } case DATA_TYPE_STRING: { char *value; (void) nvpair_value_string(elem, &value); sbuf_printf(sb, " %s=%s", nvpair_name(elem), value); if (strcmp(FM_CLASS, nvpair_name(elem)) == 0) type = value; break; } case DATA_TYPE_UINT8_ARRAY: { uint8_t *value; uint_t ii, nelem; (void) nvpair_value_uint8_array(elem, &value, &nelem); sbuf_printf(sb, " %s=", nvpair_name(elem)); for (ii = 0; ii < nelem; ii++) sbuf_printf(sb, "%02hhx", value[ii]); break; } case DATA_TYPE_UINT16_ARRAY: { uint16_t *value; uint_t ii, nelem; (void) nvpair_value_uint16_array(elem, &value, &nelem); sbuf_printf(sb, " %s=", nvpair_name(elem)); for (ii = 0; ii < nelem; ii++) sbuf_printf(sb, "%04hx", value[ii]); break; } case DATA_TYPE_UINT32_ARRAY: { uint32_t *value; uint_t ii, nelem; (void) nvpair_value_uint32_array(elem, &value, &nelem); sbuf_printf(sb, " %s=", nvpair_name(elem)); for (ii = 0; ii < nelem; ii++) sbuf_printf(sb, "%08jx", (uintmax_t)value[ii]); break; } case DATA_TYPE_INT64_ARRAY: { int64_t *value; uint_t ii, nelem; (void) nvpair_value_int64_array(elem, &value, &nelem); sbuf_printf(sb, " %s=", nvpair_name(elem)); for (ii = 0; ii < nelem; ii++) sbuf_printf(sb, "%016lld", (long long)value[ii]); break; } case DATA_TYPE_UINT64_ARRAY: { uint64_t *value; uint_t ii, nelem; (void) nvpair_value_uint64_array(elem, &value, &nelem); sbuf_printf(sb, " %s=", nvpair_name(elem)); for (ii = 0; ii < nelem; ii++) sbuf_printf(sb, "%016jx", (uintmax_t)value[ii]); break; } case DATA_TYPE_STRING_ARRAY: { char **strarr; uint_t ii, nelem; (void) nvpair_value_string_array(elem, &strarr, &nelem); for (ii = 0; ii < nelem; ii++) { if (strarr[ii] == NULL) { sbuf_printf(sb, " "); continue; } sbuf_printf(sb, " %s", strarr[ii]); if (strcmp(FM_CLASS, strarr[ii]) == 0) type = strarr[ii]; } break; } case DATA_TYPE_NVLIST: /* XXX - requires recursing in log_sysevent */ break; default: printf("%s: type %d is not implemented\n", __func__, nvpair_type(elem)); break; } } if (sbuf_finish(sb) != 0) { sbuf_delete(sb); return (ENOMEM); } if (type == NULL) type = ""; if (strncmp(type, "ESC_ZFS_", 8) == 0) { snprintf(typestr, sizeof (typestr), "misc.fs.zfs.%s", type + 8); type = typestr; } devctl_notify("ZFS", "ZFS", type, sbuf_data(sb)); sbuf_delete(sb); return (0); } static void sysevent_worker(void *arg __unused) { zfs_zevent_t *ze; nvlist_t *event; uint64_t dropped = 0; uint64_t dst_size; int error; zfs_zevent_init(&ze); for (;;) { dst_size = 131072; dropped = 0; event = NULL; error = zfs_zevent_next(ze, &event, &dst_size, &dropped); if (error) { error = zfs_zevent_wait(ze); if (error == ESHUTDOWN) break; } else { VERIFY3P(event, !=, NULL); log_sysevent(event); nvlist_free(event); } } - zfs_zevent_destroy(ze); + + /* + * We avoid zfs_zevent_destroy() here because we're otherwise racing + * against fm_fini() destroying the zevent_lock. zfs_zevent_destroy() + * will currently only clear `ze->ze_zevent` from an event list then + * free `ze`, so just inline the free() here -- events have already + * been drained. + */ + VERIFY3P(ze->ze_zevent, ==, NULL); + kmem_free(ze, sizeof (zfs_zevent_t)); + kthread_exit(); } void ddi_sysevent_init(void) { kproc_kthread_add(sysevent_worker, NULL, &system_proc, NULL, 0, 0, "zfskern", "sysevent"); }