1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2022 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include <linux/ublk_cmd.h> 7 #include <liburing.h> 8 9 #include "spdk/stdinc.h" 10 #include "spdk/string.h" 11 #include "spdk/bdev.h" 12 #include "spdk/endian.h" 13 #include "spdk/env.h" 14 #include "spdk/likely.h" 15 #include "spdk/log.h" 16 #include "spdk/util.h" 17 #include "spdk/queue.h" 18 #include "spdk/json.h" 19 #include "spdk/ublk.h" 20 #include "spdk/thread.h" 21 22 #include "ublk_internal.h" 23 24 #define UBLK_CTRL_DEV "/dev/ublk-control" 25 26 #define UBLK_CTRL_RING_DEPTH 32 27 #define UBLK_THREAD_MAX 128 28 29 static uint32_t g_num_ublk_threads = 0; 30 static struct spdk_cpuset g_core_mask; 31 32 struct ublk_tgt { 33 int ctrl_fd; 34 bool active; 35 bool is_destroying; 36 spdk_ublk_fini_cb cb_fn; 37 void *cb_arg; 38 struct io_uring ctrl_ring; 39 struct spdk_thread *ublk_threads[UBLK_THREAD_MAX]; 40 }; 41 42 static struct ublk_tgt g_ublk_tgt; 43 44 /* helpers for using io_uring */ 45 static inline int 46 ublk_setup_ring(uint32_t depth, struct io_uring *r, unsigned flags) 47 { 48 struct io_uring_params p = {}; 49 50 p.flags = flags | IORING_SETUP_CQSIZE; 51 p.cq_entries = depth; 52 53 return io_uring_queue_init_params(depth, r, &p); 54 } 55 56 void 57 spdk_ublk_init(void) 58 { 59 uint32_t i; 60 61 assert(spdk_get_thread() == spdk_thread_get_app_thread()); 62 63 spdk_cpuset_zero(&g_core_mask); 64 SPDK_ENV_FOREACH_CORE(i) { 65 spdk_cpuset_set_cpu(&g_core_mask, i, true); 66 } 67 } 68 69 static int 70 ublk_open(void) 71 { 72 int rc; 73 74 g_ublk_tgt.ctrl_fd = open(UBLK_CTRL_DEV, O_RDWR); 75 if (g_ublk_tgt.ctrl_fd < 0) { 76 rc = errno; 77 SPDK_ERRLOG("UBLK conrol dev %s can't be opened, error=%s\n", UBLK_CTRL_DEV, spdk_strerror(errno)); 78 return -rc; 79 } 80 81 rc = ublk_setup_ring(UBLK_CTRL_RING_DEPTH, &g_ublk_tgt.ctrl_ring, IORING_SETUP_SQE128); 82 if (rc < 0) { 83 SPDK_ERRLOG("UBLK ctrl queue_init: %s\n", spdk_strerror(-rc)); 84 close(g_ublk_tgt.ctrl_fd); 85 return rc; 86 } 87 88 return 0; 89 } 90 91 static int 92 ublk_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask) 93 { 94 int rc; 95 struct spdk_cpuset tmp_mask; 96 97 if (cpumask == NULL) { 98 return -EPERM; 99 } 100 101 if (mask == NULL) { 102 spdk_cpuset_copy(cpumask, &g_core_mask); 103 return 0; 104 } 105 106 rc = spdk_cpuset_parse(cpumask, mask); 107 if (rc < 0) { 108 SPDK_ERRLOG("invalid cpumask %s\n", mask); 109 return -rc; 110 } 111 112 if (spdk_cpuset_count(cpumask) == 0) { 113 SPDK_ERRLOG("no cpus specified\n"); 114 return -EINVAL; 115 } 116 117 spdk_cpuset_copy(&tmp_mask, cpumask); 118 spdk_cpuset_and(&tmp_mask, &g_core_mask); 119 120 if (!spdk_cpuset_equal(&tmp_mask, cpumask)) { 121 SPDK_ERRLOG("one of selected cpu is outside of core mask(=%s)\n", 122 spdk_cpuset_fmt(&g_core_mask)); 123 return -EINVAL; 124 } 125 126 return 0; 127 } 128 129 int 130 ublk_create_target(const char *cpumask_str) 131 { 132 int rc; 133 uint32_t i; 134 char thread_name[32]; 135 struct spdk_cpuset cpuset = {}; 136 struct spdk_cpuset thd_cpuset = {}; 137 138 if (g_ublk_tgt.active == true) { 139 SPDK_ERRLOG("UBLK target has been created\n"); 140 return -EBUSY; 141 } 142 143 rc = ublk_parse_core_mask(cpumask_str, &cpuset); 144 if (rc != 0) { 145 return rc; 146 } 147 148 rc = ublk_open(); 149 if (rc != 0) { 150 SPDK_ERRLOG("Fail to open UBLK, error=%s\n", spdk_strerror(-rc)); 151 return rc; 152 } 153 154 SPDK_ENV_FOREACH_CORE(i) { 155 if (spdk_cpuset_get_cpu(&cpuset, i)) { 156 spdk_cpuset_zero(&thd_cpuset); 157 spdk_cpuset_set_cpu(&thd_cpuset, i, true); 158 snprintf(thread_name, sizeof(thread_name), "ublk_thread%u", i); 159 g_ublk_tgt.ublk_threads[g_num_ublk_threads] = spdk_thread_create(thread_name, &thd_cpuset); 160 g_num_ublk_threads++; 161 } 162 } 163 g_ublk_tgt.active = true; 164 SPDK_NOTICELOG("UBLK target created successfully\n"); 165 166 return 0; 167 } 168 169 static void 170 _ublk_fini_done(void *args) 171 { 172 g_num_ublk_threads = 0; 173 g_ublk_tgt.is_destroying = false; 174 g_ublk_tgt.active = false; 175 if (g_ublk_tgt.cb_fn) { 176 g_ublk_tgt.cb_fn(g_ublk_tgt.cb_arg); 177 g_ublk_tgt.cb_fn = NULL; 178 g_ublk_tgt.cb_arg = NULL; 179 } 180 } 181 182 static void 183 ublk_thread_exit(void *args) 184 { 185 struct spdk_thread *ublk_thread = spdk_get_thread(); 186 uint32_t i; 187 188 for (i = 0; i < g_num_ublk_threads; i++) { 189 if (g_ublk_tgt.ublk_threads[i] == ublk_thread) { 190 spdk_thread_exit(ublk_thread); 191 } 192 } 193 } 194 195 /* This function will be used and extended in next patch */ 196 static void 197 _ublk_fini(void *args) 198 { 199 spdk_for_each_thread(ublk_thread_exit, NULL, _ublk_fini_done); 200 } 201 202 int 203 spdk_ublk_fini(spdk_ublk_fini_cb cb_fn, void *cb_arg) 204 { 205 assert(spdk_get_thread() == spdk_thread_get_app_thread()); 206 207 if (g_ublk_tgt.is_destroying == true) { 208 /* UBLK target is being destroying */ 209 return -EBUSY; 210 } 211 g_ublk_tgt.cb_fn = cb_fn; 212 g_ublk_tgt.cb_arg = cb_arg; 213 g_ublk_tgt.is_destroying = true; 214 _ublk_fini(NULL); 215 216 return 0; 217 } 218 219 int 220 ublk_destroy_target(spdk_ublk_fini_cb cb_fn, void *cb_arg) 221 { 222 int rc; 223 224 if (g_ublk_tgt.active == false) { 225 /* UBLK target has not been created */ 226 return -ENOENT; 227 } 228 229 rc = spdk_ublk_fini(cb_fn, cb_arg); 230 231 return rc; 232 } 233 234 void 235 spdk_ublk_write_config_json(struct spdk_json_write_ctx *w) 236 { 237 spdk_json_write_array_begin(w); 238 239 spdk_json_write_array_end(w); 240 } 241