In the past couple of weeks I concentrated on Coccinelle, learning its syntax and figure how to write my Coccinelle scripts to do what I want in order to complete my tasks.
Let’s see the cases promised in the previous post where “RCU_INIT_POINTER()” can be replaced with “rcu_assign_pointer()”. These are the cases from “RCU_INIT_POINTER()’s” block comment:
1. This use of RCU_INIT_POINTER() is NULLing out the pointer -or-
2. The caller has taken whatever steps are required to prevent RCU readers from concurrently accessing this pointer -or-
3. The referenced data structure has already been exposed to readers either at compile time or via rcu_assign_pointer() -and-
a. You have not made -any- reader-visible changes to this structure since then -or-
b. It is OK for readers accessing this structure from its new location to see the old state of the structure. (For example, the changes were to statistical counters or to other state where exact synchronization is not required.)
The first case is the most simpler and the Coccinelle semantic patch is very short and easy:
@@ expression E; @@
– rcu_assign_pointer(E, NULL)
+ RCU_INIT_POINTER(E, NULL)
I thought about how I can write a Coccinelle script to detect the next two cases and it was a bit hard to find particular cases that would be worth writing a Coccinelle script and automatize a specific case (that is because of the different cases where “rcu_assign_pointer()” is used). So, together with my mentor, we decided that I should be writing a Coccinelle script which detects when you should keep the call “rcu_assign_pointer()” instead.
Currently, I am trying to detect when the value that is going to be assigned to the pointer is updated before “rcu_assign_pointer()” because this is one common case where “RCU_INIT_POINTER()” isn’t needed. One difficult case is that I have to find the call of the function where “v” is a parameter and after that, to verify if “v” is updated in the code of that function:
I will keep you updated and I am hoping that in the next few days I will complete what I have in my mind.
A week ago, I took a pause from the above task. I received an email from my mentor in which he offered me an extra task, if I wanted. The task consists of writing a Coccinelle script that detects use of “alloc_percpu()” that assigns to a global variable without either an “smp_mb()”, “smp_wmb()”, or “smp_store_release()”.
“alloc_percpu()” is a macro and it allocates one instance of the type given as a parameter for every processor on the system (all percpu areas are zeroed on allocation). There are cases where is the need for barriers to prevent one or more CPU’s to see garbage values.
I started by finding the calls “alloc_percpu()”. After that, I verified if the variable that the call assigns is a local or global variable. A variable which is not local, it is global. I used the metavariable type “position” offered by Coccinelle:
@ locally @
local idexpression l;
(<+…l…+>) =@p alloc_percpu(…)
@ globally @
position p != locally.p;
g@p = alloc_percpu(…)
Practically, I located all local variables and after that, I excluded them in order to remain with the global ones.
I encountered problems when I tested because in some cases the output said that a certain variable was global when it was not. Even with this problem, during one online meeting with my mentor we realised that harder things are to come. The detection of the barrier and if that barrier is what we are looking for or not, is not easy and maybe not suitable for a Coccinelle script.
So, after a little talking, we decided to change the way in which we approach this problem. We know for sure that if the variable that “alloc_percpu()” assigns is updated after that call we need a barrier.
I will make a Coccinelle script that detects if a global variable that receives memory through the call “alloc_percpu()” is initialized after the allocation.
In a few days, I will come up with the final results for these tasks . Also, I will tell you about my adventure with qemu, trying to run the kernel.