# RCU usage ## Simple ```clike= struct myconfig { int a, b;} *curconfig; /* Returns true if the global is not NULL */ bool get(int *cur_a, int *cur_b) { struct myconfig *mcp; rcu_read_lock(); mcp = rcu_dereference(curconfig); if (mcp) { if (cur_a) *cur_a = mcp->a; if (cur_b) *cur_b = mcp->b; } rcu_read_unlock(); return mcp != NULL; } // Typical Writer bool set(int cur_a, int cur_b) { struct myconfig *mcp = kmalloc(...); struct myconfig *oldmcp; if (!mcp) return false; mcp->a = cur_a; mcp->b = cur_b; spin_lock(&mylock); oldmcp = rcu_dereference_protected(curconfig, lockdep_is_held(&mylock)); rcu_assign_pointer(curconfig, mcp); spin_unlock(&mylock); synchronize_rcu(); kfree(oldmcp); return true; } // Atomic Writer bool set(int cur_a, int cur_b) { struct myconfig *mcp = kmalloc(...); if (mcp) return false; mcp->a = cur_a; mcp->b = cur_b; mcp = xchg(&curconfig, mcp); synchronize_rcu(); kfree(mcp); return tru e; } ``` ## Rust version: ```rust= /// RCU read-side critical section guards. struct RcuGuard<'a> { phantom: PhantomData<&'a> } fn rcu_read_lock<'a>() -> RcuGuard<'a> { unsafe { bindings::rcu_read_lock(); } RcuGuard { phantom: PhantomData } } /// RAII: RcuGuard drop == unlock. impl<'a> Drop for RcuGuard<'a> { fn drop(&mut self) { unsafe { bindings::rcu_read_unlock(); } } } /// A pointer to RCU-protected data. struct RcuPtr<P: ForeignOwnable> { ptr: AtomicPtr, phantom: PhantomData<P> } struct RcuPtrOld<P: ForeignOwnable> { ptr: *mut (), phantom: PhantomData<P> } impl<P: ForeignOwnable> Drop for RcuPtrOld<P> { fn drop(&mut self) { // SAFETY: Always? unsafe { bindings::synchronize_rcu(); } <P as ForeignOwnable>::from_foreign(self.ptr); } } impl<P: ForeignOwnable> Drop for RcuPtr<P> { fn drop(&mut self) { // SAFETY: Always? unsafe { bindings::synchronize_rcu(); } <P as ForeignOwnable>::from_foreign(self.ptr.load(Relaxed)); } } impl<P: ForeignOwnable> RcuPtr<P> { pub fn new(p: P) -> Self { RcuBox { // INVARIANTS: Sets `self.ptr` to a pointer to boxed objects. ptr: AtomicPtr::new(P::into_foreign(p)), phantom: PhantomData } } /// Update but not atomic pub fn swap(&mut self, new: P) -> RcuBoxOld<T> { // SAFETY: Per INVARIANTS let old_ptr = self.ptr.load(Relaxed); // INVARIANTS: Sets `self.ptr` to a pointer to boxed objects. self.ptr.store(P::into_foreign(new), Release); RcuBoxOld { ptr: old_ptr, phantom: PhantomData } } /// Atomic update. pub fn atomic_swap(&self, new: P) -> RcuBoxOld<T> { // INVARIANTS: `new_ptr` always points to a boxed object. let new_ptr = P::into_foreign(new); let old_ptr = self.ptr.swap(new_ptr, Release); // SAFETY: Per INVARIANTS RcuBoxOld { ptr: old_ptr, phantom: PhantomData } } /// rcu_dereference(), gets a immutable reference to the object. /// /// Lifetime/borrow checking guarantee the reference cannot outlive RCU /// read-side locks. pub fn get<'a>(&self, guard: &RcuGuard<'a>) -> P::Borrowed<'a> { let ptr = self.ptr.load(Acqure); // Need `Consume` but not available. // SAFETY: Per INVARIANTS, `ptr` is a valid pointer, // and RCU guarantees no data race. unsafe { P::borrow(ptr) } } } struct Global { config: RcuBox<Config> // "struct config *", i.e. a pointer to the heap. } impl Global { fn get(&self) -> (i32, i32) { // "&self" is a reference to "Global" // struct global *self = .. ; let guard = rcu_read_lock(); let c = self.config.get(guard); // struct config *c = self->config; (c.a, c.b) // implicit `drop(guard)` here // `c` will not live longer then `guard`, so `c` cannot be used // afer `guard` dropped. } fn atomic_set(&self, a: i32, b: i32) { let new = Box::new(Config {a, b}); self.config.atomic_swap(new); // implicit drop the return value. } } struct Config { a: i32, b: i32, } ``` ## List (TODO) ## Phased state change ```clike= bool be_careful; // Common-Case Operation void cco(void) { rcu_read_lock(); if (READ_ONCE(be_careful)) cco_carefully(); else cco_quickly(); rcu_read_unlock(); } // Maintenance Operation void maint(void) { WRITE_ONCE(be_careful, true); synchronize_rcu(); do_maint(); synchronize_rcu(); WRITE_ONCE(be_careful, false); } ```