#ifndef FHGFSOPSCOMMKITCOMMON_H_ #define FHGFSOPSCOMMKITCOMMON_H_ #include #include #include #include #include #include #include #include #define BEEGFS_COMMKIT_MSGBUF_SIZE 4096 #define COMMKIT_DEBUG_READ_SEND (1 << 0) #define COMMKIT_DEBUG_READ_HEADER (1 << 1) #define COMMKIT_DEBUG_RECV_DATA (1 << 2) #define COMMKIT_DEBUG_WRITE_HEADER (1 << 3) #define COMMKIT_DEBUG_WRITE_POLL (1 << 4) #define COMMKIT_DEBUG_WRITE_SEND (1 << 5) #define COMMKIT_DEBUG_WRITE_RECV (1 << 6) // #define BEEGFS_COMMKIT_DEBUG 0b010 #ifndef BEEGFS_COMMKIT_DEBUG #define BEEGFS_COMMKIT_DEBUG 0 #endif #ifdef BEEGFS_COMMKIT_DEBUG #define CommKitErrorInjectRate 101 // fail a send/receive req every X jiffies #endif struct CommKitContext; typedef struct CommKitContext CommKitContext; static inline void __FhgfsOpsCommKitCommon_pollStateSocks(CommKitContext* context, size_t numStates); static inline void __FhgfsOpsCommKitCommon_handlePollError(CommKitContext* context, int pollRes); struct CommKitTargetInfo; enum { CK_RETRY_BUDDY_FALLBACK = 1 << 0, CK_RETRY_LOOP_EAGAIN = 1 << 1, }; enum CKTargetBadAction { CK_SKIP_TARGET = 0, CK_CONTINUE_TARGET = 1, }; struct CommKitContextOps { enum CKTargetBadAction (*selectedTargetBad)(CommKitContext*, struct CommKitTargetInfo*, const CombinedTargetState*); unsigned (*prepareHeader)(CommKitContext*, struct CommKitTargetInfo*); // returns >0 for successful send, 0 to advance to next state, or negative error code int (*sendData)(CommKitContext*, struct CommKitTargetInfo*); // return >= for success, 0 to advance, or negative beegfs error code int (*recvHeader)(CommKitContext*, struct CommKitTargetInfo*); // return >= for success, 0 to advance, or negative beegfs error code int (*recvData)(CommKitContext*, struct CommKitTargetInfo*); void (*printSendDataDetails)(CommKitContext*, struct CommKitTargetInfo*); void (*printSocketDetails)(CommKitContext*, struct CommKitTargetInfo*); int retryFlags; const char* logContext; }; /** * Additional data that is required or useful for all the states. * (This is shared states data.) * * invariant: (numRetryWaiters + numDone + numUnconnectable + numPollSocks) <= numStates */ struct CommKitContext { const struct CommKitContextOps* ops; App* app; Logger* log; void* private; RemotingIOInfo* ioInfo; struct list_head* targetInfoList; unsigned numRetryWaiters; // number of states with a communication error unsigned numDone; // number of finished states unsigned numAcquiredConns; // number of acquired conns to decide waiting or not (avoids deadlock) unsigned numUnconnectable; // states that didn't get a conn this round (reset to 0 in each round) unsigned numBufferless; // states that couldn't acquire a header buffer bool pollTimedOut; bool pollTimeoutLogged; bool connFailedLogged; PollState pollState; unsigned numPollSocks; unsigned currentRetryNum; // number of used up retries (in case of comm errors) unsigned maxNumRetries; // 0 if infinite number of retries enabled #ifdef BEEGFS_NVFS int gpudRc; #endif }; /** * Poll the state socks that need to be polled. * This will call _handlePollError() if something goes wrong with the poll(), e.g. timeout. * * Note: Caller must have checked that there actually are socks that need polling * (context.numPollSocks>0) before calling this. */ void __FhgfsOpsCommKitCommon_pollStateSocks(CommKitContext* context, size_t numStates) { int pollRes; Config* cfg = App_getConfig(context->app); size_t numWaiters = context->numPollSocks + context->numRetryWaiters + context->numDone + context->numUnconnectable + context->numBufferless; /* if not all the states are polling or done, some states must be ready to do something immediately => set timeout to 0 (non-blocking) */ /* note: be very careful with timeoutMS==0, because that means we don't release the CPU, so we need to ensure that timeoutMS==0 is no permanent condition. */ int timeoutMS = (numWaiters < numStates) ? 0 : cfg->connMsgLongTimeout; BEEGFS_BUG_ON_DEBUG(numWaiters > numStates, "numWaiters > numStates should never happen"); pollRes = SocketTk_poll(&context->pollState, timeoutMS); if(unlikely( (timeoutMS && pollRes <= 0) || (pollRes < 0) ) ) __FhgfsOpsCommKitCommon_handlePollError(context, pollRes); } void __FhgfsOpsCommKitCommon_handlePollError(CommKitContext* context, int pollRes) { // note: notifies the waiting sockets via context.pollTimedOut (they do not care whether // it was a timeout or an error) if(!context->pollTimeoutLogged) { if(!pollRes) { // no incoming data => timout or interrupted if (fatal_signal_pending(current)) { // interrupted Logger_logFormatted(context->log, 3, context->ops->logContext, "Communication (poll() ) interrupted by signal."); } else { // timout Logger_logErrFormatted(context->log, context->ops->logContext, "Communication (poll() ) timeout for %u sockets.", context->numPollSocks); } } else { // error Logger_logErrFormatted(context->log, context->ops->logContext, "Communication (poll() ) error for %u sockets. ErrCode: %d", context->numPollSocks, pollRes); } } context->pollTimeoutLogged = true; context->pollTimedOut = true; } #endif /*FHGFSOPSCOMMKITCOMMON_H_*/