xref: /spdk/test/unit/lib/blobfs/blobfs_sync_ut/blobfs_sync_ut.c (revision 7f6068f04d1fc927f3c09c7b091a77d737f12966)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 
36 #include "spdk/blobfs.h"
37 #include "spdk/env.h"
38 #include "spdk/log.h"
39 #include "spdk/thread.h"
40 #include "spdk/barrier.h"
41 
42 #include "spdk_cunit.h"
43 #include "unit/lib/blob/bs_dev_common.c"
44 #include "common/lib/test_env.c"
45 #include "blobfs/blobfs.c"
46 #include "blobfs/tree.c"
47 
48 struct spdk_filesystem *g_fs;
49 struct spdk_file *g_file;
50 int g_fserrno;
51 
52 /* Return NULL to test hardcoded defaults. */
53 struct spdk_conf_section *
54 spdk_conf_find_section(struct spdk_conf *cp, const char *name)
55 {
56 	return NULL;
57 }
58 
59 /* Return -1 to test hardcoded defaults. */
60 int
61 spdk_conf_section_get_intval(struct spdk_conf_section *sp, const char *key)
62 {
63 	return -1;
64 }
65 
66 static void
67 _fs_send_msg(spdk_thread_fn fn, void *ctx, void *thread_ctx)
68 {
69 	fn(ctx);
70 }
71 
72 struct ut_request {
73 	fs_request_fn fn;
74 	void *arg;
75 	volatile int done;
76 	int from_ut;
77 };
78 
79 static struct ut_request *g_req = NULL;
80 static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
81 
82 static void
83 send_request(fs_request_fn fn, void *arg)
84 {
85 	struct ut_request *req;
86 
87 	req = calloc(1, sizeof(*req));
88 	assert(req != NULL);
89 	req->fn = fn;
90 	req->arg = arg;
91 	req->done = 0;
92 	req->from_ut = 0;
93 
94 	pthread_mutex_lock(&g_mutex);
95 	g_req = req;
96 	pthread_mutex_unlock(&g_mutex);
97 }
98 
99 static void
100 ut_send_request(fs_request_fn fn, void *arg)
101 {
102 	struct ut_request req;
103 
104 
105 	req.fn = fn;
106 	req.arg = arg;
107 	req.done = 0;
108 	req.from_ut = 1;
109 
110 	pthread_mutex_lock(&g_mutex);
111 	g_req = &req;
112 	pthread_mutex_unlock(&g_mutex);
113 
114 	while (1) {
115 		pthread_mutex_lock(&g_mutex);
116 		if (req.done == 1) {
117 			pthread_mutex_unlock(&g_mutex);
118 			break;
119 		}
120 		pthread_mutex_unlock(&g_mutex);
121 	}
122 
123 	/*
124 	 * Make sure the address of the local req variable is not in g_req when we exit this
125 	 * function to make static analysis tools happy.
126 	 */
127 	g_req = NULL;
128 }
129 
130 static void
131 fs_op_complete(void *ctx, int fserrno)
132 {
133 	g_fserrno = fserrno;
134 }
135 
136 static void
137 fs_op_with_handle_complete(void *ctx, struct spdk_filesystem *fs, int fserrno)
138 {
139 	g_fs = fs;
140 	g_fserrno = fserrno;
141 }
142 
143 static void
144 _fs_init(void *arg)
145 {
146 	struct spdk_bs_dev *dev;
147 
148 	g_fs = NULL;
149 	g_fserrno = -1;
150 	dev = init_dev();
151 	spdk_fs_init(dev, NULL, send_request, fs_op_with_handle_complete, NULL);
152 	SPDK_CU_ASSERT_FATAL(g_fs != NULL);
153 	CU_ASSERT(g_fserrno == 0);
154 }
155 
156 static void
157 _fs_unload(void *arg)
158 {
159 	g_fserrno = -1;
160 	spdk_fs_unload(g_fs, fs_op_complete, NULL);
161 	CU_ASSERT(g_fserrno == 0);
162 	g_fs = NULL;
163 }
164 
165 static void
166 cache_write(void)
167 {
168 	uint64_t length;
169 	int rc;
170 	char buf[100];
171 	struct spdk_io_channel *channel;
172 
173 	ut_send_request(_fs_init, NULL);
174 
175 	channel = spdk_fs_alloc_io_channel_sync(g_fs);
176 
177 	rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file);
178 	CU_ASSERT(rc == 0);
179 	SPDK_CU_ASSERT_FATAL(g_file != NULL);
180 
181 	length = (4 * 1024 * 1024);
182 	rc = spdk_file_truncate(g_file, channel, length);
183 	CU_ASSERT(rc == 0);
184 
185 	spdk_file_write(g_file, channel, buf, 0, sizeof(buf));
186 
187 	CU_ASSERT(spdk_file_get_length(g_file) == length);
188 
189 	rc = spdk_file_truncate(g_file, channel, sizeof(buf));
190 	CU_ASSERT(rc == 0);
191 
192 	spdk_file_close(g_file, channel);
193 	rc = spdk_fs_delete_file(g_fs, channel, "testfile");
194 	CU_ASSERT(rc == 0);
195 
196 	rc = spdk_fs_delete_file(g_fs, channel, "testfile");
197 	CU_ASSERT(rc == -ENOENT);
198 
199 	spdk_fs_free_io_channel(channel);
200 
201 	ut_send_request(_fs_unload, NULL);
202 }
203 
204 static void
205 cache_write_null_buffer(void)
206 {
207 	uint64_t length;
208 	int rc;
209 	struct spdk_io_channel *channel;
210 
211 	ut_send_request(_fs_init, NULL);
212 
213 	channel = spdk_fs_alloc_io_channel_sync(g_fs);
214 
215 	rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file);
216 	CU_ASSERT(rc == 0);
217 	SPDK_CU_ASSERT_FATAL(g_file != NULL);
218 
219 	length = 0;
220 	rc = spdk_file_truncate(g_file, channel, length);
221 	CU_ASSERT(rc == 0);
222 
223 	rc = spdk_file_write(g_file, channel, NULL, 0, 0);
224 	CU_ASSERT(rc == 0);
225 
226 	spdk_file_close(g_file, channel);
227 	rc = spdk_fs_delete_file(g_fs, channel, "testfile");
228 	CU_ASSERT(rc == 0);
229 
230 	spdk_fs_free_io_channel(channel);
231 
232 	ut_send_request(_fs_unload, NULL);
233 }
234 
235 static void
236 fs_create_sync(void)
237 {
238 	int rc;
239 	struct spdk_io_channel *channel;
240 
241 	ut_send_request(_fs_init, NULL);
242 
243 	channel = spdk_fs_alloc_io_channel_sync(g_fs);
244 	CU_ASSERT(channel != NULL);
245 
246 	rc = spdk_fs_create_file(g_fs, channel, "testfile");
247 	CU_ASSERT(rc == 0);
248 
249 	/* Create should fail, because the file already exists. */
250 	rc = spdk_fs_create_file(g_fs, channel, "testfile");
251 	CU_ASSERT(rc != 0);
252 
253 	rc = spdk_fs_delete_file(g_fs, channel, "testfile");
254 	CU_ASSERT(rc == 0);
255 
256 	spdk_fs_free_io_channel(channel);
257 
258 	ut_send_request(_fs_unload, NULL);
259 }
260 
261 static void
262 cache_append_no_cache(void)
263 {
264 	int rc;
265 	char buf[100];
266 	struct spdk_io_channel *channel;
267 
268 	ut_send_request(_fs_init, NULL);
269 
270 	channel = spdk_fs_alloc_io_channel_sync(g_fs);
271 
272 	rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file);
273 	CU_ASSERT(rc == 0);
274 	SPDK_CU_ASSERT_FATAL(g_file != NULL);
275 
276 	spdk_file_write(g_file, channel, buf, 0 * sizeof(buf), sizeof(buf));
277 	CU_ASSERT(spdk_file_get_length(g_file) == 1 * sizeof(buf));
278 	spdk_file_write(g_file, channel, buf, 1 * sizeof(buf), sizeof(buf));
279 	CU_ASSERT(spdk_file_get_length(g_file) == 2 * sizeof(buf));
280 	spdk_file_sync(g_file, channel);
281 	cache_free_buffers(g_file);
282 	spdk_file_write(g_file, channel, buf, 2 * sizeof(buf), sizeof(buf));
283 	CU_ASSERT(spdk_file_get_length(g_file) == 3 * sizeof(buf));
284 	spdk_file_write(g_file, channel, buf, 3 * sizeof(buf), sizeof(buf));
285 	CU_ASSERT(spdk_file_get_length(g_file) == 4 * sizeof(buf));
286 	spdk_file_write(g_file, channel, buf, 4 * sizeof(buf), sizeof(buf));
287 	CU_ASSERT(spdk_file_get_length(g_file) == 5 * sizeof(buf));
288 
289 	spdk_file_close(g_file, channel);
290 	rc = spdk_fs_delete_file(g_fs, channel, "testfile");
291 	CU_ASSERT(rc == 0);
292 
293 	spdk_fs_free_io_channel(channel);
294 
295 	ut_send_request(_fs_unload, NULL);
296 }
297 
298 static void
299 fs_delete_file_without_close(void)
300 {
301 	int rc;
302 	struct spdk_io_channel *channel;
303 	struct spdk_file *file;
304 
305 	ut_send_request(_fs_init, NULL);
306 	channel = spdk_fs_alloc_io_channel_sync(g_fs);
307 	CU_ASSERT(channel != NULL);
308 
309 	rc = spdk_fs_open_file(g_fs, channel, "testfile", SPDK_BLOBFS_OPEN_CREATE, &g_file);
310 	CU_ASSERT(rc == 0);
311 	SPDK_CU_ASSERT_FATAL(g_file != NULL);
312 
313 	rc = spdk_fs_delete_file(g_fs, channel, "testfile");
314 	CU_ASSERT(rc == 0);
315 	CU_ASSERT(g_file->ref_count != 0);
316 	CU_ASSERT(g_file->is_deleted == true);
317 
318 	rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &file);
319 	CU_ASSERT(rc != 0);
320 
321 	spdk_file_close(g_file, channel);
322 
323 	rc = spdk_fs_open_file(g_fs, channel, "testfile", 0, &file);
324 	CU_ASSERT(rc != 0);
325 
326 	spdk_fs_free_io_channel(channel);
327 
328 	ut_send_request(_fs_unload, NULL);
329 
330 }
331 
332 static void
333 terminate_spdk_thread(void *arg)
334 {
335 	spdk_free_thread();
336 	pthread_exit(NULL);
337 }
338 
339 static void *
340 spdk_thread(void *arg)
341 {
342 	struct ut_request *req;
343 
344 	spdk_allocate_thread(_fs_send_msg, NULL, NULL, NULL, "thread1");
345 
346 	while (1) {
347 		pthread_mutex_lock(&g_mutex);
348 		if (g_req != NULL) {
349 			req = g_req;
350 			req->fn(req->arg);
351 			req->done = 1;
352 			if (!req->from_ut) {
353 				free(req);
354 			}
355 			g_req = NULL;
356 		}
357 		pthread_mutex_unlock(&g_mutex);
358 	}
359 
360 	spdk_free_thread();
361 
362 	return NULL;
363 }
364 
365 int main(int argc, char **argv)
366 {
367 	CU_pSuite	suite = NULL;
368 	pthread_t	spdk_tid;
369 	unsigned int	num_failures;
370 
371 	if (CU_initialize_registry() != CUE_SUCCESS) {
372 		return CU_get_error();
373 	}
374 
375 	suite = CU_add_suite("blobfs_sync_ut", NULL, NULL);
376 	if (suite == NULL) {
377 		CU_cleanup_registry();
378 		return CU_get_error();
379 	}
380 
381 	if (
382 		CU_add_test(suite, "write", cache_write) == NULL ||
383 		CU_add_test(suite, "write_null_buffer", cache_write_null_buffer) == NULL ||
384 		CU_add_test(suite, "create_sync", fs_create_sync) == NULL ||
385 		CU_add_test(suite, "append_no_cache", cache_append_no_cache) == NULL ||
386 		CU_add_test(suite, "delete_file_without_close", fs_delete_file_without_close) == NULL
387 	) {
388 		CU_cleanup_registry();
389 		return CU_get_error();
390 	}
391 
392 	spdk_allocate_thread(_fs_send_msg, NULL, NULL, NULL, "thread0");
393 
394 	pthread_create(&spdk_tid, NULL, spdk_thread, NULL);
395 	g_dev_buffer = calloc(1, DEV_BUFFER_SIZE);
396 	CU_basic_set_mode(CU_BRM_VERBOSE);
397 	CU_basic_run_tests();
398 	num_failures = CU_get_number_of_failures();
399 	CU_cleanup_registry();
400 	free(g_dev_buffer);
401 	send_request(terminate_spdk_thread, NULL);
402 	pthread_join(spdk_tid, NULL);
403 	spdk_free_thread();
404 	return num_failures;
405 }
406