summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--runtime/probe_lock.h68
-rw-r--r--translate.cxx133
2 files changed, 112 insertions, 89 deletions
diff --git a/runtime/probe_lock.h b/runtime/probe_lock.h
new file mode 100644
index 00000000..1915d4ff
--- /dev/null
+++ b/runtime/probe_lock.h
@@ -0,0 +1,68 @@
+/* probe locking header file
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * 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 _PROBE_LOCK_H
+#define _PROBE_LOCK_H
+
+#include <linux/spinlock.h>
+
+// XXX: old 2.6 kernel hack
+#ifndef read_trylock
+#define read_trylock(x) ({ read_lock(x); 1; })
+#endif
+
+struct stp_probe_lock {
+ #ifdef STP_TIMING
+ atomic_t *skipped;
+ #endif
+ rwlock_t *lock;
+ unsigned write_p;
+};
+
+
+static void
+stp_unlock_probe(const struct stp_probe_lock *locks, unsigned num_locks)
+{
+ unsigned i;
+ for (i = num_locks; i-- > 0;) {
+ if (locks[i].write_p)
+ write_unlock(locks[i].lock);
+ else
+ read_unlock(locks[i].lock);
+ }
+}
+
+
+static unsigned
+stp_lock_probe(const struct stp_probe_lock *locks, unsigned num_locks)
+{
+ unsigned i, numtrylock = 0;
+ for (i = 0; i < num_locks; ++i) {
+ if (locks[i].write_p)
+ while (!write_trylock(locks[i].lock) &&
+ (++numtrylock < MAXTRYLOCK))
+ ndelay (TRYLOCKDELAY);
+ else
+ while (!read_trylock(locks[i].lock) &&
+ (++numtrylock < MAXTRYLOCK))
+ ndelay (TRYLOCKDELAY);
+ if (unlikely(numtrylock >= MAXTRYLOCK)) {
+ atomic_inc(&skipped_count);
+ #ifdef STP_TIMING
+ atomic_inc(locks[i].skipped);
+ #endif
+ stp_unlock_probe(locks, i);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+#endif /* _PROBE_LOCK_H */
diff --git a/translate.cxx b/translate.cxx
index 1109449d..ed415abb 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -73,6 +73,7 @@ struct c_unparser: public unparser, public visitor
void emit_module_init ();
void emit_module_exit ();
void emit_function (functiondecl* v);
+ void emit_lock_decls (const varuse_collecting_visitor& v);
void emit_locks (const varuse_collecting_visitor& v);
void emit_probe (derived_probe* v);
void emit_unlocks (const varuse_collecting_visitor& v);
@@ -1622,6 +1623,14 @@ c_unparser::emit_probe (derived_probe* v)
probe_contents[oss.str()] = v->name;
+ // emit static read/write lock decls for global variables
+ varuse_collecting_visitor vut(*session);
+ if (v->needs_global_locks ())
+ {
+ v->body->visit (& vut);
+ emit_lock_decls (vut);
+ }
+
// initialize frame pointer
o->newline() << "struct " << v->name << "_locals * __restrict__ l =";
o->newline(1) << "& c->probe_locals." << v->name << ";";
@@ -1638,12 +1647,8 @@ c_unparser::emit_probe (derived_probe* v)
v->emit_probe_local_init(o);
// emit all read/write locks for global variables
- varuse_collecting_visitor vut(*session);
if (v->needs_global_locks ())
- {
- v->body->visit (& vut);
emit_locks (vut);
- }
// initialize locals
for (unsigned j=0; j<v->locals.size(); j++)
@@ -1694,13 +1699,16 @@ c_unparser::emit_probe (derived_probe* v)
void
-c_unparser::emit_locks(const varuse_collecting_visitor& vut)
+c_unparser::emit_lock_decls(const varuse_collecting_visitor& vut)
{
- o->newline() << "{";
- o->newline(1) << "unsigned numtrylock = 0;";
- o->newline() << "(void) numtrylock;";
+ unsigned numvars = 0;
+
+ if (session->verbose > 1)
+ clog << current_probe->name << " locks ";
+
+ o->newline() << "static const struct stp_probe_lock locks[] = {";
+ o->indent(1);
- string last_locked_var;
for (unsigned i = 0; i < session->globals.size(); i++)
{
vardecl* v = session->globals[i];
@@ -1732,94 +1740,44 @@ c_unparser::emit_locks(const varuse_collecting_visitor& vut)
continue;
}
- string lockcall =
- string (write_p ? "write" : "read") +
- "_trylock (& global.s_" + v->name + "_lock)";
-
- o->newline() << "while (! " << lockcall
- << "&& (++numtrylock < MAXTRYLOCK))";
- o->newline(1) << "ndelay (TRYLOCKDELAY);";
- o->newline(-1) << "if (unlikely (numtrylock >= MAXTRYLOCK)) {";
- o->newline(1) << "atomic_inc (& skipped_count);";
+ o->newline() << "{";
+ o->newline(1) << ".lock = &global.s_" + v->name + "_lock,";
+ o->newline() << ".write_p = " << (write_p ? 1 : 0) << ",";
o->newline() << "#ifdef STP_TIMING";
- o->newline() << "atomic_inc (& global.s_" << c_varname (v->name) << "_lock_skip_count);";
+ o->newline() << ".skipped = &global.s_" << c_varname (v->name) << "_lock_skip_count,";
o->newline() << "#endif";
- // The following works even if i==0. Note that using
- // globals[i-1]->name is wrong since that global may not have
- // been lockworthy by this probe.
- o->newline() << "goto unlock_" << last_locked_var << ";";
- o->newline(-1) << "}";
+ o->newline(-1) << "},";
- last_locked_var = v->name;
+ numvars ++;
+ if (session->verbose > 1)
+ clog << v->name << "[" << (read_p ? "r" : "")
+ << (write_p ? "w" : "") << "] ";
}
- o->newline() << "if (0) goto unlock_;";
+ o->newline(-1) << "};";
- o->newline(-1) << "}";
+ if (session->verbose > 1)
+ {
+ if (!numvars)
+ clog << "nothing";
+ clog << endl;
+ }
}
void
-c_unparser::emit_unlocks(const varuse_collecting_visitor& vut)
+c_unparser::emit_locks(const varuse_collecting_visitor&)
{
- unsigned numvars = 0;
-
- if (session->verbose>1)
- clog << current_probe->name << " locks ";
-
- for (int i = session->globals.size()-1; i>=0; i--) // in reverse order!
- {
- vardecl* v = session->globals[i];
- bool read_p = vut.read.find(v) != vut.read.end();
- bool write_p = vut.written.find(v) != vut.written.end();
- if (!read_p && !write_p) continue;
-
- // Duplicate lock flipping logic from above
- if (v->type == pe_stats)
- {
- if (write_p && !read_p) { read_p = true; write_p = false; }
- else if (read_p && !write_p) { read_p = false; write_p = true; }
- }
-
- // Duplicate "read-mostly" global variable logic from above.
- if (read_p && !write_p)
- {
- if (vcv_needs_global_locks.written.find(v)
- == vcv_needs_global_locks.written.end())
- continue;
- }
-
- numvars ++;
- o->newline(-1) << "unlock_" << v->name << ":";
- o->indent(1);
-
- if (session->verbose>1)
- clog << v->name << "[" << (read_p ? "r" : "")
- << (write_p ? "w" : "") << "] ";
-
- if (write_p) // emit write lock
- o->newline() << "write_unlock (& global.s_" << v->name << "_lock);";
- else // (read_p && !write_p) : emit read lock
- o->newline() << "read_unlock (& global.s_" << v->name << "_lock);";
-
- // fall through to next variable; thus the reverse ordering
- }
+ o->newline() << "if (!stp_lock_probe(locks, ARRAY_SIZE(locks)))";
+ o->newline(1) << "return;";
+ o->indent(-1);
+}
- // emit plain "unlock" label, used if the very first lock failed.
- o->newline(-1) << "unlock_: ;";
- o->indent(1);
- if (numvars) // is there a chance that any lock attempt failed?
- {
- // Formerly, we checked skipped_count > MAXSKIPPED here, and set
- // SYSTEMTAP_SESSION_ERROR if so. But now, this check is shared
- // via common_probe_entryfn_epilogue().
-
- if (session->verbose>1)
- clog << endl;
- }
- else if (session->verbose>1)
- clog << "nothing" << endl;
+void
+c_unparser::emit_unlocks(const varuse_collecting_visitor& vut)
+{
+ o->newline() << "stp_unlock_probe(locks, ARRAY_SIZE(locks));";
}
@@ -5233,13 +5191,10 @@ translate_pass (systemtap_session& s)
s.op->newline() << "#include \"loc2c-runtime.h\" ";
s.op->newline() << "#include \"access_process_vm.h\" ";
- // XXX: old 2.6 kernel hack
- s.op->newline() << "#ifndef read_trylock";
- s.op->newline() << "#define read_trylock(x) ({ read_lock(x); 1; })";
- s.op->newline() << "#endif";
-
s.up->emit_common_header (); // context etc.
+ s.op->newline() << "#include \"probe_lock.h\" ";
+
for (unsigned i=0; i<s.embeds.size(); i++)
{
s.op->newline() << s.embeds[i]->code << "\n";