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,
|
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);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
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,
|
||||||
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,6 +248,26 @@ 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,
|
||||||
|
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
|
* blocking_notifier_chain_register - Add notifier to a blocking notifier chain
|
||||||
* @nh: Pointer to head of the 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,
|
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
|
||||||
struct notifier_block *n)
|
struct notifier_block *n)
|
||||||
{
|
{
|
||||||
int ret;
|
return __blocking_notifier_chain_register(nh, n, false);
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
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);
|
||||||
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,
|
||||||
* 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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue