1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
|
/* -*- linux-c -*-
* Context Runtime Functions
* Copyright (C) 2014 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 _STAPDYN_RUNTIME_CONTEXT_H_
#define _STAPDYN_RUNTIME_CONTEXT_H_
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <pthread.h>
#include <asm/unistd.h>
/* Defined after stap_probes[] by translate.cxx */
static const char* stp_probe_point(size_t index);
/* Defined later in common_session_state.h */
static inline struct context* stp_session_context(size_t index);
static int _stp_runtime_num_contexts;
/* Locally-cached context pointer -- see _stp_runtime_entryfn_get_context()
* and _stp_runtime_entryfn_put_context(). */
static __thread struct context *tls_context;
static int _stp_runtime_contexts_init(void)
{
_stp_runtime_num_contexts = sysconf(_SC_NPROCESSORS_ONLN);
if (_stp_runtime_num_contexts < 1)
_stp_runtime_num_contexts = 1;
return 0;
}
static int _stp_runtime_contexts_alloc(void)
{
int i;
/* The allocation was already done in stp_session_init;
* we just need to initialize the context data. */
for (i = 0; i < _stp_runtime_num_contexts; i++) {
int rc;
struct context *c = stp_session_context(i);
c->data_index = i;
rc = stp_pthread_mutex_init_shared(&c->lock);
if (rc != 0) {
_stp_error("context mutex initialization failed");
return rc;
}
}
return 0;
}
/* Free the context resources.
*
* NB: This should *not* be called by every process which has mmaped the shared
* memory. Only the main process which created shm and originally called
* _stp_runtime_contexts_alloc should be the one to free it.
*/
static void _stp_runtime_contexts_free(void)
{
int i;
/* The context memory is managed elsewhere;
* we just need to teardown the context locks. */
for (i = 0; i < _stp_runtime_num_contexts; i++) {
struct context *c = stp_session_context(i);
(void)pthread_mutex_destroy(&c->lock);
}
}
static int _stp_runtime_get_data_index(void)
{
int data_index;
/* If this thread has already gotten a context structure,
* return the data index from it. */
if (tls_context != NULL)
return tls_context->data_index;
/* This shouldn't happen. */
/* FIXME: assert? */
return 0;
}
/* Figure out with cpu we're on, which is our default data_index.
* Make sure the returned data index number is within the range of
* [0.._stp_runtime_num_contexts]. Be sure to handle a sched_getcpu()
* failure (it will return -1). */
static int _stp_context_index(void)
{
/* The current cpu is the preferred index, because it will usually be
* different for every concurrent thread. (It is possible to be the same
* though, if kernel scheduling is unkind to us.) */
int index = _stp_sched_getcpu();
/* Failing cpu#, use the tid as a somewhat-random index. */
if (index < 0)
index = syscall(SYS_gettid);
return index % _stp_runtime_num_contexts;
}
static struct context * _stp_runtime_entryfn_get_context(void)
{
struct context *c;
int i, index, rc, data_index;
/* If 'tls_context' (which is thread-local storage) is already set
* for this thread, we are re-entrant, so just quit. */
if (tls_context != NULL)
return NULL;
data_index = _stp_context_index();
if (unlikely(data_index < 0))
data_index = 0;
/* Try to find a free context structure. */
index = data_index;
for (i = 0; i < _stp_runtime_num_contexts; i++, index++) {
if (index >= _stp_runtime_num_contexts)
index = 0;
c = stp_session_context(index);
if (pthread_mutex_trylock(&c->lock) == 0) {
/* We found a free context structure. Now that it is
* locked, set the TLS pointer and return the context. */
tls_context = c;
return tls_context;
}
}
/* If we're here, we couldn't find a free context structure. Wait
* on one. */
c = stp_session_context(data_index);
rc = pthread_mutex_lock(&c->lock);
if (rc == 0) {
tls_context = c;
return tls_context;
}
return NULL;
}
static void _stp_runtime_entryfn_put_context(struct context *c)
{
if (c && c == tls_context) {
tls_context = NULL;
pthread_mutex_unlock(&c->lock);
}
/* else, warn about bad state? */
return;
}
static struct context *_stp_runtime_get_context(void)
{
/* Note we don't call _stp_runtime_entryfn_get_context()
* here. This function is called after
* _stp_runtime_entryfn_get_context() and has no corresponding
* "put" function. */
return tls_context;
}
static void _stp_runtime_context_wait(void)
{
struct timespec hold_start;
int hold_index;
int holdon;
(void)clock_gettime(CLOCK_MONOTONIC_RAW, &hold_start);
hold_index = -1;
do {
int i;
holdon = 0;
struct timespec now, elapsed;
for (i = 0; i < _stp_runtime_num_contexts; i++) {
struct context *c = stp_session_context(i);
int ret = pthread_mutex_trylock(&c->lock);
if (ret == 0)
pthread_mutex_unlock(&c->lock);
else if (ret == EBUSY) {
holdon = 1;
/* Just In case things are really stuck, let's print
* some diagnostics. */
(void)clock_gettime(CLOCK_MONOTONIC_RAW, &now);
_stp_timespec_sub(&now, &hold_start, &elapsed);
/* If its been > 1 second since we started and we
* haven't already printed a message for this stuck
* context, print one. */
if (elapsed.tv_sec > 0 && (i > hold_index)) {
/* NB: c->probe_point is local memory, invalid across processes,
* so read it indirectly through c->probe_index instead. */
const char* pp = stp_probe_point(c->probe_index);
if (pp)
_stp_error("context[%d] stuck: %s", i, pp);
else
_stp_error("context[%d] stuck in unknown probe %zu",
i, c->probe_index);
hold_index = i;
}
}
/* else other pthread error == broken? what then? */
}
#ifdef STAP_OVERRIDE_STUCK_CONTEXT
/* In case things are really really stuck, we are going to
* pretend/assume/hope everything is OK, and let the cleanup
* finish. */
(void)clock_gettime(CLOCK_MONOTONIC_RAW, &now);
_stp_timespec_sub(&now, &hold_start, &elapsed);
if (elapsed.tv_sec > 10) {
_stp_warn("overriding stuck context to allow shutdown.");
holdon = 0; /* allow loop to exit */
}
#endif
if (holdon) {
sched_yield();
}
} while (holdon);
}
#endif /* _STAPDYN_RUNTIME_CONTEXT_H_ */
|