/* Ajith - Syntax Higlighter - End ----------------------------------------------- */

1.17.2012

Notification Chains in Linux Kernel - Part 02

Continuation after PART-1.

Check the PART-3

Blocking Notifier chains
A blocking notifier chain runs in the process context. The calls in the notification list could be blocked as it runs in the process context. Notifications that are not highly time critical could use blocking notifier chains.

Linux modules use blocking notifier chains to inform the modules on a change in QOS value or the addition of a new device.
<kernel/notifier.c>

186 int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
187         struct notifier_block *n)
188 {
.
199     down_write(&nh->rwsem);
200     ret = notifier_chain_register(&nh->head, n);
201     up_write(&nh->rwsem);
202     return ret;
203 }
204 EXPORT_SYMBOL_GPL(blocking_notifier_chain_register)
.
216 int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
217         struct notifier_block *n)
218 {
.
229     down_write(&nh->rwsem);
230     ret = notifier_chain_unregister(&nh->head, n);
231     up_write(&nh->rwsem);
232     return ret;
233 }
234 EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);

Raw Notifier chains 
A raw notifier chain does not manage the locking and protection of the callers. Also, there are no restrictions on callbacks, registration, or de-registration. It provides flexibility to the user to have individual lock and protection mechanisms.

Linux uses the raw notifier chain in low-level events.
<kernel/notifier.c>

297 int raw_notifier_chain_register(struct raw_notifier_head *nh,
298         struct notifier_block *n)
299 {
300     return notifier_chain_register(&nh->head, n);
301 }
302 EXPORT_SYMBOL_GPL(raw_notifier_chain_register);
.
314 int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
315         struct notifier_block *n)
316 {
317     return notifier_chain_unregister(&nh->head, n);
318 }
319 EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);

SRCU Notifier chains 
Sleepable Read Copy Update (SRCU) notifier chains are similar to the blocking notifier chain and run in the process context. It differs in the way it handles locking and protection. The SRCU methodology brings in less overhead when we notify the registered callers. On the flip side, it consumes more resource while unregistering. So it is advisable to choose this methodology where we use the notifier call often and where there’s very little requirement for removing from the chain. 
<kernel/notifier.c>

370 int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
371         struct notifier_block *n)
372 {
.
383     mutex_lock(&nh->mutex);
384     ret = notifier_chain_register(&nh->head, n);
385     mutex_unlock(&nh->mutex);
386     return ret;
387 }
388 EXPORT_SYMBOL_GPL(srcu_notifier_chain_register)
.
400 int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
401         struct notifier_block *n)
402 {
.
413     mutex_lock(&nh->mutex);
414     ret = notifier_chain_unregister(&nh->head, n);
415     mutex_unlock(&nh->mutex);
416     synchronize_srcu(&nh->srcu);
417     return ret;
418 }
419 EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);

Registering and Un-registering with a Chain 
When a kernel component is interested in the events of a given notification chain, it can register it with the general function notifier_chain_register and to unregister we can use the function notifier_chain_unregister.

NOTE: The kernel also provides a set of wrappers around notifier_chain_register. We might have to use those wrapper functions instead of directly using the notification_chain_register.
<kernel/notifier.c>

 20 static int notifier_chain_register(struct notifier_block **nl,
 21         struct notifier_block *n)
 22 {
 23     while ((*nl) != NULL) {
 24         if (n->priority > (*nl)->priority)
 25             break;
 26         nl = &((*nl)->next);
 27     }
 28     n->next = *nl;
 29     rcu_assign_pointer(*nl, n);
 30     return 0;
 31 }

For each chain, the notifier_block instances are inserted into a list, which is sorted by priority. Elements with the same priority are sorted based on insertion time: new ones go to the tail. Accesses to the notification chains are protected by the notifier_lock lock. The use of a single lock for all the notification chains is not a big constraint and does not affect performance, because subsystems usually register their notifier_call functions only at boot time or at module load time, and from that moment on access the lists in a read-only manner (that is, shared).

Because the notifier_chain_register function is called to insert callbacks into all lists, it requires that the list be specified as an input parameter. However, this function is rarely called directly; generic wrappers are used instead.
<kernel/notifier.c>

 33 static int notifier_chain_unregister(struct notifier_block **nl,
 34         struct notifier_block *n)
 35 {
 36     while ((*nl) != NULL) {
 37         if ((*nl) == n) {
 38             rcu_assign_pointer(*nl, n->next);
 39             return 0;
 40         }
 41         nl = &((*nl)->next);
 42     }
 43     return -ENOENT;
 44 }

No comments :

Post a Comment

Your comments are moderated