enum lttng_kernel_instrumentation instrumentation;
union {
+ struct lttng_kprobe kprobe;
} u;
/* Backward references: list of lttng_enabler_ref (ref to enablers) */
int lttng_event_enable(struct lttng_event *event);
int lttng_event_disable(struct lttng_event *event);
+int lttng_event_notifier_enable(struct lttng_event_notifier *event_notifier);
+int lttng_event_notifier_disable(struct lttng_event_notifier *event_notifier);
+
void lttng_transport_register(struct lttng_transport *transport);
void lttng_transport_unregister(struct lttng_transport *transport);
struct lttng_event *event);
void lttng_kprobes_unregister_event(struct lttng_event *event);
void lttng_kprobes_destroy_event_private(struct lttng_event *event);
+int lttng_kprobes_register_event_notifier(const char *symbol_name,
+ uint64_t offset,
+ uint64_t addr,
+ struct lttng_event_notifier *event_notifier);
+void lttng_kprobes_unregister_event_notifier(struct lttng_event_notifier *event_notifier);
+void lttng_kprobes_destroy_event_notifier_private(struct lttng_event_notifier *event_notifier);
#else
static inline
int lttng_kprobes_register_event(const char *name,
}
static inline
-void lttng_kprobes_destroy_private(struct lttng_event *event)
+int lttng_kprobes_register_event_notifier(const char *symbol_name,
+ uint64_t offset,
+ uint64_t addr,
+ struct lttng_event_notifier *event_notifier)
+{
+ return -ENOSYS;
+}
+
+static inline
+void lttng_kprobes_unregister_event_notifier(struct lttng_event_notifier *event_notifier)
+{
+}
+
+static inline
+void lttng_kprobes_destroy_event_notifier_private(struct lttng_event_notifier *event_notifier)
{
}
#endif
static
long lttng_event_notifier_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
+ struct lttng_event_notifier *event_notifier;
struct lttng_event_notifier_enabler *event_notifier_enabler;
enum lttng_event_type *evtype = file->private_data;
case LTTNG_KERNEL_ENABLE:
switch (*evtype) {
case LTTNG_TYPE_EVENT:
- return -EINVAL;
+ event_notifier = file->private_data;
+ return lttng_event_notifier_enable(event_notifier);
case LTTNG_TYPE_ENABLER:
event_notifier_enabler = file->private_data;
return lttng_event_notifier_enabler_enable(event_notifier_enabler);
case LTTNG_KERNEL_DISABLE:
switch (*evtype) {
case LTTNG_TYPE_EVENT:
- return -EINVAL;
+ event_notifier = file->private_data;
+ return lttng_event_notifier_disable(event_notifier);
case LTTNG_TYPE_ENABLER:
event_notifier_enabler = file->private_data;
return lttng_event_notifier_enabler_disable(event_notifier_enabler);
case LTTNG_KERNEL_TRACEPOINT:
break;
case LTTNG_KERNEL_KPROBE:
+ event_notifier_param->event.u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+ break;
case LTTNG_KERNEL_UPROBE:
case LTTNG_KERNEL_KRETPROBE:
case LTTNG_KERNEL_FUNCTION:
case LTTNG_KERNEL_TRACEPOINT:
ret = -EINVAL;
break;
- case LTTNG_KERNEL_SYSCALL:
case LTTNG_KERNEL_KPROBE:
+ WRITE_ONCE(event_notifier->enabled, 1);
+ break;
+ case LTTNG_KERNEL_SYSCALL:
case LTTNG_KERNEL_FUNCTION:
case LTTNG_KERNEL_UPROBE:
case LTTNG_KERNEL_NOOP:
case LTTNG_KERNEL_TRACEPOINT:
ret = -EINVAL;
break;
- case LTTNG_KERNEL_SYSCALL:
case LTTNG_KERNEL_KPROBE:
+ WRITE_ONCE(event_notifier->enabled, 0);
+ break;
+ case LTTNG_KERNEL_SYSCALL:
case LTTNG_KERNEL_FUNCTION:
case LTTNG_KERNEL_UPROBE:
case LTTNG_KERNEL_NOOP:
event_name = event_desc->name;
break;
case LTTNG_KERNEL_KPROBE:
+ event_name = event_notifier_param->event.name;
+ break;
case LTTNG_KERNEL_UPROBE:
case LTTNG_KERNEL_KRETPROBE:
case LTTNG_KERNEL_FUNCTION:
smp_wmb();
break;
case LTTNG_KERNEL_KPROBE:
+ /*
+ * Needs to be explicitly enabled after creation, since
+ * we may want to apply filters.
+ */
+ event_notifier->enabled = 0;
+ event_notifier->registered = 1;
+ /*
+ * Populate lttng_event_notifier structure before event
+ * registration.
+ */
+ smp_wmb();
+ ret = lttng_kprobes_register_event_notifier(
+ event_notifier_param->event.u.kprobe.symbol_name,
+ event_notifier_param->event.u.kprobe.offset,
+ event_notifier_param->event.u.kprobe.addr,
+ event_notifier);
+ if (ret) {
+ ret = -EINVAL;
+ goto register_error;
+ }
+ ret = try_module_get(event_notifier->desc->owner);
+ WARN_ON_ONCE(!ret);
+ break;
case LTTNG_KERNEL_UPROBE:
case LTTNG_KERNEL_KRETPROBE:
case LTTNG_KERNEL_FUNCTION:
desc->event_notifier_callback,
event_notifier);
break;
- case LTTNG_KERNEL_SYSCALL:
case LTTNG_KERNEL_KPROBE:
+ ret = 0;
+ break;
+ case LTTNG_KERNEL_SYSCALL:
case LTTNG_KERNEL_UPROBE:
case LTTNG_KERNEL_KRETPROBE:
case LTTNG_KERNEL_FUNCTION:
event_notifier);
break;
case LTTNG_KERNEL_KPROBE:
+ lttng_kprobes_unregister_event_notifier(event_notifier);
+ ret = 0;
+ break;
case LTTNG_KERNEL_KRETPROBE:
case LTTNG_KERNEL_FUNCTION:
case LTTNG_KERNEL_SYSCALL:
lttng_event_desc_put(event_notifier->desc);
break;
case LTTNG_KERNEL_KPROBE:
+ module_put(event_notifier->desc->owner);
+ lttng_kprobes_destroy_event_notifier_private(event_notifier);
+ break;
case LTTNG_KERNEL_KRETPROBE:
case LTTNG_KERNEL_FUNCTION:
case LTTNG_KERNEL_NOOP:
return 0;
}
+static
+int lttng_kprobes_event_notifier_handler_pre(struct kprobe *p, struct pt_regs *regs)
+{
+ struct lttng_event_notifier *event_notifier =
+ container_of(p, struct lttng_event_notifier, u.kprobe.kp);
+
+ if (unlikely(!READ_ONCE(event_notifier->enabled)))
+ return 0;
+
+ event_notifier->send_notification(event_notifier);
+
+ return 0;
+}
+
/*
* Create event description
*/
return ret;
}
+/*
+ * Create event_notifier description
+ */
+static
+int lttng_create_kprobe_event_notifier(const char *name, struct lttng_event_notifier *event_notifier)
+{
+ struct lttng_event_desc *desc;
+ int ret;
+
+ desc = kzalloc(sizeof(*event_notifier->desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+ desc->name = kstrdup(name, GFP_KERNEL);
+ if (!desc->name) {
+ ret = -ENOMEM;
+ goto error_str;
+ }
+ desc->nr_fields = 0;
+
+ desc->owner = THIS_MODULE;
+ event_notifier->desc = desc;
+
+ return 0;
+
+error_str:
+ kfree(desc);
+ return ret;
+}
+
static
int _lttng_kprobes_register(const char *symbol_name,
uint64_t offset,
lttng_kp->kp.addr = (void *) (unsigned long) addr;
/*
- * Ensure the memory we just allocated don't trigger page faults.
+ * Ensure the memory we just allocated don't event_notifier page faults.
* Well.. kprobes itself puts the page fault handler on the blacklist,
* but we can never be too careful.
*/
}
EXPORT_SYMBOL_GPL(lttng_kprobes_register_event);
+int lttng_kprobes_register_event_notifier(const char *symbol_name,
+ uint64_t offset,
+ uint64_t addr,
+ struct lttng_event_notifier *event_notifier)
+{
+ int ret;
+ ret = lttng_create_kprobe_event_notifier(symbol_name, event_notifier);
+ if (ret)
+ goto error;
+
+ ret = _lttng_kprobes_register(symbol_name, offset, addr,
+ &event_notifier->u.kprobe, lttng_kprobes_event_notifier_handler_pre);
+ if (ret)
+ goto register_error;
+
+ return 0;
+
+register_error:
+ kfree(event_notifier->desc->name);
+ kfree(event_notifier->desc);
+error:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lttng_kprobes_register_event_notifier);
+
void lttng_kprobes_unregister_event(struct lttng_event *event)
{
unregister_kprobe(&event->u.kprobe.kp);
}
EXPORT_SYMBOL_GPL(lttng_kprobes_unregister_event);
+void lttng_kprobes_unregister_event_notifier(struct lttng_event_notifier *event_notifier)
+{
+ unregister_kprobe(&event_notifier->u.kprobe.kp);
+}
+EXPORT_SYMBOL_GPL(lttng_kprobes_unregister_event_notifier);
+
void lttng_kprobes_destroy_event_private(struct lttng_event *event)
{
kfree(event->u.kprobe.symbol_name);
}
EXPORT_SYMBOL_GPL(lttng_kprobes_destroy_event_private);
+void lttng_kprobes_destroy_event_notifier_private(struct lttng_event_notifier *event_notifier)
+{
+ kfree(event_notifier->u.kprobe.symbol_name);
+ kfree(event_notifier->desc->name);
+ kfree(event_notifier->desc);
+}
+EXPORT_SYMBOL_GPL(lttng_kprobes_destroy_event_notifier_private);
+
MODULE_LICENSE("GPL and additional rights");
MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@efficios.com>");
MODULE_DESCRIPTION("LTTng kprobes probes");