Annotations for Sparse context imbalance warnings

Sparse has a validation folder which includes rules in form of C files, that a target source code should meet; if it does not , warnings and errors will be thrown once it is compiled with Sparse enabled.

https://git.kernel.org/pub/scm/devel/sparse/sparse.git/tree/validation/attr-context.c
https://git.kernel.org/pub/scm/devel/sparse/sparse.git/tree/validation/context-stmt.c
https://git.kernel.org/pub/scm/devel/sparse/sparse.git/tree/validation/context.c

There are many types of warnings that Sparse throws, in this article , we will particularly  focus on one type of these warnings: the context imbalance warnings.

Given the fact that locks coordinate the operations of multiple running processes to prevent conflicts or race condition, Context imbalance warnings occur when a possible locking fault is detected in the source code. This can be an acquire lock without the release counterpart or vice verca.

For more details about lock, click on the link.

Fixing context imbalance warnings may either require adding the right annotations or refactoring the code. It is the responsibility of the programmer to find the appropriate solution.

We discussed so far about Sparse and its context imbalance. It’s time for annotation.

What is an annotation ? Well, a small piece of code that conveys information about how functions handle locks; a function acquires lock when entering a critical section, releases when exiting this critical section, lock can also be held at entry and exit.

Having said that , there are three types of annotations describing the three situations above:

__must_hold – The specified lock is held on function entry and exit.

__acquires – The specified lock is held on function exit, but not entry.

__releases – The specified lock is held on function entry, but not exit.

These annotations are actually macros, undefined for GCC but defined during a Sparse run to use the “context” tracking feature of sparse, applied to locking. Annotations tell sparse when a lock is held, with regard to the annotated function’s entry and exit.

Annotations are however not always needed: If the function enters and exits without the lock held, acquiring and releasing the lock inside the function in a balanced way, no annotation is needed.

eg.

unsigned int kstat_irqs_usr(unsigned int irq)
{
        unsigned int sum;

        rcu_read_lock();
        sum = kstat_irqs(irq);
        rcu_read_unlock();
        return sum;
}
      

The three annotations cited above are for cases where Sparse would otherwise report a context imbalance.

At definition, the annotations or macro functions respectively __must_hold(x), __acquires(x) and __releases(x) have each the extended attribute.

This can be found in the file include/linux/compiler_types.h of the linux kernel source code.

# define __must_hold(x) __attribute__((context(x,1,1)))
# define __acquires(x)  __attribute__((context(x,0,1)))
# define __releases(x)  __attribute__((context(x,1,0)))
# define __acquire(x)   __context__(x,1)
# define __release(x)   __context__(x,-1)

Our focus is is on the first three macro.

__attribute__ is a __ GCC__ extension , it can refer a return type or data type .

__context__  is a Sparse specific specifier  .

we can easily see that the format is :

__attribute__((context(expression,in_context,out_context))

The functions require the context expression (for instance, a lock) to have the
value in_context (a constant non negative integer) when called, and return
with the value out_context (a constant non negative integer).


In the case of

 __must_hold(x) is defined as
 # define __must_hold(x) __attribute__((context(x,1,1)))

The expression is x in_context = 1 out_context = 1
  __acquires(x)
# define __acquires(x)  __attribute__((context(x,0,1)))
The expression x or lock is disabled when in_context = 0 before then enter critical section x becomes enable and out_context = 1
__releases(x)
 # define __releases(x)  __attribute__((context(x,1,0)))
expression x in_context = 1 and out_context = 0

When an API is defined with a macro, the specifier __attribute__((context(…))) can be replaced
by __context__(…). Hence the above can be rewritten as

 # define __acquire(x)   __context__(x,1)
 # define __release(x)   __context__(x,-1)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s