lttng-statedump-objs := lttng-statedump-impl.o wrapper/irqdesc.o
ifneq ($(CONFIG_HAVE_SYSCALL_TRACEPOINTS),)
-lttng-tracer-objs += lttng-syscalls.o
+lttng-tracer-objs += lttng-syscalls.o probes/lttng-probe-user.o
endif
ifneq ($(CONFIG_PERF_EVENTS),)
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/mm.h>
+#include <linux/uaccess.h>
/* Internal helpers */
#include "../../wrapper/ringbuffer/backend_internal.h"
}
/**
- * lib_ring_buffer_copy_from_user - write userspace data to a buffer backend
+ * lib_ring_buffer_copy_from_user_inatomic - write userspace data to a buffer backend
* @config : ring buffer instance configuration
* @ctx: ring buffer context. (input arguments only)
* @src : userspace source pointer to copy from
* This function copies "len" bytes of data from a userspace pointer to a
* buffer backend, at the current context offset. This is more or less a buffer
* backend-specific memcpy() operation. Calls the slow path
- * (_ring_buffer_write_from_user) if copy is crossing a page boundary.
+ * (_ring_buffer_write_from_user_inatomic) if copy is crossing a page boundary.
+ * Disable the page fault handler to ensure we never try to take the mmap_sem.
*/
static inline
-void lib_ring_buffer_copy_from_user(const struct lib_ring_buffer_config *config,
+void lib_ring_buffer_copy_from_user_inatomic(const struct lib_ring_buffer_config *config,
struct lib_ring_buffer_ctx *ctx,
const void __user *src, size_t len)
{
struct lib_ring_buffer_backend_pages *rpages;
unsigned long sb_bindex, id;
unsigned long ret;
+ mm_segment_t old_fs = get_fs();
offset &= chanb->buf_size - 1;
sbidx = offset >> chanb->subbuf_size_order;
config->mode == RING_BUFFER_OVERWRITE
&& subbuffer_id_is_noref(config, id));
+ set_fs(KERNEL_DS);
+ pagefault_disable();
if (unlikely(!access_ok(VERIFY_READ, src, len)))
goto fill_buffer;
if (likely(pagecpy == len)) {
- ret = lib_ring_buffer_do_copy_from_user(
+ ret = lib_ring_buffer_do_copy_from_user_inatomic(
rpages->p[index].virt + (offset & ~PAGE_MASK),
src, len);
if (unlikely(ret > 0)) {
goto fill_buffer;
}
} else {
- _lib_ring_buffer_copy_from_user(bufb, offset, src, len, 0);
+ _lib_ring_buffer_copy_from_user_inatomic(bufb, offset, src, len, 0);
}
+ pagefault_enable();
+ set_fs(old_fs);
ctx->buf_offset += len;
return;
fill_buffer:
+ pagefault_enable();
+ set_fs(old_fs);
/*
* In the error path we call the slow path version to avoid
* the pollution of static inline code.
extern void _lib_ring_buffer_memset(struct lib_ring_buffer_backend *bufb,
size_t offset, int c, size_t len,
ssize_t pagecpy);
-extern void _lib_ring_buffer_copy_from_user(struct lib_ring_buffer_backend *bufb,
+extern void _lib_ring_buffer_copy_from_user_inatomic(struct lib_ring_buffer_backend *bufb,
size_t offset, const void *src,
size_t len, ssize_t pagecpy);
} while (0)
/*
- * We use __copy_from_user to copy userspace data since we already
+ * We use __copy_from_user_inatomic to copy userspace data since we already
* did the access_ok for the whole range.
*/
static inline
-unsigned long lib_ring_buffer_do_copy_from_user(void *dest,
+unsigned long lib_ring_buffer_do_copy_from_user_inatomic(void *dest,
const void __user *src,
unsigned long len)
{
- return __copy_from_user(dest, src, len);
+ return __copy_from_user_inatomic(dest, src, len);
}
/*
/**
- * lib_ring_buffer_copy_from_user - write user data to a ring_buffer buffer.
+ * lib_ring_buffer_copy_from_user_inatomic - write user data to a ring_buffer buffer.
* @bufb : buffer backend
* @offset : offset within the buffer
* @src : source address
* directly without having the src pointer checked with access_ok()
* previously.
*/
-void _lib_ring_buffer_copy_from_user(struct lib_ring_buffer_backend *bufb,
+void _lib_ring_buffer_copy_from_user_inatomic(struct lib_ring_buffer_backend *bufb,
size_t offset,
const void __user *src, size_t len,
ssize_t pagecpy)
rpages = bufb->array[sb_bindex];
CHAN_WARN_ON(chanb, config->mode == RING_BUFFER_OVERWRITE
&& subbuffer_id_is_noref(config, id));
- ret = lib_ring_buffer_do_copy_from_user(rpages->p[index].virt
+ ret = lib_ring_buffer_do_copy_from_user_inatomic(rpages->p[index].virt
+ (offset & ~PAGE_MASK),
src, pagecpy) != 0;
if (ret > 0) {
}
} while (unlikely(len != pagecpy));
}
-EXPORT_SYMBOL_GPL(_lib_ring_buffer_copy_from_user);
+EXPORT_SYMBOL_GPL(_lib_ring_buffer_copy_from_user_inatomic);
/**
* lib_ring_buffer_read - read data from ring_buffer_buffer.
void lttng_event_write_from_user(struct lib_ring_buffer_ctx *ctx,
const void __user *src, size_t len)
{
- lib_ring_buffer_copy_from_user(&client_config, ctx, src, len);
+ lib_ring_buffer_copy_from_user_inatomic(&client_config, ctx, src, len);
}
static
void lttng_event_write_from_user(struct lib_ring_buffer_ctx *ctx,
const void __user *src, size_t len)
{
- lib_ring_buffer_copy_from_user(&client_config, ctx, src, len);
+ lib_ring_buffer_copy_from_user_inatomic(&client_config, ctx, src, len);
}
static
#include <linux/debugfs.h>
#include "lttng.h"
#include "lttng-types.h"
+#include "lttng-probe-user.h"
#include "../wrapper/vmalloc.h" /* for wrapper_vmalloc_sync_all() */
#include "../wrapper/ringbuffer/frontend_types.h"
#include "../lttng-events.h"
#undef __string_from_user
#define __string_from_user(_item, _src) \
__event_len += __dynamic_len[__dynamic_len_idx++] = \
- max_t(size_t, strlen_user(_src), 1);
+ max_t(size_t, lttng_strlen_user_inatomic(_src), 1);
#undef TP_PROTO
#define TP_PROTO(args...) args
--- /dev/null
+/*
+ * lttng-probe-user.c
+ *
+ * Copyright (C) 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; only
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/uaccess.h>
+#include "lttng-probe-user.h"
+
+/*
+ * Calculate string length. Include final null terminating character if there is
+ * one, or ends at first fault. Disabling page faults ensures that we can safely
+ * call this from pretty much any context, including those where the caller
+ * holds mmap_sem, or any lock which nests in mmap_sem.
+ */
+long lttng_strlen_user_inatomic(const char *addr)
+{
+ long count = 0;
+ mm_segment_t old_fs = get_fs();
+
+ set_fs(KERNEL_DS);
+ pagefault_disable();
+ for (;;) {
+ char v;
+ long ret;
+
+ ret = __copy_from_user_inatomic(&v,
+ (__force const char __user *)(addr),
+ sizeof(v));
+ if (unlikely(ret == -EFAULT))
+ break;
+ count++;
+ if (unlikely(!v))
+ break;
+ addr++;
+ }
+ pagefault_enable();
+ set_fs(old_fs);
+ return count;
+}
--- /dev/null
+#ifndef _LTTNG_PROBE_USER_H
+#define _LTTNG_PROBE_USER_H
+
+/*
+ * lttng-probe-user.h
+ *
+ * Copyright (C) 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; only
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Calculate string length. Include final null terminating character if there is
+ * one, or ends at first fault.
+ */
+long lttng_strlen_user_inatomic(const char *addr);
+
+#endif /* _LTTNG_PROBE_USER_H */