Some notes on Notification Chain- Linux Kernel

First things first, Why I am writing about this? (P.S. Also known as notifier chains) There are tons of documentation available on a click away, Yet I care to write about it. Answer to this rhetorical question (if not yet) is I did not find a content which can tell me in few simple words “What it is ?” and allow me go back to my complex word of problems to deal with and allow me worry less about this. So, signing of from my prologue, because I know your work would be waiting.

As the name says Notification Chain is a chain which is use to deliver the notifications through Linux own implementation of publisher-subscriber model for its subsystems ready to use. How?
Well, the subscriber has to register the call back function with the publisher and implementation allows publisher to have custom registration method. Before we get into this puddle, let’s try to simplify the topic with good old fashioned way “with words”.

So Notification Chain in Linux Kernel are of four types and these types goes as :
Atomic : Runs in Atomic context, which implies should be safe to interrupt context and hence usually are utilized in scenarios where the notifications are crucial. Eg: Watchdog timer notification.
Blocking : As the name suggests, it could be blocked as these executes in process contextence suitable candidates for this type of chain is the scenario where the notifications are required in process context manner.Eg: QoS services for my display driver
Raw : One things which is intentionally missing in Blocking type is that there are locks available in the implementation of blocking notifier list but for some reason we want to the chains where we want to implement our own way of locking then Raw type suits it well say CPU internal frequency management.
SRCU : Sleepable Read Copy Update , if in case we don’t know about RCU then in simple words we can say that it’s the fastest locking mechanism available for complex data types, hence the use of this type makes the chain execution faster but everything comes with its own over head and in case of SRCU the overhead is that the deletion from list is resource consuming task hence suitable candidates will be the notifications chains which may stay for long cycles. May be on-reboot notification chain.

How to ? use it follow below steps:

  • Publisher initialise the list head by BLOCKING_NOTIFIER_HEAD() this macro is defined in include/linux/notifier.h.<Refer Code>.
  • Publisher exposes the list head through export_symbol() macro OR more preferred way as said is providing the custom API and manage it inside the published in spite of exposing the symbol to outside the world for unwanted changes. <Refer Code>
  • Subscribers will resiger to their function calls through struct notifier_block <Refer Code> to list head using the custom API and if directly list is exposed then directly to the list with below APIs[Set 1]. <Refer Code>
  • Once a event occurs add the API calls on the list head to execute all the registered functions (registered by subscribers) by APIs[Set 2].<Refer Code> In the referred code there are more calls too blocking_notifier_call_chain, those are all the events when the publisher (usbCore) wants to notify the subscriber.
  • Once done with the notifications usually during exit/cleanup calls the deregister custom API or if otherwise through [Set 3] APIs can be archived. <Refer Code PublisherSubscriber>
API Set 1 
extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
 struct notifier_block *nb);
extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
 struct notifier_block *nb);
extern int raw_notifier_chain_register(struct raw_notifier_head *nh,
 struct notifier_block *nb);
extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
 struct notifier_block *nb);
 extern int blocking_notifier_chain_cond_register( struct blocking_notifier_head *nh, struct notifier_block *nb);
 API Set 2
extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
 unsigned long val, void *v);
 extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v); 
 extern int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v); 
 extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v); 
API Set 3
extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
 struct notifier_block *nb);
extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
 struct notifier_block *nb);
extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
 struct notifier_block *nb);
extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
 struct notifier_block *nb);

I tried to explain the way I understood this topic, is your way/understanding different or want to have more detailed explanation on any of the topic feel free to leave a comment. It would be immense pleasure of our team to get back with/for different and fresh set of views.

Recent Update:
Get through this slide to have in-depth view on topic.

Leave a comment