Commit c82f898d authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Rafael J. Wysocki

notifier: Add blocking/atomic_notifier_chain_register_unique_prio()

Add variant of blocking/atomic_notifier_chain_register() functions that
allow registration of a notifier only if it has unique priority, otherwise
-EBUSY error code is returned by the new functions.
Reviewed-by: default avatarMichał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: default avatarDmitry Osipenko <dmitry.osipenko@collabora.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 13dfd97a
...@@ -150,6 +150,11 @@ extern int raw_notifier_chain_register(struct raw_notifier_head *nh, ...@@ -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, extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
struct notifier_block *nb); 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, extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
struct notifier_block *nb); struct notifier_block *nb);
extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
......
...@@ -20,7 +20,8 @@ BLOCKING_NOTIFIER_HEAD(reboot_notifier_list); ...@@ -20,7 +20,8 @@ BLOCKING_NOTIFIER_HEAD(reboot_notifier_list);
*/ */
static int notifier_chain_register(struct notifier_block **nl, static int notifier_chain_register(struct notifier_block **nl,
struct notifier_block *n) struct notifier_block *n,
bool unique_priority)
{ {
while ((*nl) != NULL) { while ((*nl) != NULL) {
if (unlikely((*nl) == n)) { if (unlikely((*nl) == n)) {
...@@ -30,6 +31,8 @@ static int notifier_chain_register(struct notifier_block **nl, ...@@ -30,6 +31,8 @@ static int notifier_chain_register(struct notifier_block **nl,
} }
if (n->priority > (*nl)->priority) if (n->priority > (*nl)->priority)
break; break;
if (n->priority == (*nl)->priority && unique_priority)
return -EBUSY;
nl = &((*nl)->next); nl = &((*nl)->next);
} }
n->next = *nl; n->next = *nl;
...@@ -144,12 +147,35 @@ int atomic_notifier_chain_register(struct atomic_notifier_head *nh, ...@@ -144,12 +147,35 @@ int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
int ret; int ret;
spin_lock_irqsave(&nh->lock, flags); 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); spin_unlock_irqrestore(&nh->lock, flags);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(atomic_notifier_chain_register); 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 * atomic_notifier_chain_unregister - Remove notifier from an atomic notifier chain
* @nh: Pointer to head of the atomic notifier chain * @nh: Pointer to head of the atomic notifier chain
...@@ -222,18 +248,9 @@ bool atomic_notifier_call_chain_is_empty(struct atomic_notifier_head *nh) ...@@ -222,18 +248,9 @@ bool atomic_notifier_call_chain_is_empty(struct atomic_notifier_head *nh)
* synchronized by an rwsem. * synchronized by an rwsem.
*/ */
/** static int __blocking_notifier_chain_register(struct blocking_notifier_head *nh,
* blocking_notifier_chain_register - Add notifier to a blocking notifier chain struct notifier_block *n,
* @nh: Pointer to head of the blocking notifier chain bool unique_priority)
* @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)
{ {
int ret; int ret;
...@@ -243,15 +260,48 @@ int blocking_notifier_chain_register(struct blocking_notifier_head *nh, ...@@ -243,15 +260,48 @@ int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
* such times we must not call down_write(). * such times we must not call down_write().
*/ */
if (unlikely(system_state == SYSTEM_BOOTING)) 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); down_write(&nh->rwsem);
ret = notifier_chain_register(&nh->head, n); ret = notifier_chain_register(&nh->head, n, unique_priority);
up_write(&nh->rwsem); up_write(&nh->rwsem);
return ret; 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); 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 * blocking_notifier_chain_unregister - Remove notifier from a blocking notifier chain
* @nh: Pointer to head of the blocking notifier chain * @nh: Pointer to head of the blocking notifier chain
...@@ -354,7 +404,7 @@ EXPORT_SYMBOL_GPL(blocking_notifier_call_chain); ...@@ -354,7 +404,7 @@ EXPORT_SYMBOL_GPL(blocking_notifier_call_chain);
int raw_notifier_chain_register(struct raw_notifier_head *nh, int raw_notifier_chain_register(struct raw_notifier_head *nh,
struct notifier_block *n) 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); EXPORT_SYMBOL_GPL(raw_notifier_chain_register);
...@@ -433,10 +483,10 @@ int srcu_notifier_chain_register(struct srcu_notifier_head *nh, ...@@ -433,10 +483,10 @@ int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
* such times we must not call mutex_lock(). * such times we must not call mutex_lock().
*/ */
if (unlikely(system_state == SYSTEM_BOOTING)) 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); mutex_lock(&nh->mutex);
ret = notifier_chain_register(&nh->head, n); ret = notifier_chain_register(&nh->head, n, false);
mutex_unlock(&nh->mutex); mutex_unlock(&nh->mutex);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment