void orte_rml_base_close_channel(int fd, short flags, void *cbdata) { orte_rml_send_request_t *req = (orte_rml_send_request_t*)cbdata; orte_rml_close_channel_t *close_chan; OPAL_OUTPUT_VERBOSE((1, orte_rml_base_framework.framework_output, "%s rml_close_channel to peer %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&req->post.close_channel.channel->peer))); OPAL_TIMING_EVENT((&tm_rml, "to %s", ORTE_NAME_PRINT(&req->post.close_channel.channel->peer))); close_chan = OBJ_NEW(orte_rml_close_channel_t); close_chan->channel = req->post.close_channel.channel; close_chan->cbfunc = req->post.close_channel.cbfunc; close_chan->cbdata = req->post.close_channel.cbdata; OBJ_RELEASE(req); /* check with qos if the channel ready to be closed */ if (ORTE_SUCCESS == orte_qos_close_channel (close_chan->channel->qos, close_chan->channel->qos_channel_ptr)) { orte_rml_base_send_close_channel( close_chan); } /* complete close request with error channel busy */ else { close_chan->status = ORTE_ERR_CHANNEL_BUSY; ORTE_RML_CLOSE_CHANNEL_COMPLETE(close_chan); OBJ_RELEASE(close_chan); } }
static int send_bytes(mca_oob_tcp_peer_t* peer) { mca_oob_tcp_send_t* msg = peer->send_msg; int rc; OPAL_TIMING_EVENT((&tm,"Send %d bytes", msg->sdbytes)); while (0 < msg->sdbytes) { rc = write(peer->sd, msg->sdptr, msg->sdbytes); if (rc < 0) { if (opal_socket_errno == EINTR) { continue; } else if (opal_socket_errno == EAGAIN) { /* tell the caller to keep this message on active, * but let the event lib cycle so other messages * can progress while this socket is busy */ return ORTE_ERR_RESOURCE_BUSY; } else if (opal_socket_errno == EWOULDBLOCK) { /* tell the caller to keep this message on active, * but let the event lib cycle so other messages * can progress while this socket is busy */ return ORTE_ERR_WOULD_BLOCK; } /* we hit an error and cannot progress this message */ opal_output(0, "%s->%s mca_oob_tcp_msg_send_bytes: write failed: %s (%d) [sd = %d]", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name)), strerror(opal_socket_errno), opal_socket_errno, peer->sd); return ORTE_ERR_COMM_FAILURE; } /* update location */ msg->sdbytes -= rc; msg->sdptr += rc; } /* we sent the full data block */ return ORTE_SUCCESS; }
void orte_rml_base_open_channel(int fd, short flags, void *cbdata) { int32_t *type, type_val; orte_rml_send_request_t *req = (orte_rml_send_request_t*)cbdata; orte_process_name_t peer; orte_rml_open_channel_t *open_chan; orte_rml_channel_t *channel; opal_buffer_t *buffer; peer = req->post.open_channel.dst; OPAL_OUTPUT_VERBOSE((1, orte_rml_base_framework.framework_output, "%s rml_open_channel to peer %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&peer))); OPAL_TIMING_EVENT((&tm_rml, "to %s", ORTE_NAME_PRINT(&peer))); /* return error if a channel already exists */ if ( NULL != (channel = get_channel (&peer, req->post.open_channel.qos_attributes, false))) { req->post.open_channel.status = ORTE_ERR_OPEN_CHANNEL_DUPLICATE; req->post.open_channel.channel = channel; ORTE_RML_OPEN_CHANNEL_COMPLETE(&req->post.open_channel); OBJ_RELEASE(req); return; } channel = OBJ_NEW(orte_rml_channel_t); channel->channel_num = opal_pointer_array_add (&orte_rml_base.open_channels, channel); channel->peer = peer; open_chan = OBJ_NEW(orte_rml_open_channel_t); open_chan->dst = peer; open_chan->qos_attributes = req->post.open_channel.qos_attributes; open_chan->cbfunc = req->post.open_channel.cbfunc; open_chan->cbdata = req->post.open_channel.cbdata; OBJ_RELEASE(req); // associate open channel request and the newly created channel object open_chan->channel = channel; type = &type_val; if (!orte_get_attribute( open_chan->qos_attributes, ORTE_QOS_TYPE, (void**)&type, OPAL_UINT8)) { return; } open_chan->channel->qos = (void*) orte_qos_get_module (open_chan->qos_attributes); OPAL_OUTPUT_VERBOSE((1, orte_rml_base_framework.framework_output, "%s rml_open_channel type = %d to peer %s ", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), *type, ORTE_NAME_PRINT(&peer))); // now associate qos with the channel based on user requested attributes. if ( NULL != open_chan->channel->qos) { open_chan->channel->qos_channel_ptr = orte_qos_create_channel (open_chan->channel->qos, open_chan->qos_attributes, open_chan->channel->channel_num); // create rml send for open channel request. Call the corresponding QoS module to pack the attributes. buffer = OBJ_NEW (opal_buffer_t); // call QoS module to pack attributes if ( ORTE_SUCCESS == (orte_qos_open_channel(open_chan->channel->qos, open_chan->channel->qos_channel_ptr, buffer))) { /* pack channel number at the end */ opal_dss.pack(buffer, (void*) &open_chan->channel->channel_num, 1, OPAL_UINT32); OPAL_OUTPUT_VERBOSE((1, orte_rml_base_framework.framework_output, "%s rml_open_channel to peer %s SUCCESS sending to peer", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&peer))); // post a recieve for open_channel_response tag orte_rml.recv_buffer_nb(&peer, ORTE_RML_TAG_OPEN_CHANNEL_RESP, ORTE_RML_NON_PERSISTENT, orte_rml_base_open_channel_resp_callback, open_chan); // send request to peer to open channel orte_rml.send_buffer_nb( &peer, buffer, ORTE_RML_TAG_OPEN_CHANNEL_REQ, orte_rml_base_open_channel_send_callback, open_chan); } else { open_chan->status = ORTE_ERR_PACK_FAILURE; ORTE_RML_OPEN_CHANNEL_COMPLETE(open_chan); opal_pointer_array_set_item ( &orte_rml_base.open_channels, open_chan->channel->channel_num, NULL); // call QoS module to release the QoS channel object. orte_qos_close_channel (open_chan->channel->qos, open_chan->channel->qos_channel_ptr); OBJ_RELEASE (buffer); OBJ_RELEASE(open_chan->channel); OBJ_RELEASE(open_chan); } } else { // do error completion because a component for the requested QoS does not exist open_chan->status = ORTE_ERR_QOS_TYPE_UNSUPPORTED; ORTE_RML_OPEN_CHANNEL_COMPLETE(open_chan); opal_pointer_array_set_item ( &orte_rml_base.open_channels, open_chan->channel->channel_num, NULL); OBJ_RELEASE(open_chan->channel); OBJ_RELEASE(open_chan); } }
static void send_msg(int fd, short args, void *cbdata) { orte_rml_send_request_t *req = (orte_rml_send_request_t*)cbdata; orte_process_name_t *peer = &(req->post.dst); orte_rml_tag_t tag = req->post.tag; orte_rml_recv_t *rcv; orte_rml_send_t *snd; int bytes; orte_self_send_xfer_t *xfer; int i; char* ptr; OPAL_OUTPUT_VERBOSE((1, orte_rml_base_framework.framework_output, "%s rml_send_msg to peer %s at tag %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(peer), tag)); OPAL_TIMING_EVENT((&tm_rml, "to %s", ORTE_NAME_PRINT(peer))); /* if this is a message to myself, then just post the message * for receipt - no need to dive into the oob */ if (OPAL_EQUAL == orte_util_compare_name_fields(ORTE_NS_CMP_ALL, peer, ORTE_PROC_MY_NAME)) { /* local delivery */ OPAL_OUTPUT_VERBOSE((1, orte_rml_base_framework.framework_output, "%s rml_send_iovec_to_self at tag %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), tag)); /* send to self is a tad tricky - we really don't want * to track the send callback function throughout the recv * process and execute it upon receipt as this would provide * very different timing from a non-self message. Specifically, * if we just retain a pointer to the incoming data * and then execute the send callback prior to the receive, * then the caller will think we are done with the data and * can release it. So we have to copy the data in order to * execute the send callback prior to receiving the message. * * In truth, this really is a better mimic of the non-self * message behavior. If we actually pushed the message out * on the wire and had it loop back, then we would receive * a new block of data anyway. */ /* setup the send callback */ xfer = OBJ_NEW(orte_self_send_xfer_t); if (NULL != req->post.iov) { xfer->iov = req->post.iov; xfer->count = req->post.count; xfer->cbfunc.iov = req->post.cbfunc.iov; } else { xfer->buffer = req->post.buffer; xfer->cbfunc.buffer = req->post.cbfunc.buffer; } xfer->tag = tag; xfer->cbdata = req->post.cbdata; /* setup the event for the send callback */ opal_event_set(orte_event_base, &xfer->ev, -1, OPAL_EV_WRITE, send_self_exe, xfer); opal_event_set_priority(&xfer->ev, ORTE_MSG_PRI); opal_event_active(&xfer->ev, OPAL_EV_WRITE, 1); /* copy the message for the recv */ rcv = OBJ_NEW(orte_rml_recv_t); rcv->sender = *peer; rcv->tag = tag; if (NULL != req->post.iov) { /* get the total number of bytes in the iovec array */ bytes = 0; for (i = 0 ; i < req->post.count ; ++i) { bytes += req->post.iov[i].iov_len; } /* get the required memory allocation */ if (0 < bytes) { rcv->iov.iov_base = (IOVBASE_TYPE*)malloc(bytes); rcv->iov.iov_len = bytes; /* transfer the bytes */ ptr = (char*)rcv->iov.iov_base; for (i = 0 ; i < req->post.count ; ++i) { memcpy(ptr, req->post.iov[i].iov_base, req->post.iov[i].iov_len); ptr += req->post.iov[i].iov_len; } } } else if (0 < req->post.buffer->bytes_used) { rcv->iov.iov_base = (IOVBASE_TYPE*)malloc(req->post.buffer->bytes_used); memcpy(rcv->iov.iov_base, req->post.buffer->base_ptr, req->post.buffer->bytes_used); rcv->iov.iov_len = req->post.buffer->bytes_used; } /* post the message for receipt - since the send callback was posted * first and has the same priority, it will execute first */ ORTE_RML_ACTIVATE_MESSAGE(rcv); OBJ_RELEASE(req); return; } snd = OBJ_NEW(orte_rml_send_t); snd->dst = *peer; snd->origin = *ORTE_PROC_MY_NAME; snd->tag = tag; if (NULL != req->post.iov) { snd->iov = req->post.iov; snd->count = req->post.count; snd->cbfunc.iov = req->post.cbfunc.iov; } else { snd->buffer = req->post.buffer; snd->cbfunc.buffer = req->post.cbfunc.buffer; } snd->cbdata = req->post.cbdata; /* activate the OOB send state */ ORTE_OOB_SEND(snd); OBJ_RELEASE(req); }
int orte_rml_oob_send_nb(struct orte_rml_base_module_t *mod, orte_process_name_t* peer, struct iovec* iov, int count, orte_rml_tag_t tag, orte_rml_callback_fn_t cbfunc, void* cbdata) { orte_rml_recv_t *rcv; orte_rml_send_t *snd; int bytes; orte_self_send_xfer_t *xfer; int i; char* ptr; OPAL_OUTPUT_VERBOSE((1, orte_rml_base_framework.framework_output, "%s rml_send to peer %s at tag %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(peer), tag)); if (ORTE_RML_TAG_INVALID == tag) { /* cannot send to an invalid tag */ ORTE_ERROR_LOG(ORTE_ERR_BAD_PARAM); return ORTE_ERR_BAD_PARAM; } if (NULL == peer || OPAL_EQUAL == orte_util_compare_name_fields(ORTE_NS_CMP_ALL, ORTE_NAME_INVALID, peer)) { /* cannot send to an invalid peer */ ORTE_ERROR_LOG(ORTE_ERR_BAD_PARAM); return ORTE_ERR_BAD_PARAM; } OPAL_TIMING_EVENT((&tm_rml, "to %s", ORTE_NAME_PRINT(peer))); /* if this is a message to myself, then just post the message * for receipt - no need to dive into the oob */ if (OPAL_EQUAL == orte_util_compare_name_fields(ORTE_NS_CMP_ALL, peer, ORTE_PROC_MY_NAME)) { /* local delivery */ OPAL_OUTPUT_VERBOSE((1, orte_rml_base_framework.framework_output, "%s rml_send_iovec_to_self at tag %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), tag)); /* send to self is a tad tricky - we really don't want * to track the send callback function throughout the recv * process and execute it upon receipt as this would provide * very different timing from a non-self message. Specifically, * if we just retain a pointer to the incoming data * and then execute the send callback prior to the receive, * then the caller will think we are done with the data and * can release it. So we have to copy the data in order to * execute the send callback prior to receiving the message. * * In truth, this really is a better mimic of the non-self * message behavior. If we actually pushed the message out * on the wire and had it loop back, then we would receive * a new block of data anyway. */ /* setup the send callback */ xfer = OBJ_NEW(orte_self_send_xfer_t); xfer->iov = iov; xfer->count = count; xfer->cbfunc.iov = cbfunc; xfer->tag = tag; xfer->cbdata = cbdata; /* setup the event for the send callback */ opal_event_set(orte_event_base, &xfer->ev, -1, OPAL_EV_WRITE, send_self_exe, xfer); opal_event_set_priority(&xfer->ev, ORTE_MSG_PRI); opal_event_active(&xfer->ev, OPAL_EV_WRITE, 1); /* copy the message for the recv */ rcv = OBJ_NEW(orte_rml_recv_t); rcv->sender = *peer; rcv->tag = tag; /* get the total number of bytes in the iovec array */ bytes = 0; for (i = 0 ; i < count ; ++i) { bytes += iov[i].iov_len; } /* get the required memory allocation */ if (0 < bytes) { rcv->iov.iov_base = (IOVBASE_TYPE*)malloc(bytes); rcv->iov.iov_len = bytes; /* transfer the bytes */ ptr = (char*)rcv->iov.iov_base; for (i = 0 ; i < count ; ++i) { memcpy(ptr, iov[i].iov_base, iov[i].iov_len); ptr += iov[i].iov_len; } } /* post the message for receipt - since the send callback was posted * first and has the same priority, it will execute first */ ORTE_RML_ACTIVATE_MESSAGE(rcv); return ORTE_SUCCESS; } snd = OBJ_NEW(orte_rml_send_t); snd->dst = *peer; snd->origin = *ORTE_PROC_MY_NAME; snd->tag = tag; snd->iov = iov; snd->count = count; snd->cbfunc.iov = cbfunc; snd->cbdata = cbdata; snd->routed = strdup(mod->routed); /* activate the OOB send state */ ORTE_OOB_SEND(snd); return ORTE_SUCCESS; }
void mca_oob_tcp_recv_handler(int sd, short flags, void *cbdata) { mca_oob_tcp_peer_t* peer = (mca_oob_tcp_peer_t*)cbdata; int rc; orte_process_name_t hop; mca_oob_tcp_peer_t *relay; uint64_t ui64; if (orte_abnormal_term_ordered) { return; } opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s:tcp:recv:handler called for peer %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&peer->name)); switch (peer->state) { case MCA_OOB_TCP_CONNECT_ACK: if (ORTE_SUCCESS == (rc = mca_oob_tcp_peer_recv_connect_ack(peer, peer->sd, NULL))) { opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s:tcp:recv:handler starting send/recv events", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME)); /* we connected! Start the send/recv events */ if (!peer->recv_ev_active) { opal_event_add(&peer->recv_event, 0); peer->recv_ev_active = true; } if (peer->timer_ev_active) { opal_event_del(&peer->timer_event); peer->timer_ev_active = false; } /* if there is a message waiting to be sent, queue it */ if (NULL == peer->send_msg) { peer->send_msg = (mca_oob_tcp_send_t*)opal_list_remove_first(&peer->send_queue); } if (NULL != peer->send_msg && !peer->send_ev_active) { opal_event_add(&peer->send_event, 0); peer->send_ev_active = true; } /* update our state */ peer->state = MCA_OOB_TCP_CONNECTED; } else { opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s UNABLE TO COMPLETE CONNECT ACK WITH %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&peer->name)); opal_event_del(&peer->recv_event); ORTE_FORCED_TERMINATE(1); return; } break; case MCA_OOB_TCP_CONNECTED: opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s:tcp:recv:handler CONNECTED", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME)); /* allocate a new message and setup for recv */ if (NULL == peer->recv_msg) { opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s:tcp:recv:handler allocate new recv msg", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME)); peer->recv_msg = OBJ_NEW(mca_oob_tcp_recv_t); if (NULL == peer->recv_msg) { opal_output(0, "%s-%s mca_oob_tcp_peer_recv_handler: unable to allocate recv message\n", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name))); return; } /* start by reading the header */ peer->recv_msg->rdptr = (char*)&peer->recv_msg->hdr; peer->recv_msg->rdbytes = sizeof(mca_oob_tcp_hdr_t); } /* if the header hasn't been completely read, read it */ if (!peer->recv_msg->hdr_recvd) { opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s:tcp:recv:handler read hdr", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME)); if (ORTE_SUCCESS == (rc = read_bytes(peer))) { OPAL_TIMING_EVENT((&tm,"Header received from %s", ORTE_NAME_PRINT(&peer->name))); /* completed reading the header */ peer->recv_msg->hdr_recvd = true; /* convert the header */ MCA_OOB_TCP_HDR_NTOH(&peer->recv_msg->hdr); /* if this is a zero-byte message, then we are done */ if (0 == peer->recv_msg->hdr.nbytes) { opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s RECVD ZERO-BYTE MESSAGE FROM %s for tag %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&peer->name), peer->recv_msg->hdr.tag); peer->recv_msg->data = NULL; // make sure } else { opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s:tcp:recv:handler allocate data region of size %lu", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), (unsigned long)peer->recv_msg->hdr.nbytes); /* allocate the data region */ peer->recv_msg->data = (char*)malloc(peer->recv_msg->hdr.nbytes); /* point to it */ peer->recv_msg->rdptr = peer->recv_msg->data; peer->recv_msg->rdbytes = peer->recv_msg->hdr.nbytes; } /* fall thru and attempt to read the data */ } else if (ORTE_ERR_RESOURCE_BUSY == rc || ORTE_ERR_WOULD_BLOCK == rc) { /* exit this event and let the event lib progress */ return; } else { /* close the connection */ opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s:tcp:recv:handler error reading bytes - closing connection", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME)); mca_oob_tcp_peer_close(peer); return; } } if (peer->recv_msg->hdr_recvd) { /* continue to read the data block - we start from * wherever we left off, which could be at the * beginning or somewhere in the message */ if (ORTE_SUCCESS == (rc = read_bytes(peer))) { OPAL_TIMING_EVENT((&tm,"Msg received from %s", ORTE_NAME_PRINT(&peer->name))); /* we recvd all of the message */ opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s RECVD COMPLETE MESSAGE FROM %s (ORIGIN %s) OF %d BYTES FOR DEST %s TAG %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&peer->name), ORTE_NAME_PRINT(&peer->recv_msg->hdr.origin), (int)peer->recv_msg->hdr.nbytes, ORTE_NAME_PRINT(&peer->recv_msg->hdr.dst), peer->recv_msg->hdr.tag); /* am I the intended recipient (header was already converted back to host order)? */ if (peer->recv_msg->hdr.dst.jobid == ORTE_PROC_MY_NAME->jobid && peer->recv_msg->hdr.dst.vpid == ORTE_PROC_MY_NAME->vpid) { /* yes - post it to the RML for delivery */ opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s DELIVERING TO RML", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME)); ORTE_RML_POST_MESSAGE(&peer->recv_msg->hdr.origin, peer->recv_msg->hdr.tag, peer->recv_msg->data, peer->recv_msg->hdr.nbytes); OBJ_RELEASE(peer->recv_msg); } else { /* no - find the next hop in the route */ hop = orte_routed.get_route(&peer->recv_msg->hdr.dst); if (hop.jobid == ORTE_JOBID_INVALID || hop.vpid == ORTE_VPID_INVALID) { /* no hop known - post the error to the component * and let the OOB see if there is another way * to get there from here */ opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s NO ROUTE TO %s FROM HERE", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&peer->name)); /* let the component know about the problem */ ORTE_ACTIVATE_TCP_MSG_ERROR(NULL, peer->recv_msg, &hop, mca_oob_tcp_component_no_route); /* cleanup */ OBJ_RELEASE(peer->recv_msg); return; } else { /* does we know how to reach the next hop? */ memcpy(&ui64, (char*)&hop, sizeof(uint64_t)); if (OPAL_SUCCESS != opal_hash_table_get_value_uint64(&mca_oob_tcp_module.peers, ui64, (void**)&relay)) { opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s ADDRESS OF NEXT HOP %s TO %s IS UNKNOWN", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&hop), ORTE_NAME_PRINT(&peer->recv_msg->hdr.dst)); /* let the component know about the problem */ ORTE_ACTIVATE_TCP_MSG_ERROR(NULL, peer->recv_msg, &hop, mca_oob_tcp_component_hop_unknown); /* cleanup */ OBJ_RELEASE(peer->recv_msg); return; } opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s ROUTING TO %s FROM HERE", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&relay->name)); /* if this came from a different job family, then ensure * we know how to return */ if (ORTE_JOB_FAMILY(peer->recv_msg->hdr.origin.jobid) != ORTE_JOB_FAMILY(ORTE_PROC_MY_NAME->jobid)) { orte_routed.update_route(&(peer->recv_msg->hdr.origin), &peer->name); } /* post the message for retransmission */ MCA_OOB_TCP_QUEUE_RELAY(peer->recv_msg, relay); OBJ_RELEASE(peer->recv_msg); } } peer->recv_msg = NULL; return; } else if (ORTE_ERR_RESOURCE_BUSY == rc || ORTE_ERR_WOULD_BLOCK == rc) { /* exit this event and let the event lib progress */ return; } else { // report the error opal_output(0, "%s-%s mca_oob_tcp_peer_recv_handler: unable to recv message", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name))); /* turn off the recv event */ opal_event_del(&peer->recv_event); ORTE_FORCED_TERMINATE(1); return; } } break; default: opal_output(0, "%s-%s mca_oob_tcp_peer_recv_handler: invalid socket state(%d)", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name)), peer->state); // mca_oob_tcp_peer_close(peer); break; } }
static int read_bytes(mca_oob_tcp_peer_t* peer) { int rc; int to_read = peer->recv_msg->rdbytes; /* read until all bytes recvd or error */ while (0 < peer->recv_msg->rdbytes) { rc = read(peer->sd, peer->recv_msg->rdptr, peer->recv_msg->rdbytes); if (rc < 0) { if(opal_socket_errno == EINTR) { continue; } else if (opal_socket_errno == EAGAIN) { /* tell the caller to keep this message on active, * but let the event lib cycle so other messages * can progress while this socket is busy */ return ORTE_ERR_RESOURCE_BUSY; } else if (opal_socket_errno == EWOULDBLOCK) { /* tell the caller to keep this message on active, * but let the event lib cycle so other messages * can progress while this socket is busy */ OPAL_TIMING_EVENT((&tm,"Received %d bytes. Would block", to_read - peer->recv_msg->rdbytes)); return ORTE_ERR_WOULD_BLOCK; } /* we hit an error and cannot progress this message - report * the error back to the RML and let the caller know * to abort this message */ opal_output_verbose(OOB_TCP_DEBUG_FAIL, orte_oob_base_framework.framework_output, "%s-%s mca_oob_tcp_msg_recv: readv failed: %s (%d)", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name)), strerror(opal_socket_errno), opal_socket_errno); // mca_oob_tcp_peer_close(peer); // if (NULL != mca_oob_tcp.oob_exception_callback) { // mca_oob_tcp.oob_exception_callback(&peer->name, ORTE_RML_PEER_DISCONNECTED); //} return ORTE_ERR_COMM_FAILURE; } else if (rc == 0) { /* the remote peer closed the connection - report that condition * and let the caller know */ opal_output_verbose(OOB_TCP_DEBUG_FAIL, orte_oob_base_framework.framework_output, "%s-%s mca_oob_tcp_msg_recv: peer closed connection", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name))); /* stop all events */ if (peer->recv_ev_active) { opal_event_del(&peer->recv_event); peer->recv_ev_active = false; } if (peer->timer_ev_active) { opal_event_del(&peer->timer_event); peer->timer_ev_active = false; } if (peer->send_ev_active) { opal_event_del(&peer->send_event); peer->send_ev_active = false; } if (NULL != peer->recv_msg) { OBJ_RELEASE(peer->recv_msg); peer->recv_msg = NULL; } OPAL_TIMING_EVENT((&tm,"Received %d bytes. Would block", to_read - peer->recv_msg->rdbytes)); mca_oob_tcp_peer_close(peer); //if (NULL != mca_oob_tcp.oob_exception_callback) { // mca_oob_tcp.oob_exception_callback(&peer->peer_name, ORTE_RML_PEER_DISCONNECTED); //} return ORTE_ERR_WOULD_BLOCK; } /* we were able to read something, so adjust counters and location */ peer->recv_msg->rdbytes -= rc; peer->recv_msg->rdptr += rc; } OPAL_TIMING_EVENT((&tm,"Received %d bytes", to_read)); /* we read the full data block */ return ORTE_SUCCESS; }
/* * A file descriptor is available/ready for send. Check the state * of the socket and take the appropriate action. */ void mca_oob_tcp_send_handler(int sd, short flags, void *cbdata) { mca_oob_tcp_peer_t* peer = (mca_oob_tcp_peer_t*)cbdata; mca_oob_tcp_send_t* msg = peer->send_msg; int rc; opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s tcp:send_handler called to send to peer %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&peer->name)); switch (peer->state) { case MCA_OOB_TCP_CONNECTING: case MCA_OOB_TCP_CLOSED: opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s tcp:send_handler %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), mca_oob_tcp_state_print(peer->state)); mca_oob_tcp_peer_complete_connect(peer); /* de-activate the send event until the connection * handshake completes */ if (peer->send_ev_active) { opal_event_del(&peer->send_event); peer->send_ev_active = false; } break; case MCA_OOB_TCP_CONNECTED: opal_output_verbose(OOB_TCP_DEBUG_CONNECT, orte_oob_base_framework.framework_output, "%s tcp:send_handler SENDING TO %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), (NULL == peer->send_msg) ? "NULL" : ORTE_NAME_PRINT(&peer->name)); if (NULL != msg) { /* if the header hasn't been completely sent, send it */ if (!msg->hdr_sent) { OPAL_TIMING_EVENT((&tm,"Send header to %s", ORTE_NAME_PRINT(&peer->name))); if (ORTE_SUCCESS == (rc = send_bytes(peer))) { /* header is completely sent */ msg->hdr_sent = true; /* setup to send the data */ if (NULL != msg->data) { /* relay msg - send that data */ msg->sdptr = msg->data; msg->sdbytes = (int)ntohl(msg->hdr.nbytes); } else if (NULL == msg->msg) { /* this was a zero-byte relay - nothing more to do */ OBJ_RELEASE(msg); peer->send_msg = NULL; goto next; } else if (NULL != msg->msg->buffer) { /* send the buffer data as a single block */ msg->sdptr = msg->msg->buffer->base_ptr; msg->sdbytes = msg->msg->buffer->bytes_used; } else if (NULL != msg->msg->iov) { /* start with the first iovec */ msg->sdptr = msg->msg->iov[0].iov_base; msg->sdbytes = msg->msg->iov[0].iov_len; msg->iovnum = 0; } else { /* just send the data */ msg->sdptr = msg->msg->data; msg->sdbytes = msg->msg->count; } /* fall thru and let the send progress */ } else if (ORTE_ERR_RESOURCE_BUSY == rc || ORTE_ERR_WOULD_BLOCK == rc) { /* exit this event and let the event lib progress */ return; } else { // report the error opal_output(0, "%s-%s mca_oob_tcp_peer_send_handler: unable to send header", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name))); opal_event_del(&peer->send_event); msg->msg->status = rc; ORTE_RML_SEND_COMPLETE(msg->msg); OBJ_RELEASE(msg); peer->send_msg = NULL; goto next; } } /* progress the data transmission */ if (msg->hdr_sent) { OPAL_TIMING_EVENT((&tm,"Send msg to %s", ORTE_NAME_PRINT(&peer->name))); if (ORTE_SUCCESS == (rc = send_bytes(peer))) { /* this block is complete */ if (NULL != msg->data || NULL == msg->msg) { /* the relay is complete - release the data */ opal_output_verbose(2, orte_oob_base_framework.framework_output, "%s MESSAGE RELAY COMPLETE TO %s OF %d BYTES ON SOCKET %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name)), (int)ntohl(msg->hdr.nbytes), peer->sd); OBJ_RELEASE(msg); peer->send_msg = NULL; } else if (NULL != msg->msg->buffer) { /* we are done - notify the RML */ opal_output_verbose(2, orte_oob_base_framework.framework_output, "%s MESSAGE SEND COMPLETE TO %s OF %d BYTES ON SOCKET %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name)), (int)ntohl(msg->hdr.nbytes), peer->sd); msg->msg->status = ORTE_SUCCESS; ORTE_RML_SEND_COMPLETE(msg->msg); OBJ_RELEASE(msg); peer->send_msg = NULL; } else if (NULL != msg->msg->data) { /* this was a relay we have now completed - no need to * notify the RML as the local proc didn't initiate * the send */ opal_output_verbose(2, orte_oob_base_framework.framework_output, "%s MESSAGE RELAY COMPLETE TO %s OF %d BYTES ON SOCKET %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name)), (int)ntohl(msg->hdr.nbytes), peer->sd); msg->msg->status = ORTE_SUCCESS; OBJ_RELEASE(msg); peer->send_msg = NULL; } else { /* rotate to the next iovec */ msg->iovnum++; if (msg->iovnum < msg->msg->count) { msg->sdptr = msg->msg->iov[msg->iovnum].iov_base; msg->sdbytes = msg->msg->iov[msg->iovnum].iov_len; /* exit this event to give the event lib * a chance to progress any other pending * actions */ return; } else { /* this message is complete - notify the RML */ opal_output_verbose(2, orte_oob_base_framework.framework_output, "%s MESSAGE SEND COMPLETE TO %s OF %d BYTES ON SOCKET %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name)), (int)ntohl(msg->hdr.nbytes), peer->sd); msg->msg->status = ORTE_SUCCESS; ORTE_RML_SEND_COMPLETE(msg->msg); OBJ_RELEASE(msg); peer->send_msg = NULL; } } /* fall thru to queue the next message */ } else if (ORTE_ERR_RESOURCE_BUSY == rc || ORTE_ERR_WOULD_BLOCK == rc) { /* exit this event and let the event lib progress */ return; } else { // report the error opal_output(0, "%s-%s mca_oob_tcp_peer_send_handler: unable to send message ON SOCKET %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name)), peer->sd); opal_event_del(&peer->send_event); msg->msg->status = rc; ORTE_RML_SEND_COMPLETE(msg->msg); OBJ_RELEASE(msg); peer->send_msg = NULL; ORTE_FORCED_TERMINATE(1); return; } } next: /* if current message completed - progress any pending sends by * moving the next in the queue into the "on-deck" position. Note * that this doesn't mean we send the message right now - we will * wait for another send_event to fire before doing so. This gives * us a chance to service any pending recvs. */ peer->send_msg = (mca_oob_tcp_send_t*) opal_list_remove_first(&peer->send_queue); } /* if nothing else to do unregister for send event notifications */ if (NULL == peer->send_msg && peer->send_ev_active) { opal_event_del(&peer->send_event); peer->send_ev_active = false; } break; default: opal_output(0, "%s-%s mca_oob_tcp_peer_send_handler: invalid connection state (%d) on socket %d", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME), ORTE_NAME_PRINT(&(peer->name)), peer->state, peer->sd); if (peer->send_ev_active) { opal_event_del(&peer->send_event); peer->send_ev_active = false; } break; } }
int ompi_mpi_finalize(void) { int ret; static int32_t finalize_has_already_started = 0; opal_list_item_t *item; ompi_proc_t** procs; size_t nprocs; OPAL_TIMING_DECLARE(tm); OPAL_TIMING_INIT(&tm); /* Be a bit social if an erroneous program calls MPI_FINALIZE in two different threads, otherwise we may deadlock in ompi_comm_free() (or run into other nasty lions, tigers, or bears) */ if (! opal_atomic_cmpset_32(&finalize_has_already_started, 0, 1)) { /* Note that if we're already finalized, we cannot raise an MPI exception. The best that we can do is write something to stderr. */ char hostname[MAXHOSTNAMELEN]; pid_t pid = getpid(); gethostname(hostname, sizeof(hostname)); opal_show_help("help-mpi-runtime.txt", "mpi_finalize:invoked_multiple_times", true, hostname, pid); return MPI_ERR_OTHER; } ompi_mpiext_fini(); /* Per MPI-2:4.8, we have to free MPI_COMM_SELF before doing anything else in MPI_FINALIZE (to include setting up such that MPI_FINALIZED will return true). */ if (NULL != ompi_mpi_comm_self.comm.c_keyhash) { ompi_attr_delete_all(COMM_ATTR, &ompi_mpi_comm_self, ompi_mpi_comm_self.comm.c_keyhash); OBJ_RELEASE(ompi_mpi_comm_self.comm.c_keyhash); ompi_mpi_comm_self.comm.c_keyhash = NULL; } /* Proceed with MPI_FINALIZE */ ompi_mpi_finalized = true; /* As finalize is the last legal MPI call, we are allowed to force the release * of the user buffer used for bsend, before going anywhere further. */ (void)mca_pml_base_bsend_detach(NULL, NULL); #if OPAL_ENABLE_PROGRESS_THREADS == 0 opal_progress_set_event_flag(OPAL_EVLOOP_ONCE | OPAL_EVLOOP_NONBLOCK); #endif /* Redo ORTE calling opal_progress_event_users_increment() during MPI lifetime, to get better latency when not using TCP */ opal_progress_event_users_increment(); /* check to see if we want timing information */ OPAL_TIMING_EVENT((&tm,"Start barrier")); /* NOTE: MPI-2.1 requires that MPI_FINALIZE is "collective" across *all* connected processes. This only means that all processes have to call it. It does *not* mean that all connected processes need to synchronize (either directly or indirectly). For example, it is quite easy to construct complicated scenarios where one job is "connected" to another job via transitivity, but have no direct knowledge of each other. Consider the following case: job A spawns job B, and job B later spawns job C. A "connectedness" graph looks something like this: A <--> B <--> C So what are we *supposed* to do in this case? If job A is still connected to B when it calls FINALIZE, should it block until jobs B and C also call FINALIZE? After lengthy discussions many times over the course of this project, the issue was finally decided at the Louisville Feb 2009 meeting: no. Rationale: - "Collective" does not mean synchronizing. It only means that every process call it. Hence, in this scenario, every process in A, B, and C must call FINALIZE. - KEY POINT: if A calls FINALIZE, then it is erroneous for B or C to try to communicate with A again. - Hence, OMPI is *correct* to only effect a barrier across each jobs' MPI_COMM_WORLD before exiting. Specifically, if A calls FINALIZE long before B or C, it's *correct* if A exits at any time (and doesn't notify B or C that it is exiting). - Arguably, if B or C do try to communicate with the now-gone A, OMPI should try to print a nice error ("you tried to communicate with a job that is already gone...") instead of segv or other Badness. However, that is an *extremely* difficult problem -- sure, it's easy for A to tell B that it is finalizing, but how can A tell C? A doesn't even know about C. You'd need to construct a "connected" graph in a distributed fashion, which is fraught with race conditions, etc. Hence, our conclusion is: OMPI is *correct* in its current behavior (of only doing a barrier across its own COMM_WORLD) before exiting. Any problems that occur are as a result of erroneous MPI applications. We *could* tighten up the erroneous cases and ensure that we print nice error messages / don't crash, but that is such a difficult problem that we decided we have many other, much higher priority issues to handle that deal with non-erroneous cases. */ /* Wait for everyone to reach this point. This is a grpcomm barrier instead of an MPI barrier for (at least) two reasons: 1. An MPI barrier doesn't ensure that all messages have been transmitted before exiting (e.g., a BTL can lie and buffer a message without actually injecting it to the network, and therefore require further calls to that BTL's progress), so the possibility of a stranded message exists. 2. If the MPI communication is using an unreliable transport, there's a problem of knowing that everyone has *left* the barrier. E.g., one proc can send its ACK to the barrier message to a peer and then leave the barrier, but the ACK can get lost and therefore the peer is left in the barrier. Point #1 has been known for a long time; point #2 emerged after we added the first unreliable BTL to Open MPI and fixed the del_procs behavior around May of 2014 (see https://svn.open-mpi.org/trac/ompi/ticket/4669#comment:4 for more details). */ opal_pmix.fence(NULL, 0); /* check for timing request - get stop time and report elapsed time if so */ OPAL_TIMING_EVENT((&tm,"Finish barrier")); OPAL_TIMING_REPORT(ompi_enable_timing, &tm, "MPI_Finish"); OPAL_TIMING_RELEASE(&tm); /* * Shutdown the Checkpoint/Restart Mech. */ if (OMPI_SUCCESS != (ret = ompi_cr_finalize())) { OMPI_ERROR_LOG(ret); } /* Shut down any bindings-specific issues: C++, F77, F90 */ /* Remove all memory associated by MPI_REGISTER_DATAREP (per MPI-2:9.5.3, there is no way for an MPI application to *un*register datareps, but we don't want the OMPI layer causing memory leaks). */ while (NULL != (item = opal_list_remove_first(&ompi_registered_datareps))) { OBJ_RELEASE(item); } OBJ_DESTRUCT(&ompi_registered_datareps); /* Remove all F90 types from the hash tables. As the OBJ_DESTRUCT will * call a special destructor able to release predefined types, we can * simply call the OBJ_DESTRUCT on the hash table and all memory will * be correctly released. */ OBJ_DESTRUCT( &ompi_mpi_f90_integer_hashtable ); OBJ_DESTRUCT( &ompi_mpi_f90_real_hashtable ); OBJ_DESTRUCT( &ompi_mpi_f90_complex_hashtable ); /* Free communication objects */ /* free file resources */ if (OMPI_SUCCESS != (ret = ompi_file_finalize())) { return ret; } /* free window resources */ if (OMPI_SUCCESS != (ret = ompi_win_finalize())) { return ret; } if (OMPI_SUCCESS != (ret = ompi_osc_base_finalize())) { return ret; } /* free communicator resources. this MUST come before finalizing the PML * as this will call into the pml */ if (OMPI_SUCCESS != (ret = ompi_comm_finalize())) { return ret; } nprocs = 0; procs = ompi_proc_world(&nprocs); MCA_PML_CALL(del_procs(procs, nprocs)); free(procs); /* free pml resource */ if(OMPI_SUCCESS != (ret = mca_pml_base_finalize())) { return ret; } /* free requests */ if (OMPI_SUCCESS != (ret = ompi_request_finalize())) { return ret; } if (OMPI_SUCCESS != (ret = ompi_message_finalize())) { return ret; } /* If requested, print out a list of memory allocated by ALLOC_MEM but not freed by FREE_MEM */ if (0 != ompi_debug_show_mpi_alloc_mem_leaks) { mca_mpool_base_tree_print(ompi_debug_show_mpi_alloc_mem_leaks); } /* Now that all MPI objects dealing with communications are gone, shut down MCA types having to do with communications */ if (OMPI_SUCCESS != (ret = mca_base_framework_close(&ompi_pml_base_framework) ) ) { OMPI_ERROR_LOG(ret); return ret; } /* shut down buffered send code */ mca_pml_base_bsend_fini(); #if OPAL_ENABLE_FT_CR == 1 /* * Shutdown the CRCP Framework, must happen after PML shutdown */ if (OMPI_SUCCESS != (ret = mca_base_framework_close(&ompi_crcp_base_framework) ) ) { OMPI_ERROR_LOG(ret); return ret; } #endif /* Free secondary resources */ /* free attr resources */ if (OMPI_SUCCESS != (ret = ompi_attr_finalize())) { return ret; } /* free group resources */ if (OMPI_SUCCESS != (ret = ompi_group_finalize())) { return ret; } /* free proc resources */ if ( OMPI_SUCCESS != (ret = ompi_proc_finalize())) { return ret; } /* finalize the pubsub functions */ if (OMPI_SUCCESS != (ret = mca_base_framework_close(&ompi_pubsub_base_framework) ) ) { return ret; } /* finalize the DPM framework */ if ( OMPI_SUCCESS != (ret = mca_base_framework_close(&ompi_dpm_base_framework))) { return ret; } /* free internal error resources */ if (OMPI_SUCCESS != (ret = ompi_errcode_intern_finalize())) { return ret; } /* free error code resources */ if (OMPI_SUCCESS != (ret = ompi_mpi_errcode_finalize())) { return ret; } /* free errhandler resources */ if (OMPI_SUCCESS != (ret = ompi_errhandler_finalize())) { return ret; } /* Free all other resources */ /* free op resources */ if (OMPI_SUCCESS != (ret = ompi_op_finalize())) { return ret; } /* free ddt resources */ if (OMPI_SUCCESS != (ret = ompi_datatype_finalize())) { return ret; } /* free info resources */ if (OMPI_SUCCESS != (ret = ompi_info_finalize())) { return ret; } /* Close down MCA modules */ /* io is opened lazily, so it's only necessary to close it if it was actually opened */ if (0 < ompi_io_base_framework.framework_refcnt) { /* May have been "opened" multiple times. We want it closed now */ ompi_io_base_framework.framework_refcnt = 1; if (OMPI_SUCCESS != mca_base_framework_close(&ompi_io_base_framework)) { return ret; } } (void) mca_base_framework_close(&ompi_topo_base_framework); if (OMPI_SUCCESS != (ret = mca_base_framework_close(&ompi_osc_base_framework))) { return ret; } if (OMPI_SUCCESS != (ret = mca_base_framework_close(&ompi_coll_base_framework))) { return ret; } if (OMPI_SUCCESS != (ret = mca_base_framework_close(&ompi_bml_base_framework))) { return ret; } if (OMPI_SUCCESS != (ret = mca_base_framework_close(&opal_mpool_base_framework))) { return ret; } if (OMPI_SUCCESS != (ret = mca_base_framework_close(&opal_rcache_base_framework))) { return ret; } if (OMPI_SUCCESS != (ret = mca_base_framework_close(&opal_allocator_base_framework))) { return ret; } if (NULL != ompi_mpi_main_thread) { OBJ_RELEASE(ompi_mpi_main_thread); ompi_mpi_main_thread = NULL; } /* Leave the RTE */ if (OMPI_SUCCESS != (ret = ompi_rte_finalize())) { return ret; } ompi_rte_initialized = false; /* now close the rte framework */ if (OMPI_SUCCESS != (ret = mca_base_framework_close(&ompi_rte_base_framework) ) ) { OMPI_ERROR_LOG(ret); return ret; } if (OPAL_SUCCESS != (ret = opal_finalize_util())) { return ret; } /* All done */ return MPI_SUCCESS; }