b6d7840833ae43e5c3b1cb419c93f97a445025fc
[mirror/scst/.git] / usr / fileio / fileio.c
1 /*
2  *  fileio.c
3  *
4  *  Copyright (C) 2007 - 2008 Vladislav Bolkhovitin <vst@vlnb.net>
5  *  Copyright (C) 2007 - 2008 CMS Distribution Limited
6  *
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
10  *  of the License.
11  *
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.
16  */
17
18 #include <ctype.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <stdint.h>
26 #include <getopt.h>
27 #include <malloc.h>
28 #include <inttypes.h>
29 #include <signal.h>
30 #include <sys/types.h>
31 #include <sys/user.h>
32 #include <sys/poll.h>
33 #include <sys/ioctl.h>
34
35 #include <pthread.h>
36
37 char *app_name;
38
39 #include "common.h"
40
41 #if defined(DEBUG) || defined(TRACING)
42
43 #ifdef DEBUG
44 /*#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF \
45          & ~TRACE_FUNCTION)
46 #define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF & \
47         ~TRACE_SCSI & ~TRACE_SCSI_SERIALIZING & ~TRACE_DEBUG)
48 */
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)
52
53 #define TRACE_SN(args...)       TRACE(TRACE_SCSI_SERIALIZING, args)
54
55 #else /* DEBUG */
56
57 # ifdef TRACING
58 #define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_MGMT | \
59         TRACE_TIME | TRACE_SPECIAL)
60 # else
61 #define DEFAULT_LOG_FLAGS 0
62 # endif
63 #endif /* DEBUG */
64
65 unsigned long trace_flag = DEFAULT_LOG_FLAGS;
66 #endif /* defined(DEBUG) || defined(TRACING) */
67
68 #define DEF_BLOCK_SHIFT         9
69 #define VERSION_STR             "1.0.1"
70 #define THREADS                 7
71
72 struct vdisk_dev dev;
73 int vdisk_ID;
74 int flush_interval;
75
76 static struct option const long_options[] =
77 {
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'},
94 #endif
95 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
96         {"debug_tm_ignore", no_argument, 0, 'g'},
97 #endif
98         {"version", no_argument, 0, 'v'},
99         {"help", no_argument, 0, 'h'},
100         {0, 0, 0, 0},
101 };
102
103 static void usage(void)
104 {
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");
126 #endif
127 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
128         printf("  -g, --debug_tm_ignore Turn on DEBUG_TM_IGNORE\n");
129 #endif
130         return;
131 }
132
133 static int scst_calc_block_shift(int sector_size)
134 {
135         int block_shift = 0;
136         int t = sector_size;
137
138         if (sector_size == 0)
139                 goto out;
140
141         t = sector_size;
142         while(1) {
143                 if ((t & 1) != 0)
144                         break;
145                 t >>= 1;
146                 block_shift++;
147         }
148         if (block_shift < 9) {
149                 PRINT_ERROR("Wrong sector size %d", sector_size);
150                 block_shift = -1;
151         }
152
153 out:
154         TRACE_EXIT_RES(block_shift);
155         return block_shift;
156 }
157
158 static void *align_alloc(size_t size)
159 {
160         return memalign(PAGE_SIZE, size);
161 }
162
163 void sigalrm_handler(int signo)
164 {
165         int res;
166
167         TRACE_ENTRY();
168
169         TRACE_DBG("%s", "Flushing cache...");
170
171         res = ioctl(dev.scst_usr_fd, SCST_USER_FLUSH_CACHE, NULL);
172         if (res != 0) {
173                 res = errno;
174                 PRINT_ERROR("Unable to flush cache: %s",
175                         strerror(res));
176                 goto out;
177         }
178
179         TRACE_DBG("%s", "Flushing cache done.");
180
181         res = alarm(flush_interval);
182         if (res != 0) {
183                 res = errno;
184                 PRINT_ERROR("alarm() failed: %s", strerror(res));
185                 goto out;
186         }
187
188 out:
189         TRACE_EXIT();
190         return;
191 }
192
193 int main(int argc, char **argv)
194 {
195         int res = 0;
196         int ch, longindex;
197         int fd;
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;
205
206         setlinebuf(stdout);
207
208         res = debug_init();
209         if (res != 0)
210                 goto out;
211
212         app_name = argv[0];
213
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;
219
220         while ((ch = getopt_long(argc, argv, "+b:e:trongluF:I:cp:f:m:d:vh", long_options,
221                                 &longindex)) >= 0) {
222                 switch (ch) {
223                 case 'b':
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) {
228                                 res = -EINVAL;
229                                 goto out_usage;
230                         }
231                         break;
232                 case 'e':
233                         threads = strtol(optarg, (char **)NULL, 0);
234                         break;
235                 case 't':
236                         dev.wt_flag = 1;
237                         break;
238 #if defined(DEBUG) || defined(TRACING)
239                 case 'd':
240                         trace_flag = strtol(optarg, (char **)NULL, 0);
241                         break;
242 #endif
243                 case 'r':
244                         dev.rd_only_flag = 1;
245                         break;
246                 case 'o':
247                         dev.o_direct_flag = 1;
248                         dev.alloc_fn = align_alloc;
249                         break;
250                 case 'n':
251                         dev.nullio = 1;
252                         break;
253                 case 'c':
254                         dev.nv_cache = 1;
255                         break;
256                 case 'p':
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;
263                         else
264                                 goto out_usage;
265                         break;
266                 case 'f':
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;
272                         else
273                                 goto out_usage;
274                         break;
275                 case 'm':
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;
284                         else
285                                 goto out_usage;
286                         break;
287                 case 'l':
288                         dev.non_blocking = 1;
289                         break;
290                 case 'I':
291                         vdisk_ID = strtol(optarg, (char **)NULL, 0);
292                         break;
293                 case 'F':
294                         flush_interval = strtol(optarg, (char **)NULL, 0);
295                         if (flush_interval < 0) {
296                                 PRINT_ERROR("Wrong flush interval %d",
297                                         flush_interval);
298                                 flush_interval = 0;
299                         }
300                         break;
301                 case 'u':
302                         unreg_before_close = 1;
303                         break;
304 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
305                 case 'g':
306                         dev.debug_tm_ignore = 1;
307                         break;
308 #endif
309                 case 'v':
310                         printf("%s version %s\n", app_name, VERSION_STR);
311                         goto out_done;
312                 default:
313                         goto out_usage;
314                 }
315         }
316
317         if (optind != (argc-2))
318                 goto out_usage;
319
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;
323
324         dev.name = argv[optind];
325         dev.file_name = argv[optind+1];
326
327         TRACE_DBG("Opening file %s", dev.file_name);
328         fd = open(dev.file_name, O_RDONLY|O_LARGEFILE);
329         if (fd < 0) {
330                 res = -errno;
331                 PRINT_ERROR("Unable to open file %s (%s)", dev.file_name,
332                         strerror(-res));
333                 goto out_done;
334         }
335
336         dev.file_size = lseek64(fd, 0, SEEK_END);
337         dev.nblocks = dev.file_size >> dev.block_shift;
338
339         close(fd);
340
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");
347         if (dev.wt_flag)
348                 PRINT_INFO("    %s", "WRITE THROUGH");
349         if (dev.nv_cache)
350                 PRINT_INFO("    %s", "NV_CACHE");
351         if (dev.o_direct_flag)
352                 PRINT_INFO("    %s", "O_DIRECT");
353         if (dev.nullio)
354                 PRINT_INFO("    %s", "NULLIO");
355         if (dev.non_blocking)
356                 PRINT_INFO("    %s", "NON-BLOCKING");
357
358         switch(parse_type) {
359         case SCST_USER_PARSE_STANDARD:
360                 PRINT_INFO("    %s", "Standard parse");
361                 break;
362         case SCST_USER_PARSE_CALL:
363                 PRINT_INFO("    %s", "Call parse");
364                 break;
365         case SCST_USER_PARSE_EXCEPTION:
366                 PRINT_INFO("    %s", "Exception parse");
367                 break;
368         default:
369                 sBUG();
370         }
371
372         switch(on_free_cmd_type) {
373         case SCST_USER_ON_FREE_CMD_IGNORE:
374                 PRINT_INFO("    %s", "Ignore on_free_cmd");
375                 break;
376         case SCST_USER_ON_FREE_CMD_CALL:
377                 PRINT_INFO("    %s", "Call on_free_cmd");
378                 break;
379         default:
380                 sBUG();
381         }
382
383         switch(memory_reuse_type) {
384         case SCST_USER_MEM_REUSE_ALL:
385                 PRINT_INFO("    %s", "Full memory reuse enabled");
386                 break;
387         case SCST_USER_MEM_REUSE_READ:
388                 PRINT_INFO("    %s", "READ memory reuse enabled");
389                 break;
390         case SCST_USER_MEM_REUSE_WRITE:
391                 PRINT_INFO("    %s", "WRITE memory reuse enabled");
392                 break;
393         case SCST_USER_MEM_NO_REUSE:
394                 PRINT_INFO("    %s", "Memory reuse disabled");
395                 break;
396         default:
397                 sBUG();
398         }
399
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;
403         }
404
405 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
406         if (dev.debug_tm_ignore) {
407                 PRINT_INFO("    %s", "DEBUG_TM_IGNORE");
408         }
409 #endif
410
411 #ifdef DEBUG
412         PRINT_INFO("trace_flag %lx", trace_flag);
413 #endif
414
415         snprintf(dev.usn, sizeof(dev.usn), "%llx", gen_dev_id_num(&dev));
416         TRACE_DBG("usn %s", dev.usn);
417
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) {
421                 res = -errno;
422                 PRINT_ERROR("Unable to open SCST device %s (%s)",
423                         DEV_USER_PATH DEV_USER_NAME, strerror(-res));
424                 goto out_done;
425         }
426
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;
433
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;
437
438         desc.opt.tst = SCST_CONTR_MODE_SEP_TASK_SETS;
439         desc.opt.queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER;
440
441         res = ioctl(dev.scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc);
442         if (res != 0) {
443                 res = errno;
444                 PRINT_ERROR("Unable to register device: %s", strerror(res));
445                 goto out_close;
446         }
447
448 #if 1
449         {
450                 /* Not needed, added here only as a test */
451                 struct scst_user_opt opt;
452
453                 res = ioctl(dev.scst_usr_fd, SCST_USER_GET_OPTIONS, &opt);
454                 if (res != 0) {
455                         res = errno;
456                         PRINT_ERROR("Unable to get options: %s", strerror(res));
457                         goto out_unreg;
458                 }
459
460                 opt.parse_type = parse_type;
461                 opt.on_free_cmd_type = on_free_cmd_type;
462                 opt.memory_reuse_type = memory_reuse_type;
463
464                 res = ioctl(dev.scst_usr_fd, SCST_USER_SET_OPTIONS, &opt);
465                 if (res != 0) {
466                         res = errno;
467                         PRINT_ERROR("Unable to set options: %s", strerror(res));
468                         goto out_unreg;
469                 }
470         }
471 #endif
472
473         res = pthread_mutex_init(&dev.dev_mutex, NULL);
474         if (res != 0) {
475                 res = errno;
476                 PRINT_ERROR("pthread_mutex_init() failed: %s", strerror(res));
477                 goto out_unreg;
478         }
479
480         {
481                 pthread_t thread[threads];
482                 int i, j, rc;
483                 void *rc1;
484
485                 for(i = 0; i < threads; i++) {
486                         rc = pthread_create(&thread[i], NULL, main_loop, &dev);
487                         if (rc != 0) {
488                                 res = errno;
489                                 PRINT_ERROR("pthread_create() failed: %s",
490                                         strerror(res));
491                                 break;
492                         }
493                 }
494
495                 if (flush_interval != 0) {
496                         struct sigaction act;
497
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);
503                         if (res != 0) {
504                                 res = errno;
505                                 PRINT_ERROR("sigaction() failed: %s",
506                                         strerror(res));
507                                 goto join;
508                         }       
509
510                         res = alarm(flush_interval);
511                         if (res != 0) {
512                                 res = errno;
513                                 PRINT_ERROR("alarm() failed: %s",
514                                         strerror(res));
515                                 goto join;
516                         }
517                 }
518
519 join:
520                 j = i;
521                 for(i = 0; i < j; i++) {
522                         rc = pthread_join(thread[i], &rc1);
523                         if (rc != 0) {
524                                 res = errno;
525                                 PRINT_ERROR("pthread_join() failed: %s",
526                                         strerror(res));
527                         } else if (rc1 != NULL) {
528                                 res = (long)rc1;
529                                 PRINT_INFO("Thread %d exited, res %lx", i,
530                                         (long)rc1);
531                         } else
532                                 PRINT_INFO("Thread %d exited", i);
533                 }
534         }
535
536         pthread_mutex_destroy(&dev.dev_mutex);
537
538         alarm(0);
539
540 out_unreg:
541         if (unreg_before_close) {
542                 res = ioctl(dev.scst_usr_fd, SCST_USER_UNREGISTER_DEVICE, NULL);
543                 if (res != 0) {
544                         res = errno;
545                         PRINT_ERROR("Unable to unregister device: %s",
546                                 strerror(res));
547                         /* go through */
548                 }
549         }
550
551 out_close:
552         close(dev.scst_usr_fd);
553
554 out_done:
555         debug_done();
556
557 out:
558         return res;
559
560 out_usage:
561         usage();
562         goto out_done;
563 }