Thomas Renninger
71b8c4302b
Kernel segfault in kernel/timer.c - comm: stapio (process) - related to preloadtrace.ko OBS-URL: https://build.opensuse.org/package/show/devel:tools/systemtap?expand=0&rev=20
184 lines
6.6 KiB
Diff
184 lines
6.6 KiB
Diff
From: Frank Ch. Eigler <fche@elastic.org>
|
|
Subject: PR10651 / RHBZ653286: mod_timer vs. del_timer_sync races
|
|
References: bnc#664105
|
|
Patch-Mainline: yes
|
|
Git-commit: f2b610b6295628ae1fef744421c293883f1e4298
|
|
Git-repo: git://sources.redhat.com/git/systemtap.git
|
|
|
|
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
|
|
|
It appears possible for del_timer_sync (from outside) and mod_timer
|
|
(from within a timer callback) to race. Defeat this race by ensuring
|
|
that the timer callback checks an atomic_t flag before rescheduling
|
|
itself with mod_timer.
|
|
|
|
* runtime/transport/relay_v2.c (transport_state): Turn into an atomic_t.
|
|
Update users.
|
|
(__stp_relay_wakeup_timer): Observe flag.
|
|
(_stp_transport_data_fs_{start,stop}): Update flag before timer manipulations.
|
|
* runtime/transport/ring_buffer.c: Ditto for corresponding functions.
|
|
|
|
diff --git a/runtime/transport/relay_v2.c b/runtime/transport/relay_v2.c
|
|
index 974941d..33e198e 100644
|
|
--- a/runtime/transport/relay_v2.c
|
|
+++ b/runtime/transport/relay_v2.c
|
|
@@ -4,7 +4,7 @@
|
|
* code started as a proposed relayfs interface called 'utt'. It has
|
|
* been modified and simplified for systemtap.
|
|
*
|
|
- * Changes Copyright (C) 2009 Red Hat Inc.
|
|
+ * Changes Copyright (C) 2009-2010 Red Hat Inc.
|
|
*
|
|
* Original utt code by:
|
|
* Copyright (C) 2006 Jens Axboe <axboe@suse.de>
|
|
@@ -42,7 +42,7 @@
|
|
* to be changed. */
|
|
struct _stp_relay_data_type {
|
|
struct rchan *rchan;
|
|
- enum _stp_transport_state transport_state;
|
|
+ atomic_t /* enum _stp_transport_state */ transport_state;
|
|
struct dentry *dropped_file;
|
|
atomic_t dropped;
|
|
atomic_t wakeup;
|
|
@@ -133,7 +133,10 @@ static void __stp_relay_wakeup_timer(unsigned long val)
|
|
#endif
|
|
}
|
|
|
|
- mod_timer(&_stp_relay_data.timer, jiffies + STP_RELAY_TIMER_INTERVAL);
|
|
+ if (atomic_read(&_stp_relay_data.transport_state) == STP_TRANSPORT_RUNNING)
|
|
+ mod_timer(&_stp_relay_data.timer, jiffies + STP_RELAY_TIMER_INTERVAL);
|
|
+ else
|
|
+ dbug_trans(0, "relay_v2 wakeup timer expiry\n");
|
|
}
|
|
|
|
static void __stp_relay_timer_init(void)
|
|
@@ -149,7 +152,7 @@ static void __stp_relay_timer_init(void)
|
|
|
|
static enum _stp_transport_state _stp_transport_get_state(void)
|
|
{
|
|
- return _stp_relay_data.transport_state;
|
|
+ return atomic_read (&_stp_relay_data.transport_state);
|
|
}
|
|
|
|
static void _stp_transport_data_fs_overwrite(int overwrite)
|
|
@@ -242,19 +245,19 @@ static struct rchan_callbacks __stp_relay_callbacks = {
|
|
|
|
static void _stp_transport_data_fs_start(void)
|
|
{
|
|
- if (_stp_relay_data.transport_state == STP_TRANSPORT_INITIALIZED) {
|
|
+ if (atomic_read (&_stp_relay_data.transport_state) == STP_TRANSPORT_INITIALIZED) {
|
|
+ atomic_set (&_stp_relay_data.transport_state, STP_TRANSPORT_RUNNING);
|
|
/* We're initialized. Now start the timer. */
|
|
__stp_relay_timer_init();
|
|
- _stp_relay_data.transport_state = STP_TRANSPORT_RUNNING;
|
|
}
|
|
}
|
|
|
|
static void _stp_transport_data_fs_stop(void)
|
|
{
|
|
- if (_stp_relay_data.transport_state == STP_TRANSPORT_RUNNING) {
|
|
+ if (atomic_read (&_stp_relay_data.transport_state) == STP_TRANSPORT_RUNNING) {
|
|
+ atomic_set (&_stp_relay_data.transport_state, STP_TRANSPORT_STOPPED);
|
|
del_timer_sync(&_stp_relay_data.timer);
|
|
dbug_trans(0, "flushing...\n");
|
|
- _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED;
|
|
if (_stp_relay_data.rchan)
|
|
relay_flush(_stp_relay_data.rchan);
|
|
}
|
|
@@ -277,7 +280,7 @@ static int _stp_transport_data_fs_init(void)
|
|
u64 npages;
|
|
struct sysinfo si;
|
|
|
|
- _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED;
|
|
+ atomic_set(&_stp_relay_data.transport_state, STP_TRANSPORT_STOPPED);
|
|
_stp_relay_data.overwrite_flag = 0;
|
|
atomic_set(&_stp_relay_data.dropped, 0);
|
|
_stp_relay_data.dropped_file = NULL;
|
|
@@ -337,7 +340,7 @@ static int _stp_transport_data_fs_init(void)
|
|
goto err;
|
|
}
|
|
dbug_trans(1, "returning 0...\n");
|
|
- _stp_relay_data.transport_state = STP_TRANSPORT_INITIALIZED;
|
|
+ atomic_set (&_stp_relay_data.transport_state, STP_TRANSPORT_INITIALIZED);
|
|
|
|
return 0;
|
|
|
|
diff --git a/runtime/transport/ring_buffer.c b/runtime/transport/ring_buffer.c
|
|
index 6c327c0..232f099 100644
|
|
--- a/runtime/transport/ring_buffer.c
|
|
+++ b/runtime/transport/ring_buffer.c
|
|
@@ -55,7 +55,7 @@ struct _stp_iterator {
|
|
#endif
|
|
|
|
struct _stp_relay_data_type {
|
|
- enum _stp_transport_state transport_state;
|
|
+ atomic_t /* enum _stp_transport_state */ transport_state;
|
|
struct ring_buffer *rb;
|
|
struct _stp_iterator iter[NR_ITERS];
|
|
cpumask_var_t trace_reader_cpumask;
|
|
@@ -649,10 +649,12 @@ static int _stp_data_write_commit(void *entry)
|
|
|
|
static void __stp_relay_wakeup_timer(unsigned long val)
|
|
{
|
|
- if (waitqueue_active(&_stp_poll_wait)
|
|
- && ! _stp_ring_buffer_empty())
|
|
+ if (waitqueue_active(&_stp_poll_wait) && ! _stp_ring_buffer_empty())
|
|
wake_up_interruptible(&_stp_poll_wait);
|
|
- mod_timer(&_stp_relay_data.timer, jiffies + STP_RELAY_TIMER_INTERVAL);
|
|
+ if (atomic_read(&_stp_relay_data.transport_state) == STP_TRANSPORT_RUNNING)
|
|
+ mod_timer(&_stp_relay_data.timer, jiffies + STP_RELAY_TIMER_INTERVAL);
|
|
+ else
|
|
+ dbug_trans(0, "ring_buffer wakeup timer expiry\n");
|
|
}
|
|
|
|
static void __stp_relay_timer_start(void)
|
|
@@ -677,7 +679,7 @@ static int _stp_transport_data_fs_init(void)
|
|
int rc;
|
|
int cpu, cpu2;
|
|
|
|
- _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED;
|
|
+ atomic_set (&_stp_relay_data.transport_state, STP_TRANSPORT_STOPPED);
|
|
_stp_relay_data.rb = NULL;
|
|
|
|
// allocate buffer
|
|
@@ -738,23 +740,23 @@ static int _stp_transport_data_fs_init(void)
|
|
}
|
|
|
|
dbug_trans(1, "returning 0...\n");
|
|
- _stp_relay_data.transport_state = STP_TRANSPORT_INITIALIZED;
|
|
+ atomic_set (&_stp_relay_data.transport_state, STP_TRANSPORT_INITIALIZED);
|
|
return 0;
|
|
}
|
|
|
|
static void _stp_transport_data_fs_start(void)
|
|
{
|
|
- if (_stp_relay_data.transport_state == STP_TRANSPORT_INITIALIZED) {
|
|
- __stp_relay_timer_start();
|
|
- _stp_relay_data.transport_state = STP_TRANSPORT_RUNNING;
|
|
+ if (atomic_read(&_stp_relay_data.transport_state) == STP_TRANSPORT_INITIALIZED) {
|
|
+ atomic_set(&_stp_relay_data.transport_state, STP_TRANSPORT_RUNNING);
|
|
+ __stp_relay_timer_start();
|
|
}
|
|
}
|
|
|
|
static void _stp_transport_data_fs_stop(void)
|
|
{
|
|
- if (_stp_relay_data.transport_state == STP_TRANSPORT_RUNNING) {
|
|
- __stp_relay_timer_stop();
|
|
- _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED;
|
|
+ if (atomic_read(&_stp_relay_data.transport_state) == STP_TRANSPORT_RUNNING) {
|
|
+ atomic_set(&_stp_relay_data.transport_state, STP_TRANSPORT_STOPPED);
|
|
+ __stp_relay_timer_stop();
|
|
}
|
|
}
|
|
|
|
@@ -773,7 +775,7 @@ static void _stp_transport_data_fs_close(void)
|
|
|
|
static enum _stp_transport_state _stp_transport_get_state(void)
|
|
{
|
|
- return _stp_relay_data.transport_state;
|
|
+ return atomic_read (&_stp_relay_data.transport_state);
|
|
}
|
|
|
|
static void _stp_transport_data_fs_overwrite(int overwrite)
|