tracing: Add synthetic event command generation functions
Add functions used to generate synthetic event commands, built on top of the dynevent_cmd interface. synth_event_gen_cmd_start() is used to create a synthetic event command using a variable arg list and synth_event_gen_cmd_array_start() does the same thing but using an array of field descriptors. synth_event_add_field(), synth_event_add_field_str() and synth_event_add_fields() can be used to add single fields one by one or as a group. Once all desired fields are added, synth_event_gen_cmd_end() is used to actually execute the command and create the event. synth_event_create() does everything, including creating the event, in a single call. Link: http://lkml.kernel.org/r/38fef702fad5ef208009f459552f34a94befd860.1580323897.git.zanussi@kernel.org Acked-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Tom Zanussi <zanussi@kernel.org> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
This commit is contained in:
parent
86c5426bad
commit
35ca5207c2
2 changed files with 412 additions and 4 deletions
|
@ -357,6 +357,7 @@ extern void trace_put_event_file(struct trace_event_file *file);
|
||||||
#define MAX_DYNEVENT_CMD_LEN (2048)
|
#define MAX_DYNEVENT_CMD_LEN (2048)
|
||||||
|
|
||||||
enum dynevent_type {
|
enum dynevent_type {
|
||||||
|
DYNEVENT_TYPE_SYNTH = 1,
|
||||||
DYNEVENT_TYPE_NONE,
|
DYNEVENT_TYPE_NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -379,6 +380,42 @@ extern int dynevent_create(struct dynevent_cmd *cmd);
|
||||||
|
|
||||||
extern int synth_event_delete(const char *name);
|
extern int synth_event_delete(const char *name);
|
||||||
|
|
||||||
|
extern void synth_event_cmd_init(struct dynevent_cmd *cmd,
|
||||||
|
char *buf, int maxlen);
|
||||||
|
|
||||||
|
extern int __synth_event_gen_cmd_start(struct dynevent_cmd *cmd,
|
||||||
|
const char *name,
|
||||||
|
struct module *mod, ...);
|
||||||
|
|
||||||
|
#define synth_event_gen_cmd_start(cmd, name, mod, ...) \
|
||||||
|
__synth_event_gen_cmd_start(cmd, name, mod, ## __VA_ARGS__, NULL)
|
||||||
|
|
||||||
|
struct synth_field_desc {
|
||||||
|
const char *type;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int synth_event_gen_cmd_array_start(struct dynevent_cmd *cmd,
|
||||||
|
const char *name,
|
||||||
|
struct module *mod,
|
||||||
|
struct synth_field_desc *fields,
|
||||||
|
unsigned int n_fields);
|
||||||
|
extern int synth_event_create(const char *name,
|
||||||
|
struct synth_field_desc *fields,
|
||||||
|
unsigned int n_fields, struct module *mod);
|
||||||
|
|
||||||
|
extern int synth_event_add_field(struct dynevent_cmd *cmd,
|
||||||
|
const char *type,
|
||||||
|
const char *name);
|
||||||
|
extern int synth_event_add_field_str(struct dynevent_cmd *cmd,
|
||||||
|
const char *type_name);
|
||||||
|
extern int synth_event_add_fields(struct dynevent_cmd *cmd,
|
||||||
|
struct synth_field_desc *fields,
|
||||||
|
unsigned int n_fields);
|
||||||
|
|
||||||
|
#define synth_event_gen_cmd_end(cmd) \
|
||||||
|
dynevent_create(cmd)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Event file flags:
|
* Event file flags:
|
||||||
* ENABLED - The event is enabled
|
* ENABLED - The event is enabled
|
||||||
|
|
|
@ -379,7 +379,7 @@ struct hist_trigger_data {
|
||||||
unsigned int n_save_var_str;
|
unsigned int n_save_var_str;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int synth_event_create(int argc, const char **argv);
|
static int create_synth_event(int argc, const char **argv);
|
||||||
static int synth_event_show(struct seq_file *m, struct dyn_event *ev);
|
static int synth_event_show(struct seq_file *m, struct dyn_event *ev);
|
||||||
static int synth_event_release(struct dyn_event *ev);
|
static int synth_event_release(struct dyn_event *ev);
|
||||||
static bool synth_event_is_busy(struct dyn_event *ev);
|
static bool synth_event_is_busy(struct dyn_event *ev);
|
||||||
|
@ -387,7 +387,7 @@ static bool synth_event_match(const char *system, const char *event,
|
||||||
int argc, const char **argv, struct dyn_event *ev);
|
int argc, const char **argv, struct dyn_event *ev);
|
||||||
|
|
||||||
static struct dyn_event_operations synth_event_ops = {
|
static struct dyn_event_operations synth_event_ops = {
|
||||||
.create = synth_event_create,
|
.create = create_synth_event,
|
||||||
.show = synth_event_show,
|
.show = synth_event_show,
|
||||||
.is_busy = synth_event_is_busy,
|
.is_busy = synth_event_is_busy,
|
||||||
.free = synth_event_release,
|
.free = synth_event_release,
|
||||||
|
@ -412,6 +412,7 @@ struct synth_event {
|
||||||
struct trace_event_class class;
|
struct trace_event_class class;
|
||||||
struct trace_event_call call;
|
struct trace_event_call call;
|
||||||
struct tracepoint *tp;
|
struct tracepoint *tp;
|
||||||
|
struct module *mod;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool is_synth_event(struct dyn_event *ev)
|
static bool is_synth_event(struct dyn_event *ev)
|
||||||
|
@ -1292,6 +1293,273 @@ struct hist_var_data {
|
||||||
struct hist_trigger_data *hist_data;
|
struct hist_trigger_data *hist_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int synth_event_check_arg_fn(void *data)
|
||||||
|
{
|
||||||
|
struct dynevent_arg_pair *arg_pair = data;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
size = synth_field_size((char *)arg_pair->lhs);
|
||||||
|
|
||||||
|
return size ? 0 : -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synth_event_add_field - Add a new field to a synthetic event cmd
|
||||||
|
* @cmd: A pointer to the dynevent_cmd struct representing the new event
|
||||||
|
* @type: The type of the new field to add
|
||||||
|
* @name: The name of the new field to add
|
||||||
|
*
|
||||||
|
* Add a new field to a synthetic event cmd object. Field ordering is in
|
||||||
|
* the same order the fields are added.
|
||||||
|
*
|
||||||
|
* See synth_field_size() for available types. If field_name contains
|
||||||
|
* [n] the field is considered to be an array.
|
||||||
|
*
|
||||||
|
* Return: 0 if successful, error otherwise.
|
||||||
|
*/
|
||||||
|
int synth_event_add_field(struct dynevent_cmd *cmd, const char *type,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
struct dynevent_arg_pair arg_pair;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (cmd->type != DYNEVENT_TYPE_SYNTH)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!type || !name)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
dynevent_arg_pair_init(&arg_pair, synth_event_check_arg_fn, 0, ';');
|
||||||
|
|
||||||
|
arg_pair.lhs = type;
|
||||||
|
arg_pair.rhs = name;
|
||||||
|
|
||||||
|
ret = dynevent_arg_pair_add(cmd, &arg_pair);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (++cmd->n_fields > SYNTH_FIELDS_MAX)
|
||||||
|
ret = -EINVAL;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(synth_event_add_field);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synth_event_add_field_str - Add a new field to a synthetic event cmd
|
||||||
|
* @cmd: A pointer to the dynevent_cmd struct representing the new event
|
||||||
|
* @type_name: The type and name of the new field to add, as a single string
|
||||||
|
*
|
||||||
|
* Add a new field to a synthetic event cmd object, as a single
|
||||||
|
* string. The @type_name string is expected to be of the form 'type
|
||||||
|
* name', which will be appended by ';'. No sanity checking is done -
|
||||||
|
* what's passed in is assumed to already be well-formed. Field
|
||||||
|
* ordering is in the same order the fields are added.
|
||||||
|
*
|
||||||
|
* See synth_field_size() for available types. If field_name contains
|
||||||
|
* [n] the field is considered to be an array.
|
||||||
|
*
|
||||||
|
* Return: 0 if successful, error otherwise.
|
||||||
|
*/
|
||||||
|
int synth_event_add_field_str(struct dynevent_cmd *cmd, const char *type_name)
|
||||||
|
{
|
||||||
|
struct dynevent_arg arg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (cmd->type != DYNEVENT_TYPE_SYNTH)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!type_name)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
dynevent_arg_init(&arg, NULL, ';');
|
||||||
|
|
||||||
|
arg.str = type_name;
|
||||||
|
|
||||||
|
ret = dynevent_arg_add(cmd, &arg);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (++cmd->n_fields > SYNTH_FIELDS_MAX)
|
||||||
|
ret = -EINVAL;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(synth_event_add_field_str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synth_event_add_fields - Add multiple fields to a synthetic event cmd
|
||||||
|
* @cmd: A pointer to the dynevent_cmd struct representing the new event
|
||||||
|
* @fields: An array of type/name field descriptions
|
||||||
|
* @n_fields: The number of field descriptions contained in the fields array
|
||||||
|
*
|
||||||
|
* Add a new set of fields to a synthetic event cmd object. The event
|
||||||
|
* fields that will be defined for the event should be passed in as an
|
||||||
|
* array of struct synth_field_desc, and the number of elements in the
|
||||||
|
* array passed in as n_fields. Field ordering will retain the
|
||||||
|
* ordering given in the fields array.
|
||||||
|
*
|
||||||
|
* See synth_field_size() for available types. If field_name contains
|
||||||
|
* [n] the field is considered to be an array.
|
||||||
|
*
|
||||||
|
* Return: 0 if successful, error otherwise.
|
||||||
|
*/
|
||||||
|
int synth_event_add_fields(struct dynevent_cmd *cmd,
|
||||||
|
struct synth_field_desc *fields,
|
||||||
|
unsigned int n_fields)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < n_fields; i++) {
|
||||||
|
if (fields[i].type == NULL || fields[i].name == NULL) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = synth_event_add_field(cmd, fields[i].type, fields[i].name);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(synth_event_add_fields);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __synth_event_gen_cmd_start - Start a synthetic event command from arg list
|
||||||
|
* @cmd: A pointer to the dynevent_cmd struct representing the new event
|
||||||
|
* @name: The name of the synthetic event
|
||||||
|
* @mod: The module creating the event, NULL if not created from a module
|
||||||
|
* @args: Variable number of arg (pairs), one pair for each field
|
||||||
|
*
|
||||||
|
* NOTE: Users normally won't want to call this function directly, but
|
||||||
|
* rather use the synth_event_gen_cmd_start() wrapper, which
|
||||||
|
* automatically adds a NULL to the end of the arg list. If this
|
||||||
|
* function is used directly, make sure the last arg in the variable
|
||||||
|
* arg list is NULL.
|
||||||
|
*
|
||||||
|
* Generate a synthetic event command to be executed by
|
||||||
|
* synth_event_gen_cmd_end(). This function can be used to generate
|
||||||
|
* the complete command or only the first part of it; in the latter
|
||||||
|
* case, synth_event_add_field(), synth_event_add_field_str(), or
|
||||||
|
* synth_event_add_fields() can be used to add more fields following
|
||||||
|
* this.
|
||||||
|
*
|
||||||
|
* There should be an even number variable args, each pair consisting
|
||||||
|
* of a type followed by a field name.
|
||||||
|
*
|
||||||
|
* See synth_field_size() for available types. If field_name contains
|
||||||
|
* [n] the field is considered to be an array.
|
||||||
|
*
|
||||||
|
* Return: 0 if successful, error otherwise.
|
||||||
|
*/
|
||||||
|
int __synth_event_gen_cmd_start(struct dynevent_cmd *cmd, const char *name,
|
||||||
|
struct module *mod, ...)
|
||||||
|
{
|
||||||
|
struct dynevent_arg arg;
|
||||||
|
va_list args;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
cmd->event_name = name;
|
||||||
|
cmd->private_data = mod;
|
||||||
|
|
||||||
|
if (cmd->type != DYNEVENT_TYPE_SYNTH)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
dynevent_arg_init(&arg, NULL, 0);
|
||||||
|
arg.str = name;
|
||||||
|
ret = dynevent_arg_add(cmd, &arg);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
va_start(args, mod);
|
||||||
|
for (;;) {
|
||||||
|
const char *type, *name;
|
||||||
|
|
||||||
|
type = va_arg(args, const char *);
|
||||||
|
if (!type)
|
||||||
|
break;
|
||||||
|
name = va_arg(args, const char *);
|
||||||
|
if (!name)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (++cmd->n_fields > SYNTH_FIELDS_MAX) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = synth_event_add_field(cmd, type, name);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(__synth_event_gen_cmd_start);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synth_event_gen_cmd_array_start - Start synthetic event command from an array
|
||||||
|
* @cmd: A pointer to the dynevent_cmd struct representing the new event
|
||||||
|
* @name: The name of the synthetic event
|
||||||
|
* @fields: An array of type/name field descriptions
|
||||||
|
* @n_fields: The number of field descriptions contained in the fields array
|
||||||
|
*
|
||||||
|
* Generate a synthetic event command to be executed by
|
||||||
|
* synth_event_gen_cmd_end(). This function can be used to generate
|
||||||
|
* the complete command or only the first part of it; in the latter
|
||||||
|
* case, synth_event_add_field(), synth_event_add_field_str(), or
|
||||||
|
* synth_event_add_fields() can be used to add more fields following
|
||||||
|
* this.
|
||||||
|
*
|
||||||
|
* The event fields that will be defined for the event should be
|
||||||
|
* passed in as an array of struct synth_field_desc, and the number of
|
||||||
|
* elements in the array passed in as n_fields. Field ordering will
|
||||||
|
* retain the ordering given in the fields array.
|
||||||
|
*
|
||||||
|
* See synth_field_size() for available types. If field_name contains
|
||||||
|
* [n] the field is considered to be an array.
|
||||||
|
*
|
||||||
|
* Return: 0 if successful, error otherwise.
|
||||||
|
*/
|
||||||
|
int synth_event_gen_cmd_array_start(struct dynevent_cmd *cmd, const char *name,
|
||||||
|
struct module *mod,
|
||||||
|
struct synth_field_desc *fields,
|
||||||
|
unsigned int n_fields)
|
||||||
|
{
|
||||||
|
struct dynevent_arg arg;
|
||||||
|
unsigned int i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
cmd->event_name = name;
|
||||||
|
cmd->private_data = mod;
|
||||||
|
|
||||||
|
if (cmd->type != DYNEVENT_TYPE_SYNTH)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (n_fields > SYNTH_FIELDS_MAX)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
dynevent_arg_init(&arg, NULL, 0);
|
||||||
|
arg.str = name;
|
||||||
|
ret = dynevent_arg_add(cmd, &arg);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 0; i < n_fields; i++) {
|
||||||
|
if (fields[i].type == NULL || fields[i].name == NULL)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = synth_event_add_field(cmd, fields[i].type, fields[i].name);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(synth_event_gen_cmd_array_start);
|
||||||
|
|
||||||
static int __create_synth_event(int argc, const char *name, const char **argv)
|
static int __create_synth_event(int argc, const char *name, const char **argv)
|
||||||
{
|
{
|
||||||
struct synth_field *field, *fields[SYNTH_FIELDS_MAX];
|
struct synth_field *field, *fields[SYNTH_FIELDS_MAX];
|
||||||
|
@ -1360,6 +1628,56 @@ static int __create_synth_event(int argc, const char *name, const char **argv)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synth_event_create - Create a new synthetic event
|
||||||
|
* @name: The name of the new sythetic event
|
||||||
|
* @fields: An array of type/name field descriptions
|
||||||
|
* @n_fields: The number of field descriptions contained in the fields array
|
||||||
|
* @mod: The module creating the event, NULL if not created from a module
|
||||||
|
*
|
||||||
|
* Create a new synthetic event with the given name under the
|
||||||
|
* trace/events/synthetic/ directory. The event fields that will be
|
||||||
|
* defined for the event should be passed in as an array of struct
|
||||||
|
* synth_field_desc, and the number elements in the array passed in as
|
||||||
|
* n_fields. Field ordering will retain the ordering given in the
|
||||||
|
* fields array.
|
||||||
|
*
|
||||||
|
* If the new synthetic event is being created from a module, the mod
|
||||||
|
* param must be non-NULL. This will ensure that the trace buffer
|
||||||
|
* won't contain unreadable events.
|
||||||
|
*
|
||||||
|
* The new synth event should be deleted using synth_event_delete()
|
||||||
|
* function. The new synthetic event can be generated from modules or
|
||||||
|
* other kernel code using trace_synth_event() and related functions.
|
||||||
|
*
|
||||||
|
* Return: 0 if successful, error otherwise.
|
||||||
|
*/
|
||||||
|
int synth_event_create(const char *name, struct synth_field_desc *fields,
|
||||||
|
unsigned int n_fields, struct module *mod)
|
||||||
|
{
|
||||||
|
struct dynevent_cmd cmd;
|
||||||
|
char *buf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
buf = kzalloc(MAX_DYNEVENT_CMD_LEN, GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
synth_event_cmd_init(&cmd, buf, MAX_DYNEVENT_CMD_LEN);
|
||||||
|
|
||||||
|
ret = synth_event_gen_cmd_array_start(&cmd, name, mod,
|
||||||
|
fields, n_fields);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = synth_event_gen_cmd_end(&cmd);
|
||||||
|
out:
|
||||||
|
kfree(buf);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(synth_event_create);
|
||||||
|
|
||||||
static int destroy_synth_event(struct synth_event *se)
|
static int destroy_synth_event(struct synth_event *se)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -1388,14 +1706,33 @@ static int destroy_synth_event(struct synth_event *se)
|
||||||
int synth_event_delete(const char *event_name)
|
int synth_event_delete(const char *event_name)
|
||||||
{
|
{
|
||||||
struct synth_event *se = NULL;
|
struct synth_event *se = NULL;
|
||||||
|
struct module *mod = NULL;
|
||||||
int ret = -ENOENT;
|
int ret = -ENOENT;
|
||||||
|
|
||||||
mutex_lock(&event_mutex);
|
mutex_lock(&event_mutex);
|
||||||
se = find_synth_event(event_name);
|
se = find_synth_event(event_name);
|
||||||
if (se)
|
if (se) {
|
||||||
|
mod = se->mod;
|
||||||
ret = destroy_synth_event(se);
|
ret = destroy_synth_event(se);
|
||||||
|
}
|
||||||
mutex_unlock(&event_mutex);
|
mutex_unlock(&event_mutex);
|
||||||
|
|
||||||
|
if (mod) {
|
||||||
|
mutex_lock(&trace_types_lock);
|
||||||
|
/*
|
||||||
|
* It is safest to reset the ring buffer if the module
|
||||||
|
* being unloaded registered any events that were
|
||||||
|
* used. The only worry is if a new module gets
|
||||||
|
* loaded, and takes on the same id as the events of
|
||||||
|
* this module. When printing out the buffer, traced
|
||||||
|
* events left over from this module may be passed to
|
||||||
|
* the new module events and unexpected results may
|
||||||
|
* occur.
|
||||||
|
*/
|
||||||
|
tracing_reset_all_online_cpus();
|
||||||
|
mutex_unlock(&trace_types_lock);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(synth_event_delete);
|
EXPORT_SYMBOL_GPL(synth_event_delete);
|
||||||
|
@ -1420,7 +1757,41 @@ int synth_event_run_command(const char *command)
|
||||||
return trace_run_command(command, create_or_delete_synth_event);
|
return trace_run_command(command, create_or_delete_synth_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int synth_event_create(int argc, const char **argv)
|
static int synth_event_run_cmd(struct dynevent_cmd *cmd)
|
||||||
|
{
|
||||||
|
struct synth_event *se;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = trace_run_command(cmd->buf, create_or_delete_synth_event);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
se = find_synth_event(cmd->event_name);
|
||||||
|
if (WARN_ON(!se))
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
se->mod = cmd->private_data;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* synth_event_cmd_init - Initialize a synthetic event command object
|
||||||
|
* @cmd: A pointer to the dynevent_cmd struct representing the new event
|
||||||
|
* @buf: A pointer to the buffer used to build the command
|
||||||
|
* @maxlen: The length of the buffer passed in @buf
|
||||||
|
*
|
||||||
|
* Initialize a synthetic event command object. Use this before
|
||||||
|
* calling any of the other dyenvent_cmd functions.
|
||||||
|
*/
|
||||||
|
void synth_event_cmd_init(struct dynevent_cmd *cmd, char *buf, int maxlen)
|
||||||
|
{
|
||||||
|
dynevent_cmd_init(cmd, buf, maxlen, DYNEVENT_TYPE_SYNTH,
|
||||||
|
synth_event_run_cmd);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(synth_event_cmd_init);
|
||||||
|
|
||||||
|
static int create_synth_event(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
const char *name = argv[0];
|
const char *name = argv[0];
|
||||||
int len;
|
int len;
|
||||||
|
|
Loading…
Add table
Reference in a new issue