blob: 670466194598fd547cb214083c10d4e62f4b75c0 [file] [log] [blame]
/*
* Copyright (c) 2000-2001 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <xfs/xfs.h>
#include <xfs/jdm.h>
#include <pthread.h>
#include "types.h"
#include "exit.h"
#include "stream.h"
#include "lock.h"
#include "mlog.h"
#define N(a) (sizeof((a)) / sizeof((a)[0]))
struct spm {
stream_state_t s_state;
pthread_t s_tid;
intgen_t s_ix;
int s_exit_code;
rv_t s_exit_return;
rv_t s_exit_hint;
};
typedef struct spm spm_t;
static spm_t spm[ STREAM_SIMMAX * 3 ];
static bool_t initialized = BOOL_FALSE;
void
stream_init( void )
{
( void )memset( ( void * )spm, 0, sizeof( spm ));
initialized = BOOL_TRUE;
}
/*
* Note that the stream list structure (updated via the stream_* functions)
* is indexed by pthread_t (tid). Multiple processes can be registered against
* the same stream index, typically: the primary content process that does the
* work; and the drive slave process, which just processes stuff off the ring
* buffer. In general having multiple tids registered per stream is not an issue
* for termination status reporting, as the mlog_exit* logging functions only
* ever get called out of the primary content process.
*/
void
stream_register( pthread_t tid, intgen_t streamix )
{
spm_t *p = spm;
spm_t *ep = spm + N(spm);
ASSERT( streamix < STREAM_SIMMAX );
lock();
for ( ; p < ep ; p++ ) {
if ( p->s_state == S_FREE ) {
p->s_state = S_RUNNING;
break;
}
}
unlock();
ASSERT( p < ep );
if ( p >= ep ) return;
p->s_tid = tid;
p->s_ix = streamix;
p->s_exit_code = -1;
p->s_exit_return = RV_NONE;
p->s_exit_hint = RV_NONE;
}
/* NOTE: lock() must be held when calling stream_dead() */
void
stream_dead( pthread_t tid )
{
spm_t *p = spm;
spm_t *ep = spm + N(spm);
for ( ; p < ep ; p++ )
if ( pthread_equal( p->s_tid, tid ) ) {
p->s_state = S_ZOMBIE;
break;
}
ASSERT( p < ep );
}
void
stream_free( pthread_t tid )
{
spm_t *p = spm;
spm_t *ep = spm + N(spm);
lock();
for ( ; p < ep ; p++ ) {
if ( pthread_equal( p->s_tid, tid ) ) {
(void) memset( (void *) p, 0, sizeof(spm_t) );
p->s_state = S_FREE;
break;
}
}
unlock();
ASSERT( p < ep );
}
int
stream_find_all( stream_state_t states[], int nstates,
pthread_t tids[], int ntids )
{
int i, count = 0;
spm_t *p = spm;
spm_t *ep = spm + N(spm);
ASSERT(nstates > 0 && ntids > 0);
if (!initialized)
return 0;
/* lock - make sure we get a consistent snapshot of the stream status */
lock();
for ( ; p < ep && count < ntids; p++ )
for (i = 0; i < nstates; i++)
if (p->s_state == states[i]) {
tids[count++] = p->s_tid;
break;
}
unlock();
return count;
}
static spm_t *
stream_find( pthread_t tid, stream_state_t s[], int nstates )
{
int i;
spm_t *p = spm;
spm_t *ep = spm + N(spm);
ASSERT(nstates > 0);
/* note we don't lock the stream array in this function */
for ( ; p < ep ; p++ )
if ( pthread_equal( p->s_tid, tid ) ) {
/* check state */
for (i = 0; i < nstates; i++)
if (p->s_state == s[i])
return p;
}
#ifdef STREAM_DEBUG
{
static const char *state_strings[] = { "S_FREE", "S_RUNNING", "S_ZOMBIE" };
mlog( MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
"stream_find(): no stream with tid: %lu and state%s:",
tid, nstates == 1 ? "" : "s" );
for (i = 0; i < nstates; i++)
mlog( MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
" %s", state_strings[s[i]]);
mlog( MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE, "\n");
}
#endif /* STREAM_DEBUG */
return (spm_t *) NULL;
}
/*
* Note, the following function is called from mlog.c:mlog_va(),
* where locking may be disabled and we are already protected by
* another lock. So no locking is done in this function.
*/
intgen_t
stream_getix( pthread_t tid )
{
stream_state_t states[] = { S_RUNNING };
spm_t *p;
intgen_t ix;
p = stream_find( tid, states, N(states) );
ix = p ? p->s_ix : -1;
return ix;
}
/*
* We don't currently export spm_t and don't really want callers
* keeping pointers into the stream array, so use explicit access
* functions. Note we only expect these to be called from running/owner
* streams.
*/
#define stream_set(field_name, tid, value) \
stream_state_t states[] = { S_RUNNING }; \
spm_t *p; \
pthread_t mytid = pthread_self(); \
\
if ( !pthread_equal(mytid, (tid))) { \
mlog( MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK, \
"stream_set_" #field_name "(): " \
"foreign stream (tid %lu) " \
"not permitted to update this stream (tid %lu)\n",\
mytid, (tid)); \
return; \
} \
\
lock(); \
p = stream_find( (tid), states, N(states) ); \
if (p) p->s_exit_ ## field_name = (value); \
unlock();
void stream_set_code( pthread_t tid, int exit_code )
{
stream_set( code, tid, exit_code );
}
void stream_set_return( pthread_t tid, rv_t rv )
{
stream_set( return, tid, rv );
}
void stream_set_hint( pthread_t tid, rv_t rv )
{
stream_set( hint, tid, rv );
}
bool_t
stream_get_exit_status( pthread_t tid,
stream_state_t states[],
int nstates,
stream_state_t *state,
intgen_t *ix,
int *exit_code,
rv_t *exit_return,
rv_t *exit_hint)
{
bool_t sts = BOOL_FALSE;
spm_t *p;
lock();
p = stream_find( tid, states, nstates );
if (! p) goto unlock;
if (state) *state = p->s_state;
if (ix) *ix = p->s_ix;
if (exit_code) *exit_code = p->s_exit_code;
if (exit_return) *exit_return = p->s_exit_return;
if (exit_hint) *exit_hint = p->s_exit_hint;
sts = BOOL_TRUE;
unlock:
unlock();
return sts;
}
size_t
stream_cnt( void )
{
spm_t *p = spm;
spm_t *ep = spm + N(spm);
size_t ixmap = 0;
size_t ixcnt;
size_t bitix;
ASSERT( sizeof( ixmap ) * NBBY >= STREAM_SIMMAX );
lock();
for ( ; p < ep ; p++ ) {
if ( p->s_state == S_RUNNING ) {
ixmap |= ( size_t )1 << p->s_ix;
}
}
unlock();
ixcnt = 0;
for ( bitix = 0 ; bitix < STREAM_SIMMAX ; bitix++ ) {
if ( ixmap & ( ( size_t )1 << bitix )) {
ixcnt++;
}
}
return ixcnt;
}