mm: correctly synchronize rss-counters at exit/exec
[linux-2.6.git] / kernel / jump_label.c
index e6f1f24..4304919 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/slab.h>
 #include <linux/sort.h>
 #include <linux/err.h>
-#include <linux/jump_label.h>
+#include <linux/static_key.h>
 
 #ifdef HAVE_JUMP_LABEL
 
@@ -29,11 +29,6 @@ void jump_label_unlock(void)
        mutex_unlock(&jump_label_mutex);
 }
 
-bool jump_label_enabled(struct jump_label_key *key)
-{
-       return !!atomic_read(&key->enabled);
-}
-
 static int jump_label_cmp(const void *a, const void *b)
 {
        const struct jump_entry *jea = a;
@@ -58,29 +53,72 @@ jump_label_sort_entries(struct jump_entry *start, struct jump_entry *stop)
        sort(start, size, sizeof(struct jump_entry), jump_label_cmp, NULL);
 }
 
-static void jump_label_update(struct jump_label_key *key, int enable);
+static void jump_label_update(struct static_key *key, int enable);
 
-void jump_label_inc(struct jump_label_key *key)
+void static_key_slow_inc(struct static_key *key)
 {
        if (atomic_inc_not_zero(&key->enabled))
                return;
 
        jump_label_lock();
-       if (atomic_read(&key->enabled) == 0)
-               jump_label_update(key, JUMP_LABEL_ENABLE);
+       if (atomic_read(&key->enabled) == 0) {
+               if (!jump_label_get_branch_default(key))
+                       jump_label_update(key, JUMP_LABEL_ENABLE);
+               else
+                       jump_label_update(key, JUMP_LABEL_DISABLE);
+       }
        atomic_inc(&key->enabled);
        jump_label_unlock();
 }
+EXPORT_SYMBOL_GPL(static_key_slow_inc);
 
-void jump_label_dec(struct jump_label_key *key)
+static void __static_key_slow_dec(struct static_key *key,
+               unsigned long rate_limit, struct delayed_work *work)
 {
-       if (!atomic_dec_and_mutex_lock(&key->enabled, &jump_label_mutex))
+       if (!atomic_dec_and_mutex_lock(&key->enabled, &jump_label_mutex)) {
+               WARN(atomic_read(&key->enabled) < 0,
+                    "jump label: negative count!\n");
                return;
+       }
 
-       jump_label_update(key, JUMP_LABEL_DISABLE);
+       if (rate_limit) {
+               atomic_inc(&key->enabled);
+               schedule_delayed_work(work, rate_limit);
+       } else {
+               if (!jump_label_get_branch_default(key))
+                       jump_label_update(key, JUMP_LABEL_DISABLE);
+               else
+                       jump_label_update(key, JUMP_LABEL_ENABLE);
+       }
        jump_label_unlock();
 }
 
