/*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Andriy Gapon * * 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 AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #ifndef _SYS_WFHANDLER_H_ #define _SYS_WFHANDLER_H_ #include #include #include typedef void wfhandler_cb(void *); /* XXX */ struct wfhandler_entry { CK_SLIST_ENTRY(wfhandler_entry) wfe_link; wfhandler_cb *wfe_func; void *wfe_arg; int wfe_priority; }; struct wfhandler_list { struct mtx wfl_lock; CK_SLIST_HEAD(, wfhandler_entry) wfl_entries; volatile int wfl_phase; volatile int wfl_active[2]; }; /* Generic priority levels */ #define WFHANDLER_PRI_FIRST 0 #define WFHANDLER_PRI_ANY 10000 #define WFHANDLER_PRI_LAST 20000 void wfhandler_list_init(const char *name, struct wfhandler_list *list) { mtx_init(&list->wfl_lock, name, NULL, MTX_DEF); CK_SLIST_INIT(&list->wfl_entries); list->wfl_phase = 0; list->wfl_active[0] = 0; list->wfl_active[1] = 0; } void wfhandler_register(struct wfhandler_list *list, struct wfhandler_entry *entry) { struct wfhandler_entry *elm; struct wfhandler_entry **prevptr; /* Serialize modifications. */ mtx_lock(&list->wfl_lock); CK_SLIST_FOREACH_PREVPTR(elm, prevptr, &list->wfl_entries, wfe_link) { if (elm->wfe_priority > entry->wfe_priority) break; } CK_SLIST_INSERT_AFTER(prevptr, entry, wfe_link); mtx_unlock(&list->wfl_lock); } void wfhandler_deregister(struct wfhandler_list *list, struct wfhandler_entry *entry) { struct wfhandler_entry *elm; struct wfhandler_entry **prevptr; int phase; /* Serialize modifications. */ mtx_lock(&list->wfl_lock); phase = list->wfl_phase; CK_SLIST_FOREACH_PREVPTR(elm, prevptr, &list->wfl_entries, wfe_link) { if (elm == entry) { CK_SLIST_REMOVE_PREVPTR(prevptr, elm, wfe_link); break; } } KASSERT(elm != NULL, ("entry %p is not on handler list")); /* Switch new readers to the other busy counter. */ KASSERT(&list->wfl_active[!phase] == 0, ("idle phase is busy")); atomic_store_rel_int(&list->wfl_phase, !phase); /* * Make sure that the list updates are not reordered with the check. */ atomic_thread_fence_seq_cst(); while (list->wfl_active[phase] > 0) cpu_pause(); /* * Make sure that later operations are not reordered before the check. */ atomic_thread_fence_acq(); mtx_unlock(&list->wfl_lock); } void wfhandler_invoke(struct wfhandler_list *list) { struct wfhandler_entry *elm; int phase; /* * Ensure that all loads are after the busy count is updated * for the current generation. */ phase = list->wfl_phase; atomic_add_int(&list->wfl_active[phase], 1); atomic_thread_fence_seq_cst(); CK_SLIST_FOREACH(elm, &list->wfl_entries, wfe_link) elm->wfe_func(elm->wfe_arg); /* Ensure that all loads are before the flag is reset. */ atomic_add_rel_int(&list->wfl_active[phase], -1); } #endif /* _SYS_WFHANDLER_H_ */