|
@@ -94,6 +94,34 @@ static int notifier_call_chain(struct notifier_block **nl,
|
|
|
}
|
|
|
NOKPROBE_SYMBOL(notifier_call_chain);
|
|
|
|
|
|
+/**
|
|
|
+ * notifier_call_chain_robust - Inform the registered notifiers about an event
|
|
|
+ * and rollback on error.
|
|
|
+ * @nl: Pointer to head of the blocking notifier chain
|
|
|
+ * @val_up: Value passed unmodified to the notifier function
|
|
|
+ * @val_down: Value passed unmodified to the notifier function when recovering
|
|
|
+ * from an error on @val_up
|
|
|
+ * @v Pointer passed unmodified to the notifier function
|
|
|
+ *
|
|
|
+ * NOTE: It is important the @nl chain doesn't change between the two
|
|
|
+ * invocations of notifier_call_chain() such that we visit the
|
|
|
+ * exact same notifier callbacks; this rules out any RCU usage.
|
|
|
+ *
|
|
|
+ * Returns: the return value of the @val_up call.
|
|
|
+ */
|
|
|
+static int notifier_call_chain_robust(struct notifier_block **nl,
|
|
|
+ unsigned long val_up, unsigned long val_down,
|
|
|
+ void *v)
|
|
|
+{
|
|
|
+ int ret, nr = 0;
|
|
|
+
|
|
|
+ ret = notifier_call_chain(nl, val_up, v, -1, &nr);
|
|
|
+ if (ret & NOTIFY_STOP_MASK)
|
|
|
+ notifier_call_chain(nl, val_down, v, nr-1, NULL);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Atomic notifier chain routines. Registration and unregistration
|
|
|
* use a spinlock, and call_chain is synchronized by RCU (no locks).
|
|
@@ -144,13 +172,30 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
|
|
|
|
|
|
+int atomic_notifier_call_chain_robust(struct atomic_notifier_head *nh,
|
|
|
+ unsigned long val_up, unsigned long val_down, void *v)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Musn't use RCU; because then the notifier list can
|
|
|
+ * change between the up and down traversal.
|
|
|
+ */
|
|
|
+ spin_lock_irqsave(&nh->lock, flags);
|
|
|
+ ret = notifier_call_chain_robust(&nh->head, val_up, val_down, v);
|
|
|
+ spin_unlock_irqrestore(&nh->lock, flags);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(atomic_notifier_call_chain_robust);
|
|
|
+NOKPROBE_SYMBOL(atomic_notifier_call_chain_robust);
|
|
|
+
|
|
|
/**
|
|
|
- * __atomic_notifier_call_chain - Call functions in an atomic notifier chain
|
|
|
+ * atomic_notifier_call_chain - Call functions in an atomic notifier chain
|
|
|
* @nh: Pointer to head of the atomic notifier chain
|
|
|
* @val: Value passed unmodified to notifier function
|
|
|
* @v: Pointer passed unmodified to notifier function
|
|
|
- * @nr_to_call: See the comment for notifier_call_chain.
|
|
|
- * @nr_calls: See the comment for notifier_call_chain.
|
|
|
*
|
|
|
* Calls each function in a notifier chain in turn. The functions
|
|
|
* run in an atomic context, so they must not block.
|
|
@@ -163,24 +208,16 @@ EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
|
|
|
* Otherwise the return value is the return value
|
|
|
* of the last notifier function called.
|
|
|
*/
|
|
|
-int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
|
|
|
- unsigned long val, void *v,
|
|
|
- int nr_to_call, int *nr_calls)
|
|
|
+int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
|
|
|
+ unsigned long val, void *v)
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
rcu_read_lock();
|
|
|
- ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
|
|
|
+ ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
|
|
|
rcu_read_unlock();
|
|
|
- return ret;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(__atomic_notifier_call_chain);
|
|
|
-NOKPROBE_SYMBOL(__atomic_notifier_call_chain);
|
|
|
|
|
|
-int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
|
|
|
- unsigned long val, void *v)
|
|
|
-{
|
|
|
- return __atomic_notifier_call_chain(nh, val, v, -1, NULL);
|
|
|
+ return ret;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(atomic_notifier_call_chain);
|
|
|
NOKPROBE_SYMBOL(atomic_notifier_call_chain);
|
|
@@ -250,13 +287,30 @@ int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);
|
|
|
|
|
|
+int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh,
|
|
|
+ unsigned long val_up, unsigned long val_down, void *v)
|
|
|
+{
|
|
|
+ int ret = NOTIFY_DONE;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We check the head outside the lock, but if this access is
|
|
|
+ * racy then it does not matter what the result of the test
|
|
|
+ * is, we re-check the list after having taken the lock anyway:
|
|
|
+ */
|
|
|
+ if (rcu_access_pointer(nh->head)) {
|
|
|
+ down_read(&nh->rwsem);
|
|
|
+ ret = notifier_call_chain_robust(&nh->head, val_up, val_down, v);
|
|
|
+ up_read(&nh->rwsem);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(blocking_notifier_call_chain_robust);
|
|
|
+
|
|
|
/**
|
|
|
- * __blocking_notifier_call_chain - Call functions in a blocking notifier chain
|
|
|
+ * blocking_notifier_call_chain - Call functions in a blocking notifier chain
|
|
|
* @nh: Pointer to head of the blocking notifier chain
|
|
|
* @val: Value passed unmodified to notifier function
|
|
|
* @v: Pointer passed unmodified to notifier function
|
|
|
- * @nr_to_call: See comment for notifier_call_chain.
|
|
|
- * @nr_calls: See comment for notifier_call_chain.
|
|
|
*
|
|
|
* Calls each function in a notifier chain in turn. The functions
|
|
|
* run in a process context, so they are allowed to block.
|
|
@@ -268,9 +322,8 @@ EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);
|
|
|
* Otherwise the return value is the return value
|
|
|
* of the last notifier function called.
|
|
|
*/
|
|
|
-int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
|
|
|
- unsigned long val, void *v,
|
|
|
- int nr_to_call, int *nr_calls)
|
|
|
+int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
|
|
|
+ unsigned long val, void *v)
|
|
|
{
|
|
|
int ret = NOTIFY_DONE;
|
|
|
|
|
@@ -281,19 +334,11 @@ int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
|
|
|
*/
|
|
|
if (rcu_access_pointer(nh->head)) {
|
|
|
down_read(&nh->rwsem);
|
|
|
- ret = notifier_call_chain(&nh->head, val, v, nr_to_call,
|
|
|
- nr_calls);
|
|
|
+ ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
|
|
|
up_read(&nh->rwsem);
|
|
|
}
|
|
|
return ret;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(__blocking_notifier_call_chain);
|
|
|
-
|
|
|
-int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
|
|
|
- unsigned long val, void *v)
|
|
|
-{
|
|
|
- return __blocking_notifier_call_chain(nh, val, v, -1, NULL);
|
|
|
-}
|
|
|
EXPORT_SYMBOL_GPL(blocking_notifier_call_chain);
|
|
|
|
|
|
/*
|
|
@@ -335,13 +380,18 @@ int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);
|
|
|
|
|
|
+int raw_notifier_call_chain_robust(struct raw_notifier_head *nh,
|
|
|
+ unsigned long val_up, unsigned long val_down, void *v)
|
|
|
+{
|
|
|
+ return notifier_call_chain_robust(&nh->head, val_up, val_down, v);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(raw_notifier_call_chain_robust);
|
|
|
+
|
|
|
/**
|
|
|
- * __raw_notifier_call_chain - Call functions in a raw notifier chain
|
|
|
+ * raw_notifier_call_chain - Call functions in a raw notifier chain
|
|
|
* @nh: Pointer to head of the raw notifier chain
|
|
|
* @val: Value passed unmodified to notifier function
|
|
|
* @v: Pointer passed unmodified to notifier function
|
|
|
- * @nr_to_call: See comment for notifier_call_chain.
|
|
|
- * @nr_calls: See comment for notifier_call_chain
|
|
|
*
|
|
|
* Calls each function in a notifier chain in turn. The functions
|
|
|
* run in an undefined context.
|
|
@@ -354,18 +404,10 @@ EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);
|
|
|
* Otherwise the return value is the return value
|
|
|
* of the last notifier function called.
|
|
|
*/
|
|
|
-int __raw_notifier_call_chain(struct raw_notifier_head *nh,
|
|
|
- unsigned long val, void *v,
|
|
|
- int nr_to_call, int *nr_calls)
|
|
|
-{
|
|
|
- return notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(__raw_notifier_call_chain);
|
|
|
-
|
|
|
int raw_notifier_call_chain(struct raw_notifier_head *nh,
|
|
|
unsigned long val, void *v)
|
|
|
{
|
|
|
- return __raw_notifier_call_chain(nh, val, v, -1, NULL);
|
|
|
+ return notifier_call_chain(&nh->head, val, v, -1, NULL);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(raw_notifier_call_chain);
|
|
|
|
|
@@ -437,12 +479,10 @@ int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
|
|
|
EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);
|
|
|
|
|
|
/**
|
|
|
- * __srcu_notifier_call_chain - Call functions in an SRCU notifier chain
|
|
|
+ * srcu_notifier_call_chain - Call functions in an SRCU notifier chain
|
|
|
* @nh: Pointer to head of the SRCU notifier chain
|
|
|
* @val: Value passed unmodified to notifier function
|
|
|
* @v: Pointer passed unmodified to notifier function
|
|
|
- * @nr_to_call: See comment for notifier_call_chain.
|
|
|
- * @nr_calls: See comment for notifier_call_chain
|
|
|
*
|
|
|
* Calls each function in a notifier chain in turn. The functions
|
|
|
* run in a process context, so they are allowed to block.
|
|
@@ -454,25 +494,17 @@ EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);
|
|
|
* Otherwise the return value is the return value
|
|
|
* of the last notifier function called.
|
|
|
*/
|
|
|
-int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
|
|
|
- unsigned long val, void *v,
|
|
|
- int nr_to_call, int *nr_calls)
|
|
|
+int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
|
|
|
+ unsigned long val, void *v)
|
|
|
{
|
|
|
int ret;
|
|
|
int idx;
|
|
|
|
|
|
idx = srcu_read_lock(&nh->srcu);
|
|
|
- ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls);
|
|
|
+ ret = notifier_call_chain(&nh->head, val, v, -1, NULL);
|
|
|
srcu_read_unlock(&nh->srcu, idx);
|
|
|
return ret;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(__srcu_notifier_call_chain);
|
|
|
-
|
|
|
-int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
|
|
|
- unsigned long val, void *v)
|
|
|
-{
|
|
|
- return __srcu_notifier_call_chain(nh, val, v, -1, NULL);
|
|
|
-}
|
|
|
EXPORT_SYMBOL_GPL(srcu_notifier_call_chain);
|
|
|
|
|
|
/**
|