static int init_fabric(void) { uint64_t flags = 0; char *node, *service; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (ret) { FT_PRINTERR("fi_getinfo", ret); return ret; } ret = ft_open_fabric_res(); if (ret) return ret; ret = alloc_ep_res(fi); if (ret) return ret; ret = ft_init_ep(); if (ret) return ret; return 0; }
static int init_fabric(void) { uint64_t flags = 0; char *node, *service; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (ret) { FT_PRINTERR("fi_getinfo", ret); return ret; } // we use provider MR attributes and direct address (no offsets) // for RMA calls if (!(fi->mode & FI_PROV_MR_ATTR)) fi->mode |= FI_PROV_MR_ATTR; // get remote address if (opts.dst_addr) { addrlen = fi->dest_addrlen; remote_addr = malloc(addrlen); memcpy(remote_addr, fi->dest_addr, addrlen); } ret = fi_fabric(fi->fabric_attr, &fab, NULL); if (ret) { FT_PRINTERR("fi_fabric", ret); goto err0; } ret = fi_domain(fab, fi, &dom, NULL); if (ret) { FT_PRINTERR("fi_domain", ret); goto err1; } ret = alloc_ep_res(fi); if (ret) goto err3; ret = bind_ep_res(); if (ret) goto err4; return 0; err4: free_ep_res(); err3: fi_close(&dom->fid); err1: fi_close(&fab->fid); err0: return ret; }
static int init_fabric(void) { char *node, *service; uint64_t flags = 0; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; /* Get fabric info */ ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (ret) { FT_PRINTERR("fi_getinfo", ret); return ret; } /* Get remote address of the server */ if (opts.dst_addr) { addrlen = fi->dest_addrlen; remote_addr = malloc(addrlen); memcpy(remote_addr, fi->dest_addr, addrlen); } ret = ft_open_fabric_res(); if (ret) return ret; /* Open domain */ ret = fi_domain(fabric, fi, &domain, NULL); if (ret) { FT_PRINTERR("fi_domain", ret); return ret; } ret = alloc_ep_res(fi); if (ret) return ret; ret = ft_init_ep(NULL); if (ret) return ret; if (opts.dst_addr) { /* Insert address to the AV and get the fabric address back */ ret = fi_av_insert(av, remote_addr, 1, &remote_fi_addr, 0, &fi_ctx_av); if (ret != 1) { FT_PRINTERR("fi_av_insert", ret); return ret; } } return 0; }
static int init_fabric(void) { uint64_t flags = 0; char *node, *service; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (ret) { FT_PRINTERR("fi_getinfo", ret); return ret; } /* Check the number of EPs supported by the provider */ if (ep_cnt > fi->domain_attr->ep_cnt) { ep_cnt = fi->domain_attr->ep_cnt; fprintf(stderr, "Provider can support only %d of EPs\n", ep_cnt); } /* Get remote address */ if (opts.dst_addr) { addrlen = fi->dest_addrlen; remote_addr = malloc(addrlen * ep_cnt); memcpy(remote_addr, fi->dest_addr, addrlen); } ret = ft_open_fabric_res(); if (ret) return ret; ret = fi_domain(fabric, fi, &domain, NULL); if (ret) { FT_PRINTERR("fi_domain", ret); return ret; } fi->ep_attr->tx_ctx_cnt = FI_SHARED_CONTEXT; fi->ep_attr->rx_ctx_cnt = FI_SHARED_CONTEXT; ret = alloc_ep_res(fi); if (ret) return ret; ret = bind_ep_res(); if (ret) return ret; return 0; }
static int init_fabric(void) { uint64_t flags = 0; char *node, *service; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (ret) { FT_PRINTERR("fi_getinfo", ret); return ret; } /* check max msg size */ max_inject_size = fi->tx_attr->inject_size; if ((opts.options & FT_OPT_SIZE) && (opts.transfer_size > max_inject_size)) { fprintf(stderr, "Msg size greater than max inject size\n"); return -FI_EINVAL; } /* Get remote address */ if (opts.dst_addr) { addrlen = fi->dest_addrlen; remote_addr = malloc(addrlen); memcpy(remote_addr, fi->dest_addr, addrlen); } ret = ft_open_fabric_res(); if (ret) return ret; ret = fi_domain(fabric, fi, &domain, NULL); if (ret) { FT_PRINTERR("fi_domain", ret); return ret; } ret = alloc_ep_res(fi); if (ret) return ret; ret = ft_init_ep(NULL); if (ret) return ret; return 0; }
static int init_fabric(void) { uint64_t flags = 0; char *node, *service; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (ret) { FT_PRINTERR("fi_getinfo", ret); return ret; } /* Check the optimal number of TX and RX contexts supported by the provider */ ctx_cnt = MIN(ctx_cnt, fi->domain_attr->tx_ctx_cnt); ctx_cnt = MIN(ctx_cnt, fi->domain_attr->rx_ctx_cnt); if (!ctx_cnt) { fprintf(stderr, "Provider doesn't support contexts\n"); return 1; } fi->ep_attr->tx_ctx_cnt = ctx_cnt; fi->ep_attr->rx_ctx_cnt = ctx_cnt; ret = ft_open_fabric_res(); if (ret) return ret; ret = fi_scalable_ep(domain, fi, &sep, NULL); if (ret) { FT_PRINTERR("fi_scalable_ep", ret); return ret; } ret = alloc_ep_res(sep); if (ret) return ret; ret = bind_ep_res(); return ret; }
static int run(void) { char *node, *service; uint64_t flags; int i, ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; if (!opts.dst_addr) { ret = ft_start_server(); if (ret) return ret; } ret = opts.dst_addr ? client_connect() : server_connect(); if (ret) { return ret; } if (!(opts.options & FT_OPT_SIZE)) { for (i = 0; i < TEST_CNT; i++) { if (!ft_use_size(i, opts.sizes_enabled)) continue; opts.transfer_size = test_size[i].size; init_test(&opts, test_name, sizeof(test_name)); ret = pingpong(); if (ret) goto out; } } else { init_test(&opts, test_name, sizeof(test_name)); ret = pingpong(); if (ret) goto out; } ret = ft_finalize(); out: fi_shutdown(ep, 0); return ret; }
static int init_fabric(void) { uint64_t flags = 0; char *node, *service; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (ret) { FT_PRINTERR("fi_getinfo", ret); return ret; } /* Get remote address */ if (opts.dst_addr) { addrlen = fi->dest_addrlen; remote_addr = malloc(addrlen); memcpy(remote_addr, fi->dest_addr, addrlen); } ret = ft_open_fabric_res(); if (ret) return ret; ret = fi_domain(fabric, fi, &domain, NULL); if (ret) { FT_PRINTERR("fi_domain", ret); return ret; } ret = alloc_ep_res(fi); if (ret) return ret; ret = ft_init_ep(NULL); if (ret) return ret; return 0; }
int ft_getinfo(struct fi_info *hints, struct fi_info **info) { char *node, *service; uint64_t flags = 0; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; if (!hints->ep_attr->type) hints->ep_attr->type = FI_EP_RDM; ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, info); if (ret) { FT_PRINTERR("fi_getinfo", ret); return ret; } return 0; }
static int run(void) { char *node, *service; uint64_t flags; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; if (!opts.src_port) opts.src_port = "9229"; if (!opts.src_addr) { fprintf(stderr, "Source address (-s) is required for this test\n"); return -EXIT_FAILURE; } ret = setup_handle(); if (ret) return ret; if (!opts.dst_addr) { ret = server_listen(); if (ret) return ret; } ret = opts.dst_addr ? client_connect() : server_connect(); if (ret) { return ret; } ret = send_recv(); fi_shutdown(ep, 0); return ret; }
static int init_fabric(void) { char *node, *service; uint64_t flags = 0; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (ret) { FT_PRINTERR("fi_getinfo", ret); return ret; } if (opts.dst_addr) { addrlen = fi->dest_addrlen; remote_addr = malloc(addrlen); memcpy(remote_addr, fi->dest_addr, addrlen); } ret = fi_fabric(fi->fabric_attr, &fab, NULL); if (ret) { FT_PRINTERR("fi_fabric", ret); goto err0; } ret = fi_domain(fab, fi, &dom, NULL); if (ret) { FT_PRINTERR("fi_domain", ret); goto err1; } ret = alloc_ep_res(fi); if (ret) goto err3; ret = bind_ep_res(); if (ret) goto err4; if(opts.dst_addr) { ret = fi_av_insert(av, remote_addr, 1, &remote_fi_addr, 0, &fi_ctx_av); if (ret != 1) { FT_PRINTERR("fi_av_insert", ret); return ret; } } return 0; err4: free_ep_res(); err3: fi_close(&dom->fid); err1: fi_close(&fab->fid); err0: return ret; }
static int init_fabric(void) { uint64_t flags = 0; char *node, *service; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (ret) { FT_PRINTERR("fi_getinfo", ret); return ret; } /* Check the optimal number of TX and RX contexts supported by the provider */ ctx_cnt = MIN(ctx_cnt, fi->domain_attr->tx_ctx_cnt); ctx_cnt = MIN(ctx_cnt, fi->domain_attr->rx_ctx_cnt); if (!ctx_cnt) { fprintf(stderr, "Provider doesn't support contexts\n"); return 1; } /* We use provider MR attributes and direct address (no offsets) for RMA calls */ if (!(fi->mode & FI_PROV_MR_ATTR)) fi->mode |= FI_PROV_MR_ATTR; /* Get remote address */ if (opts.dst_addr) { addrlen = fi->dest_addrlen; remote_addr = malloc(addrlen); memcpy(remote_addr, fi->dest_addr, addrlen); } ret = fi_fabric(fi->fabric_attr, &fab, NULL); if (ret) { FT_PRINTERR("fi_fabric", ret); goto err0; } ret = fi_domain(fab, fi, &dom, NULL); if (ret) { FT_PRINTERR("fi_domain", ret); goto err1; } /* Set the required number of TX and RX context counts */ fi->ep_attr->tx_ctx_cnt = ctx_cnt; fi->ep_attr->rx_ctx_cnt = ctx_cnt; ret = fi_scalable_ep(dom, fi, &sep, NULL); if (ret) { FT_PRINTERR("fi_scalable_ep", ret); goto err2; } ret = alloc_ep_res(sep); if (ret) goto err3; ret = bind_ep_res(); if (ret) goto err4; return 0; err4: free_ep_res(); err3: fi_close(&sep->fid); err2: fi_close(&dom->fid); err1: fi_close(&fab->fid); err0: return ret; }
int main(int argc, char **argv) { char *node, *service; uint64_t flags = 0; int op, ret; opts = INIT_OPTS; hints = fi_allocinfo(); if (!hints) exit(1); hints->caps = FI_MSG; hints->ep_attr->type = FI_EP_MSG; hints->mode = FI_LOCAL_MR; hints->tx_attr->mode = hints->mode; hints->rx_attr->mode = hints->mode; hints->ep_attr->max_msg_size = 100; hints->tx_attr->size = 10; hints->rx_attr->size = 10; while ((op = getopt(argc, argv, "c:C:S:h" ADDR_OPTS INFO_OPTS)) != -1) { switch (op) { case 'c': connections = atoi(optarg); break; case 'C': hints->tx_attr->size = atoi(optarg); hints->rx_attr->size = hints->tx_attr->size; break; case 'S': hints->ep_attr->max_msg_size = atoi(optarg); break; default: ft_parse_addr_opts(op, optarg, &opts); ft_parseinfo(op, optarg, hints); break; case '?': case 'h': usage(argv[0]); } } if (optind < argc) opts.dst_addr = argv[optind]; connects_left = connections; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) goto out; ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (ret) { FT_PRINTERR("fi_getinfo", ret); goto out; } printf("using provider: %s\n", fi->fabric_attr->prov_name); ret = ft_open_fabric_res(); if (ret) goto out; if (alloc_nodes()) goto out; ret = opts.dst_addr ? run_client() : run_server(); printf("test complete\n"); destroy_nodes(); out: ft_free_res(); return -ret; }
int main(int argc, char *argv[]) { uint64_t flags = 0; char *service = NULL; char *node = NULL; struct pingpong_context *ctx; struct timeval start, end; unsigned long size = 4096; // No provider support yet //enum ibv_mtu mtu = IBV_MTU_1024; //size_t mtu = 1024; int rx_depth_default = 500; int rx_depth = 0; int iters = 1000; int use_event = 0; int rcnt, scnt; int ret, rc = 0; char * ptr; srand48(getpid() * time(NULL)); opts = INIT_OPTS; hints = fi_allocinfo(); if (!hints) return 1; while (1) { int c; c = getopt(argc, argv, "S:m:r:n:eh" ADDR_OPTS INFO_OPTS); if (c == -1) break; switch (c) { case 'S': errno = 0; size = strtol(optarg, &ptr, 10); if (ptr == optarg || *ptr != '\0' || ((size == LONG_MIN || size == LONG_MAX) && errno == ERANGE)) { fprintf(stderr, "Cannot convert from string to long\n"); rc = 1; goto err1; } break; // No provider support yet /*case 'm': mtu = strtol(optarg, NULL, 0); mtu = pp_mtu_to_enum(strtol(optarg, NULL, 0)); if (mtu < 0) { usage(argv[0]); return 1; } break; */ case 'r': rx_depth = strtol(optarg, NULL, 0); break; case 'n': iters = strtol(optarg, NULL, 0); break; case 'e': ++use_event; break; default: ft_parse_addr_opts(c, optarg, &opts); ft_parseinfo(c, optarg, hints); break; case '?': case 'h': usage(argv[0]); return 1; } } if (optind == argc - 1) opts.dst_addr = argv[optind]; else if (optind < argc) { usage(argv[0]); return 1; } page_size = sysconf(_SC_PAGESIZE); hints->ep_attr->type = FI_EP_MSG; hints->caps = FI_MSG; hints->mode = FI_LOCAL_MR; rc = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (rc) return -rc; rc = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (rc) { FT_PRINTERR("fi_getinfo", rc); return -rc; } fi_freeinfo(hints); if (rx_depth) { if (rx_depth > fi->rx_attr->size) { fprintf(stderr, "rx_depth requested: %d, " "rx_depth supported: %zd\n", rx_depth, fi->rx_attr->size); rc = 1; goto err1; } } else { rx_depth = (rx_depth_default > fi->rx_attr->size) ? fi->rx_attr->size : rx_depth_default; } ctx = pp_init_ctx(fi, size, rx_depth, use_event); if (!ctx) { rc = 1; goto err1; } if (opts.dst_addr) { /* client connect */ if (pp_connect_ctx(ctx)) { rc = 1; goto err2; } } else { /* server listen and accept */ pp_listen_ctx(ctx); pp_accept_ctx(ctx); } ctx->pending = PINGPONG_RECV_WCID; if (opts.dst_addr) { if (pp_post_send(ctx)) { fprintf(stderr, "Couldn't post send\n"); rc = 1; goto err3; } ctx->pending |= PINGPONG_SEND_WCID; } if (gettimeofday(&start, NULL)) { perror("gettimeofday"); rc = 1; goto err3; } rcnt = scnt = 0; while (rcnt < iters || scnt < iters) { struct fi_cq_entry wc; struct fi_cq_err_entry cq_err; int rd; if (use_event) { /* Blocking read */ rd = fi_cq_sread(ctx->cq, &wc, 1, NULL, -1); } else { do { rd = fi_cq_read(ctx->cq, &wc, 1); } while (rd == -FI_EAGAIN); } if (rd < 0) { fi_cq_readerr(ctx->cq, &cq_err, 0); fprintf(stderr, "cq fi_cq_readerr() %s (%d)\n", fi_cq_strerror(ctx->cq, cq_err.err, cq_err.err_data, NULL, 0), cq_err.err); rc = rd; goto err3; } switch ((int) (uintptr_t) wc.op_context) { case PINGPONG_SEND_WCID: ++scnt; break; case PINGPONG_RECV_WCID: if (--ctx->routs <= 1) { ctx->routs += pp_post_recv(ctx, ctx->rx_depth - ctx->routs); if (ctx->routs < ctx->rx_depth) { fprintf(stderr, "Couldn't post receive (%d)\n", ctx->routs); rc = 1; goto err3; } } ++rcnt; break; default: fprintf(stderr, "Completion for unknown wc_id %d\n", (int) (uintptr_t) wc.op_context); rc = 1; goto err3; } ctx->pending &= ~(int) (uintptr_t) wc.op_context; if (scnt < iters && !ctx->pending) { if (pp_post_send(ctx)) { fprintf(stderr, "Couldn't post send\n"); rc = 1; goto err3; } ctx->pending = PINGPONG_RECV_WCID | PINGPONG_SEND_WCID; } } if (gettimeofday(&end, NULL)) { perror("gettimeofday"); rc = 1; goto err3; } { float usec = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); long long bytes = (long long) size * iters * 2; printf("%lld bytes in %.2f seconds = %.2f Mbit/sec\n", bytes, usec / 1000000., bytes * 8. / usec); printf("%d iters in %.2f seconds = %.2f usec/iter\n", iters, usec / 1000000., usec / iters); } err3: fi_shutdown(ctx->ep, 0); err2: ret = pp_close_ctx(ctx); if (!rc) rc = ret; err1: fi_freeinfo(fi); return rc; }
static int run(void) { char *node, *service; uint64_t flags; int ret; size_t i; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; ret = opts.dst_addr ? client_setup() : server_setup(); if (ret) { fprintf(stderr, "error: %s\n", fi_strerror(-ret)); return ret; } /* Leave extra space for invalid size test */ cm_data = calloc(1, cm_data_size + 1); if (!cm_data) return -FI_ENOMEM; entry = calloc(1, sizeof(*entry) + cm_data_size); if (!entry) return -FI_ENOMEM; if (opts.dst_addr) { ret = ft_sock_connect(opts.dst_addr, sock_service); if (ret) goto err2; } else { ret = ft_sock_listen(sock_service); if (ret) goto err2; ret = ft_sock_accept(); if (ret) goto err1; } for (i = 1; i <= cm_data_size; i <<= 1) { printf("trying with data size: %zu\n", i); if (opts.dst_addr) ret = client(i); else ret = server(i); if (ret) goto err1; ret = ft_sock_sync(0); if (ret) goto err1; } /* Despite server not being setup to handle this, the client should fail * with -FI_EINVAL since this exceeds its max data size. */ if (opts.dst_addr) { printf("trying with data size exceeding maximum: %zu\n", cm_data_size + 1); /* Don't call client since it produces an error message. */ ret = client_connect(cm_data_size + 1); if (ret != -FI_EINVAL) { FT_ERR("expected -FI_EINVAL, got: [%d]:%s\n", ret, fi_strerror(-ret)); } else { ret = FI_SUCCESS; } } err1: ft_sock_shutdown(sock); err2: free(entry); return ret; }
static int init_fabric(void) { char *node, *service; uint64_t flags = 0; int ret; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; /* Get fabric info */ ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &fi); if (ret) { FT_PRINTERR("fi_getinfo", ret); return ret; } /* Get remote address of the server */ if (opts.dst_addr) { addrlen = fi->dest_addrlen; remote_addr = malloc(addrlen); memcpy(remote_addr, fi->dest_addr, addrlen); } /* Open fabric */ ret = fi_fabric(fi->fabric_attr, &fab, NULL); if (ret) { FT_PRINTERR("fi_fabric", ret); goto err1; } /* Open domain */ ret = fi_domain(fab, fi, &dom, NULL); if (ret) { FT_PRINTERR("fi_domain", ret); goto err2; } ret = alloc_ep_res(fi); if (ret) goto err4; ret = bind_ep_res(); if (ret) goto err5; if (opts.dst_addr) { /* Insert address to the AV and get the fabric address back */ ret = fi_av_insert(av, remote_addr, 1, &remote_fi_addr, 0, &fi_ctx_av); if (ret != 1) { FT_PRINTERR("fi_av_insert", ret); return ret; } } return 0; err5: free_ep_res(); err4: fi_close(&dom->fid); err2: fi_close(&fab->fid); err1: return ret; }
int main(int argc, char **argv) { char *node, *service; uint64_t flags = 0; struct fi_eq_attr eq_attr; int op, ret; opts = INIT_OPTS; hints = fi_allocinfo(); if (!hints) exit(1); hints->caps = FI_MSG; hints->ep_attr->type = FI_EP_MSG; hints->mode = FI_LOCAL_MR; hints->tx_attr->mode = hints->mode; hints->rx_attr->mode = hints->mode; hints->ep_attr->max_msg_size = 100; hints->tx_attr->size = 10; hints->rx_attr->size = 10; while ((op = getopt(argc, argv, "c:C:S:h" ADDR_OPTS INFO_OPTS)) != -1) { switch (op) { case 'c': connections = atoi(optarg); break; case 'C': hints->tx_attr->size = atoi(optarg); hints->rx_attr->size = hints->tx_attr->size; break; case 'S': hints->ep_attr->max_msg_size = atoi(optarg); break; default: ft_parse_addr_opts(op, optarg, &opts); ft_parseinfo(op, optarg, hints); break; case '?': case 'h': usage(argv[0]); } } if (optind < argc) opts.dst_addr = argv[optind]; connects_left = connections; ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts); if (ret) return ret; ret = fi_getinfo(FT_FIVERSION, node, service, flags, hints, &info); if (ret) { FT_PRINTERR("fi_getinfo", ret); goto exit0; } printf("using provider: %s\n", info->fabric_attr->prov_name); ret = fi_fabric(info->fabric_attr, &fabric, NULL); if (ret) { FT_PRINTERR("fi_fabric", ret); goto exit1; } memset(&eq_attr, 0, sizeof eq_attr); eq_attr.wait_obj = FI_WAIT_UNSPEC; ret = fi_eq_open(fabric, &eq_attr, &eq, NULL); if (ret) { FT_PRINTERR("fi_eq_open", ret); goto exit2; } if (alloc_nodes()) goto exit3; ret = opts.dst_addr ? run_client() : run_server(); printf("test complete\n"); destroy_nodes(); exit3: fi_close(&eq->fid); exit2: fi_close(&fabric->fid); exit1: fi_freeinfo(info); exit0: fi_freeinfo(hints); return -ret; }