RCU Kernel Implementation
In our last session, we learned how to use RCU API as a synchronization mechanism using a sample driver. Please visit the last session if you missed.
In this blog, we will be going through Kernel Implementation of RCU API.
The below diagram depicts the overall RCU API used for synchronization.
rcu_read_lock() -
The kernel implementation is as follows -
static inline void rcu_read_lock(void)
{
__rcu_read_lock();
__acquire(RCU);
rcu_lock_acquire(&rcu_lock_map);
rcu_lockdep_assert(rcu_is_watching(),
"rcu_read_lock() used illegally while idle");
}
Basically the above function disables the preemption on the particular cpu on which it is running.
So that no other process can preempt this process and the process completes its execution without interruption. After holding rcu_read_lock() it is illegal for the process to explicitly call sleep.
rcu_read_unlock()-
static inline void rcu_read_unlock(void)
{
rcu_lockdep_assert(rcu_is_watching(),
"rcu_read_unlock() used illegally while idle");
rcu_lock_release(&rcu_lock_map);
__release(RCU);
__rcu_read_unlock();
}
{
synchronize_sched();
}
rcu_assaign_pointer()-
This is a macro which assigns the RCU protected pointer with the value specified by the user.so that the further reader can get the address to read the data.
rcu_derefernce()-
#define rcu_dereference(p) rcu_dereference_check(p, 0)
In this blog, we will be going through Kernel Implementation of RCU API.
The below diagram depicts the overall RCU API used for synchronization.
rcu_read_lock() -
The kernel implementation is as follows -
static inline void rcu_read_lock(void)
{
__rcu_read_lock();
__acquire(RCU);
rcu_lock_acquire(&rcu_lock_map);
rcu_lockdep_assert(rcu_is_watching(),
"rcu_read_lock() used illegally while idle");
}
Basically the above function disables the preemption on the particular cpu on which it is running.
So that no other process can preempt this process and the process completes its execution without interruption. After holding rcu_read_lock() it is illegal for the process to explicitly call sleep.
rcu_read_unlock()-
static inline void rcu_read_unlock(void)
{
rcu_lockdep_assert(rcu_is_watching(),
"rcu_read_unlock() used illegally while idle");
rcu_lock_release(&rcu_lock_map);
__release(RCU);
__rcu_read_unlock();
}
rcu_read_unlock() will do the inverse process as rcu_read_lock() did. rcu_read_unlock() will enable the preemtion and throws error if the particular CPU is idle.
synchronize_rcu()-
static inline void synchronize_rcu(void){
synchronize_sched();
}
Synchronize_rcu() is used do updates in the Linked List. In order to free the deleted/updated node from the linked list, all other pre-existing readers must have completed their read-side critical section.
It is blocking call and blocks until all Read side critical section is completed. This is identified by running the current thread on all other CPU.
call_rcu()-
int (*call_rcu)(struct sock *nl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[]);
call_rcu() is a callback function where corresponding callback handler is registered for freeing the deleted/updated node from the linked list. This function works in a similar way as syncronize_rcu() works. The callback will be invoked when all read-side critical section is completed.
rcu_assaign_pointer()-
This is a macro which assigns the RCU protected pointer with the value specified by the user.so that the further reader can get the address to read the data.
rcu_derefernce()-
#define rcu_dereference(p) rcu_dereference_check(p, 0)
rcu_dereference will initialize the RCU protected pointer variable with zero as shown above. So that further reader does not read the list
Comments
Post a Comment