a81ac470030ff3af2a48fa1de9c2dd9e08ad672d
[mirror/scst/.git] / usr / fileio / fileio.c
1 /*
2  *  fileio.c
3  *
4  *  Copyright (C) 2007 Vladislav Bolkhovitin <vst@vlnb.net>
5  *
6  *  This program is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU General Public License
8  *  as published by the Free Software Foundation, version 2
9  *  of the License.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  *  GNU General Public License for more details.
15  */
16
17 #include <ctype.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <stdint.h>
25 #include <getopt.h>
26 #include <malloc.h>
27 #include <inttypes.h>
28
29 #include <sys/types.h>
30 #include <sys/user.h>
31 #include <sys/poll.h>
32 #include <sys/ioctl.h>
33
34 #include <pthread.h>
35
36 char *app_name;
37
38 #include "common.h"
39
40 #if defined(DEBUG) || defined(TRACING)
41
42 #ifdef DEBUG
43 /*#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF \
44          & ~TRACE_FUNCTION)
45 #define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF & \
46         ~TRACE_SCSI & ~TRACE_SCSI_SERIALIZING & ~TRACE_DEBUG)
47 */
48 #define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
49         TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_MINOR | \
50         TRACE_MGMT_DEBUG | TRACE_TIME)
51
52 #define TRACE_SN(args...)       TRACE(TRACE_SCSI_SERIALIZING, args)
53
54 #else /* DEBUG */
55
56 # ifdef TRACING
57 #define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_MGMT | \
58         TRACE_TIME | TRACE_SPECIAL)
59 # else
60 #define DEFAULT_LOG_FLAGS 0
61 # endif
62 #endif /* DEBUG */
63
64 unsigned long trace_flag = DEFAULT_LOG_FLAGS;
65 #endif /* defined(DEBUG) || defined(TRACING) */
66
67 #define DEF_BLOCK_SHIFT         9
68 #define VERSION_STR             "0.9.6"
69 #define THREADS                 7
70
71 static struct option const long_options[] =
72 {
73         {"block", required_argument, 0, 'b'},
74         {"threads", required_argument, 0, 'e'},
75         {"write_through", no_argument, 0, 't'},
76         {"read_only", no_argument, 0, 'r'},
77         {"direct", no_argument, 0, 'o'},
78         {"nullio", no_argument, 0, 'n'},
79         {"nv_cache", no_argument, 0, 'c'},
80         {"parse", required_argument, 0, 'p'},
81         {"on_free", required_argument, 0, 'f'},
82         {"mem_reuse", required_argument, 0, 'm'},
83         {"prio_thread", no_argument, 0, 's'},
84         {"non_blocking", no_argument, 0, 'l'},
85 #if defined(DEBUG) || defined(TRACING)
86         {"debug", required_argument, 0, 'd'},
87 #endif
88 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
89         {"debug_tm_ignore", no_argument, 0, 'g'},
90 #endif
91         {"version", no_argument, 0, 'v'},
92         {"help", no_argument, 0, 'h'},
93         {0, 0, 0, 0},
94 };
95
96 static void usage(void)
97 {
98         printf("Usage: %s [OPTION] name path\n", app_name);
99         printf("\nFILEIO disk target emulator for SCST\n");
100         printf("  -b, --block=size      Block size, must be power of 2 and >=512\n");
101         printf("  -e, --threads=count   Number of threads, %d by default\n", THREADS);
102         printf("  -t, --write_through   Write through mode\n");
103         printf("  -r, --read_only       Read only\n");
104         printf("  -o, --direct          O_DIRECT mode\n");
105         printf("  -n, --nullio          NULLIO mode\n");
106         printf("  -c, --nv_cache        NV_CACHE mode\n");
107         printf("  -p, --parse=type      Parse type, one of \"std\" "
108                 "(default), \"call\" or \"excpt\"\n");
109         printf("  -f, --on_free=type    On free call type, one of \"ignore\" "
110                 "(default) or \"call\"\n");
111         printf("  -m, --mem_reuse=type  Memory reuse type, one of \"all\" "
112                 "(default), \"read\", \"write\" or \"none\"\n");
113         printf("  -s, --prio_thread     Use separate thread for mgmt (prio) commands\n");
114         printf("  -l, --non_blocking    Use non-blocking operations\n");
115 #if defined(DEBUG) || defined(TRACING)
116         printf("  -d, --debug=level     Debug tracing level\n");
117 #endif
118 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
119         printf("  -g, --debug_tm_ignore Turn on DEBUG_TM_IGNORE\n");
120 #endif
121         return;
122 }
123
124 static int scst_calc_block_shift(int sector_size)
125 {
126         int block_shift = 0;
127         int t = sector_size;
128
129         if (sector_size == 0)
130                 goto out;
131
132         t = sector_size;
133         while(1) {
134                 if ((t & 1) != 0)
135                         break;
136                 t >>= 1;
137                 block_shift++;
138         }
139         if (block_shift < 9) {
140                 PRINT_ERROR("Wrong sector size %d", sector_size);
141                 block_shift = -1;
142         }
143
144 out:
145         TRACE_EXIT_RES(block_shift);
146         return block_shift;
147 }
148
149 static void *align_alloc(size_t size)
150 {
151         return memalign(PAGE_SIZE, size);
152 }
153
154 int main(int argc, char **argv)
155 {
156         int res = 0;
157         int ch, longindex;
158         int fd;
159         int parse_type = SCST_USER_PARSE_STANDARD;
160         int on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
161         int on_free_cmd_type_set = 0;
162         int memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
163         int threads = THREADS;
164         struct scst_user_dev_desc desc;
165         struct vdisk_dev dev;
166
167         setlinebuf(stdout);
168
169         res = debug_init();
170         if (res != 0)
171                 goto out;
172
173         app_name = argv[0];
174
175         memset(&dev, 0, sizeof(dev));
176         dev.block_size = (1 << DEF_BLOCK_SHIFT);
177         dev.block_shift = DEF_BLOCK_SHIFT;
178         dev.type = TYPE_DISK;
179         dev.alloc_fn = align_alloc;
180
181         while ((ch = getopt_long(argc, argv, "+b:e:tronsglcp:f:m:d:vh", long_options,
182                                 &longindex)) >= 0) {
183                 switch (ch) {
184                 case 'b':
185                         dev.block_size = atoi(optarg);
186                         PRINT_INFO("block_size %x (%s)", dev.block_size, optarg);
187                         dev.block_shift = scst_calc_block_shift(dev.block_size);
188                         if (dev.block_shift < 9) {
189                                 res = -EINVAL;
190                                 goto out_usage;
191                         }
192                         break;
193                 case 'e':
194                         threads = strtol(optarg, (char **)NULL, 0);
195                         break;
196                 case 't':
197                         dev.wt_flag = 1;
198                         break;
199 #if defined(DEBUG) || defined(TRACING)
200                 case 'd':
201                         trace_flag = strtol(optarg, (char **)NULL, 0);
202                         break;
203 #endif
204                 case 'r':
205                         dev.rd_only_flag = 1;
206                         break;
207                 case 'o':
208                         dev.o_direct_flag = 1;
209                         dev.alloc_fn = align_alloc;
210                         break;
211                 case 'n':
212                         dev.nullio = 1;
213                         break;
214                 case 'c':
215                         dev.nv_cache = 1;
216                         break;
217                 case 'p':
218                         if (strncmp(optarg, "std", 3) == 0)
219                                 parse_type = SCST_USER_PARSE_STANDARD;
220                         else if (strncmp(optarg, "call", 3) == 0)
221                                 parse_type = SCST_USER_PARSE_CALL;
222                         else if (strncmp(optarg, "excpt", 5) == 0)
223                                 parse_type = SCST_USER_PARSE_EXCEPTION;
224                         else
225                                 goto out_usage;
226                         break;
227                 case 'f':
228                         on_free_cmd_type_set = 1;
229                         if (strncmp(optarg, "ignore", 6) == 0)
230                                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
231                         else if (strncmp(optarg, "call", 3) == 0)
232                                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
233                         else
234                                 goto out_usage;
235                         break;
236                 case 'm':
237                         if (strncmp(optarg, "all", 3) == 0)
238                                 memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
239                         else if (strncmp(optarg, "read", 4) == 0)
240                                 memory_reuse_type = SCST_USER_MEM_REUSE_READ;
241                         else if (strncmp(optarg, "write", 5) == 0)
242                                 memory_reuse_type = SCST_USER_MEM_REUSE_WRITE;
243                         else if (strncmp(optarg, "none", 4) == 0)
244                                 memory_reuse_type = SCST_USER_MEM_NO_REUSE;
245                         else
246                                 goto out_usage;
247                         break;
248                 case 's':
249                         dev.prio_thr = 1;
250                         break;
251                 case 'l':
252                         dev.non_blocking = 1;
253                         break;
254 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
255                 case 'g':
256                         dev.debug_tm_ignore = 1;
257                         break;
258 #endif
259                 case 'v':
260                         printf("%s version %s\n", app_name, VERSION_STR);
261                         goto out_done;
262                 default:
263                         goto out_usage;
264                 }
265         }
266
267         if (optind != (argc-2))
268                 goto out_usage;
269
270         if (!on_free_cmd_type_set &&
271             (memory_reuse_type != SCST_USER_MEM_REUSE_ALL))
272                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
273
274         dev.name = argv[optind];
275         dev.file_name = argv[optind+1];
276
277         TRACE_DBG("Opening file %s", dev.file_name);
278         fd = open(dev.file_name, O_RDONLY|O_LARGEFILE);
279         if (fd < 0) {
280                 res = -errno;
281                 PRINT_ERROR("Unable to open file %s (%s)", dev.file_name,
282                         strerror(-res));
283                 goto out_done;
284         }
285
286         dev.file_size = lseek64(fd, 0, SEEK_END);
287         dev.nblocks = dev.file_size >> dev.block_shift;
288
289         close(fd);
290
291         PRINT_INFO("Virtual device \"%s\", path \"%s\", size %"PRId64"Mb, "
292                 "block size %d, nblocks %"PRId64", options:", dev.name,
293                 dev.file_name, (uint64_t)dev.file_size/1024/1024,
294                 dev.block_size, (uint64_t)dev.nblocks);
295         if (dev.rd_only_flag)
296                 PRINT_INFO("    %s", "READ ONLY");
297         if (dev.wt_flag)
298                 PRINT_INFO("    %s", "WRITE THROUGH");
299         if (dev.nv_cache)
300                 PRINT_INFO("    %s", "NV_CACHE");
301         if (dev.o_direct_flag)
302                 PRINT_INFO("    %s", "O_DIRECT");
303         if (dev.nullio)
304                 PRINT_INFO("    %s", "NULLIO");
305         if (dev.non_blocking)
306                 PRINT_INFO("    %s", "NON-BLOCKING");
307
308         switch(parse_type) {
309         case SCST_USER_PARSE_STANDARD:
310                 PRINT_INFO("    %s", "Standard parse");
311                 break;
312         case SCST_USER_PARSE_CALL:
313                 PRINT_INFO("    %s", "Call parse");
314                 break;
315         case SCST_USER_PARSE_EXCEPTION:
316                 PRINT_INFO("    %s", "Exception parse");
317                 break;
318         default:
319                 sBUG();
320         }
321
322         switch(on_free_cmd_type) {
323         case SCST_USER_ON_FREE_CMD_IGNORE:
324                 PRINT_INFO("    %s", "Ignore on_free_cmd");
325                 break;
326         case SCST_USER_ON_FREE_CMD_CALL:
327                 PRINT_INFO("    %s", "Call on_free_cmd");
328                 break;
329         default:
330                 sBUG();
331         }
332
333         switch(memory_reuse_type) {
334         case SCST_USER_MEM_REUSE_ALL:
335                 PRINT_INFO("    %s", "Full memory reuse enabled");
336                 break;
337         case SCST_USER_MEM_REUSE_READ:
338                 PRINT_INFO("    %s", "READ memory reuse enabled");
339                 break;
340         case SCST_USER_MEM_REUSE_WRITE:
341                 PRINT_INFO("    %s", "WRITE memory reuse enabled");
342                 break;
343         case SCST_USER_MEM_NO_REUSE:
344                 PRINT_INFO("    %s", "Memory reuse disabled");
345                 break;
346         default:
347                 sBUG();
348         }
349
350         if (!dev.o_direct_flag && (memory_reuse_type == SCST_USER_MEM_NO_REUSE)) {
351                 PRINT_INFO("    %s", "Using unaligned buffers");
352                 dev.alloc_fn = malloc;
353         }
354
355         if (dev.prio_thr) {
356                 PRINT_INFO("    %s", "Using separate prio thread");
357         }
358
359 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
360         if (dev.debug_tm_ignore) {
361                 PRINT_INFO("    %s", "DEBUG_TM_IGNORE");
362         }
363 #endif
364
365 #ifdef DEBUG
366         PRINT_INFO("trace_flag %lx", trace_flag);
367 #endif
368
369         dev.scst_usr_fd = open(DEV_USER_PATH DEV_USER_NAME, O_RDWR |
370                 (dev.non_blocking ? O_NONBLOCK : 0));
371         if (dev.scst_usr_fd < 0) {
372                 res = -errno;
373                 PRINT_ERROR("Unable to open SCST device %s (%s)",
374                         DEV_USER_PATH DEV_USER_NAME, strerror(-res));
375                 goto out_done;
376         }
377
378         memset(&desc, 0, sizeof(desc));
379         desc.version_str = (unsigned long)DEV_USER_VERSION;
380         strncpy(desc.name, dev.name, sizeof(desc.name)-1);
381         desc.name[sizeof(desc.name)-1] = '\0';
382         desc.type = dev.type;
383         desc.block_size = dev.block_size;
384
385         desc.opt.parse_type = parse_type;
386         desc.opt.on_free_cmd_type = on_free_cmd_type;
387         desc.opt.memory_reuse_type = memory_reuse_type;
388         if (dev.prio_thr)
389                 desc.opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SEPARATE;
390         else
391                 desc.opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SINGLE;
392
393         desc.opt.tst = SCST_CONTR_MODE_SEP_TASK_SETS;
394         desc.opt.queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER;
395
396         res = ioctl(dev.scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc);
397         if (res != 0) {
398                 res = errno;
399                 PRINT_ERROR("Unable to register device: %s", strerror(res));
400                 goto out_close;
401         }
402
403 #if 1
404         {
405                 /* Not needed, added here only as a test */
406                 struct scst_user_opt opt;
407
408                 res = ioctl(dev.scst_usr_fd, SCST_USER_GET_OPTIONS, &opt);
409                 if (res != 0) {
410                         res = errno;
411                         PRINT_ERROR("Unable to get options: %s", strerror(res));
412                         goto out_close;
413                 }
414
415                 opt.parse_type = parse_type;
416                 opt.on_free_cmd_type = on_free_cmd_type;
417                 opt.memory_reuse_type = memory_reuse_type;
418                 if (dev.prio_thr)
419                         opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SEPARATE;
420                 else
421                         opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SINGLE;
422
423                 res = ioctl(dev.scst_usr_fd, SCST_USER_SET_OPTIONS, &opt);
424                 if (res != 0) {
425                         res = errno;
426                         PRINT_ERROR("Unable to set options: %s", strerror(res));
427                         goto out_close;
428                 }
429         }
430 #endif
431
432         res = pthread_mutex_init(&dev.dev_mutex, NULL);
433         if (res != 0) {
434                 res = errno;
435                 PRINT_ERROR("pthread_mutex_init() failed: %s", strerror(res));
436                 goto out_close;
437         }
438
439         {
440                 pthread_t thread[threads];
441                 pthread_t prio;
442                 int i, j, rc;
443                 void *rc1;
444                 for(i = 0; i < threads; i++) {
445                         rc = pthread_create(&thread[i], NULL, main_loop, &dev);
446                         if (rc != 0) {
447                                 res = errno;
448                                 PRINT_ERROR("pthread_create() failed: %s",
449                                         strerror(res));
450                                 break;
451                         }
452                 }
453
454                 if (dev.prio_thr) {
455                         rc = pthread_create(&prio, NULL, prio_loop, &dev);
456                         if (rc != 0) {
457                                 res = errno;
458                                 PRINT_ERROR("Prio pthread_create() failed: %s",
459                                         strerror(res));
460                                 dev.prio_thr = 0;
461                         }
462                 }
463
464                 j = i;
465                 for(i = 0; i < j; i++) {
466                         rc = pthread_join(thread[i], &rc1);
467                         if (rc != 0) {
468                                 res = errno;
469                                 PRINT_ERROR("pthread_join() failed: %s",
470                                         strerror(res));
471                         } else if (rc1 != NULL) {
472                                 res = (long)rc1;
473                                 PRINT_INFO("Thread %d exited, res %lx", i,
474                                         (long)rc1);
475                         } else
476                                 PRINT_INFO("Thread %d exited", i);
477                 }
478                 if (dev.prio_thr) {
479                         rc = pthread_join(prio, &rc1);
480                         if (rc != 0) {
481                                 res = errno;
482                                 PRINT_ERROR("Prio pthread_join() failed: %s",
483                                         strerror(res));
484                         } else if (rc1 != NULL) {
485                                 res = (long)rc1;
486                                 PRINT_INFO("Prio thread %d exited, res %lx", i,
487                                         (long)rc1);
488                         } else
489                                 PRINT_INFO("Prio thread %d exited", i);
490                 }
491         }
492
493         pthread_mutex_destroy(&dev.dev_mutex);
494
495 out_close:
496         close(dev.scst_usr_fd);
497
498 out_done:
499         debug_done();
500
501 out:
502         return res;
503
504 out_usage:
505         usage();
506         goto out_done;
507 }