123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762 |
- #include <assert.h>
- #include <stdalign.h>
- #include <stdbool.h>
- #include <stddef.h>
- #include <stdio.h>
- #include <kern/atomic.h>
- #include <kern/clock.h>
- #include <kern/init.h>
- #include <kern/macros.h>
- #include <kern/rcu.h>
- #include <kern/panic.h>
- #include <kern/percpu.h>
- #include <kern/spinlock.h>
- #include <kern/sref.h>
- #include <kern/syscnt.h>
- #include <kern/thread.h>
- #include <kern/timer.h>
- #include <kern/work.h>
- #include <machine/cpu.h>
- #define RCU_WINDOW_ID_INIT_VALUE ((uint32_t)-500)
- #define RCU_WINDOW_CHECK_INTERVAL CONFIG_RCU_WINDOW_CHECK_INTERVAL
- #define RCU_NR_WORKS_CUTOFF 1000
- enum rcu_gp_state
- {
- RCU_GP_STATE_WORK_WINDOW_FLIP,
- RCU_GP_STATE_READER_WINDOW_FLIP,
- RCU_GP_STATE_WORK_FLUSH,
- };
- struct rcu_cpu_window
- {
- struct work_queue works;
- };
- struct rcu_cpu_data
- {
- enum rcu_gp_state gp_state;
- uint32_t work_wid;
- uint32_t reader_wid;
- struct rcu_cpu_window windows[2];
- struct syscnt sc_nr_detected_readers;
- };
- struct rcu_window
- {
- struct sref_counter nr_refs;
- uint64_t start_ts;
- bool active;
- };
- struct rcu_data
- {
- __cacheline_aligned enum rcu_gp_state gp_state;
- __cacheline_aligned uint32_t nr_acks;
- uint32_t wid;
- struct rcu_window windows[2];
- struct timer timer;
- struct syscnt sc_nr_windows;
- struct syscnt sc_last_window_ms;
- struct syscnt sc_longest_window_ms;
- };
- struct rcu_waiter
- {
- struct work work;
- struct spinlock lock;
- struct thread *thread;
- bool done;
- };
- static struct rcu_data rcu_data;
- static struct rcu_cpu_data rcu_cpu_data __percpu;
- static struct rcu_cpu_data*
- rcu_get_cpu_data (void)
- {
- assert (!cpu_intr_enabled ());
- assert (!thread_preempt_enabled ());
- return (cpu_local_ptr (rcu_cpu_data));
- }
- static enum rcu_gp_state
- rcu_data_get_gp_state (const struct rcu_data *data)
- {
- return (data->gp_state);
- }
- static uint32_t
- rcu_data_get_wid (const struct rcu_data *data)
- {
- return (data->wid);
- }
- static struct rcu_window*
- rcu_data_get_window_from_index (struct rcu_data *data, size_t index)
- {
- assert (index < ARRAY_SIZE (data->windows));
- return (&data->windows[index]);
- }
- static struct rcu_window*
- rcu_data_get_window (struct rcu_data *data, uint32_t wid)
- {
- return (rcu_data_get_window_from_index (data, wid & 1));
- }
- static void
- rcu_data_update_gp_state (struct rcu_data *data, enum rcu_gp_state gp_state)
- {
- assert (!data->nr_acks);
- switch (gp_state)
- {
- case RCU_GP_STATE_WORK_WINDOW_FLIP:
- assert (data->gp_state == RCU_GP_STATE_WORK_FLUSH);
- break;
- case RCU_GP_STATE_READER_WINDOW_FLIP:
- assert (data->gp_state == RCU_GP_STATE_WORK_WINDOW_FLIP);
- break;
- case RCU_GP_STATE_WORK_FLUSH:
- assert (data->gp_state == RCU_GP_STATE_READER_WINDOW_FLIP);
- break;
- default:
- panic ("rcu: invalid grace period state");
- }
- data->nr_acks = cpu_count ();
- atomic_store_rel (&data->gp_state, gp_state);
- }
- static bool
- rcu_data_check_gp_state (const struct rcu_data *data,
- enum rcu_gp_state local_gp_state,
- enum rcu_gp_state *global_gp_state)
- {
- *global_gp_state = atomic_load_rlx (&data->gp_state);
- if (likely (local_gp_state == *global_gp_state))
- return (false);
- atomic_fence_acq ();
- return (true);
- }
- static void
- rcu_window_end (struct rcu_window *window)
- {
- assert (window->active);
- window->active = false;
- }
- static void
- rcu_window_ref (struct rcu_window *window)
- {
- sref_counter_inc (&window->nr_refs);
- }
- static void
- rcu_window_unref (struct rcu_window *window)
- {
- sref_counter_dec (&window->nr_refs);
- }
- static uint64_t
- rcu_window_get_start_ts (const struct rcu_window *window)
- {
- return (window->start_ts);
- }
- static void
- rcu_window_flush (struct sref_counter *counter __unused)
- {
- rcu_data_update_gp_state (&rcu_data, RCU_GP_STATE_WORK_FLUSH);
- }
- static void __init
- rcu_window_init (struct rcu_window *window)
- {
- window->active = false;
- }
- static void
- rcu_window_start (struct rcu_window *window)
- {
- assert (!window->active);
- sref_counter_init (&window->nr_refs, 1, NULL, rcu_window_flush);
- window->start_ts = clock_get_time ();
- window->active = true;
- }
- static bool
- rcu_window_active (const struct rcu_window *window)
- {
- return (window->active);
- }
- static void
- rcu_data_end_prev_window (struct rcu_data *data, uint64_t now)
- {
- _Auto window = rcu_data_get_window (data, data->wid - 1);
- _Auto start_ts = rcu_window_get_start_ts (window);
- uint64_t duration = clock_ticks_to_ms (now - start_ts);
- syscnt_set (&data->sc_last_window_ms, duration);
- if (duration > syscnt_read (&data->sc_longest_window_ms))
- syscnt_set (&data->sc_longest_window_ms, duration);
- rcu_window_end (window);
- }
- static void
- rcu_data_schedule_timer (struct rcu_data *data, uint64_t now)
- {
- uint64_t ticks = clock_ticks_from_ms (RCU_WINDOW_CHECK_INTERVAL);
- timer_schedule (&data->timer, now + ticks);
- }
- static void
- rcu_data_ack_cpu (struct rcu_data *data)
- {
- uint32_t prev_nr_acks = atomic_sub_acq_rel (&data->nr_acks, 1);
- if (prev_nr_acks != 1)
- {
- assert (prev_nr_acks);
- return;
- }
- uint64_t now;
- switch (data->gp_state)
- {
- case RCU_GP_STATE_WORK_WINDOW_FLIP:
- rcu_data_update_gp_state (data, RCU_GP_STATE_READER_WINDOW_FLIP);
- break;
- case RCU_GP_STATE_READER_WINDOW_FLIP:
- rcu_window_unref (rcu_data_get_window (data, data->wid - 1));
- break;
- case RCU_GP_STATE_WORK_FLUSH:
- now = clock_get_time ();
- rcu_data_end_prev_window (data, now);
- rcu_data_schedule_timer (data, now);
- break;
- default:
- panic ("rcu: invalid grace period state");
- }
- }
- static bool
- rcu_data_flip_windows (struct rcu_data *data)
- {
- _Auto window = rcu_data_get_window (data, data->wid - 1);
- if (rcu_window_active (window))
- return (false);
- rcu_window_start (window);
- syscnt_inc (&data->sc_nr_windows);
- ++data->wid;
- rcu_data_update_gp_state (data, RCU_GP_STATE_WORK_WINDOW_FLIP);
- return (true);
- }
- static void
- rcu_data_check_windows (struct timer *timer)
- {
- struct rcu_data *data = &rcu_data;
- if (!rcu_data_flip_windows (data))
- rcu_data_schedule_timer (data, timer_get_time (timer));
- }
- static void __init
- rcu_data_init (struct rcu_data *data)
- {
- data->gp_state = RCU_GP_STATE_WORK_FLUSH;
- data->nr_acks = 0;
- data->wid = RCU_WINDOW_ID_INIT_VALUE;
- for (size_t i = 0; i < ARRAY_SIZE (data->windows); i++)
- rcu_window_init (rcu_data_get_window_from_index (data, i));
- rcu_window_start (rcu_data_get_window (data, data->wid));
- timer_init (&data->timer, rcu_data_check_windows, 0);
- rcu_data_schedule_timer (data, clock_get_time ());
- syscnt_register (&data->sc_nr_windows, "rcu_nr_windows");
- syscnt_register (&data->sc_last_window_ms, "rcu_last_window_ms");
- syscnt_register (&data->sc_longest_window_ms, "rcu_longest_window_ms");
- }
- static void __init
- rcu_cpu_window_init (struct rcu_cpu_window *cpu_window)
- {
- work_queue_init (&cpu_window->works);
- }
- static void
- rcu_cpu_window_queue (struct rcu_cpu_window *cpu_window, struct work *work)
- {
- work_queue_push (&cpu_window->works, work);
- }
- static void
- rcu_cpu_window_flush (struct rcu_cpu_window *cpu_window)
- {
- int flg = cpu_window->works.nr_works > RCU_NR_WORKS_CUTOFF ?
- WORK_HIGHPRIO : 0;
- work_queue_schedule (&cpu_window->works, flg);
- work_queue_init (&cpu_window->works);
- }
- static uint32_t
- rcu_cpu_data_get_reader_wid (const struct rcu_cpu_data *cpu_data)
- {
- return (cpu_data->reader_wid);
- }
- static struct rcu_cpu_window*
- rcu_cpu_data_get_window_from_index (struct rcu_cpu_data *cpu_data, size_t index)
- {
- assert (index < ARRAY_SIZE (cpu_data->windows));
- return (&cpu_data->windows[index]);
- }
- static struct rcu_cpu_window*
- rcu_cpu_data_get_window (struct rcu_cpu_data *cpu_data, uint32_t wid)
- {
- return (rcu_cpu_data_get_window_from_index (cpu_data, wid & 1));
- }
- static void __init
- rcu_cpu_data_init (struct rcu_cpu_data *cpu_data, uint32_t cpu)
- {
- struct rcu_data *data = &rcu_data;
- cpu_data->gp_state = rcu_data_get_gp_state (data);
- cpu_data->work_wid = rcu_data_get_wid (data);
- cpu_data->reader_wid = cpu_data->work_wid;
- for (size_t i = 0; i < ARRAY_SIZE (cpu_data->windows); i++)
- rcu_cpu_window_init (rcu_cpu_data_get_window_from_index (cpu_data, i));
- char name[SYSCNT_NAME_SIZE];
- snprintf (name, sizeof (name), "rcu_nr_detected_readers/%u", cpu);
- syscnt_register (&cpu_data->sc_nr_detected_readers, name);
- }
- static void
- rcu_cpu_data_queue (struct rcu_cpu_data *cpu_data, struct work *work)
- {
- _Auto cpu_window = rcu_cpu_data_get_window (cpu_data, cpu_data->work_wid);
- rcu_cpu_window_queue (cpu_window, work);
- }
- static void
- rcu_cpu_data_flush (struct rcu_cpu_data *cpu_data)
- {
- assert (cpu_data->work_wid == cpu_data->reader_wid);
- _Auto wid = cpu_data->work_wid - 1;
- rcu_cpu_window_flush (rcu_cpu_data_get_window (cpu_data, wid));
- }
- void
- rcu_reader_init (struct rcu_reader *reader)
- {
- reader->level = 0;
- reader->linked = reader->saved_sched = false;
- }
- static void
- rcu_reader_link (struct rcu_reader *reader, struct rcu_cpu_data *cpu_data)
- {
- assert (!cpu_intr_enabled ());
- assert (reader == thread_rcu_reader (thread_self ()));
- assert (!rcu_reader_linked (reader));
- reader->wid = rcu_cpu_data_get_reader_wid (cpu_data);
- reader->linked = true;
- }
- static void
- rcu_reader_unlink (struct rcu_reader *reader)
- {
- assert (reader->level == 0);
- reader->linked = false;
- if (unlikely (reader->saved_sched))
- {
- _Auto thread = structof (reader, struct thread, rcu_reader);
- thread_setscheduler (thread, thread_user_sched_policy (thread),
- reader->saved_prio);
- reader->saved_sched = false;
- }
- }
- static void
- rcu_reader_enter (struct rcu_reader *reader, struct rcu_cpu_data *cpu_data)
- {
- if (rcu_reader_linked (reader))
- return;
- struct rcu_data *data = &rcu_data;
- uint32_t wid = rcu_cpu_data_get_reader_wid (cpu_data);
- _Auto window = rcu_data_get_window (data, wid);
- rcu_reader_link (reader, cpu_data);
- rcu_window_ref (window);
- syscnt_inc (&cpu_data->sc_nr_detected_readers);
- }
- void
- rcu_reader_leave (struct rcu_reader *reader)
- {
- struct rcu_data *data = &rcu_data;
- _Auto window = rcu_data_get_window (data, reader->wid);
- rcu_window_unref (window);
- rcu_reader_unlink (reader);
- }
- static bool
- rcu_reader_account (struct rcu_reader *reader, struct rcu_cpu_data *cpu_data)
- {
- if (!rcu_reader_in_cs (reader))
- return (false);
- rcu_reader_enter (reader, cpu_data);
- return (true);
- }
- static void
- rcu_cpu_data_flip_work_wid (struct rcu_cpu_data *cpu_data)
- {
- assert (!cpu_intr_enabled ());
- assert (!thread_preempt_enabled ());
- ++cpu_data->work_wid;
- }
- static void
- rcu_cpu_data_flip_reader_wid (struct rcu_cpu_data *cpu_data)
- {
- assert (!cpu_intr_enabled ());
- assert (!thread_preempt_enabled ());
- rcu_reader_account (thread_rcu_reader (thread_self ()), cpu_data);
- ++cpu_data->reader_wid;
- }
- static void
- rcu_cpu_data_check_gp_state (struct rcu_cpu_data *cpu_data)
- {
- struct rcu_data *data = &rcu_data;
-
- for (size_t i = 0; ; i++)
- {
- enum rcu_gp_state global_gp_state,
- local_gp_state = cpu_data->gp_state;
- bool diff = rcu_data_check_gp_state (data, local_gp_state,
- &global_gp_state);
- if (! diff)
- break;
- assert (i < 2);
- switch (global_gp_state)
- {
- case RCU_GP_STATE_WORK_WINDOW_FLIP:
- rcu_cpu_data_flip_work_wid (cpu_data);
- rcu_data_ack_cpu (data);
- break;
- case RCU_GP_STATE_READER_WINDOW_FLIP:
- rcu_cpu_data_flip_reader_wid (cpu_data);
- rcu_data_ack_cpu (data);
- break;
- case RCU_GP_STATE_WORK_FLUSH:
- rcu_cpu_data_flush (cpu_data);
- rcu_data_ack_cpu (data);
- break;
- default:
- panic ("rcu: invalid grace period state");
- }
- cpu_data->gp_state = global_gp_state;
- }
- }
- bool
- rcu_report_context_switch (struct rcu_reader *reader)
- {
- assert (!cpu_intr_enabled ());
- assert (!thread_preempt_enabled ());
-
- return (rcu_reader_account (reader, rcu_get_cpu_data ()));
- }
- void
- rcu_report_periodic_event (void)
- {
- assert (!cpu_intr_enabled ());
- assert (!thread_preempt_enabled ());
- rcu_cpu_data_check_gp_state (rcu_get_cpu_data ());
- }
- void
- rcu_defer (struct work *work)
- {
- assert (!rcu_reader_in_cs (thread_rcu_reader (thread_self ())));
- cpu_flags_t flags;
- thread_preempt_disable_intr_save (&flags);
- _Auto cpu_data = rcu_get_cpu_data ();
- rcu_cpu_data_queue (cpu_data, work);
- thread_preempt_enable_intr_restore (flags);
- }
- static void
- rcu_waiter_wakeup (struct work *work)
- {
- _Auto waiter = structof (work, struct rcu_waiter, work);
- SPINLOCK_GUARD (&waiter->lock);
- waiter->done = true;
- thread_wakeup (waiter->thread);
- }
- static void
- rcu_waiter_init (struct rcu_waiter *waiter, struct thread *thread)
- {
- work_init (&waiter->work, rcu_waiter_wakeup);
- spinlock_init (&waiter->lock);
- waiter->thread = thread;
- waiter->done = false;
- }
- static void
- rcu_waiter_wait (struct rcu_waiter *waiter)
- {
- rcu_defer (&waiter->work);
- SPINLOCK_GUARD (&waiter->lock);
- while (!waiter->done)
- thread_sleep (&waiter->lock, waiter, "rcu_wait");
- }
- void
- rcu_wait (void)
- {
- struct rcu_waiter waiter;
- rcu_waiter_init (&waiter, thread_self ());
- rcu_waiter_wait (&waiter);
- }
- static int __init
- rcu_bootstrap (void)
- {
- rcu_data_init (&rcu_data);
- rcu_cpu_data_init (cpu_local_ptr (rcu_cpu_data), 0);
- return (0);
- }
- INIT_OP_DEFINE (rcu_bootstrap,
- INIT_OP_DEP (spinlock_setup, true),
- INIT_OP_DEP (sref_bootstrap, true),
- INIT_OP_DEP (syscnt_setup, true),
- INIT_OP_DEP (thread_bootstrap, true),
- INIT_OP_DEP (timer_bootstrap, true));
- static int __init
- rcu_setup (void)
- {
- for (uint32_t i = 1; i < cpu_count (); i++)
- rcu_cpu_data_init (percpu_ptr (rcu_cpu_data, i), i);
- return (0);
- }
- INIT_OP_DEFINE (rcu_setup,
- INIT_OP_DEP (cpu_mp_probe, true),
- INIT_OP_DEP (rcu_bootstrap, true));
|