diff options
-rw-r--r-- | kernel/trace/trace_events.c | 95 |
1 files changed, 88 insertions, 7 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index b92081588088..be4d3a437c17 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -770,7 +770,11 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
770 | } | 770 | } |
771 | 771 | ||
772 | static int | 772 | static int |
773 | event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | 773 | event_create_dir(struct ftrace_event_call *call, struct dentry *d_events, |
774 | const struct file_operations *id, | ||
775 | const struct file_operations *enable, | ||
776 | const struct file_operations *filter, | ||
777 | const struct file_operations *format) | ||
774 | { | 778 | { |
775 | struct dentry *entry; | 779 | struct dentry *entry; |
776 | int ret; | 780 | int ret; |
@@ -800,11 +804,11 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | |||
800 | 804 | ||
801 | if (call->regfunc) | 805 | if (call->regfunc) |
802 | entry = trace_create_file("enable", 0644, call->dir, call, | 806 | entry = trace_create_file("enable", 0644, call->dir, call, |
803 | &ftrace_enable_fops); | 807 | enable); |
804 | 808 | ||
805 | if (call->id) | 809 | if (call->id) |
806 | entry = trace_create_file("id", 0444, call->dir, call, | 810 | entry = trace_create_file("id", 0444, call->dir, call, |
807 | &ftrace_event_id_fops); | 811 | id); |
808 | 812 | ||
809 | if (call->define_fields) { | 813 | if (call->define_fields) { |
810 | ret = call->define_fields(); | 814 | ret = call->define_fields(); |
@@ -814,7 +818,7 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | |||
814 | return ret; | 818 | return ret; |
815 | } | 819 | } |
816 | entry = trace_create_file("filter", 0644, call->dir, call, | 820 | entry = trace_create_file("filter", 0644, call->dir, call, |
817 | &ftrace_event_filter_fops); | 821 | filter); |
818 | } | 822 | } |
819 | 823 | ||
820 | /* A trace may not want to export its format */ | 824 | /* A trace may not want to export its format */ |
@@ -822,7 +826,7 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | |||
822 | return 0; | 826 | return 0; |
823 | 827 | ||
824 | entry = trace_create_file("format", 0444, call->dir, call, | 828 | entry = trace_create_file("format", 0444, call->dir, call, |
825 | &ftrace_event_format_fops); | 829 | format); |
826 | 830 | ||
827 | return 0; | 831 | return 0; |
828 | } | 832 | } |
@@ -833,8 +837,60 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | |||
833 | event++) | 837 | event++) |
834 | 838 | ||
835 | #ifdef CONFIG_MODULES | 839 | #ifdef CONFIG_MODULES |
840 | |||
841 | static LIST_HEAD(ftrace_module_file_list); | ||
842 | |||
843 | /* | ||
844 | * Modules must own their file_operations to keep up with | ||
845 | * reference counting. | ||
846 | */ | ||
847 | struct ftrace_module_file_ops { | ||
848 | struct list_head list; | ||
849 | struct module *mod; | ||
850 | struct file_operations id; | ||
851 | struct file_operations enable; | ||
852 | struct file_operations format; | ||
853 | struct file_operations filter; | ||
854 | }; | ||
855 | |||
856 | static struct ftrace_module_file_ops * | ||
857 | trace_create_file_ops(struct module *mod) | ||
858 | { | ||
859 | struct ftrace_module_file_ops *file_ops; | ||
860 | |||
861 | /* | ||
862 | * This is a bit of a PITA. To allow for correct reference | ||
863 | * counting, modules must "own" their file_operations. | ||
864 | * To do this, we allocate the file operations that will be | ||
865 | * used in the event directory. | ||
866 | */ | ||
867 | |||
868 | file_ops = kmalloc(sizeof(*file_ops), GFP_KERNEL); | ||
869 | if (!file_ops) | ||
870 | return NULL; | ||
871 | |||
872 | file_ops->mod = mod; | ||
873 | |||
874 | file_ops->id = ftrace_event_id_fops; | ||
875 | file_ops->id.owner = mod; | ||
876 | |||
877 | file_ops->enable = ftrace_enable_fops; | ||
878 | file_ops->enable.owner = mod; | ||
879 | |||
880 | file_ops->filter = ftrace_event_filter_fops; | ||
881 | file_ops->filter.owner = mod; | ||
882 | |||
883 | file_ops->format = ftrace_event_format_fops; | ||
884 | file_ops->format.owner = mod; | ||
885 | |||
886 | list_add(&file_ops->list, &ftrace_module_file_list); | ||
887 | |||
888 | return file_ops; | ||
889 | } | ||
890 | |||
836 | static void trace_module_add_events(struct module *mod) | 891 | static void trace_module_add_events(struct module *mod) |
837 | { | 892 | { |
893 | struct ftrace_module_file_ops *file_ops = NULL; | ||
838 | struct ftrace_event_call *call, *start, *end; | 894 | struct ftrace_event_call *call, *start, *end; |
839 | struct dentry *d_events; | 895 | struct dentry *d_events; |
840 | 896 | ||
@@ -852,14 +908,27 @@ static void trace_module_add_events(struct module *mod) | |||
852 | /* The linker may leave blanks */ | 908 | /* The linker may leave blanks */ |
853 | if (!call->name) | 909 | if (!call->name) |
854 | continue; | 910 | continue; |
911 | |||
912 | /* | ||
913 | * This module has events, create file ops for this module | ||
914 | * if not already done. | ||
915 | */ | ||
916 | if (!file_ops) { | ||
917 | file_ops = trace_create_file_ops(mod); | ||
918 | if (!file_ops) | ||
919 | return; | ||
920 | } | ||
855 | call->mod = mod; | 921 | call->mod = mod; |
856 | list_add(&call->list, &ftrace_events); | 922 | list_add(&call->list, &ftrace_events); |
857 | event_create_dir(call, d_events); | 923 | event_create_dir(call, d_events, |
924 | &file_ops->id, &file_ops->enable, | ||
925 | &file_ops->filter, &file_ops->format); | ||
858 | } | 926 | } |
859 | } | 927 | } |
860 | 928 | ||
861 | static void trace_module_remove_events(struct module *mod) | 929 | static void trace_module_remove_events(struct module *mod) |
862 | { | 930 | { |
931 | struct ftrace_module_file_ops *file_ops; | ||
863 | struct ftrace_event_call *call, *p; | 932 | struct ftrace_event_call *call, *p; |
864 | 933 | ||
865 | list_for_each_entry_safe(call, p, &ftrace_events, list) { | 934 | list_for_each_entry_safe(call, p, &ftrace_events, list) { |
@@ -874,6 +943,16 @@ static void trace_module_remove_events(struct module *mod) | |||
874 | list_del(&call->list); | 943 | list_del(&call->list); |
875 | } | 944 | } |
876 | } | 945 | } |
946 | |||
947 | /* Now free the file_operations */ | ||
948 | list_for_each_entry(file_ops, &ftrace_module_file_list, list) { | ||
949 | if (file_ops->mod == mod) | ||
950 | break; | ||
951 | } | ||
952 | if (&file_ops->list != &ftrace_module_file_list) { | ||
953 | list_del(&file_ops->list); | ||
954 | kfree(file_ops); | ||
955 | } | ||
877 | } | 956 | } |
878 | 957 | ||
879 | static int trace_module_notify(struct notifier_block *self, | 958 | static int trace_module_notify(struct notifier_block *self, |
@@ -954,7 +1033,9 @@ static __init int event_trace_init(void) | |||
954 | if (!call->name) | 1033 | if (!call->name) |
955 | continue; | 1034 | continue; |
956 | list_add(&call->list, &ftrace_events); | 1035 | list_add(&call->list, &ftrace_events); |
957 | event_create_dir(call, d_events); | 1036 | event_create_dir(call, d_events, &ftrace_event_id_fops, |
1037 | &ftrace_enable_fops, &ftrace_event_filter_fops, | ||
1038 | &ftrace_event_format_fops); | ||
958 | } | 1039 | } |
959 | 1040 | ||
960 | ret = register_module_notifier(&trace_module_nb); | 1041 | ret = register_module_notifier(&trace_module_nb); |