// SPDX-License-Identifier: LGPL-2.1-or-later /** * This file is part of libnvme. */ /** * mi-mctp-csi-test: open a MI connection over MCTP, and send two commands * in parallel with different CSI buffers */ #include #include #include #include #include #include #include #include #include #include #include #include void fhexdump(FILE *fp, const unsigned char *buf, int len) { const int row_len = 16; int i, j; for (i = 0; i < len; i += row_len) { char hbuf[row_len * strlen("00 ") + 1]; char cbuf[row_len + strlen("|") + 1]; for (j = 0; (j < row_len) && ((i+j) < len); j++) { unsigned char c = buf[i + j]; sprintf(hbuf + j * 3, "%02x ", c); if (!isprint(c)) c = '.'; sprintf(cbuf + j, "%c", c); } strcat(cbuf, "|"); fprintf(fp, "%08x %*s |%s\n", i, 0 - (int)sizeof(hbuf) + 1, hbuf, cbuf); } } void hexdump(const unsigned char *buf, int len) { fhexdump(stdout, buf, len); } int do_get_log_page(nvme_mi_ep_t ep, int argc, char **argv) { struct nvme_get_log_args args = { 0 }; struct nvme_mi_ctrl *ctrl; uint8_t buf[4096]; uint16_t ctrl_id; int rc, tmp; if (argc < 2) { fprintf(stderr, "no controller ID specified\n"); return -1; } tmp = atoi(argv[1]); if (tmp < 0 || tmp > 0xffff) { fprintf(stderr, "invalid controller ID\n"); return -1; } ctrl_id = tmp & 0xffff; args.args_size = sizeof(args); args.log = buf; args.len = sizeof(buf); if (argc > 2) { tmp = atoi(argv[2]); args.lid = tmp & 0xff; } else { args.lid = 0x1; } ctrl = nvme_mi_init_ctrl(ep, ctrl_id); if (!ctrl) { warn("can't create controller"); return -1; } rc = nvme_mi_admin_get_log(ctrl, &args); if (rc) { warn("can't perform Get Log page command"); return -1; } printf("Get log page (log id = 0x%02x) data:\n", args.lid); hexdump(buf, args.len); return 0; } struct thread_struct { nvme_mi_ep_t ep; int argc; char **argv; int rc; }; void *csi_thread_helper(void *context) { struct thread_struct *s = (struct thread_struct *) context; s->rc = do_get_log_page(s->ep, s->argc, s->argv); return NULL; } enum action { ACTION_CSI_TEST, }; int do_csi_test(nvme_root_t root, int net, __u8 eid, int argc, char **argv) { int rc = 0; nvme_mi_ep_t ep1, ep2; ep1 = nvme_mi_open_mctp(root, net, eid); if (!ep1) errx(EXIT_FAILURE, "can't open MCTP endpoint %d:%d", net, eid); ep2 = nvme_mi_open_mctp(root, net, eid); if (!ep2) errx(EXIT_FAILURE, "can't open MCTP endpoint %d:%d", net, eid); pthread_t thread; nvme_mi_set_csi(ep1, 0);//Not necessary, but to be explicit nvme_mi_set_csi(ep2, 1); struct thread_struct s; s.ep = ep2; s.argc = argc; s.argv = argv; // Create a new thread to run my_function if (pthread_create(&thread, NULL, csi_thread_helper, &s)) { fprintf(stderr, "Error creating thread\n"); return 1; } rc = do_get_log_page(ep1, argc, argv); // Main thread continues to do other work printf("Main thread finished with rc=%d\n", rc); // Wait for the created thread to finish if (pthread_join(thread, NULL)) { fprintf(stderr, "Error joining thread\n"); return 2; } printf("Second thread finished with rc=%d\n", s.rc); nvme_mi_close(ep1); nvme_mi_close(ep2); if (rc) return rc; if (s.rc) return s.rc; return 0; } static int do_action_endpoint(enum action action, nvme_root_t root, int net, uint8_t eid, int argc, char **argv) { int rc; switch (action) { case ACTION_CSI_TEST: rc = do_csi_test(root, net, eid, argc, argv); break; default: /* This shouldn't be possible, as we should be covering all * of the enum action options above. Hoever, keep the compilers * happy and fail gracefully. */ fprintf(stderr, "invalid action %d?\n", action); rc = -1; } return rc; } int main(int argc, char **argv) { enum action action; nvme_root_t root; bool usage = true; uint8_t eid = 0; int rc = 0, net = 0; if (argc >= 5) { usage = false; net = atoi(argv[1]); eid = atoi(argv[2]) & 0xff; argv += 2; argc -= 2; } if (usage) { fprintf(stderr, "usage: %s [action] [action args]\n", argv[0]); fprintf(stderr, "where action is:\n" " csi-test []\n" "\n" ); return EXIT_FAILURE; } char *action_str = argv[1]; argc--; argv++; if (!strcmp(action_str, "csi-test")) { action = ACTION_CSI_TEST; } else { fprintf(stderr, "invalid action '%s'\n", action_str); return EXIT_FAILURE; } root = nvme_mi_create_root(stderr, DEFAULT_LOGLEVEL); if (!root) err(EXIT_FAILURE, "can't create NVMe root"); rc = do_action_endpoint(action, root, net, eid, argc, argv); nvme_mi_free_root(root); return rc ? EXIT_FAILURE : EXIT_SUCCESS; }