Skip to contentSkip to conversation

tracing/profile: Fix profile_disable vs module_unload

PATCH In review Li Zefan 5 participants 52 messages Aug 24, 2009 – Sep 13, 2009

Subsystems (3)
lore
Share & tools
lore

Li Zefan

author
#0 lore raw

PATCH 1/1 tracing/profile: Fix profile_disable vs module_unload

If the correspoding module is unloaded before ftrace_profile_disable() is called, event->profile_disable() won't be called, which can cause oops:

  # insmod trace-events-sample.ko
  # perf record -f -a -e sample:foo_bar sleep 3 &
  # sleep 1
  # rmmod trace_events_sample
  # insmod trace-events-sample.ko
  OOPS!

---

resend, CC LKML..

---
 kernel/trace/trace_event_profile.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)
kernel/trace/trace_event_profile.c +4 -1
index 11ba5bb..55a25c9 100644
@@ -5,6 +5,7 @@
  *
  */
 
+#include <linux/module.h>
 #include "trace.h"
 
 int ftrace_profile_enable(int event_id)
@@ -14,7 +15,8 @@ int ftrace_profile_enable(int event_id)
 
 	mutex_lock(&event_mutex);
 	list_for_each_entry(event, &ftrace_events, list) {
-		if (event->id == event_id && event->profile_enable) {
+		if (event->id == event_id && event->profile_enable &&
+		    try_module_get(event->mod)) {
 			ret = event->profile_enable(event);
 			break;
 		}
@@ -32,6 +34,7 @@ void ftrace_profile_disable(int event_id)
 	list_for_each_entry(event, &ftrace_events, list) {
 		if (event->id == event_id) {
 			event->profile_disable(event);
+			module_put(event->mod);
 			break;
 		}
 	}
Signature · 1 line
1.6.3
#1 lore raw
Quoted 4 lines · Li Zefan

If the correspoding module is unloaded before ftrace_profile_disable() is called, event->profile_disable() won't be called, which can cause oops:

Quoted 8 lines
  # insmod trace-events-sample.ko
  # perf record -f -a -e sample:foo_bar sleep 3 &
  # sleep 1
  # rmmod trace_events_sample
  # insmod trace-events-sample.ko
  OOPS!

Hrmm, feel fragile, why don't we check if all a modules tracepoints are unused on unload?

Quoted 34 lines
---
 kernel/trace/trace_event_profile.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)
kernel/trace/trace_event_profile.c +4 -1
index 11ba5bb..55a25c9 100644
@@ -5,6 +5,7 @@
  *
  */
 
+#include <linux/module.h>
 #include "trace.h"
 
 int ftrace_profile_enable(int event_id)
@@ -14,7 +15,8 @@ int ftrace_profile_enable(int event_id)
 
 	mutex_lock(&event_mutex);
 	list_for_each_entry(event, &ftrace_events, list) {
-		if (event->id == event_id && event->profile_enable) {
+		if (event->id == event_id && event->profile_enable &&
+		    try_module_get(event->mod)) {
 			ret = event->profile_enable(event);
 			break;
 		}
@@ -32,6 +34,7 @@ void ftrace_profile_disable(int event_id)
 	list_for_each_entry(event, &ftrace_events, list) {
 		if (event->id == event_id) {
 			event->profile_disable(event);
+			module_put(event->mod);
 			break;
 		}
 	}
author
#2 lore raw
Quoted 12 lines · Peter Zijlstra
Quoted 11 lines · Li Zefan

If the correspoding module is unloaded before ftrace_profile_disable() is called, event->profile_disable() won't be called, which can cause oops:

  # insmod trace-events-sample.ko
  # perf record -f -a -e sample:foo_bar sleep 3 &
  # sleep 1
  # rmmod trace_events_sample
  # insmod trace-events-sample.ko
  OOPS!
Quoted 6 lines
Quoted 1 line

Hrmm, feel fragile, why don't we check if all a modules tracepoints are unused on unload?

I don't think it's fragile. We are profiling via a module's tracepoint, so we should pin the module, via module_get(). If event->profile_enable() has been calld, we should make sure it's profile_disable() will be called.

#3 lore raw
Quoted 16 lines · Li Zefan
Quoted 15 lines · Peter Zijlstra
Quoted 12 lines · Li Zefan

If the correspoding module is unloaded before ftrace_profile_disable() is called, event->profile_disable() won't be called, which can cause oops:

  # insmod trace-events-sample.ko
  # perf record -f -a -e sample:foo_bar sleep 3 &
  # sleep 1
  # rmmod trace_events_sample
  # insmod trace-events-sample.ko
  OOPS!
Quoted 8 lines
Quoted 3 lines

Hrmm, feel fragile, why don't we check if all a modules tracepoints are unused on unload?

I don't think it's fragile. We are profiling via a module's tracepoint, so we should pin the module, via module_get(). If event->profile_enable() has been calld, we should make sure it's profile_disable() will be called.

What I call fragile is that everyone registering a tracepoint callback will now apparently need to worry about modules, _that_ is fragile.

Either make module unload look at tracepoint users, or place the try_get_module() in the registration hooks so that regular users don't need to worry about it.

But this is rediculous.

#4 lore raw
Quoted 26 lines · Peter Zijlstra
Quoted 16 lines · Li Zefan
Quoted 15 lines · Peter Zijlstra
Quoted 12 lines · Li Zefan

If the correspoding module is unloaded before ftrace_profile_disable() is called, event->profile_disable() won't be called, which can cause oops:

  # insmod trace-events-sample.ko
  # perf record -f -a -e sample:foo_bar sleep 3 &
  # sleep 1
  # rmmod trace_events_sample
  # insmod trace-events-sample.ko
  OOPS!
Quoted 8 lines
Quoted 3 lines

Hrmm, feel fragile, why don't we check if all a modules tracepoints are unused on unload?

I don't think it's fragile. We are profiling via a module's tracepoint, so we should pin the module, via module_get(). If event->profile_enable() has been calld, we should make sure it's profile_disable() will be called.

Quoted 7 lines

What I call fragile is that everyone registering a tracepoint callback will now apparently need to worry about modules, _that_ is fragile.

Either make module unload look at tracepoint users, or place the try_get_module() in the registration hooks so that regular users don't need to worry about it.

The bug found by Li needs to be fixed obviously.

I tend to agree with you that this does not appear to be the best place to do it: so you suggest to implicitly increase the module refcount on callback registr instead? (and releasing it when unregistering)

Same end result, slightly cleaner place to bump the refcount.

Ingo

#5 lore raw
Quoted 36 lines · Ingo Molnar
Quoted 26 lines · Peter Zijlstra
Quoted 16 lines · Li Zefan
Quoted 15 lines · Peter Zijlstra
Quoted 12 lines · Li Zefan

If the correspoding module is unloaded before ftrace_profile_disable() is called, event->profile_disable() won't be called, which can cause oops:

  # insmod trace-events-sample.ko
  # perf record -f -a -e sample:foo_bar sleep 3 &
  # sleep 1
  # rmmod trace_events_sample
  # insmod trace-events-sample.ko
  OOPS!
Quoted 8 lines
Quoted 3 lines

Hrmm, feel fragile, why don't we check if all a modules tracepoints are unused on unload?

I don't think it's fragile. We are profiling via a module's tracepoint, so we should pin the module, via module_get(). If event->profile_enable() has been calld, we should make sure it's profile_disable() will be called.

Quoted 7 lines

What I call fragile is that everyone registering a tracepoint callback will now apparently need to worry about modules, _that_ is fragile.

Either make module unload look at tracepoint users, or place the try_get_module() in the registration hooks so that regular users don't need to worry about it.

Quoted 8 lines

The bug found by Li needs to be fixed obviously.

I tend to agree with you that this does not appear to be the best place to do it: so you suggest to implicitly increase the module refcount on callback registr instead? (and releasing it when unregistering)

Same end result, slightly cleaner place to bump the refcount.

Yes, because the user of tracepoints should never need to care about modules.

maintainer
#6 lore raw
Quoted 26 lines · Peter Zijlstra
Quoted 26 lines
Quoted 11 lines
Quoted 10 lines
Quoted 5 lines

Hrmm, feel fragile, why don't we check if all a modules tracepoints are unused on unload?

I don't think it's fragile. We are profiling via a module's tracepoint, so we should pin the module, via module_get(). If event->profile_enable() has been calld, we should make sure it's profile_disable() will be called.

Quoted 7 lines

What I call fragile is that everyone registering a tracepoint callback will now apparently need to worry about modules, _that_ is fragile.

Either make module unload look at tracepoint users, or place the try_get_module() in the registration hooks so that regular users don't need to worry about it.

The bug found by Li needs to be fixed obviously.

I tend to agree with you that this does not appear to be the best place to do it: so you suggest to implicitly increase the module refcount on callback registr instead? (and releasing it when unregistering)

Quoted 4 lines
Quoted 1 line

Same end result, slightly cleaner place to bump the refcount.

Yes, because the user of tracepoints should never need to care about modules.

I also agree with Peter on this.

-- Steve

author
#7 lore raw
In reply to Peter Zijlstra
Quoted 34 lines
Quoted 34 lines
Quoted 20 lines
Quoted 13 lines
Quoted 13 lines
Quoted 12 lines

If the correspoding module is unloaded before ftrace_profile_disable() is called, event->profile_disable() won't be called, which can cause oops:

  # insmod trace-events-sample.ko
  # perf record -f -a -e sample:foo_bar sleep 3 &
  # sleep 1
  # rmmod trace_events_sample
  # insmod trace-events-sample.ko
  OOPS!
Quoted 7 lines
Quoted 3 lines

Hrmm, feel fragile, why don't we check if all a modules tracepoints are unused on unload?

I don't think it's fragile. We are profiling via a module's tracepoint, so we should pin the module, via module_get(). If event->profile_enable() has been calld, we should make sure it's profile_disable() will be called.

Quoted 7 lines

What I call fragile is that everyone registering a tracepoint callback will now apparently need to worry about modules, _that_ is fragile.

Either make module unload look at tracepoint users, or place the try_get_module() in the registration hooks so that regular users don't need to worry about it.

The bug found by Li needs to be fixed obviously.

I tend to agree with you that this does not appear to be the best place to do it: so you suggest to implicitly increase the module refcount on callback registr instead? (and releasing it when unregistering)

Quoted 5 lines
Quoted 1 line

Same end result, slightly cleaner place to bump the refcount.

Yes, because the user of tracepoints should never need to care about modules.

I'm afraid it is not feasible to bump module refcnt implicitly in tracepoint_probe_register().

If a tracepoint is registered in module_init, and unregistered in module_exit (see sample/tracepoints), the module is unloadable:

insmod
->call mod->init()
->trace_reg_foo()

     ->module_get()

rmmod
->check mod refcnt
->call mod->exit()
->trace_unreg_foo()

     ->module_put()
#8 lore raw
Quoted 49 lines · Li Zefan
Quoted 34 lines
Quoted 34 lines
Quoted 20 lines
Quoted 13 lines
Quoted 13 lines
Quoted 12 lines

If the correspoding module is unloaded before ftrace_profile_disable() is called, event->profile_disable() won't be called, which can cause oops:

  # insmod trace-events-sample.ko
  # perf record -f -a -e sample:foo_bar sleep 3 &
  # sleep 1
  # rmmod trace_events_sample
  # insmod trace-events-sample.ko
  OOPS!
Quoted 7 lines
Quoted 3 lines

Hrmm, feel fragile, why don't we check if all a modules tracepoints are unused on unload?

I don't think it's fragile. We are profiling via a module's tracepoint, so we should pin the module, via module_get(). If event->profile_enable() has been calld, we should make sure it's profile_disable() will be called.

Quoted 7 lines

What I call fragile is that everyone registering a tracepoint callback will now apparently need to worry about modules, _that_ is fragile.

Either make module unload look at tracepoint users, or place the try_get_module() in the registration hooks so that regular users don't need to worry about it.

The bug found by Li needs to be fixed obviously.

I tend to agree with you that this does not appear to be the best place to do it: so you suggest to implicitly increase the module refcount on callback registr instead? (and releasing it when unregistering)

Quoted 5 lines
Quoted 1 line

Same end result, slightly cleaner place to bump the refcount.

Yes, because the user of tracepoints should never need to care about modules.

I'm afraid it is not feasible to bump module refcnt implicitly in tracepoint_probe_register().

If a tracepoint is registered in module_init, and unregistered in module_exit (see sample/tracepoints), the module is unloadable:

insmod
->call mod->init()
->trace_reg_foo()

Quoted 7 lines
     ->module_get()

rmmod
->check mod refcnt
->call mod->exit()
->trace_unreg_foo()

     ->module_put()

Not tracepoint_probe_{un,}register(), in {un,}register_trace_$call().

Basically avoid module unload when a tracepoint from that module has registered callbacks.

author
#9 lore raw
Quoted 48 lines
Quoted 48 lines
Quoted 28 lines
Quoted 28 lines
Quoted 19 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines

If the correspoding module is unloaded before ftrace_profile_disable() is called, event->profile_disable() won't be called, which can cause oops:

  # insmod trace-events-sample.ko
  # perf record -f -a -e sample:foo_bar sleep 3 &
  # sleep 1
  # rmmod trace_events_sample
  # insmod trace-events-sample.ko
  OOPS!
Quoted 8 lines
Quoted 4 lines
Quoted 1 line

Hrmm, feel fragile, why don't we check if all a modules tracepoints are unused on unload?

I don't think it's fragile. We are profiling via a module's tracepoint, so we should pin the module, via module_get(). If event->profile_enable() has been calld, we should make sure it's profile_disable() will be called.

Quoted 7 lines

What I call fragile is that everyone registering a tracepoint callback will now apparently need to worry about modules, _that_ is fragile.

Either make module unload look at tracepoint users, or place the try_get_module() in the registration hooks so that regular users don't need to worry about it.

The bug found by Li needs to be fixed obviously.

Quoted 9 lines
Quoted 6 lines

I tend to agree with you that this does not appear to be the best place to do it: so you suggest to implicitly increase the module refcount on callback registr instead? (and releasing it when unregistering)

Same end result, slightly cleaner place to bump the refcount.

Yes, because the user of tracepoints should never need to care about modules.

I'm afraid it is not feasible to bump module refcnt implicitly in tracepoint_probe_register().

If a tracepoint is registered in module_init, and unregistered in module_exit (see sample/tracepoints), the module is unloadable:

insmod
->call mod->init()
->trace_reg_foo()

     ->module_get()
Quoted 8 lines
Quoted 5 lines

rmmod
->check mod refcnt
->call mod->exit()
->trace_unreg_foo()

     ->module_put()

Not tracepoint_probe_{un,}register(), in {un,}register_trace_$call().

Is there any difference?

	static inline int register_trace_##name(void (*probe)(proto))	\
	{								\
		int ret;						\
		void (*func)(void) = reg;				\
									\
		ret = tracepoint_probe_register(#name, (void *)probe);	\
		if (func && !ret)					\
			func();						\
		return ret;						\
	}
Quoted 2 lines

Basically avoid module unload when a tracepoint from that module has registered callbacks.

TRACE_EVENT() won't prevent this. Instead at module unload, a module notifier callback will be called to unregistread those tracepoint callbacks.

#10 lore raw
Quoted 59 lines · Li Zefan
Quoted 48 lines
Quoted 48 lines
Quoted 28 lines
Quoted 28 lines
Quoted 19 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines

If the correspoding module is unloaded before ftrace_profile_disable() is called, event->profile_disable() won't be called, which can cause oops:

  # insmod trace-events-sample.ko
  # perf record -f -a -e sample:foo_bar sleep 3 &
  # sleep 1
  # rmmod trace_events_sample
  # insmod trace-events-sample.ko
  OOPS!
Quoted 8 lines
Quoted 4 lines
Quoted 1 line

Hrmm, feel fragile, why don't we check if all a modules tracepoints are unused on unload?

I don't think it's fragile. We are profiling via a module's tracepoint, so we should pin the module, via module_get(). If event->profile_enable() has been calld, we should make sure it's profile_disable() will be called.

Quoted 7 lines

What I call fragile is that everyone registering a tracepoint callback will now apparently need to worry about modules, _that_ is fragile.

Either make module unload look at tracepoint users, or place the try_get_module() in the registration hooks so that regular users don't need to worry about it.

The bug found by Li needs to be fixed obviously.

Quoted 9 lines
Quoted 6 lines

I tend to agree with you that this does not appear to be the best place to do it: so you suggest to implicitly increase the module refcount on callback registr instead? (and releasing it when unregistering)

Same end result, slightly cleaner place to bump the refcount.

Yes, because the user of tracepoints should never need to care about modules.

I'm afraid it is not feasible to bump module refcnt implicitly in tracepoint_probe_register().

If a tracepoint is registered in module_init, and unregistered in module_exit (see sample/tracepoints), the module is unloadable:

insmod
->call mod->init()
->trace_reg_foo()

     ->module_get()
Quoted 8 lines
Quoted 5 lines

rmmod
->check mod refcnt
->call mod->exit()
->trace_unreg_foo()

     ->module_put()

Not tracepoint_probe_{un,}register(), in {un,}register_trace_$call().

Is there any difference?

Quoted 10 lines
	static inline int register_trace_##name(void (*probe)(proto))	\
	{								\
		int ret;						\
		void (*func)(void) = reg;				\
									\
		ret = tracepoint_probe_register(#name, (void *)probe);	\
		if (func && !ret)					\
			func();						\
		return ret;						\
	}

Ah, my bad, I was thikning tracepoint_probe_register() was the thing that registered the tracepoint itself, not the callback.

Ok, then what's the problem?, don't do modules that consume their own tracepoints, seems simple enough.

Quoted 5 lines
Quoted 2 lines

Basically avoid module unload when a tracepoint from that module has registered callbacks.

TRACE_EVENT() won't prevent this. Instead at module unload, a module notifier callback will be called to unregistread those tracepoint callbacks.

Ugh, that's disgusting. That means every single tracepoint user again needs to be aware of modules.

#11 lore raw
Quoted 5 lines · Peter Zijlstra

Ah, my bad, I was thikning tracepoint_probe_register() was the thing that registered the tracepoint itself, not the callback.

Ok, then what's the problem?, don't do modules that consume their own tracepoints, seems simple enough.

is this a reasonable restriction? I dont see any reason why the act of defining and providing a tracepoint should be exclusive of the ability to make use of it.

Ingo

#12 lore raw
Quoted 11 lines · Ingo Molnar
Quoted 5 lines · Peter Zijlstra

Ah, my bad, I was thikning tracepoint_probe_register() was the thing that registered the tracepoint itself, not the callback.

Ok, then what's the problem?, don't do modules that consume their own tracepoints, seems simple enough.

is this a reasonable restriction? I dont see any reason why the act of defining and providing a tracepoint should be exclusive of the ability to make use of it.

It doesn't make sense to me, you don't need your own tracepoints because you generate the events yourself, you already have them.

Furthermore, I much prefer this over spreading module gunk all over tracepoint users.

#13 lore raw
Quoted 9 lines · Peter Zijlstra
Quoted 8 lines · Ingo Molnar
Quoted 5 lines · Peter Zijlstra

Ah, my bad, I was thikning tracepoint_probe_register() was the thing that registered the tracepoint itself, not the callback.

Ok, then what's the problem?, don't do modules that consume their own tracepoints, seems simple enough.

Quoted 6 lines
Quoted 3 lines

is this a reasonable restriction? I dont see any reason why the act of defining and providing a tracepoint should be exclusive of the ability to make use of it.

It doesn't make sense to me, you don't need your own tracepoints because you generate the events yourself, you already have them.

For a reasonable large subsystem/driver i can very well imagine this to happen: why should the subsystem add _another_ layer of callbacks if it can reuse the generic tracepoint code and register itself to those?

Ingo

#14 lore raw
Quoted 15 lines · Ingo Molnar
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Ingo Molnar
Quoted 5 lines · Peter Zijlstra

Ah, my bad, I was thikning tracepoint_probe_register() was the thing that registered the tracepoint itself, not the callback.

Ok, then what's the problem?, don't do modules that consume their own tracepoints, seems simple enough.

is this a reasonable restriction? I dont see any reason why the act of defining and providing a tracepoint should be exclusive of the ability to make use of it.

Quoted 7 lines
Quoted 2 lines

It doesn't make sense to me, you don't need your own tracepoints because you generate the events yourself, you already have them.

For a reasonable large subsystem/driver i can very well imagine this to happen: why should the subsystem add _another_ layer of callbacks if it can reuse the generic tracepoint code and register itself to those?

Then that subsystem would be non functioning when the kernel is build without tracepoints.

Nothing should rely on tracepoint being present, they are and should remain a debug feature, not a core hook thing.

Do you really wish to burden every tracepoint user with the extra logic needed to deal with modules?

#15 lore raw
Quoted 19 lines · Peter Zijlstra
Quoted 18 lines · Ingo Molnar
Quoted 15 lines · Peter Zijlstra
Quoted 11 lines · Ingo Molnar
Quoted 5 lines · Peter Zijlstra

Ah, my bad, I was thikning tracepoint_probe_register() was the thing that registered the tracepoint itself, not the callback.

Ok, then what's the problem?, don't do modules that consume their own tracepoints, seems simple enough.

is this a reasonable restriction? I dont see any reason why the act of defining and providing a tracepoint should be exclusive of the ability to make use of it.

It doesn't make sense to me, you don't need your own tracepoints because you generate the events yourself, you already have them.

Quoted 7 lines
Quoted 4 lines

For a reasonable large subsystem/driver i can very well imagine this to happen: why should the subsystem add _another_ layer of callbacks if it can reuse the generic tracepoint code and register itself to those?

Then that subsystem would be non functioning when the kernel is build without tracepoints.

... except if it selects all required core kernel functionality it relies on? Tracepoints and typed callbacks really go hand in hand and it would be nice to see some more synergy in that space. (instead of crappy, prioritized notifier chains for example.)

Quoted 5 lines

Nothing should rely on tracepoint being present, they are and should remain a debug feature, not a core hook thing.

Do you really wish to burden every tracepoint user with the extra logic needed to deal with modules?

Not necessarily - i'm just outlining why i think that the 'dont allow subsystems to utilize tracepoint callbacks' is a restriction we should not live with voluntarily.

Ingo

#16 lore raw
Quoted 6 lines · Ingo Molnar
Quoted 2 lines

Do you really wish to burden every tracepoint user with the extra logic needed to deal with modules?

Not necessarily - i'm just outlining why i think that the 'dont allow subsystems to utilize tracepoint callbacks' is a restriction we should not live with voluntarily.

Well, unless someone has a bright idea that's what it comes down to. And not having to care about modules when using tracepoint wins hands down for me.

The issue seems rather simple:

Either we force everybody who uses a tracepoint to care about modules, be this by having to do try_get_module() themselves or by having to listen to some notifier and have their callback forcibly dropped on unload -- both suck IMO, suck very hard indeed.

Or by having modules that use their own tracepoint be stuck, because once you block unlock when a tracepoint has callbacks, and it installed a callback on itself, its not going to go away.

And since I don't care about modules at all and really wish they'd never been invented...

#17 lore raw
Quoted 10 lines · Peter Zijlstra
Quoted 6 lines · Ingo Molnar
Quoted 2 lines

Do you really wish to burden every tracepoint user with the extra logic needed to deal with modules?

Not necessarily - i'm just outlining why i think that the 'dont allow subsystems to utilize tracepoint callbacks' is a restriction we should not live with voluntarily.

Well, unless someone has a bright idea that's what it comes down to.

OK, I still think modules probing their own tracepoints its stupid [*], but what you could do is iterate the tracepoint's callback list and see if it has a callback outside of the module code section and then fail the unload.

[*] in the really utterly fundamentally wrong stupid class.

author
#18 lore raw
CC Mathieu
Quoted 8 lines · Peter Zijlstra
Quoted 7 lines · Peter Zijlstra
Quoted 5 lines · Ingo Molnar
Quoted 2 lines

Do you really wish to burden every tracepoint user with the extra logic needed to deal with modules?

Not necessarily - i'm just outlining why i think that the 'dont allow subsystems to utilize tracepoint callbacks' is a restriction we should not live with voluntarily.

Quoted 7 lines
Quoted 1 line

Well, unless someone has a bright idea that's what it comes down to.

OK, I still think modules probing their own tracepoints its stupid [*], but what you could do is iterate the tracepoint's callback list and see if it has a callback outside of the module code section and then fail the unload.

I don't think this can work, what if a new callback is registered after this check? Seems there's no syncronization to guarantee the result of the check can remain valid during module unload.

And the other proposal that bumping module refcnt in register_tracepoint_xxx(), I found it hard to do so, because register() won't know where the tracepoint is. For example:

register_tracepoint_foo() is in module bar, while DEFINE_TRACE(foo) and trace_foo() is in module foo.

Note, you're allowed to load bar without foo.

The simplest fix is the patch I sent, which you don't like. Maybe Mathieu, the auther of tracepoint, or some one else has some idea?

Quoted 1 line

[*] in the really utterly fundamentally wrong stupid class.

#19 lore raw
Quoted 19 lines · Li Zefan
CC Mathieu
Quoted 8 lines · Peter Zijlstra
Quoted 7 lines · Peter Zijlstra
Quoted 5 lines · Ingo Molnar
Quoted 2 lines

Do you really wish to burden every tracepoint user with the extra logic needed to deal with modules?

Not necessarily - i'm just outlining why i think that the 'dont allow subsystems to utilize tracepoint callbacks' is a restriction we should not live with voluntarily.

Quoted 7 lines
Quoted 1 line

Well, unless someone has a bright idea that's what it comes down to.

OK, I still think modules probing their own tracepoints its stupid [*], but what you could do is iterate the tracepoint's callback list and see if it has a callback outside of the module code section and then fail the unload.

Quoted 3 lines

I don't think this can work, what if a new callback is registered after this check? Seems there's no syncronization to guarantee the result of the check can remain valid during module unload.

If that race can be solved using refcounts it can be solved another way too.

Quoted 6 lines

And the other proposal that bumping module refcnt in register_tracepoint_xxx(), I found it hard to do so, because register() won't know where the tracepoint is. For example:

register_tracepoint_foo() is in module bar, while DEFINE_TRACE(foo) and trace_foo() is in module foo.

You have that call->mod pointer, right? Once you're in the register bit you should have access to the owning module, which can tell you if the to be registered callback is part of that same module.

Quoted 4 lines

Note, you're allowed to load bar without foo.

The simplest fix is the patch I sent, which you don't like. Maybe Mathieu, the auther of tracepoint, or some one else has some idea?

Yes, I think forcing every tracepoint consumer to know about modules is insane.

Aahh, I see the bug, its only ftrace that knows about the module, not tracepoints themselves, _that_ needs fixing.

#20 lore raw
Quoted 2 lines · Peter Zijlstra

Aahh, I see the bug, its only ftrace that knows about the module, not tracepoints themselves, _that_ needs fixing.

These are the bits I hacked up until I figured out the above bug.

---
 include/linux/ftrace_event.h |    2 +-
 include/linux/module.h       |   18 ++++++++++++++----
 kernel/module.c              |    9 +--------
 3 files changed, 16 insertions(+), 13 deletions(-)
include/linux/ftrace_event.h +1 -1
index df5b085..d86d60c 100644
@@ -122,7 +122,7 @@ struct ftrace_event_call {
 	struct list_head	fields;
 	int			filter_active;
 	struct event_filter	*filter;
-	void			*mod;
+	struct module		*mod;
 	void			*data;
 
 	atomic_t		profile_count;
include/linux/module.h +14 -4
index 86863cd..09e2b88 100644
@@ -409,16 +409,26 @@ struct module *__module_address(unsigned long addr);
 bool is_module_address(unsigned long addr);
 bool is_module_text_address(unsigned long addr);
 
+static inline 
+int __within_module(unsigned long addr, void *start, unsigned long size)
+{
+	return ((void *)addr >= start && (void *)addr < start + size);
+}
+
 static inline int within_module_core(unsigned long addr, struct module *mod)
 {
-	return (unsigned long)mod->module_core <= addr &&
-	       addr < (unsigned long)mod->module_core + mod->core_size;
+	return __within_module(addr, mod->module_core, mod->core_size);
 }
 
 static inline int within_module_init(unsigned long addr, struct module *mod)
 {
-	return (unsigned long)mod->module_init <= addr &&
-	       addr < (unsigned long)mod->module_init + mod->init_size;
+	return __within_module(addr, mod->module_init, mod->init_size);
+}
+
+static inline int within_module_text(unsigned long addr, struct module *mod)
+{
+	return __within_module(addr, mod->module_init, mod->init_text_size) ||
+	       __within_module(addr, mod->core_init, mod->core_text_size);
 }
 
 /* Search for module by name: must hold module_mutex. */
kernel/module.c +1 -8
index b182143..822adb6 100644
@@ -2493,11 +2493,6 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
 	return 0;
 }
 
-static inline int within(unsigned long addr, void *start, unsigned long size)
-{
-	return ((void *)addr >= start && (void *)addr < start + size);
-}
-
 #ifdef CONFIG_KALLSYMS
 /*
  * This ignores the intensely annoying "mapping symbols" found
@@ -2912,9 +2907,7 @@ struct module *__module_text_address(unsigned long addr)
 {
 	struct module *mod = __module_address(addr);
 	if (mod) {
-		/* Make sure it's within the text section. */
-		if (!within(addr, mod->module_init, mod->init_text_size)
-		    && !within(addr, mod->module_core, mod->core_text_size))
+		if (!within_module_text(addr, mod))
 			mod = NULL;
 	}
 	return mod;
#21 lore raw
In reply to Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

Aahh, I see the bug, its only ftrace that knows about the module, not tracepoints themselves, _that_ needs fixing.

You could possibly do something like:

struct module *tp_mod = __module_address(&some_tp_symbol); struct module *cb_mod = __module_text_address(func);

 if (tp_mod && tp_mod != cb_mod) {
	ret = try_get_module(tp_mod);
	if (ret)
		goto fail;

}

in register_trace_##name() or thereabout.

author
#22 lore raw
Quoted 10 lines · Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

Aahh, I see the bug, its only ftrace that knows about the module, not tracepoints themselves, _that_ needs fixing.

You could possibly do something like:

struct module *tp_mod = __module_address(&some_tp_symbol); struct module *cb_mod = __module_text_address(func);

Quoted 8 lines
 if (tp_mod && tp_mod != cb_mod) {
	ret = try_get_module(tp_mod);
	if (ret)
		goto fail;

}

in register_trace_##name() or thereabout.

Actually I tried it, but it didn't work. As I said, You can't find any tp symbol when registering tp callback. The same example again:

In module bar, we have register_trace_foo()
In module foo, we have DEFINE_TRACE(foo) and trace_foo().

bar doesn't know any symbol of foo, so it can't bump foo's refcnt,

*Note: you can load module bar without loading module foo*

#23 lore raw
Quoted 20 lines · Li Zefan
Quoted 10 lines · Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

Aahh, I see the bug, its only ftrace that knows about the module, not tracepoints themselves, _that_ needs fixing.

You could possibly do something like:

struct module *tp_mod = __module_address(&some_tp_symbol); struct module *cb_mod = __module_text_address(func);

Quoted 8 lines
 if (tp_mod && tp_mod != cb_mod) {
	ret = try_get_module(tp_mod);
	if (ret)
		goto fail;

}

in register_trace_##name() or thereabout.

Quoted 7 lines

Actually I tried it, but it didn't work. As I said, You can't find any tp symbol when registering tp callback. The same example again:

In module bar, we have register_trace_foo()
In module foo, we have DEFINE_TRACE(foo) and trace_foo().

bar doesn't know any symbol of foo, so it can't bump foo's refcnt,

Well, clearly it knows about register_trace_foo() which itself knows at least one symbol that should be in module foo, right? How else could it register a callback in that module (if it were loaded)?

It appears to use some intermediate code, in which case the intermediate code knows about foo, which too solves our problem.

Quoted 1 line

*Note: you can load module bar without loading module foo*

In which case the tracepoint registration fails, right?

author
#24 lore raw
Quoted 31 lines · Peter Zijlstra
Quoted 18 lines · Li Zefan
Quoted 9 lines · Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

Aahh, I see the bug, its only ftrace that knows about the module, not tracepoints themselves, _that_ needs fixing.

You could possibly do something like:

struct module *tp_mod = __module_address(&some_tp_symbol); struct module *cb_mod = __module_text_address(func);

Quoted 8 lines
 if (tp_mod && tp_mod != cb_mod) {
	ret = try_get_module(tp_mod);
	if (ret)
		goto fail;

}

in register_trace_##name() or thereabout.

Quoted 7 lines

Actually I tried it, but it didn't work. As I said, You can't find any tp symbol when registering tp callback. The same example again:

In module bar, we have register_trace_foo()
In module foo, we have DEFINE_TRACE(foo) and trace_foo().

bar doesn't know any symbol of foo, so it can't bump foo's refcnt,

Well, clearly it knows about register_trace_foo() which itself knows at least one symbol that should be in module foo, right? How else could it register a callback in that module (if it were loaded)?

Quoted 7 lines

It appears to use some intermediate code, in which case the intermediate code knows about foo, which too solves our problem.

Quoted 1 line

*Note: you can load module bar without loading module foo*

In which case the tracepoint registration fails, right?

No, it won't fail. ;)

Instead, when foo is loaded, tracepoint_update_probe_range() will be called, and the probe registered in bar will be added to the tracepoint.

Maybe we can do something in tracepoint_update_probe_range(). I'll try.

#25 lore raw
Quoted 37 lines · Li Zefan
Quoted 36 lines · Peter Zijlstra
Quoted 18 lines · Li Zefan
Quoted 9 lines · Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

Aahh, I see the bug, its only ftrace that knows about the module, not tracepoints themselves, _that_ needs fixing.

You could possibly do something like:

struct module *tp_mod = __module_address(&some_tp_symbol); struct module *cb_mod = __module_text_address(func);

Quoted 8 lines
 if (tp_mod && tp_mod != cb_mod) {
	ret = try_get_module(tp_mod);
	if (ret)
		goto fail;

}

in register_trace_##name() or thereabout.

Quoted 7 lines

Actually I tried it, but it didn't work. As I said, You can't find any tp symbol when registering tp callback. The same example again:

In module bar, we have register_trace_foo()
In module foo, we have DEFINE_TRACE(foo) and trace_foo().

bar doesn't know any symbol of foo, so it can't bump foo's refcnt,

Well, clearly it knows about register_trace_foo() which itself knows at least one symbol that should be in module foo, right? How else could it register a callback in that module (if it were loaded)?

It appears to use some intermediate code, in which case the intermediate code knows about foo, which too solves our problem.

Quoted 1 line

*Note: you can load module bar without loading module foo*

Quoted 7 lines
Quoted 2 lines

In which case the tracepoint registration fails, right?

No, it won't fail. ;)

Instead, when foo is loaded, tracepoint_update_probe_range() will be called, and the probe registered in bar will be added to the tracepoint.

*blink*

so we'll succeed in registering a tracepoint we know isn't there?

author
#26 lore raw
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
Quoted 15 lines
Quoted 7 lines
Quoted 2 lines

Aahh, I see the bug, its only ftrace that knows about the module, not tracepoints themselves, _that_ needs fixing.

You could possibly do something like:

struct module *tp_mod = __module_address(&some_tp_symbol); struct module *cb_mod = __module_text_address(func);

Quoted 8 lines
 if (tp_mod && tp_mod != cb_mod) {
	ret = try_get_module(tp_mod);
	if (ret)
		goto fail;

}

in register_trace_##name() or thereabout.

Quoted 7 lines

Actually I tried it, but it didn't work. As I said, You can't find any tp symbol when registering tp callback. The same example again:

In module bar, we have register_trace_foo()
In module foo, we have DEFINE_TRACE(foo) and trace_foo().

bar doesn't know any symbol of foo, so it can't bump foo's refcnt,

Well, clearly it knows about register_trace_foo() which itself knows at least one symbol that should be in module foo, right? How else could it register a callback in that module (if it were loaded)?

It appears to use some intermediate code, in which case the intermediate code knows about foo, which too solves our problem.

Quoted 1 line

*Note: you can load module bar without loading module foo*

In which case the tracepoint registration fails, right?

Quoted 9 lines
Quoted 4 lines

No, it won't fail. ;)

Instead, when foo is loaded, tracepoint_update_probe_range() will be called, and the probe registered in bar will be added to the tracepoint.

*blink*

so we'll succeed in registering a tracepoint we know isn't there?

Yeah, try this:

# cd sample/tracepoints/
# insmod tracepoint-probe-sample.ko	<--- load trace probe firstly
# insmod tracepoint-sample.ko           <--- load tracepoint secondly
# cat /proc/tracepoint-sample
cat: /proc/tracepoint-sample: Operation not permitted	<-- pls ignore this
# dmesg							<-- see the output of probe
Event is encountered with filename tracepoint-sample
Event B is encountered
Event B is encountered
Event B is encountered
Event B is encountered
Event B is encountered
Event B is encountered
Event B is encountered
Event B is encountered
Event B is encountered
Event B is encountered
maintainer
#27 lore raw
In reply to Li Zefan
Quoted 23 lines · Li Zefan
Quoted 10 lines · Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

Aahh, I see the bug, its only ftrace that knows about the module, not tracepoints themselves, _that_ needs fixing.

You could possibly do something like:

struct module *tp_mod = __module_address(&some_tp_symbol); struct module *cb_mod = __module_text_address(func);

Quoted 8 lines
 if (tp_mod && tp_mod != cb_mod) {
	ret = try_get_module(tp_mod);
	if (ret)
		goto fail;

}

in register_trace_##name() or thereabout.

Actually I tried it, but it didn't work. As I said, You can't find any tp symbol when registering tp callback. The same example again:

Quoted 6 lines

In module bar, we have register_trace_foo()
In module foo, we have DEFINE_TRACE(foo) and trace_foo().

bar doesn't know any symbol of foo, so it can't bump foo's refcnt,

*Note: you can load module bar without loading module foo*

WTF!!!!

We can register a trace point that is defined in another module without having that module?? How is that possible? That looks totally busted, and that is not a case that I think we need to worry about, except to prevent it from ever happening.

As for ref counts, would something like this work?

(untested)

-- Steve

include/linux/tracepoint.h +3 -2
index 0341f2e..055275b 100644
@@ -109,8 +109,9 @@ struct tracepoint {
 #define EXPORT_TRACEPOINT_SYMBOL(name)					\
 	EXPORT_SYMBOL(__tracepoint_##name)
 
-extern void tracepoint_update_probe_range(struct tracepoint *begin,
-	struct tracepoint *end);
+extern void tracepoint_update_probe_range(struct module *,
+					  struct tracepoint *begin,
+					  struct tracepoint *end);
 
 #else /* !CONFIG_TRACEPOINTS */
 #define DECLARE_TRACE_WITH_CALLBACK(name, proto, args, reg, unreg)	\
kernel/module.c +1 -1
index b182143..a8e69fa 100644
@@ -2974,7 +2974,7 @@ void module_update_tracepoints(void)
 	mutex_lock(&module_mutex);
 	list_for_each_entry(mod, &modules, list)
 		if (!mod->taints)
-			tracepoint_update_probe_range(mod->tracepoints,
+			tracepoint_update_probe_range(mod, mod->tracepoints,
 				mod->tracepoints + mod->num_tracepoints);
 	mutex_unlock(&module_mutex);
 }
kernel/tracepoint.c +10 -3
index 06f165a..b150255 100644
@@ -274,7 +274,8 @@ static void disable_tracepoint(struct tracepoint *elem)
  * Updates the probe callback corresponding to a range of tracepoints.
  */
 void
-tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
+tracepoint_update_probe_range(struct module *mod,
+			      struct tracepoint *begin, struct tracepoint *end)
 {
 	struct tracepoint *iter;
 	struct tracepoint_entry *mark_entry;
@@ -286,9 +287,15 @@ tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
 	for (iter = begin; iter < end; iter++) {
 		mark_entry = get_tracepoint(iter->name);
 		if (mark_entry) {
+			if (mod) {
+				if (!try_module_get(mod))
+					continue;
+			}
 			set_tracepoint(&mark_entry, iter,
 					!!mark_entry->refcount);
 		} else {
+			if (mod)
+				module_put(mod);
 			disable_tracepoint(iter);
 		}
 	}
@@ -301,7 +308,7 @@ tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
 static void tracepoint_update_probes(void)
 {
 	/* Core kernel tracepoints */
-	tracepoint_update_probe_range(__start___tracepoints,
+	tracepoint_update_probe_range(NULL, __start___tracepoints,
 		__stop___tracepoints);
 	/* tracepoints in modules. */
 	module_update_tracepoints();
@@ -556,7 +563,7 @@ int tracepoint_module_notify(struct notifier_block *self,
 	switch (val) {
 	case MODULE_STATE_COMING:
 	case MODULE_STATE_GOING:
-		tracepoint_update_probe_range(mod->tracepoints,
+		tracepoint_update_probe_range(mod, mod->tracepoints,
 			mod->tracepoints + mod->num_tracepoints);
 		break;
 	}
#28 lore raw
Quoted 31 lines · * Steven Rostedt ([email protected]) wrote:
Quoted 28 lines · Li Zefan
Quoted 10 lines · Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

Aahh, I see the bug, its only ftrace that knows about the module, not tracepoints themselves, _that_ needs fixing.

You could possibly do something like:

struct module *tp_mod = __module_address(&some_tp_symbol); struct module *cb_mod = __module_text_address(func);

Quoted 8 lines
 if (tp_mod && tp_mod != cb_mod) {
	ret = try_get_module(tp_mod);
	if (ret)
		goto fail;

}

in register_trace_##name() or thereabout.

Actually I tried it, but it didn't work. As I said, You can't find any tp symbol when registering tp callback. The same example again:

In module bar, we have register_trace_foo()
In module foo, we have DEFINE_TRACE(foo) and trace_foo().

bar doesn't know any symbol of foo, so it can't bump foo's refcnt,

Quoted 9 lines
Quoted 1 line

*Note: you can load module bar without loading module foo*

WTF!!!!

We can register a trace point that is defined in another module without having that module?? How is that possible? That looks totally busted, and that is not a case that I think we need to worry about, except to prevent it from ever happening.

Registering tracepoints even when no tracepoint definition is currently visible is the intended allowed behavior. Let's say we need to trace something happening in module init: if we disallow registering the tp callback before the module is initialized, we run in a chicken and egg problem.

So I am trying to figure out the problem source there. Is it that modules containing the tp callbacks need to know if those are actually connected to an instrumented module ? Or is it that the instrumented module needs to know if a probe module is connected to is ? Or is it the teardown of the probe module ? No refcount is needed there, because we surround the probe call by preempt disable/enable, and we use synchronize_sched() before removing the module which contains probe callbacks.

Mathieu-trying-to-figure-out-what-this-whole-thread-is-about :)

Quoted 83 lines

As for ref counts, would something like this work?

(untested)

-- Steve

include/linux/tracepoint.h +3 -2
index 0341f2e..055275b 100644
@@ -109,8 +109,9 @@ struct tracepoint {
 #define EXPORT_TRACEPOINT_SYMBOL(name)					\
 	EXPORT_SYMBOL(__tracepoint_##name)
 
-extern void tracepoint_update_probe_range(struct tracepoint *begin,
-	struct tracepoint *end);
+extern void tracepoint_update_probe_range(struct module *,
+					  struct tracepoint *begin,
+					  struct tracepoint *end);
 
 #else /* !CONFIG_TRACEPOINTS */
 #define DECLARE_TRACE_WITH_CALLBACK(name, proto, args, reg, unreg)	\
kernel/module.c +1 -1
index b182143..a8e69fa 100644
@@ -2974,7 +2974,7 @@ void module_update_tracepoints(void)
 	mutex_lock(&module_mutex);
 	list_for_each_entry(mod, &modules, list)
 		if (!mod->taints)
-			tracepoint_update_probe_range(mod->tracepoints,
+			tracepoint_update_probe_range(mod, mod->tracepoints,
 				mod->tracepoints + mod->num_tracepoints);
 	mutex_unlock(&module_mutex);
 }
kernel/tracepoint.c +10 -3
index 06f165a..b150255 100644
@@ -274,7 +274,8 @@ static void disable_tracepoint(struct tracepoint *elem)
  * Updates the probe callback corresponding to a range of tracepoints.
  */
 void
-tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
+tracepoint_update_probe_range(struct module *mod,
+			      struct tracepoint *begin, struct tracepoint *end)
 {
 	struct tracepoint *iter;
 	struct tracepoint_entry *mark_entry;
@@ -286,9 +287,15 @@ tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
 	for (iter = begin; iter < end; iter++) {
 		mark_entry = get_tracepoint(iter->name);
 		if (mark_entry) {
+			if (mod) {
+				if (!try_module_get(mod))
+					continue;
+			}
 			set_tracepoint(&mark_entry, iter,
 					!!mark_entry->refcount);
 		} else {
+			if (mod)
+				module_put(mod);
 			disable_tracepoint(iter);
 		}
 	}
@@ -301,7 +308,7 @@ tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
 static void tracepoint_update_probes(void)
 {
 	/* Core kernel tracepoints */
-	tracepoint_update_probe_range(__start___tracepoints,
+	tracepoint_update_probe_range(NULL, __start___tracepoints,
 		__stop___tracepoints);
 	/* tracepoints in modules. */
 	module_update_tracepoints();
@@ -556,7 +563,7 @@ int tracepoint_module_notify(struct notifier_block *self,
 	switch (val) {
 	case MODULE_STATE_COMING:
 	case MODULE_STATE_GOING:
-		tracepoint_update_probe_range(mod->tracepoints,
+		tracepoint_update_probe_range(mod, mod->tracepoints,
 			mod->tracepoints + mod->num_tracepoints);
 		break;
 	}
Signature · 2 lines
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
maintainer
#29 lore raw

This patch solves the problem that Li originally reported. If something registers a trace point belonging to a module, then it ups the ref count of the module. This prevents a process from registering a probe to a tracepoint belonging to a module and then having the module disappear.

Doing the example with perf in Li's original post, now errors on the rmmod, with "ERROR: Module trace_events_sample is in use".

Mathieu, can I have your acked-by on this?

include/linux/tracepoint.h +3 -2
index 0341f2e..055275b 100644
@@ -109,8 +109,9 @@ struct tracepoint {
 #define EXPORT_TRACEPOINT_SYMBOL(name)					\
 	EXPORT_SYMBOL(__tracepoint_##name)
 
-extern void tracepoint_update_probe_range(struct tracepoint *begin,
-	struct tracepoint *end);
+extern void tracepoint_update_probe_range(struct module *,
+					  struct tracepoint *begin,
+					  struct tracepoint *end);
 
 #else /* !CONFIG_TRACEPOINTS */
 #define DECLARE_TRACE_WITH_CALLBACK(name, proto, args, reg, unreg)	\
kernel/module.c +1 -1
index b182143..a8e69fa 100644
@@ -2974,7 +2974,7 @@ void module_update_tracepoints(void)
 	mutex_lock(&module_mutex);
 	list_for_each_entry(mod, &modules, list)
 		if (!mod->taints)
-			tracepoint_update_probe_range(mod->tracepoints,
+			tracepoint_update_probe_range(mod, mod->tracepoints,
 				mod->tracepoints + mod->num_tracepoints);
 	mutex_unlock(&module_mutex);
 }
kernel/tracepoint.c +14 -3
index 06f165a..089e6f9 100644
@@ -54,6 +54,7 @@ static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
  */
 struct tracepoint_entry {
 	struct hlist_node hlist;
+	struct module *mod;
 	void **funcs;
 	int refcount;	/* Number of times armed. 0 if disarmed. */
 	char name[0];
@@ -221,6 +222,7 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
 	memcpy(&e->name[0], name, name_len);
 	e->funcs = NULL;
 	e->refcount = 0;
+	e->mod = NULL;	/* Will be assigned in tracepoint_update_probe_range */
 	hlist_add_head(&e->hlist, head);
 	return e;
 }
@@ -231,6 +233,8 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
  */
 static inline void remove_tracepoint(struct tracepoint_entry *e)
 {
+	if (e->mod)
+		module_put(e->mod);
 	hlist_del(&e->hlist);
 	kfree(e);
 }
@@ -274,7 +278,8 @@ static void disable_tracepoint(struct tracepoint *elem)
  * Updates the probe callback corresponding to a range of tracepoints.
  */
 void
-tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
+tracepoint_update_probe_range(struct module *mod,
+			      struct tracepoint *begin, struct tracepoint *end)
 {
 	struct tracepoint *iter;
 	struct tracepoint_entry *mark_entry;
@@ -286,9 +291,15 @@ tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
 	for (iter = begin; iter < end; iter++) {
 		mark_entry = get_tracepoint(iter->name);
 		if (mark_entry) {
+			if (mod && !mark_entry->mod) {
+				if (!try_module_get(mod))
+					goto disable;
+				mark_entry->mod = mod;
+			}
 			set_tracepoint(&mark_entry, iter,
 					!!mark_entry->refcount);
 		} else {
+ disable:
 			disable_tracepoint(iter);
 		}
 	}
@@ -301,7 +312,7 @@ tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
 static void tracepoint_update_probes(void)
 {
 	/* Core kernel tracepoints */
-	tracepoint_update_probe_range(__start___tracepoints,
+	tracepoint_update_probe_range(NULL, __start___tracepoints,
 		__stop___tracepoints);
 	/* tracepoints in modules. */
 	module_update_tracepoints();
@@ -556,7 +567,7 @@ int tracepoint_module_notify(struct notifier_block *self,
 	switch (val) {
 	case MODULE_STATE_COMING:
 	case MODULE_STATE_GOING:
-		tracepoint_update_probe_range(mod->tracepoints,
+		tracepoint_update_probe_range(mod, mod->tracepoints,
 			mod->tracepoints + mod->num_tracepoints);
 		break;
 	}
#30 lore raw
Quoted 11 lines · * Steven Rostedt ([email protected]) wrote:

This patch solves the problem that Li originally reported. If something registers a trace point belonging to a module, then it ups the ref count of the module. This prevents a process from registering a probe to a tracepoint belonging to a module and then having the module disappear.

Doing the example with perf in Li's original post, now errors on the rmmod, with "ERROR: Module trace_events_sample is in use".

Mathieu, can I have your acked-by on this?

Sorry, it looks buggy.

It does not deal with the fact that tracepoints with the same name and arguments can be present in more than one module, or in a combination of kernel core and modules.

The struct tracepoint_entry is specific to a a tracepoint name, used for registration, but is eventually tied to all tracepoint instrumentation instances for this tracepoint name.

Mathieu

Quoted 106 lines
include/linux/tracepoint.h +3 -2
index 0341f2e..055275b 100644
@@ -109,8 +109,9 @@ struct tracepoint {
 #define EXPORT_TRACEPOINT_SYMBOL(name)					\
 	EXPORT_SYMBOL(__tracepoint_##name)
 
-extern void tracepoint_update_probe_range(struct tracepoint *begin,
-	struct tracepoint *end);
+extern void tracepoint_update_probe_range(struct module *,
+					  struct tracepoint *begin,
+					  struct tracepoint *end);
 
 #else /* !CONFIG_TRACEPOINTS */
 #define DECLARE_TRACE_WITH_CALLBACK(name, proto, args, reg, unreg)	\
kernel/module.c +1 -1
index b182143..a8e69fa 100644
@@ -2974,7 +2974,7 @@ void module_update_tracepoints(void)
 	mutex_lock(&module_mutex);
 	list_for_each_entry(mod, &modules, list)
 		if (!mod->taints)
-			tracepoint_update_probe_range(mod->tracepoints,
+			tracepoint_update_probe_range(mod, mod->tracepoints,
 				mod->tracepoints + mod->num_tracepoints);
 	mutex_unlock(&module_mutex);
 }
kernel/tracepoint.c +14 -3
index 06f165a..089e6f9 100644
@@ -54,6 +54,7 @@ static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
  */
 struct tracepoint_entry {
 	struct hlist_node hlist;
+	struct module *mod;
 	void **funcs;
 	int refcount;	/* Number of times armed. 0 if disarmed. */
 	char name[0];
@@ -221,6 +222,7 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
 	memcpy(&e->name[0], name, name_len);
 	e->funcs = NULL;
 	e->refcount = 0;
+	e->mod = NULL;	/* Will be assigned in tracepoint_update_probe_range */
 	hlist_add_head(&e->hlist, head);
 	return e;
 }
@@ -231,6 +233,8 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
  */
 static inline void remove_tracepoint(struct tracepoint_entry *e)
 {
+	if (e->mod)
+		module_put(e->mod);
 	hlist_del(&e->hlist);
 	kfree(e);
 }
@@ -274,7 +278,8 @@ static void disable_tracepoint(struct tracepoint *elem)
  * Updates the probe callback corresponding to a range of tracepoints.
  */
 void
-tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
+tracepoint_update_probe_range(struct module *mod,
+			      struct tracepoint *begin, struct tracepoint *end)
 {
 	struct tracepoint *iter;
 	struct tracepoint_entry *mark_entry;
@@ -286,9 +291,15 @@ tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
 	for (iter = begin; iter < end; iter++) {
 		mark_entry = get_tracepoint(iter->name);
 		if (mark_entry) {
+			if (mod && !mark_entry->mod) {
+				if (!try_module_get(mod))
+					goto disable;
+				mark_entry->mod = mod;
+			}
 			set_tracepoint(&mark_entry, iter,
 					!!mark_entry->refcount);
 		} else {
+ disable:
 			disable_tracepoint(iter);
 		}
 	}
@@ -301,7 +312,7 @@ tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
 static void tracepoint_update_probes(void)
 {
 	/* Core kernel tracepoints */
-	tracepoint_update_probe_range(__start___tracepoints,
+	tracepoint_update_probe_range(NULL, __start___tracepoints,
 		__stop___tracepoints);
 	/* tracepoints in modules. */
 	module_update_tracepoints();
@@ -556,7 +567,7 @@ int tracepoint_module_notify(struct notifier_block *self,
 	switch (val) {
 	case MODULE_STATE_COMING:
 	case MODULE_STATE_GOING:
-		tracepoint_update_probe_range(mod->tracepoints,
+		tracepoint_update_probe_range(mod, mod->tracepoints,
 			mod->tracepoints + mod->num_tracepoints);
 		break;
 	}
Signature · 2 lines
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
#31 lore raw
Quoted 15 lines · * Mathieu Desnoyers ([email protected]) wrote:
Quoted 11 lines · * Steven Rostedt ([email protected]) wrote:

This patch solves the problem that Li originally reported. If something registers a trace point belonging to a module, then it ups the ref count of the module. This prevents a process from registering a probe to a tracepoint belonging to a module and then having the module disappear.

Doing the example with perf in Li's original post, now errors on the rmmod, with "ERROR: Module trace_events_sample is in use".

Mathieu, can I have your acked-by on this?

Sorry, it looks buggy.

Quoted 8 lines

It does not deal with the fact that tracepoints with the same name and arguments can be present in more than one module, or in a combination of kernel core and modules.

The struct tracepoint_entry is specific to a a tracepoint name, used for registration, but is eventually tied to all tracepoint instrumentation instances for this tracepoint name.

Looking at the original post:
http://lkml.org/lkml/2009/8/24/5

the problem seems to be caused by the fact that the trace_event_profile.c keeps some knowledge of modules in internal data structures, but does not get notified of module unloads. Why don't we fix that instead ?

A quick glance at it seems to indicate that it lazily discovers new modules when the tracepoints are hit. Using module load/unload notifiers would be more appropriate. Or maybe adding a notifier call to tracepoint.c, calling notification callbacks for probe modules which need to know when the connected tracepoints are changing (when they are connected/disconnected) would probably be even more appropriate. As a result, it would remove the dynamic verification cost implied by lazy data structure lookup and check each time the probe is fired.

Mathieu

Quoted 112 lines

Mathieu

Quoted 106 lines
include/linux/tracepoint.h +3 -2
index 0341f2e..055275b 100644
@@ -109,8 +109,9 @@ struct tracepoint {
 #define EXPORT_TRACEPOINT_SYMBOL(name)					\
 	EXPORT_SYMBOL(__tracepoint_##name)
 
-extern void tracepoint_update_probe_range(struct tracepoint *begin,
-	struct tracepoint *end);
+extern void tracepoint_update_probe_range(struct module *,
+					  struct tracepoint *begin,
+					  struct tracepoint *end);
 
 #else /* !CONFIG_TRACEPOINTS */
 #define DECLARE_TRACE_WITH_CALLBACK(name, proto, args, reg, unreg)	\
kernel/module.c +1 -1
index b182143..a8e69fa 100644
@@ -2974,7 +2974,7 @@ void module_update_tracepoints(void)
 	mutex_lock(&module_mutex);
 	list_for_each_entry(mod, &modules, list)
 		if (!mod->taints)
-			tracepoint_update_probe_range(mod->tracepoints,
+			tracepoint_update_probe_range(mod, mod->tracepoints,
 				mod->tracepoints + mod->num_tracepoints);
 	mutex_unlock(&module_mutex);
 }
kernel/tracepoint.c +14 -3
index 06f165a..089e6f9 100644
@@ -54,6 +54,7 @@ static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
  */
 struct tracepoint_entry {
 	struct hlist_node hlist;
+	struct module *mod;
 	void **funcs;
 	int refcount;	/* Number of times armed. 0 if disarmed. */
 	char name[0];
@@ -221,6 +222,7 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
 	memcpy(&e->name[0], name, name_len);
 	e->funcs = NULL;
 	e->refcount = 0;
+	e->mod = NULL;	/* Will be assigned in tracepoint_update_probe_range */
 	hlist_add_head(&e->hlist, head);
 	return e;
 }
@@ -231,6 +233,8 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
  */
 static inline void remove_tracepoint(struct tracepoint_entry *e)
 {
+	if (e->mod)
+		module_put(e->mod);
 	hlist_del(&e->hlist);
 	kfree(e);
 }
@@ -274,7 +278,8 @@ static void disable_tracepoint(struct tracepoint *elem)
  * Updates the probe callback corresponding to a range of tracepoints.
  */
 void
-tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
+tracepoint_update_probe_range(struct module *mod,
+			      struct tracepoint *begin, struct tracepoint *end)
 {
 	struct tracepoint *iter;
 	struct tracepoint_entry *mark_entry;
@@ -286,9 +291,15 @@ tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
 	for (iter = begin; iter < end; iter++) {
 		mark_entry = get_tracepoint(iter->name);
 		if (mark_entry) {
+			if (mod && !mark_entry->mod) {
+				if (!try_module_get(mod))
+					goto disable;
+				mark_entry->mod = mod;
+			}
 			set_tracepoint(&mark_entry, iter,
 					!!mark_entry->refcount);
 		} else {
+ disable:
 			disable_tracepoint(iter);
 		}
 	}
@@ -301,7 +312,7 @@ tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
 static void tracepoint_update_probes(void)
 {
 	/* Core kernel tracepoints */
-	tracepoint_update_probe_range(__start___tracepoints,
+	tracepoint_update_probe_range(NULL, __start___tracepoints,
 		__stop___tracepoints);
 	/* tracepoints in modules. */
 	module_update_tracepoints();
@@ -556,7 +567,7 @@ int tracepoint_module_notify(struct notifier_block *self,
 	switch (val) {
 	case MODULE_STATE_COMING:
 	case MODULE_STATE_GOING:
-		tracepoint_update_probe_range(mod->tracepoints,
+		tracepoint_update_probe_range(mod, mod->tracepoints,
 			mod->tracepoints + mod->num_tracepoints);
 		break;
 	}
Signature · 2 lines
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
Signature · 2 lines
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
maintainer
#32 lore raw
Quoted 8 lines · Mathieu Desnoyers
Quoted 7 lines · * Mathieu Desnoyers ([email protected]) wrote:
Quoted 6 lines · * Steven Rostedt ([email protected]) wrote:

This patch solves the problem that Li originally reported. If something registers a trace point belonging to a module, then it ups the ref count of the module. This prevents a process from registering a probe to a tracepoint belonging to a module and then having the module disappear.

Quoted 7 lines
Quoted 7 lines
Quoted 5 lines

Doing the example with perf in Li's original post, now errors on the rmmod, with "ERROR: Module trace_events_sample is in use".

Mathieu, can I have your acked-by on this?

Sorry, it looks buggy.

Well, it is not that buggy. It would only prevent modules from unloading if another tracepoint with the same name and parameters had a probe registered. More of a too big of a tent deal.

Quoted 9 lines
Quoted 9 lines

It does not deal with the fact that tracepoints with the same name and arguments can be present in more than one module, or in a combination of kernel core and modules.

The struct tracepoint_entry is specific to a a tracepoint name, used for registration, but is eventually tied to all tracepoint instrumentation instances for this tracepoint name.

Anyway, this prevents your tracepoints from doing the odd things of loading a probe before it exists. Well you can, but then you prevent the unload of the module that registered it. Fine, I chucked out that patch.

Quoted 9 lines

Looking at the original post:
http://lkml.org/lkml/2009/8/24/5

the problem seems to be caused by the fact that the trace_event_profile.c keeps some knowledge of modules in internal data structures, but does not get notified of module unloads. Why don't we fix that instead ?

Quoted 9 lines

A quick glance at it seems to indicate that it lazily discovers new modules when the tracepoints are hit. Using module load/unload notifiers would be more appropriate. Or maybe adding a notifier call to tracepoint.c, calling notification callbacks for probe modules which need to know when the connected tracepoints are changing (when they are connected/disconnected) would probably be even more appropriate. As a result, it would remove the dynamic verification cost implied by lazy data structure lookup and check each time the probe is fired.

Peter is correct that he should not need to worry about modules, he doesn't build kernels with them ;-)

Here's another patch that moves the module ref count administration to the trace events themselves. This should satisfy both you and Peter.

include/trace/ftrace.h +20 -2
index 1b1f742..3f7c5dc 100644
@@ -390,6 +390,20 @@ static inline int ftrace_get_offsets_##call(				\
  *
  */
 
+#ifdef MODULE
+# define event_trace_up_ref()					\
+	do {							\
+		if (!try_module_get(THIS_MODULE)) {		\
+			atomic_dec(&event_call->profile_count);	\
+			return -1;				\
+		}						\
+	} while (0)
+# define event_trace_down_ref() module_put(THIS_MODULE)
+#else
+# define event_trace_up_ref() do { } while (0)
+# define event_trace_down_ref() do { } while (0)
+#endif
+		
 #undef TRACE_EVENT
 #define TRACE_EVENT(call, proto, args, tstruct, assign, print)		\
 									\
@@ -399,16 +413,20 @@ static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \
 {									\
 	int ret = 0;							\
 									\
-	if (!atomic_inc_return(&event_call->profile_count))		\
+	if (!atomic_inc_return(&event_call->profile_count)) {		\
+		event_trace_up_ref();					\
 		ret = register_trace_##call(ftrace_profile_##call);	\
+	}								\
 									\
 	return ret;							\
 }									\
 									\
 static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
 {									\
-	if (atomic_add_negative(-1, &event_call->profile_count))	\
+	if (atomic_add_negative(-1, &event_call->profile_count)) {	\
 		unregister_trace_##call(ftrace_profile_##call);		\
+		event_trace_down_ref();					\
+	}								\
 }
 
 #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
#33 lore raw
Quoted 30 lines · * Steven Rostedt ([email protected]) wrote:
Quoted 8 lines · Mathieu Desnoyers
Quoted 7 lines · * Mathieu Desnoyers ([email protected]) wrote:
Quoted 6 lines · * Steven Rostedt ([email protected]) wrote:

This patch solves the problem that Li originally reported. If something registers a trace point belonging to a module, then it ups the ref count of the module. This prevents a process from registering a probe to a tracepoint belonging to a module and then having the module disappear.

Quoted 7 lines
Quoted 7 lines
Quoted 5 lines

Doing the example with perf in Li's original post, now errors on the rmmod, with "ERROR: Module trace_events_sample is in use".

Mathieu, can I have your acked-by on this?

Sorry, it looks buggy.

Well, it is not that buggy. It would only prevent modules from unloading if another tracepoint with the same name and parameters had a probe registered. More of a too big of a tent deal.

Quoted 5 lines
Quoted 5 lines

It does not deal with the fact that tracepoints with the same name and arguments can be present in more than one module, or in a combination of kernel core and modules.

Quoted 9 lines
Quoted 4 lines
Quoted 4 lines

The struct tracepoint_entry is specific to a a tracepoint name, used for registration, but is eventually tied to all tracepoint instrumentation instances for this tracepoint name.

Anyway, this prevents your tracepoints from doing the odd things of loading a probe before it exists. Well you can, but then you prevent the unload of the module that registered it. Fine, I chucked out that patch.

you should try adding the required tracepoint_synchronize_unregister() call to ftrace_profile_disable_##call in ftrace.h. I expect this will fix your problem.

Note that this is a bit slow to call it at each unregistration. Ideally, a module containing tracepoint probes should call this synchronization primitive only once at module unload.

Mathieu

Quoted 75 lines
Quoted 9 lines

Looking at the original post:
http://lkml.org/lkml/2009/8/24/5

the problem seems to be caused by the fact that the trace_event_profile.c keeps some knowledge of modules in internal data structures, but does not get notified of module unloads. Why don't we fix that instead ?

Quoted 9 lines

A quick glance at it seems to indicate that it lazily discovers new modules when the tracepoints are hit. Using module load/unload notifiers would be more appropriate. Or maybe adding a notifier call to tracepoint.c, calling notification callbacks for probe modules which need to know when the connected tracepoints are changing (when they are connected/disconnected) would probably be even more appropriate. As a result, it would remove the dynamic verification cost implied by lazy data structure lookup and check each time the probe is fired.

Peter is correct that he should not need to worry about modules, he doesn't build kernels with them ;-)

Here's another patch that moves the module ref count administration to the trace events themselves. This should satisfy both you and Peter.

include/trace/ftrace.h +20 -2
index 1b1f742..3f7c5dc 100644
@@ -390,6 +390,20 @@ static inline int ftrace_get_offsets_##call(				\
  *
  */
 
+#ifdef MODULE
+# define event_trace_up_ref()					\
+	do {							\
+		if (!try_module_get(THIS_MODULE)) {		\
+			atomic_dec(&event_call->profile_count);	\
+			return -1;				\
+		}						\
+	} while (0)
+# define event_trace_down_ref() module_put(THIS_MODULE)
+#else
+# define event_trace_up_ref() do { } while (0)
+# define event_trace_down_ref() do { } while (0)
+#endif
+		
 #undef TRACE_EVENT
 #define TRACE_EVENT(call, proto, args, tstruct, assign, print)		\
 									\
@@ -399,16 +413,20 @@ static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \
 {									\
 	int ret = 0;							\
 									\
-	if (!atomic_inc_return(&event_call->profile_count))		\
+	if (!atomic_inc_return(&event_call->profile_count)) {		\
+		event_trace_up_ref();					\
 		ret = register_trace_##call(ftrace_profile_##call);	\
+	}								\
 									\
 	return ret;							\
 }									\
 									\
 static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
 {									\
-	if (atomic_add_negative(-1, &event_call->profile_count))	\
+	if (atomic_add_negative(-1, &event_call->profile_count)) {	\
 		unregister_trace_##call(ftrace_profile_##call);		\
+		event_trace_down_ref();					\
+	}								\
 }
 
 #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
Signature · 2 lines
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
maintainer
#34 lore raw
Quoted 10 lines · Mathieu Desnoyers
Quoted 4 lines · * Steven Rostedt ([email protected]) wrote:

Anyway, this prevents your tracepoints from doing the odd things of loading a probe before it exists. Well you can, but then you prevent the unload of the module that registered it. Fine, I chucked out that patch.

you should try adding the required tracepoint_synchronize_unregister() call to ftrace_profile_disable_##call in ftrace.h. I expect this will fix your problem.

Just did, and it did not solve the bug.

Quoted 4 lines

Note that this is a bit slow to call it at each unregistration. Ideally, a module containing tracepoint probes should call this synchronization primitive only once at module unload.

The bug looks like it is registering a probe, but not unregistering it before leaving the module. But when the module is loaded again, it now has a bad function to call when the tracepoint is hit.

-- Steve

#35 lore raw
Quoted 20 lines · * Steven Rostedt ([email protected]) wrote:
Quoted 10 lines · Mathieu Desnoyers
Quoted 4 lines · * Steven Rostedt ([email protected]) wrote:

Anyway, this prevents your tracepoints from doing the odd things of loading a probe before it exists. Well you can, but then you prevent the unload of the module that registered it. Fine, I chucked out that patch.

you should try adding the required tracepoint_synchronize_unregister() call to ftrace_profile_disable_##call in ftrace.h. I expect this will fix your problem.

Just did, and it did not solve the bug.

Quoted 4 lines

Note that this is a bit slow to call it at each unregistration. Ideally, a module containing tracepoint probes should call this synchronization primitive only once at module unload.

Quoted 5 lines

The bug looks like it is registering a probe, but not unregistering it before leaving the module. But when the module is loaded again, it now has a bad function to call when the tracepoint is hit.

-- Steve

Hrm, looking at #define _TRACE_PROFILE(call, proto, args) from ftrace.h is interesting. The profile reg/unreg are actually visible as:

#define _TRACE_PROFILE_INIT(call)                                       \
        .profile_count = ATOMIC_INIT(-1),                               \
        .profile_enable = ftrace_profile_enable_##call,                 \

.profile_disable = ftrace_profile_disable_##call,

So each time profiling is activated, we iterate on all tracepoints to call their profile_enable. But they stay active even when the module is unloaded. That's odd.

If _anything_ external to the module containing the probe takes responsibility for registering the probe to a tracepoint, it should unregister it before the module exits.

One simple quick-and-dirty to do this is to increment the refcount of the module containing the probe before enabling it and decrementing the refcount after disabling its probe. This is one of the solutions you proposed so far, and it looks sane. I use something similar in LTTng btw. Probe modules are refcounted when probes are connected to a tracepoints by the "probe manager".

However, because you put the DEFINE_TRACE within the same module as your actual code, you cannot unload the module unless you stop profiling. One way to fix this is, as Peter proposed, use a different module for the probe than the module you are probing. I agree with him. The module used as probe for tracing is expected to be somehow tied to tracing sessions, and I think it is OK to expect the probe module no to be unloadable as long as a tracing session which uses it is active.

Mathieu

Signature · 2 lines
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
author
#36 lore raw
In reply to Steven Rostedt
Quoted 6 lines

Peter is correct that he should not need to worry about modules, he doesn't build kernels with them ;-)

Here's another patch that moves the module ref count administration to the trace events themselves. This should satisfy both you and Peter.

In fact I had this patch in my mind, but I thought Peter insist on fixing it in tracepoint. So I'm fine with this. :)

Quoted 7 lines
include/trace/ftrace.h +0 -0
index 1b1f742..3f7c5dc 100644

include/trace/ftrace.h static inline int ftrace_get_offsets_##call( \ · line 398

Quoted 9 lines
include/trace/ftrace.h +6 -0
@@ -390,6 +390,20 @@ static inline int ftrace_get_offsets_##call(				\
  *
  */
 
+#ifdef MODULE
+# define event_trace_up_ref()					\
+	do {							\
+		if (!try_module_get(THIS_MODULE)) {		\
+			atomic_dec(&event_call->profile_count);	\
+			return -1;				\

Should return -1 or -errno like -ENOENT?

Quoted 36 lines
include/trace/ftrace.h +14 -2
+		}						\
+	} while (0)
+# define event_trace_down_ref() module_put(THIS_MODULE)
+#else
+# define event_trace_up_ref() do { } while (0)
+# define event_trace_down_ref() do { } while (0)
+#endif
+		
 #undef TRACE_EVENT
 #define TRACE_EVENT(call, proto, args, tstruct, assign, print)		\
 									\
@@ -399,16 +413,20 @@ static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \
 {									\
 	int ret = 0;							\
 									\
-	if (!atomic_inc_return(&event_call->profile_count))		\
+	if (!atomic_inc_return(&event_call->profile_count)) {		\
+		event_trace_up_ref();					\
 		ret = register_trace_##call(ftrace_profile_##call);	\
+	}								\
 									\
 	return ret;							\
 }									\
 									\
 static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
 {									\
-	if (atomic_add_negative(-1, &event_call->profile_count))	\
+	if (atomic_add_negative(-1, &event_call->profile_count)) {	\
 		unregister_trace_##call(ftrace_profile_##call);		\
+		event_trace_down_ref();					\
+	}								\
 }
 
 #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
maintainer
#37 lore raw
Quoted 9 lines · Li Zefan
Quoted 6 lines

Peter is correct that he should not need to worry about modules, he doesn't build kernels with them ;-)

Here's another patch that moves the module ref count administration to the trace events themselves. This should satisfy both you and Peter.

In fact I had this patch in my mind, but I thought Peter insist on fixing it in tracepoint. So I'm fine with this. :)

Tracepoints I consider a more low level API. Using them takes more hand work and the user needs to know what they are doing and thus, must take into account modules.

This code is automatic, and a much higher level API. The user shoud not need to worry about modules, thus the protection belongs here.

If we try to make it so a module can not have register itself, then that will just complicate the TRACE_EVENT macros even more. And those are complex enough.

Quoted 11 lines
Quoted 11 lines
include/trace/ftrace.h +0 -0
index 1b1f742..3f7c5dc 100644
@@ -390,6 +390,20 @@ static inline int ftrace_get_offsets_##call(				\
  *
  */
 
Quoted 8 lines

include/trace/ftrace.h static inline int ftrace_get_offsets_##call( \ · line 398

Quoted 6 lines
include/trace/ftrace.h +6 -0
+#ifdef MODULE
+# define event_trace_up_ref()					\
+	do {							\
+		if (!try_module_get(THIS_MODULE)) {		\
+			atomic_dec(&event_call->profile_count);	\
+			return -1;				\

Should return -1 or -errno like -ENOENT?

Thanks, I was being lazy and really did not know what to have it return. I'll commit it with a -ENOENT.

-- Steve

Quoted 38 lines
Quoted 37 lines
include/trace/ftrace.h +14 -2
+		}						\
+	} while (0)
+# define event_trace_down_ref() module_put(THIS_MODULE)
+#else
+# define event_trace_up_ref() do { } while (0)
+# define event_trace_down_ref() do { } while (0)
+#endif
+		
 #undef TRACE_EVENT
 #define TRACE_EVENT(call, proto, args, tstruct, assign, print)		\
 									\
@@ -399,16 +413,20 @@ static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \
 {									\
 	int ret = 0;							\
 									\
-	if (!atomic_inc_return(&event_call->profile_count))		\
+	if (!atomic_inc_return(&event_call->profile_count)) {		\
+		event_trace_up_ref();					\
 		ret = register_trace_##call(ftrace_profile_##call);	\
+	}								\
 									\
 	return ret;							\
 }									\
 									\
 static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
 {									\
-	if (atomic_add_negative(-1, &event_call->profile_count))	\
+	if (atomic_add_negative(-1, &event_call->profile_count)) {	\
 		unregister_trace_##call(ftrace_profile_##call);		\
+		event_trace_down_ref();					\
+	}								\
 }
 
 #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
#38 lore raw
Quoted 44 lines · * Steven Rostedt ([email protected]) wrote:
Quoted 9 lines · Li Zefan
Quoted 6 lines

Peter is correct that he should not need to worry about modules, he doesn't build kernels with them ;-)

Here's another patch that moves the module ref count administration to the trace events themselves. This should satisfy both you and Peter.

In fact I had this patch in my mind, but I thought Peter insist on fixing it in tracepoint. So I'm fine with this. :)

Tracepoints I consider a more low level API. Using them takes more hand work and the user needs to know what they are doing and thus, must take into account modules.

This code is automatic, and a much higher level API. The user shoud not need to worry about modules, thus the protection belongs here.

If we try to make it so a module can not have register itself, then that will just complicate the TRACE_EVENT macros even more. And those are complex enough.

Quoted 11 lines
Quoted 11 lines
include/trace/ftrace.h +0 -0
index 1b1f742..3f7c5dc 100644
@@ -390,6 +390,20 @@ static inline int ftrace_get_offsets_##call(				\
  *
  */
 
Quoted 8 lines

include/trace/ftrace.h static inline int ftrace_get_offsets_##call( \ · line 398

Quoted 6 lines
include/trace/ftrace.h +6 -0
+#ifdef MODULE
+# define event_trace_up_ref()					\
+	do {							\
+		if (!try_module_get(THIS_MODULE)) {		\
+			atomic_dec(&event_call->profile_count);	\
+			return -1;				\

Should return -1 or -errno like -ENOENT?

Quoted 3 lines

Thanks, I was being lazy and really did not know what to have it return. I'll commit it with a -ENOENT.

Looks good. Just don't forget to eventually add the "synchronize" calls between tracepoint unregistration and the removal of their module. There is a race condition in the way you do it currently.

Thanks,

Mathieu

Quoted 40 lines

-- Steve

Quoted 38 lines
Quoted 37 lines
include/trace/ftrace.h +14 -2
+		}						\
+	} while (0)
+# define event_trace_down_ref() module_put(THIS_MODULE)
+#else
+# define event_trace_up_ref() do { } while (0)
+# define event_trace_down_ref() do { } while (0)
+#endif
+		
 #undef TRACE_EVENT
 #define TRACE_EVENT(call, proto, args, tstruct, assign, print)		\
 									\
@@ -399,16 +413,20 @@ static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \
 {									\
 	int ret = 0;							\
 									\
-	if (!atomic_inc_return(&event_call->profile_count))		\
+	if (!atomic_inc_return(&event_call->profile_count)) {		\
+		event_trace_up_ref();					\
 		ret = register_trace_##call(ftrace_profile_##call);	\
+	}								\
 									\
 	return ret;							\
 }									\
 									\
 static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
 {									\
-	if (atomic_add_negative(-1, &event_call->profile_count))	\
+	if (atomic_add_negative(-1, &event_call->profile_count)) {	\
 		unregister_trace_##call(ftrace_profile_##call);		\
+		event_trace_down_ref();					\
+	}								\
 }
 
 #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
Signature · 2 lines
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
maintainer
#39 lore raw
Quoted 3 lines · Mathieu Desnoyers

Looks good. Just don't forget to eventually add the "synchronize" calls between tracepoint unregistration and the removal of their module. There is a race condition in the way you do it currently.

I'm trying to figure out the race here. What will disappear in the tracepoint? Could you give a brief example of the issue.

Thanks,

-- Steve

#40 lore raw
Quoted 9 lines · * Steven Rostedt ([email protected]) wrote:
Quoted 3 lines · Mathieu Desnoyers

Looks good. Just don't forget to eventually add the "synchronize" calls between tracepoint unregistration and the removal of their module. There is a race condition in the way you do it currently.

I'm trying to figure out the race here. What will disappear in the tracepoint? Could you give a brief example of the issue.

Sure,

Let's say we have a tracepoint in module instrumented.c, and a probe in module probe.c. The probe is registered by module probe.c init through the tracepoint infrastructure to connect to the tracepoint in instrumented.c. Unregistration is done in probe.c module exit.

As the instrumented code get executed (let's say periodically), it calls the connected probe. Preemption is disabled around the call.

If you unload the probe.c module, the module exit will unregister the probe, but the probe code can still be in use by another CPU. You have to wait for quiescent state with the tracepoint synchronize (which is just a wrapper over synchronize_sched() before you are allowed to complete module unload. Otherwise, you will end up reclaiming module memory that is still used by probe execution.

A test-case for this would be to create a probe with a delay in it, and an instrumented module calling the instrumentation in a loop. On a SMP system, running the instrumented code and probe load/unload in loops should trigger this race.

Mathieu

Quoted 6 lines

Thanks,

-- Steve

Signature · 2 lines
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
maintainer
#41 lore raw
Quoted 28 lines · Mathieu Desnoyers
Quoted 9 lines · * Steven Rostedt ([email protected]) wrote:
Quoted 3 lines · Mathieu Desnoyers

Looks good. Just don't forget to eventually add the "synchronize" calls between tracepoint unregistration and the removal of their module. There is a race condition in the way you do it currently.

I'm trying to figure out the race here. What will disappear in the tracepoint? Could you give a brief example of the issue.

Sure,

Let's say we have a tracepoint in module instrumented.c, and a probe in module probe.c. The probe is registered by module probe.c init through the tracepoint infrastructure to connect to the tracepoint in instrumented.c. Unregistration is done in probe.c module exit.

As the instrumented code get executed (let's say periodically), it calls the connected probe. Preemption is disabled around the call.

If you unload the probe.c module, the module exit will unregister the probe, but the probe code can still be in use by another CPU. You have to wait for quiescent state with the tracepoint synchronize (which is just a wrapper over synchronize_sched() before you are allowed to complete module unload. Otherwise, you will end up reclaiming module memory that is still used by probe execution.

Quoted 4 lines

A test-case for this would be to create a probe with a delay in it, and an instrumented module calling the instrumentation in a loop. On a SMP system, running the instrumented code and probe load/unload in loops should trigger this race.

Thanks for the explanation. So let me see if I get this correct.

For this race to occur, the probe (code that hooks to the tracepoint) must be in module that does not contain the tracepoint. We don't even need more than one module, this could occur even with a core tracepoint. If a module registers it, if it unregisters before unloading, the tracepoint may be hit before the unregister and executing while the module is unloading.

I don't think we need to worry about this with the case of TRACE_EVENT and ftrace.h. The reason is that the trace point and probes are always in the same location. The MACROS set up the probe code with the modules. Thus, to remove the module, you must also remove the tracepoint itself along with the probe. If you can be executing in the probe, then you must have hit the trace point. If you hit the trace point, then you are executing code inside the module you are removing, which is a bug in the module code itself.

Using the ftrace.h MACROS limits the use of tracepoints and this race does not exist. I feel we are safe not needing to have the tracepoint_synchronize_unregister within the ftrace.h code.

-- Steve

#42 lore raw
Quoted 53 lines · * Steven Rostedt ([email protected]) wrote:
Quoted 28 lines · Mathieu Desnoyers
Quoted 9 lines · * Steven Rostedt ([email protected]) wrote:
Quoted 3 lines · Mathieu Desnoyers

Looks good. Just don't forget to eventually add the "synchronize" calls between tracepoint unregistration and the removal of their module. There is a race condition in the way you do it currently.

I'm trying to figure out the race here. What will disappear in the tracepoint? Could you give a brief example of the issue.

Sure,

Let's say we have a tracepoint in module instrumented.c, and a probe in module probe.c. The probe is registered by module probe.c init through the tracepoint infrastructure to connect to the tracepoint in instrumented.c. Unregistration is done in probe.c module exit.

As the instrumented code get executed (let's say periodically), it calls the connected probe. Preemption is disabled around the call.

If you unload the probe.c module, the module exit will unregister the probe, but the probe code can still be in use by another CPU. You have to wait for quiescent state with the tracepoint synchronize (which is just a wrapper over synchronize_sched() before you are allowed to complete module unload. Otherwise, you will end up reclaiming module memory that is still used by probe execution.

Quoted 4 lines

A test-case for this would be to create a probe with a delay in it, and an instrumented module calling the instrumentation in a loop. On a SMP system, running the instrumented code and probe load/unload in loops should trigger this race.

Thanks for the explanation. So let me see if I get this correct.

For this race to occur, the probe (code that hooks to the tracepoint) must be in module that does not contain the tracepoint. We don't even need more than one module, this could occur even with a core tracepoint. If a module registers it, if it unregisters before unloading, the tracepoint may be hit before the unregister and executing while the module is unloading.

I don't think we need to worry about this with the case of TRACE_EVENT and ftrace.h. The reason is that the trace point and probes are always in the same location. The MACROS set up the probe code with the modules. Thus, to remove the module, you must also remove the tracepoint itself along with the probe. If you can be executing in the probe, then you must have hit the trace point. If you hit the trace point, then you are executing code inside the module you are removing, which is a bug in the module code itself.

Quoted 4 lines

Using the ftrace.h MACROS limits the use of tracepoints and this race does not exist. I feel we are safe not needing to have the tracepoint_synchronize_unregister within the ftrace.h code.

Looks right. If you can guarantee that the probe is only called from tracepoints located within the same module as the probe, you should be safe without tracepoint_synchronize_unregister. It's worth adding a comment in ftrace.h explaining that though.

Mathieu

Quoted 2 lines

-- Steve

Signature · 2 lines
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
maintainer
#43 lore raw
Quoted 21 lines · Mathieu Desnoyers
Quoted 16 lines

For this race to occur, the probe (code that hooks to the tracepoint) must be in module that does not contain the tracepoint. We don't even need more than one module, this could occur even with a core tracepoint. If a module registers it, if it unregisters before unloading, the tracepoint may be hit before the unregister and executing while the module is unloading.

I don't think we need to worry about this with the case of TRACE_EVENT and ftrace.h. The reason is that the trace point and probes are always in the same location. The MACROS set up the probe code with the modules. Thus, to remove the module, you must also remove the tracepoint itself along with the probe. If you can be executing in the probe, then you must have hit the trace point. If you hit the trace point, then you are executing code inside the module you are removing, which is a bug in the module code itself.

Quoted 4 lines

Using the ftrace.h MACROS limits the use of tracepoints and this race does not exist. I feel we are safe not needing to have the tracepoint_synchronize_unregister within the ftrace.h code.

Quoted 4 lines

Looks right. If you can guarantee that the probe is only called from tracepoints located within the same module as the probe, you should be safe without tracepoint_synchronize_unregister. It's worth adding a comment in ftrace.h explaining that though.

Yeah, I tried to trick the code to see if I can get a probe in another module than the trace point, and I get nothing but linking errors. I'm sure if I work hard enough, I may trick it to do so, but if anyone does that, I'll slap a big fat NAK on it ;-)

OK, I modify my fix to add a comment to this effect.

Thanks,

-- Steve

#44 lore raw
In reply to Steven Rostedt
Quoted 32 lines · Steven Rostedt

Here's another patch that moves the module ref count administration to the trace events themselves. This should satisfy both you and Peter.

include/trace/ftrace.h +17 -1
+#ifdef MODULE
+# define event_trace_up_ref()					\
+	do {							\
+		if (!try_module_get(THIS_MODULE)) {		\
+			atomic_dec(&event_call->profile_count);	\
+			return -1;				\
+		}						\
+	} while (0)
+# define event_trace_down_ref() module_put(THIS_MODULE)
+#else
+# define event_trace_up_ref() do { } while (0)
+# define event_trace_down_ref() do { } while (0)
+#endif
+		
 #undef TRACE_EVENT
 #define TRACE_EVENT(call, proto, args, tstruct, assign, print)		\
 									\
@@ -399,16 +413,20 @@ static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \
 {									\
 	int ret = 0;							\
 									\
-	if (!atomic_inc_return(&event_call->profile_count))		\
+	if (!atomic_inc_return(&event_call->profile_count)) {		\
+		event_trace_up_ref();					\
 		ret = register_trace_##call(ftrace_profile_##call);	\
+	}								\
 									\
 	return ret;							\

include/trace/ftrace.h static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \ · line 430

Quoted 10 lines
include/trace/ftrace.h +3 -1
 }									\
 									\
 static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
 {									\
-	if (atomic_add_negative(-1, &event_call->profile_count))	\
+	if (atomic_add_negative(-1, &event_call->profile_count)) {	\
 		unregister_trace_##call(ftrace_profile_##call);		\
+		event_trace_down_ref();					\
+	}								\
 }

Ha, almost, make that {un,}register_ftrace_##call() and stick the module gunk in there, and I'm ok.

maintainer
#45 lore raw
Quoted 18 lines · Peter Zijlstra
Quoted 17 lines · Steven Rostedt
include/trace/ftrace.h +3 -2
 									\
@@ -399,16 +413,20 @@ static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \
 {									\
 	int ret = 0;							\
 									\
-	if (!atomic_inc_return(&event_call->profile_count))		\
+	if (!atomic_inc_return(&event_call->profile_count)) {		\
+		event_trace_up_ref();					\
 		ret = register_trace_##call(ftrace_profile_##call);	\
+	}								\
 									\
 	return ret;							\
 }									\
 									\
 static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
 {									\
-	if (atomic_add_negative(-1, &event_call->profile_count))	\
Quoted 8 lines

include/trace/ftrace.h static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \ · line 430

Quoted 5 lines
include/trace/ftrace.h +3 -0
+	if (atomic_add_negative(-1, &event_call->profile_count)) {	\
 		unregister_trace_##call(ftrace_profile_##call);		\
+		event_trace_down_ref();					\
+	}								\
 }

Ha, almost, make that {un,}register_ftrace_##call() and stick the module gunk in there, and I'm ok.

Peter, that {un,}register_tace_##call is the tracepoint code. The DEFINE_TRACE creates those functions. This is the lowest level we can go in the ftrace.h file.

-- Steve

#46 lore raw
Quoted 29 lines · Steven Rostedt
Quoted 18 lines · Peter Zijlstra
Quoted 17 lines · Steven Rostedt
include/trace/ftrace.h +3 -2
 									\
@@ -399,16 +413,20 @@ static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \
 {									\
 	int ret = 0;							\
 									\
-	if (!atomic_inc_return(&event_call->profile_count))		\
+	if (!atomic_inc_return(&event_call->profile_count)) {		\
+		event_trace_up_ref();					\
 		ret = register_trace_##call(ftrace_profile_##call);	\
+	}								\
 									\
 	return ret;							\
 }									\
 									\
 static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
 {									\
-	if (atomic_add_negative(-1, &event_call->profile_count))	\
Quoted 8 lines

include/trace/ftrace.h static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \ · line 430

Quoted 5 lines
include/trace/ftrace.h +3 -0
+	if (atomic_add_negative(-1, &event_call->profile_count)) {	\
 		unregister_trace_##call(ftrace_profile_##call);		\
+		event_trace_down_ref();					\
+	}								\
 }

Ha, almost, make that {un,}register_ftrace_##call() and stick the module gunk in there, and I'm ok.

Quoted 3 lines

Peter, that {un,}register_tace_##call is the tracepoint code. The DEFINE_TRACE creates those functions. This is the lowest level we can go in the ftrace.h file.

Did you miss the extra f?

You can wrap the regular {un,}register_trace_##call() things in {un,}register_/F/trace_##call() functions which also take care of the module stuff.

Your patch is virtually identical to the one that started this thread, everybody using the TRACE_EVENT() things will still need to worry about modules.

maintainer
#47 lore raw
Quoted 30 lines · Peter Zijlstra
Quoted 29 lines · Steven Rostedt
Quoted 26 lines · Peter Zijlstra
Quoted 12 lines · Steven Rostedt
include/trace/ftrace.h +3 -1
 									\
@@ -399,16 +413,20 @@ static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \
 {									\
 	int ret = 0;							\
 									\
-	if (!atomic_inc_return(&event_call->profile_count))		\
+	if (!atomic_inc_return(&event_call->profile_count)) {		\
+		event_trace_up_ref();					\
 		ret = register_trace_##call(ftrace_profile_##call);	\
+	}								\
 									\
 	return ret;							\

include/trace/ftrace.h static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \ · line 430

Quoted 10 lines
include/trace/ftrace.h +3 -1
 }									\
 									\
 static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
 {									\
-	if (atomic_add_negative(-1, &event_call->profile_count))	\
+	if (atomic_add_negative(-1, &event_call->profile_count)) {	\
 		unregister_trace_##call(ftrace_profile_##call);		\
+		event_trace_down_ref();					\
+	}								\
 }

Ha, almost, make that {un,}register_ftrace_##call() and stick the module gunk in there, and I'm ok.

Quoted 5 lines
Quoted 3 lines

Peter, that {un,}register_tace_##call is the tracepoint code. The DEFINE_TRACE creates those functions. This is the lowest level we can go in the ftrace.h file.

Did you miss the extra f?

I thought that was a typo since there is no register_ftrace_##call.

Quoted 4 lines

You can wrap the regular {un,}register_trace_##call() things in {un,}register_/F/trace_##call() functions which also take care of the module stuff.

Oh, you mean you want me to create a wrapper for it? Note, this does not affect the normal trace "enable" code, because there's a module call back in trace_events.c that disables all the trace points in a module before unloading. And this in turn will unregister all events.

Quoted 3 lines

Your patch is virtually identical to the one that started this thread, everybody using the TRACE_EVENT() things will still need to worry about modules.

That's not true. You are not using the TRACE_EVENT, you are modifying the TRACE_EVENT. That is different. I don't see how a user could hit this issue. You hit it because you modified what was in ftrace.h, and that does not count as a user. That's a developer ;-)

-- Steve

author
#48 lore raw
In reply to Mathieu Desnoyers
Quoted 6 lines · Mathieu Desnoyers
Quoted 5 lines · * Steven Rostedt ([email protected]) wrote:

This patch solves the problem that Li originally reported. If something registers a trace point belonging to a module, then it ups the ref count of the module. This prevents a process from registering a probe to a tracepoint belonging to a module and then having the module disappear.

Quoted 8 lines
Quoted 5 lines

Doing the example with perf in Li's original post, now errors on the rmmod, with "ERROR: Module trace_events_sample is in use".

Mathieu, can I have your acked-by on this?

Sorry, it looks buggy.

And the patch itself is buggy.

Quoted 10 lines

It does not deal with the fact that tracepoints with the same name and arguments can be present in more than one module, or in a combination of kernel core and modules.

The struct tracepoint_entry is specific to a a tracepoint name, used for registration, but is eventually tied to all tracepoint instrumentation instances for this tracepoint name.

Mathieu

...

Quoted 5 lines
Quoted 5 lines
kernel/tracepoint.c +2 -1
-tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
+tracepoint_update_probe_range(struct module *mod,
+			      struct tracepoint *begin, struct tracepoint *end)
 {
 	struct tracepoint *iter;
Quoted 8 lines

kernel/tracepoint.c tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end) · line 296

Quoted 8 lines
kernel/tracepoint.c +3 -0
 	struct tracepoint_entry *mark_entry;
@@ -286,9 +291,15 @@ tracepoint_update_probe_range(struct tracepoint *begin, struct tracepoint *end)
 	for (iter = begin; iter < end; iter++) {
 		mark_entry = get_tracepoint(iter->name);
 		if (mark_entry) {
+			if (mod && !mark_entry->mod) {
+				if (!try_module_get(mod))
+					goto disable;

You can hit this code-path even when you unregister a probe, so the right way is:

	module_get() when iter->state changed from 0 from 1
	module_put() when iter->state changed from 1 to 0

But still has some other problems. You don't fail the registration, profile_enable() will return success even when the module is being destructed.

Quoted 9 lines
Quoted 9 lines
kernel/tracepoint.c +3 -0
+				mark_entry->mod = mod;
+			}
 			set_tracepoint(&mark_entry, iter,
 					!!mark_entry->refcount);
 		} else {
+ disable:
 			disable_tracepoint(iter);
 		}
 	}
#49 lore raw
In reply to Mathieu Desnoyers
Quoted 15 lines · Mathieu Desnoyers

Registering tracepoints even when no tracepoint definition is currently visible is the intended allowed behavior. Let's say we need to trace something happening in module init: if we disallow registering the tp callback before the module is initialized, we run in a chicken and egg problem.

So I am trying to figure out the problem source there. Is it that modules containing the tp callbacks need to know if those are actually connected to an instrumented module ? Or is it that the instrumented module needs to know if a probe module is connected to is ? Or is it the teardown of the probe module ? No refcount is needed there, because we surround the probe call by preempt disable/enable, and we use synchronize_sched() before removing the module which contains probe callbacks.

Quoted 1 line

Mathieu-trying-to-figure-out-what-this-whole-thread-is-about :)

OK, so the whole point seems to be that tracepoints have the funny thing you describe above, whereas the things ftrace makes out of TRACE_EVENT() get instantiated along with modules.

The reason why I rejected the initial patch (and I still think that that fix is at the wrong layer) is that I, as a consumer of whatever TRACE_EVENT() offers, should never need to consider modules.

#50 lore raw
Quoted 18 lines · * Peter Zijlstra ([email protected]) wrote:
Quoted 15 lines · Mathieu Desnoyers

Registering tracepoints even when no tracepoint definition is currently visible is the intended allowed behavior. Let's say we need to trace something happening in module init: if we disallow registering the tp callback before the module is initialized, we run in a chicken and egg problem.

So I am trying to figure out the problem source there. Is it that modules containing the tp callbacks need to know if those are actually connected to an instrumented module ? Or is it that the instrumented module needs to know if a probe module is connected to is ? Or is it the teardown of the probe module ? No refcount is needed there, because we surround the probe call by preempt disable/enable, and we use synchronize_sched() before removing the module which contains probe callbacks.

Quoted 1 line

Mathieu-trying-to-figure-out-what-this-whole-thread-is-about :)

Quoted 8 lines

OK, so the whole point seems to be that tracepoints have the funny thing you describe above, whereas the things ftrace makes out of TRACE_EVENT() get instantiated along with modules.

The reason why I rejected the initial patch (and I still think that that fix is at the wrong layer) is that I, as a consumer of whatever TRACE_EVENT() offers, should never need to consider modules.

Hrm, is it just me, or include/trace/ftrace.h fails to call the following function after tracepoint unregistration ?

/*
* tracepoint_synchronize_unregister must be called between the last tracepoint
* probe unregistration and the end of module exit to make sure there is no
* caller executing a probe when it is freed.
*/
static inline void tracepoint_synchronize_unregister(void)

{
        synchronize_sched();
}

This might solve all our problems.

Basically, it does not need to be called after each individual tracepoint unregistration, but does need to be called before removal of the module containing the probles (e.g. in module exit()).

Mathieu

Signature · 2 lines
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
#51 lore raw

[tip:tracing/core] tracing/profile: fix profile_disable vs module_unload

In reply to Li Zefan
Commit-ID:  558e6547e4b8a2b13608a24a9d3679802f91c4c7
Gitweb:     http://git.kernel.org/tip/558e6547e4b8a2b13608a24a9d3679802f91c4c7
Author:     Li Zefan <[email protected]>
AuthorDate: Mon, 24 Aug 2009 12:19:47 +0800
Committer:  Steven Rostedt <[email protected]>
CommitDate: Sat, 12 Sep 2009 22:28:38 -0400

tracing/profile: fix profile_disable vs module_unload

If the correspoding module is unloaded before ftrace_profile_disable() is called, event->profile_disable() won't be called, which can cause oops:

  # insmod trace-events-sample.ko
  # perf record -f -a -e sample:foo_bar sleep 3 &
  # sleep 1
  # rmmod trace_events_sample
  # insmod trace-events-sample.ko
  OOPS!

LKML-Reference: <[email protected]>

---
 kernel/trace/trace_event_profile.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)
kernel/trace/trace_event_profile.c +4 -1
index 11ba5bb..55a25c9 100644
@@ -5,6 +5,7 @@
  *
  */
 
+#include <linux/module.h>
 #include "trace.h"
 
 int ftrace_profile_enable(int event_id)
@@ -14,7 +15,8 @@ int ftrace_profile_enable(int event_id)
 
 	mutex_lock(&event_mutex);
 	list_for_each_entry(event, &ftrace_events, list) {
-		if (event->id == event_id && event->profile_enable) {
+		if (event->id == event_id && event->profile_enable &&
+		    try_module_get(event->mod)) {
 			ret = event->profile_enable(event);
 			break;
 		}
@@ -32,6 +34,7 @@ void ftrace_profile_disable(int event_id)
 	list_for_each_entry(event, &ftrace_events, list) {
 		if (event->id == event_id) {
 			event->profile_disable(event);
+			module_put(event->mod);
 			break;
 		}
 	}