forked from Minki/linux
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: Michał Mirosław <mirq-linux@rere.qmqm.pl> Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
13dfd97a34
commit
c82f898d87
@ -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,
|
||||
|
@ -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
|
||||
@ -222,6 +248,26 @@ bool atomic_notifier_call_chain_is_empty(struct atomic_notifier_head *nh)
|
||||
* synchronized by an rwsem.
|
||||
*/
|
||||
|
||||
static int __blocking_notifier_chain_register(struct blocking_notifier_head *nh,
|
||||
struct notifier_block *n,
|
||||
bool unique_priority)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* This code gets used during boot-up, when task switching is
|
||||
* not yet working and interrupts must remain disabled. At
|
||||
* such times we must not call down_write().
|
||||
*/
|
||||
if (unlikely(system_state == SYSTEM_BOOTING))
|
||||
return notifier_chain_register(&nh->head, n, unique_priority);
|
||||
|
||||
down_write(&nh->rwsem);
|
||||
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
|
||||
@ -235,23 +281,27 @@ bool atomic_notifier_call_chain_is_empty(struct atomic_notifier_head *nh)
|
||||
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
|
||||
struct notifier_block *n)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* This code gets used during boot-up, when task switching is
|
||||
* not yet working and interrupts must remain disabled. At
|
||||
* such times we must not call down_write().
|
||||
*/
|
||||
if (unlikely(system_state == SYSTEM_BOOTING))
|
||||
return notifier_chain_register(&nh->head, n);
|
||||
|
||||
down_write(&nh->rwsem);
|
||||
ret = notifier_chain_register(&nh->head, n);
|
||||
up_write(&nh->rwsem);
|
||||
return ret;
|
||||
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 @@ EXPORT_SYMBOL_GPL(blocking_notifier_call_chain);
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user