+2006-02-25 Martin Hunt <hunt@redhat.com>
+
+ * procfs.c (_stp_proc_write_cmd): Check return code for
+ _stp_transport_open().
+
+ * transport.c (_stp_transport_write): This function
+ can now sleep because it is
+ (_stp_handle_start): Don't use a mutex when a simple atomic
+ will do.
+ (_stp_work_queue): Check atomic to see if probe_start()
+ has finished before attempting exit.
+ (_stp_transport_close): PR2391. Cancel work queue.
+ (_stp_transport_init): If _stp_register_procfs() fails,
+ return an error code.
+
+ * transport.txt: New file. Documents transport initialization and
+ shutdown sequence.
+
+2006-02-24 Martin Hunt <hunt@redhat.com>
+
+ * transport.c (_stp_transport_init): Fail if
+ _stp_register_procfs() fails.
+
2006-02-17 Martin Hunt <hunt@redhat.com>
* procfs.c (_stp_proc_read_cmd): Change spin_lock()
return 0;
if (copy_from_user (&ti, &buf[4], sizeof(struct transport_info)))
return -EFAULT;
- _stp_transport_open (&ti);
+ if (_stp_transport_open (&ti) < 0)
+ return -1;
break;
}
default:
-#ifndef _TRANSPORT_TRANSPORT_C_ /* -*- linux-c -*- */
-#define _TRANSPORT_TRANSPORT_C_
-
-/*
+/* -*- linux-c -*-
* transport.c - stp transport functions
*
* Copyright (C) IBM Corporation, 2005
- * Copyright (C) Red Hat Inc, 2005
+ * Copyright (C) Red Hat Inc, 2005, 2006
*
- * This file is released under the GPL.
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
*/
+#ifndef _TRANSPORT_TRANSPORT_C_
+#define _TRANSPORT_TRANSPORT_C_
+
#include <linux/delay.h>
#include "transport.h"
static struct dentry *_stp_dir;
#endif
-static DECLARE_MUTEX(_stp_start_mutex);
-
+static atomic_t _stp_start_finished = ATOMIC_INIT (0);
static int _stp_dpid;
static int _stp_pid;
/*
* _stp_streaming - boolean, are we using 'streaming' output?
*/
-static inline int _stp_streaming(void)
+static int _stp_streaming(void)
{
if (_stp_transport_mode == STP_TRANSPORT_PROC)
return 1;
{
kbug ("stp_handle_start pid=%d\n", st->pid);
- down (&_stp_start_mutex);
+ /* note: st->pid is actually the return code for the reply packet */
st->pid = probe_start();
- up (&_stp_start_mutex);
+ atomic_set(&_stp_start_finished,1);
- if (st->pid < 0)
+ /* if probe_start() failed, suppress calling probe_exit() */
+ if (st->pid < 0)
_stp_exit_called = 1;
+
_stp_transport_send(STP_START, st, sizeof(*st));
}
}
#endif
-static void _stp_cleanup_and_exit (int closing)
+static void _stp_cleanup_and_exit (int dont_rmmod)
{
int failures;
- kbug("cleanup_and_exit (%d)\n", closing);
+ kbug("cleanup_and_exit (%d)\n", dont_rmmod);
if (!_stp_exit_called) {
_stp_exit_called = 1;
relay_flush(_stp_chan);
}
#endif
- kbug("SENDING STP_EXIT\n");
- _stp_transport_send(STP_EXIT, &closing, sizeof(int));
+ _stp_transport_send(STP_EXIT, &dont_rmmod, sizeof(int));
}
}
if (do_io)
wake_up_interruptible(&_stp_proc_wq);
- if (_stp_exit_flag) {
+ /* if exit flag is set AND we have finished with probe_start() */
+ if (unlikely(_stp_exit_flag && atomic_read(&_stp_start_finished))) {
cancel_delayed_work(&stp_exit);
- down (&_stp_start_mutex);
_stp_cleanup_and_exit(0);
- up (&_stp_start_mutex);
wake_up_interruptible(&_stp_proc_wq);
} else
schedule_delayed_work(&stp_exit, STP_WORK_TIMER);
-
}
/**
void _stp_transport_close()
{
kbug("************** transport_close *************\n");
+ cancel_delayed_work(&stp_exit);
_stp_cleanup_and_exit(1);
-
+ wake_up_interruptible(&_stp_proc_wq);
#ifdef STP_RELAYFS
if (_stp_transport_mode == STP_TRANSPORT_RELAYFS)
_stp_relayfs_close(_stp_chan, _stp_dir);
#endif
-
- ssleep(1);
_stp_unregister_procfs();
kbug("---- CLOSED ----\n");
}
/**
* _stp_transport_open - open proc and relayfs channels
* with proper parameters
- * Returns negative on failure, 0 otherwise.
+ * Returns negative on failure, >0 otherwise.
*
* This function registers the probe with the control channel,
* and if the probe output will not be 'streaming', creates a
{
kbug("transport_init from %ld %ld\n", (long)_stp_pid, (long)current->pid);
- _stp_register_procfs();
+ if (_stp_register_procfs() < 0)
+ return -1;
+
schedule_delayed_work(&stp_exit, STP_WORK_TIMER);
return 0;
}
static int _stp_transport_mode = STP_TRANSPORT_PROC;
#endif
-extern void _stp_transport_cleanup(void);
extern void _stp_transport_close(void);
#endif /* _TRANSPORT_TRANSPORT_H_ */
--- /dev/null
+INITIALIZATION
+
+init_module() is defined in runtime.h. It returns _stp_transport_init().
+
+int _stp_transport_init(void)
+{
+ /* allocates buffers, creates /proc/systemtap/stuff */
+ if (_stp_register_procfs() < 0)
+ return -1;
+
+ /* starts up the work queue */
+ schedule_delayed_work(&stp_exit, STP_WORK_TIMER);
+
+ /* always succeeds */
+ return 0;
+}
+
+*** Module is now loaded ***
+
+In stpd, librelay.c, init_stp() forks any commands specified with
+the "-c" parameter. Then it sends STP_TRANSPORT_INFO message with
+the buffer size (if it was specified on the stpd command line) and
+the pid from the fork (or 0 otherwise).
+
+In procfs.c, _stp_proc_write_cmd() receives the message. It calls
+_stp_transport_open().
+
+_stp_transport_open() initializes relayfs if necessary. It returns a
+STP_TRANSPORT_INFO message with the transport_mode
+(relayfs or procfs-only), and buffer information.
+
+stpd receives the STP_TRANSPORT_INFO message in stp_main_loop(),
+It initializes relayfs (if necessary) and replies with an
+STP_START message.
+
+***Communications are now fully established.***
+
+_stp_proc_write_cmd() receives the STP_START. It calls _stp_handle_start().
+
+_stp_handle_start() calls probe_start(), which the translator generates.
+It allocates memory and registers probes. When that returns,
+an atomic variable is set indicating probe_start finished. An STP_START
+message is returned to stpd with the return value from probe_start()
+
+stpd receives the STP_START message in stp_main_loop(). If the return value
+is < 0 (indicating probe_start failed to register probes, etc) stpd
+cleans up and exits.
+
+*** Probes running. Everything Up. ***
+
+
+SHUTDOWN AND UNLOADING
+
+There are 3 ways to initiate shutdown.
+
+1. stpd can initiate it. This happens when stpd receives a ^C or
+a child specified with "-c" exits.
+
+2. Something can call _stp_exit().
+
+3. rmmod. This can happen when stpd dies for an unexpected reason
+and the module is still loaded. So the user does an rmmod. Also, when
+STP_START fails, stpd simply rmmods the module.
+
+
+For #1, stpd sends an STP_EXIT message.
+_stp_proc_write_cmd() receives the STP_EXIT. It sets _stp_exit_flag
+and returns.
+
+The next time _stp_work_queue() runs, it will notice that
+_stp_exit_flag is set. It checks to see if probe_start() finished
+because we don't want to start exiting until then. It will cancel itself,
+and call _stp_cleanup_and_exit(0). That will call probe_exit() which
+unregisters the probes. Then it sends an STP_EXIT(0) message to
+stpd. stpd gets the message and calls cleanup_and_exit(0)
+
+For #2, _stp_exit() sets _stp_exit_flag.
+
+For #3, cleanup_module() calls _stp_transport_close().
+_stp_transport_close() cancels the work_queue, then calls
+_stp_cleanup_and_exit(1). The "1" is passed as an argument to
+the STP_EXIT message. Stpd calls cleanup_and_exit(1), which cleans
+up everything on its side, but does not do an "rmmod". Finally,
+_stp_unregister_procfs() to remove the procfs stuff.
+