diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9546814..914f2b9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -144,6 +144,7 @@ add_library(nwcore SHARED library/os/config.c nss/cache/control.c nss/cache/asyncio.c + nss/cache/bond.c library/fsm/fsmnw.c library/latch/intlatch.c library/latch/latch.c diff --git a/src/core/nss/cache/bond.c b/src/core/nss/cache/bond.c new file mode 100644 index 0000000..82616da --- /dev/null +++ b/src/core/nss/cache/bond.c @@ -0,0 +1,362 @@ +/**************************************************************************** + | + | (C) Copyright 1985, 1991, 1993, 1996 Novell, Inc. + | All Rights Reserved. + | + | This program is free software; you can redistribute it and/or + | modify it under the terms of version 2 of the GNU General Public + | License as published by the Free Software Foundation. + | + | This program is distributed in the hope that it will 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, contact Novell, Inc. + | + | To contact Novell about this file by physical or electronic mail, + | you may find current contact information at www.novell.com + | + |*************************************************************************** + | + | NetWare Advance File Services (NSS) module + | + |--------------------------------------------------------------------------- + | + | $Author: vandana $ + | $Date: 2005-08-10 01:03:51 +0530 (Wed, 10 Aug 2005) $ + | + | $RCSfile$ + | $Revision: 1177 $ + | + |--------------------------------------------------------------------------- + | This module is used to: + +-------------------------------------------------------------------------*/ +#include +#include +#include +#include + +ControlStore_s BondControl; /* Structure for Controlling allocation and + * tracking of bond strucutures. + */ + +STATUS BOND_Startup (void) +{ + STATUS status; + ASSERT_MPKNSS_LOCK(); + status = CONTROL_Startup( &BondControl, Config.cache.numBonds, + sizeof(Bond_s), NULL); + return status; +} + +void BOND_Shutdown (void) +{ + ASSERT_MPKNSS_LOCK(); + CONTROL_Shutdown( &BondControl); +} + +/************************************************************************** + * + * Agent 'toSignal' is dependent on 'toFlush' occuring(written) first. So + * a good way to remember the relationships recorded by this function is + * that AGENT + * 'toFlush' will occur 1st. + * 'toSignal' will occur 2nd. + * + * Agent 'toSignal' gets placed on the signal list of agent 'toFlush'. I.E. + * when 'toFlush' occurs then 'toSignal' Agent gets signaled. + * + * Agent 'toFlush' gets placed on the flush list of the agent 'toSignal'. I.E. + * if agent 'toSignal' needs to be flushed, "toSignal's" flush list is used + * to find and then tell 'toFlush' that it should flush. + * + ***************************************************************************/ + +void LB_bind (Agent_s *toSignal, Agent_s *toFlush) +{ + Bond_s *bond; + + ENTER(TBOND, bind); + ASSERT_MPKNSS_LOCK(); + + bond = GET_BOND(); + + zASSERT(bond != NULL); + + SETUP_BOND(bond, toSignal, toFlush); + + RTN_VOID(); +} + + +///************************************************************************** +// * +// ***************************************************************************/ +//void initBind (Bond_s *bond, Agent_s *toSignal, Agent_s *toFlush) +//{ +// zASSERT(bond != NULL); +// +// bond->toSignal = toSignal; +// bond->toFlush = toFlush; +// STK_PUSH(toFlush->signalList, bond, signalLink); +// DQ_ENQ( &toSignal->flushList, bond, flushLink); +//} + +/************************************************************************** + * Release the BOND and put it back on the AVAIL list + ***************************************************************************/ +void freeBond (Bond_s *bond) +{ + ENTER(TBOND, freeBond); + ASSERT_MPKNSS_LOCK(); + zASSERT(!QMEMBER( &bond->signalLink)); + zASSERT(!QMEMBER( &bond->flushLink)); +// zASSERT((bond->toFlush == NULL) || DQ_EMPTY( &bond->toFlush->flushList)); +// zASSERT((bond->toFlush == NULL) || (bond->toFlush->numLeft == 0)); + + CONTROL_FREE(bond); + RTN_VOID(); +} + +/************************************************************************** + * + ***************************************************************************/ +void initAgent ( + Agent_s *agent, + AgentSignalFunc_t signal, + char *name) +{ + static int instance = 0; + ASSERT_MPKNSS_LOCK(); + zASSERT(agent != NULL); + + ++instance; + FSMLITE_INIT( &agent->fsm, name, instance); + INIT_LATCH( &agent->latch); + INIT_ONESHOT(agent->timer); + STK_INIT(agent->signalList); + DQ_INIT( &agent->flushList); + agent->signal = (signal) ? signal : defaultSignal; + agent->numLeft = 0; + agent->state = 0; + agent->status = zOK; + agent->bondFreedSignal = NULL; + return; +} + +/************************************************************************** + * This is called when a signal handler finishes writing the requested + * item. This propogates the signals back up through the bond that the + * request has completed. + ***************************************************************************/ +void LB_defaultSignal(Agent_s *agent) +{ + Bond_s *next; + Agent_s *toSignal; + + ENTER(TBOND, defaultSignal); + ASSERT_MPKNSS_LOCK(); + zASSERT(agent->state & AGENT_FLUSHING); +// I'm removing the NO_BONDS assert, because if the agent is a beast then, +// it is possible that we are doing a cacheFlushMyCacheBufs (which will be +// followed by a defaultFlush), and new bonds are being put on the beast. +// The defaultFlush that will happen after will take care of the new +// bonds. We could get around this whole thing by X_LATCHING the beast +// while in cacheFlushMyCacheBufs. But at this point we do not want to +// take the risk of making that change. Vandana - 11/21/00 +// zASSERT(NO_BONDS(agent)); + ASSERT_SLATCH(&agent->latch); + + STK_POP(agent->signalList, next, Bond_s, signalLink); + while (next != NULL) + { + toSignal = next->toSignal; + if (QMEMBER( &next->flushLink)) + { + DQ_RMV(next, flushLink); + } + else + { + zASSERT(toSignal->numLeft != 0); + --toSignal->numLeft; + } + if ((toSignal->state & AGENT_FLUSHING) + && (toSignal->state & AGENT_AWAIT_SIGNALS) + && (NO_BONDS(toSignal))) + { + toSignal->state &= ~AGENT_AWAIT_SIGNALS; + toSignal->signal(toSignal); + } + if (toSignal->bondFreedSignal != NULL) + { + toSignal->bondFreedSignal(toSignal); + } + freeBond(next); + STK_POP(agent->signalList, next, Bond_s, signalLink); + } + agent->state &= ~AGENT_FLUSHING; + UNS_LATCH( &agent->latch); + RTN_VOID(); +} + +/************************************************************************** + * This is called to continue the default flush request. + ***************************************************************************/ +void LB_continueFlush (FsmLite_s *fsm) +{ + Agent_s *agent = STRUCT(fsm, Agent_s, fsm); + Agent_s *toFlush; + Bond_s *next; + + ENTER(TBOND, continueFlush); + ASSERT_MPKNSS_LOCK(); + if (NO_BONDS(agent)) + { + agent->signal(agent); + } + else + { + agent->state |= AGENT_AWAIT_SIGNALS; + DQ_DEQ( &agent->flushList, next, Bond_s, flushLink); + while (next != NULL) + { + toFlush = next->toFlush; + ++agent->numLeft; + defaultFlush(toFlush); + DQ_DEQ( &agent->flushList, next, Bond_s, flushLink); + } + } + RTN_VOID(); +} + +/************************************************************************** + * This routine is called when you want to write anything to the disk. + * It handles buffers, beasts, volumes, etc. This is an asyncrounous + * request and will return when all the appropriate items are queued. + * This propogates all of the flush request appropriatly through any + * dependent bonds. + ***************************************************************************/ +void LB_defaultFlush (Agent_s *agent) +{ + ENTER(TBOND, defaultFlush); + ASSERT_MPKNSS_LOCK(); + CANCEL_ALARM(agent->timer); + + if (!(agent->state & AGENT_FLUSHING)) + { + agent->state |= AGENT_FLUSHING; + /* We aquire this latch so that we can wait for this operation + * to complete. See defaultFlushWait + */ + FSM_S_LATCH( &agent->latch, &agent->fsm, LB_continueFlush); + } + RTN_VOID(); +} + +/************************************************************************** + * This will flush and then wait for it to finish. + ***************************************************************************/ +void LB_defaultFlushWait (Agent_s *agent) +{ + ENTER(TBOND, defaultFlushWait); + ASSERT_MPKNSS_LOCK(); + defaultFlush(agent); + X_BARRIER( &agent->latch); + RTN_VOID(); +} + +/* + * Lazy flush sets up the agent to signal up the dependancy when + * all of its dependancies have been flushed but it doesn't force + * them. + */ +void LB_lazyFlush (Agent_s *agent) +{ + if (NO_BONDS(agent)) + { /* + * We don't need to wait for anything else to finish, so + * clean ourself up. + */ + defaultFlush(agent); + } + else + { + ASSERT_NOLATCH( &agent->latch); + agent->state |= (AGENT_FLUSHING | AGENT_AWAIT_SIGNALS); + S_FORCE( &agent->latch); + } +} + + + +#if NSS_DEBUG IS_ENABLED && 0 +/*========================================================================= + *========================================================================= + * DEBUG CODE + *========================================================================= + *=========================================================================*/ + +/************************************************************************** + * + ***************************************************************************/ +void prDepth (NINT depth) +{ + NINT i; + ASSERT_MPKNSS_LOCK(); + for (i = 0; i < depth; ++i) + { + printf(MSGNot(" ")); + } +} + +/************************************************************************** + * + ***************************************************************************/ +void prSignals (Agent_s *agent, NINT depth) +{ + Bond_s *next; + ASSERT_MPKNSS_LOCK(); + prDepth(depth++); + printf(MSGNot("signals=%s[%d]\n"), agent->fsm.type, agent->numLeft); + STK_FOREACH(agent->signalList, next, Bond_s, signalLink) + { + prSignals(next->toSignal, depth); + } +} + +/************************************************************************** + * + ***************************************************************************/ +void prFlushes (Agent_s *agent, NINT depth) +{ + Bond_s *next; + ASSERT_MPKNSS_LOCK(); + prDepth(depth++); + printf(MSGNot("flushes=%s[%d]\n"), agent->fsm.type, agent->numLeft); + DQ_FOREACH( &agent->flushList, next, Bond_s, flushLink) + { + prFlushes(next->toFlush, depth); + } +} + +/************************************************************************** + * + ***************************************************************************/ +void prBondControl (Agent_s *agent) +{ + Bond_s *next; + ASSERT_MPKNSS_LOCK(); + printf(MSGNot("agent=%s[%d]\n"), agent->fsm.type, agent->numLeft); + STK_FOREACH(agent->signalList, next, Bond_s, signalLink) + { + printf(MSGNot("\tsignal=%s\n"), next->toSignal->fsm.type); + } + DQ_FOREACH( &agent->flushList, next, Bond_s, flushLink) + { + printf(MSGNot("\tflush=%s\n"), next->toFlush->fsm.type); + } +} + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3a3666b..be142e7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,6 +28,7 @@ if(MARS_NWE_BUILD_NWFS_TESTS) add_subdirectory(core/alarm) add_subdirectory(core/control) add_subdirectory(core/asyncio) + add_subdirectory(core/bond) add_subdirectory(core/config) add_subdirectory(core/parse) add_subdirectory(core/fsm) diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index f043c47..202b936 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(worktodo) add_subdirectory(alarm) add_subdirectory(control) add_subdirectory(asyncio) +add_subdirectory(bond) add_subdirectory(config) add_subdirectory(parse) add_subdirectory(fsm) diff --git a/tests/core/bond/CMakeLists.txt b/tests/core/bond/CMakeLists.txt new file mode 100644 index 0000000..5ed082b --- /dev/null +++ b/tests/core/bond/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(test_nwcore_bond test_nwcore_bond.c) +target_link_libraries(test_nwcore_bond PRIVATE mars_nwe::core) +add_test(NAME nwcore.bond COMMAND test_nwcore_bond) diff --git a/tests/core/bond/test_nwcore_bond.c b/tests/core/bond/test_nwcore_bond.c new file mode 100644 index 0000000..45b990a --- /dev/null +++ b/tests/core/bond/test_nwcore_bond.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#include + +#define CHECK(expr) do { \ + if (!(expr)) { \ + fprintf(stderr, "CHECK failed: %s:%d: %s\n", __FILE__, __LINE__, #expr); \ + return 1; \ + } \ +} while (0) + +extern void fsmWorkToDo(void); + +static int signal_count; + +static void test_signal(Agent_s *agent) +{ + ++signal_count; + LB_defaultSignal(agent); +} + +int main(void) +{ + Agent_s to_signal; + Agent_s to_flush; + + configStartup(); + + MPKNSS_LOCK(); + CHECK(BOND_Startup() == zOK); + + initAgent(&to_signal, test_signal, "to_signal"); + initAgent(&to_flush, NULL, "to_flush"); + + CHECK(NO_BONDS(&to_signal)); + CHECK(NO_SIGNALS(&to_flush)); + + LB_bind(&to_signal, &to_flush); + CHECK(!NO_BONDS(&to_signal)); + CHECK(!NO_SIGNALS(&to_flush)); + + LB_defaultFlush(&to_signal); + fsmWorkToDo(); + CHECK(NO_BONDS(&to_signal)); + CHECK(NO_SIGNALS(&to_flush)); + CHECK(signal_count == 1); + + BOND_Shutdown(); + MPKNSS_UNLOCK(); + return 0; +}