+static void jump_label_update_timeout(struct work_struct *work)
+{
+       struct static_key_deferred *key =
+               container_of(work, struct static_key_deferred, work.work);
+       __static_key_slow_dec(&key->key, 0, NULL);
+}
+
+void static_key_slow_dec(struct static_key *key)
+{
+       __static_key_slow_dec(key, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(static_key_slow_dec);
+
+void static_key_slow_dec_deferred(struct static_key_deferred *key)
+{
+       __static_key_slow_dec(&key->key, key->timeout, &key->work);
+}
+EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred);
+
+void jump_label_rate_limit(struct static_key_deferred *key,
+               unsigned long rl)
+{
+       key->timeout = rl;
+       INIT_DELAYED_WORK(&key->work, jump_label_update_timeout);
+}
+
 static int addr_conflict(struct jump_entry *entry, void *start, void *end)
 {
        if (entry->code <= (unsigned long)end &&
@@ -105,7 +143,19 @@ static int __jump_label_text_reserved(struct jump_entry *iter_start,
        return 0;
 }
 
-static void __jump_label_update(struct jump_label_key *key,
+/* 
+ * Update code which is definitely not currently executing.
+ * Architectures which need heavyweight synchronization to modify
+ * running code can override this to make the non-live update case
+ * cheaper.
+ */
+void __weak __init_or_module arch_jump_label_transform_static(struct jump_entry *entry,
+                                           enum jump_label_type type)
+{
+       arch_jump_label_transform(entry, type); 
+}
+
+static void __jump_label_update(struct static_key *key,
                                struct jump_entry *entry,
                                struct jump_entry *stop, int enable)
 {
@@ -122,45 +172,51 @@ static void __jump_label_update(struct jump_label_key *key,
        }
 }
 
-/*
- * Not all archs need this.
- */
-void __weak arch_jump_label_text_poke_early(jump_label_t addr)
+static enum jump_label_type jump_label_type(struct static_key *key)
 {
+       bool true_branch = jump_label_get_branch_default(key);
+       bool state = static_key_enabled(key);
+
+       if ((!true_branch && state) || (true_branch && !state))
+               return JUMP_LABEL_ENABLE;
+
+       return JUMP_LABEL_DISABLE;
 }
 
-static __init int jump_label_init(void)
+void __init jump_label_init(void)
 {
        struct jump_entry *iter_start = __start___jump_table;
        struct jump_entry *iter_stop = __stop___jump_table;
-       struct jump_label_key *key = NULL;
+       struct static_key *key = NULL;
        struct jump_entry *iter;
 
        jump_label_lock();
        jump_label_sort_entries(iter_start, iter_stop);
 
        for (iter = iter_start; iter < iter_stop; iter++) {
-               arch_jump_label_text_poke_early(iter->code);
-               if (iter->key == (jump_label_t)(unsigned long)key)
+               struct static_key *iterk;
+
+               iterk = (struct static_key *)(unsigned long)iter->key;
+               arch_jump_label_transform_static(iter, jump_label_type(iterk));
+               if (iterk == key)
                        continue;
 
-               key = (struct jump_label_key *)(unsigned long)iter->key;
-               atomic_set(&key->enabled, 0);
-               key->entries = iter;
+               key = iterk;
+               /*
+                * Set key->entries to iter, but preserve JUMP_LABEL_TRUE_BRANCH.
+                */
+               *((unsigned long *)&key->entries) += (unsigned long)iter;
 #ifdef CONFIG_MODULES
                key->next = NULL;
 #endif
        }
        jump_label_unlock();
-
-       return 0;
 }
-early_initcall(jump_label_init);
 
 #ifdef CONFIG_MODULES
 
-struct jump_label_mod {
-       struct jump_label_mod *next;
+struct static_key_mod {
+       struct static_key_mod *next;
        struct jump_entry *entries;
        struct module *mod;
 };
@@ -180,9 +236,9 @@ static int __jump_label_mod_text_reserved(void *start, void *end)
                                start, end);
 }
 
-static void __jump_label_mod_update(struct jump_label_key *key, int enable)
+static void __jump_label_mod_update(struct static_key *key, int enable)
 {
-       struct jump_label_mod *mod = key->next;
+       struct static_key_mod *mod = key->next;
 
        while (mod) {
                struct module *m = mod->mod;
@@ -212,8 +268,9 @@ void jump_label_apply_nops(struct module *mod)
        if (iter_start == iter_stop)
                return;
 
-       for (iter = iter_start; iter < iter_stop; iter++)
-               arch_jump_label_text_poke_early(iter->code);
+       for (iter = iter_start; iter < iter_stop; iter++) {
+               arch_jump_label_transform_static(iter, JUMP_LABEL_DISABLE);
+       }
 }
 
 static int jump_label_add_module(struct module *mod)
@@ -221,8 +278,8 @@ static int jump_label_add_module(struct module *mod)
        struct jump_entry *iter_start = mod->jump_entries;
        struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
        struct jump_entry *iter;
-       struct jump_label_key *key = NULL;
-       struct jump_label_mod *jlm;
+       struct static_key *key = NULL;
+       struct static_key_mod *jlm;
 
        /* if the module doesn't have jump label entries, just return */
        if (iter_start == iter_stop)
@@ -231,30 +288,31 @@ static int jump_label_add_module(struct module *mod)
        jump_label_sort_entries(iter_start, iter_stop);
 
        for (iter = iter_start; iter < iter_stop; iter++) {
-               if (iter->key == (jump_label_t)(unsigned long)key)
-                       continue;
+               struct static_key *iterk;
 
-               key = (struct jump_label_key *)(unsigned long)iter->key;
+               iterk = (struct static_key *)(unsigned long)iter->key;
+               if (iterk == key)
+                       continue;
 
+               key = iterk;
                if (__module_address(iter->key) == mod) {
-                       atomic_set(&key->enabled, 0);
-                       key->entries = iter;
+                       /*
+                        * Set key->entries to iter, but preserve JUMP_LABEL_TRUE_BRANCH.
+                        */
+                       *((unsigned long *)&key->entries) += (unsigned long)iter;
                        key->next = NULL;
                        continue;
                }
-
-               jlm = kzalloc(sizeof(struct jump_label_mod), GFP_KERNEL);
+               jlm = kzalloc(sizeof(struct static_key_mod), GFP_KERNEL);
                if (!jlm)
                        return -ENOMEM;
-
                jlm->mod = mod;
                jlm->entries = iter;
                jlm->next = key->next;
                key->next = jlm;
 
-               if (jump_label_enabled(key))
-                       __jump_label_update(key, iter, iter_stop,
-                                           JUMP_LABEL_ENABLE);
+               if (jump_label_type(key) == JUMP_LABEL_ENABLE)
+                       __jump_label_update(key, iter, iter_stop, JUMP_LABEL_ENABLE);
        }
 
        return 0;
@@ -265,14 +323,14 @@ static void jump_label_del_module(struct module *mod)
        struct jump_entry *iter_start = mod->jump_entries;
        struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
        struct jump_entry *iter;
-       struct jump_label_key *key = NULL;
-       struct jump_label_mod *jlm, **prev;
+       struct static_key *key = NULL;
+       struct static_key_mod *jlm, **prev;
 
        for (iter = iter_start; iter < iter_stop; iter++) {
                if (iter->key == (jump_label_t)(unsigned long)key)
                        continue;
 
-               key = (struct jump_label_key *)(unsigned long)iter->key;
+               key = (struct static_key *)(unsigned long)iter->key;
 
                if (__module_address(iter->key) == mod)
                        continue;
@@ -374,12 +432,13 @@ int jump_label_text_reserved(void *start, void *end)
        return ret;
 }
 
-static void jump_label_update(struct jump_label_key *key, int enable)
+static void jump_label_update(struct static_key *key, int enable)
 {
-       struct jump_entry *entry = key->entries, *stop = __stop___jump_table;
+       struct jump_entry *stop = __stop___jump_table;
+       struct jump_entry *entry = jump_label_get_entries(key);
 
 #ifdef CONFIG_MODULES
-       struct module *mod = __module_address((jump_label_t)key);
+       struct module *mod = __module_address((unsigned long)key);
 
        __jump_label_mod_update(key, enable);