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