ssize_t psmx_tagged_recv_no_event_av_table(struct fid_ep *ep, void *buf, size_t len, void *desc, fi_addr_t src_addr, uint64_t tag, uint64_t ignore, void *context) { struct psmx_fid_ep *ep_priv; psm_mq_req_t psm_req; uint64_t psm_tag, psm_tagsel; struct fi_context *fi_context; int err; ep_priv = container_of(ep, struct psmx_fid_ep, ep); psm_tag = tag & (~ep_priv->domain->reserved_tag_bits); psm_tagsel = (~ignore) | ep_priv->domain->reserved_tag_bits; fi_context = &ep_priv->nocomp_recv_context; err = psm_mq_irecv(ep_priv->domain->psm_mq, psm_tag, psm_tagsel, 0, /* flags */ buf, len, (void *)fi_context, &psm_req); return psmx_errno(err); }
ssize_t psmx_tagged_recv_no_flag_av_table(struct fid_ep *ep, void *buf, size_t len, void *desc, fi_addr_t src_addr, uint64_t tag, uint64_t ignore, void *context) { struct psmx_fid_ep *ep_priv; psm_mq_req_t psm_req; uint64_t psm_tag, psm_tagsel; struct fi_context *fi_context; int err; ep_priv = container_of(ep, struct psmx_fid_ep, ep); psm_tag = tag & (~ep_priv->domain->reserved_tag_bits); psm_tagsel = (~ignore) | ep_priv->domain->reserved_tag_bits; fi_context = context; PSMX_CTXT_TYPE(fi_context) = PSMX_TRECV_CONTEXT; PSMX_CTXT_USER(fi_context) = buf; PSMX_CTXT_EP(fi_context) = ep_priv; err = psm_mq_irecv(ep_priv->domain->psm_mq, psm_tag, psm_tagsel, 0, /* flags */ buf, len, (void *)fi_context, &psm_req); if (err != PSM_OK) return psmx_errno(err); PSMX_CTXT_REQ(fi_context) = psm_req; return 0; }
ssize_t psmx_tagged_recv_no_flag_av_table(struct fid_ep *ep, void *buf, size_t len, void *desc, fi_addr_t src_addr, uint64_t tag, uint64_t ignore, void *context) { struct psmx_fid_ep *ep_priv; psm_mq_req_t psm_req; uint64_t psm_tag, psm_tagsel; #if (PSM_VERNO_MAJOR >= 2) psm_mq_tag_t psm_tag2, psm_tagsel2; struct psmx_fid_av *av; psm_epaddr_t psm_epaddr; size_t idx; #endif struct fi_context *fi_context; int err; ep_priv = container_of(ep, struct psmx_fid_ep, ep); psm_tag = tag & (~ep_priv->domain->reserved_tag_bits); psm_tagsel = (~ignore) | ep_priv->domain->reserved_tag_bits; fi_context = context; PSMX_CTXT_TYPE(fi_context) = PSMX_TRECV_CONTEXT; PSMX_CTXT_USER(fi_context) = buf; PSMX_CTXT_EP(fi_context) = ep_priv; #if (PSM_VERNO_MAJOR >= 2) if ((ep_priv->caps & FI_DIRECTED_RECV) && src_addr != FI_ADDR_UNSPEC) { av = ep_priv->av; idx = (size_t)src_addr; if (idx >= av->last) return -FI_EINVAL; psm_epaddr = av->psm_epaddrs[idx]; } else { psm_epaddr = NULL; } PSMX_SET_TAG(psm_tag2, psm_tag, 0); PSMX_SET_TAG(psm_tagsel2, psm_tagsel, 0); err = psm_mq_irecv2(ep_priv->domain->psm_mq, psm_epaddr, &psm_tag2, &psm_tagsel2, 0, /* flags */ buf, len, (void *)fi_context, &psm_req); #else err = psm_mq_irecv(ep_priv->domain->psm_mq, psm_tag, psm_tagsel, 0, /* flags */ buf, len, (void *)fi_context, &psm_req); #endif if (err != PSM_OK) return psmx_errno(err); PSMX_CTXT_REQ(fi_context) = psm_req; return 0; }
int ompi_mtl_psm_irecv(struct mca_mtl_base_module_t* mtl, struct ompi_communicator_t *comm, int src, int tag, struct opal_convertor_t *convertor, struct mca_mtl_request_t *mtl_request) { int ret; psm_error_t err; mca_mtl_psm_request_t * mtl_psm_request = (mca_mtl_psm_request_t*) mtl_request; uint64_t mqtag; uint64_t tagsel; size_t length; ret = ompi_mtl_datatype_recv_buf(convertor, &mtl_psm_request->buf, &length, &mtl_psm_request->free_after); if (OMPI_SUCCESS != ret) return ret; mtl_psm_request->length = length; mtl_psm_request->convertor = convertor; mtl_psm_request->type = OMPI_MTL_PSM_IRECV; PSM_MAKE_TAGSEL(src, tag, comm->c_contextid, mqtag, tagsel); #if 0 printf("recv bits: 0x%016llx 0x%016llx\n", mqtag, tagsel); #endif err = psm_mq_irecv(ompi_mtl_psm.mq, mqtag, tagsel, 0, mtl_psm_request->buf, length, mtl_psm_request, &mtl_psm_request->psm_request); if (err) { orte_show_help("help-mtl-psm.txt", "error posting receive", true, psm_error_get_string(err), mtl_psm_request->buf, length); return OMPI_ERROR; } return OMPI_SUCCESS; }
int main(void) { psm_ep_t ep; psm_epid_t epid, remote_epid; psm_mq_t mq; psm_epaddr_t *epaddrs; char *send = (char*)malloc(BUF_SIZE); memset(send, 1, BUF_SIZE); char *recv = (char*)malloc(BUF_SIZE); memset(recv, 1, BUF_SIZE); psm_mq_req_t req; init_ep(&ep, &epid); write_epid((char*)&epid, sizeof(psm_epid_t), SPATH); sleep(5); read_epid((char*)&remote_epid, sizeof(psm_epid_t), CPATH); fprintf(stderr, "connecting..."); connect_endpoints(ep, 1, &remote_epid, &epaddrs); fprintf(stderr, "done\n"); init_mq(&ep, &mq); int len = 128; int repeat; int i ; while (len < BUF_SIZE) { if (len < 8388608) repeat = 1000; if (len < BUF_SIZE) repeat = 10; repeat = 10; for (i = 0; i < repeat; i++) { // fprintf(stderr, "recv: %d\n", len); psm_mq_irecv(mq, 0, 0, 0, recv, len, NULL, &req); psm_mq_wait(&req, NULL); // fprintf(stderr, "send: %d\n", len); psm_mq_send(mq, *epaddrs, 0, 0, send, len); // fprintf(stderr, "recvd: \n"); } len = len << 1; } sleep(100000); return 1; }
int psmx_am_process_rma(struct psmx_fid_domain *domain, struct psmx_am_request *req) { int err; psm_mq_req_t psm_req; if ((req->op & PSMX_AM_OP_MASK) == PSMX_AM_REQ_WRITE_LONG) { err = psm_mq_irecv(domain->psm_mq, (uint64_t)req->write.context, -1ULL, 0, (void *)req->write.addr, req->write.len, (void *)&req->fi_context, &psm_req); } else { err = psm_mq_isend(domain->psm_mq, (psm_epaddr_t)req->read.peer_addr, 0, (uint64_t)req->read.context, (void *)req->read.addr, req->read.len, (void *)&req->fi_context, &psm_req); } return psmx_errno(err); }
ssize_t psmx_tagged_recv_no_event_av_map(struct fid_ep *ep, void *buf, size_t len, void *desc, fi_addr_t src_addr, uint64_t tag, uint64_t ignore, void *context) { struct psmx_fid_ep *ep_priv; psm_mq_req_t psm_req; uint64_t psm_tag, psm_tagsel; #if (PSM_VERNO_MAJOR >= 2) psm_mq_tag_t psm_tag2, psm_tagsel2; #endif struct fi_context *fi_context; int err; ep_priv = container_of(ep, struct psmx_fid_ep, ep); psm_tag = tag & (~ep_priv->domain->reserved_tag_bits); psm_tagsel = (~ignore) | ep_priv->domain->reserved_tag_bits; fi_context = &ep_priv->nocomp_recv_context; #if (PSM_VERNO_MAJOR >= 2) if (! ((ep_priv->caps & FI_DIRECTED_RECV) && src_addr != FI_ADDR_UNSPEC)) src_addr = 0; PSMX_SET_TAG(psm_tag2, psm_tag, 0); PSMX_SET_TAG(psm_tagsel2, psm_tagsel, 0); err = psm_mq_irecv2(ep_priv->domain->psm_mq, (psm_epaddr_t)src_addr, &psm_tag2, &psm_tagsel2, 0, /* flags */ buf, len, (void *)fi_context, &psm_req); #else err = psm_mq_irecv(ep_priv->domain->psm_mq, psm_tag, psm_tagsel, 0, /* flags */ buf, len, (void *)fi_context, &psm_req); #endif return psmx_errno(err); }
static int pspsm_recvlook(pspsm_con_info_t *con_info) { /* ToDo: rename me to something like "post a receive". */ psm_error_t ret; uint64_t rtag = con_info->recv_id; void *context = (void *)((uintptr_t)con_info | 2); assert(con_info->rreq == PSM_MQ_REQINVALID); ret = psm_mq_irecv(pspsm_mq, rtag, mask, 0 /*flags*/, con_info->rbuf, con_info->rbuflen, context, &con_info->rreq); if (ret != PSM_OK) goto out_err; /* FIXME: Should probably not return an error code to indicate success. */ return -EAGAIN; out_err: pspsm_err(psm_error_get_string(ret)); pspsm_dprint(1, "pspsm_recvlook: %s", pspsm_err_str); return -1; }
int psmx_eq_poll_mq(struct psmx_fid_eq *eq, struct psmx_fid_domain *domain_if_null_eq) { psm_mq_req_t psm_req; psm_mq_status_t psm_status; struct fi_context *fi_context; struct psmx_fid_domain *domain; struct psmx_fid_ep *tmp_ep; struct psmx_fid_eq *tmp_eq; struct psmx_fid_cntr *tmp_cntr; struct psmx_event *event; int multi_recv; int err; if (eq) domain = eq->domain; else domain = domain_if_null_eq; while (1) { err = psm_mq_ipeek(domain->psm_mq, &psm_req, NULL); if (err == PSM_OK) { err = psm_mq_test(&psm_req, &psm_status); fi_context = psm_status.context; tmp_ep = PSMX_CTXT_EP(fi_context); tmp_eq = NULL; tmp_cntr = NULL; multi_recv = 0; switch (PSMX_CTXT_TYPE(fi_context)) { case PSMX_NOCOMP_SEND_CONTEXT: tmp_ep->pending_sends--; if (!tmp_ep->send_cntr_event_flag) tmp_cntr = tmp_ep->send_cntr; break; case PSMX_NOCOMP_RECV_CONTEXT: if (!tmp_ep->recv_cntr_event_flag) tmp_cntr = tmp_ep->recv_cntr; break; case PSMX_NOCOMP_WRITE_CONTEXT: tmp_ep->pending_writes--; if (!tmp_ep->write_cntr_event_flag) tmp_cntr = tmp_ep->write_cntr; break; case PSMX_NOCOMP_READ_CONTEXT: tmp_ep->pending_reads--; if (!tmp_ep->read_cntr_event_flag) tmp_cntr = tmp_ep->read_cntr; break; case PSMX_INJECT_CONTEXT: tmp_ep->pending_sends--; if (!tmp_ep->send_cntr_event_flag) tmp_cntr = tmp_ep->send_cntr; free(fi_context); break; case PSMX_INJECT_WRITE_CONTEXT: tmp_ep->pending_writes--; if (!tmp_ep->write_cntr_event_flag) tmp_cntr = tmp_ep->write_cntr; free(fi_context); break; case PSMX_SEND_CONTEXT: tmp_ep->pending_sends--; tmp_eq = tmp_ep->send_eq; tmp_cntr = tmp_ep->send_cntr; break; case PSMX_RECV_CONTEXT: tmp_eq = tmp_ep->recv_eq; tmp_cntr = tmp_ep->recv_cntr; break; case PSMX_MULTI_RECV_CONTEXT: multi_recv = 1; tmp_eq = tmp_ep->recv_eq; tmp_cntr = tmp_ep->recv_cntr; break; case PSMX_READ_CONTEXT: tmp_ep->pending_reads--; tmp_eq = tmp_ep->send_eq; tmp_cntr = tmp_ep->read_cntr; break; case PSMX_WRITE_CONTEXT: tmp_ep->pending_writes--; tmp_eq = tmp_ep->send_eq; tmp_cntr = tmp_ep->write_cntr; break; } if (tmp_eq) { event = psmx_eq_create_event_from_status(tmp_eq, &psm_status); if (!event) return -ENOMEM; psmx_eq_enqueue_event(tmp_eq, event); } if (tmp_cntr) tmp_cntr->cntr.ops->add(&tmp_cntr->cntr, 1); if (multi_recv) { struct psmx_multi_recv *req; psm_mq_req_t psm_req; req = PSMX_CTXT_USER(fi_context); req->offset += psm_status.nbytes; if (req->offset + req->min_buf_size <= req->len) { err = psm_mq_irecv(tmp_ep->domain->psm_mq, req->tag, req->tagsel, req->flag, req->buf + req->offset, req->len - req->offset, (void *)fi_context, &psm_req); if (err != PSM_OK) return psmx_errno(err); PSMX_CTXT_REQ(fi_context) = psm_req; } else { if (tmp_eq) { event = psmx_eq_create_event( tmp_eq, req->context, req->buf, FI_MULTI_RECV, req->len, req->len - req->offset, /* data */ 0, /* tag */ 0, /* olen */ 0); /* err */ if (!event) return -ENOMEM; psmx_eq_enqueue_event(tmp_eq, event); } free(req); } } if (!eq || tmp_eq == eq) return 1; } else if (err == PSM_MQ_NO_COMPLETIONS) { return 0; } else { return psmx_errno(err); } } }
ssize_t _psmx_readfrom(struct fid_ep *ep, void *buf, size_t len, void *desc, fi_addr_t src_addr, uint64_t addr, uint64_t key, void *context, uint64_t flags) { struct psmx_fid_ep *ep_priv; struct psmx_fid_av *av; struct psmx_epaddr_context *epaddr_context; struct psmx_am_request *req; psm_amarg_t args[8]; int err; int chunk_size; size_t offset = 0; uint64_t psm_tag; psm_mq_req_t psm_req; size_t idx; if (flags & FI_TRIGGER) { struct psmx_trigger *trigger; struct fi_triggered_context *ctxt = context; trigger = calloc(1, sizeof(*trigger)); if (!trigger) return -ENOMEM; trigger->op = PSMX_TRIGGERED_READ; trigger->cntr = container_of(ctxt->threshold.cntr, struct psmx_fid_cntr, cntr); trigger->threshold = ctxt->threshold.threshold; trigger->read.ep = ep; trigger->read.buf = buf; trigger->read.len = len; trigger->read.desc = desc; trigger->read.src_addr = src_addr; trigger->read.addr = addr; trigger->read.key = key; trigger->read.context = context; trigger->read.flags = flags & ~FI_TRIGGER; psmx_cntr_add_trigger(trigger->cntr, trigger); return 0; } ep_priv = container_of(ep, struct psmx_fid_ep, ep); assert(ep_priv->domain); if (!buf) return -EINVAL; av = ep_priv->av; if (av && av->type == FI_AV_TABLE) { idx = src_addr; if (idx >= av->last) return -EINVAL; src_addr = (fi_addr_t) av->psm_epaddrs[idx]; } else if (!src_addr) { return -EINVAL; } epaddr_context = psm_epaddr_getctxt((void *)src_addr); if (epaddr_context->epid == ep_priv->domain->psm_epid) return psmx_rma_self(PSMX_AM_REQ_READ, ep_priv, buf, len, desc, addr, key, context, flags, 0); req = calloc(1, sizeof(*req)); if (!req) return -ENOMEM; req->op = PSMX_AM_REQ_READ; req->read.buf = buf; req->read.len = len; req->read.addr = addr; /* needed? */ req->read.key = key; /* needed? */ req->read.context = context; req->ep = ep_priv; PSMX_CTXT_TYPE(&req->fi_context) = PSMX_READ_CONTEXT; PSMX_CTXT_USER(&req->fi_context) = context; if (ep_priv->send_cq_event_flag && !(flags & FI_EVENT)) { PSMX_CTXT_TYPE(&req->fi_context) = PSMX_NOCOMP_READ_CONTEXT; req->no_event = 1; } chunk_size = MIN(PSMX_AM_CHUNK_SIZE, psmx_am_param.max_reply_short); if (psmx_env.tagged_rma && len > chunk_size) { psm_tag = PSMX_RMA_BIT | ep_priv->domain->psm_epid; err = psm_mq_irecv(ep_priv->domain->psm_mq, psm_tag, -1ULL, 0, buf, len, (void *)&req->fi_context, &psm_req); args[0].u32w0 = PSMX_AM_REQ_READ_LONG; args[0].u32w1 = len; args[1].u64 = (uint64_t)req; args[2].u64 = addr; args[3].u64 = key; args[4].u64 = psm_tag; err = psm_am_request_short((psm_epaddr_t) src_addr, PSMX_AM_RMA_HANDLER, args, 5, NULL, 0, PSM_AM_FLAG_NOREPLY, NULL, NULL); return 0; } args[0].u32w0 = PSMX_AM_REQ_READ; args[1].u64 = (uint64_t)(uintptr_t)req; args[3].u64 = key; while (len > chunk_size) { args[0].u32w1 = chunk_size; args[2].u64 = addr; args[4].u64 = offset; err = psm_am_request_short((psm_epaddr_t) src_addr, PSMX_AM_RMA_HANDLER, args, 5, NULL, 0, 0, NULL, NULL); addr += chunk_size; len -= chunk_size; offset += chunk_size; } args[0].u32w0 = PSMX_AM_REQ_READ | PSMX_AM_EOM; args[0].u32w1 = len; args[2].u64 = addr; args[4].u64 = offset; err = psm_am_request_short((psm_epaddr_t) src_addr, PSMX_AM_RMA_HANDLER, args, 5, NULL, 0, 0, NULL, NULL); return 0; }
ssize_t _psmx_tagged_recv(struct fid_ep *ep, void *buf, size_t len, void *desc, fi_addr_t src_addr, uint64_t tag, uint64_t ignore, void *context, uint64_t flags) { struct psmx_fid_ep *ep_priv; psm_mq_req_t psm_req; uint64_t psm_tag, psm_tagsel; #if (PSM_VERNO_MAJOR >= 2) psm_mq_tag_t psm_tag2, psm_tagsel2; struct psmx_fid_av *av; size_t idx; #endif struct fi_context *fi_context; int err; ep_priv = container_of(ep, struct psmx_fid_ep, ep); if (flags & FI_PEEK) return _psmx_tagged_peek(ep, buf, len, desc, src_addr, tag, ignore, context, flags); if (flags & FI_TRIGGER) { struct psmx_trigger *trigger; struct fi_triggered_context *ctxt = context; trigger = calloc(1, sizeof(*trigger)); if (!trigger) return -FI_ENOMEM; trigger->op = PSMX_TRIGGERED_TRECV; trigger->cntr = container_of(ctxt->trigger.threshold.cntr, struct psmx_fid_cntr, cntr); trigger->threshold = ctxt->trigger.threshold.threshold; trigger->trecv.ep = ep; trigger->trecv.buf = buf; trigger->trecv.len = len; trigger->trecv.desc = desc; trigger->trecv.src_addr = src_addr; trigger->trecv.tag = tag; trigger->trecv.ignore = ignore; trigger->trecv.context = context; trigger->trecv.flags = flags & ~FI_TRIGGER; psmx_cntr_add_trigger(trigger->cntr, trigger); return 0; } #if (PSM_VERNO_MAJOR >= 2) if (flags & FI_CLAIM) { if (!context) return -FI_EINVAL; /* TODO: handle FI_DISCARD */ fi_context = context; psm_req = PSMX_CTXT_REQ(fi_context); PSMX_CTXT_TYPE(fi_context) = PSMX_TRECV_CONTEXT; PSMX_CTXT_USER(fi_context) = buf; PSMX_CTXT_EP(fi_context) = ep_priv; err = psm_mq_imrecv(ep_priv->domain->psm_mq, 0, /*flags*/ buf, len, context, &psm_req); if (err != PSM_OK) return psmx_errno(err); PSMX_CTXT_REQ(fi_context) = psm_req; return 0; } #endif if (tag & ep_priv->domain->reserved_tag_bits) { FI_WARN(&psmx_prov, FI_LOG_EP_DATA, "using reserved tag bits." "tag=%lx. reserved_bits=%lx.\n", tag, ep_priv->domain->reserved_tag_bits); } psm_tag = tag & (~ep_priv->domain->reserved_tag_bits); psm_tagsel = (~ignore) | ep_priv->domain->reserved_tag_bits; if (ep_priv->recv_selective_completion && !(flags & FI_COMPLETION) && !context) { fi_context = &ep_priv->nocomp_recv_context; } else { if (!context) return -FI_EINVAL; fi_context = context; PSMX_CTXT_TYPE(fi_context) = PSMX_TRECV_CONTEXT; PSMX_CTXT_USER(fi_context) = buf; PSMX_CTXT_EP(fi_context) = ep_priv; } #if (PSM_VERNO_MAJOR >= 2) if ((ep_priv->caps & FI_DIRECTED_RECV) && src_addr != FI_ADDR_UNSPEC) { av = ep_priv->av; if (av && av->type == FI_AV_TABLE) { idx = (size_t)src_addr; if (idx >= av->last) return -FI_EINVAL; src_addr = (fi_addr_t)av->psm_epaddrs[idx]; } } else { src_addr = 0; } PSMX_SET_TAG(psm_tag2, psm_tag, 0); PSMX_SET_TAG(psm_tagsel2, psm_tagsel, 0); err = psm_mq_irecv2(ep_priv->domain->psm_mq, (psm_epaddr_t)src_addr, &psm_tag2, &psm_tagsel2, 0, /* flags */ buf, len, (void *)fi_context, &psm_req); #else err = psm_mq_irecv(ep_priv->domain->psm_mq, psm_tag, psm_tagsel, 0, /* flags */ buf, len, (void *)fi_context, &psm_req); #endif if (err != PSM_OK) return psmx_errno(err); if (fi_context == context) PSMX_CTXT_REQ(fi_context) = psm_req; return 0; }
ssize_t _psmx_tagged_recv(struct fid_ep *ep, void *buf, size_t len, void *desc, fi_addr_t src_addr, uint64_t tag, uint64_t ignore, void *context, uint64_t flags) { struct psmx_fid_ep *ep_priv; psm_mq_req_t psm_req; uint64_t psm_tag, psm_tagsel; struct fi_context *fi_context; int err; ep_priv = container_of(ep, struct psmx_fid_ep, ep); if (flags & FI_PEEK) return _psmx_tagged_peek(ep, buf, len, desc, src_addr, tag, ignore, context, flags); if (flags & FI_TRIGGER) { struct psmx_trigger *trigger; struct fi_triggered_context *ctxt = context; trigger = calloc(1, sizeof(*trigger)); if (!trigger) return -FI_ENOMEM; trigger->op = PSMX_TRIGGERED_TRECV; trigger->cntr = container_of(ctxt->trigger.threshold.cntr, struct psmx_fid_cntr, cntr); trigger->threshold = ctxt->trigger.threshold.threshold; trigger->trecv.ep = ep; trigger->trecv.buf = buf; trigger->trecv.len = len; trigger->trecv.desc = desc; trigger->trecv.src_addr = src_addr; trigger->trecv.tag = tag; trigger->trecv.ignore = ignore; trigger->trecv.context = context; trigger->trecv.flags = flags & ~FI_TRIGGER; psmx_cntr_add_trigger(trigger->cntr, trigger); return 0; } if (tag & ep_priv->domain->reserved_tag_bits) { FI_WARN(&psmx_prov, FI_LOG_EP_DATA, "using reserved tag bits." "tag=%lx. reserved_bits=%lx.\n", tag, ep_priv->domain->reserved_tag_bits); } psm_tag = tag & (~ep_priv->domain->reserved_tag_bits); psm_tagsel = (~ignore) | ep_priv->domain->reserved_tag_bits; if (ep_priv->recv_selective_completion && !(flags & FI_COMPLETION) && !context) { fi_context = &ep_priv->nocomp_recv_context; } else { if (!context) return -FI_EINVAL; fi_context = context; PSMX_CTXT_TYPE(fi_context) = PSMX_TRECV_CONTEXT; PSMX_CTXT_USER(fi_context) = buf; PSMX_CTXT_EP(fi_context) = ep_priv; } err = psm_mq_irecv(ep_priv->domain->psm_mq, psm_tag, psm_tagsel, 0, /* flags */ buf, len, (void *)fi_context, &psm_req); if (err != PSM_OK) return psmx_errno(err); if (fi_context == context) PSMX_CTXT_REQ(fi_context) = psm_req; return 0; }