4 * Copyright (C) 2007 - 2008 Vladislav Bolkhovitin <vst@vlnb.net>
5 * Copyright (C) 2007 - 2008 CMS Distribution Limited
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, version 2
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
30 #include <sys/types.h>
33 #include <sys/ioctl.h>
41 #if defined(DEBUG) || defined(TRACING)
44 /*#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF \
46 #define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF & \
47 ~TRACE_SCSI & ~TRACE_SCSI_SERIALIZING & ~TRACE_DEBUG)
49 #define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
50 TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_MINOR | \
51 TRACE_MGMT_DEBUG | TRACE_TIME)
53 #define TRACE_SN(args...) TRACE(TRACE_SCSI_SERIALIZING, args)
58 #define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_MGMT | \
59 TRACE_TIME | TRACE_SPECIAL)
61 #define DEFAULT_LOG_FLAGS 0
65 unsigned long trace_flag = DEFAULT_LOG_FLAGS;
66 #endif /* defined(DEBUG) || defined(TRACING) */
68 #define DEF_BLOCK_SHIFT 9
69 #define VERSION_STR "1.0.1"
76 static struct option const long_options[] =
78 {"block", required_argument, 0, 'b'},
79 {"threads", required_argument, 0, 'e'},
80 {"write_through", no_argument, 0, 't'},
81 {"read_only", no_argument, 0, 'r'},
82 {"direct", no_argument, 0, 'o'},
83 {"nullio", no_argument, 0, 'n'},
84 {"nv_cache", no_argument, 0, 'c'},
85 {"parse", required_argument, 0, 'p'},
86 {"on_free", required_argument, 0, 'f'},
87 {"mem_reuse", required_argument, 0, 'm'},
88 {"non_blocking", no_argument, 0, 'l'},
89 {"vdisk_id", required_argument, 0, 'I'},
90 {"flush", required_argument, 0, 'F'},
91 {"unreg_before_close", no_argument, 0, 'u'},
92 #if defined(DEBUG) || defined(TRACING)
93 {"debug", required_argument, 0, 'd'},
95 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
96 {"debug_tm_ignore", no_argument, 0, 'g'},
98 {"version", no_argument, 0, 'v'},
99 {"help", no_argument, 0, 'h'},
103 static void usage(void)
105 printf("Usage: %s [OPTION] name path\n", app_name);
106 printf("\nFILEIO disk target emulator for SCST\n");
107 printf(" -b, --block=size Block size, must be power of 2 and >=512\n");
108 printf(" -e, --threads=count Number of threads, %d by default\n", THREADS);
109 printf(" -t, --write_through Write through mode\n");
110 printf(" -r, --read_only Read only\n");
111 printf(" -o, --direct O_DIRECT mode\n");
112 printf(" -n, --nullio NULLIO mode\n");
113 printf(" -c, --nv_cache NV_CACHE mode\n");
114 printf(" -p, --parse=type Parse type, one of \"std\" "
115 "(default), \"call\" or \"excpt\"\n");
116 printf(" -f, --on_free=type On free call type, one of \"ignore\" "
117 "(default) or \"call\"\n");
118 printf(" -m, --mem_reuse=type Memory reuse type, one of \"all\" "
119 "(default), \"read\", \"write\" or \"none\"\n");
120 printf(" -l, --non_blocking Use non-blocking operations\n");
121 printf(" -I, --vdisk_id=ID Vdisk ID (used in multi-targets setups)\n");
122 printf(" -F, --flush=n Flush SGV cache each n seconds\n");
123 printf(" -u, --unreg_before_close Unregister before close\n");
124 #if defined(DEBUG) || defined(TRACING)
125 printf(" -d, --debug=level Debug tracing level\n");
127 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
128 printf(" -g, --debug_tm_ignore Turn on DEBUG_TM_IGNORE\n");
133 static int scst_calc_block_shift(int sector_size)
138 if (sector_size == 0)
148 if (block_shift < 9) {
149 PRINT_ERROR("Wrong sector size %d", sector_size);
154 TRACE_EXIT_RES(block_shift);
158 static void *align_alloc(size_t size)
160 return memalign(PAGE_SIZE, size);
163 void sigalrm_handler(int signo)
169 TRACE_DBG("%s", "Flushing cache...");
171 res = ioctl(dev.scst_usr_fd, SCST_USER_FLUSH_CACHE, NULL);
174 PRINT_ERROR("Unable to flush cache: %s",
179 TRACE_DBG("%s", "Flushing cache done.");
181 res = alarm(flush_interval);
184 PRINT_ERROR("alarm() failed: %s", strerror(res));
193 int main(int argc, char **argv)
198 int parse_type = SCST_USER_PARSE_STANDARD;
199 int on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
200 int on_free_cmd_type_set = 0;
201 int memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
202 int threads = THREADS;
203 struct scst_user_dev_desc desc;
204 int unreg_before_close = 0;
214 memset(&dev, 0, sizeof(dev));
215 dev.block_size = (1 << DEF_BLOCK_SHIFT);
216 dev.block_shift = DEF_BLOCK_SHIFT;
217 dev.type = TYPE_DISK;
218 dev.alloc_fn = align_alloc;
220 while ((ch = getopt_long(argc, argv, "+b:e:trongluF:I:cp:f:m:d:vh", long_options,
224 dev.block_size = atoi(optarg);
225 PRINT_INFO("block_size %x (%s)", dev.block_size, optarg);
226 dev.block_shift = scst_calc_block_shift(dev.block_size);
227 if (dev.block_shift < 9) {
233 threads = strtol(optarg, (char **)NULL, 0);
238 #if defined(DEBUG) || defined(TRACING)
240 trace_flag = strtol(optarg, (char **)NULL, 0);
244 dev.rd_only_flag = 1;
247 dev.o_direct_flag = 1;
248 dev.alloc_fn = align_alloc;
257 if (strncmp(optarg, "std", 3) == 0)
258 parse_type = SCST_USER_PARSE_STANDARD;
259 else if (strncmp(optarg, "call", 3) == 0)
260 parse_type = SCST_USER_PARSE_CALL;
261 else if (strncmp(optarg, "excpt", 5) == 0)
262 parse_type = SCST_USER_PARSE_EXCEPTION;
267 on_free_cmd_type_set = 1;
268 if (strncmp(optarg, "ignore", 6) == 0)
269 on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
270 else if (strncmp(optarg, "call", 3) == 0)
271 on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
276 if (strncmp(optarg, "all", 3) == 0)
277 memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
278 else if (strncmp(optarg, "read", 4) == 0)
279 memory_reuse_type = SCST_USER_MEM_REUSE_READ;
280 else if (strncmp(optarg, "write", 5) == 0)
281 memory_reuse_type = SCST_USER_MEM_REUSE_WRITE;
282 else if (strncmp(optarg, "none", 4) == 0)
283 memory_reuse_type = SCST_USER_MEM_NO_REUSE;
288 dev.non_blocking = 1;
291 vdisk_ID = strtol(optarg, (char **)NULL, 0);
294 flush_interval = strtol(optarg, (char **)NULL, 0);
295 if (flush_interval < 0) {
296 PRINT_ERROR("Wrong flush interval %d",
302 unreg_before_close = 1;
304 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
306 dev.debug_tm_ignore = 1;
310 printf("%s version %s\n", app_name, VERSION_STR);
317 if (optind != (argc-2))
320 if (!on_free_cmd_type_set &&
321 (memory_reuse_type != SCST_USER_MEM_REUSE_ALL))
322 on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
324 dev.name = argv[optind];
325 dev.file_name = argv[optind+1];
327 TRACE_DBG("Opening file %s", dev.file_name);
328 fd = open(dev.file_name, O_RDONLY|O_LARGEFILE);
331 PRINT_ERROR("Unable to open file %s (%s)", dev.file_name,
336 dev.file_size = lseek64(fd, 0, SEEK_END);
337 dev.nblocks = dev.file_size >> dev.block_shift;
341 PRINT_INFO("Virtual device \"%s\", path \"%s\", size %"PRId64"Mb, "
342 "block size %d, nblocks %"PRId64", options:", dev.name,
343 dev.file_name, (uint64_t)dev.file_size/1024/1024,
344 dev.block_size, (uint64_t)dev.nblocks);
345 if (dev.rd_only_flag)
346 PRINT_INFO(" %s", "READ ONLY");
348 PRINT_INFO(" %s", "WRITE THROUGH");
350 PRINT_INFO(" %s", "NV_CACHE");
351 if (dev.o_direct_flag)
352 PRINT_INFO(" %s", "O_DIRECT");
354 PRINT_INFO(" %s", "NULLIO");
355 if (dev.non_blocking)
356 PRINT_INFO(" %s", "NON-BLOCKING");
359 case SCST_USER_PARSE_STANDARD:
360 PRINT_INFO(" %s", "Standard parse");
362 case SCST_USER_PARSE_CALL:
363 PRINT_INFO(" %s", "Call parse");
365 case SCST_USER_PARSE_EXCEPTION:
366 PRINT_INFO(" %s", "Exception parse");
372 switch(on_free_cmd_type) {
373 case SCST_USER_ON_FREE_CMD_IGNORE:
374 PRINT_INFO(" %s", "Ignore on_free_cmd");
376 case SCST_USER_ON_FREE_CMD_CALL:
377 PRINT_INFO(" %s", "Call on_free_cmd");
383 switch(memory_reuse_type) {
384 case SCST_USER_MEM_REUSE_ALL:
385 PRINT_INFO(" %s", "Full memory reuse enabled");
387 case SCST_USER_MEM_REUSE_READ:
388 PRINT_INFO(" %s", "READ memory reuse enabled");
390 case SCST_USER_MEM_REUSE_WRITE:
391 PRINT_INFO(" %s", "WRITE memory reuse enabled");
393 case SCST_USER_MEM_NO_REUSE:
394 PRINT_INFO(" %s", "Memory reuse disabled");
400 if (!dev.o_direct_flag && (memory_reuse_type == SCST_USER_MEM_NO_REUSE)) {
401 PRINT_INFO(" %s", "Using unaligned buffers");
402 dev.alloc_fn = malloc;
405 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
406 if (dev.debug_tm_ignore) {
407 PRINT_INFO(" %s", "DEBUG_TM_IGNORE");
412 PRINT_INFO("trace_flag %lx", trace_flag);
415 snprintf(dev.usn, sizeof(dev.usn), "%llx", gen_dev_id_num(&dev));
416 TRACE_DBG("usn %s", dev.usn);
418 dev.scst_usr_fd = open(DEV_USER_PATH DEV_USER_NAME, O_RDWR |
419 (dev.non_blocking ? O_NONBLOCK : 0));
420 if (dev.scst_usr_fd < 0) {
422 PRINT_ERROR("Unable to open SCST device %s (%s)",
423 DEV_USER_PATH DEV_USER_NAME, strerror(-res));
427 memset(&desc, 0, sizeof(desc));
428 desc.version_str = (unsigned long)DEV_USER_VERSION;
429 strncpy(desc.name, dev.name, sizeof(desc.name)-1);
430 desc.name[sizeof(desc.name)-1] = '\0';
431 desc.type = dev.type;
432 desc.block_size = dev.block_size;
434 desc.opt.parse_type = parse_type;
435 desc.opt.on_free_cmd_type = on_free_cmd_type;
436 desc.opt.memory_reuse_type = memory_reuse_type;
438 desc.opt.tst = SCST_CONTR_MODE_SEP_TASK_SETS;
439 desc.opt.queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER;
441 res = ioctl(dev.scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc);
444 PRINT_ERROR("Unable to register device: %s", strerror(res));
450 /* Not needed, added here only as a test */
451 struct scst_user_opt opt;
453 res = ioctl(dev.scst_usr_fd, SCST_USER_GET_OPTIONS, &opt);
456 PRINT_ERROR("Unable to get options: %s", strerror(res));
460 opt.parse_type = parse_type;
461 opt.on_free_cmd_type = on_free_cmd_type;
462 opt.memory_reuse_type = memory_reuse_type;
464 res = ioctl(dev.scst_usr_fd, SCST_USER_SET_OPTIONS, &opt);
467 PRINT_ERROR("Unable to set options: %s", strerror(res));
473 res = pthread_mutex_init(&dev.dev_mutex, NULL);
476 PRINT_ERROR("pthread_mutex_init() failed: %s", strerror(res));
481 pthread_t thread[threads];
485 for(i = 0; i < threads; i++) {
486 rc = pthread_create(&thread[i], NULL, main_loop, &dev);
489 PRINT_ERROR("pthread_create() failed: %s",
495 if (flush_interval != 0) {
496 struct sigaction act;
498 memset(&act, 0, sizeof(act));
499 act.sa_handler = sigalrm_handler;
500 act.sa_flags = SA_RESTART;
501 sigemptyset(&act.sa_mask);
502 res = sigaction(SIGALRM, &act, NULL);
505 PRINT_ERROR("sigaction() failed: %s",
510 res = alarm(flush_interval);
513 PRINT_ERROR("alarm() failed: %s",
521 for(i = 0; i < j; i++) {
522 rc = pthread_join(thread[i], &rc1);
525 PRINT_ERROR("pthread_join() failed: %s",
527 } else if (rc1 != NULL) {
529 PRINT_INFO("Thread %d exited, res %lx", i,
532 PRINT_INFO("Thread %d exited", i);
536 pthread_mutex_destroy(&dev.dev_mutex);
541 if (unreg_before_close) {
542 res = ioctl(dev.scst_usr_fd, SCST_USER_UNREGISTER_DEVICE, NULL);
545 PRINT_ERROR("Unable to unregister device: %s",
552 close(dev.scst_usr_fd);