diff options
Diffstat (limited to 'kern/llsync.c')
-rw-r--r-- | kern/llsync.c | 341 |
1 files changed, 0 insertions, 341 deletions
diff --git a/kern/llsync.c b/kern/llsync.c deleted file mode 100644 index 36233fa..0000000 --- a/kern/llsync.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (c) 2013-2014 Richard Braun. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * - * The method used by this module is described in the expired US patent - * 4809168, "Passive Serialization in a Multitasking Environment". It is - * similar to "Classic RCU (Read-Copy Update)" as found in Linux 2.6, with - * the notable difference that RCU actively starts grace periods, where - * passive serialization waits for two sequential "multiprocess checkpoints" - * (renamed global checkpoints in this implementation) to occur. - * - * It is used instead of RCU because of patents that may not allow writing - * an implementation not based on the Linux code (see - * http://lists.lttng.org/pipermail/lttng-dev/2013-May/020305.html). As - * patents expire, this module could be reworked to become a true RCU - * implementation. In the mean time, the module interface was carefully - * designed to be similar to RCU. - * - * TODO Gracefully handle large amounts of deferred works. - */ - -#include <assert.h> -#include <stdbool.h> -#include <stddef.h> - -#include <kern/condition.h> -#include <kern/cpumap.h> -#include <kern/init.h> -#include <kern/list.h> -#include <kern/log.h> -#include <kern/llsync.h> -#include <kern/llsync_i.h> -#include <kern/macros.h> -#include <kern/mutex.h> -#include <kern/percpu.h> -#include <kern/spinlock.h> -#include <kern/syscnt.h> -#include <kern/work.h> -#include <kern/thread.h> -#include <machine/cpu.h> - -/* - * Initial global checkpoint ID. - * - * Set to a high value to make sure overflows are correctly handled. - */ -#define LLSYNC_INITIAL_GCID ((unsigned int)-10) - -/* - * Number of pending works beyond which to issue a warning. - */ -#define LLSYNC_NR_PENDING_WORKS_WARN 10000 - -struct llsync_data llsync_data; -struct llsync_cpu_data llsync_cpu_data __percpu; - -struct llsync_waiter { - struct work work; - struct mutex lock; - struct condition cond; - int done; -}; - -static bool llsync_is_ready __read_mostly = false; - -bool -llsync_ready(void) -{ - return llsync_is_ready; -} - -static int __init -llsync_setup(void) -{ - struct llsync_cpu_data *cpu_data; - unsigned int i; - - spinlock_init(&llsync_data.lock); - work_queue_init(&llsync_data.queue0); - work_queue_init(&llsync_data.queue1); - syscnt_register(&llsync_data.sc_global_checkpoints, - "llsync_global_checkpoints"); - syscnt_register(&llsync_data.sc_periodic_checkins, - "llsync_periodic_checkins"); - syscnt_register(&llsync_data.sc_failed_periodic_checkins, - "llsync_failed_periodic_checkins"); - llsync_data.gcid.value = LLSYNC_INITIAL_GCID; - - for (i = 0; i < cpu_count(); i++) { - cpu_data = percpu_ptr(llsync_cpu_data, i); - work_queue_init(&cpu_data->queue0); - } - - return 0; -} - -INIT_OP_DEFINE(llsync_setup, - INIT_OP_DEP(log_setup, true), - INIT_OP_DEP(mutex_setup, true), - INIT_OP_DEP(spinlock_setup, true), - INIT_OP_DEP(syscnt_setup, true), - INIT_OP_DEP(thread_bootstrap, true), - INIT_OP_DEP(work_setup, true)); - -static void -llsync_process_global_checkpoint(void) -{ - struct work_queue queue; - unsigned int nr_works; - - assert(cpumap_find_first(&llsync_data.pending_checkpoints) == -1); - assert(llsync_data.nr_pending_checkpoints == 0); - - nr_works = work_queue_nr_works(&llsync_data.queue0) - + work_queue_nr_works(&llsync_data.queue1); - - /* TODO Handle hysteresis */ - if (!llsync_data.no_warning && (nr_works >= LLSYNC_NR_PENDING_WORKS_WARN)) { - llsync_data.no_warning = 1; - log_warning("llsync: large number of pending works\n"); - } - - if (llsync_data.nr_registered_cpus == 0) { - work_queue_concat(&llsync_data.queue1, &llsync_data.queue0); - work_queue_init(&llsync_data.queue0); - } else { - cpumap_copy(&llsync_data.pending_checkpoints, &llsync_data.registered_cpus); - llsync_data.nr_pending_checkpoints = llsync_data.nr_registered_cpus; - } - - work_queue_transfer(&queue, &llsync_data.queue1); - work_queue_transfer(&llsync_data.queue1, &llsync_data.queue0); - work_queue_init(&llsync_data.queue0); - - if (work_queue_nr_works(&queue) != 0) { - work_queue_schedule(&queue, 0); - } - - llsync_data.gcid.value++; - syscnt_inc(&llsync_data.sc_global_checkpoints); -} - -static void -llsync_flush_works(struct llsync_cpu_data *cpu_data) -{ - if (work_queue_nr_works(&cpu_data->queue0) == 0) { - return; - } - - work_queue_concat(&llsync_data.queue0, &cpu_data->queue0); - work_queue_init(&cpu_data->queue0); -} - -static void -llsync_commit_checkpoint(unsigned int cpu) -{ - int pending; - - pending = cpumap_test(&llsync_data.pending_checkpoints, cpu); - - if (!pending) { - return; - } - - cpumap_clear(&llsync_data.pending_checkpoints, cpu); - llsync_data.nr_pending_checkpoints--; - - if (llsync_data.nr_pending_checkpoints == 0) { - llsync_process_global_checkpoint(); - } -} - -void -llsync_register(void) -{ - struct llsync_cpu_data *cpu_data; - unsigned long flags; - unsigned int cpu; - - if (!llsync_is_ready) { - llsync_is_ready = true; - } - - cpu = cpu_id(); - cpu_data = llsync_get_cpu_data(); - - spinlock_lock_intr_save(&llsync_data.lock, &flags); - - assert(!cpu_data->registered); - assert(work_queue_nr_works(&cpu_data->queue0) == 0); - cpu_data->registered = 1; - cpu_data->gcid = llsync_data.gcid.value; - - assert(!cpumap_test(&llsync_data.registered_cpus, cpu)); - cpumap_set(&llsync_data.registered_cpus, cpu); - llsync_data.nr_registered_cpus++; - - assert(!cpumap_test(&llsync_data.pending_checkpoints, cpu)); - - if ((llsync_data.nr_registered_cpus == 1) - && (llsync_data.nr_pending_checkpoints == 0)) { - llsync_process_global_checkpoint(); - } - - spinlock_unlock_intr_restore(&llsync_data.lock, flags); -} - -void -llsync_unregister(void) -{ - struct llsync_cpu_data *cpu_data; - unsigned long flags; - unsigned int cpu; - - cpu = cpu_id(); - cpu_data = llsync_get_cpu_data(); - - spinlock_lock_intr_save(&llsync_data.lock, &flags); - - llsync_flush_works(cpu_data); - - assert(cpu_data->registered); - cpu_data->registered = 0; - - assert(cpumap_test(&llsync_data.registered_cpus, cpu)); - cpumap_clear(&llsync_data.registered_cpus, cpu); - llsync_data.nr_registered_cpus--; - - /* - * Processor registration qualifies as a checkpoint. Since unregistering - * a processor also disables commits until it's registered again, perform - * one now. - */ - llsync_commit_checkpoint(cpu); - - spinlock_unlock_intr_restore(&llsync_data.lock, flags); -} - -void -llsync_report_periodic_event(void) -{ - struct llsync_cpu_data *cpu_data; - unsigned int gcid; - - assert(thread_check_intr_context()); - - cpu_data = llsync_get_cpu_data(); - - if (!cpu_data->registered) { - assert(work_queue_nr_works(&cpu_data->queue0) == 0); - return; - } - - spinlock_lock(&llsync_data.lock); - - llsync_flush_works(cpu_data); - - gcid = llsync_data.gcid.value; - assert((gcid - cpu_data->gcid) <= 1); - - /* - * If the local copy of the global checkpoint ID matches the true - * value, the current processor has checked in. - * - * Otherwise, there were no checkpoint since the last global checkpoint. - * Check whether this periodic event occurred during a read-side critical - * section, and if not, trigger a checkpoint. - */ - if (cpu_data->gcid == gcid) { - llsync_commit_checkpoint(cpu_id()); - } else { - if (thread_llsync_in_read_cs()) { - syscnt_inc(&llsync_data.sc_failed_periodic_checkins); - } else { - cpu_data->gcid = gcid; - syscnt_inc(&llsync_data.sc_periodic_checkins); - llsync_commit_checkpoint(cpu_id()); - } - } - - spinlock_unlock(&llsync_data.lock); -} - -void -llsync_defer(struct work *work) -{ - struct llsync_cpu_data *cpu_data; - unsigned long flags; - - thread_preempt_disable_intr_save(&flags); - cpu_data = llsync_get_cpu_data(); - work_queue_push(&cpu_data->queue0, work); - thread_preempt_enable_intr_restore(flags); -} - -static void -llsync_signal(struct work *work) -{ - struct llsync_waiter *waiter; - - waiter = structof(work, struct llsync_waiter, work); - - mutex_lock(&waiter->lock); - waiter->done = 1; - condition_signal(&waiter->cond); - mutex_unlock(&waiter->lock); -} - -void -llsync_wait(void) -{ - struct llsync_waiter waiter; - - work_init(&waiter.work, llsync_signal); - mutex_init(&waiter.lock); - condition_init(&waiter.cond); - waiter.done = 0; - - llsync_defer(&waiter.work); - - mutex_lock(&waiter.lock); - - while (!waiter.done) { - condition_wait(&waiter.cond, &waiter.lock); - } - - mutex_unlock(&waiter.lock); -} |