tools headers UAPI: Sync linux/prctl.h with the kernel sources
[linux-2.6-microblaze.git] / kernel / notifier.c
index 84c987d..1b019cb 100644 (file)
@@ -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);
 
 /**