Linux 6.9-rc1
[linux-2.6-microblaze.git] / kernel / jump_label.c
index 714ac4c..d9c822b 100644 (file)
@@ -113,11 +113,40 @@ int static_key_count(struct static_key *key)
 }
 EXPORT_SYMBOL_GPL(static_key_count);
 
-void static_key_slow_inc_cpuslocked(struct static_key *key)
+/*
+ * static_key_fast_inc_not_disabled - adds a user for a static key
+ * @key: static key that must be already enabled
+ *
+ * The caller must make sure that the static key can't get disabled while
+ * in this function. It doesn't patch jump labels, only adds a user to
+ * an already enabled static key.
+ *
+ * Returns true if the increment was done. Unlike refcount_t the ref counter
+ * is not saturated, but will fail to increment on overflow.
+ */
+bool static_key_fast_inc_not_disabled(struct static_key *key)
 {
-       int v, v1;
+       int v;
 
        STATIC_KEY_CHECK_USE(key);
+       /*
+        * Negative key->enabled has a special meaning: it sends
+        * static_key_slow_inc() down the slow path, and it is non-zero
+        * so it counts as "enabled" in jump_label_update().  Note that
+        * atomic_inc_unless_negative() checks >= 0, so roll our own.
+        */
+       v = atomic_read(&key->enabled);
+       do {
+               if (v <= 0 || (v + 1) < 0)
+                       return false;
+       } while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v + 1)));
+
+       return true;
+}
+EXPORT_SYMBOL_GPL(static_key_fast_inc_not_disabled);
+
+bool static_key_slow_inc_cpuslocked(struct static_key *key)
+{
        lockdep_assert_cpus_held();
 
        /*
@@ -126,17 +155,9 @@ void static_key_slow_inc_cpuslocked(struct static_key *key)
         * jump_label_update() process.  At the same time, however,
         * the jump_label_update() call below wants to see
         * static_key_enabled(&key) for jumps to be updated properly.
-        *
-        * So give a special meaning to negative key->enabled: it sends
-        * static_key_slow_inc() down the slow path, and it is non-zero
-        * so it counts as "enabled" in jump_label_update().  Note that
-        * atomic_inc_unless_negative() checks >= 0, so roll our own.
         */
-       for (v = atomic_read(&key->enabled); v > 0; v = v1) {
-               v1 = atomic_cmpxchg(&key->enabled, v, v + 1);
-               if (likely(v1 == v))
-                       return;
-       }
+       if (static_key_fast_inc_not_disabled(key))
+               return true;
 
        jump_label_lock();
        if (atomic_read(&key->enabled) == 0) {
@@ -148,16 +169,23 @@ void static_key_slow_inc_cpuslocked(struct static_key *key)
                 */
                atomic_set_release(&key->enabled, 1);
        } else {
-               atomic_inc(&key->enabled);
+               if (WARN_ON_ONCE(!static_key_fast_inc_not_disabled(key))) {
+                       jump_label_unlock();
+                       return false;
+               }
        }
        jump_label_unlock();
+       return true;
 }
 
-void static_key_slow_inc(struct static_key *key)
+bool static_key_slow_inc(struct static_key *key)
 {
+       bool ret;
+
        cpus_read_lock();
-       static_key_slow_inc_cpuslocked(key);
+       ret = static_key_slow_inc_cpuslocked(key);
        cpus_read_unlock();
+       return ret;
 }
 EXPORT_SYMBOL_GPL(static_key_slow_inc);