xref: /spdk/lib/ublk/ublk.c (revision a1944e0170c73bf7b3bf8c48d392fb344b5dd057)
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