| /* |
| * 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; |
| } |