[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v7 02/20] notifier: Add atomic/blocking_notifier_chain_register_unique_prio()



Add variant of atomic/blocking_notifier_chain_register() functions that
doesn't allow to register notifier using a duplicated priority. The -EBUSY
error code is returned in this case by the new API functions.

Signed-off-by: Dmitry Osipenko <dmitry.osipenko@xxxxxxxxxxxxx>
---
 include/linux/notifier.h |  5 +++
 kernel/notifier.c        | 88 +++++++++++++++++++++++++++++++---------
 2 files changed, 74 insertions(+), 19 deletions(-)

diff --git a/include/linux/notifier.h b/include/linux/notifier.h
index d4717bc0ab85..ccce26197dd2 100644
--- a/include/linux/notifier.h
+++ b/include/linux/notifier.h
@@ -150,6 +150,11 @@ extern int raw_notifier_chain_register(struct 
raw_notifier_head *nh,
 extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
                struct notifier_block *nb);
 
+extern int atomic_notifier_chain_register_unique_prio(
+               struct atomic_notifier_head *nh, struct notifier_block *nb);
+extern int blocking_notifier_chain_register_unique_prio(
+               struct blocking_notifier_head *nh, struct notifier_block *nb);
+
 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,
diff --git a/kernel/notifier.c b/kernel/notifier.c
index 4ed6bda8f127..4fc32b1e6cbb 100644
--- a/kernel/notifier.c
+++ b/kernel/notifier.c
@@ -20,7 +20,8 @@ BLOCKING_NOTIFIER_HEAD(reboot_notifier_list);
  */
 
 static int notifier_chain_register(struct notifier_block **nl,
-                                  struct notifier_block *n)
+                                  struct notifier_block *n,
+                                  bool unique_priority)
 {
        while ((*nl) != NULL) {
                if (unlikely((*nl) == n)) {
@@ -30,6 +31,8 @@ static int notifier_chain_register(struct notifier_block **nl,
                }
                if (n->priority > (*nl)->priority)
                        break;
+               if (n->priority == (*nl)->priority && unique_priority)
+                       return -EBUSY;
                nl = &((*nl)->next);
        }
        n->next = *nl;
@@ -144,12 +147,35 @@ int atomic_notifier_chain_register(struct 
atomic_notifier_head *nh,
        int ret;
 
        spin_lock_irqsave(&nh->lock, flags);
-       ret = notifier_chain_register(&nh->head, n);
+       ret = notifier_chain_register(&nh->head, n, false);
        spin_unlock_irqrestore(&nh->lock, flags);
        return ret;
 }
 EXPORT_SYMBOL_GPL(atomic_notifier_chain_register);
 
+/**
+ *     atomic_notifier_chain_register_unique_prio - Add notifier to an atomic 
notifier chain
+ *     @nh: Pointer to head of the atomic notifier chain
+ *     @n: New entry in notifier chain
+ *
+ *     Adds a notifier to an atomic notifier chain if there is no other
+ *     notifier registered using the same priority.
+ *
+ *     Returns 0 on success, %-EEXIST or %-EBUSY on error.
+ */
+int atomic_notifier_chain_register_unique_prio(struct atomic_notifier_head *nh,
+                                              struct notifier_block *n)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&nh->lock, flags);
+       ret = notifier_chain_register(&nh->head, n, true);
+       spin_unlock_irqrestore(&nh->lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(atomic_notifier_chain_register_unique_prio);
+
 /**
  *     atomic_notifier_chain_unregister - Remove notifier from an atomic 
notifier chain
  *     @nh: Pointer to head of the atomic notifier chain
@@ -209,18 +235,9 @@ NOKPROBE_SYMBOL(atomic_notifier_call_chain);
  *     synchronized by an rwsem.
  */
 
-/**
- *     blocking_notifier_chain_register - Add notifier to a blocking notifier 
chain
- *     @nh: Pointer to head of the blocking notifier chain
- *     @n: New entry in notifier chain
- *
- *     Adds a notifier to a blocking notifier chain.
- *     Must be called in process context.
- *
- *     Returns 0 on success, %-EEXIST on error.
- */
-int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
-               struct notifier_block *n)
+static int __blocking_notifier_chain_register(struct blocking_notifier_head 
*nh,
+                                             struct notifier_block *n,
+                                             bool unique_priority)
 {
        int ret;
 
@@ -230,15 +247,48 @@ int blocking_notifier_chain_register(struct 
blocking_notifier_head *nh,
         * such times we must not call down_write().
         */
        if (unlikely(system_state == SYSTEM_BOOTING))
-               return notifier_chain_register(&nh->head, n);
+               return notifier_chain_register(&nh->head, n, unique_priority);
 
        down_write(&nh->rwsem);
-       ret = notifier_chain_register(&nh->head, n);
+       ret = notifier_chain_register(&nh->head, n, unique_priority);
        up_write(&nh->rwsem);
        return ret;
 }
+
+/**
+ *     blocking_notifier_chain_register - Add notifier to a blocking notifier 
chain
+ *     @nh: Pointer to head of the blocking notifier chain
+ *     @n: New entry in notifier chain
+ *
+ *     Adds a notifier to a blocking notifier chain.
+ *     Must be called in process context.
+ *
+ *     Returns 0 on success, %-EEXIST on error.
+ */
+int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
+               struct notifier_block *n)
+{
+       return __blocking_notifier_chain_register(nh, n, false);
+}
 EXPORT_SYMBOL_GPL(blocking_notifier_chain_register);
 
+/**
+ *     blocking_notifier_chain_register_unique_prio - Add notifier to a 
blocking notifier chain
+ *     @nh: Pointer to head of the blocking notifier chain
+ *     @n: New entry in notifier chain
+ *
+ *     Adds a notifier to an blocking notifier chain if there is no other
+ *     notifier registered using the same priority.
+ *
+ *     Returns 0 on success, %-EEXIST or %-EBUSY on error.
+ */
+int blocking_notifier_chain_register_unique_prio(struct blocking_notifier_head 
*nh,
+                                                struct notifier_block *n)
+{
+       return __blocking_notifier_chain_register(nh, n, true);
+}
+EXPORT_SYMBOL_GPL(blocking_notifier_chain_register_unique_prio);
+
 /**
  *     blocking_notifier_chain_unregister - Remove notifier from a blocking 
notifier chain
  *     @nh: Pointer to head of the blocking notifier chain
@@ -354,7 +404,7 @@ bool blocking_notifier_call_chain_is_empty(struct 
blocking_notifier_head *nh)
 int raw_notifier_chain_register(struct raw_notifier_head *nh,
                struct notifier_block *n)
 {
-       return notifier_chain_register(&nh->head, n);
+       return notifier_chain_register(&nh->head, n, false);
 }
 EXPORT_SYMBOL_GPL(raw_notifier_chain_register);
 
@@ -433,10 +483,10 @@ int srcu_notifier_chain_register(struct 
srcu_notifier_head *nh,
         * such times we must not call mutex_lock().
         */
        if (unlikely(system_state == SYSTEM_BOOTING))
-               return notifier_chain_register(&nh->head, n);
+               return notifier_chain_register(&nh->head, n, false);
 
        mutex_lock(&nh->mutex);
-       ret = notifier_chain_register(&nh->head, n);
+       ret = notifier_chain_register(&nh->head, n, false);
        mutex_unlock(&nh->mutex);
        return ret;
 }
-- 
2.35.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.