blk-mq: really fix plug list flushing for nomerge queues
[linux-2.6-block.git] / kernel / trace / trace_events_trigger.c
index d67992f3bb0ee02551487dfa9a87fc39e924a96c..a975571cde2407fa50910335c6b31fc2df128906 100644 (file)
@@ -347,7 +347,7 @@ __init int register_event_command(struct event_command *cmd)
  * Currently we only unregister event commands from __init, so mark
  * this __init too.
  */
-static __init int unregister_event_command(struct event_command *cmd)
+__init int unregister_event_command(struct event_command *cmd)
 {
        struct event_command *p, *n;
        int ret = -ENODEV;
@@ -641,6 +641,7 @@ event_trigger_callback(struct event_command *cmd_ops,
        trigger_data->ops = trigger_ops;
        trigger_data->cmd_ops = cmd_ops;
        INIT_LIST_HEAD(&trigger_data->list);
+       INIT_LIST_HEAD(&trigger_data->named_list);
 
        if (glob[0] == '!') {
                cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
@@ -764,6 +765,148 @@ int set_trigger_filter(char *filter_str,
        return ret;
 }
 
+static LIST_HEAD(named_triggers);
+
+/**
+ * find_named_trigger - Find the common named trigger associated with @name
+ * @name: The name of the set of named triggers to find the common data for
+ *
+ * Named triggers are sets of triggers that share a common set of
+ * trigger data.  The first named trigger registered with a given name
+ * owns the common trigger data that the others subsequently
+ * registered with the same name will reference.  This function
+ * returns the common trigger data associated with that first
+ * registered instance.
+ *
+ * Return: the common trigger data for the given named trigger on
+ * success, NULL otherwise.
+ */
+struct event_trigger_data *find_named_trigger(const char *name)
+{
+       struct event_trigger_data *data;
+
+       if (!name)
+               return NULL;
+
+       list_for_each_entry(data, &named_triggers, named_list) {
+               if (data->named_data)
+                       continue;
+               if (strcmp(data->name, name) == 0)
+                       return data;
+       }
+
+       return NULL;
+}
+
+/**
+ * is_named_trigger - determine if a given trigger is a named trigger
+ * @test: The trigger data to test
+ *
+ * Return: true if 'test' is a named trigger, false otherwise.
+ */
+bool is_named_trigger(struct event_trigger_data *test)
+{
+       struct event_trigger_data *data;
+
+       list_for_each_entry(data, &named_triggers, named_list) {
+               if (test == data)
+                       return true;
+       }
+
+       return false;
+}
+
+/**
+ * save_named_trigger - save the trigger in the named trigger list
+ * @name: The name of the named trigger set
+ * @data: The trigger data to save
+ *
+ * Return: 0 if successful, negative error otherwise.
+ */
+int save_named_trigger(const char *name, struct event_trigger_data *data)
+{
+       data->name = kstrdup(name, GFP_KERNEL);
+       if (!data->name)
+               return -ENOMEM;
+
+       list_add(&data->named_list, &named_triggers);
+
+       return 0;
+}
+
+/**
+ * del_named_trigger - delete a trigger from the named trigger list
+ * @data: The trigger data to delete
+ */
+void del_named_trigger(struct event_trigger_data *data)
+{
+       kfree(data->name);
+       data->name = NULL;
+
+       list_del(&data->named_list);
+}
+
+static void __pause_named_trigger(struct event_trigger_data *data, bool pause)
+{
+       struct event_trigger_data *test;
+
+       list_for_each_entry(test, &named_triggers, named_list) {
+               if (strcmp(test->name, data->name) == 0) {
+                       if (pause) {
+                               test->paused_tmp = test->paused;
+                               test->paused = true;
+                       } else {
+                               test->paused = test->paused_tmp;
+                       }
+               }
+       }
+}
+
+/**
+ * pause_named_trigger - Pause all named triggers with the same name
+ * @data: The trigger data of a named trigger to pause
+ *
+ * Pauses a named trigger along with all other triggers having the
+ * same name.  Because named triggers share a common set of data,
+ * pausing only one is meaningless, so pausing one named trigger needs
+ * to pause all triggers with the same name.
+ */
+void pause_named_trigger(struct event_trigger_data *data)
+{
+       __pause_named_trigger(data, true);
+}
+
+/**
+ * unpause_named_trigger - Un-pause all named triggers with the same name
+ * @data: The trigger data of a named trigger to unpause
+ *
+ * Un-pauses a named trigger along with all other triggers having the
+ * same name.  Because named triggers share a common set of data,
+ * unpausing only one is meaningless, so unpausing one named trigger
+ * needs to unpause all triggers with the same name.
+ */
+void unpause_named_trigger(struct event_trigger_data *data)
+{
+       __pause_named_trigger(data, false);
+}
+
+/**
+ * set_named_trigger_data - Associate common named trigger data
+ * @data: The trigger data of a named trigger to unpause
+ *
+ * Named triggers are sets of triggers that share a common set of
+ * trigger data.  The first named trigger registered with a given name
+ * owns the common trigger data that the others subsequently
+ * registered with the same name will reference.  This function
+ * associates the common trigger data from the first trigger with the
+ * given trigger.
+ */
+void set_named_trigger_data(struct event_trigger_data *data,
+                           struct event_trigger_data *named_data)
+{
+       data->named_data = named_data;
+}
+
 static void
 traceon_trigger(struct event_trigger_data *data, void *rec)
 {
@@ -1062,15 +1205,6 @@ static __init void unregister_trigger_traceon_traceoff_cmds(void)
        unregister_event_command(&trigger_traceoff_cmd);
 }
 
-/* Avoid typos */
-#define ENABLE_EVENT_STR       "enable_event"
-#define DISABLE_EVENT_STR      "disable_event"
-
-struct enable_trigger_data {
-       struct trace_event_file         *file;
-       bool                            enable;
-};
-
 static void
 event_enable_trigger(struct event_trigger_data *data, void *rec)
 {
@@ -1100,14 +1234,16 @@ event_enable_count_trigger(struct event_trigger_data *data, void *rec)
        event_enable_trigger(data, rec);
 }
 
-static int
-event_enable_trigger_print(struct seq_file *m, struct event_trigger_ops *ops,
-                          struct event_trigger_data *data)
+int event_enable_trigger_print(struct seq_file *m,
+                              struct event_trigger_ops *ops,
+                              struct event_trigger_data *data)
 {
        struct enable_trigger_data *enable_data = data->private_data;
 
        seq_printf(m, "%s:%s:%s",
-                  enable_data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR,
+                  enable_data->hist ?
+                  (enable_data->enable ? ENABLE_HIST_STR : DISABLE_HIST_STR) :
+                  (enable_data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR),
                   enable_data->file->event_call->class->system,
                   trace_event_name(enable_data->file->event_call));
 
@@ -1124,9 +1260,8 @@ event_enable_trigger_print(struct seq_file *m, struct event_trigger_ops *ops,
        return 0;
 }
 
-static void
-event_enable_trigger_free(struct event_trigger_ops *ops,
-                         struct event_trigger_data *data)
+void event_enable_trigger_free(struct event_trigger_ops *ops,
+                              struct event_trigger_data *data)
 {
        struct enable_trigger_data *enable_data = data->private_data;
 
@@ -1171,10 +1306,9 @@ static struct event_trigger_ops event_disable_count_trigger_ops = {
        .free                   = event_enable_trigger_free,
 };
 
-static int
-event_enable_trigger_func(struct event_command *cmd_ops,
-                         struct trace_event_file *file,
-                         char *glob, char *cmd, char *param)
+int event_enable_trigger_func(struct event_command *cmd_ops,
+                             struct trace_event_file *file,
+                             char *glob, char *cmd, char *param)
 {
        struct trace_event_file *event_enable_file;
        struct enable_trigger_data *enable_data;
@@ -1183,6 +1317,7 @@ event_enable_trigger_func(struct event_command *cmd_ops,
        struct trace_array *tr = file->tr;
        const char *system;
        const char *event;
+       bool hist = false;
        char *trigger;
        char *number;
        bool enable;
@@ -1207,8 +1342,15 @@ event_enable_trigger_func(struct event_command *cmd_ops,
        if (!event_enable_file)
                goto out;
 
-       enable = strcmp(cmd, ENABLE_EVENT_STR) == 0;
+#ifdef CONFIG_HIST_TRIGGERS
+       hist = ((strcmp(cmd, ENABLE_HIST_STR) == 0) ||
+               (strcmp(cmd, DISABLE_HIST_STR) == 0));
 
+       enable = ((strcmp(cmd, ENABLE_EVENT_STR) == 0) ||
+                 (strcmp(cmd, ENABLE_HIST_STR) == 0));
+#else
+       enable = strcmp(cmd, ENABLE_EVENT_STR) == 0;
+#endif
        trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger);
 
        ret = -ENOMEM;
@@ -1228,6 +1370,7 @@ event_enable_trigger_func(struct event_command *cmd_ops,
        INIT_LIST_HEAD(&trigger_data->list);
        RCU_INIT_POINTER(trigger_data->filter, NULL);
 
+       enable_data->hist = hist;
        enable_data->enable = enable;
        enable_data->file = event_enable_file;
        trigger_data->private_data = enable_data;
@@ -1305,10 +1448,10 @@ event_enable_trigger_func(struct event_command *cmd_ops,
        goto out;
 }
 
-static int event_enable_register_trigger(char *glob,
-                                        struct event_trigger_ops *ops,
-                                        struct event_trigger_data *data,
-                                        struct trace_event_file *file)
+int event_enable_register_trigger(char *glob,
+                                 struct event_trigger_ops *ops,
+                                 struct event_trigger_data *data,
+                                 struct trace_event_file *file)
 {
        struct enable_trigger_data *enable_data = data->private_data;
        struct enable_trigger_data *test_enable_data;
@@ -1318,6 +1461,8 @@ static int event_enable_register_trigger(char *glob,
        list_for_each_entry_rcu(test, &file->triggers, list) {
                test_enable_data = test->private_data;
                if (test_enable_data &&
+                   (test->cmd_ops->trigger_type ==
+                    data->cmd_ops->trigger_type) &&
                    (test_enable_data->file == enable_data->file)) {
                        ret = -EEXIST;
                        goto out;
@@ -1343,10 +1488,10 @@ out:
        return ret;
 }
 
-static void event_enable_unregister_trigger(char *glob,
-                                           struct event_trigger_ops *ops,
-                                           struct event_trigger_data *test,
-                                           struct trace_event_file *file)
+void event_enable_unregister_trigger(char *glob,
+                                    struct event_trigger_ops *ops,
+                                    struct event_trigger_data *test,
+                                    struct trace_event_file *file)
 {
        struct enable_trigger_data *test_enable_data = test->private_data;
        struct enable_trigger_data *enable_data;
@@ -1356,6 +1501,8 @@ static void event_enable_unregister_trigger(char *glob,
        list_for_each_entry_rcu(data, &file->triggers, list) {
                enable_data = data->private_data;
                if (enable_data &&
+                   (data->cmd_ops->trigger_type ==
+                    test->cmd_ops->trigger_type) &&
                    (enable_data->file == test_enable_data->file)) {
                        unregistered = true;
                        list_del_rcu(&data->list);
@@ -1375,8 +1522,12 @@ event_enable_get_trigger_ops(char *cmd, char *param)
        struct event_trigger_ops *ops;
        bool enable;
 
+#ifdef CONFIG_HIST_TRIGGERS
+       enable = ((strcmp(cmd, ENABLE_EVENT_STR) == 0) ||
+                 (strcmp(cmd, ENABLE_HIST_STR) == 0));
+#else
        enable = strcmp(cmd, ENABLE_EVENT_STR) == 0;
-
+#endif
        if (enable)
                ops = param ? &event_enable_count_trigger_ops :
                        &event_enable_trigger_ops;
@@ -1447,6 +1598,8 @@ __init int register_trigger_cmds(void)
        register_trigger_snapshot_cmd();
        register_trigger_stacktrace_cmd();
        register_trigger_enable_disable_cmds();
+       register_trigger_hist_enable_disable_cmds();
+       register_trigger_hist_cmd();
 
        return 0;
 }