1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 Chelsio Communications, Inc. 5 * Written by: John Baldwin <jhb@FreeBSD.org> 6 */ 7 8 #include <sys/param.h> 9 #include <sys/linker.h> 10 #include <sys/nv.h> 11 #include <sys/time.h> 12 #include <err.h> 13 #include <errno.h> 14 #include <fcntl.h> 15 #include <libnvmf.h> 16 #include <string.h> 17 18 #include <cam/ctl/ctl.h> 19 #include <cam/ctl/ctl_io.h> 20 #include <cam/ctl/ctl_ioctl.h> 21 22 #include "internal.h" 23 24 static int ctl_fd = -1; 25 static int ctl_port; 26 27 static void 28 open_ctl(void) 29 { 30 if (ctl_fd > 0) 31 return; 32 33 ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); 34 if (ctl_fd == -1 && errno == ENOENT) { 35 if (kldload("ctl") == -1) 36 err(1, "Failed to load ctl.ko"); 37 ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); 38 } 39 if (ctl_fd == -1) 40 err(1, "Failed to open %s", CTL_DEFAULT_DEV); 41 } 42 43 void 44 init_ctl_port(const char *subnqn, const struct nvmf_association_params *params) 45 { 46 char result_buf[256]; 47 struct ctl_port_entry entry; 48 struct ctl_req req; 49 nvlist_t *nvl; 50 51 open_ctl(); 52 53 nvl = nvlist_create(0); 54 55 nvlist_add_string(nvl, "subnqn", subnqn); 56 57 /* XXX: Hardcoded in discovery.c */ 58 nvlist_add_stringf(nvl, "portid", "%u", 1); 59 60 nvlist_add_stringf(nvl, "max_io_qsize", "%u", params->max_io_qsize); 61 62 memset(&req, 0, sizeof(req)); 63 strlcpy(req.driver, "nvmf", sizeof(req.driver)); 64 req.reqtype = CTL_REQ_CREATE; 65 req.args = nvlist_pack(nvl, &req.args_len); 66 if (req.args == NULL) 67 errx(1, "Failed to pack nvlist for CTL_PORT/CTL_REQ_CREATE"); 68 req.result = result_buf; 69 req.result_len = sizeof(result_buf); 70 if (ioctl(ctl_fd, CTL_PORT_REQ, &req) != 0) 71 err(1, "ioctl(CTL_PORT/CTL_REQ_CREATE)"); 72 if (req.status == CTL_LUN_ERROR) 73 errx(1, "Failed to create CTL port: %s", req.error_str); 74 if (req.status != CTL_LUN_OK) 75 errx(1, "Failed to create CTL port: %d", req.status); 76 77 nvlist_destroy(nvl); 78 nvl = nvlist_unpack(result_buf, req.result_len, 0); 79 if (nvl == NULL) 80 errx(1, "Failed to unpack nvlist from CTL_PORT/CTL_REQ_CREATE"); 81 82 ctl_port = nvlist_get_number(nvl, "port_id"); 83 nvlist_destroy(nvl); 84 85 memset(&entry, 0, sizeof(entry)); 86 entry.targ_port = ctl_port; 87 if (ioctl(ctl_fd, CTL_ENABLE_PORT, &entry) != 0) 88 errx(1, "ioctl(CTL_ENABLE_PORT)"); 89 } 90 91 void 92 shutdown_ctl_port(const char *subnqn) 93 { 94 struct ctl_req req; 95 nvlist_t *nvl; 96 97 open_ctl(); 98 99 nvl = nvlist_create(0); 100 101 nvlist_add_string(nvl, "subnqn", subnqn); 102 103 memset(&req, 0, sizeof(req)); 104 strlcpy(req.driver, "nvmf", sizeof(req.driver)); 105 req.reqtype = CTL_REQ_REMOVE; 106 req.args = nvlist_pack(nvl, &req.args_len); 107 if (req.args == NULL) 108 errx(1, "Failed to pack nvlist for CTL_PORT/CTL_REQ_REMOVE"); 109 if (ioctl(ctl_fd, CTL_PORT_REQ, &req) != 0) 110 err(1, "ioctl(CTL_PORT/CTL_REQ_REMOVE)"); 111 if (req.status == CTL_LUN_ERROR) 112 errx(1, "Failed to remove CTL port: %s", req.error_str); 113 if (req.status != CTL_LUN_OK) 114 errx(1, "Failed to remove CTL port: %d", req.status); 115 116 nvlist_destroy(nvl); 117 } 118 119 void 120 ctl_handoff_qpair(struct nvmf_qpair *qp, 121 const struct nvmf_fabric_connect_cmd *cmd, 122 const struct nvmf_fabric_connect_data *data) 123 { 124 struct ctl_nvmf req; 125 int error; 126 127 memset(&req, 0, sizeof(req)); 128 req.type = CTL_NVMF_HANDOFF; 129 error = nvmf_handoff_controller_qpair(qp, &req.data.handoff); 130 if (error != 0) { 131 warnc(error, "Failed to prepare qpair for handoff"); 132 return; 133 } 134 135 req.data.handoff.cmd = cmd; 136 req.data.handoff.data = data; 137 if (ioctl(ctl_fd, CTL_NVMF, &req) != 0) 138 warn("ioctl(CTL_NVMF/CTL_NVMF_HANDOFF)"); 139 } 140