struct xio_session *sd_xio_gw_create_session(struct xio_context *ctx, const struct node_id *nid, void *user_ctx) { struct xio_session *session; char url[256]; struct xio_session_params params; if (nid->io_transport_type == IO_TRANSPORT_TYPE_RDMA) snprintf(url, 256, "rdma://%s", addr_to_str(nid->io_addr, nid->io_port)); else snprintf(url, 256, "tcp://%s", addr_to_str(nid->io_addr, nid->io_port)); memset(¶ms, 0, sizeof(params)); params.type = XIO_SESSION_CLIENT; params.ses_ops = &gw_client_ses_ops; params.uri = url; params.user_context = user_ctx; session = xio_session_create(¶ms); return session; }
/*---------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { struct xio_session *session; char url[256]; struct session_data session_data; int i = 0; struct xio_session_params params; if (argc < 3) { printf("Usage: %s <host> <port> <transport:optional>\n", argv[0]); exit(1); } memset(&session_data, 0, sizeof(session_data)); memset(¶ms, 0, sizeof(params)); /* initialize library */ xio_init(); /* create thread context for the client */ session_data.ctx = xio_context_create(NULL, 0, -1); /* create url to connect to */ if (argc > 3) sprintf(url, "%s://%s:%s", argv[3], argv[1], argv[2]); else sprintf(url, "rdma://%s:%s", argv[1], argv[2]); params.type = XIO_SESSION_CLIENT; params.ses_ops = &ses_ops; params.user_context = &session_data; params.uri = url; session = xio_session_create(¶ms); /* connect the session */ session_data.conn = xio_connect(session, session_data.ctx, 0, NULL, &session_data); /* create "hello world" message */ for (i = 0; i < QUEUE_DEPTH; i++) { memset(&session_data.req[i], 0, sizeof(session_data.req[i])); /* header */ session_data.req[i].out.header.iov_base = strdup("hello world header request"); session_data.req[i].out.header.iov_len = strlen(session_data.req[i].out.header.iov_base) + 1; /* iovec[0]*/ session_data.req[i].in.sgl_type = XIO_SGL_TYPE_IOV; session_data.req[i].in.data_iov.max_nents = XIO_IOVLEN; session_data.req[i].out.sgl_type = XIO_SGL_TYPE_IOV; session_data.req[i].out.data_iov.max_nents = XIO_IOVLEN; session_data.req[i].out.data_iov.sglist[0].iov_base = strdup("hello world data request"); session_data.req[i].out.data_iov.sglist[0].iov_len = strlen(session_data.req[i].out.data_iov.sglist[0].iov_base) + 1; session_data.req[i].out.data_iov.nents = 1; } /* send first message */ for (i = 0; i < QUEUE_DEPTH; i++) { xio_send_request(session_data.conn, &session_data.req[i]); session_data.nsent++; } /* event dispatcher is now running */ xio_context_run_loop(session_data.ctx, XIO_INFINITE); /* normal exit phase */ fprintf(stdout, "exit signaled\n"); /* free the message */ for (i = 0; i < QUEUE_DEPTH; i++) { free(session_data.req[i].out.header.iov_base); free(session_data.req[i].out.data_iov.sglist[0].iov_base); } /* free the context */ xio_context_destroy(session_data.ctx); xio_shutdown(); printf("good bye\n"); return 0; }
/*---------------------------------------------------------------------------*/ int main(int argc, char *const argv[]) { struct xio_session *session; char url[256]; int i; struct sigaction sa; int c; char *addr = NULL; char *port = NULL; char *trans = NULL; struct xio_session_params params; struct xio_connection_params cparams; while (1) { c = getopt_long(argc, argv, "a:p:r:hdnV", longopts, NULL); if (c == -1) break; switch (c) { case 'a': addr = optarg; break; case 'p': port = optarg; break; case 'r': trans = optarg; break; case 'h': usage(argv[0], 0); case 'd': debug_flag++; nofork_flag++; break; case 'n': nofork_flag++; break; case 'V': printf("%s\n", PACKAGE_STRING); exit(0); break; default: usage(argv[0], 1); break; } } memset(&sa, 0, sizeof(sa)); sa.sa_handler = signal_handler; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sa.sa_handler = SIG_IGN; sa.sa_flags = SA_RESTART; sigaction(SIGPIPE, &sa, NULL); if (!nofork_flag && daemon(0, 0)) { logerr("daemon() failed"); exit(1); } if (!debug_flag) { openlog("xioclntd", LOG_PID, LOG_DAEMON); use_syslog = 1; } /* Create the process PID file */ if (create_pidfile(pid_file) != 0) exit(EXIT_FAILURE); memset(&session_data, 0, sizeof(session_data)); memset(¶ms, 0, sizeof(params)); memset(&cparams, 0, sizeof(cparams)); /* initialize library */ xio_init(); /* create "hello world" message */ for (i = 0; i < QUEUE_DEPTH; i++) { memset(&session_data.req[i], 0, sizeof(session_data.req[i])); /* header */ session_data.req[i].out.header.iov_base = strdup("hello world header request"); session_data.req[i].out.header.iov_len = strlen((const char *) session_data.req[i].out.header.iov_base) + 1; /* iovec[0]*/ session_data.req[i].out.sgl_type = XIO_SGL_TYPE_IOV; session_data.req[i].out.data_iov.max_nents = XIO_IOVLEN; session_data.req[i].out.data_iov.sglist[0].iov_base = strdup("hello world iovec request"); session_data.req[i].out.data_iov.sglist[0].iov_len = strlen((const char *) session_data.req[i].out.data_iov.sglist[0].iov_base) + 1; session_data.req[i].out.data_iov.nents = 1; } /* create thread context for the client */ session_data.ctx = xio_context_create(NULL, 0, -1); /* create url to connect to */ if (trans) sprintf(url, "%s://%s:%s", trans, addr, port); else sprintf(url, "rdma://%s:%s", addr, port); params.type = XIO_SESSION_CLIENT; params.ses_ops = &ses_ops; params.user_context = &session_data; params.uri = url; reconnect: session = xio_session_create(¶ms); cparams.session = session; cparams.ctx = session_data.ctx; cparams.conn_user_context = &session_data; /* connect the session */ session_data.conn = xio_connect(&cparams); /* event dispatcher is now running */ xio_context_run_loop(session_data.ctx, XIO_INFINITE); if (reconnect_flag || reload_flag) { session_data.cnt = 0; if (reconnect_flag) sleep(1); reload_flag = 0; reconnect_flag = 0; goto reconnect; } /* normal exit phase */ logit(LOG_INFO, "exit signaled\n"); /* free the message */ for (i = 0; i < QUEUE_DEPTH; i++) { free(session_data.req[i].out.header.iov_base); free(session_data.req[i].out.data_iov.sglist[0].iov_base); } /* free the context */ xio_context_destroy(session_data.ctx); xio_shutdown(); remove_pidfile(); if (use_syslog) closelog(); return 0; }
/*---------------------------------------------------------------------------*/ int run_client_test(struct perf_parameters *user_param) { struct session_data sess_data; struct perf_comm *comm; struct thread_data *tdata; char url[256]; int i = 0; int max_cpus; pthread_t statistics_thread_id; struct perf_command command; int size_log2; int max_size_log2 = 24; /* client session attributes */ struct xio_session_attr attr = { &ses_ops, NULL, 0 }; xio_init(); g_mhz = get_cpu_mhz(0); max_cpus = sysconf(_SC_NPROCESSORS_ONLN); threads_iter = 1; size_log2 = 0; tdata = calloc(user_param->threads_num, sizeof(*tdata)); if (tdata == NULL) { fprintf(fd, "malloc failed\n"); return -1; } comm = create_comm_struct(user_param); if (establish_connection(comm)) { fprintf(stderr, "failed to establish connection\n"); free(tdata); destroy_comm_struct(comm); return -1; } if (user_param->output_file) { fd = fopen(user_param->output_file, "w"); if (fd == NULL) { fprintf(fd, "file open failed. %s\n", user_param->output_file); free(sess_data.tdata); destroy_comm_struct(comm); return -1; } fprintf(fd, "size, threads, tps, bw[Mbps], lat[usec]\n"); fflush(fd); } printf("%s", RESULT_FMT); printf("%s", RESULT_LINE); while (threads_iter <= user_param->threads_num) { data_len = (uint64_t)1 << size_log2; memset(&sess_data, 0, sizeof(sess_data)); memset(tdata, 0, user_param->threads_num*sizeof(*tdata)); sess_data.tdata = tdata; command.test_param.machine_type = user_param->machine_type; command.test_param.test_type = user_param->test_type; command.test_param.verb = user_param->verb; command.test_param.data_len = data_len; command.command = GetTestParams; ctx_write_data(comm, &command, sizeof(command)); sprintf(url, "rdma://%s:%d", user_param->server_addr, user_param->server_port); sess_data.session = xio_session_create(XIO_SESSION_CLIENT, &attr, url, 0, 0, &sess_data); if (sess_data.session == NULL) { int error = xio_errno(); fprintf(stderr, "session creation failed. reason %d - (%s)\n", error, xio_strerror(error)); goto cleanup; } pthread_create(&statistics_thread_id, NULL, statistics_thread_cb, &sess_data); /* spawn threads to handle connection */ for (i = 0; i < threads_iter; i++) { sess_data.tdata[i].affinity = ((user_param->cpu + i) % max_cpus); sess_data.tdata[i].cid = i; sess_data.tdata[i].sdata = &sess_data; sess_data.tdata[i].user_param = user_param; sess_data.tdata[i].data_len = data_len; /* all threads are working on the same session */ sess_data.tdata[i].session = sess_data.session; pthread_create(&sess_data.tdata[i].thread_id, NULL, worker_thread, &sess_data.tdata[i]); } pthread_join(statistics_thread_id, NULL); /* join the threads */ for (i = 0; i < threads_iter; i++) pthread_join(sess_data.tdata[i].thread_id, NULL); /* close the session */ xio_session_destroy(sess_data.session); if (sess_data.abort) { fprintf(stderr, "program aborted\n"); goto cleanup; } /* send result to server */ command.results.bytes = data_len; command.results.threads = threads_iter; command.results.tps = sess_data.tps; command.results.avg_bw = sess_data.avg_bw; command.results.avg_lat = sess_data.avg_lat_us; command.results.min_lat = sess_data.min_lat_us; command.results.max_lat = sess_data.max_lat_us; command.command = GetTestResults; /* sync point */ ctx_write_data(comm, &command, sizeof(command)); printf(REPORT_FMT, data_len, threads_iter, sess_data.tps, sess_data.avg_bw, sess_data.avg_lat_us, sess_data.min_lat_us, sess_data.max_lat_us); if (fd) fprintf(fd, "%lu, %d, %lu, %.2lf, %.2lf\n", data_len, threads_iter, sess_data.tps, sess_data.avg_bw, sess_data.avg_lat_us); fflush(fd); /* sync point */ ctx_read_data(comm, NULL, 0, NULL); if (++size_log2 < max_size_log2) continue; threads_iter++; size_log2 = 0; } printf("%s", RESULT_LINE); cleanup: if (fd) fclose(fd); ctx_hand_shake(comm); ctx_close_connection(comm); destroy_comm_struct(comm); free(tdata); xio_shutdown(); return 0; }
/*---------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { struct xio_session *session; char url[256]; struct session_data session_data; int i = 0; /* client session attributes */ struct xio_session_attr attr = { &ses_ops, /* callbacks structure */ NULL, /* no need to pass the server private data */ 0 }; memset(&session_data, 0, sizeof(session_data)); /* initialize library */ xio_init(); /* create thread context for the client */ session_data.ctx = xio_context_create(NULL, 0); /* create url to connect to */ sprintf(url, "rdma://%s:%s", argv[1], argv[2]); session = xio_session_create(XIO_SESSION_CLIENT, &attr, url, 0, 0, &session_data); /* connect the session */ session_data.conn = xio_connect(session, session_data.ctx, 0, NULL, &session_data); /* create "hello world" message */ for (i = 0; i < QUEUE_DEPTH; i++) { memset(&session_data.req[i], 0, sizeof(session_data.req[i])); session_data.req[i].out.header.iov_base = strdup("hello world header request"); session_data.req[i].out.header.iov_len = strlen(session_data.req[i].out.header.iov_base) + 1; } /* send first message */ for (i = 0; i < QUEUE_DEPTH; i++) { xio_send_request(session_data.conn, &session_data.req[i]); session_data.nsent++; } /* event dispatcher is now running */ xio_context_run_loop(session_data.ctx, XIO_INFINITE); /* normal exit phase */ fprintf(stdout, "exit signaled\n"); /* free the message */ for (i = 0; i < QUEUE_DEPTH; i++) free(session_data.req[i].out.header.iov_base); /* free the context */ xio_context_destroy(session_data.ctx); xio_shutdown(); printf("good bye\n"); return 0; }
/*---------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { struct xio_session *session; char url[256]; struct session_data session_data; int i = 0; struct event timeout; struct event xio_event; struct timeval tv; struct xio_poll_params poll_params; struct xio_session_params params; if (argc < 3) { printf("Usage: %s <host> <port> <transport:optional>\n", argv[0]); exit(1); } memset(&session_data, 0, sizeof(session_data)); memset(¶ms, 0, sizeof(params)); /* initialize library */ xio_init(); /* create thread context for the client */ session_data.ctx = xio_context_create(NULL, 0, -1); /* get poll parameters for libevent */ xio_context_get_poll_params(session_data.ctx, &poll_params); /* create url to connect to */ if (argc > 3) sprintf(url, "%s://%s:%s", argv[3], argv[1], argv[2]); else sprintf(url, "rdma://%s:%s", argv[1], argv[2]); params.type = XIO_SESSION_CLIENT; params.ses_ops = &ses_ops; params.user_context = &session_data; params.uri = url; session = xio_session_create(¶ms); /* connect the session */ session_data.conn = xio_connect(session, session_data.ctx, 0, NULL, &session_data); /* create "hello world" message */ for (i = 0; i < QUEUE_DEPTH; i++) { memset(&session_data.req[i], 0, sizeof(session_data.req[i])); /* header */ session_data.req[i].out.header.iov_base = strdup("hello world header request"); session_data.req[i].out.header.iov_len = strlen(session_data.req[i].out.header.iov_base) + 1; /* iovec[0]*/ session_data.req[i].out.sgl_type = XIO_SGL_TYPE_IOV; session_data.req[i].out.data_iov.max_nents = XIO_IOVLEN; session_data.req[i].out.data_iov.sglist[0].iov_base = strdup("hello world iovec request"); session_data.req[i].out.data_iov.sglist[0].iov_len = strlen(session_data.req[i].out.data_iov.sglist[0].iov_base) + 1; session_data.req[i].out.data_iov.nents = 1; } /* send first message */ for (i = 0; i < QUEUE_DEPTH; i++) { xio_send_request(session_data.conn, &session_data.req[i]); session_data.nsent++; } /* Initialize the event library */ session_data.evbase = event_base_new(); /* Initialize one timer event */ event_assign(&timeout, session_data.evbase, -1, EV_PERSIST, timeout_cb, (void *)&timeout); evutil_timerclear(&tv); tv.tv_sec = 2; event_add(&timeout, &tv); event_assign(&xio_event, session_data.evbase, poll_params.fd, EV_READ|EV_PERSIST, xio_event_handler, (void *)&poll_params); /* Add it to the active events, without a timeout */ event_add(&xio_event, NULL); event_base_dispatch(session_data.evbase); fprintf(stdout, "exit signaled\n"); event_base_free(session_data.evbase); /* free the message */ for (i = 0; i < QUEUE_DEPTH; i++) { free(session_data.req[i].out.header.iov_base); free(session_data.req[i].out.data_iov.sglist[0].iov_base); } /* free the context */ xio_context_destroy(session_data.ctx); xio_shutdown(); printf("good bye\n"); return 0; }
/*---------------------------------------------------------------------------*/ static int xio_client_main(void *data) { char url[256]; struct xio_session_params params; struct xio_context_params ctx_params; struct xio_connection_params cparams; int error; int retval = 0; atomic_add(2, &module_state); print_counter = PRINT_COUNTER; print_test_config(&test_config); memset(¶ms, 0, sizeof(params)); memset(&cparams, 0, sizeof(cparams)); /* prepare buffers for this test */ if (msg_api_init(&msg_params, test_config.hdr_len, test_config.data_len, 0) != 0) { pr_err("msg_api_init failed\n"); return -1; } pool = msg_pool_alloc(MAX_POOL_SIZE, 1, 1); if (!pool) { pr_err("msg_pool_alloc failed\n"); goto cleanup; } /* create thread context for the server */ memset(&ctx_params, 0, sizeof(ctx_params)); ctx_params.flags = XIO_LOOP_GIVEN_THREAD; ctx_params.worker = current; ctx = xio_context_create(&ctx_params, POLLING_TIMEOUT, cpu); if (!ctx) { pr_err("context open failed\n"); goto cleanup; } sprintf(url, "%s://%s:%d", test_config.transport, test_config.server_addr, test_config.server_port); params.type = XIO_SESSION_CLIENT; params.ses_ops = &ses_ops; params.uri = url; g_session = xio_session_create(¶ms); if (!g_session) pr_err("session creation failed\n"); cparams.session = g_session; cparams.ctx = ctx; cparams.conn_idx = test_config.conn_idx; /* connect the session */ g_connection = xio_connect(&cparams); /* the default xio supplied main loop */ if (atomic_add_unless(&module_state, 4, 0x83)) retval = xio_context_run_loop(ctx); atomic_sub(4, &module_state); if (retval != 0) { error = xio_errno(); pr_err("running event loop failed. reason %d - (%s)\n", error, xio_strerror(error)); xio_assert(retval == 0); } /* normal exit phase */ pr_info("exit signaled\n"); xio_context_destroy(ctx); msg_pool_free(pool); cleanup: msg_api_free(&msg_params); pr_info("exit complete\n"); complete_and_exit(&cleanup_complete, 0); return 0; }
/* first message after new connection are going trough the server */ static int xio_on_new_message(struct xio_server *server, struct xio_nexus *nexus, int event, union xio_nexus_event_data *event_data) { struct xio_session *session = NULL; struct xio_connection *connection = NULL; struct xio_connection *connection1 = NULL; struct xio_task *task; uint32_t tlv_type; struct xio_session_params params; int locked = 0; if (!server || !nexus || !event_data || !event_data->msg.task) { ERROR_LOG("server [new session]: failed " \ "invalid parameter\n"); return -1; } task = event_data->msg.task; params.type = XIO_SESSION_SERVER; params.initial_sn = 0; params.ses_ops = &server->ops; params.uri = server->uri; params.private_data = NULL; params.private_data_len = 0; params.user_context = server->cb_private_data; /* read the first message type */ tlv_type = xio_read_tlv_type(&event_data->msg.task->mbuf); if (tlv_type == XIO_SESSION_SETUP_REQ) { /* create new session */ session = xio_session_create(¶ms); if (!session) { ERROR_LOG("server [new session]: failed " \ " allocating session failed\n"); return -1; } DEBUG_LOG("server [new session]: server:%p, " \ "session:%p, nexus:%p ,session_id:%d\n", server, session, nexus, session->session_id); /* get transport class routines */ session->validators_cls = xio_nexus_get_validators_cls(nexus); connection = xio_session_alloc_connection(session, server->ctx, 0, server->cb_private_data); if (!connection) { ERROR_LOG("server failed to allocate new connection\n"); goto cleanup; } connection1 = xio_session_assign_nexus(session, nexus); if (!connection1) { ERROR_LOG("server failed to assign new connection\n"); goto cleanup1; } connection = connection1; xio_idr_add_uobj(usr_idr, session, "xio_session"); xio_idr_add_uobj(usr_idr, connection, "xio_connection"); xio_connection_set_state(connection, XIO_CONNECTION_STATE_ONLINE); xio_connection_keepalive_start(connection); task->session = session; task->connection = connection; } else if (tlv_type == XIO_CONNECTION_HELLO_REQ) { struct xio_session *session1; /* find the old session without lock */ session = xio_find_session(event_data->msg.task); if (!session) { ERROR_LOG("server [new connection]: failed " \ "session not found. server:%p\n", server); xio_nexus_close(nexus, NULL); return -1; } /* lock it and retry find */ mutex_lock(&session->lock); /* session before destruction - try to lock before continue */ session1 = xio_find_session(event_data->msg.task); if (!session1) { ERROR_LOG("server [new connection]: failed " \ "session not found. server:%p\n", server); xio_nexus_close(nexus, NULL); mutex_unlock(&session->lock); return -1; } locked = 1; task->session = session; DEBUG_LOG("server [new connection]: server:%p, " \ "session:%p, nexus:%p, session_id:%d\n", server, session, nexus, session->session_id); connection = xio_session_alloc_connection( task->session, server->ctx, 0, server->cb_private_data); if (!connection) { ERROR_LOG("server failed to allocate new connection\n"); goto cleanup; } connection1 = xio_session_assign_nexus(task->session, nexus); if (!connection1) { ERROR_LOG("server failed to assign new connection\n"); goto cleanup1; } connection = connection1; /* copy the server attributes to the connection */ xio_connection_set_ops(connection, &server->ops); task->connection = connection; /* This in a multiple-portal situation */ session->state = XIO_SESSION_STATE_ONLINE; xio_connection_set_state(connection, XIO_CONNECTION_STATE_ONLINE); xio_connection_keepalive_start(connection); xio_idr_add_uobj(usr_idr, connection, "xio_connection"); } else { ERROR_LOG("server unexpected message\n"); return -1; } /* route the message to the session */ if (session) xio_nexus_notify_observer(nexus, &session->observer, event, event_data); if (locked) mutex_unlock(&session->lock); return 0; cleanup1: if (connection) xio_session_free_connection(connection); cleanup: if (session) xio_session_destroy(session); return -1; }
/*---------------------------------------------------------------------------*/ static int xio_client_main(void *data) { char **argv = (char **)data; struct xio_session *session; struct xio_session_params params; struct xio_context_params ctx_params; struct xio_connection_params cparams; char url[256]; struct xio_context *ctx; struct session_data *session_data; int i = 0; atomic_add(2, &module_state); session_data = vzalloc(sizeof(*session_data)); if (!session_data) { /*pr_err("session_data alloc failed\n");*/ return 0; } /* create thread context for the client */ memset(&ctx_params, 0, sizeof(ctx_params)); ctx_params.flags = XIO_LOOP_GIVEN_THREAD; ctx_params.worker = current; ctx = xio_context_create(&ctx_params, 0, -1); if (!ctx) { vfree(session_data); pr_err("context open filed\n"); return 0; } session_data->ctx = ctx; /* create url to connect to */ sprintf(url, "rdma://%s:%s", argv[1], argv[2]); memset(¶ms, 0, sizeof(params)); params.type = XIO_SESSION_CLIENT; params.ses_ops = &ses_ops; params.user_context = session_data; params.uri = url; session = xio_session_create(¶ms); memset(&cparams, 0, sizeof(cparams)); cparams.session = session; cparams.ctx = ctx; cparams.conn_user_context = session_data; /* connect the session */ session_data->session = session; session_data->connection = xio_connect(&cparams); /* create "hello world" message */ for (i = 0; i < QUEUE_DEPTH; i++) { struct xio_vmsg *omsg; void *buf; omsg = &session_data->req[i].out; memset(&session_data->req[i], 0, sizeof(session_data->req[i])); /* header */ buf = kstrdup("hello world header request", GFP_KERNEL); session_data->req[i].out.header.iov_base = buf; session_data->req[i].out.header.iov_len = strlen(buf) + 1; /* iovec[0]*/ omsg->sgl_type = XIO_SGL_TYPE_SCATTERLIST; sg_alloc_table(&omsg->data_tbl, SG_TBL_LEN, GFP_KERNEL); /* currently only one entry */ buf = kstrdup("hello world iovec request", GFP_KERNEL); sg_init_one(omsg->data_tbl.sgl, buf, strlen(buf) + 1); /* orig_nents is XIO_IOVLEN */ omsg->data_tbl.nents = 1; } /* send first message */ for (i = 0; i < QUEUE_DEPTH; i++) xio_send_request(session_data->connection, &session_data->req[i]); g_session_data = session_data; /* the default xio supplied main loop */ if (atomic_add_unless(&module_state, 4, 0x83)) xio_context_run_loop(ctx); atomic_sub(4, &module_state); /* normal exit phase */ pr_info("exit signaled\n"); /* free the message */ for (i = 0; i < QUEUE_DEPTH; i++) { kfree(session_data->req[i].out.header.iov_base); /* Currently need to release only one entry */ kfree(sg_virt(session_data->req[i].out.data_tbl.sgl)); sg_free_table(&session_data->req[i].out.data_tbl); } /* free the context */ xio_context_destroy(ctx); vfree(session_data); pr_info("good bye\n"); complete_and_exit(&cleanup_complete, 0); return 0; }