livepatch: introduce patch/func-walking helpers
[cascardo/linux.git] / kernel / livepatch / core.c
index c5e631c..c38398e 100644 (file)
@@ -422,7 +422,7 @@ static void klp_disable_object(struct klp_object *obj)
 {
        struct klp_func *func;
 
-       for (func = obj->funcs; func->old_name; func++)
+       klp_for_each_func(obj, func)
                if (func->state == KLP_ENABLED)
                        klp_disable_func(func);
 
@@ -440,7 +440,7 @@ static int klp_enable_object(struct klp_object *obj)
        if (WARN_ON(!klp_is_object_loaded(obj)))
                return -EINVAL;
 
-       for (func = obj->funcs; func->old_name; func++) {
+       klp_for_each_func(obj, func) {
                ret = klp_enable_func(func);
                if (ret) {
                        klp_disable_object(obj);
@@ -463,7 +463,7 @@ static int __klp_disable_patch(struct klp_patch *patch)
 
        pr_notice("disabling patch '%s'\n", patch->mod->name);
 
-       for (obj = patch->objs; obj->funcs; obj++) {
+       klp_for_each_object(patch, obj) {
                if (obj->state == KLP_ENABLED)
                        klp_disable_object(obj);
        }
@@ -523,7 +523,7 @@ static int __klp_enable_patch(struct klp_patch *patch)
 
        pr_notice("enabling patch '%s'\n", patch->mod->name);
 
-       for (obj = patch->objs; obj->funcs; obj++) {
+       klp_for_each_object(patch, obj) {
                if (!klp_is_object_loaded(obj))
                        continue;
 
@@ -651,6 +651,15 @@ static struct kobj_type klp_ktype_patch = {
        .default_attrs = klp_patch_attrs,
 };
 
+static void klp_kobj_release_object(struct kobject *kobj)
+{
+}
+
+static struct kobj_type klp_ktype_object = {
+       .release = klp_kobj_release_object,
+       .sysfs_ops = &kobj_sysfs_ops,
+};
+
 static void klp_kobj_release_func(struct kobject *kobj)
 {
 }
@@ -680,7 +689,7 @@ static void klp_free_object_loaded(struct klp_object *obj)
 
        obj->mod = NULL;
 
-       for (func = obj->funcs; func->old_name; func++)
+       klp_for_each_func(obj, func)
                func->old_addr = 0;
 }
 
@@ -695,7 +704,7 @@ static void klp_free_objects_limited(struct klp_patch *patch,
 
        for (obj = patch->objs; obj->funcs && obj != limit; obj++) {
                klp_free_funcs_limited(obj, NULL);
-               kobject_put(obj->kobj);
+               kobject_put(&obj->kobj);
        }
 }
 
@@ -713,7 +722,7 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
        func->state = KLP_DISABLED;
 
        return kobject_init_and_add(&func->kobj, &klp_ktype_func,
-                                   obj->kobj, "%s", func->old_name);
+                                   &obj->kobj, "%s", func->old_name);
 }
 
 /* parts of the initialization that is done only when the object is loaded */
@@ -729,7 +738,7 @@ static int klp_init_object_loaded(struct klp_patch *patch,
                        return ret;
        }
 
-       for (func = obj->funcs; func->old_name; func++) {
+       klp_for_each_func(obj, func) {
                ret = klp_find_verify_func_addr(obj, func);
                if (ret)
                        return ret;
@@ -753,11 +762,12 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
        klp_find_object_module(obj);
 
        name = klp_is_module(obj) ? obj->name : "vmlinux";
-       obj->kobj = kobject_create_and_add(name, &patch->kobj);
-       if (!obj->kobj)
-               return -ENOMEM;
+       ret = kobject_init_and_add(&obj->kobj, &klp_ktype_object,
+                                  &patch->kobj, "%s", name);
+       if (ret)
+               return ret;
 
-       for (func = obj->funcs; func->old_name; func++) {
+       klp_for_each_func(obj, func) {
                ret = klp_init_func(obj, func);
                if (ret)
                        goto free;
@@ -773,7 +783,7 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
 
 free:
        klp_free_funcs_limited(obj, func);
-       kobject_put(obj->kobj);
+       kobject_put(&obj->kobj);
        return ret;
 }
 
@@ -794,7 +804,7 @@ static int klp_init_patch(struct klp_patch *patch)
        if (ret)
                goto unlock;
 
-       for (obj = patch->objs; obj->funcs; obj++) {
+       klp_for_each_object(patch, obj) {
                ret = klp_init_object(patch, obj);
                if (ret)
                        goto free;
@@ -883,7 +893,7 @@ int klp_register_patch(struct klp_patch *patch)
 }
 EXPORT_SYMBOL_GPL(klp_register_patch);
 
-static void klp_module_notify_coming(struct klp_patch *patch,
+static int klp_module_notify_coming(struct klp_patch *patch,
                                     struct klp_object *obj)
 {
        struct module *pmod = patch->mod;
@@ -891,22 +901,23 @@ static void klp_module_notify_coming(struct klp_patch *patch,
        int ret;
 
        ret = klp_init_object_loaded(patch, obj);
-       if (ret)
-               goto err;
+       if (ret) {
+               pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
+                       pmod->name, mod->name, ret);
+               return ret;
+       }
 
        if (patch->state == KLP_DISABLED)
-               return;
+               return 0;
 
        pr_notice("applying patch '%s' to loading module '%s'\n",
                  pmod->name, mod->name);
 
        ret = klp_enable_object(obj);
-       if (!ret)
-               return;
-
-err:
-       pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
-               pmod->name, mod->name, ret);
+       if (ret)
+               pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
+                       pmod->name, mod->name, ret);
+       return ret;
 }
 
 static void klp_module_notify_going(struct klp_patch *patch,
@@ -930,6 +941,7 @@ disabled:
 static int klp_module_notify(struct notifier_block *nb, unsigned long action,
                             void *data)
 {
+       int ret;
        struct module *mod = data;
        struct klp_patch *patch;
        struct klp_object *obj;
@@ -949,13 +961,18 @@ static int klp_module_notify(struct notifier_block *nb, unsigned long action,
                mod->klp_alive = false;
 
        list_for_each_entry(patch, &klp_patches, list) {
-               for (obj = patch->objs; obj->funcs; obj++) {
+               klp_for_each_object(patch, obj) {
                        if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
                                continue;
 
                        if (action == MODULE_STATE_COMING) {
                                obj->mod = mod;
-                               klp_module_notify_coming(patch, obj);
+                               ret = klp_module_notify_coming(patch, obj);
+                               if (ret) {
+                                       obj->mod = NULL;
+                                       pr_warn("patch '%s' is in an inconsistent state!\n",
+                                               patch->mod->name);
+                               }
                        } else /* MODULE_STATE_GOING */
                                klp_module_notify_going(patch, obj);