DirectResult fusion_call_destroy (FusionCall *call) { D_DEBUG_AT( Fusion_Call, "%s( %p )\n", __FUNCTION__, call ); D_ASSERT( call != NULL ); D_ASSERT( call->handler != NULL || call->handler3 != NULL ); if (direct_log_domain_check( &Fusion_Call )) // avoid call to direct_trace_lookup_symbol_at D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call->handler ) ); while (ioctl (_fusion_fd( call->shared ), FUSION_CALL_DESTROY, &call->call_id)) { switch (errno) { case EINTR: continue; case EINVAL: //FIXME: kernel module destroys calls from exiting process already D_ERROR ("Fusion/Call: invalid call\n"); return DR_DESTROYED; default: break; } D_PERROR ("FUSION_CALL_DESTROY"); return DR_FAILURE; } call->handler = NULL; return DR_OK; }
DirectResult fusion_call_destroy (FusionCall *call) { D_DEBUG_AT( Fusion_Call, "%s( %p )\n", __FUNCTION__, call ); D_ASSERT( call != NULL ); D_ASSERT( call->handler != NULL ); D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call->handler ) ); while (ioctl (_fusion_fd( call->shared ), FUSION_CALL_DESTROY, &call->call_id)) { switch (errno) { case EINTR: continue; case EINVAL: D_ERROR ("Fusion/Call: invalid call\n"); return DR_DESTROYED; default: break; } D_PERROR ("FUSION_CALL_DESTROY"); return DR_FAILURE; } call->handler = NULL; return DR_OK; }
DirectResult fusion_call_execute (FusionCall *call, FusionCallExecFlags flags, int call_arg, void *call_ptr, int *ret_val) { D_DEBUG_AT( Fusion_Call, "%s( %p, 0x%x, %d, %p )\n", __FUNCTION__, call, flags, call_arg, call_ptr ); D_ASSERT( call != NULL ); if (!call->handler) return DR_DESTROYED; D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call->handler ) ); if (!(flags & FCEF_NODIRECT) && call->fusion_id == _fusion_id( call->shared )) { int ret; FusionCallHandlerResult result; result = call->handler( _fusion_id( call->shared ), call_arg, call_ptr, call->ctx, 0, &ret ); if (result != FCHR_RETURN) D_WARN( "local call handler returned FCHR_RETAIN, need FCEF_NODIRECT" ); if (ret_val) *ret_val = ret; } else { FusionCallExecute execute; execute.call_id = call->call_id; execute.call_arg = call_arg; execute.call_ptr = call_ptr; execute.flags = flags; while (ioctl( _fusion_fd( call->shared ), FUSION_CALL_EXECUTE, &execute )) { switch (errno) { case EINTR: continue; case EINVAL: // D_ERROR ("Fusion/Call: invalid call\n"); return DR_INVARG; case EIDRM: return DR_DESTROYED; default: break; } D_PERROR ("FUSION_CALL_EXECUTE"); return DR_FAILURE; } if (ret_val) *ret_val = execute.ret_val; } return DR_OK; }
void _fusion_call_process( FusionWorld *world, int call_id, FusionCallMessage *msg ) { FusionCallHandler call_handler; FusionCallReturn call_ret; FusionCallHandlerResult result; D_DEBUG_AT( Fusion_Call, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( world, FusionWorld ); D_ASSERT( msg != NULL ); call_handler = msg->handler; D_ASSERT( call_handler != NULL ); D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call_handler ) ); call_ret.val = 0; result = call_handler( msg->caller, msg->call_arg, msg->call_ptr, msg->ctx, msg->serial, &call_ret.val ); switch (result) { case FCHR_RETURN: if (msg->serial) { call_ret.serial = msg->serial; call_ret.call_id = call_id; while (ioctl (world->fusion_fd, FUSION_CALL_RETURN, &call_ret)) { switch (errno) { case EINTR: continue; case EIDRM: D_WARN( "caller withdrawn (signal?)" ); return; case EINVAL: D_ERROR( "Fusion/Call: invalid call\n" ); return; default: D_PERROR( "FUSION_CALL_RETURN" ); return; } } } break; case FCHR_RETAIN: break; default: D_BUG( "unknown result %d from call handler", result ); } }
DirectResult fusion_call_init3 (FusionCall *call, FusionCallHandler3 handler3, void *ctx, const FusionWorld *world) { FusionCallNew call_new; if (direct_log_domain_check( &Fusion_Call )) // avoid call to direct_trace_lookup_symbol_at D_DEBUG_AT( Fusion_Call, "%s( %p, %p <%s>, %p, %p )\n", __FUNCTION__, call, handler3, direct_trace_lookup_symbol_at( handler3 ), ctx, world ); D_ASSERT( call != NULL ); D_ASSERT( handler3 != NULL ); D_MAGIC_ASSERT( world, FusionWorld ); D_MAGIC_ASSERT( world->shared, FusionWorldShared ); /* Called from others. */ call_new.handler = handler3; call_new.ctx = ctx; while (ioctl( world->fusion_fd, FUSION_CALL_NEW, &call_new )) { switch (errno) { case EINTR: continue; default: break; } D_PERROR ("FUSION_CALL_NEW"); return DR_FAILURE; } memset( call, 0, sizeof(FusionCall) ); /* Store handler, called directly when called by ourself. */ call->handler3 = handler3; call->ctx = ctx; /* Store call and own fusion id. */ call->call_id = call_new.call_id; call->fusion_id = fusion_id( world ); /* Keep back pointer to shared world data. */ call->shared = world->shared; D_DEBUG_AT( Fusion_Call, " -> call id %d\n", call->call_id ); return DR_OK; }
DirectResult fusion_call_return3( FusionCall *call, unsigned int serial, void *ptr, unsigned int length ) { FusionCallReturn3 call_ret; D_DEBUG_AT( Fusion_Call, "%s( %p, serial %u, ptr %p, length %u )\n", __FUNCTION__, call, serial, ptr, length ); D_ASSERT( call != NULL ); if (direct_log_domain_check( &Fusion_Call )) // avoid call to direct_trace_lookup_symbol_at D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call->handler ) ); D_ASSUME( serial != 0 ); if (!serial) return DR_UNSUPPORTED; call_ret.call_id = call->call_id; call_ret.serial = serial; call_ret.ptr = ptr; call_ret.length = length; fusion_world_flush_calls( _fusion_world( call->shared ), 1 ); while (ioctl (_fusion_fd( call->shared ), FUSION_CALL_RETURN3, &call_ret)) { switch (errno) { case EINTR: continue; case EIDRM: D_WARN( "caller withdrawn (signal?)" ); return DR_NOCONTEXT; case EINVAL: D_ERROR( "Fusion/Call: invalid call\n" ); return DR_DESTROYED; default: break; } D_PERROR ("FUSION_CALL_RETURN3"); return DR_FAILURE; } return DR_OK; }
DirectResult fusion_call_return( FusionCall *call, unsigned int serial, int val ) { FusionCallReturn call_ret; D_DEBUG_AT( Fusion_Call, "%s( %p, %u, %d )\n", __FUNCTION__, call, serial, val ); D_ASSERT( call != NULL ); D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call->handler ) ); D_ASSUME( serial != 0 ); if (!serial) return DR_UNSUPPORTED; call_ret.call_id = call->call_id; call_ret.val = val; call_ret.serial = serial; while (ioctl (_fusion_fd( call->shared ), FUSION_CALL_RETURN, &call_ret)) { switch (errno) { case EINTR: continue; case EIDRM: D_WARN( "caller withdrawn (signal?)" ); return DR_NOCONTEXT; case EINVAL: D_ERROR( "Fusion/Call: invalid call\n" ); return DR_DESTROYED; default: break; } D_PERROR ("FUSION_CALL_RETURN"); return DR_FAILURE; } return DR_OK; }
void _fusion_call_process( FusionWorld *world, int call_id, FusionCallMessage *msg, void *ptr ) { FusionCallHandlerResult result = FCHR_RETURN; FusionCallHandler call_handler; FusionCallReturn call_ret = { .val = 0 }; D_DEBUG_AT( Fusion_Call, "%s( call_id %d, msg %p, ptr %p)\n", __FUNCTION__, call_id, msg, ptr ); D_MAGIC_ASSERT( world, FusionWorld ); D_ASSERT( msg != NULL ); D_ASSERT( msg->handler != NULL ); call_handler = msg->handler; if (direct_log_domain_check( &Fusion_Call )) // avoid call to direct_trace_lookup_symbol_at D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call_handler ) ); result = call_handler( msg->caller, msg->call_arg, ptr ? ptr : msg->call_ptr, msg->ctx, msg->serial, &call_ret.val ); switch (result) { case FCHR_RETURN: if (msg->serial) { call_ret.serial = msg->serial; call_ret.call_id = call_id; while (ioctl (world->fusion_fd, FUSION_CALL_RETURN, &call_ret)) { switch (errno) { case EINTR: continue; case EIDRM: D_WARN( "caller withdrawn (signal?)" ); return; case EINVAL: D_ERROR( "Fusion/Call: invalid call\n" ); return; default: D_PERROR( "FUSION_CALL_RETURN" ); return; } } } break; case FCHR_RETAIN: break; default: D_BUG( "unknown result %d from call handler", result ); } } void _fusion_call_process3( FusionWorld *world, int call_id, FusionCallMessage3 *msg, void *ptr ) { FusionCallHandlerResult result = FCHR_RETURN; FusionCallHandler3 call_handler; FusionCallReturn3 call_ret; char *ret_ptr = NULL; unsigned int ret_length = 0; D_DEBUG_AT( Fusion_Call, "%s( call_id %d, msg %p, ptr %p)\n", __FUNCTION__, call_id, msg, ptr ); D_MAGIC_ASSERT( world, FusionWorld ); D_ASSERT( msg != NULL ); D_ASSERT( msg->handler != NULL ); call_handler = msg->handler; if (direct_log_domain_check( &Fusion_Call )) // avoid call to direct_trace_lookup_symbol_at D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call_handler ) ); if (msg->ret_length > FUSION_CALL_RETURN_DATA_MAX) { D_ERROR( "Fusion/Call: Maximum return data length (%u) exceeded (%u)!\n", FUSION_CALL_RETURN_DATA_MAX, msg->ret_length ); } else { if (msg->ret_length > FUSION_CALL_RETURN_DATA_MAX_ON_STACK) { ret_ptr = D_MALLOC( msg->ret_length ); if (!ret_ptr) D_OOM(); } else ret_ptr = alloca( msg->ret_length ); } if (ret_ptr) result = call_handler( msg->caller, msg->call_arg, ptr ? ptr : msg->call_ptr, msg->call_length, msg->ctx, msg->serial, ret_ptr, msg->ret_length, &ret_length ); switch (result) { case FCHR_RETURN: if (msg->serial) { call_ret.call_id = call_id; call_ret.serial = msg->serial; call_ret.ptr = ret_ptr; call_ret.length = ret_length; while (ioctl (world->fusion_fd, FUSION_CALL_RETURN3, &call_ret)) { switch (errno) { case EINTR: continue; case EIDRM: D_DEBUG_AT( Fusion_Call, " -> caller withdrawn (signal?)\n" ); goto out; case EINVAL: D_ERROR( "Fusion/Call: invalid call\n" ); goto out; default: D_PERROR( "FUSION_CALL_RETURN3" ); goto out; } } } break; case FCHR_RETAIN: break; default: D_BUG( "unknown result %d from call handler", result ); } out: if (msg->ret_length > FUSION_CALL_RETURN_DATA_MAX_ON_STACK) D_FREE( ret_ptr ); }
DirectResult fusion_call_execute3(FusionCall *call, FusionCallExecFlags flags, int call_arg, void *ptr, unsigned int length, void *ret_ptr, unsigned int ret_size, unsigned int *ret_length) { FusionWorld *world; CallTLS *call_tls; D_DEBUG_AT( Fusion_Call, "%s( %p, flags 0x%x, arg %d, ptr %p, length %u, ret_ptr %p, ret_size %u )\n", __FUNCTION__, call, flags, call_arg, ptr, length, ret_ptr, ret_size ); if (ret_size > FUSION_CALL_RETURN_DATA_MAX) return DR_LIMITEXCEEDED; D_ASSERT( call != NULL ); // if (!call->handler) // return DR_DESTROYED; #if D_DEBUG_ENABLED if (direct_log_domain_check( &Fusion_Call )) // avoid call to direct_trace_lookup_symbol_at D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call->handler3 ) ); #endif world = _fusion_world( call->shared ); call_tls = Call_GetTLS( world ); if (call->fusion_id == fusion_id( world ) && (!(flags & FCEF_NODIRECT) || (call_tls->dispatcher))) { FusionCallHandlerResult result; unsigned int execute_length; D_ASSERT( call->handler3 != NULL ); result = call->handler3( call->fusion_id, call_arg, ptr, length, call->ctx, 0, ret_ptr, ret_size, &execute_length ); if (result != FCHR_RETURN) D_WARN( "local call handler returned FCHR_RETAIN, need FCEF_NODIRECT" ); if (ret_length) *ret_length = execute_length; } else { FusionCallExecute3 execute; DirectResult ret = DR_OK; // check whether we can cache this call if (flags & FCEF_QUEUE && fusion_config->call_bin_max_num > 0) { D_ASSERT( flags & FCEF_ONEWAY ); if (call_tls->bins_data_len + length > fusion_config->call_bin_max_data) { ret = fusion_world_flush_calls( world, 0 ); if (ret) return ret; } call_tls->bins[call_tls->bins_num].call_id = call->call_id; call_tls->bins[call_tls->bins_num].call_arg = call_arg; call_tls->bins[call_tls->bins_num].ptr = NULL; call_tls->bins[call_tls->bins_num].length = length; call_tls->bins[call_tls->bins_num].ret_ptr = ret_ptr; call_tls->bins[call_tls->bins_num].ret_length = ret_size; call_tls->bins[call_tls->bins_num].flags = flags | FCEF_FOLLOW; if (length > 0) { call_tls->bins[call_tls->bins_num].ptr = &call_tls->bins_data[call_tls->bins_data_len]; direct_memcpy( &call_tls->bins_data[call_tls->bins_data_len], ptr, length ); call_tls->bins_data_len += length; } call_tls->bins_num++; if (call_tls->bins_num == 1) call_tls->bins_create_ts = direct_clock_get_millis(); else if (call_tls->bins_num >= fusion_config->call_bin_max_num || direct_clock_get_millis() - call_tls->bins_create_ts >= EXECUTE3_BIN_FLUSH_MILLIS) ret = fusion_world_flush_calls( world, 0 ); return ret; } ret = fusion_world_flush_calls( world, 1 ); execute.call_id = call->call_id; execute.call_arg = call_arg; execute.ptr = ptr; execute.length = length; execute.ret_ptr = ret_ptr; execute.ret_length = ret_size; execute.flags = flags | FCEF_RESUMABLE; execute.serial = 0; D_DEBUG_AT( Fusion_Call, " -> ptr %p, length %u\n", ptr, length ); while (ioctl( world->fusion_fd, FUSION_CALL_EXECUTE3, &execute )) { switch (errno) { case EINTR: continue; case EINVAL: D_ERROR ("Fusion/Call: invalid call (id 0x%08x)\n", call->call_id); return DR_INVARG; case EIDRM: return DR_DESTROYED; default: break; } D_PERROR ("FUSION_CALL_EXECUTE3 (call id 0x%08x, creator %lu)", call->call_id, call->fusion_id ); return DR_FAILURE; } if (ret_length) *ret_length = execute.ret_length; } return DR_OK; }
DirectResult fusion_call_execute2(FusionCall *call, FusionCallExecFlags flags, int call_arg, void *ptr, unsigned int length, int *ret_val) { D_DEBUG_AT( Fusion_Call, "%s( %p, flags 0x%x, arg %d, %p, length %u )\n", __FUNCTION__, call, flags, call_arg, ptr, length ); D_ASSERT( call != NULL ); // if (!call->handler) // return DR_DESTROYED; if (direct_log_domain_check( &Fusion_Call )) // avoid call to direct_trace_lookup_symbol_at D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call->handler ) ); if (!(flags & FCEF_NODIRECT) && call->fusion_id == _fusion_id( call->shared )) { int ret; FusionCallHandlerResult result; result = call->handler( call->fusion_id, call_arg, ptr, call->ctx, 0, &ret ); if (result != FCHR_RETURN) D_WARN( "local call handler returned FCHR_RETAIN, need FCEF_NODIRECT" ); if (ret_val) *ret_val = ret; } else { FusionCallExecute2 execute; execute.call_id = call->call_id; execute.call_arg = call_arg; execute.ptr = ptr; execute.length = length; execute.flags = flags | FCEF_RESUMABLE; execute.serial = 0; fusion_world_flush_calls( _fusion_world( call->shared ), 1 ); while (ioctl( _fusion_fd( call->shared ), FUSION_CALL_EXECUTE2, &execute )) { switch (errno) { case EINTR: continue; case EINVAL: // D_ERROR ("Fusion/Call: invalid call\n"); return DR_INVARG; case EIDRM: return DR_DESTROYED; default: break; } D_PERROR ("FUSION_CALL_EXECUTE2 (call id 0x%08x, creator %lu)", call->call_id, call->fusion_id ); return DR_FAILURE; } if (ret_val) *ret_val = execute.ret_val; } return DR_OK; }
DirectResult fusion_call_execute3(FusionCall *call, FusionCallExecFlags flags, int call_arg, void *ptr, unsigned int length, void *ret_ptr, unsigned int ret_size, unsigned int *ret_length) { D_DEBUG_AT( Fusion_Call, "%s( %p, flags 0x%x, arg %d, ptr %p, length %u, ret_ptr %p, ret_size %u )\n", __FUNCTION__, call, flags, call_arg, ptr, length, ret_ptr, ret_size ); if (ret_size > FUSION_CALL_RETURN_DATA_MAX) return DR_LIMITEXCEEDED; D_ASSERT( call != NULL ); // if (!call->handler) // return DR_DESTROYED; D_DEBUG_AT( Fusion_Call, " -> %s\n", direct_trace_lookup_symbol_at( call->handler3 ) ); if (!(flags & FCEF_NODIRECT) && call->fusion_id == _fusion_id( call->shared )) { FusionCallHandlerResult result; unsigned int execute_length; D_ASSERT( call->handler3 != NULL ); result = call->handler3( call->fusion_id, call_arg, ptr, length, call->ctx, 0, ret_ptr, ret_size, &execute_length ); if (result != FCHR_RETURN) D_WARN( "local call handler returned FCHR_RETAIN, need FCEF_NODIRECT" ); if (ret_length) *ret_length = execute_length; } else { FusionCallExecute3 execute; execute.call_id = call->call_id; execute.call_arg = call_arg; execute.ptr = ptr; execute.length = length; execute.ret_ptr = ret_ptr; execute.ret_length = ret_size; execute.flags = flags; D_DEBUG_AT( Fusion_Call, " -> ptr %p, length %u\n", ptr, length ); while (ioctl( _fusion_fd( call->shared ), FUSION_CALL_EXECUTE3, &execute )) { switch (errno) { case EINTR: continue; case EINVAL: // D_ERROR ("Fusion/Call: invalid call\n"); return DR_INVARG; case EIDRM: return DR_DESTROYED; default: break; } D_PERROR ("FUSION_CALL_EXECUTE3"); return DR_FAILURE; } if (ret_length) *ret_length = execute.ret_length; } return DR_OK; }