tracepoint: Do not fail unregistering a probe due to memory failure
[linux-2.6-microblaze.git] / kernel / tracepoint.c
index 7261fa0..e8f20ae 100644 (file)
@@ -53,6 +53,12 @@ struct tp_probes {
        struct tracepoint_func probes[];
 };
 
+/* Called in removal of a func but failed to allocate a new tp_funcs */
+static void tp_stub_func(void)
+{
+       return;
+}
+
 static inline void *allocate_probes(int count)
 {
        struct tp_probes *p  = kmalloc(struct_size(p, probes, count),
@@ -131,6 +137,7 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func,
 {
        struct tracepoint_func *old, *new;
        int nr_probes = 0;
+       int stub_funcs = 0;
        int pos = -1;
 
        if (WARN_ON(!tp_func->func))
@@ -147,14 +154,34 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func,
                        if (old[nr_probes].func == tp_func->func &&
                            old[nr_probes].data == tp_func->data)
                                return ERR_PTR(-EEXIST);
+                       if (old[nr_probes].func == tp_stub_func)
+                               stub_funcs++;
                }
        }
-       /* + 2 : one for new probe, one for NULL func */
-       new = allocate_probes(nr_probes + 2);
+       /* + 2 : one for new probe, one for NULL func - stub functions */
+       new = allocate_probes(nr_probes + 2 - stub_funcs);
        if (new == NULL)
                return ERR_PTR(-ENOMEM);
        if (old) {
-               if (pos < 0) {
+               if (stub_funcs) {
+                       /* Need to copy one at a time to remove stubs */
+                       int probes = 0;
+
+                       pos = -1;
+                       for (nr_probes = 0; old[nr_probes].func; nr_probes++) {
+                               if (old[nr_probes].func == tp_stub_func)
+                                       continue;
+                               if (pos < 0 && old[nr_probes].prio < prio)
+                                       pos = probes++;
+                               new[probes++] = old[nr_probes];
+                       }
+                       nr_probes = probes;
+                       if (pos < 0)
+                               pos = probes;
+                       else
+                               nr_probes--; /* Account for insertion */
+
+               } else if (pos < 0) {
                        pos = nr_probes;
                        memcpy(new, old, nr_probes * sizeof(struct tracepoint_func));
                } else {
@@ -188,8 +215,9 @@ static void *func_remove(struct tracepoint_func **funcs,
        /* (N -> M), (N > 1, M >= 0) probes */
        if (tp_func->func) {
                for (nr_probes = 0; old[nr_probes].func; nr_probes++) {
-                       if (old[nr_probes].func == tp_func->func &&
-                            old[nr_probes].data == tp_func->data)
+                       if ((old[nr_probes].func == tp_func->func &&
+                            old[nr_probes].data == tp_func->data) ||
+                           old[nr_probes].func == tp_stub_func)
                                nr_del++;
                }
        }
@@ -208,14 +236,32 @@ static void *func_remove(struct tracepoint_func **funcs,
                /* N -> M, (N > 1, M > 0) */
                /* + 1 for NULL */
                new = allocate_probes(nr_probes - nr_del + 1);
-               if (new == NULL)
-                       return ERR_PTR(-ENOMEM);
-               for (i = 0; old[i].func; i++)
-                       if (old[i].func != tp_func->func
-                                       || old[i].data != tp_func->data)
-                               new[j++] = old[i];
-               new[nr_probes - nr_del].func = NULL;
-               *funcs = new;
+               if (new) {
+                       for (i = 0; old[i].func; i++)
+                               if ((old[i].func != tp_func->func
+                                    || old[i].data != tp_func->data)
+                                   && old[i].func != tp_stub_func)
+                                       new[j++] = old[i];
+                       new[nr_probes - nr_del].func = NULL;
+                       *funcs = new;
+               } else {
+                       /*
+                        * Failed to allocate, replace the old function
+                        * with calls to tp_stub_func.
+                        */
+                       for (i = 0; old[i].func; i++)
+                               if (old[i].func == tp_func->func &&
+                                   old[i].data == tp_func->data) {
+                                       old[i].func = tp_stub_func;
+                                       /* Set the prio to the next event. */
+                                       if (old[i + 1].func)
+                                               old[i].prio =
+                                                       old[i + 1].prio;
+                                       else
+                                               old[i].prio = -1;
+                               }
+                       *funcs = old;
+               }
        }
        debug_print_probes(*funcs);
        return old;
@@ -295,10 +341,12 @@ static int tracepoint_remove_func(struct tracepoint *tp,
        tp_funcs = rcu_dereference_protected(tp->funcs,
                        lockdep_is_held(&tracepoints_mutex));
        old = func_remove(&tp_funcs, func);
-       if (IS_ERR(old)) {
-               WARN_ON_ONCE(PTR_ERR(old) != -ENOMEM);
+       if (WARN_ON_ONCE(IS_ERR(old)))
                return PTR_ERR(old);
-       }
+
+       if (tp_funcs == old)
+               /* Failed allocating new tp_funcs, replaced func with stub */
+               return 0;
 
        if (!tp_funcs) {
                /* Removed last function */